1*0f9e9ec2Sjsg /* $OpenBSD: apldart.c,v 1.21 2024/05/13 01:15:50 jsg Exp $ */
255f7f351Skettenis /*
355f7f351Skettenis * Copyright (c) 2021 Mark Kettenis <kettenis@openbsd.org>
455f7f351Skettenis *
555f7f351Skettenis * Permission to use, copy, modify, and distribute this software for any
655f7f351Skettenis * purpose with or without fee is hereby granted, provided that the above
755f7f351Skettenis * copyright notice and this permission notice appear in all copies.
855f7f351Skettenis *
955f7f351Skettenis * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1055f7f351Skettenis * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1155f7f351Skettenis * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1255f7f351Skettenis * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1355f7f351Skettenis * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1455f7f351Skettenis * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1555f7f351Skettenis * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1655f7f351Skettenis */
1755f7f351Skettenis
1855f7f351Skettenis #include <sys/param.h>
1955f7f351Skettenis #include <sys/systm.h>
2055f7f351Skettenis #include <sys/device.h>
2155f7f351Skettenis #include <sys/extent.h>
2255f7f351Skettenis #include <sys/malloc.h>
239ced64eaSvisa #include <sys/mutex.h>
2455f7f351Skettenis
2555f7f351Skettenis #include <machine/intr.h>
2655f7f351Skettenis #include <machine/bus.h>
2755f7f351Skettenis #include <machine/fdt.h>
2855f7f351Skettenis
29494ba95aSkettenis #include <uvm/uvm_extern.h>
30494ba95aSkettenis
3155f7f351Skettenis #include <dev/ofw/openfirm.h>
3255f7f351Skettenis #include <dev/ofw/ofw_misc.h>
33f2e6717fSkettenis #include <dev/ofw/ofw_power.h>
3455f7f351Skettenis #include <dev/ofw/fdt.h>
3555f7f351Skettenis
3655f7f351Skettenis /*
3755f7f351Skettenis * This driver largely ignores stream IDs and simply uses a single
3855f7f351Skettenis * translation table for all the devices that it serves. This is good
3955f7f351Skettenis * enough for the PCIe host bridge that serves the on-board devices on
4055f7f351Skettenis * the current generation Apple Silicon Macs as these only have a
4155f7f351Skettenis * single PCIe device behind each DART.
4255f7f351Skettenis */
4355f7f351Skettenis
44c4e02adeSkettenis #define DART_PARAMS2 0x0004
45c4e02adeSkettenis #define DART_PARAMS2_BYPASS_SUPPORT (1 << 0)
4655f7f351Skettenis
47bd26d413Skettenis #define DART_T8020_TLB_CMD 0x0020
48bd26d413Skettenis #define DART_T8020_TLB_CMD_FLUSH (1 << 20)
49bd26d413Skettenis #define DART_T8020_TLB_CMD_BUSY (1 << 2)
50bd26d413Skettenis #define DART_T8020_TLB_SIDMASK 0x0034
51bd26d413Skettenis #define DART_T8020_ERROR 0x0040
52bd26d413Skettenis #define DART_T8020_ERROR_ADDR_LO 0x0050
53bd26d413Skettenis #define DART_T8020_ERROR_ADDR_HI 0x0054
54bd26d413Skettenis #define DART_T8020_CONFIG 0x0060
55bd26d413Skettenis #define DART_T8020_CONFIG_LOCK (1 << 15)
56bd26d413Skettenis #define DART_T8020_SID_ENABLE 0x00fc
57bd26d413Skettenis #define DART_T8020_TCR_BASE 0x0100
58bd26d413Skettenis #define DART_T8020_TCR_TRANSLATE_ENABLE (1 << 7)
59bd26d413Skettenis #define DART_T8020_TCR_BYPASS_DART (1 << 8)
60bd26d413Skettenis #define DART_T8020_TCR_BYPASS_DAPF (1 << 12)
61bd26d413Skettenis #define DART_T8020_TTBR_BASE 0x0200
62bd26d413Skettenis #define DART_T8020_TTBR_VALID (1U << 31)
63bd26d413Skettenis
64b4036ef4Skettenis #define DART_T8110_PARAMS3 0x0008
65b4036ef4Skettenis #define DART_T8110_PARAMS3_REV_MIN(x) (((x) >> 0) & 0xff)
66b4036ef4Skettenis #define DART_T8110_PARAMS3_REV_MAJ(x) (((x) >> 8) & 0xff)
67b4036ef4Skettenis #define DART_T8110_PARAMS3_VA_WIDTH(x) (((x) >> 16) & 0x3f)
68bd26d413Skettenis #define DART_T8110_PARAMS4 0x000c
69bd26d413Skettenis #define DART_T8110_PARAMS4_NSID_MASK (0x1ff << 0)
70bd26d413Skettenis #define DART_T8110_TLB_CMD 0x0080
71bd26d413Skettenis #define DART_T8110_TLB_CMD_BUSY (1U << 31)
72bd26d413Skettenis #define DART_T8110_TLB_CMD_FLUSH_ALL (0 << 8)
7388ef38baSkettenis #define DART_T8110_TLB_CMD_FLUSH_SID (1 << 8)
74bd26d413Skettenis #define DART_T8110_ERROR 0x0100
75bd26d413Skettenis #define DART_T8110_ERROR_MASK 0x0104
76bd26d413Skettenis #define DART_T8110_ERROR_ADDR_LO 0x0170
77bd26d413Skettenis #define DART_T8110_ERROR_ADDR_HI 0x0174
78bd26d413Skettenis #define DART_T8110_PROTECT 0x0200
79bd26d413Skettenis #define DART_T8110_PROTECT_TTBR_TCR (1 << 0)
80bd26d413Skettenis #define DART_T8110_SID_ENABLE_BASE 0x0c00
81bd26d413Skettenis #define DART_T8110_TCR_BASE 0x1000
82bd26d413Skettenis #define DART_T8110_TCR_BYPASS_DAPF (1 << 2)
83bd26d413Skettenis #define DART_T8110_TCR_BYPASS_DART (1 << 1)
84bd26d413Skettenis #define DART_T8110_TCR_TRANSLATE_ENABLE (1 << 0)
85bd26d413Skettenis #define DART_T8110_TTBR_BASE 0x1400
86bd26d413Skettenis #define DART_T8110_TTBR_VALID (1 << 0)
87c4e02adeSkettenis
8855f7f351Skettenis #define DART_PAGE_SIZE 16384
8955f7f351Skettenis #define DART_PAGE_MASK (DART_PAGE_SIZE - 1)
9055f7f351Skettenis
91bd26d413Skettenis #define DART_SID_ENABLE(sc, idx) \
92bd26d413Skettenis ((sc)->sc_sid_enable_base + 4 * (idx))
93bd26d413Skettenis #define DART_TCR(sc, sid) ((sc)->sc_tcr_base + 4 * (sid))
94bd26d413Skettenis #define DART_TTBR(sc, sid, idx) \
95bd26d413Skettenis ((sc)->sc_ttbr_base + 4 * (sc)->sc_nttbr * (sid) + 4 * (idx))
96bd26d413Skettenis #define DART_TTBR_SHIFT 12
97bd26d413Skettenis
98bd26d413Skettenis #define DART_ALL_STREAMS(sc) ((1U << (sc)->sc_nsid) - 1)
99bd26d413Skettenis
1002c847e44Skettenis /*
1012c847e44Skettenis * Some hardware (e.g. bge(4)) will always use (aligned) 64-bit memory
1022c847e44Skettenis * access. To make sure this doesn't fault, round the subpage limits
1032c847e44Skettenis * down and up accordingly.
1042c847e44Skettenis */
1052c847e44Skettenis #define DART_OFFSET_MASK 7
1062c847e44Skettenis
107f809c1d0Skettenis #define DART_L1_TABLE 0x3
1082c847e44Skettenis #define DART_L2_INVAL 0
1092c847e44Skettenis #define DART_L2_VALID (1 << 0)
1102c847e44Skettenis #define DART_L2_FULL_PAGE (1 << 1)
1112c847e44Skettenis #define DART_L2_START(addr) ((((addr) & DART_PAGE_MASK) >> 2) << 52)
1122c847e44Skettenis #define DART_L2_END(addr) ((((addr) & DART_PAGE_MASK) >> 2) << 40)
11355f7f351Skettenis
11430025dadSpatrick static inline paddr_t
apldart_round_page(paddr_t pa)11555f7f351Skettenis apldart_round_page(paddr_t pa)
11655f7f351Skettenis {
11755f7f351Skettenis return ((pa + DART_PAGE_MASK) & ~DART_PAGE_MASK);
11855f7f351Skettenis }
11955f7f351Skettenis
1202c847e44Skettenis static inline paddr_t
apldart_trunc_page(paddr_t pa)1212c847e44Skettenis apldart_trunc_page(paddr_t pa)
12255f7f351Skettenis {
12355f7f351Skettenis return (pa & ~DART_PAGE_MASK);
12455f7f351Skettenis }
12555f7f351Skettenis
1262c847e44Skettenis static inline psize_t
apldart_round_offset(psize_t off)1272c847e44Skettenis apldart_round_offset(psize_t off)
1282c847e44Skettenis {
1292c847e44Skettenis return ((off + DART_OFFSET_MASK) & ~DART_OFFSET_MASK);
1302c847e44Skettenis }
1312c847e44Skettenis
1322c847e44Skettenis static inline psize_t
apldart_trunc_offset(psize_t off)1332c847e44Skettenis apldart_trunc_offset(psize_t off)
1342c847e44Skettenis {
1352c847e44Skettenis return (off & ~DART_OFFSET_MASK);
1362c847e44Skettenis }
1372c847e44Skettenis
13855f7f351Skettenis #define HREAD4(sc, reg) \
139c4e02adeSkettenis (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
14055f7f351Skettenis #define HWRITE4(sc, reg, val) \
141c4e02adeSkettenis bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
14255f7f351Skettenis
14388ef38baSkettenis struct apldart_stream {
14488ef38baSkettenis struct apldart_softc *as_sc;
14588ef38baSkettenis int as_sid;
14688ef38baSkettenis
14788ef38baSkettenis struct extent *as_dvamap;
14888ef38baSkettenis struct mutex as_dvamap_mtx;
14988ef38baSkettenis struct apldart_dmamem *as_l1;
15088ef38baSkettenis struct apldart_dmamem **as_l2;
15188ef38baSkettenis
15288ef38baSkettenis struct machine_bus_dma_tag as_dmat;
15388ef38baSkettenis };
15488ef38baSkettenis
15555f7f351Skettenis struct apldart_softc {
15655f7f351Skettenis struct device sc_dev;
15755f7f351Skettenis bus_space_tag_t sc_iot;
158c4e02adeSkettenis bus_space_handle_t sc_ioh;
15955f7f351Skettenis bus_dma_tag_t sc_dmat;
160bd26d413Skettenis int sc_node;
161bd26d413Skettenis
162b4036ef4Skettenis int sc_ias;
163bd26d413Skettenis int sc_nsid;
164bd26d413Skettenis int sc_nttbr;
16588ef38baSkettenis int sc_shift;
166bd26d413Skettenis bus_addr_t sc_sid_enable_base;
167bd26d413Skettenis bus_addr_t sc_tcr_base;
168bd26d413Skettenis uint32_t sc_tcr_translate_enable;
169bd26d413Skettenis uint32_t sc_tcr_bypass;
170bd26d413Skettenis bus_addr_t sc_ttbr_base;
171bd26d413Skettenis uint32_t sc_ttbr_valid;
17288ef38baSkettenis void (*sc_flush_tlb)(struct apldart_softc *, int);
17355f7f351Skettenis
17455f7f351Skettenis bus_addr_t sc_dvabase;
17555f7f351Skettenis bus_addr_t sc_dvaend;
176b4036ef4Skettenis bus_addr_t sc_dvamask;
17755f7f351Skettenis
17888ef38baSkettenis struct apldart_stream **sc_as;
17955f7f351Skettenis struct iommu_device sc_id;
180f2e6717fSkettenis
181494ba95aSkettenis int sc_locked;
182494ba95aSkettenis int sc_translating;
183f2e6717fSkettenis int sc_do_suspend;
18455f7f351Skettenis };
18555f7f351Skettenis
18655f7f351Skettenis struct apldart_map_state {
18755f7f351Skettenis struct extent_region ams_er;
18855f7f351Skettenis bus_addr_t ams_dva;
18955f7f351Skettenis bus_size_t ams_len;
19055f7f351Skettenis };
19155f7f351Skettenis
19255f7f351Skettenis struct apldart_dmamem {
19355f7f351Skettenis bus_dmamap_t adm_map;
19455f7f351Skettenis bus_dma_segment_t adm_seg;
19555f7f351Skettenis size_t adm_size;
19655f7f351Skettenis caddr_t adm_kva;
19755f7f351Skettenis };
19855f7f351Skettenis
19955f7f351Skettenis #define APLDART_DMA_MAP(_adm) ((_adm)->adm_map)
20055f7f351Skettenis #define APLDART_DMA_LEN(_adm) ((_adm)->adm_size)
20155f7f351Skettenis #define APLDART_DMA_DVA(_adm) ((_adm)->adm_map->dm_segs[0].ds_addr)
20255f7f351Skettenis #define APLDART_DMA_KVA(_adm) ((void *)(_adm)->adm_kva)
20355f7f351Skettenis
20455f7f351Skettenis struct apldart_dmamem *apldart_dmamem_alloc(bus_dma_tag_t, bus_size_t,
20555f7f351Skettenis bus_size_t);
20655f7f351Skettenis void apldart_dmamem_free(bus_dma_tag_t, struct apldart_dmamem *);
20755f7f351Skettenis
20855f7f351Skettenis int apldart_match(struct device *, void *, void *);
20955f7f351Skettenis void apldart_attach(struct device *, struct device *, void *);
210f2e6717fSkettenis int apldart_activate(struct device *, int);
21155f7f351Skettenis
212471aeecfSnaddy const struct cfattach apldart_ca = {
213f2e6717fSkettenis sizeof (struct apldart_softc), apldart_match, apldart_attach, NULL,
214f2e6717fSkettenis apldart_activate
21555f7f351Skettenis };
21655f7f351Skettenis
21755f7f351Skettenis struct cfdriver apldart_cd = {
21855f7f351Skettenis NULL, "apldart", DV_DULL
21955f7f351Skettenis };
22055f7f351Skettenis
22155f7f351Skettenis bus_dma_tag_t apldart_map(void *, uint32_t *, bus_dma_tag_t);
222415019ceSpatrick void apldart_reserve(void *, uint32_t *, bus_addr_t, bus_size_t);
223bd26d413Skettenis int apldart_t8020_intr(void *);
224bd26d413Skettenis int apldart_t8110_intr(void *);
22555f7f351Skettenis
22688ef38baSkettenis void apldart_t8020_flush_tlb(struct apldart_softc *, int);
22788ef38baSkettenis void apldart_t8110_flush_tlb(struct apldart_softc *, int);
228b4036ef4Skettenis int apldart_load_map(struct apldart_stream *, bus_dmamap_t, int);
22988ef38baSkettenis void apldart_unload_map(struct apldart_stream *, bus_dmamap_t);
23055f7f351Skettenis
23155f7f351Skettenis int apldart_dmamap_create(bus_dma_tag_t, bus_size_t, int, bus_size_t,
23255f7f351Skettenis bus_size_t boundary, int, bus_dmamap_t *);
23355f7f351Skettenis void apldart_dmamap_destroy(bus_dma_tag_t, bus_dmamap_t);
23455f7f351Skettenis int apldart_dmamap_load(bus_dma_tag_t, bus_dmamap_t, void *,
23555f7f351Skettenis bus_size_t, struct proc *, int);
23655f7f351Skettenis int apldart_dmamap_load_mbuf(bus_dma_tag_t, bus_dmamap_t,
23755f7f351Skettenis struct mbuf *, int);
23855f7f351Skettenis int apldart_dmamap_load_uio(bus_dma_tag_t, bus_dmamap_t,
23955f7f351Skettenis struct uio *, int);
24055f7f351Skettenis int apldart_dmamap_load_raw(bus_dma_tag_t, bus_dmamap_t,
24155f7f351Skettenis bus_dma_segment_t *, int, bus_size_t, int);
24255f7f351Skettenis void apldart_dmamap_unload(bus_dma_tag_t, bus_dmamap_t);
24355f7f351Skettenis
24455f7f351Skettenis int
apldart_match(struct device * parent,void * match,void * aux)24555f7f351Skettenis apldart_match(struct device *parent, void *match, void *aux)
24655f7f351Skettenis {
24755f7f351Skettenis struct fdt_attach_args *faa = aux;
24855f7f351Skettenis
249bd26d413Skettenis return OF_is_compatible(faa->fa_node, "apple,t6000-dart") ||
250bd26d413Skettenis OF_is_compatible(faa->fa_node, "apple,t8103-dart") ||
251bd26d413Skettenis OF_is_compatible(faa->fa_node, "apple,t8110-dart");
25255f7f351Skettenis }
25355f7f351Skettenis
25455f7f351Skettenis void
apldart_attach(struct device * parent,struct device * self,void * aux)25555f7f351Skettenis apldart_attach(struct device *parent, struct device *self, void *aux)
25655f7f351Skettenis {
25755f7f351Skettenis struct apldart_softc *sc = (struct apldart_softc *)self;
25855f7f351Skettenis struct fdt_attach_args *faa = aux;
259b4036ef4Skettenis uint64_t dva_range[2];
260b4036ef4Skettenis uint32_t config, maj, min, params2, params3, params4, tcr, ttbr;
26155f7f351Skettenis int sid, idx;
26255f7f351Skettenis
26355f7f351Skettenis if (faa->fa_nreg < 1) {
26455f7f351Skettenis printf(": no registers\n");
26555f7f351Skettenis return;
26655f7f351Skettenis }
26755f7f351Skettenis
26855f7f351Skettenis sc->sc_iot = faa->fa_iot;
26955f7f351Skettenis if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
270c4e02adeSkettenis faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
27155f7f351Skettenis printf(": can't map registers\n");
27255f7f351Skettenis return;
27355f7f351Skettenis }
27455f7f351Skettenis
27555f7f351Skettenis sc->sc_dmat = faa->fa_dmat;
276bd26d413Skettenis sc->sc_node = faa->fa_node;
277f2e6717fSkettenis
278f2e6717fSkettenis power_domain_enable(sc->sc_node);
279f2e6717fSkettenis
280bd26d413Skettenis if (OF_is_compatible(sc->sc_node, "apple,t8110-dart")) {
281b4036ef4Skettenis params3 = HREAD4(sc, DART_T8110_PARAMS3);
282bd26d413Skettenis params4 = HREAD4(sc, DART_T8110_PARAMS4);
283b4036ef4Skettenis sc->sc_ias = DART_T8110_PARAMS3_VA_WIDTH(params3);
284bd26d413Skettenis sc->sc_nsid = params4 & DART_T8110_PARAMS4_NSID_MASK;
285bd26d413Skettenis sc->sc_nttbr = 1;
286bd26d413Skettenis sc->sc_sid_enable_base = DART_T8110_SID_ENABLE_BASE;
287bd26d413Skettenis sc->sc_tcr_base = DART_T8110_TCR_BASE;
288bd26d413Skettenis sc->sc_tcr_translate_enable = DART_T8110_TCR_TRANSLATE_ENABLE;
289bd26d413Skettenis sc->sc_tcr_bypass =
290bd26d413Skettenis DART_T8110_TCR_BYPASS_DAPF | DART_T8110_TCR_BYPASS_DART;
291bd26d413Skettenis sc->sc_ttbr_base = DART_T8110_TTBR_BASE;
292bd26d413Skettenis sc->sc_ttbr_valid = DART_T8110_TTBR_VALID;
293bd26d413Skettenis sc->sc_flush_tlb = apldart_t8110_flush_tlb;
294b4036ef4Skettenis maj = DART_T8110_PARAMS3_REV_MAJ(params3);
295b4036ef4Skettenis min = DART_T8110_PARAMS3_REV_MIN(params3);
296bd26d413Skettenis } else {
297b4036ef4Skettenis sc->sc_ias = 32;
298bd26d413Skettenis sc->sc_nsid = 16;
299bd26d413Skettenis sc->sc_nttbr = 4;
300bd26d413Skettenis sc->sc_sid_enable_base = DART_T8020_SID_ENABLE;
301bd26d413Skettenis sc->sc_tcr_base = DART_T8020_TCR_BASE;
302bd26d413Skettenis sc->sc_tcr_translate_enable = DART_T8020_TCR_TRANSLATE_ENABLE;
303bd26d413Skettenis sc->sc_tcr_bypass =
304bd26d413Skettenis DART_T8020_TCR_BYPASS_DAPF | DART_T8020_TCR_BYPASS_DART;
305bd26d413Skettenis sc->sc_ttbr_base = DART_T8020_TTBR_BASE;
306bd26d413Skettenis sc->sc_ttbr_valid = DART_T8020_TTBR_VALID;
307bd26d413Skettenis sc->sc_flush_tlb = apldart_t8020_flush_tlb;
308b4036ef4Skettenis maj = min = 0;
309bd26d413Skettenis }
310bd26d413Skettenis
311bd26d413Skettenis if (OF_is_compatible(sc->sc_node, "apple,t6000-dart") ||
312bd26d413Skettenis OF_is_compatible(sc->sc_node, "apple,t8110-dart"))
313bd26d413Skettenis sc->sc_shift = 4;
314bd26d413Skettenis
315f05e7979Skettenis /* Skip locked DARTs for now. */
316bd26d413Skettenis if (OF_is_compatible(sc->sc_node, "apple,t8110-dart")) {
317bd26d413Skettenis config = HREAD4(sc, DART_T8110_PROTECT);
318494ba95aSkettenis if (config & DART_T8110_PROTECT_TTBR_TCR)
319494ba95aSkettenis sc->sc_locked = 1;
320bd26d413Skettenis } else {
321bd26d413Skettenis config = HREAD4(sc, DART_T8020_CONFIG);
322494ba95aSkettenis if (config & DART_T8020_CONFIG_LOCK)
323494ba95aSkettenis sc->sc_locked = 1;
324bd26d413Skettenis }
32555f7f351Skettenis
326b4036ef4Skettenis if (maj != 0 || min != 0)
327b4036ef4Skettenis printf(" rev %d.%d", maj, min);
328b4036ef4Skettenis
329b4036ef4Skettenis printf(": %d bits", sc->sc_ias);
330b4036ef4Skettenis
331b4036ef4Skettenis /*
332b4036ef4Skettenis * Anything over 36 bits requires 4-level page tables which we
333b4036ef4Skettenis * don't implement yet. So limit to 36 bits.
334b4036ef4Skettenis */
335b4036ef4Skettenis if (sc->sc_ias > 36)
336b4036ef4Skettenis sc->sc_ias = 36;
337b4036ef4Skettenis sc->sc_dvamask = (1ULL << sc->sc_ias) - 1;
338b4036ef4Skettenis
339f05e7979Skettenis /*
34077431635Skettenis * Resetting the DART used for the display controller will
34177431635Skettenis * kill the framebuffer. This should be the only DART that
34277431635Skettenis * has translation enabled and a valid translation table
34377431635Skettenis * installed. Skip this DART for now.
34477431635Skettenis */
345bd26d413Skettenis for (sid = 0; sid < sc->sc_nsid; sid++) {
346bd26d413Skettenis tcr = HREAD4(sc, DART_TCR(sc, sid));
347bd26d413Skettenis if ((tcr & sc->sc_tcr_translate_enable) == 0)
34877431635Skettenis continue;
34977431635Skettenis
350bd26d413Skettenis for (idx = 0; idx < sc->sc_nttbr; idx++) {
351bd26d413Skettenis ttbr = HREAD4(sc, DART_TTBR(sc, sid, idx));
352494ba95aSkettenis if (ttbr & sc->sc_ttbr_valid)
353494ba95aSkettenis sc->sc_translating = 1;
35477431635Skettenis }
35577431635Skettenis }
35677431635Skettenis
35777431635Skettenis /*
358494ba95aSkettenis * If we have full control over this DART, do suspend it.
359f2e6717fSkettenis */
360494ba95aSkettenis sc->sc_do_suspend = !sc->sc_locked && !sc->sc_translating;
361f2e6717fSkettenis
362f2e6717fSkettenis /*
363f05e7979Skettenis * Use bypass mode if supported. This avoids an issue with
364f05e7979Skettenis * the USB3 controllers which need mappings entered into two
365f05e7979Skettenis * IOMMUs, which is somewhat difficult to implement with our
366f05e7979Skettenis * current kernel interfaces.
367f05e7979Skettenis */
368c4e02adeSkettenis params2 = HREAD4(sc, DART_PARAMS2);
369494ba95aSkettenis if ((params2 & DART_PARAMS2_BYPASS_SUPPORT) &&
370494ba95aSkettenis !sc->sc_locked && !sc->sc_translating) {
371bd26d413Skettenis for (sid = 0; sid < sc->sc_nsid; sid++)
372bd26d413Skettenis HWRITE4(sc, DART_TCR(sc, sid), sc->sc_tcr_bypass);
373b4036ef4Skettenis printf(", bypass\n");
374c4e02adeSkettenis return;
375c4e02adeSkettenis }
37655f7f351Skettenis
377494ba95aSkettenis if (sc->sc_locked)
378b4036ef4Skettenis printf(", locked\n");
379494ba95aSkettenis else if (sc->sc_translating)
380b4036ef4Skettenis printf(", translating\n");
381494ba95aSkettenis else
382f05e7979Skettenis printf("\n");
383f05e7979Skettenis
384b4036ef4Skettenis if (OF_getpropint64array(sc->sc_node, "apple,dma-range",
385b4036ef4Skettenis dva_range, sizeof(dva_range)) == sizeof(dva_range)) {
386b4036ef4Skettenis sc->sc_dvabase = dva_range[0];
387b4036ef4Skettenis sc->sc_dvaend = dva_range[0] + dva_range[1] - 1;
388b4036ef4Skettenis } else {
38914edd68eSkettenis /*
390b4036ef4Skettenis * Restrict ourselves to 32-bit addresses to cater for
391b4036ef4Skettenis * devices that don't do 64-bit DMA. Skip the first
392b4036ef4Skettenis * page to help catching bugs where a device is doing
393b4036ef4Skettenis * DMA to/from address zero because we didn't properly
394b4036ef4Skettenis * set up the DMA transfer. Skip the last page to
395b4036ef4Skettenis * avoid using the address reserved for MSIs.
39614edd68eSkettenis */
39714edd68eSkettenis sc->sc_dvabase = DART_PAGE_SIZE;
39814edd68eSkettenis sc->sc_dvaend = 0xffffffff - DART_PAGE_SIZE;
399b4036ef4Skettenis }
40055f7f351Skettenis
401494ba95aSkettenis if (!sc->sc_locked && !sc->sc_translating) {
40255f7f351Skettenis /* Disable translations. */
403bd26d413Skettenis for (sid = 0; sid < sc->sc_nsid; sid++)
404bd26d413Skettenis HWRITE4(sc, DART_TCR(sc, sid), 0);
40555f7f351Skettenis
40655f7f351Skettenis /* Remove page tables. */
407bd26d413Skettenis for (sid = 0; sid < sc->sc_nsid; sid++) {
408bd26d413Skettenis for (idx = 0; idx < sc->sc_nttbr; idx++)
409bd26d413Skettenis HWRITE4(sc, DART_TTBR(sc, sid, idx), 0);
41055f7f351Skettenis }
41188ef38baSkettenis sc->sc_flush_tlb(sc, -1);
412494ba95aSkettenis }
41355f7f351Skettenis
414bd26d413Skettenis if (OF_is_compatible(sc->sc_node, "apple,t8110-dart")) {
415bd26d413Skettenis HWRITE4(sc, DART_T8110_ERROR, HREAD4(sc, DART_T8110_ERROR));
416bd26d413Skettenis HWRITE4(sc, DART_T8110_ERROR_MASK, 0);
417bd26d413Skettenis fdt_intr_establish(faa->fa_node, IPL_NET, apldart_t8110_intr,
41855f7f351Skettenis sc, sc->sc_dev.dv_xname);
419bd26d413Skettenis } else {
420bd26d413Skettenis HWRITE4(sc, DART_T8020_ERROR, HREAD4(sc, DART_T8020_ERROR));
421bd26d413Skettenis fdt_intr_establish(faa->fa_node, IPL_NET, apldart_t8020_intr,
422bd26d413Skettenis sc, sc->sc_dev.dv_xname);
423bd26d413Skettenis }
42455f7f351Skettenis
42588ef38baSkettenis sc->sc_as = mallocarray(sc->sc_nsid, sizeof(*sc->sc_as),
42688ef38baSkettenis M_DEVBUF, M_WAITOK | M_ZERO);
42755f7f351Skettenis
42855f7f351Skettenis sc->sc_id.id_node = faa->fa_node;
42955f7f351Skettenis sc->sc_id.id_cookie = sc;
43055f7f351Skettenis sc->sc_id.id_map = apldart_map;
431415019ceSpatrick sc->sc_id.id_reserve = apldart_reserve;
43255f7f351Skettenis iommu_device_register(&sc->sc_id);
43355f7f351Skettenis }
43455f7f351Skettenis
435f2e6717fSkettenis void
apldart_suspend(struct apldart_softc * sc)436f2e6717fSkettenis apldart_suspend(struct apldart_softc *sc)
437f2e6717fSkettenis {
438f2e6717fSkettenis if (!sc->sc_do_suspend)
439f2e6717fSkettenis return;
440f2e6717fSkettenis
441f2e6717fSkettenis power_domain_disable(sc->sc_node);
442f2e6717fSkettenis }
443f2e6717fSkettenis
444f2e6717fSkettenis void
apldart_resume(struct apldart_softc * sc)445f2e6717fSkettenis apldart_resume(struct apldart_softc *sc)
446f2e6717fSkettenis {
447f2e6717fSkettenis paddr_t pa;
448f2e6717fSkettenis int ntte, nl1, nl2;
449f2e6717fSkettenis uint32_t params2;
45088ef38baSkettenis uint32_t mask;
451f2e6717fSkettenis int sid, idx;
452f2e6717fSkettenis
453f2e6717fSkettenis if (!sc->sc_do_suspend)
454f2e6717fSkettenis return;
455f2e6717fSkettenis
456f2e6717fSkettenis power_domain_enable(sc->sc_node);
457f2e6717fSkettenis
458f2e6717fSkettenis params2 = HREAD4(sc, DART_PARAMS2);
459f2e6717fSkettenis if (params2 & DART_PARAMS2_BYPASS_SUPPORT) {
460f2e6717fSkettenis for (sid = 0; sid < sc->sc_nsid; sid++)
461f2e6717fSkettenis HWRITE4(sc, DART_TCR(sc, sid), sc->sc_tcr_bypass);
462f2e6717fSkettenis return;
463f2e6717fSkettenis }
464f2e6717fSkettenis
465b4036ef4Skettenis ntte = howmany((sc->sc_dvaend & sc->sc_dvamask), DART_PAGE_SIZE);
466f2e6717fSkettenis nl2 = howmany(ntte, DART_PAGE_SIZE / sizeof(uint64_t));
467f2e6717fSkettenis nl1 = howmany(nl2, DART_PAGE_SIZE / sizeof(uint64_t));
468f2e6717fSkettenis
469f2e6717fSkettenis /* Install page tables. */
470f2e6717fSkettenis for (sid = 0; sid < sc->sc_nsid; sid++) {
47188ef38baSkettenis if (sc->sc_as[sid] == NULL)
47288ef38baSkettenis continue;
47388ef38baSkettenis pa = APLDART_DMA_DVA(sc->sc_as[sid]->as_l1);
474f2e6717fSkettenis for (idx = 0; idx < nl1; idx++) {
475f2e6717fSkettenis HWRITE4(sc, DART_TTBR(sc, sid, idx),
476f2e6717fSkettenis (pa >> DART_TTBR_SHIFT) | sc->sc_ttbr_valid);
477f2e6717fSkettenis pa += DART_PAGE_SIZE;
478f2e6717fSkettenis }
479f2e6717fSkettenis }
48088ef38baSkettenis sc->sc_flush_tlb(sc, -1);
481f2e6717fSkettenis
48288ef38baSkettenis /* Enable all active streams. */
48388ef38baSkettenis for (sid = 0; sid < sc->sc_nsid; sid++) {
48488ef38baSkettenis if (sc->sc_as[sid] == NULL)
48588ef38baSkettenis continue;
48688ef38baSkettenis mask = HREAD4(sc, DART_SID_ENABLE(sc, sid / 32));
48788ef38baSkettenis mask |= (1U << (sid % 32));
48888ef38baSkettenis HWRITE4(sc, DART_SID_ENABLE(sc, sid / 32), mask);
48988ef38baSkettenis }
490f2e6717fSkettenis
491f2e6717fSkettenis /* Enable translations. */
49288ef38baSkettenis for (sid = 0; sid < sc->sc_nsid; sid++) {
49388ef38baSkettenis if (sc->sc_as[sid] == NULL)
49488ef38baSkettenis continue;
495f2e6717fSkettenis HWRITE4(sc, DART_TCR(sc, sid), sc->sc_tcr_translate_enable);
49688ef38baSkettenis }
497f2e6717fSkettenis
498f2e6717fSkettenis if (OF_is_compatible(sc->sc_node, "apple,t8110-dart")) {
499f2e6717fSkettenis HWRITE4(sc, DART_T8110_ERROR, HREAD4(sc, DART_T8110_ERROR));
500f2e6717fSkettenis HWRITE4(sc, DART_T8110_ERROR_MASK, 0);
501f2e6717fSkettenis } else {
502f2e6717fSkettenis HWRITE4(sc, DART_T8020_ERROR, HREAD4(sc, DART_T8020_ERROR));
503f2e6717fSkettenis }
504f2e6717fSkettenis }
505f2e6717fSkettenis
506f2e6717fSkettenis int
apldart_activate(struct device * self,int act)507f2e6717fSkettenis apldart_activate(struct device *self, int act)
508f2e6717fSkettenis {
509f2e6717fSkettenis struct apldart_softc *sc = (struct apldart_softc *)self;
510f2e6717fSkettenis
511f2e6717fSkettenis switch (act) {
512f2e6717fSkettenis case DVACT_SUSPEND:
513f2e6717fSkettenis apldart_suspend(sc);
514f2e6717fSkettenis break;
515f2e6717fSkettenis case DVACT_RESUME:
516f2e6717fSkettenis apldart_resume(sc);
517f2e6717fSkettenis break;
518f2e6717fSkettenis }
519f2e6717fSkettenis
520f2e6717fSkettenis return 0;
521f2e6717fSkettenis }
522f2e6717fSkettenis
523494ba95aSkettenis void
apldart_init_locked_stream(struct apldart_stream * as)524494ba95aSkettenis apldart_init_locked_stream(struct apldart_stream *as)
525494ba95aSkettenis {
526494ba95aSkettenis struct apldart_softc *sc = as->as_sc;
527494ba95aSkettenis uint32_t ttbr;
528494ba95aSkettenis vaddr_t startva, endva, va;
529494ba95aSkettenis paddr_t pa;
530b4036ef4Skettenis bus_addr_t dva, dvaend, dvabase;
531494ba95aSkettenis volatile uint64_t *l1;
532494ba95aSkettenis int nl1, nl2, ntte;
533494ba95aSkettenis int idx;
534494ba95aSkettenis
535494ba95aSkettenis for (idx = 0; idx < sc->sc_nttbr; idx++) {
536494ba95aSkettenis ttbr = HREAD4(sc, DART_TTBR(sc, as->as_sid, idx));
537494ba95aSkettenis if ((ttbr & sc->sc_ttbr_valid) == 0)
538494ba95aSkettenis break;
539494ba95aSkettenis }
540494ba95aSkettenis KASSERT(idx > 0);
541494ba95aSkettenis
542494ba95aSkettenis nl2 = idx * (DART_PAGE_SIZE / sizeof(uint64_t));
543494ba95aSkettenis ntte = nl2 * (DART_PAGE_SIZE / sizeof(uint64_t));
544494ba95aSkettenis
545b4036ef4Skettenis dvabase = sc->sc_dvabase & ~sc->sc_dvamask;
546b4036ef4Skettenis dvaend = dvabase + (bus_addr_t)ntte * DART_PAGE_SIZE;
547494ba95aSkettenis if (dvaend < sc->sc_dvaend)
548494ba95aSkettenis sc->sc_dvaend = dvaend;
549494ba95aSkettenis
550b4036ef4Skettenis as->as_dvamap = extent_create(sc->sc_dev.dv_xname, 0, ULONG_MAX,
551b4036ef4Skettenis M_DEVBUF, NULL, 0, EX_WAITOK | EX_NOCOALESCE);
552b4036ef4Skettenis if (sc->sc_dvabase > 0) {
553b4036ef4Skettenis extent_alloc_region(as->as_dvamap, 0, sc->sc_dvabase,
554b4036ef4Skettenis EX_WAITOK);
555b4036ef4Skettenis }
556b4036ef4Skettenis if (sc->sc_dvaend < ULONG_MAX) {
557b4036ef4Skettenis extent_alloc_region(as->as_dvamap, sc->sc_dvaend + 1,
558b4036ef4Skettenis ULONG_MAX - sc->sc_dvaend, EX_WAITOK);
559b4036ef4Skettenis }
560494ba95aSkettenis
561b4036ef4Skettenis ntte = howmany((sc->sc_dvaend & sc->sc_dvamask), DART_PAGE_SIZE);
562494ba95aSkettenis nl2 = howmany(ntte, DART_PAGE_SIZE / sizeof(uint64_t));
563494ba95aSkettenis nl1 = howmany(nl2, DART_PAGE_SIZE / sizeof(uint64_t));
564494ba95aSkettenis
565494ba95aSkettenis as->as_l2 = mallocarray(nl2, sizeof(*as->as_l2),
566494ba95aSkettenis M_DEVBUF, M_WAITOK | M_ZERO);
567494ba95aSkettenis
568494ba95aSkettenis l1 = km_alloc(nl1 * DART_PAGE_SIZE, &kv_any, &kp_none, &kd_waitok);
569494ba95aSkettenis KASSERT(l1);
570494ba95aSkettenis
571494ba95aSkettenis for (idx = 0; idx < nl1; idx++) {
572494ba95aSkettenis startva = (vaddr_t)l1 + idx * DART_PAGE_SIZE;
573494ba95aSkettenis endva = startva + DART_PAGE_SIZE;
574494ba95aSkettenis ttbr = HREAD4(sc, DART_TTBR(sc, as->as_sid, idx));
575494ba95aSkettenis pa = (paddr_t)(ttbr & ~sc->sc_ttbr_valid) << DART_TTBR_SHIFT;
576494ba95aSkettenis for (va = startva; va < endva; va += PAGE_SIZE) {
577494ba95aSkettenis pmap_kenter_cache(va, pa, PROT_READ | PROT_WRITE,
578494ba95aSkettenis PMAP_CACHE_CI);
579494ba95aSkettenis pa += PAGE_SIZE;
580494ba95aSkettenis }
581494ba95aSkettenis }
582494ba95aSkettenis
583494ba95aSkettenis for (idx = 0; idx < nl2; idx++) {
584494ba95aSkettenis if (l1[idx] & DART_L1_TABLE) {
585494ba95aSkettenis dva = idx * (DART_PAGE_SIZE / sizeof(uint64_t)) *
586494ba95aSkettenis DART_PAGE_SIZE;
587494ba95aSkettenis dvaend = dva + DART_PAGE_SIZE * DART_PAGE_SIZE - 1;
588b4036ef4Skettenis extent_alloc_region(as->as_dvamap, dvabase + dva,
589b4036ef4Skettenis dvaend - dva + 1, EX_WAITOK | EX_CONFLICTOK);
590494ba95aSkettenis } else {
591494ba95aSkettenis as->as_l2[idx] = apldart_dmamem_alloc(sc->sc_dmat,
592494ba95aSkettenis DART_PAGE_SIZE, DART_PAGE_SIZE);
593494ba95aSkettenis pa = APLDART_DMA_DVA(as->as_l2[idx]);
594494ba95aSkettenis l1[idx] = (pa >> sc->sc_shift) | DART_L1_TABLE;
595494ba95aSkettenis }
596494ba95aSkettenis }
597494ba95aSkettenis sc->sc_flush_tlb(sc, as->as_sid);
598494ba95aSkettenis
599494ba95aSkettenis memcpy(&as->as_dmat, sc->sc_dmat, sizeof(*sc->sc_dmat));
600494ba95aSkettenis as->as_dmat._cookie = as;
601494ba95aSkettenis as->as_dmat._dmamap_create = apldart_dmamap_create;
602494ba95aSkettenis as->as_dmat._dmamap_destroy = apldart_dmamap_destroy;
603494ba95aSkettenis as->as_dmat._dmamap_load = apldart_dmamap_load;
604494ba95aSkettenis as->as_dmat._dmamap_load_mbuf = apldart_dmamap_load_mbuf;
605494ba95aSkettenis as->as_dmat._dmamap_load_uio = apldart_dmamap_load_uio;
606494ba95aSkettenis as->as_dmat._dmamap_load_raw = apldart_dmamap_load_raw;
607494ba95aSkettenis as->as_dmat._dmamap_unload = apldart_dmamap_unload;
608494ba95aSkettenis as->as_dmat._flags |= BUS_DMA_COHERENT;
609494ba95aSkettenis }
610494ba95aSkettenis
61188ef38baSkettenis struct apldart_stream *
apldart_alloc_stream(struct apldart_softc * sc,int sid)61288ef38baSkettenis apldart_alloc_stream(struct apldart_softc *sc, int sid)
61388ef38baSkettenis {
61488ef38baSkettenis struct apldart_stream *as;
61588ef38baSkettenis paddr_t pa;
61688ef38baSkettenis volatile uint64_t *l1;
61788ef38baSkettenis int idx, ntte, nl1, nl2;
61888ef38baSkettenis uint32_t mask;
61988ef38baSkettenis
62088ef38baSkettenis as = malloc(sizeof(*as), M_DEVBUF, M_WAITOK | M_ZERO);
62188ef38baSkettenis
62288ef38baSkettenis as->as_sc = sc;
62388ef38baSkettenis as->as_sid = sid;
62488ef38baSkettenis
625494ba95aSkettenis mtx_init(&as->as_dvamap_mtx, IPL_HIGH);
626494ba95aSkettenis
627494ba95aSkettenis if (sc->sc_locked || sc->sc_translating) {
628494ba95aSkettenis apldart_init_locked_stream(as);
629494ba95aSkettenis return as;
630494ba95aSkettenis }
631494ba95aSkettenis
632b4036ef4Skettenis as->as_dvamap = extent_create(sc->sc_dev.dv_xname, 0, ULONG_MAX,
633b4036ef4Skettenis M_DEVBUF, NULL, 0, EX_WAITOK | EX_NOCOALESCE);
634b4036ef4Skettenis if (sc->sc_dvabase > 0) {
635b4036ef4Skettenis extent_alloc_region(as->as_dvamap, 0, sc->sc_dvabase,
636b4036ef4Skettenis EX_WAITOK);
637b4036ef4Skettenis }
638b4036ef4Skettenis if (sc->sc_dvaend < ULONG_MAX) {
639b4036ef4Skettenis extent_alloc_region(as->as_dvamap, sc->sc_dvaend + 1,
640b4036ef4Skettenis ULONG_MAX - sc->sc_dvaend, EX_WAITOK);
641b4036ef4Skettenis }
64288ef38baSkettenis
64388ef38baSkettenis /*
64488ef38baSkettenis * Build translation tables. We pre-allocate the translation
64588ef38baSkettenis * tables for the entire aperture such that we don't have to
64688ef38baSkettenis * worry about growing them in an mpsafe manner later.
64788ef38baSkettenis */
64888ef38baSkettenis
649b4036ef4Skettenis ntte = howmany((sc->sc_dvaend & sc->sc_dvamask), DART_PAGE_SIZE);
65088ef38baSkettenis nl2 = howmany(ntte, DART_PAGE_SIZE / sizeof(uint64_t));
65188ef38baSkettenis nl1 = howmany(nl2, DART_PAGE_SIZE / sizeof(uint64_t));
65288ef38baSkettenis
65388ef38baSkettenis as->as_l1 = apldart_dmamem_alloc(sc->sc_dmat,
65488ef38baSkettenis nl1 * DART_PAGE_SIZE, DART_PAGE_SIZE);
65588ef38baSkettenis as->as_l2 = mallocarray(nl2, sizeof(*as->as_l2),
65688ef38baSkettenis M_DEVBUF, M_WAITOK | M_ZERO);
65788ef38baSkettenis
65888ef38baSkettenis l1 = APLDART_DMA_KVA(as->as_l1);
65988ef38baSkettenis for (idx = 0; idx < nl2; idx++) {
66088ef38baSkettenis as->as_l2[idx] = apldart_dmamem_alloc(sc->sc_dmat,
66188ef38baSkettenis DART_PAGE_SIZE, DART_PAGE_SIZE);
66288ef38baSkettenis pa = APLDART_DMA_DVA(as->as_l2[idx]);
66388ef38baSkettenis l1[idx] = (pa >> sc->sc_shift) | DART_L1_TABLE;
66488ef38baSkettenis }
66588ef38baSkettenis
66688ef38baSkettenis /* Install page tables. */
66788ef38baSkettenis pa = APLDART_DMA_DVA(as->as_l1);
66888ef38baSkettenis for (idx = 0; idx < nl1; idx++) {
66988ef38baSkettenis HWRITE4(sc, DART_TTBR(sc, sid, idx),
67088ef38baSkettenis (pa >> DART_TTBR_SHIFT) | sc->sc_ttbr_valid);
67188ef38baSkettenis pa += DART_PAGE_SIZE;
67288ef38baSkettenis }
67388ef38baSkettenis sc->sc_flush_tlb(sc, sid);
67488ef38baSkettenis
67588ef38baSkettenis /* Enable this stream. */
67688ef38baSkettenis mask = HREAD4(sc, DART_SID_ENABLE(sc, sid / 32));
67788ef38baSkettenis mask |= (1U << (sid % 32));
67888ef38baSkettenis HWRITE4(sc, DART_SID_ENABLE(sc, sid / 32), mask);
67988ef38baSkettenis
68088ef38baSkettenis /* Enable translations. */
68188ef38baSkettenis HWRITE4(sc, DART_TCR(sc, sid), sc->sc_tcr_translate_enable);
68288ef38baSkettenis
68388ef38baSkettenis memcpy(&as->as_dmat, sc->sc_dmat, sizeof(*sc->sc_dmat));
68488ef38baSkettenis as->as_dmat._cookie = as;
68588ef38baSkettenis as->as_dmat._dmamap_create = apldart_dmamap_create;
68688ef38baSkettenis as->as_dmat._dmamap_destroy = apldart_dmamap_destroy;
68788ef38baSkettenis as->as_dmat._dmamap_load = apldart_dmamap_load;
68888ef38baSkettenis as->as_dmat._dmamap_load_mbuf = apldart_dmamap_load_mbuf;
68988ef38baSkettenis as->as_dmat._dmamap_load_uio = apldart_dmamap_load_uio;
69088ef38baSkettenis as->as_dmat._dmamap_load_raw = apldart_dmamap_load_raw;
69188ef38baSkettenis as->as_dmat._dmamap_unload = apldart_dmamap_unload;
69288ef38baSkettenis as->as_dmat._flags |= BUS_DMA_COHERENT;
69388ef38baSkettenis
69488ef38baSkettenis return as;
69588ef38baSkettenis }
69688ef38baSkettenis
69755f7f351Skettenis bus_dma_tag_t
apldart_map(void * cookie,uint32_t * cells,bus_dma_tag_t dmat)69855f7f351Skettenis apldart_map(void *cookie, uint32_t *cells, bus_dma_tag_t dmat)
69955f7f351Skettenis {
70055f7f351Skettenis struct apldart_softc *sc = cookie;
70188ef38baSkettenis uint32_t sid = cells[0];
70255f7f351Skettenis
70388ef38baSkettenis KASSERT(sid < sc->sc_nsid);
70488ef38baSkettenis
70588ef38baSkettenis if (sc->sc_as[sid] == NULL)
70688ef38baSkettenis sc->sc_as[sid] = apldart_alloc_stream(sc, sid);
70788ef38baSkettenis
70888ef38baSkettenis return &sc->sc_as[sid]->as_dmat;
70955f7f351Skettenis }
71055f7f351Skettenis
711415019ceSpatrick void
apldart_reserve(void * cookie,uint32_t * cells,bus_addr_t addr,bus_size_t size)712415019ceSpatrick apldart_reserve(void *cookie, uint32_t *cells, bus_addr_t addr, bus_size_t size)
713415019ceSpatrick {
714415019ceSpatrick }
715415019ceSpatrick
71655f7f351Skettenis int
apldart_t8020_intr(void * arg)717bd26d413Skettenis apldart_t8020_intr(void *arg)
71855f7f351Skettenis {
71955f7f351Skettenis struct apldart_softc *sc = arg;
72055f7f351Skettenis
721bd26d413Skettenis panic("%s: error 0x%08x addr 0x%08x%08x\n",
722bd26d413Skettenis sc->sc_dev.dv_xname, HREAD4(sc, DART_T8020_ERROR),
723bd26d413Skettenis HREAD4(sc, DART_T8020_ERROR_ADDR_HI),
724bd26d413Skettenis HREAD4(sc, DART_T8020_ERROR_ADDR_LO));
725bd26d413Skettenis }
726bd26d413Skettenis
727bd26d413Skettenis int
apldart_t8110_intr(void * arg)728bd26d413Skettenis apldart_t8110_intr(void *arg)
729bd26d413Skettenis {
730bd26d413Skettenis struct apldart_softc *sc = arg;
731bd26d413Skettenis
732bd26d413Skettenis panic("%s: error 0x%08x addr 0x%08x%08x\n",
733bd26d413Skettenis sc->sc_dev.dv_xname, HREAD4(sc, DART_T8110_ERROR),
734bd26d413Skettenis HREAD4(sc, DART_T8110_ERROR_ADDR_HI),
735bd26d413Skettenis HREAD4(sc, DART_T8110_ERROR_ADDR_LO));
73655f7f351Skettenis }
73755f7f351Skettenis
73855f7f351Skettenis void
apldart_t8020_flush_tlb(struct apldart_softc * sc,int sid)73988ef38baSkettenis apldart_t8020_flush_tlb(struct apldart_softc *sc, int sid)
74055f7f351Skettenis {
74188ef38baSkettenis uint32_t mask;
74288ef38baSkettenis
74355f7f351Skettenis __asm volatile ("dsb sy" ::: "memory");
74455f7f351Skettenis
74588ef38baSkettenis if (sid == -1)
74688ef38baSkettenis mask = DART_ALL_STREAMS(sc);
74788ef38baSkettenis else
74888ef38baSkettenis mask = (1U << sid);
74988ef38baSkettenis
75088ef38baSkettenis HWRITE4(sc, DART_T8020_TLB_SIDMASK, mask);
751bd26d413Skettenis HWRITE4(sc, DART_T8020_TLB_CMD, DART_T8020_TLB_CMD_FLUSH);
752bd26d413Skettenis while (HREAD4(sc, DART_T8020_TLB_CMD) & DART_T8020_TLB_CMD_BUSY)
753bd26d413Skettenis CPU_BUSY_CYCLE();
754bd26d413Skettenis }
755bd26d413Skettenis
756bd26d413Skettenis void
apldart_t8110_flush_tlb(struct apldart_softc * sc,int sid)75788ef38baSkettenis apldart_t8110_flush_tlb(struct apldart_softc *sc, int sid)
758bd26d413Skettenis {
75988ef38baSkettenis uint32_t cmd;
76088ef38baSkettenis
761bd26d413Skettenis __asm volatile ("dsb sy" ::: "memory");
762bd26d413Skettenis
76388ef38baSkettenis if (sid == -1)
76488ef38baSkettenis cmd = DART_T8110_TLB_CMD_FLUSH_ALL;
76588ef38baSkettenis else
76688ef38baSkettenis cmd = DART_T8110_TLB_CMD_FLUSH_SID | sid;
76788ef38baSkettenis
76888ef38baSkettenis HWRITE4(sc, DART_T8110_TLB_CMD, cmd);
769bd26d413Skettenis while (HREAD4(sc, DART_T8110_TLB_CMD) & DART_T8110_TLB_CMD_BUSY)
77055f7f351Skettenis CPU_BUSY_CYCLE();
77155f7f351Skettenis }
77255f7f351Skettenis
77355f7f351Skettenis volatile uint64_t *
apldart_lookup_tte(struct apldart_stream * as,bus_addr_t dva)77488ef38baSkettenis apldart_lookup_tte(struct apldart_stream *as, bus_addr_t dva)
77555f7f351Skettenis {
776b4036ef4Skettenis int idx = (dva & as->as_sc->sc_dvamask) / DART_PAGE_SIZE;
77755f7f351Skettenis int l2_idx = idx / (DART_PAGE_SIZE / sizeof(uint64_t));
77855f7f351Skettenis int tte_idx = idx % (DART_PAGE_SIZE / sizeof(uint64_t));
77955f7f351Skettenis volatile uint64_t *l2;
78055f7f351Skettenis
78188ef38baSkettenis l2 = APLDART_DMA_KVA(as->as_l2[l2_idx]);
78255f7f351Skettenis return &l2[tte_idx];
78355f7f351Skettenis }
78455f7f351Skettenis
78555f7f351Skettenis int
apldart_load_map(struct apldart_stream * as,bus_dmamap_t map,int flags)786b4036ef4Skettenis apldart_load_map(struct apldart_stream *as, bus_dmamap_t map, int flags)
78755f7f351Skettenis {
78888ef38baSkettenis struct apldart_softc *sc = as->as_sc;
78955f7f351Skettenis struct apldart_map_state *ams = map->_dm_cookie;
79055f7f351Skettenis volatile uint64_t *tte;
79155f7f351Skettenis int seg, error;
79255f7f351Skettenis
79355f7f351Skettenis /* For each segment. */
79455f7f351Skettenis for (seg = 0; seg < map->dm_nsegs; seg++) {
79555f7f351Skettenis paddr_t pa = map->dm_segs[seg]._ds_paddr;
79655f7f351Skettenis psize_t off = pa - apldart_trunc_page(pa);
7972c847e44Skettenis psize_t start, end;
79855f7f351Skettenis u_long len, dva;
79955f7f351Skettenis
80055f7f351Skettenis len = apldart_round_page(map->dm_segs[seg].ds_len + off);
80155f7f351Skettenis
80288ef38baSkettenis mtx_enter(&as->as_dvamap_mtx);
803b4036ef4Skettenis if (flags & BUS_DMA_FIXED) {
804b4036ef4Skettenis dva = apldart_trunc_page(map->dm_segs[seg].ds_addr);
805b4036ef4Skettenis /* XXX truncate because "apple,dma-range" mismatch */
806b4036ef4Skettenis if (dva > sc->sc_dvaend)
807b4036ef4Skettenis dva &= sc->sc_dvamask;
808b4036ef4Skettenis error = extent_alloc_region_with_descr(as->as_dvamap,
809b4036ef4Skettenis dva, len, EX_NOWAIT, &ams[seg].ams_er);
810b4036ef4Skettenis } else {
81188ef38baSkettenis error = extent_alloc_with_descr(as->as_dvamap, len,
812b4036ef4Skettenis DART_PAGE_SIZE, 0, 0, EX_NOWAIT, &ams[seg].ams_er,
813b4036ef4Skettenis &dva);
814b4036ef4Skettenis }
81588ef38baSkettenis mtx_leave(&as->as_dvamap_mtx);
81655f7f351Skettenis if (error) {
81788ef38baSkettenis apldart_unload_map(as, map);
81855f7f351Skettenis return error;
81955f7f351Skettenis }
82055f7f351Skettenis
82155f7f351Skettenis ams[seg].ams_dva = dva;
82255f7f351Skettenis ams[seg].ams_len = len;
82355f7f351Skettenis
82455f7f351Skettenis map->dm_segs[seg].ds_addr = dva + off;
82555f7f351Skettenis
82655f7f351Skettenis pa = apldart_trunc_page(pa);
8272c847e44Skettenis start = apldart_trunc_offset(off);
8282c847e44Skettenis end = DART_PAGE_MASK;
82955f7f351Skettenis while (len > 0) {
8302c847e44Skettenis if (len < DART_PAGE_SIZE)
8312c847e44Skettenis end = apldart_round_offset(len) - 1;
8322c847e44Skettenis
83388ef38baSkettenis tte = apldart_lookup_tte(as, dva);
8345d503355Skettenis *tte = (pa >> sc->sc_shift) | DART_L2_VALID |
8352c847e44Skettenis DART_L2_START(start) | DART_L2_END(end);
83655f7f351Skettenis
83755f7f351Skettenis pa += DART_PAGE_SIZE;
83855f7f351Skettenis dva += DART_PAGE_SIZE;
83955f7f351Skettenis len -= DART_PAGE_SIZE;
8402c847e44Skettenis start = 0;
84155f7f351Skettenis }
84255f7f351Skettenis }
84355f7f351Skettenis
84488ef38baSkettenis sc->sc_flush_tlb(sc, as->as_sid);
84555f7f351Skettenis
84655f7f351Skettenis return 0;
84755f7f351Skettenis }
84855f7f351Skettenis
84955f7f351Skettenis void
apldart_unload_map(struct apldart_stream * as,bus_dmamap_t map)85088ef38baSkettenis apldart_unload_map(struct apldart_stream *as, bus_dmamap_t map)
85155f7f351Skettenis {
85288ef38baSkettenis struct apldart_softc *sc = as->as_sc;
85355f7f351Skettenis struct apldart_map_state *ams = map->_dm_cookie;
85455f7f351Skettenis volatile uint64_t *tte;
85555f7f351Skettenis int seg, error;
85655f7f351Skettenis
85755f7f351Skettenis /* For each segment. */
85855f7f351Skettenis for (seg = 0; seg < map->dm_nsegs; seg++) {
85955f7f351Skettenis u_long len, dva;
86055f7f351Skettenis
86155f7f351Skettenis if (ams[seg].ams_len == 0)
86255f7f351Skettenis continue;
86355f7f351Skettenis
86455f7f351Skettenis dva = ams[seg].ams_dva;
86555f7f351Skettenis len = ams[seg].ams_len;
86655f7f351Skettenis
86755f7f351Skettenis while (len > 0) {
86888ef38baSkettenis tte = apldart_lookup_tte(as, dva);
86955f7f351Skettenis *tte = DART_L2_INVAL;
87055f7f351Skettenis
87155f7f351Skettenis dva += DART_PAGE_SIZE;
87255f7f351Skettenis len -= DART_PAGE_SIZE;
87355f7f351Skettenis }
87455f7f351Skettenis
87588ef38baSkettenis mtx_enter(&as->as_dvamap_mtx);
87688ef38baSkettenis error = extent_free(as->as_dvamap, ams[seg].ams_dva,
87755f7f351Skettenis ams[seg].ams_len, EX_NOWAIT);
87888ef38baSkettenis mtx_leave(&as->as_dvamap_mtx);
87955f7f351Skettenis
88055f7f351Skettenis KASSERT(error == 0);
88155f7f351Skettenis
88255f7f351Skettenis ams[seg].ams_dva = 0;
88355f7f351Skettenis ams[seg].ams_len = 0;
88455f7f351Skettenis }
88555f7f351Skettenis
88688ef38baSkettenis sc->sc_flush_tlb(sc, as->as_sid);
88755f7f351Skettenis }
88855f7f351Skettenis
88955f7f351Skettenis int
apldart_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)89055f7f351Skettenis apldart_dmamap_create(bus_dma_tag_t t, bus_size_t size, int nsegments,
89155f7f351Skettenis bus_size_t maxsegsz, bus_size_t boundary, int flags, bus_dmamap_t *dmamap)
89255f7f351Skettenis {
89388ef38baSkettenis struct apldart_stream *as = t->_cookie;
89488ef38baSkettenis struct apldart_softc *sc = as->as_sc;
89555f7f351Skettenis struct apldart_map_state *ams;
89655f7f351Skettenis bus_dmamap_t map;
89755f7f351Skettenis int error;
89855f7f351Skettenis
89955f7f351Skettenis error = sc->sc_dmat->_dmamap_create(sc->sc_dmat, size, nsegments,
90055f7f351Skettenis maxsegsz, boundary, flags, &map);
90155f7f351Skettenis if (error)
90255f7f351Skettenis return error;
90355f7f351Skettenis
90455f7f351Skettenis ams = mallocarray(map->_dm_segcnt, sizeof(*ams), M_DEVBUF,
90555f7f351Skettenis (flags & BUS_DMA_NOWAIT) ? (M_NOWAIT|M_ZERO) : (M_WAITOK|M_ZERO));
90655f7f351Skettenis if (ams == NULL) {
90755f7f351Skettenis sc->sc_dmat->_dmamap_destroy(sc->sc_dmat, map);
90855f7f351Skettenis return ENOMEM;
90955f7f351Skettenis }
91055f7f351Skettenis
91155f7f351Skettenis map->_dm_cookie = ams;
91255f7f351Skettenis *dmamap = map;
91355f7f351Skettenis return 0;
91455f7f351Skettenis }
91555f7f351Skettenis
91655f7f351Skettenis void
apldart_dmamap_destroy(bus_dma_tag_t t,bus_dmamap_t map)91755f7f351Skettenis apldart_dmamap_destroy(bus_dma_tag_t t, bus_dmamap_t map)
91855f7f351Skettenis {
91988ef38baSkettenis struct apldart_stream *as = t->_cookie;
92088ef38baSkettenis struct apldart_softc *sc = as->as_sc;
92155f7f351Skettenis struct apldart_map_state *ams = map->_dm_cookie;
92255f7f351Skettenis
923f1481cb2Skettenis if (map->dm_nsegs)
924f1481cb2Skettenis apldart_dmamap_unload(t, map);
925f1481cb2Skettenis
92655f7f351Skettenis free(ams, M_DEVBUF, map->_dm_segcnt * sizeof(*ams));
92755f7f351Skettenis sc->sc_dmat->_dmamap_destroy(sc->sc_dmat, map);
92855f7f351Skettenis }
92955f7f351Skettenis
93055f7f351Skettenis int
apldart_dmamap_load(bus_dma_tag_t t,bus_dmamap_t map,void * buf,size_t buflen,struct proc * p,int flags)93155f7f351Skettenis apldart_dmamap_load(bus_dma_tag_t t, bus_dmamap_t map, void *buf,
93255f7f351Skettenis size_t buflen, struct proc *p, int flags)
93355f7f351Skettenis {
93488ef38baSkettenis struct apldart_stream *as = t->_cookie;
93588ef38baSkettenis struct apldart_softc *sc = as->as_sc;
93655f7f351Skettenis int error;
93755f7f351Skettenis
93855f7f351Skettenis error = sc->sc_dmat->_dmamap_load(sc->sc_dmat, map,
93955f7f351Skettenis buf, buflen, p, flags);
94055f7f351Skettenis if (error)
94155f7f351Skettenis return error;
94255f7f351Skettenis
943b4036ef4Skettenis error = apldart_load_map(as, map, flags);
94455f7f351Skettenis if (error)
94555f7f351Skettenis sc->sc_dmat->_dmamap_unload(sc->sc_dmat, map);
94655f7f351Skettenis
94755f7f351Skettenis return error;
94855f7f351Skettenis }
94955f7f351Skettenis
95055f7f351Skettenis int
apldart_dmamap_load_mbuf(bus_dma_tag_t t,bus_dmamap_t map,struct mbuf * m,int flags)95155f7f351Skettenis apldart_dmamap_load_mbuf(bus_dma_tag_t t, bus_dmamap_t map,
95255f7f351Skettenis struct mbuf *m, int flags)
95355f7f351Skettenis {
95488ef38baSkettenis struct apldart_stream *as = t->_cookie;
95588ef38baSkettenis struct apldart_softc *sc = as->as_sc;
95655f7f351Skettenis int error;
95755f7f351Skettenis
95855f7f351Skettenis error = sc->sc_dmat->_dmamap_load_mbuf(sc->sc_dmat, map,
95955f7f351Skettenis m, flags);
96055f7f351Skettenis if (error)
96155f7f351Skettenis return error;
96255f7f351Skettenis
963b4036ef4Skettenis error = apldart_load_map(as, map, flags);
96455f7f351Skettenis if (error)
96555f7f351Skettenis sc->sc_dmat->_dmamap_unload(sc->sc_dmat, map);
96655f7f351Skettenis
96755f7f351Skettenis return error;
96855f7f351Skettenis }
96955f7f351Skettenis
97055f7f351Skettenis int
apldart_dmamap_load_uio(bus_dma_tag_t t,bus_dmamap_t map,struct uio * uio,int flags)97155f7f351Skettenis apldart_dmamap_load_uio(bus_dma_tag_t t, bus_dmamap_t map,
97255f7f351Skettenis struct uio *uio, int flags)
97355f7f351Skettenis {
97488ef38baSkettenis struct apldart_stream *as = t->_cookie;
97588ef38baSkettenis struct apldart_softc *sc = as->as_sc;
97655f7f351Skettenis int error;
97755f7f351Skettenis
97855f7f351Skettenis error = sc->sc_dmat->_dmamap_load_uio(sc->sc_dmat, map,
97955f7f351Skettenis uio, flags);
98055f7f351Skettenis if (error)
98155f7f351Skettenis return error;
98255f7f351Skettenis
983b4036ef4Skettenis error = apldart_load_map(as, map, flags);
98455f7f351Skettenis if (error)
98555f7f351Skettenis sc->sc_dmat->_dmamap_unload(sc->sc_dmat, map);
98655f7f351Skettenis
98755f7f351Skettenis return error;
98855f7f351Skettenis }
98955f7f351Skettenis
99055f7f351Skettenis int
apldart_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)99155f7f351Skettenis apldart_dmamap_load_raw(bus_dma_tag_t t, bus_dmamap_t map,
99255f7f351Skettenis bus_dma_segment_t *segs, int nsegs, bus_size_t size, int flags)
99355f7f351Skettenis {
99488ef38baSkettenis struct apldart_stream *as = t->_cookie;
99588ef38baSkettenis struct apldart_softc *sc = as->as_sc;
996b4036ef4Skettenis int i, error;
99755f7f351Skettenis
998b4036ef4Skettenis if (flags & BUS_DMA_FIXED) {
999b4036ef4Skettenis if (map->dm_nsegs != nsegs)
1000b4036ef4Skettenis return EINVAL;
1001b4036ef4Skettenis for (i = 0; i < nsegs; i++) {
1002b4036ef4Skettenis if (map->dm_segs[i].ds_len != segs[i].ds_len)
1003b4036ef4Skettenis return EINVAL;
1004b4036ef4Skettenis map->dm_segs[i]._ds_paddr = segs[i].ds_addr;
1005b4036ef4Skettenis map->dm_segs[i]._ds_vaddr = segs[i]._ds_vaddr;
1006b4036ef4Skettenis }
1007b4036ef4Skettenis } else {
100855f7f351Skettenis error = sc->sc_dmat->_dmamap_load_raw(sc->sc_dmat, map,
100955f7f351Skettenis segs, nsegs, size, flags);
101055f7f351Skettenis if (error)
101155f7f351Skettenis return error;
1012b4036ef4Skettenis }
101355f7f351Skettenis
1014b4036ef4Skettenis error = apldart_load_map(as, map, flags);
101555f7f351Skettenis if (error)
101655f7f351Skettenis sc->sc_dmat->_dmamap_unload(sc->sc_dmat, map);
101755f7f351Skettenis
101855f7f351Skettenis return error;
101955f7f351Skettenis }
102055f7f351Skettenis
102155f7f351Skettenis void
apldart_dmamap_unload(bus_dma_tag_t t,bus_dmamap_t map)102255f7f351Skettenis apldart_dmamap_unload(bus_dma_tag_t t, bus_dmamap_t map)
102355f7f351Skettenis {
102488ef38baSkettenis struct apldart_stream *as = t->_cookie;
102588ef38baSkettenis struct apldart_softc *sc = as->as_sc;
102655f7f351Skettenis
102788ef38baSkettenis apldart_unload_map(as, map);
102855f7f351Skettenis sc->sc_dmat->_dmamap_unload(sc->sc_dmat, map);
102955f7f351Skettenis }
103055f7f351Skettenis
103155f7f351Skettenis struct apldart_dmamem *
apldart_dmamem_alloc(bus_dma_tag_t dmat,bus_size_t size,bus_size_t align)103255f7f351Skettenis apldart_dmamem_alloc(bus_dma_tag_t dmat, bus_size_t size, bus_size_t align)
103355f7f351Skettenis {
103455f7f351Skettenis struct apldart_dmamem *adm;
103555f7f351Skettenis int nsegs;
103655f7f351Skettenis
103755f7f351Skettenis adm = malloc(sizeof(*adm), M_DEVBUF, M_WAITOK | M_ZERO);
103855f7f351Skettenis adm->adm_size = size;
103955f7f351Skettenis
104055f7f351Skettenis if (bus_dmamap_create(dmat, size, 1, size, 0,
104155f7f351Skettenis BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW, &adm->adm_map) != 0)
104255f7f351Skettenis goto admfree;
104355f7f351Skettenis
104455f7f351Skettenis if (bus_dmamem_alloc(dmat, size, align, 0, &adm->adm_seg, 1,
104555f7f351Skettenis &nsegs, BUS_DMA_WAITOK | BUS_DMA_ZERO) != 0)
104655f7f351Skettenis goto destroy;
104755f7f351Skettenis
104855f7f351Skettenis if (bus_dmamem_map(dmat, &adm->adm_seg, nsegs, size,
104955f7f351Skettenis &adm->adm_kva, BUS_DMA_WAITOK | BUS_DMA_NOCACHE) != 0)
105055f7f351Skettenis goto free;
105155f7f351Skettenis
105255f7f351Skettenis if (bus_dmamap_load_raw(dmat, adm->adm_map, &adm->adm_seg,
105355f7f351Skettenis nsegs, size, BUS_DMA_WAITOK) != 0)
105455f7f351Skettenis goto unmap;
105555f7f351Skettenis
105655f7f351Skettenis return adm;
105755f7f351Skettenis
105855f7f351Skettenis unmap:
105955f7f351Skettenis bus_dmamem_unmap(dmat, adm->adm_kva, size);
106055f7f351Skettenis free:
106155f7f351Skettenis bus_dmamem_free(dmat, &adm->adm_seg, 1);
106255f7f351Skettenis destroy:
106355f7f351Skettenis bus_dmamap_destroy(dmat, adm->adm_map);
106455f7f351Skettenis admfree:
106555f7f351Skettenis free(adm, M_DEVBUF, sizeof(*adm));
106655f7f351Skettenis
106755f7f351Skettenis return NULL;
106855f7f351Skettenis }
106955f7f351Skettenis
107055f7f351Skettenis void
apldart_dmamem_free(bus_dma_tag_t dmat,struct apldart_dmamem * adm)107155f7f351Skettenis apldart_dmamem_free(bus_dma_tag_t dmat, struct apldart_dmamem *adm)
107255f7f351Skettenis {
107355f7f351Skettenis bus_dmamem_unmap(dmat, adm->adm_kva, adm->adm_size);
107455f7f351Skettenis bus_dmamem_free(dmat, &adm->adm_seg, 1);
107555f7f351Skettenis bus_dmamap_destroy(dmat, adm->adm_map);
107655f7f351Skettenis free(adm, M_DEVBUF, sizeof(*adm));
107755f7f351Skettenis }
1078