max664x.c (12114B)
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 * BCM I2C Device Driver for MAXIM 664x 8 * The MAX664x (max6646/7/9)is a temperature sensor with an I2C bus interface. The host can query 9 * the MAX664x at any time to read the temperature. For more information, 10 * see MAX664x data sheet. 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 /* The MAX664X registers */ 18 #define MAX664X_REG_LOCAL_TEMP_R 0x00 19 #define MAX664X_REG_REMOTE_TEMP_R 0x01 20 #define MAX664X_REG_STATUS_BYTE_R 0x02 21 #define MAX664X_REG_CONFIG_BYTE_R 0x03 22 #define MAX664X_REG_CONVERSION_RATE_BYTE_R 0x04 23 #define MAX664X_REG_LOCAL_ALERT_HI_LIMIT_R 0x05 24 #define MAX664X_REG_LOCAL_ALERT_LO_LIMIT_R 0x06 25 #define MAX664X_REG_REMOTE_ALERT_HI_LIMIT_R 0x07 26 #define MAX664X_REG_REMOTE_ALERT_LO_LIMIT_R 0x08 27 #define MAX664X_REG_CONFIG_BYTE_W 0x09 28 #define MAX664X_REG_CONVERSTION_RATE_BYTE_W 0x0a 29 #define MAX664X_REG_LOCAL_ALERT_HI_LIMIT_W 0x0b 30 #define MAX664X_REG_LOCAL_ALERT_LO_LIMIT_W 0x0c 31 #define MAX664X_REG_REMOTE_ALERT_HI_LIMIT_W 0x0d 32 #define MAX664X_REG_REMOTE_ALERT_LO_LIMIT_W 0x0e 33 #define MAX664X_REG_ONE_SHOT 0x0f 34 #define MAX664X_REG_REMOTE_EXTENDED_TEMP_R 0x10 35 #define MAX664X_REG_LOCAL_EXTENDED_TEMP_R 0x11 36 #define MAX664X_REG_REMOTE_OVERTEMP_LIMIT_RW 0x19 37 #define MAX664X_REG_LOCAL_OVERTEMP_LIMIT_RW 0x20 38 #define MAX664X_REG_OVERTEMP_HYSTERESIS 0x21 39 #define MAX664X_REG_FAULT_QUEUE 0x22 40 #define MAX664X_REG_MANUFACTURER_ID 0xFE 41 #define MAX664X_REG_REVISION 0xFF 42 43 /* Initial values */ 44 #define MAX664X_OP_READ 0x1 45 #define MAX664X_OP_WRITE 0x2 46 47 /* 48 * MAX664X Hardware information : registers and their human-readable 49 * counterparts. 50 */ 51 typedef struct max664x_reg_s { 52 uint8 local_temp; 53 uint8 remote_temp; 54 uint8 config; 55 uint8 status; 56 } max664x_reg_t; 57 58 /* 59 * Function: max664x_ioctl 60 * 61 * Purpose: read temperature data from the MAX664X temperature probe 62 * chip. 63 * 64 * Parameters: 65 * unit - StrataSwitch device number or I2C bus number 66 * devno - chip device id 67 * opcode - CPU request (MAX664X_OP_READ) 68 * data - address of max664x_reg_t data 69 * len - size of test data (not used) 70 * 71 * Returns: 72 * SOC_E_NONE - no error 73 * SOC_E_TIMEOUT - chip temperature reading unavailable or IO error. 74 */ 75 STATIC int 76 max664x_ioctl(int unit, int devno, int opcode, void* data, int len) 77 { 78 int rv = SOC_E_NONE; 79 max664x_reg_t* maxreg = (max664x_reg_t*)data; 80 uint8 saddr = soc_i2c_addr(unit, devno); 81 82 if (opcode == MAX664X_OP_READ) { 83 84 rv = soc_i2c_read_byte_data(unit, saddr, MAX664X_REG_LOCAL_TEMP_R, &maxreg->local_temp); 85 if (rv < 0) { 86 return rv; 87 } 88 89 soc_i2c_device(unit, devno)->rbyte += 1; 90 91 rv = soc_i2c_read_byte_data(unit, saddr, MAX664X_REG_REMOTE_TEMP_R, &maxreg->remote_temp); 92 if (rv < 0) { 93 return rv; 94 } 95 soc_i2c_device(unit, devno)->rbyte += 1; 96 97 rv = soc_i2c_read_byte_data(unit, saddr, MAX664X_REG_CONFIG_BYTE_R, &maxreg->config); 98 if (rv < 0) { 99 return rv; 100 } 101 soc_i2c_device(unit, devno)->rbyte += 1; 102 103 rv = soc_i2c_read_byte_data(unit, saddr, MAX664X_REG_STATUS_BYTE_R, &maxreg->status); 104 if (rv < 0) { 105 return rv; 106 } 107 soc_i2c_device(unit, devno)->rbyte += 1; 108 109 110 } else if (opcode == MAX664X_OP_WRITE) { 111 112 rv = soc_i2c_write_byte_data(unit, saddr, MAX664X_REG_CONFIG_BYTE_W, maxreg->config); 113 if (rv < 0) { 114 return rv; 115 } 116 117 soc_i2c_device(unit, devno)->tbyte += 1; 118 119 } else { 120 LOG_CLI((BSL_META_U(unit, 121 "invalid command for max6649 - must be read or write\n"))); 122 rv = SOC_E_UNAVAIL; 123 } 124 125 return rv; 126 } 127 128 /* 129 * MAX664X control structure. 130 */ 131 typedef struct { 132 int sleep; /* Thread sleep time (0 to exit) */ 133 int remote_temp[MAX_I2C_DEVICES]; /* last temp values */ 134 int local_temp[MAX_I2C_DEVICES]; /* last temp values */ 135 } max664x_dev_info_t; 136 137 static max664x_dev_info_t *max664x_info[SOC_MAX_NUM_DEVICES]; 138 139 #define C2F(_d) ((9*(_d)/5) + 32) /* celsius to farenheit */ 140 141 #define MINDIFF 1 /* at least 1 degree change */ 142 143 /* 144 * Function: max664x_temp_show 145 * 146 * Purpose: Query MAX664X chip for temperature and report status in 147 * degrees Celcius and Degrees Fahrenheit. 148 * 149 * Parameters: 150 * unit - switch unit 151 * dev - i2c device 152 * force - if true, show temp even if it hasn't changed 153 */ 154 STATIC void 155 max664x_temp_show(int unit, int dev, int force) 156 { 157 max664x_reg_t maxreg; 158 max664x_dev_info_t *max_info; 159 int ctemp, ftemp, lasttemp; 160 int difftemp; 161 int cnt; 162 163 max_info = max664x_info[unit]; 164 if (max_info == NULL) { 165 return; 166 } 167 168 /* Read back stats */ 169 if (max664x_ioctl(unit, dev, MAX664X_OP_READ, &maxreg, sizeof(max664x_reg_t)) < 0) { 170 LOG_CLI((BSL_META_U(unit, 171 "%s: ERROR: device not responding\n"), 172 soc_i2c_devname(unit, dev))); 173 max_info->sleep = 0; /* exit thread */ 174 return; 175 } 176 177 178 /* loop 2x for local and remote temp */ 179 for (cnt=0; cnt<2; cnt++) { 180 if (cnt==0) { 181 ctemp = maxreg.local_temp; 182 lasttemp = max_info->local_temp[dev]; 183 } else { 184 ctemp = maxreg.remote_temp; 185 lasttemp = max_info->remote_temp[dev]; 186 } 187 188 if (lasttemp == 0) { /* first time through */ 189 difftemp = MINDIFF; 190 } else { 191 difftemp = lasttemp - ctemp; 192 if (difftemp < 0) { 193 difftemp = -difftemp; 194 } 195 } 196 197 if (force || difftemp >= MINDIFF) { 198 199 if (cnt==0) { 200 max_info->local_temp[dev] = ctemp; 201 } else { 202 max_info->remote_temp[dev] = ctemp; 203 } 204 205 /* 20 degree C change: something is wrong */ 206 if (difftemp > 20) { 207 if (force) { 208 LOG_CLI((BSL_META_U(unit, 209 "%s: NOTICE: temperature unavailable\n"), 210 soc_i2c_devname(unit, dev))); 211 if (lasttemp > 0) { 212 ftemp = C2F(lasttemp); 213 LOG_CLI((BSL_META_U(unit, 214 "%s: last temperature %d.%dC, %d.%dF\n"), 215 soc_i2c_devname(unit, dev), 216 lasttemp/10, lasttemp%10, 217 ftemp/10, ftemp%10)); 218 } 219 } 220 return; 221 } 222 223 ftemp = C2F(ctemp); 224 225 if (cnt==0) { 226 LOG_CLI((BSL_META_U(unit, 227 "%s: local temperature %dC, %dF\n"), 228 soc_i2c_devname(unit, dev), 229 ctemp, 230 ftemp)); 231 } else { 232 233 LOG_CLI((BSL_META_U(unit, 234 "%s: sirius temperature %dC, %dF\n"), 235 soc_i2c_devname(unit, dev), 236 ctemp, 237 ftemp)); 238 } 239 240 } 241 } 242 } 243 244 245 /* 246 * Function: max664x_thread 247 * 248 * Purpose: Poll MAX664X devices and report stats every poll_delay seconds 249 * 250 * Parameters: 251 * unitp - void * version of unit 252 */ 253 STATIC void 254 max664x_thread(void *unitp) 255 { 256 int unit, dev, ndev; 257 max664x_dev_info_t *maxinfo; 258 259 unit = PTR_TO_INT(unitp); 260 maxinfo = max664x_info[unit]; 261 262 while (maxinfo && maxinfo->sleep) { 263 ndev = soc_i2c_device_count(unit); 264 for (dev = 0; dev < ndev; dev++) { 265 if (soc_i2c_devtype(unit, dev) == MAX664X_DEVICE_TYPE) { 266 max664x_temp_show(unit, dev, FALSE); 267 } 268 } 269 sal_usleep(maxinfo->sleep * SECOND_USEC); 270 } 271 LOG_CLI((BSL_META_U(unit, 272 "unit %d: thermal monitoring completed\n"), unit)); 273 sal_thread_exit(0); 274 } 275 276 /* 277 * Function: max664x_init 278 * 279 * Purpose: Initialize the MAX664X temperature sensor chip. 280 * 281 * Parameters: 282 * unit - StrataSwitch device number or I2C bus number 283 * devno - chip device id 284 * data - not used 285 * len - not used 286 * 287 * Returns: 288 * SOC_E_NONE - no failure 289 * SOC_E_TIMEOUT - chip or device access failure. 290 */ 291 STATIC int 292 max664x_init(int unit, int devno, 293 void* data, int len) 294 { 295 int rv; 296 max664x_reg_t maxreg; 297 298 soc_i2c_devdesc_set(unit, devno, "MAX664X Temperature Sensor"); 299 300 /* Init chip */ 301 maxreg.config = 0x80; /* bit 7 - mask out alert interrupts, bit 6 RUN=0 enables temp sensor */ 302 303 rv = max664x_ioctl(unit, devno, MAX664X_OP_WRITE, &maxreg, sizeof(max664x_reg_t)); 304 if (rv < 0) { 305 LOG_CLI((BSL_META_U(unit, 306 "%s: ERROR: device did not initialize: %s\n"), 307 soc_i2c_devname(unit, devno), 308 soc_errmsg(rv))); 309 } 310 LOG_VERBOSE(BSL_LS_SOC_I2C, (BSL_META_U(unit, 311 "MAX664X hardware status=0x%x\n"), maxreg.status)); 312 313 /* Init devinfo */ 314 if (max664x_info[unit] == NULL) { 315 max664x_info[unit] = sal_alloc(sizeof(max664x_dev_info_t), "max664x_info"); 316 if (max664x_info[unit] == NULL) { 317 return SOC_E_MEMORY; 318 } 319 sal_memset(max664x_info[unit], 0, sizeof(max664x_dev_info_t)); 320 } 321 322 return SOC_E_NONE; 323 } 324 325 /* 326 * Function: soc_i2c_max664x_monitor 327 * 328 * Purpose: 329 * Monitor configuration: start or stop a temperature probe 330 * task to monitor temperatures every nsecs. 331 * Parameters: 332 * unit - StrataSwitch device number or I2C bus number 333 * enable - start or stop a temperature probe task 334 * nsecs - number of seconds delay, between succesive queries. 335 * Returns: 336 * None 337 * Notes: 338 * None 339 */ 340 void 341 soc_i2c_max664x_monitor(int unit, int enable, int nsecs) 342 { 343 if (!soc_i2c_is_attached(unit)) { 344 soc_i2c_attach(unit, 0, 0); 345 } 346 if (max664x_info[unit] == NULL) { 347 return; 348 } 349 if (!enable) { 350 max664x_info[unit]->sleep = 0; 351 return; 352 } 353 if (max664x_info[unit]->sleep) { 354 return; 355 } 356 max664x_info[unit]->sleep = nsecs; 357 358 sal_thread_create("bcmTEMP", 359 SAL_THREAD_STKSZ, 360 50, 361 (void (*)(void*))max664x_thread, 362 INT_TO_PTR(unit)); 363 LOG_CLI((BSL_META_U(unit, 364 "unit %d: thermal monitoring enabled\n"), unit)); 365 } 366 367 368 /* 369 * Function: soc_i2c_max664x_temperature_show 370 * 371 * Purpose: Show all temperature stats on all chips. 372 * 373 * Parameters: 374 * unit - StrataSwitch device number or I2C bus number 375 * 376 * Returns: 377 * None 378 */ 379 void 380 soc_i2c_max664x_temperature_show(int unit) 381 { 382 int dev, ndev; 383 384 if (!soc_i2c_is_attached(unit)) { 385 soc_i2c_attach(unit, 0, 0); 386 } 387 if (max664x_info[unit] == NULL) { 388 return; 389 } 390 ndev = soc_i2c_device_count(unit); 391 for (dev = 0; dev < ndev; dev++) { 392 if (soc_i2c_devtype(unit, dev) == MAX664X_DEVICE_TYPE) { 393 max664x_temp_show(unit, dev, TRUE); 394 } 395 } 396 } 397 398 /* NOT USED */ 399 STATIC int 400 max664x_read(int unit, int devno, 401 uint16 addr, uint8* data, uint32* len) 402 { 403 return SOC_E_NONE; 404 } 405 406 /* NOT USED */ 407 STATIC int 408 max664x_write(int unit, int devno, 409 uint16 addr, uint8* data, uint32 len) 410 { 411 return SOC_E_NONE; 412 } 413 414 415 /* MAX664X Temperature Sensor Driver callout */ 416 i2c_driver_t _soc_i2c_max664x_driver = { 417 0x0, 0x0, /* System assigned bytes */ 418 MAX664X_DEVICE_TYPE, 419 max664x_read, 420 max664x_write, 421 max664x_ioctl, 422 max664x_init, 423 NULL, 424 }; 425