Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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         |
|                |                 |                 |                             |
+6 −0
Original line number Diff line number Diff line
@@ -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
+44 −19
Original line number Diff line number Diff line
@@ -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,6 +878,12 @@ 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",
@@ -881,6 +905,7 @@ static void __init arm_smmu_v3_init_resources(struct resource *res,
					       ACPI_EDGE_SENSITIVE,
					       &res[num_res++]);
	}
}

static bool __init arm_smmu_v3_is_coherent(struct acpi_iort_node *node)
{
+70 −24
Original line number Diff line number Diff line
@@ -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,11 +2293,42 @@ 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,
				      ARM_SMMU_IRQ_CTRL, ARM_SMMU_IRQ_CTRLACK);
@@ -2729,6 +2770,11 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
		return PTR_ERR(smmu->base);

	/* Interrupt lines */

	irq = platform_get_irq_byname(pdev, "combined");
	if (irq > 0)
		smmu->combined_irq = irq;
	else {
		irq = platform_get_irq_byname(pdev, "eventq");
		if (irq > 0)
			smmu->evtq.q.irq = irq;
@@ -2744,7 +2790,7 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
		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)