1 /*
2  * comedi/drivers/dt2801.c
3  * Device Driver for DataTranslation DT2801
4  *
5  */
6 /*
7 Driver: dt2801
8 Description: Data Translation DT2801 series and DT01-EZ
9 Author: ds
10 Status: works
11 Devices: [Data Translation] DT2801 (dt2801), DT2801-A, DT2801/5716A,
12   DT2805, DT2805/5716A, DT2808, DT2818, DT2809, DT01-EZ
13 
14 This driver can autoprobe the type of board.
15 
16 Configuration options:
17   [0] - I/O port base address
18   [1] - unused
19   [2] - A/D reference 0=differential, 1=single-ended
20   [3] - A/D range
21 	  0 = [-10, 10]
22 	  1 = [0,10]
23   [4] - D/A 0 range
24 	  0 = [-10, 10]
25 	  1 = [-5,5]
26 	  2 = [-2.5,2.5]
27 	  3 = [0,10]
28 	  4 = [0,5]
29   [5] - D/A 1 range (same choices)
30 */
31 
32 #include <linux/module.h>
33 #include "../comedidev.h"
34 #include <linux/delay.h>
35 
36 #define DT2801_TIMEOUT 1000
37 
38 /* Hardware Configuration */
39 /* ====================== */
40 
41 #define DT2801_MAX_DMA_SIZE (64 * 1024)
42 
43 /* define's */
44 /* ====================== */
45 
46 /* Commands */
47 #define DT_C_RESET       0x0
48 #define DT_C_CLEAR_ERR   0x1
49 #define DT_C_READ_ERRREG 0x2
50 #define DT_C_SET_CLOCK   0x3
51 
52 #define DT_C_TEST        0xb
53 #define DT_C_STOP        0xf
54 
55 #define DT_C_SET_DIGIN   0x4
56 #define DT_C_SET_DIGOUT  0x5
57 #define DT_C_READ_DIG    0x6
58 #define DT_C_WRITE_DIG   0x7
59 
60 #define DT_C_WRITE_DAIM  0x8
61 #define DT_C_SET_DA      0x9
62 #define DT_C_WRITE_DA    0xa
63 
64 #define DT_C_READ_ADIM   0xc
65 #define DT_C_SET_AD      0xd
66 #define DT_C_READ_AD     0xe
67 
68 /* Command modifiers (only used with read/write), EXTTRIG can be
69    used with some other commands.
70 */
71 #define DT_MOD_DMA     (1<<4)
72 #define DT_MOD_CONT    (1<<5)
73 #define DT_MOD_EXTCLK  (1<<6)
74 #define DT_MOD_EXTTRIG (1<<7)
75 
76 /* Bits in status register */
77 #define DT_S_DATA_OUT_READY   (1<<0)
78 #define DT_S_DATA_IN_FULL     (1<<1)
79 #define DT_S_READY            (1<<2)
80 #define DT_S_COMMAND          (1<<3)
81 #define DT_S_COMPOSITE_ERROR  (1<<7)
82 
83 /* registers */
84 #define DT2801_DATA		0
85 #define DT2801_STATUS		1
86 #define DT2801_CMD		1
87 
88 #if 0
89 /* ignore 'defined but not used' warning */
90 static const struct comedi_lrange range_dt2801_ai_pgh_bipolar = {
91 	4, {
92 		BIP_RANGE(10),
93 		BIP_RANGE(5),
94 		BIP_RANGE(2.5),
95 		BIP_RANGE(1.25)
96 	}
97 };
98 #endif
99 static const struct comedi_lrange range_dt2801_ai_pgl_bipolar = {
100 	4, {
101 		BIP_RANGE(10),
102 		BIP_RANGE(1),
103 		BIP_RANGE(0.1),
104 		BIP_RANGE(0.02)
105 	}
106 };
107 
108 #if 0
109 /* ignore 'defined but not used' warning */
110 static const struct comedi_lrange range_dt2801_ai_pgh_unipolar = {
111 	4, {
112 		UNI_RANGE(10),
113 		UNI_RANGE(5),
114 		UNI_RANGE(2.5),
115 		UNI_RANGE(1.25)
116 	}
117 };
118 #endif
119 static const struct comedi_lrange range_dt2801_ai_pgl_unipolar = {
120 	4, {
121 		UNI_RANGE(10),
122 		UNI_RANGE(1),
123 		UNI_RANGE(0.1),
124 		UNI_RANGE(0.02)
125 	}
126 };
127 
128 struct dt2801_board {
129 	const char *name;
130 	int boardcode;
131 	int ad_diff;
132 	int ad_chan;
133 	int adbits;
134 	int adrangetype;
135 	int dabits;
136 };
137 
138 /* Typeid's for the different boards of the DT2801-series
139    (taken from the test-software, that comes with the board)
140    */
141 static const struct dt2801_board boardtypes[] = {
142 	{
143 	 .name = "dt2801",
144 	 .boardcode = 0x09,
145 	 .ad_diff = 2,
146 	 .ad_chan = 16,
147 	 .adbits = 12,
148 	 .adrangetype = 0,
149 	 .dabits = 12},
150 	{
151 	 .name = "dt2801-a",
152 	 .boardcode = 0x52,
153 	 .ad_diff = 2,
154 	 .ad_chan = 16,
155 	 .adbits = 12,
156 	 .adrangetype = 0,
157 	 .dabits = 12},
158 	{
159 	 .name = "dt2801/5716a",
160 	 .boardcode = 0x82,
161 	 .ad_diff = 1,
162 	 .ad_chan = 16,
163 	 .adbits = 16,
164 	 .adrangetype = 1,
165 	 .dabits = 12},
166 	{
167 	 .name = "dt2805",
168 	 .boardcode = 0x12,
169 	 .ad_diff = 1,
170 	 .ad_chan = 16,
171 	 .adbits = 12,
172 	 .adrangetype = 0,
173 	 .dabits = 12},
174 	{
175 	 .name = "dt2805/5716a",
176 	 .boardcode = 0x92,
177 	 .ad_diff = 1,
178 	 .ad_chan = 16,
179 	 .adbits = 16,
180 	 .adrangetype = 1,
181 	 .dabits = 12},
182 	{
183 	 .name = "dt2808",
184 	 .boardcode = 0x20,
185 	 .ad_diff = 0,
186 	 .ad_chan = 16,
187 	 .adbits = 12,
188 	 .adrangetype = 2,
189 	 .dabits = 8},
190 	{
191 	 .name = "dt2818",
192 	 .boardcode = 0xa2,
193 	 .ad_diff = 0,
194 	 .ad_chan = 4,
195 	 .adbits = 12,
196 	 .adrangetype = 0,
197 	 .dabits = 12},
198 	{
199 	 .name = "dt2809",
200 	 .boardcode = 0xb0,
201 	 .ad_diff = 0,
202 	 .ad_chan = 8,
203 	 .adbits = 12,
204 	 .adrangetype = 1,
205 	 .dabits = 12},
206 };
207 
208 struct dt2801_private {
209 	const struct comedi_lrange *dac_range_types[2];
210 };
211 
212 /* These are the low-level routines:
213    writecommand: write a command to the board
214    writedata: write data byte
215    readdata: read data byte
216  */
217 
218 /* Only checks DataOutReady-flag, not the Ready-flag as it is done
219    in the examples of the manual. I don't see why this should be
220    necessary. */
dt2801_readdata(struct comedi_device * dev,int * data)221 static int dt2801_readdata(struct comedi_device *dev, int *data)
222 {
223 	int stat = 0;
224 	int timeout = DT2801_TIMEOUT;
225 
226 	do {
227 		stat = inb_p(dev->iobase + DT2801_STATUS);
228 		if (stat & (DT_S_COMPOSITE_ERROR | DT_S_READY))
229 			return stat;
230 		if (stat & DT_S_DATA_OUT_READY) {
231 			*data = inb_p(dev->iobase + DT2801_DATA);
232 			return 0;
233 		}
234 	} while (--timeout > 0);
235 
236 	return -ETIME;
237 }
238 
dt2801_readdata2(struct comedi_device * dev,int * data)239 static int dt2801_readdata2(struct comedi_device *dev, int *data)
240 {
241 	int lb = 0;
242 	int hb = 0;
243 	int ret;
244 
245 	ret = dt2801_readdata(dev, &lb);
246 	if (ret)
247 		return ret;
248 	ret = dt2801_readdata(dev, &hb);
249 	if (ret)
250 		return ret;
251 
252 	*data = (hb << 8) + lb;
253 	return 0;
254 }
255 
dt2801_writedata(struct comedi_device * dev,unsigned int data)256 static int dt2801_writedata(struct comedi_device *dev, unsigned int data)
257 {
258 	int stat = 0;
259 	int timeout = DT2801_TIMEOUT;
260 
261 	do {
262 		stat = inb_p(dev->iobase + DT2801_STATUS);
263 
264 		if (stat & DT_S_COMPOSITE_ERROR)
265 			return stat;
266 		if (!(stat & DT_S_DATA_IN_FULL)) {
267 			outb_p(data & 0xff, dev->iobase + DT2801_DATA);
268 			return 0;
269 		}
270 	} while (--timeout > 0);
271 
272 	return -ETIME;
273 }
274 
dt2801_writedata2(struct comedi_device * dev,unsigned int data)275 static int dt2801_writedata2(struct comedi_device *dev, unsigned int data)
276 {
277 	int ret;
278 
279 	ret = dt2801_writedata(dev, data & 0xff);
280 	if (ret < 0)
281 		return ret;
282 	ret = dt2801_writedata(dev, data >> 8);
283 	if (ret < 0)
284 		return ret;
285 
286 	return 0;
287 }
288 
dt2801_wait_for_ready(struct comedi_device * dev)289 static int dt2801_wait_for_ready(struct comedi_device *dev)
290 {
291 	int timeout = DT2801_TIMEOUT;
292 	int stat;
293 
294 	stat = inb_p(dev->iobase + DT2801_STATUS);
295 	if (stat & DT_S_READY)
296 		return 0;
297 	do {
298 		stat = inb_p(dev->iobase + DT2801_STATUS);
299 
300 		if (stat & DT_S_COMPOSITE_ERROR)
301 			return stat;
302 		if (stat & DT_S_READY)
303 			return 0;
304 	} while (--timeout > 0);
305 
306 	return -ETIME;
307 }
308 
dt2801_writecmd(struct comedi_device * dev,int command)309 static void dt2801_writecmd(struct comedi_device *dev, int command)
310 {
311 	int stat;
312 
313 	dt2801_wait_for_ready(dev);
314 
315 	stat = inb_p(dev->iobase + DT2801_STATUS);
316 	if (stat & DT_S_COMPOSITE_ERROR) {
317 		dev_dbg(dev->class_dev,
318 			"composite-error in %s, ignoring\n", __func__);
319 	}
320 	if (!(stat & DT_S_READY))
321 		dev_dbg(dev->class_dev, "!ready in %s, ignoring\n", __func__);
322 	outb_p(command, dev->iobase + DT2801_CMD);
323 }
324 
dt2801_reset(struct comedi_device * dev)325 static int dt2801_reset(struct comedi_device *dev)
326 {
327 	int board_code = 0;
328 	unsigned int stat;
329 	int timeout;
330 
331 	/* pull random data from data port */
332 	inb_p(dev->iobase + DT2801_DATA);
333 	inb_p(dev->iobase + DT2801_DATA);
334 	inb_p(dev->iobase + DT2801_DATA);
335 	inb_p(dev->iobase + DT2801_DATA);
336 
337 	/* dt2801_writecmd(dev,DT_C_STOP); */
338 	outb_p(DT_C_STOP, dev->iobase + DT2801_CMD);
339 
340 	/* dt2801_wait_for_ready(dev); */
341 	udelay(100);
342 	timeout = 10000;
343 	do {
344 		stat = inb_p(dev->iobase + DT2801_STATUS);
345 		if (stat & DT_S_READY)
346 			break;
347 	} while (timeout--);
348 	if (!timeout)
349 		dev_dbg(dev->class_dev, "timeout 1 status=0x%02x\n", stat);
350 
351 	/* dt2801_readdata(dev,&board_code); */
352 
353 	outb_p(DT_C_RESET, dev->iobase + DT2801_CMD);
354 	/* dt2801_writecmd(dev,DT_C_RESET); */
355 
356 	udelay(100);
357 	timeout = 10000;
358 	do {
359 		stat = inb_p(dev->iobase + DT2801_STATUS);
360 		if (stat & DT_S_READY)
361 			break;
362 	} while (timeout--);
363 	if (!timeout)
364 		dev_dbg(dev->class_dev, "timeout 2 status=0x%02x\n", stat);
365 
366 	dt2801_readdata(dev, &board_code);
367 
368 	return board_code;
369 }
370 
probe_number_of_ai_chans(struct comedi_device * dev)371 static int probe_number_of_ai_chans(struct comedi_device *dev)
372 {
373 	int n_chans;
374 	int stat;
375 	int data;
376 
377 	for (n_chans = 0; n_chans < 16; n_chans++) {
378 		dt2801_writecmd(dev, DT_C_READ_ADIM);
379 		dt2801_writedata(dev, 0);
380 		dt2801_writedata(dev, n_chans);
381 		stat = dt2801_readdata2(dev, &data);
382 
383 		if (stat)
384 			break;
385 	}
386 
387 	dt2801_reset(dev);
388 	dt2801_reset(dev);
389 
390 	return n_chans;
391 }
392 
393 static const struct comedi_lrange *dac_range_table[] = {
394 	&range_bipolar10,
395 	&range_bipolar5,
396 	&range_bipolar2_5,
397 	&range_unipolar10,
398 	&range_unipolar5
399 };
400 
dac_range_lkup(int opt)401 static const struct comedi_lrange *dac_range_lkup(int opt)
402 {
403 	if (opt < 0 || opt >= 5)
404 		return &range_unknown;
405 	return dac_range_table[opt];
406 }
407 
ai_range_lkup(int type,int opt)408 static const struct comedi_lrange *ai_range_lkup(int type, int opt)
409 {
410 	switch (type) {
411 	case 0:
412 		return (opt) ?
413 		    &range_dt2801_ai_pgl_unipolar :
414 		    &range_dt2801_ai_pgl_bipolar;
415 	case 1:
416 		return (opt) ? &range_unipolar10 : &range_bipolar10;
417 	case 2:
418 		return &range_unipolar5;
419 	}
420 	return &range_unknown;
421 }
422 
dt2801_error(struct comedi_device * dev,int stat)423 static int dt2801_error(struct comedi_device *dev, int stat)
424 {
425 	if (stat < 0) {
426 		if (stat == -ETIME)
427 			dev_dbg(dev->class_dev, "timeout\n");
428 		else
429 			dev_dbg(dev->class_dev, "error %d\n", stat);
430 		return stat;
431 	}
432 	dev_dbg(dev->class_dev, "error status 0x%02x, resetting...\n", stat);
433 
434 	dt2801_reset(dev);
435 	dt2801_reset(dev);
436 
437 	return -EIO;
438 }
439 
dt2801_ai_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)440 static int dt2801_ai_insn_read(struct comedi_device *dev,
441 			       struct comedi_subdevice *s,
442 			       struct comedi_insn *insn, unsigned int *data)
443 {
444 	int d;
445 	int stat;
446 	int i;
447 
448 	for (i = 0; i < insn->n; i++) {
449 		dt2801_writecmd(dev, DT_C_READ_ADIM);
450 		dt2801_writedata(dev, CR_RANGE(insn->chanspec));
451 		dt2801_writedata(dev, CR_CHAN(insn->chanspec));
452 		stat = dt2801_readdata2(dev, &d);
453 
454 		if (stat != 0)
455 			return dt2801_error(dev, stat);
456 
457 		data[i] = d;
458 	}
459 
460 	return i;
461 }
462 
dt2801_ao_insn_write(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)463 static int dt2801_ao_insn_write(struct comedi_device *dev,
464 				struct comedi_subdevice *s,
465 				struct comedi_insn *insn,
466 				unsigned int *data)
467 {
468 	unsigned int chan = CR_CHAN(insn->chanspec);
469 
470 	dt2801_writecmd(dev, DT_C_WRITE_DAIM);
471 	dt2801_writedata(dev, chan);
472 	dt2801_writedata2(dev, data[0]);
473 
474 	s->readback[chan] = data[0];
475 
476 	return 1;
477 }
478 
dt2801_dio_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)479 static int dt2801_dio_insn_bits(struct comedi_device *dev,
480 				struct comedi_subdevice *s,
481 				struct comedi_insn *insn,
482 				unsigned int *data)
483 {
484 	int which = (s == &dev->subdevices[3]) ? 1 : 0;
485 	unsigned int val = 0;
486 
487 	if (comedi_dio_update_state(s, data)) {
488 		dt2801_writecmd(dev, DT_C_WRITE_DIG);
489 		dt2801_writedata(dev, which);
490 		dt2801_writedata(dev, s->state);
491 	}
492 
493 	dt2801_writecmd(dev, DT_C_READ_DIG);
494 	dt2801_writedata(dev, which);
495 	dt2801_readdata(dev, &val);
496 
497 	data[1] = val;
498 
499 	return insn->n;
500 }
501 
dt2801_dio_insn_config(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)502 static int dt2801_dio_insn_config(struct comedi_device *dev,
503 				  struct comedi_subdevice *s,
504 				  struct comedi_insn *insn,
505 				  unsigned int *data)
506 {
507 	int ret;
508 
509 	ret = comedi_dio_insn_config(dev, s, insn, data, 0xff);
510 	if (ret)
511 		return ret;
512 
513 	dt2801_writecmd(dev, s->io_bits ? DT_C_SET_DIGOUT : DT_C_SET_DIGIN);
514 	dt2801_writedata(dev, (s == &dev->subdevices[3]) ? 1 : 0);
515 
516 	return insn->n;
517 }
518 
519 /*
520    options:
521 	[0] - i/o base
522 	[1] - unused
523 	[2] - a/d 0=differential, 1=single-ended
524 	[3] - a/d range 0=[-10,10], 1=[0,10]
525 	[4] - dac0 range 0=[-10,10], 1=[-5,5], 2=[-2.5,2.5] 3=[0,10], 4=[0,5]
526 	[5] - dac1 range 0=[-10,10], 1=[-5,5], 2=[-2.5,2.5] 3=[0,10], 4=[0,5]
527 */
dt2801_attach(struct comedi_device * dev,struct comedi_devconfig * it)528 static int dt2801_attach(struct comedi_device *dev, struct comedi_devconfig *it)
529 {
530 	const struct dt2801_board *board;
531 	struct dt2801_private *devpriv;
532 	struct comedi_subdevice *s;
533 	int board_code, type;
534 	int ret = 0;
535 	int n_ai_chans;
536 
537 	ret = comedi_request_region(dev, it->options[0], 0x2);
538 	if (ret)
539 		return ret;
540 
541 	/* do some checking */
542 
543 	board_code = dt2801_reset(dev);
544 
545 	/* heh.  if it didn't work, try it again. */
546 	if (!board_code)
547 		board_code = dt2801_reset(dev);
548 
549 	for (type = 0; type < ARRAY_SIZE(boardtypes); type++) {
550 		if (boardtypes[type].boardcode == board_code)
551 			goto havetype;
552 	}
553 	dev_dbg(dev->class_dev,
554 		"unrecognized board code=0x%02x, contact author\n", board_code);
555 	type = 0;
556 
557 havetype:
558 	dev->board_ptr = boardtypes + type;
559 	board = dev->board_ptr;
560 
561 	n_ai_chans = probe_number_of_ai_chans(dev);
562 
563 	ret = comedi_alloc_subdevices(dev, 4);
564 	if (ret)
565 		goto out;
566 
567 	devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
568 	if (!devpriv)
569 		return -ENOMEM;
570 
571 	dev->board_name = board->name;
572 
573 	s = &dev->subdevices[0];
574 	/* ai subdevice */
575 	s->type = COMEDI_SUBD_AI;
576 	s->subdev_flags = SDF_READABLE | SDF_GROUND;
577 #if 1
578 	s->n_chan = n_ai_chans;
579 #else
580 	if (it->options[2])
581 		s->n_chan = board->ad_chan;
582 	else
583 		s->n_chan = board->ad_chan / 2;
584 #endif
585 	s->maxdata = (1 << board->adbits) - 1;
586 	s->range_table = ai_range_lkup(board->adrangetype, it->options[3]);
587 	s->insn_read = dt2801_ai_insn_read;
588 
589 	s = &dev->subdevices[1];
590 	/* ao subdevice */
591 	s->type = COMEDI_SUBD_AO;
592 	s->subdev_flags = SDF_WRITABLE;
593 	s->n_chan = 2;
594 	s->maxdata = (1 << board->dabits) - 1;
595 	s->range_table_list = devpriv->dac_range_types;
596 	devpriv->dac_range_types[0] = dac_range_lkup(it->options[4]);
597 	devpriv->dac_range_types[1] = dac_range_lkup(it->options[5]);
598 	s->insn_write = dt2801_ao_insn_write;
599 
600 	ret = comedi_alloc_subdev_readback(s);
601 	if (ret)
602 		return ret;
603 
604 	s = &dev->subdevices[2];
605 	/* 1st digital subdevice */
606 	s->type = COMEDI_SUBD_DIO;
607 	s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
608 	s->n_chan = 8;
609 	s->maxdata = 1;
610 	s->range_table = &range_digital;
611 	s->insn_bits = dt2801_dio_insn_bits;
612 	s->insn_config = dt2801_dio_insn_config;
613 
614 	s = &dev->subdevices[3];
615 	/* 2nd digital subdevice */
616 	s->type = COMEDI_SUBD_DIO;
617 	s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
618 	s->n_chan = 8;
619 	s->maxdata = 1;
620 	s->range_table = &range_digital;
621 	s->insn_bits = dt2801_dio_insn_bits;
622 	s->insn_config = dt2801_dio_insn_config;
623 
624 	ret = 0;
625 out:
626 	return ret;
627 }
628 
629 static struct comedi_driver dt2801_driver = {
630 	.driver_name	= "dt2801",
631 	.module		= THIS_MODULE,
632 	.attach		= dt2801_attach,
633 	.detach		= comedi_legacy_detach,
634 };
635 module_comedi_driver(dt2801_driver);
636 
637 MODULE_AUTHOR("Comedi http://www.comedi.org");
638 MODULE_DESCRIPTION("Comedi low-level driver");
639 MODULE_LICENSE("GPL");
640