| @@ -56,6 +56,7 @@ · 55 unmodified lines |
| 56 | 56 | |
| 57 | 57 | /* List of flags for specific v2m implementation */ |
| 58 | 58 | #define GICV2M_NEEDS_SPI_OFFSET 0x00000001 |
| 59 | +#define GICV2M_GRAVITON_ADDRESS_ONLY 0x00000002 |
| 59 | 60 | |
| 60 | 61 | static LIST_HEAD(v2m_nodes); |
| 61 | 62 | static DEFINE_SPINLOCK(v2m_lock); |
| @@ -98,15 +99,26 @@ static struct msi_domain_info gicv2m_msi_domain_info = { · 36 unmodified lines |
| 98 | 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 | 110 | static void gicv2m_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) |
| 102 | 111 | { |
| 103 | 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 | 115 | msg->address_hi = upper_32_bits(addr); |
| 107 | 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 | 122 | if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET) |
| 111 | 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 | 200 | hwirq = v2m->spi_start + offset; |
| 189 | 201 | |
| 190 | 202 | err = iommu_dma_prepare_msi(info->desc, |
| 191 | | − v2m->res.start + V2M_MSI_SETSPI_NS); |
| 203 | + gicv2m_get_msi_addr(v2m, hwirq)); |
| 192 | 204 | if (err) |
| 193 | 205 | return err; |
| 194 | 206 | |
| @@ -307,7 +319,7 @@ static int gicv2m_allocate_domains(struct irq_domain *parent) · 112 unmodified lines |
| 307 | 319 | |
| 308 | 320 | static int __init gicv2m_init_one(struct fwnode_handle *fwnode, |
| 309 | 321 | u32 spi_start, u32 nr_spis, |
| 310 | | − struct resource *res) |
| 322 | + struct resource *res, u32 flags) |
| 311 | 323 | { |
| 312 | 324 | int ret; |
| 313 | 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 | 333 | INIT_LIST_HEAD(&v2m->entry); |
| 322 | 334 | v2m->fwnode = fwnode; |
| 335 | + v2m->flags = flags; |
| 323 | 336 | |
| 324 | 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 | 347 | v2m->spi_start = spi_start; |
| 335 | 348 | v2m->nr_spis = nr_spis; |
| 336 | 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 | 359 | v2m->spi_start = V2M_MSI_TYPER_BASE_SPI(typer); |
| 340 | 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 | 376 | * Broadom NS2 GICv2m implementation has an erratum where the MSI data |
| 357 | 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)) { |
| 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 | + } |
| 368 | 392 | } |
| 369 | | − |
| 370 | 393 | v2m->bm = kcalloc(BITS_TO_LONGS(v2m->nr_spis), sizeof(long), |
| 371 | 394 | GFP_KERNEL); |
| 372 | 395 | if (!v2m->bm) { |
| @@ -419,7 +442,8 @@ static int __init gicv2m_of_init(struct fwnode_handle *parent_handle, · 46 unmodified lines |
| 419 | 442 | pr_info("DT overriding V2M MSI_TYPER (base:%u, num:%u)\n", |
| 420 | 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 | 447 | if (ret) { |
| 424 | 448 | of_node_put(child); |
| 425 | 449 | break; |
| @@ -451,6 +475,25 @@ static struct fwnode_handle *gicv2m_get_fwnode(struct device *dev) · 25 unmodified lines |
| 451 | 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 | 497 | static int __init |
| 455 | 498 | acpi_parse_madt_msi(union acpi_subtable_headers *header, |
| 456 | 499 | const unsigned long end) |
| @@ -460,6 +503,7 @@ acpi_parse_madt_msi(union acpi_subtable_headers *header, · 3 unmodified lines |
| 460 | 503 | u32 spi_start = 0, nr_spis = 0; |
| 461 | 504 | struct acpi_madt_generic_msi_frame *m; |
| 462 | 505 | struct fwnode_handle *fwnode; |
| 506 | + u32 flags = 0; |
| 463 | 507 | |
| 464 | 508 | m = (struct acpi_madt_generic_msi_frame *)header; |
| 465 | 509 | if (BAD_MADT_ENTRY(m, end)) |
| @@ -469,6 +513,13 @@ acpi_parse_madt_msi(union acpi_subtable_headers *header, · 3 unmodified lines |
| 469 | 513 | res.end = m->base_address + SZ_4K - 1; |
| 470 | 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 | 523 | if (m->flags & ACPI_MADT_OVERRIDE_SPI_VALUES) { |
| 473 | 524 | spi_start = m->spi_base; |
| 474 | 525 | nr_spis = m->spi_count; |
| @@ -483,7 +534,7 @@ acpi_parse_madt_msi(union acpi_subtable_headers *header, · 8 unmodified lines |
| 483 | 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 | 538 | if (ret) |
| 488 | 539 | irq_domain_free_fwnode(fwnode); |
| 489 | 540 | |