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/arm/arm/busdma_machdep-v6.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) 2012 Ian Lepore
    3  * Copyright (c) 2010 Mark Tinguely
    4  * Copyright (c) 2004 Olivier Houchard
    5  * Copyright (c) 2002 Peter Grehan
    6  * Copyright (c) 1997, 1998 Justin T. Gibbs.
    7  * All rights reserved.
    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  *    without modification, immediately at the beginning of the file.
   15  * 2. The name of the author may not be used to endorse or promote products
   16  *    derived from this software without specific prior written permission.
   17  *
   18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   21  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
   22  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   28  * SUCH DAMAGE.
   29  *
   30  *  From i386/busdma_machdep.c 191438 2009-04-23 20:24:19Z jhb
   31  */
   32 
   33 #include <sys/cdefs.h>
   34 __FBSDID("$FreeBSD: releng/10.0/sys/arm/arm/busdma_machdep-v6.c 254229 2013-08-11 21:21:02Z cognet $");
   35 
   36 #define _ARM32_BUS_DMA_PRIVATE
   37 #include <sys/param.h>
   38 #include <sys/kdb.h>
   39 #include <ddb/ddb.h>
   40 #include <ddb/db_output.h>
   41 #include <sys/systm.h>
   42 #include <sys/malloc.h>
   43 #include <sys/bus.h>
   44 #include <sys/busdma_bufalloc.h>
   45 #include <sys/interrupt.h>
   46 #include <sys/kernel.h>
   47 #include <sys/ktr.h>
   48 #include <sys/lock.h>
   49 #include <sys/memdesc.h>
   50 #include <sys/proc.h>
   51 #include <sys/mutex.h>
   52 #include <sys/sysctl.h>
   53 #include <sys/uio.h>
   54 
   55 #include <vm/vm.h>
   56 #include <vm/vm_page.h>
   57 #include <vm/vm_map.h>
   58 #include <vm/vm_extern.h>
   59 #include <vm/vm_kern.h>
   60 
   61 #include <machine/atomic.h>
   62 #include <machine/bus.h>
   63 #include <machine/cpufunc.h>
   64 #include <machine/md_var.h>
   65 
   66 #define MAX_BPAGES 64
   67 #define BUS_DMA_COULD_BOUNCE    BUS_DMA_BUS3
   68 #define BUS_DMA_MIN_ALLOC_COMP  BUS_DMA_BUS4
   69 
   70 struct bounce_zone;
   71 
   72 struct bus_dma_tag {
   73         bus_dma_tag_t     parent;
   74         bus_size_t        alignment;
   75         bus_size_t        boundary;
   76         bus_addr_t        lowaddr;
   77         bus_addr_t        highaddr;
   78         bus_dma_filter_t *filter;
   79         void             *filterarg;
   80         bus_size_t        maxsize;
   81         u_int             nsegments;
   82         bus_size_t        maxsegsz;
   83         int               flags;
   84         int               ref_count;
   85         int               map_count;
   86         bus_dma_lock_t   *lockfunc;
   87         void             *lockfuncarg;
   88         struct bounce_zone *bounce_zone;
   89         /*
   90          * DMA range for this tag.  If the page doesn't fall within
   91          * one of these ranges, an error is returned.  The caller
   92          * may then decide what to do with the transfer.  If the
   93          * range pointer is NULL, it is ignored.
   94          */
   95         struct arm32_dma_range  *ranges;
   96         int                     _nranges;
   97         /*
   98          * Most tags need one or two segments, and can use the local tagsegs
   99          * array.  For tags with a larger limit, we'll allocate a bigger array
  100          * on first use.
  101          */
  102         bus_dma_segment_t       *segments;
  103         bus_dma_segment_t       tagsegs[2];
  104 
  105 
  106 };
  107 
  108 struct bounce_page {
  109         vm_offset_t     vaddr;          /* kva of bounce buffer */
  110         bus_addr_t      busaddr;        /* Physical address */
  111         vm_offset_t     datavaddr;      /* kva of client data */
  112         bus_addr_t      dataaddr;       /* client physical address */
  113         bus_size_t      datacount;      /* client data count */
  114         STAILQ_ENTRY(bounce_page) links;
  115 };
  116 
  117 struct sync_list {
  118         vm_offset_t     vaddr;          /* kva of bounce buffer */
  119         bus_addr_t      busaddr;        /* Physical address */
  120         bus_size_t      datacount;      /* client data count */
  121 };
  122 
  123 int busdma_swi_pending;
  124 
  125 struct bounce_zone {
  126         STAILQ_ENTRY(bounce_zone) links;
  127         STAILQ_HEAD(bp_list, bounce_page) bounce_page_list;
  128         int             total_bpages;
  129         int             free_bpages;
  130         int             reserved_bpages;
  131         int             active_bpages;
  132         int             total_bounced;
  133         int             total_deferred;
  134         int             map_count;
  135         bus_size_t      alignment;
  136         bus_addr_t      lowaddr;
  137         char            zoneid[8];
  138         char            lowaddrid[20];
  139         struct sysctl_ctx_list sysctl_tree;
  140         struct sysctl_oid *sysctl_tree_top;
  141 };
  142 
  143 static struct mtx bounce_lock;
  144 static int total_bpages;
  145 static int busdma_zonecount;
  146 static STAILQ_HEAD(, bounce_zone) bounce_zone_list;
  147 
  148 SYSCTL_NODE(_hw, OID_AUTO, busdma, CTLFLAG_RD, 0, "Busdma parameters");
  149 SYSCTL_INT(_hw_busdma, OID_AUTO, total_bpages, CTLFLAG_RD, &total_bpages, 0,
  150            "Total bounce pages");
  151 
  152 struct bus_dmamap {
  153         struct bp_list         bpages;
  154         int                    pagesneeded;
  155         int                    pagesreserved;
  156         bus_dma_tag_t          dmat;
  157         struct memdesc         mem;
  158         pmap_t                 pmap;
  159         bus_dmamap_callback_t *callback;
  160         void                  *callback_arg;
  161         int                   flags;
  162 #define DMAMAP_COHERENT         (1 << 0)
  163         STAILQ_ENTRY(bus_dmamap) links;
  164         int                    sync_count;
  165         struct sync_list       slist[];
  166 };
  167 
  168 static STAILQ_HEAD(, bus_dmamap) bounce_map_waitinglist;
  169 static STAILQ_HEAD(, bus_dmamap) bounce_map_callbacklist;
  170 
  171 static void init_bounce_pages(void *dummy);
  172 static int alloc_bounce_zone(bus_dma_tag_t dmat);
  173 static int alloc_bounce_pages(bus_dma_tag_t dmat, u_int numpages);
  174 static int reserve_bounce_pages(bus_dma_tag_t dmat, bus_dmamap_t map,
  175                                 int commit);
  176 static bus_addr_t add_bounce_page(bus_dma_tag_t dmat, bus_dmamap_t map,
  177                                   vm_offset_t vaddr, bus_addr_t addr,
  178                                   bus_size_t size);
  179 static void free_bounce_page(bus_dma_tag_t dmat, struct bounce_page *bpage);
  180 int run_filter(bus_dma_tag_t dmat, bus_addr_t paddr, bus_size_t size, int coherent);
  181 static void _bus_dmamap_count_pages(bus_dma_tag_t dmat, bus_dmamap_t map,
  182     void *buf, bus_size_t buflen, int flags);
  183 static void _bus_dmamap_count_phys(bus_dma_tag_t dmat, bus_dmamap_t map,
  184     vm_paddr_t buf, bus_size_t buflen, int flags);
  185 static int _bus_dmamap_reserve_pages(bus_dma_tag_t dmat, bus_dmamap_t map,
  186     int flags);
  187 
  188 static busdma_bufalloc_t coherent_allocator;    /* Cache of coherent buffers */
  189 static busdma_bufalloc_t standard_allocator;    /* Cache of standard buffers */
  190 static void
  191 busdma_init(void *dummy)
  192 {
  193         int uma_flags;
  194 
  195         uma_flags = 0;
  196 
  197         /* Create a cache of buffers in standard (cacheable) memory. */
  198         standard_allocator = busdma_bufalloc_create("buffer", 
  199             arm_dcache_align,   /* minimum_alignment */
  200             NULL,               /* uma_alloc func */ 
  201             NULL,               /* uma_free func */
  202             uma_flags);         /* uma_zcreate_flags */
  203 
  204 #ifdef INVARIANTS
  205         /* 
  206          * Force UMA zone to allocate service structures like
  207          * slabs using own allocator. uma_debug code performs
  208          * atomic ops on uma_slab_t fields and safety of this
  209          * operation is not guaranteed for write-back caches
  210          */
  211         uma_flags = UMA_ZONE_OFFPAGE;
  212 #endif
  213         /*
  214          * Create a cache of buffers in uncacheable memory, to implement the
  215          * BUS_DMA_COHERENT (and potentially BUS_DMA_NOCACHE) flag.
  216          */
  217         coherent_allocator = busdma_bufalloc_create("coherent",
  218             arm_dcache_align,   /* minimum_alignment */
  219             busdma_bufalloc_alloc_uncacheable, 
  220             busdma_bufalloc_free_uncacheable, 
  221             uma_flags); /* uma_zcreate_flags */
  222 }
  223 
  224 /*
  225  * This init historically used SI_SUB_VM, but now the init code requires
  226  * malloc(9) using M_DEVBUF memory, which is set up later than SI_SUB_VM, by
  227  * SI_SUB_KMEM and SI_ORDER_SECOND, so we'll go right after that by using
  228  * SI_SUB_KMEM and SI_ORDER_THIRD.
  229  */
  230 SYSINIT(busdma, SI_SUB_KMEM, SI_ORDER_THIRD, busdma_init, NULL);
  231 
  232 static __inline int
  233 _bus_dma_can_bounce(vm_offset_t lowaddr, vm_offset_t highaddr)
  234 {
  235         int i;
  236         for (i = 0; phys_avail[i] && phys_avail[i + 1]; i += 2) {
  237                 if ((lowaddr >= phys_avail[i] && lowaddr <= phys_avail[i + 1])
  238                     || (lowaddr < phys_avail[i] &&
  239                     highaddr > phys_avail[i]))
  240                         return (1);
  241         }
  242         return (0);
  243 }
  244 
  245 static __inline struct arm32_dma_range *
  246 _bus_dma_inrange(struct arm32_dma_range *ranges, int nranges,
  247     bus_addr_t curaddr)
  248 {
  249         struct arm32_dma_range *dr;
  250         int i;
  251 
  252         for (i = 0, dr = ranges; i < nranges; i++, dr++) {
  253                 if (curaddr >= dr->dr_sysbase &&
  254                     round_page(curaddr) <= (dr->dr_sysbase + dr->dr_len))
  255                         return (dr);
  256         }
  257 
  258         return (NULL);
  259 }
  260 
  261 /*
  262  * Return true if a match is made.
  263  *
  264  * To find a match walk the chain of bus_dma_tag_t's looking for 'paddr'.
  265  *
  266  * If paddr is within the bounds of the dma tag then call the filter callback
  267  * to check for a match, if there is no filter callback then assume a match.
  268  */
  269 int
  270 run_filter(bus_dma_tag_t dmat, bus_addr_t paddr, bus_size_t size, int coherent)
  271 {
  272         int retval;
  273 
  274         retval = 0;
  275 
  276         do {
  277                 if (((paddr > dmat->lowaddr && paddr <= dmat->highaddr)
  278                  || ((paddr & (dmat->alignment - 1)) != 0) ||
  279                  (!coherent && (size & arm_dcache_align_mask)) ||
  280                  (!coherent && (paddr & arm_dcache_align_mask)))
  281                  && (dmat->filter == NULL
  282                   || (*dmat->filter)(dmat->filterarg, paddr) != 0))
  283                         retval = 1;
  284 
  285                 dmat = dmat->parent;
  286         } while (retval == 0 && dmat != NULL);
  287         return (retval);
  288 }
  289 
  290 /*
  291  * Convenience function for manipulating driver locks from busdma (during
  292  * busdma_swi, for example).  Drivers that don't provide their own locks
  293  * should specify &Giant to dmat->lockfuncarg.  Drivers that use their own
  294  * non-mutex locking scheme don't have to use this at all.
  295  */
  296 void
  297 busdma_lock_mutex(void *arg, bus_dma_lock_op_t op)
  298 {
  299         struct mtx *dmtx;
  300 
  301         dmtx = (struct mtx *)arg;
  302         switch (op) {
  303         case BUS_DMA_LOCK:
  304                 mtx_lock(dmtx);
  305                 break;
  306         case BUS_DMA_UNLOCK:
  307                 mtx_unlock(dmtx);
  308                 break;
  309         default:
  310                 panic("Unknown operation 0x%x for busdma_lock_mutex!", op);
  311         }
  312 }
  313 
  314 /*
  315  * dflt_lock should never get called.  It gets put into the dma tag when
  316  * lockfunc == NULL, which is only valid if the maps that are associated
  317  * with the tag are meant to never be defered.
  318  * XXX Should have a way to identify which driver is responsible here.
  319  */
  320 static void
  321 dflt_lock(void *arg, bus_dma_lock_op_t op)
  322 {
  323         panic("driver error: busdma dflt_lock called");
  324 }
  325 
  326 /*
  327  * Allocate a device specific dma_tag.
  328  */
  329 int
  330 bus_dma_tag_create(bus_dma_tag_t parent, bus_size_t alignment,
  331                    bus_size_t boundary, bus_addr_t lowaddr,
  332                    bus_addr_t highaddr, bus_dma_filter_t *filter,
  333                    void *filterarg, bus_size_t maxsize, int nsegments,
  334                    bus_size_t maxsegsz, int flags, bus_dma_lock_t *lockfunc,
  335                    void *lockfuncarg, bus_dma_tag_t *dmat)
  336 {
  337         bus_dma_tag_t newtag;
  338         int error = 0;
  339 
  340 #if 0
  341         if (!parent)
  342                 parent = arm_root_dma_tag;
  343 #endif
  344 
  345         /* Basic sanity checking */
  346         if (boundary != 0 && boundary < maxsegsz)
  347                 maxsegsz = boundary;
  348 
  349         /* Return a NULL tag on failure */
  350         *dmat = NULL;
  351 
  352         if (maxsegsz == 0) {
  353                 return (EINVAL);
  354         }
  355 
  356         newtag = (bus_dma_tag_t)malloc(sizeof(*newtag), M_DEVBUF,
  357             M_ZERO | M_NOWAIT);
  358         if (newtag == NULL) {
  359                 CTR4(KTR_BUSDMA, "%s returned tag %p tag flags 0x%x error %d",
  360                     __func__, newtag, 0, error);
  361                 return (ENOMEM);
  362         }
  363 
  364         newtag->parent = parent;
  365         newtag->alignment = alignment;
  366         newtag->boundary = boundary;
  367         newtag->lowaddr = trunc_page((vm_paddr_t)lowaddr) + (PAGE_SIZE - 1);
  368         newtag->highaddr = trunc_page((vm_paddr_t)highaddr) +
  369             (PAGE_SIZE - 1);
  370         newtag->filter = filter;
  371         newtag->filterarg = filterarg;
  372         newtag->maxsize = maxsize;
  373         newtag->nsegments = nsegments;
  374         newtag->maxsegsz = maxsegsz;
  375         newtag->flags = flags;
  376         newtag->ref_count = 1; /* Count ourself */
  377         newtag->map_count = 0;
  378         newtag->ranges = bus_dma_get_range();
  379         newtag->_nranges = bus_dma_get_range_nb();
  380         if (lockfunc != NULL) {
  381                 newtag->lockfunc = lockfunc;
  382                 newtag->lockfuncarg = lockfuncarg;
  383         } else {
  384                 newtag->lockfunc = dflt_lock;
  385                 newtag->lockfuncarg = NULL;
  386         }
  387         /*
  388          * If all the segments we need fit into the local tagsegs array, set the
  389          * pointer now.  Otherwise NULL the pointer and an array of segments
  390          * will be allocated later, on first use.  We don't pre-allocate now
  391          * because some tags exist just to pass contraints to children in the
  392          * device hierarchy, and they tend to use BUS_SPACE_UNRESTRICTED and we
  393          * sure don't want to try to allocate an array for that.
  394          */
  395         if (newtag->nsegments <= nitems(newtag->tagsegs))
  396                 newtag->segments = newtag->tagsegs;
  397         else
  398                 newtag->segments = NULL;
  399 
  400         /* Take into account any restrictions imposed by our parent tag */
  401         if (parent != NULL) {
  402                 newtag->lowaddr = MIN(parent->lowaddr, newtag->lowaddr);
  403                 newtag->highaddr = MAX(parent->highaddr, newtag->highaddr);
  404                 if (newtag->boundary == 0)
  405                         newtag->boundary = parent->boundary;
  406                 else if (parent->boundary != 0)
  407                         newtag->boundary = MIN(parent->boundary,
  408                                                newtag->boundary);
  409                 if ((newtag->filter != NULL) ||
  410                     ((parent->flags & BUS_DMA_COULD_BOUNCE) != 0))
  411                         newtag->flags |= BUS_DMA_COULD_BOUNCE;
  412                 if (newtag->filter == NULL) {
  413                         /*
  414                          * Short circuit looking at our parent directly
  415                          * since we have encapsulated all of its information
  416                          */
  417                         newtag->filter = parent->filter;
  418                         newtag->filterarg = parent->filterarg;
  419                         newtag->parent = parent->parent;
  420                 }
  421                 if (newtag->parent != NULL)
  422                         atomic_add_int(&parent->ref_count, 1);
  423         }
  424 
  425         if (_bus_dma_can_bounce(newtag->lowaddr, newtag->highaddr)
  426          || newtag->alignment > 1)
  427                 newtag->flags |= BUS_DMA_COULD_BOUNCE;
  428         else
  429                 maxsize = 2; /* Need at most 2 bounce pages for unaligned access on cache line boundaries */
  430 
  431         if ((flags & BUS_DMA_ALLOCNOW) != 0) {
  432                 struct bounce_zone *bz;
  433 
  434                 /* Must bounce */
  435 
  436                 if ((error = alloc_bounce_zone(newtag)) != 0) {
  437                         free(newtag, M_DEVBUF);
  438                         return (error);
  439                 }
  440                 bz = newtag->bounce_zone;
  441 
  442                 if (ptoa(bz->total_bpages) < maxsize) {
  443                         int pages;
  444 
  445                         pages = atop(maxsize) - bz->total_bpages;
  446 
  447                         /* Add pages to our bounce pool */
  448                         if (alloc_bounce_pages(newtag, pages) < pages)
  449                                 error = ENOMEM;
  450                 }
  451                 /* Performed initial allocation */
  452                 newtag->flags |= BUS_DMA_MIN_ALLOC_COMP;
  453         } else
  454                 newtag->bounce_zone = NULL;
  455 
  456         if (error != 0) {
  457                 free(newtag, M_DEVBUF);
  458         } else {
  459                 *dmat = newtag;
  460         }
  461         CTR4(KTR_BUSDMA, "%s returned tag %p tag flags 0x%x error %d",
  462             __func__, newtag, (newtag != NULL ? newtag->flags : 0), error);
  463         return (error);
  464 }
  465 
  466 int
  467 bus_dma_tag_destroy(bus_dma_tag_t dmat)
  468 {
  469         bus_dma_tag_t dmat_copy;
  470         int error;
  471 
  472         error = 0;
  473         dmat_copy = dmat;
  474 
  475         if (dmat != NULL) {
  476 
  477                 if (dmat->map_count != 0) {
  478                         error = EBUSY;
  479                         goto out;
  480                 }
  481 
  482                 while (dmat != NULL) {
  483                         bus_dma_tag_t parent;
  484 
  485                         parent = dmat->parent;
  486                         atomic_subtract_int(&dmat->ref_count, 1);
  487                         if (dmat->ref_count == 0) {
  488                                 if (dmat->segments != NULL &&
  489                                     dmat->segments != dmat->tagsegs)
  490                                         free(dmat->segments, M_DEVBUF);
  491                                 free(dmat, M_DEVBUF);
  492                                 /*
  493                                  * Last reference count, so
  494                                  * release our reference
  495                                  * count on our parent.
  496                                  */
  497                                 dmat = parent;
  498                         } else
  499                                 dmat = NULL;
  500                 }
  501         }
  502 out:
  503         CTR3(KTR_BUSDMA, "%s tag %p error %d", __func__, dmat_copy, error);
  504         return (error);
  505 }
  506 
  507 static int allocate_bz_and_pages(bus_dma_tag_t dmat, bus_dmamap_t mapp)
  508 {
  509         struct bounce_zone *bz;
  510         int maxpages;
  511         int error;
  512                 
  513         if (dmat->bounce_zone == NULL)
  514                 if ((error = alloc_bounce_zone(dmat)) != 0)
  515                         return (error);
  516         bz = dmat->bounce_zone;
  517         /* Initialize the new map */
  518         STAILQ_INIT(&(mapp->bpages));
  519 
  520         /*
  521          * Attempt to add pages to our pool on a per-instance
  522          * basis up to a sane limit.
  523          */
  524         if (dmat->flags & BUS_DMA_COULD_BOUNCE)
  525                 maxpages = MAX_BPAGES;
  526         else
  527                 maxpages = 2 * bz->map_count; /* Only need at most 2 pages for buffers unaligned on cache line boundaries */
  528         if ((dmat->flags & BUS_DMA_MIN_ALLOC_COMP) == 0
  529             || (bz->map_count > 0 && bz->total_bpages < maxpages)) {
  530                 int pages;
  531                 
  532                 pages = MAX(atop(dmat->maxsize), 1);
  533                 pages = MIN(maxpages - bz->total_bpages, pages);
  534                 pages = MAX(pages, 1);
  535                 if (alloc_bounce_pages(dmat, pages) < pages)
  536                         return (ENOMEM);
  537                 
  538                 if ((dmat->flags & BUS_DMA_MIN_ALLOC_COMP) == 0)
  539                         dmat->flags |= BUS_DMA_MIN_ALLOC_COMP;
  540         }
  541         bz->map_count++;
  542         return (0);
  543 }
  544 
  545 /*
  546  * Allocate a handle for mapping from kva/uva/physical
  547  * address space into bus device space.
  548  */
  549 int
  550 bus_dmamap_create(bus_dma_tag_t dmat, int flags, bus_dmamap_t *mapp)
  551 {
  552         int mapsize;
  553         int error = 0;
  554 
  555         mapsize = sizeof(**mapp) + (sizeof(struct sync_list) * dmat->nsegments);
  556         *mapp = (bus_dmamap_t)malloc(mapsize, M_DEVBUF, M_NOWAIT | M_ZERO);
  557         if (*mapp == NULL) {
  558                 CTR3(KTR_BUSDMA, "%s: tag %p error %d", __func__, dmat, ENOMEM);
  559                 return (ENOMEM);
  560         }
  561         (*mapp)->sync_count = 0;
  562 
  563         if (dmat->segments == NULL) {
  564                 dmat->segments = (bus_dma_segment_t *)malloc(
  565                     sizeof(bus_dma_segment_t) * dmat->nsegments, M_DEVBUF,
  566                     M_NOWAIT);
  567                 if (dmat->segments == NULL) {
  568                         CTR3(KTR_BUSDMA, "%s: tag %p error %d",
  569                             __func__, dmat, ENOMEM);
  570                         free(*mapp, M_DEVBUF);
  571                         *mapp = NULL;
  572                         return (ENOMEM);
  573                 }
  574         }
  575         /*
  576          * Bouncing might be required if the driver asks for an active
  577          * exclusion region, a data alignment that is stricter than 1, and/or
  578          * an active address boundary.
  579          */
  580         error = allocate_bz_and_pages(dmat, *mapp);
  581         if (error != 0) {
  582                 free(*mapp, M_DEVBUF);
  583                 *mapp = NULL;
  584                 return (error);
  585         }
  586         return (error);
  587 }
  588 
  589 /*
  590  * Destroy a handle for mapping from kva/uva/physical
  591  * address space into bus device space.
  592  */
  593 int
  594 bus_dmamap_destroy(bus_dma_tag_t dmat, bus_dmamap_t map)
  595 {
  596         if (STAILQ_FIRST(&map->bpages) != NULL || map->sync_count != 0) {
  597                 CTR3(KTR_BUSDMA, "%s: tag %p error %d",
  598                     __func__, dmat, EBUSY);
  599                 return (EBUSY);
  600         }
  601         if (dmat->bounce_zone)
  602                 dmat->bounce_zone->map_count--;
  603         free(map, M_DEVBUF);
  604         dmat->map_count--;
  605         CTR2(KTR_BUSDMA, "%s: tag %p error 0", __func__, dmat);
  606         return (0);
  607 }
  608 
  609 
  610 /*
  611  * Allocate a piece of memory that can be efficiently mapped into
  612  * bus device space based on the constraints lited in the dma tag.
  613  * A dmamap to for use with dmamap_load is also allocated.
  614  */
  615 int
  616 bus_dmamem_alloc(bus_dma_tag_t dmat, void** vaddr, int flags,
  617                  bus_dmamap_t *mapp)
  618 {
  619         busdma_bufalloc_t ba;
  620         struct busdma_bufzone *bufzone;
  621         vm_memattr_t memattr;
  622         int mflags;
  623         int mapsize;
  624         int error;
  625 
  626         if (flags & BUS_DMA_NOWAIT)
  627                 mflags = M_NOWAIT;
  628         else
  629                 mflags = M_WAITOK;
  630 
  631         /* ARM non-snooping caches need a map for the VA cache sync structure */
  632 
  633         mapsize = sizeof(**mapp) + (sizeof(struct sync_list) * dmat->nsegments);
  634         *mapp = (bus_dmamap_t)malloc(mapsize, M_DEVBUF, M_NOWAIT | M_ZERO);
  635         if (*mapp == NULL) {
  636                 CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d",
  637                     __func__, dmat, dmat->flags, ENOMEM);
  638                 return (ENOMEM);
  639         }
  640 
  641         (*mapp)->sync_count = 0;
  642         /* We may need bounce pages, even for allocated memory */
  643         error = allocate_bz_and_pages(dmat, *mapp);
  644         if (error != 0) {
  645                 free(*mapp, M_DEVBUF);
  646                 *mapp = NULL;
  647                 return (error);
  648         }
  649 
  650         if (dmat->segments == NULL) {
  651                 dmat->segments = (bus_dma_segment_t *)malloc(
  652                     sizeof(bus_dma_segment_t) * dmat->nsegments, M_DEVBUF,
  653                     mflags);
  654                 if (dmat->segments == NULL) {
  655                         CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d",
  656                             __func__, dmat, dmat->flags, ENOMEM);
  657                         free(*mapp, M_DEVBUF);
  658                         *mapp = NULL;
  659                         return (ENOMEM);
  660                 }
  661         }
  662 
  663         if (flags & BUS_DMA_ZERO)
  664                 mflags |= M_ZERO;
  665         if (flags & BUS_DMA_COHERENT) {
  666                 memattr = VM_MEMATTR_UNCACHEABLE;
  667                 ba = coherent_allocator;
  668                 (*mapp)->flags |= DMAMAP_COHERENT;
  669         } else {
  670                 memattr = VM_MEMATTR_DEFAULT;
  671                 ba = standard_allocator;
  672                 (*mapp)->flags = 0;
  673         }
  674 
  675         /*
  676          * Try to find a bufzone in the allocator that holds a cache of buffers
  677          * of the right size for this request.  If the buffer is too big to be
  678          * held in the allocator cache, this returns NULL.
  679          */
  680         bufzone = busdma_bufalloc_findzone(ba, dmat->maxsize);
  681 
  682         /*
  683          * Allocate the buffer from the uma(9) allocator if...
  684          *  - It's small enough to be in the allocator (bufzone not NULL).
  685          *  - The alignment constraint isn't larger than the allocation size
  686          *    (the allocator aligns buffers to their size boundaries).
  687          *  - There's no need to handle lowaddr/highaddr exclusion zones.
  688          * else allocate non-contiguous pages if...
  689          *  - The page count that could get allocated doesn't exceed nsegments.
  690          *  - The alignment constraint isn't larger than a page boundary.
  691          *  - There are no boundary-crossing constraints.
  692          * else allocate a block of contiguous pages because one or more of the
  693          * constraints is something that only the contig allocator can fulfill.
  694          */
  695         if (bufzone != NULL && dmat->alignment <= bufzone->size &&
  696             !_bus_dma_can_bounce(dmat->lowaddr, dmat->highaddr)) {
  697                 *vaddr = uma_zalloc(bufzone->umazone, mflags);
  698         } else if (dmat->nsegments >= btoc(dmat->maxsize) &&
  699             dmat->alignment <= PAGE_SIZE && dmat->boundary == 0) {
  700                 *vaddr = (void *)kmem_alloc_attr(kernel_arena, dmat->maxsize,
  701                     mflags, 0, dmat->lowaddr, memattr);
  702         } else {
  703                 *vaddr = (void *)kmem_alloc_contig(kernel_arena, dmat->maxsize,
  704                     mflags, 0, dmat->lowaddr, dmat->alignment, dmat->boundary,
  705                     memattr);
  706         }
  707 
  708 
  709         if (*vaddr == NULL) {
  710                 CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d",
  711                     __func__, dmat, dmat->flags, ENOMEM);
  712                 free(*mapp, M_DEVBUF);
  713                 *mapp = NULL;
  714                 return (ENOMEM);
  715         } else if ((uintptr_t)*vaddr & (dmat->alignment - 1)) {
  716                 printf("bus_dmamem_alloc failed to align memory properly.\n");
  717         }
  718         dmat->map_count++;
  719 
  720         CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x error %d",
  721             __func__, dmat, dmat->flags, 0);
  722         return (0);
  723 }
  724 
  725 /*
  726  * Free a piece of memory and it's allociated dmamap, that was allocated
  727  * via bus_dmamem_alloc.  Make the same choice for free/contigfree.
  728  */
  729 void
  730 bus_dmamem_free(bus_dma_tag_t dmat, void *vaddr, bus_dmamap_t map)
  731 {
  732         struct busdma_bufzone *bufzone;
  733         busdma_bufalloc_t ba;
  734 
  735         if (map->flags & DMAMAP_COHERENT)
  736                 ba = coherent_allocator;
  737         else
  738                 ba = standard_allocator;
  739 
  740         /* Be careful not to access map from here on. */
  741 
  742         bufzone = busdma_bufalloc_findzone(ba, dmat->maxsize);
  743 
  744         if (bufzone != NULL && dmat->alignment <= bufzone->size &&
  745             !_bus_dma_can_bounce(dmat->lowaddr, dmat->highaddr))
  746                 uma_zfree(bufzone->umazone, vaddr);
  747         else
  748                 kmem_free(kernel_arena, (vm_offset_t)vaddr, dmat->maxsize);
  749 
  750         dmat->map_count--;
  751         free(map, M_DEVBUF);
  752         CTR3(KTR_BUSDMA, "%s: tag %p flags 0x%x", __func__, dmat, dmat->flags);
  753 }
  754 
  755 static void
  756 _bus_dmamap_count_phys(bus_dma_tag_t dmat, bus_dmamap_t map, vm_paddr_t buf,
  757     bus_size_t buflen, int flags)
  758 {
  759         bus_addr_t curaddr;
  760         bus_size_t sgsize;
  761 
  762         if (map->pagesneeded == 0) {
  763                 CTR5(KTR_BUSDMA, "lowaddr= %d, boundary= %d, alignment= %d"
  764                     " map= %p, pagesneeded= %d",
  765                     dmat->lowaddr, dmat->boundary, dmat->alignment,
  766                     map, map->pagesneeded);
  767                 /*
  768                  * Count the number of bounce pages
  769                  * needed in order to complete this transfer
  770                  */
  771                 curaddr = buf;
  772                 while (buflen != 0) {
  773                         sgsize = MIN(buflen, dmat->maxsegsz);
  774                         if (run_filter(dmat, curaddr, sgsize, 
  775                             map->flags & DMAMAP_COHERENT) != 0) {
  776                                 sgsize = MIN(sgsize, PAGE_SIZE);
  777                                 map->pagesneeded++;
  778                         }
  779                         curaddr += sgsize;
  780                         buflen -= sgsize;
  781                 }
  782                 CTR1(KTR_BUSDMA, "pagesneeded= %d", map->pagesneeded);
  783         }
  784 }
  785 
  786 static void
  787 _bus_dmamap_count_pages(bus_dma_tag_t dmat, bus_dmamap_t map,
  788     void *buf, bus_size_t buflen, int flags)
  789 {
  790         vm_offset_t vaddr;
  791         vm_offset_t vendaddr;
  792         bus_addr_t paddr;
  793 
  794         if (map->pagesneeded == 0) {
  795                 CTR5(KTR_BUSDMA, "lowaddr= %d, boundary= %d, alignment= %d"
  796                     " map= %p, pagesneeded= %d",
  797                     dmat->lowaddr, dmat->boundary, dmat->alignment,
  798                     map, map->pagesneeded);
  799                 /*
  800                  * Count the number of bounce pages
  801                  * needed in order to complete this transfer
  802                  */
  803                 vaddr = (vm_offset_t)buf;
  804                 vendaddr = (vm_offset_t)buf + buflen;
  805 
  806                 while (vaddr < vendaddr) {
  807                         if (__predict_true(map->pmap == kernel_pmap))
  808                                 paddr = pmap_kextract(vaddr);
  809                         else
  810                                 paddr = pmap_extract(map->pmap, vaddr);
  811                         if (run_filter(dmat, paddr,
  812                             min(vendaddr - vaddr, 
  813                             (PAGE_SIZE - ((vm_offset_t)vaddr & PAGE_MASK))),
  814                             map->flags & DMAMAP_COHERENT) != 0) {
  815                                 map->pagesneeded++;
  816                         }
  817                         vaddr += (PAGE_SIZE - ((vm_offset_t)vaddr & PAGE_MASK));
  818 
  819                 }
  820                 CTR1(KTR_BUSDMA, "pagesneeded= %d", map->pagesneeded);
  821         }
  822 }
  823 
  824 static int
  825 _bus_dmamap_reserve_pages(bus_dma_tag_t dmat, bus_dmamap_t map, int flags)
  826 {
  827 
  828         /* Reserve Necessary Bounce Pages */
  829         mtx_lock(&bounce_lock);
  830         if (flags & BUS_DMA_NOWAIT) {
  831                 if (reserve_bounce_pages(dmat, map, 0) != 0) {
  832                         map->pagesneeded = 0;
  833                         mtx_unlock(&bounce_lock);
  834                         return (ENOMEM);
  835                 }
  836         } else {
  837                 if (reserve_bounce_pages(dmat, map, 1) != 0) {
  838                         /* Queue us for resources */
  839                         STAILQ_INSERT_TAIL(&bounce_map_waitinglist, map, links);
  840                         mtx_unlock(&bounce_lock);
  841                         return (EINPROGRESS);
  842                 }
  843         }
  844         mtx_unlock(&bounce_lock);
  845 
  846         return (0);
  847 }
  848 
  849 /*
  850  * Add a single contiguous physical range to the segment list.
  851  */
  852 static int
  853 _bus_dmamap_addseg(bus_dma_tag_t dmat, bus_dmamap_t map, bus_addr_t curaddr,
  854                    bus_size_t sgsize, bus_dma_segment_t *segs, int *segp)
  855 {
  856         bus_addr_t baddr, bmask;
  857         int seg;
  858 
  859         /*
  860          * Make sure we don't cross any boundaries.
  861          */
  862         bmask = ~(dmat->boundary - 1);
  863         if (dmat->boundary > 0) {
  864                 baddr = (curaddr + dmat->boundary) & bmask;
  865                 if (sgsize > (baddr - curaddr))
  866                         sgsize = (baddr - curaddr);
  867         }
  868 
  869         if (dmat->ranges) {
  870                 struct arm32_dma_range *dr;
  871 
  872                 dr = _bus_dma_inrange(dmat->ranges, dmat->_nranges,
  873                     curaddr);
  874                 if (dr == NULL) {
  875                         _bus_dmamap_unload(dmat, map);
  876                         return (0);
  877                 }
  878                 /*
  879                  * In a valid DMA range.  Translate the physical
  880                  * memory address to an address in the DMA window.
  881                  */
  882                 curaddr = (curaddr - dr->dr_sysbase) + dr->dr_busbase;
  883         }
  884 
  885         /*
  886          * Insert chunk into a segment, coalescing with
  887          * previous segment if possible.
  888          */
  889         seg = *segp;
  890         if (seg == -1) {
  891                 seg = 0;
  892                 segs[seg].ds_addr = curaddr;
  893                 segs[seg].ds_len = sgsize;
  894         } else {
  895                 if (curaddr == segs[seg].ds_addr + segs[seg].ds_len &&
  896                     (segs[seg].ds_len + sgsize) <= dmat->maxsegsz &&
  897                     (dmat->boundary == 0 ||
  898                      (segs[seg].ds_addr & bmask) == (curaddr & bmask)))
  899                         segs[seg].ds_len += sgsize;
  900                 else {
  901                         if (++seg >= dmat->nsegments)
  902                                 return (0);
  903                         segs[seg].ds_addr = curaddr;
  904                         segs[seg].ds_len = sgsize;
  905                 }
  906         }
  907         *segp = seg;
  908         return (sgsize);
  909 }
  910 
  911 /*
  912  * Utility function to load a physical buffer.  segp contains
  913  * the starting segment on entrace, and the ending segment on exit.
  914  */
  915 int
  916 _bus_dmamap_load_phys(bus_dma_tag_t dmat,
  917                       bus_dmamap_t map,
  918                       vm_paddr_t buf, bus_size_t buflen,
  919                       int flags,
  920                       bus_dma_segment_t *segs,
  921                       int *segp)
  922 {
  923         bus_addr_t curaddr;
  924         bus_size_t sgsize;
  925         int error;
  926 
  927         if (segs == NULL)
  928                 segs = dmat->segments;
  929 
  930         if (((map->flags & DMAMAP_COHERENT) == 0) ||
  931             (dmat->flags & BUS_DMA_COULD_BOUNCE) != 0) {
  932                 _bus_dmamap_count_phys(dmat, map, buf, buflen, flags);
  933                 if (map->pagesneeded != 0) {
  934                         error = _bus_dmamap_reserve_pages(dmat, map, flags);
  935                         if (error)
  936                                 return (error);
  937                 }
  938         }
  939 
  940         while (buflen > 0) {
  941                 curaddr = buf;
  942                 sgsize = MIN(buflen, dmat->maxsegsz);
  943                 if ((((map->flags & DMAMAP_COHERENT) == 0) ||
  944                     ((dmat->flags & BUS_DMA_COULD_BOUNCE) != 0)) &&
  945                     map->pagesneeded != 0 && run_filter(dmat, curaddr,
  946                     sgsize, map->flags & DMAMAP_COHERENT)) {
  947                         sgsize = MIN(sgsize, PAGE_SIZE);
  948                         curaddr = add_bounce_page(dmat, map, 0, curaddr,
  949                                                   sgsize);
  950                 }
  951                 sgsize = _bus_dmamap_addseg(dmat, map, curaddr, sgsize, segs,
  952                     segp);
  953                 if (sgsize == 0)
  954                         break;
  955                 buf += sgsize;
  956                 buflen -= sgsize;
  957         }
  958 
  959         /*
  960          * Did we fit?
  961          */
  962         if (buflen != 0) {
  963                 _bus_dmamap_unload(dmat, map);
  964                 return (EFBIG); /* XXX better return value here? */
  965         }
  966         return (0);
  967 }
  968 
  969 /*
  970  * Utility function to load a linear buffer.  segp contains
  971  * the starting segment on entrace, and the ending segment on exit.
  972  */
  973 int
  974 _bus_dmamap_load_buffer(bus_dma_tag_t dmat,
  975                         bus_dmamap_t map,
  976                         void *buf, bus_size_t buflen,
  977                         pmap_t pmap,
  978                         int flags,
  979                         bus_dma_segment_t *segs,
  980                         int *segp)
  981 {
  982         bus_size_t sgsize;
  983         bus_addr_t curaddr;
  984         vm_offset_t vaddr;
  985         struct sync_list *sl;
  986         int error;
  987 
  988         if (segs == NULL)
  989                 segs = dmat->segments;
  990 
  991         map->pmap = pmap;
  992 
  993         if (!(map->flags & DMAMAP_COHERENT) ||
  994             (dmat->flags & BUS_DMA_COULD_BOUNCE) != 0) {
  995                 _bus_dmamap_count_pages(dmat, map, buf, buflen, flags);
  996                 if (map->pagesneeded != 0) {
  997                         error = _bus_dmamap_reserve_pages(dmat, map, flags);
  998                         if (error)
  999                                 return (error);
 1000                 }
 1001         }
 1002 
 1003         sl = NULL;
 1004         vaddr = (vm_offset_t)buf;
 1005 
 1006         while (buflen > 0) {
 1007                 /*
 1008                  * Get the physical address for this segment.
 1009                  */
 1010                 if (__predict_true(map->pmap == kernel_pmap))
 1011                         curaddr = pmap_kextract(vaddr);
 1012                 else
 1013                         curaddr = pmap_extract(map->pmap, vaddr);
 1014 
 1015                 /*
 1016                  * Compute the segment size, and adjust counts.
 1017                  */
 1018                 sgsize = PAGE_SIZE - ((u_long)curaddr & PAGE_MASK);
 1019                 if (sgsize > dmat->maxsegsz)
 1020                         sgsize = dmat->maxsegsz;
 1021                 if (buflen < sgsize)
 1022                         sgsize = buflen;
 1023 
 1024                 if ((((map->flags & DMAMAP_COHERENT) == 0) ||
 1025                     ((dmat->flags & BUS_DMA_COULD_BOUNCE) != 0)) &&
 1026                     map->pagesneeded != 0 && run_filter(dmat, curaddr,
 1027                     sgsize, map->flags & DMAMAP_COHERENT)) {
 1028                         curaddr = add_bounce_page(dmat, map, vaddr, curaddr,
 1029                                                   sgsize);
 1030                 } else {
 1031                         sl = &map->slist[map->sync_count - 1];
 1032                         if (map->sync_count == 0 ||
 1033 #ifdef ARM_L2_PIPT
 1034                             curaddr != sl->busaddr + sl->datacount ||
 1035 #endif
 1036                             vaddr != sl->vaddr + sl->datacount) {
 1037                                 if (++map->sync_count > dmat->nsegments)
 1038                                         goto cleanup;
 1039                                 sl++;
 1040                                 sl->vaddr = vaddr;
 1041                                 sl->datacount = sgsize;
 1042                                 sl->busaddr = curaddr;
 1043                         } else
 1044                                 sl->datacount += sgsize;
 1045                 }
 1046                 sgsize = _bus_dmamap_addseg(dmat, map, curaddr, sgsize, segs,
 1047                                             segp);
 1048                 if (sgsize == 0)
 1049                         break;
 1050                 vaddr += sgsize;
 1051                 buflen -= sgsize;
 1052         }
 1053 
 1054 cleanup:
 1055         /*
 1056          * Did we fit?
 1057          */
 1058         if (buflen != 0) {
 1059                 _bus_dmamap_unload(dmat, map);
 1060                 return (EFBIG); /* XXX better return value here? */
 1061         }
 1062         return (0);
 1063 }
 1064 
 1065 
 1066 void
 1067 __bus_dmamap_waitok(bus_dma_tag_t dmat, bus_dmamap_t map,
 1068                     struct memdesc *mem, bus_dmamap_callback_t *callback,
 1069                     void *callback_arg)
 1070 {
 1071 
 1072         map->mem = *mem;
 1073         map->dmat = dmat;
 1074         map->callback = callback;
 1075         map->callback_arg = callback_arg;
 1076 }
 1077 
 1078 bus_dma_segment_t *
 1079 _bus_dmamap_complete(bus_dma_tag_t dmat, bus_dmamap_t map,
 1080                      bus_dma_segment_t *segs, int nsegs, int error)
 1081 {
 1082 
 1083         if (segs == NULL)
 1084                 segs = dmat->segments;
 1085         return (segs);
 1086 }
 1087 
 1088 /*
 1089  * Release the mapping held by map.
 1090  */
 1091 void
 1092 _bus_dmamap_unload(bus_dma_tag_t dmat, bus_dmamap_t map)
 1093 {
 1094         struct bounce_page *bpage;
 1095         struct bounce_zone *bz;
 1096 
 1097         if ((bz = dmat->bounce_zone) != NULL) {
 1098                 while ((bpage = STAILQ_FIRST(&map->bpages)) != NULL) {
 1099                         STAILQ_REMOVE_HEAD(&map->bpages, links);
 1100                         free_bounce_page(dmat, bpage);
 1101                 }
 1102 
 1103                 bz = dmat->bounce_zone;
 1104                 bz->free_bpages += map->pagesreserved;
 1105                 bz->reserved_bpages -= map->pagesreserved;
 1106                 map->pagesreserved = 0;
 1107                 map->pagesneeded = 0;
 1108         }
 1109         map->sync_count = 0;
 1110 }
 1111 
 1112 #ifdef notyetbounceuser
 1113         /* If busdma uses user pages, then the interrupt handler could
 1114          * be use the kernel vm mapping. Both bounce pages and sync list
 1115          * do not cross page boundaries.
 1116          * Below is a rough sequence that a person would do to fix the
 1117          * user page reference in the kernel vmspace. This would be
 1118          * done in the dma post routine.
 1119          */
 1120 void
 1121 _bus_dmamap_fix_user(vm_offset_t buf, bus_size_t len,
 1122                         pmap_t pmap, int op)
 1123 {
 1124         bus_size_t sgsize;
 1125         bus_addr_t curaddr;
 1126         vm_offset_t va;
 1127 
 1128                 /* each synclist entry is contained within a single page.
 1129                  *
 1130                  * this would be needed if BUS_DMASYNC_POSTxxxx was implemented
 1131                 */
 1132         curaddr = pmap_extract(pmap, buf);
 1133         va = pmap_dma_map(curaddr);
 1134         switch (op) {
 1135         case SYNC_USER_INV:
 1136                 cpu_dcache_wb_range(va, sgsize);
 1137                 break;
 1138 
 1139         case SYNC_USER_COPYTO:
 1140                 bcopy((void *)va, (void *)bounce, sgsize);
 1141                 break;
 1142 
 1143         case SYNC_USER_COPYFROM:
 1144                 bcopy((void *) bounce, (void *)va, sgsize);
 1145                 break;
 1146 
 1147         default:
 1148                 break;
 1149         }
 1150 
 1151         pmap_dma_unmap(va);
 1152 }
 1153 #endif
 1154 
 1155 #ifdef ARM_L2_PIPT
 1156 #define l2cache_wb_range(va, pa, size) cpu_l2cache_wb_range(pa, size)
 1157 #define l2cache_wbinv_range(va, pa, size) cpu_l2cache_wbinv_range(pa, size)
 1158 #define l2cache_inv_range(va, pa, size) cpu_l2cache_inv_range(pa, size)
 1159 #else
 1160 #define l2cache_wb_range(va, pa, size) cpu_l2cache_wb_range(va, size)
 1161 #define l2cache_wbinv_range(va, pa, size) cpu_l2cache_wbinv_range(va, size)
 1162 #define l2cache_inv_range(va, pa, size) cpu_l2cache_inv_range(va, size)
 1163 #endif
 1164 
 1165 void
 1166 _bus_dmamap_sync(bus_dma_tag_t dmat, bus_dmamap_t map, bus_dmasync_op_t op)
 1167 {
 1168         struct bounce_page *bpage;
 1169         struct sync_list *sl, *end;
 1170         /*
 1171          * If the buffer was from user space, it is possible that this is not
 1172          * the same vm map, especially on a POST operation.  It's not clear that
 1173          * dma on userland buffers can work at all right now, certainly not if a
 1174          * partial cacheline flush has to be handled.  To be safe, until we're
 1175          * able to test direct userland dma, panic on a map mismatch.
 1176          */
 1177         if ((bpage = STAILQ_FIRST(&map->bpages)) != NULL) {
 1178                 if (!pmap_dmap_iscurrent(map->pmap))
 1179                         panic("_bus_dmamap_sync: wrong user map for bounce sync.");
 1180                 /* Handle data bouncing. */
 1181                 CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x op 0x%x "
 1182                     "performing bounce", __func__, dmat, dmat->flags, op);
 1183 
 1184                 if (op & BUS_DMASYNC_PREWRITE) {
 1185                         while (bpage != NULL) {
 1186                                 if (bpage->datavaddr != 0)
 1187                                         bcopy((void *)bpage->datavaddr,
 1188                                               (void *)bpage->vaddr,
 1189                                               bpage->datacount);
 1190                                 else
 1191                                         physcopyout(bpage->dataaddr,
 1192                                               (void *)bpage->vaddr,
 1193                                               bpage->datacount);
 1194                                 cpu_dcache_wb_range((vm_offset_t)bpage->vaddr,
 1195                                         bpage->datacount);
 1196                                 l2cache_wb_range((vm_offset_t)bpage->vaddr,
 1197                                     (vm_offset_t)bpage->busaddr, 
 1198                                     bpage->datacount);
 1199                                 bpage = STAILQ_NEXT(bpage, links);
 1200                         }
 1201                         dmat->bounce_zone->total_bounced++;
 1202                 }
 1203 
 1204                 if (op & BUS_DMASYNC_POSTREAD) {
 1205                         while (bpage != NULL) {
 1206                                 vm_offset_t startv;
 1207                                 vm_paddr_t startp;
 1208                                 int len;
 1209 
 1210                                 startv = bpage->vaddr &~ arm_dcache_align_mask;
 1211                                 startp = bpage->busaddr &~ arm_dcache_align_mask;
 1212                                 len = bpage->datacount;
 1213                                 
 1214                                 if (startv != bpage->vaddr)
 1215                                         len += bpage->vaddr & arm_dcache_align_mask;
 1216                                 if (len & arm_dcache_align_mask) 
 1217                                         len = (len -
 1218                                             (len & arm_dcache_align_mask)) +
 1219                                             arm_dcache_align;
 1220                                 cpu_dcache_inv_range(startv, len);
 1221                                 l2cache_inv_range(startv, startp, len);
 1222                                 if (bpage->datavaddr != 0)
 1223                                         bcopy((void *)bpage->vaddr,
 1224                                               (void *)bpage->datavaddr,
 1225                                               bpage->datacount);
 1226                                 else
 1227                                         physcopyin((void *)bpage->vaddr,
 1228                                               bpage->dataaddr,
 1229                                               bpage->datacount);
 1230                                 bpage = STAILQ_NEXT(bpage, links);
 1231                         }
 1232                         dmat->bounce_zone->total_bounced++;
 1233                 }
 1234         }
 1235         if (map->flags & DMAMAP_COHERENT)
 1236                 return;
 1237 
 1238         if (map->sync_count != 0) {
 1239                 if (!pmap_dmap_iscurrent(map->pmap))
 1240                         panic("_bus_dmamap_sync: wrong user map for sync.");
 1241                 /* ARM caches are not self-snooping for dma */
 1242 
 1243                 sl = &map->slist[0];
 1244                 end = &map->slist[map->sync_count];
 1245                 CTR4(KTR_BUSDMA, "%s: tag %p tag flags 0x%x op 0x%x "
 1246                     "performing sync", __func__, dmat, dmat->flags, op);
 1247 
 1248                 switch (op) {
 1249                 case BUS_DMASYNC_PREWRITE:
 1250                         while (sl != end) {
 1251                             cpu_dcache_wb_range(sl->vaddr, sl->datacount);
 1252                             l2cache_wb_range(sl->vaddr, sl->busaddr,
 1253                                 sl->datacount);
 1254                             sl++;
 1255                         }
 1256                         break;
 1257 
 1258                 case BUS_DMASYNC_PREREAD:
 1259                         while (sl != end) {
 1260                                 cpu_dcache_inv_range(sl->vaddr, sl->datacount);
 1261                                 l2cache_inv_range(sl->vaddr, sl->busaddr, 
 1262                                     sl->datacount);
 1263                                 sl++;
 1264                         }
 1265                         break;
 1266 
 1267                 case BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD:
 1268                         while (sl != end) {
 1269                                 cpu_dcache_wbinv_range(sl->vaddr, sl->datacount);
 1270                                 l2cache_wbinv_range(sl->vaddr,
 1271                                     sl->busaddr, sl->datacount);
 1272                                 sl++;
 1273                         }
 1274                         break;
 1275 
 1276                 default:
 1277                         break;
 1278                 }
 1279         }
 1280 }
 1281 
 1282 static void
 1283 init_bounce_pages(void *dummy __unused)
 1284 {
 1285 
 1286         total_bpages = 0;
 1287         STAILQ_INIT(&bounce_zone_list);
 1288         STAILQ_INIT(&bounce_map_waitinglist);
 1289         STAILQ_INIT(&bounce_map_callbacklist);
 1290         mtx_init(&bounce_lock, "bounce pages lock", NULL, MTX_DEF);
 1291 }
 1292 SYSINIT(bpages, SI_SUB_LOCK, SI_ORDER_ANY, init_bounce_pages, NULL);
 1293 
 1294 static struct sysctl_ctx_list *
 1295 busdma_sysctl_tree(struct bounce_zone *bz)
 1296 {
 1297         return (&bz->sysctl_tree);
 1298 }
 1299 
 1300 static struct sysctl_oid *
 1301 busdma_sysctl_tree_top(struct bounce_zone *bz)
 1302 {
 1303         return (bz->sysctl_tree_top);
 1304 }
 1305 
 1306 static int
 1307 alloc_bounce_zone(bus_dma_tag_t dmat)
 1308 {
 1309         struct bounce_zone *bz;
 1310 
 1311         /* Check to see if we already have a suitable zone */
 1312         STAILQ_FOREACH(bz, &bounce_zone_list, links) {
 1313                 if ((dmat->alignment <= bz->alignment)
 1314                  && (dmat->lowaddr >= bz->lowaddr)) {
 1315                         dmat->bounce_zone = bz;
 1316                         return (0);
 1317                 }
 1318         }
 1319 
 1320         if ((bz = (struct bounce_zone *)malloc(sizeof(*bz), M_DEVBUF,
 1321             M_NOWAIT | M_ZERO)) == NULL)
 1322                 return (ENOMEM);
 1323 
 1324         STAILQ_INIT(&bz->bounce_page_list);
 1325         bz->free_bpages = 0;
 1326         bz->reserved_bpages = 0;
 1327         bz->active_bpages = 0;
 1328         bz->lowaddr = dmat->lowaddr;
 1329         bz->alignment = MAX(dmat->alignment, PAGE_SIZE);
 1330         bz->map_count = 0;
 1331         snprintf(bz->zoneid, 8, "zone%d", busdma_zonecount);
 1332         busdma_zonecount++;
 1333         snprintf(bz->lowaddrid, 18, "%#jx", (uintmax_t)bz->lowaddr);
 1334         STAILQ_INSERT_TAIL(&bounce_zone_list, bz, links);
 1335         dmat->bounce_zone = bz;
 1336 
 1337         sysctl_ctx_init(&bz->sysctl_tree);
 1338         bz->sysctl_tree_top = SYSCTL_ADD_NODE(&bz->sysctl_tree,
 1339             SYSCTL_STATIC_CHILDREN(_hw_busdma), OID_AUTO, bz->zoneid,
 1340             CTLFLAG_RD, 0, "");
 1341         if (bz->sysctl_tree_top == NULL) {
 1342                 sysctl_ctx_free(&bz->sysctl_tree);
 1343                 return (0);     /* XXX error code? */
 1344         }
 1345 
 1346         SYSCTL_ADD_INT(busdma_sysctl_tree(bz),
 1347             SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
 1348             "total_bpages", CTLFLAG_RD, &bz->total_bpages, 0,
 1349             "Total bounce pages");
 1350         SYSCTL_ADD_INT(busdma_sysctl_tree(bz),
 1351             SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
 1352             "free_bpages", CTLFLAG_RD, &bz->free_bpages, 0,
 1353             "Free bounce pages");
 1354         SYSCTL_ADD_INT(busdma_sysctl_tree(bz),
 1355             SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
 1356             "reserved_bpages", CTLFLAG_RD, &bz->reserved_bpages, 0,
 1357             "Reserved bounce pages");
 1358         SYSCTL_ADD_INT(busdma_sysctl_tree(bz),
 1359             SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
 1360             "active_bpages", CTLFLAG_RD, &bz->active_bpages, 0,
 1361             "Active bounce pages");
 1362         SYSCTL_ADD_INT(busdma_sysctl_tree(bz),
 1363             SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
 1364             "total_bounced", CTLFLAG_RD, &bz->total_bounced, 0,
 1365             "Total bounce requests");
 1366         SYSCTL_ADD_INT(busdma_sysctl_tree(bz),
 1367             SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
 1368             "total_deferred", CTLFLAG_RD, &bz->total_deferred, 0,
 1369             "Total bounce requests that were deferred");
 1370         SYSCTL_ADD_STRING(busdma_sysctl_tree(bz),
 1371             SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
 1372             "lowaddr", CTLFLAG_RD, bz->lowaddrid, 0, "");
 1373         SYSCTL_ADD_INT(busdma_sysctl_tree(bz),
 1374             SYSCTL_CHILDREN(busdma_sysctl_tree_top(bz)), OID_AUTO,
 1375             "alignment", CTLFLAG_RD, &bz->alignment, 0, "");
 1376 
 1377         return (0);
 1378 }
 1379 
 1380 static int
 1381 alloc_bounce_pages(bus_dma_tag_t dmat, u_int numpages)
 1382 {
 1383         struct bounce_zone *bz;
 1384         int count;
 1385 
 1386         bz = dmat->bounce_zone;
 1387         count = 0;
 1388         while (numpages > 0) {
 1389                 struct bounce_page *bpage;
 1390 
 1391                 bpage = (struct bounce_page *)malloc(sizeof(*bpage), M_DEVBUF,
 1392                                                      M_NOWAIT | M_ZERO);
 1393 
 1394                 if (bpage == NULL)
 1395                         break;
 1396                 bpage->vaddr = (vm_offset_t)contigmalloc(PAGE_SIZE, M_DEVBUF,
 1397                                                          M_NOWAIT, 0ul,
 1398                                                          bz->lowaddr,
 1399                                                          PAGE_SIZE,
 1400                                                          0);
 1401                 if (bpage->vaddr == 0) {
 1402                         free(bpage, M_DEVBUF);
 1403                         break;
 1404                 }
 1405                 bpage->busaddr = pmap_kextract(bpage->vaddr);
 1406                 mtx_lock(&bounce_lock);
 1407                 STAILQ_INSERT_TAIL(&bz->bounce_page_list, bpage, links);
 1408                 total_bpages++;
 1409                 bz->total_bpages++;
 1410                 bz->free_bpages++;
 1411                 mtx_unlock(&bounce_lock);
 1412                 count++;
 1413                 numpages--;
 1414         }
 1415         return (count);
 1416 }
 1417 
 1418 static int
 1419 reserve_bounce_pages(bus_dma_tag_t dmat, bus_dmamap_t map, int commit)
 1420 {
 1421         struct bounce_zone *bz;
 1422         int pages;
 1423 
 1424         mtx_assert(&bounce_lock, MA_OWNED);
 1425         bz = dmat->bounce_zone;
 1426         pages = MIN(bz->free_bpages, map->pagesneeded - map->pagesreserved);
 1427         if (commit == 0 && map->pagesneeded > (map->pagesreserved + pages))
 1428                 return (map->pagesneeded - (map->pagesreserved + pages));
 1429         bz->free_bpages -= pages;
 1430         bz->reserved_bpages += pages;
 1431         map->pagesreserved += pages;
 1432         pages = map->pagesneeded - map->pagesreserved;
 1433 
 1434         return (pages);
 1435 }
 1436 
 1437 static bus_addr_t
 1438 add_bounce_page(bus_dma_tag_t dmat, bus_dmamap_t map, vm_offset_t vaddr,
 1439                 bus_addr_t addr, bus_size_t size)
 1440 {
 1441         struct bounce_zone *bz;
 1442         struct bounce_page *bpage;
 1443 
 1444         KASSERT(dmat->bounce_zone != NULL, ("no bounce zone in dma tag"));
 1445         KASSERT(map != NULL,
 1446             ("add_bounce_page: bad map %p", map));
 1447 
 1448         bz = dmat->bounce_zone;
 1449         if (map->pagesneeded == 0)
 1450                 panic("add_bounce_page: map doesn't need any pages");
 1451         map->pagesneeded--;
 1452 
 1453         if (map->pagesreserved == 0)
 1454                 panic("add_bounce_page: map doesn't need any pages");
 1455         map->pagesreserved--;
 1456 
 1457         mtx_lock(&bounce_lock);
 1458         bpage = STAILQ_FIRST(&bz->bounce_page_list);
 1459         if (bpage == NULL)
 1460                 panic("add_bounce_page: free page list is empty");
 1461 
 1462         STAILQ_REMOVE_HEAD(&bz->bounce_page_list, links);
 1463         bz->reserved_bpages--;
 1464         bz->active_bpages++;
 1465         mtx_unlock(&bounce_lock);
 1466 
 1467         if (dmat->flags & BUS_DMA_KEEP_PG_OFFSET) {
 1468                 /* Page offset needs to be preserved. */
 1469                 bpage->vaddr |= vaddr & PAGE_MASK;
 1470                 bpage->busaddr |= vaddr & PAGE_MASK;
 1471         }
 1472         bpage->datavaddr = vaddr;
 1473         bpage->dataaddr = addr;
 1474         bpage->datacount = size;
 1475         STAILQ_INSERT_TAIL(&(map->bpages), bpage, links);
 1476         return (bpage->busaddr);
 1477 }
 1478 
 1479 static void
 1480 free_bounce_page(bus_dma_tag_t dmat, struct bounce_page *bpage)
 1481 {
 1482         struct bus_dmamap *map;
 1483         struct bounce_zone *bz;
 1484 
 1485         bz = dmat->bounce_zone;
 1486         bpage->datavaddr = 0;
 1487         bpage->datacount = 0;
 1488         if (dmat->flags & BUS_DMA_KEEP_PG_OFFSET) {
 1489                 /*
 1490                  * Reset the bounce page to start at offset 0.  Other uses
 1491                  * of this bounce page may need to store a full page of
 1492                  * data and/or assume it starts on a page boundary.
 1493                  */
 1494                 bpage->vaddr &= ~PAGE_MASK;
 1495                 bpage->busaddr &= ~PAGE_MASK;
 1496         }
 1497 
 1498         mtx_lock(&bounce_lock);
 1499         STAILQ_INSERT_HEAD(&bz->bounce_page_list, bpage, links);
 1500         bz->free_bpages++;
 1501         bz->active_bpages--;
 1502         if ((map = STAILQ_FIRST(&bounce_map_waitinglist)) != NULL) {
 1503                 if (reserve_bounce_pages(map->dmat, map, 1) == 0) {
 1504                         STAILQ_REMOVE_HEAD(&bounce_map_waitinglist, links);
 1505                         STAILQ_INSERT_TAIL(&bounce_map_callbacklist,
 1506                                            map, links);
 1507                         busdma_swi_pending = 1;
 1508                         bz->total_deferred++;
 1509                         swi_sched(vm_ih, 0);
 1510                 }
 1511         }
 1512         mtx_unlock(&bounce_lock);
 1513 }
 1514 
 1515 void
 1516 busdma_swi(void)
 1517 {
 1518         bus_dma_tag_t dmat;
 1519         struct bus_dmamap *map;
 1520 
 1521         mtx_lock(&bounce_lock);
 1522         while ((map = STAILQ_FIRST(&bounce_map_callbacklist)) != NULL) {
 1523                 STAILQ_REMOVE_HEAD(&bounce_map_callbacklist, links);
 1524                 mtx_unlock(&bounce_lock);
 1525                 dmat = map->dmat;
 1526                 (dmat->lockfunc)(dmat->lockfuncarg, BUS_DMA_LOCK);
 1527                 bus_dmamap_load_mem(map->dmat, map, &map->mem, map->callback,
 1528                                     map->callback_arg, BUS_DMA_WAITOK);
 1529                 (dmat->lockfunc)(dmat->lockfuncarg, BUS_DMA_UNLOCK);
 1530                 mtx_lock(&bounce_lock);
 1531         }
 1532         mtx_unlock(&bounce_lock);
 1533 }

Cache object: 75b8e8e67cef5daaed38e178c38e880b


[ 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.