staging: comedi: adv_pci1710: separate out PCI-1720 support as a new driver
authorH Hartley Sweeten <hsweeten@visionengravers.com>
Fri, 30 Oct 2015 18:10:05 +0000 (11:10 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 16 Nov 2015 04:02:47 +0000 (20:02 -0800)
The PCI-1710 series boards are multifunction data acquisition boards with
analog inputs and outputs, digital inputs and outputs, and counter/timer
functions.

The PCI-1720 is a simple 4 channel analog output board. It also uses a
unique register map.

Separate out the PCI-1720 support as a new driver, adv_pci1720, to ease
maintainability.

Fix some issues with the PCI-1720 support in the new driver:
  1) the registers are all 8-bit
  2) remove the analog output "reset" when the driver attaches/detaches
  3) disable "synchronized output" to simplify the analog outputs
  4) remove the need for the private data
  5) add support for the BoardID register to allow multiple cards

Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com>
Reviewed-by: Ian Abbott <abbotti@mev.co.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/comedi/Kconfig
drivers/staging/comedi/drivers/Makefile
drivers/staging/comedi/drivers/adv_pci1710.c
drivers/staging/comedi/drivers/adv_pci1720.c [new file with mode: 0644]

index ac0f01007abd83825b4405ea46ba010d3a1efa6e..945c85a10793074cb7ec246d0ebc16f5eef30f8b 100644 (file)
@@ -737,15 +737,23 @@ config COMEDI_ADL_PCI9118
          called adl_pci9118.
 
 config COMEDI_ADV_PCI1710
-       tristate "Advantech PCI-171x, PCI-1720 and PCI-1731 support"
+       tristate "Advantech PCI-171x and PCI-1731 support"
        select COMEDI_8254
        ---help---
          Enable support for Advantech PCI-1710, PCI-1710HG, PCI-1711,
-         PCI-1713, PCI-1720 and PCI-1731
+         PCI-1713 and PCI-1731
 
          To compile this driver as a module, choose M here: the module will be
          called adv_pci1710.
 
+config COMEDI_ADV_PCI1720
+       tristate "Advantech PCI-1720 support"
+       ---help---
+         Enable support for Advantech PCI-1720 Analog Output board.
+
+         To compile this driver as a module, choose M here: the module will be
+         called adv_pci1720.
+
 config COMEDI_ADV_PCI1723
        tristate "Advantech PCI-1723 support"
        ---help---
index c3b8f2d7611b9fb0f5159b8034609cb9cce2e308..94c179bea71ef41a1fced9cbabdadef1107506ed 100644 (file)
@@ -78,6 +78,7 @@ obj-$(CONFIG_COMEDI_ADL_PCI8164)      += adl_pci8164.o
 obj-$(CONFIG_COMEDI_ADL_PCI9111)       += adl_pci9111.o
 obj-$(CONFIG_COMEDI_ADL_PCI9118)       += adl_pci9118.o
 obj-$(CONFIG_COMEDI_ADV_PCI1710)       += adv_pci1710.o
+obj-$(CONFIG_COMEDI_ADV_PCI1720)       += adv_pci1720.o
 obj-$(CONFIG_COMEDI_ADV_PCI1723)       += adv_pci1723.o
 obj-$(CONFIG_COMEDI_ADV_PCI1724)       += adv_pci1724.o
 obj-$(CONFIG_COMEDI_ADV_PCI_DIO)       += adv_pci_dio.o
index 399c511cfe0aba3b1e2714610512af05a6e049a8..45d7f42312ec3da8576e905e7a2f8fe3ebdecc7f 100644 (file)
@@ -11,8 +11,9 @@
  * Driver: adv_pci1710
  * Description: Comedi driver for Advantech PCI-1710 series boards
  * Devices: [Advantech] PCI-1710 (adv_pci1710), PCI-1710HG, PCI-1711,
- *   PCI-1713, PCI-1720, PCI-1731
+ *   PCI-1713, PCI-1731
  * Author: Michal Dobes <dobes@tesnet.cz>
+ * Updated: Fri, 29 Oct 2015 17:19:35 -0700
  * Status: works
  *
  * Configuration options: not applicable, uses PCI auto config
 #define PCI171X_DO_REG         0x10    /* W:   digital outputs */
 #define PCI171X_TIMER_BASE     0x18    /* R/W: 8254 timer */
 
-/*
- * PCI-1720 only has analog outputs and has a different
- * register map (dev->iobase)
- */
-#define PCI1720_DA_REG(x)      (0x00 + ((x) * 2)) /* W:   D/A registers */
-#define PCI1720_RANGE_REG      0x08    /* R/W: D/A range register */
-#define PCI1720_SYNC_REG       0x09    /* W:   D/A synchronized output */
-#define PCI1720_SYNC_CTRL_REG  0x0f    /* R/W: D/A synchronized control */
-#define PCI1720_SYNC_CTRL_SC0  BIT(0)  /* set synchronous output mode */
-
 static const struct comedi_lrange range_pci1710_3 = {
        9, {
                BIP_RANGE(5),
@@ -122,15 +113,6 @@ static const struct comedi_lrange range_pci17x1 = {
 
 static const char range_codes_pci17x1[] = { 0x00, 0x01, 0x02, 0x03, 0x04 };
 
-static const struct comedi_lrange pci1720_ao_range = {
-       4, {
-               UNI_RANGE(5),
-               UNI_RANGE(10),
-               BIP_RANGE(5),
-               BIP_RANGE(10)
-       }
-};
-
 static const struct comedi_lrange pci171x_ao_range = {
        2, {
                UNI_RANGE(5),
@@ -143,7 +125,6 @@ enum pci1710_boardid {
        BOARD_PCI1710HG,
        BOARD_PCI1711,
        BOARD_PCI1713,
-       BOARD_PCI1720,
        BOARD_PCI1731,
 };
 
@@ -153,7 +134,6 @@ struct boardtype {
        const struct comedi_lrange *rangelist_ai;       /*  rangelist for A/D */
        const char *rangecode_ai;       /*  range codes for programming */
        unsigned int is_pci1713:1;
-       unsigned int is_pci1720:1;
        unsigned int has_irq:1;
        unsigned int has_large_fifo:1;  /* 4K or 1K FIFO */
        unsigned int has_diff_ai:1;
@@ -207,11 +187,6 @@ static const struct boardtype boardtypes[] = {
                .has_large_fifo = 1,
                .has_diff_ai    = 1,
        },
-       [BOARD_PCI1720] = {
-               .name           = "pci1720",
-               .is_pci1720     = 1,
-               .has_ao         = 1,
-       },
        [BOARD_PCI1731] = {
                .name           = "pci1731",
                .n_aichan       = 16,
@@ -465,36 +440,6 @@ static int pci171x_do_insn_bits(struct comedi_device *dev,
        return insn->n;
 }
 
-static int pci1720_ao_insn_write(struct comedi_device *dev,
-                                struct comedi_subdevice *s,
-                                struct comedi_insn *insn,
-                                unsigned int *data)
-{
-       struct pci1710_private *devpriv = dev->private;
-       unsigned int chan = CR_CHAN(insn->chanspec);
-       unsigned int range = CR_RANGE(insn->chanspec);
-       unsigned int val;
-       int i;
-
-       val = devpriv->da_ranges & (~(0x03 << (chan << 1)));
-       val |= (range << (chan << 1));
-       if (val != devpriv->da_ranges) {
-               outb(val, dev->iobase + PCI1720_RANGE_REG);
-               devpriv->da_ranges = val;
-       }
-
-       val = s->readback[chan];
-       for (i = 0; i < insn->n; i++) {
-               val = data[i];
-               outw(val, dev->iobase + PCI1720_DA_REG(chan));
-               outb(0, dev->iobase + PCI1720_SYNC_REG); /* update outputs */
-       }
-
-       s->readback[chan] = val;
-
-       return insn->n;
-}
-
 static int pci171x_ai_cancel(struct comedi_device *dev,
                             struct comedi_subdevice *s)
 {
@@ -790,7 +735,7 @@ static int pci171x_insn_counter_config(struct comedi_device *dev,
        return insn->n;
 }
 
-static int pci171x_reset(struct comedi_device *dev)
+static int pci1710_reset(struct comedi_device *dev)
 {
        const struct boardtype *board = dev->board_ptr;
        struct pci1710_private *devpriv = dev->private;
@@ -815,33 +760,6 @@ static int pci171x_reset(struct comedi_device *dev)
        return 0;
 }
 
-static int pci1720_reset(struct comedi_device *dev)
-{
-       struct pci1710_private *devpriv = dev->private;
-       /* set synchronous output mode */
-       outb(PCI1720_SYNC_CTRL_SC0, dev->iobase + PCI1720_SYNC_CTRL_REG);
-       devpriv->da_ranges = 0xAA;
-       /* set all ranges to +/-5V and outputs to 0V */
-       outb(devpriv->da_ranges, dev->iobase + PCI1720_RANGE_REG);
-       outw(0x0800, dev->iobase + PCI1720_DA_REG(0));
-       outw(0x0800, dev->iobase + PCI1720_DA_REG(1));
-       outw(0x0800, dev->iobase + PCI1720_DA_REG(2));
-       outw(0x0800, dev->iobase + PCI1720_DA_REG(3));
-       outb(0, dev->iobase + PCI1720_SYNC_REG);        /* update outputs */
-
-       return 0;
-}
-
-static int pci1710_reset(struct comedi_device *dev)
-{
-       const struct boardtype *board = dev->board_ptr;
-
-       if (board->is_pci1720)
-               return pci1720_reset(dev);
-
-       return pci171x_reset(dev);
-}
-
 static int pci1710_auto_attach(struct comedi_device *dev,
                               unsigned long context)
 {
@@ -922,29 +840,15 @@ static int pci1710_auto_attach(struct comedi_device *dev,
                s = &dev->subdevices[subdev];
                s->type         = COMEDI_SUBD_AO;
                s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
+               s->n_chan       = 2;
                s->maxdata      = 0x0fff;
-               if (board->is_pci1720) {
-                       s->n_chan       = 4;
-                       s->range_table  = &pci1720_ao_range;
-                       s->insn_write   = pci1720_ao_insn_write;
-               } else {
-                       s->n_chan       = 2;
-                       s->range_table  = &pci171x_ao_range;
-                       s->insn_write   = pci171x_ao_insn_write;
-               }
+               s->range_table  = &pci171x_ao_range;
+               s->insn_write   = pci171x_ao_insn_write;
 
                ret = comedi_alloc_subdev_readback(s);
                if (ret)
                        return ret;
 
-               /* initialize the readback values to match the board reset */
-               if (board->is_pci1720) {
-                       int i;
-
-                       for (i = 0; i < s->n_chan; i++)
-                               s->readback[i] = 0x0800;
-               }
-
                subdev++;
        }
 
@@ -1063,7 +967,6 @@ static const struct pci_device_id adv_pci1710_pci_table[] = {
        },
        { PCI_VDEVICE(ADVANTECH, 0x1711), BOARD_PCI1711 },
        { PCI_VDEVICE(ADVANTECH, 0x1713), BOARD_PCI1713 },
-       { PCI_VDEVICE(ADVANTECH, 0x1720), BOARD_PCI1720 },
        { PCI_VDEVICE(ADVANTECH, 0x1731), BOARD_PCI1731 },
        { 0 }
 };
diff --git a/drivers/staging/comedi/drivers/adv_pci1720.c b/drivers/staging/comedi/drivers/adv_pci1720.c
new file mode 100644 (file)
index 0000000..4830a1c
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ * COMEDI driver for Advantech PCI-1720U
+ * Copyright (c) 2015 H Hartley Sweeten <hsweeten@visionengravers.com>
+ *
+ * Separated from the adv_pci1710 driver written by:
+ * Michal Dobes <dobes@tesnet.cz>
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Driver: adv_pci1720
+ * Description: 4-channel Isolated D/A Output board
+ * Devices: [Advantech] PCI-7120U (adv_pci1720)
+ * Author: H Hartley Sweeten <hsweeten@visionengravers.com>
+ * Updated: Fri, 29 Oct 2015 17:19:35 -0700
+ * Status: untested
+ *
+ * Configuration options: not applicable, uses PCI auto config
+ *
+ * The PCI-1720 has 4 isolated 12-bit analog output channels with multiple
+ * output ranges. It also has a BoardID switch to allow differentiating
+ * multiple boards in the system.
+ *
+ * The analog outputs can operate in two modes, immediate and synchronized.
+ * This driver currently does not support the synchronized output mode.
+ *
+ * Jumpers JP1 to JP4 are used to set the current sink ranges for each
+ * analog output channel. In order to use the current sink ranges, the
+ * unipolar 5V range must be used. The voltage output and sink output for
+ * each channel is available on the connector as separate pins.
+ *
+ * Jumper JP5 controls the "hot" reset state of the analog outputs.
+ * Depending on its setting, the analog outputs will either keep the
+ * last settings and output values or reset to the default state after
+ * a "hot" reset. The default state for all channels is uniploar 5V range
+ * and all the output values are 0V. To allow this feature to work, the
+ * analog outputs are not "reset" when the driver attaches.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+
+#include "../comedi_pci.h"
+
+/*
+ * PCI BAR2 Register map (dev->iobase)
+ */
+#define PCI1720_AO_LSB_REG(x)          (0x00 + ((x) * 2))
+#define PCI1720_AO_MSB_REG(x)          (0x01 + ((x) * 2))
+#define PCI1720_AO_RANGE_REG           0x08
+#define PCI1720_AO_RANGE(c, r)         (((r) & 0x3) << ((c) * 2))
+#define PCI1720_AO_RANGE_MASK(c)       PCI1720_AO_RANGE((c), 0x3)
+#define PCI1720_SYNC_REG               0x09
+#define PCI1720_SYNC_CTRL_REG          0x0f
+#define PCI1720_SYNC_CTRL_SC0          BIT(0)
+#define PCI1720_BOARDID_REG            0x14
+
+static const struct comedi_lrange pci1720_ao_range = {
+       4, {
+               UNI_RANGE(5),
+               UNI_RANGE(10),
+               BIP_RANGE(5),
+               BIP_RANGE(10)
+       }
+};
+
+static int pci1720_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);
+       unsigned int val;
+       int i;
+
+       /* set the channel range and polarity */
+       val = inb(dev->iobase + PCI1720_AO_RANGE_REG);
+       val &= ~PCI1720_AO_RANGE_MASK(chan);
+       val |= PCI1720_AO_RANGE(chan, range);
+       outb(val, dev->iobase + PCI1720_AO_RANGE_REG);
+
+       val = s->readback[chan];
+       for (i = 0; i < insn->n; i++) {
+               val = data[i];
+
+               outb(val & 0xff, dev->iobase + PCI1720_AO_LSB_REG(chan));
+               outb((val >> 8) & 0xff, dev->iobase + PCI1720_AO_MSB_REG(chan));
+
+               /* conversion time is 2us (500 kHz throughput) */
+               usleep_range(2, 100);
+       }
+
+       s->readback[chan] = val;
+
+       return insn->n;
+}
+
+static int pci1720_di_insn_bits(struct comedi_device *dev,
+                               struct comedi_subdevice *s,
+                               struct comedi_insn *insn,
+                               unsigned int *data)
+{
+       data[1] = inb(dev->iobase + PCI1720_BOARDID_REG);
+
+       return insn->n;
+}
+
+static int pci1720_auto_attach(struct comedi_device *dev,
+                              unsigned long context)
+{
+       struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+       struct comedi_subdevice *s;
+       int ret;
+
+       ret = comedi_pci_enable(dev);
+       if (ret)
+               return ret;
+       dev->iobase = pci_resource_start(pcidev, 2);
+
+       ret = comedi_alloc_subdevices(dev, 2);
+       if (ret)
+               return ret;
+
+       /* Analog Output subdevice */
+       s = &dev->subdevices[0];
+       s->type         = COMEDI_SUBD_AO;
+       s->subdev_flags = SDF_WRITABLE;
+       s->n_chan       = 4;
+       s->maxdata      = 0x0fff;
+       s->range_table  = &pci1720_ao_range;
+       s->insn_write   = pci1720_ao_insn_write;
+
+       ret = comedi_alloc_subdev_readback(s);
+       if (ret)
+               return ret;
+
+       /* Digital Input subdevice (BoardID SW1) */
+       s = &dev->subdevices[1];
+       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    = pci1720_di_insn_bits;
+
+       /* disable synchronized output, channels update when written */
+       outb(0, dev->iobase + PCI1720_SYNC_CTRL_REG);
+
+       return 0;
+}
+
+static struct comedi_driver adv_pci1720_driver = {
+       .driver_name    = "adv_pci1720",
+       .module         = THIS_MODULE,
+       .auto_attach    = pci1720_auto_attach,
+       .detach         = comedi_pci_detach,
+};
+
+static int adv_pci1720_pci_probe(struct pci_dev *dev,
+                                const struct pci_device_id *id)
+{
+       return comedi_pci_auto_config(dev, &adv_pci1720_driver,
+                                     id->driver_data);
+}
+
+static const struct pci_device_id adv_pci1720_pci_table[] = {
+       { PCI_DEVICE(PCI_VENDOR_ID_ADVANTECH, 0x1720) },
+       { 0 }
+};
+MODULE_DEVICE_TABLE(pci, adv_pci1720_pci_table);
+
+static struct pci_driver adv_pci1720_pci_driver = {
+       .name           = "adv_pci1720",
+       .id_table       = adv_pci1720_pci_table,
+       .probe          = adv_pci1720_pci_probe,
+       .remove         = comedi_pci_auto_unconfig,
+};
+module_comedi_pci_driver(adv_pci1720_driver, adv_pci1720_pci_driver);
+
+MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>");
+MODULE_DESCRIPTION("Comedi driver for Advantech PCI-1720 Analog Output board");
+MODULE_LICENSE("GPL");