| @@ -56,6 +56,7 @@ · 55 unmodified lines |
| 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 = { · 36 unmodified lines |
| 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, · 75 unmodified lines |
| 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) · 112 unmodified lines |
| 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, · 6 unmodified lines |
| 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, · 8 unmodified lines |
| 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, · 14 unmodified lines |
| 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, · 46 unmodified lines |
| 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) · 25 unmodified lines |
| 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, · 3 unmodified lines |
| 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, · 3 unmodified lines |
| 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, · 8 unmodified lines |
| 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 | |