Commit f935448a authored by Geetha Sowjanya's avatar Geetha Sowjanya Committed by Will Deacon

iommu/arm-smmu-v3: Add workaround for Cavium ThunderX2 erratum #126

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: default avatarLorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Signed-off-by: default avatarGeetha sowjanya <gakula@caviumnetworks.com>
[will: reworked irq equality checking and added SPI check]
Signed-off-by: default avatarWill Deacon <will.deacon@arm.com>
parent 99caf177
...@@ -63,6 +63,7 @@ stable kernels. ...@@ -63,6 +63,7 @@ stable kernels.
| Cavium | ThunderX Core | #27456 | CAVIUM_ERRATUM_27456 | | Cavium | ThunderX Core | #27456 | CAVIUM_ERRATUM_27456 |
| Cavium | ThunderX SMMUv2 | #27704 | N/A | | Cavium | ThunderX SMMUv2 | #27704 | N/A |
| Cavium | ThunderX2 SMMUv3| #74 | N/A | | Cavium | ThunderX2 SMMUv3| #74 | N/A |
| Cavium | ThunderX2 SMMUv3| #126 | N/A |
| | | | | | | | | |
| Freescale/NXP | LS2080A/LS1043A | A-008585 | FSL_ERRATUM_A008585 | | Freescale/NXP | LS2080A/LS1043A | A-008585 | FSL_ERRATUM_A008585 |
| | | | | | | | | |
......
...@@ -26,6 +26,12 @@ the PCIe specification. ...@@ -26,6 +26,12 @@ the PCIe specification.
* "priq" - PRI Queue not empty * "priq" - PRI Queue not empty
* "cmdq-sync" - CMD_SYNC complete * "cmdq-sync" - CMD_SYNC complete
* "gerror" - Global Error activated * "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 - #iommu-cells : See the generic IOMMU binding described in
devicetree/bindings/pci/pci-iommu.txt devicetree/bindings/pci/pci-iommu.txt
......
...@@ -833,6 +833,24 @@ static int __init arm_smmu_v3_count_resources(struct acpi_iort_node *node) ...@@ -833,6 +833,24 @@ static int __init arm_smmu_v3_count_resources(struct acpi_iort_node *node)
return num_res; 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) static unsigned long arm_smmu_v3_resource_size(struct acpi_iort_smmu_v3 *smmu)
{ {
/* /*
...@@ -860,6 +878,12 @@ static void __init arm_smmu_v3_init_resources(struct resource *res, ...@@ -860,6 +878,12 @@ static void __init arm_smmu_v3_init_resources(struct resource *res,
res[num_res].flags = IORESOURCE_MEM; res[num_res].flags = IORESOURCE_MEM;
num_res++; 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) if (smmu->event_gsiv)
acpi_iort_register_irq(smmu->event_gsiv, "eventq", acpi_iort_register_irq(smmu->event_gsiv, "eventq",
...@@ -880,6 +904,7 @@ static void __init arm_smmu_v3_init_resources(struct resource *res, ...@@ -880,6 +904,7 @@ static void __init arm_smmu_v3_init_resources(struct resource *res,
acpi_iort_register_irq(smmu->sync_gsiv, "cmdq-sync", acpi_iort_register_irq(smmu->sync_gsiv, "cmdq-sync",
ACPI_EDGE_SENSITIVE, ACPI_EDGE_SENSITIVE,
&res[num_res++]); &res[num_res++]);
}
} }
static bool __init arm_smmu_v3_is_coherent(struct acpi_iort_node *node) static bool __init arm_smmu_v3_is_coherent(struct acpi_iort_node *node)
......
...@@ -615,6 +615,7 @@ struct arm_smmu_device { ...@@ -615,6 +615,7 @@ struct arm_smmu_device {
struct arm_smmu_priq priq; struct arm_smmu_priq priq;
int gerr_irq; int gerr_irq;
int combined_irq;
unsigned long ias; /* IPA */ unsigned long ias; /* IPA */
unsigned long oas; /* PA */ unsigned long oas; /* PA */
...@@ -1330,6 +1331,24 @@ static irqreturn_t arm_smmu_gerror_handler(int irq, void *dev) ...@@ -1330,6 +1331,24 @@ static irqreturn_t arm_smmu_gerror_handler(int irq, void *dev)
return IRQ_HANDLED; 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 */ /* IO_PGTABLE API */
static void __arm_smmu_tlb_sync(struct arm_smmu_device *smmu) 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) ...@@ -2229,18 +2248,9 @@ static void arm_smmu_setup_msis(struct arm_smmu_device *smmu)
devm_add_action(dev, arm_smmu_free_msis, dev); 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; int irq, ret;
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;
}
arm_smmu_setup_msis(smmu); arm_smmu_setup_msis(smmu);
...@@ -2283,10 +2293,41 @@ static int arm_smmu_setup_irqs(struct arm_smmu_device *smmu) ...@@ -2283,10 +2293,41 @@ static int arm_smmu_setup_irqs(struct arm_smmu_device *smmu)
if (ret < 0) if (ret < 0)
dev_warn(smmu->dev, dev_warn(smmu->dev,
"failed to enable priq irq\n"); "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 */ /* Enable interrupt generation on the SMMU */
ret = arm_smmu_write_reg_sync(smmu, irqen_flags, ret = arm_smmu_write_reg_sync(smmu, irqen_flags,
...@@ -2729,6 +2770,11 @@ static int arm_smmu_device_probe(struct platform_device *pdev) ...@@ -2729,6 +2770,11 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
return PTR_ERR(smmu->base); return PTR_ERR(smmu->base);
/* Interrupt lines */ /* Interrupt lines */
irq = platform_get_irq_byname(pdev, "combined");
if (irq > 0)
smmu->combined_irq = irq;
else {
irq = platform_get_irq_byname(pdev, "eventq"); irq = platform_get_irq_byname(pdev, "eventq");
if (irq > 0) if (irq > 0)
smmu->evtq.q.irq = irq; smmu->evtq.q.irq = irq;
...@@ -2744,7 +2790,7 @@ static int arm_smmu_device_probe(struct platform_device *pdev) ...@@ -2744,7 +2790,7 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
irq = platform_get_irq_byname(pdev, "gerror"); irq = platform_get_irq_byname(pdev, "gerror");
if (irq > 0) if (irq > 0)
smmu->gerr_irq = irq; smmu->gerr_irq = irq;
}
/* Probe the h/w */ /* Probe the h/w */
ret = arm_smmu_device_hw_probe(smmu); ret = arm_smmu_device_hw_probe(smmu);
if (ret) if (ret)
......
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