staging: comedi: s526: remove unreachable code in s526_attach()
[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
86static const int s526_ports[] = {
87 REG_TCR,
88 REG_WDC,
89 REG_DAC,
90 REG_ADC,
91 REG_ADD,
92 REG_DIO,
93 REG_IER,
94 REG_ISR,
95 REG_MSC,
96 REG_C0L,
97 REG_C0H,
98 REG_C0M,
99 REG_C0C,
100 REG_C1L,
101 REG_C1H,
102 REG_C1M,
103 REG_C1C,
104 REG_C2L,
105 REG_C2H,
106 REG_C2M,
107 REG_C2C,
108 REG_C3L,
109 REG_C3H,
110 REG_C3M,
111 REG_C3C,
112 REG_EED,
113 REG_EEC
114};
115
4b1d53f0 116struct counter_mode_register_t {
c9c62f4e 117#if defined(__LITTLE_ENDIAN_BITFIELD)
0c988d00
EW
118 unsigned short coutSource:1;
119 unsigned short coutPolarity:1;
120 unsigned short autoLoadResetRcap:3;
121 unsigned short hwCtEnableSource:2;
122 unsigned short ctEnableCtrl:2;
123 unsigned short clockSource:2;
124 unsigned short countDir:1;
125 unsigned short countDirCtrl:1;
126 unsigned short outputRegLatchCtrl:1;
127 unsigned short preloadRegSel:1;
128 unsigned short reserved:1;
2b0318a6
IA
129 #elif defined(__BIG_ENDIAN_BITFIELD)
130 unsigned short reserved:1;
131 unsigned short preloadRegSel:1;
132 unsigned short outputRegLatchCtrl:1;
133 unsigned short countDirCtrl:1;
134 unsigned short countDir:1;
135 unsigned short clockSource:2;
136 unsigned short ctEnableCtrl:2;
137 unsigned short hwCtEnableSource:2;
138 unsigned short autoLoadResetRcap:3;
139 unsigned short coutPolarity:1;
140 unsigned short coutSource:1;
141#else
142#error Unknown bit field order
143#endif
4b1d53f0 144};
0c988d00 145
ca98ee7b 146union cmReg {
4b1d53f0 147 struct counter_mode_register_t reg;
0c988d00 148 unsigned short value;
ca98ee7b 149};
0c988d00
EW
150
151#define MAX_GPCT_CONFIG_DATA 6
152
153/* Different Application Classes for GPCT Subdevices */
154/* The list is not exhaustive and needs discussion! */
dfb0503e 155enum S526_GPCT_APP_CLASS {
0c988d00
EW
156 CountingAndTimeMeasurement,
157 SinglePulseGeneration,
158 PulseTrainGeneration,
159 PositionMeasurement,
160 Miscellaneous
dfb0503e 161};
0c988d00
EW
162
163/* Config struct for different GPCT subdevice Application Classes and
164 their options
165*/
39f76660 166struct s526GPCTConfig {
dfb0503e 167 enum S526_GPCT_APP_CLASS app;
0c988d00 168 int data[MAX_GPCT_CONFIG_DATA];
39f76660 169};
0c988d00
EW
170
171/*
172 * Board descriptions for two imaginary boards. Describing the
173 * boards in this way is optional, and completely driver-dependent.
174 * Some drivers use arrays such as this, other do not.
175 */
c611ad33 176struct s526_board {
0c988d00
EW
177 const char *name;
178 int gpct_chans;
179 int gpct_bits;
180 int ad_chans;
181 int ad_bits;
182 int da_chans;
183 int da_bits;
184 int have_dio;
c611ad33 185};
0c988d00 186
c611ad33 187static const struct s526_board s526_boards[] = {
0c988d00 188 {
0a85b6f0
MT
189 .name = "s526",
190 .gpct_chans = 4,
191 .gpct_bits = 24,
192 .ad_chans = 8,
193 .ad_bits = 16,
194 .da_chans = 4,
195 .da_bits = 16,
196 .have_dio = 1,
197 }
0c988d00
EW
198};
199
0c988d00
EW
200/* this structure is for data unique to this hardware driver. If
201 several hardware drivers keep similar information in this structure,
c9c62f4e
XF
202 feel free to suggest moving the variable to the struct comedi_device
203 struct.
204*/
6dc1ece0 205struct s526_private {
790c5541 206 unsigned int ao_readback[2];
39f76660 207 struct s526GPCTConfig s526_gpct_config[4];
0c988d00 208 unsigned short s526_ai_config;
6dc1ece0
BP
209};
210
0a85b6f0
MT
211static int s526_gpct_rinsn(struct comedi_device *dev,
212 struct comedi_subdevice *s, struct comedi_insn *insn,
213 unsigned int *data)
0c988d00 214{
232f6502 215 int i; /* counts the Data */
0c988d00
EW
216 int counter_channel = CR_CHAN(insn->chanspec);
217 unsigned short datalow;
218 unsigned short datahigh;
219
232f6502 220 /* Read the low word first */
0c988d00 221 for (i = 0; i < insn->n; i++) {
36fe5d26
HS
222 datalow = inw(dev->iobase + REG_C0L + counter_channel * 8);
223 datahigh = inw(dev->iobase + REG_C0H + counter_channel * 8);
0c988d00
EW
224 data[i] = (int)(datahigh & 0x00FF);
225 data[i] = (data[i] << 16) | (datalow & 0xFFFF);
0c988d00
EW
226 }
227 return i;
228}
229
0a85b6f0
MT
230static int s526_gpct_insn_config(struct comedi_device *dev,
231 struct comedi_subdevice *s,
232 struct comedi_insn *insn, unsigned int *data)
0c988d00 233{
5f221062 234 struct s526_private *devpriv = dev->private;
232f6502 235 int subdev_channel = CR_CHAN(insn->chanspec); /* Unpack chanspec */
0c988d00 236 int i;
790c5541 237 short value;
ca98ee7b 238 union cmReg cmReg;
0c988d00 239
898143bd 240 for (i = 0; i < MAX_GPCT_CONFIG_DATA; i++)
b2abd982 241 devpriv->s526_gpct_config[subdev_channel].data[i] = data[i];
0c988d00 242
232f6502
BP
243 /* Check what type of Counter the user requested, data[0] contains */
244 /* the Application type */
b2abd982 245 switch (data[0]) {
0c988d00
EW
246 case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
247 /*
248 data[0]: Application Type
249 data[1]: Counter Mode Register Value
250 data[2]: Pre-load Register Value
251 data[3]: Conter Control Register
252 */
c9c62f4e 253 printk(KERN_INFO "s526: GPCT_INSN_CONFIG: Configuring Encoder\n");
0c988d00 254 devpriv->s526_gpct_config[subdev_channel].app =
0a85b6f0 255 PositionMeasurement;
0c988d00 256
232f6502 257#if 0
0a85b6f0
MT
258 /* Example of Counter Application */
259 /* One-shot (software trigger) */
260 cmReg.reg.coutSource = 0; /* out RCAP */
261 cmReg.reg.coutPolarity = 1; /* Polarity inverted */
c9c62f4e 262 cmReg.reg.autoLoadResetRcap = 0;/* Auto load disabled */
0a85b6f0
MT
263 cmReg.reg.hwCtEnableSource = 3; /* NOT RCAP */
264 cmReg.reg.ctEnableCtrl = 2; /* Hardware */
265 cmReg.reg.clockSource = 2; /* Internal */
266 cmReg.reg.countDir = 1; /* Down */
267 cmReg.reg.countDirCtrl = 1; /* Software */
268 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
269 cmReg.reg.preloadRegSel = 0; /* PR0 */
270 cmReg.reg.reserved = 0;
0c988d00 271
36fe5d26 272 outw(cmReg.value, dev->iobase + REG_C0M + subdev_channel * 8);
0a85b6f0 273
36fe5d26
HS
274 outw(0x0001, dev->iobase + REG_C0H + subdev_channel * 8);
275 outw(0x3C68, dev->iobase + REG_C0L + subdev_channel * 8);
0a85b6f0 276
c9c62f4e 277 /* Reset the counter */
36fe5d26 278 outw(0x8000, dev->iobase + REG_C0C + subdev_channel * 8);
c9c62f4e 279 /* Load the counter from PR0 */
36fe5d26 280 outw(0x4000, dev->iobase + REG_C0C + subdev_channel * 8);
0c988d00 281
c9c62f4e 282 /* Reset RCAP (fires one-shot) */
36fe5d26 283 outw(0x0008, dev->iobase + REG_C0C + subdev_channel * 8);
0c988d00 284
232f6502 285#endif
0c988d00
EW
286
287#if 1
232f6502 288 /* Set Counter Mode Register */
b2abd982 289 cmReg.value = data[1] & 0xFFFF;
36fe5d26 290 outw(cmReg.value, dev->iobase + REG_C0M + subdev_channel * 8);
0c988d00 291
232f6502 292 /* Reset the counter if it is software preload */
0c988d00 293 if (cmReg.reg.autoLoadResetRcap == 0) {
c9c62f4e 294 /* Reset the counter */
36fe5d26 295 outw(0x8000, dev->iobase + REG_C0C + subdev_channel * 8);
c9c62f4e 296 /* Load the counter from PR0
36fe5d26 297 * outw(0x4000, dev->iobase + REG_C0C + subdev_channel * 8);
c9c62f4e 298 */
0c988d00
EW
299 }
300#else
c9c62f4e
XF
301 /* 0 quadrature, 1 software control */
302 cmReg.reg.countDirCtrl = 0;
0c988d00 303
232f6502 304 /* data[1] contains GPCT_X1, GPCT_X2 or GPCT_X4 */
b2abd982 305 if (data[1] == GPCT_X2)
0c988d00 306 cmReg.reg.clockSource = 1;
b2abd982 307 else if (data[1] == GPCT_X4)
0c988d00 308 cmReg.reg.clockSource = 2;
c9c62f4e 309 else
0c988d00 310 cmReg.reg.clockSource = 0;
0c988d00 311
232f6502 312 /* When to take into account the indexpulse: */
b2abd982
HS
313 /*if (data[2] == GPCT_IndexPhaseLowLow) {
314 } else if (data[2] == GPCT_IndexPhaseLowHigh) {
315 } else if (data[2] == GPCT_IndexPhaseHighLow) {
316 } else if (data[2] == GPCT_IndexPhaseHighHigh) {
c9c62f4e 317 }*/
232f6502 318 /* Take into account the index pulse? */
b2abd982 319 if (data[3] == GPCT_RESET_COUNTER_ON_INDEX)
c9c62f4e
XF
320 /* Auto load with INDEX^ */
321 cmReg.reg.autoLoadResetRcap = 4;
0c988d00 322
232f6502 323 /* Set Counter Mode Register */
b2abd982 324 cmReg.value = (short)(data[1] & 0xFFFF);
36fe5d26 325 outw(cmReg.value, dev->iobase + REG_C0M + subdev_channel * 8);
0c988d00 326
5044a2c0 327 /* Load the pre-load register high word */
b2abd982 328 value = (short)((data[2] >> 16) & 0xFFFF);
36fe5d26 329 outw(value, dev->iobase + REG_C0H + subdev_channel * 8);
0c988d00 330
5044a2c0 331 /* Load the pre-load register low word */
b2abd982 332 value = (short)(data[2] & 0xFFFF);
36fe5d26 333 outw(value, dev->iobase + REG_C0L + subdev_channel * 8);
0c988d00 334
232f6502 335 /* Write the Counter Control Register */
b2abd982
HS
336 if (data[3] != 0) {
337 value = (short)(data[3] & 0xFFFF);
36fe5d26 338 outw(value, dev->iobase + REG_C0C + subdev_channel * 8);
0c988d00 339 }
232f6502 340 /* Reset the counter if it is software preload */
0c988d00 341 if (cmReg.reg.autoLoadResetRcap == 0) {
c9c62f4e 342 /* Reset the counter */
36fe5d26 343 outw(0x8000, dev->iobase + REG_C0C + subdev_channel * 8);
c9c62f4e 344 /* Load the counter from PR0 */
36fe5d26 345 outw(0x4000, dev->iobase + REG_C0C + subdev_channel * 8);
0c988d00
EW
346 }
347#endif
348 break;
349
350 case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
351 /*
352 data[0]: Application Type
353 data[1]: Counter Mode Register Value
354 data[2]: Pre-load Register 0 Value
355 data[3]: Pre-load Register 1 Value
356 data[4]: Conter Control Register
357 */
c9c62f4e 358 printk(KERN_INFO "s526: GPCT_INSN_CONFIG: Configuring SPG\n");
0c988d00 359 devpriv->s526_gpct_config[subdev_channel].app =
0a85b6f0 360 SinglePulseGeneration;
0c988d00 361
232f6502 362 /* Set Counter Mode Register */
b2abd982 363 cmReg.value = (short)(data[1] & 0xFFFF);
232f6502 364 cmReg.reg.preloadRegSel = 0; /* PR0 */
36fe5d26 365 outw(cmReg.value, dev->iobase + REG_C0M + subdev_channel * 8);
0c988d00 366
5044a2c0 367 /* Load the pre-load register 0 high word */
b2abd982 368 value = (short)((data[2] >> 16) & 0xFFFF);
36fe5d26 369 outw(value, dev->iobase + REG_C0H + subdev_channel * 8);
0c988d00 370
5044a2c0 371 /* Load the pre-load register 0 low word */
b2abd982 372 value = (short)(data[2] & 0xFFFF);
36fe5d26 373 outw(value, dev->iobase + REG_C0L + subdev_channel * 8);
0c988d00 374
232f6502 375 /* Set Counter Mode Register */
b2abd982 376 cmReg.value = (short)(data[1] & 0xFFFF);
232f6502 377 cmReg.reg.preloadRegSel = 1; /* PR1 */
36fe5d26 378 outw(cmReg.value, dev->iobase + REG_C0M + subdev_channel * 8);
0c988d00 379
5044a2c0 380 /* Load the pre-load register 1 high word */
b2abd982 381 value = (short)((data[3] >> 16) & 0xFFFF);
36fe5d26 382 outw(value, dev->iobase + REG_C0H + subdev_channel * 8);
0c988d00 383
5044a2c0 384 /* Load the pre-load register 1 low word */
b2abd982 385 value = (short)(data[3] & 0xFFFF);
36fe5d26 386 outw(value, dev->iobase + REG_C0L + subdev_channel * 8);
0c988d00 387
232f6502 388 /* Write the Counter Control Register */
b2abd982
HS
389 if (data[4] != 0) {
390 value = (short)(data[4] & 0xFFFF);
36fe5d26 391 outw(value, dev->iobase + REG_C0C + subdev_channel * 8);
0c988d00
EW
392 }
393 break;
394
395 case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
396 /*
397 data[0]: Application Type
398 data[1]: Counter Mode Register Value
399 data[2]: Pre-load Register 0 Value
400 data[3]: Pre-load Register 1 Value
401 data[4]: Conter Control Register
402 */
c9c62f4e 403 printk(KERN_INFO "s526: GPCT_INSN_CONFIG: Configuring PTG\n");
0c988d00 404 devpriv->s526_gpct_config[subdev_channel].app =
0a85b6f0 405 PulseTrainGeneration;
0c988d00 406
232f6502 407 /* Set Counter Mode Register */
b2abd982 408 cmReg.value = (short)(data[1] & 0xFFFF);
232f6502 409 cmReg.reg.preloadRegSel = 0; /* PR0 */
36fe5d26 410 outw(cmReg.value, dev->iobase + REG_C0M + subdev_channel * 8);
0c988d00 411
5044a2c0 412 /* Load the pre-load register 0 high word */
b2abd982 413 value = (short)((data[2] >> 16) & 0xFFFF);
36fe5d26 414 outw(value, dev->iobase + REG_C0H + subdev_channel * 8);
0c988d00 415
5044a2c0 416 /* Load the pre-load register 0 low word */
b2abd982 417 value = (short)(data[2] & 0xFFFF);
36fe5d26 418 outw(value, dev->iobase + REG_C0L + subdev_channel * 8);
0c988d00 419
232f6502 420 /* Set Counter Mode Register */
b2abd982 421 cmReg.value = (short)(data[1] & 0xFFFF);
232f6502 422 cmReg.reg.preloadRegSel = 1; /* PR1 */
36fe5d26 423 outw(cmReg.value, dev->iobase + REG_C0M + subdev_channel * 8);
0c988d00 424
5044a2c0 425 /* Load the pre-load register 1 high word */
b2abd982 426 value = (short)((data[3] >> 16) & 0xFFFF);
36fe5d26 427 outw(value, dev->iobase + REG_C0H + subdev_channel * 8);
0c988d00 428
5044a2c0 429 /* Load the pre-load register 1 low word */
b2abd982 430 value = (short)(data[3] & 0xFFFF);
36fe5d26 431 outw(value, dev->iobase + REG_C0L + subdev_channel * 8);
0c988d00 432
232f6502 433 /* Write the Counter Control Register */
b2abd982
HS
434 if (data[4] != 0) {
435 value = (short)(data[4] & 0xFFFF);
36fe5d26 436 outw(value, dev->iobase + REG_C0C + subdev_channel * 8);
0c988d00
EW
437 }
438 break;
439
440 default:
c9c62f4e 441 printk(KERN_ERR "s526: unsupported GPCT_insn_config\n");
0c988d00
EW
442 return -EINVAL;
443 break;
444 }
445
446 return insn->n;
447}
448
0a85b6f0
MT
449static int s526_gpct_winsn(struct comedi_device *dev,
450 struct comedi_subdevice *s, struct comedi_insn *insn,
451 unsigned int *data)
0c988d00 452{
5f221062 453 struct s526_private *devpriv = dev->private;
232f6502 454 int subdev_channel = CR_CHAN(insn->chanspec); /* Unpack chanspec */
790c5541 455 short value;
ca98ee7b 456 union cmReg cmReg;
0c988d00 457
c9c62f4e
XF
458 printk(KERN_INFO "s526: GPCT_INSN_WRITE on channel %d\n",
459 subdev_channel);
36fe5d26 460 cmReg.value = inw(dev->iobase + REG_C0M + subdev_channel * 8);
c9c62f4e 461 printk(KERN_INFO "s526: Counter Mode Register: %x\n", cmReg.value);
232f6502 462 /* Check what Application of Counter this channel is configured for */
0c988d00
EW
463 switch (devpriv->s526_gpct_config[subdev_channel].app) {
464 case PositionMeasurement:
c9c62f4e 465 printk(KERN_INFO "S526: INSN_WRITE: PM\n");
36fe5d26
HS
466 outw(0xFFFF & ((*data) >> 16), dev->iobase + REG_C0H +
467 subdev_channel * 8);
468 outw(0xFFFF & (*data), dev->iobase + REG_C0L + subdev_channel * 8);
0c988d00
EW
469 break;
470
471 case SinglePulseGeneration:
c9c62f4e 472 printk(KERN_INFO "S526: INSN_WRITE: SPG\n");
36fe5d26
HS
473 outw(0xFFFF & ((*data) >> 16), dev->iobase + REG_C0H +
474 subdev_channel * 8);
475 outw(0xFFFF & (*data), dev->iobase + REG_C0L + subdev_channel * 8);
0c988d00
EW
476 break;
477
478 case PulseTrainGeneration:
479 /* data[0] contains the PULSE_WIDTH
480 data[1] contains the PULSE_PERIOD
481 @pre PULSE_PERIOD > PULSE_WIDTH > 0
482 The above periods must be expressed as a multiple of the
483 pulse frequency on the selected source
484 */
c9c62f4e 485 printk(KERN_INFO "S526: INSN_WRITE: PTG\n");
b2abd982 486 if ((data[1] > data[0]) && (data[0] > 0)) {
0c988d00 487 (devpriv->s526_gpct_config[subdev_channel]).data[0] =
b2abd982 488 data[0];
0c988d00 489 (devpriv->s526_gpct_config[subdev_channel]).data[1] =
b2abd982 490 data[1];
0c988d00 491 } else {
c9c62f4e 492 printk(KERN_ERR "s526: INSN_WRITE: PTG: Problem with Pulse params -> %d %d\n",
b2abd982 493 data[0], data[1]);
0c988d00
EW
494 return -EINVAL;
495 }
496
0a85b6f0 497 value = (short)((*data >> 16) & 0xFFFF);
36fe5d26 498 outw(value, dev->iobase + REG_C0H + subdev_channel * 8);
0a85b6f0 499 value = (short)(*data & 0xFFFF);
36fe5d26 500 outw(value, dev->iobase + REG_C0L + subdev_channel * 8);
0c988d00 501 break;
232f6502 502 default: /* Impossible */
0a85b6f0
MT
503 printk
504 ("s526: INSN_WRITE: Functionality %d not implemented yet\n",
505 devpriv->s526_gpct_config[subdev_channel].app);
0c988d00
EW
506 return -EINVAL;
507 break;
508 }
232f6502 509 /* return the number of samples written */
0c988d00
EW
510 return insn->n;
511}
512
513#define ISR_ADC_DONE 0x4
0a85b6f0
MT
514static int s526_ai_insn_config(struct comedi_device *dev,
515 struct comedi_subdevice *s,
516 struct comedi_insn *insn, unsigned int *data)
0c988d00 517{
5f221062 518 struct s526_private *devpriv = dev->private;
0c988d00
EW
519 int result = -EINVAL;
520
521 if (insn->n < 1)
522 return result;
523
524 result = insn->n;
525
526 /* data[0] : channels was set in relevant bits.
527 data[1] : delay
528 */
529 /* COMMENT: abbotti 2008-07-24: I don't know why you'd want to
530 * enable channels here. The channel should be enabled in the
531 * INSN_READ handler. */
532
232f6502 533 /* Enable ADC interrupt */
0171e6f5 534 outw(ISR_ADC_DONE, dev->iobase + REG_IER);
0c988d00
EW
535 devpriv->s526_ai_config = (data[0] & 0x3FF) << 5;
536 if (data[1] > 0)
232f6502 537 devpriv->s526_ai_config |= 0x8000; /* set the delay */
0c988d00 538
232f6502 539 devpriv->s526_ai_config |= 0x0001; /* ADC start bit. */
0c988d00
EW
540
541 return result;
542}
543
544/*
545 * "instructions" read/write data in "one-shot" or "software-triggered"
546 * mode.
547 */
da91b269 548static int s526_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 549 struct comedi_insn *insn, unsigned int *data)
0c988d00 550{
5f221062 551 struct s526_private *devpriv = dev->private;
0c988d00
EW
552 int n, i;
553 int chan = CR_CHAN(insn->chanspec);
554 unsigned short value;
555 unsigned int d;
556 unsigned int status;
557
558 /* Set configured delay, enable channel for this channel only,
559 * select "ADC read" channel, set "ADC start" bit. */
560 value = (devpriv->s526_ai_config & 0x8000) |
0a85b6f0 561 ((1 << 5) << chan) | (chan << 1) | 0x0001;
0c988d00
EW
562
563 /* convert n samples */
564 for (n = 0; n < insn->n; n++) {
565 /* trigger conversion */
0171e6f5 566 outw(value, dev->iobase + REG_ADC);
0c988d00
EW
567
568#define TIMEOUT 100
569 /* wait for conversion to end */
570 for (i = 0; i < TIMEOUT; i++) {
0171e6f5 571 status = inw(dev->iobase + REG_ISR);
0c988d00 572 if (status & ISR_ADC_DONE) {
0171e6f5 573 outw(ISR_ADC_DONE, dev->iobase + REG_ISR);
0c988d00
EW
574 break;
575 }
576 }
577 if (i == TIMEOUT) {
5f74ea14 578 /* printk() should be used instead of printk()
0c988d00 579 * whenever the code can be called from real-time. */
c9c62f4e 580 printk(KERN_ERR "s526: ADC(0x%04x) timeout\n",
0171e6f5 581 inw(dev->iobase + REG_ISR));
0c988d00
EW
582 return -ETIMEDOUT;
583 }
584
585 /* read data */
0171e6f5 586 d = inw(dev->iobase + REG_ADD);
0c988d00
EW
587
588 /* munge data */
589 data[n] = d ^ 0x8000;
590 }
591
592 /* return the number of samples read/written */
593 return n;
594}
595
da91b269 596static int s526_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 597 struct comedi_insn *insn, unsigned int *data)
0c988d00 598{
5f221062 599 struct s526_private *devpriv = dev->private;
0c988d00
EW
600 int i;
601 int chan = CR_CHAN(insn->chanspec);
602 unsigned short val;
603
0c988d00 604 val = chan << 1;
232f6502 605/* outw(val, dev->iobase + REG_DAC); */
0171e6f5 606 outw(val, dev->iobase + REG_DAC);
0c988d00
EW
607
608 /* Writing a list of values to an AO channel is probably not
609 * very useful, but that's how the interface is defined. */
610 for (i = 0; i < insn->n; i++) {
611 /* a typical programming sequence */
c9c62f4e
XF
612 /* write the data to preload register
613 * outw(data[i], dev->iobase + REG_ADD);
614 */
615 /* write the data to preload register */
0171e6f5 616 outw(data[i], dev->iobase + REG_ADD);
0c988d00 617 devpriv->ao_readback[chan] = data[i];
232f6502 618/* outw(val + 1, dev->iobase + REG_DAC); starts the D/A conversion. */
0171e6f5
HS
619 /* starts the D/A conversion */
620 outw(val + 1, dev->iobase + REG_DAC);
0c988d00
EW
621 }
622
623 /* return the number of samples read/written */
624 return i;
625}
626
627/* AO subdevices should have a read insn as well as a write insn.
628 * Usually this means copying a value stored in devpriv. */
da91b269 629static int s526_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 630 struct comedi_insn *insn, unsigned int *data)
0c988d00 631{
5f221062 632 struct s526_private *devpriv = dev->private;
0c988d00
EW
633 int i;
634 int chan = CR_CHAN(insn->chanspec);
635
636 for (i = 0; i < insn->n; i++)
637 data[i] = devpriv->ao_readback[chan];
638
639 return i;
640}
641
642/* DIO devices are slightly special. Although it is possible to
643 * implement the insn_read/insn_write interface, it is much more
644 * useful to applications if you implement the insn_bits interface.
645 * This allows packed reading/writing of the DIO channels. The
646 * comedi core can convert between insn_bits and insn_read/write */
0a85b6f0
MT
647static int s526_dio_insn_bits(struct comedi_device *dev,
648 struct comedi_subdevice *s,
649 struct comedi_insn *insn, unsigned int *data)
0c988d00 650{
0c988d00
EW
651 /* The insn data is a mask in data[0] and the new data
652 * in data[1], each channel cooresponding to a bit. */
653 if (data[0]) {
654 s->state &= ~data[0];
655 s->state |= data[0] & data[1];
656 /* Write out the new digital output lines */
0171e6f5 657 outw(s->state, dev->iobase + REG_DIO);
0c988d00
EW
658 }
659
660 /* on return, data[1] contains the value of the digital
661 * input and output lines. */
0171e6f5 662 data[1] = inw(dev->iobase + REG_DIO) & 0xff;
0c988d00
EW
663 /* or we could just return the software copy of the output values if
664 * it was a purely digital output subdevice */
10f27014 665 /* data[1]=s->state & 0xFF; */
0c988d00 666
a2714e3e 667 return insn->n;
0c988d00
EW
668}
669
0a85b6f0
MT
670static int s526_dio_insn_config(struct comedi_device *dev,
671 struct comedi_subdevice *s,
672 struct comedi_insn *insn, unsigned int *data)
0c988d00
EW
673{
674 int chan = CR_CHAN(insn->chanspec);
10f27014 675 int group, mask;
0c988d00 676
c9c62f4e 677 printk(KERN_INFO "S526 DIO insn_config\n");
0c988d00 678
0c988d00
EW
679 /* The input or output configuration of each digital line is
680 * configured by a special insn_config instruction. chanspec
681 * contains the channel to be changed, and data[0] contains the
682 * value COMEDI_INPUT or COMEDI_OUTPUT. */
683
10f27014
IA
684 group = chan >> 2;
685 mask = 0xF << (group << 2);
686 switch (data[0]) {
687 case INSN_CONFIG_DIO_OUTPUT:
c9c62f4e
XF
688 /* bit 10/11 set the group 1/2's mode */
689 s->state |= 1 << (group + 10);
10f27014
IA
690 s->io_bits |= mask;
691 break;
692 case INSN_CONFIG_DIO_INPUT:
c9c62f4e 693 s->state &= ~(1 << (group + 10)); /* 1 is output, 0 is input. */
10f27014
IA
694 s->io_bits &= ~mask;
695 break;
696 case INSN_CONFIG_DIO_QUERY:
697 data[1] = (s->io_bits & mask) ? COMEDI_OUTPUT : COMEDI_INPUT;
698 return insn->n;
699 default:
700 return -EINVAL;
0c988d00 701 }
0171e6f5 702 outw(s->state, dev->iobase + REG_DIO);
0c988d00
EW
703
704 return 1;
705}
706
e9a4a7fb
HS
707static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it)
708{
5254cbe7 709 const struct s526_board *board = comedi_board(dev);
5f221062 710 struct s526_private *devpriv;
e9a4a7fb
HS
711 struct comedi_subdevice *s;
712 int iobase;
8b6c5694 713 int ret;
e9a4a7fb
HS
714
715 printk(KERN_INFO "comedi%d: s526: ", dev->minor);
716
717 iobase = it->options[0];
5254cbe7 718 if (!iobase || !request_region(iobase, S526_IOSIZE, board->name)) {
e9a4a7fb
HS
719 comedi_error(dev, "I/O port conflict");
720 return -EIO;
721 }
722 dev->iobase = iobase;
723
724 printk("iobase=0x%lx\n", dev->iobase);
725
5254cbe7 726 dev->board_name = board->name;
e9a4a7fb 727
5f221062
HS
728 ret = alloc_private(dev, sizeof(*devpriv));
729 if (ret)
730 return ret;
731 devpriv = dev->private;
e9a4a7fb 732
8b6c5694
HS
733 ret = comedi_alloc_subdevices(dev, 4);
734 if (ret)
735 return ret;
e9a4a7fb 736
97073c05 737 s = &dev->subdevices[0];
e9a4a7fb
HS
738 /* GENERAL-PURPOSE COUNTER/TIME (GPCT) */
739 s->type = COMEDI_SUBD_COUNTER;
740 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL;
741 /* KG: What does SDF_LSAMPL (see multiq3.c) mean? */
5254cbe7 742 s->n_chan = board->gpct_chans;
e9a4a7fb
HS
743 s->maxdata = 0x00ffffff; /* 24 bit counter */
744 s->insn_read = s526_gpct_rinsn;
745 s->insn_config = s526_gpct_insn_config;
746 s->insn_write = s526_gpct_winsn;
747
748 /* Command are not implemented yet, however they are necessary to
749 allocate the necessary memory for the comedi_async struct (used
750 to trigger the GPCT in case of pulsegenerator function */
751 /* s->do_cmd = s526_gpct_cmd; */
752 /* s->do_cmdtest = s526_gpct_cmdtest; */
753 /* s->cancel = s526_gpct_cancel; */
754
97073c05 755 s = &dev->subdevices[1];
e9a4a7fb
HS
756 /* dev->read_subdev=s; */
757 /* analog input subdevice */
758 s->type = COMEDI_SUBD_AI;
759 /* we support differential */
760 s->subdev_flags = SDF_READABLE | SDF_DIFF;
761 /* channels 0 to 7 are the regular differential inputs */
762 /* channel 8 is "reference 0" (+10V), channel 9 is "reference 1" (0V) */
763 s->n_chan = 10;
764 s->maxdata = 0xffff;
765 s->range_table = &range_bipolar10;
766 s->len_chanlist = 16; /* This is the maximum chanlist length that
767 the board can handle */
768 s->insn_read = s526_ai_rinsn;
769 s->insn_config = s526_ai_insn_config;
770
97073c05 771 s = &dev->subdevices[2];
e9a4a7fb
HS
772 /* analog output subdevice */
773 s->type = COMEDI_SUBD_AO;
774 s->subdev_flags = SDF_WRITABLE;
775 s->n_chan = 4;
776 s->maxdata = 0xffff;
777 s->range_table = &range_bipolar10;
778 s->insn_write = s526_ao_winsn;
779 s->insn_read = s526_ao_rinsn;
780
97073c05 781 s = &dev->subdevices[3];
e9a4a7fb 782 /* digital i/o subdevice */
5254cbe7 783 if (board->have_dio) {
e9a4a7fb
HS
784 s->type = COMEDI_SUBD_DIO;
785 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
786 s->n_chan = 8;
787 s->maxdata = 1;
788 s->range_table = &range_digital;
789 s->insn_bits = s526_dio_insn_bits;
790 s->insn_config = s526_dio_insn_config;
791 } else {
792 s->type = COMEDI_SUBD_UNUSED;
793 }
794
795 printk(KERN_INFO "attached\n");
796
797 return 1;
e9a4a7fb
HS
798}
799
484ecc95 800static void s526_detach(struct comedi_device *dev)
e9a4a7fb 801{
e9a4a7fb
HS
802 if (dev->iobase > 0)
803 release_region(dev->iobase, S526_IOSIZE);
e9a4a7fb
HS
804}
805
294f930d 806static struct comedi_driver s526_driver = {
e9a4a7fb
HS
807 .driver_name = "s526",
808 .module = THIS_MODULE,
809 .attach = s526_attach,
810 .detach = s526_detach,
811 .board_name = &s526_boards[0].name,
812 .offset = sizeof(struct s526_board),
813 .num_names = ARRAY_SIZE(s526_boards),
814};
294f930d 815module_comedi_driver(s526_driver);
90f703d3
AT
816
817MODULE_AUTHOR("Comedi http://www.comedi.org");
818MODULE_DESCRIPTION("Comedi low-level driver");
819MODULE_LICENSE("GPL");
This page took 0.401762 seconds and 5 git commands to generate.