iommu/arm-smmu-v3: Add workaround for Cavium ThunderX2 erratum #126
authorGeetha Sowjanya <geethasowjanya.akula@cavium.com>
Fri, 23 Jun 2017 13:34:36 +0000 (19:04 +0530)
committerWill Deacon <will.deacon@arm.com>
Fri, 23 Jun 2017 16:58:04 +0000 (17:58 +0100)
Cavium ThunderX2 SMMU doesn't support MSI and also doesn't have unique irq
lines for gerror, eventq and cmdq-sync.

New named irq "combined" is set as a errata workaround, which allows to
share the irq line by register single irq handler for all the interrupts.

Acked-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Signed-off-by: Geetha sowjanya <gakula@caviumnetworks.com>
[will: reworked irq equality checking and added SPI check]
Signed-off-by: Will Deacon <will.deacon@arm.com>
Documentation/arm64/silicon-errata.txt
Documentation/devicetree/bindings/iommu/arm,smmu-v3.txt
drivers/acpi/arm64/iort.c
drivers/iommu/arm-smmu-v3.c

index ef4e43590685d95371e6c0c45568ffac0246d9f2..8564795257760c3f3e9881058d71f85ad990942c 100644 (file)
@@ -63,6 +63,7 @@ stable kernels.
 | Cavium         | ThunderX Core   | #27456          | CAVIUM_ERRATUM_27456        |
 | Cavium         | ThunderX SMMUv2 | #27704          | N/A                         |
 | Cavium         | ThunderX2 SMMUv3| #74             | N/A                         |
+| Cavium         | ThunderX2 SMMUv3| #126            | N/A                         |
 |                |                 |                 |                             |
 | Freescale/NXP  | LS2080A/LS1043A | A-008585        | FSL_ERRATUM_A008585         |
 |                |                 |                 |                             |
index e7855cf6038eb1531a8486c942cc8e195c154b48..c9abbf3e4f68238faaa287dc9fb2222af0e4c236 100644 (file)
@@ -26,6 +26,12 @@ the PCIe specification.
                       * "priq"      - PRI Queue not empty
                       * "cmdq-sync" - CMD_SYNC complete
                       * "gerror"    - Global Error activated
+                      * "combined"  - The combined interrupt is optional,
+                                     and should only be provided if the
+                                     hardware supports just a single,
+                                     combined interrupt line.
+                                     If provided, then the combined interrupt
+                                     will be used in preference to any others.
 
 - #iommu-cells      : See the generic IOMMU binding described in
                         devicetree/bindings/pci/pci-iommu.txt
index a8ebda9f7e97474873459398315bfe847de262ec..83d65d96676f1ac9c6f30b1c02556dc21dabfca2 100644 (file)
@@ -833,6 +833,24 @@ static int __init arm_smmu_v3_count_resources(struct acpi_iort_node *node)
        return num_res;
 }
 
+static bool arm_smmu_v3_is_combined_irq(struct acpi_iort_smmu_v3 *smmu)
+{
+       /*
+        * Cavium ThunderX2 implementation doesn't not support unique
+        * irq line. Use single irq line for all the SMMUv3 interrupts.
+        */
+       if (smmu->model != ACPI_IORT_SMMU_V3_CAVIUM_CN99XX)
+               return false;
+
+       /*
+        * ThunderX2 doesn't support MSIs from the SMMU, so we're checking
+        * SPI numbers here.
+        */
+       return smmu->event_gsiv == smmu->pri_gsiv &&
+              smmu->event_gsiv == smmu->gerr_gsiv &&
+              smmu->event_gsiv == smmu->sync_gsiv;
+}
+
 static unsigned long arm_smmu_v3_resource_size(struct acpi_iort_smmu_v3 *smmu)
 {
        /*
@@ -860,26 +878,33 @@ static void __init arm_smmu_v3_init_resources(struct resource *res,
        res[num_res].flags = IORESOURCE_MEM;
 
        num_res++;
+       if (arm_smmu_v3_is_combined_irq(smmu)) {
+               if (smmu->event_gsiv)
+                       acpi_iort_register_irq(smmu->event_gsiv, "combined",
+                                              ACPI_EDGE_SENSITIVE,
+                                              &res[num_res++]);
+       } else {
 
-       if (smmu->event_gsiv)
-               acpi_iort_register_irq(smmu->event_gsiv, "eventq",
-                                      ACPI_EDGE_SENSITIVE,
-                                      &res[num_res++]);
-
-       if (smmu->pri_gsiv)
-               acpi_iort_register_irq(smmu->pri_gsiv, "priq",
-                                      ACPI_EDGE_SENSITIVE,
-                                      &res[num_res++]);
-
-       if (smmu->gerr_gsiv)
-               acpi_iort_register_irq(smmu->gerr_gsiv, "gerror",
-                                      ACPI_EDGE_SENSITIVE,
-                                      &res[num_res++]);
-
-       if (smmu->sync_gsiv)
-               acpi_iort_register_irq(smmu->sync_gsiv, "cmdq-sync",
-                                      ACPI_EDGE_SENSITIVE,
-                                      &res[num_res++]);
+               if (smmu->event_gsiv)
+                       acpi_iort_register_irq(smmu->event_gsiv, "eventq",
+                                              ACPI_EDGE_SENSITIVE,
+                                              &res[num_res++]);
+
+               if (smmu->pri_gsiv)
+                       acpi_iort_register_irq(smmu->pri_gsiv, "priq",
+                                              ACPI_EDGE_SENSITIVE,
+                                              &res[num_res++]);
+
+               if (smmu->gerr_gsiv)
+                       acpi_iort_register_irq(smmu->gerr_gsiv, "gerror",
+                                              ACPI_EDGE_SENSITIVE,
+                                              &res[num_res++]);
+
+               if (smmu->sync_gsiv)
+                       acpi_iort_register_irq(smmu->sync_gsiv, "cmdq-sync",
+                                              ACPI_EDGE_SENSITIVE,
+                                              &res[num_res++]);
+       }
 }
 
 static bool __init arm_smmu_v3_is_coherent(struct acpi_iort_node *node)
index 81fc1b5c91ee1bd7035159ecdb77bd71696d5352..568c400eeaed8d3528cc80b7dd597420e842024b 100644 (file)
@@ -615,6 +615,7 @@ struct arm_smmu_device {
        struct arm_smmu_priq            priq;
 
        int                             gerr_irq;
+       int                             combined_irq;
 
        unsigned long                   ias; /* IPA */
        unsigned long                   oas; /* PA */
@@ -1330,6 +1331,24 @@ static irqreturn_t arm_smmu_gerror_handler(int irq, void *dev)
        return IRQ_HANDLED;
 }
 
+static irqreturn_t arm_smmu_combined_irq_thread(int irq, void *dev)
+{
+       struct arm_smmu_device *smmu = dev;
+
+       arm_smmu_evtq_thread(irq, dev);
+       if (smmu->features & ARM_SMMU_FEAT_PRI)
+               arm_smmu_priq_thread(irq, dev);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t arm_smmu_combined_irq_handler(int irq, void *dev)
+{
+       arm_smmu_gerror_handler(irq, dev);
+       arm_smmu_cmdq_sync_handler(irq, dev);
+       return IRQ_WAKE_THREAD;
+}
+
 /* IO_PGTABLE API */
 static void __arm_smmu_tlb_sync(struct arm_smmu_device *smmu)
 {
@@ -2229,18 +2248,9 @@ static void arm_smmu_setup_msis(struct arm_smmu_device *smmu)
        devm_add_action(dev, arm_smmu_free_msis, dev);
 }
 
-static int arm_smmu_setup_irqs(struct arm_smmu_device *smmu)
+static void arm_smmu_setup_unique_irqs(struct arm_smmu_device *smmu)
 {
-       int ret, irq;
-       u32 irqen_flags = IRQ_CTRL_EVTQ_IRQEN | IRQ_CTRL_GERROR_IRQEN;
-
-       /* Disable IRQs first */
-       ret = arm_smmu_write_reg_sync(smmu, 0, ARM_SMMU_IRQ_CTRL,
-                                     ARM_SMMU_IRQ_CTRLACK);
-       if (ret) {
-               dev_err(smmu->dev, "failed to disable irqs\n");
-               return ret;
-       }
+       int irq, ret;
 
        arm_smmu_setup_msis(smmu);
 
@@ -2283,10 +2293,41 @@ static int arm_smmu_setup_irqs(struct arm_smmu_device *smmu)
                        if (ret < 0)
                                dev_warn(smmu->dev,
                                         "failed to enable priq irq\n");
-                       else
-                               irqen_flags |= IRQ_CTRL_PRIQ_IRQEN;
                }
        }
+}
+
+static int arm_smmu_setup_irqs(struct arm_smmu_device *smmu)
+{
+       int ret, irq;
+       u32 irqen_flags = IRQ_CTRL_EVTQ_IRQEN | IRQ_CTRL_GERROR_IRQEN;
+
+       /* Disable IRQs first */
+       ret = arm_smmu_write_reg_sync(smmu, 0, ARM_SMMU_IRQ_CTRL,
+                                     ARM_SMMU_IRQ_CTRLACK);
+       if (ret) {
+               dev_err(smmu->dev, "failed to disable irqs\n");
+               return ret;
+       }
+
+       irq = smmu->combined_irq;
+       if (irq) {
+               /*
+                * Cavium ThunderX2 implementation doesn't not support unique
+                * irq lines. Use single irq line for all the SMMUv3 interrupts.
+                */
+               ret = devm_request_threaded_irq(smmu->dev, irq,
+                                       arm_smmu_combined_irq_handler,
+                                       arm_smmu_combined_irq_thread,
+                                       IRQF_ONESHOT,
+                                       "arm-smmu-v3-combined-irq", smmu);
+               if (ret < 0)
+                       dev_warn(smmu->dev, "failed to enable combined irq\n");
+       } else
+               arm_smmu_setup_unique_irqs(smmu);
+
+       if (smmu->features & ARM_SMMU_FEAT_PRI)
+               irqen_flags |= IRQ_CTRL_PRIQ_IRQEN;
 
        /* Enable interrupt generation on the SMMU */
        ret = arm_smmu_write_reg_sync(smmu, irqen_flags,
@@ -2729,22 +2770,27 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
                return PTR_ERR(smmu->base);
 
        /* Interrupt lines */
-       irq = platform_get_irq_byname(pdev, "eventq");
-       if (irq > 0)
-               smmu->evtq.q.irq = irq;
 
-       irq = platform_get_irq_byname(pdev, "priq");
+       irq = platform_get_irq_byname(pdev, "combined");
        if (irq > 0)
-               smmu->priq.q.irq = irq;
+               smmu->combined_irq = irq;
+       else {
+               irq = platform_get_irq_byname(pdev, "eventq");
+               if (irq > 0)
+                       smmu->evtq.q.irq = irq;
 
-       irq = platform_get_irq_byname(pdev, "cmdq-sync");
-       if (irq > 0)
-               smmu->cmdq.q.irq = irq;
+               irq = platform_get_irq_byname(pdev, "priq");
+               if (irq > 0)
+                       smmu->priq.q.irq = irq;
 
-       irq = platform_get_irq_byname(pdev, "gerror");
-       if (irq > 0)
-               smmu->gerr_irq = irq;
+               irq = platform_get_irq_byname(pdev, "cmdq-sync");
+               if (irq > 0)
+                       smmu->cmdq.q.irq = irq;
 
+               irq = platform_get_irq_byname(pdev, "gerror");
+               if (irq > 0)
+                       smmu->gerr_irq = irq;
+       }
        /* Probe the h/w */
        ret = arm_smmu_device_hw_probe(smmu);
        if (ret)