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 = {
64 static const struct comedi_lrange range_dt2811_pgh_ai_2_5_bipolar = {
69 RANGE(-0.3125, 0.3125)
73 static const struct comedi_lrange range_dt2811_pgh_ai_5_bipolar = {
82 static const struct comedi_lrange range_dt2811_pgl_ai_5_unipolar = {
91 static const struct comedi_lrange range_dt2811_pgl_ai_2_5_bipolar = {
100 static const struct comedi_lrange range_dt2811_pgl_ai_5_bipolar = {
111 0x00 ADCSR R/W A/D Control/Status Register
112 bit 7 - (R) 1 indicates A/D conversion done
113 reading ADDAT clears bit
115 bit 6 - (R) 1 indicates A/D error
117 bit 5 - (R) 1 indicates A/D busy, cleared at end
123 bit 2 - (R/W) 1 indicates interrupts enabled
124 bits 1,0 - (R/W) mode bits
125 00 single conversion on ADGCR load
126 01 continuous conversion, internal clock,
127 (clock enabled on ADGCR load)
128 10 continuous conversion, internal clock,
130 11 continuous conversion, external clock,
133 0x01 ADGCR R/W A/D Gain/Channel Register
134 bit 6,7 - (R/W) gain select
135 00 gain=1, both PGH, PGL models
136 01 gain=2 PGH, 10 PGL
137 10 gain=4 PGH, 100 PGL
138 11 gain=8 PGH, 500 PGL
140 bit 3-0 - (R/W) channel select
141 channel number from 0-15
143 0x02,0x03 (R) ADDAT A/D Data Register
144 (W) DADAT0 D/A Data Register 0
148 0x04,0x05 (W) DADAT0 D/A Data Register 1
150 0x06 (R) DIO0 Digital Input Port 0
151 (W) DIO1 Digital Output Port 1
153 0x07 TMRCTR (R/W) Timer/Counter Register
155 bits 5-3 - Timer frequency control (mantissa)
156 543 divisor freqency (kHz)
165 bits 2-0 - Timer frequency control (exponent)
166 210 multiply divisor/divide frequency by
178 #define TIMEOUT 10000
180 #define DT2811_SIZE 8
182 #define DT2811_ADCSR 0
183 #define DT2811_ADGCR 1
184 #define DT2811_ADDATLO 2
185 #define DT2811_ADDATHI 3
186 #define DT2811_DADAT0LO 2
187 #define DT2811_DADAT0HI 3
188 #define DT2811_DADAT1LO 4
189 #define DT2811_DADAT1HI 5
191 #define DT2811_TMRCTR 7
199 #define DT2811_ADDONE 0x80
200 #define DT2811_ADERROR 0x40
201 #define DT2811_ADBUSY 0x20
202 #define DT2811_CLRERROR 0x10
203 #define DT2811_INTENB 0x04
204 #define DT2811_ADMODE 0x03
206 struct dt2811_board {
209 const struct comedi_lrange *bip_5;
210 const struct comedi_lrange *bip_2_5;
211 const struct comedi_lrange *unip_5;
214 static const struct dt2811_board boardtypes[] = {
216 &range_dt2811_pgh_ai_5_bipolar,
217 &range_dt2811_pgh_ai_2_5_bipolar,
218 &range_dt2811_pgh_ai_5_unipolar,
221 &range_dt2811_pgl_ai_5_bipolar,
222 &range_dt2811_pgl_ai_2_5_bipolar,
223 &range_dt2811_pgl_ai_5_unipolar,
227 #define this_board ((const struct dt2811_board *)dev->board_ptr)
229 static int dt2811_attach(struct comedi_device *dev,
230 struct comedi_devconfig *it);
231 static int dt2811_detach(struct comedi_device *dev);
232 static struct comedi_driver driver_dt2811 = {
233 .driver_name = "dt2811",
234 .module = THIS_MODULE,
235 .attach = dt2811_attach,
236 .detach = dt2811_detach,
237 .board_name = &boardtypes[0].name,
238 .num_names = ARRAY_SIZE(boardtypes),
239 .offset = sizeof(struct dt2811_board),
242 static int __init driver_dt2811_init_module(void)
244 return comedi_driver_register(&driver_dt2811);
247 static void __exit driver_dt2811_cleanup_module(void)
249 comedi_driver_unregister(&driver_dt2811);
252 module_init(driver_dt2811_init_module);
253 module_exit(driver_dt2811_cleanup_module);
255 static int dt2811_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
256 struct comedi_insn *insn, unsigned int *data);
257 static int dt2811_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
258 struct comedi_insn *insn, unsigned int *data);
259 static int dt2811_ao_insn_read(struct comedi_device *dev,
260 struct comedi_subdevice *s,
261 struct comedi_insn *insn, unsigned int *data);
262 static int dt2811_di_insn_bits(struct comedi_device *dev,
263 struct comedi_subdevice *s,
264 struct comedi_insn *insn, unsigned int *data);
265 static int dt2811_do_insn_bits(struct comedi_device *dev,
266 struct comedi_subdevice *s,
267 struct comedi_insn *insn, unsigned int *data);
269 enum { card_2811_pgh, card_2811_pgl };
271 struct dt2811_private {
275 adc_singleended, adc_diff, adc_pseudo_diff
278 dac_bipolar_5, dac_bipolar_2_5, dac_unipolar_5
280 const struct comedi_lrange *range_type_list[2];
281 unsigned int ao_readback[2];
284 #define devpriv ((struct dt2811_private *)dev->private)
286 static const struct comedi_lrange *dac_range_types[] = {
292 #define DT2811_TIMEOUT 5
295 static irqreturn_t dt2811_interrupt(int irq, void *d)
299 struct comedi_device *dev = d;
301 if (!dev->attached) {
302 comedi_error(dev, "spurious interrupt");
306 lo = inb(dev->iobase + DT2811_ADDATLO);
307 hi = inb(dev->iobase + DT2811_ADDATHI);
309 data = lo + (hi << 8);
311 if (!(--devpriv->ntrig)) {
312 /* how to turn off acquisition */
313 s->async->events |= COMEDI_SB_EOA;
315 comedi_event(dev, s);
321 options[0] Board base address
323 options[2] Input configuration
326 2 == pseudo-differential
327 options[3] Analog input range configuration
328 0 == bipolar 5 (-5V -- +5V)
329 1 == bipolar 2.5V (-2.5V -- +2.5V)
330 2 == unipolar 5V (0V -- +5V)
331 options[4] Analog output 0 range configuration
332 0 == bipolar 5 (-5V -- +5V)
333 1 == bipolar 2.5V (-2.5V -- +2.5V)
334 2 == unipolar 5V (0V -- +5V)
335 options[5] Analog output 1 range configuration
336 0 == bipolar 5 (-5V -- +5V)
337 1 == bipolar 2.5V (-2.5V -- +2.5V)
338 2 == unipolar 5V (0V -- +5V)
341 static int dt2811_attach(struct comedi_device *dev, struct comedi_devconfig *it)
344 /* unsigned long irqs; */
348 struct comedi_subdevice *s;
349 unsigned long iobase;
351 iobase = it->options[0];
353 printk(KERN_INFO "comedi%d: dt2811:base=0x%04lx\n", dev->minor, iobase);
355 if (!request_region(iobase, DT2811_SIZE, driver_name)) {
356 printk(KERN_ERR "I/O port conflict\n");
360 dev->iobase = iobase;
361 dev->board_name = this_board->name;
364 outb(0, dev->iobase + DT2811_ADCSR);
366 i = inb(dev->iobase + DT2811_ADDATLO);
367 i = inb(dev->iobase + DT2811_ADDATHI);
371 irq = it->options[1];
375 irqs = probe_irq_on();
377 outb(DT2811_CLRERROR | DT2811_INTENB,
378 dev->iobase + DT2811_ADCSR);
379 outb(0, dev->iobase + DT2811_ADGCR);
383 irq = probe_irq_off(irqs);
384 restore_flags(flags);
386 /*outb(DT2811_CLRERROR|DT2811_INTENB,
387 dev->iobase+DT2811_ADCSR);*/
389 if (inb(dev->iobase + DT2811_ADCSR) & DT2811_ADERROR)
390 printk(KERN_ERR "error probing irq (bad)\n");
393 i = inb(dev->iobase + DT2811_ADDATLO);
394 i = inb(dev->iobase + DT2811_ADDATHI);
395 printk(KERN_INFO "(irq = %d)\n", irq);
396 ret = request_irq(irq, dt2811_interrupt, 0,
401 } else if (irq == 0) {
402 printk(KERN_INFO "(no irq)\n");
404 printk(KERN_ERR "( multiple irq's -- this is bad! )\n");
409 ret = alloc_subdevices(dev, 4);
413 ret = alloc_private(dev, sizeof(struct dt2811_private));
417 switch (it->options[2]) {
419 devpriv->adc_mux = adc_singleended;
422 devpriv->adc_mux = adc_diff;
425 devpriv->adc_mux = adc_pseudo_diff;
428 devpriv->adc_mux = adc_singleended;
431 switch (it->options[4]) {
433 devpriv->dac_range[0] = dac_bipolar_5;
436 devpriv->dac_range[0] = dac_bipolar_2_5;
439 devpriv->dac_range[0] = dac_unipolar_5;
442 devpriv->dac_range[0] = dac_bipolar_5;
445 switch (it->options[5]) {
447 devpriv->dac_range[1] = dac_bipolar_5;
450 devpriv->dac_range[1] = dac_bipolar_2_5;
453 devpriv->dac_range[1] = dac_unipolar_5;
456 devpriv->dac_range[1] = dac_bipolar_5;
460 s = dev->subdevices + 0;
461 /* initialize the ADC subdevice */
462 s->type = COMEDI_SUBD_AI;
463 s->subdev_flags = SDF_READABLE | SDF_GROUND;
464 s->n_chan = devpriv->adc_mux == adc_diff ? 8 : 16;
465 s->insn_read = dt2811_ai_insn;
467 switch (it->options[3]) {
470 s->range_table = this_board->bip_5;
473 s->range_table = this_board->bip_2_5;
476 s->range_table = this_board->unip_5;
480 s = dev->subdevices + 1;
482 s->type = COMEDI_SUBD_AO;
483 s->subdev_flags = SDF_WRITABLE;
485 s->insn_write = dt2811_ao_insn;
486 s->insn_read = dt2811_ao_insn_read;
488 s->range_table_list = devpriv->range_type_list;
489 devpriv->range_type_list[0] = dac_range_types[devpriv->dac_range[0]];
490 devpriv->range_type_list[1] = dac_range_types[devpriv->dac_range[1]];
492 s = dev->subdevices + 2;
494 s->type = COMEDI_SUBD_DI;
495 s->subdev_flags = SDF_READABLE;
497 s->insn_bits = dt2811_di_insn_bits;
499 s->range_table = &range_digital;
501 s = dev->subdevices + 3;
503 s->type = COMEDI_SUBD_DO;
504 s->subdev_flags = SDF_WRITABLE;
506 s->insn_bits = dt2811_do_insn_bits;
509 s->range_table = &range_digital;
514 static int dt2811_detach(struct comedi_device *dev)
516 printk(KERN_INFO "comedi%d: dt2811: remove\n", dev->minor);
519 free_irq(dev->irq, dev);
521 release_region(dev->iobase, DT2811_SIZE);
526 static int dt2811_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
527 struct comedi_insn *insn, unsigned int *data)
529 int chan = CR_CHAN(insn->chanspec);
530 int timeout = DT2811_TIMEOUT;
533 for (i = 0; i < insn->n; i++) {
534 outb(chan, dev->iobase + DT2811_ADGCR);
537 && inb(dev->iobase + DT2811_ADCSR) & DT2811_ADBUSY)
542 data[i] = inb(dev->iobase + DT2811_ADDATLO);
543 data[i] |= inb(dev->iobase + DT2811_ADDATHI) << 8;
551 /* Wow. This is code from the Comedi stone age. But it hasn't been
552 * replaced, so I'll let it stay. */
553 int dt2811_adtrig(kdev_t minor, comedi_adtrig *adtrig)
555 struct comedi_device *dev = comedi_devices + minor;
559 dev->curadchan = adtrig->chan;
560 switch (dev->i_admode) {
562 dev->ntrig = adtrig->n - 1;
564 /*printk("dt2811: AD soft trigger\n"); */
565 /*outb(DT2811_CLRERROR|DT2811_INTENB,
566 dev->iobase+DT2811_ADCSR); */
567 outb(dev->curadchan, dev->iobase + DT2811_ADGCR);
568 do_gettimeofday(&trigtime);
571 dev->ntrig = adtrig->n;
579 static int dt2811_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
580 struct comedi_insn *insn, unsigned int *data)
585 chan = CR_CHAN(insn->chanspec);
587 for (i = 0; i < insn->n; i++) {
588 outb(data[i] & 0xff, dev->iobase + DT2811_DADAT0LO + 2 * chan);
589 outb((data[i] >> 8) & 0xff,
590 dev->iobase + DT2811_DADAT0HI + 2 * chan);
591 devpriv->ao_readback[chan] = data[i];
597 static int dt2811_ao_insn_read(struct comedi_device *dev,
598 struct comedi_subdevice *s,
599 struct comedi_insn *insn, unsigned int *data)
604 chan = CR_CHAN(insn->chanspec);
606 for (i = 0; i < insn->n; i++)
607 data[i] = devpriv->ao_readback[chan];
612 static int dt2811_di_insn_bits(struct comedi_device *dev,
613 struct comedi_subdevice *s,
614 struct comedi_insn *insn, unsigned int *data)
619 data[1] = inb(dev->iobase + DT2811_DIO);
624 static int dt2811_do_insn_bits(struct comedi_device *dev,
625 struct comedi_subdevice *s,
626 struct comedi_insn *insn, unsigned int *data)
631 s->state &= ~data[0];
632 s->state |= data[0] & data[1];
633 outb(s->state, dev->iobase + DT2811_DIO);
640 MODULE_AUTHOR("Comedi http://www.comedi.org");
641 MODULE_DESCRIPTION("Comedi low-level driver");
642 MODULE_LICENSE("GPL");