162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci// Miscellaneous Arm SMMU implementation and integration quirks 362306a36Sopenharmony_ci// Copyright (C) 2019 Arm Limited 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#define pr_fmt(fmt) "arm-smmu: " fmt 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/bitfield.h> 862306a36Sopenharmony_ci#include <linux/of.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "arm-smmu.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_cistatic int arm_smmu_gr0_ns(int offset) 1462306a36Sopenharmony_ci{ 1562306a36Sopenharmony_ci switch (offset) { 1662306a36Sopenharmony_ci case ARM_SMMU_GR0_sCR0: 1762306a36Sopenharmony_ci case ARM_SMMU_GR0_sACR: 1862306a36Sopenharmony_ci case ARM_SMMU_GR0_sGFSR: 1962306a36Sopenharmony_ci case ARM_SMMU_GR0_sGFSYNR0: 2062306a36Sopenharmony_ci case ARM_SMMU_GR0_sGFSYNR1: 2162306a36Sopenharmony_ci case ARM_SMMU_GR0_sGFSYNR2: 2262306a36Sopenharmony_ci return offset + 0x400; 2362306a36Sopenharmony_ci default: 2462306a36Sopenharmony_ci return offset; 2562306a36Sopenharmony_ci } 2662306a36Sopenharmony_ci} 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic u32 arm_smmu_read_ns(struct arm_smmu_device *smmu, int page, 2962306a36Sopenharmony_ci int offset) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci if (page == ARM_SMMU_GR0) 3262306a36Sopenharmony_ci offset = arm_smmu_gr0_ns(offset); 3362306a36Sopenharmony_ci return readl_relaxed(arm_smmu_page(smmu, page) + offset); 3462306a36Sopenharmony_ci} 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic void arm_smmu_write_ns(struct arm_smmu_device *smmu, int page, 3762306a36Sopenharmony_ci int offset, u32 val) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci if (page == ARM_SMMU_GR0) 4062306a36Sopenharmony_ci offset = arm_smmu_gr0_ns(offset); 4162306a36Sopenharmony_ci writel_relaxed(val, arm_smmu_page(smmu, page) + offset); 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* Since we don't care for sGFAR, we can do without 64-bit accessors */ 4562306a36Sopenharmony_cistatic const struct arm_smmu_impl calxeda_impl = { 4662306a36Sopenharmony_ci .read_reg = arm_smmu_read_ns, 4762306a36Sopenharmony_ci .write_reg = arm_smmu_write_ns, 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistruct cavium_smmu { 5262306a36Sopenharmony_ci struct arm_smmu_device smmu; 5362306a36Sopenharmony_ci u32 id_base; 5462306a36Sopenharmony_ci}; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic int cavium_cfg_probe(struct arm_smmu_device *smmu) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci static atomic_t context_count = ATOMIC_INIT(0); 5962306a36Sopenharmony_ci struct cavium_smmu *cs = container_of(smmu, struct cavium_smmu, smmu); 6062306a36Sopenharmony_ci /* 6162306a36Sopenharmony_ci * Cavium CN88xx erratum #27704. 6262306a36Sopenharmony_ci * Ensure ASID and VMID allocation is unique across all SMMUs in 6362306a36Sopenharmony_ci * the system. 6462306a36Sopenharmony_ci */ 6562306a36Sopenharmony_ci cs->id_base = atomic_fetch_add(smmu->num_context_banks, &context_count); 6662306a36Sopenharmony_ci dev_notice(smmu->dev, "\tenabling workaround for Cavium erratum 27704\n"); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci return 0; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic int cavium_init_context(struct arm_smmu_domain *smmu_domain, 7262306a36Sopenharmony_ci struct io_pgtable_cfg *pgtbl_cfg, struct device *dev) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci struct cavium_smmu *cs = container_of(smmu_domain->smmu, 7562306a36Sopenharmony_ci struct cavium_smmu, smmu); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci if (smmu_domain->stage == ARM_SMMU_DOMAIN_S2) 7862306a36Sopenharmony_ci smmu_domain->cfg.vmid += cs->id_base; 7962306a36Sopenharmony_ci else 8062306a36Sopenharmony_ci smmu_domain->cfg.asid += cs->id_base; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci return 0; 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic const struct arm_smmu_impl cavium_impl = { 8662306a36Sopenharmony_ci .cfg_probe = cavium_cfg_probe, 8762306a36Sopenharmony_ci .init_context = cavium_init_context, 8862306a36Sopenharmony_ci}; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic struct arm_smmu_device *cavium_smmu_impl_init(struct arm_smmu_device *smmu) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci struct cavium_smmu *cs; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci cs = devm_krealloc(smmu->dev, smmu, sizeof(*cs), GFP_KERNEL); 9562306a36Sopenharmony_ci if (!cs) 9662306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci cs->smmu.impl = &cavium_impl; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci return &cs->smmu; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci#define ARM_MMU500_ACTLR_CPRE (1 << 1) 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci#define ARM_MMU500_ACR_CACHE_LOCK (1 << 26) 10762306a36Sopenharmony_ci#define ARM_MMU500_ACR_S2CRB_TLBEN (1 << 10) 10862306a36Sopenharmony_ci#define ARM_MMU500_ACR_SMTNMB_TLBEN (1 << 8) 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ciint arm_mmu500_reset(struct arm_smmu_device *smmu) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci u32 reg, major; 11362306a36Sopenharmony_ci int i; 11462306a36Sopenharmony_ci /* 11562306a36Sopenharmony_ci * On MMU-500 r2p0 onwards we need to clear ACR.CACHE_LOCK before 11662306a36Sopenharmony_ci * writes to the context bank ACTLRs will stick. And we just hope that 11762306a36Sopenharmony_ci * Secure has also cleared SACR.CACHE_LOCK for this to take effect... 11862306a36Sopenharmony_ci */ 11962306a36Sopenharmony_ci reg = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_ID7); 12062306a36Sopenharmony_ci major = FIELD_GET(ARM_SMMU_ID7_MAJOR, reg); 12162306a36Sopenharmony_ci reg = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_sACR); 12262306a36Sopenharmony_ci if (major >= 2) 12362306a36Sopenharmony_ci reg &= ~ARM_MMU500_ACR_CACHE_LOCK; 12462306a36Sopenharmony_ci /* 12562306a36Sopenharmony_ci * Allow unmatched Stream IDs to allocate bypass 12662306a36Sopenharmony_ci * TLB entries for reduced latency. 12762306a36Sopenharmony_ci */ 12862306a36Sopenharmony_ci reg |= ARM_MMU500_ACR_SMTNMB_TLBEN | ARM_MMU500_ACR_S2CRB_TLBEN; 12962306a36Sopenharmony_ci arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_sACR, reg); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci /* 13262306a36Sopenharmony_ci * Disable MMU-500's not-particularly-beneficial next-page 13362306a36Sopenharmony_ci * prefetcher for the sake of errata #841119 and #826419. 13462306a36Sopenharmony_ci */ 13562306a36Sopenharmony_ci for (i = 0; i < smmu->num_context_banks; ++i) { 13662306a36Sopenharmony_ci reg = arm_smmu_cb_read(smmu, i, ARM_SMMU_CB_ACTLR); 13762306a36Sopenharmony_ci reg &= ~ARM_MMU500_ACTLR_CPRE; 13862306a36Sopenharmony_ci arm_smmu_cb_write(smmu, i, ARM_SMMU_CB_ACTLR, reg); 13962306a36Sopenharmony_ci reg = arm_smmu_cb_read(smmu, i, ARM_SMMU_CB_ACTLR); 14062306a36Sopenharmony_ci if (reg & ARM_MMU500_ACTLR_CPRE) 14162306a36Sopenharmony_ci dev_warn_once(smmu->dev, "Failed to disable prefetcher [errata #841119 and #826419], check ACR.CACHE_LOCK\n"); 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci return 0; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic const struct arm_smmu_impl arm_mmu500_impl = { 14862306a36Sopenharmony_ci .reset = arm_mmu500_reset, 14962306a36Sopenharmony_ci}; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic u64 mrvl_mmu500_readq(struct arm_smmu_device *smmu, int page, int off) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci /* 15462306a36Sopenharmony_ci * Marvell Armada-AP806 erratum #582743. 15562306a36Sopenharmony_ci * Split all the readq to double readl 15662306a36Sopenharmony_ci */ 15762306a36Sopenharmony_ci return hi_lo_readq_relaxed(arm_smmu_page(smmu, page) + off); 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic void mrvl_mmu500_writeq(struct arm_smmu_device *smmu, int page, int off, 16162306a36Sopenharmony_ci u64 val) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci /* 16462306a36Sopenharmony_ci * Marvell Armada-AP806 erratum #582743. 16562306a36Sopenharmony_ci * Split all the writeq to double writel 16662306a36Sopenharmony_ci */ 16762306a36Sopenharmony_ci hi_lo_writeq_relaxed(val, arm_smmu_page(smmu, page) + off); 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic int mrvl_mmu500_cfg_probe(struct arm_smmu_device *smmu) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* 17462306a36Sopenharmony_ci * Armada-AP806 erratum #582743. 17562306a36Sopenharmony_ci * Hide the SMMU_IDR2.PTFSv8 fields to sidestep the AArch64 17662306a36Sopenharmony_ci * formats altogether and allow using 32 bits access on the 17762306a36Sopenharmony_ci * interconnect. 17862306a36Sopenharmony_ci */ 17962306a36Sopenharmony_ci smmu->features &= ~(ARM_SMMU_FEAT_FMT_AARCH64_4K | 18062306a36Sopenharmony_ci ARM_SMMU_FEAT_FMT_AARCH64_16K | 18162306a36Sopenharmony_ci ARM_SMMU_FEAT_FMT_AARCH64_64K); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci return 0; 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic const struct arm_smmu_impl mrvl_mmu500_impl = { 18762306a36Sopenharmony_ci .read_reg64 = mrvl_mmu500_readq, 18862306a36Sopenharmony_ci .write_reg64 = mrvl_mmu500_writeq, 18962306a36Sopenharmony_ci .cfg_probe = mrvl_mmu500_cfg_probe, 19062306a36Sopenharmony_ci .reset = arm_mmu500_reset, 19162306a36Sopenharmony_ci}; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistruct arm_smmu_device *arm_smmu_impl_init(struct arm_smmu_device *smmu) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci const struct device_node *np = smmu->dev->of_node; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci /* 19962306a36Sopenharmony_ci * Set the impl for model-specific implementation quirks first, 20062306a36Sopenharmony_ci * such that platform integration quirks can pick it up and 20162306a36Sopenharmony_ci * inherit from it if necessary. 20262306a36Sopenharmony_ci */ 20362306a36Sopenharmony_ci switch (smmu->model) { 20462306a36Sopenharmony_ci case ARM_MMU500: 20562306a36Sopenharmony_ci smmu->impl = &arm_mmu500_impl; 20662306a36Sopenharmony_ci break; 20762306a36Sopenharmony_ci case CAVIUM_SMMUV2: 20862306a36Sopenharmony_ci return cavium_smmu_impl_init(smmu); 20962306a36Sopenharmony_ci default: 21062306a36Sopenharmony_ci break; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci /* This is implicitly MMU-400 */ 21462306a36Sopenharmony_ci if (of_property_read_bool(np, "calxeda,smmu-secure-config-access")) 21562306a36Sopenharmony_ci smmu->impl = &calxeda_impl; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (of_device_is_compatible(np, "nvidia,tegra234-smmu") || 21862306a36Sopenharmony_ci of_device_is_compatible(np, "nvidia,tegra194-smmu") || 21962306a36Sopenharmony_ci of_device_is_compatible(np, "nvidia,tegra186-smmu")) 22062306a36Sopenharmony_ci return nvidia_smmu_impl_init(smmu); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_ARM_SMMU_QCOM)) 22362306a36Sopenharmony_ci smmu = qcom_smmu_impl_init(smmu); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (of_device_is_compatible(np, "marvell,ap806-smmu-500")) 22662306a36Sopenharmony_ci smmu->impl = &mrvl_mmu500_impl; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci return smmu; 22962306a36Sopenharmony_ci} 230