blob: 2e51ce4f242e44e670c9ba203db575833015e6bd [file] [log] [blame]
Caleb Connollya95de052023-12-11 18:41:42 +00001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2023 Linaro Ltd.
4 * Basic ARM SMMU-500 driver, assuming a pre-initialised SMMU and only IDENTITY domains
5 * this driver only implements the bare minimum to configure stream mappings for periphals
6 * used by u-boot on platforms where the SMMU can't be disabled.
7 */
8
9#include <log.h>
10#include <cpu_func.h>
11#include <dm.h>
12#include <iommu.h>
13#include <linux/bitfield.h>
Christian Marangi57c2ec82025-06-07 23:11:18 +020014#include <linux/bug.h>
Caleb Connollya95de052023-12-11 18:41:42 +000015#include <linux/list.h>
16#include <linux/err.h>
17#include <lmb.h>
18#include <memalign.h>
19#include <asm/io.h>
20
21#define ARM_SMMU_GR0 0
22#define ARM_SMMU_GR1 1
23
24#define ARM_SMMU_GR0_ID0 0x20
25#define ARM_SMMU_ID0_NUMSMRG GENMASK(7, 0) /* Number of stream mapping groups */
26#define ARM_SMMU_GR0_ID1 0x24
27#define ARM_SMMU_ID1_PAGESIZE \
28 BIT(31) /* Page shift is 16 bits when set, otherwise 23 */
29#define ARM_SMMU_ID1_NUMPAGENDXB \
30 GENMASK(30, 28) /* Number of pages before context banks */
31#define ARM_SMMU_ID1_NUMCB GENMASK(7, 0) /* Number of context banks supported */
32
33#define ARM_SMMU_GR1_CBAR(n) (0x0 + ((n) << 2))
34#define ARM_SMMU_CBAR_TYPE GENMASK(17, 16)
35#define ARM_SMMU_CBAR_VMID GENMASK(7, 0)
36enum arm_smmu_cbar_type {
37 CBAR_TYPE_S2_TRANS,
38 CBAR_TYPE_S1_TRANS_S2_BYPASS,
39 CBAR_TYPE_S1_TRANS_S2_FAULT,
40 CBAR_TYPE_S1_TRANS_S2_TRANS,
41};
42
43#define ARM_SMMU_GR1_CBA2R(n) (0x800 + ((n) << 2))
44#define ARM_SMMU_CBA2R_VA64 BIT(0)
45
46/* Per-CB system control register */
47#define ARM_SMMU_CB_SCTLR 0x0
48#define ARM_SMMU_SCTLR_CFCFG BIT(7) /* Stall on context fault */
49#define ARM_SMMU_SCTLR_CFIE BIT(6) /* Context fault interrupt enable */
50#define ARM_SMMU_SCTLR_CFRE BIT(5) /* Abort on context fault */
51
52/* Translation Table Base, holds address of translation table in memory to be used
53 * for this context bank. Or 0 for bypass
54 */
55#define ARM_SMMU_CB_TTBR0 0x20
56#define ARM_SMMU_CB_TTBR1 0x28
57/* Translation Control Register, configured TTBR/TLB behaviour (0 for bypass) */
58#define ARM_SMMU_CB_TCR 0x30
59/* Memory Attribute Indirection, also 0 for bypass */
60#define ARM_SMMU_CB_S1_MAIR0 0x38
61#define ARM_SMMU_CB_S1_MAIR1 0x3c
62
63#define ARM_SMMU_GR0_SMR(n) (0x800 + ((n) << 2))
64#define ARM_SMMU_SMR_VALID BIT(31)
65#define ARM_SMMU_SMR_MASK GENMASK(31, 16) // Always 0 for now??
66#define ARM_SMMU_SMR_ID GENMASK(15, 0)
67
68#define ARM_SMMU_GR0_S2CR(n) (0xc00 + ((n) << 2))
69#define ARM_SMMU_S2CR_PRIVCFG GENMASK(25, 24)
70
71enum arm_smmu_s2cr_privcfg {
72 S2CR_PRIVCFG_DEFAULT,
73 S2CR_PRIVCFG_DIPAN,
74 S2CR_PRIVCFG_UNPRIV,
75 S2CR_PRIVCFG_PRIV,
76};
77
78#define ARM_SMMU_S2CR_TYPE GENMASK(17, 16)
79
80enum arm_smmu_s2cr_type {
81 S2CR_TYPE_TRANS,
82 S2CR_TYPE_BYPASS,
83 S2CR_TYPE_FAULT,
84};
85
86#define ARM_SMMU_S2CR_EXIDVALID BIT(10)
87#define ARM_SMMU_S2CR_CBNDX GENMASK(7, 0)
88
89#define VMID_UNUSED 0xff
90
91struct qcom_smmu_priv {
92 phys_addr_t base;
93 struct list_head devices;
94 struct udevice *dev;
Caleb Connollybd59d5d2024-11-13 06:09:24 +010095 /* SMMU is not needed when running in EL2 */
96 bool disable;
Caleb Connollya95de052023-12-11 18:41:42 +000097
98 /* Read-once config */
99 int num_cb;
100 int num_smr;
101 u32 pgshift;
102 u32 cb_pg_offset;
103};
104
105struct mmu_dev {
106 struct list_head li;
107 struct udevice *dev;
108 u16 sid;
109 u16 cbx;
110 u16 smr;
111};
112
113#define page_addr(priv, page) ((priv)->base + ((page) << (priv)->pgshift))
114
115#define smmu_readl(priv, page, offset) readl(page_addr(priv, page) + offset)
116#define gr0_readl(priv, offset) smmu_readl(priv, ARM_SMMU_GR0, offset)
117#define gr1_readl(priv, offset) smmu_readl(priv, ARM_SMMU_GR1, offset)
118#define cbx_readl(priv, cbx, offset) \
119 smmu_readl(priv, (priv->cb_pg_offset) + cbx, offset)
120
121#define smmu_writel(priv, page, offset, value) \
122 writel((value), page_addr(priv, page) + offset)
123#define gr0_writel(priv, offset, value) \
124 smmu_writel(priv, ARM_SMMU_GR0, offset, (value))
125#define gr1_writel(priv, offset, value) \
126 smmu_writel(priv, ARM_SMMU_GR1, offset, (value))
127#define cbx_writel(priv, cbx, offset, value) \
128 smmu_writel(priv, (priv->cb_pg_offset) + cbx, offset, value)
129
130#define gr1_setbits(priv, offset, value) \
131 gr1_writel(priv, offset, gr1_readl(priv, offset) | (value))
132
133static int get_stream_id(struct udevice *dev)
134{
135 ofnode node = dev_ofnode(dev);
136 struct ofnode_phandle_args args;
137 int count = ofnode_parse_phandle_with_args(node, "iommus",
138 "#iommu-cells", 0, 0, &args);
139
Caleb Connolly61c3cff2024-11-13 06:00:56 +0100140 if (count < 0) {
Caleb Connollya95de052023-12-11 18:41:42 +0000141 printf("Error: %s: iommus property not found or wrong number of cells\n",
142 __func__);
143 return -EINVAL;
144 }
145
146 return args.args[0]; // Some mask from bit 16 onward?
147}
148
149static struct mmu_dev *alloc_dev(struct udevice *dev)
150{
151 struct qcom_smmu_priv *priv = dev_get_priv(dev->iommu);
152 struct mmu_dev *mmu_dev;
153 int sid;
154
155 sid = get_stream_id(dev);
156 debug("%s %s has SID %#x\n", dev->iommu->name, dev->name, sid);
157 if (sid < 0 || sid > 0xffff) {
158 printf("\tSMMU: Invalid stream ID for %s\n", dev->name);
159 return ERR_PTR(-EINVAL);
160 }
161
162 /* We only support a single SID per device for now */
163 list_for_each_entry(mmu_dev, &priv->devices, li) {
164 if (mmu_dev->sid == sid)
165 return ERR_PTR(-EEXIST);
166 }
167
168 mmu_dev = calloc(sizeof(*mmu_dev), 1);
169 if (!mmu_dev)
170 return ERR_PTR(-ENOMEM);
171
172 mmu_dev->dev = dev;
173 mmu_dev->sid = sid;
174
175 list_add_tail(&mmu_dev->li, &priv->devices);
176
177 return mmu_dev;
178}
179
180/* Find and init the first free context bank */
181static int alloc_cb(struct qcom_smmu_priv *priv)
182{
183 u32 cbar, type, vmid, val;
184
185 for (int i = 0; i < priv->num_cb; i++) {
186 cbar = gr1_readl(priv, ARM_SMMU_GR1_CBAR(i));
187 type = FIELD_GET(ARM_SMMU_CBAR_TYPE, cbar);
188 vmid = FIELD_GET(ARM_SMMU_CBAR_VMID, cbar);
189
190 /* Check that the context bank is available. We haven't reset the SMMU so
191 * we just make a best guess.
192 */
193 if (type != CBAR_TYPE_S2_TRANS &&
194 (type != CBAR_TYPE_S1_TRANS_S2_BYPASS ||
195 vmid != VMID_UNUSED))
196 continue;
197
198 debug("%s: Found free context bank %d (cbar %#x)\n",
199 priv->dev->name, i, cbar);
200 type = CBAR_TYPE_S1_TRANS_S2_BYPASS;
201 vmid = 0;
202 cbar &= ~ARM_SMMU_CBAR_TYPE & ~ARM_SMMU_CBAR_VMID;
203 cbar |= FIELD_PREP(ARM_SMMU_CBAR_TYPE, type) |
204 FIELD_PREP(ARM_SMMU_CBAR_VMID, vmid);
205 gr1_writel(priv, ARM_SMMU_GR1_CBAR(i), cbar);
206
207 val = IS_ENABLED(CONFIG_ARM64) == 1 ? ARM_SMMU_CBA2R_VA64 : 0;
208 gr1_setbits(priv, ARM_SMMU_GR1_CBA2R(i), val);
209 return i;
210 }
211
212 return -1;
213}
214
215/* Search for a context bank that is already configured for this stream
216 * returns the context bank index or -ENOENT
217 */
218static int find_smr(struct qcom_smmu_priv *priv, u16 stream_id)
219{
220 u32 val;
221 int i;
222
223 for (i = 0; i < priv->num_smr; i++) {
224 val = gr0_readl(priv, ARM_SMMU_GR0_SMR(i));
225 if (!(val & ARM_SMMU_SMR_VALID) ||
226 FIELD_GET(ARM_SMMU_SMR_ID, val) != stream_id)
227 continue;
228
229 return i;
230 }
231
232 return -ENOENT;
233}
234
235static int configure_smr_s2cr(struct qcom_smmu_priv *priv, struct mmu_dev *mdev)
236{
237 u32 val;
238 int i;
239
240 for (i = 0; i < priv->num_smr; i++) {
241 /* Configure SMR */
242 val = gr0_readl(priv, ARM_SMMU_GR0_SMR(i));
243 if (val & ARM_SMMU_SMR_VALID)
244 continue;
245
246 val = mdev->sid | ARM_SMMU_SMR_VALID;
247 gr0_writel(priv, ARM_SMMU_GR0_SMR(i), val);
248
249 /*
250 * WARNING: Don't change this to use S2CR_TYPE_BYPASS!
251 * Some Qualcomm boards have angry hypervisor firmware
252 * that converts S2CR type BYPASS to type FAULT on write.
253 * We don't use virtual addressing for these boards in
254 * u-boot so we can get away with using S2CR_TYPE_TRANS
255 * instead
256 */
257 val = FIELD_PREP(ARM_SMMU_S2CR_TYPE, S2CR_TYPE_TRANS) |
258 FIELD_PREP(ARM_SMMU_S2CR_CBNDX, mdev->cbx);
259 gr0_writel(priv, ARM_SMMU_GR0_S2CR(i), val);
260
261 mdev->smr = i;
262 break;
263 }
264
265 /* Make sure our writes went through */
266 mb();
267
268 return 0;
269}
270
271static int qcom_smmu_connect(struct udevice *dev)
272{
273 struct mmu_dev *mdev;
274 struct qcom_smmu_priv *priv;
275 int ret;
276
277 debug("%s: %s -> %s\n", __func__, dev->name, dev->iommu->name);
278
279 priv = dev_get_priv(dev->iommu);
280 if (WARN_ON(!priv))
281 return -EINVAL;
282
Caleb Connollybd59d5d2024-11-13 06:09:24 +0100283 if (priv->disable)
284 return 0;
285
Caleb Connollya95de052023-12-11 18:41:42 +0000286 mdev = alloc_dev(dev);
287 if (IS_ERR(mdev) && PTR_ERR(mdev) != -EEXIST) {
288 printf("%s: %s Couldn't create mmu context\n", __func__,
289 dev->name);
290 return PTR_ERR(mdev);
291 } else if (IS_ERR(mdev)) { // -EEXIST
292 return 0;
293 }
294
295 if (find_smr(priv, mdev->sid) >= 0) {
296 debug("Found existing context bank for %s, skipping init\n",
297 dev->name);
298 return 0;
299 }
300
301 ret = alloc_cb(priv);
302 if (ret < 0 || ret > 0xff) {
303 printf("Error: %s: failed to allocate context bank for %s\n",
304 __func__, dev->name);
305 return 0;
306 }
307 mdev->cbx = ret;
308
309 /* Configure context bank registers */
310 cbx_writel(priv, mdev->cbx, ARM_SMMU_CB_TTBR0, 0x0);
311 cbx_writel(priv, mdev->cbx, ARM_SMMU_CB_TTBR1, 0x0);
312 cbx_writel(priv, mdev->cbx, ARM_SMMU_CB_S1_MAIR0, 0x0);
313 cbx_writel(priv, mdev->cbx, ARM_SMMU_CB_S1_MAIR1, 0x0);
314 cbx_writel(priv, mdev->cbx, ARM_SMMU_CB_SCTLR,
315 ARM_SMMU_SCTLR_CFIE | ARM_SMMU_SCTLR_CFRE |
316 ARM_SMMU_SCTLR_CFCFG);
317 cbx_writel(priv, mdev->cbx, ARM_SMMU_CB_TCR, 0x0);
318
319 /* Ensure that our writes went through */
320 mb();
321
322 configure_smr_s2cr(priv, mdev);
323
324 return 0;
325}
326
327#ifdef DEBUG
Caleb Connollyf33bbd32024-03-20 14:30:51 +0000328static inline void dump_boot_mappings(struct qcom_smmu_priv *priv)
Caleb Connollya95de052023-12-11 18:41:42 +0000329{
330 u32 val;
331 int i;
332
333 debug(" SMMU dump boot mappings:\n");
334 for (i = 0; i < priv->num_smr; i++) {
335 val = gr0_readl(priv, ARM_SMMU_GR0_SMR(i));
336 if (val & ARM_SMMU_SMR_VALID)
337 debug("\tSMR %3d: SID: %#lx\n", i,
338 FIELD_GET(ARM_SMMU_SMR_ID, val));
339 }
340}
341#else
342#define dump_boot_mappings(priv) \
343 do { \
344 } while (0)
345#endif
346
347static int qcom_smmu_probe(struct udevice *dev)
348{
349 struct qcom_smmu_priv *priv;
350 u32 val;
351
352 priv = dev_get_priv(dev);
353 priv->dev = dev;
354 priv->base = dev_read_addr(dev);
355 INIT_LIST_HEAD(&priv->devices);
356
Caleb Connollybd59d5d2024-11-13 06:09:24 +0100357 priv->disable = current_el() > 1;
358
Caleb Connollya95de052023-12-11 18:41:42 +0000359 /* Read SMMU config */
360 val = gr0_readl(priv, ARM_SMMU_GR0_ID0);
361 priv->num_smr = FIELD_GET(ARM_SMMU_ID0_NUMSMRG, val);
362
363 val = gr0_readl(priv, ARM_SMMU_GR0_ID1);
364 priv->num_cb = FIELD_GET(ARM_SMMU_ID1_NUMCB, val);
365 priv->pgshift = FIELD_GET(ARM_SMMU_ID1_PAGESIZE, val) ? 16 : 12;
366 priv->cb_pg_offset = 1
367 << (FIELD_GET(ARM_SMMU_ID1_NUMPAGENDXB, val) + 1);
368
369 dump_boot_mappings(priv);
370
371 return 0;
372}
373
374static int qcom_smmu_remove(struct udevice *dev)
375{
376 (void)dev;
377 /*
378 * We should probably try and de-configure things here,
379 * however I'm yet to find a way to do it without crashing
380 * and it seems like Linux doesn't care at all anyway.
381 */
382
383 return 0;
384}
385
386static struct iommu_ops qcom_smmu_ops = {
387 .connect = qcom_smmu_connect,
388};
389
390static const struct udevice_id qcom_smmu500_ids[] = {
391 { .compatible = "qcom,sdm845-smmu-500" },
Caleb Connollyb59ba462024-08-21 15:41:49 +0200392 { .compatible = "qcom,sc7280-smmu-500" },
Caleb Connollyfc133192024-04-18 18:25:46 +0100393 { .compatible = "qcom,smmu-500", },
Caleb Connollya95de052023-12-11 18:41:42 +0000394 { /* sentinel */ }
395};
396
397U_BOOT_DRIVER(qcom_smmu500) = {
398 .name = "qcom_smmu500",
399 .id = UCLASS_IOMMU,
400 .of_match = qcom_smmu500_ids,
401 .priv_auto = sizeof(struct qcom_smmu_priv),
402 .ops = &qcom_smmu_ops,
403 .probe = qcom_smmu_probe,
404 .remove = qcom_smmu_remove,
405 .flags = DM_FLAG_OS_PREPARE,
406};