18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci// Miscellaneous Arm SMMU implementation and integration quirks 38c2ecf20Sopenharmony_ci// Copyright (C) 2019 Arm Limited 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "arm-smmu: " fmt 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/bitfield.h> 88c2ecf20Sopenharmony_ci#include <linux/of.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include "arm-smmu.h" 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_cistatic int arm_smmu_gr0_ns(int offset) 148c2ecf20Sopenharmony_ci{ 158c2ecf20Sopenharmony_ci switch(offset) { 168c2ecf20Sopenharmony_ci case ARM_SMMU_GR0_sCR0: 178c2ecf20Sopenharmony_ci case ARM_SMMU_GR0_sACR: 188c2ecf20Sopenharmony_ci case ARM_SMMU_GR0_sGFSR: 198c2ecf20Sopenharmony_ci case ARM_SMMU_GR0_sGFSYNR0: 208c2ecf20Sopenharmony_ci case ARM_SMMU_GR0_sGFSYNR1: 218c2ecf20Sopenharmony_ci case ARM_SMMU_GR0_sGFSYNR2: 228c2ecf20Sopenharmony_ci return offset + 0x400; 238c2ecf20Sopenharmony_ci default: 248c2ecf20Sopenharmony_ci return offset; 258c2ecf20Sopenharmony_ci } 268c2ecf20Sopenharmony_ci} 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic u32 arm_smmu_read_ns(struct arm_smmu_device *smmu, int page, 298c2ecf20Sopenharmony_ci int offset) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci if (page == ARM_SMMU_GR0) 328c2ecf20Sopenharmony_ci offset = arm_smmu_gr0_ns(offset); 338c2ecf20Sopenharmony_ci return readl_relaxed(arm_smmu_page(smmu, page) + offset); 348c2ecf20Sopenharmony_ci} 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic void arm_smmu_write_ns(struct arm_smmu_device *smmu, int page, 378c2ecf20Sopenharmony_ci int offset, u32 val) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci if (page == ARM_SMMU_GR0) 408c2ecf20Sopenharmony_ci offset = arm_smmu_gr0_ns(offset); 418c2ecf20Sopenharmony_ci writel_relaxed(val, arm_smmu_page(smmu, page) + offset); 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* Since we don't care for sGFAR, we can do without 64-bit accessors */ 458c2ecf20Sopenharmony_cistatic const struct arm_smmu_impl calxeda_impl = { 468c2ecf20Sopenharmony_ci .read_reg = arm_smmu_read_ns, 478c2ecf20Sopenharmony_ci .write_reg = arm_smmu_write_ns, 488c2ecf20Sopenharmony_ci}; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistruct cavium_smmu { 528c2ecf20Sopenharmony_ci struct arm_smmu_device smmu; 538c2ecf20Sopenharmony_ci u32 id_base; 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic int cavium_cfg_probe(struct arm_smmu_device *smmu) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci static atomic_t context_count = ATOMIC_INIT(0); 598c2ecf20Sopenharmony_ci struct cavium_smmu *cs = container_of(smmu, struct cavium_smmu, smmu); 608c2ecf20Sopenharmony_ci /* 618c2ecf20Sopenharmony_ci * Cavium CN88xx erratum #27704. 628c2ecf20Sopenharmony_ci * Ensure ASID and VMID allocation is unique across all SMMUs in 638c2ecf20Sopenharmony_ci * the system. 648c2ecf20Sopenharmony_ci */ 658c2ecf20Sopenharmony_ci cs->id_base = atomic_fetch_add(smmu->num_context_banks, &context_count); 668c2ecf20Sopenharmony_ci dev_notice(smmu->dev, "\tenabling workaround for Cavium erratum 27704\n"); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci return 0; 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic int cavium_init_context(struct arm_smmu_domain *smmu_domain, 728c2ecf20Sopenharmony_ci struct io_pgtable_cfg *pgtbl_cfg, struct device *dev) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci struct cavium_smmu *cs = container_of(smmu_domain->smmu, 758c2ecf20Sopenharmony_ci struct cavium_smmu, smmu); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (smmu_domain->stage == ARM_SMMU_DOMAIN_S2) 788c2ecf20Sopenharmony_ci smmu_domain->cfg.vmid += cs->id_base; 798c2ecf20Sopenharmony_ci else 808c2ecf20Sopenharmony_ci smmu_domain->cfg.asid += cs->id_base; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci return 0; 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic const struct arm_smmu_impl cavium_impl = { 868c2ecf20Sopenharmony_ci .cfg_probe = cavium_cfg_probe, 878c2ecf20Sopenharmony_ci .init_context = cavium_init_context, 888c2ecf20Sopenharmony_ci}; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic struct arm_smmu_device *cavium_smmu_impl_init(struct arm_smmu_device *smmu) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci struct cavium_smmu *cs; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci cs = devm_kzalloc(smmu->dev, sizeof(*cs), GFP_KERNEL); 958c2ecf20Sopenharmony_ci if (!cs) 968c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci cs->smmu = *smmu; 998c2ecf20Sopenharmony_ci cs->smmu.impl = &cavium_impl; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci devm_kfree(smmu->dev, smmu); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci return &cs->smmu; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci#define ARM_MMU500_ACTLR_CPRE (1 << 1) 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci#define ARM_MMU500_ACR_CACHE_LOCK (1 << 26) 1108c2ecf20Sopenharmony_ci#define ARM_MMU500_ACR_S2CRB_TLBEN (1 << 10) 1118c2ecf20Sopenharmony_ci#define ARM_MMU500_ACR_SMTNMB_TLBEN (1 << 8) 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ciint arm_mmu500_reset(struct arm_smmu_device *smmu) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci u32 reg, major; 1168c2ecf20Sopenharmony_ci int i; 1178c2ecf20Sopenharmony_ci /* 1188c2ecf20Sopenharmony_ci * On MMU-500 r2p0 onwards we need to clear ACR.CACHE_LOCK before 1198c2ecf20Sopenharmony_ci * writes to the context bank ACTLRs will stick. And we just hope that 1208c2ecf20Sopenharmony_ci * Secure has also cleared SACR.CACHE_LOCK for this to take effect... 1218c2ecf20Sopenharmony_ci */ 1228c2ecf20Sopenharmony_ci reg = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_ID7); 1238c2ecf20Sopenharmony_ci major = FIELD_GET(ARM_SMMU_ID7_MAJOR, reg); 1248c2ecf20Sopenharmony_ci reg = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_sACR); 1258c2ecf20Sopenharmony_ci if (major >= 2) 1268c2ecf20Sopenharmony_ci reg &= ~ARM_MMU500_ACR_CACHE_LOCK; 1278c2ecf20Sopenharmony_ci /* 1288c2ecf20Sopenharmony_ci * Allow unmatched Stream IDs to allocate bypass 1298c2ecf20Sopenharmony_ci * TLB entries for reduced latency. 1308c2ecf20Sopenharmony_ci */ 1318c2ecf20Sopenharmony_ci reg |= ARM_MMU500_ACR_SMTNMB_TLBEN | ARM_MMU500_ACR_S2CRB_TLBEN; 1328c2ecf20Sopenharmony_ci arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_sACR, reg); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci /* 1358c2ecf20Sopenharmony_ci * Disable MMU-500's not-particularly-beneficial next-page 1368c2ecf20Sopenharmony_ci * prefetcher for the sake of errata #841119 and #826419. 1378c2ecf20Sopenharmony_ci */ 1388c2ecf20Sopenharmony_ci for (i = 0; i < smmu->num_context_banks; ++i) { 1398c2ecf20Sopenharmony_ci reg = arm_smmu_cb_read(smmu, i, ARM_SMMU_CB_ACTLR); 1408c2ecf20Sopenharmony_ci reg &= ~ARM_MMU500_ACTLR_CPRE; 1418c2ecf20Sopenharmony_ci arm_smmu_cb_write(smmu, i, ARM_SMMU_CB_ACTLR, reg); 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci return 0; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic const struct arm_smmu_impl arm_mmu500_impl = { 1488c2ecf20Sopenharmony_ci .reset = arm_mmu500_reset, 1498c2ecf20Sopenharmony_ci}; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic u64 mrvl_mmu500_readq(struct arm_smmu_device *smmu, int page, int off) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci /* 1548c2ecf20Sopenharmony_ci * Marvell Armada-AP806 erratum #582743. 1558c2ecf20Sopenharmony_ci * Split all the readq to double readl 1568c2ecf20Sopenharmony_ci */ 1578c2ecf20Sopenharmony_ci return hi_lo_readq_relaxed(arm_smmu_page(smmu, page) + off); 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic void mrvl_mmu500_writeq(struct arm_smmu_device *smmu, int page, int off, 1618c2ecf20Sopenharmony_ci u64 val) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci /* 1648c2ecf20Sopenharmony_ci * Marvell Armada-AP806 erratum #582743. 1658c2ecf20Sopenharmony_ci * Split all the writeq to double writel 1668c2ecf20Sopenharmony_ci */ 1678c2ecf20Sopenharmony_ci hi_lo_writeq_relaxed(val, arm_smmu_page(smmu, page) + off); 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic int mrvl_mmu500_cfg_probe(struct arm_smmu_device *smmu) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci /* 1748c2ecf20Sopenharmony_ci * Armada-AP806 erratum #582743. 1758c2ecf20Sopenharmony_ci * Hide the SMMU_IDR2.PTFSv8 fields to sidestep the AArch64 1768c2ecf20Sopenharmony_ci * formats altogether and allow using 32 bits access on the 1778c2ecf20Sopenharmony_ci * interconnect. 1788c2ecf20Sopenharmony_ci */ 1798c2ecf20Sopenharmony_ci smmu->features &= ~(ARM_SMMU_FEAT_FMT_AARCH64_4K | 1808c2ecf20Sopenharmony_ci ARM_SMMU_FEAT_FMT_AARCH64_16K | 1818c2ecf20Sopenharmony_ci ARM_SMMU_FEAT_FMT_AARCH64_64K); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci return 0; 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic const struct arm_smmu_impl mrvl_mmu500_impl = { 1878c2ecf20Sopenharmony_ci .read_reg64 = mrvl_mmu500_readq, 1888c2ecf20Sopenharmony_ci .write_reg64 = mrvl_mmu500_writeq, 1898c2ecf20Sopenharmony_ci .cfg_probe = mrvl_mmu500_cfg_probe, 1908c2ecf20Sopenharmony_ci .reset = arm_mmu500_reset, 1918c2ecf20Sopenharmony_ci}; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistruct arm_smmu_device *arm_smmu_impl_init(struct arm_smmu_device *smmu) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci const struct device_node *np = smmu->dev->of_node; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci /* 1998c2ecf20Sopenharmony_ci * Set the impl for model-specific implementation quirks first, 2008c2ecf20Sopenharmony_ci * such that platform integration quirks can pick it up and 2018c2ecf20Sopenharmony_ci * inherit from it if necessary. 2028c2ecf20Sopenharmony_ci */ 2038c2ecf20Sopenharmony_ci switch (smmu->model) { 2048c2ecf20Sopenharmony_ci case ARM_MMU500: 2058c2ecf20Sopenharmony_ci smmu->impl = &arm_mmu500_impl; 2068c2ecf20Sopenharmony_ci break; 2078c2ecf20Sopenharmony_ci case CAVIUM_SMMUV2: 2088c2ecf20Sopenharmony_ci return cavium_smmu_impl_init(smmu); 2098c2ecf20Sopenharmony_ci default: 2108c2ecf20Sopenharmony_ci break; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci /* This is implicitly MMU-400 */ 2148c2ecf20Sopenharmony_ci if (of_property_read_bool(np, "calxeda,smmu-secure-config-access")) 2158c2ecf20Sopenharmony_ci smmu->impl = &calxeda_impl; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (of_device_is_compatible(np, "nvidia,tegra194-smmu")) 2188c2ecf20Sopenharmony_ci return nvidia_smmu_impl_init(smmu); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if (of_device_is_compatible(np, "qcom,sdm845-smmu-500") || 2218c2ecf20Sopenharmony_ci of_device_is_compatible(np, "qcom,sc7180-smmu-500") || 2228c2ecf20Sopenharmony_ci of_device_is_compatible(np, "qcom,sm8150-smmu-500") || 2238c2ecf20Sopenharmony_ci of_device_is_compatible(np, "qcom,sm8250-smmu-500")) 2248c2ecf20Sopenharmony_ci return qcom_smmu_impl_init(smmu); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (of_device_is_compatible(np, "marvell,ap806-smmu-500")) 2278c2ecf20Sopenharmony_ci smmu->impl = &mrvl_mmu500_impl; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci return smmu; 2308c2ecf20Sopenharmony_ci} 231