xref: /openbmc/qemu/hw/cpu/arm11mpcore.c (revision 7b960dc37df088f9ca71bdc2a611864eae38c5c4)
10434e30aSPaolo Bonzini /*
20434e30aSPaolo Bonzini  * ARM11MPCore internal peripheral emulation.
30434e30aSPaolo Bonzini  *
40434e30aSPaolo Bonzini  * Copyright (c) 2006-2007 CodeSourcery.
50434e30aSPaolo Bonzini  * Written by Paul Brook
60434e30aSPaolo Bonzini  *
70434e30aSPaolo Bonzini  * This code is licensed under the GPL.
80434e30aSPaolo Bonzini  */
90434e30aSPaolo Bonzini 
10*7b960dc3SAndreas Färber #include "hw/cpu/arm11mpcore.h"
11306476eaSAndreas Färber #include "hw/intc/realview_gic.h"
120434e30aSPaolo Bonzini 
130434e30aSPaolo Bonzini 
140434e30aSPaolo Bonzini static void mpcore_priv_set_irq(void *opaque, int irq, int level)
150434e30aSPaolo Bonzini {
160434e30aSPaolo Bonzini     ARM11MPCorePriveState *s = (ARM11MPCorePriveState *)opaque;
1708602ac5SAndreas Färber 
1808602ac5SAndreas Färber     qemu_set_irq(qdev_get_gpio_in(DEVICE(&s->gic), irq), level);
190434e30aSPaolo Bonzini }
200434e30aSPaolo Bonzini 
210434e30aSPaolo Bonzini static void mpcore_priv_map_setup(ARM11MPCorePriveState *s)
220434e30aSPaolo Bonzini {
230434e30aSPaolo Bonzini     int i;
2453cb9a1cSAndreas Färber     SysBusDevice *scubusdev = SYS_BUS_DEVICE(&s->scu);
2508602ac5SAndreas Färber     DeviceState *gicdev = DEVICE(&s->gic);
2608602ac5SAndreas Färber     SysBusDevice *gicbusdev = SYS_BUS_DEVICE(&s->gic);
2708602ac5SAndreas Färber     SysBusDevice *timerbusdev = SYS_BUS_DEVICE(&s->mptimer);
2808602ac5SAndreas Färber     SysBusDevice *wdtbusdev = SYS_BUS_DEVICE(&s->wdtimer);
2953cb9a1cSAndreas Färber 
3053cb9a1cSAndreas Färber     memory_region_add_subregion(&s->container, 0,
3153cb9a1cSAndreas Färber                                 sysbus_mmio_get_region(scubusdev, 0));
320434e30aSPaolo Bonzini     /* GIC CPU interfaces: "current CPU" at 0x100, then specific CPUs
330434e30aSPaolo Bonzini      * at 0x200, 0x300...
340434e30aSPaolo Bonzini      */
350434e30aSPaolo Bonzini     for (i = 0; i < (s->num_cpu + 1); i++) {
360434e30aSPaolo Bonzini         hwaddr offset = 0x100 + (i * 0x100);
370434e30aSPaolo Bonzini         memory_region_add_subregion(&s->container, offset,
380434e30aSPaolo Bonzini                                     sysbus_mmio_get_region(gicbusdev, i + 1));
390434e30aSPaolo Bonzini     }
400434e30aSPaolo Bonzini     /* Add the regions for timer and watchdog for "current CPU" and
410434e30aSPaolo Bonzini      * for each specific CPU.
420434e30aSPaolo Bonzini      */
430434e30aSPaolo Bonzini     for (i = 0; i < (s->num_cpu + 1); i++) {
440434e30aSPaolo Bonzini         /* Timers at 0x600, 0x700, ...; watchdogs at 0x620, 0x720, ... */
450434e30aSPaolo Bonzini         hwaddr offset = 0x600 + i * 0x100;
460434e30aSPaolo Bonzini         memory_region_add_subregion(&s->container, offset,
470434e30aSPaolo Bonzini                                     sysbus_mmio_get_region(timerbusdev, i));
480434e30aSPaolo Bonzini         memory_region_add_subregion(&s->container, offset + 0x20,
490434e30aSPaolo Bonzini                                     sysbus_mmio_get_region(wdtbusdev, i));
500434e30aSPaolo Bonzini     }
510434e30aSPaolo Bonzini     memory_region_add_subregion(&s->container, 0x1000,
520434e30aSPaolo Bonzini                                 sysbus_mmio_get_region(gicbusdev, 0));
530434e30aSPaolo Bonzini     /* Wire up the interrupt from each watchdog and timer.
540434e30aSPaolo Bonzini      * For each core the timer is PPI 29 and the watchdog PPI 30.
550434e30aSPaolo Bonzini      */
560434e30aSPaolo Bonzini     for (i = 0; i < s->num_cpu; i++) {
570434e30aSPaolo Bonzini         int ppibase = (s->num_irq - 32) + i * 32;
580434e30aSPaolo Bonzini         sysbus_connect_irq(timerbusdev, i,
5908602ac5SAndreas Färber                            qdev_get_gpio_in(gicdev, ppibase + 29));
600434e30aSPaolo Bonzini         sysbus_connect_irq(wdtbusdev, i,
6108602ac5SAndreas Färber                            qdev_get_gpio_in(gicdev, ppibase + 30));
620434e30aSPaolo Bonzini     }
630434e30aSPaolo Bonzini }
640434e30aSPaolo Bonzini 
6508602ac5SAndreas Färber static void mpcore_priv_realize(DeviceState *dev, Error **errp)
660434e30aSPaolo Bonzini {
6708602ac5SAndreas Färber     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
6856fc0281SAndreas Färber     ARM11MPCorePriveState *s = ARM11MPCORE_PRIV(dev);
6953cb9a1cSAndreas Färber     DeviceState *scudev = DEVICE(&s->scu);
7008602ac5SAndreas Färber     DeviceState *gicdev = DEVICE(&s->gic);
7108602ac5SAndreas Färber     DeviceState *mptimerdev = DEVICE(&s->mptimer);
7208602ac5SAndreas Färber     DeviceState *wdtimerdev = DEVICE(&s->wdtimer);
7308602ac5SAndreas Färber     Error *err = NULL;
7453cb9a1cSAndreas Färber 
7553cb9a1cSAndreas Färber     qdev_prop_set_uint32(scudev, "num-cpu", s->num_cpu);
7608602ac5SAndreas Färber     object_property_set_bool(OBJECT(&s->scu), true, "realized", &err);
7708602ac5SAndreas Färber     if (err != NULL) {
7808602ac5SAndreas Färber         error_propagate(errp, err);
7908602ac5SAndreas Färber         return;
8008602ac5SAndreas Färber     }
810434e30aSPaolo Bonzini 
8208602ac5SAndreas Färber     qdev_prop_set_uint32(gicdev, "num-cpu", s->num_cpu);
8308602ac5SAndreas Färber     qdev_prop_set_uint32(gicdev, "num-irq", s->num_irq);
8408602ac5SAndreas Färber     object_property_set_bool(OBJECT(&s->gic), true, "realized", &err);
8508602ac5SAndreas Färber     if (err != NULL) {
8608602ac5SAndreas Färber         error_propagate(errp, err);
8708602ac5SAndreas Färber         return;
8808602ac5SAndreas Färber     }
890434e30aSPaolo Bonzini 
900434e30aSPaolo Bonzini     /* Pass through outbound IRQ lines from the GIC */
9108602ac5SAndreas Färber     sysbus_pass_irq(sbd, SYS_BUS_DEVICE(&s->gic));
920434e30aSPaolo Bonzini 
930434e30aSPaolo Bonzini     /* Pass through inbound GPIO lines to the GIC */
9456fc0281SAndreas Färber     qdev_init_gpio_in(dev, mpcore_priv_set_irq, s->num_irq - 32);
950434e30aSPaolo Bonzini 
9608602ac5SAndreas Färber     qdev_prop_set_uint32(mptimerdev, "num-cpu", s->num_cpu);
9708602ac5SAndreas Färber     object_property_set_bool(OBJECT(&s->mptimer), true, "realized", &err);
9808602ac5SAndreas Färber     if (err != NULL) {
9908602ac5SAndreas Färber         error_propagate(errp, err);
10008602ac5SAndreas Färber         return;
10108602ac5SAndreas Färber     }
1020434e30aSPaolo Bonzini 
10308602ac5SAndreas Färber     qdev_prop_set_uint32(wdtimerdev, "num-cpu", s->num_cpu);
10408602ac5SAndreas Färber     object_property_set_bool(OBJECT(&s->wdtimer), true, "realized", &err);
10508602ac5SAndreas Färber     if (err != NULL) {
10608602ac5SAndreas Färber         error_propagate(errp, err);
10708602ac5SAndreas Färber         return;
10808602ac5SAndreas Färber     }
1090434e30aSPaolo Bonzini 
1100434e30aSPaolo Bonzini     mpcore_priv_map_setup(s);
1110434e30aSPaolo Bonzini }
1120434e30aSPaolo Bonzini 
1132c42c3a0SAndreas Färber static void mpcore_priv_initfn(Object *obj)
1142c42c3a0SAndreas Färber {
1152c42c3a0SAndreas Färber     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
1162c42c3a0SAndreas Färber     ARM11MPCorePriveState *s = ARM11MPCORE_PRIV(obj);
1172c42c3a0SAndreas Färber 
1182c42c3a0SAndreas Färber     memory_region_init(&s->container, OBJECT(s),
1192c42c3a0SAndreas Färber                        "mpcore-priv-container", 0x2000);
1202c42c3a0SAndreas Färber     sysbus_init_mmio(sbd, &s->container);
12153cb9a1cSAndreas Färber 
12253cb9a1cSAndreas Färber     object_initialize(&s->scu, sizeof(s->scu), TYPE_ARM11_SCU);
12353cb9a1cSAndreas Färber     qdev_set_parent_bus(DEVICE(&s->scu), sysbus_get_default());
12408602ac5SAndreas Färber 
12508602ac5SAndreas Färber     object_initialize(&s->gic, sizeof(s->gic), TYPE_ARM_GIC);
12608602ac5SAndreas Färber     qdev_set_parent_bus(DEVICE(&s->gic), sysbus_get_default());
12708602ac5SAndreas Färber     /* Request the legacy 11MPCore GIC behaviour: */
12808602ac5SAndreas Färber     qdev_prop_set_uint32(DEVICE(&s->gic), "revision", 0);
12908602ac5SAndreas Färber 
13008602ac5SAndreas Färber     object_initialize(&s->mptimer, sizeof(s->mptimer), TYPE_ARM_MPTIMER);
13108602ac5SAndreas Färber     qdev_set_parent_bus(DEVICE(&s->mptimer), sysbus_get_default());
13208602ac5SAndreas Färber 
13308602ac5SAndreas Färber     object_initialize(&s->wdtimer, sizeof(s->wdtimer), TYPE_ARM_MPTIMER);
13408602ac5SAndreas Färber     qdev_set_parent_bus(DEVICE(&s->wdtimer), sysbus_get_default());
1352c42c3a0SAndreas Färber }
1362c42c3a0SAndreas Färber 
13745c0a675SAndreas Färber #define TYPE_REALVIEW_MPCORE_RIRQ "realview_mpcore"
13845c0a675SAndreas Färber #define REALVIEW_MPCORE_RIRQ(obj) \
13945c0a675SAndreas Färber     OBJECT_CHECK(mpcore_rirq_state, (obj), TYPE_REALVIEW_MPCORE_RIRQ)
14045c0a675SAndreas Färber 
1410434e30aSPaolo Bonzini /* Dummy PIC to route IRQ lines.  The baseboard has 4 independent IRQ
1420434e30aSPaolo Bonzini    controllers.  The output of these, plus some of the raw input lines
1430434e30aSPaolo Bonzini    are fed into a single SMP-aware interrupt controller on the CPU.  */
1440434e30aSPaolo Bonzini typedef struct {
14545c0a675SAndreas Färber     SysBusDevice parent_obj;
14645c0a675SAndreas Färber 
1470434e30aSPaolo Bonzini     qemu_irq cpuic[32];
1480434e30aSPaolo Bonzini     qemu_irq rvic[4][64];
1490434e30aSPaolo Bonzini     uint32_t num_cpu;
150306476eaSAndreas Färber 
151306476eaSAndreas Färber     ARM11MPCorePriveState priv;
152306476eaSAndreas Färber     RealViewGICState gic[4];
1530434e30aSPaolo Bonzini } mpcore_rirq_state;
1540434e30aSPaolo Bonzini 
1550434e30aSPaolo Bonzini /* Map baseboard IRQs onto CPU IRQ lines.  */
1560434e30aSPaolo Bonzini static const int mpcore_irq_map[32] = {
1570434e30aSPaolo Bonzini     -1, -1, -1, -1,  1,  2, -1, -1,
1580434e30aSPaolo Bonzini     -1, -1,  6, -1,  4,  5, -1, -1,
1590434e30aSPaolo Bonzini     -1, 14, 15,  0,  7,  8, -1, -1,
1600434e30aSPaolo Bonzini     -1, -1, -1, -1,  9,  3, -1, -1,
1610434e30aSPaolo Bonzini };
1620434e30aSPaolo Bonzini 
1630434e30aSPaolo Bonzini static void mpcore_rirq_set_irq(void *opaque, int irq, int level)
1640434e30aSPaolo Bonzini {
1650434e30aSPaolo Bonzini     mpcore_rirq_state *s = (mpcore_rirq_state *)opaque;
1660434e30aSPaolo Bonzini     int i;
1670434e30aSPaolo Bonzini 
1680434e30aSPaolo Bonzini     for (i = 0; i < 4; i++) {
1690434e30aSPaolo Bonzini         qemu_set_irq(s->rvic[i][irq], level);
1700434e30aSPaolo Bonzini     }
1710434e30aSPaolo Bonzini     if (irq < 32) {
1720434e30aSPaolo Bonzini         irq = mpcore_irq_map[irq];
1730434e30aSPaolo Bonzini         if (irq >= 0) {
1740434e30aSPaolo Bonzini             qemu_set_irq(s->cpuic[irq], level);
1750434e30aSPaolo Bonzini         }
1760434e30aSPaolo Bonzini     }
1770434e30aSPaolo Bonzini }
1780434e30aSPaolo Bonzini 
179306476eaSAndreas Färber static void realview_mpcore_realize(DeviceState *dev, Error **errp)
1800434e30aSPaolo Bonzini {
181306476eaSAndreas Färber     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
18245c0a675SAndreas Färber     mpcore_rirq_state *s = REALVIEW_MPCORE_RIRQ(dev);
183306476eaSAndreas Färber     DeviceState *priv = DEVICE(&s->priv);
1840434e30aSPaolo Bonzini     DeviceState *gic;
185306476eaSAndreas Färber     SysBusDevice *gicbusdev;
186306476eaSAndreas Färber     Error *err = NULL;
1870434e30aSPaolo Bonzini     int n;
1880434e30aSPaolo Bonzini     int i;
1890434e30aSPaolo Bonzini 
1900434e30aSPaolo Bonzini     qdev_prop_set_uint32(priv, "num-cpu", s->num_cpu);
191306476eaSAndreas Färber     object_property_set_bool(OBJECT(&s->priv), true, "realized", &err);
192306476eaSAndreas Färber     if (err != NULL) {
193306476eaSAndreas Färber         error_propagate(errp, err);
194306476eaSAndreas Färber         return;
195306476eaSAndreas Färber     }
196306476eaSAndreas Färber     sysbus_pass_irq(sbd, SYS_BUS_DEVICE(&s->priv));
1970434e30aSPaolo Bonzini     for (i = 0; i < 32; i++) {
1980434e30aSPaolo Bonzini         s->cpuic[i] = qdev_get_gpio_in(priv, i);
1990434e30aSPaolo Bonzini     }
2000434e30aSPaolo Bonzini     /* ??? IRQ routing is hardcoded to "normal" mode.  */
2010434e30aSPaolo Bonzini     for (n = 0; n < 4; n++) {
202306476eaSAndreas Färber         object_property_set_bool(OBJECT(&s->gic[n]), true, "realized", &err);
203306476eaSAndreas Färber         if (err != NULL) {
204306476eaSAndreas Färber             error_propagate(errp, err);
205306476eaSAndreas Färber             return;
206306476eaSAndreas Färber         }
207306476eaSAndreas Färber         gic = DEVICE(&s->gic[n]);
208306476eaSAndreas Färber         gicbusdev = SYS_BUS_DEVICE(&s->gic[n]);
209306476eaSAndreas Färber         sysbus_mmio_map(gicbusdev, 0, 0x10040000 + n * 0x10000);
210306476eaSAndreas Färber         sysbus_connect_irq(gicbusdev, 0, s->cpuic[10 + n]);
2110434e30aSPaolo Bonzini         for (i = 0; i < 64; i++) {
2120434e30aSPaolo Bonzini             s->rvic[n][i] = qdev_get_gpio_in(gic, i);
2130434e30aSPaolo Bonzini         }
2140434e30aSPaolo Bonzini     }
21545c0a675SAndreas Färber     qdev_init_gpio_in(dev, mpcore_rirq_set_irq, 64);
216306476eaSAndreas Färber }
217306476eaSAndreas Färber 
218306476eaSAndreas Färber static void mpcore_rirq_init(Object *obj)
219306476eaSAndreas Färber {
220306476eaSAndreas Färber     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
221306476eaSAndreas Färber     mpcore_rirq_state *s = REALVIEW_MPCORE_RIRQ(obj);
222306476eaSAndreas Färber     SysBusDevice *privbusdev;
223306476eaSAndreas Färber     int i;
224306476eaSAndreas Färber 
225306476eaSAndreas Färber     object_initialize(&s->priv, sizeof(s->priv), TYPE_ARM11MPCORE_PRIV);
226306476eaSAndreas Färber     qdev_set_parent_bus(DEVICE(&s->priv), sysbus_get_default());
227306476eaSAndreas Färber     privbusdev = SYS_BUS_DEVICE(&s->priv);
228306476eaSAndreas Färber     sysbus_init_mmio(sbd, sysbus_mmio_get_region(privbusdev, 0));
229306476eaSAndreas Färber 
230306476eaSAndreas Färber     for (i = 0; i < 4; i++) {
231306476eaSAndreas Färber         object_initialize(&s->gic[i], sizeof(s->gic[i]), TYPE_REALVIEW_GIC);
232306476eaSAndreas Färber         qdev_set_parent_bus(DEVICE(&s->gic[i]), sysbus_get_default());
233306476eaSAndreas Färber     }
2340434e30aSPaolo Bonzini }
2350434e30aSPaolo Bonzini 
2360434e30aSPaolo Bonzini static Property mpcore_rirq_properties[] = {
2370434e30aSPaolo Bonzini     DEFINE_PROP_UINT32("num-cpu", mpcore_rirq_state, num_cpu, 1),
2380434e30aSPaolo Bonzini     DEFINE_PROP_END_OF_LIST(),
2390434e30aSPaolo Bonzini };
2400434e30aSPaolo Bonzini 
2410434e30aSPaolo Bonzini static void mpcore_rirq_class_init(ObjectClass *klass, void *data)
2420434e30aSPaolo Bonzini {
2430434e30aSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
2440434e30aSPaolo Bonzini 
245306476eaSAndreas Färber     dc->realize = realview_mpcore_realize;
2460434e30aSPaolo Bonzini     dc->props = mpcore_rirq_properties;
2470434e30aSPaolo Bonzini }
2480434e30aSPaolo Bonzini 
2490434e30aSPaolo Bonzini static const TypeInfo mpcore_rirq_info = {
25045c0a675SAndreas Färber     .name          = TYPE_REALVIEW_MPCORE_RIRQ,
2510434e30aSPaolo Bonzini     .parent        = TYPE_SYS_BUS_DEVICE,
2520434e30aSPaolo Bonzini     .instance_size = sizeof(mpcore_rirq_state),
253306476eaSAndreas Färber     .instance_init = mpcore_rirq_init,
2540434e30aSPaolo Bonzini     .class_init    = mpcore_rirq_class_init,
2550434e30aSPaolo Bonzini };
2560434e30aSPaolo Bonzini 
2570434e30aSPaolo Bonzini static Property mpcore_priv_properties[] = {
2580434e30aSPaolo Bonzini     DEFINE_PROP_UINT32("num-cpu", ARM11MPCorePriveState, num_cpu, 1),
2590434e30aSPaolo Bonzini     /* The ARM11 MPCORE TRM says the on-chip controller may have
2600434e30aSPaolo Bonzini      * anything from 0 to 224 external interrupt IRQ lines (with another
2610434e30aSPaolo Bonzini      * 32 internal). We default to 32+32, which is the number provided by
2620434e30aSPaolo Bonzini      * the ARM11 MPCore test chip in the Realview Versatile Express
2630434e30aSPaolo Bonzini      * coretile. Other boards may differ and should set this property
2640434e30aSPaolo Bonzini      * appropriately. Some Linux kernels may not boot if the hardware
2650434e30aSPaolo Bonzini      * has more IRQ lines than the kernel expects.
2660434e30aSPaolo Bonzini      */
2670434e30aSPaolo Bonzini     DEFINE_PROP_UINT32("num-irq", ARM11MPCorePriveState, num_irq, 64),
2680434e30aSPaolo Bonzini     DEFINE_PROP_END_OF_LIST(),
2690434e30aSPaolo Bonzini };
2700434e30aSPaolo Bonzini 
2710434e30aSPaolo Bonzini static void mpcore_priv_class_init(ObjectClass *klass, void *data)
2720434e30aSPaolo Bonzini {
2730434e30aSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
2740434e30aSPaolo Bonzini 
27508602ac5SAndreas Färber     dc->realize = mpcore_priv_realize;
2760434e30aSPaolo Bonzini     dc->props = mpcore_priv_properties;
2770434e30aSPaolo Bonzini }
2780434e30aSPaolo Bonzini 
2790434e30aSPaolo Bonzini static const TypeInfo mpcore_priv_info = {
28056fc0281SAndreas Färber     .name          = TYPE_ARM11MPCORE_PRIV,
2810434e30aSPaolo Bonzini     .parent        = TYPE_SYS_BUS_DEVICE,
2820434e30aSPaolo Bonzini     .instance_size = sizeof(ARM11MPCorePriveState),
2832c42c3a0SAndreas Färber     .instance_init = mpcore_priv_initfn,
2840434e30aSPaolo Bonzini     .class_init    = mpcore_priv_class_init,
2850434e30aSPaolo Bonzini };
2860434e30aSPaolo Bonzini 
2870434e30aSPaolo Bonzini static void arm11mpcore_register_types(void)
2880434e30aSPaolo Bonzini {
2890434e30aSPaolo Bonzini     type_register_static(&mpcore_rirq_info);
2900434e30aSPaolo Bonzini     type_register_static(&mpcore_priv_info);
2910434e30aSPaolo Bonzini }
2920434e30aSPaolo Bonzini 
2930434e30aSPaolo Bonzini type_init(arm11mpcore_register_types)
294