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 struct comedi_lrange range_dt2811_pgh_ai_5_unipolar = { 4, {
61 static const struct comedi_lrange range_dt2811_pgh_ai_2_5_bipolar = { 4, {
65 RANGE(-0.3125, 0.3125)
68 static const struct comedi_lrange range_dt2811_pgh_ai_5_bipolar = { 4, {
75 static const struct comedi_lrange range_dt2811_pgl_ai_5_unipolar = { 4, {
82 static const struct comedi_lrange range_dt2811_pgl_ai_2_5_bipolar = { 4, {
89 static const struct 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
194 struct dt2811_board {
197 const struct comedi_lrange *bip_5;
198 const struct comedi_lrange *bip_2_5;
199 const struct comedi_lrange *unip_5;
202 static const struct dt2811_board boardtypes[] = {
204 &range_dt2811_pgh_ai_5_bipolar,
205 &range_dt2811_pgh_ai_2_5_bipolar,
206 &range_dt2811_pgh_ai_5_unipolar,
209 &range_dt2811_pgl_ai_5_bipolar,
210 &range_dt2811_pgl_ai_2_5_bipolar,
211 &range_dt2811_pgl_ai_5_unipolar,
215 #define this_board ((const struct dt2811_board *)dev->board_ptr)
217 static int dt2811_attach(struct comedi_device * dev, struct comedi_devconfig * it);
218 static int dt2811_detach(struct comedi_device * dev);
219 static struct comedi_driver driver_dt2811 = {
220 driver_name:"dt2811",
222 attach:dt2811_attach,
223 detach:dt2811_detach,
224 board_name:&boardtypes[0].name,
225 num_names:sizeof(boardtypes) / sizeof(struct dt2811_board),
226 offset:sizeof(struct dt2811_board),
229 COMEDI_INITCLEANUP(driver_dt2811);
231 static int dt2811_ai_insn(struct comedi_device * dev, struct comedi_subdevice * s,
232 struct comedi_insn * insn, unsigned int * data);
233 static int dt2811_ao_insn(struct comedi_device * dev, struct comedi_subdevice * s,
234 struct comedi_insn * insn, unsigned int * data);
235 static int dt2811_ao_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
236 struct comedi_insn * insn, unsigned int * data);
237 static int dt2811_di_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
238 struct comedi_insn * insn, unsigned int * data);
239 static int dt2811_do_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
240 struct comedi_insn * insn, unsigned int * data);
242 enum { card_2811_pgh, card_2811_pgl };
244 struct dt2811_private {
248 adc_singleended, adc_diff, adc_pseudo_diff
251 dac_bipolar_5, dac_bipolar_2_5, dac_unipolar_5
253 const struct comedi_lrange *range_type_list[2];
254 unsigned int ao_readback[2];
257 #define devpriv ((struct dt2811_private *)dev->private)
259 static const struct comedi_lrange *dac_range_types[] = {
265 #define DT2811_TIMEOUT 5
268 static irqreturn_t dt2811_interrupt(int irq, void *d PT_REGS_ARG)
272 struct comedi_device *dev = d;
274 if (!dev->attached) {
275 comedi_error(dev, "spurious interrupt");
279 lo = inb(dev->iobase + DT2811_ADDATLO);
280 hi = inb(dev->iobase + DT2811_ADDATHI);
282 data = lo + (hi << 8);
284 if (!(--devpriv->ntrig)) {
285 /* how to turn off acquisition */
286 s->async->events |= COMEDI_SB_EOA;
288 comedi_event(dev, s);
294 options[0] Board base address
296 options[2] Input configuration
299 2 == pseudo-differential
300 options[3] Analog input range configuration
301 0 == bipolar 5 (-5V -- +5V)
302 1 == bipolar 2.5V (-2.5V -- +2.5V)
303 2 == unipolar 5V (0V -- +5V)
304 options[4] Analog output 0 range configuration
305 0 == bipolar 5 (-5V -- +5V)
306 1 == bipolar 2.5V (-2.5V -- +2.5V)
307 2 == unipolar 5V (0V -- +5V)
308 options[5] Analog output 1 range configuration
309 0 == bipolar 5 (-5V -- +5V)
310 1 == bipolar 2.5V (-2.5V -- +2.5V)
311 2 == unipolar 5V (0V -- +5V)
314 static int dt2811_attach(struct comedi_device * dev, struct comedi_devconfig * it)
317 //unsigned long irqs;
320 struct comedi_subdevice *s;
321 unsigned long iobase;
323 iobase = it->options[0];
325 printk("comedi%d: dt2811: base=0x%04lx\n", dev->minor, iobase);
327 if (!request_region(iobase, DT2811_SIZE, driver_name)) {
328 printk("I/O port conflict\n");
332 dev->iobase = iobase;
333 dev->board_name = this_board->name;
336 outb(0, dev->iobase + DT2811_ADCSR);
338 i = inb(dev->iobase + DT2811_ADDATLO);
339 i = inb(dev->iobase + DT2811_ADDATHI);
343 irq = it->options[1];
347 irqs = probe_irq_on();
349 outb(DT2811_CLRERROR | DT2811_INTENB,
350 dev->iobase + DT2811_ADCSR);
351 outb(0, dev->iobase + DT2811_ADGCR);
355 irq = probe_irq_off(irqs);
356 restore_flags(flags);
358 /*outb(DT2811_CLRERROR|DT2811_INTENB,dev->iobase+DT2811_ADCSR); */
360 if (inb(dev->iobase + DT2811_ADCSR) & DT2811_ADERROR) {
361 printk("error probing irq (bad) \n");
365 i = inb(dev->iobase + DT2811_ADDATLO);
366 i = inb(dev->iobase + DT2811_ADDATHI);
367 printk("(irq = %d)\n", irq);
368 ret = comedi_request_irq(irq, dt2811_interrupt, 0,
373 } else if (irq == 0) {
374 printk("(no irq)\n");
376 printk("( multiple irq's -- this is bad! )\n");
381 if ((ret = alloc_subdevices(dev, 4)) < 0)
383 if ((ret = alloc_private(dev, sizeof(struct dt2811_private))) < 0)
385 switch (it->options[2]) {
387 devpriv->adc_mux = adc_singleended;
390 devpriv->adc_mux = adc_diff;
393 devpriv->adc_mux = adc_pseudo_diff;
396 devpriv->adc_mux = adc_singleended;
399 switch (it->options[4]) {
401 devpriv->dac_range[0] = dac_bipolar_5;
404 devpriv->dac_range[0] = dac_bipolar_2_5;
407 devpriv->dac_range[0] = dac_unipolar_5;
410 devpriv->dac_range[0] = dac_bipolar_5;
413 switch (it->options[5]) {
415 devpriv->dac_range[1] = dac_bipolar_5;
418 devpriv->dac_range[1] = dac_bipolar_2_5;
421 devpriv->dac_range[1] = dac_unipolar_5;
424 devpriv->dac_range[1] = dac_bipolar_5;
428 s = dev->subdevices + 0;
429 /* initialize the ADC subdevice */
430 s->type = COMEDI_SUBD_AI;
431 s->subdev_flags = SDF_READABLE | SDF_GROUND;
432 s->n_chan = devpriv->adc_mux == adc_diff ? 8 : 16;
433 s->insn_read = dt2811_ai_insn;
435 switch (it->options[3]) {
438 s->range_table = this_board->bip_5;
441 s->range_table = this_board->bip_2_5;
444 s->range_table = this_board->unip_5;
448 s = dev->subdevices + 1;
450 s->type = COMEDI_SUBD_AO;
451 s->subdev_flags = SDF_WRITABLE;
453 s->insn_write = dt2811_ao_insn;
454 s->insn_read = dt2811_ao_insn_read;
456 s->range_table_list = devpriv->range_type_list;
457 devpriv->range_type_list[0] = dac_range_types[devpriv->dac_range[0]];
458 devpriv->range_type_list[1] = dac_range_types[devpriv->dac_range[1]];
460 s = dev->subdevices + 2;
462 s->type = COMEDI_SUBD_DI;
463 s->subdev_flags = SDF_READABLE;
465 s->insn_bits = dt2811_di_insn_bits;
467 s->range_table = &range_digital;
469 s = dev->subdevices + 3;
471 s->type = COMEDI_SUBD_DO;
472 s->subdev_flags = SDF_WRITABLE;
474 s->insn_bits = dt2811_do_insn_bits;
477 s->range_table = &range_digital;
482 static int dt2811_detach(struct comedi_device * dev)
484 printk("comedi%d: dt2811: remove\n", dev->minor);
487 comedi_free_irq(dev->irq, dev);
490 release_region(dev->iobase, DT2811_SIZE);
496 static int dt2811_ai_insn(struct comedi_device * dev, struct comedi_subdevice * s,
497 struct comedi_insn * insn, unsigned int * data)
499 int chan = CR_CHAN(insn->chanspec);
500 int timeout = DT2811_TIMEOUT;
503 for (i = 0; i < insn->n; i++) {
504 outb(chan, dev->iobase + DT2811_ADGCR);
507 && inb(dev->iobase + DT2811_ADCSR) & DT2811_ADBUSY)
512 data[i] = inb(dev->iobase + DT2811_ADDATLO);
513 data[i] |= inb(dev->iobase + DT2811_ADDATHI) << 8;
521 /* Wow. This is code from the Comedi stone age. But it hasn't been
522 * replaced, so I'll let it stay. */
523 int dt2811_adtrig(kdev_t minor, comedi_adtrig * adtrig)
525 struct comedi_device *dev = comedi_devices + minor;
529 dev->curadchan = adtrig->chan;
530 switch (dev->i_admode) {
532 dev->ntrig = adtrig->n - 1;
533 /*printk("dt2811: AD soft trigger\n"); */
534 /*outb(DT2811_CLRERROR|DT2811_INTENB,dev->iobase+DT2811_ADCSR); *//* not neccessary */
535 outb(dev->curadchan, dev->iobase + DT2811_ADGCR);
536 do_gettimeofday(&trigtime);
539 dev->ntrig = adtrig->n;
547 static int dt2811_ao_insn(struct comedi_device * dev, struct comedi_subdevice * s,
548 struct comedi_insn * insn, unsigned int * data)
553 chan = CR_CHAN(insn->chanspec);
555 for (i = 0; i < insn->n; i++) {
556 outb(data[i] & 0xff, dev->iobase + DT2811_DADAT0LO + 2 * chan);
557 outb((data[i] >> 8) & 0xff,
558 dev->iobase + DT2811_DADAT0HI + 2 * chan);
559 devpriv->ao_readback[chan] = data[i];
565 static int dt2811_ao_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
566 struct comedi_insn * insn, unsigned int * data)
571 chan = CR_CHAN(insn->chanspec);
573 for (i = 0; i < insn->n; i++) {
574 data[i] = devpriv->ao_readback[chan];
580 static int dt2811_di_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
581 struct comedi_insn * insn, unsigned int * data)
586 data[1] = inb(dev->iobase + DT2811_DIO);
591 static int dt2811_do_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
592 struct comedi_insn * insn, unsigned int * data)
597 s->state &= ~data[0];
598 s->state |= data[0] & data[1];
599 outb(s->state, dev->iobase + DT2811_DIO);