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