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