From 9f896c0d4978238800a8f45cd2dc0b0d48d0d963 Mon Sep 17 00:00:00 2001 From: Robin Murphy Date: Thu, 15 Aug 2019 19:37:35 +0100 Subject: [PATCH 1/2] iommu/arm-smmu: Add configuration implementation hook Probing the ID registers and setting up the SMMU configuration is an area where overrides and workarounds may well be needed. Indeed, the Cavium workaround detection lives there at the moment, so let's break that out. Change-Id: I2b828a98981866a1a5adf21555055b8a4c9788d2 Signed-off-by: Robin Murphy Signed-off-by: Will Deacon Git-commit: 3995e18689fda2434863bdf2a8cd19acea4e61f0 Git-repo: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git [isaacm@codeauora.org: resolve trivial merge conflicts] Signed-off-by: Isaac J. Manjarres --- drivers/iommu/arm-smmu-impl.c | 34 ++++++++++++++++++++++++++++++++++ drivers/iommu/arm-smmu.c | 17 +++-------------- drivers/iommu/arm-smmu.h | 1 + 3 files changed, 38 insertions(+), 14 deletions(-) diff --git a/drivers/iommu/arm-smmu-impl.c b/drivers/iommu/arm-smmu-impl.c index 0657c85580cb..696417908793 100644 --- a/drivers/iommu/arm-smmu-impl.c +++ b/drivers/iommu/arm-smmu-impl.c @@ -47,8 +47,42 @@ const struct arm_smmu_impl calxeda_impl = { }; +static int cavium_cfg_probe(struct arm_smmu_device *smmu) +{ + static atomic_t context_count = ATOMIC_INIT(0); + /* + * Cavium CN88xx erratum #27704. + * Ensure ASID and VMID allocation is unique across all SMMUs in + * the system. + */ + smmu->cavium_id_base = atomic_fetch_add(smmu->num_context_banks, + &context_count); + dev_notice(smmu->dev, "\tenabling workaround for Cavium erratum 27704\n"); + + return 0; +} + +const struct arm_smmu_impl cavium_impl = { + .cfg_probe = cavium_cfg_probe, +}; + + struct arm_smmu_device *arm_smmu_impl_init(struct arm_smmu_device *smmu) { + /* + * We will inevitably have to combine model-specific implementation + * quirks with platform-specific integration quirks, but everything + * we currently support happens to work out as straightforward + * mutually-exclusive assignments. + */ + switch (smmu->model) { + case CAVIUM_SMMUV2: + smmu->impl = &cavium_impl; + break; + default: + break; + } + if (of_property_read_bool(smmu->dev->of_node, "calxeda,smmu-secure-config-access")) smmu->impl = &calxeda_impl; diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index cf4b4e393b39..047c486c3934 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -215,8 +215,6 @@ struct arm_smmu_domain { struct msm_iommu_domain domain; }; -static atomic_t cavium_smmu_context_count = ATOMIC_INIT(0); - static bool using_legacy_binding, using_generic_binding; struct arm_smmu_option_prop { @@ -4392,18 +4390,6 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu) } dev_dbg(smmu->dev, "\t%u context banks (%u stage-2 only)\n", smmu->num_context_banks, smmu->num_s2_context_banks); - /* - * Cavium CN88xx erratum #27704. - * Ensure ASID and VMID allocation is unique across all SMMUs in - * the system. - */ - if (smmu->model == CAVIUM_SMMUV2) { - smmu->cavium_id_base = - atomic_add_return(smmu->num_context_banks, - &cavium_smmu_context_count); - smmu->cavium_id_base -= smmu->num_context_banks; - dev_notice(smmu->dev, "\tenabling workaround for Cavium erratum 27704\n"); - } smmu->cbs = devm_kcalloc(smmu->dev, smmu->num_context_banks, sizeof(*smmu->cbs), GFP_KERNEL); if (!smmu->cbs) @@ -4472,6 +4458,9 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu) dev_dbg(smmu->dev, "\tStage-2: %lu-bit IPA -> %lu-bit PA\n", smmu->ipa_size, smmu->pa_size); + if (smmu->impl && smmu->impl->cfg_probe) + return smmu->impl->cfg_probe(smmu); + return 0; } diff --git a/drivers/iommu/arm-smmu.h b/drivers/iommu/arm-smmu.h index aa208a50c7dd..bfd54b7da264 100644 --- a/drivers/iommu/arm-smmu.h +++ b/drivers/iommu/arm-smmu.h @@ -373,6 +373,7 @@ struct arm_smmu_impl { u64 (*read_reg64)(struct arm_smmu_device *smmu, int page, int offset); void (*write_reg64)(struct arm_smmu_device *smmu, int page, int offset, u64 val); + int (*cfg_probe)(struct arm_smmu_device *smmu); }; static inline void __iomem *arm_smmu_page(struct arm_smmu_device *smmu, int n) From 45eebc8243bb3b7f70bbba0307c866baa19ab164 Mon Sep 17 00:00:00 2001 From: Robin Murphy Date: Thu, 15 Aug 2019 19:37:36 +0100 Subject: [PATCH 2/2] iommu/arm-smmu: Add reset implementation hook Reset is an activity rife with implementation-defined poking. Add a corresponding hook, and use it to encapsulate the existing MMU-500 details. Change-Id: I674738a3d93af8152282d645a2d1c0e4f1ff5dd9 Signed-off-by: Robin Murphy Signed-off-by: Will Deacon Git-commit: 62b993a36e4c2d60669beb3d1afe038ed44a41ec Git-repo: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git [isaacm@codeaurora.org: resolved merge conflicts] Signed-off-by: Isaac J. Manjarres --- drivers/iommu/arm-smmu-impl.c | 49 +++++++++++++++++++++++++++++++++++ drivers/iommu/arm-smmu.c | 36 +++---------------------- drivers/iommu/arm-smmu.h | 1 + 3 files changed, 53 insertions(+), 33 deletions(-) diff --git a/drivers/iommu/arm-smmu-impl.c b/drivers/iommu/arm-smmu-impl.c index 696417908793..4dc8b1c4befb 100644 --- a/drivers/iommu/arm-smmu-impl.c +++ b/drivers/iommu/arm-smmu-impl.c @@ -4,6 +4,7 @@ #define pr_fmt(fmt) "arm-smmu: " fmt +#include #include #include "arm-smmu.h" @@ -67,6 +68,51 @@ const struct arm_smmu_impl cavium_impl = { }; +#define ARM_MMU500_ACTLR_CPRE (1 << 1) + +#define ARM_MMU500_ACR_CACHE_LOCK (1 << 26) +#define ARM_MMU500_ACR_S2CRB_TLBEN (1 << 10) +#define ARM_MMU500_ACR_SMTNMB_TLBEN (1 << 8) + +static int arm_mmu500_reset(struct arm_smmu_device *smmu) +{ + u32 reg, major; + int i; + /* + * On MMU-500 r2p0 onwards we need to clear ACR.CACHE_LOCK before + * writes to the context bank ACTLRs will stick. And we just hope that + * Secure has also cleared SACR.CACHE_LOCK for this to take effect... + */ + reg = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_ID7); + major = FIELD_GET(ID7_MAJOR, reg); + reg = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_sACR); + if (major >= 2) + reg &= ~ARM_MMU500_ACR_CACHE_LOCK; + /* + * Allow unmatched Stream IDs to allocate bypass + * TLB entries for reduced latency. + */ + reg |= ARM_MMU500_ACR_SMTNMB_TLBEN | ARM_MMU500_ACR_S2CRB_TLBEN; + arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_sACR, reg); + + /* + * Disable MMU-500's not-particularly-beneficial next-page + * prefetcher for the sake of errata #841119 and #826419. + */ + for (i = 0; i < smmu->num_context_banks; ++i) { + reg = arm_smmu_cb_read(smmu, i, ARM_SMMU_CB_ACTLR); + reg &= ~ARM_MMU500_ACTLR_CPRE; + arm_smmu_cb_write(smmu, i, ARM_SMMU_CB_ACTLR, reg); + } + + return 0; +} + +const struct arm_smmu_impl arm_mmu500_impl = { + .reset = arm_mmu500_reset, +}; + + struct arm_smmu_device *arm_smmu_impl_init(struct arm_smmu_device *smmu) { /* @@ -76,6 +122,9 @@ struct arm_smmu_device *arm_smmu_impl_init(struct arm_smmu_device *smmu) * mutually-exclusive assignments. */ switch (smmu->model) { + case ARM_MMU500: + smmu->impl = &arm_mmu500_impl; + break; case CAVIUM_SMMUV2: smmu->impl = &cavium_impl; break; diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 047c486c3934..7332e983cb27 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -66,11 +66,7 @@ */ #define QCOM_DUMMY_VAL -1 -#define ARM_MMU500_ACTLR_CPRE (1 << 1) - #define ARM_MMU500_ACR_CACHE_LOCK (1 << 26) -#define ARM_MMU500_ACR_S2CRB_TLBEN (1 << 10) -#define ARM_MMU500_ACR_SMTNMB_TLBEN (1 << 8) #define TLB_LOOP_TIMEOUT 500000 /* 500ms */ #define TLB_SPIN_COUNT 10 @@ -3863,40 +3859,11 @@ static struct msm_iommu_ops arm_smmu_ops = { static void arm_smmu_context_bank_reset(struct arm_smmu_device *smmu) { int i; - u32 reg, major; - - if (smmu->model == ARM_MMU500) { - /* - * Before clearing ARM_MMU500_ACTLR_CPRE, need to - * clear CACHE_LOCK bit of ACR first. And, CACHE_LOCK - * bit is only present in MMU-500r2 onwards. - */ - reg = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_ID7); - major = FIELD_GET(ID7_MAJOR, reg); - reg = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_sACR); - if (major >= 2) - reg &= ~ARM_MMU500_ACR_CACHE_LOCK; - /* - * Allow unmatched Stream IDs to allocate bypass - * TLB entries for reduced latency. - */ - reg |= ARM_MMU500_ACR_SMTNMB_TLBEN; - arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_sACR, reg); - } /* Make sure all context banks are disabled and clear CB_FSR */ for (i = 0; i < smmu->num_context_banks; ++i) { arm_smmu_write_context_bank(smmu, i, 0); arm_smmu_cb_write(smmu, i, ARM_SMMU_CB_FSR, FSR_FAULT); - /* - * Disable MMU-500's not-particularly-beneficial next-page - * prefetcher for the sake of errata #841119 and #826419. - */ - if (smmu->model == ARM_MMU500) { - reg = arm_smmu_cb_read(smmu, i, ARM_SMMU_CB_ACTLR); - reg &= ~ARM_MMU500_ACTLR_CPRE; - arm_smmu_cb_write(smmu, i, ARM_SMMU_CB_ACTLR, reg); - } } } @@ -3955,6 +3922,9 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu) reg &= ~(sCR0_SHCFG_MASK << sCR0_SHCFG_SHIFT); reg |= sCR0_SHCFG_NSH << sCR0_SHCFG_SHIFT; + if (smmu->impl && smmu->impl->reset) + smmu->impl->reset(smmu); + /* Push the button */ arm_smmu_tlb_sync_global(smmu); wmb(); diff --git a/drivers/iommu/arm-smmu.h b/drivers/iommu/arm-smmu.h index bfd54b7da264..0c6d6e13e891 100644 --- a/drivers/iommu/arm-smmu.h +++ b/drivers/iommu/arm-smmu.h @@ -374,6 +374,7 @@ struct arm_smmu_impl { void (*write_reg64)(struct arm_smmu_device *smmu, int page, int offset, u64 val); int (*cfg_probe)(struct arm_smmu_device *smmu); + int (*reset)(struct arm_smmu_device *smmu); }; static inline void __iomem *arm_smmu_page(struct arm_smmu_device *smmu, int n)