hc
2024-02-20 102a0743326a03cd1a1202ceda21e175b7d3575c
kernel/drivers/irqchip/irq-gic-v2m.c
....@@ -1,3 +1,4 @@
1
+// SPDX-License-Identifier: GPL-2.0-only
12 /*
23 * ARM GIC v2m MSI(-X) support
34 * Support for Message Signaled Interrupts for systems that
....@@ -7,10 +8,6 @@
78 * Authors: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
89 * Harish Kasiviswanathan <harish.kasiviswanathan@amd.com>
910 * Brandon Anderson <brandon.anderson@amd.com>
10
- *
11
- * This program is free software; you can redistribute it and/or modify it
12
- * under the terms of the GNU General Public License version 2 as published
13
- * by the Free Software Foundation.
1411 */
1512
1613 #define pr_fmt(fmt) "GICv2m: " fmt
....@@ -20,6 +17,7 @@
2017 #include <linux/irq.h>
2118 #include <linux/irqdomain.h>
2219 #include <linux/kernel.h>
20
+#include <linux/pci.h>
2321 #include <linux/msi.h>
2422 #include <linux/of_address.h>
2523 #include <linux/of_pci.h>
....@@ -56,6 +54,7 @@
5654
5755 /* List of flags for specific v2m implementation */
5856 #define GICV2M_NEEDS_SPI_OFFSET 0x00000001
57
+#define GICV2M_GRAVITON_ADDRESS_ONLY 0x00000002
5958
6059 static LIST_HEAD(v2m_nodes);
6160 static DEFINE_SPINLOCK(v2m_lock);
....@@ -98,19 +97,30 @@
9897 .chip = &gicv2m_msi_irq_chip,
9998 };
10099
100
+static phys_addr_t gicv2m_get_msi_addr(struct v2m_data *v2m, int hwirq)
101
+{
102
+ if (v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY)
103
+ return v2m->res.start | ((hwirq - 32) << 3);
104
+ else
105
+ return v2m->res.start + V2M_MSI_SETSPI_NS;
106
+}
107
+
101108 static void gicv2m_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
102109 {
103110 struct v2m_data *v2m = irq_data_get_irq_chip_data(data);
104
- phys_addr_t addr = v2m->res.start + V2M_MSI_SETSPI_NS;
111
+ phys_addr_t addr = gicv2m_get_msi_addr(v2m, data->hwirq);
105112
106113 msg->address_hi = upper_32_bits(addr);
107114 msg->address_lo = lower_32_bits(addr);
108
- msg->data = data->hwirq;
109115
116
+ if (v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY)
117
+ msg->data = 0;
118
+ else
119
+ msg->data = data->hwirq;
110120 if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET)
111121 msg->data -= v2m->spi_offset;
112122
113
- iommu_dma_map_msi_msg(data->irq, msg);
123
+ iommu_dma_compose_msi_msg(irq_data_get_msi_desc(data), msg);
114124 }
115125
116126 static struct irq_chip gicv2m_irq_chip = {
....@@ -167,6 +177,7 @@
167177 static int gicv2m_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
168178 unsigned int nr_irqs, void *args)
169179 {
180
+ msi_alloc_info_t *info = args;
170181 struct v2m_data *v2m = NULL, *tmp;
171182 int hwirq, offset, i, err = 0;
172183
....@@ -185,6 +196,11 @@
185196 return -ENOSPC;
186197
187198 hwirq = v2m->spi_start + offset;
199
+
200
+ err = iommu_dma_prepare_msi(info->desc,
201
+ gicv2m_get_msi_addr(v2m, hwirq));
202
+ if (err)
203
+ return err;
188204
189205 for (i = 0; i < nr_irqs; i++) {
190206 err = gicv2m_irq_gic_domain_alloc(domain, virq + i, hwirq + i);
....@@ -301,7 +317,7 @@
301317
302318 static int __init gicv2m_init_one(struct fwnode_handle *fwnode,
303319 u32 spi_start, u32 nr_spis,
304
- struct resource *res)
320
+ struct resource *res, u32 flags)
305321 {
306322 int ret;
307323 struct v2m_data *v2m;
....@@ -314,6 +330,7 @@
314330
315331 INIT_LIST_HEAD(&v2m->entry);
316332 v2m->fwnode = fwnode;
333
+ v2m->flags = flags;
317334
318335 memcpy(&v2m->res, res, sizeof(struct resource));
319336
....@@ -328,7 +345,14 @@
328345 v2m->spi_start = spi_start;
329346 v2m->nr_spis = nr_spis;
330347 } else {
331
- u32 typer = readl_relaxed(v2m->base + V2M_MSI_TYPER);
348
+ u32 typer;
349
+
350
+ /* Graviton should always have explicit spi_start/nr_spis */
351
+ if (v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY) {
352
+ ret = -EINVAL;
353
+ goto err_iounmap;
354
+ }
355
+ typer = readl_relaxed(v2m->base + V2M_MSI_TYPER);
332356
333357 v2m->spi_start = V2M_MSI_TYPER_BASE_SPI(typer);
334358 v2m->nr_spis = V2M_MSI_TYPER_NUM_SPI(typer);
....@@ -347,20 +371,23 @@
347371 * the MSI data is the absolute value within the range from
348372 * spi_start to (spi_start + num_spis).
349373 *
350
- * Broadom NS2 GICv2m implementation has an erratum where the MSI data
374
+ * Broadcom NS2 GICv2m implementation has an erratum where the MSI data
351375 * is 'spi_number - 32'
376
+ *
377
+ * Reading that register fails on the Graviton implementation
352378 */
353
- switch (readl_relaxed(v2m->base + V2M_MSI_IIDR)) {
354
- case XGENE_GICV2M_MSI_IIDR:
355
- v2m->flags |= GICV2M_NEEDS_SPI_OFFSET;
356
- v2m->spi_offset = v2m->spi_start;
357
- break;
358
- case BCM_NS2_GICV2M_MSI_IIDR:
359
- v2m->flags |= GICV2M_NEEDS_SPI_OFFSET;
360
- v2m->spi_offset = 32;
361
- break;
379
+ if (!(v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY)) {
380
+ switch (readl_relaxed(v2m->base + V2M_MSI_IIDR)) {
381
+ case XGENE_GICV2M_MSI_IIDR:
382
+ v2m->flags |= GICV2M_NEEDS_SPI_OFFSET;
383
+ v2m->spi_offset = v2m->spi_start;
384
+ break;
385
+ case BCM_NS2_GICV2M_MSI_IIDR:
386
+ v2m->flags |= GICV2M_NEEDS_SPI_OFFSET;
387
+ v2m->spi_offset = 32;
388
+ break;
389
+ }
362390 }
363
-
364391 v2m->bm = kcalloc(BITS_TO_LONGS(v2m->nr_spis), sizeof(long),
365392 GFP_KERNEL);
366393 if (!v2m->bm) {
....@@ -413,7 +440,8 @@
413440 pr_info("DT overriding V2M MSI_TYPER (base:%u, num:%u)\n",
414441 spi_start, nr_spis);
415442
416
- ret = gicv2m_init_one(&child->fwnode, spi_start, nr_spis, &res);
443
+ ret = gicv2m_init_one(&child->fwnode, spi_start, nr_spis,
444
+ &res, 0);
417445 if (ret) {
418446 of_node_put(child);
419447 break;
....@@ -445,8 +473,27 @@
445473 return data->fwnode;
446474 }
447475
476
+static bool acpi_check_amazon_graviton_quirks(void)
477
+{
478
+ static struct acpi_table_madt *madt;
479
+ acpi_status status;
480
+ bool rc = false;
481
+
482
+#define ACPI_AMZN_OEM_ID "AMAZON"
483
+
484
+ status = acpi_get_table(ACPI_SIG_MADT, 0,
485
+ (struct acpi_table_header **)&madt);
486
+
487
+ if (ACPI_FAILURE(status) || !madt)
488
+ return rc;
489
+ rc = !memcmp(madt->header.oem_id, ACPI_AMZN_OEM_ID, ACPI_OEM_ID_SIZE);
490
+ acpi_put_table((struct acpi_table_header *)madt);
491
+
492
+ return rc;
493
+}
494
+
448495 static int __init
449
-acpi_parse_madt_msi(struct acpi_subtable_header *header,
496
+acpi_parse_madt_msi(union acpi_subtable_headers *header,
450497 const unsigned long end)
451498 {
452499 int ret;
....@@ -454,6 +501,7 @@
454501 u32 spi_start = 0, nr_spis = 0;
455502 struct acpi_madt_generic_msi_frame *m;
456503 struct fwnode_handle *fwnode;
504
+ u32 flags = 0;
457505
458506 m = (struct acpi_madt_generic_msi_frame *)header;
459507 if (BAD_MADT_ENTRY(m, end))
....@@ -463,6 +511,13 @@
463511 res.end = m->base_address + SZ_4K - 1;
464512 res.flags = IORESOURCE_MEM;
465513
514
+ if (acpi_check_amazon_graviton_quirks()) {
515
+ pr_info("applying Amazon Graviton quirk\n");
516
+ res.end = res.start + SZ_8K - 1;
517
+ flags |= GICV2M_GRAVITON_ADDRESS_ONLY;
518
+ gicv2m_msi_domain_info.flags &= ~MSI_FLAG_MULTI_PCI_MSI;
519
+ }
520
+
466521 if (m->flags & ACPI_MADT_OVERRIDE_SPI_VALUES) {
467522 spi_start = m->spi_base;
468523 nr_spis = m->spi_count;
....@@ -471,13 +526,13 @@
471526 spi_start, nr_spis);
472527 }
473528
474
- fwnode = irq_domain_alloc_fwnode((void *)m->base_address);
529
+ fwnode = irq_domain_alloc_fwnode(&res.start);
475530 if (!fwnode) {
476531 pr_err("Unable to allocate GICv2m domain token\n");
477532 return -EINVAL;
478533 }
479534
480
- ret = gicv2m_init_one(fwnode, spi_start, nr_spis, &res);
535
+ ret = gicv2m_init_one(fwnode, spi_start, nr_spis, &res, flags);
481536 if (ret)
482537 irq_domain_free_fwnode(fwnode);
483538