staging: comedi: ni_pcidio: remove custom DPRINTK macro
[firefly-linux-kernel-4.4.55.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,
283                           struct comedi_subdevice *s,
284                           struct comedi_insn *insn,
285                           unsigned int *data)
286 {
287         struct das08_private_struct *devpriv = dev->private;
288
289         if (comedi_dio_update_state(s, data)) {
290                 /* prevent race with setting of analog input mux */
291                 spin_lock(&dev->spinlock);
292                 devpriv->do_mux_bits &= ~DAS08_DO_MASK;
293                 devpriv->do_mux_bits |= DAS08_OP(s->state);
294                 outb(devpriv->do_mux_bits, dev->iobase + DAS08_CONTROL);
295                 spin_unlock(&dev->spinlock);
296         }
297
298         data[1] = s->state;
299
300         return insn->n;
301 }
302
303 static int das08jr_di_rbits(struct comedi_device *dev,
304                             struct comedi_subdevice *s,
305                             struct comedi_insn *insn, unsigned int *data)
306 {
307         data[0] = 0;
308         data[1] = inb(dev->iobase + DAS08JR_DIO);
309
310         return insn->n;
311 }
312
313 static int das08jr_do_wbits(struct comedi_device *dev,
314                             struct comedi_subdevice *s,
315                             struct comedi_insn *insn,
316                             unsigned int *data)
317 {
318         if (comedi_dio_update_state(s, data))
319                 outb(s->state, dev->iobase + DAS08JR_DIO);
320
321         data[1] = s->state;
322
323         return insn->n;
324 }
325
326 static void das08_ao_set_data(struct comedi_device *dev,
327                               unsigned int chan, unsigned int data)
328 {
329         const struct das08_board_struct *thisboard = comedi_board(dev);
330         struct das08_private_struct *devpriv = dev->private;
331         unsigned char lsb;
332         unsigned char msb;
333
334         lsb = data & 0xff;
335         msb = (data >> 8) & 0xff;
336         if (thisboard->is_jr) {
337                 outb(lsb, dev->iobase + DAS08JR_AO_LSB(chan));
338                 outb(msb, dev->iobase + DAS08JR_AO_MSB(chan));
339                 /* load DACs */
340                 inb(dev->iobase + DAS08JR_DIO);
341         } else {
342                 outb(lsb, dev->iobase + DAS08AO_AO_LSB(chan));
343                 outb(msb, dev->iobase + DAS08AO_AO_MSB(chan));
344                 /* load DACs */
345                 inb(dev->iobase + DAS08AO_AO_UPDATE);
346         }
347         devpriv->ao_readback[chan] = data;
348 }
349
350 static void das08_ao_initialize(struct comedi_device *dev,
351                                 struct comedi_subdevice *s)
352 {
353         int n;
354         unsigned int data;
355
356         data = s->maxdata / 2;  /* should be about 0 volts */
357         for (n = 0; n < s->n_chan; n++)
358                 das08_ao_set_data(dev, n, data);
359 }
360
361 static int das08_ao_winsn(struct comedi_device *dev,
362                           struct comedi_subdevice *s,
363                           struct comedi_insn *insn, unsigned int *data)
364 {
365         unsigned int n;
366         unsigned int chan;
367
368         chan = CR_CHAN(insn->chanspec);
369
370         for (n = 0; n < insn->n; n++)
371                 das08_ao_set_data(dev, chan, *data);
372
373         return n;
374 }
375
376 static int das08_ao_rinsn(struct comedi_device *dev,
377                           struct comedi_subdevice *s,
378                           struct comedi_insn *insn, unsigned int *data)
379 {
380         struct das08_private_struct *devpriv = dev->private;
381         unsigned int n;
382         unsigned int chan;
383
384         chan = CR_CHAN(insn->chanspec);
385
386         for (n = 0; n < insn->n; n++)
387                 data[n] = devpriv->ao_readback[chan];
388
389         return n;
390 }
391
392 static void i8254_initialize(struct comedi_device *dev)
393 {
394         const struct das08_board_struct *thisboard = comedi_board(dev);
395         unsigned long i8254_iobase = dev->iobase + thisboard->i8254_offset;
396         unsigned int mode = I8254_MODE0 | I8254_BINARY;
397         int i;
398
399         for (i = 0; i < 3; ++i)
400                 i8254_set_mode(i8254_iobase, 0, i, mode);
401 }
402
403 static int das08_counter_read(struct comedi_device *dev,
404                               struct comedi_subdevice *s,
405                               struct comedi_insn *insn, unsigned int *data)
406 {
407         const struct das08_board_struct *thisboard = comedi_board(dev);
408         unsigned long i8254_iobase = dev->iobase + thisboard->i8254_offset;
409         int chan = insn->chanspec;
410
411         data[0] = i8254_read(i8254_iobase, 0, chan);
412         return 1;
413 }
414
415 static int das08_counter_write(struct comedi_device *dev,
416                                struct comedi_subdevice *s,
417                                struct comedi_insn *insn, unsigned int *data)
418 {
419         const struct das08_board_struct *thisboard = comedi_board(dev);
420         unsigned long i8254_iobase = dev->iobase + thisboard->i8254_offset;
421         int chan = insn->chanspec;
422
423         i8254_write(i8254_iobase, 0, chan, data[0]);
424         return 1;
425 }
426
427 static int das08_counter_config(struct comedi_device *dev,
428                                 struct comedi_subdevice *s,
429                                 struct comedi_insn *insn, unsigned int *data)
430 {
431         const struct das08_board_struct *thisboard = comedi_board(dev);
432         unsigned long i8254_iobase = dev->iobase + thisboard->i8254_offset;
433         int chan = insn->chanspec;
434
435         switch (data[0]) {
436         case INSN_CONFIG_SET_COUNTER_MODE:
437                 i8254_set_mode(i8254_iobase, 0, chan, data[1]);
438                 break;
439         case INSN_CONFIG_8254_READ_STATUS:
440                 data[1] = i8254_status(i8254_iobase, 0, chan);
441                 break;
442         default:
443                 return -EINVAL;
444                 break;
445         }
446         return 2;
447 }
448
449 int das08_common_attach(struct comedi_device *dev, unsigned long iobase)
450 {
451         const struct das08_board_struct *thisboard = comedi_board(dev);
452         struct das08_private_struct *devpriv = dev->private;
453         struct comedi_subdevice *s;
454         int ret;
455
456         dev->iobase = iobase;
457
458         dev->board_name = thisboard->name;
459
460         ret = comedi_alloc_subdevices(dev, 6);
461         if (ret)
462                 return ret;
463
464         s = &dev->subdevices[0];
465         /* ai */
466         if (thisboard->ai_nbits) {
467                 s->type = COMEDI_SUBD_AI;
468                 /* XXX some boards actually have differential
469                  * inputs instead of single ended.
470                  * The driver does nothing with arefs though,
471                  * so it's no big deal.
472                  */
473                 s->subdev_flags = SDF_READABLE | SDF_GROUND;
474                 s->n_chan = 8;
475                 s->maxdata = (1 << thisboard->ai_nbits) - 1;
476                 s->range_table = das08_ai_lranges[thisboard->ai_pg];
477                 s->insn_read = das08_ai_rinsn;
478                 devpriv->pg_gainlist = das08_gainlists[thisboard->ai_pg];
479         } else {
480                 s->type = COMEDI_SUBD_UNUSED;
481         }
482
483         s = &dev->subdevices[1];
484         /* ao */
485         if (thisboard->ao_nbits) {
486                 s->type = COMEDI_SUBD_AO;
487                 s->subdev_flags = SDF_WRITABLE;
488                 s->n_chan = 2;
489                 s->maxdata = (1 << thisboard->ao_nbits) - 1;
490                 s->range_table = &range_bipolar5;
491                 s->insn_write = das08_ao_winsn;
492                 s->insn_read = das08_ao_rinsn;
493                 das08_ao_initialize(dev, s);
494         } else {
495                 s->type = COMEDI_SUBD_UNUSED;
496         }
497
498         s = &dev->subdevices[2];
499         /* di */
500         if (thisboard->di_nchan) {
501                 s->type = COMEDI_SUBD_DI;
502                 s->subdev_flags = SDF_READABLE;
503                 s->n_chan = thisboard->di_nchan;
504                 s->maxdata = 1;
505                 s->range_table = &range_digital;
506                 s->insn_bits =
507                         thisboard->is_jr ? das08jr_di_rbits : das08_di_rbits;
508         } else {
509                 s->type = COMEDI_SUBD_UNUSED;
510         }
511
512         s = &dev->subdevices[3];
513         /* do */
514         if (thisboard->do_nchan) {
515                 s->type = COMEDI_SUBD_DO;
516                 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
517                 s->n_chan = thisboard->do_nchan;
518                 s->maxdata = 1;
519                 s->range_table = &range_digital;
520                 s->insn_bits =
521                         thisboard->is_jr ? das08jr_do_wbits : das08_do_wbits;
522         } else {
523                 s->type = COMEDI_SUBD_UNUSED;
524         }
525
526         s = &dev->subdevices[4];
527         /* 8255 */
528         if (thisboard->i8255_offset != 0) {
529                 subdev_8255_init(dev, s, NULL, (unsigned long)(dev->iobase +
530                                                                thisboard->
531                                                                i8255_offset));
532         } else {
533                 s->type = COMEDI_SUBD_UNUSED;
534         }
535
536         s = &dev->subdevices[5];
537         /* 8254 */
538         if (thisboard->i8254_offset != 0) {
539                 s->type = COMEDI_SUBD_COUNTER;
540                 s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
541                 s->n_chan = 3;
542                 s->maxdata = 0xFFFF;
543                 s->insn_read = das08_counter_read;
544                 s->insn_write = das08_counter_write;
545                 s->insn_config = das08_counter_config;
546                 i8254_initialize(dev);
547         } else {
548                 s->type = COMEDI_SUBD_UNUSED;
549         }
550
551         return 0;
552 }
553 EXPORT_SYMBOL_GPL(das08_common_attach);
554
555 static int __init das08_init(void)
556 {
557         return 0;
558 }
559 module_init(das08_init);
560
561 static void __exit das08_exit(void)
562 {
563 }
564 module_exit(das08_exit);
565
566 MODULE_AUTHOR("Comedi http://www.comedi.org");
567 MODULE_DESCRIPTION("Comedi low-level driver");
568 MODULE_LICENSE("GPL");