1 1.17 skrll /* $NetBSD: isadma_bounce.c,v 1.17 2022/01/22 15:10:30 skrll Exp $ */ 2 1.1 thorpej 3 1.1 thorpej /*- 4 1.1 thorpej * Copyright (c) 1996, 1997, 1998, 2000 The NetBSD Foundation, Inc. 5 1.1 thorpej * All rights reserved. 6 1.1 thorpej * 7 1.1 thorpej * This code is derived from software contributed to The NetBSD Foundation 8 1.1 thorpej * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 9 1.1 thorpej * NASA Ames Research Center. 10 1.1 thorpej * 11 1.1 thorpej * Redistribution and use in source and binary forms, with or without 12 1.1 thorpej * modification, are permitted provided that the following conditions 13 1.1 thorpej * are met: 14 1.1 thorpej * 1. Redistributions of source code must retain the above copyright 15 1.1 thorpej * notice, this list of conditions and the following disclaimer. 16 1.1 thorpej * 2. Redistributions in binary form must reproduce the above copyright 17 1.1 thorpej * notice, this list of conditions and the following disclaimer in the 18 1.1 thorpej * documentation and/or other materials provided with the distribution. 19 1.1 thorpej * 20 1.1 thorpej * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 1.1 thorpej * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 1.1 thorpej * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 1.1 thorpej * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 1.1 thorpej * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 1.1 thorpej * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 1.1 thorpej * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 1.1 thorpej * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 1.1 thorpej * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 1.1 thorpej * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 1.1 thorpej * POSSIBILITY OF SUCH DAMAGE. 31 1.1 thorpej */ 32 1.2 thorpej 33 1.2 thorpej #include <sys/cdefs.h> /* RCS ID & Copyright macro defns */ 34 1.2 thorpej 35 1.17 skrll __KERNEL_RCSID(0, "$NetBSD: isadma_bounce.c,v 1.17 2022/01/22 15:10:30 skrll Exp $"); 36 1.1 thorpej 37 1.1 thorpej #include <sys/param.h> 38 1.1 thorpej #include <sys/systm.h> 39 1.1 thorpej #include <sys/syslog.h> 40 1.1 thorpej #include <sys/device.h> 41 1.14 thorpej #include <sys/kmem.h> 42 1.1 thorpej #include <sys/proc.h> 43 1.1 thorpej #include <sys/mbuf.h> 44 1.1 thorpej 45 1.1 thorpej #define _ALPHA_BUS_DMA_PRIVATE 46 1.11 dyoung #include <sys/bus.h> 47 1.1 thorpej 48 1.1 thorpej #include <dev/isa/isareg.h> 49 1.1 thorpej #include <dev/isa/isavar.h> 50 1.1 thorpej 51 1.1 thorpej extern paddr_t avail_end; 52 1.1 thorpej 53 1.1 thorpej /* 54 1.1 thorpej * Cookie used by bouncing ISA DMA. A pointer to one of these is stashed 55 1.1 thorpej * in the DMA map. 56 1.1 thorpej */ 57 1.1 thorpej struct isadma_bounce_cookie { 58 1.1 thorpej int id_flags; /* flags; see below */ 59 1.1 thorpej 60 1.1 thorpej /* 61 1.1 thorpej * Information about the original buffer used during 62 1.1 thorpej * DMA map syncs. Note that origbuflen is only used 63 1.1 thorpej * for ID_BUFTYPE_LINEAR. 64 1.1 thorpej */ 65 1.1 thorpej void *id_origbuf; /* pointer to orig buffer if 66 1.1 thorpej bouncing */ 67 1.1 thorpej bus_size_t id_origbuflen; /* ...and size */ 68 1.1 thorpej int id_buftype; /* type of buffer */ 69 1.1 thorpej 70 1.1 thorpej void *id_bouncebuf; /* pointer to the bounce buffer */ 71 1.1 thorpej bus_size_t id_bouncebuflen; /* ...and size */ 72 1.1 thorpej int id_nbouncesegs; /* number of valid bounce segs */ 73 1.1 thorpej bus_dma_segment_t id_bouncesegs[1]; /* array of bounce buffer 74 1.1 thorpej physical memory segments */ 75 1.1 thorpej }; 76 1.1 thorpej 77 1.1 thorpej /* id_flags */ 78 1.1 thorpej #define ID_MIGHT_NEED_BOUNCE 0x01 /* map could need bounce buffers */ 79 1.1 thorpej #define ID_HAS_BOUNCE 0x02 /* map currently has bounce buffers */ 80 1.1 thorpej #define ID_IS_BOUNCING 0x04 /* map is bouncing current xfer */ 81 1.1 thorpej 82 1.1 thorpej /* id_buftype */ 83 1.1 thorpej #define ID_BUFTYPE_INVALID 0 84 1.1 thorpej #define ID_BUFTYPE_LINEAR 1 85 1.1 thorpej #define ID_BUFTYPE_MBUF 2 86 1.1 thorpej #define ID_BUFTYPE_UIO 3 87 1.1 thorpej #define ID_BUFTYPE_RAW 4 88 1.1 thorpej 89 1.15 thorpej static int isadma_bounce_alloc_bouncebuf(bus_dma_tag_t, bus_dmamap_t, 90 1.15 thorpej bus_size_t, int); 91 1.15 thorpej static void isadma_bounce_free_bouncebuf(bus_dma_tag_t, bus_dmamap_t); 92 1.1 thorpej 93 1.1 thorpej /* 94 1.14 thorpej * Returns true if the system memory configuration exceeds the 95 1.14 thorpej * capabilities of ISA DMA. 96 1.1 thorpej */ 97 1.14 thorpej static bool 98 1.14 thorpej isadma_bounce_check_range(bus_dma_tag_t const t) 99 1.1 thorpej { 100 1.14 thorpej return avail_end > (t->_wbase + t->_wsize); 101 1.14 thorpej } 102 1.1 thorpej 103 1.14 thorpej static int 104 1.14 thorpej isadma_bounce_cookieflags(bus_dma_tag_t const t, bus_dmamap_t const map) 105 1.14 thorpej { 106 1.14 thorpej int cookieflags = 0; 107 1.1 thorpej 108 1.1 thorpej /* 109 1.1 thorpej * ISA only has 24-bits of address space. This means 110 1.1 thorpej * we can't DMA to pages over 16M. In order to DMA to 111 1.1 thorpej * arbitrary buffers, we use "bounce buffers" - pages 112 1.1 thorpej * in memory below the 16M boundary. On DMA reads, 113 1.1 thorpej * DMA happens to the bounce buffers, and is copied into 114 1.1 thorpej * the caller's buffer. On writes, data is copied into 115 1.16 skrll * the bounce buffer, and the DMA happens from those 116 1.1 thorpej * pages. To software using the DMA mapping interface, 117 1.1 thorpej * this looks simply like a data cache. 118 1.1 thorpej * 119 1.1 thorpej * If we have more than 16M of RAM in the system, we may 120 1.1 thorpej * need bounce buffers. We check and remember that here. 121 1.1 thorpej * 122 1.1 thorpej * ...or, there is an opposite case. The most segments 123 1.1 thorpej * a transfer will require is (maxxfer / PAGE_SIZE) + 1. If 124 1.1 thorpej * the caller can't handle that many segments (e.g. the 125 1.1 thorpej * ISA DMA controller), we may have to bounce it as well. 126 1.1 thorpej */ 127 1.14 thorpej if (isadma_bounce_check_range(t) || 128 1.1 thorpej ((map->_dm_size / PAGE_SIZE) + 1) > map->_dm_segcnt) { 129 1.1 thorpej cookieflags |= ID_MIGHT_NEED_BOUNCE; 130 1.14 thorpej } 131 1.14 thorpej return cookieflags; 132 1.14 thorpej } 133 1.14 thorpej 134 1.14 thorpej static size_t 135 1.14 thorpej isadma_bounce_cookiesize(bus_dmamap_t const map, int cookieflags) 136 1.14 thorpej { 137 1.14 thorpej size_t cookiesize = sizeof(struct isadma_bounce_cookie); 138 1.14 thorpej 139 1.14 thorpej if (cookieflags & ID_MIGHT_NEED_BOUNCE) { 140 1.1 thorpej cookiesize += (sizeof(bus_dma_segment_t) * 141 1.1 thorpej (map->_dm_segcnt - 1)); 142 1.1 thorpej } 143 1.14 thorpej return cookiesize; 144 1.14 thorpej } 145 1.14 thorpej 146 1.14 thorpej static int 147 1.14 thorpej isadma_bounce_cookie_alloc(bus_dma_tag_t const t, bus_dmamap_t const map, 148 1.14 thorpej int const flags) 149 1.14 thorpej { 150 1.14 thorpej struct isadma_bounce_cookie *cookie; 151 1.14 thorpej int cookieflags = isadma_bounce_cookieflags(t, map); 152 1.14 thorpej 153 1.14 thorpej if ((cookie = kmem_zalloc(isadma_bounce_cookiesize(map, cookieflags), 154 1.14 thorpej (flags & BUS_DMA_NOWAIT) ? KM_NOSLEEP : KM_SLEEP)) == NULL) { 155 1.14 thorpej return ENOMEM; 156 1.14 thorpej } 157 1.14 thorpej 158 1.14 thorpej cookie->id_flags = cookieflags; 159 1.14 thorpej map->_dm_cookie = cookie; 160 1.14 thorpej 161 1.14 thorpej return 0; 162 1.14 thorpej } 163 1.14 thorpej 164 1.14 thorpej static void 165 1.14 thorpej isadma_bounce_cookie_free(bus_dmamap_t const map) 166 1.14 thorpej { 167 1.14 thorpej struct isadma_bounce_cookie *cookie = map->_dm_cookie; 168 1.14 thorpej 169 1.14 thorpej if (cookie != NULL) { 170 1.14 thorpej kmem_free(map->_dm_cookie, 171 1.14 thorpej isadma_bounce_cookiesize(map, cookie->id_flags)); 172 1.14 thorpej map->_dm_cookie = NULL; 173 1.14 thorpej } 174 1.14 thorpej } 175 1.14 thorpej 176 1.14 thorpej /* 177 1.14 thorpej * Create an ISA DMA map. 178 1.14 thorpej */ 179 1.14 thorpej int 180 1.14 thorpej isadma_bounce_dmamap_create(bus_dma_tag_t t, bus_size_t size, int nsegments, 181 1.14 thorpej bus_size_t maxsegsz, bus_size_t boundary, int flags, bus_dmamap_t *dmamp) 182 1.14 thorpej { 183 1.14 thorpej struct isadma_bounce_cookie *cookie; 184 1.14 thorpej bus_dmamap_t map; 185 1.14 thorpej int error; 186 1.14 thorpej 187 1.14 thorpej /* Call common function to create the basic map. */ 188 1.14 thorpej error = _bus_dmamap_create(t, size, nsegments, maxsegsz, boundary, 189 1.14 thorpej flags, dmamp); 190 1.14 thorpej if (error) 191 1.14 thorpej return (error); 192 1.14 thorpej 193 1.14 thorpej map = *dmamp; 194 1.14 thorpej map->_dm_cookie = NULL; 195 1.1 thorpej 196 1.1 thorpej /* 197 1.1 thorpej * Allocate our cookie. 198 1.1 thorpej */ 199 1.14 thorpej if ((error = isadma_bounce_cookie_alloc(t, map, flags)) != 0) { 200 1.1 thorpej goto out; 201 1.1 thorpej } 202 1.14 thorpej cookie = map->_dm_cookie; 203 1.1 thorpej 204 1.14 thorpej if (cookie->id_flags & ID_MIGHT_NEED_BOUNCE) { 205 1.1 thorpej /* 206 1.1 thorpej * Allocate the bounce pages now if the caller 207 1.1 thorpej * wishes us to do so. 208 1.1 thorpej */ 209 1.14 thorpej if (flags & BUS_DMA_ALLOCNOW) { 210 1.14 thorpej error = isadma_bounce_alloc_bouncebuf(t, map, size, 211 1.14 thorpej flags); 212 1.14 thorpej } 213 1.1 thorpej } 214 1.1 thorpej 215 1.1 thorpej out: 216 1.1 thorpej if (error) { 217 1.14 thorpej isadma_bounce_cookie_free(map); 218 1.1 thorpej _bus_dmamap_destroy(t, map); 219 1.1 thorpej } 220 1.1 thorpej return (error); 221 1.1 thorpej } 222 1.1 thorpej 223 1.1 thorpej /* 224 1.1 thorpej * Destroy an ISA DMA map. 225 1.1 thorpej */ 226 1.1 thorpej void 227 1.1 thorpej isadma_bounce_dmamap_destroy(bus_dma_tag_t t, bus_dmamap_t map) 228 1.1 thorpej { 229 1.1 thorpej struct isadma_bounce_cookie *cookie = map->_dm_cookie; 230 1.1 thorpej 231 1.1 thorpej /* 232 1.1 thorpej * Free any bounce pages this map might hold. 233 1.1 thorpej */ 234 1.1 thorpej if (cookie->id_flags & ID_HAS_BOUNCE) 235 1.1 thorpej isadma_bounce_free_bouncebuf(t, map); 236 1.1 thorpej 237 1.14 thorpej isadma_bounce_cookie_free(map); 238 1.1 thorpej _bus_dmamap_destroy(t, map); 239 1.1 thorpej } 240 1.1 thorpej 241 1.1 thorpej /* 242 1.1 thorpej * Load an ISA DMA map with a linear buffer. 243 1.1 thorpej */ 244 1.1 thorpej int 245 1.1 thorpej isadma_bounce_dmamap_load(bus_dma_tag_t t, bus_dmamap_t map, void *buf, 246 1.1 thorpej size_t buflen, struct proc *p, int flags) 247 1.1 thorpej { 248 1.1 thorpej struct isadma_bounce_cookie *cookie = map->_dm_cookie; 249 1.1 thorpej int error; 250 1.1 thorpej 251 1.1 thorpej /* 252 1.1 thorpej * Make sure that on error condition we return "no valid mappings." 253 1.1 thorpej */ 254 1.1 thorpej map->dm_mapsize = 0; 255 1.1 thorpej map->dm_nsegs = 0; 256 1.1 thorpej 257 1.1 thorpej /* 258 1.1 thorpej * Try to load the map the normal way. If this errors out, 259 1.1 thorpej * and we can bounce, we will. 260 1.1 thorpej */ 261 1.1 thorpej error = _bus_dmamap_load_direct(t, map, buf, buflen, p, flags); 262 1.13 christos if (error == 0 || (cookie->id_flags & ID_MIGHT_NEED_BOUNCE) == 0) 263 1.1 thorpej return (error); 264 1.1 thorpej 265 1.1 thorpej /* 266 1.1 thorpej * First attempt failed; bounce it. 267 1.1 thorpej */ 268 1.1 thorpej 269 1.1 thorpej /* 270 1.1 thorpej * Allocate bounce pages, if necessary. 271 1.1 thorpej */ 272 1.1 thorpej if ((cookie->id_flags & ID_HAS_BOUNCE) == 0) { 273 1.1 thorpej error = isadma_bounce_alloc_bouncebuf(t, map, buflen, flags); 274 1.1 thorpej if (error) 275 1.1 thorpej return (error); 276 1.1 thorpej } 277 1.1 thorpej 278 1.1 thorpej /* 279 1.1 thorpej * Cache a pointer to the caller's buffer and load the DMA map 280 1.1 thorpej * with the bounce buffer. 281 1.1 thorpej */ 282 1.1 thorpej cookie->id_origbuf = buf; 283 1.1 thorpej cookie->id_origbuflen = buflen; 284 1.1 thorpej cookie->id_buftype = ID_BUFTYPE_LINEAR; 285 1.1 thorpej error = _bus_dmamap_load_direct(t, map, cookie->id_bouncebuf, buflen, 286 1.1 thorpej p, flags); 287 1.1 thorpej if (error) { 288 1.1 thorpej /* 289 1.1 thorpej * Free the bounce pages, unless our resources 290 1.1 thorpej * are reserved for our exclusive use. 291 1.1 thorpej */ 292 1.1 thorpej if ((map->_dm_flags & BUS_DMA_ALLOCNOW) == 0) 293 1.1 thorpej isadma_bounce_free_bouncebuf(t, map); 294 1.1 thorpej return (error); 295 1.1 thorpej } 296 1.1 thorpej 297 1.1 thorpej /* ...so isadma_bounce_dmamap_sync() knows we're bouncing */ 298 1.1 thorpej cookie->id_flags |= ID_IS_BOUNCING; 299 1.4 thorpej map->_dm_window = t; 300 1.1 thorpej return (0); 301 1.1 thorpej } 302 1.1 thorpej 303 1.1 thorpej /* 304 1.1 thorpej * Like isadma_bounce_dmamap_load(), but for mbufs. 305 1.1 thorpej */ 306 1.1 thorpej int 307 1.1 thorpej isadma_bounce_dmamap_load_mbuf(bus_dma_tag_t t, bus_dmamap_t map, 308 1.12 matt struct mbuf *m0, int flags) 309 1.1 thorpej { 310 1.1 thorpej struct isadma_bounce_cookie *cookie = map->_dm_cookie; 311 1.1 thorpej int error; 312 1.1 thorpej 313 1.1 thorpej /* 314 1.1 thorpej * Make sure on error condition we return "no valid mappings." 315 1.1 thorpej */ 316 1.1 thorpej map->dm_mapsize = 0; 317 1.1 thorpej map->dm_nsegs = 0; 318 1.1 thorpej 319 1.1 thorpej #ifdef DIAGNOSTIC 320 1.1 thorpej if ((m0->m_flags & M_PKTHDR) == 0) 321 1.1 thorpej panic("isadma_bounce_dmamap_load_mbuf: no packet header"); 322 1.1 thorpej #endif 323 1.1 thorpej 324 1.1 thorpej if (m0->m_pkthdr.len > map->_dm_size) 325 1.1 thorpej return (EINVAL); 326 1.1 thorpej 327 1.1 thorpej /* 328 1.1 thorpej * Try to load the map the normal way. If this errors out, 329 1.1 thorpej * and we can bounce, we will. 330 1.1 thorpej */ 331 1.1 thorpej error = _bus_dmamap_load_mbuf_direct(t, map, m0, flags); 332 1.13 christos if (error == 0 || (cookie->id_flags & ID_MIGHT_NEED_BOUNCE) == 0) 333 1.1 thorpej return (error); 334 1.1 thorpej 335 1.1 thorpej /* 336 1.1 thorpej * First attempt failed; bounce it. 337 1.1 thorpej */ 338 1.1 thorpej 339 1.1 thorpej /* 340 1.1 thorpej * Allocate bounce pages, if necessary. 341 1.1 thorpej */ 342 1.1 thorpej if ((cookie->id_flags & ID_HAS_BOUNCE) == 0) { 343 1.1 thorpej error = isadma_bounce_alloc_bouncebuf(t, map, m0->m_pkthdr.len, 344 1.1 thorpej flags); 345 1.1 thorpej if (error) 346 1.1 thorpej return (error); 347 1.1 thorpej } 348 1.1 thorpej 349 1.1 thorpej /* 350 1.1 thorpej * Cache a pointer to the caller's buffer and load the DMA map 351 1.1 thorpej * with the bounce buffer. 352 1.1 thorpej */ 353 1.1 thorpej cookie->id_origbuf = m0; 354 1.1 thorpej cookie->id_origbuflen = m0->m_pkthdr.len; /* not really used */ 355 1.1 thorpej cookie->id_buftype = ID_BUFTYPE_MBUF; 356 1.1 thorpej error = _bus_dmamap_load_direct(t, map, cookie->id_bouncebuf, 357 1.1 thorpej m0->m_pkthdr.len, NULL, flags); 358 1.1 thorpej if (error) { 359 1.1 thorpej /* 360 1.1 thorpej * Free the bounce pages, unless our resources 361 1.1 thorpej * are reserved for our exclusive use. 362 1.1 thorpej */ 363 1.1 thorpej if ((map->_dm_flags & BUS_DMA_ALLOCNOW) == 0) 364 1.1 thorpej isadma_bounce_free_bouncebuf(t, map); 365 1.1 thorpej return (error); 366 1.1 thorpej } 367 1.1 thorpej 368 1.1 thorpej /* ...so isadma_bounce_dmamap_sync() knows we're bouncing */ 369 1.1 thorpej cookie->id_flags |= ID_IS_BOUNCING; 370 1.4 thorpej map->_dm_window = t; 371 1.1 thorpej return (0); 372 1.1 thorpej } 373 1.1 thorpej 374 1.1 thorpej /* 375 1.1 thorpej * Like isadma_bounce_dmamap_load(), but for uios. 376 1.1 thorpej */ 377 1.1 thorpej int 378 1.1 thorpej isadma_bounce_dmamap_load_uio(bus_dma_tag_t t, bus_dmamap_t map, 379 1.1 thorpej struct uio *uio, int flags) 380 1.1 thorpej { 381 1.1 thorpej 382 1.1 thorpej panic("isadma_bounce_dmamap_load_uio: not implemented"); 383 1.1 thorpej } 384 1.1 thorpej 385 1.1 thorpej /* 386 1.1 thorpej * Like isadma_bounce_dmamap_load(), but for raw memory allocated with 387 1.1 thorpej * bus_dmamem_alloc(). 388 1.1 thorpej */ 389 1.1 thorpej int 390 1.1 thorpej isadma_bounce_dmamap_load_raw(bus_dma_tag_t t, bus_dmamap_t map, 391 1.1 thorpej bus_dma_segment_t *segs, int nsegs, bus_size_t size, int flags) 392 1.1 thorpej { 393 1.1 thorpej 394 1.1 thorpej panic("isadma_bounce_dmamap_load_raw: not implemented"); 395 1.1 thorpej } 396 1.1 thorpej 397 1.1 thorpej /* 398 1.1 thorpej * Unload an ISA DMA map. 399 1.1 thorpej */ 400 1.1 thorpej void 401 1.1 thorpej isadma_bounce_dmamap_unload(bus_dma_tag_t t, bus_dmamap_t map) 402 1.1 thorpej { 403 1.1 thorpej struct isadma_bounce_cookie *cookie = map->_dm_cookie; 404 1.1 thorpej 405 1.1 thorpej /* 406 1.1 thorpej * If we have bounce pages, free them, unless they're 407 1.1 thorpej * reserved for our exclusive use. 408 1.1 thorpej */ 409 1.1 thorpej if ((cookie->id_flags & ID_HAS_BOUNCE) && 410 1.1 thorpej (map->_dm_flags & BUS_DMA_ALLOCNOW) == 0) 411 1.1 thorpej isadma_bounce_free_bouncebuf(t, map); 412 1.1 thorpej 413 1.1 thorpej cookie->id_flags &= ~ID_IS_BOUNCING; 414 1.1 thorpej cookie->id_buftype = ID_BUFTYPE_INVALID; 415 1.1 thorpej 416 1.1 thorpej /* 417 1.1 thorpej * Do the generic bits of the unload. 418 1.1 thorpej */ 419 1.1 thorpej _bus_dmamap_unload(t, map); 420 1.1 thorpej } 421 1.1 thorpej 422 1.1 thorpej /* 423 1.1 thorpej * Synchronize an ISA DMA map. 424 1.1 thorpej */ 425 1.1 thorpej void 426 1.1 thorpej isadma_bounce_dmamap_sync(bus_dma_tag_t t, bus_dmamap_t map, bus_addr_t offset, 427 1.1 thorpej bus_size_t len, int ops) 428 1.1 thorpej { 429 1.1 thorpej struct isadma_bounce_cookie *cookie = map->_dm_cookie; 430 1.1 thorpej 431 1.1 thorpej /* 432 1.1 thorpej * Mixing PRE and POST operations is not allowed. 433 1.1 thorpej */ 434 1.1 thorpej if ((ops & (BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE)) != 0 && 435 1.1 thorpej (ops & (BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE)) != 0) 436 1.1 thorpej panic("isadma_bounce_dmamap_sync: mix PRE and POST"); 437 1.1 thorpej 438 1.1 thorpej #ifdef DIAGNOSTIC 439 1.1 thorpej if ((ops & (BUS_DMASYNC_PREWRITE|BUS_DMASYNC_POSTREAD)) != 0) { 440 1.1 thorpej if (offset >= map->dm_mapsize) 441 1.1 thorpej panic("isadma_bounce_dmamap_sync: bad offset"); 442 1.1 thorpej if (len == 0 || (offset + len) > map->dm_mapsize) 443 1.1 thorpej panic("isadma_bounce_dmamap_sync: bad length"); 444 1.1 thorpej } 445 1.1 thorpej #endif 446 1.1 thorpej 447 1.1 thorpej /* 448 1.1 thorpej * If we're not bouncing, just drain the write buffer 449 1.1 thorpej * and return. 450 1.1 thorpej */ 451 1.1 thorpej if ((cookie->id_flags & ID_IS_BOUNCING) == 0) { 452 1.1 thorpej alpha_mb(); 453 1.1 thorpej return; 454 1.1 thorpej } 455 1.1 thorpej 456 1.1 thorpej switch (cookie->id_buftype) { 457 1.1 thorpej case ID_BUFTYPE_LINEAR: 458 1.1 thorpej /* 459 1.1 thorpej * Nothing to do for pre-read. 460 1.1 thorpej */ 461 1.1 thorpej 462 1.1 thorpej if (ops & BUS_DMASYNC_PREWRITE) { 463 1.1 thorpej /* 464 1.1 thorpej * Copy the caller's buffer to the bounce buffer. 465 1.1 thorpej */ 466 1.1 thorpej memcpy((char *)cookie->id_bouncebuf + offset, 467 1.1 thorpej (char *)cookie->id_origbuf + offset, len); 468 1.1 thorpej } 469 1.1 thorpej 470 1.1 thorpej if (ops & BUS_DMASYNC_POSTREAD) { 471 1.1 thorpej /* 472 1.1 thorpej * Copy the bounce buffer to the caller's buffer. 473 1.1 thorpej */ 474 1.1 thorpej memcpy((char *)cookie->id_origbuf + offset, 475 1.1 thorpej (char *)cookie->id_bouncebuf + offset, len); 476 1.1 thorpej } 477 1.1 thorpej 478 1.1 thorpej /* 479 1.1 thorpej * Nothing to do for post-write. 480 1.1 thorpej */ 481 1.1 thorpej break; 482 1.1 thorpej 483 1.1 thorpej case ID_BUFTYPE_MBUF: 484 1.1 thorpej { 485 1.1 thorpej struct mbuf *m, *m0 = cookie->id_origbuf; 486 1.1 thorpej bus_size_t minlen, moff; 487 1.1 thorpej 488 1.1 thorpej /* 489 1.1 thorpej * Nothing to do for pre-read. 490 1.1 thorpej */ 491 1.1 thorpej 492 1.1 thorpej if (ops & BUS_DMASYNC_PREWRITE) { 493 1.1 thorpej /* 494 1.1 thorpej * Copy the caller's buffer to the bounce buffer. 495 1.1 thorpej */ 496 1.1 thorpej m_copydata(m0, offset, len, 497 1.1 thorpej (char *)cookie->id_bouncebuf + offset); 498 1.1 thorpej } 499 1.1 thorpej 500 1.1 thorpej if (ops & BUS_DMASYNC_POSTREAD) { 501 1.1 thorpej /* 502 1.1 thorpej * Copy the bounce buffer to the caller's buffer. 503 1.1 thorpej */ 504 1.1 thorpej for (moff = offset, m = m0; m != NULL && len != 0; 505 1.1 thorpej m = m->m_next) { 506 1.1 thorpej /* Find the beginning mbuf. */ 507 1.1 thorpej if (moff >= m->m_len) { 508 1.1 thorpej moff -= m->m_len; 509 1.1 thorpej continue; 510 1.1 thorpej } 511 1.1 thorpej 512 1.1 thorpej /* 513 1.1 thorpej * Now at the first mbuf to sync; nail 514 1.1 thorpej * each one until we have exhausted the 515 1.1 thorpej * length. 516 1.1 thorpej */ 517 1.1 thorpej minlen = len < m->m_len - moff ? 518 1.1 thorpej len : m->m_len - moff; 519 1.1 thorpej 520 1.7 yamt memcpy(mtod(m, char *) + moff, 521 1.1 thorpej (char *)cookie->id_bouncebuf + offset, 522 1.1 thorpej minlen); 523 1.1 thorpej 524 1.1 thorpej moff = 0; 525 1.1 thorpej len -= minlen; 526 1.1 thorpej offset += minlen; 527 1.1 thorpej } 528 1.1 thorpej } 529 1.1 thorpej 530 1.1 thorpej /* 531 1.1 thorpej * Nothing to do for post-write. 532 1.1 thorpej */ 533 1.1 thorpej break; 534 1.1 thorpej } 535 1.1 thorpej 536 1.1 thorpej case ID_BUFTYPE_UIO: 537 1.1 thorpej panic("isadma_bounce_dmamap_sync: ID_BUFTYPE_UIO"); 538 1.1 thorpej break; 539 1.1 thorpej 540 1.1 thorpej case ID_BUFTYPE_RAW: 541 1.1 thorpej panic("isadma_bounce_dmamap_sync: ID_BUFTYPE_RAW"); 542 1.1 thorpej break; 543 1.1 thorpej 544 1.1 thorpej case ID_BUFTYPE_INVALID: 545 1.1 thorpej panic("isadma_bounce_dmamap_sync: ID_BUFTYPE_INVALID"); 546 1.1 thorpej break; 547 1.1 thorpej 548 1.1 thorpej default: 549 1.1 thorpej printf("unknown buffer type %d\n", cookie->id_buftype); 550 1.1 thorpej panic("isadma_bounce_dmamap_sync"); 551 1.1 thorpej } 552 1.1 thorpej 553 1.1 thorpej /* Drain the write buffer. */ 554 1.1 thorpej alpha_mb(); 555 1.1 thorpej } 556 1.1 thorpej 557 1.1 thorpej /* 558 1.1 thorpej * Allocate memory safe for ISA DMA. 559 1.1 thorpej */ 560 1.1 thorpej int 561 1.1 thorpej isadma_bounce_dmamem_alloc(bus_dma_tag_t t, bus_size_t size, 562 1.1 thorpej bus_size_t alignment, bus_size_t boundary, bus_dma_segment_t *segs, 563 1.1 thorpej int nsegs, int *rsegs, int flags) 564 1.1 thorpej { 565 1.1 thorpej paddr_t high; 566 1.1 thorpej 567 1.1 thorpej if (avail_end > ISA_DMA_BOUNCE_THRESHOLD) 568 1.17 skrll high = ISA_DMA_BOUNCE_THRESHOLD - 1; 569 1.1 thorpej else 570 1.17 skrll high = avail_end - 1; 571 1.1 thorpej 572 1.1 thorpej return (_bus_dmamem_alloc_range(t, size, alignment, boundary, 573 1.1 thorpej segs, nsegs, rsegs, flags, 0, high)); 574 1.1 thorpej } 575 1.1 thorpej 576 1.1 thorpej /********************************************************************** 577 1.1 thorpej * ISA DMA utility functions 578 1.1 thorpej **********************************************************************/ 579 1.1 thorpej 580 1.15 thorpej static int 581 1.1 thorpej isadma_bounce_alloc_bouncebuf(bus_dma_tag_t t, bus_dmamap_t map, 582 1.1 thorpej bus_size_t size, int flags) 583 1.1 thorpej { 584 1.1 thorpej struct isadma_bounce_cookie *cookie = map->_dm_cookie; 585 1.1 thorpej int error = 0; 586 1.1 thorpej 587 1.1 thorpej cookie->id_bouncebuflen = round_page(size); 588 1.1 thorpej error = isadma_bounce_dmamem_alloc(t, cookie->id_bouncebuflen, 589 1.1 thorpej PAGE_SIZE, map->_dm_boundary, cookie->id_bouncesegs, 590 1.1 thorpej map->_dm_segcnt, &cookie->id_nbouncesegs, flags); 591 1.1 thorpej if (error) 592 1.1 thorpej goto out; 593 1.1 thorpej error = _bus_dmamem_map(t, cookie->id_bouncesegs, 594 1.1 thorpej cookie->id_nbouncesegs, cookie->id_bouncebuflen, 595 1.6 christos (void **)&cookie->id_bouncebuf, flags); 596 1.1 thorpej 597 1.1 thorpej out: 598 1.1 thorpej if (error) { 599 1.1 thorpej _bus_dmamem_free(t, cookie->id_bouncesegs, 600 1.1 thorpej cookie->id_nbouncesegs); 601 1.1 thorpej cookie->id_bouncebuflen = 0; 602 1.1 thorpej cookie->id_nbouncesegs = 0; 603 1.1 thorpej } else 604 1.1 thorpej cookie->id_flags |= ID_HAS_BOUNCE; 605 1.1 thorpej 606 1.1 thorpej return (error); 607 1.1 thorpej } 608 1.1 thorpej 609 1.15 thorpej static void 610 1.1 thorpej isadma_bounce_free_bouncebuf(bus_dma_tag_t t, bus_dmamap_t map) 611 1.1 thorpej { 612 1.1 thorpej struct isadma_bounce_cookie *cookie = map->_dm_cookie; 613 1.1 thorpej 614 1.1 thorpej _bus_dmamem_unmap(t, cookie->id_bouncebuf, 615 1.1 thorpej cookie->id_bouncebuflen); 616 1.1 thorpej _bus_dmamem_free(t, cookie->id_bouncesegs, 617 1.1 thorpej cookie->id_nbouncesegs); 618 1.1 thorpej cookie->id_bouncebuflen = 0; 619 1.1 thorpej cookie->id_nbouncesegs = 0; 620 1.1 thorpej cookie->id_flags &= ~ID_HAS_BOUNCE; 621 1.1 thorpej } 622