diff options
Diffstat (limited to 'hw/arm/xlnx-versal.c')
| -rw-r--r-- | hw/arm/xlnx-versal.c | 63 |
1 files changed, 62 insertions, 1 deletions
diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index 964250bf15..d3a084a063 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -43,6 +43,17 @@ #define VERSAL_NUM_PMC_APB_IRQS 18 #define NUM_OSPI_IRQ_LINES 3 +/* + * IRQ descriptor to catch the following cases: + * - Multiple devices can connect to the same IRQ. They are OR'ed together. + */ +FIELD(VERSAL_IRQ, IRQ, 0, 16) +FIELD(VERSAL_IRQ, ORED, 18, 1) +FIELD(VERSAL_IRQ, OR_IDX, 19, 4) /* input index on the IRQ OR gate */ + +#define OR_IRQ(irq, or_idx) \ + (R_VERSAL_IRQ_ORED_MASK | ((or_idx) << R_VERSAL_IRQ_OR_IDX_SHIFT) | (irq)) + typedef struct VersalSimplePeriphMap { uint64_t addr; int irq; @@ -174,9 +185,53 @@ static inline Object *versal_get_child_idx(Versal *s, const char *child, return versal_get_child(s, n); } +/* + * When the R_VERSAL_IRQ_ORED flag is set on an IRQ descriptor, this function is + * used to return the corresponding or gate input IRQ. The or gate is created if + * not already existant. + * + * Or gates are placed under the /soc/irq-or-gates QOM container. + */ +static qemu_irq versal_get_irq_or_gate_in(Versal *s, int irq_idx, + qemu_irq target_irq) +{ + Object *container = versal_get_child(s, "irq-or-gates"); + DeviceState *dev; + g_autofree char *name; + int idx, or_idx; + + idx = FIELD_EX32(irq_idx, VERSAL_IRQ, IRQ); + or_idx = FIELD_EX32(irq_idx, VERSAL_IRQ, OR_IDX); + + name = g_strdup_printf("irq[%d]", idx); + dev = DEVICE(object_resolve_path_at(container, name)); + + if (dev == NULL) { + dev = qdev_new(TYPE_OR_IRQ); + object_property_add_child(container, name, OBJECT(dev)); + qdev_prop_set_uint16(dev, "num-lines", 1 << R_VERSAL_IRQ_OR_IDX_LENGTH); + qdev_realize_and_unref(dev, NULL, &error_abort); + qdev_connect_gpio_out(dev, 0, target_irq); + } + + return qdev_get_gpio_in(dev, or_idx); +} + static qemu_irq versal_get_irq(Versal *s, int irq_idx) { - return qdev_get_gpio_in(DEVICE(&s->fpd.apu.gic), irq_idx); + qemu_irq irq; + bool ored; + + ored = FIELD_EX32(irq_idx, VERSAL_IRQ, ORED); + + irq = qdev_get_gpio_in(DEVICE(&s->fpd.apu.gic), + FIELD_EX32(irq_idx, VERSAL_IRQ, IRQ)); + + if (ored) { + irq = versal_get_irq_or_gate_in(s, irq_idx, irq); + } + + return irq; } static void versal_sysbus_connect_irq(Versal *s, SysBusDevice *sbd, @@ -1217,6 +1272,7 @@ static void versal_realize(DeviceState *dev, Error **errp) { Versal *s = XLNX_VERSAL_BASE(dev); qemu_irq pic[XLNX_VERSAL_NR_IRQS]; + Object *container; const VersalMap *map = versal_get_map(s); size_t i; @@ -1227,6 +1283,11 @@ static void versal_realize(DeviceState *dev, Error **errp) versal_create_apu_cpus(s); versal_create_apu_gic(s, pic); + + container = object_new(TYPE_CONTAINER); + object_property_add_child(OBJECT(s), "irq-or-gates", container); + object_unref(container); + versal_create_rpu_cpus(s); for (i = 0; i < map->num_uart; i++) { |