a1664caa1d961fd89cf637f6c1a66efa0da093b3
[linux-flexiantxendom0-3.2.10.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 static const struct dt2811_board boardtypes[] = {
215         {"dt2811-pgh",
216          &range_dt2811_pgh_ai_5_bipolar,
217          &range_dt2811_pgh_ai_2_5_bipolar,
218          &range_dt2811_pgh_ai_5_unipolar,
219          },
220         {"dt2811-pgl",
221          &range_dt2811_pgl_ai_5_bipolar,
222          &range_dt2811_pgl_ai_2_5_bipolar,
223          &range_dt2811_pgl_ai_5_unipolar,
224          },
225 };
226
227 #define this_board ((const struct dt2811_board *)dev->board_ptr)
228
229 static int dt2811_attach(struct comedi_device *dev,
230                          struct comedi_devconfig *it);
231 static int dt2811_detach(struct comedi_device *dev);
232 static struct comedi_driver driver_dt2811 = {
233         .driver_name = "dt2811",
234         .module = THIS_MODULE,
235         .attach = dt2811_attach,
236         .detach = dt2811_detach,
237         .board_name = &boardtypes[0].name,
238         .num_names = ARRAY_SIZE(boardtypes),
239         .offset = sizeof(struct dt2811_board),
240 };
241
242 static int __init driver_dt2811_init_module(void)
243 {
244         return comedi_driver_register(&driver_dt2811);
245 }
246
247 static void __exit driver_dt2811_cleanup_module(void)
248 {
249         comedi_driver_unregister(&driver_dt2811);
250 }
251
252 module_init(driver_dt2811_init_module);
253 module_exit(driver_dt2811_cleanup_module);
254
255 static int dt2811_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
256                           struct comedi_insn *insn, unsigned int *data);
257 static int dt2811_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
258                           struct comedi_insn *insn, unsigned int *data);
259 static int dt2811_ao_insn_read(struct comedi_device *dev,
260                                struct comedi_subdevice *s,
261                                struct comedi_insn *insn, unsigned int *data);
262 static int dt2811_di_insn_bits(struct comedi_device *dev,
263                                struct comedi_subdevice *s,
264                                struct comedi_insn *insn, unsigned int *data);
265 static int dt2811_do_insn_bits(struct comedi_device *dev,
266                                struct comedi_subdevice *s,
267                                struct comedi_insn *insn, unsigned int *data);
268
269 enum { card_2811_pgh, card_2811_pgl };
270
271 struct dt2811_private {
272         int ntrig;
273         int curadchan;
274         enum {
275                 adc_singleended, adc_diff, adc_pseudo_diff
276         } adc_mux;
277         enum {
278                 dac_bipolar_5, dac_bipolar_2_5, dac_unipolar_5
279         } dac_range[2];
280         const struct comedi_lrange *range_type_list[2];
281         unsigned int ao_readback[2];
282 };
283
284 #define devpriv ((struct dt2811_private *)dev->private)
285
286 static const struct comedi_lrange *dac_range_types[] = {
287         &range_bipolar5,
288         &range_bipolar2_5,
289         &range_unipolar5
290 };
291
292 #define DT2811_TIMEOUT 5
293
294 #if 0
295 static irqreturn_t dt2811_interrupt(int irq, void *d)
296 {
297         int lo, hi;
298         int data;
299         struct comedi_device *dev = d;
300
301         if (!dev->attached) {
302                 comedi_error(dev, "spurious interrupt");
303                 return IRQ_HANDLED;
304         }
305
306         lo = inb(dev->iobase + DT2811_ADDATLO);
307         hi = inb(dev->iobase + DT2811_ADDATHI);
308
309         data = lo + (hi << 8);
310
311         if (!(--devpriv->ntrig)) {
312                 /* how to turn off acquisition */
313                 s->async->events |= COMEDI_SB_EOA;
314         }
315         comedi_event(dev, s);
316         return IRQ_HANDLED;
317 }
318 #endif
319
320 /*
321   options[0]   Board base address
322   options[1]   IRQ
323   options[2]   Input configuration
324                  0 == single-ended
325                  1 == differential
326                  2 == pseudo-differential
327   options[3]   Analog input range configuration
328                  0 == bipolar 5  (-5V -- +5V)
329                  1 == bipolar 2.5V  (-2.5V -- +2.5V)
330                  2 == unipolar 5V  (0V -- +5V)
331   options[4]   Analog output 0 range configuration
332                  0 == bipolar 5  (-5V -- +5V)
333                  1 == bipolar 2.5V  (-2.5V -- +2.5V)
334                  2 == unipolar 5V  (0V -- +5V)
335   options[5]   Analog output 1 range configuration
336                  0 == bipolar 5  (-5V -- +5V)
337                  1 == bipolar 2.5V  (-2.5V -- +2.5V)
338                  2 == unipolar 5V  (0V -- +5V)
339 */
340
341 static int dt2811_attach(struct comedi_device *dev, struct comedi_devconfig *it)
342 {
343         /* int i, irq; */
344         /* unsigned long irqs; */
345         /* long flags; */
346
347         int ret;
348         struct comedi_subdevice *s;
349         unsigned long iobase;
350
351         iobase = it->options[0];
352
353         printk(KERN_INFO "comedi%d: dt2811:base=0x%04lx\n", dev->minor, iobase);
354
355         if (!request_region(iobase, DT2811_SIZE, driver_name)) {
356                 printk(KERN_ERR "I/O port conflict\n");
357                 return -EIO;
358         }
359
360         dev->iobase = iobase;
361         dev->board_name = this_board->name;
362
363 #if 0
364         outb(0, dev->iobase + DT2811_ADCSR);
365         udelay(100);
366         i = inb(dev->iobase + DT2811_ADDATLO);
367         i = inb(dev->iobase + DT2811_ADDATHI);
368 #endif
369
370 #if 0
371         irq = it->options[1];
372         if (irq < 0) {
373                 save_flags(flags);
374                 sti();
375                 irqs = probe_irq_on();
376
377                 outb(DT2811_CLRERROR | DT2811_INTENB,
378                      dev->iobase + DT2811_ADCSR);
379                 outb(0, dev->iobase + DT2811_ADGCR);
380
381                 udelay(100);
382
383                 irq = probe_irq_off(irqs);
384                 restore_flags(flags);
385
386                 /*outb(DT2811_CLRERROR|DT2811_INTENB,
387                         dev->iobase+DT2811_ADCSR);*/
388
389                 if (inb(dev->iobase + DT2811_ADCSR) & DT2811_ADERROR)
390                         printk(KERN_ERR "error probing irq (bad)\n");
391                 dev->irq = 0;
392                 if (irq > 0) {
393                         i = inb(dev->iobase + DT2811_ADDATLO);
394                         i = inb(dev->iobase + DT2811_ADDATHI);
395                         printk(KERN_INFO "(irq = %d)\n", irq);
396                         ret = request_irq(irq, dt2811_interrupt, 0,
397                                           driver_name, dev);
398                         if (ret < 0)
399                                 return -EIO;
400                         dev->irq = irq;
401                 } else if (irq == 0) {
402                         printk(KERN_INFO "(no irq)\n");
403                 } else {
404                         printk(KERN_ERR "( multiple irq's -- this is bad! )\n");
405                 }
406         }
407 #endif
408
409         ret = alloc_subdevices(dev, 4);
410         if (ret < 0)
411                 return ret;
412
413         ret = alloc_private(dev, sizeof(struct dt2811_private));
414         if (ret < 0)
415                 return ret;
416
417         switch (it->options[2]) {
418         case 0:
419                 devpriv->adc_mux = adc_singleended;
420                 break;
421         case 1:
422                 devpriv->adc_mux = adc_diff;
423                 break;
424         case 2:
425                 devpriv->adc_mux = adc_pseudo_diff;
426                 break;
427         default:
428                 devpriv->adc_mux = adc_singleended;
429                 break;
430         }
431         switch (it->options[4]) {
432         case 0:
433                 devpriv->dac_range[0] = dac_bipolar_5;
434                 break;
435         case 1:
436                 devpriv->dac_range[0] = dac_bipolar_2_5;
437                 break;
438         case 2:
439                 devpriv->dac_range[0] = dac_unipolar_5;
440                 break;
441         default:
442                 devpriv->dac_range[0] = dac_bipolar_5;
443                 break;
444         }
445         switch (it->options[5]) {
446         case 0:
447                 devpriv->dac_range[1] = dac_bipolar_5;
448                 break;
449         case 1:
450                 devpriv->dac_range[1] = dac_bipolar_2_5;
451                 break;
452         case 2:
453                 devpriv->dac_range[1] = dac_unipolar_5;
454                 break;
455         default:
456                 devpriv->dac_range[1] = dac_bipolar_5;
457                 break;
458         }
459
460         s = dev->subdevices + 0;
461         /* initialize the ADC subdevice */
462         s->type = COMEDI_SUBD_AI;
463         s->subdev_flags = SDF_READABLE | SDF_GROUND;
464         s->n_chan = devpriv->adc_mux == adc_diff ? 8 : 16;
465         s->insn_read = dt2811_ai_insn;
466         s->maxdata = 0xfff;
467         switch (it->options[3]) {
468         case 0:
469         default:
470                 s->range_table = this_board->bip_5;
471                 break;
472         case 1:
473                 s->range_table = this_board->bip_2_5;
474                 break;
475         case 2:
476                 s->range_table = this_board->unip_5;
477                 break;
478         }
479
480         s = dev->subdevices + 1;
481         /* ao subdevice */
482         s->type = COMEDI_SUBD_AO;
483         s->subdev_flags = SDF_WRITABLE;
484         s->n_chan = 2;
485         s->insn_write = dt2811_ao_insn;
486         s->insn_read = dt2811_ao_insn_read;
487         s->maxdata = 0xfff;
488         s->range_table_list = devpriv->range_type_list;
489         devpriv->range_type_list[0] = dac_range_types[devpriv->dac_range[0]];
490         devpriv->range_type_list[1] = dac_range_types[devpriv->dac_range[1]];
491
492         s = dev->subdevices + 2;
493         /* di subdevice */
494         s->type = COMEDI_SUBD_DI;
495         s->subdev_flags = SDF_READABLE;
496         s->n_chan = 8;
497         s->insn_bits = dt2811_di_insn_bits;
498         s->maxdata = 1;
499         s->range_table = &range_digital;
500
501         s = dev->subdevices + 3;
502         /* do subdevice */
503         s->type = COMEDI_SUBD_DO;
504         s->subdev_flags = SDF_WRITABLE;
505         s->n_chan = 8;
506         s->insn_bits = dt2811_do_insn_bits;
507         s->maxdata = 1;
508         s->state = 0;
509         s->range_table = &range_digital;
510
511         return 0;
512 }
513
514 static int dt2811_detach(struct comedi_device *dev)
515 {
516         printk(KERN_INFO "comedi%d: dt2811: remove\n", dev->minor);
517
518         if (dev->irq)
519                 free_irq(dev->irq, dev);
520         if (dev->iobase)
521                 release_region(dev->iobase, DT2811_SIZE);
522
523         return 0;
524 }
525
526 static int dt2811_ai_insn(struct comedi_device *dev, struct comedi_subdevice *s,
527                           struct comedi_insn *insn, unsigned int *data)
528 {
529         int chan = CR_CHAN(insn->chanspec);
530         int timeout = DT2811_TIMEOUT;
531         int i;
532
533         for (i = 0; i < insn->n; i++) {
534                 outb(chan, dev->iobase + DT2811_ADGCR);
535
536                 while (timeout
537                        && inb(dev->iobase + DT2811_ADCSR) & DT2811_ADBUSY)
538                         timeout--;
539                 if (!timeout)
540                         return -ETIME;
541
542                 data[i] = inb(dev->iobase + DT2811_ADDATLO);
543                 data[i] |= inb(dev->iobase + DT2811_ADDATHI) << 8;
544                 data[i] &= 0xfff;
545         }
546
547         return i;
548 }
549
550 #if 0
551 /* Wow.  This is code from the Comedi stone age.  But it hasn't been
552  * replaced, so I'll let it stay. */
553 int dt2811_adtrig(kdev_t minor, comedi_adtrig *adtrig)
554 {
555         struct comedi_device *dev = comedi_devices + minor;
556
557         if (adtrig->n < 1)
558                 return 0;
559         dev->curadchan = adtrig->chan;
560         switch (dev->i_admode) {
561         case COMEDI_MDEMAND:
562                 dev->ntrig = adtrig->n - 1;
563                 /* not neccessary */
564                 /*printk("dt2811: AD soft trigger\n"); */
565                 /*outb(DT2811_CLRERROR|DT2811_INTENB,
566                         dev->iobase+DT2811_ADCSR); */
567                 outb(dev->curadchan, dev->iobase + DT2811_ADGCR);
568                 do_gettimeofday(&trigtime);
569                 break;
570         case COMEDI_MCONTS:
571                 dev->ntrig = adtrig->n;
572                 break;
573         }
574
575         return 0;
576 }
577 #endif
578
579 static int dt2811_ao_insn(struct comedi_device *dev, struct comedi_subdevice *s,
580                           struct comedi_insn *insn, unsigned int *data)
581 {
582         int i;
583         int chan;
584
585         chan = CR_CHAN(insn->chanspec);
586
587         for (i = 0; i < insn->n; i++) {
588                 outb(data[i] & 0xff, dev->iobase + DT2811_DADAT0LO + 2 * chan);
589                 outb((data[i] >> 8) & 0xff,
590                      dev->iobase + DT2811_DADAT0HI + 2 * chan);
591                 devpriv->ao_readback[chan] = data[i];
592         }
593
594         return i;
595 }
596
597 static int dt2811_ao_insn_read(struct comedi_device *dev,
598                                struct comedi_subdevice *s,
599                                struct comedi_insn *insn, unsigned int *data)
600 {
601         int i;
602         int chan;
603
604         chan = CR_CHAN(insn->chanspec);
605
606         for (i = 0; i < insn->n; i++)
607                 data[i] = devpriv->ao_readback[chan];
608
609         return i;
610 }
611
612 static int dt2811_di_insn_bits(struct comedi_device *dev,
613                                struct comedi_subdevice *s,
614                                struct comedi_insn *insn, unsigned int *data)
615 {
616         if (insn->n != 2)
617                 return -EINVAL;
618
619         data[1] = inb(dev->iobase + DT2811_DIO);
620
621         return 2;
622 }
623
624 static int dt2811_do_insn_bits(struct comedi_device *dev,
625                                struct comedi_subdevice *s,
626                                struct comedi_insn *insn, unsigned int *data)
627 {
628         if (insn->n != 2)
629                 return -EINVAL;
630
631         s->state &= ~data[0];
632         s->state |= data[0] & data[1];
633         outb(s->state, dev->iobase + DT2811_DIO);
634
635         data[1] = s->state;
636
637         return 2;
638 }
639
640 MODULE_AUTHOR("Comedi http://www.comedi.org");
641 MODULE_DESCRIPTION("Comedi low-level driver");
642 MODULE_LICENSE("GPL");