staging: comedi: das08: rename and rewrite DAS08_EOC
[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 #define   DAS08_IRQ                     (1<<3)
63 #define   DAS08_IP(x)                   (((x)>>4)&0x7)
64 #define DAS08_CONTROL           2
65 #define   DAS08_MUX_MASK        0x7
66 #define   DAS08_MUX(x)          ((x) & DAS08_MUX_MASK)
67 #define   DAS08_INTE                    (1<<3)
68 #define   DAS08_DO_MASK         0xf0
69 #define   DAS08_OP(x)           (((x) << 4) & DAS08_DO_MASK)
70
71 /*
72     cio-das08jr.pdf
73
74   "das08/jr-ao"
75
76   0     a/d bits 0-3            unused
77   1     a/d bits 4-11           start 12 bit
78   2     eoc, mux                mux
79   3     di                      do
80   4     unused                  ao0_lsb
81   5     unused                  ao0_msb
82   6     unused                  ao1_lsb
83   7     unused                  ao1_msb
84
85 */
86
87 #define DAS08JR_DIO             3
88 #define DAS08JR_AO_LSB(x)       ((x) ? 6 : 4)
89 #define DAS08JR_AO_MSB(x)       ((x) ? 7 : 5)
90
91 /*
92     cio-das08_aox.pdf
93
94   "das08-aoh"
95   "das08-aol"
96   "das08-aom"
97
98   0     a/d bits 0-3            start 8 bit
99   1     a/d bits 4-11           start 12 bit
100   2     eoc, ip1-3, irq, mux    op1-4, inte, mux
101   3     mux, gain status        gain control
102   4567  8254
103   8     unused                  ao0_lsb
104   9     unused                  ao0_msb
105   a     unused                  ao1_lsb
106   b     unused                  ao1_msb
107   89ab
108   cdef  8255
109 */
110
111 #define DAS08AO_GAIN_CONTROL    3
112 #define DAS08AO_GAIN_STATUS     3
113
114 #define DAS08AO_AO_LSB(x)       ((x) ? 0xa : 8)
115 #define DAS08AO_AO_MSB(x)       ((x) ? 0xb : 9)
116 #define DAS08AO_AO_UPDATE       8
117
118 /* gainlist same as _pgx_ below */
119
120 static const struct comedi_lrange range_das08_pgl = {
121         9, {
122                 BIP_RANGE(10),
123                 BIP_RANGE(5),
124                 BIP_RANGE(2.5),
125                 BIP_RANGE(1.25),
126                 BIP_RANGE(0.625),
127                 UNI_RANGE(10),
128                 UNI_RANGE(5),
129                 UNI_RANGE(2.5),
130                 UNI_RANGE(1.25)
131         }
132 };
133
134 static const struct comedi_lrange range_das08_pgh = {
135         12, {
136                 BIP_RANGE(10),
137                 BIP_RANGE(5),
138                 BIP_RANGE(1),
139                 BIP_RANGE(0.5),
140                 BIP_RANGE(0.1),
141                 BIP_RANGE(0.05),
142                 BIP_RANGE(0.01),
143                 BIP_RANGE(0.005),
144                 UNI_RANGE(10),
145                 UNI_RANGE(1),
146                 UNI_RANGE(0.1),
147                 UNI_RANGE(0.01)
148         }
149 };
150
151 static const struct comedi_lrange range_das08_pgm = {
152         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         [das08_pg_none]         = &range_unknown,
181         [das08_bipolar5]        = &range_bipolar5,
182         [das08_pgh]             = &range_das08_pgh,
183         [das08_pgl]             = &range_das08_pgl,
184         [das08_pgm]             = &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         [das08_pg_none]         = NULL,
195         [das08_bipolar5]        = NULL,
196         [das08_pgh]             = das08_pgh_gainlist,
197         [das08_pgl]             = das08_pgl_gainlist,
198         [das08_pgm]             = das08_pgm_gainlist,
199 };
200
201 static int das08_ai_eoc(struct comedi_device *dev,
202                         struct comedi_subdevice *s,
203                         struct comedi_insn *insn,
204                         unsigned long context)
205 {
206         unsigned int status;
207
208         status = inb(dev->iobase + DAS08_STATUS_REG);
209         if ((status & DAS08_STATUS_AI_BUSY) == 0)
210                 return 0;
211         return -EBUSY;
212 }
213
214 static int das08_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
215                           struct comedi_insn *insn, unsigned int *data)
216 {
217         const struct das08_board_struct *thisboard = dev->board_ptr;
218         struct das08_private_struct *devpriv = dev->private;
219         int n;
220         int chan;
221         int range;
222         int lsb, msb;
223         int ret;
224
225         chan = CR_CHAN(insn->chanspec);
226         range = CR_RANGE(insn->chanspec);
227
228         /* clear crap */
229         inb(dev->iobase + DAS08_AI_LSB_REG);
230         inb(dev->iobase + DAS08_AI_MSB_REG);
231
232         /* set multiplexer */
233         /*  lock to prevent race with digital output */
234         spin_lock(&dev->spinlock);
235         devpriv->do_mux_bits &= ~DAS08_MUX_MASK;
236         devpriv->do_mux_bits |= DAS08_MUX(chan);
237         outb(devpriv->do_mux_bits, dev->iobase + DAS08_CONTROL);
238         spin_unlock(&dev->spinlock);
239
240         if (devpriv->pg_gainlist) {
241                 /* set gain/range */
242                 range = CR_RANGE(insn->chanspec);
243                 outb(devpriv->pg_gainlist[range],
244                      dev->iobase + DAS08AO_GAIN_CONTROL);
245         }
246
247         for (n = 0; n < insn->n; n++) {
248                 /* clear over-range bits for 16-bit boards */
249                 if (thisboard->ai_nbits == 16)
250                         if (inb(dev->iobase + DAS08_AI_MSB_REG) & 0x80)
251                                 dev_info(dev->class_dev, "over-range\n");
252
253                 /* trigger conversion */
254                 outb_p(0, dev->iobase + DAS08_AI_TRIG_REG);
255
256                 ret = comedi_timeout(dev, s, insn, das08_ai_eoc, 0);
257                 if (ret)
258                         return ret;
259
260                 msb = inb(dev->iobase + DAS08_AI_MSB_REG);
261                 lsb = inb(dev->iobase + DAS08_AI_LSB_REG);
262                 if (thisboard->ai_encoding == das08_encode12) {
263                         data[n] = (lsb >> 4) | (msb << 4);
264                 } else if (thisboard->ai_encoding == das08_pcm_encode12) {
265                         data[n] = (msb << 8) + lsb;
266                 } else if (thisboard->ai_encoding == das08_encode16) {
267                         /* FPOS 16-bit boards are sign-magnitude */
268                         if (msb & 0x80)
269                                 data[n] = (1 << 15) | lsb | ((msb & 0x7f) << 8);
270                         else
271                                 data[n] = (1 << 15) - (lsb | (msb & 0x7f) << 8);
272                 } else {
273                         dev_err(dev->class_dev, "bug! unknown ai encoding\n");
274                         return -1;
275                 }
276         }
277
278         return n;
279 }
280
281 static int das08_di_rbits(struct comedi_device *dev, struct comedi_subdevice *s,
282                           struct comedi_insn *insn, unsigned int *data)
283 {
284         data[0] = 0;
285         data[1] = DAS08_IP(inb(dev->iobase + DAS08_STATUS_REG));
286
287         return insn->n;
288 }
289
290 static int das08_do_wbits(struct comedi_device *dev,
291                           struct comedi_subdevice *s,
292                           struct comedi_insn *insn,
293                           unsigned int *data)
294 {
295         struct das08_private_struct *devpriv = dev->private;
296
297         if (comedi_dio_update_state(s, data)) {
298                 /* prevent race with setting of analog input mux */
299                 spin_lock(&dev->spinlock);
300                 devpriv->do_mux_bits &= ~DAS08_DO_MASK;
301                 devpriv->do_mux_bits |= DAS08_OP(s->state);
302                 outb(devpriv->do_mux_bits, dev->iobase + DAS08_CONTROL);
303                 spin_unlock(&dev->spinlock);
304         }
305
306         data[1] = s->state;
307
308         return insn->n;
309 }
310
311 static int das08jr_di_rbits(struct comedi_device *dev,
312                             struct comedi_subdevice *s,
313                             struct comedi_insn *insn, unsigned int *data)
314 {
315         data[0] = 0;
316         data[1] = inb(dev->iobase + DAS08JR_DIO);
317
318         return insn->n;
319 }
320
321 static int das08jr_do_wbits(struct comedi_device *dev,
322                             struct comedi_subdevice *s,
323                             struct comedi_insn *insn,
324                             unsigned int *data)
325 {
326         if (comedi_dio_update_state(s, data))
327                 outb(s->state, dev->iobase + DAS08JR_DIO);
328
329         data[1] = s->state;
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 = dev->board_ptr;
338         unsigned char lsb;
339         unsigned char msb;
340
341         lsb = data & 0xff;
342         msb = (data >> 8) & 0xff;
343         if (thisboard->is_jr) {
344                 outb(lsb, dev->iobase + DAS08JR_AO_LSB(chan));
345                 outb(msb, dev->iobase + DAS08JR_AO_MSB(chan));
346                 /* load DACs */
347                 inb(dev->iobase + DAS08JR_DIO);
348         } else {
349                 outb(lsb, dev->iobase + DAS08AO_AO_LSB(chan));
350                 outb(msb, dev->iobase + DAS08AO_AO_MSB(chan));
351                 /* load DACs */
352                 inb(dev->iobase + DAS08AO_AO_UPDATE);
353         }
354 }
355
356 static int das08_ao_insn_write(struct comedi_device *dev,
357                                struct comedi_subdevice *s,
358                                struct comedi_insn *insn,
359                                unsigned int *data)
360 {
361         unsigned int chan = CR_CHAN(insn->chanspec);
362         unsigned int val = s->readback[chan];
363         int i;
364
365         for (i = 0; i < insn->n; i++) {
366                 val = data[i];
367                 das08_ao_set_data(dev, chan, val);
368         }
369         s->readback[chan] = val;
370
371         return insn->n;
372 }
373
374 int das08_common_attach(struct comedi_device *dev, unsigned long iobase)
375 {
376         const struct das08_board_struct *thisboard = dev->board_ptr;
377         struct das08_private_struct *devpriv = dev->private;
378         struct comedi_subdevice *s;
379         int ret;
380         int i;
381
382         dev->iobase = iobase;
383
384         dev->board_name = thisboard->name;
385
386         ret = comedi_alloc_subdevices(dev, 6);
387         if (ret)
388                 return ret;
389
390         s = &dev->subdevices[0];
391         /* ai */
392         if (thisboard->ai_nbits) {
393                 s->type = COMEDI_SUBD_AI;
394                 /* XXX some boards actually have differential
395                  * inputs instead of single ended.
396                  * The driver does nothing with arefs though,
397                  * so it's no big deal.
398                  */
399                 s->subdev_flags = SDF_READABLE | SDF_GROUND;
400                 s->n_chan = 8;
401                 s->maxdata = (1 << thisboard->ai_nbits) - 1;
402                 s->range_table = das08_ai_lranges[thisboard->ai_pg];
403                 s->insn_read = das08_ai_rinsn;
404                 devpriv->pg_gainlist = das08_gainlists[thisboard->ai_pg];
405         } else {
406                 s->type = COMEDI_SUBD_UNUSED;
407         }
408
409         s = &dev->subdevices[1];
410         /* ao */
411         if (thisboard->ao_nbits) {
412                 s->type = COMEDI_SUBD_AO;
413                 s->subdev_flags = SDF_WRITABLE;
414                 s->n_chan = 2;
415                 s->maxdata = (1 << thisboard->ao_nbits) - 1;
416                 s->range_table = &range_bipolar5;
417                 s->insn_write = das08_ao_insn_write;
418
419                 ret = comedi_alloc_subdev_readback(s);
420                 if (ret)
421                         return ret;
422
423                 /* initialize all channels to 0V */
424                 for (i = 0; i < s->n_chan; i++) {
425                         s->readback[i] = s->maxdata / 2;
426                         das08_ao_set_data(dev, i, s->readback[i]);
427                 }
428         } else {
429                 s->type = COMEDI_SUBD_UNUSED;
430         }
431
432         s = &dev->subdevices[2];
433         /* di */
434         if (thisboard->di_nchan) {
435                 s->type = COMEDI_SUBD_DI;
436                 s->subdev_flags = SDF_READABLE;
437                 s->n_chan = thisboard->di_nchan;
438                 s->maxdata = 1;
439                 s->range_table = &range_digital;
440                 s->insn_bits =
441                         thisboard->is_jr ? das08jr_di_rbits : das08_di_rbits;
442         } else {
443                 s->type = COMEDI_SUBD_UNUSED;
444         }
445
446         s = &dev->subdevices[3];
447         /* do */
448         if (thisboard->do_nchan) {
449                 s->type = COMEDI_SUBD_DO;
450                 s->subdev_flags = SDF_WRITABLE;
451                 s->n_chan = thisboard->do_nchan;
452                 s->maxdata = 1;
453                 s->range_table = &range_digital;
454                 s->insn_bits =
455                         thisboard->is_jr ? das08jr_do_wbits : das08_do_wbits;
456         } else {
457                 s->type = COMEDI_SUBD_UNUSED;
458         }
459
460         s = &dev->subdevices[4];
461         /* 8255 */
462         if (thisboard->i8255_offset != 0) {
463                 ret = subdev_8255_init(dev, s, NULL, thisboard->i8255_offset);
464                 if (ret)
465                         return ret;
466         } else {
467                 s->type = COMEDI_SUBD_UNUSED;
468         }
469
470         /* Counter subdevice (8254) */
471         s = &dev->subdevices[5];
472         if (thisboard->i8254_offset) {
473                 dev->pacer = comedi_8254_init(dev->iobase +
474                                               thisboard->i8254_offset,
475                                               0, I8254_IO8, 0);
476                 if (!dev->pacer)
477                         return -ENOMEM;
478
479                 comedi_8254_subdevice_init(s, dev->pacer);
480         } else {
481                 s->type = COMEDI_SUBD_UNUSED;
482         }
483
484         return 0;
485 }
486 EXPORT_SYMBOL_GPL(das08_common_attach);
487
488 static int __init das08_init(void)
489 {
490         return 0;
491 }
492 module_init(das08_init);
493
494 static void __exit das08_exit(void)
495 {
496 }
497 module_exit(das08_exit);
498
499 MODULE_AUTHOR("Comedi http://www.comedi.org");
500 MODULE_DESCRIPTION("Comedi low-level driver");
501 MODULE_LICENSE("GPL");