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
This commit is contained in:
Rafael J. Wysocki
2026-02-05 12:42:18 +01:00
14 changed files with 734 additions and 154 deletions

View File

@@ -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)
{
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++) {

View File

@@ -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;

View File

@@ -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;

View File

@@ -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,49 +677,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 +692,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 +715,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;
}
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;
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);
@@ -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

View File

@@ -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

View File

@@ -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,

View File

@@ -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;
*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 +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

View File

@@ -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;
}
/**

View File

@@ -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

View File

@@ -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) { }

View File

@@ -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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;
}