- patches.arch/x86_mce_intel_decode_physical_address.patch:
[linux-flexiantxendom0-3.2.10.git] / 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     You should have received a copy of the GNU General Public License
19     along with this program; if not, write to the Free Software
20     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
22 */
23 /*
24 Driver: mpc624
25 Description: Micro/sys MPC-624 PC/104 board
26 Devices: [Micro/sys] MPC-624 (mpc624)
27 Author: Stanislaw Raczynski <sraczynski@op.pl>
28 Updated: Thu, 15 Sep 2005 12:01:18 +0200
29 Status: working
30
31     The Micro/sys MPC-624 board is based on the LTC2440 24-bit sigma-delta
32     ADC chip.
33
34     Subdevices supported by the driver:
35     - Analog In:   supported
36     - Digital I/O: not supported
37     - LEDs:        not supported
38     - EEPROM:      not supported
39
40 Configuration Options:
41   [0] - I/O base address
42   [1] - convertion rate
43         Convertion rate  RMS noise  Effective Number Of Bits
44         0      3.52kHz        23uV                17
45         1      1.76kHz       3.5uV                20
46         2       880Hz         2uV                21.3
47         3       440Hz        1.4uV               21.8
48         4       220Hz         1uV                22.4
49         5       110Hz        750uV               22.9
50         6       55Hz         510nV               23.4
51         7      27.5Hz        375nV                24
52         8      13.75Hz       250nV               24.4
53         9      6.875Hz       200nV               24.6
54   [2] - voltage range
55         0      -1.01V .. +1.01V
56         1      -10.1V .. +10.1V
57 */
58
59 #include "../comedidev.h"
60
61 #include <linux/ioport.h>
62 #include <linux/delay.h>
63
64 /* Consecutive I/O port addresses */
65 #define MPC624_SIZE             16
66
67 /* Offsets of different ports */
68 #define MPC624_MASTER_CONTROL   0 /* not used */
69 #define MPC624_GNMUXCH          1 /* Gain, Mux, Channel of ADC */
70 #define MPC624_ADC              2 /* read/write to/from ADC */
71 #define MPC624_EE               3 /* read/write to/from serial EEPROM via I2C */
72 #define MPC624_LEDS             4 /* write to LEDs */
73 #define MPC624_DIO              5 /* read/write to/from digital I/O ports */
74 #define MPC624_IRQ_MASK         6 /* IRQ masking enable/disable */
75
76 /* Register bits' names */
77 #define MPC624_ADBUSY           (1<<5)
78 #define MPC624_ADSDO            (1<<4)
79 #define MPC624_ADFO             (1<<3)
80 #define MPC624_ADCS             (1<<2)
81 #define MPC624_ADSCK            (1<<1)
82 #define MPC624_ADSDI            (1<<0)
83
84 /* SDI Speed/Resolution Programming bits */
85 #define MPC624_OSR4             (1<<31)
86 #define MPC624_OSR3             (1<<30)
87 #define MPC624_OSR2             (1<<29)
88 #define MPC624_OSR1             (1<<28)
89 #define MPC624_OSR0             (1<<27)
90
91 /* 32-bit output value bits' names */
92 #define MPC624_EOC_BIT          (1<<31)
93 #define MPC624_DMY_BIT          (1<<30)
94 #define MPC624_SGN_BIT          (1<<29)
95
96 /* Convertion speeds */
97 /* OSR4 OSR3 OSR2 OSR1 OSR0  Convertion rate  RMS noise  ENOB^
98  *  X    0    0    0    1        3.52kHz        23uV      17
99  *  X    0    0    1    0        1.76kHz       3.5uV      20
100  *  X    0    0    1    1         880Hz         2uV      21.3
101  *  X    0    1    0    0         440Hz        1.4uV     21.8
102  *  X    0    1    0    1         220Hz         1uV      22.4
103  *  X    0    1    1    0         110Hz        750uV     22.9
104  *  X    0    1    1    1          55Hz        510nV     23.4
105  *  X    1    0    0    0         27.5Hz       375nV      24
106  *  X    1    0    0    1        13.75Hz       250nV     24.4
107  *  X    1    1    1    1        6.875Hz       200nV     24.6
108  *
109  * ^ - Effective Number Of Bits
110  */
111
112 #define MPC624_SPEED_3_52_kHz (MPC624_OSR4 | MPC624_OSR0)
113 #define MPC624_SPEED_1_76_kHz (MPC624_OSR4 | MPC624_OSR1)
114 #define MPC624_SPEED_880_Hz   (MPC624_OSR4 | MPC624_OSR1 | MPC624_OSR0)
115 #define MPC624_SPEED_440_Hz   (MPC624_OSR4 | MPC624_OSR2)
116 #define MPC624_SPEED_220_Hz   (MPC624_OSR4 | MPC624_OSR2 | MPC624_OSR0)
117 #define MPC624_SPEED_110_Hz   (MPC624_OSR4 | MPC624_OSR2 | MPC624_OSR1)
118 #define MPC624_SPEED_55_Hz \
119         (MPC624_OSR4 | MPC624_OSR2 | MPC624_OSR1 | MPC624_OSR0)
120 #define MPC624_SPEED_27_5_Hz  (MPC624_OSR4 | MPC624_OSR3)
121 #define MPC624_SPEED_13_75_Hz (MPC624_OSR4 | MPC624_OSR3 | MPC624_OSR0)
122 #define MPC624_SPEED_6_875_Hz \
123         (MPC624_OSR4 | MPC624_OSR3 | MPC624_OSR2 | MPC624_OSR1 | MPC624_OSR0)
124 /* -------------------------------------------------------------------------- */
125 struct skel_private {
126
127         /*  set by mpc624_attach() from driver's parameters */
128         unsigned long int ulConvertionRate;
129 };
130
131 #define devpriv ((struct skel_private *)dev->private)
132 /* -------------------------------------------------------------------------- */
133 static const struct comedi_lrange range_mpc624_bipolar1 = {
134         1,
135         {
136 /* BIP_RANGE(1.01)  this is correct, */
137          /*  but my MPC-624 actually seems to have a range of 2.02 */
138          BIP_RANGE(2.02)
139          }
140 };
141
142 static const struct comedi_lrange range_mpc624_bipolar10 = {
143         1,
144         {
145 /* BIP_RANGE(10.1)   this is correct, */
146          /*  but my MPC-624 actually seems to have a range of 20.2 */
147          BIP_RANGE(20.2)
148          }
149 };
150
151 /* -------------------------------------------------------------------------- */
152 static int mpc624_attach(struct comedi_device *dev,
153                          struct comedi_devconfig *it);
154 static int mpc624_detach(struct comedi_device *dev);
155 /* -------------------------------------------------------------------------- */
156 static struct comedi_driver driver_mpc624 = {
157         .driver_name = "mpc624",
158         .module = THIS_MODULE,
159         .attach = mpc624_attach,
160         .detach = mpc624_detach
161 };
162
163 /* -------------------------------------------------------------------------- */
164 static int mpc624_ai_rinsn(struct comedi_device *dev,
165                            struct comedi_subdevice *s, struct comedi_insn *insn,
166                            unsigned int *data);
167 /* -------------------------------------------------------------------------- */
168 static int mpc624_attach(struct comedi_device *dev, struct comedi_devconfig *it)
169 {
170         struct comedi_subdevice *s;
171         unsigned long iobase;
172
173         iobase = it->options[0];
174         printk(KERN_INFO "comedi%d: mpc624 [0x%04lx, ", dev->minor, iobase);
175         if (request_region(iobase, MPC624_SIZE, "mpc624") == NULL) {
176                 printk(KERN_ERR "I/O port(s) in use\n");
177                 return -EIO;
178         }
179
180         dev->iobase = iobase;
181         dev->board_name = "mpc624";
182
183         /*  Private structure initialization */
184         if (alloc_private(dev, sizeof(struct skel_private)) < 0)
185                 return -ENOMEM;
186
187         switch (it->options[1]) {
188         case 0:
189                 devpriv->ulConvertionRate = MPC624_SPEED_3_52_kHz;
190                 printk(KERN_INFO "3.52 kHz, ");
191                 break;
192         case 1:
193                 devpriv->ulConvertionRate = MPC624_SPEED_1_76_kHz;
194                 printk(KERN_INFO "1.76 kHz, ");
195                 break;
196         case 2:
197                 devpriv->ulConvertionRate = MPC624_SPEED_880_Hz;
198                 printk(KERN_INFO "880 Hz, ");
199                 break;
200         case 3:
201                 devpriv->ulConvertionRate = MPC624_SPEED_440_Hz;
202                 printk(KERN_INFO "440 Hz, ");
203                 break;
204         case 4:
205                 devpriv->ulConvertionRate = MPC624_SPEED_220_Hz;
206                 printk(KERN_INFO "220 Hz, ");
207                 break;
208         case 5:
209                 devpriv->ulConvertionRate = MPC624_SPEED_110_Hz;
210                 printk(KERN_INFO "110 Hz, ");
211                 break;
212         case 6:
213                 devpriv->ulConvertionRate = MPC624_SPEED_55_Hz;
214                 printk(KERN_INFO "55 Hz, ");
215                 break;
216         case 7:
217                 devpriv->ulConvertionRate = MPC624_SPEED_27_5_Hz;
218                 printk(KERN_INFO "27.5 Hz, ");
219                 break;
220         case 8:
221                 devpriv->ulConvertionRate = MPC624_SPEED_13_75_Hz;
222                 printk(KERN_INFO "13.75 Hz, ");
223                 break;
224         case 9:
225                 devpriv->ulConvertionRate = MPC624_SPEED_6_875_Hz;
226                 printk(KERN_INFO "6.875 Hz, ");
227                 break;
228         default:
229                 printk
230                     (KERN_ERR "illegal convertion rate setting!"
231                         " Valid numbers are 0..9. Using 9 => 6.875 Hz, ");
232                 devpriv->ulConvertionRate = MPC624_SPEED_3_52_kHz;
233         }
234
235         /*  Subdevices structures */
236         if (alloc_subdevices(dev, 1) < 0)
237                 return -ENOMEM;
238
239         s = dev->subdevices + 0;
240         s->type = COMEDI_SUBD_AI;
241         s->subdev_flags = SDF_READABLE | SDF_DIFF;
242         s->n_chan = 8;
243         switch (it->options[1]) {
244         default:
245                 s->maxdata = 0x3FFFFFFF;
246                 printk(KERN_INFO "30 bit, ");
247         }
248
249         switch (it->options[1]) {
250         case 0:
251                 s->range_table = &range_mpc624_bipolar1;
252                 printk(KERN_INFO "1.01V]: ");
253                 break;
254         default:
255                 s->range_table = &range_mpc624_bipolar10;
256                 printk(KERN_INFO "10.1V]: ");
257         }
258         s->len_chanlist = 1;
259         s->insn_read = mpc624_ai_rinsn;
260
261         printk(KERN_INFO "attached\n");
262
263         return 1;
264 }
265
266 static int mpc624_detach(struct comedi_device *dev)
267 {
268         printk(KERN_INFO "comedi%d: mpc624: remove\n", dev->minor);
269
270         if (dev->iobase)
271                 release_region(dev->iobase, MPC624_SIZE);
272
273         return 0;
274 }
275
276 /* Timeout 200ms */
277 #define TIMEOUT 200
278
279 static int mpc624_ai_rinsn(struct comedi_device *dev,
280                            struct comedi_subdevice *s, struct comedi_insn *insn,
281                            unsigned int *data)
282 {
283         int n, i;
284         unsigned long int data_in, data_out;
285         unsigned char ucPort;
286
287         /*
288          *  WARNING:
289          *  We always write 0 to GNSWA bit, so the channel range is +-/10.1Vdc
290          */
291         outb(insn->chanspec, dev->iobase + MPC624_GNMUXCH);
292 /* printk("Channel %d:\n", insn->chanspec); */
293         if (!insn->n) {
294                 printk(KERN_INFO "MPC624: Warning, no data to acquire\n");
295                 return 0;
296         }
297
298         for (n = 0; n < insn->n; n++) {
299                 /*  Trigger the convertion */
300                 outb(MPC624_ADSCK, dev->iobase + MPC624_ADC);
301                 udelay(1);
302                 outb(MPC624_ADCS | MPC624_ADSCK, dev->iobase + MPC624_ADC);
303                 udelay(1);
304                 outb(0, dev->iobase + MPC624_ADC);
305                 udelay(1);
306
307                 /*  Wait for the convertion to end */
308                 for (i = 0; i < TIMEOUT; i++) {
309                         ucPort = inb(dev->iobase + MPC624_ADC);
310                         if (ucPort & MPC624_ADBUSY)
311                                 udelay(1000);
312                         else
313                                 break;
314                 }
315                 if (i == TIMEOUT) {
316                         printk(KERN_ERR "MPC624: timeout (%dms)\n", TIMEOUT);
317                         data[n] = 0;
318                         return -ETIMEDOUT;
319                 }
320                 /*  Start reading data */
321                 data_in = 0;
322                 data_out = devpriv->ulConvertionRate;
323                 udelay(1);
324                 for (i = 0; i < 32; i++) {
325                         /*  Set the clock low */
326                         outb(0, dev->iobase + MPC624_ADC);
327                         udelay(1);
328
329                         if (data_out & (1 << 31)) { /*  the next bit is a 1 */
330                                 /*  Set the ADSDI line (send to MPC624) */
331                                 outb(MPC624_ADSDI, dev->iobase + MPC624_ADC);
332                                 udelay(1);
333                                 /*  Set the clock high */
334                                 outb(MPC624_ADSCK | MPC624_ADSDI,
335                                      dev->iobase + MPC624_ADC);
336                         } else {        /*  the next bit is a 0 */
337
338                                 /*  Set the ADSDI line (send to MPC624) */
339                                 outb(0, dev->iobase + MPC624_ADC);
340                                 udelay(1);
341                                 /*  Set the clock high */
342                                 outb(MPC624_ADSCK, dev->iobase + MPC624_ADC);
343                         }
344                         /*  Read ADSDO on high clock (receive from MPC624) */
345                         udelay(1);
346                         data_in <<= 1;
347                         data_in |=
348                             (inb(dev->iobase + MPC624_ADC) & MPC624_ADSDO) >> 4;
349                         udelay(1);
350
351                         data_out <<= 1;
352                 }
353
354                 /*
355                  *  Received 32-bit long value consist of:
356                  *    31: EOC -
357                  *          (End Of Transmission) bit - should be 0
358                  *    30: DMY
359                  *          (Dummy) bit - should be 0
360                  *    29: SIG
361                  *          (Sign) bit- 1 if the voltage is positive,
362                  *                      0 if negative
363                  *    28: MSB
364                  *          (Most Significant Bit) - the first bit of
365                  *                                   the conversion result
366                  *    ....
367                  *    05: LSB
368                  *          (Least Significant Bit)- the last bit of the
369                  *                                   conversion result
370                  *    04-00: sub-LSB
371                  *          - sub-LSBs are basically noise, but when
372                  *            averaged properly, they can increase conversion
373                  *            precision up to 29 bits; they can be discarded
374                  *            without loss of resolution.
375                  */
376
377                 if (data_in & MPC624_EOC_BIT)
378                         printk(KERN_INFO "MPC624:EOC bit is set (data_in=%lu)!",
379                                data_in);
380                 if (data_in & MPC624_DMY_BIT)
381                         printk(KERN_INFO "MPC624:DMY bit is set (data_in=%lu)!",
382                                data_in);
383                 if (data_in & MPC624_SGN_BIT) { /* Volatge is positive */
384                         /*
385                          * comedi operates on unsigned numbers, so mask off EOC
386                          * and DMY and don't clear the SGN bit
387                          */
388                         data_in &= 0x3FFFFFFF;
389                         data[n] = data_in;
390                 } else { /*  The voltage is negative */
391                         /*
392                          * data_in contains a number in 30-bit two's complement
393                          * code and we must deal with it
394                          */
395                         data_in |= MPC624_SGN_BIT;
396                         data_in = ~data_in;
397                         data_in += 1;
398                         data_in &= ~(MPC624_EOC_BIT | MPC624_DMY_BIT);
399                         /*  clear EOC and DMY bits */
400                         data_in = 0x20000000 - data_in;
401                         data[n] = data_in;
402                 }
403         }
404
405         /*  Return the number of samples read/written */
406         return n;
407 }
408
409 COMEDI_INITCLEANUP(driver_mpc624);