Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / staging / comedi / drivers / mpc624.c
1 /*
2     comedi/drivers/mpc624.c
3     Hardware driver for a Micro/sys inc. MPC-624 PC/104 board
4
5     COMEDI - Linux Control and Measurement Device Interface
6     Copyright (C) 2000 David A. Schleef <ds@schleef.org>
7
8     This program is free software; you can redistribute it and/or modify
9     it under the terms of the GNU General Public License as published by
10     the Free Software Foundation; either version 2 of the License, or
11     (at your option) any later version.
12
13     This program is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU General Public License for more details.
17 */
18 /*
19 Driver: mpc624
20 Description: Micro/sys MPC-624 PC/104 board
21 Devices: [Micro/sys] MPC-624 (mpc624)
22 Author: Stanislaw Raczynski <sraczynski@op.pl>
23 Updated: Thu, 15 Sep 2005 12:01:18 +0200
24 Status: working
25
26     The Micro/sys MPC-624 board is based on the LTC2440 24-bit sigma-delta
27     ADC chip.
28
29     Subdevices supported by the driver:
30     - Analog In:   supported
31     - Digital I/O: not supported
32     - LEDs:        not supported
33     - EEPROM:      not supported
34
35 Configuration Options:
36   [0] - I/O base address
37   [1] - conversion rate
38         Conversion rate  RMS noise  Effective Number Of Bits
39         0      3.52kHz        23uV                17
40         1      1.76kHz       3.5uV                20
41         2       880Hz         2uV                21.3
42         3       440Hz        1.4uV               21.8
43         4       220Hz         1uV                22.4
44         5       110Hz        750uV               22.9
45         6       55Hz         510nV               23.4
46         7      27.5Hz        375nV                24
47         8      13.75Hz       250nV               24.4
48         9      6.875Hz       200nV               24.6
49   [2] - voltage range
50         0      -1.01V .. +1.01V
51         1      -10.1V .. +10.1V
52 */
53
54 #include <linux/module.h>
55 #include "../comedidev.h"
56
57 #include <linux/delay.h>
58
59 /* Offsets of different ports */
60 #define MPC624_MASTER_CONTROL   0 /* not used */
61 #define MPC624_GNMUXCH          1 /* Gain, Mux, Channel of ADC */
62 #define MPC624_ADC              2 /* read/write to/from ADC */
63 #define MPC624_EE               3 /* read/write to/from serial EEPROM via I2C */
64 #define MPC624_LEDS             4 /* write to LEDs */
65 #define MPC624_DIO              5 /* read/write to/from digital I/O ports */
66 #define MPC624_IRQ_MASK         6 /* IRQ masking enable/disable */
67
68 /* Register bits' names */
69 #define MPC624_ADBUSY           (1<<5)
70 #define MPC624_ADSDO            (1<<4)
71 #define MPC624_ADFO             (1<<3)
72 #define MPC624_ADCS             (1<<2)
73 #define MPC624_ADSCK            (1<<1)
74 #define MPC624_ADSDI            (1<<0)
75
76 /* SDI Speed/Resolution Programming bits */
77 #define MPC624_OSR4             (1<<31)
78 #define MPC624_OSR3             (1<<30)
79 #define MPC624_OSR2             (1<<29)
80 #define MPC624_OSR1             (1<<28)
81 #define MPC624_OSR0             (1<<27)
82
83 /* 32-bit output value bits' names */
84 #define MPC624_EOC_BIT          (1<<31)
85 #define MPC624_DMY_BIT          (1<<30)
86 #define MPC624_SGN_BIT          (1<<29)
87
88 /* Conversion speeds */
89 /* OSR4 OSR3 OSR2 OSR1 OSR0  Conversion rate  RMS noise  ENOB^
90  *  X    0    0    0    1        3.52kHz        23uV      17
91  *  X    0    0    1    0        1.76kHz       3.5uV      20
92  *  X    0    0    1    1         880Hz         2uV      21.3
93  *  X    0    1    0    0         440Hz        1.4uV     21.8
94  *  X    0    1    0    1         220Hz         1uV      22.4
95  *  X    0    1    1    0         110Hz        750uV     22.9
96  *  X    0    1    1    1          55Hz        510nV     23.4
97  *  X    1    0    0    0         27.5Hz       375nV      24
98  *  X    1    0    0    1        13.75Hz       250nV     24.4
99  *  X    1    1    1    1        6.875Hz       200nV     24.6
100  *
101  * ^ - Effective Number Of Bits
102  */
103
104 #define MPC624_SPEED_3_52_kHz (MPC624_OSR4 | MPC624_OSR0)
105 #define MPC624_SPEED_1_76_kHz (MPC624_OSR4 | MPC624_OSR1)
106 #define MPC624_SPEED_880_Hz   (MPC624_OSR4 | MPC624_OSR1 | MPC624_OSR0)
107 #define MPC624_SPEED_440_Hz   (MPC624_OSR4 | MPC624_OSR2)
108 #define MPC624_SPEED_220_Hz   (MPC624_OSR4 | MPC624_OSR2 | MPC624_OSR0)
109 #define MPC624_SPEED_110_Hz   (MPC624_OSR4 | MPC624_OSR2 | MPC624_OSR1)
110 #define MPC624_SPEED_55_Hz \
111         (MPC624_OSR4 | MPC624_OSR2 | MPC624_OSR1 | MPC624_OSR0)
112 #define MPC624_SPEED_27_5_Hz  (MPC624_OSR4 | MPC624_OSR3)
113 #define MPC624_SPEED_13_75_Hz (MPC624_OSR4 | MPC624_OSR3 | MPC624_OSR0)
114 #define MPC624_SPEED_6_875_Hz \
115         (MPC624_OSR4 | MPC624_OSR3 | MPC624_OSR2 | MPC624_OSR1 | MPC624_OSR0)
116 /* -------------------------------------------------------------------------- */
117 struct mpc624_private {
118         /*  set by mpc624_attach() from driver's parameters */
119         unsigned long int ulConvertionRate;
120 };
121
122 /* -------------------------------------------------------------------------- */
123 static const struct comedi_lrange range_mpc624_bipolar1 = {
124         1,
125         {
126 /* BIP_RANGE(1.01)  this is correct, */
127          /*  but my MPC-624 actually seems to have a range of 2.02 */
128          BIP_RANGE(2.02)
129          }
130 };
131
132 static const struct comedi_lrange range_mpc624_bipolar10 = {
133         1,
134         {
135 /* BIP_RANGE(10.1)   this is correct, */
136          /*  but my MPC-624 actually seems to have a range of 20.2 */
137          BIP_RANGE(20.2)
138          }
139 };
140
141 static int mpc624_ai_eoc(struct comedi_device *dev,
142                          struct comedi_subdevice *s,
143                          struct comedi_insn *insn,
144                          unsigned long context)
145 {
146         unsigned char status;
147
148         status = inb(dev->iobase + MPC624_ADC);
149         if ((status & MPC624_ADBUSY) == 0)
150                 return 0;
151         return -EBUSY;
152 }
153
154 static int mpc624_ai_rinsn(struct comedi_device *dev,
155                            struct comedi_subdevice *s, struct comedi_insn *insn,
156                            unsigned int *data)
157 {
158         struct mpc624_private *devpriv = dev->private;
159         int n, i;
160         unsigned long int data_in, data_out;
161         int ret;
162
163         /*
164          *  WARNING:
165          *  We always write 0 to GNSWA bit, so the channel range is +-/10.1Vdc
166          */
167         outb(insn->chanspec, dev->iobase + MPC624_GNMUXCH);
168
169         for (n = 0; n < insn->n; n++) {
170                 /*  Trigger the conversion */
171                 outb(MPC624_ADSCK, dev->iobase + MPC624_ADC);
172                 udelay(1);
173                 outb(MPC624_ADCS | MPC624_ADSCK, dev->iobase + MPC624_ADC);
174                 udelay(1);
175                 outb(0, dev->iobase + MPC624_ADC);
176                 udelay(1);
177
178                 /*  Wait for the conversion to end */
179                 ret = comedi_timeout(dev, s, insn, mpc624_ai_eoc, 0);
180                 if (ret)
181                         return ret;
182
183                 /*  Start reading data */
184                 data_in = 0;
185                 data_out = devpriv->ulConvertionRate;
186                 udelay(1);
187                 for (i = 0; i < 32; i++) {
188                         /*  Set the clock low */
189                         outb(0, dev->iobase + MPC624_ADC);
190                         udelay(1);
191
192                         if (data_out & (1 << 31)) { /*  the next bit is a 1 */
193                                 /*  Set the ADSDI line (send to MPC624) */
194                                 outb(MPC624_ADSDI, dev->iobase + MPC624_ADC);
195                                 udelay(1);
196                                 /*  Set the clock high */
197                                 outb(MPC624_ADSCK | MPC624_ADSDI,
198                                      dev->iobase + MPC624_ADC);
199                         } else {        /*  the next bit is a 0 */
200
201                                 /*  Set the ADSDI line (send to MPC624) */
202                                 outb(0, dev->iobase + MPC624_ADC);
203                                 udelay(1);
204                                 /*  Set the clock high */
205                                 outb(MPC624_ADSCK, dev->iobase + MPC624_ADC);
206                         }
207                         /*  Read ADSDO on high clock (receive from MPC624) */
208                         udelay(1);
209                         data_in <<= 1;
210                         data_in |=
211                             (inb(dev->iobase + MPC624_ADC) & MPC624_ADSDO) >> 4;
212                         udelay(1);
213
214                         data_out <<= 1;
215                 }
216
217                 /*
218                  *  Received 32-bit long value consist of:
219                  *    31: EOC -
220                  *          (End Of Transmission) bit - should be 0
221                  *    30: DMY
222                  *          (Dummy) bit - should be 0
223                  *    29: SIG
224                  *          (Sign) bit- 1 if the voltage is positive,
225                  *                      0 if negative
226                  *    28: MSB
227                  *          (Most Significant Bit) - the first bit of
228                  *                                   the conversion result
229                  *    ....
230                  *    05: LSB
231                  *          (Least Significant Bit)- the last bit of the
232                  *                                   conversion result
233                  *    04-00: sub-LSB
234                  *          - sub-LSBs are basically noise, but when
235                  *            averaged properly, they can increase conversion
236                  *            precision up to 29 bits; they can be discarded
237                  *            without loss of resolution.
238                  */
239
240                 if (data_in & MPC624_EOC_BIT)
241                         dev_dbg(dev->class_dev,
242                                 "EOC bit is set (data_in=%lu)!", data_in);
243                 if (data_in & MPC624_DMY_BIT)
244                         dev_dbg(dev->class_dev,
245                                 "DMY bit is set (data_in=%lu)!", data_in);
246                 if (data_in & MPC624_SGN_BIT) { /* Volatge is positive */
247                         /*
248                          * comedi operates on unsigned numbers, so mask off EOC
249                          * and DMY and don't clear the SGN bit
250                          */
251                         data_in &= 0x3FFFFFFF;
252                         data[n] = data_in;
253                 } else { /*  The voltage is negative */
254                         /*
255                          * data_in contains a number in 30-bit two's complement
256                          * code and we must deal with it
257                          */
258                         data_in |= MPC624_SGN_BIT;
259                         data_in = ~data_in;
260                         data_in += 1;
261                         data_in &= ~(MPC624_EOC_BIT | MPC624_DMY_BIT);
262                         /*  clear EOC and DMY bits */
263                         data_in = 0x20000000 - data_in;
264                         data[n] = data_in;
265                 }
266         }
267
268         /*  Return the number of samples read/written */
269         return n;
270 }
271
272 static int mpc624_attach(struct comedi_device *dev, struct comedi_devconfig *it)
273 {
274         struct mpc624_private *devpriv;
275         struct comedi_subdevice *s;
276         int ret;
277
278         ret = comedi_request_region(dev, it->options[0], 0x10);
279         if (ret)
280                 return ret;
281
282         devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
283         if (!devpriv)
284                 return -ENOMEM;
285
286         switch (it->options[1]) {
287         case 0:
288                 devpriv->ulConvertionRate = MPC624_SPEED_3_52_kHz;
289                 break;
290         case 1:
291                 devpriv->ulConvertionRate = MPC624_SPEED_1_76_kHz;
292                 break;
293         case 2:
294                 devpriv->ulConvertionRate = MPC624_SPEED_880_Hz;
295                 break;
296         case 3:
297                 devpriv->ulConvertionRate = MPC624_SPEED_440_Hz;
298                 break;
299         case 4:
300                 devpriv->ulConvertionRate = MPC624_SPEED_220_Hz;
301                 break;
302         case 5:
303                 devpriv->ulConvertionRate = MPC624_SPEED_110_Hz;
304                 break;
305         case 6:
306                 devpriv->ulConvertionRate = MPC624_SPEED_55_Hz;
307                 break;
308         case 7:
309                 devpriv->ulConvertionRate = MPC624_SPEED_27_5_Hz;
310                 break;
311         case 8:
312                 devpriv->ulConvertionRate = MPC624_SPEED_13_75_Hz;
313                 break;
314         case 9:
315                 devpriv->ulConvertionRate = MPC624_SPEED_6_875_Hz;
316                 break;
317         default:
318                 devpriv->ulConvertionRate = MPC624_SPEED_3_52_kHz;
319         }
320
321         ret = comedi_alloc_subdevices(dev, 1);
322         if (ret)
323                 return ret;
324
325         s = &dev->subdevices[0];
326         s->type = COMEDI_SUBD_AI;
327         s->subdev_flags = SDF_READABLE | SDF_DIFF;
328         s->n_chan = 8;
329         switch (it->options[1]) {
330         default:
331                 s->maxdata = 0x3FFFFFFF;
332         }
333
334         switch (it->options[1]) {
335         case 0:
336                 s->range_table = &range_mpc624_bipolar1;
337                 break;
338         default:
339                 s->range_table = &range_mpc624_bipolar10;
340         }
341         s->len_chanlist = 1;
342         s->insn_read = mpc624_ai_rinsn;
343
344         return 0;
345 }
346
347 static struct comedi_driver mpc624_driver = {
348         .driver_name    = "mpc624",
349         .module         = THIS_MODULE,
350         .attach         = mpc624_attach,
351         .detach         = comedi_legacy_detach,
352 };
353 module_comedi_driver(mpc624_driver);
354
355 MODULE_AUTHOR("Comedi http://www.comedi.org");
356 MODULE_DESCRIPTION("Comedi low-level driver");
357 MODULE_LICENSE("GPL");