Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / 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 /*
19 Driver: s526
20 Description: Sensoray 526 driver
21 Devices: [Sensoray] 526 (s526)
22 Author: Richie
23         Everett Wang <everett.wang@everteq.com>
24 Updated: Thu, 14 Sep. 2006
25 Status: experimental
26
27 Encoder works
28 Analog input works
29 Analog output works
30 PWM output works
31 Commands are not supported yet.
32
33 Configuration Options:
34
35 comedi_config /dev/comedi0 s526 0x2C0,0x3
36
37 */
38
39 #include <linux/module.h>
40 #include "../comedidev.h"
41 #include <asm/byteorder.h>
42
43 #define S526_START_AI_CONV      0
44 #define S526_AI_READ            0
45
46 /* Ports */
47 #define S526_NUM_PORTS 27
48
49 /* registers */
50 #define REG_TCR 0x00
51 #define REG_WDC 0x02
52 #define REG_DAC 0x04
53 #define REG_ADC 0x06
54 #define REG_ADD 0x08
55 #define REG_DIO 0x0A
56 #define REG_IER 0x0C
57 #define REG_ISR 0x0E
58 #define REG_MSC 0x10
59 #define REG_C0L 0x12
60 #define REG_C0H 0x14
61 #define REG_C0M 0x16
62 #define REG_C0C 0x18
63 #define REG_C1L 0x1A
64 #define REG_C1H 0x1C
65 #define REG_C1M 0x1E
66 #define REG_C1C 0x20
67 #define REG_C2L 0x22
68 #define REG_C2H 0x24
69 #define REG_C2M 0x26
70 #define REG_C2C 0x28
71 #define REG_C3L 0x2A
72 #define REG_C3H 0x2C
73 #define REG_C3M 0x2E
74 #define REG_C3C 0x30
75 #define REG_EED 0x32
76 #define REG_EEC 0x34
77
78 struct counter_mode_register_t {
79 #if defined(__LITTLE_ENDIAN_BITFIELD)
80         unsigned short coutSource:1;
81         unsigned short coutPolarity:1;
82         unsigned short autoLoadResetRcap:3;
83         unsigned short hwCtEnableSource:2;
84         unsigned short ctEnableCtrl:2;
85         unsigned short clockSource:2;
86         unsigned short countDir:1;
87         unsigned short countDirCtrl:1;
88         unsigned short outputRegLatchCtrl:1;
89         unsigned short preloadRegSel:1;
90         unsigned short reserved:1;
91  #elif defined(__BIG_ENDIAN_BITFIELD)
92         unsigned short reserved:1;
93         unsigned short preloadRegSel:1;
94         unsigned short outputRegLatchCtrl:1;
95         unsigned short countDirCtrl:1;
96         unsigned short countDir:1;
97         unsigned short clockSource:2;
98         unsigned short ctEnableCtrl:2;
99         unsigned short hwCtEnableSource:2;
100         unsigned short autoLoadResetRcap:3;
101         unsigned short coutPolarity:1;
102         unsigned short coutSource:1;
103 #else
104 #error Unknown bit field order
105 #endif
106 };
107
108 union cmReg {
109         struct counter_mode_register_t reg;
110         unsigned short value;
111 };
112
113 struct s526_private {
114         unsigned int gpct_config[4];
115         unsigned short ai_config;
116 };
117
118 static int s526_gpct_rinsn(struct comedi_device *dev,
119                            struct comedi_subdevice *s,
120                            struct comedi_insn *insn,
121                            unsigned int *data)
122 {
123         unsigned int chan = CR_CHAN(insn->chanspec);
124         unsigned long chan_iobase = dev->iobase + chan * 8;
125         unsigned int lo;
126         unsigned int hi;
127         int i;
128
129         for (i = 0; i < insn->n; i++) {
130                 /* Read the low word first */
131                 lo = inw(chan_iobase + REG_C0L) & 0xffff;
132                 hi = inw(chan_iobase + REG_C0H) & 0xff;
133
134                 data[i] = (hi << 16) | lo;
135         }
136
137         return insn->n;
138 }
139
140 static int s526_gpct_insn_config(struct comedi_device *dev,
141                                  struct comedi_subdevice *s,
142                                  struct comedi_insn *insn,
143                                  unsigned int *data)
144 {
145         struct s526_private *devpriv = dev->private;
146         unsigned int chan = CR_CHAN(insn->chanspec);
147         unsigned long chan_iobase = dev->iobase + chan * 8;
148         unsigned int val;
149         union cmReg cmReg;
150
151         /*  Check what type of Counter the user requested, data[0] contains */
152         /*  the Application type */
153         switch (data[0]) {
154         case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
155                 /*
156                    data[0]: Application Type
157                    data[1]: Counter Mode Register Value
158                    data[2]: Pre-load Register Value
159                    data[3]: Conter Control Register
160                  */
161                 devpriv->gpct_config[chan] = data[0];
162
163 #if 0
164                 /*  Example of Counter Application */
165                 /* One-shot (software trigger) */
166                 cmReg.reg.coutSource = 0;       /*  out RCAP */
167                 cmReg.reg.coutPolarity = 1;     /*  Polarity inverted */
168                 cmReg.reg.autoLoadResetRcap = 0;/*  Auto load disabled */
169                 cmReg.reg.hwCtEnableSource = 3; /*  NOT RCAP */
170                 cmReg.reg.ctEnableCtrl = 2;     /*  Hardware */
171                 cmReg.reg.clockSource = 2;      /*  Internal */
172                 cmReg.reg.countDir = 1; /*  Down */
173                 cmReg.reg.countDirCtrl = 1;     /*  Software */
174                 cmReg.reg.outputRegLatchCtrl = 0;       /*  latch on read */
175                 cmReg.reg.preloadRegSel = 0;    /*  PR0 */
176                 cmReg.reg.reserved = 0;
177
178                 outw(cmReg.value, chan_iobase + REG_C0M);
179
180                 outw(0x0001, chan_iobase + REG_C0H);
181                 outw(0x3C68, chan_iobase + REG_C0L);
182
183                 /*  Reset the counter */
184                 outw(0x8000, chan_iobase + REG_C0C);
185                 /*  Load the counter from PR0 */
186                 outw(0x4000, chan_iobase + REG_C0C);
187
188                 /*  Reset RCAP (fires one-shot) */
189                 outw(0x0008, chan_iobase + REG_C0C);
190
191 #endif
192
193 #if 1
194                 /*  Set Counter Mode Register */
195                 cmReg.value = data[1] & 0xffff;
196                 outw(cmReg.value, chan_iobase + REG_C0M);
197
198                 /*  Reset the counter if it is software preload */
199                 if (cmReg.reg.autoLoadResetRcap == 0) {
200                         /*  Reset the counter */
201                         outw(0x8000, chan_iobase + REG_C0C);
202                         /* Load the counter from PR0
203                          * outw(0x4000, chan_iobase + REG_C0C);
204                          */
205                 }
206 #else
207                 /*  0 quadrature, 1 software control */
208                 cmReg.reg.countDirCtrl = 0;
209
210                 /*  data[1] contains GPCT_X1, GPCT_X2 or GPCT_X4 */
211                 if (data[1] == GPCT_X2)
212                         cmReg.reg.clockSource = 1;
213                 else if (data[1] == GPCT_X4)
214                         cmReg.reg.clockSource = 2;
215                 else
216                         cmReg.reg.clockSource = 0;
217
218                 /*  When to take into account the indexpulse: */
219                 /*if (data[2] == GPCT_IndexPhaseLowLow) {
220                 } else if (data[2] == GPCT_IndexPhaseLowHigh) {
221                 } else if (data[2] == GPCT_IndexPhaseHighLow) {
222                 } else if (data[2] == GPCT_IndexPhaseHighHigh) {
223                 }*/
224                 /*  Take into account the index pulse? */
225                 if (data[3] == GPCT_RESET_COUNTER_ON_INDEX)
226                         /*  Auto load with INDEX^ */
227                         cmReg.reg.autoLoadResetRcap = 4;
228
229                 /*  Set Counter Mode Register */
230                 cmReg.value = data[1] & 0xffff;
231                 outw(cmReg.value, chan_iobase + REG_C0M);
232
233                 /*  Load the pre-load register high word */
234                 val = (data[2] >> 16) & 0xffff;
235                 outw(val, chan_iobase + REG_C0H);
236
237                 /*  Load the pre-load register low word */
238                 val = data[2] & 0xffff;
239                 outw(val, chan_iobase + REG_C0L);
240
241                 /*  Write the Counter Control Register */
242                 if (data[3]) {
243                         val = data[3] & 0xffff;
244                         outw(val, chan_iobase + REG_C0C);
245                 }
246                 /*  Reset the counter if it is software preload */
247                 if (cmReg.reg.autoLoadResetRcap == 0) {
248                         /*  Reset the counter */
249                         outw(0x8000, chan_iobase + REG_C0C);
250                         /*  Load the counter from PR0 */
251                         outw(0x4000, chan_iobase + REG_C0C);
252                 }
253 #endif
254                 break;
255
256         case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
257                 /*
258                    data[0]: Application Type
259                    data[1]: Counter Mode Register Value
260                    data[2]: Pre-load Register 0 Value
261                    data[3]: Pre-load Register 1 Value
262                    data[4]: Conter Control Register
263                  */
264                 devpriv->gpct_config[chan] = data[0];
265
266                 /*  Set Counter Mode Register */
267                 cmReg.value = data[1] & 0xffff;
268                 cmReg.reg.preloadRegSel = 0;    /*  PR0 */
269                 outw(cmReg.value, chan_iobase + REG_C0M);
270
271                 /*  Load the pre-load register 0 high word */
272                 val = (data[2] >> 16) & 0xffff;
273                 outw(val, chan_iobase + REG_C0H);
274
275                 /*  Load the pre-load register 0 low word */
276                 val = data[2] & 0xffff;
277                 outw(val, chan_iobase + REG_C0L);
278
279                 /*  Set Counter Mode Register */
280                 cmReg.value = data[1] & 0xffff;
281                 cmReg.reg.preloadRegSel = 1;    /*  PR1 */
282                 outw(cmReg.value, chan_iobase + REG_C0M);
283
284                 /*  Load the pre-load register 1 high word */
285                 val = (data[3] >> 16) & 0xffff;
286                 outw(val, chan_iobase + REG_C0H);
287
288                 /*  Load the pre-load register 1 low word */
289                 val = data[3] & 0xffff;
290                 outw(val, chan_iobase + REG_C0L);
291
292                 /*  Write the Counter Control Register */
293                 if (data[4]) {
294                         val = data[4] & 0xffff;
295                         outw(val, chan_iobase + REG_C0C);
296                 }
297                 break;
298
299         case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
300                 /*
301                    data[0]: Application Type
302                    data[1]: Counter Mode Register Value
303                    data[2]: Pre-load Register 0 Value
304                    data[3]: Pre-load Register 1 Value
305                    data[4]: Conter Control Register
306                  */
307                 devpriv->gpct_config[chan] = data[0];
308
309                 /*  Set Counter Mode Register */
310                 cmReg.value = data[1] & 0xffff;
311                 cmReg.reg.preloadRegSel = 0;    /*  PR0 */
312                 outw(cmReg.value, chan_iobase + REG_C0M);
313
314                 /*  Load the pre-load register 0 high word */
315                 val = (data[2] >> 16) & 0xffff;
316                 outw(val, chan_iobase + REG_C0H);
317
318                 /*  Load the pre-load register 0 low word */
319                 val = data[2] & 0xffff;
320                 outw(val, chan_iobase + REG_C0L);
321
322                 /*  Set Counter Mode Register */
323                 cmReg.value = data[1] & 0xffff;
324                 cmReg.reg.preloadRegSel = 1;    /*  PR1 */
325                 outw(cmReg.value, chan_iobase + REG_C0M);
326
327                 /*  Load the pre-load register 1 high word */
328                 val = (data[3] >> 16) & 0xffff;
329                 outw(val, chan_iobase + REG_C0H);
330
331                 /*  Load the pre-load register 1 low word */
332                 val = data[3] & 0xffff;
333                 outw(val, chan_iobase + REG_C0L);
334
335                 /*  Write the Counter Control Register */
336                 if (data[4]) {
337                         val = data[4] & 0xffff;
338                         outw(val, chan_iobase + REG_C0C);
339                 }
340                 break;
341
342         default:
343                 return -EINVAL;
344         }
345
346         return insn->n;
347 }
348
349 static int s526_gpct_winsn(struct comedi_device *dev,
350                            struct comedi_subdevice *s,
351                            struct comedi_insn *insn,
352                            unsigned int *data)
353 {
354         struct s526_private *devpriv = dev->private;
355         unsigned int chan = CR_CHAN(insn->chanspec);
356         unsigned long chan_iobase = dev->iobase + chan * 8;
357
358         inw(chan_iobase + REG_C0M);     /* Is this read required? */
359
360         /*  Check what Application of Counter this channel is configured for */
361         switch (devpriv->gpct_config[chan]) {
362         case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
363                 /* data[0] contains the PULSE_WIDTH
364                    data[1] contains the PULSE_PERIOD
365                    @pre PULSE_PERIOD > PULSE_WIDTH > 0
366                    The above periods must be expressed as a multiple of the
367                    pulse frequency on the selected source
368                  */
369                 if ((data[1] <= data[0]) || !data[0])
370                         return -EINVAL;
371
372                 /* Fall thru to write the PULSE_WIDTH */
373
374         case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
375         case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
376                 outw((data[0] >> 16) & 0xffff, chan_iobase + REG_C0H);
377                 outw(data[0] & 0xffff, chan_iobase + REG_C0L);
378                 break;
379
380         default:
381                 return -EINVAL;
382         }
383
384         return insn->n;
385 }
386
387 #define ISR_ADC_DONE 0x4
388 static int s526_ai_insn_config(struct comedi_device *dev,
389                                struct comedi_subdevice *s,
390                                struct comedi_insn *insn, unsigned int *data)
391 {
392         struct s526_private *devpriv = dev->private;
393         int result = -EINVAL;
394
395         if (insn->n < 1)
396                 return result;
397
398         result = insn->n;
399
400         /* data[0] : channels was set in relevant bits.
401            data[1] : delay
402          */
403         /* COMMENT: abbotti 2008-07-24: I don't know why you'd want to
404          * enable channels here.  The channel should be enabled in the
405          * INSN_READ handler. */
406
407         /*  Enable ADC interrupt */
408         outw(ISR_ADC_DONE, dev->iobase + REG_IER);
409         devpriv->ai_config = (data[0] & 0x3ff) << 5;
410         if (data[1] > 0)
411                 devpriv->ai_config |= 0x8000;   /* set the delay */
412
413         devpriv->ai_config |= 0x0001;           /* ADC start bit */
414
415         return result;
416 }
417
418 static int s526_ai_eoc(struct comedi_device *dev,
419                        struct comedi_subdevice *s,
420                        struct comedi_insn *insn,
421                        unsigned long context)
422 {
423         unsigned int status;
424
425         status = inw(dev->iobase + REG_ISR);
426         if (status & ISR_ADC_DONE)
427                 return 0;
428         return -EBUSY;
429 }
430
431 static int s526_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
432                          struct comedi_insn *insn, unsigned int *data)
433 {
434         struct s526_private *devpriv = dev->private;
435         unsigned int chan = CR_CHAN(insn->chanspec);
436         int n;
437         unsigned short value;
438         unsigned int d;
439         int ret;
440
441         /* Set configured delay, enable channel for this channel only,
442          * select "ADC read" channel, set "ADC start" bit. */
443         value = (devpriv->ai_config & 0x8000) |
444                 ((1 << 5) << chan) | (chan << 1) | 0x0001;
445
446         /* convert n samples */
447         for (n = 0; n < insn->n; n++) {
448                 /* trigger conversion */
449                 outw(value, dev->iobase + REG_ADC);
450
451                 /* wait for conversion to end */
452                 ret = comedi_timeout(dev, s, insn, s526_ai_eoc, 0);
453                 if (ret)
454                         return ret;
455
456                 outw(ISR_ADC_DONE, dev->iobase + REG_ISR);
457
458                 /* read data */
459                 d = inw(dev->iobase + REG_ADD);
460
461                 /* munge data */
462                 data[n] = d ^ 0x8000;
463         }
464
465         /* return the number of samples read/written */
466         return n;
467 }
468
469 static int s526_ao_insn_write(struct comedi_device *dev,
470                               struct comedi_subdevice *s,
471                               struct comedi_insn *insn,
472                               unsigned int *data)
473 {
474         unsigned int chan = CR_CHAN(insn->chanspec);
475         unsigned int val = s->readback[chan];
476         int i;
477
478         outw(chan << 1, dev->iobase + REG_DAC);
479
480         for (i = 0; i < insn->n; i++) {
481                 val = data[i];
482                 outw(val, dev->iobase + REG_ADD);
483                 /* starts the D/A conversion */
484                 outw((chan << 1) | 1, dev->iobase + REG_DAC);
485         }
486         s->readback[chan] = val;
487
488         return insn->n;
489 }
490
491 static int s526_dio_insn_bits(struct comedi_device *dev,
492                               struct comedi_subdevice *s,
493                               struct comedi_insn *insn,
494                               unsigned int *data)
495 {
496         if (comedi_dio_update_state(s, data))
497                 outw(s->state, dev->iobase + REG_DIO);
498
499         data[1] = inw(dev->iobase + REG_DIO) & 0xff;
500
501         return insn->n;
502 }
503
504 static int s526_dio_insn_config(struct comedi_device *dev,
505                                 struct comedi_subdevice *s,
506                                 struct comedi_insn *insn,
507                                 unsigned int *data)
508 {
509         unsigned int chan = CR_CHAN(insn->chanspec);
510         unsigned int mask;
511         int ret;
512
513         if (chan < 4)
514                 mask = 0x0f;
515         else
516                 mask = 0xf0;
517
518         ret = comedi_dio_insn_config(dev, s, insn, data, mask);
519         if (ret)
520                 return ret;
521
522         /* bit 10/11 set the group 1/2's mode */
523         if (s->io_bits & 0x0f)
524                 s->state |= (1 << 10);
525         else
526                 s->state &= ~(1 << 10);
527         if (s->io_bits & 0xf0)
528                 s->state |= (1 << 11);
529         else
530                 s->state &= ~(1 << 11);
531
532         outw(s->state, dev->iobase + REG_DIO);
533
534         return insn->n;
535 }
536
537 static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it)
538 {
539         struct s526_private *devpriv;
540         struct comedi_subdevice *s;
541         int ret;
542
543         ret = comedi_request_region(dev, it->options[0], 0x40);
544         if (ret)
545                 return ret;
546
547         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
548         if (!devpriv)
549                 return -ENOMEM;
550
551         ret = comedi_alloc_subdevices(dev, 4);
552         if (ret)
553                 return ret;
554
555         s = &dev->subdevices[0];
556         /* GENERAL-PURPOSE COUNTER/TIME (GPCT) */
557         s->type = COMEDI_SUBD_COUNTER;
558         s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL;
559         s->n_chan = 4;
560         s->maxdata = 0x00ffffff;        /* 24 bit counter */
561         s->insn_read = s526_gpct_rinsn;
562         s->insn_config = s526_gpct_insn_config;
563         s->insn_write = s526_gpct_winsn;
564
565         s = &dev->subdevices[1];
566         /* analog input subdevice */
567         s->type = COMEDI_SUBD_AI;
568         s->subdev_flags = SDF_READABLE | SDF_DIFF;
569         /* channels 0 to 7 are the regular differential inputs */
570         /* channel 8 is "reference 0" (+10V), channel 9 is "reference 1" (0V) */
571         s->n_chan = 10;
572         s->maxdata = 0xffff;
573         s->range_table = &range_bipolar10;
574         s->len_chanlist = 16;
575         s->insn_read = s526_ai_rinsn;
576         s->insn_config = s526_ai_insn_config;
577
578         s = &dev->subdevices[2];
579         /* analog output subdevice */
580         s->type = COMEDI_SUBD_AO;
581         s->subdev_flags = SDF_WRITABLE;
582         s->n_chan = 4;
583         s->maxdata = 0xffff;
584         s->range_table = &range_bipolar10;
585         s->insn_write = s526_ao_insn_write;
586
587         ret = comedi_alloc_subdev_readback(s);
588         if (ret)
589                 return ret;
590
591         s = &dev->subdevices[3];
592         /* digital i/o subdevice */
593         s->type = COMEDI_SUBD_DIO;
594         s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
595         s->n_chan = 8;
596         s->maxdata = 1;
597         s->range_table = &range_digital;
598         s->insn_bits = s526_dio_insn_bits;
599         s->insn_config = s526_dio_insn_config;
600
601         return 0;
602 }
603
604 static struct comedi_driver s526_driver = {
605         .driver_name    = "s526",
606         .module         = THIS_MODULE,
607         .attach         = s526_attach,
608         .detach         = comedi_legacy_detach,
609 };
610 module_comedi_driver(s526_driver);
611
612 MODULE_AUTHOR("Comedi http://www.comedi.org");
613 MODULE_DESCRIPTION("Comedi low-level driver");
614 MODULE_LICENSE("GPL");