From: David Schleef <ds@schleef.org>
Date: Thu, 19 Feb 2009 18:08:26 +0000 (-0800)
Subject: Staging: comedi: add dt2811 driver
X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=3c443716217c9bd10ae0ce2f0ea3238f28bb5c31;p=GitHub%2Fexynos8895%2Fandroid_kernel_samsung_universal8895.git

Staging: comedi: add dt2811 driver

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>
---

diff --git a/drivers/staging/comedi/drivers/dt2811.c b/drivers/staging/comedi/drivers/dt2811.c
new file mode 100644
index 000000000000..99cd98067ca4
--- /dev/null
+++ b/drivers/staging/comedi/drivers/dt2811.c
@@ -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;
+}