Merge tag 'hsi-for-3.17' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-hsi
[cascardo/linux.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 /*
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 ao_readback[2];
115         unsigned int gpct_config[4];
116         unsigned short ai_config;
117 };
118
119 static int s526_gpct_rinsn(struct comedi_device *dev,
120                            struct comedi_subdevice *s,
121                            struct comedi_insn *insn,
122                            unsigned int *data)
123 {
124         unsigned int chan = CR_CHAN(insn->chanspec);
125         unsigned long chan_iobase = dev->iobase + chan * 8;
126         unsigned int lo;
127         unsigned int hi;
128         int i;
129
130         for (i = 0; i < insn->n; i++) {
131                 /* Read the low word first */
132                 lo = inw(chan_iobase + REG_C0L) & 0xffff;
133                 hi = inw(chan_iobase + REG_C0H) & 0xff;
134
135                 data[i] = (hi << 16) | lo;
136         }
137
138         return insn->n;
139 }
140
141 static int s526_gpct_insn_config(struct comedi_device *dev,
142                                  struct comedi_subdevice *s,
143                                  struct comedi_insn *insn,
144                                  unsigned int *data)
145 {
146         struct s526_private *devpriv = dev->private;
147         unsigned int chan = CR_CHAN(insn->chanspec);
148         unsigned long chan_iobase = dev->iobase + chan * 8;
149         unsigned int val;
150         union cmReg cmReg;
151
152         /*  Check what type of Counter the user requested, data[0] contains */
153         /*  the Application type */
154         switch (data[0]) {
155         case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
156                 /*
157                    data[0]: Application Type
158                    data[1]: Counter Mode Register Value
159                    data[2]: Pre-load Register Value
160                    data[3]: Conter Control Register
161                  */
162                 devpriv->gpct_config[chan] = data[0];
163
164 #if 0
165                 /*  Example of Counter Application */
166                 /* One-shot (software trigger) */
167                 cmReg.reg.coutSource = 0;       /*  out RCAP */
168                 cmReg.reg.coutPolarity = 1;     /*  Polarity inverted */
169                 cmReg.reg.autoLoadResetRcap = 0;/*  Auto load disabled */
170                 cmReg.reg.hwCtEnableSource = 3; /*  NOT RCAP */
171                 cmReg.reg.ctEnableCtrl = 2;     /*  Hardware */
172                 cmReg.reg.clockSource = 2;      /*  Internal */
173                 cmReg.reg.countDir = 1; /*  Down */
174                 cmReg.reg.countDirCtrl = 1;     /*  Software */
175                 cmReg.reg.outputRegLatchCtrl = 0;       /*  latch on read */
176                 cmReg.reg.preloadRegSel = 0;    /*  PR0 */
177                 cmReg.reg.reserved = 0;
178
179                 outw(cmReg.value, chan_iobase + REG_C0M);
180
181                 outw(0x0001, chan_iobase + REG_C0H);
182                 outw(0x3C68, chan_iobase + REG_C0L);
183
184                 /*  Reset the counter */
185                 outw(0x8000, chan_iobase + REG_C0C);
186                 /*  Load the counter from PR0 */
187                 outw(0x4000, chan_iobase + REG_C0C);
188
189                 /*  Reset RCAP (fires one-shot) */
190                 outw(0x0008, chan_iobase + REG_C0C);
191
192 #endif
193
194 #if 1
195                 /*  Set Counter Mode Register */
196                 cmReg.value = data[1] & 0xffff;
197                 outw(cmReg.value, chan_iobase + REG_C0M);
198
199                 /*  Reset the counter if it is software preload */
200                 if (cmReg.reg.autoLoadResetRcap == 0) {
201                         /*  Reset the counter */
202                         outw(0x8000, chan_iobase + REG_C0C);
203                         /* Load the counter from PR0
204                          * outw(0x4000, chan_iobase + REG_C0C);
205                          */
206                 }
207 #else
208                 /*  0 quadrature, 1 software control */
209                 cmReg.reg.countDirCtrl = 0;
210
211                 /*  data[1] contains GPCT_X1, GPCT_X2 or GPCT_X4 */
212                 if (data[1] == GPCT_X2)
213                         cmReg.reg.clockSource = 1;
214                 else if (data[1] == GPCT_X4)
215                         cmReg.reg.clockSource = 2;
216                 else
217                         cmReg.reg.clockSource = 0;
218
219                 /*  When to take into account the indexpulse: */
220                 /*if (data[2] == GPCT_IndexPhaseLowLow) {
221                 } else if (data[2] == GPCT_IndexPhaseLowHigh) {
222                 } else if (data[2] == GPCT_IndexPhaseHighLow) {
223                 } else if (data[2] == GPCT_IndexPhaseHighHigh) {
224                 }*/
225                 /*  Take into account the index pulse? */
226                 if (data[3] == GPCT_RESET_COUNTER_ON_INDEX)
227                         /*  Auto load with INDEX^ */
228                         cmReg.reg.autoLoadResetRcap = 4;
229
230                 /*  Set Counter Mode Register */
231                 cmReg.value = data[1] & 0xffff;
232                 outw(cmReg.value, chan_iobase + REG_C0M);
233
234                 /*  Load the pre-load register high word */
235                 val = (data[2] >> 16) & 0xffff;
236                 outw(val, chan_iobase + REG_C0H);
237
238                 /*  Load the pre-load register low word */
239                 val = data[2] & 0xffff;
240                 outw(val, chan_iobase + REG_C0L);
241
242                 /*  Write the Counter Control Register */
243                 if (data[3]) {
244                         val = data[3] & 0xffff;
245                         outw(val, chan_iobase + REG_C0C);
246                 }
247                 /*  Reset the counter if it is software preload */
248                 if (cmReg.reg.autoLoadResetRcap == 0) {
249                         /*  Reset the counter */
250                         outw(0x8000, chan_iobase + REG_C0C);
251                         /*  Load the counter from PR0 */
252                         outw(0x4000, chan_iobase + REG_C0C);
253                 }
254 #endif
255                 break;
256
257         case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
258                 /*
259                    data[0]: Application Type
260                    data[1]: Counter Mode Register Value
261                    data[2]: Pre-load Register 0 Value
262                    data[3]: Pre-load Register 1 Value
263                    data[4]: Conter Control Register
264                  */
265                 devpriv->gpct_config[chan] = data[0];
266
267                 /*  Set Counter Mode Register */
268                 cmReg.value = data[1] & 0xffff;
269                 cmReg.reg.preloadRegSel = 0;    /*  PR0 */
270                 outw(cmReg.value, chan_iobase + REG_C0M);
271
272                 /*  Load the pre-load register 0 high word */
273                 val = (data[2] >> 16) & 0xffff;
274                 outw(val, chan_iobase + REG_C0H);
275
276                 /*  Load the pre-load register 0 low word */
277                 val = data[2] & 0xffff;
278                 outw(val, chan_iobase + REG_C0L);
279
280                 /*  Set Counter Mode Register */
281                 cmReg.value = data[1] & 0xffff;
282                 cmReg.reg.preloadRegSel = 1;    /*  PR1 */
283                 outw(cmReg.value, chan_iobase + REG_C0M);
284
285                 /*  Load the pre-load register 1 high word */
286                 val = (data[3] >> 16) & 0xffff;
287                 outw(val, chan_iobase + REG_C0H);
288
289                 /*  Load the pre-load register 1 low word */
290                 val = data[3] & 0xffff;
291                 outw(val, chan_iobase + REG_C0L);
292
293                 /*  Write the Counter Control Register */
294                 if (data[4]) {
295                         val = data[4] & 0xffff;
296                         outw(val, chan_iobase + REG_C0C);
297                 }
298                 break;
299
300         case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
301                 /*
302                    data[0]: Application Type
303                    data[1]: Counter Mode Register Value
304                    data[2]: Pre-load Register 0 Value
305                    data[3]: Pre-load Register 1 Value
306                    data[4]: Conter Control Register
307                  */
308                 devpriv->gpct_config[chan] = data[0];
309
310                 /*  Set Counter Mode Register */
311                 cmReg.value = data[1] & 0xffff;
312                 cmReg.reg.preloadRegSel = 0;    /*  PR0 */
313                 outw(cmReg.value, chan_iobase + REG_C0M);
314
315                 /*  Load the pre-load register 0 high word */
316                 val = (data[2] >> 16) & 0xffff;
317                 outw(val, chan_iobase + REG_C0H);
318
319                 /*  Load the pre-load register 0 low word */
320                 val = data[2] & 0xffff;
321                 outw(val, chan_iobase + REG_C0L);
322
323                 /*  Set Counter Mode Register */
324                 cmReg.value = data[1] & 0xffff;
325                 cmReg.reg.preloadRegSel = 1;    /*  PR1 */
326                 outw(cmReg.value, chan_iobase + REG_C0M);
327
328                 /*  Load the pre-load register 1 high word */
329                 val = (data[3] >> 16) & 0xffff;
330                 outw(val, chan_iobase + REG_C0H);
331
332                 /*  Load the pre-load register 1 low word */
333                 val = data[3] & 0xffff;
334                 outw(val, chan_iobase + REG_C0L);
335
336                 /*  Write the Counter Control Register */
337                 if (data[4]) {
338                         val = data[4] & 0xffff;
339                         outw(val, chan_iobase + REG_C0C);
340                 }
341                 break;
342
343         default:
344                 return -EINVAL;
345         }
346
347         return insn->n;
348 }
349
350 static int s526_gpct_winsn(struct comedi_device *dev,
351                            struct comedi_subdevice *s,
352                            struct comedi_insn *insn,
353                            unsigned int *data)
354 {
355         struct s526_private *devpriv = dev->private;
356         unsigned int chan = CR_CHAN(insn->chanspec);
357         unsigned long chan_iobase = dev->iobase + chan * 8;
358
359         inw(chan_iobase + REG_C0M);     /* Is this read required? */
360
361         /*  Check what Application of Counter this channel is configured for */
362         switch (devpriv->gpct_config[chan]) {
363         case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
364                 /* data[0] contains the PULSE_WIDTH
365                    data[1] contains the PULSE_PERIOD
366                    @pre PULSE_PERIOD > PULSE_WIDTH > 0
367                    The above periods must be expressed as a multiple of the
368                    pulse frequency on the selected source
369                  */
370                 if ((data[1] <= data[0]) || !data[0])
371                         return -EINVAL;
372
373                 /* Fall thru to write the PULSE_WIDTH */
374
375         case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
376         case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
377                 outw((data[0] >> 16) & 0xffff, chan_iobase + REG_C0H);
378                 outw(data[0] & 0xffff, chan_iobase + REG_C0L);
379                 break;
380
381         default:
382                 return -EINVAL;
383         }
384
385         return insn->n;
386 }
387
388 #define ISR_ADC_DONE 0x4
389 static int s526_ai_insn_config(struct comedi_device *dev,
390                                struct comedi_subdevice *s,
391                                struct comedi_insn *insn, unsigned int *data)
392 {
393         struct s526_private *devpriv = dev->private;
394         int result = -EINVAL;
395
396         if (insn->n < 1)
397                 return result;
398
399         result = insn->n;
400
401         /* data[0] : channels was set in relevant bits.
402            data[1] : delay
403          */
404         /* COMMENT: abbotti 2008-07-24: I don't know why you'd want to
405          * enable channels here.  The channel should be enabled in the
406          * INSN_READ handler. */
407
408         /*  Enable ADC interrupt */
409         outw(ISR_ADC_DONE, dev->iobase + REG_IER);
410         devpriv->ai_config = (data[0] & 0x3ff) << 5;
411         if (data[1] > 0)
412                 devpriv->ai_config |= 0x8000;   /* set the delay */
413
414         devpriv->ai_config |= 0x0001;           /* ADC start bit */
415
416         return result;
417 }
418
419 static int s526_ai_eoc(struct comedi_device *dev,
420                        struct comedi_subdevice *s,
421                        struct comedi_insn *insn,
422                        unsigned long context)
423 {
424         unsigned int status;
425
426         status = inw(dev->iobase + REG_ISR);
427         if (status & ISR_ADC_DONE)
428                 return 0;
429         return -EBUSY;
430 }
431
432 static int s526_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
433                          struct comedi_insn *insn, unsigned int *data)
434 {
435         struct s526_private *devpriv = dev->private;
436         unsigned int chan = CR_CHAN(insn->chanspec);
437         int n;
438         unsigned short value;
439         unsigned int d;
440         int ret;
441
442         /* Set configured delay, enable channel for this channel only,
443          * select "ADC read" channel, set "ADC start" bit. */
444         value = (devpriv->ai_config & 0x8000) |
445                 ((1 << 5) << chan) | (chan << 1) | 0x0001;
446
447         /* convert n samples */
448         for (n = 0; n < insn->n; n++) {
449                 /* trigger conversion */
450                 outw(value, dev->iobase + REG_ADC);
451
452                 /* wait for conversion to end */
453                 ret = comedi_timeout(dev, s, insn, s526_ai_eoc, 0);
454                 if (ret)
455                         return ret;
456
457                 outw(ISR_ADC_DONE, dev->iobase + REG_ISR);
458
459                 /* read data */
460                 d = inw(dev->iobase + REG_ADD);
461
462                 /* munge data */
463                 data[n] = d ^ 0x8000;
464         }
465
466         /* return the number of samples read/written */
467         return n;
468 }
469
470 static int s526_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
471                          struct comedi_insn *insn, unsigned int *data)
472 {
473         struct s526_private *devpriv = dev->private;
474         unsigned int chan = CR_CHAN(insn->chanspec);
475         unsigned short val;
476         int i;
477
478         val = chan << 1;
479         outw(val, dev->iobase + REG_DAC);
480
481         for (i = 0; i < insn->n; i++) {
482                 outw(data[i], dev->iobase + REG_ADD);
483                 devpriv->ao_readback[chan] = data[i];
484                 /* starts the D/A conversion */
485                 outw(val + 1, dev->iobase + REG_DAC);
486         }
487
488         return i;
489 }
490
491 static int s526_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
492                          struct comedi_insn *insn, unsigned int *data)
493 {
494         struct s526_private *devpriv = dev->private;
495         unsigned int chan = CR_CHAN(insn->chanspec);
496         int i;
497
498         for (i = 0; i < insn->n; i++)
499                 data[i] = devpriv->ao_readback[chan];
500
501         return i;
502 }
503
504 static int s526_dio_insn_bits(struct comedi_device *dev,
505                               struct comedi_subdevice *s,
506                               struct comedi_insn *insn,
507                               unsigned int *data)
508 {
509         if (comedi_dio_update_state(s, data))
510                 outw(s->state, dev->iobase + REG_DIO);
511
512         data[1] = inw(dev->iobase + REG_DIO) & 0xff;
513
514         return insn->n;
515 }
516
517 static int s526_dio_insn_config(struct comedi_device *dev,
518                                 struct comedi_subdevice *s,
519                                 struct comedi_insn *insn,
520                                 unsigned int *data)
521 {
522         unsigned int chan = CR_CHAN(insn->chanspec);
523         unsigned int mask;
524         int ret;
525
526         if (chan < 4)
527                 mask = 0x0f;
528         else
529                 mask = 0xf0;
530
531         ret = comedi_dio_insn_config(dev, s, insn, data, mask);
532         if (ret)
533                 return ret;
534
535         /* bit 10/11 set the group 1/2's mode */
536         if (s->io_bits & 0x0f)
537                 s->state |= (1 << 10);
538         else
539                 s->state &= ~(1 << 10);
540         if (s->io_bits & 0xf0)
541                 s->state |= (1 << 11);
542         else
543                 s->state &= ~(1 << 11);
544
545         outw(s->state, dev->iobase + REG_DIO);
546
547         return insn->n;
548 }
549
550 static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it)
551 {
552         struct s526_private *devpriv;
553         struct comedi_subdevice *s;
554         int ret;
555
556         ret = comedi_request_region(dev, it->options[0], 0x40);
557         if (ret)
558                 return ret;
559
560         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
561         if (!devpriv)
562                 return -ENOMEM;
563
564         ret = comedi_alloc_subdevices(dev, 4);
565         if (ret)
566                 return ret;
567
568         s = &dev->subdevices[0];
569         /* GENERAL-PURPOSE COUNTER/TIME (GPCT) */
570         s->type = COMEDI_SUBD_COUNTER;
571         s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL;
572         s->n_chan = 4;
573         s->maxdata = 0x00ffffff;        /* 24 bit counter */
574         s->insn_read = s526_gpct_rinsn;
575         s->insn_config = s526_gpct_insn_config;
576         s->insn_write = s526_gpct_winsn;
577
578         s = &dev->subdevices[1];
579         /* analog input subdevice */
580         s->type = COMEDI_SUBD_AI;
581         s->subdev_flags = SDF_READABLE | SDF_DIFF;
582         /* channels 0 to 7 are the regular differential inputs */
583         /* channel 8 is "reference 0" (+10V), channel 9 is "reference 1" (0V) */
584         s->n_chan = 10;
585         s->maxdata = 0xffff;
586         s->range_table = &range_bipolar10;
587         s->len_chanlist = 16;
588         s->insn_read = s526_ai_rinsn;
589         s->insn_config = s526_ai_insn_config;
590
591         s = &dev->subdevices[2];
592         /* analog output subdevice */
593         s->type = COMEDI_SUBD_AO;
594         s->subdev_flags = SDF_WRITABLE;
595         s->n_chan = 4;
596         s->maxdata = 0xffff;
597         s->range_table = &range_bipolar10;
598         s->insn_write = s526_ao_winsn;
599         s->insn_read = s526_ao_rinsn;
600
601         s = &dev->subdevices[3];
602         /* digital i/o subdevice */
603         s->type = COMEDI_SUBD_DIO;
604         s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
605         s->n_chan = 8;
606         s->maxdata = 1;
607         s->range_table = &range_digital;
608         s->insn_bits = s526_dio_insn_bits;
609         s->insn_config = s526_dio_insn_config;
610
611         return 0;
612 }
613
614 static struct comedi_driver s526_driver = {
615         .driver_name    = "s526",
616         .module         = THIS_MODULE,
617         .attach         = s526_attach,
618         .detach         = comedi_legacy_detach,
619 };
620 module_comedi_driver(s526_driver);
621
622 MODULE_AUTHOR("Comedi http://www.comedi.org");
623 MODULE_DESCRIPTION("Comedi low-level driver");
624 MODULE_LICENSE("GPL");