Skip to content

Commit a97efa5

Browse files
Lorenzo Pieralisirafaeljw
authored andcommitted
irqchip/gic-v5: Add ACPI ITS probing
On ACPI ARM64 systems the GICv5 ITS configuration and translate frames are described in the MADT table. Refactor the current GICv5 ITS driver code to share common functions between ACPI and OF and implement ACPI probing in the GICv5 ITS driver. Add iort_msi_xlate() to map a device ID and retrieve an MSI controller fwnode node for ACPI systems and update pci_msi_map_rid_ctlr_node() to use it in its ACPI code path. Add the required functions to IORT code for deviceID retrieval and IRQ domain registration and look-up so that the GICv5 ITS driver in an ACPI based system can be successfully probed. Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com> Acked-by: Thomas Gleixner <tglx@kernel.org> Link: https://patch.msgid.link/20260115-gicv5-host-acpi-v3-5-c13a9a150388@kernel.org Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
1 parent 35866ef commit a97efa5

7 files changed

Lines changed: 244 additions & 45 deletions

File tree

drivers/acpi/arm64/iort.c

Lines changed: 77 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -595,45 +595,45 @@ u32 iort_msi_map_id(struct device *dev, u32 input_id)
595595
}
596596

597597
/**
598-
* iort_pmsi_get_dev_id() - Get the device id for a device
598+
* iort_msi_xlate() - Map a MSI input ID for a device
599599
* @dev: The device for which the mapping is to be done.
600-
* @dev_id: The device ID found.
600+
* @input_id: The device input ID.
601+
* @fwnode: Pointer to store the fwnode.
601602
*
602-
* Returns: 0 for successful find a dev id, -ENODEV on error
603+
* Returns: mapped MSI ID on success, input ID otherwise
604+
* On success, the fwnode pointer is initialized to the MSI
605+
* controller fwnode handle.
603606
*/
604-
int iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id)
607+
u32 iort_msi_xlate(struct device *dev, u32 input_id, struct fwnode_handle **fwnode)
605608
{
606-
int i, index;
609+
struct acpi_iort_its_group *its;
607610
struct acpi_iort_node *node;
611+
u32 dev_id;
608612

609613
node = iort_find_dev_node(dev);
610614
if (!node)
611-
return -ENODEV;
615+
return input_id;
612616

613-
index = iort_get_id_mapping_index(node);
614-
/* if there is a valid index, go get the dev_id directly */
615-
if (index >= 0) {
616-
if (iort_node_get_id(node, dev_id, index))
617-
return 0;
618-
} else {
619-
for (i = 0; i < node->mapping_count; i++) {
620-
if (iort_node_map_platform_id(node, dev_id,
621-
IORT_MSI_TYPE, i))
622-
return 0;
623-
}
624-
}
617+
node = iort_node_map_id(node, input_id, &dev_id, IORT_MSI_TYPE);
618+
if (!node)
619+
return input_id;
625620

626-
return -ENODEV;
621+
/* Move to ITS specific data */
622+
its = (struct acpi_iort_its_group *)node->node_data;
623+
624+
*fwnode = iort_find_domain_token(its->identifiers[0]);
625+
626+
return dev_id;
627627
}
628628

629-
static int __maybe_unused iort_find_its_base(u32 its_id, phys_addr_t *base)
629+
int iort_its_translate_pa(struct fwnode_handle *node, phys_addr_t *base)
630630
{
631631
struct iort_its_msi_chip *its_msi_chip;
632632
int ret = -ENODEV;
633633

634634
spin_lock(&iort_msi_chip_lock);
635635
list_for_each_entry(its_msi_chip, &iort_msi_chip_list, list) {
636-
if (its_msi_chip->translation_id == its_id) {
636+
if (its_msi_chip->fw_node == node) {
637637
*base = its_msi_chip->base_addr;
638638
ret = 0;
639639
break;
@@ -644,6 +644,62 @@ static int __maybe_unused iort_find_its_base(u32 its_id, phys_addr_t *base)
644644
return ret;
645645
}
646646

647+
static int __maybe_unused iort_find_its_base(u32 its_id, phys_addr_t *base)
648+
{
649+
struct fwnode_handle *fwnode = iort_find_domain_token(its_id);
650+
651+
if (!fwnode)
652+
return -ENODEV;
653+
654+
return iort_its_translate_pa(fwnode, base);
655+
}
656+
657+
/**
658+
* iort_pmsi_get_msi_info() - Get the device id and translate frame PA for a device
659+
* @dev: The device for which the mapping is to be done.
660+
* @dev_id: The device ID found.
661+
* @pa: optional pointer to store translate frame address.
662+
*
663+
* Returns: 0 for successful devid and pa retrieval, -ENODEV on error
664+
*/
665+
int iort_pmsi_get_msi_info(struct device *dev, u32 *dev_id, phys_addr_t *pa)
666+
{
667+
struct acpi_iort_node *node, *parent = NULL;
668+
struct acpi_iort_its_group *its;
669+
int i, index;
670+
671+
node = iort_find_dev_node(dev);
672+
if (!node)
673+
return -ENODEV;
674+
675+
index = iort_get_id_mapping_index(node);
676+
/* if there is a valid index, go get the dev_id directly */
677+
if (index >= 0) {
678+
parent = iort_node_get_id(node, dev_id, index);
679+
} else {
680+
for (i = 0; i < node->mapping_count; i++) {
681+
parent = iort_node_map_platform_id(node, dev_id,
682+
IORT_MSI_TYPE, i);
683+
if (parent)
684+
break;
685+
}
686+
}
687+
688+
if (!parent)
689+
return -ENODEV;
690+
691+
if (pa) {
692+
int ret;
693+
694+
its = (struct acpi_iort_its_group *)node->node_data;
695+
ret = iort_find_its_base(its->identifiers[0], pa);
696+
if (ret)
697+
return ret;
698+
}
699+
700+
return 0;
701+
}
702+
647703
/**
648704
* iort_dev_find_its_id() - Find the ITS identifier for a device
649705
* @dev: The device.

drivers/irqchip/irq-gic-its-msi-parent.c

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,24 @@
1919
MSI_FLAG_PCI_MSIX | \
2020
MSI_FLAG_MULTI_PCI_MSI)
2121

22-
static int its_translate_frame_address(struct device_node *msi_node, phys_addr_t *pa)
22+
static int its_translate_frame_address(struct fwnode_handle *msi_node, phys_addr_t *pa)
2323
{
2424
struct resource res;
2525
int ret;
2626

27-
ret = of_property_match_string(msi_node, "reg-names", "ns-translate");
28-
if (ret < 0)
29-
return ret;
27+
if (is_of_node(msi_node)) {
28+
struct device_node *msi_np = to_of_node(msi_node);
3029

31-
ret = of_address_to_resource(msi_node, ret, &res);
32-
if (ret)
33-
return ret;
30+
ret = of_property_match_string(msi_np, "reg-names", "ns-translate");
31+
if (ret < 0)
32+
return ret;
33+
34+
ret = of_address_to_resource(msi_np, ret, &res);
35+
if (ret)
36+
return ret;
37+
} else {
38+
ret = iort_its_translate_pa(msi_node, &res.start);
39+
}
3440

3541
*pa = res.start;
3642
return 0;
@@ -120,7 +126,7 @@ static int its_v5_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
120126
if (!msi_node)
121127
return -ENODEV;
122128

123-
ret = its_translate_frame_address(to_of_node(msi_node), &pa);
129+
ret = its_translate_frame_address(msi_node, &pa);
124130
if (ret)
125131
return -ENODEV;
126132

@@ -161,7 +167,7 @@ static int of_pmsi_get_msi_info(struct irq_domain *domain, struct device *dev, u
161167
ret = -EINVAL;
162168

163169
if (!ret && pa)
164-
ret = its_translate_frame_address(it.node, pa);
170+
ret = its_translate_frame_address(of_fwnode_handle(it.node), pa);
165171

166172
if (!ret)
167173
*dev_id = args;
@@ -176,11 +182,6 @@ static int of_pmsi_get_msi_info(struct irq_domain *domain, struct device *dev, u
176182
return of_map_id(dev->of_node, dev->id, "msi-map", "msi-map-mask", &msi_ctrl, dev_id);
177183
}
178184

179-
int __weak iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id)
180-
{
181-
return -1;
182-
}
183-
184185
static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev,
185186
int nvec, msi_alloc_info_t *info)
186187
{
@@ -191,7 +192,7 @@ static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev,
191192
if (dev->of_node)
192193
ret = of_pmsi_get_msi_info(domain->parent, dev, &dev_id, NULL);
193194
else
194-
ret = iort_pmsi_get_dev_id(dev, &dev_id);
195+
ret = iort_pmsi_get_msi_info(dev, &dev_id, NULL);
195196
if (ret)
196197
return ret;
197198

@@ -214,10 +215,10 @@ static int its_v5_pmsi_prepare(struct irq_domain *domain, struct device *dev,
214215
u32 dev_id;
215216
int ret;
216217

217-
if (!dev->of_node)
218-
return -ENODEV;
219-
220-
ret = of_pmsi_get_msi_info(domain->parent, dev, &dev_id, &pa);
218+
if (dev->of_node)
219+
ret = of_pmsi_get_msi_info(domain->parent, dev, &dev_id, &pa);
220+
else
221+
ret = iort_pmsi_get_msi_info(dev, &dev_id, &pa);
221222
if (ret)
222223
return ret;
223224

drivers/irqchip/irq-gic-v5-irs.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -814,8 +814,11 @@ void __init gicv5_irs_its_probe(void)
814814
{
815815
struct gicv5_irs_chip_data *irs_data;
816816

817-
list_for_each_entry(irs_data, &irs_nodes, entry)
818-
gicv5_its_of_probe(to_of_node(irs_data->fwnode));
817+
if (acpi_disabled)
818+
list_for_each_entry(irs_data, &irs_nodes, entry)
819+
gicv5_its_of_probe(to_of_node(irs_data->fwnode));
820+
else
821+
gicv5_its_acpi_probe();
819822
}
820823

821824
int __init gicv5_irs_of_probe(struct device_node *parent)

drivers/irqchip/irq-gic-v5-its.c

Lines changed: 130 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
#define pr_fmt(fmt) "GICv5 ITS: " fmt
77

8+
#include <linux/acpi.h>
9+
#include <linux/acpi_iort.h>
810
#include <linux/bitmap.h>
911
#include <linux/iommu.h>
1012
#include <linux/init.h>
@@ -1115,7 +1117,7 @@ static int gicv5_its_init_domain(struct gicv5_its_chip_data *its, struct irq_dom
11151117
}
11161118

11171119
static int __init gicv5_its_init_bases(void __iomem *its_base, struct fwnode_handle *handle,
1118-
struct irq_domain *parent_domain)
1120+
struct irq_domain *parent_domain, bool noncoherent)
11191121
{
11201122
struct device_node *np = to_of_node(handle);
11211123
struct gicv5_its_chip_data *its_node;
@@ -1208,7 +1210,8 @@ static int __init gicv5_its_init(struct device_node *node)
12081210
}
12091211

12101212
ret = gicv5_its_init_bases(its_base, of_fwnode_handle(node),
1211-
gicv5_global_data.lpi_domain);
1213+
gicv5_global_data.lpi_domain,
1214+
of_property_read_bool(node, "dma-noncoherent"));
12121215
if (ret)
12131216
goto out_unmap;
12141217

@@ -1231,3 +1234,128 @@ void __init gicv5_its_of_probe(struct device_node *parent)
12311234
pr_err("Failed to init ITS %s\n", np->full_name);
12321235
}
12331236
}
1237+
1238+
#ifdef CONFIG_ACPI
1239+
1240+
#define ACPI_GICV5_ITS_MEM_SIZE (SZ_64K)
1241+
1242+
static struct acpi_madt_gicv5_translator *current_its_entry __initdata;
1243+
static struct fwnode_handle *current_its_fwnode __initdata;
1244+
1245+
static int __init gic_acpi_parse_madt_its_translate(union acpi_subtable_headers *header,
1246+
const unsigned long end)
1247+
{
1248+
struct acpi_madt_gicv5_translate_frame *its_frame;
1249+
struct fwnode_handle *msi_dom_handle;
1250+
struct resource res = {};
1251+
int err;
1252+
1253+
its_frame = (struct acpi_madt_gicv5_translate_frame *)header;
1254+
if (its_frame->linked_translator_id != current_its_entry->translator_id)
1255+
return 0;
1256+
1257+
res.start = its_frame->base_address;
1258+
res.end = its_frame->base_address + ACPI_GICV5_ITS_MEM_SIZE - 1;
1259+
res.flags = IORESOURCE_MEM;
1260+
1261+
msi_dom_handle = irq_domain_alloc_parented_fwnode(&res.start, current_its_fwnode);
1262+
if (!msi_dom_handle) {
1263+
pr_err("ITS@%pa: Unable to allocate GICv5 ITS translate domain token\n",
1264+
&res.start);
1265+
return -ENOMEM;
1266+
}
1267+
1268+
err = iort_register_domain_token(its_frame->translate_frame_id, res.start,
1269+
msi_dom_handle);
1270+
if (err) {
1271+
pr_err("ITS@%pa: Unable to register GICv5 ITS domain token (ITS TRANSLATE FRAME ID %d) to IORT\n",
1272+
&res.start, its_frame->translate_frame_id);
1273+
irq_domain_free_fwnode(msi_dom_handle);
1274+
return err;
1275+
}
1276+
1277+
return 0;
1278+
}
1279+
1280+
static int __init gic_acpi_free_madt_its_translate(union acpi_subtable_headers *header,
1281+
const unsigned long end)
1282+
{
1283+
struct acpi_madt_gicv5_translate_frame *its_frame;
1284+
struct fwnode_handle *msi_dom_handle;
1285+
1286+
its_frame = (struct acpi_madt_gicv5_translate_frame *)header;
1287+
if (its_frame->linked_translator_id != current_its_entry->translator_id)
1288+
return 0;
1289+
1290+
msi_dom_handle = iort_find_domain_token(its_frame->translate_frame_id);
1291+
if (!msi_dom_handle)
1292+
return 0;
1293+
1294+
iort_deregister_domain_token(its_frame->translate_frame_id);
1295+
irq_domain_free_fwnode(msi_dom_handle);
1296+
1297+
return 0;
1298+
}
1299+
1300+
static int __init gic_acpi_parse_madt_its(union acpi_subtable_headers *header,
1301+
const unsigned long end)
1302+
{
1303+
struct acpi_madt_gicv5_translator *its_entry;
1304+
struct fwnode_handle *dom_handle;
1305+
struct resource res = {};
1306+
void __iomem *its_base;
1307+
int err;
1308+
1309+
its_entry = (struct acpi_madt_gicv5_translator *)header;
1310+
res.start = its_entry->base_address;
1311+
res.end = its_entry->base_address + ACPI_GICV5_ITS_MEM_SIZE - 1;
1312+
res.flags = IORESOURCE_MEM;
1313+
1314+
if (!request_mem_region(res.start, resource_size(&res), "GICv5 ITS"))
1315+
return -EBUSY;
1316+
1317+
dom_handle = irq_domain_alloc_fwnode(&res.start);
1318+
if (!dom_handle) {
1319+
pr_err("ITS@%pa: Unable to allocate GICv5 ITS domain token\n",
1320+
&res.start);
1321+
err = -ENOMEM;
1322+
goto out_rel_res;
1323+
}
1324+
1325+
current_its_entry = its_entry;
1326+
current_its_fwnode = dom_handle;
1327+
1328+
acpi_table_parse_madt(ACPI_MADT_TYPE_GICV5_ITS_TRANSLATE,
1329+
gic_acpi_parse_madt_its_translate, 0);
1330+
1331+
its_base = ioremap(res.start, ACPI_GICV5_ITS_MEM_SIZE);
1332+
if (!its_base) {
1333+
err = -ENOMEM;
1334+
goto out_unregister;
1335+
}
1336+
1337+
err = gicv5_its_init_bases(its_base, dom_handle, gicv5_global_data.lpi_domain,
1338+
its_entry->flags & ACPI_MADT_GICV5_ITS_NON_COHERENT);
1339+
if (err)
1340+
goto out_unmap;
1341+
1342+
return 0;
1343+
1344+
out_unmap:
1345+
iounmap(its_base);
1346+
out_unregister:
1347+
acpi_table_parse_madt(ACPI_MADT_TYPE_GICV5_ITS_TRANSLATE,
1348+
gic_acpi_free_madt_its_translate, 0);
1349+
irq_domain_free_fwnode(dom_handle);
1350+
out_rel_res:
1351+
release_mem_region(res.start, resource_size(&res));
1352+
return err;
1353+
}
1354+
1355+
void __init gicv5_its_acpi_probe(void)
1356+
{
1357+
acpi_table_parse_madt(ACPI_MADT_TYPE_GICV5_ITS, gic_acpi_parse_madt_its, 0);
1358+
}
1359+
#else
1360+
void __init gicv5_its_acpi_probe(void) { }
1361+
#endif

drivers/pci/msi/irqdomain.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,8 @@ u32 pci_msi_map_rid_ctlr_node(struct irq_domain *domain, struct pci_dev *pdev,
401401
rid = of_msi_xlate(&pdev->dev, &msi_ctlr_node, rid);
402402
if (msi_ctlr_node)
403403
*node = of_fwnode_handle(msi_ctlr_node);
404+
} else {
405+
rid = iort_msi_xlate(&pdev->dev, rid, node);
404406
}
405407

406408
return rid;

0 commit comments

Comments
 (0)