1 /* $NetBSD: isadma_bounce.c,v 1.15 2022/01/22 15:10:31 skrll Exp $ */ 2 3 /*- 4 * Copyright (c) 1996, 1997, 1998, 2000, 2001 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 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. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 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. 19 * 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. 31 */ 32 33 #include <sys/cdefs.h> 34 __KERNEL_RCSID(0, "$NetBSD: isadma_bounce.c,v 1.15 2022/01/22 15:10:31 skrll Exp $"); 35 36 #define _MIPS_BUS_DMA_PRIVATE 37 38 #include <sys/param.h> 39 #include <sys/bus.h> 40 #include <sys/device.h> 41 #include <sys/malloc.h> 42 #include <sys/mbuf.h> 43 #include <sys/proc.h> 44 #include <sys/systm.h> 45 46 #include <mips/cache.h> 47 #include <mips/locore.h> 48 49 #include <dev/isa/isareg.h> 50 #include <dev/isa/isavar.h> 51 52 #include <uvm/uvm_extern.h> 53 54 int isadma_bounce_alloc_bouncebuf(bus_dma_tag_t, bus_dmamap_t, 55 bus_size_t, int); 56 void isadma_bounce_free_bouncebuf(bus_dma_tag_t, bus_dmamap_t); 57 58 /* 59 * Create an ISA DMA map. 60 */ 61 int 62 isadma_bounce_dmamap_create(bus_dma_tag_t t, bus_size_t size, int nsegments, 63 bus_size_t maxsegsz, bus_size_t boundary, int flags, bus_dmamap_t *dmamp) 64 { 65 struct mips_bus_dma_cookie *cookie; 66 bus_dmamap_t map; 67 int error, cookieflags; 68 void *cookiestore; 69 size_t cookiesize; 70 71 /* Call common function to create the basic map. */ 72 error = _bus_dmamap_create(t, size, nsegments, maxsegsz, boundary, 73 flags, dmamp); 74 if (error) 75 return (error); 76 77 map = *dmamp; 78 map->_dm_cookie = NULL; 79 80 cookiesize = sizeof(*cookie); 81 82 /* 83 * ISA only has 24-bits of address space. This means 84 * we can't DMA to pages over 16M. In order to DMA to 85 * arbitrary buffers, we use "bounce buffers" - pages 86 * in memory below the 16M boundary. On DMA reads, 87 * DMA happens to the bounce buffers, and is copied into 88 * the caller's buffer. On writes, data is copied into 89 * the bounce buffer, and the DMA happens from those 90 * pages. To software using the DMA mapping interface, 91 * this looks simply like a data cache. 92 * 93 * If we have more than 16M of RAM in the system, we may 94 * need bounce buffers. We check and remember that here. 95 * 96 * ...or, there is an opposite case. The most segments 97 * a transfer will require is (maxxfer / PAGE_SIZE) + 1. If 98 * the caller can't handle that many segments (e.g. the 99 * ISA DMA controller), we may have to bounce it as well. 100 */ 101 cookieflags = 0; 102 if (_BUS_AVAIL_END > (t->_wbase + t->_bounce_alloc_hi - t->_bounce_alloc_lo - 1) 103 || ((map->_dm_size / PAGE_SIZE) + 1) > map->_dm_segcnt) { 104 cookieflags |= _BUS_DMA_MIGHT_NEED_BOUNCE; 105 cookiesize += (sizeof(bus_dma_segment_t) * 106 (map->_dm_segcnt - 1)); 107 } 108 109 /* 110 * Allocate our cookie. 111 */ 112 if ((cookiestore = malloc(cookiesize, M_DMAMAP, 113 (flags & BUS_DMA_NOWAIT) ? M_NOWAIT : M_WAITOK)) == NULL) { 114 error = ENOMEM; 115 goto out; 116 } 117 memset(cookiestore, 0, cookiesize); 118 cookie = (struct mips_bus_dma_cookie *)cookiestore; 119 cookie->id_flags = cookieflags; 120 map->_dm_cookie = cookie; 121 122 if (cookieflags & _BUS_DMA_MIGHT_NEED_BOUNCE) { 123 /* 124 * Allocate the bounce pages now if the caller 125 * wishes us to do so. 126 */ 127 if ((flags & BUS_DMA_ALLOCNOW) == 0) 128 goto out; 129 130 error = isadma_bounce_alloc_bouncebuf(t, map, size, flags); 131 } 132 133 out: 134 if (error) { 135 if (map->_dm_cookie != NULL) 136 free(map->_dm_cookie, M_DMAMAP); 137 _bus_dmamap_destroy(t, map); 138 } 139 return (error); 140 } 141 142 /* 143 * Destroy an ISA DMA map. 144 */ 145 void 146 isadma_bounce_dmamap_destroy(bus_dma_tag_t t, bus_dmamap_t map) 147 { 148 struct mips_bus_dma_cookie *cookie = map->_dm_cookie; 149 150 /* 151 * Free any bounce pages this map might hold. 152 */ 153 if (cookie->id_flags & _BUS_DMA_HAS_BOUNCE) 154 isadma_bounce_free_bouncebuf(t, map); 155 156 free(cookie, M_DMAMAP); 157 _bus_dmamap_destroy(t, map); 158 } 159 160 /* 161 * Load an ISA DMA map with a linear buffer. 162 */ 163 int 164 isadma_bounce_dmamap_load(bus_dma_tag_t t, bus_dmamap_t map, void *buf, 165 bus_size_t buflen, struct proc *p, int flags) 166 { 167 struct mips_bus_dma_cookie *cookie = map->_dm_cookie; 168 int error; 169 170 /* 171 * Make sure that on error condition we return "no valid mappings." 172 */ 173 map->dm_mapsize = 0; 174 map->dm_nsegs = 0; 175 176 /* 177 * Try to load the map the normal way. If this errors out, 178 * and we can bounce, we will. 179 */ 180 error = _bus_dmamap_load(t, map, buf, buflen, p, flags); 181 if (error == 0 || (cookie->id_flags & _BUS_DMA_MIGHT_NEED_BOUNCE) == 0) 182 return (error); 183 184 /* 185 * First attempt failed; bounce it. 186 */ 187 188 /* 189 * Allocate bounce pages, if necessary. 190 */ 191 if ((cookie->id_flags & _BUS_DMA_HAS_BOUNCE) == 0) { 192 error = isadma_bounce_alloc_bouncebuf(t, map, buflen, flags); 193 if (error) 194 return (error); 195 } 196 197 /* 198 * Cache a pointer to the caller's buffer and load the DMA map 199 * with the bounce buffer. 200 */ 201 cookie->id_origbuf = buf; 202 cookie->id_origbuflen = buflen; 203 cookie->id_buftype = _BUS_DMA_BUFTYPE_LINEAR; 204 error = _bus_dmamap_load(t, map, cookie->id_bouncebuf, buflen, 205 p, flags); 206 if (error) { 207 /* 208 * Free the bounce pages, unless our resources 209 * are reserved for our exclusive use. 210 */ 211 if ((map->_dm_flags & BUS_DMA_ALLOCNOW) == 0) 212 isadma_bounce_free_bouncebuf(t, map); 213 return (error); 214 } 215 216 /* ...so isadma_bounce_dmamap_sync() knows we're bouncing */ 217 cookie->id_flags |= _BUS_DMA_IS_BOUNCING; 218 return (0); 219 } 220 221 /* 222 * Like isadma_bounce_dmamap_load(), but for mbufs. 223 */ 224 int 225 isadma_bounce_dmamap_load_mbuf(bus_dma_tag_t t, bus_dmamap_t map, 226 struct mbuf *m0, int flags) 227 { 228 struct mips_bus_dma_cookie *cookie = map->_dm_cookie; 229 int error; 230 231 /* 232 * Make sure on error condition we return "no valid mappings." 233 */ 234 map->dm_mapsize = 0; 235 map->dm_nsegs = 0; 236 237 #ifdef DIAGNOSTIC 238 if ((m0->m_flags & M_PKTHDR) == 0) 239 panic("isadma_bounce_dmamap_load_mbuf: no packet header"); 240 #endif 241 242 if (m0->m_pkthdr.len > map->_dm_size) 243 return (EINVAL); 244 245 /* 246 * Try to load the map the normal way. If this errors out, 247 * and we can bounce, we will. 248 */ 249 error = _bus_dmamap_load_mbuf(t, map, m0, flags); 250 if (error == 0 || (cookie->id_flags & _BUS_DMA_MIGHT_NEED_BOUNCE) == 0) 251 return (error); 252 253 /* 254 * First attempt failed; bounce it. 255 */ 256 257 /* 258 * Allocate bounce pages, if necessary. 259 */ 260 if ((cookie->id_flags & _BUS_DMA_HAS_BOUNCE) == 0) { 261 error = isadma_bounce_alloc_bouncebuf(t, map, m0->m_pkthdr.len, 262 flags); 263 if (error) 264 return (error); 265 } 266 267 /* 268 * Cache a pointer to the caller's buffer and load the DMA map 269 * with the bounce buffer. 270 */ 271 cookie->id_origbuf = m0; 272 cookie->id_origbuflen = m0->m_pkthdr.len; /* not really used */ 273 cookie->id_buftype = _BUS_DMA_BUFTYPE_MBUF; 274 error = _bus_dmamap_load(t, map, cookie->id_bouncebuf, 275 m0->m_pkthdr.len, NULL, flags); 276 if (error) { 277 /* 278 * Free the bounce pages, unless our resources 279 * are reserved for our exclusive use. 280 */ 281 if ((map->_dm_flags & BUS_DMA_ALLOCNOW) == 0) 282 isadma_bounce_free_bouncebuf(t, map); 283 return (error); 284 } 285 286 /* ...so isadma_bounce_dmamap_sync() knows we're bouncing */ 287 cookie->id_flags |= _BUS_DMA_IS_BOUNCING; 288 return (0); 289 } 290 291 /* 292 * Like isadma_bounce_dmamap_load(), but for uios. 293 */ 294 int 295 isadma_bounce_dmamap_load_uio(bus_dma_tag_t t, bus_dmamap_t map, 296 struct uio *uio, int flags) 297 { 298 299 panic("isadma_bounce_dmamap_load_uio: not implemented"); 300 } 301 302 /* 303 * Like isadma_bounce_dmamap_load(), but for raw memory allocated with 304 * bus_dmamem_alloc(). 305 */ 306 int 307 isadma_bounce_dmamap_load_raw(bus_dma_tag_t t, bus_dmamap_t map, 308 bus_dma_segment_t *segs, int nsegs, bus_size_t size, int flags) 309 { 310 311 panic("isadma_bounce_dmamap_load_raw: not implemented"); 312 } 313 314 /* 315 * Unload an ISA DMA map. 316 */ 317 void 318 isadma_bounce_dmamap_unload(bus_dma_tag_t t, bus_dmamap_t map) 319 { 320 struct mips_bus_dma_cookie *cookie = map->_dm_cookie; 321 322 /* 323 * If we have bounce pages, free them, unless they're 324 * reserved for our exclusive use. 325 */ 326 if ((cookie->id_flags & _BUS_DMA_HAS_BOUNCE) && 327 (map->_dm_flags & BUS_DMA_ALLOCNOW) == 0) 328 isadma_bounce_free_bouncebuf(t, map); 329 330 cookie->id_flags &= ~_BUS_DMA_IS_BOUNCING; 331 cookie->id_buftype = _BUS_DMA_BUFTYPE_INVALID; 332 333 /* 334 * Do the generic bits of the unload. 335 */ 336 _bus_dmamap_unload(t, map); 337 } 338 339 /* 340 * Synchronize an ISA DMA map. 341 */ 342 void 343 isadma_bounce_dmamap_sync(bus_dma_tag_t t, bus_dmamap_t map, bus_addr_t offset, 344 bus_size_t len, int ops) 345 { 346 struct mips_bus_dma_cookie *cookie = map->_dm_cookie; 347 348 /* 349 * Mixing PRE and POST operations is not allowed. 350 */ 351 if ((ops & (BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE)) != 0 && 352 (ops & (BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE)) != 0) 353 panic("isadma_bounce_dmamap_sync: mix PRE and POST"); 354 355 #ifdef DIAGNOSTIC 356 if ((ops & (BUS_DMASYNC_PREWRITE|BUS_DMASYNC_POSTREAD)) != 0) { 357 if (offset >= map->dm_mapsize) 358 panic("isadma_bounce_dmamap_sync: bad offset"); 359 if (len == 0 || (offset + len) > map->dm_mapsize) 360 panic("isadma_bounce_dmamap_sync: bad length"); 361 } 362 #endif 363 364 /* 365 * If we're not bouncing, just do the normal sync operation 366 * and return. 367 */ 368 if ((cookie->id_flags & _BUS_DMA_IS_BOUNCING) == 0) { 369 _bus_dmamap_sync(t, map, offset, len, ops); 370 return; 371 } 372 373 /* 374 * Flush data cache for PREREAD. This has the side-effect 375 * of invalidating the cache. Done at PREREAD since it 376 * causes the cache line(s) to be written back to memory. 377 * 378 * Copy the original buffer to the bounce buffer and flush 379 * the data cache for PREWRITE, so that the contents 380 * of the data buffer in memory reflect reality. 381 * 382 * Copy the bounce buffer to the original buffer in POSTREAD. 383 */ 384 385 switch (cookie->id_buftype) { 386 case _BUS_DMA_BUFTYPE_LINEAR: 387 /* 388 * Nothing to do for pre-read. 389 */ 390 391 if (ops & BUS_DMASYNC_PREWRITE) { 392 /* 393 * Copy the caller's buffer to the bounce buffer. 394 */ 395 memcpy((char *)cookie->id_bouncebuf + offset, 396 (char *)cookie->id_origbuf + offset, len); 397 wbflush(); 398 } 399 400 if (ops & BUS_DMASYNC_POSTREAD) { 401 /* 402 * Copy the bounce buffer to the caller's buffer. 403 */ 404 memcpy((char *)cookie->id_origbuf + offset, 405 (char *)cookie->id_bouncebuf + offset, len); 406 } 407 408 /* 409 * Nothing to do for post-write. 410 */ 411 break; 412 413 case _BUS_DMA_BUFTYPE_MBUF: 414 { 415 struct mbuf *m, *m0 = cookie->id_origbuf; 416 bus_size_t minlen, moff; 417 418 /* 419 * Nothing to do for pre-read. 420 */ 421 422 if (ops & BUS_DMASYNC_PREWRITE) { 423 /* 424 * Copy the caller's buffer to the bounce buffer. 425 */ 426 m_copydata(m0, offset, len, 427 (char *)cookie->id_bouncebuf + offset); 428 } 429 430 if (ops & BUS_DMASYNC_POSTREAD) { 431 /* 432 * Copy the bounce buffer to the caller's buffer. 433 */ 434 for (moff = offset, m = m0; m != NULL && len != 0; 435 m = m->m_next) { 436 /* Find the beginning mbuf. */ 437 if (moff >= m->m_len) { 438 moff -= m->m_len; 439 continue; 440 } 441 442 /* 443 * Now at the first mbuf to sync; nail 444 * each one until we have exhausted the 445 * length. 446 */ 447 minlen = len < m->m_len - moff ? 448 len : m->m_len - moff; 449 450 memcpy(mtod(m, char *) + moff, 451 (char *)cookie->id_bouncebuf + offset, 452 minlen); 453 454 moff = 0; 455 len -= minlen; 456 offset += minlen; 457 } 458 } 459 460 /* 461 * Nothing to do for post-write. 462 */ 463 break; 464 } 465 466 case _BUS_DMA_BUFTYPE_UIO: 467 panic("isadma_bounce_dmamap_sync: _BUS_DMA_BUFTYPE_UIO"); 468 break; 469 470 case _BUS_DMA_BUFTYPE_RAW: 471 panic("isadma_bounce_dmamap_sync: _BUS_DMA_BUFTYPE_RAW"); 472 break; 473 474 case _BUS_DMA_BUFTYPE_INVALID: 475 panic("isadma_bounce_dmamap_sync: _BUS_DMA_BUFTYPE_INVALID"); 476 break; 477 478 default: 479 printf("unknown buffer type %d\n", cookie->id_buftype); 480 panic("isadma_bounce_dmamap_sync"); 481 } 482 483 /* Drain the write buffer. */ 484 wbflush(); 485 486 /* XXXJRT */ 487 if (ops & (BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE)) 488 mips_dcache_wbinv_range((vaddr_t)cookie->id_bouncebuf + offset, 489 len); 490 } 491 492 /* 493 * Allocate memory safe for ISA DMA. 494 */ 495 int 496 isadma_bounce_dmamem_alloc(bus_dma_tag_t t, bus_size_t size, 497 bus_size_t alignment, bus_size_t boundary, bus_dma_segment_t *segs, 498 int nsegs, int *rsegs, int flags) 499 { 500 paddr_t high; 501 502 if (_BUS_AVAIL_END > ISA_DMA_BOUNCE_THRESHOLD - 1) 503 high = ISA_DMA_BOUNCE_THRESHOLD - 1; 504 else 505 high = _BUS_AVAIL_END; 506 507 return (_bus_dmamem_alloc_range(t, size, alignment, boundary, 508 segs, nsegs, rsegs, flags, 0, high)); 509 } 510 511 /********************************************************************** 512 * ISA DMA utility functions 513 **********************************************************************/ 514 515 int 516 isadma_bounce_alloc_bouncebuf(bus_dma_tag_t t, bus_dmamap_t map, 517 bus_size_t size, int flags) 518 { 519 struct mips_bus_dma_cookie *cookie = map->_dm_cookie; 520 int error = 0; 521 522 cookie->id_bouncebuflen = round_page(size); 523 error = isadma_bounce_dmamem_alloc(t, cookie->id_bouncebuflen, 524 PAGE_SIZE, map->_dm_boundary, cookie->id_bouncesegs, 525 map->_dm_segcnt, &cookie->id_nbouncesegs, flags); 526 if (error) 527 goto out; 528 error = _bus_dmamem_map(t, cookie->id_bouncesegs, 529 cookie->id_nbouncesegs, cookie->id_bouncebuflen, 530 (void **)&cookie->id_bouncebuf, flags); 531 532 out: 533 if (error) { 534 _bus_dmamem_free(t, cookie->id_bouncesegs, 535 cookie->id_nbouncesegs); 536 cookie->id_bouncebuflen = 0; 537 cookie->id_nbouncesegs = 0; 538 } else 539 cookie->id_flags |= _BUS_DMA_HAS_BOUNCE; 540 541 return (error); 542 } 543 544 void 545 isadma_bounce_free_bouncebuf(bus_dma_tag_t t, bus_dmamap_t map) 546 { 547 struct mips_bus_dma_cookie *cookie = map->_dm_cookie; 548 549 _bus_dmamem_unmap(t, cookie->id_bouncebuf, 550 cookie->id_bouncebuflen); 551 _bus_dmamem_free(t, cookie->id_bouncesegs, 552 cookie->id_nbouncesegs); 553 cookie->id_bouncebuflen = 0; 554 cookie->id_nbouncesegs = 0; 555 cookie->id_flags &= ~_BUS_DMA_HAS_BOUNCE; 556 } 557