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