busdma_machdep.c revision 259510
1/*-
2 * Copyright (c) 1997 Justin T. Gibbs.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions, and the following disclaimer,
10 *    without modification, immediately at the beginning of the file.
11 * 2. The name of the author may not be used to endorse or promote products
12 *    derived from this software without specific prior written permission.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
18 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: stable/10/sys/ia64/ia64/busdma_machdep.c 259510 2013-12-17 13:38:21Z kib $");
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/kernel.h>
33#include <sys/malloc.h>
34#include <sys/lock.h>
35#include <sys/mutex.h>
36#include <sys/bus.h>
37#include <sys/interrupt.h>
38#include <sys/memdesc.h>
39#include <sys/proc.h>
40#include <sys/sysctl.h>
41#include <sys/uio.h>
42
43#include <vm/vm.h>
44#include <vm/vm_page.h>
45#include <vm/vm_map.h>
46
47#include <machine/atomic.h>
48#include <machine/bus.h>
49#include <machine/md_var.h>
50
51#define	MAX_BPAGES	1024
52
53struct bus_dma_tag {
54	bus_dma_tag_t	parent;
55	bus_size_t	alignment;
56	bus_addr_t	boundary;
57	bus_addr_t	lowaddr;
58	bus_addr_t	highaddr;
59	bus_dma_filter_t *filter;
60	void		*filterarg;
61	bus_size_t	maxsize;
62	u_int		nsegments;
63	bus_size_t	maxsegsz;
64	int		flags;
65	int		ref_count;
66	int		map_count;
67	bus_dma_lock_t	*lockfunc;
68	void		*lockfuncarg;
69	bus_dma_segment_t *segments;
70};
71
72struct bounce_page {
73	vm_offset_t	vaddr;		/* kva of bounce buffer */
74	bus_addr_t	busaddr;	/* Physical address */
75	vm_offset_t	datavaddr;	/* kva of client data */
76	bus_addr_t	dataaddr;	/* client physical address */
77	bus_size_t	datacount;	/* client data count */
78	STAILQ_ENTRY(bounce_page) links;
79};
80
81u_int busdma_swi_pending;
82
83static struct mtx bounce_lock;
84static STAILQ_HEAD(bp_list, bounce_page) bounce_page_list;
85static int free_bpages;
86static int reserved_bpages;
87static int active_bpages;
88static int total_bpages;
89static int total_bounced;
90static int total_deferred;
91
92static SYSCTL_NODE(_hw, OID_AUTO, busdma, CTLFLAG_RD, 0, "Busdma parameters");
93SYSCTL_INT(_hw_busdma, OID_AUTO, free_bpages, CTLFLAG_RD, &free_bpages, 0,
94    "Free bounce pages");
95SYSCTL_INT(_hw_busdma, OID_AUTO, reserved_bpages, CTLFLAG_RD, &reserved_bpages,
96    0, "Reserved bounce pages");
97SYSCTL_INT(_hw_busdma, OID_AUTO, active_bpages, CTLFLAG_RD, &active_bpages, 0,
98    "Active bounce pages");
99SYSCTL_INT(_hw_busdma, OID_AUTO, total_bpages, CTLFLAG_RD, &total_bpages, 0,
100    "Total bounce pages");
101SYSCTL_INT(_hw_busdma, OID_AUTO, total_bounced, CTLFLAG_RD, &total_bounced, 0,
102    "Total bounce requests");
103SYSCTL_INT(_hw_busdma, OID_AUTO, total_deferred, CTLFLAG_RD, &total_deferred,
104    0, "Total bounce requests that were deferred");
105
106struct bus_dmamap {
107	struct bp_list	bpages;
108	int		pagesneeded;
109	int		pagesreserved;
110	bus_dma_tag_t	dmat;
111	struct memdesc	mem;
112	bus_dmamap_callback_t *callback;
113	void		*callback_arg;
114	STAILQ_ENTRY(bus_dmamap) links;
115};
116
117static STAILQ_HEAD(, bus_dmamap) bounce_map_waitinglist;
118static STAILQ_HEAD(, bus_dmamap) bounce_map_callbacklist;
119static struct bus_dmamap nobounce_dmamap;
120
121static void init_bounce_pages(void *dummy);
122static int alloc_bounce_pages(bus_dma_tag_t dmat, u_int numpages);
123static int reserve_bounce_pages(bus_dma_tag_t dmat, bus_dmamap_t map,
124    int commit);
125static bus_addr_t add_bounce_page(bus_dma_tag_t dmat, bus_dmamap_t map,
126    vm_offset_t vaddr, bus_addr_t addr, bus_size_t size);
127static void free_bounce_page(bus_dma_tag_t dmat, struct bounce_page *bpage);
128static __inline int run_filter(bus_dma_tag_t dmat, bus_addr_t paddr,
129    bus_size_t len);
130
131/*
132 * Return true if a match is made.
133 *
134 * To find a match walk the chain of bus_dma_tag_t's looking for 'paddr'.
135 *
136 * If paddr is within the bounds of the dma tag then call the filter callback
137 * to check for a match, if there is no filter callback then assume a match.
138 */
139static __inline int
140run_filter(bus_dma_tag_t dmat, bus_addr_t paddr, bus_size_t len)
141{
142	bus_addr_t bndy;
143	int retval;
144
145	retval = 0;
146	bndy = dmat->boundary;
147	do {
148		if (((paddr > dmat->lowaddr && paddr <= dmat->highaddr) ||
149		    (paddr & (dmat->alignment - 1)) != 0 ||
150		    (paddr & bndy) != ((paddr + len) & bndy)) &&
151		    (dmat->filter == NULL ||
152		    (*dmat->filter)(dmat->filterarg, paddr) != 0))
153			retval = 1;
154		dmat = dmat->parent;
155	} while (retval == 0 && dmat != NULL);
156	return (retval);
157}
158
159/*
160 * Convenience function for manipulating driver locks from busdma (during
161 * busdma_swi, for example).  Drivers that don't provide their own locks
162 * should specify &Giant to dmat->lockfuncarg.  Drivers that use their own
163 * non-mutex locking scheme don't have to use this at all.
164 */
165void
166busdma_lock_mutex(void *arg, bus_dma_lock_op_t op)
167{
168	struct mtx *dmtx;
169
170	dmtx = (struct mtx *)arg;
171	switch (op) {
172	case BUS_DMA_LOCK:
173		mtx_lock(dmtx);
174		break;
175	case BUS_DMA_UNLOCK:
176		mtx_unlock(dmtx);
177		break;
178	default:
179		panic("Unknown operation 0x%x for busdma_lock_mutex!", op);
180	}
181}
182
183/*
184 * dflt_lock should never get called.  It gets put into the dma tag when
185 * lockfunc == NULL, which is only valid if the maps that are associated
186 * with the tag are meant to never be defered.
187 * XXX Should have a way to identify which driver is responsible here.
188 */
189static void
190dflt_lock(void *arg, bus_dma_lock_op_t op)
191{
192	panic("driver error: busdma dflt_lock called");
193}
194
195#define BUS_DMA_MIN_ALLOC_COMP BUS_DMA_BUS4
196
197/*
198 * Allocate a device specific dma_tag.
199 */
200int
201bus_dma_tag_create(bus_dma_tag_t parent, bus_size_t alignment,
202    bus_addr_t boundary, bus_addr_t lowaddr, bus_addr_t highaddr,
203    bus_dma_filter_t *filter, void *filterarg, bus_size_t maxsize,
204    int nsegments, bus_size_t maxsegsz, int flags, bus_dma_lock_t *lockfunc,
205    void *lockfuncarg, bus_dma_tag_t *dmat)
206{
207	bus_dma_tag_t newtag;
208	int error = 0;
209
210	/* Basic sanity checking */
211	if (boundary != 0 && boundary < maxsegsz)
212		maxsegsz = boundary;
213
214	/* Return a NULL tag on failure */
215	*dmat = NULL;
216
217	newtag = (bus_dma_tag_t)malloc(sizeof(*newtag), M_DEVBUF, M_NOWAIT);
218	if (newtag == NULL)
219		return (ENOMEM);
220
221	newtag->parent = parent;
222	newtag->alignment = alignment;
223	newtag->boundary = boundary;
224	newtag->lowaddr = trunc_page(lowaddr) + (PAGE_SIZE - 1);
225	newtag->highaddr = trunc_page(highaddr) + (PAGE_SIZE - 1);
226	newtag->filter = filter;
227	newtag->filterarg = filterarg;
228	newtag->maxsize = maxsize;
229	newtag->nsegments = nsegments;
230	newtag->maxsegsz = maxsegsz;
231	newtag->flags = flags;
232	newtag->ref_count = 1; /* Count ourself */
233	newtag->map_count = 0;
234	if (lockfunc != NULL) {
235		newtag->lockfunc = lockfunc;
236		newtag->lockfuncarg = lockfuncarg;
237	} else {
238		newtag->lockfunc = dflt_lock;
239		newtag->lockfuncarg = NULL;
240	}
241	newtag->segments = NULL;
242
243	/* Take into account any restrictions imposed by our parent tag */
244	if (parent != NULL) {
245		newtag->lowaddr = MIN(parent->lowaddr, newtag->lowaddr);
246		newtag->highaddr = MAX(parent->highaddr, newtag->highaddr);
247		if (newtag->boundary == 0)
248			newtag->boundary = parent->boundary;
249		else if (parent->boundary != 0)
250			newtag->boundary = MIN(parent->boundary,
251			    newtag->boundary);
252		if (newtag->filter == NULL) {
253			/*
254			 * Short circuit looking at our parent directly
255			 * since we have encapsulated all of its information
256			 */
257			newtag->filter = parent->filter;
258			newtag->filterarg = parent->filterarg;
259			newtag->parent = parent->parent;
260		}
261		if (newtag->parent != NULL)
262			atomic_add_int(&parent->ref_count, 1);
263	}
264
265	if (newtag->lowaddr < paddr_max && (flags & BUS_DMA_ALLOCNOW) != 0) {
266		/* Must bounce */
267
268		if (ptoa(total_bpages) < maxsize) {
269			int pages;
270
271			pages = atop(maxsize) - total_bpages;
272
273			/* Add pages to our bounce pool */
274			if (alloc_bounce_pages(newtag, pages) < pages)
275				error = ENOMEM;
276		}
277		/* Performed initial allocation */
278		newtag->flags |= BUS_DMA_MIN_ALLOC_COMP;
279	}
280
281	if (error != 0) {
282		free(newtag, M_DEVBUF);
283	} else {
284		*dmat = newtag;
285	}
286	return (error);
287}
288
289int
290bus_dma_tag_destroy(bus_dma_tag_t dmat)
291{
292	if (dmat != NULL) {
293
294		if (dmat->map_count != 0)
295			return (EBUSY);
296
297		while (dmat != NULL) {
298			bus_dma_tag_t parent;
299
300			parent = dmat->parent;
301			atomic_subtract_int(&dmat->ref_count, 1);
302			if (dmat->ref_count == 0) {
303				if (dmat->segments != NULL)
304					free(dmat->segments, M_DEVBUF);
305				free(dmat, M_DEVBUF);
306				/*
307				 * Last reference count, so
308				 * release our reference
309				 * count on our parent.
310				 */
311				dmat = parent;
312			} else
313				dmat = NULL;
314		}
315	}
316	return (0);
317}
318
319/*
320 * Allocate a handle for mapping from kva/uva/physical
321 * address space into bus device space.
322 */
323int
324bus_dmamap_create(bus_dma_tag_t dmat, int flags, bus_dmamap_t *mapp)
325{
326	int error;
327
328	error = 0;
329
330	if (dmat->segments == NULL) {
331		dmat->segments = (bus_dma_segment_t *)malloc(
332		    sizeof(bus_dma_segment_t) * dmat->nsegments, M_DEVBUF,
333		    M_NOWAIT);
334		if (dmat->segments == NULL)
335			return (ENOMEM);
336	}
337
338	/*
339	 * Bouncing might be required if the driver asks for an active
340	 * exclusion region, a data alignment that is stricter than 1, and/or
341	 * an active address boundary.
342	 */
343	if (dmat->lowaddr < paddr_max) {
344		/* Must bounce */
345		int maxpages;
346
347		*mapp = (bus_dmamap_t)malloc(sizeof(**mapp), M_DEVBUF,
348		    M_NOWAIT | M_ZERO);
349		if (*mapp == NULL)
350			return (ENOMEM);
351
352		/* Initialize the new map */
353		STAILQ_INIT(&((*mapp)->bpages));
354
355		/*
356		 * Attempt to add pages to our pool on a per-instance
357		 * basis up to a sane limit.
358		 */
359		maxpages = MIN(MAX_BPAGES, atop(paddr_max - dmat->lowaddr));
360		if ((dmat->flags & BUS_DMA_MIN_ALLOC_COMP) == 0
361		 || (dmat->map_count > 0 && total_bpages < maxpages)) {
362			int pages;
363
364			pages = MAX(atop(dmat->maxsize), 1);
365			pages = MIN(maxpages - total_bpages, pages);
366			if (alloc_bounce_pages(dmat, pages) < pages)
367				error = ENOMEM;
368
369			if ((dmat->flags & BUS_DMA_MIN_ALLOC_COMP) == 0) {
370				if (error == 0)
371					dmat->flags |= BUS_DMA_MIN_ALLOC_COMP;
372			} else {
373				error = 0;
374			}
375		}
376	} else {
377		*mapp = NULL;
378	}
379	if (error == 0)
380		dmat->map_count++;
381	return (error);
382}
383
384/*
385 * Destroy a handle for mapping from kva/uva/physical
386 * address space into bus device space.
387 */
388int
389bus_dmamap_destroy(bus_dma_tag_t dmat, bus_dmamap_t map)
390{
391
392	if (map != NULL && map != &nobounce_dmamap) {
393		if (STAILQ_FIRST(&map->bpages) != NULL)
394			return (EBUSY);
395		free(map, M_DEVBUF);
396	}
397	dmat->map_count--;
398	return (0);
399}
400
401
402/*
403 * Allocate a piece of memory that can be efficiently mapped into
404 * bus device space based on the constraints lited in the dma tag.
405 * A dmamap to for use with dmamap_load is also allocated.
406 */
407int
408bus_dmamem_alloc(bus_dma_tag_t dmat, void** vaddr, int flags,
409    bus_dmamap_t *mapp)
410{
411	int mflags;
412
413	if (flags & BUS_DMA_NOWAIT)
414		mflags = M_NOWAIT;
415	else
416		mflags = M_WAITOK;
417
418	/* If we succeed, no mapping/bouncing will be required */
419	*mapp = NULL;
420
421	if (dmat->segments == NULL) {
422		dmat->segments = (bus_dma_segment_t *)malloc(
423		    sizeof(bus_dma_segment_t) * dmat->nsegments, M_DEVBUF,
424		    mflags);
425		if (dmat->segments == NULL)
426			return (ENOMEM);
427	}
428	if (flags & BUS_DMA_ZERO)
429		mflags |= M_ZERO;
430
431	/*
432	 * XXX:
433	 * (dmat->alignment < dmat->maxsize) is just a quick hack; the exact
434	 * alignment guarantees of malloc need to be nailed down, and the
435	 * code below should be rewritten to take that into account.
436	 *
437	 * In the meantime, we'll warn the user if malloc gets it wrong.
438	 */
439	if ((dmat->maxsize <= PAGE_SIZE) &&
440	   (dmat->alignment < dmat->maxsize) &&
441	    dmat->lowaddr >= paddr_max) {
442		*vaddr = malloc(dmat->maxsize, M_DEVBUF, mflags);
443	} else {
444		/*
445		 * XXX Use Contigmalloc until it is merged into this facility
446		 *     and handles multi-seg allocations.  Nobody is doing
447		 *     multi-seg allocations yet though.
448		 * XXX Certain AGP hardware does.
449		 */
450		*vaddr = contigmalloc(dmat->maxsize, M_DEVBUF, mflags,
451		    0ul, dmat->lowaddr, dmat->alignment? dmat->alignment : 1ul,
452		    dmat->boundary);
453	}
454	if (*vaddr == NULL)
455		return (ENOMEM);
456	else if (vtophys(*vaddr) & (dmat->alignment - 1))
457		printf("bus_dmamem_alloc failed to align memory properly.\n");
458	return (0);
459}
460
461/*
462 * Free a piece of memory and it's allociated dmamap, that was allocated
463 * via bus_dmamem_alloc.  Make the same choice for free/contigfree.
464 */
465void
466bus_dmamem_free(bus_dma_tag_t dmat, void *vaddr, bus_dmamap_t map)
467{
468	/*
469	 * dmamem does not need to be bounced, so the map should be
470	 * NULL
471	 */
472	if (map != NULL)
473		panic("bus_dmamem_free: Invalid map freed\n");
474	if ((dmat->maxsize <= PAGE_SIZE) &&
475	   (dmat->alignment < dmat->maxsize) &&
476	    dmat->lowaddr >= paddr_max)
477		free(vaddr, M_DEVBUF);
478	else {
479		contigfree(vaddr, dmat->maxsize, M_DEVBUF);
480	}
481}
482
483static void
484_bus_dmamap_count_phys(bus_dma_tag_t dmat, bus_dmamap_t map, vm_paddr_t buf,
485    bus_size_t buflen, int flags)
486{
487	bus_addr_t curaddr;
488	bus_size_t sgsize;
489
490	if ((dmat->lowaddr < paddr_max || dmat->boundary > 0 ||
491	    dmat->alignment > 1) && map != &nobounce_dmamap &&
492	    map->pagesneeded == 0) {
493		/*
494		 * Count the number of bounce pages
495		 * needed in order to complete this transfer
496		 */
497		curaddr = buf;
498		while (buflen != 0) {
499			sgsize = MIN(buflen, dmat->maxsegsz);
500			if (run_filter(dmat, curaddr, 0) != 0) {
501				sgsize = MIN(sgsize, PAGE_SIZE);
502				map->pagesneeded++;
503			}
504			curaddr += sgsize;
505			buflen -= sgsize;
506		}
507	}
508}
509
510static void
511_bus_dmamap_count_pages(bus_dma_tag_t dmat, bus_dmamap_t map, pmap_t pmap,
512    void *buf, bus_size_t buflen, int flags)
513{
514	vm_offset_t vaddr;
515	vm_offset_t vendaddr;
516	bus_addr_t paddr;
517
518	if ((dmat->lowaddr < paddr_max || dmat->boundary > 0 ||
519	    dmat->alignment > 1) && map != &nobounce_dmamap &&
520	    map->pagesneeded == 0) {
521		/*
522		 * Count the number of bounce pages
523		 * needed in order to complete this transfer
524		 */
525		vaddr = trunc_page((vm_offset_t)buf);
526		vendaddr = (vm_offset_t)buf + buflen;
527
528		while (vaddr < vendaddr) {
529			if (pmap == kernel_pmap)
530				paddr = pmap_kextract(vaddr);
531			else
532				paddr = pmap_extract(pmap, vaddr);
533			if (run_filter(dmat, paddr, 0) != 0)
534				map->pagesneeded++;
535			vaddr += PAGE_SIZE;
536		}
537	}
538}
539
540static int
541_bus_dmamap_reserve_pages(bus_dma_tag_t dmat, bus_dmamap_t map, int flags)
542{
543
544	/* Reserve Necessary Bounce Pages */
545	mtx_lock(&bounce_lock);
546	if (flags & BUS_DMA_NOWAIT) {
547		if (reserve_bounce_pages(dmat, map, 0) != 0) {
548			mtx_unlock(&bounce_lock);
549			return (ENOMEM);
550		}
551	} else {
552		if (reserve_bounce_pages(dmat, map, 1) != 0) {
553			/* Queue us for resources */
554			STAILQ_INSERT_TAIL(&bounce_map_waitinglist,
555			    map, links);
556			mtx_unlock(&bounce_lock);
557			return (EINPROGRESS);
558		}
559	}
560	mtx_unlock(&bounce_lock);
561
562	return (0);
563}
564
565/*
566 * Add a single contiguous physical range to the segment list.
567 */
568static int
569_bus_dmamap_addseg(bus_dma_tag_t dmat, bus_dmamap_t map, bus_addr_t curaddr,
570    bus_size_t sgsize, bus_dma_segment_t *segs, int *segp)
571{
572	bus_addr_t baddr, bmask;
573	int seg;
574
575	/*
576	 * Make sure we don't cross any boundaries.
577	 */
578	bmask = ~(dmat->boundary - 1);
579	if (dmat->boundary > 0) {
580		baddr = (curaddr + dmat->boundary) & bmask;
581		if (sgsize > (baddr - curaddr))
582			sgsize = (baddr - curaddr);
583	}
584
585	/*
586	 * Insert chunk into a segment, coalescing with
587	 * previous segment if possible.
588	 */
589	seg = *segp;
590	if (seg == -1) {
591		seg = 0;
592		segs[seg].ds_addr = curaddr;
593		segs[seg].ds_len = sgsize;
594	} else {
595		if (curaddr == segs[seg].ds_addr + segs[seg].ds_len &&
596		    (segs[seg].ds_len + sgsize) <= dmat->maxsegsz &&
597		    (dmat->boundary == 0 ||
598		    (segs[seg].ds_addr & bmask) == (curaddr & bmask)))
599			segs[seg].ds_len += sgsize;
600		else {
601			if (++seg >= dmat->nsegments)
602				return (0);
603			segs[seg].ds_addr = curaddr;
604			segs[seg].ds_len = sgsize;
605		}
606	}
607	*segp = seg;
608	return (sgsize);
609}
610
611/*
612 * Utility function to load a physical buffer.  segp contains
613 * the starting segment on entrace, and the ending segment on exit.
614 */
615int
616_bus_dmamap_load_phys(bus_dma_tag_t dmat, bus_dmamap_t map,
617    vm_paddr_t buf, bus_size_t buflen, int flags, bus_dma_segment_t *segs,
618    int *segp)
619{
620	bus_addr_t curaddr;
621	bus_size_t sgsize;
622	int error;
623
624	if (map == NULL)
625		map = &nobounce_dmamap;
626
627	if (segs == NULL)
628		segs = dmat->segments;
629
630	if (map != &nobounce_dmamap) {
631		_bus_dmamap_count_phys(dmat, map, buf, buflen, flags);
632		if (map->pagesneeded != 0) {
633			error = _bus_dmamap_reserve_pages(dmat, map, flags);
634			if (error)
635				return (error);
636		}
637	}
638
639	while (buflen > 0) {
640		curaddr = buf;
641		sgsize = MIN(buflen, dmat->maxsegsz);
642		if (map->pagesneeded != 0 &&
643		    run_filter(dmat, curaddr, sgsize)) {
644			sgsize = MIN(sgsize, PAGE_SIZE);
645			curaddr = add_bounce_page(dmat, map, 0, curaddr,
646			    sgsize);
647		}
648		sgsize = _bus_dmamap_addseg(dmat, map, curaddr, sgsize, segs,
649		    segp);
650		if (sgsize == 0)
651			break;
652		buf += sgsize;
653		buflen -= sgsize;
654	}
655
656	/*
657	 * Did we fit?
658	 */
659	return (buflen != 0 ? EFBIG : 0); /* XXX better return value here? */
660}
661
662int
663_bus_dmamap_load_ma(bus_dma_tag_t dmat, bus_dmamap_t map,
664    struct vm_page **ma, bus_size_t tlen, int ma_offs, int flags,
665    bus_dma_segment_t *segs, int *segp)
666{
667
668	return (bus_dmamap_load_ma_triv(dmat, map, ma, tlen, ma_offs, flags,
669	    segs, segp));
670}
671
672/*
673 * Utility function to load a linear buffer.  segp contains
674 * the starting segment on entrace, and the ending segment on exit.
675 */
676int
677_bus_dmamap_load_buffer(bus_dma_tag_t dmat, bus_dmamap_t map, void *buf,
678    bus_size_t buflen, pmap_t pmap, int flags,
679    bus_dma_segment_t *segs, int *segp)
680{
681	bus_size_t sgsize;
682	bus_addr_t curaddr;
683	vm_offset_t vaddr;
684	int error;
685
686	if (map == NULL)
687		map = &nobounce_dmamap;
688
689	if (segs == NULL)
690		segs = dmat->segments;
691
692	if (map != &nobounce_dmamap) {
693		_bus_dmamap_count_pages(dmat, map, pmap, buf, buflen, flags);
694		if (map->pagesneeded != 0) {
695			error = _bus_dmamap_reserve_pages(dmat, map, flags);
696			if (error)
697				return (error);
698		}
699	}
700
701	vaddr = (vm_offset_t)buf;
702
703	while (buflen > 0) {
704		/*
705		 * Get the physical address for this segment.
706		 */
707		if (pmap == kernel_pmap)
708			curaddr = pmap_kextract(vaddr);
709		else
710			curaddr = pmap_extract(pmap, vaddr);
711
712		/*
713		 * Compute the segment size, and adjust counts.
714		 */
715		sgsize = PAGE_SIZE - ((u_long)curaddr & PAGE_MASK);
716		if (sgsize > dmat->maxsegsz)
717			sgsize = dmat->maxsegsz;
718		if (buflen < sgsize)
719			sgsize = buflen;
720
721		if (map->pagesneeded != 0 && run_filter(dmat, curaddr, sgsize))
722			curaddr = add_bounce_page(dmat, map, vaddr, curaddr,
723			    sgsize);
724
725		sgsize = _bus_dmamap_addseg(dmat, map, curaddr, sgsize, segs,
726		    segp);
727		if (sgsize == 0)
728			break;
729
730		vaddr += sgsize;
731		buflen -= sgsize;
732	}
733
734	/*
735	 * Did we fit?
736	 */
737	return (buflen != 0 ? EFBIG : 0); /* XXX better return value here? */
738}
739
740
741void
742__bus_dmamap_waitok(bus_dma_tag_t dmat, bus_dmamap_t map,
743    struct memdesc *mem, bus_dmamap_callback_t *callback, void *callback_arg)
744{
745	if (map != NULL) {
746		map->dmat = dmat;
747		map->mem = *mem;
748		map->callback = callback;
749		map->callback_arg = callback_arg;
750	}
751}
752
753bus_dma_segment_t *
754_bus_dmamap_complete(bus_dma_tag_t dmat, bus_dmamap_t map,
755    bus_dma_segment_t *segs, int nsegs, int error)
756{
757
758	if (segs == NULL)
759		segs = dmat->segments;
760	return (segs);
761}
762
763/*
764 * Release the mapping held by map.
765 */
766void
767_bus_dmamap_unload(bus_dma_tag_t dmat, bus_dmamap_t map)
768{
769	struct bounce_page *bpage;
770
771	while ((bpage = STAILQ_FIRST(&map->bpages)) != NULL) {
772		STAILQ_REMOVE_HEAD(&map->bpages, links);
773		free_bounce_page(dmat, bpage);
774	}
775}
776
777void
778_bus_dmamap_sync(bus_dma_tag_t dmat, bus_dmamap_t map, bus_dmasync_op_t op)
779{
780	struct bounce_page *bpage;
781
782	if ((bpage = STAILQ_FIRST(&map->bpages)) != NULL) {
783		/*
784		 * Handle data bouncing.  We might also
785		 * want to add support for invalidating
786		 * the caches on broken hardware
787		 */
788
789		if (op & BUS_DMASYNC_PREWRITE) {
790			while (bpage != NULL) {
791				if (bpage->datavaddr != 0)
792					bcopy((void *)bpage->datavaddr,
793					    (void *)bpage->vaddr,
794					    bpage->datacount);
795				else
796					physcopyout(bpage->dataaddr,
797					    (void *)bpage->vaddr,
798					    bpage->datacount);
799				bpage = STAILQ_NEXT(bpage, links);
800			}
801			total_bounced++;
802		}
803
804		if (op & BUS_DMASYNC_POSTREAD) {
805			while (bpage != NULL) {
806				if (bpage->datavaddr != 0)
807					bcopy((void *)bpage->vaddr,
808					    (void *)bpage->datavaddr,
809					    bpage->datacount);
810				else
811					physcopyin((void *)bpage->vaddr,
812					    bpage->dataaddr,
813					    bpage->datacount);
814				bpage = STAILQ_NEXT(bpage, links);
815			}
816			total_bounced++;
817		}
818	}
819}
820
821static void
822init_bounce_pages(void *dummy __unused)
823{
824
825	free_bpages = 0;
826	reserved_bpages = 0;
827	active_bpages = 0;
828	total_bpages = 0;
829	STAILQ_INIT(&bounce_page_list);
830	STAILQ_INIT(&bounce_map_waitinglist);
831	STAILQ_INIT(&bounce_map_callbacklist);
832	mtx_init(&bounce_lock, "bounce pages lock", NULL, MTX_DEF);
833}
834SYSINIT(bpages, SI_SUB_LOCK, SI_ORDER_ANY, init_bounce_pages, NULL);
835
836static int
837alloc_bounce_pages(bus_dma_tag_t dmat, u_int numpages)
838{
839	int count;
840
841	count = 0;
842	while (numpages > 0) {
843		struct bounce_page *bpage;
844
845		bpage = (struct bounce_page *)malloc(sizeof(*bpage), M_DEVBUF,
846		    M_NOWAIT | M_ZERO);
847		if (bpage == NULL)
848			break;
849		bpage->vaddr = (vm_offset_t)contigmalloc(PAGE_SIZE, M_DEVBUF,
850		    M_NOWAIT, 0ul, dmat->lowaddr, PAGE_SIZE, dmat->boundary);
851		if (bpage->vaddr == 0) {
852			free(bpage, M_DEVBUF);
853			break;
854		}
855		bpage->busaddr = pmap_kextract(bpage->vaddr);
856		mtx_lock(&bounce_lock);
857		STAILQ_INSERT_TAIL(&bounce_page_list, bpage, links);
858		total_bpages++;
859		free_bpages++;
860		mtx_unlock(&bounce_lock);
861		count++;
862		numpages--;
863	}
864	return (count);
865}
866
867static int
868reserve_bounce_pages(bus_dma_tag_t dmat, bus_dmamap_t map, int commit)
869{
870	int pages;
871
872	mtx_assert(&bounce_lock, MA_OWNED);
873	pages = MIN(free_bpages, map->pagesneeded - map->pagesreserved);
874	if (commit == 0 && map->pagesneeded > (map->pagesreserved + pages))
875		return (map->pagesneeded - (map->pagesreserved + pages));
876	free_bpages -= pages;
877	reserved_bpages += pages;
878	map->pagesreserved += pages;
879	pages = map->pagesneeded - map->pagesreserved;
880
881	return (pages);
882}
883
884static bus_addr_t
885add_bounce_page(bus_dma_tag_t dmat, bus_dmamap_t map, vm_offset_t vaddr,
886    bus_addr_t addr, bus_size_t size)
887{
888	struct bounce_page *bpage;
889
890	KASSERT(map != NULL && map != &nobounce_dmamap,
891	    ("add_bounce_page: bad map %p", map));
892
893	if (map->pagesneeded == 0)
894		panic("add_bounce_page: map doesn't need any pages");
895	map->pagesneeded--;
896
897	if (map->pagesreserved == 0)
898		panic("add_bounce_page: map doesn't need any pages");
899	map->pagesreserved--;
900
901	mtx_lock(&bounce_lock);
902	bpage = STAILQ_FIRST(&bounce_page_list);
903	if (bpage == NULL)
904		panic("add_bounce_page: free page list is empty");
905
906	STAILQ_REMOVE_HEAD(&bounce_page_list, links);
907	reserved_bpages--;
908	active_bpages++;
909	mtx_unlock(&bounce_lock);
910
911	if (dmat->flags & BUS_DMA_KEEP_PG_OFFSET) {
912		/* Page offset needs to be preserved. */
913		bpage->vaddr |= vaddr & PAGE_MASK;
914		bpage->busaddr |= vaddr & PAGE_MASK;
915	}
916	bpage->datavaddr = vaddr;
917	bpage->dataaddr = addr;
918	bpage->datacount = size;
919	STAILQ_INSERT_TAIL(&(map->bpages), bpage, links);
920	return (bpage->busaddr);
921}
922
923static void
924free_bounce_page(bus_dma_tag_t dmat, struct bounce_page *bpage)
925{
926	struct bus_dmamap *map;
927
928	bpage->datavaddr = 0;
929	bpage->datacount = 0;
930	if (dmat->flags & BUS_DMA_KEEP_PG_OFFSET) {
931		/*
932		 * Reset the bounce page to start at offset 0.  Other uses
933		 * of this bounce page may need to store a full page of
934		 * data and/or assume it starts on a page boundary.
935		 */
936		bpage->vaddr &= ~PAGE_MASK;
937		bpage->busaddr &= ~PAGE_MASK;
938	}
939
940	mtx_lock(&bounce_lock);
941	STAILQ_INSERT_HEAD(&bounce_page_list, bpage, links);
942	free_bpages++;
943	active_bpages--;
944	if ((map = STAILQ_FIRST(&bounce_map_waitinglist)) != NULL) {
945		if (reserve_bounce_pages(map->dmat, map, 1) == 0) {
946			STAILQ_REMOVE_HEAD(&bounce_map_waitinglist, links);
947			STAILQ_INSERT_TAIL(&bounce_map_callbacklist, map,
948			    links);
949			busdma_swi_pending = 1;
950			total_deferred++;
951			swi_sched(vm_ih, 0);
952		}
953	}
954	mtx_unlock(&bounce_lock);
955}
956
957void
958busdma_swi(void)
959{
960	bus_dma_tag_t dmat;
961	struct bus_dmamap *map;
962
963	mtx_lock(&bounce_lock);
964	while ((map = STAILQ_FIRST(&bounce_map_callbacklist)) != NULL) {
965		STAILQ_REMOVE_HEAD(&bounce_map_callbacklist, links);
966		mtx_unlock(&bounce_lock);
967		dmat = map->dmat;
968		(dmat->lockfunc)(dmat->lockfuncarg, BUS_DMA_LOCK);
969		bus_dmamap_load_mem(map->dmat, map, &map->mem, map->callback,
970		    map->callback_arg, BUS_DMA_WAITOK);
971		(dmat->lockfunc)(dmat->lockfuncarg, BUS_DMA_UNLOCK);
972		mtx_lock(&bounce_lock);
973	}
974	mtx_unlock(&bounce_lock);
975}
976