1*b5511178Sjmcneill /* $NetBSD: apple_dart.c,v 1.5 2023/02/24 11:19:15 jmcneill Exp $ */
2f6a78933Sskrll /* $OpenBSD: apldart.c,v 1.10 2022/02/27 17:36:52 kettenis Exp $ */
3db7e12daSjmcneill
4db7e12daSjmcneill /*-
5db7e12daSjmcneill * Copyright (c) 2021 Mark Kettenis <kettenis@openbsd.org>
6db7e12daSjmcneill * Copyright (c) 2021 Jared McNeill <jmcneill@invisible.ca>
7db7e12daSjmcneill *
8db7e12daSjmcneill * Permission to use, copy, modify, and distribute this software for any
9db7e12daSjmcneill * purpose with or without fee is hereby granted, provided that the above
10db7e12daSjmcneill * copyright notice and this permission notice appear in all copies.
11db7e12daSjmcneill *
12db7e12daSjmcneill * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13db7e12daSjmcneill * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14db7e12daSjmcneill * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15db7e12daSjmcneill * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16db7e12daSjmcneill * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17db7e12daSjmcneill * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18db7e12daSjmcneill * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19db7e12daSjmcneill */
20db7e12daSjmcneill
21db7e12daSjmcneill //#define APPLE_DART_DEBUG
22db7e12daSjmcneill
23db7e12daSjmcneill #include <sys/cdefs.h>
24*b5511178Sjmcneill __KERNEL_RCSID(0, "$NetBSD: apple_dart.c,v 1.5 2023/02/24 11:19:15 jmcneill Exp $");
25db7e12daSjmcneill
26db7e12daSjmcneill #include <sys/param.h>
27db7e12daSjmcneill #include <sys/bus.h>
28db7e12daSjmcneill #include <sys/device.h>
29db7e12daSjmcneill #include <sys/intr.h>
30db7e12daSjmcneill #include <sys/kernel.h>
31db7e12daSjmcneill #include <sys/systm.h>
32db7e12daSjmcneill #include <sys/kmem.h>
33db7e12daSjmcneill #include <sys/vmem.h>
34db7e12daSjmcneill
35db7e12daSjmcneill #include <arm/cpufunc.h>
36db7e12daSjmcneill
37db7e12daSjmcneill #include <dev/fdt/fdtvar.h>
38f6a78933Sskrll /*
39f6a78933Sskrll * This driver largely ignores stream IDs and simply uses a single
40f6a78933Sskrll * translation table for all the devices that it serves. This is good
41f6a78933Sskrll * enough for the PCIe host bridge that serves the on-board devices on
42f6a78933Sskrll * the current generation Apple Silicon Macs as these only have a
43f6a78933Sskrll * single PCIe device behind each DART.
44f6a78933Sskrll */
45db7e12daSjmcneill
46db7e12daSjmcneill /*
47db7e12daSjmcneill * DART registers
48db7e12daSjmcneill */
49f6a78933Sskrll #define DART_PARAMS2 0x0004
50f6a78933Sskrll #define DART_PARAMS2_BYPASS_SUPPORT __BIT(0)
51db7e12daSjmcneill #define DART_TLB_OP 0x0020
52db7e12daSjmcneill #define DART_TLB_OP_BUSY __BIT(2)
53f6a78933Sskrll #define DART_TLB_OP_FLUSH __BIT(20)
54db7e12daSjmcneill #define DART_TLB_OP_SIDMASK 0x0034
55db7e12daSjmcneill #define DART_ERR_STATUS 0x0040
56f6a78933Sskrll #define DART_ERR_FLAG __BIT(31)
57f6a78933Sskrll #define DART_ERR_STREAM_MASK __BITS(27, 24)
58f6a78933Sskrll #define DART_ERR_CODE_MASK __BITS(11, 0)
59f6a78933Sskrll #define DART_ERR_READ_FAULT __BIT(4)
60f6a78933Sskrll #define DART_ERR_WRITE_FAULT __BIT(3)
61f6a78933Sskrll #define DART_ERR_NOPTE __BIT(2)
62f6a78933Sskrll #define DART_ERR_NOPMD __BIT(1)
63f6a78933Sskrll #define DART_ERR_NOTTBR __BIT(0)
64db7e12daSjmcneill #define DART_ERR_ADDRL 0x0050
65db7e12daSjmcneill #define DART_ERR_ADDRH 0x0054
66f6a78933Sskrll #define DART_CONFIG 0x0060
67f6a78933Sskrll #define DART_CONFIG_LOCK __BIT(15)
68f6a78933Sskrll #define DART_TCR(sid) (0x0100 + (sid) * 0x4)
69f6a78933Sskrll #define DART_TCR_TRANSLATE_ENABLE __BIT(7)
70f6a78933Sskrll #define DART_TCR_BYPASS_DART __BIT(8)
71f6a78933Sskrll #define DART_TCR_BYPASS_DAPF __BIT(12)
72db7e12daSjmcneill #define DART_TTBR(sid, idx) (0x0200 + (sid) * 0x10 + (idx) * 0x4)
73db7e12daSjmcneill #define DART_TTBR_VALID __BIT(31)
74db7e12daSjmcneill #define DART_TTBR_SHIFT 12
75db7e12daSjmcneill
76f6a78933Sskrll #define DART_NUM_STREAMS 16
77f6a78933Sskrll #define DART_ALL_STREAMS ((1 << DART_NUM_STREAMS) - 1)
78f6a78933Sskrll
79db7e12daSjmcneill #define DART_APERTURE_START 0x00100000
80db7e12daSjmcneill #define DART_APERTURE_SIZE 0x3fe00000
81db7e12daSjmcneill #define DART_PAGE_SIZE 16384
82db7e12daSjmcneill #define DART_PAGE_MASK (DART_PAGE_SIZE - 1)
83db7e12daSjmcneill
84f6a78933Sskrll /*
85f6a78933Sskrll * Some hardware (e.g. bge(4)) will always use (aligned) 64-bit memory
86f6a78933Sskrll * access. To make sure this doesn't fault, round the subpage limits
87f6a78933Sskrll * down and up accordingly.
88f6a78933Sskrll */
89f6a78933Sskrll #define DART_OFFSET_MASK 7
90f6a78933Sskrll
91f6a78933Sskrll #define DART_L1_TABLE 0x3
92db7e12daSjmcneill #define DART_L2_INVAL 0x0
93f6a78933Sskrll #define DART_L2_VALID __BIT(0)
94f6a78933Sskrll #define DART_L2_FULL_PAGE __BIT(1)
95f6a78933Sskrll
96f6a78933Sskrll #define DART_L2_START_MASK __BITS(63, 52)
97f6a78933Sskrll #define DART_L2_END_MASK __BITS(51, 40)
98f6a78933Sskrll #define DART_L2_SUBPAGE(addr) __SHIFTOUT((addr), __BITS(13, 2))
99f6a78933Sskrll #define DART_L2_START(addr) __SHIFTIN(DART_L2_SUBPAGE(addr), DART_L2_START_MASK)
100f6a78933Sskrll #define DART_L2_END(addr) __SHIFTIN(DART_L2_SUBPAGE(addr), DART_L2_END_MASK)
101db7e12daSjmcneill
102db7e12daSjmcneill #define DART_ROUND_PAGE(pa) (((pa) + DART_PAGE_MASK) & ~DART_PAGE_MASK)
103db7e12daSjmcneill #define DART_TRUNC_PAGE(pa) ((pa) & ~DART_PAGE_MASK)
104f6a78933Sskrll #define DART_ROUND_OFFSET(pa) (((pa) + DART_OFFSET_MASK) & ~DART_OFFSET_MASK)
105f6a78933Sskrll #define DART_TRUNC_OFFSET(pa) ((pa) & ~DART_OFFSET_MASK)
106db7e12daSjmcneill
107db7e12daSjmcneill static const struct device_compatible_entry compat_data[] = {
108db7e12daSjmcneill { .compat = "apple,dart-m1", .value = 16 },
109f6a78933Sskrll { .compat = "apple,t8103-dart", .value = 16 },
110db7e12daSjmcneill DEVICE_COMPAT_EOL
111db7e12daSjmcneill };
112db7e12daSjmcneill
113db7e12daSjmcneill static struct arm32_dma_range apple_dart_dma_ranges[] = {
114db7e12daSjmcneill [0] = {
115db7e12daSjmcneill .dr_sysbase = 0,
116db7e12daSjmcneill .dr_busbase = 0,
117db7e12daSjmcneill .dr_len = UINTPTR_MAX,
118db7e12daSjmcneill .dr_flags = _BUS_DMAMAP_COHERENT,
119db7e12daSjmcneill }
120db7e12daSjmcneill };
121db7e12daSjmcneill
122db7e12daSjmcneill struct apple_dart_map_state {
123db7e12daSjmcneill bus_addr_t ams_dva;
124db7e12daSjmcneill bus_size_t ams_len;
125db7e12daSjmcneill };
126db7e12daSjmcneill
127db7e12daSjmcneill struct apple_dart_dma {
128db7e12daSjmcneill bus_dmamap_t dma_map;
129db7e12daSjmcneill bus_dma_segment_t dma_seg;
130db7e12daSjmcneill bus_size_t dma_size;
131db7e12daSjmcneill void *dma_kva;
132db7e12daSjmcneill };
133db7e12daSjmcneill
134db7e12daSjmcneill #define DART_DMA_MAP(_dma) ((_dma)->dma_map)
135db7e12daSjmcneill #define DART_DMA_LEN(_dma) ((_dma)->dma_size)
136db7e12daSjmcneill #define DART_DMA_DVA(_dma) ((_dma)->dma_map->dm_segs[0].ds_addr)
137db7e12daSjmcneill #define DART_DMA_KVA(_dma) ((_dma)->dma_kva)
138db7e12daSjmcneill
139db7e12daSjmcneill struct apple_dart_softc {
140db7e12daSjmcneill device_t sc_dev;
141db7e12daSjmcneill int sc_phandle;
142db7e12daSjmcneill bus_space_tag_t sc_bst;
143db7e12daSjmcneill bus_space_handle_t sc_bsh;
144db7e12daSjmcneill bus_dma_tag_t sc_dmat;
145db7e12daSjmcneill
146db7e12daSjmcneill uint64_t sc_sid_mask;
147db7e12daSjmcneill u_int sc_nsid;
148db7e12daSjmcneill
149db7e12daSjmcneill vmem_t *sc_dvamap;
150db7e12daSjmcneill
151db7e12daSjmcneill struct apple_dart_dma *sc_l1;
152db7e12daSjmcneill struct apple_dart_dma **sc_l2;
153db7e12daSjmcneill u_int sc_nl2;
154db7e12daSjmcneill
155db7e12daSjmcneill struct arm32_bus_dma_tag sc_bus_dmat;
156db7e12daSjmcneill };
157db7e12daSjmcneill
158db7e12daSjmcneill #define DART_READ(sc, reg) \
159db7e12daSjmcneill bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
160db7e12daSjmcneill #define DART_WRITE(sc, reg, val) \
161db7e12daSjmcneill bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
162db7e12daSjmcneill
163db7e12daSjmcneill static void
apple_dart_flush_tlb(struct apple_dart_softc * sc)164db7e12daSjmcneill apple_dart_flush_tlb(struct apple_dart_softc *sc)
165db7e12daSjmcneill {
166db7e12daSjmcneill dsb(sy);
167db7e12daSjmcneill isb();
168db7e12daSjmcneill
169db7e12daSjmcneill DART_WRITE(sc, DART_TLB_OP_SIDMASK, sc->sc_sid_mask);
170db7e12daSjmcneill DART_WRITE(sc, DART_TLB_OP, DART_TLB_OP_FLUSH);
171db7e12daSjmcneill while ((DART_READ(sc, DART_TLB_OP) & DART_TLB_OP_BUSY) != 0) {
172db7e12daSjmcneill __asm volatile ("yield" ::: "memory");
173db7e12daSjmcneill }
174db7e12daSjmcneill }
175db7e12daSjmcneill
176db7e12daSjmcneill static struct apple_dart_dma *
apple_dart_dma_alloc(bus_dma_tag_t dmat,bus_size_t size,bus_size_t align)177db7e12daSjmcneill apple_dart_dma_alloc(bus_dma_tag_t dmat, bus_size_t size, bus_size_t align)
178db7e12daSjmcneill {
179db7e12daSjmcneill struct apple_dart_dma *dma;
180db7e12daSjmcneill int nsegs, error;
181db7e12daSjmcneill
182db7e12daSjmcneill dma = kmem_zalloc(sizeof(*dma), KM_SLEEP);
183db7e12daSjmcneill dma->dma_size = size;
184db7e12daSjmcneill
185db7e12daSjmcneill error = bus_dmamem_alloc(dmat, size, align, 0, &dma->dma_seg, 1,
186db7e12daSjmcneill &nsegs, BUS_DMA_WAITOK);
187db7e12daSjmcneill if (error != 0) {
188db7e12daSjmcneill goto destroy;
189db7e12daSjmcneill }
190db7e12daSjmcneill
191db7e12daSjmcneill error = bus_dmamem_map(dmat, &dma->dma_seg, nsegs, size,
192db7e12daSjmcneill &dma->dma_kva, BUS_DMA_WAITOK | BUS_DMA_NOCACHE);
193db7e12daSjmcneill if (error != 0) {
194db7e12daSjmcneill goto free;
195db7e12daSjmcneill }
196db7e12daSjmcneill
197db7e12daSjmcneill error = bus_dmamap_create(dmat, size, 1, size, 0,
198db7e12daSjmcneill BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW, &dma->dma_map);
199db7e12daSjmcneill if (error != 0) {
200db7e12daSjmcneill goto dmafree;
201db7e12daSjmcneill }
202db7e12daSjmcneill
203db7e12daSjmcneill error = bus_dmamap_load(dmat, dma->dma_map, dma->dma_kva, size,
204db7e12daSjmcneill NULL, BUS_DMA_WAITOK);
205db7e12daSjmcneill if (error != 0) {
206db7e12daSjmcneill goto unmap;
207db7e12daSjmcneill }
208db7e12daSjmcneill
209db7e12daSjmcneill memset(dma->dma_kva, 0, size);
210db7e12daSjmcneill
211db7e12daSjmcneill return dma;
212db7e12daSjmcneill
213db7e12daSjmcneill destroy:
214db7e12daSjmcneill bus_dmamap_destroy(dmat, dma->dma_map);
215db7e12daSjmcneill unmap:
216db7e12daSjmcneill bus_dmamem_unmap(dmat, dma->dma_kva, size);
217db7e12daSjmcneill free:
218db7e12daSjmcneill bus_dmamem_free(dmat, &dma->dma_seg, 1);
219db7e12daSjmcneill dmafree:
220db7e12daSjmcneill kmem_free(dma, sizeof(*dma));
221db7e12daSjmcneill return NULL;
222db7e12daSjmcneill }
223db7e12daSjmcneill
224db7e12daSjmcneill static int
apple_dart_intr(void * priv)225db7e12daSjmcneill apple_dart_intr(void *priv)
226db7e12daSjmcneill {
227db7e12daSjmcneill struct apple_dart_softc * const sc = priv;
228db7e12daSjmcneill char fdt_path[128];
229db7e12daSjmcneill uint64_t addr;
230db7e12daSjmcneill uint32_t status;
231db7e12daSjmcneill
232db7e12daSjmcneill status = DART_READ(sc, DART_ERR_STATUS);
233f6a78933Sskrll addr = __SHIFTIN(DART_READ(sc, DART_ERR_ADDRL), __BITS(31, 0));
234f6a78933Sskrll addr |= __SHIFTIN(DART_READ(sc, DART_ERR_ADDRH), __BITS(63, 32));
235db7e12daSjmcneill DART_WRITE(sc, DART_ERR_STATUS, status);
236db7e12daSjmcneill
237f6a78933Sskrll if ((status & DART_ERR_FLAG) == 0)
238f6a78933Sskrll return 1;
239f6a78933Sskrll
240f6a78933Sskrll #ifdef APPLE_DART_DEBUG
241f6a78933Sskrll printf("%s: status %#"PRIx32"\n", __func__, status);
242f6a78933Sskrll printf("%s: addrl %#"PRIx32"\n", __func__, DART_READ(sc, DART_ERR_ADDRL));
243f6a78933Sskrll printf("%s: addrh %#"PRIx32"\n", __func__, DART_READ(sc, DART_ERR_ADDRH));
244f6a78933Sskrll #endif
245f6a78933Sskrll
246f6a78933Sskrll const char *reason = NULL;
247f6a78933Sskrll int32_t code = __SHIFTOUT(status, DART_ERR_CODE_MASK);
248f6a78933Sskrll switch (code) {
249f6a78933Sskrll case DART_ERR_NOTTBR:
250f6a78933Sskrll reason = "no ttbr for address";
251f6a78933Sskrll break;
252f6a78933Sskrll case DART_ERR_NOPMD:
253f6a78933Sskrll reason = "no pmd for address";
254f6a78933Sskrll break;
255f6a78933Sskrll case DART_ERR_NOPTE:
256f6a78933Sskrll reason = "no pte for address";
257f6a78933Sskrll break;
258f6a78933Sskrll case DART_ERR_WRITE_FAULT:
259f6a78933Sskrll reason = "write fault";
260f6a78933Sskrll break;
261f6a78933Sskrll case DART_ERR_READ_FAULT:
262f6a78933Sskrll reason = "read fault";
263f6a78933Sskrll break;
264f6a78933Sskrll }
265db7e12daSjmcneill fdtbus_get_path(sc->sc_phandle, fdt_path, sizeof(fdt_path));
266db7e12daSjmcneill
267f6a78933Sskrll printf("%s (%s): error addr 0x%016lx status 0x%08x: %s\n",
268f6a78933Sskrll device_xname(sc->sc_dev), fdt_path, addr, status, reason);
269db7e12daSjmcneill
270db7e12daSjmcneill return 1;
271db7e12daSjmcneill }
272db7e12daSjmcneill
273db7e12daSjmcneill static volatile uint64_t *
apple_dart_lookup_tte(struct apple_dart_softc * sc,bus_addr_t dva)274db7e12daSjmcneill apple_dart_lookup_tte(struct apple_dart_softc *sc, bus_addr_t dva)
275db7e12daSjmcneill {
276db7e12daSjmcneill int idx = dva / DART_PAGE_SIZE;
277db7e12daSjmcneill int l2_idx = idx / (DART_PAGE_SIZE / sizeof(uint64_t));
278db7e12daSjmcneill int tte_idx = idx % (DART_PAGE_SIZE / sizeof(uint64_t));
279f6a78933Sskrll volatile uint64_t *l2 = DART_DMA_KVA(sc->sc_l2[l2_idx]);
280db7e12daSjmcneill
281db7e12daSjmcneill return &l2[tte_idx];
282db7e12daSjmcneill }
283db7e12daSjmcneill
284db7e12daSjmcneill static void
apple_dart_unload_map(struct apple_dart_softc * sc,bus_dmamap_t map)285db7e12daSjmcneill apple_dart_unload_map(struct apple_dart_softc *sc, bus_dmamap_t map)
286db7e12daSjmcneill {
287db7e12daSjmcneill struct apple_dart_map_state *ams = map->_dm_iommu;
288db7e12daSjmcneill volatile uint64_t *tte;
289db7e12daSjmcneill int seg;
290db7e12daSjmcneill
291db7e12daSjmcneill /* For each segment */
292db7e12daSjmcneill for (seg = 0; seg < map->dm_nsegs; seg++) {
293db7e12daSjmcneill u_long len, dva;
294db7e12daSjmcneill
295db7e12daSjmcneill if (ams[seg].ams_len == 0) {
296db7e12daSjmcneill continue;
297db7e12daSjmcneill }
298db7e12daSjmcneill
299db7e12daSjmcneill dva = ams[seg].ams_dva;
300db7e12daSjmcneill len = ams[seg].ams_len;
301db7e12daSjmcneill
302db7e12daSjmcneill while (len > 0) {
303db7e12daSjmcneill tte = apple_dart_lookup_tte(sc, dva);
304db7e12daSjmcneill *tte = DART_L2_INVAL;
305db7e12daSjmcneill
306db7e12daSjmcneill dva += DART_PAGE_SIZE;
307db7e12daSjmcneill len -= DART_PAGE_SIZE;
308db7e12daSjmcneill }
309db7e12daSjmcneill
310db7e12daSjmcneill vmem_xfree(sc->sc_dvamap, ams[seg].ams_dva, ams[seg].ams_len);
311db7e12daSjmcneill
312db7e12daSjmcneill ams[seg].ams_dva = 0;
313db7e12daSjmcneill ams[seg].ams_len = 0;
314db7e12daSjmcneill }
315db7e12daSjmcneill
316db7e12daSjmcneill apple_dart_flush_tlb(sc);
317db7e12daSjmcneill }
318db7e12daSjmcneill
319db7e12daSjmcneill static int
apple_dart_load_map(struct apple_dart_softc * sc,bus_dmamap_t map)320db7e12daSjmcneill apple_dart_load_map(struct apple_dart_softc *sc, bus_dmamap_t map)
321db7e12daSjmcneill {
322db7e12daSjmcneill struct apple_dart_map_state *ams = map->_dm_iommu;
323db7e12daSjmcneill volatile uint64_t *tte;
324db7e12daSjmcneill int seg, error;
325db7e12daSjmcneill
326db7e12daSjmcneill /* For each segment */
327db7e12daSjmcneill for (seg = 0; seg < map->dm_nsegs; seg++) {
328db7e12daSjmcneill paddr_t pa = map->dm_segs[seg]._ds_paddr;
329db7e12daSjmcneill psize_t off = pa - DART_TRUNC_PAGE(pa);
330db7e12daSjmcneill u_long len, dva;
331db7e12daSjmcneill
332db7e12daSjmcneill len = DART_ROUND_PAGE(map->dm_segs[seg].ds_len + off);
333db7e12daSjmcneill
334db7e12daSjmcneill #ifdef APPLE_DART_DEBUG
335db7e12daSjmcneill device_printf(sc->sc_dev, "load pa=%#lx off=%lu len=%lu ",
336db7e12daSjmcneill pa, off, len);
337db7e12daSjmcneill #endif
338db7e12daSjmcneill
339db7e12daSjmcneill error = vmem_xalloc(sc->sc_dvamap, len, DART_PAGE_SIZE, 0,
340db7e12daSjmcneill 0, VMEM_ADDR_MIN, VMEM_ADDR_MAX, VM_BESTFIT|VM_NOSLEEP,
341db7e12daSjmcneill &dva);
342db7e12daSjmcneill if (error != 0) {
343db7e12daSjmcneill apple_dart_unload_map(sc, map);
344db7e12daSjmcneill #ifdef APPLE_DART_DEBUG
345db7e12daSjmcneill printf("error=%d\n", error);
346db7e12daSjmcneill #endif
347db7e12daSjmcneill return error;
348db7e12daSjmcneill }
349db7e12daSjmcneill
350db7e12daSjmcneill #ifdef APPLE_DART_DEBUG
351db7e12daSjmcneill printf("dva=%#lx\n", dva);
352db7e12daSjmcneill #endif
353db7e12daSjmcneill
354db7e12daSjmcneill ams[seg].ams_dva = dva;
355db7e12daSjmcneill ams[seg].ams_len = len;
356db7e12daSjmcneill
357db7e12daSjmcneill map->dm_segs[seg].ds_addr = dva + off;
358db7e12daSjmcneill
359db7e12daSjmcneill pa = DART_TRUNC_PAGE(pa);
360f6a78933Sskrll paddr_t start = DART_TRUNC_OFFSET(off);
361f6a78933Sskrll paddr_t end = DART_PAGE_MASK;
362db7e12daSjmcneill while (len > 0) {
363db7e12daSjmcneill tte = apple_dart_lookup_tte(sc, dva);
364f6a78933Sskrll if (len < DART_PAGE_SIZE)
365f6a78933Sskrll end = DART_ROUND_OFFSET(len) - 1;
366db7e12daSjmcneill
367f6a78933Sskrll *tte = pa | DART_L2_VALID |
368f6a78933Sskrll DART_L2_START(start) | DART_L2_END(end);
369f6a78933Sskrll #ifdef APPLE_DART_DEBUG
370f6a78933Sskrll printf("tte %p = %"PRIx64"\n", tte, *tte);
371f6a78933Sskrll #endif
372db7e12daSjmcneill pa += DART_PAGE_SIZE;
373db7e12daSjmcneill dva += DART_PAGE_SIZE;
374db7e12daSjmcneill len -= DART_PAGE_SIZE;
375f6a78933Sskrll start = 0;
376db7e12daSjmcneill }
377db7e12daSjmcneill }
378db7e12daSjmcneill
379db7e12daSjmcneill apple_dart_flush_tlb(sc);
380db7e12daSjmcneill
381db7e12daSjmcneill return 0;
382db7e12daSjmcneill }
383db7e12daSjmcneill
384db7e12daSjmcneill static int
apple_dart_dmamap_create(bus_dma_tag_t t,bus_size_t size,int nsegments,bus_size_t maxsegsz,bus_size_t boundary,int flags,bus_dmamap_t * dmamap)385db7e12daSjmcneill apple_dart_dmamap_create(bus_dma_tag_t t, bus_size_t size, int nsegments,
386db7e12daSjmcneill bus_size_t maxsegsz, bus_size_t boundary, int flags, bus_dmamap_t *dmamap)
387db7e12daSjmcneill {
388db7e12daSjmcneill struct apple_dart_softc *sc = t->_cookie;
389db7e12daSjmcneill struct apple_dart_map_state *ams;
390db7e12daSjmcneill bus_dmamap_t map;
391db7e12daSjmcneill int error;
392db7e12daSjmcneill
393db7e12daSjmcneill error = sc->sc_dmat->_dmamap_create(sc->sc_dmat, size, nsegments,
394db7e12daSjmcneill maxsegsz, boundary, flags, &map);
395db7e12daSjmcneill if (error != 0) {
396db7e12daSjmcneill return error;
397db7e12daSjmcneill }
398db7e12daSjmcneill
399db7e12daSjmcneill ams = kmem_zalloc(map->_dm_segcnt * sizeof(*ams),
400db7e12daSjmcneill (flags & BUS_DMA_NOWAIT) != 0 ? KM_NOSLEEP : KM_SLEEP);
401db7e12daSjmcneill if (ams == NULL) {
402db7e12daSjmcneill sc->sc_dmat->_dmamap_destroy(sc->sc_dmat, map);
403db7e12daSjmcneill return ENOMEM;
404db7e12daSjmcneill }
405db7e12daSjmcneill
406db7e12daSjmcneill map->_dm_iommu = ams;
407db7e12daSjmcneill *dmamap = map;
408db7e12daSjmcneill return 0;
409db7e12daSjmcneill }
410db7e12daSjmcneill
411db7e12daSjmcneill static void
apple_dart_dmamap_destroy(bus_dma_tag_t t,bus_dmamap_t map)412db7e12daSjmcneill apple_dart_dmamap_destroy(bus_dma_tag_t t, bus_dmamap_t map)
413db7e12daSjmcneill {
414db7e12daSjmcneill struct apple_dart_softc *sc = t->_cookie;
415db7e12daSjmcneill struct apple_dart_map_state *ams = map->_dm_iommu;
416db7e12daSjmcneill
417db7e12daSjmcneill kmem_free(ams, map->_dm_segcnt * sizeof(*ams));
418db7e12daSjmcneill sc->sc_dmat->_dmamap_destroy(sc->sc_dmat, map);
419db7e12daSjmcneill }
420db7e12daSjmcneill
421db7e12daSjmcneill static int
apple_dart_dmamap_load(bus_dma_tag_t t,bus_dmamap_t map,void * buf,size_t buflen,struct proc * p,int flags)422db7e12daSjmcneill apple_dart_dmamap_load(bus_dma_tag_t t, bus_dmamap_t map, void *buf,
423db7e12daSjmcneill size_t buflen, struct proc *p, int flags)
424db7e12daSjmcneill {
425db7e12daSjmcneill struct apple_dart_softc *sc = t->_cookie;
426db7e12daSjmcneill int error;
427db7e12daSjmcneill
428db7e12daSjmcneill error = sc->sc_dmat->_dmamap_load(sc->sc_dmat, map,
429db7e12daSjmcneill buf, buflen, p, flags);
430db7e12daSjmcneill if (error != 0) {
431db7e12daSjmcneill return error;
432db7e12daSjmcneill }
433db7e12daSjmcneill
434db7e12daSjmcneill error = apple_dart_load_map(sc, map);
435db7e12daSjmcneill if (error != 0) {
436db7e12daSjmcneill sc->sc_dmat->_dmamap_unload(sc->sc_dmat, map);
437db7e12daSjmcneill }
438db7e12daSjmcneill
439db7e12daSjmcneill return error;
440db7e12daSjmcneill }
441db7e12daSjmcneill
442db7e12daSjmcneill static int
apple_dart_dmamap_load_mbuf(bus_dma_tag_t t,bus_dmamap_t map,struct mbuf * m,int flags)443db7e12daSjmcneill apple_dart_dmamap_load_mbuf(bus_dma_tag_t t, bus_dmamap_t map,
444db7e12daSjmcneill struct mbuf *m, int flags)
445db7e12daSjmcneill {
446db7e12daSjmcneill struct apple_dart_softc *sc = t->_cookie;
447db7e12daSjmcneill int error;
448db7e12daSjmcneill
449db7e12daSjmcneill error = sc->sc_dmat->_dmamap_load_mbuf(sc->sc_dmat, map,
450db7e12daSjmcneill m, flags);
451db7e12daSjmcneill if (error != 0) {
452db7e12daSjmcneill return error;
453db7e12daSjmcneill }
454db7e12daSjmcneill
455db7e12daSjmcneill error = apple_dart_load_map(sc, map);
456db7e12daSjmcneill if (error != 0) {
457db7e12daSjmcneill sc->sc_dmat->_dmamap_unload(sc->sc_dmat, map);
458db7e12daSjmcneill }
459db7e12daSjmcneill
460db7e12daSjmcneill return error;
461db7e12daSjmcneill }
462db7e12daSjmcneill
463db7e12daSjmcneill static int
apple_dart_dmamap_load_uio(bus_dma_tag_t t,bus_dmamap_t map,struct uio * uio,int flags)464db7e12daSjmcneill apple_dart_dmamap_load_uio(bus_dma_tag_t t, bus_dmamap_t map,
465db7e12daSjmcneill struct uio *uio, int flags)
466db7e12daSjmcneill {
467db7e12daSjmcneill struct apple_dart_softc *sc = t->_cookie;
468db7e12daSjmcneill int error;
469db7e12daSjmcneill
470db7e12daSjmcneill error = sc->sc_dmat->_dmamap_load_uio(sc->sc_dmat, map,
471db7e12daSjmcneill uio, flags);
472db7e12daSjmcneill if (error != 0) {
473db7e12daSjmcneill return error;
474db7e12daSjmcneill }
475db7e12daSjmcneill
476db7e12daSjmcneill error = apple_dart_load_map(sc, map);
477db7e12daSjmcneill if (error != 0) {
478db7e12daSjmcneill sc->sc_dmat->_dmamap_unload(sc->sc_dmat, map);
479db7e12daSjmcneill }
480db7e12daSjmcneill
481db7e12daSjmcneill return error;
482db7e12daSjmcneill }
483db7e12daSjmcneill
484db7e12daSjmcneill static int
apple_dart_dmamap_load_raw(bus_dma_tag_t t,bus_dmamap_t map,bus_dma_segment_t * segs,int nsegs,bus_size_t size,int flags)485db7e12daSjmcneill apple_dart_dmamap_load_raw(bus_dma_tag_t t, bus_dmamap_t map,
486db7e12daSjmcneill bus_dma_segment_t *segs, int nsegs, bus_size_t size, int flags)
487db7e12daSjmcneill {
488db7e12daSjmcneill struct apple_dart_softc *sc = t->_cookie;
489db7e12daSjmcneill int error;
490db7e12daSjmcneill
491db7e12daSjmcneill error = sc->sc_dmat->_dmamap_load_raw(sc->sc_dmat, map,
492db7e12daSjmcneill segs, nsegs, size, flags);
493db7e12daSjmcneill if (error != 0) {
494db7e12daSjmcneill return error;
495db7e12daSjmcneill }
496db7e12daSjmcneill
497db7e12daSjmcneill error = apple_dart_load_map(sc, map);
498db7e12daSjmcneill if (error != 0) {
499db7e12daSjmcneill sc->sc_dmat->_dmamap_unload(sc->sc_dmat, map);
500db7e12daSjmcneill }
501db7e12daSjmcneill
502db7e12daSjmcneill return error;
503db7e12daSjmcneill }
504db7e12daSjmcneill
505db7e12daSjmcneill static void
apple_dart_dmamap_unload(bus_dma_tag_t t,bus_dmamap_t map)506db7e12daSjmcneill apple_dart_dmamap_unload(bus_dma_tag_t t, bus_dmamap_t map)
507db7e12daSjmcneill {
508db7e12daSjmcneill struct apple_dart_softc *sc = t->_cookie;
509db7e12daSjmcneill
510db7e12daSjmcneill apple_dart_unload_map(sc, map);
511db7e12daSjmcneill sc->sc_dmat->_dmamap_unload(sc->sc_dmat, map);
512db7e12daSjmcneill }
513db7e12daSjmcneill
514a808ce0eSjmcneill static bus_dma_tag_t
apple_dart_iommu_map(device_t dev,const u_int * data,bus_dma_tag_t dmat)515a808ce0eSjmcneill apple_dart_iommu_map(device_t dev, const u_int *data, bus_dma_tag_t dmat)
516a808ce0eSjmcneill {
517a808ce0eSjmcneill struct apple_dart_softc * const sc = device_private(dev);
518a808ce0eSjmcneill
519a808ce0eSjmcneill return &sc->sc_bus_dmat;
520a808ce0eSjmcneill }
521a808ce0eSjmcneill
522a808ce0eSjmcneill const struct fdtbus_iommu_func apple_dart_iommu_funcs = {
523a808ce0eSjmcneill .map = apple_dart_iommu_map,
524a808ce0eSjmcneill };
525a808ce0eSjmcneill
526db7e12daSjmcneill static int
apple_dart_match(device_t parent,cfdata_t cf,void * aux)527db7e12daSjmcneill apple_dart_match(device_t parent, cfdata_t cf, void *aux)
528db7e12daSjmcneill {
529db7e12daSjmcneill struct fdt_attach_args * const faa = aux;
530db7e12daSjmcneill
531db7e12daSjmcneill return of_compatible_match(faa->faa_phandle, compat_data);
532db7e12daSjmcneill }
533db7e12daSjmcneill
534db7e12daSjmcneill static void
apple_dart_attach(device_t parent,device_t self,void * aux)535db7e12daSjmcneill apple_dart_attach(device_t parent, device_t self, void *aux)
536db7e12daSjmcneill {
537db7e12daSjmcneill struct apple_dart_softc * const sc = device_private(self);
538db7e12daSjmcneill struct fdt_attach_args * const faa = aux;
539db7e12daSjmcneill const int phandle = faa->faa_phandle;
540db7e12daSjmcneill char intrstr[128];
541db7e12daSjmcneill volatile uint64_t *l1;
542db7e12daSjmcneill bus_addr_t addr;
543db7e12daSjmcneill bus_size_t size;
544db7e12daSjmcneill u_int sid, idx;
545db7e12daSjmcneill paddr_t pa;
546db7e12daSjmcneill void *ih;
547db7e12daSjmcneill
548db7e12daSjmcneill if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
549db7e12daSjmcneill aprint_error(": couldn't get registers\n");
550db7e12daSjmcneill return;
551db7e12daSjmcneill }
552db7e12daSjmcneill if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
553db7e12daSjmcneill aprint_error(": couldn't decode interrupt\n");
554db7e12daSjmcneill return;
555db7e12daSjmcneill }
556db7e12daSjmcneill
557db7e12daSjmcneill sc->sc_dev = self;
558db7e12daSjmcneill sc->sc_phandle = phandle;
559db7e12daSjmcneill sc->sc_dmat = faa->faa_dmat;
560db7e12daSjmcneill sc->sc_bst = faa->faa_bst;
56177a7b674Sjmcneill if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
562db7e12daSjmcneill aprint_error(": couldn't map registers\n");
563db7e12daSjmcneill return;
564db7e12daSjmcneill }
565db7e12daSjmcneill
566f6a78933Sskrll /* Skip locked DARTs for now. */
567f6a78933Sskrll uint32_t config = DART_READ(sc, DART_CONFIG);
568f6a78933Sskrll if (config & DART_CONFIG_LOCK) {
569f6a78933Sskrll aprint_naive("\n");
570f6a78933Sskrll aprint_normal(": locked\n");
571f6a78933Sskrll return;
572db7e12daSjmcneill }
573db7e12daSjmcneill
574f6a78933Sskrll /*
575f6a78933Sskrll * Use bypass mode if supported. This avoids an issue with
576f6a78933Sskrll * the USB3 controllers which need mappings entered into two
577f6a78933Sskrll * IOMMUs, which is somewhat difficult to implement with our
578f6a78933Sskrll * current kernel interfaces.
579f6a78933Sskrll */
580f6a78933Sskrll uint32_t params2 = DART_READ(sc, DART_PARAMS2);
581f6a78933Sskrll if (params2 & DART_PARAMS2_BYPASS_SUPPORT) {
582f6a78933Sskrll for (sid = 0; sid < DART_NUM_STREAMS; sid++) {
583f6a78933Sskrll DART_WRITE(sc, DART_TCR(sid),
584f6a78933Sskrll DART_TCR_BYPASS_DART | DART_TCR_BYPASS_DAPF);
585f6a78933Sskrll }
586f6a78933Sskrll aprint_naive("\n");
587f6a78933Sskrll aprint_normal(": bypass\n");
588f6a78933Sskrll return;
589f6a78933Sskrll }
590f6a78933Sskrll
591f6a78933Sskrll sc->sc_nsid = of_compatible_lookup(phandle, compat_data)->value;
592f6a78933Sskrll sc->sc_sid_mask = __MASK(sc->sc_nsid);
593f6a78933Sskrll
594db7e12daSjmcneill aprint_naive("\n");
595db7e12daSjmcneill aprint_normal(": Apple DART @ %#lx/%#lx, %u SIDs (mask 0x%lx)\n",
596db7e12daSjmcneill addr, size, sc->sc_nsid, sc->sc_sid_mask);
597db7e12daSjmcneill
598db7e12daSjmcneill KASSERT(sc->sc_nsid == 16);
599db7e12daSjmcneill KASSERT(sc->sc_sid_mask == 0xffff);
600db7e12daSjmcneill
601db7e12daSjmcneill sc->sc_dvamap = vmem_create(device_xname(self),
602db7e12daSjmcneill DART_APERTURE_START, DART_APERTURE_SIZE, DART_PAGE_SIZE,
603db7e12daSjmcneill NULL, NULL, NULL, 0, VM_SLEEP, IPL_HIGH);
604db7e12daSjmcneill if (sc->sc_dvamap == NULL) {
605db7e12daSjmcneill aprint_error_dev(self, "couldn't allocate DVA map\n");
606db7e12daSjmcneill return;
607db7e12daSjmcneill }
608db7e12daSjmcneill
609db7e12daSjmcneill /* Disable translations */
610db7e12daSjmcneill for (sid = 0; sid < sc->sc_nsid; sid++) {
611f6a78933Sskrll DART_WRITE(sc, DART_TCR(sid), 0);
612db7e12daSjmcneill }
613db7e12daSjmcneill
614db7e12daSjmcneill /* Remove page tables */
615db7e12daSjmcneill for (sid = 0; sid < sc->sc_nsid; sid++) {
616db7e12daSjmcneill for (idx = 0; idx < 4; idx++) {
617db7e12daSjmcneill DART_WRITE(sc, DART_TTBR(sid, idx), 0);
618db7e12daSjmcneill }
619db7e12daSjmcneill }
620db7e12daSjmcneill apple_dart_flush_tlb(sc);
621db7e12daSjmcneill
622db7e12daSjmcneill /*
623db7e12daSjmcneill * Build translation tables. We pre-allocate the translation
624db7e12daSjmcneill * tables for the entire aperture such that we don't have to worry
625db7e12daSjmcneill * about growing them in an mpsafe manner later.
626f6a78933Sskrll *
627f6a78933Sskrll * Cover the entire address space [0, ..._START + ..._SIZE) even if vmem
628f6a78933Sskrll * only allocates from [..._START, ..._START + ...+SIZE)
629db7e12daSjmcneill */
630db7e12daSjmcneill
631db7e12daSjmcneill const u_int ntte = howmany(DART_APERTURE_START + DART_APERTURE_SIZE - 1,
632db7e12daSjmcneill DART_PAGE_SIZE);
633db7e12daSjmcneill const u_int nl2 = howmany(ntte, DART_PAGE_SIZE / sizeof(uint64_t));
634db7e12daSjmcneill const u_int nl1 = howmany(nl2, DART_PAGE_SIZE / sizeof(uint64_t));
635db7e12daSjmcneill
636db7e12daSjmcneill sc->sc_l1 = apple_dart_dma_alloc(sc->sc_dmat,
637db7e12daSjmcneill nl1 * DART_PAGE_SIZE, DART_PAGE_SIZE);
638db7e12daSjmcneill if (sc->sc_l1 == NULL) {
639db7e12daSjmcneill aprint_error_dev(self, "couldn't allocate L1 tables\n");
640db7e12daSjmcneill return;
641db7e12daSjmcneill }
642db7e12daSjmcneill sc->sc_l2 = kmem_zalloc(nl2 * sizeof(*sc->sc_l2), KM_SLEEP);
643db7e12daSjmcneill sc->sc_nl2 = nl2;
644db7e12daSjmcneill
645db7e12daSjmcneill l1 = DART_DMA_KVA(sc->sc_l1);
646db7e12daSjmcneill for (idx = 0; idx < nl2; idx++) {
647db7e12daSjmcneill sc->sc_l2[idx] = apple_dart_dma_alloc(sc->sc_dmat,
648db7e12daSjmcneill DART_PAGE_SIZE, DART_PAGE_SIZE);
649db7e12daSjmcneill if (sc->sc_l2[idx] == NULL) {
650db7e12daSjmcneill aprint_error_dev(self,
651db7e12daSjmcneill "couldn't allocate L2 tables\n");
652db7e12daSjmcneill return;
653db7e12daSjmcneill }
654f6a78933Sskrll
655db7e12daSjmcneill l1[idx] = DART_DMA_DVA(sc->sc_l2[idx]) | DART_L1_TABLE;
656f6a78933Sskrll #ifdef APPLE_DART_DEBUG
657f6a78933Sskrll printf("l1[%d] (%p) = %"PRIx64"\n", idx, &l1[idx], l1[idx]);
658f6a78933Sskrll #endif
659db7e12daSjmcneill }
660db7e12daSjmcneill
661db7e12daSjmcneill /* Install page tables */
662db7e12daSjmcneill for (sid = 0; sid < sc->sc_nsid; sid++) {
663db7e12daSjmcneill pa = DART_DMA_DVA(sc->sc_l1);
664db7e12daSjmcneill for (idx = 0; idx < nl1; idx++) {
665f6a78933Sskrll KASSERTMSG(__SHIFTOUT(pa, __BITS(DART_TTBR_SHIFT - 1, 0)) == 0,
666f6a78933Sskrll "TTBR pa is not correctly aligned %" PRIxPADDR, pa);
667f6a78933Sskrll
668db7e12daSjmcneill DART_WRITE(sc, DART_TTBR(sid, idx),
669db7e12daSjmcneill (pa >> DART_TTBR_SHIFT) | DART_TTBR_VALID);
670db7e12daSjmcneill pa += DART_PAGE_SIZE;
671f6a78933Sskrll #ifdef APPLE_DART_DEBUG
672f6a78933Sskrll printf("writing %"PRIx64" to %"PRIx32"\n",
673f6a78933Sskrll (pa >> DART_TTBR_SHIFT) | DART_TTBR_VALID,
674f6a78933Sskrll DART_TTBR(sid, idx));
675f6a78933Sskrll #endif
676db7e12daSjmcneill }
677db7e12daSjmcneill }
678db7e12daSjmcneill apple_dart_flush_tlb(sc);
679db7e12daSjmcneill
680db7e12daSjmcneill /* Enable translations */
681db7e12daSjmcneill for (sid = 0; sid < sc->sc_nsid; sid++) {
682f6a78933Sskrll DART_WRITE(sc, DART_TCR(sid), DART_TCR_TRANSLATE_ENABLE);
683db7e12daSjmcneill }
684db7e12daSjmcneill
685db7e12daSjmcneill ih = fdtbus_intr_establish_xname(phandle, 0, IPL_HIGH, FDT_INTR_MPSAFE,
686db7e12daSjmcneill apple_dart_intr, sc, device_xname(self));
687db7e12daSjmcneill if (ih == NULL) {
688db7e12daSjmcneill aprint_error_dev(self, "couldn't establish interrupt on %s\n",
689db7e12daSjmcneill intrstr);
690db7e12daSjmcneill return;
691db7e12daSjmcneill }
692db7e12daSjmcneill aprint_normal_dev(self, "interrupting on %s\n", intrstr);
693db7e12daSjmcneill
694db7e12daSjmcneill /* Setup bus DMA tag */
695db7e12daSjmcneill sc->sc_bus_dmat = *sc->sc_dmat;
696db7e12daSjmcneill sc->sc_bus_dmat._ranges = apple_dart_dma_ranges;
697db7e12daSjmcneill sc->sc_bus_dmat._nranges = 1;
698db7e12daSjmcneill sc->sc_bus_dmat._cookie = sc;
699db7e12daSjmcneill sc->sc_bus_dmat._dmamap_create = apple_dart_dmamap_create;
700db7e12daSjmcneill sc->sc_bus_dmat._dmamap_destroy = apple_dart_dmamap_destroy;
701db7e12daSjmcneill sc->sc_bus_dmat._dmamap_load = apple_dart_dmamap_load;
702db7e12daSjmcneill sc->sc_bus_dmat._dmamap_load_mbuf = apple_dart_dmamap_load_mbuf;
703db7e12daSjmcneill sc->sc_bus_dmat._dmamap_load_uio = apple_dart_dmamap_load_uio;
704db7e12daSjmcneill sc->sc_bus_dmat._dmamap_load_raw = apple_dart_dmamap_load_raw;
705db7e12daSjmcneill sc->sc_bus_dmat._dmamap_unload = apple_dart_dmamap_unload;
706db7e12daSjmcneill
707a808ce0eSjmcneill fdtbus_register_iommu(self, phandle, &apple_dart_iommu_funcs);
708db7e12daSjmcneill }
709db7e12daSjmcneill
710db7e12daSjmcneill CFATTACH_DECL_NEW(apple_dart, sizeof(struct apple_dart_softc),
711db7e12daSjmcneill apple_dart_match, apple_dart_attach, NULL, NULL);
712