summaryrefslogtreecommitdiffstats
path: root/drivers/irqchip/irq-gic-v2m.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/irqchip/irq-gic-v2m.c')
-rw-r--r--drivers/irqchip/irq-gic-v2m.c85
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
60static LIST_HEAD(v2m_nodes); 61static LIST_HEAD(v2m_nodes);
61static DEFINE_SPINLOCK(v2m_lock); 62static 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
102static 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
101static void gicv2m_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) 110static 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
308static int __init gicv2m_init_one(struct fwnode_handle *fwnode, 320static 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
478static 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
454static int __init 497static int __init
455acpi_parse_madt_msi(union acpi_subtable_headers *header, 498acpi_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