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