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