3 * Sensoray s526 Comedi driver
5 * COMEDI - Linux Control and Measurement Device Interface
6 * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
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.
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.
21 * Description: Sensoray 526 driver
22 * Devices: [Sensoray] 526 (s526)
24 * Everett Wang <everett.wang@everteq.com>
25 * Updated: Thu, 14 Sep. 2006
26 * Status: experimental
32 * Commands are not supported yet.
34 * Configuration Options:
35 * [0] - I/O port base address
38 #include <linux/module.h>
39 #include "../comedidev.h"
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)
179 struct s526_private {
180 unsigned int gpct_config[4];
181 unsigned short ai_ctrl;
184 static void s526_gpct_write(struct comedi_device *dev,
185 unsigned int chan, unsigned int val)
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));
192 static unsigned int s526_gpct_read(struct comedi_device *dev,
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;
204 static int s526_gpct_rinsn(struct comedi_device *dev,
205 struct comedi_subdevice *s,
206 struct comedi_insn *insn,
209 unsigned int chan = CR_CHAN(insn->chanspec);
212 for (i = 0; i < insn->n; i++)
213 data[i] = s526_gpct_read(dev, chan);
218 static int s526_gpct_insn_config(struct comedi_device *dev,
219 struct comedi_subdevice *s,
220 struct comedi_insn *insn,
223 struct s526_private *devpriv = dev->private;
224 unsigned int chan = CR_CHAN(insn->chanspec);
228 * Check what type of Counter the user requested
229 * data[0] contains the Application type
232 case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
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
239 devpriv->gpct_config[chan] = data[0];
242 /* Set Counter Mode Register */
243 val = data[1] & 0xffff;
244 outw(val, dev->iobase + S526_GPCT_MODE_REG(chan));
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));
253 * Load the counter from PR0
254 * outw(S526_GPCT_CTRL_CT_LOAD,
255 * dev->iobase + S526_GPCT_CTRL_REG(chan));
259 val = S526_GPCT_MODE_CTDIR_CTRL_QUAD;
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;
267 val |= S526_GPCT_MODE_CLK_SRC_QUADX1;
269 /* When to take into account the indexpulse: */
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) {
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;
283 /* Set Counter Mode Register */
284 val = data[1] & 0xffff;
285 outw(val, dev->iobase + S526_GPCT_MODE_REG(chan));
287 /* Load the pre-load register */
288 s526_gpct_write(dev, chan, data[2]);
290 /* Write the Counter Control Register */
292 outw(data[3] & 0xffff,
293 dev->iobase + S526_GPCT_CTRL_REG(chan));
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));
308 case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
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
316 devpriv->gpct_config[chan] = data[0];
318 /* Set Counter Mode Register */
319 val = data[1] & 0xffff;
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));
325 /* Load the pre-load register 0 */
326 s526_gpct_write(dev, chan, data[2]);
328 /* Set Counter Mode Register */
329 val = data[1] & 0xffff;
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));
335 /* Load the pre-load register 1 */
336 s526_gpct_write(dev, chan, data[3]);
338 /* Write the Counter Control Register */
340 val = data[4] & 0xffff;
341 outw(val, dev->iobase + S526_GPCT_CTRL_REG(chan));
345 case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
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
353 devpriv->gpct_config[chan] = data[0];
355 /* Set Counter Mode Register */
356 val = data[1] & 0xffff;
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));
362 /* Load the pre-load register 0 */
363 s526_gpct_write(dev, chan, data[2]);
365 /* Set Counter Mode Register */
366 val = data[1] & 0xffff;
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));
372 /* Load the pre-load register 1 */
373 s526_gpct_write(dev, chan, data[3]);
375 /* Write the Counter Control Register */
377 val = data[4] & 0xffff;
378 outw(val, dev->iobase + S526_GPCT_CTRL_REG(chan));
389 static int s526_gpct_winsn(struct comedi_device *dev,
390 struct comedi_subdevice *s,
391 struct comedi_insn *insn,
394 struct s526_private *devpriv = dev->private;
395 unsigned int chan = CR_CHAN(insn->chanspec);
397 inw(dev->iobase + S526_GPCT_MODE_REG(chan)); /* Is this required? */
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:
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
409 if ((data[1] <= data[0]) || !data[0])
412 /* Fall thru to write the PULSE_WIDTH */
414 case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
415 case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
416 s526_gpct_write(dev, chan, data[0]);
426 static int s526_eoc(struct comedi_device *dev,
427 struct comedi_subdevice *s,
428 struct comedi_insn *insn,
429 unsigned long context)
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);
442 static int s526_ai_insn_read(struct comedi_device *dev,
443 struct comedi_subdevice *s,
444 struct comedi_insn *insn,
447 struct s526_private *devpriv = dev->private;
448 unsigned int chan = CR_CHAN(insn->chanspec);
454 ctrl = S526_AI_CTRL_CONV(chan) | S526_AI_CTRL_READ(chan) |
456 if (ctrl != devpriv->ai_ctrl) {
458 * The multiplexor needs to change, enable the 15us
459 * delay for the first sample.
461 devpriv->ai_ctrl = ctrl;
462 ctrl |= S526_AI_CTRL_DELAY;
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;
470 /* wait for conversion to end */
471 ret = comedi_timeout(dev, s, insn, s526_eoc, S526_INT_AI);
475 val = inw(dev->iobase + S526_AI_REG);
476 data[i] = comedi_offset_munge(s, val);
482 static int s526_ao_insn_write(struct comedi_device *dev,
483 struct comedi_subdevice *s,
484 struct comedi_insn *insn,
487 unsigned int chan = CR_CHAN(insn->chanspec);
488 unsigned int ctrl = S526_AO_CTRL_CHAN(chan);
489 unsigned int val = s->readback[chan];
493 outw(ctrl, dev->iobase + S526_AO_CTRL_REG);
494 ctrl |= S526_AO_CTRL_START;
496 for (i = 0; i < insn->n; i++) {
498 outw(val, dev->iobase + S526_AO_REG);
499 outw(ctrl, dev->iobase + S526_AO_CTRL_REG);
501 /* wait for conversion to end */
502 ret = comedi_timeout(dev, s, insn, s526_eoc, S526_INT_AO);
506 s->readback[chan] = val;
511 static int s526_dio_insn_bits(struct comedi_device *dev,
512 struct comedi_subdevice *s,
513 struct comedi_insn *insn,
516 if (comedi_dio_update_state(s, data))
517 outw(s->state, dev->iobase + S526_DIO_CTRL_REG);
519 data[1] = inw(dev->iobase + S526_DIO_CTRL_REG) & 0xff;
524 static int s526_dio_insn_config(struct comedi_device *dev,
525 struct comedi_subdevice *s,
526 struct comedi_insn *insn,
529 unsigned int chan = CR_CHAN(insn->chanspec);
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).
542 ret = comedi_dio_insn_config(dev, s, insn, data, mask);
546 if (s->io_bits & 0x0f)
547 s->state |= S526_DIO_CTRL_GRP1_OUT;
549 s->state &= ~S526_DIO_CTRL_GRP1_OUT;
550 if (s->io_bits & 0xf0)
551 s->state |= S526_DIO_CTRL_GRP2_OUT;
553 s->state &= ~S526_DIO_CTRL_GRP2_OUT;
555 outw(s->state, dev->iobase + S526_DIO_CTRL_REG);
560 static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it)
562 struct s526_private *devpriv;
563 struct comedi_subdevice *s;
566 ret = comedi_request_region(dev, it->options[0], 0x40);
570 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
574 ret = comedi_alloc_subdevices(dev, 4);
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;
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;
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)
594 s = &dev->subdevices[1];
595 s->type = COMEDI_SUBD_AI;
596 s->subdev_flags = SDF_READABLE | SDF_DIFF;
599 s->range_table = &range_bipolar10;
600 s->len_chanlist = 16;
601 s->insn_read = s526_ai_insn_read;
603 /* Analog Output subdevice */
604 s = &dev->subdevices[2];
605 s->type = COMEDI_SUBD_AO;
606 s->subdev_flags = SDF_WRITABLE;
609 s->range_table = &range_bipolar10;
610 s->insn_write = s526_ao_insn_write;
612 ret = comedi_alloc_subdev_readback(s);
616 /* Digital I/O subdevice */
617 s = &dev->subdevices[3];
618 s->type = COMEDI_SUBD_DIO;
619 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
622 s->range_table = &range_digital;
623 s->insn_bits = s526_dio_insn_bits;
624 s->insn_config = s526_dio_insn_config;
629 static struct comedi_driver s526_driver = {
630 .driver_name = "s526",
631 .module = THIS_MODULE,
632 .attach = s526_attach,
633 .detach = comedi_legacy_detach,
635 module_comedi_driver(s526_driver);
637 MODULE_AUTHOR("Comedi http://www.comedi.org");
638 MODULE_DESCRIPTION("Comedi low-level driver");
639 MODULE_LICENSE("GPL");