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: 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>
This commit is contained in:
Родитель
99caf177f6
Коммит
f935448acf
|
@ -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 |
|
||||
| | | | |
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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->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->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->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->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)
|
||||
|
|
|
@ -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)
|
||||
|
|
Загрузка…
Ссылка в новой задаче