linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/7] irqchip/gic-v5: Code first ACPI boot support
@ 2025-12-18 10:14 Lorenzo Pieralisi
  2025-12-18 10:14 ` [PATCH v2 1/7] ACPICA: Add GICv5 MADT structures Lorenzo Pieralisi
                   ` (6 more replies)
  0 siblings, 7 replies; 18+ messages in thread
From: Lorenzo Pieralisi @ 2025-12-18 10:14 UTC (permalink / raw)
  To: Rafael J. Wysocki, Len Brown, Robert Moore, Thomas Gleixner,
	Hanjun Guo, Sudeep Holla, Marc Zyngier, Bjorn Helgaas
  Cc: linux-acpi, acpica-devel, linux-kernel, linux-arm-kernel,
	linux-pci, Lorenzo Pieralisi, Jose Marinho

The ACPI and ACPI IORT specifications were updated to support bindings
required to describe GICv5 based systems.

The ACPI specification GICv5 bindings ECR [1] were approved and the
required changes merged in the ACPICA upstream repository[5].

The Arm IORT specification [2] has been updated to include GICv5 IWB
specific bindings in revision E.g.

Implement kernel code that - based on the aforementioned bindings - adds
support for GICv5 ACPI probing.

ACPICA changes supporting the bindings are posted with the series; they
were cherry-picked from the upcoming ACPICA Linuxised release patches
and they should _not_ be merged in any upstream branch because the
full set of Linuxised ACPICA changes will be subsequently posted in
order to be merged, I added the two ACPICA patches to make the series
self-contained.

The ACPI bindings were prototyped in edk2 - code available in these
branches [3][4].

===========================
Kernel implementation notes
===========================

IRS and ITS probing is triggered using the standard irqchip ACPI probing
mechanism - there is no significant difference compared to previous GIC
versions other.

The only difference that is worth noting is that GICv3/4 systems include a
single MADT component describing the interrupt controller (ie GIC distributor)
whereas GICv5 systems include one or more IRSes. The probing code is
implemented so that an MADT IRS detection triggers probing for all IRSes
in one go.

The IWB driver probes like any other ACPI device. IORT code is updated so
that a deviceID for the IWB can be detected.

The only major change compared to GICv3/4 systems is the GSI namespace that
is split between PPI/SPI IRQs and IWB backed IRQs.

The main GSI handle - to map an IRQ - has to detect whether to look-up
using the top level GSI domain or an IWB domain in that the two IRQ
namespaces are decoupled.

IORT code implements the logic to retrieve an IWB domain by looking up its
IWB frame id, as described in [1].

Most important implementation detail worth noting is that - at this stage -
ACPI code is not capable of handling devices probe order IRQ dependency on
the interrupt controller driver their IRQ is routed to.

This is not an issue on GICv3/4 systems in that the full GIC hierarchy
probes earlier than any other device, so by the time IRQs mappings have to
be carried out (ie acpi_register_gsi()) the GIC drivers have already
probed.

On GICv5 systems, the IWB is modelled as a device and its device driver
probes at device_initcall time. That's when the IWB IRQ domain is actually
registered - which poses problems for devices whose IRQs are IWB routed and
require to resolve the IRQ mapping before the IWB driver has a chance to
probe.

Work on resolving devices<->IWB probe order dependency has started in
parallel with this series and will be posted shortly.

For PPI/SPI/LPI backed IRQs the probe dependency is not a problem because
in GICv5 systems the IRSes and ITSes probe early so their IRQ domain are
set in place before devices require IRQ mappings.

ACPICA patches are a Linuxised version of ACPICA GICv5 upstream changes
[5] and should not be considered for merging because they would conflict
with the full ACPICA release changes patchset that will be posted later
in this dev cycle (owing to patch dependencies in the ACPICA commit
history) they are there so that the patch series is self-contained.

[1] https://github.com/tianocore/edk2/issues/11148
[2] https://developer.arm.com/documentation/den0049/eg
[3] https://github.com/LeviYeoReum/edk2/tree/levi/gicv5_patch
[4] https://github.com/LeviYeoReum/edk2-platforms/tree/levi/gicv5_patch
[5] https://github.com/acpica/acpica/commits/master/

Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
---
Changes in v2:
- Cherry-picked ACPICA upstream changes
- Minor editorial changes
- Removed the "not for merging" tag because now ACPI specs are approved
- Rebased against v6.19-rc1
- Link to v1: https://lore.kernel.org/r/20251028-gicv5-host-acpi-v1-0-01a862feb5ca@kernel.org

---
Jose Marinho (2):
      ACPICA: Add GICv5 MADT structures
      ACPICA: Add Arm IORT IWB node definitions

Lorenzo Pieralisi (5):
      irqdomain: Add parent field to struct irqchip_fwid
      PCI/MSI: Make the pci_msi_map_rid_ctlr_node() interface firmware agnostic
      irqchip/gic-v5: Add ACPI IRS probing
      irqchip/gic-v5: Add ACPI ITS probing
      irqchip/gic-v5: Add ACPI IWB probing

 drivers/acpi/arm64/iort.c                | 190 +++++++++++++++++++-----
 drivers/acpi/bus.c                       |   3 +
 drivers/irqchip/irq-gic-its-msi-parent.c |  43 +++---
 drivers/irqchip/irq-gic-v5-irs.c         | 246 ++++++++++++++++++++++++-------
 drivers/irqchip/irq-gic-v5-its.c         | 132 ++++++++++++++++-
 drivers/irqchip/irq-gic-v5-iwb.c         |  42 ++++--
 drivers/irqchip/irq-gic-v5.c             | 138 ++++++++++++++---
 drivers/pci/msi/irqdomain.c              |  24 ++-
 include/acpi/actbl2.h                    |  56 ++++++-
 include/linux/acpi.h                     |   1 +
 include/linux/acpi_iort.h                |  11 +-
 include/linux/irqchip/arm-gic-v5.h       |   8 +
 include/linux/irqdomain.h                |  30 +++-
 include/linux/msi.h                      |   3 +-
 kernel/irq/irqdomain.c                   |  14 +-
 15 files changed, 784 insertions(+), 157 deletions(-)
---
base-commit: 8f0b4cce4481fb22653697cced8d0d04027cb1e8
change-id: 20251022-gicv5-host-acpi-d59d6c1d3d07

Best regards,
-- 
Lorenzo Pieralisi <lpieralisi@kernel.org>



^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH v2 1/7] ACPICA: Add GICv5 MADT structures
  2025-12-18 10:14 [PATCH v2 0/7] irqchip/gic-v5: Code first ACPI boot support Lorenzo Pieralisi
@ 2025-12-18 10:14 ` Lorenzo Pieralisi
  2025-12-18 10:14 ` [PATCH v2 2/7] ACPICA: Add Arm IORT IWB node definitions Lorenzo Pieralisi
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 18+ messages in thread
From: Lorenzo Pieralisi @ 2025-12-18 10:14 UTC (permalink / raw)
  To: Rafael J. Wysocki, Len Brown, Robert Moore, Thomas Gleixner,
	Hanjun Guo, Sudeep Holla, Marc Zyngier, Bjorn Helgaas
  Cc: linux-acpi, acpica-devel, linux-kernel, linux-arm-kernel,
	linux-pci, Lorenzo Pieralisi, Jose Marinho

From: Jose Marinho <jose.marinho@arm.com>

The GICv5 adds the following MADT structures:
- IRS
- ITS Config Frame
- ITS Translate Frame

The ACPI spec ECR is at https://github.com/tianocore/edk2/issues/11148

Link: https://github.com/acpica/acpica/commit/69cca52d
Signed-off-by: Jose Marinho <jose.marinho@arm.com>
Signed-off-by: Bob Moore <robert.moore@intel.com>
Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
---
 include/acpi/actbl2.h | 49 +++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 45 insertions(+), 4 deletions(-)

diff --git a/include/acpi/actbl2.h b/include/acpi/actbl2.h
index f726bce3eb84..fdabc6c64a9c 100644
--- a/include/acpi/actbl2.h
+++ b/include/acpi/actbl2.h
@@ -1167,7 +1167,10 @@ enum acpi_madt_type {
 	ACPI_MADT_TYPE_IMSIC = 25,
 	ACPI_MADT_TYPE_APLIC = 26,
 	ACPI_MADT_TYPE_PLIC = 27,
-	ACPI_MADT_TYPE_RESERVED = 28,	/* 28 to 0x7F are reserved */
+	ACPI_MADT_TYPE_GICV5_IRS = 28,
+	ACPI_MADT_TYPE_GICV5_ITS = 29,
+	ACPI_MADT_TYPE_GICV5_ITS_TRANSLATE = 30,
+	ACPI_MADT_TYPE_RESERVED = 31,	/* 31 to 0x7F are reserved */
 	ACPI_MADT_TYPE_OEM_RESERVED = 0x80	/* 0x80 to 0xFF are reserved for OEM use */
 };
 
@@ -1289,7 +1292,7 @@ struct acpi_madt_local_x2apic_nmi {
 	u8 reserved[3];		/* reserved - must be zero */
 };
 
-/* 11: Generic interrupt - GICC (ACPI 5.0 + ACPI 6.0 + ACPI 6.3 + ACPI 6.5 changes) */
+/* 11: Generic interrupt - GICC (ACPI 5.0 + ACPI 6.0 + ACPI 6.3 + ACPI 6.5 + ACPI 6.7 changes) */
 
 struct acpi_madt_generic_interrupt {
 	struct acpi_subtable_header header;
@@ -1310,6 +1313,8 @@ struct acpi_madt_generic_interrupt {
 	u8 reserved2[1];
 	u16 spe_interrupt;	/* ACPI 6.3 */
 	u16 trbe_interrupt;	/* ACPI 6.5 */
+	u16 iaffid;		/* ACPI 6.7 */
+	u32 irs_id;
 };
 
 /* Masks for Flags field above */
@@ -1332,7 +1337,7 @@ struct acpi_madt_generic_distributor {
 	u8 reserved2[3];	/* reserved - must be zero */
 };
 
-/* Values for Version field above */
+/* Values for Version field above and Version field in acpi_madt_gicv5_irs */
 
 enum acpi_madt_gic_version {
 	ACPI_MADT_GIC_VERSION_NONE = 0,
@@ -1340,7 +1345,8 @@ enum acpi_madt_gic_version {
 	ACPI_MADT_GIC_VERSION_V2 = 2,
 	ACPI_MADT_GIC_VERSION_V3 = 3,
 	ACPI_MADT_GIC_VERSION_V4 = 4,
-	ACPI_MADT_GIC_VERSION_RESERVED = 5	/* 5 and greater are reserved */
+	ACPI_MADT_GIC_VERSION_V5 = 5,
+	ACPI_MADT_GIC_VERSION_RESERVED = 6	/* 6 and greater are reserved */
 };
 
 /* 13: Generic MSI Frame (ACPI 5.1) */
@@ -1611,6 +1617,41 @@ struct acpi_madt_plic {
 	u32 gsi_base;
 };
 
+/* 28: Arm GICv5 IRS (ACPI 6.7) */
+struct acpi_madt_gicv5_irs {
+	struct acpi_subtable_header header;
+	u8 version;
+	u8 reserved;
+	u32 irs_id;
+	u32 flags;
+	u32 reserved2;
+	u64 config_base_address;
+	u64 setlpi_base_address;
+};
+
+#define ACPI_MADT_IRS_NON_COHERENT      (1)
+
+/* 29: Arm GICv5 ITS Config Frame (ACPI 6.7) */
+struct acpi_madt_gicv5_translator {
+	struct acpi_subtable_header header;
+	u8 flags;
+	u8 reserved;		/* reserved - must be zero */
+	u32 translator_id;
+	u64 base_address;
+};
+
+#define ACPI_MADT_GICV5_ITS_NON_COHERENT      (1)
+
+/* 30: Arm GICv5 ITS Translate Frame (ACPI 6.7) */
+struct acpi_madt_gicv5_translate_frame {
+	struct acpi_subtable_header header;
+	u16 reserved;		/* reserved - must be zero */
+	u32 linked_translator_id;
+	u32 translate_frame_id;
+	u32 reserved2;
+	u64 base_address;
+};
+
 /* 80: OEM data */
 
 struct acpi_madt_oem_data {

-- 
2.50.1



^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v2 2/7] ACPICA: Add Arm IORT IWB node definitions
  2025-12-18 10:14 [PATCH v2 0/7] irqchip/gic-v5: Code first ACPI boot support Lorenzo Pieralisi
  2025-12-18 10:14 ` [PATCH v2 1/7] ACPICA: Add GICv5 MADT structures Lorenzo Pieralisi
@ 2025-12-18 10:14 ` Lorenzo Pieralisi
  2025-12-18 10:14 ` [PATCH v2 3/7] irqdomain: Add parent field to struct irqchip_fwid Lorenzo Pieralisi
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 18+ messages in thread
From: Lorenzo Pieralisi @ 2025-12-18 10:14 UTC (permalink / raw)
  To: Rafael J. Wysocki, Len Brown, Robert Moore, Thomas Gleixner,
	Hanjun Guo, Sudeep Holla, Marc Zyngier, Bjorn Helgaas
  Cc: linux-acpi, acpica-devel, linux-kernel, linux-arm-kernel,
	linux-pci, Lorenzo Pieralisi, Jose Marinho

From: Jose Marinho <jose.marinho@arm.com>

The IORT IUWB node is defined in IORT issue E.g
See https://developer.arm.com/documentation/den0049/eg

Link: https://github.com/acpica/acpica/commit/a90dc2f5
Signed-off-by: Jose Marinho <jose.marinho@arm.com>
Signed-off-by: Bob Moore <robert.moore@intel.com>
Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
---
 include/acpi/actbl2.h | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/include/acpi/actbl2.h b/include/acpi/actbl2.h
index fdabc6c64a9c..ba1a36d23e32 100644
--- a/include/acpi/actbl2.h
+++ b/include/acpi/actbl2.h
@@ -680,6 +680,7 @@ enum acpi_iort_node_type {
 	ACPI_IORT_NODE_SMMU_V3 = 0x04,
 	ACPI_IORT_NODE_PMCG = 0x05,
 	ACPI_IORT_NODE_RMR = 0x06,
+	ACPI_IORT_NODE_IWB = 0x07,
 };
 
 struct acpi_iort_id_mapping {
@@ -858,6 +859,12 @@ struct acpi_iort_rmr_desc {
 	u32 reserved;
 };
 
+struct acpi_iort_iwb {
+	u64 base_address;
+	u16 iwb_index;		/* Unique IWB identifier matching with the IWB GSI namespace. */
+	char device_name[];	/* Path of the IWB namespace object */
+};
+
 /*******************************************************************************
  *
  * IVRS - I/O Virtualization Reporting Structure

-- 
2.50.1



^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v2 3/7] irqdomain: Add parent field to struct irqchip_fwid
  2025-12-18 10:14 [PATCH v2 0/7] irqchip/gic-v5: Code first ACPI boot support Lorenzo Pieralisi
  2025-12-18 10:14 ` [PATCH v2 1/7] ACPICA: Add GICv5 MADT structures Lorenzo Pieralisi
  2025-12-18 10:14 ` [PATCH v2 2/7] ACPICA: Add Arm IORT IWB node definitions Lorenzo Pieralisi
@ 2025-12-18 10:14 ` Lorenzo Pieralisi
  2026-01-05 12:01   ` Jonathan Cameron
  2025-12-18 10:14 ` [PATCH v2 4/7] PCI/MSI: Make the pci_msi_map_rid_ctlr_node() interface firmware agnostic Lorenzo Pieralisi
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 18+ messages in thread
From: Lorenzo Pieralisi @ 2025-12-18 10:14 UTC (permalink / raw)
  To: Rafael J. Wysocki, Len Brown, Robert Moore, Thomas Gleixner,
	Hanjun Guo, Sudeep Holla, Marc Zyngier, Bjorn Helgaas
  Cc: linux-acpi, acpica-devel, linux-kernel, linux-arm-kernel,
	linux-pci, Lorenzo Pieralisi

The GICv5 driver IRQ domain hierarchy requires adding a parent field to
struct irqchip_fwid so that core code can reference a fwnode_handle parent
for a given fwnode.

Add a parent field to struct irqchip_fwid and update the related kernel API
functions to initialize and handle it.

Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Marc Zyngier <maz@kernel.org>
---
 include/linux/irqdomain.h | 30 ++++++++++++++++++++++++++----
 kernel/irq/irqdomain.c    | 14 +++++++++++++-
 2 files changed, 39 insertions(+), 5 deletions(-)

diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index 62f81bbeb490..b9df84b447a1 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -257,7 +257,8 @@ static inline void irq_domain_set_pm_device(struct irq_domain *d, struct device
 
 #ifdef CONFIG_IRQ_DOMAIN
 struct fwnode_handle *__irq_domain_alloc_fwnode(unsigned int type, int id,
-						const char *name, phys_addr_t *pa);
+						const char *name, phys_addr_t *pa,
+						struct fwnode_handle *parent);
 
 enum {
 	IRQCHIP_FWNODE_REAL,
@@ -267,18 +268,39 @@ enum {
 
 static inline struct fwnode_handle *irq_domain_alloc_named_fwnode(const char *name)
 {
-	return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_NAMED, 0, name, NULL);
+	return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_NAMED, 0, name, NULL, NULL);
+}
+
+static inline
+struct fwnode_handle *irq_domain_alloc_named_fwnode_parent(const char *name,
+							   struct fwnode_handle *parent)
+{
+	return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_NAMED, 0, name, NULL, parent);
 }
 
 static inline struct fwnode_handle *irq_domain_alloc_named_id_fwnode(const char *name, int id)
 {
 	return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_NAMED_ID, id, name,
-					 NULL);
+					 NULL, NULL);
+}
+
+static inline
+struct fwnode_handle *irq_domain_alloc_named_id_fwnode_parent(const char *name, int id,
+							      struct fwnode_handle *parent)
+{
+	return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_NAMED_ID, id, name,
+					 NULL, parent);
 }
 
 static inline struct fwnode_handle *irq_domain_alloc_fwnode(phys_addr_t *pa)
 {
-	return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_REAL, 0, NULL, pa);
+	return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_REAL, 0, NULL, pa, NULL);
+}
+
+static inline struct fwnode_handle *irq_domain_alloc_fwnode_parent(phys_addr_t *pa,
+								   struct fwnode_handle *parent)
+{
+	return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_REAL, 0, NULL, pa, parent);
 }
 
 void irq_domain_free_fwnode(struct fwnode_handle *fwnode);
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 2652c4cfd877..baf77cd167c4 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -33,6 +33,7 @@ static void irq_domain_free_one_irq(struct irq_domain *domain, unsigned int virq
 
 struct irqchip_fwid {
 	struct fwnode_handle	fwnode;
+	struct fwnode_handle	*parent;
 	unsigned int		type;
 	char			*name;
 	phys_addr_t		*pa;
@@ -53,8 +54,16 @@ static const char *irqchip_fwnode_get_name(const struct fwnode_handle *fwnode)
 	return fwid->name;
 }
 
+static struct fwnode_handle *irqchip_fwnode_get_parent(const struct fwnode_handle *fwnode)
+{
+	struct irqchip_fwid *fwid = container_of(fwnode, struct irqchip_fwid, fwnode);
+
+	return fwid->parent;
+}
+
 const struct fwnode_operations irqchip_fwnode_ops = {
 	.get_name = irqchip_fwnode_get_name,
+	.get_parent = irqchip_fwnode_get_parent,
 };
 EXPORT_SYMBOL_GPL(irqchip_fwnode_ops);
 
@@ -65,6 +74,7 @@ EXPORT_SYMBOL_GPL(irqchip_fwnode_ops);
  * @id:		Optional user provided id if name != NULL
  * @name:	Optional user provided domain name
  * @pa:		Optional user-provided physical address
+ * @parent:	Optional parent fwnode_handle
  *
  * Allocate a struct irqchip_fwid, and return a pointer to the embedded
  * fwnode_handle (or NULL on failure).
@@ -76,7 +86,8 @@ EXPORT_SYMBOL_GPL(irqchip_fwnode_ops);
  */
 struct fwnode_handle *__irq_domain_alloc_fwnode(unsigned int type, int id,
 						const char *name,
-						phys_addr_t *pa)
+						phys_addr_t *pa,
+						struct fwnode_handle *parent)
 {
 	struct irqchip_fwid *fwid;
 	char *n;
@@ -104,6 +115,7 @@ struct fwnode_handle *__irq_domain_alloc_fwnode(unsigned int type, int id,
 	fwid->type = type;
 	fwid->name = n;
 	fwid->pa = pa;
+	fwid->parent = parent;
 	fwnode_init(&fwid->fwnode, &irqchip_fwnode_ops);
 	return &fwid->fwnode;
 }

-- 
2.50.1



^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v2 4/7] PCI/MSI: Make the pci_msi_map_rid_ctlr_node() interface firmware agnostic
  2025-12-18 10:14 [PATCH v2 0/7] irqchip/gic-v5: Code first ACPI boot support Lorenzo Pieralisi
                   ` (2 preceding siblings ...)
  2025-12-18 10:14 ` [PATCH v2 3/7] irqdomain: Add parent field to struct irqchip_fwid Lorenzo Pieralisi
@ 2025-12-18 10:14 ` Lorenzo Pieralisi
  2026-01-05 12:21   ` Jonathan Cameron
  2026-01-05 17:35   ` Bjorn Helgaas
  2025-12-18 10:14 ` [PATCH v2 5/7] irqchip/gic-v5: Add ACPI IRS probing Lorenzo Pieralisi
                   ` (2 subsequent siblings)
  6 siblings, 2 replies; 18+ messages in thread
From: Lorenzo Pieralisi @ 2025-12-18 10:14 UTC (permalink / raw)
  To: Rafael J. Wysocki, Len Brown, Robert Moore, Thomas Gleixner,
	Hanjun Guo, Sudeep Holla, Marc Zyngier, Bjorn Helgaas
  Cc: linux-acpi, acpica-devel, linux-kernel, linux-arm-kernel,
	linux-pci, Lorenzo Pieralisi

To support booting with OF and ACPI seamlessly, GIC ITS parent code
requires the PCI/MSI irqdomain layer to implement a function to retrieve
an MSI controller fwnode and map an RID in a firmware agnostic way
(ie pci_msi_map_rid_ctlr_node()).

Convert pci_msi_map_rid_ctlr_node() to an OF agnostic interface
(fwnode_handle based) and update the GIC ITS MSI parent code to reflect
the pci_msi_map_rid_ctlr_node() change.

Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Marc Zyngier <maz@kernel.org>
---
 drivers/irqchip/irq-gic-its-msi-parent.c |  8 ++++----
 drivers/pci/msi/irqdomain.c              | 22 +++++++++++++++++-----
 include/linux/msi.h                      |  3 ++-
 3 files changed, 23 insertions(+), 10 deletions(-)

diff --git a/drivers/irqchip/irq-gic-its-msi-parent.c b/drivers/irqchip/irq-gic-its-msi-parent.c
index 12f45228c867..4d1ad1ee005d 100644
--- a/drivers/irqchip/irq-gic-its-msi-parent.c
+++ b/drivers/irqchip/irq-gic-its-msi-parent.c
@@ -104,7 +104,7 @@ static int its_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
 static int its_v5_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
 				  int nvec, msi_alloc_info_t *info)
 {
-	struct device_node *msi_node = NULL;
+	struct fwnode_handle *msi_node = NULL;
 	struct msi_domain_info *msi_info;
 	struct pci_dev *pdev;
 	phys_addr_t pa;
@@ -116,15 +116,15 @@ static int its_v5_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
 
 	pdev = to_pci_dev(dev);
 
-	rid = pci_msi_map_rid_ctlr_node(pdev, &msi_node);
+	rid = pci_msi_map_rid_ctlr_node(domain->parent, pdev, &msi_node);
 	if (!msi_node)
 		return -ENODEV;
 
-	ret = its_translate_frame_address(msi_node, &pa);
+	ret = its_translate_frame_address(to_of_node(msi_node), &pa);
 	if (ret)
 		return -ENODEV;
 
-	of_node_put(msi_node);
+	fwnode_handle_put(msi_node);
 
 	/* ITS specific DeviceID */
 	info->scratchpad[0].ul = rid;
diff --git a/drivers/pci/msi/irqdomain.c b/drivers/pci/msi/irqdomain.c
index a329060287b5..3136341e802c 100644
--- a/drivers/pci/msi/irqdomain.c
+++ b/drivers/pci/msi/irqdomain.c
@@ -376,23 +376,35 @@ u32 pci_msi_domain_get_msi_rid(struct irq_domain *domain, struct pci_dev *pdev)
 }
 
 /**
- * pci_msi_map_rid_ctlr_node - Get the MSI controller node and MSI requester id (RID)
+ * pci_msi_map_rid_ctlr_node - Get the MSI controller fwnode_handle and MSI requester id (RID)
+ * @domain:	The interrupt domain
  * @pdev:	The PCI device
- * @node:	Pointer to store the MSI controller device node
+ * @node:	Pointer to store the MSI controller fwnode_handle
  *
- * Use the firmware data to find the MSI controller node for @pdev.
+ * Use the firmware data to find the MSI controller fwnode_handle for @pdev.
  * If found map the RID and initialize @node with it. @node value must
  * be set to NULL on entry.
  *
  * Returns: The RID.
  */
-u32 pci_msi_map_rid_ctlr_node(struct pci_dev *pdev, struct device_node **node)
+u32 pci_msi_map_rid_ctlr_node(struct irq_domain *domain, struct pci_dev *pdev,
+			      struct fwnode_handle **node)
 {
+	struct device_node *of_node;
 	u32 rid = pci_dev_id(pdev);
 
 	pci_for_each_dma_alias(pdev, get_msi_id_cb, &rid);
 
-	return of_msi_xlate(&pdev->dev, node, rid);
+	of_node = irq_domain_get_of_node(domain);
+	if (of_node) {
+		struct device_node *msi_ctlr_node = NULL;
+
+		rid = of_msi_xlate(&pdev->dev, &msi_ctlr_node, rid);
+		if (msi_ctlr_node)
+			*node = of_fwnode_handle(msi_ctlr_node);
+	}
+
+	return rid;
 }
 
 /**
diff --git a/include/linux/msi.h b/include/linux/msi.h
index 8003e3218c46..8ddb05d5c96a 100644
--- a/include/linux/msi.h
+++ b/include/linux/msi.h
@@ -702,7 +702,8 @@ void __pci_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg);
 void pci_msi_mask_irq(struct irq_data *data);
 void pci_msi_unmask_irq(struct irq_data *data);
 u32 pci_msi_domain_get_msi_rid(struct irq_domain *domain, struct pci_dev *pdev);
-u32 pci_msi_map_rid_ctlr_node(struct pci_dev *pdev, struct device_node **node);
+u32 pci_msi_map_rid_ctlr_node(struct irq_domain *domain, struct pci_dev *pdev,
+			      struct fwnode_handle **node);
 struct irq_domain *pci_msi_get_device_domain(struct pci_dev *pdev);
 void pci_msix_prepare_desc(struct irq_domain *domain, msi_alloc_info_t *arg,
 			   struct msi_desc *desc);

-- 
2.50.1



^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v2 5/7] irqchip/gic-v5: Add ACPI IRS probing
  2025-12-18 10:14 [PATCH v2 0/7] irqchip/gic-v5: Code first ACPI boot support Lorenzo Pieralisi
                   ` (3 preceding siblings ...)
  2025-12-18 10:14 ` [PATCH v2 4/7] PCI/MSI: Make the pci_msi_map_rid_ctlr_node() interface firmware agnostic Lorenzo Pieralisi
@ 2025-12-18 10:14 ` Lorenzo Pieralisi
  2026-01-05 13:24   ` Jonathan Cameron
  2025-12-18 10:14 ` [PATCH v2 6/7] irqchip/gic-v5: Add ACPI ITS probing Lorenzo Pieralisi
  2025-12-18 10:14 ` [PATCH v2 7/7] irqchip/gic-v5: Add ACPI IWB probing Lorenzo Pieralisi
  6 siblings, 1 reply; 18+ messages in thread
From: Lorenzo Pieralisi @ 2025-12-18 10:14 UTC (permalink / raw)
  To: Rafael J. Wysocki, Len Brown, Robert Moore, Thomas Gleixner,
	Hanjun Guo, Sudeep Holla, Marc Zyngier, Bjorn Helgaas
  Cc: linux-acpi, acpica-devel, linux-kernel, linux-arm-kernel,
	linux-pci, Lorenzo Pieralisi

On ARM64 ACPI systems GICv5 IRSes are described in MADT sub-entries.

Add the required plumbing to parse MADT IRS firmware table entries and
probe the IRS components in ACPI.

Refactor the OF based probe so that common code paths can be reused for
ACPI as well in the process.

Augment the irqdomain_ops.translate() for PPI and SPI IRQs in order to
provide support for their ACPI based firmware translation.

Implement an irqchip ACPI based callback to initialize the global GSI
domain upon an MADT IRS detection.

The IRQCHIP_ACPI_DECLARE() entry in the top level GICv5 driver is only used
to trigger the IRS probing (ie the global GSI domain is initialized once on
the first call on multi-IRS systems); IRS probing takes place by calling
acpi_table_parse_madt() in the IRS sub-driver, that probes all IRSes
in sequence.

Add a new ACPI interrupt model so that it can be detected at runtime and
distinguished from previous GIC architecture models.

Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: "Rafael J. Wysocki" <rafael@kernel.org>
Cc: Marc Zyngier <maz@kernel.org>
---
 drivers/acpi/bus.c                 |   3 +
 drivers/irqchip/irq-gic-v5-irs.c   | 239 +++++++++++++++++++++++++++++--------
 drivers/irqchip/irq-gic-v5.c       | 134 ++++++++++++++++++---
 include/linux/acpi.h               |   1 +
 include/linux/irqchip/arm-gic-v5.h |   1 +
 5 files changed, 308 insertions(+), 70 deletions(-)

diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index a984ccd4a2a0..e4f4059c4f1d 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -1197,6 +1197,9 @@ static int __init acpi_bus_init_irq(void)
 	case ACPI_IRQ_MODEL_GIC:
 		message = "GIC";
 		break;
+	case ACPI_IRQ_MODEL_GIC_V5:
+		message = "GICv5";
+		break;
 	case ACPI_IRQ_MODEL_PLATFORM:
 		message = "platform specific model";
 		break;
diff --git a/drivers/irqchip/irq-gic-v5-irs.c b/drivers/irqchip/irq-gic-v5-irs.c
index ce2732d649a3..07c3df5692af 100644
--- a/drivers/irqchip/irq-gic-v5-irs.c
+++ b/drivers/irqchip/irq-gic-v5-irs.c
@@ -5,6 +5,7 @@
 
 #define pr_fmt(fmt)	"GICv5 IRS: " fmt
 
+#include <linux/acpi.h>
 #include <linux/kmemleak.h>
 #include <linux/log2.h>
 #include <linux/of.h>
@@ -545,15 +546,15 @@ int gicv5_irs_register_cpu(int cpuid)
 
 static void __init gicv5_irs_init_bases(struct gicv5_irs_chip_data *irs_data,
 					void __iomem *irs_base,
-					struct fwnode_handle *handle)
+					struct fwnode_handle *handle,
+					bool noncoherent)
 {
-	struct device_node *np = to_of_node(handle);
 	u32 cr0, cr1;
 
 	irs_data->fwnode = handle;
 	irs_data->irs_base = irs_base;
 
-	if (of_property_read_bool(np, "dma-noncoherent")) {
+	if (noncoherent) {
 		/*
 		 * A non-coherent IRS implies that some cache levels cannot be
 		 * used coherently by the cores and GIC. Our only option is to mark
@@ -678,49 +679,13 @@ static void irs_setup_pri_bits(u32 idr1)
 	}
 }
 
-static int __init gicv5_irs_init(struct device_node *node)
+static int __init gicv5_irs_init(struct gicv5_irs_chip_data *irs_data)
 {
-	struct gicv5_irs_chip_data *irs_data;
-	void __iomem *irs_base;
-	u32 idr, spi_count;
-	u8 iaffid_bits;
-	int ret;
+	u32 spi_count, idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR2);
 
-	irs_data = kzalloc(sizeof(*irs_data), GFP_KERNEL);
-	if (!irs_data)
-		return -ENOMEM;
-
-	raw_spin_lock_init(&irs_data->spi_config_lock);
-
-	ret = of_property_match_string(node, "reg-names", "ns-config");
-	if (ret < 0) {
-		pr_err("%pOF: ns-config reg-name not present\n", node);
-		goto out_err;
-	}
-
-	irs_base = of_io_request_and_map(node, ret, of_node_full_name(node));
-	if (IS_ERR(irs_base)) {
-		pr_err("%pOF: unable to map GICv5 IRS registers\n", node);
-		ret = PTR_ERR(irs_base);
-		goto out_err;
-	}
-
-	gicv5_irs_init_bases(irs_data, irs_base, &node->fwnode);
-
-	idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR1);
-	iaffid_bits = FIELD_GET(GICV5_IRS_IDR1_IAFFID_BITS, idr) + 1;
-
-	ret = gicv5_irs_of_init_affinity(node, irs_data, iaffid_bits);
-	if (ret) {
-		pr_err("Failed to parse CPU IAFFIDs from the device tree!\n");
-		goto out_iomem;
-	}
-
-	idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR2);
 	if (WARN(!FIELD_GET(GICV5_IRS_IDR2_LPI, idr),
 		 "LPI support not available - no IPIs, can't proceed\n")) {
-		ret = -ENODEV;
-		goto out_iomem;
+		return -ENODEV;
 	}
 
 	idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR7);
@@ -729,14 +694,6 @@ static int __init gicv5_irs_init(struct device_node *node)
 	idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR6);
 	irs_data->spi_range = FIELD_GET(GICV5_IRS_IDR6_SPI_IRS_RANGE, idr);
 
-	if (irs_data->spi_range) {
-		pr_info("%s detected SPI range [%u-%u]\n",
-						of_node_full_name(node),
-						irs_data->spi_min,
-						irs_data->spi_min +
-						irs_data->spi_range - 1);
-	}
-
 	/*
 	 * Do the global setting only on the first IRS.
 	 * Global properties (iaffid_bits, global spi count) are guaranteed to
@@ -760,6 +717,60 @@ static int __init gicv5_irs_init(struct device_node *node)
 	list_add_tail(&irs_data->entry, &irs_nodes);
 
 	return 0;
+}
+
+static int __init gicv5_irs_of_init(struct device_node *node)
+{
+	struct gicv5_irs_chip_data *irs_data;
+	void __iomem *irs_base;
+	u8 iaffid_bits;
+	u32 idr;
+	int ret;
+
+	irs_data = kzalloc(sizeof(*irs_data), GFP_KERNEL);
+	if (!irs_data)
+		return -ENOMEM;
+
+	raw_spin_lock_init(&irs_data->spi_config_lock);
+
+	ret = of_property_match_string(node, "reg-names", "ns-config");
+	if (ret < 0) {
+		pr_err("%pOF: ns-config reg-name not present\n", node);
+		goto out_err;
+	}
+
+	irs_base = of_io_request_and_map(node, ret, of_node_full_name(node));
+	if (IS_ERR(irs_base)) {
+		pr_err("%pOF: unable to map GICv5 IRS registers\n", node);
+		ret = PTR_ERR(irs_base);
+		goto out_err;
+	}
+
+	gicv5_irs_init_bases(irs_data, irs_base, &node->fwnode,
+			     of_property_read_bool(node, "dma-noncoherent"));
+
+	idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR1);
+	iaffid_bits = FIELD_GET(GICV5_IRS_IDR1_IAFFID_BITS, idr) + 1;
+
+	ret = gicv5_irs_of_init_affinity(node, irs_data, iaffid_bits);
+	if (ret) {
+		pr_err("Failed to parse CPU IAFFIDs from the device tree!\n");
+		goto out_iomem;
+	}
+
+	ret = gicv5_irs_init(irs_data);
+	if (ret)
+		goto out_iomem;
+
+	if (irs_data->spi_range) {
+		pr_info("%s detected SPI range [%u-%u]\n",
+						of_node_full_name(node),
+						irs_data->spi_min,
+						irs_data->spi_min +
+						irs_data->spi_range - 1);
+	}
+
+	return ret;
 
 out_iomem:
 	iounmap(irs_base);
@@ -818,10 +829,136 @@ int __init gicv5_irs_of_probe(struct device_node *parent)
 		if (!of_device_is_compatible(np, "arm,gic-v5-irs"))
 			continue;
 
-		ret = gicv5_irs_init(np);
+		ret = gicv5_irs_of_init(np);
 		if (ret)
 			pr_err("Failed to init IRS %s\n", np->full_name);
 	}
 
 	return list_empty(&irs_nodes) ? -ENODEV : 0;
 }
+
+#ifdef CONFIG_ACPI
+
+#define ACPI_GICV5_IRS_MEM_SIZE (SZ_64K)
+static struct gicv5_irs_chip_data *current_irs_data __initdata;
+static int current_irsid __initdata = -1;
+static u8 current_iaffid_bits __initdata;
+
+static int __init gic_acpi_parse_iaffid(union acpi_subtable_headers *header,
+					const unsigned long end)
+{
+	struct acpi_madt_generic_interrupt *gicc = (struct acpi_madt_generic_interrupt *)header;
+	int cpu;
+
+	if (!(gicc->flags & (ACPI_MADT_ENABLED | ACPI_MADT_GICC_ONLINE_CAPABLE)))
+		return 0;
+
+	if (gicc->irs_id == current_irsid) {
+		cpu = get_logical_index(gicc->arm_mpidr);
+
+		if (gicc->iaffid & ~GENMASK(current_iaffid_bits - 1, 0)) {
+			pr_warn("CPU %d iaffid 0x%x exceeds IRS iaffid bits\n", cpu, gicc->iaffid);
+			return 0;
+		}
+
+		// Bind the IAFFID and the CPU
+		per_cpu(cpu_iaffid, cpu).iaffid = gicc->iaffid;
+		per_cpu(cpu_iaffid, cpu).valid = true;
+		pr_debug("Processed IAFFID %u for CPU%d", per_cpu(cpu_iaffid, cpu).iaffid, cpu);
+
+		// We also know that the CPU is connected to this IRS
+		per_cpu(per_cpu_irs_data, cpu) = current_irs_data;
+	}
+
+	return 0;
+}
+
+static int __init gicv5_irs_acpi_init_affinity(u32 irsid, struct gicv5_irs_chip_data *irs_data)
+{
+	u32 idr;
+
+	current_irsid = irsid;
+	current_irs_data = irs_data;
+
+	idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR1);
+	current_iaffid_bits = FIELD_GET(GICV5_IRS_IDR1_IAFFID_BITS, idr) + 1;
+
+	acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT, gic_acpi_parse_iaffid, 0);
+
+	return 0;
+}
+
+static struct resource * __init gic_request_region(resource_size_t base, resource_size_t size,
+						   const char *name)
+{
+	struct resource *r = request_mem_region(base, size, name);
+
+	if (!r)
+		pr_warn_once(FW_BUG "%s region %pa has overlapping address\n", name, &base);
+
+	return r;
+}
+
+static int __init gic_acpi_parse_madt_irs(union acpi_subtable_headers *header,
+					  const unsigned long end)
+{
+	struct acpi_madt_gicv5_irs *irs = (struct acpi_madt_gicv5_irs *)header;
+	struct gicv5_irs_chip_data *irs_data;
+	void __iomem *irs_base;
+	struct resource *r;
+	int ret;
+
+	// Per-IRS data structure
+	irs_data = kzalloc(sizeof(*irs_data), GFP_KERNEL);
+	if (!irs_data)
+		return -ENOMEM;
+
+	// This spinlock is used for SPI config changes
+	raw_spin_lock_init(&irs_data->spi_config_lock);
+
+	irs_base = ioremap(irs->config_base_address, ACPI_GICV5_IRS_MEM_SIZE);
+	if (!irs_base) {
+		pr_err("Unable to map GIC IRS registers\n");
+		ret = -ENOMEM;
+		goto out_free;
+	}
+
+	r = gic_request_region(irs->config_base_address, ACPI_GICV5_IRS_MEM_SIZE, "GICv5 IRS");
+	if (!r) {
+		ret = -EBUSY;
+		goto out_map;
+	}
+
+	gicv5_irs_init_bases(irs_data, irs_base, NULL, irs->flags & ACPI_MADT_IRS_NON_COHERENT);
+
+	gicv5_irs_acpi_init_affinity(irs->irs_id, irs_data);
+
+	ret = gicv5_irs_init(irs_data);
+	if (ret)
+		goto out_release;
+
+	if (irs_data->spi_range) {
+		pr_info("%s @%llx detected SPI range [%u-%u]\n", "IRS", irs->config_base_address,
+									irs_data->spi_min,
+									irs_data->spi_min +
+									irs_data->spi_range - 1);
+	}
+
+	return 0;
+
+out_release:
+	release_mem_region(r->start, resource_size(r));
+out_map:
+	iounmap(irs_base);
+out_free:
+	kfree(irs_data);
+	return ret;
+}
+
+int __init gicv5_irs_acpi_probe(void)
+{
+	acpi_table_parse_madt(ACPI_MADT_TYPE_GICV5_IRS, gic_acpi_parse_madt_irs, 0);
+
+	return list_empty(&irs_nodes) ? -ENODEV : 0;
+}
+#endif
diff --git a/drivers/irqchip/irq-gic-v5.c b/drivers/irqchip/irq-gic-v5.c
index 41ef286c4d78..23fd551c4347 100644
--- a/drivers/irqchip/irq-gic-v5.c
+++ b/drivers/irqchip/irq-gic-v5.c
@@ -579,16 +579,36 @@ static __always_inline int gicv5_irq_domain_translate(struct irq_domain *d,
 						      unsigned int *type,
 						      const u8 hwirq_type)
 {
-	if (!is_of_node(fwspec->fwnode))
-		return -EINVAL;
+	unsigned int hwirq_trigger;
+	u8 fwspec_irq_type;
 
-	if (fwspec->param_count < 3)
-		return -EINVAL;
+	if (is_of_node(fwspec->fwnode)) {
 
-	if (fwspec->param[0] != hwirq_type)
-		return -EINVAL;
+		if (fwspec->param_count < 3)
+			return -EINVAL;
 
-	*hwirq = fwspec->param[1];
+		fwspec_irq_type = fwspec->param[0];
+
+		if (fwspec->param[0] != hwirq_type)
+			return -EINVAL;
+
+		*hwirq = fwspec->param[1];
+		hwirq_trigger = fwspec->param[2];
+	}
+
+	if (is_fwnode_irqchip(fwspec->fwnode)) {
+
+		if (fwspec->param_count != 2)
+			return -EINVAL;
+
+		fwspec_irq_type = FIELD_GET(GICV5_HWIRQ_TYPE, fwspec->param[0]);
+
+		if (fwspec_irq_type != hwirq_type)
+			return -EINVAL;
+
+		*hwirq = FIELD_GET(GICV5_HWIRQ_ID, fwspec->param[0]);
+		hwirq_trigger = fwspec->param[1];
+	}
 
 	switch (hwirq_type) {
 	case GICV5_HWIRQ_TYPE_PPI:
@@ -600,7 +620,7 @@ static __always_inline int gicv5_irq_domain_translate(struct irq_domain *d,
 							 IRQ_TYPE_EDGE_RISING;
 		break;
 	case GICV5_HWIRQ_TYPE_SPI:
-		*type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
+		*type = hwirq_trigger & IRQ_TYPE_SENSE_MASK;
 		break;
 	default:
 		BUILD_BUG_ON(1);
@@ -660,10 +680,18 @@ static void gicv5_irq_domain_free(struct irq_domain *domain, unsigned int virq,
 static int gicv5_irq_ppi_domain_select(struct irq_domain *d, struct irq_fwspec *fwspec,
 				       enum irq_domain_bus_token bus_token)
 {
+	u32 hwirq_type;
+
 	if (fwspec->fwnode != d->fwnode)
 		return 0;
 
-	if (fwspec->param[0] != GICV5_HWIRQ_TYPE_PPI)
+	if (is_of_node(fwspec->fwnode))
+		hwirq_type = fwspec->param[0];
+
+	if (is_fwnode_irqchip(fwspec->fwnode))
+		hwirq_type = FIELD_GET(GICV5_HWIRQ_TYPE, fwspec->param[0]);
+
+	if (hwirq_type != GICV5_HWIRQ_TYPE_PPI)
 		return 0;
 
 	return (d == gicv5_global_data.ppi_domain);
@@ -718,10 +746,18 @@ static int gicv5_irq_spi_domain_alloc(struct irq_domain *domain, unsigned int vi
 static int gicv5_irq_spi_domain_select(struct irq_domain *d, struct irq_fwspec *fwspec,
 				       enum irq_domain_bus_token bus_token)
 {
+	u32 hwirq_type;
+
 	if (fwspec->fwnode != d->fwnode)
 		return 0;
 
-	if (fwspec->param[0] != GICV5_HWIRQ_TYPE_SPI)
+	if (is_of_node(fwspec->fwnode))
+		hwirq_type = fwspec->param[0];
+
+	if (is_fwnode_irqchip(fwspec->fwnode))
+		hwirq_type = FIELD_GET(GICV5_HWIRQ_TYPE, fwspec->param[0]);
+
+	if (hwirq_type != GICV5_HWIRQ_TYPE_SPI)
 		return 0;
 
 	return (d == gicv5_global_data.spi_domain);
@@ -1082,16 +1118,12 @@ static inline void __init gic_of_setup_kvm_info(struct device_node *node)
 }
 #endif // CONFIG_KVM
 
-static int __init gicv5_of_init(struct device_node *node, struct device_node *parent)
+static int __init gicv5_init_common(struct fwnode_handle *parent_domain)
 {
-	int ret = gicv5_irs_of_probe(node);
+	int ret = gicv5_init_domains(parent_domain);
 	if (ret)
 		return ret;
 
-	ret = gicv5_init_domains(of_fwnode_handle(node));
-	if (ret)
-		goto out_irs;
-
 	gicv5_set_cpuif_pribits();
 	gicv5_set_cpuif_idbits();
 
@@ -1113,18 +1145,82 @@ static int __init gicv5_of_init(struct device_node *node, struct device_node *pa
 	gicv5_smp_init();
 
 	gicv5_irs_its_probe();
-
-	gic_of_setup_kvm_info(node);
-
 	return 0;
 
 out_int:
 	gicv5_cpu_disable_interrupts();
 out_dom:
 	gicv5_free_domains();
+	return ret;
+}
+
+static int __init gicv5_of_init(struct device_node *node, struct device_node *parent)
+{
+	int ret = gicv5_irs_of_probe(node);
+	if (ret)
+		return ret;
+
+	ret = gicv5_init_common(of_fwnode_handle(node));
+	if (ret)
+		goto out_irs;
+
+	gic_of_setup_kvm_info(node);
+
+	return 0;
 out_irs:
 	gicv5_irs_remove();
 
 	return ret;
 }
 IRQCHIP_DECLARE(gic_v5, "arm,gic-v5", gicv5_of_init);
+
+#ifdef CONFIG_ACPI
+static bool __init acpi_validate_gic_table(struct acpi_subtable_header *header,
+					   struct acpi_probe_entry *ape)
+{
+	struct acpi_madt_gicv5_irs *irs = (struct acpi_madt_gicv5_irs *)header;
+
+	return (irs->version == ape->driver_data);
+}
+
+static struct fwnode_handle *gsi_domain_handle;
+
+static struct fwnode_handle *gic_v5_get_gsi_domain_id(u32 gsi)
+{
+	return gsi_domain_handle;
+}
+
+static int __init gic_acpi_init(union acpi_subtable_headers *header, const unsigned long end)
+{
+	struct acpi_madt_gicv5_irs *irs = (struct acpi_madt_gicv5_irs *)header;
+	int ret;
+
+	if (gsi_domain_handle)
+		return 0;
+
+	gsi_domain_handle = irq_domain_alloc_fwnode(&irs->config_base_address);
+	if (!gsi_domain_handle)
+		return -ENOMEM;
+
+	ret = gicv5_irs_acpi_probe();
+	if (ret)
+		goto out_fwnode;
+
+	ret = gicv5_init_common(gsi_domain_handle);
+	if (ret)
+		goto out_irs;
+
+	acpi_set_irq_model(ACPI_IRQ_MODEL_GIC_V5, gic_v5_get_gsi_domain_id);
+
+	return 0;
+
+out_irs:
+	gicv5_irs_remove();
+out_fwnode:
+	irq_domain_free_fwnode(gsi_domain_handle);
+	return ret;
+}
+IRQCHIP_ACPI_DECLARE(gic_v5, ACPI_MADT_TYPE_GICV5_IRS,
+		     acpi_validate_gic_table, ACPI_MADT_GIC_VERSION_V5,
+		     gic_acpi_init);
+#endif
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index fbf0c3a65f59..3a412dcebc29 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -107,6 +107,7 @@ enum acpi_irq_model_id {
 	ACPI_IRQ_MODEL_IOSAPIC,
 	ACPI_IRQ_MODEL_PLATFORM,
 	ACPI_IRQ_MODEL_GIC,
+	ACPI_IRQ_MODEL_GIC_V5,
 	ACPI_IRQ_MODEL_LPIC,
 	ACPI_IRQ_MODEL_RINTC,
 	ACPI_IRQ_MODEL_COUNT
diff --git a/include/linux/irqchip/arm-gic-v5.h b/include/linux/irqchip/arm-gic-v5.h
index 68ddcdb1cec5..ff5b1a4931d7 100644
--- a/include/linux/irqchip/arm-gic-v5.h
+++ b/include/linux/irqchip/arm-gic-v5.h
@@ -344,6 +344,7 @@ void __init gicv5_init_lpi_domain(void);
 void __init gicv5_free_lpi_domain(void);
 
 int gicv5_irs_of_probe(struct device_node *parent);
+int gicv5_irs_acpi_probe(void);
 void gicv5_irs_remove(void);
 int gicv5_irs_enable(void);
 void gicv5_irs_its_probe(void);

-- 
2.50.1



^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v2 6/7] irqchip/gic-v5: Add ACPI ITS probing
  2025-12-18 10:14 [PATCH v2 0/7] irqchip/gic-v5: Code first ACPI boot support Lorenzo Pieralisi
                   ` (4 preceding siblings ...)
  2025-12-18 10:14 ` [PATCH v2 5/7] irqchip/gic-v5: Add ACPI IRS probing Lorenzo Pieralisi
@ 2025-12-18 10:14 ` Lorenzo Pieralisi
  2026-01-05 13:55   ` Jonathan Cameron
  2025-12-18 10:14 ` [PATCH v2 7/7] irqchip/gic-v5: Add ACPI IWB probing Lorenzo Pieralisi
  6 siblings, 1 reply; 18+ messages in thread
From: Lorenzo Pieralisi @ 2025-12-18 10:14 UTC (permalink / raw)
  To: Rafael J. Wysocki, Len Brown, Robert Moore, Thomas Gleixner,
	Hanjun Guo, Sudeep Holla, Marc Zyngier, Bjorn Helgaas
  Cc: linux-acpi, acpica-devel, linux-kernel, linux-arm-kernel,
	linux-pci, Lorenzo Pieralisi

On ACPI ARM64 systems the GICv5 ITS configuration and translate frames
are described in the MADT table.

Refactor the current GICv5 ITS driver code to share common functions
between ACPI and OF and implement ACPI probing in the GICv5 ITS driver.

Add iort_msi_xlate() to map a device ID and retrieve an MSI controller
fwnode node for ACPI systems and update pci_msi_map_rid_ctlr_node() to
use it in its ACPI code path.

Add the required functions to IORT code for deviceID retrieval and IRQ
domain registration and look-up so that the GICv5 ITS driver in an
ACPI based system can be successfully probed.

Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Hanjun Guo <guohanjun@huawei.com>
Cc: Sudeep Holla <sudeep.holla@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
---
 drivers/acpi/arm64/iort.c                |  95 +++++++++++++++++-----
 drivers/irqchip/irq-gic-its-msi-parent.c |  39 ++++-----
 drivers/irqchip/irq-gic-v5-irs.c         |   7 +-
 drivers/irqchip/irq-gic-v5-its.c         | 132 ++++++++++++++++++++++++++++++-
 drivers/pci/msi/irqdomain.c              |   2 +
 include/linux/acpi_iort.h                |  10 ++-
 include/linux/irqchip/arm-gic-v5.h       |   1 +
 7 files changed, 241 insertions(+), 45 deletions(-)

diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
index 65f0f56ad753..17dbe66da804 100644
--- a/drivers/acpi/arm64/iort.c
+++ b/drivers/acpi/arm64/iort.c
@@ -595,45 +595,45 @@ u32 iort_msi_map_id(struct device *dev, u32 input_id)
 }
 
 /**
- * iort_pmsi_get_dev_id() - Get the device id for a device
+ * iort_msi_xlate() - Map a MSI input ID for a device
  * @dev: The device for which the mapping is to be done.
- * @dev_id: The device ID found.
+ * @input_id: The device input ID.
+ * @fwnode: Pointer to store the fwnode.
  *
- * Returns: 0 for successful find a dev id, -ENODEV on error
+ * Returns: mapped MSI ID on success, input ID otherwise
+ *	    On success, the fwnode pointer is initialized to the MSI
+ *	    controller fwnode handle.
  */
-int iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id)
+u32 iort_msi_xlate(struct device *dev, u32 input_id, struct fwnode_handle **fwnode)
 {
-	int i, index;
+	struct acpi_iort_its_group *its;
 	struct acpi_iort_node *node;
+	u32 dev_id;
 
 	node = iort_find_dev_node(dev);
 	if (!node)
-		return -ENODEV;
+		return input_id;
 
-	index = iort_get_id_mapping_index(node);
-	/* if there is a valid index, go get the dev_id directly */
-	if (index >= 0) {
-		if (iort_node_get_id(node, dev_id, index))
-			return 0;
-	} else {
-		for (i = 0; i < node->mapping_count; i++) {
-			if (iort_node_map_platform_id(node, dev_id,
-						      IORT_MSI_TYPE, i))
-				return 0;
-		}
-	}
+	node = iort_node_map_id(node, input_id, &dev_id, IORT_MSI_TYPE);
+	if (!node)
+		return input_id;
 
-	return -ENODEV;
+	/* Move to ITS specific data */
+	its = (struct acpi_iort_its_group *)node->node_data;
+
+	*fwnode = iort_find_domain_token(its->identifiers[0]);
+
+	return dev_id;
 }
 
-static int __maybe_unused iort_find_its_base(u32 its_id, phys_addr_t *base)
+int iort_its_translate_pa(struct fwnode_handle *node, phys_addr_t *base)
 {
 	struct iort_its_msi_chip *its_msi_chip;
 	int ret = -ENODEV;
 
 	spin_lock(&iort_msi_chip_lock);
 	list_for_each_entry(its_msi_chip, &iort_msi_chip_list, list) {
-		if (its_msi_chip->translation_id == its_id) {
+		if (its_msi_chip->fw_node == node) {
 			*base = its_msi_chip->base_addr;
 			ret = 0;
 			break;
@@ -644,6 +644,59 @@ static int __maybe_unused iort_find_its_base(u32 its_id, phys_addr_t *base)
 	return ret;
 }
 
+static int __maybe_unused iort_find_its_base(u32 its_id, phys_addr_t *base)
+{
+	struct fwnode_handle *fwnode = iort_find_domain_token(its_id);
+
+	if (!fwnode)
+		return -ENODEV;
+
+	return iort_its_translate_pa(fwnode, base);
+}
+
+/**
+ * iort_pmsi_get_msi_info() - Get the device id and translate frame PA for a device
+ * @dev: The device for which the mapping is to be done.
+ * @dev_id: The device ID found.
+ * @pa: optional pointer to store translate frame address.
+ *
+ * Returns: 0 for successful devid and pa retrieval, -ENODEV on error
+ */
+int iort_pmsi_get_msi_info(struct device *dev, u32 *dev_id, phys_addr_t *pa)
+{
+	struct acpi_iort_node *node, *parent = NULL;
+	struct acpi_iort_its_group *its;
+	int i, index;
+
+	node = iort_find_dev_node(dev);
+	if (!node)
+		return -ENODEV;
+
+	index = iort_get_id_mapping_index(node);
+	/* if there is a valid index, go get the dev_id directly */
+	if (index >= 0) {
+		parent = iort_node_get_id(node, dev_id, index);
+	} else {
+		for (i = 0; i < node->mapping_count; i++) {
+			parent = iort_node_map_platform_id(node, dev_id,
+						      IORT_MSI_TYPE, i);
+			if (parent)
+				break;
+		}
+	}
+
+	if (parent && pa) {
+		int ret;
+
+		its = (struct acpi_iort_its_group *)node->node_data;
+		ret = iort_find_its_base(its->identifiers[0], pa);
+		if (ret)
+			return ret;
+	}
+
+	return parent ? 0 : -ENODEV;
+}
+
 /**
  * iort_dev_find_its_id() - Find the ITS identifier for a device
  * @dev: The device.
diff --git a/drivers/irqchip/irq-gic-its-msi-parent.c b/drivers/irqchip/irq-gic-its-msi-parent.c
index 4d1ad1ee005d..a832cdb2e697 100644
--- a/drivers/irqchip/irq-gic-its-msi-parent.c
+++ b/drivers/irqchip/irq-gic-its-msi-parent.c
@@ -19,18 +19,24 @@
 				 MSI_FLAG_PCI_MSIX      |	\
 				 MSI_FLAG_MULTI_PCI_MSI)
 
-static int its_translate_frame_address(struct device_node *msi_node, phys_addr_t *pa)
+static int its_translate_frame_address(struct fwnode_handle *msi_node, phys_addr_t *pa)
 {
 	struct resource res;
 	int ret;
 
-	ret = of_property_match_string(msi_node, "reg-names", "ns-translate");
-	if (ret < 0)
-		return ret;
+	if (is_of_node(msi_node)) {
+		struct device_node *msi_np = to_of_node(msi_node);
 
-	ret = of_address_to_resource(msi_node, ret, &res);
-	if (ret)
-		return ret;
+		ret = of_property_match_string(msi_np, "reg-names", "ns-translate");
+		if (ret < 0)
+			return ret;
+
+		ret = of_address_to_resource(msi_np, ret, &res);
+		if (ret)
+			return ret;
+	} else {
+		ret = iort_its_translate_pa(msi_node, &res.start);
+	}
 
 	*pa = res.start;
 	return 0;
@@ -120,7 +126,7 @@ static int its_v5_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
 	if (!msi_node)
 		return -ENODEV;
 
-	ret = its_translate_frame_address(to_of_node(msi_node), &pa);
+	ret = its_translate_frame_address(msi_node, &pa);
 	if (ret)
 		return -ENODEV;
 
@@ -161,7 +167,7 @@ static int of_pmsi_get_msi_info(struct irq_domain *domain, struct device *dev, u
 				ret = -EINVAL;
 
 			if (!ret && pa)
-				ret = its_translate_frame_address(it.node, pa);
+				ret = its_translate_frame_address(of_fwnode_handle(it.node), pa);
 
 			if (!ret)
 				*dev_id = args;
@@ -176,11 +182,6 @@ static int of_pmsi_get_msi_info(struct irq_domain *domain, struct device *dev, u
 	return of_map_id(dev->of_node, dev->id, "msi-map", "msi-map-mask", &msi_ctrl, dev_id);
 }
 
-int __weak iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id)
-{
-	return -1;
-}
-
 static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev,
 			    int nvec, msi_alloc_info_t *info)
 {
@@ -191,7 +192,7 @@ static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev,
 	if (dev->of_node)
 		ret = of_pmsi_get_msi_info(domain->parent, dev, &dev_id, NULL);
 	else
-		ret = iort_pmsi_get_dev_id(dev, &dev_id);
+		ret = iort_pmsi_get_msi_info(dev, &dev_id, NULL);
 	if (ret)
 		return ret;
 
@@ -214,10 +215,10 @@ static int its_v5_pmsi_prepare(struct irq_domain *domain, struct device *dev,
 	u32 dev_id;
 	int ret;
 
-	if (!dev->of_node)
-		return -ENODEV;
-
-	ret = of_pmsi_get_msi_info(domain->parent, dev, &dev_id, &pa);
+	if (dev->of_node)
+		ret = of_pmsi_get_msi_info(domain->parent, dev, &dev_id, &pa);
+	else
+		ret = iort_pmsi_get_msi_info(dev, &dev_id, &pa);
 	if (ret)
 		return ret;
 
diff --git a/drivers/irqchip/irq-gic-v5-irs.c b/drivers/irqchip/irq-gic-v5-irs.c
index 07c3df5692af..a3a0b270b5be 100644
--- a/drivers/irqchip/irq-gic-v5-irs.c
+++ b/drivers/irqchip/irq-gic-v5-irs.c
@@ -816,8 +816,11 @@ void __init gicv5_irs_its_probe(void)
 {
 	struct gicv5_irs_chip_data *irs_data;
 
-	list_for_each_entry(irs_data, &irs_nodes, entry)
-		gicv5_its_of_probe(to_of_node(irs_data->fwnode));
+	if (acpi_disabled)
+		list_for_each_entry(irs_data, &irs_nodes, entry)
+			gicv5_its_of_probe(to_of_node(irs_data->fwnode));
+	else
+		gicv5_its_acpi_probe();
 }
 
 int __init gicv5_irs_of_probe(struct device_node *parent)
diff --git a/drivers/irqchip/irq-gic-v5-its.c b/drivers/irqchip/irq-gic-v5-its.c
index 554485f0be1f..751ccd88549a 100644
--- a/drivers/irqchip/irq-gic-v5-its.c
+++ b/drivers/irqchip/irq-gic-v5-its.c
@@ -5,6 +5,8 @@
 
 #define pr_fmt(fmt)	"GICv5 ITS: " fmt
 
+#include <linux/acpi.h>
+#include <linux/acpi_iort.h>
 #include <linux/bitmap.h>
 #include <linux/iommu.h>
 #include <linux/init.h>
@@ -1115,7 +1117,7 @@ static int gicv5_its_init_domain(struct gicv5_its_chip_data *its, struct irq_dom
 }
 
 static int __init gicv5_its_init_bases(void __iomem *its_base, struct fwnode_handle *handle,
-				       struct irq_domain *parent_domain)
+				       struct irq_domain *parent_domain, bool noncoherent)
 {
 	struct device_node *np = to_of_node(handle);
 	struct gicv5_its_chip_data *its_node;
@@ -1208,7 +1210,8 @@ static int __init gicv5_its_init(struct device_node *node)
 	}
 
 	ret = gicv5_its_init_bases(its_base, of_fwnode_handle(node),
-				   gicv5_global_data.lpi_domain);
+				   gicv5_global_data.lpi_domain,
+				   of_property_read_bool(node, "dma-noncoherent"));
 	if (ret)
 		goto out_unmap;
 
@@ -1231,3 +1234,128 @@ void __init gicv5_its_of_probe(struct device_node *parent)
 			pr_err("Failed to init ITS %s\n", np->full_name);
 	}
 }
+
+#ifdef CONFIG_ACPI
+
+#define ACPI_GICV5_ITS_MEM_SIZE (SZ_64K)
+
+static struct acpi_madt_gicv5_translator *current_its_entry __initdata;
+static struct fwnode_handle *current_its_fwnode __initdata;
+
+static int __init gic_acpi_parse_madt_its_translate(union acpi_subtable_headers *header,
+						    const unsigned long end)
+{
+	struct acpi_madt_gicv5_translate_frame *its_frame;
+	struct fwnode_handle *msi_dom_handle;
+	struct resource res = {};
+	int err;
+
+	its_frame = (struct acpi_madt_gicv5_translate_frame *)header;
+	if (its_frame->linked_translator_id != current_its_entry->translator_id)
+		return 0;
+
+	res.start = its_frame->base_address;
+	res.end = its_frame->base_address + ACPI_GICV5_ITS_MEM_SIZE - 1;
+	res.flags = IORESOURCE_MEM;
+
+	msi_dom_handle = irq_domain_alloc_fwnode_parent(&res.start, current_its_fwnode);
+	if (!msi_dom_handle) {
+		pr_err("ITS@%pa: Unable to allocate GICv5 ITS translate domain token\n",
+		       &res.start);
+		return -ENOMEM;
+	}
+
+	err = iort_register_domain_token(its_frame->translate_frame_id, res.start,
+					 msi_dom_handle);
+	if (err) {
+		pr_err("ITS@%pa: Unable to register GICv5 ITS domain token (ITS TRANSLATE FRAME ID %d) to IORT\n",
+		       &res.start, its_frame->translate_frame_id);
+		irq_domain_free_fwnode(msi_dom_handle);
+		return err;
+	}
+
+	return 0;
+}
+
+static int __init gic_acpi_free_madt_its_translate(union acpi_subtable_headers *header,
+						   const unsigned long end)
+{
+	struct acpi_madt_gicv5_translate_frame *its_frame;
+	struct fwnode_handle *msi_dom_handle;
+
+	its_frame = (struct acpi_madt_gicv5_translate_frame *)header;
+	if (its_frame->linked_translator_id != current_its_entry->translator_id)
+		return 0;
+
+	msi_dom_handle = iort_find_domain_token(its_frame->translate_frame_id);
+	if (!msi_dom_handle)
+		return 0;
+
+	iort_deregister_domain_token(its_frame->translate_frame_id);
+	irq_domain_free_fwnode(msi_dom_handle);
+
+	return 0;
+}
+
+static int __init gic_acpi_parse_madt_its(union acpi_subtable_headers *header,
+					  const unsigned long end)
+{
+	struct acpi_madt_gicv5_translator *its_entry;
+	struct fwnode_handle *dom_handle;
+	struct resource res = {};
+	void __iomem *its_base;
+	int err;
+
+	its_entry = (struct acpi_madt_gicv5_translator *)header;
+	res.start = its_entry->base_address;
+	res.end = its_entry->base_address + ACPI_GICV5_ITS_MEM_SIZE - 1;
+	res.flags = IORESOURCE_MEM;
+
+	if (!request_mem_region(res.start, resource_size(&res), "GICv5 ITS"))
+		return -EBUSY;
+
+	dom_handle = irq_domain_alloc_fwnode(&res.start);
+	if (!dom_handle) {
+		pr_err("ITS@%pa: Unable to allocate GICv5 ITS domain token\n",
+		       &res.start);
+		err = -ENOMEM;
+		goto out_rel_res;
+	}
+
+	current_its_entry = its_entry;
+	current_its_fwnode = dom_handle;
+
+	acpi_table_parse_madt(ACPI_MADT_TYPE_GICV5_ITS_TRANSLATE,
+			      gic_acpi_parse_madt_its_translate, 0);
+
+	its_base = ioremap(res.start, ACPI_GICV5_ITS_MEM_SIZE);
+	if (!its_base) {
+		err = -ENOMEM;
+		goto out_unregister;
+	}
+
+	err = gicv5_its_init_bases(its_base, dom_handle, gicv5_global_data.lpi_domain,
+				   its_entry->flags & ACPI_MADT_GICV5_ITS_NON_COHERENT);
+	if (err)
+		goto out_unmap;
+
+	return 0;
+
+out_unmap:
+	iounmap(its_base);
+out_unregister:
+	acpi_table_parse_madt(ACPI_MADT_TYPE_GICV5_ITS_TRANSLATE,
+			      gic_acpi_free_madt_its_translate, 0);
+	irq_domain_free_fwnode(dom_handle);
+out_rel_res:
+	release_mem_region(res.start, resource_size(&res));
+	return err;
+}
+
+void __init gicv5_its_acpi_probe(void)
+{
+	acpi_table_parse_madt(ACPI_MADT_TYPE_GICV5_ITS, gic_acpi_parse_madt_its, 0);
+}
+#else
+void __init gicv5_its_acpi_probe(void) { }
+#endif
diff --git a/drivers/pci/msi/irqdomain.c b/drivers/pci/msi/irqdomain.c
index 3136341e802c..25fcc2114803 100644
--- a/drivers/pci/msi/irqdomain.c
+++ b/drivers/pci/msi/irqdomain.c
@@ -402,6 +402,8 @@ u32 pci_msi_map_rid_ctlr_node(struct irq_domain *domain, struct pci_dev *pdev,
 		rid = of_msi_xlate(&pdev->dev, &msi_ctlr_node, rid);
 		if (msi_ctlr_node)
 			*node = of_fwnode_handle(msi_ctlr_node);
+	} else {
+		rid = iort_msi_xlate(&pdev->dev, rid, node);
 	}
 
 	return rid;
diff --git a/include/linux/acpi_iort.h b/include/linux/acpi_iort.h
index d4ed5622cf2b..2d22268677a9 100644
--- a/include/linux/acpi_iort.h
+++ b/include/linux/acpi_iort.h
@@ -27,12 +27,14 @@ int iort_register_domain_token(int trans_id, phys_addr_t base,
 			       struct fwnode_handle *fw_node);
 void iort_deregister_domain_token(int trans_id);
 struct fwnode_handle *iort_find_domain_token(int trans_id);
-int iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id);
 
 #ifdef CONFIG_ACPI_IORT
 u32 iort_msi_map_id(struct device *dev, u32 id);
+u32 iort_msi_xlate(struct device *dev, u32 id, struct fwnode_handle **node);
+int iort_its_translate_pa(struct fwnode_handle *node, phys_addr_t *base);
 struct irq_domain *iort_get_device_domain(struct device *dev, u32 id,
 					  enum irq_domain_bus_token bus_token);
+int iort_pmsi_get_msi_info(struct device *dev, u32 *dev_id, phys_addr_t *pa);
 void acpi_configure_pmsi_domain(struct device *dev);
 void iort_get_rmr_sids(struct fwnode_handle *iommu_fwnode,
 		       struct list_head *head);
@@ -46,9 +48,15 @@ phys_addr_t acpi_iort_dma_get_max_cpu_address(void);
 #else
 static inline u32 iort_msi_map_id(struct device *dev, u32 id)
 { return id; }
+static inline u32 iort_msi_xlate(struct device *dev, u32 id, struct fwnode_handle **node)
+{ return id; }
+static inline int iort_its_translate_pa(struct fwnode_handle *node, phys_addr_t *base)
+{ return -ENODEV; }
 static inline struct irq_domain *iort_get_device_domain(
 	struct device *dev, u32 id, enum irq_domain_bus_token bus_token)
 { return NULL; }
+static inline int iort_pmsi_get_msi_info(struct device *dev, u32 *dev_id, phys_addr_t *pa)
+{ return -ENODEV; }
 static inline void acpi_configure_pmsi_domain(struct device *dev) { }
 static inline
 void iort_get_rmr_sids(struct fwnode_handle *iommu_fwnode, struct list_head *head) { }
diff --git a/include/linux/irqchip/arm-gic-v5.h b/include/linux/irqchip/arm-gic-v5.h
index ff5b1a4931d7..334b6986435c 100644
--- a/include/linux/irqchip/arm-gic-v5.h
+++ b/include/linux/irqchip/arm-gic-v5.h
@@ -392,4 +392,5 @@ int gicv5_alloc_lpi(void);
 void gicv5_free_lpi(u32 lpi);
 
 void __init gicv5_its_of_probe(struct device_node *parent);
+void __init gicv5_its_acpi_probe(void);
 #endif

-- 
2.50.1



^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH v2 7/7] irqchip/gic-v5: Add ACPI IWB probing
  2025-12-18 10:14 [PATCH v2 0/7] irqchip/gic-v5: Code first ACPI boot support Lorenzo Pieralisi
                   ` (5 preceding siblings ...)
  2025-12-18 10:14 ` [PATCH v2 6/7] irqchip/gic-v5: Add ACPI ITS probing Lorenzo Pieralisi
@ 2025-12-18 10:14 ` Lorenzo Pieralisi
  2026-01-05 15:35   ` Jonathan Cameron
  6 siblings, 1 reply; 18+ messages in thread
From: Lorenzo Pieralisi @ 2025-12-18 10:14 UTC (permalink / raw)
  To: Rafael J. Wysocki, Len Brown, Robert Moore, Thomas Gleixner,
	Hanjun Guo, Sudeep Holla, Marc Zyngier, Bjorn Helgaas
  Cc: linux-acpi, acpica-devel, linux-kernel, linux-arm-kernel,
	linux-pci, Lorenzo Pieralisi

To probe an IWB in an ACPI based system it is required:

- to implement the IORT functions handling the IWB IORT node and create
  functions to retrieve IWB firmware information
- to augment the driver to match the DSDT ACPI "ARMH0003" device and
  retrieve the IWB wire and trigger mask from the GSI interrupt descriptor
  in the IWB msi_domain_ops.msi_translate() function

Make the required driver changes to enable IWB probing in ACPI systems.

The GICv5 GSI format requires special handling for IWB routed IRQs.

Add IWB GSI detection to the top level driver gic_v5_get_gsi_domain_id()
function so that the correct IRQ domain for a GSI can be detected by
parsing the GSI and check whether it is an IWB-backed IRQ or not.

Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Hanjun Guo <guohanjun@huawei.com>
Cc: Sudeep Holla <sudeep.holla@arm.com>
Cc: Marc Zyngier <maz@kernel.org>
---
 drivers/acpi/arm64/iort.c          | 95 ++++++++++++++++++++++++++++++++------
 drivers/irqchip/irq-gic-v5-iwb.c   | 42 +++++++++++++----
 drivers/irqchip/irq-gic-v5.c       |  4 ++
 include/linux/acpi_iort.h          |  1 +
 include/linux/irqchip/arm-gic-v5.h |  6 +++
 5 files changed, 123 insertions(+), 25 deletions(-)

diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
index 17dbe66da804..4b0b753db738 100644
--- a/drivers/acpi/arm64/iort.c
+++ b/drivers/acpi/arm64/iort.c
@@ -264,39 +264,47 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node,
 	struct device *dev = context;
 	acpi_status status = AE_NOT_FOUND;
 
-	if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT) {
+	if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT ||
+	    node->type == ACPI_IORT_NODE_IWB) {
 		struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
-		struct acpi_device *adev;
 		struct acpi_iort_named_component *ncomp;
-		struct device *nc_dev = dev;
+		struct acpi_iort_iwb *iwb;
+		struct device *cdev = dev;
+		struct acpi_device *adev;
+		const char *device_name;
 
 		/*
 		 * Walk the device tree to find a device with an
 		 * ACPI companion; there is no point in scanning
-		 * IORT for a device matching a named component if
+		 * IORT for a device matching a named component or IWB if
 		 * the device does not have an ACPI companion to
 		 * start with.
 		 */
 		do {
-			adev = ACPI_COMPANION(nc_dev);
+			adev = ACPI_COMPANION(cdev);
 			if (adev)
 				break;
 
-			nc_dev = nc_dev->parent;
-		} while (nc_dev);
+			cdev = cdev->parent;
+		} while (cdev);
 
 		if (!adev)
 			goto out;
 
 		status = acpi_get_name(adev->handle, ACPI_FULL_PATHNAME, &buf);
 		if (ACPI_FAILURE(status)) {
-			dev_warn(nc_dev, "Can't get device full path name\n");
+			dev_warn(cdev, "Can't get device full path name\n");
 			goto out;
 		}
 
-		ncomp = (struct acpi_iort_named_component *)node->node_data;
-		status = !strcmp(ncomp->device_name, buf.pointer) ?
-							AE_OK : AE_NOT_FOUND;
+		if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT) {
+			ncomp = (struct acpi_iort_named_component *)node->node_data;
+			device_name = ncomp->device_name;
+		} else {
+			iwb = (struct acpi_iort_iwb *)node->node_data;
+			device_name = iwb->device_name;
+		}
+		status = !strcmp(device_name, buf.pointer) ?  AE_OK : AE_NOT_FOUND;
 		acpi_os_free(buf.pointer);
 	} else if (node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) {
 		struct acpi_iort_root_complex *pci_rc;
@@ -317,12 +325,28 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node,
 	return status;
 }
 
+static acpi_status iort_match_iwb_callback(struct acpi_iort_node *node, void *context)
+{
+	acpi_status status = AE_NOT_FOUND;
+	u32 *id = context;
+
+	if (node->type == ACPI_IORT_NODE_IWB) {
+		struct acpi_iort_iwb *iwb;
+
+		iwb = (struct acpi_iort_iwb *)node->node_data;
+		status = iwb->iwb_index == *id ? AE_OK : AE_NOT_FOUND;
+	}
+
+	return status;
+}
+
 static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in,
 		       u32 *rid_out, bool check_overlap)
 {
 	/* Single mapping does not care for input id */
 	if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) {
 		if (type == ACPI_IORT_NODE_NAMED_COMPONENT ||
+		    type == ACPI_IORT_NODE_IWB		   ||
 		    type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX) {
 			*rid_out = map->output_base;
 			return 0;
@@ -392,6 +416,7 @@ static struct acpi_iort_node *iort_node_get_id(struct acpi_iort_node *node,
 
 	if (map->flags & ACPI_IORT_ID_SINGLE_MAPPING) {
 		if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT ||
+		    node->type == ACPI_IORT_NODE_IWB ||
 		    node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX ||
 		    node->type == ACPI_IORT_NODE_SMMU_V3 ||
 		    node->type == ACPI_IORT_NODE_PMCG) {
@@ -562,9 +587,14 @@ static struct acpi_iort_node *iort_find_dev_node(struct device *dev)
 			return node;
 		/*
 		 * if not, then it should be a platform device defined in
-		 * DSDT/SSDT (with Named Component node in IORT)
+		 * DSDT/SSDT (with Named Component node in IORT) or an
+		 * IWB device in the DSDT/SSDT.
 		 */
-		return iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,
+		node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,
+				      iort_match_node_callback, dev);
+		if (node)
+			return node;
+		return iort_scan_node(ACPI_IORT_NODE_IWB,
 				      iort_match_node_callback, dev);
 	}
 
@@ -756,6 +786,35 @@ struct irq_domain *iort_get_device_domain(struct device *dev, u32 id,
 	return irq_find_matching_fwnode(handle, bus_token);
 }
 
+struct fwnode_handle *iort_iwb_handle(u32 iwb_id)
+{
+	struct fwnode_handle *fwnode;
+	struct acpi_iort_node *node;
+	struct acpi_device *device;
+	struct acpi_iort_iwb *iwb;
+	acpi_status status;
+	acpi_handle handle;
+
+	/* find its associated IWB node */
+	node = iort_scan_node(ACPI_IORT_NODE_IWB, iort_match_iwb_callback, &iwb_id);
+	if (!node)
+		return NULL;
+
+	iwb = (struct acpi_iort_iwb *)node->node_data;
+	status = acpi_get_handle(NULL, iwb->device_name, &handle);
+	if (ACPI_FAILURE(status))
+		return NULL;
+
+	device = acpi_get_acpi_dev(handle);
+	if (!device)
+		return NULL;
+
+	fwnode = acpi_fwnode_handle(device);
+	acpi_put_acpi_dev(device);
+
+	return fwnode;
+}
+
 static void iort_set_device_domain(struct device *dev,
 				   struct acpi_iort_node *node)
 {
@@ -816,8 +875,14 @@ static struct irq_domain *iort_get_platform_device_domain(struct device *dev)
 	/* find its associated iort node */
 	node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,
 			      iort_match_node_callback, dev);
-	if (!node)
-		return NULL;
+	if (!node) {
+		/* find its associated iort node */
+		node = iort_scan_node(ACPI_IORT_NODE_IWB,
+				      iort_match_node_callback, dev);
+
+		if (!node)
+			return NULL;
+	}
 
 	/* then find its msi parent node */
 	for (i = 0; i < node->mapping_count; i++) {
diff --git a/drivers/irqchip/irq-gic-v5-iwb.c b/drivers/irqchip/irq-gic-v5-iwb.c
index ad9fdc14d1c6..c7d5fd34d053 100644
--- a/drivers/irqchip/irq-gic-v5-iwb.c
+++ b/drivers/irqchip/irq-gic-v5-iwb.c
@@ -4,6 +4,7 @@
  */
 #define pr_fmt(fmt)	"GICv5 IWB: " fmt
 
+#include <linux/acpi.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/msi.h>
@@ -136,18 +137,31 @@ static int gicv5_iwb_irq_domain_translate(struct irq_domain *d, struct irq_fwspe
 					  irq_hw_number_t *hwirq,
 					  unsigned int *type)
 {
-	if (!is_of_node(fwspec->fwnode))
-		return -EINVAL;
+	if (is_of_node(fwspec->fwnode)) {
 
-	if (fwspec->param_count < 2)
-		return -EINVAL;
+		if (fwspec->param_count < 2)
+			return -EINVAL;
 
-	/*
-	 * param[0] is be the wire
-	 * param[1] is the interrupt type
-	 */
-	*hwirq = fwspec->param[0];
-	*type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
+		/*
+		 * param[0] is be the wire
+		 * param[1] is the interrupt type
+		 */
+		*hwirq = fwspec->param[0];
+		*type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
+	}
+
+	if (is_acpi_device_node(fwspec->fwnode)) {
+
+		if (fwspec->param_count < 2)
+			return -EINVAL;
+
+		/*
+		 * Extract the wire from param[0]
+		 * param[1] is the interrupt type
+		 */
+		*hwirq = FIELD_GET(GICV5_GSI_IWB_WIRE, fwspec->param[0]);
+		*type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
+	}
 
 	return 0;
 }
@@ -265,10 +279,18 @@ static const struct of_device_id gicv5_iwb_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, gicv5_iwb_of_match);
 
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id iwb_acpi_match[] = {
+	{ "ARMH0003", 0 },
+	{}
+};
+#endif
+
 static struct platform_driver gicv5_iwb_platform_driver = {
 	.driver = {
 		.name			= "GICv5 IWB",
 		.of_match_table		= gicv5_iwb_of_match,
+		.acpi_match_table	= ACPI_PTR(iwb_acpi_match),
 		.suppress_bind_attrs	= true,
 	},
 	.probe				= gicv5_iwb_device_probe,
diff --git a/drivers/irqchip/irq-gic-v5.c b/drivers/irqchip/irq-gic-v5.c
index 23fd551c4347..da867dd2e77d 100644
--- a/drivers/irqchip/irq-gic-v5.c
+++ b/drivers/irqchip/irq-gic-v5.c
@@ -5,6 +5,7 @@
 
 #define pr_fmt(fmt)	"GICv5: " fmt
 
+#include <linux/acpi_iort.h>
 #include <linux/cpuhotplug.h>
 #include <linux/idr.h>
 #include <linux/irqdomain.h>
@@ -1187,6 +1188,9 @@ static struct fwnode_handle *gsi_domain_handle;
 
 static struct fwnode_handle *gic_v5_get_gsi_domain_id(u32 gsi)
 {
+	if (FIELD_GET(GICV5_GSI_IC_TYPE, gsi) == GICV5_GSI_IWB_TYPE)
+		return iort_iwb_handle(FIELD_GET(GICV5_GSI_IWB_FRAME_ID, gsi));
+
 	return gsi_domain_handle;
 }
 
diff --git a/include/linux/acpi_iort.h b/include/linux/acpi_iort.h
index 2d22268677a9..17bb3374f4ca 100644
--- a/include/linux/acpi_iort.h
+++ b/include/linux/acpi_iort.h
@@ -27,6 +27,7 @@ int iort_register_domain_token(int trans_id, phys_addr_t base,
 			       struct fwnode_handle *fw_node);
 void iort_deregister_domain_token(int trans_id);
 struct fwnode_handle *iort_find_domain_token(int trans_id);
+struct fwnode_handle *iort_iwb_handle(u32 iwb_id);
 
 #ifdef CONFIG_ACPI_IORT
 u32 iort_msi_map_id(struct device *dev, u32 id);
diff --git a/include/linux/irqchip/arm-gic-v5.h b/include/linux/irqchip/arm-gic-v5.h
index 334b6986435c..3da1ad80fc9d 100644
--- a/include/linux/irqchip/arm-gic-v5.h
+++ b/include/linux/irqchip/arm-gic-v5.h
@@ -265,6 +265,12 @@
 
 #define GICV5_IWB_WENABLE_STATUSR_IDLE		BIT(0)
 
+#define GICV5_GSI_IC_TYPE			GENMASK(31, 29)
+#define GICV5_GSI_IWB_TYPE			0x7
+
+#define GICV5_GSI_IWB_FRAME_ID			GENMASK(28, 16)
+#define GICV5_GSI_IWB_WIRE			GENMASK(15, 0)
+
 /*
  * Global Data structures and functions
  */

-- 
2.50.1



^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 3/7] irqdomain: Add parent field to struct irqchip_fwid
  2025-12-18 10:14 ` [PATCH v2 3/7] irqdomain: Add parent field to struct irqchip_fwid Lorenzo Pieralisi
@ 2026-01-05 12:01   ` Jonathan Cameron
  2026-01-07  8:58     ` Lorenzo Pieralisi
  0 siblings, 1 reply; 18+ messages in thread
From: Jonathan Cameron @ 2026-01-05 12:01 UTC (permalink / raw)
  To: Lorenzo Pieralisi
  Cc: Rafael J. Wysocki, Len Brown, Robert Moore, Thomas Gleixner,
	Hanjun Guo, Sudeep Holla, Marc Zyngier, Bjorn Helgaas, linux-acpi,
	acpica-devel, linux-kernel, linux-arm-kernel, linux-pci

On Thu, 18 Dec 2025 11:14:29 +0100
Lorenzo Pieralisi <lpieralisi@kernel.org> wrote:

> The GICv5 driver IRQ domain hierarchy requires adding a parent field to
> struct irqchip_fwid so that core code can reference a fwnode_handle parent
> for a given fwnode.
> 
> Add a parent field to struct irqchip_fwid and update the related kernel API
> functions to initialize and handle it.
> 
> Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
> Cc: Thomas Gleixner <tglx@linutronix.de>
> Cc: Marc Zyngier <maz@kernel.org>
Hi Lorenzo,

Happy new year.

> ---
>  include/linux/irqdomain.h | 30 ++++++++++++++++++++++++++----
>  kernel/irq/irqdomain.c    | 14 +++++++++++++-
>  2 files changed, 39 insertions(+), 5 deletions(-)
> 
> diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
> index 62f81bbeb490..b9df84b447a1 100644
> --- a/include/linux/irqdomain.h
> +++ b/include/linux/irqdomain.h
> @@ -257,7 +257,8 @@ static inline void irq_domain_set_pm_device(struct irq_domain *d, struct device
>  
>  #ifdef CONFIG_IRQ_DOMAIN
>  struct fwnode_handle *__irq_domain_alloc_fwnode(unsigned int type, int id,
> -						const char *name, phys_addr_t *pa);
> +						const char *name, phys_addr_t *pa,
> +						struct fwnode_handle *parent);
>  
>  enum {
>  	IRQCHIP_FWNODE_REAL,
> @@ -267,18 +268,39 @@ enum {
>  
>  static inline struct fwnode_handle *irq_domain_alloc_named_fwnode(const char *name)
>  {
> -	return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_NAMED, 0, name, NULL);
> +	return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_NAMED, 0, name, NULL, NULL);
> +}
> +
> +static inline
> +struct fwnode_handle *irq_domain_alloc_named_fwnode_parent(const char *name,
> +							   struct fwnode_handle *parent)

The name of this makes me think it's allocating the named fwnode parent, rather that
the named fwnode + setting it's parent.

There aren't all that many calls to irq_domain_named_fwnode(), maybe to avoid challenge
of a new name, just add the parameter to all of them? (25ish)  Mind you the current
pattern for similar cases is a helper, so maybe not.

Or go with something similar to named and have

irq_domain_alloc_named_parented_fwnode()?

I'm not that bothered though if you think the current naming is the best we can do.

Jonathan

> +{
> +	return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_NAMED, 0, name, NULL, parent);
>  }




^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 4/7] PCI/MSI: Make the pci_msi_map_rid_ctlr_node() interface firmware agnostic
  2025-12-18 10:14 ` [PATCH v2 4/7] PCI/MSI: Make the pci_msi_map_rid_ctlr_node() interface firmware agnostic Lorenzo Pieralisi
@ 2026-01-05 12:21   ` Jonathan Cameron
  2026-01-07  9:23     ` Lorenzo Pieralisi
  2026-01-05 17:35   ` Bjorn Helgaas
  1 sibling, 1 reply; 18+ messages in thread
From: Jonathan Cameron @ 2026-01-05 12:21 UTC (permalink / raw)
  To: Lorenzo Pieralisi
  Cc: Rafael J. Wysocki, Len Brown, Robert Moore, Thomas Gleixner,
	Hanjun Guo, Sudeep Holla, Marc Zyngier, Bjorn Helgaas, linux-acpi,
	acpica-devel, linux-kernel, linux-arm-kernel, linux-pci

On Thu, 18 Dec 2025 11:14:30 +0100
Lorenzo Pieralisi <lpieralisi@kernel.org> wrote:

> To support booting with OF and ACPI seamlessly, GIC ITS parent code
> requires the PCI/MSI irqdomain layer to implement a function to retrieve
> an MSI controller fwnode and map an RID in a firmware agnostic way
> (ie pci_msi_map_rid_ctlr_node()).
> 
> Convert pci_msi_map_rid_ctlr_node() to an OF agnostic interface
> (fwnode_handle based) and update the GIC ITS MSI parent code to reflect
> the pci_msi_map_rid_ctlr_node() change.
> 
> Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
> Cc: Thomas Gleixner <tglx@linutronix.de>
> Cc: Bjorn Helgaas <bhelgaas@google.com>
> Cc: Marc Zyngier <maz@kernel.org>
Hi Lorenzo,

A few minor comments inline.  All in the 'up to you' category.

Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>

> ---

> diff --git a/drivers/pci/msi/irqdomain.c b/drivers/pci/msi/irqdomain.c
> index a329060287b5..3136341e802c 100644
> --- a/drivers/pci/msi/irqdomain.c
> +++ b/drivers/pci/msi/irqdomain.c
> @@ -376,23 +376,35 @@ u32 pci_msi_domain_get_msi_rid(struct irq_domain *domain, struct pci_dev *pdev)
>  }
>  
>  /**
> - * pci_msi_map_rid_ctlr_node - Get the MSI controller node and MSI requester id (RID)
> + * pci_msi_map_rid_ctlr_node - Get the MSI controller fwnode_handle and MSI requester id (RID)
> + * @domain:	The interrupt domain
>   * @pdev:	The PCI device
> - * @node:	Pointer to store the MSI controller device node
> + * @node:	Pointer to store the MSI controller fwnode_handle
>   *
> - * Use the firmware data to find the MSI controller node for @pdev.
> + * Use the firmware data to find the MSI controller fwnode_handle for @pdev.
>   * If found map the RID and initialize @node with it. @node value must
>   * be set to NULL on entry.
>   *
>   * Returns: The RID.
>   */
> -u32 pci_msi_map_rid_ctlr_node(struct pci_dev *pdev, struct device_node **node)
> +u32 pci_msi_map_rid_ctlr_node(struct irq_domain *domain, struct pci_dev *pdev,
> +			      struct fwnode_handle **node)
>  {
> +	struct device_node *of_node;
>  	u32 rid = pci_dev_id(pdev);
>  
>  	pci_for_each_dma_alias(pdev, get_msi_id_cb, &rid);
>  
> -	return of_msi_xlate(&pdev->dev, node, rid);
> +	of_node = irq_domain_get_of_node(domain);
> +	if (of_node) {

I haven't read on but my assumption is of_node is never used for anything else.
I'd make that explicit by not having the local variable.
	if (irq_domain_get_of_node(domain))

Might even be worth a comment to say this is just checking of is in use for the
domain in general?

> +		struct device_node *msi_ctlr_node = NULL;
> +
> +		rid = of_msi_xlate(&pdev->dev, &msi_ctlr_node, rid);
> +		if (msi_ctlr_node)

Do you need the protection? Ultimately that depends on whether
setting *node = NULL on failure to match is a problem.
It's a bit subtle, but if your new code matches behavior of old code
then *node was always NULL at entry to this function so setting it
to NULL again (which is what happens if ms_ctrl_node == NULL) should
be fine.

Maybe it's all a bit subtle though so perhaps the check is worth having.

> +			*node = of_fwnode_handle(msi_ctlr_node);
> +	}
> +
> +	return rid;
>  }
>  
>  /**
> diff --git a/include/linux/msi.h b/include/linux/msi.h
> index 8003e3218c46..8ddb05d5c96a 100644
> --- a/include/linux/msi.h
> +++ b/include/linux/msi.h
> @@ -702,7 +702,8 @@ void __pci_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg);
>  void pci_msi_mask_irq(struct irq_data *data);
>  void pci_msi_unmask_irq(struct irq_data *data);
>  u32 pci_msi_domain_get_msi_rid(struct irq_domain *domain, struct pci_dev *pdev);
> -u32 pci_msi_map_rid_ctlr_node(struct pci_dev *pdev, struct device_node **node);
> +u32 pci_msi_map_rid_ctlr_node(struct irq_domain *domain, struct pci_dev *pdev,
> +			      struct fwnode_handle **node);
>  struct irq_domain *pci_msi_get_device_domain(struct pci_dev *pdev);
>  void pci_msix_prepare_desc(struct irq_domain *domain, msi_alloc_info_t *arg,
>  			   struct msi_desc *desc);
> 



^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 5/7] irqchip/gic-v5: Add ACPI IRS probing
  2025-12-18 10:14 ` [PATCH v2 5/7] irqchip/gic-v5: Add ACPI IRS probing Lorenzo Pieralisi
@ 2026-01-05 13:24   ` Jonathan Cameron
  0 siblings, 0 replies; 18+ messages in thread
From: Jonathan Cameron @ 2026-01-05 13:24 UTC (permalink / raw)
  To: Lorenzo Pieralisi
  Cc: Rafael J. Wysocki, Len Brown, Robert Moore, Thomas Gleixner,
	Hanjun Guo, Sudeep Holla, Marc Zyngier, Bjorn Helgaas, linux-acpi,
	acpica-devel, linux-kernel, linux-arm-kernel, linux-pci

On Thu, 18 Dec 2025 11:14:31 +0100
Lorenzo Pieralisi <lpieralisi@kernel.org> wrote:

> On ARM64 ACPI systems GICv5 IRSes are described in MADT sub-entries.
> 
> Add the required plumbing to parse MADT IRS firmware table entries and
> probe the IRS components in ACPI.
> 
> Refactor the OF based probe so that common code paths can be reused for
> ACPI as well in the process.
> 
> Augment the irqdomain_ops.translate() for PPI and SPI IRQs in order to
> provide support for their ACPI based firmware translation.
> 
> Implement an irqchip ACPI based callback to initialize the global GSI
> domain upon an MADT IRS detection.
> 
> The IRQCHIP_ACPI_DECLARE() entry in the top level GICv5 driver is only used
> to trigger the IRS probing (ie the global GSI domain is initialized once on
> the first call on multi-IRS systems); IRS probing takes place by calling
> acpi_table_parse_madt() in the IRS sub-driver, that probes all IRSes
> in sequence.
> 
> Add a new ACPI interrupt model so that it can be detected at runtime and
> distinguished from previous GIC architecture models.
> 
> Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
> Cc: Thomas Gleixner <tglx@linutronix.de>
> Cc: "Rafael J. Wysocki" <rafael@kernel.org>
> Cc: Marc Zyngier <maz@kernel.org>
Just a few minor comments inline.

> diff --git a/drivers/irqchip/irq-gic-v5-irs.c b/drivers/irqchip/irq-gic-v5-irs.c
> index ce2732d649a3..07c3df5692af 100644
> --- a/drivers/irqchip/irq-gic-v5-irs.c
> +++ b/drivers/irqchip/irq-gic-v5-irs.c
> @@ -5,6 +5,7 @@
>  
>  #define pr_fmt(fmt)	"GICv5 IRS: " fmt
>  
> +#include <linux/acpi.h>
>  #include <linux/kmemleak.h>
>  #include <linux/log2.h>
>  #include <linux/of.h>
> @@ -545,15 +546,15 @@ int gicv5_irs_register_cpu(int cpuid)
>  
>  static void __init gicv5_irs_init_bases(struct gicv5_irs_chip_data *irs_data,
>  					void __iomem *irs_base,
> -					struct fwnode_handle *handle)
> +					struct fwnode_handle *handle,
> +					bool noncoherent)
>  {
> -	struct device_node *np = to_of_node(handle);
>  	u32 cr0, cr1;
>  
>  	irs_data->fwnode = handle;

Looking this again as you are touching it.

This feels a tiny bit out of place, as not obvious to me why the fwnode being
set is related to initializing bases.  Perhaps that belongs at the caller?
 
>  	irs_data->irs_base = irs_base;
>  
> -	if (of_property_read_bool(np, "dma-noncoherent")) {
> +	if (noncoherent) {
>  		/*
>  		 * A non-coherent IRS implies that some cache levels cannot be
>  		 * used coherently by the cores and GIC. Our only option is to mark
> @@ -678,49 +679,13 @@ static void irs_setup_pri_bits(u32 idr1)
>  	}
>  }
>  

...

> +static int __init gicv5_irs_of_init(struct device_node *node)
> +{

Not sure whether it would be worth it by inclination would have been
a noop refactor patch, then the ACPI support. I'd have found it a little
bit easier to review.

> +	struct gicv5_irs_chip_data *irs_data;
> +	void __iomem *irs_base;
> +	u8 iaffid_bits;
> +	u32 idr;
> +	int ret;
> +
> +	irs_data = kzalloc(sizeof(*irs_data), GFP_KERNEL);
> +	if (!irs_data)
> +		return -ENOMEM;
> +
> +	raw_spin_lock_init(&irs_data->spi_config_lock);
> +
> +	ret = of_property_match_string(node, "reg-names", "ns-config");
> +	if (ret < 0) {
> +		pr_err("%pOF: ns-config reg-name not present\n", node);
> +		goto out_err;

Obviously comes form original code, but this label could give some indication
of where we are going and why. out_free_data maybe?

Perhaps I'm just being fussy as I had to open the code up to check given
it didn't end up in the context.

> +	}
> +
...

> +static int __init gic_acpi_parse_iaffid(union acpi_subtable_headers *header,
> +					const unsigned long end)
> +{
> +	struct acpi_madt_generic_interrupt *gicc = (struct acpi_madt_generic_interrupt *)header;
> +	int cpu;
> +
> +	if (!(gicc->flags & (ACPI_MADT_ENABLED | ACPI_MADT_GICC_ONLINE_CAPABLE)))
> +		return 0;
> +
> +	if (gicc->irs_id == current_irsid) {
Unless this is going to get more complex I'd flip the log for an early return on
'not this one'
	if (gicc->irs_id != current_irsid)
		return 0;

mostly to save on long lines where they aren't needed.

> +		cpu = get_logical_index(gicc->arm_mpidr);
> +
> +		if (gicc->iaffid & ~GENMASK(current_iaffid_bits - 1, 0)) {
> +			pr_warn("CPU %d iaffid 0x%x exceeds IRS iaffid bits\n", cpu, gicc->iaffid);
> +			return 0;
> +		}
> +
> +		// Bind the IAFFID and the CPU
> +		per_cpu(cpu_iaffid, cpu).iaffid = gicc->iaffid;
> +		per_cpu(cpu_iaffid, cpu).valid = true;
> +		pr_debug("Processed IAFFID %u for CPU%d", per_cpu(cpu_iaffid, cpu).iaffid, cpu);
> +
> +		// We also know that the CPU is connected to this IRS
> +		per_cpu(per_cpu_irs_data, cpu) = current_irs_data;
> +	}
> +
> +	return 0;
> +}
> +
> +static int __init gicv5_irs_acpi_init_affinity(u32 irsid, struct gicv5_irs_chip_data *irs_data)
> +{
> +	u32 idr;
> +
> +	current_irsid = irsid;
> +	current_irs_data = irs_data;
> +
> +	idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR1);
> +	current_iaffid_bits = FIELD_GET(GICV5_IRS_IDR1_IAFFID_BITS, idr) + 1;
> +
> +	acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT, gic_acpi_parse_iaffid, 0);
> +
> +	return 0;
> +}

> +
> +static int __init gic_acpi_parse_madt_irs(union acpi_subtable_headers *header,
> +					  const unsigned long end)
> +{
> +	struct acpi_madt_gicv5_irs *irs = (struct acpi_madt_gicv5_irs *)header;
> +	struct gicv5_irs_chip_data *irs_data;
> +	void __iomem *irs_base;
> +	struct resource *r;
> +	int ret;
> +
> +	// Per-IRS data structure

It's your code to look after, so I don't care that strongly but this does
seem to be inconsistent wrt to local style and use of C style
single line comments.  I'd stick to /* */ throughout.


> +	irs_data = kzalloc(sizeof(*irs_data), GFP_KERNEL);
> +	if (!irs_data)
> +		return -ENOMEM;
> +
> +	// This spinlock is used for SPI config changes
> +	raw_spin_lock_init(&irs_data->spi_config_lock);
> +
> +	irs_base = ioremap(irs->config_base_address, ACPI_GICV5_IRS_MEM_SIZE);
> +	if (!irs_base) {
> +		pr_err("Unable to map GIC IRS registers\n");
> +		ret = -ENOMEM;
> +		goto out_free;
> +	}
> +
> +	r = gic_request_region(irs->config_base_address, ACPI_GICV5_IRS_MEM_SIZE, "GICv5 IRS");

Really minor but I wonder if this should follow the same ordering as
of_io_request_and_map() which does the request_mem_region() before
trying to ioremap()?

Nice to have the same ordering though that would then put you at odds iwth the
gicv3 redist code that does it in this order.  Guess can't win them all and
an ephemeral mapping that is then torn down on error of the second call
doesn't really matter.  

> +	if (!r) {
> +		ret = -EBUSY;
> +		goto out_map;
> +	}
> +
> +	gicv5_irs_init_bases(irs_data, irs_base, NULL, irs->flags & ACPI_MADT_IRS_NON_COHERENT);
> +
> +	gicv5_irs_acpi_init_affinity(irs->irs_id, irs_data);
> +
> +	ret = gicv5_irs_init(irs_data);
> +	if (ret)
> +		goto out_release;
> +
> +	if (irs_data->spi_range) {
> +		pr_info("%s @%llx detected SPI range [%u-%u]\n", "IRS", irs->config_base_address,
> +									irs_data->spi_min,
> +									irs_data->spi_min +
> +									irs_data->spi_range - 1);
> +	}
> +
> +	return 0;
> +
> +out_release:
> +	release_mem_region(r->start, resource_size(r));
> +out_map:
> +	iounmap(irs_base);
> +out_free:
> +	kfree(irs_data);
> +	return ret;
> +}

> diff --git a/drivers/irqchip/irq-gic-v5.c b/drivers/irqchip/irq-gic-v5.c
> index 41ef286c4d78..23fd551c4347 100644
> --- a/drivers/irqchip/irq-gic-v5.c
> +++ b/drivers/irqchip/irq-gic-v5.c
> @@ -579,16 +579,36 @@ static __always_inline int gicv5_irq_domain_translate(struct irq_domain *d,
>  						      unsigned int *type,
>  						      const u8 hwirq_type)
>  {
> -	if (!is_of_node(fwspec->fwnode))
> -		return -EINVAL;
> +	unsigned int hwirq_trigger;
> +	u8 fwspec_irq_type;
>  
> -	if (fwspec->param_count < 3)
> -		return -EINVAL;
> +	if (is_of_node(fwspec->fwnode)) {
>  
> -	if (fwspec->param[0] != hwirq_type)
> -		return -EINVAL;
> +		if (fwspec->param_count < 3)
> +			return -EINVAL;
>  
> -	*hwirq = fwspec->param[1];
> +		fwspec_irq_type = fwspec->param[0];
> +
> +		if (fwspec->param[0] != hwirq_type)
> +			return -EINVAL;
> +
> +		*hwirq = fwspec->param[1];
> +		hwirq_trigger = fwspec->param[2];
> +	}
> +
> +	if (is_fwnode_irqchip(fwspec->fwnode)) {
> +
> +		if (fwspec->param_count != 2)
> +			return -EINVAL;
> +
> +		fwspec_irq_type = FIELD_GET(GICV5_HWIRQ_TYPE, fwspec->param[0]);
> +
> +		if (fwspec_irq_type != hwirq_type)
> +			return -EINVAL;
> +
> +		*hwirq = FIELD_GET(GICV5_HWIRQ_ID, fwspec->param[0]);
> +		hwirq_trigger = fwspec->param[1];
> +	}
>  
>  	switch (hwirq_type) {
>  	case GICV5_HWIRQ_TYPE_PPI:
> @@ -600,7 +620,7 @@ static __always_inline int gicv5_irq_domain_translate(struct irq_domain *d,
>  							 IRQ_TYPE_EDGE_RISING;
>  		break;
>  	case GICV5_HWIRQ_TYPE_SPI:
> -		*type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
> +		*type = hwirq_trigger & IRQ_TYPE_SENSE_MASK;
>  		break;
>  	default:
>  		BUILD_BUG_ON(1);
> @@ -660,10 +680,18 @@ static void gicv5_irq_domain_free(struct irq_domain *domain, unsigned int virq,
>  static int gicv5_irq_ppi_domain_select(struct irq_domain *d, struct irq_fwspec *fwspec,
>  				       enum irq_domain_bus_token bus_token)
>  {
> +	u32 hwirq_type;
> +
>  	if (fwspec->fwnode != d->fwnode)
>  		return 0;
>  
> -	if (fwspec->param[0] != GICV5_HWIRQ_TYPE_PPI)
> +	if (is_of_node(fwspec->fwnode))
> +		hwirq_type = fwspec->param[0];
> +
> +	if (is_fwnode_irqchip(fwspec->fwnode))
> +		hwirq_type = FIELD_GET(GICV5_HWIRQ_TYPE, fwspec->param[0]);

Bit borderline but maybe worth a helper for getting the irq type from
the fwspec? Or a more generic helper that optionally gets that and the other
stuff you need in gicv5_irq_domain_translate()

> +
> +	if (hwirq_type != GICV5_HWIRQ_TYPE_PPI)
>  		return 0;
>  
>  	return (d == gicv5_global_data.ppi_domain);
> @@ -718,10 +746,18 @@ static int gicv5_irq_spi_domain_alloc(struct irq_domain *domain, unsigned int vi
>  static int gicv5_irq_spi_domain_select(struct irq_domain *d, struct irq_fwspec *fwspec,
>  				       enum irq_domain_bus_token bus_token)
>  {
> +	u32 hwirq_type;
> +
>  	if (fwspec->fwnode != d->fwnode)
>  		return 0;
>  
> -	if (fwspec->param[0] != GICV5_HWIRQ_TYPE_SPI)
> +	if (is_of_node(fwspec->fwnode))
> +		hwirq_type = fwspec->param[0];
> +
> +	if (is_fwnode_irqchip(fwspec->fwnode))
> +		hwirq_type = FIELD_GET(GICV5_HWIRQ_TYPE, fwspec->param[0]);
> +
> +	if (hwirq_type != GICV5_HWIRQ_TYPE_SPI)
>  		return 0;
>  
>  	return (d == gicv5_global_data.spi_domain);
> @@ -1082,16 +1118,12 @@ static inline void __init gic_of_setup_kvm_info(struct device_node *node)
>  }
>  #endif // CONFIG_KVM

Thanks,

Jonathan



^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 6/7] irqchip/gic-v5: Add ACPI ITS probing
  2025-12-18 10:14 ` [PATCH v2 6/7] irqchip/gic-v5: Add ACPI ITS probing Lorenzo Pieralisi
@ 2026-01-05 13:55   ` Jonathan Cameron
  0 siblings, 0 replies; 18+ messages in thread
From: Jonathan Cameron @ 2026-01-05 13:55 UTC (permalink / raw)
  To: Lorenzo Pieralisi
  Cc: Rafael J. Wysocki, Len Brown, Robert Moore, Thomas Gleixner,
	Hanjun Guo, Sudeep Holla, Marc Zyngier, Bjorn Helgaas, linux-acpi,
	acpica-devel, linux-kernel, linux-arm-kernel, linux-pci

On Thu, 18 Dec 2025 11:14:32 +0100
Lorenzo Pieralisi <lpieralisi@kernel.org> wrote:

> On ACPI ARM64 systems the GICv5 ITS configuration and translate frames
> are described in the MADT table.
> 
> Refactor the current GICv5 ITS driver code to share common functions
> between ACPI and OF and implement ACPI probing in the GICv5 ITS driver.
> 
> Add iort_msi_xlate() to map a device ID and retrieve an MSI controller
> fwnode node for ACPI systems and update pci_msi_map_rid_ctlr_node() to
> use it in its ACPI code path.
> 
> Add the required functions to IORT code for deviceID retrieval and IRQ
> domain registration and look-up so that the GICv5 ITS driver in an
> ACPI based system can be successfully probed.
> 
> Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
> Cc: Thomas Gleixner <tglx@linutronix.de>
> Cc: Hanjun Guo <guohanjun@huawei.com>
> Cc: Sudeep Holla <sudeep.holla@arm.com>
> Cc: Marc Zyngier <maz@kernel.org>
Hi Lorenzo,

Diff was in a rather unfriendly mood on this one and smashing the xlate
on top of the wrong function (wrt to what was being replaced).

Ah well. Only one minor comment inline.  Not really my area of expertise
beyond wanting this to move forwards quickly but none the less,

Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>

> ---
>  drivers/acpi/arm64/iort.c                |  95 +++++++++++++++++-----
>  drivers/irqchip/irq-gic-its-msi-parent.c |  39 ++++-----
>  drivers/irqchip/irq-gic-v5-irs.c         |   7 +-
>  drivers/irqchip/irq-gic-v5-its.c         | 132 ++++++++++++++++++++++++++++++-
>  drivers/pci/msi/irqdomain.c              |   2 +
>  include/linux/acpi_iort.h                |  10 ++-
>  include/linux/irqchip/arm-gic-v5.h       |   1 +
>  7 files changed, 241 insertions(+), 45 deletions(-)
> 
> diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
> index 65f0f56ad753..17dbe66da804 100644
> --- a/drivers/acpi/arm64/iort.c
> +++ b/drivers/acpi/arm64/iort.c
> @@ -595,45 +595,45 @@ u32 iort_msi_map_id(struct device *dev, u32 input_id)
>  }
>
> +/**
> + * iort_pmsi_get_msi_info() - Get the device id and translate frame PA for a device
> + * @dev: The device for which the mapping is to be done.
> + * @dev_id: The device ID found.
> + * @pa: optional pointer to store translate frame address.
> + *
> + * Returns: 0 for successful devid and pa retrieval, -ENODEV on error
> + */
> +int iort_pmsi_get_msi_info(struct device *dev, u32 *dev_id, phys_addr_t *pa)
> +{
> +	struct acpi_iort_node *node, *parent = NULL;
> +	struct acpi_iort_its_group *its;
> +	int i, index;
> +
> +	node = iort_find_dev_node(dev);
> +	if (!node)
> +		return -ENODEV;
> +
> +	index = iort_get_id_mapping_index(node);
> +	/* if there is a valid index, go get the dev_id directly */
> +	if (index >= 0) {
> +		parent = iort_node_get_id(node, dev_id, index);
> +	} else {
> +		for (i = 0; i < node->mapping_count; i++) {
> +			parent = iort_node_map_platform_id(node, dev_id,
> +						      IORT_MSI_TYPE, i);
> +			if (parent)
> +				break;
> +		}
> +	}
> +
Another borderline comment on what I think is a small readabilty
improvement.

I'd handle the only error case that would otherwise use the ternary below
here
	if (!parent)
		return -ENODEV;

	if (pa) {
		...
	}
	return 0;

> +	if (parent && pa) {
> +		int ret;
> +
> +		its = (struct acpi_iort_its_group *)node->node_data;
> +		ret = iort_find_its_base(its->identifiers[0], pa);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return parent ? 0 : -ENODEV;
> +}




^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 7/7] irqchip/gic-v5: Add ACPI IWB probing
  2025-12-18 10:14 ` [PATCH v2 7/7] irqchip/gic-v5: Add ACPI IWB probing Lorenzo Pieralisi
@ 2026-01-05 15:35   ` Jonathan Cameron
  0 siblings, 0 replies; 18+ messages in thread
From: Jonathan Cameron @ 2026-01-05 15:35 UTC (permalink / raw)
  To: Lorenzo Pieralisi
  Cc: Rafael J. Wysocki, Len Brown, Robert Moore, Thomas Gleixner,
	Hanjun Guo, Sudeep Holla, Marc Zyngier, Bjorn Helgaas, linux-acpi,
	acpica-devel, linux-kernel, linux-arm-kernel, linux-pci

On Thu, 18 Dec 2025 11:14:33 +0100
Lorenzo Pieralisi <lpieralisi@kernel.org> wrote:

> To probe an IWB in an ACPI based system it is required:
> 
> - to implement the IORT functions handling the IWB IORT node and create
>   functions to retrieve IWB firmware information
> - to augment the driver to match the DSDT ACPI "ARMH0003" device and
>   retrieve the IWB wire and trigger mask from the GSI interrupt descriptor
>   in the IWB msi_domain_ops.msi_translate() function
> 
> Make the required driver changes to enable IWB probing in ACPI systems.
> 
> The GICv5 GSI format requires special handling for IWB routed IRQs.
> 
> Add IWB GSI detection to the top level driver gic_v5_get_gsi_domain_id()
> function so that the correct IRQ domain for a GSI can be detected by
> parsing the GSI and check whether it is an IWB-backed IRQ or not.
> 
> Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
> Cc: Thomas Gleixner <tglx@linutronix.de>
> Cc: Hanjun Guo <guohanjun@huawei.com>
> Cc: Sudeep Holla <sudeep.holla@arm.com>
> Cc: Marc Zyngier <maz@kernel.org>
A couple of trivial comments inline. Overall this series looks in a good
state to me.
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>

> ---
>  drivers/acpi/arm64/iort.c          | 95 ++++++++++++++++++++++++++++++++------
>  drivers/irqchip/irq-gic-v5-iwb.c   | 42 +++++++++++++----
>  drivers/irqchip/irq-gic-v5.c       |  4 ++
>  include/linux/acpi_iort.h          |  1 +
>  include/linux/irqchip/arm-gic-v5.h |  6 +++
>  5 files changed, 123 insertions(+), 25 deletions(-)
> 
> diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
> index 17dbe66da804..4b0b753db738 100644
> --- a/drivers/acpi/arm64/iort.c
> +++ b/drivers/acpi/arm64/iort.c

> @@ -317,12 +325,28 @@ static acpi_status iort_match_node_callback(struct acpi_iort_node *node,
>  	return status;
>  }
>  
> +static acpi_status iort_match_iwb_callback(struct acpi_iort_node *node, void *context)
> +{
> +	acpi_status status = AE_NOT_FOUND;
> +	u32 *id = context;
> +
> +	if (node->type == ACPI_IORT_NODE_IWB) {
> +		struct acpi_iort_iwb *iwb;
> +
> +		iwb = (struct acpi_iort_iwb *)node->node_data;
> +		status = iwb->iwb_index == *id ? AE_OK : AE_NOT_FOUND;
> +	}
> +
> +	return status;
Simpler flow with a quick exclusion of wrong nodes.
	if (node->type != ACPI_IORT_NODE_IWB)
		return AE_NOT_FOUND;
	....
	iwb = ...
	
Also not sure I'd use a ternary here given it's only slightly more code
as more readable.
	if (iwb->iwb_index != *id)
		return AE_NOT_FOUND;

	return AE_OK;

> +}


> diff --git a/drivers/irqchip/irq-gic-v5-iwb.c b/drivers/irqchip/irq-gic-v5-iwb.c
> index ad9fdc14d1c6..c7d5fd34d053 100644
> --- a/drivers/irqchip/irq-gic-v5-iwb.c
> +++ b/drivers/irqchip/irq-gic-v5-iwb.c
> @@ -4,6 +4,7 @@
>   */
>  #define pr_fmt(fmt)	"GICv5 IWB: " fmt
>  
> +#include <linux/acpi.h>
>  #include <linux/init.h>
>  #include <linux/kernel.h>
>  #include <linux/msi.h>
> @@ -136,18 +137,31 @@ static int gicv5_iwb_irq_domain_translate(struct irq_domain *d, struct irq_fwspe
>  					  irq_hw_number_t *hwirq,
>  					  unsigned int *type)
>  {
> -	if (!is_of_node(fwspec->fwnode))
> -		return -EINVAL;
> +	if (is_of_node(fwspec->fwnode)) {
>  
> -	if (fwspec->param_count < 2)
> -		return -EINVAL;
> +		if (fwspec->param_count < 2)
> +			return -EINVAL;
>  
> -	/*
> -	 * param[0] is be the wire
> -	 * param[1] is the interrupt type
> -	 */
> -	*hwirq = fwspec->param[0];
> -	*type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
> +		/*
> +		 * param[0] is be the wire
> +		 * param[1] is the interrupt type
> +		 */
> +		*hwirq = fwspec->param[0];
> +		*type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;

As below, FIELD_GET() would improve reviewability a little.


> +	}
> +
> +	if (is_acpi_device_node(fwspec->fwnode)) {
> +
> +		if (fwspec->param_count < 2)
> +			return -EINVAL;
> +
> +		/*
> +		 * Extract the wire from param[0]
> +		 * param[1] is the interrupt type
> +		 */
> +		*hwirq = FIELD_GET(GICV5_GSI_IWB_WIRE, fwspec->param[0]);
> +		*type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;

I'd prefer this FIELD_GET() for this as well so there is no need to
go sanity check that it is the lowest bits.

> +	}
>  
>  	return 0;


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 4/7] PCI/MSI: Make the pci_msi_map_rid_ctlr_node() interface firmware agnostic
  2025-12-18 10:14 ` [PATCH v2 4/7] PCI/MSI: Make the pci_msi_map_rid_ctlr_node() interface firmware agnostic Lorenzo Pieralisi
  2026-01-05 12:21   ` Jonathan Cameron
@ 2026-01-05 17:35   ` Bjorn Helgaas
  1 sibling, 0 replies; 18+ messages in thread
From: Bjorn Helgaas @ 2026-01-05 17:35 UTC (permalink / raw)
  To: Lorenzo Pieralisi
  Cc: Rafael J. Wysocki, Len Brown, Robert Moore, Thomas Gleixner,
	Hanjun Guo, Sudeep Holla, Marc Zyngier, Bjorn Helgaas, linux-acpi,
	acpica-devel, linux-kernel, linux-arm-kernel, linux-pci

On Thu, Dec 18, 2025 at 11:14:30AM +0100, Lorenzo Pieralisi wrote:
> To support booting with OF and ACPI seamlessly, GIC ITS parent code
> requires the PCI/MSI irqdomain layer to implement a function to retrieve
> an MSI controller fwnode and map an RID in a firmware agnostic way
> (ie pci_msi_map_rid_ctlr_node()).
> 
> Convert pci_msi_map_rid_ctlr_node() to an OF agnostic interface
> (fwnode_handle based) and update the GIC ITS MSI parent code to reflect
> the pci_msi_map_rid_ctlr_node() change.
> 
> Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
> Cc: Thomas Gleixner <tglx@linutronix.de>
> Cc: Bjorn Helgaas <bhelgaas@google.com>
> Cc: Marc Zyngier <maz@kernel.org>

Acked-by: Bjorn Helgaas <bhelgaas@google.com>

I assume this series will be merged via some non-PCI tree.  Let me
know if you need anything else.

> ---
>  drivers/irqchip/irq-gic-its-msi-parent.c |  8 ++++----
>  drivers/pci/msi/irqdomain.c              | 22 +++++++++++++++++-----
>  include/linux/msi.h                      |  3 ++-
>  3 files changed, 23 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/irqchip/irq-gic-its-msi-parent.c b/drivers/irqchip/irq-gic-its-msi-parent.c
> index 12f45228c867..4d1ad1ee005d 100644
> --- a/drivers/irqchip/irq-gic-its-msi-parent.c
> +++ b/drivers/irqchip/irq-gic-its-msi-parent.c
> @@ -104,7 +104,7 @@ static int its_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
>  static int its_v5_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
>  				  int nvec, msi_alloc_info_t *info)
>  {
> -	struct device_node *msi_node = NULL;
> +	struct fwnode_handle *msi_node = NULL;
>  	struct msi_domain_info *msi_info;
>  	struct pci_dev *pdev;
>  	phys_addr_t pa;
> @@ -116,15 +116,15 @@ static int its_v5_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
>  
>  	pdev = to_pci_dev(dev);
>  
> -	rid = pci_msi_map_rid_ctlr_node(pdev, &msi_node);
> +	rid = pci_msi_map_rid_ctlr_node(domain->parent, pdev, &msi_node);
>  	if (!msi_node)
>  		return -ENODEV;
>  
> -	ret = its_translate_frame_address(msi_node, &pa);
> +	ret = its_translate_frame_address(to_of_node(msi_node), &pa);
>  	if (ret)
>  		return -ENODEV;
>  
> -	of_node_put(msi_node);
> +	fwnode_handle_put(msi_node);
>  
>  	/* ITS specific DeviceID */
>  	info->scratchpad[0].ul = rid;
> diff --git a/drivers/pci/msi/irqdomain.c b/drivers/pci/msi/irqdomain.c
> index a329060287b5..3136341e802c 100644
> --- a/drivers/pci/msi/irqdomain.c
> +++ b/drivers/pci/msi/irqdomain.c
> @@ -376,23 +376,35 @@ u32 pci_msi_domain_get_msi_rid(struct irq_domain *domain, struct pci_dev *pdev)
>  }
>  
>  /**
> - * pci_msi_map_rid_ctlr_node - Get the MSI controller node and MSI requester id (RID)
> + * pci_msi_map_rid_ctlr_node - Get the MSI controller fwnode_handle and MSI requester id (RID)
> + * @domain:	The interrupt domain
>   * @pdev:	The PCI device
> - * @node:	Pointer to store the MSI controller device node
> + * @node:	Pointer to store the MSI controller fwnode_handle
>   *
> - * Use the firmware data to find the MSI controller node for @pdev.
> + * Use the firmware data to find the MSI controller fwnode_handle for @pdev.
>   * If found map the RID and initialize @node with it. @node value must
>   * be set to NULL on entry.
>   *
>   * Returns: The RID.
>   */
> -u32 pci_msi_map_rid_ctlr_node(struct pci_dev *pdev, struct device_node **node)
> +u32 pci_msi_map_rid_ctlr_node(struct irq_domain *domain, struct pci_dev *pdev,
> +			      struct fwnode_handle **node)
>  {
> +	struct device_node *of_node;
>  	u32 rid = pci_dev_id(pdev);
>  
>  	pci_for_each_dma_alias(pdev, get_msi_id_cb, &rid);
>  
> -	return of_msi_xlate(&pdev->dev, node, rid);
> +	of_node = irq_domain_get_of_node(domain);
> +	if (of_node) {
> +		struct device_node *msi_ctlr_node = NULL;
> +
> +		rid = of_msi_xlate(&pdev->dev, &msi_ctlr_node, rid);
> +		if (msi_ctlr_node)
> +			*node = of_fwnode_handle(msi_ctlr_node);
> +	}
> +
> +	return rid;
>  }
>  
>  /**
> diff --git a/include/linux/msi.h b/include/linux/msi.h
> index 8003e3218c46..8ddb05d5c96a 100644
> --- a/include/linux/msi.h
> +++ b/include/linux/msi.h
> @@ -702,7 +702,8 @@ void __pci_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg);
>  void pci_msi_mask_irq(struct irq_data *data);
>  void pci_msi_unmask_irq(struct irq_data *data);
>  u32 pci_msi_domain_get_msi_rid(struct irq_domain *domain, struct pci_dev *pdev);
> -u32 pci_msi_map_rid_ctlr_node(struct pci_dev *pdev, struct device_node **node);
> +u32 pci_msi_map_rid_ctlr_node(struct irq_domain *domain, struct pci_dev *pdev,
> +			      struct fwnode_handle **node);
>  struct irq_domain *pci_msi_get_device_domain(struct pci_dev *pdev);
>  void pci_msix_prepare_desc(struct irq_domain *domain, msi_alloc_info_t *arg,
>  			   struct msi_desc *desc);
> 
> -- 
> 2.50.1
> 


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 3/7] irqdomain: Add parent field to struct irqchip_fwid
  2026-01-05 12:01   ` Jonathan Cameron
@ 2026-01-07  8:58     ` Lorenzo Pieralisi
  2026-01-07 10:04       ` Jonathan Cameron
  0 siblings, 1 reply; 18+ messages in thread
From: Lorenzo Pieralisi @ 2026-01-07  8:58 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Rafael J. Wysocki, Len Brown, Robert Moore, Thomas Gleixner,
	Hanjun Guo, Sudeep Holla, Marc Zyngier, Bjorn Helgaas, linux-acpi,
	acpica-devel, linux-kernel, linux-arm-kernel, linux-pci

On Mon, Jan 05, 2026 at 12:01:08PM +0000, Jonathan Cameron wrote:
> On Thu, 18 Dec 2025 11:14:29 +0100
> Lorenzo Pieralisi <lpieralisi@kernel.org> wrote:
> 
> > The GICv5 driver IRQ domain hierarchy requires adding a parent field to
> > struct irqchip_fwid so that core code can reference a fwnode_handle parent
> > for a given fwnode.
> > 
> > Add a parent field to struct irqchip_fwid and update the related kernel API
> > functions to initialize and handle it.
> > 
> > Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
> > Cc: Thomas Gleixner <tglx@linutronix.de>
> > Cc: Marc Zyngier <maz@kernel.org>
> Hi Lorenzo,
> 
> Happy new year.

Happy New Year !

> > ---
> >  include/linux/irqdomain.h | 30 ++++++++++++++++++++++++++----
> >  kernel/irq/irqdomain.c    | 14 +++++++++++++-
> >  2 files changed, 39 insertions(+), 5 deletions(-)
> > 
> > diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
> > index 62f81bbeb490..b9df84b447a1 100644
> > --- a/include/linux/irqdomain.h
> > +++ b/include/linux/irqdomain.h
> > @@ -257,7 +257,8 @@ static inline void irq_domain_set_pm_device(struct irq_domain *d, struct device
> >  
> >  #ifdef CONFIG_IRQ_DOMAIN
> >  struct fwnode_handle *__irq_domain_alloc_fwnode(unsigned int type, int id,
> > -						const char *name, phys_addr_t *pa);
> > +						const char *name, phys_addr_t *pa,
> > +						struct fwnode_handle *parent);
> >  
> >  enum {
> >  	IRQCHIP_FWNODE_REAL,
> > @@ -267,18 +268,39 @@ enum {
> >  
> >  static inline struct fwnode_handle *irq_domain_alloc_named_fwnode(const char *name)
> >  {
> > -	return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_NAMED, 0, name, NULL);
> > +	return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_NAMED, 0, name, NULL, NULL);
> > +}
> > +
> > +static inline
> > +struct fwnode_handle *irq_domain_alloc_named_fwnode_parent(const char *name,
> > +							   struct fwnode_handle *parent)
> 
> The name of this makes me think it's allocating the named fwnode parent, rather that
> the named fwnode + setting it's parent.
> 
> There aren't all that many calls to irq_domain_named_fwnode(), maybe to avoid challenge
> of a new name, just add the parameter to all of them? (25ish)  Mind you the current
> pattern for similar cases is a helper, so maybe not.

Similar cases ? Have you got anything specific I can look into ?

> Or go with something similar to named and have
> 
> irq_domain_alloc_named_parented_fwnode()?

Or I can add a set_parent() helper (though that's a bit of churn IMO) ?

If Thomas has a preference I will follow that, all of the above is doable
for me.

> I'm not that bothered though if you think the current naming is the best we can do.

I think you have a point - as per my comment above.

Thanks,
Lorenzo

> Jonathan
> 
> > +{
> > +	return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_NAMED, 0, name, NULL, parent);
> >  }
> 
> 


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 4/7] PCI/MSI: Make the pci_msi_map_rid_ctlr_node() interface firmware agnostic
  2026-01-05 12:21   ` Jonathan Cameron
@ 2026-01-07  9:23     ` Lorenzo Pieralisi
  0 siblings, 0 replies; 18+ messages in thread
From: Lorenzo Pieralisi @ 2026-01-07  9:23 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Rafael J. Wysocki, Len Brown, Robert Moore, Thomas Gleixner,
	Hanjun Guo, Sudeep Holla, Marc Zyngier, Bjorn Helgaas, linux-acpi,
	acpica-devel, linux-kernel, linux-arm-kernel, linux-pci

On Mon, Jan 05, 2026 at 12:21:14PM +0000, Jonathan Cameron wrote:
> On Thu, 18 Dec 2025 11:14:30 +0100
> Lorenzo Pieralisi <lpieralisi@kernel.org> wrote:
> 
> > To support booting with OF and ACPI seamlessly, GIC ITS parent code
> > requires the PCI/MSI irqdomain layer to implement a function to retrieve
> > an MSI controller fwnode and map an RID in a firmware agnostic way
> > (ie pci_msi_map_rid_ctlr_node()).
> > 
> > Convert pci_msi_map_rid_ctlr_node() to an OF agnostic interface
> > (fwnode_handle based) and update the GIC ITS MSI parent code to reflect
> > the pci_msi_map_rid_ctlr_node() change.
> > 
> > Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
> > Cc: Thomas Gleixner <tglx@linutronix.de>
> > Cc: Bjorn Helgaas <bhelgaas@google.com>
> > Cc: Marc Zyngier <maz@kernel.org>
> Hi Lorenzo,
> 
> A few minor comments inline.  All in the 'up to you' category.
> 
> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
> 
> > ---
> 
> > diff --git a/drivers/pci/msi/irqdomain.c b/drivers/pci/msi/irqdomain.c
> > index a329060287b5..3136341e802c 100644
> > --- a/drivers/pci/msi/irqdomain.c
> > +++ b/drivers/pci/msi/irqdomain.c
> > @@ -376,23 +376,35 @@ u32 pci_msi_domain_get_msi_rid(struct irq_domain *domain, struct pci_dev *pdev)
> >  }
> >  
> >  /**
> > - * pci_msi_map_rid_ctlr_node - Get the MSI controller node and MSI requester id (RID)
> > + * pci_msi_map_rid_ctlr_node - Get the MSI controller fwnode_handle and MSI requester id (RID)
> > + * @domain:	The interrupt domain
> >   * @pdev:	The PCI device
> > - * @node:	Pointer to store the MSI controller device node
> > + * @node:	Pointer to store the MSI controller fwnode_handle
> >   *
> > - * Use the firmware data to find the MSI controller node for @pdev.
> > + * Use the firmware data to find the MSI controller fwnode_handle for @pdev.
> >   * If found map the RID and initialize @node with it. @node value must
> >   * be set to NULL on entry.
> >   *
> >   * Returns: The RID.
> >   */
> > -u32 pci_msi_map_rid_ctlr_node(struct pci_dev *pdev, struct device_node **node)
> > +u32 pci_msi_map_rid_ctlr_node(struct irq_domain *domain, struct pci_dev *pdev,
> > +			      struct fwnode_handle **node)
> >  {
> > +	struct device_node *of_node;
> >  	u32 rid = pci_dev_id(pdev);
> >  
> >  	pci_for_each_dma_alias(pdev, get_msi_id_cb, &rid);
> >  
> > -	return of_msi_xlate(&pdev->dev, node, rid);
> > +	of_node = irq_domain_get_of_node(domain);
> > +	if (of_node) {
> 
> I haven't read on but my assumption is of_node is never used for anything else.
> I'd make that explicit by not having the local variable.
> 	if (irq_domain_get_of_node(domain))
> 
> Might even be worth a comment to say this is just checking of is in use for the
> domain in general?

Yes, I thought an explicit variable would make it clearer, don't know,
not a big deal either way I believe.

> > +		struct device_node *msi_ctlr_node = NULL;
> > +
> > +		rid = of_msi_xlate(&pdev->dev, &msi_ctlr_node, rid);
> > +		if (msi_ctlr_node)
> 
> Do you need the protection? Ultimately that depends on whether
> setting *node = NULL on failure to match is a problem.
> It's a bit subtle, but if your new code matches behavior of old code
> then *node was always NULL at entry to this function so setting it
> to NULL again (which is what happens if ms_ctrl_node == NULL) should
> be fine.
> 
> Maybe it's all a bit subtle though so perhaps the check is worth having.

As above, I thought that to help understand what the function does
assigning only if !NULL would help, you are right though.

Thanks,
Lorenzo

> > +			*node = of_fwnode_handle(msi_ctlr_node);
> > +	}
> > +
> > +	return rid;
> >  }
> >  
> >  /**
> > diff --git a/include/linux/msi.h b/include/linux/msi.h
> > index 8003e3218c46..8ddb05d5c96a 100644
> > --- a/include/linux/msi.h
> > +++ b/include/linux/msi.h
> > @@ -702,7 +702,8 @@ void __pci_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg);
> >  void pci_msi_mask_irq(struct irq_data *data);
> >  void pci_msi_unmask_irq(struct irq_data *data);
> >  u32 pci_msi_domain_get_msi_rid(struct irq_domain *domain, struct pci_dev *pdev);
> > -u32 pci_msi_map_rid_ctlr_node(struct pci_dev *pdev, struct device_node **node);
> > +u32 pci_msi_map_rid_ctlr_node(struct irq_domain *domain, struct pci_dev *pdev,
> > +			      struct fwnode_handle **node);
> >  struct irq_domain *pci_msi_get_device_domain(struct pci_dev *pdev);
> >  void pci_msix_prepare_desc(struct irq_domain *domain, msi_alloc_info_t *arg,
> >  			   struct msi_desc *desc);
> > 
> 


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 3/7] irqdomain: Add parent field to struct irqchip_fwid
  2026-01-07  8:58     ` Lorenzo Pieralisi
@ 2026-01-07 10:04       ` Jonathan Cameron
  2026-01-07 17:31         ` Lorenzo Pieralisi
  0 siblings, 1 reply; 18+ messages in thread
From: Jonathan Cameron @ 2026-01-07 10:04 UTC (permalink / raw)
  To: Lorenzo Pieralisi
  Cc: Rafael J. Wysocki, Len Brown, Robert Moore, Thomas Gleixner,
	Hanjun Guo, Sudeep Holla, Marc Zyngier, Bjorn Helgaas, linux-acpi,
	acpica-devel, linux-kernel, linux-arm-kernel, linux-pci

On Wed, 7 Jan 2026 09:58:07 +0100
Lorenzo Pieralisi <lpieralisi@kernel.org> wrote:

> On Mon, Jan 05, 2026 at 12:01:08PM +0000, Jonathan Cameron wrote:
> > On Thu, 18 Dec 2025 11:14:29 +0100
> > Lorenzo Pieralisi <lpieralisi@kernel.org> wrote:
> >   
> > > The GICv5 driver IRQ domain hierarchy requires adding a parent field to
> > > struct irqchip_fwid so that core code can reference a fwnode_handle parent
> > > for a given fwnode.
> > > 
> > > Add a parent field to struct irqchip_fwid and update the related kernel API
> > > functions to initialize and handle it.
> > > 
> > > Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
> > > Cc: Thomas Gleixner <tglx@linutronix.de>
> > > Cc: Marc Zyngier <maz@kernel.org>  
> > Hi Lorenzo,
> > 
> > Happy new year.  
> 
> Happy New Year !
> 
> > > ---
> > >  include/linux/irqdomain.h | 30 ++++++++++++++++++++++++++----
> > >  kernel/irq/irqdomain.c    | 14 +++++++++++++-
> > >  2 files changed, 39 insertions(+), 5 deletions(-)
> > > 
> > > diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
> > > index 62f81bbeb490..b9df84b447a1 100644
> > > --- a/include/linux/irqdomain.h
> > > +++ b/include/linux/irqdomain.h
> > > @@ -257,7 +257,8 @@ static inline void irq_domain_set_pm_device(struct irq_domain *d, struct device
> > >  
> > >  #ifdef CONFIG_IRQ_DOMAIN
> > >  struct fwnode_handle *__irq_domain_alloc_fwnode(unsigned int type, int id,
> > > -						const char *name, phys_addr_t *pa);
> > > +						const char *name, phys_addr_t *pa,
> > > +						struct fwnode_handle *parent);
> > >  
> > >  enum {
> > >  	IRQCHIP_FWNODE_REAL,
> > > @@ -267,18 +268,39 @@ enum {
> > >  
> > >  static inline struct fwnode_handle *irq_domain_alloc_named_fwnode(const char *name)
> > >  {
> > > -	return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_NAMED, 0, name, NULL);
> > > +	return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_NAMED, 0, name, NULL, NULL);
> > > +}
> > > +
> > > +static inline
> > > +struct fwnode_handle *irq_domain_alloc_named_fwnode_parent(const char *name,
> > > +							   struct fwnode_handle *parent)  
> > 
> > The name of this makes me think it's allocating the named fwnode parent, rather that
> > the named fwnode + setting it's parent.
> > 
> > There aren't all that many calls to irq_domain_named_fwnode(), maybe to avoid challenge
> > of a new name, just add the parameter to all of them? (25ish)  Mind you the current
> > pattern for similar cases is a helper, so maybe not.  
> 
> Similar cases ? Have you got anything specific I can look into ?

I meant all the different irq_domain_alloc_xxxxx variants that call
__irq_domain_alloc_fwnode() with a subset of parameters set to NULL.

That seems to say there is a precedence for making the presence of the parameter
part of the name rather than requiring callers to set the ones they don't want to
NULL.  So it argues for a helper like this one just for consistency.

> 
> > Or go with something similar to named and have
> > 
> > irq_domain_alloc_named_parented_fwnode()?  
> 
> Or I can add a set_parent() helper (though that's a bit of churn IMO) ?
> 
> If Thomas has a preference I will follow that, all of the above is doable
> for me.

Agreed. Let's see what Thomas prefers (i.e. make the decision his problem ;)

Jonathan

> 
> > I'm not that bothered though if you think the current naming is the best we can do.  
> 
> I think you have a point - as per my comment above.
> 
> Thanks,
> Lorenzo
> 
> > Jonathan
> >   
> > > +{
> > > +	return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_NAMED, 0, name, NULL, parent);
> > >  }  
> > 
> >   
> 



^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH v2 3/7] irqdomain: Add parent field to struct irqchip_fwid
  2026-01-07 10:04       ` Jonathan Cameron
@ 2026-01-07 17:31         ` Lorenzo Pieralisi
  0 siblings, 0 replies; 18+ messages in thread
From: Lorenzo Pieralisi @ 2026-01-07 17:31 UTC (permalink / raw)
  To: Jonathan Cameron, Thomas Gleixner
  Cc: Rafael J. Wysocki, Len Brown, Robert Moore, Hanjun Guo,
	Sudeep Holla, Marc Zyngier, Bjorn Helgaas, linux-acpi,
	acpica-devel, linux-kernel, linux-arm-kernel, linux-pci

On Wed, Jan 07, 2026 at 10:04:52AM +0000, Jonathan Cameron wrote:
> On Wed, 7 Jan 2026 09:58:07 +0100
> Lorenzo Pieralisi <lpieralisi@kernel.org> wrote:
> 
> > On Mon, Jan 05, 2026 at 12:01:08PM +0000, Jonathan Cameron wrote:
> > > On Thu, 18 Dec 2025 11:14:29 +0100
> > > Lorenzo Pieralisi <lpieralisi@kernel.org> wrote:
> > >   
> > > > The GICv5 driver IRQ domain hierarchy requires adding a parent field to
> > > > struct irqchip_fwid so that core code can reference a fwnode_handle parent
> > > > for a given fwnode.
> > > > 
> > > > Add a parent field to struct irqchip_fwid and update the related kernel API
> > > > functions to initialize and handle it.
> > > > 
> > > > Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
> > > > Cc: Thomas Gleixner <tglx@linutronix.de>
> > > > Cc: Marc Zyngier <maz@kernel.org>  
> > > Hi Lorenzo,
> > > 
> > > Happy new year.  
> > 
> > Happy New Year !
> > 
> > > > ---
> > > >  include/linux/irqdomain.h | 30 ++++++++++++++++++++++++++----
> > > >  kernel/irq/irqdomain.c    | 14 +++++++++++++-
> > > >  2 files changed, 39 insertions(+), 5 deletions(-)
> > > > 
> > > > diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
> > > > index 62f81bbeb490..b9df84b447a1 100644
> > > > --- a/include/linux/irqdomain.h
> > > > +++ b/include/linux/irqdomain.h
> > > > @@ -257,7 +257,8 @@ static inline void irq_domain_set_pm_device(struct irq_domain *d, struct device
> > > >  
> > > >  #ifdef CONFIG_IRQ_DOMAIN
> > > >  struct fwnode_handle *__irq_domain_alloc_fwnode(unsigned int type, int id,
> > > > -						const char *name, phys_addr_t *pa);
> > > > +						const char *name, phys_addr_t *pa,
> > > > +						struct fwnode_handle *parent);
> > > >  
> > > >  enum {
> > > >  	IRQCHIP_FWNODE_REAL,
> > > > @@ -267,18 +268,39 @@ enum {
> > > >  
> > > >  static inline struct fwnode_handle *irq_domain_alloc_named_fwnode(const char *name)
> > > >  {
> > > > -	return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_NAMED, 0, name, NULL);
> > > > +	return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_NAMED, 0, name, NULL, NULL);
> > > > +}
> > > > +
> > > > +static inline
> > > > +struct fwnode_handle *irq_domain_alloc_named_fwnode_parent(const char *name,
> > > > +							   struct fwnode_handle *parent)  
> > > 
> > > The name of this makes me think it's allocating the named fwnode parent, rather that
> > > the named fwnode + setting it's parent.
> > > 
> > > There aren't all that many calls to irq_domain_named_fwnode(), maybe to avoid challenge
> > > of a new name, just add the parameter to all of them? (25ish)  Mind you the current
> > > pattern for similar cases is a helper, so maybe not.  
> > 
> > Similar cases ? Have you got anything specific I can look into ?
> 
> I meant all the different irq_domain_alloc_xxxxx variants that call
> __irq_domain_alloc_fwnode() with a subset of parameters set to NULL.
> 
> That seems to say there is a precedence for making the presence of the parameter
> part of the name rather than requiring callers to set the ones they don't want to
> NULL.  So it argues for a helper like this one just for consistency.

Yep that's why I wrote it this way but that does not mean it can't be
changed.

> > 
> > > Or go with something similar to named and have
> > > 
> > > irq_domain_alloc_named_parented_fwnode()?  
> > 
> > Or I can add a set_parent() helper (though that's a bit of churn IMO) ?
> > 
> > If Thomas has a preference I will follow that, all of the above is doable
> > for me.
> 
> Agreed. Let's see what Thomas prefers (i.e. make the decision his problem ;)

Thomas do you have any preference on the matter please ? It is not a big deal
either way I'd just like to respin promptly (provided the rest of the series
does not require further changes other than the ones Jon suggested and I
addressed) if possible please.

Thanks,
Lorenzo

> Jonathan
> 
> > 
> > > I'm not that bothered though if you think the current naming is the best we can do.  
> > 
> > I think you have a point - as per my comment above.
> > 
> > Thanks,
> > Lorenzo
> > 
> > > Jonathan
> > >   
> > > > +{
> > > > +	return __irq_domain_alloc_fwnode(IRQCHIP_FWNODE_NAMED, 0, name, NULL, parent);
> > > >  }  
> > > 
> > >   
> > 
> 


^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2026-01-07 17:32 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-12-18 10:14 [PATCH v2 0/7] irqchip/gic-v5: Code first ACPI boot support Lorenzo Pieralisi
2025-12-18 10:14 ` [PATCH v2 1/7] ACPICA: Add GICv5 MADT structures Lorenzo Pieralisi
2025-12-18 10:14 ` [PATCH v2 2/7] ACPICA: Add Arm IORT IWB node definitions Lorenzo Pieralisi
2025-12-18 10:14 ` [PATCH v2 3/7] irqdomain: Add parent field to struct irqchip_fwid Lorenzo Pieralisi
2026-01-05 12:01   ` Jonathan Cameron
2026-01-07  8:58     ` Lorenzo Pieralisi
2026-01-07 10:04       ` Jonathan Cameron
2026-01-07 17:31         ` Lorenzo Pieralisi
2025-12-18 10:14 ` [PATCH v2 4/7] PCI/MSI: Make the pci_msi_map_rid_ctlr_node() interface firmware agnostic Lorenzo Pieralisi
2026-01-05 12:21   ` Jonathan Cameron
2026-01-07  9:23     ` Lorenzo Pieralisi
2026-01-05 17:35   ` Bjorn Helgaas
2025-12-18 10:14 ` [PATCH v2 5/7] irqchip/gic-v5: Add ACPI IRS probing Lorenzo Pieralisi
2026-01-05 13:24   ` Jonathan Cameron
2025-12-18 10:14 ` [PATCH v2 6/7] irqchip/gic-v5: Add ACPI ITS probing Lorenzo Pieralisi
2026-01-05 13:55   ` Jonathan Cameron
2025-12-18 10:14 ` [PATCH v2 7/7] irqchip/gic-v5: Add ACPI IWB probing Lorenzo Pieralisi
2026-01-05 15:35   ` Jonathan Cameron

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).