1/* $NetBSD: lm87.c,v 1.16 2022/07/02 16:28:39 andvar 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.16 2022/07/02 16:28:39 andvar 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 temperature 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 temperature 1 high limit */ 63#define LM87_EXT_LOW 0x38 /* External temperature low limit */ 64#define LM87_INT_HIGH 0x39 /* Internal temperature 1 high limit */ 65#define LM87_INT_LOW 0x3a /* Internal temperature 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 92struct lmenv_id { 93 u_int8_t id, family; 94 const char *name; 95}; 96 97static 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 118struct 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 130int lmenv_match(device_t, cfdata_t, void *); 131void lmenv_attach(device_t, device_t, void *); 132 133void lmenv_refresh(struct sysmon_envsys *, envsys_data_t *); 134 135CFATTACH_DECL_NEW(lmenv, sizeof(struct lmenv_softc), 136 lmenv_match, lmenv_attach, NULL, NULL); 137 138static const struct device_compatible_entry compat_data[] = { 139 { .compat = "lm87" }, 140 { .compat = "lm87cimt" }, 141 { .compat = "adm9240" }, 142 { .compat = "lm81" }, 143 { .compat = "ds1780" }, 144 DEVICE_COMPAT_EOL 145}; 146 147int 148lmenv_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 if (iic_acquire_bus(ia->ia_tag, 0)) 166 return 0; 167 error = iic_exec(ia->ia_tag, I2C_OP_READ_WITH_STOP, ia->ia_addr, 168 &cmd, 1, &val, 1, 0); 169 iic_release_bus(ia->ia_tag, 0); 170 171 if (error) 172 return 0; 173 174 for (i = 0; lmenv_ids[i].id != 0; i++) 175 if (lmenv_ids[i].id == val) 176 return I2C_MATCH_ADDRESS_AND_PROBE; 177 178 return 0; 179} 180 181void 182lmenv_attach(device_t parent, device_t self, void *aux) 183{ 184 struct lmenv_softc *sc = device_private(self); 185 struct i2c_attach_args *ia = aux; 186 u_int8_t cmd, data, data2; 187 int i; 188 189 sc->sc_tag = ia->ia_tag; 190 sc->sc_addr = ia->ia_addr; 191 192 iic_acquire_bus(sc->sc_tag, 0); 193 194 cmd = LM87_COMPANY_ID; 195 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 196 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) { 197 iic_release_bus(sc->sc_tag, 0); 198 printf(": cannot read ID register\n"); 199 return; 200 } 201 for (i = 0; lmenv_ids[i].id != 0; i++) 202 if (lmenv_ids[i].id == data) 203 break; 204 205 cmd = LM87_REVISION; 206 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 207 sc->sc_addr, &cmd, sizeof cmd, &data2, sizeof data, 0)) { 208 iic_release_bus(sc->sc_tag, 0); 209 printf(": cannot read revision register\n"); 210 return; 211 } 212 printf(": %s rev %x\n", lmenv_ids[i].name, data2); 213 sc->sc_family = lmenv_ids[i].family; 214 215 cmd = LM87_FANDIV; 216 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 217 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) { 218 iic_release_bus(sc->sc_tag, 0); 219 printf(", cannot read Fan Divisor register\n"); 220 return; 221 } 222 sc->sc_fan1_div = 1 << ((data >> 4) & 0x03); 223 sc->sc_fan2_div = 1 << ((data >> 6) & 0x03); 224 225 if (sc->sc_family == 87) { 226 cmd = LM87_CHANNEL; 227 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 228 sc->sc_addr, &cmd, sizeof cmd, &sc->sc_channel, 229 sizeof sc->sc_channel, 0)) { 230 iic_release_bus(sc->sc_tag, 0); 231 printf(", cannot read Channel register\n"); 232 return; 233 } 234 } else 235 sc->sc_channel = 0; 236 237 cmd = LM87_CONFIG1; 238 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 239 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) { 240 iic_release_bus(sc->sc_tag, 0); 241 printf(", cannot read Configuration Register 1\n"); 242 return; 243 } 244 245 /* 246 * if chip is not running, try to start it. 247 * if it is stalled doing an interrupt, unstall it 248 */ 249 data2 = (data | LM87_CONFIG1_START); 250 data2 = data2 & ~LM87_CONFIG1_INTCLR; 251 252 if (data != data2) { 253 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 254 sc->sc_addr, &cmd, sizeof cmd, &data2, sizeof data2, 0)) { 255 iic_release_bus(sc->sc_tag, 0); 256 printf(", cannot write Configuration Register 1\n"); 257 return; 258 } 259 } 260 iic_release_bus(sc->sc_tag, 0); 261 262 /* Initialize sensor data. */ 263 sc->sc_sensor[LMENV_2_5V].state = ENVSYS_SINVALID; 264 if (sc->sc_channel & LM87_CHANNEL_TEMP2) { 265 sc->sc_sensor[LMENV_INT_TEMP].units = ENVSYS_STEMP; 266 strlcpy(sc->sc_sensor[LMENV_2_5V].desc, "External 2", 267 sizeof(sc->sc_sensor[LMENV_2_5V].desc)); 268 } else { 269 sc->sc_sensor[LMENV_2_5V].units = ENVSYS_SVOLTS_DC; 270 strlcpy(sc->sc_sensor[LMENV_2_5V].desc, "+2.5Vin", 271 sizeof(sc->sc_sensor[LMENV_2_5V].desc)); 272 } 273 274 sc->sc_sensor[LMENV_VCCP1].state = ENVSYS_SINVALID; 275 sc->sc_sensor[LMENV_VCCP1].units = ENVSYS_SVOLTS_DC; 276 strlcpy(sc->sc_sensor[LMENV_VCCP1].desc, "Vccp1", 277 sizeof(sc->sc_sensor[LMENV_VCCP1].desc)); 278 279 sc->sc_sensor[LMENV_VCC].state = ENVSYS_SINVALID; 280 sc->sc_sensor[LMENV_VCC].units = ENVSYS_SVOLTS_DC; 281 strlcpy(sc->sc_sensor[LMENV_VCC].desc, "+Vcc", 282 sizeof(sc->sc_sensor[LMENV_VCC].desc)); 283 284 sc->sc_sensor[LMENV_5V].state = ENVSYS_SINVALID; 285 sc->sc_sensor[LMENV_5V].units = ENVSYS_SVOLTS_DC; 286 strlcpy(sc->sc_sensor[LMENV_5V].desc, "+5Vin/Vcc", 287 sizeof(sc->sc_sensor[LMENV_5V].desc)); 288 289 sc->sc_sensor[LMENV_12V].state = ENVSYS_SINVALID; 290 sc->sc_sensor[LMENV_12V].units = ENVSYS_SVOLTS_DC; 291 strlcpy(sc->sc_sensor[LMENV_12V].desc, "+12Vin", 292 sizeof(sc->sc_sensor[LMENV_12V].desc)); 293 294 sc->sc_sensor[LMENV_VCCP2].state = ENVSYS_SINVALID; 295 if (!(sc->sc_channel & LM87_CHANNEL_TEMP2)) { 296 sc->sc_sensor[LMENV_VCCP2].units = ENVSYS_SVOLTS_DC; 297 strlcpy(sc->sc_sensor[LMENV_VCCP2].desc, "Vccp2", 298 sizeof(sc->sc_sensor[LMENV_VCCP2].desc)); 299 } 300 301 sc->sc_sensor[LMENV_EXT_TEMP].state = ENVSYS_SINVALID; 302 sc->sc_sensor[LMENV_EXT_TEMP].units = ENVSYS_STEMP; 303 if (sc->sc_channel & LM87_CHANNEL_TEMP2) 304 strlcpy(sc->sc_sensor[LMENV_EXT_TEMP].desc, "External 1", 305 sizeof(sc->sc_sensor[LMENV_EXT_TEMP].desc)); 306 else 307 strlcpy(sc->sc_sensor[LMENV_EXT_TEMP].desc, "External", 308 sizeof(sc->sc_sensor[LMENV_EXT_TEMP].desc)); 309 310 sc->sc_sensor[LMENV_INT_TEMP].state = ENVSYS_SINVALID; 311 sc->sc_sensor[LMENV_INT_TEMP].units = ENVSYS_STEMP; 312 strlcpy(sc->sc_sensor[LMENV_INT_TEMP].desc, "Internal", 313 sizeof(sc->sc_sensor[LMENV_INT_TEMP].desc)); 314 315 sc->sc_sensor[LMENV_FAN1].state = ENVSYS_SINVALID; 316 if (sc->sc_channel & LM87_CHANNEL_AIN1) { 317 sc->sc_sensor[LMENV_FAN1].units = ENVSYS_SVOLTS_DC; 318 strlcpy(sc->sc_sensor[LMENV_FAN1].desc, "AIN1", 319 sizeof(sc->sc_sensor[LMENV_FAN1].desc)); 320 } else { 321 sc->sc_sensor[LMENV_FAN1].units = ENVSYS_SFANRPM; 322 strlcpy(sc->sc_sensor[LMENV_FAN1].desc, "FAN1", 323 sizeof(sc->sc_sensor[LMENV_FAN1].desc)); 324 } 325 326 sc->sc_sensor[LMENV_FAN2].state = ENVSYS_SINVALID; 327 if (sc->sc_channel & LM87_CHANNEL_AIN2) { 328 sc->sc_sensor[LMENV_FAN2].units = ENVSYS_SVOLTS_DC; 329 strlcpy(sc->sc_sensor[LMENV_FAN2].desc, "AIN2", 330 sizeof(sc->sc_sensor[LMENV_FAN2].desc)); 331 } else { 332 sc->sc_sensor[LMENV_FAN2].units = ENVSYS_SFANRPM; 333 strlcpy(sc->sc_sensor[LMENV_FAN2].desc, "FAN2", 334 sizeof(sc->sc_sensor[LMENV_FAN2].desc)); 335 } 336 337 sc->sc_sme = sysmon_envsys_create(); 338 for (i = 0; i < LMENV_NUM_SENSORS; i++) 339 if (sysmon_envsys_sensor_attach(sc->sc_sme, 340 &sc->sc_sensor[i])) { 341 sysmon_envsys_destroy(sc->sc_sme); 342 aprint_error_dev(self, 343 "unable to attach sensor %d at sysmon\n", i); 344 return; 345 } 346 sc->sc_sme->sme_name = device_xname(self); 347 sc->sc_sme->sme_cookie = sc; 348 sc->sc_sme->sme_refresh = lmenv_refresh; 349 if (sysmon_envsys_register(sc->sc_sme)) { 350 aprint_error_dev(self, 351 "unable to register with sysmon\n"); 352 sysmon_envsys_destroy(sc->sc_sme); 353 return; 354 } 355} 356 357void 358lmenv_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 359{ 360 struct lmenv_softc *sc = sme->sme_cookie; 361 u_int8_t cmd, data; 362 u_int tmp; 363 364 iic_acquire_bus(sc->sc_tag, 0); 365 366 cmd = LM87_2_5V + edata->sensor; 367 if (iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 368 sc->sc_addr, &cmd, sizeof cmd, &data, sizeof data, 0)) { 369 edata->state = ENVSYS_SINVALID; 370 return; 371 } 372 373 switch (edata->sensor) { 374 case LMENV_2_5V: 375 /* Might be external temperature 2 */ 376 if (sc->sc_channel & LM87_CHANNEL_TEMP2) { 377 if (data == 0x80) 378 edata->state = ENVSYS_SINVALID; 379 else { 380 edata->value_cur = 381 (int8_t)data * 1000000 + 273150000; 382 edata->state = ENVSYS_SVALID; 383 } 384 break; 385 } 386 edata->value_cur = 2500000 * data / 192; 387 edata->state = ENVSYS_SVALID; 388 break; 389 case LMENV_5V: 390 edata->value_cur = 5000000 * data / 192; 391 edata->state = ENVSYS_SVALID; 392 break; 393 case LMENV_12V: 394 edata->value_cur = 12000000 * data / 192; 395 edata->state = ENVSYS_SVALID; 396 break; 397 case LMENV_VCCP1: 398 edata->value_cur = 2700000 * data / 192; 399 edata->state = ENVSYS_SVALID; 400 break; 401 case LMENV_VCCP2: 402 /* If monitoring external temperature 2, this isn't monitored */ 403 if (sc->sc_channel & LM87_CHANNEL_TEMP2) { 404 edata->state = ENVSYS_SINVALID; 405 break; 406 } 407 edata->value_cur = 2700000 * data / 192; 408 edata->state = ENVSYS_SVALID; 409 break; 410 case LMENV_VCC: 411 /* Voltage scale selectable (5V or 3.3V) */ 412 edata->value_cur = 413 (LM87_CHANNEL_VCC5 ? 5000000 : 3300000) * data / 192; 414 edata->state = ENVSYS_SVALID; 415 break; 416 case LMENV_EXT_TEMP: 417 if (sc->sc_family == 81) { 418 edata->state = ENVSYS_SINVALID; 419 break; /* missing on LM81 */ 420 } 421 /* FALLTHROUGH */ 422 case LMENV_INT_TEMP: 423 if (data == 0x80) 424 edata->state = ENVSYS_SINVALID; 425 else { 426 edata->value_cur = (int8_t)data * 1000000 + 273150000; 427 edata->state = ENVSYS_SVALID; 428 } 429 break; 430 case LMENV_FAN1: 431 /* Might be analogue input 1 */ 432 if (sc->sc_channel & LM87_CHANNEL_AIN1) { 433 edata->value_cur = 1870000 * data / 192; 434 edata->state = ENVSYS_SVALID; 435 break; 436 } 437 if (data == 0xff) { 438 edata->state = ENVSYS_SINVALID; 439 break; 440 } 441 tmp = data * sc->sc_fan1_div; 442 if (tmp == 0) 443 edata->state = ENVSYS_SINVALID; 444 else { 445 edata->value_cur = 1350000 / tmp; 446 edata->state = ENVSYS_SVALID; 447 } 448 break; 449 case LMENV_FAN2: 450 /* Might be analogue input 2 */ 451 if (sc->sc_channel & LM87_CHANNEL_AIN2) { 452 edata->value_cur = 1870000 * data / 192; 453 edata->state = ENVSYS_SVALID; 454 break; 455 } 456 if (data == 0xff) { 457 edata->state = ENVSYS_SINVALID; 458 break; 459 } 460 tmp = data * sc->sc_fan2_div; 461 if (tmp == 0) 462 edata->state = ENVSYS_SINVALID; 463 else { 464 edata->value_cur = 1350000 / tmp; 465 edata->state = ENVSYS_SVALID; 466 } 467 break; 468 default: 469 edata->state = ENVSYS_SINVALID; 470 break; 471 } 472 473 iic_release_bus(sc->sc_tag, 0); 474} 475