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