]> bbs.cooldavid.org Git - net-next-2.6.git/blame - drivers/staging/comedi/drivers/s526.c
Staging: comedi: s526: Fix number of channels on DIO subdevice
[net-next-2.6.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>
46
47#define S526_SIZE 64
48
49#define S526_START_AI_CONV 0
50#define S526_AI_READ 0
51
52/* Ports */
53#define S526_IOSIZE 0x40
54#define S526_NUM_PORTS 27
55
56/* registers */
57#define REG_TCR 0x00
58#define REG_WDC 0x02
59#define REG_DAC 0x04
60#define REG_ADC 0x06
61#define REG_ADD 0x08
62#define REG_DIO 0x0A
63#define REG_IER 0x0C
64#define REG_ISR 0x0E
65#define REG_MSC 0x10
66#define REG_C0L 0x12
67#define REG_C0H 0x14
68#define REG_C0M 0x16
69#define REG_C0C 0x18
70#define REG_C1L 0x1A
71#define REG_C1H 0x1C
72#define REG_C1M 0x1E
73#define REG_C1C 0x20
74#define REG_C2L 0x22
75#define REG_C2H 0x24
76#define REG_C2M 0x26
77#define REG_C2C 0x28
78#define REG_C3L 0x2A
79#define REG_C3H 0x2C
80#define REG_C3M 0x2E
81#define REG_C3C 0x30
82#define REG_EED 0x32
83#define REG_EEC 0x34
84
85static const int s526_ports[] = {
86 REG_TCR,
87 REG_WDC,
88 REG_DAC,
89 REG_ADC,
90 REG_ADD,
91 REG_DIO,
92 REG_IER,
93 REG_ISR,
94 REG_MSC,
95 REG_C0L,
96 REG_C0H,
97 REG_C0M,
98 REG_C0C,
99 REG_C1L,
100 REG_C1H,
101 REG_C1M,
102 REG_C1C,
103 REG_C2L,
104 REG_C2H,
105 REG_C2M,
106 REG_C2C,
107 REG_C3L,
108 REG_C3H,
109 REG_C3M,
110 REG_C3C,
111 REG_EED,
112 REG_EEC
113};
114
4b1d53f0 115struct counter_mode_register_t {
0c988d00
EW
116 unsigned short coutSource:1;
117 unsigned short coutPolarity:1;
118 unsigned short autoLoadResetRcap:3;
119 unsigned short hwCtEnableSource:2;
120 unsigned short ctEnableCtrl:2;
121 unsigned short clockSource:2;
122 unsigned short countDir:1;
123 unsigned short countDirCtrl:1;
124 unsigned short outputRegLatchCtrl:1;
125 unsigned short preloadRegSel:1;
126 unsigned short reserved:1;
4b1d53f0 127};
0c988d00
EW
128
129union {
4b1d53f0 130 struct counter_mode_register_t reg;
0c988d00
EW
131 unsigned short value;
132} cmReg;
133
134#define MAX_GPCT_CONFIG_DATA 6
135
136/* Different Application Classes for GPCT Subdevices */
137/* The list is not exhaustive and needs discussion! */
dfb0503e 138enum S526_GPCT_APP_CLASS {
0c988d00
EW
139 CountingAndTimeMeasurement,
140 SinglePulseGeneration,
141 PulseTrainGeneration,
142 PositionMeasurement,
143 Miscellaneous
dfb0503e 144};
0c988d00
EW
145
146/* Config struct for different GPCT subdevice Application Classes and
147 their options
148*/
39f76660 149struct s526GPCTConfig {
dfb0503e 150 enum S526_GPCT_APP_CLASS app;
0c988d00 151 int data[MAX_GPCT_CONFIG_DATA];
39f76660 152};
0c988d00
EW
153
154/*
155 * Board descriptions for two imaginary boards. Describing the
156 * boards in this way is optional, and completely driver-dependent.
157 * Some drivers use arrays such as this, other do not.
158 */
c611ad33 159struct s526_board {
0c988d00
EW
160 const char *name;
161 int gpct_chans;
162 int gpct_bits;
163 int ad_chans;
164 int ad_bits;
165 int da_chans;
166 int da_bits;
167 int have_dio;
c611ad33 168};
0c988d00 169
c611ad33 170static const struct s526_board s526_boards[] = {
0c988d00 171 {
0a85b6f0
MT
172 .name = "s526",
173 .gpct_chans = 4,
174 .gpct_bits = 24,
175 .ad_chans = 8,
176 .ad_bits = 16,
177 .da_chans = 4,
178 .da_bits = 16,
179 .have_dio = 1,
180 }
0c988d00
EW
181};
182
183#define ADDR_REG(reg) (dev->iobase + (reg))
184#define ADDR_CHAN_REG(reg, chan) (dev->iobase + (reg) + (chan) * 8)
185
186/*
187 * Useful for shorthand access to the particular board structure
188 */
c611ad33 189#define thisboard ((const struct s526_board *)dev->board_ptr)
0c988d00
EW
190
191/* this structure is for data unique to this hardware driver. If
192 several hardware drivers keep similar information in this structure,
71b5f4f1 193 feel free to suggest moving the variable to the struct comedi_device struct. */
6dc1ece0
BP
194struct s526_private {
195
0c988d00
EW
196 int data;
197
198 /* would be useful for a PCI device */
199 struct pci_dev *pci_dev;
200
201 /* Used for AO readback */
790c5541 202 unsigned int ao_readback[2];
0c988d00 203
39f76660 204 struct s526GPCTConfig s526_gpct_config[4];
0c988d00 205 unsigned short s526_ai_config;
6dc1ece0
BP
206};
207
0c988d00
EW
208/*
209 * most drivers define the following macro to make it easy to
210 * access the private structure.
211 */
6dc1ece0 212#define devpriv ((struct s526_private *)dev->private)
0c988d00
EW
213
214/*
139dfbdf 215 * The struct comedi_driver structure tells the Comedi core module
0c988d00
EW
216 * which functions to call to configure/deconfigure (attach/detach)
217 * the board, and also about the kernel module that contains
218 * the device code.
219 */
da91b269
BP
220static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it);
221static int s526_detach(struct comedi_device *dev);
139dfbdf 222static struct comedi_driver driver_s526 = {
68c3dbff
BP
223 .driver_name = "s526",
224 .module = THIS_MODULE,
225 .attach = s526_attach,
226 .detach = s526_detach,
0c988d00
EW
227/* It is not necessary to implement the following members if you are
228 * writing a driver for a ISA PnP or PCI card */
229 /* Most drivers will support multiple types of boards by
230 * having an array of board structures. These were defined
231 * in s526_boards[] above. Note that the element 'name'
232 * was first in the structure -- Comedi uses this fact to
233 * extract the name of the board without knowing any details
234 * about the structure except for its length.
235 * When a device is attached (by comedi_config), the name
236 * of the device is given to Comedi, and Comedi tries to
237 * match it by going through the list of board names. If
238 * there is a match, the address of the pointer is put
239 * into dev->board_ptr and driver->attach() is called.
240 *
241 * Note that these are not necessary if you can determine
242 * the type of board in software. ISA PnP, PCI, and PCMCIA
243 * devices are such boards.
244 */
68c3dbff
BP
245 .board_name = &s526_boards[0].name,
246 .offset = sizeof(struct s526_board),
8629efa4 247 .num_names = ARRAY_SIZE(s526_boards),
0c988d00
EW
248};
249
0a85b6f0
MT
250static int s526_gpct_rinsn(struct comedi_device *dev,
251 struct comedi_subdevice *s, struct comedi_insn *insn,
252 unsigned int *data);
253static int s526_gpct_insn_config(struct comedi_device *dev,
254 struct comedi_subdevice *s,
255 struct comedi_insn *insn, unsigned int *data);
256static int s526_gpct_winsn(struct comedi_device *dev,
257 struct comedi_subdevice *s, struct comedi_insn *insn,
258 unsigned int *data);
259static int s526_ai_insn_config(struct comedi_device *dev,
260 struct comedi_subdevice *s,
261 struct comedi_insn *insn, unsigned int *data);
da91b269 262static int s526_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 263 struct comedi_insn *insn, unsigned int *data);
da91b269 264static int s526_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 265 struct comedi_insn *insn, unsigned int *data);
da91b269 266static int s526_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0
MT
267 struct comedi_insn *insn, unsigned int *data);
268static int s526_dio_insn_bits(struct comedi_device *dev,
269 struct comedi_subdevice *s,
270 struct comedi_insn *insn, unsigned int *data);
271static int s526_dio_insn_config(struct comedi_device *dev,
272 struct comedi_subdevice *s,
273 struct comedi_insn *insn, unsigned int *data);
0c988d00
EW
274
275/*
276 * Attach is called by the Comedi core to configure the driver
277 * for a particular board. If you specified a board_name array
278 * in the driver structure, dev->board_ptr contains that
279 * address.
280 */
da91b269 281static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it)
0c988d00 282{
34c43922 283 struct comedi_subdevice *s;
0c988d00
EW
284 int iobase;
285 int i, n;
232f6502
BP
286/* short value; */
287/* int subdev_channel = 0; */
0c988d00
EW
288
289 printk("comedi%d: s526: ", dev->minor);
290
291 iobase = it->options[0];
292 if (!iobase || !request_region(iobase, S526_IOSIZE, thisboard->name)) {
293 comedi_error(dev, "I/O port conflict");
294 return -EIO;
295 }
296 dev->iobase = iobase;
297
298 printk("iobase=0x%lx\n", dev->iobase);
299
300 /*** make it a little quieter, exw, 8/29/06
301 for (i = 0; i < S526_NUM_PORTS; i++) {
302 printk("0x%02x: 0x%04x\n", ADDR_REG(s526_ports[i]), inw(ADDR_REG(s526_ports[i])));
303 }
304 ***/
305
306/*
307 * Initialize dev->board_name. Note that we can use the "thisboard"
308 * macro now, since we just initialized it in the last line.
309 */
310 dev->board_ptr = &s526_boards[0];
311
312 dev->board_name = thisboard->name;
313
314/*
315 * Allocate the private structure area. alloc_private() is a
316 * convenient macro defined in comedidev.h.
317 */
6dc1ece0 318 if (alloc_private(dev, sizeof(struct s526_private)) < 0)
0c988d00
EW
319 return -ENOMEM;
320
321/*
322 * Allocate the subdevice structures. alloc_subdevice() is a
323 * convenient macro defined in comedidev.h.
324 */
325 dev->n_subdevices = 4;
326 if (alloc_subdevices(dev, dev->n_subdevices) < 0)
327 return -ENOMEM;
328
329 s = dev->subdevices + 0;
330 /* GENERAL-PURPOSE COUNTER/TIME (GPCT) */
331 s->type = COMEDI_SUBD_COUNTER;
332 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL;
333 /* KG: What does SDF_LSAMPL (see multiq3.c) mean? */
334 s->n_chan = thisboard->gpct_chans;
335 s->maxdata = 0x00ffffff; /* 24 bit counter */
336 s->insn_read = s526_gpct_rinsn;
337 s->insn_config = s526_gpct_insn_config;
338 s->insn_write = s526_gpct_winsn;
339
340 /* Command are not implemented yet, however they are necessary to
341 allocate the necessary memory for the comedi_async struct (used
342 to trigger the GPCT in case of pulsegenerator function */
232f6502
BP
343 /* s->do_cmd = s526_gpct_cmd; */
344 /* s->do_cmdtest = s526_gpct_cmdtest; */
345 /* s->cancel = s526_gpct_cancel; */
0c988d00
EW
346
347 s = dev->subdevices + 1;
232f6502 348 /* dev->read_subdev=s; */
0c988d00
EW
349 /* analog input subdevice */
350 s->type = COMEDI_SUBD_AI;
351 /* we support differential */
352 s->subdev_flags = SDF_READABLE | SDF_DIFF;
353 /* channels 0 to 7 are the regular differential inputs */
354 /* channel 8 is "reference 0" (+10V), channel 9 is "reference 1" (0V) */
355 s->n_chan = 10;
356 s->maxdata = 0xffff;
357 s->range_table = &range_bipolar10;
358 s->len_chanlist = 16; /* This is the maximum chanlist length that
359 the board can handle */
360 s->insn_read = s526_ai_rinsn;
361 s->insn_config = s526_ai_insn_config;
362
363 s = dev->subdevices + 2;
364 /* analog output subdevice */
365 s->type = COMEDI_SUBD_AO;
366 s->subdev_flags = SDF_WRITABLE;
367 s->n_chan = 4;
368 s->maxdata = 0xffff;
369 s->range_table = &range_bipolar10;
370 s->insn_write = s526_ao_winsn;
371 s->insn_read = s526_ao_rinsn;
372
373 s = dev->subdevices + 3;
374 /* digital i/o subdevice */
375 if (thisboard->have_dio) {
376 s->type = COMEDI_SUBD_DIO;
377 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
10f27014 378 s->n_chan = 8;
0c988d00
EW
379 s->maxdata = 1;
380 s->range_table = &range_digital;
381 s->insn_bits = s526_dio_insn_bits;
382 s->insn_config = s526_dio_insn_config;
383 } else {
384 s->type = COMEDI_SUBD_UNUSED;
385 }
386
387 printk("attached\n");
388
389 return 1;
390
391#if 0
232f6502
BP
392 /* Example of Counter Application */
393 /* One-shot (software trigger) */
394 cmReg.reg.coutSource = 0; /* out RCAP */
395 cmReg.reg.coutPolarity = 1; /* Polarity inverted */
396 cmReg.reg.autoLoadResetRcap = 1; /* Auto load 0:disabled, 1:enabled */
397 cmReg.reg.hwCtEnableSource = 3; /* NOT RCAP */
398 cmReg.reg.ctEnableCtrl = 2; /* Hardware */
399 cmReg.reg.clockSource = 2; /* Internal */
400 cmReg.reg.countDir = 1; /* Down */
401 cmReg.reg.countDirCtrl = 1; /* Software */
402 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
403 cmReg.reg.preloadRegSel = 0; /* PR0 */
0c988d00
EW
404 cmReg.reg.reserved = 0;
405
406 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
407
408 outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel));
409 outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel));
410
232f6502
BP
411 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset the counter */
412 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Load the counter from PR0 */
0c988d00 413
232f6502 414 outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset RCAP (fires one-shot) */
0c988d00
EW
415
416#else
417
232f6502
BP
418 /* Set Counter Mode Register */
419 cmReg.reg.coutSource = 0; /* out RCAP */
420 cmReg.reg.coutPolarity = 0; /* Polarity inverted */
421 cmReg.reg.autoLoadResetRcap = 0; /* Auto load disabled */
422 cmReg.reg.hwCtEnableSource = 2; /* NOT RCAP */
423 cmReg.reg.ctEnableCtrl = 1; /* 1: Software, >1 : Hardware */
424 cmReg.reg.clockSource = 3; /* x4 */
425 cmReg.reg.countDir = 0; /* up */
426 cmReg.reg.countDirCtrl = 0; /* quadrature */
427 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
428 cmReg.reg.preloadRegSel = 0; /* PR0 */
0c988d00
EW
429 cmReg.reg.reserved = 0;
430
431 n = 0;
432 printk("Mode reg=0x%04x, 0x%04lx\n", cmReg.value, ADDR_CHAN_REG(REG_C0M,
0a85b6f0 433 n));
0c988d00
EW
434 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
435 udelay(1000);
436 printk("Read back mode reg=0x%04x\n", inw(ADDR_CHAN_REG(REG_C0M, n)));
437
232f6502
BP
438 /* Load the pre-laod register high word */
439/* value = (short) (0x55); */
440/* outw(value, ADDR_CHAN_REG(REG_C0H, n)); */
0c988d00 441
232f6502
BP
442 /* Load the pre-laod register low word */
443/* value = (short)(0xaa55); */
444/* outw(value, ADDR_CHAN_REG(REG_C0L, n)); */
0c988d00 445
232f6502
BP
446 /* Write the Counter Control Register */
447/* outw(value, ADDR_CHAN_REG(REG_C0C, 0)); */
0c988d00 448
232f6502 449 /* Reset the counter if it is software preload */
0c988d00 450 if (cmReg.reg.autoLoadResetRcap == 0) {
232f6502
BP
451 outw(0x8000, ADDR_CHAN_REG(REG_C0C, n)); /* Reset the counter */
452 outw(0x4000, ADDR_CHAN_REG(REG_C0C, n)); /* Load the counter from PR0 */
0c988d00
EW
453 }
454
455 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
456 udelay(1000);
457 printk("Read back mode reg=0x%04x\n", inw(ADDR_CHAN_REG(REG_C0M, n)));
458
459#endif
460 printk("Current registres:\n");
461
462 for (i = 0; i < S526_NUM_PORTS; i++) {
463 printk("0x%02lx: 0x%04x\n", ADDR_REG(s526_ports[i]),
0a85b6f0 464 inw(ADDR_REG(s526_ports[i])));
0c988d00
EW
465 }
466 return 1;
467}
468
469/*
470 * _detach is called to deconfigure a device. It should deallocate
471 * resources.
472 * This function is also called when _attach() fails, so it should be
473 * careful not to release resources that were not necessarily
474 * allocated by _attach(). dev->private and dev->subdevices are
475 * deallocated automatically by the core.
476 */
da91b269 477static int s526_detach(struct comedi_device *dev)
0c988d00
EW
478{
479 printk("comedi%d: s526: remove\n", dev->minor);
480
481 if (dev->iobase > 0)
482 release_region(dev->iobase, S526_IOSIZE);
483
484 return 0;
485}
486
0a85b6f0
MT
487static int s526_gpct_rinsn(struct comedi_device *dev,
488 struct comedi_subdevice *s, struct comedi_insn *insn,
489 unsigned int *data)
0c988d00 490{
232f6502 491 int i; /* counts the Data */
0c988d00
EW
492 int counter_channel = CR_CHAN(insn->chanspec);
493 unsigned short datalow;
494 unsigned short datahigh;
495
232f6502 496 /* Check if (n > 0) */
0c988d00
EW
497 if (insn->n <= 0) {
498 printk("s526: INSN_READ: n should be > 0\n");
499 return -EINVAL;
500 }
232f6502 501 /* Read the low word first */
0c988d00
EW
502 for (i = 0; i < insn->n; i++) {
503 datalow = inw(ADDR_CHAN_REG(REG_C0L, counter_channel));
504 datahigh = inw(ADDR_CHAN_REG(REG_C0H, counter_channel));
505 data[i] = (int)(datahigh & 0x00FF);
506 data[i] = (data[i] << 16) | (datalow & 0xFFFF);
232f6502 507/* printk("s526 GPCT[%d]: %x(0x%04x, 0x%04x)\n", counter_channel, data[i], datahigh, datalow); */
0c988d00
EW
508 }
509 return i;
510}
511
0a85b6f0
MT
512static int s526_gpct_insn_config(struct comedi_device *dev,
513 struct comedi_subdevice *s,
514 struct comedi_insn *insn, unsigned int *data)
0c988d00 515{
232f6502 516 int subdev_channel = CR_CHAN(insn->chanspec); /* Unpack chanspec */
0c988d00 517 int i;
790c5541 518 short value;
0c988d00 519
232f6502 520/* printk("s526: GPCT_INSN_CONFIG: Configuring Channel %d\n", subdev_channel); */
0c988d00
EW
521
522 for (i = 0; i < MAX_GPCT_CONFIG_DATA; i++) {
523 devpriv->s526_gpct_config[subdev_channel].data[i] =
0a85b6f0 524 insn->data[i];
232f6502 525/* printk("data[%d]=%x\n", i, insn->data[i]); */
0c988d00
EW
526 }
527
232f6502
BP
528 /* Check what type of Counter the user requested, data[0] contains */
529 /* the Application type */
0c988d00
EW
530 switch (insn->data[0]) {
531 case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
532 /*
533 data[0]: Application Type
534 data[1]: Counter Mode Register Value
535 data[2]: Pre-load Register Value
536 data[3]: Conter Control Register
537 */
538 printk("s526: GPCT_INSN_CONFIG: Configuring Encoder\n");
539 devpriv->s526_gpct_config[subdev_channel].app =
0a85b6f0 540 PositionMeasurement;
0c988d00 541
232f6502 542#if 0
0a85b6f0
MT
543 /* Example of Counter Application */
544 /* One-shot (software trigger) */
545 cmReg.reg.coutSource = 0; /* out RCAP */
546 cmReg.reg.coutPolarity = 1; /* Polarity inverted */
547 cmReg.reg.autoLoadResetRcap = 0; /* Auto load disabled */
548 cmReg.reg.hwCtEnableSource = 3; /* NOT RCAP */
549 cmReg.reg.ctEnableCtrl = 2; /* Hardware */
550 cmReg.reg.clockSource = 2; /* Internal */
551 cmReg.reg.countDir = 1; /* Down */
552 cmReg.reg.countDirCtrl = 1; /* Software */
553 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
554 cmReg.reg.preloadRegSel = 0; /* PR0 */
555 cmReg.reg.reserved = 0;
0c988d00 556
0a85b6f0
MT
557 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
558
559 outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel));
560 outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel));
561
562 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset the counter */
563 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Load the counter from PR0 */
0c988d00 564
0a85b6f0 565 outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset RCAP (fires one-shot) */
0c988d00 566
232f6502 567#endif
0c988d00
EW
568
569#if 1
232f6502
BP
570 /* Set Counter Mode Register */
571 cmReg.reg.coutSource = 0; /* out RCAP */
572 cmReg.reg.coutPolarity = 0; /* Polarity inverted */
573 cmReg.reg.autoLoadResetRcap = 0; /* Auto load disabled */
574 cmReg.reg.hwCtEnableSource = 2; /* NOT RCAP */
575 cmReg.reg.ctEnableCtrl = 1; /* 1: Software, >1 : Hardware */
576 cmReg.reg.clockSource = 3; /* x4 */
577 cmReg.reg.countDir = 0; /* up */
578 cmReg.reg.countDirCtrl = 0; /* quadrature */
579 cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
580 cmReg.reg.preloadRegSel = 0; /* PR0 */
0c988d00
EW
581 cmReg.reg.reserved = 0;
582
232f6502
BP
583 /* Set Counter Mode Register */
584/* printk("s526: Counter Mode register=%x\n", cmReg.value); */
0c988d00
EW
585 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
586
232f6502 587 /* Reset the counter if it is software preload */
0c988d00 588 if (cmReg.reg.autoLoadResetRcap == 0) {
232f6502
BP
589 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset the counter */
590/* outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); Load the counter from PR0 */
0c988d00
EW
591 }
592#else
232f6502 593 cmReg.reg.countDirCtrl = 0; /* 0 quadrature, 1 software control */
0c988d00 594
232f6502 595 /* data[1] contains GPCT_X1, GPCT_X2 or GPCT_X4 */
0c988d00
EW
596 if (insn->data[1] == GPCT_X2) {
597 cmReg.reg.clockSource = 1;
598 } else if (insn->data[1] == GPCT_X4) {
599 cmReg.reg.clockSource = 2;
600 } else {
601 cmReg.reg.clockSource = 0;
602 }
603
232f6502 604 /* When to take into account the indexpulse: */
0c988d00
EW
605 if (insn->data[2] == GPCT_IndexPhaseLowLow) {
606 } else if (insn->data[2] == GPCT_IndexPhaseLowHigh) {
607 } else if (insn->data[2] == GPCT_IndexPhaseHighLow) {
608 } else if (insn->data[2] == GPCT_IndexPhaseHighHigh) {
609 }
232f6502 610 /* Take into account the index pulse? */
0c988d00 611 if (insn->data[3] == GPCT_RESET_COUNTER_ON_INDEX)
232f6502 612 cmReg.reg.autoLoadResetRcap = 4; /* Auto load with INDEX^ */
0c988d00 613
232f6502 614 /* Set Counter Mode Register */
0a85b6f0 615 cmReg.value = (short)(insn->data[1] & 0xFFFF);
0c988d00
EW
616 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
617
232f6502 618 /* Load the pre-laod register high word */
0a85b6f0 619 value = (short)((insn->data[2] >> 16) & 0xFFFF);
0c988d00
EW
620 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
621
232f6502 622 /* Load the pre-laod register low word */
0a85b6f0 623 value = (short)(insn->data[2] & 0xFFFF);
0c988d00
EW
624 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
625
232f6502 626 /* Write the Counter Control Register */
0c988d00 627 if (insn->data[3] != 0) {
0a85b6f0 628 value = (short)(insn->data[3] & 0xFFFF);
0c988d00
EW
629 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
630 }
232f6502 631 /* Reset the counter if it is software preload */
0c988d00 632 if (cmReg.reg.autoLoadResetRcap == 0) {
232f6502
BP
633 outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Reset the counter */
634 outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel)); /* Load the counter from PR0 */
0c988d00
EW
635 }
636#endif
637 break;
638
639 case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
640 /*
641 data[0]: Application Type
642 data[1]: Counter Mode Register Value
643 data[2]: Pre-load Register 0 Value
644 data[3]: Pre-load Register 1 Value
645 data[4]: Conter Control Register
646 */
647 printk("s526: GPCT_INSN_CONFIG: Configuring SPG\n");
648 devpriv->s526_gpct_config[subdev_channel].app =
0a85b6f0 649 SinglePulseGeneration;
0c988d00 650
232f6502 651 /* Set Counter Mode Register */
0a85b6f0 652 cmReg.value = (short)(insn->data[1] & 0xFFFF);
232f6502 653 cmReg.reg.preloadRegSel = 0; /* PR0 */
0c988d00
EW
654 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
655
232f6502 656 /* Load the pre-laod register 0 high word */
0a85b6f0 657 value = (short)((insn->data[2] >> 16) & 0xFFFF);
0c988d00
EW
658 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
659
232f6502 660 /* Load the pre-laod register 0 low word */
0a85b6f0 661 value = (short)(insn->data[2] & 0xFFFF);
0c988d00
EW
662 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
663
232f6502 664 /* Set Counter Mode Register */
0a85b6f0 665 cmReg.value = (short)(insn->data[1] & 0xFFFF);
232f6502 666 cmReg.reg.preloadRegSel = 1; /* PR1 */
0c988d00
EW
667 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
668
232f6502 669 /* Load the pre-laod register 1 high word */
0a85b6f0 670 value = (short)((insn->data[3] >> 16) & 0xFFFF);
0c988d00
EW
671 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
672
232f6502 673 /* Load the pre-laod register 1 low word */
0a85b6f0 674 value = (short)(insn->data[3] & 0xFFFF);
0c988d00
EW
675 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
676
232f6502 677 /* Write the Counter Control Register */
0c988d00 678 if (insn->data[3] != 0) {
0a85b6f0 679 value = (short)(insn->data[3] & 0xFFFF);
0c988d00
EW
680 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
681 }
682 break;
683
684 case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
685 /*
686 data[0]: Application Type
687 data[1]: Counter Mode Register Value
688 data[2]: Pre-load Register 0 Value
689 data[3]: Pre-load Register 1 Value
690 data[4]: Conter Control Register
691 */
692 printk("s526: GPCT_INSN_CONFIG: Configuring PTG\n");
693 devpriv->s526_gpct_config[subdev_channel].app =
0a85b6f0 694 PulseTrainGeneration;
0c988d00 695
232f6502 696 /* Set Counter Mode Register */
0a85b6f0 697 cmReg.value = (short)(insn->data[1] & 0xFFFF);
232f6502 698 cmReg.reg.preloadRegSel = 0; /* PR0 */
0c988d00
EW
699 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
700
232f6502 701 /* Load the pre-laod register 0 high word */
0a85b6f0 702 value = (short)((insn->data[2] >> 16) & 0xFFFF);
0c988d00
EW
703 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
704
232f6502 705 /* Load the pre-laod register 0 low word */
0a85b6f0 706 value = (short)(insn->data[2] & 0xFFFF);
0c988d00
EW
707 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
708
232f6502 709 /* Set Counter Mode Register */
0a85b6f0 710 cmReg.value = (short)(insn->data[1] & 0xFFFF);
232f6502 711 cmReg.reg.preloadRegSel = 1; /* PR1 */
0c988d00
EW
712 outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
713
232f6502 714 /* Load the pre-laod register 1 high word */
0a85b6f0 715 value = (short)((insn->data[3] >> 16) & 0xFFFF);
0c988d00
EW
716 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
717
232f6502 718 /* Load the pre-laod register 1 low word */
0a85b6f0 719 value = (short)(insn->data[3] & 0xFFFF);
0c988d00
EW
720 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
721
232f6502 722 /* Write the Counter Control Register */
0c988d00 723 if (insn->data[3] != 0) {
0a85b6f0 724 value = (short)(insn->data[3] & 0xFFFF);
0c988d00
EW
725 outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
726 }
727 break;
728
729 default:
730 printk("s526: unsupported GPCT_insn_config\n");
731 return -EINVAL;
732 break;
733 }
734
735 return insn->n;
736}
737
0a85b6f0
MT
738static int s526_gpct_winsn(struct comedi_device *dev,
739 struct comedi_subdevice *s, struct comedi_insn *insn,
740 unsigned int *data)
0c988d00 741{
232f6502 742 int subdev_channel = CR_CHAN(insn->chanspec); /* Unpack chanspec */
790c5541 743 short value;
0c988d00
EW
744
745 printk("s526: GPCT_INSN_WRITE on channel %d\n", subdev_channel);
746 cmReg.value = inw(ADDR_CHAN_REG(REG_C0M, subdev_channel));
747 printk("s526: Counter Mode Register: %x\n", cmReg.value);
232f6502 748 /* Check what Application of Counter this channel is configured for */
0c988d00
EW
749 switch (devpriv->s526_gpct_config[subdev_channel].app) {
750 case PositionMeasurement:
751 printk("S526: INSN_WRITE: PM\n");
752 outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H,
0a85b6f0 753 subdev_channel));
0c988d00
EW
754 outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel));
755 break;
756
757 case SinglePulseGeneration:
758 printk("S526: INSN_WRITE: SPG\n");
759 outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H,
0a85b6f0 760 subdev_channel));
0c988d00
EW
761 outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel));
762 break;
763
764 case PulseTrainGeneration:
765 /* data[0] contains the PULSE_WIDTH
766 data[1] contains the PULSE_PERIOD
767 @pre PULSE_PERIOD > PULSE_WIDTH > 0
768 The above periods must be expressed as a multiple of the
769 pulse frequency on the selected source
770 */
771 printk("S526: INSN_WRITE: PTG\n");
772 if ((insn->data[1] > insn->data[0]) && (insn->data[0] > 0)) {
773 (devpriv->s526_gpct_config[subdev_channel]).data[0] =
0a85b6f0 774 insn->data[0];
0c988d00 775 (devpriv->s526_gpct_config[subdev_channel]).data[1] =
0a85b6f0 776 insn->data[1];
0c988d00
EW
777 } else {
778 printk("%d \t %d\n", insn->data[1], insn->data[2]);
0a85b6f0
MT
779 printk
780 ("s526: INSN_WRITE: PTG: Problem with Pulse params\n");
0c988d00
EW
781 return -EINVAL;
782 }
783
0a85b6f0 784 value = (short)((*data >> 16) & 0xFFFF);
0c988d00 785 outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
0a85b6f0 786 value = (short)(*data & 0xFFFF);
0c988d00
EW
787 outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
788 break;
232f6502 789 default: /* Impossible */
0a85b6f0
MT
790 printk
791 ("s526: INSN_WRITE: Functionality %d not implemented yet\n",
792 devpriv->s526_gpct_config[subdev_channel].app);
0c988d00
EW
793 return -EINVAL;
794 break;
795 }
232f6502 796 /* return the number of samples written */
0c988d00
EW
797 return insn->n;
798}
799
800#define ISR_ADC_DONE 0x4
0a85b6f0
MT
801static int s526_ai_insn_config(struct comedi_device *dev,
802 struct comedi_subdevice *s,
803 struct comedi_insn *insn, unsigned int *data)
0c988d00
EW
804{
805 int result = -EINVAL;
806
807 if (insn->n < 1)
808 return result;
809
810 result = insn->n;
811
812 /* data[0] : channels was set in relevant bits.
813 data[1] : delay
814 */
815 /* COMMENT: abbotti 2008-07-24: I don't know why you'd want to
816 * enable channels here. The channel should be enabled in the
817 * INSN_READ handler. */
818
232f6502 819 /* Enable ADC interrupt */
0c988d00 820 outw(ISR_ADC_DONE, ADDR_REG(REG_IER));
232f6502 821/* printk("s526: ADC current value: 0x%04x\n", inw(ADDR_REG(REG_ADC))); */
0c988d00
EW
822 devpriv->s526_ai_config = (data[0] & 0x3FF) << 5;
823 if (data[1] > 0)
232f6502 824 devpriv->s526_ai_config |= 0x8000; /* set the delay */
0c988d00 825
232f6502 826 devpriv->s526_ai_config |= 0x0001; /* ADC start bit. */
0c988d00
EW
827
828 return result;
829}
830
831/*
832 * "instructions" read/write data in "one-shot" or "software-triggered"
833 * mode.
834 */
da91b269 835static int s526_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 836 struct comedi_insn *insn, unsigned int *data)
0c988d00
EW
837{
838 int n, i;
839 int chan = CR_CHAN(insn->chanspec);
840 unsigned short value;
841 unsigned int d;
842 unsigned int status;
843
844 /* Set configured delay, enable channel for this channel only,
845 * select "ADC read" channel, set "ADC start" bit. */
846 value = (devpriv->s526_ai_config & 0x8000) |
0a85b6f0 847 ((1 << 5) << chan) | (chan << 1) | 0x0001;
0c988d00
EW
848
849 /* convert n samples */
850 for (n = 0; n < insn->n; n++) {
851 /* trigger conversion */
852 outw(value, ADDR_REG(REG_ADC));
232f6502
BP
853/* printk("s526: Wrote 0x%04x to ADC\n", value); */
854/* printk("s526: ADC reg=0x%04x\n", inw(ADDR_REG(REG_ADC))); */
0c988d00
EW
855
856#define TIMEOUT 100
857 /* wait for conversion to end */
858 for (i = 0; i < TIMEOUT; i++) {
859 status = inw(ADDR_REG(REG_ISR));
860 if (status & ISR_ADC_DONE) {
861 outw(ISR_ADC_DONE, ADDR_REG(REG_ISR));
862 break;
863 }
864 }
865 if (i == TIMEOUT) {
5f74ea14 866 /* printk() should be used instead of printk()
0c988d00 867 * whenever the code can be called from real-time. */
5f74ea14 868 printk("s526: ADC(0x%04x) timeout\n",
0a85b6f0 869 inw(ADDR_REG(REG_ISR)));
0c988d00
EW
870 return -ETIMEDOUT;
871 }
872
873 /* read data */
874 d = inw(ADDR_REG(REG_ADD));
232f6502 875/* printk("AI[%d]=0x%04x\n", n, (unsigned short)(d & 0xFFFF)); */
0c988d00
EW
876
877 /* munge data */
878 data[n] = d ^ 0x8000;
879 }
880
881 /* return the number of samples read/written */
882 return n;
883}
884
da91b269 885static int s526_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 886 struct comedi_insn *insn, unsigned int *data)
0c988d00
EW
887{
888 int i;
889 int chan = CR_CHAN(insn->chanspec);
890 unsigned short val;
891
232f6502 892/* printk("s526_ao_winsn\n"); */
0c988d00 893 val = chan << 1;
232f6502 894/* outw(val, dev->iobase + REG_DAC); */
0c988d00
EW
895 outw(val, ADDR_REG(REG_DAC));
896
897 /* Writing a list of values to an AO channel is probably not
898 * very useful, but that's how the interface is defined. */
899 for (i = 0; i < insn->n; i++) {
900 /* a typical programming sequence */
232f6502
BP
901/* outw(data[i], dev->iobase + REG_ADD); write the data to preload register */
902 outw(data[i], ADDR_REG(REG_ADD)); /* write the data to preload register */
0c988d00 903 devpriv->ao_readback[chan] = data[i];
232f6502
BP
904/* outw(val + 1, dev->iobase + REG_DAC); starts the D/A conversion. */
905 outw(val + 1, ADDR_REG(REG_DAC)); /* starts the D/A conversion. */
0c988d00
EW
906 }
907
908 /* return the number of samples read/written */
909 return i;
910}
911
912/* AO subdevices should have a read insn as well as a write insn.
913 * Usually this means copying a value stored in devpriv. */
da91b269 914static int s526_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
0a85b6f0 915 struct comedi_insn *insn, unsigned int *data)
0c988d00
EW
916{
917 int i;
918 int chan = CR_CHAN(insn->chanspec);
919
920 for (i = 0; i < insn->n; i++)
921 data[i] = devpriv->ao_readback[chan];
922
923 return i;
924}
925
926/* DIO devices are slightly special. Although it is possible to
927 * implement the insn_read/insn_write interface, it is much more
928 * useful to applications if you implement the insn_bits interface.
929 * This allows packed reading/writing of the DIO channels. The
930 * comedi core can convert between insn_bits and insn_read/write */
0a85b6f0
MT
931static int s526_dio_insn_bits(struct comedi_device *dev,
932 struct comedi_subdevice *s,
933 struct comedi_insn *insn, unsigned int *data)
0c988d00
EW
934{
935 if (insn->n != 2)
936 return -EINVAL;
937
938 /* The insn data is a mask in data[0] and the new data
939 * in data[1], each channel cooresponding to a bit. */
940 if (data[0]) {
941 s->state &= ~data[0];
942 s->state |= data[0] & data[1];
943 /* Write out the new digital output lines */
944 outw(s->state, ADDR_REG(REG_DIO));
945 }
946
947 /* on return, data[1] contains the value of the digital
948 * input and output lines. */
232f6502 949 data[1] = inw(ADDR_REG(REG_DIO)) & 0xFF; /* low 8 bits are the data */
0c988d00
EW
950 /* or we could just return the software copy of the output values if
951 * it was a purely digital output subdevice */
10f27014 952 /* data[1]=s->state & 0xFF; */
0c988d00
EW
953
954 return 2;
955}
956
0a85b6f0
MT
957static int s526_dio_insn_config(struct comedi_device *dev,
958 struct comedi_subdevice *s,
959 struct comedi_insn *insn, unsigned int *data)
0c988d00
EW
960{
961 int chan = CR_CHAN(insn->chanspec);
10f27014 962 int group, mask;
0c988d00
EW
963
964 printk("S526 DIO insn_config\n");
965
0c988d00
EW
966 /* The input or output configuration of each digital line is
967 * configured by a special insn_config instruction. chanspec
968 * contains the channel to be changed, and data[0] contains the
969 * value COMEDI_INPUT or COMEDI_OUTPUT. */
970
10f27014
IA
971 group = chan >> 2;
972 mask = 0xF << (group << 2);
973 switch (data[0]) {
974 case INSN_CONFIG_DIO_OUTPUT:
975 s->state |= 1 << (group + 10); // bit 10/11 set the group 1/2's mode
976 s->io_bits |= mask;
977 break;
978 case INSN_CONFIG_DIO_INPUT:
979 s->state &= ~(1 << (group + 10));// 1 is output, 0 is input.
980 s->io_bits &= ~mask;
981 break;
982 case INSN_CONFIG_DIO_QUERY:
983 data[1] = (s->io_bits & mask) ? COMEDI_OUTPUT : COMEDI_INPUT;
984 return insn->n;
985 default:
986 return -EINVAL;
0c988d00 987 }
10f27014 988 outw(s->state, ADDR_REG(REG_DIO));
0c988d00
EW
989
990 return 1;
991}
992
993/*
994 * A convenient macro that defines init_module() and cleanup_module(),
995 * as necessary.
996 */
997COMEDI_INITCLEANUP(driver_s526);