patch from Dan Christian.
authorDavid Schleef <ds@schleef.org>
Thu, 29 Aug 2002 00:01:11 +0000 (00:01 +0000)
committerDavid Schleef <ds@schleef.org>
Thu, 29 Aug 2002 00:01:11 +0000 (00:01 +0000)
 - DMA support (experimental)
 - Fix timing limits to properly handle single channel and multi channel cases
 - Stop using the About counter, it really wasn't needed and caused problems
 - The usual clean ups and doc improvements

comedi/drivers/rtd520.c
comedi/drivers/rtd520.h

index e1c9e555d222b702e07ec00005895d2765c26298..35eae3f48c8526284ada75c9428b22560129a27b 100644 (file)
@@ -25,7 +25,7 @@ Description: Real Time Devices PCI4520/DM7520
 Author: Dan Christian
 Devices: [Real Time Devices] DM7520HR-1 (DM7520), DM7520HR-8 (DM7520-8),
   PCI4520 (PCI4520), PCI4520-8 (PCI4520-8)
-Status: works.  Only tested on DM7520-8.
+Status: Works.  Only tested on DM7520-8.  Not SMP safe.
 
 Configuration options:
   [0] - PCI bus of device (optional)
@@ -62,7 +62,7 @@ Configuration options:
     PCI chip: http://www.plxtech.com/products/toolbox/9080.htm
 
     Notes:
-    This board is (almost) completely memory mapped.
+    This board is memory mapped.  There is some IO stuff, but it isn't needed.
 
     I use a pretty loose naming style within the driver (rtd_blah).
     All externally visible names should be rtd520_blah.
@@ -71,21 +71,27 @@ Configuration options:
 
     This board is somewhat related to the RTD PCI4400 board.
 
-    I borrowed heavily from the ni_mio_common, ni_atmio16d, and das1800,
-    since they have the best documented code.
+    I borrowed heavily from the ni_mio_common, ni_atmio16d, mite, and
+    das1800, since they have the best documented code.  Driver
+    cb_pcidas64.c uses the same DMA controller.
+
+    As far as I can tell, the About interrupt doesnt work if Sample is
+    also enabled.  It turns out that About really isn't needed, since
+    we always count down samples read.
+
+    There was some timer/counter code, but it didn't follow the right API.
 
 */
 
 /*
   driver status:
 
-  Analog in supports instruction and command mode.  I can do 200Khz
-  mutli-channel sampling on a 400Mhz K6-2 with 70% idle.  The DMA isn't
-  used yet.
-  
-  Digital IO and analog out support instruction mode.
+  Analog-In supports instruction and command mode.  
 
-  There was some timer/counter code, but it didn't follow the right API.
+  With DMA, 1.15Mhz with 70% idle on a 400Mhz K6-2 (single channel).
+  Can do 200Khz multi-channel sampling with 70% idle on a 400Mhz K6-2 (no DMA).
+  
+  Digital-IO and Analog-Out support instruction mode.
 
 */
 
@@ -104,18 +110,27 @@ Configuration options:
 #include <linux/init.h>
 
 #include <asm/io.h>
+#include <asm/dma.h>
 #include <linux/comedidev.h>
 
 
 /*======================================================================
-  Drivier specific stuff (tunable)
+  Driver specific stuff (tunable)
 ======================================================================*/
-/* Target period for periodic transfers */
-/* if this is too low, efficiency is poor */
+/* Enable this to test the new DMA support. You may get hard lock ups */
+#define USE_DMA
+
+/* We really only need 2 buffers.  More than that means being much
+   smarter about knowing which ones are full. */
+#define DMA_CHAIN_COUNT 2      /* max DMA segments/buffers in a ring (min 2)*/
+
+/* Target period for periodic transfers.  This sets the user read latency. */
+/* Note: There are certain rates where we give this up and transfer 1/2 FIFO */
+/* If this is too low, efficiency is poor */
 #define TRANS_TARGET_PERIOD 10000000   /* 10 ms (in nanoseconds) */
 
+/* Set a practical limit on how long a list to support (affects memory use) */
 /* The board support a channel list up to the FIFO length (1K or 8K) */
-/* Raising the MAX_CHANLIST uses more memory per board */
 #define RTD_MAX_CHANLIST       128     /* max channel list that we allow */
 
 /* tuning for ai/ao instruction done polling */
@@ -123,11 +138,13 @@ Configuration options:
 #define WAIT_QUIETLY   /* as nothing, spin on done bit */
 #define RTD_ADC_TIMEOUT        66000           /* 2 msec at 33mhz bus rate */
 #define RTD_DAC_TIMEOUT        66000
+#define RTD_DMA_TIMEOUT        33000           /* 1 msec */
 #else
 /* by delaying, power and electrical noise are reduced somewhat */
 #define WAIT_QUIETLY   udelay (1)
 #define RTD_ADC_TIMEOUT        2000            /* in usec */
 #define RTD_DAC_TIMEOUT        2000            /* in usec */
+#define RTD_DMA_TIMEOUT        1000            /* in usec */
 #endif
 
 /*======================================================================
@@ -151,10 +168,33 @@ Configuration options:
 #define RTD_CLOCK_RATE 8000000         /* 8Mhz onboard clock */
 #define RTD_CLOCK_BASE 125             /* clock period in ns */
 
-#define RTD_MAX_SPEED  1600            /* in nanoseconds */
-#define RTD_MIN_SPEED  1000000000      /* in nanoseconds ??? */
+/* Note: these speed are slower than the spec, but fit the counter resolution*/
+#define RTD_MAX_SPEED  1625            /* when sampling, in nanoseconds */
+/* max speed if we don't have to wait for settling */
+#define RTD_MAX_SPEED_1        875             /* if single channel, in nanoseconds */
+
+#define RTD_MIN_SPEED  2097151875      /* (24bit counter) in nanoseconds */
+/* min speed when only 1 channel (no burst counter) */
+#define RTD_MIN_SPEED_1        5000000         /* 200Hz, in nanoseconds */
+
 
 #include "rtd520.h"
+#include "plx9080.h"
+
+/* Setup continuous ring of 1/2 FIFO transfers.  See RTD manual p91 */
+#define DMA_MODE_BITS (\
+                      PLX_LOCAL_BUS_16_WIDE_BITS \
+                      | PLX_DMA_EN_READYIN_BIT \
+                      | PLX_DMA_LOCAL_BURST_EN_BIT \
+                      | PLX_EN_CHAIN_BIT \
+                      | PLX_DMA_INTR_PCI_BIT \
+                      | PLX_LOCAL_ADDR_CONST_BIT \
+                      | PLX_DEMAND_MODE_BIT)
+
+#define DMA_TRANSFER_BITS (\
+/* descriptors in PCI memory*/         PLX_DESC_IN_PCI_BIT \
+/* interrupt at end of block */ | PLX_INTR_TERM_COUNT \
+/* from board to PCI */                | PLX_XFER_LOCAL_TO_PCI) 
 
 /*======================================================================
   Comedi specific stuff
@@ -308,9 +348,7 @@ typedef struct{
 
     unsigned long      intCount;       /* interrupt count */
     long               aiCount;        /* total transfer size (samples) */
-    unsigned long      aiExtraInt;     /* ints but no data */
     int                        transCount;     /* # to tranfer data. 0->1/2FIFO*/
-    int                        aboutWrap;      /* number of about overflows needed */
     int                        flags;          /* flag event modes */
 
                                        /* PCI device info */
@@ -332,11 +370,24 @@ typedef struct{
     u16                intClearMask;           /* interrupt clear mask */
     u8         utcCtrl[4];             /* crtl mode for 3 utc + read back */
     u8         dioStatus;              /* could be read back (dio0Ctrl) */
-
+#ifdef USE_DMA
+    /* Always DMA 1/2 FIFO.  Buffer (dmaBuff?) is (at least) twice that size.
+       After transferring, interrupt processes 1/2 FIFO and passes to comedi */
+    s16                dma0Offset;             /* current processing offset (0, 1/2)*/
+    uint16_t *dma0Buff[DMA_CHAIN_COUNT];       /* DMA buffers (for ADC) */
+    dma_addr_t dma0BuffPhysAddr[DMA_CHAIN_COUNT]; /* physical addresses */
+    struct plx_dma_desc *dma0Chain;  /* DMA descriptor ring for dmaBuff*/
+    dma_addr_t dma0ChainPhysAddr; /* physical addresses */
+    /* shadow registers */
+    u8         dma0Control;
+    u8         dma1Control;
+#endif /* USE_DMA */
 } rtdPrivate;
 
 /* bit defines for "flags" */
 #define SEND_EOS       0x01            /* send End Of Scan events */
+#define DMA0_ACTIVE    0x02            /* DMA0 is active */
+#define DMA1_ACTIVE    0x04            /* DMA1 is active */
 
 /* Macros for accessing channel list bit array */
 #define CHAN_ARRAY_TEST(array,index) \
@@ -529,13 +580,6 @@ typedef struct{
     writel (v,devpriv->las0+((n <= 0) ? LAS0_UOUT0_SELECT : LAS0_UOUT1_SELECT))
 
 
-/* PLX9080 interrupt mask and status */
-#define RtdPLXInterruptRead(dev) \
-    readl (devpriv->lcfg+LCFG_ITCSR)
-#define RtdPLXInterruptWrite(dev,v) \
-    writel (v, devpriv->lcfg+LCFG_ITCSR)
-
-
 /* Digital IO */
 #define RtdDio0Read(dev) \
     (readw (devpriv->las0+LAS0_DIO0) & 0xff)
@@ -581,6 +625,85 @@ typedef struct{
 #define RtdDacClearFifo(dev,n) \
     writel (0, devpriv->las0+(((n) == 0) ? LAS0_DAC1_RESET : LAS0_DAC2_RESET))
 
+/* Set source for DMA 0 (write only, shadow?) */
+#define RtdDma0Source(dev,n) \
+    writel ((n) & 0xf, devpriv->las0+LAS0_DMA0_SRC)
+
+/* Set source for DMA 1 (write only, shadow?) */
+#define RtdDma1Source(dev,n) \
+    writel ((n) & 0xf, devpriv->las0+LAS0_DMA1_SRC)
+
+/* Reset board state for DMA 0 */
+#define RtdDma0Reset(dev) \
+    writel (0, devpriv->las0+LAS0_DMA0_RESET)
+
+/* Reset board state for DMA 1 */
+#define RtdDma1Reset(dev) \
+    writel (0, devpriv->las0+LAS0_DMA1_SRC)
+
+
+/* PLX9080 interrupt mask and status */
+#define RtdPlxInterruptRead(dev) \
+    readl (devpriv->lcfg+LCFG_ITCSR)
+#define RtdPlxInterruptWrite(dev,v) \
+    writel (v, devpriv->lcfg+LCFG_ITCSR)
+
+/* Set  mode for DMA 0 */
+#define RtdDma0Mode(dev,m) \
+    writel ((m), devpriv->lcfg+LCFG_DMAMODE0)
+
+/* Set PCI address for DMA 0 */
+#define RtdDma0PciAddr(dev,a) \
+    writel ((a), devpriv->lcfg+LCFG_DMAPADR0)
+
+/* Set local address for DMA 0 */
+#define RtdDma0LocalAddr(dev,a) \
+    writel ((a), devpriv->lcfg+LCFG_DMALADR0)
+
+/* Set byte count for DMA 0 */
+#define RtdDma0Count(dev,c) \
+    writel ((c), devpriv->lcfg+LCFG_DMASIZ0)
+
+/* Set next descriptor for DMA 0 */
+#define RtdDma0Next(dev,a) \
+    writel ((a), devpriv->lcfg+LCFG_DMADPR0)
+
+/* Set  mode for DMA 1 */
+#define RtdDma1Mode(dev,m) \
+    writel ((m), devpriv->lcfg+LCFG_DMAMODE1)
+
+/* Set PCI address for DMA 1 */
+#define RtdDma1PciAddr(dev,a) \
+    writel ((a), devpriv->lcfg+LCFG_DMAADR1)
+
+/* Set local address for DMA 1 */
+#define RtdDma1LocalAddr(dev,a) \
+    writel ((a), devpriv->lcfg+LCFG_DMALADR1)
+
+/* Set byte count for DMA 1 */
+#define RtdDma1Count(dev,c) \
+    writel ((c), devpriv->lcfg+LCFG_DMASIZ1)
+
+/* Set next descriptor for DMA 1 */
+#define RtdDma1Next(dev,a) \
+    writel ((a), devpriv->lcfg+LCFG_DMADPR1)
+
+/* Set control for DMA 0 (write only, shadow?) */
+#define RtdDma0Control(dev,n) \
+    writeb (devpriv->dma0Control = (n), devpriv->lcfg+LCFG_DMACSR0)
+
+/* Get status for DMA 0 */
+#define RtdDma0Status(dev) \
+    readb (devpriv->lcfg+LCFG_DMACSR0)
+
+/* Set control for DMA 1 (write only, shadow?) */
+#define RtdDma1Control(dev,n) \
+    writeb (devpriv->dma1Control = (n), devpriv->lcfg+LCFG_DMACSR1)
+
+/* Get status for DMA 1 */
+#define RtdDma1Status(dev) \
+    readb (devpriv->lcfg+LCFG_DMACSR1)
+
 /*
  * The comedi_driver structure tells the Comedi core module
  * which functions to call to configure/deconfigure (attach/detach)
@@ -635,9 +758,18 @@ static int rtd_attach (
     unsigned long physLas0;            /* configuation */
     unsigned long physLas1;            /* data area */
     unsigned long physLcfg;            /* PLX9080 */
+#ifdef USE_DMA
+    int index;
+#endif
 
     printk ("comedi%d: rtd520 attaching.\n", dev->minor);
 
+#if defined (CONFIG_COMEDI_DEBUG) && defined (USE_DMA)
+    /* This is registered as a module parameter.  Can be set at run time? */
+    if (0 == comedi_debug)             /* enable debug printks */
+       comedi_debug = 1;
+#endif
+
     /*
      * Allocate the private structure area.  alloc_private() is a
      * convenient macro defined in comedidev.h.
@@ -698,8 +830,10 @@ static int rtd_attach (
     devpriv->las1 = ioremap(physLas1, LAS1_PCISIZE);
     devpriv->lcfg = ioremap(physLcfg, LCFG_PCISIZE);
 
+    /* It is critical that the FIFO length matches the board.  Otherwise,
+       data transfers and DMA will either read 0s or overwrite memory! */
     printk ("%s: ( fifoLength = %d )" , dev->board_name, thisboard->fifoLen);
-    /*printk ("%s: LAS0=%lx, LAS1=%lx, CFG=%lx.\n", dev->board_name,
+    /*DPRINTK ("%s: LAS0=%lx, LAS1=%lx, CFG=%lx.\n", dev->board_name,
       physLas0, physLas1, physLcfg);*/
 
     /*
@@ -708,8 +842,9 @@ static int rtd_attach (
      * n_subdevices being set correctly.
      */
     dev->n_subdevices=4;
-    if (alloc_subdevices(dev)<0)
+    if (alloc_subdevices(dev)<0) {
        return -ENOMEM;
+    }
 
     s=dev->subdevices+0;
     dev->read_subdev=s;
@@ -762,12 +897,72 @@ static int rtd_attach (
     dev->irq = devpriv->pci_dev->irq;
     if(dev->irq>0){
        if((ret=comedi_request_irq (dev->irq, rtd_interrupt,
-                                   SA_SHIRQ, "rtd520", dev))<0)
+                                   SA_SHIRQ, "rtd520", dev)) < 0) {
+           printk("Could not get interrupt! (%d)\n", dev->irq);
            return ret;
-       printk("( irq = %d )\n", dev->irq);
+       }
+       printk("( irq = %d )", dev->irq);
     } else {
        printk("( NO IRQ )");
     }
+
+#ifdef USE_DMA
+    printk("( DMA buffers = %d )\n", DMA_CHAIN_COUNT);
+    /* The PLX9080 has 2 DMA controllers, but there could be 4 sources:
+       ADC, digital, DAC1, and DAC2.  Since only the ADC supports cmd mode
+       right now, this isn't an issue (yet) */
+    devpriv->dma0Offset = 0;
+
+    for(index = 0; index < DMA_CHAIN_COUNT; index++) {
+       devpriv->dma0Buff[index] =
+           pci_alloc_consistent(devpriv->pci_dev,
+                                sizeof (u16) * thisboard->fifoLen/2,
+                                &devpriv->dma0BuffPhysAddr[index]);
+       if (devpriv->dma0Buff[index] == NULL) {
+           ret = -ENOMEM;
+           goto rtd_attach_die_error;
+       }
+       DPRINTK ("buff[%d] @ %p virtual, %x PCI\n",
+                index,
+                devpriv->dma0Buff[index], devpriv->dma0BuffPhysAddr[index]);
+    }
+
+    /* setup DMA descriptor ring (use cpu_to_le32 for byte ordering?) */
+    devpriv->dma0Chain =
+       pci_alloc_consistent (devpriv->pci_dev,
+                             sizeof(struct plx_dma_desc) * DMA_CHAIN_COUNT,
+                             &devpriv->dma0ChainPhysAddr);
+    for (index = 0; index < DMA_CHAIN_COUNT; index++) {
+       devpriv->dma0Chain[index].pci_start_addr =
+           devpriv->dma0BuffPhysAddr[index];
+       devpriv->dma0Chain[index].local_start_addr =
+           DMALADDR_ADC;
+       devpriv->dma0Chain[index].transfer_size =
+           sizeof (u16) * thisboard->fifoLen/2;
+       devpriv->dma0Chain[index].next =
+           (devpriv->dma0ChainPhysAddr + ((index + 1) % (DMA_CHAIN_COUNT))
+            * sizeof(devpriv->dma0Chain[0]))
+           | DMA_TRANSFER_BITS;
+       DPRINTK ("ring[%d] @%lx PCI: %x, local: %x, N: 0x%x, next: %x\n",
+                index,
+                ((long)devpriv->dma0ChainPhysAddr
+                 + (index * sizeof(devpriv->dma0Chain[0]))),
+                devpriv->dma0Chain[index].pci_start_addr,
+                devpriv->dma0Chain[index].local_start_addr,
+                devpriv->dma0Chain[index].transfer_size,
+                devpriv->dma0Chain[index].next);
+    }
+
+    if (devpriv->dma0Chain == NULL) {
+       ret = -ENOMEM;
+       goto rtd_attach_die_error;
+    }
+
+    RtdDma0Mode (dev, DMA_MODE_BITS);
+    RtdDma0Source (dev, DMAS_ADFIFO_HALF_FULL); /* set DMA trigger source */
+#else /* USE_DMA */
+    printk("\n");                      /* end setup line */
+#endif /* USE_DMA */
        
                                        /* initialize board, per RTD spec */
                                        /* also, initialize shadow registers */
@@ -788,15 +983,57 @@ static int rtd_attach (
     RtdUtcCtrlPut (dev, 3, 0);         /* safe state, set shadow */
     /* TODO: set user out source ??? */
 
-    if (dev->irq) {                    /* enable interrupt controller */
-       RtdPLXInterruptWrite (dev,
-                             RtdPLXInterruptRead (dev) | (0x0800));
+    if (dev->irq) {                    /* enable plx9080 interrupts */
+       RtdPlxInterruptWrite (dev, ICS_PIE | ICS_PLIE);
     }
 
 
     printk("comedi%d: rtd520 driver attached.\n", dev->minor);
 
     return 1;
+
+    /* hit an error, clean up memory and return ret */
+rtd_attach_die_error:
+#ifdef USE_DMA
+    for(index = 0; index < DMA_CHAIN_COUNT; index++) {
+       if (NULL != devpriv->dma0Buff[index]) { /* free buffer memory*/
+           pci_free_consistent (devpriv->pci_dev,
+                                sizeof (u16) * thisboard->fifoLen/2,
+                                devpriv->dma0Buff[index],
+                                devpriv->dma0BuffPhysAddr[index]);
+           devpriv->dma0Buff[index] = NULL;
+       }
+    }
+    if (NULL != devpriv->dma0Chain) {
+       pci_free_consistent (devpriv->pci_dev,
+                            sizeof(struct plx_dma_desc)
+                            * DMA_CHAIN_COUNT,
+                            devpriv->dma0Chain,
+                            devpriv->dma0ChainPhysAddr);
+       devpriv->dma0Chain = NULL;
+    }
+#endif /* USE_DMA */
+    /* subdevices and priv are freed by the core */
+    if (dev->irq) {
+       /* disable interrupt controller */
+       RtdPlxInterruptWrite (
+           dev,
+           RtdPlxInterruptRead (dev)
+           & ~(ICS_PLIE | ICS_DMA0_E | ICS_DMA1_E));
+       comedi_free_irq (dev->irq, dev);
+    }
+
+    /* release all regions that were allocated */
+    if (devpriv->las0) {
+       iounmap (devpriv->las0);
+    }
+    if (devpriv->las1) {
+       iounmap (devpriv->las1);
+    }
+    if (devpriv->lcfg) {
+       iounmap (devpriv->lcfg);
+    }
+    return ret;
 }
 
 /*
@@ -810,26 +1047,55 @@ static int rtd_attach (
 static int rtd_detach (
     comedi_device *dev)
 {
-    DPRINTK("comedi%d: rtd520: removing (%ld ints, %ld extra ai)\n(int status 0x%x, overrun status 0x%x, fifo status 0x%x)...\n",
-          dev->minor, devpriv->intCount, devpriv->aiExtraInt,
+#ifdef USE_DMA
+    int index;
+#endif
+
+    DPRINTK("comedi%d: rtd520: removing (%ld ints)\n(int status 0x%x, overrun status 0x%x, fifo status 0x%x)...\n",
+          dev->minor, devpriv->intCount, 
           0xffff & RtdInterruptStatus (dev),
           0xffff & RtdInterruptOverrunStatus (dev),
           (0xffff & RtdFifoStatus (dev)) ^ 0x6666);
 
     if (devpriv) {
        /* Shut down any board ops by resetting it */
+#ifdef USE_DMA
+       RtdDma0Control (dev, 0);        /* disable DMA */
+       RtdDma1Control (dev, 0);        /* disable DMA */
+       RtdPlxInterruptWrite (dev, ICS_PIE | ICS_PLIE);
+#endif /* USE_DMA */
        RtdResetBoard (dev);
        RtdInterruptMask (dev, 0);
        RtdInterruptClearMask (dev,~0);
        RtdInterruptClear(dev);         /* clears bits set by mask */
 
+#ifdef USE_DMA
        /* release DMA */
+       for(index = 0; index < DMA_CHAIN_COUNT; index++) {
+           if (NULL != devpriv->dma0Buff[index]) {
+               pci_free_consistent (devpriv->pci_dev,
+                                    sizeof (u16) * thisboard->fifoLen/2,
+                                    devpriv->dma0Buff[index],
+                                    devpriv->dma0BuffPhysAddr[index]);
+               devpriv->dma0Buff[index] = NULL;
+           }
+       }
+       if (NULL != devpriv->dma0Chain) {
+           pci_free_consistent (devpriv->pci_dev,
+                                sizeof(struct plx_dma_desc) * DMA_CHAIN_COUNT,
+                                devpriv->dma0Chain,
+                                devpriv->dma0ChainPhysAddr);
+           devpriv->dma0Chain = NULL;
+       }
+#endif /* USE_DMA */
 
        /* release IRQ */
        if (dev->irq) {
            /* disable interrupt controller */
-           RtdPLXInterruptWrite (dev,
-                                 RtdPLXInterruptRead (dev) & ~(0x0800));
+           RtdPlxInterruptWrite (
+               dev,
+               RtdPlxInterruptRead (dev)
+               & ~(ICS_PLIE | ICS_DMA0_E | ICS_DMA1_E));
            comedi_free_irq (dev->irq, dev);
        }
 
@@ -1051,6 +1317,124 @@ static void ai_read_dregs (
     }
 }
 
+#ifdef USE_DMA
+/*
+  Terminate a DMA transfer and wait for everything to quiet down
+*/
+void abort_dma (
+    comedi_device *dev,
+    unsigned int channel)              /* DMA channel 0, 1 */
+{
+    unsigned long dma_cs_addr;         /* the control/status register */
+    uint8_t status;
+    unsigned int ii;
+    //unsigned long flags;
+
+    dma_cs_addr = (unsigned long)devpriv->lcfg
+       + ((channel == 0) ? LCFG_DMACSR0 : LCFG_DMACSR1);
+
+    // spinlock for plx dma control/status reg
+    //comedi_spin_lock_irqsave( &dev->spinlock, flags );
+
+    // abort dma transfer if necessary
+    status = readb(dma_cs_addr);
+    if((status & PLX_DMA_EN_BIT) == 0) { /* not enabled (Error?) */
+       DPRINTK ("rtd520: AbortDma on non-active channel %d (0x%x)\n",
+                channel, status);
+       goto abortDmaExit;
+    }
+
+    /* wait to make sure done bit is zero (needed?) */
+    for (ii = 0;
+       (status & PLX_DMA_DONE_BIT) && ii < RTD_DMA_TIMEOUT;
+       ii++) {
+       WAIT_QUIETLY;
+       status = readb(dma_cs_addr);
+    }
+    if (status & PLX_DMA_DONE_BIT) {
+       printk("rtd520: Timeout waiting for dma %i done clear\n", channel);
+       goto abortDmaExit;
+    }
+
+    /* disable channel (required) */
+    writeb(0, dma_cs_addr);
+    udelay(1);                         /* needed?? */
+    /* set abort bit for channel */
+    writeb(PLX_DMA_ABORT_BIT, dma_cs_addr);
+
+    // wait for dma done bit to be set
+    status = readb(dma_cs_addr);
+    for(ii = 0;
+       (status & PLX_DMA_DONE_BIT) == 0 && ii < RTD_DMA_TIMEOUT;
+       ii++) {
+       status = readb(dma_cs_addr);
+       WAIT_QUIETLY;
+    }
+    if ((status & PLX_DMA_DONE_BIT) == 0) {
+       printk("rtd520: Timeout waiting for dma %i done set\n", channel);
+    }
+
+abortDmaExit:
+    //comedi_spin_unlock_irqrestore( &dev->spinlock, flags );
+}
+
+/*
+  Process what is in the DMA transfer buffer and pass to comedi
+  Note: this is not re-entrant
+*/
+static void ai_process_dma (
+    comedi_device *dev,
+    comedi_subdevice *s)
+{
+    int ii, n;
+    s16 *dp;
+
+    if (devpriv->aiCount == 0)         /* transfer already complete */
+       return;
+
+    dp = devpriv->dma0Buff[devpriv->dma0Offset];
+    for (ii = 0; ii < thisboard->fifoLen/2;) { /* convert samples */
+       sampl_t sample;
+
+       if (CHAN_ARRAY_TEST (devpriv->chanBipolar, s->async->cur_chan)) {
+           sample = (*dp >> 3) + 2048; /* convert to comedi unsigned data */
+       } else {
+           sample = *dp >> 3;         /* low 3 bits are marker lines */
+       }
+       *dp++ = sample;                 /* put processed value back */
+
+       if (++s->async->cur_chan >= s->async->cmd.chanlist_len)
+           s->async->cur_chan = 0;
+
+       ++ii;                           /* number ready to transfer */
+       if (devpriv->aiCount > 0) {     /* < 0, means read forever */
+           if (--devpriv->aiCount == 0) { /* done */
+               /*DPRINTK ("rtd520: Final %d samples\n", ii);*/
+               break;
+           }
+       }
+    }
+
+    /* now pass the whole array to the comedi buffer */
+    dp = devpriv->dma0Buff[devpriv->dma0Offset];
+    n = comedi_buf_write_alloc (s->async, ii * sizeof (s16));
+    if (n < (ii * sizeof (s16))) {     /* any residual is an error */
+       DPRINTK ("rtd520:ai_process_dma buffer overflow %d samples!\n",
+                ii - (n / sizeof (s16)));
+       s->async->events |= COMEDI_CB_ERROR;
+    }
+    comedi_buf_memcpy_to (s->async, 0, dp, n);
+    comedi_buf_write_free (s->async, n);
+
+    /* always at least 1 scan -- 1/2 FIFO is larger than our max scan list */
+    s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS;
+
+    if (++devpriv->dma0Offset >= DMA_CHAIN_COUNT) { /* next buffer */
+       devpriv->dma0Offset = 0;
+    }
+}
+#endif /* USE_DMA */
+
 /*
   Handle all rtd520 interrupts.  
   Runs atomically and is never re-entered.
@@ -1063,79 +1447,94 @@ static void rtd_interrupt (
     struct pt_regs *regs)              /* cpu context (ignored) */
 {
     comedi_device *dev = d;            /* must be called "dev" for devpriv */
-    u16 status = RtdInterruptStatus (dev);
+    u16 status;
     u16 fifoStatus;
     comedi_subdevice *s = dev->subdevices + 0; /* analog in subdevice */
 
     devpriv->intCount++;               /* DEBUG statistics */
 
-    /* if interrupt was not caused by our board */
-    /* needed??? we dont claim to share interrupt lines */
-    if ((0 == status)
-       || !(dev->attached)) {
-       return;
-    }
-
     fifoStatus = RtdFifoStatus (dev);
-    /* check for FIFO full, this automatically halts the ADC */
+    /* check for FIFO full, this automatically halts the ADC! */
     if (!(fifoStatus & FS_ADC_FULL)) { /* 0 -> full */
        DPRINTK("rtd520: FIFO full! fifo_status=0x%x\n",
                fifoStatus ^ 0x6666);   /* should be all 0s */
        RtdPacerStop (dev);             /* Stop PACER */
-       RtdInterruptMask (dev, 0);      /* mask out ABOUT and SAMPLE */
+#ifdef USE_DMA
+       if (devpriv->flags & DMA0_ACTIVE) {
+           abort_dma (dev, 0);
+           devpriv->flags &= ~DMA0_ACTIVE;
+           RtdPlxInterruptWrite (dev,
+                                 RtdPlxInterruptRead (dev) & ~ICS_DMA0_E);
+       }
+#endif /* USE_DMA */
+       RtdInterruptMask (dev, 0);      /* mask out SAMPLE */
        s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
        comedi_event (dev, s, s->async->events);
        RtdAdcClearFifo (dev);          /* clears full flag */
+       status = RtdInterruptStatus (dev);
        RtdInterruptClearMask (dev, status);
        RtdInterruptClear (dev);
        return;
     } 
 
-    /* ABOUT does not seem to be causing PCI interrupts */
-    if (status & IRQM_ADC_ABOUT_CNT) { /* about count -> done (maybe) */
-       if (0 == devpriv->aboutWrap) {  /* final about interrupt */
-           DPRINTK("rtd520: Done. %ld remain, fifo_status=0x%x\n",
-                   devpriv->aiCount, 
-                   fifoStatus ^ 0x6666); /* should be all 0s */
-           goto transferDone;
-       } else if (devpriv->aiCount > 0) { /* finite acquisition */
-           /* multi-count wraps */
-           if (devpriv->aiCount < devpriv->aboutWrap) {
-#if 0
-               DPRINTK("rtd520: About done. %ld remain of %d, fifo_status=0x%x\n",
-                       devpriv->aiCount, devpriv->aboutWrap,
-                       fifoStatus ^ 0x6666); /* should be all 0s */
-#endif
-               devpriv->aboutWrap = 0;
-               RtdAboutStopEnable (dev, 0); /* enable stop */
-       
+#ifdef USE_DMA
+    if (devpriv->flags & DMA0_ACTIVE) {        /* Check DMA */
+       u32 istatus = RtdPlxInterruptRead (dev);
+
+       if (istatus & ICS_DMA0_A) {
+           ai_process_dma (dev, s);
+           /*DPRINTK ("rtd520: DMA transfer: %ld to go, istatus %x\n",
+             devpriv->aiCount, istatus);*/
+           RtdDma0Control (dev, (devpriv->dma0Control & ~PLX_DMA_START_BIT)
+                           | PLX_CLEAR_DMA_INTR_BIT);
+           if (0 == devpriv->aiCount) { /* counted down */
+               DPRINTK("rtd520: Samples Done (DMA).\n");
+               goto transferDone;
            }
+           comedi_event (dev, s, s->async->events);
+       } else {
+           /*DPRINTK ("rtd520: No DMA ready: istatus %x\n", istatus);*/
        }
     }
-    else if (status & IRQM_ADC_SAMPLE_CNT) { /* sample count -> read FIFO */
+    /* Fall through and check for other interrupt sources */
+#endif /* USE_DMA */
+
+    status = RtdInterruptStatus (dev);
+    /* if interrupt was not caused by our board, or handled above */
+    if (0 == status) {
+       return;
+    }
+
+    if (status & IRQM_ADC_SAMPLE_CNT) { /* sample count -> read FIFO */
        /* since the priority interrupt controller may have queued a sample
           counter interrupt, even though we have already finish,
           we must handle the possibility that there is no data here */
        if (!(fifoStatus & FS_ADC_HEMPTY)) { /* 0 -> 1/2 full */
+           /*DPRINTK("rtd520: Sample int, reading 1/2FIFO.  fifo_status 0x%x\n",
+             fifoStatus ^ 0x6666);*/
            ai_read_n (dev, s, thisboard->fifoLen / 2);
            if (0 == devpriv->aiCount) { /* counted down */
-               DPRINTK("rtd520: Samples Done 1/2. fifo_status was 0x%x\n",
+               DPRINTK("rtd520: Samples Done (1/2). fifo_status was 0x%x\n",
                        fifoStatus ^ 0x6666); /* should read all 0s */
                goto transferDone;
            }
            comedi_event (dev, s, s->async->events);
        } else if (devpriv->transCount > 0) {   /* read often */
+           /*DPRINTK("rtd520: Sample int, reading %d  fifo_status 0x%x\n",
+             devpriv->transCount, fifoStatus ^ 0x6666);*/
            if (fifoStatus & FS_ADC_EMPTY) { /* 1 -> not empty */
                ai_read_n (dev, s, devpriv->transCount);
                if (0 == devpriv->aiCount) { /* counted down */
-                   DPRINTK("rtd520: Samples Done n. fifo_status was 0x%x\n",
+                   DPRINTK("rtd520: Samples Done (N). fifo_status was 0x%x\n",
                            fifoStatus ^ 0x6666); /* should read all 0s */
                    goto transferDone;
                }
                comedi_event (dev, s, s->async->events);
            }
-       } /* else wait for 1/2 FIFO */
-
+       } else {                        /* wait for 1/2 FIFO */
+           /*DPRINTK("rtd520: Sample int.  Wait for 1/2. fifo_status 0x%x\n",
+             fifoStatus ^ 0x6666);*/
+       }
     }
     else {
        DPRINTK ("rtd520: unknown interrupt source!\n");
@@ -1145,7 +1544,7 @@ static void rtd_interrupt (
        DPRINTK("rtd520: Interrupt overrun! over_status=0x%x\n",
                0xffff & RtdInterruptOverrunStatus (dev));
        RtdPacerStop (dev);             /* Stop PACER */
-       RtdInterruptMask (dev, 0);      /* mask out ABOUT and SAMPLE */
+       RtdInterruptMask (dev, 0);      /* mask out SAMPLE */
        s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
        comedi_event (dev, s, s->async->events);
        RtdInterruptOverrunClear (dev);
@@ -1157,27 +1556,43 @@ static void rtd_interrupt (
     return;
 
 transferDone:
-    RtdInterruptMask (dev, 0); /* mask out ABOUT and SAMPLE */
     RtdPacerStop (dev);                /* Stop PACER */
-
-    fifoStatus = RtdFifoStatus (dev);
-    if (!(fifoStatus & FS_ADC_HEMPTY)) { /* 0 -> 1/2 full */
-       ai_read_n (dev, s, thisboard->fifoLen / 2);
+    RtdInterruptMask (dev, 0); /* mask out SAMPLE */
+#ifdef USE_DMA
+    if (devpriv->flags & DMA0_ACTIVE) {
+       RtdPlxInterruptWrite (dev,      /* disable any more interrupts */
+                             RtdPlxInterruptRead (dev) & ~ICS_DMA0_E);
+       abort_dma (dev, 0);
+       devpriv->flags &= ~DMA0_ACTIVE;
+       /* if Using DMA, then we should have read everything by now */
+       if (devpriv->aiCount > 0) {
+           DPRINTK ("rtd520: Lost DMA data! %ld remain\n",
+                    devpriv->aiCount);
+       }
+    }
+#endif /* USE_DMA */
+
+    if (devpriv->aiCount > 0) {                /* there shouldn't be anything left */
+       fifoStatus = RtdFifoStatus (dev);
+       DPRINTK("rtd520: Finishing up. %ld remain, fifoStat=%x\n",
+               devpriv->aiCount,
+               fifoStatus ^ 0x6666); /* should read all 0s */
+       ai_read_dregs (dev, s); /* read anything left in FIFO */
     }
-    ai_read_dregs (dev, s);    /* read anything left in FIFO */
+
     s->async->events |= COMEDI_CB_EOA;/* signal end to comedi */
     comedi_event (dev, s, s->async->events);
-       
-    fifoStatus = RtdFifoStatus (dev);
-    DPRINTK("rtd520: Done. %ld ints, %ld extra ai, %ld remain\n\tintStatus=0x%x, overrun_status=0x%x, fifo_status=0x%x\n",
-           devpriv->intCount, devpriv->aiExtraInt, devpriv->aiCount,
-           status,
-           0xffff & RtdInterruptOverrunStatus (dev),
-           fifoStatus ^ 0x6666); /* should be all 0s */
 
                                        /* clear the interrupt */
+    status = RtdInterruptStatus (dev);
     RtdInterruptClearMask (dev, status);
     RtdInterruptClear (dev);
+       
+    fifoStatus = RtdFifoStatus (dev);  /* DEBUG */
+    DPRINTK("rtd520: Acquisition complete. %ld ints, intStat=%x, overStat=%x\n",
+           devpriv->intCount,
+           status,
+           0xffff & RtdInterruptOverrunStatus (dev));
 }
 
 #if 0
@@ -1187,6 +1602,7 @@ transferDone:
 static int rtd_ai_poll (comedi_device *dev,comedi_subdevice *s)
 {
     /* TODO: This needs to mask interrupts, read_dregs, and then re-enable */
+    /* Not sure what to do if DMA is active */
     return s->async->buf_write_count - s->async->buf_read_count;
 }
 #endif
@@ -1271,15 +1687,29 @@ static int rtd_ai_cmdtest (
     }
 
     if (cmd->scan_begin_src == TRIG_TIMER){
-       if (cmd->scan_begin_arg < RTD_MAX_SPEED) {
-           cmd->scan_begin_arg = RTD_MAX_SPEED;
-           rtd_ns_to_timer(&cmd->scan_begin_arg, TRIG_ROUND_UP);
-           err++;
-       }
-       if (cmd->scan_begin_arg > RTD_MIN_SPEED) {
-           cmd->scan_begin_arg = RTD_MIN_SPEED;
-           rtd_ns_to_timer(&cmd->scan_begin_arg, TRIG_ROUND_DOWN);
-           err++;
+       /* Note: these are time periods, not actual rates */
+       if (1 == cmd->chanlist_len) {   /* no scanning */
+           if (cmd->scan_begin_arg < RTD_MAX_SPEED_1) {
+               cmd->scan_begin_arg = RTD_MAX_SPEED_1;
+               rtd_ns_to_timer(&cmd->scan_begin_arg, TRIG_ROUND_UP);
+               err++;
+           }
+           if (cmd->scan_begin_arg > RTD_MIN_SPEED_1) {
+               cmd->scan_begin_arg = RTD_MIN_SPEED_1;
+               rtd_ns_to_timer(&cmd->scan_begin_arg, TRIG_ROUND_DOWN);
+               err++;
+           }
+       } else {
+           if (cmd->scan_begin_arg < RTD_MAX_SPEED) {
+               cmd->scan_begin_arg = RTD_MAX_SPEED;
+               rtd_ns_to_timer(&cmd->scan_begin_arg, TRIG_ROUND_UP);
+               err++;
+           }
+           if (cmd->scan_begin_arg > RTD_MIN_SPEED) {
+               cmd->scan_begin_arg = RTD_MIN_SPEED;
+               rtd_ns_to_timer(&cmd->scan_begin_arg, TRIG_ROUND_DOWN);
+               err++;
+           }
        }
     } else {
        /* external trigger */
@@ -1291,15 +1721,28 @@ static int rtd_ai_cmdtest (
        }
     }
     if (cmd->convert_src==TRIG_TIMER) {
-       if (cmd->convert_arg < RTD_MAX_SPEED) {
-           cmd->convert_arg = RTD_MAX_SPEED;
-           rtd_ns_to_timer(&cmd->convert_arg, TRIG_ROUND_UP);
-           err++;
-       }
-       if (cmd->convert_arg > RTD_MIN_SPEED) {
-           cmd->convert_arg = RTD_MIN_SPEED;
-           rtd_ns_to_timer(&cmd->convert_arg, TRIG_ROUND_DOWN);
-           err++;
+       if (1 == cmd->chanlist_len) {   /* no scanning */
+           if (cmd->convert_arg < RTD_MAX_SPEED_1) {
+               cmd->convert_arg = RTD_MAX_SPEED_1;
+               rtd_ns_to_timer(&cmd->convert_arg, TRIG_ROUND_UP);
+               err++;
+           }
+           if (cmd->convert_arg > RTD_MIN_SPEED_1) {
+               cmd->convert_arg = RTD_MIN_SPEED_1;
+               rtd_ns_to_timer(&cmd->convert_arg, TRIG_ROUND_DOWN);
+               err++;
+           }
+       } else {
+           if (cmd->convert_arg < RTD_MAX_SPEED) {
+               cmd->convert_arg = RTD_MAX_SPEED;
+               rtd_ns_to_timer(&cmd->convert_arg, TRIG_ROUND_UP);
+               err++;
+           }
+           if (cmd->convert_arg > RTD_MIN_SPEED) {
+               cmd->convert_arg = RTD_MIN_SPEED;
+               rtd_ns_to_timer(&cmd->convert_arg, TRIG_ROUND_DOWN);
+               err++;
+           }
        }
     } else {
        /* external trigger */
@@ -1381,9 +1824,15 @@ static int rtd_ai_cmd (
     int timer;
 
                                        /* stop anything currently running */
-    RtdPacerStop (dev);                        /* Stop PACER */
+    RtdPacerStop (dev);                        /* make sure PACER is stopped */
     RtdAdcClearFifo (dev);             /* clear any old data */
     RtdInterruptOverrunClear(dev);
+    devpriv->intCount = 0;
+
+    if (! dev->irq) {                  /* we need interrupts for this */
+       DPRINTK ("rtd520: ERROR! No interrupt available!\n");
+       return -ENXIO;
+    }
 
     /* start configuration */
     /* load channel list and reset CGT */
@@ -1400,6 +1849,8 @@ static int rtd_ai_cmd (
        RtdPacerStartSource (dev, 0);   /* software triggers pacer */
        RtdAdcConversionSource (dev, 1); /* PACER triggers ADC */
     }
+    RtdAdcSampleCounter (dev,          /* 1/2 FIFO or max sample range */
+                        (thisboard->fifoLen > 1024) ? 1023 : 511);
 
     if (TRIG_TIMER == cmd->scan_begin_src) {
                                        /* scan_begin_arg is in nanoseconds */
@@ -1427,24 +1878,19 @@ static int rtd_ai_cmd (
            /* out of counter range, use 1/2 fifo instead */
            devpriv->transCount = 0;
            devpriv->flags &= ~SEND_EOS;
-           RtdAdcSampleCounter (dev,
-                                (thisboard->fifoLen > 1024) ? 1023 : 511);
        } else {
            /* interrupt for each tranfer */
            RtdAdcSampleCounter (dev, devpriv->transCount-1);
        }
 
-       DPRINTK ("rtd520: scanLen=%d tranferCount=%d fifoLen=%d\n\tscanTime(ns)=%d flags=0x%x\n",
+       DPRINTK ("rtd520: scanLen=%d tranferCount=%d fifoLen=%d\n  scanTime(ns)=%d flags=0x%x\n",
                 cmd->chanlist_len, devpriv->transCount, thisboard->fifoLen,
                 cmd->scan_begin_arg, devpriv->flags);
-    } else {
+    } else {                           /* unknown timing, just use 1/2 FIFO */
        devpriv->transCount = 0;
        devpriv->flags &= ~SEND_EOS;
-       RtdAdcSampleCounter (dev,       /* setup a periodic interrupt */
-                            (thisboard->fifoLen > 1024) ? 1023 : 511);
     }
-    RtdPacerStopSource (dev, 3);       /* stop on ABOUT count down*/
-    RtdAboutStopEnable (dev, 0);       /* actually stop (see below) */
+    RtdPacerStopSource (dev, 0);       /* stop on SOFTWARE stop */
     RtdPacerClockSource (dev, 1);      /* use INTERNAL 8Mhz clock source */
     RtdAdcSampleCounterSource (dev, 1);        /* count samples, not scans */
 
@@ -1453,55 +1899,15 @@ static int rtd_ai_cmd (
     /* First, setup when to stop */
     switch(cmd->stop_src){
     case TRIG_COUNT:                   /* stop after N scans */
-       if ((cmd->chanlist_len <= 1)    /* no scanning to do */
-           && (cmd->stop_arg <= 1)) {
-           RtdAdcConversionSource (dev, 0); /* SOFTWARE trigger */
-       } else {
-           int n = cmd->stop_arg * cmd->chanlist_len;
-           /* load about counter (16bit) with number of SAMPLES */
-           devpriv->aiCount = n;
-           if (n <= 0x10000) {
-               /* Note: stop on underflow.  Load with N-1 */
-               DPRINTK ("rtd520: setting about counter to %d\n", n);
-               devpriv->aboutWrap = 0;
-               RtdAboutCounter (dev, n - 1);
-           } else {                    /* multiple counter wraps */
-               int     mm, dd, ii;
-
-               /*DPRINTK ("rtd520: multi-wrap count %d = %d x %d \n",
-                 n, cmd->chanlist_len, cmd->stop_arg);*/
-               /* interrupt on ABOUT wrap, until last wrap */
-               /* we can run long, aiCount handles the excess */
-               dd = (n + 0xffff-1) / 0xffff;/* find divisor, round up */
-               mm = n / dd;
-               /* dd * mm >= n, mm < 0xffff */
-
-                                       /* try to find good divisor */
-               for (ii = 0; ((mm*dd) < n) && (ii < 6); ++ii) {
-                   dd++;
-                   mm = n / dd;
-               }
-
-               if ((mm*dd) < n) {      /* just run long */
-                   ++mm;
-                   /* test case: try asking for 3 x 65537 samples */
-               }
-               /* dd * mm >= n, mm < 0xffff */
-               DPRINTK ("rtd520: multi-wrap count (%d) %d = (%d) x %d\n",
-                       n, mm*dd, mm, dd);
-               devpriv->aboutWrap = mm;
-               RtdAboutCounter (dev, mm-1);
-               RtdAboutStopEnable (dev, 1); /* just interrupt */
-           }
+       devpriv->aiCount = cmd->stop_arg * cmd->chanlist_len;
+       if ((devpriv->transCount > 0)
+           && (devpriv->transCount > devpriv->aiCount)) {
+           devpriv->transCount = devpriv->aiCount;
        }
-
        break;
 
     case TRIG_NONE:                    /* stop when cancel is called */
        devpriv->aiCount = -1;          /* read forever */
-       RtdPacerStopSource (dev, 0);    /* stop on SOFTWARE stop */
-       RtdAboutStopEnable (dev, 1);    /* just interrupt. needed??? */
-       RtdAboutCounter (dev, ~0);      /* minimize interrupts. needed??? */
        break;
 
     default:
@@ -1549,30 +1955,48 @@ static int rtd_ai_cmd (
        DPRINTK ("rtd520: Warning! ignoring convert_src mode %d\n",
                cmd->convert_src);
     }
-
-
     /* end configuration */
 
-    /* BUG: start_src is ASSUMED to be TRIG_NOW */
-
-    /* interrupt setup */
-    if (! dev->irq) {
-       DPRINTK ("rtd520: ERROR! No interrupt available!\n");
-       return -ENXIO;
-    }
 
-    /* This doesn't work.  There is NO WAY to clear an interrupt
+    /* This doesn't seem to work.  There is no way to clear an interrupt
        that the priority controller has queued! */
     RtdInterruptClearMask (dev, ~0); /* clear any existing flags */
     RtdInterruptClear (dev);
 
     /* TODO: allow multiple interrupt sources */
-    if ((devpriv->transCount > 0) || (devpriv->aiCount > 512)) {
-       RtdInterruptMask (dev, IRQM_ADC_ABOUT_CNT | IRQM_ADC_SAMPLE_CNT );
-    } else {
-       RtdInterruptMask (dev, IRQM_ADC_ABOUT_CNT);
+    if (devpriv->transCount > 0) {     /* transfer every N samples */
+       RtdInterruptMask (dev, IRQM_ADC_SAMPLE_CNT);
+       DPRINTK ("rtd520: Transferring every %d\n", devpriv->transCount);
+    }
+    else {                            /* 1/2 FIFO transfers, use DMA */
+#ifdef USE_DMA
+       RtdDma0Control (dev, PLX_CLEAR_DMA_INTR_BIT); /* disable and clear any old ints*/
+       devpriv->flags |= DMA0_ACTIVE;
+       
+       /* point to first transfer in ring */
+       devpriv->dma0Offset = 0;
+       RtdDma0Next (dev,               /* point to first block */
+                    devpriv->dma0Chain[DMA_CHAIN_COUNT-1].next);
+       RtdDma0Mode (dev, DMA_MODE_BITS);
+       RtdDma0Source (dev, DMAS_ADFIFO_HALF_FULL); /* set DMA trigger source*/
+       RtdDma0Reset (dev);                     /* reset onboard state */
+
+
+       RtdPlxInterruptWrite (dev,      /* enable interrupt */
+                             RtdPlxInterruptRead (dev) | ICS_DMA0_E);
+       /* Must be 2 steps.  See PLX app note about "Starting a DMA transfer"*/
+       RtdDma0Control (dev, PLX_DMA_EN_BIT); /* enable DMA (clear INTR?) */
+       RtdDma0Control (dev, PLX_DMA_EN_BIT | PLX_DMA_START_BIT); /*start DMA*/
+       DPRINTK ("rtd520: Using DMA0 transfers. plxInt %x RtdInt %x\n", 
+                RtdPlxInterruptRead (dev), devpriv->intMask);
+#else /* USE_DMA */
+       RtdInterruptMask (dev, IRQM_ADC_SAMPLE_CNT );
+       DPRINTK ("rtd520: Transferring every 1/2 FIFO\n");
+#endif /* USE_DMA */
     }
 
+    /* BUG: start_src is ASSUMED to be TRIG_NOW */
+    /* BUG? it seems like things are running before the "start" */
     RtdPacerStart (dev);               /* Start PACER */
     return 0;
 }
@@ -1587,6 +2011,13 @@ static int rtd_ai_cancel (
                                        /* more is probably needed here */
     RtdPacerStop (dev);                        /* Stop PACER */
     RtdAdcConversionSource (dev, 0);   /* software trigger only */
+#ifdef USE_DMA
+    if (devpriv->flags & DMA0_ACTIVE) {
+       RtdPlxInterruptWrite (dev,      /* disable any more interrupts */
+                             RtdPlxInterruptRead (dev) & ~ICS_DMA0_E);
+       abort_dma (dev, 0);
+    }
+#endif /* USE_DMA */
     return 0;
 }
 
index 1dea123b171c3de4cbd7c6027751db3b88307f56..df674ab4ce22ddb861900000a14906305f6b18d1 100644 (file)
 #define LCFG_ITCSR              0x0068    // INTCSR, Interrupt Control/Status Register
 #define LCFG_DMAMODE0           0x0080    // DMA Channel 0 Mode Register
 #define LCFG_DMAPADR0           0x0084    // DMA Channel 0 PCI Address Register
-#define LCFG_DMALADR0           0x0088    // DMA Channel 0 Local Address Register
+#define LCFG_DMALADR0           0x0088    // DMA Channel 0 Local Address Reg
 #define LCFG_DMASIZ0            0x008C    // DMA Channel 0 Transfer Size (Bytes) Register
 #define LCFG_DMADPR0            0x0090    // DMA Channel 0 Descriptor Pointer Register
 #define LCFG_DMAMODE1           0x0094    // DMA Channel 1 Mode Register
 #define DMAS_DAC1_FIFO_HALF_EMPTY  0x9  // D/A1 FIFO half empty
 #define DMAS_DAC2_FIFO_HALF_EMPTY  0xA  // D/A2 FIFO half empty
 
-//  PLX Interrupt Control/Status enable masks (LCFG)
-#define PIRQE_PCI          0x00000100  //PCI Interrupt Enable (defaulton)
-#define PIRQE_LINT         0x00000800  //Local Interrupt Enable (bit 11)
-#define PIRQE_DMA0         0x00040000  //DMA Channel 0 Interrupt Enable
-#define PIRQE_DMA1         0x00080000  //DMA Channel 1 Interrupt Enable
-//  PLX Interrupt Control/Status status masks (LCFG)
-#define PIRQS_LINT         0x00008000  //Local Interrupt Active (bit 15)
-#define PIRQS_DMA0         0x00200000  //DMA Channel 0 Interrupt Active
-#define PIRQS_DMA1         0x00400000  //DMA Channel 1 Interrupt Active
-
-// DMA Mode Register (LCFG)
-#define DMA_8BIT         0x0000    // 8 bit wide transfer
-#define DMA_16BIT        0x0001    // 16 bit wide transfer
-#define DMA_32BIT        0x0003    // 32 bit wide transfer
-#define DMA_RDYEN        0x0040    // READY enable
-#define DMA_BTERM        0x0080    // BTERM enable
-#define DMA_LBURST       0x0100    // Local Burst enable
-#define DMA_CHAIN        0x0200    // Chain mode DMA
-#define DMA_ITEN         0x0400    // DMA done IT enable
-#define DMA_LA_CONST     0x0800    // Local Address constant
-#define DMA_DEMAND       0x1000    // Demand mode DMA
-#define DMA_WRANDI       0x2000    // Write and Invalidate mode DMA
-#define DMA_CLRCNT       0x010000  // Clear transfer count
-#define DMA_IT_PCI       0x020000  // DMA IT rerouted to PCI IT
-
-//  DMA Command/Status bits (LCFG)
-#define DMA_ENABLED       0x01         // DMA Channel 0 enabled
-#define DMA_START         0x02         // DMA Channel 0 started
-#define DMA_ABORT         0x04         // Abort DMA Channel 0 transfer
-#define DMA_CLEAR_IT      0x08         // Clear DMA Channel 0 IT
-#define DMA_DONE          0x10         // DMA Channel 0 transfer done
-
-// DMA Transfer Direction
-#define DMAD_FROMCARD      1            // DMA transfer from card to memory
-#define DMAD_TOCARD        0            // DMA transfer from memory to card
-
-// Chained DMA Transfer Direction
-#define DMA_CHAINED_READ       8       // DMA transfer from memory to card
-#define DMA_CHAINED_WRITE      0       // DMA transfer from card to memory
-
-// DMA Local Addresses   (LAS1 offset)
-#define DMALADDR_ADC       0x0000      // A/D FIFO
-#define DMALADDR_HDIN      0x0004      // High Speed Digital Input FIFO
-#define DMALADDR_DAC1      0x0008      // D/A1 FIFO
-#define DMALADDR_DAC2      0x000C      // D/A2 FIFO
+// DMA Local Addresses   (0x40000000+LAS1 offset)
+#define DMALADDR_ADC       0x40000000  // A/D FIFO
+#define DMALADDR_HDIN      0x40000004  // High Speed Digital Input FIFO
+#define DMALADDR_DAC1      0x40000008  // D/A1 FIFO
+#define DMALADDR_DAC2      0x4000000C  // D/A2 FIFO
+
 
 // Port 0 compare modes (SetDIO0CompareMode)
 #define DIO_MODE_EVENT     0           // Event Mode