1/* $NetBSD: isadma_machdep.c,v 1.15 2011/07/19 15:07:43 dyoung Exp $ */ 2 3/*- 4 * Copyright (c) 1996, 1997, 1998 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_machdep.c,v 1.15 2011/07/19 15:07:43 dyoung Exp $"); 35 36#define ISA_DMA_STATS 37 38#include <sys/param.h> 39#include <sys/systm.h> 40#include <sys/syslog.h> 41#include <sys/device.h> 42#include <sys/malloc.h> 43#include <sys/proc.h> 44#include <sys/mbuf.h> 45 46#define _ARM32_BUS_DMA_PRIVATE 47#include <sys/bus.h> 48 49#include <dev/isa/isareg.h> 50#include <dev/isa/isavar.h> 51 52#include <uvm/uvm_extern.h> 53 54/* 55 * ISA has a 24-bit address limitation, so at most it has a 16M 56 * DMA range. However, some platforms have a more limited range, 57 * e.g. the Shark NC. On these systems, we are provided with 58 * a set of DMA ranges. The pmap module is aware of these ranges 59 * and places DMA-safe memory for them onto an alternate free list 60 * so that they are protected from being used to service page faults, 61 * etc. (unless we've run out of memory elsewhere). 62 */ 63extern struct arm32_dma_range *shark_isa_dma_ranges; 64extern int shark_isa_dma_nranges; 65 66int _isa_bus_dmamap_create(bus_dma_tag_t, bus_size_t, int, 67 bus_size_t, bus_size_t, int, bus_dmamap_t *); 68void _isa_bus_dmamap_destroy(bus_dma_tag_t, bus_dmamap_t); 69int _isa_bus_dmamap_load(bus_dma_tag_t, bus_dmamap_t, void *, 70 bus_size_t, struct proc *, int); 71int _isa_bus_dmamap_load_mbuf(bus_dma_tag_t, bus_dmamap_t, 72 struct mbuf *, int); 73int _isa_bus_dmamap_load_uio(bus_dma_tag_t, bus_dmamap_t, 74 struct uio *, int); 75int _isa_bus_dmamap_load_raw(bus_dma_tag_t, bus_dmamap_t, 76 bus_dma_segment_t *, int, bus_size_t, int); 77void _isa_bus_dmamap_unload(bus_dma_tag_t, bus_dmamap_t); 78void _isa_bus_dmamap_sync(bus_dma_tag_t, bus_dmamap_t, 79 bus_addr_t, bus_size_t, int); 80 81int _isa_bus_dmamem_alloc(bus_dma_tag_t, bus_size_t, bus_size_t, 82 bus_size_t, bus_dma_segment_t *, int, int *, int); 83 84int _isa_dma_alloc_bouncebuf(bus_dma_tag_t, bus_dmamap_t, 85 bus_size_t, int); 86void _isa_dma_free_bouncebuf(bus_dma_tag_t, bus_dmamap_t); 87 88/* 89 * Entry points for ISA DMA. These are mostly wrappers around 90 * the generic functions that understand how to deal with bounce 91 * buffers, if necessary. 92 */ 93struct arm32_bus_dma_tag isa_bus_dma_tag = { 94 0, /* _ranges */ 95 0, /* _nranges */ 96 NULL, /* _cookie */ 97 _isa_bus_dmamap_create, 98 _isa_bus_dmamap_destroy, 99 _isa_bus_dmamap_load, 100 _isa_bus_dmamap_load_mbuf, 101 _isa_bus_dmamap_load_uio, 102 _isa_bus_dmamap_load_raw, 103 _isa_bus_dmamap_unload, 104 _isa_bus_dmamap_sync, /* pre */ 105 _isa_bus_dmamap_sync, /* post */ 106 _isa_bus_dmamem_alloc, 107 _bus_dmamem_free, 108 _bus_dmamem_map, 109 _bus_dmamem_unmap, 110 _bus_dmamem_mmap, 111}; 112 113/* 114 * Initialize ISA DMA. 115 */ 116void 117isa_dma_init(void) 118{ 119 120 isa_bus_dma_tag._ranges = shark_isa_dma_ranges; 121 isa_bus_dma_tag._nranges = shark_isa_dma_nranges; 122} 123 124/********************************************************************** 125 * bus.h dma interface entry points 126 **********************************************************************/ 127 128#ifdef ISA_DMA_STATS 129#define STAT_INCR(v) (v)++ 130#define STAT_DECR(v) do { \ 131 if ((v) == 0) \ 132 printf("%s:%d -- Already 0!\n", __FILE__, __LINE__); \ 133 else \ 134 (v)--; \ 135 } while (0) 136u_long isa_dma_stats_loads; 137u_long isa_dma_stats_bounces; 138u_long isa_dma_stats_nbouncebufs; 139#else 140#define STAT_INCR(v) 141#define STAT_DECR(v) 142#endif 143 144/* 145 * Create an ISA DMA map. 146 */ 147int 148_isa_bus_dmamap_create(bus_dma_tag_t t, bus_size_t size, int nsegments, bus_size_t maxsegsz, bus_size_t boundary, int flags, bus_dmamap_t *dmamp) 149{ 150 struct arm32_isa_dma_cookie *cookie; 151 bus_dmamap_t map; 152 int error, cookieflags; 153 void *cookiestore; 154 size_t cookiesize; 155 156 /* Call common function to create the basic map. */ 157 error = _bus_dmamap_create(t, size, nsegments, maxsegsz, boundary, 158 flags, dmamp); 159 if (error) 160 return (error); 161 162 map = *dmamp; 163 map->_dm_cookie = NULL; 164 165 cookiesize = sizeof(struct arm32_isa_dma_cookie); 166 167 /* 168 * ISA only has 24-bits of address space. This means 169 * we can't DMA to pages over 16M. In order to DMA to 170 * arbitrary buffers, we use "bounce buffers" - pages 171 * in memory below the 16M boundary. On DMA reads, 172 * DMA happens to the bounce buffers, and is copied into 173 * the caller's buffer. On writes, data is copied into 174 * but bounce buffer, and the DMA happens from those 175 * pages. To software using the DMA mapping interface, 176 * this looks simply like a data cache. 177 * 178 * If we have more than 16M of RAM in the system, we may 179 * need bounce buffers. We check and remember that here. 180 * 181 * There are exceptions, however. VLB devices can do 182 * 32-bit DMA, and indicate that here. 183 * 184 * ...or, there is an opposite case. The most segments 185 * a transfer will require is (maxxfer / PAGE_SIZE) + 1. If 186 * the caller can't handle that many segments (e.g. the 187 * ISA DMA controller), we may have to bounce it as well. 188 * 189 * Well, not really... see note above regarding DMA ranges. 190 * Because of the range issue on this platform, we just 191 * always "might bounce". 192 */ 193 cookieflags = ID_MIGHT_NEED_BOUNCE; 194 cookiesize += (sizeof(bus_dma_segment_t) * map->_dm_segcnt); 195 196 /* 197 * Allocate our cookie. 198 */ 199 if ((cookiestore = malloc(cookiesize, M_DMAMAP, 200 (flags & BUS_DMA_NOWAIT) ? M_NOWAIT : M_WAITOK)) == NULL) { 201 error = ENOMEM; 202 goto out; 203 } 204 memset(cookiestore, 0, cookiesize); 205 cookie = (struct arm32_isa_dma_cookie *)cookiestore; 206 cookie->id_flags = cookieflags; 207 map->_dm_cookie = cookie; 208 209 if (cookieflags & ID_MIGHT_NEED_BOUNCE) { 210 /* 211 * Allocate the bounce pages now if the caller 212 * wishes us to do so. 213 */ 214 if ((flags & BUS_DMA_ALLOCNOW) == 0) 215 goto out; 216 217 error = _isa_dma_alloc_bouncebuf(t, map, size, flags); 218 } 219 220 out: 221 if (error) { 222 if (map->_dm_cookie != NULL) 223 free(map->_dm_cookie, M_DMAMAP); 224 _bus_dmamap_destroy(t, map); 225 } 226 return (error); 227} 228 229/* 230 * Destroy an ISA DMA map. 231 */ 232void 233_isa_bus_dmamap_destroy(bus_dma_tag_t t, bus_dmamap_t map) 234{ 235 struct arm32_isa_dma_cookie *cookie = map->_dm_cookie; 236 237 /* 238 * Free any bounce pages this map might hold. 239 */ 240 if (cookie->id_flags & ID_HAS_BOUNCE) 241 _isa_dma_free_bouncebuf(t, map); 242 243 free(cookie, M_DMAMAP); 244 _bus_dmamap_destroy(t, map); 245} 246 247/* 248 * Load an ISA DMA map with a linear buffer. 249 */ 250int 251_isa_bus_dmamap_load(bus_dma_tag_t t, bus_dmamap_t map, void *buf, 252 bus_size_t buflen, struct proc *p, int flags) 253{ 254 struct arm32_isa_dma_cookie *cookie = map->_dm_cookie; 255 int error; 256 257 STAT_INCR(isa_dma_stats_loads); 258 259 /* 260 * Make sure that on error condition we return "no valid mappings." 261 */ 262 map->dm_mapsize = 0; 263 map->dm_nsegs = 0; 264 265 /* 266 * Try to load the map the normal way. If this errors out, 267 * and we can bounce, we will. 268 */ 269 error = _bus_dmamap_load(t, map, buf, buflen, p, flags); 270 if (error == 0 || 271 (error != 0 && (cookie->id_flags & ID_MIGHT_NEED_BOUNCE) == 0)) 272 return (error); 273 274 /* 275 * First attempt failed; bounce it. 276 */ 277 278 STAT_INCR(isa_dma_stats_bounces); 279 280 /* 281 * Allocate bounce pages, if necessary. 282 */ 283 if ((cookie->id_flags & ID_HAS_BOUNCE) == 0) { 284 error = _isa_dma_alloc_bouncebuf(t, map, buflen, flags); 285 if (error) 286 return (error); 287 } 288 289 /* 290 * Cache a pointer to the caller's buffer and load the DMA map 291 * with the bounce buffer. 292 */ 293 cookie->id_origbuf = buf; 294 cookie->id_origbuflen = buflen; 295 cookie->id_buftype = ID_BUFTYPE_LINEAR; 296 error = _bus_dmamap_load(t, map, cookie->id_bouncebuf, buflen, 297 NULL, flags); 298 if (error) { 299 /* 300 * Free the bounce pages, unless our resources 301 * are reserved for our exclusive use. 302 */ 303 if ((map->_dm_flags & BUS_DMA_ALLOCNOW) == 0) 304 _isa_dma_free_bouncebuf(t, map); 305 return (error); 306 } 307 308 /* ...so _isa_bus_dmamap_sync() knows we're bouncing */ 309 cookie->id_flags |= ID_IS_BOUNCING; 310 return (0); 311} 312 313/* 314 * Like _isa_bus_dmamap_load(), but for mbufs. 315 */ 316int 317_isa_bus_dmamap_load_mbuf( bus_dma_tag_t t, bus_dmamap_t map, struct mbuf *m0, 318 int flags) 319{ 320 struct arm32_isa_dma_cookie *cookie = map->_dm_cookie; 321 int error; 322 323 /* 324 * Make sure that on error condition we return "no valid mappings." 325 */ 326 map->dm_mapsize = 0; 327 map->dm_nsegs = 0; 328 329#ifdef DIAGNOSTIC 330 if ((m0->m_flags & M_PKTHDR) == 0) 331 panic("_isa_bus_dmamap_load_mbuf: no packet header"); 332#endif 333 334 if (m0->m_pkthdr.len > map->_dm_size) 335 return (EINVAL); 336 337 /* 338 * Try to load the map the normal way. If this errors out, 339 * and we can bounce, we will. 340 */ 341 error = _bus_dmamap_load_mbuf(t, map, m0, flags); 342 if (error == 0 || 343 (error != 0 && (cookie->id_flags & ID_MIGHT_NEED_BOUNCE) == 0)) 344 return (error); 345 346 /* 347 * First attempt failed; bounce it. 348 */ 349 350 STAT_INCR(isa_dma_stats_bounces); 351 352 /* 353 * Allocate bounce pages, if necessary. 354 */ 355 if ((cookie->id_flags & ID_HAS_BOUNCE) == 0) { 356 error = _isa_dma_alloc_bouncebuf(t, map, m0->m_pkthdr.len, 357 flags); 358 if (error) 359 return (error); 360 } 361 362 /* 363 * Cache a pointer to the caller's buffer and load the DMA map 364 * with the bounce buffer. 365 */ 366 cookie->id_origbuf = m0; 367 cookie->id_origbuflen = m0->m_pkthdr.len; /* not really used */ 368 cookie->id_buftype = ID_BUFTYPE_MBUF; 369 error = _bus_dmamap_load(t, map, cookie->id_bouncebuf, 370 m0->m_pkthdr.len, NULL, flags); 371 if (error) { 372 /* 373 * Free the bounce pages, unless our resources 374 * are reserved for our exclusive use. 375 */ 376 if ((map->_dm_flags & BUS_DMA_ALLOCNOW) == 0) 377 _isa_dma_free_bouncebuf(t, map); 378 return (error); 379 } 380 381 /* ...so _isa_bus_dmamap_sync() knows we're bouncing */ 382 cookie->id_flags |= ID_IS_BOUNCING; 383 return (0); 384} 385 386/* 387 * Like _isa_bus_dmamap_load(), but for uios. 388 */ 389int 390_isa_bus_dmamap_load_uio(bus_dma_tag_t t, bus_dmamap_t map, struct uio *uio, int flags) 391{ 392 393 panic("_isa_bus_dmamap_load_uio: not implemented"); 394} 395 396/* 397 * Like _isa_bus_dmamap_load(), but for raw memory allocated with 398 * bus_dmamem_alloc(). 399 */ 400int 401_isa_bus_dmamap_load_raw(bus_dma_tag_t t, bus_dmamap_t map, bus_dma_segment_t *segs, int nsegs, bus_size_t size, int flags) 402{ 403 404 panic("_isa_bus_dmamap_load_raw: not implemented"); 405} 406 407/* 408 * Unload an ISA DMA map. 409 */ 410void 411_isa_bus_dmamap_unload(bus_dma_tag_t t, bus_dmamap_t map) 412{ 413 struct arm32_isa_dma_cookie *cookie = map->_dm_cookie; 414 415 /* 416 * If we have bounce pages, free them, unless they're 417 * reserved for our exclusive use. 418 */ 419 if ((cookie->id_flags & ID_HAS_BOUNCE) && 420 (map->_dm_flags & BUS_DMA_ALLOCNOW) == 0) 421 _isa_dma_free_bouncebuf(t, map); 422 423 cookie->id_flags &= ~ID_IS_BOUNCING; 424 cookie->id_buftype = ID_BUFTYPE_INVALID; 425 426 /* 427 * Do the generic bits of the unload. 428 */ 429 _bus_dmamap_unload(t, map); 430} 431 432/* 433 * Synchronize an ISA DMA map. 434 */ 435void 436_isa_bus_dmamap_sync(bus_dma_tag_t t, bus_dmamap_t map, bus_addr_t offset, bus_size_t len, int ops) 437{ 438 struct arm32_isa_dma_cookie *cookie = map->_dm_cookie; 439 440 /* 441 * Mixing PRE and POST operations is not allowed. 442 */ 443 if ((ops & (BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE)) != 0 && 444 (ops & (BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE)) != 0) 445 panic("_isa_bus_dmamap_sync: mix PRE and POST"); 446 447#ifdef DIAGNOSTIC 448 if ((ops & (BUS_DMASYNC_PREWRITE|BUS_DMASYNC_POSTREAD)) != 0) { 449 if (offset >= map->dm_mapsize) 450 panic("_isa_bus_dmamap_sync: bad offset"); 451 if (len == 0 || (offset + len) > map->dm_mapsize) 452 panic("_isa_bus_dmamap_sync: bad length"); 453 } 454#endif 455 456 /* 457 * If we're not bouncing, just return; nothing to do. 458 */ 459 if ((cookie->id_flags & ID_IS_BOUNCING) == 0) 460 return; 461 462 switch (cookie->id_buftype) { 463 case ID_BUFTYPE_LINEAR: 464 /* 465 * Nothing to do for pre-read. 466 */ 467 468 if (ops & BUS_DMASYNC_PREWRITE) { 469 /* 470 * Copy the caller's buffer to the bounce buffer. 471 */ 472 memcpy((char *)cookie->id_bouncebuf + offset, 473 (char *)cookie->id_origbuf + offset, len); 474 } 475 476 if (ops & BUS_DMASYNC_POSTREAD) { 477 /* 478 * Copy the bounce buffer to the caller's buffer. 479 */ 480 memcpy((char *)cookie->id_origbuf + offset, 481 (char *)cookie->id_bouncebuf + offset, len); 482 } 483 484 /* 485 * Nothing to do for post-write. 486 */ 487 break; 488 489 case ID_BUFTYPE_MBUF: 490 { 491 struct mbuf *m, *m0 = cookie->id_origbuf; 492 bus_size_t minlen, moff; 493 494 /* 495 * Nothing to do for pre-read. 496 */ 497 498 if (ops & BUS_DMASYNC_PREWRITE) { 499 /* 500 * Copy the caller's buffer to the bounce buffer. 501 */ 502 m_copydata(m0, offset, len, 503 (char *)cookie->id_bouncebuf + offset); 504 } 505 506 if (ops & BUS_DMASYNC_POSTREAD) { 507 /* 508 * Copy the bounce buffer to the caller's buffer. 509 */ 510 for (moff = offset, m = m0; m != NULL && len != 0; 511 m = m->m_next) { 512 /* Find the beginning mbuf. */ 513 if (moff >= m->m_len) { 514 moff -= m->m_len; 515 continue; 516 } 517 518 /* 519 * Now at the first mbuf to sync; nail 520 * each one until we have exhausted the 521 * length. 522 */ 523 minlen = len < m->m_len - moff ? 524 len : m->m_len - moff; 525 526 memcpy(mtod(m, char *) + moff, 527 (char *)cookie->id_bouncebuf + offset, 528 minlen); 529 530 moff = 0; 531 len -= minlen; 532 offset += minlen; 533 } 534 } 535 536 /* 537 * Nothing to do for post-write. 538 */ 539 break; 540 } 541 542 case ID_BUFTYPE_UIO: 543 panic("_isa_bus_dmamap_sync: ID_BUFTYPE_UIO"); 544 break; 545 546 case ID_BUFTYPE_RAW: 547 panic("_isa_bus_dmamap_sync: ID_BUFTYPE_RAW"); 548 break; 549 550 case ID_BUFTYPE_INVALID: 551 panic("_isa_bus_dmamap_sync: ID_BUFTYPE_INVALID"); 552 break; 553 554 default: 555 printf("unknown buffer type %d\n", cookie->id_buftype); 556 panic("_isa_bus_dmamap_sync"); 557 } 558} 559 560/* 561 * Allocate memory safe for ISA DMA. 562 */ 563int 564_isa_bus_dmamem_alloc(bus_dma_tag_t t, bus_size_t size, bus_size_t alignment, bus_size_t boundary, bus_dma_segment_t *segs, int nsegs, int *rsegs, int flags) 565{ 566 567 if (t->_ranges == NULL) 568 return (ENOMEM); 569 570 /* _bus_dmamem_alloc() does the range checks for us. */ 571 return (_bus_dmamem_alloc(t, size, alignment, boundary, segs, nsegs, 572 rsegs, flags)); 573} 574 575/********************************************************************** 576 * ISA DMA utility functions 577 **********************************************************************/ 578 579int 580_isa_dma_alloc_bouncebuf(bus_dma_tag_t t, bus_dmamap_t map, bus_size_t size, int flags) 581{ 582 struct arm32_isa_dma_cookie *cookie = map->_dm_cookie; 583 int error = 0; 584 585 cookie->id_bouncebuflen = round_page(size); 586 error = _isa_bus_dmamem_alloc(t, cookie->id_bouncebuflen, 587 PAGE_SIZE, map->_dm_boundary, cookie->id_bouncesegs, 588 map->_dm_segcnt, &cookie->id_nbouncesegs, flags); 589 if (error) 590 goto out; 591 error = _bus_dmamem_map(t, cookie->id_bouncesegs, 592 cookie->id_nbouncesegs, cookie->id_bouncebuflen, 593 (void **)&cookie->id_bouncebuf, flags); 594 595 out: 596 if (error) { 597 _bus_dmamem_free(t, cookie->id_bouncesegs, 598 cookie->id_nbouncesegs); 599 cookie->id_bouncebuflen = 0; 600 cookie->id_nbouncesegs = 0; 601 } else { 602 cookie->id_flags |= ID_HAS_BOUNCE; 603 STAT_INCR(isa_dma_stats_nbouncebufs); 604 } 605 606 return (error); 607} 608 609void 610_isa_dma_free_bouncebuf(bus_dma_tag_t t, bus_dmamap_t map) 611{ 612 struct arm32_isa_dma_cookie *cookie = map->_dm_cookie; 613 614 STAT_DECR(isa_dma_stats_nbouncebufs); 615 616 _bus_dmamem_unmap(t, cookie->id_bouncebuf, 617 cookie->id_bouncebuflen); 618 _bus_dmamem_free(t, cookie->id_bouncesegs, 619 cookie->id_nbouncesegs); 620 cookie->id_bouncebuflen = 0; 621 cookie->id_nbouncesegs = 0; 622 cookie->id_flags &= ~ID_HAS_BOUNCE; 623} 624