diff options
| author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2026-02-05 12:42:18 +0100 |
|---|---|---|
| committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2026-02-05 12:42:18 +0100 |
| commit | 04cd14ff02d618e42be21f4b7ab3822c10ce97df (patch) | |
| tree | 5445bc4a49c240e4cfe8be6f7bbbaff23b37f8ec | |
| parent | 0e8ac1d3be35e51e38c263f3b46f91fb79c51e22 (diff) | |
| parent | 05bff3419adaa272713be4c07d287756a4b2c5f5 (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.c | 193 | ||||
| -rw-r--r-- | drivers/acpi/bus.c | 3 | ||||
| -rw-r--r-- | drivers/irqchip/irq-gic-its-msi-parent.c | 43 | ||||
| -rw-r--r-- | drivers/irqchip/irq-gic-v5-irs.c | 227 | ||||
| -rw-r--r-- | drivers/irqchip/irq-gic-v5-its.c | 132 | ||||
| -rw-r--r-- | drivers/irqchip/irq-gic-v5-iwb.c | 42 | ||||
| -rw-r--r-- | drivers/irqchip/irq-gic-v5.c | 138 | ||||
| -rw-r--r-- | drivers/pci/msi/irqdomain.c | 23 | ||||
| -rw-r--r-- | include/linux/acpi.h | 1 | ||||
| -rw-r--r-- | include/linux/acpi_iort.h | 11 | ||||
| -rw-r--r-- | include/linux/irqchip/arm-gic-v5.h | 8 | ||||
| -rw-r--r-- | include/linux/irqdomain.h | 30 | ||||
| -rw-r--r-- | include/linux/msi.h | 3 | ||||
| -rw-r--r-- | kernel/irq/irqdomain.c | 14 |
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; } |
