1 /* $NetBSD: acpi_tz.c,v 1.74 2010/06/07 17:28:17 jruoho Exp $ */ 2 3 /* 4 * Copyright (c) 2003 Jared D. McNeill <jmcneill@invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. The name of the author may not be used to endorse or promote products 13 * derived from this software without specific prior written permission. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 20 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 22 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 /* 29 * ACPI Thermal Zone driver 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: acpi_tz.c,v 1.74 2010/06/07 17:28:17 jruoho Exp $"); 34 35 #include <sys/param.h> 36 #include <sys/device.h> 37 #include <sys/callout.h> 38 #include <sys/kernel.h> 39 #include <sys/systm.h> 40 41 #include <dev/acpi/acpireg.h> 42 #include <dev/acpi/acpivar.h> 43 #include <dev/acpi/acpi_power.h> 44 45 #define _COMPONENT ACPI_TZ_COMPONENT 46 ACPI_MODULE_NAME ("acpi_tz") 47 48 #define ACPI_NOTIFY_TZ_ZONE 0x80 49 #define ACPI_NOTIFY_TZ_TRIP 0x81 50 #define ACPI_NOTIFY_TZ_DEVLIST 0x82 51 52 #define ATZ_F_CRITICAL 0x01 /* zone critical */ 53 #define ATZ_F_HOT 0x02 /* zone hot */ 54 #define ATZ_F_PASSIVE 0x04 /* zone passive cooling */ 55 #define ATZ_F_PASSIVEONLY 0x08 /* zone is passive cooling only */ 56 57 #define ATZ_ACTIVE_NONE -1 58 59 /* 60 * The constants are as follows: 61 * 62 * ATZ_TZP_RATE default polling interval (30 seconds) if no _TZP 63 * ATZ_NLEVELS number of cooling levels for _ACx and _ALx 64 * ATZ_ZEROC 0 C, measured in 0.1 Kelvin 65 * ATZ_TMP_INVALID temporarily invalid temperature 66 * ATZ_ZONE_EXPIRE zone info refetch interval (15 minutes) 67 */ 68 #define ATZ_TZP_RATE 300 69 #define ATZ_NLEVELS 10 70 #define ATZ_ZEROC 2732 71 #define ATZ_TMP_INVALID 0xffffffff 72 #define ATZ_ZONE_EXPIRE 9000 73 74 /* 75 * All temperatures are reported in 0.1 Kelvin. 76 * The ACPI specification assumes that K = C + 273.2 77 * rather than the nominal 273.15 used by envsys(4). 78 */ 79 #define ATZ2UKELVIN(t) ((t) * 100000 - 50000) 80 81 struct acpitz_zone { 82 ACPI_BUFFER al[ATZ_NLEVELS]; 83 uint32_t ac[ATZ_NLEVELS]; 84 uint32_t crt; 85 uint32_t hot; 86 uint32_t rtv; 87 uint32_t psv; 88 uint32_t tc1; 89 uint32_t tc2; 90 uint32_t tmp; 91 uint32_t prevtmp; 92 uint32_t tzp; 93 uint32_t fanmin; 94 uint32_t fanmax; 95 uint32_t fancurrent; 96 }; 97 98 struct acpitz_softc { 99 struct acpi_devnode *sc_node; 100 struct sysmon_envsys *sc_sme; 101 struct acpitz_zone sc_zone; 102 struct callout sc_callout; 103 envsys_data_t sc_temp_sensor; 104 envsys_data_t sc_fan_sensor; 105 int sc_active; 106 int sc_flags; 107 int sc_zone_expire; 108 bool sc_first; 109 bool sc_have_fan; 110 }; 111 112 static int acpitz_match(device_t, cfdata_t, void *); 113 static void acpitz_attach(device_t, device_t, void *); 114 static int acpitz_detach(device_t, int); 115 static void acpitz_get_status(void *); 116 static void acpitz_get_zone(void *, int); 117 static void acpitz_get_zone_quiet(void *); 118 static char *acpitz_celcius_string(int); 119 static void acpitz_power_off(struct acpitz_softc *); 120 static void acpitz_power_zone(struct acpitz_softc *, int, int); 121 static void acpitz_sane_temp(uint32_t *tmp); 122 static ACPI_STATUS acpitz_switch_cooler(ACPI_OBJECT *, void *); 123 static void acpitz_notify_handler(ACPI_HANDLE, uint32_t, void *); 124 static int acpitz_get_integer(device_t, const char *, uint32_t *); 125 static void acpitz_tick(void *); 126 static void acpitz_init_envsys(device_t); 127 static void acpitz_get_limits(struct sysmon_envsys *, 128 envsys_data_t *, 129 sysmon_envsys_lim_t *, uint32_t *); 130 static int acpitz_get_fanspeed(device_t, uint32_t *, 131 uint32_t *, uint32_t *); 132 #ifdef notyet 133 static ACPI_STATUS acpitz_set_fanspeed(device_t, uint32_t); 134 #endif 135 136 CFATTACH_DECL_NEW(acpitz, sizeof(struct acpitz_softc), 137 acpitz_match, acpitz_attach, acpitz_detach, NULL); 138 139 /* 140 * acpitz_match: autoconf(9) match routine 141 */ 142 static int 143 acpitz_match(device_t parent, cfdata_t match, void *aux) 144 { 145 struct acpi_attach_args *aa = aux; 146 147 if (aa->aa_node->ad_type != ACPI_TYPE_THERMAL) 148 return 0; 149 150 return 1; 151 } 152 153 /* 154 * acpitz_attach: autoconf(9) attach routine 155 */ 156 static void 157 acpitz_attach(device_t parent, device_t self, void *aux) 158 { 159 struct acpitz_softc *sc = device_private(self); 160 struct acpi_attach_args *aa = aux; 161 ACPI_INTEGER val; 162 ACPI_STATUS rv; 163 164 aprint_naive("\n"); 165 aprint_normal(": ACPI Thermal Zone\n"); 166 167 sc->sc_first = true; 168 sc->sc_have_fan = false; 169 sc->sc_node = aa->aa_node; 170 sc->sc_zone.tzp = ATZ_TZP_RATE; 171 172 /* 173 * The _TZP (ACPI 4.0, p. 430) defines the recommended 174 * polling interval (in tenths of seconds). A value zero 175 * means that polling "should not be necessary". 176 */ 177 rv = acpi_eval_integer(sc->sc_node->ad_handle, "_TZP", &val); 178 179 if (ACPI_SUCCESS(rv) && val != 0) 180 sc->sc_zone.tzp = val; 181 182 aprint_debug_dev(self, "sample rate %d.%ds\n", 183 sc->sc_zone.tzp / 10, sc->sc_zone.tzp % 10); 184 185 sc->sc_zone_expire = ATZ_ZONE_EXPIRE / sc->sc_zone.tzp; 186 187 /* 188 * XXX: The fan controls seen here are available on 189 * some HP laptops. Arguably these should not 190 * appear in a generic device driver like this. 191 */ 192 if (acpitz_get_fanspeed(self, &sc->sc_zone.fanmin, 193 &sc->sc_zone.fanmax, &sc->sc_zone.fancurrent) == 0) 194 sc->sc_have_fan = true; 195 196 acpitz_get_zone(self, 1); 197 acpitz_get_status(self); 198 199 (void)pmf_device_register(self, NULL, NULL); 200 (void)acpi_register_notify(sc->sc_node, acpitz_notify_handler); 201 202 callout_init(&sc->sc_callout, CALLOUT_MPSAFE); 203 callout_setfunc(&sc->sc_callout, acpitz_tick, self); 204 205 acpitz_init_envsys(self); 206 207 callout_schedule(&sc->sc_callout, sc->sc_zone.tzp * hz / 10); 208 } 209 210 static int 211 acpitz_detach(device_t self, int flags) 212 { 213 struct acpitz_softc *sc = device_private(self); 214 ACPI_HANDLE hdl; 215 ACPI_BUFFER al; 216 ACPI_STATUS rv; 217 int i; 218 219 callout_halt(&sc->sc_callout, NULL); 220 callout_destroy(&sc->sc_callout); 221 222 pmf_device_deregister(self); 223 acpi_deregister_notify(sc->sc_node); 224 225 /* 226 * Although the device itself should not contain any power 227 * resources, we have possibly used the resources of active 228 * cooling devices. To unregister these, first fetch a fresh 229 * active cooling zone, and then detach the resources from 230 * the reference handles contained in the cooling zone. 231 */ 232 acpitz_get_zone(self, 0); 233 234 for (i = 0; i < ATZ_NLEVELS; i++) { 235 236 if (sc->sc_zone.al[i].Pointer == NULL) 237 continue; 238 239 al = sc->sc_zone.al[i]; 240 rv = acpi_eval_reference_handle(al.Pointer, &hdl); 241 242 if (ACPI_SUCCESS(rv)) 243 acpi_power_deregister(hdl); 244 245 ACPI_FREE(sc->sc_zone.al[i].Pointer); 246 } 247 248 if (sc->sc_sme != NULL) 249 sysmon_envsys_unregister(sc->sc_sme); 250 251 return 0; 252 } 253 254 static void 255 acpitz_get_zone_quiet(void *opaque) 256 { 257 acpitz_get_zone(opaque, 0); 258 } 259 260 static void 261 acpitz_get_status(void *opaque) 262 { 263 device_t dv = opaque; 264 struct acpitz_softc *sc = device_private(dv); 265 uint32_t tmp, active, fmin, fmax, fcurrent; 266 int changed, flags, i; 267 268 sc->sc_zone_expire--; 269 270 if (sc->sc_zone_expire <= 0) { 271 sc->sc_zone_expire = ATZ_ZONE_EXPIRE / sc->sc_zone.tzp; 272 273 ACPI_DEBUG_PRINT((ACPI_DB_INFO, 274 "%s: zone refetch forced\n", device_xname(dv))); 275 276 acpitz_get_zone(dv, 0); 277 } 278 279 if (acpitz_get_integer(dv, "_TMP", &tmp) != 0) 280 return; 281 282 sc->sc_zone.prevtmp = sc->sc_zone.tmp; 283 sc->sc_zone.tmp = tmp; 284 285 if (sc->sc_first != false) 286 sc->sc_zone.prevtmp = tmp; /* XXX: Sanity check? */ 287 288 if (acpitz_get_fanspeed(dv, &fmin, &fmax, &fcurrent) == 0) { 289 290 if (fcurrent != ATZ_TMP_INVALID) 291 sc->sc_zone.fancurrent = fcurrent; 292 } 293 294 sc->sc_temp_sensor.state = ENVSYS_SVALID; 295 sc->sc_temp_sensor.value_cur = ATZ2UKELVIN(sc->sc_zone.tmp); 296 297 sc->sc_fan_sensor.state = ENVSYS_SVALID; 298 sc->sc_fan_sensor.value_cur = sc->sc_zone.fancurrent; 299 300 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s: zone temperature is %s C\n", 301 device_xname(dv), acpitz_celcius_string(sc->sc_zone.tmp))); 302 303 /* 304 * XXX: Passive cooling is not yet supported. 305 */ 306 if ((sc->sc_flags & ATZ_F_PASSIVEONLY) != 0) 307 return; 308 309 /* 310 * As noted in ACPI 4.0 (p. 420), the temperature 311 * thresholds are conveyed in the optional _ACx 312 * object (x = 0 ... 9). The smaller the x, the 313 * greater the cooling level. We prefer to keep 314 * the highest cooling mode when in "active". 315 */ 316 active = ATZ_ACTIVE_NONE; 317 318 for (i = ATZ_NLEVELS - 1; i >= 0; i--) { 319 320 if (sc->sc_zone.ac[i] == ATZ_TMP_INVALID) 321 continue; 322 323 if (sc->sc_zone.ac[i] <= tmp) 324 active = i; 325 } 326 327 flags = sc->sc_flags & ~(ATZ_F_CRITICAL | ATZ_F_HOT | ATZ_F_PASSIVE); 328 329 if (sc->sc_zone.psv != ATZ_TMP_INVALID && tmp >= sc->sc_zone.psv) 330 flags |= ATZ_F_PASSIVE; 331 332 if (sc->sc_zone.hot != ATZ_TMP_INVALID && tmp >= sc->sc_zone.hot) 333 flags |= ATZ_F_HOT; 334 335 if (sc->sc_zone.crt != ATZ_TMP_INVALID && tmp >= sc->sc_zone.crt) 336 flags |= ATZ_F_CRITICAL; 337 338 if (flags != sc->sc_flags) { 339 340 changed = (sc->sc_flags ^ flags) & flags; 341 sc->sc_flags = flags; 342 343 if ((changed & ATZ_F_CRITICAL) != 0) { 344 sc->sc_temp_sensor.state = ENVSYS_SCRITOVER; 345 346 aprint_debug_dev(dv, "zone went critical, %s C\n", 347 acpitz_celcius_string(tmp)); 348 349 } else if ((changed & ATZ_F_HOT) != 0) { 350 sc->sc_temp_sensor.state = ENVSYS_SCRITOVER; 351 352 aprint_debug_dev(dv, "zone went hot, %s C\n", 353 acpitz_celcius_string(tmp)); 354 } 355 } 356 357 /* Power on the fans. */ 358 if (sc->sc_active != active) { 359 360 if (sc->sc_active != ATZ_ACTIVE_NONE) 361 acpitz_power_zone(sc, sc->sc_active, 0); 362 363 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s: active cooling " 364 "level %u\n", device_xname(dv), active)); 365 366 if (active != ATZ_ACTIVE_NONE) 367 acpitz_power_zone(sc, active, 1); 368 369 sc->sc_active = active; 370 } 371 } 372 373 static char * 374 acpitz_celcius_string(int dk) 375 { 376 static char buf[10]; 377 int dc; 378 379 dc = abs(dk - ATZ_ZEROC); 380 381 (void)snprintf(buf, sizeof(buf), "%s%d.%d", 382 (dk >= ATZ_ZEROC) ? "" : "-", dc / 10, dc % 10); 383 384 return buf; 385 } 386 387 static ACPI_STATUS 388 acpitz_switch_cooler(ACPI_OBJECT *obj, void *arg) 389 { 390 int flag, pwr_state; 391 ACPI_HANDLE cooler; 392 ACPI_STATUS rv; 393 394 /* 395 * The _ALx object is a package in which the elements 396 * are reference handles to an active cooling device 397 * (typically PNP0C0B, ACPI fan device). Try to turn 398 * on (or off) the power resources behind these handles 399 * to start (or terminate) the active cooling. 400 */ 401 flag = *(int *)arg; 402 pwr_state = (flag != 0) ? ACPI_STATE_D0 : ACPI_STATE_D3; 403 404 rv = acpi_eval_reference_handle(obj, &cooler); 405 406 if (ACPI_FAILURE(rv)) 407 return rv; 408 409 (void)acpi_power_set(cooler, pwr_state); 410 411 return AE_OK; 412 } 413 414 /* 415 * acpitz_power_zone: 416 * 417 * Power on or off the i:th part of the zone zone. 418 */ 419 static void 420 acpitz_power_zone(struct acpitz_softc *sc, int i, int on) 421 { 422 423 KASSERT(i >= 0 && i < ATZ_NLEVELS); 424 425 (void)acpi_foreach_package_object(sc->sc_zone.al[i].Pointer, 426 acpitz_switch_cooler, &on); 427 } 428 429 430 /* 431 * acpitz_power_off: 432 * 433 * Power off parts of the zone. 434 */ 435 static void 436 acpitz_power_off(struct acpitz_softc *sc) 437 { 438 int i; 439 440 for (i = 0 ; i < ATZ_NLEVELS; i++) { 441 442 if (sc->sc_zone.al[i].Pointer == NULL) 443 continue; 444 445 acpitz_power_zone(sc, i, 0); 446 } 447 448 sc->sc_active = ATZ_ACTIVE_NONE; 449 sc->sc_flags &= ~(ATZ_F_CRITICAL | ATZ_F_HOT | ATZ_F_PASSIVE); 450 } 451 452 static void 453 acpitz_get_zone(void *opaque, int verbose) 454 { 455 device_t dv = opaque; 456 struct acpitz_softc *sc = device_private(dv); 457 int comma, i, valid_levels; 458 ACPI_OBJECT *obj; 459 ACPI_STATUS rv; 460 char buf[5]; 461 462 if (sc->sc_first != true) { 463 acpitz_power_off(sc); 464 465 for (i = 0; i < ATZ_NLEVELS; i++) { 466 467 if (sc->sc_zone.al[i].Pointer != NULL) 468 ACPI_FREE(sc->sc_zone.al[i].Pointer); 469 470 sc->sc_zone.al[i].Pointer = NULL; 471 } 472 } 473 474 valid_levels = 0; 475 476 for (i = 0; i < ATZ_NLEVELS; i++) { 477 478 (void)snprintf(buf, sizeof(buf), "_AC%d", i); 479 480 if (acpitz_get_integer(dv, buf, &sc->sc_zone.ac[i])) 481 continue; 482 483 (void)snprintf(buf, sizeof(buf), "_AL%d", i); 484 485 rv = acpi_eval_struct(sc->sc_node->ad_handle, buf, 486 &sc->sc_zone.al[i]); 487 488 if (ACPI_FAILURE(rv)) { 489 sc->sc_zone.al[i].Pointer = NULL; 490 continue; 491 } 492 493 obj = sc->sc_zone.al[i].Pointer; 494 495 if (obj->Type != ACPI_TYPE_PACKAGE) { 496 sc->sc_zone.al[i].Pointer = NULL; 497 ACPI_FREE(obj); 498 continue; 499 } 500 501 if (sc->sc_first != false) 502 aprint_normal_dev(dv, "active cooling level %d: %sC\n", 503 i, acpitz_celcius_string(sc->sc_zone.ac[i])); 504 505 valid_levels++; 506 } 507 508 /* 509 * A brief summary (ACPI 4.0, section 11.4): 510 * 511 * _TMP : current temperature (in tenths of degrees) 512 * _CRT : critical trip-point at which to shutdown 513 * _HOT : critical trip-point at which to go to S4 514 * _PSV : passive cooling policy threshold 515 * _TC1 : thermal constant for passive cooling 516 * _TC2 : thermal constant for passive cooling 517 */ 518 (void)acpitz_get_integer(dv, "_TMP", &sc->sc_zone.tmp); 519 (void)acpitz_get_integer(dv, "_CRT", &sc->sc_zone.crt); 520 (void)acpitz_get_integer(dv, "_HOT", &sc->sc_zone.hot); 521 (void)acpitz_get_integer(dv, "_PSV", &sc->sc_zone.psv); 522 (void)acpitz_get_integer(dv, "_TC1", &sc->sc_zone.tc1); 523 (void)acpitz_get_integer(dv, "_TC2", &sc->sc_zone.tc2); 524 525 /* 526 * If _RTV is not present or present and zero, 527 * values are absolute (see ACPI 4.0, 425). 528 */ 529 acpitz_get_integer(dv, "_RTV", &sc->sc_zone.rtv); 530 531 if (sc->sc_zone.rtv == ATZ_TMP_INVALID) 532 sc->sc_zone.rtv = 0; 533 534 acpitz_sane_temp(&sc->sc_zone.tmp); 535 acpitz_sane_temp(&sc->sc_zone.crt); 536 acpitz_sane_temp(&sc->sc_zone.hot); 537 acpitz_sane_temp(&sc->sc_zone.psv); 538 539 if (verbose != 0) { 540 comma = 0; 541 542 aprint_verbose_dev(dv, ""); 543 544 if (sc->sc_zone.crt != ATZ_TMP_INVALID) { 545 aprint_verbose("critical %s C", 546 acpitz_celcius_string(sc->sc_zone.crt)); 547 comma = 1; 548 } 549 550 if (sc->sc_zone.hot != ATZ_TMP_INVALID) { 551 aprint_verbose("%shot %s C", comma ? ", " : "", 552 acpitz_celcius_string(sc->sc_zone.hot)); 553 comma = 1; 554 } 555 556 if (sc->sc_zone.psv != ATZ_TMP_INVALID) { 557 aprint_verbose("%spassive %s C", comma ? ", " : "", 558 acpitz_celcius_string(sc->sc_zone.psv)); 559 comma = 1; 560 } 561 562 if (valid_levels == 0) { 563 sc->sc_flags |= ATZ_F_PASSIVEONLY; 564 565 if (sc->sc_first != false) 566 aprint_verbose("%spassive cooling", comma ? 567 ", " : ""); 568 } 569 570 aprint_verbose("\n"); 571 } 572 573 for (i = 0; i < ATZ_NLEVELS; i++) 574 acpitz_sane_temp(&sc->sc_zone.ac[i]); 575 576 acpitz_power_off(sc); 577 sc->sc_first = false; 578 } 579 580 static void 581 acpitz_notify_handler(ACPI_HANDLE hdl, uint32_t notify, void *opaque) 582 { 583 ACPI_OSD_EXEC_CALLBACK func = NULL; 584 device_t dv = opaque; 585 586 switch (notify) { 587 588 case ACPI_NOTIFY_TZ_ZONE: 589 func = acpitz_get_status; 590 break; 591 592 case ACPI_NOTIFY_TZ_TRIP: 593 case ACPI_NOTIFY_TZ_DEVLIST: 594 func = acpitz_get_zone_quiet; 595 break; 596 597 default: 598 aprint_debug_dev(dv, "unknown notify 0x%02X\n", notify); 599 return; 600 } 601 602 (void)AcpiOsExecute(OSL_NOTIFY_HANDLER, func, dv); 603 } 604 605 static void 606 acpitz_sane_temp(uint32_t *tmp) 607 { 608 /* Sane temperatures are beteen 0 and 150 C. */ 609 if (*tmp < ATZ_ZEROC || *tmp > ATZ_ZEROC + 1500) 610 *tmp = ATZ_TMP_INVALID; 611 } 612 613 static int 614 acpitz_get_integer(device_t dv, const char *cm, uint32_t *val) 615 { 616 struct acpitz_softc *sc = device_private(dv); 617 ACPI_INTEGER tmp; 618 ACPI_STATUS rv; 619 620 rv = acpi_eval_integer(sc->sc_node->ad_handle, cm, &tmp); 621 622 if (ACPI_FAILURE(rv)) { 623 *val = ATZ_TMP_INVALID; 624 625 ACPI_DEBUG_PRINT((ACPI_DB_DEBUG_OBJECT, 626 "%s: failed to evaluate %s: %s\n", 627 device_xname(dv), cm, AcpiFormatException(rv))); 628 629 return 1; 630 } 631 632 *val = tmp; 633 634 return 0; 635 } 636 637 static int 638 acpitz_get_fanspeed(device_t dv, 639 uint32_t *fanmin, uint32_t *fanmax, uint32_t *fancurrent) 640 { 641 struct acpitz_softc *sc = device_private(dv); 642 ACPI_INTEGER fmin, fmax, fcurr; 643 ACPI_HANDLE handle; 644 ACPI_STATUS rv; 645 int rc = 0; 646 647 handle = sc->sc_node->ad_handle; 648 649 rv = acpi_eval_integer(handle, "FMIN", &fmin); 650 651 if (ACPI_FAILURE(rv)) { 652 fmin = ATZ_TMP_INVALID; 653 rc = 1; 654 } 655 656 rv = acpi_eval_integer(handle, "FMAX", &fmax); 657 658 if (ACPI_FAILURE(rv)) { 659 fmax = ATZ_TMP_INVALID; 660 rc = 1; 661 } 662 rv = acpi_eval_integer(handle, "FRSP", &fcurr); 663 664 if (ACPI_FAILURE(rv)) { 665 fcurr = ATZ_TMP_INVALID; 666 rc = 1; 667 } 668 669 if (fanmin != NULL) 670 *fanmin = fmin; 671 672 if (fanmax != NULL) 673 *fanmax = fmax; 674 675 if (fancurrent != NULL) 676 *fancurrent = fcurr; 677 678 return rc; 679 } 680 681 #ifdef notyet 682 static ACPI_STATUS 683 acpitz_set_fanspeed(device_t dv, uint32_t fanspeed) 684 { 685 struct acpitz_softc *sc = device_private(dv); 686 ACPI_HANDLE handle; 687 ACPI_STATUS rv; 688 689 handle = sc->sc_node->ad_handle; 690 691 rv = acpi_eval_set_integer(handle, "FSSP", fanspeed); 692 693 if (ACPI_FAILURE(rv)) 694 aprint_debug_dev(dv, "failed to set fan speed to %u RPM: %s\n", 695 fanspeed, AcpiFormatException(rv)); 696 697 return rv; 698 } 699 #endif 700 701 static void 702 acpitz_tick(void *opaque) 703 { 704 device_t dv = opaque; 705 struct acpitz_softc *sc = device_private(dv); 706 707 (void)AcpiOsExecute(OSL_NOTIFY_HANDLER, acpitz_get_status, dv); 708 709 callout_schedule(&sc->sc_callout, sc->sc_zone.tzp * hz / 10); 710 } 711 712 static void 713 acpitz_init_envsys(device_t dv) 714 { 715 const int flags = ENVSYS_FMONLIMITS | ENVSYS_FMONNOTSUPP; 716 struct acpitz_softc *sc = device_private(dv); 717 718 sc->sc_sme = sysmon_envsys_create(); 719 720 sc->sc_sme->sme_cookie = sc; 721 sc->sc_sme->sme_name = device_xname(dv); 722 sc->sc_sme->sme_flags = SME_DISABLE_REFRESH; 723 sc->sc_sme->sme_get_limits = acpitz_get_limits; 724 725 sc->sc_temp_sensor.flags = flags; 726 sc->sc_temp_sensor.units = ENVSYS_STEMP; 727 728 (void)strlcpy(sc->sc_temp_sensor.desc, "temperature", 729 sizeof(sc->sc_temp_sensor.desc)); 730 731 if (sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_temp_sensor)) 732 goto out; 733 734 if (sc->sc_have_fan != false) { 735 736 sc->sc_fan_sensor.flags = flags; 737 sc->sc_fan_sensor.units = ENVSYS_SFANRPM; 738 739 (void)strlcpy(sc->sc_fan_sensor.desc, 740 "FAN", sizeof(sc->sc_fan_sensor.desc)); 741 742 /* Ignore error because fan sensor is optional. */ 743 (void)sysmon_envsys_sensor_attach(sc->sc_sme, 744 &sc->sc_fan_sensor); 745 } 746 747 if (sysmon_envsys_register(sc->sc_sme) == 0) 748 return; 749 750 out: 751 aprint_error_dev(dv, "unable to register with sysmon\n"); 752 753 sysmon_envsys_destroy(sc->sc_sme); 754 sc->sc_sme = NULL; 755 } 756 757 static void 758 acpitz_get_limits(struct sysmon_envsys *sme, envsys_data_t *edata, 759 sysmon_envsys_lim_t *limits, uint32_t *props) 760 { 761 struct acpitz_softc *sc = sme->sme_cookie; 762 int i; 763 764 switch (edata->units) { 765 case ENVSYS_STEMP: 766 *props = 0; 767 if (sc->sc_zone.hot != ATZ_TMP_INVALID) { 768 *props |= PROP_CRITMAX; 769 limits->sel_critmax = ATZ2UKELVIN(sc->sc_zone.hot); 770 } else if (sc->sc_zone.crt != ATZ_TMP_INVALID) { 771 *props |= PROP_CRITMAX; 772 limits->sel_critmax = ATZ2UKELVIN(sc->sc_zone.crt); 773 } 774 for (i = 0; i < ATZ_NLEVELS; i++) { 775 if (sc->sc_zone.ac[i] != ATZ_TMP_INVALID) { 776 limits->sel_warnmax = 777 ATZ2UKELVIN(sc->sc_zone.ac[i]); 778 *props |= PROP_WARNMAX; 779 break; 780 } 781 } 782 break; 783 784 case ENVSYS_SFANRPM: 785 *props = 0; 786 if (sc->sc_zone.fanmin != ATZ_TMP_INVALID) { 787 *props |= PROP_WARNMIN; 788 limits->sel_warnmin = sc->sc_zone.fanmin; 789 sc->sc_fan_sensor.flags |= ENVSYS_FVALID_MIN; 790 } 791 if (sc->sc_zone.fanmax != ATZ_TMP_INVALID) { 792 *props |= PROP_WARNMAX; 793 limits->sel_warnmax = sc->sc_zone.fanmax; 794 sc->sc_fan_sensor.flags |= ENVSYS_FVALID_MAX; 795 } 796 break; 797 } 798 } 799