1139825Simp/*-
286230Stmm * Copyright (c) 1999, 2000 Matthew R. Green
386230Stmm * All rights reserved.
486230Stmm *
586230Stmm * Redistribution and use in source and binary forms, with or without
686230Stmm * modification, are permitted provided that the following conditions
786230Stmm * are met:
886230Stmm * 1. Redistributions of source code must retain the above copyright
986230Stmm *    notice, this list of conditions and the following disclaimer.
1086230Stmm * 2. Redistributions in binary form must reproduce the above copyright
1186230Stmm *    notice, this list of conditions and the following disclaimer in the
1286230Stmm *    documentation and/or other materials provided with the distribution.
1386230Stmm *
1486230Stmm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1586230Stmm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1686230Stmm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1786230Stmm * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1886230Stmm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
1986230Stmm * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2086230Stmm * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2186230Stmm * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2286230Stmm * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2386230Stmm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2486230Stmm * SUCH DAMAGE.
25219567Smarius *
26219567Smarius *	from: NetBSD: iommu.c,v 1.82 2008/05/30 02:29:37 mrg Exp
2786230Stmm */
2886230Stmm/*-
29219567Smarius * Copyright (c) 1999-2002 Eduardo Horvath
30219567Smarius * Copyright (c) 2001-2003 Thomas Moestl
31220931Smarius * Copyright (c) 2007, 2009 Marius Strobl <marius@FreeBSD.org>
3286230Stmm * All rights reserved.
3386230Stmm *
3486230Stmm * Redistribution and use in source and binary forms, with or without
3586230Stmm * modification, are permitted provided that the following conditions
3686230Stmm * are met:
3786230Stmm * 1. Redistributions of source code must retain the above copyright
3886230Stmm *    notice, this list of conditions and the following disclaimer.
3986230Stmm * 2. Redistributions in binary form must reproduce the above copyright
4086230Stmm *    notice, this list of conditions and the following disclaimer in the
4186230Stmm *    documentation and/or other materials provided with the distribution.
42219567Smarius * 3. The name of the author may not be used to endorse or promote products
43219567Smarius *    derived from this software without specific prior written permission.
4486230Stmm *
45219567Smarius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
46219567Smarius * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
47219567Smarius * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
48219567Smarius * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
49219567Smarius * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
50219567Smarius * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
51219567Smarius * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
52219567Smarius * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
53219567Smarius * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
5486230Stmm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
5586230Stmm * SUCH DAMAGE.
5686230Stmm *
57219567Smarius *	from: NetBSD: sbus.c,v 1.50 2002/06/20 18:26:24 eeh Exp
5886230Stmm */
5986230Stmm
60167308Smarius#include <sys/cdefs.h>
61167308Smarius__FBSDID("$FreeBSD$");
62167308Smarius
6386230Stmm/*
64167308Smarius * UltraSPARC IOMMU support; used by both the PCI and SBus code.
65117003Stmm *
66117003Stmm * TODO:
67117003Stmm * - Support sub-page boundaries.
68117003Stmm * - Fix alignment handling for small allocations (the possible page offset
69176995Smarius *   of malloc()ed memory is not handled at all).  Revise interaction of
70117003Stmm *   alignment with the load_mbuf and load_uio functions.
71117003Stmm * - Handle lowaddr and highaddr in some way, and try to work out a way
72176995Smarius *   for filter callbacks to work.  Currently, only lowaddr is honored
73117003Stmm *   in that no addresses above it are considered at all.
74117003Stmm * - Implement BUS_DMA_ALLOCNOW in bus_dma_tag_create as far as possible.
75117003Stmm * - Check the possible return values and callback error arguments;
76117003Stmm *   the callback currently gets called in error conditions where it should
77117003Stmm *   not be.
78117003Stmm * - When running out of DVMA space, return EINPROGRESS in the non-
79117003Stmm *   BUS_DMA_NOWAIT case and delay the callback until sufficient space
80117003Stmm *   becomes available.
8186230Stmm */
82131376Smarius
8386230Stmm#include "opt_iommu.h"
8486230Stmm
8586230Stmm#include <sys/param.h>
8686230Stmm#include <sys/kernel.h>
87117390Stmm#include <sys/lock.h>
88108830Stmm#include <sys/malloc.h>
89108830Stmm#include <sys/mbuf.h>
90117390Stmm#include <sys/mutex.h>
91204152Smarius#include <sys/pcpu.h>
92108830Stmm#include <sys/proc.h>
9386230Stmm#include <sys/systm.h>
94108830Stmm#include <sys/uio.h>
9586230Stmm
9686230Stmm#include <vm/vm.h>
9786230Stmm#include <vm/pmap.h>
98108830Stmm#include <vm/vm_map.h>
9986230Stmm
100200923Smarius#include <machine/asi.h>
10186230Stmm#include <machine/bus.h>
10293070Stmm#include <machine/bus_private.h>
10386230Stmm#include <machine/iommureg.h>
10486230Stmm#include <machine/pmap.h>
10586230Stmm#include <machine/resource.h>
106200923Smarius#include <machine/ver.h>
10786230Stmm
10886230Stmm#include <sys/rman.h>
10986230Stmm
11086230Stmm#include <machine/iommuvar.h>
11186230Stmm
112108830Stmm/*
113185008Smarius * Tuning constants
114108830Stmm */
115114484Stmm#define	IOMMU_MAX_PRE		(32 * 1024)
116108830Stmm#define	IOMMU_MAX_PRE_SEG	3
117108830Stmm
118185008Smarius/* Threshold for using the streaming buffer */
119117390Stmm#define	IOMMU_STREAM_THRESH	128
120117390Stmm
121249132Smavstatic MALLOC_DEFINE(M_IOMMU, "dvmamem", "IOMMU DVMA Buffers");
122100188Stmm
123100188Stmmstatic	int iommu_strbuf_flush_sync(struct iommu_state *);
124100188Stmm#ifdef IOMMU_DIAG
125176995Smariusstatic	void iommu_diag(struct iommu_state *, vm_offset_t va);
12686230Stmm#endif
12786230Stmm
128100188Stmm/*
129171730Smarius * Helpers
130117390Stmm */
131176995Smarius#define	IOMMU_READ8(is, reg, off)					\
132176995Smarius	bus_space_read_8((is)->is_bustag, (is)->is_bushandle,		\
13390616Stmm	    (is)->reg + (off))
13490616Stmm#define	IOMMU_WRITE8(is, reg, off, v)					\
135176995Smarius	bus_space_write_8((is)->is_bustag, (is)->is_bushandle,		\
13690616Stmm	    (is)->reg + (off), (v))
13790616Stmm
138117390Stmm#define	IOMMU_HAS_SB(is)						\
139117390Stmm	((is)->is_sb[0] != 0 || (is)->is_sb[1] != 0)
140117390Stmm
14193070Stmm/*
14293070Stmm * Always overallocate one page; this is needed to handle alignment of the
14393070Stmm * buffer, so it makes sense using a lazy allocation scheme.
14493070Stmm */
14593070Stmm#define	IOMMU_SIZE_ROUNDUP(sz)						\
14693070Stmm	(round_io_page(sz) + IO_PAGE_SIZE)
14793070Stmm
148100188Stmm#define	IOMMU_SET_TTE(is, va, tte)					\
149171730Smarius	((is)->is_tsb[IOTSBSLOT(va)] = (tte))
150117390Stmm#define	IOMMU_GET_TTE(is, va)						\
151171730Smarius	(is)->is_tsb[IOTSBSLOT(va)]
15286230Stmm
153108830Stmm/* Resource helpers */
154108830Stmm#define	IOMMU_RES_START(res)						\
155108830Stmm	((bus_addr_t)rman_get_start(res) << IO_PAGE_SHIFT)
156108830Stmm#define	IOMMU_RES_END(res)						\
157108830Stmm	((bus_addr_t)(rman_get_end(res) + 1) << IO_PAGE_SHIFT)
158108830Stmm#define	IOMMU_RES_SIZE(res)						\
159108830Stmm	((bus_size_t)rman_get_size(res) << IO_PAGE_SHIFT)
160108830Stmm
161108830Stmm/* Helpers for struct bus_dmamap_res */
162108830Stmm#define	BDR_START(r)	IOMMU_RES_START((r)->dr_res)
163108830Stmm#define	BDR_END(r)	IOMMU_RES_END((r)->dr_res)
164108830Stmm#define	BDR_SIZE(r)	IOMMU_RES_SIZE((r)->dr_res)
165108830Stmm
166171730Smarius/* Locking macros */
167171730Smarius#define	IS_LOCK(is)	mtx_lock(&is->is_mtx)
168171730Smarius#define	IS_LOCK_ASSERT(is)	mtx_assert(&is->is_mtx, MA_OWNED)
169171730Smarius#define	IS_UNLOCK(is)	mtx_unlock(&is->is_mtx)
170117390Stmm
171176995Smarius/* Flush a page from the TLB.  No locking required, since this is atomic. */
172100188Stmmstatic __inline void
173100188Stmmiommu_tlb_flush(struct iommu_state *is, bus_addr_t va)
174100188Stmm{
175100188Stmm
176200923Smarius	if ((is->is_flags & IOMMU_FIRE) != 0)
177200923Smarius		/*
178200923Smarius		 * Direct page flushing is not supported and also not
179200923Smarius		 * necessary due to cache snooping.
180200923Smarius		 */
181200923Smarius		return;
182171730Smarius	IOMMU_WRITE8(is, is_iommu, IMR_FLUSH, va);
183100188Stmm}
184100188Stmm
185117390Stmm/*
186176995Smarius * Flush a page from the streaming buffer.  No locking required, since this
187176995Smarius * is atomic.
188117390Stmm */
189100188Stmmstatic __inline void
190100188Stmmiommu_strbuf_flushpg(struct iommu_state *is, bus_addr_t va)
191100188Stmm{
192100188Stmm	int i;
193100188Stmm
194171730Smarius	for (i = 0; i < 2; i++)
195100188Stmm		if (is->is_sb[i] != 0)
196100188Stmm			IOMMU_WRITE8(is, is_sb[i], ISR_PGFLUSH, va);
197100188Stmm}
198100188Stmm
199117390Stmm/*
200117390Stmm * Flush an address from the streaming buffer(s); this is an asynchronous
201176995Smarius * operation.  To make sure that it has completed, iommu_strbuf_sync() needs
202176995Smarius * to be called.  No locking required.
203117390Stmm */
204100188Stmmstatic __inline void
205117390Stmmiommu_strbuf_flush(struct iommu_state *is, bus_addr_t va)
206100188Stmm{
207100188Stmm
208171730Smarius	iommu_strbuf_flushpg(is, va);
209100188Stmm}
210100188Stmm
211117390Stmm/* Synchronize all outstanding flush operations. */
21293070Stmmstatic __inline void
213117390Stmmiommu_strbuf_sync(struct iommu_state *is)
21493070Stmm{
21593070Stmm
216117390Stmm	IS_LOCK_ASSERT(is);
217171730Smarius	iommu_strbuf_flush_sync(is);
218117390Stmm}
219117390Stmm
220117390Stmm/* LRU queue handling for lazy resource allocation. */
221117390Stmmstatic __inline void
222117390Stmmiommu_map_insq(struct iommu_state *is, bus_dmamap_t map)
223117390Stmm{
224117390Stmm
225117390Stmm	IS_LOCK_ASSERT(is);
226108830Stmm	if (!SLIST_EMPTY(&map->dm_reslist)) {
227108830Stmm		if (map->dm_onq)
228171730Smarius			TAILQ_REMOVE(&is->is_maplruq, map, dm_maplruq);
229171730Smarius		TAILQ_INSERT_TAIL(&is->is_maplruq, map, dm_maplruq);
230108830Stmm		map->dm_onq = 1;
23193070Stmm	}
23293070Stmm}
23393070Stmm
23493070Stmmstatic __inline void
235117390Stmmiommu_map_remq(struct iommu_state *is, bus_dmamap_t map)
23693070Stmm{
23793070Stmm
238117390Stmm	IS_LOCK_ASSERT(is);
239108830Stmm	if (map->dm_onq)
240171730Smarius		TAILQ_REMOVE(&is->is_maplruq, map, dm_maplruq);
241108830Stmm	map->dm_onq = 0;
24293070Stmm}
24393070Stmm
24493070Stmm/*
245167308Smarius * initialise the UltraSPARC IOMMU (PCI or SBus):
24686230Stmm *	- allocate and setup the iotsb.
24786230Stmm *	- enable the IOMMU
24886230Stmm *	- initialise the streaming buffers (if they exist)
24986230Stmm *	- create a private DVMA map.
25086230Stmm */
25186230Stmmvoid
252200923Smariusiommu_init(const char *name, struct iommu_state *is, u_int tsbsize,
253200923Smarius    uint32_t iovabase, u_int resvpg)
25486230Stmm{
25586230Stmm	vm_size_t size;
25690616Stmm	vm_offset_t offs;
257200923Smarius	uint64_t end, obpmap, obpptsb, tte;
258200923Smarius	u_int maxtsbsize, obptsbentries, obptsbsize, slot, tsbentries;
25990616Stmm	int i;
26086230Stmm
26186230Stmm	/*
262200923Smarius	 * Setup the IOMMU.
26386230Stmm	 *
264200923Smarius	 * The sun4u IOMMU is part of the PCI or SBus controller so we
26586230Stmm	 * will deal with it here..
26686230Stmm	 *
26786230Stmm	 * The IOMMU address space always ends at 0xffffe000, but the starting
26886230Stmm	 * address depends on the size of the map.  The map size is 1024 * 2 ^
26986230Stmm	 * is->is_tsbsize entries, where each entry is 8 bytes.  The start of
27086230Stmm	 * the map can be calculated by (0xffffe000 << (8 + is->is_tsbsize)).
27186230Stmm	 */
272200923Smarius	if ((is->is_flags & IOMMU_FIRE) != 0) {
273200923Smarius		maxtsbsize = IOMMU_TSB512K;
274200923Smarius		/*
275200923Smarius		 * We enable bypass in order to be able to use a physical
276200923Smarius		 * address for the event queue base.
277200923Smarius		 */
278200923Smarius		is->is_cr = IOMMUCR_SE | IOMMUCR_CM_C_TLB_TBW | IOMMUCR_BE;
279200923Smarius	} else {
280200923Smarius		maxtsbsize = IOMMU_TSB128K;
281200923Smarius		is->is_cr = (tsbsize << IOMMUCR_TSBSZ_SHIFT) | IOMMUCR_DE;
282200923Smarius	}
283200923Smarius	if (tsbsize > maxtsbsize)
284200923Smarius		panic("%s: unsupported TSB size	", __func__);
285200923Smarius	tsbentries = IOMMU_TSBENTRIES(tsbsize);
286200923Smarius	is->is_cr |= IOMMUCR_EN;
28786230Stmm	is->is_tsbsize = tsbsize;
28886230Stmm	is->is_dvmabase = iovabase;
28986230Stmm	if (iovabase == -1)
29086230Stmm		is->is_dvmabase = IOTSB_VSTART(is->is_tsbsize);
29186230Stmm
292108802Stmm	size = IOTSB_BASESZ << is->is_tsbsize;
293200923Smarius	printf("%s: DVMA map: %#lx to %#lx %d entries%s\n", name,
294100188Stmm	    is->is_dvmabase, is->is_dvmabase +
295200923Smarius	    (size << (IO_PAGE_SHIFT - IOTTE_SHIFT)) - 1, tsbentries,
296185008Smarius	    IOMMU_HAS_SB(is) ? ", streaming buffer" : "");
29786230Stmm
298171730Smarius	/*
299171730Smarius	 * Set up resource mamangement.
300171730Smarius	 */
301171730Smarius	mtx_init(&is->is_mtx, "iommu", NULL, MTX_DEF);
302171730Smarius	end = is->is_dvmabase + (size << (IO_PAGE_SHIFT - IOTTE_SHIFT));
303171730Smarius	is->is_dvma_rman.rm_type = RMAN_ARRAY;
304171730Smarius	is->is_dvma_rman.rm_descr = "DVMA Memory";
305171730Smarius	if (rman_init(&is->is_dvma_rman) != 0 ||
306171730Smarius	    rman_manage_region(&is->is_dvma_rman,
307171730Smarius	    (is->is_dvmabase >> IO_PAGE_SHIFT) + resvpg,
308171730Smarius	    (end >> IO_PAGE_SHIFT) - 1) != 0)
309171730Smarius		panic("%s: could not initialize DVMA rman", __func__);
310171730Smarius	TAILQ_INIT(&is->is_maplruq);
31186230Stmm
31286230Stmm	/*
313171730Smarius	 * Allocate memory for I/O page tables.  They need to be
314171730Smarius	 * physically contiguous.
315171730Smarius	 */
316171730Smarius	is->is_tsb = contigmalloc(size, M_DEVBUF, M_NOWAIT, 0, ~0UL,
317171730Smarius	    PAGE_SIZE, 0);
318200923Smarius	if (is->is_tsb == NULL)
319171730Smarius		panic("%s: contigmalloc failed", __func__);
320171730Smarius	is->is_ptsb = pmap_kextract((vm_offset_t)is->is_tsb);
321171730Smarius	bzero(is->is_tsb, size);
322171730Smarius
323171730Smarius	/*
324200923Smarius	 * Add the PROM mappings to the kernel IOTSB if desired.
325200923Smarius	 * Note that the firmware of certain Darwin boards doesn't set
326200923Smarius	 * the TSB size correctly.
327200923Smarius	 */
328200923Smarius	if ((is->is_flags & IOMMU_FIRE) != 0)
329200923Smarius		obptsbsize = (IOMMU_READ8(is, is_iommu, IMR_TSB) &
330200923Smarius		    IOMMUTB_TSBSZ_MASK) >> IOMMUTB_TSBSZ_SHIFT;
331200923Smarius	else
332200923Smarius		obptsbsize = (IOMMU_READ8(is, is_iommu, IMR_CTL) &
333200923Smarius		    IOMMUCR_TSBSZ_MASK) >> IOMMUCR_TSBSZ_SHIFT;
334200923Smarius	obptsbentries = IOMMU_TSBENTRIES(obptsbsize);
335200923Smarius	if (bootverbose)
336200923Smarius		printf("%s: PROM IOTSB size: %d (%d entries)\n", name,
337200923Smarius		    obptsbsize, obptsbentries);
338200923Smarius	if ((is->is_flags & IOMMU_PRESERVE_PROM) != 0 &&
339204152Smarius	    !(PCPU_GET(impl) == CPU_IMPL_ULTRASPARCIIi && obptsbsize == 7)) {
340200923Smarius		if (obptsbentries > tsbentries)
341200923Smarius			panic("%s: PROM IOTSB entries exceed kernel",
342200923Smarius			    __func__);
343200923Smarius		obpptsb = IOMMU_READ8(is, is_iommu, IMR_TSB) &
344200923Smarius		    IOMMUTB_TB_MASK;
345200923Smarius		for (i = 0; i < obptsbentries; i++) {
346200923Smarius			tte = ldxa(obpptsb + i * 8, ASI_PHYS_USE_EC);
347200923Smarius			if ((tte & IOTTE_V) == 0)
348200923Smarius				continue;
349200923Smarius			slot = tsbentries - obptsbentries + i;
350200923Smarius			if (bootverbose)
351200923Smarius				printf("%s: adding PROM IOTSB slot %d "
352200923Smarius				    "(kernel slot %d) TTE: %#lx\n", name,
353200923Smarius				    i, slot, tte);
354200923Smarius			obpmap = (is->is_dvmabase + slot * IO_PAGE_SIZE) >>
355200923Smarius			    IO_PAGE_SHIFT;
356200923Smarius			if (rman_reserve_resource(&is->is_dvma_rman, obpmap,
357200923Smarius			    obpmap, IO_PAGE_SIZE >> IO_PAGE_SHIFT, RF_ACTIVE,
358200923Smarius			    NULL) == NULL)
359200923Smarius				panic("%s: could not reserve PROM IOTSB slot "
360200923Smarius				    "%d (kernel slot %d)", __func__, i, slot);
361200923Smarius			is->is_tsb[slot] = tte;
362200923Smarius		}
363200923Smarius	}
364200923Smarius
365200923Smarius	/*
36686230Stmm	 * Initialize streaming buffer, if it is there.
36786230Stmm	 */
368100188Stmm	if (IOMMU_HAS_SB(is)) {
36990616Stmm		/*
37090616Stmm		 * Find two 64-byte blocks in is_flush that are aligned on
37190616Stmm		 * a 64-byte boundary for flushing.
37290616Stmm		 */
37390616Stmm		offs = roundup2((vm_offset_t)is->is_flush,
37490616Stmm		    STRBUF_FLUSHSYNC_NBYTES);
37590616Stmm		for (i = 0; i < 2; i++, offs += STRBUF_FLUSHSYNC_NBYTES) {
376200923Smarius			is->is_flushva[i] = (uint64_t *)offs;
37790616Stmm			is->is_flushpa[i] = pmap_kextract(offs);
37890616Stmm		}
37990616Stmm	}
38086230Stmm
38186230Stmm	/*
382117390Stmm	 * Now actually start up the IOMMU.
38386230Stmm	 */
38486230Stmm	iommu_reset(is);
38586230Stmm}
38686230Stmm
38786230Stmm/*
38886230Stmm * Streaming buffers don't exist on the UltraSPARC IIi; we should have
38986230Stmm * detected that already and disabled them.  If not, we will notice that
39086230Stmm * they aren't there when the STRBUF_EN bit does not remain.
39186230Stmm */
39286230Stmmvoid
39386230Stmmiommu_reset(struct iommu_state *is)
39486230Stmm{
395200923Smarius	uint64_t tsb;
39690616Stmm	int i;
39786230Stmm
398200923Smarius	tsb = is->is_ptsb;
399200923Smarius	if ((is->is_flags & IOMMU_FIRE) != 0) {
400200923Smarius		tsb |= is->is_tsbsize;
401200923Smarius		IOMMU_WRITE8(is, is_iommu, IMR_CACHE_INVAL, ~0ULL);
402200923Smarius	}
403200923Smarius	IOMMU_WRITE8(is, is_iommu, IMR_TSB, tsb);
404200923Smarius	IOMMU_WRITE8(is, is_iommu, IMR_CTL, is->is_cr);
40586230Stmm
40690616Stmm	for (i = 0; i < 2; i++) {
40790616Stmm		if (is->is_sb[i] != 0) {
408185008Smarius			IOMMU_WRITE8(is, is_sb[i], ISR_CTL, STRBUF_EN |
409185008Smarius			    ((is->is_flags & IOMMU_RERUN_DISABLE) != 0 ?
410185008Smarius			    STRBUF_RR_DIS : 0));
41186230Stmm
412185008Smarius			/* No streaming buffers?  Disable them. */
413185008Smarius			if ((IOMMU_READ8(is, is_sb[i], ISR_CTL) &
414185008Smarius			    STRBUF_EN) == 0)
41590616Stmm				is->is_sb[i] = 0;
41690616Stmm		}
41790616Stmm	}
418200923Smarius
419200923Smarius	(void)IOMMU_READ8(is, is_iommu, IMR_CTL);
42086230Stmm}
42186230Stmm
42286230Stmm/*
423176995Smarius * Enter a mapping into the TSB.  No locking required, since each TSB slot is
424117390Stmm * uniquely assigned to a single map.
42586230Stmm */
426117390Stmmstatic void
427117390Stmmiommu_enter(struct iommu_state *is, vm_offset_t va, vm_paddr_t pa,
428117390Stmm    int stream, int flags)
42986230Stmm{
430200923Smarius	uint64_t tte;
43186230Stmm
432108802Stmm	KASSERT(va >= is->is_dvmabase,
433176995Smarius	    ("%s: va %#lx not in DVMA space", __func__, va));
434171730Smarius	KASSERT(pa <= is->is_pmaxaddr,
435176995Smarius	    ("%s: XXX: physical address too large (%#lx)", __func__, pa));
43686230Stmm
43786230Stmm	tte = MAKEIOTTE(pa, !(flags & BUS_DMA_NOWRITE),
438117390Stmm	    !(flags & BUS_DMA_NOCACHE), stream);
43986230Stmm
440100188Stmm	IOMMU_SET_TTE(is, va, tte);
441100188Stmm	iommu_tlb_flush(is, va);
44290616Stmm#ifdef IOMMU_DIAG
443117390Stmm	IS_LOCK(is);
44486230Stmm	iommu_diag(is, va);
445117390Stmm	IS_UNLOCK(is);
44686230Stmm#endif
44786230Stmm}
44886230Stmm
44986230Stmm/*
450176995Smarius * Remove mappings created by iommu_enter().  Flush the streaming buffer,
451176995Smarius * but do not synchronize it.  Returns whether a streaming buffer flush
452176995Smarius * was performed.
45386230Stmm */
454117390Stmmstatic int
45586230Stmmiommu_remove(struct iommu_state *is, vm_offset_t va, vm_size_t len)
45686230Stmm{
457200923Smarius	int slot, streamed = 0;
45886230Stmm
45986230Stmm#ifdef IOMMU_DIAG
46086230Stmm	iommu_diag(is, va);
46186230Stmm#endif
46286230Stmm
463100188Stmm	KASSERT(va >= is->is_dvmabase,
464176995Smarius	    ("%s: va 0x%lx not in DVMA space", __func__, (u_long)va));
465100188Stmm	KASSERT(va + len >= va,
466176995Smarius	    ("%s: va 0x%lx + len 0x%lx wraps", __func__, (long)va, (long)len));
467100188Stmm
46886230Stmm	va = trunc_io_page(va);
46986230Stmm	while (len > 0) {
470117390Stmm		if ((IOMMU_GET_TTE(is, va) & IOTTE_STREAM) != 0) {
471117390Stmm			streamed = 1;
472117390Stmm			iommu_strbuf_flush(is, va);
473117390Stmm		}
474100188Stmm		len -= ulmin(len, IO_PAGE_SIZE);
475100188Stmm		IOMMU_SET_TTE(is, va, 0);
476100188Stmm		iommu_tlb_flush(is, va);
477200923Smarius		if ((is->is_flags & IOMMU_FLUSH_CACHE) != 0) {
478200923Smarius			slot = IOTSBSLOT(va);
479200923Smarius			if (len <= IO_PAGE_SIZE || slot % 8 == 7)
480200923Smarius				IOMMU_WRITE8(is, is_iommu, IMR_CACHE_FLUSH,
481200923Smarius				    is->is_ptsb + slot * 8);
482200923Smarius		}
48386230Stmm		va += IO_PAGE_SIZE;
48486230Stmm	}
485117390Stmm	return (streamed);
48686230Stmm}
48786230Stmm
488117390Stmm/* Decode an IOMMU fault for host bridge error handlers. */
48993053Stmmvoid
49093053Stmmiommu_decode_fault(struct iommu_state *is, vm_offset_t phys)
49193053Stmm{
49293053Stmm	bus_addr_t va;
49393053Stmm	long idx;
49493053Stmm
495171730Smarius	idx = phys - is->is_ptsb;
496171730Smarius	if (phys < is->is_ptsb ||
49793053Stmm	    idx > (PAGE_SIZE << is->is_tsbsize))
49893053Stmm		return;
49993053Stmm	va = is->is_dvmabase +
50093053Stmm	    (((bus_addr_t)idx >> IOTTE_SHIFT) << IO_PAGE_SHIFT);
50193053Stmm	printf("IOMMU fault virtual address %#lx\n", (u_long)va);
50293053Stmm}
50393053Stmm
504117390Stmm/*
505117390Stmm * A barrier operation which makes sure that all previous streaming buffer
506117390Stmm * flushes complete before it returns.
507117390Stmm */
50886230Stmmstatic int
509100188Stmmiommu_strbuf_flush_sync(struct iommu_state *is)
51086230Stmm{
51186230Stmm	struct timeval cur, end;
51290616Stmm	int i;
51386230Stmm
514117390Stmm	IS_LOCK_ASSERT(is);
515100188Stmm	if (!IOMMU_HAS_SB(is))
51686230Stmm		return (0);
51786230Stmm
51886230Stmm	/*
51986230Stmm	 * Streaming buffer flushes:
52086230Stmm	 *
52186230Stmm	 *   1 Tell strbuf to flush by storing va to strbuf_pgflush.  If
52286230Stmm	 *     we're not on a cache line boundary (64-bits):
52386230Stmm	 *   2 Store 0 in flag
52486230Stmm	 *   3 Store pointer to flag in flushsync
52586230Stmm	 *   4 wait till flushsync becomes 0x1
52686230Stmm	 *
527176995Smarius	 * If it takes more than .5 sec, something went wrong.
52886230Stmm	 */
52990616Stmm	*is->is_flushva[0] = 1;
53090616Stmm	*is->is_flushva[1] = 1;
53186230Stmm	membar(StoreStore);
53290616Stmm	for (i = 0; i < 2; i++) {
53390616Stmm		if (is->is_sb[i] != 0) {
53490616Stmm			*is->is_flushva[i] = 0;
53590616Stmm			IOMMU_WRITE8(is, is_sb[i], ISR_FLUSHSYNC,
53690616Stmm			    is->is_flushpa[i]);
53790616Stmm		}
53890616Stmm	}
53986230Stmm
540105545Stmm	microuptime(&cur);
54186230Stmm	end.tv_sec = 0;
542117390Stmm	/*
543176995Smarius	 * 0.5s is the recommended timeout from the U2S manual.  The actual
544117390Stmm	 * time required should be smaller by at least a factor of 1000.
545117390Stmm	 * We have no choice but to busy-wait.
546117390Stmm	 */
54786230Stmm	end.tv_usec = 500000;
54886230Stmm	timevaladd(&end, &cur);
54986230Stmm
55090616Stmm	while ((!*is->is_flushva[0] || !*is->is_flushva[1]) &&
55190616Stmm	    timevalcmp(&cur, &end, <=))
552105545Stmm		microuptime(&cur);
55386230Stmm
55490616Stmm	if (!*is->is_flushva[0] || !*is->is_flushva[1]) {
555167308Smarius		panic("%s: flush timeout %ld, %ld at %#lx", __func__,
55690616Stmm		    *is->is_flushva[0], *is->is_flushva[1], is->is_flushpa[0]);
55786230Stmm	}
558108802Stmm
559117390Stmm	return (1);
56086230Stmm}
56186230Stmm
562117390Stmm/* Determine whether we may enable streaming on a mapping. */
563117390Stmmstatic __inline int
564117390Stmmiommu_use_streaming(struct iommu_state *is, bus_dmamap_t map, bus_size_t size)
565117390Stmm{
566117390Stmm
567117390Stmm	return (size >= IOMMU_STREAM_THRESH && IOMMU_HAS_SB(is) &&
568117390Stmm	    (map->dm_flags & DMF_COHERENT) == 0);
569117390Stmm}
570117390Stmm
571117390Stmm/*
572176995Smarius * Allocate DVMA virtual memory for a map.  The map may not be on a queue,
573176995Smarius * so that it can be freely modified.
574117390Stmm */
57590616Stmmstatic int
57693070Stmmiommu_dvma_valloc(bus_dma_tag_t t, struct iommu_state *is, bus_dmamap_t map,
57793070Stmm    bus_size_t size)
57890616Stmm{
579108830Stmm	struct resource *res;
580108830Stmm	struct bus_dmamap_res *bdr;
581109651Stmm	bus_size_t align, sgsize;
58290616Stmm
583176995Smarius	KASSERT(!map->dm_onq, ("%s: map on queue!", __func__));
584108830Stmm	if ((bdr = malloc(sizeof(*bdr), M_IOMMU, M_NOWAIT)) == NULL)
585108830Stmm		return (EAGAIN);
58690616Stmm	/*
58793070Stmm	 * If a boundary is specified, a map cannot be larger than it; however
58893070Stmm	 * we do not clip currently, as that does not play well with the lazy
58993070Stmm	 * allocation code.
59093070Stmm	 * Alignment to a page boundary is always enforced.
59190616Stmm	 */
592108815Stmm	align = (t->dt_alignment + IO_PAGE_MASK) >> IO_PAGE_SHIFT;
59393070Stmm	sgsize = round_io_page(size) >> IO_PAGE_SHIFT;
594108815Stmm	if (t->dt_boundary > 0 && t->dt_boundary < IO_PAGE_SIZE)
595167308Smarius		panic("%s: illegal boundary specified", __func__);
596171730Smarius	res = rman_reserve_resource_bound(&is->is_dvma_rman, 0L,
597109651Stmm	    t->dt_lowaddr >> IO_PAGE_SHIFT, sgsize,
598109651Stmm	    t->dt_boundary >> IO_PAGE_SHIFT,
59990616Stmm	    RF_ACTIVE | rman_make_alignment_flags(align), NULL);
600109651Stmm	if (res == NULL) {
601109651Stmm		free(bdr, M_IOMMU);
60290616Stmm		return (ENOMEM);
603109651Stmm	}
60490616Stmm
605108830Stmm	bdr->dr_res = res;
606108830Stmm	bdr->dr_used = 0;
607108830Stmm	SLIST_INSERT_HEAD(&map->dm_reslist, bdr, dr_link);
60890616Stmm	return (0);
60990616Stmm}
61090616Stmm
611108830Stmm/* Unload the map and mark all resources as unused, but do not free them. */
61290616Stmmstatic void
613108830Stmmiommu_dvmamap_vunload(struct iommu_state *is, bus_dmamap_t map)
61490616Stmm{
615108830Stmm	struct bus_dmamap_res *r;
616117390Stmm	int streamed = 0;
61790616Stmm
618185008Smarius	IS_LOCK_ASSERT(is);	/* for iommu_strbuf_sync() below */
619108830Stmm	SLIST_FOREACH(r, &map->dm_reslist, dr_link) {
620117390Stmm		streamed |= iommu_remove(is, BDR_START(r), r->dr_used);
621108830Stmm		r->dr_used = 0;
622108830Stmm	}
623117390Stmm	if (streamed)
624117390Stmm		iommu_strbuf_sync(is);
625108830Stmm}
626108830Stmm
627108830Stmm/* Free a DVMA virtual memory resource. */
628108830Stmmstatic __inline void
629108830Stmmiommu_dvma_vfree_res(bus_dmamap_t map, struct bus_dmamap_res *r)
630108830Stmm{
631108830Stmm
632176995Smarius	KASSERT(r->dr_used == 0, ("%s: resource busy!", __func__));
633108830Stmm	if (r->dr_res != NULL && rman_release_resource(r->dr_res) != 0)
63490616Stmm		printf("warning: DVMA space lost\n");
635108830Stmm	SLIST_REMOVE(&map->dm_reslist, r, bus_dmamap_res, dr_link);
636108830Stmm	free(r, M_IOMMU);
63790616Stmm}
63890616Stmm
639108830Stmm/* Free all DVMA virtual memory for a map. */
640108830Stmmstatic void
641108830Stmmiommu_dvma_vfree(struct iommu_state *is, bus_dmamap_t map)
642108830Stmm{
643108830Stmm
644117390Stmm	IS_LOCK(is);
645117390Stmm	iommu_map_remq(is, map);
646108830Stmm	iommu_dvmamap_vunload(is, map);
647117390Stmm	IS_UNLOCK(is);
648108830Stmm	while (!SLIST_EMPTY(&map->dm_reslist))
649108830Stmm		iommu_dvma_vfree_res(map, SLIST_FIRST(&map->dm_reslist));
650108830Stmm}
651108830Stmm
652108830Stmm/* Prune a map, freeing all unused DVMA resources. */
653108830Stmmstatic bus_size_t
654117390Stmmiommu_dvma_vprune(struct iommu_state *is, bus_dmamap_t map)
655108830Stmm{
656108830Stmm	struct bus_dmamap_res *r, *n;
657108830Stmm	bus_size_t freed = 0;
658108830Stmm
659117390Stmm	IS_LOCK_ASSERT(is);
660108830Stmm	for (r = SLIST_FIRST(&map->dm_reslist); r != NULL; r = n) {
661108830Stmm		n = SLIST_NEXT(r, dr_link);
662108830Stmm		if (r->dr_used == 0) {
663108830Stmm			freed += BDR_SIZE(r);
664108830Stmm			iommu_dvma_vfree_res(map, r);
665108830Stmm		}
666108830Stmm	}
667117390Stmm	if (SLIST_EMPTY(&map->dm_reslist))
668117390Stmm		iommu_map_remq(is, map);
669108830Stmm	return (freed);
670108830Stmm}
671108830Stmm
672108830Stmm/*
673108830Stmm * Try to find a suitably-sized (and if requested, -aligned) slab of DVMA
674117390Stmm * memory with IO page offset voffs.
675108830Stmm */
676108830Stmmstatic bus_addr_t
677108830Stmmiommu_dvma_vfindseg(bus_dmamap_t map, vm_offset_t voffs, bus_size_t size,
678108830Stmm    bus_addr_t amask)
679108830Stmm{
680108830Stmm	struct bus_dmamap_res *r;
681108830Stmm	bus_addr_t dvmaddr, dvmend;
682108830Stmm
683176995Smarius	KASSERT(!map->dm_onq, ("%s: map on queue!", __func__));
684108830Stmm	SLIST_FOREACH(r, &map->dm_reslist, dr_link) {
685108830Stmm		dvmaddr = round_io_page(BDR_START(r) + r->dr_used);
686108830Stmm		/* Alignment can only work with voffs == 0. */
687108830Stmm		dvmaddr = (dvmaddr + amask) & ~amask;
688108830Stmm		dvmaddr += voffs;
689108830Stmm		dvmend = dvmaddr + size;
690108830Stmm		if (dvmend <= BDR_END(r)) {
691108830Stmm			r->dr_used = dvmend - BDR_START(r);
692108830Stmm			return (dvmaddr);
693108830Stmm		}
694108830Stmm	}
695108830Stmm	return (0);
696108830Stmm}
697108830Stmm
698108830Stmm/*
699108830Stmm * Try to find or allocate a slab of DVMA space; see above.
700108830Stmm */
701108830Stmmstatic int
702108830Stmmiommu_dvma_vallocseg(bus_dma_tag_t dt, struct iommu_state *is, bus_dmamap_t map,
703108830Stmm    vm_offset_t voffs, bus_size_t size, bus_addr_t amask, bus_addr_t *addr)
704108830Stmm{
705127344Stmm	bus_dmamap_t tm, last;
706108830Stmm	bus_addr_t dvmaddr, freed;
707117390Stmm	int error, complete = 0;
708108830Stmm
709108830Stmm	dvmaddr = iommu_dvma_vfindseg(map, voffs, size, amask);
710108830Stmm
711108830Stmm	/* Need to allocate. */
712108830Stmm	if (dvmaddr == 0) {
713108830Stmm		while ((error = iommu_dvma_valloc(dt, is, map,
714117390Stmm			voffs + size)) == ENOMEM && !complete) {
715108830Stmm			/*
716127344Stmm			 * Free the allocated DVMA of a few maps until
717108830Stmm			 * the required size is reached. This is an
718108830Stmm			 * approximation to not have to call the allocation
719108830Stmm			 * function too often; most likely one free run
720108830Stmm			 * will not suffice if not one map was large enough
721108830Stmm			 * itself due to fragmentation.
722108830Stmm			 */
723117390Stmm			IS_LOCK(is);
724108830Stmm			freed = 0;
725171730Smarius			last = TAILQ_LAST(&is->is_maplruq, iommu_maplruq_head);
726108830Stmm			do {
727171730Smarius				tm = TAILQ_FIRST(&is->is_maplruq);
728127344Stmm				complete = tm == last;
729127344Stmm				if (tm == NULL)
730117390Stmm					break;
731117390Stmm				freed += iommu_dvma_vprune(is, tm);
732117390Stmm				/* Move to the end. */
733117390Stmm				iommu_map_insq(is, tm);
734127344Stmm			} while (freed < size && !complete);
735117390Stmm			IS_UNLOCK(is);
736108830Stmm		}
737108830Stmm		if (error != 0)
738108830Stmm			return (error);
739108830Stmm		dvmaddr = iommu_dvma_vfindseg(map, voffs, size, amask);
740176995Smarius		KASSERT(dvmaddr != 0, ("%s: allocation failed unexpectedly!",
741176995Smarius		    __func__));
742108830Stmm	}
743108830Stmm	*addr = dvmaddr;
744108830Stmm	return (0);
745108830Stmm}
746108830Stmm
747116541Stmmstatic int
748116541Stmmiommu_dvmamem_alloc(bus_dma_tag_t dt, void **vaddr, int flags,
749116541Stmm    bus_dmamap_t *mapp)
75086230Stmm{
751116541Stmm	struct iommu_state *is = dt->dt_cookie;
752118090Stmm	int error, mflags;
75386230Stmm
75486230Stmm	/*
755171730Smarius	 * XXX: This will break for 32 bit transfers on machines with more
756171730Smarius	 * than is->is_pmaxaddr memory.
75786230Stmm	 */
758116541Stmm	if ((error = sparc64_dma_alloc_map(dt, mapp)) != 0)
75986230Stmm		return (error);
760118090Stmm
761118090Stmm	if ((flags & BUS_DMA_NOWAIT) != 0)
762118090Stmm		mflags = M_NOWAIT;
763118090Stmm	else
764118090Stmm		mflags = M_WAITOK;
765118090Stmm	if ((flags & BUS_DMA_ZERO) != 0)
766118090Stmm		mflags |= M_ZERO;
767118090Stmm
768118090Stmm	if ((*vaddr = malloc(dt->dt_maxsize, M_IOMMU, mflags)) == NULL) {
76990616Stmm		error = ENOMEM;
770116541Stmm		sparc64_dma_free_map(dt, *mapp);
771108830Stmm		return (error);
77290616Stmm	}
773117390Stmm	if ((flags & BUS_DMA_COHERENT) != 0)
774117390Stmm		(*mapp)->dm_flags |= DMF_COHERENT;
77593070Stmm	/*
776176995Smarius	 * Try to preallocate DVMA space.  If this fails, it is retried at
777176995Smarius	 * load time.
77893070Stmm	 */
779115316Sscottl	iommu_dvma_valloc(dt, is, *mapp, IOMMU_SIZE_ROUNDUP(dt->dt_maxsize));
780117390Stmm	IS_LOCK(is);
781117390Stmm	iommu_map_insq(is, *mapp);
782117390Stmm	IS_UNLOCK(is);
78386230Stmm	return (0);
78486230Stmm}
78586230Stmm
786116541Stmmstatic void
787116541Stmmiommu_dvmamem_free(bus_dma_tag_t dt, void *vaddr, bus_dmamap_t map)
78886230Stmm{
789116541Stmm	struct iommu_state *is = dt->dt_cookie;
79086230Stmm
791108830Stmm	iommu_dvma_vfree(is, map);
792116541Stmm	sparc64_dma_free_map(dt, map);
79386230Stmm	free(vaddr, M_IOMMU);
79486230Stmm}
79586230Stmm
796116541Stmmstatic int
797116541Stmmiommu_dvmamap_create(bus_dma_tag_t dt, int flags, bus_dmamap_t *mapp)
79890616Stmm{
799116541Stmm	struct iommu_state *is = dt->dt_cookie;
800108830Stmm	bus_size_t totsz, presz, currsz;
801108830Stmm	int error, i, maxpre;
80290616Stmm
803116541Stmm	if ((error = sparc64_dma_alloc_map(dt, mapp)) != 0)
80490616Stmm		return (error);
805117390Stmm	if ((flags & BUS_DMA_COHERENT) != 0)
806117390Stmm		(*mapp)->dm_flags |= DMF_COHERENT;
80790616Stmm	/*
808108830Stmm	 * Preallocate DVMA space; if this fails now, it is retried at load
809176995Smarius	 * time.  Through bus_dmamap_load_mbuf() and bus_dmamap_load_uio(),
810176995Smarius	 * it is possible to have multiple discontiguous segments in a single
811176995Smarius	 * map, which is handled by allocating additional resources, instead
812176995Smarius	 * of increasing the size, to avoid fragmentation.
813176995Smarius	 * Clamp preallocation to IOMMU_MAX_PRE.  In some situations we can
81493070Stmm	 * handle more; that case is handled by reallocating at map load time.
81590616Stmm	 */
816167308Smarius	totsz = ulmin(IOMMU_SIZE_ROUNDUP(dt->dt_maxsize), IOMMU_MAX_PRE);
817108830Stmm	error = iommu_dvma_valloc(dt, is, *mapp, totsz);
818108830Stmm	if (error != 0)
819108830Stmm		return (0);
820108830Stmm	/*
821108830Stmm	 * Try to be smart about preallocating some additional segments if
822108830Stmm	 * needed.
823108830Stmm	 */
824108830Stmm	maxpre = imin(dt->dt_nsegments, IOMMU_MAX_PRE_SEG);
825108830Stmm	presz = dt->dt_maxsize / maxpre;
826109651Stmm	for (i = 1; i < maxpre && totsz < IOMMU_MAX_PRE; i++) {
827108830Stmm		currsz = round_io_page(ulmin(presz, IOMMU_MAX_PRE - totsz));
828108830Stmm		error = iommu_dvma_valloc(dt, is, *mapp, currsz);
829108830Stmm		if (error != 0)
830108830Stmm			break;
831108830Stmm		totsz += currsz;
832108830Stmm	}
833117390Stmm	IS_LOCK(is);
834117390Stmm	iommu_map_insq(is, *mapp);
835117390Stmm	IS_UNLOCK(is);
83690616Stmm	return (0);
83790616Stmm}
83890616Stmm
839116541Stmmstatic int
840116541Stmmiommu_dvmamap_destroy(bus_dma_tag_t dt, bus_dmamap_t map)
84190616Stmm{
842116541Stmm	struct iommu_state *is = dt->dt_cookie;
84390616Stmm
844108830Stmm	iommu_dvma_vfree(is, map);
845116541Stmm	sparc64_dma_free_map(dt, map);
846116541Stmm	return (0);
84790616Stmm}
84890616Stmm
84986230Stmm/*
850251874Sscottl * Utility function to load a physical buffer.  segp contains
851251874Sscottl * the starting segment on entrace, and the ending segment on exit.
85286230Stmm */
853108830Stmmstatic int
854251874Sscottliommu_dvmamap_load_phys(bus_dma_tag_t dt, bus_dmamap_t map, vm_paddr_t buf,
855251874Sscottl    bus_size_t buflen, int flags, bus_dma_segment_t *segs, int *segp)
85686230Stmm{
857200923Smarius	bus_addr_t amask, dvmaddr, dvmoffs;
858117003Stmm	bus_size_t sgsize, esize;
859251874Sscottl	struct iommu_state *is;
860251874Sscottl	vm_offset_t voffs;
861113238Sjake	vm_paddr_t curaddr;
862188456Smarius	int error, firstpg, sgcnt;
863200923Smarius	u_int slot;
86486230Stmm
865251874Sscottl	is = dt->dt_cookie;
866251874Sscottl	if (*segp == -1) {
867251874Sscottl		if ((map->dm_flags & DMF_LOADED) != 0) {
868251874Sscottl#ifdef DIAGNOSTIC
869251874Sscottl			printf("%s: map still in use\n", __func__);
870251874Sscottl#endif
871251874Sscottl			bus_dmamap_unload(dt, map);
872251874Sscottl		}
873251874Sscottl
874251874Sscottl		/*
875251874Sscottl		 * Make sure that the map is not on a queue so that the
876251874Sscottl		 * resource list may be safely accessed and modified without
877251874Sscottl		 * needing the lock to cover the whole operation.
878251874Sscottl		 */
879251874Sscottl		IS_LOCK(is);
880251874Sscottl		iommu_map_remq(is, map);
881251874Sscottl		IS_UNLOCK(is);
882251874Sscottl
883251874Sscottl		amask = dt->dt_alignment - 1;
884251874Sscottl	} else
885251874Sscottl		amask = 0;
886176995Smarius	KASSERT(buflen != 0, ("%s: buflen == 0!", __func__));
887108815Stmm	if (buflen > dt->dt_maxsize)
88886230Stmm		return (EINVAL);
88986230Stmm
890251874Sscottl	if (segs == NULL)
891251874Sscottl		segs = dt->dt_segments;
89293070Stmm
893251874Sscottl	voffs = buf & IO_PAGE_MASK;
89486230Stmm
895108830Stmm	/* Try to find a slab that is large enough. */
896108830Stmm	error = iommu_dvma_vallocseg(dt, is, map, voffs, buflen, amask,
897108830Stmm	    &dvmaddr);
898108830Stmm	if (error != 0)
899108830Stmm		return (error);
900108830Stmm
901108830Stmm	sgcnt = *segp;
902108830Stmm	firstpg = 1;
903188456Smarius	map->dm_flags &= ~DMF_STREAMED;
904188456Smarius	map->dm_flags |= iommu_use_streaming(is, map, buflen) != 0 ?
905188456Smarius	    DMF_STREAMED : 0;
90686230Stmm	for (; buflen > 0; ) {
907251874Sscottl		curaddr = buf;
90886230Stmm
90986230Stmm		/*
91086230Stmm		 * Compute the segment size, and adjust counts.
91186230Stmm		 */
912251874Sscottl		sgsize = IO_PAGE_SIZE - ((u_long)buf & IO_PAGE_MASK);
91386230Stmm		if (buflen < sgsize)
91486230Stmm			sgsize = buflen;
91586230Stmm
916117003Stmm		buflen -= sgsize;
917251874Sscottl		buf += sgsize;
918117003Stmm
919200923Smarius		dvmoffs = trunc_io_page(dvmaddr);
920200923Smarius		iommu_enter(is, dvmoffs, trunc_io_page(curaddr),
921188456Smarius		    (map->dm_flags & DMF_STREAMED) != 0, flags);
922200923Smarius		if ((is->is_flags & IOMMU_FLUSH_CACHE) != 0) {
923200923Smarius			slot = IOTSBSLOT(dvmoffs);
924200923Smarius			if (buflen <= 0 || slot % 8 == 7)
925200923Smarius				IOMMU_WRITE8(is, is_iommu, IMR_CACHE_FLUSH,
926200923Smarius				    is->is_ptsb + slot * 8);
927200923Smarius		}
92886230Stmm
929117003Stmm		/*
930117003Stmm		 * Chop the chunk up into segments of at most maxsegsz, but try
931117003Stmm		 * to fill each segment as well as possible.
932117003Stmm		 */
933117003Stmm		if (!firstpg) {
934117003Stmm			esize = ulmin(sgsize,
935140281Sscottl			    dt->dt_maxsegsz - segs[sgcnt].ds_len);
936140281Sscottl			segs[sgcnt].ds_len += esize;
937117003Stmm			sgsize -= esize;
938117003Stmm			dvmaddr += esize;
939117003Stmm		}
940117003Stmm		while (sgsize > 0) {
94186230Stmm			sgcnt++;
942131224Sscottl			if (sgcnt >= dt->dt_nsegments)
943109651Stmm				return (EFBIG);
944108830Stmm			/*
945176995Smarius			 * No extra alignment here - the common practice in
946176995Smarius			 * the busdma code seems to be that only the first
947176995Smarius			 * segment needs to satisfy the alignment constraints
948176995Smarius			 * (and that only for bus_dmamem_alloc()ed maps).
949176995Smarius			 * It is assumed that such tags have maxsegsize >=
950176995Smarius			 * maxsize.
951108830Stmm			 */
952117003Stmm			esize = ulmin(sgsize, dt->dt_maxsegsz);
953140281Sscottl			segs[sgcnt].ds_addr = dvmaddr;
954140281Sscottl			segs[sgcnt].ds_len = esize;
955117003Stmm			sgsize -= esize;
956117003Stmm			dvmaddr += esize;
957117003Stmm		}
958117003Stmm
959108830Stmm		firstpg = 0;
96086230Stmm	}
961108830Stmm	*segp = sgcnt;
96286230Stmm	return (0);
96386230Stmm}
96486230Stmm
965251874Sscottl/*
966251874Sscottl * IOMMU DVMA operations, common to PCI and SBus
967251874Sscottl */
968116541Stmmstatic int
969251874Sscottliommu_dvmamap_load_buffer(bus_dma_tag_t dt, bus_dmamap_t map, void *buf,
970251874Sscottl    bus_size_t buflen, pmap_t pmap, int flags, bus_dma_segment_t *segs,
971251874Sscottl    int *segp)
972108830Stmm{
973251874Sscottl	bus_addr_t amask, dvmaddr, dvmoffs;
974251874Sscottl	bus_size_t sgsize, esize;
975251874Sscottl	struct iommu_state *is;
976251874Sscottl	vm_offset_t vaddr, voffs;
977251874Sscottl	vm_paddr_t curaddr;
978251874Sscottl	int error, firstpg, sgcnt;
979251874Sscottl	u_int slot;
98086230Stmm
981251874Sscottl	is = dt->dt_cookie;
982251874Sscottl	if (*segp == -1) {
983251874Sscottl		if ((map->dm_flags & DMF_LOADED) != 0) {
984108830Stmm#ifdef DIAGNOSTIC
985251874Sscottl			printf("%s: map still in use\n", __func__);
986108830Stmm#endif
987251874Sscottl			bus_dmamap_unload(dt, map);
988251874Sscottl		}
989108830Stmm
990251874Sscottl		/*
991251874Sscottl		 * Make sure that the map is not on a queue so that the
992251874Sscottl		 * resource list may be safely accessed and modified without
993251874Sscottl		 * needing the lock to cover the whole operation.
994251874Sscottl		 */
995251874Sscottl		IS_LOCK(is);
996251874Sscottl		iommu_map_remq(is, map);
997251874Sscottl		IS_UNLOCK(is);
998117390Stmm
999251874Sscottl		amask = dt->dt_alignment - 1;
1000251874Sscottl	} else
1001251874Sscottl		amask = 0;
1002251874Sscottl	KASSERT(buflen != 0, ("%s: buflen == 0!", __func__));
1003251874Sscottl	if (buflen > dt->dt_maxsize)
1004251874Sscottl		return (EINVAL);
1005108830Stmm
1006251874Sscottl	if (segs == NULL)
1007251874Sscottl		segs = dt->dt_segments;
1008108830Stmm
1009251874Sscottl	vaddr = (vm_offset_t)buf;
1010251874Sscottl	voffs = vaddr & IO_PAGE_MASK;
1011108830Stmm
1012251874Sscottl	/* Try to find a slab that is large enough. */
1013251874Sscottl	error = iommu_dvma_vallocseg(dt, is, map, voffs, buflen, amask,
1014251874Sscottl	    &dvmaddr);
1015251874Sscottl	if (error != 0)
1016251874Sscottl		return (error);
1017108830Stmm
1018251874Sscottl	sgcnt = *segp;
1019251874Sscottl	firstpg = 1;
1020251874Sscottl	map->dm_flags &= ~DMF_STREAMED;
1021251874Sscottl	map->dm_flags |= iommu_use_streaming(is, map, buflen) != 0 ?
1022251874Sscottl	    DMF_STREAMED : 0;
1023251874Sscottl	for (; buflen > 0; ) {
1024251874Sscottl		/*
1025251874Sscottl		 * Get the physical address for this page.
1026251874Sscottl		 */
1027251874Sscottl		if (pmap == kernel_pmap)
1028251874Sscottl			curaddr = pmap_kextract(vaddr);
1029251874Sscottl		else
1030251874Sscottl			curaddr = pmap_extract(pmap, vaddr);
1031108830Stmm
1032251874Sscottl		/*
1033251874Sscottl		 * Compute the segment size, and adjust counts.
1034251874Sscottl		 */
1035251874Sscottl		sgsize = IO_PAGE_SIZE - ((u_long)vaddr & IO_PAGE_MASK);
1036251874Sscottl		if (buflen < sgsize)
1037251874Sscottl			sgsize = buflen;
1038108830Stmm
1039251874Sscottl		buflen -= sgsize;
1040251874Sscottl		vaddr += sgsize;
1041117390Stmm
1042251874Sscottl		dvmoffs = trunc_io_page(dvmaddr);
1043251874Sscottl		iommu_enter(is, dvmoffs, trunc_io_page(curaddr),
1044251874Sscottl		    (map->dm_flags & DMF_STREAMED) != 0, flags);
1045251874Sscottl		if ((is->is_flags & IOMMU_FLUSH_CACHE) != 0) {
1046251874Sscottl			slot = IOTSBSLOT(dvmoffs);
1047251874Sscottl			if (buflen <= 0 || slot % 8 == 7)
1048251874Sscottl				IOMMU_WRITE8(is, is_iommu, IMR_CACHE_FLUSH,
1049251874Sscottl				    is->is_ptsb + slot * 8);
1050108830Stmm		}
1051108830Stmm
1052251874Sscottl		/*
1053251874Sscottl		 * Chop the chunk up into segments of at most maxsegsz, but try
1054251874Sscottl		 * to fill each segment as well as possible.
1055251874Sscottl		 */
1056251874Sscottl		if (!firstpg) {
1057251874Sscottl			esize = ulmin(sgsize,
1058251874Sscottl			    dt->dt_maxsegsz - segs[sgcnt].ds_len);
1059251874Sscottl			segs[sgcnt].ds_len += esize;
1060251874Sscottl			sgsize -= esize;
1061251874Sscottl			dvmaddr += esize;
1062251874Sscottl		}
1063251874Sscottl		while (sgsize > 0) {
1064251874Sscottl			sgcnt++;
1065251874Sscottl			if (sgcnt >= dt->dt_nsegments)
1066251874Sscottl				return (EFBIG);
1067251874Sscottl			/*
1068251874Sscottl			 * No extra alignment here - the common practice in
1069251874Sscottl			 * the busdma code seems to be that only the first
1070251874Sscottl			 * segment needs to satisfy the alignment constraints
1071251874Sscottl			 * (and that only for bus_dmamem_alloc()ed maps).
1072251874Sscottl			 * It is assumed that such tags have maxsegsize >=
1073251874Sscottl			 * maxsize.
1074251874Sscottl			 */
1075251874Sscottl			esize = ulmin(sgsize, dt->dt_maxsegsz);
1076251874Sscottl			segs[sgcnt].ds_addr = dvmaddr;
1077251874Sscottl			segs[sgcnt].ds_len = esize;
1078251874Sscottl			sgsize -= esize;
1079251874Sscottl			dvmaddr += esize;
1080251874Sscottl		}
1081251874Sscottl
1082251874Sscottl		firstpg = 0;
1083108830Stmm	}
1084251874Sscottl	*segp = sgcnt;
1085251874Sscottl	return (0);
1086108830Stmm}
1087108830Stmm
1088251874Sscottlstatic void
1089251874Sscottliommu_dvmamap_waitok(bus_dma_tag_t dmat, bus_dmamap_t map,
1090251874Sscottl    struct memdesc *mem, bus_dmamap_callback_t *callback, void *callback_arg)
1091140281Sscottl{
1092140281Sscottl}
1093140281Sscottl
1094251874Sscottlstatic bus_dma_segment_t *
1095251874Sscottliommu_dvmamap_complete(bus_dma_tag_t dt, bus_dmamap_t map,
1096251874Sscottl    bus_dma_segment_t *segs, int nsegs, int error)
1097108830Stmm{
1098116541Stmm	struct iommu_state *is = dt->dt_cookie;
1099108830Stmm
1100117390Stmm	IS_LOCK(is);
1101117390Stmm	iommu_map_insq(is, map);
1102251874Sscottl	if (error != 0) {
1103108830Stmm		iommu_dvmamap_vunload(is, map);
1104117390Stmm		IS_UNLOCK(is);
1105108830Stmm	} else {
1106117390Stmm		IS_UNLOCK(is);
1107117390Stmm		map->dm_flags |= DMF_LOADED;
1108108830Stmm	}
1109251874Sscottl	if (segs == NULL)
1110251874Sscottl		segs = dt->dt_segments;
1111251874Sscottl	return (segs);
1112108830Stmm}
1113108830Stmm
1114116541Stmmstatic void
1115116541Stmmiommu_dvmamap_unload(bus_dma_tag_t dt, bus_dmamap_t map)
111686230Stmm{
1117116541Stmm	struct iommu_state *is = dt->dt_cookie;
111886230Stmm
1119117390Stmm	if ((map->dm_flags & DMF_LOADED) == 0)
1120107470Stmm		return;
1121117390Stmm	IS_LOCK(is);
1122108830Stmm	iommu_dvmamap_vunload(is, map);
1123117390Stmm	iommu_map_insq(is, map);
1124117390Stmm	IS_UNLOCK(is);
1125117390Stmm	map->dm_flags &= ~DMF_LOADED;
112686230Stmm}
112786230Stmm
1128116541Stmmstatic void
1129116541Stmmiommu_dvmamap_sync(bus_dma_tag_t dt, bus_dmamap_t map, bus_dmasync_op_t op)
113086230Stmm{
1131116541Stmm	struct iommu_state *is = dt->dt_cookie;
1132108830Stmm	struct bus_dmamap_res *r;
113386230Stmm	vm_offset_t va;
113486230Stmm	vm_size_t len;
1135117390Stmm	int streamed = 0;
113686230Stmm
1137117390Stmm	if ((map->dm_flags & DMF_LOADED) == 0)
1138117390Stmm		return;
1139188456Smarius	if ((map->dm_flags & DMF_STREAMED) != 0 &&
1140117390Stmm	    ((op & BUS_DMASYNC_POSTREAD) != 0 ||
1141167308Smarius	    (op & BUS_DMASYNC_PREWRITE) != 0)) {
1142117390Stmm		IS_LOCK(is);
1143108830Stmm		SLIST_FOREACH(r, &map->dm_reslist, dr_link) {
1144108830Stmm			va = (vm_offset_t)BDR_START(r);
1145108830Stmm			len = r->dr_used;
1146176995Smarius			/*
1147176995Smarius			 * If we have a streaming buffer, flush it here
1148176995Smarius			 * first.
1149176995Smarius			 */
1150108830Stmm			while (len > 0) {
1151176995Smarius				if ((IOMMU_GET_TTE(is, va) &
1152176995Smarius				    IOTTE_STREAM) != 0) {
1153117390Stmm					streamed = 1;
1154117390Stmm					iommu_strbuf_flush(is, va);
1155117390Stmm				}
1156108830Stmm				len -= ulmin(len, IO_PAGE_SIZE);
1157108830Stmm				va += IO_PAGE_SIZE;
1158108830Stmm			}
115986230Stmm		}
1160117390Stmm		if (streamed)
1161117390Stmm			iommu_strbuf_sync(is);
1162117390Stmm		IS_UNLOCK(is);
116386230Stmm	}
1164117390Stmm	if ((op & BUS_DMASYNC_PREWRITE) != 0)
1165117390Stmm		membar(Sync);
116686230Stmm}
116786230Stmm
116886230Stmm#ifdef IOMMU_DIAG
116986230Stmm
117086230Stmm/*
117186230Stmm * Perform an IOMMU diagnostic access and print the tag belonging to va.
117286230Stmm */
117386230Stmmstatic void
117486230Stmmiommu_diag(struct iommu_state *is, vm_offset_t va)
117586230Stmm{
117686230Stmm	int i;
1177178840Smarius	uint64_t data, tag;
117886230Stmm
1179200923Smarius	if ((is->is_flags & IOMMU_FIRE) != 0)
1180200923Smarius		return;
1181117390Stmm	IS_LOCK_ASSERT(is);
118290616Stmm	IOMMU_WRITE8(is, is_dva, 0, trunc_io_page(va));
118386230Stmm	membar(StoreStore | StoreLoad);
1184167308Smarius	printf("%s: tte entry %#lx", __func__, IOMMU_GET_TTE(is, va));
118590616Stmm	if (is->is_dtcmp != 0) {
1186108802Stmm		printf(", tag compare register is %#lx\n",
118790616Stmm		    IOMMU_READ8(is, is_dtcmp, 0));
118890616Stmm	} else
118990616Stmm		printf("\n");
119086230Stmm	for (i = 0; i < 16; i++) {
119190616Stmm		tag = IOMMU_READ8(is, is_dtag, i * 8);
119290616Stmm		data = IOMMU_READ8(is, is_ddram, i * 8);
1193167308Smarius		printf("%s: tag %d: %#lx, vpn %#lx, err %lx; "
1194167308Smarius		    "data %#lx, pa %#lx, v %d, c %d\n", __func__, i,
119586230Stmm		    tag, (tag & IOMMU_DTAG_VPNMASK) << IOMMU_DTAG_VPNSHIFT,
119686230Stmm		    (tag & IOMMU_DTAG_ERRMASK) >> IOMMU_DTAG_ERRSHIFT, data,
119786230Stmm		    (data & IOMMU_DDATA_PGMASK) << IOMMU_DDATA_PGSHIFT,
119886230Stmm		    (data & IOMMU_DDATA_V) != 0, (data & IOMMU_DDATA_C) != 0);
119986230Stmm	}
120086230Stmm}
120186230Stmm
120286230Stmm#endif /* IOMMU_DIAG */
1203116541Stmm
1204116541Stmmstruct bus_dma_methods iommu_dma_methods = {
1205116541Stmm	iommu_dvmamap_create,
1206116541Stmm	iommu_dvmamap_destroy,
1207251874Sscottl	iommu_dvmamap_load_phys,
1208251874Sscottl	iommu_dvmamap_load_buffer,
1209251874Sscottl	iommu_dvmamap_waitok,
1210251874Sscottl	iommu_dvmamap_complete,
1211116541Stmm	iommu_dvmamap_unload,
1212116541Stmm	iommu_dvmamap_sync,
1213116541Stmm	iommu_dvmamem_alloc,
1214116541Stmm	iommu_dvmamem_free,
1215116541Stmm};
1216