mpc624.c revision b8c623e5dde98a00ada4af47bec92a708794c573
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 \ 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/* -------------------------------------------------------------------------- */ 125struct 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/* -------------------------------------------------------------------------- */ 133static 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 142static 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/* -------------------------------------------------------------------------- */ 152static int mpc624_attach(struct comedi_device *dev, 153 struct comedi_devconfig *it); 154static int mpc624_detach(struct comedi_device *dev); 155/* -------------------------------------------------------------------------- */ 156static struct comedi_driver driver_mpc624 = { 157 .driver_name = "mpc624", 158 .module = THIS_MODULE, 159 .attach = mpc624_attach, 160 .detach = mpc624_detach 161}; 162 163/* -------------------------------------------------------------------------- */ 164static int mpc624_ai_rinsn(struct comedi_device *dev, 165 struct comedi_subdevice *s, struct comedi_insn *insn, 166 unsigned int *data); 167/* -------------------------------------------------------------------------- */ 168static 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 266static 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 279static 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 409COMEDI_INITCLEANUP(driver_mpc624); 410