These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / staging / comedi / drivers / s526.c
1 /*
2  * 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 /*
20  * Driver: s526
21  * Description: Sensoray 526 driver
22  * Devices: [Sensoray] 526 (s526)
23  * Author: Richie
24  *         Everett Wang <everett.wang@everteq.com>
25  * Updated: Thu, 14 Sep. 2006
26  * Status: experimental
27  *
28  * Encoder works
29  * Analog input works
30  * Analog output works
31  * PWM output works
32  * Commands are not supported yet.
33  *
34  * Configuration Options:
35  *   [0] - I/O port base address
36  */
37
38 #include <linux/module.h>
39 #include "../comedidev.h"
40 #include <asm/byteorder.h>
41
42 /*
43  * Register I/O map
44  */
45 #define S526_TIMER_REG          0x00
46 #define S526_TIMER_LOAD(x)      (((x) & 0xff) << 8)
47 #define S526_TIMER_MODE         ((x) << 1)
48 #define S526_TIMER_MANUAL       S526_TIMER_MODE(0)
49 #define S526_TIMER_AUTO         S526_TIMER_MODE(1)
50 #define S526_TIMER_RESTART      BIT(0)
51 #define S526_WDOG_REG           0x02
52 #define S526_WDOG_INVERTED      BIT(4)
53 #define S526_WDOG_ENA           BIT(3)
54 #define S526_WDOG_INTERVAL(x)   (((x) & 0x7) << 0)
55 #define S526_AO_CTRL_REG        0x04
56 #define S526_AO_CTRL_RESET      BIT(3)
57 #define S526_AO_CTRL_CHAN(x)    (((x) & 0x3) << 1)
58 #define S526_AO_CTRL_START      BIT(0)
59 #define S526_AI_CTRL_REG        0x06
60 #define S526_AI_CTRL_DELAY      BIT(15)
61 #define S526_AI_CTRL_CONV(x)    (1 << (5 + ((x) & 0x9)))
62 #define S526_AI_CTRL_READ(x)    (((x) & 0xf) << 1)
63 #define S526_AI_CTRL_START      BIT(0)
64 #define S526_AO_REG             0x08
65 #define S526_AI_REG             0x08
66 #define S526_DIO_CTRL_REG       0x0a
67 #define S526_DIO_CTRL_DIO3_NEG  BIT(15) /* irq on DIO3 neg/pos edge */
68 #define S526_DIO_CTRL_DIO2_NEG  BIT(14) /* irq on DIO2 neg/pos edge */
69 #define S526_DIO_CTRL_DIO1_NEG  BIT(13) /* irq on DIO1 neg/pos edge */
70 #define S526_DIO_CTRL_DIO0_NEG  BIT(12) /* irq on DIO0 neg/pos edge */
71 #define S526_DIO_CTRL_GRP2_OUT  BIT(11)
72 #define S526_DIO_CTRL_GRP1_OUT  BIT(10)
73 #define S526_DIO_CTRL_GRP2_NEG  BIT(8)  /* irq on DIO[4-7] neg/pos edge */
74 #define S526_INT_ENA_REG        0x0c
75 #define S526_INT_STATUS_REG     0x0e
76 #define S526_INT_DIO(x)         BIT(8 + ((x) & 0x7))
77 #define S526_INT_EEPROM         BIT(7)  /* status only */
78 #define S526_INT_CNTR(x)        BIT(3 + (3 - ((x) & 0x3)))
79 #define S526_INT_AI             BIT(2)
80 #define S526_INT_AO             BIT(1)
81 #define S526_INT_TIMER          BIT(0)
82 #define S526_MISC_REG           0x10
83 #define S526_MISC_LED_OFF       BIT(0)
84 #define S526_GPCT_LSB_REG(x)    (0x12 + ((x) * 8))
85 #define S526_GPCT_MSB_REG(x)    (0x14 + ((x) * 8))
86 #define S526_GPCT_MODE_REG(x)   (0x16 + ((x) * 8))
87 #define S526_GPCT_CTRL_REG(x)   (0x18 + ((x) * 8))
88 #define S526_EEPROM_DATA_REG    0x32
89 #define S526_EEPROM_CTRL_REG    0x34
90 #define S526_EEPROM_CTRL_ADDR(x) (((x) & 0x3f) << 3)
91 #define S526_EEPROM_CTRL(x)     (((x) & 0x3) << 1)
92 #define S526_EEPROM_CTRL_READ   S526_EEPROM_CTRL(2)
93 #define S526_EEPROM_CTRL_START  BIT(0)
94
95 struct counter_mode_register_t {
96 #if defined(__LITTLE_ENDIAN_BITFIELD)
97         unsigned short coutSource:1;
98         unsigned short coutPolarity:1;
99         unsigned short autoLoadResetRcap:3;
100         unsigned short hwCtEnableSource:2;
101         unsigned short ctEnableCtrl:2;
102         unsigned short clockSource:2;
103         unsigned short countDir:1;
104         unsigned short countDirCtrl:1;
105         unsigned short outputRegLatchCtrl:1;
106         unsigned short preloadRegSel:1;
107         unsigned short reserved:1;
108  #elif defined(__BIG_ENDIAN_BITFIELD)
109         unsigned short reserved:1;
110         unsigned short preloadRegSel:1;
111         unsigned short outputRegLatchCtrl:1;
112         unsigned short countDirCtrl:1;
113         unsigned short countDir:1;
114         unsigned short clockSource:2;
115         unsigned short ctEnableCtrl:2;
116         unsigned short hwCtEnableSource:2;
117         unsigned short autoLoadResetRcap:3;
118         unsigned short coutPolarity:1;
119         unsigned short coutSource:1;
120 #else
121 #error Unknown bit field order
122 #endif
123 };
124
125 union cmReg {
126         struct counter_mode_register_t reg;
127         unsigned short value;
128 };
129
130 struct s526_private {
131         unsigned int gpct_config[4];
132         unsigned short ai_ctrl;
133 };
134
135 static void s526_gpct_write(struct comedi_device *dev,
136                             unsigned int chan, unsigned int val)
137 {
138         /* write high word then low word */
139         outw((val >> 16) & 0xffff, dev->iobase + S526_GPCT_MSB_REG(chan));
140         outw(val & 0xffff, dev->iobase + S526_GPCT_LSB_REG(chan));
141 }
142
143 static unsigned int s526_gpct_read(struct comedi_device *dev,
144                                    unsigned int chan)
145 {
146         unsigned int val;
147
148         /* read the low word then high word */
149         val = inw(dev->iobase + S526_GPCT_LSB_REG(chan)) & 0xffff;
150         val |= (inw(dev->iobase + S526_GPCT_MSB_REG(chan)) & 0xff) << 16;
151
152         return val;
153 }
154
155 static int s526_gpct_rinsn(struct comedi_device *dev,
156                            struct comedi_subdevice *s,
157                            struct comedi_insn *insn,
158                            unsigned int *data)
159 {
160         unsigned int chan = CR_CHAN(insn->chanspec);
161         int i;
162
163         for (i = 0; i < insn->n; i++)
164                 data[i] = s526_gpct_read(dev, chan);
165
166         return insn->n;
167 }
168
169 static int s526_gpct_insn_config(struct comedi_device *dev,
170                                  struct comedi_subdevice *s,
171                                  struct comedi_insn *insn,
172                                  unsigned int *data)
173 {
174         struct s526_private *devpriv = dev->private;
175         unsigned int chan = CR_CHAN(insn->chanspec);
176         unsigned int val;
177         union cmReg cmReg;
178
179         /*
180          * Check what type of Counter the user requested
181          * data[0] contains the Application type
182          */
183         switch (data[0]) {
184         case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
185                 /*
186                  * data[0]: Application Type
187                  * data[1]: Counter Mode Register Value
188                  * data[2]: Pre-load Register Value
189                  * data[3]: Conter Control Register
190                  */
191                 devpriv->gpct_config[chan] = data[0];
192
193 #if 1
194                 /*  Set Counter Mode Register */
195                 cmReg.value = data[1] & 0xffff;
196                 outw(cmReg.value, dev->iobase + S526_GPCT_MODE_REG(chan));
197
198                 /*  Reset the counter if it is software preload */
199                 if (cmReg.reg.autoLoadResetRcap == 0) {
200                         /*  Reset the counter */
201                         outw(0x8000, dev->iobase + S526_GPCT_CTRL_REG(chan));
202                         /* Load the counter from PR0
203                          * outw(0x4000, dev->iobase + S526_GPCT_CTRL_REG(chan));
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                 /*
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                  */
226                 /*  Take into account the index pulse? */
227                 if (data[3] == GPCT_RESET_COUNTER_ON_INDEX)
228                         /*  Auto load with INDEX^ */
229                         cmReg.reg.autoLoadResetRcap = 4;
230
231                 /*  Set Counter Mode Register */
232                 cmReg.value = data[1] & 0xffff;
233                 outw(cmReg.value, dev->iobase + S526_GPCT_MODE_REG(chan));
234
235                 /*  Load the pre-load register */
236                 s526_gpct_write(dev, chan, data[2]);
237
238                 /*  Write the Counter Control Register */
239                 if (data[3])
240                         outw(data[3] & 0xffff,
241                              dev->iobase + S526_GPCT_CTRL_REG(chan));
242
243                 /*  Reset the counter if it is software preload */
244                 if (cmReg.reg.autoLoadResetRcap == 0) {
245                         /*  Reset the counter */
246                         outw(0x8000, dev->iobase + S526_GPCT_CTRL_REG(chan));
247                         /*  Load the counter from PR0 */
248                         outw(0x4000, dev->iobase + S526_GPCT_CTRL_REG(chan));
249                 }
250 #endif
251                 break;
252
253         case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
254                 /*
255                  * data[0]: Application Type
256                  * data[1]: Counter Mode Register Value
257                  * data[2]: Pre-load Register 0 Value
258                  * data[3]: Pre-load Register 1 Value
259                  * data[4]: Conter Control Register
260                  */
261                 devpriv->gpct_config[chan] = data[0];
262
263                 /*  Set Counter Mode Register */
264                 cmReg.value = data[1] & 0xffff;
265                 cmReg.reg.preloadRegSel = 0;    /*  PR0 */
266                 outw(cmReg.value, dev->iobase + S526_GPCT_MODE_REG(chan));
267
268                 /* Load the pre-load register 0 */
269                 s526_gpct_write(dev, chan, data[2]);
270
271                 /*  Set Counter Mode Register */
272                 cmReg.value = data[1] & 0xffff;
273                 cmReg.reg.preloadRegSel = 1;    /*  PR1 */
274                 outw(cmReg.value, dev->iobase + S526_GPCT_MODE_REG(chan));
275
276                 /* Load the pre-load register 1 */
277                 s526_gpct_write(dev, chan, data[3]);
278
279                 /*  Write the Counter Control Register */
280                 if (data[4]) {
281                         val = data[4] & 0xffff;
282                         outw(val, dev->iobase + S526_GPCT_CTRL_REG(chan));
283                 }
284                 break;
285
286         case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
287                 /*
288                  * data[0]: Application Type
289                  * data[1]: Counter Mode Register Value
290                  * data[2]: Pre-load Register 0 Value
291                  * data[3]: Pre-load Register 1 Value
292                  * data[4]: Conter Control Register
293                  */
294                 devpriv->gpct_config[chan] = data[0];
295
296                 /*  Set Counter Mode Register */
297                 cmReg.value = data[1] & 0xffff;
298                 cmReg.reg.preloadRegSel = 0;    /*  PR0 */
299                 outw(cmReg.value, dev->iobase + S526_GPCT_MODE_REG(chan));
300
301                 /* Load the pre-load register 0 */
302                 s526_gpct_write(dev, chan, data[2]);
303
304                 /*  Set Counter Mode Register */
305                 cmReg.value = data[1] & 0xffff;
306                 cmReg.reg.preloadRegSel = 1;    /*  PR1 */
307                 outw(cmReg.value, dev->iobase + S526_GPCT_MODE_REG(chan));
308
309                 /* Load the pre-load register 1 */
310                 s526_gpct_write(dev, chan, data[3]);
311
312                 /*  Write the Counter Control Register */
313                 if (data[4]) {
314                         val = data[4] & 0xffff;
315                         outw(val, dev->iobase + S526_GPCT_CTRL_REG(chan));
316                 }
317                 break;
318
319         default:
320                 return -EINVAL;
321         }
322
323         return insn->n;
324 }
325
326 static int s526_gpct_winsn(struct comedi_device *dev,
327                            struct comedi_subdevice *s,
328                            struct comedi_insn *insn,
329                            unsigned int *data)
330 {
331         struct s526_private *devpriv = dev->private;
332         unsigned int chan = CR_CHAN(insn->chanspec);
333
334         inw(dev->iobase + S526_GPCT_MODE_REG(chan));    /* Is this required? */
335
336         /*  Check what Application of Counter this channel is configured for */
337         switch (devpriv->gpct_config[chan]) {
338         case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
339                 /*
340                  * data[0] contains the PULSE_WIDTH
341                  * data[1] contains the PULSE_PERIOD
342                  * @pre PULSE_PERIOD > PULSE_WIDTH > 0
343                  * The above periods must be expressed as a multiple of the
344                  * pulse frequency on the selected source
345                  */
346                 if ((data[1] <= data[0]) || !data[0])
347                         return -EINVAL;
348
349                 /* Fall thru to write the PULSE_WIDTH */
350
351         case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
352         case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
353                 s526_gpct_write(dev, chan, data[0]);
354                 break;
355
356         default:
357                 return -EINVAL;
358         }
359
360         return insn->n;
361 }
362
363 static int s526_eoc(struct comedi_device *dev,
364                     struct comedi_subdevice *s,
365                     struct comedi_insn *insn,
366                     unsigned long context)
367 {
368         unsigned int status;
369
370         status = inw(dev->iobase + S526_INT_STATUS_REG);
371         if (status & context) {
372                 /* we got our eoc event, clear it */
373                 outw(context, dev->iobase + S526_INT_STATUS_REG);
374                 return 0;
375         }
376         return -EBUSY;
377 }
378
379 static int s526_ai_insn_read(struct comedi_device *dev,
380                              struct comedi_subdevice *s,
381                              struct comedi_insn *insn,
382                              unsigned int *data)
383 {
384         struct s526_private *devpriv = dev->private;
385         unsigned int chan = CR_CHAN(insn->chanspec);
386         unsigned int ctrl;
387         unsigned int val;
388         int ret;
389         int i;
390
391         ctrl = S526_AI_CTRL_CONV(chan) | S526_AI_CTRL_READ(chan) |
392                S526_AI_CTRL_START;
393         if (ctrl != devpriv->ai_ctrl) {
394                 /*
395                  * The multiplexor needs to change, enable the 15us
396                  * delay for the first sample.
397                  */
398                 devpriv->ai_ctrl = ctrl;
399                 ctrl |= S526_AI_CTRL_DELAY;
400         }
401
402         for (i = 0; i < insn->n; i++) {
403                 /* trigger conversion */
404                 outw(ctrl, dev->iobase + S526_AI_CTRL_REG);
405                 ctrl &= ~S526_AI_CTRL_DELAY;
406
407                 /* wait for conversion to end */
408                 ret = comedi_timeout(dev, s, insn, s526_eoc, S526_INT_AI);
409                 if (ret)
410                         return ret;
411
412                 val = inw(dev->iobase + S526_AI_REG);
413                 data[i] = comedi_offset_munge(s, val);
414         }
415
416         return insn->n;
417 }
418
419 static int s526_ao_insn_write(struct comedi_device *dev,
420                               struct comedi_subdevice *s,
421                               struct comedi_insn *insn,
422                               unsigned int *data)
423 {
424         unsigned int chan = CR_CHAN(insn->chanspec);
425         unsigned int ctrl = S526_AO_CTRL_CHAN(chan);
426         unsigned int val = s->readback[chan];
427         int ret;
428         int i;
429
430         outw(ctrl, dev->iobase + S526_AO_CTRL_REG);
431         ctrl |= S526_AO_CTRL_START;
432
433         for (i = 0; i < insn->n; i++) {
434                 val = data[i];
435                 outw(val, dev->iobase + S526_AO_REG);
436                 outw(ctrl, dev->iobase + S526_AO_CTRL_REG);
437
438                 /* wait for conversion to end */
439                 ret = comedi_timeout(dev, s, insn, s526_eoc, S526_INT_AO);
440                 if (ret)
441                         return ret;
442         }
443         s->readback[chan] = val;
444
445         return insn->n;
446 }
447
448 static int s526_dio_insn_bits(struct comedi_device *dev,
449                               struct comedi_subdevice *s,
450                               struct comedi_insn *insn,
451                               unsigned int *data)
452 {
453         if (comedi_dio_update_state(s, data))
454                 outw(s->state, dev->iobase + S526_DIO_CTRL_REG);
455
456         data[1] = inw(dev->iobase + S526_DIO_CTRL_REG) & 0xff;
457
458         return insn->n;
459 }
460
461 static int s526_dio_insn_config(struct comedi_device *dev,
462                                 struct comedi_subdevice *s,
463                                 struct comedi_insn *insn,
464                                 unsigned int *data)
465 {
466         unsigned int chan = CR_CHAN(insn->chanspec);
467         unsigned int mask;
468         int ret;
469
470         /*
471          * Digital I/O can be configured as inputs or outputs in
472          * groups of 4; DIO group 1 (DIO0-3) and DIO group 2 (DIO4-7).
473          */
474         if (chan < 4)
475                 mask = 0x0f;
476         else
477                 mask = 0xf0;
478
479         ret = comedi_dio_insn_config(dev, s, insn, data, mask);
480         if (ret)
481                 return ret;
482
483         if (s->io_bits & 0x0f)
484                 s->state |= S526_DIO_CTRL_GRP1_OUT;
485         else
486                 s->state &= ~S526_DIO_CTRL_GRP1_OUT;
487         if (s->io_bits & 0xf0)
488                 s->state |= S526_DIO_CTRL_GRP2_OUT;
489         else
490                 s->state &= ~S526_DIO_CTRL_GRP2_OUT;
491
492         outw(s->state, dev->iobase + S526_DIO_CTRL_REG);
493
494         return insn->n;
495 }
496
497 static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it)
498 {
499         struct s526_private *devpriv;
500         struct comedi_subdevice *s;
501         int ret;
502
503         ret = comedi_request_region(dev, it->options[0], 0x40);
504         if (ret)
505                 return ret;
506
507         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
508         if (!devpriv)
509                 return -ENOMEM;
510
511         ret = comedi_alloc_subdevices(dev, 4);
512         if (ret)
513                 return ret;
514
515         /* General-Purpose Counter/Timer (GPCT) */
516         s = &dev->subdevices[0];
517         s->type         = COMEDI_SUBD_COUNTER;
518         s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL;
519         s->n_chan       = 4;
520         s->maxdata      = 0x00ffffff;
521         s->insn_read    = s526_gpct_rinsn;
522         s->insn_config  = s526_gpct_insn_config;
523         s->insn_write   = s526_gpct_winsn;
524
525         /*
526          * Analog Input subdevice
527          * channels 0 to 7 are the regular differential inputs
528          * channel 8 is "reference 0" (+10V)
529          * channel 9 is "reference 1" (0V)
530          */
531         s = &dev->subdevices[1];
532         s->type         = COMEDI_SUBD_AI;
533         s->subdev_flags = SDF_READABLE | SDF_DIFF;
534         s->n_chan       = 10;
535         s->maxdata      = 0xffff;
536         s->range_table  = &range_bipolar10;
537         s->len_chanlist = 16;
538         s->insn_read    = s526_ai_insn_read;
539
540         /* Analog Output subdevice */
541         s = &dev->subdevices[2];
542         s->type         = COMEDI_SUBD_AO;
543         s->subdev_flags = SDF_WRITABLE;
544         s->n_chan       = 4;
545         s->maxdata      = 0xffff;
546         s->range_table  = &range_bipolar10;
547         s->insn_write   = s526_ao_insn_write;
548
549         ret = comedi_alloc_subdev_readback(s);
550         if (ret)
551                 return ret;
552
553         /* Digital I/O subdevice */
554         s = &dev->subdevices[3];
555         s->type         = COMEDI_SUBD_DIO;
556         s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
557         s->n_chan       = 8;
558         s->maxdata      = 1;
559         s->range_table  = &range_digital;
560         s->insn_bits    = s526_dio_insn_bits;
561         s->insn_config  = s526_dio_insn_config;
562
563         return 0;
564 }
565
566 static struct comedi_driver s526_driver = {
567         .driver_name    = "s526",
568         .module         = THIS_MODULE,
569         .attach         = s526_attach,
570         .detach         = comedi_legacy_detach,
571 };
572 module_comedi_driver(s526_driver);
573
574 MODULE_AUTHOR("Comedi http://www.comedi.org");
575 MODULE_DESCRIPTION("Comedi low-level driver");
576 MODULE_LICENSE("GPL");