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/interrupt.h>
45 #include "../comedidev.h"
47 #include <linux/ioport.h>
49 static const struct comedi_lrange range_dt2811_pgh_ai_5_unipolar = {
58 static const struct comedi_lrange range_dt2811_pgh_ai_2_5_bipolar = {
63 RANGE(-0.3125, 0.3125)
67 static const struct comedi_lrange range_dt2811_pgh_ai_5_bipolar = {
76 static const struct comedi_lrange range_dt2811_pgl_ai_5_unipolar = {
85 static const struct comedi_lrange range_dt2811_pgl_ai_2_5_bipolar = {
94 static const struct comedi_lrange range_dt2811_pgl_ai_5_bipolar = {
105 0x00 ADCSR R/W A/D Control/Status Register
106 bit 7 - (R) 1 indicates A/D conversion done
107 reading ADDAT clears bit
109 bit 6 - (R) 1 indicates A/D error
111 bit 5 - (R) 1 indicates A/D busy, cleared at end
117 bit 2 - (R/W) 1 indicates interrupts enabled
118 bits 1,0 - (R/W) mode bits
119 00 single conversion on ADGCR load
120 01 continuous conversion, internal clock,
121 (clock enabled on ADGCR load)
122 10 continuous conversion, internal clock,
124 11 continuous conversion, external clock,
127 0x01 ADGCR R/W A/D Gain/Channel Register
128 bit 6,7 - (R/W) gain select
129 00 gain=1, both PGH, PGL models
130 01 gain=2 PGH, 10 PGL
131 10 gain=4 PGH, 100 PGL
132 11 gain=8 PGH, 500 PGL
134 bit 3-0 - (R/W) channel select
135 channel number from 0-15
137 0x02,0x03 (R) ADDAT A/D Data Register
138 (W) DADAT0 D/A Data Register 0
142 0x04,0x05 (W) DADAT0 D/A Data Register 1
144 0x06 (R) DIO0 Digital Input Port 0
145 (W) DIO1 Digital Output Port 1
147 0x07 TMRCTR (R/W) Timer/Counter Register
149 bits 5-3 - Timer frequency control (mantissa)
150 543 divisor freqency (kHz)
159 bits 2-0 - Timer frequency control (exponent)
160 210 multiply divisor/divide frequency by
172 #define TIMEOUT 10000
174 #define DT2811_SIZE 8
176 #define DT2811_ADCSR 0
177 #define DT2811_ADGCR 1
178 #define DT2811_ADDATLO 2
179 #define DT2811_ADDATHI 3
180 #define DT2811_DADAT0LO 2
181 #define DT2811_DADAT0HI 3
182 #define DT2811_DADAT1LO 4
183 #define DT2811_DADAT1HI 5
185 #define DT2811_TMRCTR 7
193 #define DT2811_ADDONE 0x80
194 #define DT2811_ADERROR 0x40
195 #define DT2811_ADBUSY 0x20
196 #define DT2811_CLRERROR 0x10
197 #define DT2811_INTENB 0x04
198 #define DT2811_ADMODE 0x03
200 struct dt2811_board {
203 const struct comedi_lrange *bip_5;
204 const struct comedi_lrange *bip_2_5;
205 const struct comedi_lrange *unip_5;
208 enum { card_2811_pgh, card_2811_pgl };
210 struct dt2811_private {
214 adc_singleended, adc_diff, adc_pseudo_diff
217 dac_bipolar_5, dac_bipolar_2_5, dac_unipolar_5
219 const struct comedi_lrange *range_type_list[2];
220 unsigned int ao_readback[2];
223 static const struct comedi_lrange *dac_range_types[] = {
229 #define DT2811_TIMEOUT 5
232 static irqreturn_t dt2811_interrupt(int irq, void *d)
236 struct comedi_device *dev = d;
237 struct dt2811_private *devpriv = dev->private;
239 if (!dev->attached) {
240 comedi_error(dev, "spurious interrupt");
244 lo = inb(dev->iobase + DT2811_ADDATLO);
245 hi = inb(dev->iobase + DT2811_ADDATHI);
247 data = lo + (hi << 8);
249 if (!(--devpriv->ntrig)) {
250 /* how to turn off acquisition */
251 s->async->events |= COMEDI_SB_EOA;
253 comedi_event(dev, s);
258 static int dt2811_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
259 struct comedi_insn *insn, unsigned int *data)
261 int chan = CR_CHAN(insn->chanspec);
262 int timeout = DT2811_TIMEOUT;
265 for (i = 0; i < insn->n; i++) {
266 outb(chan, dev->iobase + DT2811_ADGCR);
269 && inb(dev->iobase + DT2811_ADCSR) & DT2811_ADBUSY)
274 data[i] = inb(dev->iobase + DT2811_ADDATLO);
275 data[i] |= inb(dev->iobase + DT2811_ADDATHI) << 8;
283 /* Wow. This is code from the Comedi stone age. But it hasn't been
284 * replaced, so I'll let it stay. */
285 int dt2811_adtrig(kdev_t minor, comedi_adtrig *adtrig)
287 struct comedi_device *dev = comedi_devices + minor;
291 dev->curadchan = adtrig->chan;
292 switch (dev->i_admode) {
294 dev->ntrig = adtrig->n - 1;
296 /*printk("dt2811: AD soft trigger\n"); */
297 /*outb(DT2811_CLRERROR|DT2811_INTENB,
298 dev->iobase+DT2811_ADCSR); */
299 outb(dev->curadchan, dev->iobase + DT2811_ADGCR);
300 do_gettimeofday(&trigtime);
303 dev->ntrig = adtrig->n;
311 static int dt2811_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
312 struct comedi_insn *insn, unsigned int *data)
314 struct dt2811_private *devpriv = dev->private;
318 chan = CR_CHAN(insn->chanspec);
320 for (i = 0; i < insn->n; i++) {
321 outb(data[i] & 0xff, dev->iobase + DT2811_DADAT0LO + 2 * chan);
322 outb((data[i] >> 8) & 0xff,
323 dev->iobase + DT2811_DADAT0HI + 2 * chan);
324 devpriv->ao_readback[chan] = data[i];
330 static int dt2811_ao_insn_read(struct comedi_device *dev,
331 struct comedi_subdevice *s,
332 struct comedi_insn *insn, unsigned int *data)
334 struct dt2811_private *devpriv = dev->private;
338 chan = CR_CHAN(insn->chanspec);
340 for (i = 0; i < insn->n; i++)
341 data[i] = devpriv->ao_readback[chan];
346 static int dt2811_di_insn_bits(struct comedi_device *dev,
347 struct comedi_subdevice *s,
348 struct comedi_insn *insn, unsigned int *data)
350 data[1] = inb(dev->iobase + DT2811_DIO);
355 static int dt2811_do_insn_bits(struct comedi_device *dev,
356 struct comedi_subdevice *s,
357 struct comedi_insn *insn, unsigned int *data)
359 s->state &= ~data[0];
360 s->state |= data[0] & data[1];
361 outb(s->state, dev->iobase + DT2811_DIO);
369 options[0] Board base address
371 options[2] Input configuration
374 2 == pseudo-differential
375 options[3] Analog input range configuration
376 0 == bipolar 5 (-5V -- +5V)
377 1 == bipolar 2.5V (-2.5V -- +2.5V)
378 2 == unipolar 5V (0V -- +5V)
379 options[4] Analog output 0 range configuration
380 0 == bipolar 5 (-5V -- +5V)
381 1 == bipolar 2.5V (-2.5V -- +2.5V)
382 2 == unipolar 5V (0V -- +5V)
383 options[5] Analog output 1 range configuration
384 0 == bipolar 5 (-5V -- +5V)
385 1 == bipolar 2.5V (-2.5V -- +2.5V)
386 2 == unipolar 5V (0V -- +5V)
388 static int dt2811_attach(struct comedi_device *dev, struct comedi_devconfig *it)
391 /* unsigned long irqs; */
394 const struct dt2811_board *board = comedi_board(dev);
395 struct dt2811_private *devpriv;
397 struct comedi_subdevice *s;
399 ret = comedi_request_region(dev, it->options[0], DT2811_SIZE);
404 outb(0, dev->iobase + DT2811_ADCSR);
406 i = inb(dev->iobase + DT2811_ADDATLO);
407 i = inb(dev->iobase + DT2811_ADDATHI);
411 irq = it->options[1];
415 irqs = probe_irq_on();
417 outb(DT2811_CLRERROR | DT2811_INTENB,
418 dev->iobase + DT2811_ADCSR);
419 outb(0, dev->iobase + DT2811_ADGCR);
423 irq = probe_irq_off(irqs);
424 restore_flags(flags);
426 /*outb(DT2811_CLRERROR|DT2811_INTENB,
427 dev->iobase+DT2811_ADCSR);*/
429 if (inb(dev->iobase + DT2811_ADCSR) & DT2811_ADERROR)
430 printk(KERN_ERR "error probing irq (bad)\n");
433 i = inb(dev->iobase + DT2811_ADDATLO);
434 i = inb(dev->iobase + DT2811_ADDATHI);
435 printk(KERN_INFO "(irq = %d)\n", irq);
436 ret = request_irq(irq, dt2811_interrupt, 0,
437 dev->board_name, dev);
441 } else if (irq == 0) {
442 printk(KERN_INFO "(no irq)\n");
444 printk(KERN_ERR "( multiple irq's -- this is bad! )\n");
449 ret = comedi_alloc_subdevices(dev, 4);
453 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
456 dev->private = devpriv;
458 switch (it->options[2]) {
460 devpriv->adc_mux = adc_singleended;
463 devpriv->adc_mux = adc_diff;
466 devpriv->adc_mux = adc_pseudo_diff;
469 devpriv->adc_mux = adc_singleended;
472 switch (it->options[4]) {
474 devpriv->dac_range[0] = dac_bipolar_5;
477 devpriv->dac_range[0] = dac_bipolar_2_5;
480 devpriv->dac_range[0] = dac_unipolar_5;
483 devpriv->dac_range[0] = dac_bipolar_5;
486 switch (it->options[5]) {
488 devpriv->dac_range[1] = dac_bipolar_5;
491 devpriv->dac_range[1] = dac_bipolar_2_5;
494 devpriv->dac_range[1] = dac_unipolar_5;
497 devpriv->dac_range[1] = dac_bipolar_5;
501 s = &dev->subdevices[0];
502 /* initialize the ADC subdevice */
503 s->type = COMEDI_SUBD_AI;
504 s->subdev_flags = SDF_READABLE | SDF_GROUND;
505 s->n_chan = devpriv->adc_mux == adc_diff ? 8 : 16;
506 s->insn_read = dt2811_ai_insn;
508 switch (it->options[3]) {
511 s->range_table = board->bip_5;
514 s->range_table = board->bip_2_5;
517 s->range_table = board->unip_5;
521 s = &dev->subdevices[1];
523 s->type = COMEDI_SUBD_AO;
524 s->subdev_flags = SDF_WRITABLE;
526 s->insn_write = dt2811_ao_insn;
527 s->insn_read = dt2811_ao_insn_read;
529 s->range_table_list = devpriv->range_type_list;
530 devpriv->range_type_list[0] = dac_range_types[devpriv->dac_range[0]];
531 devpriv->range_type_list[1] = dac_range_types[devpriv->dac_range[1]];
533 s = &dev->subdevices[2];
535 s->type = COMEDI_SUBD_DI;
536 s->subdev_flags = SDF_READABLE;
538 s->insn_bits = dt2811_di_insn_bits;
540 s->range_table = &range_digital;
542 s = &dev->subdevices[3];
544 s->type = COMEDI_SUBD_DO;
545 s->subdev_flags = SDF_WRITABLE;
547 s->insn_bits = dt2811_do_insn_bits;
550 s->range_table = &range_digital;
555 static const struct dt2811_board boardtypes[] = {
557 .name = "dt2811-pgh",
558 .bip_5 = &range_dt2811_pgh_ai_5_bipolar,
559 .bip_2_5 = &range_dt2811_pgh_ai_2_5_bipolar,
560 .unip_5 = &range_dt2811_pgh_ai_5_unipolar,
562 .name = "dt2811-pgl",
563 .bip_5 = &range_dt2811_pgl_ai_5_bipolar,
564 .bip_2_5 = &range_dt2811_pgl_ai_2_5_bipolar,
565 .unip_5 = &range_dt2811_pgl_ai_5_unipolar,
569 static struct comedi_driver dt2811_driver = {
570 .driver_name = "dt2811",
571 .module = THIS_MODULE,
572 .attach = dt2811_attach,
573 .detach = comedi_legacy_detach,
574 .board_name = &boardtypes[0].name,
575 .num_names = ARRAY_SIZE(boardtypes),
576 .offset = sizeof(struct dt2811_board),
578 module_comedi_driver(dt2811_driver);
580 MODULE_AUTHOR("Comedi http://www.comedi.org");
581 MODULE_DESCRIPTION("Comedi low-level driver");
582 MODULE_LICENSE("GPL");