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