diff options
Diffstat (limited to 'hw/arm')
-rw-r--r-- | hw/arm/armsse.c | 206 |
1 files changed, 164 insertions, 42 deletions
diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index a2ae5d3c4b..5cb2b78b1f 100644 --- a/hw/arm/armsse.c +++ b/hw/arm/armsse.c @@ -21,18 +21,35 @@ struct ARMSSEInfo { const char *name; int sram_banks; + int num_cpus; }; static const ARMSSEInfo armsse_variants[] = { { .name = TYPE_IOTKIT, .sram_banks = 1, + .num_cpus = 1, }, }; /* Clock frequency in HZ of the 32KHz "slow clock" */ #define S32KCLK (32 * 1000) +/* Is internal IRQ n shared between CPUs in a multi-core SSE ? */ +static bool irq_is_common[32] = { + [0 ... 5] = true, + /* 6, 7: per-CPU MHU interrupts */ + [8 ... 12] = true, + /* 13: per-CPU icache interrupt */ + /* 14: reserved */ + [15 ... 20] = true, + /* 21: reserved */ + [22 ... 26] = true, + /* 27: reserved */ + /* 28, 29: per-CPU CTI interrupts */ + /* 30, 31: reserved */ +}; + /* Create an alias region of @size bytes starting at @base * which mirrors the memory starting at @orig. */ @@ -125,13 +142,18 @@ static void armsse_init(Object *obj) int i; assert(info->sram_banks <= MAX_SRAM_BANKS); + assert(info->num_cpus <= SSE_MAX_CPUS); memory_region_init(&s->container, obj, "armsse-container", UINT64_MAX); - sysbus_init_child_obj(obj, "armv7m", &s->armv7m, sizeof(s->armv7m), - TYPE_ARMV7M); - qdev_prop_set_string(DEVICE(&s->armv7m), "cpu-type", - ARM_CPU_TYPE_NAME("cortex-m33")); + for (i = 0; i < info->num_cpus; i++) { + char *name = g_strdup_printf("armv7m%d", i); + sysbus_init_child_obj(obj, name, &s->armv7m[i], sizeof(s->armv7m), + TYPE_ARMV7M); + qdev_prop_set_string(DEVICE(&s->armv7m[i]), "cpu-type", + ARM_CPU_TYPE_NAME("cortex-m33")); + g_free(name); + } sysbus_init_child_obj(obj, "secctl", &s->secctl, sizeof(s->secctl), TYPE_IOTKIT_SECCTL); @@ -192,13 +214,25 @@ static void armsse_init(Object *obj) TYPE_SPLIT_IRQ, &error_abort, NULL); g_free(name); } + if (info->num_cpus > 1) { + for (i = 0; i < ARRAY_SIZE(s->cpu_irq_splitter); i++) { + if (irq_is_common[i]) { + char *name = g_strdup_printf("cpu-irq-splitter%d", i); + SplitIRQ *splitter = &s->cpu_irq_splitter[i]; + + object_initialize_child(obj, name, splitter, sizeof(*splitter), + TYPE_SPLIT_IRQ, &error_abort, NULL); + g_free(name); + } + } + } } static void armsse_exp_irq(void *opaque, int n, int level) { - ARMSSE *s = ARMSSE(opaque); + qemu_irq *irqarray = opaque; - qemu_set_irq(s->exp_irqs[n], level); + qemu_set_irq(irqarray[n], level); } static void armsse_mpcexp_status(void *opaque, int n, int level) @@ -207,6 +241,26 @@ static void armsse_mpcexp_status(void *opaque, int n, int level) qemu_set_irq(s->mpcexp_status_in[n], level); } +static qemu_irq armsse_get_common_irq_in(ARMSSE *s, int irqno) +{ + /* + * Return a qemu_irq which can be used to signal IRQ n to + * all CPUs in the SSE. + */ + ARMSSEClass *asc = ARMSSE_GET_CLASS(s); + const ARMSSEInfo *info = asc->info; + + assert(irq_is_common[irqno]); + + if (info->num_cpus == 1) { + /* Only one CPU -- just connect directly to it */ + return qdev_get_gpio_in(DEVICE(&s->armv7m[0]), irqno); + } else { + /* Connect to the splitter which feeds all CPUs */ + return qdev_get_gpio_in(DEVICE(&s->cpu_irq_splitter[irqno]), 0); + } +} + static void armsse_realize(DeviceState *dev, Error **errp) { ARMSSE *s = ARMSSE(dev); @@ -280,37 +334,105 @@ static void armsse_realize(DeviceState *dev, Error **errp) memory_region_add_subregion_overlap(&s->container, 0, s->board_memory, -1); - qdev_prop_set_uint32(DEVICE(&s->armv7m), "num-irq", s->exp_numirq + 32); - /* In real hardware the initial Secure VTOR is set from the INITSVTOR0 - * register in the IoT Kit System Control Register block, and the - * initial value of that is in turn specifiable by the FPGA that - * instantiates the IoT Kit. In QEMU we don't implement this wrinkle, - * and simply set the CPU's init-svtor to the IoT Kit default value. - */ - qdev_prop_set_uint32(DEVICE(&s->armv7m), "init-svtor", 0x10000000); - object_property_set_link(OBJECT(&s->armv7m), OBJECT(&s->container), - "memory", &err); - if (err) { - error_propagate(errp, err); - return; - } - object_property_set_link(OBJECT(&s->armv7m), OBJECT(s), "idau", &err); - if (err) { - error_propagate(errp, err); - return; - } - object_property_set_bool(OBJECT(&s->armv7m), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; + for (i = 0; i < info->num_cpus; i++) { + DeviceState *cpudev = DEVICE(&s->armv7m[i]); + Object *cpuobj = OBJECT(&s->armv7m[i]); + int j; + char *gpioname; + + qdev_prop_set_uint32(cpudev, "num-irq", s->exp_numirq + 32); + /* + * In real hardware the initial Secure VTOR is set from the INITSVTOR0 + * register in the IoT Kit System Control Register block, and the + * initial value of that is in turn specifiable by the FPGA that + * instantiates the IoT Kit. In QEMU we don't implement this wrinkle, + * and simply set the CPU's init-svtor to the IoT Kit default value. + * In SSE-200 the situation is similar, except that the default value + * is a reset-time signal input. Typically a board using the SSE-200 + * will have a system control processor whose boot firmware initializes + * the INITSVTOR* registers before powering up the CPUs in any case, + * so the hardware's default value doesn't matter. QEMU doesn't emulate + * the control processor, so instead we behave in the way that the + * firmware does. All boards currently known about have firmware that + * sets the INITSVTOR0 and INITSVTOR1 registers to 0x10000000, like the + * IoTKit default. We can make this more configurable if necessary. + */ + qdev_prop_set_uint32(cpudev, "init-svtor", 0x10000000); + /* + * Start all CPUs except CPU0 powered down. In real hardware it is + * a configurable property of the SSE-200 which CPUs start powered up + * (via the CPUWAIT0_RST and CPUWAIT1_RST parameters), but since all + * the boards we care about start CPU0 and leave CPU1 powered off, + * we hard-code that for now. We can add QOM properties for this + * later if necessary. + */ + if (i > 0) { + object_property_set_bool(cpuobj, true, "start-powered-off", &err); + if (err) { + error_propagate(errp, err); + return; + } + } + object_property_set_link(cpuobj, OBJECT(&s->container), "memory", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_link(cpuobj, OBJECT(s), "idau", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(cpuobj, true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + /* Connect EXP_IRQ/EXP_CPUn_IRQ GPIOs to the NVIC's lines 32 and up */ + s->exp_irqs[i] = g_new(qemu_irq, s->exp_numirq); + for (j = 0; j < s->exp_numirq; j++) { + s->exp_irqs[i][j] = qdev_get_gpio_in(cpudev, i + 32); + } + if (i == 0) { + gpioname = g_strdup("EXP_IRQ"); + } else { + gpioname = g_strdup_printf("EXP_CPU%d_IRQ", i); + } + qdev_init_gpio_in_named_with_opaque(dev, armsse_exp_irq, + s->exp_irqs[i], + gpioname, s->exp_numirq); + g_free(gpioname); } - /* Connect our EXP_IRQ GPIOs to the NVIC's lines 32 and up. */ - s->exp_irqs = g_new(qemu_irq, s->exp_numirq); - for (i = 0; i < s->exp_numirq; i++) { - s->exp_irqs[i] = qdev_get_gpio_in(DEVICE(&s->armv7m), i + 32); + /* Wire up the splitters that connect common IRQs to all CPUs */ + if (info->num_cpus > 1) { + for (i = 0; i < ARRAY_SIZE(s->cpu_irq_splitter); i++) { + if (irq_is_common[i]) { + Object *splitter = OBJECT(&s->cpu_irq_splitter[i]); + DeviceState *devs = DEVICE(splitter); + int cpunum; + + object_property_set_int(splitter, info->num_cpus, + "num-lines", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(splitter, true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + for (cpunum = 0; cpunum < info->num_cpus; cpunum++) { + DeviceState *cpudev = DEVICE(&s->armv7m[cpunum]); + + qdev_connect_gpio_out(devs, cpunum, + qdev_get_gpio_in(cpudev, i)); + } + } + } } - qdev_init_gpio_in_named(dev, armsse_exp_irq, "EXP_IRQ", s->exp_numirq); /* Set up the big aliases first */ make_alias(s, &s->alias1, "alias 1", 0x10000000, 0x10000000, 0x00000000); @@ -407,7 +529,7 @@ static void armsse_realize(DeviceState *dev, Error **errp) return; } qdev_connect_gpio_out(DEVICE(&s->mpc_irq_orgate), 0, - qdev_get_gpio_in(DEVICE(&s->armv7m), 9)); + armsse_get_common_irq_in(s, 9)); /* Devices behind APB PPC0: * 0x40000000: timer0 @@ -424,7 +546,7 @@ static void armsse_realize(DeviceState *dev, Error **errp) return; } sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer0), 0, - qdev_get_gpio_in(DEVICE(&s->armv7m), 3)); + armsse_get_common_irq_in(s, 3)); mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->timer0), 0); object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[0]", &err); if (err) { @@ -439,7 +561,7 @@ static void armsse_realize(DeviceState *dev, Error **errp) return; } sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer1), 0, - qdev_get_gpio_in(DEVICE(&s->armv7m), 4)); + armsse_get_common_irq_in(s, 4)); mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->timer1), 0); object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[1]", &err); if (err) { @@ -455,7 +577,7 @@ static void armsse_realize(DeviceState *dev, Error **errp) return; } sysbus_connect_irq(SYS_BUS_DEVICE(&s->dualtimer), 0, - qdev_get_gpio_in(DEVICE(&s->armv7m), 5)); + armsse_get_common_irq_in(s, 5)); mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dualtimer), 0); object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[2]", &err); if (err) { @@ -513,7 +635,7 @@ static void armsse_realize(DeviceState *dev, Error **errp) return; } qdev_connect_gpio_out(DEVICE(&s->ppc_irq_orgate), 0, - qdev_get_gpio_in(DEVICE(&s->armv7m), 10)); + armsse_get_common_irq_in(s, 10)); /* 0x40010000 .. 0x4001ffff: private CPU region: unused in IoTKit */ @@ -528,7 +650,7 @@ static void armsse_realize(DeviceState *dev, Error **errp) return; } sysbus_connect_irq(SYS_BUS_DEVICE(&s->s32ktimer), 0, - qdev_get_gpio_in(DEVICE(&s->armv7m), 2)); + armsse_get_common_irq_in(s, 2)); mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->s32ktimer), 0); object_property_set_link(OBJECT(&s->apb_ppc1), OBJECT(mr), "port[0]", &err); if (err) { @@ -609,7 +731,7 @@ static void armsse_realize(DeviceState *dev, Error **errp) return; } sysbus_connect_irq(SYS_BUS_DEVICE(&s->nswatchdog), 0, - qdev_get_gpio_in(DEVICE(&s->armv7m), 1)); + armsse_get_common_irq_in(s, 1)); sysbus_mmio_map(SYS_BUS_DEVICE(&s->nswatchdog), 0, 0x40081000); qdev_prop_set_uint32(DEVICE(&s->swatchdog), "wdogclk-frq", s->mainclk_frq); @@ -715,7 +837,7 @@ static void armsse_realize(DeviceState *dev, Error **errp) qdev_pass_gpios(dev_secctl, dev, "mscexp_clear"); qdev_pass_gpios(dev_secctl, dev, "mscexp_ns"); qdev_connect_gpio_out_named(dev_secctl, "msc_irq", 0, - qdev_get_gpio_in(DEVICE(&s->armv7m), 11)); + armsse_get_common_irq_in(s, 11)); /* * Expose our container region to the board model; this corresponds |