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