lm63.c (8149B)
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 Texas Instruments LM63 8 * The LM63 is a Remote Diode Digital Temperature Sensor with Integrated Fan 9 * Control temperature sensor with an I2C bus interface. The host can query 10 * the LM63 at any time to read the temperature. For more information, 11 * see the Texas Instruments LM63 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 19 20 #define LM63_REG_TEMP 0x00 21 22 /* 23 * Convert from hardware values to degree C. 24 * 25 * Note that you should be a bit careful with which arguments 26 * these macros are called: arguments may be evaluated more than once. 27 * 28 * TEMP_FROM_REG converts hardware register value to software temperature 29 * value. Bits[7:0]. This 8-bit value is in 2's complement format, with an 30 * LSB equal to 1 degree C, the range can be from -128 to +127 degree C. 31 * So, to ease the conversion we typecast the value to signed char. 32 * Software value is also in fixed value format, each unit represents 1 33 * C, the range of the converted value can be from -128 to +127. 34 */ 35 #define TEMP_FROM_REG(val) ((signed char)val) 36 37 /* Initial values */ 38 #define LM63_OP_READ 0x1 39 #define LM63_OP_WRITE 0x2 40 41 /* 42 * LM63 Hardware information : registers and their human-readable 43 * counterparts. 44 */ 45 typedef struct lm63_s { 46 uint8 temp; /* Hardware info */ 47 int temp_val; 48 } lm63_t; 49 50 /* 51 * Function: lm63_ioctl 52 * 53 * Purpose: read local temperature data from the LM63 temperature chip. 54 * 55 * Parameters: 56 * unit - StrataSwitch device number or I2C bus number 57 * devno - chip device id 58 * opcode - CPU request (LM63_OP_READ) 59 * data - address of lm63_t data 60 * len - size of test data (not used) 61 * 62 * Returns: 63 * SOC_E_NONE - no error 64 * SOC_E_TIMEOUT - chip temperature reading unavailable or IO error. 65 */ 66 STATIC int 67 lm63_ioctl(int unit, int devno, int opcode, void* data, int len) 68 { 69 int r = SOC_E_NONE; 70 lm63_t* l = (lm63_t*)data; 71 uint8 saddr = soc_i2c_addr(unit, devno); 72 73 if (opcode == LM63_OP_READ) { 74 75 r = soc_i2c_read_byte_data(unit, saddr, LM63_REG_TEMP, &l->temp); 76 if (r < 0) { 77 return r; 78 } 79 80 soc_i2c_device(unit, devno)->rbyte += 1; 81 82 /* Convert the register values to temperature values */ 83 l->temp_val = TEMP_FROM_REG(l->temp); 84 } 85 return r; 86 } 87 88 /* 89 * LM63 control structure. 90 */ 91 typedef struct { 92 int sleep; /* Thread sleep time (0 to exit) */ 93 int temp[MAX_I2C_DEVICES]; /* last temp values */ 94 } lm63_dev_info_t; 95 96 static lm63_dev_info_t *lm63_info[SOC_MAX_NUM_DEVICES]; 97 98 #define C2F(_d) ((9*(_d)/5) + 32) /* celsius to fahrenheit */ 99 #define MINDIFF 1 /* at least 1 degree change */ 100 101 /* 102 * Function: lm63_temp_show 103 * 104 * Purpose: Query LM63 chip for temperature and report status in 105 * degrees Celsius and Degrees Fahrenheit. 106 * 107 * Parameters: 108 * unit - switch unit 109 * dev - i2c device 110 * force - if true, show temp even if it hasn't changed 111 */ 112 STATIC void 113 lm63_temp_show(int unit, int dev, int force) 114 { 115 lm63_t lm63; 116 lm63_dev_info_t *lmi; 117 int ctemp, ftemp, lasttemp; 118 int difftemp; 119 120 lmi = lm63_info[unit]; 121 if (lmi == NULL) { 122 return; 123 } 124 125 /* Read back stats */ 126 if (lm63_ioctl(unit, dev, LM63_OP_READ, &lm63, sizeof(lm63_t)) < 0) { 127 LOG_CLI((BSL_META_U(unit, "%s: ERROR: device not responding\n"), 128 soc_i2c_devname(unit, dev))); 129 lmi->sleep = 0; /* exit thread */ 130 return; 131 } 132 133 ctemp = lm63.temp_val; 134 lasttemp = lmi->temp[dev]; 135 136 if (lasttemp == 0) { /* first time through */ 137 difftemp = MINDIFF; 138 } else { 139 difftemp = lasttemp - ctemp; 140 if (difftemp < 0) { 141 difftemp = -difftemp; 142 } 143 } 144 145 if (force || difftemp >= MINDIFF) { 146 lmi->temp[dev] = ctemp; 147 148 /* 20 degree C change: something is wrong */ 149 if (difftemp > 20) { 150 if (force) { 151 LOG_CLI((BSL_META_U(unit, "%s: NOTICE: Temperature Unavailable\n"), 152 soc_i2c_devname(unit, dev))); 153 if (lasttemp > 0) { 154 ftemp = C2F(lasttemp); 155 LOG_CLI((BSL_META_U(unit, "%s: Last Temperature %dC, %dF\n"), 156 soc_i2c_devname(unit, dev), 157 lasttemp, ftemp)); 158 } 159 } 160 return; 161 } 162 163 ftemp = C2F(ctemp); 164 165 LOG_CLI((BSL_META_U(unit, "%s: Temperature %dC, %dF\n"), 166 soc_i2c_devname(unit, dev), 167 ctemp, ftemp)); 168 } 169 } 170 171 /* 172 * Function: lm63_thread 173 * 174 * Purpose: Poll LM63 devices and report stats every poll_delay seconds 175 * 176 * Parameters: 177 * unitp - void * version of unit 178 */ 179 STATIC void 180 lm63_thread(void *unitp) 181 { 182 int unit, dev, ndev; 183 lm63_dev_info_t *lmi; 184 185 unit = PTR_TO_INT(unitp); 186 lmi = lm63_info[unit]; 187 188 while (lmi && lmi->sleep) { 189 ndev = soc_i2c_device_count(unit); 190 for (dev = 0; dev < ndev; dev++) { 191 if (soc_i2c_devtype(unit, dev) == LM63_DEVICE_TYPE) { 192 lm63_temp_show(unit, dev, FALSE); 193 } 194 } 195 sal_usleep(lmi->sleep * SECOND_USEC); 196 } 197 LOG_CLI((BSL_META_U(unit, "unit %d: Thermal monitoring completed\n"), unit)); 198 sal_thread_exit(0); 199 } 200 201 202 STATIC int 203 lm63_init(int unit, int devno, void* data, int len) 204 { 205 soc_i2c_devdesc_set(unit, devno, "LM63 Temperature Sensor"); 206 207 /* Init devinfo */ 208 if (lm63_info[unit] == NULL) { 209 lm63_info[unit] = sal_alloc(sizeof(lm63_dev_info_t), "lm63_info"); 210 if (lm63_info[unit] == NULL) { 211 return SOC_E_MEMORY; 212 } 213 sal_memset(lm63_info[unit], 0, sizeof(lm63_dev_info_t)); 214 } 215 return SOC_E_NONE; 216 } 217 218 /* 219 * Function: soc_i2c_lm63_monitor 220 * 221 * Purpose: 222 * Monitor configuration: start or stop a temperature probe 223 * task to monitor temperatures every nsecs. 224 * Parameters: 225 * unit - StrataSwitch device number or I2C bus number 226 * enable - start or stop a temperature probe task 227 * nsecs - number of seconds delay, between succesive queries. 228 * Returns: 229 * None 230 * Notes: 231 * None 232 */ 233 void 234 soc_i2c_lm63_monitor(int unit, int enable, int nsecs) 235 { 236 if (!soc_i2c_is_attached(unit)) { 237 soc_i2c_attach(unit, 0, 0); 238 } 239 if (lm63_info[unit] == NULL) { 240 return; 241 } 242 if (!enable) { 243 lm63_info[unit]->sleep = 0; 244 return; 245 } 246 if (lm63_info[unit]->sleep) { 247 return; 248 } 249 lm63_info[unit]->sleep = nsecs; 250 251 sal_thread_create("bcmTEMP", 252 SAL_THREAD_STKSZ, 253 50, 254 (void (*)(void*))lm63_thread, 255 INT_TO_PTR(unit)); 256 LOG_CLI((BSL_META_U(unit, "unit %d: Thermal monitoring enabled\n"), unit)); 257 } 258 259 /* 260 * Function: soc_i2c_lm63_temperature_show 261 * 262 * Purpose: Show all temperature stats on all chips. 263 * 264 * Parameters: 265 * unit - StrataSwitch device number or I2C bus number 266 * 267 * Returns: 268 * None 269 */ 270 void 271 soc_i2c_lm63_temperature_show(int unit) 272 { 273 int dev, ndev; 274 275 if (!soc_i2c_is_attached(unit)) { 276 soc_i2c_attach(unit, 0, 0); 277 } 278 if (lm63_info[unit] == NULL) { 279 return; 280 } 281 ndev = soc_i2c_device_count(unit); 282 for (dev = 0; dev < ndev; dev++) { 283 if (soc_i2c_devtype(unit, dev) == LM63_DEVICE_TYPE) { 284 lm63_temp_show(unit, dev, TRUE); 285 } 286 } 287 } 288 289 /* NOT USED */ 290 STATIC int 291 lm63_read(int unit, int devno, uint16 addr, uint8* data, uint32* len) 292 { 293 return SOC_E_NONE; 294 } 295 296 /* NOT USED */ 297 STATIC int 298 lm63_write(int unit, int devno, uint16 addr, uint8* data, uint32 len) 299 { 300 return SOC_E_NONE; 301 } 302 303 /* LM63 Temperature Sensor Driver callout */ 304 i2c_driver_t _soc_i2c_lm63_driver = { 305 0x0, 0x0, /* System assigned bytes */ 306 LM63_DEVICE_TYPE, 307 lm63_read, 308 lm63_write, 309 lm63_ioctl, 310 lm63_init, 311 NULL, 312 };