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