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