xref: /openbsd-src/sys/arch/arm64/dev/smmu.c (revision 6258f8b260d7ba588c4c691207dc68d4409a9547)
1*6258f8b2Spatrick /* $OpenBSD: smmu.c,v 1.21 2022/09/11 10:28:56 patrick Exp $ */
21a30e4e7Spatrick /*
31a30e4e7Spatrick  * Copyright (c) 2008-2009,2014-2016 Dale Rahn <drahn@dalerahn.com>
41a30e4e7Spatrick  * Copyright (c) 2021 Patrick Wildt <patrick@blueri.se>
51a30e4e7Spatrick  *
61a30e4e7Spatrick  * Permission to use, copy, modify, and distribute this software for any
71a30e4e7Spatrick  * purpose with or without fee is hereby granted, provided that the above
81a30e4e7Spatrick  * copyright notice and this permission notice appear in all copies.
91a30e4e7Spatrick  *
101a30e4e7Spatrick  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
111a30e4e7Spatrick  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
121a30e4e7Spatrick  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
131a30e4e7Spatrick  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
141a30e4e7Spatrick  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
151a30e4e7Spatrick  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
161a30e4e7Spatrick  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
171a30e4e7Spatrick  */
181a30e4e7Spatrick 
191a30e4e7Spatrick #include <sys/param.h>
201a30e4e7Spatrick #include <sys/systm.h>
211a30e4e7Spatrick #include <sys/device.h>
221a30e4e7Spatrick #include <sys/pool.h>
231a30e4e7Spatrick #include <sys/atomic.h>
241a30e4e7Spatrick 
251a30e4e7Spatrick #include <machine/bus.h>
26*6258f8b2Spatrick #include <machine/cpufunc.h>
271a30e4e7Spatrick 
281a30e4e7Spatrick #include <uvm/uvm_extern.h>
291a30e4e7Spatrick #include <arm64/vmparam.h>
301a30e4e7Spatrick #include <arm64/pmap.h>
311a30e4e7Spatrick 
321a30e4e7Spatrick #include <dev/pci/pcivar.h>
331a30e4e7Spatrick #include <arm64/dev/smmuvar.h>
341a30e4e7Spatrick #include <arm64/dev/smmureg.h>
351a30e4e7Spatrick 
360aa222ffSpatrick struct smmu_map_state {
370aa222ffSpatrick 	struct extent_region	sms_er;
380aa222ffSpatrick 	bus_addr_t		sms_dva;
390aa222ffSpatrick 	bus_size_t		sms_len;
40b9720b93Spatrick 	bus_size_t		sms_loaded;
410aa222ffSpatrick };
420aa222ffSpatrick 
431a30e4e7Spatrick struct smmuvp0 {
441a30e4e7Spatrick 	uint64_t l0[VP_IDX0_CNT];
451a30e4e7Spatrick 	struct smmuvp1 *vp[VP_IDX0_CNT];
461a30e4e7Spatrick };
471a30e4e7Spatrick 
481a30e4e7Spatrick struct smmuvp1 {
491a30e4e7Spatrick 	uint64_t l1[VP_IDX1_CNT];
501a30e4e7Spatrick 	struct smmuvp2 *vp[VP_IDX1_CNT];
511a30e4e7Spatrick };
521a30e4e7Spatrick 
531a30e4e7Spatrick struct smmuvp2 {
541a30e4e7Spatrick 	uint64_t l2[VP_IDX2_CNT];
551a30e4e7Spatrick 	struct smmuvp3 *vp[VP_IDX2_CNT];
561a30e4e7Spatrick };
571a30e4e7Spatrick 
581a30e4e7Spatrick struct smmuvp3 {
591a30e4e7Spatrick 	uint64_t l3[VP_IDX3_CNT];
601a30e4e7Spatrick };
611a30e4e7Spatrick 
621a30e4e7Spatrick CTASSERT(sizeof(struct smmuvp0) == sizeof(struct smmuvp1));
631a30e4e7Spatrick CTASSERT(sizeof(struct smmuvp0) == sizeof(struct smmuvp2));
642322545bSpatrick CTASSERT(sizeof(struct smmuvp0) != sizeof(struct smmuvp3));
651a30e4e7Spatrick 
661a30e4e7Spatrick uint32_t smmu_gr0_read_4(struct smmu_softc *, bus_size_t);
671a30e4e7Spatrick void smmu_gr0_write_4(struct smmu_softc *, bus_size_t, uint32_t);
681a30e4e7Spatrick uint32_t smmu_gr1_read_4(struct smmu_softc *, bus_size_t);
691a30e4e7Spatrick void smmu_gr1_write_4(struct smmu_softc *, bus_size_t, uint32_t);
701a30e4e7Spatrick uint32_t smmu_cb_read_4(struct smmu_softc *, int, bus_size_t);
711a30e4e7Spatrick void smmu_cb_write_4(struct smmu_softc *, int, bus_size_t, uint32_t);
721a30e4e7Spatrick uint64_t smmu_cb_read_8(struct smmu_softc *, int, bus_size_t);
731a30e4e7Spatrick void smmu_cb_write_8(struct smmu_softc *, int, bus_size_t, uint64_t);
741a30e4e7Spatrick 
751a30e4e7Spatrick void smmu_tlb_sync_global(struct smmu_softc *);
761a30e4e7Spatrick void smmu_tlb_sync_context(struct smmu_domain *);
771a30e4e7Spatrick 
781a30e4e7Spatrick struct smmu_domain *smmu_domain_lookup(struct smmu_softc *, uint32_t);
791a30e4e7Spatrick struct smmu_domain *smmu_domain_create(struct smmu_softc *, uint32_t);
801a30e4e7Spatrick 
811a30e4e7Spatrick void smmu_set_l1(struct smmu_domain *, uint64_t, struct smmuvp1 *);
821a30e4e7Spatrick void smmu_set_l2(struct smmu_domain *, uint64_t, struct smmuvp1 *,
831a30e4e7Spatrick     struct smmuvp2 *);
841a30e4e7Spatrick void smmu_set_l3(struct smmu_domain *, uint64_t, struct smmuvp2 *,
851a30e4e7Spatrick     struct smmuvp3 *);
861a30e4e7Spatrick 
872322545bSpatrick int smmu_vp_lookup(struct smmu_domain *, vaddr_t, uint64_t **);
882322545bSpatrick int smmu_vp_enter(struct smmu_domain *, vaddr_t, uint64_t **, int);
891a30e4e7Spatrick 
902322545bSpatrick uint64_t smmu_fill_pte(struct smmu_domain *, vaddr_t, paddr_t,
911a30e4e7Spatrick     vm_prot_t, int, int);
922322545bSpatrick void smmu_pte_update(struct smmu_domain *, uint64_t, uint64_t *);
932322545bSpatrick void smmu_pte_remove(struct smmu_domain *, vaddr_t);
941a30e4e7Spatrick 
951a30e4e7Spatrick int smmu_enter(struct smmu_domain *, vaddr_t, paddr_t, vm_prot_t, int, int);
96b9720b93Spatrick void smmu_map(struct smmu_domain *, vaddr_t, paddr_t, vm_prot_t, int, int);
97b9720b93Spatrick void smmu_unmap(struct smmu_domain *, vaddr_t);
981a30e4e7Spatrick void smmu_remove(struct smmu_domain *, vaddr_t);
991a30e4e7Spatrick 
1001a30e4e7Spatrick int smmu_load_map(struct smmu_domain *, bus_dmamap_t);
1011a30e4e7Spatrick void smmu_unload_map(struct smmu_domain *, bus_dmamap_t);
1021a30e4e7Spatrick 
1031a30e4e7Spatrick int smmu_dmamap_create(bus_dma_tag_t , bus_size_t, int,
1041a30e4e7Spatrick      bus_size_t, bus_size_t, int, bus_dmamap_t *);
1051a30e4e7Spatrick void smmu_dmamap_destroy(bus_dma_tag_t , bus_dmamap_t);
1061a30e4e7Spatrick int smmu_dmamap_load(bus_dma_tag_t , bus_dmamap_t, void *,
1071a30e4e7Spatrick      bus_size_t, struct proc *, int);
1081a30e4e7Spatrick int smmu_dmamap_load_mbuf(bus_dma_tag_t , bus_dmamap_t,
1091a30e4e7Spatrick      struct mbuf *, int);
1101a30e4e7Spatrick int smmu_dmamap_load_uio(bus_dma_tag_t , bus_dmamap_t,
1111a30e4e7Spatrick      struct uio *, int);
1121a30e4e7Spatrick int smmu_dmamap_load_raw(bus_dma_tag_t , bus_dmamap_t,
1131a30e4e7Spatrick      bus_dma_segment_t *, int, bus_size_t, int);
1141a30e4e7Spatrick void smmu_dmamap_unload(bus_dma_tag_t , bus_dmamap_t);
1151a30e4e7Spatrick 
1161a30e4e7Spatrick struct cfdriver smmu_cd = {
1171a30e4e7Spatrick 	NULL, "smmu", DV_DULL
1181a30e4e7Spatrick };
1191a30e4e7Spatrick 
1201a30e4e7Spatrick int
smmu_attach(struct smmu_softc * sc)1211a30e4e7Spatrick smmu_attach(struct smmu_softc *sc)
1221a30e4e7Spatrick {
1231a30e4e7Spatrick 	uint32_t reg;
1241a30e4e7Spatrick 	int i;
1251a30e4e7Spatrick 
1261a30e4e7Spatrick 	SIMPLEQ_INIT(&sc->sc_domains);
1271a30e4e7Spatrick 
1281a30e4e7Spatrick 	pool_init(&sc->sc_vp_pool, sizeof(struct smmuvp0), PAGE_SIZE, IPL_VM, 0,
1291a30e4e7Spatrick 	    "smmu_vp", NULL);
1301a30e4e7Spatrick 	pool_setlowat(&sc->sc_vp_pool, 20);
1312322545bSpatrick 	pool_init(&sc->sc_vp3_pool, sizeof(struct smmuvp3), PAGE_SIZE, IPL_VM, 0,
1322322545bSpatrick 	    "smmu_vp3", NULL);
1332322545bSpatrick 	pool_setlowat(&sc->sc_vp3_pool, 20);
1341a30e4e7Spatrick 
1351a30e4e7Spatrick 	reg = smmu_gr0_read_4(sc, SMMU_IDR0);
1361a30e4e7Spatrick 	if (reg & SMMU_IDR0_S1TS)
1371a30e4e7Spatrick 		sc->sc_has_s1 = 1;
1381a30e4e7Spatrick 	/*
1391a30e4e7Spatrick 	 * Marvell's 8040 does not support 64-bit writes, hence it
1401a30e4e7Spatrick 	 * is not possible to invalidate stage-2, because the ASID
1411a30e4e7Spatrick 	 * is part of the upper 32-bits and they'd be ignored.
1421a30e4e7Spatrick 	 */
1431a30e4e7Spatrick 	if (sc->sc_is_ap806)
1441a30e4e7Spatrick 		sc->sc_has_s1 = 0;
1451a30e4e7Spatrick 	if (reg & SMMU_IDR0_S2TS)
1461a30e4e7Spatrick 		sc->sc_has_s2 = 1;
1471a30e4e7Spatrick 	if (!sc->sc_has_s1 && !sc->sc_has_s2)
1481a30e4e7Spatrick 		return 1;
1491a30e4e7Spatrick 	if (reg & SMMU_IDR0_EXIDS)
1501a30e4e7Spatrick 		sc->sc_has_exids = 1;
1511a30e4e7Spatrick 
1521a30e4e7Spatrick 	sc->sc_num_streams = 1 << SMMU_IDR0_NUMSIDB(reg);
1531a30e4e7Spatrick 	if (sc->sc_has_exids)
1541a30e4e7Spatrick 		sc->sc_num_streams = 1 << 16;
1551a30e4e7Spatrick 	sc->sc_stream_mask = sc->sc_num_streams - 1;
1561a30e4e7Spatrick 	if (reg & SMMU_IDR0_SMS) {
1571a30e4e7Spatrick 		sc->sc_num_streams = SMMU_IDR0_NUMSMRG(reg);
1581a30e4e7Spatrick 		if (sc->sc_num_streams == 0)
1591a30e4e7Spatrick 			return 1;
1601a30e4e7Spatrick 		sc->sc_smr = mallocarray(sc->sc_num_streams,
1611a30e4e7Spatrick 		    sizeof(*sc->sc_smr), M_DEVBUF, M_WAITOK | M_ZERO);
1621a30e4e7Spatrick 	}
1631a30e4e7Spatrick 
1641a30e4e7Spatrick 	reg = smmu_gr0_read_4(sc, SMMU_IDR1);
1651a30e4e7Spatrick 	sc->sc_pagesize = 4 * 1024;
1661a30e4e7Spatrick 	if (reg & SMMU_IDR1_PAGESIZE_64K)
1671a30e4e7Spatrick 		sc->sc_pagesize = 64 * 1024;
1681a30e4e7Spatrick 	sc->sc_numpage = 1 << (SMMU_IDR1_NUMPAGENDXB(reg) + 1);
1691a30e4e7Spatrick 
1701a30e4e7Spatrick 	/* 0 to NUMS2CB == stage-2, NUMS2CB to NUMCB == stage-1 */
1711a30e4e7Spatrick 	sc->sc_num_context_banks = SMMU_IDR1_NUMCB(reg);
1721a30e4e7Spatrick 	sc->sc_num_s2_context_banks = SMMU_IDR1_NUMS2CB(reg);
1731a30e4e7Spatrick 	if (sc->sc_num_s2_context_banks > sc->sc_num_context_banks)
1741a30e4e7Spatrick 		return 1;
1751a30e4e7Spatrick 	sc->sc_cb = mallocarray(sc->sc_num_context_banks,
1761a30e4e7Spatrick 	    sizeof(*sc->sc_cb), M_DEVBUF, M_WAITOK | M_ZERO);
1771a30e4e7Spatrick 
1781a30e4e7Spatrick 	reg = smmu_gr0_read_4(sc, SMMU_IDR2);
1791a30e4e7Spatrick 	if (reg & SMMU_IDR2_VMID16S)
1801a30e4e7Spatrick 		sc->sc_has_vmid16s = 1;
1811a30e4e7Spatrick 
1821a30e4e7Spatrick 	switch (SMMU_IDR2_IAS(reg)) {
1831a30e4e7Spatrick 	case SMMU_IDR2_IAS_32BIT:
1841a30e4e7Spatrick 		sc->sc_ipa_bits = 32;
1851a30e4e7Spatrick 		break;
1861a30e4e7Spatrick 	case SMMU_IDR2_IAS_36BIT:
1871a30e4e7Spatrick 		sc->sc_ipa_bits = 36;
1881a30e4e7Spatrick 		break;
1891a30e4e7Spatrick 	case SMMU_IDR2_IAS_40BIT:
1901a30e4e7Spatrick 		sc->sc_ipa_bits = 40;
1911a30e4e7Spatrick 		break;
1921a30e4e7Spatrick 	case SMMU_IDR2_IAS_42BIT:
1931a30e4e7Spatrick 		sc->sc_ipa_bits = 42;
1941a30e4e7Spatrick 		break;
1951a30e4e7Spatrick 	case SMMU_IDR2_IAS_44BIT:
1961a30e4e7Spatrick 		sc->sc_ipa_bits = 44;
1971a30e4e7Spatrick 		break;
1981a30e4e7Spatrick 	case SMMU_IDR2_IAS_48BIT:
1991a30e4e7Spatrick 	default:
2001a30e4e7Spatrick 		sc->sc_ipa_bits = 48;
2011a30e4e7Spatrick 		break;
2021a30e4e7Spatrick 	}
2031a30e4e7Spatrick 	switch (SMMU_IDR2_OAS(reg)) {
2041a30e4e7Spatrick 	case SMMU_IDR2_OAS_32BIT:
2051a30e4e7Spatrick 		sc->sc_pa_bits = 32;
2061a30e4e7Spatrick 		break;
2071a30e4e7Spatrick 	case SMMU_IDR2_OAS_36BIT:
2081a30e4e7Spatrick 		sc->sc_pa_bits = 36;
2091a30e4e7Spatrick 		break;
2101a30e4e7Spatrick 	case SMMU_IDR2_OAS_40BIT:
2111a30e4e7Spatrick 		sc->sc_pa_bits = 40;
2121a30e4e7Spatrick 		break;
2131a30e4e7Spatrick 	case SMMU_IDR2_OAS_42BIT:
2141a30e4e7Spatrick 		sc->sc_pa_bits = 42;
2151a30e4e7Spatrick 		break;
2161a30e4e7Spatrick 	case SMMU_IDR2_OAS_44BIT:
2171a30e4e7Spatrick 		sc->sc_pa_bits = 44;
2181a30e4e7Spatrick 		break;
2191a30e4e7Spatrick 	case SMMU_IDR2_OAS_48BIT:
2201a30e4e7Spatrick 	default:
2211a30e4e7Spatrick 		sc->sc_pa_bits = 48;
2221a30e4e7Spatrick 		break;
2231a30e4e7Spatrick 	}
2241a30e4e7Spatrick 	switch (SMMU_IDR2_UBS(reg)) {
2251a30e4e7Spatrick 	case SMMU_IDR2_UBS_32BIT:
2261a30e4e7Spatrick 		sc->sc_va_bits = 32;
2271a30e4e7Spatrick 		break;
2281a30e4e7Spatrick 	case SMMU_IDR2_UBS_36BIT:
2291a30e4e7Spatrick 		sc->sc_va_bits = 36;
2301a30e4e7Spatrick 		break;
2311a30e4e7Spatrick 	case SMMU_IDR2_UBS_40BIT:
2321a30e4e7Spatrick 		sc->sc_va_bits = 40;
2331a30e4e7Spatrick 		break;
2341a30e4e7Spatrick 	case SMMU_IDR2_UBS_42BIT:
2351a30e4e7Spatrick 		sc->sc_va_bits = 42;
2361a30e4e7Spatrick 		break;
2371a30e4e7Spatrick 	case SMMU_IDR2_UBS_44BIT:
2381a30e4e7Spatrick 		sc->sc_va_bits = 44;
2391a30e4e7Spatrick 		break;
2401a30e4e7Spatrick 	case SMMU_IDR2_UBS_49BIT:
2411a30e4e7Spatrick 	default:
2421a30e4e7Spatrick 		sc->sc_va_bits = 48;
2431a30e4e7Spatrick 		break;
2441a30e4e7Spatrick 	}
2451a30e4e7Spatrick 
246d0fbdf68Spatrick 	printf(": %u CBs (%u S2-only)",
2471a30e4e7Spatrick 	    sc->sc_num_context_banks, sc->sc_num_s2_context_banks);
248d0fbdf68Spatrick 	if (sc->sc_is_qcom) {
249d0fbdf68Spatrick 		/*
250d0fbdf68Spatrick 		 * In theory we should check if bypass quirk is needed by
251d0fbdf68Spatrick 		 * modifying S2CR and re-checking if the value is different.
252d0fbdf68Spatrick 		 * This does not work on the last S2CR, but on the first,
253d0fbdf68Spatrick 		 * which is in use.  Revisit this once we have other QCOM HW.
254d0fbdf68Spatrick 		 */
255d0fbdf68Spatrick 		sc->sc_bypass_quirk = 1;
256d0fbdf68Spatrick 		printf(", bypass quirk");
257d0fbdf68Spatrick 		/*
258d0fbdf68Spatrick 		 * Create special context that is turned off.  This allows us
259d0fbdf68Spatrick 		 * to map a stream to a context bank where translation is not
260d0fbdf68Spatrick 		 * happening, and hence bypassed.
261d0fbdf68Spatrick 		 */
262d0fbdf68Spatrick 		sc->sc_cb[sc->sc_num_context_banks - 1] =
263d0fbdf68Spatrick 		    malloc(sizeof(struct smmu_cb), M_DEVBUF, M_WAITOK | M_ZERO);
264a68d7a25Spatrick 		smmu_cb_write_4(sc, sc->sc_num_context_banks - 1,
265a68d7a25Spatrick 		    SMMU_CB_SCTLR, 0);
266d0fbdf68Spatrick 		smmu_gr1_write_4(sc, SMMU_CBAR(sc->sc_num_context_banks - 1),
267d0fbdf68Spatrick 		    SMMU_CBAR_TYPE_S1_TRANS_S2_BYPASS);
268d0fbdf68Spatrick 	}
269d0fbdf68Spatrick 	printf("\n");
2701a30e4e7Spatrick 
2711a30e4e7Spatrick 	/* Clear Global Fault Status Register */
2721a30e4e7Spatrick 	smmu_gr0_write_4(sc, SMMU_SGFSR, smmu_gr0_read_4(sc, SMMU_SGFSR));
2731a30e4e7Spatrick 
2741a30e4e7Spatrick 	for (i = 0; i < sc->sc_num_streams; i++) {
275d0fbdf68Spatrick 		/* On QCOM HW we need to keep current streams running. */
276d0fbdf68Spatrick 		if (sc->sc_is_qcom && sc->sc_smr &&
277d0fbdf68Spatrick 		    smmu_gr0_read_4(sc, SMMU_SMR(i)) & SMMU_SMR_VALID) {
278a68d7a25Spatrick 			reg = smmu_gr0_read_4(sc, SMMU_SMR(i));
279d0fbdf68Spatrick 			sc->sc_smr[i] = malloc(sizeof(struct smmu_smr),
280d0fbdf68Spatrick 			    M_DEVBUF, M_WAITOK | M_ZERO);
281a68d7a25Spatrick 			sc->sc_smr[i]->ss_id = (reg >> SMMU_SMR_ID_SHIFT) &
282a68d7a25Spatrick 			    SMMU_SMR_ID_MASK;
283a68d7a25Spatrick 			sc->sc_smr[i]->ss_mask = (reg >> SMMU_SMR_MASK_SHIFT) &
284a68d7a25Spatrick 			    SMMU_SMR_MASK_MASK;
285d0fbdf68Spatrick 			if (sc->sc_bypass_quirk) {
286d0fbdf68Spatrick 				smmu_gr0_write_4(sc, SMMU_S2CR(i),
287d0fbdf68Spatrick 				    SMMU_S2CR_TYPE_TRANS |
288d0fbdf68Spatrick 				    sc->sc_num_context_banks - 1);
289d0fbdf68Spatrick 			} else {
290d0fbdf68Spatrick 				smmu_gr0_write_4(sc, SMMU_S2CR(i),
291d0fbdf68Spatrick 				    SMMU_S2CR_TYPE_BYPASS | 0xff);
292d0fbdf68Spatrick 			}
293d0fbdf68Spatrick 			continue;
294d0fbdf68Spatrick 		}
2951a30e4e7Spatrick #if 1
2961a30e4e7Spatrick 		/* Setup all streams to fault by default */
2971a30e4e7Spatrick 		smmu_gr0_write_4(sc, SMMU_S2CR(i), SMMU_S2CR_TYPE_FAULT);
2981a30e4e7Spatrick #else
2991a30e4e7Spatrick 		/* For stream indexing, USFCFG bypass isn't enough! */
3001a30e4e7Spatrick 		smmu_gr0_write_4(sc, SMMU_S2CR(i), SMMU_S2CR_TYPE_BYPASS);
3011a30e4e7Spatrick #endif
3021a30e4e7Spatrick 		/*  Disable all stream map registers */
3031a30e4e7Spatrick 		if (sc->sc_smr)
3041a30e4e7Spatrick 			smmu_gr0_write_4(sc, SMMU_SMR(i), 0);
3051a30e4e7Spatrick 	}
3061a30e4e7Spatrick 
3071a30e4e7Spatrick 	for (i = 0; i < sc->sc_num_context_banks; i++) {
3081a30e4e7Spatrick 		/* Disable Context Bank */
3091a30e4e7Spatrick 		smmu_cb_write_4(sc, i, SMMU_CB_SCTLR, 0);
3101a30e4e7Spatrick 		/* Clear Context Bank Fault Status Register */
3111a30e4e7Spatrick 		smmu_cb_write_4(sc, i, SMMU_CB_FSR, SMMU_CB_FSR_MASK);
3121a30e4e7Spatrick 	}
3131a30e4e7Spatrick 
3141a30e4e7Spatrick 	/* Invalidate TLB */
3151a30e4e7Spatrick 	smmu_gr0_write_4(sc, SMMU_TLBIALLH, ~0);
3161a30e4e7Spatrick 	smmu_gr0_write_4(sc, SMMU_TLBIALLNSNH, ~0);
3171a30e4e7Spatrick 
3181a30e4e7Spatrick 	if (sc->sc_is_mmu500) {
3191a30e4e7Spatrick 		reg = smmu_gr0_read_4(sc, SMMU_SACR);
3201a30e4e7Spatrick 		if (SMMU_IDR7_MAJOR(smmu_gr0_read_4(sc, SMMU_IDR7)) >= 2)
3211a30e4e7Spatrick 			reg &= ~SMMU_SACR_MMU500_CACHE_LOCK;
3221a30e4e7Spatrick 		reg |= SMMU_SACR_MMU500_SMTNMB_TLBEN |
3231a30e4e7Spatrick 		    SMMU_SACR_MMU500_S2CRB_TLBEN;
3241a30e4e7Spatrick 		smmu_gr0_write_4(sc, SMMU_SACR, reg);
3251a30e4e7Spatrick 		for (i = 0; i < sc->sc_num_context_banks; i++) {
3261a30e4e7Spatrick 			reg = smmu_cb_read_4(sc, i, SMMU_CB_ACTLR);
3271a30e4e7Spatrick 			reg &= ~SMMU_CB_ACTLR_CPRE;
3281a30e4e7Spatrick 			smmu_cb_write_4(sc, i, SMMU_CB_ACTLR, reg);
3291a30e4e7Spatrick 		}
3301a30e4e7Spatrick 	}
3311a30e4e7Spatrick 
3321a30e4e7Spatrick 	/* Enable SMMU */
3331a30e4e7Spatrick 	reg = smmu_gr0_read_4(sc, SMMU_SCR0);
3341a30e4e7Spatrick 	reg &= ~(SMMU_SCR0_CLIENTPD |
3351a30e4e7Spatrick 	    SMMU_SCR0_FB | SMMU_SCR0_BSU_MASK);
3361a30e4e7Spatrick #if 1
3371a30e4e7Spatrick 	/* Disable bypass for unknown streams */
3381a30e4e7Spatrick 	reg |= SMMU_SCR0_USFCFG;
3391a30e4e7Spatrick #else
3401a30e4e7Spatrick 	/* Enable bypass for unknown streams */
3411a30e4e7Spatrick 	reg &= ~SMMU_SCR0_USFCFG;
3421a30e4e7Spatrick #endif
3431a30e4e7Spatrick 	reg |= SMMU_SCR0_GFRE | SMMU_SCR0_GFIE |
3441a30e4e7Spatrick 	    SMMU_SCR0_GCFGFRE | SMMU_SCR0_GCFGFIE |
3451a30e4e7Spatrick 	    SMMU_SCR0_VMIDPNE | SMMU_SCR0_PTM;
3461a30e4e7Spatrick 	if (sc->sc_has_exids)
3471a30e4e7Spatrick 		reg |= SMMU_SCR0_EXIDENABLE;
3481a30e4e7Spatrick 	if (sc->sc_has_vmid16s)
3491a30e4e7Spatrick 		reg |= SMMU_SCR0_VMID16EN;
3501a30e4e7Spatrick 
3511a30e4e7Spatrick 	smmu_tlb_sync_global(sc);
3521a30e4e7Spatrick 	smmu_gr0_write_4(sc, SMMU_SCR0, reg);
3531a30e4e7Spatrick 
3541a30e4e7Spatrick 	return 0;
3551a30e4e7Spatrick }
3561a30e4e7Spatrick 
3571a30e4e7Spatrick int
smmu_global_irq(void * cookie)3581a30e4e7Spatrick smmu_global_irq(void *cookie)
3591a30e4e7Spatrick {
3601a30e4e7Spatrick 	struct smmu_softc *sc = cookie;
3611a30e4e7Spatrick 	uint32_t reg;
3621a30e4e7Spatrick 
3631a30e4e7Spatrick 	reg = smmu_gr0_read_4(sc, SMMU_SGFSR);
3641a30e4e7Spatrick 	if (reg == 0)
3651a30e4e7Spatrick 		return 0;
3661a30e4e7Spatrick 
367a77502b0Spatrick 	printf("%s: SGFSR 0x%08x SGFSYNR0 0x%08x SGFSYNR1 0x%08x "
368a77502b0Spatrick 	    "SGFSYNR2 0x%08x\n", sc->sc_dev.dv_xname, reg,
3691a30e4e7Spatrick 	    smmu_gr0_read_4(sc, SMMU_SGFSYNR0),
3701a30e4e7Spatrick 	    smmu_gr0_read_4(sc, SMMU_SGFSYNR1),
3711a30e4e7Spatrick 	    smmu_gr0_read_4(sc, SMMU_SGFSYNR2));
3721a30e4e7Spatrick 
3731a30e4e7Spatrick 	smmu_gr0_write_4(sc, SMMU_SGFSR, reg);
3741a30e4e7Spatrick 
3751a30e4e7Spatrick 	return 1;
3761a30e4e7Spatrick }
3771a30e4e7Spatrick 
3781a30e4e7Spatrick int
smmu_context_irq(void * cookie)3791a30e4e7Spatrick smmu_context_irq(void *cookie)
3801a30e4e7Spatrick {
3811a30e4e7Spatrick 	struct smmu_cb_irq *cbi = cookie;
3821a30e4e7Spatrick 	struct smmu_softc *sc = cbi->cbi_sc;
3831a30e4e7Spatrick 	uint32_t reg;
3841a30e4e7Spatrick 
3851a30e4e7Spatrick 	reg = smmu_cb_read_4(sc, cbi->cbi_idx, SMMU_CB_FSR);
3861a30e4e7Spatrick 	if ((reg & SMMU_CB_FSR_MASK) == 0)
3871a30e4e7Spatrick 		return 0;
3881a30e4e7Spatrick 
389a77502b0Spatrick 	printf("%s: FSR 0x%08x FSYNR0 0x%08x FAR 0x%llx "
390a77502b0Spatrick 	    "CBFRSYNRA 0x%08x\n", sc->sc_dev.dv_xname, reg,
3911a30e4e7Spatrick 	    smmu_cb_read_4(sc, cbi->cbi_idx, SMMU_CB_FSYNR0),
3921a30e4e7Spatrick 	    smmu_cb_read_8(sc, cbi->cbi_idx, SMMU_CB_FAR),
3931a30e4e7Spatrick 	    smmu_gr1_read_4(sc, SMMU_CBFRSYNRA(cbi->cbi_idx)));
3941a30e4e7Spatrick 
3951a30e4e7Spatrick 	smmu_cb_write_4(sc, cbi->cbi_idx, SMMU_CB_FSR, reg);
3961a30e4e7Spatrick 
3971a30e4e7Spatrick 	return 1;
3981a30e4e7Spatrick }
3991a30e4e7Spatrick 
4001a30e4e7Spatrick void
smmu_tlb_sync_global(struct smmu_softc * sc)4011a30e4e7Spatrick smmu_tlb_sync_global(struct smmu_softc *sc)
4021a30e4e7Spatrick {
4031a30e4e7Spatrick 	int i;
4041a30e4e7Spatrick 
4051a30e4e7Spatrick 	smmu_gr0_write_4(sc, SMMU_STLBGSYNC, ~0);
4061a30e4e7Spatrick 	for (i = 1000; i > 0; i--) {
4071a30e4e7Spatrick 		if ((smmu_gr0_read_4(sc, SMMU_STLBGSTATUS) &
4081a30e4e7Spatrick 		    SMMU_STLBGSTATUS_GSACTIVE) == 0)
4091a30e4e7Spatrick 			return;
4101a30e4e7Spatrick 	}
4111a30e4e7Spatrick 
4121a30e4e7Spatrick 	printf("%s: global TLB sync timeout\n",
4131a30e4e7Spatrick 	    sc->sc_dev.dv_xname);
4141a30e4e7Spatrick }
4151a30e4e7Spatrick 
4161a30e4e7Spatrick void
smmu_tlb_sync_context(struct smmu_domain * dom)4171a30e4e7Spatrick smmu_tlb_sync_context(struct smmu_domain *dom)
4181a30e4e7Spatrick {
4191a30e4e7Spatrick 	struct smmu_softc *sc = dom->sd_sc;
4201a30e4e7Spatrick 	int i;
4211a30e4e7Spatrick 
4221a30e4e7Spatrick 	smmu_cb_write_4(sc, dom->sd_cb_idx, SMMU_CB_TLBSYNC, ~0);
4231a30e4e7Spatrick 	for (i = 1000; i > 0; i--) {
4241a30e4e7Spatrick 		if ((smmu_cb_read_4(sc, dom->sd_cb_idx, SMMU_CB_TLBSTATUS) &
4251a30e4e7Spatrick 		    SMMU_CB_TLBSTATUS_SACTIVE) == 0)
4261a30e4e7Spatrick 			return;
4271a30e4e7Spatrick 	}
4281a30e4e7Spatrick 
4291a30e4e7Spatrick 	printf("%s: context TLB sync timeout\n",
4301a30e4e7Spatrick 	    sc->sc_dev.dv_xname);
4311a30e4e7Spatrick }
4321a30e4e7Spatrick 
4331a30e4e7Spatrick uint32_t
smmu_gr0_read_4(struct smmu_softc * sc,bus_size_t off)4341a30e4e7Spatrick smmu_gr0_read_4(struct smmu_softc *sc, bus_size_t off)
4351a30e4e7Spatrick {
4361a30e4e7Spatrick 	uint32_t base = 0 * sc->sc_pagesize;
4371a30e4e7Spatrick 
4381a30e4e7Spatrick 	return bus_space_read_4(sc->sc_iot, sc->sc_ioh, base + off);
4391a30e4e7Spatrick }
4401a30e4e7Spatrick 
4411a30e4e7Spatrick void
smmu_gr0_write_4(struct smmu_softc * sc,bus_size_t off,uint32_t val)4421a30e4e7Spatrick smmu_gr0_write_4(struct smmu_softc *sc, bus_size_t off, uint32_t val)
4431a30e4e7Spatrick {
4441a30e4e7Spatrick 	uint32_t base = 0 * sc->sc_pagesize;
4451a30e4e7Spatrick 
4461a30e4e7Spatrick 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, base + off, val);
4471a30e4e7Spatrick }
4481a30e4e7Spatrick 
4491a30e4e7Spatrick uint32_t
smmu_gr1_read_4(struct smmu_softc * sc,bus_size_t off)4501a30e4e7Spatrick smmu_gr1_read_4(struct smmu_softc *sc, bus_size_t off)
4511a30e4e7Spatrick {
4521a30e4e7Spatrick 	uint32_t base = 1 * sc->sc_pagesize;
4531a30e4e7Spatrick 
4541a30e4e7Spatrick 	return bus_space_read_4(sc->sc_iot, sc->sc_ioh, base + off);
4551a30e4e7Spatrick }
4561a30e4e7Spatrick 
4571a30e4e7Spatrick void
smmu_gr1_write_4(struct smmu_softc * sc,bus_size_t off,uint32_t val)4581a30e4e7Spatrick smmu_gr1_write_4(struct smmu_softc *sc, bus_size_t off, uint32_t val)
4591a30e4e7Spatrick {
4601a30e4e7Spatrick 	uint32_t base = 1 * sc->sc_pagesize;
4611a30e4e7Spatrick 
4621a30e4e7Spatrick 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, base + off, val);
4631a30e4e7Spatrick }
4641a30e4e7Spatrick 
4651a30e4e7Spatrick uint32_t
smmu_cb_read_4(struct smmu_softc * sc,int idx,bus_size_t off)4661a30e4e7Spatrick smmu_cb_read_4(struct smmu_softc *sc, int idx, bus_size_t off)
4671a30e4e7Spatrick {
4681a30e4e7Spatrick 	uint32_t base;
4691a30e4e7Spatrick 
4701a30e4e7Spatrick 	base = sc->sc_numpage * sc->sc_pagesize; /* SMMU_CB_BASE */
4711a30e4e7Spatrick 	base += idx * sc->sc_pagesize; /* SMMU_CBn_BASE */
4721a30e4e7Spatrick 
4731a30e4e7Spatrick 	return bus_space_read_4(sc->sc_iot, sc->sc_ioh, base + off);
4741a30e4e7Spatrick }
4751a30e4e7Spatrick 
4761a30e4e7Spatrick void
smmu_cb_write_4(struct smmu_softc * sc,int idx,bus_size_t off,uint32_t val)4771a30e4e7Spatrick smmu_cb_write_4(struct smmu_softc *sc, int idx, bus_size_t off, uint32_t val)
4781a30e4e7Spatrick {
4791a30e4e7Spatrick 	uint32_t base;
4801a30e4e7Spatrick 
4811a30e4e7Spatrick 	base = sc->sc_numpage * sc->sc_pagesize; /* SMMU_CB_BASE */
4821a30e4e7Spatrick 	base += idx * sc->sc_pagesize; /* SMMU_CBn_BASE */
4831a30e4e7Spatrick 
4841a30e4e7Spatrick 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, base + off, val);
4851a30e4e7Spatrick }
4861a30e4e7Spatrick 
4871a30e4e7Spatrick uint64_t
smmu_cb_read_8(struct smmu_softc * sc,int idx,bus_size_t off)4881a30e4e7Spatrick smmu_cb_read_8(struct smmu_softc *sc, int idx, bus_size_t off)
4891a30e4e7Spatrick {
4901a30e4e7Spatrick 	uint64_t reg;
4911a30e4e7Spatrick 	uint32_t base;
4921a30e4e7Spatrick 
4931a30e4e7Spatrick 	base = sc->sc_numpage * sc->sc_pagesize; /* SMMU_CB_BASE */
4941a30e4e7Spatrick 	base += idx * sc->sc_pagesize; /* SMMU_CBn_BASE */
4951a30e4e7Spatrick 
4961a30e4e7Spatrick 	if (sc->sc_is_ap806) {
4971a30e4e7Spatrick 		reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, base + off + 4);
4981a30e4e7Spatrick 		reg <<= 32;
4991a30e4e7Spatrick 		reg |= bus_space_read_4(sc->sc_iot, sc->sc_ioh, base + off + 0);
5001a30e4e7Spatrick 		return reg;
5011a30e4e7Spatrick 	}
5021a30e4e7Spatrick 
5031a30e4e7Spatrick 	return bus_space_read_8(sc->sc_iot, sc->sc_ioh, base + off);
5041a30e4e7Spatrick }
5051a30e4e7Spatrick 
5061a30e4e7Spatrick void
smmu_cb_write_8(struct smmu_softc * sc,int idx,bus_size_t off,uint64_t val)5071a30e4e7Spatrick smmu_cb_write_8(struct smmu_softc *sc, int idx, bus_size_t off, uint64_t val)
5081a30e4e7Spatrick {
5091a30e4e7Spatrick 	uint32_t base;
5101a30e4e7Spatrick 
5111a30e4e7Spatrick 	base = sc->sc_numpage * sc->sc_pagesize; /* SMMU_CB_BASE */
5121a30e4e7Spatrick 	base += idx * sc->sc_pagesize; /* SMMU_CBn_BASE */
5131a30e4e7Spatrick 
5141a30e4e7Spatrick 	if (sc->sc_is_ap806) {
5151a30e4e7Spatrick 		bus_space_write_4(sc->sc_iot, sc->sc_ioh, base + off + 4,
5161a30e4e7Spatrick 		    val >> 32);
5171a30e4e7Spatrick 		bus_space_write_4(sc->sc_iot, sc->sc_ioh, base + off + 0,
5181a30e4e7Spatrick 		    val & 0xffffffff);
5191a30e4e7Spatrick 		return;
5201a30e4e7Spatrick 	}
5211a30e4e7Spatrick 
5221a30e4e7Spatrick 	bus_space_write_8(sc->sc_iot, sc->sc_ioh, base + off, val);
5231a30e4e7Spatrick }
5241a30e4e7Spatrick 
5251a30e4e7Spatrick bus_dma_tag_t
smmu_device_map(void * cookie,uint32_t sid,bus_dma_tag_t dmat)52688933a1fSpatrick smmu_device_map(void *cookie, uint32_t sid, bus_dma_tag_t dmat)
5271a30e4e7Spatrick {
52888933a1fSpatrick 	struct smmu_softc *sc = cookie;
5291a30e4e7Spatrick 	struct smmu_domain *dom;
5301a30e4e7Spatrick 
5311a30e4e7Spatrick 	dom = smmu_domain_lookup(sc, sid);
5321a30e4e7Spatrick 	if (dom == NULL)
5331a30e4e7Spatrick 		return dmat;
5341a30e4e7Spatrick 
535c6c0a1deSpatrick 	if (dom->sd_dmat == NULL) {
536c6c0a1deSpatrick 		dom->sd_dmat = malloc(sizeof(*dom->sd_dmat),
5371a30e4e7Spatrick 		    M_DEVBUF, M_WAITOK);
538c6c0a1deSpatrick 		memcpy(dom->sd_dmat, sc->sc_dmat,
539c6c0a1deSpatrick 		    sizeof(*dom->sd_dmat));
540c6c0a1deSpatrick 		dom->sd_dmat->_cookie = dom;
541c6c0a1deSpatrick 		dom->sd_dmat->_dmamap_create = smmu_dmamap_create;
542c6c0a1deSpatrick 		dom->sd_dmat->_dmamap_destroy = smmu_dmamap_destroy;
543c6c0a1deSpatrick 		dom->sd_dmat->_dmamap_load = smmu_dmamap_load;
544c6c0a1deSpatrick 		dom->sd_dmat->_dmamap_load_mbuf = smmu_dmamap_load_mbuf;
545c6c0a1deSpatrick 		dom->sd_dmat->_dmamap_load_uio = smmu_dmamap_load_uio;
546c6c0a1deSpatrick 		dom->sd_dmat->_dmamap_load_raw = smmu_dmamap_load_raw;
547c6c0a1deSpatrick 		dom->sd_dmat->_dmamap_unload = smmu_dmamap_unload;
5480aa222ffSpatrick 		dom->sd_dmat->_flags |= BUS_DMA_COHERENT;
5491a30e4e7Spatrick 	}
5501a30e4e7Spatrick 
551c6c0a1deSpatrick 	return dom->sd_dmat;
5521a30e4e7Spatrick }
5531a30e4e7Spatrick 
5541a30e4e7Spatrick struct smmu_domain *
smmu_domain_lookup(struct smmu_softc * sc,uint32_t sid)5551a30e4e7Spatrick smmu_domain_lookup(struct smmu_softc *sc, uint32_t sid)
5561a30e4e7Spatrick {
5571a30e4e7Spatrick 	struct smmu_domain *dom;
5581a30e4e7Spatrick 
5591a30e4e7Spatrick 	SIMPLEQ_FOREACH(dom, &sc->sc_domains, sd_list) {
5601a30e4e7Spatrick 		if (dom->sd_sid == sid)
5611a30e4e7Spatrick 			return dom;
5621a30e4e7Spatrick 	}
5631a30e4e7Spatrick 
5641a30e4e7Spatrick 	return smmu_domain_create(sc, sid);
5651a30e4e7Spatrick }
5661a30e4e7Spatrick 
5671a30e4e7Spatrick struct smmu_domain *
smmu_domain_create(struct smmu_softc * sc,uint32_t sid)5681a30e4e7Spatrick smmu_domain_create(struct smmu_softc *sc, uint32_t sid)
5691a30e4e7Spatrick {
5701a30e4e7Spatrick 	struct smmu_domain *dom;
5711a30e4e7Spatrick 	uint32_t iovabits, reg;
5721a30e4e7Spatrick 	paddr_t pa;
5731a30e4e7Spatrick 	vaddr_t l0va;
5741a30e4e7Spatrick 	int i, start, end;
5751a30e4e7Spatrick 
5761a30e4e7Spatrick 	dom = malloc(sizeof(*dom), M_DEVBUF, M_WAITOK | M_ZERO);
5770aa222ffSpatrick 	mtx_init(&dom->sd_iova_mtx, IPL_VM);
5780aa222ffSpatrick 	mtx_init(&dom->sd_pmap_mtx, IPL_VM);
5791a30e4e7Spatrick 	dom->sd_sc = sc;
5801a30e4e7Spatrick 	dom->sd_sid = sid;
5811a30e4e7Spatrick 
5821a30e4e7Spatrick 	/* Prefer stage 1 if possible! */
5831a30e4e7Spatrick 	if (sc->sc_has_s1) {
5841a30e4e7Spatrick 		start = sc->sc_num_s2_context_banks;
5851a30e4e7Spatrick 		end = sc->sc_num_context_banks;
5861a30e4e7Spatrick 		dom->sd_stage = 1;
5871a30e4e7Spatrick 	} else {
5881a30e4e7Spatrick 		start = 0;
5891a30e4e7Spatrick 		end = sc->sc_num_context_banks;
5901a30e4e7Spatrick 		dom->sd_stage = 2;
5911a30e4e7Spatrick 	}
5921a30e4e7Spatrick 
5931a30e4e7Spatrick 	for (i = start; i < end; i++) {
5941a30e4e7Spatrick 		if (sc->sc_cb[i] != NULL)
5951a30e4e7Spatrick 			continue;
5961a30e4e7Spatrick 		sc->sc_cb[i] = malloc(sizeof(struct smmu_cb),
5971a30e4e7Spatrick 		    M_DEVBUF, M_WAITOK | M_ZERO);
5981a30e4e7Spatrick 		dom->sd_cb_idx = i;
5991a30e4e7Spatrick 		break;
6001a30e4e7Spatrick 	}
6011a30e4e7Spatrick 	if (i >= end) {
602a77502b0Spatrick 		printf("%s: out of context blocks, I/O device will fail\n",
603a77502b0Spatrick 		    sc->sc_dev.dv_xname);
6041a30e4e7Spatrick 		free(dom, M_DEVBUF, sizeof(*dom));
6051a30e4e7Spatrick 		return NULL;
6061a30e4e7Spatrick 	}
6071a30e4e7Spatrick 
6081a30e4e7Spatrick 	/* Stream indexing is easy */
6091a30e4e7Spatrick 	dom->sd_smr_idx = sid;
6101a30e4e7Spatrick 
6111a30e4e7Spatrick 	/* Stream mapping is a bit more effort */
6121a30e4e7Spatrick 	if (sc->sc_smr) {
6131a30e4e7Spatrick 		for (i = 0; i < sc->sc_num_streams; i++) {
614a68d7a25Spatrick 			/* Take over QCOM SMRs */
615a68d7a25Spatrick 			if (sc->sc_is_qcom && sc->sc_smr[i] != NULL &&
616a68d7a25Spatrick 			    sc->sc_smr[i]->ss_dom == NULL &&
617a68d7a25Spatrick 			    sc->sc_smr[i]->ss_id == sid &&
618a68d7a25Spatrick 			    sc->sc_smr[i]->ss_mask == 0) {
619a68d7a25Spatrick 				free(sc->sc_smr[i], M_DEVBUF,
620a68d7a25Spatrick 				    sizeof(struct smmu_smr));
621a68d7a25Spatrick 				sc->sc_smr[i] = NULL;
622a68d7a25Spatrick 			}
6231a30e4e7Spatrick 			if (sc->sc_smr[i] != NULL)
6241a30e4e7Spatrick 				continue;
6251a30e4e7Spatrick 			sc->sc_smr[i] = malloc(sizeof(struct smmu_smr),
6261a30e4e7Spatrick 			    M_DEVBUF, M_WAITOK | M_ZERO);
627a68d7a25Spatrick 			sc->sc_smr[i]->ss_dom = dom;
628a68d7a25Spatrick 			sc->sc_smr[i]->ss_id = sid;
629a68d7a25Spatrick 			sc->sc_smr[i]->ss_mask = 0;
6301a30e4e7Spatrick 			dom->sd_smr_idx = i;
6311a30e4e7Spatrick 			break;
6321a30e4e7Spatrick 		}
6331a30e4e7Spatrick 
6341a30e4e7Spatrick 		if (i >= sc->sc_num_streams) {
6351a30e4e7Spatrick 			free(sc->sc_cb[dom->sd_cb_idx], M_DEVBUF,
6361a30e4e7Spatrick 			    sizeof(struct smmu_cb));
6371a30e4e7Spatrick 			sc->sc_cb[dom->sd_cb_idx] = NULL;
6381a30e4e7Spatrick 			free(dom, M_DEVBUF, sizeof(*dom));
639a77502b0Spatrick 			printf("%s: out of streams, I/O device will fail\n",
640a77502b0Spatrick 			    sc->sc_dev.dv_xname);
6411a30e4e7Spatrick 			return NULL;
6421a30e4e7Spatrick 		}
6431a30e4e7Spatrick 	}
6441a30e4e7Spatrick 
6451a30e4e7Spatrick 	reg = SMMU_CBA2R_VA64;
6461a30e4e7Spatrick 	if (sc->sc_has_vmid16s)
6471a30e4e7Spatrick 		reg |= (dom->sd_cb_idx + 1) << SMMU_CBA2R_VMID16_SHIFT;
6481a30e4e7Spatrick 	smmu_gr1_write_4(sc, SMMU_CBA2R(dom->sd_cb_idx), reg);
6491a30e4e7Spatrick 
6501a30e4e7Spatrick 	if (dom->sd_stage == 1) {
6511a30e4e7Spatrick 		reg = SMMU_CBAR_TYPE_S1_TRANS_S2_BYPASS |
6521a30e4e7Spatrick 		    SMMU_CBAR_BPSHCFG_NSH | SMMU_CBAR_MEMATTR_WB;
6531a30e4e7Spatrick 	} else {
6541a30e4e7Spatrick 		reg = SMMU_CBAR_TYPE_S2_TRANS;
6551a30e4e7Spatrick 		if (!sc->sc_has_vmid16s)
6561a30e4e7Spatrick 			reg |= (dom->sd_cb_idx + 1) << SMMU_CBAR_VMID_SHIFT;
6571a30e4e7Spatrick 	}
6581a30e4e7Spatrick 	smmu_gr1_write_4(sc, SMMU_CBAR(dom->sd_cb_idx), reg);
6591a30e4e7Spatrick 
6601a30e4e7Spatrick 	if (dom->sd_stage == 1) {
6611a30e4e7Spatrick 		reg = SMMU_CB_TCR2_AS | SMMU_CB_TCR2_SEP_UPSTREAM;
6621a30e4e7Spatrick 		switch (sc->sc_ipa_bits) {
6631a30e4e7Spatrick 		case 32:
6641a30e4e7Spatrick 			reg |= SMMU_CB_TCR2_PASIZE_32BIT;
6651a30e4e7Spatrick 			break;
6661a30e4e7Spatrick 		case 36:
6671a30e4e7Spatrick 			reg |= SMMU_CB_TCR2_PASIZE_36BIT;
6681a30e4e7Spatrick 			break;
6691a30e4e7Spatrick 		case 40:
6701a30e4e7Spatrick 			reg |= SMMU_CB_TCR2_PASIZE_40BIT;
6711a30e4e7Spatrick 			break;
6721a30e4e7Spatrick 		case 42:
6731a30e4e7Spatrick 			reg |= SMMU_CB_TCR2_PASIZE_42BIT;
6741a30e4e7Spatrick 			break;
6751a30e4e7Spatrick 		case 44:
6761a30e4e7Spatrick 			reg |= SMMU_CB_TCR2_PASIZE_44BIT;
6771a30e4e7Spatrick 			break;
6781a30e4e7Spatrick 		case 48:
6791a30e4e7Spatrick 			reg |= SMMU_CB_TCR2_PASIZE_48BIT;
6801a30e4e7Spatrick 			break;
6811a30e4e7Spatrick 		}
6821a30e4e7Spatrick 		smmu_cb_write_4(sc, dom->sd_cb_idx, SMMU_CB_TCR2, reg);
6831a30e4e7Spatrick 	}
6841a30e4e7Spatrick 
6851a30e4e7Spatrick 	if (dom->sd_stage == 1)
6861a30e4e7Spatrick 		iovabits = sc->sc_va_bits;
6871a30e4e7Spatrick 	else
6881a30e4e7Spatrick 		iovabits = sc->sc_ipa_bits;
6891a30e4e7Spatrick 	/*
6901a30e4e7Spatrick 	 * Marvell's 8040 does not support 64-bit writes, hence we
6911a30e4e7Spatrick 	 * can only address 44-bits of VA space for TLB invalidation.
6921a30e4e7Spatrick 	 */
6931a30e4e7Spatrick 	if (sc->sc_is_ap806)
6941a30e4e7Spatrick 		iovabits = min(44, iovabits);
6951a30e4e7Spatrick 	if (iovabits >= 40)
6961a30e4e7Spatrick 		dom->sd_4level = 1;
6971a30e4e7Spatrick 
6981a30e4e7Spatrick 	reg = SMMU_CB_TCR_TG0_4KB | SMMU_CB_TCR_T0SZ(64 - iovabits);
6991a30e4e7Spatrick 	if (dom->sd_stage == 1) {
7001a30e4e7Spatrick 		reg |= SMMU_CB_TCR_EPD1;
7011a30e4e7Spatrick 	} else {
7021a30e4e7Spatrick 		if (dom->sd_4level)
7031a30e4e7Spatrick 			reg |= SMMU_CB_TCR_S2_SL0_4KB_L0;
7041a30e4e7Spatrick 		else
7051a30e4e7Spatrick 			reg |= SMMU_CB_TCR_S2_SL0_4KB_L1;
7061a30e4e7Spatrick 		switch (sc->sc_pa_bits) {
7071a30e4e7Spatrick 		case 32:
7081a30e4e7Spatrick 			reg |= SMMU_CB_TCR_S2_PASIZE_32BIT;
7091a30e4e7Spatrick 			break;
7101a30e4e7Spatrick 		case 36:
7111a30e4e7Spatrick 			reg |= SMMU_CB_TCR_S2_PASIZE_36BIT;
7121a30e4e7Spatrick 			break;
7131a30e4e7Spatrick 		case 40:
7141a30e4e7Spatrick 			reg |= SMMU_CB_TCR_S2_PASIZE_40BIT;
7151a30e4e7Spatrick 			break;
7161a30e4e7Spatrick 		case 42:
7171a30e4e7Spatrick 			reg |= SMMU_CB_TCR_S2_PASIZE_42BIT;
7181a30e4e7Spatrick 			break;
7191a30e4e7Spatrick 		case 44:
7201a30e4e7Spatrick 			reg |= SMMU_CB_TCR_S2_PASIZE_44BIT;
7211a30e4e7Spatrick 			break;
7221a30e4e7Spatrick 		case 48:
7231a30e4e7Spatrick 			reg |= SMMU_CB_TCR_S2_PASIZE_48BIT;
7241a30e4e7Spatrick 			break;
7251a30e4e7Spatrick 		}
7261a30e4e7Spatrick 	}
7271a30e4e7Spatrick 	if (sc->sc_coherent)
7281a30e4e7Spatrick 		reg |= SMMU_CB_TCR_IRGN0_WBWA | SMMU_CB_TCR_ORGN0_WBWA |
7291a30e4e7Spatrick 		    SMMU_CB_TCR_SH0_ISH;
7301a30e4e7Spatrick 	else
7311a30e4e7Spatrick 		reg |= SMMU_CB_TCR_IRGN0_NC | SMMU_CB_TCR_ORGN0_NC |
7321a30e4e7Spatrick 		    SMMU_CB_TCR_SH0_OSH;
7331a30e4e7Spatrick 	smmu_cb_write_4(sc, dom->sd_cb_idx, SMMU_CB_TCR, reg);
7341a30e4e7Spatrick 
7351a30e4e7Spatrick 	if (dom->sd_4level) {
7361a30e4e7Spatrick 		while (dom->sd_vp.l0 == NULL) {
7371a30e4e7Spatrick 			dom->sd_vp.l0 = pool_get(&sc->sc_vp_pool,
7381a30e4e7Spatrick 			    PR_WAITOK | PR_ZERO);
7391a30e4e7Spatrick 		}
7401a30e4e7Spatrick 		l0va = (vaddr_t)dom->sd_vp.l0->l0; /* top level is l0 */
7411a30e4e7Spatrick 	} else {
7421a30e4e7Spatrick 		while (dom->sd_vp.l1 == NULL) {
7431a30e4e7Spatrick 			dom->sd_vp.l1 = pool_get(&sc->sc_vp_pool,
7441a30e4e7Spatrick 			    PR_WAITOK | PR_ZERO);
7451a30e4e7Spatrick 		}
7461a30e4e7Spatrick 		l0va = (vaddr_t)dom->sd_vp.l1->l1; /* top level is l1 */
7471a30e4e7Spatrick 	}
7481a30e4e7Spatrick 	pmap_extract(pmap_kernel(), l0va, &pa);
7491a30e4e7Spatrick 
7501a30e4e7Spatrick 	if (dom->sd_stage == 1) {
7511a30e4e7Spatrick 		smmu_cb_write_8(sc, dom->sd_cb_idx, SMMU_CB_TTBR0,
7521a30e4e7Spatrick 		    (uint64_t)dom->sd_cb_idx << SMMU_CB_TTBR_ASID_SHIFT | pa);
7531a30e4e7Spatrick 		smmu_cb_write_8(sc, dom->sd_cb_idx, SMMU_CB_TTBR1,
7541a30e4e7Spatrick 		    (uint64_t)dom->sd_cb_idx << SMMU_CB_TTBR_ASID_SHIFT);
7551a30e4e7Spatrick 	} else
7561a30e4e7Spatrick 		smmu_cb_write_8(sc, dom->sd_cb_idx, SMMU_CB_TTBR0, pa);
7571a30e4e7Spatrick 
7581a30e4e7Spatrick 	if (dom->sd_stage == 1) {
7591a30e4e7Spatrick 		smmu_cb_write_4(sc, dom->sd_cb_idx, SMMU_CB_MAIR0,
7601a30e4e7Spatrick 		    SMMU_CB_MAIR_MAIR_ATTR(SMMU_CB_MAIR_DEVICE_nGnRnE, 0) |
7611a30e4e7Spatrick 		    SMMU_CB_MAIR_MAIR_ATTR(SMMU_CB_MAIR_DEVICE_nGnRE, 1) |
7621a30e4e7Spatrick 		    SMMU_CB_MAIR_MAIR_ATTR(SMMU_CB_MAIR_DEVICE_NC, 2) |
7631a30e4e7Spatrick 		    SMMU_CB_MAIR_MAIR_ATTR(SMMU_CB_MAIR_DEVICE_WB, 3));
7641a30e4e7Spatrick 		smmu_cb_write_4(sc, dom->sd_cb_idx, SMMU_CB_MAIR1,
7651a30e4e7Spatrick 		    SMMU_CB_MAIR_MAIR_ATTR(SMMU_CB_MAIR_DEVICE_WT, 0));
7661a30e4e7Spatrick 	}
7671a30e4e7Spatrick 
7681a30e4e7Spatrick 	reg = SMMU_CB_SCTLR_M | SMMU_CB_SCTLR_TRE | SMMU_CB_SCTLR_AFE |
7691a30e4e7Spatrick 	    SMMU_CB_SCTLR_CFRE | SMMU_CB_SCTLR_CFIE;
7701a30e4e7Spatrick 	if (dom->sd_stage == 1)
7711a30e4e7Spatrick 		reg |= SMMU_CB_SCTLR_ASIDPNE;
7721a30e4e7Spatrick 	smmu_cb_write_4(sc, dom->sd_cb_idx, SMMU_CB_SCTLR, reg);
7731a30e4e7Spatrick 
7741a30e4e7Spatrick 	/* Point stream to context block */
7751a30e4e7Spatrick 	reg = SMMU_S2CR_TYPE_TRANS | dom->sd_cb_idx;
7761a30e4e7Spatrick 	if (sc->sc_has_exids && sc->sc_smr)
7771a30e4e7Spatrick 		reg |= SMMU_S2CR_EXIDVALID;
7781a30e4e7Spatrick 	smmu_gr0_write_4(sc, SMMU_S2CR(dom->sd_smr_idx), reg);
7791a30e4e7Spatrick 
7801a30e4e7Spatrick 	/* Map stream idx to S2CR idx */
7811a30e4e7Spatrick 	if (sc->sc_smr) {
7821a30e4e7Spatrick 		reg = sid;
7831a30e4e7Spatrick 		if (!sc->sc_has_exids)
7841a30e4e7Spatrick 			reg |= SMMU_SMR_VALID;
7851a30e4e7Spatrick 		smmu_gr0_write_4(sc, SMMU_SMR(dom->sd_smr_idx), reg);
7861a30e4e7Spatrick 	}
7871a30e4e7Spatrick 
7881a30e4e7Spatrick 	snprintf(dom->sd_exname, sizeof(dom->sd_exname), "%s:%x",
7891a30e4e7Spatrick 	    sc->sc_dev.dv_xname, sid);
7907ffc43e0Spatrick 	dom->sd_iovamap = extent_create(dom->sd_exname, 0,
791ab86b902Spatrick 	    (1LL << iovabits) - 1, M_DEVBUF, NULL, 0, EX_WAITOK |
792ab86b902Spatrick 	    EX_NOCOALESCE);
7931a30e4e7Spatrick 
7947ffc43e0Spatrick 	/* Reserve first page (to catch NULL access) */
7957ffc43e0Spatrick 	extent_alloc_region(dom->sd_iovamap, 0, PAGE_SIZE, EX_WAITOK);
7967ffc43e0Spatrick 
7971a30e4e7Spatrick 	SIMPLEQ_INSERT_TAIL(&sc->sc_domains, dom, sd_list);
7981a30e4e7Spatrick 	return dom;
7991a30e4e7Spatrick }
8001a30e4e7Spatrick 
801415019ceSpatrick void
smmu_reserve_region(void * cookie,uint32_t sid,bus_addr_t addr,bus_size_t size)802415019ceSpatrick smmu_reserve_region(void *cookie, uint32_t sid, bus_addr_t addr,
803415019ceSpatrick     bus_size_t size)
804415019ceSpatrick {
805415019ceSpatrick 	struct smmu_softc *sc = cookie;
806415019ceSpatrick 	struct smmu_domain *dom;
807415019ceSpatrick 
808415019ceSpatrick 	dom = smmu_domain_lookup(sc, sid);
809415019ceSpatrick 	if (dom == NULL)
810415019ceSpatrick 		return;
811415019ceSpatrick 
812415019ceSpatrick 	extent_alloc_region(dom->sd_iovamap, addr, size,
813415019ceSpatrick 	    EX_WAITOK | EX_CONFLICTOK);
814415019ceSpatrick }
815415019ceSpatrick 
8161a30e4e7Spatrick /* basically pmap follows */
8171a30e4e7Spatrick 
8181a30e4e7Spatrick /* virtual to physical helpers */
8191a30e4e7Spatrick static inline int
VP_IDX0(vaddr_t va)8201a30e4e7Spatrick VP_IDX0(vaddr_t va)
8211a30e4e7Spatrick {
8221a30e4e7Spatrick 	return (va >> VP_IDX0_POS) & VP_IDX0_MASK;
8231a30e4e7Spatrick }
8241a30e4e7Spatrick 
8251a30e4e7Spatrick static inline int
VP_IDX1(vaddr_t va)8261a30e4e7Spatrick VP_IDX1(vaddr_t va)
8271a30e4e7Spatrick {
8281a30e4e7Spatrick 	return (va >> VP_IDX1_POS) & VP_IDX1_MASK;
8291a30e4e7Spatrick }
8301a30e4e7Spatrick 
8311a30e4e7Spatrick static inline int
VP_IDX2(vaddr_t va)8321a30e4e7Spatrick VP_IDX2(vaddr_t va)
8331a30e4e7Spatrick {
8341a30e4e7Spatrick 	return (va >> VP_IDX2_POS) & VP_IDX2_MASK;
8351a30e4e7Spatrick }
8361a30e4e7Spatrick 
8371a30e4e7Spatrick static inline int
VP_IDX3(vaddr_t va)8381a30e4e7Spatrick VP_IDX3(vaddr_t va)
8391a30e4e7Spatrick {
8401a30e4e7Spatrick 	return (va >> VP_IDX3_POS) & VP_IDX3_MASK;
8411a30e4e7Spatrick }
8421a30e4e7Spatrick 
8431a30e4e7Spatrick static inline uint64_t
VP_Lx(paddr_t pa)8441a30e4e7Spatrick VP_Lx(paddr_t pa)
8451a30e4e7Spatrick {
8461a30e4e7Spatrick 	/*
8471a30e4e7Spatrick 	 * This function takes the pa address given and manipulates it
8481a30e4e7Spatrick 	 * into the form that should be inserted into the VM table.
8491a30e4e7Spatrick 	 */
8501a30e4e7Spatrick 	return pa | Lx_TYPE_PT;
8511a30e4e7Spatrick }
8521a30e4e7Spatrick 
8531a30e4e7Spatrick void
smmu_set_l1(struct smmu_domain * dom,uint64_t va,struct smmuvp1 * l1_va)8541a30e4e7Spatrick smmu_set_l1(struct smmu_domain *dom, uint64_t va, struct smmuvp1 *l1_va)
8551a30e4e7Spatrick {
856*6258f8b2Spatrick 	struct smmu_softc *sc = dom->sd_sc;
8571a30e4e7Spatrick 	uint64_t pg_entry;
8581a30e4e7Spatrick 	paddr_t l1_pa;
8591a30e4e7Spatrick 	int idx0;
8601a30e4e7Spatrick 
8611a30e4e7Spatrick 	if (pmap_extract(pmap_kernel(), (vaddr_t)l1_va, &l1_pa) == 0)
8622322545bSpatrick 		panic("%s: unable to find vp pa mapping %p", __func__, l1_va);
8631a30e4e7Spatrick 
8641a30e4e7Spatrick 	if (l1_pa & (Lx_TABLE_ALIGN-1))
8652322545bSpatrick 		panic("%s: misaligned L2 table", __func__);
8661a30e4e7Spatrick 
8671a30e4e7Spatrick 	pg_entry = VP_Lx(l1_pa);
8681a30e4e7Spatrick 
8691a30e4e7Spatrick 	idx0 = VP_IDX0(va);
8701a30e4e7Spatrick 	dom->sd_vp.l0->vp[idx0] = l1_va;
8711a30e4e7Spatrick 	dom->sd_vp.l0->l0[idx0] = pg_entry;
872*6258f8b2Spatrick 	membar_producer(); /* XXX bus dma sync? */
873*6258f8b2Spatrick 	if (!sc->sc_coherent)
874*6258f8b2Spatrick 		cpu_dcache_wb_range((vaddr_t)&dom->sd_vp.l0->l0[idx0],
875*6258f8b2Spatrick 		    sizeof(dom->sd_vp.l0->l0[idx0]));
8761a30e4e7Spatrick }
8771a30e4e7Spatrick 
8781a30e4e7Spatrick void
smmu_set_l2(struct smmu_domain * dom,uint64_t va,struct smmuvp1 * vp1,struct smmuvp2 * l2_va)8791a30e4e7Spatrick smmu_set_l2(struct smmu_domain *dom, uint64_t va, struct smmuvp1 *vp1,
8801a30e4e7Spatrick     struct smmuvp2 *l2_va)
8811a30e4e7Spatrick {
882*6258f8b2Spatrick 	struct smmu_softc *sc = dom->sd_sc;
8831a30e4e7Spatrick 	uint64_t pg_entry;
8841a30e4e7Spatrick 	paddr_t l2_pa;
8851a30e4e7Spatrick 	int idx1;
8861a30e4e7Spatrick 
8871a30e4e7Spatrick 	if (pmap_extract(pmap_kernel(), (vaddr_t)l2_va, &l2_pa) == 0)
8882322545bSpatrick 		panic("%s: unable to find vp pa mapping %p", __func__, l2_va);
8891a30e4e7Spatrick 
8901a30e4e7Spatrick 	if (l2_pa & (Lx_TABLE_ALIGN-1))
8912322545bSpatrick 		panic("%s: misaligned L2 table", __func__);
8921a30e4e7Spatrick 
8931a30e4e7Spatrick 	pg_entry = VP_Lx(l2_pa);
8941a30e4e7Spatrick 
8951a30e4e7Spatrick 	idx1 = VP_IDX1(va);
8961a30e4e7Spatrick 	vp1->vp[idx1] = l2_va;
8971a30e4e7Spatrick 	vp1->l1[idx1] = pg_entry;
898*6258f8b2Spatrick 	membar_producer(); /* XXX bus dma sync? */
899*6258f8b2Spatrick 	if (!sc->sc_coherent)
900*6258f8b2Spatrick 		cpu_dcache_wb_range((vaddr_t)&vp1->l1[idx1],
901*6258f8b2Spatrick 		    sizeof(vp1->l1[idx1]));
9021a30e4e7Spatrick }
9031a30e4e7Spatrick 
9041a30e4e7Spatrick void
smmu_set_l3(struct smmu_domain * dom,uint64_t va,struct smmuvp2 * vp2,struct smmuvp3 * l3_va)9051a30e4e7Spatrick smmu_set_l3(struct smmu_domain *dom, uint64_t va, struct smmuvp2 *vp2,
9061a30e4e7Spatrick     struct smmuvp3 *l3_va)
9071a30e4e7Spatrick {
908*6258f8b2Spatrick 	struct smmu_softc *sc = dom->sd_sc;
9091a30e4e7Spatrick 	uint64_t pg_entry;
9101a30e4e7Spatrick 	paddr_t l3_pa;
9111a30e4e7Spatrick 	int idx2;
9121a30e4e7Spatrick 
9131a30e4e7Spatrick 	if (pmap_extract(pmap_kernel(), (vaddr_t)l3_va, &l3_pa) == 0)
9142322545bSpatrick 		panic("%s: unable to find vp pa mapping %p", __func__, l3_va);
9151a30e4e7Spatrick 
9161a30e4e7Spatrick 	if (l3_pa & (Lx_TABLE_ALIGN-1))
9172322545bSpatrick 		panic("%s: misaligned L2 table", __func__);
9181a30e4e7Spatrick 
9191a30e4e7Spatrick 	pg_entry = VP_Lx(l3_pa);
9201a30e4e7Spatrick 
9211a30e4e7Spatrick 	idx2 = VP_IDX2(va);
9221a30e4e7Spatrick 	vp2->vp[idx2] = l3_va;
9231a30e4e7Spatrick 	vp2->l2[idx2] = pg_entry;
924*6258f8b2Spatrick 	membar_producer(); /* XXX bus dma sync? */
925*6258f8b2Spatrick 	if (!sc->sc_coherent)
926*6258f8b2Spatrick 		cpu_dcache_wb_range((vaddr_t)&vp2->l2[idx2],
927*6258f8b2Spatrick 		    sizeof(vp2->l2[idx2]));
9281a30e4e7Spatrick }
9291a30e4e7Spatrick 
9302322545bSpatrick int
smmu_vp_lookup(struct smmu_domain * dom,vaddr_t va,uint64_t ** pl3entry)9311a30e4e7Spatrick smmu_vp_lookup(struct smmu_domain *dom, vaddr_t va, uint64_t **pl3entry)
9321a30e4e7Spatrick {
9331a30e4e7Spatrick 	struct smmuvp1 *vp1;
9341a30e4e7Spatrick 	struct smmuvp2 *vp2;
9351a30e4e7Spatrick 	struct smmuvp3 *vp3;
9361a30e4e7Spatrick 
9371a30e4e7Spatrick 	if (dom->sd_4level) {
9381a30e4e7Spatrick 		if (dom->sd_vp.l0 == NULL) {
9392322545bSpatrick 			return ENXIO;
9401a30e4e7Spatrick 		}
9411a30e4e7Spatrick 		vp1 = dom->sd_vp.l0->vp[VP_IDX0(va)];
9421a30e4e7Spatrick 	} else {
9431a30e4e7Spatrick 		vp1 = dom->sd_vp.l1;
9441a30e4e7Spatrick 	}
9451a30e4e7Spatrick 	if (vp1 == NULL) {
9462322545bSpatrick 		return ENXIO;
9471a30e4e7Spatrick 	}
9481a30e4e7Spatrick 
9491a30e4e7Spatrick 	vp2 = vp1->vp[VP_IDX1(va)];
9501a30e4e7Spatrick 	if (vp2 == NULL) {
9512322545bSpatrick 		return ENXIO;
9521a30e4e7Spatrick 	}
9531a30e4e7Spatrick 
9541a30e4e7Spatrick 	vp3 = vp2->vp[VP_IDX2(va)];
9551a30e4e7Spatrick 	if (vp3 == NULL) {
9562322545bSpatrick 		return ENXIO;
9571a30e4e7Spatrick 	}
9581a30e4e7Spatrick 
9591a30e4e7Spatrick 	if (pl3entry != NULL)
9601a30e4e7Spatrick 		*pl3entry = &(vp3->l3[VP_IDX3(va)]);
9611a30e4e7Spatrick 
9622322545bSpatrick 	return 0;
9631a30e4e7Spatrick }
9641a30e4e7Spatrick 
9651a30e4e7Spatrick int
smmu_vp_enter(struct smmu_domain * dom,vaddr_t va,uint64_t ** pl3entry,int flags)9662322545bSpatrick smmu_vp_enter(struct smmu_domain *dom, vaddr_t va, uint64_t **pl3entry,
9671a30e4e7Spatrick     int flags)
9681a30e4e7Spatrick {
9691a30e4e7Spatrick 	struct smmu_softc *sc = dom->sd_sc;
9701a30e4e7Spatrick 	struct smmuvp1 *vp1;
9711a30e4e7Spatrick 	struct smmuvp2 *vp2;
9721a30e4e7Spatrick 	struct smmuvp3 *vp3;
9731a30e4e7Spatrick 
9741a30e4e7Spatrick 	if (dom->sd_4level) {
9751a30e4e7Spatrick 		vp1 = dom->sd_vp.l0->vp[VP_IDX0(va)];
9761a30e4e7Spatrick 		if (vp1 == NULL) {
9770aa222ffSpatrick 			mtx_enter(&dom->sd_pmap_mtx);
9780aa222ffSpatrick 			vp1 = dom->sd_vp.l0->vp[VP_IDX0(va)];
9791a30e4e7Spatrick 			if (vp1 == NULL) {
9800aa222ffSpatrick 				vp1 = pool_get(&sc->sc_vp_pool,
9810aa222ffSpatrick 				    PR_NOWAIT | PR_ZERO);
9820aa222ffSpatrick 				if (vp1 == NULL) {
9830aa222ffSpatrick 					mtx_leave(&dom->sd_pmap_mtx);
9841a30e4e7Spatrick 					return ENOMEM;
9851a30e4e7Spatrick 				}
9861a30e4e7Spatrick 				smmu_set_l1(dom, va, vp1);
9871a30e4e7Spatrick 			}
9880aa222ffSpatrick 			mtx_leave(&dom->sd_pmap_mtx);
9890aa222ffSpatrick 		}
9901a30e4e7Spatrick 	} else {
9911a30e4e7Spatrick 		vp1 = dom->sd_vp.l1;
9921a30e4e7Spatrick 	}
9931a30e4e7Spatrick 
9941a30e4e7Spatrick 	vp2 = vp1->vp[VP_IDX1(va)];
9951a30e4e7Spatrick 	if (vp2 == NULL) {
9960aa222ffSpatrick 		mtx_enter(&dom->sd_pmap_mtx);
9970aa222ffSpatrick 		vp2 = vp1->vp[VP_IDX1(va)];
9980aa222ffSpatrick 		if (vp2 == NULL) {
9991a30e4e7Spatrick 			vp2 = pool_get(&sc->sc_vp_pool, PR_NOWAIT | PR_ZERO);
10001a30e4e7Spatrick 			if (vp2 == NULL) {
10010aa222ffSpatrick 				mtx_leave(&dom->sd_pmap_mtx);
10021a30e4e7Spatrick 				return ENOMEM;
10031a30e4e7Spatrick 			}
10041a30e4e7Spatrick 			smmu_set_l2(dom, va, vp1, vp2);
10051a30e4e7Spatrick 		}
10060aa222ffSpatrick 		mtx_leave(&dom->sd_pmap_mtx);
10070aa222ffSpatrick 	}
10081a30e4e7Spatrick 
10091a30e4e7Spatrick 	vp3 = vp2->vp[VP_IDX2(va)];
10101a30e4e7Spatrick 	if (vp3 == NULL) {
10110aa222ffSpatrick 		mtx_enter(&dom->sd_pmap_mtx);
10120aa222ffSpatrick 		vp3 = vp2->vp[VP_IDX2(va)];
10130aa222ffSpatrick 		if (vp3 == NULL) {
10142322545bSpatrick 			vp3 = pool_get(&sc->sc_vp3_pool, PR_NOWAIT | PR_ZERO);
10151a30e4e7Spatrick 			if (vp3 == NULL) {
10160aa222ffSpatrick 				mtx_leave(&dom->sd_pmap_mtx);
10171a30e4e7Spatrick 				return ENOMEM;
10181a30e4e7Spatrick 			}
10191a30e4e7Spatrick 			smmu_set_l3(dom, va, vp2, vp3);
10201a30e4e7Spatrick 		}
10210aa222ffSpatrick 		mtx_leave(&dom->sd_pmap_mtx);
10220aa222ffSpatrick 	}
10231a30e4e7Spatrick 
10242322545bSpatrick 	if (pl3entry != NULL)
10252322545bSpatrick 		*pl3entry = &(vp3->l3[VP_IDX3(va)]);
10262322545bSpatrick 
10271a30e4e7Spatrick 	return 0;
10281a30e4e7Spatrick }
10291a30e4e7Spatrick 
10302322545bSpatrick uint64_t
smmu_fill_pte(struct smmu_domain * dom,vaddr_t va,paddr_t pa,vm_prot_t prot,int flags,int cache)10311a30e4e7Spatrick smmu_fill_pte(struct smmu_domain *dom, vaddr_t va, paddr_t pa,
10322322545bSpatrick     vm_prot_t prot, int flags, int cache)
10331a30e4e7Spatrick {
10342322545bSpatrick 	uint64_t pted;
10352322545bSpatrick 
10362322545bSpatrick 	pted = pa & PTE_RPGN;
10371a30e4e7Spatrick 
10381a30e4e7Spatrick 	switch (cache) {
10391a30e4e7Spatrick 	case PMAP_CACHE_WB:
10401a30e4e7Spatrick 		break;
10411a30e4e7Spatrick 	case PMAP_CACHE_WT:
10421a30e4e7Spatrick 		break;
10431a30e4e7Spatrick 	case PMAP_CACHE_CI:
10441a30e4e7Spatrick 		break;
10451a30e4e7Spatrick 	case PMAP_CACHE_DEV_NGNRNE:
10461a30e4e7Spatrick 		break;
10471a30e4e7Spatrick 	case PMAP_CACHE_DEV_NGNRE:
10481a30e4e7Spatrick 		break;
10491a30e4e7Spatrick 	default:
10501a30e4e7Spatrick 		panic("%s: invalid cache mode", __func__);
10511a30e4e7Spatrick 	}
10521a30e4e7Spatrick 
10532322545bSpatrick 	pted |= cache;
10542322545bSpatrick 	pted |= flags & (PROT_READ|PROT_WRITE|PROT_EXEC);
10552322545bSpatrick 	return pted;
10561a30e4e7Spatrick }
10571a30e4e7Spatrick 
10581a30e4e7Spatrick void
smmu_pte_update(struct smmu_domain * dom,uint64_t pted,uint64_t * pl3)10592322545bSpatrick smmu_pte_update(struct smmu_domain *dom, uint64_t pted, uint64_t *pl3)
10601a30e4e7Spatrick {
1061*6258f8b2Spatrick 	struct smmu_softc *sc = dom->sd_sc;
10621a30e4e7Spatrick 	uint64_t pte, access_bits;
10631a30e4e7Spatrick 	uint64_t attr = 0;
10641a30e4e7Spatrick 
10651a30e4e7Spatrick 	/* see mair in locore.S */
10662322545bSpatrick 	switch (pted & PMAP_CACHE_BITS) {
10671a30e4e7Spatrick 	case PMAP_CACHE_WB:
10681a30e4e7Spatrick 		/* inner and outer writeback */
10691a30e4e7Spatrick 		if (dom->sd_stage == 1)
10701a30e4e7Spatrick 			attr |= ATTR_IDX(PTE_ATTR_WB);
10711a30e4e7Spatrick 		else
10721a30e4e7Spatrick 			attr |= ATTR_IDX(PTE_MEMATTR_WB);
10731a30e4e7Spatrick 		attr |= ATTR_SH(SH_INNER);
10741a30e4e7Spatrick 		break;
10751a30e4e7Spatrick 	case PMAP_CACHE_WT:
10761a30e4e7Spatrick 		/* inner and outer writethrough */
10771a30e4e7Spatrick 		if (dom->sd_stage == 1)
10781a30e4e7Spatrick 			attr |= ATTR_IDX(PTE_ATTR_WT);
10791a30e4e7Spatrick 		else
10801a30e4e7Spatrick 			attr |= ATTR_IDX(PTE_MEMATTR_WT);
10811a30e4e7Spatrick 		attr |= ATTR_SH(SH_INNER);
10821a30e4e7Spatrick 		break;
10831a30e4e7Spatrick 	case PMAP_CACHE_CI:
10841a30e4e7Spatrick 		if (dom->sd_stage == 1)
10851a30e4e7Spatrick 			attr |= ATTR_IDX(PTE_ATTR_CI);
10861a30e4e7Spatrick 		else
10871a30e4e7Spatrick 			attr |= ATTR_IDX(PTE_MEMATTR_CI);
10881a30e4e7Spatrick 		attr |= ATTR_SH(SH_INNER);
10891a30e4e7Spatrick 		break;
10901a30e4e7Spatrick 	case PMAP_CACHE_DEV_NGNRNE:
10911a30e4e7Spatrick 		if (dom->sd_stage == 1)
10921a30e4e7Spatrick 			attr |= ATTR_IDX(PTE_ATTR_DEV_NGNRNE);
10931a30e4e7Spatrick 		else
10941a30e4e7Spatrick 			attr |= ATTR_IDX(PTE_MEMATTR_DEV_NGNRNE);
10951a30e4e7Spatrick 		attr |= ATTR_SH(SH_INNER);
10961a30e4e7Spatrick 		break;
10971a30e4e7Spatrick 	case PMAP_CACHE_DEV_NGNRE:
10981a30e4e7Spatrick 		if (dom->sd_stage == 1)
10991a30e4e7Spatrick 			attr |= ATTR_IDX(PTE_ATTR_DEV_NGNRE);
11001a30e4e7Spatrick 		else
11011a30e4e7Spatrick 			attr |= ATTR_IDX(PTE_MEMATTR_DEV_NGNRE);
11021a30e4e7Spatrick 		attr |= ATTR_SH(SH_INNER);
11031a30e4e7Spatrick 		break;
11041a30e4e7Spatrick 	default:
11051a30e4e7Spatrick 		panic("%s: invalid cache mode", __func__);
11061a30e4e7Spatrick 	}
11071a30e4e7Spatrick 
11081a30e4e7Spatrick 	access_bits = ATTR_PXN | ATTR_AF;
11091a30e4e7Spatrick 	if (dom->sd_stage == 1) {
11101a30e4e7Spatrick 		attr |= ATTR_nG;
11111a30e4e7Spatrick 		access_bits |= ATTR_AP(1);
11122322545bSpatrick 		if ((pted & PROT_READ) &&
11132322545bSpatrick 		    !(pted & PROT_WRITE))
11141a30e4e7Spatrick 			access_bits |= ATTR_AP(2);
11151a30e4e7Spatrick 	} else {
11162322545bSpatrick 		if (pted & PROT_READ)
11171a30e4e7Spatrick 			access_bits |= ATTR_AP(1);
11182322545bSpatrick 		if (pted & PROT_WRITE)
11191a30e4e7Spatrick 			access_bits |= ATTR_AP(2);
11201a30e4e7Spatrick 	}
11211a30e4e7Spatrick 
11222322545bSpatrick 	pte = (pted & PTE_RPGN) | attr | access_bits | L3_P;
11231a30e4e7Spatrick 	*pl3 = pte;
1124*6258f8b2Spatrick 	membar_producer(); /* XXX bus dma sync? */
1125*6258f8b2Spatrick 	if (!sc->sc_coherent)
1126*6258f8b2Spatrick 		cpu_dcache_wb_range((vaddr_t)pl3, sizeof(*pl3));
11271a30e4e7Spatrick }
11281a30e4e7Spatrick 
11291a30e4e7Spatrick void
smmu_pte_remove(struct smmu_domain * dom,vaddr_t va)11302322545bSpatrick smmu_pte_remove(struct smmu_domain *dom, vaddr_t va)
11311a30e4e7Spatrick {
11321a30e4e7Spatrick 	/* put entry into table */
11331a30e4e7Spatrick 	/* need to deal with ref/change here */
1134*6258f8b2Spatrick 	struct smmu_softc *sc = dom->sd_sc;
11351a30e4e7Spatrick 	struct smmuvp1 *vp1;
11361a30e4e7Spatrick 	struct smmuvp2 *vp2;
11371a30e4e7Spatrick 	struct smmuvp3 *vp3;
11381a30e4e7Spatrick 
11391a30e4e7Spatrick 	if (dom->sd_4level)
11402322545bSpatrick 		vp1 = dom->sd_vp.l0->vp[VP_IDX0(va)];
11411a30e4e7Spatrick 	else
11421a30e4e7Spatrick 		vp1 = dom->sd_vp.l1;
11431a30e4e7Spatrick 	if (vp1 == NULL) {
11442322545bSpatrick 		panic("%s: missing the l1 for va %lx domain %p", __func__,
11452322545bSpatrick 		    va, dom);
11461a30e4e7Spatrick 	}
11472322545bSpatrick 	vp2 = vp1->vp[VP_IDX1(va)];
11481a30e4e7Spatrick 	if (vp2 == NULL) {
11492322545bSpatrick 		panic("%s: missing the l2 for va %lx domain %p", __func__,
11502322545bSpatrick 		    va, dom);
11511a30e4e7Spatrick 	}
11522322545bSpatrick 	vp3 = vp2->vp[VP_IDX2(va)];
11531a30e4e7Spatrick 	if (vp3 == NULL) {
11542322545bSpatrick 		panic("%s: missing the l3 for va %lx domain %p", __func__,
11552322545bSpatrick 		    va, dom);
11561a30e4e7Spatrick 	}
11572322545bSpatrick 	vp3->l3[VP_IDX3(va)] = 0;
1158*6258f8b2Spatrick 	membar_producer(); /* XXX bus dma sync? */
1159*6258f8b2Spatrick 	if (!sc->sc_coherent)
1160*6258f8b2Spatrick 		cpu_dcache_wb_range((vaddr_t)&vp3->l3[VP_IDX3(va)],
1161*6258f8b2Spatrick 		    sizeof(vp3->l3[VP_IDX3(va)]));
11621a30e4e7Spatrick }
11631a30e4e7Spatrick 
11641a30e4e7Spatrick int
smmu_enter(struct smmu_domain * dom,vaddr_t va,paddr_t pa,vm_prot_t prot,int flags,int cache)1165b9720b93Spatrick smmu_enter(struct smmu_domain *dom, vaddr_t va, paddr_t pa, vm_prot_t prot,
11661a30e4e7Spatrick     int flags, int cache)
11671a30e4e7Spatrick {
11682322545bSpatrick 	uint64_t *pl3;
11691a30e4e7Spatrick 
11702322545bSpatrick 	if (smmu_vp_lookup(dom, va, &pl3) != 0) {
11712322545bSpatrick 		if (smmu_vp_enter(dom, va, &pl3, flags))
11722322545bSpatrick 			return ENOMEM;
11731a30e4e7Spatrick 	}
11741a30e4e7Spatrick 
11752322545bSpatrick 	if (flags & (PROT_READ|PROT_WRITE|PROT_EXEC))
11762322545bSpatrick 		smmu_map(dom, va, pa, prot, flags, cache);
11771a30e4e7Spatrick 
11782322545bSpatrick 	return 0;
11791a30e4e7Spatrick }
11801a30e4e7Spatrick 
1181b9720b93Spatrick void
smmu_map(struct smmu_domain * dom,vaddr_t va,paddr_t pa,vm_prot_t prot,int flags,int cache)1182b9720b93Spatrick smmu_map(struct smmu_domain *dom, vaddr_t va, paddr_t pa, vm_prot_t prot,
11831a30e4e7Spatrick     int flags, int cache)
11841a30e4e7Spatrick {
11852322545bSpatrick 	uint64_t *pl3;
11862322545bSpatrick 	uint64_t pted;
11872322545bSpatrick 	int ret;
11881a30e4e7Spatrick 
1189b9720b93Spatrick 	/* IOVA must already be allocated */
11902322545bSpatrick 	ret = smmu_vp_lookup(dom, va, &pl3);
11912322545bSpatrick 	KASSERT(ret == 0);
1192b9720b93Spatrick 
1193b9720b93Spatrick 	/* Update PTED information for physical address */
11942322545bSpatrick 	pted = smmu_fill_pte(dom, va, pa, prot, flags, cache);
1195b9720b93Spatrick 
1196b9720b93Spatrick 	/* Insert updated information */
11972322545bSpatrick 	smmu_pte_update(dom, pted, pl3);
11981a30e4e7Spatrick }
11991a30e4e7Spatrick 
1200b9720b93Spatrick void
smmu_unmap(struct smmu_domain * dom,vaddr_t va)1201b9720b93Spatrick smmu_unmap(struct smmu_domain *dom, vaddr_t va)
1202b9720b93Spatrick {
1203b9720b93Spatrick 	struct smmu_softc *sc = dom->sd_sc;
12042322545bSpatrick 	int ret;
12051a30e4e7Spatrick 
1206b9720b93Spatrick 	/* IOVA must already be allocated */
12072322545bSpatrick 	ret = smmu_vp_lookup(dom, va, NULL);
12082322545bSpatrick 	KASSERT(ret == 0);
1209b9720b93Spatrick 
12102322545bSpatrick 	/* Remove mapping from pagetable */
12112322545bSpatrick 	smmu_pte_remove(dom, va);
1212b9720b93Spatrick 
1213b9720b93Spatrick 	/* Invalidate IOTLB */
1214b9720b93Spatrick 	if (dom->sd_stage == 1)
1215b9720b93Spatrick 		smmu_cb_write_8(sc, dom->sd_cb_idx, SMMU_CB_TLBIVAL,
1216b9720b93Spatrick 		    (uint64_t)dom->sd_cb_idx << 48 | va >> PAGE_SHIFT);
1217b9720b93Spatrick 	else
1218b9720b93Spatrick 		smmu_cb_write_8(sc, dom->sd_cb_idx, SMMU_CB_TLBIIPAS2L,
1219b9720b93Spatrick 		    va >> PAGE_SHIFT);
12201a30e4e7Spatrick }
12211a30e4e7Spatrick 
12221a30e4e7Spatrick void
smmu_remove(struct smmu_domain * dom,vaddr_t va)12231a30e4e7Spatrick smmu_remove(struct smmu_domain *dom, vaddr_t va)
12241a30e4e7Spatrick {
12252322545bSpatrick 	/* TODO: garbage collect page tables? */
12261a30e4e7Spatrick }
12271a30e4e7Spatrick 
12281a30e4e7Spatrick int
smmu_load_map(struct smmu_domain * dom,bus_dmamap_t map)12291a30e4e7Spatrick smmu_load_map(struct smmu_domain *dom, bus_dmamap_t map)
12301a30e4e7Spatrick {
12310aa222ffSpatrick 	struct smmu_map_state *sms = map->_dm_cookie;
12320aa222ffSpatrick 	u_long dva, maplen;
1233b9720b93Spatrick 	int seg;
12341a30e4e7Spatrick 
12350aa222ffSpatrick 	maplen = 0;
12361a30e4e7Spatrick 	for (seg = 0; seg < map->dm_nsegs; seg++) {
12370aa222ffSpatrick 		paddr_t pa = map->dm_segs[seg]._ds_paddr;
12380aa222ffSpatrick 		psize_t off = pa - trunc_page(pa);
12390aa222ffSpatrick 		maplen += round_page(map->dm_segs[seg].ds_len + off);
12401a30e4e7Spatrick 	}
1241b9720b93Spatrick 	KASSERT(maplen <= sms->sms_len);
12421a30e4e7Spatrick 
1243b9720b93Spatrick 	dva = sms->sms_dva;
12440aa222ffSpatrick 	for (seg = 0; seg < map->dm_nsegs; seg++) {
12450aa222ffSpatrick 		paddr_t pa = map->dm_segs[seg]._ds_paddr;
12460aa222ffSpatrick 		psize_t off = pa - trunc_page(pa);
12470aa222ffSpatrick 		u_long len = round_page(map->dm_segs[seg].ds_len + off);
12480aa222ffSpatrick 
12490aa222ffSpatrick 		map->dm_segs[seg].ds_addr = dva + off;
12500aa222ffSpatrick 
12510aa222ffSpatrick 		pa = trunc_page(pa);
12520aa222ffSpatrick 		while (len > 0) {
1253b9720b93Spatrick 			smmu_map(dom, dva, pa,
12540aa222ffSpatrick 			    PROT_READ | PROT_WRITE,
12550aa222ffSpatrick 			    PROT_READ | PROT_WRITE, PMAP_CACHE_WB);
12560aa222ffSpatrick 
12570aa222ffSpatrick 			dva += PAGE_SIZE;
12580aa222ffSpatrick 			pa += PAGE_SIZE;
12590aa222ffSpatrick 			len -= PAGE_SIZE;
1260b9720b93Spatrick 			sms->sms_loaded += PAGE_SIZE;
12610aa222ffSpatrick 		}
12620aa222ffSpatrick 	}
12630aa222ffSpatrick 
1264b9720b93Spatrick 	return 0;
12651a30e4e7Spatrick }
12661a30e4e7Spatrick 
12671a30e4e7Spatrick void
smmu_unload_map(struct smmu_domain * dom,bus_dmamap_t map)12681a30e4e7Spatrick smmu_unload_map(struct smmu_domain *dom, bus_dmamap_t map)
12691a30e4e7Spatrick {
12700aa222ffSpatrick 	struct smmu_map_state *sms = map->_dm_cookie;
12710aa222ffSpatrick 	u_long len, dva;
12721a30e4e7Spatrick 
1273b9720b93Spatrick 	if (sms->sms_loaded == 0)
12740aa222ffSpatrick 		return;
12750aa222ffSpatrick 
12760aa222ffSpatrick 	dva = sms->sms_dva;
1277b9720b93Spatrick 	len = sms->sms_loaded;
12780aa222ffSpatrick 
12790aa222ffSpatrick 	while (len > 0) {
1280b9720b93Spatrick 		smmu_unmap(dom, dva);
12810aa222ffSpatrick 
12820aa222ffSpatrick 		dva += PAGE_SIZE;
12830aa222ffSpatrick 		len -= PAGE_SIZE;
12841a30e4e7Spatrick 	}
12850aa222ffSpatrick 
1286b9720b93Spatrick 	sms->sms_loaded = 0;
12871a30e4e7Spatrick 
12881a30e4e7Spatrick 	smmu_tlb_sync_context(dom);
12891a30e4e7Spatrick }
12901a30e4e7Spatrick 
12911a30e4e7Spatrick int
smmu_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)12921a30e4e7Spatrick smmu_dmamap_create(bus_dma_tag_t t, bus_size_t size, int nsegments,
12930aa222ffSpatrick     bus_size_t maxsegsz, bus_size_t boundary, int flags, bus_dmamap_t *dmamap)
12941a30e4e7Spatrick {
12951a30e4e7Spatrick 	struct smmu_domain *dom = t->_cookie;
1296d87886f2Spatrick 	struct smmu_softc *sc = dom->sd_sc;
12970aa222ffSpatrick 	struct smmu_map_state *sms;
12980aa222ffSpatrick 	bus_dmamap_t map;
1299b9720b93Spatrick 	u_long dva, len;
13000aa222ffSpatrick 	int error;
13011a30e4e7Spatrick 
1302d87886f2Spatrick 	error = sc->sc_dmat->_dmamap_create(sc->sc_dmat, size,
13030aa222ffSpatrick 	    nsegments, maxsegsz, boundary, flags, &map);
13040aa222ffSpatrick 	if (error)
13050aa222ffSpatrick 		return error;
13060aa222ffSpatrick 
13070aa222ffSpatrick 	sms = malloc(sizeof(*sms), M_DEVBUF, (flags & BUS_DMA_NOWAIT) ?
13080aa222ffSpatrick 	     (M_NOWAIT|M_ZERO) : (M_WAITOK|M_ZERO));
13090aa222ffSpatrick 	if (sms == NULL) {
1310d87886f2Spatrick 		sc->sc_dmat->_dmamap_destroy(sc->sc_dmat, map);
13110aa222ffSpatrick 		return ENOMEM;
13120aa222ffSpatrick 	}
13130aa222ffSpatrick 
1314b9720b93Spatrick 	/* Approximation of maximum pages needed. */
1315b9720b93Spatrick 	len = round_page(size) + nsegments * PAGE_SIZE;
1316b9720b93Spatrick 
1317a5eb7f55Spatrick 	/* Allocate IOVA, and a guard page at the end. */
1318b9720b93Spatrick 	mtx_enter(&dom->sd_iova_mtx);
1319a5eb7f55Spatrick 	error = extent_alloc_with_descr(dom->sd_iovamap, len + PAGE_SIZE,
1320b9720b93Spatrick 	    PAGE_SIZE, 0, 0, EX_NOWAIT, &sms->sms_er, &dva);
1321b9720b93Spatrick 	mtx_leave(&dom->sd_iova_mtx);
1322b9720b93Spatrick 	if (error) {
1323b9720b93Spatrick 		sc->sc_dmat->_dmamap_destroy(sc->sc_dmat, map);
1324b9720b93Spatrick 		free(sms, M_DEVBUF, sizeof(*sms));
1325b9720b93Spatrick 		return error;
1326b9720b93Spatrick 	}
1327b9720b93Spatrick 
1328b9720b93Spatrick 	sms->sms_dva = dva;
1329b9720b93Spatrick 	sms->sms_len = len;
1330b9720b93Spatrick 
1331b9720b93Spatrick 	while (len > 0) {
1332b9720b93Spatrick 		error = smmu_enter(dom, dva, dva, PROT_READ | PROT_WRITE,
1333b9720b93Spatrick 		    PROT_NONE, PMAP_CACHE_WB);
1334b9720b93Spatrick 		KASSERT(error == 0); /* FIXME: rollback smmu_enter() */
1335b9720b93Spatrick 		dva += PAGE_SIZE;
1336b9720b93Spatrick 		len -= PAGE_SIZE;
1337b9720b93Spatrick 	}
1338b9720b93Spatrick 
13390aa222ffSpatrick 	map->_dm_cookie = sms;
13400aa222ffSpatrick 	*dmamap = map;
13410aa222ffSpatrick 	return 0;
13421a30e4e7Spatrick }
13431a30e4e7Spatrick 
13441a30e4e7Spatrick void
smmu_dmamap_destroy(bus_dma_tag_t t,bus_dmamap_t map)13451a30e4e7Spatrick smmu_dmamap_destroy(bus_dma_tag_t t, bus_dmamap_t map)
13461a30e4e7Spatrick {
13471a30e4e7Spatrick 	struct smmu_domain *dom = t->_cookie;
1348d87886f2Spatrick 	struct smmu_softc *sc = dom->sd_sc;
13490aa222ffSpatrick 	struct smmu_map_state *sms = map->_dm_cookie;
1350b9720b93Spatrick 	u_long dva, len;
1351b9720b93Spatrick 	int error;
13521a30e4e7Spatrick 
1353b9720b93Spatrick 	if (sms->sms_loaded)
13540aa222ffSpatrick 		smmu_dmamap_unload(t, map);
1355b9720b93Spatrick 
1356b9720b93Spatrick 	dva = sms->sms_dva;
1357b9720b93Spatrick 	len = sms->sms_len;
1358b9720b93Spatrick 
1359b9720b93Spatrick 	while (len > 0) {
1360b9720b93Spatrick 		smmu_remove(dom, dva);
1361b9720b93Spatrick 		dva += PAGE_SIZE;
1362b9720b93Spatrick 		len -= PAGE_SIZE;
1363b9720b93Spatrick 	}
1364b9720b93Spatrick 
1365b9720b93Spatrick 	mtx_enter(&dom->sd_iova_mtx);
1366b9720b93Spatrick 	error = extent_free(dom->sd_iovamap, sms->sms_dva,
1367a5eb7f55Spatrick 	    sms->sms_len + PAGE_SIZE, EX_NOWAIT);
1368b9720b93Spatrick 	mtx_leave(&dom->sd_iova_mtx);
1369b9720b93Spatrick 	KASSERT(error == 0);
1370b9720b93Spatrick 
13710aa222ffSpatrick 	free(sms, M_DEVBUF, sizeof(*sms));
1372d87886f2Spatrick 	sc->sc_dmat->_dmamap_destroy(sc->sc_dmat, map);
13731a30e4e7Spatrick }
13741a30e4e7Spatrick 
13751a30e4e7Spatrick int
smmu_dmamap_load(bus_dma_tag_t t,bus_dmamap_t map,void * buf,bus_size_t buflen,struct proc * p,int flags)13761a30e4e7Spatrick smmu_dmamap_load(bus_dma_tag_t t, bus_dmamap_t map, void *buf,
13771a30e4e7Spatrick     bus_size_t buflen, struct proc *p, int flags)
13781a30e4e7Spatrick {
13791a30e4e7Spatrick 	struct smmu_domain *dom = t->_cookie;
1380d87886f2Spatrick 	struct smmu_softc *sc = dom->sd_sc;
13811a30e4e7Spatrick 	int error;
13821a30e4e7Spatrick 
1383d87886f2Spatrick 	error = sc->sc_dmat->_dmamap_load(sc->sc_dmat, map,
13841a30e4e7Spatrick 	    buf, buflen, p, flags);
13851a30e4e7Spatrick 	if (error)
13861a30e4e7Spatrick 		return error;
13871a30e4e7Spatrick 
13881a30e4e7Spatrick 	error = smmu_load_map(dom, map);
13891a30e4e7Spatrick 	if (error)
1390d87886f2Spatrick 		sc->sc_dmat->_dmamap_unload(sc->sc_dmat, map);
13911a30e4e7Spatrick 
13921a30e4e7Spatrick 	return error;
13931a30e4e7Spatrick }
13941a30e4e7Spatrick 
13951a30e4e7Spatrick int
smmu_dmamap_load_mbuf(bus_dma_tag_t t,bus_dmamap_t map,struct mbuf * m0,int flags)13961a30e4e7Spatrick smmu_dmamap_load_mbuf(bus_dma_tag_t t, bus_dmamap_t map, struct mbuf *m0,
13971a30e4e7Spatrick     int flags)
13981a30e4e7Spatrick {
13991a30e4e7Spatrick 	struct smmu_domain *dom = t->_cookie;
1400d87886f2Spatrick 	struct smmu_softc *sc = dom->sd_sc;
14011a30e4e7Spatrick 	int error;
14021a30e4e7Spatrick 
1403d87886f2Spatrick 	error = sc->sc_dmat->_dmamap_load_mbuf(sc->sc_dmat, map,
14041a30e4e7Spatrick 	    m0, flags);
14051a30e4e7Spatrick 	if (error)
14061a30e4e7Spatrick 		return error;
14071a30e4e7Spatrick 
14081a30e4e7Spatrick 	error = smmu_load_map(dom, map);
14091a30e4e7Spatrick 	if (error)
1410d87886f2Spatrick 		sc->sc_dmat->_dmamap_unload(sc->sc_dmat, map);
14111a30e4e7Spatrick 
14121a30e4e7Spatrick 	return error;
14131a30e4e7Spatrick }
14141a30e4e7Spatrick 
14151a30e4e7Spatrick int
smmu_dmamap_load_uio(bus_dma_tag_t t,bus_dmamap_t map,struct uio * uio,int flags)14161a30e4e7Spatrick smmu_dmamap_load_uio(bus_dma_tag_t t, bus_dmamap_t map, struct uio *uio,
14171a30e4e7Spatrick     int flags)
14181a30e4e7Spatrick {
14191a30e4e7Spatrick 	struct smmu_domain *dom = t->_cookie;
1420d87886f2Spatrick 	struct smmu_softc *sc = dom->sd_sc;
14211a30e4e7Spatrick 	int error;
14221a30e4e7Spatrick 
1423d87886f2Spatrick 	error = sc->sc_dmat->_dmamap_load_uio(sc->sc_dmat, map,
14241a30e4e7Spatrick 	    uio, flags);
14251a30e4e7Spatrick 	if (error)
14261a30e4e7Spatrick 		return error;
14271a30e4e7Spatrick 
14281a30e4e7Spatrick 	error = smmu_load_map(dom, map);
14291a30e4e7Spatrick 	if (error)
1430d87886f2Spatrick 		sc->sc_dmat->_dmamap_unload(sc->sc_dmat, map);
14311a30e4e7Spatrick 
14321a30e4e7Spatrick 	return error;
14331a30e4e7Spatrick }
14341a30e4e7Spatrick 
14351a30e4e7Spatrick int
smmu_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)14361a30e4e7Spatrick smmu_dmamap_load_raw(bus_dma_tag_t t, bus_dmamap_t map, bus_dma_segment_t *segs,
14371a30e4e7Spatrick     int nsegs, bus_size_t size, int flags)
14381a30e4e7Spatrick {
14391a30e4e7Spatrick 	struct smmu_domain *dom = t->_cookie;
1440d87886f2Spatrick 	struct smmu_softc *sc = dom->sd_sc;
14411a30e4e7Spatrick 	int error;
14421a30e4e7Spatrick 
1443d87886f2Spatrick 	error = sc->sc_dmat->_dmamap_load_raw(sc->sc_dmat, map,
14441a30e4e7Spatrick 	    segs, nsegs, size, flags);
14451a30e4e7Spatrick 	if (error)
14461a30e4e7Spatrick 		return error;
14471a30e4e7Spatrick 
14481a30e4e7Spatrick 	error = smmu_load_map(dom, map);
14491a30e4e7Spatrick 	if (error)
1450d87886f2Spatrick 		sc->sc_dmat->_dmamap_unload(sc->sc_dmat, map);
14511a30e4e7Spatrick 
14521a30e4e7Spatrick 	return error;
14531a30e4e7Spatrick }
14541a30e4e7Spatrick 
14551a30e4e7Spatrick void
smmu_dmamap_unload(bus_dma_tag_t t,bus_dmamap_t map)14561a30e4e7Spatrick smmu_dmamap_unload(bus_dma_tag_t t, bus_dmamap_t map)
14571a30e4e7Spatrick {
14581a30e4e7Spatrick 	struct smmu_domain *dom = t->_cookie;
1459d87886f2Spatrick 	struct smmu_softc *sc = dom->sd_sc;
14601a30e4e7Spatrick 
14611a30e4e7Spatrick 	smmu_unload_map(dom, map);
1462d87886f2Spatrick 	sc->sc_dmat->_dmamap_unload(sc->sc_dmat, map);
14631a30e4e7Spatrick }
1464