mpc624.c revision 5f74ea14c07fee91d3bdbaad88bff6264c6200e6
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 printk("comedi%d: mpc624 [0x%04lx, ", dev->minor, iobase); 170 if (request_region(iobase, MPC624_SIZE, "mpc624") == NULL) { 171 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 printk("3.52 kHz, "); 186 break; 187 case 1: 188 devpriv->ulConvertionRate = MPC624_SPEED_1_76_kHz; 189 printk("1.76 kHz, "); 190 break; 191 case 2: 192 devpriv->ulConvertionRate = MPC624_SPEED_880_Hz; 193 printk("880 Hz, "); 194 break; 195 case 3: 196 devpriv->ulConvertionRate = MPC624_SPEED_440_Hz; 197 printk("440 Hz, "); 198 break; 199 case 4: 200 devpriv->ulConvertionRate = MPC624_SPEED_220_Hz; 201 printk("220 Hz, "); 202 break; 203 case 5: 204 devpriv->ulConvertionRate = MPC624_SPEED_110_Hz; 205 printk("110 Hz, "); 206 break; 207 case 6: 208 devpriv->ulConvertionRate = MPC624_SPEED_55_Hz; 209 printk("55 Hz, "); 210 break; 211 case 7: 212 devpriv->ulConvertionRate = MPC624_SPEED_27_5_Hz; 213 printk("27.5 Hz, "); 214 break; 215 case 8: 216 devpriv->ulConvertionRate = MPC624_SPEED_13_75_Hz; 217 printk("13.75 Hz, "); 218 break; 219 case 9: 220 devpriv->ulConvertionRate = MPC624_SPEED_6_875_Hz; 221 printk("6.875 Hz, "); 222 break; 223 default: 224 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 printk("30 bit, "); 241 } 242 243 switch (it->options[1]) { 244 case 0: 245 s->range_table = &range_mpc624_bipolar1; 246 printk("1.01V]: "); 247 break; 248 default: 249 s->range_table = &range_mpc624_bipolar10; 250 printk("10.1V]: "); 251 } 252 s->len_chanlist = 1; 253 s->insn_read = mpc624_ai_rinsn; 254 255 printk("attached\n"); 256 257 return 1; 258} 259 260static int mpc624_detach(struct comedi_device *dev) 261{ 262 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/* printk("Channel %d: \n", insn->chanspec); */ 283 if (!insn->n) { 284 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 udelay(1); 292 outb(MPC624_ADCS | MPC624_ADSCK, dev->iobase + MPC624_ADC); 293 udelay(1); 294 outb(0, dev->iobase + MPC624_ADC); 295 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 udelay(1000); 302 else 303 break; 304 } 305 if (i == TIMEOUT) { 306 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 udelay(1); 314 for (i = 0; i < 32; i++) { 315 /* Set the clock low */ 316 outb(0, dev->iobase + MPC624_ADC); 317 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 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 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 udelay(1); 337 data_in <<= 1; 338 data_in |= 339 (inb(dev->iobase + 340 MPC624_ADC) & MPC624_ADSDO) >> 4; 341 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 printk("MPC624: EOC bit is set (data_in=%lu)!", 361 data_in); 362 if (data_in & MPC624_DMY_BIT) 363 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