Commit 90b4c555 authored by Zeev Zilberman's avatar Zeev Zilberman Committed by Marc Zyngier

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: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: default avatarZeev Zilberman <zeev@amazon.com>
Signed-off-by: default avatarMarc Zyngier <marc.zyngier@arm.com>
parent 4770533f
...@@ -56,6 +56,7 @@ ...@@ -56,6 +56,7 @@
/* List of flags for specific v2m implementation */ /* List of flags for specific v2m implementation */
#define GICV2M_NEEDS_SPI_OFFSET 0x00000001 #define GICV2M_NEEDS_SPI_OFFSET 0x00000001
#define GICV2M_GRAVITON_ADDRESS_ONLY 0x00000002
static LIST_HEAD(v2m_nodes); static LIST_HEAD(v2m_nodes);
static DEFINE_SPINLOCK(v2m_lock); static DEFINE_SPINLOCK(v2m_lock);
...@@ -98,15 +99,26 @@ static struct msi_domain_info gicv2m_msi_domain_info = { ...@@ -98,15 +99,26 @@ static struct msi_domain_info gicv2m_msi_domain_info = {
.chip = &gicv2m_msi_irq_chip, .chip = &gicv2m_msi_irq_chip,
}; };
static phys_addr_t gicv2m_get_msi_addr(struct v2m_data *v2m, int hwirq)
{
if (v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY)
return v2m->res.start | ((hwirq - 32) << 3);
else
return v2m->res.start + V2M_MSI_SETSPI_NS;
}
static void gicv2m_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) static void gicv2m_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
{ {
struct v2m_data *v2m = irq_data_get_irq_chip_data(data); struct v2m_data *v2m = irq_data_get_irq_chip_data(data);
phys_addr_t addr = v2m->res.start + V2M_MSI_SETSPI_NS; phys_addr_t addr = gicv2m_get_msi_addr(v2m, data->hwirq);
msg->address_hi = upper_32_bits(addr); msg->address_hi = upper_32_bits(addr);
msg->address_lo = lower_32_bits(addr); msg->address_lo = lower_32_bits(addr);
msg->data = data->hwirq;
if (v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY)
msg->data = 0;
else
msg->data = data->hwirq;
if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET) if (v2m->flags & GICV2M_NEEDS_SPI_OFFSET)
msg->data -= v2m->spi_offset; msg->data -= v2m->spi_offset;
...@@ -188,7 +200,7 @@ static int gicv2m_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, ...@@ -188,7 +200,7 @@ static int gicv2m_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
hwirq = v2m->spi_start + offset; hwirq = v2m->spi_start + offset;
err = iommu_dma_prepare_msi(info->desc, err = iommu_dma_prepare_msi(info->desc,
v2m->res.start + V2M_MSI_SETSPI_NS); gicv2m_get_msi_addr(v2m, hwirq));
if (err) if (err)
return err; return err;
...@@ -307,7 +319,7 @@ static int gicv2m_allocate_domains(struct irq_domain *parent) ...@@ -307,7 +319,7 @@ static int gicv2m_allocate_domains(struct irq_domain *parent)
static int __init gicv2m_init_one(struct fwnode_handle *fwnode, static int __init gicv2m_init_one(struct fwnode_handle *fwnode,
u32 spi_start, u32 nr_spis, u32 spi_start, u32 nr_spis,
struct resource *res) struct resource *res, u32 flags)
{ {
int ret; int ret;
struct v2m_data *v2m; struct v2m_data *v2m;
...@@ -320,6 +332,7 @@ static int __init gicv2m_init_one(struct fwnode_handle *fwnode, ...@@ -320,6 +332,7 @@ static int __init gicv2m_init_one(struct fwnode_handle *fwnode,
INIT_LIST_HEAD(&v2m->entry); INIT_LIST_HEAD(&v2m->entry);
v2m->fwnode = fwnode; v2m->fwnode = fwnode;
v2m->flags = flags;
memcpy(&v2m->res, res, sizeof(struct resource)); memcpy(&v2m->res, res, sizeof(struct resource));
...@@ -334,7 +347,14 @@ static int __init gicv2m_init_one(struct fwnode_handle *fwnode, ...@@ -334,7 +347,14 @@ static int __init gicv2m_init_one(struct fwnode_handle *fwnode,
v2m->spi_start = spi_start; v2m->spi_start = spi_start;
v2m->nr_spis = nr_spis; v2m->nr_spis = nr_spis;
} else { } else {
u32 typer = readl_relaxed(v2m->base + V2M_MSI_TYPER); u32 typer;
/* Graviton should always have explicit spi_start/nr_spis */
if (v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY) {
ret = -EINVAL;
goto err_iounmap;
}
typer = readl_relaxed(v2m->base + V2M_MSI_TYPER);
v2m->spi_start = V2M_MSI_TYPER_BASE_SPI(typer); v2m->spi_start = V2M_MSI_TYPER_BASE_SPI(typer);
v2m->nr_spis = V2M_MSI_TYPER_NUM_SPI(typer); v2m->nr_spis = V2M_MSI_TYPER_NUM_SPI(typer);
...@@ -355,18 +375,21 @@ static int __init gicv2m_init_one(struct fwnode_handle *fwnode, ...@@ -355,18 +375,21 @@ static int __init gicv2m_init_one(struct fwnode_handle *fwnode,
* *
* Broadom NS2 GICv2m implementation has an erratum where the MSI data * Broadom NS2 GICv2m implementation has an erratum where the MSI data
* is 'spi_number - 32' * is 'spi_number - 32'
*
* Reading that register fails on the Graviton implementation
*/ */
switch (readl_relaxed(v2m->base + V2M_MSI_IIDR)) { if (!(v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY)) {
case XGENE_GICV2M_MSI_IIDR: switch (readl_relaxed(v2m->base + V2M_MSI_IIDR)) {
v2m->flags |= GICV2M_NEEDS_SPI_OFFSET; case XGENE_GICV2M_MSI_IIDR:
v2m->spi_offset = v2m->spi_start; v2m->flags |= GICV2M_NEEDS_SPI_OFFSET;
break; v2m->spi_offset = v2m->spi_start;
case BCM_NS2_GICV2M_MSI_IIDR: break;
v2m->flags |= GICV2M_NEEDS_SPI_OFFSET; case BCM_NS2_GICV2M_MSI_IIDR:
v2m->spi_offset = 32; v2m->flags |= GICV2M_NEEDS_SPI_OFFSET;
break; v2m->spi_offset = 32;
break;
}
} }
v2m->bm = kcalloc(BITS_TO_LONGS(v2m->nr_spis), sizeof(long), v2m->bm = kcalloc(BITS_TO_LONGS(v2m->nr_spis), sizeof(long),
GFP_KERNEL); GFP_KERNEL);
if (!v2m->bm) { if (!v2m->bm) {
...@@ -419,7 +442,8 @@ static int __init gicv2m_of_init(struct fwnode_handle *parent_handle, ...@@ -419,7 +442,8 @@ static int __init gicv2m_of_init(struct fwnode_handle *parent_handle,
pr_info("DT overriding V2M MSI_TYPER (base:%u, num:%u)\n", pr_info("DT overriding V2M MSI_TYPER (base:%u, num:%u)\n",
spi_start, nr_spis); spi_start, nr_spis);
ret = gicv2m_init_one(&child->fwnode, spi_start, nr_spis, &res); ret = gicv2m_init_one(&child->fwnode, spi_start, nr_spis,
&res, 0);
if (ret) { if (ret) {
of_node_put(child); of_node_put(child);
break; break;
...@@ -451,6 +475,25 @@ static struct fwnode_handle *gicv2m_get_fwnode(struct device *dev) ...@@ -451,6 +475,25 @@ static struct fwnode_handle *gicv2m_get_fwnode(struct device *dev)
return data->fwnode; return data->fwnode;
} }
static bool acpi_check_amazon_graviton_quirks(void)
{
static struct acpi_table_madt *madt;
acpi_status status;
bool rc = false;
#define ACPI_AMZN_OEM_ID "AMAZON"
status = acpi_get_table(ACPI_SIG_MADT, 0,
(struct acpi_table_header **)&madt);
if (ACPI_FAILURE(status) || !madt)
return rc;
rc = !memcmp(madt->header.oem_id, ACPI_AMZN_OEM_ID, ACPI_OEM_ID_SIZE);
acpi_put_table((struct acpi_table_header *)madt);
return rc;
}
static int __init static int __init
acpi_parse_madt_msi(union acpi_subtable_headers *header, acpi_parse_madt_msi(union acpi_subtable_headers *header,
const unsigned long end) const unsigned long end)
...@@ -460,6 +503,7 @@ acpi_parse_madt_msi(union acpi_subtable_headers *header, ...@@ -460,6 +503,7 @@ acpi_parse_madt_msi(union acpi_subtable_headers *header,
u32 spi_start = 0, nr_spis = 0; u32 spi_start = 0, nr_spis = 0;
struct acpi_madt_generic_msi_frame *m; struct acpi_madt_generic_msi_frame *m;
struct fwnode_handle *fwnode; struct fwnode_handle *fwnode;
u32 flags = 0;
m = (struct acpi_madt_generic_msi_frame *)header; m = (struct acpi_madt_generic_msi_frame *)header;
if (BAD_MADT_ENTRY(m, end)) if (BAD_MADT_ENTRY(m, end))
...@@ -469,6 +513,13 @@ acpi_parse_madt_msi(union acpi_subtable_headers *header, ...@@ -469,6 +513,13 @@ acpi_parse_madt_msi(union acpi_subtable_headers *header,
res.end = m->base_address + SZ_4K - 1; res.end = m->base_address + SZ_4K - 1;
res.flags = IORESOURCE_MEM; res.flags = IORESOURCE_MEM;
if (acpi_check_amazon_graviton_quirks()) {
pr_info("applying Amazon Graviton quirk\n");
res.end = res.start + SZ_8K - 1;
flags |= GICV2M_GRAVITON_ADDRESS_ONLY;
gicv2m_msi_domain_info.flags &= ~MSI_FLAG_MULTI_PCI_MSI;
}
if (m->flags & ACPI_MADT_OVERRIDE_SPI_VALUES) { if (m->flags & ACPI_MADT_OVERRIDE_SPI_VALUES) {
spi_start = m->spi_base; spi_start = m->spi_base;
nr_spis = m->spi_count; nr_spis = m->spi_count;
...@@ -483,7 +534,7 @@ acpi_parse_madt_msi(union acpi_subtable_headers *header, ...@@ -483,7 +534,7 @@ acpi_parse_madt_msi(union acpi_subtable_headers *header,
return -EINVAL; return -EINVAL;
} }
ret = gicv2m_init_one(fwnode, spi_start, nr_spis, &res); ret = gicv2m_init_one(fwnode, spi_start, nr_spis, &res, flags);
if (ret) if (ret)
irq_domain_free_fwnode(fwnode); irq_domain_free_fwnode(fwnode);
......
...@@ -1343,6 +1343,9 @@ static int __init gic_init_bases(void __iomem *dist_base, ...@@ -1343,6 +1343,9 @@ static int __init gic_init_bases(void __iomem *dist_base,
if (gic_dist_supports_lpis()) { if (gic_dist_supports_lpis()) {
its_init(handle, &gic_data.rdists, gic_data.domain); its_init(handle, &gic_data.rdists, gic_data.domain);
its_cpu_init(); its_cpu_init();
} else {
if (IS_ENABLED(CONFIG_ARM_GIC_V2M))
gicv2m_init(handle, gic_data.domain);
} }
if (gic_prio_masking_enabled()) { if (gic_prio_masking_enabled()) {
......
...@@ -39,4 +39,9 @@ struct gic_kvm_info { ...@@ -39,4 +39,9 @@ struct gic_kvm_info {
const struct gic_kvm_info *gic_get_kvm_info(void); const struct gic_kvm_info *gic_get_kvm_info(void);
struct irq_domain;
struct fwnode_handle;
int gicv2m_init(struct fwnode_handle *parent_handle,
struct irq_domain *parent);
#endif /* __LINUX_IRQCHIP_ARM_GIC_COMMON_H */ #endif /* __LINUX_IRQCHIP_ARM_GIC_COMMON_H */
...@@ -160,9 +160,6 @@ int gic_of_init_child(struct device *dev, struct gic_chip_data **gic, int irq); ...@@ -160,9 +160,6 @@ int gic_of_init_child(struct device *dev, struct gic_chip_data **gic, int irq);
*/ */
void gic_init(void __iomem *dist , void __iomem *cpu); void gic_init(void __iomem *dist , void __iomem *cpu);
int gicv2m_init(struct fwnode_handle *parent_handle,
struct irq_domain *parent);
void gic_send_sgi(unsigned int cpu_id, unsigned int irq); void gic_send_sgi(unsigned int cpu_id, unsigned int irq);
int gic_get_cpu_id(unsigned int cpu); int gic_get_cpu_id(unsigned int cpu);
void gic_migrate_target(unsigned int new_cpu_id); void gic_migrate_target(unsigned int new_cpu_id);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment