diff options
Diffstat (limited to 'drivers/irqchip/irq-gic-v2m.c')
-rw-r--r-- | drivers/irqchip/irq-gic-v2m.c | 85 |
1 files changed, 68 insertions, 17 deletions
diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c index 3c77ab676e54..5356739d4799 100644 --- a/drivers/irqchip/irq-gic-v2m.c +++ b/drivers/irqchip/irq-gic-v2m.c | |||
@@ -56,6 +56,7 @@ | |||
56 | 56 | ||
57 | /* List of flags for specific v2m implementation */ | 57 | /* List of flags for specific v2m implementation */ |
58 | #define GICV2M_NEEDS_SPI_OFFSET 0x00000001 | 58 | #define GICV2M_NEEDS_SPI_OFFSET 0x00000001 |
59 | #define GICV2M_GRAVITON_ADDRESS_ONLY 0x00000002 | ||
59 | 60 | ||
60 | static LIST_HEAD(v2m_nodes); | 61 | static LIST_HEAD(v2m_nodes); |
61 | static DEFINE_SPINLOCK(v2m_lock); | 62 | static DEFINE_SPINLOCK(v2m_lock); |
@@ -98,15 +99,26 @@ static struct msi_domain_info gicv2m_msi_domain_info = { | |||
98 | .chip = &gicv2m_msi_irq_chip, | 99 | .chip = &gicv2m_msi_irq_chip, |
99 | }; | 100 | }; |
100 | 101 | ||
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 | |||
101 | static void gicv2m_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) | 110 | static void gicv2m_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) |
102 | { | 111 | { |
103 | struct v2m_data *v2m = irq_data_get_irq_chip_data(data); | 112 | 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); |
105 | 114 | ||
106 | msg->address_hi = upper_32_bits(addr); | 115 | msg->address_hi = upper_32_bits(addr); |
107 | msg->address_lo = lower_32_bits(addr); | 116 | msg->address_lo = lower_32_bits(addr); |
108 | msg->data = data->hwirq; | ||
109 | 117 | ||
118 | if (v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY) | ||
119 | msg->data = 0; | ||
120 | else | ||
121 | msg->data = data->hwirq; | ||
110 | if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET) | 122 | if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET) |
111 | msg->data -= v2m->spi_offset; | 123 | msg->data -= v2m->spi_offset; |
112 | 124 | ||
@@ -188,7 +200,7 @@ static int gicv2m_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, | |||
188 | hwirq = v2m->spi_start + offset; | 200 | hwirq = v2m->spi_start + offset; |
189 | 201 | ||
190 | err = iommu_dma_prepare_msi(info->desc, | 202 | err = iommu_dma_prepare_msi(info->desc, |
191 | v2m->res.start + V2M_MSI_SETSPI_NS); | 203 | gicv2m_get_msi_addr(v2m, hwirq)); |
192 | if (err) | 204 | if (err) |
193 | return err; | 205 | return err; |
194 | 206 | ||
@@ -307,7 +319,7 @@ static int gicv2m_allocate_domains(struct irq_domain *parent) | |||
307 | 319 | ||
308 | static int __init gicv2m_init_one(struct fwnode_handle *fwnode, | 320 | static int __init gicv2m_init_one(struct fwnode_handle *fwnode, |
309 | u32 spi_start, u32 nr_spis, | 321 | u32 spi_start, u32 nr_spis, |
310 | struct resource *res) | 322 | struct resource *res, u32 flags) |
311 | { | 323 | { |
312 | int ret; | 324 | int ret; |
313 | struct v2m_data *v2m; | 325 | struct v2m_data *v2m; |
@@ -320,6 +332,7 @@ static int __init gicv2m_init_one(struct fwnode_handle *fwnode, | |||
320 | 332 | ||
321 | INIT_LIST_HEAD(&v2m->entry); | 333 | INIT_LIST_HEAD(&v2m->entry); |
322 | v2m->fwnode = fwnode; | 334 | v2m->fwnode = fwnode; |
335 | v2m->flags = flags; | ||
323 | 336 | ||
324 | memcpy(&v2m->res, res, sizeof(struct resource)); | 337 | memcpy(&v2m->res, res, sizeof(struct resource)); |
325 | 338 | ||
@@ -334,7 +347,14 @@ static int __init gicv2m_init_one(struct fwnode_handle *fwnode, | |||
334 | v2m->spi_start = spi_start; | 347 | v2m->spi_start = spi_start; |
335 | v2m->nr_spis = nr_spis; | 348 | v2m->nr_spis = nr_spis; |
336 | } else { | 349 | } 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); | ||
338 | 358 | ||
339 | v2m->spi_start = V2M_MSI_TYPER_BASE_SPI(typer); | 359 | v2m->spi_start = V2M_MSI_TYPER_BASE_SPI(typer); |
340 | v2m->nr_spis = V2M_MSI_TYPER_NUM_SPI(typer); | 360 | v2m->nr_spis = V2M_MSI_TYPER_NUM_SPI(typer); |
@@ -355,18 +375,21 @@ static int __init gicv2m_init_one(struct fwnode_handle *fwnode, | |||
355 | * | 375 | * |
356 | * Broadom NS2 GICv2m implementation has an erratum where the MSI data | 376 | * Broadom NS2 GICv2m implementation has an erratum where the MSI data |
357 | * is 'spi_number - 32' | 377 | * is 'spi_number - 32' |
378 | * | ||
379 | * Reading that register fails on the Graviton implementation | ||
358 | */ | 380 | */ |
359 | switch (readl_relaxed(v2m->base + V2M_MSI_IIDR)) { | 381 | if (!(v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY)) { |
360 | case XGENE_GICV2M_MSI_IIDR: | 382 | switch (readl_relaxed(v2m->base + V2M_MSI_IIDR)) { |
361 | v2m->flags |= GICV2M_NEEDS_SPI_OFFSET; | 383 | case XGENE_GICV2M_MSI_IIDR: |
362 | v2m->spi_offset = v2m->spi_start; | 384 | v2m->flags |= GICV2M_NEEDS_SPI_OFFSET; |
363 | break; | 385 | v2m->spi_offset = v2m->spi_start; |
364 | case BCM_NS2_GICV2M_MSI_IIDR: | 386 | break; |
365 | v2m->flags |= GICV2M_NEEDS_SPI_OFFSET; | 387 | case BCM_NS2_GICV2M_MSI_IIDR: |
366 | v2m->spi_offset = 32; | 388 | v2m->flags |= GICV2M_NEEDS_SPI_OFFSET; |
367 | break; | 389 | v2m->spi_offset = 32; |
390 | break; | ||
391 | } | ||
368 | } | 392 | } |
369 | |||
370 | v2m->bm = kcalloc(BITS_TO_LONGS(v2m->nr_spis), sizeof(long), | 393 | v2m->bm = kcalloc(BITS_TO_LONGS(v2m->nr_spis), sizeof(long), |
371 | GFP_KERNEL); | 394 | GFP_KERNEL); |
372 | if (!v2m->bm) { | 395 | if (!v2m->bm) { |
@@ -419,7 +442,8 @@ static int __init gicv2m_of_init(struct fwnode_handle *parent_handle, | |||
419 | pr_info("DT overriding V2M MSI_TYPER (base:%u, num:%u)\n", | 442 | pr_info("DT overriding V2M MSI_TYPER (base:%u, num:%u)\n", |
420 | spi_start, nr_spis); | 443 | spi_start, nr_spis); |
421 | 444 | ||
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); | ||
423 | if (ret) { | 447 | if (ret) { |
424 | of_node_put(child); | 448 | of_node_put(child); |
425 | break; | 449 | break; |
@@ -451,6 +475,25 @@ static struct fwnode_handle *gicv2m_get_fwnode(struct device *dev) | |||
451 | return data->fwnode; | 475 | return data->fwnode; |
452 | } | 476 | } |
453 | 477 | ||
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 | |||
454 | static int __init | 497 | static int __init |
455 | acpi_parse_madt_msi(union acpi_subtable_headers *header, | 498 | acpi_parse_madt_msi(union acpi_subtable_headers *header, |
456 | const unsigned long end) | 499 | const unsigned long end) |
@@ -460,6 +503,7 @@ acpi_parse_madt_msi(union acpi_subtable_headers *header, | |||
460 | u32 spi_start = 0, nr_spis = 0; | 503 | u32 spi_start = 0, nr_spis = 0; |
461 | struct acpi_madt_generic_msi_frame *m; | 504 | struct acpi_madt_generic_msi_frame *m; |
462 | struct fwnode_handle *fwnode; | 505 | struct fwnode_handle *fwnode; |
506 | u32 flags = 0; | ||
463 | 507 | ||
464 | m = (struct acpi_madt_generic_msi_frame *)header; | 508 | m = (struct acpi_madt_generic_msi_frame *)header; |
465 | if (BAD_MADT_ENTRY(m, end)) | 509 | if (BAD_MADT_ENTRY(m, end)) |
@@ -469,6 +513,13 @@ acpi_parse_madt_msi(union acpi_subtable_headers *header, | |||
469 | res.end = m->base_address + SZ_4K - 1; | 513 | res.end = m->base_address + SZ_4K - 1; |
470 | res.flags = IORESOURCE_MEM; | 514 | res.flags = IORESOURCE_MEM; |
471 | 515 | ||
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 | |||
472 | if (m->flags & ACPI_MADT_OVERRIDE_SPI_VALUES) { | 523 | if (m->flags & ACPI_MADT_OVERRIDE_SPI_VALUES) { |
473 | spi_start = m->spi_base; | 524 | spi_start = m->spi_base; |
474 | nr_spis = m->spi_count; | 525 | nr_spis = m->spi_count; |
@@ -483,7 +534,7 @@ acpi_parse_madt_msi(union acpi_subtable_headers *header, | |||
483 | return -EINVAL; | 534 | return -EINVAL; |
484 | } | 535 | } |
485 | 536 | ||
486 | ret = gicv2m_init_one(fwnode, spi_start, nr_spis, &res); | 537 | ret = gicv2m_init_one(fwnode, spi_start, nr_spis, &res, flags); |
487 | if (ret) | 538 | if (ret) |
488 | irq_domain_free_fwnode(fwnode); | 539 | irq_domain_free_fwnode(fwnode); |
489 | 540 | ||