xref: /linux/drivers/comedi/drivers/s526.c (revision 2da68a77)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * s526.c
4  * Sensoray s526 Comedi driver
5  *
6  * COMEDI - Linux Control and Measurement Device Interface
7  * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
8  */
9 
10 /*
11  * Driver: s526
12  * Description: Sensoray 526 driver
13  * Devices: [Sensoray] 526 (s526)
14  * Author: Richie
15  *	   Everett Wang <everett.wang@everteq.com>
16  * Updated: Thu, 14 Sep. 2006
17  * Status: experimental
18  *
19  * Encoder works
20  * Analog input works
21  * Analog output works
22  * PWM output works
23  * Commands are not supported yet.
24  *
25  * Configuration Options:
26  *   [0] - I/O port base address
27  */
28 
29 #include <linux/module.h>
30 #include <linux/comedi/comedidev.h>
31 
32 /*
33  * Register I/O map
34  */
35 #define S526_TIMER_REG		0x00
36 #define S526_TIMER_LOAD(x)	(((x) & 0xff) << 8)
37 #define S526_TIMER_MODE		((x) << 1)
38 #define S526_TIMER_MANUAL	S526_TIMER_MODE(0)
39 #define S526_TIMER_AUTO		S526_TIMER_MODE(1)
40 #define S526_TIMER_RESTART	BIT(0)
41 #define S526_WDOG_REG		0x02
42 #define S526_WDOG_INVERTED	BIT(4)
43 #define S526_WDOG_ENA		BIT(3)
44 #define S526_WDOG_INTERVAL(x)	(((x) & 0x7) << 0)
45 #define S526_AO_CTRL_REG	0x04
46 #define S526_AO_CTRL_RESET	BIT(3)
47 #define S526_AO_CTRL_CHAN(x)	(((x) & 0x3) << 1)
48 #define S526_AO_CTRL_START	BIT(0)
49 #define S526_AI_CTRL_REG	0x06
50 #define S526_AI_CTRL_DELAY	BIT(15)
51 #define S526_AI_CTRL_CONV(x)	(1 << (5 + ((x) & 0x9)))
52 #define S526_AI_CTRL_READ(x)	(((x) & 0xf) << 1)
53 #define S526_AI_CTRL_START	BIT(0)
54 #define S526_AO_REG		0x08
55 #define S526_AI_REG		0x08
56 #define S526_DIO_CTRL_REG	0x0a
57 #define S526_DIO_CTRL_DIO3_NEG	BIT(15)	/* irq on DIO3 neg/pos edge */
58 #define S526_DIO_CTRL_DIO2_NEG	BIT(14)	/* irq on DIO2 neg/pos edge */
59 #define S526_DIO_CTRL_DIO1_NEG	BIT(13)	/* irq on DIO1 neg/pos edge */
60 #define S526_DIO_CTRL_DIO0_NEG	BIT(12)	/* irq on DIO0 neg/pos edge */
61 #define S526_DIO_CTRL_GRP2_OUT	BIT(11)
62 #define S526_DIO_CTRL_GRP1_OUT	BIT(10)
63 #define S526_DIO_CTRL_GRP2_NEG	BIT(8)	/* irq on DIO[4-7] neg/pos edge */
64 #define S526_INT_ENA_REG	0x0c
65 #define S526_INT_STATUS_REG	0x0e
66 #define S526_INT_DIO(x)		BIT(8 + ((x) & 0x7))
67 #define S526_INT_EEPROM		BIT(7)	/* status only */
68 #define S526_INT_CNTR(x)	BIT(3 + (3 - ((x) & 0x3)))
69 #define S526_INT_AI		BIT(2)
70 #define S526_INT_AO		BIT(1)
71 #define S526_INT_TIMER		BIT(0)
72 #define S526_MISC_REG		0x10
73 #define S526_MISC_LED_OFF	BIT(0)
74 #define S526_GPCT_LSB_REG(x)	(0x12 + ((x) * 8))
75 #define S526_GPCT_MSB_REG(x)	(0x14 + ((x) * 8))
76 #define S526_GPCT_MODE_REG(x)	(0x16 + ((x) * 8))
77 #define S526_GPCT_MODE_COUT_SRC(x)	((x) << 0)
78 #define S526_GPCT_MODE_COUT_SRC_MASK	S526_GPCT_MODE_COUT_SRC(0x1)
79 #define S526_GPCT_MODE_COUT_SRC_RCAP	S526_GPCT_MODE_COUT_SRC(0)
80 #define S526_GPCT_MODE_COUT_SRC_RTGL	S526_GPCT_MODE_COUT_SRC(1)
81 #define S526_GPCT_MODE_COUT_POL(x)	((x) << 1)
82 #define S526_GPCT_MODE_COUT_POL_MASK	S526_GPCT_MODE_COUT_POL(0x1)
83 #define S526_GPCT_MODE_COUT_POL_NORM	S526_GPCT_MODE_COUT_POL(0)
84 #define S526_GPCT_MODE_COUT_POL_INV	S526_GPCT_MODE_COUT_POL(1)
85 #define S526_GPCT_MODE_AUTOLOAD(x)	((x) << 2)
86 #define S526_GPCT_MODE_AUTOLOAD_MASK	S526_GPCT_MODE_AUTOLOAD(0x7)
87 #define S526_GPCT_MODE_AUTOLOAD_NONE	S526_GPCT_MODE_AUTOLOAD(0)
88 /* these 3 bits can be OR'ed */
89 #define S526_GPCT_MODE_AUTOLOAD_RO	S526_GPCT_MODE_AUTOLOAD(0x1)
90 #define S526_GPCT_MODE_AUTOLOAD_IXFALL	S526_GPCT_MODE_AUTOLOAD(0x2)
91 #define S526_GPCT_MODE_AUTOLOAD_IXRISE	S526_GPCT_MODE_AUTOLOAD(0x4)
92 #define S526_GPCT_MODE_HWCTEN_SRC(x)	((x) << 5)
93 #define S526_GPCT_MODE_HWCTEN_SRC_MASK	S526_GPCT_MODE_HWCTEN_SRC(0x3)
94 #define S526_GPCT_MODE_HWCTEN_SRC_CEN	S526_GPCT_MODE_HWCTEN_SRC(0)
95 #define S526_GPCT_MODE_HWCTEN_SRC_IX	S526_GPCT_MODE_HWCTEN_SRC(1)
96 #define S526_GPCT_MODE_HWCTEN_SRC_IXRF	S526_GPCT_MODE_HWCTEN_SRC(2)
97 #define S526_GPCT_MODE_HWCTEN_SRC_NRCAP	S526_GPCT_MODE_HWCTEN_SRC(3)
98 #define S526_GPCT_MODE_CTEN_CTRL(x)	((x) << 7)
99 #define S526_GPCT_MODE_CTEN_CTRL_MASK	S526_GPCT_MODE_CTEN_CTRL(0x3)
100 #define S526_GPCT_MODE_CTEN_CTRL_DIS	S526_GPCT_MODE_CTEN_CTRL(0)
101 #define S526_GPCT_MODE_CTEN_CTRL_ENA	S526_GPCT_MODE_CTEN_CTRL(1)
102 #define S526_GPCT_MODE_CTEN_CTRL_HW	S526_GPCT_MODE_CTEN_CTRL(2)
103 #define S526_GPCT_MODE_CTEN_CTRL_INVHW	S526_GPCT_MODE_CTEN_CTRL(3)
104 #define S526_GPCT_MODE_CLK_SRC(x)	((x) << 9)
105 #define S526_GPCT_MODE_CLK_SRC_MASK	S526_GPCT_MODE_CLK_SRC(0x3)
106 /* if count direction control set to quadrature */
107 #define S526_GPCT_MODE_CLK_SRC_QUADX1	S526_GPCT_MODE_CLK_SRC(0)
108 #define S526_GPCT_MODE_CLK_SRC_QUADX2	S526_GPCT_MODE_CLK_SRC(1)
109 #define S526_GPCT_MODE_CLK_SRC_QUADX4	S526_GPCT_MODE_CLK_SRC(2)
110 #define S526_GPCT_MODE_CLK_SRC_QUADX4_	S526_GPCT_MODE_CLK_SRC(3)
111 /* if count direction control set to software control */
112 #define S526_GPCT_MODE_CLK_SRC_ARISE	S526_GPCT_MODE_CLK_SRC(0)
113 #define S526_GPCT_MODE_CLK_SRC_AFALL	S526_GPCT_MODE_CLK_SRC(1)
114 #define S526_GPCT_MODE_CLK_SRC_INT	S526_GPCT_MODE_CLK_SRC(2)
115 #define S526_GPCT_MODE_CLK_SRC_INTHALF	S526_GPCT_MODE_CLK_SRC(3)
116 #define S526_GPCT_MODE_CT_DIR(x)	((x) << 11)
117 #define S526_GPCT_MODE_CT_DIR_MASK	S526_GPCT_MODE_CT_DIR(0x1)
118 /* if count direction control set to software control */
119 #define S526_GPCT_MODE_CT_DIR_UP	S526_GPCT_MODE_CT_DIR(0)
120 #define S526_GPCT_MODE_CT_DIR_DOWN	S526_GPCT_MODE_CT_DIR(1)
121 #define S526_GPCT_MODE_CTDIR_CTRL(x)	((x) << 12)
122 #define S526_GPCT_MODE_CTDIR_CTRL_MASK	S526_GPCT_MODE_CTDIR_CTRL(0x1)
123 #define S526_GPCT_MODE_CTDIR_CTRL_QUAD	S526_GPCT_MODE_CTDIR_CTRL(0)
124 #define S526_GPCT_MODE_CTDIR_CTRL_SOFT	S526_GPCT_MODE_CTDIR_CTRL(1)
125 #define S526_GPCT_MODE_LATCH_CTRL(x)	((x) << 13)
126 #define S526_GPCT_MODE_LATCH_CTRL_MASK	S526_GPCT_MODE_LATCH_CTRL(0x1)
127 #define S526_GPCT_MODE_LATCH_CTRL_READ	S526_GPCT_MODE_LATCH_CTRL(0)
128 #define S526_GPCT_MODE_LATCH_CTRL_EVENT	S526_GPCT_MODE_LATCH_CTRL(1)
129 #define S526_GPCT_MODE_PR_SELECT(x)	((x) << 14)
130 #define S526_GPCT_MODE_PR_SELECT_MASK	S526_GPCT_MODE_PR_SELECT(0x1)
131 #define S526_GPCT_MODE_PR_SELECT_PR0	S526_GPCT_MODE_PR_SELECT(0)
132 #define S526_GPCT_MODE_PR_SELECT_PR1	S526_GPCT_MODE_PR_SELECT(1)
133 /* Control/Status - R = readable, W = writeable, C = write 1 to clear */
134 #define S526_GPCT_CTRL_REG(x)	(0x18 + ((x) * 8))
135 #define S526_GPCT_CTRL_EV_STATUS(x)	((x) << 0)		/* RC */
136 #define S526_GPCT_CTRL_EV_STATUS_MASK	S526_GPCT_EV_STATUS(0xf)
137 #define S526_GPCT_CTRL_EV_STATUS_NONE	S526_GPCT_EV_STATUS(0)
138 /* these 4 bits can be OR'ed */
139 #define S526_GPCT_CTRL_EV_STATUS_ECAP	S526_GPCT_EV_STATUS(0x1)
140 #define S526_GPCT_CTRL_EV_STATUS_ICAPN	S526_GPCT_EV_STATUS(0x2)
141 #define S526_GPCT_CTRL_EV_STATUS_ICAPP	S526_GPCT_EV_STATUS(0x4)
142 #define S526_GPCT_CTRL_EV_STATUS_RCAP	S526_GPCT_EV_STATUS(0x8)
143 #define S526_GPCT_CTRL_COUT_STATUS	BIT(4)			/* R */
144 #define S526_GPCT_CTRL_INDEX_STATUS	BIT(5)			/* R */
145 #define S525_GPCT_CTRL_INTEN(x)		((x) << 6)		/* W */
146 #define S525_GPCT_CTRL_INTEN_MASK	S526_GPCT_CTRL_INTEN(0xf)
147 #define S525_GPCT_CTRL_INTEN_NONE	S526_GPCT_CTRL_INTEN(0)
148 /* these 4 bits can be OR'ed */
149 #define S525_GPCT_CTRL_INTEN_ERROR	S526_GPCT_CTRL_INTEN(0x1)
150 #define S525_GPCT_CTRL_INTEN_IXFALL	S526_GPCT_CTRL_INTEN(0x2)
151 #define S525_GPCT_CTRL_INTEN_IXRISE	S526_GPCT_CTRL_INTEN(0x4)
152 #define S525_GPCT_CTRL_INTEN_RO		S526_GPCT_CTRL_INTEN(0x8)
153 #define S525_GPCT_CTRL_LATCH_SEL(x)	((x) << 10)		/* W */
154 #define S525_GPCT_CTRL_LATCH_SEL_MASK	S526_GPCT_CTRL_LATCH_SEL(0x7)
155 #define S525_GPCT_CTRL_LATCH_SEL_NONE	S526_GPCT_CTRL_LATCH_SEL(0)
156 /* these 3 bits can be OR'ed */
157 #define S525_GPCT_CTRL_LATCH_SEL_IXFALL	S526_GPCT_CTRL_LATCH_SEL(0x1)
158 #define S525_GPCT_CTRL_LATCH_SEL_IXRISE	S526_GPCT_CTRL_LATCH_SEL(0x2)
159 #define S525_GPCT_CTRL_LATCH_SEL_ITIMER	S526_GPCT_CTRL_LATCH_SEL(0x4)
160 #define S525_GPCT_CTRL_CT_ARM		BIT(13)			/* W */
161 #define S525_GPCT_CTRL_CT_LOAD		BIT(14)			/* W */
162 #define S526_GPCT_CTRL_CT_RESET		BIT(15)			/* W */
163 #define S526_EEPROM_DATA_REG	0x32
164 #define S526_EEPROM_CTRL_REG	0x34
165 #define S526_EEPROM_CTRL_ADDR(x) (((x) & 0x3f) << 3)
166 #define S526_EEPROM_CTRL(x)	(((x) & 0x3) << 1)
167 #define S526_EEPROM_CTRL_READ	S526_EEPROM_CTRL(2)
168 #define S526_EEPROM_CTRL_START	BIT(0)
169 
170 struct s526_private {
171 	unsigned int gpct_config[4];
172 	unsigned short ai_ctrl;
173 };
174 
175 static void s526_gpct_write(struct comedi_device *dev,
176 			    unsigned int chan, unsigned int val)
177 {
178 	/* write high word then low word */
179 	outw((val >> 16) & 0xffff, dev->iobase + S526_GPCT_MSB_REG(chan));
180 	outw(val & 0xffff, dev->iobase + S526_GPCT_LSB_REG(chan));
181 }
182 
183 static unsigned int s526_gpct_read(struct comedi_device *dev,
184 				   unsigned int chan)
185 {
186 	unsigned int val;
187 
188 	/* read the low word then high word */
189 	val = inw(dev->iobase + S526_GPCT_LSB_REG(chan)) & 0xffff;
190 	val |= (inw(dev->iobase + S526_GPCT_MSB_REG(chan)) & 0xff) << 16;
191 
192 	return val;
193 }
194 
195 static int s526_gpct_rinsn(struct comedi_device *dev,
196 			   struct comedi_subdevice *s,
197 			   struct comedi_insn *insn,
198 			   unsigned int *data)
199 {
200 	unsigned int chan = CR_CHAN(insn->chanspec);
201 	int i;
202 
203 	for (i = 0; i < insn->n; i++)
204 		data[i] = s526_gpct_read(dev, chan);
205 
206 	return insn->n;
207 }
208 
209 static int s526_gpct_insn_config(struct comedi_device *dev,
210 				 struct comedi_subdevice *s,
211 				 struct comedi_insn *insn,
212 				 unsigned int *data)
213 {
214 	struct s526_private *devpriv = dev->private;
215 	unsigned int chan = CR_CHAN(insn->chanspec);
216 	unsigned int val;
217 
218 	/*
219 	 * Check what type of Counter the user requested
220 	 * data[0] contains the Application type
221 	 */
222 	switch (data[0]) {
223 	case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
224 		/*
225 		 * data[0]: Application Type
226 		 * data[1]: Counter Mode Register Value
227 		 * data[2]: Pre-load Register Value
228 		 * data[3]: Conter Control Register
229 		 */
230 		devpriv->gpct_config[chan] = data[0];
231 
232 #if 1
233 		/*  Set Counter Mode Register */
234 		val = data[1] & 0xffff;
235 		outw(val, dev->iobase + S526_GPCT_MODE_REG(chan));
236 
237 		/*  Reset the counter if it is software preload */
238 		if ((val & S526_GPCT_MODE_AUTOLOAD_MASK) ==
239 		    S526_GPCT_MODE_AUTOLOAD_NONE) {
240 			/*  Reset the counter */
241 			outw(S526_GPCT_CTRL_CT_RESET,
242 			     dev->iobase + S526_GPCT_CTRL_REG(chan));
243 			/*
244 			 * Load the counter from PR0
245 			 * outw(S526_GPCT_CTRL_CT_LOAD,
246 			 *      dev->iobase + S526_GPCT_CTRL_REG(chan));
247 			 */
248 		}
249 #else
250 		val = S526_GPCT_MODE_CTDIR_CTRL_QUAD;
251 
252 		/*  data[1] contains GPCT_X1, GPCT_X2 or GPCT_X4 */
253 		if (data[1] == GPCT_X2)
254 			val |= S526_GPCT_MODE_CLK_SRC_QUADX2;
255 		else if (data[1] == GPCT_X4)
256 			val |= S526_GPCT_MODE_CLK_SRC_QUADX4;
257 		else
258 			val |= S526_GPCT_MODE_CLK_SRC_QUADX1;
259 
260 		/*  When to take into account the indexpulse: */
261 		/*
262 		 * if (data[2] == GPCT_IndexPhaseLowLow) {
263 		 * } else if (data[2] == GPCT_IndexPhaseLowHigh) {
264 		 * } else if (data[2] == GPCT_IndexPhaseHighLow) {
265 		 * } else if (data[2] == GPCT_IndexPhaseHighHigh) {
266 		 * }
267 		 */
268 		/*  Take into account the index pulse? */
269 		if (data[3] == GPCT_RESET_COUNTER_ON_INDEX) {
270 			/*  Auto load with INDEX^ */
271 			val |= S526_GPCT_MODE_AUTOLOAD_IXRISE;
272 		}
273 
274 		/*  Set Counter Mode Register */
275 		val = data[1] & 0xffff;
276 		outw(val, dev->iobase + S526_GPCT_MODE_REG(chan));
277 
278 		/*  Load the pre-load register */
279 		s526_gpct_write(dev, chan, data[2]);
280 
281 		/*  Write the Counter Control Register */
282 		if (data[3])
283 			outw(data[3] & 0xffff,
284 			     dev->iobase + S526_GPCT_CTRL_REG(chan));
285 
286 		/*  Reset the counter if it is software preload */
287 		if ((val & S526_GPCT_MODE_AUTOLOAD_MASK) ==
288 		    S526_GPCT_MODE_AUTOLOAD_NONE) {
289 			/*  Reset the counter */
290 			outw(S526_GPCT_CTRL_CT_RESET,
291 			     dev->iobase + S526_GPCT_CTRL_REG(chan));
292 			/*  Load the counter from PR0 */
293 			outw(S526_GPCT_CTRL_CT_LOAD,
294 			     dev->iobase + S526_GPCT_CTRL_REG(chan));
295 		}
296 #endif
297 		break;
298 
299 	case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
300 		/*
301 		 * data[0]: Application Type
302 		 * data[1]: Counter Mode Register Value
303 		 * data[2]: Pre-load Register 0 Value
304 		 * data[3]: Pre-load Register 1 Value
305 		 * data[4]: Conter Control Register
306 		 */
307 		devpriv->gpct_config[chan] = data[0];
308 
309 		/*  Set Counter Mode Register */
310 		val = data[1] & 0xffff;
311 		/* Select PR0 */
312 		val &= ~S526_GPCT_MODE_PR_SELECT_MASK;
313 		val |= S526_GPCT_MODE_PR_SELECT_PR0;
314 		outw(val, dev->iobase + S526_GPCT_MODE_REG(chan));
315 
316 		/* Load the pre-load register 0 */
317 		s526_gpct_write(dev, chan, data[2]);
318 
319 		/*  Set Counter Mode Register */
320 		val = data[1] & 0xffff;
321 		/* Select PR1 */
322 		val &= ~S526_GPCT_MODE_PR_SELECT_MASK;
323 		val |= S526_GPCT_MODE_PR_SELECT_PR1;
324 		outw(val, dev->iobase + S526_GPCT_MODE_REG(chan));
325 
326 		/* Load the pre-load register 1 */
327 		s526_gpct_write(dev, chan, data[3]);
328 
329 		/*  Write the Counter Control Register */
330 		if (data[4]) {
331 			val = data[4] & 0xffff;
332 			outw(val, dev->iobase + S526_GPCT_CTRL_REG(chan));
333 		}
334 		break;
335 
336 	case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
337 		/*
338 		 * data[0]: Application Type
339 		 * data[1]: Counter Mode Register Value
340 		 * data[2]: Pre-load Register 0 Value
341 		 * data[3]: Pre-load Register 1 Value
342 		 * data[4]: Conter Control Register
343 		 */
344 		devpriv->gpct_config[chan] = data[0];
345 
346 		/*  Set Counter Mode Register */
347 		val = data[1] & 0xffff;
348 		/* Select PR0 */
349 		val &= ~S526_GPCT_MODE_PR_SELECT_MASK;
350 		val |= S526_GPCT_MODE_PR_SELECT_PR0;
351 		outw(val, dev->iobase + S526_GPCT_MODE_REG(chan));
352 
353 		/* Load the pre-load register 0 */
354 		s526_gpct_write(dev, chan, data[2]);
355 
356 		/*  Set Counter Mode Register */
357 		val = data[1] & 0xffff;
358 		/* Select PR1 */
359 		val &= ~S526_GPCT_MODE_PR_SELECT_MASK;
360 		val |= S526_GPCT_MODE_PR_SELECT_PR1;
361 		outw(val, dev->iobase + S526_GPCT_MODE_REG(chan));
362 
363 		/* Load the pre-load register 1 */
364 		s526_gpct_write(dev, chan, data[3]);
365 
366 		/*  Write the Counter Control Register */
367 		if (data[4]) {
368 			val = data[4] & 0xffff;
369 			outw(val, dev->iobase + S526_GPCT_CTRL_REG(chan));
370 		}
371 		break;
372 
373 	default:
374 		return -EINVAL;
375 	}
376 
377 	return insn->n;
378 }
379 
380 static int s526_gpct_winsn(struct comedi_device *dev,
381 			   struct comedi_subdevice *s,
382 			   struct comedi_insn *insn,
383 			   unsigned int *data)
384 {
385 	struct s526_private *devpriv = dev->private;
386 	unsigned int chan = CR_CHAN(insn->chanspec);
387 
388 	inw(dev->iobase + S526_GPCT_MODE_REG(chan));	/* Is this required? */
389 
390 	/*  Check what Application of Counter this channel is configured for */
391 	switch (devpriv->gpct_config[chan]) {
392 	case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
393 		/*
394 		 * data[0] contains the PULSE_WIDTH
395 		 * data[1] contains the PULSE_PERIOD
396 		 * @pre PULSE_PERIOD > PULSE_WIDTH > 0
397 		 * The above periods must be expressed as a multiple of the
398 		 * pulse frequency on the selected source
399 		 */
400 		if ((data[1] <= data[0]) || !data[0])
401 			return -EINVAL;
402 		/* to write the PULSE_WIDTH */
403 		fallthrough;
404 	case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
405 	case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
406 		s526_gpct_write(dev, chan, data[0]);
407 		break;
408 
409 	default:
410 		return -EINVAL;
411 	}
412 
413 	return insn->n;
414 }
415 
416 static int s526_eoc(struct comedi_device *dev,
417 		    struct comedi_subdevice *s,
418 		    struct comedi_insn *insn,
419 		    unsigned long context)
420 {
421 	unsigned int status;
422 
423 	status = inw(dev->iobase + S526_INT_STATUS_REG);
424 	if (status & context) {
425 		/* we got our eoc event, clear it */
426 		outw(context, dev->iobase + S526_INT_STATUS_REG);
427 		return 0;
428 	}
429 	return -EBUSY;
430 }
431 
432 static int s526_ai_insn_read(struct comedi_device *dev,
433 			     struct comedi_subdevice *s,
434 			     struct comedi_insn *insn,
435 			     unsigned int *data)
436 {
437 	struct s526_private *devpriv = dev->private;
438 	unsigned int chan = CR_CHAN(insn->chanspec);
439 	unsigned int ctrl;
440 	unsigned int val;
441 	int ret;
442 	int i;
443 
444 	ctrl = S526_AI_CTRL_CONV(chan) | S526_AI_CTRL_READ(chan) |
445 	       S526_AI_CTRL_START;
446 	if (ctrl != devpriv->ai_ctrl) {
447 		/*
448 		 * The multiplexor needs to change, enable the 15us
449 		 * delay for the first sample.
450 		 */
451 		devpriv->ai_ctrl = ctrl;
452 		ctrl |= S526_AI_CTRL_DELAY;
453 	}
454 
455 	for (i = 0; i < insn->n; i++) {
456 		/* trigger conversion */
457 		outw(ctrl, dev->iobase + S526_AI_CTRL_REG);
458 		ctrl &= ~S526_AI_CTRL_DELAY;
459 
460 		/* wait for conversion to end */
461 		ret = comedi_timeout(dev, s, insn, s526_eoc, S526_INT_AI);
462 		if (ret)
463 			return ret;
464 
465 		val = inw(dev->iobase + S526_AI_REG);
466 		data[i] = comedi_offset_munge(s, val);
467 	}
468 
469 	return insn->n;
470 }
471 
472 static int s526_ao_insn_write(struct comedi_device *dev,
473 			      struct comedi_subdevice *s,
474 			      struct comedi_insn *insn,
475 			      unsigned int *data)
476 {
477 	unsigned int chan = CR_CHAN(insn->chanspec);
478 	unsigned int ctrl = S526_AO_CTRL_CHAN(chan);
479 	unsigned int val = s->readback[chan];
480 	int ret;
481 	int i;
482 
483 	outw(ctrl, dev->iobase + S526_AO_CTRL_REG);
484 	ctrl |= S526_AO_CTRL_START;
485 
486 	for (i = 0; i < insn->n; i++) {
487 		val = data[i];
488 		outw(val, dev->iobase + S526_AO_REG);
489 		outw(ctrl, dev->iobase + S526_AO_CTRL_REG);
490 
491 		/* wait for conversion to end */
492 		ret = comedi_timeout(dev, s, insn, s526_eoc, S526_INT_AO);
493 		if (ret)
494 			return ret;
495 	}
496 	s->readback[chan] = val;
497 
498 	return insn->n;
499 }
500 
501 static int s526_dio_insn_bits(struct comedi_device *dev,
502 			      struct comedi_subdevice *s,
503 			      struct comedi_insn *insn,
504 			      unsigned int *data)
505 {
506 	if (comedi_dio_update_state(s, data))
507 		outw(s->state, dev->iobase + S526_DIO_CTRL_REG);
508 
509 	data[1] = inw(dev->iobase + S526_DIO_CTRL_REG) & 0xff;
510 
511 	return insn->n;
512 }
513 
514 static int s526_dio_insn_config(struct comedi_device *dev,
515 				struct comedi_subdevice *s,
516 				struct comedi_insn *insn,
517 				unsigned int *data)
518 {
519 	unsigned int chan = CR_CHAN(insn->chanspec);
520 	unsigned int mask;
521 	int ret;
522 
523 	/*
524 	 * Digital I/O can be configured as inputs or outputs in
525 	 * groups of 4; DIO group 1 (DIO0-3) and DIO group 2 (DIO4-7).
526 	 */
527 	if (chan < 4)
528 		mask = 0x0f;
529 	else
530 		mask = 0xf0;
531 
532 	ret = comedi_dio_insn_config(dev, s, insn, data, mask);
533 	if (ret)
534 		return ret;
535 
536 	if (s->io_bits & 0x0f)
537 		s->state |= S526_DIO_CTRL_GRP1_OUT;
538 	else
539 		s->state &= ~S526_DIO_CTRL_GRP1_OUT;
540 	if (s->io_bits & 0xf0)
541 		s->state |= S526_DIO_CTRL_GRP2_OUT;
542 	else
543 		s->state &= ~S526_DIO_CTRL_GRP2_OUT;
544 
545 	outw(s->state, dev->iobase + S526_DIO_CTRL_REG);
546 
547 	return insn->n;
548 }
549 
550 static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it)
551 {
552 	struct s526_private *devpriv;
553 	struct comedi_subdevice *s;
554 	int ret;
555 
556 	ret = comedi_request_region(dev, it->options[0], 0x40);
557 	if (ret)
558 		return ret;
559 
560 	devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
561 	if (!devpriv)
562 		return -ENOMEM;
563 
564 	ret = comedi_alloc_subdevices(dev, 4);
565 	if (ret)
566 		return ret;
567 
568 	/* General-Purpose Counter/Timer (GPCT) */
569 	s = &dev->subdevices[0];
570 	s->type		= COMEDI_SUBD_COUNTER;
571 	s->subdev_flags	= SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL;
572 	s->n_chan	= 4;
573 	s->maxdata	= 0x00ffffff;
574 	s->insn_read	= s526_gpct_rinsn;
575 	s->insn_config	= s526_gpct_insn_config;
576 	s->insn_write	= s526_gpct_winsn;
577 
578 	/*
579 	 * Analog Input subdevice
580 	 * channels 0 to 7 are the regular differential inputs
581 	 * channel 8 is "reference 0" (+10V)
582 	 * channel 9 is "reference 1" (0V)
583 	 */
584 	s = &dev->subdevices[1];
585 	s->type		= COMEDI_SUBD_AI;
586 	s->subdev_flags	= SDF_READABLE | SDF_DIFF;
587 	s->n_chan	= 10;
588 	s->maxdata	= 0xffff;
589 	s->range_table	= &range_bipolar10;
590 	s->len_chanlist	= 16;
591 	s->insn_read	= s526_ai_insn_read;
592 
593 	/* Analog Output subdevice */
594 	s = &dev->subdevices[2];
595 	s->type		= COMEDI_SUBD_AO;
596 	s->subdev_flags	= SDF_WRITABLE;
597 	s->n_chan	= 4;
598 	s->maxdata	= 0xffff;
599 	s->range_table	= &range_bipolar10;
600 	s->insn_write	= s526_ao_insn_write;
601 
602 	ret = comedi_alloc_subdev_readback(s);
603 	if (ret)
604 		return ret;
605 
606 	/* Digital I/O subdevice */
607 	s = &dev->subdevices[3];
608 	s->type		= COMEDI_SUBD_DIO;
609 	s->subdev_flags	= SDF_READABLE | SDF_WRITABLE;
610 	s->n_chan	= 8;
611 	s->maxdata	= 1;
612 	s->range_table	= &range_digital;
613 	s->insn_bits	= s526_dio_insn_bits;
614 	s->insn_config	= s526_dio_insn_config;
615 
616 	return 0;
617 }
618 
619 static struct comedi_driver s526_driver = {
620 	.driver_name	= "s526",
621 	.module		= THIS_MODULE,
622 	.attach		= s526_attach,
623 	.detach		= comedi_legacy_detach,
624 };
625 module_comedi_driver(s526_driver);
626 
627 MODULE_AUTHOR("Comedi https://www.comedi.org");
628 MODULE_DESCRIPTION("Comedi low-level driver");
629 MODULE_LICENSE("GPL");
630