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