The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/mips/mips/busdma_machdep.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2006 Oleksandr Tymoshenko
    3  * All rights reserved.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions, and the following disclaimer,
   10  *    without modification, immediately at the beginning of the file.
   11  * 2. The name of the author may not be used to endorse or promote products
   12  *    derived from this software without specific prior written permission.
   13  *
   14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   17  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
   18  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   24  * SUCH DAMAGE.
   25  *
   26  */
   27 
   28 /*-
   29  * Copyright (c) 1997, 1998, 2001 The NetBSD Foundation, Inc.
   30  * All rights reserved.
   31  *
   32  * This code is derived from software contributed to The NetBSD Foundation
   33  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
   34  * NASA Ames Research Center.
   35  *
   36  * Redistribution and use in source and binary forms, with or without
   37  * modification, are permitted provided that the following conditions
   38  * are met:
   39  * 1. Redistributions of source code must retain the above copyright
   40  *    notice, this list of conditions and the following disclaimer.
   41  * 2. Redistributions in binary form must reproduce the above copyright
   42  *    notice, this list of conditions and the following disclaimer in the
   43  *    documentation and/or other materials provided with the distribution.
   44  * 3. All advertising materials mentioning features or use of this software
   45  *    must display the following acknowledgement:
   46  *      This product includes software developed by the NetBSD
   47  *      Foundation, Inc. and its contributors.
   48  * 4. Neither the name of The NetBSD Foundation nor the names of its
   49  *    contributors may be used to endorse or promote products derived
   50  *    from this software without specific prior written permission.
   51  *
   52  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
   53  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   54  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   55  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
   56  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   57  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   58  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   59  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   60  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   61  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   62  * POSSIBILITY OF SUCH DAMAGE.
   63  */
   64 
   65 /*      $NetBSD: bus_dma.c,v 1.17 2006/03/01 12:38:11 yamt Exp $        */
   66 
   67 #include <sys/cdefs.h>
   68 __FBSDID("$FreeBSD: releng/8.1/sys/mips/mips/busdma_machdep.c 195162 2009-06-29 16:45:50Z imp $");
   69 
   70 #include <sys/param.h>
   71 #include <sys/systm.h>
   72 #include <sys/malloc.h>
   73 #include <sys/bus.h>
   74 #include <sys/interrupt.h>
   75 #include <sys/lock.h>
   76 #include <sys/proc.h>
   77 #include <sys/mutex.h>
   78 #include <sys/mbuf.h>
   79 #include <sys/uio.h>
   80 #include <sys/ktr.h>
   81 #include <sys/kernel.h>
   82 
   83 #include <vm/vm.h>
   84 #include <vm/vm_page.h>
   85 #include <vm/vm_map.h>
   86 
   87 #include <machine/atomic.h>
   88 #include <machine/bus.h>
   89 #include <machine/cache.h>
   90 #include <machine/cpufunc.h>
   91 
   92 struct bus_dma_tag {
   93         bus_dma_tag_t           parent;
   94         bus_size_t              alignment;
   95         bus_size_t              boundary;
   96         bus_addr_t              lowaddr;
   97         bus_addr_t              highaddr;
   98         bus_dma_filter_t        *filter;
   99         void                    *filterarg;
  100         bus_size_t              maxsize;
  101         u_int                   nsegments;
  102         bus_size_t              maxsegsz;
  103         int                     flags;
  104         int                     ref_count;
  105         int                     map_count;
  106         bus_dma_lock_t          *lockfunc;
  107         void                    *lockfuncarg;
  108         /* XXX: machine-dependent fields */
  109         vm_offset_t             _physbase;
  110         vm_offset_t             _wbase;
  111         vm_offset_t             _wsize;
  112 };
  113 
  114 #define DMAMAP_LINEAR           0x1
  115 #define DMAMAP_MBUF             0x2
  116 #define DMAMAP_UIO              0x4
  117 #define DMAMAP_ALLOCATED        0x10
  118 #define DMAMAP_TYPE_MASK        (DMAMAP_LINEAR|DMAMAP_MBUF|DMAMAP_UIO)
  119 #define DMAMAP_COHERENT         0x8
  120 struct bus_dmamap {
  121         bus_dma_tag_t   dmat;
  122         int             flags;
  123         void            *buffer;
  124         void            *origbuffer;
  125         void            *allocbuffer;
  126         TAILQ_ENTRY(bus_dmamap) freelist;
  127         int             len;
  128 };
  129 
  130 static TAILQ_HEAD(,bus_dmamap) dmamap_freelist = 
  131         TAILQ_HEAD_INITIALIZER(dmamap_freelist);
  132 
  133 #define BUSDMA_STATIC_MAPS      500
  134 static struct bus_dmamap map_pool[BUSDMA_STATIC_MAPS];
  135 
  136 static struct mtx busdma_mtx;
  137 
  138 MTX_SYSINIT(busdma_mtx, &busdma_mtx, "busdma lock", MTX_DEF);
  139 
  140 static void
  141 mips_dmamap_freelist_init(void *dummy)
  142 {
  143         int i;
  144 
  145         for (i = 0; i < BUSDMA_STATIC_MAPS; i++) 
  146                 TAILQ_INSERT_HEAD(&dmamap_freelist, &map_pool[i], freelist);
  147 }
  148 
  149 SYSINIT(busdma, SI_SUB_VM, SI_ORDER_ANY, mips_dmamap_freelist_init, NULL);
  150 
  151 /*
  152  * Check to see if the specified page is in an allowed DMA range.
  153  */
  154 
  155 static __inline int
  156 bus_dmamap_load_buffer(bus_dma_tag_t dmat, bus_dma_segment_t *segs,
  157     bus_dmamap_t map, void *buf, bus_size_t buflen, struct pmap *pmap,
  158     int flags, vm_offset_t *lastaddrp, int *segp);
  159 
  160 /*
  161  * Convenience function for manipulating driver locks from busdma (during
  162  * busdma_swi, for example).  Drivers that don't provide their own locks
  163  * should specify &Giant to dmat->lockfuncarg.  Drivers that use their own
  164  * non-mutex locking scheme don't have to use this at all.
  165  */
  166 void
  167 busdma_lock_mutex(void *arg, bus_dma_lock_op_t op)
  168 {
  169         struct mtx *dmtx;
  170 
  171         dmtx = (struct mtx *)arg;
  172         switch (op) {
  173         case BUS_DMA_LOCK:
  174                 mtx_lock(dmtx);
  175                 break;
  176         case BUS_DMA_UNLOCK:
  177                 mtx_unlock(dmtx);
  178                 break;
  179         default:
  180                 panic("Unknown operation 0x%x for busdma_lock_mutex!", op);
  181         }
  182 }
  183 
  184 /*
  185  * dflt_lock should never get called.  It gets put into the dma tag when
  186  * lockfunc == NULL, which is only valid if the maps that are associated
  187  * with the tag are meant to never be defered.
  188  * XXX Should have a way to identify which driver is responsible here.
  189  */
  190 static void
  191 dflt_lock(void *arg, bus_dma_lock_op_t op)
  192 {
  193 #ifdef INVARIANTS
  194         panic("driver error: busdma dflt_lock called");
  195 #else
  196         printf("DRIVER_ERROR: busdma dflt_lock called\n");
  197 #endif
  198 }
  199 
  200 static __inline bus_dmamap_t
  201 _busdma_alloc_dmamap(void)
  202 {
  203         bus_dmamap_t map;
  204 
  205         mtx_lock(&busdma_mtx);
  206         map = TAILQ_FIRST(&dmamap_freelist);
  207         if (map)
  208                 TAILQ_REMOVE(&dmamap_freelist, map, freelist);
  209         mtx_unlock(&busdma_mtx);
  210         if (!map) {
  211                 map = malloc(sizeof(*map), M_DEVBUF, M_NOWAIT | M_ZERO);
  212                 if (map)
  213                         map->flags = DMAMAP_ALLOCATED;
  214         } else
  215                 map->flags = 0;
  216         return (map);
  217 }
  218 
  219 static __inline void 
  220 _busdma_free_dmamap(bus_dmamap_t map)
  221 {
  222         if (map->flags & DMAMAP_ALLOCATED)
  223                 free(map, M_DEVBUF);
  224         else {
  225                 mtx_lock(&busdma_mtx);
  226                 TAILQ_INSERT_HEAD(&dmamap_freelist, map, freelist);
  227                 mtx_unlock(&busdma_mtx);
  228         }
  229 }
  230 
  231 int
  232 bus_dma_tag_create(bus_dma_tag_t parent, bus_size_t alignment,
  233                    bus_size_t boundary, bus_addr_t lowaddr,
  234                    bus_addr_t highaddr, bus_dma_filter_t *filter,
  235                    void *filterarg, bus_size_t maxsize, int nsegments,
  236                    bus_size_t maxsegsz, int flags, bus_dma_lock_t *lockfunc,
  237                    void *lockfuncarg, bus_dma_tag_t *dmat)
  238 {
  239         bus_dma_tag_t newtag;
  240         int error = 0;
  241 
  242         /* Basic sanity checking */
  243         if (boundary != 0 && boundary < maxsegsz)
  244                 maxsegsz = boundary;
  245 
  246         /* Return a NULL tag on failure */
  247         *dmat = NULL;
  248 
  249         newtag = (bus_dma_tag_t)malloc(sizeof(*newtag), M_DEVBUF,
  250             M_ZERO | M_NOWAIT);
  251         if (newtag == NULL) {
  252                 CTR4(KTR_BUSDMA, "%s returned tag %p tag flags 0x%x error %d",
  253                     __func__, newtag, 0, error);
  254                 return (ENOMEM);
  255         }
  256 
  257         newtag->parent = parent;
  258         newtag->alignment = alignment;
  259         newtag->boundary = boundary;
  260         newtag->lowaddr = trunc_page((vm_paddr_t)lowaddr) + (PAGE_SIZE - 1);
  261         newtag->highaddr = trunc_page((vm_paddr_t)highaddr) +
  262             (PAGE_SIZE - 1);
  263         newtag->filter = filter;
  264         newtag->filterarg = filterarg;
  265         newtag->maxsize = maxsize;
  266         newtag->nsegments = nsegments;
  267         newtag->maxsegsz = maxsegsz;
  268         newtag->flags = flags;
  269         newtag->ref_count = 1; /* Count ourself */
  270         newtag->map_count = 0;
  271         newtag->_wbase = 0;
  272         newtag->_physbase = 0;
  273         /* XXXMIPS: Should we limit window size to amount of physical memory */
  274         newtag->_wsize = MIPS_KSEG1_START - MIPS_KSEG0_START;
  275         if (lockfunc != NULL) {
  276                 newtag->lockfunc = lockfunc;
  277                 newtag->lockfuncarg = lockfuncarg;
  278         } else {
  279                 newtag->lockfunc = dflt_lock;
  280                 newtag->lockfuncarg = NULL;
  281         }
  282 
  283         /* Take into account any restrictions imposed by our parent tag */
  284         if (parent != NULL) {
  285                 newtag->lowaddr = MIN(parent->lowaddr, newtag->lowaddr);
  286                 newtag->highaddr = MAX(parent->highaddr, newtag->highaddr);
  287                 if (newtag->boundary == 0)
  288                         newtag->boundary = parent->boundary;
  289                 else if (parent->boundary != 0)
  290                         newtag->boundary = MIN(parent->boundary,
  291                                                newtag->boundary);
  292                 if (newtag->filter == NULL) {
  293                         /*
  294                          * Short circuit looking at our parent directly
  295                          * since we have encapsulated all of its information
  296                          */
  297                         newtag->filter = parent->filter;
  298                         newtag->filterarg = parent->filterarg;
  299                         newtag->parent = parent->parent;
  300                 }
  301                 if (newtag->parent != NULL)
  302                         atomic_add_int(&parent->ref_count, 1);
  303         }
  304 
  305         if (error != 0) {
  306                 free(newtag, M_DEVBUF);
  307         } else {
  308                 *dmat = newtag;
  309         }
  310         CTR4(KTR_BUSDMA, "%s returned tag %p tag flags 0x%x error %d",
  311             __func__, newtag, (newtag != NULL ? newtag->flags : 0), error);
  312         return (error);
  313 }
  314 
  315 int
  316 bus_dma_tag_destroy(bus_dma_tag_t dmat)
  317 {
  318 #ifdef KTR
  319         bus_dma_tag_t dmat_copy = dmat;
  320 #endif
  321 
  322         if (dmat != NULL) {
  323                 
  324                 if (dmat->map_count != 0)
  325                         return (EBUSY);
  326                 
  327                 while (dmat != NULL) {
  328                         bus_dma_tag_t parent;
  329                         
  330                         parent = dmat->parent;
  331                         atomic_subtract_int(&dmat->ref_count, 1);
  332                         if (dmat->ref_count == 0) {
  333                                 free(dmat, M_DEVBUF);
  334                                 /*
  335                                  * Last reference count, so
  336                                  * release our reference
  337                                  * count on our parent.
  338                                  */
  339                                 dmat = parent;
  340                         } else
  341                                 dmat = NULL;
  342                 }
  343         }
  344         CTR2(KTR_BUSDMA, "%s tag %p", __func__, dmat_copy);
  345 
  346         return (0);
  347 }
  348 
  349 /*
  350  * Allocate a handle for mapping from kva/uva/physical
  351  * address space into bus device space.
  352  */
  353 int
  354 bus_dmamap_create(bus_dma_tag_t dmat, int flags, bus_dmamap_t *mapp)
  355 {
  356         bus_dmamap_t newmap;
  357 #ifdef KTR
  358         int error = 0;
  359 #endif
  360 
  361         newmap = _busdma_alloc_dmamap();
  362         if (newmap == NULL) {
  363                 CTR3(KTR_BUSDMA, "%s: tag %p error %d", __func__, dmat, ENOMEM);
  364                 return (ENOMEM);
  365         }
  366         *mapp = newmap;
  367         newmap->dmat = dmat;
  368         dmat->map_count++;
  369 
  370         CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d",
  371             __func__, dmat, dmat->flags, error);
  372 
  373         return (0);
  374 
  375 }
  376 
  377 /*
  378  * Destroy a handle for mapping from kva/uva/physical
  379  * address space into bus device space.
  380  */
  381 int
  382 bus_dmamap_destroy(bus_dma_tag_t dmat, bus_dmamap_t map)
  383 {
  384         _busdma_free_dmamap(map);
  385         dmat->map_count--;
  386         CTR2(KTR_BUSDMA, "%s: tag %p error 0", __func__, dmat);
  387         return (0);
  388 }
  389 
  390 /*
  391  * Allocate a piece of memory that can be efficiently mapped into
  392  * bus device space based on the constraints lited in the dma tag.
  393  * A dmamap to for use with dmamap_load is also allocated.
  394  */
  395 int
  396 bus_dmamem_alloc(bus_dma_tag_t dmat, void** vaddr, int flags,
  397                  bus_dmamap_t *mapp)
  398 {
  399         bus_dmamap_t newmap = NULL;
  400 
  401         int mflags;
  402 
  403         if (flags & BUS_DMA_NOWAIT)
  404                 mflags = M_NOWAIT;
  405         else
  406                 mflags = M_WAITOK;
  407         if (flags & BUS_DMA_ZERO)
  408                 mflags |= M_ZERO;
  409 
  410         newmap = _busdma_alloc_dmamap();
  411         if (newmap == NULL) {
  412                 CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d",
  413                     __func__, dmat, dmat->flags, ENOMEM);
  414                 return (ENOMEM);
  415         }
  416         dmat->map_count++;
  417         *mapp = newmap;
  418         newmap->dmat = dmat;
  419         
  420         if (dmat->maxsize <= PAGE_SIZE) {
  421                 *vaddr = malloc(dmat->maxsize, M_DEVBUF, mflags);
  422         } else {
  423                 /*
  424                  * XXX Use Contigmalloc until it is merged into this facility
  425                  *     and handles multi-seg allocations.  Nobody is doing
  426                  *     multi-seg allocations yet though.
  427                  */
  428                  vm_paddr_t maxphys;
  429                  if((uint32_t)dmat->lowaddr >= MIPS_KSEG0_LARGEST_PHYS) {
  430                    /* Note in the else case I just put in what was already
  431                     * being passed in dmat->lowaddr. I am not sure
  432                     * how this would have worked. Since lowaddr is in the
  433                     * max address postion. I would have thought that the
  434                     * caller would have wanted dmat->highaddr. That is
  435                     * presuming they are asking for physical addresses
  436                     * which is what contigmalloc takes. - RRS
  437                     */
  438                    maxphys = MIPS_KSEG0_LARGEST_PHYS - 1;
  439                  } else {
  440                    maxphys = dmat->lowaddr;
  441                  }
  442                 *vaddr = contigmalloc(dmat->maxsize, M_DEVBUF, mflags,
  443                     0ul, maxphys, dmat->alignment? dmat->alignment : 1ul,
  444                     dmat->boundary);
  445         }
  446         if (*vaddr == NULL) {
  447                 if (newmap != NULL) {
  448                         _busdma_free_dmamap(newmap);
  449                         dmat->map_count--;
  450                 }
  451                 *mapp = NULL;
  452                 return (ENOMEM);
  453         }
  454         if (flags & BUS_DMA_COHERENT) {
  455                 void *tmpaddr = (void *)*vaddr;
  456 
  457                 if (tmpaddr) {
  458                         tmpaddr = (void *)MIPS_PHYS_TO_KSEG1(vtophys(tmpaddr));
  459                         newmap->origbuffer = *vaddr;
  460                         newmap->allocbuffer = tmpaddr;
  461                         mips_dcache_wbinv_range((vm_offset_t)*vaddr,
  462                             dmat->maxsize);
  463                         *vaddr = tmpaddr;
  464                 } else
  465                         newmap->origbuffer = newmap->allocbuffer = NULL;
  466         } else 
  467                 newmap->origbuffer = newmap->allocbuffer = NULL;
  468         return (0);
  469 
  470 }
  471 
  472 /*
  473  * Free a piece of memory and it's allocated dmamap, that was allocated
  474  * via bus_dmamem_alloc.  Make the same choice for free/contigfree.
  475  */
  476 void
  477 bus_dmamem_free(bus_dma_tag_t dmat, void *vaddr, bus_dmamap_t map)
  478 {
  479         if (map->allocbuffer) {
  480                 KASSERT(map->allocbuffer == vaddr,
  481                     ("Trying to freeing the wrong DMA buffer"));
  482                 vaddr = map->origbuffer;
  483         }
  484         if (dmat->maxsize <= PAGE_SIZE)
  485                 free(vaddr, M_DEVBUF);
  486         else {
  487                 contigfree(vaddr, dmat->maxsize, M_DEVBUF);
  488         }
  489         dmat->map_count--;
  490         _busdma_free_dmamap(map);
  491         CTR3(KTR_BUSDMA, "%s: tag %p flags 0x%x", __func__, dmat, dmat->flags);
  492 
  493 }
  494 
  495 /*
  496  * Utility function to load a linear buffer.  lastaddrp holds state
  497  * between invocations (for multiple-buffer loads).  segp contains
  498  * the starting segment on entrance, and the ending segment on exit.
  499  * first indicates if this is the first invocation of this function.
  500  */
  501 static __inline int
  502 bus_dmamap_load_buffer(bus_dma_tag_t dmat, bus_dma_segment_t *segs,
  503     bus_dmamap_t map, void *buf, bus_size_t buflen, struct pmap *pmap,
  504     int flags, vm_offset_t *lastaddrp, int *segp)
  505 {
  506         bus_size_t sgsize;
  507         bus_size_t bmask;
  508         vm_offset_t curaddr, lastaddr;
  509         vm_offset_t vaddr = (vm_offset_t)buf;
  510         int seg;
  511         int error = 0;
  512 
  513         lastaddr = *lastaddrp;
  514         bmask = ~(dmat->boundary - 1);
  515 
  516         for (seg = *segp; buflen > 0 ; ) {
  517                 /*
  518                  * Get the physical address for this segment.
  519                  */
  520                 KASSERT(kernel_pmap == pmap, ("pmap is not kernel pmap"));
  521                 curaddr = pmap_kextract(vaddr);
  522 
  523                 /*
  524                  * If we're beyond the current DMA window, indicate
  525                  * that and try to fall back onto something else.
  526                  */
  527                 if (curaddr < dmat->_physbase ||
  528                     curaddr >= (dmat->_physbase + dmat->_wsize))
  529                         return (EINVAL);
  530 
  531                 /*
  532                  * In a valid DMA range.  Translate the physical
  533                  * memory address to an address in the DMA window.
  534                  */
  535                 curaddr = (curaddr - dmat->_physbase) + dmat->_wbase;
  536 
  537 
  538                 /*
  539                  * Compute the segment size, and adjust counts.
  540                  */
  541                 sgsize = PAGE_SIZE - ((u_long)curaddr & PAGE_MASK);
  542                 if (buflen < sgsize)
  543                         sgsize = buflen;
  544 
  545                 /*
  546                  * Insert chunk into a segment, coalescing with
  547                  * the previous segment if possible.
  548                  */
  549                 if (seg >= 0 && curaddr == lastaddr &&
  550                     (segs[seg].ds_len + sgsize) <= dmat->maxsegsz &&
  551                     (dmat->boundary == 0 ||
  552                      (segs[seg].ds_addr & bmask) == 
  553                      (curaddr & bmask))) {
  554                         segs[seg].ds_len += sgsize;
  555                         goto segdone;
  556                 } else {
  557                         if (++seg >= dmat->nsegments)
  558                                 break;
  559                         segs[seg].ds_addr = curaddr;
  560                         segs[seg].ds_len = sgsize;
  561                 }
  562                 if (error)
  563                         break;
  564 segdone:
  565                 lastaddr = curaddr + sgsize;
  566                 vaddr += sgsize;
  567                 buflen -= sgsize;
  568         }
  569 
  570         *segp = seg;
  571         *lastaddrp = lastaddr;
  572 
  573         /*
  574          * Did we fit?
  575          */
  576         if (buflen != 0)
  577                 error = EFBIG;
  578 
  579         return error;
  580 }
  581 
  582 /*
  583  * Map the buffer buf into bus space using the dmamap map.
  584  */
  585 int
  586 bus_dmamap_load(bus_dma_tag_t dmat, bus_dmamap_t map, void *buf,
  587                 bus_size_t buflen, bus_dmamap_callback_t *callback,
  588                 void *callback_arg, int flags)
  589 {
  590         vm_offset_t     lastaddr = 0;
  591         int             error, nsegs = -1;
  592 #ifdef __CC_SUPPORTS_DYNAMIC_ARRAY_INIT
  593         bus_dma_segment_t dm_segments[dmat->nsegments];
  594 #else
  595         bus_dma_segment_t dm_segments[BUS_DMAMAP_NSEGS];
  596 #endif
  597 
  598         KASSERT(dmat != NULL, ("dmatag is NULL"));
  599         KASSERT(map != NULL, ("dmamap is NULL"));
  600         map->flags &= ~DMAMAP_TYPE_MASK;
  601         map->flags |= DMAMAP_LINEAR|DMAMAP_COHERENT;
  602         map->buffer = buf;
  603         map->len = buflen;
  604         error = bus_dmamap_load_buffer(dmat,
  605             dm_segments, map, buf, buflen, kernel_pmap,
  606             flags, &lastaddr, &nsegs);
  607 
  608         if (error)
  609                 (*callback)(callback_arg, NULL, 0, error);
  610         else
  611                 (*callback)(callback_arg, dm_segments, nsegs + 1, error);
  612         
  613         CTR5(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d nsegs %d",
  614             __func__, dmat, dmat->flags, nsegs + 1, error);
  615 
  616         return (0);
  617 
  618 }
  619 
  620 /*
  621  * Like bus_dmamap_load(), but for mbufs.
  622  */
  623 int
  624 bus_dmamap_load_mbuf(bus_dma_tag_t dmat, bus_dmamap_t map, struct mbuf *m0,
  625                      bus_dmamap_callback2_t *callback, void *callback_arg,
  626                      int flags)
  627 {
  628 #ifdef __CC_SUPPORTS_DYNAMIC_ARRAY_INIT
  629         bus_dma_segment_t dm_segments[dmat->nsegments];
  630 #else
  631         bus_dma_segment_t dm_segments[BUS_DMAMAP_NSEGS];
  632 #endif
  633         int nsegs = -1, error = 0;
  634 
  635         M_ASSERTPKTHDR(m0);
  636 
  637         map->flags &= ~DMAMAP_TYPE_MASK;
  638         map->flags |= DMAMAP_MBUF | DMAMAP_COHERENT;
  639         map->buffer = m0;
  640         map->len = 0;
  641 
  642         if (m0->m_pkthdr.len <= dmat->maxsize) {
  643                 vm_offset_t lastaddr = 0;
  644                 struct mbuf *m;
  645 
  646                 for (m = m0; m != NULL && error == 0; m = m->m_next) {
  647                         if (m->m_len > 0) {
  648                                 error = bus_dmamap_load_buffer(dmat,
  649                                     dm_segments, map, m->m_data, m->m_len, 
  650                                     kernel_pmap, flags, &lastaddr, &nsegs);
  651                                 map->len += m->m_len;
  652                         }
  653                 }
  654         } else {
  655                 error = EINVAL;
  656         }
  657 
  658         if (error) {
  659                 /* 
  660                  * force "no valid mappings" on error in callback.
  661                  */
  662                 (*callback)(callback_arg, dm_segments, 0, 0, error);
  663         } else {
  664                 (*callback)(callback_arg, dm_segments, nsegs + 1,
  665                     m0->m_pkthdr.len, error);
  666         }
  667         CTR5(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d nsegs %d",
  668             __func__, dmat, dmat->flags, error, nsegs + 1);
  669 
  670         return (error);
  671 }
  672 
  673 int
  674 bus_dmamap_load_mbuf_sg(bus_dma_tag_t dmat, bus_dmamap_t map,
  675                         struct mbuf *m0, bus_dma_segment_t *segs, int *nsegs,
  676                         int flags)
  677 {
  678         int error = 0;
  679 
  680         M_ASSERTPKTHDR(m0);
  681 
  682         flags |= BUS_DMA_NOWAIT;
  683         *nsegs = -1;
  684         map->flags &= ~DMAMAP_TYPE_MASK;
  685         map->flags |= DMAMAP_MBUF | DMAMAP_COHERENT;
  686         map->buffer = m0;
  687         map->len = 0;
  688 
  689         if (m0->m_pkthdr.len <= dmat->maxsize) {
  690                 vm_offset_t lastaddr = 0;
  691                 struct mbuf *m;
  692 
  693                 for (m = m0; m != NULL && error == 0; m = m->m_next) {
  694                         if (m->m_len > 0) {
  695                                 error = bus_dmamap_load_buffer(dmat, segs, map,
  696                                     m->m_data, m->m_len, 
  697                                     kernel_pmap, flags, &lastaddr, nsegs);
  698                                 map->len += m->m_len;
  699                         }
  700                 }
  701         } else {
  702                 error = EINVAL;
  703         }
  704 
  705         ++*nsegs;
  706         CTR5(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d nsegs %d",
  707             __func__, dmat, dmat->flags, error, *nsegs);
  708 
  709         return (error);
  710 
  711 }
  712 
  713 /*
  714  * Like bus_dmamap_load(), but for uios.
  715  */
  716 int
  717 bus_dmamap_load_uio(bus_dma_tag_t dmat, bus_dmamap_t map, struct uio *uio,
  718     bus_dmamap_callback2_t *callback, void *callback_arg,
  719     int flags)
  720 {
  721 
  722         panic("Unimplemented %s at %s:%d\n", __func__, __FILE__, __LINE__);
  723         return (0);
  724 }
  725 
  726 /*
  727  * Release the mapping held by map.
  728  */
  729 void
  730 _bus_dmamap_unload(bus_dma_tag_t dmat, bus_dmamap_t map)
  731 {
  732 
  733         return;
  734 }
  735 
  736 static __inline void
  737 bus_dmamap_sync_buf(void *buf, int len, bus_dmasync_op_t op)
  738 {
  739 
  740         switch (op) {
  741         case BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE:
  742                 mips_dcache_wbinv_range((vm_offset_t)buf, len);
  743                 break;
  744 
  745         case BUS_DMASYNC_PREREAD:
  746 #if 1
  747                 mips_dcache_wbinv_range((vm_offset_t)buf, len);
  748 #else
  749                 mips_dcache_inv_range((vm_offset_t)buf, len);
  750 #endif
  751                 break;
  752 
  753         case BUS_DMASYNC_PREWRITE:
  754                 mips_dcache_wb_range((vm_offset_t)buf, len);
  755                 break;
  756         }
  757 }
  758 
  759 void
  760 _bus_dmamap_sync(bus_dma_tag_t dmat, bus_dmamap_t map, bus_dmasync_op_t op)
  761 {
  762         struct mbuf *m;
  763         struct uio *uio;
  764         int resid;
  765         struct iovec *iov;
  766         
  767 
  768         /*
  769          * Mixing PRE and POST operations is not allowed.
  770          */
  771         if ((op & (BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE)) != 0 &&
  772             (op & (BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE)) != 0)
  773                 panic("_bus_dmamap_sync: mix PRE and POST");
  774 
  775         /*
  776          * Since we're dealing with a virtually-indexed, write-back
  777          * cache, we need to do the following things:
  778          *
  779          *      PREREAD -- Invalidate D-cache.  Note we might have
  780          *      to also write-back here if we have to use an Index
  781          *      op, or if the buffer start/end is not cache-line aligned.
  782          *
  783          *      PREWRITE -- Write-back the D-cache.  If we have to use
  784          *      an Index op, we also have to invalidate.  Note that if
  785          *      we are doing PREREAD|PREWRITE, we can collapse everything
  786          *      into a single op.
  787          *
  788          *      POSTREAD -- Nothing.
  789          *
  790          *      POSTWRITE -- Nothing.
  791          */
  792 
  793         /*
  794          * Flush the write buffer.
  795          * XXX Is this always necessary?
  796          */
  797         mips_wbflush();
  798 
  799         op &= (BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
  800         if (op == 0)
  801                 return;
  802 
  803         CTR3(KTR_BUSDMA, "%s: op %x flags %x", __func__, op, map->flags);
  804         switch(map->flags & DMAMAP_TYPE_MASK) {
  805         case DMAMAP_LINEAR:
  806                 bus_dmamap_sync_buf(map->buffer, map->len, op);
  807                 break;
  808         case DMAMAP_MBUF:
  809                 m = map->buffer;
  810                 while (m) {
  811                         if (m->m_len > 0)
  812                                 bus_dmamap_sync_buf(m->m_data, m->m_len, op);
  813                         m = m->m_next;
  814                 }
  815                 break;
  816         case DMAMAP_UIO:
  817                 uio = map->buffer;
  818                 iov = uio->uio_iov;
  819                 resid = uio->uio_resid;
  820                 for (int i = 0; i < uio->uio_iovcnt && resid != 0; i++) {
  821                         bus_size_t minlen = resid < iov[i].iov_len ? resid :
  822                             iov[i].iov_len;
  823                         if (minlen > 0) {
  824                                 bus_dmamap_sync_buf(iov[i].iov_base, minlen, op);
  825                                 resid -= minlen;
  826                         }
  827                 }
  828                 break;
  829         default:
  830                 break;
  831         }
  832 }

Cache object: dd0b37a9a03ba6972dbaa385179a5360


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.