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