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