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