Commit | Line | Data |
---|---|---|
90a5038d SR |
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 (MPC624_OSR4 | MPC624_OSR2 | MPC624_OSR1 | MPC624_OSR0) | |
119 | #define MPC624_SPEED_27_5_Hz (MPC624_OSR4 | MPC624_OSR3 ) | |
120 | #define MPC624_SPEED_13_75_Hz (MPC624_OSR4 | MPC624_OSR3 | MPC624_OSR0) | |
121 | #define MPC624_SPEED_6_875_Hz (MPC624_OSR4 | MPC624_OSR3 | MPC624_OSR2 | MPC624_OSR1 | MPC624_OSR0) | |
122 | //---------------------------------------------------------------------------- | |
123 | typedef struct { | |
124 | unsigned long int ulConvertionRate; // set by mpc624_attach() from driver's parameters | |
125 | } skel_private; | |
126 | ||
127 | #define devpriv ((skel_private *)dev->private) | |
128 | //---------------------------------------------------------------------------- | |
129 | static const comedi_lrange range_mpc624_bipolar1 = { | |
130 | 1, | |
131 | { | |
132 | // BIP_RANGE(1.01) // this is correct, | |
133 | // but my MPC-624 actually seems to have a range of 2.02 | |
134 | BIP_RANGE(2.02) | |
135 | } | |
136 | }; | |
137 | static const comedi_lrange range_mpc624_bipolar10 = { | |
138 | 1, | |
139 | { | |
140 | // BIP_RANGE(10.1) // this is correct, | |
141 | // but my MPC-624 actually seems to have a range of 20.2 | |
142 | BIP_RANGE(20.2) | |
143 | } | |
144 | }; | |
145 | ||
146 | //---------------------------------------------------------------------------- | |
71b5f4f1 BP |
147 | static int mpc624_attach(struct comedi_device * dev, comedi_devconfig * it); |
148 | static int mpc624_detach(struct comedi_device * dev); | |
90a5038d | 149 | //---------------------------------------------------------------------------- |
139dfbdf | 150 | static struct comedi_driver driver_mpc624 = { |
90a5038d SR |
151 | driver_name:"mpc624", |
152 | module:THIS_MODULE, | |
153 | attach:mpc624_attach, | |
154 | detach:mpc624_detach | |
155 | }; | |
156 | ||
157 | //---------------------------------------------------------------------------- | |
34c43922 | 158 | static int mpc624_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s, |
790c5541 | 159 | comedi_insn * insn, unsigned int * data); |
90a5038d | 160 | //---------------------------------------------------------------------------- |
71b5f4f1 | 161 | static int mpc624_attach(struct comedi_device * dev, comedi_devconfig * it) |
90a5038d | 162 | { |
34c43922 | 163 | struct comedi_subdevice *s; |
90a5038d SR |
164 | unsigned long iobase; |
165 | ||
166 | iobase = it->options[0]; | |
167 | rt_printk("comedi%d: mpc624 [0x%04lx, ", dev->minor, iobase); | |
168 | if (request_region(iobase, MPC624_SIZE, "mpc624") == NULL) { | |
169 | rt_printk("I/O port(s) in use\n"); | |
170 | return -EIO; | |
171 | } | |
172 | ||
173 | dev->iobase = iobase; | |
174 | dev->board_name = "mpc624"; | |
175 | ||
176 | // Private structure initialization | |
177 | if (alloc_private(dev, sizeof(skel_private)) < 0) | |
178 | return -ENOMEM; | |
179 | ||
180 | switch (it->options[1]) { | |
181 | case 0: | |
182 | devpriv->ulConvertionRate = MPC624_SPEED_3_52_kHz; | |
183 | rt_printk("3.52 kHz, "); | |
184 | break; | |
185 | case 1: | |
186 | devpriv->ulConvertionRate = MPC624_SPEED_1_76_kHz; | |
187 | rt_printk("1.76 kHz, "); | |
188 | break; | |
189 | case 2: | |
190 | devpriv->ulConvertionRate = MPC624_SPEED_880_Hz; | |
191 | rt_printk("880 Hz, "); | |
192 | break; | |
193 | case 3: | |
194 | devpriv->ulConvertionRate = MPC624_SPEED_440_Hz; | |
195 | rt_printk("440 Hz, "); | |
196 | break; | |
197 | case 4: | |
198 | devpriv->ulConvertionRate = MPC624_SPEED_220_Hz; | |
199 | rt_printk("220 Hz, "); | |
200 | break; | |
201 | case 5: | |
202 | devpriv->ulConvertionRate = MPC624_SPEED_110_Hz; | |
203 | rt_printk("110 Hz, "); | |
204 | break; | |
205 | case 6: | |
206 | devpriv->ulConvertionRate = MPC624_SPEED_55_Hz; | |
207 | rt_printk("55 Hz, "); | |
208 | break; | |
209 | case 7: | |
210 | devpriv->ulConvertionRate = MPC624_SPEED_27_5_Hz; | |
211 | rt_printk("27.5 Hz, "); | |
212 | break; | |
213 | case 8: | |
214 | devpriv->ulConvertionRate = MPC624_SPEED_13_75_Hz; | |
215 | rt_printk("13.75 Hz, "); | |
216 | break; | |
217 | case 9: | |
218 | devpriv->ulConvertionRate = MPC624_SPEED_6_875_Hz; | |
219 | rt_printk("6.875 Hz, "); | |
220 | break; | |
221 | default: | |
222 | rt_printk | |
223 | ("illegal convertion rate setting! Valid numbers are 0..9. Using 9 => 6.875 Hz, "); | |
224 | devpriv->ulConvertionRate = MPC624_SPEED_3_52_kHz; | |
225 | } | |
226 | ||
227 | // Subdevices structures | |
228 | if (alloc_subdevices(dev, 1) < 0) | |
229 | return -ENOMEM; | |
230 | ||
231 | s = dev->subdevices + 0; | |
232 | s->type = COMEDI_SUBD_AI; | |
233 | s->subdev_flags = SDF_READABLE | SDF_DIFF; | |
234 | s->n_chan = 8; | |
235 | switch (it->options[1]) { | |
236 | default: | |
237 | s->maxdata = 0x3FFFFFFF; | |
238 | rt_printk("30 bit, "); | |
239 | } | |
240 | ||
241 | switch (it->options[1]) { | |
242 | case 0: | |
243 | s->range_table = &range_mpc624_bipolar1; | |
244 | rt_printk("1.01V]: "); | |
245 | break; | |
246 | default: | |
247 | s->range_table = &range_mpc624_bipolar10; | |
248 | rt_printk("10.1V]: "); | |
249 | } | |
250 | s->len_chanlist = 1; | |
251 | s->insn_read = mpc624_ai_rinsn; | |
252 | ||
253 | rt_printk("attached\n"); | |
254 | ||
255 | return 1; | |
256 | } | |
257 | ||
71b5f4f1 | 258 | static int mpc624_detach(struct comedi_device * dev) |
90a5038d SR |
259 | { |
260 | rt_printk("comedi%d: mpc624: remove\n", dev->minor); | |
261 | ||
262 | if (dev->iobase) | |
263 | release_region(dev->iobase, MPC624_SIZE); | |
264 | ||
265 | return 0; | |
266 | } | |
267 | ||
268 | // Timeout 200ms | |
269 | #define TIMEOUT 200 | |
270 | ||
34c43922 | 271 | static int mpc624_ai_rinsn(struct comedi_device * dev, struct comedi_subdevice * s, |
790c5541 | 272 | comedi_insn * insn, unsigned int * data) |
90a5038d SR |
273 | { |
274 | int n, i; | |
275 | unsigned long int data_in, data_out; | |
276 | unsigned char ucPort; | |
277 | ||
278 | // WARNING: We always write 0 to GNSWA bit, so the channel range is +-/10.1Vdc | |
279 | outb(insn->chanspec, dev->iobase + MPC624_GNMUXCH); | |
280 | // rt_printk("Channel %d: \n", insn->chanspec); | |
281 | if (!insn->n) { | |
282 | rt_printk("MPC624: Warning, no data to aquire\n"); | |
283 | return 0; | |
284 | } | |
285 | ||
286 | for (n = 0; n < insn->n; n++) { | |
287 | // Trigger the convertion | |
288 | outb(MPC624_ADSCK, dev->iobase + MPC624_ADC); | |
289 | comedi_udelay(1); | |
290 | outb(MPC624_ADCS | MPC624_ADSCK, dev->iobase + MPC624_ADC); | |
291 | comedi_udelay(1); | |
292 | outb(0, dev->iobase + MPC624_ADC); | |
293 | comedi_udelay(1); | |
294 | ||
295 | // Wait for the convertion to end | |
296 | for (i = 0; i < TIMEOUT; i++) { | |
297 | ucPort = inb(dev->iobase + MPC624_ADC); | |
298 | if (ucPort & MPC624_ADBUSY) | |
299 | comedi_udelay(1000); | |
300 | else | |
301 | break; | |
302 | } | |
303 | if (i == TIMEOUT) { | |
304 | rt_printk("MPC624: timeout (%dms)\n", TIMEOUT); | |
305 | data[n] = 0; | |
306 | return -ETIMEDOUT; | |
307 | } | |
308 | // Start reading data | |
309 | data_in = 0; | |
310 | data_out = devpriv->ulConvertionRate; | |
311 | comedi_udelay(1); | |
312 | for (i = 0; i < 32; i++) { | |
313 | // Set the clock low | |
314 | outb(0, dev->iobase + MPC624_ADC); | |
315 | comedi_udelay(1); | |
316 | ||
317 | if (data_out & (1 << 31)) // the next bit is a 1 | |
318 | { | |
319 | // Set the ADSDI line (send to MPC624) | |
320 | outb(MPC624_ADSDI, dev->iobase + MPC624_ADC); | |
321 | comedi_udelay(1); | |
322 | // Set the clock high | |
323 | outb(MPC624_ADSCK | MPC624_ADSDI, | |
324 | dev->iobase + MPC624_ADC); | |
325 | } else // the next bit is a 0 | |
326 | { | |
327 | // Set the ADSDI line (send to MPC624) | |
328 | outb(0, dev->iobase + MPC624_ADC); | |
329 | comedi_udelay(1); | |
330 | // Set the clock high | |
331 | outb(MPC624_ADSCK, dev->iobase + MPC624_ADC); | |
332 | } | |
333 | // Read ADSDO on high clock (receive from MPC624) | |
334 | comedi_udelay(1); | |
335 | data_in <<= 1; | |
336 | data_in |= | |
337 | (inb(dev->iobase + | |
338 | MPC624_ADC) & MPC624_ADSDO) >> 4; | |
339 | comedi_udelay(1); | |
340 | ||
341 | data_out <<= 1; | |
342 | } | |
343 | ||
344 | // Received 32-bit long value consist of: | |
345 | // 31: EOC (End Of Transmission) bit - should be 0 | |
346 | // 30: DMY (Dummy) bit - should be 0 | |
347 | // 29: SIG (Sign) bit - 1 if the voltage is positive, 0 if negative | |
348 | // 28: MSB (Most Significant Bit) - the first bit of convertion result | |
349 | // .... | |
350 | // 05: LSB (Least Significant Bit) - the last bit of convertion result | |
351 | // 04: sub-LSB - sub-LSBs are basically noise, but when | |
352 | // 03: sub-LSB averaged properly, they can increase convertion | |
353 | // 02: sub-LSB precision up to 29 bits; they can be discarded | |
354 | // 01: sub-LSB without loss of resolution. | |
355 | // 00: sub-LSB | |
356 | ||
357 | if (data_in & MPC624_EOC_BIT) | |
358 | rt_printk("MPC624: EOC bit is set (data_in=%lu)!", | |
359 | data_in); | |
360 | if (data_in & MPC624_DMY_BIT) | |
361 | rt_printk("MPC624: DMY bit is set (data_in=%lu)!", | |
362 | data_in); | |
363 | if (data_in & MPC624_SGN_BIT) // check the sign bit | |
364 | { // The voltage is positive | |
365 | data_in &= 0x3FFFFFFF; // EOC and DMY should be 0, but we will mask them out just to be sure | |
366 | data[n] = data_in; // comedi operates on unsigned numbers, so we don't clear the SGN bit | |
367 | // SGN bit is still set! It's correct, since we're converting to unsigned. | |
368 | } else { // The voltage is negative | |
369 | // data_in contains a number in 30-bit two's complement code and we must deal with it | |
370 | data_in |= MPC624_SGN_BIT; | |
371 | data_in = ~data_in; | |
372 | data_in += 1; | |
373 | data_in &= ~(MPC624_EOC_BIT | MPC624_DMY_BIT); | |
374 | // clear EOC and DMY bits | |
375 | data_in = 0x20000000 - data_in; | |
376 | data[n] = data_in; | |
377 | } | |
378 | } | |
379 | ||
380 | // Return the number of samples read/written | |
381 | return n; | |
382 | } | |
383 | ||
384 | COMEDI_INITCLEANUP(driver_mpc624); |