staging: comedi: s526: cleanup s526_gpct_rinsn()
[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
EW
120
121#define MAX_GPCT_CONFIG_DATA 6
122
123/* Different Application Classes for GPCT Subdevices */
124/* The list is not exhaustive and needs discussion! */
dfb0503e 125enum S526_GPCT_APP_CLASS {
0c988d00
EW
126 CountingAndTimeMeasurement,
127 SinglePulseGeneration,
128 PulseTrainGeneration,
129 PositionMeasurement,
130 Miscellaneous
dfb0503e 131};
0c988d00
EW
132
133/* Config struct for different GPCT subdevice Application Classes and
134 their options
135*/
39f76660 136struct s526GPCTConfig {
dfb0503e 137 enum S526_GPCT_APP_CLASS app;
0c988d00 138 int data[MAX_GPCT_CONFIG_DATA];
39f76660 139};
0c988d00 140
6dc1ece0 141struct s526_private {
790c5541 142 unsigned int ao_readback[2];
39f76660 143 struct s526GPCTConfig s526_gpct_config[4];
0c988d00 144 unsigned short s526_ai_config;
6dc1ece0
BP
145};
146
0a85b6f0 147static int s526_gpct_rinsn(struct comedi_device *dev,
2a29edf6
HS
148 struct comedi_subdevice *s,
149 struct comedi_insn *insn,
0a85b6f0 150 unsigned int *data)
0c988d00 151{
43a35276 152 unsigned int chan = CR_CHAN(insn->chanspec);
2a29edf6
HS
153 unsigned long chan_iobase = dev->iobase + chan * 8;
154 unsigned int lo;
155 unsigned int hi;
43a35276 156 int i;
0c988d00 157
0c988d00 158 for (i = 0; i < insn->n; i++) {
2a29edf6
HS
159 /* Read the low word first */
160 lo = inw(chan_iobase + REG_C0L) & 0xffff;
161 hi = inw(chan_iobase + REG_C0H) & 0xff;
162
163 data[i] = (hi << 16) | lo;
0c988d00 164 }
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,
171 struct comedi_insn *insn, unsigned int *data)
0c988d00 172{
5f221062 173 struct s526_private *devpriv = dev->private;
43a35276 174 unsigned int chan = CR_CHAN(insn->chanspec);
0c988d00 175 int i;
790c5541 176 short value;
ca98ee7b 177 union cmReg cmReg;
0c988d00 178
898143bd 179 for (i = 0; i < MAX_GPCT_CONFIG_DATA; i++)
43a35276 180 devpriv->s526_gpct_config[chan].data[i] = data[i];
0c988d00 181
232f6502
BP
182 /* Check what type of Counter the user requested, data[0] contains */
183 /* the Application type */
b2abd982 184 switch (data[0]) {
0c988d00
EW
185 case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
186 /*
187 data[0]: Application Type
188 data[1]: Counter Mode Register Value
189 data[2]: Pre-load Register Value
190 data[3]: Conter Control Register
191 */
43a35276 192 devpriv->s526_gpct_config[chan].app = PositionMeasurement;
0c988d00 193
232f6502 194#if 0
0a85b6f0
MT
195 /* Example of Counter Application */
196 /* One-shot (software trigger) */
197 cmReg.reg.coutSource = 0; /* out RCAP */
198 cmReg.reg.coutPolarity = 1; /* Polarity inverted */
c9c62f4e 199 cmReg.reg.autoLoadResetRcap = 0;/* Auto load disabled */
0a85b6f0
MT
200 cmReg.reg.hwCtEnableSource = 3; /* NOT RCAP */
201 cmReg.reg.ctEnableCtrl = 2; /* Hardware */
202 cmReg.reg.clockSource = 2; /* Internal */
203 cmReg.reg.countDir = 1; /* Down */
204 cmReg.reg.countDirCtrl = 1; /* Software */
205 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
206 cmReg.reg.preloadRegSel = 0; /* PR0 */
207 cmReg.reg.reserved = 0;
0c988d00 208
43a35276 209 outw(cmReg.value, dev->iobase + REG_C0M + chan * 8);
0a85b6f0 210
43a35276
HS
211 outw(0x0001, dev->iobase + REG_C0H + chan * 8);
212 outw(0x3C68, dev->iobase + REG_C0L + chan * 8);
0a85b6f0 213
c9c62f4e 214 /* Reset the counter */
43a35276 215 outw(0x8000, dev->iobase + REG_C0C + chan * 8);
c9c62f4e 216 /* Load the counter from PR0 */
43a35276 217 outw(0x4000, dev->iobase + REG_C0C + chan * 8);
0c988d00 218
c9c62f4e 219 /* Reset RCAP (fires one-shot) */
43a35276 220 outw(0x0008, dev->iobase + REG_C0C + chan * 8);
0c988d00 221
232f6502 222#endif
0c988d00
EW
223
224#if 1
232f6502 225 /* Set Counter Mode Register */
b2abd982 226 cmReg.value = data[1] & 0xFFFF;
43a35276 227 outw(cmReg.value, dev->iobase + REG_C0M + chan * 8);
0c988d00 228
232f6502 229 /* Reset the counter if it is software preload */
0c988d00 230 if (cmReg.reg.autoLoadResetRcap == 0) {
c9c62f4e 231 /* Reset the counter */
43a35276 232 outw(0x8000, dev->iobase + REG_C0C + chan * 8);
c9c62f4e 233 /* Load the counter from PR0
43a35276 234 * outw(0x4000, dev->iobase + REG_C0C + chan * 8);
c9c62f4e 235 */
0c988d00
EW
236 }
237#else
c9c62f4e
XF
238 /* 0 quadrature, 1 software control */
239 cmReg.reg.countDirCtrl = 0;
0c988d00 240
232f6502 241 /* data[1] contains GPCT_X1, GPCT_X2 or GPCT_X4 */
b2abd982 242 if (data[1] == GPCT_X2)
0c988d00 243 cmReg.reg.clockSource = 1;
b2abd982 244 else if (data[1] == GPCT_X4)
0c988d00 245 cmReg.reg.clockSource = 2;
c9c62f4e 246 else
0c988d00 247 cmReg.reg.clockSource = 0;
0c988d00 248
232f6502 249 /* When to take into account the indexpulse: */
b2abd982
HS
250 /*if (data[2] == GPCT_IndexPhaseLowLow) {
251 } else if (data[2] == GPCT_IndexPhaseLowHigh) {
252 } else if (data[2] == GPCT_IndexPhaseHighLow) {
253 } else if (data[2] == GPCT_IndexPhaseHighHigh) {
c9c62f4e 254 }*/
232f6502 255 /* Take into account the index pulse? */
b2abd982 256 if (data[3] == GPCT_RESET_COUNTER_ON_INDEX)
c9c62f4e
XF
257 /* Auto load with INDEX^ */
258 cmReg.reg.autoLoadResetRcap = 4;
0c988d00 259
232f6502 260 /* Set Counter Mode Register */
b2abd982 261 cmReg.value = (short)(data[1] & 0xFFFF);
43a35276 262 outw(cmReg.value, dev->iobase + REG_C0M + chan * 8);
0c988d00 263
5044a2c0 264 /* Load the pre-load register high word */
b2abd982 265 value = (short)((data[2] >> 16) & 0xFFFF);
43a35276 266 outw(value, dev->iobase + REG_C0H + chan * 8);
0c988d00 267
5044a2c0 268 /* Load the pre-load register low word */
b2abd982 269 value = (short)(data[2] & 0xFFFF);
43a35276 270 outw(value, dev->iobase + REG_C0L + chan * 8);
0c988d00 271
232f6502 272 /* Write the Counter Control Register */
b2abd982
HS
273 if (data[3] != 0) {
274 value = (short)(data[3] & 0xFFFF);
43a35276 275 outw(value, dev->iobase + REG_C0C + chan * 8);
0c988d00 276 }
232f6502 277 /* Reset the counter if it is software preload */
0c988d00 278 if (cmReg.reg.autoLoadResetRcap == 0) {
c9c62f4e 279 /* Reset the counter */
43a35276 280 outw(0x8000, dev->iobase + REG_C0C + chan * 8);
c9c62f4e 281 /* Load the counter from PR0 */
43a35276 282 outw(0x4000, dev->iobase + REG_C0C + chan * 8);
0c988d00
EW
283 }
284#endif
285 break;
286
287 case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
288 /*
289 data[0]: Application Type
290 data[1]: Counter Mode Register Value
291 data[2]: Pre-load Register 0 Value
292 data[3]: Pre-load Register 1 Value
293 data[4]: Conter Control Register
294 */
43a35276 295 devpriv->s526_gpct_config[chan].app = SinglePulseGeneration;
0c988d00 296
232f6502 297 /* Set Counter Mode Register */
b2abd982 298 cmReg.value = (short)(data[1] & 0xFFFF);
232f6502 299 cmReg.reg.preloadRegSel = 0; /* PR0 */
43a35276 300 outw(cmReg.value, dev->iobase + REG_C0M + chan * 8);
0c988d00 301
5044a2c0 302 /* Load the pre-load register 0 high word */
b2abd982 303 value = (short)((data[2] >> 16) & 0xFFFF);
43a35276 304 outw(value, dev->iobase + REG_C0H + chan * 8);
0c988d00 305
5044a2c0 306 /* Load the pre-load register 0 low word */
b2abd982 307 value = (short)(data[2] & 0xFFFF);
43a35276 308 outw(value, dev->iobase + REG_C0L + chan * 8);
0c988d00 309
232f6502 310 /* Set Counter Mode Register */
b2abd982 311 cmReg.value = (short)(data[1] & 0xFFFF);
232f6502 312 cmReg.reg.preloadRegSel = 1; /* PR1 */
43a35276 313 outw(cmReg.value, dev->iobase + REG_C0M + chan * 8);
0c988d00 314
5044a2c0 315 /* Load the pre-load register 1 high word */
b2abd982 316 value = (short)((data[3] >> 16) & 0xFFFF);
43a35276 317 outw(value, dev->iobase + REG_C0H + chan * 8);
0c988d00 318
5044a2c0 319 /* Load the pre-load register 1 low word */
b2abd982 320 value = (short)(data[3] & 0xFFFF);
43a35276 321 outw(value, dev->iobase + REG_C0L + chan * 8);
0c988d00 322
232f6502 323 /* Write the Counter Control Register */
b2abd982
HS
324 if (data[4] != 0) {
325 value = (short)(data[4] & 0xFFFF);
43a35276 326 outw(value, dev->iobase + REG_C0C + chan * 8);
0c988d00
EW
327 }
328 break;
329
330 case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
331 /*
332 data[0]: Application Type
333 data[1]: Counter Mode Register Value
334 data[2]: Pre-load Register 0 Value
335 data[3]: Pre-load Register 1 Value
336 data[4]: Conter Control Register
337 */
43a35276 338 devpriv->s526_gpct_config[chan].app = PulseTrainGeneration;
0c988d00 339
232f6502 340 /* Set Counter Mode Register */
b2abd982 341 cmReg.value = (short)(data[1] & 0xFFFF);
232f6502 342 cmReg.reg.preloadRegSel = 0; /* PR0 */
43a35276 343 outw(cmReg.value, dev->iobase + REG_C0M + chan * 8);
0c988d00 344
5044a2c0 345 /* Load the pre-load register 0 high word */
b2abd982 346 value = (short)((data[2] >> 16) & 0xFFFF);
43a35276 347 outw(value, dev->iobase + REG_C0H + chan * 8);
0c988d00 348
5044a2c0 349 /* Load the pre-load register 0 low word */
b2abd982 350 value = (short)(data[2] & 0xFFFF);
43a35276 351 outw(value, dev->iobase + REG_C0L + chan * 8);
0c988d00 352
232f6502 353 /* Set Counter Mode Register */
b2abd982 354 cmReg.value = (short)(data[1] & 0xFFFF);
232f6502 355 cmReg.reg.preloadRegSel = 1; /* PR1 */
43a35276 356 outw(cmReg.value, dev->iobase + REG_C0M + chan * 8);
0c988d00 357
5044a2c0 358 /* Load the pre-load register 1 high word */
b2abd982 359 value = (short)((data[3] >> 16) & 0xFFFF);
43a35276 360 outw(value, dev->iobase + REG_C0H + chan * 8);
0c988d00 361
5044a2c0 362 /* Load the pre-load register 1 low word */
b2abd982 363 value = (short)(data[3] & 0xFFFF);
43a35276 364 outw(value, dev->iobase + REG_C0L + chan * 8);
0c988d00 365
232f6502 366 /* Write the Counter Control Register */
b2abd982
HS
367 if (data[4] != 0) {
368 value = (short)(data[4] & 0xFFFF);
43a35276 369 outw(value, dev->iobase + REG_C0C + chan * 8);
0c988d00
EW
370 }
371 break;
372
373 default:
0c988d00
EW
374 return -EINVAL;
375 break;
376 }
377
378 return insn->n;
379}
380
0a85b6f0
MT
381static int s526_gpct_winsn(struct comedi_device *dev,
382 struct comedi_subdevice *s, struct comedi_insn *insn,
383 unsigned int *data)
0c988d00 384{
5f221062 385 struct s526_private *devpriv = dev->private;
43a35276 386 unsigned int chan = CR_CHAN(insn->chanspec);
790c5541 387 short value;
ca98ee7b 388 union cmReg cmReg;
0c988d00 389
43a35276 390 cmReg.value = inw(dev->iobase + REG_C0M + chan * 8);
232f6502 391 /* Check what Application of Counter this channel is configured for */
43a35276 392 switch (devpriv->s526_gpct_config[chan].app) {
0c988d00 393 case PositionMeasurement:
43a35276
HS
394 outw(0xFFFF & ((*data) >> 16), dev->iobase + REG_C0H + chan * 8);
395 outw(0xFFFF & (*data), dev->iobase + REG_C0L + chan * 8);
0c988d00
EW
396 break;
397
398 case SinglePulseGeneration:
43a35276
HS
399 outw(0xFFFF & ((*data) >> 16), dev->iobase + REG_C0H + chan * 8);
400 outw(0xFFFF & (*data), dev->iobase + REG_C0L + chan * 8);
0c988d00
EW
401 break;
402
403 case PulseTrainGeneration:
404 /* data[0] contains the PULSE_WIDTH
405 data[1] contains the PULSE_PERIOD
406 @pre PULSE_PERIOD > PULSE_WIDTH > 0
407 The above periods must be expressed as a multiple of the
408 pulse frequency on the selected source
409 */
b2abd982 410 if ((data[1] > data[0]) && (data[0] > 0)) {
43a35276
HS
411 (devpriv->s526_gpct_config[chan]).data[0] = data[0];
412 (devpriv->s526_gpct_config[chan]).data[1] = data[1];
0c988d00 413 } else {
0c988d00
EW
414 return -EINVAL;
415 }
416
0a85b6f0 417 value = (short)((*data >> 16) & 0xFFFF);
43a35276 418 outw(value, dev->iobase + REG_C0H + chan * 8);
0a85b6f0 419 value = (short)(*data & 0xFFFF);
43a35276 420 outw(value, dev->iobase + REG_C0L + chan * 8);
0c988d00 421 break;
232f6502 422 default: /* Impossible */
0c988d00
EW
423 return -EINVAL;
424 break;
425 }
232f6502 426 /* return the number of samples written */
0c988d00
EW
427 return insn->n;
428}
429
430#define ISR_ADC_DONE 0x4
0a85b6f0
MT
431static int s526_ai_insn_config(struct comedi_device *dev,
432 struct comedi_subdevice *s,
433 struct comedi_insn *insn, unsigned int *data)
0c988d00 434{
5f221062 435 struct s526_private *devpriv = dev->private;
0c988d00
EW
436 int result = -EINVAL;
437
438 if (insn->n < 1)
439 return result;
440
441 result = insn->n;
442
443 /* data[0] : channels was set in relevant bits.
444 data[1] : delay
445 */
446 /* COMMENT: abbotti 2008-07-24: I don't know why you'd want to
447 * enable channels here. The channel should be enabled in the
448 * INSN_READ handler. */
449
232f6502 450 /* Enable ADC interrupt */
0171e6f5 451 outw(ISR_ADC_DONE, dev->iobase + REG_IER);
0c988d00
EW
452 devpriv->s526_ai_config = (data[0] & 0x3FF) << 5;
453 if (data[1] > 0)
232f6502 454 devpriv->s526_ai_config |= 0x8000; /* set the delay */
0c988d00 455
232f6502 456 devpriv->s526_ai_config |= 0x0001; /* ADC start bit. */
0c988d00
EW
457
458 return result;
459}
460
da91b269 461static int s526_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 462 struct comedi_insn *insn, unsigned int *data)
0c988d00 463{
5f221062 464 struct s526_private *devpriv = dev->private;
43a35276 465 unsigned int chan = CR_CHAN(insn->chanspec);
0c988d00 466 int n, i;
0c988d00
EW
467 unsigned short value;
468 unsigned int d;
469 unsigned int status;
470
471 /* Set configured delay, enable channel for this channel only,
472 * select "ADC read" channel, set "ADC start" bit. */
473 value = (devpriv->s526_ai_config & 0x8000) |
0a85b6f0 474 ((1 << 5) << chan) | (chan << 1) | 0x0001;
0c988d00
EW
475
476 /* convert n samples */
477 for (n = 0; n < insn->n; n++) {
478 /* trigger conversion */
0171e6f5 479 outw(value, dev->iobase + REG_ADC);
0c988d00
EW
480
481#define TIMEOUT 100
482 /* wait for conversion to end */
483 for (i = 0; i < TIMEOUT; i++) {
0171e6f5 484 status = inw(dev->iobase + REG_ISR);
0c988d00 485 if (status & ISR_ADC_DONE) {
0171e6f5 486 outw(ISR_ADC_DONE, dev->iobase + REG_ISR);
0c988d00
EW
487 break;
488 }
489 }
d160895f 490 if (i == TIMEOUT)
0c988d00 491 return -ETIMEDOUT;
0c988d00
EW
492
493 /* read data */
0171e6f5 494 d = inw(dev->iobase + REG_ADD);
0c988d00
EW
495
496 /* munge data */
497 data[n] = d ^ 0x8000;
498 }
499
500 /* return the number of samples read/written */
501 return n;
502}
503
da91b269 504static int s526_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 505 struct comedi_insn *insn, unsigned int *data)
0c988d00 506{
5f221062 507 struct s526_private *devpriv = dev->private;
43a35276 508 unsigned int chan = CR_CHAN(insn->chanspec);
0c988d00 509 unsigned short val;
43a35276 510 int i;
0c988d00 511
0c988d00 512 val = chan << 1;
0171e6f5 513 outw(val, dev->iobase + REG_DAC);
0c988d00 514
0c988d00 515 for (i = 0; i < insn->n; i++) {
0171e6f5 516 outw(data[i], dev->iobase + REG_ADD);
0c988d00 517 devpriv->ao_readback[chan] = data[i];
0171e6f5
HS
518 /* starts the D/A conversion */
519 outw(val + 1, dev->iobase + REG_DAC);
0c988d00
EW
520 }
521
0c988d00
EW
522 return i;
523}
524
da91b269 525static int s526_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 526 struct comedi_insn *insn, unsigned int *data)
0c988d00 527{
5f221062 528 struct s526_private *devpriv = dev->private;
43a35276 529 unsigned int chan = CR_CHAN(insn->chanspec);
0c988d00 530 int i;
0c988d00
EW
531
532 for (i = 0; i < insn->n; i++)
533 data[i] = devpriv->ao_readback[chan];
534
535 return i;
536}
537
0a85b6f0
MT
538static int s526_dio_insn_bits(struct comedi_device *dev,
539 struct comedi_subdevice *s,
540 struct comedi_insn *insn, unsigned int *data)
0c988d00 541{
0c988d00
EW
542 if (data[0]) {
543 s->state &= ~data[0];
544 s->state |= data[0] & data[1];
2c781789 545
0171e6f5 546 outw(s->state, dev->iobase + REG_DIO);
0c988d00
EW
547 }
548
0171e6f5 549 data[1] = inw(dev->iobase + REG_DIO) & 0xff;
0c988d00 550
a2714e3e 551 return insn->n;
0c988d00
EW
552}
553
0a85b6f0
MT
554static int s526_dio_insn_config(struct comedi_device *dev,
555 struct comedi_subdevice *s,
556 struct comedi_insn *insn, unsigned int *data)
0c988d00 557{
43a35276 558 unsigned int chan = CR_CHAN(insn->chanspec);
10f27014 559 int group, mask;
0c988d00 560
10f27014
IA
561 group = chan >> 2;
562 mask = 0xF << (group << 2);
563 switch (data[0]) {
564 case INSN_CONFIG_DIO_OUTPUT:
c9c62f4e
XF
565 /* bit 10/11 set the group 1/2's mode */
566 s->state |= 1 << (group + 10);
10f27014
IA
567 s->io_bits |= mask;
568 break;
569 case INSN_CONFIG_DIO_INPUT:
c9c62f4e 570 s->state &= ~(1 << (group + 10)); /* 1 is output, 0 is input. */
10f27014
IA
571 s->io_bits &= ~mask;
572 break;
573 case INSN_CONFIG_DIO_QUERY:
574 data[1] = (s->io_bits & mask) ? COMEDI_OUTPUT : COMEDI_INPUT;
575 return insn->n;
576 default:
577 return -EINVAL;
0c988d00 578 }
0171e6f5 579 outw(s->state, dev->iobase + REG_DIO);
0c988d00
EW
580
581 return 1;
582}
583
e9a4a7fb
HS
584static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it)
585{
5f221062 586 struct s526_private *devpriv;
e9a4a7fb
HS
587 struct comedi_subdevice *s;
588 int iobase;
8b6c5694 589 int ret;
e9a4a7fb 590
3d9083b2
HS
591 dev->board_name = dev->driver->driver_name;
592
e9a4a7fb 593 iobase = it->options[0];
3d9083b2 594 if (!iobase || !request_region(iobase, S526_IOSIZE, dev->board_name)) {
e9a4a7fb
HS
595 comedi_error(dev, "I/O port conflict");
596 return -EIO;
597 }
598 dev->iobase = iobase;
599
5f221062
HS
600 ret = alloc_private(dev, sizeof(*devpriv));
601 if (ret)
602 return ret;
603 devpriv = dev->private;
e9a4a7fb 604
8b6c5694
HS
605 ret = comedi_alloc_subdevices(dev, 4);
606 if (ret)
607 return ret;
e9a4a7fb 608
97073c05 609 s = &dev->subdevices[0];
e9a4a7fb
HS
610 /* GENERAL-PURPOSE COUNTER/TIME (GPCT) */
611 s->type = COMEDI_SUBD_COUNTER;
612 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL;
3d9083b2 613 s->n_chan = 4;
e9a4a7fb
HS
614 s->maxdata = 0x00ffffff; /* 24 bit counter */
615 s->insn_read = s526_gpct_rinsn;
616 s->insn_config = s526_gpct_insn_config;
617 s->insn_write = s526_gpct_winsn;
618
97073c05 619 s = &dev->subdevices[1];
e9a4a7fb
HS
620 /* analog input subdevice */
621 s->type = COMEDI_SUBD_AI;
e9a4a7fb
HS
622 s->subdev_flags = SDF_READABLE | SDF_DIFF;
623 /* channels 0 to 7 are the regular differential inputs */
624 /* channel 8 is "reference 0" (+10V), channel 9 is "reference 1" (0V) */
625 s->n_chan = 10;
626 s->maxdata = 0xffff;
627 s->range_table = &range_bipolar10;
2c781789 628 s->len_chanlist = 16;
e9a4a7fb
HS
629 s->insn_read = s526_ai_rinsn;
630 s->insn_config = s526_ai_insn_config;
631
97073c05 632 s = &dev->subdevices[2];
e9a4a7fb
HS
633 /* analog output subdevice */
634 s->type = COMEDI_SUBD_AO;
635 s->subdev_flags = SDF_WRITABLE;
636 s->n_chan = 4;
637 s->maxdata = 0xffff;
638 s->range_table = &range_bipolar10;
639 s->insn_write = s526_ao_winsn;
640 s->insn_read = s526_ao_rinsn;
641
97073c05 642 s = &dev->subdevices[3];
e9a4a7fb 643 /* digital i/o subdevice */
3d9083b2
HS
644 s->type = COMEDI_SUBD_DIO;
645 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
646 s->n_chan = 8;
647 s->maxdata = 1;
648 s->range_table = &range_digital;
649 s->insn_bits = s526_dio_insn_bits;
650 s->insn_config = s526_dio_insn_config;
e9a4a7fb 651
d160895f 652 dev_info(dev->class_dev, "%s attached\n", dev->board_name);
e9a4a7fb
HS
653
654 return 1;
e9a4a7fb
HS
655}
656
484ecc95 657static void s526_detach(struct comedi_device *dev)
e9a4a7fb 658{
e9a4a7fb
HS
659 if (dev->iobase > 0)
660 release_region(dev->iobase, S526_IOSIZE);
e9a4a7fb
HS
661}
662
294f930d 663static struct comedi_driver s526_driver = {
e9a4a7fb
HS
664 .driver_name = "s526",
665 .module = THIS_MODULE,
666 .attach = s526_attach,
667 .detach = s526_detach,
e9a4a7fb 668};
294f930d 669module_comedi_driver(s526_driver);
90f703d3
AT
670
671MODULE_AUTHOR("Comedi http://www.comedi.org");
672MODULE_DESCRIPTION("Comedi low-level driver");
673MODULE_LICENSE("GPL");
This page took 0.379794 seconds and 5 git commands to generate.