diff options
Diffstat (limited to 'drivers/staging/comedi/drivers/addi_apci_3120.c')
-rw-r--r-- | drivers/staging/comedi/drivers/addi_apci_3120.c | 1117 |
1 files changed, 0 insertions, 1117 deletions
diff --git a/drivers/staging/comedi/drivers/addi_apci_3120.c b/drivers/staging/comedi/drivers/addi_apci_3120.c deleted file mode 100644 index 1ed3b33d1a30..000000000000 --- a/drivers/staging/comedi/drivers/addi_apci_3120.c +++ /dev/null @@ -1,1117 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * addi_apci_3120.c - * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module. - * - * ADDI-DATA GmbH - * Dieselstrasse 3 - * D-77833 Ottersweier - * Tel: +19(0)7223/9493-0 - * Fax: +49(0)7223/9493-92 - * http://www.addi-data.com - * info@addi-data.com - */ - -#include <linux/module.h> -#include <linux/interrupt.h> - -#include "../comedi_pci.h" -#include "amcc_s5933.h" - -/* - * PCI BAR 0 register map (devpriv->amcc) - * see amcc_s5933.h for register and bit defines - */ -#define APCI3120_FIFO_ADVANCE_ON_BYTE_2 BIT(29) - -/* - * PCI BAR 1 register map (dev->iobase) - */ -#define APCI3120_AI_FIFO_REG 0x00 -#define APCI3120_CTRL_REG 0x00 -#define APCI3120_CTRL_EXT_TRIG BIT(15) -#define APCI3120_CTRL_GATE(x) BIT(12 + (x)) -#define APCI3120_CTRL_PR(x) (((x) & 0xf) << 8) -#define APCI3120_CTRL_PA(x) (((x) & 0xf) << 0) -#define APCI3120_AI_SOFTTRIG_REG 0x02 -#define APCI3120_STATUS_REG 0x02 -#define APCI3120_STATUS_EOC_INT BIT(15) -#define APCI3120_STATUS_AMCC_INT BIT(14) -#define APCI3120_STATUS_EOS_INT BIT(13) -#define APCI3120_STATUS_TIMER2_INT BIT(12) -#define APCI3120_STATUS_INT_MASK (0xf << 12) -#define APCI3120_STATUS_TO_DI_BITS(x) (((x) >> 8) & 0xf) -#define APCI3120_STATUS_TO_VERSION(x) (((x) >> 4) & 0xf) -#define APCI3120_STATUS_FIFO_FULL BIT(2) -#define APCI3120_STATUS_FIFO_EMPTY BIT(1) -#define APCI3120_STATUS_DA_READY BIT(0) -#define APCI3120_TIMER_REG 0x04 -#define APCI3120_CHANLIST_REG 0x06 -#define APCI3120_CHANLIST_INDEX(x) (((x) & 0xf) << 8) -#define APCI3120_CHANLIST_UNIPOLAR BIT(7) -#define APCI3120_CHANLIST_GAIN(x) (((x) & 0x3) << 4) -#define APCI3120_CHANLIST_MUX(x) (((x) & 0xf) << 0) -#define APCI3120_AO_REG(x) (0x08 + (((x) / 4) * 2)) -#define APCI3120_AO_MUX(x) (((x) & 0x3) << 14) -#define APCI3120_AO_DATA(x) ((x) << 0) -#define APCI3120_TIMER_MODE_REG 0x0c -#define APCI3120_TIMER_MODE(_t, _m) ((_m) << ((_t) * 2)) -#define APCI3120_TIMER_MODE0 0 /* I8254_MODE0 */ -#define APCI3120_TIMER_MODE2 1 /* I8254_MODE2 */ -#define APCI3120_TIMER_MODE4 2 /* I8254_MODE4 */ -#define APCI3120_TIMER_MODE5 3 /* I8254_MODE5 */ -#define APCI3120_TIMER_MODE_MASK(_t) (3 << ((_t) * 2)) -#define APCI3120_CTR0_REG 0x0d -#define APCI3120_CTR0_DO_BITS(x) ((x) << 4) -#define APCI3120_CTR0_TIMER_SEL(x) ((x) << 0) -#define APCI3120_MODE_REG 0x0e -#define APCI3120_MODE_TIMER2_CLK(x) (((x) & 0x3) << 6) -#define APCI3120_MODE_TIMER2_CLK_OSC APCI3120_MODE_TIMER2_CLK(0) -#define APCI3120_MODE_TIMER2_CLK_OUT1 APCI3120_MODE_TIMER2_CLK(1) -#define APCI3120_MODE_TIMER2_CLK_EOC APCI3120_MODE_TIMER2_CLK(2) -#define APCI3120_MODE_TIMER2_CLK_EOS APCI3120_MODE_TIMER2_CLK(3) -#define APCI3120_MODE_TIMER2_CLK_MASK APCI3120_MODE_TIMER2_CLK(3) -#define APCI3120_MODE_TIMER2_AS(x) (((x) & 0x3) << 4) -#define APCI3120_MODE_TIMER2_AS_TIMER APCI3120_MODE_TIMER2_AS(0) -#define APCI3120_MODE_TIMER2_AS_COUNTER APCI3120_MODE_TIMER2_AS(1) -#define APCI3120_MODE_TIMER2_AS_WDOG APCI3120_MODE_TIMER2_AS(2) -#define APCI3120_MODE_TIMER2_AS_MASK APCI3120_MODE_TIMER2_AS(3) -#define APCI3120_MODE_SCAN_ENA BIT(3) -#define APCI3120_MODE_TIMER2_IRQ_ENA BIT(2) -#define APCI3120_MODE_EOS_IRQ_ENA BIT(1) -#define APCI3120_MODE_EOC_IRQ_ENA BIT(0) - -/* - * PCI BAR 2 register map (devpriv->addon) - */ -#define APCI3120_ADDON_ADDR_REG 0x00 -#define APCI3120_ADDON_DATA_REG 0x02 -#define APCI3120_ADDON_CTRL_REG 0x04 -#define APCI3120_ADDON_CTRL_AMWEN_ENA BIT(1) -#define APCI3120_ADDON_CTRL_A2P_FIFO_ENA BIT(0) - -/* - * Board revisions - */ -#define APCI3120_REVA 0xa -#define APCI3120_REVB 0xb -#define APCI3120_REVA_OSC_BASE 70 /* 70ns = 14.29MHz */ -#define APCI3120_REVB_OSC_BASE 50 /* 50ns = 20MHz */ - -static const struct comedi_lrange apci3120_ai_range = { - 8, { - BIP_RANGE(10), - BIP_RANGE(5), - BIP_RANGE(2), - BIP_RANGE(1), - UNI_RANGE(10), - UNI_RANGE(5), - UNI_RANGE(2), - UNI_RANGE(1) - } -}; - -enum apci3120_boardid { - BOARD_APCI3120, - BOARD_APCI3001, -}; - -struct apci3120_board { - const char *name; - unsigned int ai_is_16bit:1; - unsigned int has_ao:1; -}; - -static const struct apci3120_board apci3120_boardtypes[] = { - [BOARD_APCI3120] = { - .name = "apci3120", - .ai_is_16bit = 1, - .has_ao = 1, - }, - [BOARD_APCI3001] = { - .name = "apci3001", - }, -}; - -struct apci3120_dmabuf { - unsigned short *virt; - dma_addr_t hw; - unsigned int size; - unsigned int use_size; -}; - -struct apci3120_private { - unsigned long amcc; - unsigned long addon; - unsigned int osc_base; - unsigned int use_dma:1; - unsigned int use_double_buffer:1; - unsigned int cur_dmabuf:1; - struct apci3120_dmabuf dmabuf[2]; - unsigned char do_bits; - unsigned char timer_mode; - unsigned char mode; - unsigned short ctrl; -}; - -static void apci3120_addon_write(struct comedi_device *dev, - unsigned int val, unsigned int reg) -{ - struct apci3120_private *devpriv = dev->private; - - /* 16-bit interface for AMCC add-on registers */ - - outw(reg, devpriv->addon + APCI3120_ADDON_ADDR_REG); - outw(val & 0xffff, devpriv->addon + APCI3120_ADDON_DATA_REG); - - outw(reg + 2, devpriv->addon + APCI3120_ADDON_ADDR_REG); - outw((val >> 16) & 0xffff, devpriv->addon + APCI3120_ADDON_DATA_REG); -} - -static void apci3120_init_dma(struct comedi_device *dev, - struct apci3120_dmabuf *dmabuf) -{ - struct apci3120_private *devpriv = dev->private; - - /* AMCC - enable transfer count and reset A2P FIFO */ - outl(AGCSTS_TC_ENABLE | AGCSTS_RESET_A2P_FIFO, - devpriv->amcc + AMCC_OP_REG_AGCSTS); - - /* Add-On - enable transfer count and reset A2P FIFO */ - apci3120_addon_write(dev, AGCSTS_TC_ENABLE | AGCSTS_RESET_A2P_FIFO, - AMCC_OP_REG_AGCSTS); - - /* AMCC - enable transfers and reset A2P flags */ - outl(RESET_A2P_FLAGS | EN_A2P_TRANSFERS, - devpriv->amcc + AMCC_OP_REG_MCSR); - - /* Add-On - DMA start address */ - apci3120_addon_write(dev, dmabuf->hw, AMCC_OP_REG_AMWAR); - - /* Add-On - Number of acquisitions */ - apci3120_addon_write(dev, dmabuf->use_size, AMCC_OP_REG_AMWTC); - - /* AMCC - enable write complete (DMA) and set FIFO advance */ - outl(APCI3120_FIFO_ADVANCE_ON_BYTE_2 | AINT_WRITE_COMPL, - devpriv->amcc + AMCC_OP_REG_INTCSR); - - /* Add-On - enable DMA */ - outw(APCI3120_ADDON_CTRL_AMWEN_ENA | APCI3120_ADDON_CTRL_A2P_FIFO_ENA, - devpriv->addon + APCI3120_ADDON_CTRL_REG); -} - -static void apci3120_setup_dma(struct comedi_device *dev, - struct comedi_subdevice *s) -{ - struct apci3120_private *devpriv = dev->private; - struct comedi_cmd *cmd = &s->async->cmd; - struct apci3120_dmabuf *dmabuf0 = &devpriv->dmabuf[0]; - struct apci3120_dmabuf *dmabuf1 = &devpriv->dmabuf[1]; - unsigned int dmalen0 = dmabuf0->size; - unsigned int dmalen1 = dmabuf1->size; - unsigned int scan_bytes; - - scan_bytes = comedi_samples_to_bytes(s, cmd->scan_end_arg); - - if (cmd->stop_src == TRIG_COUNT) { - /* - * Must we fill full first buffer? And must we fill - * full second buffer when first is once filled? - */ - if (dmalen0 > (cmd->stop_arg * scan_bytes)) - dmalen0 = cmd->stop_arg * scan_bytes; - else if (dmalen1 > (cmd->stop_arg * scan_bytes - dmalen0)) - dmalen1 = cmd->stop_arg * scan_bytes - dmalen0; - } - - if (cmd->flags & CMDF_WAKE_EOS) { - /* don't we want wake up every scan? */ - if (dmalen0 > scan_bytes) { - dmalen0 = scan_bytes; - if (cmd->scan_end_arg & 1) - dmalen0 += 2; - } - if (dmalen1 > scan_bytes) { - dmalen1 = scan_bytes; - if (cmd->scan_end_arg & 1) - dmalen1 -= 2; - if (dmalen1 < 4) - dmalen1 = 4; - } - } else { - /* isn't output buff smaller that our DMA buff? */ - if (dmalen0 > s->async->prealloc_bufsz) - dmalen0 = s->async->prealloc_bufsz; - if (dmalen1 > s->async->prealloc_bufsz) - dmalen1 = s->async->prealloc_bufsz; - } - dmabuf0->use_size = dmalen0; - dmabuf1->use_size = dmalen1; - - apci3120_init_dma(dev, dmabuf0); -} - -/* - * There are three timers on the board. They all use the same base - * clock with a fixed prescaler for each timer. The base clock used - * depends on the board version and type. - * - * APCI-3120 Rev A boards OSC = 14.29MHz base clock (~70ns) - * APCI-3120 Rev B boards OSC = 20MHz base clock (50ns) - * APCI-3001 boards OSC = 20MHz base clock (50ns) - * - * The prescalers for each timer are: - * Timer 0 CLK = OSC/10 - * Timer 1 CLK = OSC/1000 - * Timer 2 CLK = OSC/1000 - */ -static unsigned int apci3120_ns_to_timer(struct comedi_device *dev, - unsigned int timer, - unsigned int ns, - unsigned int flags) -{ - struct apci3120_private *devpriv = dev->private; - unsigned int prescale = (timer == 0) ? 10 : 1000; - unsigned int timer_base = devpriv->osc_base * prescale; - unsigned int divisor; - - switch (flags & CMDF_ROUND_MASK) { - case CMDF_ROUND_UP: - divisor = DIV_ROUND_UP(ns, timer_base); - break; - case CMDF_ROUND_DOWN: - divisor = ns / timer_base; - break; - case CMDF_ROUND_NEAREST: - default: - divisor = DIV_ROUND_CLOSEST(ns, timer_base); - break; - } - - if (timer == 2) { - /* timer 2 is 24-bits */ - if (divisor > 0x00ffffff) - divisor = 0x00ffffff; - } else { - /* timers 0 and 1 are 16-bits */ - if (divisor > 0xffff) - divisor = 0xffff; - } - /* the timers require a minimum divisor of 2 */ - if (divisor < 2) - divisor = 2; - - return divisor; -} - -static void apci3120_clr_timer2_interrupt(struct comedi_device *dev) -{ - /* a dummy read of APCI3120_CTR0_REG clears the timer 2 interrupt */ - inb(dev->iobase + APCI3120_CTR0_REG); -} - -static void apci3120_timer_write(struct comedi_device *dev, - unsigned int timer, unsigned int val) -{ - struct apci3120_private *devpriv = dev->private; - - /* write 16-bit value to timer (lower 16-bits of timer 2) */ - outb(APCI3120_CTR0_DO_BITS(devpriv->do_bits) | - APCI3120_CTR0_TIMER_SEL(timer), - dev->iobase + APCI3120_CTR0_REG); - outw(val & 0xffff, dev->iobase + APCI3120_TIMER_REG); - - if (timer == 2) { - /* write upper 16-bits to timer 2 */ - outb(APCI3120_CTR0_DO_BITS(devpriv->do_bits) | - APCI3120_CTR0_TIMER_SEL(timer + 1), - dev->iobase + APCI3120_CTR0_REG); - outw((val >> 16) & 0xffff, dev->iobase + APCI3120_TIMER_REG); - } -} - -static unsigned int apci3120_timer_read(struct comedi_device *dev, - unsigned int timer) -{ - struct apci3120_private *devpriv = dev->private; - unsigned int val; - - /* read 16-bit value from timer (lower 16-bits of timer 2) */ - outb(APCI3120_CTR0_DO_BITS(devpriv->do_bits) | - APCI3120_CTR0_TIMER_SEL(timer), - dev->iobase + APCI3120_CTR0_REG); - val = inw(dev->iobase + APCI3120_TIMER_REG); - - if (timer == 2) { - /* read upper 16-bits from timer 2 */ - outb(APCI3120_CTR0_DO_BITS(devpriv->do_bits) | - APCI3120_CTR0_TIMER_SEL(timer + 1), - dev->iobase + APCI3120_CTR0_REG); - val |= (inw(dev->iobase + APCI3120_TIMER_REG) << 16); - } - - return val; -} - -static void apci3120_timer_set_mode(struct comedi_device *dev, - unsigned int timer, unsigned int mode) -{ - struct apci3120_private *devpriv = dev->private; - - devpriv->timer_mode &= ~APCI3120_TIMER_MODE_MASK(timer); - devpriv->timer_mode |= APCI3120_TIMER_MODE(timer, mode); - outb(devpriv->timer_mode, dev->iobase + APCI3120_TIMER_MODE_REG); -} - -static void apci3120_timer_enable(struct comedi_device *dev, - unsigned int timer, bool enable) -{ - struct apci3120_private *devpriv = dev->private; - - if (enable) - devpriv->ctrl |= APCI3120_CTRL_GATE(timer); - else - devpriv->ctrl &= ~APCI3120_CTRL_GATE(timer); - outw(devpriv->ctrl, dev->iobase + APCI3120_CTRL_REG); -} - -static void apci3120_exttrig_enable(struct comedi_device *dev, bool enable) -{ - struct apci3120_private *devpriv = dev->private; - - if (enable) - devpriv->ctrl |= APCI3120_CTRL_EXT_TRIG; - else - devpriv->ctrl &= ~APCI3120_CTRL_EXT_TRIG; - outw(devpriv->ctrl, dev->iobase + APCI3120_CTRL_REG); -} - -static void apci3120_set_chanlist(struct comedi_device *dev, - struct comedi_subdevice *s, - int n_chan, unsigned int *chanlist) -{ - struct apci3120_private *devpriv = dev->private; - int i; - - /* set chanlist for scan */ - for (i = 0; i < n_chan; i++) { - unsigned int chan = CR_CHAN(chanlist[i]); - unsigned int range = CR_RANGE(chanlist[i]); - unsigned int val; - - val = APCI3120_CHANLIST_MUX(chan) | - APCI3120_CHANLIST_GAIN(range) | - APCI3120_CHANLIST_INDEX(i); - - if (comedi_range_is_unipolar(s, range)) - val |= APCI3120_CHANLIST_UNIPOLAR; - - outw(val, dev->iobase + APCI3120_CHANLIST_REG); - } - - /* a dummy read of APCI3120_TIMER_MODE_REG resets the ai FIFO */ - inw(dev->iobase + APCI3120_TIMER_MODE_REG); - - /* set scan length (PR) and scan start (PA) */ - devpriv->ctrl = APCI3120_CTRL_PR(n_chan - 1) | APCI3120_CTRL_PA(0); - outw(devpriv->ctrl, dev->iobase + APCI3120_CTRL_REG); - - /* enable chanlist scanning if necessary */ - if (n_chan > 1) - devpriv->mode |= APCI3120_MODE_SCAN_ENA; -} - -static void apci3120_interrupt_dma(struct comedi_device *dev, - struct comedi_subdevice *s) -{ - struct apci3120_private *devpriv = dev->private; - struct comedi_async *async = s->async; - struct comedi_cmd *cmd = &async->cmd; - struct apci3120_dmabuf *dmabuf; - unsigned int nbytes; - unsigned int nsamples; - - dmabuf = &devpriv->dmabuf[devpriv->cur_dmabuf]; - - nbytes = dmabuf->use_size - inl(devpriv->amcc + AMCC_OP_REG_MWTC); - - if (nbytes < dmabuf->use_size) - dev_err(dev->class_dev, "Interrupted DMA transfer!\n"); - if (nbytes & 1) { - dev_err(dev->class_dev, "Odd count of bytes in DMA ring!\n"); - async->events |= COMEDI_CB_ERROR; - return; - } - - nsamples = comedi_bytes_to_samples(s, nbytes); - if (nsamples) { - comedi_buf_write_samples(s, dmabuf->virt, nsamples); - - if (!(cmd->flags & CMDF_WAKE_EOS)) - async->events |= COMEDI_CB_EOS; - } - - if ((async->events & COMEDI_CB_CANCEL_MASK) || - (cmd->stop_src == TRIG_COUNT && async->scans_done >= cmd->stop_arg)) - return; - - if (devpriv->use_double_buffer) { - /* switch DMA buffers for next interrupt */ - devpriv->cur_dmabuf = !devpriv->cur_dmabuf; - dmabuf = &devpriv->dmabuf[devpriv->cur_dmabuf]; - apci3120_init_dma(dev, dmabuf); - } else { - /* restart DMA if not using double buffering */ - apci3120_init_dma(dev, dmabuf); - } -} - -static irqreturn_t apci3120_interrupt(int irq, void *d) -{ - struct comedi_device *dev = d; - struct apci3120_private *devpriv = dev->private; - struct comedi_subdevice *s = dev->read_subdev; - struct comedi_async *async = s->async; - struct comedi_cmd *cmd = &async->cmd; - unsigned int status; - unsigned int int_amcc; - - status = inw(dev->iobase + APCI3120_STATUS_REG); - int_amcc = inl(devpriv->amcc + AMCC_OP_REG_INTCSR); - - if (!(status & APCI3120_STATUS_INT_MASK) && - !(int_amcc & ANY_S593X_INT)) { - dev_err(dev->class_dev, "IRQ from unknown source\n"); - return IRQ_NONE; - } - - outl(int_amcc | AINT_INT_MASK, devpriv->amcc + AMCC_OP_REG_INTCSR); - - if (devpriv->ctrl & APCI3120_CTRL_EXT_TRIG) - apci3120_exttrig_enable(dev, false); - - if (int_amcc & MASTER_ABORT_INT) - dev_err(dev->class_dev, "AMCC IRQ - MASTER DMA ABORT!\n"); - if (int_amcc & TARGET_ABORT_INT) - dev_err(dev->class_dev, "AMCC IRQ - TARGET DMA ABORT!\n"); - - if ((status & APCI3120_STATUS_EOS_INT) && - (devpriv->mode & APCI3120_MODE_EOS_IRQ_ENA)) { - unsigned short val; - int i; - - for (i = 0; i < cmd->chanlist_len; i++) { - val = inw(dev->iobase + APCI3120_AI_FIFO_REG); - comedi_buf_write_samples(s, &val, 1); - } - - devpriv->mode |= APCI3120_MODE_EOS_IRQ_ENA; - outb(devpriv->mode, dev->iobase + APCI3120_MODE_REG); - } - - if (status & APCI3120_STATUS_TIMER2_INT) { - /* - * for safety... - * timer2 interrupts are not enabled in the driver - */ - apci3120_clr_timer2_interrupt(dev); - } - - if (status & APCI3120_STATUS_AMCC_INT) { - /* AMCC- Clear write complete interrupt (DMA) */ - outl(AINT_WT_COMPLETE, devpriv->amcc + AMCC_OP_REG_INTCSR); - - /* do some data transfer */ - apci3120_interrupt_dma(dev, s); - } - - if (cmd->stop_src == TRIG_COUNT && async->scans_done >= cmd->stop_arg) - async->events |= COMEDI_CB_EOA; - - comedi_handle_events(dev, s); - - return IRQ_HANDLED; -} - -static int apci3120_ai_cmd(struct comedi_device *dev, - struct comedi_subdevice *s) -{ - struct apci3120_private *devpriv = dev->private; - struct comedi_cmd *cmd = &s->async->cmd; - unsigned int divisor; - - /* set default mode bits */ - devpriv->mode = APCI3120_MODE_TIMER2_CLK_OSC | - APCI3120_MODE_TIMER2_AS_TIMER; - - /* AMCC- Clear write complete interrupt (DMA) */ - outl(AINT_WT_COMPLETE, devpriv->amcc + AMCC_OP_REG_INTCSR); - - devpriv->cur_dmabuf = 0; - - /* load chanlist for command scan */ - apci3120_set_chanlist(dev, s, cmd->chanlist_len, cmd->chanlist); - - if (cmd->start_src == TRIG_EXT) - apci3120_exttrig_enable(dev, true); - - if (cmd->scan_begin_src == TRIG_TIMER) { - /* - * Timer 1 is used in MODE2 (rate generator) to set the - * start time for each scan. - */ - divisor = apci3120_ns_to_timer(dev, 1, cmd->scan_begin_arg, - cmd->flags); - apci3120_timer_set_mode(dev, 1, APCI3120_TIMER_MODE2); - apci3120_timer_write(dev, 1, divisor); - } - - /* - * Timer 0 is used in MODE2 (rate generator) to set the conversion - * time for each acquisition. - */ - divisor = apci3120_ns_to_timer(dev, 0, cmd->convert_arg, cmd->flags); - apci3120_timer_set_mode(dev, 0, APCI3120_TIMER_MODE2); - apci3120_timer_write(dev, 0, divisor); - - if (devpriv->use_dma) - apci3120_setup_dma(dev, s); - else - devpriv->mode |= APCI3120_MODE_EOS_IRQ_ENA; - - /* set mode to enable acquisition */ - outb(devpriv->mode, dev->iobase + APCI3120_MODE_REG); - - if (cmd->scan_begin_src == TRIG_TIMER) - apci3120_timer_enable(dev, 1, true); - apci3120_timer_enable(dev, 0, true); - - return 0; -} - -static int apci3120_ai_cmdtest(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_cmd *cmd) -{ - unsigned int arg; - int err = 0; - - /* Step 1 : check if triggers are trivially valid */ - - err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT); - err |= comedi_check_trigger_src(&cmd->scan_begin_src, - TRIG_TIMER | TRIG_FOLLOW); - err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_TIMER); - err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT); - err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE); - - if (err) - return 1; - - /* Step 2a : make sure trigger sources are unique */ - - err |= comedi_check_trigger_is_unique(cmd->start_src); - err |= comedi_check_trigger_is_unique(cmd->scan_begin_src); - err |= comedi_check_trigger_is_unique(cmd->stop_src); - - /* Step 2b : and mutually compatible */ - - if (err) - return 2; - - /* Step 3: check if arguments are trivially valid */ - - err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0); - - if (cmd->scan_begin_src == TRIG_TIMER) { /* Test Delay timing */ - err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg, - 100000); - } - - /* minimum conversion time per sample is 10us */ - err |= comedi_check_trigger_arg_min(&cmd->convert_arg, 10000); - - err |= comedi_check_trigger_arg_min(&cmd->chanlist_len, 1); - err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg, - cmd->chanlist_len); - - if (cmd->stop_src == TRIG_COUNT) - err |= comedi_check_trigger_arg_min(&cmd->stop_arg, 1); - else /* TRIG_NONE */ - err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0); - - if (err) - return 3; - - /* Step 4: fix up any arguments */ - - if (cmd->scan_begin_src == TRIG_TIMER) { - /* scan begin must be larger than the scan time */ - arg = cmd->convert_arg * cmd->scan_end_arg; - err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg, arg); - } - - if (err) - return 4; - - /* Step 5: check channel list if it exists */ - - return 0; -} - -static int apci3120_cancel(struct comedi_device *dev, - struct comedi_subdevice *s) -{ - struct apci3120_private *devpriv = dev->private; - - /* Add-On - disable DMA */ - outw(0, devpriv->addon + 4); - - /* Add-On - disable bus master */ - apci3120_addon_write(dev, 0, AMCC_OP_REG_AGCSTS); - - /* AMCC - disable bus master */ - outl(0, devpriv->amcc + AMCC_OP_REG_MCSR); - - /* disable all counters, ext trigger, and reset scan */ - devpriv->ctrl = 0; - outw(devpriv->ctrl, dev->iobase + APCI3120_CTRL_REG); - - /* DISABLE_ALL_INTERRUPT */ - devpriv->mode = 0; - outb(devpriv->mode, dev->iobase + APCI3120_MODE_REG); - - inw(dev->iobase + APCI3120_STATUS_REG); - devpriv->cur_dmabuf = 0; - - return 0; -} - -static int apci3120_ai_eoc(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned long context) -{ - unsigned int status; - - status = inw(dev->iobase + APCI3120_STATUS_REG); - if ((status & APCI3120_STATUS_EOC_INT) == 0) - return 0; - return -EBUSY; -} - -static int apci3120_ai_insn_read(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) -{ - struct apci3120_private *devpriv = dev->private; - unsigned int divisor; - int ret; - int i; - - /* set mode for A/D conversions by software trigger with timer 0 */ - devpriv->mode = APCI3120_MODE_TIMER2_CLK_OSC | - APCI3120_MODE_TIMER2_AS_TIMER; - outb(devpriv->mode, dev->iobase + APCI3120_MODE_REG); - - /* load chanlist for single channel scan */ - apci3120_set_chanlist(dev, s, 1, &insn->chanspec); - - /* - * Timer 0 is used in MODE4 (software triggered strobe) to set the - * conversion time for each acquisition. Each conversion is triggered - * when the divisor is written to the timer, The conversion is done - * when the EOC bit in the status register is '0'. - */ - apci3120_timer_set_mode(dev, 0, APCI3120_TIMER_MODE4); - apci3120_timer_enable(dev, 0, true); - - /* fixed conversion time of 10 us */ - divisor = apci3120_ns_to_timer(dev, 0, 10000, CMDF_ROUND_NEAREST); - - for (i = 0; i < insn->n; i++) { - /* trigger conversion */ - apci3120_timer_write(dev, 0, divisor); - - ret = comedi_timeout(dev, s, insn, apci3120_ai_eoc, 0); - if (ret) - return ret; - - data[i] = inw(dev->iobase + APCI3120_AI_FIFO_REG); - } - - return insn->n; -} - -static int apci3120_ao_ready(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned long context) -{ - unsigned int status; - - status = inw(dev->iobase + APCI3120_STATUS_REG); - if (status & APCI3120_STATUS_DA_READY) - return 0; - return -EBUSY; -} - -static int apci3120_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); - int i; - - for (i = 0; i < insn->n; i++) { - unsigned int val = data[i]; - int ret; - - ret = comedi_timeout(dev, s, insn, apci3120_ao_ready, 0); - if (ret) - return ret; - - outw(APCI3120_AO_MUX(chan) | APCI3120_AO_DATA(val), - dev->iobase + APCI3120_AO_REG(chan)); - - s->readback[chan] = val; - } - - return insn->n; -} - -static int apci3120_di_insn_bits(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) -{ - unsigned int status; - - status = inw(dev->iobase + APCI3120_STATUS_REG); - data[1] = APCI3120_STATUS_TO_DI_BITS(status); - - return insn->n; -} - -static int apci3120_do_insn_bits(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) -{ - struct apci3120_private *devpriv = dev->private; - - if (comedi_dio_update_state(s, data)) { - devpriv->do_bits = s->state; - outb(APCI3120_CTR0_DO_BITS(devpriv->do_bits), - dev->iobase + APCI3120_CTR0_REG); - } - - data[1] = s->state; - - return insn->n; -} - -static int apci3120_timer_insn_config(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) -{ - struct apci3120_private *devpriv = dev->private; - unsigned int divisor; - unsigned int status; - unsigned int mode; - unsigned int timer_mode; - - switch (data[0]) { - case INSN_CONFIG_ARM: - apci3120_clr_timer2_interrupt(dev); - divisor = apci3120_ns_to_timer(dev, 2, data[1], - CMDF_ROUND_DOWN); - apci3120_timer_write(dev, 2, divisor); - apci3120_timer_enable(dev, 2, true); - break; - - case INSN_CONFIG_DISARM: - apci3120_timer_enable(dev, 2, false); - apci3120_clr_timer2_interrupt(dev); - break; - - case INSN_CONFIG_GET_COUNTER_STATUS: - data[1] = 0; - data[2] = COMEDI_COUNTER_ARMED | COMEDI_COUNTER_COUNTING | - COMEDI_COUNTER_TERMINAL_COUNT; - - if (devpriv->ctrl & APCI3120_CTRL_GATE(2)) { - data[1] |= COMEDI_COUNTER_ARMED; - data[1] |= COMEDI_COUNTER_COUNTING; - } - status = inw(dev->iobase + APCI3120_STATUS_REG); - if (status & APCI3120_STATUS_TIMER2_INT) { - data[1] &= ~COMEDI_COUNTER_COUNTING; - data[1] |= COMEDI_COUNTER_TERMINAL_COUNT; - } - break; - - case INSN_CONFIG_SET_COUNTER_MODE: - switch (data[1]) { - case I8254_MODE0: - mode = APCI3120_MODE_TIMER2_AS_COUNTER; - timer_mode = APCI3120_TIMER_MODE0; - break; - case I8254_MODE2: - mode = APCI3120_MODE_TIMER2_AS_TIMER; - timer_mode = APCI3120_TIMER_MODE2; - break; - case I8254_MODE4: - mode = APCI3120_MODE_TIMER2_AS_TIMER; - timer_mode = APCI3120_TIMER_MODE4; - break; - case I8254_MODE5: - mode = APCI3120_MODE_TIMER2_AS_WDOG; - timer_mode = APCI3120_TIMER_MODE5; - break; - default: - return -EINVAL; - } - apci3120_timer_enable(dev, 2, false); - apci3120_clr_timer2_interrupt(dev); - apci3120_timer_set_mode(dev, 2, timer_mode); - devpriv->mode &= ~APCI3120_MODE_TIMER2_AS_MASK; - devpriv->mode |= mode; - outb(devpriv->mode, dev->iobase + APCI3120_MODE_REG); - break; - - default: - return -EINVAL; - } - - return insn->n; -} - -static int apci3120_timer_insn_read(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data) -{ - int i; - - for (i = 0; i < insn->n; i++) - data[i] = apci3120_timer_read(dev, 2); - - return insn->n; -} - -static void apci3120_dma_alloc(struct comedi_device *dev) -{ - struct apci3120_private *devpriv = dev->private; - struct apci3120_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->use_dma = 1; - if (i == 1) - devpriv->use_double_buffer = 1; - } -} - -static void apci3120_dma_free(struct comedi_device *dev) -{ - struct apci3120_private *devpriv = dev->private; - struct apci3120_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 void apci3120_reset(struct comedi_device *dev) -{ - /* disable all interrupt sources */ - outb(0, dev->iobase + APCI3120_MODE_REG); - - /* disable all counters, ext trigger, and reset scan */ - outw(0, dev->iobase + APCI3120_CTRL_REG); - - /* clear interrupt status */ - inw(dev->iobase + APCI3120_STATUS_REG); -} - -static int apci3120_auto_attach(struct comedi_device *dev, - unsigned long context) -{ - struct pci_dev *pcidev = comedi_to_pci_dev(dev); - const struct apci3120_board *board = NULL; - struct apci3120_private *devpriv; - struct comedi_subdevice *s; - unsigned int status; - int ret; - - if (context < ARRAY_SIZE(apci3120_boardtypes)) - board = &apci3120_boardtypes[context]; - if (!board) - return -ENODEV; - dev->board_ptr = board; - dev->board_name = board->name; - - devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); - if (!devpriv) - return -ENOMEM; - - ret = comedi_pci_enable(dev); - if (ret) - return ret; - pci_set_master(pcidev); - - dev->iobase = pci_resource_start(pcidev, 1); - devpriv->amcc = pci_resource_start(pcidev, 0); - devpriv->addon = pci_resource_start(pcidev, 2); - - apci3120_reset(dev); - - if (pcidev->irq > 0) { - ret = request_irq(pcidev->irq, apci3120_interrupt, IRQF_SHARED, - dev->board_name, dev); - if (ret == 0) { - dev->irq = pcidev->irq; - - apci3120_dma_alloc(dev); - } - } - - status = inw(dev->iobase + APCI3120_STATUS_REG); - if (APCI3120_STATUS_TO_VERSION(status) == APCI3120_REVB || - context == BOARD_APCI3001) - devpriv->osc_base = APCI3120_REVB_OSC_BASE; - else - devpriv->osc_base = APCI3120_REVA_OSC_BASE; - - ret = comedi_alloc_subdevices(dev, 5); - 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 = 16; - s->maxdata = board->ai_is_16bit ? 0xffff : 0x0fff; - s->range_table = &apci3120_ai_range; - s->insn_read = apci3120_ai_insn_read; - if (dev->irq) { - dev->read_subdev = s; - s->subdev_flags |= SDF_CMD_READ; - s->len_chanlist = s->n_chan; - s->do_cmdtest = apci3120_ai_cmdtest; - s->do_cmd = apci3120_ai_cmd; - s->cancel = apci3120_cancel; - } - - /* Analog Output subdevice */ - s = &dev->subdevices[1]; - if (board->has_ao) { - s->type = COMEDI_SUBD_AO; - s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON; - s->n_chan = 8; - s->maxdata = 0x3fff; - s->range_table = &range_bipolar10; - s->insn_write = apci3120_ao_insn_write; - - ret = comedi_alloc_subdev_readback(s); - if (ret) - return ret; - } else { - s->type = COMEDI_SUBD_UNUSED; - } - - /* 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 = apci3120_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 = apci3120_do_insn_bits; - - /* Timer subdevice */ - s = &dev->subdevices[4]; - s->type = COMEDI_SUBD_TIMER; - s->subdev_flags = SDF_READABLE; - s->n_chan = 1; - s->maxdata = 0x00ffffff; - s->insn_config = apci3120_timer_insn_config; - s->insn_read = apci3120_timer_insn_read; - - return 0; -} - -static void apci3120_detach(struct comedi_device *dev) -{ - comedi_pci_detach(dev); - apci3120_dma_free(dev); -} - -static struct comedi_driver apci3120_driver = { - .driver_name = "addi_apci_3120", - .module = THIS_MODULE, - .auto_attach = apci3120_auto_attach, - .detach = apci3120_detach, -}; - -static int apci3120_pci_probe(struct pci_dev *dev, - const struct pci_device_id *id) -{ - return comedi_pci_auto_config(dev, &apci3120_driver, id->driver_data); -} - -static const struct pci_device_id apci3120_pci_table[] = { - { PCI_VDEVICE(AMCC, 0x818d), BOARD_APCI3120 }, - { PCI_VDEVICE(AMCC, 0x828d), BOARD_APCI3001 }, - { 0 } -}; -MODULE_DEVICE_TABLE(pci, apci3120_pci_table); - -static struct pci_driver apci3120_pci_driver = { - .name = "addi_apci_3120", - .id_table = apci3120_pci_table, - .probe = apci3120_pci_probe, - .remove = comedi_pci_auto_unconfig, -}; -module_comedi_pci_driver(apci3120_driver, apci3120_pci_driver); - -MODULE_AUTHOR("Comedi https://www.comedi.org"); -MODULE_DESCRIPTION("ADDI-DATA APCI-3120, Analog input board"); -MODULE_LICENSE("GPL"); |