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