Merge branch 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[platform/kernel/linux-rpi.git] / drivers / irqchip / irq-gic-v2m.c
index 875ac80..7338f90 100644 (file)
@@ -53,6 +53,7 @@
 
 /* List of flags for specific v2m implementation */
 #define GICV2M_NEEDS_SPI_OFFSET                0x00000001
+#define GICV2M_GRAVITON_ADDRESS_ONLY   0x00000002
 
 static LIST_HEAD(v2m_nodes);
 static DEFINE_SPINLOCK(v2m_lock);
@@ -95,15 +96,26 @@ static struct msi_domain_info gicv2m_msi_domain_info = {
        .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)
 {
        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_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)
                msg->data -= v2m->spi_offset;
 
@@ -185,7 +197,7 @@ static int gicv2m_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
        hwirq = v2m->spi_start + offset;
 
        err = iommu_dma_prepare_msi(info->desc,
-                                   v2m->res.start + V2M_MSI_SETSPI_NS);
+                                   gicv2m_get_msi_addr(v2m, hwirq));
        if (err)
                return err;
 
@@ -304,7 +316,7 @@ static int gicv2m_allocate_domains(struct irq_domain *parent)
 
 static int __init gicv2m_init_one(struct fwnode_handle *fwnode,
                                  u32 spi_start, u32 nr_spis,
-                                 struct resource *res)
+                                 struct resource *res, u32 flags)
 {
        int ret;
        struct v2m_data *v2m;
@@ -317,6 +329,7 @@ static int __init gicv2m_init_one(struct fwnode_handle *fwnode,
 
        INIT_LIST_HEAD(&v2m->entry);
        v2m->fwnode = fwnode;
+       v2m->flags = flags;
 
        memcpy(&v2m->res, res, sizeof(struct resource));
 
@@ -331,7 +344,14 @@ static int __init gicv2m_init_one(struct fwnode_handle *fwnode,
                v2m->spi_start = spi_start;
                v2m->nr_spis = nr_spis;
        } 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->nr_spis = V2M_MSI_TYPER_NUM_SPI(typer);
@@ -352,18 +372,21 @@ static int __init gicv2m_init_one(struct fwnode_handle *fwnode,
         *
         * Broadom NS2 GICv2m implementation has an erratum where the MSI data
         * is 'spi_number - 32'
+        *
+        * Reading that register fails on the Graviton implementation
         */
-       switch (readl_relaxed(v2m->base + V2M_MSI_IIDR)) {
-       case XGENE_GICV2M_MSI_IIDR:
-               v2m->flags |= GICV2M_NEEDS_SPI_OFFSET;
-               v2m->spi_offset = v2m->spi_start;
-               break;
-       case BCM_NS2_GICV2M_MSI_IIDR:
-               v2m->flags |= GICV2M_NEEDS_SPI_OFFSET;
-               v2m->spi_offset = 32;
-               break;
+       if (!(v2m->flags & GICV2M_GRAVITON_ADDRESS_ONLY)) {
+               switch (readl_relaxed(v2m->base + V2M_MSI_IIDR)) {
+               case XGENE_GICV2M_MSI_IIDR:
+                       v2m->flags |= GICV2M_NEEDS_SPI_OFFSET;
+                       v2m->spi_offset = v2m->spi_start;
+                       break;
+               case BCM_NS2_GICV2M_MSI_IIDR:
+                       v2m->flags |= GICV2M_NEEDS_SPI_OFFSET;
+                       v2m->spi_offset = 32;
+                       break;
+               }
        }
-
        v2m->bm = kcalloc(BITS_TO_LONGS(v2m->nr_spis), sizeof(long),
                          GFP_KERNEL);
        if (!v2m->bm) {
@@ -416,7 +439,8 @@ static int __init gicv2m_of_init(struct fwnode_handle *parent_handle,
                        pr_info("DT overriding V2M MSI_TYPER (base:%u, num:%u)\n",
                                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) {
                        of_node_put(child);
                        break;
@@ -448,6 +472,25 @@ static struct fwnode_handle *gicv2m_get_fwnode(struct device *dev)
        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
 acpi_parse_madt_msi(union acpi_subtable_headers *header,
                    const unsigned long end)
@@ -457,6 +500,7 @@ acpi_parse_madt_msi(union acpi_subtable_headers *header,
        u32 spi_start = 0, nr_spis = 0;
        struct acpi_madt_generic_msi_frame *m;
        struct fwnode_handle *fwnode;
+       u32 flags = 0;
 
        m = (struct acpi_madt_generic_msi_frame *)header;
        if (BAD_MADT_ENTRY(m, end))
@@ -466,6 +510,13 @@ acpi_parse_madt_msi(union acpi_subtable_headers *header,
        res.end = m->base_address + SZ_4K - 1;
        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) {
                spi_start = m->spi_base;
                nr_spis = m->spi_count;
@@ -480,7 +531,7 @@ acpi_parse_madt_msi(union acpi_subtable_headers *header,
                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)
                irq_domain_free_fwnode(fwnode);