diff options
Diffstat (limited to 'drivers/staging/comedi/drivers/dt2811.c')
-rw-r--r-- | drivers/staging/comedi/drivers/dt2811.c | 474 |
1 files changed, 474 insertions, 0 deletions
diff --git a/drivers/staging/comedi/drivers/dt2811.c b/drivers/staging/comedi/drivers/dt2811.c new file mode 100644 index 000000000..a80773291 --- /dev/null +++ b/drivers/staging/comedi/drivers/dt2811.c @@ -0,0 +1,474 @@ +/* + 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. + */ +/* +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 <linux/module.h> +#include "../comedidev.h" + +static const struct comedi_lrange range_dt2811_pgh_ai_5_unipolar = { + 4, { + UNI_RANGE(5), + UNI_RANGE(2.5), + UNI_RANGE(1.25), + UNI_RANGE(0.625) + } +}; + +static const struct comedi_lrange range_dt2811_pgh_ai_2_5_bipolar = { + 4, { + BIP_RANGE(2.5), + BIP_RANGE(1.25), + BIP_RANGE(0.625), + BIP_RANGE(0.3125) + } +}; + +static const struct comedi_lrange range_dt2811_pgh_ai_5_bipolar = { + 4, { + BIP_RANGE(5), + BIP_RANGE(2.5), + BIP_RANGE(1.25), + BIP_RANGE(0.625) + } +}; + +static const struct comedi_lrange range_dt2811_pgl_ai_5_unipolar = { + 4, { + UNI_RANGE(5), + UNI_RANGE(0.5), + UNI_RANGE(0.05), + UNI_RANGE(0.01) + } +}; + +static const struct comedi_lrange range_dt2811_pgl_ai_2_5_bipolar = { + 4, { + BIP_RANGE(2.5), + BIP_RANGE(0.25), + BIP_RANGE(0.025), + BIP_RANGE(0.005) + } +}; + +static const struct comedi_lrange range_dt2811_pgl_ai_5_bipolar = { + 4, { + BIP_RANGE(5), + BIP_RANGE(0.5), + BIP_RANGE(0.05), + BIP_RANGE(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_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 + +struct dt2811_board { + const char *name; + const struct comedi_lrange *bip_5; + const struct comedi_lrange *bip_2_5; + const struct comedi_lrange *unip_5; +}; + +enum { card_2811_pgh, card_2811_pgl }; + +struct dt2811_private { + 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 struct comedi_lrange *range_type_list[2]; +}; + +static const struct comedi_lrange *dac_range_types[] = { + &range_bipolar5, + &range_bipolar2_5, + &range_unipolar5 +}; + +static int dt2811_ai_eoc(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned long context) +{ + unsigned int status; + + status = inb(dev->iobase + DT2811_ADCSR); + if ((status & DT2811_ADBUSY) == 0) + return 0; + return -EBUSY; +} + +static int dt2811_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s, + struct comedi_insn *insn, unsigned int *data) +{ + int chan = CR_CHAN(insn->chanspec); + int ret; + int i; + + for (i = 0; i < insn->n; i++) { + outb(chan, dev->iobase + DT2811_ADGCR); + + ret = comedi_timeout(dev, s, insn, dt2811_ai_eoc, 0); + if (ret) + return ret; + + data[i] = inb(dev->iobase + DT2811_ADDATLO); + data[i] |= inb(dev->iobase + DT2811_ADDATHI) << 8; + data[i] &= 0xfff; + } + + return i; +} + +static int dt2811_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 val = s->readback[chan]; + int i; + + for (i = 0; i < insn->n; i++) { + val = data[i]; + outb(val & 0xff, dev->iobase + DT2811_DADAT0LO + 2 * chan); + outb((val >> 8) & 0xff, + dev->iobase + DT2811_DADAT0HI + 2 * chan); + } + s->readback[chan] = val; + + return insn->n; +} + +static int dt2811_di_insn_bits(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, unsigned int *data) +{ + data[1] = inb(dev->iobase + DT2811_DIO); + + return insn->n; +} + +static int dt2811_do_insn_bits(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data) +{ + if (comedi_dio_update_state(s, data)) + outb(s->state, dev->iobase + DT2811_DIO); + + data[1] = s->state; + + return insn->n; +} + +/* + 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(struct comedi_device *dev, struct comedi_devconfig *it) +{ + /* int i; */ + const struct dt2811_board *board = dev->board_ptr; + struct dt2811_private *devpriv; + int ret; + struct comedi_subdevice *s; + + ret = comedi_request_region(dev, it->options[0], 0x8); + if (ret) + return ret; + +#if 0 + outb(0, dev->iobase + DT2811_ADCSR); + udelay(100); + i = inb(dev->iobase + DT2811_ADDATLO); + i = inb(dev->iobase + DT2811_ADDATHI); +#endif + + ret = comedi_alloc_subdevices(dev, 4); + if (ret) + return ret; + + devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv)); + if (!devpriv) + return -ENOMEM; + + 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 = board->bip_5; + break; + case 1: + s->range_table = board->bip_2_5; + break; + case 2: + s->range_table = 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->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->insn_write = dt2811_ao_insn_write; + + ret = comedi_alloc_subdev_readback(s); + if (ret) + return ret; + + 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 const struct dt2811_board boardtypes[] = { + { + .name = "dt2811-pgh", + .bip_5 = &range_dt2811_pgh_ai_5_bipolar, + .bip_2_5 = &range_dt2811_pgh_ai_2_5_bipolar, + .unip_5 = &range_dt2811_pgh_ai_5_unipolar, + }, { + .name = "dt2811-pgl", + .bip_5 = &range_dt2811_pgl_ai_5_bipolar, + .bip_2_5 = &range_dt2811_pgl_ai_2_5_bipolar, + .unip_5 = &range_dt2811_pgl_ai_5_unipolar, + }, +}; + +static struct comedi_driver dt2811_driver = { + .driver_name = "dt2811", + .module = THIS_MODULE, + .attach = dt2811_attach, + .detach = comedi_legacy_detach, + .board_name = &boardtypes[0].name, + .num_names = ARRAY_SIZE(boardtypes), + .offset = sizeof(struct dt2811_board), +}; +module_comedi_driver(dt2811_driver); + +MODULE_AUTHOR("Comedi http://www.comedi.org"); +MODULE_DESCRIPTION("Comedi low-level driver"); +MODULE_LICENSE("GPL"); |