* [PATCH not for merging 0/7] irqchip/gic-v5: Code first ACPI boot support
@ 2025-10-28 10:18 Lorenzo Pieralisi
2025-10-28 10:18 ` [PATCH not for merging 1/7] ACPICA: Add GICv5 MADT structures Lorenzo Pieralisi
` (6 more replies)
0 siblings, 7 replies; 8+ messages in thread
From: Lorenzo Pieralisi @ 2025-10-28 10:18 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 are being updated to support bindings
required to describe GICv5 based systems.
The ACPI specification GICv5 bindings ECR [1] was published and now it is
going through the approval process to get it ratified.
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
to make it self-contained - they should not be considered for merging
(and consequently the kernel code as well is not ready to be upstreamed,
it is posted so that it can be reviewed ahead of spec ratification, we
are not expecting any major spec changes).
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.
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 pull request [5] and
should not be considered for merging (ACPICA changes won't be finalized
until the ACPI specification is ratified), they are there so that the
kernel can map/parse GICv5 ACPI firmware data structures
[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/pull/1043
Signed-off-by: Lorenzo Pieralisi <lpieralisi@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 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 | 45 +++---
drivers/irqchip/irq-gic-v5-irs.c | 247 ++++++++++++++++++++++++-------
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, 786 insertions(+), 158 deletions(-)
---
base-commit: 211ddde0823f1442e4ad052a2f30f050145ccada
change-id: 20251022-gicv5-host-acpi-d59d6c1d3d07
Best regards,
--
Lorenzo Pieralisi <lpieralisi@kernel.org>
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH not for merging 1/7] ACPICA: Add GICv5 MADT structures
2025-10-28 10:18 [PATCH not for merging 0/7] irqchip/gic-v5: Code first ACPI boot support Lorenzo Pieralisi
@ 2025-10-28 10:18 ` Lorenzo Pieralisi
2025-10-28 10:19 ` [PATCH not for merging 2/7] ACPICA: Add Arm IORT IWB node definitions Lorenzo Pieralisi
` (5 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Lorenzo Pieralisi @ 2025-10-28 10:18 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>
ACPICA commit 3996a7eec0ded24b22fa2098fb63f4805a3e809c
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/3996a7ee
Signed-off-by: Jose Marinho <jose.marinho@arm.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] 8+ messages in thread
* [PATCH not for merging 2/7] ACPICA: Add Arm IORT IWB node definitions
2025-10-28 10:18 [PATCH not for merging 0/7] irqchip/gic-v5: Code first ACPI boot support Lorenzo Pieralisi
2025-10-28 10:18 ` [PATCH not for merging 1/7] ACPICA: Add GICv5 MADT structures Lorenzo Pieralisi
@ 2025-10-28 10:19 ` Lorenzo Pieralisi
2025-10-28 10:19 ` [PATCH not for merging 3/7] irqdomain: Add parent field to irqchip_fwid Lorenzo Pieralisi
` (4 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Lorenzo Pieralisi @ 2025-10-28 10:19 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>
ACPICA commit d68693b24e5235a7da4d57e690d3c13380c14c16
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/d68693b2
Signed-off-by: Jose Marinho <jose.marinho@arm.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] 8+ messages in thread
* [PATCH not for merging 3/7] irqdomain: Add parent field to irqchip_fwid
2025-10-28 10:18 [PATCH not for merging 0/7] irqchip/gic-v5: Code first ACPI boot support Lorenzo Pieralisi
2025-10-28 10:18 ` [PATCH not for merging 1/7] ACPICA: Add GICv5 MADT structures Lorenzo Pieralisi
2025-10-28 10:19 ` [PATCH not for merging 2/7] ACPICA: Add Arm IORT IWB node definitions Lorenzo Pieralisi
@ 2025-10-28 10:19 ` Lorenzo Pieralisi
2025-10-28 10:19 ` [PATCH not for merging 4/7] PCI/MSI: Make the pci_msi_map_rid_ctlr_node() interface firmware agnostic Lorenzo Pieralisi
` (3 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Lorenzo Pieralisi @ 2025-10-28 10:19 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 4a86e6b915dd..289a390ccbe6 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -236,7 +236,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,
@@ -246,18 +247,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 dc473faadcc8..a6e159785cc2 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] 8+ messages in thread
* [PATCH not for merging 4/7] PCI/MSI: Make the pci_msi_map_rid_ctlr_node() interface firmware agnostic
2025-10-28 10:18 [PATCH not for merging 0/7] irqchip/gic-v5: Code first ACPI boot support Lorenzo Pieralisi
` (2 preceding siblings ...)
2025-10-28 10:19 ` [PATCH not for merging 3/7] irqdomain: Add parent field to irqchip_fwid Lorenzo Pieralisi
@ 2025-10-28 10:19 ` Lorenzo Pieralisi
2025-10-28 10:19 ` [PATCH not for merging 5/7] irqchip/gic-v5: Add ACPI IRS probing Lorenzo Pieralisi
` (2 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Lorenzo Pieralisi @ 2025-10-28 10:19 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 eb1473f1448a..87b0e33870da 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 ce741ed9dc3f..d10b72aff1ee 100644
--- a/drivers/pci/msi/irqdomain.c
+++ b/drivers/pci/msi/irqdomain.c
@@ -466,23 +466,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 d415dd15a0a9..7513db929cbc 100644
--- a/include/linux/msi.h
+++ b/include/linux/msi.h
@@ -705,7 +705,8 @@ struct irq_domain *pci_msi_create_irq_domain(struct fwnode_handle *fwnode,
struct msi_domain_info *info,
struct irq_domain *parent);
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] 8+ messages in thread
* [PATCH not for merging 5/7] irqchip/gic-v5: Add ACPI IRS probing
2025-10-28 10:18 [PATCH not for merging 0/7] irqchip/gic-v5: Code first ACPI boot support Lorenzo Pieralisi
` (3 preceding siblings ...)
2025-10-28 10:19 ` [PATCH not for merging 4/7] PCI/MSI: Make the pci_msi_map_rid_ctlr_node() interface firmware agnostic Lorenzo Pieralisi
@ 2025-10-28 10:19 ` Lorenzo Pieralisi
2025-10-28 10:19 ` [PATCH not for merging 6/7] irqchip/gic-v5: Add ACPI ITS probing Lorenzo Pieralisi
2025-10-28 10:19 ` [PATCH not for merging 7/7] irqchip/gic-v5: Add ACPI IWB probing Lorenzo Pieralisi
6 siblings, 0 replies; 8+ messages in thread
From: Lorenzo Pieralisi @ 2025-10-28 10:19 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 | 240 +++++++++++++++++++++++++++++--------
drivers/irqchip/irq-gic-v5.c | 134 ++++++++++++++++++---
include/linux/acpi.h | 1 +
include/linux/irqchip/arm-gic-v5.h | 1 +
5 files changed, 309 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..a28c88480f82 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,137 @@ 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);
+
+ /* Get distributor base address */
+ 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 5ff5d99f6ead..58b75084c976 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -106,6 +106,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] 8+ messages in thread
* [PATCH not for merging 6/7] irqchip/gic-v5: Add ACPI ITS probing
2025-10-28 10:18 [PATCH not for merging 0/7] irqchip/gic-v5: Code first ACPI boot support Lorenzo Pieralisi
` (4 preceding siblings ...)
2025-10-28 10:19 ` [PATCH not for merging 5/7] irqchip/gic-v5: Add ACPI IRS probing Lorenzo Pieralisi
@ 2025-10-28 10:19 ` Lorenzo Pieralisi
2025-10-28 10:19 ` [PATCH not for merging 7/7] irqchip/gic-v5: Add ACPI IWB probing Lorenzo Pieralisi
6 siblings, 0 replies; 8+ messages in thread
From: Lorenzo Pieralisi @ 2025-10-28 10:19 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 | 41 +++++-----
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, 242 insertions(+), 46 deletions(-)
diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
index 65f0f56ad753..7354760cad5d 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 -ENXIO;
- 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 -ENXIO;
- 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 87b0e33870da..3bc4cf8f64e4 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;
@@ -200,7 +206,7 @@ static int of_v5_pmsi_get_msi_info(struct irq_domain *domain, struct device *dev
return -EINVAL;
*dev_id = args.args[0];
- ret = its_translate_frame_address(args.np, pa);
+ ret = its_translate_frame_address(of_fwnode_handle(args.np), pa);
if (ret)
return -ENODEV;
break;
@@ -213,7 +219,7 @@ static int of_v5_pmsi_get_msi_info(struct irq_domain *domain, struct device *dev
ret = of_map_id(dev->of_node, dev->id, "msi-map", "msi-map-mask", &np, dev_id);
if (np) {
- ret = its_translate_frame_address(np, pa);
+ ret = its_translate_frame_address(of_fwnode_handle(np), pa);
of_node_put(np);
}
}
@@ -221,11 +227,6 @@ static int of_v5_pmsi_get_msi_info(struct irq_domain *domain, struct device *dev
return ret;
}
-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)
{
@@ -236,7 +237,7 @@ static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev,
if (dev->of_node)
ret = of_pmsi_get_dev_id(domain->parent, dev, &dev_id);
else
- ret = iort_pmsi_get_dev_id(dev, &dev_id);
+ ret = iort_pmsi_get_msi_info(dev, &dev_id, NULL);
if (ret)
return ret;
@@ -259,10 +260,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_v5_pmsi_get_msi_info(domain->parent, dev, &dev_id, &pa);
+ if (dev->of_node)
+ ret = of_v5_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 a28c88480f82..01af03c3c8ee 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 d10b72aff1ee..539aed2e0630 100644
--- a/drivers/pci/msi/irqdomain.c
+++ b/drivers/pci/msi/irqdomain.c
@@ -492,6 +492,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] 8+ messages in thread
* [PATCH not for merging 7/7] irqchip/gic-v5: Add ACPI IWB probing
2025-10-28 10:18 [PATCH not for merging 0/7] irqchip/gic-v5: Code first ACPI boot support Lorenzo Pieralisi
` (5 preceding siblings ...)
2025-10-28 10:19 ` [PATCH not for merging 6/7] irqchip/gic-v5: Add ACPI ITS probing Lorenzo Pieralisi
@ 2025-10-28 10:19 ` Lorenzo Pieralisi
6 siblings, 0 replies; 8+ messages in thread
From: Lorenzo Pieralisi @ 2025-10-28 10:19 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 7354760cad5d..76ec31d385cd 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..f6f3d30cbc99 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;
+
+ /*
+ * param[0] is the wire
+ * 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] 8+ messages in thread
end of thread, other threads:[~2025-10-28 10:19 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-10-28 10:18 [PATCH not for merging 0/7] irqchip/gic-v5: Code first ACPI boot support Lorenzo Pieralisi
2025-10-28 10:18 ` [PATCH not for merging 1/7] ACPICA: Add GICv5 MADT structures Lorenzo Pieralisi
2025-10-28 10:19 ` [PATCH not for merging 2/7] ACPICA: Add Arm IORT IWB node definitions Lorenzo Pieralisi
2025-10-28 10:19 ` [PATCH not for merging 3/7] irqdomain: Add parent field to irqchip_fwid Lorenzo Pieralisi
2025-10-28 10:19 ` [PATCH not for merging 4/7] PCI/MSI: Make the pci_msi_map_rid_ctlr_node() interface firmware agnostic Lorenzo Pieralisi
2025-10-28 10:19 ` [PATCH not for merging 5/7] irqchip/gic-v5: Add ACPI IRS probing Lorenzo Pieralisi
2025-10-28 10:19 ` [PATCH not for merging 6/7] irqchip/gic-v5: Add ACPI ITS probing Lorenzo Pieralisi
2025-10-28 10:19 ` [PATCH not for merging 7/7] irqchip/gic-v5: Add ACPI IWB probing Lorenzo Pieralisi
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).