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
> 

Reply via email to