xref: /linux/drivers/comedi/drivers/dt3000.c (revision cbac924200b838cfb8d8b1415113d788089dc50b)
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 
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 
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 
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 
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 
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... */
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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