summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorZeev Zilberman <zeev@amazon.com>2019-06-10 06:52:01 -0400
committerMarc Zyngier <marc.zyngier@arm.com>2019-06-11 07:14:35 -0400
commit90b4c55586155cf13bbafbd4e55327f89681859d (patch)
treefb1093057d515ae1e24ab38f104f00f5131af1f0
parent4770533f71de8d1891795d92b55633d82a80f882 (diff)
irqchip/gic-v2m: Add support for Amazon Graviton variant of GICv3+GICv2m
Add support for Amazon Graviton custom variant of GICv2m, where the message is encoded using the MSI message address, as opposed to standard GICv2m, where the SPI number is encoded in the MSI message data. In addition, the Graviton flavor of GICv2m is used along GICv3 (and not GICv2). Co-developed-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Zeev Zilberman <zeev@amazon.com> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
-rw-r--r--drivers/irqchip/irq-gic-v2m.c85
-rw-r--r--drivers/irqchip/irq-gic-v3.c3
-rw-r--r--include/linux/irqchip/arm-gic-common.h5
-rw-r--r--include/linux/irqchip/arm-gic.h3
4 files changed, 76 insertions, 20 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
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index f44cd89cfc40..1282f81696b2 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -1343,6 +1343,9 @@ static int __init gic_init_bases(void __iomem *dist_base,
1343 if (gic_dist_supports_lpis()) { 1343 if (gic_dist_supports_lpis()) {
1344 its_init(handle, &gic_data.rdists, gic_data.domain); 1344 its_init(handle, &gic_data.rdists, gic_data.domain);
1345 its_cpu_init(); 1345 its_cpu_init();
1346 } else {
1347 if (IS_ENABLED(CONFIG_ARM_GIC_V2M))
1348 gicv2m_init(handle, gic_data.domain);
1346 } 1349 }
1347 1350
1348 if (gic_prio_masking_enabled()) { 1351 if (gic_prio_masking_enabled()) {
diff --git a/include/linux/irqchip/arm-gic-common.h b/include/linux/irqchip/arm-gic-common.h
index 9a1a479a2bf4..62a882104790 100644
--- a/include/linux/irqchip/arm-gic-common.h
+++ b/include/linux/irqchip/arm-gic-common.h
@@ -39,4 +39,9 @@ struct gic_kvm_info {
39 39
40const struct gic_kvm_info *gic_get_kvm_info(void); 40const struct gic_kvm_info *gic_get_kvm_info(void);
41 41
42struct irq_domain;
43struct fwnode_handle;
44int gicv2m_init(struct fwnode_handle *parent_handle,
45 struct irq_domain *parent);
46
42#endif /* __LINUX_IRQCHIP_ARM_GIC_COMMON_H */ 47#endif /* __LINUX_IRQCHIP_ARM_GIC_COMMON_H */
diff --git a/include/linux/irqchip/arm-gic.h b/include/linux/irqchip/arm-gic.h
index 0f049b384ccd..7bd3bc6baa40 100644
--- a/include/linux/irqchip/arm-gic.h
+++ b/include/linux/irqchip/arm-gic.h
@@ -160,9 +160,6 @@ int gic_of_init_child(struct device *dev, struct gic_chip_data **gic, int irq);
160 */ 160 */
161void gic_init(void __iomem *dist , void __iomem *cpu); 161void gic_init(void __iomem *dist , void __iomem *cpu);
162 162
163int gicv2m_init(struct fwnode_handle *parent_handle,
164 struct irq_domain *parent);
165
166void gic_send_sgi(unsigned int cpu_id, unsigned int irq); 163void gic_send_sgi(unsigned int cpu_id, unsigned int irq);
167int gic_get_cpu_id(unsigned int cpu); 164int gic_get_cpu_id(unsigned int cpu);
168void gic_migrate_target(unsigned int new_cpu_id); 165void gic_migrate_target(unsigned int new_cpu_id);