summaryrefslogtreecommitdiff
path: root/drivers/staging/comedi/drivers/addi_apci_3xxx.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/comedi/drivers/addi_apci_3xxx.c')
-rw-r--r--drivers/staging/comedi/drivers/addi_apci_3xxx.c961
1 files changed, 0 insertions, 961 deletions
diff --git a/drivers/staging/comedi/drivers/addi_apci_3xxx.c b/drivers/staging/comedi/drivers/addi_apci_3xxx.c
deleted file mode 100644
index a90d59377e18..000000000000
--- a/drivers/staging/comedi/drivers/addi_apci_3xxx.c
+++ /dev/null
@@ -1,961 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * addi_apci_3xxx.c
- * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
- * Project manager: S. Weber
- *
- * 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"
-
-#define CONV_UNIT_NS BIT(0)
-#define CONV_UNIT_US BIT(1)
-#define CONV_UNIT_MS BIT(2)
-
-static const struct comedi_lrange apci3xxx_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)
- }
-};
-
-static const struct comedi_lrange apci3xxx_ao_range = {
- 2, {
- BIP_RANGE(10),
- UNI_RANGE(10)
- }
-};
-
-enum apci3xxx_boardid {
- BOARD_APCI3000_16,
- BOARD_APCI3000_8,
- BOARD_APCI3000_4,
- BOARD_APCI3006_16,
- BOARD_APCI3006_8,
- BOARD_APCI3006_4,
- BOARD_APCI3010_16,
- BOARD_APCI3010_8,
- BOARD_APCI3010_4,
- BOARD_APCI3016_16,
- BOARD_APCI3016_8,
- BOARD_APCI3016_4,
- BOARD_APCI3100_16_4,
- BOARD_APCI3100_8_4,
- BOARD_APCI3106_16_4,
- BOARD_APCI3106_8_4,
- BOARD_APCI3110_16_4,
- BOARD_APCI3110_8_4,
- BOARD_APCI3116_16_4,
- BOARD_APCI3116_8_4,
- BOARD_APCI3003,
- BOARD_APCI3002_16,
- BOARD_APCI3002_8,
- BOARD_APCI3002_4,
- BOARD_APCI3500,
-};
-
-struct apci3xxx_boardinfo {
- const char *name;
- int ai_subdev_flags;
- int ai_n_chan;
- unsigned int ai_maxdata;
- unsigned char ai_conv_units;
- unsigned int ai_min_acq_ns;
- unsigned int has_ao:1;
- unsigned int has_dig_in:1;
- unsigned int has_dig_out:1;
- unsigned int has_ttl_io:1;
-};
-
-static const struct apci3xxx_boardinfo apci3xxx_boardtypes[] = {
- [BOARD_APCI3000_16] = {
- .name = "apci3000-16",
- .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
- .ai_n_chan = 16,
- .ai_maxdata = 0x0fff,
- .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
- .ai_min_acq_ns = 10000,
- .has_ttl_io = 1,
- },
- [BOARD_APCI3000_8] = {
- .name = "apci3000-8",
- .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
- .ai_n_chan = 8,
- .ai_maxdata = 0x0fff,
- .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
- .ai_min_acq_ns = 10000,
- .has_ttl_io = 1,
- },
- [BOARD_APCI3000_4] = {
- .name = "apci3000-4",
- .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
- .ai_n_chan = 4,
- .ai_maxdata = 0x0fff,
- .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
- .ai_min_acq_ns = 10000,
- .has_ttl_io = 1,
- },
- [BOARD_APCI3006_16] = {
- .name = "apci3006-16",
- .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
- .ai_n_chan = 16,
- .ai_maxdata = 0xffff,
- .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
- .ai_min_acq_ns = 10000,
- .has_ttl_io = 1,
- },
- [BOARD_APCI3006_8] = {
- .name = "apci3006-8",
- .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
- .ai_n_chan = 8,
- .ai_maxdata = 0xffff,
- .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
- .ai_min_acq_ns = 10000,
- .has_ttl_io = 1,
- },
- [BOARD_APCI3006_4] = {
- .name = "apci3006-4",
- .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
- .ai_n_chan = 4,
- .ai_maxdata = 0xffff,
- .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
- .ai_min_acq_ns = 10000,
- .has_ttl_io = 1,
- },
- [BOARD_APCI3010_16] = {
- .name = "apci3010-16",
- .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
- .ai_n_chan = 16,
- .ai_maxdata = 0x0fff,
- .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
- .ai_min_acq_ns = 5000,
- .has_dig_in = 1,
- .has_dig_out = 1,
- .has_ttl_io = 1,
- },
- [BOARD_APCI3010_8] = {
- .name = "apci3010-8",
- .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
- .ai_n_chan = 8,
- .ai_maxdata = 0x0fff,
- .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
- .ai_min_acq_ns = 5000,
- .has_dig_in = 1,
- .has_dig_out = 1,
- .has_ttl_io = 1,
- },
- [BOARD_APCI3010_4] = {
- .name = "apci3010-4",
- .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
- .ai_n_chan = 4,
- .ai_maxdata = 0x0fff,
- .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
- .ai_min_acq_ns = 5000,
- .has_dig_in = 1,
- .has_dig_out = 1,
- .has_ttl_io = 1,
- },
- [BOARD_APCI3016_16] = {
- .name = "apci3016-16",
- .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
- .ai_n_chan = 16,
- .ai_maxdata = 0xffff,
- .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
- .ai_min_acq_ns = 5000,
- .has_dig_in = 1,
- .has_dig_out = 1,
- .has_ttl_io = 1,
- },
- [BOARD_APCI3016_8] = {
- .name = "apci3016-8",
- .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
- .ai_n_chan = 8,
- .ai_maxdata = 0xffff,
- .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
- .ai_min_acq_ns = 5000,
- .has_dig_in = 1,
- .has_dig_out = 1,
- .has_ttl_io = 1,
- },
- [BOARD_APCI3016_4] = {
- .name = "apci3016-4",
- .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
- .ai_n_chan = 4,
- .ai_maxdata = 0xffff,
- .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
- .ai_min_acq_ns = 5000,
- .has_dig_in = 1,
- .has_dig_out = 1,
- .has_ttl_io = 1,
- },
- [BOARD_APCI3100_16_4] = {
- .name = "apci3100-16-4",
- .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
- .ai_n_chan = 16,
- .ai_maxdata = 0x0fff,
- .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
- .ai_min_acq_ns = 10000,
- .has_ao = 1,
- .has_ttl_io = 1,
- },
- [BOARD_APCI3100_8_4] = {
- .name = "apci3100-8-4",
- .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
- .ai_n_chan = 8,
- .ai_maxdata = 0x0fff,
- .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
- .ai_min_acq_ns = 10000,
- .has_ao = 1,
- .has_ttl_io = 1,
- },
- [BOARD_APCI3106_16_4] = {
- .name = "apci3106-16-4",
- .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
- .ai_n_chan = 16,
- .ai_maxdata = 0xffff,
- .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
- .ai_min_acq_ns = 10000,
- .has_ao = 1,
- .has_ttl_io = 1,
- },
- [BOARD_APCI3106_8_4] = {
- .name = "apci3106-8-4",
- .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
- .ai_n_chan = 8,
- .ai_maxdata = 0xffff,
- .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
- .ai_min_acq_ns = 10000,
- .has_ao = 1,
- .has_ttl_io = 1,
- },
- [BOARD_APCI3110_16_4] = {
- .name = "apci3110-16-4",
- .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
- .ai_n_chan = 16,
- .ai_maxdata = 0x0fff,
- .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
- .ai_min_acq_ns = 5000,
- .has_ao = 1,
- .has_dig_in = 1,
- .has_dig_out = 1,
- .has_ttl_io = 1,
- },
- [BOARD_APCI3110_8_4] = {
- .name = "apci3110-8-4",
- .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
- .ai_n_chan = 8,
- .ai_maxdata = 0x0fff,
- .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
- .ai_min_acq_ns = 5000,
- .has_ao = 1,
- .has_dig_in = 1,
- .has_dig_out = 1,
- .has_ttl_io = 1,
- },
- [BOARD_APCI3116_16_4] = {
- .name = "apci3116-16-4",
- .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
- .ai_n_chan = 16,
- .ai_maxdata = 0xffff,
- .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
- .ai_min_acq_ns = 5000,
- .has_ao = 1,
- .has_dig_in = 1,
- .has_dig_out = 1,
- .has_ttl_io = 1,
- },
- [BOARD_APCI3116_8_4] = {
- .name = "apci3116-8-4",
- .ai_subdev_flags = SDF_COMMON | SDF_GROUND | SDF_DIFF,
- .ai_n_chan = 8,
- .ai_maxdata = 0xffff,
- .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
- .ai_min_acq_ns = 5000,
- .has_ao = 1,
- .has_dig_in = 1,
- .has_dig_out = 1,
- .has_ttl_io = 1,
- },
- [BOARD_APCI3003] = {
- .name = "apci3003",
- .ai_subdev_flags = SDF_DIFF,
- .ai_n_chan = 4,
- .ai_maxdata = 0xffff,
- .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US |
- CONV_UNIT_NS,
- .ai_min_acq_ns = 2500,
- .has_dig_in = 1,
- .has_dig_out = 1,
- },
- [BOARD_APCI3002_16] = {
- .name = "apci3002-16",
- .ai_subdev_flags = SDF_DIFF,
- .ai_n_chan = 16,
- .ai_maxdata = 0xffff,
- .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
- .ai_min_acq_ns = 5000,
- .has_dig_in = 1,
- .has_dig_out = 1,
- },
- [BOARD_APCI3002_8] = {
- .name = "apci3002-8",
- .ai_subdev_flags = SDF_DIFF,
- .ai_n_chan = 8,
- .ai_maxdata = 0xffff,
- .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
- .ai_min_acq_ns = 5000,
- .has_dig_in = 1,
- .has_dig_out = 1,
- },
- [BOARD_APCI3002_4] = {
- .name = "apci3002-4",
- .ai_subdev_flags = SDF_DIFF,
- .ai_n_chan = 4,
- .ai_maxdata = 0xffff,
- .ai_conv_units = CONV_UNIT_MS | CONV_UNIT_US,
- .ai_min_acq_ns = 5000,
- .has_dig_in = 1,
- .has_dig_out = 1,
- },
- [BOARD_APCI3500] = {
- .name = "apci3500",
- .has_ao = 1,
- .has_ttl_io = 1,
- },
-};
-
-struct apci3xxx_private {
- unsigned int ai_timer;
- unsigned char ai_time_base;
-};
-
-static irqreturn_t apci3xxx_irq_handler(int irq, void *d)
-{
- struct comedi_device *dev = d;
- struct comedi_subdevice *s = dev->read_subdev;
- unsigned int status;
- unsigned int val;
-
- /* Test if interrupt occur */
- status = readl(dev->mmio + 16);
- if ((status & 0x2) == 0x2) {
- /* Reset the interrupt */
- writel(status, dev->mmio + 16);
-
- val = readl(dev->mmio + 28);
- comedi_buf_write_samples(s, &val, 1);
-
- s->async->events |= COMEDI_CB_EOA;
- comedi_handle_events(dev, s);
-
- return IRQ_HANDLED;
- }
- return IRQ_NONE;
-}
-
-static int apci3xxx_ai_started(struct comedi_device *dev)
-{
- if ((readl(dev->mmio + 8) & 0x80000) == 0x80000)
- return 1;
-
- return 0;
-}
-
-static int apci3xxx_ai_setup(struct comedi_device *dev, unsigned int chanspec)
-{
- unsigned int chan = CR_CHAN(chanspec);
- unsigned int range = CR_RANGE(chanspec);
- unsigned int aref = CR_AREF(chanspec);
- unsigned int delay_mode;
- unsigned int val;
-
- if (apci3xxx_ai_started(dev))
- return -EBUSY;
-
- /* Clear the FIFO */
- writel(0x10000, dev->mmio + 12);
-
- /* Get and save the delay mode */
- delay_mode = readl(dev->mmio + 4);
- delay_mode &= 0xfffffef0;
-
- /* Channel configuration selection */
- writel(delay_mode, dev->mmio + 4);
-
- /* Make the configuration */
- val = (range & 3) | ((range >> 2) << 6) |
- ((aref == AREF_DIFF) << 7);
- writel(val, dev->mmio + 0);
-
- /* Channel selection */
- writel(delay_mode | 0x100, dev->mmio + 4);
- writel(chan, dev->mmio + 0);
-
- /* Restore delay mode */
- writel(delay_mode, dev->mmio + 4);
-
- /* Set the number of sequence to 1 */
- writel(1, dev->mmio + 48);
-
- return 0;
-}
-
-static int apci3xxx_ai_eoc(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn,
- unsigned long context)
-{
- unsigned int status;
-
- status = readl(dev->mmio + 20);
- if (status & 0x1)
- return 0;
- return -EBUSY;
-}
-
-static int apci3xxx_ai_insn_read(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn,
- unsigned int *data)
-{
- int ret;
- int i;
-
- ret = apci3xxx_ai_setup(dev, insn->chanspec);
- if (ret)
- return ret;
-
- for (i = 0; i < insn->n; i++) {
- /* Start the conversion */
- writel(0x80000, dev->mmio + 8);
-
- /* Wait the EOS */
- ret = comedi_timeout(dev, s, insn, apci3xxx_ai_eoc, 0);
- if (ret)
- return ret;
-
- /* Read the analog value */
- data[i] = readl(dev->mmio + 28);
- }
-
- return insn->n;
-}
-
-static int apci3xxx_ai_ns_to_timer(struct comedi_device *dev,
- unsigned int *ns, unsigned int flags)
-{
- const struct apci3xxx_boardinfo *board = dev->board_ptr;
- struct apci3xxx_private *devpriv = dev->private;
- unsigned int base;
- unsigned int timer;
- int time_base;
-
- /* time_base: 0 = ns, 1 = us, 2 = ms */
- for (time_base = 0; time_base < 3; time_base++) {
- /* skip unsupported time bases */
- if (!(board->ai_conv_units & (1 << time_base)))
- continue;
-
- switch (time_base) {
- case 0:
- base = 1;
- break;
- case 1:
- base = 1000;
- break;
- case 2:
- base = 1000000;
- break;
- }
-
- switch (flags & CMDF_ROUND_MASK) {
- case CMDF_ROUND_NEAREST:
- default:
- timer = DIV_ROUND_CLOSEST(*ns, base);
- break;
- case CMDF_ROUND_DOWN:
- timer = *ns / base;
- break;
- case CMDF_ROUND_UP:
- timer = DIV_ROUND_UP(*ns, base);
- break;
- }
-
- if (timer < 0x10000) {
- devpriv->ai_time_base = time_base;
- devpriv->ai_timer = timer;
- *ns = timer * time_base;
- return 0;
- }
- }
- return -EINVAL;
-}
-
-static int apci3xxx_ai_cmdtest(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_cmd *cmd)
-{
- const struct apci3xxx_boardinfo *board = dev->board_ptr;
- int err = 0;
- unsigned int arg;
-
- /* Step 1 : check if triggers are trivially valid */
-
- err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
- err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_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->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);
- err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
- err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
- board->ai_min_acq_ns);
- 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 */
-
- arg = cmd->convert_arg;
- err |= apci3xxx_ai_ns_to_timer(dev, &arg, cmd->flags);
- err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
-
- if (err)
- return 4;
-
- return 0;
-}
-
-static int apci3xxx_ai_cmd(struct comedi_device *dev,
- struct comedi_subdevice *s)
-{
- struct apci3xxx_private *devpriv = dev->private;
- struct comedi_cmd *cmd = &s->async->cmd;
- int ret;
-
- ret = apci3xxx_ai_setup(dev, cmd->chanlist[0]);
- if (ret)
- return ret;
-
- /* Set the convert timing unit */
- writel(devpriv->ai_time_base, dev->mmio + 36);
-
- /* Set the convert timing */
- writel(devpriv->ai_timer, dev->mmio + 32);
-
- /* Start the conversion */
- writel(0x180000, dev->mmio + 8);
-
- return 0;
-}
-
-static int apci3xxx_ai_cancel(struct comedi_device *dev,
- struct comedi_subdevice *s)
-{
- return 0;
-}
-
-static int apci3xxx_ao_eoc(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn,
- unsigned long context)
-{
- unsigned int status;
-
- status = readl(dev->mmio + 96);
- if (status & 0x100)
- return 0;
- return -EBUSY;
-}
-
-static int apci3xxx_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 range = CR_RANGE(insn->chanspec);
- int ret;
- int i;
-
- for (i = 0; i < insn->n; i++) {
- unsigned int val = data[i];
-
- /* Set the range selection */
- writel(range, dev->mmio + 96);
-
- /* Write the analog value to the selected channel */
- writel((val << 8) | chan, dev->mmio + 100);
-
- /* Wait the end of transfer */
- ret = comedi_timeout(dev, s, insn, apci3xxx_ao_eoc, 0);
- if (ret)
- return ret;
-
- s->readback[chan] = val;
- }
-
- return insn->n;
-}
-
-static int apci3xxx_di_insn_bits(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn,
- unsigned int *data)
-{
- data[1] = inl(dev->iobase + 32) & 0xf;
-
- return insn->n;
-}
-
-static int apci3xxx_do_insn_bits(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn,
- unsigned int *data)
-{
- s->state = inl(dev->iobase + 48) & 0xf;
-
- if (comedi_dio_update_state(s, data))
- outl(s->state, dev->iobase + 48);
-
- data[1] = s->state;
-
- return insn->n;
-}
-
-static int apci3xxx_dio_insn_config(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn,
- unsigned int *data)
-{
- unsigned int chan = CR_CHAN(insn->chanspec);
- unsigned int mask = 0;
- int ret;
-
- /*
- * Port 0 (channels 0-7) are always inputs
- * Port 1 (channels 8-15) are always outputs
- * Port 2 (channels 16-23) are programmable i/o
- */
- if (data[0] != INSN_CONFIG_DIO_QUERY) {
- /* ignore all other instructions for ports 0 and 1 */
- if (chan < 16)
- return -EINVAL;
-
- /* changing any channel in port 2 changes the entire port */
- mask = 0xff0000;
- }
-
- ret = comedi_dio_insn_config(dev, s, insn, data, mask);
- if (ret)
- return ret;
-
- /* update port 2 configuration */
- outl((s->io_bits >> 24) & 0xff, dev->iobase + 224);
-
- return insn->n;
-}
-
-static int apci3xxx_dio_insn_bits(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn,
- unsigned int *data)
-{
- unsigned int mask;
- unsigned int val;
-
- mask = comedi_dio_update_state(s, data);
- if (mask) {
- if (mask & 0xff)
- outl(s->state & 0xff, dev->iobase + 80);
- if (mask & 0xff0000)
- outl((s->state >> 16) & 0xff, dev->iobase + 112);
- }
-
- val = inl(dev->iobase + 80);
- val |= (inl(dev->iobase + 64) << 8);
- if (s->io_bits & 0xff0000)
- val |= (inl(dev->iobase + 112) << 16);
- else
- val |= (inl(dev->iobase + 96) << 16);
-
- data[1] = val;
-
- return insn->n;
-}
-
-static int apci3xxx_reset(struct comedi_device *dev)
-{
- unsigned int val;
- int i;
-
- /* Disable the interrupt */
- disable_irq(dev->irq);
-
- /* Clear the start command */
- writel(0, dev->mmio + 8);
-
- /* Reset the interrupt flags */
- val = readl(dev->mmio + 16);
- writel(val, dev->mmio + 16);
-
- /* clear the EOS */
- readl(dev->mmio + 20);
-
- /* Clear the FIFO */
- for (i = 0; i < 16; i++)
- val = readl(dev->mmio + 28);
-
- /* Enable the interrupt */
- enable_irq(dev->irq);
-
- return 0;
-}
-
-static int apci3xxx_auto_attach(struct comedi_device *dev,
- unsigned long context)
-{
- struct pci_dev *pcidev = comedi_to_pci_dev(dev);
- const struct apci3xxx_boardinfo *board = NULL;
- struct apci3xxx_private *devpriv;
- struct comedi_subdevice *s;
- int n_subdevices;
- int subdev;
- int ret;
-
- if (context < ARRAY_SIZE(apci3xxx_boardtypes))
- board = &apci3xxx_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;
-
- dev->iobase = pci_resource_start(pcidev, 2);
- dev->mmio = pci_ioremap_bar(pcidev, 3);
- if (!dev->mmio)
- return -ENOMEM;
-
- if (pcidev->irq > 0) {
- ret = request_irq(pcidev->irq, apci3xxx_irq_handler,
- IRQF_SHARED, dev->board_name, dev);
- if (ret == 0)
- dev->irq = pcidev->irq;
- }
-
- n_subdevices = (board->ai_n_chan ? 0 : 1) + board->has_ao +
- board->has_dig_in + board->has_dig_out +
- board->has_ttl_io;
- ret = comedi_alloc_subdevices(dev, n_subdevices);
- if (ret)
- return ret;
-
- subdev = 0;
-
- /* Analog Input subdevice */
- if (board->ai_n_chan) {
- s = &dev->subdevices[subdev];
- s->type = COMEDI_SUBD_AI;
- s->subdev_flags = SDF_READABLE | board->ai_subdev_flags;
- s->n_chan = board->ai_n_chan;
- s->maxdata = board->ai_maxdata;
- s->range_table = &apci3xxx_ai_range;
- s->insn_read = apci3xxx_ai_insn_read;
- if (dev->irq) {
- /*
- * FIXME: The hardware supports multiple scan modes
- * but the original addi-data driver only supported
- * reading a single channel with interrupts. Need a
- * proper datasheet to fix this.
- *
- * The following scan modes are supported by the
- * hardware:
- * 1) Single software scan
- * 2) Single hardware triggered scan
- * 3) Continuous software scan
- * 4) Continuous software scan with timer delay
- * 5) Continuous hardware triggered scan
- * 6) Continuous hardware triggered scan with timer
- * delay
- *
- * For now, limit the chanlist to a single channel.
- */
- dev->read_subdev = s;
- s->subdev_flags |= SDF_CMD_READ;
- s->len_chanlist = 1;
- s->do_cmdtest = apci3xxx_ai_cmdtest;
- s->do_cmd = apci3xxx_ai_cmd;
- s->cancel = apci3xxx_ai_cancel;
- }
-
- subdev++;
- }
-
- /* Analog Output subdevice */
- if (board->has_ao) {
- s = &dev->subdevices[subdev];
- s->type = COMEDI_SUBD_AO;
- s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
- s->n_chan = 4;
- s->maxdata = 0x0fff;
- s->range_table = &apci3xxx_ao_range;
- s->insn_write = apci3xxx_ao_insn_write;
-
- ret = comedi_alloc_subdev_readback(s);
- if (ret)
- return ret;
-
- subdev++;
- }
-
- /* Digital Input subdevice */
- if (board->has_dig_in) {
- s = &dev->subdevices[subdev];
- 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 = apci3xxx_di_insn_bits;
-
- subdev++;
- }
-
- /* Digital Output subdevice */
- if (board->has_dig_out) {
- s = &dev->subdevices[subdev];
- 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 = apci3xxx_do_insn_bits;
-
- subdev++;
- }
-
- /* TTL Digital I/O subdevice */
- if (board->has_ttl_io) {
- s = &dev->subdevices[subdev];
- s->type = COMEDI_SUBD_DIO;
- s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
- s->n_chan = 24;
- s->maxdata = 1;
- s->io_bits = 0xff; /* channels 0-7 are always outputs */
- s->range_table = &range_digital;
- s->insn_config = apci3xxx_dio_insn_config;
- s->insn_bits = apci3xxx_dio_insn_bits;
-
- subdev++;
- }
-
- apci3xxx_reset(dev);
- return 0;
-}
-
-static void apci3xxx_detach(struct comedi_device *dev)
-{
- if (dev->iobase)
- apci3xxx_reset(dev);
- comedi_pci_detach(dev);
-}
-
-static struct comedi_driver apci3xxx_driver = {
- .driver_name = "addi_apci_3xxx",
- .module = THIS_MODULE,
- .auto_attach = apci3xxx_auto_attach,
- .detach = apci3xxx_detach,
-};
-
-static int apci3xxx_pci_probe(struct pci_dev *dev,
- const struct pci_device_id *id)
-{
- return comedi_pci_auto_config(dev, &apci3xxx_driver, id->driver_data);
-}
-
-static const struct pci_device_id apci3xxx_pci_table[] = {
- { PCI_VDEVICE(ADDIDATA, 0x3010), BOARD_APCI3000_16 },
- { PCI_VDEVICE(ADDIDATA, 0x300f), BOARD_APCI3000_8 },
- { PCI_VDEVICE(ADDIDATA, 0x300e), BOARD_APCI3000_4 },
- { PCI_VDEVICE(ADDIDATA, 0x3013), BOARD_APCI3006_16 },
- { PCI_VDEVICE(ADDIDATA, 0x3014), BOARD_APCI3006_8 },
- { PCI_VDEVICE(ADDIDATA, 0x3015), BOARD_APCI3006_4 },
- { PCI_VDEVICE(ADDIDATA, 0x3016), BOARD_APCI3010_16 },
- { PCI_VDEVICE(ADDIDATA, 0x3017), BOARD_APCI3010_8 },
- { PCI_VDEVICE(ADDIDATA, 0x3018), BOARD_APCI3010_4 },
- { PCI_VDEVICE(ADDIDATA, 0x3019), BOARD_APCI3016_16 },
- { PCI_VDEVICE(ADDIDATA, 0x301a), BOARD_APCI3016_8 },
- { PCI_VDEVICE(ADDIDATA, 0x301b), BOARD_APCI3016_4 },
- { PCI_VDEVICE(ADDIDATA, 0x301c), BOARD_APCI3100_16_4 },
- { PCI_VDEVICE(ADDIDATA, 0x301d), BOARD_APCI3100_8_4 },
- { PCI_VDEVICE(ADDIDATA, 0x301e), BOARD_APCI3106_16_4 },
- { PCI_VDEVICE(ADDIDATA, 0x301f), BOARD_APCI3106_8_4 },
- { PCI_VDEVICE(ADDIDATA, 0x3020), BOARD_APCI3110_16_4 },
- { PCI_VDEVICE(ADDIDATA, 0x3021), BOARD_APCI3110_8_4 },
- { PCI_VDEVICE(ADDIDATA, 0x3022), BOARD_APCI3116_16_4 },
- { PCI_VDEVICE(ADDIDATA, 0x3023), BOARD_APCI3116_8_4 },
- { PCI_VDEVICE(ADDIDATA, 0x300B), BOARD_APCI3003 },
- { PCI_VDEVICE(ADDIDATA, 0x3002), BOARD_APCI3002_16 },
- { PCI_VDEVICE(ADDIDATA, 0x3003), BOARD_APCI3002_8 },
- { PCI_VDEVICE(ADDIDATA, 0x3004), BOARD_APCI3002_4 },
- { PCI_VDEVICE(ADDIDATA, 0x3024), BOARD_APCI3500 },
- { 0 }
-};
-MODULE_DEVICE_TABLE(pci, apci3xxx_pci_table);
-
-static struct pci_driver apci3xxx_pci_driver = {
- .name = "addi_apci_3xxx",
- .id_table = apci3xxx_pci_table,
- .probe = apci3xxx_pci_probe,
- .remove = comedi_pci_auto_unconfig,
-};
-module_comedi_pci_driver(apci3xxx_driver, apci3xxx_pci_driver);
-
-MODULE_AUTHOR("Comedi https://www.comedi.org");
-MODULE_DESCRIPTION("Comedi low-level driver");
-MODULE_LICENSE("GPL");