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/i386/isa/isa_dma.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) 1991 The Regents of the University of California.
    3  * All rights reserved.
    4  *
    5  * This code is derived from software contributed to Berkeley by
    6  * William Jolitz.
    7  *
    8  * Redistribution and use in source and binary forms, with or without
    9  * modification, are permitted provided that the following conditions
   10  * are met:
   11  * 1. Redistributions of source code must retain the above copyright
   12  *    notice, this list of conditions and the following disclaimer.
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in the
   15  *    documentation and/or other materials provided with the distribution.
   16  * 3. All advertising materials mentioning features or use of this software
   17  *    must display the following acknowledgement:
   18  *      This product includes software developed by the University of
   19  *      California, Berkeley and its contributors.
   20  * 4. Neither the name of the University nor the names of its contributors
   21  *    may be used to endorse or promote products derived from this software
   22  *    without specific prior written permission.
   23  *
   24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   34  * SUCH DAMAGE.
   35  *
   36  *      from: @(#)isa.c 7.2 (Berkeley) 5/13/91
   37  * $FreeBSD$
   38  */
   39 
   40 /*
   41  * code to manage AT bus
   42  *
   43  * 92/08/18  Frank P. MacLachlan (fpm@crash.cts.com):
   44  * Fixed uninitialized variable problem and added code to deal
   45  * with DMA page boundaries in isa_dmarangecheck().  Fixed word
   46  * mode DMA count compution and reorganized DMA setup code in
   47  * isa_dmastart()
   48  */
   49 
   50 #include <sys/param.h>
   51 #include <sys/systm.h>
   52 #include <sys/malloc.h>
   53 #include <vm/vm.h>
   54 #include <vm/vm_param.h>
   55 #include <vm/pmap.h>
   56 #include <i386/isa/isa.h>
   57 #include <i386/isa/isa_dma.h>
   58 #include <i386/isa/ic/i8237.h>
   59 
   60 /*
   61 **  Register definitions for DMA controller 1 (channels 0..3):
   62 */
   63 #define DMA1_CHN(c)     (IO_DMA1 + 1*(2*(c)))   /* addr reg for channel c */
   64 #define DMA1_SMSK       (IO_DMA1 + 1*10)        /* single mask register */
   65 #define DMA1_MODE       (IO_DMA1 + 1*11)        /* mode register */
   66 #define DMA1_FFC        (IO_DMA1 + 1*12)        /* clear first/last FF */
   67 
   68 /*
   69 **  Register definitions for DMA controller 2 (channels 4..7):
   70 */
   71 #define DMA2_CHN(c)     (IO_DMA2 + 2*(2*(c)))   /* addr reg for channel c */
   72 #define DMA2_SMSK       (IO_DMA2 + 2*10)        /* single mask register */
   73 #define DMA2_MODE       (IO_DMA2 + 2*11)        /* mode register */
   74 #define DMA2_FFC        (IO_DMA2 + 2*12)        /* clear first/last FF */
   75 
   76 static int isa_dmarangecheck __P((caddr_t va, u_int length, int chan));
   77 
   78 static caddr_t  dma_bouncebuf[8];
   79 static u_int    dma_bouncebufsize[8];
   80 static u_int8_t dma_bounced = 0;
   81 static u_int8_t dma_busy = 0;           /* Used in isa_dmastart() */
   82 static u_int8_t dma_inuse = 0;          /* User for acquire/release */
   83 static u_int8_t dma_auto_mode = 0;
   84 
   85 #define VALID_DMA_MASK (7)
   86 
   87 /* high byte of address is stored in this port for i-th dma channel */
   88 static int dmapageport[8] = { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a };
   89 
   90 /*
   91  * Setup a DMA channel's bounce buffer.
   92  */
   93 void
   94 isa_dmainit(chan, bouncebufsize)
   95         int chan;
   96         u_int bouncebufsize;
   97 {
   98         void *buf;
   99 
  100 #ifdef DIAGNOSTIC
  101         if (chan & ~VALID_DMA_MASK)
  102                 panic("isa_dmainit: channel out of range");
  103 
  104         if (dma_bouncebuf[chan] != NULL)
  105                 panic("isa_dmainit: impossible request"); 
  106 #endif
  107 
  108         dma_bouncebufsize[chan] = bouncebufsize;
  109 
  110         /* Try malloc() first.  It works better if it works. */
  111         buf = malloc(bouncebufsize, M_DEVBUF, M_NOWAIT);
  112         if (buf != NULL) {
  113                 if (isa_dmarangecheck(buf, bouncebufsize, chan) == 0) {
  114                         dma_bouncebuf[chan] = buf;
  115                         return;
  116                 }
  117                 free(buf, M_DEVBUF);
  118         }
  119         buf = contigmalloc(bouncebufsize, M_DEVBUF, M_NOWAIT, 0ul, 0xfffffful,
  120                            1ul, chan & 4 ? 0x20000ul : 0x10000ul);
  121         if (buf == NULL)
  122                 printf("isa_dmainit(%d, %d) failed\n", chan, bouncebufsize);
  123         else
  124                 dma_bouncebuf[chan] = buf;
  125 }
  126 
  127 /*
  128  * Register a DMA channel's usage.  Usually called from a device driver
  129  * in open() or during its initialization.
  130  */
  131 int
  132 isa_dma_acquire(chan)
  133         int chan;
  134 {
  135 #ifdef DIAGNOSTIC
  136         if (chan & ~VALID_DMA_MASK)
  137                 panic("isa_dma_acquire: channel out of range");
  138 #endif
  139 
  140         if (dma_inuse & (1 << chan)) {
  141                 printf("isa_dma_acquire: channel %d already in use\n", chan);
  142                 return (EBUSY);
  143         }
  144         dma_inuse |= (1 << chan);
  145         dma_auto_mode &= ~(1 << chan);
  146 
  147         return (0);
  148 }
  149 
  150 /*
  151  * Unregister a DMA channel's usage.  Usually called from a device driver
  152  * during close() or during its shutdown.
  153  */
  154 void
  155 isa_dma_release(chan)
  156         int chan;
  157 {
  158 #ifdef DIAGNOSTIC
  159         if (chan & ~VALID_DMA_MASK)
  160                 panic("isa_dma_release: channel out of range");
  161 
  162         if ((dma_inuse & (1 << chan)) == 0)
  163                 printf("isa_dma_release: channel %d not in use\n", chan);
  164 #endif
  165 
  166         if (dma_busy & (1 << chan)) {
  167                 dma_busy &= ~(1 << chan);
  168                 /* 
  169                  * XXX We should also do "dma_bounced &= (1 << chan);"
  170                  * because we are acting on behalf of isa_dmadone() which
  171                  * was not called to end the last DMA operation.  This does
  172                  * not matter now, but it may in the future.
  173                  */
  174         }
  175 
  176         dma_inuse &= ~(1 << chan);
  177         dma_auto_mode &= ~(1 << chan);
  178 }
  179 
  180 /*
  181  * isa_dmacascade(): program 8237 DMA controller channel to accept
  182  * external dma control by a board.
  183  */
  184 void
  185 isa_dmacascade(chan)
  186         int chan;
  187 {
  188 #ifdef DIAGNOSTIC
  189         if (chan & ~VALID_DMA_MASK)
  190                 panic("isa_dmacascade: channel out of range");
  191 #endif
  192 
  193         /* set dma channel mode, and set dma channel mode */
  194         if ((chan & 4) == 0) {
  195                 outb(DMA1_MODE, DMA37MD_CASCADE | chan);
  196                 outb(DMA1_SMSK, chan);
  197         } else {
  198                 outb(DMA2_MODE, DMA37MD_CASCADE | (chan & 3));
  199                 outb(DMA2_SMSK, chan & 3);
  200         }
  201 }
  202 
  203 /*
  204  * isa_dmastart(): program 8237 DMA controller channel, avoid page alignment
  205  * problems by using a bounce buffer.
  206  */
  207 void
  208 isa_dmastart(int flags, caddr_t addr, u_int nbytes, int chan)
  209 {
  210         vm_paddr_t phys;
  211         int waport;
  212         caddr_t newaddr;
  213 
  214 #ifdef DIAGNOSTIC
  215         if (chan & ~VALID_DMA_MASK)
  216                 panic("isa_dmastart: channel out of range");
  217 
  218         if ((chan < 4 && nbytes > (1<<16))
  219             || (chan >= 4 && (nbytes > (1<<17) || (u_int)addr & 1)))
  220                 panic("isa_dmastart: impossible request");
  221 
  222         if ((dma_inuse & (1 << chan)) == 0)
  223                 printf("isa_dmastart: channel %d not acquired\n", chan);
  224 #endif
  225 
  226 #if 0
  227         /*
  228          * XXX This should be checked, but drivers like ad1848 only call
  229          * isa_dmastart() once because they use Auto DMA mode.  If we
  230          * leave this in, drivers that do this will print this continuously.
  231          */
  232         if (dma_busy & (1 << chan))
  233                 printf("isa_dmastart: channel %d busy\n", chan);
  234 #endif
  235 
  236         dma_busy |= (1 << chan);
  237 
  238         if (isa_dmarangecheck(addr, nbytes, chan)) {
  239                 if (dma_bouncebuf[chan] == NULL
  240                     || dma_bouncebufsize[chan] < nbytes)
  241                         panic("isa_dmastart: bad bounce buffer"); 
  242                 dma_bounced |= (1 << chan);
  243                 newaddr = dma_bouncebuf[chan];
  244 
  245                 /* copy bounce buffer on write */
  246                 if (!(flags & ISADMA_READ))
  247                         bcopy(addr, newaddr, nbytes);
  248                 addr = newaddr;
  249         }
  250 
  251         /* translate to physical */
  252         phys = pmap_extract(pmap_kernel(), (vm_offset_t)addr);
  253 
  254         if (flags & ISADMA_RAW) {
  255             dma_auto_mode |= (1 << chan);
  256         } else { 
  257             dma_auto_mode &= ~(1 << chan);
  258         }
  259 
  260         if ((chan & 4) == 0) {
  261                 /*
  262                  * Program one of DMA channels 0..3.  These are
  263                  * byte mode channels.
  264                  */
  265                 /* set dma channel mode, and reset address ff */
  266 
  267                 /* If ISADMA_RAW flag is set, then use autoinitialise mode */
  268                 if (flags & ISADMA_RAW) {
  269                   if (flags & ISADMA_READ)
  270                         outb(DMA1_MODE, DMA37MD_AUTO|DMA37MD_WRITE|chan);
  271                   else
  272                         outb(DMA1_MODE, DMA37MD_AUTO|DMA37MD_READ|chan);
  273                 }
  274                 else
  275                 if (flags & ISADMA_READ)
  276                         outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|chan);
  277                 else
  278                         outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_READ|chan);
  279                 outb(DMA1_FFC, 0);
  280 
  281                 /* send start address */
  282                 waport =  DMA1_CHN(chan);
  283                 outb(waport, phys);
  284                 outb(waport, phys>>8);
  285                 outb(dmapageport[chan], phys>>16);
  286 
  287                 /* send count */
  288                 outb(waport + 1, --nbytes);
  289                 outb(waport + 1, nbytes>>8);
  290 
  291                 /* unmask channel */
  292                 outb(DMA1_SMSK, chan);
  293         } else {
  294                 /*
  295                  * Program one of DMA channels 4..7.  These are
  296                  * word mode channels.
  297                  */
  298                 /* set dma channel mode, and reset address ff */
  299 
  300                 /* If ISADMA_RAW flag is set, then use autoinitialise mode */
  301                 if (flags & ISADMA_RAW) {
  302                   if (flags & ISADMA_READ)
  303                         outb(DMA2_MODE, DMA37MD_AUTO|DMA37MD_WRITE|(chan&3));
  304                   else
  305                         outb(DMA2_MODE, DMA37MD_AUTO|DMA37MD_READ|(chan&3));
  306                 }
  307                 else
  308                 if (flags & ISADMA_READ)
  309                         outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|(chan&3));
  310                 else
  311                         outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_READ|(chan&3));
  312                 outb(DMA2_FFC, 0);
  313 
  314                 /* send start address */
  315                 waport = DMA2_CHN(chan - 4);
  316                 outb(waport, phys>>1);
  317                 outb(waport, phys>>9);
  318                 outb(dmapageport[chan], phys>>16);
  319 
  320                 /* send count */
  321                 nbytes >>= 1;
  322                 outb(waport + 2, --nbytes);
  323                 outb(waport + 2, nbytes>>8);
  324 
  325                 /* unmask channel */
  326                 outb(DMA2_SMSK, chan & 3);
  327         }
  328 }
  329 
  330 void
  331 isa_dmadone(int flags, caddr_t addr, int nbytes, int chan)
  332 {  
  333 #ifdef DIAGNOSTIC
  334         if (chan & ~VALID_DMA_MASK)
  335                 panic("isa_dmadone: channel out of range");
  336 
  337         if ((dma_inuse & (1 << chan)) == 0)
  338                 printf("isa_dmadone: channel %d not acquired\n", chan);
  339 #endif
  340 
  341         if (((dma_busy & (1 << chan)) == 0) && 
  342             (dma_auto_mode & (1 << chan)) == 0 )
  343                 printf("isa_dmadone: channel %d not busy\n", chan);
  344 
  345         if ((dma_auto_mode & (1 << chan)) == 0)
  346                 outb(chan & 4 ? DMA2_SMSK : DMA1_SMSK, (chan & 3) | 4);
  347 
  348         if (dma_bounced & (1 << chan)) {
  349                 /* copy bounce buffer on read */
  350                 if (flags & ISADMA_READ)
  351                         bcopy(dma_bouncebuf[chan], addr, nbytes);
  352 
  353                 dma_bounced &= ~(1 << chan);
  354         }
  355         dma_busy &= ~(1 << chan);
  356 }
  357 
  358 /*
  359  * Check for problems with the address range of a DMA transfer
  360  * (non-contiguous physical pages, outside of bus address space,
  361  * crossing DMA page boundaries).
  362  * Return true if special handling needed.
  363  */
  364 
  365 static int
  366 isa_dmarangecheck(caddr_t va, u_int length, int chan)
  367 {
  368         vm_paddr_t phys, priorpage = 0;
  369         vm_offset_t endva;
  370         u_int dma_pgmsk = (chan & 4) ?  ~(128*1024-1) : ~(64*1024-1);
  371 
  372         endva = (vm_offset_t)round_page((vm_offset_t)va + length);
  373         for (; va < (caddr_t) endva ; va += PAGE_SIZE) {
  374                 phys = trunc_page(pmap_extract(pmap_kernel(), (vm_offset_t)va));
  375 #define ISARAM_END      RAM_END
  376                 if (phys == 0)
  377                         panic("isa_dmacheck: no physical page present");
  378                 if (phys >= ISARAM_END)
  379                         return (1);
  380                 if (priorpage) {
  381                         if (priorpage + PAGE_SIZE != phys)
  382                                 return (1);
  383                         /* check if crossing a DMA page boundary */
  384                         if (((u_int)priorpage ^ (u_int)phys) & dma_pgmsk)
  385                                 return (1);
  386                 }
  387                 priorpage = phys;
  388         }
  389         return (0);
  390 }
  391 
  392 /*
  393  * Query the progress of a transfer on a DMA channel.
  394  *
  395  * To avoid having to interrupt a transfer in progress, we sample
  396  * each of the high and low databytes twice, and apply the following
  397  * logic to determine the correct count.
  398  *
  399  * Reads are performed with interrupts disabled, thus it is to be
  400  * expected that the time between reads is very small.  At most
  401  * one rollover in the low count byte can be expected within the
  402  * four reads that are performed.
  403  *
  404  * There are three gaps in which a rollover can occur :
  405  *
  406  * - read low1
  407  *              gap1
  408  * - read high1
  409  *              gap2
  410  * - read low2
  411  *              gap3
  412  * - read high2
  413  *
  414  * If a rollover occurs in gap1 or gap2, the low2 value will be
  415  * greater than the low1 value.  In this case, low2 and high2 are a
  416  * corresponding pair. 
  417  *
  418  * In any other case, low1 and high1 can be considered to be correct.
  419  *
  420  * The function returns the number of bytes remaining in the transfer,
  421  * or -1 if the channel requested is not active.
  422  *
  423  */
  424 int
  425 isa_dmastatus(int chan)
  426 {
  427         u_long  cnt = 0;
  428         int     ffport, waport;
  429         u_long  low1, high1, low2, high2;
  430 
  431         /* channel active? */
  432         if ((dma_inuse & (1 << chan)) == 0) {
  433                 printf("isa_dmastatus: channel %d not active\n", chan);
  434                 return(-1);
  435         }
  436         /* channel busy? */
  437 
  438         if (((dma_busy & (1 << chan)) == 0) &&
  439             (dma_auto_mode & (1 << chan)) == 0 ) {
  440             printf("chan %d not busy\n", chan);
  441             return -2 ;
  442         }       
  443         if (chan < 4) {                 /* low DMA controller */
  444                 ffport = DMA1_FFC;
  445                 waport = DMA1_CHN(chan) + 1;
  446         } else {                        /* high DMA controller */
  447                 ffport = DMA2_FFC;
  448                 waport = DMA2_CHN(chan - 4) + 2;
  449         }
  450 
  451         disable_intr();                 /* no interrupts Mr Jones! */
  452         outb(ffport, 0);                /* clear register LSB flipflop */
  453         low1 = inb(waport);
  454         high1 = inb(waport);
  455         outb(ffport, 0);                /* clear again */
  456         low2 = inb(waport);
  457         high2 = inb(waport);
  458         enable_intr();                  /* enable interrupts again */
  459 
  460         /* 
  461          * Now decide if a wrap has tried to skew our results.
  462          * Note that after TC, the count will read 0xffff, while we want 
  463          * to return zero, so we add and then mask to compensate.
  464          */
  465         if (low1 >= low2) {
  466                 cnt = (low1 + (high1 << 8) + 1) & 0xffff;
  467         } else {
  468                 cnt = (low2 + (high2 << 8) + 1) & 0xffff;
  469         }
  470 
  471         if (chan >= 4)                  /* high channels move words */
  472                 cnt *= 2;
  473         return(cnt);
  474 }
  475 
  476 /*
  477  * Stop a DMA transfer currently in progress.
  478  */
  479 int
  480 isa_dmastop(int chan) 
  481 {
  482         if ((dma_inuse & (1 << chan)) == 0)
  483                 printf("isa_dmastop: channel %d not acquired\n", chan);  
  484 
  485         if (((dma_busy & (1 << chan)) == 0) &&
  486             ((dma_auto_mode & (1 << chan)) == 0)) {
  487                 printf("chan %d not busy\n", chan);
  488                 return -2 ;
  489         }
  490     
  491         if ((chan & 4) == 0) {
  492                 outb(DMA1_SMSK, (chan & 3) | 4 /* disable mask */);
  493         } else {
  494                 outb(DMA2_SMSK, (chan & 3) | 4 /* disable mask */);
  495         }
  496         return(isa_dmastatus(chan));
  497 }

Cache object: 12d5c97a2d44410fced9bff866d0671e


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