From: David Schleef Date: Thu, 29 Aug 2002 00:01:11 +0000 (+0000) Subject: patch from Dan Christian. X-Git-Tag: r0_7_66~118 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=b07b03421052f59bc38adfabe28878f01b96bf20;p=comedi.git patch from Dan Christian. - 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 --- diff --git a/comedi/drivers/rtd520.c b/comedi/drivers/rtd520.c index e1c9e555..35eae3f4 100644 --- a/comedi/drivers/rtd520.c +++ b/comedi/drivers/rtd520.c @@ -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 #include +#include #include /*====================================================================== - 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; } diff --git a/comedi/drivers/rtd520.h b/comedi/drivers/rtd520.h index 1dea123b..df674ab4 100644 --- a/comedi/drivers/rtd520.h +++ b/comedi/drivers/rtd520.h @@ -138,7 +138,7 @@ #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 @@ -353,51 +353,12 @@ #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