summaryrefslogtreecommitdiff
path: root/drivers/staging/comedi/drivers/addi_apci_3120.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/comedi/drivers/addi_apci_3120.c')
-rw-r--r--drivers/staging/comedi/drivers/addi_apci_3120.c1117
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");