staging: comedi: s526: don't dereference insn->data pointer
[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
200#define ADDR_REG(reg) (dev->iobase + (reg))
201#define ADDR_CHAN_REG(reg, chan) (dev->iobase + (reg) + (chan) * 8)
202
0c988d00
EW
203/* this structure is for data unique to this hardware driver. If
204 several hardware drivers keep similar information in this structure,
c9c62f4e
XF
205 feel free to suggest moving the variable to the struct comedi_device
206 struct.
207*/
6dc1ece0 208struct s526_private {
790c5541 209 unsigned int ao_readback[2];
39f76660 210 struct s526GPCTConfig s526_gpct_config[4];
0c988d00 211 unsigned short s526_ai_config;
6dc1ece0
BP
212};
213
0c988d00
EW
214/*
215 * most drivers define the following macro to make it easy to
216 * access the private structure.
217 */
6dc1ece0 218#define devpriv ((struct s526_private *)dev->private)
0c988d00 219
0a85b6f0
MT
220static int s526_gpct_rinsn(struct comedi_device *dev,
221 struct comedi_subdevice *s, struct comedi_insn *insn,
222 unsigned int *data)
0c988d00 223{
232f6502 224 int i; /* counts the Data */
0c988d00
EW
225 int counter_channel = CR_CHAN(insn->chanspec);
226 unsigned short datalow;
227 unsigned short datahigh;
228
232f6502 229 /* Check if (n > 0) */
0c988d00 230 if (insn->n <= 0) {
c9c62f4e 231 printk(KERN_ERR "s526: INSN_READ: n should be > 0\n");
0c988d00
EW
232 return -EINVAL;
233 }
232f6502 234 /* Read the low word first */
0c988d00
EW
235 for (i = 0; i < insn->n; i++) {
236 datalow = inw(ADDR_CHAN_REG(REG_C0L, counter_channel));
237 datahigh = inw(ADDR_CHAN_REG(REG_C0H, counter_channel));
238 data[i] = (int)(datahigh & 0x00FF);
239 data[i] = (data[i] << 16) | (datalow & 0xFFFF);
c9c62f4e
XF
240 /* printk("s526 GPCT[%d]: %x(0x%04x, 0x%04x)\n",
241 counter_channel, data[i], datahigh, datalow); */
0c988d00
EW
242 }
243 return i;
244}
245
0a85b6f0
MT
246static int s526_gpct_insn_config(struct comedi_device *dev,
247 struct comedi_subdevice *s,
248 struct comedi_insn *insn, unsigned int *data)
0c988d00 249{
232f6502 250 int subdev_channel = CR_CHAN(insn->chanspec); /* Unpack chanspec */
0c988d00 251 int i;
790c5541 252 short value;
ca98ee7b 253 union cmReg cmReg;
0c988d00 254
c9c62f4e
XF
255 /* printk("s526: GPCT_INSN_CONFIG: Configuring Channel %d\n",
256 subdev_channel); */
0c988d00
EW
257
258 for (i = 0; i < MAX_GPCT_CONFIG_DATA; i++) {
b2abd982
HS
259 devpriv->s526_gpct_config[subdev_channel].data[i] = data[i];
260/* printk("data[%d]=%x\n", i, data[i]); */
0c988d00
EW
261 }
262
232f6502
BP
263 /* Check what type of Counter the user requested, data[0] contains */
264 /* the Application type */
b2abd982 265 switch (data[0]) {
0c988d00
EW
266 case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
267 /*
268 data[0]: Application Type
269 data[1]: Counter Mode Register Value
270 data[2]: Pre-load Register Value
271 data[3]: Conter Control Register
272 */
c9c62f4e 273 printk(KERN_INFO "s526: GPCT_INSN_CONFIG: Configuring Encoder\n");
0c988d00 274 devpriv->s526_gpct_config[subdev_channel].app =
0a85b6f0 275 PositionMeasurement;
0c988d00 276
232f6502 277#if 0
0a85b6f0
MT
278 /* Example of Counter Application */
279 /* One-shot (software trigger) */
280 cmReg.reg.coutSource = 0; /* out RCAP */
281 cmReg.reg.coutPolarity = 1; /* Polarity inverted */
c9c62f4e 282 cmReg.reg.autoLoadResetRcap = 0;/* Auto load disabled */
0a85b6f0
MT
283 cmReg.reg.hwCtEnableSource = 3; /* NOT RCAP */
284 cmReg.reg.ctEnableCtrl = 2; /* Hardware */
285 cmReg.reg.clockSource = 2; /* Internal */
286 cmReg.reg.countDir = 1; /* Down */
287 cmReg.reg.countDirCtrl = 1; /* Software */
288 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
289 cmReg.reg.preloadRegSel = 0; /* PR0 */
290 cmReg.reg.reserved = 0;
0c988d00 291
0a85b6f0
MT
292 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
293
294 outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel));
295 outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel));
296
c9c62f4e
XF
297 /* Reset the counter */
298 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
299 /* Load the counter from PR0 */
300 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
0c988d00 301
c9c62f4e
XF
302 /* Reset RCAP (fires one-shot) */
303 outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel));
0c988d00 304
232f6502 305#endif
0c988d00
EW
306
307#if 1
232f6502 308 /* Set Counter Mode Register */
b2abd982 309 cmReg.value = data[1] & 0xFFFF;
0c988d00 310
232f6502 311/* printk("s526: Counter Mode register=%x\n", cmReg.value); */
0c988d00
EW
312 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
313
232f6502 314 /* Reset the counter if it is software preload */
0c988d00 315 if (cmReg.reg.autoLoadResetRcap == 0) {
c9c62f4e
XF
316 /* Reset the counter */
317 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
318 /* Load the counter from PR0
319 * outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
320 */
0c988d00
EW
321 }
322#else
c9c62f4e
XF
323 /* 0 quadrature, 1 software control */
324 cmReg.reg.countDirCtrl = 0;
0c988d00 325
232f6502 326 /* data[1] contains GPCT_X1, GPCT_X2 or GPCT_X4 */
b2abd982 327 if (data[1] == GPCT_X2)
0c988d00 328 cmReg.reg.clockSource = 1;
b2abd982 329 else if (data[1] == GPCT_X4)
0c988d00 330 cmReg.reg.clockSource = 2;
c9c62f4e 331 else
0c988d00 332 cmReg.reg.clockSource = 0;
0c988d00 333
232f6502 334 /* When to take into account the indexpulse: */
b2abd982
HS
335 /*if (data[2] == GPCT_IndexPhaseLowLow) {
336 } else if (data[2] == GPCT_IndexPhaseLowHigh) {
337 } else if (data[2] == GPCT_IndexPhaseHighLow) {
338 } else if (data[2] == GPCT_IndexPhaseHighHigh) {
c9c62f4e 339 }*/
232f6502 340 /* Take into account the index pulse? */
b2abd982 341 if (data[3] == GPCT_RESET_COUNTER_ON_INDEX)
c9c62f4e
XF
342 /* Auto load with INDEX^ */
343 cmReg.reg.autoLoadResetRcap = 4;
0c988d00 344
232f6502 345 /* Set Counter Mode Register */
b2abd982 346 cmReg.value = (short)(data[1] & 0xFFFF);
0c988d00
EW
347 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
348
5044a2c0 349 /* Load the pre-load register high word */
b2abd982 350 value = (short)((data[2] >> 16) & 0xFFFF);
0c988d00
EW
351 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
352
5044a2c0 353 /* Load the pre-load register low word */
b2abd982 354 value = (short)(data[2] & 0xFFFF);
0c988d00
EW
355 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
356
232f6502 357 /* Write the Counter Control Register */
b2abd982
HS
358 if (data[3] != 0) {
359 value = (short)(data[3] & 0xFFFF);
0c988d00
EW
360 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
361 }
232f6502 362 /* Reset the counter if it is software preload */
0c988d00 363 if (cmReg.reg.autoLoadResetRcap == 0) {
c9c62f4e
XF
364 /* Reset the counter */
365 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
366 /* Load the counter from PR0 */
367 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
0c988d00
EW
368 }
369#endif
370 break;
371
372 case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
373 /*
374 data[0]: Application Type
375 data[1]: Counter Mode Register Value
376 data[2]: Pre-load Register 0 Value
377 data[3]: Pre-load Register 1 Value
378 data[4]: Conter Control Register
379 */
c9c62f4e 380 printk(KERN_INFO "s526: GPCT_INSN_CONFIG: Configuring SPG\n");
0c988d00 381 devpriv->s526_gpct_config[subdev_channel].app =
0a85b6f0 382 SinglePulseGeneration;
0c988d00 383
232f6502 384 /* Set Counter Mode Register */
b2abd982 385 cmReg.value = (short)(data[1] & 0xFFFF);
232f6502 386 cmReg.reg.preloadRegSel = 0; /* PR0 */
0c988d00
EW
387 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
388
5044a2c0 389 /* Load the pre-load register 0 high word */
b2abd982 390 value = (short)((data[2] >> 16) & 0xFFFF);
0c988d00
EW
391 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
392
5044a2c0 393 /* Load the pre-load register 0 low word */
b2abd982 394 value = (short)(data[2] & 0xFFFF);
0c988d00
EW
395 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
396
232f6502 397 /* Set Counter Mode Register */
b2abd982 398 cmReg.value = (short)(data[1] & 0xFFFF);
232f6502 399 cmReg.reg.preloadRegSel = 1; /* PR1 */
0c988d00
EW
400 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
401
5044a2c0 402 /* Load the pre-load register 1 high word */
b2abd982 403 value = (short)((data[3] >> 16) & 0xFFFF);
0c988d00
EW
404 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
405
5044a2c0 406 /* Load the pre-load register 1 low word */
b2abd982 407 value = (short)(data[3] & 0xFFFF);
0c988d00
EW
408 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
409
232f6502 410 /* Write the Counter Control Register */
b2abd982
HS
411 if (data[4] != 0) {
412 value = (short)(data[4] & 0xFFFF);
0c988d00
EW
413 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
414 }
415 break;
416
417 case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
418 /*
419 data[0]: Application Type
420 data[1]: Counter Mode Register Value
421 data[2]: Pre-load Register 0 Value
422 data[3]: Pre-load Register 1 Value
423 data[4]: Conter Control Register
424 */
c9c62f4e 425 printk(KERN_INFO "s526: GPCT_INSN_CONFIG: Configuring PTG\n");
0c988d00 426 devpriv->s526_gpct_config[subdev_channel].app =
0a85b6f0 427 PulseTrainGeneration;
0c988d00 428
232f6502 429 /* Set Counter Mode Register */
b2abd982 430 cmReg.value = (short)(data[1] & 0xFFFF);
232f6502 431 cmReg.reg.preloadRegSel = 0; /* PR0 */
0c988d00
EW
432 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
433
5044a2c0 434 /* Load the pre-load register 0 high word */
b2abd982 435 value = (short)((data[2] >> 16) & 0xFFFF);
0c988d00
EW
436 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
437
5044a2c0 438 /* Load the pre-load register 0 low word */
b2abd982 439 value = (short)(data[2] & 0xFFFF);
0c988d00
EW
440 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
441
232f6502 442 /* Set Counter Mode Register */
b2abd982 443 cmReg.value = (short)(data[1] & 0xFFFF);
232f6502 444 cmReg.reg.preloadRegSel = 1; /* PR1 */
0c988d00
EW
445 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
446
5044a2c0 447 /* Load the pre-load register 1 high word */
b2abd982 448 value = (short)((data[3] >> 16) & 0xFFFF);
0c988d00
EW
449 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
450
5044a2c0 451 /* Load the pre-load register 1 low word */
b2abd982 452 value = (short)(data[3] & 0xFFFF);
0c988d00
EW
453 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
454
232f6502 455 /* Write the Counter Control Register */
b2abd982
HS
456 if (data[4] != 0) {
457 value = (short)(data[4] & 0xFFFF);
0c988d00
EW
458 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
459 }
460 break;
461
462 default:
c9c62f4e 463 printk(KERN_ERR "s526: unsupported GPCT_insn_config\n");
0c988d00
EW
464 return -EINVAL;
465 break;
466 }
467
468 return insn->n;
469}
470
0a85b6f0
MT
471static int s526_gpct_winsn(struct comedi_device *dev,
472 struct comedi_subdevice *s, struct comedi_insn *insn,
473 unsigned int *data)
0c988d00 474{
232f6502 475 int subdev_channel = CR_CHAN(insn->chanspec); /* Unpack chanspec */
790c5541 476 short value;
ca98ee7b 477 union cmReg cmReg;
0c988d00 478
c9c62f4e
XF
479 printk(KERN_INFO "s526: GPCT_INSN_WRITE on channel %d\n",
480 subdev_channel);
0c988d00 481 cmReg.value = inw(ADDR_CHAN_REG(REG_C0M, subdev_channel));
c9c62f4e 482 printk(KERN_INFO "s526: Counter Mode Register: %x\n", cmReg.value);
232f6502 483 /* Check what Application of Counter this channel is configured for */
0c988d00
EW
484 switch (devpriv->s526_gpct_config[subdev_channel].app) {
485 case PositionMeasurement:
c9c62f4e 486 printk(KERN_INFO "S526: INSN_WRITE: PM\n");
0c988d00 487 outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H,
0a85b6f0 488 subdev_channel));
0c988d00
EW
489 outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel));
490 break;
491
492 case SinglePulseGeneration:
c9c62f4e 493 printk(KERN_INFO "S526: INSN_WRITE: SPG\n");
0c988d00 494 outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H,
0a85b6f0 495 subdev_channel));
0c988d00
EW
496 outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel));
497 break;
498
499 case PulseTrainGeneration:
500 /* data[0] contains the PULSE_WIDTH
501 data[1] contains the PULSE_PERIOD
502 @pre PULSE_PERIOD > PULSE_WIDTH > 0
503 The above periods must be expressed as a multiple of the
504 pulse frequency on the selected source
505 */
c9c62f4e 506 printk(KERN_INFO "S526: INSN_WRITE: PTG\n");
b2abd982 507 if ((data[1] > data[0]) && (data[0] > 0)) {
0c988d00 508 (devpriv->s526_gpct_config[subdev_channel]).data[0] =
b2abd982 509 data[0];
0c988d00 510 (devpriv->s526_gpct_config[subdev_channel]).data[1] =
b2abd982 511 data[1];
0c988d00 512 } else {
c9c62f4e 513 printk(KERN_ERR "s526: INSN_WRITE: PTG: Problem with Pulse params -> %d %d\n",
b2abd982 514 data[0], data[1]);
0c988d00
EW
515 return -EINVAL;
516 }
517
0a85b6f0 518 value = (short)((*data >> 16) & 0xFFFF);
0c988d00 519 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
0a85b6f0 520 value = (short)(*data & 0xFFFF);
0c988d00
EW
521 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
522 break;
232f6502 523 default: /* Impossible */
0a85b6f0
MT
524 printk
525 ("s526: INSN_WRITE: Functionality %d not implemented yet\n",
526 devpriv->s526_gpct_config[subdev_channel].app);
0c988d00
EW
527 return -EINVAL;
528 break;
529 }
232f6502 530 /* return the number of samples written */
0c988d00
EW
531 return insn->n;
532}
533
534#define ISR_ADC_DONE 0x4
0a85b6f0
MT
535static int s526_ai_insn_config(struct comedi_device *dev,
536 struct comedi_subdevice *s,
537 struct comedi_insn *insn, unsigned int *data)
0c988d00
EW
538{
539 int result = -EINVAL;
540
541 if (insn->n < 1)
542 return result;
543
544 result = insn->n;
545
546 /* data[0] : channels was set in relevant bits.
547 data[1] : delay
548 */
549 /* COMMENT: abbotti 2008-07-24: I don't know why you'd want to
550 * enable channels here. The channel should be enabled in the
551 * INSN_READ handler. */
552
232f6502 553 /* Enable ADC interrupt */
0c988d00 554 outw(ISR_ADC_DONE, ADDR_REG(REG_IER));
232f6502 555/* printk("s526: ADC current value: 0x%04x\n", inw(ADDR_REG(REG_ADC))); */
0c988d00
EW
556 devpriv->s526_ai_config = (data[0] & 0x3FF) << 5;
557 if (data[1] > 0)
232f6502 558 devpriv->s526_ai_config |= 0x8000; /* set the delay */
0c988d00 559
232f6502 560 devpriv->s526_ai_config |= 0x0001; /* ADC start bit. */
0c988d00
EW
561
562 return result;
563}
564
565/*
566 * "instructions" read/write data in "one-shot" or "software-triggered"
567 * mode.
568 */
da91b269 569static int s526_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 570 struct comedi_insn *insn, unsigned int *data)
0c988d00
EW
571{
572 int n, i;
573 int chan = CR_CHAN(insn->chanspec);
574 unsigned short value;
575 unsigned int d;
576 unsigned int status;
577
578 /* Set configured delay, enable channel for this channel only,
579 * select "ADC read" channel, set "ADC start" bit. */
580 value = (devpriv->s526_ai_config & 0x8000) |
0a85b6f0 581 ((1 << 5) << chan) | (chan << 1) | 0x0001;
0c988d00
EW
582
583 /* convert n samples */
584 for (n = 0; n < insn->n; n++) {
585 /* trigger conversion */
586 outw(value, ADDR_REG(REG_ADC));
232f6502
BP
587/* printk("s526: Wrote 0x%04x to ADC\n", value); */
588/* printk("s526: ADC reg=0x%04x\n", inw(ADDR_REG(REG_ADC))); */
0c988d00
EW
589
590#define TIMEOUT 100
591 /* wait for conversion to end */
592 for (i = 0; i < TIMEOUT; i++) {
593 status = inw(ADDR_REG(REG_ISR));
594 if (status & ISR_ADC_DONE) {
595 outw(ISR_ADC_DONE, ADDR_REG(REG_ISR));
596 break;
597 }
598 }
599 if (i == TIMEOUT) {
5f74ea14 600 /* printk() should be used instead of printk()
0c988d00 601 * whenever the code can be called from real-time. */
c9c62f4e 602 printk(KERN_ERR "s526: ADC(0x%04x) timeout\n",
0a85b6f0 603 inw(ADDR_REG(REG_ISR)));
0c988d00
EW
604 return -ETIMEDOUT;
605 }
606
607 /* read data */
608 d = inw(ADDR_REG(REG_ADD));
232f6502 609/* printk("AI[%d]=0x%04x\n", n, (unsigned short)(d & 0xFFFF)); */
0c988d00
EW
610
611 /* munge data */
612 data[n] = d ^ 0x8000;
613 }
614
615 /* return the number of samples read/written */
616 return n;
617}
618
da91b269 619static int s526_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 620 struct comedi_insn *insn, unsigned int *data)
0c988d00
EW
621{
622 int i;
623 int chan = CR_CHAN(insn->chanspec);
624 unsigned short val;
625
232f6502 626/* printk("s526_ao_winsn\n"); */
0c988d00 627 val = chan << 1;
232f6502 628/* outw(val, dev->iobase + REG_DAC); */
0c988d00
EW
629 outw(val, ADDR_REG(REG_DAC));
630
631 /* Writing a list of values to an AO channel is probably not
632 * very useful, but that's how the interface is defined. */
633 for (i = 0; i < insn->n; i++) {
634 /* a typical programming sequence */
c9c62f4e
XF
635 /* write the data to preload register
636 * outw(data[i], dev->iobase + REG_ADD);
637 */
638 /* write the data to preload register */
639 outw(data[i], ADDR_REG(REG_ADD));
0c988d00 640 devpriv->ao_readback[chan] = data[i];
232f6502 641/* outw(val + 1, dev->iobase + REG_DAC); starts the D/A conversion. */
c9c62f4e 642 outw(val + 1, ADDR_REG(REG_DAC)); /*starts the D/A conversion.*/
0c988d00
EW
643 }
644
645 /* return the number of samples read/written */
646 return i;
647}
648
649/* AO subdevices should have a read insn as well as a write insn.
650 * Usually this means copying a value stored in devpriv. */
da91b269 651static int s526_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 652 struct comedi_insn *insn, unsigned int *data)
0c988d00
EW
653{
654 int i;
655 int chan = CR_CHAN(insn->chanspec);
656
657 for (i = 0; i < insn->n; i++)
658 data[i] = devpriv->ao_readback[chan];
659
660 return i;
661}
662
663/* DIO devices are slightly special. Although it is possible to
664 * implement the insn_read/insn_write interface, it is much more
665 * useful to applications if you implement the insn_bits interface.
666 * This allows packed reading/writing of the DIO channels. The
667 * comedi core can convert between insn_bits and insn_read/write */
0a85b6f0
MT
668static int s526_dio_insn_bits(struct comedi_device *dev,
669 struct comedi_subdevice *s,
670 struct comedi_insn *insn, unsigned int *data)
0c988d00 671{
0c988d00
EW
672 /* The insn data is a mask in data[0] and the new data
673 * in data[1], each channel cooresponding to a bit. */
674 if (data[0]) {
675 s->state &= ~data[0];
676 s->state |= data[0] & data[1];
677 /* Write out the new digital output lines */
678 outw(s->state, ADDR_REG(REG_DIO));
679 }
680
681 /* on return, data[1] contains the value of the digital
682 * input and output lines. */
c9c62f4e 683 data[1] = inw(ADDR_REG(REG_DIO)) & 0xFF; /* low 8 bits are the data */
0c988d00
EW
684 /* or we could just return the software copy of the output values if
685 * it was a purely digital output subdevice */
10f27014 686 /* data[1]=s->state & 0xFF; */
0c988d00 687
a2714e3e 688 return insn->n;
0c988d00
EW
689}
690
0a85b6f0
MT
691static int s526_dio_insn_config(struct comedi_device *dev,
692 struct comedi_subdevice *s,
693 struct comedi_insn *insn, unsigned int *data)
0c988d00
EW
694{
695 int chan = CR_CHAN(insn->chanspec);
10f27014 696 int group, mask;
0c988d00 697
c9c62f4e 698 printk(KERN_INFO "S526 DIO insn_config\n");
0c988d00 699
0c988d00
EW
700 /* The input or output configuration of each digital line is
701 * configured by a special insn_config instruction. chanspec
702 * contains the channel to be changed, and data[0] contains the
703 * value COMEDI_INPUT or COMEDI_OUTPUT. */
704
10f27014
IA
705 group = chan >> 2;
706 mask = 0xF << (group << 2);
707 switch (data[0]) {
708 case INSN_CONFIG_DIO_OUTPUT:
c9c62f4e
XF
709 /* bit 10/11 set the group 1/2's mode */
710 s->state |= 1 << (group + 10);
10f27014
IA
711 s->io_bits |= mask;
712 break;
713 case INSN_CONFIG_DIO_INPUT:
c9c62f4e 714 s->state &= ~(1 << (group + 10)); /* 1 is output, 0 is input. */
10f27014
IA
715 s->io_bits &= ~mask;
716 break;
717 case INSN_CONFIG_DIO_QUERY:
718 data[1] = (s->io_bits & mask) ? COMEDI_OUTPUT : COMEDI_INPUT;
719 return insn->n;
720 default:
721 return -EINVAL;
0c988d00 722 }
10f27014 723 outw(s->state, ADDR_REG(REG_DIO));
0c988d00
EW
724
725 return 1;
726}
727
e9a4a7fb
HS
728static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it)
729{
5254cbe7 730 const struct s526_board *board = comedi_board(dev);
e9a4a7fb
HS
731 struct comedi_subdevice *s;
732 int iobase;
733 int i, n;
8b6c5694 734 int ret;
e9a4a7fb
HS
735/* short value; */
736/* int subdev_channel = 0; */
737 union cmReg cmReg;
738
739 printk(KERN_INFO "comedi%d: s526: ", dev->minor);
740
741 iobase = it->options[0];
5254cbe7 742 if (!iobase || !request_region(iobase, S526_IOSIZE, board->name)) {
e9a4a7fb
HS
743 comedi_error(dev, "I/O port conflict");
744 return -EIO;
745 }
746 dev->iobase = iobase;
747
748 printk("iobase=0x%lx\n", dev->iobase);
749
750 /*** make it a little quieter, exw, 8/29/06
751 for (i = 0; i < S526_NUM_PORTS; i++) {
752 printk("0x%02x: 0x%04x\n", ADDR_REG(s526_ports[i]),
753 inw(ADDR_REG(s526_ports[i])));
754 }
755 ***/
756
5254cbe7 757 dev->board_name = board->name;
e9a4a7fb
HS
758
759/*
760 * Allocate the private structure area. alloc_private() is a
761 * convenient macro defined in comedidev.h.
762 */
763 if (alloc_private(dev, sizeof(struct s526_private)) < 0)
764 return -ENOMEM;
765
8b6c5694
HS
766 ret = comedi_alloc_subdevices(dev, 4);
767 if (ret)
768 return ret;
e9a4a7fb 769
97073c05 770 s = &dev->subdevices[0];
e9a4a7fb
HS
771 /* GENERAL-PURPOSE COUNTER/TIME (GPCT) */
772 s->type = COMEDI_SUBD_COUNTER;
773 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL;
774 /* KG: What does SDF_LSAMPL (see multiq3.c) mean? */
5254cbe7 775 s->n_chan = board->gpct_chans;
e9a4a7fb
HS
776 s->maxdata = 0x00ffffff; /* 24 bit counter */
777 s->insn_read = s526_gpct_rinsn;
778 s->insn_config = s526_gpct_insn_config;
779 s->insn_write = s526_gpct_winsn;
780
781 /* Command are not implemented yet, however they are necessary to
782 allocate the necessary memory for the comedi_async struct (used
783 to trigger the GPCT in case of pulsegenerator function */
784 /* s->do_cmd = s526_gpct_cmd; */
785 /* s->do_cmdtest = s526_gpct_cmdtest; */
786 /* s->cancel = s526_gpct_cancel; */
787
97073c05 788 s = &dev->subdevices[1];
e9a4a7fb
HS
789 /* dev->read_subdev=s; */
790 /* analog input subdevice */
791 s->type = COMEDI_SUBD_AI;
792 /* we support differential */
793 s->subdev_flags = SDF_READABLE | SDF_DIFF;
794 /* channels 0 to 7 are the regular differential inputs */
795 /* channel 8 is "reference 0" (+10V), channel 9 is "reference 1" (0V) */
796 s->n_chan = 10;
797 s->maxdata = 0xffff;
798 s->range_table = &range_bipolar10;
799 s->len_chanlist = 16; /* This is the maximum chanlist length that
800 the board can handle */
801 s->insn_read = s526_ai_rinsn;
802 s->insn_config = s526_ai_insn_config;
803
97073c05 804 s = &dev->subdevices[2];
e9a4a7fb
HS
805 /* analog output subdevice */
806 s->type = COMEDI_SUBD_AO;
807 s->subdev_flags = SDF_WRITABLE;
808 s->n_chan = 4;
809 s->maxdata = 0xffff;
810 s->range_table = &range_bipolar10;
811 s->insn_write = s526_ao_winsn;
812 s->insn_read = s526_ao_rinsn;
813
97073c05 814 s = &dev->subdevices[3];
e9a4a7fb 815 /* digital i/o subdevice */
5254cbe7 816 if (board->have_dio) {
e9a4a7fb
HS
817 s->type = COMEDI_SUBD_DIO;
818 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
819 s->n_chan = 8;
820 s->maxdata = 1;
821 s->range_table = &range_digital;
822 s->insn_bits = s526_dio_insn_bits;
823 s->insn_config = s526_dio_insn_config;
824 } else {
825 s->type = COMEDI_SUBD_UNUSED;
826 }
827
828 printk(KERN_INFO "attached\n");
829
830 return 1;
831
832#if 0
833 /* Example of Counter Application */
834 /* One-shot (software trigger) */
835 cmReg.reg.coutSource = 0; /* out RCAP */
836 cmReg.reg.coutPolarity = 1; /* Polarity inverted */
837 cmReg.reg.autoLoadResetRcap = 1;/* Auto load 0:disabled, 1:enabled */
838 cmReg.reg.hwCtEnableSource = 3; /* NOT RCAP */
839 cmReg.reg.ctEnableCtrl = 2; /* Hardware */
840 cmReg.reg.clockSource = 2; /* Internal */
841 cmReg.reg.countDir = 1; /* Down */
842 cmReg.reg.countDirCtrl = 1; /* Software */
843 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
844 cmReg.reg.preloadRegSel = 0; /* PR0 */
845 cmReg.reg.reserved = 0;
846
847 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
848
849 outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel));
850 outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel));
851
852 /* Reset the counter */
853 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
854 /* Load the counter from PR0 */
855 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
856 /* Reset RCAP (fires one-shot) */
857 outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel));
858
859#else
860
861 /* Set Counter Mode Register */
862 cmReg.reg.coutSource = 0; /* out RCAP */
863 cmReg.reg.coutPolarity = 0; /* Polarity inverted */
864 cmReg.reg.autoLoadResetRcap = 0; /* Auto load disabled */
865 cmReg.reg.hwCtEnableSource = 2; /* NOT RCAP */
866 cmReg.reg.ctEnableCtrl = 1; /* 1: Software, >1 : Hardware */
867 cmReg.reg.clockSource = 3; /* x4 */
868 cmReg.reg.countDir = 0; /* up */
869 cmReg.reg.countDirCtrl = 0; /* quadrature */
870 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
871 cmReg.reg.preloadRegSel = 0; /* PR0 */
872 cmReg.reg.reserved = 0;
873
874 n = 0;
875 printk(KERN_INFO "Mode reg=0x%04x, 0x%04lx\n",
876 cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
877 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
878 udelay(1000);
879 printk(KERN_INFO "Read back mode reg=0x%04x\n",
880 inw(ADDR_CHAN_REG(REG_C0M, n)));
881
882 /* Load the pre-load register high word */
883/* value = (short) (0x55); */
884/* outw(value, ADDR_CHAN_REG(REG_C0H, n)); */
885
886 /* Load the pre-load register low word */
887/* value = (short)(0xaa55); */
888/* outw(value, ADDR_CHAN_REG(REG_C0L, n)); */
889
890 /* Write the Counter Control Register */
891/* outw(value, ADDR_CHAN_REG(REG_C0C, 0)); */
892
893 /* Reset the counter if it is software preload */
894 if (cmReg.reg.autoLoadResetRcap == 0) {
895 /* Reset the counter */
896 outw(0x8000, ADDR_CHAN_REG(REG_C0C, n));
897 /* Load the counter from PR0 */
898 outw(0x4000, ADDR_CHAN_REG(REG_C0C, n));
899 }
900
901 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
902 udelay(1000);
903 printk(KERN_INFO "Read back mode reg=0x%04x\n",
904 inw(ADDR_CHAN_REG(REG_C0M, n)));
905
906#endif
907 printk(KERN_INFO "Current registres:\n");
908
909 for (i = 0; i < S526_NUM_PORTS; i++) {
910 printk(KERN_INFO "0x%02lx: 0x%04x\n",
911 ADDR_REG(s526_ports[i]), inw(ADDR_REG(s526_ports[i])));
912 }
913 return 1;
914}
915
484ecc95 916static void s526_detach(struct comedi_device *dev)
e9a4a7fb 917{
e9a4a7fb
HS
918 if (dev->iobase > 0)
919 release_region(dev->iobase, S526_IOSIZE);
e9a4a7fb
HS
920}
921
294f930d 922static struct comedi_driver s526_driver = {
e9a4a7fb
HS
923 .driver_name = "s526",
924 .module = THIS_MODULE,
925 .attach = s526_attach,
926 .detach = s526_detach,
927 .board_name = &s526_boards[0].name,
928 .offset = sizeof(struct s526_board),
929 .num_names = ARRAY_SIZE(s526_boards),
930};
294f930d 931module_comedi_driver(s526_driver);
90f703d3
AT
932
933MODULE_AUTHOR("Comedi http://www.comedi.org");
934MODULE_DESCRIPTION("Comedi low-level driver");
935MODULE_LICENSE("GPL");
This page took 0.389943 seconds and 5 git commands to generate.