Skip to content

Commit 57d7219

Browse files
Lorenzo PieralisiMarc Zyngier
authored andcommitted
irqchip/gic-v5: Add GICv5 ITS support
The GICv5 architecture implements Interrupt Translation Service (ITS) components in order to translate events coming from peripherals into interrupt events delivered to the connected IRSes. Events (ie MSI memory writes to ITS translate frame), are translated by the ITS using tables kept in memory. ITS translation tables for peripherals is kept in memory storage (device table [DT] and Interrupt Translation Table [ITT]) that is allocated by the driver on boot. Both tables can be 1- or 2-level; the structure is chosen by the driver after probing the ITS HW parameters and checking the allowed table splits and supported {device/event}_IDbits. DT table entries are allocated on demand (ie when a device is probed); the DT table is sized using the number of supported deviceID bits in that that's a system design decision (ie the number of deviceID bits implemented should reflect the number of devices expected in a system) therefore it makes sense to allocate a DT table that can cater for the maximum number of devices. DT and ITT tables are allocated using the kmalloc interface; the allocation size may be smaller than a page or larger, and must provide contiguous memory pages. LPIs INTIDs backing the device events are allocated one-by-one and only upon Linux IRQ allocation; this to avoid preallocating a large number of LPIs to cover the HW device MSI vector size whereas few MSI entries are actually enabled by a device. ITS cacheability/shareability attributes are programmed according to the provided firmware ITS description. The GICv5 partially reuses the GICv3 ITS MSI parent infrastructure and adds functions required to retrieve the ITS translate frame addresses out of msi-map and msi-parent properties to implement the GICv5 ITS MSI parent callbacks. Co-developed-by: Sascha Bischoff <[email protected]> Signed-off-by: Sascha Bischoff <[email protected]> Co-developed-by: Timothy Hayes <[email protected]> Signed-off-by: Timothy Hayes <[email protected]> Signed-off-by: Lorenzo Pieralisi <[email protected]> Reviewed-by: Marc Zyngier <[email protected]> Cc: Thomas Gleixner <[email protected]> Cc: Marc Zyngier <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Marc Zyngier <[email protected]>
1 parent 8b65db1 commit 57d7219

File tree

9 files changed

+1561
-1
lines changed

9 files changed

+1561
-1
lines changed

MAINTAINERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1970,6 +1970,7 @@ M: Marc Zyngier <[email protected]>
19701970
L: [email protected] (moderated for non-subscribers)
19711971
S: Maintained
19721972
F: Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5*.yaml
1973+
F: drivers/irqchip/irq-gic-its-msi-parent.[ch]
19731974
F: drivers/irqchip/irq-gic-v5*.[ch]
19741975
F: include/linux/irqchip/arm-gic-v5.h
19751976

drivers/irqchip/Kconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ config ARM_GIC_V5
6262
bool
6363
select IRQ_DOMAIN_HIERARCHY
6464
select GENERIC_IRQ_EFFECTIVE_AFF_MASK
65+
select GENERIC_MSI_IRQ
66+
select IRQ_MSI_LIB
67+
select ARM_GIC_ITS_PARENT
6568

6669
config ARM_NVIC
6770
bool

drivers/irqchip/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ obj-$(CONFIG_ARM_GIC_ITS_PARENT) += irq-gic-its-msi-parent.o
3737
obj-$(CONFIG_ARM_GIC_V3_ITS) += irq-gic-v3-its.o irq-gic-v4.o
3838
obj-$(CONFIG_ARM_GIC_V3_ITS_FSL_MC) += irq-gic-v3-its-fsl-mc-msi.o
3939
obj-$(CONFIG_PARTITION_PERCPU) += irq-partition-percpu.o
40-
obj-$(CONFIG_ARM_GIC_V5) += irq-gic-v5.o irq-gic-v5-irs.o
40+
obj-$(CONFIG_ARM_GIC_V5) += irq-gic-v5.o irq-gic-v5-irs.o irq-gic-v5-its.o
4141
obj-$(CONFIG_HISILICON_IRQ_MBIGEN) += irq-mbigen.o
4242
obj-$(CONFIG_ARM_NVIC) += irq-nvic.o
4343
obj-$(CONFIG_ARM_VIC) += irq-vic.o

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

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// Copyright (C) 2022 Intel
66

77
#include <linux/acpi_iort.h>
8+
#include <linux/of_address.h>
89
#include <linux/pci.h>
910

1011
#include "irq-gic-its-msi-parent.h"
@@ -18,6 +19,23 @@
1819
MSI_FLAG_PCI_MSIX | \
1920
MSI_FLAG_MULTI_PCI_MSI)
2021

22+
static int its_translate_frame_address(struct device_node *msi_node, phys_addr_t *pa)
23+
{
24+
struct resource res;
25+
int ret;
26+
27+
ret = of_property_match_string(msi_node, "reg-names", "ns-translate");
28+
if (ret < 0)
29+
return ret;
30+
31+
ret = of_address_to_resource(msi_node, ret, &res);
32+
if (ret)
33+
return ret;
34+
35+
*pa = res.start;
36+
return 0;
37+
}
38+
2139
#ifdef CONFIG_PCI_MSI
2240
static int its_pci_msi_vec_count(struct pci_dev *pdev, void *data)
2341
{
@@ -82,8 +100,46 @@ static int its_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
82100
msi_info = msi_get_domain_info(domain->parent);
83101
return msi_info->ops->msi_prepare(domain->parent, dev, nvec, info);
84102
}
103+
104+
static int its_v5_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
105+
int nvec, msi_alloc_info_t *info)
106+
{
107+
struct device_node *msi_node = NULL;
108+
struct msi_domain_info *msi_info;
109+
struct pci_dev *pdev;
110+
phys_addr_t pa;
111+
u32 rid;
112+
int ret;
113+
114+
if (!dev_is_pci(dev))
115+
return -EINVAL;
116+
117+
pdev = to_pci_dev(dev);
118+
119+
rid = pci_msi_map_rid_ctlr_node(pdev, &msi_node);
120+
if (!msi_node)
121+
return -ENODEV;
122+
123+
ret = its_translate_frame_address(msi_node, &pa);
124+
if (ret)
125+
return -ENODEV;
126+
127+
of_node_put(msi_node);
128+
129+
/* ITS specific DeviceID */
130+
info->scratchpad[0].ul = rid;
131+
/* ITS translate frame physical address */
132+
info->scratchpad[1].ul = pa;
133+
134+
/* Always allocate power of two vectors */
135+
nvec = roundup_pow_of_two(nvec);
136+
137+
msi_info = msi_get_domain_info(domain->parent);
138+
return msi_info->ops->msi_prepare(domain->parent, dev, nvec, info);
139+
}
85140
#else /* CONFIG_PCI_MSI */
86141
#define its_pci_msi_prepare NULL
142+
#define its_v5_pci_msi_prepare NULL
87143
#endif /* !CONFIG_PCI_MSI */
88144

89145
static int of_pmsi_get_dev_id(struct irq_domain *domain, struct device *dev,
@@ -118,6 +174,53 @@ static int of_pmsi_get_dev_id(struct irq_domain *domain, struct device *dev,
118174
return ret;
119175
}
120176

177+
static int of_v5_pmsi_get_msi_info(struct irq_domain *domain, struct device *dev,
178+
u32 *dev_id, phys_addr_t *pa)
179+
{
180+
int ret, index = 0;
181+
/*
182+
* Retrieve the DeviceID and the ITS translate frame node pointer
183+
* out of the msi-parent property.
184+
*/
185+
do {
186+
struct of_phandle_args args;
187+
188+
ret = of_parse_phandle_with_args(dev->of_node,
189+
"msi-parent", "#msi-cells",
190+
index, &args);
191+
if (ret)
192+
break;
193+
/*
194+
* The IRQ domain fwnode is the msi controller parent
195+
* in GICv5 (where the msi controller nodes are the
196+
* ITS translate frames).
197+
*/
198+
if (args.np->parent == irq_domain_get_of_node(domain)) {
199+
if (WARN_ON(args.args_count != 1))
200+
return -EINVAL;
201+
*dev_id = args.args[0];
202+
203+
ret = its_translate_frame_address(args.np, pa);
204+
if (ret)
205+
return -ENODEV;
206+
break;
207+
}
208+
index++;
209+
} while (!ret);
210+
211+
if (ret) {
212+
struct device_node *np = NULL;
213+
214+
ret = of_map_id(dev->of_node, dev->id, "msi-map", "msi-map-mask", &np, dev_id);
215+
if (np) {
216+
ret = its_translate_frame_address(np, pa);
217+
of_node_put(np);
218+
}
219+
}
220+
221+
return ret;
222+
}
223+
121224
int __weak iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id)
122225
{
123226
return -1;
@@ -148,6 +251,33 @@ static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev,
148251
dev, nvec, info);
149252
}
150253

254+
static int its_v5_pmsi_prepare(struct irq_domain *domain, struct device *dev,
255+
int nvec, msi_alloc_info_t *info)
256+
{
257+
struct msi_domain_info *msi_info;
258+
phys_addr_t pa;
259+
u32 dev_id;
260+
int ret;
261+
262+
if (!dev->of_node)
263+
return -ENODEV;
264+
265+
ret = of_v5_pmsi_get_msi_info(domain->parent, dev, &dev_id, &pa);
266+
if (ret)
267+
return ret;
268+
269+
/* ITS specific DeviceID */
270+
info->scratchpad[0].ul = dev_id;
271+
/* ITS translate frame physical address */
272+
info->scratchpad[1].ul = pa;
273+
274+
/* Allocate always as a power of 2 */
275+
nvec = roundup_pow_of_two(nvec);
276+
277+
msi_info = msi_get_domain_info(domain->parent);
278+
return msi_info->ops->msi_prepare(domain->parent, dev, nvec, info);
279+
}
280+
151281
static void its_msi_teardown(struct irq_domain *domain, msi_alloc_info_t *info)
152282
{
153283
struct msi_domain_info *msi_info;
@@ -199,6 +329,32 @@ static bool its_init_dev_msi_info(struct device *dev, struct irq_domain *domain,
199329
return true;
200330
}
201331

332+
static bool its_v5_init_dev_msi_info(struct device *dev, struct irq_domain *domain,
333+
struct irq_domain *real_parent, struct msi_domain_info *info)
334+
{
335+
if (!msi_lib_init_dev_msi_info(dev, domain, real_parent, info))
336+
return false;
337+
338+
switch (info->bus_token) {
339+
case DOMAIN_BUS_PCI_DEVICE_MSI:
340+
case DOMAIN_BUS_PCI_DEVICE_MSIX:
341+
info->ops->msi_prepare = its_v5_pci_msi_prepare;
342+
info->ops->msi_teardown = its_msi_teardown;
343+
break;
344+
case DOMAIN_BUS_DEVICE_MSI:
345+
case DOMAIN_BUS_WIRED_TO_MSI:
346+
info->ops->msi_prepare = its_v5_pmsi_prepare;
347+
info->ops->msi_teardown = its_msi_teardown;
348+
break;
349+
default:
350+
/* Confused. How did the lib return true? */
351+
WARN_ON_ONCE(1);
352+
return false;
353+
}
354+
355+
return true;
356+
}
357+
202358
const struct msi_parent_ops gic_v3_its_msi_parent_ops = {
203359
.supported_flags = ITS_MSI_FLAGS_SUPPORTED,
204360
.required_flags = ITS_MSI_FLAGS_REQUIRED,
@@ -208,3 +364,13 @@ const struct msi_parent_ops gic_v3_its_msi_parent_ops = {
208364
.prefix = "ITS-",
209365
.init_dev_msi_info = its_init_dev_msi_info,
210366
};
367+
368+
const struct msi_parent_ops gic_v5_its_msi_parent_ops = {
369+
.supported_flags = ITS_MSI_FLAGS_SUPPORTED,
370+
.required_flags = ITS_MSI_FLAGS_REQUIRED,
371+
.chip_flags = MSI_CHIP_FLAG_SET_EOI,
372+
.bus_select_token = DOMAIN_BUS_NEXUS,
373+
.bus_select_mask = MATCH_PCI_MSI | MATCH_PLATFORM_MSI,
374+
.prefix = "ITS-v5-",
375+
.init_dev_msi_info = its_v5_init_dev_msi_info,
376+
};

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@
77
#define _IRQ_GIC_ITS_MSI_PARENT_H
88

99
extern const struct msi_parent_ops gic_v3_its_msi_parent_ops;
10+
extern const struct msi_parent_ops gic_v5_its_msi_parent_ops;
1011

1112
#endif /* _IRQ_GIC_ITS_MSI_PARENT_H */

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,22 @@ static int gicv5_irs_wait_for_idle(struct gicv5_irs_chip_data *irs_data)
484484
GICV5_IRS_CR0_IDLE, NULL);
485485
}
486486

487+
void gicv5_irs_syncr(void)
488+
{
489+
struct gicv5_irs_chip_data *irs_data;
490+
u32 syncr;
491+
492+
irs_data = list_first_entry_or_null(&irs_nodes, struct gicv5_irs_chip_data, entry);
493+
if (WARN_ON_ONCE(!irs_data))
494+
return;
495+
496+
syncr = FIELD_PREP(GICV5_IRS_SYNCR_SYNC, 1);
497+
irs_writel_relaxed(irs_data, syncr, GICV5_IRS_SYNCR);
498+
499+
gicv5_wait_for_op(irs_data->irs_base, GICV5_IRS_SYNC_STATUSR,
500+
GICV5_IRS_SYNC_STATUSR_IDLE);
501+
}
502+
487503
int gicv5_irs_register_cpu(int cpuid)
488504
{
489505
struct gicv5_irs_chip_data *irs_data;
@@ -780,6 +796,14 @@ int __init gicv5_irs_enable(void)
780796
return 0;
781797
}
782798

799+
void __init gicv5_irs_its_probe(void)
800+
{
801+
struct gicv5_irs_chip_data *irs_data;
802+
803+
list_for_each_entry(irs_data, &irs_nodes, entry)
804+
gicv5_its_of_probe(to_of_node(irs_data->fwnode));
805+
}
806+
783807
int __init gicv5_irs_of_probe(struct device_node *parent)
784808
{
785809
struct device_node *np;

0 commit comments

Comments
 (0)