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 "../comedidev.h"
50 #include <linux/ioport.h>
52 static const char *driver_name = "dt2811";
54 static const comedi_lrange range_dt2811_pgh_ai_5_unipolar = { 4, {
61 static const comedi_lrange range_dt2811_pgh_ai_2_5_bipolar = { 4, {
65 RANGE(-0.3125, 0.3125)
68 static const comedi_lrange range_dt2811_pgh_ai_5_bipolar = { 4, {
75 static const comedi_lrange range_dt2811_pgl_ai_5_unipolar = { 4, {
82 static const comedi_lrange range_dt2811_pgl_ai_2_5_bipolar = { 4, {
89 static const comedi_lrange range_dt2811_pgl_ai_5_bipolar = { 4, {
99 0x00 ADCSR R/W A/D Control/Status Register
100 bit 7 - (R) 1 indicates A/D conversion done
101 reading ADDAT clears bit
103 bit 6 - (R) 1 indicates A/D error
105 bit 5 - (R) 1 indicates A/D busy, cleared at end
111 bit 2 - (R/W) 1 indicates interrupts enabled
112 bits 1,0 - (R/W) mode bits
113 00 single conversion on ADGCR load
114 01 continuous conversion, internal clock,
115 (clock enabled on ADGCR load)
116 10 continuous conversion, internal clock,
118 11 continuous conversion, external clock,
121 0x01 ADGCR R/W A/D Gain/Channel Register
122 bit 6,7 - (R/W) gain select
123 00 gain=1, both PGH, PGL models
124 01 gain=2 PGH, 10 PGL
125 10 gain=4 PGH, 100 PGL
126 11 gain=8 PGH, 500 PGL
128 bit 3-0 - (R/W) channel select
129 channel number from 0-15
131 0x02,0x03 (R) ADDAT A/D Data Register
132 (W) DADAT0 D/A Data Register 0
136 0x04,0x05 (W) DADAT0 D/A Data Register 1
138 0x06 (R) DIO0 Digital Input Port 0
139 (W) DIO1 Digital Output Port 1
141 0x07 TMRCTR (R/W) Timer/Counter Register
143 bits 5-3 - Timer frequency control (mantissa)
144 543 divisor freqency (kHz)
153 bits 2-0 - Timer frequency control (exponent)
154 210 multiply divisor/divide frequency by
166 #define TIMEOUT 10000
168 #define DT2811_SIZE 8
170 #define DT2811_ADCSR 0
171 #define DT2811_ADGCR 1
172 #define DT2811_ADDATLO 2
173 #define DT2811_ADDATHI 3
174 #define DT2811_DADAT0LO 2
175 #define DT2811_DADAT0HI 3
176 #define DT2811_DADAT1LO 4
177 #define DT2811_DADAT1HI 5
179 #define DT2811_TMRCTR 7
187 #define DT2811_ADDONE 0x80
188 #define DT2811_ADERROR 0x40
189 #define DT2811_ADBUSY 0x20
190 #define DT2811_CLRERROR 0x10
191 #define DT2811_INTENB 0x04
192 #define DT2811_ADMODE 0x03
196 const comedi_lrange *bip_5;
197 const comedi_lrange *bip_2_5;
198 const comedi_lrange *unip_5;
200 static const boardtype boardtypes[] = {
202 &range_dt2811_pgh_ai_5_bipolar,
203 &range_dt2811_pgh_ai_2_5_bipolar,
204 &range_dt2811_pgh_ai_5_unipolar,
207 &range_dt2811_pgl_ai_5_bipolar,
208 &range_dt2811_pgl_ai_2_5_bipolar,
209 &range_dt2811_pgl_ai_5_unipolar,
213 #define this_board ((const boardtype *)dev->board_ptr)
215 static int dt2811_attach(comedi_device * dev, comedi_devconfig * it);
216 static int dt2811_detach(comedi_device * dev);
217 static comedi_driver driver_dt2811 = {
218 driver_name:"dt2811",
220 attach:dt2811_attach,
221 detach:dt2811_detach,
222 board_name:&boardtypes[0].name,
223 num_names:sizeof(boardtypes) / sizeof(boardtype),
224 offset:sizeof(boardtype),
227 COMEDI_INITCLEANUP(driver_dt2811);
229 static int dt2811_ai_insn(comedi_device * dev, comedi_subdevice * s,
230 comedi_insn * insn, lsampl_t * data);
231 static int dt2811_ao_insn(comedi_device * dev, comedi_subdevice * s,
232 comedi_insn * insn, lsampl_t * data);
233 static int dt2811_ao_insn_read(comedi_device * dev, comedi_subdevice * s,
234 comedi_insn * insn, lsampl_t * data);
235 static int dt2811_di_insn_bits(comedi_device * dev, comedi_subdevice * s,
236 comedi_insn * insn, lsampl_t * data);
237 static int dt2811_do_insn_bits(comedi_device * dev, comedi_subdevice * s,
238 comedi_insn * insn, lsampl_t * data);
240 enum { card_2811_pgh, card_2811_pgl };
245 adc_singleended, adc_diff, adc_pseudo_diff
248 dac_bipolar_5, dac_bipolar_2_5, dac_unipolar_5
250 const comedi_lrange *range_type_list[2];
251 lsampl_t ao_readback[2];
254 #define devpriv ((dt2811_private *)dev->private)
256 static const comedi_lrange *dac_range_types[] = {
262 #define DT2811_TIMEOUT 5
265 static irqreturn_t dt2811_interrupt(int irq, void *d PT_REGS_ARG)
269 comedi_device *dev = d;
271 if (!dev->attached) {
272 comedi_error(dev, "spurious interrupt");
276 lo = inb(dev->iobase + DT2811_ADDATLO);
277 hi = inb(dev->iobase + DT2811_ADDATHI);
279 data = lo + (hi << 8);
281 if (!(--devpriv->ntrig)) {
282 /* how to turn off acquisition */
283 s->async->events |= COMEDI_SB_EOA;
285 comedi_event(dev, s);
291 options[0] Board base address
293 options[2] Input configuration
296 2 == pseudo-differential
297 options[3] Analog input range configuration
298 0 == bipolar 5 (-5V -- +5V)
299 1 == bipolar 2.5V (-2.5V -- +2.5V)
300 2 == unipolar 5V (0V -- +5V)
301 options[4] Analog output 0 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[5] Analog output 1 range configuration
306 0 == bipolar 5 (-5V -- +5V)
307 1 == bipolar 2.5V (-2.5V -- +2.5V)
308 2 == unipolar 5V (0V -- +5V)
311 static int dt2811_attach(comedi_device * dev, comedi_devconfig * it)
314 //unsigned long irqs;
318 unsigned long iobase;
320 iobase = it->options[0];
322 printk("comedi%d: dt2811: base=0x%04lx\n", dev->minor, iobase);
324 if (!request_region(iobase, DT2811_SIZE, driver_name)) {
325 printk("I/O port conflict\n");
329 dev->iobase = iobase;
330 dev->board_name = this_board->name;
333 outb(0, dev->iobase + DT2811_ADCSR);
335 i = inb(dev->iobase + DT2811_ADDATLO);
336 i = inb(dev->iobase + DT2811_ADDATHI);
340 irq = it->options[1];
344 irqs = probe_irq_on();
346 outb(DT2811_CLRERROR | DT2811_INTENB,
347 dev->iobase + DT2811_ADCSR);
348 outb(0, dev->iobase + DT2811_ADGCR);
352 irq = probe_irq_off(irqs);
353 restore_flags(flags);
355 /*outb(DT2811_CLRERROR|DT2811_INTENB,dev->iobase+DT2811_ADCSR); */
357 if (inb(dev->iobase + DT2811_ADCSR) & DT2811_ADERROR) {
358 printk("error probing irq (bad) \n");
362 i = inb(dev->iobase + DT2811_ADDATLO);
363 i = inb(dev->iobase + DT2811_ADDATHI);
364 printk("(irq = %d)\n", irq);
365 ret = comedi_request_irq(irq, dt2811_interrupt, 0,
370 } else if (irq == 0) {
371 printk("(no irq)\n");
373 printk("( multiple irq's -- this is bad! )\n");
378 if ((ret = alloc_subdevices(dev, 4)) < 0)
380 if ((ret = alloc_private(dev, sizeof(dt2811_private))) < 0)
382 switch (it->options[2]) {
384 devpriv->adc_mux = adc_singleended;
387 devpriv->adc_mux = adc_diff;
390 devpriv->adc_mux = adc_pseudo_diff;
393 devpriv->adc_mux = adc_singleended;
396 switch (it->options[4]) {
398 devpriv->dac_range[0] = dac_bipolar_5;
401 devpriv->dac_range[0] = dac_bipolar_2_5;
404 devpriv->dac_range[0] = dac_unipolar_5;
407 devpriv->dac_range[0] = dac_bipolar_5;
410 switch (it->options[5]) {
412 devpriv->dac_range[1] = dac_bipolar_5;
415 devpriv->dac_range[1] = dac_bipolar_2_5;
418 devpriv->dac_range[1] = dac_unipolar_5;
421 devpriv->dac_range[1] = dac_bipolar_5;
425 s = dev->subdevices + 0;
426 /* initialize the ADC subdevice */
427 s->type = COMEDI_SUBD_AI;
428 s->subdev_flags = SDF_READABLE | SDF_GROUND;
429 s->n_chan = devpriv->adc_mux == adc_diff ? 8 : 16;
430 s->insn_read = dt2811_ai_insn;
432 switch (it->options[3]) {
435 s->range_table = this_board->bip_5;
438 s->range_table = this_board->bip_2_5;
441 s->range_table = this_board->unip_5;
445 s = dev->subdevices + 1;
447 s->type = COMEDI_SUBD_AO;
448 s->subdev_flags = SDF_WRITABLE;
450 s->insn_write = dt2811_ao_insn;
451 s->insn_read = dt2811_ao_insn_read;
453 s->range_table_list = devpriv->range_type_list;
454 devpriv->range_type_list[0] = dac_range_types[devpriv->dac_range[0]];
455 devpriv->range_type_list[1] = dac_range_types[devpriv->dac_range[1]];
457 s = dev->subdevices + 2;
459 s->type = COMEDI_SUBD_DI;
460 s->subdev_flags = SDF_READABLE;
462 s->insn_bits = dt2811_di_insn_bits;
464 s->range_table = &range_digital;
466 s = dev->subdevices + 3;
468 s->type = COMEDI_SUBD_DO;
469 s->subdev_flags = SDF_WRITABLE;
471 s->insn_bits = dt2811_do_insn_bits;
474 s->range_table = &range_digital;
479 static int dt2811_detach(comedi_device * dev)
481 printk("comedi%d: dt2811: remove\n", dev->minor);
484 comedi_free_irq(dev->irq, dev);
487 release_region(dev->iobase, DT2811_SIZE);
493 static int dt2811_ai_insn(comedi_device * dev, comedi_subdevice * s,
494 comedi_insn * insn, lsampl_t * data)
496 int chan = CR_CHAN(insn->chanspec);
497 int timeout = DT2811_TIMEOUT;
500 for (i = 0; i < insn->n; i++) {
501 outb(chan, dev->iobase + DT2811_ADGCR);
504 && inb(dev->iobase + DT2811_ADCSR) & DT2811_ADBUSY)
509 data[i] = inb(dev->iobase + DT2811_ADDATLO);
510 data[i] |= inb(dev->iobase + DT2811_ADDATHI) << 8;
518 /* Wow. This is code from the Comedi stone age. But it hasn't been
519 * replaced, so I'll let it stay. */
520 int dt2811_adtrig(kdev_t minor, comedi_adtrig * adtrig)
522 comedi_device *dev = comedi_devices + minor;
526 dev->curadchan = adtrig->chan;
527 switch (dev->i_admode) {
529 dev->ntrig = adtrig->n - 1;
530 /*printk("dt2811: AD soft trigger\n"); */
531 /*outb(DT2811_CLRERROR|DT2811_INTENB,dev->iobase+DT2811_ADCSR); *//* not neccessary */
532 outb(dev->curadchan, dev->iobase + DT2811_ADGCR);
533 do_gettimeofday(&trigtime);
536 dev->ntrig = adtrig->n;
544 static int dt2811_ao_insn(comedi_device * dev, comedi_subdevice * s,
545 comedi_insn * insn, lsampl_t * data)
550 chan = CR_CHAN(insn->chanspec);
552 for (i = 0; i < insn->n; i++) {
553 outb(data[i] & 0xff, dev->iobase + DT2811_DADAT0LO + 2 * chan);
554 outb((data[i] >> 8) & 0xff,
555 dev->iobase + DT2811_DADAT0HI + 2 * chan);
556 devpriv->ao_readback[chan] = data[i];
562 static int dt2811_ao_insn_read(comedi_device * dev, comedi_subdevice * s,
563 comedi_insn * insn, lsampl_t * data)
568 chan = CR_CHAN(insn->chanspec);
570 for (i = 0; i < insn->n; i++) {
571 data[i] = devpriv->ao_readback[chan];
577 static int dt2811_di_insn_bits(comedi_device * dev, comedi_subdevice * s,
578 comedi_insn * insn, lsampl_t * data)
583 data[1] = inb(dev->iobase + DT2811_DIO);
588 static int dt2811_do_insn_bits(comedi_device * dev, comedi_subdevice * s,
589 comedi_insn * insn, lsampl_t * data)
594 s->state &= ~data[0];
595 s->state |= data[0] & data[1];
596 outb(s->state, dev->iobase + DT2811_DIO);