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