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