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 enum { card_2811_pgh, card_2811_pgl };
216 struct dt2811_private {
220 adc_singleended, adc_diff, adc_pseudo_diff
223 dac_bipolar_5, dac_bipolar_2_5, dac_unipolar_5
225 const struct comedi_lrange *range_type_list[2];
226 unsigned int ao_readback[2];
229 static const struct comedi_lrange *dac_range_types[] = {
235 #define DT2811_TIMEOUT 5
238 static irqreturn_t dt2811_interrupt(int irq, void *d)
242 struct comedi_device *dev = d;
243 struct dt2811_private *devpriv = dev->private;
245 if (!dev->attached) {
246 comedi_error(dev, "spurious interrupt");
250 lo = inb(dev->iobase + DT2811_ADDATLO);
251 hi = inb(dev->iobase + DT2811_ADDATHI);
253 data = lo + (hi << 8);
255 if (!(--devpriv->ntrig)) {
256 /* how to turn off acquisition */
257 s->async->events |= COMEDI_SB_EOA;
259 comedi_event(dev, s);
264 static int dt2811_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
265 struct comedi_insn *insn, unsigned int *data)
267 int chan = CR_CHAN(insn->chanspec);
268 int timeout = DT2811_TIMEOUT;
271 for (i = 0; i < insn->n; i++) {
272 outb(chan, dev->iobase + DT2811_ADGCR);
275 && inb(dev->iobase + DT2811_ADCSR) & DT2811_ADBUSY)
280 data[i] = inb(dev->iobase + DT2811_ADDATLO);
281 data[i] |= inb(dev->iobase + DT2811_ADDATHI) << 8;
289 /* Wow. This is code from the Comedi stone age. But it hasn't been
290 * replaced, so I'll let it stay. */
291 int dt2811_adtrig(kdev_t minor, comedi_adtrig *adtrig)
293 struct comedi_device *dev = comedi_devices + minor;
297 dev->curadchan = adtrig->chan;
298 switch (dev->i_admode) {
300 dev->ntrig = adtrig->n - 1;
302 /*printk("dt2811: AD soft trigger\n"); */
303 /*outb(DT2811_CLRERROR|DT2811_INTENB,
304 dev->iobase+DT2811_ADCSR); */
305 outb(dev->curadchan, dev->iobase + DT2811_ADGCR);
306 do_gettimeofday(&trigtime);
309 dev->ntrig = adtrig->n;
317 static int dt2811_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
318 struct comedi_insn *insn, unsigned int *data)
320 struct dt2811_private *devpriv = dev->private;
324 chan = CR_CHAN(insn->chanspec);
326 for (i = 0; i < insn->n; i++) {
327 outb(data[i] & 0xff, dev->iobase + DT2811_DADAT0LO + 2 * chan);
328 outb((data[i] >> 8) & 0xff,
329 dev->iobase + DT2811_DADAT0HI + 2 * chan);
330 devpriv->ao_readback[chan] = data[i];
336 static int dt2811_ao_insn_read(struct comedi_device *dev,
337 struct comedi_subdevice *s,
338 struct comedi_insn *insn, unsigned int *data)
340 struct dt2811_private *devpriv = dev->private;
344 chan = CR_CHAN(insn->chanspec);
346 for (i = 0; i < insn->n; i++)
347 data[i] = devpriv->ao_readback[chan];
352 static int dt2811_di_insn_bits(struct comedi_device *dev,
353 struct comedi_subdevice *s,
354 struct comedi_insn *insn, unsigned int *data)
356 data[1] = inb(dev->iobase + DT2811_DIO);
361 static int dt2811_do_insn_bits(struct comedi_device *dev,
362 struct comedi_subdevice *s,
363 struct comedi_insn *insn, unsigned int *data)
365 s->state &= ~data[0];
366 s->state |= data[0] & data[1];
367 outb(s->state, dev->iobase + DT2811_DIO);
375 options[0] Board base address
377 options[2] Input configuration
380 2 == pseudo-differential
381 options[3] Analog input range configuration
382 0 == bipolar 5 (-5V -- +5V)
383 1 == bipolar 2.5V (-2.5V -- +2.5V)
384 2 == unipolar 5V (0V -- +5V)
385 options[4] Analog output 0 range configuration
386 0 == bipolar 5 (-5V -- +5V)
387 1 == bipolar 2.5V (-2.5V -- +2.5V)
388 2 == unipolar 5V (0V -- +5V)
389 options[5] Analog output 1 range configuration
390 0 == bipolar 5 (-5V -- +5V)
391 1 == bipolar 2.5V (-2.5V -- +2.5V)
392 2 == unipolar 5V (0V -- +5V)
394 static int dt2811_attach(struct comedi_device *dev, struct comedi_devconfig *it)
397 /* unsigned long irqs; */
400 const struct dt2811_board *board = comedi_board(dev);
401 struct dt2811_private *devpriv;
403 struct comedi_subdevice *s;
404 unsigned long iobase;
406 iobase = it->options[0];
408 printk(KERN_INFO "comedi%d: dt2811:base=0x%04lx\n", dev->minor, iobase);
410 if (!request_region(iobase, DT2811_SIZE, driver_name)) {
411 printk(KERN_ERR "I/O port conflict\n");
415 dev->iobase = iobase;
416 dev->board_name = board->name;
419 outb(0, dev->iobase + DT2811_ADCSR);
421 i = inb(dev->iobase + DT2811_ADDATLO);
422 i = inb(dev->iobase + DT2811_ADDATHI);
426 irq = it->options[1];
430 irqs = probe_irq_on();
432 outb(DT2811_CLRERROR | DT2811_INTENB,
433 dev->iobase + DT2811_ADCSR);
434 outb(0, dev->iobase + DT2811_ADGCR);
438 irq = probe_irq_off(irqs);
439 restore_flags(flags);
441 /*outb(DT2811_CLRERROR|DT2811_INTENB,
442 dev->iobase+DT2811_ADCSR);*/
444 if (inb(dev->iobase + DT2811_ADCSR) & DT2811_ADERROR)
445 printk(KERN_ERR "error probing irq (bad)\n");
448 i = inb(dev->iobase + DT2811_ADDATLO);
449 i = inb(dev->iobase + DT2811_ADDATHI);
450 printk(KERN_INFO "(irq = %d)\n", irq);
451 ret = request_irq(irq, dt2811_interrupt, 0,
456 } else if (irq == 0) {
457 printk(KERN_INFO "(no irq)\n");
459 printk(KERN_ERR "( multiple irq's -- this is bad! )\n");
464 ret = comedi_alloc_subdevices(dev, 4);
468 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
471 dev->private = devpriv;
473 switch (it->options[2]) {
475 devpriv->adc_mux = adc_singleended;
478 devpriv->adc_mux = adc_diff;
481 devpriv->adc_mux = adc_pseudo_diff;
484 devpriv->adc_mux = adc_singleended;
487 switch (it->options[4]) {
489 devpriv->dac_range[0] = dac_bipolar_5;
492 devpriv->dac_range[0] = dac_bipolar_2_5;
495 devpriv->dac_range[0] = dac_unipolar_5;
498 devpriv->dac_range[0] = dac_bipolar_5;
501 switch (it->options[5]) {
503 devpriv->dac_range[1] = dac_bipolar_5;
506 devpriv->dac_range[1] = dac_bipolar_2_5;
509 devpriv->dac_range[1] = dac_unipolar_5;
512 devpriv->dac_range[1] = dac_bipolar_5;
516 s = &dev->subdevices[0];
517 /* initialize the ADC subdevice */
518 s->type = COMEDI_SUBD_AI;
519 s->subdev_flags = SDF_READABLE | SDF_GROUND;
520 s->n_chan = devpriv->adc_mux == adc_diff ? 8 : 16;
521 s->insn_read = dt2811_ai_insn;
523 switch (it->options[3]) {
526 s->range_table = board->bip_5;
529 s->range_table = board->bip_2_5;
532 s->range_table = board->unip_5;
536 s = &dev->subdevices[1];
538 s->type = COMEDI_SUBD_AO;
539 s->subdev_flags = SDF_WRITABLE;
541 s->insn_write = dt2811_ao_insn;
542 s->insn_read = dt2811_ao_insn_read;
544 s->range_table_list = devpriv->range_type_list;
545 devpriv->range_type_list[0] = dac_range_types[devpriv->dac_range[0]];
546 devpriv->range_type_list[1] = dac_range_types[devpriv->dac_range[1]];
548 s = &dev->subdevices[2];
550 s->type = COMEDI_SUBD_DI;
551 s->subdev_flags = SDF_READABLE;
553 s->insn_bits = dt2811_di_insn_bits;
555 s->range_table = &range_digital;
557 s = &dev->subdevices[3];
559 s->type = COMEDI_SUBD_DO;
560 s->subdev_flags = SDF_WRITABLE;
562 s->insn_bits = dt2811_do_insn_bits;
565 s->range_table = &range_digital;
570 static void dt2811_detach(struct comedi_device *dev)
573 free_irq(dev->irq, dev);
575 release_region(dev->iobase, DT2811_SIZE);
578 static const struct dt2811_board boardtypes[] = {
580 .name = "dt2811-pgh",
581 .bip_5 = &range_dt2811_pgh_ai_5_bipolar,
582 .bip_2_5 = &range_dt2811_pgh_ai_2_5_bipolar,
583 .unip_5 = &range_dt2811_pgh_ai_5_unipolar,
585 .name = "dt2811-pgl",
586 .bip_5 = &range_dt2811_pgl_ai_5_bipolar,
587 .bip_2_5 = &range_dt2811_pgl_ai_2_5_bipolar,
588 .unip_5 = &range_dt2811_pgl_ai_5_unipolar,
592 static struct comedi_driver dt2811_driver = {
593 .driver_name = "dt2811",
594 .module = THIS_MODULE,
595 .attach = dt2811_attach,
596 .detach = dt2811_detach,
597 .board_name = &boardtypes[0].name,
598 .num_names = ARRAY_SIZE(boardtypes),
599 .offset = sizeof(struct dt2811_board),
601 module_comedi_driver(dt2811_driver);
603 MODULE_AUTHOR("Comedi http://www.comedi.org");
604 MODULE_DESCRIPTION("Comedi low-level driver");
605 MODULE_LICENSE("GPL");