dt2811.c revision da91b2692e0939b307f9047192d2b9fe07793e7a
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)
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
320	int ret;
321	struct comedi_subdevice *s;
322	unsigned long iobase;
323
324	iobase = it->options[0];
325
326	printk("comedi%d: dt2811: base=0x%04lx\n", dev->minor, iobase);
327
328	if (!request_region(iobase, DT2811_SIZE, driver_name)) {
329		printk("I/O port conflict\n");
330		return -EIO;
331	}
332
333	dev->iobase = iobase;
334	dev->board_name = this_board->name;
335
336#if 0
337	outb(0, dev->iobase + DT2811_ADCSR);
338	comedi_udelay(100);
339	i = inb(dev->iobase + DT2811_ADDATLO);
340	i = inb(dev->iobase + DT2811_ADDATHI);
341#endif
342
343#if 0
344	irq = it->options[1];
345	if (irq < 0) {
346		save_flags(flags);
347		sti();
348		irqs = probe_irq_on();
349
350		outb(DT2811_CLRERROR | DT2811_INTENB,
351			dev->iobase + DT2811_ADCSR);
352		outb(0, dev->iobase + DT2811_ADGCR);
353
354		comedi_udelay(100);
355
356		irq = probe_irq_off(irqs);
357		restore_flags(flags);
358
359		/*outb(DT2811_CLRERROR|DT2811_INTENB,dev->iobase+DT2811_ADCSR); */
360
361		if (inb(dev->iobase + DT2811_ADCSR) & DT2811_ADERROR) {
362			printk("error probing irq (bad) \n");
363		}
364		dev->irq = 0;
365		if (irq > 0) {
366			i = inb(dev->iobase + DT2811_ADDATLO);
367			i = inb(dev->iobase + DT2811_ADDATHI);
368			printk("(irq = %d)\n", irq);
369			ret = comedi_request_irq(irq, dt2811_interrupt, 0,
370				driver_name, dev);
371			if (ret < 0)
372				return -EIO;
373			dev->irq = irq;
374		} else if (irq == 0) {
375			printk("(no irq)\n");
376		} else {
377			printk("( multiple irq's -- this is bad! )\n");
378		}
379	}
380#endif
381
382	if ((ret = alloc_subdevices(dev, 4)) < 0)
383		return ret;
384	if ((ret = alloc_private(dev, sizeof(struct dt2811_private))) < 0)
385		return ret;
386	switch (it->options[2]) {
387	case 0:
388		devpriv->adc_mux = adc_singleended;
389		break;
390	case 1:
391		devpriv->adc_mux = adc_diff;
392		break;
393	case 2:
394		devpriv->adc_mux = adc_pseudo_diff;
395		break;
396	default:
397		devpriv->adc_mux = adc_singleended;
398		break;
399	}
400	switch (it->options[4]) {
401	case 0:
402		devpriv->dac_range[0] = dac_bipolar_5;
403		break;
404	case 1:
405		devpriv->dac_range[0] = dac_bipolar_2_5;
406		break;
407	case 2:
408		devpriv->dac_range[0] = dac_unipolar_5;
409		break;
410	default:
411		devpriv->dac_range[0] = dac_bipolar_5;
412		break;
413	}
414	switch (it->options[5]) {
415	case 0:
416		devpriv->dac_range[1] = dac_bipolar_5;
417		break;
418	case 1:
419		devpriv->dac_range[1] = dac_bipolar_2_5;
420		break;
421	case 2:
422		devpriv->dac_range[1] = dac_unipolar_5;
423		break;
424	default:
425		devpriv->dac_range[1] = dac_bipolar_5;
426		break;
427	}
428
429	s = dev->subdevices + 0;
430	/* initialize the ADC subdevice */
431	s->type = COMEDI_SUBD_AI;
432	s->subdev_flags = SDF_READABLE | SDF_GROUND;
433	s->n_chan = devpriv->adc_mux == adc_diff ? 8 : 16;
434	s->insn_read = dt2811_ai_insn;
435	s->maxdata = 0xfff;
436	switch (it->options[3]) {
437	case 0:
438	default:
439		s->range_table = this_board->bip_5;
440		break;
441	case 1:
442		s->range_table = this_board->bip_2_5;
443		break;
444	case 2:
445		s->range_table = this_board->unip_5;
446		break;
447	}
448
449	s = dev->subdevices + 1;
450	/* ao subdevice */
451	s->type = COMEDI_SUBD_AO;
452	s->subdev_flags = SDF_WRITABLE;
453	s->n_chan = 2;
454	s->insn_write = dt2811_ao_insn;
455	s->insn_read = dt2811_ao_insn_read;
456	s->maxdata = 0xfff;
457	s->range_table_list = devpriv->range_type_list;
458	devpriv->range_type_list[0] = dac_range_types[devpriv->dac_range[0]];
459	devpriv->range_type_list[1] = dac_range_types[devpriv->dac_range[1]];
460
461	s = dev->subdevices + 2;
462	/* di subdevice */
463	s->type = COMEDI_SUBD_DI;
464	s->subdev_flags = SDF_READABLE;
465	s->n_chan = 8;
466	s->insn_bits = dt2811_di_insn_bits;
467	s->maxdata = 1;
468	s->range_table = &range_digital;
469
470	s = dev->subdevices + 3;
471	/* do subdevice */
472	s->type = COMEDI_SUBD_DO;
473	s->subdev_flags = SDF_WRITABLE;
474	s->n_chan = 8;
475	s->insn_bits = dt2811_do_insn_bits;
476	s->maxdata = 1;
477	s->state = 0;
478	s->range_table = &range_digital;
479
480	return 0;
481}
482
483static int dt2811_detach(struct comedi_device *dev)
484{
485	printk("comedi%d: dt2811: remove\n", dev->minor);
486
487	if (dev->irq) {
488		comedi_free_irq(dev->irq, dev);
489	}
490	if (dev->iobase) {
491		release_region(dev->iobase, DT2811_SIZE);
492	}
493
494	return 0;
495}
496
497static int dt2811_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
498	struct comedi_insn *insn, unsigned int *data)
499{
500	int chan = CR_CHAN(insn->chanspec);
501	int timeout = DT2811_TIMEOUT;
502	int i;
503
504	for (i = 0; i < insn->n; i++) {
505		outb(chan, dev->iobase + DT2811_ADGCR);
506
507		while (timeout
508			&& inb(dev->iobase + DT2811_ADCSR) & DT2811_ADBUSY)
509			timeout--;
510		if (!timeout)
511			return -ETIME;
512
513		data[i] = inb(dev->iobase + DT2811_ADDATLO);
514		data[i] |= inb(dev->iobase + DT2811_ADDATHI) << 8;
515		data[i] &= 0xfff;
516	}
517
518	return i;
519}
520
521#if 0
522/* Wow.  This is code from the Comedi stone age.  But it hasn't been
523 * replaced, so I'll let it stay. */
524int dt2811_adtrig(kdev_t minor, comedi_adtrig *adtrig)
525{
526	struct comedi_device *dev = comedi_devices + minor;
527
528	if (adtrig->n < 1)
529		return 0;
530	dev->curadchan = adtrig->chan;
531	switch (dev->i_admode) {
532	case COMEDI_MDEMAND:
533		dev->ntrig = adtrig->n - 1;
534		/*printk("dt2811: AD soft trigger\n"); */
535		/*outb(DT2811_CLRERROR|DT2811_INTENB,dev->iobase+DT2811_ADCSR); */ /* not neccessary */
536		outb(dev->curadchan, dev->iobase + DT2811_ADGCR);
537		do_gettimeofday(&trigtime);
538		break;
539	case COMEDI_MCONTS:
540		dev->ntrig = adtrig->n;
541		break;
542	}
543
544	return 0;
545}
546#endif
547
548static int dt2811_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
549	struct comedi_insn *insn, unsigned int *data)
550{
551	int i;
552	int chan;
553
554	chan = CR_CHAN(insn->chanspec);
555
556	for (i = 0; i < insn->n; i++) {
557		outb(data[i] & 0xff, dev->iobase + DT2811_DADAT0LO + 2 * chan);
558		outb((data[i] >> 8) & 0xff,
559			dev->iobase + DT2811_DADAT0HI + 2 * chan);
560		devpriv->ao_readback[chan] = data[i];
561	}
562
563	return i;
564}
565
566static int dt2811_ao_insn_read(struct comedi_device *dev, struct comedi_subdevice *s,
567	struct comedi_insn *insn, unsigned int *data)
568{
569	int i;
570	int chan;
571
572	chan = CR_CHAN(insn->chanspec);
573
574	for (i = 0; i < insn->n; i++) {
575		data[i] = devpriv->ao_readback[chan];
576	}
577
578	return i;
579}
580
581static int dt2811_di_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s,
582	struct comedi_insn *insn, unsigned int *data)
583{
584	if (insn->n != 2)
585		return -EINVAL;
586
587	data[1] = inb(dev->iobase + DT2811_DIO);
588
589	return 2;
590}
591
592static int dt2811_do_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s,
593	struct comedi_insn *insn, unsigned int *data)
594{
595	if (insn->n != 2)
596		return -EINVAL;
597
598	s->state &= ~data[0];
599	s->state |= data[0] & data[1];
600	outb(s->state, dev->iobase + DT2811_DIO);
601
602	data[1] = s->state;
603
604	return 2;
605}
606