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