1 /* $NetBSD: int_bus_dma.c,v 1.5 2002/01/25 20:57:43 thorpej 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 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the NetBSD 22 * Foundation, Inc. and its contributors. 23 * 4. Neither the name of The NetBSD Foundation nor the names of its 24 * contributors may be used to endorse or promote products derived 25 * from this software without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGE. 38 */ 39 /* 40 * The integrator board has memory steering hardware that means that 41 * the normal physical addresses used by the processor cannot be used 42 * for DMA. Instead we have to use the "core module alias mapping 43 * addresses". We don't use these for normal processor accesses since 44 * they are much slower than the direct addresses when accessing 45 * memory on the local board. 46 */ 47 48 #include <sys/param.h> 49 #include <sys/systm.h> 50 #include <sys/kernel.h> 51 #include <sys/map.h> 52 #include <sys/proc.h> 53 #include <sys/buf.h> 54 #include <sys/reboot.h> 55 #include <sys/conf.h> 56 #include <sys/file.h> 57 #include <sys/malloc.h> 58 #include <sys/mbuf.h> 59 #include <sys/vnode.h> 60 #include <sys/device.h> 61 62 #include <uvm/uvm_extern.h> 63 64 #define _ARM32_BUS_DMA_PRIVATE 65 #include <evbarm/integrator/int_bus_dma.h> 66 67 #include <machine/cpu.h> 68 #include <arm/cpufunc.h> 69 70 static int integrator_bus_dmamap_load_buffer __P((bus_dma_tag_t, 71 bus_dmamap_t, void *, bus_size_t, struct proc *, int, 72 vm_offset_t *, int *, int)); 73 static int integrator_bus_dma_inrange __P((bus_dma_segment_t *, int, 74 bus_addr_t)); 75 76 /* 77 * Common function for loading a DMA map with a linear buffer. May 78 * be called by bus-specific DMA map load functions. 79 */ 80 int 81 integrator_bus_dmamap_load(t, map, buf, buflen, p, flags) 82 bus_dma_tag_t t; 83 bus_dmamap_t map; 84 void *buf; 85 bus_size_t buflen; 86 struct proc *p; 87 int flags; 88 { 89 vm_offset_t lastaddr; 90 int seg, error; 91 92 #ifdef DEBUG_DMA 93 printf("dmamap_load: t=%p map=%p buf=%p len=%lx p=%p f=%d\n", 94 t, map, buf, buflen, p, flags); 95 #endif /* DEBUG_DMA */ 96 97 /* 98 * Make sure that on error condition we return "no valid mappings". 99 */ 100 map->dm_mapsize = 0; 101 map->dm_nsegs = 0; 102 103 if (buflen > map->_dm_size) 104 return (EINVAL); 105 106 seg = 0; 107 error = integrator_bus_dmamap_load_buffer(t, map, buf, buflen, p, flags, 108 &lastaddr, &seg, 1); 109 if (error == 0) { 110 map->dm_mapsize = buflen; 111 map->dm_nsegs = seg + 1; 112 map->_dm_proc = p; 113 } 114 #ifdef DEBUG_DMA 115 printf("dmamap_load: error=%d\n", error); 116 #endif /* DEBUG_DMA */ 117 return (error); 118 } 119 120 /* 121 * Like _bus_dmamap_load(), but for mbufs. 122 */ 123 int 124 integrator_bus_dmamap_load_mbuf(t, map, m0, flags) 125 bus_dma_tag_t t; 126 bus_dmamap_t map; 127 struct mbuf *m0; 128 int flags; 129 { 130 vm_offset_t lastaddr; 131 int seg, error, first; 132 struct mbuf *m; 133 134 #ifdef DEBUG_DMA 135 printf("dmamap_load_mbuf: t=%p map=%p m0=%p f=%d\n", 136 t, map, m0, flags); 137 #endif /* DEBUG_DMA */ 138 139 /* 140 * Make sure that on error condition we return "no valid mappings." 141 */ 142 map->dm_mapsize = 0; 143 map->dm_nsegs = 0; 144 145 #ifdef DIAGNOSTIC 146 if ((m0->m_flags & M_PKTHDR) == 0) 147 panic("integrator_bus_dmamap_load_mbuf: no packet header"); 148 #endif /* DIAGNOSTIC */ 149 150 if (m0->m_pkthdr.len > map->_dm_size) 151 return (EINVAL); 152 153 first = 1; 154 seg = 0; 155 error = 0; 156 for (m = m0; m != NULL && error == 0; m = m->m_next) { 157 error = integrator_bus_dmamap_load_buffer(t, map, m->m_data, 158 m->m_len, NULL, flags, &lastaddr, &seg, first); 159 first = 0; 160 } 161 if (error == 0) { 162 map->dm_mapsize = m0->m_pkthdr.len; 163 map->dm_nsegs = seg + 1; 164 map->_dm_proc = NULL; /* always kernel */ 165 } 166 #ifdef DEBUG_DMA 167 printf("dmamap_load_mbuf: error=%d\n", error); 168 #endif /* DEBUG_DMA */ 169 return (error); 170 } 171 172 /* 173 * Like _bus_dmamap_load(), but for uios. 174 */ 175 int 176 integrator_bus_dmamap_load_uio(t, map, uio, flags) 177 bus_dma_tag_t t; 178 bus_dmamap_t map; 179 struct uio *uio; 180 int flags; 181 { 182 vm_offset_t lastaddr; 183 int seg, i, error, first; 184 bus_size_t minlen, resid; 185 struct proc *p = NULL; 186 struct iovec *iov; 187 caddr_t addr; 188 189 /* 190 * Make sure that on error condition we return "no valid mappings." 191 */ 192 map->dm_mapsize = 0; 193 map->dm_nsegs = 0; 194 195 resid = uio->uio_resid; 196 iov = uio->uio_iov; 197 198 if (uio->uio_segflg == UIO_USERSPACE) { 199 p = uio->uio_procp; 200 #ifdef DIAGNOSTIC 201 if (p == NULL) 202 panic("integrator_bus_dmamap_load_uio: USERSPACE but no proc"); 203 #endif 204 } 205 206 first = 1; 207 seg = 0; 208 error = 0; 209 for (i = 0; i < uio->uio_iovcnt && resid != 0 && error == 0; i++) { 210 /* 211 * Now at the first iovec to load. Load each iovec 212 * until we have exhausted the residual count. 213 */ 214 minlen = resid < iov[i].iov_len ? resid : iov[i].iov_len; 215 addr = (caddr_t)iov[i].iov_base; 216 217 error = integrator_bus_dmamap_load_buffer(t, map, addr, minlen, 218 p, flags, &lastaddr, &seg, first); 219 first = 0; 220 221 resid -= minlen; 222 } 223 if (error == 0) { 224 map->dm_mapsize = uio->uio_resid; 225 map->dm_nsegs = seg + 1; 226 map->_dm_proc = p; 227 } 228 return (error); 229 } 230 231 /* 232 * Common function for DMA-safe memory allocation. May be called 233 * by bus-specific DMA memory allocation functions. 234 */ 235 236 extern vm_offset_t physical_start; 237 extern vm_offset_t physical_freestart; 238 extern vm_offset_t physical_freeend; 239 extern vm_offset_t physical_end; 240 241 int 242 integrator_bus_dmamem_alloc(t, size, alignment, boundary, segs, nsegs, rsegs, flags) 243 bus_dma_tag_t t; 244 bus_size_t size, alignment, boundary; 245 bus_dma_segment_t *segs; 246 int nsegs; 247 int *rsegs; 248 int flags; 249 { 250 int error; 251 #ifdef DEBUG_DMA 252 printf("dmamem_alloc t=%p size=%lx align=%lx boundary=%lx segs=%p nsegs=%x rsegs=%p flags=%x\n", 253 t, size, alignment, boundary, segs, nsegs, rsegs, flags); 254 #endif /* DEBUG_DMA */ 255 error = (integrator_bus_dmamem_alloc_range(t, size, alignment, boundary, 256 segs, nsegs, rsegs, flags, trunc_page(physical_start), trunc_page(physical_end))); 257 #ifdef DEBUG_DMA 258 printf("dmamem_alloc: =%d\n", error); 259 #endif /* DEBUG_DMA */ 260 return(error); 261 } 262 263 /* 264 * Common function for freeing DMA-safe memory. May be called by 265 * bus-specific DMA memory free functions. 266 */ 267 void 268 integrator_bus_dmamem_free(t, segs, nsegs) 269 bus_dma_tag_t t; 270 bus_dma_segment_t *segs; 271 int nsegs; 272 { 273 struct vm_page *m; 274 bus_addr_t addr; 275 struct pglist mlist; 276 int curseg; 277 278 #ifdef DEBUG_DMA 279 printf("dmamem_free: t=%p segs=%p nsegs=%x\n", t, segs, nsegs); 280 #endif /* DEBUG_DMA */ 281 282 /* 283 * Build a list of pages to free back to the VM system. 284 */ 285 TAILQ_INIT(&mlist); 286 for (curseg = 0; curseg < nsegs; curseg++) { 287 for (addr = segs[curseg].ds_addr; 288 addr < (segs[curseg].ds_addr + segs[curseg].ds_len); 289 addr += PAGE_SIZE) { 290 m = PHYS_TO_VM_PAGE(CM_ALIAS_TO_LOCAL(addr)); 291 TAILQ_INSERT_TAIL(&mlist, m, pageq); 292 } 293 } 294 uvm_pglistfree(&mlist); 295 } 296 297 /* 298 * Common function for mapping DMA-safe memory. May be called by 299 * bus-specific DMA memory map functions. 300 */ 301 int 302 integrator_bus_dmamem_map(t, segs, nsegs, size, kvap, flags) 303 bus_dma_tag_t t; 304 bus_dma_segment_t *segs; 305 int nsegs; 306 size_t size; 307 caddr_t *kvap; 308 int flags; 309 { 310 vm_offset_t va; 311 bus_addr_t addr; 312 int curseg; 313 pt_entry_t *ptep/*, pte*/; 314 315 #ifdef DEBUG_DMA 316 printf("dmamem_map: t=%p segs=%p nsegs=%x size=%lx flags=%x\n", t, 317 segs, nsegs, (unsigned long)size, flags); 318 #endif /* DEBUG_DMA */ 319 320 size = round_page(size); 321 va = uvm_km_valloc(kernel_map, size); 322 323 if (va == 0) 324 return (ENOMEM); 325 326 *kvap = (caddr_t)va; 327 328 for (curseg = 0; curseg < nsegs; curseg++) { 329 for (addr = segs[curseg].ds_addr; 330 addr < (segs[curseg].ds_addr + segs[curseg].ds_len); 331 addr += NBPG, va += NBPG, size -= NBPG) { 332 #ifdef DEBUG_DMA 333 printf("wiring p%lx to v%lx", CM_ALIAS_TO_LOCAL(addr), 334 va); 335 #endif /* DEBUG_DMA */ 336 if (size == 0) 337 panic("integrator_bus_dmamem_map: size botch"); 338 pmap_enter(pmap_kernel(), va, CM_ALIAS_TO_LOCAL(addr), 339 VM_PROT_READ | VM_PROT_WRITE, 340 VM_PROT_READ | VM_PROT_WRITE | PMAP_WIRED); 341 /* 342 * If the memory must remain coherent with the 343 * cache then we must make the memory uncacheable 344 * in order to maintain virtual cache coherency. 345 * We must also guarentee the cache does not already 346 * contain the virtal addresses we are making 347 * uncacheable. 348 */ 349 if (flags & BUS_DMA_COHERENT) { 350 cpu_dcache_wbinv_range(va, NBPG); 351 cpu_drain_writebuf(); 352 ptep = vtopte(va); 353 *ptep = ((*ptep) & (~PT_C | PT_B)); 354 tlb_flush(); 355 } 356 #ifdef DEBUG_DMA 357 ptep = vtopte(va); 358 printf(" pte=v%p *pte=%x\n", ptep, *ptep); 359 #endif /* DEBUG_DMA */ 360 } 361 } 362 pmap_update(pmap_kernel()); 363 #ifdef DEBUG_DMA 364 printf("dmamem_map: =%p\n", *kvap); 365 #endif /* DEBUG_DMA */ 366 return (0); 367 } 368 369 /* 370 * Common functin for mmap(2)'ing DMA-safe memory. May be called by 371 * bus-specific DMA mmap(2)'ing functions. 372 */ 373 paddr_t 374 integrator_bus_dmamem_mmap(t, segs, nsegs, off, prot, flags) 375 bus_dma_tag_t t; 376 bus_dma_segment_t *segs; 377 int nsegs; 378 off_t off; 379 int prot, flags; 380 { 381 int i; 382 383 for (i = 0; i < nsegs; i++) { 384 #ifdef DIAGNOSTIC 385 if (off & PGOFSET) 386 panic("integrator_bus_dmamem_mmap: offset unaligned"); 387 if (segs[i].ds_addr & PGOFSET) 388 panic("integrator_bus_dmamem_mmap: segment unaligned"); 389 if (segs[i].ds_len & PGOFSET) 390 panic("integrator_bus_dmamem_mmap: segment size not multiple" 391 " of page size"); 392 #endif /* DIAGNOSTIC */ 393 if (off >= segs[i].ds_len) { 394 off -= segs[i].ds_len; 395 continue; 396 } 397 398 return arm_byte_to_page((u_long)CM_ALIAS_TO_LOCAL(segs[i].ds_addr) + off); 399 } 400 401 /* Page not found. */ 402 return -1; 403 } 404 405 /********************************************************************** 406 * DMA utility functions 407 **********************************************************************/ 408 409 /* 410 * Utility function to load a linear buffer. lastaddrp holds state 411 * between invocations (for multiple-buffer loads). segp contains 412 * the starting segment on entrace, and the ending segment on exit. 413 * first indicates if this is the first invocation of this function. 414 */ 415 static int 416 integrator_bus_dmamap_load_buffer(t, map, buf, buflen, p, flags, lastaddrp, 417 segp, first) 418 bus_dma_tag_t t; 419 bus_dmamap_t map; 420 void *buf; 421 bus_size_t buflen; 422 struct proc *p; 423 int flags; 424 vm_offset_t *lastaddrp; 425 int *segp; 426 int first; 427 { 428 bus_size_t sgsize; 429 bus_addr_t curaddr, lastaddr, baddr, bmask; 430 vm_offset_t vaddr = (vm_offset_t)buf; 431 int seg; 432 pmap_t pmap; 433 434 #ifdef DEBUG_DMA 435 printf("integrator_bus_dmamem_load_buffer(buf=%p, len=%lx, flags=%d, 1st=%d)\n", 436 buf, buflen, flags, first); 437 #endif /* DEBUG_DMA */ 438 439 if (p != NULL) 440 pmap = p->p_vmspace->vm_map.pmap; 441 else 442 pmap = pmap_kernel(); 443 444 lastaddr = *lastaddrp; 445 bmask = ~(map->_dm_boundary - 1); 446 447 for (seg = *segp; buflen > 0; ) { 448 /* 449 * Get the physical address for this segment. 450 */ 451 (void) pmap_extract(pmap, (vaddr_t)vaddr, &curaddr); 452 453 /* 454 * Make sure we're in an allowed DMA range. 455 */ 456 if (t->_ranges != NULL && 457 integrator_bus_dma_inrange(t->_ranges, t->_nranges, curaddr) == 0) 458 return (EINVAL); 459 460 /* 461 * Compute the segment size, and adjust counts. 462 */ 463 sgsize = NBPG - ((u_long)vaddr & PGOFSET); 464 if (buflen < sgsize) 465 sgsize = buflen; 466 467 /* 468 * Make sure we don't cross any boundaries. 469 */ 470 if (map->_dm_boundary > 0) { 471 baddr = (curaddr + map->_dm_boundary) & bmask; 472 if (sgsize > (baddr - curaddr)) 473 sgsize = (baddr - curaddr); 474 } 475 476 /* 477 * Insert chunk into a segment, coalescing with 478 * previous segment if possible. 479 */ 480 if (first) { 481 map->dm_segs[seg].ds_addr = LOCAL_TO_CM_ALIAS(curaddr); 482 map->dm_segs[seg].ds_len = sgsize; 483 map->dm_segs[seg]._ds_vaddr = vaddr; 484 first = 0; 485 } else { 486 if (curaddr == lastaddr && 487 (map->dm_segs[seg].ds_len + sgsize) <= 488 map->_dm_maxsegsz && 489 (map->_dm_boundary == 0 || 490 (map->dm_segs[seg].ds_addr & bmask) == 491 (LOCAL_TO_CM_ALIAS(curaddr) & bmask))) 492 map->dm_segs[seg].ds_len += sgsize; 493 else { 494 if (++seg >= map->_dm_segcnt) 495 break; 496 map->dm_segs[seg].ds_addr = LOCAL_TO_CM_ALIAS(curaddr); 497 map->dm_segs[seg].ds_len = sgsize; 498 map->dm_segs[seg]._ds_vaddr = vaddr; 499 } 500 } 501 502 lastaddr = curaddr + sgsize; 503 vaddr += sgsize; 504 buflen -= sgsize; 505 } 506 507 *segp = seg; 508 *lastaddrp = lastaddr; 509 510 /* 511 * Did we fit? 512 */ 513 if (buflen != 0) 514 return (EFBIG); /* XXX better return value here? */ 515 return (0); 516 } 517 518 /* 519 * Check to see if the specified page is in an allowed DMA range. 520 */ 521 static int 522 integrator_bus_dma_inrange(ranges, nranges, curaddr) 523 bus_dma_segment_t *ranges; 524 int nranges; 525 bus_addr_t curaddr; 526 { 527 bus_dma_segment_t *ds; 528 int i; 529 530 for (i = 0, ds = ranges; i < nranges; i++, ds++) { 531 if (curaddr >= CM_ALIAS_TO_LOCAL(ds->ds_addr) && 532 round_page(curaddr) <= (CM_ALIAS_TO_LOCAL(ds->ds_addr) + ds->ds_len)) 533 return (1); 534 } 535 536 return (0); 537 } 538 539 /* 540 * Allocate physical memory from the given physical address range. 541 * Called by DMA-safe memory allocation methods. 542 */ 543 int 544 integrator_bus_dmamem_alloc_range(t, size, alignment, boundary, segs, nsegs, rsegs, 545 flags, low, high) 546 bus_dma_tag_t t; 547 bus_size_t size, alignment, boundary; 548 bus_dma_segment_t *segs; 549 int nsegs; 550 int *rsegs; 551 int flags; 552 vm_offset_t low; 553 vm_offset_t high; 554 { 555 vm_offset_t curaddr, lastaddr; 556 struct vm_page *m; 557 struct pglist mlist; 558 int curseg, error; 559 560 #ifdef DEBUG_DMA 561 printf("alloc_range: t=%p size=%lx align=%lx boundary=%lx segs=%p nsegs=%x rsegs=%p flags=%x lo=%lx hi=%lx\n", 562 t, size, alignment, boundary, segs, nsegs, rsegs, flags, low, high); 563 #endif /* DEBUG_DMA */ 564 565 /* Always round the size. */ 566 size = round_page(size); 567 568 /* 569 * Allocate pages from the VM system. 570 */ 571 TAILQ_INIT(&mlist); 572 error = uvm_pglistalloc(size, low, high, alignment, boundary, 573 &mlist, nsegs, (flags & BUS_DMA_NOWAIT) == 0); 574 if (error) 575 return (error); 576 577 /* 578 * Compute the location, size, and number of segments actually 579 * returned by the VM code. 580 */ 581 m = mlist.tqh_first; 582 curseg = 0; 583 lastaddr = VM_PAGE_TO_PHYS(m); 584 segs[curseg].ds_addr = LOCAL_TO_CM_ALIAS(lastaddr); 585 segs[curseg].ds_len = PAGE_SIZE; 586 #ifdef DEBUG_DMA 587 printf("alloc: page %lx\n", lastaddr); 588 #endif /* DEBUG_DMA */ 589 m = m->pageq.tqe_next; 590 591 for (; m != NULL; m = m->pageq.tqe_next) { 592 curaddr = VM_PAGE_TO_PHYS(m); 593 #ifdef DIAGNOSTIC 594 if (curaddr < low || curaddr >= high) { 595 printf("uvm_pglistalloc returned non-sensical" 596 " address 0x%lx\n", curaddr); 597 panic("integrator_bus_dmamem_alloc_range"); 598 } 599 #endif /* DIAGNOSTIC */ 600 #ifdef DEBUG_DMA 601 printf("alloc: page %lx\n", curaddr); 602 #endif /* DEBUG_DMA */ 603 if (curaddr == (lastaddr + PAGE_SIZE)) 604 segs[curseg].ds_len += PAGE_SIZE; 605 else { 606 curseg++; 607 segs[curseg].ds_addr = LOCAL_TO_CM_ALIAS(curaddr); 608 segs[curseg].ds_len = PAGE_SIZE; 609 } 610 lastaddr = curaddr; 611 } 612 613 *rsegs = curseg + 1; 614 615 return (0); 616 } 617