📄 rtd520.c
字号:
/* 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 <ds@schleef.org> 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.*//*Driver: rtd520.oDescription: Real Time Devices PCI4520/DM7520Author: Dan ChristianDevices: [Real Time Devices] DM7520HR-1 (DM7520), DM7520HR-8 (DM7520-8), PCI4520 (PCI4520), PCI4520-8 (PCI4520-8)Status: Works. Only tested on DM7520-8. Not SMP safe.Configuration options: [0] - PCI bus of device (optional) If bus/slot is not specified, the first available PCI device will be used. [1] - PCI slot of device (optional)*//* 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 (8254) 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/PC104/DM/analog%20IO/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 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. I use camelCase for structures (and inside them). I may also use upper CamelCase for function names (old habit). This board is somewhat related to the RTD PCI4400 board. 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. With DMA, you can sample at 1.15Mhz with 70% idle on a 400Mhz K6-2 (single channel, 64K read buffer). I get random system lockups when using DMA with ALI-15xx based systems. I haven't been able to test any other chipsets. The lockups happen soon after the start of an acquistion, not in the middle of a long run. Without DMA, you can do 620Khz sampling with 20% idle on a 400Mhz K6-2 (with a 256K read buffer). Digital-IO and Analog-Out only support instruction mode.*/#include <linux/comedidev.h>#include <linux/delay.h>#include <linux/pci.h>/*====================================================================== Driver specific stuff (tunable)======================================================================*//* 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) */#define RTD_MAX_CHANLIST 128 /* max channel list that we allow *//* tuning for ai/ao instruction done polling */#ifdef FAST_SPIN#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 comedi_udelay (1)#define RTD_ADC_TIMEOUT 2000 /* in usec */#define RTD_DAC_TIMEOUT 2000 /* in usec */#define RTD_DMA_TIMEOUT 1000 /* in usec */#endif/*====================================================================== Board specific stuff======================================================================*//* registers */#define PCI_VENDOR_ID_RTD 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 *//* 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======================================================================*//* The board has 3 input modes and the gains of 1,2,4,...32 (, 64, 128)*/static comedi_lrange rtd_ai_7520_range = { 18, { /* +-5V input range gain steps */ 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), /* +-10V input range gain steps */ BIP_RANGE(10.0), BIP_RANGE(10.0/2), BIP_RANGE(10.0/4), BIP_RANGE(10.0/8), BIP_RANGE(10.0/16), BIP_RANGE(10.0/32), /* +10V input range gain steps */ 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_ai_4520_range = { 24, { /* +-5V input range gain steps */ 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), BIP_RANGE(5.0/64), BIP_RANGE(5.0/128), /* +-10V input range gain steps */ BIP_RANGE(10.0), BIP_RANGE(10.0/2), BIP_RANGE(10.0/4), BIP_RANGE(10.0/8), BIP_RANGE(10.0/16), BIP_RANGE(10.0/32), BIP_RANGE(10.0/64), BIP_RANGE(10.0/128), /* +10V input range gain steps */ 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), UNI_RANGE(10.0/64), UNI_RANGE(10.0/128),}};/* Table order matches range values */static comedi_lrange rtd_ao_range = { 4, { RANGE(0, 5), RANGE(0, 10), RANGE(-5, 5), RANGE(-10, 10),}};/* Board descriptions */typedef struct rtdBoard_struct{ char *name; /* must be first */ int device_id; int aiChans; int aiBits; int aiMaxGain; int fifoLen; int range10Start; /* start of +-10V range */ int rangeUniStart; /* start of +10V range */} rtdBoard;static rtdBoard rtd520Boards[] = { { name: "DM7520", device_id: 0x7520, aiChans: 16, aiBits: 12, aiMaxGain: 32, fifoLen: 1024, range10Start: 6, rangeUniStart: 12, }, { name: "DM7520-8", device_id: 0x7520, aiChans: 16, aiBits: 12, aiMaxGain: 32, fifoLen: 8192, range10Start: 6, rangeUniStart: 12, }, { name: "PCI4520", device_id: 0x4520, aiChans: 16, aiBits: 12, aiMaxGain: 128, fifoLen: 1024, range10Start: 8, rangeUniStart: 16, }, { name: "PCI4520-8", device_id: 0x4520, aiChans: 16, aiBits: 12, aiMaxGain: 128, fifoLen: 8192, range10Start: 8, rangeUniStart: 16, },};static struct pci_device_id rtd520_pci_table[] __devinitdata = { { PCI_VENDOR_ID_RTD, 0x7520, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { PCI_VENDOR_ID_RTD, 0x4520, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { 0 }};MODULE_DEVICE_TABLE(pci, rtd520_pci_table);/* * 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 */ long aiCount; /* total transfer size (samples) */ int transCount; /* # to tranfer data. 0->1/2FIFO*/ int flags; /* flag event modes */ /* PCI device info */ struct pci_dev *pci_dev; /* channel list info */ /* chanBipolar tracks whether a channel is bipolar (and needs +2048) */ unsigned char chanBipolar[RTD_MAX_CHANLIST/8]; /* bit array */ /* read back data */ lsampl_t aoValue[2]; /* Used for AO read back */ /* timer gate (when enabled) */ u8 utcGate[4]; /* 1 extra allows simple range check */ /* shadow registers affect other registers, but cant be read back */ /* The macros below update these on writes */ u16 intMask; /* interrupt mask */ 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) \ (((array)[(index)/8] >> ((index) & 0x7)) & 0x1)#define CHAN_ARRAY_SET(array,index) \ (((array)[(index)/8] |= 1 << ((index) & 0x7)))#define CHAN_ARRAY_CLEAR(array,index) \ (((array)[(index)/8] &= ~(1 << ((index) & 0x7))))/* * 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 RtdAdcClearFifo(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)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -