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