From 72095adbd987f37c0c9de02ac12f58459789093b Mon Sep 17 00:00:00 2001 From: David Schleef Date: Thu, 19 Feb 2009 10:09:38 -0800 Subject: Staging: comedi: add dt2815 driver From: David Schleef Driver for DataTranslation DT2815 From: David Schleef Cc: Ian Abbott Cc: Frank Mori Hess Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/drivers/dt2815.c | 262 ++++++++++++++++++++++++++++++++ 1 file changed, 262 insertions(+) --- /dev/null +++ b/drivers/staging/comedi/drivers/dt2815.c @@ -0,0 +1,262 @@ +/* + comedi/drivers/dt2815.c + Hardware driver for Data Translation DT2815 + + COMEDI - Linux Control and Measurement Device Interface + Copyright (C) 1999 Anders Blomdell + + 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: dt2815 +Description: Data Translation DT2815 +Author: ds +Status: mostly complete, untested +Devices: [Data Translation] DT2815 (dt2815) + +I'm not sure anyone has ever tested this board. If you have information +contrary, please update. + +Configuration options: + [0] - I/O port base base address + [1] - IRQ (unused) + [2] - Voltage unipolar/bipolar configuration + 0 == unipolar 5V (0V -- +5V) + 1 == bipolar 5V (-5V -- +5V) + [3] - Current offset configuration + 0 == disabled (0mA -- +32mAV) + 1 == enabled (+4mA -- +20mAV) + [4] - Firmware program configuration + 0 == program 1 (see manual table 5-4) + 1 == program 2 (see manual table 5-4) + 2 == program 3 (see manual table 5-4) + 3 == program 4 (see manual table 5-4) + [5] - Analog output 0 range configuration + 0 == voltage + 1 == current + [6] - Analog output 1 range configuration (same options) + [7] - Analog output 2 range configuration (same options) + [8] - Analog output 3 range configuration (same options) + [9] - Analog output 4 range configuration (same options) + [10] - Analog output 5 range configuration (same options) + [11] - Analog output 6 range configuration (same options) + [12] - Analog output 7 range configuration (same options) +*/ + +#include "../comedidev.h" + +#include +#include + +static const comedi_lrange range_dt2815_ao_32_current = { 1, { + RANGE_mA(0, 32) + } +}; +static const comedi_lrange range_dt2815_ao_20_current = { 1, { + RANGE_mA(4, 20) + } +}; + +#define DT2815_SIZE 2 + +#define DT2815_DATA 0 +#define DT2815_STATUS 1 + +static int dt2815_attach(comedi_device * dev, comedi_devconfig * it); +static int dt2815_detach(comedi_device * dev); +static comedi_driver driver_dt2815 = { + driver_name:"dt2815", + module:THIS_MODULE, + attach:dt2815_attach, + detach:dt2815_detach, +}; + +COMEDI_INITCLEANUP(driver_dt2815); + +static void dt2815_free_resources(comedi_device * dev); + +typedef struct { + const comedi_lrange *range_type_list[8]; + lsampl_t ao_readback[8]; +} dt2815_private; + +#define devpriv ((dt2815_private *)dev->private) + +static int dt2815_wait_for_status(comedi_device * dev, int status) +{ + int i; + + for (i = 0; i < 100; i++) { + if (inb(dev->iobase + DT2815_STATUS) == status) + break; + } + return status; +} + +static int dt2815_ao_insn_read(comedi_device * dev, comedi_subdevice * s, + comedi_insn * insn, lsampl_t * data) +{ + int i; + int chan = CR_CHAN(insn->chanspec); + + for (i = 0; i < insn->n; i++) { + data[i] = devpriv->ao_readback[chan]; + } + + return i; +} + +static int dt2815_ao_insn(comedi_device * dev, comedi_subdevice * s, + comedi_insn * insn, lsampl_t * data) +{ + int i; + int chan = CR_CHAN(insn->chanspec); + unsigned int status; + unsigned int lo, hi; + + for (i = 0; i < insn->n; i++) { + lo = ((data[i] & 0x0f) << 4) | (chan << 1) | 0x01; + hi = (data[i] & 0xff0) >> 4; + + status = dt2815_wait_for_status(dev, 0x00); + if (status != 0) { + rt_printk + ("dt2815: failed to write low byte on %d reason %x\n", + chan, status); + return -EBUSY; + } + + outb(lo, dev->iobase + DT2815_DATA); + + status = dt2815_wait_for_status(dev, 0x10); + if (status != 0x10) { + rt_printk + ("dt2815: failed to write high byte on %d reason %x\n", + chan, status); + return -EBUSY; + } + devpriv->ao_readback[chan] = data[i]; + } + return i; +} + +/* + options[0] Board base address + options[1] IRQ (not applicable) + options[2] Voltage unipolar/bipolar configuration + 0 == unipolar 5V (0V -- +5V) + 1 == bipolar 5V (-5V -- +5V) + options[3] Current offset configuration + 0 == disabled (0mA -- +32mAV) + 1 == enabled (+4mA -- +20mAV) + options[4] Firmware program configuration + 0 == program 1 (see manual table 5-4) + 1 == program 2 (see manual table 5-4) + 2 == program 3 (see manual table 5-4) + 3 == program 4 (see manual table 5-4) + options[5] Analog output 0 range configuration + 0 == voltage + 1 == current + options[6] Analog output 1 range configuration + ... + options[12] Analog output 7 range configuration + 0 == voltage + 1 == current + */ + +static int dt2815_attach(comedi_device * dev, comedi_devconfig * it) +{ + comedi_subdevice *s; + int i; + const comedi_lrange *current_range_type, *voltage_range_type; + unsigned long iobase; + + iobase = it->options[0]; + printk("comedi%d: dt2815: 0x%04lx ", dev->minor, iobase); + if (!request_region(iobase, DT2815_SIZE, "dt2815")) { + printk("I/O port conflict\n"); + return -EIO; + } + + dev->iobase = iobase; + dev->board_name = "dt2815"; + + if (alloc_subdevices(dev, 1) < 0) + return -ENOMEM; + if (alloc_private(dev, sizeof(dt2815_private)) < 0) + return -ENOMEM; + + s = dev->subdevices; + /* ao subdevice */ + s->type = COMEDI_SUBD_AO; + s->subdev_flags = SDF_WRITABLE; + s->maxdata = 0xfff; + s->n_chan = 8; + s->insn_write = dt2815_ao_insn; + s->insn_read = dt2815_ao_insn_read; + s->range_table_list = devpriv->range_type_list; + + current_range_type = (it->options[3]) + ? &range_dt2815_ao_20_current : &range_dt2815_ao_32_current; + voltage_range_type = (it->options[2]) + ? &range_bipolar5 : &range_unipolar5; + for (i = 0; i < 8; i++) { + devpriv->range_type_list[i] = (it->options[5 + i]) + ? current_range_type : voltage_range_type; + } + + /* Init the 2815 */ + outb(0x00, dev->iobase + DT2815_STATUS); + for (i = 0; i < 100; i++) { + /* This is incredibly slow (approx 20 ms) */ + unsigned int status; + + comedi_udelay(1000); + status = inb(dev->iobase + DT2815_STATUS); + if (status == 4) { + unsigned int program; + program = (it->options[4] & 0x3) << 3 | 0x7; + outb(program, dev->iobase + DT2815_DATA); + printk(", program: 0x%x (@t=%d)\n", program, i); + break; + } else if (status != 0x00) { + printk("dt2815: unexpected status 0x%x (@t=%d)\n", + status, i); + if (status & 0x60) { + outb(0x00, dev->iobase + DT2815_STATUS); + } + } + } + + printk("\n"); + + return 0; +} + +static void dt2815_free_resources(comedi_device * dev) +{ + if (dev->iobase) + release_region(dev->iobase, DT2815_SIZE); +} + +static int dt2815_detach(comedi_device * dev) +{ + printk("comedi%d: dt2815: remove\n", dev->minor); + + dt2815_free_resources(dev); + + return 0; +}