staging: comedi: comedi_test: use unsigned int for waveform timing
[linux-2.6-block.git] / drivers / staging / comedi / drivers / s526.c
CommitLineData
0c988d00 1/*
de332b11
HS
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 */
0c988d00 18
0c988d00 19/*
de332b11
HS
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 */
0c988d00 37
ce157f80 38#include <linux/module.h>
0c988d00 39#include "../comedidev.h"
2b0318a6 40#include <asm/byteorder.h>
0c988d00 41
537dd665
HS
42/*
43 * Register I/O map
44 */
4e04fd32
HS
45#define S526_TIMER_REG 0x00
46#define S526_TIMER_LOAD(x) (((x) & 0xff) << 8)
47#define S526_TIMER_MODE ((x) << 1)
48#define S526_TIMER_MANUAL S526_TIMER_MODE(0)
49#define S526_TIMER_AUTO S526_TIMER_MODE(1)
50#define S526_TIMER_RESTART BIT(0)
088c1ce0
HS
51#define S526_WDOG_REG 0x02
52#define S526_WDOG_INVERTED BIT(4)
53#define S526_WDOG_ENA BIT(3)
54#define S526_WDOG_INTERVAL(x) (((x) & 0x7) << 0)
abbb6489
HS
55#define S526_AO_CTRL_REG 0x04
56#define S526_AO_CTRL_RESET BIT(3)
57#define S526_AO_CTRL_CHAN(x) (((x) & 0x3) << 1)
58#define S526_AO_CTRL_START BIT(0)
fe79b3d0
HS
59#define S526_AI_CTRL_REG 0x06
60#define S526_AI_CTRL_DELAY BIT(15)
61#define S526_AI_CTRL_CONV(x) (1 << (5 + ((x) & 0x9)))
62#define S526_AI_CTRL_READ(x) (((x) & 0xf) << 1)
63#define S526_AI_CTRL_START BIT(0)
15bccf2e
HS
64#define S526_AO_REG 0x08
65#define S526_AI_REG 0x08
658441b4
HS
66#define S526_DIO_CTRL_REG 0x0a
67#define S526_DIO_CTRL_DIO3_NEG BIT(15) /* irq on DIO3 neg/pos edge */
68#define S526_DIO_CTRL_DIO2_NEG BIT(14) /* irq on DIO2 neg/pos edge */
69#define S526_DIO_CTRL_DIO1_NEG BIT(13) /* irq on DIO1 neg/pos edge */
70#define S526_DIO_CTRL_DIO0_NEG BIT(12) /* irq on DIO0 neg/pos edge */
71#define S526_DIO_CTRL_GRP2_OUT BIT(11)
72#define S526_DIO_CTRL_GRP1_OUT BIT(10)
73#define S526_DIO_CTRL_GRP2_NEG BIT(8) /* irq on DIO[4-7] neg/pos edge */
8a5d6d2e
HS
74#define S526_INT_ENA_REG 0x0c
75#define S526_INT_STATUS_REG 0x0e
76#define S526_INT_DIO(x) BIT(8 + ((x) & 0x7))
77#define S526_INT_EEPROM BIT(7) /* status only */
78#define S526_INT_CNTR(x) BIT(3 + (3 - ((x) & 0x3)))
79#define S526_INT_AI BIT(2)
80#define S526_INT_AO BIT(1)
81#define S526_INT_TIMER BIT(0)
64fe38f4
HS
82#define S526_MISC_REG 0x10
83#define S526_MISC_LED_OFF BIT(0)
1d0d1c00
HS
84#define S526_GPCT_LSB_REG(x) (0x12 + ((x) * 8))
85#define S526_GPCT_MSB_REG(x) (0x14 + ((x) * 8))
86#define S526_GPCT_MODE_REG(x) (0x16 + ((x) * 8))
87#define S526_GPCT_CTRL_REG(x) (0x18 + ((x) * 8))
2c6b5824
HS
88#define S526_EEPROM_DATA_REG 0x32
89#define S526_EEPROM_CTRL_REG 0x34
90#define S526_EEPROM_CTRL_ADDR(x) (((x) & 0x3f) << 3)
91#define S526_EEPROM_CTRL(x) (((x) & 0x3) << 1)
92#define S526_EEPROM_CTRL_READ S526_EEPROM_CTRL(2)
93#define S526_EEPROM_CTRL_START BIT(0)
0c988d00 94
4b1d53f0 95struct counter_mode_register_t {
c9c62f4e 96#if defined(__LITTLE_ENDIAN_BITFIELD)
0c988d00
EW
97 unsigned short coutSource:1;
98 unsigned short coutPolarity:1;
99 unsigned short autoLoadResetRcap:3;
100 unsigned short hwCtEnableSource:2;
101 unsigned short ctEnableCtrl:2;
102 unsigned short clockSource:2;
103 unsigned short countDir:1;
104 unsigned short countDirCtrl:1;
105 unsigned short outputRegLatchCtrl:1;
106 unsigned short preloadRegSel:1;
107 unsigned short reserved:1;
2b0318a6
IA
108 #elif defined(__BIG_ENDIAN_BITFIELD)
109 unsigned short reserved:1;
110 unsigned short preloadRegSel:1;
111 unsigned short outputRegLatchCtrl:1;
112 unsigned short countDirCtrl:1;
113 unsigned short countDir:1;
114 unsigned short clockSource:2;
115 unsigned short ctEnableCtrl:2;
116 unsigned short hwCtEnableSource:2;
117 unsigned short autoLoadResetRcap:3;
118 unsigned short coutPolarity:1;
119 unsigned short coutSource:1;
120#else
121#error Unknown bit field order
122#endif
4b1d53f0 123};
0c988d00 124
ca98ee7b 125union cmReg {
4b1d53f0 126 struct counter_mode_register_t reg;
0c988d00 127 unsigned short value;
ca98ee7b 128};
0c988d00 129
6dc1ece0 130struct s526_private {
675f98f1 131 unsigned int gpct_config[4];
21424e3f 132 unsigned short ai_ctrl;
6dc1ece0
BP
133};
134
1d0d1c00
HS
135static void s526_gpct_write(struct comedi_device *dev,
136 unsigned int chan, unsigned int val)
137{
138 /* write high word then low word */
139 outw((val >> 16) & 0xffff, dev->iobase + S526_GPCT_MSB_REG(chan));
140 outw(val & 0xffff, dev->iobase + S526_GPCT_LSB_REG(chan));
141}
142
143static unsigned int s526_gpct_read(struct comedi_device *dev,
144 unsigned int chan)
145{
146 unsigned int val;
147
148 /* read the low word then high word */
149 val = inw(dev->iobase + S526_GPCT_LSB_REG(chan)) & 0xffff;
150 val |= (inw(dev->iobase + S526_GPCT_MSB_REG(chan)) & 0xff) << 16;
151
152 return val;
153}
154
0a85b6f0 155static int s526_gpct_rinsn(struct comedi_device *dev,
2a29edf6
HS
156 struct comedi_subdevice *s,
157 struct comedi_insn *insn,
0a85b6f0 158 unsigned int *data)
0c988d00 159{
43a35276 160 unsigned int chan = CR_CHAN(insn->chanspec);
43a35276 161 int i;
0c988d00 162
1d0d1c00
HS
163 for (i = 0; i < insn->n; i++)
164 data[i] = s526_gpct_read(dev, chan);
2a29edf6
HS
165
166 return insn->n;
0c988d00
EW
167}
168
0a85b6f0
MT
169static int s526_gpct_insn_config(struct comedi_device *dev,
170 struct comedi_subdevice *s,
5a5614cb
HS
171 struct comedi_insn *insn,
172 unsigned int *data)
0c988d00 173{
5f221062 174 struct s526_private *devpriv = dev->private;
43a35276 175 unsigned int chan = CR_CHAN(insn->chanspec);
5a5614cb 176 unsigned int val;
ca98ee7b 177 union cmReg cmReg;
0c988d00 178
a399d81d
HS
179 /*
180 * Check what type of Counter the user requested
181 * data[0] contains the Application type
182 */
b2abd982 183 switch (data[0]) {
0c988d00
EW
184 case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
185 /*
a399d81d
HS
186 * data[0]: Application Type
187 * data[1]: Counter Mode Register Value
188 * data[2]: Pre-load Register Value
189 * data[3]: Conter Control Register
0c988d00 190 */
675f98f1 191 devpriv->gpct_config[chan] = data[0];
0c988d00 192
0c988d00 193#if 1
232f6502 194 /* Set Counter Mode Register */
5a5614cb 195 cmReg.value = data[1] & 0xffff;
1d0d1c00 196 outw(cmReg.value, dev->iobase + S526_GPCT_MODE_REG(chan));
0c988d00 197
232f6502 198 /* Reset the counter if it is software preload */
0c988d00 199 if (cmReg.reg.autoLoadResetRcap == 0) {
c9c62f4e 200 /* Reset the counter */
1d0d1c00 201 outw(0x8000, dev->iobase + S526_GPCT_CTRL_REG(chan));
c9c62f4e 202 /* Load the counter from PR0
1d0d1c00 203 * outw(0x4000, dev->iobase + S526_GPCT_CTRL_REG(chan));
c9c62f4e 204 */
0c988d00
EW
205 }
206#else
c9c62f4e
XF
207 /* 0 quadrature, 1 software control */
208 cmReg.reg.countDirCtrl = 0;
0c988d00 209
232f6502 210 /* data[1] contains GPCT_X1, GPCT_X2 or GPCT_X4 */
b2abd982 211 if (data[1] == GPCT_X2)
0c988d00 212 cmReg.reg.clockSource = 1;
b2abd982 213 else if (data[1] == GPCT_X4)
0c988d00 214 cmReg.reg.clockSource = 2;
c9c62f4e 215 else
0c988d00 216 cmReg.reg.clockSource = 0;
0c988d00 217
232f6502 218 /* When to take into account the indexpulse: */
a399d81d
HS
219 /*
220 * if (data[2] == GPCT_IndexPhaseLowLow) {
221 * } else if (data[2] == GPCT_IndexPhaseLowHigh) {
222 * } else if (data[2] == GPCT_IndexPhaseHighLow) {
223 * } else if (data[2] == GPCT_IndexPhaseHighHigh) {
224 * }
225 */
232f6502 226 /* Take into account the index pulse? */
b2abd982 227 if (data[3] == GPCT_RESET_COUNTER_ON_INDEX)
c9c62f4e
XF
228 /* Auto load with INDEX^ */
229 cmReg.reg.autoLoadResetRcap = 4;
0c988d00 230
232f6502 231 /* Set Counter Mode Register */
5a5614cb 232 cmReg.value = data[1] & 0xffff;
1d0d1c00 233 outw(cmReg.value, dev->iobase + S526_GPCT_MODE_REG(chan));
0c988d00 234
1d0d1c00
HS
235 /* Load the pre-load register */
236 s526_gpct_write(dev, chan, data[2]);
0c988d00 237
232f6502 238 /* Write the Counter Control Register */
1d0d1c00
HS
239 if (data[3])
240 outw(data[3] & 0xffff,
241 dev->iobase + S526_GPCT_CTRL_REG(chan));
242
232f6502 243 /* Reset the counter if it is software preload */
0c988d00 244 if (cmReg.reg.autoLoadResetRcap == 0) {
c9c62f4e 245 /* Reset the counter */
1d0d1c00 246 outw(0x8000, dev->iobase + S526_GPCT_CTRL_REG(chan));
c9c62f4e 247 /* Load the counter from PR0 */
1d0d1c00 248 outw(0x4000, dev->iobase + S526_GPCT_CTRL_REG(chan));
0c988d00
EW
249 }
250#endif
251 break;
252
253 case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
254 /*
a399d81d
HS
255 * data[0]: Application Type
256 * data[1]: Counter Mode Register Value
257 * data[2]: Pre-load Register 0 Value
258 * data[3]: Pre-load Register 1 Value
259 * data[4]: Conter Control Register
0c988d00 260 */
675f98f1 261 devpriv->gpct_config[chan] = data[0];
0c988d00 262
232f6502 263 /* Set Counter Mode Register */
5a5614cb 264 cmReg.value = data[1] & 0xffff;
232f6502 265 cmReg.reg.preloadRegSel = 0; /* PR0 */
1d0d1c00 266 outw(cmReg.value, dev->iobase + S526_GPCT_MODE_REG(chan));
0c988d00 267
1d0d1c00
HS
268 /* Load the pre-load register 0 */
269 s526_gpct_write(dev, chan, data[2]);
0c988d00 270
232f6502 271 /* Set Counter Mode Register */
5a5614cb 272 cmReg.value = data[1] & 0xffff;
232f6502 273 cmReg.reg.preloadRegSel = 1; /* PR1 */
1d0d1c00 274 outw(cmReg.value, dev->iobase + S526_GPCT_MODE_REG(chan));
0c988d00 275
1d0d1c00
HS
276 /* Load the pre-load register 1 */
277 s526_gpct_write(dev, chan, data[3]);
0c988d00 278
232f6502 279 /* Write the Counter Control Register */
5a5614cb
HS
280 if (data[4]) {
281 val = data[4] & 0xffff;
1d0d1c00 282 outw(val, dev->iobase + S526_GPCT_CTRL_REG(chan));
0c988d00
EW
283 }
284 break;
285
286 case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
287 /*
a399d81d
HS
288 * data[0]: Application Type
289 * data[1]: Counter Mode Register Value
290 * data[2]: Pre-load Register 0 Value
291 * data[3]: Pre-load Register 1 Value
292 * data[4]: Conter Control Register
0c988d00 293 */
675f98f1 294 devpriv->gpct_config[chan] = data[0];
0c988d00 295
232f6502 296 /* Set Counter Mode Register */
5a5614cb 297 cmReg.value = data[1] & 0xffff;
232f6502 298 cmReg.reg.preloadRegSel = 0; /* PR0 */
1d0d1c00 299 outw(cmReg.value, dev->iobase + S526_GPCT_MODE_REG(chan));
0c988d00 300
1d0d1c00
HS
301 /* Load the pre-load register 0 */
302 s526_gpct_write(dev, chan, data[2]);
0c988d00 303
232f6502 304 /* Set Counter Mode Register */
5a5614cb 305 cmReg.value = data[1] & 0xffff;
232f6502 306 cmReg.reg.preloadRegSel = 1; /* PR1 */
1d0d1c00 307 outw(cmReg.value, dev->iobase + S526_GPCT_MODE_REG(chan));
0c988d00 308
1d0d1c00
HS
309 /* Load the pre-load register 1 */
310 s526_gpct_write(dev, chan, data[3]);
0c988d00 311
232f6502 312 /* Write the Counter Control Register */
5a5614cb
HS
313 if (data[4]) {
314 val = data[4] & 0xffff;
1d0d1c00 315 outw(val, dev->iobase + S526_GPCT_CTRL_REG(chan));
0c988d00
EW
316 }
317 break;
318
319 default:
0c988d00 320 return -EINVAL;
0c988d00
EW
321 }
322
323 return insn->n;
324}
325
0a85b6f0 326static int s526_gpct_winsn(struct comedi_device *dev,
5c813bb1
HS
327 struct comedi_subdevice *s,
328 struct comedi_insn *insn,
0a85b6f0 329 unsigned int *data)
0c988d00 330{
5f221062 331 struct s526_private *devpriv = dev->private;
43a35276 332 unsigned int chan = CR_CHAN(insn->chanspec);
5c813bb1 333
1d0d1c00 334 inw(dev->iobase + S526_GPCT_MODE_REG(chan)); /* Is this required? */
0c988d00 335
232f6502 336 /* Check what Application of Counter this channel is configured for */
675f98f1
HS
337 switch (devpriv->gpct_config[chan]) {
338 case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
a399d81d
HS
339 /*
340 * data[0] contains the PULSE_WIDTH
341 * data[1] contains the PULSE_PERIOD
342 * @pre PULSE_PERIOD > PULSE_WIDTH > 0
343 * The above periods must be expressed as a multiple of the
344 * pulse frequency on the selected source
0c988d00 345 */
67f2021f 346 if ((data[1] <= data[0]) || !data[0])
0c988d00 347 return -EINVAL;
0c988d00 348
5c813bb1
HS
349 /* Fall thru to write the PULSE_WIDTH */
350
675f98f1
HS
351 case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
352 case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
1d0d1c00 353 s526_gpct_write(dev, chan, data[0]);
0c988d00 354 break;
5c813bb1
HS
355
356 default:
0c988d00 357 return -EINVAL;
0c988d00 358 }
5c813bb1 359
0c988d00
EW
360 return insn->n;
361}
362
09c5d6c8
HS
363static int s526_eoc(struct comedi_device *dev,
364 struct comedi_subdevice *s,
365 struct comedi_insn *insn,
366 unsigned long context)
043fff87
HS
367{
368 unsigned int status;
369
8a5d6d2e 370 status = inw(dev->iobase + S526_INT_STATUS_REG);
09c5d6c8
HS
371 if (status & context) {
372 /* we got our eoc event, clear it */
373 outw(context, dev->iobase + S526_INT_STATUS_REG);
043fff87 374 return 0;
09c5d6c8 375 }
043fff87
HS
376 return -EBUSY;
377}
378
bf483f1e
HS
379static int s526_ai_insn_read(struct comedi_device *dev,
380 struct comedi_subdevice *s,
381 struct comedi_insn *insn,
382 unsigned int *data)
0c988d00 383{
5f221062 384 struct s526_private *devpriv = dev->private;
43a35276 385 unsigned int chan = CR_CHAN(insn->chanspec);
fe79b3d0 386 unsigned int ctrl;
bf483f1e 387 unsigned int val;
043fff87 388 int ret;
bf483f1e 389 int i;
0c988d00 390
21424e3f 391 ctrl = S526_AI_CTRL_CONV(chan) | S526_AI_CTRL_READ(chan) |
fe79b3d0 392 S526_AI_CTRL_START;
21424e3f
HS
393 if (ctrl != devpriv->ai_ctrl) {
394 /*
395 * The multiplexor needs to change, enable the 15us
396 * delay for the first sample.
397 */
398 devpriv->ai_ctrl = ctrl;
399 ctrl |= S526_AI_CTRL_DELAY;
400 }
0c988d00 401
bf483f1e 402 for (i = 0; i < insn->n; i++) {
0c988d00 403 /* trigger conversion */
fe79b3d0 404 outw(ctrl, dev->iobase + S526_AI_CTRL_REG);
21424e3f 405 ctrl &= ~S526_AI_CTRL_DELAY;
0c988d00 406
0c988d00 407 /* wait for conversion to end */
09c5d6c8 408 ret = comedi_timeout(dev, s, insn, s526_eoc, S526_INT_AI);
043fff87
HS
409 if (ret)
410 return ret;
411
bf483f1e
HS
412 val = inw(dev->iobase + S526_AI_REG);
413 data[i] = comedi_offset_munge(s, val);
0c988d00
EW
414 }
415
bf483f1e 416 return insn->n;
0c988d00
EW
417}
418
6db4a1f5
HS
419static int s526_ao_insn_write(struct comedi_device *dev,
420 struct comedi_subdevice *s,
421 struct comedi_insn *insn,
422 unsigned int *data)
0c988d00 423{
43a35276 424 unsigned int chan = CR_CHAN(insn->chanspec);
abbb6489 425 unsigned int ctrl = S526_AO_CTRL_CHAN(chan);
6db4a1f5 426 unsigned int val = s->readback[chan];
09c5d6c8 427 int ret;
43a35276 428 int i;
0c988d00 429
abbb6489
HS
430 outw(ctrl, dev->iobase + S526_AO_CTRL_REG);
431 ctrl |= S526_AO_CTRL_START;
0c988d00 432
0c988d00 433 for (i = 0; i < insn->n; i++) {
6db4a1f5 434 val = data[i];
15bccf2e 435 outw(val, dev->iobase + S526_AO_REG);
abbb6489 436 outw(ctrl, dev->iobase + S526_AO_CTRL_REG);
09c5d6c8
HS
437
438 /* wait for conversion to end */
439 ret = comedi_timeout(dev, s, insn, s526_eoc, S526_INT_AO);
440 if (ret)
441 return ret;
0c988d00 442 }
6db4a1f5 443 s->readback[chan] = val;
0c988d00 444
6db4a1f5 445 return insn->n;
0c988d00
EW
446}
447
0a85b6f0
MT
448static int s526_dio_insn_bits(struct comedi_device *dev,
449 struct comedi_subdevice *s,
97f4289a
HS
450 struct comedi_insn *insn,
451 unsigned int *data)
0c988d00 452{
97f4289a 453 if (comedi_dio_update_state(s, data))
658441b4 454 outw(s->state, dev->iobase + S526_DIO_CTRL_REG);
0c988d00 455
658441b4 456 data[1] = inw(dev->iobase + S526_DIO_CTRL_REG) & 0xff;
0c988d00 457
a2714e3e 458 return insn->n;
0c988d00
EW
459}
460
0a85b6f0
MT
461static int s526_dio_insn_config(struct comedi_device *dev,
462 struct comedi_subdevice *s,
5dacadcc
HS
463 struct comedi_insn *insn,
464 unsigned int *data)
0c988d00 465{
43a35276 466 unsigned int chan = CR_CHAN(insn->chanspec);
5dacadcc
HS
467 unsigned int mask;
468 int ret;
469
658441b4
HS
470 /*
471 * Digital I/O can be configured as inputs or outputs in
472 * groups of 4; DIO group 1 (DIO0-3) and DIO group 2 (DIO4-7).
473 */
5dacadcc
HS
474 if (chan < 4)
475 mask = 0x0f;
476 else
477 mask = 0xf0;
478
479 ret = comedi_dio_insn_config(dev, s, insn, data, mask);
480 if (ret)
481 return ret;
482
5dacadcc 483 if (s->io_bits & 0x0f)
658441b4 484 s->state |= S526_DIO_CTRL_GRP1_OUT;
5dacadcc 485 else
658441b4 486 s->state &= ~S526_DIO_CTRL_GRP1_OUT;
5dacadcc 487 if (s->io_bits & 0xf0)
658441b4 488 s->state |= S526_DIO_CTRL_GRP2_OUT;
5dacadcc 489 else
658441b4 490 s->state &= ~S526_DIO_CTRL_GRP2_OUT;
0c988d00 491
658441b4 492 outw(s->state, dev->iobase + S526_DIO_CTRL_REG);
0c988d00 493
5dacadcc 494 return insn->n;
0c988d00
EW
495}
496
e9a4a7fb
HS
497static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it)
498{
5f221062 499 struct s526_private *devpriv;
e9a4a7fb 500 struct comedi_subdevice *s;
8b6c5694 501 int ret;
e9a4a7fb 502
862755ec 503 ret = comedi_request_region(dev, it->options[0], 0x40);
3cbc2810
HS
504 if (ret)
505 return ret;
e9a4a7fb 506
0bdab509 507 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
c34fa261
HS
508 if (!devpriv)
509 return -ENOMEM;
e9a4a7fb 510
8b6c5694
HS
511 ret = comedi_alloc_subdevices(dev, 4);
512 if (ret)
513 return ret;
e9a4a7fb 514
12911c2d 515 /* General-Purpose Counter/Timer (GPCT) */
97073c05 516 s = &dev->subdevices[0];
12911c2d
HS
517 s->type = COMEDI_SUBD_COUNTER;
518 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL;
519 s->n_chan = 4;
520 s->maxdata = 0x00ffffff;
521 s->insn_read = s526_gpct_rinsn;
522 s->insn_config = s526_gpct_insn_config;
523 s->insn_write = s526_gpct_winsn;
e9a4a7fb 524
12911c2d
HS
525 /*
526 * Analog Input subdevice
527 * channels 0 to 7 are the regular differential inputs
528 * channel 8 is "reference 0" (+10V)
529 * channel 9 is "reference 1" (0V)
530 */
97073c05 531 s = &dev->subdevices[1];
12911c2d
HS
532 s->type = COMEDI_SUBD_AI;
533 s->subdev_flags = SDF_READABLE | SDF_DIFF;
534 s->n_chan = 10;
535 s->maxdata = 0xffff;
536 s->range_table = &range_bipolar10;
537 s->len_chanlist = 16;
bf483f1e 538 s->insn_read = s526_ai_insn_read;
12911c2d
HS
539
540 /* Analog Output subdevice */
97073c05 541 s = &dev->subdevices[2];
12911c2d
HS
542 s->type = COMEDI_SUBD_AO;
543 s->subdev_flags = SDF_WRITABLE;
544 s->n_chan = 4;
545 s->maxdata = 0xffff;
546 s->range_table = &range_bipolar10;
547 s->insn_write = s526_ao_insn_write;
6db4a1f5
HS
548
549 ret = comedi_alloc_subdev_readback(s);
550 if (ret)
551 return ret;
e9a4a7fb 552
12911c2d 553 /* Digital I/O subdevice */
97073c05 554 s = &dev->subdevices[3];
12911c2d
HS
555 s->type = COMEDI_SUBD_DIO;
556 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
557 s->n_chan = 8;
558 s->maxdata = 1;
559 s->range_table = &range_digital;
560 s->insn_bits = s526_dio_insn_bits;
561 s->insn_config = s526_dio_insn_config;
e9a4a7fb 562
fb780d21 563 return 0;
e9a4a7fb
HS
564}
565
294f930d 566static struct comedi_driver s526_driver = {
e9a4a7fb
HS
567 .driver_name = "s526",
568 .module = THIS_MODULE,
569 .attach = s526_attach,
21208519 570 .detach = comedi_legacy_detach,
e9a4a7fb 571};
294f930d 572module_comedi_driver(s526_driver);
90f703d3
AT
573
574MODULE_AUTHOR("Comedi http://www.comedi.org");
575MODULE_DESCRIPTION("Comedi low-level driver");
576MODULE_LICENSE("GPL");