1 /* $NetBSD: lm87.c,v 1.11 2019/12/23 14:43:03 thorpej Exp $ */ 2 /* $OpenBSD: lm87.c,v 1.20 2008/11/10 05:19:48 cnst Exp $ */ 3 4 /* 5 * Copyright (c) 2005 Mark Kettenis 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <sys/cdefs.h> 21 __KERNEL_RCSID(0, "$NetBSD: lm87.c,v 1.11 2019/12/23 14:43:03 thorpej Exp $"); 22 23 #include <sys/param.h> 24 #include <sys/systm.h> 25 #include <sys/device.h> 26 #include <dev/sysmon/sysmonvar.h> 27 28 #include <dev/i2c/i2cvar.h> 29 30 /* LM87 registers */ 31 #define LM87_INT_HHIGH_L 0x13 /* Hardware int high limit (lockable) */ 32 #define LM87_EXT_HHIGH_L 0x14 /* Hardware ext high limit (lockable) */ 33 #define LM87_TEST 0x15 34 #define LM87_CHANNEL 0x16 /* Dual purpose pin and scaling */ 35 #define LM87_INT_HHIGH 0x17 /* Hardware int temp high limit */ 36 #define LM87_EXT_HHIGH 0x18 /* Hardware ext temp high limit */ 37 #define LM87_DAC_DATA 0x19 /* DAC output scaling */ 38 #define LM87_AIN1_LOW 0x1a /* Analog in 1 low limit */ 39 #define LM87_AIN2_LOW 0x1b /* Analog in 2 low limit */ 40 #define LM87_2_5V 0x20 /* +2.5V or ext temp 2 reading */ 41 #define LM87_VCCP1 0x21 /* Vccp1 reading */ 42 #define LM87_VCC 0x22 /* +Vcc reading */ 43 #define LM87_5V 0x23 /* +5V reading */ 44 #define LM87_12V 0x24 /* +12V reading */ 45 #define LM87_VCCP2 0x25 /* Vccp2 reading */ 46 #define LM87_EXT_TEMP 0x26 /* External tempurature 1 reading */ 47 #define LM87_INT_TEMP 0x27 /* Internal temperature reading */ 48 #define LM87_FAN1 0x28 /* Fan1 or AIN1 reading */ 49 #define LM87_FAN2 0x29 /* Fan2 or AIN2 reading */ 50 #define LM87_2_5V_HIGH 0x2b /* +2.5V or ext temp 2 high limit */ 51 #define LM87_2_5V_LOW 0x2c /* +2.5V or ext temp 2 low limit */ 52 #define LM87_VCCP1_HIGH 0x2d /* Vccp1 high limit */ 53 #define LM87_VCCP1_LOW 0x2e /* Vccp1 low limit */ 54 #define LM87_VCC_HIGH 0x2f /* +3.3V (Vcc) high limit */ 55 #define LM87_VCC_LOW 0x30 /* +3.3V (Vcc) low limit */ 56 #define LM87_5V_HIGH 0x31 /* +5V high limit */ 57 #define LM87_5V_LOW 0x32 /* +5V low limit */ 58 #define LM87_12V_HIGH 0x33 /* +12V high limit */ 59 #define LM87_12V_LOW 0x34 /* +12V low limit */ 60 #define LM87_VCCP2_HIGH 0x35 /* Vccp2 high limit */ 61 #define LM87_VCCP2_LOW 0x36 /* Vccp2 low limit */ 62 #define LM87_EXT_HIGH 0x37 /* External tempurature 1 high limit */ 63 #define LM87_EXT_LOW 0x38 /* External tempurature low limit */ 64 #define LM87_INT_HIGH 0x39 /* Internal tempurature 1 high limit */ 65 #define LM87_INT_LOW 0x3a /* Internal tempurature low limit */ 66 #define LM87_FAN1_HIGH 0x3b /* Fan 1 count or AIN1 high limit */ 67 #define LM87_FAN2_HIGH 0x3c /* Fan 2 count or AIN2 high limit */ 68 #define LM87_COMPANY_ID 0x3e /* Company ID */ 69 #define LM87_REVISION 0x3f /* Revision */ 70 #define LM87_CONFIG1 0x40 /* Configuration 1 */ 71 #define LM87_INT_STAT1 0x41 /* Interrupt status 1 */ 72 #define LM87_INT_STAT2 0x42 /* Interrupt status 2 */ 73 #define LM87_INT_MASK1 0x43 /* Interrupt mask 1 */ 74 #define LM87_INT_MASK2 0x44 /* Interrupt mask 2 */ 75 #define LM87_CI_CLEAR 0x46 /* Chassis intrusion */ 76 #define LM87_FANDIV 0x47 /* Fan divisor + VID 0-3 */ 77 #define LM87_VID4 0x48 /* VID4 */ 78 #define LM87_CONFIG2 0x4a /* Configuration 2 */ 79 #define LM87_INT_MIRR1 0x4c /* Interrupt status 1 mirror */ 80 #define LM87_INT_MIRR2 0x4d /* Interrupt status 2 mirror */ 81 #define LM87_ALERT 0x80 /* SMB Alert enable */ 82 83 /* Register contents */ 84 #define LM87_CONFIG1_START 0x01 85 #define LM87_CONFIG1_INTCLR 0x08 86 87 #define LM87_CHANNEL_AIN1 0x01 88 #define LM87_CHANNEL_AIN2 0x02 89 #define LM87_CHANNEL_TEMP2 0x04 90 #define LM87_CHANNEL_VCC5 0x08 91 92 struct lmenv_id { 93 u_int8_t id, family; 94 const char *name; 95 }; 96 97 static const struct lmenv_id lmenv_ids[] = { 98 { 0x01, 81, "LM81" }, 99 { 0x02, 87, "LM87" }, /* LM87 or LM87CIMT */ 100 { 0x23, 81, "ADM9240" }, 101 { 0xda, 81, "DSL780" }, 102 { 0x00, 0, NULL } 103 }; 104 105 /* Sensors */ 106 #define LMENV_2_5V 0 107 #define LMENV_VCCP1 1 108 #define LMENV_VCC 2 109 #define LMENV_5V 3 110 #define LMENV_12V 4 111 #define LMENV_VCCP2 5 112 #define LMENV_EXT_TEMP 6 113 #define LMENV_INT_TEMP 7 114 #define LMENV_FAN1 8 115 #define LMENV_FAN2 9 116 #define LMENV_NUM_SENSORS 10 117 118 struct lmenv_softc { 119 i2c_tag_t sc_tag; 120 i2c_addr_t sc_addr; 121 122 int sc_fan1_div, sc_fan2_div; 123 int sc_family; 124 uint8_t sc_channel; 125 126 struct sysmon_envsys *sc_sme; 127 envsys_data_t sc_sensor[LMENV_NUM_SENSORS]; 128 }; 129 130 int lmenv_match(device_t, cfdata_t, void *); 131 void lmenv_attach(device_t, device_t, void *); 132 133 void lmenv_refresh(struct sysmon_envsys *, envsys_data_t *); 134 135 CFATTACH_DECL_NEW(lmenv, sizeof(struct lmenv_softc), 136 lmenv_match, lmenv_attach, NULL, NULL); 137 138 static const struct device_compatible_entry compat_data[] = { 139 { "lm87", 0 }, 140 { "lm87cimt", 0 }, 141 { "adm9240", 0 }, 142 { "lm81", 0 }, 143 { "ds1780", 0 }, 144 { NULL, 0 } 145 }; 146 147 int 148 lmenv_match(device_t parent, cfdata_t match, void *aux) 149 { 150 struct i2c_attach_args *ia = aux; 151 u_int8_t cmd, val; 152 int error, i, match_result; 153 154 if (iic_use_direct_match(ia, match, compat_data, &match_result)) 155 return match_result; 156 157 /* 158 * Indirect config - not much we can do! 159 * Check typical addresses and read the Company ID register 160 */ 161 if ((ia->ia_addr < 0x2c) || (ia->ia_addr > 0x2f)) 162 return 0; 163 164 cmd = LM87_COMPANY_ID; 165 iic_acquire_bus(ia->ia_tag, 0); 166 error = iic_exec(ia->ia_tag, I2C_OP_READ_WITH_STOP, ia->ia_addr, 167 &cmd, 1, &val, 1, 0); 168 iic_release_bus(ia->ia_tag, 0); 169 170 if (error) 171 return 0; 172 173 for (i = 0; lmenv_ids[i].id != 0; i++) 174 if (lmenv_ids[i].id == val) 175 return I2C_MATCH_ADDRESS_AND_PROBE; 176 177 return 0; 178 } 179 180 void 181 lmenv_attach(device_t parent, device_t self, void *aux) 182 { 183 struct lmenv_softc *sc = device_private(self); 184 struct i2c_attach_args *ia = aux; 185 u_int8_t cmd, data, data2; 186 int i; 187 188 sc->sc_tag = ia->ia_tag; 189 sc->sc_addr = ia->ia_addr; 190 191 iic_acquire_bus(sc->sc_tag, 0); 192 193 cmd = LM87_COMPANY_ID; 194 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 195 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) { 196 iic_release_bus(sc->sc_tag, 0); 197 printf(": cannot read ID register\n"); 198 return; 199 } 200 for (i = 0; lmenv_ids[i].id != 0; i++) 201 if (lmenv_ids[i].id == data) 202 break; 203 204 cmd = LM87_REVISION; 205 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 206 sc->sc_addr, &cmd, sizeof cmd, &data2, sizeof data, 0)) { 207 iic_release_bus(sc->sc_tag, 0); 208 printf(": cannot read revision register\n"); 209 return; 210 } 211 printf(": %s rev %x\n", lmenv_ids[i].name, data2); 212 sc->sc_family = lmenv_ids[i].family; 213 214 cmd = LM87_FANDIV; 215 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 216 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) { 217 iic_release_bus(sc->sc_tag, 0); 218 printf(", cannot read Fan Divisor register\n"); 219 return; 220 } 221 sc->sc_fan1_div = 1 << ((data >> 4) & 0x03); 222 sc->sc_fan2_div = 1 << ((data >> 6) & 0x03); 223 224 if (sc->sc_family == 87) { 225 cmd = LM87_CHANNEL; 226 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 227 sc->sc_addr, &cmd, sizeof cmd, &sc->sc_channel, 228 sizeof sc->sc_channel, 0)) { 229 iic_release_bus(sc->sc_tag, 0); 230 printf(", cannot read Channel register\n"); 231 return; 232 } 233 } else 234 sc->sc_channel = 0; 235 236 cmd = LM87_CONFIG1; 237 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 238 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) { 239 iic_release_bus(sc->sc_tag, 0); 240 printf(", cannot read Configuration Register 1\n"); 241 return; 242 } 243 244 /* 245 * if chip is not running, try to start it. 246 * if it is stalled doing an interrupt, unstall it 247 */ 248 data2 = (data | LM87_CONFIG1_START); 249 data2 = data2 & ~LM87_CONFIG1_INTCLR; 250 251 if (data != data2) { 252 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 253 sc->sc_addr, &cmd, sizeof cmd, &data2, sizeof data2, 0)) { 254 iic_release_bus(sc->sc_tag, 0); 255 printf(", cannot write Configuration Register 1\n"); 256 return; 257 } 258 } 259 iic_release_bus(sc->sc_tag, 0); 260 261 /* Initialize sensor data. */ 262 sc->sc_sensor[LMENV_2_5V].state = ENVSYS_SINVALID; 263 if (sc->sc_channel & LM87_CHANNEL_TEMP2) { 264 sc->sc_sensor[LMENV_INT_TEMP].units = ENVSYS_STEMP; 265 strlcpy(sc->sc_sensor[LMENV_2_5V].desc, "External 2", 266 sizeof(sc->sc_sensor[LMENV_2_5V].desc)); 267 } else { 268 sc->sc_sensor[LMENV_2_5V].units = ENVSYS_SVOLTS_DC; 269 strlcpy(sc->sc_sensor[LMENV_2_5V].desc, "+2.5Vin", 270 sizeof(sc->sc_sensor[LMENV_2_5V].desc)); 271 } 272 273 sc->sc_sensor[LMENV_VCCP1].state = ENVSYS_SINVALID; 274 sc->sc_sensor[LMENV_VCCP1].units = ENVSYS_SVOLTS_DC; 275 strlcpy(sc->sc_sensor[LMENV_VCCP1].desc, "Vccp1", 276 sizeof(sc->sc_sensor[LMENV_VCCP1].desc)); 277 278 sc->sc_sensor[LMENV_VCC].state = ENVSYS_SINVALID; 279 sc->sc_sensor[LMENV_VCC].units = ENVSYS_SVOLTS_DC; 280 strlcpy(sc->sc_sensor[LMENV_VCC].desc, "+Vcc", 281 sizeof(sc->sc_sensor[LMENV_VCC].desc)); 282 283 sc->sc_sensor[LMENV_5V].state = ENVSYS_SINVALID; 284 sc->sc_sensor[LMENV_5V].units = ENVSYS_SVOLTS_DC; 285 strlcpy(sc->sc_sensor[LMENV_5V].desc, "+5Vin/Vcc", 286 sizeof(sc->sc_sensor[LMENV_5V].desc)); 287 288 sc->sc_sensor[LMENV_12V].state = ENVSYS_SINVALID; 289 sc->sc_sensor[LMENV_12V].units = ENVSYS_SVOLTS_DC; 290 strlcpy(sc->sc_sensor[LMENV_12V].desc, "+12Vin", 291 sizeof(sc->sc_sensor[LMENV_12V].desc)); 292 293 sc->sc_sensor[LMENV_VCCP2].state = ENVSYS_SINVALID; 294 if (!(sc->sc_channel & LM87_CHANNEL_TEMP2)) { 295 sc->sc_sensor[LMENV_VCCP2].units = ENVSYS_SVOLTS_DC; 296 strlcpy(sc->sc_sensor[LMENV_VCCP2].desc, "Vccp2", 297 sizeof(sc->sc_sensor[LMENV_VCCP2].desc)); 298 } 299 300 sc->sc_sensor[LMENV_EXT_TEMP].state = ENVSYS_SINVALID; 301 sc->sc_sensor[LMENV_EXT_TEMP].units = ENVSYS_STEMP; 302 if (sc->sc_channel & LM87_CHANNEL_TEMP2) 303 strlcpy(sc->sc_sensor[LMENV_EXT_TEMP].desc, "External 1", 304 sizeof(sc->sc_sensor[LMENV_EXT_TEMP].desc)); 305 else 306 strlcpy(sc->sc_sensor[LMENV_EXT_TEMP].desc, "External", 307 sizeof(sc->sc_sensor[LMENV_EXT_TEMP].desc)); 308 309 sc->sc_sensor[LMENV_INT_TEMP].state = ENVSYS_SINVALID; 310 sc->sc_sensor[LMENV_INT_TEMP].units = ENVSYS_STEMP; 311 strlcpy(sc->sc_sensor[LMENV_INT_TEMP].desc, "Internal", 312 sizeof(sc->sc_sensor[LMENV_INT_TEMP].desc)); 313 314 sc->sc_sensor[LMENV_FAN1].state = ENVSYS_SINVALID; 315 if (sc->sc_channel & LM87_CHANNEL_AIN1) { 316 sc->sc_sensor[LMENV_FAN1].units = ENVSYS_SVOLTS_DC; 317 strlcpy(sc->sc_sensor[LMENV_FAN1].desc, "AIN1", 318 sizeof(sc->sc_sensor[LMENV_FAN1].desc)); 319 } else { 320 sc->sc_sensor[LMENV_FAN1].units = ENVSYS_SFANRPM; 321 strlcpy(sc->sc_sensor[LMENV_FAN1].desc, "FAN1", 322 sizeof(sc->sc_sensor[LMENV_FAN1].desc)); 323 } 324 325 sc->sc_sensor[LMENV_FAN2].state = ENVSYS_SINVALID; 326 if (sc->sc_channel & LM87_CHANNEL_AIN2) { 327 sc->sc_sensor[LMENV_FAN2].units = ENVSYS_SVOLTS_DC; 328 strlcpy(sc->sc_sensor[LMENV_FAN2].desc, "AIN2", 329 sizeof(sc->sc_sensor[LMENV_FAN2].desc)); 330 } else { 331 sc->sc_sensor[LMENV_FAN2].units = ENVSYS_SFANRPM; 332 strlcpy(sc->sc_sensor[LMENV_FAN2].desc, "FAN2", 333 sizeof(sc->sc_sensor[LMENV_FAN2].desc)); 334 } 335 336 sc->sc_sme = sysmon_envsys_create(); 337 for (i = 0; i < LMENV_NUM_SENSORS; i++) 338 if (sysmon_envsys_sensor_attach(sc->sc_sme, 339 &sc->sc_sensor[i])) { 340 sysmon_envsys_destroy(sc->sc_sme); 341 aprint_error_dev(self, 342 "unable to attach sensor %d at sysmon\n", i); 343 return; 344 } 345 sc->sc_sme->sme_name = device_xname(self); 346 sc->sc_sme->sme_cookie = sc; 347 sc->sc_sme->sme_refresh = lmenv_refresh; 348 if (sysmon_envsys_register(sc->sc_sme)) { 349 aprint_error_dev(self, 350 "unable to register with sysmon\n"); 351 sysmon_envsys_destroy(sc->sc_sme); 352 return; 353 } 354 } 355 356 void 357 lmenv_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 358 { 359 struct lmenv_softc *sc = sme->sme_cookie; 360 u_int8_t cmd, data; 361 u_int tmp; 362 363 iic_acquire_bus(sc->sc_tag, 0); 364 365 cmd = LM87_2_5V + edata->sensor; 366 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 367 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) { 368 edata->state = ENVSYS_SINVALID; 369 return; 370 } 371 372 switch (edata->sensor) { 373 case LMENV_2_5V: 374 /* Might be external temperature 2 */ 375 if (sc->sc_channel & LM87_CHANNEL_TEMP2) { 376 if (data == 0x80) 377 edata->state = ENVSYS_SINVALID; 378 else { 379 edata->value_cur = 380 (int8_t)data * 1000000 + 273150000; 381 edata->state = ENVSYS_SVALID; 382 } 383 break; 384 } 385 edata->value_cur = 2500000 * data / 192; 386 edata->state = ENVSYS_SVALID; 387 break; 388 case LMENV_5V: 389 edata->value_cur = 5000000 * data / 192; 390 edata->state = ENVSYS_SVALID; 391 break; 392 case LMENV_12V: 393 edata->value_cur = 12000000 * data / 192; 394 edata->state = ENVSYS_SVALID; 395 break; 396 case LMENV_VCCP1: 397 edata->value_cur = 2700000 * data / 192; 398 edata->state = ENVSYS_SVALID; 399 break; 400 case LMENV_VCCP2: 401 /* If monitoring external temperature 2, this isn't monitored */ 402 if (sc->sc_channel & LM87_CHANNEL_TEMP2) { 403 edata->state = ENVSYS_SINVALID; 404 break; 405 } 406 edata->value_cur = 2700000 * data / 192; 407 edata->state = ENVSYS_SVALID; 408 break; 409 case LMENV_VCC: 410 /* Voltage scale selectable (5V or 3.3V) */ 411 edata->value_cur = 412 (LM87_CHANNEL_VCC5 ? 5000000 : 3300000) * data / 192; 413 edata->state = ENVSYS_SVALID; 414 break; 415 case LMENV_EXT_TEMP: 416 if (sc->sc_family == 81) { 417 edata->state = ENVSYS_SINVALID; 418 break; /* missing on LM81 */ 419 } 420 /* FALLTHROUGH */ 421 case LMENV_INT_TEMP: 422 if (data == 0x80) 423 edata->state = ENVSYS_SINVALID; 424 else { 425 edata->value_cur = (int8_t)data * 1000000 + 273150000; 426 edata->state = ENVSYS_SVALID; 427 } 428 break; 429 case LMENV_FAN1: 430 /* Might be analogue input 1 */ 431 if (sc->sc_channel & LM87_CHANNEL_AIN1) { 432 edata->value_cur = 1870000 * data / 192; 433 edata->state = ENVSYS_SVALID; 434 break; 435 } 436 if (data == 0xff) { 437 edata->state = ENVSYS_SINVALID; 438 break; 439 } 440 tmp = data * sc->sc_fan1_div; 441 if (tmp == 0) 442 edata->state = ENVSYS_SINVALID; 443 else { 444 edata->value_cur = 1350000 / tmp; 445 edata->state = ENVSYS_SVALID; 446 } 447 break; 448 case LMENV_FAN2: 449 /* Might be analogue input 2 */ 450 if (sc->sc_channel & LM87_CHANNEL_AIN2) { 451 edata->value_cur = 1870000 * data / 192; 452 edata->state = ENVSYS_SVALID; 453 break; 454 } 455 if (data == 0xff) { 456 edata->state = ENVSYS_SINVALID; 457 break; 458 } 459 tmp = data * sc->sc_fan2_div; 460 if (tmp == 0) 461 edata->state = ENVSYS_SINVALID; 462 else { 463 edata->value_cur = 1350000 / tmp; 464 edata->state = ENVSYS_SVALID; 465 } 466 break; 467 default: 468 edata->state = ENVSYS_SINVALID; 469 break; 470 } 471 472 iic_release_bus(sc->sc_tag, 0); 473 } 474