GNU Linux-libre 4.19.286-gnu1
[releases.git] / drivers / staging / comedi / drivers / s526.c
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 "../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                 /* fall through */
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 http://www.comedi.org");
628 MODULE_DESCRIPTION("Comedi low-level driver");
629 MODULE_LICENSE("GPL");