1 /* $NetBSD: lm75.c,v 1.47 2025/01/02 18:40:54 skrll Exp $ */ 2 3 /* 4 * Copyright (c) 2003 Wasabi Systems, Inc. 5 * All rights reserved. 6 * 7 * Written by Jason R. Thorpe for Wasabi Systems, Inc. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed for the NetBSD Project by 20 * Wasabi Systems, Inc. 21 * 4. The name of Wasabi Systems, Inc. may not be used to endorse 22 * or promote products derived from this software without specific prior 23 * written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 * POSSIBILITY OF SUCH DAMAGE. 36 */ 37 38 #include <sys/cdefs.h> 39 __KERNEL_RCSID(0, "$NetBSD: lm75.c,v 1.47 2025/01/02 18:40:54 skrll Exp $"); 40 41 #include <sys/param.h> 42 #include <sys/systm.h> 43 #include <sys/device.h> 44 #include <sys/kernel.h> 45 #include <sys/sysctl.h> 46 47 #include <dev/sysmon/sysmonvar.h> 48 49 #include <dev/i2c/i2cvar.h> 50 #include <dev/i2c/lm75reg.h> 51 52 struct lmtemp_softc { 53 device_t sc_dev; 54 i2c_tag_t sc_tag; 55 int sc_address; 56 prop_dictionary_t sc_prop; 57 58 struct sysmon_envsys *sc_sme; 59 envsys_data_t sc_sensor; 60 int sc_tmax; 61 uint32_t sc_smax, sc_smin, sc_scrit; 62 63 uint32_t (*sc_lmtemp_decode)(const uint8_t *, int); 64 void (*sc_lmtemp_encode)(const uint32_t, uint8_t *, int); 65 }; 66 67 static int lmtemp_match(device_t, cfdata_t, void *); 68 static void lmtemp_attach(device_t, device_t, void *); 69 70 CFATTACH_DECL_NEW(lmtemp, sizeof(struct lmtemp_softc), 71 lmtemp_match, lmtemp_attach, NULL, NULL); 72 73 static void lmtemp_refresh(struct sysmon_envsys *, envsys_data_t *); 74 static int lmtemp_config_write(struct lmtemp_softc *, uint8_t); 75 static int lmtemp_temp_write(struct lmtemp_softc *, uint8_t, uint32_t, 76 int); 77 static int lmtemp_temp_read(struct lmtemp_softc *, uint8_t, uint32_t *, 78 int); 79 static uint32_t lmtemp_decode_lm75(const uint8_t *, int); 80 static uint32_t lmtemp_decode_ds75(const uint8_t *, int); 81 static uint32_t lmtemp_decode_lm77(const uint8_t *, int); 82 static void lmtemp_encode_lm75(const uint32_t, uint8_t *, int); 83 static void lmtemp_encode_ds75(const uint32_t, uint8_t *, int); 84 static void lmtemp_encode_lm77(const uint32_t, uint8_t *, int); 85 static void lmtemp_getlim_lm75(struct sysmon_envsys *, envsys_data_t *, 86 sysmon_envsys_lim_t *, uint32_t *); 87 static void lmtemp_getlim_lm77(struct sysmon_envsys *, envsys_data_t *, 88 sysmon_envsys_lim_t *, uint32_t *); 89 static void lmtemp_setlim_lm75(struct sysmon_envsys *, envsys_data_t *, 90 sysmon_envsys_lim_t *, uint32_t *); 91 static void lmtemp_setlim_lm77(struct sysmon_envsys *, envsys_data_t *, 92 sysmon_envsys_lim_t *, uint32_t *); 93 94 static void lmtemp_setup_sysctl(struct lmtemp_softc *); 95 static int sysctl_lm75_temp(SYSCTLFN_ARGS); 96 97 enum { 98 lmtemp_lm75 = 0, 99 lmtemp_ds75 = 1, 100 lmtemp_lm77 = 2, 101 }; 102 103 static const struct device_compatible_entry compat_data[] = { 104 { .compat = "national,lm75", .value = lmtemp_lm75 }, 105 { .compat = "i2c-lm75", .value = lmtemp_lm75 }, 106 { .compat = "lm75", .value = lmtemp_lm75 }, 107 108 /* XXX Linux treats ds1775 and ds75 differently. */ 109 { .compat = "dallas,ds1775", .value = lmtemp_ds75 }, 110 { .compat = "ds1775", .value = lmtemp_ds75 }, 111 112 { .compat = "national,lm77", .value = lmtemp_lm77 }, 113 114 /* 115 * see XXX in _attach() below: add code once non-lm75 matches are 116 * added here! 117 */ 118 DEVICE_COMPAT_EOL 119 }; 120 121 static const struct { 122 const char *lmtemp_name; 123 int lmtemp_addrmask; 124 int lmtemp_addr; 125 uint32_t (*lmtemp_decode)(const uint8_t *, int); 126 void (*lmtemp_encode)(const uint32_t, uint8_t *, int); 127 void (*lmtemp_getlim)(struct sysmon_envsys *, envsys_data_t *, 128 sysmon_envsys_lim_t *, uint32_t *); 129 void (*lmtemp_setlim)(struct sysmon_envsys *, envsys_data_t *, 130 sysmon_envsys_lim_t *, uint32_t *); 131 } lmtemptbl[] = { 132 [lmtemp_lm75] = 133 { 134 .lmtemp_name = "LM75", 135 .lmtemp_addrmask = LM75_ADDRMASK, 136 .lmtemp_addr = LM75_ADDR, 137 .lmtemp_decode = lmtemp_decode_lm75, 138 .lmtemp_encode = lmtemp_encode_lm75, 139 .lmtemp_getlim = lmtemp_getlim_lm75, 140 .lmtemp_setlim = lmtemp_setlim_lm75, 141 }, 142 [lmtemp_ds75] = 143 { 144 .lmtemp_name = "DS75", 145 .lmtemp_addrmask = LM75_ADDRMASK, 146 .lmtemp_addr = LM75_ADDR, 147 .lmtemp_decode = lmtemp_decode_ds75, 148 .lmtemp_encode = lmtemp_encode_ds75, 149 .lmtemp_getlim = lmtemp_getlim_lm75, 150 .lmtemp_setlim = lmtemp_setlim_lm75, 151 }, 152 [lmtemp_lm77] = 153 { 154 .lmtemp_name = "LM77", 155 .lmtemp_addrmask = LM77_ADDRMASK, 156 .lmtemp_addr = LM77_ADDR, 157 .lmtemp_decode = lmtemp_decode_lm77, 158 .lmtemp_encode = lmtemp_encode_lm77, 159 .lmtemp_getlim = lmtemp_getlim_lm77, 160 .lmtemp_setlim = lmtemp_setlim_lm77, 161 }, 162 }; 163 164 static int 165 lmtemp_match(device_t parent, cfdata_t cf, void *aux) 166 { 167 struct i2c_attach_args *ia = aux; 168 int i, match_result; 169 170 if (iic_use_direct_match(ia, cf, compat_data, &match_result)) 171 return match_result; 172 173 /* 174 * Indirect config - not much we can do! 175 */ 176 for (i = 0; i < __arraycount(lmtemptbl); i++) { 177 if (i == cf->cf_flags) { 178 break; 179 } 180 } 181 if (i == __arraycount(lmtemptbl)) { 182 return 0; 183 } 184 185 if ((ia->ia_addr & lmtemptbl[i].lmtemp_addrmask) == 186 lmtemptbl[i].lmtemp_addr) 187 return I2C_MATCH_ADDRESS_ONLY; 188 189 return 0; 190 } 191 192 static void 193 lmtemp_attach(device_t parent, device_t self, void *aux) 194 { 195 struct lmtemp_softc *sc = device_private(self); 196 struct i2c_attach_args *ia = aux; 197 const struct device_compatible_entry *dce; 198 char name[64]; 199 const char *desc; 200 int i; 201 202 sc->sc_dev = self; 203 dce = iic_compatible_lookup(ia, compat_data); 204 if (dce != NULL) { 205 i = (int)dce->value; 206 } else { 207 for (i = 0; i < __arraycount(lmtemptbl); i++) { 208 if (i == device_cfdata(self)->cf_flags) { 209 break; 210 } 211 } 212 KASSERT(i < __arraycount(lmtemptbl)); 213 } 214 215 sc->sc_tag = ia->ia_tag; 216 sc->sc_address = ia->ia_addr; 217 sc->sc_prop = ia->ia_prop; 218 219 if (ia->ia_prop != NULL) prop_object_retain(sc->sc_prop); 220 221 aprint_naive(": Temperature Sensor\n"); 222 if (ia->ia_name) { 223 aprint_normal(": %s %s Temperature Sensor\n", ia->ia_name, 224 lmtemptbl[i].lmtemp_name); 225 } else { 226 aprint_normal(": %s Temperature Sensor\n", 227 lmtemptbl[i].lmtemp_name); 228 } 229 230 sc->sc_lmtemp_decode = lmtemptbl[i].lmtemp_decode; 231 sc->sc_lmtemp_encode = lmtemptbl[i].lmtemp_encode; 232 233 if (iic_acquire_bus(sc->sc_tag, 0)) { 234 aprint_error_dev(self, 235 "unable to acquire I2C bus\n"); 236 return; 237 } 238 239 /* Read temperature limit(s) and remember initial value(s). */ 240 if (i == lmtemp_lm77) { 241 if (lmtemp_temp_read(sc, LM77_REG_TCRIT_SET_POINT, 242 &sc->sc_scrit, 1) != 0) { 243 aprint_error_dev(self, 244 "unable to read low register\n"); 245 iic_release_bus(sc->sc_tag, 0); 246 return; 247 } 248 if (lmtemp_temp_read(sc, LM77_REG_TLOW_SET_POINT, 249 &sc->sc_smin, 1) != 0) { 250 aprint_error_dev(self, 251 "unable to read low register\n"); 252 iic_release_bus(sc->sc_tag, 0); 253 return; 254 } 255 if (lmtemp_temp_read(sc, LM77_REG_THIGH_SET_POINT, 256 &sc->sc_smax, 1) != 0) { 257 aprint_error_dev(self, 258 "unable to read high register\n"); 259 iic_release_bus(sc->sc_tag, 0); 260 return; 261 } 262 } else { /* LM75 or compatible */ 263 if (lmtemp_temp_read(sc, LM75_REG_TOS_SET_POINT, 264 &sc->sc_smax, 1) != 0) { 265 aprint_error_dev(self, "unable to read Tos register\n"); 266 iic_release_bus(sc->sc_tag, 0); 267 return; 268 } 269 } 270 sc->sc_tmax = sc->sc_smax; 271 272 if (i == lmtemp_lm75) 273 lmtemp_setup_sysctl(sc); 274 275 /* Set the configuration of the LM75 to defaults. */ 276 if (lmtemp_config_write(sc, LM75_CONFIG_FAULT_QUEUE_4) != 0) { 277 aprint_error_dev(self, "unable to write config register\n"); 278 iic_release_bus(sc->sc_tag, 0); 279 return; 280 } 281 iic_release_bus(sc->sc_tag, 0); 282 283 sc->sc_sme = sysmon_envsys_create(); 284 /* Initialize sensor data. */ 285 sc->sc_sensor.units = ENVSYS_STEMP; 286 sc->sc_sensor.state = ENVSYS_SINVALID; 287 sc->sc_sensor.flags = ENVSYS_FMONLIMITS | ENVSYS_FHAS_ENTROPY; 288 289 (void)strlcpy(name, 290 ia->ia_name? ia->ia_name : device_xname(self), 291 sizeof(sc->sc_sensor.desc)); 292 293 if (prop_dictionary_get_string(sc->sc_prop, "s00", &desc)) { 294 strncpy(name, desc, 64); 295 } 296 297 (void)strlcpy(sc->sc_sensor.desc, name, 298 sizeof(sc->sc_sensor.desc)); 299 if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor)) { 300 sysmon_envsys_destroy(sc->sc_sme); 301 return; 302 } 303 304 /* Hook into system monitor. */ 305 sc->sc_sme->sme_name = device_xname(self); 306 sc->sc_sme->sme_cookie = sc; 307 sc->sc_sme->sme_refresh = lmtemp_refresh; 308 sc->sc_sme->sme_get_limits = lmtemptbl[i].lmtemp_getlim; 309 sc->sc_sme->sme_set_limits = lmtemptbl[i].lmtemp_setlim; 310 311 if (sysmon_envsys_register(sc->sc_sme)) { 312 aprint_error_dev(self, "unable to register with sysmon\n"); 313 sysmon_envsys_destroy(sc->sc_sme); 314 } 315 } 316 317 static int 318 lmtemp_config_write(struct lmtemp_softc *sc, uint8_t val) 319 { 320 uint8_t cmdbuf[2]; 321 322 cmdbuf[0] = LM75_REG_CONFIG; 323 cmdbuf[1] = val; 324 325 return iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 326 sc->sc_address, cmdbuf, 1, &cmdbuf[1], 1, 0); 327 } 328 329 static int 330 lmtemp_temp_write(struct lmtemp_softc *sc, uint8_t reg, uint32_t val, int degc) 331 { 332 uint8_t cmdbuf[3]; 333 334 cmdbuf[0] = reg; 335 sc->sc_lmtemp_encode(val, &cmdbuf[1], degc); 336 337 return iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 338 sc->sc_address, cmdbuf, 1, &cmdbuf[1], 2, 0); 339 } 340 341 static int 342 lmtemp_temp_read(struct lmtemp_softc *sc, uint8_t which, uint32_t *valp, 343 int degc) 344 { 345 int error; 346 uint8_t cmdbuf[1]; 347 uint8_t buf[LM75_TEMP_LEN]; 348 349 cmdbuf[0] = which; 350 351 error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 352 sc->sc_address, cmdbuf, 1, buf, LM75_TEMP_LEN, 0); 353 if (error) 354 return error; 355 356 *valp = sc->sc_lmtemp_decode(buf, degc); 357 return 0; 358 } 359 360 static void 361 lmtemp_refresh_sensor_data(struct lmtemp_softc *sc) 362 { 363 uint32_t val; 364 int error; 365 366 error = lmtemp_temp_read(sc, LM75_REG_TEMP, &val, 0); 367 if (error) { 368 #if 0 369 aprint_error_dev(sc->sc_dev, "unable to read temperature, error = %d\n", 370 error); 371 #endif 372 sc->sc_sensor.state = ENVSYS_SINVALID; 373 return; 374 } 375 376 sc->sc_sensor.value_cur = val; 377 sc->sc_sensor.state = ENVSYS_SVALID; 378 } 379 380 static void 381 lmtemp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 382 { 383 struct lmtemp_softc *sc = sme->sme_cookie; 384 385 if (iic_acquire_bus(sc->sc_tag, 0)) /* also locks our instance */ 386 return; 387 lmtemp_refresh_sensor_data(sc); 388 iic_release_bus(sc->sc_tag, 0); /* also unlocks our instance */ 389 } 390 391 static void 392 lmtemp_getlim_lm75(struct sysmon_envsys *sme, envsys_data_t *edata, 393 sysmon_envsys_lim_t *limits, uint32_t *props) 394 { 395 struct lmtemp_softc *sc = sme->sme_cookie; 396 uint32_t val; 397 398 *props &= ~(PROP_CRITMAX); 399 400 if (iic_acquire_bus(sc->sc_tag, 0)) 401 return; 402 if (lmtemp_temp_read(sc, LM75_REG_TOS_SET_POINT, &val, 0) == 0) { 403 limits->sel_critmax = val; 404 *props |= PROP_CRITMAX; 405 } 406 iic_release_bus(sc->sc_tag, 0); 407 } 408 409 static void 410 lmtemp_getlim_lm77(struct sysmon_envsys *sme, envsys_data_t *edata, 411 sysmon_envsys_lim_t *limits, uint32_t *props) 412 { 413 struct lmtemp_softc *sc = sme->sme_cookie; 414 uint32_t val; 415 416 *props &= ~(PROP_CRITMAX | PROP_WARNMAX | PROP_WARNMIN); 417 418 if (iic_acquire_bus(sc->sc_tag, 0)) 419 return; 420 if (lmtemp_temp_read(sc, LM77_REG_TCRIT_SET_POINT, &val, 0) == 0) { 421 limits->sel_critmax = val; 422 *props |= PROP_CRITMAX; 423 } 424 if (lmtemp_temp_read(sc, LM77_REG_THIGH_SET_POINT, &val, 0) == 0) { 425 limits->sel_warnmax = val; 426 *props |= PROP_WARNMAX; 427 } 428 if (lmtemp_temp_read(sc, LM77_REG_TLOW_SET_POINT, &val, 0) == 0) { 429 limits->sel_warnmin = val; 430 *props |= PROP_WARNMIN; 431 } 432 iic_release_bus(sc->sc_tag, 0); 433 } 434 435 static void 436 lmtemp_setlim_lm75(struct sysmon_envsys *sme, envsys_data_t *edata, 437 sysmon_envsys_lim_t *limits, uint32_t *props) 438 { 439 struct lmtemp_softc *sc = sme->sme_cookie; 440 int32_t limit; 441 442 if (*props & PROP_CRITMAX) { 443 if (limits == NULL) /* Restore defaults */ 444 limit = sc->sc_smax; 445 else 446 limit = limits->sel_critmax; 447 if (iic_acquire_bus(sc->sc_tag, 0)) 448 return; 449 lmtemp_temp_write(sc, LM75_REG_THYST_SET_POINT, 450 limit - 5000000, 0); 451 lmtemp_temp_write(sc, LM75_REG_TOS_SET_POINT, limit, 0); 452 iic_release_bus(sc->sc_tag, 0); 453 454 /* Synchronise sysctl */ 455 sc->sc_tmax = (limit - 273150000) / 1000000; 456 } 457 } 458 459 static void 460 lmtemp_setlim_lm77(struct sysmon_envsys *sme, envsys_data_t *edata, 461 sysmon_envsys_lim_t *limits, uint32_t *props) 462 { 463 struct lmtemp_softc *sc = sme->sme_cookie; 464 int32_t limit; 465 466 iic_acquire_bus(sc->sc_tag, 0); 467 if (*props & PROP_CRITMAX) { 468 if (limits == NULL) /* Restore defaults */ 469 limit = sc->sc_scrit; 470 else 471 limit = limits->sel_critmax; 472 lmtemp_temp_write(sc, LM77_REG_TCRIT_SET_POINT, limit, 0); 473 } 474 if (*props & PROP_WARNMAX) { 475 if (limits == NULL) /* Restore defaults */ 476 limit = sc->sc_smax; 477 else 478 limit = limits->sel_warnmax; 479 lmtemp_temp_write(sc, LM77_REG_THIGH_SET_POINT, limit, 0); 480 } 481 if (*props & PROP_WARNMIN) { 482 if (limits == NULL) /* Restore defaults */ 483 limit = sc->sc_smin; 484 else 485 limit = limits->sel_warnmin; 486 lmtemp_temp_write(sc, LM77_REG_TLOW_SET_POINT, limit, 0); 487 } 488 iic_release_bus(sc->sc_tag, 0); 489 } 490 491 static uint32_t 492 lmtemp_decode_lm75(const uint8_t *buf, int degc) 493 { 494 int temp; 495 uint32_t val; 496 497 /* 498 * LM75 temps are the most-significant 9 bits of a 16-bit reg. 499 * sign-extend the MSB and add in the 0.5 from the LSB 500 */ 501 temp = (int8_t) buf[0]; 502 temp = (temp << 1) + ((buf[1] >> 7) & 0x1); 503 504 /* Temp is given in 1/2 deg. C, we convert to C or uK. */ 505 if (degc) 506 val = temp / 2; 507 else 508 val = temp * 500000 + 273150000; 509 510 return val; 511 } 512 513 static uint32_t 514 lmtemp_decode_ds75(const uint8_t *buf, int degc) 515 { 516 int temp; 517 518 /* 519 * Sign-extend the MSB byte, and add in the fractions of a 520 * degree contained in the LSB (precision 1/16th DegC). 521 */ 522 temp = (int8_t)buf[0]; 523 temp = (temp << 4) | ((buf[1] >> 4) & 0xf); 524 525 /* 526 * Conversion to C or uK is simple. 527 */ 528 if (degc) 529 return temp / 16; 530 else 531 return (temp * 62500 + 273150000); 532 } 533 534 static uint32_t 535 lmtemp_decode_lm77(const uint8_t *buf, int degc) 536 { 537 int temp; 538 uint32_t val; 539 540 /* 541 * Describe each bits of temperature registers on LM77. 542 * D15 - D12: Sign 543 * D11 - D3 : Bit8(MSB) - Bit0 544 */ 545 temp = (int8_t)buf[0]; 546 temp = (temp << 5) | ((buf[1] >> 3) & 0x1f); 547 548 /* Temp is given in 1/2 deg. C, we convert to C or uK. */ 549 if (degc) 550 val = temp / 2; 551 else 552 val = temp * 500000 + 273150000; 553 554 return val; 555 } 556 557 static void lmtemp_encode_lm75(const uint32_t val, uint8_t *buf, int degc) 558 { 559 int temp; 560 561 /* Convert from C or uK to register format */ 562 if (degc) 563 temp = val * 2; 564 else 565 temp = (val - 273150000) / 500000; 566 buf[0] = (temp >> 1) & 0xff; 567 buf[1] = (temp & 1) << 7; 568 } 569 570 static void lmtemp_encode_ds75(const uint32_t val, uint8_t *buf, int degc) 571 { 572 int temp; 573 574 /* Convert from C or uK to register format */ 575 if (degc) 576 temp = val * 16; 577 else 578 temp = (val - 273150000) / 62500; 579 buf[0] = (temp >> 4) & 0xff; 580 buf[1] = (temp & 0xf) << 4; 581 } 582 583 static void lmtemp_encode_lm77(const uint32_t val, uint8_t *buf, int degc) 584 { 585 int temp; 586 587 /* Convert from C or uK to register format */ 588 if (degc) 589 temp = val * 2; 590 else 591 temp = (val - 273150000) / 500000; 592 buf[0] = (temp >> 5) & 0xff; 593 buf[1] = (temp & 0x1f) << 3; 594 } 595 596 static void 597 lmtemp_setup_sysctl(struct lmtemp_softc *sc) 598 { 599 const struct sysctlnode *me = NULL, *node = NULL; 600 601 sysctl_createv(NULL, 0, NULL, &me, 602 CTLFLAG_READWRITE, 603 CTLTYPE_NODE, device_xname(sc->sc_dev), NULL, 604 NULL, 0, NULL, 0, 605 CTL_MACHDEP, CTL_CREATE, CTL_EOL); 606 607 sysctl_createv(NULL, 0, NULL, &node, 608 CTLFLAG_READWRITE | CTLFLAG_OWNDESC, 609 CTLTYPE_INT, "temp", "Threshold temperature", 610 sysctl_lm75_temp, 1, (void *)sc, 0, 611 CTL_MACHDEP, me->sysctl_num, CTL_CREATE, CTL_EOL); 612 } 613 614 static int 615 sysctl_lm75_temp(SYSCTLFN_ARGS) 616 { 617 struct sysctlnode node = *rnode; 618 struct lmtemp_softc *sc = node.sysctl_data; 619 int temp, error; 620 621 if (newp) { 622 623 /* we're asked to write */ 624 node.sysctl_data = &sc->sc_tmax; 625 if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) { 626 627 temp = *(int *)node.sysctl_data; 628 sc->sc_tmax = temp; 629 error = iic_acquire_bus(sc->sc_tag, 0); 630 if (error) 631 return error; 632 lmtemp_temp_write(sc, LM75_REG_THYST_SET_POINT, 633 sc->sc_tmax - 5, 1); 634 lmtemp_temp_write(sc, LM75_REG_TOS_SET_POINT, 635 sc->sc_tmax, 1); 636 iic_release_bus(sc->sc_tag, 0); 637 638 /* Synchronise envsys - calls lmtemp_getlim_lm75() */ 639 sysmon_envsys_update_limits(sc->sc_sme, &sc->sc_sensor); 640 return 0; 641 } 642 return EINVAL; 643 } else { 644 645 node.sysctl_data = &sc->sc_tmax; 646 node.sysctl_size = 4; 647 return (sysctl_lookup(SYSCTLFN_CALL(&node))); 648 } 649 650 return 0; 651 } 652 653 SYSCTL_SETUP(sysctl_lmtemp_setup, "sysctl lmtemp subtree setup") 654 { 655 656 sysctl_createv(NULL, 0, NULL, NULL, 657 CTLFLAG_PERMANENT, 658 CTLTYPE_NODE, "machdep", NULL, 659 NULL, 0, NULL, 0, 660 CTL_MACHDEP, CTL_EOL); 661 } 662 663 664