staging: comedi: das08: rename DAS08AO_AO_LSB() and DAS08AO_AO_MSB()
[firefly-linux-kernel-4.4.55.git] / 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 /*
46  * Data format of DAS08_AI_LSB_REG and DAS08_AI_MSB_REG depends on
47  * 'ai_encoding' member of board structure:
48  *
49  * das08_encode12     : DATA[11..4] = MSB[7..0], DATA[3..0] = LSB[7..4].
50  * das08_pcm_encode12 : DATA[11..8] = MSB[3..0], DATA[7..9] = LSB[7..0].
51  * das08_encode16     : SIGN = MSB[7], MAGNITUDE[14..8] = MSB[6..0],
52  *                      MAGNITUDE[7..0] = LSB[7..0].
53  *                      SIGN==0 for negative input, SIGN==1 for positive input.
54  *                      Note: when read a second time after conversion
55  *                            complete, MSB[7] is an "over-range" bit.
56  */
57 #define DAS08_AI_LSB_REG        0x00    /* (R) AI least significant bits */
58 #define DAS08_AI_MSB_REG        0x01    /* (R) AI most significant bits */
59 #define DAS08_AI_TRIG_REG       0x01    /* (W) AI software trigger */
60 #define DAS08_STATUS_REG        0x02    /* (R) status */
61 #define DAS08_STATUS_AI_BUSY    BIT(7)  /* AI conversion in progress */
62 /*
63  * The IRQ status bit is set to 1 by a rising edge on the external interrupt
64  * input (which may be jumpered to the pacer output).  It is cleared by
65  * setting the INTE control bit to 0.  Not present on "JR" boards.
66  */
67 #define DAS08_STATUS_IRQ        BIT(3)  /* latched interrupt input */
68 /* digital inputs (not "JR" boards) */
69 #define DAS08_STATUS_DI(x)      (((x) & 0x70) >> 4)
70 #define DAS08_CONTROL_REG       0x02    /* (W) control */
71 /*
72  * Note: The AI multiplexor channel can also be read from status register using
73  * the same mask.
74  */
75 #define DAS08_CONTROL_MUX_MASK  0x7     /* multiplexor channel mask */
76 #define DAS08_CONTROL_MUX(x)    ((x) & DAS08_CONTROL_MUX_MASK) /* mux channel */
77 #define DAS08_CONTROL_INTE      BIT(3)  /* interrupt enable (not "JR" boards) */
78 #define DAS08_CONTROL_DO_MASK   0xf0    /* digital outputs mask (not "JR") */
79 /* digital outputs (not "JR" boards) */
80 #define DAS08_CONTROL_DO(x)     (((x) << 4) & DAS08_CONTROL_DO_MASK)
81 /*
82  * (R/W) programmable AI gain ("PGx" and "AOx" boards):
83  * + bits 3..0 (R/W) show/set the gain for the current AI mux channel
84  * + bits 6..4 (R) show the current AI mux channel
85  * + bit 7 (R) not unused
86  */
87 #define DAS08_GAIN_REG          0x03
88
89 /*
90     cio-das08jr.pdf
91
92   "das08/jr-ao"
93
94   0     a/d bits 0-3            unused
95   1     a/d bits 4-11           start 12 bit
96   2     eoc, mux                mux
97   3     di                      do
98   4     unused                  ao0_lsb
99   5     unused                  ao0_msb
100   6     unused                  ao1_lsb
101   7     unused                  ao1_msb
102
103 */
104
105 #define DAS08JR_DI_REG          0x03    /* (R) digital inputs ("JR" boards) */
106 #define DAS08JR_DO_REG          0x03    /* (W) digital outputs ("JR" boards) */
107 /* (W) analog output l.s.b. registers for 2 channels ("JR" boards) */
108 #define DAS08JR_AO_LSB_REG(x)   ((x) ? 0x06 : 0x04)
109 /* (W) analog output m.s.b. registers for 2 channels ("JR" boards) */
110 #define DAS08JR_AO_MSB_REG(x)   ((x) ? 0x07 : 0x05)
111 /*
112  * (R) update analog outputs ("JR" boards set for simultaneous output)
113  *     (same register as digital inputs)
114  */
115 #define DAS08JR_AO_UPDATE_REG   0x03
116
117 /*
118     cio-das08_aox.pdf
119
120   "das08-aoh"
121   "das08-aol"
122   "das08-aom"
123
124   0     a/d bits 0-3            start 8 bit
125   1     a/d bits 4-11           start 12 bit
126   2     eoc, ip1-3, irq, mux    op1-4, inte, mux
127   3     mux, gain status        gain control
128   4567  8254
129   8     unused                  ao0_lsb
130   9     unused                  ao0_msb
131   a     unused                  ao1_lsb
132   b     unused                  ao1_msb
133   89ab
134   cdef  8255
135 */
136
137 /* (W) analog output l.s.b. registers for 2 channels ("AOx" boards) */
138 #define DAS08AOX_AO_LSB_REG(x)  ((x) ? 0x0a : 0x08)
139 /* (W) analog output m.s.b. registers for 2 channels ("AOx" boards) */
140 #define DAS08AOX_AO_MSB_REG(x)  ((x) ? 0x0b : 0x09)
141 #define DAS08AO_AO_UPDATE       8
142
143 /* gainlist same as _pgx_ below */
144
145 static const struct comedi_lrange range_das08_pgl = {
146         9, {
147                 BIP_RANGE(10),
148                 BIP_RANGE(5),
149                 BIP_RANGE(2.5),
150                 BIP_RANGE(1.25),
151                 BIP_RANGE(0.625),
152                 UNI_RANGE(10),
153                 UNI_RANGE(5),
154                 UNI_RANGE(2.5),
155                 UNI_RANGE(1.25)
156         }
157 };
158
159 static const struct comedi_lrange range_das08_pgh = {
160         12, {
161                 BIP_RANGE(10),
162                 BIP_RANGE(5),
163                 BIP_RANGE(1),
164                 BIP_RANGE(0.5),
165                 BIP_RANGE(0.1),
166                 BIP_RANGE(0.05),
167                 BIP_RANGE(0.01),
168                 BIP_RANGE(0.005),
169                 UNI_RANGE(10),
170                 UNI_RANGE(1),
171                 UNI_RANGE(0.1),
172                 UNI_RANGE(0.01)
173         }
174 };
175
176 static const struct comedi_lrange range_das08_pgm = {
177         9, {
178                 BIP_RANGE(10),
179                 BIP_RANGE(5),
180                 BIP_RANGE(0.5),
181                 BIP_RANGE(0.05),
182                 BIP_RANGE(0.01),
183                 UNI_RANGE(10),
184                 UNI_RANGE(1),
185                 UNI_RANGE(0.1),
186                 UNI_RANGE(0.01)
187         }
188 };                              /*
189                                    cio-das08jr.pdf
190
191                                    "das08/jr-ao"
192
193                                    0 a/d bits 0-3            unused
194                                    1 a/d bits 4-11           start 12 bit
195                                    2 eoc, mux                mux
196                                    3 di                      do
197                                    4 unused                  ao0_lsb
198                                    5 unused                  ao0_msb
199                                    6 unused                  ao1_lsb
200                                    7 unused                  ao1_msb
201
202                                  */
203
204 static const struct comedi_lrange *const das08_ai_lranges[] = {
205         [das08_pg_none]         = &range_unknown,
206         [das08_bipolar5]        = &range_bipolar5,
207         [das08_pgh]             = &range_das08_pgh,
208         [das08_pgl]             = &range_das08_pgl,
209         [das08_pgm]             = &range_das08_pgm,
210 };
211
212 static const int das08_pgh_gainlist[] = {
213         8, 0, 10, 2, 12, 4, 14, 6, 1, 3, 5, 7
214 };
215 static const int das08_pgl_gainlist[] = { 8, 0, 2, 4, 6, 1, 3, 5, 7 };
216 static const int das08_pgm_gainlist[] = { 8, 0, 10, 12, 14, 9, 11, 13, 15 };
217
218 static const int *const das08_gainlists[] = {
219         [das08_pg_none]         = NULL,
220         [das08_bipolar5]        = NULL,
221         [das08_pgh]             = das08_pgh_gainlist,
222         [das08_pgl]             = das08_pgl_gainlist,
223         [das08_pgm]             = das08_pgm_gainlist,
224 };
225
226 static int das08_ai_eoc(struct comedi_device *dev,
227                         struct comedi_subdevice *s,
228                         struct comedi_insn *insn,
229                         unsigned long context)
230 {
231         unsigned int status;
232
233         status = inb(dev->iobase + DAS08_STATUS_REG);
234         if ((status & DAS08_STATUS_AI_BUSY) == 0)
235                 return 0;
236         return -EBUSY;
237 }
238
239 static int das08_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
240                           struct comedi_insn *insn, unsigned int *data)
241 {
242         const struct das08_board_struct *thisboard = dev->board_ptr;
243         struct das08_private_struct *devpriv = dev->private;
244         int n;
245         int chan;
246         int range;
247         int lsb, msb;
248         int ret;
249
250         chan = CR_CHAN(insn->chanspec);
251         range = CR_RANGE(insn->chanspec);
252
253         /* clear crap */
254         inb(dev->iobase + DAS08_AI_LSB_REG);
255         inb(dev->iobase + DAS08_AI_MSB_REG);
256
257         /* set multiplexer */
258         /*  lock to prevent race with digital output */
259         spin_lock(&dev->spinlock);
260         devpriv->do_mux_bits &= ~DAS08_CONTROL_MUX_MASK;
261         devpriv->do_mux_bits |= DAS08_CONTROL_MUX(chan);
262         outb(devpriv->do_mux_bits, dev->iobase + DAS08_CONTROL_REG);
263         spin_unlock(&dev->spinlock);
264
265         if (devpriv->pg_gainlist) {
266                 /* set gain/range */
267                 range = CR_RANGE(insn->chanspec);
268                 outb(devpriv->pg_gainlist[range],
269                      dev->iobase + DAS08_GAIN_REG);
270         }
271
272         for (n = 0; n < insn->n; n++) {
273                 /* clear over-range bits for 16-bit boards */
274                 if (thisboard->ai_nbits == 16)
275                         if (inb(dev->iobase + DAS08_AI_MSB_REG) & 0x80)
276                                 dev_info(dev->class_dev, "over-range\n");
277
278                 /* trigger conversion */
279                 outb_p(0, dev->iobase + DAS08_AI_TRIG_REG);
280
281                 ret = comedi_timeout(dev, s, insn, das08_ai_eoc, 0);
282                 if (ret)
283                         return ret;
284
285                 msb = inb(dev->iobase + DAS08_AI_MSB_REG);
286                 lsb = inb(dev->iobase + DAS08_AI_LSB_REG);
287                 if (thisboard->ai_encoding == das08_encode12) {
288                         data[n] = (lsb >> 4) | (msb << 4);
289                 } else if (thisboard->ai_encoding == das08_pcm_encode12) {
290                         data[n] = (msb << 8) + lsb;
291                 } else if (thisboard->ai_encoding == das08_encode16) {
292                         /* FPOS 16-bit boards are sign-magnitude */
293                         if (msb & 0x80)
294                                 data[n] = (1 << 15) | lsb | ((msb & 0x7f) << 8);
295                         else
296                                 data[n] = (1 << 15) - (lsb | (msb & 0x7f) << 8);
297                 } else {
298                         dev_err(dev->class_dev, "bug! unknown ai encoding\n");
299                         return -1;
300                 }
301         }
302
303         return n;
304 }
305
306 static int das08_di_rbits(struct comedi_device *dev, struct comedi_subdevice *s,
307                           struct comedi_insn *insn, unsigned int *data)
308 {
309         data[0] = 0;
310         data[1] = DAS08_STATUS_DI(inb(dev->iobase + DAS08_STATUS_REG));
311
312         return insn->n;
313 }
314
315 static int das08_do_wbits(struct comedi_device *dev,
316                           struct comedi_subdevice *s,
317                           struct comedi_insn *insn,
318                           unsigned int *data)
319 {
320         struct das08_private_struct *devpriv = dev->private;
321
322         if (comedi_dio_update_state(s, data)) {
323                 /* prevent race with setting of analog input mux */
324                 spin_lock(&dev->spinlock);
325                 devpriv->do_mux_bits &= ~DAS08_CONTROL_DO_MASK;
326                 devpriv->do_mux_bits |= DAS08_CONTROL_DO(s->state);
327                 outb(devpriv->do_mux_bits, dev->iobase + DAS08_CONTROL_REG);
328                 spin_unlock(&dev->spinlock);
329         }
330
331         data[1] = s->state;
332
333         return insn->n;
334 }
335
336 static int das08jr_di_rbits(struct comedi_device *dev,
337                             struct comedi_subdevice *s,
338                             struct comedi_insn *insn, unsigned int *data)
339 {
340         data[0] = 0;
341         data[1] = inb(dev->iobase + DAS08JR_DI_REG);
342
343         return insn->n;
344 }
345
346 static int das08jr_do_wbits(struct comedi_device *dev,
347                             struct comedi_subdevice *s,
348                             struct comedi_insn *insn,
349                             unsigned int *data)
350 {
351         if (comedi_dio_update_state(s, data))
352                 outb(s->state, dev->iobase + DAS08JR_DO_REG);
353
354         data[1] = s->state;
355
356         return insn->n;
357 }
358
359 static void das08_ao_set_data(struct comedi_device *dev,
360                               unsigned int chan, unsigned int data)
361 {
362         const struct das08_board_struct *thisboard = dev->board_ptr;
363         unsigned char lsb;
364         unsigned char msb;
365
366         lsb = data & 0xff;
367         msb = (data >> 8) & 0xff;
368         if (thisboard->is_jr) {
369                 outb(lsb, dev->iobase + DAS08JR_AO_LSB_REG(chan));
370                 outb(msb, dev->iobase + DAS08JR_AO_MSB_REG(chan));
371                 /* load DACs */
372                 inb(dev->iobase + DAS08JR_AO_UPDATE_REG);
373         } else {
374                 outb(lsb, dev->iobase + DAS08AOX_AO_LSB_REG(chan));
375                 outb(msb, dev->iobase + DAS08AOX_AO_MSB_REG(chan));
376                 /* load DACs */
377                 inb(dev->iobase + DAS08AO_AO_UPDATE);
378         }
379 }
380
381 static int das08_ao_insn_write(struct comedi_device *dev,
382                                struct comedi_subdevice *s,
383                                struct comedi_insn *insn,
384                                unsigned int *data)
385 {
386         unsigned int chan = CR_CHAN(insn->chanspec);
387         unsigned int val = s->readback[chan];
388         int i;
389
390         for (i = 0; i < insn->n; i++) {
391                 val = data[i];
392                 das08_ao_set_data(dev, chan, val);
393         }
394         s->readback[chan] = val;
395
396         return insn->n;
397 }
398
399 int das08_common_attach(struct comedi_device *dev, unsigned long iobase)
400 {
401         const struct das08_board_struct *thisboard = dev->board_ptr;
402         struct das08_private_struct *devpriv = dev->private;
403         struct comedi_subdevice *s;
404         int ret;
405         int i;
406
407         dev->iobase = iobase;
408
409         dev->board_name = thisboard->name;
410
411         ret = comedi_alloc_subdevices(dev, 6);
412         if (ret)
413                 return ret;
414
415         s = &dev->subdevices[0];
416         /* ai */
417         if (thisboard->ai_nbits) {
418                 s->type = COMEDI_SUBD_AI;
419                 /* XXX some boards actually have differential
420                  * inputs instead of single ended.
421                  * The driver does nothing with arefs though,
422                  * so it's no big deal.
423                  */
424                 s->subdev_flags = SDF_READABLE | SDF_GROUND;
425                 s->n_chan = 8;
426                 s->maxdata = (1 << thisboard->ai_nbits) - 1;
427                 s->range_table = das08_ai_lranges[thisboard->ai_pg];
428                 s->insn_read = das08_ai_rinsn;
429                 devpriv->pg_gainlist = das08_gainlists[thisboard->ai_pg];
430         } else {
431                 s->type = COMEDI_SUBD_UNUSED;
432         }
433
434         s = &dev->subdevices[1];
435         /* ao */
436         if (thisboard->ao_nbits) {
437                 s->type = COMEDI_SUBD_AO;
438                 s->subdev_flags = SDF_WRITABLE;
439                 s->n_chan = 2;
440                 s->maxdata = (1 << thisboard->ao_nbits) - 1;
441                 s->range_table = &range_bipolar5;
442                 s->insn_write = das08_ao_insn_write;
443
444                 ret = comedi_alloc_subdev_readback(s);
445                 if (ret)
446                         return ret;
447
448                 /* initialize all channels to 0V */
449                 for (i = 0; i < s->n_chan; i++) {
450                         s->readback[i] = s->maxdata / 2;
451                         das08_ao_set_data(dev, i, s->readback[i]);
452                 }
453         } else {
454                 s->type = COMEDI_SUBD_UNUSED;
455         }
456
457         s = &dev->subdevices[2];
458         /* di */
459         if (thisboard->di_nchan) {
460                 s->type = COMEDI_SUBD_DI;
461                 s->subdev_flags = SDF_READABLE;
462                 s->n_chan = thisboard->di_nchan;
463                 s->maxdata = 1;
464                 s->range_table = &range_digital;
465                 s->insn_bits =
466                         thisboard->is_jr ? das08jr_di_rbits : das08_di_rbits;
467         } else {
468                 s->type = COMEDI_SUBD_UNUSED;
469         }
470
471         s = &dev->subdevices[3];
472         /* do */
473         if (thisboard->do_nchan) {
474                 s->type = COMEDI_SUBD_DO;
475                 s->subdev_flags = SDF_WRITABLE;
476                 s->n_chan = thisboard->do_nchan;
477                 s->maxdata = 1;
478                 s->range_table = &range_digital;
479                 s->insn_bits =
480                         thisboard->is_jr ? das08jr_do_wbits : das08_do_wbits;
481         } else {
482                 s->type = COMEDI_SUBD_UNUSED;
483         }
484
485         s = &dev->subdevices[4];
486         /* 8255 */
487         if (thisboard->i8255_offset != 0) {
488                 ret = subdev_8255_init(dev, s, NULL, thisboard->i8255_offset);
489                 if (ret)
490                         return ret;
491         } else {
492                 s->type = COMEDI_SUBD_UNUSED;
493         }
494
495         /* Counter subdevice (8254) */
496         s = &dev->subdevices[5];
497         if (thisboard->i8254_offset) {
498                 dev->pacer = comedi_8254_init(dev->iobase +
499                                               thisboard->i8254_offset,
500                                               0, I8254_IO8, 0);
501                 if (!dev->pacer)
502                         return -ENOMEM;
503
504                 comedi_8254_subdevice_init(s, dev->pacer);
505         } else {
506                 s->type = COMEDI_SUBD_UNUSED;
507         }
508
509         return 0;
510 }
511 EXPORT_SYMBOL_GPL(das08_common_attach);
512
513 static int __init das08_init(void)
514 {
515         return 0;
516 }
517 module_init(das08_init);
518
519 static void __exit das08_exit(void)
520 {
521 }
522 module_exit(das08_exit);
523
524 MODULE_AUTHOR("Comedi http://www.comedi.org");
525 MODULE_DESCRIPTION("Comedi low-level driver");
526 MODULE_LICENSE("GPL");