On Wed, Jul 16, 2025 at 11:54:07AM +0200, Luc Michel wrote: > The Versal SoC contains two GICs: one GICv3 in the APU and one GICv2 in > the RPU (currently not instantiated). To prepare for the GICv2 > instantiation, add support for multiple GICs when connecting interrupts. > > When a GIC is created, the first-cpu-index property is set on it, and a > pointer to the GIC is stored in the intc array. When connecting an IRQ, > a TYPE_SPLIT_IRQ device is created with its num-lines property set to > the number of GICs in the SoC. The split device is used to fan out the > IRQ to all the GICs. > > Signed-off-by: Luc Michel <luc.mic...@amd.com>
Reviewed-by: Francisco Iglesias <francisco.igles...@amd.com> > --- > include/hw/arm/xlnx-versal.h | 1 + > hw/arm/xlnx-versal.c | 55 +++++++++++++++++++++++++++++++++--- > 2 files changed, 52 insertions(+), 4 deletions(-) > > diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h > index ba5719d80f5..9b11ffb845d 100644 > --- a/include/hw/arm/xlnx-versal.h > +++ b/include/hw/arm/xlnx-versal.h > @@ -40,10 +40,11 @@ OBJECT_DECLARE_TYPE(Versal, VersalClass, XLNX_VERSAL_BASE) > struct Versal { > /*< private >*/ > SysBusDevice parent_obj; > > /*< public >*/ > + GArray *intc; > MemoryRegion mr_ps; > > struct { > /* 4 ranges to access DDR. */ > MemoryRegion mr_ddr_ranges[4]; > diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c > index ba8c69bd435..58cd874f81f 100644 > --- a/hw/arm/xlnx-versal.c > +++ b/hw/arm/xlnx-versal.c > @@ -43,10 +43,11 @@ > #include "hw/misc/xlnx-versal-cframe-reg.h" > #include "hw/or-irq.h" > #include "hw/misc/xlnx-versal-crl.h" > #include "hw/intc/arm_gicv3_common.h" > #include "hw/intc/arm_gicv3_its_common.h" > +#include "hw/core/split-irq.h" > > #define XLNX_VERSAL_ACPU_TYPE ARM_CPU_TYPE_NAME("cortex-a72") > #define XLNX_VERSAL_RCPU_TYPE ARM_CPU_TYPE_NAME("cortex-r5f") > #define GEM_REVISION 0x40070106 > > @@ -318,10 +319,47 @@ static inline Object *versal_get_child_idx(Versal *s, > const char *child, > g_autofree char *n = g_strdup_printf("%s[%zu]", child, idx); > > return versal_get_child(s, n); > } > > +/* > + * The SoC embeds multiple GICs. They all receives the same IRQ lines at the > + * same index. This function creates a TYPE_SPLIT_IRQ device to fan out the > + * given IRQ input to all the GICs. > + * > + * The TYPE_SPLIT_IRQ devices lie in the /soc/irq-splits QOM container > + */ > +static qemu_irq versal_get_gic_irq(Versal *s, int irq_idx) > +{ > + DeviceState *split; > + Object *container = versal_get_child(s, "irq-splits"); > + int idx = FIELD_EX32(irq_idx, VERSAL_IRQ, IRQ); > + g_autofree char *name = g_strdup_printf("irq[%d]", idx); > + > + split = DEVICE(object_resolve_path_at(container, name)); > + > + if (split == NULL) { > + size_t i; > + > + split = qdev_new(TYPE_SPLIT_IRQ); > + qdev_prop_set_uint16(split, "num-lines", s->intc->len); > + object_property_add_child(container, name, OBJECT(split)); > + qdev_realize_and_unref(split, NULL, &error_abort); > + > + for (i = 0; i < s->intc->len; i++) { > + DeviceState *gic; > + > + gic = g_array_index(s->intc, DeviceState *, i); > + qdev_connect_gpio_out(split, i, qdev_get_gpio_in(gic, idx)); > + } > + } else { > + g_assert(FIELD_EX32(irq_idx, VERSAL_IRQ, ORED)); > + } > + > + return qdev_get_gpio_in(split, 0); > +} > + > /* > * 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. > * > @@ -354,16 +392,14 @@ static qemu_irq versal_get_irq_or_gate_in(Versal *s, > int irq_idx, > > static qemu_irq versal_get_irq(Versal *s, int irq_idx) > { > qemu_irq irq; > bool ored; > - DeviceState *gic; > > ored = FIELD_EX32(irq_idx, VERSAL_IRQ, ORED); > > - gic = DEVICE(versal_get_child_idx(s, "apu-gic", 0)); > - irq = qdev_get_gpio_in(gic, irq_idx); > + irq = versal_get_gic_irq(s, irq_idx); > > if (ored) { > irq = versal_get_irq_or_gate_in(s, irq_idx, irq); > } > > @@ -502,10 +538,11 @@ static void versal_create_gic_its(Versal *s, > } > > static DeviceState *versal_create_gic(Versal *s, > const VersalCpuClusterMap *map, > MemoryRegion *mr, > + int first_cpu_idx, > size_t num_cpu) > { > DeviceState *dev; > SysBusDevice *sbd; > QList *redist_region_count; > @@ -526,10 +563,11 @@ static DeviceState *versal_create_gic(Versal *s, > qdev_prop_set_array(dev, "redist-region-count", redist_region_count); > > qdev_prop_set_bit(dev, "has-security-extensions", true); > qdev_prop_set_bit(dev, "has-lpi", map->gic.has_its); > object_property_set_link(OBJECT(dev), "sysmem", OBJECT(mr), > &error_abort); > + qdev_prop_set_uint32(dev, "first-cpu-index", first_cpu_idx); > > sysbus_realize_and_unref(sbd, &error_fatal); > > memory_region_add_subregion(mr, map->gic.dist, > sysbus_mmio_get_region(sbd, 0)); > @@ -552,10 +590,12 @@ static DeviceState *versal_create_gic(Versal *s, > qemu_fdt_setprop(s->cfg.fdt, node, "interrupt-controller", NULL, 0); > } > > versal_create_gic_its(s, map, dev, mr, node); > > + g_array_append_val(s->intc, dev); > + > return dev; > } > > static void connect_gic_to_cpu(const VersalCpuClusterMap *map, > DeviceState *gic, DeviceState *cpu, size_t > idx, > @@ -609,13 +649,15 @@ static inline void versal_create_and_connect_gic(Versal > *s, > MemoryRegion *mr, > DeviceState **cpus, > size_t num_cpu) > { > DeviceState *gic; > + int first_cpu_idx; > size_t i; > > - gic = versal_create_gic(s, map, mr, num_cpu); > + first_cpu_idx = CPU(cpus[0])->cpu_index; > + gic = versal_create_gic(s, map, mr, first_cpu_idx, num_cpu); > > for (i = 0; i < num_cpu; i++) { > connect_gic_to_cpu(map, gic, cpus[i], i, num_cpu); > } > } > @@ -1540,10 +1582,14 @@ static void versal_realize(DeviceState *dev, Error > **errp) > > s->phandle.clk_25mhz = fdt_add_clk_node(s, "/clk25", 25 * 1000 * 1000); > s->phandle.clk_125mhz = fdt_add_clk_node(s, "/clk125", 125 * 1000 * > 1000); > s->phandle.gic = qemu_fdt_alloc_phandle(s->cfg.fdt); > > + container = object_new(TYPE_CONTAINER); > + object_property_add_child(OBJECT(s), "irq-splits", container); > + object_unref(container); > + > container = object_new(TYPE_CONTAINER); > object_property_add_child(OBJECT(s), "irq-or-gates", container); > object_unref(container); > > qemu_fdt_setprop_cell(s->cfg.fdt, "/", "interrupt-parent", > s->phandle.gic); > @@ -1710,10 +1756,11 @@ static void versal_base_init(Object *obj) > > memory_region_init(&s->lpd.rpu.mr, obj, "mr-rpu", UINT64_MAX); > memory_region_init(&s->mr_ps, obj, "mr-ps-switch", UINT64_MAX); > memory_region_init_alias(&s->lpd.rpu.mr_ps_alias, OBJECT(s), > "mr-rpu-ps-alias", &s->mr_ps, 0, UINT64_MAX); > + s->intc = g_array_new(false, false, sizeof(DeviceState *)); > > num_can = versal_get_map(s)->num_canfd; > s->cfg.canbus = g_new0(CanBusState *, num_can); > > for (i = 0; i < num_can; i++) { > -- > 2.50.0 >