max127.c (12164B)
1 /* 2 * 3 * This license is set out in https://raw.githubusercontent.com/Broadcom-Network-Switching-Software/OpenBCM/master/Legal/LICENSE file. 4 * 5 * Copyright 2007-2019 Broadcom Inc. All rights reserved. 6 * 7 * MAX127 multi-range 12-bit D-A converter chip which provides for 8 * eight (8) analog input channels that are independently software 9 * programmable via I2C bus. 10 * See also: MAXIM MAX127 data sheet for more details. 11 */ 12 #include <sal/types.h> 13 #include <soc/drv.h> 14 #include <soc/error.h> 15 #include <soc/i2c.h> 16 #include <shared/bsl.h> 17 /* Table 3: Range and Polarity Selection */ 18 #define RPSEL_0_5 0x0 19 #define RPSEL_0_10 0x2 20 #define RPSEL_5_5 0x1 21 #define RPSEL_10_10 0x3 22 23 /* Table 4: Power Down and Clock */ 24 #define PD_NORMAL 0x0 25 #define PD_STANDBY 0x2 26 #define PD_FULL_PD 0x3 27 28 /* 29 * Make a MAX127 Control Byte 30 * See Table 1: Control Byte Format 31 */ 32 #define MAX127_CONTROL(ch, rps, pds)\ 33 (0x80 | (ch) << 4 | (rps) << 2 | (pds)) 34 35 /* Decimal point formatting */ 36 #define INT_FRAC_3PT_3(x) (x) / 1000000, ((x) / 1000) % 1000 37 38 /* Transform raw HW register value to binary value */ 39 #define MAX127_12BIT(x) ((((x) << 8) | ((x) >> 8)) >> 4); 40 41 /* Number of samples when reading A/D channel */ 42 int soc_i2c_max127_iterations = 100; 43 44 /* 45 * Function: max127_range_str 46 * 47 * Purpose: 48 * Given an RPSEL value, return string representation of range. 49 * Parameters: 50 * val - RPSEL HW value from A/D chip 51 * Returns: 52 * String representing voltage range (0-5, 0-10, +/-5, +/-10) 53 * Notes: 54 * None 55 */ 56 STATIC char * 57 max127_range_str(int rpsel) 58 { 59 if (rpsel == RPSEL_0_5) 60 return "(0-5)"; 61 if (rpsel == RPSEL_0_10) 62 return "(0-10)"; 63 if (rpsel == RPSEL_5_5) 64 return "(+/-5)"; 65 if (rpsel == RPSEL_10_10) 66 return "(+/-10)"; 67 return "(unknown)"; 68 } 69 70 /* 71 * Function: max127_range_from_value 72 * 73 * Purpose: 74 * Given a raw +/-10 AD output value, return appropriate range. 75 * Parameters: 76 * value - HW value from A/D chip 77 * Returns: 78 * Range to use for best precision (0-5, 0-10, +/-5, +/-10) 79 * Notes: 80 * Initially, we perform the conversion using +/-10 range selection. 81 * We then find out which range selection makes sense based on the 82 * output value. 83 */ 84 STATIC int 85 max127_range_from_value(uint16 value) 86 { 87 int slack = 2; 88 89 /* 2-complement to positive linear scale */ 90 value = (value + 2048) & 0xFFF; 91 92 if (value > (3072 - slack)) 93 return RPSEL_0_10; 94 if (value > (2048 + slack)) 95 return RPSEL_0_5; 96 if (value > (1024 + slack)) 97 return RPSEL_5_5; 98 return RPSEL_10_10; 99 } 100 101 /* 102 * Function: max127_value 103 * 104 * Purpose: 105 * Convert binary value from HW to actual voltage. 106 * Parameters: 107 * val - HW value from A/D chip 108 * range - current input range 109 * Returns: 110 * Actual voltage in V 111 * Notes: 112 * None 113 */ 114 #ifdef COMPILER_HAS_DOUBLE 115 116 #define RPSEL_FS0 (1.2207 * 4.096) 117 #define RPSEL_FS1 (2.4414 * 4.096) 118 #define RPSCALE 4096.0 119 120 STATIC double 121 max127_value(uint16 val, uint16 range) 122 { 123 double x = 0; 124 125 if(val == 0) 126 return 0.0; 127 if(range == RPSEL_0_5){ 128 /* x = val / RPSCALE * RPSEL_FS0; */ 129 x = val * 0.00122; 130 } 131 else if (range == RPSEL_0_10) 132 x = val / RPSCALE * RPSEL_FS1; 133 else if(range == RPSEL_5_5){ 134 x = - 10.0/4096.0 * (4096 - val); 135 } 136 else if (range == RPSEL_10_10){ 137 x = - 20.0/4096.0 * (4096 - val); 138 } 139 return x; 140 } 141 #else 142 STATIC int 143 max127_value(uint16 val, uint16 range) 144 { 145 int x; 146 147 if (val == 0) { 148 return 0; 149 } 150 151 switch (range) { 152 case RPSEL_0_5: 153 x = val * 5; 154 break; 155 case RPSEL_0_10: 156 x = val * 10; 157 break; 158 case RPSEL_5_5: 159 x = (((val + 2048) & 0xFFF) - 2048) * 10; 160 break; 161 case RPSEL_10_10: 162 x = (((val + 2048) & 0xFFF) - 2048) * 20; 163 break; 164 default: 165 x = 0; 166 break; 167 } 168 169 /* Convert to uV with rounding and no overflow */ 170 x *= ((100 * 1000000) / 4096); 171 x += 50; 172 x /= 100; 173 174 return x; 175 } 176 #endif 177 178 /* 179 * Data: max127_vals 180 * Purpose: Range and Polarity selection for each A/D channel. 181 */ 182 #ifdef COMPILER_HAS_DOUBLE 183 STATIC 184 max127_t max127_vals[] = { 185 /* Initial conversion values: +/-10 */ 186 {0,10,-10,0,0, 187 MAX127_CONTROL(I2C_ADC_CH0, RPSEL_10_10, PD_NORMAL), RPSEL_10_10 }, 188 {0,10,-10,0,0, 189 MAX127_CONTROL(I2C_ADC_CH1, RPSEL_10_10, PD_NORMAL), RPSEL_10_10 }, 190 {0,10,-10,0,0, 191 MAX127_CONTROL(I2C_ADC_CH2, RPSEL_10_10, PD_NORMAL), RPSEL_10_10 }, 192 {0,10,-10,0,0, 193 MAX127_CONTROL(I2C_ADC_CH3, RPSEL_10_10, PD_NORMAL), RPSEL_10_10 }, 194 {0,10,-10,0,0, 195 MAX127_CONTROL(I2C_ADC_CH4, RPSEL_10_10, PD_NORMAL), RPSEL_10_10 }, 196 {0,10,-10,0,0, 197 MAX127_CONTROL(I2C_ADC_CH5, RPSEL_10_10, PD_NORMAL), RPSEL_10_10 }, 198 {0,10,-10,0,0, 199 MAX127_CONTROL(I2C_ADC_CH6, RPSEL_10_10, PD_NORMAL), RPSEL_10_10 }, 200 {0,10,-10,0,0, 201 MAX127_CONTROL(I2C_ADC_CH7, RPSEL_10_10, PD_NORMAL), RPSEL_10_10 } 202 } ; 203 #define N_MAX127_CHAN COUNTOF(max127_vals) 204 #else 205 #define N_MAX127_CHAN 8 206 #endif 207 208 /* 209 * Function: max127_calibrate 210 * 211 * Purpose: Update MAX127 control word with specified RPSEL value. 212 * 213 * Parameters: 214 * ch - MAX127 A/D channel number 215 * rpsel - range and polarity selection for MAX127 A/D conversion. 216 * 217 * Returns: 218 * None 219 */ 220 STATIC void 221 max127_calibrate(int ch, max127_t *data, int rpsel) 222 { 223 data->cr = rpsel; 224 data->cv = MAX127_CONTROL(ch, rpsel, PD_NORMAL); 225 } 226 227 228 229 extern uint32 soc_i2c_pci_read(int unit, uint32 addr); 230 extern int soc_i2c_pci_write(int unit, uint32 addr, uint32 data); 231 232 233 /* 234 * Function: max127_conversion 235 * 236 * Purpose: Perform A/D conversions on a channel 237 * 238 * Parameters: 239 * unit - StrataSwitch device number or I2C bus number 240 * devno - chip device id 241 * ch - A/D channel number (0 <= channel <= 7) 242 * val - HW value from A/D chip for A/D conversion (raw HW value) 243 * 244 * Returns: 245 * None 246 */ 247 STATIC int 248 max127_conversion(int unit, int devno, int ch, max127_t *data, uint16 *val) 249 { 250 uint8 saddr; 251 252 saddr = soc_i2c_addr(unit, devno); 253 254 /* CHi start conversion, read back data */ 255 if (soc_i2c_write_byte(unit, saddr, (uint8)data->cv) < 0) { 256 LOG_CLI((BSL_META_U(unit, 257 "unit %d i2c %s: could not configure channel %d\n"), 258 unit, soc_i2c_devname(unit,devno), ch)); 259 *val = 0; 260 return -1; 261 } 262 263 soc_i2c_device(unit, devno)->tbyte++; 264 265 /* Read back CHi conversion value */ 266 if (soc_i2c_read_word(unit, saddr, val) < 0) { 267 LOG_CLI((BSL_META_U(unit, 268 "unit %d i2c %s: could not read channel %d\n"), 269 unit, soc_i2c_devname(unit,devno), ch)); 270 *val = 0; 271 return -1; 272 } 273 274 soc_i2c_device(unit, devno)->rbyte += 2; 275 return 0; 276 } 277 278 STATIC void 279 max127_read_channel(int unit, int devno, int ch, max127_t *data) 280 { 281 int i; 282 uint16 val; 283 #ifdef COMPILER_HAS_DOUBLE 284 double voltage; 285 #else 286 int voltage; 287 #endif 288 /* Reset sample data */ 289 data->val = 0; 290 #ifdef COMPILER_HAS_DOUBLE 291 data->min = 10; 292 data->max = -10; 293 #else 294 data->min = 10000000; /* uV */ 295 data->max = -10000000; /* uV */ 296 #endif 297 298 /* Reset/calibrate scale to +/-10 */ 299 max127_calibrate(ch, data, RPSEL_10_10); 300 301 /* Perform conversion on +/-10 scale */ 302 max127_conversion(unit, devno, ch, data, &val); 303 304 /* Convert to 12-bit unsigned int for conversion value */ 305 val = MAX127_12BIT(val); 306 307 /* Store new current range/scale */ 308 max127_calibrate(ch, data, max127_range_from_value(val)); 309 310 for (i = 0; i < soc_i2c_max127_iterations; val=0, i++) { 311 312 /* Perform conversion on new scale */ 313 if (max127_conversion(unit, devno, ch, data, &val) < 0) { 314 break; 315 } 316 317 /* Convert to 12-bit unsigned int for conversion value */ 318 val = MAX127_12BIT(val); 319 320 /* Compute voltage from binary value */ 321 voltage = max127_value(val, data->cr); 322 323 /* Store the running total */ 324 data->val += voltage; 325 326 /* Compute new min */ 327 if (voltage < data->min) { 328 data->min = voltage; 329 } 330 331 /* Compute new max */ 332 if (voltage > data->max) { 333 data->max = voltage; 334 } 335 } 336 337 /* Compute average and delta */ 338 if (i > 1) { 339 data->val /= i; 340 } 341 data->delta = data->max - data->min; 342 } 343 344 /* 345 * Function: max127_ioctl 346 * 347 * Purpose: 348 * Perform A/D conversion and report data. 349 * Parameters: 350 * unit - StrataSwitch device number or I2C bus number 351 * devno - chip device id 352 * ch - A/D channel 353 * data - buffer for storing A/D output data (max127_t) 354 * len - unused 355 * Returns: 356 * SOC_E_NONE - no error 357 * Notes: 358 * If a valid channel number is given, run conversions for 359 * that channel and return result in supplied buffer. 360 * If a negative channel number is given, simply run all 361 * conversions and display results on the console. 362 */ 363 STATIC int 364 max127_ioctl(int unit, int devno, int cmd, void *data, int len) 365 { 366 int i; 367 max127_t maxdata; 368 369 switch (cmd) { 370 case I2C_ADC_DUMP_ALL: 371 /* Just raw dump */ 372 for (i = 0; i < N_MAX127_CHAN; i++) { 373 #ifdef COMPILER_HAS_DOUBLE 374 max127_read_channel(unit, devno, i, &max127_vals[i]); 375 LOG_CLI((BSL_META_U(unit, 376 "%s ch%d: %.3fV\t%s\t" 377 "%d samples range %.3f..%.3f (delta %.3f)\n"), 378 soc_i2c_devname(unit, devno), i, 379 max127_vals[i].val, 380 max127_range_str(max127_vals[i].cr), 381 soc_i2c_max127_iterations, 382 max127_vals[i].min, 383 max127_vals[i].max, 384 max127_vals[i].delta)); 385 #else 386 max127_t _data; 387 max127_read_channel(unit, devno, i, &_data); 388 LOG_CLI((BSL_META_U(unit, 389 "%s ch%d: %d.%03dV\t%s\t" 390 "%d samples range %d.%03d..%d.%03d (delta %d.%03d)\n"), 391 soc_i2c_devname(unit, devno), i, 392 INT_FRAC_3PT_3(_data.val), 393 max127_range_str(_data.cr), 394 soc_i2c_max127_iterations, 395 INT_FRAC_3PT_3(_data.min), 396 INT_FRAC_3PT_3(_data.max), 397 INT_FRAC_3PT_3(_data.delta))); 398 #endif 399 } 400 break; 401 case I2C_ADC_QUERY_CHANNEL: 402 if (data) { 403 sal_memset(&maxdata, 0, sizeof(maxdata)); 404 /* len is channel */ 405 max127_read_channel(unit, devno, len, &maxdata); 406 ((i2c_adc_t*)data)->max = maxdata.max; 407 ((i2c_adc_t*)data)->min = maxdata.min; 408 ((i2c_adc_t*)data)->val = maxdata.val; 409 ((i2c_adc_t*)data)->delta = maxdata.delta; 410 ((i2c_adc_t*)data)->nsamples = soc_i2c_max127_iterations; 411 } else { 412 return SOC_E_PARAM; 413 } 414 break; 415 case I2C_ADC_SET_SAMPLES: 416 if (data) { 417 soc_i2c_max127_iterations = *(int*)data; 418 } else { 419 return SOC_E_PARAM; 420 } 421 break; 422 default: 423 LOG_CLI((BSL_META_U(unit, 424 "Unit:%d Dev:%d Unsupported IOCTL Cmd %x\n"), 425 unit, devno, cmd)); 426 return SOC_E_PARAM; 427 } 428 return SOC_E_NONE; 429 430 } 431 432 /* 433 * Function: max127_init 434 * 435 * Purpose: 436 * Initialize the MAX127 A/D Chip 437 * Parameters: 438 * unit - StrataSwitch device number or I2C bus number 439 * devno - chip device id 440 * data - not used 441 * len - not used 442 * Returns: 443 * SOC_E_NONE - no failure 444 */ 445 STATIC int 446 max127_init(int unit, int devno, void *data, int len) 447 { 448 soc_i2c_devdesc_set(unit, devno, "MAX127 A/D Converter"); 449 return SOC_E_NONE; 450 } 451 452 /* NOT USED */ 453 STATIC int 454 max127_read(int unit, int devno, 455 uint16 addr, uint8 *data, uint32* len) 456 { 457 return SOC_E_NONE; 458 } 459 460 /* NOT USED */ 461 STATIC int 462 max127_write(int unit, int devno, 463 uint16 addr, uint8 *data, uint32 len) 464 { 465 return SOC_E_NONE; 466 } 467 468 /* MAX127 A-D converter driver callout */ 469 i2c_driver_t _soc_i2c_max127_driver = { 470 0x0, 0x0, /* System assigned bytes */ 471 MAX127_DEVICE_TYPE, 472 max127_read, 473 max127_write, 474 max127_ioctl, 475 max127_init, 476 NULL, 477 };