51ef695698a3721bfa858a2411516bfc3d3d004a
[GitHub/mt8127/android_kernel_alcatel_ttab.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 }