Staging: comedi: add dt2811 driver
authorDavid Schleef <ds@schleef.org>
Thu, 19 Feb 2009 18:08:26 +0000 (10:08 -0800)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 3 Apr 2009 21:53:50 +0000 (14:53 -0700)
Driver for DataTranslation DT2811

From: David Schleef <ds@schleef.org>
Cc: Ian Abbott <abbotti@mev.co.uk>
Cc: Frank Mori Hess <fmhess@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/staging/comedi/drivers/dt2811.c [new file with mode: 0644]

diff --git a/drivers/staging/comedi/drivers/dt2811.c b/drivers/staging/comedi/drivers/dt2811.c
new file mode 100644 (file)
index 0000000..99cd980
--- /dev/null
@@ -0,0 +1,601 @@
+/*
+   comedi/drivers/dt2811.c
+   Hardware driver for Data Translation DT2811
+
+   COMEDI - Linux Control and Measurement Device Interface
+   History:
+   Base Version  - David A. Schleef <ds@schleef.org>
+   December 1998 - Updated to work.  David does not have a DT2811
+   board any longer so this was suffering from bitrot.
+   Updated performed by ...
+
+   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.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+/*
+Driver: dt2811
+Description: Data Translation DT2811
+Author: ds
+Devices: [Data Translation] DT2811-PGL (dt2811-pgl), DT2811-PGH (dt2811-pgh)
+Status: works
+
+Configuration options:
+  [0] - I/O port base address
+  [1] - IRQ, although this is currently unused
+  [2] - A/D reference
+          0 = signle-ended
+          1 = differential
+         2 = pseudo-differential (common reference)
+  [3] - A/D range
+          0 = [-5,5]
+         1 = [-2.5,2.5]
+         2 = [0,5]
+  [4] - D/A 0 range (same choices)
+  [4] - D/A 1 range (same choices)
+*/
+
+#include "../comedidev.h"
+
+#include <linux/ioport.h>
+
+static const char *driver_name = "dt2811";
+
+static const comedi_lrange range_dt2811_pgh_ai_5_unipolar = { 4, {
+                       RANGE(0, 5),
+                       RANGE(0, 2.5),
+                       RANGE(0, 1.25),
+                       RANGE(0, 0.625)
+       }
+};
+static const comedi_lrange range_dt2811_pgh_ai_2_5_bipolar = { 4, {
+                       RANGE(-2.5, 2.5),
+                       RANGE(-1.25, 1.25),
+                       RANGE(-0.625, 0.625),
+                       RANGE(-0.3125, 0.3125)
+       }
+};
+static const comedi_lrange range_dt2811_pgh_ai_5_bipolar = { 4, {
+                       RANGE(-5, 5),
+                       RANGE(-2.5, 2.5),
+                       RANGE(-1.25, 1.25),
+                       RANGE(-0.625, 0.625)
+       }
+};
+static const comedi_lrange range_dt2811_pgl_ai_5_unipolar = { 4, {
+                       RANGE(0, 5),
+                       RANGE(0, 0.5),
+                       RANGE(0, 0.05),
+                       RANGE(0, 0.01)
+       }
+};
+static const comedi_lrange range_dt2811_pgl_ai_2_5_bipolar = { 4, {
+                       RANGE(-2.5, 2.5),
+                       RANGE(-0.25, 0.25),
+                       RANGE(-0.025, 0.025),
+                       RANGE(-0.005, 0.005)
+       }
+};
+static const comedi_lrange range_dt2811_pgl_ai_5_bipolar = { 4, {
+                       RANGE(-5, 5),
+                       RANGE(-0.5, 0.5),
+                       RANGE(-0.05, 0.05),
+                       RANGE(-0.01, 0.01)
+       }
+};
+
+/*
+
+   0x00    ADCSR R/W  A/D Control/Status Register
+   bit 7 - (R) 1 indicates A/D conversion done
+   reading ADDAT clears bit
+   (W) ignored
+   bit 6 - (R) 1 indicates A/D error
+   (W) ignored
+   bit 5 - (R) 1 indicates A/D busy, cleared at end
+   of conversion
+   (W) ignored
+   bit 4 - (R) 0
+   (W)
+   bit 3 - (R) 0
+   bit 2 - (R/W) 1 indicates interrupts enabled
+   bits 1,0 - (R/W) mode bits
+   00  single conversion on ADGCR load
+   01  continuous conversion, internal clock,
+   (clock enabled on ADGCR load)
+   10  continuous conversion, internal clock,
+   external trigger
+   11  continuous conversion, external clock,
+   external trigger
+
+   0x01    ADGCR R/W A/D Gain/Channel Register
+   bit 6,7 - (R/W) gain select
+   00  gain=1, both PGH, PGL models
+   01  gain=2 PGH, 10 PGL
+   10  gain=4 PGH, 100 PGL
+   11  gain=8 PGH, 500 PGL
+   bit 4,5 - reserved
+   bit 3-0 - (R/W) channel select
+   channel number from 0-15
+
+   0x02,0x03 (R) ADDAT A/D Data Register
+   (W) DADAT0 D/A Data Register 0
+   0x02 low byte
+   0x03 high byte
+
+   0x04,0x05 (W) DADAT0 D/A Data Register 1
+
+   0x06 (R) DIO0 Digital Input Port 0
+   (W) DIO1 Digital Output Port 1
+
+   0x07 TMRCTR (R/W) Timer/Counter Register
+   bits 6,7 - reserved
+   bits 5-3 - Timer frequency control (mantissa)
+   543  divisor  freqency (kHz)
+   000  1        600
+   001  10       60
+   010  2        300
+   011  3        200
+   100  4        150
+   101  5        120
+   110  6        100
+   111  12       50
+   bits 2-0 - Timer frequency control (exponent)
+   210  multiply divisor/divide frequency by
+   000  1
+   001  10
+   010  100
+   011  1000
+   100  10000
+   101  100000
+   110  1000000
+   111  10000000
+
+ */
+
+#define TIMEOUT 10000
+
+#define DT2811_SIZE 8
+
+#define DT2811_ADCSR 0
+#define DT2811_ADGCR 1
+#define DT2811_ADDATLO 2
+#define DT2811_ADDATHI 3
+#define DT2811_DADAT0LO 2
+#define DT2811_DADAT0HI 3
+#define DT2811_DADAT1LO 4
+#define DT2811_DADAT1HI 5
+#define DT2811_DIO 6
+#define DT2811_TMRCTR 7
+
+/*
+ * flags
+ */
+
+/* ADCSR */
+
+#define DT2811_ADDONE   0x80
+#define DT2811_ADERROR  0x40
+#define DT2811_ADBUSY   0x20
+#define DT2811_CLRERROR 0x10
+#define DT2811_INTENB   0x04
+#define DT2811_ADMODE   0x03
+
+typedef struct {
+       const char *name;
+       const comedi_lrange *bip_5;
+       const comedi_lrange *bip_2_5;
+       const comedi_lrange *unip_5;
+} boardtype;
+static const boardtype boardtypes[] = {
+       {"dt2811-pgh",
+                       &range_dt2811_pgh_ai_5_bipolar,
+                       &range_dt2811_pgh_ai_2_5_bipolar,
+                       &range_dt2811_pgh_ai_5_unipolar,
+               },
+       {"dt2811-pgl",
+                       &range_dt2811_pgl_ai_5_bipolar,
+                       &range_dt2811_pgl_ai_2_5_bipolar,
+                       &range_dt2811_pgl_ai_5_unipolar,
+               },
+};
+
+#define this_board ((const boardtype *)dev->board_ptr)
+
+static int dt2811_attach(comedi_device * dev, comedi_devconfig * it);
+static int dt2811_detach(comedi_device * dev);
+static comedi_driver driver_dt2811 = {
+      driver_name:"dt2811",
+      module:THIS_MODULE,
+      attach:dt2811_attach,
+      detach:dt2811_detach,
+      board_name:&boardtypes[0].name,
+      num_names:sizeof(boardtypes) / sizeof(boardtype),
+      offset:sizeof(boardtype),
+};
+
+COMEDI_INITCLEANUP(driver_dt2811);
+
+static int dt2811_ai_insn(comedi_device * dev, comedi_subdevice * s,
+       comedi_insn * insn, lsampl_t * data);
+static int dt2811_ao_insn(comedi_device * dev, comedi_subdevice * s,
+       comedi_insn * insn, lsampl_t * data);
+static int dt2811_ao_insn_read(comedi_device * dev, comedi_subdevice * s,
+       comedi_insn * insn, lsampl_t * data);
+static int dt2811_di_insn_bits(comedi_device * dev, comedi_subdevice * s,
+       comedi_insn * insn, lsampl_t * data);
+static int dt2811_do_insn_bits(comedi_device * dev, comedi_subdevice * s,
+       comedi_insn * insn, lsampl_t * data);
+
+enum { card_2811_pgh, card_2811_pgl };
+typedef struct {
+       int ntrig;
+       int curadchan;
+       enum {
+               adc_singleended, adc_diff, adc_pseudo_diff
+       } adc_mux;
+       enum {
+               dac_bipolar_5, dac_bipolar_2_5, dac_unipolar_5
+       } dac_range[2];
+       const comedi_lrange *range_type_list[2];
+       lsampl_t ao_readback[2];
+} dt2811_private;
+
+#define devpriv ((dt2811_private *)dev->private)
+
+static const comedi_lrange *dac_range_types[] = {
+       &range_bipolar5,
+       &range_bipolar2_5,
+       &range_unipolar5
+};
+
+#define DT2811_TIMEOUT 5
+
+#if 0
+static irqreturn_t dt2811_interrupt(int irq, void *d PT_REGS_ARG)
+{
+       int lo, hi;
+       int data;
+       comedi_device *dev = d;
+
+       if (!dev->attached) {
+               comedi_error(dev, "spurious interrupt");
+               return IRQ_HANDLED;
+       }
+
+       lo = inb(dev->iobase + DT2811_ADDATLO);
+       hi = inb(dev->iobase + DT2811_ADDATHI);
+
+       data = lo + (hi << 8);
+
+       if (!(--devpriv->ntrig)) {
+               /* how to turn off acquisition */
+               s->async->events |= COMEDI_SB_EOA;
+       }
+       comedi_event(dev, s);
+       return IRQ_HANDLED;
+}
+#endif
+
+/*
+  options[0]   Board base address
+  options[1]   IRQ
+  options[2]   Input configuration
+                 0 == single-ended
+                 1 == differential
+                 2 == pseudo-differential
+  options[3]   Analog input range configuration
+                 0 == bipolar 5  (-5V -- +5V)
+                 1 == bipolar 2.5V  (-2.5V -- +2.5V)
+                 2 == unipolar 5V  (0V -- +5V)
+  options[4]   Analog output 0 range configuration
+                 0 == bipolar 5  (-5V -- +5V)
+                 1 == bipolar 2.5V  (-2.5V -- +2.5V)
+                 2 == unipolar 5V  (0V -- +5V)
+  options[5]   Analog output 1 range configuration
+                 0 == bipolar 5  (-5V -- +5V)
+                 1 == bipolar 2.5V  (-2.5V -- +2.5V)
+                 2 == unipolar 5V  (0V -- +5V)
+*/
+
+static int dt2811_attach(comedi_device * dev, comedi_devconfig * it)
+{
+       //int i, irq;
+       //unsigned long irqs;
+       //long flags;
+       int ret;
+       comedi_subdevice *s;
+       unsigned long iobase;
+
+       iobase = it->options[0];
+
+       printk("comedi%d: dt2811: base=0x%04lx\n", dev->minor, iobase);
+
+       if (!request_region(iobase, DT2811_SIZE, driver_name)) {
+               printk("I/O port conflict\n");
+               return -EIO;
+       }
+
+       dev->iobase = iobase;
+       dev->board_name = this_board->name;
+
+#if 0
+       outb(0, dev->iobase + DT2811_ADCSR);
+       comedi_udelay(100);
+       i = inb(dev->iobase + DT2811_ADDATLO);
+       i = inb(dev->iobase + DT2811_ADDATHI);
+#endif
+
+#if 0
+       irq = it->options[1];
+       if (irq < 0) {
+               save_flags(flags);
+               sti();
+               irqs = probe_irq_on();
+
+               outb(DT2811_CLRERROR | DT2811_INTENB,
+                       dev->iobase + DT2811_ADCSR);
+               outb(0, dev->iobase + DT2811_ADGCR);
+
+               comedi_udelay(100);
+
+               irq = probe_irq_off(irqs);
+               restore_flags(flags);
+
+               /*outb(DT2811_CLRERROR|DT2811_INTENB,dev->iobase+DT2811_ADCSR); */
+
+               if (inb(dev->iobase + DT2811_ADCSR) & DT2811_ADERROR) {
+                       printk("error probing irq (bad) \n");
+               }
+               dev->irq = 0;
+               if (irq > 0) {
+                       i = inb(dev->iobase + DT2811_ADDATLO);
+                       i = inb(dev->iobase + DT2811_ADDATHI);
+                       printk("(irq = %d)\n", irq);
+                       ret = comedi_request_irq(irq, dt2811_interrupt, 0,
+                               driver_name, dev);
+                       if (ret < 0)
+                               return -EIO;
+                       dev->irq = irq;
+               } else if (irq == 0) {
+                       printk("(no irq)\n");
+               } else {
+                       printk("( multiple irq's -- this is bad! )\n");
+               }
+       }
+#endif
+
+       if ((ret = alloc_subdevices(dev, 4)) < 0)
+               return ret;
+       if ((ret = alloc_private(dev, sizeof(dt2811_private))) < 0)
+               return ret;
+       switch (it->options[2]) {
+       case 0:
+               devpriv->adc_mux = adc_singleended;
+               break;
+       case 1:
+               devpriv->adc_mux = adc_diff;
+               break;
+       case 2:
+               devpriv->adc_mux = adc_pseudo_diff;
+               break;
+       default:
+               devpriv->adc_mux = adc_singleended;
+               break;
+       }
+       switch (it->options[4]) {
+       case 0:
+               devpriv->dac_range[0] = dac_bipolar_5;
+               break;
+       case 1:
+               devpriv->dac_range[0] = dac_bipolar_2_5;
+               break;
+       case 2:
+               devpriv->dac_range[0] = dac_unipolar_5;
+               break;
+       default:
+               devpriv->dac_range[0] = dac_bipolar_5;
+               break;
+       }
+       switch (it->options[5]) {
+       case 0:
+               devpriv->dac_range[1] = dac_bipolar_5;
+               break;
+       case 1:
+               devpriv->dac_range[1] = dac_bipolar_2_5;
+               break;
+       case 2:
+               devpriv->dac_range[1] = dac_unipolar_5;
+               break;
+       default:
+               devpriv->dac_range[1] = dac_bipolar_5;
+               break;
+       }
+
+       s = dev->subdevices + 0;
+       /* initialize the ADC subdevice */
+       s->type = COMEDI_SUBD_AI;
+       s->subdev_flags = SDF_READABLE | SDF_GROUND;
+       s->n_chan = devpriv->adc_mux == adc_diff ? 8 : 16;
+       s->insn_read = dt2811_ai_insn;
+       s->maxdata = 0xfff;
+       switch (it->options[3]) {
+       case 0:
+       default:
+               s->range_table = this_board->bip_5;
+               break;
+       case 1:
+               s->range_table = this_board->bip_2_5;
+               break;
+       case 2:
+               s->range_table = this_board->unip_5;
+               break;
+       }
+
+       s = dev->subdevices + 1;
+       /* ao subdevice */
+       s->type = COMEDI_SUBD_AO;
+       s->subdev_flags = SDF_WRITABLE;
+       s->n_chan = 2;
+       s->insn_write = dt2811_ao_insn;
+       s->insn_read = dt2811_ao_insn_read;
+       s->maxdata = 0xfff;
+       s->range_table_list = devpriv->range_type_list;
+       devpriv->range_type_list[0] = dac_range_types[devpriv->dac_range[0]];
+       devpriv->range_type_list[1] = dac_range_types[devpriv->dac_range[1]];
+
+       s = dev->subdevices + 2;
+       /* di subdevice */
+       s->type = COMEDI_SUBD_DI;
+       s->subdev_flags = SDF_READABLE;
+       s->n_chan = 8;
+       s->insn_bits = dt2811_di_insn_bits;
+       s->maxdata = 1;
+       s->range_table = &range_digital;
+
+       s = dev->subdevices + 3;
+       /* do subdevice */
+       s->type = COMEDI_SUBD_DO;
+       s->subdev_flags = SDF_WRITABLE;
+       s->n_chan = 8;
+       s->insn_bits = dt2811_do_insn_bits;
+       s->maxdata = 1;
+       s->state = 0;
+       s->range_table = &range_digital;
+
+       return 0;
+}
+
+static int dt2811_detach(comedi_device * dev)
+{
+       printk("comedi%d: dt2811: remove\n", dev->minor);
+
+       if (dev->irq) {
+               comedi_free_irq(dev->irq, dev);
+       }
+       if (dev->iobase) {
+               release_region(dev->iobase, DT2811_SIZE);
+       }
+
+       return 0;
+}
+
+static int dt2811_ai_insn(comedi_device * dev, comedi_subdevice * s,
+       comedi_insn * insn, lsampl_t * data)
+{
+       int chan = CR_CHAN(insn->chanspec);
+       int timeout = DT2811_TIMEOUT;
+       int i;
+
+       for (i = 0; i < insn->n; i++) {
+               outb(chan, dev->iobase + DT2811_ADGCR);
+
+               while (timeout
+                       && inb(dev->iobase + DT2811_ADCSR) & DT2811_ADBUSY)
+                       timeout--;
+               if (!timeout)
+                       return -ETIME;
+
+               data[i] = inb(dev->iobase + DT2811_ADDATLO);
+               data[i] |= inb(dev->iobase + DT2811_ADDATHI) << 8;
+               data[i] &= 0xfff;
+       }
+
+       return i;
+}
+
+#if 0
+/* Wow.  This is code from the Comedi stone age.  But it hasn't been
+ * replaced, so I'll let it stay. */
+int dt2811_adtrig(kdev_t minor, comedi_adtrig * adtrig)
+{
+       comedi_device *dev = comedi_devices + minor;
+
+       if (adtrig->n < 1)
+               return 0;
+       dev->curadchan = adtrig->chan;
+       switch (dev->i_admode) {
+       case COMEDI_MDEMAND:
+               dev->ntrig = adtrig->n - 1;
+               /*printk("dt2811: AD soft trigger\n"); */
+               /*outb(DT2811_CLRERROR|DT2811_INTENB,dev->iobase+DT2811_ADCSR); *//* not neccessary */
+               outb(dev->curadchan, dev->iobase + DT2811_ADGCR);
+               do_gettimeofday(&trigtime);
+               break;
+       case COMEDI_MCONTS:
+               dev->ntrig = adtrig->n;
+               break;
+       }
+
+       return 0;
+}
+#endif
+
+static int dt2811_ao_insn(comedi_device * dev, comedi_subdevice * s,
+       comedi_insn * insn, lsampl_t * data)
+{
+       int i;
+       int chan;
+
+       chan = CR_CHAN(insn->chanspec);
+
+       for (i = 0; i < insn->n; i++) {
+               outb(data[i] & 0xff, dev->iobase + DT2811_DADAT0LO + 2 * chan);
+               outb((data[i] >> 8) & 0xff,
+                       dev->iobase + DT2811_DADAT0HI + 2 * chan);
+               devpriv->ao_readback[chan] = data[i];
+       }
+
+       return i;
+}
+
+static int dt2811_ao_insn_read(comedi_device * dev, comedi_subdevice * s,
+       comedi_insn * insn, lsampl_t * data)
+{
+       int i;
+       int chan;
+
+       chan = CR_CHAN(insn->chanspec);
+
+       for (i = 0; i < insn->n; i++) {
+               data[i] = devpriv->ao_readback[chan];
+       }
+
+       return i;
+}
+
+static int dt2811_di_insn_bits(comedi_device * dev, comedi_subdevice * s,
+       comedi_insn * insn, lsampl_t * data)
+{
+       if (insn->n != 2)
+               return -EINVAL;
+
+       data[1] = inb(dev->iobase + DT2811_DIO);
+
+       return 2;
+}
+
+static int dt2811_do_insn_bits(comedi_device * dev, comedi_subdevice * s,
+       comedi_insn * insn, lsampl_t * data)
+{
+       if (insn->n != 2)
+               return -EINVAL;
+
+       s->state &= ~data[0];
+       s->state |= data[0] & data[1];
+       outb(s->state, dev->iobase + DT2811_DIO);
+
+       data[1] = s->state;
+
+       return 2;
+}