Merge commit 'v3.7-rc1' into stable/for-linus-3.7
[firefly-linux-kernel-4.4.55.git] / drivers / staging / comedi / drivers / dt2811.c
1 /*
2    comedi/drivers/dt2811.c
3    Hardware driver for Data Translation DT2811
4
5    COMEDI - Linux Control and Measurement Device Interface
6    History:
7    Base Version  - David A. Schleef <ds@schleef.org>
8    December 1998 - Updated to work.  David does not have a DT2811
9    board any longer so this was suffering from bitrot.
10    Updated performed by ...
11
12    This program is free software; you can redistribute it and/or modify
13    it under the terms of the GNU General Public License as published by
14    the Free Software Foundation; either version 2 of the License, or
15    (at your option) any later version.
16
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20    GNU General Public License for more details.
21
22    You should have received a copy of the GNU General Public License
23    along with this program; if not, write to the Free Software
24    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25  */
26 /*
27 Driver: dt2811
28 Description: Data Translation DT2811
29 Author: ds
30 Devices: [Data Translation] DT2811-PGL (dt2811-pgl), DT2811-PGH (dt2811-pgh)
31 Status: works
32
33 Configuration options:
34   [0] - I/O port base address
35   [1] - IRQ, although this is currently unused
36   [2] - A/D reference
37           0 = signle-ended
38           1 = differential
39           2 = pseudo-differential (common reference)
40   [3] - A/D range
41           0 = [-5, 5]
42           1 = [-2.5, 2.5]
43           2 = [0, 5]
44   [4] - D/A 0 range (same choices)
45   [4] - D/A 1 range (same choices)
46 */
47
48 #include <linux/interrupt.h>
49 #include "../comedidev.h"
50
51 #include <linux/ioport.h>
52
53 static const char *driver_name = "dt2811";
54
55 static const struct comedi_lrange range_dt2811_pgh_ai_5_unipolar = {
56         4, {
57                 RANGE(0, 5),
58                 RANGE(0, 2.5),
59                 RANGE(0, 1.25),
60                 RANGE(0, 0.625)
61         }
62 };
63
64 static const struct comedi_lrange range_dt2811_pgh_ai_2_5_bipolar = {
65         4, {
66                 RANGE(-2.5, 2.5),
67                 RANGE(-1.25, 1.25),
68                 RANGE(-0.625, 0.625),
69                 RANGE(-0.3125, 0.3125)
70         }
71 };
72
73 static const struct comedi_lrange range_dt2811_pgh_ai_5_bipolar = {
74         4, {
75                 RANGE(-5, 5),
76                 RANGE(-2.5, 2.5),
77                 RANGE(-1.25, 1.25),
78                 RANGE(-0.625, 0.625)
79         }
80 };
81
82 static const struct comedi_lrange range_dt2811_pgl_ai_5_unipolar = {
83         4, {
84                 RANGE(0, 5),
85                 RANGE(0, 0.5),
86                 RANGE(0, 0.05),
87                 RANGE(0, 0.01)
88         }
89 };
90
91 static const struct comedi_lrange range_dt2811_pgl_ai_2_5_bipolar = {
92         4, {
93                 RANGE(-2.5, 2.5),
94                 RANGE(-0.25, 0.25),
95                 RANGE(-0.025, 0.025),
96                 RANGE(-0.005, 0.005)
97         }
98 };
99
100 static const struct comedi_lrange range_dt2811_pgl_ai_5_bipolar = {
101         4, {
102                 RANGE(-5, 5),
103                 RANGE(-0.5, 0.5),
104                 RANGE(-0.05, 0.05),
105                 RANGE(-0.01, 0.01)
106         }
107 };
108
109 /*
110
111    0x00    ADCSR R/W  A/D Control/Status Register
112    bit 7 - (R) 1 indicates A/D conversion done
113    reading ADDAT clears bit
114    (W) ignored
115    bit 6 - (R) 1 indicates A/D error
116    (W) ignored
117    bit 5 - (R) 1 indicates A/D busy, cleared at end
118    of conversion
119    (W) ignored
120    bit 4 - (R) 0
121    (W)
122    bit 3 - (R) 0
123    bit 2 - (R/W) 1 indicates interrupts enabled
124    bits 1,0 - (R/W) mode bits
125    00  single conversion on ADGCR load
126    01  continuous conversion, internal clock,
127    (clock enabled on ADGCR load)
128    10  continuous conversion, internal clock,
129    external trigger
130    11  continuous conversion, external clock,
131    external trigger
132
133    0x01    ADGCR R/W A/D Gain/Channel Register
134    bit 6,7 - (R/W) gain select
135    00  gain=1, both PGH, PGL models
136    01  gain=2 PGH, 10 PGL
137    10  gain=4 PGH, 100 PGL
138    11  gain=8 PGH, 500 PGL
139    bit 4,5 - reserved
140    bit 3-0 - (R/W) channel select
141    channel number from 0-15
142
143    0x02,0x03 (R) ADDAT A/D Data Register
144    (W) DADAT0 D/A Data Register 0
145    0x02 low byte
146    0x03 high byte
147
148    0x04,0x05 (W) DADAT0 D/A Data Register 1
149
150    0x06 (R) DIO0 Digital Input Port 0
151    (W) DIO1 Digital Output Port 1
152
153    0x07 TMRCTR (R/W) Timer/Counter Register
154    bits 6,7 - reserved
155    bits 5-3 - Timer frequency control (mantissa)
156    543  divisor  freqency (kHz)
157    000  1        600
158    001  10       60
159    010  2        300
160    011  3        200
161    100  4        150
162    101  5        120
163    110  6        100
164    111  12       50
165    bits 2-0 - Timer frequency control (exponent)
166    210  multiply divisor/divide frequency by
167    000  1
168    001  10
169    010  100
170    011  1000
171    100  10000
172    101  100000
173    110  1000000
174    111  10000000
175
176  */
177
178 #define TIMEOUT 10000
179
180 #define DT2811_SIZE 8
181
182 #define DT2811_ADCSR 0
183 #define DT2811_ADGCR 1
184 #define DT2811_ADDATLO 2
185 #define DT2811_ADDATHI 3
186 #define DT2811_DADAT0LO 2
187 #define DT2811_DADAT0HI 3
188 #define DT2811_DADAT1LO 4
189 #define DT2811_DADAT1HI 5
190 #define DT2811_DIO 6
191 #define DT2811_TMRCTR 7
192
193 /*
194  * flags
195  */
196
197 /* ADCSR */
198
199 #define DT2811_ADDONE   0x80
200 #define DT2811_ADERROR  0x40
201 #define DT2811_ADBUSY   0x20
202 #define DT2811_CLRERROR 0x10
203 #define DT2811_INTENB   0x04
204 #define DT2811_ADMODE   0x03
205
206 struct dt2811_board {
207
208         const char *name;
209         const struct comedi_lrange *bip_5;
210         const struct comedi_lrange *bip_2_5;
211         const struct comedi_lrange *unip_5;
212 };
213
214 enum { card_2811_pgh, card_2811_pgl };
215
216 struct dt2811_private {
217         int ntrig;
218         int curadchan;
219         enum {
220                 adc_singleended, adc_diff, adc_pseudo_diff
221         } adc_mux;
222         enum {
223                 dac_bipolar_5, dac_bipolar_2_5, dac_unipolar_5
224         } dac_range[2];
225         const struct comedi_lrange *range_type_list[2];
226         unsigned int ao_readback[2];
227 };
228
229 #define devpriv ((struct dt2811_private *)dev->private)
230
231 static const struct comedi_lrange *dac_range_types[] = {
232         &range_bipolar5,
233         &range_bipolar2_5,
234         &range_unipolar5
235 };
236
237 #define DT2811_TIMEOUT 5
238
239 #if 0
240 static irqreturn_t dt2811_interrupt(int irq, void *d)
241 {
242         int lo, hi;
243         int data;
244         struct comedi_device *dev = d;
245
246         if (!dev->attached) {
247                 comedi_error(dev, "spurious interrupt");
248                 return IRQ_HANDLED;
249         }
250
251         lo = inb(dev->iobase + DT2811_ADDATLO);
252         hi = inb(dev->iobase + DT2811_ADDATHI);
253
254         data = lo + (hi << 8);
255
256         if (!(--devpriv->ntrig)) {
257                 /* how to turn off acquisition */
258                 s->async->events |= COMEDI_SB_EOA;
259         }
260         comedi_event(dev, s);
261         return IRQ_HANDLED;
262 }
263 #endif
264
265 static int dt2811_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
266                           struct comedi_insn *insn, unsigned int *data)
267 {
268         int chan = CR_CHAN(insn->chanspec);
269         int timeout = DT2811_TIMEOUT;
270         int i;
271
272         for (i = 0; i < insn->n; i++) {
273                 outb(chan, dev->iobase + DT2811_ADGCR);
274
275                 while (timeout
276                        && inb(dev->iobase + DT2811_ADCSR) & DT2811_ADBUSY)
277                         timeout--;
278                 if (!timeout)
279                         return -ETIME;
280
281                 data[i] = inb(dev->iobase + DT2811_ADDATLO);
282                 data[i] |= inb(dev->iobase + DT2811_ADDATHI) << 8;
283                 data[i] &= 0xfff;
284         }
285
286         return i;
287 }
288
289 #if 0
290 /* Wow.  This is code from the Comedi stone age.  But it hasn't been
291  * replaced, so I'll let it stay. */
292 int dt2811_adtrig(kdev_t minor, comedi_adtrig *adtrig)
293 {
294         struct comedi_device *dev = comedi_devices + minor;
295
296         if (adtrig->n < 1)
297                 return 0;
298         dev->curadchan = adtrig->chan;
299         switch (dev->i_admode) {
300         case COMEDI_MDEMAND:
301                 dev->ntrig = adtrig->n - 1;
302                 /* not necessary */
303                 /*printk("dt2811: AD soft trigger\n"); */
304                 /*outb(DT2811_CLRERROR|DT2811_INTENB,
305                         dev->iobase+DT2811_ADCSR); */
306                 outb(dev->curadchan, dev->iobase + DT2811_ADGCR);
307                 do_gettimeofday(&trigtime);
308                 break;
309         case COMEDI_MCONTS:
310                 dev->ntrig = adtrig->n;
311                 break;
312         }
313
314         return 0;
315 }
316 #endif
317
318 static int dt2811_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
319                           struct comedi_insn *insn, unsigned int *data)
320 {
321         int i;
322         int chan;
323
324         chan = CR_CHAN(insn->chanspec);
325
326         for (i = 0; i < insn->n; i++) {
327                 outb(data[i] & 0xff, dev->iobase + DT2811_DADAT0LO + 2 * chan);
328                 outb((data[i] >> 8) & 0xff,
329                      dev->iobase + DT2811_DADAT0HI + 2 * chan);
330                 devpriv->ao_readback[chan] = data[i];
331         }
332
333         return i;
334 }
335
336 static int dt2811_ao_insn_read(struct comedi_device *dev,
337                                struct comedi_subdevice *s,
338                                struct comedi_insn *insn, unsigned int *data)
339 {
340         int i;
341         int chan;
342
343         chan = CR_CHAN(insn->chanspec);
344
345         for (i = 0; i < insn->n; i++)
346                 data[i] = devpriv->ao_readback[chan];
347
348         return i;
349 }
350
351 static int dt2811_di_insn_bits(struct comedi_device *dev,
352                                struct comedi_subdevice *s,
353                                struct comedi_insn *insn, unsigned int *data)
354 {
355         data[1] = inb(dev->iobase + DT2811_DIO);
356
357         return insn->n;
358 }
359
360 static int dt2811_do_insn_bits(struct comedi_device *dev,
361                                struct comedi_subdevice *s,
362                                struct comedi_insn *insn, unsigned int *data)
363 {
364         s->state &= ~data[0];
365         s->state |= data[0] & data[1];
366         outb(s->state, dev->iobase + DT2811_DIO);
367
368         data[1] = s->state;
369
370         return insn->n;
371 }
372
373 /*
374   options[0]   Board base address
375   options[1]   IRQ
376   options[2]   Input configuration
377                  0 == single-ended
378                  1 == differential
379                  2 == pseudo-differential
380   options[3]   Analog input range configuration
381                  0 == bipolar 5  (-5V -- +5V)
382                  1 == bipolar 2.5V  (-2.5V -- +2.5V)
383                  2 == unipolar 5V  (0V -- +5V)
384   options[4]   Analog output 0 range configuration
385                  0 == bipolar 5  (-5V -- +5V)
386                  1 == bipolar 2.5V  (-2.5V -- +2.5V)
387                  2 == unipolar 5V  (0V -- +5V)
388   options[5]   Analog output 1 range configuration
389                  0 == bipolar 5  (-5V -- +5V)
390                  1 == bipolar 2.5V  (-2.5V -- +2.5V)
391                  2 == unipolar 5V  (0V -- +5V)
392 */
393 static int dt2811_attach(struct comedi_device *dev, struct comedi_devconfig *it)
394 {
395         /* int i, irq; */
396         /* unsigned long irqs; */
397         /* long flags; */
398
399         const struct dt2811_board *board = comedi_board(dev);
400         int ret;
401         struct comedi_subdevice *s;
402         unsigned long iobase;
403
404         iobase = it->options[0];
405
406         printk(KERN_INFO "comedi%d: dt2811:base=0x%04lx\n", dev->minor, iobase);
407
408         if (!request_region(iobase, DT2811_SIZE, driver_name)) {
409                 printk(KERN_ERR "I/O port conflict\n");
410                 return -EIO;
411         }
412
413         dev->iobase = iobase;
414         dev->board_name = board->name;
415
416 #if 0
417         outb(0, dev->iobase + DT2811_ADCSR);
418         udelay(100);
419         i = inb(dev->iobase + DT2811_ADDATLO);
420         i = inb(dev->iobase + DT2811_ADDATHI);
421 #endif
422
423 #if 0
424         irq = it->options[1];
425         if (irq < 0) {
426                 save_flags(flags);
427                 sti();
428                 irqs = probe_irq_on();
429
430                 outb(DT2811_CLRERROR | DT2811_INTENB,
431                      dev->iobase + DT2811_ADCSR);
432                 outb(0, dev->iobase + DT2811_ADGCR);
433
434                 udelay(100);
435
436                 irq = probe_irq_off(irqs);
437                 restore_flags(flags);
438
439                 /*outb(DT2811_CLRERROR|DT2811_INTENB,
440                         dev->iobase+DT2811_ADCSR);*/
441
442                 if (inb(dev->iobase + DT2811_ADCSR) & DT2811_ADERROR)
443                         printk(KERN_ERR "error probing irq (bad)\n");
444                 dev->irq = 0;
445                 if (irq > 0) {
446                         i = inb(dev->iobase + DT2811_ADDATLO);
447                         i = inb(dev->iobase + DT2811_ADDATHI);
448                         printk(KERN_INFO "(irq = %d)\n", irq);
449                         ret = request_irq(irq, dt2811_interrupt, 0,
450                                           driver_name, dev);
451                         if (ret < 0)
452                                 return -EIO;
453                         dev->irq = irq;
454                 } else if (irq == 0) {
455                         printk(KERN_INFO "(no irq)\n");
456                 } else {
457                         printk(KERN_ERR "( multiple irq's -- this is bad! )\n");
458                 }
459         }
460 #endif
461
462         ret = comedi_alloc_subdevices(dev, 4);
463         if (ret)
464                 return ret;
465
466         ret = alloc_private(dev, sizeof(struct dt2811_private));
467         if (ret < 0)
468                 return ret;
469
470         switch (it->options[2]) {
471         case 0:
472                 devpriv->adc_mux = adc_singleended;
473                 break;
474         case 1:
475                 devpriv->adc_mux = adc_diff;
476                 break;
477         case 2:
478                 devpriv->adc_mux = adc_pseudo_diff;
479                 break;
480         default:
481                 devpriv->adc_mux = adc_singleended;
482                 break;
483         }
484         switch (it->options[4]) {
485         case 0:
486                 devpriv->dac_range[0] = dac_bipolar_5;
487                 break;
488         case 1:
489                 devpriv->dac_range[0] = dac_bipolar_2_5;
490                 break;
491         case 2:
492                 devpriv->dac_range[0] = dac_unipolar_5;
493                 break;
494         default:
495                 devpriv->dac_range[0] = dac_bipolar_5;
496                 break;
497         }
498         switch (it->options[5]) {
499         case 0:
500                 devpriv->dac_range[1] = dac_bipolar_5;
501                 break;
502         case 1:
503                 devpriv->dac_range[1] = dac_bipolar_2_5;
504                 break;
505         case 2:
506                 devpriv->dac_range[1] = dac_unipolar_5;
507                 break;
508         default:
509                 devpriv->dac_range[1] = dac_bipolar_5;
510                 break;
511         }
512
513         s = &dev->subdevices[0];
514         /* initialize the ADC subdevice */
515         s->type = COMEDI_SUBD_AI;
516         s->subdev_flags = SDF_READABLE | SDF_GROUND;
517         s->n_chan = devpriv->adc_mux == adc_diff ? 8 : 16;
518         s->insn_read = dt2811_ai_insn;
519         s->maxdata = 0xfff;
520         switch (it->options[3]) {
521         case 0:
522         default:
523                 s->range_table = board->bip_5;
524                 break;
525         case 1:
526                 s->range_table = board->bip_2_5;
527                 break;
528         case 2:
529                 s->range_table = board->unip_5;
530                 break;
531         }
532
533         s = &dev->subdevices[1];
534         /* ao subdevice */
535         s->type = COMEDI_SUBD_AO;
536         s->subdev_flags = SDF_WRITABLE;
537         s->n_chan = 2;
538         s->insn_write = dt2811_ao_insn;
539         s->insn_read = dt2811_ao_insn_read;
540         s->maxdata = 0xfff;
541         s->range_table_list = devpriv->range_type_list;
542         devpriv->range_type_list[0] = dac_range_types[devpriv->dac_range[0]];
543         devpriv->range_type_list[1] = dac_range_types[devpriv->dac_range[1]];
544
545         s = &dev->subdevices[2];
546         /* di subdevice */
547         s->type = COMEDI_SUBD_DI;
548         s->subdev_flags = SDF_READABLE;
549         s->n_chan = 8;
550         s->insn_bits = dt2811_di_insn_bits;
551         s->maxdata = 1;
552         s->range_table = &range_digital;
553
554         s = &dev->subdevices[3];
555         /* do subdevice */
556         s->type = COMEDI_SUBD_DO;
557         s->subdev_flags = SDF_WRITABLE;
558         s->n_chan = 8;
559         s->insn_bits = dt2811_do_insn_bits;
560         s->maxdata = 1;
561         s->state = 0;
562         s->range_table = &range_digital;
563
564         return 0;
565 }
566
567 static void dt2811_detach(struct comedi_device *dev)
568 {
569         if (dev->irq)
570                 free_irq(dev->irq, dev);
571         if (dev->iobase)
572                 release_region(dev->iobase, DT2811_SIZE);
573 }
574
575 static const struct dt2811_board boardtypes[] = {
576         {
577                 .name           = "dt2811-pgh",
578                 .bip_5          = &range_dt2811_pgh_ai_5_bipolar,
579                 .bip_2_5        = &range_dt2811_pgh_ai_2_5_bipolar,
580                 .unip_5         = &range_dt2811_pgh_ai_5_unipolar,
581         }, {
582                 .name           = "dt2811-pgl",
583                 .bip_5          = &range_dt2811_pgl_ai_5_bipolar,
584                 .bip_2_5        = &range_dt2811_pgl_ai_2_5_bipolar,
585                 .unip_5         = &range_dt2811_pgl_ai_5_unipolar,
586         },
587 };
588
589 static struct comedi_driver dt2811_driver = {
590         .driver_name    = "dt2811",
591         .module         = THIS_MODULE,
592         .attach         = dt2811_attach,
593         .detach         = dt2811_detach,
594         .board_name     = &boardtypes[0].name,
595         .num_names      = ARRAY_SIZE(boardtypes),
596         .offset         = sizeof(struct dt2811_board),
597 };
598 module_comedi_driver(dt2811_driver);
599
600 MODULE_AUTHOR("Comedi http://www.comedi.org");
601 MODULE_DESCRIPTION("Comedi low-level driver");
602 MODULE_LICENSE("GPL");