dt2811.c revision 7114a28011f9d5f3d981731ad341177c21f9d948
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 = {
56	4, {
57		RANGE(0, 5),
58		RANGE(0, 2.5),
59		RANGE(0, 1.25),
60		RANGE(0, 0.625)
61	}
62};
63
64static 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
73static 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
82static 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
91static 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
100static 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
206struct 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
214static 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
229static int dt2811_attach(struct comedi_device *dev,
230			 struct comedi_devconfig *it);
231static int dt2811_detach(struct comedi_device *dev);
232static 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
242static int __init driver_dt2811_init_module(void)
243{
244	return comedi_driver_register(&driver_dt2811);
245}
246
247static void __exit driver_dt2811_cleanup_module(void)
248{
249	comedi_driver_unregister(&driver_dt2811);
250}
251
252module_init(driver_dt2811_init_module);
253module_exit(driver_dt2811_cleanup_module);
254
255static int dt2811_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
256			  struct comedi_insn *insn, unsigned int *data);
257static int dt2811_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
258			  struct comedi_insn *insn, unsigned int *data);
259static int dt2811_ao_insn_read(struct comedi_device *dev,
260			       struct comedi_subdevice *s,
261			       struct comedi_insn *insn, unsigned int *data);
262static int dt2811_di_insn_bits(struct comedi_device *dev,
263			       struct comedi_subdevice *s,
264			       struct comedi_insn *insn, unsigned int *data);
265static int dt2811_do_insn_bits(struct comedi_device *dev,
266			       struct comedi_subdevice *s,
267			       struct comedi_insn *insn, unsigned int *data);
268
269enum { card_2811_pgh, card_2811_pgl };
270
271struct 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
286static 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
295static 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
341static 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
514static 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
526static 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. */
553int 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 neccessary */
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
579static 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
597static 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
612static 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
624static 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
640MODULE_AUTHOR("Comedi http://www.comedi.org");
641MODULE_DESCRIPTION("Comedi low-level driver");
642MODULE_LICENSE("GPL");
643