dt2811.c revision 71b5f4f11971dea972832ad63a994c7e5b45db6b
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 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 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 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 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 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 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
194typedef struct {
195	const char *name;
196	const comedi_lrange *bip_5;
197	const comedi_lrange *bip_2_5;
198	const comedi_lrange *unip_5;
199} boardtype;
200static const boardtype boardtypes[] = {
201	{"dt2811-pgh",
202			&range_dt2811_pgh_ai_5_bipolar,
203			&range_dt2811_pgh_ai_2_5_bipolar,
204			&range_dt2811_pgh_ai_5_unipolar,
205		},
206	{"dt2811-pgl",
207			&range_dt2811_pgl_ai_5_bipolar,
208			&range_dt2811_pgl_ai_2_5_bipolar,
209			&range_dt2811_pgl_ai_5_unipolar,
210		},
211};
212
213#define this_board ((const boardtype *)dev->board_ptr)
214
215static int dt2811_attach(struct comedi_device * dev, comedi_devconfig * it);
216static int dt2811_detach(struct comedi_device * dev);
217static comedi_driver driver_dt2811 = {
218      driver_name:"dt2811",
219      module:THIS_MODULE,
220      attach:dt2811_attach,
221      detach:dt2811_detach,
222      board_name:&boardtypes[0].name,
223      num_names:sizeof(boardtypes) / sizeof(boardtype),
224      offset:sizeof(boardtype),
225};
226
227COMEDI_INITCLEANUP(driver_dt2811);
228
229static int dt2811_ai_insn(struct comedi_device * dev, comedi_subdevice * s,
230	comedi_insn * insn, unsigned int * data);
231static int dt2811_ao_insn(struct comedi_device * dev, comedi_subdevice * s,
232	comedi_insn * insn, unsigned int * data);
233static int dt2811_ao_insn_read(struct comedi_device * dev, comedi_subdevice * s,
234	comedi_insn * insn, unsigned int * data);
235static int dt2811_di_insn_bits(struct comedi_device * dev, comedi_subdevice * s,
236	comedi_insn * insn, unsigned int * data);
237static int dt2811_do_insn_bits(struct comedi_device * dev, comedi_subdevice * s,
238	comedi_insn * insn, unsigned int * data);
239
240enum { card_2811_pgh, card_2811_pgl };
241typedef struct {
242	int ntrig;
243	int curadchan;
244	enum {
245		adc_singleended, adc_diff, adc_pseudo_diff
246	} adc_mux;
247	enum {
248		dac_bipolar_5, dac_bipolar_2_5, dac_unipolar_5
249	} dac_range[2];
250	const comedi_lrange *range_type_list[2];
251	unsigned int ao_readback[2];
252} dt2811_private;
253
254#define devpriv ((dt2811_private *)dev->private)
255
256static const comedi_lrange *dac_range_types[] = {
257	&range_bipolar5,
258	&range_bipolar2_5,
259	&range_unipolar5
260};
261
262#define DT2811_TIMEOUT 5
263
264#if 0
265static irqreturn_t dt2811_interrupt(int irq, void *d PT_REGS_ARG)
266{
267	int lo, hi;
268	int data;
269	struct comedi_device *dev = d;
270
271	if (!dev->attached) {
272		comedi_error(dev, "spurious interrupt");
273		return IRQ_HANDLED;
274	}
275
276	lo = inb(dev->iobase + DT2811_ADDATLO);
277	hi = inb(dev->iobase + DT2811_ADDATHI);
278
279	data = lo + (hi << 8);
280
281	if (!(--devpriv->ntrig)) {
282		/* how to turn off acquisition */
283		s->async->events |= COMEDI_SB_EOA;
284	}
285	comedi_event(dev, s);
286	return IRQ_HANDLED;
287}
288#endif
289
290/*
291  options[0]   Board base address
292  options[1]   IRQ
293  options[2]   Input configuration
294                 0 == single-ended
295                 1 == differential
296                 2 == pseudo-differential
297  options[3]   Analog input range configuration
298                 0 == bipolar 5  (-5V -- +5V)
299                 1 == bipolar 2.5V  (-2.5V -- +2.5V)
300                 2 == unipolar 5V  (0V -- +5V)
301  options[4]   Analog output 0 range configuration
302                 0 == bipolar 5  (-5V -- +5V)
303                 1 == bipolar 2.5V  (-2.5V -- +2.5V)
304                 2 == unipolar 5V  (0V -- +5V)
305  options[5]   Analog output 1 range configuration
306                 0 == bipolar 5  (-5V -- +5V)
307                 1 == bipolar 2.5V  (-2.5V -- +2.5V)
308                 2 == unipolar 5V  (0V -- +5V)
309*/
310
311static int dt2811_attach(struct comedi_device * dev, comedi_devconfig * it)
312{
313	//int i, irq;
314	//unsigned long irqs;
315	//long flags;
316	int ret;
317	comedi_subdevice *s;
318	unsigned long iobase;
319
320	iobase = it->options[0];
321
322	printk("comedi%d: dt2811: base=0x%04lx\n", dev->minor, iobase);
323
324	if (!request_region(iobase, DT2811_SIZE, driver_name)) {
325		printk("I/O port conflict\n");
326		return -EIO;
327	}
328
329	dev->iobase = iobase;
330	dev->board_name = this_board->name;
331
332#if 0
333	outb(0, dev->iobase + DT2811_ADCSR);
334	comedi_udelay(100);
335	i = inb(dev->iobase + DT2811_ADDATLO);
336	i = inb(dev->iobase + DT2811_ADDATHI);
337#endif
338
339#if 0
340	irq = it->options[1];
341	if (irq < 0) {
342		save_flags(flags);
343		sti();
344		irqs = probe_irq_on();
345
346		outb(DT2811_CLRERROR | DT2811_INTENB,
347			dev->iobase + DT2811_ADCSR);
348		outb(0, dev->iobase + DT2811_ADGCR);
349
350		comedi_udelay(100);
351
352		irq = probe_irq_off(irqs);
353		restore_flags(flags);
354
355		/*outb(DT2811_CLRERROR|DT2811_INTENB,dev->iobase+DT2811_ADCSR); */
356
357		if (inb(dev->iobase + DT2811_ADCSR) & DT2811_ADERROR) {
358			printk("error probing irq (bad) \n");
359		}
360		dev->irq = 0;
361		if (irq > 0) {
362			i = inb(dev->iobase + DT2811_ADDATLO);
363			i = inb(dev->iobase + DT2811_ADDATHI);
364			printk("(irq = %d)\n", irq);
365			ret = comedi_request_irq(irq, dt2811_interrupt, 0,
366				driver_name, dev);
367			if (ret < 0)
368				return -EIO;
369			dev->irq = irq;
370		} else if (irq == 0) {
371			printk("(no irq)\n");
372		} else {
373			printk("( multiple irq's -- this is bad! )\n");
374		}
375	}
376#endif
377
378	if ((ret = alloc_subdevices(dev, 4)) < 0)
379		return ret;
380	if ((ret = alloc_private(dev, sizeof(dt2811_private))) < 0)
381		return ret;
382	switch (it->options[2]) {
383	case 0:
384		devpriv->adc_mux = adc_singleended;
385		break;
386	case 1:
387		devpriv->adc_mux = adc_diff;
388		break;
389	case 2:
390		devpriv->adc_mux = adc_pseudo_diff;
391		break;
392	default:
393		devpriv->adc_mux = adc_singleended;
394		break;
395	}
396	switch (it->options[4]) {
397	case 0:
398		devpriv->dac_range[0] = dac_bipolar_5;
399		break;
400	case 1:
401		devpriv->dac_range[0] = dac_bipolar_2_5;
402		break;
403	case 2:
404		devpriv->dac_range[0] = dac_unipolar_5;
405		break;
406	default:
407		devpriv->dac_range[0] = dac_bipolar_5;
408		break;
409	}
410	switch (it->options[5]) {
411	case 0:
412		devpriv->dac_range[1] = dac_bipolar_5;
413		break;
414	case 1:
415		devpriv->dac_range[1] = dac_bipolar_2_5;
416		break;
417	case 2:
418		devpriv->dac_range[1] = dac_unipolar_5;
419		break;
420	default:
421		devpriv->dac_range[1] = dac_bipolar_5;
422		break;
423	}
424
425	s = dev->subdevices + 0;
426	/* initialize the ADC subdevice */
427	s->type = COMEDI_SUBD_AI;
428	s->subdev_flags = SDF_READABLE | SDF_GROUND;
429	s->n_chan = devpriv->adc_mux == adc_diff ? 8 : 16;
430	s->insn_read = dt2811_ai_insn;
431	s->maxdata = 0xfff;
432	switch (it->options[3]) {
433	case 0:
434	default:
435		s->range_table = this_board->bip_5;
436		break;
437	case 1:
438		s->range_table = this_board->bip_2_5;
439		break;
440	case 2:
441		s->range_table = this_board->unip_5;
442		break;
443	}
444
445	s = dev->subdevices + 1;
446	/* ao subdevice */
447	s->type = COMEDI_SUBD_AO;
448	s->subdev_flags = SDF_WRITABLE;
449	s->n_chan = 2;
450	s->insn_write = dt2811_ao_insn;
451	s->insn_read = dt2811_ao_insn_read;
452	s->maxdata = 0xfff;
453	s->range_table_list = devpriv->range_type_list;
454	devpriv->range_type_list[0] = dac_range_types[devpriv->dac_range[0]];
455	devpriv->range_type_list[1] = dac_range_types[devpriv->dac_range[1]];
456
457	s = dev->subdevices + 2;
458	/* di subdevice */
459	s->type = COMEDI_SUBD_DI;
460	s->subdev_flags = SDF_READABLE;
461	s->n_chan = 8;
462	s->insn_bits = dt2811_di_insn_bits;
463	s->maxdata = 1;
464	s->range_table = &range_digital;
465
466	s = dev->subdevices + 3;
467	/* do subdevice */
468	s->type = COMEDI_SUBD_DO;
469	s->subdev_flags = SDF_WRITABLE;
470	s->n_chan = 8;
471	s->insn_bits = dt2811_do_insn_bits;
472	s->maxdata = 1;
473	s->state = 0;
474	s->range_table = &range_digital;
475
476	return 0;
477}
478
479static int dt2811_detach(struct comedi_device * dev)
480{
481	printk("comedi%d: dt2811: remove\n", dev->minor);
482
483	if (dev->irq) {
484		comedi_free_irq(dev->irq, dev);
485	}
486	if (dev->iobase) {
487		release_region(dev->iobase, DT2811_SIZE);
488	}
489
490	return 0;
491}
492
493static int dt2811_ai_insn(struct comedi_device * dev, comedi_subdevice * s,
494	comedi_insn * insn, unsigned int * data)
495{
496	int chan = CR_CHAN(insn->chanspec);
497	int timeout = DT2811_TIMEOUT;
498	int i;
499
500	for (i = 0; i < insn->n; i++) {
501		outb(chan, dev->iobase + DT2811_ADGCR);
502
503		while (timeout
504			&& inb(dev->iobase + DT2811_ADCSR) & DT2811_ADBUSY)
505			timeout--;
506		if (!timeout)
507			return -ETIME;
508
509		data[i] = inb(dev->iobase + DT2811_ADDATLO);
510		data[i] |= inb(dev->iobase + DT2811_ADDATHI) << 8;
511		data[i] &= 0xfff;
512	}
513
514	return i;
515}
516
517#if 0
518/* Wow.  This is code from the Comedi stone age.  But it hasn't been
519 * replaced, so I'll let it stay. */
520int dt2811_adtrig(kdev_t minor, comedi_adtrig * adtrig)
521{
522	struct comedi_device *dev = comedi_devices + minor;
523
524	if (adtrig->n < 1)
525		return 0;
526	dev->curadchan = adtrig->chan;
527	switch (dev->i_admode) {
528	case COMEDI_MDEMAND:
529		dev->ntrig = adtrig->n - 1;
530		/*printk("dt2811: AD soft trigger\n"); */
531		/*outb(DT2811_CLRERROR|DT2811_INTENB,dev->iobase+DT2811_ADCSR); *//* not neccessary */
532		outb(dev->curadchan, dev->iobase + DT2811_ADGCR);
533		do_gettimeofday(&trigtime);
534		break;
535	case COMEDI_MCONTS:
536		dev->ntrig = adtrig->n;
537		break;
538	}
539
540	return 0;
541}
542#endif
543
544static int dt2811_ao_insn(struct comedi_device * dev, comedi_subdevice * s,
545	comedi_insn * insn, unsigned int * data)
546{
547	int i;
548	int chan;
549
550	chan = CR_CHAN(insn->chanspec);
551
552	for (i = 0; i < insn->n; i++) {
553		outb(data[i] & 0xff, dev->iobase + DT2811_DADAT0LO + 2 * chan);
554		outb((data[i] >> 8) & 0xff,
555			dev->iobase + DT2811_DADAT0HI + 2 * chan);
556		devpriv->ao_readback[chan] = data[i];
557	}
558
559	return i;
560}
561
562static int dt2811_ao_insn_read(struct comedi_device * dev, comedi_subdevice * s,
563	comedi_insn * insn, unsigned int * data)
564{
565	int i;
566	int chan;
567
568	chan = CR_CHAN(insn->chanspec);
569
570	for (i = 0; i < insn->n; i++) {
571		data[i] = devpriv->ao_readback[chan];
572	}
573
574	return i;
575}
576
577static int dt2811_di_insn_bits(struct comedi_device * dev, comedi_subdevice * s,
578	comedi_insn * insn, unsigned int * data)
579{
580	if (insn->n != 2)
581		return -EINVAL;
582
583	data[1] = inb(dev->iobase + DT2811_DIO);
584
585	return 2;
586}
587
588static int dt2811_do_insn_bits(struct comedi_device * dev, comedi_subdevice * s,
589	comedi_insn * insn, unsigned int * data)
590{
591	if (insn->n != 2)
592		return -EINVAL;
593
594	s->state &= ~data[0];
595	s->state |= data[0] & data[1];
596	outb(s->state, dev->iobase + DT2811_DIO);
597
598	data[1] = s->state;
599
600	return 2;
601}
602