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 
dt3k_send_cmd(struct comedi_device * dev,unsigned int cmd)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 
dt3k_readsingle(struct comedi_device * dev,unsigned int subsys,unsigned int chan,unsigned int gain)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 
dt3k_writesingle(struct comedi_device * dev,unsigned int subsys,unsigned int chan,unsigned int data)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 
dt3k_ai_empty_fifo(struct comedi_device * dev,struct comedi_subdevice * s)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 
dt3k_ai_cancel(struct comedi_device * dev,struct comedi_subdevice * s)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... */
dt3k_interrupt(int irq,void * d)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 
dt3k_ns_to_timer(unsigned int timer_base,unsigned int * nanosec,unsigned int flags)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 
dt3k_ai_cmdtest(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_cmd * cmd)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(&cmd->
443 							    scan_begin_arg,
444 							    arg);
445 		}
446 	}
447 
448 	if (err)
449 		return 4;
450 
451 	return 0;
452 }
453 
dt3k_ai_cmd(struct comedi_device * dev,struct comedi_subdevice * s)454 static int dt3k_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
455 {
456 	struct comedi_cmd *cmd = &s->async->cmd;
457 	int i;
458 	unsigned int chan, range, aref;
459 	unsigned int divider;
460 	unsigned int tscandiv;
461 
462 	for (i = 0; i < cmd->chanlist_len; i++) {
463 		chan = CR_CHAN(cmd->chanlist[i]);
464 		range = CR_RANGE(cmd->chanlist[i]);
465 
466 		writew((range << 6) | chan, dev->mmio + DPR_ADC_BUFFER + i);
467 	}
468 	aref = CR_AREF(cmd->chanlist[0]);
469 
470 	writew(cmd->scan_end_arg, dev->mmio + DPR_PARAMS(0));
471 
472 	if (cmd->convert_src == TRIG_TIMER) {
473 		divider = dt3k_ns_to_timer(50, &cmd->convert_arg, cmd->flags);
474 		writew((divider >> 16), dev->mmio + DPR_PARAMS(1));
475 		writew((divider & 0xffff), dev->mmio + DPR_PARAMS(2));
476 	}
477 
478 	if (cmd->scan_begin_src == TRIG_TIMER) {
479 		tscandiv = dt3k_ns_to_timer(100, &cmd->scan_begin_arg,
480 					    cmd->flags);
481 		writew((tscandiv >> 16), dev->mmio + DPR_PARAMS(3));
482 		writew((tscandiv & 0xffff), dev->mmio + DPR_PARAMS(4));
483 	}
484 
485 	writew(DPR_PARAM5_AD_TRIG_INT_RETRIG, dev->mmio + DPR_PARAMS(5));
486 	writew((aref == AREF_DIFF) ? DPR_PARAM6_AD_DIFF : 0,
487 	       dev->mmio + DPR_PARAMS(6));
488 
489 	writew(DPR_AI_FIFO_DEPTH / 2, dev->mmio + DPR_PARAMS(7));
490 
491 	writew(DPR_SUBSYS_AI, dev->mmio + DPR_SUBSYS);
492 	dt3k_send_cmd(dev, DPR_CMD_CONFIG);
493 
494 	writew(DPR_INTR_ADFULL | DPR_INTR_ADSWERR | DPR_INTR_ADHWERR,
495 	       dev->mmio + DPR_INT_MASK);
496 
497 	debug_n_ints = 0;
498 
499 	writew(DPR_SUBSYS_AI, dev->mmio + DPR_SUBSYS);
500 	dt3k_send_cmd(dev, DPR_CMD_START);
501 
502 	return 0;
503 }
504 
dt3k_ai_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)505 static int dt3k_ai_insn_read(struct comedi_device *dev,
506 			     struct comedi_subdevice *s,
507 			     struct comedi_insn *insn,
508 			     unsigned int *data)
509 {
510 	int i;
511 	unsigned int chan, gain, aref;
512 
513 	chan = CR_CHAN(insn->chanspec);
514 	gain = CR_RANGE(insn->chanspec);
515 	/* XXX docs don't explain how to select aref */
516 	aref = CR_AREF(insn->chanspec);
517 
518 	for (i = 0; i < insn->n; i++)
519 		data[i] = dt3k_readsingle(dev, DPR_SUBSYS_AI, chan, gain);
520 
521 	return i;
522 }
523 
dt3k_ao_insn_write(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)524 static int dt3k_ao_insn_write(struct comedi_device *dev,
525 			      struct comedi_subdevice *s,
526 			      struct comedi_insn *insn,
527 			      unsigned int *data)
528 {
529 	unsigned int chan = CR_CHAN(insn->chanspec);
530 	unsigned int val = s->readback[chan];
531 	int i;
532 
533 	for (i = 0; i < insn->n; i++) {
534 		val = data[i];
535 		dt3k_writesingle(dev, DPR_SUBSYS_AO, chan, val);
536 	}
537 	s->readback[chan] = val;
538 
539 	return insn->n;
540 }
541 
dt3k_dio_config(struct comedi_device * dev,int bits)542 static void dt3k_dio_config(struct comedi_device *dev, int bits)
543 {
544 	/* XXX */
545 	writew(DPR_SUBSYS_DOUT, dev->mmio + DPR_SUBSYS);
546 
547 	writew(bits, dev->mmio + DPR_PARAMS(0));
548 
549 	/* XXX write 0 to DPR_PARAMS(1) and DPR_PARAMS(2) ? */
550 
551 	dt3k_send_cmd(dev, DPR_CMD_CONFIG);
552 }
553 
dt3k_dio_insn_config(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)554 static int dt3k_dio_insn_config(struct comedi_device *dev,
555 				struct comedi_subdevice *s,
556 				struct comedi_insn *insn,
557 				unsigned int *data)
558 {
559 	unsigned int chan = CR_CHAN(insn->chanspec);
560 	unsigned int mask;
561 	int ret;
562 
563 	if (chan < 4)
564 		mask = 0x0f;
565 	else
566 		mask = 0xf0;
567 
568 	ret = comedi_dio_insn_config(dev, s, insn, data, mask);
569 	if (ret)
570 		return ret;
571 
572 	dt3k_dio_config(dev, (s->io_bits & 0x01) | ((s->io_bits & 0x10) >> 3));
573 
574 	return insn->n;
575 }
576 
dt3k_dio_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)577 static int dt3k_dio_insn_bits(struct comedi_device *dev,
578 			      struct comedi_subdevice *s,
579 			      struct comedi_insn *insn,
580 			      unsigned int *data)
581 {
582 	if (comedi_dio_update_state(s, data))
583 		dt3k_writesingle(dev, DPR_SUBSYS_DOUT, 0, s->state);
584 
585 	data[1] = dt3k_readsingle(dev, DPR_SUBSYS_DIN, 0, 0);
586 
587 	return insn->n;
588 }
589 
dt3k_mem_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)590 static int dt3k_mem_insn_read(struct comedi_device *dev,
591 			      struct comedi_subdevice *s,
592 			      struct comedi_insn *insn,
593 			      unsigned int *data)
594 {
595 	unsigned int addr = CR_CHAN(insn->chanspec);
596 	int i;
597 
598 	for (i = 0; i < insn->n; i++) {
599 		writew(DPR_SUBSYS_MEM, dev->mmio + DPR_SUBSYS);
600 		writew(addr, dev->mmio + DPR_PARAMS(0));
601 		writew(1, dev->mmio + DPR_PARAMS(1));
602 
603 		dt3k_send_cmd(dev, DPR_CMD_READCODE);
604 
605 		data[i] = readw(dev->mmio + DPR_PARAMS(2));
606 	}
607 
608 	return i;
609 }
610 
dt3000_auto_attach(struct comedi_device * dev,unsigned long context)611 static int dt3000_auto_attach(struct comedi_device *dev,
612 			      unsigned long context)
613 {
614 	struct pci_dev *pcidev = comedi_to_pci_dev(dev);
615 	const struct dt3k_boardtype *board = NULL;
616 	struct dt3k_private *devpriv;
617 	struct comedi_subdevice *s;
618 	int ret = 0;
619 
620 	if (context < ARRAY_SIZE(dt3k_boardtypes))
621 		board = &dt3k_boardtypes[context];
622 	if (!board)
623 		return -ENODEV;
624 	dev->board_ptr = board;
625 	dev->board_name = board->name;
626 
627 	devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
628 	if (!devpriv)
629 		return -ENOMEM;
630 
631 	ret = comedi_pci_enable(dev);
632 	if (ret < 0)
633 		return ret;
634 
635 	dev->mmio = pci_ioremap_bar(pcidev, 0);
636 	if (!dev->mmio)
637 		return -ENOMEM;
638 
639 	if (pcidev->irq) {
640 		ret = request_irq(pcidev->irq, dt3k_interrupt, IRQF_SHARED,
641 				  dev->board_name, dev);
642 		if (ret == 0)
643 			dev->irq = pcidev->irq;
644 	}
645 
646 	ret = comedi_alloc_subdevices(dev, 4);
647 	if (ret)
648 		return ret;
649 
650 	/* Analog Input subdevice */
651 	s = &dev->subdevices[0];
652 	s->type		= COMEDI_SUBD_AI;
653 	s->subdev_flags	= SDF_READABLE | SDF_GROUND | SDF_DIFF;
654 	s->n_chan	= board->adchan;
655 	s->maxdata	= board->ai_is_16bit ? 0xffff : 0x0fff;
656 	s->range_table	= &range_dt3000_ai;	/* XXX */
657 	s->insn_read	= dt3k_ai_insn_read;
658 	if (dev->irq) {
659 		dev->read_subdev = s;
660 		s->subdev_flags	|= SDF_CMD_READ;
661 		s->len_chanlist	= 512;
662 		s->do_cmd	= dt3k_ai_cmd;
663 		s->do_cmdtest	= dt3k_ai_cmdtest;
664 		s->cancel	= dt3k_ai_cancel;
665 	}
666 
667 	/* Analog Output subdevice */
668 	s = &dev->subdevices[1];
669 	if (board->has_ao) {
670 		s->type		= COMEDI_SUBD_AO;
671 		s->subdev_flags	= SDF_WRITABLE;
672 		s->n_chan	= 2;
673 		s->maxdata	= 0x0fff;
674 		s->range_table	= &range_bipolar10;
675 		s->insn_write	= dt3k_ao_insn_write;
676 
677 		ret = comedi_alloc_subdev_readback(s);
678 		if (ret)
679 			return ret;
680 
681 	} else {
682 		s->type		= COMEDI_SUBD_UNUSED;
683 	}
684 
685 	/* Digital I/O subdevice */
686 	s = &dev->subdevices[2];
687 	s->type		= COMEDI_SUBD_DIO;
688 	s->subdev_flags	= SDF_READABLE | SDF_WRITABLE;
689 	s->n_chan	= 8;
690 	s->maxdata	= 1;
691 	s->range_table	= &range_digital;
692 	s->insn_config	= dt3k_dio_insn_config;
693 	s->insn_bits	= dt3k_dio_insn_bits;
694 
695 	/* Memory subdevice */
696 	s = &dev->subdevices[3];
697 	s->type		= COMEDI_SUBD_MEMORY;
698 	s->subdev_flags	= SDF_READABLE;
699 	s->n_chan	= 0x1000;
700 	s->maxdata	= 0xff;
701 	s->range_table	= &range_unknown;
702 	s->insn_read	= dt3k_mem_insn_read;
703 
704 	return 0;
705 }
706 
707 static struct comedi_driver dt3000_driver = {
708 	.driver_name	= "dt3000",
709 	.module		= THIS_MODULE,
710 	.auto_attach	= dt3000_auto_attach,
711 	.detach		= comedi_pci_detach,
712 };
713 
dt3000_pci_probe(struct pci_dev * dev,const struct pci_device_id * id)714 static int dt3000_pci_probe(struct pci_dev *dev,
715 			    const struct pci_device_id *id)
716 {
717 	return comedi_pci_auto_config(dev, &dt3000_driver, id->driver_data);
718 }
719 
720 static const struct pci_device_id dt3000_pci_table[] = {
721 	{ PCI_VDEVICE(DT, 0x0022), BOARD_DT3001 },
722 	{ PCI_VDEVICE(DT, 0x0023), BOARD_DT3002 },
723 	{ PCI_VDEVICE(DT, 0x0024), BOARD_DT3003 },
724 	{ PCI_VDEVICE(DT, 0x0025), BOARD_DT3004 },
725 	{ PCI_VDEVICE(DT, 0x0026), BOARD_DT3005 },
726 	{ PCI_VDEVICE(DT, 0x0027), BOARD_DT3001_PGL },
727 	{ PCI_VDEVICE(DT, 0x0028), BOARD_DT3003_PGL },
728 	{ 0 }
729 };
730 MODULE_DEVICE_TABLE(pci, dt3000_pci_table);
731 
732 static struct pci_driver dt3000_pci_driver = {
733 	.name		= "dt3000",
734 	.id_table	= dt3000_pci_table,
735 	.probe		= dt3000_pci_probe,
736 	.remove		= comedi_pci_auto_unconfig,
737 };
738 module_comedi_pci_driver(dt3000_driver, dt3000_pci_driver);
739 
740 MODULE_AUTHOR("Comedi http://www.comedi.org");
741 MODULE_DESCRIPTION("Comedi driver for Data Translation DT3000 series boards");
742 MODULE_LICENSE("GPL");
743