2 comedi/drivers/dt2811.c
3 Hardware driver for Data Translation DT2811
5 COMEDI - Linux Control and Measurement Device Interface
7 Base Version - David A. Schleef <ds@schleef.org>
8 December 1998 - Updated to work. David does not have a DT2811
9 board any longer so this was suffering from bitrot.
10 Updated performed by ...
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 2 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 Description: Data Translation DT2811
30 Devices: [Data Translation] DT2811-PGL (dt2811-pgl), DT2811-PGH (dt2811-pgh)
33 Configuration options:
34 [0] - I/O port base address
35 [1] - IRQ, although this is currently unused
39 2 = pseudo-differential (common reference)
44 [4] - D/A 0 range (same choices)
45 [4] - D/A 1 range (same choices)
48 #include <linux/interrupt.h>
49 #include "../comedidev.h"
51 #include <linux/ioport.h>
53 static const char *driver_name = "dt2811";
55 static const struct comedi_lrange range_dt2811_pgh_ai_5_unipolar = { 4, {
62 static const struct comedi_lrange range_dt2811_pgh_ai_2_5_bipolar = { 4, {
66 RANGE(-0.3125, 0.3125)
69 static const struct comedi_lrange range_dt2811_pgh_ai_5_bipolar = { 4, {
76 static const struct comedi_lrange range_dt2811_pgl_ai_5_unipolar = { 4, {
83 static const struct comedi_lrange range_dt2811_pgl_ai_2_5_bipolar = { 4, {
90 static const struct comedi_lrange range_dt2811_pgl_ai_5_bipolar = { 4, {
100 0x00 ADCSR R/W A/D Control/Status Register
101 bit 7 - (R) 1 indicates A/D conversion done
102 reading ADDAT clears bit
104 bit 6 - (R) 1 indicates A/D error
106 bit 5 - (R) 1 indicates A/D busy, cleared at end
112 bit 2 - (R/W) 1 indicates interrupts enabled
113 bits 1,0 - (R/W) mode bits
114 00 single conversion on ADGCR load
115 01 continuous conversion, internal clock,
116 (clock enabled on ADGCR load)
117 10 continuous conversion, internal clock,
119 11 continuous conversion, external clock,
122 0x01 ADGCR R/W A/D Gain/Channel Register
123 bit 6,7 - (R/W) gain select
124 00 gain=1, both PGH, PGL models
125 01 gain=2 PGH, 10 PGL
126 10 gain=4 PGH, 100 PGL
127 11 gain=8 PGH, 500 PGL
129 bit 3-0 - (R/W) channel select
130 channel number from 0-15
132 0x02,0x03 (R) ADDAT A/D Data Register
133 (W) DADAT0 D/A Data Register 0
137 0x04,0x05 (W) DADAT0 D/A Data Register 1
139 0x06 (R) DIO0 Digital Input Port 0
140 (W) DIO1 Digital Output Port 1
142 0x07 TMRCTR (R/W) Timer/Counter Register
144 bits 5-3 - Timer frequency control (mantissa)
145 543 divisor freqency (kHz)
154 bits 2-0 - Timer frequency control (exponent)
155 210 multiply divisor/divide frequency by
167 #define TIMEOUT 10000
169 #define DT2811_SIZE 8
171 #define DT2811_ADCSR 0
172 #define DT2811_ADGCR 1
173 #define DT2811_ADDATLO 2
174 #define DT2811_ADDATHI 3
175 #define DT2811_DADAT0LO 2
176 #define DT2811_DADAT0HI 3
177 #define DT2811_DADAT1LO 4
178 #define DT2811_DADAT1HI 5
180 #define DT2811_TMRCTR 7
188 #define DT2811_ADDONE 0x80
189 #define DT2811_ADERROR 0x40
190 #define DT2811_ADBUSY 0x20
191 #define DT2811_CLRERROR 0x10
192 #define DT2811_INTENB 0x04
193 #define DT2811_ADMODE 0x03
195 struct dt2811_board {
198 const struct comedi_lrange *bip_5;
199 const struct comedi_lrange *bip_2_5;
200 const struct comedi_lrange *unip_5;
203 static const struct dt2811_board boardtypes[] = {
205 &range_dt2811_pgh_ai_5_bipolar,
206 &range_dt2811_pgh_ai_2_5_bipolar,
207 &range_dt2811_pgh_ai_5_unipolar,
210 &range_dt2811_pgl_ai_5_bipolar,
211 &range_dt2811_pgl_ai_2_5_bipolar,
212 &range_dt2811_pgl_ai_5_unipolar,
216 #define this_board ((const struct dt2811_board *)dev->board_ptr)
218 static int dt2811_attach(struct comedi_device *dev, struct comedi_devconfig *it);
219 static int dt2811_detach(struct comedi_device *dev);
220 static struct comedi_driver driver_dt2811 = {
221 .driver_name = "dt2811",
222 .module = THIS_MODULE,
223 .attach = dt2811_attach,
224 .detach = dt2811_detach,
225 .board_name = &boardtypes[0].name,
226 .num_names = ARRAY_SIZE(boardtypes),
227 .offset = sizeof(struct dt2811_board),
230 COMEDI_INITCLEANUP(driver_dt2811);
232 static int dt2811_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
233 struct comedi_insn *insn, unsigned int *data);
234 static int dt2811_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
235 struct comedi_insn *insn, unsigned int *data);
236 static int dt2811_ao_insn_read(struct comedi_device *dev, struct comedi_subdevice *s,
237 struct comedi_insn *insn, unsigned int *data);
238 static int dt2811_di_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s,
239 struct comedi_insn *insn, unsigned int *data);
240 static int dt2811_do_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s,
241 struct comedi_insn *insn, unsigned int *data);
243 enum { card_2811_pgh, card_2811_pgl };
245 struct dt2811_private {
249 adc_singleended, adc_diff, adc_pseudo_diff
252 dac_bipolar_5, dac_bipolar_2_5, dac_unipolar_5
254 const struct comedi_lrange *range_type_list[2];
255 unsigned int ao_readback[2];
258 #define devpriv ((struct dt2811_private *)dev->private)
260 static const struct comedi_lrange *dac_range_types[] = {
266 #define DT2811_TIMEOUT 5
269 static irqreturn_t dt2811_interrupt(int irq, void *d)
273 struct comedi_device *dev = d;
275 if (!dev->attached) {
276 comedi_error(dev, "spurious interrupt");
280 lo = inb(dev->iobase + DT2811_ADDATLO);
281 hi = inb(dev->iobase + DT2811_ADDATHI);
283 data = lo + (hi << 8);
285 if (!(--devpriv->ntrig)) {
286 /* how to turn off acquisition */
287 s->async->events |= COMEDI_SB_EOA;
289 comedi_event(dev, s);
295 options[0] Board base address
297 options[2] Input configuration
300 2 == pseudo-differential
301 options[3] Analog input range configuration
302 0 == bipolar 5 (-5V -- +5V)
303 1 == bipolar 2.5V (-2.5V -- +2.5V)
304 2 == unipolar 5V (0V -- +5V)
305 options[4] Analog output 0 range configuration
306 0 == bipolar 5 (-5V -- +5V)
307 1 == bipolar 2.5V (-2.5V -- +2.5V)
308 2 == unipolar 5V (0V -- +5V)
309 options[5] Analog output 1 range configuration
310 0 == bipolar 5 (-5V -- +5V)
311 1 == bipolar 2.5V (-2.5V -- +2.5V)
312 2 == unipolar 5V (0V -- +5V)
315 static int dt2811_attach(struct comedi_device *dev, struct comedi_devconfig *it)
318 /* unsigned long irqs; */
322 struct comedi_subdevice *s;
323 unsigned long iobase;
325 iobase = it->options[0];
327 printk("comedi%d: dt2811: base=0x%04lx\n", dev->minor, iobase);
329 if (!request_region(iobase, DT2811_SIZE, driver_name)) {
330 printk("I/O port conflict\n");
334 dev->iobase = iobase;
335 dev->board_name = this_board->name;
338 outb(0, dev->iobase + DT2811_ADCSR);
340 i = inb(dev->iobase + DT2811_ADDATLO);
341 i = inb(dev->iobase + DT2811_ADDATHI);
345 irq = it->options[1];
349 irqs = probe_irq_on();
351 outb(DT2811_CLRERROR | DT2811_INTENB,
352 dev->iobase + DT2811_ADCSR);
353 outb(0, dev->iobase + DT2811_ADGCR);
357 irq = probe_irq_off(irqs);
358 restore_flags(flags);
360 /*outb(DT2811_CLRERROR|DT2811_INTENB,dev->iobase+DT2811_ADCSR); */
362 if (inb(dev->iobase + DT2811_ADCSR) & DT2811_ADERROR) {
363 printk("error probing irq (bad) \n");
367 i = inb(dev->iobase + DT2811_ADDATLO);
368 i = inb(dev->iobase + DT2811_ADDATHI);
369 printk("(irq = %d)\n", irq);
370 ret = request_irq(irq, dt2811_interrupt, 0,
375 } else if (irq == 0) {
376 printk("(no irq)\n");
378 printk("( multiple irq's -- this is bad! )\n");
383 ret = alloc_subdevices(dev, 4);
387 ret = alloc_private(dev, sizeof(struct dt2811_private));
391 switch (it->options[2]) {
393 devpriv->adc_mux = adc_singleended;
396 devpriv->adc_mux = adc_diff;
399 devpriv->adc_mux = adc_pseudo_diff;
402 devpriv->adc_mux = adc_singleended;
405 switch (it->options[4]) {
407 devpriv->dac_range[0] = dac_bipolar_5;
410 devpriv->dac_range[0] = dac_bipolar_2_5;
413 devpriv->dac_range[0] = dac_unipolar_5;
416 devpriv->dac_range[0] = dac_bipolar_5;
419 switch (it->options[5]) {
421 devpriv->dac_range[1] = dac_bipolar_5;
424 devpriv->dac_range[1] = dac_bipolar_2_5;
427 devpriv->dac_range[1] = dac_unipolar_5;
430 devpriv->dac_range[1] = dac_bipolar_5;
434 s = dev->subdevices + 0;
435 /* initialize the ADC subdevice */
436 s->type = COMEDI_SUBD_AI;
437 s->subdev_flags = SDF_READABLE | SDF_GROUND;
438 s->n_chan = devpriv->adc_mux == adc_diff ? 8 : 16;
439 s->insn_read = dt2811_ai_insn;
441 switch (it->options[3]) {
444 s->range_table = this_board->bip_5;
447 s->range_table = this_board->bip_2_5;
450 s->range_table = this_board->unip_5;
454 s = dev->subdevices + 1;
456 s->type = COMEDI_SUBD_AO;
457 s->subdev_flags = SDF_WRITABLE;
459 s->insn_write = dt2811_ao_insn;
460 s->insn_read = dt2811_ao_insn_read;
462 s->range_table_list = devpriv->range_type_list;
463 devpriv->range_type_list[0] = dac_range_types[devpriv->dac_range[0]];
464 devpriv->range_type_list[1] = dac_range_types[devpriv->dac_range[1]];
466 s = dev->subdevices + 2;
468 s->type = COMEDI_SUBD_DI;
469 s->subdev_flags = SDF_READABLE;
471 s->insn_bits = dt2811_di_insn_bits;
473 s->range_table = &range_digital;
475 s = dev->subdevices + 3;
477 s->type = COMEDI_SUBD_DO;
478 s->subdev_flags = SDF_WRITABLE;
480 s->insn_bits = dt2811_do_insn_bits;
483 s->range_table = &range_digital;
488 static int dt2811_detach(struct comedi_device *dev)
490 printk("comedi%d: dt2811: remove\n", dev->minor);
493 free_irq(dev->irq, dev);
496 release_region(dev->iobase, DT2811_SIZE);
502 static int dt2811_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
503 struct comedi_insn *insn, unsigned int *data)
505 int chan = CR_CHAN(insn->chanspec);
506 int timeout = DT2811_TIMEOUT;
509 for (i = 0; i < insn->n; i++) {
510 outb(chan, dev->iobase + DT2811_ADGCR);
513 && inb(dev->iobase + DT2811_ADCSR) & DT2811_ADBUSY)
518 data[i] = inb(dev->iobase + DT2811_ADDATLO);
519 data[i] |= inb(dev->iobase + DT2811_ADDATHI) << 8;
527 /* Wow. This is code from the Comedi stone age. But it hasn't been
528 * replaced, so I'll let it stay. */
529 int dt2811_adtrig(kdev_t minor, comedi_adtrig *adtrig)
531 struct comedi_device *dev = comedi_devices + minor;
535 dev->curadchan = adtrig->chan;
536 switch (dev->i_admode) {
538 dev->ntrig = adtrig->n - 1;
539 /*printk("dt2811: AD soft trigger\n"); */
540 /*outb(DT2811_CLRERROR|DT2811_INTENB,dev->iobase+DT2811_ADCSR); */ /* not neccessary */
541 outb(dev->curadchan, dev->iobase + DT2811_ADGCR);
542 do_gettimeofday(&trigtime);
545 dev->ntrig = adtrig->n;
553 static int dt2811_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
554 struct comedi_insn *insn, unsigned int *data)
559 chan = CR_CHAN(insn->chanspec);
561 for (i = 0; i < insn->n; i++) {
562 outb(data[i] & 0xff, dev->iobase + DT2811_DADAT0LO + 2 * chan);
563 outb((data[i] >> 8) & 0xff,
564 dev->iobase + DT2811_DADAT0HI + 2 * chan);
565 devpriv->ao_readback[chan] = data[i];
571 static int dt2811_ao_insn_read(struct comedi_device *dev, struct comedi_subdevice *s,
572 struct comedi_insn *insn, unsigned int *data)
577 chan = CR_CHAN(insn->chanspec);
579 for (i = 0; i < insn->n; i++) {
580 data[i] = devpriv->ao_readback[chan];
586 static int dt2811_di_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s,
587 struct comedi_insn *insn, unsigned int *data)
592 data[1] = inb(dev->iobase + DT2811_DIO);
597 static int dt2811_do_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s,
598 struct comedi_insn *insn, unsigned int *data)
603 s->state &= ~data[0];
604 s->state |= data[0] & data[1];
605 outb(s->state, dev->iobase + DT2811_DIO);