xref: /openbmc/linux/drivers/comedi/drivers/dt2801.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
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