1 /* $NetBSD: bus_dma.c,v 1.16 2020/12/19 21:27:52 thorpej Exp $ */
2
3 /*
4 * Copyright (c) 2005 NONAKA Kimihiro
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include <sys/cdefs.h>
29 __KERNEL_RCSID(0, "$NetBSD: bus_dma.c,v 1.16 2020/12/19 21:27:52 thorpej Exp $");
30
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/kernel.h>
34 #include <sys/device.h>
35 #include <sys/kmem.h>
36 #include <sys/mbuf.h>
37
38 #include <uvm/uvm.h>
39
40 #include <sh3/cache.h>
41
42 #include <machine/autoconf.h>
43 #define _LANDISK_BUS_DMA_PRIVATE
44 #include <sys/bus.h>
45
46 #if defined(DEBUG) && defined(BUSDMA_DEBUG)
47 int busdma_debug = 0;
48 #define DPRINTF(a) if (busdma_debug) printf a
49 #else
50 #define DPRINTF(a)
51 #endif
52
53 struct _bus_dma_tag landisk_bus_dma = {
54 ._cookie = NULL,
55
56 ._dmamap_create = _bus_dmamap_create,
57 ._dmamap_destroy = _bus_dmamap_destroy,
58 ._dmamap_load = _bus_dmamap_load,
59 ._dmamap_load_mbuf = _bus_dmamap_load_mbuf,
60 ._dmamap_load_uio = _bus_dmamap_load_uio,
61 ._dmamap_load_raw = _bus_dmamap_load_raw,
62 ._dmamap_unload = _bus_dmamap_unload,
63 ._dmamap_sync = _bus_dmamap_sync,
64
65 ._dmamem_alloc = _bus_dmamem_alloc,
66 ._dmamem_free = _bus_dmamem_free,
67 ._dmamem_map = _bus_dmamem_map,
68 ._dmamem_unmap = _bus_dmamem_unmap,
69 ._dmamem_mmap = _bus_dmamem_mmap,
70 };
71
72 static size_t
_bus_dmamap_mapsize(int const nsegments)73 _bus_dmamap_mapsize(int const nsegments)
74 {
75 KASSERT(nsegments > 0);
76 return sizeof(struct _bus_dmamap)
77 + (sizeof(bus_dma_segment_t) * (nsegments - 1));
78 }
79
80 /*
81 * Create a DMA map.
82 */
83 int
_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)84 _bus_dmamap_create(bus_dma_tag_t t, bus_size_t size, int nsegments,
85 bus_size_t maxsegsz, bus_size_t boundary, int flags, bus_dmamap_t *dmamp)
86 {
87 bus_dmamap_t map;
88 void *mapstore;
89
90 DPRINTF(("%s: t = %p, size = %ld, nsegments = %d, maxsegsz = %ld,"
91 " boundary = %ld, flags = %x\n",
92 __func__, t, size, nsegments, maxsegsz, boundary, flags));
93
94 /*
95 * Allocate and initialize the DMA map. The end of the map is
96 * a variable-sized array of segments, so we allocate enough
97 * room for them in one shot. bus_dmamap_t includes one
98 * bus_dma_segment_t already, hence the (nsegments - 1).
99 *
100 * Note that we don't preserve WAITOK and NOWAIT flags.
101 * Preservation of ALLOCNOW notifies others that we've
102 * reserved these resources, and they are not to be freed.
103 */
104 mapstore = kmem_zalloc(_bus_dmamap_mapsize(nsegments),
105 (flags & BUS_DMA_NOWAIT) ? KM_NOSLEEP : KM_SLEEP);
106 if (mapstore == NULL)
107 return ENOMEM;
108
109 DPRINTF(("%s: dmamp = %p\n", __func__, mapstore));
110
111 map = (bus_dmamap_t)mapstore;
112 map->_dm_size = size;
113 map->_dm_segcnt = nsegments;
114 map->_dm_maxsegsz = maxsegsz;
115 map->_dm_boundary = boundary;
116 map->_dm_flags = flags & ~(BUS_DMA_WAITOK | BUS_DMA_NOWAIT);
117
118 map->dm_mapsize = 0; /* no valid mappings */
119 map->dm_nsegs = 0;
120
121 *dmamp = map;
122 return 0;
123 }
124
125 /*
126 * Destroy a DMA map.
127 */
128 void
_bus_dmamap_destroy(bus_dma_tag_t t,bus_dmamap_t map)129 _bus_dmamap_destroy(bus_dma_tag_t t, bus_dmamap_t map)
130 {
131
132 DPRINTF(("%s: t = %p, map = %p\n", __func__, t, map));
133
134 kmem_free(map, _bus_dmamap_mapsize(map->_dm_segcnt));
135 }
136
137 static inline int
_bus_dmamap_load_paddr(bus_dma_tag_t t,bus_dmamap_t map,paddr_t paddr,vaddr_t vaddr,int size,int * segp,paddr_t * lastaddrp,int first)138 _bus_dmamap_load_paddr(bus_dma_tag_t t, bus_dmamap_t map,
139 paddr_t paddr, vaddr_t vaddr, int size, int *segp, paddr_t *lastaddrp,
140 int first)
141 {
142 bus_dma_segment_t * const segs = map->dm_segs;
143 bus_addr_t bmask = ~(map->_dm_boundary - 1);
144 bus_addr_t lastaddr;
145 int nseg;
146 int sgsize;
147
148 nseg = *segp;
149 lastaddr = *lastaddrp;
150
151 DPRINTF(("%s: t = %p, map = %p, paddr = 0x%08lx,"
152 " vaddr = 0x%08lx, size = %d\n",
153 __func__, t, map, paddr, vaddr, size));
154 DPRINTF(("%s: nseg = %d, bmask = 0x%08lx, lastaddr = 0x%08lx\n",
155 __func__, nseg, bmask, lastaddr));
156
157 do {
158 sgsize = size;
159
160 /*
161 * Make sure we don't cross any boundaries.
162 */
163 if (map->_dm_boundary > 0) {
164 bus_addr_t baddr; /* next boundary address */
165
166 baddr = (paddr + map->_dm_boundary) & bmask;
167 if (sgsize > (baddr - paddr))
168 sgsize = (baddr - paddr);
169 }
170
171 DPRINTF(("%s: sgsize = %d\n", __func__, sgsize));
172
173 /*
174 * Insert chunk coalescing with previous segment if possible.
175 */
176 if (first) {
177 DPRINTF(("%s: first\n", __func__));
178 first = 0;
179
180 segs[nseg].ds_addr = SH3_PHYS_TO_P2SEG(paddr);
181 segs[nseg].ds_len = sgsize;
182 segs[nseg]._ds_vaddr = vaddr;
183 }
184 else if ((paddr == lastaddr)
185 && (segs[nseg].ds_len + sgsize <= map->_dm_maxsegsz)
186 && (map->_dm_boundary == 0 ||
187 (segs[nseg].ds_addr & bmask) == (paddr & bmask)))
188 {
189 DPRINTF(("%s: coalesce\n", __func__));
190
191 segs[nseg].ds_len += sgsize;
192 }
193 else {
194 DPRINTF(("%s: new\n", __func__));
195
196 ++nseg;
197 if (nseg >= map->_dm_segcnt)
198 break;
199
200 segs[nseg].ds_addr = SH3_PHYS_TO_P2SEG(paddr);
201 segs[nseg].ds_len = sgsize;
202 segs[nseg]._ds_vaddr = vaddr;
203 }
204
205 paddr += sgsize;
206 vaddr += sgsize;
207 size -= sgsize;
208 lastaddr = paddr;
209
210 DPRINTF(("%s: lastaddr = 0x%08lx, paddr = 0x%08lx,"
211 " vaddr = 0x%08lx, size = %d\n",
212 __func__, lastaddr, paddr, vaddr, size));
213 } while (size > 0);
214
215 DPRINTF(("%s: nseg = %d\n", __func__, nseg));
216
217 *segp = nseg;
218 *lastaddrp = lastaddr;
219
220 if (size != 0) {
221 /*
222 * It didn't fit. If there is a chained window, we
223 * will automatically fall back to it.
224 */
225 return (EFBIG); /* XXX better return value here? */
226 }
227
228 return (0);
229 }
230
231 static inline int
_bus_bus_dmamap_load_buffer(bus_dma_tag_t t,bus_dmamap_t map,void * buf,bus_size_t buflen,struct proc * p,int flags,int * segp)232 _bus_bus_dmamap_load_buffer(bus_dma_tag_t t, bus_dmamap_t map, void *buf,
233 bus_size_t buflen, struct proc *p, int flags, int *segp)
234 {
235 bus_size_t sgsize;
236 bus_addr_t curaddr;
237 bus_size_t len;
238 paddr_t lastaddr;
239 vaddr_t vaddr = (vaddr_t)buf;
240 pmap_t pmap;
241 int first;
242 int error;
243
244 DPRINTF(("%s: t = %p, map = %p, buf = %p, buflen = %ld,"
245 " p = %p, flags = %x\n",
246 __func__, t, map, buf, buflen, p, flags));
247
248 if (p != NULL)
249 pmap = p->p_vmspace->vm_map.pmap;
250 else
251 pmap = pmap_kernel();
252
253 first = 1;
254 lastaddr = 0;
255
256 len = buflen;
257 while (len > 0) {
258 bool mapped;
259
260 mapped = pmap_extract(pmap, vaddr, &curaddr);
261 if (!mapped)
262 return EFAULT;
263
264 sgsize = PAGE_SIZE - (vaddr & PGOFSET);
265 if (len < sgsize)
266 sgsize = len;
267
268 error = _bus_dmamap_load_paddr(t, map, curaddr, vaddr, sgsize,
269 segp, &lastaddr, first);
270 if (error)
271 return error;
272
273 vaddr += sgsize;
274 len -= sgsize;
275 first = 0;
276 }
277
278 return 0;
279 }
280
281 /*
282 * Load a DMA map with a linear buffer.
283 */
284 int
_bus_dmamap_load(bus_dma_tag_t t,bus_dmamap_t map,void * buf,bus_size_t buflen,struct proc * p,int flags)285 _bus_dmamap_load(bus_dma_tag_t t, bus_dmamap_t map, void *buf,
286 bus_size_t buflen, struct proc *p, int flags)
287 {
288 bus_addr_t addr = (bus_addr_t)buf;
289 paddr_t lastaddr;
290 int seg;
291 int first;
292 int error;
293
294 DPRINTF(("%s: t = %p, map = %p, buf = %p, buflen = %ld,"
295 " p = %p, flags = %x\n",
296 __func__, t, map, buf, buflen, p, flags));
297
298 /* make sure that on error condition we return "no valid mappings" */
299 map->dm_mapsize = 0;
300 map->dm_nsegs = 0;
301
302 if (buflen > map->_dm_size)
303 return (EINVAL);
304
305 error = 0;
306 seg = 0;
307
308 if (SH3_P1SEG_BASE <= addr && addr + buflen <= SH3_P2SEG_END) {
309 bus_addr_t curaddr;
310 bus_size_t sgsize;
311 bus_size_t len = buflen;
312
313 DPRINTF(("%s: P[12]SEG (0x%08lx)\n", __func__, addr));
314
315 first = 1;
316 lastaddr = 0;
317
318 while (len > 0) {
319 curaddr = SH3_P1SEG_TO_PHYS(addr);
320
321 sgsize = PAGE_SIZE - ((u_long)addr & PGOFSET);
322 if (len < sgsize)
323 sgsize = len;
324
325 error = _bus_dmamap_load_paddr(t, map,
326 curaddr, addr, sgsize,
327 &seg, &lastaddr, first);
328 if (error)
329 break;
330
331 addr += sgsize;
332 len -= sgsize;
333 first = 0;
334 }
335 }
336 else {
337 error = _bus_bus_dmamap_load_buffer(t, map, buf, buflen,
338 p, flags, &seg);
339 }
340
341 if (error)
342 return (error);
343
344 map->dm_nsegs = seg + 1;
345 map->dm_mapsize = buflen;
346 return 0;
347 }
348
349 /*
350 * Like _bus_dmamap_load(), but for mbufs.
351 */
352 int
_bus_dmamap_load_mbuf(bus_dma_tag_t t,bus_dmamap_t map,struct mbuf * m0,int flags)353 _bus_dmamap_load_mbuf(bus_dma_tag_t t, bus_dmamap_t map, struct mbuf *m0,
354 int flags)
355 {
356 struct mbuf *m;
357 paddr_t lastaddr;
358 int seg;
359 int first;
360 int error;
361
362 DPRINTF(("%s: t = %p, map = %p, m0 = %p, flags = %x\n",
363 __func__, t, map, m0, flags));
364
365 /* make sure that on error condition we return "no valid mappings" */
366 map->dm_nsegs = 0;
367 map->dm_mapsize = 0;
368
369 #ifdef DIAGNOSTIC
370 if ((m0->m_flags & M_PKTHDR) == 0)
371 panic("_bus_dmamap_load_mbuf: no packet header");
372 #endif
373
374 if (m0->m_pkthdr.len > map->_dm_size)
375 return (EINVAL);
376
377 seg = 0;
378 first = 1;
379 lastaddr = 0;
380
381 for (m = m0; m != NULL; m = m->m_next) {
382 paddr_t paddr;
383 vaddr_t vaddr;
384 int size;
385
386 if (m->m_len == 0)
387 continue;
388
389 vaddr = (vaddr_t)m->m_data;
390 size = m->m_len;
391
392 if (SH3_P1SEG_BASE <= vaddr && vaddr < SH3_P3SEG_BASE) {
393 paddr = (paddr_t)(PMAP_UNMAP_POOLPAGE(vaddr));
394 error = _bus_dmamap_load_paddr(t, map,
395 paddr, vaddr, size,
396 &seg, &lastaddr, first);
397 if (error)
398 return error;
399 first = 0;
400 }
401 else {
402 /* XXX: stolen from load_buffer, need to refactor */
403 while (size > 0) {
404 bus_size_t sgsize;
405 bool mapped;
406
407 mapped = pmap_extract(pmap_kernel(), vaddr,
408 &paddr);
409 if (!mapped)
410 return EFAULT;
411
412 sgsize = PAGE_SIZE - (vaddr & PGOFSET);
413 if (size < sgsize)
414 sgsize = size;
415
416 error = _bus_dmamap_load_paddr(t, map,
417 paddr, vaddr, sgsize,
418 &seg, &lastaddr, first);
419 if (error)
420 return error;
421
422 vaddr += sgsize;
423 size -= sgsize;
424 first = 0;
425 }
426
427 }
428 }
429
430 map->dm_nsegs = seg + 1;
431 map->dm_mapsize = m0->m_pkthdr.len;
432 return 0;
433 }
434
435 /*
436 * Like _bus_dmamap_load(), but for uios.
437 */
438 int
_bus_dmamap_load_uio(bus_dma_tag_t t,bus_dmamap_t map,struct uio * uio,int flags)439 _bus_dmamap_load_uio(bus_dma_tag_t t, bus_dmamap_t map, struct uio *uio,
440 int flags)
441 {
442
443 panic("_bus_dmamap_load_uio: not implemented");
444 }
445
446 /*
447 * Like _bus_dmamap_load(), but for raw memory allocated with
448 * bus_dmamem_alloc().
449 */
450 int
_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)451 _bus_dmamap_load_raw(bus_dma_tag_t t, bus_dmamap_t map,
452 bus_dma_segment_t *segs, int nsegs, bus_size_t size, int flags)
453 {
454
455 panic("_bus_dmamap_load_raw: not implemented");
456 }
457
458 /*
459 * Unload a DMA map.
460 */
461 void
_bus_dmamap_unload(bus_dma_tag_t t,bus_dmamap_t map)462 _bus_dmamap_unload(bus_dma_tag_t t, bus_dmamap_t map)
463 {
464
465 DPRINTF(("%s: t = %p, map = %p\n", __func__, t, map));
466
467 map->dm_nsegs = 0;
468 map->dm_mapsize = 0;
469 }
470
471 /*
472 * Synchronize a DMA map.
473 */
474 void
_bus_dmamap_sync(bus_dma_tag_t t,bus_dmamap_t map,bus_addr_t offset,bus_size_t len,int ops)475 _bus_dmamap_sync(bus_dma_tag_t t, bus_dmamap_t map, bus_addr_t offset,
476 bus_size_t len, int ops)
477 {
478 bus_size_t minlen;
479 bus_addr_t addr, naddr;
480 int i;
481
482 DPRINTF(("%s: t = %p, map = %p, offset = %ld, len = %ld, ops = %x\n",
483 __func__, t, map, offset, len, ops));
484
485 #ifdef DIAGNOSTIC
486 if ((ops & (BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE)) != 0 &&
487 (ops & (BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE)) != 0)
488 panic("_bus_dmamap_sync: mix PRE and POST");
489
490 if (offset >= map->dm_mapsize)
491 panic("_bus_dmamap_sync: bad offset");
492 if ((offset + len) > map->dm_mapsize)
493 panic("_bus_dmamap_sync: bad length");
494 #endif
495
496 if (!sh_cache_enable_dcache) {
497 /* Nothing to do */
498 DPRINTF(("%s: disabled D-Cache\n", __func__));
499 return;
500 }
501
502 for (i = 0; i < map->dm_nsegs && len != 0; i++) {
503 /* Find the beginning segment. */
504 if (offset >= map->dm_segs[i].ds_len) {
505 offset -= map->dm_segs[i].ds_len;
506 continue;
507 }
508
509 /*
510 * Now at the first segment to sync; nail
511 * each segment until we have exhausted the
512 * length.
513 */
514 minlen = len < map->dm_segs[i].ds_len - offset ?
515 len : map->dm_segs[i].ds_len - offset;
516
517 addr = map->dm_segs[i]._ds_vaddr;
518 naddr = addr + offset;
519
520 if ((naddr >= SH3_P2SEG_BASE)
521 && (naddr + minlen <= SH3_P2SEG_END)) {
522 DPRINTF(("%s: P2SEG (0x%08lx)\n", __func__, naddr));
523 offset = 0;
524 len -= minlen;
525 continue;
526 }
527
528 DPRINTF(("%s: flushing segment %d "
529 "(0x%lx+%lx, 0x%lx+0x%lx) (remain = %ld)\n",
530 __func__, i,
531 addr, offset, addr, offset + minlen - 1, len));
532
533 switch (ops) {
534 case BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE:
535 if (SH_HAS_WRITEBACK_CACHE)
536 sh_dcache_wbinv_range(naddr, minlen);
537 else
538 sh_dcache_inv_range(naddr, minlen);
539 break;
540
541 case BUS_DMASYNC_PREREAD:
542 if (SH_HAS_WRITEBACK_CACHE &&
543 ((naddr | minlen) & (sh_cache_line_size - 1)) != 0)
544 sh_dcache_wbinv_range(naddr, minlen);
545 else
546 sh_dcache_inv_range(naddr, minlen);
547 break;
548
549 case BUS_DMASYNC_PREWRITE:
550 if (SH_HAS_WRITEBACK_CACHE)
551 sh_dcache_wb_range(naddr, minlen);
552 break;
553 }
554 offset = 0;
555 len -= minlen;
556 }
557 }
558
559 /*
560 * Allocate memory safe for DMA.
561 */
562 int
_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)563 _bus_dmamem_alloc(bus_dma_tag_t t, bus_size_t size, bus_size_t alignment,
564 bus_size_t boundary, bus_dma_segment_t *segs, int nsegs, int *rsegs,
565 int flags)
566 {
567 extern paddr_t avail_start, avail_end; /* from pmap.c */
568 struct pglist mlist;
569 paddr_t curaddr, lastaddr;
570 struct vm_page *m;
571 int curseg, error;
572
573 DPRINTF(("%s: t = %p, size = %ld, alignment = %ld, boundary = %ld,"
574 " segs = %p, nsegs = %d, rsegs = %p, flags = %x\n",
575 __func__, t, size, alignment, boundary,
576 segs, nsegs, rsegs, flags));
577 DPRINTF(("%s: avail_start = 0x%08lx, avail_end = 0x%08lx\n",
578 __func__, avail_start, avail_end));
579
580 /* Always round the size. */
581 size = round_page(size);
582
583 /*
584 * Allocate the pages from the VM system.
585 */
586 error = uvm_pglistalloc(size, avail_start, avail_end - PAGE_SIZE,
587 alignment, boundary, &mlist, nsegs, (flags & BUS_DMA_NOWAIT) == 0);
588 if (error)
589 return (error);
590
591 /*
592 * Compute the location, size, and number of segments actually
593 * returned by the VM code.
594 */
595 m = mlist.tqh_first;
596 curseg = 0;
597 lastaddr = segs[curseg].ds_addr = VM_PAGE_TO_PHYS(m);
598 segs[curseg].ds_len = PAGE_SIZE;
599
600 DPRINTF(("%s: m = %p, lastaddr = 0x%08lx\n", __func__, m, lastaddr));
601
602 while ((m = TAILQ_NEXT(m, pageq.queue)) != NULL) {
603 curaddr = VM_PAGE_TO_PHYS(m);
604 DPRINTF(("%s: m = %p, curaddr = 0x%08lx, lastaddr = 0x%08lx\n",
605 __func__, m, curaddr, lastaddr));
606
607 if (curaddr == (lastaddr + PAGE_SIZE)) {
608 segs[curseg].ds_len += PAGE_SIZE;
609 } else {
610 DPRINTF(("%s: new segment\n", __func__));
611 curseg++;
612 segs[curseg].ds_addr = curaddr;
613 segs[curseg].ds_len = PAGE_SIZE;
614 }
615 lastaddr = curaddr;
616 }
617
618 *rsegs = curseg + 1;
619
620 DPRINTF(("%s: curseg = %d, *rsegs = %d\n", __func__, curseg, *rsegs));
621
622 return (0);
623 }
624
625 /*
626 * Common function for freeing DMA-safe memory. May be called by
627 * bus-specific DMA memory free functions.
628 */
629 void
_bus_dmamem_free(bus_dma_tag_t t,bus_dma_segment_t * segs,int nsegs)630 _bus_dmamem_free(bus_dma_tag_t t, bus_dma_segment_t *segs, int nsegs)
631 {
632 struct vm_page *m;
633 bus_addr_t addr;
634 struct pglist mlist;
635 int curseg;
636
637 DPRINTF(("%s: t = %p, segs = %p, nsegs = %d\n",
638 __func__, t, segs, nsegs));
639
640 /*
641 * Build a list of pages to free back to the VM system.
642 */
643 TAILQ_INIT(&mlist);
644 for (curseg = 0; curseg < nsegs; curseg++) {
645 DPRINTF(("%s: segs[%d]: ds_addr = 0x%08lx, ds_len = %ld\n",
646 __func__, curseg,
647 segs[curseg].ds_addr, segs[curseg].ds_len));
648
649 for (addr = segs[curseg].ds_addr;
650 addr < (segs[curseg].ds_addr + segs[curseg].ds_len);
651 addr += PAGE_SIZE)
652 {
653 m = PHYS_TO_VM_PAGE(addr);
654 DPRINTF(("%s: m = %p\n", __func__, m));
655 TAILQ_INSERT_TAIL(&mlist, m, pageq.queue);
656 }
657 }
658
659 uvm_pglistfree(&mlist);
660 }
661
662
663 int
_bus_dmamem_map(bus_dma_tag_t t,bus_dma_segment_t * segs,int nsegs,size_t size,void ** kvap,int flags)664 _bus_dmamem_map(bus_dma_tag_t t, bus_dma_segment_t *segs, int nsegs,
665 size_t size, void **kvap, int flags)
666 {
667 vaddr_t va, topva;
668 bus_addr_t addr;
669 int curseg;
670
671 DPRINTF(("%s: t = %p, segs = %p, nsegs = %d, size = %d,"
672 " kvap = %p, flags = %x\n",
673 __func__, t, segs, nsegs, size, kvap, flags));
674
675 /*
676 * If we're mapping only a single segment, use direct-mapped
677 * va, to avoid thrashing the TLB.
678 */
679 if (nsegs == 1) {
680 if (flags & BUS_DMA_COHERENT)
681 *kvap = (void *)SH3_PHYS_TO_P2SEG(segs[0].ds_addr);
682 else
683 *kvap = (void *)SH3_PHYS_TO_P1SEG(segs[0].ds_addr);
684
685 DPRINTF(("%s: addr = 0x%08lx, kva = %p\n",
686 __func__, segs[0].ds_addr, *kvap));
687 return 0;
688 }
689
690
691 /* Always round the size. */
692 size = round_page(size);
693
694 va = uvm_km_alloc(kernel_map, size, 0, UVM_KMF_VAONLY
695 | ((flags & BUS_DMA_NOWAIT) ? UVM_KMF_NOWAIT : 0));
696 if (va == 0)
697 return (ENOMEM);
698 topva = va;
699
700 for (curseg = 0; curseg < nsegs; curseg++) {
701 DPRINTF(("%s: segs[%d]: ds_addr = 0x%08lx, ds_len = %ld\n",
702 __func__, curseg,
703 segs[curseg].ds_addr, segs[curseg].ds_len));
704
705 for (addr = segs[curseg].ds_addr;
706 addr < segs[curseg].ds_addr + segs[curseg].ds_len;
707 addr += PAGE_SIZE, va += PAGE_SIZE, size -= PAGE_SIZE)
708 {
709 if (__predict_false(size == 0))
710 panic("_bus_dmamem_map: size botch");
711
712 pmap_kenter_pa(va, addr,
713 VM_PROT_READ | VM_PROT_WRITE, 0);
714 }
715 }
716
717 pmap_update(pmap_kernel());
718 *kvap = (void *)topva;
719 return (0);
720 }
721
722 void
_bus_dmamem_unmap(bus_dma_tag_t t,void * kva,size_t size)723 _bus_dmamem_unmap(bus_dma_tag_t t, void *kva, size_t size)
724 {
725 vaddr_t vaddr = (vaddr_t)kva;
726
727 DPRINTF(("%s: t = %p, kva = %p, size = %d\n",
728 __func__, t, kva, size));
729
730 #ifdef DIAGNOSTIC
731 if (vaddr & PAGE_MASK)
732 panic("_bus_dmamem_unmap");
733 #endif
734
735 /* nothing to do if we mapped it via P1SEG or P2SEG */
736 if (SH3_P1SEG_BASE <= vaddr && vaddr <= SH3_P2SEG_END)
737 return;
738
739 size = round_page(size);
740 pmap_kremove(vaddr, size);
741 pmap_update(pmap_kernel());
742 uvm_km_free(kernel_map, vaddr, size, UVM_KMF_VAONLY);
743 }
744
745 paddr_t
_bus_dmamem_mmap(bus_dma_tag_t t,bus_dma_segment_t * segs,int nsegs,off_t off,int prot,int flags)746 _bus_dmamem_mmap(bus_dma_tag_t t, bus_dma_segment_t *segs, int nsegs,
747 off_t off, int prot, int flags)
748 {
749
750 /* Not implemented. */
751 return (paddr_t)(-1);
752 }
753