1 // SPDX-License-Identifier: GPL-2.0-only
3 * ARM GIC v2m MSI(-X) support
4 * Support for Message Signaled Interrupts for systems that
5 * implement ARM Generic Interrupt Controller: GICv2m.
7 * Copyright (C) 2014 Advanced Micro Devices, Inc.
8 * Authors: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
9 * Harish Kasiviswanathan <harish.kasiviswanathan@amd.com>
10 * Brandon Anderson <brandon.anderson@amd.com>
13 #define pr_fmt(fmt) "GICv2m: " fmt
15 #include <linux/acpi.h>
16 #include <linux/iommu.h>
17 #include <linux/irq.h>
18 #include <linux/irqdomain.h>
19 #include <linux/kernel.h>
20 #include <linux/pci.h>
21 #include <linux/msi.h>
22 #include <linux/of_address.h>
23 #include <linux/of_pci.h>
24 #include <linux/slab.h>
25 #include <linux/spinlock.h>
26 #include <linux/irqchip/arm-gic.h>
27 #include <linux/irqchip/arm-gic-common.h>
29 #include "irq-msi-lib.h"
34 * [25:16] lowest SPI assigned to MSI
36 * [9:0] Numer of SPIs assigned to MSI
38 #define V2M_MSI_TYPER 0x008
39 #define V2M_MSI_TYPER_BASE_SHIFT 16
40 #define V2M_MSI_TYPER_BASE_MASK 0x3FF
41 #define V2M_MSI_TYPER_NUM_MASK 0x3FF
42 #define V2M_MSI_SETSPI_NS 0x040
43 #define V2M_MIN_SPI 32
44 #define V2M_MAX_SPI 1019
45 #define V2M_MSI_IIDR 0xFCC
47 #define V2M_MSI_TYPER_BASE_SPI(x) \
48 (((x) >> V2M_MSI_TYPER_BASE_SHIFT) & V2M_MSI_TYPER_BASE_MASK)
50 #define V2M_MSI_TYPER_NUM_SPI(x) ((x) & V2M_MSI_TYPER_NUM_MASK)
52 /* APM X-Gene with GICv2m MSI_IIDR register value */
53 #define XGENE_GICV2M_MSI_IIDR 0x06000170
55 /* Broadcom NS2 GICv2m MSI_IIDR register value */
56 #define BCM_NS2_GICV2M_MSI_IIDR 0x0000013f
58 /* List of flags for specific v2m implementation */
59 #define GICV2M_NEEDS_SPI_OFFSET 0x00000001
60 #define GICV2M_GRAVITON_ADDRESS_ONLY 0x00000002
62 static LIST_HEAD(v2m_nodes
);
63 static DEFINE_SPINLOCK(v2m_lock
);
66 struct list_head entry
;
67 struct fwnode_handle
*fwnode
;
68 struct resource res
; /* GICv2m resource */
69 void __iomem
*base
; /* GICv2m virt address */
70 u32 spi_start
; /* The SPI number that MSIs start */
71 u32 nr_spis
; /* The number of SPIs for MSIs */
72 u32 spi_offset
; /* offset to be subtracted from SPI number */
73 unsigned long *bm
; /* MSI vector bitmap */
74 u32 flags
; /* v2m flags for specific implementation */
77 static phys_addr_t
gicv2m_get_msi_addr(struct v2m_data
*v2m
, int hwirq
)
79 if (v2m
->flags
& GICV2M_GRAVITON_ADDRESS_ONLY
)
80 return v2m
->res
.start
| ((hwirq
- 32) << 3);
82 return v2m
->res
.start
+ V2M_MSI_SETSPI_NS
;
85 static void gicv2m_compose_msi_msg(struct irq_data
*data
, struct msi_msg
*msg
)
87 struct v2m_data
*v2m
= irq_data_get_irq_chip_data(data
);
88 phys_addr_t addr
= gicv2m_get_msi_addr(v2m
, data
->hwirq
);
90 msg
->address_hi
= upper_32_bits(addr
);
91 msg
->address_lo
= lower_32_bits(addr
);
93 if (v2m
->flags
& GICV2M_GRAVITON_ADDRESS_ONLY
)
96 msg
->data
= data
->hwirq
;
97 if (v2m
->flags
& GICV2M_NEEDS_SPI_OFFSET
)
98 msg
->data
-= v2m
->spi_offset
;
100 iommu_dma_compose_msi_msg(irq_data_get_msi_desc(data
), msg
);
103 static struct irq_chip gicv2m_irq_chip
= {
105 .irq_mask
= irq_chip_mask_parent
,
106 .irq_unmask
= irq_chip_unmask_parent
,
107 .irq_eoi
= irq_chip_eoi_parent
,
108 .irq_set_affinity
= irq_chip_set_affinity_parent
,
109 .irq_compose_msi_msg
= gicv2m_compose_msi_msg
,
112 static int gicv2m_irq_gic_domain_alloc(struct irq_domain
*domain
,
114 irq_hw_number_t hwirq
)
116 struct irq_fwspec fwspec
;
120 if (is_of_node(domain
->parent
->fwnode
)) {
121 fwspec
.fwnode
= domain
->parent
->fwnode
;
122 fwspec
.param_count
= 3;
124 fwspec
.param
[1] = hwirq
- 32;
125 fwspec
.param
[2] = IRQ_TYPE_EDGE_RISING
;
126 } else if (is_fwnode_irqchip(domain
->parent
->fwnode
)) {
127 fwspec
.fwnode
= domain
->parent
->fwnode
;
128 fwspec
.param_count
= 2;
129 fwspec
.param
[0] = hwirq
;
130 fwspec
.param
[1] = IRQ_TYPE_EDGE_RISING
;
135 err
= irq_domain_alloc_irqs_parent(domain
, virq
, 1, &fwspec
);
139 /* Configure the interrupt line to be edge */
140 d
= irq_domain_get_irq_data(domain
->parent
, virq
);
141 d
->chip
->irq_set_type(d
, IRQ_TYPE_EDGE_RISING
);
145 static void gicv2m_unalloc_msi(struct v2m_data
*v2m
, unsigned int hwirq
,
148 spin_lock(&v2m_lock
);
149 bitmap_release_region(v2m
->bm
, hwirq
- v2m
->spi_start
,
150 get_count_order(nr_irqs
));
151 spin_unlock(&v2m_lock
);
154 static int gicv2m_irq_domain_alloc(struct irq_domain
*domain
, unsigned int virq
,
155 unsigned int nr_irqs
, void *args
)
157 msi_alloc_info_t
*info
= args
;
158 struct v2m_data
*v2m
= NULL
, *tmp
;
159 int hwirq
, offset
, i
, err
= 0;
161 spin_lock(&v2m_lock
);
162 list_for_each_entry(tmp
, &v2m_nodes
, entry
) {
163 offset
= bitmap_find_free_region(tmp
->bm
, tmp
->nr_spis
,
164 get_count_order(nr_irqs
));
170 spin_unlock(&v2m_lock
);
175 hwirq
= v2m
->spi_start
+ offset
;
177 err
= iommu_dma_prepare_msi(info
->desc
,
178 gicv2m_get_msi_addr(v2m
, hwirq
));
182 for (i
= 0; i
< nr_irqs
; i
++) {
183 err
= gicv2m_irq_gic_domain_alloc(domain
, virq
+ i
, hwirq
+ i
);
187 irq_domain_set_hwirq_and_chip(domain
, virq
+ i
, hwirq
+ i
,
188 &gicv2m_irq_chip
, v2m
);
194 irq_domain_free_irqs_parent(domain
, virq
, nr_irqs
);
195 gicv2m_unalloc_msi(v2m
, hwirq
, nr_irqs
);
199 static void gicv2m_irq_domain_free(struct irq_domain
*domain
,
200 unsigned int virq
, unsigned int nr_irqs
)
202 struct irq_data
*d
= irq_domain_get_irq_data(domain
, virq
);
203 struct v2m_data
*v2m
= irq_data_get_irq_chip_data(d
);
205 gicv2m_unalloc_msi(v2m
, d
->hwirq
, nr_irqs
);
206 irq_domain_free_irqs_parent(domain
, virq
, nr_irqs
);
209 static const struct irq_domain_ops gicv2m_domain_ops
= {
210 .select
= msi_lib_irq_domain_select
,
211 .alloc
= gicv2m_irq_domain_alloc
,
212 .free
= gicv2m_irq_domain_free
,
215 static bool is_msi_spi_valid(u32 base
, u32 num
)
217 if (base
< V2M_MIN_SPI
) {
218 pr_err("Invalid MSI base SPI (base:%u)\n", base
);
222 if ((num
== 0) || (base
+ num
> V2M_MAX_SPI
)) {
223 pr_err("Number of SPIs (%u) exceed maximum (%u)\n",
224 num
, V2M_MAX_SPI
- V2M_MIN_SPI
+ 1);
231 static void __init
gicv2m_teardown(void)
233 struct v2m_data
*v2m
, *tmp
;
235 list_for_each_entry_safe(v2m
, tmp
, &v2m_nodes
, entry
) {
236 list_del(&v2m
->entry
);
237 bitmap_free(v2m
->bm
);
239 of_node_put(to_of_node(v2m
->fwnode
));
240 if (is_fwnode_irqchip(v2m
->fwnode
))
241 irq_domain_free_fwnode(v2m
->fwnode
);
247 #define GICV2M_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \
248 MSI_FLAG_USE_DEF_CHIP_OPS | \
249 MSI_FLAG_PCI_MSI_MASK_PARENT)
251 #define GICV2M_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK | \
252 MSI_FLAG_PCI_MSIX | \
253 MSI_FLAG_MULTI_PCI_MSI)
255 static struct msi_parent_ops gicv2m_msi_parent_ops
= {
256 .supported_flags
= GICV2M_MSI_FLAGS_SUPPORTED
,
257 .required_flags
= GICV2M_MSI_FLAGS_REQUIRED
,
258 .bus_select_token
= DOMAIN_BUS_NEXUS
,
259 .bus_select_mask
= MATCH_PCI_MSI
| MATCH_PLATFORM_MSI
,
261 .init_dev_msi_info
= msi_lib_init_dev_msi_info
,
264 static __init
int gicv2m_allocate_domains(struct irq_domain
*parent
)
266 struct irq_domain
*inner_domain
;
267 struct v2m_data
*v2m
;
269 v2m
= list_first_entry_or_null(&v2m_nodes
, struct v2m_data
, entry
);
273 inner_domain
= irq_domain_create_hierarchy(parent
, 0, 0, v2m
->fwnode
,
274 &gicv2m_domain_ops
, v2m
);
276 pr_err("Failed to create GICv2m domain\n");
280 irq_domain_update_bus_token(inner_domain
, DOMAIN_BUS_NEXUS
);
281 inner_domain
->flags
|= IRQ_DOMAIN_FLAG_MSI_PARENT
;
282 inner_domain
->msi_parent_ops
= &gicv2m_msi_parent_ops
;
286 static int __init
gicv2m_init_one(struct fwnode_handle
*fwnode
,
287 u32 spi_start
, u32 nr_spis
,
288 struct resource
*res
, u32 flags
)
291 struct v2m_data
*v2m
;
293 v2m
= kzalloc(sizeof(struct v2m_data
), GFP_KERNEL
);
297 INIT_LIST_HEAD(&v2m
->entry
);
298 v2m
->fwnode
= fwnode
;
301 memcpy(&v2m
->res
, res
, sizeof(struct resource
));
303 v2m
->base
= ioremap(v2m
->res
.start
, resource_size(&v2m
->res
));
305 pr_err("Failed to map GICv2m resource\n");
310 if (spi_start
&& nr_spis
) {
311 v2m
->spi_start
= spi_start
;
312 v2m
->nr_spis
= nr_spis
;
316 /* Graviton should always have explicit spi_start/nr_spis */
317 if (v2m
->flags
& GICV2M_GRAVITON_ADDRESS_ONLY
) {
321 typer
= readl_relaxed(v2m
->base
+ V2M_MSI_TYPER
);
323 v2m
->spi_start
= V2M_MSI_TYPER_BASE_SPI(typer
);
324 v2m
->nr_spis
= V2M_MSI_TYPER_NUM_SPI(typer
);
327 if (!is_msi_spi_valid(v2m
->spi_start
, v2m
->nr_spis
)) {
333 * APM X-Gene GICv2m implementation has an erratum where
334 * the MSI data needs to be the offset from the spi_start
335 * in order to trigger the correct MSI interrupt. This is
336 * different from the standard GICv2m implementation where
337 * the MSI data is the absolute value within the range from
338 * spi_start to (spi_start + num_spis).
340 * Broadcom NS2 GICv2m implementation has an erratum where the MSI data
341 * is 'spi_number - 32'
343 * Reading that register fails on the Graviton implementation
345 if (!(v2m
->flags
& GICV2M_GRAVITON_ADDRESS_ONLY
)) {
346 switch (readl_relaxed(v2m
->base
+ V2M_MSI_IIDR
)) {
347 case XGENE_GICV2M_MSI_IIDR
:
348 v2m
->flags
|= GICV2M_NEEDS_SPI_OFFSET
;
349 v2m
->spi_offset
= v2m
->spi_start
;
351 case BCM_NS2_GICV2M_MSI_IIDR
:
352 v2m
->flags
|= GICV2M_NEEDS_SPI_OFFSET
;
353 v2m
->spi_offset
= 32;
357 v2m
->bm
= bitmap_zalloc(v2m
->nr_spis
, GFP_KERNEL
);
363 list_add_tail(&v2m
->entry
, &v2m_nodes
);
365 pr_info("range%pR, SPI[%d:%d]\n", res
,
366 v2m
->spi_start
, (v2m
->spi_start
+ v2m
->nr_spis
- 1));
376 static __initconst
struct of_device_id gicv2m_device_id
[] = {
377 { .compatible
= "arm,gic-v2m-frame", },
381 static int __init
gicv2m_of_init(struct fwnode_handle
*parent_handle
,
382 struct irq_domain
*parent
)
385 struct device_node
*node
= to_of_node(parent_handle
);
386 struct device_node
*child
;
388 for (child
= of_find_matching_node(node
, gicv2m_device_id
); child
;
389 child
= of_find_matching_node(child
, gicv2m_device_id
)) {
390 u32 spi_start
= 0, nr_spis
= 0;
393 if (!of_property_read_bool(child
, "msi-controller"))
396 ret
= of_address_to_resource(child
, 0, &res
);
398 pr_err("Failed to allocate v2m resource.\n");
402 if (!of_property_read_u32(child
, "arm,msi-base-spi",
404 !of_property_read_u32(child
, "arm,msi-num-spis", &nr_spis
))
405 pr_info("DT overriding V2M MSI_TYPER (base:%u, num:%u)\n",
408 ret
= gicv2m_init_one(&child
->fwnode
, spi_start
, nr_spis
,
417 ret
= gicv2m_allocate_domains(parent
);
424 static int acpi_num_msi
;
426 static __init
struct fwnode_handle
*gicv2m_get_fwnode(struct device
*dev
)
428 struct v2m_data
*data
;
430 if (WARN_ON(acpi_num_msi
<= 0))
433 /* We only return the fwnode of the first MSI frame. */
434 data
= list_first_entry_or_null(&v2m_nodes
, struct v2m_data
, entry
);
441 static __init
bool acpi_check_amazon_graviton_quirks(void)
443 static struct acpi_table_madt
*madt
;
447 #define ACPI_AMZN_OEM_ID "AMAZON"
449 status
= acpi_get_table(ACPI_SIG_MADT
, 0,
450 (struct acpi_table_header
**)&madt
);
452 if (ACPI_FAILURE(status
) || !madt
)
454 rc
= !memcmp(madt
->header
.oem_id
, ACPI_AMZN_OEM_ID
, ACPI_OEM_ID_SIZE
);
455 acpi_put_table((struct acpi_table_header
*)madt
);
461 acpi_parse_madt_msi(union acpi_subtable_headers
*header
,
462 const unsigned long end
)
466 u32 spi_start
= 0, nr_spis
= 0;
467 struct acpi_madt_generic_msi_frame
*m
;
468 struct fwnode_handle
*fwnode
;
471 m
= (struct acpi_madt_generic_msi_frame
*)header
;
472 if (BAD_MADT_ENTRY(m
, end
))
475 res
.start
= m
->base_address
;
476 res
.end
= m
->base_address
+ SZ_4K
- 1;
477 res
.flags
= IORESOURCE_MEM
;
479 if (acpi_check_amazon_graviton_quirks()) {
480 pr_info("applying Amazon Graviton quirk\n");
481 res
.end
= res
.start
+ SZ_8K
- 1;
482 flags
|= GICV2M_GRAVITON_ADDRESS_ONLY
;
483 gicv2m_msi_parent_ops
.supported_flags
&= ~MSI_FLAG_MULTI_PCI_MSI
;
486 if (m
->flags
& ACPI_MADT_OVERRIDE_SPI_VALUES
) {
487 spi_start
= m
->spi_base
;
488 nr_spis
= m
->spi_count
;
490 pr_info("ACPI overriding V2M MSI_TYPER (base:%u, num:%u)\n",
494 fwnode
= irq_domain_alloc_fwnode(&res
.start
);
496 pr_err("Unable to allocate GICv2m domain token\n");
500 ret
= gicv2m_init_one(fwnode
, spi_start
, nr_spis
, &res
, flags
);
502 irq_domain_free_fwnode(fwnode
);
507 static int __init
gicv2m_acpi_init(struct irq_domain
*parent
)
511 if (acpi_num_msi
> 0)
514 acpi_num_msi
= acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_MSI_FRAME
,
515 acpi_parse_madt_msi
, 0);
517 if (acpi_num_msi
<= 0)
520 ret
= gicv2m_allocate_domains(parent
);
524 pci_msi_register_fwnode_provider(&gicv2m_get_fwnode
);
532 #else /* CONFIG_ACPI */
533 static int __init
gicv2m_acpi_init(struct irq_domain
*parent
)
537 #endif /* CONFIG_ACPI */
539 int __init
gicv2m_init(struct fwnode_handle
*parent_handle
,
540 struct irq_domain
*parent
)
542 if (is_of_node(parent_handle
))
543 return gicv2m_of_init(parent_handle
, parent
);
545 return gicv2m_acpi_init(parent
);