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 struct comedi_lrange range_dt2811_pgh_ai_5_unipolar = {
62 static const struct comedi_lrange range_dt2811_pgh_ai_2_5_bipolar = {
67 RANGE(-0.3125, 0.3125)
71 static const struct comedi_lrange range_dt2811_pgh_ai_5_bipolar = {
80 static const struct comedi_lrange range_dt2811_pgl_ai_5_unipolar = {
89 static const struct comedi_lrange range_dt2811_pgl_ai_2_5_bipolar = {
98 static const struct comedi_lrange range_dt2811_pgl_ai_5_bipolar = {
109 0x00 ADCSR R/W A/D Control/Status Register
110 bit 7 - (R) 1 indicates A/D conversion done
111 reading ADDAT clears bit
113 bit 6 - (R) 1 indicates A/D error
115 bit 5 - (R) 1 indicates A/D busy, cleared at end
121 bit 2 - (R/W) 1 indicates interrupts enabled
122 bits 1,0 - (R/W) mode bits
123 00 single conversion on ADGCR load
124 01 continuous conversion, internal clock,
125 (clock enabled on ADGCR load)
126 10 continuous conversion, internal clock,
128 11 continuous conversion, external clock,
131 0x01 ADGCR R/W A/D Gain/Channel Register
132 bit 6,7 - (R/W) gain select
133 00 gain=1, both PGH, PGL models
134 01 gain=2 PGH, 10 PGL
135 10 gain=4 PGH, 100 PGL
136 11 gain=8 PGH, 500 PGL
138 bit 3-0 - (R/W) channel select
139 channel number from 0-15
141 0x02,0x03 (R) ADDAT A/D Data Register
142 (W) DADAT0 D/A Data Register 0
146 0x04,0x05 (W) DADAT0 D/A Data Register 1
148 0x06 (R) DIO0 Digital Input Port 0
149 (W) DIO1 Digital Output Port 1
151 0x07 TMRCTR (R/W) Timer/Counter Register
153 bits 5-3 - Timer frequency control (mantissa)
154 543 divisor freqency (kHz)
163 bits 2-0 - Timer frequency control (exponent)
164 210 multiply divisor/divide frequency by
176 #define TIMEOUT 10000
178 #define DT2811_SIZE 8
180 #define DT2811_ADCSR 0
181 #define DT2811_ADGCR 1
182 #define DT2811_ADDATLO 2
183 #define DT2811_ADDATHI 3
184 #define DT2811_DADAT0LO 2
185 #define DT2811_DADAT0HI 3
186 #define DT2811_DADAT1LO 4
187 #define DT2811_DADAT1HI 5
189 #define DT2811_TMRCTR 7
197 #define DT2811_ADDONE 0x80
198 #define DT2811_ADERROR 0x40
199 #define DT2811_ADBUSY 0x20
200 #define DT2811_CLRERROR 0x10
201 #define DT2811_INTENB 0x04
202 #define DT2811_ADMODE 0x03
204 struct dt2811_board {
207 const struct comedi_lrange *bip_5;
208 const struct comedi_lrange *bip_2_5;
209 const struct comedi_lrange *unip_5;
212 enum { card_2811_pgh, card_2811_pgl };
214 struct dt2811_private {
218 adc_singleended, adc_diff, adc_pseudo_diff
221 dac_bipolar_5, dac_bipolar_2_5, dac_unipolar_5
223 const struct comedi_lrange *range_type_list[2];
224 unsigned int ao_readback[2];
227 static const struct comedi_lrange *dac_range_types[] = {
233 #define DT2811_TIMEOUT 5
236 static irqreturn_t dt2811_interrupt(int irq, void *d)
240 struct comedi_device *dev = d;
241 struct dt2811_private *devpriv = dev->private;
243 if (!dev->attached) {
244 comedi_error(dev, "spurious interrupt");
248 lo = inb(dev->iobase + DT2811_ADDATLO);
249 hi = inb(dev->iobase + DT2811_ADDATHI);
251 data = lo + (hi << 8);
253 if (!(--devpriv->ntrig)) {
254 /* how to turn off acquisition */
255 s->async->events |= COMEDI_SB_EOA;
257 comedi_event(dev, s);
262 static int dt2811_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
263 struct comedi_insn *insn, unsigned int *data)
265 int chan = CR_CHAN(insn->chanspec);
266 int timeout = DT2811_TIMEOUT;
269 for (i = 0; i < insn->n; i++) {
270 outb(chan, dev->iobase + DT2811_ADGCR);
273 && inb(dev->iobase + DT2811_ADCSR) & DT2811_ADBUSY)
278 data[i] = inb(dev->iobase + DT2811_ADDATLO);
279 data[i] |= inb(dev->iobase + DT2811_ADDATHI) << 8;
287 /* Wow. This is code from the Comedi stone age. But it hasn't been
288 * replaced, so I'll let it stay. */
289 int dt2811_adtrig(kdev_t minor, comedi_adtrig *adtrig)
291 struct comedi_device *dev = comedi_devices + minor;
295 dev->curadchan = adtrig->chan;
296 switch (dev->i_admode) {
298 dev->ntrig = adtrig->n - 1;
300 /*printk("dt2811: AD soft trigger\n"); */
301 /*outb(DT2811_CLRERROR|DT2811_INTENB,
302 dev->iobase+DT2811_ADCSR); */
303 outb(dev->curadchan, dev->iobase + DT2811_ADGCR);
304 do_gettimeofday(&trigtime);
307 dev->ntrig = adtrig->n;
315 static int dt2811_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
316 struct comedi_insn *insn, unsigned int *data)
318 struct dt2811_private *devpriv = dev->private;
322 chan = CR_CHAN(insn->chanspec);
324 for (i = 0; i < insn->n; i++) {
325 outb(data[i] & 0xff, dev->iobase + DT2811_DADAT0LO + 2 * chan);
326 outb((data[i] >> 8) & 0xff,
327 dev->iobase + DT2811_DADAT0HI + 2 * chan);
328 devpriv->ao_readback[chan] = data[i];
334 static int dt2811_ao_insn_read(struct comedi_device *dev,
335 struct comedi_subdevice *s,
336 struct comedi_insn *insn, unsigned int *data)
338 struct dt2811_private *devpriv = dev->private;
342 chan = CR_CHAN(insn->chanspec);
344 for (i = 0; i < insn->n; i++)
345 data[i] = devpriv->ao_readback[chan];
350 static int dt2811_di_insn_bits(struct comedi_device *dev,
351 struct comedi_subdevice *s,
352 struct comedi_insn *insn, unsigned int *data)
354 data[1] = inb(dev->iobase + DT2811_DIO);
359 static int dt2811_do_insn_bits(struct comedi_device *dev,
360 struct comedi_subdevice *s,
361 struct comedi_insn *insn, unsigned int *data)
363 s->state &= ~data[0];
364 s->state |= data[0] & data[1];
365 outb(s->state, dev->iobase + DT2811_DIO);
373 options[0] Board base address
375 options[2] Input configuration
378 2 == pseudo-differential
379 options[3] Analog input 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[4] Analog output 0 range configuration
384 0 == bipolar 5 (-5V -- +5V)
385 1 == bipolar 2.5V (-2.5V -- +2.5V)
386 2 == unipolar 5V (0V -- +5V)
387 options[5] Analog output 1 range configuration
388 0 == bipolar 5 (-5V -- +5V)
389 1 == bipolar 2.5V (-2.5V -- +2.5V)
390 2 == unipolar 5V (0V -- +5V)
392 static int dt2811_attach(struct comedi_device *dev, struct comedi_devconfig *it)
395 /* unsigned long irqs; */
398 const struct dt2811_board *board = comedi_board(dev);
399 struct dt2811_private *devpriv;
401 struct comedi_subdevice *s;
403 ret = comedi_request_region(dev, it->options[0], DT2811_SIZE);
408 outb(0, dev->iobase + DT2811_ADCSR);
410 i = inb(dev->iobase + DT2811_ADDATLO);
411 i = inb(dev->iobase + DT2811_ADDATHI);
415 irq = it->options[1];
419 irqs = probe_irq_on();
421 outb(DT2811_CLRERROR | DT2811_INTENB,
422 dev->iobase + DT2811_ADCSR);
423 outb(0, dev->iobase + DT2811_ADGCR);
427 irq = probe_irq_off(irqs);
428 restore_flags(flags);
430 /*outb(DT2811_CLRERROR|DT2811_INTENB,
431 dev->iobase+DT2811_ADCSR);*/
433 if (inb(dev->iobase + DT2811_ADCSR) & DT2811_ADERROR)
434 printk(KERN_ERR "error probing irq (bad)\n");
437 i = inb(dev->iobase + DT2811_ADDATLO);
438 i = inb(dev->iobase + DT2811_ADDATHI);
439 printk(KERN_INFO "(irq = %d)\n", irq);
440 ret = request_irq(irq, dt2811_interrupt, 0,
441 dev->board_name, dev);
445 } else if (irq == 0) {
446 printk(KERN_INFO "(no irq)\n");
448 printk(KERN_ERR "( multiple irq's -- this is bad! )\n");
453 ret = comedi_alloc_subdevices(dev, 4);
457 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
460 dev->private = devpriv;
462 switch (it->options[2]) {
464 devpriv->adc_mux = adc_singleended;
467 devpriv->adc_mux = adc_diff;
470 devpriv->adc_mux = adc_pseudo_diff;
473 devpriv->adc_mux = adc_singleended;
476 switch (it->options[4]) {
478 devpriv->dac_range[0] = dac_bipolar_5;
481 devpriv->dac_range[0] = dac_bipolar_2_5;
484 devpriv->dac_range[0] = dac_unipolar_5;
487 devpriv->dac_range[0] = dac_bipolar_5;
490 switch (it->options[5]) {
492 devpriv->dac_range[1] = dac_bipolar_5;
495 devpriv->dac_range[1] = dac_bipolar_2_5;
498 devpriv->dac_range[1] = dac_unipolar_5;
501 devpriv->dac_range[1] = dac_bipolar_5;
505 s = &dev->subdevices[0];
506 /* initialize the ADC subdevice */
507 s->type = COMEDI_SUBD_AI;
508 s->subdev_flags = SDF_READABLE | SDF_GROUND;
509 s->n_chan = devpriv->adc_mux == adc_diff ? 8 : 16;
510 s->insn_read = dt2811_ai_insn;
512 switch (it->options[3]) {
515 s->range_table = board->bip_5;
518 s->range_table = board->bip_2_5;
521 s->range_table = board->unip_5;
525 s = &dev->subdevices[1];
527 s->type = COMEDI_SUBD_AO;
528 s->subdev_flags = SDF_WRITABLE;
530 s->insn_write = dt2811_ao_insn;
531 s->insn_read = dt2811_ao_insn_read;
533 s->range_table_list = devpriv->range_type_list;
534 devpriv->range_type_list[0] = dac_range_types[devpriv->dac_range[0]];
535 devpriv->range_type_list[1] = dac_range_types[devpriv->dac_range[1]];
537 s = &dev->subdevices[2];
539 s->type = COMEDI_SUBD_DI;
540 s->subdev_flags = SDF_READABLE;
542 s->insn_bits = dt2811_di_insn_bits;
544 s->range_table = &range_digital;
546 s = &dev->subdevices[3];
548 s->type = COMEDI_SUBD_DO;
549 s->subdev_flags = SDF_WRITABLE;
551 s->insn_bits = dt2811_do_insn_bits;
554 s->range_table = &range_digital;
559 static const struct dt2811_board boardtypes[] = {
561 .name = "dt2811-pgh",
562 .bip_5 = &range_dt2811_pgh_ai_5_bipolar,
563 .bip_2_5 = &range_dt2811_pgh_ai_2_5_bipolar,
564 .unip_5 = &range_dt2811_pgh_ai_5_unipolar,
566 .name = "dt2811-pgl",
567 .bip_5 = &range_dt2811_pgl_ai_5_bipolar,
568 .bip_2_5 = &range_dt2811_pgl_ai_2_5_bipolar,
569 .unip_5 = &range_dt2811_pgl_ai_5_unipolar,
573 static struct comedi_driver dt2811_driver = {
574 .driver_name = "dt2811",
575 .module = THIS_MODULE,
576 .attach = dt2811_attach,
577 .detach = comedi_legacy_detach,
578 .board_name = &boardtypes[0].name,
579 .num_names = ARRAY_SIZE(boardtypes),
580 .offset = sizeof(struct dt2811_board),
582 module_comedi_driver(dt2811_driver);
584 MODULE_AUTHOR("Comedi http://www.comedi.org");
585 MODULE_DESCRIPTION("Comedi low-level driver");
586 MODULE_LICENSE("GPL");