1 /*        $NetBSD: bus.c,v 1.39 2024/01/07 07:58:35 isaki Exp $       */
2 
3 /*-
4  * Copyright (c) 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.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * bus_space(9) and bus_dma(9) implementation for NetBSD/x68k.
34  * These are default implementations; some buses may use their own.
35  */
36 
37 #include "opt_m68k_arch.h"
38 
39 #include <sys/cdefs.h>
40 __KERNEL_RCSID(0, "$NetBSD: bus.c,v 1.39 2024/01/07 07:58:35 isaki Exp $");
41 
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/malloc.h>
45 #include <sys/mbuf.h>
46 #include <sys/kernel.h>
47 #include <sys/conf.h>
48 #include <sys/device.h>
49 #include <sys/proc.h>
50 
51 #include <uvm/uvm_extern.h>
52 
53 #include <m68k/cacheops.h>
54 #include <machine/bus.h>
55 
56 #include <dev/bus_dma/bus_dmamem_common.h>
57 
58 #if defined(M68040) || defined(M68060)
59 static inline void dmasync_flush(bus_addr_t, bus_size_t);
60 static inline void dmasync_inval(bus_addr_t, bus_size_t);
61 #endif
62 
63 int
x68k_bus_space_alloc(bus_space_tag_t t,bus_addr_t rstart,bus_addr_t rend,bus_size_t size,bus_size_t alignment,bus_size_t boundary,int flags,bus_addr_t * bpap,bus_space_handle_t * bshp)64 x68k_bus_space_alloc(bus_space_tag_t t, bus_addr_t rstart, bus_addr_t rend,
65     bus_size_t size, bus_size_t alignment, bus_size_t boundary, int flags,
66     bus_addr_t *bpap, bus_space_handle_t *bshp)
67 {
68           return (EINVAL);
69 }
70 
71 void
x68k_bus_space_free(bus_space_tag_t t,bus_space_handle_t bsh,bus_size_t size)72 x68k_bus_space_free(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t size)
73 {
74           panic("bus_space_free: shouldn't be here");
75 }
76 
77 
78 extern paddr_t avail_end;
79 
80 /*
81  * Common function for DMA map creation.  May be called by bus-specific
82  * DMA map creation functions.
83  */
84 int
x68k_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)85 x68k_bus_dmamap_create(bus_dma_tag_t t, bus_size_t size, int nsegments,
86     bus_size_t maxsegsz, bus_size_t boundary, int flags, bus_dmamap_t *dmamp)
87 {
88           struct x68k_bus_dmamap *map;
89           void *mapstore;
90           size_t mapsize;
91 
92           /*
93            * Allocate and initialize the DMA map.  The end of the map
94            * is a variable-sized array of segments, so we allocate enough
95            * room for them in one shot.
96            *
97            * Note we don't preserve the WAITOK or NOWAIT flags.  Preservation
98            * of ALLOCNOW notifies others that we've reserved these resources,
99            * and they are not to be freed.
100            *
101            * The bus_dmamap_t includes one bus_dma_segment_t, hence
102            * the (nsegments - 1).
103            */
104           mapsize = sizeof(struct x68k_bus_dmamap) +
105               (sizeof(bus_dma_segment_t) * (nsegments - 1));
106           if ((mapstore = malloc(mapsize, M_DMAMAP,
107               (flags & BUS_DMA_NOWAIT) ? M_NOWAIT : M_WAITOK)) == NULL)
108                     return (ENOMEM);
109 
110           memset(mapstore, 0, mapsize);
111           map = (struct x68k_bus_dmamap *)mapstore;
112           map->x68k_dm_size = size;
113           map->x68k_dm_segcnt = nsegments;
114           map->x68k_dm_maxmaxsegsz = maxsegsz;
115           map->x68k_dm_boundary = boundary;
116           map->x68k_dm_bounce_thresh = t->_bounce_thresh;
117           map->x68k_dm_flags = flags & ~(BUS_DMA_WAITOK|BUS_DMA_NOWAIT);
118           map->dm_maxsegsz = maxsegsz;
119           map->dm_mapsize = 0;                    /* no valid mappings */
120           map->dm_nsegs = 0;
121 
122           *dmamp = map;
123           return (0);
124 }
125 
126 /*
127  * Common function for DMA map destruction.  May be called by bus-specific
128  * DMA map destruction functions.
129  */
130 void
x68k_bus_dmamap_destroy(bus_dma_tag_t t,bus_dmamap_t map)131 x68k_bus_dmamap_destroy(bus_dma_tag_t t, bus_dmamap_t map)
132 {
133 
134           free(map, M_DMAMAP);
135 }
136 
137 /*
138  * Common function for loading a DMA map with a linear buffer.  May
139  * be called by bus-specific DMA map load functions.
140  */
141 int
x68k_bus_dmamap_load(bus_dma_tag_t t,bus_dmamap_t map,void * buf,bus_size_t buflen,struct proc * p,int flags)142 x68k_bus_dmamap_load(bus_dma_tag_t t, bus_dmamap_t map, void *buf,
143     bus_size_t buflen, struct proc *p, int flags)
144 {
145           paddr_t lastaddr;
146           int seg, error;
147 
148           /*
149            * Make sure that on error condition we return "no valid mappings".
150            */
151           map->dm_mapsize = 0;
152           map->dm_nsegs = 0;
153           KASSERT(map->dm_maxsegsz <= map->x68k_dm_maxmaxsegsz);
154 
155           if (buflen > map->x68k_dm_size)
156                     return (EINVAL);
157 
158           seg = 0;
159           error = x68k_bus_dmamap_load_buffer(map, buf, buflen, p, flags,
160               &lastaddr, &seg, 1);
161           if (error == 0) {
162                     map->dm_mapsize = buflen;
163                     map->dm_nsegs = seg + 1;
164           }
165           return (error);
166 }
167 
168 /*
169  * Like x68k_bus_dmamap_load(), but for mbufs.
170  */
171 int
x68k_bus_dmamap_load_mbuf(bus_dma_tag_t t,bus_dmamap_t map,struct mbuf * m0,int flags)172 x68k_bus_dmamap_load_mbuf(bus_dma_tag_t t, bus_dmamap_t map, struct mbuf *m0,
173     int flags)
174 {
175           paddr_t lastaddr;
176           int seg, error, first;
177           struct mbuf *m;
178 
179           /*
180            * Make sure that on error condition we return "no valid mappings."
181            */
182           map->dm_mapsize = 0;
183           map->dm_nsegs = 0;
184           KASSERT(map->dm_maxsegsz <= map->x68k_dm_maxmaxsegsz);
185 
186 #ifdef DIAGNOSTIC
187           if ((m0->m_flags & M_PKTHDR) == 0)
188                     panic("x68k_bus_dmamap_load_mbuf: no packet header");
189 #endif
190 
191           if (m0->m_pkthdr.len > map->x68k_dm_size)
192                     return (EINVAL);
193 
194           first = 1;
195           seg = 0;
196           error = 0;
197           for (m = m0; m != NULL && error == 0; m = m->m_next) {
198                     if (m->m_len == 0)
199                               continue;
200                     error = x68k_bus_dmamap_load_buffer(map, m->m_data, m->m_len,
201                         NULL, flags, &lastaddr, &seg, first);
202                     first = 0;
203           }
204           if (error == 0) {
205                     map->dm_mapsize = m0->m_pkthdr.len;
206                     map->dm_nsegs = seg + 1;
207           }
208           return (error);
209 }
210 
211 /*
212  * Like x68k_bus_dmamap_load(), but for uios.
213  */
214 int
x68k_bus_dmamap_load_uio(bus_dma_tag_t t,bus_dmamap_t map,struct uio * uio,int flags)215 x68k_bus_dmamap_load_uio(bus_dma_tag_t t, bus_dmamap_t map, struct uio *uio,
216     int flags)
217 {
218 #if 0
219           paddr_t lastaddr;
220           int seg, i, error, first;
221           bus_size_t minlen, resid;
222           struct proc *p = NULL;
223           struct iovec *iov;
224           void *addr;
225 
226           /*
227            * Make sure that on error condition we return "no valid mappings."
228            */
229           map->dm_mapsize = 0;
230           map->dm_nsegs = 0;
231           KASSERT(map->dm_maxsegsz <= map->x68k_dm_maxmaxsegsz);
232 
233           resid = uio->uio_resid;
234           iov = uio->uio_iov;
235 
236           if (uio->uio_segflg == UIO_USERSPACE) {
237                     p = uio->uio_lwp ? uio->uio_lwp->l_proc : NULL;
238 #ifdef DIAGNOSTIC
239                     if (p == NULL)
240                               panic("_bus_dmamap_load_uio: USERSPACE but no proc");
241 #endif
242           }
243 
244           first = 1;
245           seg = 0;
246           error = 0;
247           for (i = 0; i < uio->uio_iovcnt && resid != 0 && error == 0; i++) {
248                     /*
249                      * Now at the first iovec to load.  Load each iovec
250                      * until we have exhausted the residual count.
251                      */
252                     minlen = resid < iov[i].iov_len ? resid : iov[i].iov_len;
253                     addr = (void *)iov[i].iov_base;
254 
255                     error = x68k_bus_dmamap_load_buffer(map, addr, minlen,
256                         p, flags, &lastaddr, &seg, first);
257                     first = 0;
258 
259                     resid -= minlen;
260           }
261           if (error == 0) {
262                     map->dm_mapsize = uio->uio_resid;
263                     map->dm_nsegs = seg + 1;
264           }
265           return (error);
266 #else
267           panic ("x68k_bus_dmamap_load_uio: not implemented");
268 #endif
269 }
270 
271 /*
272  * Like x68k_bus_dmamap_load(), but for raw memory allocated with
273  * bus_dmamem_alloc().
274  */
275 int
x68k_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)276 x68k_bus_dmamap_load_raw(bus_dma_tag_t t, bus_dmamap_t map,
277     bus_dma_segment_t *segs, int nsegs, bus_size_t size, int flags)
278 {
279 
280           panic("x68k_bus_dmamap_load_raw: not implemented");
281 }
282 
283 /*
284  * Common function for unloading a DMA map.  May be called by
285  * bus-specific DMA map unload functions.
286  */
287 void
x68k_bus_dmamap_unload(bus_dma_tag_t t,bus_dmamap_t map)288 x68k_bus_dmamap_unload(bus_dma_tag_t t, bus_dmamap_t map)
289 {
290 
291           /*
292            * No resources to free; just mark the mappings as
293            * invalid.
294            */
295           map->dm_maxsegsz = map->x68k_dm_maxmaxsegsz;
296           map->dm_mapsize = 0;
297           map->dm_nsegs = 0;
298 }
299 
300 #if defined(M68040) || defined(M68060)
301 static inline void
dmasync_flush(bus_addr_t addr,bus_size_t len)302 dmasync_flush(bus_addr_t addr, bus_size_t len)
303 {
304           bus_addr_t end = addr+len;
305 
306           if (len <= 1024) {
307                     addr = addr & ~0xF;
308 
309                     do {
310                               DCFL(addr);
311                               addr += 16;
312                     } while (addr < end);
313           } else {
314                     addr = m68k_trunc_page(addr);
315 
316                     do {
317                               DCFP(addr);
318                               addr += PAGE_SIZE;
319                     } while (addr < end);
320           }
321 }
322 
323 static inline void
dmasync_inval(bus_addr_t addr,bus_size_t len)324 dmasync_inval(bus_addr_t addr, bus_size_t len)
325 {
326           bus_addr_t end = addr+len;
327 
328           if (len <= 1024) {
329                     addr = addr & ~0xF;
330 
331                     do {
332                               DCFL(addr);
333                               ICPL(addr);
334                               addr += 16;
335                     } while (addr < end);
336           } else {
337                     addr = m68k_trunc_page(addr);
338 
339                     do {
340                               DCPL(addr);
341                               ICPP(addr);
342                               addr += PAGE_SIZE;
343                     } while (addr < end);
344           }
345 }
346 #endif
347 
348 /*
349  * Common function for DMA map synchronization.  May be called
350  * by bus-specific DMA map synchronization functions.
351  */
352 void
x68k_bus_dmamap_sync(bus_dma_tag_t t,bus_dmamap_t map,bus_addr_t offset,bus_size_t len,int ops)353 x68k_bus_dmamap_sync(bus_dma_tag_t t, bus_dmamap_t map, bus_addr_t offset,
354     bus_size_t len, int ops)
355 {
356 #if defined(M68040) || defined(M68060)
357           bus_dma_segment_t *ds = map->dm_segs;
358           bus_addr_t seg;
359           int i;
360 
361           if ((ops & (BUS_DMASYNC_PREREAD|BUS_DMASYNC_POSTWRITE)) == 0)
362                     return;
363 #if defined(M68020) || defined(M68030)
364           if (mmutype != MMU_68040) {
365                     if ((ops & BUS_DMASYNC_POSTWRITE) == 0)
366                               return;   /* no copyback cache */
367                     ICIA();             /* no per-page/per-line control */
368                     DCIA();
369                     return;
370           }
371 #endif
372           if (offset >= map->dm_mapsize)
373                     return;   /* driver bug; warn it? */
374           if (offset+len > map->dm_mapsize)
375                     len = map->dm_mapsize; /* driver bug; warn it? */
376 
377           i = 0;
378           while (ds[i].ds_len <= offset) {
379                     offset -= ds[i++].ds_len;
380                     continue;
381           }
382           while (len > 0) {
383                     seg = ds[i].ds_len - offset;
384                     if (seg > len)
385                               seg = len;
386                     if (mmutype == MMU_68040 && (ops & BUS_DMASYNC_PREWRITE))
387                               dmasync_flush(ds[i].ds_addr+offset, seg);
388                     if (ops & BUS_DMASYNC_POSTREAD)
389                               dmasync_inval(ds[i].ds_addr+offset, seg);
390                     offset = 0;
391                     len -= seg;
392                     i++;
393           }
394 #else  /* no 040/060 */
395           if ((ops & BUS_DMASYNC_POSTWRITE)) {
396                     ICIA();             /* no per-page/per-line control */
397                     DCIA();
398           }
399 #endif
400 }
401 
402 /*
403  * Common function for DMA-safe memory allocation.  May be called
404  * by bus-specific DMA memory allocation functions.
405  */
406 int
x68k_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)407 x68k_bus_dmamem_alloc(bus_dma_tag_t t, bus_size_t size, bus_size_t alignment,
408     bus_size_t boundary, bus_dma_segment_t *segs, int nsegs, int *rsegs,
409     int flags)
410 {
411 
412           return (x68k_bus_dmamem_alloc_range(t, size, alignment, boundary,
413               segs, nsegs, rsegs, flags, 0, trunc_page(avail_end)));
414 }
415 
416 /*
417  * Common function for freeing DMA-safe memory.  May be called by
418  * bus-specific DMA memory free functions.
419  */
420 void
x68k_bus_dmamem_free(bus_dma_tag_t t,bus_dma_segment_t * segs,int nsegs)421 x68k_bus_dmamem_free(bus_dma_tag_t t, bus_dma_segment_t *segs, int nsegs)
422 {
423 
424           _bus_dmamem_free_common(t, segs, nsegs);
425 }
426 
427 /*
428  * Common function for mapping DMA-safe memory.  May be called by
429  * bus-specific DMA memory map functions.
430  */
431 int
x68k_bus_dmamem_map(bus_dma_tag_t t,bus_dma_segment_t * segs,int nsegs,size_t size,void ** kvap,int flags)432 x68k_bus_dmamem_map(bus_dma_tag_t t, bus_dma_segment_t *segs, int nsegs,
433     size_t size, void **kvap, int flags)
434 {
435 
436           /* XXX BUS_DMA_COHERENT */
437           return (_bus_dmamem_map_common(t, segs, nsegs, size, kvap, flags, 0));
438 }
439 
440 /*
441  * Common function for unmapping DMA-safe memory.  May be called by
442  * bus-specific DMA memory unmapping functions.
443  */
444 void
x68k_bus_dmamem_unmap(bus_dma_tag_t t,void * kva,size_t size)445 x68k_bus_dmamem_unmap(bus_dma_tag_t t, void *kva, size_t size)
446 {
447 
448           _bus_dmamem_unmap_common(t, kva, size);
449 }
450 
451 /*
452  * Common function for mmap(2)'ing DMA-safe memory.  May be called by
453  * bus-specific DMA mmap(2)'ing functions.
454  */
455 paddr_t
x68k_bus_dmamem_mmap(bus_dma_tag_t t,bus_dma_segment_t * segs,int nsegs,off_t off,int prot,int flags)456 x68k_bus_dmamem_mmap(bus_dma_tag_t t, bus_dma_segment_t *segs, int nsegs,
457     off_t off, int prot, int flags)
458 {
459           bus_addr_t rv;
460 
461           rv = _bus_dmamem_mmap_common(t, segs, nsegs, off, prot, flags);
462           if (rv == (bus_addr_t)-1)
463                     return (-1);
464 
465           return (m68k_btop((char *)rv));
466 }
467 
468 
469 /**********************************************************************
470  * DMA utility functions
471  **********************************************************************/
472 
473 /*
474  * Utility function to load a linear buffer.  lastaddrp holds state
475  * between invocations (for multiple-buffer loads).  segp contains
476  * the starting segment on entrance, and the ending segment on exit.
477  * first indicates if this is the first invocation of this function.
478  */
479 int
x68k_bus_dmamap_load_buffer(bus_dmamap_t map,void * buf,bus_size_t buflen,struct proc * p,int flags,paddr_t * lastaddrp,int * segp,int first)480 x68k_bus_dmamap_load_buffer(bus_dmamap_t map, void *buf, bus_size_t buflen,
481      struct proc *p, int flags, paddr_t *lastaddrp, int *segp, int first)
482 {
483           bus_size_t sgsize;
484           bus_addr_t curaddr, lastaddr, baddr, bmask;
485           vaddr_t vaddr = (vaddr_t)buf;
486           int seg;
487           pmap_t pmap;
488 
489           if (p != NULL)
490                     pmap = p->p_vmspace->vm_map.pmap;
491           else
492                     pmap = pmap_kernel();
493 
494           lastaddr = *lastaddrp;
495           bmask  = ~(map->x68k_dm_boundary - 1);
496 
497           for (seg = *segp; buflen > 0 ; ) {
498                     /*
499                      * Get the physical address for this segment.
500                      */
501                     (void) pmap_extract(pmap, vaddr, &curaddr);
502 
503                     /*
504                      * If we're beyond the bounce threshold, notify
505                      * the caller.
506                      */
507                     if (map->x68k_dm_bounce_thresh != 0 &&
508                         curaddr >= map->x68k_dm_bounce_thresh)
509                               return (EINVAL);
510 
511                     /*
512                      * Compute the segment size, and adjust counts.
513                      */
514                     sgsize = PAGE_SIZE - m68k_page_offset(vaddr);
515                     if (buflen < sgsize)
516                               sgsize = buflen;
517 
518                     /*
519                      * Make sure we don't cross any boundaries.
520                      */
521                     if (map->x68k_dm_boundary > 0) {
522                               baddr = (curaddr + map->x68k_dm_boundary) & bmask;
523                               if (sgsize > (baddr - curaddr))
524                                         sgsize = (baddr - curaddr);
525                     }
526 
527                     /*
528                      * Insert chunk into a segment, coalescing with
529                      * previous segment if possible.
530                      */
531                     if (first) {
532                               map->dm_segs[seg].ds_addr = curaddr;
533                               map->dm_segs[seg].ds_len = sgsize;
534                               first = 0;
535                     } else {
536                               if (curaddr == lastaddr &&
537                                   (map->dm_segs[seg].ds_len + sgsize) <=
538                                    map->dm_maxsegsz &&
539                                   (map->x68k_dm_boundary == 0 ||
540                                    (map->dm_segs[seg].ds_addr & bmask) ==
541                                    (curaddr & bmask)))
542                                         map->dm_segs[seg].ds_len += sgsize;
543                               else {
544                                         if (++seg >= map->x68k_dm_segcnt)
545                                                   break;
546                                         map->dm_segs[seg].ds_addr = curaddr;
547                                         map->dm_segs[seg].ds_len = sgsize;
548                               }
549                     }
550 
551                     lastaddr = curaddr + sgsize;
552                     vaddr += sgsize;
553                     buflen -= sgsize;
554           }
555 
556           *segp = seg;
557           *lastaddrp = lastaddr;
558 
559           /*
560            * Did we fit?
561            */
562           if (buflen != 0)
563                     return (EFBIG);               /* XXX better return value here? */
564           return (0);
565 }
566 
567 /*
568  * Allocate physical memory from the given physical address range.
569  * Called by DMA-safe memory allocation methods.
570  */
571 int
x68k_bus_dmamem_alloc_range(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,paddr_t low,paddr_t high)572 x68k_bus_dmamem_alloc_range(bus_dma_tag_t t, bus_size_t size,
573     bus_size_t alignment, bus_size_t boundary, bus_dma_segment_t *segs,
574     int nsegs, int *rsegs, int flags, paddr_t low, paddr_t high)
575 {
576 
577           return (_bus_dmamem_alloc_range_common(t, size, alignment, boundary,
578                                                          segs, nsegs, rsegs, flags,
579                                                          low, high));
580 }
581