[PATCH 2/3] iommu/arm-smmu-v3: Add workaround for Cavium ThunderX2 erratum #74

From: Geetha sowjanya
Date: Thu Apr 27 2017 - 08:06:12 EST


From: Linu Cherian <linu.cherian@xxxxxxxxxx>

Cavium 99xx SMMU implementation doesn't support page 1 register space.
Based on silicon id, ARM_SMMU_PAGE0_REGS_ONLY macro is set as an errata
workaround.

This macro when set, replaces all page 1 offsets used for
EVTQ_PROD/CONS, PRIQ_PROD/CONS register access with page 0 offsets.

Signed-off-by: Linu Cherian <linu.cherian@xxxxxxxxxx>
Signed-off-by: Geetha <gakula@xxxxxxxxxx>
---
Documentation/arm64/silicon-errata.txt | 1 +
drivers/acpi/arm64/iort.c | 14 +++++++++++++-
drivers/iommu/arm-smmu-v3.c | 32 +++++++++++++++++++++++++++-----
3 files changed, 41 insertions(+), 6 deletions(-)

diff --git a/Documentation/arm64/silicon-errata.txt b/Documentation/arm64/silicon-errata.txt
index 2f66683..629e2ce 100644
--- a/Documentation/arm64/silicon-errata.txt
+++ b/Documentation/arm64/silicon-errata.txt
@@ -61,6 +61,7 @@ stable kernels.
| Cavium | ThunderX GICv3 | #23154 | CAVIUM_ERRATUM_23154 |
| Cavium | ThunderX Core | #27456 | CAVIUM_ERRATUM_27456 |
| Cavium | ThunderX SMMUv2 | #27704 | N/A |
+| Cavium | ThunderX2 SMMUv3| #74 | N/A |
| | | | |
| Freescale/NXP | LS2080A/LS1043A | A-008585 | FSL_ERRATUM_A008585 |
| | | | |
diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
index 4a5bb96..a074ce9 100644
--- a/drivers/acpi/arm64/iort.c
+++ b/drivers/acpi/arm64/iort.c
@@ -25,6 +25,7 @@
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
+#include <asm/cputype.h>

#define IORT_TYPE_MASK(type) (1 << (type))
#define IORT_MSI_TYPE (1 << ACPI_IORT_NODE_ITS_GROUP)
@@ -669,12 +670,23 @@ static void __init arm_smmu_v3_init_resources(struct resource *res,
{
struct acpi_iort_smmu_v3 *smmu;
int num_res = 0;
+ u32 cpu_model;
+ unsigned long size = SZ_128K;

/* Retrieve SMMUv3 specific data */
smmu = (struct acpi_iort_smmu_v3 *)node->node_data;

+ /*
+ * Override the size, for Cavium CN99xx implementations
+ * which doesn't support the page 1 SMMU register space.
+ */
+ cpu_model = read_cpuid_id() & MIDR_CPU_MODEL_MASK;
+ if (cpu_model == MIDR_THUNDERX_99XX ||
+ cpu_model == MIDR_BRCM_VULCAN)
+ size = SZ_64K;
+
res[num_res].start = smmu->base_address;
- res[num_res].end = smmu->base_address + SZ_128K - 1;
+ res[num_res].end = smmu->base_address + size - 1;
res[num_res].flags = IORESOURCE_MEM;

num_res++;
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index 1dcd154..ee23ccd 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -38,6 +38,7 @@
#include <linux/platform_device.h>

#include <linux/amba/bus.h>
+#include <asm/cputype.h>

#include "io-pgtable.h"

@@ -176,15 +177,15 @@
#define ARM_SMMU_CMDQ_CONS 0x9c

#define ARM_SMMU_EVTQ_BASE 0xa0
-#define ARM_SMMU_EVTQ_PROD 0x100a8
-#define ARM_SMMU_EVTQ_CONS 0x100ac
+#define ARM_SMMU_EVTQ_PROD (page1_offset_adjust(0x100a8))
+#define ARM_SMMU_EVTQ_CONS (page1_offset_adjust(0x100ac))
#define ARM_SMMU_EVTQ_IRQ_CFG0 0xb0
#define ARM_SMMU_EVTQ_IRQ_CFG1 0xb8
#define ARM_SMMU_EVTQ_IRQ_CFG2 0xbc

#define ARM_SMMU_PRIQ_BASE 0xc0
-#define ARM_SMMU_PRIQ_PROD 0x100c8
-#define ARM_SMMU_PRIQ_CONS 0x100cc
+#define ARM_SMMU_PRIQ_PROD (page1_offset_adjust(0x100c8))
+#define ARM_SMMU_PRIQ_CONS (page1_offset_adjust(0x100cc))
#define ARM_SMMU_PRIQ_IRQ_CFG0 0xd0
#define ARM_SMMU_PRIQ_IRQ_CFG1 0xd8
#define ARM_SMMU_PRIQ_IRQ_CFG2 0xdc
@@ -412,6 +413,10 @@
#define MSI_IOVA_BASE 0x8000000
#define MSI_IOVA_LENGTH 0x100000

+#define ARM_SMMU_PAGE0_REGS_ONLY \
+ (((read_cpuid_id() & MIDR_CPU_MODEL_MASK) == MIDR_THUNDERX_99XX) \
+ || ((read_cpuid_id() & MIDR_CPU_MODEL_MASK) == MIDR_BRCM_VULCAN))
+
static bool disable_bypass;
module_param_named(disable_bypass, disable_bypass, bool, S_IRUGO);
MODULE_PARM_DESC(disable_bypass,
@@ -660,6 +665,15 @@ struct arm_smmu_option_prop {
{ 0, NULL},
};

+static inline unsigned long page1_offset_adjust(
+ unsigned long off)
+{
+ if (!ARM_SMMU_PAGE0_REGS_ONLY)
+ return off;
+ else
+ return (off - SZ_64K);
+}
+
static struct arm_smmu_domain *to_smmu_domain(struct iommu_domain *dom)
{
return container_of(dom, struct arm_smmu_domain, domain);
@@ -2631,6 +2645,14 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev,
return ret;
}

+static unsigned long arm_smmu_resource_size(void)
+{
+ if (ARM_SMMU_PAGE0_REGS_ONLY)
+ return SZ_64K;
+ else
+ return SZ_128K;
+}
+
static int arm_smmu_device_probe(struct platform_device *pdev)
{
int irq, ret;
@@ -2649,7 +2671,7 @@ static int arm_smmu_device_probe(struct platform_device *pdev)

/* Base address */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (resource_size(res) + 1 < SZ_128K) {
+ if (resource_size(res) + 1 < arm_smmu_resource_size()) {
dev_err(dev, "MMIO region too small (%pr)\n", res);
return -EINVAL;
}
--
1.9.1