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