xref: /linux/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c (revision f9081b8ff5934b8d69c748d0200e844cadd2c667)
1e86d1aa8SWill Deacon // SPDX-License-Identifier: GPL-2.0-only
2e86d1aa8SWill Deacon /*
3e86d1aa8SWill Deacon  * Copyright (c) 2019, The Linux Foundation. All rights reserved.
4e86d1aa8SWill Deacon  */
5e86d1aa8SWill Deacon 
6e86d1aa8SWill Deacon #include <linux/of_device.h>
7e86d1aa8SWill Deacon #include <linux/qcom_scm.h>
8e86d1aa8SWill Deacon 
9e86d1aa8SWill Deacon #include "arm-smmu.h"
10e86d1aa8SWill Deacon 
11e86d1aa8SWill Deacon struct qcom_smmu {
12e86d1aa8SWill Deacon 	struct arm_smmu_device smmu;
13*f9081b8fSBjorn Andersson 	bool bypass_quirk;
14*f9081b8fSBjorn Andersson 	u8 bypass_cbndx;
15e86d1aa8SWill Deacon };
16e86d1aa8SWill Deacon 
17*f9081b8fSBjorn Andersson static struct qcom_smmu *to_qcom_smmu(struct arm_smmu_device *smmu)
18*f9081b8fSBjorn Andersson {
19*f9081b8fSBjorn Andersson 	return container_of(smmu, struct qcom_smmu, smmu);
20*f9081b8fSBjorn Andersson }
21*f9081b8fSBjorn Andersson 
22e46b3c0dSJoerg Roedel static const struct of_device_id qcom_smmu_client_of_match[] __maybe_unused = {
23e86d1aa8SWill Deacon 	{ .compatible = "qcom,adreno" },
24e86d1aa8SWill Deacon 	{ .compatible = "qcom,mdp4" },
25e86d1aa8SWill Deacon 	{ .compatible = "qcom,mdss" },
26e86d1aa8SWill Deacon 	{ .compatible = "qcom,sc7180-mdss" },
27e86d1aa8SWill Deacon 	{ .compatible = "qcom,sc7180-mss-pil" },
28e86d1aa8SWill Deacon 	{ .compatible = "qcom,sdm845-mdss" },
29e86d1aa8SWill Deacon 	{ .compatible = "qcom,sdm845-mss-pil" },
30e86d1aa8SWill Deacon 	{ }
31e86d1aa8SWill Deacon };
32e86d1aa8SWill Deacon 
3307a7f2caSBjorn Andersson static int qcom_smmu_cfg_probe(struct arm_smmu_device *smmu)
3407a7f2caSBjorn Andersson {
35*f9081b8fSBjorn Andersson 	unsigned int last_s2cr = ARM_SMMU_GR0_S2CR(smmu->num_mapping_groups - 1);
36*f9081b8fSBjorn Andersson 	struct qcom_smmu *qsmmu = to_qcom_smmu(smmu);
37*f9081b8fSBjorn Andersson 	u32 reg;
3807a7f2caSBjorn Andersson 	u32 smr;
3907a7f2caSBjorn Andersson 	int i;
4007a7f2caSBjorn Andersson 
41*f9081b8fSBjorn Andersson 	/*
42*f9081b8fSBjorn Andersson 	 * With some firmware versions writes to S2CR of type FAULT are
43*f9081b8fSBjorn Andersson 	 * ignored, and writing BYPASS will end up written as FAULT in the
44*f9081b8fSBjorn Andersson 	 * register. Perform a write to S2CR to detect if this is the case and
45*f9081b8fSBjorn Andersson 	 * if so reserve a context bank to emulate bypass streams.
46*f9081b8fSBjorn Andersson 	 */
47*f9081b8fSBjorn Andersson 	reg = FIELD_PREP(ARM_SMMU_S2CR_TYPE, S2CR_TYPE_BYPASS) |
48*f9081b8fSBjorn Andersson 	      FIELD_PREP(ARM_SMMU_S2CR_CBNDX, 0xff) |
49*f9081b8fSBjorn Andersson 	      FIELD_PREP(ARM_SMMU_S2CR_PRIVCFG, S2CR_PRIVCFG_DEFAULT);
50*f9081b8fSBjorn Andersson 	arm_smmu_gr0_write(smmu, last_s2cr, reg);
51*f9081b8fSBjorn Andersson 	reg = arm_smmu_gr0_read(smmu, last_s2cr);
52*f9081b8fSBjorn Andersson 	if (FIELD_GET(ARM_SMMU_S2CR_TYPE, reg) != S2CR_TYPE_BYPASS) {
53*f9081b8fSBjorn Andersson 		qsmmu->bypass_quirk = true;
54*f9081b8fSBjorn Andersson 		qsmmu->bypass_cbndx = smmu->num_context_banks - 1;
55*f9081b8fSBjorn Andersson 
56*f9081b8fSBjorn Andersson 		set_bit(qsmmu->bypass_cbndx, smmu->context_map);
57*f9081b8fSBjorn Andersson 
58*f9081b8fSBjorn Andersson 		reg = FIELD_PREP(ARM_SMMU_CBAR_TYPE, CBAR_TYPE_S1_TRANS_S2_BYPASS);
59*f9081b8fSBjorn Andersson 		arm_smmu_gr1_write(smmu, ARM_SMMU_GR1_CBAR(qsmmu->bypass_cbndx), reg);
60*f9081b8fSBjorn Andersson 	}
61*f9081b8fSBjorn Andersson 
6207a7f2caSBjorn Andersson 	for (i = 0; i < smmu->num_mapping_groups; i++) {
6307a7f2caSBjorn Andersson 		smr = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_SMR(i));
6407a7f2caSBjorn Andersson 
6507a7f2caSBjorn Andersson 		if (FIELD_GET(ARM_SMMU_SMR_VALID, smr)) {
6607a7f2caSBjorn Andersson 			smmu->smrs[i].id = FIELD_GET(ARM_SMMU_SMR_ID, smr);
6707a7f2caSBjorn Andersson 			smmu->smrs[i].mask = FIELD_GET(ARM_SMMU_SMR_MASK, smr);
6807a7f2caSBjorn Andersson 			smmu->smrs[i].valid = true;
6907a7f2caSBjorn Andersson 
7007a7f2caSBjorn Andersson 			smmu->s2crs[i].type = S2CR_TYPE_BYPASS;
7107a7f2caSBjorn Andersson 			smmu->s2crs[i].privcfg = S2CR_PRIVCFG_DEFAULT;
7207a7f2caSBjorn Andersson 			smmu->s2crs[i].cbndx = 0xff;
7307a7f2caSBjorn Andersson 		}
7407a7f2caSBjorn Andersson 	}
7507a7f2caSBjorn Andersson 
7607a7f2caSBjorn Andersson 	return 0;
7707a7f2caSBjorn Andersson }
7807a7f2caSBjorn Andersson 
79*f9081b8fSBjorn Andersson static void qcom_smmu_write_s2cr(struct arm_smmu_device *smmu, int idx)
80*f9081b8fSBjorn Andersson {
81*f9081b8fSBjorn Andersson 	struct arm_smmu_s2cr *s2cr = smmu->s2crs + idx;
82*f9081b8fSBjorn Andersson 	struct qcom_smmu *qsmmu = to_qcom_smmu(smmu);
83*f9081b8fSBjorn Andersson 	u32 cbndx = s2cr->cbndx;
84*f9081b8fSBjorn Andersson 	u32 type = s2cr->type;
85*f9081b8fSBjorn Andersson 	u32 reg;
86*f9081b8fSBjorn Andersson 
87*f9081b8fSBjorn Andersson 	if (qsmmu->bypass_quirk) {
88*f9081b8fSBjorn Andersson 		if (type == S2CR_TYPE_BYPASS) {
89*f9081b8fSBjorn Andersson 			/*
90*f9081b8fSBjorn Andersson 			 * Firmware with quirky S2CR handling will substitute
91*f9081b8fSBjorn Andersson 			 * BYPASS writes with FAULT, so point the stream to the
92*f9081b8fSBjorn Andersson 			 * reserved context bank and ask for translation on the
93*f9081b8fSBjorn Andersson 			 * stream
94*f9081b8fSBjorn Andersson 			 */
95*f9081b8fSBjorn Andersson 			type = S2CR_TYPE_TRANS;
96*f9081b8fSBjorn Andersson 			cbndx = qsmmu->bypass_cbndx;
97*f9081b8fSBjorn Andersson 		} else if (type == S2CR_TYPE_FAULT) {
98*f9081b8fSBjorn Andersson 			/*
99*f9081b8fSBjorn Andersson 			 * Firmware with quirky S2CR handling will ignore FAULT
100*f9081b8fSBjorn Andersson 			 * writes, so trick it to write FAULT by asking for a
101*f9081b8fSBjorn Andersson 			 * BYPASS.
102*f9081b8fSBjorn Andersson 			 */
103*f9081b8fSBjorn Andersson 			type = S2CR_TYPE_BYPASS;
104*f9081b8fSBjorn Andersson 			cbndx = 0xff;
105*f9081b8fSBjorn Andersson 		}
106*f9081b8fSBjorn Andersson 	}
107*f9081b8fSBjorn Andersson 
108*f9081b8fSBjorn Andersson 	reg = FIELD_PREP(ARM_SMMU_S2CR_TYPE, type) |
109*f9081b8fSBjorn Andersson 	      FIELD_PREP(ARM_SMMU_S2CR_CBNDX, cbndx) |
110*f9081b8fSBjorn Andersson 	      FIELD_PREP(ARM_SMMU_S2CR_PRIVCFG, s2cr->privcfg);
111*f9081b8fSBjorn Andersson 	arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_S2CR(idx), reg);
112*f9081b8fSBjorn Andersson }
113*f9081b8fSBjorn Andersson 
114e86d1aa8SWill Deacon static int qcom_smmu_def_domain_type(struct device *dev)
115e86d1aa8SWill Deacon {
116e86d1aa8SWill Deacon 	const struct of_device_id *match =
117e86d1aa8SWill Deacon 		of_match_device(qcom_smmu_client_of_match, dev);
118e86d1aa8SWill Deacon 
119e86d1aa8SWill Deacon 	return match ? IOMMU_DOMAIN_IDENTITY : 0;
120e86d1aa8SWill Deacon }
121e86d1aa8SWill Deacon 
122e86d1aa8SWill Deacon static int qcom_sdm845_smmu500_reset(struct arm_smmu_device *smmu)
123e86d1aa8SWill Deacon {
124e86d1aa8SWill Deacon 	int ret;
125e86d1aa8SWill Deacon 
126e86d1aa8SWill Deacon 	/*
127e86d1aa8SWill Deacon 	 * To address performance degradation in non-real time clients,
128e86d1aa8SWill Deacon 	 * such as USB and UFS, turn off wait-for-safe on sdm845 based boards,
129e86d1aa8SWill Deacon 	 * such as MTP and db845, whose firmwares implement secure monitor
130e86d1aa8SWill Deacon 	 * call handlers to turn on/off the wait-for-safe logic.
131e86d1aa8SWill Deacon 	 */
132e86d1aa8SWill Deacon 	ret = qcom_scm_qsmmu500_wait_safe_toggle(0);
133e86d1aa8SWill Deacon 	if (ret)
134e86d1aa8SWill Deacon 		dev_warn(smmu->dev, "Failed to turn off SAFE logic\n");
135e86d1aa8SWill Deacon 
136e86d1aa8SWill Deacon 	return ret;
137e86d1aa8SWill Deacon }
138e86d1aa8SWill Deacon 
139e86d1aa8SWill Deacon static int qcom_smmu500_reset(struct arm_smmu_device *smmu)
140e86d1aa8SWill Deacon {
141e86d1aa8SWill Deacon 	const struct device_node *np = smmu->dev->of_node;
142e86d1aa8SWill Deacon 
143e86d1aa8SWill Deacon 	arm_mmu500_reset(smmu);
144e86d1aa8SWill Deacon 
145e86d1aa8SWill Deacon 	if (of_device_is_compatible(np, "qcom,sdm845-smmu-500"))
146e86d1aa8SWill Deacon 		return qcom_sdm845_smmu500_reset(smmu);
147e86d1aa8SWill Deacon 
148e86d1aa8SWill Deacon 	return 0;
149e86d1aa8SWill Deacon }
150e86d1aa8SWill Deacon 
151e86d1aa8SWill Deacon static const struct arm_smmu_impl qcom_smmu_impl = {
15207a7f2caSBjorn Andersson 	.cfg_probe = qcom_smmu_cfg_probe,
153e86d1aa8SWill Deacon 	.def_domain_type = qcom_smmu_def_domain_type,
154e86d1aa8SWill Deacon 	.reset = qcom_smmu500_reset,
155*f9081b8fSBjorn Andersson 	.write_s2cr = qcom_smmu_write_s2cr,
156e86d1aa8SWill Deacon };
157e86d1aa8SWill Deacon 
158e86d1aa8SWill Deacon struct arm_smmu_device *qcom_smmu_impl_init(struct arm_smmu_device *smmu)
159e86d1aa8SWill Deacon {
160e86d1aa8SWill Deacon 	struct qcom_smmu *qsmmu;
161e86d1aa8SWill Deacon 
162e86d1aa8SWill Deacon 	qsmmu = devm_kzalloc(smmu->dev, sizeof(*qsmmu), GFP_KERNEL);
163e86d1aa8SWill Deacon 	if (!qsmmu)
164e86d1aa8SWill Deacon 		return ERR_PTR(-ENOMEM);
165e86d1aa8SWill Deacon 
166e86d1aa8SWill Deacon 	qsmmu->smmu = *smmu;
167e86d1aa8SWill Deacon 
168e86d1aa8SWill Deacon 	qsmmu->smmu.impl = &qcom_smmu_impl;
169e86d1aa8SWill Deacon 	devm_kfree(smmu->dev, smmu);
170e86d1aa8SWill Deacon 
171e86d1aa8SWill Deacon 	return &qsmmu->smmu;
172e86d1aa8SWill Deacon }
173