summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2026-02-05 12:42:18 +0100
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2026-02-05 12:42:18 +0100
commit04cd14ff02d618e42be21f4b7ab3822c10ce97df (patch)
tree5445bc4a49c240e4cfe8be6f7bbbaff23b37f8ec
parent0e8ac1d3be35e51e38c263f3b46f91fb79c51e22 (diff)
parent05bff3419adaa272713be4c07d287756a4b2c5f5 (diff)
Merge branch 'acpi-irq'
Merge ARM-related irq subsystem changes based on the recent ACPICA updates for 6.20-rc1/7.0-rc1: - Add support for GICv5 ACPI probing on ARM which is based on the GICv5 MADT structures and ARM IORT IWB node definitions recently added to ACPICA (Lorenzo Pieralisi) * acpi-irq: irqchip/gic-v5: Add ACPI IWB probing irqchip/gic-v5: Add ACPI ITS probing irqchip/gic-v5: Add ACPI IRS probing irqchip/gic-v5: Split IRS probing into OF and generic portions PCI/MSI: Make the pci_msi_map_rid_ctlr_node() interface firmware agnostic irqdomain: Add parent field to struct irqchip_fwid
-rw-r--r--drivers/acpi/arm64/iort.c193
-rw-r--r--drivers/acpi/bus.c3
-rw-r--r--drivers/irqchip/irq-gic-its-msi-parent.c43
-rw-r--r--drivers/irqchip/irq-gic-v5-irs.c227
-rw-r--r--drivers/irqchip/irq-gic-v5-its.c132
-rw-r--r--drivers/irqchip/irq-gic-v5-iwb.c42
-rw-r--r--drivers/irqchip/irq-gic-v5.c138
-rw-r--r--drivers/pci/msi/irqdomain.c23
-rw-r--r--include/linux/acpi.h1
-rw-r--r--include/linux/acpi_iort.h11
-rw-r--r--include/linux/irqchip/arm-gic-v5.h8
-rw-r--r--include/linux/irqdomain.h30
-rw-r--r--include/linux/msi.h3
-rw-r--r--kernel/irq/irqdomain.c14
14 files changed, 724 insertions, 144 deletions
diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
index 65f0f56ad753..ed827b2fc437 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 @@ out:
return status;
}
+static acpi_status iort_match_iwb_callback(struct acpi_iort_node *node, void *context)
+{
+ struct acpi_iort_iwb *iwb;
+ u32 *id = context;
+
+ if (node->type != ACPI_IORT_NODE_IWB)
+ return AE_NOT_FOUND;
+
+ iwb = (struct acpi_iort_iwb *)node->node_data;
+ if (iwb->iwb_index != *id)
+ return AE_NOT_FOUND;
+
+ return AE_OK;
+}
+
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);
}
@@ -595,45 +625,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 +674,62 @@ 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)
+ return -ENODEV;
+
+ if (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 0;
+}
+
/**
* iort_dev_find_its_id() - Find the ITS identifier for a device
* @dev: The device.
@@ -703,6 +789,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)
{
@@ -763,8 +878,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/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-its-msi-parent.c b/drivers/irqchip/irq-gic-its-msi-parent.c
index 12f45228c867..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;
@@ -104,7 +110,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,7 +122,7 @@ 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;
@@ -124,7 +130,7 @@ static int its_v5_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
if (ret)
return -ENODEV;
- of_node_put(msi_node);
+ fwnode_handle_put(msi_node);
/* ITS specific DeviceID */
info->scratchpad[0].ul = rid;
@@ -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 ce2732d649a3..fcd032b5e2fa 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,13 @@ 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)
+ 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,12 +677,52 @@ 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)
+{
+ u32 spi_count, 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")) {
+ return -ENODEV;
+ }
+
+ idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR7);
+ irs_data->spi_min = FIELD_GET(GICV5_IRS_IDR7_SPI_BASE, idr);
+
+ idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR6);
+ irs_data->spi_range = FIELD_GET(GICV5_IRS_IDR6_SPI_IRS_RANGE, idr);
+
+ /*
+ * Do the global setting only on the first IRS.
+ * Global properties (iaffid_bits, global spi count) are guaranteed to
+ * be consistent across IRSes by the architecture.
+ */
+ if (list_empty(&irs_nodes)) {
+
+ idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR1);
+ irs_setup_pri_bits(idr);
+
+ idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR5);
+
+ spi_count = FIELD_GET(GICV5_IRS_IDR5_SPI_RANGE, idr);
+ gicv5_global_data.global_spi_count = spi_count;
+
+ gicv5_init_lpi_domain();
+
+ pr_debug("Detected %u SPIs globally\n", spi_count);
+ }
+
+ 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;
- u32 idr, spi_count;
u8 iaffid_bits;
+ u32 idr;
int ret;
irs_data = kzalloc(sizeof(*irs_data), GFP_KERNEL);
@@ -705,7 +744,8 @@ static int __init gicv5_irs_init(struct device_node *node)
goto out_err;
}
- gicv5_irs_init_bases(irs_data, irs_base, &node->fwnode);
+ irs_data->fwnode = of_fwnode_handle(node);
+ gicv5_irs_init_bases(irs_data, irs_base, 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;
@@ -716,18 +756,9 @@ static int __init gicv5_irs_init(struct device_node *node)
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;
+ ret = gicv5_irs_init(irs_data);
+ if (ret)
goto out_iomem;
- }
-
- idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR7);
- irs_data->spi_min = FIELD_GET(GICV5_IRS_IDR7_SPI_BASE, idr);
-
- 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",
@@ -737,29 +768,7 @@ static int __init gicv5_irs_init(struct device_node *node)
irs_data->spi_range - 1);
}
- /*
- * Do the global setting only on the first IRS.
- * Global properties (iaffid_bits, global spi count) are guaranteed to
- * be consistent across IRSes by the architecture.
- */
- if (list_empty(&irs_nodes)) {
-
- idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR1);
- irs_setup_pri_bits(idr);
-
- idr = irs_readl_relaxed(irs_data, GICV5_IRS_IDR5);
-
- spi_count = FIELD_GET(GICV5_IRS_IDR5_SPI_RANGE, idr);
- gicv5_global_data.global_spi_count = spi_count;
-
- gicv5_init_lpi_domain();
-
- pr_debug("Detected %u SPIs globally\n", spi_count);
- }
-
- list_add_tail(&irs_data->entry, &irs_nodes);
-
- return 0;
+ return ret;
out_iomem:
iounmap(irs_base);
@@ -805,8 +814,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)
@@ -818,10 +830,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)
+ return 0;
+
+ 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);
+
+ r = gic_request_region(irs->config_base_address, ACPI_GICV5_IRS_MEM_SIZE, "GICv5 IRS");
+ if (!r) {
+ ret = -EBUSY;
+ goto out_free;
+ }
+
+ 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_release;
+ }
+
+ gicv5_irs_init_bases(irs_data, irs_base, 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_map;
+
+ 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_map:
+ iounmap(irs_base);
+out_release:
+ release_mem_region(r->start, resource_size(r));
+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-its.c b/drivers/irqchip/irq-gic-v5-its.c
index 8e22134b9f48..e24ce3d9fb62 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_parented_fwnode(&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/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 41ef286c4d78..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>
@@ -579,16 +580,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;
+
+ 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;
- *hwirq = fwspec->param[1];
+ 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 +621,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 +681,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 +747,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 +1119,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 +1146,85 @@ 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)
+{
+ 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;
+}
+
+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/drivers/pci/msi/irqdomain.c b/drivers/pci/msi/irqdomain.c
index a329060287b5..6e65f0f44112 100644
--- a/drivers/pci/msi/irqdomain.c
+++ b/drivers/pci/msi/irqdomain.c
@@ -376,23 +376,36 @@ 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)
{
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);
+ /* Check whether the domain fwnode is an OF node */
+ if (irq_domain_get_of_node(domain)) {
+ 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);
+ } else {
+ rid = iort_msi_xlate(&pdev->dev, rid, node);
+ }
+
+ return rid;
}
/**
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/acpi_iort.h b/include/linux/acpi_iort.h
index d4ed5622cf2b..17bb3374f4ca 100644
--- a/include/linux/acpi_iort.h
+++ b/include/linux/acpi_iort.h
@@ -27,12 +27,15 @@ 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);
+struct fwnode_handle *iort_iwb_handle(u32 iwb_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 +49,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 68ddcdb1cec5..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
*/
@@ -344,6 +350,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);
@@ -391,4 +398,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
diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index 62f81bbeb490..73c25d40846c 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_parented_fwnode(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_parented_fwnode(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_parented_fwnode(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/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);
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;
}