xref: /openbmc/qemu/hw/cpu/arm11mpcore.c (revision b7163687)
1 /*
2  * ARM11MPCore internal peripheral emulation.
3  *
4  * Copyright (c) 2006-2007 CodeSourcery.
5  * Written by Paul Brook
6  *
7  * This code is licensed under the GPL.
8  */
9 
10 #include "hw/sysbus.h"
11 #include "qemu/timer.h"
12 
13 /* MPCore private memory region.  */
14 
15 typedef struct ARM11MPCorePriveState {
16     SysBusDevice busdev;
17     uint32_t scu_control;
18     int iomemtype;
19     uint32_t old_timer_status[8];
20     uint32_t num_cpu;
21     MemoryRegion iomem;
22     MemoryRegion container;
23     DeviceState *mptimer;
24     DeviceState *wdtimer;
25     DeviceState *gic;
26     uint32_t num_irq;
27 } ARM11MPCorePriveState;
28 
29 /* Per-CPU private memory mapped IO.  */
30 
31 static uint64_t mpcore_scu_read(void *opaque, hwaddr offset,
32                                 unsigned size)
33 {
34     ARM11MPCorePriveState *s = (ARM11MPCorePriveState *)opaque;
35     int id;
36     /* SCU */
37     switch (offset) {
38     case 0x00: /* Control.  */
39         return s->scu_control;
40     case 0x04: /* Configuration.  */
41         id = ((1 << s->num_cpu) - 1) << 4;
42         return id | (s->num_cpu - 1);
43     case 0x08: /* CPU status.  */
44         return 0;
45     case 0x0c: /* Invalidate all.  */
46         return 0;
47     default:
48         qemu_log_mask(LOG_GUEST_ERROR,
49                       "mpcore_priv_read: Bad offset %x\n", (int)offset);
50         return 0;
51     }
52 }
53 
54 static void mpcore_scu_write(void *opaque, hwaddr offset,
55                              uint64_t value, unsigned size)
56 {
57     ARM11MPCorePriveState *s = (ARM11MPCorePriveState *)opaque;
58     /* SCU */
59     switch (offset) {
60     case 0: /* Control register.  */
61         s->scu_control = value & 1;
62         break;
63     case 0x0c: /* Invalidate all.  */
64         /* This is a no-op as cache is not emulated.  */
65         break;
66     default:
67         qemu_log_mask(LOG_GUEST_ERROR,
68                       "mpcore_priv_read: Bad offset %x\n", (int)offset);
69     }
70 }
71 
72 static const MemoryRegionOps mpcore_scu_ops = {
73     .read = mpcore_scu_read,
74     .write = mpcore_scu_write,
75     .endianness = DEVICE_NATIVE_ENDIAN,
76 };
77 
78 static void mpcore_priv_set_irq(void *opaque, int irq, int level)
79 {
80     ARM11MPCorePriveState *s = (ARM11MPCorePriveState *)opaque;
81     qemu_set_irq(qdev_get_gpio_in(s->gic, irq), level);
82 }
83 
84 static void mpcore_priv_map_setup(ARM11MPCorePriveState *s)
85 {
86     int i;
87     SysBusDevice *gicbusdev = SYS_BUS_DEVICE(s->gic);
88     SysBusDevice *timerbusdev = SYS_BUS_DEVICE(s->mptimer);
89     SysBusDevice *wdtbusdev = SYS_BUS_DEVICE(s->wdtimer);
90     memory_region_init(&s->container, OBJECT(s),
91                        "mpcode-priv-container", 0x2000);
92     memory_region_init_io(&s->iomem, OBJECT(s),
93                           &mpcore_scu_ops, s, "mpcore-scu", 0x100);
94     memory_region_add_subregion(&s->container, 0, &s->iomem);
95     /* GIC CPU interfaces: "current CPU" at 0x100, then specific CPUs
96      * at 0x200, 0x300...
97      */
98     for (i = 0; i < (s->num_cpu + 1); i++) {
99         hwaddr offset = 0x100 + (i * 0x100);
100         memory_region_add_subregion(&s->container, offset,
101                                     sysbus_mmio_get_region(gicbusdev, i + 1));
102     }
103     /* Add the regions for timer and watchdog for "current CPU" and
104      * for each specific CPU.
105      */
106     for (i = 0; i < (s->num_cpu + 1); i++) {
107         /* Timers at 0x600, 0x700, ...; watchdogs at 0x620, 0x720, ... */
108         hwaddr offset = 0x600 + i * 0x100;
109         memory_region_add_subregion(&s->container, offset,
110                                     sysbus_mmio_get_region(timerbusdev, i));
111         memory_region_add_subregion(&s->container, offset + 0x20,
112                                     sysbus_mmio_get_region(wdtbusdev, i));
113     }
114     memory_region_add_subregion(&s->container, 0x1000,
115                                 sysbus_mmio_get_region(gicbusdev, 0));
116     /* Wire up the interrupt from each watchdog and timer.
117      * For each core the timer is PPI 29 and the watchdog PPI 30.
118      */
119     for (i = 0; i < s->num_cpu; i++) {
120         int ppibase = (s->num_irq - 32) + i * 32;
121         sysbus_connect_irq(timerbusdev, i,
122                            qdev_get_gpio_in(s->gic, ppibase + 29));
123         sysbus_connect_irq(wdtbusdev, i,
124                            qdev_get_gpio_in(s->gic, ppibase + 30));
125     }
126 }
127 
128 static int mpcore_priv_init(SysBusDevice *dev)
129 {
130     ARM11MPCorePriveState *s = FROM_SYSBUS(ARM11MPCorePriveState, dev);
131 
132     s->gic = qdev_create(NULL, "arm_gic");
133     qdev_prop_set_uint32(s->gic, "num-cpu", s->num_cpu);
134     qdev_prop_set_uint32(s->gic, "num-irq", s->num_irq);
135     /* Request the legacy 11MPCore GIC behaviour: */
136     qdev_prop_set_uint32(s->gic, "revision", 0);
137     qdev_init_nofail(s->gic);
138 
139     /* Pass through outbound IRQ lines from the GIC */
140     sysbus_pass_irq(dev, SYS_BUS_DEVICE(s->gic));
141 
142     /* Pass through inbound GPIO lines to the GIC */
143     qdev_init_gpio_in(&s->busdev.qdev, mpcore_priv_set_irq, s->num_irq - 32);
144 
145     s->mptimer = qdev_create(NULL, "arm_mptimer");
146     qdev_prop_set_uint32(s->mptimer, "num-cpu", s->num_cpu);
147     qdev_init_nofail(s->mptimer);
148 
149     s->wdtimer = qdev_create(NULL, "arm_mptimer");
150     qdev_prop_set_uint32(s->wdtimer, "num-cpu", s->num_cpu);
151     qdev_init_nofail(s->wdtimer);
152 
153     mpcore_priv_map_setup(s);
154     sysbus_init_mmio(dev, &s->container);
155     return 0;
156 }
157 
158 /* Dummy PIC to route IRQ lines.  The baseboard has 4 independent IRQ
159    controllers.  The output of these, plus some of the raw input lines
160    are fed into a single SMP-aware interrupt controller on the CPU.  */
161 typedef struct {
162     SysBusDevice busdev;
163     SysBusDevice *priv;
164     qemu_irq cpuic[32];
165     qemu_irq rvic[4][64];
166     uint32_t num_cpu;
167 } mpcore_rirq_state;
168 
169 /* Map baseboard IRQs onto CPU IRQ lines.  */
170 static const int mpcore_irq_map[32] = {
171     -1, -1, -1, -1,  1,  2, -1, -1,
172     -1, -1,  6, -1,  4,  5, -1, -1,
173     -1, 14, 15,  0,  7,  8, -1, -1,
174     -1, -1, -1, -1,  9,  3, -1, -1,
175 };
176 
177 static void mpcore_rirq_set_irq(void *opaque, int irq, int level)
178 {
179     mpcore_rirq_state *s = (mpcore_rirq_state *)opaque;
180     int i;
181 
182     for (i = 0; i < 4; i++) {
183         qemu_set_irq(s->rvic[i][irq], level);
184     }
185     if (irq < 32) {
186         irq = mpcore_irq_map[irq];
187         if (irq >= 0) {
188             qemu_set_irq(s->cpuic[irq], level);
189         }
190     }
191 }
192 
193 static int realview_mpcore_init(SysBusDevice *dev)
194 {
195     mpcore_rirq_state *s = FROM_SYSBUS(mpcore_rirq_state, dev);
196     DeviceState *gic;
197     DeviceState *priv;
198     int n;
199     int i;
200 
201     priv = qdev_create(NULL, "arm11mpcore_priv");
202     qdev_prop_set_uint32(priv, "num-cpu", s->num_cpu);
203     qdev_init_nofail(priv);
204     s->priv = SYS_BUS_DEVICE(priv);
205     sysbus_pass_irq(dev, s->priv);
206     for (i = 0; i < 32; i++) {
207         s->cpuic[i] = qdev_get_gpio_in(priv, i);
208     }
209     /* ??? IRQ routing is hardcoded to "normal" mode.  */
210     for (n = 0; n < 4; n++) {
211         gic = sysbus_create_simple("realview_gic", 0x10040000 + n * 0x10000,
212                                    s->cpuic[10 + n]);
213         for (i = 0; i < 64; i++) {
214             s->rvic[n][i] = qdev_get_gpio_in(gic, i);
215         }
216     }
217     qdev_init_gpio_in(&dev->qdev, mpcore_rirq_set_irq, 64);
218     sysbus_init_mmio(dev, sysbus_mmio_get_region(s->priv, 0));
219     return 0;
220 }
221 
222 static Property mpcore_rirq_properties[] = {
223     DEFINE_PROP_UINT32("num-cpu", mpcore_rirq_state, num_cpu, 1),
224     DEFINE_PROP_END_OF_LIST(),
225 };
226 
227 static void mpcore_rirq_class_init(ObjectClass *klass, void *data)
228 {
229     DeviceClass *dc = DEVICE_CLASS(klass);
230     SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
231 
232     k->init = realview_mpcore_init;
233     dc->props = mpcore_rirq_properties;
234 }
235 
236 static const TypeInfo mpcore_rirq_info = {
237     .name          = "realview_mpcore",
238     .parent        = TYPE_SYS_BUS_DEVICE,
239     .instance_size = sizeof(mpcore_rirq_state),
240     .class_init    = mpcore_rirq_class_init,
241 };
242 
243 static Property mpcore_priv_properties[] = {
244     DEFINE_PROP_UINT32("num-cpu", ARM11MPCorePriveState, num_cpu, 1),
245     /* The ARM11 MPCORE TRM says the on-chip controller may have
246      * anything from 0 to 224 external interrupt IRQ lines (with another
247      * 32 internal). We default to 32+32, which is the number provided by
248      * the ARM11 MPCore test chip in the Realview Versatile Express
249      * coretile. Other boards may differ and should set this property
250      * appropriately. Some Linux kernels may not boot if the hardware
251      * has more IRQ lines than the kernel expects.
252      */
253     DEFINE_PROP_UINT32("num-irq", ARM11MPCorePriveState, num_irq, 64),
254     DEFINE_PROP_END_OF_LIST(),
255 };
256 
257 static void mpcore_priv_class_init(ObjectClass *klass, void *data)
258 {
259     DeviceClass *dc = DEVICE_CLASS(klass);
260     SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
261 
262     k->init = mpcore_priv_init;
263     dc->props = mpcore_priv_properties;
264 }
265 
266 static const TypeInfo mpcore_priv_info = {
267     .name          = "arm11mpcore_priv",
268     .parent        = TYPE_SYS_BUS_DEVICE,
269     .instance_size = sizeof(ARM11MPCorePriveState),
270     .class_init    = mpcore_priv_class_init,
271 };
272 
273 static void arm11mpcore_register_types(void)
274 {
275     type_register_static(&mpcore_rirq_info);
276     type_register_static(&mpcore_priv_info);
277 }
278 
279 type_init(arm11mpcore_register_types)
280