dt2811.c revision 8629efa4cbf6f89a54a85af4b8bc31762af01800
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 = ARRAY_SIZE(boardtypes),
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	ret = alloc_subdevices(dev, 4);
383	if (ret < 0)
384		return ret;
385
386	ret = alloc_private(dev, sizeof(struct dt2811_private));
387	if (ret < 0)
388		return ret;
389
390	switch (it->options[2]) {
391	case 0:
392		devpriv->adc_mux = adc_singleended;
393		break;
394	case 1:
395		devpriv->adc_mux = adc_diff;
396		break;
397	case 2:
398		devpriv->adc_mux = adc_pseudo_diff;
399		break;
400	default:
401		devpriv->adc_mux = adc_singleended;
402		break;
403	}
404	switch (it->options[4]) {
405	case 0:
406		devpriv->dac_range[0] = dac_bipolar_5;
407		break;
408	case 1:
409		devpriv->dac_range[0] = dac_bipolar_2_5;
410		break;
411	case 2:
412		devpriv->dac_range[0] = dac_unipolar_5;
413		break;
414	default:
415		devpriv->dac_range[0] = dac_bipolar_5;
416		break;
417	}
418	switch (it->options[5]) {
419	case 0:
420		devpriv->dac_range[1] = dac_bipolar_5;
421		break;
422	case 1:
423		devpriv->dac_range[1] = dac_bipolar_2_5;
424		break;
425	case 2:
426		devpriv->dac_range[1] = dac_unipolar_5;
427		break;
428	default:
429		devpriv->dac_range[1] = dac_bipolar_5;
430		break;
431	}
432
433	s = dev->subdevices + 0;
434	/* initialize the ADC subdevice */
435	s->type = COMEDI_SUBD_AI;
436	s->subdev_flags = SDF_READABLE | SDF_GROUND;
437	s->n_chan = devpriv->adc_mux == adc_diff ? 8 : 16;
438	s->insn_read = dt2811_ai_insn;
439	s->maxdata = 0xfff;
440	switch (it->options[3]) {
441	case 0:
442	default:
443		s->range_table = this_board->bip_5;
444		break;
445	case 1:
446		s->range_table = this_board->bip_2_5;
447		break;
448	case 2:
449		s->range_table = this_board->unip_5;
450		break;
451	}
452
453	s = dev->subdevices + 1;
454	/* ao subdevice */
455	s->type = COMEDI_SUBD_AO;
456	s->subdev_flags = SDF_WRITABLE;
457	s->n_chan = 2;
458	s->insn_write = dt2811_ao_insn;
459	s->insn_read = dt2811_ao_insn_read;
460	s->maxdata = 0xfff;
461	s->range_table_list = devpriv->range_type_list;
462	devpriv->range_type_list[0] = dac_range_types[devpriv->dac_range[0]];
463	devpriv->range_type_list[1] = dac_range_types[devpriv->dac_range[1]];
464
465	s = dev->subdevices + 2;
466	/* di subdevice */
467	s->type = COMEDI_SUBD_DI;
468	s->subdev_flags = SDF_READABLE;
469	s->n_chan = 8;
470	s->insn_bits = dt2811_di_insn_bits;
471	s->maxdata = 1;
472	s->range_table = &range_digital;
473
474	s = dev->subdevices + 3;
475	/* do subdevice */
476	s->type = COMEDI_SUBD_DO;
477	s->subdev_flags = SDF_WRITABLE;
478	s->n_chan = 8;
479	s->insn_bits = dt2811_do_insn_bits;
480	s->maxdata = 1;
481	s->state = 0;
482	s->range_table = &range_digital;
483
484	return 0;
485}
486
487static int dt2811_detach(struct comedi_device *dev)
488{
489	printk("comedi%d: dt2811: remove\n", dev->minor);
490
491	if (dev->irq) {
492		comedi_free_irq(dev->irq, dev);
493	}
494	if (dev->iobase) {
495		release_region(dev->iobase, DT2811_SIZE);
496	}
497
498	return 0;
499}
500
501static int dt2811_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
502	struct comedi_insn *insn, unsigned int *data)
503{
504	int chan = CR_CHAN(insn->chanspec);
505	int timeout = DT2811_TIMEOUT;
506	int i;
507
508	for (i = 0; i < insn->n; i++) {
509		outb(chan, dev->iobase + DT2811_ADGCR);
510
511		while (timeout
512			&& inb(dev->iobase + DT2811_ADCSR) & DT2811_ADBUSY)
513			timeout--;
514		if (!timeout)
515			return -ETIME;
516
517		data[i] = inb(dev->iobase + DT2811_ADDATLO);
518		data[i] |= inb(dev->iobase + DT2811_ADDATHI) << 8;
519		data[i] &= 0xfff;
520	}
521
522	return i;
523}
524
525#if 0
526/* Wow.  This is code from the Comedi stone age.  But it hasn't been
527 * replaced, so I'll let it stay. */
528int dt2811_adtrig(kdev_t minor, comedi_adtrig *adtrig)
529{
530	struct comedi_device *dev = comedi_devices + minor;
531
532	if (adtrig->n < 1)
533		return 0;
534	dev->curadchan = adtrig->chan;
535	switch (dev->i_admode) {
536	case COMEDI_MDEMAND:
537		dev->ntrig = adtrig->n - 1;
538		/*printk("dt2811: AD soft trigger\n"); */
539		/*outb(DT2811_CLRERROR|DT2811_INTENB,dev->iobase+DT2811_ADCSR); */ /* not neccessary */
540		outb(dev->curadchan, dev->iobase + DT2811_ADGCR);
541		do_gettimeofday(&trigtime);
542		break;
543	case COMEDI_MCONTS:
544		dev->ntrig = adtrig->n;
545		break;
546	}
547
548	return 0;
549}
550#endif
551
552static int dt2811_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
553	struct comedi_insn *insn, unsigned int *data)
554{
555	int i;
556	int chan;
557
558	chan = CR_CHAN(insn->chanspec);
559
560	for (i = 0; i < insn->n; i++) {
561		outb(data[i] & 0xff, dev->iobase + DT2811_DADAT0LO + 2 * chan);
562		outb((data[i] >> 8) & 0xff,
563			dev->iobase + DT2811_DADAT0HI + 2 * chan);
564		devpriv->ao_readback[chan] = data[i];
565	}
566
567	return i;
568}
569
570static int dt2811_ao_insn_read(struct comedi_device *dev, struct comedi_subdevice *s,
571	struct comedi_insn *insn, unsigned int *data)
572{
573	int i;
574	int chan;
575
576	chan = CR_CHAN(insn->chanspec);
577
578	for (i = 0; i < insn->n; i++) {
579		data[i] = devpriv->ao_readback[chan];
580	}
581
582	return i;
583}
584
585static int dt2811_di_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s,
586	struct comedi_insn *insn, unsigned int *data)
587{
588	if (insn->n != 2)
589		return -EINVAL;
590
591	data[1] = inb(dev->iobase + DT2811_DIO);
592
593	return 2;
594}
595
596static int dt2811_do_insn_bits(struct comedi_device *dev, struct comedi_subdevice *s,
597	struct comedi_insn *insn, unsigned int *data)
598{
599	if (insn->n != 2)
600		return -EINVAL;
601
602	s->state &= ~data[0];
603	s->state |= data[0] & data[1];
604	outb(s->state, dev->iobase + DT2811_DIO);
605
606	data[1] = s->state;
607
608	return 2;
609}
610