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