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