1 /* $NetBSD: lm75.c,v 1.51 2026/05/27 10:35:22 jdc 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.51 2026/05/27 10:35:22 jdc 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 57 struct sysmon_envsys *sc_sme; 58 envsys_data_t sc_sensor; 59 int sc_tmax; 60 uint32_t sc_smax, sc_smin, sc_scrit; 61 62 uint32_t (*sc_lmtemp_decode)(const uint8_t *, int); 63 void (*sc_lmtemp_encode)(const uint32_t, uint8_t *, int); 64 }; 65 66 static int lmtemp_match(device_t, cfdata_t, void *); 67 static void lmtemp_attach(device_t, device_t, void *); 68 69 CFATTACH_DECL_NEW(lmtemp, sizeof(struct lmtemp_softc), 70 lmtemp_match, lmtemp_attach, NULL, NULL); 71 72 static void lmtemp_refresh(struct sysmon_envsys *, envsys_data_t *); 73 static int lmtemp_config_write(struct lmtemp_softc *, uint8_t); 74 static int lmtemp_temp_write(struct lmtemp_softc *, uint8_t, uint32_t, 75 int); 76 static int lmtemp_temp_read(struct lmtemp_softc *, uint8_t, uint32_t *, 77 int); 78 static uint32_t lmtemp_decode_lm75(const uint8_t *, int); 79 static uint32_t lmtemp_decode_ds75(const uint8_t *, int); 80 static uint32_t lmtemp_decode_lm77(const uint8_t *, int); 81 static void lmtemp_encode_lm75(const uint32_t, uint8_t *, int); 82 static void lmtemp_encode_ds75(const uint32_t, uint8_t *, int); 83 static void lmtemp_encode_lm77(const uint32_t, uint8_t *, int); 84 static void lmtemp_getlim_lm75(struct sysmon_envsys *, envsys_data_t *, 85 sysmon_envsys_lim_t *, uint32_t *); 86 static void lmtemp_getlim_lm77(struct sysmon_envsys *, envsys_data_t *, 87 sysmon_envsys_lim_t *, uint32_t *); 88 static void lmtemp_setlim_lm75(struct sysmon_envsys *, envsys_data_t *, 89 sysmon_envsys_lim_t *, uint32_t *); 90 static void lmtemp_setlim_lm77(struct sysmon_envsys *, envsys_data_t *, 91 sysmon_envsys_lim_t *, uint32_t *); 92 93 static void lmtemp_setup_sysctl(struct lmtemp_softc *); 94 static int sysctl_lm75_temp(SYSCTLFN_ARGS); 95 96 enum { 97 lmtemp_lm75 = 0, 98 lmtemp_ds75 = 1, 99 lmtemp_lm77 = 2, 100 }; 101 102 static const struct device_compatible_entry compat_data[] = { 103 { .compat = "national,lm75", .value = lmtemp_lm75 }, 104 { .compat = "i2c-lm75", .value = lmtemp_lm75 }, 105 { .compat = "lm75", .value = lmtemp_lm75 }, 106 107 /* XXX Linux treats ds1775 and ds75 differently. */ 108 { .compat = "dallas,ds1775", .value = lmtemp_ds75 }, 109 { .compat = "ds1775", .value = lmtemp_ds75 }, 110 111 { .compat = "national,lm77", .value = lmtemp_lm77 }, 112 113 /* 114 * see XXX in _attach() below: add code once non-lm75 matches are 115 * added here! 116 */ 117 DEVICE_COMPAT_EOL 118 }; 119 120 static const struct { 121 const char *lmtemp_name; 122 int lmtemp_addrmask; 123 int lmtemp_addr; 124 uint32_t (*lmtemp_decode)(const uint8_t *, int); 125 void (*lmtemp_encode)(const uint32_t, uint8_t *, int); 126 void (*lmtemp_getlim)(struct sysmon_envsys *, envsys_data_t *, 127 sysmon_envsys_lim_t *, uint32_t *); 128 void (*lmtemp_setlim)(struct sysmon_envsys *, envsys_data_t *, 129 sysmon_envsys_lim_t *, uint32_t *); 130 } lmtemptbl[] = { 131 [lmtemp_lm75] = 132 { 133 .lmtemp_name = "LM75", 134 .lmtemp_addrmask = LM75_ADDRMASK, 135 .lmtemp_addr = LM75_ADDR, 136 .lmtemp_decode = lmtemp_decode_lm75, 137 .lmtemp_encode = lmtemp_encode_lm75, 138 .lmtemp_getlim = lmtemp_getlim_lm75, 139 .lmtemp_setlim = lmtemp_setlim_lm75, 140 }, 141 [lmtemp_ds75] = 142 { 143 .lmtemp_name = "DS75", 144 .lmtemp_addrmask = LM75_ADDRMASK, 145 .lmtemp_addr = LM75_ADDR, 146 .lmtemp_decode = lmtemp_decode_ds75, 147 .lmtemp_encode = lmtemp_encode_ds75, 148 .lmtemp_getlim = lmtemp_getlim_lm75, 149 .lmtemp_setlim = lmtemp_setlim_lm75, 150 }, 151 [lmtemp_lm77] = 152 { 153 .lmtemp_name = "LM77", 154 .lmtemp_addrmask = LM77_ADDRMASK, 155 .lmtemp_addr = LM77_ADDR, 156 .lmtemp_decode = lmtemp_decode_lm77, 157 .lmtemp_encode = lmtemp_encode_lm77, 158 .lmtemp_getlim = lmtemp_getlim_lm77, 159 .lmtemp_setlim = lmtemp_setlim_lm77, 160 }, 161 }; 162 163 static int 164 lmtemp_match(device_t parent, cfdata_t cf, void *aux) 165 { 166 struct i2c_attach_args *ia = aux; 167 int i, match_result; 168 169 if (iic_use_direct_match(ia, cf, compat_data, &match_result)) 170 return match_result; 171 172 /* 173 * Indirect config - not much we can do! 174 */ 175 for (i = 0; i < __arraycount(lmtemptbl); i++) { 176 if (i == cf->cf_flags) { 177 break; 178 } 179 } 180 if (i == __arraycount(lmtemptbl)) { 181 return 0; 182 } 183 184 if ((ia->ia_addr & lmtemptbl[i].lmtemp_addrmask) == 185 lmtemptbl[i].lmtemp_addr) 186 return I2C_MATCH_ADDRESS_ONLY; 187 188 return 0; 189 } 190 191 static void 192 lmtemp_attach(device_t parent, device_t self, void *aux) 193 { 194 struct lmtemp_softc *sc = device_private(self); 195 struct i2c_attach_args *ia = aux; 196 const struct device_compatible_entry *dce; 197 char name[64]; 198 int i; 199 uint8_t config = LM75_CONFIG_FAULT_QUEUE_4; 200 201 sc->sc_dev = self; 202 dce = iic_compatible_lookup(ia, compat_data); 203 if (dce != NULL) { 204 i = (int)dce->value; 205 } else { 206 for (i = 0; i < __arraycount(lmtemptbl); i++) { 207 if (i == device_cfdata(self)->cf_flags) { 208 break; 209 } 210 } 211 KASSERT(i < __arraycount(lmtemptbl)); 212 } 213 214 sc->sc_tag = ia->ia_tag; 215 sc->sc_address = ia->ia_addr; 216 217 aprint_naive(": Temperature Sensor\n"); 218 if (ia->ia_name) { 219 aprint_normal(": %s %s Temperature Sensor\n", ia->ia_name, 220 lmtemptbl[i].lmtemp_name); 221 } else { 222 aprint_normal(": %s Temperature Sensor\n", 223 lmtemptbl[i].lmtemp_name); 224 } 225 226 sc->sc_lmtemp_decode = lmtemptbl[i].lmtemp_decode; 227 sc->sc_lmtemp_encode = lmtemptbl[i].lmtemp_encode; 228 229 if (iic_acquire_bus(sc->sc_tag, 0)) { 230 aprint_error_dev(self, 231 "unable to acquire I2C bus\n"); 232 return; 233 } 234 235 /* Read temperature limit(s) and remember initial value(s). */ 236 if (i == lmtemp_lm77) { 237 if (lmtemp_temp_read(sc, LM77_REG_TCRIT_SET_POINT, 238 &sc->sc_scrit, 1) != 0) { 239 aprint_error_dev(self, 240 "unable to read low register\n"); 241 iic_release_bus(sc->sc_tag, 0); 242 return; 243 } 244 if (lmtemp_temp_read(sc, LM77_REG_TLOW_SET_POINT, 245 &sc->sc_smin, 1) != 0) { 246 aprint_error_dev(self, 247 "unable to read low register\n"); 248 iic_release_bus(sc->sc_tag, 0); 249 return; 250 } 251 if (lmtemp_temp_read(sc, LM77_REG_THIGH_SET_POINT, 252 &sc->sc_smax, 1) != 0) { 253 aprint_error_dev(self, 254 "unable to read high register\n"); 255 iic_release_bus(sc->sc_tag, 0); 256 return; 257 } 258 } else { /* LM75 or compatible */ 259 if (lmtemp_temp_read(sc, LM75_REG_TOS_SET_POINT, 260 &sc->sc_smax, 1) != 0) { 261 aprint_error_dev(self, "unable to read Tos register\n"); 262 iic_release_bus(sc->sc_tag, 0); 263 return; 264 } 265 } 266 sc->sc_tmax = sc->sc_smax; 267 268 if (i == lmtemp_lm75) 269 lmtemp_setup_sysctl(sc); 270 271 /* DS75 has better resolution */ 272 if (i == lmtemp_ds75) 273 config = LM75_CONFIG_FAULT_QUEUE_4 | DS75_CONFIG_RES_11BIT; 274 275 /* Set the configuration of the LM75 to defaults. */ 276 if (lmtemp_config_write(sc, config) != 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 device_getprop_string(self, "s00", name, sizeof(name)); 294 295 (void)strlcpy(sc->sc_sensor.desc, name, 296 sizeof(sc->sc_sensor.desc)); 297 if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_sensor)) { 298 sysmon_envsys_destroy(sc->sc_sme); 299 return; 300 } 301 302 /* Hook into system monitor. */ 303 sc->sc_sme->sme_name = device_xname(self); 304 sc->sc_sme->sme_cookie = sc; 305 sc->sc_sme->sme_refresh = lmtemp_refresh; 306 sc->sc_sme->sme_get_limits = lmtemptbl[i].lmtemp_getlim; 307 sc->sc_sme->sme_set_limits = lmtemptbl[i].lmtemp_setlim; 308 309 if (sysmon_envsys_register(sc->sc_sme)) { 310 aprint_error_dev(self, "unable to register with sysmon\n"); 311 sysmon_envsys_destroy(sc->sc_sme); 312 } 313 } 314 315 static int 316 lmtemp_config_write(struct lmtemp_softc *sc, uint8_t val) 317 { 318 uint8_t cmdbuf[2]; 319 320 cmdbuf[0] = LM75_REG_CONFIG; 321 cmdbuf[1] = val; 322 323 return iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 324 sc->sc_address, cmdbuf, 1, &cmdbuf[1], 1, 0); 325 } 326 327 static int 328 lmtemp_temp_write(struct lmtemp_softc *sc, uint8_t reg, uint32_t val, int degc) 329 { 330 uint8_t cmdbuf[3]; 331 332 cmdbuf[0] = reg; 333 sc->sc_lmtemp_encode(val, &cmdbuf[1], degc); 334 335 return iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, 336 sc->sc_address, cmdbuf, 1, &cmdbuf[1], 2, 0); 337 } 338 339 static int 340 lmtemp_temp_read(struct lmtemp_softc *sc, uint8_t which, uint32_t *valp, 341 int degc) 342 { 343 int error; 344 uint8_t cmdbuf[1]; 345 uint8_t buf[LM75_TEMP_LEN]; 346 347 cmdbuf[0] = which; 348 349 error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, 350 sc->sc_address, cmdbuf, 1, buf, LM75_TEMP_LEN, 0); 351 if (error) 352 return error; 353 354 *valp = sc->sc_lmtemp_decode(buf, degc); 355 return 0; 356 } 357 358 static void 359 lmtemp_refresh_sensor_data(struct lmtemp_softc *sc) 360 { 361 uint32_t val; 362 int error; 363 364 error = lmtemp_temp_read(sc, LM75_REG_TEMP, &val, 0); 365 if (error) { 366 #if 0 367 aprint_error_dev(sc->sc_dev, "unable to read temperature, error = %d\n", 368 error); 369 #endif 370 sc->sc_sensor.state = ENVSYS_SINVALID; 371 return; 372 } 373 374 sc->sc_sensor.value_cur = val; 375 sc->sc_sensor.state = ENVSYS_SVALID; 376 } 377 378 static void 379 lmtemp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 380 { 381 struct lmtemp_softc *sc = sme->sme_cookie; 382 383 if (iic_acquire_bus(sc->sc_tag, 0)) /* also locks our instance */ 384 return; 385 lmtemp_refresh_sensor_data(sc); 386 iic_release_bus(sc->sc_tag, 0); /* also unlocks our instance */ 387 } 388 389 static void 390 lmtemp_getlim_lm75(struct sysmon_envsys *sme, envsys_data_t *edata, 391 sysmon_envsys_lim_t *limits, uint32_t *props) 392 { 393 struct lmtemp_softc *sc = sme->sme_cookie; 394 uint32_t val; 395 396 *props &= ~(PROP_CRITMAX); 397 398 if (iic_acquire_bus(sc->sc_tag, 0)) 399 return; 400 if (lmtemp_temp_read(sc, LM75_REG_TOS_SET_POINT, &val, 0) == 0) { 401 limits->sel_critmax = val; 402 *props |= PROP_CRITMAX; 403 } 404 iic_release_bus(sc->sc_tag, 0); 405 } 406 407 static void 408 lmtemp_getlim_lm77(struct sysmon_envsys *sme, envsys_data_t *edata, 409 sysmon_envsys_lim_t *limits, uint32_t *props) 410 { 411 struct lmtemp_softc *sc = sme->sme_cookie; 412 uint32_t val; 413 414 *props &= ~(PROP_CRITMAX | PROP_WARNMAX | PROP_WARNMIN); 415 416 if (iic_acquire_bus(sc->sc_tag, 0)) 417 return; 418 if (lmtemp_temp_read(sc, LM77_REG_TCRIT_SET_POINT, &val, 0) == 0) { 419 limits->sel_critmax = val; 420 *props |= PROP_CRITMAX; 421 } 422 if (lmtemp_temp_read(sc, LM77_REG_THIGH_SET_POINT, &val, 0) == 0) { 423 limits->sel_warnmax = val; 424 *props |= PROP_WARNMAX; 425 } 426 if (lmtemp_temp_read(sc, LM77_REG_TLOW_SET_POINT, &val, 0) == 0) { 427 limits->sel_warnmin = val; 428 *props |= PROP_WARNMIN; 429 } 430 iic_release_bus(sc->sc_tag, 0); 431 } 432 433 static void 434 lmtemp_setlim_lm75(struct sysmon_envsys *sme, envsys_data_t *edata, 435 sysmon_envsys_lim_t *limits, uint32_t *props) 436 { 437 struct lmtemp_softc *sc = sme->sme_cookie; 438 int32_t limit; 439 int degc; 440 441 if (limits == NULL || *props & PROP_CRITMAX) { 442 if (limits == NULL) { /* Restore defaults */ 443 limit = sc->sc_smax; 444 degc = 1; 445 } else { 446 limit = limits->sel_critmax; 447 degc = 0; 448 } 449 if (iic_acquire_bus(sc->sc_tag, 0)) 450 return; 451 lmtemp_temp_write(sc, LM75_REG_THYST_SET_POINT, 452 limit - 5000000, degc); 453 lmtemp_temp_write(sc, LM75_REG_TOS_SET_POINT, limit, degc); 454 iic_release_bus(sc->sc_tag, 0); 455 456 /* Synchronise sysctl */ 457 sc->sc_tmax = (limit - 273150000) / 1000000; 458 } 459 } 460 461 static void 462 lmtemp_setlim_lm77(struct sysmon_envsys *sme, envsys_data_t *edata, 463 sysmon_envsys_lim_t *limits, uint32_t *props) 464 { 465 struct lmtemp_softc *sc = sme->sme_cookie; 466 int32_t limit; 467 int degc; 468 469 iic_acquire_bus(sc->sc_tag, 0); 470 if (limits == NULL || *props & PROP_CRITMAX) { 471 if (limits == NULL) { /* Restore defaults */ 472 limit = sc->sc_scrit; 473 degc = 1; 474 } else { 475 limit = limits->sel_critmax; 476 degc = 0; 477 } 478 lmtemp_temp_write(sc, LM77_REG_TCRIT_SET_POINT, limit, degc); 479 } 480 if (limits == NULL || *props & PROP_WARNMAX) { 481 if (limits == NULL) { /* Restore defaults */ 482 limit = sc->sc_smax; 483 degc = 1; 484 } else { 485 limit = limits->sel_warnmax; 486 degc = 0; 487 } 488 lmtemp_temp_write(sc, LM77_REG_THIGH_SET_POINT, limit, degc); 489 } 490 if (limits == NULL || *props & PROP_WARNMIN) { 491 if (limits == NULL) { /* Restore defaults */ 492 limit = sc->sc_smin; 493 degc = 1; 494 } else { 495 limit = limits->sel_warnmin; 496 degc = 0; 497 } 498 lmtemp_temp_write(sc, LM77_REG_TLOW_SET_POINT, limit, degc); 499 } 500 iic_release_bus(sc->sc_tag, 0); 501 } 502 503 static uint32_t 504 lmtemp_decode_lm75(const uint8_t *buf, int degc) 505 { 506 int temp; 507 uint32_t val; 508 509 /* 510 * LM75 temps are the most-significant 9 bits of a 16-bit reg. 511 * sign-extend the MSB and add in the 0.5 from the LSB 512 */ 513 temp = (int8_t) buf[0]; 514 temp = (temp << 1) + ((buf[1] >> 7) & 0x1); 515 516 /* Temp is given in 1/2 deg. C, we convert to C or uK. */ 517 if (degc) 518 val = temp / 2; 519 else 520 val = temp * 500000 + 273150000; 521 522 return val; 523 } 524 525 static uint32_t 526 lmtemp_decode_ds75(const uint8_t *buf, int degc) 527 { 528 int temp; 529 530 /* 531 * Sign-extend the MSB byte, and add in the fractions of a 532 * degree contained in the LSB (precision 1/16th DegC). 533 */ 534 temp = (int8_t)buf[0]; 535 temp = (temp << 4) | ((buf[1] >> 4) & 0xf); 536 537 /* 538 * Conversion to C or uK is simple. 539 */ 540 if (degc) 541 return temp / 16; 542 else 543 return (temp * 62500 + 273150000); 544 } 545 546 static uint32_t 547 lmtemp_decode_lm77(const uint8_t *buf, int degc) 548 { 549 int temp; 550 uint32_t val; 551 552 /* 553 * Describe each bits of temperature registers on LM77. 554 * D15 - D12: Sign 555 * D11 - D3 : Bit8(MSB) - Bit0 556 */ 557 temp = (int8_t)buf[0]; 558 temp = (temp << 5) | ((buf[1] >> 3) & 0x1f); 559 560 /* Temp is given in 1/2 deg. C, we convert to C or uK. */ 561 if (degc) 562 val = temp / 2; 563 else 564 val = temp * 500000 + 273150000; 565 566 return val; 567 } 568 569 static void lmtemp_encode_lm75(const uint32_t val, uint8_t *buf, int degc) 570 { 571 int temp; 572 573 /* Convert from C or uK to register format */ 574 if (degc) 575 temp = val * 2; 576 else 577 temp = (val - 273150000) / 500000; 578 buf[0] = (temp >> 1) & 0xff; 579 buf[1] = (temp & 1) << 7; 580 } 581 582 static void lmtemp_encode_ds75(const uint32_t val, uint8_t *buf, int degc) 583 { 584 int temp; 585 586 /* Convert from C or uK to register format */ 587 if (degc) 588 temp = val * 16; 589 else 590 temp = (val - 273150000) / 62500; 591 buf[0] = (temp >> 4) & 0xff; 592 buf[1] = (temp & 0xf) << 4; 593 } 594 595 static void lmtemp_encode_lm77(const uint32_t val, uint8_t *buf, int degc) 596 { 597 int temp; 598 599 /* Convert from C or uK to register format */ 600 if (degc) 601 temp = val * 2; 602 else 603 temp = (val - 273150000) / 500000; 604 buf[0] = (temp >> 5) & 0xff; 605 buf[1] = (temp & 0x1f) << 3; 606 } 607 608 static void 609 lmtemp_setup_sysctl(struct lmtemp_softc *sc) 610 { 611 const struct sysctlnode *me = NULL, *node = NULL; 612 613 sysctl_createv(NULL, 0, NULL, &me, 614 CTLFLAG_READWRITE, 615 CTLTYPE_NODE, device_xname(sc->sc_dev), NULL, 616 NULL, 0, NULL, 0, 617 CTL_MACHDEP, CTL_CREATE, CTL_EOL); 618 619 sysctl_createv(NULL, 0, NULL, &node, 620 CTLFLAG_READWRITE | CTLFLAG_OWNDESC, 621 CTLTYPE_INT, "temp", "Threshold temperature", 622 sysctl_lm75_temp, 1, (void *)sc, 0, 623 CTL_MACHDEP, me->sysctl_num, CTL_CREATE, CTL_EOL); 624 } 625 626 static int 627 sysctl_lm75_temp(SYSCTLFN_ARGS) 628 { 629 struct sysctlnode node = *rnode; 630 struct lmtemp_softc *sc = node.sysctl_data; 631 int temp, error; 632 633 if (newp) { 634 635 /* we're asked to write */ 636 node.sysctl_data = &sc->sc_tmax; 637 if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) { 638 639 temp = *(int *)node.sysctl_data; 640 sc->sc_tmax = temp; 641 error = iic_acquire_bus(sc->sc_tag, 0); 642 if (error) 643 return error; 644 lmtemp_temp_write(sc, LM75_REG_THYST_SET_POINT, 645 sc->sc_tmax - 5, 1); 646 lmtemp_temp_write(sc, LM75_REG_TOS_SET_POINT, 647 sc->sc_tmax, 1); 648 iic_release_bus(sc->sc_tag, 0); 649 650 /* Synchronise envsys - calls lmtemp_getlim_lm75() */ 651 sysmon_envsys_update_limits(sc->sc_sme, &sc->sc_sensor); 652 return 0; 653 } 654 return EINVAL; 655 } else { 656 657 node.sysctl_data = &sc->sc_tmax; 658 node.sysctl_size = 4; 659 return (sysctl_lookup(SYSCTLFN_CALL(&node))); 660 } 661 662 return 0; 663 } 664 665 SYSCTL_SETUP(sysctl_lmtemp_setup, "sysctl lmtemp subtree setup") 666 { 667 668 sysctl_createv(NULL, 0, NULL, NULL, 669 CTLFLAG_PERMANENT, 670 CTLTYPE_NODE, "machdep", NULL, 671 NULL, 0, NULL, 0, 672 CTL_MACHDEP, CTL_EOL); 673 } 674 675 676