1 /*- 2 * Copyright (c) 2000, 2001 Michael Smith 3 * Copyright (c) 2000 BSDi 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, 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 * $FreeBSD$ 28 */ 29 30 #include "opt_acpi.h" 31 #include <sys/param.h> 32 #include <sys/kernel.h> 33 #include <sys/bus.h> 34 #include <sys/reboot.h> 35 #include <sys/sysctl.h> 36 37 #include "acpi.h" 38 39 #include <dev/acpica/acpivar.h> 40 41 /* 42 * Hooks for the ACPI CA debugging infrastructure 43 */ 44 #define _COMPONENT ACPI_THERMAL 45 MODULE_NAME("THERMAL") 46 47 #define TZ_ZEROC 2732 48 #define TZ_KELVTOC(x) (((x) - TZ_ZEROC) / 10), (((x) - TZ_ZEROC) % 10) 49 50 #define TZ_DPRINT(dev, x...) do { \ 51 if (acpi_get_verbose(acpi_device_get_parent_softc(dev))) \ 52 device_printf(dev, x); \ 53 } while (0) 54 55 #define TZ_NOTIFY_TEMPERATURE 0x80 56 #define TZ_NOTIFY_DEVICES 0x81 57 #define TZ_NOTIFY_LEVELS 0x82 58 59 #define TZ_POLLRATE (hz * 10) /* every ten seconds */ 60 61 #define TZ_NUMLEVELS 10 /* defined by ACPI spec */ 62 struct acpi_tz_zone { 63 int ac[TZ_NUMLEVELS]; 64 ACPI_BUFFER al[TZ_NUMLEVELS]; 65 int crt; 66 int hot; 67 ACPI_BUFFER psl; 68 int psv; 69 int tc1; 70 int tc2; 71 int tsp; 72 int tzp; 73 }; 74 75 76 struct acpi_tz_softc { 77 device_t tz_dev; /* device handle */ 78 ACPI_HANDLE tz_handle; /* thermal zone handle */ 79 struct callout_handle tz_timeout; /* poll routine handle */ 80 int tz_temperature; /* current temperature */ 81 int tz_active; /* current active cooling */ 82 #define TZ_ACTIVE_NONE -1 83 int tz_requested; /* user-requested minimum active cooling */ 84 int tz_thflags; /* current temperature-related flags */ 85 #define TZ_THFLAG_NONE 0 86 #define TZ_THFLAG_PSV (1<<0) 87 #define TZ_THFLAG_HOT (1<<2) 88 #define TZ_THFLAG_CRT (1<<3) 89 int tz_flags; 90 #define TZ_FLAG_NO_SCP (1<<0) /* no _SCP method */ 91 #define TZ_FLAG_GETPROFILE (1<<1) /* fetch powerprofile in timeout */ 92 struct timespec tz_cooling_started; /* current cooling starting time */ 93 94 struct sysctl_ctx_list tz_sysctl_ctx; /* sysctl tree */ 95 struct sysctl_oid *tz_sysctl_tree; 96 97 struct acpi_tz_zone tz_zone; /* thermal zone parameters */ 98 }; 99 100 static int acpi_tz_probe(device_t dev); 101 static int acpi_tz_attach(device_t dev); 102 static int acpi_tz_establish(struct acpi_tz_softc *sc); 103 static void acpi_tz_monitor(struct acpi_tz_softc *sc); 104 static void acpi_tz_all_off(struct acpi_tz_softc *sc); 105 static void acpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg); 106 static void acpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg); 107 static void acpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data); 108 static void acpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what); 109 static int acpi_tz_active_sysctl(SYSCTL_HANDLER_ARGS); 110 static void acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context); 111 static void acpi_tz_timeout(void *arg); 112 static void acpi_tz_powerprofile(void *arg); 113 114 static device_method_t acpi_tz_methods[] = { 115 /* Device interface */ 116 DEVMETHOD(device_probe, acpi_tz_probe), 117 DEVMETHOD(device_attach, acpi_tz_attach), 118 119 {0, 0} 120 }; 121 122 static driver_t acpi_tz_driver = { 123 "acpi_tz", 124 acpi_tz_methods, 125 sizeof(struct acpi_tz_softc), 126 }; 127 128 devclass_t acpi_tz_devclass; 129 DRIVER_MODULE(acpi_tz, acpi, acpi_tz_driver, acpi_tz_devclass, 0, 0); 130 131 static struct sysctl_ctx_list acpi_tz_sysctl_ctx; 132 static struct sysctl_oid *acpi_tz_sysctl_tree; 133 134 static int acpi_tz_min_runtime = 0;/* minimum cooling run time */ 135 136 /* 137 * Match an ACPI thermal zone. 138 */ 139 static int 140 acpi_tz_probe(device_t dev) 141 { 142 int result; 143 144 ACPI_LOCK; 145 146 /* no FUNCTION_TRACE - too noisy */ 147 148 if ((acpi_get_type(dev) == ACPI_TYPE_THERMAL) && 149 !acpi_disabled("thermal")) { 150 device_set_desc(dev, "thermal zone"); 151 result = -10; 152 } else { 153 result = ENXIO; 154 } 155 ACPI_UNLOCK; 156 return(result); 157 } 158 159 /* 160 * Attach to an ACPI thermal zone. 161 */ 162 static int 163 acpi_tz_attach(device_t dev) 164 { 165 struct acpi_tz_softc *sc; 166 struct acpi_softc *acpi_sc; 167 int error; 168 char oidname[8]; 169 int i; 170 171 FUNCTION_TRACE(__func__); 172 173 ACPI_LOCK; 174 175 sc = device_get_softc(dev); 176 sc->tz_dev = dev; 177 sc->tz_handle = acpi_get_handle(dev); 178 sc->tz_requested = TZ_ACTIVE_NONE; 179 180 /* 181 * Parse the current state of the thermal zone and build control 182 * structures. 183 */ 184 if ((error = acpi_tz_establish(sc)) != 0) 185 goto out; 186 187 /* 188 * Register for any Notify events sent to this zone. 189 */ 190 AcpiInstallNotifyHandler(sc->tz_handle, ACPI_DEVICE_NOTIFY, 191 acpi_tz_notify_handler, sc); 192 193 /* 194 * Create our sysctl nodes. 195 * 196 * XXX we need a mechanism for adding nodes under ACPI. 197 */ 198 if (device_get_unit(dev) == 0) { 199 acpi_sc = acpi_device_get_parent_softc(dev); 200 sysctl_ctx_init(&acpi_tz_sysctl_ctx); 201 acpi_tz_sysctl_tree = SYSCTL_ADD_NODE(&acpi_tz_sysctl_ctx, 202 SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), 203 OID_AUTO, "thermal", CTLFLAG_RD, 0, ""); 204 SYSCTL_ADD_INT(&acpi_tz_sysctl_ctx, 205 SYSCTL_CHILDREN(acpi_tz_sysctl_tree), 206 OID_AUTO, "min_runtime", CTLFLAG_RD | CTLFLAG_RW, 207 &acpi_tz_min_runtime, 0, "minimum cooling run time in sec"); 208 } 209 sysctl_ctx_init(&sc->tz_sysctl_ctx); 210 sprintf(oidname, "tz%d", device_get_unit(dev)); 211 sc->tz_sysctl_tree = SYSCTL_ADD_NODE(&sc->tz_sysctl_ctx, 212 SYSCTL_CHILDREN(acpi_tz_sysctl_tree), OID_AUTO, 213 oidname, CTLFLAG_RD, 0, ""); 214 SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), 215 OID_AUTO, "temperature", CTLFLAG_RD, 216 &sc->tz_temperature, 0, "current thermal zone temperature"); 217 SYSCTL_ADD_PROC(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), 218 OID_AUTO, "active", CTLTYPE_INT | CTLFLAG_RW, 219 sc, 0, acpi_tz_active_sysctl, "I", ""); 220 221 SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), 222 OID_AUTO, "thermal_flags", CTLFLAG_RD, 223 &sc->tz_thflags, 0, "thermal zone flags"); 224 SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), 225 OID_AUTO, "_PSV", CTLFLAG_RD, 226 &sc->tz_zone.psv, 0, ""); 227 SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), 228 OID_AUTO, "_HOT", CTLFLAG_RD, 229 &sc->tz_zone.hot, 0, ""); 230 SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), 231 OID_AUTO, "_CRT", CTLFLAG_RD, 232 &sc->tz_zone.crt, 0, ""); 233 for (i = 0; i < TZ_NUMLEVELS; i++) { 234 sprintf(oidname, "_AC%d", i); 235 SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), 236 OID_AUTO, oidname, CTLFLAG_RD, 237 &sc->tz_zone.ac[i], 0, ""); 238 } 239 240 /* 241 * Register our power profile event handler, and flag it for a manual 242 * invocation by our timeout. We defer it like this so that the rest 243 * of the subsystem has time to come up. 244 */ 245 EVENTHANDLER_REGISTER(powerprofile_change, acpi_tz_powerprofile, sc, 0); 246 sc->tz_flags |= TZ_FLAG_GETPROFILE; 247 248 /* 249 * Don't bother evaluating/printing the temperature at this point; 250 * on many systems it'll be bogus until the EC is running. 251 */ 252 253 out: 254 ACPI_UNLOCK; 255 256 /* 257 * Start the timeout routine, with enough delay for the rest of the 258 * subsystem to come up. 259 */ 260 sc->tz_timeout = timeout(acpi_tz_timeout, sc, TZ_POLLRATE); 261 262 return_VALUE(error); 263 } 264 265 /* 266 * Parse the current state of this thermal zone and set up to use it. 267 * 268 * Note that we may have previous state, which will have to be discarded. 269 */ 270 static int 271 acpi_tz_establish(struct acpi_tz_softc *sc) 272 { 273 ACPI_OBJECT *obj; 274 int i; 275 char nbuf[8]; 276 277 FUNCTION_TRACE(__func__); 278 279 ACPI_ASSERTLOCK; 280 281 /* 282 * Power everything off and erase any existing state. 283 */ 284 acpi_tz_all_off(sc); 285 for (i = 0; i < TZ_NUMLEVELS; i++) 286 if (sc->tz_zone.al[i].Pointer != NULL) 287 AcpiOsFree(sc->tz_zone.al[i].Pointer); 288 if (sc->tz_zone.psl.Pointer != NULL) 289 AcpiOsFree(sc->tz_zone.psl.Pointer); 290 bzero(&sc->tz_zone, sizeof(sc->tz_zone)); 291 292 /* 293 * Evaluate thermal zone parameters. 294 */ 295 for (i = 0; i < TZ_NUMLEVELS; i++) { 296 sprintf(nbuf, "_AC%d", i); 297 acpi_tz_getparam(sc, nbuf, &sc->tz_zone.ac[i]); 298 sprintf(nbuf, "_AL%d", i); 299 acpi_EvaluateIntoBuffer(sc->tz_handle, nbuf, NULL, &sc->tz_zone.al[i]); 300 obj = (ACPI_OBJECT *)sc->tz_zone.al[i].Pointer; 301 if (obj != NULL) { 302 /* should be a package containing a list of power objects */ 303 if (obj->Type != ACPI_TYPE_PACKAGE) { 304 device_printf(sc->tz_dev, "%s has unknown object type %d, rejecting\n", 305 nbuf, obj->Type); 306 return_VALUE(ENXIO); 307 } 308 } 309 } 310 acpi_tz_getparam(sc, "_CRT", &sc->tz_zone.crt); 311 acpi_tz_getparam(sc, "_HOT", &sc->tz_zone.hot); 312 acpi_EvaluateIntoBuffer(sc->tz_handle, "_PSL", NULL, &sc->tz_zone.psl); 313 acpi_tz_getparam(sc, "_PSV", &sc->tz_zone.psv); 314 acpi_tz_getparam(sc, "_TC1", &sc->tz_zone.tc1); 315 acpi_tz_getparam(sc, "_TC2", &sc->tz_zone.tc2); 316 acpi_tz_getparam(sc, "_TSP", &sc->tz_zone.tsp); 317 acpi_tz_getparam(sc, "_TZP", &sc->tz_zone.tzp); 318 319 /* 320 * Sanity-check the values we've been given. 321 * 322 * XXX what do we do about systems that give us the same value for 323 * more than one of these setpoints? 324 */ 325 acpi_tz_sanity(sc, &sc->tz_zone.crt, "_CRT"); 326 acpi_tz_sanity(sc, &sc->tz_zone.hot, "_HOT"); 327 acpi_tz_sanity(sc, &sc->tz_zone.psv, "_PSV"); 328 for (i = 0; i < TZ_NUMLEVELS; i++) 329 acpi_tz_sanity(sc, &sc->tz_zone.ac[i], "_ACx"); 330 331 /* 332 * Power off everything that we've just been given. 333 */ 334 acpi_tz_all_off(sc); 335 336 return_VALUE(0); 337 } 338 339 static char *aclevel_string[] = { 340 "NONE", "_AC0", "_AC1", "_AC2", "_AC3", "_AC4", 341 "_AC5", "_AC6", "_AC7", "_AC8", "_AC9" }; 342 343 static __inline const char * 344 acpi_tz_aclevel_string(int active) 345 { 346 if (active < -1 || active >= TZ_NUMLEVELS) { 347 return (aclevel_string[0]); 348 } 349 350 return (aclevel_string[active+1]); 351 } 352 353 /* 354 * Evaluate the condition of a thermal zone, take appropriate actions. 355 */ 356 static void 357 acpi_tz_monitor(struct acpi_tz_softc *sc) 358 { 359 int temp; 360 int i; 361 int newactive, newflags; 362 struct timespec curtime; 363 364 FUNCTION_TRACE(__func__); 365 366 ACPI_ASSERTLOCK; 367 368 /* 369 * Get the current temperature. 370 */ 371 if ((acpi_EvaluateInteger(sc->tz_handle, "_TMP", &temp)) != AE_OK) { 372 device_printf(sc->tz_dev, "error fetching current temperature\n"); 373 /* XXX disable zone? go to max cooling? */ 374 return_VOID; 375 } 376 ACPI_DEBUG_PRINT((ACPI_DB_VALUES, "got %d.%dC\n", TZ_KELVTOC(temp))); 377 sc->tz_temperature = temp; 378 379 /* 380 * Work out what we ought to be doing right now. 381 * 382 * Note that the _ACx levels sort from hot to cold. 383 */ 384 newactive = TZ_ACTIVE_NONE; 385 for (i = TZ_NUMLEVELS - 1; i >= 0; i--) { 386 if ((sc->tz_zone.ac[i] != -1) && (temp >= sc->tz_zone.ac[i])) { 387 newactive = i; 388 if (sc->tz_active != newactive) { 389 TZ_DPRINT(sc->tz_dev, 390 "_AC%d: temperature %d.%d >= setpoint %d.%d\n", i, 391 TZ_KELVTOC(temp), TZ_KELVTOC(sc->tz_zone.ac[i])); 392 getnanotime(&sc->tz_cooling_started); 393 } 394 } 395 } 396 397 /* 398 * We are going to get _ACx level down (colder side), but give a guaranteed 399 * minimum cooling run time if requested. 400 */ 401 if (acpi_tz_min_runtime > 0 && sc->tz_active != TZ_ACTIVE_NONE && 402 (newactive == TZ_ACTIVE_NONE || newactive > sc->tz_active)) { 403 getnanotime(&curtime); 404 timespecsub(&curtime, &sc->tz_cooling_started); 405 if (curtime.tv_sec < acpi_tz_min_runtime) { 406 newactive = sc->tz_active; 407 } 408 } 409 410 /* handle user override of active mode */ 411 if (sc->tz_requested > newactive) 412 newactive = sc->tz_requested; 413 414 /* update temperature-related flags */ 415 newflags = TZ_THFLAG_NONE; 416 if ((sc->tz_zone.psv != -1) && (temp >= sc->tz_zone.psv)) 417 newflags |= TZ_THFLAG_PSV; 418 if ((sc->tz_zone.hot != -1) && (temp >= sc->tz_zone.hot)) 419 newflags |= TZ_THFLAG_HOT; 420 if ((sc->tz_zone.crt != -1) && (temp >= sc->tz_zone.crt)) 421 newflags |= TZ_THFLAG_CRT; 422 423 /* 424 * If the active cooling state has changed, we have to switch things. 425 */ 426 if (newactive != sc->tz_active) { 427 428 /* turn off the cooling devices that are on, if any are */ 429 if (sc->tz_active != TZ_ACTIVE_NONE) 430 acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_zone.al[sc->tz_active].Pointer, 431 acpi_tz_switch_cooler_off, sc); 432 433 /* turn on cooling devices that are required, if any are */ 434 if (newactive != TZ_ACTIVE_NONE) 435 acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_zone.al[newactive].Pointer, 436 acpi_tz_switch_cooler_on, sc); 437 TZ_DPRINT(sc->tz_dev, "switched from %s to %s: %d.%dC\n", 438 acpi_tz_aclevel_string(sc->tz_active), 439 acpi_tz_aclevel_string(newactive), TZ_KELVTOC(temp)); 440 sc->tz_active = newactive; 441 } 442 443 /* 444 * XXX (de)activate any passive cooling that may be required. 445 */ 446 447 /* 448 * If we have just become _HOT or _CRT, warn the user. 449 * 450 * We should actually shut down at this point, but it's not clear 451 * that some systems don't actually map _CRT to the same value as _AC0. 452 */ 453 if ((newflags & (TZ_THFLAG_HOT | TZ_THFLAG_CRT)) && 454 !(sc->tz_thflags & (TZ_THFLAG_HOT | TZ_THFLAG_CRT))) { 455 device_printf(sc->tz_dev, "WARNING - current temperature (%d.%dC) exceeds system limits\n", 456 TZ_KELVTOC(sc->tz_temperature), sc->tz_temperature); 457 /* shutdown_nice(RB_POWEROFF);*/ 458 } 459 sc->tz_thflags = newflags; 460 461 return_VOID; 462 } 463 464 /* 465 * Turn off all the cooling devices. 466 */ 467 static void 468 acpi_tz_all_off(struct acpi_tz_softc *sc) 469 { 470 int i; 471 472 FUNCTION_TRACE(__func__); 473 474 ACPI_ASSERTLOCK; 475 476 /* 477 * Scan all the _ALx objects, and turn them all off. 478 */ 479 for (i = 0; i < TZ_NUMLEVELS; i++) { 480 if (sc->tz_zone.al[i].Pointer == NULL) 481 continue; 482 acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_zone.al[i].Pointer, 483 acpi_tz_switch_cooler_off, sc); 484 } 485 486 /* 487 * XXX revert any passive-cooling options. 488 */ 489 490 sc->tz_active = TZ_ACTIVE_NONE; 491 sc->tz_thflags = TZ_THFLAG_NONE; 492 return_VOID; 493 } 494 495 /* 496 * Given an object, verify that it's a reference to a device of some sort, 497 * and try to switch it off. 498 */ 499 static void 500 acpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg) 501 { 502 ACPI_HANDLE cooler; 503 504 FUNCTION_TRACE(__func__); 505 506 ACPI_ASSERTLOCK; 507 508 switch(obj->Type) { 509 case ACPI_TYPE_STRING: 510 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to turn %s off\n", obj->String.Pointer)); 511 512 /* 513 * Find the handle for the device and turn it off. 514 * The String object here seems to contain a fully-qualified path, so we 515 * don't have to search for it in our parents. 516 * 517 * XXX This may not always be the case. 518 */ 519 if (AcpiGetHandle(NULL, obj->String.Pointer, &cooler) == AE_OK) 520 acpi_pwr_switch_consumer(cooler, ACPI_STATE_D3); 521 break; 522 523 default: 524 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to handle unsupported object type %d\n", 525 obj->Type)); 526 break; 527 } 528 return_VOID; 529 } 530 531 /* 532 * Given an object, verify that it's a reference to a device of some sort, 533 * and try to switch it on. 534 * 535 * XXX replication of off/on function code is bad, mmmkay? 536 */ 537 static void 538 acpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg) 539 { 540 struct acpi_tz_softc *sc = (struct acpi_tz_softc *)arg; 541 ACPI_HANDLE cooler; 542 ACPI_STATUS status; 543 544 FUNCTION_TRACE(__func__); 545 546 ACPI_ASSERTLOCK; 547 548 switch(obj->Type) { 549 case ACPI_TYPE_STRING: 550 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to turn %s on\n", obj->String.Pointer)); 551 552 /* 553 * Find the handle for the device and turn it off. 554 * The String object here seems to contain a fully-qualified path, so we 555 * don't have to search for it in our parents. 556 * 557 * XXX This may not always be the case. 558 */ 559 if (AcpiGetHandle(NULL, obj->String.Pointer, &cooler) == AE_OK) { 560 if (ACPI_FAILURE(status = acpi_pwr_switch_consumer(cooler, ACPI_STATE_D0))) { 561 device_printf(sc->tz_dev, "failed to activate %s - %s\n", 562 obj->String.Pointer, AcpiFormatException(status)); 563 } 564 } else { 565 device_printf(sc->tz_dev, "couldn't find %s\n", obj->String.Pointer); 566 } 567 break; 568 569 default: 570 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "called to handle unsupported object type %d\n", 571 obj->Type)); 572 break; 573 } 574 return_VOID; 575 } 576 577 /* 578 * Read/debug-print a parameter, default it to -1. 579 */ 580 static void 581 acpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data) 582 { 583 584 FUNCTION_TRACE(__func__); 585 586 ACPI_ASSERTLOCK; 587 588 if (acpi_EvaluateInteger(sc->tz_handle, node, data) != AE_OK) { 589 *data = -1; 590 } else { 591 ACPI_DEBUG_PRINT((ACPI_DB_VALUES, "%s.%s = %d\n", acpi_name(sc->tz_handle), 592 node, *data)); 593 } 594 return_VOID; 595 } 596 597 /* 598 * Sanity-check a temperature value. Assume that setpoints 599 * should be between 0C and 150C. 600 */ 601 static void 602 acpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what) 603 { 604 if ((*val != -1) && ((*val < TZ_ZEROC) || (*val > (TZ_ZEROC + 1500)))) { 605 device_printf(sc->tz_dev, "%s value is absurd, ignored (%d.%dC)\n", 606 what, TZ_KELVTOC(*val)); 607 *val = -1; 608 } 609 } 610 611 /* 612 * Respond to a sysctl on the active state node. 613 */ 614 static int 615 acpi_tz_active_sysctl(SYSCTL_HANDLER_ARGS) 616 { 617 struct acpi_tz_softc *sc; 618 int active; 619 int error; 620 621 ACPI_LOCK; 622 623 sc = (struct acpi_tz_softc *)oidp->oid_arg1; 624 active = sc->tz_active; 625 error = sysctl_handle_int(oidp, &active, 0, req); 626 627 /* error or no new value */ 628 if ((error != 0) || (req->newptr == NULL)) 629 goto out; 630 631 /* range check */ 632 if ((active < -1) || (active >= TZ_NUMLEVELS)) { 633 error = EINVAL; 634 goto out; 635 } 636 637 /* set new preferred level and re-switch */ 638 sc->tz_requested = active; 639 acpi_tz_monitor(sc); 640 641 out: 642 ACPI_UNLOCK; 643 return(error); 644 } 645 646 /* 647 * Respond to a Notify event sent to the zone. 648 */ 649 static void 650 acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context) 651 { 652 struct acpi_tz_softc *sc = (struct acpi_tz_softc *)context; 653 654 FUNCTION_TRACE(__func__); 655 656 ACPI_ASSERTLOCK; 657 658 switch(notify) { 659 case TZ_NOTIFY_TEMPERATURE: 660 /* temperature change occurred */ 661 AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_monitor, sc); 662 break; 663 case TZ_NOTIFY_DEVICES: 664 case TZ_NOTIFY_LEVELS: 665 /* zone devices/setpoints changed */ 666 AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_establish, sc); 667 break; 668 default: 669 device_printf(sc->tz_dev, "unknown Notify event 0x%x\n", notify); 670 break; 671 } 672 return_VOID; 673 } 674 675 /* 676 * Poll the thermal zone. 677 */ 678 static void 679 acpi_tz_timeout(void *arg) 680 { 681 struct acpi_tz_softc *sc = (struct acpi_tz_softc *)arg; 682 683 /* do we need to get the power profile settings? */ 684 if (sc->tz_flags & TZ_FLAG_GETPROFILE) { 685 acpi_tz_powerprofile(arg); 686 sc->tz_flags &= ~TZ_FLAG_GETPROFILE; 687 } 688 689 ACPI_LOCK; 690 691 /* check temperature, take action */ 692 AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_monitor, sc); 693 694 /* XXX passive cooling actions? */ 695 696 /* re-register ourself */ 697 sc->tz_timeout = timeout(acpi_tz_timeout, sc, TZ_POLLRATE); 698 699 ACPI_UNLOCK; 700 } 701 702 /* 703 * System power profile may have changed; fetch and notify the 704 * thermal zone accordingly. 705 * 706 * Since this can be called from an arbitrary eventhandler, it needs 707 * to get the ACPI lock itself. 708 */ 709 static void 710 acpi_tz_powerprofile(void *arg) 711 { 712 ACPI_OBJECT_LIST args; 713 ACPI_OBJECT obj; 714 ACPI_STATUS status; 715 struct acpi_tz_softc *sc = (struct acpi_tz_softc *)arg; 716 717 ACPI_LOCK; 718 719 /* check that we haven't decided there's no _SCP method */ 720 if (!(sc->tz_flags & TZ_FLAG_NO_SCP)) { 721 722 /* call _SCP to set the new profile */ 723 obj.Type = ACPI_TYPE_INTEGER; 724 obj.Integer.Value = (powerprofile_get_state() == POWERPROFILE_PERFORMANCE) ? 0 : 1; 725 args.Count = 1; 726 args.Pointer = &obj; 727 if (ACPI_FAILURE(status = AcpiEvaluateObject(sc->tz_handle, "_SCP", &args, NULL))) { 728 if (status != AE_NOT_FOUND) 729 device_printf(sc->tz_dev, "can't evaluate %s._SCP - %s\n", acpi_name(sc->tz_handle), 730 AcpiFormatException(status)); 731 sc->tz_flags |= TZ_FLAG_NO_SCP; 732 } else { 733 /* we have to re-evaluate the entire zone now */ 734 AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_establish, sc); 735 } 736 } 737 ACPI_UNLOCK; 738 } 739 740