dt2811.c revision 0a85b6f0ab0d2edb0d41b32697111ce0e4f43496
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/*
27Driver: dt2811
28Description: Data Translation DT2811
29Author: ds
30Devices: [Data Translation] DT2811-PGL (dt2811-pgl), DT2811-PGH (dt2811-pgh)
31Status: works
32
33Configuration 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
53static const char *driver_name = "dt2811";
54
55static const struct comedi_lrange range_dt2811_pgh_ai_5_unipolar = { 4, {
56									 RANGE
57									 (0, 5),
58									 RANGE
59									 (0,
60									  2.5),
61									 RANGE
62									 (0,
63									  1.25),
64									 RANGE
65									 (0,
66									  0.625)
67									 }
68};
69
70static const struct comedi_lrange range_dt2811_pgh_ai_2_5_bipolar = { 4, {
71									  RANGE
72									  (-2.5,
73									   2.5),
74									  RANGE
75									  (-1.25,
76									   1.25),
77									  RANGE
78									  (-0.625,
79									   0.625),
80									  RANGE
81									  (-0.3125,
82									   0.3125)
83									  }
84};
85
86static const struct comedi_lrange range_dt2811_pgh_ai_5_bipolar = { 4, {
87									RANGE
88									(-5, 5),
89									RANGE
90									(-2.5,
91									 2.5),
92									RANGE
93									(-1.25,
94									 1.25),
95									RANGE
96									(-0.625,
97									 0.625)
98									}
99};
100
101static const struct comedi_lrange range_dt2811_pgl_ai_5_unipolar = { 4, {
102									 RANGE
103									 (0, 5),
104									 RANGE
105									 (0,
106									  0.5),
107									 RANGE
108									 (0,
109									  0.05),
110									 RANGE
111									 (0,
112									  0.01)
113									 }
114};
115
116static const struct comedi_lrange range_dt2811_pgl_ai_2_5_bipolar = { 4, {
117									  RANGE
118									  (-2.5,
119									   2.5),
120									  RANGE
121									  (-0.25,
122									   0.25),
123									  RANGE
124									  (-0.025,
125									   0.025),
126									  RANGE
127									  (-0.005,
128									   0.005)
129									  }
130};
131
132static const struct comedi_lrange range_dt2811_pgl_ai_5_bipolar = { 4, {
133									RANGE
134									(-5, 5),
135									RANGE
136									(-0.5,
137									 0.5),
138									RANGE
139									(-0.05,
140									 0.05),
141									RANGE
142									(-0.01,
143									 0.01)
144									}
145};
146
147/*
148
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
152   (W) ignored
153   bit 6 - (R) 1 indicates A/D error
154   (W) ignored
155   bit 5 - (R) 1 indicates A/D busy, cleared at end
156   of conversion
157   (W) ignored
158   bit 4 - (R) 0
159   (W)
160   bit 3 - (R) 0
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,
167   external trigger
168   11  continuous conversion, external clock,
169   external trigger
170
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
177   bit 4,5 - reserved
178   bit 3-0 - (R/W) channel select
179   channel number from 0-15
180
181   0x02,0x03 (R) ADDAT A/D Data Register
182   (W) DADAT0 D/A Data Register 0
183   0x02 low byte
184   0x03 high byte
185
186   0x04,0x05 (W) DADAT0 D/A Data Register 1
187
188   0x06 (R) DIO0 Digital Input Port 0
189   (W) DIO1 Digital Output Port 1
190
191   0x07 TMRCTR (R/W) Timer/Counter Register
192   bits 6,7 - reserved
193   bits 5-3 - Timer frequency control (mantissa)
194   543  divisor  freqency (kHz)
195   000  1        600
196   001  10       60
197   010  2        300
198   011  3        200
199   100  4        150
200   101  5        120
201   110  6        100
202   111  12       50
203   bits 2-0 - Timer frequency control (exponent)
204   210  multiply divisor/divide frequency by
205   000  1
206   001  10
207   010  100
208   011  1000
209   100  10000
210   101  100000
211   110  1000000
212   111  10000000
213
214 */
215
216#define TIMEOUT 10000
217
218#define DT2811_SIZE 8
219
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
228#define DT2811_DIO 6
229#define DT2811_TMRCTR 7
230
231/*
232 * flags
233 */
234
235/* ADCSR */
236
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
243
244struct dt2811_board {
245
246	const char *name;
247	const struct comedi_lrange *bip_5;
248	const struct comedi_lrange *bip_2_5;
249	const struct comedi_lrange *unip_5;
250};
251
252static const struct dt2811_board boardtypes[] = {
253	{"dt2811-pgh",
254	 &range_dt2811_pgh_ai_5_bipolar,
255	 &range_dt2811_pgh_ai_2_5_bipolar,
256	 &range_dt2811_pgh_ai_5_unipolar,
257	 },
258	{"dt2811-pgl",
259	 &range_dt2811_pgl_ai_5_bipolar,
260	 &range_dt2811_pgl_ai_2_5_bipolar,
261	 &range_dt2811_pgl_ai_5_unipolar,
262	 },
263};
264
265#define this_board ((const struct dt2811_board *)dev->board_ptr)
266
267static int dt2811_attach(struct comedi_device *dev,
268			 struct comedi_devconfig *it);
269static int dt2811_detach(struct comedi_device *dev);
270static 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),
278};
279
280COMEDI_INITCLEANUP(driver_dt2811);
281
282static int dt2811_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
283			  struct comedi_insn *insn, unsigned int *data);
284static int dt2811_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
285			  struct comedi_insn *insn, unsigned int *data);
286static int dt2811_ao_insn_read(struct comedi_device *dev,
287			       struct comedi_subdevice *s,
288			       struct comedi_insn *insn, unsigned int *data);
289static int dt2811_di_insn_bits(struct comedi_device *dev,
290			       struct comedi_subdevice *s,
291			       struct comedi_insn *insn, unsigned int *data);
292static int dt2811_do_insn_bits(struct comedi_device *dev,
293			       struct comedi_subdevice *s,
294			       struct comedi_insn *insn, unsigned int *data);
295
296enum { card_2811_pgh, card_2811_pgl };
297
298struct dt2811_private {
299	int ntrig;
300	int curadchan;
301	enum {
302		adc_singleended, adc_diff, adc_pseudo_diff
303	} adc_mux;
304	enum {
305		dac_bipolar_5, dac_bipolar_2_5, dac_unipolar_5
306	} dac_range[2];
307	const struct comedi_lrange *range_type_list[2];
308	unsigned int ao_readback[2];
309};
310
311#define devpriv ((struct dt2811_private *)dev->private)
312
313static const struct comedi_lrange *dac_range_types[] = {
314	&range_bipolar5,
315	&range_bipolar2_5,
316	&range_unipolar5
317};
318
319#define DT2811_TIMEOUT 5
320
321#if 0
322static irqreturn_t dt2811_interrupt(int irq, void *d)
323{
324	int lo, hi;
325	int data;
326	struct comedi_device *dev = d;
327
328	if (!dev->attached) {
329		comedi_error(dev, "spurious interrupt");
330		return IRQ_HANDLED;
331	}
332
333	lo = inb(dev->iobase + DT2811_ADDATLO);
334	hi = inb(dev->iobase + DT2811_ADDATHI);
335
336	data = lo + (hi << 8);
337
338	if (!(--devpriv->ntrig)) {
339		/* how to turn off acquisition */
340		s->async->events |= COMEDI_SB_EOA;
341	}
342	comedi_event(dev, s);
343	return IRQ_HANDLED;
344}
345#endif
346
347/*
348  options[0]   Board base address
349  options[1]   IRQ
350  options[2]   Input configuration
351                 0 == single-ended
352                 1 == differential
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)
366*/
367
368static int dt2811_attach(struct comedi_device *dev, struct comedi_devconfig *it)
369{
370	/* int i, irq; */
371	/* unsigned long irqs; */
372	/* long flags; */
373
374	int ret;
375	struct comedi_subdevice *s;
376	unsigned long iobase;
377
378	iobase = it->options[0];
379
380	printk("comedi%d: dt2811: base=0x%04lx\n", dev->minor, iobase);
381
382	if (!request_region(iobase, DT2811_SIZE, driver_name)) {
383		printk("I/O port conflict\n");
384		return -EIO;
385	}
386
387	dev->iobase = iobase;
388	dev->board_name = this_board->name;
389
390#if 0
391	outb(0, dev->iobase + DT2811_ADCSR);
392	udelay(100);
393	i = inb(dev->iobase + DT2811_ADDATLO);
394	i = inb(dev->iobase + DT2811_ADDATHI);
395#endif
396
397#if 0
398	irq = it->options[1];
399	if (irq < 0) {
400		save_flags(flags);
401		sti();
402		irqs = probe_irq_on();
403
404		outb(DT2811_CLRERROR | DT2811_INTENB,
405		     dev->iobase + DT2811_ADCSR);
406		outb(0, dev->iobase + DT2811_ADGCR);
407
408		udelay(100);
409
410		irq = probe_irq_off(irqs);
411		restore_flags(flags);
412
413		/*outb(DT2811_CLRERROR|DT2811_INTENB,dev->iobase+DT2811_ADCSR); */
414
415		if (inb(dev->iobase + DT2811_ADCSR) & DT2811_ADERROR) {
416			printk("error probing irq (bad) \n");
417		}
418		dev->irq = 0;
419		if (irq > 0) {
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,
424					  driver_name, dev);
425			if (ret < 0)
426				return -EIO;
427			dev->irq = irq;
428		} else if (irq == 0) {
429			printk("(no irq)\n");
430		} else {
431			printk("( multiple irq's -- this is bad! )\n");
432		}
433	}
434#endif
435
436	ret = alloc_subdevices(dev, 4);
437	if (ret < 0)
438		return ret;
439
440	ret = alloc_private(dev, sizeof(struct dt2811_private));
441	if (ret < 0)
442		return ret;
443
444	switch (it->options[2]) {
445	case 0:
446		devpriv->adc_mux = adc_singleended;
447		break;
448	case 1:
449		devpriv->adc_mux = adc_diff;
450		break;
451	case 2:
452		devpriv->adc_mux = adc_pseudo_diff;
453		break;
454	default:
455		devpriv->adc_mux = adc_singleended;
456		break;
457	}
458	switch (it->options[4]) {
459	case 0:
460		devpriv->dac_range[0] = dac_bipolar_5;
461		break;
462	case 1:
463		devpriv->dac_range[0] = dac_bipolar_2_5;
464		break;
465	case 2:
466		devpriv->dac_range[0] = dac_unipolar_5;
467		break;
468	default:
469		devpriv->dac_range[0] = dac_bipolar_5;
470		break;
471	}
472	switch (it->options[5]) {
473	case 0:
474		devpriv->dac_range[1] = dac_bipolar_5;
475		break;
476	case 1:
477		devpriv->dac_range[1] = dac_bipolar_2_5;
478		break;
479	case 2:
480		devpriv->dac_range[1] = dac_unipolar_5;
481		break;
482	default:
483		devpriv->dac_range[1] = dac_bipolar_5;
484		break;
485	}
486
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;
493	s->maxdata = 0xfff;
494	switch (it->options[3]) {
495	case 0:
496	default:
497		s->range_table = this_board->bip_5;
498		break;
499	case 1:
500		s->range_table = this_board->bip_2_5;
501		break;
502	case 2:
503		s->range_table = this_board->unip_5;
504		break;
505	}
506
507	s = dev->subdevices + 1;
508	/* ao subdevice */
509	s->type = COMEDI_SUBD_AO;
510	s->subdev_flags = SDF_WRITABLE;
511	s->n_chan = 2;
512	s->insn_write = dt2811_ao_insn;
513	s->insn_read = dt2811_ao_insn_read;
514	s->maxdata = 0xfff;
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]];
518
519	s = dev->subdevices + 2;
520	/* di subdevice */
521	s->type = COMEDI_SUBD_DI;
522	s->subdev_flags = SDF_READABLE;
523	s->n_chan = 8;
524	s->insn_bits = dt2811_di_insn_bits;
525	s->maxdata = 1;
526	s->range_table = &range_digital;
527
528	s = dev->subdevices + 3;
529	/* do subdevice */
530	s->type = COMEDI_SUBD_DO;
531	s->subdev_flags = SDF_WRITABLE;
532	s->n_chan = 8;
533	s->insn_bits = dt2811_do_insn_bits;
534	s->maxdata = 1;
535	s->state = 0;
536	s->range_table = &range_digital;
537
538	return 0;
539}
540
541static int dt2811_detach(struct comedi_device *dev)
542{
543	printk("comedi%d: dt2811: remove\n", dev->minor);
544
545	if (dev->irq) {
546		free_irq(dev->irq, dev);
547	}
548	if (dev->iobase) {
549		release_region(dev->iobase, DT2811_SIZE);
550	}
551
552	return 0;
553}
554
555static int dt2811_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
556			  struct comedi_insn *insn, unsigned int *data)
557{
558	int chan = CR_CHAN(insn->chanspec);
559	int timeout = DT2811_TIMEOUT;
560	int i;
561
562	for (i = 0; i < insn->n; i++) {
563		outb(chan, dev->iobase + DT2811_ADGCR);
564
565		while (timeout
566		       && inb(dev->iobase + DT2811_ADCSR) & DT2811_ADBUSY)
567			timeout--;
568		if (!timeout)
569			return -ETIME;
570
571		data[i] = inb(dev->iobase + DT2811_ADDATLO);
572		data[i] |= inb(dev->iobase + DT2811_ADDATHI) << 8;
573		data[i] &= 0xfff;
574	}
575
576	return i;
577}
578
579#if 0
580/* Wow.  This is code from the Comedi stone age.  But it hasn't been
581 * replaced, so I'll let it stay. */
582int dt2811_adtrig(kdev_t minor, comedi_adtrig * adtrig)
583{
584	struct comedi_device *dev = comedi_devices + minor;
585
586	if (adtrig->n < 1)
587		return 0;
588	dev->curadchan = adtrig->chan;
589	switch (dev->i_admode) {
590	case COMEDI_MDEMAND:
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);
596		break;
597	case COMEDI_MCONTS:
598		dev->ntrig = adtrig->n;
599		break;
600	}
601
602	return 0;
603}
604#endif
605
606static int dt2811_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
607			  struct comedi_insn *insn, unsigned int *data)
608{
609	int i;
610	int chan;
611
612	chan = CR_CHAN(insn->chanspec);
613
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];
619	}
620
621	return i;
622}
623
624static int dt2811_ao_insn_read(struct comedi_device *dev,
625			       struct comedi_subdevice *s,
626			       struct comedi_insn *insn, unsigned int *data)
627{
628	int i;
629	int chan;
630
631	chan = CR_CHAN(insn->chanspec);
632
633	for (i = 0; i < insn->n; i++) {
634		data[i] = devpriv->ao_readback[chan];
635	}
636
637	return i;
638}
639
640static int dt2811_di_insn_bits(struct comedi_device *dev,
641			       struct comedi_subdevice *s,
642			       struct comedi_insn *insn, unsigned int *data)
643{
644	if (insn->n != 2)
645		return -EINVAL;
646
647	data[1] = inb(dev->iobase + DT2811_DIO);
648
649	return 2;
650}
651
652static int dt2811_do_insn_bits(struct comedi_device *dev,
653			       struct comedi_subdevice *s,
654			       struct comedi_insn *insn, unsigned int *data)
655{
656	if (insn->n != 2)
657		return -EINVAL;
658
659	s->state &= ~data[0];
660	s->state |= data[0] & data[1];
661	outb(s->state, dev->iobase + DT2811_DIO);
662
663	data[1] = s->state;
664
665	return 2;
666}
667