• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2    comedi/drivers/dt2811.c
3    Hardware driver for Data Translation DT2811
4 
5    COMEDI - Linux Control and Measurement Device Interface
6    History:
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 ...
11 
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.
16 
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.
21 
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.
25  */
26 /*
27 Driver: dt2811
28 Description: Data Translation DT2811
29 Author: ds
30 Devices: [Data Translation] DT2811-PGL (dt2811-pgl), DT2811-PGH (dt2811-pgh)
31 Status: works
32 
33 Configuration options:
34   [0] - I/O port base address
35   [1] - IRQ, although this is currently unused
36   [2] - A/D reference
37 	  0 = signle-ended
38 	  1 = differential
39 	  2 = pseudo-differential (common reference)
40   [3] - A/D range
41 	  0 = [-5, 5]
42 	  1 = [-2.5, 2.5]
43 	  2 = [0, 5]
44   [4] - D/A 0 range (same choices)
45   [4] - D/A 1 range (same choices)
46 */
47 
48 #include <linux/interrupt.h>
49 #include "../comedidev.h"
50 
51 #include <linux/ioport.h>
52 
53 static const char *driver_name = "dt2811";
54 
55 static const struct comedi_lrange range_dt2811_pgh_ai_5_unipolar = {
56 	4, {
57 		RANGE(0, 5),
58 		RANGE(0, 2.5),
59 		RANGE(0, 1.25),
60 		RANGE(0, 0.625)
61 	}
62 };
63 
64 static const struct comedi_lrange range_dt2811_pgh_ai_2_5_bipolar = {
65 	4, {
66 		RANGE(-2.5, 2.5),
67 		RANGE(-1.25, 1.25),
68 		RANGE(-0.625, 0.625),
69 		RANGE(-0.3125, 0.3125)
70 	}
71 };
72 
73 static const struct comedi_lrange range_dt2811_pgh_ai_5_bipolar = {
74 	4, {
75 		RANGE(-5, 5),
76 		RANGE(-2.5, 2.5),
77 		RANGE(-1.25, 1.25),
78 		RANGE(-0.625, 0.625)
79 	}
80 };
81 
82 static const struct comedi_lrange range_dt2811_pgl_ai_5_unipolar = {
83 	4, {
84 		RANGE(0, 5),
85 		RANGE(0, 0.5),
86 		RANGE(0, 0.05),
87 		RANGE(0, 0.01)
88 	}
89 };
90 
91 static const struct comedi_lrange range_dt2811_pgl_ai_2_5_bipolar = {
92 	4, {
93 		RANGE(-2.5, 2.5),
94 		RANGE(-0.25, 0.25),
95 		RANGE(-0.025, 0.025),
96 		RANGE(-0.005, 0.005)
97 	}
98 };
99 
100 static const struct comedi_lrange range_dt2811_pgl_ai_5_bipolar = {
101 	4, {
102 		RANGE(-5, 5),
103 		RANGE(-0.5, 0.5),
104 		RANGE(-0.05, 0.05),
105 		RANGE(-0.01, 0.01)
106 	}
107 };
108 
109 /*
110 
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
114    (W) ignored
115    bit 6 - (R) 1 indicates A/D error
116    (W) ignored
117    bit 5 - (R) 1 indicates A/D busy, cleared at end
118    of conversion
119    (W) ignored
120    bit 4 - (R) 0
121    (W)
122    bit 3 - (R) 0
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,
129    external trigger
130    11  continuous conversion, external clock,
131    external trigger
132 
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
139    bit 4,5 - reserved
140    bit 3-0 - (R/W) channel select
141    channel number from 0-15
142 
143    0x02,0x03 (R) ADDAT A/D Data Register
144    (W) DADAT0 D/A Data Register 0
145    0x02 low byte
146    0x03 high byte
147 
148    0x04,0x05 (W) DADAT0 D/A Data Register 1
149 
150    0x06 (R) DIO0 Digital Input Port 0
151    (W) DIO1 Digital Output Port 1
152 
153    0x07 TMRCTR (R/W) Timer/Counter Register
154    bits 6,7 - reserved
155    bits 5-3 - Timer frequency control (mantissa)
156    543  divisor  freqency (kHz)
157    000  1        600
158    001  10       60
159    010  2        300
160    011  3        200
161    100  4        150
162    101  5        120
163    110  6        100
164    111  12       50
165    bits 2-0 - Timer frequency control (exponent)
166    210  multiply divisor/divide frequency by
167    000  1
168    001  10
169    010  100
170    011  1000
171    100  10000
172    101  100000
173    110  1000000
174    111  10000000
175 
176  */
177 
178 #define TIMEOUT 10000
179 
180 #define DT2811_SIZE 8
181 
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
190 #define DT2811_DIO 6
191 #define DT2811_TMRCTR 7
192 
193 /*
194  * flags
195  */
196 
197 /* ADCSR */
198 
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
205 
206 struct dt2811_board {
207 
208 	const char *name;
209 	const struct comedi_lrange *bip_5;
210 	const struct comedi_lrange *bip_2_5;
211 	const struct comedi_lrange *unip_5;
212 };
213 
214 static const struct dt2811_board boardtypes[] = {
215 	{"dt2811-pgh",
216 	 &range_dt2811_pgh_ai_5_bipolar,
217 	 &range_dt2811_pgh_ai_2_5_bipolar,
218 	 &range_dt2811_pgh_ai_5_unipolar,
219 	 },
220 	{"dt2811-pgl",
221 	 &range_dt2811_pgl_ai_5_bipolar,
222 	 &range_dt2811_pgl_ai_2_5_bipolar,
223 	 &range_dt2811_pgl_ai_5_unipolar,
224 	 },
225 };
226 
227 #define this_board ((const struct dt2811_board *)dev->board_ptr)
228 
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),
240 };
241 
driver_dt2811_init_module(void)242 static int __init driver_dt2811_init_module(void)
243 {
244 	return comedi_driver_register(&driver_dt2811);
245 }
246 
driver_dt2811_cleanup_module(void)247 static void __exit driver_dt2811_cleanup_module(void)
248 {
249 	comedi_driver_unregister(&driver_dt2811);
250 }
251 
252 module_init(driver_dt2811_init_module);
253 module_exit(driver_dt2811_cleanup_module);
254 
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);
268 
269 enum { card_2811_pgh, card_2811_pgl };
270 
271 struct dt2811_private {
272 	int ntrig;
273 	int curadchan;
274 	enum {
275 		adc_singleended, adc_diff, adc_pseudo_diff
276 	} adc_mux;
277 	enum {
278 		dac_bipolar_5, dac_bipolar_2_5, dac_unipolar_5
279 	} dac_range[2];
280 	const struct comedi_lrange *range_type_list[2];
281 	unsigned int ao_readback[2];
282 };
283 
284 #define devpriv ((struct dt2811_private *)dev->private)
285 
286 static const struct comedi_lrange *dac_range_types[] = {
287 	&range_bipolar5,
288 	&range_bipolar2_5,
289 	&range_unipolar5
290 };
291 
292 #define DT2811_TIMEOUT 5
293 
294 #if 0
295 static irqreturn_t dt2811_interrupt(int irq, void *d)
296 {
297 	int lo, hi;
298 	int data;
299 	struct comedi_device *dev = d;
300 
301 	if (!dev->attached) {
302 		comedi_error(dev, "spurious interrupt");
303 		return IRQ_HANDLED;
304 	}
305 
306 	lo = inb(dev->iobase + DT2811_ADDATLO);
307 	hi = inb(dev->iobase + DT2811_ADDATHI);
308 
309 	data = lo + (hi << 8);
310 
311 	if (!(--devpriv->ntrig)) {
312 		/* how to turn off acquisition */
313 		s->async->events |= COMEDI_SB_EOA;
314 	}
315 	comedi_event(dev, s);
316 	return IRQ_HANDLED;
317 }
318 #endif
319 
320 /*
321   options[0]   Board base address
322   options[1]   IRQ
323   options[2]   Input configuration
324 		 0 == single-ended
325 		 1 == differential
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)
339 */
340 
dt2811_attach(struct comedi_device * dev,struct comedi_devconfig * it)341 static int dt2811_attach(struct comedi_device *dev, struct comedi_devconfig *it)
342 {
343 	/* int i, irq; */
344 	/* unsigned long irqs; */
345 	/* long flags; */
346 
347 	int ret;
348 	struct comedi_subdevice *s;
349 	unsigned long iobase;
350 
351 	iobase = it->options[0];
352 
353 	printk(KERN_INFO "comedi%d: dt2811:base=0x%04lx\n", dev->minor, iobase);
354 
355 	if (!request_region(iobase, DT2811_SIZE, driver_name)) {
356 		printk(KERN_ERR "I/O port conflict\n");
357 		return -EIO;
358 	}
359 
360 	dev->iobase = iobase;
361 	dev->board_name = this_board->name;
362 
363 #if 0
364 	outb(0, dev->iobase + DT2811_ADCSR);
365 	udelay(100);
366 	i = inb(dev->iobase + DT2811_ADDATLO);
367 	i = inb(dev->iobase + DT2811_ADDATHI);
368 #endif
369 
370 #if 0
371 	irq = it->options[1];
372 	if (irq < 0) {
373 		save_flags(flags);
374 		sti();
375 		irqs = probe_irq_on();
376 
377 		outb(DT2811_CLRERROR | DT2811_INTENB,
378 		     dev->iobase + DT2811_ADCSR);
379 		outb(0, dev->iobase + DT2811_ADGCR);
380 
381 		udelay(100);
382 
383 		irq = probe_irq_off(irqs);
384 		restore_flags(flags);
385 
386 		/*outb(DT2811_CLRERROR|DT2811_INTENB,
387 			dev->iobase+DT2811_ADCSR);*/
388 
389 		if (inb(dev->iobase + DT2811_ADCSR) & DT2811_ADERROR)
390 			printk(KERN_ERR "error probing irq (bad)\n");
391 		dev->irq = 0;
392 		if (irq > 0) {
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,
397 					  driver_name, dev);
398 			if (ret < 0)
399 				return -EIO;
400 			dev->irq = irq;
401 		} else if (irq == 0) {
402 			printk(KERN_INFO "(no irq)\n");
403 		} else {
404 			printk(KERN_ERR "( multiple irq's -- this is bad! )\n");
405 		}
406 	}
407 #endif
408 
409 	ret = alloc_subdevices(dev, 4);
410 	if (ret < 0)
411 		return ret;
412 
413 	ret = alloc_private(dev, sizeof(struct dt2811_private));
414 	if (ret < 0)
415 		return ret;
416 
417 	switch (it->options[2]) {
418 	case 0:
419 		devpriv->adc_mux = adc_singleended;
420 		break;
421 	case 1:
422 		devpriv->adc_mux = adc_diff;
423 		break;
424 	case 2:
425 		devpriv->adc_mux = adc_pseudo_diff;
426 		break;
427 	default:
428 		devpriv->adc_mux = adc_singleended;
429 		break;
430 	}
431 	switch (it->options[4]) {
432 	case 0:
433 		devpriv->dac_range[0] = dac_bipolar_5;
434 		break;
435 	case 1:
436 		devpriv->dac_range[0] = dac_bipolar_2_5;
437 		break;
438 	case 2:
439 		devpriv->dac_range[0] = dac_unipolar_5;
440 		break;
441 	default:
442 		devpriv->dac_range[0] = dac_bipolar_5;
443 		break;
444 	}
445 	switch (it->options[5]) {
446 	case 0:
447 		devpriv->dac_range[1] = dac_bipolar_5;
448 		break;
449 	case 1:
450 		devpriv->dac_range[1] = dac_bipolar_2_5;
451 		break;
452 	case 2:
453 		devpriv->dac_range[1] = dac_unipolar_5;
454 		break;
455 	default:
456 		devpriv->dac_range[1] = dac_bipolar_5;
457 		break;
458 	}
459 
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;
466 	s->maxdata = 0xfff;
467 	switch (it->options[3]) {
468 	case 0:
469 	default:
470 		s->range_table = this_board->bip_5;
471 		break;
472 	case 1:
473 		s->range_table = this_board->bip_2_5;
474 		break;
475 	case 2:
476 		s->range_table = this_board->unip_5;
477 		break;
478 	}
479 
480 	s = dev->subdevices + 1;
481 	/* ao subdevice */
482 	s->type = COMEDI_SUBD_AO;
483 	s->subdev_flags = SDF_WRITABLE;
484 	s->n_chan = 2;
485 	s->insn_write = dt2811_ao_insn;
486 	s->insn_read = dt2811_ao_insn_read;
487 	s->maxdata = 0xfff;
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]];
491 
492 	s = dev->subdevices + 2;
493 	/* di subdevice */
494 	s->type = COMEDI_SUBD_DI;
495 	s->subdev_flags = SDF_READABLE;
496 	s->n_chan = 8;
497 	s->insn_bits = dt2811_di_insn_bits;
498 	s->maxdata = 1;
499 	s->range_table = &range_digital;
500 
501 	s = dev->subdevices + 3;
502 	/* do subdevice */
503 	s->type = COMEDI_SUBD_DO;
504 	s->subdev_flags = SDF_WRITABLE;
505 	s->n_chan = 8;
506 	s->insn_bits = dt2811_do_insn_bits;
507 	s->maxdata = 1;
508 	s->state = 0;
509 	s->range_table = &range_digital;
510 
511 	return 0;
512 }
513 
dt2811_detach(struct comedi_device * dev)514 static int dt2811_detach(struct comedi_device *dev)
515 {
516 	printk(KERN_INFO "comedi%d: dt2811: remove\n", dev->minor);
517 
518 	if (dev->irq)
519 		free_irq(dev->irq, dev);
520 	if (dev->iobase)
521 		release_region(dev->iobase, DT2811_SIZE);
522 
523 	return 0;
524 }
525 
dt2811_ai_insn(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)526 static int dt2811_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
527 			  struct comedi_insn *insn, unsigned int *data)
528 {
529 	int chan = CR_CHAN(insn->chanspec);
530 	int timeout = DT2811_TIMEOUT;
531 	int i;
532 
533 	for (i = 0; i < insn->n; i++) {
534 		outb(chan, dev->iobase + DT2811_ADGCR);
535 
536 		while (timeout
537 		       && inb(dev->iobase + DT2811_ADCSR) & DT2811_ADBUSY)
538 			timeout--;
539 		if (!timeout)
540 			return -ETIME;
541 
542 		data[i] = inb(dev->iobase + DT2811_ADDATLO);
543 		data[i] |= inb(dev->iobase + DT2811_ADDATHI) << 8;
544 		data[i] &= 0xfff;
545 	}
546 
547 	return i;
548 }
549 
550 #if 0
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)
554 {
555 	struct comedi_device *dev = comedi_devices + minor;
556 
557 	if (adtrig->n < 1)
558 		return 0;
559 	dev->curadchan = adtrig->chan;
560 	switch (dev->i_admode) {
561 	case COMEDI_MDEMAND:
562 		dev->ntrig = adtrig->n - 1;
563 		/* not necessary */
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);
569 		break;
570 	case COMEDI_MCONTS:
571 		dev->ntrig = adtrig->n;
572 		break;
573 	}
574 
575 	return 0;
576 }
577 #endif
578 
dt2811_ao_insn(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)579 static int dt2811_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
580 			  struct comedi_insn *insn, unsigned int *data)
581 {
582 	int i;
583 	int chan;
584 
585 	chan = CR_CHAN(insn->chanspec);
586 
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];
592 	}
593 
594 	return i;
595 }
596 
dt2811_ao_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)597 static int dt2811_ao_insn_read(struct comedi_device *dev,
598 			       struct comedi_subdevice *s,
599 			       struct comedi_insn *insn, unsigned int *data)
600 {
601 	int i;
602 	int chan;
603 
604 	chan = CR_CHAN(insn->chanspec);
605 
606 	for (i = 0; i < insn->n; i++)
607 		data[i] = devpriv->ao_readback[chan];
608 
609 	return i;
610 }
611 
dt2811_di_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)612 static int dt2811_di_insn_bits(struct comedi_device *dev,
613 			       struct comedi_subdevice *s,
614 			       struct comedi_insn *insn, unsigned int *data)
615 {
616 	if (insn->n != 2)
617 		return -EINVAL;
618 
619 	data[1] = inb(dev->iobase + DT2811_DIO);
620 
621 	return 2;
622 }
623 
dt2811_do_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)624 static int dt2811_do_insn_bits(struct comedi_device *dev,
625 			       struct comedi_subdevice *s,
626 			       struct comedi_insn *insn, unsigned int *data)
627 {
628 	if (insn->n != 2)
629 		return -EINVAL;
630 
631 	s->state &= ~data[0];
632 	s->state |= data[0] & data[1];
633 	outb(s->state, dev->iobase + DT2811_DIO);
634 
635 	data[1] = s->state;
636 
637 	return 2;
638 }
639 
640 MODULE_AUTHOR("Comedi http://www.comedi.org");
641 MODULE_DESCRIPTION("Comedi low-level driver");
642 MODULE_LICENSE("GPL");
643