lm75.c (12430B)
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 * BCM56xx I2C Device Driver for National Semiconductor LM75 8 * The LM75 is a temperature sensor, Delta-Sigma ADC, and digital 9 * over-temp detector with an I2C bus interface. The host can query 10 * the LM75 at any time to read the temperature. For more information, 11 * see the National Semiconductor LM75 Datasheet. 12 */ 13 #include <sal/types.h> 14 #include <soc/drv.h> 15 #include <soc/error.h> 16 #include <soc/i2c.h> 17 #include <shared/bsl.h> 18 /* The LM75 registers */ 19 #define LM75_REG_TEMP 0x00 20 #define LM75_REG_CONF 0x01 21 #define LM75_REG_THYST 0x02 22 #define LM75_REG_TOS 0x03 23 24 /* 25 * Function: lm75_limit 26 * 27 * Purpose: Range check the input value. 28 * 29 * Parameters: 30 * value - input comparator value 31 * low - low value 32 * high - high value 33 * 34 * Returns: 35 * value or high or low value if out of range. 36 */ 37 STATIC INLINE int 38 lm75_limit(long value, long low, long high) 39 { 40 if (value < low) 41 return low; 42 else if (value > high) 43 return high; 44 else 45 return value; 46 } 47 48 /* 49 * Function: lm75_swap 50 * 51 * Purpose: byte swap a 16 bit value returned from LM75 52 * 53 * Parameters: 54 * value - input 16 bit value. 55 * 56 * Returns: 57 * byte swapped value. 58 */ 59 STATIC uint16 60 lm75_swap(uint16 val) 61 { 62 return (val >> 8) | (val << 8); 63 } 64 65 /* 66 * Convert from hardware values to tenths of a degree C. 67 * 68 * Rounding and limit checking is only done on the TO_REG variants. 69 * Note that you should be a bit careful with which arguments 70 * these macros are called: arguments may be evaluated more than once. 71 * 72 * TEMP_FROM_REG converts hardware register value to software temperature 73 * value. Bit[15:7] (i.e. the most significant 9 bit) of hardware register is 74 * the temperature. These 9-bit value is in 2's complement format, each unit 75 * represents 0.5 degree C, the range can be from -128.0 to +127.5 degree C. 76 * Softwrae value is also in fixed value format, each unit represents 0.1 77 * C, the range of the converted value can be from -1280 to +1275. The 78 * conversion first signed extended the 16 bit signed value to 32 bit signed 79 * value to avoid overflow. Using arithmatic division by 128 instead of right 80 * shift 7 bit to avoid the complier implementation-dependent behavior on 81 * most significant filled bits on right shift operation. 82 */ 83 #define TEMP_FROM_REG(val) ((int)(int16)val * 5 / 128) 84 #define TEMP_TO_REG(val) (lm75_limit((val<0?(0x200+\ 85 ((val)/5))<<7:(((val) + 2) / 5) << 7),0,0xffff)) 86 87 /* Initial values */ 88 #define LM75_INIT_TEMP_OS 600 /* overtemp shutdown: 60.0 */ 89 #define LM75_INIT_TEMP_HYST 500 /* clear alarm temp: 50.0 */ 90 #define LM75_OP_READ 0x1 91 #define LM75_OP_WRITE 0x2 92 93 /* 94 * LM75 Hardware information : registers and their human-readable 95 * counterparts. 96 */ 97 typedef struct lm75_s { 98 uint16 temp; /* Hardware info */ 99 uint8 conf; 100 uint16 t_hist; 101 uint16 t_os; 102 int temp_val; /* Human readable values in fixed point */ 103 int hist_val; 104 int os_val; 105 } lm75_t; 106 107 /* 108 * Function: lm75_ioctl 109 * 110 * Purpose: read temperature data from the LM75 temperature probe 111 * chip. 112 * 113 * Parameters: 114 * unit - StrataSwitch device number or I2C bus number 115 * devno - chip device id 116 * opcode - CPU request (LM75_OP_READ) 117 * data - address of lm75_t data 118 * len - size of test data (not used) 119 * 120 * Returns: 121 * SOC_E_NONE - no error 122 * SOC_E_TIMEOUT - chip temperature reading unavailable or IO error. 123 */ 124 STATIC int 125 lm75_ioctl(int unit, int devno, 126 int opcode, void* data, int len) 127 { 128 int r = SOC_E_NONE; 129 lm75_t* l = (lm75_t*)data; 130 uint8 saddr = soc_i2c_addr(unit, devno); 131 int t_os, t_hist; 132 133 if (opcode == LM75_OP_READ) { 134 135 r = soc_i2c_read_word_data(unit, saddr, LM75_REG_TEMP, &l->temp); 136 if (r < 0) { 137 return r; 138 } 139 140 soc_i2c_device(unit, devno)->rbyte += 2; 141 142 r = soc_i2c_read_word_data(unit, saddr, LM75_REG_TOS, &l->t_os); 143 if (r < 0) { 144 return r; 145 } 146 147 soc_i2c_device(unit, devno)->rbyte += 2; 148 149 r = soc_i2c_read_word_data(unit, saddr, LM75_REG_THYST, &l->t_hist); 150 if (r < 0) { 151 return r; 152 } 153 154 soc_i2c_device(unit, devno)->rbyte += 2; 155 156 /* Byte swap the 16 bit registers */ 157 l->temp = lm75_swap(l->temp); 158 l->t_hist = lm75_swap(l->t_hist); 159 l->t_os = lm75_swap(l->t_os); 160 161 /* Convert the register values to temperature values */ 162 l->temp_val = TEMP_FROM_REG(l->temp); 163 l->hist_val = TEMP_FROM_REG(l->t_hist); 164 l->os_val = TEMP_FROM_REG(l->t_os); 165 } else { 166 /* Write regvalues to HW */ 167 t_os = l->t_os; 168 t_hist = l->t_hist; 169 /* Convert to register format */ 170 t_hist = TEMP_TO_REG(t_hist); 171 t_os = TEMP_TO_REG(t_os); 172 /* Byte swap */ 173 t_hist = lm75_swap(t_hist); 174 t_os = lm75_swap(t_os); 175 176 /* Feed back to chip */ 177 r = soc_i2c_write_word_data(unit, saddr, LM75_REG_TOS, t_os); 178 if (r < 0) { 179 return r; 180 } 181 182 soc_i2c_device(unit, devno)->tbyte += 2; 183 184 r = soc_i2c_write_word_data(unit, saddr, LM75_REG_THYST, t_hist); 185 if (r < 0) { 186 return r; 187 } 188 189 soc_i2c_device(unit, devno)->tbyte += 2; 190 191 r = soc_i2c_write_byte_data(unit, saddr, LM75_REG_TEMP, l->conf); 192 if (r < 0) { 193 return r; 194 } 195 196 soc_i2c_device(unit, devno)->tbyte++; 197 } 198 return r; 199 } 200 201 /* 202 * LM75 control structure. 203 */ 204 typedef struct { 205 int sleep; /* Thread sleep time (0 to exit) */ 206 int temp[MAX_I2C_DEVICES]; /* last temp values */ 207 } lm75_dev_info_t; 208 209 static lm75_dev_info_t *lm75_info[SOC_MAX_NUM_DEVICES]; 210 211 #define C2F(_d) ((9*(_d)/5) + 320) /* celsius*10 to farenheit*10 */ 212 213 #define MINDIFF 10 /* at least 1 degree change */ 214 215 /* 216 * Function: lm75_temp_show 217 * 218 * Purpose: Query LM75 chip for temperature and report status in 219 * degrees Celcius and Degrees Fahrenheit. 220 * 221 * Parameters: 222 * unit - switch unit 223 * dev - i2c device 224 * force - if true, show temp even if it hasn't changed 225 */ 226 STATIC void 227 lm75_temp_show(int unit, int dev, int force) 228 { 229 lm75_t lm75; 230 lm75_dev_info_t *lmi; 231 int ctemp, ftemp, lasttemp; 232 int difftemp, alarm_temp, shut_temp; 233 234 lmi = lm75_info[unit]; 235 if (lmi == NULL) { 236 return; 237 } 238 239 /* Read back stats */ 240 if (lm75_ioctl(unit, dev, LM75_OP_READ, &lm75, sizeof(lm75_t)) < 0) { 241 LOG_CLI((BSL_META_U(unit, 242 "%s: ERROR: device not responding\n"), 243 soc_i2c_devname(unit, dev))); 244 lmi->sleep = 0; /* exit thread */ 245 return; 246 } 247 248 ctemp = lm75.temp_val; 249 lasttemp = lmi->temp[dev]; 250 251 if (lasttemp == 0) { /* first time through */ 252 difftemp = MINDIFF; 253 } else { 254 difftemp = lasttemp - ctemp; 255 if (difftemp < 0) { 256 difftemp = -difftemp; 257 } 258 } 259 260 if (force || difftemp >= MINDIFF) { 261 lmi->temp[dev] = ctemp; 262 alarm_temp = lm75.hist_val; 263 shut_temp = lm75.os_val; 264 265 /* 20 degree C change: something is wrong */ 266 if (difftemp > 200) { 267 if (force) { 268 LOG_CLI((BSL_META_U(unit, 269 "%s: NOTICE: temperature unavailable\n"), 270 soc_i2c_devname(unit, dev))); 271 if (lasttemp > 0) { 272 ftemp = C2F(lasttemp); 273 LOG_CLI((BSL_META_U(unit, 274 "%s: last temperature %d.%dC, %d.%dF\n"), 275 soc_i2c_devname(unit, dev), 276 lasttemp/10, lasttemp%10, 277 ftemp/10, ftemp%10)); 278 } 279 } 280 return; 281 } 282 283 ftemp = C2F(ctemp); 284 285 if (ctemp > shut_temp) { 286 LOG_CLI((BSL_META_U(unit, 287 "%s: temperature %d.%dC, %d.%dF " 288 "WARNING: over shutdown %d.%dC\n"), 289 soc_i2c_devname(unit, dev), 290 ctemp/10, ctemp%10, 291 ftemp/10, ftemp%10, 292 shut_temp/10, shut_temp%10)); 293 } else if (ctemp > alarm_temp) { 294 LOG_CLI((BSL_META_U(unit, 295 "%s: temperature %d.%dC, %d.%dF " 296 "WARNING: over alarm %d.%dC\n"), 297 soc_i2c_devname(unit, dev), 298 ctemp/10, ctemp%10, 299 ftemp/10, ftemp%10, 300 alarm_temp/10, alarm_temp%10)); 301 } else { 302 LOG_CLI((BSL_META_U(unit, 303 "%s: temperature %d.%dC, %d.%dF\n"), 304 soc_i2c_devname(unit, dev), 305 ctemp/10, ctemp%10, 306 ftemp/10, ftemp%10)); 307 } 308 309 } 310 } 311 312 /* 313 * Function: lm75_thread 314 * 315 * Purpose: Poll LM75 devices and report stats every poll_delay seconds 316 * 317 * Parameters: 318 * unitp - void * version of unit 319 */ 320 STATIC void 321 lm75_thread(void *unitp) 322 { 323 int unit, dev, ndev; 324 lm75_dev_info_t *lmi; 325 326 unit = PTR_TO_INT(unitp); 327 lmi = lm75_info[unit]; 328 329 while (lmi && lmi->sleep) { 330 ndev = soc_i2c_device_count(unit); 331 for (dev = 0; dev < ndev; dev++) { 332 if (soc_i2c_devtype(unit, dev) == LM75_DEVICE_TYPE) { 333 lm75_temp_show(unit, dev, FALSE); 334 } 335 } 336 sal_usleep(lmi->sleep * SECOND_USEC); 337 } 338 LOG_CLI((BSL_META_U(unit, 339 "unit %d: thermal monitoring completed\n"), unit)); 340 sal_thread_exit(0); 341 } 342 343 /* 344 * Function: lm75_init 345 * 346 * Purpose: Initialize the LM75 temperature sensor chip. 347 * 348 * Parameters: 349 * unit - StrataSwitch device number or I2C bus number 350 * devno - chip device id 351 * data - not used 352 * len - not used 353 * 354 * Returns: 355 * SOC_E_NONE - no failure 356 * SOC_E_TIMEOUT - chip or device access failure. 357 */ 358 STATIC int 359 lm75_init(int unit, int devno, 360 void* data, int len) 361 { 362 int rv; 363 lm75_t lm75; 364 365 soc_i2c_devdesc_set(unit, devno, "LM75 Temperature Sensor"); 366 367 /* Init chip */ 368 lm75.t_hist = LM75_INIT_TEMP_HYST; 369 lm75.t_os = LM75_INIT_TEMP_OS; 370 lm75.conf = 0; 371 rv = lm75_ioctl(unit, devno, LM75_OP_WRITE, &lm75, sizeof(lm75_t)); 372 if (rv < 0) { 373 LOG_CLI((BSL_META_U(unit, 374 "%s: ERROR: device did not initialize: %s\n"), 375 soc_i2c_devname(unit, devno), 376 soc_errmsg(rv))); 377 } 378 379 /* Init devinfo */ 380 if (lm75_info[unit] == NULL) { 381 lm75_info[unit] = sal_alloc(sizeof(lm75_dev_info_t), "lm75_info"); 382 if (lm75_info[unit] == NULL) { 383 return SOC_E_MEMORY; 384 } 385 sal_memset(lm75_info[unit], 0, sizeof(lm75_dev_info_t)); 386 } 387 388 return SOC_E_NONE; 389 } 390 391 /* 392 * Function: soc_i2c_lm75_monitor 393 * 394 * Purpose: 395 * Monitor configuration: start or stop a temperature probe 396 * task to monitor temperatures every nsecs. 397 * Parameters: 398 * unit - StrataSwitch device number or I2C bus number 399 * enable - start or stop a temperature probe task 400 * nsecs - number of seconds delay, between succesive queries. 401 * Returns: 402 * None 403 * Notes: 404 * None 405 */ 406 void 407 soc_i2c_lm75_monitor(int unit, int enable, int nsecs) 408 { 409 if (!soc_i2c_is_attached(unit)) { 410 soc_i2c_attach(unit, 0, 0); 411 } 412 if (lm75_info[unit] == NULL) { 413 return; 414 } 415 if (!enable) { 416 lm75_info[unit]->sleep = 0; 417 return; 418 } 419 if (lm75_info[unit]->sleep) { 420 return; 421 } 422 lm75_info[unit]->sleep = nsecs; 423 424 sal_thread_create("bcmTEMP", 425 SAL_THREAD_STKSZ, 426 50, 427 (void (*)(void*))lm75_thread, 428 INT_TO_PTR(unit)); 429 LOG_CLI((BSL_META_U(unit, 430 "unit %d: thermal monitoring enabled\n"), unit)); 431 } 432 433 434 /* 435 * Function: soc_i2c_lm75_temperature_show 436 * 437 * Purpose: Show all temperature stats on all chips. 438 * 439 * Parameters: 440 * unit - StrataSwitch device number or I2C bus number 441 * 442 * Returns: 443 * None 444 */ 445 void 446 soc_i2c_lm75_temperature_show(int unit) 447 { 448 int dev, ndev; 449 450 if (!soc_i2c_is_attached(unit)) { 451 soc_i2c_attach(unit, 0, 0); 452 } 453 if (lm75_info[unit] == NULL) { 454 return; 455 } 456 ndev = soc_i2c_device_count(unit); 457 for (dev = 0; dev < ndev; dev++) { 458 if (soc_i2c_devtype(unit, dev) == LM75_DEVICE_TYPE) { 459 lm75_temp_show(unit, dev, TRUE); 460 } 461 } 462 } 463 464 /* NOT USED */ 465 STATIC int 466 lm75_read(int unit, int devno, 467 uint16 addr, uint8* data, uint32* len) 468 { 469 return SOC_E_NONE; 470 } 471 472 /* NOT USED */ 473 STATIC int 474 lm75_write(int unit, int devno, 475 uint16 addr, uint8* data, uint32 len) 476 { 477 return SOC_E_NONE; 478 } 479 480 481 /* LM75 Temperature Sensor Driver callout */ 482 i2c_driver_t _soc_i2c_lm75_driver = { 483 0x0, 0x0, /* System assigned bytes */ 484 LM75_DEVICE_TYPE, 485 lm75_read, 486 lm75_write, 487 lm75_ioctl, 488 lm75_init, 489 NULL, 490 };