busdma_machdep-v6.c revision 243909
1121985Sjhb/*-
2121985Sjhb * Copyright (c) 2010 Mark Tinguely
3121985Sjhb * Copyright (c) 2004 Olivier Houchard
4121985Sjhb * Copyright (c) 2002 Peter Grehan
5121985Sjhb * Copyright (c) 1997, 1998 Justin T. Gibbs.
6121985Sjhb * All rights reserved.
7121985Sjhb *
8121985Sjhb * Redistribution and use in source and binary forms, with or without
9121985Sjhb * modification, are permitted provided that the following conditions
10121985Sjhb * are met:
11121985Sjhb * 1. Redistributions of source code must retain the above copyright
12121985Sjhb *    notice, this list of conditions, and the following disclaimer,
13121985Sjhb *    without modification, immediately at the beginning of the file.
14121985Sjhb * 2. The name of the author may not be used to endorse or promote products
15121985Sjhb *    derived from this software without specific prior written permission.
16121985Sjhb *
17121985Sjhb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18121985Sjhb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19121985Sjhb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20121985Sjhb * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
21121985Sjhb * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22121985Sjhb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23121985Sjhb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24121985Sjhb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25121985Sjhb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26121985Sjhb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27121985Sjhb * SUCH DAMAGE.
28121985Sjhb *
29121985Sjhb *  From i386/busdma_machdep.c 191438 2009-04-23 20:24:19Z jhb
30121985Sjhb */
31121985Sjhb
32121985Sjhb#include <sys/cdefs.h>
33121985Sjhb__FBSDID("$FreeBSD: head/sys/arm/arm/busdma_machdep-v6.c 243909 2012-12-05 21:07:27Z cognet $");
34121985Sjhb
35277285Simp#define _ARM32_BUS_DMA_PRIVATE
36121985Sjhb#include <sys/param.h>
37121985Sjhb#include <sys/kdb.h>
38121985Sjhb#include <ddb/ddb.h>
39121985Sjhb#include <ddb/db_output.h>
40121985Sjhb#include <sys/systm.h>
41121985Sjhb#include <sys/malloc.h>
42121985Sjhb#include <sys/bus.h>
43129876Sphk#include <sys/interrupt.h>
44121985Sjhb#include <sys/kernel.h>
45121985Sjhb#include <sys/ktr.h>
46121985Sjhb#include <sys/lock.h>
47121985Sjhb#include <sys/proc.h>
48121985Sjhb#include <sys/mutex.h>
49121985Sjhb#include <sys/mbuf.h>
50121985Sjhb#include <sys/uio.h>
51121985Sjhb#include <sys/sysctl.h>
52124188Sjhb
53204309Sattilio#include <vm/vm.h>
54122051Snyan#include <vm/vm_page.h>
55146049Snyan#include <vm/vm_map.h>
56122051Snyan
57263379Simp#include <machine/atomic.h>
58122051Snyan#include <machine/bus.h>
59121985Sjhb#include <machine/cpufunc.h>
60277311Simp#include <machine/md_var.h>
61277311Simp
62277311Simp#define MAX_BPAGES 64
63121985Sjhb#define BUS_DMA_COULD_BOUNCE	BUS_DMA_BUS3
64204309Sattilio#define BUS_DMA_MIN_ALLOC_COMP	BUS_DMA_BUS4
65204309Sattilio
66204309Sattilio#define FIX_DMAP_BUS_DMASYNC_POSTREAD
67204309Sattilio
68204309Sattiliostruct bounce_zone;
69204309Sattilio
70204309Sattiliostruct bus_dma_tag {
71204309Sattilio	bus_dma_tag_t	  parent;
72121985Sjhb	bus_size_t	  alignment;
73121985Sjhb	bus_size_t	  boundary;
74121985Sjhb	bus_addr_t	  lowaddr;
75339928Sjhb	bus_addr_t	  highaddr;
76339928Sjhb	bus_dma_filter_t *filter;
77128875Sjhb	void		 *filterarg;
78128875Sjhb	bus_size_t	  maxsize;
79121985Sjhb	u_int		  nsegments;
80121985Sjhb	bus_size_t	  maxsegsz;
81121985Sjhb	int		  flags;
82121985Sjhb	int		  ref_count;
83121985Sjhb	int		  map_count;
84121985Sjhb	bus_dma_lock_t	 *lockfunc;
85121985Sjhb	void		 *lockfuncarg;
86121985Sjhb	bus_dma_segment_t *segments;
87121985Sjhb	struct bounce_zone *bounce_zone;
88329462Skib	/*
89329462Skib	 * DMA range for this tag.  If the page doesn't fall within
90329462Skib	 * one of these ranges, an error is returned.  The caller
91329462Skib	 * may then decide what to do with the transfer.  If the
92329462Skib	 * range pointer is NULL, it is ignored.
93329462Skib	 */
94329462Skib	struct arm32_dma_range	*ranges;
95329462Skib	int			_nranges;
96329462Skib
97329462Skib};
98121985Sjhb
99121985Sjhbstruct bounce_page {
100121985Sjhb	vm_offset_t	vaddr;		/* kva of bounce buffer */
101339928Sjhb	bus_addr_t	busaddr;	/* Physical address */
102339928Sjhb	vm_offset_t	datavaddr;	/* kva of client data */
103340016Sjhb	bus_size_t	datacount;	/* client data count */
104339928Sjhb	STAILQ_ENTRY(bounce_page) links;
105339928Sjhb};
106339928Sjhb
107339928Sjhbstruct sync_list {
108339928Sjhb	vm_offset_t	vaddr;		/* kva of bounce buffer */
109339928Sjhb	bus_addr_t	busaddr;	/* Physical address */
110339928Sjhb	bus_size_t	datacount;	/* client data count */
111339928Sjhb	STAILQ_ENTRY(sync_list) slinks;
112339928Sjhb};
113339928Sjhb
114339928Sjhbint busdma_swi_pending;
115339928Sjhb
116339928Sjhbstruct bounce_zone {
117339928Sjhb	STAILQ_ENTRY(bounce_zone) links;
118339928Sjhb	STAILQ_HEAD(bp_list, bounce_page) bounce_page_list;
119339928Sjhb	int		total_bpages;
120121985Sjhb	int		free_bpages;
121121985Sjhb	int		reserved_bpages;
122128929Sjhb	int		active_bpages;
123329462Skib	int		total_bounced;
124121985Sjhb	int		total_deferred;
125121985Sjhb	int		map_count;
126121985Sjhb	bus_size_t	alignment;
127121985Sjhb	bus_addr_t	lowaddr;
128121985Sjhb	char		zoneid[8];
129121985Sjhb	char		lowaddrid[20];
130339928Sjhb	struct sysctl_ctx_list sysctl_tree;
131121985Sjhb	struct sysctl_oid *sysctl_tree_top;
132121985Sjhb};
133121985Sjhb
134121985Sjhbstatic struct mtx bounce_lock;
135329462Skibstatic int total_bpages;
136128929Sjhbstatic int busdma_zonecount;
137128929Sjhbstatic STAILQ_HEAD(, bounce_zone) bounce_zone_list;
138122897Sjhb
139122897SjhbSYSCTL_NODE(_hw, OID_AUTO, busdma, CTLFLAG_RD, 0, "Busdma parameters");
140121985SjhbSYSCTL_INT(_hw_busdma, OID_AUTO, total_bpages, CTLFLAG_RD, &total_bpages, 0,
141121985Sjhb	   "Total bounce pages");
142340016Sjhb
143121985Sjhbstruct bus_dmamap {
144133017Sscottl	struct bp_list	       bpages;
145121985Sjhb	int		       pagesneeded;
146121985Sjhb	int		       pagesreserved;
147121985Sjhb	bus_dma_tag_t	       dmat;
148169391Sjhb	void		      *buf;		/* unmapped buffer pointer */
149121985Sjhb	bus_size_t	       buflen;		/* unmapped buffer length */
150255726Sgibbs	pmap_t		       pmap;
151121985Sjhb	bus_dmamap_callback_t *callback;
152128931Sjhb	void		      *callback_arg;
153128931Sjhb	STAILQ_ENTRY(bus_dmamap) links;
154195249Sjhb	STAILQ_HEAD(,sync_list)	slist;
155121985Sjhb};
156121985Sjhb
157121985Sjhbstatic STAILQ_HEAD(, bus_dmamap) bounce_map_waitinglist;
158339928Sjhbstatic STAILQ_HEAD(, bus_dmamap) bounce_map_callbacklist;
159339928Sjhb
160121985Sjhbstatic void init_bounce_pages(void *dummy);
161121985Sjhbstatic int alloc_bounce_zone(bus_dma_tag_t dmat);
162121985Sjhbstatic int alloc_bounce_pages(bus_dma_tag_t dmat, u_int numpages);
163121985Sjhbstatic int reserve_bounce_pages(bus_dma_tag_t dmat, bus_dmamap_t map,
164121985Sjhb				int commit);
165121985Sjhbstatic bus_addr_t add_bounce_page(bus_dma_tag_t dmat, bus_dmamap_t map,
166121985Sjhb				   vm_offset_t vaddr, bus_size_t size);
167121985Sjhbstatic void free_bounce_page(bus_dma_tag_t dmat, struct bounce_page *bpage);
168121985Sjhbint run_filter(bus_dma_tag_t dmat, bus_addr_t paddr);
169121985Sjhbstatic int _bus_dmamap_count_pages(bus_dma_tag_t dmat, bus_dmamap_t map,
170121985Sjhb    void *buf, bus_size_t buflen, int flags);
171121985Sjhb
172121985Sjhbstatic __inline int
173121985Sjhb_bus_dma_can_bounce(vm_offset_t lowaddr, vm_offset_t highaddr)
174121985Sjhb{
175121985Sjhb	int i;
176121985Sjhb	for (i = 0; phys_avail[i] && phys_avail[i + 1]; i += 2) {
177121985Sjhb		if ((lowaddr >= phys_avail[i] && lowaddr <= phys_avail[i + 1])
178121985Sjhb		    || (lowaddr < phys_avail[i] &&
179121985Sjhb		    highaddr > phys_avail[i]))
180121985Sjhb			return (1);
181298308Spfg	}
182128875Sjhb	return (0);
183133017Sscottl}
184133017Sscottl
185133017Sscottlstatic __inline struct arm32_dma_range *
186133017Sscottl_bus_dma_inrange(struct arm32_dma_range *ranges, int nranges,
187133017Sscottl    bus_addr_t curaddr)
188133017Sscottl{
189133017Sscottl	struct arm32_dma_range *dr;
190133017Sscottl	int i;
191133017Sscottl
192133017Sscottl	for (i = 0, dr = ranges; i < nranges; i++, dr++) {
193133017Sscottl		if (curaddr >= dr->dr_sysbase &&
194133017Sscottl		    round_page(curaddr) <= (dr->dr_sysbase + dr->dr_len))
195133017Sscottl			return (dr);
196133017Sscottl	}
197133017Sscottl
198133017Sscottl	return (NULL);
199133017Sscottl}
200133017Sscottl
201133017Sscottl/*
202133017Sscottl * Return true if a match is made.
203133017Sscottl *
204133017Sscottl * To find a match walk the chain of bus_dma_tag_t's looking for 'paddr'.
205133017Sscottl *
206133017Sscottl * If paddr is within the bounds of the dma tag then call the filter callback
207133017Sscottl * to check for a match, if there is no filter callback then assume a match.
208133017Sscottl */
209133017Sscottlint
210133017Sscottlrun_filter(bus_dma_tag_t dmat, bus_addr_t paddr)
211133017Sscottl{
212121985Sjhb	int retval;
213340016Sjhb
214340016Sjhb	retval = 0;
215340016Sjhb
216340016Sjhb	do {
217340016Sjhb		if (((paddr > dmat->lowaddr && paddr <= dmat->highaddr)
218340016Sjhb		 || ((paddr & (dmat->alignment - 1)) != 0))
219340016Sjhb		 && (dmat->filter == NULL
220340016Sjhb		  || (*dmat->filter)(dmat->filterarg, paddr) != 0))
221340016Sjhb			retval = 1;
222340016Sjhb
223340016Sjhb		dmat = dmat->parent;
224340016Sjhb	} while (retval == 0 && dmat != NULL);
225340016Sjhb	return (retval);
226340016Sjhb}
227340016Sjhb
228340016Sjhb/*
229340016Sjhb * Convenience function for manipulating driver locks from busdma (during
230340016Sjhb * busdma_swi, for example).  Drivers that don't provide their own locks
231340016Sjhb * should specify &Giant to dmat->lockfuncarg.  Drivers that use their own
232340016Sjhb * non-mutex locking scheme don't have to use this at all.
233340016Sjhb */
234340016Sjhbvoid
235340016Sjhbbusdma_lock_mutex(void *arg, bus_dma_lock_op_t op)
236340016Sjhb{
237340016Sjhb	struct mtx *dmtx;
238340016Sjhb
239340016Sjhb	dmtx = (struct mtx *)arg;
240340016Sjhb	switch (op) {
241340016Sjhb	case BUS_DMA_LOCK:
242340016Sjhb		mtx_lock(dmtx);
243340016Sjhb		break;
244340016Sjhb	case BUS_DMA_UNLOCK:
245340016Sjhb		mtx_unlock(dmtx);
246340016Sjhb		break;
247340016Sjhb	default:
248340016Sjhb		panic("Unknown operation 0x%x for busdma_lock_mutex!", op);
249121985Sjhb	}
250121985Sjhb}
251121985Sjhb
252121985Sjhb/*
253121985Sjhb * dflt_lock should never get called.  It gets put into the dma tag when
254177468Sjhb * lockfunc == NULL, which is only valid if the maps that are associated
255339928Sjhb * with the tag are meant to never be defered.
256339928Sjhb * XXX Should have a way to identify which driver is responsible here.
257339928Sjhb */
258128875Sjhbstatic void
259177468Sjhbdflt_lock(void *arg, bus_dma_lock_op_t op)
260121985Sjhb{
261121985Sjhb	panic("driver error: busdma dflt_lock called");
262121985Sjhb}
263133017Sscottl
264121985Sjhb/*
265121985Sjhb * Allocate a device specific dma_tag.
266121985Sjhb */
267121985Sjhbint
268177468Sjhbbus_dma_tag_create(bus_dma_tag_t parent, bus_size_t alignment,
269133017Sscottl		   bus_size_t boundary, bus_addr_t lowaddr,
270339928Sjhb		   bus_addr_t highaddr, bus_dma_filter_t *filter,
271339928Sjhb		   void *filterarg, bus_size_t maxsize, int nsegments,
272133017Sscottl		   bus_size_t maxsegsz, int flags, bus_dma_lock_t *lockfunc,
273133017Sscottl		   void *lockfuncarg, bus_dma_tag_t *dmat)
274133017Sscottl{
275133017Sscottl	bus_dma_tag_t newtag;
276133017Sscottl	int error = 0;
277133017Sscottl
278133017Sscottl#if 0
279133017Sscottl	if (!parent)
280133017Sscottl		parent = arm_root_dma_tag;
281133017Sscottl#endif
282133017Sscottl
283133017Sscottl	/* Basic sanity checking */
284133017Sscottl	if (boundary != 0 && boundary < maxsegsz)
285133017Sscottl		maxsegsz = boundary;
286177468Sjhb
287121985Sjhb	/* Return a NULL tag on failure */
288121985Sjhb	*dmat = NULL;
289121985Sjhb
290121985Sjhb	if (maxsegsz == 0) {
291121985Sjhb		return (EINVAL);
292121985Sjhb	}
293177468Sjhb
294133017Sscottl	newtag = (bus_dma_tag_t)malloc(sizeof(*newtag), M_DEVBUF,
295177468Sjhb	    M_ZERO | M_NOWAIT);
296121985Sjhb	if (newtag == NULL) {
297121985Sjhb		CTR4(KTR_BUSDMA, "%s returned tag %p tag flags 0x%x error %d",
298121985Sjhb		    __func__, newtag, 0, error);
299121985Sjhb		return (ENOMEM);
300121985Sjhb	}
301121985Sjhb
302121985Sjhb	newtag->parent = parent;
303177468Sjhb	newtag->alignment = alignment;
304133017Sscottl	newtag->boundary = boundary;
305177468Sjhb	newtag->lowaddr = trunc_page((vm_paddr_t)lowaddr) + (PAGE_SIZE - 1);
306121985Sjhb	newtag->highaddr = trunc_page((vm_paddr_t)highaddr) +
307121985Sjhb	    (PAGE_SIZE - 1);
308121985Sjhb	newtag->filter = filter;
309121985Sjhb	newtag->filterarg = filterarg;
310121985Sjhb	newtag->maxsize = maxsize;
311121985Sjhb	newtag->nsegments = nsegments;
312121985Sjhb	newtag->maxsegsz = maxsegsz;
313121985Sjhb	newtag->flags = flags;
314169391Sjhb	newtag->ref_count = 1; /* Count ourself */
315169391Sjhb	newtag->map_count = 0;
316169391Sjhb	newtag->ranges = bus_dma_get_range();
317169391Sjhb	newtag->_nranges = bus_dma_get_range_nb();
318169391Sjhb	if (lockfunc != NULL) {
319169391Sjhb		newtag->lockfunc = lockfunc;
320121985Sjhb		newtag->lockfuncarg = lockfuncarg;
321121985Sjhb	} else {
322121985Sjhb		newtag->lockfunc = dflt_lock;
323121985Sjhb		newtag->lockfuncarg = NULL;
324121985Sjhb	}
325121985Sjhb	newtag->segments = NULL;
326121985Sjhb
327121985Sjhb	/* Take into account any restrictions imposed by our parent tag */
328121985Sjhb	if (parent != NULL) {
329121985Sjhb		newtag->lowaddr = MIN(parent->lowaddr, newtag->lowaddr);
330121985Sjhb		newtag->highaddr = MAX(parent->highaddr, newtag->highaddr);
331121985Sjhb		if (newtag->boundary == 0)
332121985Sjhb			newtag->boundary = parent->boundary;
333121985Sjhb		else if (parent->boundary != 0)
334121985Sjhb			newtag->boundary = MIN(parent->boundary,
335128875Sjhb					       newtag->boundary);
336121985Sjhb		if ((newtag->filter != NULL) ||
337121985Sjhb		    ((parent->flags & BUS_DMA_COULD_BOUNCE) != 0))
338121985Sjhb			newtag->flags |= BUS_DMA_COULD_BOUNCE;
339255726Sgibbs		if (newtag->filter == NULL) {
340121985Sjhb			/*
341163219Sjhb			 * Short circuit looking at our parent directly
342121985Sjhb			 * since we have encapsulated all of its information
343163219Sjhb			 */
344129009Snyan			newtag->filter = parent->filter;
345163219Sjhb			newtag->filterarg = parent->filterarg;
346163219Sjhb			newtag->parent = parent->parent;
347129009Snyan		}
348121985Sjhb		if (newtag->parent != NULL)
349121985Sjhb			atomic_add_int(&parent->ref_count, 1);
350128931Sjhb	}
351128931Sjhb
352128931Sjhb	if (_bus_dma_can_bounce(newtag->lowaddr, newtag->highaddr)
353128931Sjhb	 || newtag->alignment > 1)
354128931Sjhb		newtag->flags |= BUS_DMA_COULD_BOUNCE;
355128931Sjhb
356128931Sjhb	if (((newtag->flags & BUS_DMA_COULD_BOUNCE) != 0) &&
357128931Sjhb	    (flags & BUS_DMA_ALLOCNOW) != 0) {
358128931Sjhb		struct bounce_zone *bz;
359128931Sjhb
360128931Sjhb		/* Must bounce */
361128931Sjhb
362128931Sjhb		if ((error = alloc_bounce_zone(newtag)) != 0) {
363128931Sjhb			free(newtag, M_DEVBUF);
364128931Sjhb			return (error);
365128931Sjhb		}
366128931Sjhb		bz = newtag->bounce_zone;
367128931Sjhb
368128931Sjhb		if (ptoa(bz->total_bpages) < maxsize) {
369128931Sjhb			int pages;
370128931Sjhb
371128931Sjhb			pages = atop(maxsize) - bz->total_bpages;
372128931Sjhb
373128931Sjhb			/* Add pages to our bounce pool */
374128931Sjhb			if (alloc_bounce_pages(newtag, pages) < pages)
375128931Sjhb				error = ENOMEM;
376129009Snyan		}
377129009Snyan		/* Performed initial allocation */
378129009Snyan		newtag->flags |= BUS_DMA_MIN_ALLOC_COMP;
379129009Snyan	} else
380129009Snyan		newtag->bounce_zone = NULL;
381129009Snyan
382129009Snyan	if (error != 0) {
383129009Snyan		free(newtag, M_DEVBUF);
384129009Snyan	} else {
385129009Snyan		*dmat = newtag;
386129009Snyan	}
387128931Sjhb	CTR4(KTR_BUSDMA, "%s returned tag %p tag flags 0x%x error %d",
388128931Sjhb	    __func__, newtag, (newtag != NULL ? newtag->flags : 0), error);
389128931Sjhb	return (error);
390128931Sjhb}
391128931Sjhb
392128931Sjhbint
393128931Sjhbbus_dma_tag_destroy(bus_dma_tag_t dmat)
394128931Sjhb{
395128931Sjhb	bus_dma_tag_t dmat_copy;
396128931Sjhb	int error;
397128931Sjhb
398128931Sjhb	error = 0;
399128931Sjhb	dmat_copy = dmat;
400140451Sjhb
401128931Sjhb	if (dmat != NULL) {
402128931Sjhb
403128931Sjhb		if (dmat->map_count != 0) {
404128931Sjhb			error = EBUSY;
405128931Sjhb			goto out;
406128931Sjhb		}
407128931Sjhb
408128931Sjhb		while (dmat != NULL) {
409128931Sjhb			bus_dma_tag_t parent;
410177468Sjhb
411128931Sjhb			parent = dmat->parent;
412128931Sjhb			atomic_subtract_int(&dmat->ref_count, 1);
413177468Sjhb			if (dmat->ref_count == 0) {
414128931Sjhb				if (dmat->segments != NULL)
415129009Snyan					free(dmat->segments, M_DEVBUF);
416128931Sjhb				free(dmat, M_DEVBUF);
417128931Sjhb				/*
418195249Sjhb				 * Last reference count, so
419156124Sjhb				 * release our reference
420156124Sjhb				 * count on our parent.
421156124Sjhb				 */
422156124Sjhb				dmat = parent;
423156124Sjhb			} else
424156124Sjhb				dmat = NULL;
425156124Sjhb		}
426156124Sjhb	}
427156124Sjhbout:
428156124Sjhb	CTR3(KTR_BUSDMA, "%s tag %p error %d", __func__, dmat_copy, error);
429156124Sjhb	return (error);
430121985Sjhb}
431121985Sjhb
432121985Sjhb/*
433121985Sjhb * Allocate a handle for mapping from kva/uva/physical
434121985Sjhb * address space into bus device space.
435177468Sjhb */
436121985Sjhbint
437122692Sjhbbus_dmamap_create(bus_dma_tag_t dmat, int flags, bus_dmamap_t *mapp)
438121985Sjhb{
439122692Sjhb	int error;
440121985Sjhb
441121985Sjhb	error = 0;
442122692Sjhb
443121985Sjhb	*mapp = (bus_dmamap_t)malloc(sizeof(**mapp), M_DEVBUF,
444121985Sjhb					     M_NOWAIT | M_ZERO);
445121985Sjhb	if (*mapp == NULL) {
446121985Sjhb		CTR3(KTR_BUSDMA, "%s: tag %p error %d", __func__, dmat, ENOMEM);
447121985Sjhb		return (ENOMEM);
448121985Sjhb	}
449121985Sjhb	STAILQ_INIT(&((*mapp)->slist));
450121985Sjhb
451121985Sjhb	if (dmat->segments == NULL) {
452121985Sjhb		dmat->segments = (bus_dma_segment_t *)malloc(
453121985Sjhb		    sizeof(bus_dma_segment_t) * dmat->nsegments, M_DEVBUF,
454129131Sjhb		    M_NOWAIT);
455121985Sjhb		if (dmat->segments == NULL) {
456129131Sjhb			CTR3(KTR_BUSDMA, "%s: tag %p error %d",
457121985Sjhb			    __func__, dmat, ENOMEM);
458121985Sjhb			free(*mapp, M_DEVBUF);
459121985Sjhb			*mapp = NULL;
460121985Sjhb			return (ENOMEM);
461121985Sjhb		}
462121985Sjhb	}
463121985Sjhb	/*
464121985Sjhb	 * Bouncing might be required if the driver asks for an active
465339928Sjhb	 * exclusion region, a data alignment that is stricter than 1, and/or
466121985Sjhb	 * an active address boundary.
467121985Sjhb	 */
468122692Sjhb	if (dmat->flags & BUS_DMA_COULD_BOUNCE) {
469121985Sjhb
470121985Sjhb		/* Must bounce */
471122692Sjhb		struct bounce_zone *bz;
472121985Sjhb		int maxpages;
473122692Sjhb
474121985Sjhb		if (dmat->bounce_zone == NULL) {
475177468Sjhb			if ((error = alloc_bounce_zone(dmat)) != 0) {
476121985Sjhb				free(*mapp, M_DEVBUF);
477121985Sjhb				*mapp = NULL;
478121985Sjhb				return (error);
479121985Sjhb			}
480121985Sjhb		}
481122897Sjhb		bz = dmat->bounce_zone;
482122897Sjhb
483121985Sjhb		/* Initialize the new map */
484121985Sjhb		STAILQ_INIT(&((*mapp)->bpages));
485121985Sjhb
486121985Sjhb		/*
487121985Sjhb		 * Attempt to add pages to our pool on a per-instance
488122897Sjhb		 * basis up to a sane limit.
489122897Sjhb		 */
490128875Sjhb		maxpages = MAX_BPAGES;
491122897Sjhb		if ((dmat->flags & BUS_DMA_MIN_ALLOC_COMP) == 0
492122897Sjhb		 || (bz->map_count > 0 && bz->total_bpages < maxpages)) {
493122897Sjhb			int pages;
494122897Sjhb
495122897Sjhb			pages = MAX(atop(dmat->maxsize), 1);
496329462Skib			pages = MIN(maxpages - bz->total_bpages, pages);
497329462Skib			pages = MAX(pages, 1);
498122897Sjhb			if (alloc_bounce_pages(dmat, pages) < pages)
499128929Sjhb				error = ENOMEM;
500128929Sjhb
501128929Sjhb			if ((dmat->flags & BUS_DMA_MIN_ALLOC_COMP) == 0) {
502128929Sjhb				if (error == 0)
503128929Sjhb					dmat->flags |= BUS_DMA_MIN_ALLOC_COMP;
504128929Sjhb			} else {
505128929Sjhb				error = 0;
506128929Sjhb			}
507128929Sjhb		}
508129009Snyan		bz->map_count++;
509129009Snyan	}
510129009Snyan	if (error == 0)
511129009Snyan		dmat->map_count++;
512129009Snyan	CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d",
513129009Snyan	    __func__, dmat, dmat->flags, error);
514129009Snyan	return (error);
515129009Snyan}
516129009Snyan
517129009Snyan/*
518129009Snyan * Destroy a handle for mapping from kva/uva/physical
519129009Snyan * address space into bus device space.
520129009Snyan */
521129009Snyanint
522128929Sjhbbus_dmamap_destroy(bus_dma_tag_t dmat, bus_dmamap_t map)
523128929Sjhb{
524128929Sjhb	if (STAILQ_FIRST(&map->bpages) != NULL ||
525128929Sjhb	    STAILQ_FIRST(&map->slist) != NULL) {
526128929Sjhb		CTR3(KTR_BUSDMA, "%s: tag %p error %d",
527128929Sjhb		    __func__, dmat, EBUSY);
528128929Sjhb		return (EBUSY);
529128929Sjhb	}
530129095Sjhb	if (dmat->bounce_zone)
531129095Sjhb		dmat->bounce_zone->map_count--;
532128929Sjhb	free(map, M_DEVBUF);
533140451Sjhb	dmat->map_count--;
534128929Sjhb	CTR2(KTR_BUSDMA, "%s: tag %p error 0", __func__, dmat);
535128929Sjhb	return (0);
536128929Sjhb}
537128929Sjhb
538128929Sjhb
539128929Sjhb/*
540128929Sjhb * Allocate a piece of memory that can be efficiently mapped into
541128929Sjhb * bus device space based on the constraints lited in the dma tag.
542128929Sjhb * A dmamap to for use with dmamap_load is also allocated.
543128929Sjhb */
544128929Sjhbint
545128929Sjhbbus_dmamem_alloc(bus_dma_tag_t dmat, void** vaddr, int flags,
546128929Sjhb		 bus_dmamap_t *mapp)
547128929Sjhb{
548128929Sjhb	int mflags, len;
549128929Sjhb
550128929Sjhb	if (flags & BUS_DMA_NOWAIT)
551129009Snyan		mflags = M_NOWAIT;
552121985Sjhb	else
553121985Sjhb		mflags = M_WAITOK;
554121985Sjhb
555121985Sjhb	/* ARM non-snooping caches need a map for the VA cache sync structure */
556121985Sjhb
557121985Sjhb	*mapp = (bus_dmamap_t)malloc(sizeof(**mapp), M_DEVBUF,
558153136Sjhb					     M_NOWAIT | M_ZERO);
559163219Sjhb	if (*mapp == NULL) {
560163219Sjhb		CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d",
561163219Sjhb		    __func__, dmat, dmat->flags, ENOMEM);
562163219Sjhb		return (ENOMEM);
563163219Sjhb	}
564163219Sjhb
565163219Sjhb	STAILQ_INIT(&((*mapp)->slist));
566340016Sjhb
567340016Sjhb	if (dmat->segments == NULL) {
568121985Sjhb		dmat->segments = (bus_dma_segment_t *)malloc(
569269675Sroyger		    sizeof(bus_dma_segment_t) * dmat->nsegments, M_DEVBUF,
570121985Sjhb		    mflags);
571121985Sjhb		if (dmat->segments == NULL) {
572165302Skmacy			CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d",
573121985Sjhb			    __func__, dmat, dmat->flags, ENOMEM);
574121985Sjhb			free(*mapp, M_DEVBUF);
575121985Sjhb			*mapp = NULL;
576153242Sjhb			return (ENOMEM);
577153146Sjhb		}
578122898Sjhb	}
579122898Sjhb
580151658Sjhb	if (flags & BUS_DMA_ZERO)
581122898Sjhb		mflags |= M_ZERO;
582122898Sjhb
583153146Sjhb	/*
584122898Sjhb	 * XXX:
585122898Sjhb	 * (dmat->alignment < dmat->maxsize) is just a quick hack; the exact
586122898Sjhb	 * alignment guarantees of malloc need to be nailed down, and the
587122898Sjhb	 * code below should be rewritten to take that into account.
588122898Sjhb	 *
589122898Sjhb	 * In the meantime, we'll warn the user if malloc gets it wrong.
590122898Sjhb	 *
591177468Sjhb	 * allocate at least a cache line. This should help avoid cache
592122898Sjhb	 * corruption.
593122898Sjhb	 */
594122898Sjhb	len = max(dmat->maxsize, arm_dcache_align);
595177468Sjhb        if (len <= PAGE_SIZE &&
596129131Sjhb	   (dmat->alignment < len) &&
597122898Sjhb	   !_bus_dma_can_bounce(dmat->lowaddr, dmat->highaddr)) {
598122898Sjhb		*vaddr = malloc(len, M_DEVBUF, mflags);
599165302Skmacy	} else {
600121985Sjhb		/*
601121985Sjhb		 * XXX Use Contigmalloc until it is merged into this facility
602121985Sjhb		 *     and handles multi-seg allocations.  Nobody is doing
603121985Sjhb		 *     multi-seg allocations yet though.
604121985Sjhb		 * XXX Certain AGP hardware does.
605121985Sjhb		 */
606121985Sjhb		*vaddr = contigmalloc(len, M_DEVBUF, mflags,
607121985Sjhb		    0ul, dmat->lowaddr, dmat->alignment? dmat->alignment : 1ul,
608121985Sjhb		    dmat->boundary);
609121985Sjhb	}
610121985Sjhb	if (*vaddr == NULL) {
611121985Sjhb		CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d",
612121985Sjhb		    __func__, dmat, dmat->flags, ENOMEM);
613121985Sjhb		free(*mapp, M_DEVBUF);
614121985Sjhb		*mapp = NULL;
615121985Sjhb		return (ENOMEM);
616121985Sjhb	} else if ((uintptr_t)*vaddr & (dmat->alignment - 1)) {
617121985Sjhb		printf("bus_dmamem_alloc failed to align memory properly.\n");
618121985Sjhb	}
619121985Sjhb	dmat->map_count++;
620121985Sjhb
621121985Sjhb	CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d",
622121985Sjhb	    __func__, dmat, dmat->flags, 0);
623121985Sjhb	return (0);
624121985Sjhb}
625121985Sjhb
626121985Sjhb/*
627121985Sjhb * Free a piece of memory and it's allociated dmamap, that was allocated
628121985Sjhb * via bus_dmamem_alloc.  Make the same choice for free/contigfree.
629121985Sjhb */
630121985Sjhbvoid
631121985Sjhbbus_dmamem_free(bus_dma_tag_t dmat, void *vaddr, bus_dmamap_t map)
632121985Sjhb{
633121985Sjhb	int len;
634121985Sjhb
635121985Sjhb#ifdef mftnotyet
636121985Sjhb	pmap_change_attr((vm_offset_t)vaddr, dmat->maxsize, ARM_WRITE_BACK);
637121985Sjhb#endif
638121985Sjhb	len = max(dmat->maxsize, arm_dcache_align);
639127135Snjl        if (len <= PAGE_SIZE &&
640121985Sjhb	   (dmat->alignment < len) &&
641121985Sjhb	   !_bus_dma_can_bounce(dmat->lowaddr, dmat->highaddr))
642121985Sjhb		free(vaddr, M_DEVBUF);
643121985Sjhb	else {
644121985Sjhb		contigfree(vaddr, len, M_DEVBUF);
645121985Sjhb	}
646121985Sjhb	dmat->map_count--;
647121985Sjhb	free(map, M_DEVBUF);
648121985Sjhb	CTR3(KTR_BUSDMA, "%s: tag %p flags 0x%x", __func__, dmat, dmat->flags);
649121985Sjhb}
650121985Sjhb
651121985Sjhbstatic int
652121985Sjhb_bus_dmamap_count_pages(bus_dma_tag_t dmat, bus_dmamap_t map,
653121985Sjhb    void *buf, bus_size_t buflen, int flags)
654121985Sjhb{
655121985Sjhb	vm_offset_t vaddr;
656121985Sjhb	vm_offset_t vendaddr;
657121985Sjhb	bus_addr_t paddr;
658121985Sjhb
659121985Sjhb	if (map->pagesneeded == 0) {
660121985Sjhb		CTR5(KTR_BUSDMA, "lowaddr= %d, boundary= %d, alignment= %d"
661121985Sjhb		    " map= %p, pagesneeded= %d",
662121985Sjhb		    dmat->lowaddr, dmat->boundary, dmat->alignment,
663121985Sjhb		    map, map->pagesneeded);
664121985Sjhb		/*
665122051Snyan		 * Count the number of bounce pages
666121985Sjhb		 * needed in order to complete this transfer
667122051Snyan		 */
668121985Sjhb		vaddr = (vm_offset_t)buf;
669121985Sjhb		vendaddr = (vm_offset_t)buf + buflen;
670121985Sjhb
671121985Sjhb		while (vaddr < vendaddr) {
672121985Sjhb			if (__predict_true(map->pmap == pmap_kernel()))
673121985Sjhb				paddr = pmap_kextract(vaddr);
674121985Sjhb			else
675121985Sjhb				paddr = pmap_extract(map->pmap, vaddr);
676121985Sjhb			if (((dmat->flags & BUS_DMA_COULD_BOUNCE) != 0) &&
677121985Sjhb			    run_filter(dmat, paddr) != 0) {
678121985Sjhb				map->pagesneeded++;
679121985Sjhb			}
680121985Sjhb			vaddr += (PAGE_SIZE - ((vm_offset_t)vaddr & PAGE_MASK));
681121985Sjhb
682121985Sjhb		}
683121985Sjhb		CTR1(KTR_BUSDMA, "pagesneeded= %d", map->pagesneeded);
684	}
685
686	/* Reserve Necessary Bounce Pages */
687	if (map->pagesneeded != 0) {
688		mtx_lock(&bounce_lock);
689		if (flags & BUS_DMA_NOWAIT) {
690			if (reserve_bounce_pages(dmat, map, 0) != 0) {
691				map->pagesneeded = 0;
692				mtx_unlock(&bounce_lock);
693				return (ENOMEM);
694			}
695		} else {
696			if (reserve_bounce_pages(dmat, map, 1) != 0) {
697				/* Queue us for resources */
698				map->dmat = dmat;
699				map->buf = buf;
700				map->buflen = buflen;
701				STAILQ_INSERT_TAIL(&bounce_map_waitinglist,
702				    map, links);
703				mtx_unlock(&bounce_lock);
704				return (EINPROGRESS);
705			}
706		}
707		mtx_unlock(&bounce_lock);
708	}
709
710	return (0);
711}
712
713/*
714 * Utility function to load a linear buffer. lastaddrp holds state
715 * between invocations (for multiple-buffer loads).  segp contains
716 * the starting segment on entrace, and the ending segment on exit.
717 * first indicates if this is the first invocation of this function.
718 */
719static __inline int
720_bus_dmamap_load_buffer(bus_dma_tag_t dmat,
721			bus_dmamap_t map,
722			void *buf, bus_size_t buflen,
723			int flags,
724			bus_addr_t *lastaddrp,
725			bus_dma_segment_t *segs,
726			int *segp,
727			int first)
728{
729	bus_size_t sgsize;
730	bus_addr_t curaddr, lastaddr, baddr, bmask;
731	vm_offset_t vaddr;
732	struct sync_list *sl;
733	int seg, error;
734
735	if ((dmat->flags & BUS_DMA_COULD_BOUNCE) != 0) {
736		error = _bus_dmamap_count_pages(dmat, map, buf, buflen, flags);
737		if (error)
738			return (error);
739	}
740
741	sl = NULL;
742	vaddr = (vm_offset_t)buf;
743	lastaddr = *lastaddrp;
744	bmask = ~(dmat->boundary - 1);
745
746	for (seg = *segp; buflen > 0 ; ) {
747		/*
748		 * Get the physical address for this segment.
749		 */
750		if (__predict_true(map->pmap == pmap_kernel()))
751			curaddr = pmap_kextract(vaddr);
752		else
753			curaddr = pmap_extract(map->pmap, vaddr);
754
755		/*
756		 * Compute the segment size, and adjust counts.
757		 */
758		sgsize = PAGE_SIZE - ((u_long)curaddr & PAGE_MASK);
759		if (sgsize > dmat->maxsegsz)
760			sgsize = dmat->maxsegsz;
761		if (buflen < sgsize)
762			sgsize = buflen;
763
764		/*
765		 * Make sure we don't cross any boundaries.
766		 */
767		if (dmat->boundary > 0) {
768			baddr = (curaddr + dmat->boundary) & bmask;
769			if (sgsize > (baddr - curaddr))
770				sgsize = (baddr - curaddr);
771		}
772
773		if (((dmat->flags & BUS_DMA_COULD_BOUNCE) != 0) &&
774		    map->pagesneeded != 0 && run_filter(dmat, curaddr)) {
775			curaddr = add_bounce_page(dmat, map, vaddr, sgsize);
776		} else {
777			/* add_sync_list(dmat, map, vaddr, sgsize, cflag); */
778			sl = (struct sync_list *)malloc(sizeof(struct sync_list),
779						M_DEVBUF, M_NOWAIT | M_ZERO);
780			if (sl == NULL)
781				goto cleanup;
782			STAILQ_INSERT_TAIL(&(map->slist), sl, slinks);
783			sl->vaddr = vaddr;
784			sl->datacount = sgsize;
785			sl->busaddr = curaddr;
786		}
787
788
789		if (dmat->ranges) {
790			struct arm32_dma_range *dr;
791
792			dr = _bus_dma_inrange(dmat->ranges, dmat->_nranges,
793			    curaddr);
794			if (dr == NULL) {
795				_bus_dmamap_unload(dmat, map);
796				return (EINVAL);
797			}
798			/*
799			 * In a valid DMA range.  Translate the physical
800			 * memory address to an address in the DMA window.
801			 */
802			curaddr = (curaddr - dr->dr_sysbase) + dr->dr_busbase;
803		}
804
805		/*
806		 * Insert chunk into a segment, coalescing with
807		 * previous segment if possible.
808		 */
809		if (first) {
810			segs[seg].ds_addr = curaddr;
811			segs[seg].ds_len = sgsize;
812			first = 0;
813		} else {
814			if (curaddr == lastaddr &&
815			    (segs[seg].ds_len + sgsize) <= dmat->maxsegsz &&
816			    (dmat->boundary == 0 ||
817			     (segs[seg].ds_addr & bmask) == (curaddr & bmask)))
818				segs[seg].ds_len += sgsize;
819			else {
820				if (++seg >= dmat->nsegments)
821					break;
822				segs[seg].ds_addr = curaddr;
823				segs[seg].ds_len = sgsize;
824			}
825		}
826
827		lastaddr = curaddr + sgsize;
828		vaddr += sgsize;
829		buflen -= sgsize;
830	}
831
832	*segp = seg;
833	*lastaddrp = lastaddr;
834cleanup:
835	/*
836	 * Did we fit?
837	 */
838	if (buflen != 0) {
839		_bus_dmamap_unload(dmat, map);
840		return(EFBIG); /* XXX better return value here? */
841	}
842	return (0);
843}
844
845/*
846 * Map the buffer buf into bus space using the dmamap map.
847 */
848int
849bus_dmamap_load(bus_dma_tag_t dmat, bus_dmamap_t map, void *buf,
850		bus_size_t buflen, bus_dmamap_callback_t *callback,
851		void *callback_arg, int flags)
852{
853	bus_addr_t		lastaddr = 0;
854	int			error, nsegs = 0;
855
856	flags |= BUS_DMA_WAITOK;
857	map->callback = callback;
858	map->callback_arg = callback_arg;
859	map->pmap = kernel_pmap;
860
861	error = _bus_dmamap_load_buffer(dmat, map, buf, buflen, flags,
862		     &lastaddr, dmat->segments, &nsegs, 1);
863
864	CTR5(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d nsegs %d",
865	    __func__, dmat, dmat->flags, error, nsegs + 1);
866
867	if (error == EINPROGRESS) {
868		return (error);
869	}
870
871	if (error)
872		(*callback)(callback_arg, dmat->segments, 0, error);
873	else
874		(*callback)(callback_arg, dmat->segments, nsegs + 1, 0);
875
876	/*
877	 * Return ENOMEM to the caller so that it can pass it up the stack.
878	 * This error only happens when NOWAIT is set, so deferal is disabled.
879	 */
880	if (error == ENOMEM)
881		return (error);
882
883	return (0);
884}
885
886
887/*
888 * Like _bus_dmamap_load(), but for mbufs.
889 */
890static __inline int
891_bus_dmamap_load_mbuf_sg(bus_dma_tag_t dmat, bus_dmamap_t map,
892			struct mbuf *m0, bus_dma_segment_t *segs, int *nsegs,
893			int flags)
894{
895	int error;
896
897	M_ASSERTPKTHDR(m0);
898	map->pmap = kernel_pmap;
899
900	flags |= BUS_DMA_NOWAIT;
901	*nsegs = 0;
902	error = 0;
903	if (m0->m_pkthdr.len <= dmat->maxsize) {
904		int first = 1;
905		bus_addr_t lastaddr = 0;
906		struct mbuf *m;
907
908		for (m = m0; m != NULL && error == 0; m = m->m_next) {
909			if (m->m_len > 0) {
910				error = _bus_dmamap_load_buffer(dmat, map,
911						m->m_data, m->m_len,
912						flags, &lastaddr,
913						segs, nsegs, first);
914				first = 0;
915			}
916		}
917	} else {
918		error = EINVAL;
919	}
920
921	/* XXX FIXME: Having to increment nsegs is really annoying */
922	++*nsegs;
923	CTR5(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d nsegs %d",
924	    __func__, dmat, dmat->flags, error, *nsegs);
925	return (error);
926}
927
928int
929bus_dmamap_load_mbuf(bus_dma_tag_t dmat, bus_dmamap_t map,
930		     struct mbuf *m0,
931		     bus_dmamap_callback2_t *callback, void *callback_arg,
932		     int flags)
933{
934	int nsegs, error;
935
936	error = _bus_dmamap_load_mbuf_sg(dmat, map, m0, dmat->segments, &nsegs,
937		    flags);
938
939	if (error) {
940		/* force "no valid mappings" in callback */
941		(*callback)(callback_arg, dmat->segments, 0, 0, error);
942	} else {
943		(*callback)(callback_arg, dmat->segments,
944			    nsegs, m0->m_pkthdr.len, error);
945	}
946	CTR5(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d nsegs %d",
947	    __func__, dmat, dmat->flags, error, nsegs);
948
949	return (error);
950}
951
952int
953bus_dmamap_load_mbuf_sg(bus_dma_tag_t dmat, bus_dmamap_t map,
954			struct mbuf *m0, bus_dma_segment_t *segs, int *nsegs,
955			int flags)
956{
957	return (_bus_dmamap_load_mbuf_sg(dmat, map, m0, segs, nsegs, flags));
958}
959
960/*
961 * Like _bus_dmamap_load(), but for uios.
962 */
963int
964bus_dmamap_load_uio(bus_dma_tag_t dmat, bus_dmamap_t map,
965		    struct uio *uio,
966		    bus_dmamap_callback2_t *callback, void *callback_arg,
967		    int flags)
968{
969	bus_addr_t lastaddr;
970	int nsegs, error, first, i;
971	bus_size_t resid;
972	struct iovec *iov;
973
974	flags |= BUS_DMA_NOWAIT;
975	resid = uio->uio_resid;
976	iov = uio->uio_iov;
977
978	if (uio->uio_segflg == UIO_USERSPACE) {
979		KASSERT(uio->uio_td != NULL,
980			("bus_dmamap_load_uio: USERSPACE but no proc"));
981		map->pmap = vmspace_pmap(uio->uio_td->td_proc->p_vmspace);
982	} else
983		map->pmap = kernel_pmap;
984
985	nsegs = 0;
986	error = 0;
987	first = 1;
988	lastaddr = (bus_addr_t) 0;
989	for (i = 0; i < uio->uio_iovcnt && resid != 0 && !error; i++) {
990		/*
991		 * Now at the first iovec to load.  Load each iovec
992		 * until we have exhausted the residual count.
993		 */
994		bus_size_t minlen =
995			resid < iov[i].iov_len ? resid : iov[i].iov_len;
996		caddr_t addr = (caddr_t) iov[i].iov_base;
997
998		if (minlen > 0) {
999			error = _bus_dmamap_load_buffer(dmat, map,
1000					addr, minlen, flags, &lastaddr,
1001					dmat->segments, &nsegs, first);
1002			first = 0;
1003			resid -= minlen;
1004		}
1005	}
1006
1007	if (error) {
1008		/* force "no valid mappings" in callback */
1009		(*callback)(callback_arg, dmat->segments, 0, 0, error);
1010	} else {
1011		(*callback)(callback_arg, dmat->segments,
1012			    nsegs+1, uio->uio_resid, error);
1013	}
1014	CTR5(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d nsegs %d",
1015	    __func__, dmat, dmat->flags, error, nsegs + 1);
1016	return (error);
1017}
1018
1019/*
1020 * Release the mapping held by map.
1021 */
1022void
1023_bus_dmamap_unload(bus_dma_tag_t dmat, bus_dmamap_t map)
1024{
1025	struct bounce_page *bpage;
1026	struct bounce_zone *bz;
1027	struct sync_list *sl;
1028
1029        while ((sl = STAILQ_FIRST(&map->slist)) != NULL) {
1030                STAILQ_REMOVE_HEAD(&map->slist, slinks);
1031                free(sl, M_DEVBUF);
1032        }
1033
1034	if ((bz = dmat->bounce_zone) != NULL) {
1035		while ((bpage = STAILQ_FIRST(&map->bpages)) != NULL) {
1036			STAILQ_REMOVE_HEAD(&map->bpages, links);
1037			free_bounce_page(dmat, bpage);
1038		}
1039
1040		bz = dmat->bounce_zone;
1041		bz->free_bpages += map->pagesreserved;
1042		bz->reserved_bpages -= map->pagesreserved;
1043		map->pagesreserved = 0;
1044		map->pagesneeded = 0;
1045	}
1046}
1047
1048#ifdef notyetbounceuser
1049	/* If busdma uses user pages, then the interrupt handler could
1050	 * be use the kernel vm mapping. Both bounce pages and sync list
1051	 * do not cross page boundaries.
1052	 * Below is a rough sequence that a person would do to fix the
1053	 * user page reference in the kernel vmspace. This would be
1054	 * done in the dma post routine.
1055	 */
1056void
1057_bus_dmamap_fix_user(vm_offset_t buf, bus_size_t len,
1058			pmap_t pmap, int op)
1059{
1060	bus_size_t sgsize;
1061	bus_addr_t curaddr;
1062	vm_offset_t va;
1063
1064		/* each synclist entry is contained within a single page.
1065		 *
1066		 * this would be needed if BUS_DMASYNC_POSTxxxx was implemented
1067		*/
1068	curaddr = pmap_extract(pmap, buf);
1069	va = pmap_dma_map(curaddr);
1070	switch (op) {
1071	case SYNC_USER_INV:
1072		cpu_dcache_wb_range(va, sgsize);
1073		break;
1074
1075	case SYNC_USER_COPYTO:
1076		bcopy((void *)va, (void *)bounce, sgsize);
1077		break;
1078
1079	case SYNC_USER_COPYFROM:
1080		bcopy((void *) bounce, (void *)va, sgsize);
1081		break;
1082
1083	default:
1084		break;
1085	}
1086
1087	pmap_dma_unmap(va);
1088}
1089#endif
1090
1091#ifdef ARM_L2_PIPT
1092#define l2cache_wb_range(va, pa, size) cpu_l2cache_wb_range(pa, size)
1093#define l2cache_wbinv_range(va, pa, size) cpu_l2cache_wbinv_range(pa, size)
1094#define l2cache_inv_range(va, pa, size) cpu_l2cache_inv_range(pa, size)
1095#else
1096#define l2cache_wb_range(va, pa, size) cpu_l2cache_wb_range(va, size)
1097#define l2cache_wbinv_range(va, pa, size) cpu_l2cache_wbinv_range(va, size)
1098#define l2cache_inv_range(va, pa, size) cpu_l2cache_inv_range(va, size)
1099#endif
1100
1101void
1102_bus_dmamap_sync(bus_dma_tag_t dmat, bus_dmamap_t map, bus_dmasync_op_t op)
1103{
1104	struct bounce_page *bpage;
1105	struct sync_list *sl;
1106	bus_size_t len, unalign;
1107	vm_offset_t buf, ebuf;
1108#ifdef FIX_DMAP_BUS_DMASYNC_POSTREAD
1109	vm_offset_t bbuf;
1110	char _tmp_cl[arm_dcache_align], _tmp_clend[arm_dcache_align];
1111#endif
1112	int listcount = 0;
1113
1114		/* if buffer was from user space, it it possible that this
1115		 * is not the same vm map. The fix is to map each page in
1116		 * the buffer into the current address space (KVM) and then
1117		 * do the bounce copy or sync list cache operation.
1118		 *
1119		 * The sync list entries are already broken into
1120		 * their respective physical pages.
1121		 */
1122	if (!pmap_dmap_iscurrent(map->pmap))
1123		printf("_bus_dmamap_sync: wrong user map: %p %x\n", map->pmap, op);
1124
1125	if ((bpage = STAILQ_FIRST(&map->bpages)) != NULL) {
1126
1127		/* Handle data bouncing. */
1128		CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x op 0x%x "
1129		    "performing bounce", __func__, dmat, dmat->flags, op);
1130
1131		if (op & BUS_DMASYNC_PREWRITE) {
1132			while (bpage != NULL) {
1133				bcopy((void *)bpage->datavaddr,
1134				      (void *)bpage->vaddr,
1135				      bpage->datacount);
1136				cpu_dcache_wb_range((vm_offset_t)bpage->vaddr,
1137					bpage->datacount);
1138				l2cache_wb_range((vm_offset_t)bpage->vaddr,
1139				    (vm_offset_t)bpage->busaddr,
1140				    bpage->datacount);
1141				bpage = STAILQ_NEXT(bpage, links);
1142			}
1143			dmat->bounce_zone->total_bounced++;
1144		}
1145
1146		if (op & BUS_DMASYNC_POSTREAD) {
1147			if (!pmap_dmap_iscurrent(map->pmap))
1148			    panic("_bus_dmamap_sync: wrong user map. apply fix");
1149
1150			cpu_dcache_inv_range((vm_offset_t)bpage->vaddr,
1151					bpage->datacount);
1152			l2cache_inv_range((vm_offset_t)bpage->vaddr,
1153			    (vm_offset_t)bpage->busaddr,
1154			    bpage->datacount);
1155			while (bpage != NULL) {
1156				vm_offset_t startv;
1157				vm_paddr_t startp;
1158				int len;
1159
1160				startv = bpage->vaddr &~ arm_dcache_align_mask;
1161				startp = bpage->busaddr &~ arm_dcache_align_mask;
1162				len = bpage->datacount;
1163
1164				if (startv != bpage->vaddr)
1165					len += bpage->vaddr & arm_dcache_align_mask;
1166				if (len & arm_dcache_align_mask)
1167					len = (len -
1168					    (len & arm_dcache_align_mask)) +
1169					    arm_dcache_align;
1170				cpu_dcache_inv_range(startv, len);
1171				l2cache_inv_range(startv, startp, len);
1172				bcopy((void *)bpage->vaddr,
1173				      (void *)bpage->datavaddr,
1174				      bpage->datacount);
1175				bpage = STAILQ_NEXT(bpage, links);
1176			}
1177			dmat->bounce_zone->total_bounced++;
1178		}
1179	}
1180
1181	sl = STAILQ_FIRST(&map->slist);
1182	while (sl) {
1183		listcount++;
1184		sl = STAILQ_NEXT(sl, slinks);
1185	}
1186	if ((sl = STAILQ_FIRST(&map->slist)) != NULL) {
1187		/* ARM caches are not self-snooping for dma */
1188
1189		CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x op 0x%x "
1190		    "performing sync", __func__, dmat, dmat->flags, op);
1191
1192		switch (op) {
1193		case BUS_DMASYNC_PREWRITE:
1194			while (sl != NULL) {
1195			    cpu_dcache_wb_range(sl->vaddr, sl->datacount);
1196			    l2cache_wb_range(sl->vaddr, sl->busaddr,
1197				sl->datacount);
1198			    sl = STAILQ_NEXT(sl, slinks);
1199			}
1200			break;
1201
1202		case BUS_DMASYNC_PREREAD:
1203			while (sl != NULL) {
1204					/* write back the unaligned portions */
1205				vm_paddr_t physaddr = sl->busaddr, ephysaddr;
1206				buf = sl->vaddr;
1207				len = sl->datacount;
1208				ebuf = buf + len;	/* end of buffer */
1209				ephysaddr = physaddr + len;
1210				unalign = buf & arm_dcache_align_mask;
1211				if (unalign) {
1212						/* wbinv leading fragment */
1213					buf &= ~arm_dcache_align_mask;
1214					physaddr &= ~arm_dcache_align_mask;
1215					cpu_dcache_wbinv_range(buf,
1216							arm_dcache_align);
1217					l2cache_wbinv_range(buf, physaddr,
1218					    arm_dcache_align);
1219					buf += arm_dcache_align;
1220					physaddr += arm_dcache_align;
1221					/* number byte in buffer wbinv */
1222					unalign = arm_dcache_align - unalign;
1223					if (len > unalign)
1224						len -= unalign;
1225					else
1226						len = 0;
1227				}
1228				unalign = ebuf & arm_dcache_align_mask;
1229				if (ebuf > buf && unalign) {
1230						/* wbinv trailing fragment */
1231					len -= unalign;
1232					ebuf -= unalign;
1233					ephysaddr -= unalign;
1234					cpu_dcache_wbinv_range(ebuf,
1235							arm_dcache_align);
1236					l2cache_wbinv_range(ebuf, ephysaddr,
1237					    arm_dcache_align);
1238				}
1239				if (ebuf > buf) {
1240					cpu_dcache_inv_range(buf, len);
1241					l2cache_inv_range(buf, physaddr, len);
1242				}
1243				sl = STAILQ_NEXT(sl, slinks);
1244			}
1245			break;
1246
1247		case BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD:
1248			while (sl != NULL) {
1249				cpu_dcache_wbinv_range(sl->vaddr, sl->datacount);
1250				l2cache_wbinv_range(sl->vaddr,
1251				    sl->busaddr, sl->datacount);
1252				sl = STAILQ_NEXT(sl, slinks);
1253			}
1254			break;
1255
1256#ifdef FIX_DMAP_BUS_DMASYNC_POSTREAD
1257		case BUS_DMASYNC_POSTREAD:
1258			if (!pmap_dmap_iscurrent(map->pmap))
1259			     panic("_bus_dmamap_sync: wrong user map. apply fix");
1260			while (sl != NULL) {
1261					/* write back the unaligned portions */
1262				vm_paddr_t physaddr;
1263				buf = sl->vaddr;
1264				len = sl->datacount;
1265				physaddr = sl->busaddr;
1266				bbuf = buf & ~arm_dcache_align_mask;
1267				ebuf = buf + len;
1268				physaddr = physaddr & ~arm_dcache_align_mask;
1269				unalign = buf & arm_dcache_align_mask;
1270				if (unalign) {
1271					memcpy(_tmp_cl, (void *)bbuf, unalign);
1272					len += unalign; /* inv entire cache line */
1273				}
1274				unalign = ebuf & arm_dcache_align_mask;
1275				if (unalign) {
1276					unalign = arm_dcache_align - unalign;
1277					memcpy(_tmp_clend, (void *)ebuf, unalign);
1278					len += unalign; /* inv entire cache line */
1279				}
1280					/* inv are cache length aligned */
1281				cpu_dcache_inv_range(bbuf, len);
1282				l2cache_inv_range(bbuf, physaddr, len);
1283
1284				unalign = (vm_offset_t)buf & arm_dcache_align_mask;
1285				if (unalign) {
1286					memcpy((void *)bbuf, _tmp_cl, unalign);
1287				}
1288				unalign = ebuf & arm_dcache_align_mask;
1289				if (unalign) {
1290					unalign = arm_dcache_align - unalign;
1291					memcpy((void *)ebuf, _tmp_clend, unalign);
1292				}
1293				sl = STAILQ_NEXT(sl, slinks);
1294			}
1295				break;
1296#endif /* FIX_DMAP_BUS_DMASYNC_POSTREAD */
1297
1298		default:
1299			break;
1300		}
1301	}
1302}
1303
1304static void
1305init_bounce_pages(void *dummy __unused)
1306{
1307
1308	total_bpages = 0;
1309	STAILQ_INIT(&bounce_zone_list);
1310	STAILQ_INIT(&bounce_map_waitinglist);
1311	STAILQ_INIT(&bounce_map_callbacklist);
1312	mtx_init(&bounce_lock, "bounce pages lock", NULL, MTX_DEF);
1313}
1314SYSINIT(bpages, SI_SUB_LOCK, SI_ORDER_ANY, init_bounce_pages, NULL);
1315
1316static struct sysctl_ctx_list *
1317busdma_sysctl_tree(struct bounce_zone *bz)
1318{
1319	return (&bz->sysctl_tree);
1320}
1321
1322static struct sysctl_oid *
1323busdma_sysctl_tree_top(struct bounce_zone *bz)
1324{
1325	return (bz->sysctl_tree_top);
1326}
1327
1328static int
1329alloc_bounce_zone(bus_dma_tag_t dmat)
1330{
1331	struct bounce_zone *bz;
1332
1333	/* Check to see if we already have a suitable zone */
1334	STAILQ_FOREACH(bz, &bounce_zone_list, links) {
1335		if ((dmat->alignment <= bz->alignment)
1336		 && (dmat->lowaddr >= bz->lowaddr)) {
1337			dmat->bounce_zone = bz;
1338			return (0);
1339		}
1340	}
1341
1342	if ((bz = (struct bounce_zone *)malloc(sizeof(*bz), M_DEVBUF,
1343	    M_NOWAIT | M_ZERO)) == NULL)
1344		return (ENOMEM);
1345
1346	STAILQ_INIT(&bz->bounce_page_list);
1347	bz->free_bpages = 0;
1348	bz->reserved_bpages = 0;
1349	bz->active_bpages = 0;
1350	bz->lowaddr = dmat->lowaddr;
1351	bz->alignment = MAX(dmat->alignment, PAGE_SIZE);
1352	bz->map_count = 0;
1353	snprintf(bz->zoneid, 8, "zone%d", busdma_zonecount);
1354	busdma_zonecount++;
1355	snprintf(bz->lowaddrid, 18, "%#jx", (uintmax_t)bz->lowaddr);
1356	STAILQ_INSERT_TAIL(&bounce_zone_list, bz, links);
1357	dmat->bounce_zone = bz;
1358
1359	sysctl_ctx_init(&bz->sysctl_tree);
1360	bz->sysctl_tree_top = SYSCTL_ADD_NODE(&bz->sysctl_tree,
1361	    SYSCTL_STATIC_CHILDREN(_hw_busdma), OID_AUTO, bz->zoneid,
1362	    CTLFLAG_RD, 0, "");
1363	if (bz->sysctl_tree_top == NULL) {
1364		sysctl_ctx_free(&bz->sysctl_tree);
1365		return (0);	/* XXX error code? */
1366	}
1367
1368	SYSCTL_ADD_INT(busdma_sysctl_tree(bz),
1369	    SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
1370	    "total_bpages", CTLFLAG_RD, &bz->total_bpages, 0,
1371	    "Total bounce pages");
1372	SYSCTL_ADD_INT(busdma_sysctl_tree(bz),
1373	    SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
1374	    "free_bpages", CTLFLAG_RD, &bz->free_bpages, 0,
1375	    "Free bounce pages");
1376	SYSCTL_ADD_INT(busdma_sysctl_tree(bz),
1377	    SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
1378	    "reserved_bpages", CTLFLAG_RD, &bz->reserved_bpages, 0,
1379	    "Reserved bounce pages");
1380	SYSCTL_ADD_INT(busdma_sysctl_tree(bz),
1381	    SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
1382	    "active_bpages", CTLFLAG_RD, &bz->active_bpages, 0,
1383	    "Active bounce pages");
1384	SYSCTL_ADD_INT(busdma_sysctl_tree(bz),
1385	    SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
1386	    "total_bounced", CTLFLAG_RD, &bz->total_bounced, 0,
1387	    "Total bounce requests");
1388	SYSCTL_ADD_INT(busdma_sysctl_tree(bz),
1389	    SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
1390	    "total_deferred", CTLFLAG_RD, &bz->total_deferred, 0,
1391	    "Total bounce requests that were deferred");
1392	SYSCTL_ADD_STRING(busdma_sysctl_tree(bz),
1393	    SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
1394	    "lowaddr", CTLFLAG_RD, bz->lowaddrid, 0, "");
1395	SYSCTL_ADD_INT(busdma_sysctl_tree(bz),
1396	    SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
1397	    "alignment", CTLFLAG_RD, &bz->alignment, 0, "");
1398
1399	return (0);
1400}
1401
1402static int
1403alloc_bounce_pages(bus_dma_tag_t dmat, u_int numpages)
1404{
1405	struct bounce_zone *bz;
1406	int count;
1407
1408	bz = dmat->bounce_zone;
1409	count = 0;
1410	while (numpages > 0) {
1411		struct bounce_page *bpage;
1412
1413		bpage = (struct bounce_page *)malloc(sizeof(*bpage), M_DEVBUF,
1414						     M_NOWAIT | M_ZERO);
1415
1416		if (bpage == NULL)
1417			break;
1418		bpage->vaddr = (vm_offset_t)contigmalloc(PAGE_SIZE, M_DEVBUF,
1419							 M_NOWAIT, 0ul,
1420							 bz->lowaddr,
1421							 PAGE_SIZE,
1422							 0);
1423		if (bpage->vaddr == 0) {
1424			free(bpage, M_DEVBUF);
1425			break;
1426		}
1427		bpage->busaddr = pmap_kextract(bpage->vaddr);
1428		mtx_lock(&bounce_lock);
1429		STAILQ_INSERT_TAIL(&bz->bounce_page_list, bpage, links);
1430		total_bpages++;
1431		bz->total_bpages++;
1432		bz->free_bpages++;
1433		mtx_unlock(&bounce_lock);
1434		count++;
1435		numpages--;
1436	}
1437	return (count);
1438}
1439
1440static int
1441reserve_bounce_pages(bus_dma_tag_t dmat, bus_dmamap_t map, int commit)
1442{
1443	struct bounce_zone *bz;
1444	int pages;
1445
1446	mtx_assert(&bounce_lock, MA_OWNED);
1447	bz = dmat->bounce_zone;
1448	pages = MIN(bz->free_bpages, map->pagesneeded - map->pagesreserved);
1449	if (commit == 0 && map->pagesneeded > (map->pagesreserved + pages))
1450		return (map->pagesneeded - (map->pagesreserved + pages));
1451	bz->free_bpages -= pages;
1452	bz->reserved_bpages += pages;
1453	map->pagesreserved += pages;
1454	pages = map->pagesneeded - map->pagesreserved;
1455
1456	return (pages);
1457}
1458
1459static bus_addr_t
1460add_bounce_page(bus_dma_tag_t dmat, bus_dmamap_t map, vm_offset_t vaddr,
1461		bus_size_t size)
1462{
1463	struct bounce_zone *bz;
1464	struct bounce_page *bpage;
1465
1466	KASSERT(dmat->bounce_zone != NULL, ("no bounce zone in dma tag"));
1467	KASSERT(map != NULL,
1468	    ("add_bounce_page: bad map %p", map));
1469
1470	bz = dmat->bounce_zone;
1471	if (map->pagesneeded == 0)
1472		panic("add_bounce_page: map doesn't need any pages");
1473	map->pagesneeded--;
1474
1475	if (map->pagesreserved == 0)
1476		panic("add_bounce_page: map doesn't need any pages");
1477	map->pagesreserved--;
1478
1479	mtx_lock(&bounce_lock);
1480	bpage = STAILQ_FIRST(&bz->bounce_page_list);
1481	if (bpage == NULL)
1482		panic("add_bounce_page: free page list is empty");
1483
1484	STAILQ_REMOVE_HEAD(&bz->bounce_page_list, links);
1485	bz->reserved_bpages--;
1486	bz->active_bpages++;
1487	mtx_unlock(&bounce_lock);
1488
1489	if (dmat->flags & BUS_DMA_KEEP_PG_OFFSET) {
1490		/* Page offset needs to be preserved. */
1491		bpage->vaddr |= vaddr & PAGE_MASK;
1492		bpage->busaddr |= vaddr & PAGE_MASK;
1493	}
1494	bpage->datavaddr = vaddr;
1495	bpage->datacount = size;
1496	STAILQ_INSERT_TAIL(&(map->bpages), bpage, links);
1497	return (bpage->busaddr);
1498}
1499
1500static void
1501free_bounce_page(bus_dma_tag_t dmat, struct bounce_page *bpage)
1502{
1503	struct bus_dmamap *map;
1504	struct bounce_zone *bz;
1505
1506	bz = dmat->bounce_zone;
1507	bpage->datavaddr = 0;
1508	bpage->datacount = 0;
1509	if (dmat->flags & BUS_DMA_KEEP_PG_OFFSET) {
1510		/*
1511		 * Reset the bounce page to start at offset 0.  Other uses
1512		 * of this bounce page may need to store a full page of
1513		 * data and/or assume it starts on a page boundary.
1514		 */
1515		bpage->vaddr &= ~PAGE_MASK;
1516		bpage->busaddr &= ~PAGE_MASK;
1517	}
1518
1519	mtx_lock(&bounce_lock);
1520	STAILQ_INSERT_HEAD(&bz->bounce_page_list, bpage, links);
1521	bz->free_bpages++;
1522	bz->active_bpages--;
1523	if ((map = STAILQ_FIRST(&bounce_map_waitinglist)) != NULL) {
1524		if (reserve_bounce_pages(map->dmat, map, 1) == 0) {
1525			STAILQ_REMOVE_HEAD(&bounce_map_waitinglist, links);
1526			STAILQ_INSERT_TAIL(&bounce_map_callbacklist,
1527					   map, links);
1528			busdma_swi_pending = 1;
1529			bz->total_deferred++;
1530			swi_sched(vm_ih, 0);
1531		}
1532	}
1533	mtx_unlock(&bounce_lock);
1534}
1535
1536void
1537busdma_swi(void)
1538{
1539	bus_dma_tag_t dmat;
1540	struct bus_dmamap *map;
1541
1542	mtx_lock(&bounce_lock);
1543	while ((map = STAILQ_FIRST(&bounce_map_callbacklist)) != NULL) {
1544		STAILQ_REMOVE_HEAD(&bounce_map_callbacklist, links);
1545		mtx_unlock(&bounce_lock);
1546		dmat = map->dmat;
1547		(dmat->lockfunc)(dmat->lockfuncarg, BUS_DMA_LOCK);
1548		bus_dmamap_load(map->dmat, map, map->buf, map->buflen,
1549				map->callback, map->callback_arg, /*flags*/0);
1550		(dmat->lockfunc)(dmat->lockfuncarg, BUS_DMA_UNLOCK);
1551		mtx_lock(&bounce_lock);
1552	}
1553	mtx_unlock(&bounce_lock);
1554}
1555