1 /* $NetBSD: isadma_bounce.c,v 1.7 2007/03/05 18:21:59 matt Exp $ */
4 * Copyright (c) 1996, 1997, 1998, 2000, 2001 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
9 * NASA Ames Research Center.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
33 #include <sys/cdefs.h>
34 __KERNEL_RCSID(0, "$NetBSD: isadma_bounce.c,v 1.7 2007/03/05 18:21:59 matt Exp $");
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/syslog.h>
39 #include <sys/device.h>
40 #include <sys/malloc.h>
44 #include <mips/cache.h>
46 #define _ALGOR_BUS_DMA_PRIVATE
47 #include <machine/bus.h>
48 #include <machine/locore.h>
50 #include <dev/isa/isareg.h>
51 #include <dev/isa/isavar.h>
53 #include <uvm/uvm_extern.h>
55 extern paddr_t avail_end
;
58 * Cookie used by bouncing ISA DMA. A pointer to one of these is stashed
61 struct isadma_bounce_cookie
{
62 int id_flags
; /* flags; see below */
65 * Information about the original buffer used during
66 * DMA map syncs. Note that origbuflen is only used
67 * for ID_BUFTYPE_LINEAR.
69 void *id_origbuf
; /* pointer to orig buffer if
71 bus_size_t id_origbuflen
; /* ...and size */
72 int id_buftype
; /* type of buffer */
74 void *id_bouncebuf
; /* pointer to the bounce buffer */
75 bus_size_t id_bouncebuflen
; /* ...and size */
76 int id_nbouncesegs
; /* number of valid bounce segs */
77 bus_dma_segment_t id_bouncesegs
[1]; /* array of bounce buffer
78 physical memory segments */
82 #define ID_MIGHT_NEED_BOUNCE 0x01 /* map could need bounce buffers */
83 #define ID_HAS_BOUNCE 0x02 /* map currently has bounce buffers */
84 #define ID_IS_BOUNCING 0x04 /* map is bouncing current xfer */
87 #define ID_BUFTYPE_INVALID 0
88 #define ID_BUFTYPE_LINEAR 1
89 #define ID_BUFTYPE_MBUF 2
90 #define ID_BUFTYPE_UIO 3
91 #define ID_BUFTYPE_RAW 4
93 int isadma_bounce_alloc_bouncebuf(bus_dma_tag_t
, bus_dmamap_t
,
95 void isadma_bounce_free_bouncebuf(bus_dma_tag_t
, bus_dmamap_t
);
98 * Create an ISA DMA map.
101 isadma_bounce_dmamap_create(bus_dma_tag_t t
, bus_size_t size
, int nsegments
,
102 bus_size_t maxsegsz
, bus_size_t boundary
, int flags
, bus_dmamap_t
*dmamp
)
104 struct isadma_bounce_cookie
*cookie
;
106 int error
, cookieflags
;
110 /* Call common function to create the basic map. */
111 error
= _bus_dmamap_create(t
, size
, nsegments
, maxsegsz
, boundary
,
117 map
->_dm_cookie
= NULL
;
119 cookiesize
= sizeof(*cookie
);
122 * ISA only has 24-bits of address space. This means
123 * we can't DMA to pages over 16M. In order to DMA to
124 * arbitrary buffers, we use "bounce buffers" - pages
125 * in memory below the 16M boundary. On DMA reads,
126 * DMA happens to the bounce buffers, and is copied into
127 * the caller's buffer. On writes, data is copied into
128 * but bounce buffer, and the DMA happens from those
129 * pages. To software using the DMA mapping interface,
130 * this looks simply like a data cache.
132 * If we have more than 16M of RAM in the system, we may
133 * need bounce buffers. We check and remember that here.
135 * ...or, there is an opposite case. The most segments
136 * a transfer will require is (maxxfer / PAGE_SIZE) + 1. If
137 * the caller can't handle that many segments (e.g. the
138 * ISA DMA controller), we may have to bounce it as well.
141 if (avail_end
> (t
->_wbase
+ t
->_wsize
) ||
142 ((map
->_dm_size
/ PAGE_SIZE
) + 1) > map
->_dm_segcnt
) {
143 cookieflags
|= ID_MIGHT_NEED_BOUNCE
;
144 cookiesize
+= (sizeof(bus_dma_segment_t
) *
145 (map
->_dm_segcnt
- 1));
149 * Allocate our cookie.
151 if ((cookiestore
= malloc(cookiesize
, M_DMAMAP
,
152 (flags
& BUS_DMA_NOWAIT
) ? M_NOWAIT
: M_WAITOK
)) == NULL
) {
156 memset(cookiestore
, 0, cookiesize
);
157 cookie
= (struct isadma_bounce_cookie
*)cookiestore
;
158 cookie
->id_flags
= cookieflags
;
159 map
->_dm_cookie
= cookie
;
161 if (cookieflags
& ID_MIGHT_NEED_BOUNCE
) {
163 * Allocate the bounce pages now if the caller
164 * wishes us to do so.
166 if ((flags
& BUS_DMA_ALLOCNOW
) == 0)
169 error
= isadma_bounce_alloc_bouncebuf(t
, map
, size
, flags
);
174 if (map
->_dm_cookie
!= NULL
)
175 free(map
->_dm_cookie
, M_DMAMAP
);
176 _bus_dmamap_destroy(t
, map
);
182 * Destroy an ISA DMA map.
185 isadma_bounce_dmamap_destroy(bus_dma_tag_t t
, bus_dmamap_t map
)
187 struct isadma_bounce_cookie
*cookie
= map
->_dm_cookie
;
190 * Free any bounce pages this map might hold.
192 if (cookie
->id_flags
& ID_HAS_BOUNCE
)
193 isadma_bounce_free_bouncebuf(t
, map
);
195 free(cookie
, M_DMAMAP
);
196 _bus_dmamap_destroy(t
, map
);
200 * Load an ISA DMA map with a linear buffer.
203 isadma_bounce_dmamap_load(bus_dma_tag_t t
, bus_dmamap_t map
, void *buf
,
204 bus_size_t buflen
, struct proc
*p
, int flags
)
206 struct isadma_bounce_cookie
*cookie
= map
->_dm_cookie
;
210 * Make sure that on error condition we return "no valid mappings."
216 * Try to load the map the normal way. If this errors out,
217 * and we can bounce, we will.
219 error
= _bus_dmamap_load(t
, map
, buf
, buflen
, p
, flags
);
221 (error
!= 0 && (cookie
->id_flags
& ID_MIGHT_NEED_BOUNCE
) == 0))
225 * First attempt failed; bounce it.
229 * Allocate bounce pages, if necessary.
231 if ((cookie
->id_flags
& ID_HAS_BOUNCE
) == 0) {
232 error
= isadma_bounce_alloc_bouncebuf(t
, map
, buflen
, flags
);
238 * Cache a pointer to the caller's buffer and load the DMA map
239 * with the bounce buffer.
241 cookie
->id_origbuf
= buf
;
242 cookie
->id_origbuflen
= buflen
;
243 cookie
->id_buftype
= ID_BUFTYPE_LINEAR
;
244 error
= _bus_dmamap_load(t
, map
, cookie
->id_bouncebuf
, buflen
,
248 * Free the bounce pages, unless our resources
249 * are reserved for our exclusive use.
251 if ((map
->_dm_flags
& BUS_DMA_ALLOCNOW
) == 0)
252 isadma_bounce_free_bouncebuf(t
, map
);
256 /* ...so isadma_bounce_dmamap_sync() knows we're bouncing */
257 cookie
->id_flags
|= ID_IS_BOUNCING
;
262 * Like isadma_bounce_dmamap_load(), but for mbufs.
265 isadma_bounce_dmamap_load_mbuf(bus_dma_tag_t t
, bus_dmamap_t map
,
266 struct mbuf
*m0
, int flags
)
268 struct isadma_bounce_cookie
*cookie
= map
->_dm_cookie
;
272 * Make sure on error condition we return "no valid mappings."
278 if ((m0
->m_flags
& M_PKTHDR
) == 0)
279 panic("isadma_bounce_dmamap_load_mbuf: no packet header");
282 if (m0
->m_pkthdr
.len
> map
->_dm_size
)
286 * Try to load the map the normal way. If this errors out,
287 * and we can bounce, we will.
289 error
= _bus_dmamap_load_mbuf(t
, map
, m0
, flags
);
291 (error
!= 0 && (cookie
->id_flags
& ID_MIGHT_NEED_BOUNCE
) == 0))
295 * First attempt failed; bounce it.
299 * Allocate bounce pages, if necessary.
301 if ((cookie
->id_flags
& ID_HAS_BOUNCE
) == 0) {
302 error
= isadma_bounce_alloc_bouncebuf(t
, map
, m0
->m_pkthdr
.len
,
309 * Cache a pointer to the caller's buffer and load the DMA map
310 * with the bounce buffer.
312 cookie
->id_origbuf
= m0
;
313 cookie
->id_origbuflen
= m0
->m_pkthdr
.len
; /* not really used */
314 cookie
->id_buftype
= ID_BUFTYPE_MBUF
;
315 error
= _bus_dmamap_load(t
, map
, cookie
->id_bouncebuf
,
316 m0
->m_pkthdr
.len
, NULL
, flags
);
319 * Free the bounce pages, unless our resources
320 * are reserved for our exclusive use.
322 if ((map
->_dm_flags
& BUS_DMA_ALLOCNOW
) == 0)
323 isadma_bounce_free_bouncebuf(t
, map
);
327 /* ...so isadma_bounce_dmamap_sync() knows we're bouncing */
328 cookie
->id_flags
|= ID_IS_BOUNCING
;
333 * Like isadma_bounce_dmamap_load(), but for uios.
336 isadma_bounce_dmamap_load_uio(bus_dma_tag_t t
, bus_dmamap_t map
,
337 struct uio
*uio
, int flags
)
340 panic("isadma_bounce_dmamap_load_uio: not implemented");
344 * Like isadma_bounce_dmamap_load(), but for raw memory allocated with
345 * bus_dmamem_alloc().
348 isadma_bounce_dmamap_load_raw(bus_dma_tag_t t
, bus_dmamap_t map
,
349 bus_dma_segment_t
*segs
, int nsegs
, bus_size_t size
, int flags
)
352 panic("isadma_bounce_dmamap_load_raw: not implemented");
356 * Unload an ISA DMA map.
359 isadma_bounce_dmamap_unload(bus_dma_tag_t t
, bus_dmamap_t map
)
361 struct isadma_bounce_cookie
*cookie
= map
->_dm_cookie
;
364 * If we have bounce pages, free them, unless they're
365 * reserved for our exclusive use.
367 if ((cookie
->id_flags
& ID_HAS_BOUNCE
) &&
368 (map
->_dm_flags
& BUS_DMA_ALLOCNOW
) == 0)
369 isadma_bounce_free_bouncebuf(t
, map
);
371 cookie
->id_flags
&= ~ID_IS_BOUNCING
;
372 cookie
->id_buftype
= ID_BUFTYPE_INVALID
;
375 * Do the generic bits of the unload.
377 _bus_dmamap_unload(t
, map
);
381 * Synchronize an ISA DMA map.
384 isadma_bounce_dmamap_sync(bus_dma_tag_t t
, bus_dmamap_t map
, bus_addr_t offset
,
385 bus_size_t len
, int ops
)
387 struct isadma_bounce_cookie
*cookie
= map
->_dm_cookie
;
390 * Mixing PRE and POST operations is not allowed.
392 if ((ops
& (BUS_DMASYNC_PREREAD
|BUS_DMASYNC_PREWRITE
)) != 0 &&
393 (ops
& (BUS_DMASYNC_POSTREAD
|BUS_DMASYNC_POSTWRITE
)) != 0)
394 panic("isadma_bounce_dmamap_sync: mix PRE and POST");
397 if ((ops
& (BUS_DMASYNC_PREWRITE
|BUS_DMASYNC_POSTREAD
)) != 0) {
398 if (offset
>= map
->dm_mapsize
)
399 panic("isadma_bounce_dmamap_sync: bad offset");
400 if (len
== 0 || (offset
+ len
) > map
->dm_mapsize
)
401 panic("isadma_bounce_dmamap_sync: bad length");
406 * If we're not bouncing, just do the normal sync operation
409 if ((cookie
->id_flags
& ID_IS_BOUNCING
) == 0) {
410 _bus_dmamap_sync(t
, map
, offset
, len
, ops
);
415 * Flush data cache for PREREAD. This has the side-effect
416 * of invalidating the cache. Done at PREREAD since it
417 * causes the cache line(s) to be written back to memory.
419 * Copy the original buffer to the bounce buffer and flush
420 * the data cache for PREWRITE, so that the contents
421 * of the data buffer in memory reflect reality.
423 * Copy the bounce buffer to the original buffer in POSTREAD.
426 switch (cookie
->id_buftype
) {
427 case ID_BUFTYPE_LINEAR
:
429 * Nothing to do for pre-read.
432 if (ops
& BUS_DMASYNC_PREWRITE
) {
434 * Copy the caller's buffer to the bounce buffer.
436 memcpy((char *)cookie
->id_bouncebuf
+ offset
,
437 (char *)cookie
->id_origbuf
+ offset
, len
);
441 if (ops
& BUS_DMASYNC_POSTREAD
) {
443 * Copy the bounce buffer to the caller's buffer.
445 memcpy((char *)cookie
->id_origbuf
+ offset
,
446 (char *)cookie
->id_bouncebuf
+ offset
, len
);
450 * Nothing to do for post-write.
454 case ID_BUFTYPE_MBUF
:
456 struct mbuf
*m
, *m0
= cookie
->id_origbuf
;
457 bus_size_t minlen
, moff
;
460 * Nothing to do for pre-read.
463 if (ops
& BUS_DMASYNC_PREWRITE
) {
465 * Copy the caller's buffer to the bounce buffer.
467 m_copydata(m0
, offset
, len
,
468 (char *)cookie
->id_bouncebuf
+ offset
);
471 if (ops
& BUS_DMASYNC_POSTREAD
) {
473 * Copy the bounce buffer to the caller's buffer.
475 for (moff
= offset
, m
= m0
; m
!= NULL
&& len
!= 0;
477 /* Find the beginning mbuf. */
478 if (moff
>= m
->m_len
) {
484 * Now at the first mbuf to sync; nail
485 * each one until we have exhausted the
488 minlen
= len
< m
->m_len
- moff
?
489 len
: m
->m_len
- moff
;
491 memcpy(mtod(m
, uint8_t *) + moff
,
492 (char *)cookie
->id_bouncebuf
+ offset
,
502 * Nothing to do for post-write.
508 panic("isadma_bounce_dmamap_sync: ID_BUFTYPE_UIO");
512 panic("isadma_bounce_dmamap_sync: ID_BUFTYPE_RAW");
515 case ID_BUFTYPE_INVALID
:
516 panic("isadma_bounce_dmamap_sync: ID_BUFTYPE_INVALID");
520 printf("unknown buffer type %d\n", cookie
->id_buftype
);
521 panic("isadma_bounce_dmamap_sync");
524 /* Drain the write buffer. */
528 if (ops
& (BUS_DMASYNC_PREREAD
|BUS_DMASYNC_PREWRITE
))
529 mips_dcache_wbinv_range((vaddr_t
)cookie
->id_bouncebuf
+ offset
,
534 * Allocate memory safe for ISA DMA.
537 isadma_bounce_dmamem_alloc(bus_dma_tag_t t
, bus_size_t size
,
538 bus_size_t alignment
, bus_size_t boundary
, bus_dma_segment_t
*segs
,
539 int nsegs
, int *rsegs
, int flags
)
543 if (avail_end
> ISA_DMA_BOUNCE_THRESHOLD
)
544 high
= trunc_page(ISA_DMA_BOUNCE_THRESHOLD
);
546 high
= trunc_page(avail_end
);
548 return (_bus_dmamem_alloc_range(t
, size
, alignment
, boundary
,
549 segs
, nsegs
, rsegs
, flags
, 0, high
));
552 /**********************************************************************
553 * ISA DMA utility functions
554 **********************************************************************/
557 isadma_bounce_alloc_bouncebuf(bus_dma_tag_t t
, bus_dmamap_t map
,
558 bus_size_t size
, int flags
)
560 struct isadma_bounce_cookie
*cookie
= map
->_dm_cookie
;
563 cookie
->id_bouncebuflen
= round_page(size
);
564 error
= isadma_bounce_dmamem_alloc(t
, cookie
->id_bouncebuflen
,
565 PAGE_SIZE
, map
->_dm_boundary
, cookie
->id_bouncesegs
,
566 map
->_dm_segcnt
, &cookie
->id_nbouncesegs
, flags
);
569 error
= _bus_dmamem_map(t
, cookie
->id_bouncesegs
,
570 cookie
->id_nbouncesegs
, cookie
->id_bouncebuflen
,
571 (void **)&cookie
->id_bouncebuf
, flags
);
575 _bus_dmamem_free(t
, cookie
->id_bouncesegs
,
576 cookie
->id_nbouncesegs
);
577 cookie
->id_bouncebuflen
= 0;
578 cookie
->id_nbouncesegs
= 0;
580 cookie
->id_flags
|= ID_HAS_BOUNCE
;
586 isadma_bounce_free_bouncebuf(bus_dma_tag_t t
, bus_dmamap_t map
)
588 struct isadma_bounce_cookie
*cookie
= map
->_dm_cookie
;
590 _bus_dmamem_unmap(t
, cookie
->id_bouncebuf
,
591 cookie
->id_bouncebuflen
);
592 _bus_dmamem_free(t
, cookie
->id_bouncesegs
,
593 cookie
->id_nbouncesegs
);
594 cookie
->id_bouncebuflen
= 0;
595 cookie
->id_nbouncesegs
= 0;
596 cookie
->id_flags
&= ~ID_HAS_BOUNCE
;