dt2811.c revision 42f1884d0683d0d0d4b0895aaed02dbed5d8b921
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 "../comedidev.h"
49
50#include <linux/ioport.h>
51
52static const char *driver_name = "dt2811";
53
54static const struct comedi_lrange range_dt2811_pgh_ai_5_unipolar = { 4, {
55			RANGE(0, 5),
56			RANGE(0, 2.5),
57			RANGE(0, 1.25),
58			RANGE(0, 0.625)
59	}
60};
61static const struct comedi_lrange range_dt2811_pgh_ai_2_5_bipolar = { 4, {
62			RANGE(-2.5, 2.5),
63			RANGE(-1.25, 1.25),
64			RANGE(-0.625, 0.625),
65			RANGE(-0.3125, 0.3125)
66	}
67};
68static const struct comedi_lrange range_dt2811_pgh_ai_5_bipolar = { 4, {
69			RANGE(-5, 5),
70			RANGE(-2.5, 2.5),
71			RANGE(-1.25, 1.25),
72			RANGE(-0.625, 0.625)
73	}
74};
75static const struct comedi_lrange range_dt2811_pgl_ai_5_unipolar = { 4, {
76			RANGE(0, 5),
77			RANGE(0, 0.5),
78			RANGE(0, 0.05),
79			RANGE(0, 0.01)
80	}
81};
82static const struct comedi_lrange range_dt2811_pgl_ai_2_5_bipolar = { 4, {
83			RANGE(-2.5, 2.5),
84			RANGE(-0.25, 0.25),
85			RANGE(-0.025, 0.025),
86			RANGE(-0.005, 0.005)
87	}
88};
89static const struct comedi_lrange range_dt2811_pgl_ai_5_bipolar = { 4, {
90			RANGE(-5, 5),
91			RANGE(-0.5, 0.5),
92			RANGE(-0.05, 0.05),
93			RANGE(-0.01, 0.01)
94	}
95};
96
97/*
98
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
102   (W) ignored
103   bit 6 - (R) 1 indicates A/D error
104   (W) ignored
105   bit 5 - (R) 1 indicates A/D busy, cleared at end
106   of conversion
107   (W) ignored
108   bit 4 - (R) 0
109   (W)
110   bit 3 - (R) 0
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,
117   external trigger
118   11  continuous conversion, external clock,
119   external trigger
120
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
127   bit 4,5 - reserved
128   bit 3-0 - (R/W) channel select
129   channel number from 0-15
130
131   0x02,0x03 (R) ADDAT A/D Data Register
132   (W) DADAT0 D/A Data Register 0
133   0x02 low byte
134   0x03 high byte
135
136   0x04,0x05 (W) DADAT0 D/A Data Register 1
137
138   0x06 (R) DIO0 Digital Input Port 0
139   (W) DIO1 Digital Output Port 1
140
141   0x07 TMRCTR (R/W) Timer/Counter Register
142   bits 6,7 - reserved
143   bits 5-3 - Timer frequency control (mantissa)
144   543  divisor  freqency (kHz)
145   000  1        600
146   001  10       60
147   010  2        300
148   011  3        200
149   100  4        150
150   101  5        120
151   110  6        100
152   111  12       50
153   bits 2-0 - Timer frequency control (exponent)
154   210  multiply divisor/divide frequency by
155   000  1
156   001  10
157   010  100
158   011  1000
159   100  10000
160   101  100000
161   110  1000000
162   111  10000000
163
164 */
165
166#define TIMEOUT 10000
167
168#define DT2811_SIZE 8
169
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
178#define DT2811_DIO 6
179#define DT2811_TMRCTR 7
180
181/*
182 * flags
183 */
184
185/* ADCSR */
186
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
193
194struct dt2811_board {
195
196	const char *name;
197	const struct comedi_lrange *bip_5;
198	const struct comedi_lrange *bip_2_5;
199	const struct comedi_lrange *unip_5;
200};
201
202static const struct dt2811_board boardtypes[] = {
203	{"dt2811-pgh",
204			&range_dt2811_pgh_ai_5_bipolar,
205			&range_dt2811_pgh_ai_2_5_bipolar,
206			&range_dt2811_pgh_ai_5_unipolar,
207		},
208	{"dt2811-pgl",
209			&range_dt2811_pgl_ai_5_bipolar,
210			&range_dt2811_pgl_ai_2_5_bipolar,
211			&range_dt2811_pgl_ai_5_unipolar,
212		},
213};
214
215#define this_board ((const struct dt2811_board *)dev->board_ptr)
216
217static int dt2811_attach(struct comedi_device * dev, struct comedi_devconfig * it);
218static int dt2811_detach(struct comedi_device * dev);
219static struct comedi_driver driver_dt2811 = {
220      driver_name:"dt2811",
221      module:THIS_MODULE,
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),
227};
228
229COMEDI_INITCLEANUP(driver_dt2811);
230
231static int dt2811_ai_insn(struct comedi_device * dev, struct comedi_subdevice * s,
232	struct comedi_insn * insn, unsigned int * data);
233static int dt2811_ao_insn(struct comedi_device * dev, struct comedi_subdevice * s,
234	struct comedi_insn * insn, unsigned int * data);
235static int dt2811_ao_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
236	struct comedi_insn * insn, unsigned int * data);
237static int dt2811_di_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
238	struct comedi_insn * insn, unsigned int * data);
239static int dt2811_do_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
240	struct comedi_insn * insn, unsigned int * data);
241
242enum { card_2811_pgh, card_2811_pgl };
243
244struct dt2811_private {
245	int ntrig;
246	int curadchan;
247	enum {
248		adc_singleended, adc_diff, adc_pseudo_diff
249	} adc_mux;
250	enum {
251		dac_bipolar_5, dac_bipolar_2_5, dac_unipolar_5
252	} dac_range[2];
253	const struct comedi_lrange *range_type_list[2];
254	unsigned int ao_readback[2];
255};
256
257#define devpriv ((struct dt2811_private *)dev->private)
258
259static const struct comedi_lrange *dac_range_types[] = {
260	&range_bipolar5,
261	&range_bipolar2_5,
262	&range_unipolar5
263};
264
265#define DT2811_TIMEOUT 5
266
267#if 0
268static irqreturn_t dt2811_interrupt(int irq, void *d PT_REGS_ARG)
269{
270	int lo, hi;
271	int data;
272	struct comedi_device *dev = d;
273
274	if (!dev->attached) {
275		comedi_error(dev, "spurious interrupt");
276		return IRQ_HANDLED;
277	}
278
279	lo = inb(dev->iobase + DT2811_ADDATLO);
280	hi = inb(dev->iobase + DT2811_ADDATHI);
281
282	data = lo + (hi << 8);
283
284	if (!(--devpriv->ntrig)) {
285		/* how to turn off acquisition */
286		s->async->events |= COMEDI_SB_EOA;
287	}
288	comedi_event(dev, s);
289	return IRQ_HANDLED;
290}
291#endif
292
293/*
294  options[0]   Board base address
295  options[1]   IRQ
296  options[2]   Input configuration
297                 0 == single-ended
298                 1 == differential
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)
312*/
313
314static int dt2811_attach(struct comedi_device * dev, struct comedi_devconfig * it)
315{
316	//int i, irq;
317	//unsigned long irqs;
318	//long flags;
319	int ret;
320	struct comedi_subdevice *s;
321	unsigned long iobase;
322
323	iobase = it->options[0];
324
325	printk("comedi%d: dt2811: base=0x%04lx\n", dev->minor, iobase);
326
327	if (!request_region(iobase, DT2811_SIZE, driver_name)) {
328		printk("I/O port conflict\n");
329		return -EIO;
330	}
331
332	dev->iobase = iobase;
333	dev->board_name = this_board->name;
334
335#if 0
336	outb(0, dev->iobase + DT2811_ADCSR);
337	comedi_udelay(100);
338	i = inb(dev->iobase + DT2811_ADDATLO);
339	i = inb(dev->iobase + DT2811_ADDATHI);
340#endif
341
342#if 0
343	irq = it->options[1];
344	if (irq < 0) {
345		save_flags(flags);
346		sti();
347		irqs = probe_irq_on();
348
349		outb(DT2811_CLRERROR | DT2811_INTENB,
350			dev->iobase + DT2811_ADCSR);
351		outb(0, dev->iobase + DT2811_ADGCR);
352
353		comedi_udelay(100);
354
355		irq = probe_irq_off(irqs);
356		restore_flags(flags);
357
358		/*outb(DT2811_CLRERROR|DT2811_INTENB,dev->iobase+DT2811_ADCSR); */
359
360		if (inb(dev->iobase + DT2811_ADCSR) & DT2811_ADERROR) {
361			printk("error probing irq (bad) \n");
362		}
363		dev->irq = 0;
364		if (irq > 0) {
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,
369				driver_name, dev);
370			if (ret < 0)
371				return -EIO;
372			dev->irq = irq;
373		} else if (irq == 0) {
374			printk("(no irq)\n");
375		} else {
376			printk("( multiple irq's -- this is bad! )\n");
377		}
378	}
379#endif
380
381	if ((ret = alloc_subdevices(dev, 4)) < 0)
382		return ret;
383	if ((ret = alloc_private(dev, sizeof(struct dt2811_private))) < 0)
384		return ret;
385	switch (it->options[2]) {
386	case 0:
387		devpriv->adc_mux = adc_singleended;
388		break;
389	case 1:
390		devpriv->adc_mux = adc_diff;
391		break;
392	case 2:
393		devpriv->adc_mux = adc_pseudo_diff;
394		break;
395	default:
396		devpriv->adc_mux = adc_singleended;
397		break;
398	}
399	switch (it->options[4]) {
400	case 0:
401		devpriv->dac_range[0] = dac_bipolar_5;
402		break;
403	case 1:
404		devpriv->dac_range[0] = dac_bipolar_2_5;
405		break;
406	case 2:
407		devpriv->dac_range[0] = dac_unipolar_5;
408		break;
409	default:
410		devpriv->dac_range[0] = dac_bipolar_5;
411		break;
412	}
413	switch (it->options[5]) {
414	case 0:
415		devpriv->dac_range[1] = dac_bipolar_5;
416		break;
417	case 1:
418		devpriv->dac_range[1] = dac_bipolar_2_5;
419		break;
420	case 2:
421		devpriv->dac_range[1] = dac_unipolar_5;
422		break;
423	default:
424		devpriv->dac_range[1] = dac_bipolar_5;
425		break;
426	}
427
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;
434	s->maxdata = 0xfff;
435	switch (it->options[3]) {
436	case 0:
437	default:
438		s->range_table = this_board->bip_5;
439		break;
440	case 1:
441		s->range_table = this_board->bip_2_5;
442		break;
443	case 2:
444		s->range_table = this_board->unip_5;
445		break;
446	}
447
448	s = dev->subdevices + 1;
449	/* ao subdevice */
450	s->type = COMEDI_SUBD_AO;
451	s->subdev_flags = SDF_WRITABLE;
452	s->n_chan = 2;
453	s->insn_write = dt2811_ao_insn;
454	s->insn_read = dt2811_ao_insn_read;
455	s->maxdata = 0xfff;
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]];
459
460	s = dev->subdevices + 2;
461	/* di subdevice */
462	s->type = COMEDI_SUBD_DI;
463	s->subdev_flags = SDF_READABLE;
464	s->n_chan = 8;
465	s->insn_bits = dt2811_di_insn_bits;
466	s->maxdata = 1;
467	s->range_table = &range_digital;
468
469	s = dev->subdevices + 3;
470	/* do subdevice */
471	s->type = COMEDI_SUBD_DO;
472	s->subdev_flags = SDF_WRITABLE;
473	s->n_chan = 8;
474	s->insn_bits = dt2811_do_insn_bits;
475	s->maxdata = 1;
476	s->state = 0;
477	s->range_table = &range_digital;
478
479	return 0;
480}
481
482static int dt2811_detach(struct comedi_device * dev)
483{
484	printk("comedi%d: dt2811: remove\n", dev->minor);
485
486	if (dev->irq) {
487		comedi_free_irq(dev->irq, dev);
488	}
489	if (dev->iobase) {
490		release_region(dev->iobase, DT2811_SIZE);
491	}
492
493	return 0;
494}
495
496static int dt2811_ai_insn(struct comedi_device * dev, struct comedi_subdevice * s,
497	struct comedi_insn * insn, unsigned int * data)
498{
499	int chan = CR_CHAN(insn->chanspec);
500	int timeout = DT2811_TIMEOUT;
501	int i;
502
503	for (i = 0; i < insn->n; i++) {
504		outb(chan, dev->iobase + DT2811_ADGCR);
505
506		while (timeout
507			&& inb(dev->iobase + DT2811_ADCSR) & DT2811_ADBUSY)
508			timeout--;
509		if (!timeout)
510			return -ETIME;
511
512		data[i] = inb(dev->iobase + DT2811_ADDATLO);
513		data[i] |= inb(dev->iobase + DT2811_ADDATHI) << 8;
514		data[i] &= 0xfff;
515	}
516
517	return i;
518}
519
520#if 0
521/* Wow.  This is code from the Comedi stone age.  But it hasn't been
522 * replaced, so I'll let it stay. */
523int dt2811_adtrig(kdev_t minor, comedi_adtrig * adtrig)
524{
525	struct comedi_device *dev = comedi_devices + minor;
526
527	if (adtrig->n < 1)
528		return 0;
529	dev->curadchan = adtrig->chan;
530	switch (dev->i_admode) {
531	case COMEDI_MDEMAND:
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);
537		break;
538	case COMEDI_MCONTS:
539		dev->ntrig = adtrig->n;
540		break;
541	}
542
543	return 0;
544}
545#endif
546
547static int dt2811_ao_insn(struct comedi_device * dev, struct comedi_subdevice * s,
548	struct comedi_insn * insn, unsigned int * data)
549{
550	int i;
551	int chan;
552
553	chan = CR_CHAN(insn->chanspec);
554
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];
560	}
561
562	return i;
563}
564
565static int dt2811_ao_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
566	struct comedi_insn * insn, unsigned int * data)
567{
568	int i;
569	int chan;
570
571	chan = CR_CHAN(insn->chanspec);
572
573	for (i = 0; i < insn->n; i++) {
574		data[i] = devpriv->ao_readback[chan];
575	}
576
577	return i;
578}
579
580static int dt2811_di_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
581	struct comedi_insn * insn, unsigned int * data)
582{
583	if (insn->n != 2)
584		return -EINVAL;
585
586	data[1] = inb(dev->iobase + DT2811_DIO);
587
588	return 2;
589}
590
591static int dt2811_do_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
592	struct comedi_insn * insn, unsigned int * data)
593{
594	if (insn->n != 2)
595		return -EINVAL;
596
597	s->state &= ~data[0];
598	s->state |= data[0] & data[1];
599	outb(s->state, dev->iobase + DT2811_DIO);
600
601	data[1] = s->state;
602
603	return 2;
604}
605