dt3000.c revision 2696fb57e6af653dd8b4df41b16754579f42fc78
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    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
22*/
23/*
24Driver: dt3000
25Description: Data Translation DT3000 series
26Author: ds
27Devices: [Data Translation] DT3001 (dt3000), DT3001-PGL, DT3002, DT3003,
28  DT3003-PGL, DT3004, DT3005, DT3004-200
29Updated: Mon, 14 Apr 2008 15:41:24 +0100
30Status: works
31
32Configuration Options:
33  [0] - PCI bus of device (optional)
34  [1] - PCI slot of device (optional)
35  If bus/slot is not specified, the first supported
36  PCI device found will be used.
37
38There is code to support AI commands, but it may not work.
39
40AO commands are not supported.
41*/
42
43/*
44   The DT3000 series is Data Translation's attempt to make a PCI
45   data acquisition board.  The design of this series is very nice,
46   since each board has an on-board DSP (Texas Instruments TMS320C52).
47   However, a few details are a little annoying.  The boards lack
48   bus-mastering DMA, which eliminates them from serious work.
49   They also are not capable of autocalibration, which is a common
50   feature in modern hardware.  The default firmware is pretty bad,
51   making it nearly impossible to write an RT compatible driver.
52   It would make an interesting project to write a decent firmware
53   for these boards.
54
55   Data Translation originally wanted an NDA for the documentation
56   for the 3k series.  However, if you ask nicely, they might send
57   you the docs without one, also.
58*/
59
60#define DEBUG 1
61
62#include "../comedidev.h"
63#include <linux/delay.h>
64
65#include "comedi_pci.h"
66
67#define PCI_VENDOR_ID_DT	0x1116
68
69static const struct comedi_lrange range_dt3000_ai = { 4, {
70			RANGE(-10, 10),
71			RANGE(-5, 5),
72			RANGE(-2.5, 2.5),
73			RANGE(-1.25, 1.25)
74	}
75};
76static const struct comedi_lrange range_dt3000_ai_pgl = { 4, {
77			RANGE(-10, 10),
78			RANGE(-1, 1),
79			RANGE(-0.1, 0.1),
80			RANGE(-0.02, 0.02)
81	}
82};
83
84struct dt3k_boardtype {
85
86	const char *name;
87	unsigned int device_id;
88	int adchan;
89	int adbits;
90	int ai_speed;
91	const struct comedi_lrange *adrange;
92	int dachan;
93	int dabits;
94};
95
96
97static const struct dt3k_boardtype dt3k_boardtypes[] = {
98      {name:"dt3001",
99	      device_id:0x22,
100	      adchan:	16,
101	      adbits:	12,
102	      adrange:	&range_dt3000_ai,
103	      ai_speed:3000,
104	      dachan:	2,
105	      dabits:	12,
106		},
107      {name:"dt3001-pgl",
108	      device_id:0x27,
109	      adchan:	16,
110	      adbits:	12,
111	      adrange:	&range_dt3000_ai_pgl,
112	      ai_speed:3000,
113	      dachan:	2,
114	      dabits:	12,
115		},
116      {name:"dt3002",
117	      device_id:0x23,
118	      adchan:	32,
119	      adbits:	12,
120	      adrange:	&range_dt3000_ai,
121	      ai_speed:3000,
122	      dachan:	0,
123	      dabits:	0,
124		},
125      {name:"dt3003",
126	      device_id:0x24,
127	      adchan:	64,
128	      adbits:	12,
129	      adrange:	&range_dt3000_ai,
130	      ai_speed:3000,
131	      dachan:	2,
132	      dabits:	12,
133		},
134      {name:"dt3003-pgl",
135	      device_id:0x28,
136	      adchan:	64,
137	      adbits:	12,
138	      adrange:	&range_dt3000_ai_pgl,
139	      ai_speed:3000,
140	      dachan:	2,
141	      dabits:	12,
142		},
143      {name:"dt3004",
144	      device_id:0x25,
145	      adchan:	16,
146	      adbits:	16,
147	      adrange:	&range_dt3000_ai,
148	      ai_speed:10000,
149	      dachan:	2,
150	      dabits:	12,
151		},
152      {name:"dt3005",		/* a.k.a. 3004-200 */
153	      device_id:0x26,
154	      adchan:	16,
155	      adbits:	16,
156	      adrange:	&range_dt3000_ai,
157	      ai_speed:5000,
158	      dachan:	2,
159	      dabits:	12,
160		},
161};
162
163#define n_dt3k_boards sizeof(dt3k_boardtypes)/sizeof(struct dt3k_boardtype)
164#define this_board ((const struct dt3k_boardtype *)dev->board_ptr)
165
166static DEFINE_PCI_DEVICE_TABLE(dt3k_pci_table) = {
167	{PCI_VENDOR_ID_DT, 0x0022, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
168	{PCI_VENDOR_ID_DT, 0x0027, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
169	{PCI_VENDOR_ID_DT, 0x0023, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
170	{PCI_VENDOR_ID_DT, 0x0024, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
171	{PCI_VENDOR_ID_DT, 0x0028, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
172	{PCI_VENDOR_ID_DT, 0x0025, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
173	{PCI_VENDOR_ID_DT, 0x0026, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
174	{0}
175};
176
177MODULE_DEVICE_TABLE(pci, dt3k_pci_table);
178
179#define DT3000_SIZE		(4*0x1000)
180
181/* dual-ported RAM location definitions */
182
183#define DPR_DAC_buffer		(4*0x000)
184#define DPR_ADC_buffer		(4*0x800)
185#define DPR_Command		(4*0xfd3)
186#define DPR_SubSys		(4*0xfd3)
187#define DPR_Encode		(4*0xfd4)
188#define DPR_Params(a)		(4*(0xfd5+(a)))
189#define DPR_Tick_Reg_Lo		(4*0xff5)
190#define DPR_Tick_Reg_Hi		(4*0xff6)
191#define DPR_DA_Buf_Front	(4*0xff7)
192#define DPR_DA_Buf_Rear		(4*0xff8)
193#define DPR_AD_Buf_Front	(4*0xff9)
194#define DPR_AD_Buf_Rear		(4*0xffa)
195#define DPR_Int_Mask		(4*0xffb)
196#define DPR_Intr_Flag		(4*0xffc)
197#define DPR_Response_Mbx	(4*0xffe)
198#define DPR_Command_Mbx		(4*0xfff)
199
200#define AI_FIFO_DEPTH	2003
201#define AO_FIFO_DEPTH	2048
202
203/* command list */
204
205#define CMD_GETBRDINFO		0
206#define CMD_CONFIG		1
207#define CMD_GETCONFIG		2
208#define CMD_START		3
209#define CMD_STOP		4
210#define CMD_READSINGLE		5
211#define CMD_WRITESINGLE		6
212#define CMD_CALCCLOCK		7
213#define CMD_READEVENTS		8
214#define CMD_WRITECTCTRL		16
215#define CMD_READCTCTRL		17
216#define CMD_WRITECT		18
217#define CMD_READCT		19
218#define CMD_WRITEDATA		32
219#define CMD_READDATA		33
220#define CMD_WRITEIO		34
221#define CMD_READIO		35
222#define CMD_WRITECODE		36
223#define CMD_READCODE		37
224#define CMD_EXECUTE		38
225#define CMD_HALT		48
226
227#define SUBS_AI		0
228#define SUBS_AO		1
229#define SUBS_DIN	2
230#define SUBS_DOUT	3
231#define SUBS_MEM	4
232#define SUBS_CT		5
233
234/* interrupt flags */
235#define DT3000_CMDONE		0x80
236#define DT3000_CTDONE		0x40
237#define DT3000_DAHWERR		0x20
238#define DT3000_DASWERR		0x10
239#define DT3000_DAEMPTY		0x08
240#define DT3000_ADHWERR		0x04
241#define DT3000_ADSWERR		0x02
242#define DT3000_ADFULL		0x01
243
244#define DT3000_COMPLETION_MASK	0xff00
245#define DT3000_COMMAND_MASK	0x00ff
246#define DT3000_NOTPROCESSED	0x0000
247#define DT3000_NOERROR		0x5500
248#define DT3000_ERROR		0xaa00
249#define DT3000_NOTSUPPORTED	0xff00
250
251#define DT3000_EXTERNAL_CLOCK	1
252#define DT3000_RISING_EDGE	2
253
254#define TMODE_MASK		0x1c
255
256#define DT3000_AD_TRIG_INTERNAL		(0<<2)
257#define DT3000_AD_TRIG_EXTERNAL		(1<<2)
258#define DT3000_AD_RETRIG_INTERNAL	(2<<2)
259#define DT3000_AD_RETRIG_EXTERNAL	(3<<2)
260#define DT3000_AD_EXTRETRIG		(4<<2)
261
262#define DT3000_CHANNEL_MODE_SE		0
263#define DT3000_CHANNEL_MODE_DI		1
264
265struct dt3k_private {
266
267	struct pci_dev *pci_dev;
268	resource_size_t phys_addr;
269	void *io_addr;
270	unsigned int lock;
271	unsigned int ao_readback[2];
272	unsigned int ai_front;
273	unsigned int ai_rear;
274};
275
276#define devpriv ((struct dt3k_private *)dev->private)
277
278static int dt3000_attach(struct comedi_device * dev, struct comedi_devconfig * it);
279static int dt3000_detach(struct comedi_device * dev);
280static struct comedi_driver driver_dt3000 = {
281      driver_name:"dt3000",
282      module:THIS_MODULE,
283      attach:dt3000_attach,
284      detach:dt3000_detach,
285};
286
287COMEDI_PCI_INITCLEANUP(driver_dt3000, dt3k_pci_table);
288
289static void dt3k_ai_empty_fifo(struct comedi_device * dev, struct comedi_subdevice * s);
290static int dt3k_ns_to_timer(unsigned int timer_base, unsigned int *arg,
291	unsigned int round_mode);
292static int dt3k_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s);
293#ifdef DEBUG
294static void debug_intr_flags(unsigned int flags);
295#endif
296
297#define TIMEOUT 100
298
299static int dt3k_send_cmd(struct comedi_device * dev, unsigned int cmd)
300{
301	int i;
302	unsigned int status = 0;
303
304	writew(cmd, devpriv->io_addr + DPR_Command_Mbx);
305
306	for (i = 0; i < TIMEOUT; i++) {
307		status = readw(devpriv->io_addr + DPR_Command_Mbx);
308		if ((status & DT3000_COMPLETION_MASK) != DT3000_NOTPROCESSED)
309			break;
310		comedi_udelay(1);
311	}
312	if ((status & DT3000_COMPLETION_MASK) == DT3000_NOERROR) {
313		return 0;
314	}
315
316	printk("dt3k_send_cmd() timeout/error status=0x%04x\n", status);
317
318	return -ETIME;
319}
320
321static unsigned int dt3k_readsingle(struct comedi_device * dev, unsigned int subsys,
322	unsigned int chan, unsigned int gain)
323{
324	writew(subsys, devpriv->io_addr + DPR_SubSys);
325
326	writew(chan, devpriv->io_addr + DPR_Params(0));
327	writew(gain, devpriv->io_addr + DPR_Params(1));
328
329	dt3k_send_cmd(dev, CMD_READSINGLE);
330
331	return readw(devpriv->io_addr + DPR_Params(2));
332}
333
334static void dt3k_writesingle(struct comedi_device * dev, unsigned int subsys,
335	unsigned int chan, unsigned int data)
336{
337	writew(subsys, devpriv->io_addr + DPR_SubSys);
338
339	writew(chan, devpriv->io_addr + DPR_Params(0));
340	writew(0, devpriv->io_addr + DPR_Params(1));
341	writew(data, devpriv->io_addr + DPR_Params(2));
342
343	dt3k_send_cmd(dev, CMD_WRITESINGLE);
344}
345
346static int debug_n_ints = 0;
347
348/* FIXME! Assumes shared interrupt is for this card. */
349/* What's this debug_n_ints stuff? Obviously needs some work... */
350static irqreturn_t dt3k_interrupt(int irq, void *d)
351{
352	struct comedi_device *dev = d;
353	struct comedi_subdevice *s;
354	unsigned int status;
355
356	if (!dev->attached) {
357		return IRQ_NONE;
358	}
359
360	s = dev->subdevices + 0;
361	status = readw(devpriv->io_addr + DPR_Intr_Flag);
362#ifdef DEBUG
363	debug_intr_flags(status);
364#endif
365
366	if (status & DT3000_ADFULL) {
367		dt3k_ai_empty_fifo(dev, s);
368		s->async->events |= COMEDI_CB_BLOCK;
369	}
370
371	if (status & (DT3000_ADSWERR | DT3000_ADHWERR)) {
372		s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
373	}
374
375	debug_n_ints++;
376	if (debug_n_ints >= 10) {
377		dt3k_ai_cancel(dev, s);
378		s->async->events |= COMEDI_CB_EOA;
379	}
380
381	comedi_event(dev, s);
382	return IRQ_HANDLED;
383}
384
385#ifdef DEBUG
386static char *intr_flags[] = {
387	"AdFull", "AdSwError", "AdHwError", "DaEmpty",
388	"DaSwError", "DaHwError", "CtDone", "CmDone",
389};
390static void debug_intr_flags(unsigned int flags)
391{
392	int i;
393	printk("dt3k: intr_flags:");
394	for (i = 0; i < 8; i++) {
395		if (flags & (1 << i)) {
396			printk(" %s", intr_flags[i]);
397		}
398	}
399	printk("\n");
400}
401#endif
402
403static void dt3k_ai_empty_fifo(struct comedi_device * dev, struct comedi_subdevice * s)
404{
405	int front;
406	int rear;
407	int count;
408	int i;
409	short data;
410
411	front = readw(devpriv->io_addr + DPR_AD_Buf_Front);
412	count = front - devpriv->ai_front;
413	if (count < 0)
414		count += AI_FIFO_DEPTH;
415
416	printk("reading %d samples\n", count);
417
418	rear = devpriv->ai_rear;
419
420	for (i = 0; i < count; i++) {
421		data = readw(devpriv->io_addr + DPR_ADC_buffer + rear);
422		comedi_buf_put(s->async, data);
423		rear++;
424		if (rear >= AI_FIFO_DEPTH)
425			rear = 0;
426	}
427
428	devpriv->ai_rear = rear;
429	writew(rear, devpriv->io_addr + DPR_AD_Buf_Rear);
430}
431
432static int dt3k_ai_cmdtest(struct comedi_device * dev, struct comedi_subdevice * s,
433	struct comedi_cmd * cmd)
434{
435	int err = 0;
436	int tmp;
437
438	/* step 1: make sure trigger sources are trivially valid */
439
440	tmp = cmd->start_src;
441	cmd->start_src &= TRIG_NOW;
442	if (!cmd->start_src || tmp != cmd->start_src)
443		err++;
444
445	tmp = cmd->scan_begin_src;
446	cmd->scan_begin_src &= TRIG_TIMER;
447	if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
448		err++;
449
450	tmp = cmd->convert_src;
451	cmd->convert_src &= TRIG_TIMER;
452	if (!cmd->convert_src || tmp != cmd->convert_src)
453		err++;
454
455	tmp = cmd->scan_end_src;
456	cmd->scan_end_src &= TRIG_COUNT;
457	if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
458		err++;
459
460	tmp = cmd->stop_src;
461	cmd->stop_src &= TRIG_COUNT;
462	if (!cmd->stop_src || tmp != cmd->stop_src)
463		err++;
464
465	if (err)
466		return 1;
467
468	/* step 2: make sure trigger sources are unique and mutually compatible */
469
470	if (err)
471		return 2;
472
473	/* step 3: make sure arguments are trivially compatible */
474
475	if (cmd->start_arg != 0) {
476		cmd->start_arg = 0;
477		err++;
478	}
479
480	if (cmd->scan_begin_src == TRIG_TIMER) {
481		if (cmd->scan_begin_arg < this_board->ai_speed) {
482			cmd->scan_begin_arg = this_board->ai_speed;
483			err++;
484		}
485		if (cmd->scan_begin_arg > 100 * 16 * 65535) {
486			cmd->scan_begin_arg = 100 * 16 * 65535;
487			err++;
488		}
489	} else {
490		/* not supported */
491	}
492	if (cmd->convert_src == TRIG_TIMER) {
493		if (cmd->convert_arg < this_board->ai_speed) {
494			cmd->convert_arg = this_board->ai_speed;
495			err++;
496		}
497		if (cmd->convert_arg > 50 * 16 * 65535) {
498			cmd->convert_arg = 50 * 16 * 65535;
499			err++;
500		}
501	} else {
502		/* not supported */
503	}
504
505	if (cmd->scan_end_arg != cmd->chanlist_len) {
506		cmd->scan_end_arg = cmd->chanlist_len;
507		err++;
508	}
509	if (cmd->stop_src == TRIG_COUNT) {
510		if (cmd->stop_arg > 0x00ffffff) {
511			cmd->stop_arg = 0x00ffffff;
512			err++;
513		}
514	} else {
515		/* TRIG_NONE */
516		if (cmd->stop_arg != 0) {
517			cmd->stop_arg = 0;
518			err++;
519		}
520	}
521
522	if (err)
523		return 3;
524
525	/* step 4: fix up any arguments */
526
527	if (cmd->scan_begin_src == TRIG_TIMER) {
528		tmp = cmd->scan_begin_arg;
529		dt3k_ns_to_timer(100, &cmd->scan_begin_arg,
530			cmd->flags & TRIG_ROUND_MASK);
531		if (tmp != cmd->scan_begin_arg)
532			err++;
533	} else {
534		/* not supported */
535	}
536	if (cmd->convert_src == TRIG_TIMER) {
537		tmp = cmd->convert_arg;
538		dt3k_ns_to_timer(50, &cmd->convert_arg,
539			cmd->flags & TRIG_ROUND_MASK);
540		if (tmp != cmd->convert_arg)
541			err++;
542		if (cmd->scan_begin_src == TRIG_TIMER &&
543			cmd->scan_begin_arg <
544			cmd->convert_arg * cmd->scan_end_arg) {
545			cmd->scan_begin_arg =
546				cmd->convert_arg * cmd->scan_end_arg;
547			err++;
548		}
549	} else {
550		/* not supported */
551	}
552
553	if (err)
554		return 4;
555
556	return 0;
557}
558
559static int dt3k_ns_to_timer(unsigned int timer_base, unsigned int *nanosec,
560	unsigned int round_mode)
561{
562	int divider, base, prescale;
563
564	/* This function needs improvment */
565	/* Don't know if divider==0 works. */
566
567	for (prescale = 0; prescale < 16; prescale++) {
568		base = timer_base * (prescale + 1);
569		switch (round_mode) {
570		case TRIG_ROUND_NEAREST:
571		default:
572			divider = (*nanosec + base / 2) / base;
573			break;
574		case TRIG_ROUND_DOWN:
575			divider = (*nanosec) / base;
576			break;
577		case TRIG_ROUND_UP:
578			divider = (*nanosec) / base;
579			break;
580		}
581		if (divider < 65536) {
582			*nanosec = divider * base;
583			return (prescale << 16) | (divider);
584		}
585	}
586
587	prescale = 15;
588	base = timer_base * (1 << prescale);
589	divider = 65535;
590	*nanosec = divider * base;
591	return (prescale << 16) | (divider);
592}
593
594static int dt3k_ai_cmd(struct comedi_device * dev, struct comedi_subdevice * s)
595{
596	struct comedi_cmd *cmd = &s->async->cmd;
597	int i;
598	unsigned int chan, range, aref;
599	unsigned int divider;
600	unsigned int tscandiv;
601	int ret;
602	unsigned int mode;
603
604	printk("dt3k_ai_cmd:\n");
605	for (i = 0; i < cmd->chanlist_len; i++) {
606		chan = CR_CHAN(cmd->chanlist[i]);
607		range = CR_RANGE(cmd->chanlist[i]);
608
609		writew((range << 6) | chan,
610			devpriv->io_addr + DPR_ADC_buffer + i);
611	}
612	aref = CR_AREF(cmd->chanlist[0]);
613
614	writew(cmd->scan_end_arg, devpriv->io_addr + DPR_Params(0));
615	printk("param[0]=0x%04x\n", cmd->scan_end_arg);
616
617	if (cmd->convert_src == TRIG_TIMER) {
618		divider = dt3k_ns_to_timer(50, &cmd->convert_arg,
619			cmd->flags & TRIG_ROUND_MASK);
620		writew((divider >> 16), devpriv->io_addr + DPR_Params(1));
621		printk("param[1]=0x%04x\n", divider >> 16);
622		writew((divider & 0xffff), devpriv->io_addr + DPR_Params(2));
623		printk("param[2]=0x%04x\n", divider & 0xffff);
624	} else {
625		/* not supported */
626	}
627
628	if (cmd->scan_begin_src == TRIG_TIMER) {
629		tscandiv = dt3k_ns_to_timer(100, &cmd->scan_begin_arg,
630			cmd->flags & TRIG_ROUND_MASK);
631		writew((tscandiv >> 16), devpriv->io_addr + DPR_Params(3));
632		printk("param[3]=0x%04x\n", tscandiv >> 16);
633		writew((tscandiv & 0xffff), devpriv->io_addr + DPR_Params(4));
634		printk("param[4]=0x%04x\n", tscandiv & 0xffff);
635	} else {
636		/* not supported */
637	}
638
639	mode = DT3000_AD_RETRIG_INTERNAL | 0 | 0;
640	writew(mode, devpriv->io_addr + DPR_Params(5));
641	printk("param[5]=0x%04x\n", mode);
642	writew(aref == AREF_DIFF, devpriv->io_addr + DPR_Params(6));
643	printk("param[6]=0x%04x\n", aref == AREF_DIFF);
644
645	writew(AI_FIFO_DEPTH / 2, devpriv->io_addr + DPR_Params(7));
646	printk("param[7]=0x%04x\n", AI_FIFO_DEPTH / 2);
647
648	writew(SUBS_AI, devpriv->io_addr + DPR_SubSys);
649	ret = dt3k_send_cmd(dev, CMD_CONFIG);
650
651	writew(DT3000_ADFULL | DT3000_ADSWERR | DT3000_ADHWERR,
652		devpriv->io_addr + DPR_Int_Mask);
653
654	debug_n_ints = 0;
655
656	writew(SUBS_AI, devpriv->io_addr + DPR_SubSys);
657	ret = dt3k_send_cmd(dev, CMD_START);
658
659	return 0;
660}
661
662static int dt3k_ai_cancel(struct comedi_device * dev, struct comedi_subdevice * s)
663{
664	int ret;
665
666	writew(SUBS_AI, devpriv->io_addr + DPR_SubSys);
667	ret = dt3k_send_cmd(dev, CMD_STOP);
668
669	writew(0, devpriv->io_addr + DPR_Int_Mask);
670
671	return 0;
672}
673
674static int dt3k_ai_insn(struct comedi_device * dev, struct comedi_subdevice * s,
675	struct comedi_insn * insn, unsigned int * data)
676{
677	int i;
678	unsigned int chan, gain, aref;
679
680	chan = CR_CHAN(insn->chanspec);
681	gain = CR_RANGE(insn->chanspec);
682	/* XXX docs don't explain how to select aref */
683	aref = CR_AREF(insn->chanspec);
684
685	for (i = 0; i < insn->n; i++) {
686		data[i] = dt3k_readsingle(dev, SUBS_AI, chan, gain);
687	}
688
689	return i;
690}
691
692static int dt3k_ao_insn(struct comedi_device * dev, struct comedi_subdevice * s,
693	struct comedi_insn * insn, unsigned int * data)
694{
695	int i;
696	unsigned int chan;
697
698	chan = CR_CHAN(insn->chanspec);
699	for (i = 0; i < insn->n; i++) {
700		dt3k_writesingle(dev, SUBS_AO, chan, data[i]);
701		devpriv->ao_readback[chan] = data[i];
702	}
703
704	return i;
705}
706
707static int dt3k_ao_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
708	struct comedi_insn * insn, unsigned int * data)
709{
710	int i;
711	unsigned int chan;
712
713	chan = CR_CHAN(insn->chanspec);
714	for (i = 0; i < insn->n; i++) {
715		data[i] = devpriv->ao_readback[chan];
716	}
717
718	return i;
719}
720
721static void dt3k_dio_config(struct comedi_device * dev, int bits)
722{
723	/* XXX */
724	writew(SUBS_DOUT, devpriv->io_addr + DPR_SubSys);
725
726	writew(bits, devpriv->io_addr + DPR_Params(0));
727#if 0
728	/* don't know */
729	writew(0, devpriv->io_addr + DPR_Params(1));
730	writew(0, devpriv->io_addr + DPR_Params(2));
731#endif
732
733	dt3k_send_cmd(dev, CMD_CONFIG);
734}
735
736static int dt3k_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
737	struct comedi_insn * insn, unsigned int * data)
738{
739	int mask;
740
741	mask = (CR_CHAN(insn->chanspec) < 4) ? 0x0f : 0xf0;
742
743	switch (data[0]) {
744	case INSN_CONFIG_DIO_OUTPUT:
745		s->io_bits |= mask;
746		break;
747	case INSN_CONFIG_DIO_INPUT:
748		s->io_bits &= ~mask;
749		break;
750	case INSN_CONFIG_DIO_QUERY:
751		data[1] =
752			(s->io_bits & (1 << CR_CHAN(insn->
753					chanspec))) ? COMEDI_OUTPUT :
754			COMEDI_INPUT;
755		return insn->n;
756		break;
757	default:
758		return -EINVAL;
759		break;
760	}
761	mask = (s->io_bits & 0x01) | ((s->io_bits & 0x10) >> 3);
762	dt3k_dio_config(dev, mask);
763
764	return insn->n;
765}
766
767static int dt3k_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
768	struct comedi_insn * insn, unsigned int * data)
769{
770	if (insn->n != 2)
771		return -EINVAL;
772
773	if (data[0]) {
774		s->state &= ~data[0];
775		s->state |= data[1] & data[0];
776		dt3k_writesingle(dev, SUBS_DOUT, 0, s->state);
777	}
778	data[1] = dt3k_readsingle(dev, SUBS_DIN, 0, 0);
779
780	return 2;
781}
782
783static int dt3k_mem_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
784	struct comedi_insn * insn, unsigned int * data)
785{
786	unsigned int addr = CR_CHAN(insn->chanspec);
787	int i;
788
789	for (i = 0; i < insn->n; i++) {
790		writew(SUBS_MEM, devpriv->io_addr + DPR_SubSys);
791		writew(addr, devpriv->io_addr + DPR_Params(0));
792		writew(1, devpriv->io_addr + DPR_Params(1));
793
794		dt3k_send_cmd(dev, CMD_READCODE);
795
796		data[i] = readw(devpriv->io_addr + DPR_Params(2));
797	}
798
799	return i;
800}
801
802static int dt_pci_probe(struct comedi_device * dev, int bus, int slot);
803
804static int dt3000_attach(struct comedi_device * dev, struct comedi_devconfig * it)
805{
806	struct comedi_subdevice *s;
807	int bus, slot;
808	int ret = 0;
809
810	printk("dt3000:");
811	bus = it->options[0];
812	slot = it->options[1];
813
814	if ((ret = alloc_private(dev, sizeof(struct dt3k_private))) < 0)
815		return ret;
816
817	ret = dt_pci_probe(dev, bus, slot);
818	if (ret < 0)
819		return ret;
820	if (ret == 0) {
821		printk(" no DT board found\n");
822		return -ENODEV;
823	}
824
825	dev->board_name = this_board->name;
826
827	if (comedi_request_irq(devpriv->pci_dev->irq, dt3k_interrupt,
828			IRQF_SHARED, "dt3000", dev)) {
829		printk(" unable to allocate IRQ %u\n", devpriv->pci_dev->irq);
830		return -EINVAL;
831	}
832	dev->irq = devpriv->pci_dev->irq;
833
834	if ((ret = alloc_subdevices(dev, 4)) < 0)
835		return ret;
836
837	s = dev->subdevices;
838	dev->read_subdev = s;
839
840	/* ai subdevice */
841	s->type = COMEDI_SUBD_AI;
842	s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF | SDF_CMD_READ;
843	s->n_chan = this_board->adchan;
844	s->insn_read = dt3k_ai_insn;
845	s->maxdata = (1 << this_board->adbits) - 1;
846	s->len_chanlist = 512;
847	s->range_table = &range_dt3000_ai;	/* XXX */
848	s->do_cmd = dt3k_ai_cmd;
849	s->do_cmdtest = dt3k_ai_cmdtest;
850	s->cancel = dt3k_ai_cancel;
851
852	s++;
853	/* ao subsystem */
854	s->type = COMEDI_SUBD_AO;
855	s->subdev_flags = SDF_WRITABLE;
856	s->n_chan = 2;
857	s->insn_read = dt3k_ao_insn_read;
858	s->insn_write = dt3k_ao_insn;
859	s->maxdata = (1 << this_board->dabits) - 1;
860	s->len_chanlist = 1;
861	s->range_table = &range_bipolar10;
862
863	s++;
864	/* dio subsystem */
865	s->type = COMEDI_SUBD_DIO;
866	s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
867	s->n_chan = 8;
868	s->insn_config = dt3k_dio_insn_config;
869	s->insn_bits = dt3k_dio_insn_bits;
870	s->maxdata = 1;
871	s->len_chanlist = 8;
872	s->range_table = &range_digital;
873
874	s++;
875	/* mem subsystem */
876	s->type = COMEDI_SUBD_MEMORY;
877	s->subdev_flags = SDF_READABLE;
878	s->n_chan = 0x1000;
879	s->insn_read = dt3k_mem_insn_read;
880	s->maxdata = 0xff;
881	s->len_chanlist = 1;
882	s->range_table = &range_unknown;
883
884#if 0
885	s++;
886	/* proc subsystem */
887	s->type = COMEDI_SUBD_PROC;
888#endif
889
890	return 0;
891}
892
893static int dt3000_detach(struct comedi_device * dev)
894{
895	if (dev->irq)
896		comedi_free_irq(dev->irq, dev);
897
898	if (devpriv) {
899		if (devpriv->pci_dev) {
900			if (devpriv->phys_addr) {
901				comedi_pci_disable(devpriv->pci_dev);
902			}
903			pci_dev_put(devpriv->pci_dev);
904		}
905		if (devpriv->io_addr)
906			iounmap(devpriv->io_addr);
907	}
908	/* XXX */
909
910	return 0;
911}
912
913static struct pci_dev *dt_pci_find_device(struct pci_dev *from, int *board);
914static int setup_pci(struct comedi_device * dev);
915
916static int dt_pci_probe(struct comedi_device * dev, int bus, int slot)
917{
918	int board;
919	int ret;
920	struct pci_dev *pcidev;
921
922	pcidev = NULL;
923	while ((pcidev = dt_pci_find_device(pcidev, &board)) != NULL) {
924		if ((bus == 0 && slot == 0) ||
925			(pcidev->bus->number == bus &&
926			 PCI_SLOT(pcidev->devfn) == slot)) {
927			break;
928		}
929	}
930	devpriv->pci_dev = pcidev;
931
932	if (board >= 0)
933		dev->board_ptr = dt3k_boardtypes + board;
934
935	if (!devpriv->pci_dev)
936		return 0;
937
938	if ((ret = setup_pci(dev)) < 0)
939		return ret;
940
941	return 1;
942}
943
944static int setup_pci(struct comedi_device * dev)
945{
946	resource_size_t addr;
947	int ret;
948
949	ret = comedi_pci_enable(devpriv->pci_dev, "dt3000");
950	if (ret < 0)
951		return ret;
952
953	addr = pci_resource_start(devpriv->pci_dev, 0);
954	devpriv->phys_addr = addr;
955	devpriv->io_addr = ioremap(devpriv->phys_addr, DT3000_SIZE);
956	if (!devpriv->io_addr)
957		return -ENOMEM;
958#if DEBUG
959	printk("0x%08llx mapped to %p, ",
960		(unsigned long long)devpriv->phys_addr, devpriv->io_addr);
961#endif
962
963	return 0;
964}
965
966static struct pci_dev *dt_pci_find_device(struct pci_dev *from, int *board)
967{
968	int i;
969
970	for (from = pci_get_device(PCI_VENDOR_ID_DT, PCI_ANY_ID, from);
971		from != NULL;
972		from = pci_get_device(PCI_VENDOR_ID_DT, PCI_ANY_ID, from)) {
973		for (i = 0; i < n_dt3k_boards; i++) {
974			if (from->device == dt3k_boardtypes[i].device_id) {
975				*board = i;
976				return from;
977			}
978		}
979		printk("unknown Data Translation PCI device found with device_id=0x%04x\n", from->device);
980	}
981	*board = -1;
982	return from;
983}
984