]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/staging/comedi/drivers/das08.c
Merge branch 'next' of git://git.kernel.org/pub/scm/virt/kvm/kvm
[karo-tx-linux.git] / drivers / staging / comedi / drivers / das08.c
1 /*
2  *  comedi/drivers/das08.c
3  *  comedi driver for common DAS08 support (used by ISA/PCI/PCMCIA drivers)
4  *
5  *  COMEDI - Linux Control and Measurement Device Interface
6  *  Copyright (C) 2000 David A. Schleef <ds@schleef.org>
7  *  Copyright (C) 2001,2002,2003 Frank Mori Hess <fmhess@users.sourceforge.net>
8  *  Copyright (C) 2004 Salvador E. Tropea <set@users.sf.net> <set@ieee.org>
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2 of the License, or
13  *  (at your option) any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  */
20
21 /*
22  * Driver: das08
23  * Description: DAS-08 compatible boards
24  * Devices: various, see das08_isa, das08_cs, and das08_pci drivers
25  * Author: Warren Jasper, ds, Frank Hess
26  * Updated: Fri, 31 Aug 2012 19:19:06 +0100
27  * Status: works
28  *
29  * This driver is used by the das08_isa, das08_cs, and das08_pci
30  * drivers to provide the common support for the DAS-08 hardware.
31  *
32  * The driver doesn't support asynchronous commands, since the
33  * cheap das08 hardware doesn't really support them.
34  */
35
36 #include <linux/module.h>
37
38 #include "../comedidev.h"
39
40 #include "8255.h"
41 #include "8253.h"
42 #include "das08.h"
43
44 /*
45     cio-das08.pdf
46
47   "isa-das08"
48
49   0     a/d bits 0-3            start 8 bit
50   1     a/d bits 4-11           start 12 bit
51   2     eoc, ip1-3, irq, mux    op1-4, inte, mux
52   3     unused                  unused
53   4567  8254
54   89ab  8255
55
56   requires hard-wiring for async ai
57
58 */
59
60 #define DAS08_LSB               0
61 #define DAS08_MSB               1
62 #define DAS08_TRIG_12BIT        1
63 #define DAS08_STATUS            2
64 #define   DAS08_EOC                     (1<<7)
65 #define   DAS08_IRQ                     (1<<3)
66 #define   DAS08_IP(x)                   (((x)>>4)&0x7)
67 #define DAS08_CONTROL           2
68 #define   DAS08_MUX_MASK        0x7
69 #define   DAS08_MUX(x)          ((x) & DAS08_MUX_MASK)
70 #define   DAS08_INTE                    (1<<3)
71 #define   DAS08_DO_MASK         0xf0
72 #define   DAS08_OP(x)           (((x) << 4) & DAS08_DO_MASK)
73
74 /*
75     cio-das08jr.pdf
76
77   "das08/jr-ao"
78
79   0     a/d bits 0-3            unused
80   1     a/d bits 4-11           start 12 bit
81   2     eoc, mux                mux
82   3     di                      do
83   4     unused                  ao0_lsb
84   5     unused                  ao0_msb
85   6     unused                  ao1_lsb
86   7     unused                  ao1_msb
87
88 */
89
90 #define DAS08JR_DIO             3
91 #define DAS08JR_AO_LSB(x)       ((x) ? 6 : 4)
92 #define DAS08JR_AO_MSB(x)       ((x) ? 7 : 5)
93
94 /*
95     cio-das08_aox.pdf
96
97   "das08-aoh"
98   "das08-aol"
99   "das08-aom"
100
101   0     a/d bits 0-3            start 8 bit
102   1     a/d bits 4-11           start 12 bit
103   2     eoc, ip1-3, irq, mux    op1-4, inte, mux
104   3     mux, gain status        gain control
105   4567  8254
106   8     unused                  ao0_lsb
107   9     unused                  ao0_msb
108   a     unused                  ao1_lsb
109   b     unused                  ao1_msb
110   89ab
111   cdef  8255
112 */
113
114 #define DAS08AO_GAIN_CONTROL    3
115 #define DAS08AO_GAIN_STATUS     3
116
117 #define DAS08AO_AO_LSB(x)       ((x) ? 0xa : 8)
118 #define DAS08AO_AO_MSB(x)       ((x) ? 0xb : 9)
119 #define DAS08AO_AO_UPDATE       8
120
121 /* gainlist same as _pgx_ below */
122
123 static const struct comedi_lrange range_das08_pgl = { 9, {
124                                                           BIP_RANGE(10),
125                                                           BIP_RANGE(5),
126                                                           BIP_RANGE(2.5),
127                                                           BIP_RANGE(1.25),
128                                                           BIP_RANGE(0.625),
129                                                           UNI_RANGE(10),
130                                                           UNI_RANGE(5),
131                                                           UNI_RANGE(2.5),
132                                                           UNI_RANGE(1.25)
133                                                           }
134 };
135
136 static const struct comedi_lrange range_das08_pgh = { 12, {
137                                                            BIP_RANGE(10),
138                                                            BIP_RANGE(5),
139                                                            BIP_RANGE(1),
140                                                            BIP_RANGE(0.5),
141                                                            BIP_RANGE(0.1),
142                                                            BIP_RANGE(0.05),
143                                                            BIP_RANGE(0.01),
144                                                            BIP_RANGE(0.005),
145                                                            UNI_RANGE(10),
146                                                            UNI_RANGE(1),
147                                                            UNI_RANGE(0.1),
148                                                            UNI_RANGE(0.01),
149                                                            }
150 };
151
152 static const struct comedi_lrange range_das08_pgm = { 9, {
153                                                           BIP_RANGE(10),
154                                                           BIP_RANGE(5),
155                                                           BIP_RANGE(0.5),
156                                                           BIP_RANGE(0.05),
157                                                           BIP_RANGE(0.01),
158                                                           UNI_RANGE(10),
159                                                           UNI_RANGE(1),
160                                                           UNI_RANGE(0.1),
161                                                           UNI_RANGE(0.01)
162                                                           }
163 };                              /*
164                                    cio-das08jr.pdf
165
166                                    "das08/jr-ao"
167
168                                    0 a/d bits 0-3            unused
169                                    1 a/d bits 4-11           start 12 bit
170                                    2 eoc, mux                mux
171                                    3 di                      do
172                                    4 unused                  ao0_lsb
173                                    5 unused                  ao0_msb
174                                    6 unused                  ao1_lsb
175                                    7 unused                  ao1_msb
176
177                                  */
178
179 static const struct comedi_lrange *const das08_ai_lranges[] = {
180         &range_unknown,
181         &range_bipolar5,
182         &range_das08_pgh,
183         &range_das08_pgl,
184         &range_das08_pgm,
185 };
186
187 static const int das08_pgh_gainlist[] = {
188         8, 0, 10, 2, 12, 4, 14, 6, 1, 3, 5, 7
189 };
190 static const int das08_pgl_gainlist[] = { 8, 0, 2, 4, 6, 1, 3, 5, 7 };
191 static const int das08_pgm_gainlist[] = { 8, 0, 10, 12, 14, 9, 11, 13, 15 };
192
193 static const int *const das08_gainlists[] = {
194         NULL,
195         NULL,
196         das08_pgh_gainlist,
197         das08_pgl_gainlist,
198         das08_pgm_gainlist,
199 };
200
201 #define TIMEOUT 100000
202
203 static int das08_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
204                           struct comedi_insn *insn, unsigned int *data)
205 {
206         const struct das08_board_struct *thisboard = comedi_board(dev);
207         struct das08_private_struct *devpriv = dev->private;
208         int i, n;
209         int chan;
210         int range;
211         int lsb, msb;
212
213         chan = CR_CHAN(insn->chanspec);
214         range = CR_RANGE(insn->chanspec);
215
216         /* clear crap */
217         inb(dev->iobase + DAS08_LSB);
218         inb(dev->iobase + DAS08_MSB);
219
220         /* set multiplexer */
221         /*  lock to prevent race with digital output */
222         spin_lock(&dev->spinlock);
223         devpriv->do_mux_bits &= ~DAS08_MUX_MASK;
224         devpriv->do_mux_bits |= DAS08_MUX(chan);
225         outb(devpriv->do_mux_bits, dev->iobase + DAS08_CONTROL);
226         spin_unlock(&dev->spinlock);
227
228         if (s->range_table->length > 1) {
229                 /* set gain/range */
230                 range = CR_RANGE(insn->chanspec);
231                 outb(devpriv->pg_gainlist[range],
232                      dev->iobase + DAS08AO_GAIN_CONTROL);
233         }
234
235         for (n = 0; n < insn->n; n++) {
236                 /* clear over-range bits for 16-bit boards */
237                 if (thisboard->ai_nbits == 16)
238                         if (inb(dev->iobase + DAS08_MSB) & 0x80)
239                                 dev_info(dev->class_dev, "over-range\n");
240
241                 /* trigger conversion */
242                 outb_p(0, dev->iobase + DAS08_TRIG_12BIT);
243
244                 for (i = 0; i < TIMEOUT; i++) {
245                         if (!(inb(dev->iobase + DAS08_STATUS) & DAS08_EOC))
246                                 break;
247                 }
248                 if (i == TIMEOUT) {
249                         dev_err(dev->class_dev, "timeout\n");
250                         return -ETIME;
251                 }
252                 msb = inb(dev->iobase + DAS08_MSB);
253                 lsb = inb(dev->iobase + DAS08_LSB);
254                 if (thisboard->ai_encoding == das08_encode12) {
255                         data[n] = (lsb >> 4) | (msb << 4);
256                 } else if (thisboard->ai_encoding == das08_pcm_encode12) {
257                         data[n] = (msb << 8) + lsb;
258                 } else if (thisboard->ai_encoding == das08_encode16) {
259                         /* FPOS 16-bit boards are sign-magnitude */
260                         if (msb & 0x80)
261                                 data[n] = (1 << 15) | lsb | ((msb & 0x7f) << 8);
262                         else
263                                 data[n] = (1 << 15) - (lsb | (msb & 0x7f) << 8);
264                 } else {
265                         comedi_error(dev, "bug! unknown ai encoding");
266                         return -1;
267                 }
268         }
269
270         return n;
271 }
272
273 static int das08_di_rbits(struct comedi_device *dev, struct comedi_subdevice *s,
274                           struct comedi_insn *insn, unsigned int *data)
275 {
276         data[0] = 0;
277         data[1] = DAS08_IP(inb(dev->iobase + DAS08_STATUS));
278
279         return insn->n;
280 }
281
282 static int das08_do_wbits(struct comedi_device *dev, struct comedi_subdevice *s,
283                           struct comedi_insn *insn, unsigned int *data)
284 {
285         struct das08_private_struct *devpriv = dev->private;
286         int wbits;
287
288         /*  get current settings of digital output lines */
289         wbits = (devpriv->do_mux_bits >> 4) & 0xf;
290         /*  null bits we are going to set */
291         wbits &= ~data[0];
292         /*  set new bit values */
293         wbits |= data[0] & data[1];
294         /*  remember digital output bits */
295         /*  prevent race with setting of analog input mux */
296         spin_lock(&dev->spinlock);
297         devpriv->do_mux_bits &= ~DAS08_DO_MASK;
298         devpriv->do_mux_bits |= DAS08_OP(wbits);
299         outb(devpriv->do_mux_bits, dev->iobase + DAS08_CONTROL);
300         spin_unlock(&dev->spinlock);
301
302         data[1] = wbits;
303
304         return insn->n;
305 }
306
307 static int das08jr_di_rbits(struct comedi_device *dev,
308                             struct comedi_subdevice *s,
309                             struct comedi_insn *insn, unsigned int *data)
310 {
311         data[0] = 0;
312         data[1] = inb(dev->iobase + DAS08JR_DIO);
313
314         return insn->n;
315 }
316
317 static int das08jr_do_wbits(struct comedi_device *dev,
318                             struct comedi_subdevice *s,
319                             struct comedi_insn *insn, unsigned int *data)
320 {
321         struct das08_private_struct *devpriv = dev->private;
322
323         /*  null bits we are going to set */
324         devpriv->do_bits &= ~data[0];
325         /*  set new bit values */
326         devpriv->do_bits |= data[0] & data[1];
327         outb(devpriv->do_bits, dev->iobase + DAS08JR_DIO);
328
329         data[1] = devpriv->do_bits;
330
331         return insn->n;
332 }
333
334 static void das08_ao_set_data(struct comedi_device *dev,
335                               unsigned int chan, unsigned int data)
336 {
337         const struct das08_board_struct *thisboard = comedi_board(dev);
338         struct das08_private_struct *devpriv = dev->private;
339         unsigned char lsb;
340         unsigned char msb;
341
342         lsb = data & 0xff;
343         msb = (data >> 8) & 0xff;
344         if (thisboard->is_jr) {
345                 outb(lsb, dev->iobase + DAS08JR_AO_LSB(chan));
346                 outb(msb, dev->iobase + DAS08JR_AO_MSB(chan));
347                 /* load DACs */
348                 inb(dev->iobase + DAS08JR_DIO);
349         } else {
350                 outb(lsb, dev->iobase + DAS08AO_AO_LSB(chan));
351                 outb(msb, dev->iobase + DAS08AO_AO_MSB(chan));
352                 /* load DACs */
353                 inb(dev->iobase + DAS08AO_AO_UPDATE);
354         }
355         devpriv->ao_readback[chan] = data;
356 }
357
358 static void das08_ao_initialize(struct comedi_device *dev,
359                                 struct comedi_subdevice *s)
360 {
361         int n;
362         unsigned int data;
363
364         data = s->maxdata / 2;  /* should be about 0 volts */
365         for (n = 0; n < s->n_chan; n++)
366                 das08_ao_set_data(dev, n, data);
367 }
368
369 static int das08_ao_winsn(struct comedi_device *dev,
370                           struct comedi_subdevice *s,
371                           struct comedi_insn *insn, unsigned int *data)
372 {
373         unsigned int n;
374         unsigned int chan;
375
376         chan = CR_CHAN(insn->chanspec);
377
378         for (n = 0; n < insn->n; n++)
379                 das08_ao_set_data(dev, chan, *data);
380
381         return n;
382 }
383
384 static int das08_ao_rinsn(struct comedi_device *dev,
385                           struct comedi_subdevice *s,
386                           struct comedi_insn *insn, unsigned int *data)
387 {
388         struct das08_private_struct *devpriv = dev->private;
389         unsigned int n;
390         unsigned int chan;
391
392         chan = CR_CHAN(insn->chanspec);
393
394         for (n = 0; n < insn->n; n++)
395                 data[n] = devpriv->ao_readback[chan];
396
397         return n;
398 }
399
400 static void i8254_initialize(struct comedi_device *dev)
401 {
402         const struct das08_board_struct *thisboard = comedi_board(dev);
403         unsigned long i8254_iobase = dev->iobase + thisboard->i8254_offset;
404         unsigned int mode = I8254_MODE0 | I8254_BINARY;
405         int i;
406
407         for (i = 0; i < 3; ++i)
408                 i8254_set_mode(i8254_iobase, 0, i, mode);
409 }
410
411 static int das08_counter_read(struct comedi_device *dev,
412                               struct comedi_subdevice *s,
413                               struct comedi_insn *insn, unsigned int *data)
414 {
415         const struct das08_board_struct *thisboard = comedi_board(dev);
416         unsigned long i8254_iobase = dev->iobase + thisboard->i8254_offset;
417         int chan = insn->chanspec;
418
419         data[0] = i8254_read(i8254_iobase, 0, chan);
420         return 1;
421 }
422
423 static int das08_counter_write(struct comedi_device *dev,
424                                struct comedi_subdevice *s,
425                                struct comedi_insn *insn, unsigned int *data)
426 {
427         const struct das08_board_struct *thisboard = comedi_board(dev);
428         unsigned long i8254_iobase = dev->iobase + thisboard->i8254_offset;
429         int chan = insn->chanspec;
430
431         i8254_write(i8254_iobase, 0, chan, data[0]);
432         return 1;
433 }
434
435 static int das08_counter_config(struct comedi_device *dev,
436                                 struct comedi_subdevice *s,
437                                 struct comedi_insn *insn, unsigned int *data)
438 {
439         const struct das08_board_struct *thisboard = comedi_board(dev);
440         unsigned long i8254_iobase = dev->iobase + thisboard->i8254_offset;
441         int chan = insn->chanspec;
442
443         switch (data[0]) {
444         case INSN_CONFIG_SET_COUNTER_MODE:
445                 i8254_set_mode(i8254_iobase, 0, chan, data[1]);
446                 break;
447         case INSN_CONFIG_8254_READ_STATUS:
448                 data[1] = i8254_status(i8254_iobase, 0, chan);
449                 break;
450         default:
451                 return -EINVAL;
452                 break;
453         }
454         return 2;
455 }
456
457 int das08_common_attach(struct comedi_device *dev, unsigned long iobase)
458 {
459         const struct das08_board_struct *thisboard = comedi_board(dev);
460         struct das08_private_struct *devpriv = dev->private;
461         struct comedi_subdevice *s;
462         int ret;
463
464         dev->iobase = iobase;
465
466         dev->board_name = thisboard->name;
467
468         ret = comedi_alloc_subdevices(dev, 6);
469         if (ret)
470                 return ret;
471
472         s = &dev->subdevices[0];
473         /* ai */
474         if (thisboard->ai_nbits) {
475                 s->type = COMEDI_SUBD_AI;
476                 /* XXX some boards actually have differential
477                  * inputs instead of single ended.
478                  * The driver does nothing with arefs though,
479                  * so it's no big deal.
480                  */
481                 s->subdev_flags = SDF_READABLE | SDF_GROUND;
482                 s->n_chan = 8;
483                 s->maxdata = (1 << thisboard->ai_nbits) - 1;
484                 s->range_table = das08_ai_lranges[thisboard->ai_pg];
485                 s->insn_read = das08_ai_rinsn;
486                 devpriv->pg_gainlist = das08_gainlists[thisboard->ai_pg];
487         } else {
488                 s->type = COMEDI_SUBD_UNUSED;
489         }
490
491         s = &dev->subdevices[1];
492         /* ao */
493         if (thisboard->ao_nbits) {
494                 s->type = COMEDI_SUBD_AO;
495                 s->subdev_flags = SDF_WRITABLE;
496                 s->n_chan = 2;
497                 s->maxdata = (1 << thisboard->ao_nbits) - 1;
498                 s->range_table = &range_bipolar5;
499                 s->insn_write = das08_ao_winsn;
500                 s->insn_read = das08_ao_rinsn;
501                 das08_ao_initialize(dev, s);
502         } else {
503                 s->type = COMEDI_SUBD_UNUSED;
504         }
505
506         s = &dev->subdevices[2];
507         /* di */
508         if (thisboard->di_nchan) {
509                 s->type = COMEDI_SUBD_DI;
510                 s->subdev_flags = SDF_READABLE;
511                 s->n_chan = thisboard->di_nchan;
512                 s->maxdata = 1;
513                 s->range_table = &range_digital;
514                 s->insn_bits =
515                         thisboard->is_jr ? das08jr_di_rbits : das08_di_rbits;
516         } else {
517                 s->type = COMEDI_SUBD_UNUSED;
518         }
519
520         s = &dev->subdevices[3];
521         /* do */
522         if (thisboard->do_nchan) {
523                 s->type = COMEDI_SUBD_DO;
524                 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
525                 s->n_chan = thisboard->do_nchan;
526                 s->maxdata = 1;
527                 s->range_table = &range_digital;
528                 s->insn_bits =
529                         thisboard->is_jr ? das08jr_do_wbits : das08_do_wbits;
530         } else {
531                 s->type = COMEDI_SUBD_UNUSED;
532         }
533
534         s = &dev->subdevices[4];
535         /* 8255 */
536         if (thisboard->i8255_offset != 0) {
537                 subdev_8255_init(dev, s, NULL, (unsigned long)(dev->iobase +
538                                                                thisboard->
539                                                                i8255_offset));
540         } else {
541                 s->type = COMEDI_SUBD_UNUSED;
542         }
543
544         s = &dev->subdevices[5];
545         /* 8254 */
546         if (thisboard->i8254_offset != 0) {
547                 s->type = COMEDI_SUBD_COUNTER;
548                 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
549                 s->n_chan = 3;
550                 s->maxdata = 0xFFFF;
551                 s->insn_read = das08_counter_read;
552                 s->insn_write = das08_counter_write;
553                 s->insn_config = das08_counter_config;
554                 i8254_initialize(dev);
555         } else {
556                 s->type = COMEDI_SUBD_UNUSED;
557         }
558
559         return 0;
560 }
561 EXPORT_SYMBOL_GPL(das08_common_attach);
562
563 static int __init das08_init(void)
564 {
565         return 0;
566 }
567 module_init(das08_init);
568
569 static void __exit das08_exit(void)
570 {
571 }
572 module_exit(das08_exit);
573
574 MODULE_AUTHOR("Comedi http://www.comedi.org");
575 MODULE_DESCRIPTION("Comedi low-level driver");
576 MODULE_LICENSE("GPL");