Commit | Line | Data |
---|---|---|
759aaa10 VG |
1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* | |
3 | * Copyright (c) 2019, The Linux Foundation. All rights reserved. | |
4 | */ | |
5 | ||
a51627c5 | 6 | #include <linux/acpi.h> |
5c7469c6 | 7 | #include <linux/adreno-smmu-priv.h> |
b9b721d1 | 8 | #include <linux/delay.h> |
0e764a01 | 9 | #include <linux/of_device.h> |
759aaa10 VG |
10 | #include <linux/qcom_scm.h> |
11 | ||
12 | #include "arm-smmu.h" | |
b9b721d1 | 13 | #include "arm-smmu-qcom.h" |
759aaa10 | 14 | |
b9b721d1 | 15 | #define QCOM_DUMMY_VAL -1 |
759aaa10 | 16 | |
f9081b8f BA |
17 | static struct qcom_smmu *to_qcom_smmu(struct arm_smmu_device *smmu) |
18 | { | |
19 | return container_of(smmu, struct qcom_smmu, smmu); | |
20 | } | |
21 | ||
b9b721d1 SPR |
22 | static void qcom_smmu_tlb_sync(struct arm_smmu_device *smmu, int page, |
23 | int sync, int status) | |
24 | { | |
25 | unsigned int spin_cnt, delay; | |
26 | u32 reg; | |
27 | ||
28 | arm_smmu_writel(smmu, page, sync, QCOM_DUMMY_VAL); | |
29 | for (delay = 1; delay < TLB_LOOP_TIMEOUT; delay *= 2) { | |
30 | for (spin_cnt = TLB_SPIN_COUNT; spin_cnt > 0; spin_cnt--) { | |
31 | reg = arm_smmu_readl(smmu, page, status); | |
32 | if (!(reg & ARM_SMMU_sTLBGSTATUS_GSACTIVE)) | |
33 | return; | |
34 | cpu_relax(); | |
35 | } | |
36 | udelay(delay); | |
37 | } | |
38 | ||
39 | qcom_smmu_tlb_sync_debug(smmu); | |
40 | } | |
41 | ||
bffb2eaf RC |
42 | static void qcom_adreno_smmu_write_sctlr(struct arm_smmu_device *smmu, int idx, |
43 | u32 reg) | |
44 | { | |
ba6014a4 RC |
45 | struct qcom_smmu *qsmmu = to_qcom_smmu(smmu); |
46 | ||
bffb2eaf RC |
47 | /* |
48 | * On the GPU device we want to process subsequent transactions after a | |
49 | * fault to keep the GPU from hanging | |
50 | */ | |
51 | reg |= ARM_SMMU_SCTLR_HUPCF; | |
52 | ||
ba6014a4 RC |
53 | if (qsmmu->stall_enabled & BIT(idx)) |
54 | reg |= ARM_SMMU_SCTLR_CFCFG; | |
55 | ||
bffb2eaf RC |
56 | arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_SCTLR, reg); |
57 | } | |
58 | ||
ab5df7b9 JC |
59 | static void qcom_adreno_smmu_get_fault_info(const void *cookie, |
60 | struct adreno_smmu_fault_info *info) | |
61 | { | |
62 | struct arm_smmu_domain *smmu_domain = (void *)cookie; | |
63 | struct arm_smmu_cfg *cfg = &smmu_domain->cfg; | |
64 | struct arm_smmu_device *smmu = smmu_domain->smmu; | |
65 | ||
66 | info->fsr = arm_smmu_cb_read(smmu, cfg->cbndx, ARM_SMMU_CB_FSR); | |
67 | info->fsynr0 = arm_smmu_cb_read(smmu, cfg->cbndx, ARM_SMMU_CB_FSYNR0); | |
68 | info->fsynr1 = arm_smmu_cb_read(smmu, cfg->cbndx, ARM_SMMU_CB_FSYNR1); | |
69 | info->far = arm_smmu_cb_readq(smmu, cfg->cbndx, ARM_SMMU_CB_FAR); | |
70 | info->cbfrsynra = arm_smmu_gr1_read(smmu, ARM_SMMU_GR1_CBFRSYNRA(cfg->cbndx)); | |
c31112fb | 71 | info->ttbr0 = arm_smmu_cb_readq(smmu, cfg->cbndx, ARM_SMMU_CB_TTBR0); |
ab5df7b9 JC |
72 | info->contextidr = arm_smmu_cb_read(smmu, cfg->cbndx, ARM_SMMU_CB_CONTEXTIDR); |
73 | } | |
74 | ||
ba6014a4 RC |
75 | static void qcom_adreno_smmu_set_stall(const void *cookie, bool enabled) |
76 | { | |
77 | struct arm_smmu_domain *smmu_domain = (void *)cookie; | |
78 | struct arm_smmu_cfg *cfg = &smmu_domain->cfg; | |
79 | struct qcom_smmu *qsmmu = to_qcom_smmu(smmu_domain->smmu); | |
80 | ||
81 | if (enabled) | |
82 | qsmmu->stall_enabled |= BIT(cfg->cbndx); | |
83 | else | |
84 | qsmmu->stall_enabled &= ~BIT(cfg->cbndx); | |
85 | } | |
86 | ||
87 | static void qcom_adreno_smmu_resume_translation(const void *cookie, bool terminate) | |
88 | { | |
89 | struct arm_smmu_domain *smmu_domain = (void *)cookie; | |
90 | struct arm_smmu_cfg *cfg = &smmu_domain->cfg; | |
91 | struct arm_smmu_device *smmu = smmu_domain->smmu; | |
92 | u32 reg = 0; | |
93 | ||
94 | if (terminate) | |
95 | reg |= ARM_SMMU_RESUME_TERMINATE; | |
96 | ||
97 | arm_smmu_cb_write(smmu, cfg->cbndx, ARM_SMMU_CB_RESUME, reg); | |
98 | } | |
99 | ||
5c7469c6 JC |
100 | #define QCOM_ADRENO_SMMU_GPU_SID 0 |
101 | ||
102 | static bool qcom_adreno_smmu_is_gpu_device(struct device *dev) | |
103 | { | |
104 | struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); | |
105 | int i; | |
106 | ||
107 | /* | |
108 | * The GPU will always use SID 0 so that is a handy way to uniquely | |
109 | * identify it and configure it for per-instance pagetables | |
110 | */ | |
111 | for (i = 0; i < fwspec->num_ids; i++) { | |
112 | u16 sid = FIELD_GET(ARM_SMMU_SMR_ID, fwspec->ids[i]); | |
113 | ||
114 | if (sid == QCOM_ADRENO_SMMU_GPU_SID) | |
115 | return true; | |
116 | } | |
117 | ||
118 | return false; | |
119 | } | |
120 | ||
121 | static const struct io_pgtable_cfg *qcom_adreno_smmu_get_ttbr1_cfg( | |
122 | const void *cookie) | |
123 | { | |
124 | struct arm_smmu_domain *smmu_domain = (void *)cookie; | |
125 | struct io_pgtable *pgtable = | |
126 | io_pgtable_ops_to_pgtable(smmu_domain->pgtbl_ops); | |
127 | return &pgtable->cfg; | |
128 | } | |
129 | ||
130 | /* | |
131 | * Local implementation to configure TTBR0 with the specified pagetable config. | |
132 | * The GPU driver will call this to enable TTBR0 when per-instance pagetables | |
133 | * are active | |
134 | */ | |
135 | ||
136 | static int qcom_adreno_smmu_set_ttbr0_cfg(const void *cookie, | |
137 | const struct io_pgtable_cfg *pgtbl_cfg) | |
138 | { | |
139 | struct arm_smmu_domain *smmu_domain = (void *)cookie; | |
140 | struct io_pgtable *pgtable = io_pgtable_ops_to_pgtable(smmu_domain->pgtbl_ops); | |
141 | struct arm_smmu_cfg *cfg = &smmu_domain->cfg; | |
142 | struct arm_smmu_cb *cb = &smmu_domain->smmu->cbs[cfg->cbndx]; | |
143 | ||
144 | /* The domain must have split pagetables already enabled */ | |
145 | if (cb->tcr[0] & ARM_SMMU_TCR_EPD1) | |
146 | return -EINVAL; | |
147 | ||
148 | /* If the pagetable config is NULL, disable TTBR0 */ | |
149 | if (!pgtbl_cfg) { | |
150 | /* Do nothing if it is already disabled */ | |
151 | if ((cb->tcr[0] & ARM_SMMU_TCR_EPD0)) | |
152 | return -EINVAL; | |
153 | ||
154 | /* Set TCR to the original configuration */ | |
155 | cb->tcr[0] = arm_smmu_lpae_tcr(&pgtable->cfg); | |
156 | cb->ttbr[0] = FIELD_PREP(ARM_SMMU_TTBRn_ASID, cb->cfg->asid); | |
157 | } else { | |
158 | u32 tcr = cb->tcr[0]; | |
159 | ||
160 | /* Don't call this again if TTBR0 is already enabled */ | |
161 | if (!(cb->tcr[0] & ARM_SMMU_TCR_EPD0)) | |
162 | return -EINVAL; | |
163 | ||
164 | tcr |= arm_smmu_lpae_tcr(pgtbl_cfg); | |
165 | tcr &= ~(ARM_SMMU_TCR_EPD0 | ARM_SMMU_TCR_EPD1); | |
166 | ||
167 | cb->tcr[0] = tcr; | |
168 | cb->ttbr[0] = pgtbl_cfg->arm_lpae_s1_cfg.ttbr; | |
169 | cb->ttbr[0] |= FIELD_PREP(ARM_SMMU_TTBRn_ASID, cb->cfg->asid); | |
170 | } | |
171 | ||
172 | arm_smmu_write_context_bank(smmu_domain->smmu, cb->cfg->cbndx); | |
173 | ||
174 | return 0; | |
175 | } | |
176 | ||
177 | static int qcom_adreno_smmu_alloc_context_bank(struct arm_smmu_domain *smmu_domain, | |
178 | struct arm_smmu_device *smmu, | |
179 | struct device *dev, int start) | |
180 | { | |
181 | int count; | |
182 | ||
183 | /* | |
184 | * Assign context bank 0 to the GPU device so the GPU hardware can | |
185 | * switch pagetables | |
186 | */ | |
187 | if (qcom_adreno_smmu_is_gpu_device(dev)) { | |
188 | start = 0; | |
189 | count = 1; | |
190 | } else { | |
191 | start = 1; | |
192 | count = smmu->num_context_banks; | |
193 | } | |
194 | ||
195 | return __arm_smmu_alloc_bitmap(smmu->context_map, start, count); | |
196 | } | |
197 | ||
a242f429 EA |
198 | static bool qcom_adreno_can_do_ttbr1(struct arm_smmu_device *smmu) |
199 | { | |
200 | const struct device_node *np = smmu->dev->of_node; | |
201 | ||
202 | if (of_device_is_compatible(np, "qcom,msm8996-smmu-v2")) | |
203 | return false; | |
204 | ||
205 | return true; | |
206 | } | |
207 | ||
5c7469c6 JC |
208 | static int qcom_adreno_smmu_init_context(struct arm_smmu_domain *smmu_domain, |
209 | struct io_pgtable_cfg *pgtbl_cfg, struct device *dev) | |
210 | { | |
211 | struct adreno_smmu_priv *priv; | |
212 | ||
ef75702d SPR |
213 | smmu_domain->cfg.flush_walk_prefer_tlbiasid = true; |
214 | ||
5c7469c6 JC |
215 | /* Only enable split pagetables for the GPU device (SID 0) */ |
216 | if (!qcom_adreno_smmu_is_gpu_device(dev)) | |
217 | return 0; | |
218 | ||
219 | /* | |
220 | * All targets that use the qcom,adreno-smmu compatible string *should* | |
221 | * be AARCH64 stage 1 but double check because the arm-smmu code assumes | |
222 | * that is the case when the TTBR1 quirk is enabled | |
223 | */ | |
a242f429 EA |
224 | if (qcom_adreno_can_do_ttbr1(smmu_domain->smmu) && |
225 | (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) && | |
5c7469c6 JC |
226 | (smmu_domain->cfg.fmt == ARM_SMMU_CTX_FMT_AARCH64)) |
227 | pgtbl_cfg->quirks |= IO_PGTABLE_QUIRK_ARM_TTBR1; | |
228 | ||
229 | /* | |
230 | * Initialize private interface with GPU: | |
231 | */ | |
232 | ||
233 | priv = dev_get_drvdata(dev); | |
234 | priv->cookie = smmu_domain; | |
235 | priv->get_ttbr1_cfg = qcom_adreno_smmu_get_ttbr1_cfg; | |
236 | priv->set_ttbr0_cfg = qcom_adreno_smmu_set_ttbr0_cfg; | |
ab5df7b9 | 237 | priv->get_fault_info = qcom_adreno_smmu_get_fault_info; |
ba6014a4 RC |
238 | priv->set_stall = qcom_adreno_smmu_set_stall; |
239 | priv->resume_translation = qcom_adreno_smmu_resume_translation; | |
5c7469c6 JC |
240 | |
241 | return 0; | |
242 | } | |
243 | ||
a082121b | 244 | static const struct of_device_id qcom_smmu_client_of_match[] __maybe_unused = { |
0e764a01 JC |
245 | { .compatible = "qcom,adreno" }, |
246 | { .compatible = "qcom,mdp4" }, | |
247 | { .compatible = "qcom,mdss" }, | |
248 | { .compatible = "qcom,sc7180-mdss" }, | |
d100ff38 | 249 | { .compatible = "qcom,sc7180-mss-pil" }, |
0b779f56 | 250 | { .compatible = "qcom,sc7280-mdss" }, |
e37f1fe4 | 251 | { .compatible = "qcom,sc7280-mss-pil" }, |
1a7180ff | 252 | { .compatible = "qcom,sc8180x-mdss" }, |
3482c0b7 | 253 | { .compatible = "qcom,sm8250-mdss" }, |
0e764a01 | 254 | { .compatible = "qcom,sdm845-mdss" }, |
d100ff38 | 255 | { .compatible = "qcom,sdm845-mss-pil" }, |
0e764a01 JC |
256 | { } |
257 | }; | |
258 | ||
ef75702d SPR |
259 | static int qcom_smmu_init_context(struct arm_smmu_domain *smmu_domain, |
260 | struct io_pgtable_cfg *pgtbl_cfg, struct device *dev) | |
261 | { | |
262 | smmu_domain->cfg.flush_walk_prefer_tlbiasid = true; | |
263 | ||
264 | return 0; | |
265 | } | |
266 | ||
07a7f2ca BA |
267 | static int qcom_smmu_cfg_probe(struct arm_smmu_device *smmu) |
268 | { | |
f9081b8f BA |
269 | unsigned int last_s2cr = ARM_SMMU_GR0_S2CR(smmu->num_mapping_groups - 1); |
270 | struct qcom_smmu *qsmmu = to_qcom_smmu(smmu); | |
271 | u32 reg; | |
07a7f2ca BA |
272 | u32 smr; |
273 | int i; | |
274 | ||
f9081b8f BA |
275 | /* |
276 | * With some firmware versions writes to S2CR of type FAULT are | |
277 | * ignored, and writing BYPASS will end up written as FAULT in the | |
278 | * register. Perform a write to S2CR to detect if this is the case and | |
279 | * if so reserve a context bank to emulate bypass streams. | |
280 | */ | |
281 | reg = FIELD_PREP(ARM_SMMU_S2CR_TYPE, S2CR_TYPE_BYPASS) | | |
282 | FIELD_PREP(ARM_SMMU_S2CR_CBNDX, 0xff) | | |
283 | FIELD_PREP(ARM_SMMU_S2CR_PRIVCFG, S2CR_PRIVCFG_DEFAULT); | |
284 | arm_smmu_gr0_write(smmu, last_s2cr, reg); | |
285 | reg = arm_smmu_gr0_read(smmu, last_s2cr); | |
286 | if (FIELD_GET(ARM_SMMU_S2CR_TYPE, reg) != S2CR_TYPE_BYPASS) { | |
287 | qsmmu->bypass_quirk = true; | |
288 | qsmmu->bypass_cbndx = smmu->num_context_banks - 1; | |
289 | ||
290 | set_bit(qsmmu->bypass_cbndx, smmu->context_map); | |
291 | ||
aded8c7c BA |
292 | arm_smmu_cb_write(smmu, qsmmu->bypass_cbndx, ARM_SMMU_CB_SCTLR, 0); |
293 | ||
f9081b8f BA |
294 | reg = FIELD_PREP(ARM_SMMU_CBAR_TYPE, CBAR_TYPE_S1_TRANS_S2_BYPASS); |
295 | arm_smmu_gr1_write(smmu, ARM_SMMU_GR1_CBAR(qsmmu->bypass_cbndx), reg); | |
296 | } | |
297 | ||
07a7f2ca BA |
298 | for (i = 0; i < smmu->num_mapping_groups; i++) { |
299 | smr = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_SMR(i)); | |
300 | ||
301 | if (FIELD_GET(ARM_SMMU_SMR_VALID, smr)) { | |
dead723e IM |
302 | /* Ignore valid bit for SMR mask extraction. */ |
303 | smr &= ~ARM_SMMU_SMR_VALID; | |
07a7f2ca BA |
304 | smmu->smrs[i].id = FIELD_GET(ARM_SMMU_SMR_ID, smr); |
305 | smmu->smrs[i].mask = FIELD_GET(ARM_SMMU_SMR_MASK, smr); | |
306 | smmu->smrs[i].valid = true; | |
307 | ||
308 | smmu->s2crs[i].type = S2CR_TYPE_BYPASS; | |
309 | smmu->s2crs[i].privcfg = S2CR_PRIVCFG_DEFAULT; | |
310 | smmu->s2crs[i].cbndx = 0xff; | |
311 | } | |
312 | } | |
313 | ||
314 | return 0; | |
315 | } | |
316 | ||
f9081b8f BA |
317 | static void qcom_smmu_write_s2cr(struct arm_smmu_device *smmu, int idx) |
318 | { | |
319 | struct arm_smmu_s2cr *s2cr = smmu->s2crs + idx; | |
320 | struct qcom_smmu *qsmmu = to_qcom_smmu(smmu); | |
321 | u32 cbndx = s2cr->cbndx; | |
322 | u32 type = s2cr->type; | |
323 | u32 reg; | |
324 | ||
325 | if (qsmmu->bypass_quirk) { | |
326 | if (type == S2CR_TYPE_BYPASS) { | |
327 | /* | |
328 | * Firmware with quirky S2CR handling will substitute | |
329 | * BYPASS writes with FAULT, so point the stream to the | |
330 | * reserved context bank and ask for translation on the | |
331 | * stream | |
332 | */ | |
333 | type = S2CR_TYPE_TRANS; | |
334 | cbndx = qsmmu->bypass_cbndx; | |
335 | } else if (type == S2CR_TYPE_FAULT) { | |
336 | /* | |
337 | * Firmware with quirky S2CR handling will ignore FAULT | |
338 | * writes, so trick it to write FAULT by asking for a | |
339 | * BYPASS. | |
340 | */ | |
341 | type = S2CR_TYPE_BYPASS; | |
342 | cbndx = 0xff; | |
343 | } | |
344 | } | |
345 | ||
346 | reg = FIELD_PREP(ARM_SMMU_S2CR_TYPE, type) | | |
347 | FIELD_PREP(ARM_SMMU_S2CR_CBNDX, cbndx) | | |
348 | FIELD_PREP(ARM_SMMU_S2CR_PRIVCFG, s2cr->privcfg); | |
349 | arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_S2CR(idx), reg); | |
350 | } | |
351 | ||
0e764a01 JC |
352 | static int qcom_smmu_def_domain_type(struct device *dev) |
353 | { | |
354 | const struct of_device_id *match = | |
355 | of_match_device(qcom_smmu_client_of_match, dev); | |
356 | ||
357 | return match ? IOMMU_DOMAIN_IDENTITY : 0; | |
358 | } | |
359 | ||
759aaa10 VG |
360 | static int qcom_sdm845_smmu500_reset(struct arm_smmu_device *smmu) |
361 | { | |
362 | int ret; | |
363 | ||
417b76ad DB |
364 | arm_mmu500_reset(smmu); |
365 | ||
759aaa10 VG |
366 | /* |
367 | * To address performance degradation in non-real time clients, | |
368 | * such as USB and UFS, turn off wait-for-safe on sdm845 based boards, | |
369 | * such as MTP and db845, whose firmwares implement secure monitor | |
370 | * call handlers to turn on/off the wait-for-safe logic. | |
371 | */ | |
372 | ret = qcom_scm_qsmmu500_wait_safe_toggle(0); | |
373 | if (ret) | |
374 | dev_warn(smmu->dev, "Failed to turn off SAFE logic\n"); | |
375 | ||
376 | return ret; | |
377 | } | |
378 | ||
b4c6ee51 DB |
379 | static const struct arm_smmu_impl qcom_smmu_v2_impl = { |
380 | .init_context = qcom_smmu_init_context, | |
381 | .cfg_probe = qcom_smmu_cfg_probe, | |
382 | .def_domain_type = qcom_smmu_def_domain_type, | |
383 | .write_s2cr = qcom_smmu_write_s2cr, | |
384 | .tlb_sync = qcom_smmu_tlb_sync, | |
385 | }; | |
386 | ||
387 | static const struct arm_smmu_impl qcom_smmu_500_impl = { | |
ef75702d | 388 | .init_context = qcom_smmu_init_context, |
07a7f2ca | 389 | .cfg_probe = qcom_smmu_cfg_probe, |
0e764a01 | 390 | .def_domain_type = qcom_smmu_def_domain_type, |
417b76ad DB |
391 | .reset = arm_mmu500_reset, |
392 | .write_s2cr = qcom_smmu_write_s2cr, | |
393 | .tlb_sync = qcom_smmu_tlb_sync, | |
394 | }; | |
395 | ||
396 | static const struct arm_smmu_impl sdm845_smmu_500_impl = { | |
397 | .init_context = qcom_smmu_init_context, | |
398 | .cfg_probe = qcom_smmu_cfg_probe, | |
399 | .def_domain_type = qcom_smmu_def_domain_type, | |
400 | .reset = qcom_sdm845_smmu500_reset, | |
f9081b8f | 401 | .write_s2cr = qcom_smmu_write_s2cr, |
b9b721d1 | 402 | .tlb_sync = qcom_smmu_tlb_sync, |
759aaa10 VG |
403 | }; |
404 | ||
b4c6ee51 DB |
405 | static const struct arm_smmu_impl qcom_adreno_smmu_v2_impl = { |
406 | .init_context = qcom_adreno_smmu_init_context, | |
407 | .def_domain_type = qcom_smmu_def_domain_type, | |
408 | .alloc_context_bank = qcom_adreno_smmu_alloc_context_bank, | |
409 | .write_sctlr = qcom_adreno_smmu_write_sctlr, | |
410 | .tlb_sync = qcom_smmu_tlb_sync, | |
411 | }; | |
412 | ||
413 | static const struct arm_smmu_impl qcom_adreno_smmu_500_impl = { | |
5c7469c6 JC |
414 | .init_context = qcom_adreno_smmu_init_context, |
415 | .def_domain_type = qcom_smmu_def_domain_type, | |
417b76ad | 416 | .reset = arm_mmu500_reset, |
5c7469c6 | 417 | .alloc_context_bank = qcom_adreno_smmu_alloc_context_bank, |
bffb2eaf | 418 | .write_sctlr = qcom_adreno_smmu_write_sctlr, |
b9b721d1 | 419 | .tlb_sync = qcom_smmu_tlb_sync, |
5c7469c6 JC |
420 | }; |
421 | ||
422 | static struct arm_smmu_device *qcom_smmu_create(struct arm_smmu_device *smmu, | |
4c1d0ad1 | 423 | const struct qcom_smmu_match_data *data) |
759aaa10 | 424 | { |
30b912a0 | 425 | const struct device_node *np = smmu->dev->of_node; |
4c1d0ad1 | 426 | const struct arm_smmu_impl *impl; |
759aaa10 VG |
427 | struct qcom_smmu *qsmmu; |
428 | ||
4c1d0ad1 DB |
429 | if (!data) |
430 | return ERR_PTR(-EINVAL); | |
431 | ||
30b912a0 DB |
432 | if (np && of_device_is_compatible(np, "qcom,adreno-smmu")) |
433 | impl = data->adreno_impl; | |
434 | else | |
435 | impl = data->impl; | |
436 | ||
4c1d0ad1 DB |
437 | if (!impl) |
438 | return smmu; | |
439 | ||
72b55c96 JS |
440 | /* Check to make sure qcom_scm has finished probing */ |
441 | if (!qcom_scm_is_available()) | |
442 | return ERR_PTR(-EPROBE_DEFER); | |
443 | ||
af9da914 | 444 | qsmmu = devm_krealloc(smmu->dev, smmu, sizeof(*qsmmu), GFP_KERNEL); |
759aaa10 VG |
445 | if (!qsmmu) |
446 | return ERR_PTR(-ENOMEM); | |
447 | ||
5c7469c6 | 448 | qsmmu->smmu.impl = impl; |
4172dda2 | 449 | qsmmu->cfg = data->cfg; |
759aaa10 VG |
450 | |
451 | return &qsmmu->smmu; | |
452 | } | |
5c7469c6 | 453 | |
4172dda2 DB |
454 | /* Implementation Defined Register Space 0 register offsets */ |
455 | static const u32 qcom_smmu_impl0_reg_offset[] = { | |
456 | [QCOM_SMMU_TBU_PWR_STATUS] = 0x2204, | |
457 | [QCOM_SMMU_STATS_SYNC_INV_TBU_ACK] = 0x25dc, | |
458 | [QCOM_SMMU_MMU2QSS_AND_SAFE_WAIT_CNTR] = 0x2670, | |
459 | }; | |
460 | ||
461 | static const struct qcom_smmu_config qcom_smmu_impl0_cfg = { | |
462 | .reg_offset = qcom_smmu_impl0_reg_offset, | |
463 | }; | |
464 | ||
30b912a0 DB |
465 | /* |
466 | * It is not yet possible to use MDP SMMU with the bypass quirk on the msm8996, | |
467 | * there are not enough context banks. | |
468 | */ | |
469 | static const struct qcom_smmu_match_data msm8996_smmu_data = { | |
470 | .impl = NULL, | |
b4c6ee51 | 471 | .adreno_impl = &qcom_adreno_smmu_v2_impl, |
4c1d0ad1 DB |
472 | }; |
473 | ||
b4c6ee51 DB |
474 | static const struct qcom_smmu_match_data qcom_smmu_v2_data = { |
475 | .impl = &qcom_smmu_v2_impl, | |
476 | .adreno_impl = &qcom_adreno_smmu_v2_impl, | |
4c1d0ad1 DB |
477 | }; |
478 | ||
417b76ad DB |
479 | static const struct qcom_smmu_match_data sdm845_smmu_500_data = { |
480 | .impl = &sdm845_smmu_500_impl, | |
481 | /* | |
482 | * No need for adreno impl here. On sdm845 the Adreno SMMU is handled | |
483 | * by the separate sdm845-smmu-v2 device. | |
484 | */ | |
4172dda2 DB |
485 | /* Also no debug configuration. */ |
486 | }; | |
487 | ||
488 | static const struct qcom_smmu_match_data qcom_smmu_500_impl0_data = { | |
b4c6ee51 DB |
489 | .impl = &qcom_smmu_500_impl, |
490 | .adreno_impl = &qcom_adreno_smmu_500_impl, | |
4172dda2 | 491 | .cfg = &qcom_smmu_impl0_cfg, |
417b76ad DB |
492 | }; |
493 | ||
80b71080 DB |
494 | /* |
495 | * Do not add any more qcom,SOC-smmu-500 entries to this list, unless they need | |
496 | * special handling and can not be covered by the qcom,smmu-500 entry. | |
497 | */ | |
00597f9f | 498 | static const struct of_device_id __maybe_unused qcom_smmu_impl_of_match[] = { |
30b912a0 | 499 | { .compatible = "qcom,msm8996-smmu-v2", .data = &msm8996_smmu_data }, |
b4c6ee51 | 500 | { .compatible = "qcom,msm8998-smmu-v2", .data = &qcom_smmu_v2_data }, |
4172dda2 DB |
501 | { .compatible = "qcom,qcm2290-smmu-500", .data = &qcom_smmu_500_impl0_data }, |
502 | { .compatible = "qcom,qdu1000-smmu-500", .data = &qcom_smmu_500_impl0_data }, | |
503 | { .compatible = "qcom,sc7180-smmu-500", .data = &qcom_smmu_500_impl0_data }, | |
504 | { .compatible = "qcom,sc7280-smmu-500", .data = &qcom_smmu_500_impl0_data }, | |
505 | { .compatible = "qcom,sc8180x-smmu-500", .data = &qcom_smmu_500_impl0_data }, | |
506 | { .compatible = "qcom,sc8280xp-smmu-500", .data = &qcom_smmu_500_impl0_data }, | |
b4c6ee51 DB |
507 | { .compatible = "qcom,sdm630-smmu-v2", .data = &qcom_smmu_v2_data }, |
508 | { .compatible = "qcom,sdm845-smmu-v2", .data = &qcom_smmu_v2_data }, | |
417b76ad | 509 | { .compatible = "qcom,sdm845-smmu-500", .data = &sdm845_smmu_500_data }, |
4172dda2 DB |
510 | { .compatible = "qcom,sm6115-smmu-500", .data = &qcom_smmu_500_impl0_data}, |
511 | { .compatible = "qcom,sm6125-smmu-500", .data = &qcom_smmu_500_impl0_data }, | |
3811a728 | 512 | { .compatible = "qcom,sm6350-smmu-v2", .data = &qcom_smmu_v2_data }, |
4172dda2 DB |
513 | { .compatible = "qcom,sm6350-smmu-500", .data = &qcom_smmu_500_impl0_data }, |
514 | { .compatible = "qcom,sm6375-smmu-500", .data = &qcom_smmu_500_impl0_data }, | |
515 | { .compatible = "qcom,sm8150-smmu-500", .data = &qcom_smmu_500_impl0_data }, | |
516 | { .compatible = "qcom,sm8250-smmu-500", .data = &qcom_smmu_500_impl0_data }, | |
517 | { .compatible = "qcom,sm8350-smmu-500", .data = &qcom_smmu_500_impl0_data }, | |
518 | { .compatible = "qcom,sm8450-smmu-500", .data = &qcom_smmu_500_impl0_data }, | |
80b71080 | 519 | { .compatible = "qcom,smmu-500", .data = &qcom_smmu_500_impl0_data }, |
00597f9f SPR |
520 | { } |
521 | }; | |
522 | ||
22c2d718 | 523 | #ifdef CONFIG_ACPI |
a51627c5 SG |
524 | static struct acpi_platform_list qcom_acpi_platlist[] = { |
525 | { "LENOVO", "CB-01 ", 0x8180, ACPI_SIG_IORT, equal, "QCOM SMMU" }, | |
526 | { "QCOM ", "QCOMEDK2", 0x8180, ACPI_SIG_IORT, equal, "QCOM SMMU" }, | |
527 | { } | |
528 | }; | |
22c2d718 | 529 | #endif |
a51627c5 | 530 | |
5c7469c6 JC |
531 | struct arm_smmu_device *qcom_smmu_impl_init(struct arm_smmu_device *smmu) |
532 | { | |
00597f9f | 533 | const struct device_node *np = smmu->dev->of_node; |
4c1d0ad1 | 534 | const struct of_device_id *match; |
5c7469c6 | 535 | |
22c2d718 | 536 | #ifdef CONFIG_ACPI |
a51627c5 SG |
537 | if (np == NULL) { |
538 | /* Match platform for ACPI boot */ | |
539 | if (acpi_match_platform_list(qcom_acpi_platlist) >= 0) | |
4172dda2 | 540 | return qcom_smmu_create(smmu, &qcom_smmu_500_impl0_data); |
a51627c5 | 541 | } |
22c2d718 | 542 | #endif |
00597f9f | 543 | |
4c1d0ad1 DB |
544 | match = of_match_node(qcom_smmu_impl_of_match, np); |
545 | if (match) | |
546 | return qcom_smmu_create(smmu, match->data); | |
ab9a77a1 | 547 | |
00597f9f | 548 | return smmu; |
5c7469c6 | 549 | } |