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