mpc624.c revision 790c55415aa31f4c732729f94d2c3a54f7d3bfc2
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/* 24Driver: mpc624 25Description: Micro/sys MPC-624 PC/104 board 26Devices: [Micro/sys] MPC-624 (mpc624) 27Author: Stanislaw Raczynski <sraczynski@op.pl> 28Updated: Thu, 15 Sep 2005 12:01:18 +0200 29Status: 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 40Configuration 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//---------------------------------------------------------------------------- 123typedef 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//---------------------------------------------------------------------------- 129static 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}; 137static 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//---------------------------------------------------------------------------- 147static int mpc624_attach(comedi_device * dev, comedi_devconfig * it); 148static int mpc624_detach(comedi_device * dev); 149//---------------------------------------------------------------------------- 150static comedi_driver driver_mpc624 = { 151 driver_name:"mpc624", 152 module:THIS_MODULE, 153 attach:mpc624_attach, 154 detach:mpc624_detach 155}; 156 157//---------------------------------------------------------------------------- 158static int mpc624_ai_rinsn(comedi_device * dev, comedi_subdevice * s, 159 comedi_insn * insn, unsigned int * data); 160//---------------------------------------------------------------------------- 161static int mpc624_attach(comedi_device * dev, comedi_devconfig * it) 162{ 163 comedi_subdevice *s; 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 258static int mpc624_detach(comedi_device * dev) 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 271static int mpc624_ai_rinsn(comedi_device * dev, comedi_subdevice * s, 272 comedi_insn * insn, unsigned int * data) 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 384COMEDI_INITCLEANUP(driver_mpc624); 385