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.michel@amd.com>
Reviewed-by: Francisco Iglesias <francisco.iglesias@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
>