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.
24 Description: Data Translation DT2811
26 Devices: [Data Translation] DT2811-PGL (dt2811-pgl), DT2811-PGH (dt2811-pgh)
29 Configuration options:
30 [0] - I/O port base address
31 [1] - IRQ, although this is currently unused
35 2 = pseudo-differential (common reference)
40 [4] - D/A 0 range (same choices)
41 [4] - D/A 1 range (same choices)
44 #include <linux/module.h>
45 #include <linux/interrupt.h>
46 #include "../comedidev.h"
48 static const struct comedi_lrange range_dt2811_pgh_ai_5_unipolar = {
57 static const struct comedi_lrange range_dt2811_pgh_ai_2_5_bipolar = {
62 RANGE(-0.3125, 0.3125)
66 static const struct comedi_lrange range_dt2811_pgh_ai_5_bipolar = {
75 static const struct comedi_lrange range_dt2811_pgl_ai_5_unipolar = {
84 static const struct comedi_lrange range_dt2811_pgl_ai_2_5_bipolar = {
93 static const struct comedi_lrange range_dt2811_pgl_ai_5_bipolar = {
104 0x00 ADCSR R/W A/D Control/Status Register
105 bit 7 - (R) 1 indicates A/D conversion done
106 reading ADDAT clears bit
108 bit 6 - (R) 1 indicates A/D error
110 bit 5 - (R) 1 indicates A/D busy, cleared at end
116 bit 2 - (R/W) 1 indicates interrupts enabled
117 bits 1,0 - (R/W) mode bits
118 00 single conversion on ADGCR load
119 01 continuous conversion, internal clock,
120 (clock enabled on ADGCR load)
121 10 continuous conversion, internal clock,
123 11 continuous conversion, external clock,
126 0x01 ADGCR R/W A/D Gain/Channel Register
127 bit 6,7 - (R/W) gain select
128 00 gain=1, both PGH, PGL models
129 01 gain=2 PGH, 10 PGL
130 10 gain=4 PGH, 100 PGL
131 11 gain=8 PGH, 500 PGL
133 bit 3-0 - (R/W) channel select
134 channel number from 0-15
136 0x02,0x03 (R) ADDAT A/D Data Register
137 (W) DADAT0 D/A Data Register 0
141 0x04,0x05 (W) DADAT0 D/A Data Register 1
143 0x06 (R) DIO0 Digital Input Port 0
144 (W) DIO1 Digital Output Port 1
146 0x07 TMRCTR (R/W) Timer/Counter Register
148 bits 5-3 - Timer frequency control (mantissa)
149 543 divisor freqency (kHz)
158 bits 2-0 - Timer frequency control (exponent)
159 210 multiply divisor/divide frequency by
171 #define TIMEOUT 10000
173 #define DT2811_SIZE 8
175 #define DT2811_ADCSR 0
176 #define DT2811_ADGCR 1
177 #define DT2811_ADDATLO 2
178 #define DT2811_ADDATHI 3
179 #define DT2811_DADAT0LO 2
180 #define DT2811_DADAT0HI 3
181 #define DT2811_DADAT1LO 4
182 #define DT2811_DADAT1HI 5
184 #define DT2811_TMRCTR 7
192 #define DT2811_ADDONE 0x80
193 #define DT2811_ADERROR 0x40
194 #define DT2811_ADBUSY 0x20
195 #define DT2811_CLRERROR 0x10
196 #define DT2811_INTENB 0x04
197 #define DT2811_ADMODE 0x03
199 struct dt2811_board {
202 const struct comedi_lrange *bip_5;
203 const struct comedi_lrange *bip_2_5;
204 const struct comedi_lrange *unip_5;
207 enum { card_2811_pgh, card_2811_pgl };
209 struct dt2811_private {
213 adc_singleended, adc_diff, adc_pseudo_diff
216 dac_bipolar_5, dac_bipolar_2_5, dac_unipolar_5
218 const struct comedi_lrange *range_type_list[2];
219 unsigned int ao_readback[2];
222 static const struct comedi_lrange *dac_range_types[] = {
228 #define DT2811_TIMEOUT 5
231 static irqreturn_t dt2811_interrupt(int irq, void *d)
235 struct comedi_device *dev = d;
236 struct dt2811_private *devpriv = dev->private;
238 if (!dev->attached) {
239 comedi_error(dev, "spurious interrupt");
243 lo = inb(dev->iobase + DT2811_ADDATLO);
244 hi = inb(dev->iobase + DT2811_ADDATHI);
246 data = lo + (hi << 8);
248 if (!(--devpriv->ntrig)) {
249 /* how to turn off acquisition */
250 s->async->events |= COMEDI_SB_EOA;
252 comedi_event(dev, s);
257 static int dt2811_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
258 struct comedi_insn *insn, unsigned int *data)
260 int chan = CR_CHAN(insn->chanspec);
261 int timeout = DT2811_TIMEOUT;
264 for (i = 0; i < insn->n; i++) {
265 outb(chan, dev->iobase + DT2811_ADGCR);
268 && inb(dev->iobase + DT2811_ADCSR) & DT2811_ADBUSY)
273 data[i] = inb(dev->iobase + DT2811_ADDATLO);
274 data[i] |= inb(dev->iobase + DT2811_ADDATHI) << 8;
282 /* Wow. This is code from the Comedi stone age. But it hasn't been
283 * replaced, so I'll let it stay. */
284 int dt2811_adtrig(kdev_t minor, comedi_adtrig *adtrig)
286 struct comedi_device *dev = comedi_devices + minor;
290 dev->curadchan = adtrig->chan;
291 switch (dev->i_admode) {
293 dev->ntrig = adtrig->n - 1;
295 /*printk("dt2811: AD soft trigger\n"); */
296 /*outb(DT2811_CLRERROR|DT2811_INTENB,
297 dev->iobase+DT2811_ADCSR); */
298 outb(dev->curadchan, dev->iobase + DT2811_ADGCR);
299 do_gettimeofday(&trigtime);
302 dev->ntrig = adtrig->n;
310 static int dt2811_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
311 struct comedi_insn *insn, unsigned int *data)
313 struct dt2811_private *devpriv = dev->private;
317 chan = CR_CHAN(insn->chanspec);
319 for (i = 0; i < insn->n; i++) {
320 outb(data[i] & 0xff, dev->iobase + DT2811_DADAT0LO + 2 * chan);
321 outb((data[i] >> 8) & 0xff,
322 dev->iobase + DT2811_DADAT0HI + 2 * chan);
323 devpriv->ao_readback[chan] = data[i];
329 static int dt2811_ao_insn_read(struct comedi_device *dev,
330 struct comedi_subdevice *s,
331 struct comedi_insn *insn, unsigned int *data)
333 struct dt2811_private *devpriv = dev->private;
337 chan = CR_CHAN(insn->chanspec);
339 for (i = 0; i < insn->n; i++)
340 data[i] = devpriv->ao_readback[chan];
345 static int dt2811_di_insn_bits(struct comedi_device *dev,
346 struct comedi_subdevice *s,
347 struct comedi_insn *insn, unsigned int *data)
349 data[1] = inb(dev->iobase + DT2811_DIO);
354 static int dt2811_do_insn_bits(struct comedi_device *dev,
355 struct comedi_subdevice *s,
356 struct comedi_insn *insn, unsigned int *data)
358 s->state &= ~data[0];
359 s->state |= data[0] & data[1];
360 outb(s->state, dev->iobase + DT2811_DIO);
368 options[0] Board base address
370 options[2] Input configuration
373 2 == pseudo-differential
374 options[3] Analog input range configuration
375 0 == bipolar 5 (-5V -- +5V)
376 1 == bipolar 2.5V (-2.5V -- +2.5V)
377 2 == unipolar 5V (0V -- +5V)
378 options[4] Analog output 0 range configuration
379 0 == bipolar 5 (-5V -- +5V)
380 1 == bipolar 2.5V (-2.5V -- +2.5V)
381 2 == unipolar 5V (0V -- +5V)
382 options[5] Analog output 1 range configuration
383 0 == bipolar 5 (-5V -- +5V)
384 1 == bipolar 2.5V (-2.5V -- +2.5V)
385 2 == unipolar 5V (0V -- +5V)
387 static int dt2811_attach(struct comedi_device *dev, struct comedi_devconfig *it)
390 /* unsigned long irqs; */
393 const struct dt2811_board *board = comedi_board(dev);
394 struct dt2811_private *devpriv;
396 struct comedi_subdevice *s;
398 ret = comedi_request_region(dev, it->options[0], DT2811_SIZE);
403 outb(0, dev->iobase + DT2811_ADCSR);
405 i = inb(dev->iobase + DT2811_ADDATLO);
406 i = inb(dev->iobase + DT2811_ADDATHI);
410 irq = it->options[1];
414 irqs = probe_irq_on();
416 outb(DT2811_CLRERROR | DT2811_INTENB,
417 dev->iobase + DT2811_ADCSR);
418 outb(0, dev->iobase + DT2811_ADGCR);
422 irq = probe_irq_off(irqs);
423 restore_flags(flags);
425 /*outb(DT2811_CLRERROR|DT2811_INTENB,
426 dev->iobase+DT2811_ADCSR);*/
428 if (inb(dev->iobase + DT2811_ADCSR) & DT2811_ADERROR)
429 printk(KERN_ERR "error probing irq (bad)\n");
432 i = inb(dev->iobase + DT2811_ADDATLO);
433 i = inb(dev->iobase + DT2811_ADDATHI);
434 printk(KERN_INFO "(irq = %d)\n", irq);
435 ret = request_irq(irq, dt2811_interrupt, 0,
436 dev->board_name, dev);
440 } else if (irq == 0) {
441 printk(KERN_INFO "(no irq)\n");
443 printk(KERN_ERR "( multiple irq's -- this is bad! )\n");
448 ret = comedi_alloc_subdevices(dev, 4);
452 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
456 switch (it->options[2]) {
458 devpriv->adc_mux = adc_singleended;
461 devpriv->adc_mux = adc_diff;
464 devpriv->adc_mux = adc_pseudo_diff;
467 devpriv->adc_mux = adc_singleended;
470 switch (it->options[4]) {
472 devpriv->dac_range[0] = dac_bipolar_5;
475 devpriv->dac_range[0] = dac_bipolar_2_5;
478 devpriv->dac_range[0] = dac_unipolar_5;
481 devpriv->dac_range[0] = dac_bipolar_5;
484 switch (it->options[5]) {
486 devpriv->dac_range[1] = dac_bipolar_5;
489 devpriv->dac_range[1] = dac_bipolar_2_5;
492 devpriv->dac_range[1] = dac_unipolar_5;
495 devpriv->dac_range[1] = dac_bipolar_5;
499 s = &dev->subdevices[0];
500 /* initialize the ADC subdevice */
501 s->type = COMEDI_SUBD_AI;
502 s->subdev_flags = SDF_READABLE | SDF_GROUND;
503 s->n_chan = devpriv->adc_mux == adc_diff ? 8 : 16;
504 s->insn_read = dt2811_ai_insn;
506 switch (it->options[3]) {
509 s->range_table = board->bip_5;
512 s->range_table = board->bip_2_5;
515 s->range_table = board->unip_5;
519 s = &dev->subdevices[1];
521 s->type = COMEDI_SUBD_AO;
522 s->subdev_flags = SDF_WRITABLE;
524 s->insn_write = dt2811_ao_insn;
525 s->insn_read = dt2811_ao_insn_read;
527 s->range_table_list = devpriv->range_type_list;
528 devpriv->range_type_list[0] = dac_range_types[devpriv->dac_range[0]];
529 devpriv->range_type_list[1] = dac_range_types[devpriv->dac_range[1]];
531 s = &dev->subdevices[2];
533 s->type = COMEDI_SUBD_DI;
534 s->subdev_flags = SDF_READABLE;
536 s->insn_bits = dt2811_di_insn_bits;
538 s->range_table = &range_digital;
540 s = &dev->subdevices[3];
542 s->type = COMEDI_SUBD_DO;
543 s->subdev_flags = SDF_WRITABLE;
545 s->insn_bits = dt2811_do_insn_bits;
548 s->range_table = &range_digital;
553 static const struct dt2811_board boardtypes[] = {
555 .name = "dt2811-pgh",
556 .bip_5 = &range_dt2811_pgh_ai_5_bipolar,
557 .bip_2_5 = &range_dt2811_pgh_ai_2_5_bipolar,
558 .unip_5 = &range_dt2811_pgh_ai_5_unipolar,
560 .name = "dt2811-pgl",
561 .bip_5 = &range_dt2811_pgl_ai_5_bipolar,
562 .bip_2_5 = &range_dt2811_pgl_ai_2_5_bipolar,
563 .unip_5 = &range_dt2811_pgl_ai_5_unipolar,
567 static struct comedi_driver dt2811_driver = {
568 .driver_name = "dt2811",
569 .module = THIS_MODULE,
570 .attach = dt2811_attach,
571 .detach = comedi_legacy_detach,
572 .board_name = &boardtypes[0].name,
573 .num_names = ARRAY_SIZE(boardtypes),
574 .offset = sizeof(struct dt2811_board),
576 module_comedi_driver(dt2811_driver);
578 MODULE_AUTHOR("Comedi http://www.comedi.org");
579 MODULE_DESCRIPTION("Comedi low-level driver");
580 MODULE_LICENSE("GPL");