Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
[deliverable/linux.git] / drivers / staging / comedi / drivers / s526.c
CommitLineData
0c988d00
EW
1/*
2 comedi/drivers/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.
0c988d00
EW
17*/
18/*
19Driver: s526
20Description: Sensoray 526 driver
21Devices: [Sensoray] 526 (s526)
22Author: Richie
23 Everett Wang <everett.wang@everteq.com>
24Updated: Thu, 14 Sep. 2006
25Status: experimental
26
27Encoder works
28Analog input works
29Analog output works
30PWM output works
31Commands are not supported yet.
32
33Configuration Options:
34
35comedi_config /dev/comedi0 s526 0x2C0,0x3
36
37*/
38
ce157f80 39#include <linux/module.h>
0c988d00 40#include "../comedidev.h"
2b0318a6 41#include <asm/byteorder.h>
0c988d00 42
0c988d00
EW
43#define S526_START_AI_CONV 0
44#define S526_AI_READ 0
45
46/* Ports */
0c988d00
EW
47#define S526_NUM_PORTS 27
48
49/* registers */
50#define REG_TCR 0x00
51#define REG_WDC 0x02
52#define REG_DAC 0x04
53#define REG_ADC 0x06
54#define REG_ADD 0x08
55#define REG_DIO 0x0A
56#define REG_IER 0x0C
57#define REG_ISR 0x0E
58#define REG_MSC 0x10
59#define REG_C0L 0x12
60#define REG_C0H 0x14
61#define REG_C0M 0x16
62#define REG_C0C 0x18
63#define REG_C1L 0x1A
64#define REG_C1H 0x1C
65#define REG_C1M 0x1E
66#define REG_C1C 0x20
67#define REG_C2L 0x22
68#define REG_C2H 0x24
69#define REG_C2M 0x26
70#define REG_C2C 0x28
71#define REG_C3L 0x2A
72#define REG_C3H 0x2C
73#define REG_C3M 0x2E
74#define REG_C3C 0x30
75#define REG_EED 0x32
76#define REG_EEC 0x34
77
4b1d53f0 78struct counter_mode_register_t {
c9c62f4e 79#if defined(__LITTLE_ENDIAN_BITFIELD)
0c988d00
EW
80 unsigned short coutSource:1;
81 unsigned short coutPolarity:1;
82 unsigned short autoLoadResetRcap:3;
83 unsigned short hwCtEnableSource:2;
84 unsigned short ctEnableCtrl:2;
85 unsigned short clockSource:2;
86 unsigned short countDir:1;
87 unsigned short countDirCtrl:1;
88 unsigned short outputRegLatchCtrl:1;
89 unsigned short preloadRegSel:1;
90 unsigned short reserved:1;
2b0318a6
IA
91 #elif defined(__BIG_ENDIAN_BITFIELD)
92 unsigned short reserved:1;
93 unsigned short preloadRegSel:1;
94 unsigned short outputRegLatchCtrl:1;
95 unsigned short countDirCtrl:1;
96 unsigned short countDir:1;
97 unsigned short clockSource:2;
98 unsigned short ctEnableCtrl:2;
99 unsigned short hwCtEnableSource:2;
100 unsigned short autoLoadResetRcap:3;
101 unsigned short coutPolarity:1;
102 unsigned short coutSource:1;
103#else
104#error Unknown bit field order
105#endif
4b1d53f0 106};
0c988d00 107
ca98ee7b 108union cmReg {
4b1d53f0 109 struct counter_mode_register_t reg;
0c988d00 110 unsigned short value;
ca98ee7b 111};
0c988d00 112
6dc1ece0 113struct s526_private {
675f98f1 114 unsigned int gpct_config[4];
bda60cbf 115 unsigned short ai_config;
6dc1ece0
BP
116};
117
0a85b6f0 118static int s526_gpct_rinsn(struct comedi_device *dev,
2a29edf6
HS
119 struct comedi_subdevice *s,
120 struct comedi_insn *insn,
0a85b6f0 121 unsigned int *data)
0c988d00 122{
43a35276 123 unsigned int chan = CR_CHAN(insn->chanspec);
2a29edf6
HS
124 unsigned long chan_iobase = dev->iobase + chan * 8;
125 unsigned int lo;
126 unsigned int hi;
43a35276 127 int i;
0c988d00 128
0c988d00 129 for (i = 0; i < insn->n; i++) {
2a29edf6
HS
130 /* Read the low word first */
131 lo = inw(chan_iobase + REG_C0L) & 0xffff;
132 hi = inw(chan_iobase + REG_C0H) & 0xff;
133
134 data[i] = (hi << 16) | lo;
0c988d00 135 }
2a29edf6
HS
136
137 return insn->n;
0c988d00
EW
138}
139
0a85b6f0
MT
140static int s526_gpct_insn_config(struct comedi_device *dev,
141 struct comedi_subdevice *s,
5a5614cb
HS
142 struct comedi_insn *insn,
143 unsigned int *data)
0c988d00 144{
5f221062 145 struct s526_private *devpriv = dev->private;
43a35276 146 unsigned int chan = CR_CHAN(insn->chanspec);
5a5614cb
HS
147 unsigned long chan_iobase = dev->iobase + chan * 8;
148 unsigned int val;
ca98ee7b 149 union cmReg cmReg;
0c988d00 150
232f6502
BP
151 /* Check what type of Counter the user requested, data[0] contains */
152 /* the Application type */
b2abd982 153 switch (data[0]) {
0c988d00
EW
154 case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
155 /*
156 data[0]: Application Type
157 data[1]: Counter Mode Register Value
158 data[2]: Pre-load Register Value
159 data[3]: Conter Control Register
160 */
675f98f1 161 devpriv->gpct_config[chan] = data[0];
0c988d00 162
232f6502 163#if 0
0a85b6f0
MT
164 /* Example of Counter Application */
165 /* One-shot (software trigger) */
166 cmReg.reg.coutSource = 0; /* out RCAP */
167 cmReg.reg.coutPolarity = 1; /* Polarity inverted */
c9c62f4e 168 cmReg.reg.autoLoadResetRcap = 0;/* Auto load disabled */
0a85b6f0
MT
169 cmReg.reg.hwCtEnableSource = 3; /* NOT RCAP */
170 cmReg.reg.ctEnableCtrl = 2; /* Hardware */
171 cmReg.reg.clockSource = 2; /* Internal */
172 cmReg.reg.countDir = 1; /* Down */
173 cmReg.reg.countDirCtrl = 1; /* Software */
174 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
175 cmReg.reg.preloadRegSel = 0; /* PR0 */
176 cmReg.reg.reserved = 0;
0c988d00 177
5a5614cb 178 outw(cmReg.value, chan_iobase + REG_C0M);
0a85b6f0 179
5a5614cb
HS
180 outw(0x0001, chan_iobase + REG_C0H);
181 outw(0x3C68, chan_iobase + REG_C0L);
0a85b6f0 182
c9c62f4e 183 /* Reset the counter */
5a5614cb 184 outw(0x8000, chan_iobase + REG_C0C);
c9c62f4e 185 /* Load the counter from PR0 */
5a5614cb 186 outw(0x4000, chan_iobase + REG_C0C);
0c988d00 187
c9c62f4e 188 /* Reset RCAP (fires one-shot) */
5a5614cb 189 outw(0x0008, chan_iobase + REG_C0C);
0c988d00 190
232f6502 191#endif
0c988d00
EW
192
193#if 1
232f6502 194 /* Set Counter Mode Register */
5a5614cb
HS
195 cmReg.value = data[1] & 0xffff;
196 outw(cmReg.value, chan_iobase + REG_C0M);
0c988d00 197
232f6502 198 /* Reset the counter if it is software preload */
0c988d00 199 if (cmReg.reg.autoLoadResetRcap == 0) {
c9c62f4e 200 /* Reset the counter */
5a5614cb 201 outw(0x8000, chan_iobase + REG_C0C);
c9c62f4e 202 /* Load the counter from PR0
5a5614cb 203 * outw(0x4000, chan_iobase + REG_C0C);
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: */
b2abd982
HS
219 /*if (data[2] == GPCT_IndexPhaseLowLow) {
220 } else if (data[2] == GPCT_IndexPhaseLowHigh) {
221 } else if (data[2] == GPCT_IndexPhaseHighLow) {
222 } else if (data[2] == GPCT_IndexPhaseHighHigh) {
c9c62f4e 223 }*/
232f6502 224 /* Take into account the index pulse? */
b2abd982 225 if (data[3] == GPCT_RESET_COUNTER_ON_INDEX)
c9c62f4e
XF
226 /* Auto load with INDEX^ */
227 cmReg.reg.autoLoadResetRcap = 4;
0c988d00 228
232f6502 229 /* Set Counter Mode Register */
5a5614cb
HS
230 cmReg.value = data[1] & 0xffff;
231 outw(cmReg.value, chan_iobase + REG_C0M);
0c988d00 232
5044a2c0 233 /* Load the pre-load register high word */
5a5614cb
HS
234 val = (data[2] >> 16) & 0xffff;
235 outw(val, chan_iobase + REG_C0H);
0c988d00 236
5044a2c0 237 /* Load the pre-load register low word */
5a5614cb
HS
238 val = data[2] & 0xffff;
239 outw(val, chan_iobase + REG_C0L);
0c988d00 240
232f6502 241 /* Write the Counter Control Register */
5a5614cb
HS
242 if (data[3]) {
243 val = data[3] & 0xffff;
244 outw(val, chan_iobase + REG_C0C);
0c988d00 245 }
232f6502 246 /* Reset the counter if it is software preload */
0c988d00 247 if (cmReg.reg.autoLoadResetRcap == 0) {
c9c62f4e 248 /* Reset the counter */
5a5614cb 249 outw(0x8000, chan_iobase + REG_C0C);
c9c62f4e 250 /* Load the counter from PR0 */
5a5614cb 251 outw(0x4000, chan_iobase + REG_C0C);
0c988d00
EW
252 }
253#endif
254 break;
255
256 case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
257 /*
258 data[0]: Application Type
259 data[1]: Counter Mode Register Value
260 data[2]: Pre-load Register 0 Value
261 data[3]: Pre-load Register 1 Value
262 data[4]: Conter Control Register
263 */
675f98f1 264 devpriv->gpct_config[chan] = data[0];
0c988d00 265
232f6502 266 /* Set Counter Mode Register */
5a5614cb 267 cmReg.value = data[1] & 0xffff;
232f6502 268 cmReg.reg.preloadRegSel = 0; /* PR0 */
5a5614cb 269 outw(cmReg.value, chan_iobase + REG_C0M);
0c988d00 270
5044a2c0 271 /* Load the pre-load register 0 high word */
5a5614cb
HS
272 val = (data[2] >> 16) & 0xffff;
273 outw(val, chan_iobase + REG_C0H);
0c988d00 274
5044a2c0 275 /* Load the pre-load register 0 low word */
5a5614cb
HS
276 val = data[2] & 0xffff;
277 outw(val, chan_iobase + REG_C0L);
0c988d00 278
232f6502 279 /* Set Counter Mode Register */
5a5614cb 280 cmReg.value = data[1] & 0xffff;
232f6502 281 cmReg.reg.preloadRegSel = 1; /* PR1 */
5a5614cb 282 outw(cmReg.value, chan_iobase + REG_C0M);
0c988d00 283
5044a2c0 284 /* Load the pre-load register 1 high word */
5a5614cb
HS
285 val = (data[3] >> 16) & 0xffff;
286 outw(val, chan_iobase + REG_C0H);
0c988d00 287
5044a2c0 288 /* Load the pre-load register 1 low word */
5a5614cb
HS
289 val = data[3] & 0xffff;
290 outw(val, chan_iobase + REG_C0L);
0c988d00 291
232f6502 292 /* Write the Counter Control Register */
5a5614cb
HS
293 if (data[4]) {
294 val = data[4] & 0xffff;
295 outw(val, chan_iobase + REG_C0C);
0c988d00
EW
296 }
297 break;
298
299 case INSN_CONFIG_GPCT_PULSE_TRAIN_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 */
675f98f1 307 devpriv->gpct_config[chan] = data[0];
0c988d00 308
232f6502 309 /* Set Counter Mode Register */
5a5614cb 310 cmReg.value = data[1] & 0xffff;
232f6502 311 cmReg.reg.preloadRegSel = 0; /* PR0 */
5a5614cb 312 outw(cmReg.value, chan_iobase + REG_C0M);
0c988d00 313
5044a2c0 314 /* Load the pre-load register 0 high word */
5a5614cb
HS
315 val = (data[2] >> 16) & 0xffff;
316 outw(val, chan_iobase + REG_C0H);
0c988d00 317
5044a2c0 318 /* Load the pre-load register 0 low word */
5a5614cb
HS
319 val = data[2] & 0xffff;
320 outw(val, chan_iobase + REG_C0L);
0c988d00 321
232f6502 322 /* Set Counter Mode Register */
5a5614cb 323 cmReg.value = data[1] & 0xffff;
232f6502 324 cmReg.reg.preloadRegSel = 1; /* PR1 */
5a5614cb 325 outw(cmReg.value, chan_iobase + REG_C0M);
0c988d00 326
5044a2c0 327 /* Load the pre-load register 1 high word */
5a5614cb
HS
328 val = (data[3] >> 16) & 0xffff;
329 outw(val, chan_iobase + REG_C0H);
0c988d00 330
5044a2c0 331 /* Load the pre-load register 1 low word */
5a5614cb
HS
332 val = data[3] & 0xffff;
333 outw(val, chan_iobase + REG_C0L);
0c988d00 334
232f6502 335 /* Write the Counter Control Register */
5a5614cb
HS
336 if (data[4]) {
337 val = data[4] & 0xffff;
338 outw(val, chan_iobase + REG_C0C);
0c988d00
EW
339 }
340 break;
341
342 default:
0c988d00 343 return -EINVAL;
0c988d00
EW
344 }
345
346 return insn->n;
347}
348
0a85b6f0 349static int s526_gpct_winsn(struct comedi_device *dev,
5c813bb1
HS
350 struct comedi_subdevice *s,
351 struct comedi_insn *insn,
0a85b6f0 352 unsigned int *data)
0c988d00 353{
5f221062 354 struct s526_private *devpriv = dev->private;
43a35276 355 unsigned int chan = CR_CHAN(insn->chanspec);
5c813bb1
HS
356 unsigned long chan_iobase = dev->iobase + chan * 8;
357
358 inw(chan_iobase + REG_C0M); /* Is this read required? */
0c988d00 359
232f6502 360 /* Check what Application of Counter this channel is configured for */
675f98f1
HS
361 switch (devpriv->gpct_config[chan]) {
362 case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
0c988d00
EW
363 /* data[0] contains the PULSE_WIDTH
364 data[1] contains the PULSE_PERIOD
365 @pre PULSE_PERIOD > PULSE_WIDTH > 0
366 The above periods must be expressed as a multiple of the
367 pulse frequency on the selected source
368 */
67f2021f 369 if ((data[1] <= data[0]) || !data[0])
0c988d00 370 return -EINVAL;
0c988d00 371
5c813bb1
HS
372 /* Fall thru to write the PULSE_WIDTH */
373
675f98f1
HS
374 case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
375 case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
5c813bb1
HS
376 outw((data[0] >> 16) & 0xffff, chan_iobase + REG_C0H);
377 outw(data[0] & 0xffff, chan_iobase + REG_C0L);
0c988d00 378 break;
5c813bb1
HS
379
380 default:
0c988d00 381 return -EINVAL;
0c988d00 382 }
5c813bb1 383
0c988d00
EW
384 return insn->n;
385}
386
387#define ISR_ADC_DONE 0x4
0a85b6f0
MT
388static int s526_ai_insn_config(struct comedi_device *dev,
389 struct comedi_subdevice *s,
390 struct comedi_insn *insn, unsigned int *data)
0c988d00 391{
5f221062 392 struct s526_private *devpriv = dev->private;
0c988d00
EW
393 int result = -EINVAL;
394
395 if (insn->n < 1)
396 return result;
397
398 result = insn->n;
399
400 /* data[0] : channels was set in relevant bits.
401 data[1] : delay
402 */
403 /* COMMENT: abbotti 2008-07-24: I don't know why you'd want to
404 * enable channels here. The channel should be enabled in the
405 * INSN_READ handler. */
406
232f6502 407 /* Enable ADC interrupt */
0171e6f5 408 outw(ISR_ADC_DONE, dev->iobase + REG_IER);
bda60cbf 409 devpriv->ai_config = (data[0] & 0x3ff) << 5;
0c988d00 410 if (data[1] > 0)
bda60cbf 411 devpriv->ai_config |= 0x8000; /* set the delay */
0c988d00 412
bda60cbf 413 devpriv->ai_config |= 0x0001; /* ADC start bit */
0c988d00
EW
414
415 return result;
416}
417
043fff87
HS
418static int s526_ai_eoc(struct comedi_device *dev,
419 struct comedi_subdevice *s,
420 struct comedi_insn *insn,
421 unsigned long context)
422{
423 unsigned int status;
424
425 status = inw(dev->iobase + REG_ISR);
426 if (status & ISR_ADC_DONE)
427 return 0;
428 return -EBUSY;
429}
430
da91b269 431static int s526_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 432 struct comedi_insn *insn, unsigned int *data)
0c988d00 433{
5f221062 434 struct s526_private *devpriv = dev->private;
43a35276 435 unsigned int chan = CR_CHAN(insn->chanspec);
043fff87 436 int n;
0c988d00
EW
437 unsigned short value;
438 unsigned int d;
043fff87 439 int ret;
0c988d00
EW
440
441 /* Set configured delay, enable channel for this channel only,
442 * select "ADC read" channel, set "ADC start" bit. */
bda60cbf
HS
443 value = (devpriv->ai_config & 0x8000) |
444 ((1 << 5) << chan) | (chan << 1) | 0x0001;
0c988d00
EW
445
446 /* convert n samples */
447 for (n = 0; n < insn->n; n++) {
448 /* trigger conversion */
0171e6f5 449 outw(value, dev->iobase + REG_ADC);
0c988d00 450
0c988d00 451 /* wait for conversion to end */
043fff87
HS
452 ret = comedi_timeout(dev, s, insn, s526_ai_eoc, 0);
453 if (ret)
454 return ret;
455
456 outw(ISR_ADC_DONE, dev->iobase + REG_ISR);
0c988d00
EW
457
458 /* read data */
0171e6f5 459 d = inw(dev->iobase + REG_ADD);
0c988d00
EW
460
461 /* munge data */
462 data[n] = d ^ 0x8000;
463 }
464
465 /* return the number of samples read/written */
466 return n;
467}
468
6db4a1f5
HS
469static int s526_ao_insn_write(struct comedi_device *dev,
470 struct comedi_subdevice *s,
471 struct comedi_insn *insn,
472 unsigned int *data)
0c988d00 473{
43a35276 474 unsigned int chan = CR_CHAN(insn->chanspec);
6db4a1f5 475 unsigned int val = s->readback[chan];
43a35276 476 int i;
0c988d00 477
6db4a1f5 478 outw(chan << 1, dev->iobase + REG_DAC);
0c988d00 479
0c988d00 480 for (i = 0; i < insn->n; i++) {
6db4a1f5
HS
481 val = data[i];
482 outw(val, dev->iobase + REG_ADD);
0171e6f5 483 /* starts the D/A conversion */
6db4a1f5 484 outw((chan << 1) | 1, dev->iobase + REG_DAC);
0c988d00 485 }
6db4a1f5 486 s->readback[chan] = val;
0c988d00 487
6db4a1f5 488 return insn->n;
0c988d00
EW
489}
490
0a85b6f0
MT
491static int s526_dio_insn_bits(struct comedi_device *dev,
492 struct comedi_subdevice *s,
97f4289a
HS
493 struct comedi_insn *insn,
494 unsigned int *data)
0c988d00 495{
97f4289a 496 if (comedi_dio_update_state(s, data))
0171e6f5 497 outw(s->state, dev->iobase + REG_DIO);
0c988d00 498
0171e6f5 499 data[1] = inw(dev->iobase + REG_DIO) & 0xff;
0c988d00 500
a2714e3e 501 return insn->n;
0c988d00
EW
502}
503
0a85b6f0
MT
504static int s526_dio_insn_config(struct comedi_device *dev,
505 struct comedi_subdevice *s,
5dacadcc
HS
506 struct comedi_insn *insn,
507 unsigned int *data)
0c988d00 508{
43a35276 509 unsigned int chan = CR_CHAN(insn->chanspec);
5dacadcc
HS
510 unsigned int mask;
511 int ret;
512
513 if (chan < 4)
514 mask = 0x0f;
515 else
516 mask = 0xf0;
517
518 ret = comedi_dio_insn_config(dev, s, insn, data, mask);
519 if (ret)
520 return ret;
521
522 /* bit 10/11 set the group 1/2's mode */
523 if (s->io_bits & 0x0f)
524 s->state |= (1 << 10);
525 else
526 s->state &= ~(1 << 10);
527 if (s->io_bits & 0xf0)
528 s->state |= (1 << 11);
529 else
530 s->state &= ~(1 << 11);
0c988d00 531
0171e6f5 532 outw(s->state, dev->iobase + REG_DIO);
0c988d00 533
5dacadcc 534 return insn->n;
0c988d00
EW
535}
536
e9a4a7fb
HS
537static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it)
538{
5f221062 539 struct s526_private *devpriv;
e9a4a7fb 540 struct comedi_subdevice *s;
8b6c5694 541 int ret;
e9a4a7fb 542
862755ec 543 ret = comedi_request_region(dev, it->options[0], 0x40);
3cbc2810
HS
544 if (ret)
545 return ret;
e9a4a7fb 546
0bdab509 547 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
c34fa261
HS
548 if (!devpriv)
549 return -ENOMEM;
e9a4a7fb 550
8b6c5694
HS
551 ret = comedi_alloc_subdevices(dev, 4);
552 if (ret)
553 return ret;
e9a4a7fb 554
97073c05 555 s = &dev->subdevices[0];
e9a4a7fb
HS
556 /* GENERAL-PURPOSE COUNTER/TIME (GPCT) */
557 s->type = COMEDI_SUBD_COUNTER;
558 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL;
3d9083b2 559 s->n_chan = 4;
e9a4a7fb
HS
560 s->maxdata = 0x00ffffff; /* 24 bit counter */
561 s->insn_read = s526_gpct_rinsn;
562 s->insn_config = s526_gpct_insn_config;
563 s->insn_write = s526_gpct_winsn;
564
97073c05 565 s = &dev->subdevices[1];
e9a4a7fb
HS
566 /* analog input subdevice */
567 s->type = COMEDI_SUBD_AI;
e9a4a7fb
HS
568 s->subdev_flags = SDF_READABLE | SDF_DIFF;
569 /* channels 0 to 7 are the regular differential inputs */
570 /* channel 8 is "reference 0" (+10V), channel 9 is "reference 1" (0V) */
571 s->n_chan = 10;
572 s->maxdata = 0xffff;
573 s->range_table = &range_bipolar10;
2c781789 574 s->len_chanlist = 16;
e9a4a7fb
HS
575 s->insn_read = s526_ai_rinsn;
576 s->insn_config = s526_ai_insn_config;
577
97073c05 578 s = &dev->subdevices[2];
e9a4a7fb
HS
579 /* analog output subdevice */
580 s->type = COMEDI_SUBD_AO;
581 s->subdev_flags = SDF_WRITABLE;
582 s->n_chan = 4;
583 s->maxdata = 0xffff;
584 s->range_table = &range_bipolar10;
6db4a1f5
HS
585 s->insn_write = s526_ao_insn_write;
586 s->insn_read = comedi_readback_insn_read;
587
588 ret = comedi_alloc_subdev_readback(s);
589 if (ret)
590 return ret;
e9a4a7fb 591
97073c05 592 s = &dev->subdevices[3];
e9a4a7fb 593 /* digital i/o subdevice */
3d9083b2
HS
594 s->type = COMEDI_SUBD_DIO;
595 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
596 s->n_chan = 8;
597 s->maxdata = 1;
598 s->range_table = &range_digital;
599 s->insn_bits = s526_dio_insn_bits;
600 s->insn_config = s526_dio_insn_config;
e9a4a7fb 601
fb780d21 602 return 0;
e9a4a7fb
HS
603}
604
294f930d 605static struct comedi_driver s526_driver = {
e9a4a7fb
HS
606 .driver_name = "s526",
607 .module = THIS_MODULE,
608 .attach = s526_attach,
21208519 609 .detach = comedi_legacy_detach,
e9a4a7fb 610};
294f930d 611module_comedi_driver(s526_driver);
90f703d3
AT
612
613MODULE_AUTHOR("Comedi http://www.comedi.org");
614MODULE_DESCRIPTION("Comedi low-level driver");
615MODULE_LICENSE("GPL");
This page took 0.684336 seconds and 5 git commands to generate.