Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / staging / comedi / drivers / das08.c
1 /*
2  *  comedi/drivers/das08.c
3  *  comedi module 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 #include <linux/module.h>
22
23 #include "../comedidev.h"
24
25 #include "8255.h"
26 #include "comedi_8254.h"
27 #include "das08.h"
28
29 /*
30     cio-das08.pdf
31
32   "isa-das08"
33
34   0     a/d bits 0-3            start 8 bit
35   1     a/d bits 4-11           start 12 bit
36   2     eoc, ip1-3, irq, mux    op1-4, inte, mux
37   3     unused                  unused
38   4567  8254
39   89ab  8255
40
41   requires hard-wiring for async ai
42
43 */
44
45 #define DAS08_LSB               0
46 #define DAS08_MSB               1
47 #define DAS08_TRIG_12BIT        1
48 #define DAS08_STATUS            2
49 #define   DAS08_EOC                     (1<<7)
50 #define   DAS08_IRQ                     (1<<3)
51 #define   DAS08_IP(x)                   (((x)>>4)&0x7)
52 #define DAS08_CONTROL           2
53 #define   DAS08_MUX_MASK        0x7
54 #define   DAS08_MUX(x)          ((x) & DAS08_MUX_MASK)
55 #define   DAS08_INTE                    (1<<3)
56 #define   DAS08_DO_MASK         0xf0
57 #define   DAS08_OP(x)           (((x) << 4) & DAS08_DO_MASK)
58
59 /*
60     cio-das08jr.pdf
61
62   "das08/jr-ao"
63
64   0     a/d bits 0-3            unused
65   1     a/d bits 4-11           start 12 bit
66   2     eoc, mux                mux
67   3     di                      do
68   4     unused                  ao0_lsb
69   5     unused                  ao0_msb
70   6     unused                  ao1_lsb
71   7     unused                  ao1_msb
72
73 */
74
75 #define DAS08JR_DIO             3
76 #define DAS08JR_AO_LSB(x)       ((x) ? 6 : 4)
77 #define DAS08JR_AO_MSB(x)       ((x) ? 7 : 5)
78
79 /*
80     cio-das08_aox.pdf
81
82   "das08-aoh"
83   "das08-aol"
84   "das08-aom"
85
86   0     a/d bits 0-3            start 8 bit
87   1     a/d bits 4-11           start 12 bit
88   2     eoc, ip1-3, irq, mux    op1-4, inte, mux
89   3     mux, gain status        gain control
90   4567  8254
91   8     unused                  ao0_lsb
92   9     unused                  ao0_msb
93   a     unused                  ao1_lsb
94   b     unused                  ao1_msb
95   89ab
96   cdef  8255
97 */
98
99 #define DAS08AO_GAIN_CONTROL    3
100 #define DAS08AO_GAIN_STATUS     3
101
102 #define DAS08AO_AO_LSB(x)       ((x) ? 0xa : 8)
103 #define DAS08AO_AO_MSB(x)       ((x) ? 0xb : 9)
104 #define DAS08AO_AO_UPDATE       8
105
106 /* gainlist same as _pgx_ below */
107
108 static const struct comedi_lrange range_das08_pgl = {
109         9, {
110                 BIP_RANGE(10),
111                 BIP_RANGE(5),
112                 BIP_RANGE(2.5),
113                 BIP_RANGE(1.25),
114                 BIP_RANGE(0.625),
115                 UNI_RANGE(10),
116                 UNI_RANGE(5),
117                 UNI_RANGE(2.5),
118                 UNI_RANGE(1.25)
119         }
120 };
121
122 static const struct comedi_lrange range_das08_pgh = {
123         12, {
124                 BIP_RANGE(10),
125                 BIP_RANGE(5),
126                 BIP_RANGE(1),
127                 BIP_RANGE(0.5),
128                 BIP_RANGE(0.1),
129                 BIP_RANGE(0.05),
130                 BIP_RANGE(0.01),
131                 BIP_RANGE(0.005),
132                 UNI_RANGE(10),
133                 UNI_RANGE(1),
134                 UNI_RANGE(0.1),
135                 UNI_RANGE(0.01)
136         }
137 };
138
139 static const struct comedi_lrange range_das08_pgm = {
140         9, {
141                 BIP_RANGE(10),
142                 BIP_RANGE(5),
143                 BIP_RANGE(0.5),
144                 BIP_RANGE(0.05),
145                 BIP_RANGE(0.01),
146                 UNI_RANGE(10),
147                 UNI_RANGE(1),
148                 UNI_RANGE(0.1),
149                 UNI_RANGE(0.01)
150         }
151 };                              /*
152                                    cio-das08jr.pdf
153
154                                    "das08/jr-ao"
155
156                                    0 a/d bits 0-3            unused
157                                    1 a/d bits 4-11           start 12 bit
158                                    2 eoc, mux                mux
159                                    3 di                      do
160                                    4 unused                  ao0_lsb
161                                    5 unused                  ao0_msb
162                                    6 unused                  ao1_lsb
163                                    7 unused                  ao1_msb
164
165                                  */
166
167 static const struct comedi_lrange *const das08_ai_lranges[] = {
168         &range_unknown,
169         &range_bipolar5,
170         &range_das08_pgh,
171         &range_das08_pgl,
172         &range_das08_pgm,
173 };
174
175 static const int das08_pgh_gainlist[] = {
176         8, 0, 10, 2, 12, 4, 14, 6, 1, 3, 5, 7
177 };
178 static const int das08_pgl_gainlist[] = { 8, 0, 2, 4, 6, 1, 3, 5, 7 };
179 static const int das08_pgm_gainlist[] = { 8, 0, 10, 12, 14, 9, 11, 13, 15 };
180
181 static const int *const das08_gainlists[] = {
182         NULL,
183         NULL,
184         das08_pgh_gainlist,
185         das08_pgl_gainlist,
186         das08_pgm_gainlist,
187 };
188
189 static int das08_ai_eoc(struct comedi_device *dev,
190                         struct comedi_subdevice *s,
191                         struct comedi_insn *insn,
192                         unsigned long context)
193 {
194         unsigned int status;
195
196         status = inb(dev->iobase + DAS08_STATUS);
197         if ((status & DAS08_EOC) == 0)
198                 return 0;
199         return -EBUSY;
200 }
201
202 static int das08_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
203                           struct comedi_insn *insn, unsigned int *data)
204 {
205         const struct das08_board_struct *thisboard = dev->board_ptr;
206         struct das08_private_struct *devpriv = dev->private;
207         int n;
208         int chan;
209         int range;
210         int lsb, msb;
211         int ret;
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                 ret = comedi_timeout(dev, s, insn, das08_ai_eoc, 0);
245                 if (ret)
246                         return ret;
247
248                 msb = inb(dev->iobase + DAS08_MSB);
249                 lsb = inb(dev->iobase + DAS08_LSB);
250                 if (thisboard->ai_encoding == das08_encode12) {
251                         data[n] = (lsb >> 4) | (msb << 4);
252                 } else if (thisboard->ai_encoding == das08_pcm_encode12) {
253                         data[n] = (msb << 8) + lsb;
254                 } else if (thisboard->ai_encoding == das08_encode16) {
255                         /* FPOS 16-bit boards are sign-magnitude */
256                         if (msb & 0x80)
257                                 data[n] = (1 << 15) | lsb | ((msb & 0x7f) << 8);
258                         else
259                                 data[n] = (1 << 15) - (lsb | (msb & 0x7f) << 8);
260                 } else {
261                         dev_err(dev->class_dev, "bug! unknown ai encoding\n");
262                         return -1;
263                 }
264         }
265
266         return n;
267 }
268
269 static int das08_di_rbits(struct comedi_device *dev, struct comedi_subdevice *s,
270                           struct comedi_insn *insn, unsigned int *data)
271 {
272         data[0] = 0;
273         data[1] = DAS08_IP(inb(dev->iobase + DAS08_STATUS));
274
275         return insn->n;
276 }
277
278 static int das08_do_wbits(struct comedi_device *dev,
279                           struct comedi_subdevice *s,
280                           struct comedi_insn *insn,
281                           unsigned int *data)
282 {
283         struct das08_private_struct *devpriv = dev->private;
284
285         if (comedi_dio_update_state(s, data)) {
286                 /* prevent race with setting of analog input mux */
287                 spin_lock(&dev->spinlock);
288                 devpriv->do_mux_bits &= ~DAS08_DO_MASK;
289                 devpriv->do_mux_bits |= DAS08_OP(s->state);
290                 outb(devpriv->do_mux_bits, dev->iobase + DAS08_CONTROL);
291                 spin_unlock(&dev->spinlock);
292         }
293
294         data[1] = s->state;
295
296         return insn->n;
297 }
298
299 static int das08jr_di_rbits(struct comedi_device *dev,
300                             struct comedi_subdevice *s,
301                             struct comedi_insn *insn, unsigned int *data)
302 {
303         data[0] = 0;
304         data[1] = inb(dev->iobase + DAS08JR_DIO);
305
306         return insn->n;
307 }
308
309 static int das08jr_do_wbits(struct comedi_device *dev,
310                             struct comedi_subdevice *s,
311                             struct comedi_insn *insn,
312                             unsigned int *data)
313 {
314         if (comedi_dio_update_state(s, data))
315                 outb(s->state, dev->iobase + DAS08JR_DIO);
316
317         data[1] = s->state;
318
319         return insn->n;
320 }
321
322 static void das08_ao_set_data(struct comedi_device *dev,
323                               unsigned int chan, unsigned int data)
324 {
325         const struct das08_board_struct *thisboard = dev->board_ptr;
326         unsigned char lsb;
327         unsigned char msb;
328
329         lsb = data & 0xff;
330         msb = (data >> 8) & 0xff;
331         if (thisboard->is_jr) {
332                 outb(lsb, dev->iobase + DAS08JR_AO_LSB(chan));
333                 outb(msb, dev->iobase + DAS08JR_AO_MSB(chan));
334                 /* load DACs */
335                 inb(dev->iobase + DAS08JR_DIO);
336         } else {
337                 outb(lsb, dev->iobase + DAS08AO_AO_LSB(chan));
338                 outb(msb, dev->iobase + DAS08AO_AO_MSB(chan));
339                 /* load DACs */
340                 inb(dev->iobase + DAS08AO_AO_UPDATE);
341         }
342 }
343
344 static int das08_ao_insn_write(struct comedi_device *dev,
345                                struct comedi_subdevice *s,
346                                struct comedi_insn *insn,
347                                unsigned int *data)
348 {
349         unsigned int chan = CR_CHAN(insn->chanspec);
350         unsigned int val = s->readback[chan];
351         int i;
352
353         for (i = 0; i < insn->n; i++) {
354                 val = data[i];
355                 das08_ao_set_data(dev, chan, val);
356         }
357         s->readback[chan] = val;
358
359         return insn->n;
360 }
361
362 int das08_common_attach(struct comedi_device *dev, unsigned long iobase)
363 {
364         const struct das08_board_struct *thisboard = dev->board_ptr;
365         struct das08_private_struct *devpriv = dev->private;
366         struct comedi_subdevice *s;
367         int ret;
368         int i;
369
370         dev->iobase = iobase;
371
372         dev->board_name = thisboard->name;
373
374         ret = comedi_alloc_subdevices(dev, 6);
375         if (ret)
376                 return ret;
377
378         s = &dev->subdevices[0];
379         /* ai */
380         if (thisboard->ai_nbits) {
381                 s->type = COMEDI_SUBD_AI;
382                 /* XXX some boards actually have differential
383                  * inputs instead of single ended.
384                  * The driver does nothing with arefs though,
385                  * so it's no big deal.
386                  */
387                 s->subdev_flags = SDF_READABLE | SDF_GROUND;
388                 s->n_chan = 8;
389                 s->maxdata = (1 << thisboard->ai_nbits) - 1;
390                 s->range_table = das08_ai_lranges[thisboard->ai_pg];
391                 s->insn_read = das08_ai_rinsn;
392                 devpriv->pg_gainlist = das08_gainlists[thisboard->ai_pg];
393         } else {
394                 s->type = COMEDI_SUBD_UNUSED;
395         }
396
397         s = &dev->subdevices[1];
398         /* ao */
399         if (thisboard->ao_nbits) {
400                 s->type = COMEDI_SUBD_AO;
401                 s->subdev_flags = SDF_WRITABLE;
402                 s->n_chan = 2;
403                 s->maxdata = (1 << thisboard->ao_nbits) - 1;
404                 s->range_table = &range_bipolar5;
405                 s->insn_write = das08_ao_insn_write;
406
407                 ret = comedi_alloc_subdev_readback(s);
408                 if (ret)
409                         return ret;
410
411                 /* initialize all channels to 0V */
412                 for (i = 0; i < s->n_chan; i++) {
413                         s->readback[i] = s->maxdata / 2;
414                         das08_ao_set_data(dev, i, s->readback[i]);
415                 }
416         } else {
417                 s->type = COMEDI_SUBD_UNUSED;
418         }
419
420         s = &dev->subdevices[2];
421         /* di */
422         if (thisboard->di_nchan) {
423                 s->type = COMEDI_SUBD_DI;
424                 s->subdev_flags = SDF_READABLE;
425                 s->n_chan = thisboard->di_nchan;
426                 s->maxdata = 1;
427                 s->range_table = &range_digital;
428                 s->insn_bits =
429                         thisboard->is_jr ? das08jr_di_rbits : das08_di_rbits;
430         } else {
431                 s->type = COMEDI_SUBD_UNUSED;
432         }
433
434         s = &dev->subdevices[3];
435         /* do */
436         if (thisboard->do_nchan) {
437                 s->type = COMEDI_SUBD_DO;
438                 s->subdev_flags = SDF_WRITABLE;
439                 s->n_chan = thisboard->do_nchan;
440                 s->maxdata = 1;
441                 s->range_table = &range_digital;
442                 s->insn_bits =
443                         thisboard->is_jr ? das08jr_do_wbits : das08_do_wbits;
444         } else {
445                 s->type = COMEDI_SUBD_UNUSED;
446         }
447
448         s = &dev->subdevices[4];
449         /* 8255 */
450         if (thisboard->i8255_offset != 0) {
451                 ret = subdev_8255_init(dev, s, NULL, thisboard->i8255_offset);
452                 if (ret)
453                         return ret;
454         } else {
455                 s->type = COMEDI_SUBD_UNUSED;
456         }
457
458         /* Counter subdevice (8254) */
459         s = &dev->subdevices[5];
460         if (thisboard->i8254_offset) {
461                 dev->pacer = comedi_8254_init(dev->iobase +
462                                               thisboard->i8254_offset,
463                                               0, I8254_IO8, 0);
464                 if (!dev->pacer)
465                         return -ENOMEM;
466
467                 comedi_8254_subdevice_init(s, dev->pacer);
468         } else {
469                 s->type = COMEDI_SUBD_UNUSED;
470         }
471
472         return 0;
473 }
474 EXPORT_SYMBOL_GPL(das08_common_attach);
475
476 static int __init das08_init(void)
477 {
478         return 0;
479 }
480 module_init(das08_init);
481
482 static void __exit das08_exit(void)
483 {
484 }
485 module_exit(das08_exit);
486
487 MODULE_AUTHOR("Comedi http://www.comedi.org");
488 MODULE_DESCRIPTION("Comedi low-level driver");
489 MODULE_LICENSE("GPL");