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