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