Skip to content

Commit 90b4c55

Browse files
Zeev ZilbermanMarc Zyngier
Zeev Zilberman
authored and
Marc Zyngier
committed
irqchip/gic-v2m: Add support for Amazon Graviton variant of GICv3+GICv2m
Add support for Amazon Graviton custom variant of GICv2m, where the message is encoded using the MSI message address, as opposed to standard GICv2m, where the SPI number is encoded in the MSI message data. In addition, the Graviton flavor of GICv2m is used along GICv3 (and not GICv2). Co-developed-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Zeev Zilberman <zeev@amazon.com> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
1 parent 4770533 commit 90b4c55

File tree

4 files changed

+76
-20
lines changed

4 files changed

+76
-20
lines changed

drivers/irqchip/irq-gic-v2m.c

Lines changed: 68 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656

5757
/* List of flags for specific v2m implementation */
5858
#define GICV2M_NEEDS_SPI_OFFSET 0x00000001
59+
#define GICV2M_GRAVITON_ADDRESS_ONLY 0x00000002
5960

6061
static LIST_HEAD(v2m_nodes);
6162
static DEFINE_SPINLOCK(v2m_lock);
@@ -98,15 +99,26 @@ static struct msi_domain_info gicv2m_msi_domain_info = {
9899
.chip = &gicv2m_msi_irq_chip,
99100
};
100101

102+
static phys_addr_t gicv2m_get_msi_addr(struct v2m_data *v2m, int hwirq)
103+
{
104+
if (v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY)
105+
return v2m->res.start | ((hwirq - 32) << 3);
106+
else
107+
return v2m->res.start + V2M_MSI_SETSPI_NS;
108+
}
109+
101110
static void gicv2m_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
102111
{
103112
struct v2m_data *v2m = irq_data_get_irq_chip_data(data);
104-
phys_addr_t addr = v2m->res.start + V2M_MSI_SETSPI_NS;
113+
phys_addr_t addr = gicv2m_get_msi_addr(v2m, data->hwirq);
105114

106115
msg->address_hi = upper_32_bits(addr);
107116
msg->address_lo = lower_32_bits(addr);
108-
msg->data = data->hwirq;
109117

118+
if (v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY)
119+
msg->data = 0;
120+
else
121+
msg->data = data->hwirq;
110122
if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET)
111123
msg->data -= v2m->spi_offset;
112124

@@ -188,7 +200,7 @@ static int gicv2m_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
188200
hwirq = v2m->spi_start + offset;
189201

190202
err = iommu_dma_prepare_msi(info->desc,
191-
v2m->res.start + V2M_MSI_SETSPI_NS);
203+
gicv2m_get_msi_addr(v2m, hwirq));
192204
if (err)
193205
return err;
194206

@@ -307,7 +319,7 @@ static int gicv2m_allocate_domains(struct irq_domain *parent)
307319

308320
static int __init gicv2m_init_one(struct fwnode_handle *fwnode,
309321
u32 spi_start, u32 nr_spis,
310-
struct resource *res)
322+
struct resource *res, u32 flags)
311323
{
312324
int ret;
313325
struct v2m_data *v2m;
@@ -320,6 +332,7 @@ static int __init gicv2m_init_one(struct fwnode_handle *fwnode,
320332

321333
INIT_LIST_HEAD(&v2m->entry);
322334
v2m->fwnode = fwnode;
335+
v2m->flags = flags;
323336

324337
memcpy(&v2m->res, res, sizeof(struct resource));
325338

@@ -334,7 +347,14 @@ static int __init gicv2m_init_one(struct fwnode_handle *fwnode,
334347
v2m->spi_start = spi_start;
335348
v2m->nr_spis = nr_spis;
336349
} else {
337-
u32 typer = readl_relaxed(v2m->base + V2M_MSI_TYPER);
350+
u32 typer;
351+
352+
/* Graviton should always have explicit spi_start/nr_spis */
353+
if (v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY) {
354+
ret = -EINVAL;
355+
goto err_iounmap;
356+
}
357+
typer = readl_relaxed(v2m->base + V2M_MSI_TYPER);
338358

339359
v2m->spi_start = V2M_MSI_TYPER_BASE_SPI(typer);
340360
v2m->nr_spis = V2M_MSI_TYPER_NUM_SPI(typer);
@@ -355,18 +375,21 @@ static int __init gicv2m_init_one(struct fwnode_handle *fwnode,
355375
*
356376
* Broadom NS2 GICv2m implementation has an erratum where the MSI data
357377
* is 'spi_number - 32'
378+
*
379+
* Reading that register fails on the Graviton implementation
358380
*/
359-
switch (readl_relaxed(v2m->base + V2M_MSI_IIDR)) {
360-
case XGENE_GICV2M_MSI_IIDR:
361-
v2m->flags |= GICV2M_NEEDS_SPI_OFFSET;
362-
v2m->spi_offset = v2m->spi_start;
363-
break;
364-
case BCM_NS2_GICV2M_MSI_IIDR:
365-
v2m->flags |= GICV2M_NEEDS_SPI_OFFSET;
366-
v2m->spi_offset = 32;
367-
break;
381+
if (!(v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY)) {
382+
switch (readl_relaxed(v2m->base + V2M_MSI_IIDR)) {
383+
case XGENE_GICV2M_MSI_IIDR:
384+
v2m->flags |= GICV2M_NEEDS_SPI_OFFSET;
385+
v2m->spi_offset = v2m->spi_start;
386+
break;
387+
case BCM_NS2_GICV2M_MSI_IIDR:
388+
v2m->flags |= GICV2M_NEEDS_SPI_OFFSET;
389+
v2m->spi_offset = 32;
390+
break;
391+
}
368392
}
369-
370393
v2m->bm = kcalloc(BITS_TO_LONGS(v2m->nr_spis), sizeof(long),
371394
GFP_KERNEL);
372395
if (!v2m->bm) {
@@ -419,7 +442,8 @@ static int __init gicv2m_of_init(struct fwnode_handle *parent_handle,
419442
pr_info("DT overriding V2M MSI_TYPER (base:%u, num:%u)\n",
420443
spi_start, nr_spis);
421444

422-
ret = gicv2m_init_one(&child->fwnode, spi_start, nr_spis, &res);
445+
ret = gicv2m_init_one(&child->fwnode, spi_start, nr_spis,
446+
&res, 0);
423447
if (ret) {
424448
of_node_put(child);
425449
break;
@@ -451,6 +475,25 @@ static struct fwnode_handle *gicv2m_get_fwnode(struct device *dev)
451475
return data->fwnode;
452476
}
453477

478+
static bool acpi_check_amazon_graviton_quirks(void)
479+
{
480+
static struct acpi_table_madt *madt;
481+
acpi_status status;
482+
bool rc = false;
483+
484+
#define ACPI_AMZN_OEM_ID "AMAZON"
485+
486+
status = acpi_get_table(ACPI_SIG_MADT, 0,
487+
(struct acpi_table_header **)&madt);
488+
489+
if (ACPI_FAILURE(status) || !madt)
490+
return rc;
491+
rc = !memcmp(madt->header.oem_id, ACPI_AMZN_OEM_ID, ACPI_OEM_ID_SIZE);
492+
acpi_put_table((struct acpi_table_header *)madt);
493+
494+
return rc;
495+
}
496+
454497
static int __init
455498
acpi_parse_madt_msi(union acpi_subtable_headers *header,
456499
const unsigned long end)
@@ -460,6 +503,7 @@ acpi_parse_madt_msi(union acpi_subtable_headers *header,
460503
u32 spi_start = 0, nr_spis = 0;
461504
struct acpi_madt_generic_msi_frame *m;
462505
struct fwnode_handle *fwnode;
506+
u32 flags = 0;
463507

464508
m = (struct acpi_madt_generic_msi_frame *)header;
465509
if (BAD_MADT_ENTRY(m, end))
@@ -469,6 +513,13 @@ acpi_parse_madt_msi(union acpi_subtable_headers *header,
469513
res.end = m->base_address + SZ_4K - 1;
470514
res.flags = IORESOURCE_MEM;
471515

516+
if (acpi_check_amazon_graviton_quirks()) {
517+
pr_info("applying Amazon Graviton quirk\n");
518+
res.end = res.start + SZ_8K - 1;
519+
flags |= GICV2M_GRAVITON_ADDRESS_ONLY;
520+
gicv2m_msi_domain_info.flags &= ~MSI_FLAG_MULTI_PCI_MSI;
521+
}
522+
472523
if (m->flags & ACPI_MADT_OVERRIDE_SPI_VALUES) {
473524
spi_start = m->spi_base;
474525
nr_spis = m->spi_count;
@@ -483,7 +534,7 @@ acpi_parse_madt_msi(union acpi_subtable_headers *header,
483534
return -EINVAL;
484535
}
485536

486-
ret = gicv2m_init_one(fwnode, spi_start, nr_spis, &res);
537+
ret = gicv2m_init_one(fwnode, spi_start, nr_spis, &res, flags);
487538
if (ret)
488539
irq_domain_free_fwnode(fwnode);
489540

drivers/irqchip/irq-gic-v3.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1343,6 +1343,9 @@ static int __init gic_init_bases(void __iomem *dist_base,
13431343
if (gic_dist_supports_lpis()) {
13441344
its_init(handle, &gic_data.rdists, gic_data.domain);
13451345
its_cpu_init();
1346+
} else {
1347+
if (IS_ENABLED(CONFIG_ARM_GIC_V2M))
1348+
gicv2m_init(handle, gic_data.domain);
13461349
}
13471350

13481351
if (gic_prio_masking_enabled()) {

include/linux/irqchip/arm-gic-common.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,9 @@ struct gic_kvm_info {
3939

4040
const struct gic_kvm_info *gic_get_kvm_info(void);
4141

42+
struct irq_domain;
43+
struct fwnode_handle;
44+
int gicv2m_init(struct fwnode_handle *parent_handle,
45+
struct irq_domain *parent);
46+
4247
#endif /* __LINUX_IRQCHIP_ARM_GIC_COMMON_H */

include/linux/irqchip/arm-gic.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,6 @@ int gic_of_init_child(struct device *dev, struct gic_chip_data **gic, int irq);
160160
*/
161161
void gic_init(void __iomem *dist , void __iomem *cpu);
162162

163-
int gicv2m_init(struct fwnode_handle *parent_handle,
164-
struct irq_domain *parent);
165-
166163
void gic_send_sgi(unsigned int cpu_id, unsigned int irq);
167164
int gic_get_cpu_id(unsigned int cpu);
168165
void gic_migrate_target(unsigned int new_cpu_id);

0 commit comments

Comments
 (0)