summaryrefslogtreecommitdiff
path: root/drivers/staging/comedi/drivers/adl_pci9118.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/comedi/drivers/adl_pci9118.c')
-rw-r--r--drivers/staging/comedi/drivers/adl_pci9118.c1736
1 files changed, 0 insertions, 1736 deletions
diff --git a/drivers/staging/comedi/drivers/adl_pci9118.c b/drivers/staging/comedi/drivers/adl_pci9118.c
deleted file mode 100644
index cda3a4267dca..000000000000
--- a/drivers/staging/comedi/drivers/adl_pci9118.c
+++ /dev/null
@@ -1,1736 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * comedi/drivers/adl_pci9118.c
- *
- * hardware driver for ADLink cards:
- * card: PCI-9118DG, PCI-9118HG, PCI-9118HR
- * driver: pci9118dg, pci9118hg, pci9118hr
- *
- * Author: Michal Dobes <dobes@tesnet.cz>
- *
- */
-
-/*
- * Driver: adl_pci9118
- * Description: Adlink PCI-9118DG, PCI-9118HG, PCI-9118HR
- * Author: Michal Dobes <dobes@tesnet.cz>
- * Devices: [ADLink] PCI-9118DG (pci9118dg), PCI-9118HG (pci9118hg),
- * PCI-9118HR (pci9118hr)
- * Status: works
- *
- * This driver supports AI, AO, DI and DO subdevices.
- * AI subdevice supports cmd and insn interface,
- * other subdevices support only insn interface.
- * For AI:
- * - If cmd->scan_begin_src=TRIG_EXT then trigger input is TGIN (pin 46).
- * - If cmd->convert_src=TRIG_EXT then trigger input is EXTTRG (pin 44).
- * - If cmd->start_src/stop_src=TRIG_EXT then trigger input is TGIN (pin 46).
- * - It is not necessary to have cmd.scan_end_arg=cmd.chanlist_len but
- * cmd.scan_end_arg modulo cmd.chanlist_len must by 0.
- * - If return value of cmdtest is 5 then you've bad channel list
- * (it isn't possible mixture S.E. and DIFF inputs or bipolar and unipolar
- * ranges).
- *
- * There are some hardware limitations:
- * a) You cann't use mixture of unipolar/bipoar ranges or differencial/single
- * ended inputs.
- * b) DMA transfers must have the length aligned to two samples (32 bit),
- * so there is some problems if cmd->chanlist_len is odd. This driver tries
- * bypass this with adding one sample to the end of the every scan and discard
- * it on output but this can't be used if cmd->scan_begin_src=TRIG_FOLLOW
- * and is used flag CMDF_WAKE_EOS, then driver switch to interrupt driven mode
- * with interrupt after every sample.
- * c) If isn't used DMA then you can use only mode where
- * cmd->scan_begin_src=TRIG_FOLLOW.
- *
- * Configuration options:
- * [0] - PCI bus of device (optional)
- * [1] - PCI slot of device (optional)
- * If bus/slot is not specified, then first available PCI
- * card will be used.
- * [2] - 0= standard 8 DIFF/16 SE channels configuration
- * n = external multiplexer connected, 1 <= n <= 256
- * [3] - ignored
- * [4] - sample&hold signal - card can generate signal for external S&H board
- * 0 = use SSHO(pin 45) signal is generated in onboard hardware S&H logic
- * 0 != use ADCHN7(pin 23) signal is generated from driver, number say how
- * long delay is requested in ns and sign polarity of the hold
- * (in this case external multiplexor can serve only 128 channels)
- * [5] - ignored
- */
-
-/*
- * FIXME
- *
- * All the supported boards have the same PCI vendor and device IDs, so
- * auto-attachment of PCI devices will always find the first board type.
- *
- * Perhaps the boards have different subdevice IDs that we could use to
- * distinguish them?
- *
- * Need some device attributes so the board type can be corrected after
- * attachment if necessary, and possibly to set other options supported by
- * manual attachment.
- */
-
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/gfp.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-
-#include "../comedi_pci.h"
-
-#include "amcc_s5933.h"
-#include "comedi_8254.h"
-
-/*
- * PCI BAR2 Register map (dev->iobase)
- */
-#define PCI9118_TIMER_BASE 0x00
-#define PCI9118_AI_FIFO_REG 0x10
-#define PCI9118_AO_REG(x) (0x10 + ((x) * 4))
-#define PCI9118_AI_STATUS_REG 0x18
-#define PCI9118_AI_STATUS_NFULL BIT(8) /* 0=FIFO full (fatal) */
-#define PCI9118_AI_STATUS_NHFULL BIT(7) /* 0=FIFO half full */
-#define PCI9118_AI_STATUS_NEPTY BIT(6) /* 0=FIFO empty */
-#define PCI9118_AI_STATUS_ACMP BIT(5) /* 1=about trigger complete */
-#define PCI9118_AI_STATUS_DTH BIT(4) /* 1=ext. digital trigger */
-#define PCI9118_AI_STATUS_BOVER BIT(3) /* 1=burst overrun (fatal) */
-#define PCI9118_AI_STATUS_ADOS BIT(2) /* 1=A/D over speed (warn) */
-#define PCI9118_AI_STATUS_ADOR BIT(1) /* 1=A/D overrun (fatal) */
-#define PCI9118_AI_STATUS_ADRDY BIT(0) /* 1=A/D ready */
-#define PCI9118_AI_CTRL_REG 0x18
-#define PCI9118_AI_CTRL_UNIP BIT(7) /* 1=unipolar */
-#define PCI9118_AI_CTRL_DIFF BIT(6) /* 1=differential inputs */
-#define PCI9118_AI_CTRL_SOFTG BIT(5) /* 1=8254 software gate */
-#define PCI9118_AI_CTRL_EXTG BIT(4) /* 1=8254 TGIN(pin 46) gate */
-#define PCI9118_AI_CTRL_EXTM BIT(3) /* 1=ext. trigger (pin 44) */
-#define PCI9118_AI_CTRL_TMRTR BIT(2) /* 1=8254 is trigger source */
-#define PCI9118_AI_CTRL_INT BIT(1) /* 1=enable interrupt */
-#define PCI9118_AI_CTRL_DMA BIT(0) /* 1=enable DMA */
-#define PCI9118_DIO_REG 0x1c
-#define PCI9118_SOFTTRG_REG 0x20
-#define PCI9118_AI_CHANLIST_REG 0x24
-#define PCI9118_AI_CHANLIST_RANGE(x) (((x) & 0x3) << 8)
-#define PCI9118_AI_CHANLIST_CHAN(x) ((x) << 0)
-#define PCI9118_AI_BURST_NUM_REG 0x28
-#define PCI9118_AI_AUTOSCAN_MODE_REG 0x2c
-#define PCI9118_AI_CFG_REG 0x30
-#define PCI9118_AI_CFG_PDTRG BIT(7) /* 1=positive trigger */
-#define PCI9118_AI_CFG_PETRG BIT(6) /* 1=positive ext. trigger */
-#define PCI9118_AI_CFG_BSSH BIT(5) /* 1=with sample & hold */
-#define PCI9118_AI_CFG_BM BIT(4) /* 1=burst mode */
-#define PCI9118_AI_CFG_BS BIT(3) /* 1=burst mode start */
-#define PCI9118_AI_CFG_PM BIT(2) /* 1=post trigger */
-#define PCI9118_AI_CFG_AM BIT(1) /* 1=about trigger */
-#define PCI9118_AI_CFG_START BIT(0) /* 1=trigger start */
-#define PCI9118_FIFO_RESET_REG 0x34
-#define PCI9118_INT_CTRL_REG 0x38
-#define PCI9118_INT_CTRL_TIMER BIT(3) /* timer interrupt */
-#define PCI9118_INT_CTRL_ABOUT BIT(2) /* about trigger complete */
-#define PCI9118_INT_CTRL_HFULL BIT(1) /* A/D FIFO half full */
-#define PCI9118_INT_CTRL_DTRG BIT(0) /* ext. digital trigger */
-
-#define START_AI_EXT 0x01 /* start measure on external trigger */
-#define STOP_AI_EXT 0x02 /* stop measure on external trigger */
-#define STOP_AI_INT 0x08 /* stop measure on internal trigger */
-
-static const struct comedi_lrange pci9118_ai_range = {
- 8, {
- BIP_RANGE(5),
- BIP_RANGE(2.5),
- BIP_RANGE(1.25),
- BIP_RANGE(0.625),
- UNI_RANGE(10),
- UNI_RANGE(5),
- UNI_RANGE(2.5),
- UNI_RANGE(1.25)
- }
-};
-
-static const struct comedi_lrange pci9118hg_ai_range = {
- 8, {
- BIP_RANGE(5),
- BIP_RANGE(0.5),
- BIP_RANGE(0.05),
- BIP_RANGE(0.005),
- UNI_RANGE(10),
- UNI_RANGE(1),
- UNI_RANGE(0.1),
- UNI_RANGE(0.01)
- }
-};
-
-enum pci9118_boardid {
- BOARD_PCI9118DG,
- BOARD_PCI9118HG,
- BOARD_PCI9118HR,
-};
-
-struct pci9118_boardinfo {
- const char *name;
- unsigned int ai_is_16bit:1;
- unsigned int is_hg:1;
-};
-
-static const struct pci9118_boardinfo pci9118_boards[] = {
- [BOARD_PCI9118DG] = {
- .name = "pci9118dg",
- },
- [BOARD_PCI9118HG] = {
- .name = "pci9118hg",
- .is_hg = 1,
- },
- [BOARD_PCI9118HR] = {
- .name = "pci9118hr",
- .ai_is_16bit = 1,
- },
-};
-
-struct pci9118_dmabuf {
- unsigned short *virt; /* virtual address of buffer */
- dma_addr_t hw; /* hardware (bus) address of buffer */
- unsigned int size; /* size of dma buffer in bytes */
- unsigned int use_size; /* which size we may now use for transfer */
-};
-
-struct pci9118_private {
- unsigned long iobase_a; /* base+size for AMCC chip */
- unsigned int master:1;
- unsigned int dma_doublebuf:1;
- unsigned int ai_neverending:1;
- unsigned int usedma:1;
- unsigned int usemux:1;
- unsigned char ai_ctrl;
- unsigned char int_ctrl;
- unsigned char ai_cfg;
- unsigned int ai_do; /* what do AI? 0=nothing, 1 to 4 mode */
- unsigned int ai_n_realscanlen; /*
- * what we must transfer for one
- * outgoing scan include front/back adds
- */
- unsigned int ai_act_dmapos; /* position in actual real stream */
- unsigned int ai_add_front; /*
- * how many channels we must add
- * before scan to satisfy S&H?
- */
- unsigned int ai_add_back; /*
- * how many channels we must add
- * before scan to satisfy DMA?
- */
- unsigned int ai_flags;
- char ai12_startstop; /*
- * measure can start/stop
- * on external trigger
- */
- unsigned int dma_actbuf; /* which buffer is used now */
- struct pci9118_dmabuf dmabuf[2];
- int softsshdelay; /*
- * >0 use software S&H,
- * numer is requested delay in ns
- */
- unsigned char softsshsample; /*
- * polarity of S&H signal
- * in sample state
- */
- unsigned char softsshhold; /*
- * polarity of S&H signal
- * in hold state
- */
- unsigned int ai_ns_min;
-};
-
-static void pci9118_amcc_setup_dma(struct comedi_device *dev, unsigned int buf)
-{
- struct pci9118_private *devpriv = dev->private;
- struct pci9118_dmabuf *dmabuf = &devpriv->dmabuf[buf];
-
- /* set the master write address and transfer count */
- outl(dmabuf->hw, devpriv->iobase_a + AMCC_OP_REG_MWAR);
- outl(dmabuf->use_size, devpriv->iobase_a + AMCC_OP_REG_MWTC);
-}
-
-static void pci9118_amcc_dma_ena(struct comedi_device *dev, bool enable)
-{
- struct pci9118_private *devpriv = dev->private;
- unsigned int mcsr;
-
- mcsr = inl(devpriv->iobase_a + AMCC_OP_REG_MCSR);
- if (enable)
- mcsr |= RESET_A2P_FLAGS | A2P_HI_PRIORITY | EN_A2P_TRANSFERS;
- else
- mcsr &= ~EN_A2P_TRANSFERS;
- outl(mcsr, devpriv->iobase_a + AMCC_OP_REG_MCSR);
-}
-
-static void pci9118_amcc_int_ena(struct comedi_device *dev, bool enable)
-{
- struct pci9118_private *devpriv = dev->private;
- unsigned int intcsr;
-
- /* enable/disable interrupt for AMCC Incoming Mailbox 4 (32-bit) */
- intcsr = inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR);
- if (enable)
- intcsr |= 0x1f00;
- else
- intcsr &= ~0x1f00;
- outl(intcsr, devpriv->iobase_a + AMCC_OP_REG_INTCSR);
-}
-
-static void pci9118_ai_reset_fifo(struct comedi_device *dev)
-{
- /* writing any value resets the A/D FIFO */
- outl(0, dev->iobase + PCI9118_FIFO_RESET_REG);
-}
-
-static int pci9118_ai_check_chanlist(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_cmd *cmd)
-{
- struct pci9118_private *devpriv = dev->private;
- unsigned int range0 = CR_RANGE(cmd->chanlist[0]);
- unsigned int aref0 = CR_AREF(cmd->chanlist[0]);
- int i;
-
- /* single channel scans are always ok */
- if (cmd->chanlist_len == 1)
- return 0;
-
- for (i = 1; i < cmd->chanlist_len; i++) {
- unsigned int chan = CR_CHAN(cmd->chanlist[i]);
- unsigned int range = CR_RANGE(cmd->chanlist[i]);
- unsigned int aref = CR_AREF(cmd->chanlist[i]);
-
- if (aref != aref0) {
- dev_err(dev->class_dev,
- "Differential and single ended inputs can't be mixed!\n");
- return -EINVAL;
- }
- if (comedi_range_is_bipolar(s, range) !=
- comedi_range_is_bipolar(s, range0)) {
- dev_err(dev->class_dev,
- "Bipolar and unipolar ranges can't be mixed!\n");
- return -EINVAL;
- }
- if (!devpriv->usemux && aref == AREF_DIFF &&
- (chan >= (s->n_chan / 2))) {
- dev_err(dev->class_dev,
- "AREF_DIFF is only available for the first 8 channels!\n");
- return -EINVAL;
- }
- }
-
- return 0;
-}
-
-static void pci9118_set_chanlist(struct comedi_device *dev,
- struct comedi_subdevice *s,
- int n_chan, unsigned int *chanlist,
- int frontadd, int backadd)
-{
- struct pci9118_private *devpriv = dev->private;
- unsigned int chan0 = CR_CHAN(chanlist[0]);
- unsigned int range0 = CR_RANGE(chanlist[0]);
- unsigned int aref0 = CR_AREF(chanlist[0]);
- unsigned int ssh = 0x00;
- unsigned int val;
- int i;
-
- /*
- * Configure analog input based on the first chanlist entry.
- * All entries are either unipolar or bipolar and single-ended
- * or differential.
- */
- devpriv->ai_ctrl = 0;
- if (comedi_range_is_unipolar(s, range0))
- devpriv->ai_ctrl |= PCI9118_AI_CTRL_UNIP;
- if (aref0 == AREF_DIFF)
- devpriv->ai_ctrl |= PCI9118_AI_CTRL_DIFF;
- outl(devpriv->ai_ctrl, dev->iobase + PCI9118_AI_CTRL_REG);
-
- /* gods know why this sequence! */
- outl(2, dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG);
- outl(0, dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG);
- outl(1, dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG);
-
- /* insert channels for S&H */
- if (frontadd) {
- val = PCI9118_AI_CHANLIST_CHAN(chan0) |
- PCI9118_AI_CHANLIST_RANGE(range0);
- ssh = devpriv->softsshsample;
- for (i = 0; i < frontadd; i++) {
- outl(val | ssh, dev->iobase + PCI9118_AI_CHANLIST_REG);
- ssh = devpriv->softsshhold;
- }
- }
-
- /* store chanlist */
- for (i = 0; i < n_chan; i++) {
- unsigned int chan = CR_CHAN(chanlist[i]);
- unsigned int range = CR_RANGE(chanlist[i]);
-
- val = PCI9118_AI_CHANLIST_CHAN(chan) |
- PCI9118_AI_CHANLIST_RANGE(range);
- outl(val | ssh, dev->iobase + PCI9118_AI_CHANLIST_REG);
- }
-
- /* insert channels to fit onto 32bit DMA */
- if (backadd) {
- val = PCI9118_AI_CHANLIST_CHAN(chan0) |
- PCI9118_AI_CHANLIST_RANGE(range0);
- for (i = 0; i < backadd; i++)
- outl(val | ssh, dev->iobase + PCI9118_AI_CHANLIST_REG);
- }
- /* close scan queue */
- outl(0, dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG);
- /* udelay(100); important delay, or first sample will be crippled */
-}
-
-static void pci9118_ai_mode4_switch(struct comedi_device *dev,
- unsigned int next_buf)
-{
- struct pci9118_private *devpriv = dev->private;
- struct pci9118_dmabuf *dmabuf = &devpriv->dmabuf[next_buf];
-
- devpriv->ai_cfg = PCI9118_AI_CFG_PDTRG | PCI9118_AI_CFG_PETRG |
- PCI9118_AI_CFG_AM;
- outl(devpriv->ai_cfg, dev->iobase + PCI9118_AI_CFG_REG);
- comedi_8254_load(dev->pacer, 0, dmabuf->hw >> 1,
- I8254_MODE0 | I8254_BINARY);
- devpriv->ai_cfg |= PCI9118_AI_CFG_START;
- outl(devpriv->ai_cfg, dev->iobase + PCI9118_AI_CFG_REG);
-}
-
-static unsigned int pci9118_ai_samples_ready(struct comedi_device *dev,
- struct comedi_subdevice *s,
- unsigned int n_raw_samples)
-{
- struct pci9118_private *devpriv = dev->private;
- struct comedi_cmd *cmd = &s->async->cmd;
- unsigned int start_pos = devpriv->ai_add_front;
- unsigned int stop_pos = start_pos + cmd->chanlist_len;
- unsigned int span_len = stop_pos + devpriv->ai_add_back;
- unsigned int dma_pos = devpriv->ai_act_dmapos;
- unsigned int whole_spans, n_samples, x;
-
- if (span_len == cmd->chanlist_len)
- return n_raw_samples; /* use all samples */
-
- /*
- * Not all samples are to be used. Buffer contents consist of a
- * possibly non-whole number of spans and a region of each span
- * is to be used.
- *
- * Account for samples in whole number of spans.
- */
- whole_spans = n_raw_samples / span_len;
- n_samples = whole_spans * cmd->chanlist_len;
- n_raw_samples -= whole_spans * span_len;
-
- /*
- * Deal with remaining samples which could overlap up to two spans.
- */
- while (n_raw_samples) {
- if (dma_pos < start_pos) {
- /* Skip samples before start position. */
- x = start_pos - dma_pos;
- if (x > n_raw_samples)
- x = n_raw_samples;
- dma_pos += x;
- n_raw_samples -= x;
- if (!n_raw_samples)
- break;
- }
- if (dma_pos < stop_pos) {
- /* Include samples before stop position. */
- x = stop_pos - dma_pos;
- if (x > n_raw_samples)
- x = n_raw_samples;
- n_samples += x;
- dma_pos += x;
- n_raw_samples -= x;
- }
- /* Advance to next span. */
- start_pos += span_len;
- stop_pos += span_len;
- }
- return n_samples;
-}
-
-static void pci9118_ai_dma_xfer(struct comedi_device *dev,
- struct comedi_subdevice *s,
- unsigned short *dma_buffer,
- unsigned int n_raw_samples)
-{
- struct pci9118_private *devpriv = dev->private;
- struct comedi_cmd *cmd = &s->async->cmd;
- unsigned int start_pos = devpriv->ai_add_front;
- unsigned int stop_pos = start_pos + cmd->chanlist_len;
- unsigned int span_len = stop_pos + devpriv->ai_add_back;
- unsigned int dma_pos = devpriv->ai_act_dmapos;
- unsigned int x;
-
- if (span_len == cmd->chanlist_len) {
- /* All samples are to be copied. */
- comedi_buf_write_samples(s, dma_buffer, n_raw_samples);
- dma_pos += n_raw_samples;
- } else {
- /*
- * Not all samples are to be copied. Buffer contents consist
- * of a possibly non-whole number of spans and a region of
- * each span is to be copied.
- */
- while (n_raw_samples) {
- if (dma_pos < start_pos) {
- /* Skip samples before start position. */
- x = start_pos - dma_pos;
- if (x > n_raw_samples)
- x = n_raw_samples;
- dma_pos += x;
- n_raw_samples -= x;
- if (!n_raw_samples)
- break;
- }
- if (dma_pos < stop_pos) {
- /* Copy samples before stop position. */
- x = stop_pos - dma_pos;
- if (x > n_raw_samples)
- x = n_raw_samples;
- comedi_buf_write_samples(s, dma_buffer, x);
- dma_pos += x;
- n_raw_samples -= x;
- }
- /* Advance to next span. */
- start_pos += span_len;
- stop_pos += span_len;
- }
- }
- /* Update position in span for next time. */
- devpriv->ai_act_dmapos = dma_pos % span_len;
-}
-
-static void pci9118_exttrg_enable(struct comedi_device *dev, bool enable)
-{
- struct pci9118_private *devpriv = dev->private;
-
- if (enable)
- devpriv->int_ctrl |= PCI9118_INT_CTRL_DTRG;
- else
- devpriv->int_ctrl &= ~PCI9118_INT_CTRL_DTRG;
- outl(devpriv->int_ctrl, dev->iobase + PCI9118_INT_CTRL_REG);
-
- if (devpriv->int_ctrl)
- pci9118_amcc_int_ena(dev, true);
- else
- pci9118_amcc_int_ena(dev, false);
-}
-
-static void pci9118_calc_divisors(struct comedi_device *dev,
- struct comedi_subdevice *s,
- unsigned int *tim1, unsigned int *tim2,
- unsigned int flags, int chans,
- unsigned int *div1, unsigned int *div2,
- unsigned int chnsshfront)
-{
- struct comedi_8254 *pacer = dev->pacer;
- struct comedi_cmd *cmd = &s->async->cmd;
-
- *div1 = *tim2 / pacer->osc_base; /* convert timer (burst) */
- *div2 = *tim1 / pacer->osc_base; /* scan timer */
- *div2 = *div2 / *div1; /* major timer is c1*c2 */
- if (*div2 < chans)
- *div2 = chans;
-
- *tim2 = *div1 * pacer->osc_base; /* real convert timer */
-
- if (cmd->convert_src == TRIG_NOW && !chnsshfront) {
- /* use BSSH signal */
- if (*div2 < (chans + 2))
- *div2 = chans + 2;
- }
-
- *tim1 = *div1 * *div2 * pacer->osc_base;
-}
-
-static void pci9118_start_pacer(struct comedi_device *dev, int mode)
-{
- if (mode == 1 || mode == 2 || mode == 4)
- comedi_8254_pacer_enable(dev->pacer, 1, 2, true);
-}
-
-static int pci9118_ai_cancel(struct comedi_device *dev,
- struct comedi_subdevice *s)
-{
- struct pci9118_private *devpriv = dev->private;
-
- if (devpriv->usedma)
- pci9118_amcc_dma_ena(dev, false);
- pci9118_exttrg_enable(dev, false);
- comedi_8254_pacer_enable(dev->pacer, 1, 2, false);
- /* set default config (disable burst and triggers) */
- devpriv->ai_cfg = PCI9118_AI_CFG_PDTRG | PCI9118_AI_CFG_PETRG;
- outl(devpriv->ai_cfg, dev->iobase + PCI9118_AI_CFG_REG);
- /* reset acquisition control */
- devpriv->ai_ctrl = 0;
- outl(devpriv->ai_ctrl, dev->iobase + PCI9118_AI_CTRL_REG);
- outl(0, dev->iobase + PCI9118_AI_BURST_NUM_REG);
- /* reset scan queue */
- outl(1, dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG);
- outl(2, dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG);
- pci9118_ai_reset_fifo(dev);
-
- devpriv->int_ctrl = 0;
- outl(devpriv->int_ctrl, dev->iobase + PCI9118_INT_CTRL_REG);
- pci9118_amcc_int_ena(dev, false);
-
- devpriv->ai_do = 0;
- devpriv->usedma = 0;
-
- devpriv->ai_act_dmapos = 0;
- s->async->inttrig = NULL;
- devpriv->ai_neverending = 0;
- devpriv->dma_actbuf = 0;
-
- return 0;
-}
-
-static void pci9118_ai_munge(struct comedi_device *dev,
- struct comedi_subdevice *s, void *data,
- unsigned int num_bytes,
- unsigned int start_chan_index)
-{
- struct pci9118_private *devpriv = dev->private;
- unsigned short *array = data;
- unsigned int num_samples = comedi_bytes_to_samples(s, num_bytes);
- unsigned int i;
- __be16 *barray = data;
-
- for (i = 0; i < num_samples; i++) {
- if (devpriv->usedma)
- array[i] = be16_to_cpu(barray[i]);
- if (s->maxdata == 0xffff)
- array[i] ^= 0x8000;
- else
- array[i] = (array[i] >> 4) & 0x0fff;
- }
-}
-
-static void pci9118_ai_get_onesample(struct comedi_device *dev,
- struct comedi_subdevice *s)
-{
- struct pci9118_private *devpriv = dev->private;
- struct comedi_cmd *cmd = &s->async->cmd;
- unsigned short sampl;
-
- sampl = inl(dev->iobase + PCI9118_AI_FIFO_REG);
-
- comedi_buf_write_samples(s, &sampl, 1);
-
- if (!devpriv->ai_neverending) {
- if (s->async->scans_done >= cmd->stop_arg)
- s->async->events |= COMEDI_CB_EOA;
- }
-}
-
-static void pci9118_ai_get_dma(struct comedi_device *dev,
- struct comedi_subdevice *s)
-{
- struct pci9118_private *devpriv = dev->private;
- struct comedi_cmd *cmd = &s->async->cmd;
- struct pci9118_dmabuf *dmabuf = &devpriv->dmabuf[devpriv->dma_actbuf];
- unsigned int n_all = comedi_bytes_to_samples(s, dmabuf->use_size);
- unsigned int n_valid;
- bool more_dma;
-
- /* determine whether more DMA buffers to do after this one */
- n_valid = pci9118_ai_samples_ready(dev, s, n_all);
- more_dma = n_valid < comedi_nsamples_left(s, n_valid + 1);
-
- /* switch DMA buffers and restart DMA if double buffering */
- if (more_dma && devpriv->dma_doublebuf) {
- devpriv->dma_actbuf = 1 - devpriv->dma_actbuf;
- pci9118_amcc_setup_dma(dev, devpriv->dma_actbuf);
- if (devpriv->ai_do == 4)
- pci9118_ai_mode4_switch(dev, devpriv->dma_actbuf);
- }
-
- if (n_all)
- pci9118_ai_dma_xfer(dev, s, dmabuf->virt, n_all);
-
- if (!devpriv->ai_neverending) {
- if (s->async->scans_done >= cmd->stop_arg)
- s->async->events |= COMEDI_CB_EOA;
- }
-
- if (s->async->events & COMEDI_CB_CANCEL_MASK)
- more_dma = false;
-
- /* restart DMA if not double buffering */
- if (more_dma && !devpriv->dma_doublebuf) {
- pci9118_amcc_setup_dma(dev, 0);
- if (devpriv->ai_do == 4)
- pci9118_ai_mode4_switch(dev, 0);
- }
-}
-
-static irqreturn_t pci9118_interrupt(int irq, void *d)
-{
- struct comedi_device *dev = d;
- struct comedi_subdevice *s = dev->read_subdev;
- struct pci9118_private *devpriv = dev->private;
- unsigned int intsrc; /* IRQ reasons from card */
- unsigned int intcsr; /* INT register from AMCC chip */
- unsigned int adstat; /* STATUS register */
-
- if (!dev->attached)
- return IRQ_NONE;
-
- intsrc = inl(dev->iobase + PCI9118_INT_CTRL_REG) & 0xf;
- intcsr = inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR);
-
- if (!intsrc && !(intcsr & ANY_S593X_INT))
- return IRQ_NONE;
-
- outl(intcsr | 0x00ff0000, devpriv->iobase_a + AMCC_OP_REG_INTCSR);
-
- if (intcsr & MASTER_ABORT_INT) {
- dev_err(dev->class_dev, "AMCC IRQ - MASTER DMA ABORT!\n");
- s->async->events |= COMEDI_CB_ERROR;
- goto interrupt_exit;
- }
-
- if (intcsr & TARGET_ABORT_INT) {
- dev_err(dev->class_dev, "AMCC IRQ - TARGET DMA ABORT!\n");
- s->async->events |= COMEDI_CB_ERROR;
- goto interrupt_exit;
- }
-
- adstat = inl(dev->iobase + PCI9118_AI_STATUS_REG);
- if ((adstat & PCI9118_AI_STATUS_NFULL) == 0) {
- dev_err(dev->class_dev,
- "A/D FIFO Full status (Fatal Error!)\n");
- s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW;
- goto interrupt_exit;
- }
- if (adstat & PCI9118_AI_STATUS_BOVER) {
- dev_err(dev->class_dev,
- "A/D Burst Mode Overrun Status (Fatal Error!)\n");
- s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW;
- goto interrupt_exit;
- }
- if (adstat & PCI9118_AI_STATUS_ADOS) {
- dev_err(dev->class_dev, "A/D Over Speed Status (Warning!)\n");
- s->async->events |= COMEDI_CB_ERROR;
- goto interrupt_exit;
- }
- if (adstat & PCI9118_AI_STATUS_ADOR) {
- dev_err(dev->class_dev, "A/D Overrun Status (Fatal Error!)\n");
- s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW;
- goto interrupt_exit;
- }
-
- if (!devpriv->ai_do)
- return IRQ_HANDLED;
-
- if (devpriv->ai12_startstop) {
- if ((adstat & PCI9118_AI_STATUS_DTH) &&
- (intsrc & PCI9118_INT_CTRL_DTRG)) {
- /* start/stop of measure */
- if (devpriv->ai12_startstop & START_AI_EXT) {
- /* deactivate EXT trigger */
- devpriv->ai12_startstop &= ~START_AI_EXT;
- if (!(devpriv->ai12_startstop & STOP_AI_EXT))
- pci9118_exttrg_enable(dev, false);
-
- /* start pacer */
- pci9118_start_pacer(dev, devpriv->ai_do);
- outl(devpriv->ai_ctrl,
- dev->iobase + PCI9118_AI_CTRL_REG);
- } else if (devpriv->ai12_startstop & STOP_AI_EXT) {
- /* deactivate EXT trigger */
- devpriv->ai12_startstop &= ~STOP_AI_EXT;
- pci9118_exttrg_enable(dev, false);
-
- /* on next interrupt measure will stop */
- devpriv->ai_neverending = 0;
- }
- }
- }
-
- if (devpriv->usedma)
- pci9118_ai_get_dma(dev, s);
- else
- pci9118_ai_get_onesample(dev, s);
-
-interrupt_exit:
- comedi_handle_events(dev, s);
- return IRQ_HANDLED;
-}
-
-static void pci9118_ai_cmd_start(struct comedi_device *dev)
-{
- struct pci9118_private *devpriv = dev->private;
-
- outl(devpriv->int_ctrl, dev->iobase + PCI9118_INT_CTRL_REG);
- outl(devpriv->ai_cfg, dev->iobase + PCI9118_AI_CFG_REG);
- if (devpriv->ai_do != 3) {
- pci9118_start_pacer(dev, devpriv->ai_do);
- devpriv->ai_ctrl |= PCI9118_AI_CTRL_SOFTG;
- }
- outl(devpriv->ai_ctrl, dev->iobase + PCI9118_AI_CTRL_REG);
-}
-
-static int pci9118_ai_inttrig(struct comedi_device *dev,
- struct comedi_subdevice *s,
- unsigned int trig_num)
-{
- struct comedi_cmd *cmd = &s->async->cmd;
-
- if (trig_num != cmd->start_arg)
- return -EINVAL;
-
- s->async->inttrig = NULL;
- pci9118_ai_cmd_start(dev);
-
- return 1;
-}
-
-static int pci9118_ai_setup_dma(struct comedi_device *dev,
- struct comedi_subdevice *s)
-{
- struct pci9118_private *devpriv = dev->private;
- struct comedi_cmd *cmd = &s->async->cmd;
- struct pci9118_dmabuf *dmabuf0 = &devpriv->dmabuf[0];
- struct pci9118_dmabuf *dmabuf1 = &devpriv->dmabuf[1];
- unsigned int dmalen0 = dmabuf0->size;
- unsigned int dmalen1 = dmabuf1->size;
- unsigned int scan_bytes = devpriv->ai_n_realscanlen *
- comedi_bytes_per_sample(s);
-
- /* isn't output buff smaller that our DMA buff? */
- if (dmalen0 > s->async->prealloc_bufsz) {
- /* align to 32bit down */
- dmalen0 = s->async->prealloc_bufsz & ~3L;
- }
- if (dmalen1 > s->async->prealloc_bufsz) {
- /* align to 32bit down */
- dmalen1 = s->async->prealloc_bufsz & ~3L;
- }
-
- /* we want wake up every scan? */
- if (devpriv->ai_flags & CMDF_WAKE_EOS) {
- if (dmalen0 < scan_bytes) {
- /* uff, too short DMA buffer, disable EOS support! */
- devpriv->ai_flags &= (~CMDF_WAKE_EOS);
- dev_info(dev->class_dev,
- "WAR: DMA0 buf too short, can't support CMDF_WAKE_EOS (%d<%d)\n",
- dmalen0, scan_bytes);
- } else {
- /* short first DMA buffer to one scan */
- dmalen0 = scan_bytes;
- if (dmalen0 < 4) {
- dev_info(dev->class_dev,
- "ERR: DMA0 buf len bug? (%d<4)\n",
- dmalen0);
- dmalen0 = 4;
- }
- }
- }
- if (devpriv->ai_flags & CMDF_WAKE_EOS) {
- if (dmalen1 < scan_bytes) {
- /* uff, too short DMA buffer, disable EOS support! */
- devpriv->ai_flags &= (~CMDF_WAKE_EOS);
- dev_info(dev->class_dev,
- "WAR: DMA1 buf too short, can't support CMDF_WAKE_EOS (%d<%d)\n",
- dmalen1, scan_bytes);
- } else {
- /* short second DMA buffer to one scan */
- dmalen1 = scan_bytes;
- if (dmalen1 < 4) {
- dev_info(dev->class_dev,
- "ERR: DMA1 buf len bug? (%d<4)\n",
- dmalen1);
- dmalen1 = 4;
- }
- }
- }
-
- /* transfer without CMDF_WAKE_EOS */
- if (!(devpriv->ai_flags & CMDF_WAKE_EOS)) {
- unsigned int tmp;
-
- /* if it's possible then align DMA buffers to length of scan */
- tmp = dmalen0;
- dmalen0 = (dmalen0 / scan_bytes) * scan_bytes;
- dmalen0 &= ~3L;
- if (!dmalen0)
- dmalen0 = tmp; /* uff. very long scan? */
- tmp = dmalen1;
- dmalen1 = (dmalen1 / scan_bytes) * scan_bytes;
- dmalen1 &= ~3L;
- if (!dmalen1)
- dmalen1 = tmp; /* uff. very long scan? */
- /*
- * if measure isn't neverending then test, if it fits whole
- * into one or two DMA buffers
- */
- if (!devpriv->ai_neverending) {
- unsigned long long scanlen;
-
- scanlen = (unsigned long long)scan_bytes *
- cmd->stop_arg;
-
- /* fits whole measure into one DMA buffer? */
- if (dmalen0 > scanlen) {
- dmalen0 = scanlen;
- dmalen0 &= ~3L;
- } else {
- /* fits whole measure into two DMA buffer? */
- if (dmalen1 > (scanlen - dmalen0)) {
- dmalen1 = scanlen - dmalen0;
- dmalen1 &= ~3L;
- }
- }
- }
- }
-
- /* these DMA buffer size will be used */
- devpriv->dma_actbuf = 0;
- dmabuf0->use_size = dmalen0;
- dmabuf1->use_size = dmalen1;
-
- pci9118_amcc_dma_ena(dev, false);
- pci9118_amcc_setup_dma(dev, 0);
- /* init DMA transfer */
- outl(0x00000000 | AINT_WRITE_COMPL,
- devpriv->iobase_a + AMCC_OP_REG_INTCSR);
-/* outl(0x02000000|AINT_WRITE_COMPL, devpriv->iobase_a+AMCC_OP_REG_INTCSR); */
- pci9118_amcc_dma_ena(dev, true);
- outl(inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR) | EN_A2P_TRANSFERS,
- devpriv->iobase_a + AMCC_OP_REG_INTCSR);
- /* allow bus mastering */
-
- return 0;
-}
-
-static int pci9118_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
-{
- struct pci9118_private *devpriv = dev->private;
- struct comedi_8254 *pacer = dev->pacer;
- struct comedi_cmd *cmd = &s->async->cmd;
- unsigned int addchans = 0;
- unsigned int scanlen;
-
- devpriv->ai12_startstop = 0;
- devpriv->ai_flags = cmd->flags;
- devpriv->ai_add_front = 0;
- devpriv->ai_add_back = 0;
-
- /* prepare for start/stop conditions */
- if (cmd->start_src == TRIG_EXT)
- devpriv->ai12_startstop |= START_AI_EXT;
- if (cmd->stop_src == TRIG_EXT) {
- devpriv->ai_neverending = 1;
- devpriv->ai12_startstop |= STOP_AI_EXT;
- }
- if (cmd->stop_src == TRIG_NONE)
- devpriv->ai_neverending = 1;
- if (cmd->stop_src == TRIG_COUNT)
- devpriv->ai_neverending = 0;
-
- /*
- * use additional sample at end of every scan
- * to satisty DMA 32 bit transfer?
- */
- devpriv->ai_add_front = 0;
- devpriv->ai_add_back = 0;
- if (devpriv->master) {
- devpriv->usedma = 1;
- if ((cmd->flags & CMDF_WAKE_EOS) &&
- (cmd->scan_end_arg == 1)) {
- if (cmd->convert_src == TRIG_NOW)
- devpriv->ai_add_back = 1;
- if (cmd->convert_src == TRIG_TIMER) {
- devpriv->usedma = 0;
- /*
- * use INT transfer if scanlist
- * have only one channel
- */
- }
- }
- if ((cmd->flags & CMDF_WAKE_EOS) &&
- (cmd->scan_end_arg & 1) &&
- (cmd->scan_end_arg > 1)) {
- if (cmd->scan_begin_src == TRIG_FOLLOW) {
- devpriv->usedma = 0;
- /*
- * XXX maybe can be corrected to use 16 bit DMA
- */
- } else { /*
- * well, we must insert one sample
- * to end of EOS to meet 32 bit transfer
- */
- devpriv->ai_add_back = 1;
- }
- }
- } else { /* interrupt transfer don't need any correction */
- devpriv->usedma = 0;
- }
-
- /*
- * we need software S&H signal?
- * It adds two samples before every scan as minimum
- */
- if (cmd->convert_src == TRIG_NOW && devpriv->softsshdelay) {
- devpriv->ai_add_front = 2;
- if ((devpriv->usedma == 1) && (devpriv->ai_add_back == 1)) {
- /* move it to front */
- devpriv->ai_add_front++;
- devpriv->ai_add_back = 0;
- }
- if (cmd->convert_arg < devpriv->ai_ns_min)
- cmd->convert_arg = devpriv->ai_ns_min;
- addchans = devpriv->softsshdelay / cmd->convert_arg;
- if (devpriv->softsshdelay % cmd->convert_arg)
- addchans++;
- if (addchans > (devpriv->ai_add_front - 1)) {
- /* uff, still short */
- devpriv->ai_add_front = addchans + 1;
- if (devpriv->usedma == 1)
- if ((devpriv->ai_add_front +
- cmd->chanlist_len +
- devpriv->ai_add_back) & 1)
- devpriv->ai_add_front++;
- /* round up to 32 bit */
- }
- }
- /* well, we now know what must be all added */
- scanlen = devpriv->ai_add_front + cmd->chanlist_len +
- devpriv->ai_add_back;
- /*
- * what we must take from card in real to have cmd->scan_end_arg
- * on output?
- */
- devpriv->ai_n_realscanlen = scanlen *
- (cmd->scan_end_arg / cmd->chanlist_len);
-
- if (scanlen > s->len_chanlist) {
- dev_err(dev->class_dev,
- "range/channel list is too long for actual configuration!\n");
- return -EINVAL;
- }
-
- /*
- * Configure analog input and load the chanlist.
- * The acquisition control bits are enabled later.
- */
- pci9118_set_chanlist(dev, s, cmd->chanlist_len, cmd->chanlist,
- devpriv->ai_add_front, devpriv->ai_add_back);
-
- /* Determine acquisition mode and calculate timing */
- devpriv->ai_do = 0;
- if (cmd->scan_begin_src != TRIG_TIMER &&
- cmd->convert_src == TRIG_TIMER) {
- /* cascaded timers 1 and 2 are used for convert timing */
- if (cmd->scan_begin_src == TRIG_EXT)
- devpriv->ai_do = 4;
- else
- devpriv->ai_do = 1;
-
- comedi_8254_cascade_ns_to_timer(pacer, &cmd->convert_arg,
- devpriv->ai_flags &
- CMDF_ROUND_NEAREST);
- comedi_8254_update_divisors(pacer);
-
- devpriv->ai_ctrl |= PCI9118_AI_CTRL_TMRTR;
-
- if (!devpriv->usedma) {
- devpriv->ai_ctrl |= PCI9118_AI_CTRL_INT;
- devpriv->int_ctrl |= PCI9118_INT_CTRL_TIMER;
- }
-
- if (cmd->scan_begin_src == TRIG_EXT) {
- struct pci9118_dmabuf *dmabuf = &devpriv->dmabuf[0];
-
- devpriv->ai_cfg |= PCI9118_AI_CFG_AM;
- outl(devpriv->ai_cfg, dev->iobase + PCI9118_AI_CFG_REG);
- comedi_8254_load(pacer, 0, dmabuf->hw >> 1,
- I8254_MODE0 | I8254_BINARY);
- devpriv->ai_cfg |= PCI9118_AI_CFG_START;
- }
- }
-
- if (cmd->scan_begin_src == TRIG_TIMER &&
- cmd->convert_src != TRIG_EXT) {
- if (!devpriv->usedma) {
- dev_err(dev->class_dev,
- "cmd->scan_begin_src=TRIG_TIMER works only with bus mastering!\n");
- return -EIO;
- }
-
- /* double timed action */
- devpriv->ai_do = 2;
-
- pci9118_calc_divisors(dev, s,
- &cmd->scan_begin_arg, &cmd->convert_arg,
- devpriv->ai_flags,
- devpriv->ai_n_realscanlen,
- &pacer->divisor1,
- &pacer->divisor2,
- devpriv->ai_add_front);
-
- devpriv->ai_ctrl |= PCI9118_AI_CTRL_TMRTR;
- devpriv->ai_cfg |= PCI9118_AI_CFG_BM | PCI9118_AI_CFG_BS;
- if (cmd->convert_src == TRIG_NOW && !devpriv->softsshdelay)
- devpriv->ai_cfg |= PCI9118_AI_CFG_BSSH;
- outl(devpriv->ai_n_realscanlen,
- dev->iobase + PCI9118_AI_BURST_NUM_REG);
- }
-
- if (cmd->scan_begin_src == TRIG_FOLLOW &&
- cmd->convert_src == TRIG_EXT) {
- /* external trigger conversion */
- devpriv->ai_do = 3;
-
- devpriv->ai_ctrl |= PCI9118_AI_CTRL_EXTM;
- }
-
- if (devpriv->ai_do == 0) {
- dev_err(dev->class_dev,
- "Unable to determine acquisition mode! BUG in (*do_cmdtest)?\n");
- return -EINVAL;
- }
-
- if (devpriv->usedma)
- devpriv->ai_ctrl |= PCI9118_AI_CTRL_DMA;
-
- /* set default config (disable burst and triggers) */
- devpriv->ai_cfg = PCI9118_AI_CFG_PDTRG | PCI9118_AI_CFG_PETRG;
- outl(devpriv->ai_cfg, dev->iobase + PCI9118_AI_CFG_REG);
- udelay(1);
- pci9118_ai_reset_fifo(dev);
-
- /* clear A/D and INT status registers */
- inl(dev->iobase + PCI9118_AI_STATUS_REG);
- inl(dev->iobase + PCI9118_INT_CTRL_REG);
-
- devpriv->ai_act_dmapos = 0;
-
- if (devpriv->usedma) {
- pci9118_ai_setup_dma(dev, s);
-
- outl(0x02000000 | AINT_WRITE_COMPL,
- devpriv->iobase_a + AMCC_OP_REG_INTCSR);
- } else {
- pci9118_amcc_int_ena(dev, true);
- }
-
- /* start async command now or wait for internal trigger */
- if (cmd->start_src == TRIG_NOW)
- pci9118_ai_cmd_start(dev);
- else if (cmd->start_src == TRIG_INT)
- s->async->inttrig = pci9118_ai_inttrig;
-
- /* enable external trigger for command start/stop */
- if (cmd->start_src == TRIG_EXT || cmd->stop_src == TRIG_EXT)
- pci9118_exttrg_enable(dev, true);
-
- return 0;
-}
-
-static int pci9118_ai_cmdtest(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_cmd *cmd)
-{
- struct pci9118_private *devpriv = dev->private;
- int err = 0;
- unsigned int flags;
- unsigned int arg;
-
- /* Step 1 : check if triggers are trivially valid */
-
- err |= comedi_check_trigger_src(&cmd->start_src,
- TRIG_NOW | TRIG_EXT | TRIG_INT);
-
- flags = TRIG_FOLLOW;
- if (devpriv->master)
- flags |= TRIG_TIMER | TRIG_EXT;
- err |= comedi_check_trigger_src(&cmd->scan_begin_src, flags);
-
- flags = TRIG_TIMER | TRIG_EXT;
- if (devpriv->master)
- flags |= TRIG_NOW;
- err |= comedi_check_trigger_src(&cmd->convert_src, flags);
-
- err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
- err |= comedi_check_trigger_src(&cmd->stop_src,
- TRIG_COUNT | TRIG_NONE | TRIG_EXT);
-
- if (err)
- return 1;
-
- /* Step 2a : make sure trigger sources are unique */
-
- err |= comedi_check_trigger_is_unique(cmd->start_src);
- 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 (cmd->start_src == TRIG_EXT && cmd->scan_begin_src == TRIG_EXT)
- err |= -EINVAL;
-
- if ((cmd->scan_begin_src & (TRIG_TIMER | TRIG_EXT)) &&
- (!(cmd->convert_src & (TRIG_TIMER | TRIG_NOW))))
- err |= -EINVAL;
-
- if ((cmd->scan_begin_src == TRIG_FOLLOW) &&
- (!(cmd->convert_src & (TRIG_TIMER | TRIG_EXT))))
- err |= -EINVAL;
-
- if (cmd->stop_src == TRIG_EXT && cmd->scan_begin_src == TRIG_EXT)
- err |= -EINVAL;
-
- if (err)
- return 2;
-
- /* Step 3: check if arguments are trivially valid */
-
- switch (cmd->start_src) {
- case TRIG_NOW:
- case TRIG_EXT:
- err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
- break;
- case TRIG_INT:
- /* start_arg is the internal trigger (any value) */
- break;
- }
-
- if (cmd->scan_begin_src & (TRIG_FOLLOW | TRIG_EXT))
- err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
-
- if ((cmd->scan_begin_src == TRIG_TIMER) &&
- (cmd->convert_src == TRIG_TIMER) && (cmd->scan_end_arg == 1)) {
- cmd->scan_begin_src = TRIG_FOLLOW;
- cmd->convert_arg = cmd->scan_begin_arg;
- cmd->scan_begin_arg = 0;
- }
-
- if (cmd->scan_begin_src == TRIG_TIMER) {
- err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
- devpriv->ai_ns_min);
- }
-
- if (cmd->scan_begin_src == TRIG_EXT) {
- if (cmd->scan_begin_arg) {
- cmd->scan_begin_arg = 0;
- err |= -EINVAL;
- err |= comedi_check_trigger_arg_max(&cmd->scan_end_arg,
- 65535);
- }
- }
-
- if (cmd->convert_src & (TRIG_TIMER | TRIG_NOW)) {
- err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
- devpriv->ai_ns_min);
- }
-
- if (cmd->convert_src == TRIG_EXT)
- err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
-
- 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);
-
- err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1);
-
- err |= comedi_check_trigger_arg_min(&cmd->scan_end_arg,
- cmd->chanlist_len);
-
- if ((cmd->scan_end_arg % cmd->chanlist_len)) {
- cmd->scan_end_arg =
- cmd->chanlist_len * (cmd->scan_end_arg / cmd->chanlist_len);
- err |= -EINVAL;
- }
-
- if (err)
- return 3;
-
- /* step 4: fix up any arguments */
-
- if (cmd->scan_begin_src == TRIG_TIMER) {
- arg = cmd->scan_begin_arg;
- comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
- err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
- }
-
- if (cmd->convert_src & (TRIG_TIMER | TRIG_NOW)) {
- arg = cmd->convert_arg;
- comedi_8254_cascade_ns_to_timer(dev->pacer, &arg, cmd->flags);
- err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
-
- if (cmd->scan_begin_src == TRIG_TIMER &&
- cmd->convert_src == TRIG_NOW) {
- if (cmd->convert_arg == 0) {
- arg = devpriv->ai_ns_min *
- (cmd->scan_end_arg + 2);
- } else {
- arg = cmd->convert_arg * cmd->chanlist_len;
- }
- err |= comedi_check_trigger_arg_min(
- &cmd->scan_begin_arg, arg);
- }
- }
-
- if (err)
- return 4;
-
- /* Step 5: check channel list if it exists */
-
- if (cmd->chanlist)
- err |= pci9118_ai_check_chanlist(dev, s, cmd);
-
- if (err)
- return 5;
-
- return 0;
-}
-
-static int pci9118_ai_eoc(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn,
- unsigned long context)
-{
- unsigned int status;
-
- status = inl(dev->iobase + PCI9118_AI_STATUS_REG);
- if (status & PCI9118_AI_STATUS_ADRDY)
- return 0;
- return -EBUSY;
-}
-
-static void pci9118_ai_start_conv(struct comedi_device *dev)
-{
- /* writing any value triggers an A/D conversion */
- outl(0, dev->iobase + PCI9118_SOFTTRG_REG);
-}
-
-static int pci9118_ai_insn_read(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn,
- unsigned int *data)
-{
- struct pci9118_private *devpriv = dev->private;
- unsigned int val;
- int ret;
- int i;
-
- /*
- * Configure analog input based on the chanspec.
- * Acqusition is software controlled without interrupts.
- */
- pci9118_set_chanlist(dev, s, 1, &insn->chanspec, 0, 0);
-
- /* set default config (disable burst and triggers) */
- devpriv->ai_cfg = PCI9118_AI_CFG_PDTRG | PCI9118_AI_CFG_PETRG;
- outl(devpriv->ai_cfg, dev->iobase + PCI9118_AI_CFG_REG);
-
- pci9118_ai_reset_fifo(dev);
-
- for (i = 0; i < insn->n; i++) {
- pci9118_ai_start_conv(dev);
-
- ret = comedi_timeout(dev, s, insn, pci9118_ai_eoc, 0);
- if (ret)
- return ret;
-
- val = inl(dev->iobase + PCI9118_AI_FIFO_REG);
- if (s->maxdata == 0xffff)
- data[i] = (val & 0xffff) ^ 0x8000;
- else
- data[i] = (val >> 4) & 0xfff;
- }
-
- return insn->n;
-}
-
-static int pci9118_ao_insn_write(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn,
- unsigned int *data)
-{
- unsigned int chan = CR_CHAN(insn->chanspec);
- unsigned int val = s->readback[chan];
- int i;
-
- for (i = 0; i < insn->n; i++) {
- val = data[i];
- outl(val, dev->iobase + PCI9118_AO_REG(chan));
- }
- s->readback[chan] = val;
-
- return insn->n;
-}
-
-static int pci9118_di_insn_bits(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn,
- unsigned int *data)
-{
- /*
- * The digital inputs and outputs share the read register.
- * bits [7:4] are the digital outputs
- * bits [3:0] are the digital inputs
- */
- data[1] = inl(dev->iobase + PCI9118_DIO_REG) & 0xf;
-
- return insn->n;
-}
-
-static int pci9118_do_insn_bits(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn,
- unsigned int *data)
-{
- /*
- * The digital outputs are set with the same register that
- * the digital inputs and outputs are read from. But the
- * outputs are set with bits [3:0] so we can simply write
- * the s->state to set them.
- */
- if (comedi_dio_update_state(s, data))
- outl(s->state, dev->iobase + PCI9118_DIO_REG);
-
- data[1] = s->state;
-
- return insn->n;
-}
-
-static void pci9118_reset(struct comedi_device *dev)
-{
- /* reset analog input subsystem */
- outl(0, dev->iobase + PCI9118_INT_CTRL_REG);
- outl(0, dev->iobase + PCI9118_AI_CTRL_REG);
- outl(0, dev->iobase + PCI9118_AI_CFG_REG);
- pci9118_ai_reset_fifo(dev);
-
- /* clear any pending interrupts and status */
- inl(dev->iobase + PCI9118_INT_CTRL_REG);
- inl(dev->iobase + PCI9118_AI_STATUS_REG);
-
- /* reset DMA and scan queue */
- outl(0, dev->iobase + PCI9118_AI_BURST_NUM_REG);
- outl(1, dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG);
- outl(2, dev->iobase + PCI9118_AI_AUTOSCAN_MODE_REG);
-
- /* reset analog outputs to 0V */
- outl(2047, dev->iobase + PCI9118_AO_REG(0));
- outl(2047, dev->iobase + PCI9118_AO_REG(1));
-}
-
-static struct pci_dev *pci9118_find_pci(struct comedi_device *dev,
- struct comedi_devconfig *it)
-{
- struct pci_dev *pcidev = NULL;
- int bus = it->options[0];
- int slot = it->options[1];
-
- for_each_pci_dev(pcidev) {
- if (pcidev->vendor != PCI_VENDOR_ID_AMCC)
- continue;
- if (pcidev->device != 0x80d9)
- continue;
- if (bus || slot) {
- /* requested particular bus/slot */
- if (pcidev->bus->number != bus ||
- PCI_SLOT(pcidev->devfn) != slot)
- continue;
- }
- return pcidev;
- }
- dev_err(dev->class_dev,
- "no supported board found! (req. bus/slot : %d/%d)\n",
- bus, slot);
- return NULL;
-}
-
-static void pci9118_alloc_dma(struct comedi_device *dev)
-{
- struct pci9118_private *devpriv = dev->private;
- struct pci9118_dmabuf *dmabuf;
- int order;
- int i;
-
- for (i = 0; i < 2; i++) {
- dmabuf = &devpriv->dmabuf[i];
- for (order = 2; order >= 0; order--) {
- dmabuf->virt =
- dma_alloc_coherent(dev->hw_dev, PAGE_SIZE << order,
- &dmabuf->hw, GFP_KERNEL);
- if (dmabuf->virt)
- break;
- }
- if (!dmabuf->virt)
- break;
- dmabuf->size = PAGE_SIZE << order;
-
- if (i == 0)
- devpriv->master = 1;
- if (i == 1)
- devpriv->dma_doublebuf = 1;
- }
-}
-
-static void pci9118_free_dma(struct comedi_device *dev)
-{
- struct pci9118_private *devpriv = dev->private;
- struct pci9118_dmabuf *dmabuf;
- int i;
-
- if (!devpriv)
- return;
-
- for (i = 0; i < 2; i++) {
- dmabuf = &devpriv->dmabuf[i];
- if (dmabuf->virt) {
- dma_free_coherent(dev->hw_dev, dmabuf->size,
- dmabuf->virt, dmabuf->hw);
- }
- }
-}
-
-static int pci9118_common_attach(struct comedi_device *dev,
- int ext_mux, int softsshdelay)
-{
- const struct pci9118_boardinfo *board = dev->board_ptr;
- struct pci_dev *pcidev = comedi_to_pci_dev(dev);
- struct pci9118_private *devpriv;
- struct comedi_subdevice *s;
- int ret;
- int i;
- u16 u16w;
-
- devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
- if (!devpriv)
- return -ENOMEM;
-
- ret = comedi_pci_enable(dev);
- if (ret)
- return ret;
- pci_set_master(pcidev);
-
- devpriv->iobase_a = pci_resource_start(pcidev, 0);
- dev->iobase = pci_resource_start(pcidev, 2);
-
- dev->pacer = comedi_8254_init(dev->iobase + PCI9118_TIMER_BASE,
- I8254_OSC_BASE_4MHZ, I8254_IO32, 0);
- if (!dev->pacer)
- return -ENOMEM;
-
- pci9118_reset(dev);
-
- if (pcidev->irq) {
- ret = request_irq(pcidev->irq, pci9118_interrupt, IRQF_SHARED,
- dev->board_name, dev);
- if (ret == 0) {
- dev->irq = pcidev->irq;
-
- pci9118_alloc_dma(dev);
- }
- }
-
- if (ext_mux > 0) {
- if (ext_mux > 256)
- ext_mux = 256; /* max 256 channels! */
- if (softsshdelay > 0)
- if (ext_mux > 128)
- ext_mux = 128;
- devpriv->usemux = 1;
- } else {
- devpriv->usemux = 0;
- }
-
- if (softsshdelay < 0) {
- /* select sample&hold signal polarity */
- devpriv->softsshdelay = -softsshdelay;
- devpriv->softsshsample = 0x80;
- devpriv->softsshhold = 0x00;
- } else {
- devpriv->softsshdelay = softsshdelay;
- devpriv->softsshsample = 0x00;
- devpriv->softsshhold = 0x80;
- }
-
- pci_read_config_word(pcidev, PCI_COMMAND, &u16w);
- pci_write_config_word(pcidev, PCI_COMMAND, u16w | 64);
- /* Enable parity check for parity error */
-
- ret = comedi_alloc_subdevices(dev, 4);
- if (ret)
- return ret;
-
- /* Analog Input subdevice */
- s = &dev->subdevices[0];
- s->type = COMEDI_SUBD_AI;
- s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND | SDF_DIFF;
- s->n_chan = (devpriv->usemux) ? ext_mux : 16;
- s->maxdata = board->ai_is_16bit ? 0xffff : 0x0fff;
- s->range_table = board->is_hg ? &pci9118hg_ai_range
- : &pci9118_ai_range;
- s->insn_read = pci9118_ai_insn_read;
- if (dev->irq) {
- dev->read_subdev = s;
- s->subdev_flags |= SDF_CMD_READ;
- s->len_chanlist = 255;
- s->do_cmdtest = pci9118_ai_cmdtest;
- s->do_cmd = pci9118_ai_cmd;
- s->cancel = pci9118_ai_cancel;
- s->munge = pci9118_ai_munge;
- }
-
- if (s->maxdata == 0xffff) {
- /*
- * 16-bit samples are from an ADS7805 A/D converter.
- * Minimum sampling rate is 10us.
- */
- devpriv->ai_ns_min = 10000;
- } else {
- /*
- * 12-bit samples are from an ADS7800 A/D converter.
- * Minimum sampling rate is 3us.
- */
- devpriv->ai_ns_min = 3000;
- }
-
- /* Analog Output subdevice */
- s = &dev->subdevices[1];
- s->type = COMEDI_SUBD_AO;
- s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
- s->n_chan = 2;
- s->maxdata = 0x0fff;
- s->range_table = &range_bipolar10;
- s->insn_write = pci9118_ao_insn_write;
-
- ret = comedi_alloc_subdev_readback(s);
- if (ret)
- return ret;
-
- /* the analog outputs were reset to 0V, make the readback match */
- for (i = 0; i < s->n_chan; i++)
- s->readback[i] = 2047;
-
- /* Digital Input subdevice */
- s = &dev->subdevices[2];
- s->type = COMEDI_SUBD_DI;
- s->subdev_flags = SDF_READABLE;
- s->n_chan = 4;
- s->maxdata = 1;
- s->range_table = &range_digital;
- s->insn_bits = pci9118_di_insn_bits;
-
- /* Digital Output subdevice */
- s = &dev->subdevices[3];
- s->type = COMEDI_SUBD_DO;
- s->subdev_flags = SDF_WRITABLE;
- s->n_chan = 4;
- s->maxdata = 1;
- s->range_table = &range_digital;
- s->insn_bits = pci9118_do_insn_bits;
-
- /* get the current state of the digital outputs */
- s->state = inl(dev->iobase + PCI9118_DIO_REG) >> 4;
-
- return 0;
-}
-
-static int pci9118_attach(struct comedi_device *dev,
- struct comedi_devconfig *it)
-{
- struct pci_dev *pcidev;
- int ext_mux, softsshdelay;
-
- ext_mux = it->options[2];
- softsshdelay = it->options[4];
-
- pcidev = pci9118_find_pci(dev, it);
- if (!pcidev)
- return -EIO;
- comedi_set_hw_dev(dev, &pcidev->dev);
-
- return pci9118_common_attach(dev, ext_mux, softsshdelay);
-}
-
-static int pci9118_auto_attach(struct comedi_device *dev,
- unsigned long context)
-{
- struct pci_dev *pcidev = comedi_to_pci_dev(dev);
- const struct pci9118_boardinfo *board = NULL;
-
- if (context < ARRAY_SIZE(pci9118_boards))
- board = &pci9118_boards[context];
- if (!board)
- return -ENODEV;
- dev->board_ptr = board;
- dev->board_name = board->name;
-
- /*
- * Need to 'get' the PCI device to match the 'put' in pci9118_detach().
- * (The 'put' also matches the implicit 'get' by pci9118_find_pci().)
- */
- pci_dev_get(pcidev);
- /* no external mux, no sample-hold delay */
- return pci9118_common_attach(dev, 0, 0);
-}
-
-static void pci9118_detach(struct comedi_device *dev)
-{
- struct pci_dev *pcidev = comedi_to_pci_dev(dev);
-
- if (dev->iobase)
- pci9118_reset(dev);
- comedi_pci_detach(dev);
- pci9118_free_dma(dev);
- pci_dev_put(pcidev);
-}
-
-static struct comedi_driver adl_pci9118_driver = {
- .driver_name = "adl_pci9118",
- .module = THIS_MODULE,
- .attach = pci9118_attach,
- .auto_attach = pci9118_auto_attach,
- .detach = pci9118_detach,
- .num_names = ARRAY_SIZE(pci9118_boards),
- .board_name = &pci9118_boards[0].name,
- .offset = sizeof(struct pci9118_boardinfo),
-};
-
-static int adl_pci9118_pci_probe(struct pci_dev *dev,
- const struct pci_device_id *id)
-{
- return comedi_pci_auto_config(dev, &adl_pci9118_driver,
- id->driver_data);
-}
-
-/* FIXME: All the supported board types have the same device ID! */
-static const struct pci_device_id adl_pci9118_pci_table[] = {
- { PCI_VDEVICE(AMCC, 0x80d9), BOARD_PCI9118DG },
-/* { PCI_VDEVICE(AMCC, 0x80d9), BOARD_PCI9118HG }, */
-/* { PCI_VDEVICE(AMCC, 0x80d9), BOARD_PCI9118HR }, */
- { 0 }
-};
-MODULE_DEVICE_TABLE(pci, adl_pci9118_pci_table);
-
-static struct pci_driver adl_pci9118_pci_driver = {
- .name = "adl_pci9118",
- .id_table = adl_pci9118_pci_table,
- .probe = adl_pci9118_pci_probe,
- .remove = comedi_pci_auto_unconfig,
-};
-module_comedi_pci_driver(adl_pci9118_driver, adl_pci9118_pci_driver);
-
-MODULE_AUTHOR("Comedi https://www.comedi.org");
-MODULE_DESCRIPTION("Comedi low-level driver");
-MODULE_LICENSE("GPL");