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