1 /* $NetBSD: isadma_bounce.c,v 1.7 2007/03/06 00:48:08 simonb 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/06 00:48:08 simonb 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>
45 #define _MIPS_BUS_DMA_PRIVATE
46 #include <machine/bus.h>
47 #include <machine/locore.h>
49 #include <dev/isa/isareg.h>
50 #include <dev/isa/isavar.h>
52 #include <uvm/uvm_extern.h>
54 extern paddr_t avail_end
;
57 * Cookie used by bouncing ISA DMA. A pointer to one of these is stashed
60 struct isadma_bounce_cookie
{
61 int id_flags
; /* flags; see below */
64 * Information about the original buffer used during
65 * DMA map syncs. Note that origbuflen is only used
66 * for ID_BUFTYPE_LINEAR.
68 void *id_origbuf
; /* pointer to orig buffer if
70 bus_size_t id_origbuflen
; /* ...and size */
71 int id_buftype
; /* type of buffer */
73 void *id_bouncebuf
; /* pointer to the bounce buffer */
74 bus_size_t id_bouncebuflen
; /* ...and size */
75 int id_nbouncesegs
; /* number of valid bounce segs */
76 bus_dma_segment_t id_bouncesegs
[1]; /* array of bounce buffer
77 physical memory segments */
81 #define ID_MIGHT_NEED_BOUNCE 0x01 /* map could need bounce buffers */
82 #define ID_HAS_BOUNCE 0x02 /* map currently has bounce buffers */
83 #define ID_IS_BOUNCING 0x04 /* map is bouncing current xfer */
86 #define ID_BUFTYPE_INVALID 0
87 #define ID_BUFTYPE_LINEAR 1
88 #define ID_BUFTYPE_MBUF 2
89 #define ID_BUFTYPE_UIO 3
90 #define ID_BUFTYPE_RAW 4
92 int isadma_bounce_alloc_bouncebuf(bus_dma_tag_t
, bus_dmamap_t
,
94 void isadma_bounce_free_bouncebuf(bus_dma_tag_t
, bus_dmamap_t
);
97 * Create an ISA DMA map.
100 isadma_bounce_dmamap_create(bus_dma_tag_t t
, bus_size_t size
, int nsegments
,
101 bus_size_t maxsegsz
, bus_size_t boundary
, int flags
, bus_dmamap_t
*dmamp
)
103 struct isadma_bounce_cookie
*cookie
;
105 int error
, cookieflags
;
109 /* Call common function to create the basic map. */
110 error
= _bus_dmamap_create(t
, size
, nsegments
, maxsegsz
, boundary
,
116 map
->_dm_cookie
= NULL
;
118 cookiesize
= sizeof(*cookie
);
121 * ISA only has 24-bits of address space. This means
122 * we can't DMA to pages over 16M. In order to DMA to
123 * arbitrary buffers, we use "bounce buffers" - pages
124 * in memory below the 16M boundary. On DMA reads,
125 * DMA happens to the bounce buffers, and is copied into
126 * the caller's buffer. On writes, data is copied into
127 * but bounce buffer, and the DMA happens from those
128 * pages. To software using the DMA mapping interface,
129 * this looks simply like a data cache.
131 * If we have more than 16M of RAM in the system, we may
132 * need bounce buffers. We check and remember that here.
134 * ...or, there is an opposite case. The most segments
135 * a transfer will require is (maxxfer / PAGE_SIZE) + 1. If
136 * the caller can't handle that many segments (e.g. the
137 * ISA DMA controller), we may have to bounce it as well.
140 if (avail_end
> (t
->_wbase
+ t
->_wsize
) ||
141 ((map
->_dm_size
/ PAGE_SIZE
) + 1) > map
->_dm_segcnt
) {
142 cookieflags
|= ID_MIGHT_NEED_BOUNCE
;
143 cookiesize
+= (sizeof(bus_dma_segment_t
) *
144 (map
->_dm_segcnt
- 1));
148 * Allocate our cookie.
150 if ((cookiestore
= malloc(cookiesize
, M_DMAMAP
,
151 (flags
& BUS_DMA_NOWAIT
) ? M_NOWAIT
: M_WAITOK
)) == NULL
) {
155 memset(cookiestore
, 0, cookiesize
);
156 cookie
= (struct isadma_bounce_cookie
*)cookiestore
;
157 cookie
->id_flags
= cookieflags
;
158 map
->_dm_cookie
= cookie
;
160 if (cookieflags
& ID_MIGHT_NEED_BOUNCE
) {
162 * Allocate the bounce pages now if the caller
163 * wishes us to do so.
165 if ((flags
& BUS_DMA_ALLOCNOW
) == 0)
168 error
= isadma_bounce_alloc_bouncebuf(t
, map
, size
, flags
);
173 if (map
->_dm_cookie
!= NULL
)
174 free(map
->_dm_cookie
, M_DMAMAP
);
175 _bus_dmamap_destroy(t
, map
);
181 * Destroy an ISA DMA map.
184 isadma_bounce_dmamap_destroy(bus_dma_tag_t t
, bus_dmamap_t map
)
186 struct isadma_bounce_cookie
*cookie
= map
->_dm_cookie
;
189 * Free any bounce pages this map might hold.
191 if (cookie
->id_flags
& ID_HAS_BOUNCE
)
192 isadma_bounce_free_bouncebuf(t
, map
);
194 free(cookie
, M_DMAMAP
);
195 _bus_dmamap_destroy(t
, map
);
199 * Load an ISA DMA map with a linear buffer.
202 isadma_bounce_dmamap_load(bus_dma_tag_t t
, bus_dmamap_t map
, void *buf
,
203 bus_size_t buflen
, struct proc
*p
, int flags
)
205 struct isadma_bounce_cookie
*cookie
= map
->_dm_cookie
;
209 * Make sure that on error condition we return "no valid mappings."
215 * Try to load the map the normal way. If this errors out,
216 * and we can bounce, we will.
218 error
= _bus_dmamap_load(t
, map
, buf
, buflen
, p
, flags
);
220 (error
!= 0 && (cookie
->id_flags
& ID_MIGHT_NEED_BOUNCE
) == 0))
224 * First attempt failed; bounce it.
228 * Allocate bounce pages, if necessary.
230 if ((cookie
->id_flags
& ID_HAS_BOUNCE
) == 0) {
231 error
= isadma_bounce_alloc_bouncebuf(t
, map
, buflen
, flags
);
237 * Cache a pointer to the caller's buffer and load the DMA map
238 * with the bounce buffer.
240 cookie
->id_origbuf
= buf
;
241 cookie
->id_origbuflen
= buflen
;
242 cookie
->id_buftype
= ID_BUFTYPE_LINEAR
;
243 error
= _bus_dmamap_load(t
, map
, cookie
->id_bouncebuf
, buflen
,
247 * Free the bounce pages, unless our resources
248 * are reserved for our exclusive use.
250 if ((map
->_dm_flags
& BUS_DMA_ALLOCNOW
) == 0)
251 isadma_bounce_free_bouncebuf(t
, map
);
255 /* ...so isadma_bounce_dmamap_sync() knows we're bouncing */
256 cookie
->id_flags
|= ID_IS_BOUNCING
;
261 * Like isadma_bounce_dmamap_load(), but for mbufs.
264 isadma_bounce_dmamap_load_mbuf(bus_dma_tag_t t
, bus_dmamap_t map
,
265 struct mbuf
*m0
, int flags
)
267 struct isadma_bounce_cookie
*cookie
= map
->_dm_cookie
;
271 * Make sure on error condition we return "no valid mappings."
277 if ((m0
->m_flags
& M_PKTHDR
) == 0)
278 panic("isadma_bounce_dmamap_load_mbuf: no packet header");
281 if (m0
->m_pkthdr
.len
> map
->_dm_size
)
285 * Try to load the map the normal way. If this errors out,
286 * and we can bounce, we will.
288 error
= _bus_dmamap_load_mbuf(t
, map
, m0
, flags
);
290 (error
!= 0 && (cookie
->id_flags
& ID_MIGHT_NEED_BOUNCE
) == 0))
294 * First attempt failed; bounce it.
298 * Allocate bounce pages, if necessary.
300 if ((cookie
->id_flags
& ID_HAS_BOUNCE
) == 0) {
301 error
= isadma_bounce_alloc_bouncebuf(t
, map
, m0
->m_pkthdr
.len
,
308 * Cache a pointer to the caller's buffer and load the DMA map
309 * with the bounce buffer.
311 cookie
->id_origbuf
= m0
;
312 cookie
->id_origbuflen
= m0
->m_pkthdr
.len
; /* not really used */
313 cookie
->id_buftype
= ID_BUFTYPE_MBUF
;
314 error
= _bus_dmamap_load(t
, map
, cookie
->id_bouncebuf
,
315 m0
->m_pkthdr
.len
, NULL
, flags
);
318 * Free the bounce pages, unless our resources
319 * are reserved for our exclusive use.
321 if ((map
->_dm_flags
& BUS_DMA_ALLOCNOW
) == 0)
322 isadma_bounce_free_bouncebuf(t
, map
);
326 /* ...so isadma_bounce_dmamap_sync() knows we're bouncing */
327 cookie
->id_flags
|= ID_IS_BOUNCING
;
332 * Like isadma_bounce_dmamap_load(), but for uios.
335 isadma_bounce_dmamap_load_uio(bus_dma_tag_t t
, bus_dmamap_t map
,
336 struct uio
*uio
, int flags
)
339 panic("isadma_bounce_dmamap_load_uio: not implemented");
343 * Like isadma_bounce_dmamap_load(), but for raw memory allocated with
344 * bus_dmamem_alloc().
347 isadma_bounce_dmamap_load_raw(bus_dma_tag_t t
, bus_dmamap_t map
,
348 bus_dma_segment_t
*segs
, int nsegs
, bus_size_t size
, int flags
)
351 panic("isadma_bounce_dmamap_load_raw: not implemented");
355 * Unload an ISA DMA map.
358 isadma_bounce_dmamap_unload(bus_dma_tag_t t
, bus_dmamap_t map
)
360 struct isadma_bounce_cookie
*cookie
= map
->_dm_cookie
;
363 * If we have bounce pages, free them, unless they're
364 * reserved for our exclusive use.
366 if ((cookie
->id_flags
& ID_HAS_BOUNCE
) &&
367 (map
->_dm_flags
& BUS_DMA_ALLOCNOW
) == 0)
368 isadma_bounce_free_bouncebuf(t
, map
);
370 cookie
->id_flags
&= ~ID_IS_BOUNCING
;
371 cookie
->id_buftype
= ID_BUFTYPE_INVALID
;
374 * Do the generic bits of the unload.
376 _bus_dmamap_unload(t
, map
);
380 * Synchronize an ISA DMA map.
383 isadma_bounce_dmamap_sync(bus_dma_tag_t t
, bus_dmamap_t map
, bus_addr_t offset
,
384 bus_size_t len
, int ops
)
386 struct isadma_bounce_cookie
*cookie
= map
->_dm_cookie
;
389 * Mixing PRE and POST operations is not allowed.
391 if ((ops
& (BUS_DMASYNC_PREREAD
|BUS_DMASYNC_PREWRITE
)) != 0 &&
392 (ops
& (BUS_DMASYNC_POSTREAD
|BUS_DMASYNC_POSTWRITE
)) != 0)
393 panic("isadma_bounce_dmamap_sync: mix PRE and POST");
396 if ((ops
& (BUS_DMASYNC_PREWRITE
|BUS_DMASYNC_POSTREAD
)) != 0) {
397 if (offset
>= map
->dm_mapsize
)
398 panic("isadma_bounce_dmamap_sync: bad offset");
399 if (len
== 0 || (offset
+ len
) > map
->dm_mapsize
)
400 panic("isadma_bounce_dmamap_sync: bad length");
405 * If we're not bouncing, just do the normal sync operation
408 if ((cookie
->id_flags
& ID_IS_BOUNCING
) == 0) {
409 _bus_dmamap_sync(t
, map
, offset
, len
, ops
);
414 * Flush data cache for PREREAD. This has the side-effect
415 * of invalidating the cache. Done at PREREAD since it
416 * causes the cache line(s) to be written back to memory.
418 * Copy the original buffer to the bounce buffer and flush
419 * the data cache for PREWRITE, so that the contents
420 * of the data buffer in memory reflect reality.
422 * Copy the bounce buffer to the original buffer in POSTREAD.
425 switch (cookie
->id_buftype
) {
426 case ID_BUFTYPE_LINEAR
:
428 * Nothing to do for pre-read.
431 if (ops
& BUS_DMASYNC_PREWRITE
) {
433 * Copy the caller's buffer to the bounce buffer.
435 memcpy((char *)cookie
->id_bouncebuf
+ offset
,
436 (char *)cookie
->id_origbuf
+ offset
, len
);
440 if (ops
& BUS_DMASYNC_POSTREAD
) {
442 * Copy the bounce buffer to the caller's buffer.
444 memcpy((char *)cookie
->id_origbuf
+ offset
,
445 (char *)cookie
->id_bouncebuf
+ offset
, len
);
449 * Nothing to do for post-write.
453 case ID_BUFTYPE_MBUF
:
455 struct mbuf
*m
, *m0
= cookie
->id_origbuf
;
456 bus_size_t minlen
, moff
;
459 * Nothing to do for pre-read.
462 if (ops
& BUS_DMASYNC_PREWRITE
) {
464 * Copy the caller's buffer to the bounce buffer.
466 m_copydata(m0
, offset
, len
,
467 (char *)cookie
->id_bouncebuf
+ offset
);
470 if (ops
& BUS_DMASYNC_POSTREAD
) {
472 * Copy the bounce buffer to the caller's buffer.
474 for (moff
= offset
, m
= m0
; m
!= NULL
&& len
!= 0;
476 /* Find the beginning mbuf. */
477 if (moff
>= m
->m_len
) {
483 * Now at the first mbuf to sync; nail
484 * each one until we have exhausted the
487 minlen
= len
< m
->m_len
- moff
?
488 len
: m
->m_len
- moff
;
490 memcpy(mtod(m
, char *) + moff
,
491 (char *)cookie
->id_bouncebuf
+ offset
,
501 * Nothing to do for post-write.
507 panic("isadma_bounce_dmamap_sync: ID_BUFTYPE_UIO");
511 panic("isadma_bounce_dmamap_sync: ID_BUFTYPE_RAW");
514 case ID_BUFTYPE_INVALID
:
515 panic("isadma_bounce_dmamap_sync: ID_BUFTYPE_INVALID");
519 printf("unknown buffer type %d\n", cookie
->id_buftype
);
520 panic("isadma_bounce_dmamap_sync");
523 /* Drain the write buffer. */
527 if (ops
& (BUS_DMASYNC_PREREAD
|BUS_DMASYNC_PREWRITE
))
528 mips_dcache_wbinv_range((vaddr_t
)cookie
->id_bouncebuf
+ offset
,
533 * Allocate memory safe for ISA DMA.
536 isadma_bounce_dmamem_alloc(bus_dma_tag_t t
, bus_size_t size
,
537 bus_size_t alignment
, bus_size_t boundary
, bus_dma_segment_t
*segs
,
538 int nsegs
, int *rsegs
, int flags
)
542 if (avail_end
> ISA_DMA_BOUNCE_THRESHOLD
)
543 high
= trunc_page(ISA_DMA_BOUNCE_THRESHOLD
);
545 high
= trunc_page(avail_end
);
547 return (_bus_dmamem_alloc_range(t
, size
, alignment
, boundary
,
548 segs
, nsegs
, rsegs
, flags
, 0, high
));
551 /**********************************************************************
552 * ISA DMA utility functions
553 **********************************************************************/
556 isadma_bounce_alloc_bouncebuf(bus_dma_tag_t t
, bus_dmamap_t map
,
557 bus_size_t size
, int flags
)
559 struct isadma_bounce_cookie
*cookie
= map
->_dm_cookie
;
562 cookie
->id_bouncebuflen
= round_page(size
);
563 error
= isadma_bounce_dmamem_alloc(t
, cookie
->id_bouncebuflen
,
564 PAGE_SIZE
, map
->_dm_boundary
, cookie
->id_bouncesegs
,
565 map
->_dm_segcnt
, &cookie
->id_nbouncesegs
, flags
);
568 error
= _bus_dmamem_map(t
, cookie
->id_bouncesegs
,
569 cookie
->id_nbouncesegs
, cookie
->id_bouncebuflen
,
570 (void **)&cookie
->id_bouncebuf
, flags
);
574 _bus_dmamem_free(t
, cookie
->id_bouncesegs
,
575 cookie
->id_nbouncesegs
);
576 cookie
->id_bouncebuflen
= 0;
577 cookie
->id_nbouncesegs
= 0;
579 cookie
->id_flags
|= ID_HAS_BOUNCE
;
585 isadma_bounce_free_bouncebuf(bus_dma_tag_t t
, bus_dmamap_t map
)
587 struct isadma_bounce_cookie
*cookie
= map
->_dm_cookie
;
589 _bus_dmamem_unmap(t
, cookie
->id_bouncebuf
,
590 cookie
->id_bouncebuflen
);
591 _bus_dmamem_free(t
, cookie
->id_bouncesegs
,
592 cookie
->id_nbouncesegs
);
593 cookie
->id_bouncebuflen
= 0;
594 cookie
->id_nbouncesegs
= 0;
595 cookie
->id_flags
&= ~ID_HAS_BOUNCE
;