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