1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2021 Mark Kettenis <kettenis@openbsd.org>
4  */
5 
6 #include <common.h>
7 #include <cpu_func.h>
8 #include <dm.h>
9 #include <iommu.h>
10 #include <lmb.h>
11 #include <memalign.h>
12 #include <asm/io.h>
13 
14 #define DART_PARAMS2		0x0004
15 #define  DART_PARAMS2_BYPASS_SUPPORT	BIT(0)
16 
17 #define DART_T8020_TLB_CMD		0x0020
18 #define  DART_T8020_TLB_CMD_FLUSH		BIT(20)
19 #define  DART_T8020_TLB_CMD_BUSY		BIT(2)
20 #define DART_T8020_TLB_SIDMASK		0x0034
21 #define DART_T8020_ERROR		0x0040
22 #define DART_T8020_ERROR_ADDR_LO	0x0050
23 #define DART_T8020_ERROR_ADDR_HI	0x0054
24 #define DART_T8020_CONFIG		0x0060
25 #define  DART_T8020_CONFIG_LOCK			BIT(15)
26 #define DART_T8020_SID_ENABLE		0x00fc
27 #define DART_T8020_TCR_BASE		0x0100
28 #define  DART_T8020_TCR_TRANSLATE_ENABLE	BIT(7)
29 #define  DART_T8020_TCR_BYPASS_DART		BIT(8)
30 #define  DART_T8020_TCR_BYPASS_DAPF		BIT(12)
31 #define DART_T8020_TTBR_BASE		0x0200
32 #define  DART_T8020_TTBR_VALID			BIT(31)
33 
34 #define DART_T8110_PARAMS4		0x000c
35 #define  DART_T8110_PARAMS4_NSID_MASK		(0x1ff << 0)
36 #define DART_T8110_TLB_CMD		0x0080
37 #define  DART_T8110_TLB_CMD_BUSY		BIT(31)
38 #define  DART_T8110_TLB_CMD_FLUSH_ALL		BIT(8)
39 #define DART_T8110_ERROR		0x0100
40 #define DART_T8110_ERROR_MASK		0x0104
41 #define DART_T8110_ERROR_ADDR_LO	0x0170
42 #define DART_T8110_ERROR_ADDR_HI	0x0174
43 #define DART_T8110_PROTECT		0x0200
44 #define  DART_T8110_PROTECT_TTBR_TCR		BIT(0)
45 #define DART_T8110_SID_ENABLE_BASE	0x0c00
46 #define DART_T8110_TCR_BASE		0x1000
47 #define  DART_T8110_TCR_BYPASS_DAPF		BIT(2)
48 #define  DART_T8110_TCR_BYPASS_DART		BIT(1)
49 #define  DART_T8110_TCR_TRANSLATE_ENABLE	BIT(0)
50 #define DART_T8110_TTBR_BASE		0x1400
51 #define  DART_T8110_TTBR_VALID			BIT(0)
52 
53 #define DART_SID_ENABLE(priv, idx) \
54 	((priv)->sid_enable_base + 4 * (idx))
55 #define DART_TCR(priv, sid)	((priv)->tcr_base + 4 * (sid))
56 #define DART_TTBR(priv, sid, idx)	\
57 	((priv)->ttbr_base + 4 * (priv)->nttbr * (sid) + 4 * (idx))
58 #define  DART_TTBR_SHIFT	12
59 
60 #define DART_ALL_STREAMS(priv)	((1U << (priv)->nsid) - 1)
61 
62 #define DART_PAGE_SIZE		SZ_16K
63 #define DART_PAGE_MASK		(DART_PAGE_SIZE - 1)
64 
65 #define DART_L1_TABLE		0x3
66 #define DART_L2_INVAL		0
67 #define DART_L2_VALID		BIT(0)
68 #define DART_L2_FULL_PAGE	BIT(1)
69 #define DART_L2_START(addr)	((((addr) & DART_PAGE_MASK) >> 2) << 52)
70 #define DART_L2_END(addr)	((((addr) & DART_PAGE_MASK) >> 2) << 40)
71 
72 struct apple_dart_priv {
73 	void *base;
74 	struct lmb lmb;
75 	u64 *l1, *l2;
76 	int bypass, shift;
77 
78 	dma_addr_t dvabase;
79 	dma_addr_t dvaend;
80 
81 	int nsid;
82 	int nttbr;
83 	int sid_enable_base;
84 	int tcr_base;
85 	u32 tcr_translate_enable;
86 	u32 tcr_bypass;
87 	int ttbr_base;
88 	u32 ttbr_valid;
89 	void (*flush_tlb)(struct apple_dart_priv *priv);
90 };
91 
apple_dart_t8020_flush_tlb(struct apple_dart_priv * priv)92 static void apple_dart_t8020_flush_tlb(struct apple_dart_priv *priv)
93 {
94 	dsb();
95 
96 	writel(DART_ALL_STREAMS(priv), priv->base + DART_T8020_TLB_SIDMASK);
97 	writel(DART_T8020_TLB_CMD_FLUSH, priv->base + DART_T8020_TLB_CMD);
98 	while (readl(priv->base + DART_T8020_TLB_CMD) &
99 	       DART_T8020_TLB_CMD_BUSY)
100 		continue;
101 }
102 
apple_dart_t8110_flush_tlb(struct apple_dart_priv * priv)103 static void apple_dart_t8110_flush_tlb(struct apple_dart_priv *priv)
104 {
105 	dsb();
106 
107 	writel(DART_T8110_TLB_CMD_FLUSH_ALL,
108 	       priv->base + DART_T8110_TLB_CMD_FLUSH_ALL);
109 	while (readl(priv->base + DART_T8110_TLB_CMD) &
110 	       DART_T8110_TLB_CMD_BUSY)
111 		continue;
112 }
113 
apple_dart_map(struct udevice * dev,void * addr,size_t size)114 static dma_addr_t apple_dart_map(struct udevice *dev, void *addr, size_t size)
115 {
116 	struct apple_dart_priv *priv = dev_get_priv(dev);
117 	phys_addr_t paddr, dva;
118 	phys_size_t psize, off;
119 	int i, idx;
120 
121 	if (priv->bypass)
122 		return (phys_addr_t)addr;
123 
124 	paddr = ALIGN_DOWN((phys_addr_t)addr, DART_PAGE_SIZE);
125 	off = (phys_addr_t)addr - paddr;
126 	psize = ALIGN(size + off, DART_PAGE_SIZE);
127 
128 	dva = lmb_alloc(&priv->lmb, psize, DART_PAGE_SIZE);
129 
130 	idx = dva / DART_PAGE_SIZE;
131 	for (i = 0; i < psize / DART_PAGE_SIZE; i++) {
132 		priv->l2[idx + i] = (paddr  >> priv->shift) | DART_L2_VALID |
133 			DART_L2_START(0LL) | DART_L2_END(~0LL);
134 		paddr += DART_PAGE_SIZE;
135 	}
136 	flush_dcache_range((unsigned long)&priv->l2[idx],
137 			   (unsigned long)&priv->l2[idx + i]);
138 	priv->flush_tlb(priv);
139 
140 	return dva + off;
141 }
142 
apple_dart_unmap(struct udevice * dev,dma_addr_t addr,size_t size)143 static void apple_dart_unmap(struct udevice *dev, dma_addr_t addr, size_t size)
144 {
145 	struct apple_dart_priv *priv = dev_get_priv(dev);
146 	phys_addr_t dva;
147 	phys_size_t psize;
148 	int i, idx;
149 
150 	if (priv->bypass)
151 		return;
152 
153 	dva = ALIGN_DOWN(addr, DART_PAGE_SIZE);
154 	psize = size + (addr - dva);
155 	psize = ALIGN(psize, DART_PAGE_SIZE);
156 
157 	idx = dva / DART_PAGE_SIZE;
158 	for (i = 0; i < psize / DART_PAGE_SIZE; i++)
159 		priv->l2[idx + i] = DART_L2_INVAL;
160 	flush_dcache_range((unsigned long)&priv->l2[idx],
161 			   (unsigned long)&priv->l2[idx + i]);
162 	priv->flush_tlb(priv);
163 
164 	lmb_free(&priv->lmb, dva, psize);
165 }
166 
167 static struct iommu_ops apple_dart_ops = {
168 	.map = apple_dart_map,
169 	.unmap = apple_dart_unmap,
170 };
171 
apple_dart_probe(struct udevice * dev)172 static int apple_dart_probe(struct udevice *dev)
173 {
174 	struct apple_dart_priv *priv = dev_get_priv(dev);
175 	dma_addr_t addr;
176 	phys_addr_t l2;
177 	int ntte, nl1, nl2;
178 	int sid, i;
179 	u32 params2, params4;
180 
181 	priv->base = dev_read_addr_ptr(dev);
182 	if (!priv->base)
183 		return -EINVAL;
184 
185 	if (device_is_compatible(dev, "apple,t8110-dart")) {
186 		params4 = readl(priv->base + DART_T8110_PARAMS4);
187 		priv->nsid = params4 & DART_T8110_PARAMS4_NSID_MASK;
188 		priv->nttbr = 1;
189 		priv->sid_enable_base = DART_T8110_SID_ENABLE_BASE;
190 		priv->tcr_base = DART_T8110_TCR_BASE;
191 		priv->tcr_translate_enable = DART_T8110_TCR_TRANSLATE_ENABLE;
192 		priv->tcr_bypass =
193 		    DART_T8110_TCR_BYPASS_DAPF | DART_T8110_TCR_BYPASS_DART;
194 		priv->ttbr_base = DART_T8110_TTBR_BASE;
195 		priv->ttbr_valid = DART_T8110_TTBR_VALID;
196 		priv->flush_tlb = apple_dart_t8110_flush_tlb;
197 	} else {
198 		priv->nsid = 16;
199 		priv->nttbr = 4;
200 		priv->sid_enable_base = DART_T8020_SID_ENABLE;
201 		priv->tcr_base = DART_T8020_TCR_BASE;
202 		priv->tcr_translate_enable = DART_T8020_TCR_TRANSLATE_ENABLE;
203 		priv->tcr_bypass =
204 		    DART_T8020_TCR_BYPASS_DAPF | DART_T8020_TCR_BYPASS_DART;
205 		priv->ttbr_base = DART_T8020_TTBR_BASE;
206 		priv->ttbr_valid = DART_T8020_TTBR_VALID;
207 		priv->flush_tlb = apple_dart_t8020_flush_tlb;
208 	}
209 
210 	if (device_is_compatible(dev, "apple,t6000-dart") ||
211 	    device_is_compatible(dev, "apple,t8110-dart"))
212 		priv->shift = 4;
213 
214 	priv->dvabase = DART_PAGE_SIZE;
215 	priv->dvaend = SZ_4G - DART_PAGE_SIZE;
216 
217 	lmb_init(&priv->lmb);
218 	lmb_add(&priv->lmb, priv->dvabase, priv->dvaend - priv->dvabase);
219 
220 	/* Disable translations. */
221 	for (sid = 0; sid < priv->nsid; sid++)
222 		writel(0, priv->base + DART_TCR(priv, sid));
223 
224 	/* Remove page tables. */
225 	for (sid = 0; sid < priv->nsid; sid++) {
226 		for (i = 0; i < priv->nttbr; i++)
227 			writel(0, priv->base + DART_TTBR(priv, sid, i));
228 	}
229 	priv->flush_tlb(priv);
230 
231 	params2 = readl(priv->base + DART_PARAMS2);
232 	if (params2 & DART_PARAMS2_BYPASS_SUPPORT) {
233 		for (sid = 0; sid < priv->nsid; sid++) {
234 			writel(priv->tcr_bypass,
235 			       priv->base + DART_TCR(priv, sid));
236 		}
237 		priv->bypass = 1;
238 		return 0;
239 	}
240 
241 	ntte = DIV_ROUND_UP(priv->dvaend, DART_PAGE_SIZE);
242 	nl2 = DIV_ROUND_UP(ntte, DART_PAGE_SIZE / sizeof(u64));
243 	nl1 = DIV_ROUND_UP(nl2, DART_PAGE_SIZE / sizeof(u64));
244 
245 	priv->l2 = memalign(DART_PAGE_SIZE, nl2 * DART_PAGE_SIZE);
246 	memset(priv->l2, 0, nl2 * DART_PAGE_SIZE);
247 	flush_dcache_range((unsigned long)priv->l2,
248 			   (unsigned long)priv->l2 + nl2 * DART_PAGE_SIZE);
249 
250 	priv->l1 = memalign(DART_PAGE_SIZE, nl1 * DART_PAGE_SIZE);
251 	memset(priv->l1, 0, nl1 * DART_PAGE_SIZE);
252 	l2 = (phys_addr_t)priv->l2;
253 	for (i = 0; i < nl2; i++) {
254 		priv->l1[i] = (l2 >> priv->shift) | DART_L1_TABLE;
255 		l2 += DART_PAGE_SIZE;
256 	}
257 	flush_dcache_range((unsigned long)priv->l1,
258 			   (unsigned long)priv->l1 + nl1 * DART_PAGE_SIZE);
259 
260 	/* Install page tables. */
261 	for (sid = 0; sid < priv->nsid; sid++) {
262 		addr = (phys_addr_t)priv->l1;
263 		for (i = 0; i < nl1; i++) {
264 			writel(addr >> DART_TTBR_SHIFT | priv->ttbr_valid,
265 			       priv->base + DART_TTBR(priv, sid, i));
266 			addr += DART_PAGE_SIZE;
267 		}
268 	}
269 	priv->flush_tlb(priv);
270 
271 	/* Enable all streams. */
272 	for (i = 0; i < priv->nsid / 32; i++)
273 		writel(~0, priv->base + DART_SID_ENABLE(priv, i));
274 
275 	/* Enable translations. */
276 	for (sid = 0; sid < priv->nsid; sid++) {
277 		writel(priv->tcr_translate_enable,
278 		       priv->base + DART_TCR(priv, sid));
279 	}
280 
281 	return 0;
282 }
283 
apple_dart_remove(struct udevice * dev)284 static int apple_dart_remove(struct udevice *dev)
285 {
286 	struct apple_dart_priv *priv = dev_get_priv(dev);
287 	int sid, i;
288 
289 	/* Disable translations. */
290 	for (sid = 0; sid < priv->nsid; sid++)
291 		writel(0, priv->base + DART_TCR(priv, sid));
292 
293 	/* Remove page tables. */
294 	for (sid = 0; sid < priv->nsid; sid++) {
295 		for (i = 0; i < priv->nttbr; i++)
296 			writel(0, priv->base + DART_TTBR(priv, sid, i));
297 	}
298 	priv->flush_tlb(priv);
299 
300 	return 0;
301 }
302 
303 static const struct udevice_id apple_dart_ids[] = {
304 	{ .compatible = "apple,t8103-dart" },
305 	{ .compatible = "apple,t6000-dart" },
306 	{ .compatible = "apple,t8110-dart" },
307 	{ /* sentinel */ }
308 };
309 
310 U_BOOT_DRIVER(apple_dart) = {
311 	.name = "apple_dart",
312 	.id = UCLASS_IOMMU,
313 	.of_match = apple_dart_ids,
314 	.priv_auto = sizeof(struct apple_dart_priv),
315 	.ops = &apple_dart_ops,
316 	.probe = apple_dart_probe,
317 	.remove = apple_dart_remove,
318 	.flags	= DM_FLAG_OS_PREPARE
319 };
320