diff options
Diffstat (limited to 'drivers/staging/comedi/drivers/adl_pci9118.c')
-rw-r--r-- | drivers/staging/comedi/drivers/adl_pci9118.c | 1736 |
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"); |