diff options
Diffstat (limited to 'drivers/staging/comedi/drivers/rtd520.c')
-rw-r--r-- | drivers/staging/comedi/drivers/rtd520.c | 1365 |
1 files changed, 0 insertions, 1365 deletions
diff --git a/drivers/staging/comedi/drivers/rtd520.c b/drivers/staging/comedi/drivers/rtd520.c deleted file mode 100644 index 2d99a648b054..000000000000 --- a/drivers/staging/comedi/drivers/rtd520.c +++ /dev/null @@ -1,1365 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * 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> - */ - -/* - * Driver: rtd520 - * Description: Real Time Devices PCI4520/DM7520 - * Devices: [Real Time Devices] DM7520HR-1 (DM7520), DM7520HR-8, - * PCI4520 (PCI4520), PCI4520-8 - * Author: Dan Christian - * Status: Works. Only tested on DM7520-8. Not SMP safe. - * - * Configuration options: not applicable, uses PCI auto config - */ - -/* - * 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/io/pci9080 - * - * 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 doesn't work if Sample is - * also enabled. It turns out that About really isn't needed, since - * we always count down samples read. - */ - -/* - * 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/module.h> -#include <linux/delay.h> -#include <linux/interrupt.h> - -#include "../comedi_pci.h" - -#include "comedi_8254.h" -#include "plx9080.h" - -/* - * Local Address Space 0 Offsets - */ -#define LAS0_USER_IO 0x0008 /* User I/O */ -#define LAS0_ADC 0x0010 /* FIFO Status/Software A/D Start */ -#define FS_DAC1_NOT_EMPTY BIT(0) /* DAC1 FIFO not empty */ -#define FS_DAC1_HEMPTY BIT(1) /* DAC1 FIFO half empty */ -#define FS_DAC1_NOT_FULL BIT(2) /* DAC1 FIFO not full */ -#define FS_DAC2_NOT_EMPTY BIT(4) /* DAC2 FIFO not empty */ -#define FS_DAC2_HEMPTY BIT(5) /* DAC2 FIFO half empty */ -#define FS_DAC2_NOT_FULL BIT(6) /* DAC2 FIFO not full */ -#define FS_ADC_NOT_EMPTY BIT(8) /* ADC FIFO not empty */ -#define FS_ADC_HEMPTY BIT(9) /* ADC FIFO half empty */ -#define FS_ADC_NOT_FULL BIT(10) /* ADC FIFO not full */ -#define FS_DIN_NOT_EMPTY BIT(12) /* DIN FIFO not empty */ -#define FS_DIN_HEMPTY BIT(13) /* DIN FIFO half empty */ -#define FS_DIN_NOT_FULL BIT(14) /* DIN FIFO not full */ -#define LAS0_UPDATE_DAC(x) (0x0014 + ((x) * 0x4)) /* D/Ax Update (w) */ -#define LAS0_DAC 0x0024 /* Software Simultaneous Update (w) */ -#define LAS0_PACER 0x0028 /* Software Pacer Start/Stop */ -#define LAS0_TIMER 0x002c /* Timer Status/HDIN Software Trig. */ -#define LAS0_IT 0x0030 /* Interrupt Status/Enable */ -#define IRQM_ADC_FIFO_WRITE BIT(0) /* ADC FIFO Write */ -#define IRQM_CGT_RESET BIT(1) /* Reset CGT */ -#define IRQM_CGT_PAUSE BIT(3) /* Pause CGT */ -#define IRQM_ADC_ABOUT_CNT BIT(4) /* About Counter out */ -#define IRQM_ADC_DELAY_CNT BIT(5) /* Delay Counter out */ -#define IRQM_ADC_SAMPLE_CNT BIT(6) /* ADC Sample Counter */ -#define IRQM_DAC1_UCNT BIT(7) /* DAC1 Update Counter */ -#define IRQM_DAC2_UCNT BIT(8) /* DAC2 Update Counter */ -#define IRQM_UTC1 BIT(9) /* User TC1 out */ -#define IRQM_UTC1_INV BIT(10) /* User TC1 out, inverted */ -#define IRQM_UTC2 BIT(11) /* User TC2 out */ -#define IRQM_DIGITAL_IT BIT(12) /* Digital Interrupt */ -#define IRQM_EXTERNAL_IT BIT(13) /* External Interrupt */ -#define IRQM_ETRIG_RISING BIT(14) /* Ext Trigger rising-edge */ -#define IRQM_ETRIG_FALLING BIT(15) /* Ext Trigger falling-edge */ -#define LAS0_CLEAR 0x0034 /* Clear/Set Interrupt Clear Mask */ -#define LAS0_OVERRUN 0x0038 /* Pending interrupts/Clear Overrun */ -#define LAS0_PCLK 0x0040 /* Pacer Clock (24bit) */ -#define LAS0_BCLK 0x0044 /* Burst Clock (10bit) */ -#define LAS0_ADC_SCNT 0x0048 /* A/D Sample counter (10bit) */ -#define LAS0_DAC1_UCNT 0x004c /* D/A1 Update counter (10 bit) */ -#define LAS0_DAC2_UCNT 0x0050 /* D/A2 Update counter (10 bit) */ -#define LAS0_DCNT 0x0054 /* Delay counter (16 bit) */ -#define LAS0_ACNT 0x0058 /* About counter (16 bit) */ -#define LAS0_DAC_CLK 0x005c /* DAC clock (16bit) */ -#define LAS0_8254_TIMER_BASE 0x0060 /* 8254 timer/counter base */ -#define LAS0_DIO0 0x0070 /* Digital I/O Port 0 */ -#define LAS0_DIO1 0x0074 /* Digital I/O Port 1 */ -#define LAS0_DIO0_CTRL 0x0078 /* Digital I/O Control */ -#define LAS0_DIO_STATUS 0x007c /* Digital I/O Status */ -#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 /* HiSpd DI 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_DAC_CTRL(x) (0x0150 + ((x) * 0x14)) /* D/Ax type/range */ -#define LAS0_DAC_SRC(x) (0x0154 + ((x) * 0x14)) /* D/Ax update source */ -#define LAS0_DAC_CYCLE(x) (0x0158 + ((x) * 0x14)) /* D/Ax cycle mode */ -#define LAS0_DAC_RESET(x) (0x015c + ((x) * 0x14)) /* D/Ax FIFO reset */ -#define LAS0_DAC_FIFO_CLEAR(x) (0x0160 + ((x) * 0x14)) /* D/Ax 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 /* Ext. Trigger polarity select */ -#define LAS0_EINT_POLARITY 0x01a8 /* Ext. Interrupt polarity select */ -#define LAS0_8254_CLK_SEL(x) (0x01ac + ((x) * 0x8)) /* 8254 clock select */ -#define LAS0_8254_GATE_SEL(x) (0x01b0 + ((x) * 0x8)) /* 8254 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 */ - -/* - * Local Address Space 1 Offsets - */ -#define LAS1_ADC_FIFO 0x0000 /* A/D FIFO (16bit) */ -#define LAS1_HDIO_FIFO 0x0004 /* HiSpd DI FIFO (16bit) */ -#define LAS1_DAC_FIFO(x) (0x0008 + ((x) * 0x4)) /* D/Ax FIFO (16bit) */ - -/* - * Driver specific stuff (tunable) - */ - -/* - * 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 */ - -/* - * Board specific stuff - */ - -#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 */ - -/* 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 const struct 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 const struct 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 const struct comedi_lrange rtd_ao_range = { - 4, { - UNI_RANGE(5), - UNI_RANGE(10), - BIP_RANGE(5), - BIP_RANGE(10), - } -}; - -enum rtd_boardid { - BOARD_DM7520, - BOARD_PCI4520, -}; - -struct rtd_boardinfo { - const char *name; - int range_bip10; /* start of +-10V range */ - int range_uni10; /* start of +10V range */ - const struct comedi_lrange *ai_range; -}; - -static const struct rtd_boardinfo rtd520_boards[] = { - [BOARD_DM7520] = { - .name = "DM7520", - .range_bip10 = 6, - .range_uni10 = 12, - .ai_range = &rtd_ai_7520_range, - }, - [BOARD_PCI4520] = { - .name = "PCI4520", - .range_bip10 = 8, - .range_uni10 = 16, - .ai_range = &rtd_ai_4520_range, - }, -}; - -struct rtd_private { - /* memory mapped board structures */ - void __iomem *las1; - void __iomem *lcfg; - - long ai_count; /* total transfer size (samples) */ - int xfer_count; /* # to transfer data. 0->1/2FIFO */ - int flags; /* flag event modes */ - unsigned int fifosz; - - /* 8254 Timer/Counter gate and clock sources */ - unsigned char timer_gate_src[3]; - unsigned char timer_clk_src[3]; -}; - -/* 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 */ - -/* - * 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, - unsigned int flags, int base) -{ - int divider; - - switch (flags & CMDF_ROUND_MASK) { - case CMDF_ROUND_NEAREST: - default: - divider = DIV_ROUND_CLOSEST(*nanosec, base); - break; - case CMDF_ROUND_DOWN: - divider = (*nanosec) / base; - break; - case CMDF_ROUND_UP: - divider = DIV_ROUND_UP(*nanosec, 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, unsigned int flags) -{ - return rtd_ns_to_timer_base(ns, flags, RTD_CLOCK_BASE); -} - -/* Convert a single comedi channel-gain entry to a RTD520 table entry */ -static unsigned short rtd_convert_chan_gain(struct comedi_device *dev, - unsigned int chanspec, int index) -{ - const struct rtd_boardinfo *board = dev->board_ptr; - unsigned int chan = CR_CHAN(chanspec); - unsigned int range = CR_RANGE(chanspec); - unsigned int aref = CR_AREF(chanspec); - unsigned short r = 0; - - r |= chan & 0xf; - - /* Note: we also setup the channel list bipolar flag array */ - if (range < board->range_bip10) { - /* +-5 range */ - r |= 0x000; - r |= (range & 0x7) << 4; - } else if (range < board->range_uni10) { - /* +-10 range */ - r |= 0x100; - r |= ((range - board->range_bip10) & 0x7) << 4; - } else { - /* +10 range */ - r |= 0x200; - r |= ((range - board->range_uni10) & 0x7) << 4; - } - - switch (aref) { - case AREF_GROUND: /* on-board ground */ - break; - - case AREF_COMMON: - r |= 0x80; /* ref external analog common */ - break; - - case AREF_DIFF: - r |= 0x400; /* differential inputs */ - break; - - case AREF_OTHER: /* ??? */ - break; - } - return r; -} - -/* Setup the channel-gain table from a comedi list */ -static void rtd_load_channelgain_list(struct comedi_device *dev, - unsigned int n_chan, unsigned int *list) -{ - if (n_chan > 1) { /* setup channel gain table */ - int ii; - - writel(0, dev->mmio + LAS0_CGT_CLEAR); - writel(1, dev->mmio + LAS0_CGT_ENABLE); - for (ii = 0; ii < n_chan; ii++) { - writel(rtd_convert_chan_gain(dev, list[ii], ii), - dev->mmio + LAS0_CGT_WRITE); - } - } else { /* just use the channel gain latch */ - writel(0, dev->mmio + LAS0_CGT_ENABLE); - writel(rtd_convert_chan_gain(dev, list[0], 0), - dev->mmio + LAS0_CGL_WRITE); - } -} - -/* - * Determine fifo size by doing adc conversions until the fifo half - * empty status flag clears. - */ -static int rtd520_probe_fifo_depth(struct comedi_device *dev) -{ - unsigned int chanspec = CR_PACK(0, 0, AREF_GROUND); - unsigned int i; - static const unsigned int limit = 0x2000; - unsigned int fifo_size = 0; - - writel(0, dev->mmio + LAS0_ADC_FIFO_CLEAR); - rtd_load_channelgain_list(dev, 1, &chanspec); - /* ADC conversion trigger source: SOFTWARE */ - writel(0, dev->mmio + LAS0_ADC_CONVERSION); - /* convert samples */ - for (i = 0; i < limit; ++i) { - unsigned int fifo_status; - /* trigger conversion */ - writew(0, dev->mmio + LAS0_ADC); - usleep_range(1, 1000); - fifo_status = readl(dev->mmio + LAS0_ADC); - if ((fifo_status & FS_ADC_HEMPTY) == 0) { - fifo_size = 2 * i; - break; - } - } - if (i == limit) { - dev_info(dev->class_dev, "failed to probe fifo size.\n"); - return -EIO; - } - writel(0, dev->mmio + LAS0_ADC_FIFO_CLEAR); - if (fifo_size != 0x400 && fifo_size != 0x2000) { - dev_info(dev->class_dev, - "unexpected fifo size of %i, expected 1024 or 8192.\n", - fifo_size); - return -EIO; - } - return fifo_size; -} - -static int rtd_ai_eoc(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned long context) -{ - unsigned int status; - - status = readl(dev->mmio + LAS0_ADC); - if (status & FS_ADC_NOT_EMPTY) - return 0; - return -EBUSY; -} - -static int rtd_ai_rinsn(struct comedi_device *dev, - struct comedi_subdevice *s, struct comedi_insn *insn, - unsigned int *data) -{ - struct rtd_private *devpriv = dev->private; - unsigned int range = CR_RANGE(insn->chanspec); - int ret; - int n; - - /* clear any old fifo data */ - writel(0, dev->mmio + LAS0_ADC_FIFO_CLEAR); - - /* write channel to multiplexer and clear channel gain table */ - rtd_load_channelgain_list(dev, 1, &insn->chanspec); - - /* ADC conversion trigger source: SOFTWARE */ - writel(0, dev->mmio + LAS0_ADC_CONVERSION); - - /* convert n samples */ - for (n = 0; n < insn->n; n++) { - unsigned short d; - /* trigger conversion */ - writew(0, dev->mmio + LAS0_ADC); - - ret = comedi_timeout(dev, s, insn, rtd_ai_eoc, 0); - if (ret) - return ret; - - /* read data */ - d = readw(devpriv->las1 + LAS1_ADC_FIFO); - d >>= 3; /* low 3 bits are marker lines */ - - /* convert bipolar data to comedi unsigned data */ - if (comedi_range_is_bipolar(s, range)) - d = comedi_offset_munge(s, d); - - data[n] = d & s->maxdata; - } - - /* return the number of samples read/written */ - return n; -} - -static int ai_read_n(struct comedi_device *dev, struct comedi_subdevice *s, - int count) -{ - struct rtd_private *devpriv = dev->private; - struct comedi_async *async = s->async; - struct comedi_cmd *cmd = &async->cmd; - int ii; - - for (ii = 0; ii < count; ii++) { - unsigned int range = CR_RANGE(cmd->chanlist[async->cur_chan]); - unsigned short d; - - if (devpriv->ai_count == 0) { /* done */ - d = readw(devpriv->las1 + LAS1_ADC_FIFO); - continue; - } - - d = readw(devpriv->las1 + LAS1_ADC_FIFO); - d >>= 3; /* low 3 bits are marker lines */ - - /* convert bipolar data to comedi unsigned data */ - if (comedi_range_is_bipolar(s, range)) - d = comedi_offset_munge(s, d); - d &= s->maxdata; - - if (!comedi_buf_write_samples(s, &d, 1)) - return -1; - - if (devpriv->ai_count > 0) /* < 0, means read forever */ - devpriv->ai_count--; - } - return 0; -} - -static irqreturn_t rtd_interrupt(int irq, void *d) -{ - struct comedi_device *dev = d; - struct comedi_subdevice *s = dev->read_subdev; - struct rtd_private *devpriv = dev->private; - u32 overrun; - u16 status; - u16 fifo_status; - - if (!dev->attached) - return IRQ_NONE; - - fifo_status = readl(dev->mmio + LAS0_ADC); - /* check for FIFO full, this automatically halts the ADC! */ - if (!(fifo_status & FS_ADC_NOT_FULL)) /* 0 -> full */ - goto xfer_abort; - - status = readw(dev->mmio + LAS0_IT); - /* if interrupt was not caused by our board, or handled above */ - if (status == 0) - return IRQ_HANDLED; - - if (status & IRQM_ADC_ABOUT_CNT) { /* sample count -> read FIFO */ - /* - * since the priority interrupt controller may have queued - * a sample counter interrupt, even though we have already - * finished, we must handle the possibility that there is - * no data here - */ - if (!(fifo_status & FS_ADC_HEMPTY)) { - /* FIFO half full */ - if (ai_read_n(dev, s, devpriv->fifosz / 2) < 0) - goto xfer_abort; - - if (devpriv->ai_count == 0) - goto xfer_done; - } else if (devpriv->xfer_count > 0) { - if (fifo_status & FS_ADC_NOT_EMPTY) { - /* FIFO not empty */ - if (ai_read_n(dev, s, devpriv->xfer_count) < 0) - goto xfer_abort; - - if (devpriv->ai_count == 0) - goto xfer_done; - } - } - } - - overrun = readl(dev->mmio + LAS0_OVERRUN) & 0xffff; - if (overrun) - goto xfer_abort; - - /* clear the interrupt */ - writew(status, dev->mmio + LAS0_CLEAR); - readw(dev->mmio + LAS0_CLEAR); - - comedi_handle_events(dev, s); - - return IRQ_HANDLED; - -xfer_abort: - s->async->events |= COMEDI_CB_ERROR; - -xfer_done: - s->async->events |= COMEDI_CB_EOA; - - /* clear the interrupt */ - status = readw(dev->mmio + LAS0_IT); - writew(status, dev->mmio + LAS0_CLEAR); - readw(dev->mmio + LAS0_CLEAR); - - fifo_status = readl(dev->mmio + LAS0_ADC); - overrun = readl(dev->mmio + LAS0_OVERRUN) & 0xffff; - - comedi_handle_events(dev, s); - - return IRQ_HANDLED; -} - -static int rtd_ai_cmdtest(struct comedi_device *dev, - struct comedi_subdevice *s, struct comedi_cmd *cmd) -{ - int err = 0; - unsigned int arg; - - /* Step 1 : check if triggers are trivially valid */ - - err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW); - err |= comedi_check_trigger_src(&cmd->scan_begin_src, - TRIG_TIMER | TRIG_EXT); - err |= comedi_check_trigger_src(&cmd->convert_src, - TRIG_TIMER | TRIG_EXT); - err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); - err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE); - - if (err) - return 1; - - /* Step 2a : make sure trigger sources are unique */ - - err |= comedi_check_trigger_is_unique(cmd->scan_begin_src); - err |= comedi_check_trigger_is_unique(cmd->convert_src); - err |= comedi_check_trigger_is_unique(cmd->stop_src); - - /* Step 2b : and mutually compatible */ - - if (err) - return 2; - - /* Step 3: check if arguments are trivially valid */ - - err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0); - - if (cmd->scan_begin_src == TRIG_TIMER) { - /* Note: these are time periods, not actual rates */ - if (cmd->chanlist_len == 1) { /* no scanning */ - if (comedi_check_trigger_arg_min(&cmd->scan_begin_arg, - RTD_MAX_SPEED_1)) { - rtd_ns_to_timer(&cmd->scan_begin_arg, - CMDF_ROUND_UP); - err |= -EINVAL; - } - if (comedi_check_trigger_arg_max(&cmd->scan_begin_arg, - RTD_MIN_SPEED_1)) { - rtd_ns_to_timer(&cmd->scan_begin_arg, - CMDF_ROUND_DOWN); - err |= -EINVAL; - } - } else { - if (comedi_check_trigger_arg_min(&cmd->scan_begin_arg, - RTD_MAX_SPEED)) { - rtd_ns_to_timer(&cmd->scan_begin_arg, - CMDF_ROUND_UP); - err |= -EINVAL; - } - if (comedi_check_trigger_arg_max(&cmd->scan_begin_arg, - RTD_MIN_SPEED)) { - rtd_ns_to_timer(&cmd->scan_begin_arg, - CMDF_ROUND_DOWN); - err |= -EINVAL; - } - } - } else { - /* external trigger */ - /* should be level/edge, hi/lo specification here */ - /* should specify multiple external triggers */ - err |= comedi_check_trigger_arg_max(&cmd->scan_begin_arg, 9); - } - - if (cmd->convert_src == TRIG_TIMER) { - if (cmd->chanlist_len == 1) { /* no scanning */ - if (comedi_check_trigger_arg_min(&cmd->convert_arg, - RTD_MAX_SPEED_1)) { - rtd_ns_to_timer(&cmd->convert_arg, - CMDF_ROUND_UP); - err |= -EINVAL; - } - if (comedi_check_trigger_arg_max(&cmd->convert_arg, - RTD_MIN_SPEED_1)) { - rtd_ns_to_timer(&cmd->convert_arg, - CMDF_ROUND_DOWN); - err |= -EINVAL; - } - } else { - if (comedi_check_trigger_arg_min(&cmd->convert_arg, - RTD_MAX_SPEED)) { - rtd_ns_to_timer(&cmd->convert_arg, - CMDF_ROUND_UP); - err |= -EINVAL; - } - if (comedi_check_trigger_arg_max(&cmd->convert_arg, - RTD_MIN_SPEED)) { - rtd_ns_to_timer(&cmd->convert_arg, - CMDF_ROUND_DOWN); - err |= -EINVAL; - } - } - } else { - /* external trigger */ - /* see above */ - err |= comedi_check_trigger_arg_max(&cmd->convert_arg, 9); - } - - err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg, - cmd->chanlist_len); - - if (cmd->stop_src == TRIG_COUNT) - err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1); - else /* TRIG_NONE */ - err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0); - - if (err) - return 3; - - /* step 4: fix up any arguments */ - - if (cmd->scan_begin_src == TRIG_TIMER) { - arg = cmd->scan_begin_arg; - rtd_ns_to_timer(&arg, cmd->flags); - err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg); - } - - if (cmd->convert_src == TRIG_TIMER) { - arg = cmd->convert_arg; - rtd_ns_to_timer(&arg, cmd->flags); - err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg); - - if (cmd->scan_begin_src == TRIG_TIMER) { - arg = cmd->convert_arg * cmd->scan_end_arg; - err |= comedi_check_trigger_arg_min( - &cmd->scan_begin_arg, arg); - } - } - - if (err) - return 4; - - return 0; -} - -static int rtd_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s) -{ - struct rtd_private *devpriv = dev->private; - struct comedi_cmd *cmd = &s->async->cmd; - int timer; - - /* stop anything currently running */ - /* pacer stop source: SOFTWARE */ - writel(0, dev->mmio + LAS0_PACER_STOP); - writel(0, dev->mmio + LAS0_PACER); /* stop pacer */ - writel(0, dev->mmio + LAS0_ADC_CONVERSION); - writew(0, dev->mmio + LAS0_IT); - writel(0, dev->mmio + LAS0_ADC_FIFO_CLEAR); - writel(0, dev->mmio + LAS0_OVERRUN); - - /* start configuration */ - /* load channel list and reset CGT */ - rtd_load_channelgain_list(dev, cmd->chanlist_len, cmd->chanlist); - - /* setup the common case and override if needed */ - if (cmd->chanlist_len > 1) { - /* pacer start source: SOFTWARE */ - writel(0, dev->mmio + LAS0_PACER_START); - /* burst trigger source: PACER */ - writel(1, dev->mmio + LAS0_BURST_START); - /* ADC conversion trigger source: BURST */ - writel(2, dev->mmio + LAS0_ADC_CONVERSION); - } else { /* single channel */ - /* pacer start source: SOFTWARE */ - writel(0, dev->mmio + LAS0_PACER_START); - /* ADC conversion trigger source: PACER */ - writel(1, dev->mmio + LAS0_ADC_CONVERSION); - } - writel((devpriv->fifosz / 2 - 1) & 0xffff, dev->mmio + LAS0_ACNT); - - if (cmd->scan_begin_src == TRIG_TIMER) { - /* scan_begin_arg is in nanoseconds */ - /* find out how many samples to wait before transferring */ - if (cmd->flags & CMDF_WAKE_EOS) { - /* - * this may generate un-sustainable interrupt rates - * the application is responsible for doing the - * right thing - */ - devpriv->xfer_count = cmd->chanlist_len; - devpriv->flags |= SEND_EOS; - } else { - /* arrange to transfer data periodically */ - devpriv->xfer_count = - (TRANS_TARGET_PERIOD * cmd->chanlist_len) / - cmd->scan_begin_arg; - if (devpriv->xfer_count < cmd->chanlist_len) { - /* transfer after each scan (and avoid 0) */ - devpriv->xfer_count = cmd->chanlist_len; - } else { /* make a multiple of scan length */ - devpriv->xfer_count = - DIV_ROUND_UP(devpriv->xfer_count, - cmd->chanlist_len); - devpriv->xfer_count *= cmd->chanlist_len; - } - devpriv->flags |= SEND_EOS; - } - if (devpriv->xfer_count >= (devpriv->fifosz / 2)) { - /* out of counter range, use 1/2 fifo instead */ - devpriv->xfer_count = 0; - devpriv->flags &= ~SEND_EOS; - } else { - /* interrupt for each transfer */ - writel((devpriv->xfer_count - 1) & 0xffff, - dev->mmio + LAS0_ACNT); - } - } else { /* unknown timing, just use 1/2 FIFO */ - devpriv->xfer_count = 0; - devpriv->flags &= ~SEND_EOS; - } - /* pacer clock source: INTERNAL 8MHz */ - writel(1, dev->mmio + LAS0_PACER_SELECT); - /* just interrupt, don't stop */ - writel(1, dev->mmio + LAS0_ACNT_STOP_ENABLE); - - /* 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 */ - devpriv->ai_count = cmd->stop_arg * cmd->chanlist_len; - if ((devpriv->xfer_count > 0) && - (devpriv->xfer_count > devpriv->ai_count)) { - devpriv->xfer_count = devpriv->ai_count; - } - break; - - case TRIG_NONE: /* stop when cancel is called */ - devpriv->ai_count = -1; /* read forever */ - break; - } - - /* Scan timing */ - switch (cmd->scan_begin_src) { - case TRIG_TIMER: /* periodic scanning */ - timer = rtd_ns_to_timer(&cmd->scan_begin_arg, - CMDF_ROUND_NEAREST); - /* set PACER clock */ - writel(timer & 0xffffff, dev->mmio + LAS0_PCLK); - - break; - - case TRIG_EXT: - /* pacer start source: EXTERNAL */ - writel(1, dev->mmio + LAS0_PACER_START); - break; - } - - /* Sample timing within a scan */ - switch (cmd->convert_src) { - case TRIG_TIMER: /* periodic */ - if (cmd->chanlist_len > 1) { - /* only needed for multi-channel */ - timer = rtd_ns_to_timer(&cmd->convert_arg, - CMDF_ROUND_NEAREST); - /* setup BURST clock */ - writel(timer & 0x3ff, dev->mmio + LAS0_BCLK); - } - - break; - - case TRIG_EXT: /* external */ - /* burst trigger source: EXTERNAL */ - writel(2, dev->mmio + LAS0_BURST_START); - break; - } - /* end configuration */ - - /* - * This doesn't seem to work. There is no way to clear an interrupt - * that the priority controller has queued! - */ - writew(~0, dev->mmio + LAS0_CLEAR); - readw(dev->mmio + LAS0_CLEAR); - - /* TODO: allow multiple interrupt sources */ - /* transfer every N samples */ - writew(IRQM_ADC_ABOUT_CNT, dev->mmio + LAS0_IT); - - /* BUG: start_src is ASSUMED to be TRIG_NOW */ - /* BUG? it seems like things are running before the "start" */ - readl(dev->mmio + LAS0_PACER); /* start pacer */ - return 0; -} - -static int rtd_ai_cancel(struct comedi_device *dev, struct comedi_subdevice *s) -{ - struct rtd_private *devpriv = dev->private; - - /* pacer stop source: SOFTWARE */ - writel(0, dev->mmio + LAS0_PACER_STOP); - writel(0, dev->mmio + LAS0_PACER); /* stop pacer */ - writel(0, dev->mmio + LAS0_ADC_CONVERSION); - writew(0, dev->mmio + LAS0_IT); - devpriv->ai_count = 0; /* stop and don't transfer any more */ - writel(0, dev->mmio + LAS0_ADC_FIFO_CLEAR); - return 0; -} - -static int rtd_ao_eoc(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned long context) -{ - unsigned int chan = CR_CHAN(insn->chanspec); - unsigned int bit = (chan == 0) ? FS_DAC1_NOT_EMPTY : FS_DAC2_NOT_EMPTY; - unsigned int status; - - status = readl(dev->mmio + LAS0_ADC); - if (status & bit) - return 0; - return -EBUSY; -} - -static int rtd_ao_insn_write(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) -{ - struct rtd_private *devpriv = dev->private; - unsigned int chan = CR_CHAN(insn->chanspec); - unsigned int range = CR_RANGE(insn->chanspec); - int ret; - int i; - - /* Configure the output range (table index matches the range values) */ - writew(range & 7, dev->mmio + LAS0_DAC_CTRL(chan)); - - for (i = 0; i < insn->n; ++i) { - unsigned int val = data[i]; - - /* bipolar uses 2's complement values with an extended sign */ - if (comedi_range_is_bipolar(s, range)) { - val = comedi_offset_munge(s, val); - val |= (val & ((s->maxdata + 1) >> 1)) << 1; - } - - /* shift the 12-bit data (+ sign) to match the register */ - val <<= 3; - - writew(val, devpriv->las1 + LAS1_DAC_FIFO(chan)); - writew(0, dev->mmio + LAS0_UPDATE_DAC(chan)); - - ret = comedi_timeout(dev, s, insn, rtd_ao_eoc, 0); - if (ret) - return ret; - - s->readback[chan] = data[i]; - } - - return insn->n; -} - -static int rtd_dio_insn_bits(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) -{ - if (comedi_dio_update_state(s, data)) - writew(s->state & 0xff, dev->mmio + LAS0_DIO0); - - data[1] = readw(dev->mmio + LAS0_DIO0) & 0xff; - - return insn->n; -} - -static int rtd_dio_insn_config(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) -{ - int ret; - - ret = comedi_dio_insn_config(dev, s, insn, data, 0); - if (ret) - return ret; - - /* TODO support digital match interrupts and strobes */ - - /* set direction */ - writew(0x01, dev->mmio + LAS0_DIO_STATUS); - writew(s->io_bits & 0xff, dev->mmio + LAS0_DIO0_CTRL); - - /* clear interrupts */ - writew(0x00, dev->mmio + LAS0_DIO_STATUS); - - /* port1 can only be all input or all output */ - - /* there are also 2 user input lines and 2 user output lines */ - - return insn->n; -} - -static int rtd_counter_insn_config(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) -{ - struct rtd_private *devpriv = dev->private; - unsigned int chan = CR_CHAN(insn->chanspec); - unsigned int max_src; - unsigned int src; - - switch (data[0]) { - case INSN_CONFIG_SET_GATE_SRC: - /* - * 8254 Timer/Counter gate sources: - * - * 0 = Not gated, free running (reset state) - * 1 = Gated, off - * 2 = Ext. TC Gate 1 - * 3 = Ext. TC Gate 2 - * 4 = Previous TC out (chan 1 and 2 only) - */ - src = data[2]; - max_src = (chan == 0) ? 3 : 4; - if (src > max_src) - return -EINVAL; - - devpriv->timer_gate_src[chan] = src; - writeb(src, dev->mmio + LAS0_8254_GATE_SEL(chan)); - break; - case INSN_CONFIG_GET_GATE_SRC: - data[2] = devpriv->timer_gate_src[chan]; - break; - case INSN_CONFIG_SET_CLOCK_SRC: - /* - * 8254 Timer/Counter clock sources: - * - * 0 = 8 MHz (reset state) - * 1 = Ext. TC Clock 1 - * 2 = Ext. TX Clock 2 - * 3 = Ext. Pacer Clock - * 4 = Previous TC out (chan 1 and 2 only) - * 5 = High-Speed Digital Input Sampling signal (chan 1 only) - */ - src = data[1]; - switch (chan) { - case 0: - max_src = 3; - break; - case 1: - max_src = 5; - break; - case 2: - max_src = 4; - break; - default: - return -EINVAL; - } - if (src > max_src) - return -EINVAL; - - devpriv->timer_clk_src[chan] = src; - writeb(src, dev->mmio + LAS0_8254_CLK_SEL(chan)); - break; - case INSN_CONFIG_GET_CLOCK_SRC: - src = devpriv->timer_clk_src[chan]; - data[1] = devpriv->timer_clk_src[chan]; - data[2] = (src == 0) ? RTD_CLOCK_BASE : 0; - break; - default: - return -EINVAL; - } - - return insn->n; -} - -static void rtd_reset(struct comedi_device *dev) -{ - struct rtd_private *devpriv = dev->private; - - writel(0, dev->mmio + LAS0_BOARD_RESET); - usleep_range(100, 1000); /* needed? */ - writel(0, devpriv->lcfg + PLX_REG_INTCSR); - writew(0, dev->mmio + LAS0_IT); - writew(~0, dev->mmio + LAS0_CLEAR); - readw(dev->mmio + LAS0_CLEAR); -} - -/* - * initialize board, per RTD spec - * also, initialize shadow registers - */ -static void rtd_init_board(struct comedi_device *dev) -{ - rtd_reset(dev); - - writel(0, dev->mmio + LAS0_OVERRUN); - writel(0, dev->mmio + LAS0_CGT_CLEAR); - writel(0, dev->mmio + LAS0_ADC_FIFO_CLEAR); - writel(0, dev->mmio + LAS0_DAC_RESET(0)); - writel(0, dev->mmio + LAS0_DAC_RESET(1)); - /* clear digital IO fifo */ - writew(0, dev->mmio + LAS0_DIO_STATUS); - /* TODO: set user out source ??? */ -} - -/* The RTD driver does this */ -static void rtd_pci_latency_quirk(struct comedi_device *dev, - struct pci_dev *pcidev) -{ - unsigned char pci_latency; - - pci_read_config_byte(pcidev, PCI_LATENCY_TIMER, &pci_latency); - if (pci_latency < 32) { - dev_info(dev->class_dev, - "PCI latency changed from %d to %d\n", - pci_latency, 32); - pci_write_config_byte(pcidev, PCI_LATENCY_TIMER, 32); - } -} - -static int rtd_auto_attach(struct comedi_device *dev, - unsigned long context) -{ - struct pci_dev *pcidev = comedi_to_pci_dev(dev); - const struct rtd_boardinfo *board = NULL; - struct rtd_private *devpriv; - struct comedi_subdevice *s; - int ret; - - if (context < ARRAY_SIZE(rtd520_boards)) - board = &rtd520_boards[context]; - if (!board) - return -ENODEV; - dev->board_ptr = board; - dev->board_name = board->name; - - devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); - if (!devpriv) - return -ENOMEM; - - ret = comedi_pci_enable(dev); - if (ret) - return ret; - - dev->mmio = pci_ioremap_bar(pcidev, 2); - devpriv->las1 = pci_ioremap_bar(pcidev, 3); - devpriv->lcfg = pci_ioremap_bar(pcidev, 0); - if (!dev->mmio || !devpriv->las1 || !devpriv->lcfg) - return -ENOMEM; - - rtd_pci_latency_quirk(dev, pcidev); - - if (pcidev->irq) { - ret = request_irq(pcidev->irq, rtd_interrupt, IRQF_SHARED, - dev->board_name, dev); - if (ret == 0) - dev->irq = pcidev->irq; - } - - ret = comedi_alloc_subdevices(dev, 4); - if (ret) - return ret; - - s = &dev->subdevices[0]; - /* analog input subdevice */ - s->type = COMEDI_SUBD_AI; - s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON | SDF_DIFF; - s->n_chan = 16; - s->maxdata = 0x0fff; - s->range_table = board->ai_range; - s->len_chanlist = RTD_MAX_CHANLIST; - s->insn_read = rtd_ai_rinsn; - if (dev->irq) { - dev->read_subdev = s; - s->subdev_flags |= SDF_CMD_READ; - 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_AO; - s->subdev_flags = SDF_WRITABLE; - s->n_chan = 2; - s->maxdata = 0x0fff; - s->range_table = &rtd_ao_range; - s->insn_write = rtd_ao_insn_write; - - ret = comedi_alloc_subdev_readback(s); - if (ret) - return ret; - - s = &dev->subdevices[2]; - /* digital i/o subdevice */ - s->type = COMEDI_SUBD_DIO; - s->subdev_flags = SDF_READABLE | SDF_WRITABLE; - /* we only support port 0 right now. Ignoring port 1 and user IO */ - s->n_chan = 8; - s->maxdata = 1; - s->range_table = &range_digital; - s->insn_bits = rtd_dio_insn_bits; - s->insn_config = rtd_dio_insn_config; - - /* 8254 Timer/Counter subdevice */ - s = &dev->subdevices[3]; - dev->pacer = comedi_8254_mm_init(dev->mmio + LAS0_8254_TIMER_BASE, - RTD_CLOCK_BASE, I8254_IO8, 2); - if (!dev->pacer) - return -ENOMEM; - - comedi_8254_subdevice_init(s, dev->pacer); - dev->pacer->insn_config = rtd_counter_insn_config; - - rtd_init_board(dev); - - ret = rtd520_probe_fifo_depth(dev); - if (ret < 0) - return ret; - devpriv->fifosz = ret; - - if (dev->irq) - writel(PLX_INTCSR_PIEN | PLX_INTCSR_PLIEN, - devpriv->lcfg + PLX_REG_INTCSR); - - return 0; -} - -static void rtd_detach(struct comedi_device *dev) -{ - struct rtd_private *devpriv = dev->private; - - if (devpriv) { - /* Shut down any board ops by resetting it */ - if (dev->mmio && devpriv->lcfg) - rtd_reset(dev); - if (dev->irq) - free_irq(dev->irq, dev); - if (dev->mmio) - iounmap(dev->mmio); - if (devpriv->las1) - iounmap(devpriv->las1); - if (devpriv->lcfg) - iounmap(devpriv->lcfg); - } - comedi_pci_disable(dev); -} - -static struct comedi_driver rtd520_driver = { - .driver_name = "rtd520", - .module = THIS_MODULE, - .auto_attach = rtd_auto_attach, - .detach = rtd_detach, -}; - -static int rtd520_pci_probe(struct pci_dev *dev, - const struct pci_device_id *id) -{ - return comedi_pci_auto_config(dev, &rtd520_driver, id->driver_data); -} - -static const struct pci_device_id rtd520_pci_table[] = { - { PCI_VDEVICE(RTD, 0x7520), BOARD_DM7520 }, - { PCI_VDEVICE(RTD, 0x4520), BOARD_PCI4520 }, - { 0 } -}; -MODULE_DEVICE_TABLE(pci, rtd520_pci_table); - -static struct pci_driver rtd520_pci_driver = { - .name = "rtd520", - .id_table = rtd520_pci_table, - .probe = rtd520_pci_probe, - .remove = comedi_pci_auto_unconfig, -}; -module_comedi_pci_driver(rtd520_driver, rtd520_pci_driver); - -MODULE_AUTHOR("Comedi https://www.comedi.org"); -MODULE_DESCRIPTION("Comedi low-level driver"); -MODULE_LICENSE("GPL"); |