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