xref: /openbmc/linux/drivers/comedi/drivers/dt3000.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
1  // SPDX-License-Identifier: GPL-2.0+
2  /*
3   * dt3000.c
4   * Data Translation DT3000 series driver
5   *
6   * COMEDI - Linux Control and Measurement Device Interface
7   * Copyright (C) 1999 David A. Schleef <ds@schleef.org>
8   */
9  
10  /*
11   * Driver: dt3000
12   * Description: Data Translation DT3000 series
13   * Devices: [Data Translation] DT3001 (dt3000), DT3001-PGL, DT3002, DT3003,
14   *   DT3003-PGL, DT3004, DT3005, DT3004-200
15   * Author: ds
16   * Updated: Mon, 14 Apr 2008 15:41:24 +0100
17   * Status: works
18   *
19   * Configuration Options: not applicable, uses PCI auto config
20   *
21   * There is code to support AI commands, but it may not work.
22   *
23   * AO commands are not supported.
24   */
25  
26  /*
27   * The DT3000 series is Data Translation's attempt to make a PCI
28   * data acquisition board.  The design of this series is very nice,
29   * since each board has an on-board DSP (Texas Instruments TMS320C52).
30   * However, a few details are a little annoying.  The boards lack
31   * bus-mastering DMA, which eliminates them from serious work.
32   * They also are not capable of autocalibration, which is a common
33   * feature in modern hardware.  The default firmware is pretty bad,
34   * making it nearly impossible to write an RT compatible driver.
35   * It would make an interesting project to write a decent firmware
36   * for these boards.
37   *
38   * Data Translation originally wanted an NDA for the documentation
39   * for the 3k series.  However, if you ask nicely, they might send
40   * you the docs without one, also.
41   */
42  
43  #include <linux/module.h>
44  #include <linux/delay.h>
45  #include <linux/interrupt.h>
46  #include <linux/comedi/comedi_pci.h>
47  
48  /*
49   * PCI BAR0 - dual-ported RAM location definitions (dev->mmio)
50   */
51  #define DPR_DAC_BUFFER		(4 * 0x000)
52  #define DPR_ADC_BUFFER		(4 * 0x800)
53  #define DPR_COMMAND		(4 * 0xfd3)
54  #define DPR_SUBSYS		(4 * 0xfd3)
55  #define DPR_SUBSYS_AI		0
56  #define DPR_SUBSYS_AO		1
57  #define DPR_SUBSYS_DIN		2
58  #define DPR_SUBSYS_DOUT		3
59  #define DPR_SUBSYS_MEM		4
60  #define DPR_SUBSYS_CT		5
61  #define DPR_ENCODE		(4 * 0xfd4)
62  #define DPR_PARAMS(x)		(4 * (0xfd5 + (x)))
63  #define DPR_TICK_REG_LO		(4 * 0xff5)
64  #define DPR_TICK_REG_HI		(4 * 0xff6)
65  #define DPR_DA_BUF_FRONT	(4 * 0xff7)
66  #define DPR_DA_BUF_REAR		(4 * 0xff8)
67  #define DPR_AD_BUF_FRONT	(4 * 0xff9)
68  #define DPR_AD_BUF_REAR		(4 * 0xffa)
69  #define DPR_INT_MASK		(4 * 0xffb)
70  #define DPR_INTR_FLAG		(4 * 0xffc)
71  #define DPR_INTR_CMDONE		BIT(7)
72  #define DPR_INTR_CTDONE		BIT(6)
73  #define DPR_INTR_DAHWERR	BIT(5)
74  #define DPR_INTR_DASWERR	BIT(4)
75  #define DPR_INTR_DAEMPTY	BIT(3)
76  #define DPR_INTR_ADHWERR	BIT(2)
77  #define DPR_INTR_ADSWERR	BIT(1)
78  #define DPR_INTR_ADFULL		BIT(0)
79  #define DPR_RESPONSE_MBX	(4 * 0xffe)
80  #define DPR_CMD_MBX		(4 * 0xfff)
81  #define DPR_CMD_COMPLETION(x)	((x) << 8)
82  #define DPR_CMD_NOTPROCESSED	DPR_CMD_COMPLETION(0x00)
83  #define DPR_CMD_NOERROR		DPR_CMD_COMPLETION(0x55)
84  #define DPR_CMD_ERROR		DPR_CMD_COMPLETION(0xaa)
85  #define DPR_CMD_NOTSUPPORTED	DPR_CMD_COMPLETION(0xff)
86  #define DPR_CMD_COMPLETION_MASK	DPR_CMD_COMPLETION(0xff)
87  #define DPR_CMD(x)		((x) << 0)
88  #define DPR_CMD_GETBRDINFO	DPR_CMD(0)
89  #define DPR_CMD_CONFIG		DPR_CMD(1)
90  #define DPR_CMD_GETCONFIG	DPR_CMD(2)
91  #define DPR_CMD_START		DPR_CMD(3)
92  #define DPR_CMD_STOP		DPR_CMD(4)
93  #define DPR_CMD_READSINGLE	DPR_CMD(5)
94  #define DPR_CMD_WRITESINGLE	DPR_CMD(6)
95  #define DPR_CMD_CALCCLOCK	DPR_CMD(7)
96  #define DPR_CMD_READEVENTS	DPR_CMD(8)
97  #define DPR_CMD_WRITECTCTRL	DPR_CMD(16)
98  #define DPR_CMD_READCTCTRL	DPR_CMD(17)
99  #define DPR_CMD_WRITECT		DPR_CMD(18)
100  #define DPR_CMD_READCT		DPR_CMD(19)
101  #define DPR_CMD_WRITEDATA	DPR_CMD(32)
102  #define DPR_CMD_READDATA	DPR_CMD(33)
103  #define DPR_CMD_WRITEIO		DPR_CMD(34)
104  #define DPR_CMD_READIO		DPR_CMD(35)
105  #define DPR_CMD_WRITECODE	DPR_CMD(36)
106  #define DPR_CMD_READCODE	DPR_CMD(37)
107  #define DPR_CMD_EXECUTE		DPR_CMD(38)
108  #define DPR_CMD_HALT		DPR_CMD(48)
109  #define DPR_CMD_MASK		DPR_CMD(0xff)
110  
111  #define DPR_PARAM5_AD_TRIG(x)		(((x) & 0x7) << 2)
112  #define DPR_PARAM5_AD_TRIG_INT		DPR_PARAM5_AD_TRIG(0)
113  #define DPR_PARAM5_AD_TRIG_EXT		DPR_PARAM5_AD_TRIG(1)
114  #define DPR_PARAM5_AD_TRIG_INT_RETRIG	DPR_PARAM5_AD_TRIG(2)
115  #define DPR_PARAM5_AD_TRIG_EXT_RETRIG	DPR_PARAM5_AD_TRIG(3)
116  #define DPR_PARAM5_AD_TRIG_INT_RETRIG2	DPR_PARAM5_AD_TRIG(4)
117  
118  #define DPR_PARAM6_AD_DIFF		BIT(0)
119  
120  #define DPR_AI_FIFO_DEPTH		2003
121  #define DPR_AO_FIFO_DEPTH		2048
122  
123  #define DPR_EXTERNAL_CLOCK		1
124  #define DPR_RISING_EDGE			2
125  
126  #define DPR_TMODE_MASK			0x1c
127  
128  #define DPR_CMD_TIMEOUT			100
129  
130  static const struct comedi_lrange range_dt3000_ai = {
131  	4, {
132  		BIP_RANGE(10),
133  		BIP_RANGE(5),
134  		BIP_RANGE(2.5),
135  		BIP_RANGE(1.25)
136  	}
137  };
138  
139  static const struct comedi_lrange range_dt3000_ai_pgl = {
140  	4, {
141  		BIP_RANGE(10),
142  		BIP_RANGE(1),
143  		BIP_RANGE(0.1),
144  		BIP_RANGE(0.02)
145  	}
146  };
147  
148  enum dt3k_boardid {
149  	BOARD_DT3001,
150  	BOARD_DT3001_PGL,
151  	BOARD_DT3002,
152  	BOARD_DT3003,
153  	BOARD_DT3003_PGL,
154  	BOARD_DT3004,
155  	BOARD_DT3005,
156  };
157  
158  struct dt3k_boardtype {
159  	const char *name;
160  	int adchan;
161  	int ai_speed;
162  	const struct comedi_lrange *adrange;
163  	unsigned int ai_is_16bit:1;
164  	unsigned int has_ao:1;
165  };
166  
167  static const struct dt3k_boardtype dt3k_boardtypes[] = {
168  	[BOARD_DT3001] = {
169  		.name		= "dt3001",
170  		.adchan		= 16,
171  		.adrange	= &range_dt3000_ai,
172  		.ai_speed	= 3000,
173  		.has_ao		= 1,
174  	},
175  	[BOARD_DT3001_PGL] = {
176  		.name		= "dt3001-pgl",
177  		.adchan		= 16,
178  		.adrange	= &range_dt3000_ai_pgl,
179  		.ai_speed	= 3000,
180  		.has_ao		= 1,
181  	},
182  	[BOARD_DT3002] = {
183  		.name		= "dt3002",
184  		.adchan		= 32,
185  		.adrange	= &range_dt3000_ai,
186  		.ai_speed	= 3000,
187  	},
188  	[BOARD_DT3003] = {
189  		.name		= "dt3003",
190  		.adchan		= 64,
191  		.adrange	= &range_dt3000_ai,
192  		.ai_speed	= 3000,
193  		.has_ao		= 1,
194  	},
195  	[BOARD_DT3003_PGL] = {
196  		.name		= "dt3003-pgl",
197  		.adchan		= 64,
198  		.adrange	= &range_dt3000_ai_pgl,
199  		.ai_speed	= 3000,
200  		.has_ao		= 1,
201  	},
202  	[BOARD_DT3004] = {
203  		.name		= "dt3004",
204  		.adchan		= 16,
205  		.adrange	= &range_dt3000_ai,
206  		.ai_speed	= 10000,
207  		.ai_is_16bit	= 1,
208  		.has_ao		= 1,
209  	},
210  	[BOARD_DT3005] = {
211  		.name		= "dt3005",	/* a.k.a. 3004-200 */
212  		.adchan		= 16,
213  		.adrange	= &range_dt3000_ai,
214  		.ai_speed	= 5000,
215  		.ai_is_16bit	= 1,
216  		.has_ao		= 1,
217  	},
218  };
219  
220  struct dt3k_private {
221  	unsigned int lock;
222  	unsigned int ai_front;
223  	unsigned int ai_rear;
224  };
225  
dt3k_send_cmd(struct comedi_device * dev,unsigned int cmd)226  static void dt3k_send_cmd(struct comedi_device *dev, unsigned int cmd)
227  {
228  	int i;
229  	unsigned int status = 0;
230  
231  	writew(cmd, dev->mmio + DPR_CMD_MBX);
232  
233  	for (i = 0; i < DPR_CMD_TIMEOUT; i++) {
234  		status = readw(dev->mmio + DPR_CMD_MBX);
235  		status &= DPR_CMD_COMPLETION_MASK;
236  		if (status != DPR_CMD_NOTPROCESSED)
237  			break;
238  		udelay(1);
239  	}
240  
241  	if (status != DPR_CMD_NOERROR)
242  		dev_dbg(dev->class_dev, "%s: timeout/error status=0x%04x\n",
243  			__func__, status);
244  }
245  
dt3k_readsingle(struct comedi_device * dev,unsigned int subsys,unsigned int chan,unsigned int gain)246  static unsigned int dt3k_readsingle(struct comedi_device *dev,
247  				    unsigned int subsys, unsigned int chan,
248  				    unsigned int gain)
249  {
250  	writew(subsys, dev->mmio + DPR_SUBSYS);
251  
252  	writew(chan, dev->mmio + DPR_PARAMS(0));
253  	writew(gain, dev->mmio + DPR_PARAMS(1));
254  
255  	dt3k_send_cmd(dev, DPR_CMD_READSINGLE);
256  
257  	return readw(dev->mmio + DPR_PARAMS(2));
258  }
259  
dt3k_writesingle(struct comedi_device * dev,unsigned int subsys,unsigned int chan,unsigned int data)260  static void dt3k_writesingle(struct comedi_device *dev, unsigned int subsys,
261  			     unsigned int chan, unsigned int data)
262  {
263  	writew(subsys, dev->mmio + DPR_SUBSYS);
264  
265  	writew(chan, dev->mmio + DPR_PARAMS(0));
266  	writew(0, dev->mmio + DPR_PARAMS(1));
267  	writew(data, dev->mmio + DPR_PARAMS(2));
268  
269  	dt3k_send_cmd(dev, DPR_CMD_WRITESINGLE);
270  }
271  
dt3k_ai_empty_fifo(struct comedi_device * dev,struct comedi_subdevice * s)272  static void dt3k_ai_empty_fifo(struct comedi_device *dev,
273  			       struct comedi_subdevice *s)
274  {
275  	struct dt3k_private *devpriv = dev->private;
276  	int front;
277  	int rear;
278  	int count;
279  	int i;
280  	unsigned short data;
281  
282  	front = readw(dev->mmio + DPR_AD_BUF_FRONT);
283  	count = front - devpriv->ai_front;
284  	if (count < 0)
285  		count += DPR_AI_FIFO_DEPTH;
286  
287  	rear = devpriv->ai_rear;
288  
289  	for (i = 0; i < count; i++) {
290  		data = readw(dev->mmio + DPR_ADC_BUFFER + rear);
291  		comedi_buf_write_samples(s, &data, 1);
292  		rear++;
293  		if (rear >= DPR_AI_FIFO_DEPTH)
294  			rear = 0;
295  	}
296  
297  	devpriv->ai_rear = rear;
298  	writew(rear, dev->mmio + DPR_AD_BUF_REAR);
299  }
300  
dt3k_ai_cancel(struct comedi_device * dev,struct comedi_subdevice * s)301  static int dt3k_ai_cancel(struct comedi_device *dev,
302  			  struct comedi_subdevice *s)
303  {
304  	writew(DPR_SUBSYS_AI, dev->mmio + DPR_SUBSYS);
305  	dt3k_send_cmd(dev, DPR_CMD_STOP);
306  
307  	writew(0, dev->mmio + DPR_INT_MASK);
308  
309  	return 0;
310  }
311  
312  static int debug_n_ints;
313  
314  /* FIXME! Assumes shared interrupt is for this card. */
315  /* What's this debug_n_ints stuff? Obviously needs some work... */
dt3k_interrupt(int irq,void * d)316  static irqreturn_t dt3k_interrupt(int irq, void *d)
317  {
318  	struct comedi_device *dev = d;
319  	struct comedi_subdevice *s = dev->read_subdev;
320  	unsigned int status;
321  
322  	if (!dev->attached)
323  		return IRQ_NONE;
324  
325  	status = readw(dev->mmio + DPR_INTR_FLAG);
326  
327  	if (status & DPR_INTR_ADFULL)
328  		dt3k_ai_empty_fifo(dev, s);
329  
330  	if (status & (DPR_INTR_ADSWERR | DPR_INTR_ADHWERR))
331  		s->async->events |= COMEDI_CB_ERROR;
332  
333  	debug_n_ints++;
334  	if (debug_n_ints >= 10)
335  		s->async->events |= COMEDI_CB_EOA;
336  
337  	comedi_handle_events(dev, s);
338  	return IRQ_HANDLED;
339  }
340  
dt3k_ns_to_timer(unsigned int timer_base,unsigned int * nanosec,unsigned int flags)341  static int dt3k_ns_to_timer(unsigned int timer_base, unsigned int *nanosec,
342  			    unsigned int flags)
343  {
344  	unsigned int divider, base, prescale;
345  
346  	/* This function needs improvement */
347  	/* Don't know if divider==0 works. */
348  
349  	for (prescale = 0; prescale < 16; prescale++) {
350  		base = timer_base * (prescale + 1);
351  		switch (flags & CMDF_ROUND_MASK) {
352  		case CMDF_ROUND_NEAREST:
353  		default:
354  			divider = DIV_ROUND_CLOSEST(*nanosec, base);
355  			break;
356  		case CMDF_ROUND_DOWN:
357  			divider = (*nanosec) / base;
358  			break;
359  		case CMDF_ROUND_UP:
360  			divider = DIV_ROUND_UP(*nanosec, base);
361  			break;
362  		}
363  		if (divider < 65536) {
364  			*nanosec = divider * base;
365  			return (prescale << 16) | (divider);
366  		}
367  	}
368  
369  	prescale = 15;
370  	base = timer_base * (prescale + 1);
371  	divider = 65535;
372  	*nanosec = divider * base;
373  	return (prescale << 16) | (divider);
374  }
375  
dt3k_ai_cmdtest(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_cmd * cmd)376  static int dt3k_ai_cmdtest(struct comedi_device *dev,
377  			   struct comedi_subdevice *s, struct comedi_cmd *cmd)
378  {
379  	const struct dt3k_boardtype *board = dev->board_ptr;
380  	int err = 0;
381  	unsigned int arg;
382  
383  	/* Step 1 : check if triggers are trivially valid */
384  
385  	err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
386  	err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
387  	err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
388  	err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
389  	err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_COUNT);
390  
391  	if (err)
392  		return 1;
393  
394  	/* Step 2a : make sure trigger sources are unique */
395  	/* Step 2b : and mutually compatible */
396  
397  	/* Step 3: check if arguments are trivially valid */
398  
399  	err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
400  
401  	if (cmd->scan_begin_src == TRIG_TIMER) {
402  		err |= comedi_check_trigger_arg_min(&cmd->scan_begin_arg,
403  						    board->ai_speed);
404  		err |= comedi_check_trigger_arg_max(&cmd->scan_begin_arg,
405  						    100 * 16 * 65535);
406  	}
407  
408  	if (cmd->convert_src == TRIG_TIMER) {
409  		err |= comedi_check_trigger_arg_min(&cmd->convert_arg,
410  						    board->ai_speed);
411  		err |= comedi_check_trigger_arg_max(&cmd->convert_arg,
412  						    50 * 16 * 65535);
413  	}
414  
415  	err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
416  					   cmd->chanlist_len);
417  
418  	if (cmd->stop_src == TRIG_COUNT)
419  		err |= comedi_check_trigger_arg_max(&cmd->stop_arg, 0x00ffffff);
420  	else	/* TRIG_NONE */
421  		err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
422  
423  	if (err)
424  		return 3;
425  
426  	/* step 4: fix up any arguments */
427  
428  	if (cmd->scan_begin_src == TRIG_TIMER) {
429  		arg = cmd->scan_begin_arg;
430  		dt3k_ns_to_timer(100, &arg, cmd->flags);
431  		err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, arg);
432  	}
433  
434  	if (cmd->convert_src == TRIG_TIMER) {
435  		arg = cmd->convert_arg;
436  		dt3k_ns_to_timer(50, &arg, cmd->flags);
437  		err |= comedi_check_trigger_arg_is(&cmd->convert_arg, arg);
438  
439  		if (cmd->scan_begin_src == TRIG_TIMER) {
440  			arg = cmd->convert_arg * cmd->scan_end_arg;
441  			err |= comedi_check_trigger_arg_min(
442  				&cmd->scan_begin_arg, arg);
443  		}
444  	}
445  
446  	if (err)
447  		return 4;
448  
449  	return 0;
450  }
451  
dt3k_ai_cmd(struct comedi_device * dev,struct comedi_subdevice * s)452  static int dt3k_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
453  {
454  	struct comedi_cmd *cmd = &s->async->cmd;
455  	int i;
456  	unsigned int chan, range, aref;
457  	unsigned int divider;
458  	unsigned int tscandiv;
459  
460  	for (i = 0; i < cmd->chanlist_len; i++) {
461  		chan = CR_CHAN(cmd->chanlist[i]);
462  		range = CR_RANGE(cmd->chanlist[i]);
463  
464  		writew((range << 6) | chan, dev->mmio + DPR_ADC_BUFFER + i);
465  	}
466  	aref = CR_AREF(cmd->chanlist[0]);
467  
468  	writew(cmd->scan_end_arg, dev->mmio + DPR_PARAMS(0));
469  
470  	if (cmd->convert_src == TRIG_TIMER) {
471  		divider = dt3k_ns_to_timer(50, &cmd->convert_arg, cmd->flags);
472  		writew((divider >> 16), dev->mmio + DPR_PARAMS(1));
473  		writew((divider & 0xffff), dev->mmio + DPR_PARAMS(2));
474  	}
475  
476  	if (cmd->scan_begin_src == TRIG_TIMER) {
477  		tscandiv = dt3k_ns_to_timer(100, &cmd->scan_begin_arg,
478  					    cmd->flags);
479  		writew((tscandiv >> 16), dev->mmio + DPR_PARAMS(3));
480  		writew((tscandiv & 0xffff), dev->mmio + DPR_PARAMS(4));
481  	}
482  
483  	writew(DPR_PARAM5_AD_TRIG_INT_RETRIG, dev->mmio + DPR_PARAMS(5));
484  	writew((aref == AREF_DIFF) ? DPR_PARAM6_AD_DIFF : 0,
485  	       dev->mmio + DPR_PARAMS(6));
486  
487  	writew(DPR_AI_FIFO_DEPTH / 2, dev->mmio + DPR_PARAMS(7));
488  
489  	writew(DPR_SUBSYS_AI, dev->mmio + DPR_SUBSYS);
490  	dt3k_send_cmd(dev, DPR_CMD_CONFIG);
491  
492  	writew(DPR_INTR_ADFULL | DPR_INTR_ADSWERR | DPR_INTR_ADHWERR,
493  	       dev->mmio + DPR_INT_MASK);
494  
495  	debug_n_ints = 0;
496  
497  	writew(DPR_SUBSYS_AI, dev->mmio + DPR_SUBSYS);
498  	dt3k_send_cmd(dev, DPR_CMD_START);
499  
500  	return 0;
501  }
502  
dt3k_ai_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)503  static int dt3k_ai_insn_read(struct comedi_device *dev,
504  			     struct comedi_subdevice *s,
505  			     struct comedi_insn *insn,
506  			     unsigned int *data)
507  {
508  	int i;
509  	unsigned int chan, gain;
510  
511  	chan = CR_CHAN(insn->chanspec);
512  	gain = CR_RANGE(insn->chanspec);
513  	/* XXX docs don't explain how to select aref */
514  
515  	for (i = 0; i < insn->n; i++)
516  		data[i] = dt3k_readsingle(dev, DPR_SUBSYS_AI, chan, gain);
517  
518  	return i;
519  }
520  
dt3k_ao_insn_write(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)521  static int dt3k_ao_insn_write(struct comedi_device *dev,
522  			      struct comedi_subdevice *s,
523  			      struct comedi_insn *insn,
524  			      unsigned int *data)
525  {
526  	unsigned int chan = CR_CHAN(insn->chanspec);
527  	unsigned int val = s->readback[chan];
528  	int i;
529  
530  	for (i = 0; i < insn->n; i++) {
531  		val = data[i];
532  		dt3k_writesingle(dev, DPR_SUBSYS_AO, chan, val);
533  	}
534  	s->readback[chan] = val;
535  
536  	return insn->n;
537  }
538  
dt3k_dio_config(struct comedi_device * dev,int bits)539  static void dt3k_dio_config(struct comedi_device *dev, int bits)
540  {
541  	/* XXX */
542  	writew(DPR_SUBSYS_DOUT, dev->mmio + DPR_SUBSYS);
543  
544  	writew(bits, dev->mmio + DPR_PARAMS(0));
545  
546  	/* XXX write 0 to DPR_PARAMS(1) and DPR_PARAMS(2) ? */
547  
548  	dt3k_send_cmd(dev, DPR_CMD_CONFIG);
549  }
550  
dt3k_dio_insn_config(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)551  static int dt3k_dio_insn_config(struct comedi_device *dev,
552  				struct comedi_subdevice *s,
553  				struct comedi_insn *insn,
554  				unsigned int *data)
555  {
556  	unsigned int chan = CR_CHAN(insn->chanspec);
557  	unsigned int mask;
558  	int ret;
559  
560  	if (chan < 4)
561  		mask = 0x0f;
562  	else
563  		mask = 0xf0;
564  
565  	ret = comedi_dio_insn_config(dev, s, insn, data, mask);
566  	if (ret)
567  		return ret;
568  
569  	dt3k_dio_config(dev, (s->io_bits & 0x01) | ((s->io_bits & 0x10) >> 3));
570  
571  	return insn->n;
572  }
573  
dt3k_dio_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)574  static int dt3k_dio_insn_bits(struct comedi_device *dev,
575  			      struct comedi_subdevice *s,
576  			      struct comedi_insn *insn,
577  			      unsigned int *data)
578  {
579  	if (comedi_dio_update_state(s, data))
580  		dt3k_writesingle(dev, DPR_SUBSYS_DOUT, 0, s->state);
581  
582  	data[1] = dt3k_readsingle(dev, DPR_SUBSYS_DIN, 0, 0);
583  
584  	return insn->n;
585  }
586  
dt3k_mem_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)587  static int dt3k_mem_insn_read(struct comedi_device *dev,
588  			      struct comedi_subdevice *s,
589  			      struct comedi_insn *insn,
590  			      unsigned int *data)
591  {
592  	unsigned int addr = CR_CHAN(insn->chanspec);
593  	int i;
594  
595  	for (i = 0; i < insn->n; i++) {
596  		writew(DPR_SUBSYS_MEM, dev->mmio + DPR_SUBSYS);
597  		writew(addr, dev->mmio + DPR_PARAMS(0));
598  		writew(1, dev->mmio + DPR_PARAMS(1));
599  
600  		dt3k_send_cmd(dev, DPR_CMD_READCODE);
601  
602  		data[i] = readw(dev->mmio + DPR_PARAMS(2));
603  	}
604  
605  	return i;
606  }
607  
dt3000_auto_attach(struct comedi_device * dev,unsigned long context)608  static int dt3000_auto_attach(struct comedi_device *dev,
609  			      unsigned long context)
610  {
611  	struct pci_dev *pcidev = comedi_to_pci_dev(dev);
612  	const struct dt3k_boardtype *board = NULL;
613  	struct dt3k_private *devpriv;
614  	struct comedi_subdevice *s;
615  	int ret = 0;
616  
617  	if (context < ARRAY_SIZE(dt3k_boardtypes))
618  		board = &dt3k_boardtypes[context];
619  	if (!board)
620  		return -ENODEV;
621  	dev->board_ptr = board;
622  	dev->board_name = board->name;
623  
624  	devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
625  	if (!devpriv)
626  		return -ENOMEM;
627  
628  	ret = comedi_pci_enable(dev);
629  	if (ret < 0)
630  		return ret;
631  
632  	dev->mmio = pci_ioremap_bar(pcidev, 0);
633  	if (!dev->mmio)
634  		return -ENOMEM;
635  
636  	if (pcidev->irq) {
637  		ret = request_irq(pcidev->irq, dt3k_interrupt, IRQF_SHARED,
638  				  dev->board_name, dev);
639  		if (ret == 0)
640  			dev->irq = pcidev->irq;
641  	}
642  
643  	ret = comedi_alloc_subdevices(dev, 4);
644  	if (ret)
645  		return ret;
646  
647  	/* Analog Input subdevice */
648  	s = &dev->subdevices[0];
649  	s->type		= COMEDI_SUBD_AI;
650  	s->subdev_flags	= SDF_READABLE | SDF_GROUND | SDF_DIFF;
651  	s->n_chan	= board->adchan;
652  	s->maxdata	= board->ai_is_16bit ? 0xffff : 0x0fff;
653  	s->range_table	= &range_dt3000_ai;	/* XXX */
654  	s->insn_read	= dt3k_ai_insn_read;
655  	if (dev->irq) {
656  		dev->read_subdev = s;
657  		s->subdev_flags	|= SDF_CMD_READ;
658  		s->len_chanlist	= 512;
659  		s->do_cmd	= dt3k_ai_cmd;
660  		s->do_cmdtest	= dt3k_ai_cmdtest;
661  		s->cancel	= dt3k_ai_cancel;
662  	}
663  
664  	/* Analog Output subdevice */
665  	s = &dev->subdevices[1];
666  	if (board->has_ao) {
667  		s->type		= COMEDI_SUBD_AO;
668  		s->subdev_flags	= SDF_WRITABLE;
669  		s->n_chan	= 2;
670  		s->maxdata	= 0x0fff;
671  		s->range_table	= &range_bipolar10;
672  		s->insn_write	= dt3k_ao_insn_write;
673  
674  		ret = comedi_alloc_subdev_readback(s);
675  		if (ret)
676  			return ret;
677  
678  	} else {
679  		s->type		= COMEDI_SUBD_UNUSED;
680  	}
681  
682  	/* Digital I/O subdevice */
683  	s = &dev->subdevices[2];
684  	s->type		= COMEDI_SUBD_DIO;
685  	s->subdev_flags	= SDF_READABLE | SDF_WRITABLE;
686  	s->n_chan	= 8;
687  	s->maxdata	= 1;
688  	s->range_table	= &range_digital;
689  	s->insn_config	= dt3k_dio_insn_config;
690  	s->insn_bits	= dt3k_dio_insn_bits;
691  
692  	/* Memory subdevice */
693  	s = &dev->subdevices[3];
694  	s->type		= COMEDI_SUBD_MEMORY;
695  	s->subdev_flags	= SDF_READABLE;
696  	s->n_chan	= 0x1000;
697  	s->maxdata	= 0xff;
698  	s->range_table	= &range_unknown;
699  	s->insn_read	= dt3k_mem_insn_read;
700  
701  	return 0;
702  }
703  
704  static struct comedi_driver dt3000_driver = {
705  	.driver_name	= "dt3000",
706  	.module		= THIS_MODULE,
707  	.auto_attach	= dt3000_auto_attach,
708  	.detach		= comedi_pci_detach,
709  };
710  
dt3000_pci_probe(struct pci_dev * dev,const struct pci_device_id * id)711  static int dt3000_pci_probe(struct pci_dev *dev,
712  			    const struct pci_device_id *id)
713  {
714  	return comedi_pci_auto_config(dev, &dt3000_driver, id->driver_data);
715  }
716  
717  static const struct pci_device_id dt3000_pci_table[] = {
718  	{ PCI_VDEVICE(DT, 0x0022), BOARD_DT3001 },
719  	{ PCI_VDEVICE(DT, 0x0023), BOARD_DT3002 },
720  	{ PCI_VDEVICE(DT, 0x0024), BOARD_DT3003 },
721  	{ PCI_VDEVICE(DT, 0x0025), BOARD_DT3004 },
722  	{ PCI_VDEVICE(DT, 0x0026), BOARD_DT3005 },
723  	{ PCI_VDEVICE(DT, 0x0027), BOARD_DT3001_PGL },
724  	{ PCI_VDEVICE(DT, 0x0028), BOARD_DT3003_PGL },
725  	{ 0 }
726  };
727  MODULE_DEVICE_TABLE(pci, dt3000_pci_table);
728  
729  static struct pci_driver dt3000_pci_driver = {
730  	.name		= "dt3000",
731  	.id_table	= dt3000_pci_table,
732  	.probe		= dt3000_pci_probe,
733  	.remove		= comedi_pci_auto_unconfig,
734  };
735  module_comedi_pci_driver(dt3000_driver, dt3000_pci_driver);
736  
737  MODULE_AUTHOR("Comedi https://www.comedi.org");
738  MODULE_DESCRIPTION("Comedi driver for Data Translation DT3000 series boards");
739  MODULE_LICENSE("GPL");
740