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