• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 /*
23 Driver: dt2811
24 Description: Data Translation DT2811
25 Author: ds
26 Devices: [Data Translation] DT2811-PGL (dt2811-pgl), DT2811-PGH (dt2811-pgh)
27 Status: works
28 
29 Configuration options:
30   [0] - I/O port base address
31   [1] - IRQ, although this is currently unused
32   [2] - A/D reference
33 	  0 = signle-ended
34 	  1 = differential
35 	  2 = pseudo-differential (common reference)
36   [3] - A/D range
37 	  0 = [-5, 5]
38 	  1 = [-2.5, 2.5]
39 	  2 = [0, 5]
40   [4] - D/A 0 range (same choices)
41   [4] - D/A 1 range (same choices)
42 */
43 
44 #include <linux/module.h>
45 #include "../comedidev.h"
46 
47 static const struct comedi_lrange range_dt2811_pgh_ai_5_unipolar = {
48 	4, {
49 		UNI_RANGE(5),
50 		UNI_RANGE(2.5),
51 		UNI_RANGE(1.25),
52 		UNI_RANGE(0.625)
53 	}
54 };
55 
56 static const struct comedi_lrange range_dt2811_pgh_ai_2_5_bipolar = {
57 	4, {
58 		BIP_RANGE(2.5),
59 		BIP_RANGE(1.25),
60 		BIP_RANGE(0.625),
61 		BIP_RANGE(0.3125)
62 	}
63 };
64 
65 static const struct comedi_lrange range_dt2811_pgh_ai_5_bipolar = {
66 	4, {
67 		BIP_RANGE(5),
68 		BIP_RANGE(2.5),
69 		BIP_RANGE(1.25),
70 		BIP_RANGE(0.625)
71 	}
72 };
73 
74 static const struct comedi_lrange range_dt2811_pgl_ai_5_unipolar = {
75 	4, {
76 		UNI_RANGE(5),
77 		UNI_RANGE(0.5),
78 		UNI_RANGE(0.05),
79 		UNI_RANGE(0.01)
80 	}
81 };
82 
83 static const struct comedi_lrange range_dt2811_pgl_ai_2_5_bipolar = {
84 	4, {
85 		BIP_RANGE(2.5),
86 		BIP_RANGE(0.25),
87 		BIP_RANGE(0.025),
88 		BIP_RANGE(0.005)
89 	}
90 };
91 
92 static const struct comedi_lrange range_dt2811_pgl_ai_5_bipolar = {
93 	4, {
94 		BIP_RANGE(5),
95 		BIP_RANGE(0.5),
96 		BIP_RANGE(0.05),
97 		BIP_RANGE(0.01)
98 	}
99 };
100 
101 /*
102 
103    0x00    ADCSR R/W  A/D Control/Status Register
104    bit 7 - (R) 1 indicates A/D conversion done
105    reading ADDAT clears bit
106    (W) ignored
107    bit 6 - (R) 1 indicates A/D error
108    (W) ignored
109    bit 5 - (R) 1 indicates A/D busy, cleared at end
110    of conversion
111    (W) ignored
112    bit 4 - (R) 0
113    (W)
114    bit 3 - (R) 0
115    bit 2 - (R/W) 1 indicates interrupts enabled
116    bits 1,0 - (R/W) mode bits
117    00  single conversion on ADGCR load
118    01  continuous conversion, internal clock,
119    (clock enabled on ADGCR load)
120    10  continuous conversion, internal clock,
121    external trigger
122    11  continuous conversion, external clock,
123    external trigger
124 
125    0x01    ADGCR R/W A/D Gain/Channel Register
126    bit 6,7 - (R/W) gain select
127    00  gain=1, both PGH, PGL models
128    01  gain=2 PGH, 10 PGL
129    10  gain=4 PGH, 100 PGL
130    11  gain=8 PGH, 500 PGL
131    bit 4,5 - reserved
132    bit 3-0 - (R/W) channel select
133    channel number from 0-15
134 
135    0x02,0x03 (R) ADDAT A/D Data Register
136    (W) DADAT0 D/A Data Register 0
137    0x02 low byte
138    0x03 high byte
139 
140    0x04,0x05 (W) DADAT0 D/A Data Register 1
141 
142    0x06 (R) DIO0 Digital Input Port 0
143    (W) DIO1 Digital Output Port 1
144 
145    0x07 TMRCTR (R/W) Timer/Counter Register
146    bits 6,7 - reserved
147    bits 5-3 - Timer frequency control (mantissa)
148    543  divisor  freqency (kHz)
149    000  1        600
150    001  10       60
151    010  2        300
152    011  3        200
153    100  4        150
154    101  5        120
155    110  6        100
156    111  12       50
157    bits 2-0 - Timer frequency control (exponent)
158    210  multiply divisor/divide frequency by
159    000  1
160    001  10
161    010  100
162    011  1000
163    100  10000
164    101  100000
165    110  1000000
166    111  10000000
167 
168  */
169 
170 #define TIMEOUT 10000
171 
172 #define DT2811_ADCSR 0
173 #define DT2811_ADGCR 1
174 #define DT2811_ADDATLO 2
175 #define DT2811_ADDATHI 3
176 #define DT2811_DADAT0LO 2
177 #define DT2811_DADAT0HI 3
178 #define DT2811_DADAT1LO 4
179 #define DT2811_DADAT1HI 5
180 #define DT2811_DIO 6
181 #define DT2811_TMRCTR 7
182 
183 /*
184  * flags
185  */
186 
187 /* ADCSR */
188 
189 #define DT2811_ADDONE   0x80
190 #define DT2811_ADERROR  0x40
191 #define DT2811_ADBUSY   0x20
192 #define DT2811_CLRERROR 0x10
193 #define DT2811_INTENB   0x04
194 #define DT2811_ADMODE   0x03
195 
196 struct dt2811_board {
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 
203 enum { card_2811_pgh, card_2811_pgl };
204 
205 struct dt2811_private {
206 	int ntrig;
207 	int curadchan;
208 	enum {
209 		adc_singleended, adc_diff, adc_pseudo_diff
210 	} adc_mux;
211 	enum {
212 		dac_bipolar_5, dac_bipolar_2_5, dac_unipolar_5
213 	} dac_range[2];
214 	const struct comedi_lrange *range_type_list[2];
215 };
216 
217 static const struct comedi_lrange *dac_range_types[] = {
218 	&range_bipolar5,
219 	&range_bipolar2_5,
220 	&range_unipolar5
221 };
222 
dt2811_ai_eoc(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned long context)223 static int dt2811_ai_eoc(struct comedi_device *dev,
224 			 struct comedi_subdevice *s,
225 			 struct comedi_insn *insn,
226 			 unsigned long context)
227 {
228 	unsigned int status;
229 
230 	status = inb(dev->iobase + DT2811_ADCSR);
231 	if ((status & DT2811_ADBUSY) == 0)
232 		return 0;
233 	return -EBUSY;
234 }
235 
dt2811_ai_insn(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)236 static int dt2811_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
237 			  struct comedi_insn *insn, unsigned int *data)
238 {
239 	int chan = CR_CHAN(insn->chanspec);
240 	int ret;
241 	int i;
242 
243 	for (i = 0; i < insn->n; i++) {
244 		outb(chan, dev->iobase + DT2811_ADGCR);
245 
246 		ret = comedi_timeout(dev, s, insn, dt2811_ai_eoc, 0);
247 		if (ret)
248 			return ret;
249 
250 		data[i] = inb(dev->iobase + DT2811_ADDATLO);
251 		data[i] |= inb(dev->iobase + DT2811_ADDATHI) << 8;
252 		data[i] &= 0xfff;
253 	}
254 
255 	return i;
256 }
257 
dt2811_ao_insn_write(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)258 static int dt2811_ao_insn_write(struct comedi_device *dev,
259 				struct comedi_subdevice *s,
260 				struct comedi_insn *insn,
261 				unsigned int *data)
262 {
263 	unsigned int chan = CR_CHAN(insn->chanspec);
264 	unsigned int val = s->readback[chan];
265 	int i;
266 
267 	for (i = 0; i < insn->n; i++) {
268 		val = data[i];
269 		outb(val & 0xff, dev->iobase + DT2811_DADAT0LO + 2 * chan);
270 		outb((val >> 8) & 0xff,
271 		     dev->iobase + DT2811_DADAT0HI + 2 * chan);
272 	}
273 	s->readback[chan] = val;
274 
275 	return insn->n;
276 }
277 
dt2811_di_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)278 static int dt2811_di_insn_bits(struct comedi_device *dev,
279 			       struct comedi_subdevice *s,
280 			       struct comedi_insn *insn, unsigned int *data)
281 {
282 	data[1] = inb(dev->iobase + DT2811_DIO);
283 
284 	return insn->n;
285 }
286 
dt2811_do_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)287 static int dt2811_do_insn_bits(struct comedi_device *dev,
288 			       struct comedi_subdevice *s,
289 			       struct comedi_insn *insn,
290 			       unsigned int *data)
291 {
292 	if (comedi_dio_update_state(s, data))
293 		outb(s->state, dev->iobase + DT2811_DIO);
294 
295 	data[1] = s->state;
296 
297 	return insn->n;
298 }
299 
300 /*
301   options[0]   Board base address
302   options[1]   IRQ
303   options[2]   Input configuration
304 		 0 == single-ended
305 		 1 == differential
306 		 2 == pseudo-differential
307   options[3]   Analog input range configuration
308 		 0 == bipolar 5  (-5V -- +5V)
309 		 1 == bipolar 2.5V  (-2.5V -- +2.5V)
310 		 2 == unipolar 5V  (0V -- +5V)
311   options[4]   Analog output 0 range configuration
312 		 0 == bipolar 5  (-5V -- +5V)
313 		 1 == bipolar 2.5V  (-2.5V -- +2.5V)
314 		 2 == unipolar 5V  (0V -- +5V)
315   options[5]   Analog output 1 range configuration
316 		 0 == bipolar 5  (-5V -- +5V)
317 		 1 == bipolar 2.5V  (-2.5V -- +2.5V)
318 		 2 == unipolar 5V  (0V -- +5V)
319 */
dt2811_attach(struct comedi_device * dev,struct comedi_devconfig * it)320 static int dt2811_attach(struct comedi_device *dev, struct comedi_devconfig *it)
321 {
322 	/* int i; */
323 	const struct dt2811_board *board = dev->board_ptr;
324 	struct dt2811_private *devpriv;
325 	int ret;
326 	struct comedi_subdevice *s;
327 
328 	ret = comedi_request_region(dev, it->options[0], 0x8);
329 	if (ret)
330 		return ret;
331 
332 #if 0
333 	outb(0, dev->iobase + DT2811_ADCSR);
334 	udelay(100);
335 	i = inb(dev->iobase + DT2811_ADDATLO);
336 	i = inb(dev->iobase + DT2811_ADDATHI);
337 #endif
338 
339 	ret = comedi_alloc_subdevices(dev, 4);
340 	if (ret)
341 		return ret;
342 
343 	devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
344 	if (!devpriv)
345 		return -ENOMEM;
346 
347 	switch (it->options[2]) {
348 	case 0:
349 		devpriv->adc_mux = adc_singleended;
350 		break;
351 	case 1:
352 		devpriv->adc_mux = adc_diff;
353 		break;
354 	case 2:
355 		devpriv->adc_mux = adc_pseudo_diff;
356 		break;
357 	default:
358 		devpriv->adc_mux = adc_singleended;
359 		break;
360 	}
361 	switch (it->options[4]) {
362 	case 0:
363 		devpriv->dac_range[0] = dac_bipolar_5;
364 		break;
365 	case 1:
366 		devpriv->dac_range[0] = dac_bipolar_2_5;
367 		break;
368 	case 2:
369 		devpriv->dac_range[0] = dac_unipolar_5;
370 		break;
371 	default:
372 		devpriv->dac_range[0] = dac_bipolar_5;
373 		break;
374 	}
375 	switch (it->options[5]) {
376 	case 0:
377 		devpriv->dac_range[1] = dac_bipolar_5;
378 		break;
379 	case 1:
380 		devpriv->dac_range[1] = dac_bipolar_2_5;
381 		break;
382 	case 2:
383 		devpriv->dac_range[1] = dac_unipolar_5;
384 		break;
385 	default:
386 		devpriv->dac_range[1] = dac_bipolar_5;
387 		break;
388 	}
389 
390 	s = &dev->subdevices[0];
391 	/* initialize the ADC subdevice */
392 	s->type = COMEDI_SUBD_AI;
393 	s->subdev_flags = SDF_READABLE | SDF_GROUND;
394 	s->n_chan = devpriv->adc_mux == adc_diff ? 8 : 16;
395 	s->insn_read = dt2811_ai_insn;
396 	s->maxdata = 0xfff;
397 	switch (it->options[3]) {
398 	case 0:
399 	default:
400 		s->range_table = board->bip_5;
401 		break;
402 	case 1:
403 		s->range_table = board->bip_2_5;
404 		break;
405 	case 2:
406 		s->range_table = board->unip_5;
407 		break;
408 	}
409 
410 	s = &dev->subdevices[1];
411 	/* ao subdevice */
412 	s->type = COMEDI_SUBD_AO;
413 	s->subdev_flags = SDF_WRITABLE;
414 	s->n_chan = 2;
415 	s->maxdata = 0xfff;
416 	s->range_table_list = devpriv->range_type_list;
417 	devpriv->range_type_list[0] = dac_range_types[devpriv->dac_range[0]];
418 	devpriv->range_type_list[1] = dac_range_types[devpriv->dac_range[1]];
419 	s->insn_write = dt2811_ao_insn_write;
420 
421 	ret = comedi_alloc_subdev_readback(s);
422 	if (ret)
423 		return ret;
424 
425 	s = &dev->subdevices[2];
426 	/* di subdevice */
427 	s->type = COMEDI_SUBD_DI;
428 	s->subdev_flags = SDF_READABLE;
429 	s->n_chan = 8;
430 	s->insn_bits = dt2811_di_insn_bits;
431 	s->maxdata = 1;
432 	s->range_table = &range_digital;
433 
434 	s = &dev->subdevices[3];
435 	/* do subdevice */
436 	s->type = COMEDI_SUBD_DO;
437 	s->subdev_flags = SDF_WRITABLE;
438 	s->n_chan = 8;
439 	s->insn_bits = dt2811_do_insn_bits;
440 	s->maxdata = 1;
441 	s->state = 0;
442 	s->range_table = &range_digital;
443 
444 	return 0;
445 }
446 
447 static const struct dt2811_board boardtypes[] = {
448 	{
449 		.name		= "dt2811-pgh",
450 		.bip_5		= &range_dt2811_pgh_ai_5_bipolar,
451 		.bip_2_5	= &range_dt2811_pgh_ai_2_5_bipolar,
452 		.unip_5		= &range_dt2811_pgh_ai_5_unipolar,
453 	}, {
454 		.name		= "dt2811-pgl",
455 		.bip_5		= &range_dt2811_pgl_ai_5_bipolar,
456 		.bip_2_5	= &range_dt2811_pgl_ai_2_5_bipolar,
457 		.unip_5		= &range_dt2811_pgl_ai_5_unipolar,
458 	},
459 };
460 
461 static struct comedi_driver dt2811_driver = {
462 	.driver_name	= "dt2811",
463 	.module		= THIS_MODULE,
464 	.attach		= dt2811_attach,
465 	.detach		= comedi_legacy_detach,
466 	.board_name	= &boardtypes[0].name,
467 	.num_names	= ARRAY_SIZE(boardtypes),
468 	.offset		= sizeof(struct dt2811_board),
469 };
470 module_comedi_driver(dt2811_driver);
471 
472 MODULE_AUTHOR("Comedi http://www.comedi.org");
473 MODULE_DESCRIPTION("Comedi low-level driver");
474 MODULE_LICENSE("GPL");
475