]> CyberLeo.Net >> Repos - FreeBSD/releng/8.2.git/blob - sys/sun4v/sun4v/bus_machdep.c
MF8 r217553:
[FreeBSD/releng/8.2.git] / sys / sun4v / sun4v / bus_machdep.c
1 /*-
2  * Copyright (c) 1996, 1997, 1998 The NetBSD Foundation, Inc.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to The NetBSD Foundation
6  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
7  * NASA Ames Research Center.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *      This product includes software developed by the NetBSD
20  *      Foundation, Inc. and its contributors.
21  * 4. Neither the name of The NetBSD Foundation nor the names of its
22  *    contributors may be used to endorse or promote products derived
23  *    from this software without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
26  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
29  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35  * POSSIBILITY OF SUCH DAMAGE.
36  */
37 /*-
38  * Copyright (c) 1992, 1993
39  *      The Regents of the University of California.  All rights reserved.
40  *
41  * This software was developed by the Computer Systems Engineering group
42  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
43  * contributed to Berkeley.
44  *
45  * Redistribution and use in source and binary forms, with or without
46  * modification, are permitted provided that the following conditions
47  * are met:
48  * 1. Redistributions of source code must retain the above copyright
49  *    notice, this list of conditions and the following disclaimer.
50  * 2. Redistributions in binary form must reproduce the above copyright
51  *    notice, this list of conditions and the following disclaimer in the
52  *    documentation and/or other materials provided with the distribution.
53  * 4. Neither the name of the University nor the names of its contributors
54  *    may be used to endorse or promote products derived from this software
55  *    without specific prior written permission.
56  *
57  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
58  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
59  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
60  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
61  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
62  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
63  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
64  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
65  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
66  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
67  * SUCH DAMAGE.
68  */
69 /*-
70  * Copyright (c) 1997, 1998 Justin T. Gibbs.
71  * All rights reserved.
72  * Copyright 2001 by Thomas Moestl <tmm@FreeBSD.org>.  All rights reserved.
73  *
74  * Redistribution and use in source and binary forms, with or without
75  * modification, are permitted provided that the following conditions
76  * are met:
77  * 1. Redistributions of source code must retain the above copyright
78  *    notice, this list of conditions, and the following disclaimer,
79  *    without modification, immediately at the beginning of the file.
80  * 2. The name of the author may not be used to endorse or promote products
81  *    derived from this software without specific prior written permission.
82  *
83  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
84  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
85  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
86  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
87  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
88  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
89  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
90  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
91  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
92  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
93  * SUCH DAMAGE.
94  *
95  *      from: @(#)machdep.c     8.6 (Berkeley) 1/14/94
96  *      from: NetBSD: machdep.c,v 1.111 2001/09/15 07:13:40 eeh Exp
97  *      and
98  *      from: FreeBSD: src/sys/i386/i386/busdma_machdep.c,v 1.24 2001/08/15
99  */
100
101 #include <sys/cdefs.h>
102 __FBSDID("$FreeBSD$");
103
104 #include <sys/param.h>
105 #include <sys/bus.h>
106 #include <sys/lock.h>
107 #include <sys/malloc.h>
108 #include <sys/mbuf.h>
109 #include <sys/mutex.h>
110 #include <sys/proc.h>
111 #include <sys/smp.h>
112 #include <sys/systm.h>
113 #include <sys/uio.h>
114
115 #include <vm/vm.h>
116 #include <vm/vm_extern.h>
117 #include <vm/vm_kern.h>
118 #include <vm/vm_page.h>
119 #include <vm/vm_param.h>
120 #include <vm/vm_map.h>
121
122 #include <machine/asi.h>
123 #include <machine/atomic.h>
124 #include <machine/bus.h>
125 #include <machine/bus_private.h>
126 #include <machine/smp.h>
127 #include <machine/tlb.h>
128
129 static void nexus_bus_barrier(bus_space_tag_t, bus_space_handle_t,
130     bus_size_t, bus_size_t, int);
131
132 /* ASIs for bus access */
133 const int bus_type_asi[] = {
134         ASI_REAL_IO,            /* nexus */
135         ASI_REAL_IO,            /* SBus */
136         ASI_REAL_IO_L,          /* PCI configuration space */
137         ASI_REAL_IO_L,          /* PCI memory space */
138         ASI_REAL_IO_L,          /* PCI I/O space */
139         0
140 };
141
142 const int bus_stream_asi[] = {
143         ASI_REAL_IO,            /* nexus */
144         ASI_REAL_IO,            /* SBus */
145         ASI_REAL_IO,            /* PCI configuration space */
146         ASI_REAL_IO,            /* PCI memory space */
147         ASI_REAL_IO,            /* PCI I/O space */
148         0
149 };
150
151 /*
152  * Convenience function for manipulating driver locks from busdma (during
153  * busdma_swi, for example).  Drivers that don't provide their own locks
154  * should specify &Giant to dmat->lockfuncarg.  Drivers that use their own
155  * non-mutex locking scheme don't have to use this at all.
156  */
157 void
158 busdma_lock_mutex(void *arg, bus_dma_lock_op_t op)
159 {
160         struct mtx *dmtx;
161
162         dmtx = (struct mtx *)arg;
163         switch (op) {
164         case BUS_DMA_LOCK:
165                 mtx_lock(dmtx);
166                 break;
167         case BUS_DMA_UNLOCK:
168                 mtx_unlock(dmtx);
169                 break;
170         default:
171                 panic("Unknown operation 0x%x for busdma_lock_mutex!", op);
172         }
173 }
174
175 /*
176  * dflt_lock should never get called.  It gets put into the dma tag when
177  * lockfunc == NULL, which is only valid if the maps that are associated
178  * with the tag are meant to never be defered.
179  * XXX Should have a way to identify which driver is responsible here.
180  */
181 static void
182 dflt_lock(void *arg, bus_dma_lock_op_t op)
183 {
184
185         panic("driver error: busdma dflt_lock called");
186 }
187
188 /*
189  * Since there is no way for a device to obtain a dma tag from its parent
190  * we use this kluge to handle different the different supported bus systems.
191  * The sparc64_root_dma_tag is used as parent for tags that have none, so that
192  * the correct methods will be used.
193  */
194 bus_dma_tag_t sparc64_root_dma_tag;
195
196 /*
197  * Allocate a device specific dma_tag.
198  */
199 int
200 bus_dma_tag_create(bus_dma_tag_t parent, bus_size_t alignment,
201     bus_size_t boundary, bus_addr_t lowaddr, bus_addr_t highaddr,
202     bus_dma_filter_t *filter, void *filterarg, bus_size_t maxsize,
203     int nsegments, bus_size_t maxsegsz, int flags, bus_dma_lock_t *lockfunc,
204     void *lockfuncarg, bus_dma_tag_t *dmat)
205 {
206         bus_dma_tag_t impptag;
207         bus_dma_tag_t newtag;
208
209         /* Return a NULL tag on failure */
210         *dmat = NULL;
211
212         newtag = (bus_dma_tag_t)malloc(sizeof(*newtag), M_DEVBUF, M_NOWAIT);
213         if (newtag == NULL)
214                 return (ENOMEM);
215
216         impptag = parent != NULL ? parent : sparc64_root_dma_tag;
217         /*
218          * The method table pointer and the cookie need to be taken over from
219          * the parent or the root tag.
220          */
221         newtag->dt_cookie = impptag->dt_cookie;
222         newtag->dt_mt = impptag->dt_mt;
223
224         newtag->dt_parent = parent;
225         newtag->dt_alignment = alignment;
226         newtag->dt_boundary = boundary;
227         newtag->dt_lowaddr = trunc_page((vm_offset_t)lowaddr) + (PAGE_SIZE - 1);
228         newtag->dt_highaddr = trunc_page((vm_offset_t)highaddr) +
229             (PAGE_SIZE - 1);
230         newtag->dt_filter = filter;
231         newtag->dt_filterarg = filterarg;
232         newtag->dt_maxsize = maxsize;
233         newtag->dt_nsegments = nsegments;
234         newtag->dt_maxsegsz = maxsegsz;
235         newtag->dt_flags = flags;
236         newtag->dt_ref_count = 1; /* Count ourselves */
237         newtag->dt_map_count = 0;
238
239         if (lockfunc != NULL) {
240                 newtag->dt_lockfunc = lockfunc;
241                 newtag->dt_lockfuncarg = lockfuncarg;
242         } else {
243                 newtag->dt_lockfunc = dflt_lock;
244                 newtag->dt_lockfuncarg = NULL;
245         }
246
247         newtag->dt_segments = NULL;
248
249         /* Take into account any restrictions imposed by our parent tag. */
250         if (parent != NULL) {
251                 newtag->dt_lowaddr = ulmin(parent->dt_lowaddr,
252                     newtag->dt_lowaddr);
253                 newtag->dt_highaddr = ulmax(parent->dt_highaddr,
254                     newtag->dt_highaddr);
255                 if (newtag->dt_boundary == 0)
256                         newtag->dt_boundary = parent->dt_boundary;
257                 else if (parent->dt_boundary != 0)
258                         newtag->dt_boundary = ulmin(parent->dt_boundary,
259                             newtag->dt_boundary);
260                 atomic_add_int(&parent->dt_ref_count, 1);
261         }
262
263         if (newtag->dt_boundary > 0)
264                 newtag->dt_maxsegsz = ulmin(newtag->dt_maxsegsz,
265                     newtag->dt_boundary);
266
267         *dmat = newtag;
268         return (0);
269 }
270
271 int
272 bus_dma_tag_destroy(bus_dma_tag_t dmat)
273 {
274         bus_dma_tag_t parent;
275
276         if (dmat != NULL) {
277                 if (dmat->dt_map_count != 0)
278                         return (EBUSY);
279                 while (dmat != NULL) {
280                         parent = dmat->dt_parent;
281                         atomic_subtract_int(&dmat->dt_ref_count, 1);
282                         if (dmat->dt_ref_count == 0) {
283                                 if (dmat->dt_segments != NULL)
284                                         free(dmat->dt_segments, M_DEVBUF);
285                                 free(dmat, M_DEVBUF);
286                                 /*
287                                  * Last reference count, so
288                                  * release our reference
289                                  * count on our parent.
290                                  */
291                                 dmat = parent;
292                         } else
293                                 dmat = NULL;
294                 }
295         }
296         return (0);
297 }
298
299 /* Allocate/free a tag, and do the necessary management work. */
300 int
301 sparc64_dma_alloc_map(bus_dma_tag_t dmat, bus_dmamap_t *mapp)
302 {
303
304         if (dmat->dt_segments == NULL) {
305                 dmat->dt_segments = (bus_dma_segment_t *)malloc(
306                     sizeof(bus_dma_segment_t) * dmat->dt_nsegments, M_DEVBUF,
307                     M_NOWAIT);
308                 if (dmat->dt_segments == NULL)
309                         return (ENOMEM);
310         }
311         *mapp = malloc(sizeof(**mapp), M_DEVBUF, M_NOWAIT | M_ZERO);
312         if (*mapp == NULL)
313                 return (ENOMEM);
314
315         SLIST_INIT(&(*mapp)->dm_reslist);
316         dmat->dt_map_count++;
317         return (0);
318 }
319
320 void
321 sparc64_dma_free_map(bus_dma_tag_t dmat, bus_dmamap_t map)
322 {
323
324         free(map, M_DEVBUF);
325         dmat->dt_map_count--;
326 }
327
328 static int
329 nexus_dmamap_create(bus_dma_tag_t dmat, int flags, bus_dmamap_t *mapp)
330 {
331
332         return (sparc64_dma_alloc_map(dmat, mapp));
333 }
334
335 static int
336 nexus_dmamap_destroy(bus_dma_tag_t dmat, bus_dmamap_t map)
337 {
338
339         sparc64_dma_free_map(dmat, map);
340         return (0);
341 }
342
343 /*
344  * Utility function to load a linear buffer.  lastaddrp holds state
345  * between invocations (for multiple-buffer loads).  segp contains
346  * the starting segment on entrace, and the ending segment on exit.
347  * first indicates if this is the first invocation of this function.
348  */
349 static int
350 _nexus_dmamap_load_buffer(bus_dma_tag_t dmat, void *buf, bus_size_t buflen,
351     struct thread *td, int flags, bus_addr_t *lastaddrp,
352     bus_dma_segment_t *segs, int *segp, int first)
353 {
354         bus_size_t sgsize;
355         bus_addr_t curaddr, lastaddr, baddr, bmask;
356         vm_offset_t vaddr = (vm_offset_t)buf;
357         int seg;
358         pmap_t pmap;
359
360         if (td != NULL)
361                 pmap = vmspace_pmap(td->td_proc->p_vmspace);
362         else
363                 pmap = NULL;
364
365         lastaddr = *lastaddrp;
366         bmask  = ~(dmat->dt_boundary - 1);
367
368         for (seg = *segp; buflen > 0 ; ) {
369                 /*
370                  * Get the physical address for this segment.
371                  */
372                 if (pmap)
373                         curaddr = pmap_extract(pmap, vaddr);
374                 else
375                         curaddr = pmap_kextract(vaddr);
376
377                 /*
378                  * Compute the segment size, and adjust counts.
379                  */
380                 sgsize = PAGE_SIZE - ((u_long)curaddr & PAGE_MASK);
381                 if (sgsize > dmat->dt_maxsegsz)
382                         sgsize = dmat->dt_maxsegsz;
383                 if (buflen < sgsize)
384                         sgsize = buflen;
385
386                 /*
387                  * Make sure we don't cross any boundaries.
388                  */
389                 if (dmat->dt_boundary > 0) {
390                         baddr = (curaddr + dmat->dt_boundary) & bmask;
391                         if (sgsize > (baddr - curaddr))
392                                 sgsize = (baddr - curaddr);
393                 }
394
395                 /*
396                  * Insert chunk into a segment, coalescing with
397                  * previous segment if possible.
398                  */
399                 if (first) {
400                         segs[seg].ds_addr = curaddr;
401                         segs[seg].ds_len = sgsize;
402                         first = 0;
403                 } else {
404                         if (curaddr == lastaddr &&
405                             (segs[seg].ds_len + sgsize) <= dmat->dt_maxsegsz &&
406                             (dmat->dt_boundary == 0 ||
407                             (segs[seg].ds_addr & bmask) == (curaddr & bmask)))
408                                 segs[seg].ds_len += sgsize;
409                         else {
410                                 if (++seg >= dmat->dt_nsegments)
411                                         break;
412                                 segs[seg].ds_addr = curaddr;
413                                 segs[seg].ds_len = sgsize;
414                         }
415                 }
416
417                 lastaddr = curaddr + sgsize;
418                 vaddr += sgsize;
419                 buflen -= sgsize;
420         }
421
422         *segp = seg;
423         *lastaddrp = lastaddr;
424
425         /*
426          * Did we fit?
427          */
428         return (buflen != 0 ? EFBIG : 0); /* XXX better return value here? */
429 }
430
431 /*
432  * Common function for loading a DMA map with a linear buffer.  May
433  * be called by bus-specific DMA map load functions.
434  *
435  * Most SPARCs have IOMMUs in the bus controllers.  In those cases
436  * they only need one segment and will use virtual addresses for DVMA.
437  * Those bus controllers should intercept these vectors and should
438  * *NEVER* call nexus_dmamap_load() which is used only by devices that
439  * bypass DVMA.
440  */
441 static int
442 nexus_dmamap_load(bus_dma_tag_t dmat, bus_dmamap_t map, void *buf,
443     bus_size_t buflen, bus_dmamap_callback_t *callback, void *callback_arg,
444     int flags)
445 {
446         bus_addr_t lastaddr;
447         int error, nsegs;
448
449         error = _nexus_dmamap_load_buffer(dmat, buf, buflen, NULL, flags,
450             &lastaddr, dmat->dt_segments, &nsegs, 1);
451
452         if (error == 0) {
453                 (*callback)(callback_arg, dmat->dt_segments, nsegs + 1, 0);
454                 map->dm_flags |= DMF_LOADED;
455         } else
456                 (*callback)(callback_arg, NULL, 0, error);
457
458         return (0);
459 }
460
461 /*
462  * Like nexus_dmamap_load(), but for mbufs.
463  */
464 static int
465 nexus_dmamap_load_mbuf(bus_dma_tag_t dmat, bus_dmamap_t map, struct mbuf *m0,
466     bus_dmamap_callback2_t *callback, void *callback_arg, int flags)
467 {
468         int nsegs, error;
469
470         M_ASSERTPKTHDR(m0);
471
472         nsegs = 0;
473         error = 0;
474         if (m0->m_pkthdr.len <= dmat->dt_maxsize) {
475                 int first = 1;
476                 bus_addr_t lastaddr = 0;
477                 struct mbuf *m;
478
479                 for (m = m0; m != NULL && error == 0; m = m->m_next) {
480                         if (m->m_len > 0) {
481                                 error = _nexus_dmamap_load_buffer(dmat,
482                                     m->m_data, m->m_len,NULL, flags, &lastaddr,
483                                     dmat->dt_segments, &nsegs, first);
484                                 first = 0;
485                         }
486                 }
487         } else {
488                 error = EINVAL;
489         }
490
491         if (error) {
492                 /* force "no valid mappings" in callback */
493                 (*callback)(callback_arg, dmat->dt_segments, 0, 0, error);
494         } else {
495                 map->dm_flags |= DMF_LOADED;
496                 (*callback)(callback_arg, dmat->dt_segments, nsegs + 1,
497                     m0->m_pkthdr.len, error);
498         }
499         return (error);
500 }
501
502 static int
503 nexus_dmamap_load_mbuf_sg(bus_dma_tag_t dmat, bus_dmamap_t map, struct mbuf *m0,
504     bus_dma_segment_t *segs, int *nsegs, int flags)
505 {
506         int error;
507
508         M_ASSERTPKTHDR(m0);
509
510         *nsegs = 0;
511         error = 0;
512         if (m0->m_pkthdr.len <= dmat->dt_maxsize) {
513                 int first = 1;
514                 bus_addr_t lastaddr = 0;
515                 struct mbuf *m;
516
517                 for (m = m0; m != NULL && error == 0; m = m->m_next) {
518                         if (m->m_len > 0) {
519                                 error = _nexus_dmamap_load_buffer(dmat,
520                                     m->m_data, m->m_len,NULL, flags, &lastaddr,
521                                     segs, nsegs, first);
522                                 first = 0;
523                         }
524                 }
525         } else {
526                 error = EINVAL;
527         }
528
529         ++*nsegs;
530         return (error);
531 }
532
533 /*
534  * Like nexus_dmamap_load(), but for uios.
535  */
536 static int
537 nexus_dmamap_load_uio(bus_dma_tag_t dmat, bus_dmamap_t map, struct uio *uio,
538     bus_dmamap_callback2_t *callback, void *callback_arg, int flags)
539 {
540         bus_addr_t lastaddr;
541         int nsegs, error, first, i;
542         bus_size_t resid;
543         struct iovec *iov;
544         struct thread *td = NULL;
545
546         resid = uio->uio_resid;
547         iov = uio->uio_iov;
548
549         if (uio->uio_segflg == UIO_USERSPACE) {
550                 td = uio->uio_td;
551                 KASSERT(td != NULL, ("%s: USERSPACE but no proc", __func__));
552         }
553
554         nsegs = 0;
555         error = 0;
556         first = 1;
557         for (i = 0; i < uio->uio_iovcnt && resid != 0 && !error; i++) {
558                 /*
559                  * Now at the first iovec to load.  Load each iovec
560                  * until we have exhausted the residual count.
561                  */
562                 bus_size_t minlen =
563                         resid < iov[i].iov_len ? resid : iov[i].iov_len;
564                 caddr_t addr = (caddr_t) iov[i].iov_base;
565
566                 if (minlen > 0) {
567                         error = _nexus_dmamap_load_buffer(dmat, addr, minlen,
568                             td, flags, &lastaddr, dmat->dt_segments, &nsegs,
569                             first);
570                         first = 0;
571
572                         resid -= minlen;
573                 }
574         }
575
576         if (error) {
577                 /* force "no valid mappings" in callback */
578                 (*callback)(callback_arg, dmat->dt_segments, 0, 0, error);
579         } else {
580                 map->dm_flags |= DMF_LOADED;
581                 (*callback)(callback_arg, dmat->dt_segments, nsegs + 1,
582                     uio->uio_resid, error);
583         }
584         return (error);
585 }
586
587 /*
588  * Common function for unloading a DMA map.  May be called by
589  * bus-specific DMA map unload functions.
590  */
591 static void
592 nexus_dmamap_unload(bus_dma_tag_t dmat, bus_dmamap_t map)
593 {
594
595         map->dm_flags &= ~DMF_LOADED;
596 }
597
598 /*
599  * Common function for DMA map synchronization.  May be called
600  * by bus-specific DMA map synchronization functions.
601  */
602 static void
603 nexus_dmamap_sync(bus_dma_tag_t dmat, bus_dmamap_t map, bus_dmasync_op_t op)
604 {
605
606         /*
607          * We sync out our caches, but the bus must do the same.
608          *
609          * Actually a #Sync is expensive.  We should optimize.
610          */
611         if ((op & BUS_DMASYNC_PREREAD) || (op & BUS_DMASYNC_PREWRITE)) {
612                 /*
613                  * Don't really need to do anything, but flush any pending
614                  * writes anyway.
615                  */
616                 membar(Sync);
617         }
618 #if 0
619         /* Should not be needed. */
620         if (op & BUS_DMASYNC_POSTREAD) {
621                 ecache_flush((vm_offset_t)map->buf,
622                     (vm_offset_t)map->buf + map->buflen - 1);
623         }
624 #endif
625         if (op & BUS_DMASYNC_POSTWRITE) {
626                 /* Nothing to do.  Handled by the bus controller. */
627         }
628 }
629
630 /*
631  * Common function for DMA-safe memory allocation.  May be called
632  * by bus-specific DMA memory allocation functions.
633  */
634 static int
635 nexus_dmamem_alloc(bus_dma_tag_t dmat, void **vaddr, int flags,
636     bus_dmamap_t *mapp)
637 {
638         int mflags;
639
640         if (flags & BUS_DMA_NOWAIT)
641                 mflags = M_NOWAIT;
642         else
643                 mflags = M_WAITOK;
644         if (flags & BUS_DMA_ZERO)
645                 mflags |= M_ZERO;
646
647         /*
648          * XXX:
649          * (dmat->dt_alignment < dmat->dt_maxsize) is just a quick hack; the
650          * exact alignment guarantees of malloc need to be nailed down, and
651          * the code below should be rewritten to take that into account.
652          *
653          * In the meantime, we'll warn the user if malloc gets it wrong.
654          */
655         if (dmat->dt_maxsize <= PAGE_SIZE &&
656             dmat->dt_alignment < dmat->dt_maxsize)
657                 *vaddr = malloc(dmat->dt_maxsize, M_DEVBUF, mflags);
658         else {
659                 /*
660                  * XXX use contigmalloc until it is merged into this
661                  * facility and handles multi-seg allocations.  Nobody
662                  * is doing multi-seg allocations yet though.
663                  */
664                 *vaddr = contigmalloc(dmat->dt_maxsize, M_DEVBUF, mflags,
665                     0ul, dmat->dt_lowaddr,
666                     dmat->dt_alignment ? dmat->dt_alignment : 1UL,
667                     dmat->dt_boundary);
668         }
669         if (*vaddr == NULL)
670                 return (ENOMEM);
671         if ((uintptr_t)*vaddr % dmat->dt_alignment)
672                 printf("%s: failed to align memory properly.\n", __func__);
673         return (0);
674 }
675
676 /*
677  * Common function for freeing DMA-safe memory.  May be called by
678  * bus-specific DMA memory free functions.
679  */
680 static void
681 nexus_dmamem_free(bus_dma_tag_t dmat, void *vaddr, bus_dmamap_t map)
682 {
683
684         if (dmat->dt_maxsize <= PAGE_SIZE &&
685             dmat->dt_alignment < dmat->dt_maxsize)
686                 free(vaddr, M_DEVBUF);
687         else
688                 contigfree(vaddr, dmat->dt_maxsize, M_DEVBUF);
689 }
690
691 struct bus_dma_methods nexus_dma_methods = {
692         nexus_dmamap_create,
693         nexus_dmamap_destroy,
694         nexus_dmamap_load,
695         nexus_dmamap_load_mbuf,
696         nexus_dmamap_load_mbuf_sg,
697         nexus_dmamap_load_uio,
698         nexus_dmamap_unload,
699         nexus_dmamap_sync,
700         nexus_dmamem_alloc,
701         nexus_dmamem_free,
702 };
703
704 struct bus_dma_tag nexus_dmatag = {
705         NULL,
706         NULL,
707         8,
708         0,
709         0,
710         0x3ffffffff,
711         NULL,           /* XXX */
712         NULL,
713         0x3ffffffff,    /* XXX */
714         0xff,           /* XXX */
715         0xffffffff,     /* XXX */
716         0,
717         0,
718         0,
719         NULL,
720         NULL,
721         NULL,
722         &nexus_dma_methods,
723 };
724
725 /*
726  * Helpers to map/unmap bus memory
727  */
728 int
729 sparc64_bus_mem_map(bus_space_tag_t tag, bus_space_handle_t handle,
730     bus_size_t size, int flags, vm_offset_t vaddr, void **hp)
731 {
732 #if 0
733         vm_offset_t addr;
734         vm_offset_t sva;
735         vm_offset_t va;
736         vm_paddr_t pa;
737         vm_size_t vsz;
738         u_long pm_flags;
739
740         addr = (vm_offset_t)handle;
741         size = round_page(size);
742         if (size == 0) {
743                 printf("%s: zero size\n", __func__);
744                 return (EINVAL);
745         }
746         switch (tag->bst_type) {
747         case PCI_CONFIG_BUS_SPACE:
748         case PCI_IO_BUS_SPACE:
749         case PCI_MEMORY_BUS_SPACE:
750                 pm_flags = TD_IE;
751                 break;
752         default:
753                 pm_flags = 0;
754                 break;
755         }
756
757         if (!(flags & BUS_SPACE_MAP_CACHEABLE))
758                 pm_flags |= TD_E;
759
760         if (vaddr != 0L)
761                 sva = trunc_page(vaddr);
762         else {
763                 if ((sva = kmem_alloc_nofault(kernel_map, size)) == 0)
764                         panic("%s: cannot allocate virtual memory", __func__);
765         }
766
767         /* Preserve page offset. */
768         *hp = (void *)(sva | ((u_long)addr & PAGE_MASK));
769
770         pa = trunc_page(addr);
771         if ((flags & BUS_SPACE_MAP_READONLY) == 0)
772                 pm_flags |= TD_W;
773
774         va = sva;
775         vsz = size;
776         do {
777                 pmap_kenter_flags(va, pa, pm_flags);
778                 va += PAGE_SIZE;
779                 pa += PAGE_SIZE;
780         } while ((vsz -= PAGE_SIZE) > 0);
781         tlb_range_demap(kernel_pmap, sva, sva + size - 1);
782 #endif
783         return (0);
784 }
785
786 int
787 sparc64_bus_mem_unmap(void *bh, bus_size_t size)
788 {
789 #if 0
790         vm_offset_t sva;
791         vm_offset_t va;
792         vm_offset_t endva;
793
794         sva = trunc_page((vm_offset_t)bh);
795         endva = sva + round_page(size);
796         for (va = sva; va < endva; va += PAGE_SIZE)
797                 pmap_kremove_flags(va);
798         tlb_range_demap(kernel_pmap, sva, sva + size - 1);
799         kmem_free(kernel_map, sva, size);
800 #endif
801         return (0);
802 }
803
804 /*
805  * Fake up a bus tag, for use by console drivers in early boot when the
806  * regular means to allocate resources are not yet available.
807  * Addr is the physical address of the desired start of the handle.
808  */
809 bus_space_handle_t
810 sparc64_fake_bustag(int space, bus_addr_t addr, struct bus_space_tag *ptag)
811 {
812
813         ptag->bst_cookie = NULL;
814         ptag->bst_parent = NULL;
815         ptag->bst_type = space;
816         ptag->bst_bus_barrier = nexus_bus_barrier;
817         return (addr);
818 }
819
820 /*
821  * Base bus space handlers.
822  */
823
824 static void
825 nexus_bus_barrier(bus_space_tag_t t, bus_space_handle_t h, bus_size_t offset,
826     bus_size_t size, int flags)
827 {
828
829         /*
830          * We have lots of alternatives depending on whether we're
831          * synchronizing loads with loads, loads with stores, stores
832          * with loads, or stores with stores.  The only ones that seem
833          * generic are #Sync and #MemIssue.  I'll use #Sync for safety.
834          */
835         switch(flags) {
836         case BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE:
837         case BUS_SPACE_BARRIER_READ:
838         case BUS_SPACE_BARRIER_WRITE:
839                 membar(Sync);
840                 break;
841         default:
842                 panic("%s: unknown flags", __func__);
843         }
844         return;
845 }
846
847 struct bus_space_tag nexus_bustag = {
848         NULL,                           /* cookie */
849         NULL,                           /* parent bus tag */
850         NEXUS_BUS_SPACE,                /* type */
851         nexus_bus_barrier,              /* bus_space_barrier */
852 };