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