Merge branch 'topic/hda' into for-linus
[linux-2.6] / 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 PT_REGS_ARG)
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         int ret;
320         struct comedi_subdevice *s;
321         unsigned long iobase;
322
323         iobase = it->options[0];
324
325         printk("comedi%d: dt2811: base=0x%04lx\n", dev->minor, iobase);
326
327         if (!request_region(iobase, DT2811_SIZE, driver_name)) {
328                 printk("I/O port conflict\n");
329                 return -EIO;
330         }
331
332         dev->iobase = iobase;
333         dev->board_name = this_board->name;
334
335 #if 0
336         outb(0, dev->iobase + DT2811_ADCSR);
337         comedi_udelay(100);
338         i = inb(dev->iobase + DT2811_ADDATLO);
339         i = inb(dev->iobase + DT2811_ADDATHI);
340 #endif
341
342 #if 0
343         irq = it->options[1];
344         if (irq < 0) {
345                 save_flags(flags);
346                 sti();
347                 irqs = probe_irq_on();
348
349                 outb(DT2811_CLRERROR | DT2811_INTENB,
350                         dev->iobase + DT2811_ADCSR);
351                 outb(0, dev->iobase + DT2811_ADGCR);
352
353                 comedi_udelay(100);
354
355                 irq = probe_irq_off(irqs);
356                 restore_flags(flags);
357
358                 /*outb(DT2811_CLRERROR|DT2811_INTENB,dev->iobase+DT2811_ADCSR); */
359
360                 if (inb(dev->iobase + DT2811_ADCSR) & DT2811_ADERROR) {
361                         printk("error probing irq (bad) \n");
362                 }
363                 dev->irq = 0;
364                 if (irq > 0) {
365                         i = inb(dev->iobase + DT2811_ADDATLO);
366                         i = inb(dev->iobase + DT2811_ADDATHI);
367                         printk("(irq = %d)\n", irq);
368                         ret = comedi_request_irq(irq, dt2811_interrupt, 0,
369                                 driver_name, dev);
370                         if (ret < 0)
371                                 return -EIO;
372                         dev->irq = irq;
373                 } else if (irq == 0) {
374                         printk("(no irq)\n");
375                 } else {
376                         printk("( multiple irq's -- this is bad! )\n");
377                 }
378         }
379 #endif
380
381         if ((ret = alloc_subdevices(dev, 4)) < 0)
382                 return ret;
383         if ((ret = alloc_private(dev, sizeof(struct dt2811_private))) < 0)
384                 return ret;
385         switch (it->options[2]) {
386         case 0:
387                 devpriv->adc_mux = adc_singleended;
388                 break;
389         case 1:
390                 devpriv->adc_mux = adc_diff;
391                 break;
392         case 2:
393                 devpriv->adc_mux = adc_pseudo_diff;
394                 break;
395         default:
396                 devpriv->adc_mux = adc_singleended;
397                 break;
398         }
399         switch (it->options[4]) {
400         case 0:
401                 devpriv->dac_range[0] = dac_bipolar_5;
402                 break;
403         case 1:
404                 devpriv->dac_range[0] = dac_bipolar_2_5;
405                 break;
406         case 2:
407                 devpriv->dac_range[0] = dac_unipolar_5;
408                 break;
409         default:
410                 devpriv->dac_range[0] = dac_bipolar_5;
411                 break;
412         }
413         switch (it->options[5]) {
414         case 0:
415                 devpriv->dac_range[1] = dac_bipolar_5;
416                 break;
417         case 1:
418                 devpriv->dac_range[1] = dac_bipolar_2_5;
419                 break;
420         case 2:
421                 devpriv->dac_range[1] = dac_unipolar_5;
422                 break;
423         default:
424                 devpriv->dac_range[1] = dac_bipolar_5;
425                 break;
426         }
427
428         s = dev->subdevices + 0;
429         /* initialize the ADC subdevice */
430         s->type = COMEDI_SUBD_AI;
431         s->subdev_flags = SDF_READABLE | SDF_GROUND;
432         s->n_chan = devpriv->adc_mux == adc_diff ? 8 : 16;
433         s->insn_read = dt2811_ai_insn;
434         s->maxdata = 0xfff;
435         switch (it->options[3]) {
436         case 0:
437         default:
438                 s->range_table = this_board->bip_5;
439                 break;
440         case 1:
441                 s->range_table = this_board->bip_2_5;
442                 break;
443         case 2:
444                 s->range_table = this_board->unip_5;
445                 break;
446         }
447
448         s = dev->subdevices + 1;
449         /* ao subdevice */
450         s->type = COMEDI_SUBD_AO;
451         s->subdev_flags = SDF_WRITABLE;
452         s->n_chan = 2;
453         s->insn_write = dt2811_ao_insn;
454         s->insn_read = dt2811_ao_insn_read;
455         s->maxdata = 0xfff;
456         s->range_table_list = devpriv->range_type_list;
457         devpriv->range_type_list[0] = dac_range_types[devpriv->dac_range[0]];
458         devpriv->range_type_list[1] = dac_range_types[devpriv->dac_range[1]];
459
460         s = dev->subdevices + 2;
461         /* di subdevice */
462         s->type = COMEDI_SUBD_DI;
463         s->subdev_flags = SDF_READABLE;
464         s->n_chan = 8;
465         s->insn_bits = dt2811_di_insn_bits;
466         s->maxdata = 1;
467         s->range_table = &range_digital;
468
469         s = dev->subdevices + 3;
470         /* do subdevice */
471         s->type = COMEDI_SUBD_DO;
472         s->subdev_flags = SDF_WRITABLE;
473         s->n_chan = 8;
474         s->insn_bits = dt2811_do_insn_bits;
475         s->maxdata = 1;
476         s->state = 0;
477         s->range_table = &range_digital;
478
479         return 0;
480 }
481
482 static int dt2811_detach(struct comedi_device * dev)
483 {
484         printk("comedi%d: dt2811: remove\n", dev->minor);
485
486         if (dev->irq) {
487                 comedi_free_irq(dev->irq, dev);
488         }
489         if (dev->iobase) {
490                 release_region(dev->iobase, DT2811_SIZE);
491         }
492
493         return 0;
494 }
495
496 static int dt2811_ai_insn(struct comedi_device * dev, struct comedi_subdevice * s,
497         struct comedi_insn * insn, unsigned int * data)
498 {
499         int chan = CR_CHAN(insn->chanspec);
500         int timeout = DT2811_TIMEOUT;
501         int i;
502
503         for (i = 0; i < insn->n; i++) {
504                 outb(chan, dev->iobase + DT2811_ADGCR);
505
506                 while (timeout
507                         && inb(dev->iobase + DT2811_ADCSR) & DT2811_ADBUSY)
508                         timeout--;
509                 if (!timeout)
510                         return -ETIME;
511
512                 data[i] = inb(dev->iobase + DT2811_ADDATLO);
513                 data[i] |= inb(dev->iobase + DT2811_ADDATHI) << 8;
514                 data[i] &= 0xfff;
515         }
516
517         return i;
518 }
519
520 #if 0
521 /* Wow.  This is code from the Comedi stone age.  But it hasn't been
522  * replaced, so I'll let it stay. */
523 int dt2811_adtrig(kdev_t minor, comedi_adtrig * adtrig)
524 {
525         struct comedi_device *dev = comedi_devices + minor;
526
527         if (adtrig->n < 1)
528                 return 0;
529         dev->curadchan = adtrig->chan;
530         switch (dev->i_admode) {
531         case COMEDI_MDEMAND:
532                 dev->ntrig = adtrig->n - 1;
533                 /*printk("dt2811: AD soft trigger\n"); */
534                 /*outb(DT2811_CLRERROR|DT2811_INTENB,dev->iobase+DT2811_ADCSR); *//* not neccessary */
535                 outb(dev->curadchan, dev->iobase + DT2811_ADGCR);
536                 do_gettimeofday(&trigtime);
537                 break;
538         case COMEDI_MCONTS:
539                 dev->ntrig = adtrig->n;
540                 break;
541         }
542
543         return 0;
544 }
545 #endif
546
547 static int dt2811_ao_insn(struct comedi_device * dev, struct comedi_subdevice * s,
548         struct comedi_insn * insn, unsigned int * data)
549 {
550         int i;
551         int chan;
552
553         chan = CR_CHAN(insn->chanspec);
554
555         for (i = 0; i < insn->n; i++) {
556                 outb(data[i] & 0xff, dev->iobase + DT2811_DADAT0LO + 2 * chan);
557                 outb((data[i] >> 8) & 0xff,
558                         dev->iobase + DT2811_DADAT0HI + 2 * chan);
559                 devpriv->ao_readback[chan] = data[i];
560         }
561
562         return i;
563 }
564
565 static int dt2811_ao_insn_read(struct comedi_device * dev, struct comedi_subdevice * s,
566         struct comedi_insn * insn, unsigned int * data)
567 {
568         int i;
569         int chan;
570
571         chan = CR_CHAN(insn->chanspec);
572
573         for (i = 0; i < insn->n; i++) {
574                 data[i] = devpriv->ao_readback[chan];
575         }
576
577         return i;
578 }
579
580 static int dt2811_di_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
581         struct comedi_insn * insn, unsigned int * data)
582 {
583         if (insn->n != 2)
584                 return -EINVAL;
585
586         data[1] = inb(dev->iobase + DT2811_DIO);
587
588         return 2;
589 }
590
591 static int dt2811_do_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
592         struct comedi_insn * insn, unsigned int * data)
593 {
594         if (insn->n != 2)
595                 return -EINVAL;
596
597         s->state &= ~data[0];
598         s->state |= data[0] & data[1];
599         outb(s->state, dev->iobase + DT2811_DIO);
600
601         data[1] = s->state;
602
603         return 2;
604 }