From 90e8bd2c8b84d5002f2e881d2e29ebe8ac77ee13 Mon Sep 17 00:00:00 2001 From: David Schleef Date: Thu, 10 May 2001 09:57:50 +0000 Subject: [PATCH] Added driver from Dan Christian --- comedi/drivers/rtd520.c | 1539 +++++++++++++++++++++++++++++++++++++++ comedi/drivers/rtd520.h | 455 ++++++++++++ 2 files changed, 1994 insertions(+) create mode 100644 comedi/drivers/rtd520.c create mode 100644 comedi/drivers/rtd520.h diff --git a/comedi/drivers/rtd520.c b/comedi/drivers/rtd520.c new file mode 100644 index 00000000..befdd3a8 --- /dev/null +++ b/comedi/drivers/rtd520.c @@ -0,0 +1,1539 @@ +/* + comedi/drivers/rtd520.c + Comedi driver for Real Time Devices (RTD) PCI4520/DM7520 + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 2001 David A. Schleef + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + Created by Dan Christian, NASA Ames Research Center. + + The PCI4520 is a PCI card. The DM7520 is a PC/104-plus card. + Both have: + 8/16 12 bit ADC with FIFO and channel gain table + 8 bits high speed digital out (for external MUX) (or 8 in or 8 out) + 8 bits high speed digital in with FIFO and interrupt on change (or 8 IO) + 2 12 bit DACs with FIFOs + 2 bits output + 2 bits input + bus mastering DMA + timers: ADC sample, pacer, burst, about, delay, DA1, DA2 + sample counter + 3 user timer/counters + external interrupt + + The DM7520 has slightly fewer features (fewer gain steps). + + These boards can support external multiplexors and multi-board + synchronization, but this driver doesn't support that. + + Board docs: http://www.rtdusa.com/dm7520.htm + Data sheet: http://www.rtdusa.com/pdf/dm7520.pdf + Example source: http://www.rtdusa.com/examples/dm/dm7520.zip + Call them and ask for the register level manual. + PCI chip: http://www.plxtech.com/products/toolbox/9080.htm + + Notes: + This board is (almost) completely memory mapped. + + I use a pretty loose naming style within the driver (rtd_blah). + All externally visible names should be rtd520_blah. + I use camelCase in and for structures. + I may also use upper CamelCase for function names. + + This board somewhat related to the PCI4400 board. + + I borrowed heavily from the ni_mio_common, ni_atmio16d, and das1800, + since they have the best documented code. + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/*====================================================================== + Board specific stuff +======================================================================*/ + +/* registers */ +#define RTD_VENDOR_ID 0x1435 +/* + The board has three memory windows: las0, las1, and lcfg (the PCI chip) + Las1 has the data and can be burst DMAed 32bits at a time. +*/ +#define LCFG_PCIINDEX 0 +/* PCI region 1 is a 256 byte IO space mapping. Use??? */ +#define LAS0_PCIINDEX 2 /* PCI memory resources */ +#define LAS1_PCIINDEX 3 +#define LCFG_PCISIZE 0x100 +#define LAS0_PCISIZE 0x200 +#define LAS1_PCISIZE 0x10 + +#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 ??? */ +#define RTD_SETTLE_DELAY 1 /* in usec */ +#define RTD_ADC_TIMEOUT 1000 /* in usec */ + +#include "rtd520.h" + +/* + Bit fields for one entry in the channel gain table +*/ +typedef struct rtdChanGain_struct { + unsigned int channel : 4; + unsigned int gain : 3; + unsigned int nrse : 1; /* SE GND: 0=AGND, 1=AINSENSE */ + unsigned int range : 2; /* 0=+-5, 1=+-10, 2=+10 */ + unsigned int diff : 1; /* 0=SE, 1=differental */ + unsigned int pause : 1; + unsigned int dac1 : 1; + unsigned int dac2 : 1; + unsigned int skip : 1; + unsigned int reserved: 1; +} rtdChanGain; + +/*====================================================================== + Comedi specific stuff +======================================================================*/ + +/* + The board has 3 input modes and the gains of 1,2,4,...32 (, 64, 128) +*/ +static comedi_lrange rtd_ai_7520_range = { 6, { + /* TODO: BIP_RANGE(10.0) */ + BIP_RANGE(5.0), + BIP_RANGE(5.0/2), + BIP_RANGE(5.0/4), + BIP_RANGE(5.0/8), + BIP_RANGE(5.0/16), + BIP_RANGE(5.0/32), + /*UNI_RANGE(10.0), + UNI_RANGE(10.0/2), + UNI_RANGE(10.0/4), + UNI_RANGE(10.0/8), + UNI_RANGE(10.0/16), + UNI_RANGE(10.0/32),*/ +}}; + /* PCI4520 has two more gains (6 more entries) */ + +static comedi_lrange rtd_ao_range = { 4, { + RANGE(-10, 10), + RANGE(-5, 5), + RANGE(0, 10), + RANGE(0, 5), +}}; + +/* + Board descriptions + */ +typedef struct rtdBoard_struct{ + char *name; /* must be first */ + int device_id; + int aiChans; + int aiBits; + int aiMaxGain; + int fifoLen; + int haveDio; /* is digital IO supported */ +} rtdBoard; + +rtdBoard rtd520Boards[] = { + { + name: "DM7520", + device_id: 0x7520, + aiChans: 16, + aiBits: 12, + aiMaxGain: 32, + fifoLen: 1024, + haveDio: 0, + }, + { + name: "DM7520-8", + device_id: 0x7520, + aiChans: 16, + aiBits: 12, + aiMaxGain: 32, + fifoLen: 8192, + haveDio: 0, + }, + { + name: "PCI4520", + device_id: 0x4520, + aiChans: 16, + aiBits: 12, + aiMaxGain: 128, + fifoLen: 1024, + haveDio: 0, + }, + { + name: "PCI4520-8", + device_id: 0x4520, + aiChans: 16, + aiBits: 12, + aiMaxGain: 128, + fifoLen: 8192, + haveDio: 0, + }, +}; + +/* + * Useful for shorthand access to the particular board structure + */ +#define thisboard ((rtdBoard *)dev->board_ptr) + +/* + This structure is for data unique to this hardware driver. + This is also unique for each board in the system. +*/ +typedef struct{ + /* memory mapped board structures */ + void *las0; + void *las1; + void *lcfg; + + unsigned long intCount; /* interrupt count */ + unsigned long aiCount; /* total transfer size (samples) */ + unsigned long aiExtraInt; /* ints but no data */ + int aboutWrap; + + /* PCI device info */ + struct pci_dev *pci_dev; + + /* Used for AO readback */ + lsampl_t ao_readback[2]; + + /* NEEDED???: these 3 arent used after init */ + unsigned long physLas0; /* configuation */ + unsigned long physLas1; /* data area */ + unsigned long physLcfg; /* PLX9080 */ + +} rtdPrivate; + +/* + * most drivers define the following macro to make it easy to + * access the private structure. + */ +#define devpriv ((rtdPrivate *)dev->private) + + +/* Macros to access registers */ + +/* Reset board */ +#define RtdResetBoard(dev) \ + writel (0, devpriv->las0+LAS0_BOARD_RESET) + +/* Reset channel gain table read pointer */ +#define RtdResetCGT(dev) \ + writel (0, devpriv->las0+LAS0_CGT_RESET) + +/* Reset channel gain table read and write pointers */ +#define RtdClearCGT(dev) \ + writel (0, devpriv->las0+LAS0_CGT_CLEAR) + +/* Reset channel gain table read and write pointers */ +#define RtdEnableCGT(dev,v) \ + writel ((v > 0) ? 1 : 0, devpriv->las0+LAS0_CGT_ENABLE) + +/* Write channel gain table entry */ +#define RtdWriteCGTable(dev,v) \ + writel (v, devpriv->las0+LAS0_CGT_WRITE) + +/* Write Channel Gain Latch */ +#define RtdWriteCGLatch(dev,v) \ + writel (v, devpriv->las0+LAS0_CGL_WRITE) + +/* Reset ADC FIFO */ +#define RtdClearAdcFifo(dev) \ + writel (0, devpriv->las0+LAS0_ADC_FIFO_CLEAR) + +/* Set ADC start conversion source select (write only) */ +#define RtdAdcConversionSource(dev,v) \ + writel (v, devpriv->las0+LAS0_ADC_CONVERSION) + +/* Set burst start source select (write only) */ +#define RtdBurstStartSource(dev,v) \ + writel (v, devpriv->las0+LAS0_BURST_START) + +/* Set Pacer start source select (write only) */ +#define RtdPacerStartSource(dev,v) \ + writel (v, devpriv->las0+LAS0_PACER_START) + +/* Set Pacer stop source select (write only) */ +#define RtdPacerStopSource(dev,v) \ + writel (v, devpriv->las0+LAS0_PACER_STOP) + +/* Set Pacer clock source select (write only) 0=external 1=internal */ +#define RtdPacerClockSource(dev,v) \ + writel ((v > 0) ? 1 : 0, devpriv->las0+LAS0_PACER_SELECT) + +/* Set sample counter source select (write only) */ +#define RtdAdcSampleCounterSource(dev,v) \ + writel (v, devpriv->las0+LAS0_ADC_SCNT_SRC) + +/* Set Pacer trigger mode select (write only) 0=single cycle, 1=repeat */ +#define RtdPacerTriggerMode(dev,v) \ + writel ((v > 0) ? 1 : 0, devpriv->las0+LAS0_PACER_REPEAT) + +/* Set About counter stop enable (write only) */ +#define RtdAboutStopEnable(dev,v) \ + writel ((v > 0) ? 1 : 0, devpriv->las0+LAS0_ACNT_STOP_ENABLE) + +/* Set external trigger polarity (write only) 0=positive edge, 1=negative */ +#define RtdTriggerPolarity(dev,v) \ + writel ((v > 0) ? 1 : 0, devpriv->las0+LAS0_ETRG_POLARITY) + +/* Start single ADC conversion */ +#define RtdAdcStart(dev) \ + writew (0, devpriv->las0+LAS0_ADC) + +/* Read one ADC data value (12bit+sign as 16bit) */ +/* Note: matches what DMA would get. Actual value >> 3 */ +#define RtdAdcFifoGet(dev) \ + readw (devpriv->las1+LAS1_ADC_FIFO) + +/* Read two ADC data values */ +#define RtdAdcFifoGet2(dev) \ + readl (devpriv->las1+LAS1_ADC_FIFO) + +/* FIFO status */ +#define RtdFifoStatus(dev) \ + readl (devpriv->las0+LAS0_ADC) + +/* pacer start/stop read=start, write=stop*/ +#define RtdPacerStart(dev) \ + readl (devpriv->las0+LAS0_PACER) +#define RtdPacerStop(dev) \ + writel (0, devpriv->las0+LAS0_PACER) + +/* Interrupt status */ +#define RtdInterruptStatus(dev) \ + readl (devpriv->las0+LAS0_IT) + +/* Interrupt mask */ +#define RtdInterruptMask(dev,v) \ + writel (v,devpriv->las0+LAS0_IT) + +/* Interrupt status clear (only bits set in mask) */ +#define RtdInterruptClear(dev) \ + readl (devpriv->las0+LAS0_CLEAR) + +/* Interrupt clear mask */ +#define RtdInterruptClearMask(dev,v) \ + writel (v, devpriv->las0+LAS0_CLEAR) + +/* Interrupt overrun status */ +#define RtdInterruptOverrunStatus(dev) \ + readl (devpriv->las0+LAS0_OVERRUN) + +/* Interrupt overrun clear */ +#define RtdInterruptOverrunClear(dev) \ + writel (0, devpriv->las0+LAS0_OVERRUN) + +/* Pacer counter, 24bit */ +#define RtdPacerCount(dev) \ + readl (devpriv->las0+LAS0_PCLK) +#define RtdPacerCounter(dev,v) \ + writel ((v) & 0xffffff,devpriv->las0+LAS0_PCLK) + +/* Burst counter, 10bit */ +#define RtdBurstCount(dev) \ + readl (devpriv->las0+LAS0_BCLK) +#define RtdBurstCounter(dev,v) \ + writel ((v) & 0x3ff,devpriv->las0+LAS0_BCLK) + +/* Delay counter, 16bit */ +#define RtdDelayCount(dev) \ + readl (devpriv->las0+LAS0_DCLK) +#define RtdDelayCounter(dev,v) \ + writel ((v) & 0xffff, devpriv->las0+LAS0_DCLK) + +/* About counter, 16bit */ +#define RtdAboutCount(dev) \ + readl (devpriv->las0+LAS0_ACNT) +#define RtdAboutCounter(dev,v) \ + writel ((v) & 0xffff, devpriv->las0+LAS0_ACNT) + +/* ADC sample counter, 10bit */ +#define RtdAdcSampleCount(dev) \ + readl (devpriv->las0+LAS0_ADC_SCNT) +#define RtdAdcSampleCounter(dev,v) \ + writel ((v) & 0x3ff, devpriv->las0+LAS0_ADC_SCNT) + +/* User timer/counter, 16bit (two step access, LSB,MSB) */ +/* UTC Status word must have the right counter and latching mode in it!!! */ +#define RtdUtcCount(dev,n) \ + (readb (devpriv->las0 \ + + ((n <= 0) ? LAS0_UTC0 : ((1 == n) ? LAS0_UTC1 : LAS0_UTC2))) \ + | (readb (devpriv->las0 \ + + ((n <= 0) ? LAS0_UTC0 : ((1 == n) ? LAS0_UTC1 : LAS0_UTC2))) << 8)) + +#define RtdUtcCounter(dev,n,v) \ + writeb ((v) & 0xff, devpriv->las0 \ + + ((n <= 0) ? LAS0_UTC0 : ((1 == n) ? LAS0_UTC1 : LAS0_UTC2))), \ + writeb (((v) >> 8) & 0xff, devpriv->las0 \ + + ((n <= 0) ? LAS0_UTC0 : ((1 == n) ? LAS0_UTC1 : LAS0_UTC2))) + +/* Set UTC mode. Forces proper latching mode and binary counting. */ +#define RtdUtcModeSet(dev,n,v) \ + writeb (((n & 3) << 6) | (((v) & 7) < 1) | 0x30, \ + devpriv->las0 + LAS0_UTC_CTRL) + +/* Get UTC mode. Forces proper latching mode and binary counting. */ +/* The docs say that you cant read mode, but dos driver implements it! */ +#define RtdUtcModeGet(dev,n) \ + (writeb (((n & 3) << 1) | 0xE0, devpriv->las0 + LAS0_UTC_CTRL), \ + readw (devpriv->las0 + LAS0_UTC_CTRL) & 0xff) + +/* Set UTCn clock source (write only) */ +#define RtdUtcClockSource(dev,n,v) \ + writew (v, devpriv->las0 \ + + ((n <= 0) ? LAS0_UTC0_CLOCK : \ + ((1 == n) ? LAS0_UTC1_CLOCK : LAS0_UTC2_CLOCK))) + +/* Set UTCn gate source (write only) */ +#define RtdUtcGateSource(dev,n,v) \ + writew (v, devpriv->las0 \ + + ((n <= 0) ? LAS0_UTC0_GATE : \ + ((1 == n) ? LAS0_UTC1_GATE : LAS0_UTC2_GATE))) + +/* User output N source select (write only) */ +#define RtdUsrOutSource(dev,n,v) \ + 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) + + +/* + * The comedi_driver structure tells the Comedi core module + * which functions to call to configure/deconfigure (attach/detach) + * the board, and also about the kernel module that contains + * the device code. + */ +static int rtd_attach (comedi_device *dev, comedi_devconfig *it); +static int rtd_detach (comedi_device *dev); + +comedi_driver rtd520Driver={ + driver_name: "rtd520", + module: THIS_MODULE, + attach: rtd_attach, + detach: rtd_detach, + + /* It is not necessary to implement the following members if you are + * writing a driver for a ISA PnP or PCI card */ + /* Most drivers will support multiple types of boards by + * having an array of board structures. These were defined + * in rtd520Boards[] above. Note that the element 'name' + * was first in the structure -- Comedi uses this fact to + * extract the name of the board without knowing any details + * about the structure except for its length. + * When a device is attached (by comedi_config), the name + * of the device is given to Comedi, and Comedi tries to + * match it by going through the list of board names. If + * there is a match, the address of the pointer is put + * into dev->board_ptr and driver->attach() is called. + * + * Note that these are not necessary if you can determine + * the type of board in software. ISA PnP, PCI, and PCMCIA + * devices are such boards. + */ + board_name: rtd520Boards, + offset: sizeof(rtdBoard), + num_names: sizeof(rtd520Boards) / sizeof(rtdBoard), +}; + +static int rtd_ai_rinsn (comedi_device *dev, comedi_subdevice *s, + comedi_insn *insn, lsampl_t *data); +static int rtd_ao_winsn (comedi_device *dev, comedi_subdevice *s, + comedi_insn *insn, lsampl_t *data); +static int rtd_ao_rinsn (comedi_device *dev, comedi_subdevice *s, + comedi_insn *insn, lsampl_t *data); +static int rtd_dio_insn_bits (comedi_device *dev, comedi_subdevice *s, + comedi_insn *insn, lsampl_t *data); +static int rtd_dio_insn_config (comedi_device *dev, comedi_subdevice *s, + comedi_insn *insn, lsampl_t *data); +static int rtd_ai_cmdtest (comedi_device *dev,comedi_subdevice *s, + comedi_cmd *cmd); +static int rtd_ai_cmd ( comedi_device *dev, comedi_subdevice *s); +static int rtd_ai_cancel ( comedi_device *dev, comedi_subdevice *s); +static int rtd_ns_to_timer (unsigned int *ns, int roundMode); +static void rtd_interrupt ( int irq, void *d, struct pt_regs *regs); + + +/* + * Attach is called by the Comedi core to configure the driver + * for a particular board. If you specified a board_name array + * in the driver structure, dev->board_ptr contains that + * address. + */ +static int rtd_attach ( + comedi_device *dev, + comedi_devconfig *it) /* board name and options flags */ +{ + comedi_subdevice *s; + struct pci_dev* pcidev; + int index; + int ret; + + printk ("comedi%d: rtd520 attaching.\n", dev->minor); + + /* + * Allocate the private structure area. alloc_private() is a + * convenient macro defined in comedidev.h. + */ + if (alloc_private (dev, sizeof(rtdPrivate))<0) + return -ENOMEM; + + /* + * Probe the device to determine what device in the series it is. + */ + pci_for_each_dev (pcidev) { + if (pcidev->vendor == RTD_VENDOR_ID) { + if (it->options[0] && it->options[1]) { + if (pcidev->bus->number == it->options[0] + && PCI_SLOT(pcidev->devfn) == it->options[1]) { + printk("rtd520: found bus=%d slot=%d\n", + it->options[0], it->options[1]); + break; /* found it */ + } + } else { /* specific board/slot not specified */ + break; /* found one */ + } + } + } + + if (!pcidev) { + if (it->options[0] && it->options[1]) { + printk ("No RTD card at bus=%d slot=%d.\n", + it->options[0], it->options[1]); + } else { + printk ("No RTD card found.\n"); + } + return -EIO; + } + + /* See if this is a model that we know about */ + for (index=0; index < rtd520Driver.num_names; index++){ + if (rtd520Boards[index].device_id == pcidev->device) { + break; + } + } + if (index >= rtd520Driver.num_names) { + printk ("Found an RTD card, but not a supported type (%x).\n", + pcidev->device); + return -EIO; + } else { + devpriv->pci_dev = pcidev; + dev->board_ptr = rtd520Boards+index; + } + /* + * Initialize dev->board_name. Note that we can use the "thisboard" + * macro now, since we just initialized it in the last line. + */ + dev->board_name = thisboard->name; + + /* + * Initialize base addresses + */ + /* Get the physical address from PCI config */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) + devpriv->physLas0 = devpriv->pci_dev->base_address[LAS0_PCIINDEX]; + devpriv->physLas1 = devpriv->pci_dev->base_address[LAS1_PCIINDEX]; + devpriv->physLcfg = devpriv->pci_dev->base_address[LCFG_PCIINDEX]; +#else + devpriv->physLas0 = devpriv->pci_dev->resource[LAS0_PCIINDEX].start; + devpriv->physLas1 = devpriv->pci_dev->resource[LAS1_PCIINDEX].start; + devpriv->physLcfg = devpriv->pci_dev->resource[LCFG_PCIINDEX].start; +#endif + /* Now have the kernel map this into memory */ + /* ASSUME page aligned */ + devpriv->las0 = ioremap(devpriv->physLas0, LAS0_PCISIZE); + devpriv->las1 = ioremap(devpriv->physLas1, LAS1_PCISIZE); + devpriv->lcfg = ioremap(devpriv->physLcfg, LCFG_PCISIZE); + + printk ("%s: ", dev->board_name); + /*printk ("%s: LAS0=%lx, LAS1=%lx, CFG=%lx.\n", dev->board_name, + devpriv->physLas0, devpriv->physLas1, devpriv->physLcfg);*/ + + /* + * Allocate the subdevice structures. alloc_subdevice() is a + * convenient macro defined in comedidev.h. It relies on + * n_subdevices being set correctly. + */ + dev->n_subdevices=4; + if (alloc_subdevices(dev)<0) + return -ENOMEM; + + s=dev->subdevices+0; + dev->read_subdev=s; + /* analog input subdevice */ + s->type=COMEDI_SUBD_AI; + s->subdev_flags=SDF_READABLE; + s->n_chan=thisboard->aiChans; + s->maxdata=(1<aiBits)-1; + s->range_table = &rtd_ai_7520_range; + s->len_chanlist = thisboard->fifoLen; + s->insn_read = &rtd_ai_rinsn; + s->do_cmd = &rtd_ai_cmd; + s->do_cmdtest = &rtd_ai_cmdtest; + s->cancel = &rtd_ai_cancel; + + s=dev->subdevices+1; + /* analog output subdevice */ + s->type = COMEDI_SUBD_UNUSED; + /*s->type=COMEDI_SUBD_AO;*/ + s->subdev_flags=SDF_WRITEABLE; + s->n_chan =2; + s->maxdata =(1<aiBits)-1; + s->range_table = &rtd_ao_range; + s->insn_write = &rtd_ao_winsn; + s->insn_read = &rtd_ao_rinsn; + + s=dev->subdevices+2; + /* digital i/o subdevice */ + if (thisboard->haveDio){ + s->type=COMEDI_SUBD_DIO; + s->subdev_flags=SDF_READABLE|SDF_WRITEABLE; + s->n_chan=16; + s->maxdata=1; + s->range_table=&range_digital; + s->insn_bits = rtd_dio_insn_bits; + s->insn_config = rtd_dio_insn_config; + } else { + s->type = COMEDI_SUBD_UNUSED; + } + + s=dev->subdevices+3; + s->n_chan=3; + /* 3 timer/counter subdevices */ + s->type = COMEDI_SUBD_UNUSED; + + /* check if our interrupt is available and get it */ + dev->irq = devpriv->pci_dev->irq; + if(dev->irq>0){ + if((ret=comedi_request_irq (dev->irq, rtd_interrupt, + 0, "rtd520", dev))<0) + return ret; + printk("( irq = %d )\n", dev->irq); + } else { + printk("( NO IRQ )"); + } + + /* initialize board, per RTD spec */ + RtdResetBoard (dev); + RtdInterruptMask (dev,0); + RtdInterruptClearMask (dev,~0); + RtdInterruptClear(dev); /* clears bits set by mask */ + RtdInterruptOverrunClear(dev); + RtdClearCGT (dev); + RtdClearAdcFifo (dev); + /* clear DA FIFO */ + /* clear digital IO */ + if (dev->irq) { /* enable interrupt controller */ + RtdPLXInterruptWrite (dev, + RtdPLXInterruptRead (dev) | (0x0800)); + } + + printk("comedi%d: rtd520 driver attached.\n", dev->minor); + + return 1; +} + +/* + * _detach is called to deconfigure a device. It should deallocate + * resources. + * This function is also called when _attach() fails, so it should be + * careful not to release resources that were not necessarily + * allocated by _attach(). dev->private and dev->subdevices are + * deallocated automatically by the core. + */ +static int rtd_detach ( + comedi_device *dev) +{ + printk("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, + 0xffff & RtdInterruptStatus (dev), + 0xffff & RtdInterruptOverrunStatus (dev), + 0xffff & RtdFifoStatus (dev)); + if (devpriv) { + /* Shut down any board ops by reseting it */ + RtdResetBoard (dev); + RtdInterruptMask (dev, 0); + RtdInterruptClearMask (dev,~0); + RtdInterruptClear(dev); /* clears bits set by mask */ + RtdClearCGT (dev); + RtdClearAdcFifo (dev); + + /* release DMA */ + + /* release IRQ */ + if (dev->irq) { + /* disable interrupt controller */ + RtdPLXInterruptWrite (dev, + RtdPLXInterruptRead (dev) & ~(0x0800)); + 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); + } + } + + printk("comedi%d: rtd520: removed.\n",dev->minor); + + return 0; +} + +/* + Convert a single comedi channel-gain entry to a RTD520 table entry +*/ +static unsigned short rtdConvertChanGain ( + unsigned int comediChan) +{ + unsigned int chan, range, aref; + unsigned short r=0; + + chan = CR_CHAN (comediChan); + range = CR_RANGE (comediChan); + aref = CR_AREF (comediChan); + + r |= chan & 0xf; + + /* TODO: Should also be able to switch into +-=10 range */ + /* HACK!!! should not use a constant here */ + if (range < 6) { /* first 6 are bipolar */ + r |= 0x000; /* +-5 range */ + r |= (range & 0x7) << 4; /* gain */ + } else { + r |= 0x200; /* +10 range */ + r |= ((range-6) & 0x7) << 4; /* gain */ + } + + switch (aref) { + case AREF_GROUND: + break; + + case AREF_COMMON: + r |= 0x80; /* ref external analog common */ + break; + + case AREF_DIFF: + r |= 0x400; /* DIFF */ + break; + + case AREF_OTHER: /* ??? */ + break; + } + printk ("chan=%d r=%d a=%d -> 0x%x\n", + chan, range, aref, r); + return r; +} + +/* + Setup the channel-gain table from a comedi list +*/ +static void rtd_load_channelgain_list ( + comedi_device *dev, + unsigned int n_chan, + unsigned int *list) +{ + if (n_chan > 1) { /* setup channel gain table */ + int ii; + RtdClearCGT (dev); + RtdEnableCGT(dev, 1); /* enable table */ + for(ii=0; ii < n_chan; ii++){ + RtdWriteCGTable (dev, rtdConvertChanGain (list[ii])); + } + } else { /* just use the channel gain latch */ + RtdEnableCGT(dev, 0); /* disable table, enable latch */ + RtdWriteCGLatch (dev, rtdConvertChanGain (list[0])); + } +} + +/* + "instructions" read/write data in "one-shot" or "software-triggered" + mode (simplest case). + This doesnt use interrupts. + */ +static int rtd_ai_rinsn ( + comedi_device *dev, + comedi_subdevice *s, + comedi_insn *insn, + lsampl_t *data) +{ + int n, ii; + int stat; + /* clear channel gain table */ + /* write channel to multiplexer */ + rtd_load_channelgain_list (dev, 1, &insn->chanspec); + + /* set conversion source */ + RtdAdcConversionSource (dev, 0); /* software */ + + /* wait for mux to settle */ + udelay (RTD_SETTLE_DELAY); + + /* clear any old fifo data */ + RtdClearAdcFifo (dev); + + stat = RtdFifoStatus (dev); /* DEBUG */ + if (stat & FS_ADC_EMPTY) { /* 1 -> not empty */ + printk ("rtd520: Warning: fifo didn't seem to clear! FifoStatus=0x%x\n", + stat); + } + + /* convert n samples */ + for (n=0; n < insn->n; n++) { + s16 d; + /* trigger conversion */ + RtdAdcStart (dev); + + /* wait for conversion to end */ + udelay ((2*RTD_MAX_SPEED + RTD_MAX_SPEED - 1)/1000); + for (ii = 0; ii < RTD_ADC_TIMEOUT; ++ii) { + /* by delaying here, we try to reduce system electrical noise */ + udelay (1); + stat = RtdFifoStatus (dev); + if (stat & FS_ADC_EMPTY) /* 1 -> not empty */ + break; + } + if (ii >= RTD_ADC_TIMEOUT) { + printk ("rtd520: Error: Never got ADC done flag! FifoStatus=0x%x\n", + stat); + return -ETIMEDOUT; + } + + /* read data */ + d = RtdAdcFifoGet (dev); /* get 2s comp value */ + /*printk ("rtd520: Got 0x%x after %d usec\n", d, ii+1);*/ + d = d >> 3; /* low 3 bits are marker lines */ + data[n] = d + 2048; /* convert to comedi unsigned data */ + } + + /* return the number of samples read/written */ + return n; +} + +/* + Fifo is a least half full. Get what we know is there.... Fast! + This uses 1/2 the bus cycles of read_dregs (below). + + The manual claims that we can do a lword read, but it doesn't work here. +*/ +static void ai_read_half_fifo ( + comedi_device *dev, + comedi_subdevice *s) +{ + int ii; + + for (ii = 0; ii < thisboard->fifoLen / 2; ii++) { + s16 d = RtdAdcFifoGet (dev); /* get 2s comp value */ + d = d >> 3; /* low 3 bits are marker lines */ + + if (devpriv->aiCount <= 0) { + /* should never happen */ + devpriv->aiExtraInt++; + continue; + } + + /* check and deal with buffer wrap */ + if (s->async->buf_int_ptr >= s->async->data_len) { + s->async->buf_int_ptr = 0; + comedi_eobuf(dev, s); + } + /* write into buffer */ + *((sampl_t *)((void *)s->async->data + s->async->buf_int_ptr)) + = d + 2048; /* convert to comedi unsigned data */ + s->async->buf_int_count += sizeof(sampl_t); + s->async->buf_int_ptr += sizeof(sampl_t); + devpriv->aiCount--; + } +} + +/* + unknown amout of data is waiting in fifo. +*/ +static void ai_read_dregs ( + comedi_device *dev, + comedi_subdevice *s) +{ + while (RtdFifoStatus (dev) & FS_ADC_EMPTY) { /* 1 -> not empty */ + s16 d = RtdAdcFifoGet (dev); /* get 2s comp value */ + + d = d >> 3; /* low 3 bits are marker lines */ + + if (devpriv->aiCount <= 0) { + /* should never happen */ + devpriv->aiExtraInt++; + continue; + } + + /* check and deal with buffer wrap */ + if (s->async->buf_int_ptr >= s->async->data_len) { + s->async->buf_int_ptr = 0; + comedi_eobuf(dev, s); + } + /* write into buffer */ + *((sampl_t *)((void *)s->async->data + s->async->buf_int_ptr)) + = d + 2048; /* convert to comedi unsigned data */ + s->async->buf_int_count += sizeof(sampl_t); + s->async->buf_int_ptr += sizeof(sampl_t); + devpriv->aiCount--; + } +} + +/* + Handle all rtd520 interrupts. + Runs atomically and is never re-entered. + This is a "slow handler"; other interrupts may be active. + The data conversion may someday happen in a "bottom half". +*/ +static void rtd_interrupt ( + int irq, /* interrupt number (ignored) */ + void *d, /* our data */ + struct pt_regs *regs) /* cpu context (ignored) */ +{ + comedi_device *dev = d; /* must be called "dev" for devpriv */ + u16 status = RtdInterruptStatus (dev); + + devpriv->intCount++; + + /* if interrupt was not caused by our board */ + /* needed??? we dont claim to share interrupt lines */ + if ((0 == status) + || !(dev->attached)) { + return; + } + /* Either end if a sequence (about), or time to flush the fifo (sample) */ + /* You can only interrupt on fifo half full if doing DMA */ + if (status & (IRQM_ADC_ABOUT_CNT | IRQM_ADC_SAMPLE_CNT)) { + comedi_subdevice *s = dev->subdevices + 0; /* analog in subdevice */ + /* only read in big chunks */ + if (RtdFifoStatus (dev) & FS_ADC_HEMPTY) { + ai_read_half_fifo (dev, s); + comedi_bufcheck (dev, s); /* signal something there */ + } + + if (status & IRQM_ADC_ABOUT_CNT) { /* about counter terminated */ + if (devpriv->aboutWrap) { /* multi-count wraps */ + if (0 == devpriv->aiCount) { /* done! stop! */ + RtdPacerStop (dev); /* Stop PACER */ + RtdInterruptMask (dev, 0); /* mask out ABOUT and SAMPLE */ + ai_read_dregs (dev, s); + comedi_done (dev, s); /* signal end to comedi */ + } else if (devpriv->aiCount < devpriv->aboutWrap) { + RtdAboutStopEnable (dev, 0); /* enable stop */ + devpriv->aboutWrap = 0; + } + } else { /* done */ + /* TODO: allow multiple interrupt sources */ + RtdInterruptMask (dev, 0);/* mask out ABOUT and SAMPLE */ + ai_read_dregs (dev, s); + + comedi_done (dev, s); /* signal end to comedi */ + } + } + + } + /* clear the interrupt */ + RtdInterruptClearMask (dev, status); + RtdInterruptClear (dev); +} + +/* + cmdtest tests a particular command to see if it is valid. + Using the cmdtest ioctl, a user can create a valid cmd + and then have it executed by the cmd ioctl (asyncronously). + + cmdtest returns 1,2,3,4 or 0, depending on which tests + the command passes. +*/ + +static int +rtd_ai_cmdtest ( + comedi_device *dev, + comedi_subdevice *s, + comedi_cmd *cmd) +{ + int err=0; + int tmp; + + /* step 1: make sure trigger sources are trivially valid */ + + tmp = cmd->start_src; + cmd->start_src &= TRIG_NOW; + if (!cmd->start_src && tmp != cmd->start_src) { + printk ("rtd520: cmdtest error! start_src does not include NOW %x\n", + cmd->start_src); + err++; + } + + tmp=cmd->scan_begin_src; + cmd->scan_begin_src &= TRIG_TIMER|TRIG_EXT; + if (!cmd->scan_begin_src && tmp != cmd->scan_begin_src) { + printk ("rtd520: cmdtest error! scan_begin_src includes neither TIMER or EXT %x\n", + cmd->scan_begin_src); + err++; + } + + tmp=cmd->convert_src; + cmd->convert_src &= TRIG_TIMER|TRIG_EXT; + if (!cmd->convert_src && tmp != cmd->convert_src) { + printk ("rtd520: cmdtest error! convert_src includes neither TIMER or EXT %x\n", + cmd->convert_src); + err++; + } + + tmp=cmd->scan_end_src; + cmd->scan_end_src &= TRIG_COUNT; + if (!cmd->scan_end_src && tmp != cmd->scan_end_src) { + printk ("rtd520: cmdtest error! scan_end_src does not include COUNT %x\n", + cmd->scan_end_src); + err++; + } + + tmp=cmd->stop_src; + cmd->stop_src &= TRIG_COUNT|TRIG_NONE; + if (!cmd->stop_src && tmp != cmd->stop_src) { + printk ("rtd520: cmdtest error! stop_src includes neither COUNT or NONE %x\n", + cmd->stop_src); + err++; + } + + if (err) + return 1; + + /* step 2: make sure trigger sources are unique + and mutually compatible */ +#if 0 + /* note that mutual compatiblity is not an issue here */ + if (cmd->scan_begin_src !=TRIG_TIMER && + cmd->scan_begin_src !=TRIG_EXT) { + err++; + } + if (cmd->convert_src !=TRIG_TIMER && + cmd->convert_src !=TRIG_EXT) { + err++; + } + if (cmd->stop_src != TRIG_TIMER && + cmd->stop_src != TRIG_EXT) { + err++; + } + + if (err) { + printk ("rtd520: cmdtest error! Some trigger compatibility test failed.\n"); + return 2; + } +#endif + + /* step 3: make sure arguments are trivially compatible */ + + if (cmd->start_arg != 0) { + cmd->start_arg = 0; + printk ("rtd520: cmdtest: start_arg not 0\n"); + err++; + } + + if (cmd->scan_begin_src == TRIG_TIMER){ + if (cmd->scan_begin_arg < RTD_MAX_SPEED) { + cmd->scan_begin_arg = RTD_MAX_SPEED; + printk ("rtd520: cmdtest: scan rate greater than max.\n"); + err++; + } + if (cmd->scan_begin_arg > RTD_MIN_SPEED) { + cmd->scan_begin_arg = RTD_MIN_SPEED; + printk ("rtd520: cmdtest: scan rate lower than min.\n"); + err++; + } + } else { + /* external trigger */ + /* should be level/edge, hi/lo specification here */ + /* should specify multiple external triggers */ + if (cmd->scan_begin_arg > 9) { + cmd->scan_begin_arg = 9; + printk ("rtd520: cmdtest: scan_begin_arg out of range\n"); + err++; + } + } + if (cmd->convert_src==TRIG_TIMER) { + if (cmd->convert_arg < RTD_MAX_SPEED) { + cmd->convert_arg = RTD_MAX_SPEED; + printk ("rtd520: cmdtest: convert rate greater than max.\n"); + err++; + } + if (cmd->convert_arg > RTD_MIN_SPEED) { + cmd->convert_arg = RTD_MIN_SPEED; + printk ("rtd520: cmdtest: convert rate lower than min.\n"); + err++; + } + } else { + /* external trigger */ + /* see above */ + if (cmd->convert_arg > 9) { + cmd->convert_arg = 9; + printk ("rtd520: cmdtest: convert_arg out of range\n"); + err++; + } + } + +#if 0 + if (cmd->scan_end_arg != cmd->chanlist_len) { + cmd->scan_end_arg = cmd->chanlist_len; + err++; + } +#endif + if (cmd->stop_src==TRIG_COUNT) { + /* TODO check for rounding error due to counter wrap */ + + } else { + /* TRIG_NONE */ + if (cmd->stop_arg!=0) { + cmd->stop_arg=0; + printk ("rtd520: cmdtest: stop_arg not 0\n"); + err++; + } + } + + if (err) { + printk ("rtd520: cmdtest error! Some argument compatibility test failed.\n"); + return 3; + } + + /* step 4: fix up any arguments */ + + if (cmd->scan_begin_src == TRIG_TIMER) { + tmp=cmd->scan_begin_arg; + rtd_ns_to_timer(&cmd->scan_begin_arg, + cmd->flags&TRIG_ROUND_MASK); + if (tmp!=cmd->scan_begin_arg) { + err++; + } + } + if (cmd->convert_src == TRIG_TIMER){ + tmp=cmd->convert_arg; + rtd_ns_to_timer(&cmd->convert_arg, + cmd->flags&TRIG_ROUND_MASK); + if (tmp!=cmd->convert_arg) { + err++; + } + if (cmd->scan_begin_src == TRIG_TIMER + && (cmd->scan_begin_arg + < (cmd->convert_arg * cmd->scan_end_arg))) { + cmd->scan_begin_arg=cmd->convert_arg*cmd->scan_end_arg; + err++; + } + } + + if (err) { + printk ("rtd520: cmdtest error! Some timer value was altered.\n"); + return 4; + } + + return 0; +} + +/* + Execute a analog in command with many possible triggering options. + The data get stored in the async structure of the subdevice. + This is usually done by an interrupt handler. + Userland gets to the data using read calls. +*/ +static int rtd_ai_cmd ( + comedi_device *dev, + comedi_subdevice *s) +{ + comedi_cmd *cmd=&s->async->cmd; + int timer; + int justPoll = 0; /* can we do a simple poll */ + + /* stop anything currently running */ + RtdPacerStop (dev); /* Stop PACER */ + + /* start configuration */ + rtd_load_channelgain_list (dev, cmd->chanlist_len, cmd->chanlist); + + /* setup the common case and override if needed */ + if (cmd->chanlist_len > 1) { + printk ("rtd520: Multi channel setup\n"); + RtdPacerStartSource (dev, 0); /* software triggers pacer */ + RtdBurstStartSource (dev, 1); /* PACER triggers burst */ + RtdAdcConversionSource (dev, 2); /* BURST triggers ADC */ + } else { /* single channel */ + printk ("rtd520: single channel setup\n"); + RtdPacerStartSource (dev, 0); /* software triggers pacer */ + RtdAdcConversionSource (dev, 1); /* PACER triggers ADC */ + } + + 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) */ + RtdPacerClockSource (dev, 1); /* use INTERNAL 8Mhz clock source */ + RtdAdcSampleCounterSource (dev, 1); /* count samples, not scans */ + + /* BUG!!! these look like enumerated values, but they are bit fields */ + + /* 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)) { + justPoll = 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 */ + printk ("rtd520: loading %d into about\n", n - 1); + devpriv->aboutWrap = 0; + RtdAboutCounter (dev, n - 1); + } else { /* multiple counter wraps */ + int mm, dd; + + /* interrupt on ABOUT wrap, until last wrap */ + mm = n & 0xffff; + dd = n / mm; /* effective divisor */ + while (mm < 0xfff) { /* make sure we have time to arm */ + dd++; + mm = (n + dd-1) / dd; /* round up, if needed */ + } + /* TODO fix round error */ + printk ("rtd520: Warning! count value %d > 65536 using %d\n", + n, mm); + devpriv->aboutWrap = mm; + RtdAboutCounter (dev, mm-1); + RtdAboutStopEnable (dev, 1); /* just interrupt */ + } + } + + break; + + case TRIG_NONE: /* stop when cancel is called */ + RtdPacerStopSource (dev, 0); /* stop on SOFTWARE stop */ + break; + + default: + printk ("rtd520: Warning! ignoring stop_src mode %d\n", + cmd->stop_src); + } + + + /* Scan timing */ + switch (cmd->scan_begin_src) { + case TRIG_TIMER: /* periodic scanning */ + timer=rtd_ns_to_timer(&cmd->scan_begin_arg,TRIG_ROUND_NEAREST); + /* set PACER clock */ + printk ("rtd520: loading %d into pacer for %dns\n", + timer, cmd->scan_begin_arg); + RtdPacerCounter (dev, timer); + + break; + + case TRIG_EXT: + RtdPacerStartSource (dev, 1); /* EXTERNALy trigger pacer */ + break; + + default: + printk ("rtd520: Warning! ignoring scan_begin_src mode %d\n", + cmd->scan_begin_src); + } + + /* Sample timing within a scan */ + switch(cmd->convert_src){ + case TRIG_TIMER: /* periodic */ + timer=rtd_ns_to_timer(&cmd->convert_arg,TRIG_ROUND_NEAREST); + /* setup BURST clock */ + printk ("rtd520: loading %d into burst\n", timer); + RtdBurstCounter (dev, timer); + + break; + + case TRIG_EXT: /* external */ + RtdBurstStartSource (dev, 2); /* EXTERNALy trigger burst */ + break; + + default: + printk ("rtd520: Warning! ignoring convert_src mode %d\n", + cmd->convert_src); + } + + + /* end configuration */ + + /* start_src is ASSUMED to be TRIG_NOW */ + /* initial settling */ + udelay (RTD_SETTLE_DELAY); + /* clear any old data */ + RtdClearAdcFifo (dev); + + /* see if we can do a simple polled input */ + if (justPoll) { + int stat = RtdFifoStatus (dev); /* DEBUG */ + s16 d; + int ii; + + /* DEBUG */ + if (stat & FS_ADC_EMPTY) { /* 1 -> not empty */ + printk ("rtd520: ai_cmd Warning: fifo didn't seem to clear! FifoStatus=0x%x\n", + stat); + } else { + printk ("rtd520: ai_cmd: polling for sample.\n"); + } + + /* trigger conversion */ + RtdAdcStart (dev); + + udelay ((2*RTD_MAX_SPEED + RTD_MAX_SPEED - 1)/1000); + /* right now, this means just 1 sample. emulate ai_rinsn */ + for (ii = 0; ii < RTD_ADC_TIMEOUT; ++ii) { + /* by delaying here, we try to reduce system electrical noise */ + udelay (1); + stat = RtdFifoStatus (dev); + if (stat & FS_ADC_EMPTY) /* 1 -> not empty */ + break; + } + if (ii >= RTD_ADC_TIMEOUT) { + printk ("rtd520: ai_cmd Error: Never got data in FIFO! FifoStatus=0x%x\n", + stat); + return -ETIMEDOUT; + } + + /* read data */ + d = RtdAdcFifoGet (dev); /* get 2s comp value */ + /*printk ("rtd520: Got 0x%x after %d usec\n", d, ii+1);*/ + d = d >> 3; /* low 3 bits are marker lines */ + + /* write into buffer */ + *((sampl_t *)((void *)s->async->data + s->async->buf_int_ptr)) + = d + 2048; /* convert to comedi unsigned data */ + s->async->buf_int_count += sizeof(sampl_t); + s->async->buf_int_ptr += sizeof(sampl_t); + comedi_done (dev, s); + } else { + /* interrupt setup */ + if (! dev->irq) { + printk ("rtd520: ERROR! No interrupt available!\n"); + return -ENXIO; + } + + printk("rtd520: using interrupts. (%ld ints, %ld extra ai)\n(int status 0x%x, overrun status 0x%x, fifo status 0x%x)\n", + devpriv->intCount, devpriv->aiExtraInt, + 0xffff & RtdInterruptStatus (dev), + 0xffff & RtdInterruptOverrunStatus (dev), + 0xffff & RtdFifoStatus (dev)); + + RtdInterruptClearMask (dev, ~0); /* clear any existing flags */ + RtdInterruptClear (dev); + /*DEBUG RtdInterruptOverrunClear(dev);*/ + + /* TODO: allow multiple interrupt sources */ + if (devpriv->aiCount > 512) { + RtdInterruptMask (dev, IRQM_ADC_ABOUT_CNT | IRQM_ADC_SAMPLE_CNT ); + } else { + RtdInterruptMask (dev, IRQM_ADC_ABOUT_CNT); + } + + RtdPacerStart (dev); /* Start PACER */ + } + return 0; +} + +/* + Stop a running data aquisition. +*/ +static int rtd_ai_cancel ( + comedi_device *dev, + comedi_subdevice *s) +{ + /* more is probably needed here */ + RtdPacerStop (dev); /* Stop PACER */ + RtdAdcConversionSource (dev, 0); /* software trigger only */ + return 0; +} + +/* + Given a desired period and the clock period (both in ns), + return the proper counter value (divider-1). + Sets the original period to be the true value. + Note: you have to check if the value is larger than the counter range! +*/ +static int rtd_ns_to_timer_base ( + unsigned int *nanosec, /* desired period (in ns) */ + int round_mode, + double base) /* clock period (in ns) */ +{ + int divider; + + switch(round_mode){ + case TRIG_ROUND_NEAREST: + default: + divider=(*nanosec+base/2)/base; + break; + case TRIG_ROUND_DOWN: + divider=(*nanosec)/base; + break; + case TRIG_ROUND_UP: + divider=(*nanosec+base-1)/base; + break; + } + if (divider < 2) divider = 2; /* min is divide by 2 */ + + /* Note: we don't check for max, because different timers + have different ranges */ + + *nanosec=base*divider; + return divider - 1; /* countdown is divisor+1 */ +} + +/* + Given a desired period (in ns), + return the proper counter value (divider-1) for the internal clock. + Sets the original period to be the true value. +*/ +static int rtd_ns_to_timer ( + unsigned int *ns, + int round_mode) +{ + return rtd_ns_to_timer_base (ns, round_mode, RTD_CLOCK_BASE); +} + +static int rtd_ao_winsn ( + comedi_device *dev, + comedi_subdevice *s, + comedi_insn *insn, + lsampl_t *data) +{ + int i; + int chan = CR_CHAN(insn->chanspec); + + /* Writing a list of values to an AO channel is probably not + * very useful, but that's how the interface is defined. */ + for(i=0;in;i++){ + /* a typical programming sequence */ + //outw(data[i],dev->iobase + RTD_DA0 + chan); + devpriv->ao_readback[chan] = data[i]; + } + + /* return the number of samples read/written */ + return 1; +} + +/* AO subdevices should have a read insn as well as a write insn. + * Usually this means copying a value stored in devpriv. */ +static int rtd_ao_rinsn ( + comedi_device *dev, + comedi_subdevice *s, + comedi_insn *insn, + lsampl_t *data) +{ + int i; + int chan = CR_CHAN(insn->chanspec); + + for(i=0;in;i++) + data[i] = devpriv->ao_readback[chan]; + + return i; +} + +/* DIO devices are slightly special. Although it is possible to + * implement the insn_read/insn_write interface, it is much more + * useful to applications if you implement the insn_bits interface. + * This allows packed reading/writing of the DIO channels. The + * comedi core can convert between insn_bits and insn_read/write */ +static int rtd_dio_insn_bits ( + comedi_device *dev, + comedi_subdevice *s, + comedi_insn *insn, + lsampl_t *data) +{ + if (insn->n!=2) return -EINVAL; + + /* The insn data is a mask in data[0] and the new data + * in data[1], each channel cooresponding to a bit. */ + if (data[0]) { + s->state &= ~data[0]; + s->state |= data[0]&data[1]; + /* Write out the new digital output lines */ + //outw(s->state,dev->iobase + RTD_DIO); + } + /* on return, data[1] contains the value of the digital + * input lines. */ + //data[1]=inw(dev->iobase + RTD_DIO); + + return 2; +} + +static int rtd_dio_insn_config ( + comedi_device *dev, + comedi_subdevice *s, + comedi_insn *insn, + lsampl_t *data) +{ + int chan=CR_CHAN(insn->chanspec); + + if (insn->n!=1) return -EINVAL; + + /* The input or output configuration of each digital line is + * configured by a special insn_config instruction. chanspec + * contains the channel to be changed, and data[0] contains the + * value COMEDI_INPUT or COMEDI_OUTPUT. */ + + if (data[0]==COMEDI_OUTPUT) { + s->io_bits |= 1<io_bits &= ~(1<io_bits,dev->iobase + RTD_DIO_CONFIG); + + return 1; +} + + +/* + * A convenient macro that defines init_module() and cleanup_module(), + * as necessary. + */ +COMEDI_INITCLEANUP(rtd520Driver); + diff --git a/comedi/drivers/rtd520.h b/comedi/drivers/rtd520.h new file mode 100644 index 00000000..1dea123b --- /dev/null +++ b/comedi/drivers/rtd520.h @@ -0,0 +1,455 @@ +/* + comedi/drivers/rtd520.h + Comedi driver defines for Real Time Devices (RTD) PCI4520/DM7520 + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 2001 David A. Schleef + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + Created by Dan Christian, NASA Ames Research Center. + See board notes in rtd520.c +*/ + +/* + LAS0 Runtime Area + Local Address Space 0 Offset Read Function Write Function +*/ +#define LAS0_SPARE_00 0x0000 // - - +#define LAS0_SPARE_04 0x0004 // - - +#define LAS0_USER_IO 0x0008 // Read User Inputs Write User Outputs +#define LAS0_SPARE_0C 0x000C // - - +#define LAS0_ADC 0x0010 // Read FIFO Status Software A/D Start +#define LAS0_DAC1 0x0014 // - Software D/A1 Update +#define LAS0_DAC2 0x0018 // - Software D/A2 Update +#define LAS0_SPARE_1C 0x001C // - - +#define LAS0_SPARE_20 0x0020 // - - +#define LAS0_DAC 0x0024 // - Software Simultaneous D/A1 and D/A2 Update +#define LAS0_PACER 0x0028 // Software Pacer Start Software Pacer Stop +#define LAS0_TIMER 0x002C // Read Timer Counters Status HDIN Software Trigger +#define LAS0_IT 0x0030 // Read Interrupt Status Write Interrupt Enable Mask Register +#define LAS0_CLEAR 0x0034 // Clear ITs set by Clear Mask Set Interrupt Clear Mask +#define LAS0_OVERRUN 0x0038 // Read pending interrupts Clear Overrun Register +#define LAS0_SPARE_3C 0x003C // - - + +/* + LAS0 Runtime Area Timer/Counter,Dig.IO + Name Local Address Function +*/ +#define LAS0_PCLK 0x0040 // Pacer Clock value (24bit) Pacer Clock load (24bit) +#define LAS0_BCLK 0x0044 // Burst Clock value (10bit) Burst Clock load (10bit) +#define LAS0_ADC_SCNT 0x0048 // A/D Sample counter value (10bit) A/D Sample counter load (10bit) +#define LAS0_DAC1_UCNT 0x004C // D/A1 Update counter value (10 bit) D/A1 Update counter load (10bit) +#define LAS0_DAC2_UCNT 0x0050 // D/A2 Update counter value (10 bit) D/A2 Update counter load (10bit) +#define LAS0_DCNT 0x0054 // Delay counter value (16 bit) Delay counter load (16bit) +#define LAS0_ACNT 0x0058 // About counter value (16 bit) About counter load (16bit) +#define LAS0_DAC_CLK 0x005C // DAC clock value (16bit) DAC clock load (16bit) +#define LAS0_UTC0 0x0060 // 8254 TC Counter 0 User TC 0 value Load count in TC Counter 0 +#define LAS0_UTC1 0x0064 // 8254 TC Counter 1 User TC 1 value Load count in TC Counter 1 +#define LAS0_UTC2 0x0068 // 8254 TC Counter 2 User TC 2 value Load count in TC Counter 2 +#define LAS0_UTC_CTRL 0x006C // 8254 TC Control Word Program counter mode for TC +#define LAS0_DIO0 0x0070 // Digital I/O Port 0 Read Port Digital I/O Port 0 Write Port +#define LAS0_DIO1 0x0074 // Digital I/O Port 1 Read Port Digital I/O Port 1 Write Port +#define LAS0_DIO0_CTRL 0x0078 // Clear digital IRQ status flag/read Clear digital chip/program Port 0 +#define LAS0_DIO_STATUS 0x007C // Read Digital I/O Status word Program digital control register & + +/* + LAS0 Setup Area + Name Local Address Function +*/ +#define LAS0_BOARD_RESET 0x0100 // Board reset +#define LAS0_DMA0_SRC 0x0104 // DMA 0 Sources select +#define LAS0_DMA1_SRC 0x0108 // DMA 1 Sources select +#define LAS0_ADC_CONVERSION 0x010C // A/D Conversion Signal select +#define LAS0_BURST_START 0x0110 // Burst Clock Start Trigger select +#define LAS0_PACER_START 0x0114 // Pacer Clock Start Trigger select +#define LAS0_PACER_STOP 0x0118 // Pacer Clock Stop Trigger select +#define LAS0_ACNT_STOP_ENABLE 0x011C // About Counter Stop Enable +#define LAS0_PACER_REPEAT 0x0120 // Pacer Start Trigger Mode select +#define LAS0_DIN_START 0x0124 // High Speed Digital Input Sampling Signal select +#define LAS0_DIN_FIFO_CLEAR 0x0128 // Digital Input FIFO Clear +#define LAS0_ADC_FIFO_CLEAR 0x012C // A/D FIFO Clear +#define LAS0_CGT_WRITE 0x0130 // Channel Gain Table Write +#define LAS0_CGL_WRITE 0x0134 // Channel Gain Latch Write +#define LAS0_CG_DATA 0x0138 // Digital Table Write +#define LAS0_CGT_ENABLE 0x013C // Channel Gain Table Enable +#define LAS0_CG_ENABLE 0x0140 // Digital Table Enable +#define LAS0_CGT_PAUSE 0x0144 // Table Pause Enable +#define LAS0_CGT_RESET 0x0148 // Reset Channel Gain Table +#define LAS0_CGT_CLEAR 0x014C // Clear Channel Gain Table +#define LAS0_DAC1_CTRL 0x0150 // D/A1 output type/range +#define LAS0_DAC1_SRC 0x0154 // D/A1 update source +#define LAS0_DAC1_CYCLE 0x0158 // D/A1 cycle mode +#define LAS0_DAC1_RESET 0x015C // D/A1 FIFO reset +#define LAS0_DAC1_FIFO_CLEAR 0x0160 // D/A1 FIFO clear +#define LAS0_DAC2_CTRL 0x0164 // D/A2 output type/range +#define LAS0_DAC2_SRC 0x0168 // D/A2 update source +#define LAS0_DAC2_CYCLE 0x016C // D/A2 cycle mode +#define LAS0_DAC2_RESET 0x0170 // D/A2 FIFO reset +#define LAS0_DAC2_FIFO_CLEAR 0x0174 // D/A2 FIFO clear +#define LAS0_ADC_SCNT_SRC 0x0178 // A/D Sample Counter Source select +#define LAS0_PACER_SELECT 0x0180 // Pacer Clock select +#define LAS0_SBUS0_SRC 0x0184 // SyncBus 0 Source select +#define LAS0_SBUS0_ENABLE 0x0188 // SyncBus 0 enable +#define LAS0_SBUS1_SRC 0x018C // SyncBus 1 Source select +#define LAS0_SBUS1_ENABLE 0x0190 // SyncBus 1 enable +#define LAS0_SBUS2_SRC 0x0198 // SyncBus 2 Source select +#define LAS0_SBUS2_ENABLE 0x019C // SyncBus 2 enable +#define LAS0_ETRG_POLARITY 0x01A4 // External Trigger polarity select +#define LAS0_EINT_POLARITY 0x01A8 // External Interrupt polarity select +#define LAS0_UTC0_CLOCK 0x01AC // UTC0 Clock select +#define LAS0_UTC0_GATE 0x01B0 // UTC0 Gate select +#define LAS0_UTC1_CLOCK 0x01B4 // UTC1 Clock select +#define LAS0_UTC1_GATE 0x01B8 // UTC1 Gate select +#define LAS0_UTC2_CLOCK 0x01BC // UTC2 Clock select +#define LAS0_UTC2_GATE 0x01C0 // UTC2 Gate select +#define LAS0_UOUT0_SELECT 0x01C4 // User Output 0 source select +#define LAS0_UOUT1_SELECT 0x01C8 // User Output 1 source select +#define LAS0_DMA0_RESET 0x01CC // DMA0 Request state machine reset +#define LAS0_DMA1_RESET 0x01D0 // DMA1 Request state machine reset + +/* + LAS1 + Name Local Address Function +*/ +#define LAS1_ADC_FIFO 0x0000 // Read A/D FIFO (16bit) - +#define LAS1_HDIO_FIFO 0x0004 // Read High Speed Digital Input FIFO (16bit) - +#define LAS1_DAC1_FIFO 0x0008 // - Write D/A1 FIFO (16bit) +#define LAS1_DAC2_FIFO 0x000C // - Write D/A2 FIFO (16bit) + +/* + LCFG: PLX 9080 local config & runtime registers + Name Local Address Function +*/ +#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_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 LCFG_DMAPADR1 0x0098 // DMA Channel 1 PCI Address Register +#define LCFG_DMALADR1 0x009C // DMA Channel 1 Local Address Register +#define LCFG_DMASIZ1 0x00A0 // DMA Channel 1 Transfer Size (Bytes) Register +#define LCFG_DMADPR1 0x00A4 // DMA Channel 1 Descriptor Pointer Register +#define LCFG_DMACSR0 0x00A8 // DMA Channel 0 Command/Status Register +#define LCFG_DMACSR1 0x00A9 // DMA Channel 0 Command/Status Register +#define LCFG_DMAARB 0x00AC // DMA Arbitration Register +#define LCFG_DMATHR 0x00B0 // DMA Threshold Register + + + +/*====================================================================== + Resister bit definitions +======================================================================*/ + +// FIFO Status Word Bits (RtdFifoStatus) +#define FS_DAC1_EMPTY 0x0001 // D0 - DAC1 FIFO not empty +#define FS_DAC1_HEMPTY 0x0002 // D1 - DAC1 FIFO not half empty +#define FS_DAC1_FULL 0x0004 // D2 - DAC1 FIFO not full +#define FS_DAC2_EMPTY 0x0010 // D4 - DAC2 FIFO not empty +#define FS_DAC2_HEMPTY 0x0020 // D5 - DAC2 FIFO not half empty +#define FS_DAC2_FULL 0x0040 // D6 - DAC2 FIFO not full +#define FS_ADC_EMPTY 0x0100 // D8 - ADC FIFO not empty +#define FS_ADC_HEMPTY 0x0200 // D9 - ADC FIFO not half empty +#define FS_ADC_FULL 0x0400 // D10 - ADC FIFO not full +#define FS_DIN_EMPTY 0x1000 // D12 - DIN FIFO not empty +#define FS_DIN_HEMPTY 0x2000 // D13 - DIN FIFO not half empty +#define FS_DIN_FULL 0x4000 // D14 - DIN FIFO not full + +// Timer Status Word Bits (GetTimerStatus) +#define TS_PCLK_GATE 0x0001 +// D0 - Pacer Clock Gate [0 - gated, 1 - enabled] +#define TS_BCLK_GATE 0x0002 +// D1 - Burst Clock Gate [0 - disabled, 1 - running] +#define TS_DCNT_GATE 0x0004 +// D2 - Pacer Clock Delayed Start Trigger [0 - delay over, 1 - delay in +// progress] +#define TS_ACNT_GATE 0x0008 +// D3 - Pacer Clock About Trigger [0 - completed, 1 - in progress] +#define TS_PCLK_RUN 0x0010 +// D4 - Pacer Clock Shutdown Flag [0 - Pacer Clock cannot be start +// triggered only by Software Pacer Start Command, 1 - Pacer Clock can +// be start triggered] + + +// External Trigger polarity select +// External Interrupt polarity select +#define POL_POSITIVE 0x0 // positive edge +#define POL_NEGATIVE 0x1 // negative edge + +// User Output Signal select (SetUout0Source, SetUout1Source) +#define UOUT_ADC 0x0 // A/D Conversion Signal +#define UOUT_DAC1 0x1 // D/A1 Update +#define UOUT_DAC2 0x2 // D/A2 Update +#define UOUT_SOFTWARE 0x3 // Software Programmable + +// Pacer clock select (SetPacerSource) +#define PCLK_INTERNAL 1 // Internal Pacer Clock +#define PCLK_EXTERNAL 0 // External Pacer Clock + +// A/D Sample Counter Sources (SetAdcntSource, SetupSampleCounter) +#define ADC_SCNT_CGT_RESET 0x0 // needs restart with StartPacer +#define ADC_SCNT_FIFO_WRITE 0x1 + +// A/D Conversion Signal Select (for SetConversionSelect) +#define ADC_START_SOFTWARE 0x0 // Software A/D Start +#define ADC_START_PCLK 0x1 // Pacer Clock (Ext. Int. see Func.509) +#define ADC_START_BCLK 0x2 // Burst Clock +#define ADC_START_DIGITAL_IT 0x3 // Digital Interrupt +#define ADC_START_DAC1_MARKER1 0x4 // D/A 1 Data Marker 1 +#define ADC_START_DAC2_MARKER1 0x5 // D/A 2 Data Marker 1 +#define ADC_START_SBUS0 0x6 // SyncBus 0 +#define ADC_START_SBUS1 0x7 // SyncBus 1 +#define ADC_START_SBUS2 0x8 // SyncBus 2 + +// Burst Clock start trigger select (SetBurstStart) +#define BCLK_START_SOFTWARE 0x0 // Software A/D Start (StartBurst) +#define BCLK_START_PCLK 0x1 // Pacer Clock +#define BCLK_START_ETRIG 0x2 // External Trigger +#define BCLK_START_DIGITAL_IT 0x3 // Digital Interrupt +#define BCLK_START_SBUS0 0x4 // SyncBus 0 +#define BCLK_START_SBUS1 0x5 // SyncBus 1 +#define BCLK_START_SBUS2 0x6 // SyncBus 2 + +// Pacer Clock start trigger select (SetPacerStart) +#define PCLK_START_SOFTWARE 0x0 // Software Pacer Start (StartPacer) +#define PCLK_START_ETRIG 0x1 // External trigger +#define PCLK_START_DIGITAL_IT 0x2 // Digital interrupt +#define PCLK_START_UTC2 0x3 // User TC 2 out +#define PCLK_START_SBUS0 0x4 // SyncBus 0 +#define PCLK_START_SBUS1 0x5 // SyncBus 1 +#define PCLK_START_SBUS2 0x6 // SyncBus 2 +#define PCLK_START_D_SOFTWARE 0x8 // Delayed Software Pacer Start +#define PCLK_START_D_ETRIG 0x9 // Delayed external trigger +#define PCLK_START_D_DIGITAL_IT 0xA // Delayed digital interrupt +#define PCLK_START_D_UTC2 0xB // Delayed User TC 2 out +#define PCLK_START_D_SBUS0 0xC // Delayed SyncBus 0 +#define PCLK_START_D_SBUS1 0xD // Delayed SyncBus 1 +#define PCLK_START_D_SBUS2 0xE // Delayed SyncBus 2 +#define PCLK_START_ETRIG_GATED 0xF // External Trigger Gated controlled mode + +// Pacer Clock Stop Trigger select (SetPacerStop) +#define PCLK_STOP_SOFTWARE 0x0 // Software Pacer Stop (StopPacer) +#define PCLK_STOP_ETRIG 0x1 // External Trigger +#define PCLK_STOP_DIGITAL_IT 0x2 // Digital Interrupt +#define PCLK_STOP_ACNT 0x3 // About Counter +#define PCLK_STOP_UTC2 0x4 // User TC2 out +#define PCLK_STOP_SBUS0 0x5 // SyncBus 0 +#define PCLK_STOP_SBUS1 0x6 // SyncBus 1 +#define PCLK_STOP_SBUS2 0x7 // SyncBus 2 +#define PCLK_STOP_A_SOFTWARE 0x8 // About Software Pacer Stop +#define PCLK_STOP_A_ETRIG 0x9 // About External Trigger +#define PCLK_STOP_A_DIGITAL_IT 0xA // About Digital Interrupt +#define PCLK_STOP_A_UTC2 0xC // About User TC2 out +#define PCLK_STOP_A_SBUS0 0xD // About SyncBus 0 +#define PCLK_STOP_A_SBUS1 0xE // About SyncBus 1 +#define PCLK_STOP_A_SBUS2 0xF // About SyncBus 2 + +// About Counter Stop Enable +#define ACNT_STOP 0x0 // stop enable +#define ACNT_NO_STOP 0x1 // stop disabled + +// DAC update source (SetDAC1Start & SetDAC2Start) +#define DAC_START_SOFTWARE 0x0 // Software Update +#define DAC_START_CGT 0x1 // CGT controlled Update +#define DAC_START_DAC_CLK 0x2 // D/A Clock +#define DAC_START_EPCLK 0x3 // External Pacer Clock +#define DAC_START_SBUS0 0x4 // SyncBus 0 +#define DAC_START_SBUS1 0x5 // SyncBus 1 +#define DAC_START_SBUS2 0x6 // SyncBus 2 + +// DAC Cycle Mode (SetDAC1Cycle, SetDAC2Cycle, SetupDAC) +#define DAC_CYCLE_SINGLE 0x0 // not cycle +#define DAC_CYCLE_MULTI 0x1 // cycle + +// 8254 Operation Modes (Set8254Mode, SetupTimerCounter) +#define M8254_EVENT_COUNTER 0 // Event Counter +#define M8254_HW_ONE_SHOT 1 // Hardware-Retriggerable One-Shot +#define M8254_RATE_GENERATOR 2 // Rate Generator +#define M8254_SQUARE_WAVE 3 // Square Wave Mode +#define M8254_SW_STROBE 4 // Software Triggered Strobe +#define M8254_HW_STROBE 5 // Hardware Triggered Strobe (Retriggerable) + +// User Timer/Counter 0 Clock Select (SetUtc0Clock) +#define CUTC0_8MHZ 0x0 // 8MHz +#define CUTC0_EXT_TC_CLOCK1 0x1 // Ext. TC Clock 1 +#define CUTC0_EXT_TC_CLOCK2 0x2 // Ext. TC Clock 2 +#define CUTC0_EXT_PCLK 0x3 // Ext. Pacer Clock + +// User Timer/Counter 1 Clock Select (SetUtc1Clock) +#define CUTC1_8MHZ 0x0 // 8MHz +#define CUTC1_EXT_TC_CLOCK1 0x1 // Ext. TC Clock 1 +#define CUTC1_EXT_TC_CLOCK2 0x2 // Ext. TC Clock 2 +#define CUTC1_EXT_PCLK 0x3 // Ext. Pacer Clock +#define CUTC1_UTC0_OUT 0x4 // User Timer/Counter 0 out +#define CUTC1_DIN_SIGNAL 0x5 // High-Speed Digital Input Sampling signal + +// User Timer/Counter 2 Clock Select (SetUtc2Clock) +#define CUTC2_8MHZ 0x0 // 8MHz +#define CUTC2_EXT_TC_CLOCK1 0x1 // Ext. TC Clock 1 +#define CUTC2_EXT_TC_CLOCK2 0x2 // Ext. TC Clock 2 +#define CUTC2_EXT_PCLK 0x3 // Ext. Pacer Clock +#define CUTC2_UTC1_OUT 0x4 // User Timer/Counter 1 out + +// User Timer/Counter 0 Gate Select (SetUtc0Gate) +#define GUTC0_NOT_GATED 0x0 // Not gated +#define GUTC0_GATED 0x1 // Gated +#define GUTC0_EXT_TC_GATE1 0x2 // Ext. TC Gate 1 +#define GUTC0_EXT_TC_GATE2 0x3 // Ext. TC Gate 2 + +// User Timer/Counter 1 Gate Select (SetUtc1Gate) +#define GUTC1_NOT_GATED 0x0 // Not gated +#define GUTC1_GATED 0x1 // Gated +#define GUTC1_EXT_TC_GATE1 0x2 // Ext. TC Gate 1 +#define GUTC1_EXT_TC_GATE2 0x3 // Ext. TC Gate 2 +#define GUTC1_UTC0_OUT 0x4 // User Timer/Counter 0 out + +// User Timer/Counter 2 Gate Select (SetUtc2Gate) +#define GUTC2_NOT_GATED 0x0 // Not gated +#define GUTC2_GATED 0x1 // Gated +#define GUTC2_EXT_TC_GATE1 0x2 // Ext. TC Gate 1 +#define GUTC2_EXT_TC_GATE2 0x3 // Ext. TC Gate 2 +#define GUTC2_UTC1_OUT 0x4 // User Timer/Counter 1 out + +// Interrupt Source Masks (SetITMask, ClearITMask, GetITStatus) +#define IRQM_ADC_FIFO_WRITE 0x0001 // ADC FIFO Write +#define IRQM_CGT_RESET 0x0002 // Reset CGT +#define IRQM_CGT_PAUSE 0x0008 // Pause CGT +#define IRQM_ADC_ABOUT_CNT 0x0010 // About Counter out +#define IRQM_ADC_DELAY_CNT 0x0020 // Delay Counter out +#define IRQM_ADC_SAMPLE_CNT 0x0040 // ADC Sample Counter +#define IRQM_DAC1_UCNT 0x0080 // DAC1 Update Counter +#define IRQM_DAC2_UCNT 0x0100 // DAC2 Update Counter +#define IRQM_UTC1 0x0200 // User TC1 out +#define IRQM_UTC1_INV 0x0400 // User TC1 out, inverted +#define IRQM_UTC2 0x0800 // User TC2 out +#define IRQM_DIGITAL_IT 0x1000 // Digital Interrupt +#define IRQM_EXTERNAL_IT 0x2000 // External Interrupt +#define IRQM_ETRIG_RISING 0x4000 // External Trigger rising-edge +#define IRQM_ETRIG_FALLING 0x8000 // External Trigger falling-edge + +// DMA Request Sources (LAS0) +#define DMAS_DISABLED 0x0 // DMA Disabled +#define DMAS_ADC_SCNT 0x1 // ADC Sample Counter +#define DMAS_DAC1_UCNT 0x2 // D/A1 Update Counter +#define DMAS_DAC2_UCNT 0x3 // D/A2 Update Counter +#define DMAS_UTC1 0x4 // User TC1 out +#define DMAS_ADFIFO_HALF_FULL 0x8 // A/D FIFO half full +#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 + +// Port 0 compare modes (SetDIO0CompareMode) +#define DIO_MODE_EVENT 0 // Event Mode +#define DIO_MODE_MATCH 1 // Match Mode + +// Digital Table Enable (Port 1 disable) +#define DTBL_DISABLE 0 // Enable Digital Table +#define DTBL_ENABLE 1 // Disable Digital Table + +// Sampling Signal for High Speed Digital Input (SetHdinStart) +#define HDIN_SOFTWARE 0x0 // Software Trigger +#define HDIN_ADC 0x1 // A/D Conversion Signal +#define HDIN_UTC0 0x2 // User TC out 0 +#define HDIN_UTC1 0x3 // User TC out 1 +#define HDIN_UTC2 0x4 // User TC out 2 +#define HDIN_EPCLK 0x5 // External Pacer Clock +#define HDIN_ETRG 0x6 // External Trigger + +// Channel Gain Table / Channel Gain Latch +#define CSC_LATCH 0 // Channel Gain Latch mode +#define CSC_CGT 1 // Channel Gain Table mode + +// Channel Gain Table Pause Enable +#define CGT_PAUSE_DISABLE 0 // Channel Gain Table Pause Disable +#define CGT_PAUSE_ENABLE 1 // Channel Gain Table Pause Enable + +// DAC output type/range (p63) +#define AOUT_UNIP5 0 // 0..+5 Volt +#define AOUT_UNIP10 1 // 0..+10 Volt +#define AOUT_BIP5 2 // -5..+5 Volt +#define AOUT_BIP10 3 // -10..+10 Volt + +// Ghannel Gain Table field definitions (p61) +// Gain +#define GAIN1 0 +#define GAIN2 1 +#define GAIN4 2 +#define GAIN8 3 +#define GAIN16 4 +#define GAIN32 5 +#define GAIN64 6 +#define GAIN128 7 + +// Input range/polarity +#define AIN_BIP5 0 // -5..+5 Volt +#define AIN_BIP10 1 // -10..+10 Volt +#define AIN_UNIP10 2 // 0..+10 Volt + +// non referenced single ended select bit +#define NRSE_AGND 0 // AGND referenced SE input +#define NRSE_AINS 1 // AIN SENSE referenced SE input + +// single ended vs differential +#define GND_SE 0 // Single-Ended +#define GND_DIFF 1 // Differential -- 2.26.2