summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--hw/arm/armsse.c206
-rw-r--r--include/hw/arm/armsse.h21
2 files changed, 180 insertions, 47 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
diff --git a/include/hw/arm/armsse.h b/include/hw/arm/armsse.h
index e4a0501331..faf5dfed25 100644
--- a/include/hw/arm/armsse.h
+++ b/include/hw/arm/armsse.h
@@ -28,9 +28,16 @@
* + QOM property "memory" is a MemoryRegion containing the devices provided
* by the board model.
* + QOM property "MAINCLK" is the frequency of the main system clock
- * + QOM property "EXP_NUMIRQ" sets the number of expansion interrupts
- * + Named GPIO inputs "EXP_IRQ" 0..n are the expansion interrupts, which
- * are wired to the NVIC lines 32 .. n+32
+ * + QOM property "EXP_NUMIRQ" sets the number of expansion interrupts.
+ * (In hardware, the SSE-200 permits the number of expansion interrupts
+ * for the two CPUs to be configured separately, but we restrict it to
+ * being the same for both, to avoid having to have separate Property
+ * lists for different variants. This restriction can be relaxed later
+ * if necessary.)
+ * + Named GPIO inputs "EXP_IRQ" 0..n are the expansion interrupts for CPU 0,
+ * which are wired to its NVIC lines 32 .. n+32
+ * + Named GPIO inputs "EXP_CPU1_IRQ" 0..n are the expansion interrupts for
+ * CPU 1, which are wired to its NVIC lines 32 .. n+32
* + sysbus MMIO region 0 is the "AHB Slave Expansion" which allows
* bus master devices in the board model to make transactions into
* all the devices and memory areas in the IoTKit
@@ -95,12 +102,14 @@
#error Too many SRAM banks
#endif
+#define SSE_MAX_CPUS 2
+
typedef struct ARMSSE {
/*< private >*/
SysBusDevice parent_obj;
/*< public >*/
- ARMv7MState armv7m;
+ ARMv7MState armv7m[SSE_MAX_CPUS];
IoTKitSecCtl secctl;
TZPPC apb_ppc0;
TZPPC apb_ppc1;
@@ -115,6 +124,8 @@ typedef struct ARMSSE {
qemu_or_irq mpc_irq_orgate;
qemu_or_irq nmi_orgate;
+ SplitIRQ cpu_irq_splitter[32];
+
CMSDKAPBDualTimer dualtimer;
CMSDKAPBWatchdog s32kwatchdog;
@@ -130,7 +141,7 @@ typedef struct ARMSSE {
MemoryRegion alias3;
MemoryRegion sram[MAX_SRAM_BANKS];
- qemu_irq *exp_irqs;
+ qemu_irq *exp_irqs[SSE_MAX_CPUS];
qemu_irq ppc0_irq;
qemu_irq ppc1_irq;
qemu_irq sec_resp_cfg;