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
= { 4, {
70 static const struct comedi_lrange range_dt2811_pgh_ai_2_5_bipolar
= { 4, {
86 static const struct comedi_lrange range_dt2811_pgh_ai_5_bipolar
= { 4, {
101 static const struct comedi_lrange range_dt2811_pgl_ai_5_unipolar
= { 4, {
116 static const struct comedi_lrange range_dt2811_pgl_ai_2_5_bipolar
= { 4, {
132 static const struct comedi_lrange range_dt2811_pgl_ai_5_bipolar
= { 4, {
149 0x00 ADCSR R/W A/D Control/Status Register
150 bit 7 - (R) 1 indicates A/D conversion done
151 reading ADDAT clears bit
153 bit 6 - (R) 1 indicates A/D error
155 bit 5 - (R) 1 indicates A/D busy, cleared at end
161 bit 2 - (R/W) 1 indicates interrupts enabled
162 bits 1,0 - (R/W) mode bits
163 00 single conversion on ADGCR load
164 01 continuous conversion, internal clock,
165 (clock enabled on ADGCR load)
166 10 continuous conversion, internal clock,
168 11 continuous conversion, external clock,
171 0x01 ADGCR R/W A/D Gain/Channel Register
172 bit 6,7 - (R/W) gain select
173 00 gain=1, both PGH, PGL models
174 01 gain=2 PGH, 10 PGL
175 10 gain=4 PGH, 100 PGL
176 11 gain=8 PGH, 500 PGL
178 bit 3-0 - (R/W) channel select
179 channel number from 0-15
181 0x02,0x03 (R) ADDAT A/D Data Register
182 (W) DADAT0 D/A Data Register 0
186 0x04,0x05 (W) DADAT0 D/A Data Register 1
188 0x06 (R) DIO0 Digital Input Port 0
189 (W) DIO1 Digital Output Port 1
191 0x07 TMRCTR (R/W) Timer/Counter Register
193 bits 5-3 - Timer frequency control (mantissa)
194 543 divisor freqency (kHz)
203 bits 2-0 - Timer frequency control (exponent)
204 210 multiply divisor/divide frequency by
216 #define TIMEOUT 10000
218 #define DT2811_SIZE 8
220 #define DT2811_ADCSR 0
221 #define DT2811_ADGCR 1
222 #define DT2811_ADDATLO 2
223 #define DT2811_ADDATHI 3
224 #define DT2811_DADAT0LO 2
225 #define DT2811_DADAT0HI 3
226 #define DT2811_DADAT1LO 4
227 #define DT2811_DADAT1HI 5
229 #define DT2811_TMRCTR 7
237 #define DT2811_ADDONE 0x80
238 #define DT2811_ADERROR 0x40
239 #define DT2811_ADBUSY 0x20
240 #define DT2811_CLRERROR 0x10
241 #define DT2811_INTENB 0x04
242 #define DT2811_ADMODE 0x03
244 struct dt2811_board
{
247 const struct comedi_lrange
*bip_5
;
248 const struct comedi_lrange
*bip_2_5
;
249 const struct comedi_lrange
*unip_5
;
252 static const struct dt2811_board boardtypes
[] = {
254 &range_dt2811_pgh_ai_5_bipolar
,
255 &range_dt2811_pgh_ai_2_5_bipolar
,
256 &range_dt2811_pgh_ai_5_unipolar
,
259 &range_dt2811_pgl_ai_5_bipolar
,
260 &range_dt2811_pgl_ai_2_5_bipolar
,
261 &range_dt2811_pgl_ai_5_unipolar
,
265 #define this_board ((const struct dt2811_board *)dev->board_ptr)
267 static int dt2811_attach(struct comedi_device
*dev
,
268 struct comedi_devconfig
*it
);
269 static int dt2811_detach(struct comedi_device
*dev
);
270 static struct comedi_driver driver_dt2811
= {
271 .driver_name
= "dt2811",
272 .module
= THIS_MODULE
,
273 .attach
= dt2811_attach
,
274 .detach
= dt2811_detach
,
275 .board_name
= &boardtypes
[0].name
,
276 .num_names
= ARRAY_SIZE(boardtypes
),
277 .offset
= sizeof(struct dt2811_board
),
280 COMEDI_INITCLEANUP(driver_dt2811
);
282 static int dt2811_ai_insn(struct comedi_device
*dev
, struct comedi_subdevice
*s
,
283 struct comedi_insn
*insn
, unsigned int *data
);
284 static int dt2811_ao_insn(struct comedi_device
*dev
, struct comedi_subdevice
*s
,
285 struct comedi_insn
*insn
, unsigned int *data
);
286 static int dt2811_ao_insn_read(struct comedi_device
*dev
,
287 struct comedi_subdevice
*s
,
288 struct comedi_insn
*insn
, unsigned int *data
);
289 static int dt2811_di_insn_bits(struct comedi_device
*dev
,
290 struct comedi_subdevice
*s
,
291 struct comedi_insn
*insn
, unsigned int *data
);
292 static int dt2811_do_insn_bits(struct comedi_device
*dev
,
293 struct comedi_subdevice
*s
,
294 struct comedi_insn
*insn
, unsigned int *data
);
296 enum { card_2811_pgh
, card_2811_pgl
};
298 struct dt2811_private
{
302 adc_singleended
, adc_diff
, adc_pseudo_diff
305 dac_bipolar_5
, dac_bipolar_2_5
, dac_unipolar_5
307 const struct comedi_lrange
*range_type_list
[2];
308 unsigned int ao_readback
[2];
311 #define devpriv ((struct dt2811_private *)dev->private)
313 static const struct comedi_lrange
*dac_range_types
[] = {
319 #define DT2811_TIMEOUT 5
322 static irqreturn_t
dt2811_interrupt(int irq
, void *d
)
326 struct comedi_device
*dev
= d
;
328 if (!dev
->attached
) {
329 comedi_error(dev
, "spurious interrupt");
333 lo
= inb(dev
->iobase
+ DT2811_ADDATLO
);
334 hi
= inb(dev
->iobase
+ DT2811_ADDATHI
);
336 data
= lo
+ (hi
<< 8);
338 if (!(--devpriv
->ntrig
)) {
339 /* how to turn off acquisition */
340 s
->async
->events
|= COMEDI_SB_EOA
;
342 comedi_event(dev
, s
);
348 options[0] Board base address
350 options[2] Input configuration
353 2 == pseudo-differential
354 options[3] Analog input range configuration
355 0 == bipolar 5 (-5V -- +5V)
356 1 == bipolar 2.5V (-2.5V -- +2.5V)
357 2 == unipolar 5V (0V -- +5V)
358 options[4] Analog output 0 range configuration
359 0 == bipolar 5 (-5V -- +5V)
360 1 == bipolar 2.5V (-2.5V -- +2.5V)
361 2 == unipolar 5V (0V -- +5V)
362 options[5] Analog output 1 range configuration
363 0 == bipolar 5 (-5V -- +5V)
364 1 == bipolar 2.5V (-2.5V -- +2.5V)
365 2 == unipolar 5V (0V -- +5V)
368 static int dt2811_attach(struct comedi_device
*dev
, struct comedi_devconfig
*it
)
371 /* unsigned long irqs; */
375 struct comedi_subdevice
*s
;
376 unsigned long iobase
;
378 iobase
= it
->options
[0];
380 printk("comedi%d: dt2811: base=0x%04lx\n", dev
->minor
, iobase
);
382 if (!request_region(iobase
, DT2811_SIZE
, driver_name
)) {
383 printk("I/O port conflict\n");
387 dev
->iobase
= iobase
;
388 dev
->board_name
= this_board
->name
;
391 outb(0, dev
->iobase
+ DT2811_ADCSR
);
393 i
= inb(dev
->iobase
+ DT2811_ADDATLO
);
394 i
= inb(dev
->iobase
+ DT2811_ADDATHI
);
398 irq
= it
->options
[1];
402 irqs
= probe_irq_on();
404 outb(DT2811_CLRERROR
| DT2811_INTENB
,
405 dev
->iobase
+ DT2811_ADCSR
);
406 outb(0, dev
->iobase
+ DT2811_ADGCR
);
410 irq
= probe_irq_off(irqs
);
411 restore_flags(flags
);
413 /*outb(DT2811_CLRERROR|DT2811_INTENB,dev->iobase+DT2811_ADCSR); */
415 if (inb(dev
->iobase
+ DT2811_ADCSR
) & DT2811_ADERROR
) {
416 printk("error probing irq (bad) \n");
420 i
= inb(dev
->iobase
+ DT2811_ADDATLO
);
421 i
= inb(dev
->iobase
+ DT2811_ADDATHI
);
422 printk("(irq = %d)\n", irq
);
423 ret
= request_irq(irq
, dt2811_interrupt
, 0,
428 } else if (irq
== 0) {
429 printk("(no irq)\n");
431 printk("( multiple irq's -- this is bad! )\n");
436 ret
= alloc_subdevices(dev
, 4);
440 ret
= alloc_private(dev
, sizeof(struct dt2811_private
));
444 switch (it
->options
[2]) {
446 devpriv
->adc_mux
= adc_singleended
;
449 devpriv
->adc_mux
= adc_diff
;
452 devpriv
->adc_mux
= adc_pseudo_diff
;
455 devpriv
->adc_mux
= adc_singleended
;
458 switch (it
->options
[4]) {
460 devpriv
->dac_range
[0] = dac_bipolar_5
;
463 devpriv
->dac_range
[0] = dac_bipolar_2_5
;
466 devpriv
->dac_range
[0] = dac_unipolar_5
;
469 devpriv
->dac_range
[0] = dac_bipolar_5
;
472 switch (it
->options
[5]) {
474 devpriv
->dac_range
[1] = dac_bipolar_5
;
477 devpriv
->dac_range
[1] = dac_bipolar_2_5
;
480 devpriv
->dac_range
[1] = dac_unipolar_5
;
483 devpriv
->dac_range
[1] = dac_bipolar_5
;
487 s
= dev
->subdevices
+ 0;
488 /* initialize the ADC subdevice */
489 s
->type
= COMEDI_SUBD_AI
;
490 s
->subdev_flags
= SDF_READABLE
| SDF_GROUND
;
491 s
->n_chan
= devpriv
->adc_mux
== adc_diff
? 8 : 16;
492 s
->insn_read
= dt2811_ai_insn
;
494 switch (it
->options
[3]) {
497 s
->range_table
= this_board
->bip_5
;
500 s
->range_table
= this_board
->bip_2_5
;
503 s
->range_table
= this_board
->unip_5
;
507 s
= dev
->subdevices
+ 1;
509 s
->type
= COMEDI_SUBD_AO
;
510 s
->subdev_flags
= SDF_WRITABLE
;
512 s
->insn_write
= dt2811_ao_insn
;
513 s
->insn_read
= dt2811_ao_insn_read
;
515 s
->range_table_list
= devpriv
->range_type_list
;
516 devpriv
->range_type_list
[0] = dac_range_types
[devpriv
->dac_range
[0]];
517 devpriv
->range_type_list
[1] = dac_range_types
[devpriv
->dac_range
[1]];
519 s
= dev
->subdevices
+ 2;
521 s
->type
= COMEDI_SUBD_DI
;
522 s
->subdev_flags
= SDF_READABLE
;
524 s
->insn_bits
= dt2811_di_insn_bits
;
526 s
->range_table
= &range_digital
;
528 s
= dev
->subdevices
+ 3;
530 s
->type
= COMEDI_SUBD_DO
;
531 s
->subdev_flags
= SDF_WRITABLE
;
533 s
->insn_bits
= dt2811_do_insn_bits
;
536 s
->range_table
= &range_digital
;
541 static int dt2811_detach(struct comedi_device
*dev
)
543 printk("comedi%d: dt2811: remove\n", dev
->minor
);
546 free_irq(dev
->irq
, dev
);
549 release_region(dev
->iobase
, DT2811_SIZE
);
555 static int dt2811_ai_insn(struct comedi_device
*dev
, struct comedi_subdevice
*s
,
556 struct comedi_insn
*insn
, unsigned int *data
)
558 int chan
= CR_CHAN(insn
->chanspec
);
559 int timeout
= DT2811_TIMEOUT
;
562 for (i
= 0; i
< insn
->n
; i
++) {
563 outb(chan
, dev
->iobase
+ DT2811_ADGCR
);
566 && inb(dev
->iobase
+ DT2811_ADCSR
) & DT2811_ADBUSY
)
571 data
[i
] = inb(dev
->iobase
+ DT2811_ADDATLO
);
572 data
[i
] |= inb(dev
->iobase
+ DT2811_ADDATHI
) << 8;
580 /* Wow. This is code from the Comedi stone age. But it hasn't been
581 * replaced, so I'll let it stay. */
582 int dt2811_adtrig(kdev_t minor
, comedi_adtrig
* adtrig
)
584 struct comedi_device
*dev
= comedi_devices
+ minor
;
588 dev
->curadchan
= adtrig
->chan
;
589 switch (dev
->i_admode
) {
591 dev
->ntrig
= adtrig
->n
- 1;
592 /*printk("dt2811: AD soft trigger\n"); */
593 /*outb(DT2811_CLRERROR|DT2811_INTENB,dev->iobase+DT2811_ADCSR); *//* not neccessary */
594 outb(dev
->curadchan
, dev
->iobase
+ DT2811_ADGCR
);
595 do_gettimeofday(&trigtime
);
598 dev
->ntrig
= adtrig
->n
;
606 static int dt2811_ao_insn(struct comedi_device
*dev
, struct comedi_subdevice
*s
,
607 struct comedi_insn
*insn
, unsigned int *data
)
612 chan
= CR_CHAN(insn
->chanspec
);
614 for (i
= 0; i
< insn
->n
; i
++) {
615 outb(data
[i
] & 0xff, dev
->iobase
+ DT2811_DADAT0LO
+ 2 * chan
);
616 outb((data
[i
] >> 8) & 0xff,
617 dev
->iobase
+ DT2811_DADAT0HI
+ 2 * chan
);
618 devpriv
->ao_readback
[chan
] = data
[i
];
624 static int dt2811_ao_insn_read(struct comedi_device
*dev
,
625 struct comedi_subdevice
*s
,
626 struct comedi_insn
*insn
, unsigned int *data
)
631 chan
= CR_CHAN(insn
->chanspec
);
633 for (i
= 0; i
< insn
->n
; i
++) {
634 data
[i
] = devpriv
->ao_readback
[chan
];
640 static int dt2811_di_insn_bits(struct comedi_device
*dev
,
641 struct comedi_subdevice
*s
,
642 struct comedi_insn
*insn
, unsigned int *data
)
647 data
[1] = inb(dev
->iobase
+ DT2811_DIO
);
652 static int dt2811_do_insn_bits(struct comedi_device
*dev
,
653 struct comedi_subdevice
*s
,
654 struct comedi_insn
*insn
, unsigned int *data
)
659 s
->state
&= ~data
[0];
660 s
->state
|= data
[0] & data
[1];
661 outb(s
->state
, dev
->iobase
+ DT2811_DIO
);