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_NOTIFY_TEMPERATURE 0x80 51 #define TZ_NOTIFY_DEVICES 0x81 52 #define TZ_NOTIFY_LEVELS 0x82 53 54 #define TZ_POLLRATE (hz * 10) /* every ten seconds */ 55 56 #define TZ_NUMLEVELS 10 /* defined by ACPI spec */ 57 struct acpi_tz_state { 58 int ac[TZ_NUMLEVELS]; 59 ACPI_BUFFER al[TZ_NUMLEVELS]; 60 int crt; 61 int hot; 62 ACPI_BUFFER psl; 63 int psv; 64 int tc1; 65 int tc2; 66 int tsp; 67 int tzp; 68 }; 69 70 71 struct acpi_tz_softc { 72 device_t tz_dev; 73 ACPI_HANDLE tz_handle; 74 struct callout_handle tz_timeout; 75 int tz_temperature; 76 int tz_active; 77 #define TZ_ACTIVE_NONE -1 78 int tz_flags; 79 #define TZ_FLAG_NONE 0 80 #define TZ_FLAG_PSV (1<<0) 81 #define TZ_FLAG_HOT (1<<2) 82 #define TZ_FLAG_CRT (1<<3) 83 84 struct sysctl_ctx_list tz_sysctl_ctx; 85 struct sysctl_oid *tz_sysctl_tree; 86 87 struct acpi_tz_state tz_state; 88 }; 89 90 static int acpi_tz_probe(device_t dev); 91 static int acpi_tz_attach(device_t dev); 92 static int acpi_tz_establish(struct acpi_tz_softc *sc); 93 static void acpi_tz_monitor(struct acpi_tz_softc *sc); 94 static void acpi_tz_all_off(struct acpi_tz_softc *sc); 95 static void acpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg); 96 static void acpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg); 97 static void acpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data); 98 static void acpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what); 99 static void acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context); 100 static void acpi_tz_timeout(void *arg); 101 102 static device_method_t acpi_tz_methods[] = { 103 /* Device interface */ 104 DEVMETHOD(device_probe, acpi_tz_probe), 105 DEVMETHOD(device_attach, acpi_tz_attach), 106 107 {0, 0} 108 }; 109 110 static driver_t acpi_tz_driver = { 111 "acpi_tz", 112 acpi_tz_methods, 113 sizeof(struct acpi_tz_softc), 114 }; 115 116 devclass_t acpi_tz_devclass; 117 DRIVER_MODULE(acpi_tz, acpi, acpi_tz_driver, acpi_tz_devclass, 0, 0); 118 119 static struct sysctl_ctx_list acpi_tz_sysctl_ctx; 120 static struct sysctl_oid *acpi_tz_sysctl_tree; 121 122 /* 123 * Match an ACPI thermal zone. 124 */ 125 static int 126 acpi_tz_probe(device_t dev) 127 { 128 int result; 129 130 ACPI_LOCK; 131 132 /* no FUNCTION_TRACE - too noisy */ 133 134 if ((acpi_get_type(dev) == ACPI_TYPE_THERMAL) && 135 !acpi_disabled("thermal")) { 136 device_set_desc(dev, "thermal zone"); 137 result = -10; 138 } else { 139 result = ENXIO; 140 } 141 ACPI_UNLOCK; 142 return(result); 143 } 144 145 /* 146 * Attach to an ACPI thermal zone. 147 */ 148 static int 149 acpi_tz_attach(device_t dev) 150 { 151 struct acpi_tz_softc *sc; 152 struct acpi_softc *acpi_sc; 153 int error; 154 char oidname[8]; 155 int i; 156 157 FUNCTION_TRACE(__func__); 158 159 ACPI_LOCK; 160 161 sc = device_get_softc(dev); 162 sc->tz_dev = dev; 163 sc->tz_handle = acpi_get_handle(dev); 164 165 /* 166 * Parse the current state of the thermal zone and build control 167 * structures. 168 */ 169 if ((error = acpi_tz_establish(sc)) != 0) 170 goto out; 171 172 /* 173 * Register for any Notify events sent to this zone. 174 */ 175 AcpiInstallNotifyHandler(sc->tz_handle, ACPI_DEVICE_NOTIFY, 176 acpi_tz_notify_handler, sc); 177 178 /* 179 * Create our sysctl nodes. 180 * 181 * XXX we need a mechanism for adding nodes under ACPI. 182 */ 183 if (device_get_unit(dev) == 0) { 184 acpi_sc = acpi_device_get_parent_softc(dev); 185 sysctl_ctx_init(&acpi_tz_sysctl_ctx); 186 acpi_tz_sysctl_tree = SYSCTL_ADD_NODE(&acpi_tz_sysctl_ctx, 187 SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), 188 OID_AUTO, "thermal", CTLFLAG_RD, 0, ""); 189 } 190 sysctl_ctx_init(&sc->tz_sysctl_ctx); 191 sprintf(oidname, "tz%d", device_get_unit(dev)); 192 sc->tz_sysctl_tree = SYSCTL_ADD_NODE(&sc->tz_sysctl_ctx, 193 SYSCTL_CHILDREN(acpi_tz_sysctl_tree), OID_AUTO, 194 oidname, CTLFLAG_RD, 0, ""); 195 SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), 196 OID_AUTO, "temperature", CTLFLAG_RD, 197 &sc->tz_temperature, 0, "current thermal zone temperature"); 198 SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), 199 OID_AUTO, "active", CTLFLAG_RD, 200 &sc->tz_active, 0, "active cooling mode"); 201 SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), 202 OID_AUTO, "flags", CTLFLAG_RD, 203 &sc->tz_flags, 0, "thermal zone flags"); 204 205 SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), 206 OID_AUTO, "_PSV", CTLFLAG_RD, 207 &sc->tz_state.psv, 0, ""); 208 SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), 209 OID_AUTO, "_HOT", CTLFLAG_RD, 210 &sc->tz_state.hot, 0, ""); 211 SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), 212 OID_AUTO, "_CRT", CTLFLAG_RD, 213 &sc->tz_state.crt, 0, ""); 214 for (i = 0; i < TZ_NUMLEVELS; i++) { 215 sprintf(oidname, "_AC%d", i); 216 SYSCTL_ADD_INT(&sc->tz_sysctl_ctx, SYSCTL_CHILDREN(sc->tz_sysctl_tree), 217 OID_AUTO, oidname, CTLFLAG_RD, 218 &sc->tz_state.ac[i], 0, ""); 219 } 220 221 /* 222 * Don't bother evaluating/printing the temperature at this point; 223 * on many systems it'll be bogus until the EC is running. 224 */ 225 226 out: 227 ACPI_UNLOCK; 228 return_VALUE(error); 229 } 230 231 /* 232 * Parse the current state of this thermal zone and set up to use it. 233 * 234 * Note that we may have previous state, which will have to be discarded. 235 */ 236 static int 237 acpi_tz_establish(struct acpi_tz_softc *sc) 238 { 239 ACPI_OBJECT *obj; 240 int i; 241 char nbuf[8]; 242 243 FUNCTION_TRACE(__func__); 244 245 ACPI_ASSERTLOCK; 246 247 /* 248 * Power everything off and erase any existing state. 249 */ 250 acpi_tz_all_off(sc); 251 for (i = 0; i < TZ_NUMLEVELS; i++) 252 if (sc->tz_state.al[i].Pointer != NULL) 253 AcpiOsFree(sc->tz_state.al[i].Pointer); 254 if (sc->tz_state.psl.Pointer != NULL) 255 AcpiOsFree(sc->tz_state.psl.Pointer); 256 bzero(&sc->tz_state, sizeof(sc->tz_state)); 257 258 /* kill the timeout (harmless if not running */ 259 untimeout(acpi_tz_timeout, sc, sc->tz_timeout); 260 261 /* 262 * Evaluate thermal zone parameters. 263 */ 264 for (i = 0; i < TZ_NUMLEVELS; i++) { 265 sprintf(nbuf, "_AC%d", i); 266 acpi_tz_getparam(sc, nbuf, &sc->tz_state.ac[i]); 267 sprintf(nbuf, "_AL%d", i); 268 acpi_EvaluateIntoBuffer(sc->tz_handle, nbuf, NULL, &sc->tz_state.al[i]); 269 obj = (ACPI_OBJECT *)sc->tz_state.al[i].Pointer; 270 if (obj != NULL) { 271 /* should be a package containing a list of power objects */ 272 if (obj->Type != ACPI_TYPE_PACKAGE) { 273 device_printf(sc->tz_dev, "%s has unknown object type %d, rejecting\n", 274 nbuf, obj->Type); 275 return_VALUE(ENXIO); 276 } 277 } 278 } 279 acpi_tz_getparam(sc, "_CRT", &sc->tz_state.crt); 280 acpi_tz_getparam(sc, "_HOT", &sc->tz_state.hot); 281 acpi_EvaluateIntoBuffer(sc->tz_handle, "_PSL", NULL, &sc->tz_state.psl); 282 acpi_tz_getparam(sc, "_PSV", &sc->tz_state.psv); 283 acpi_tz_getparam(sc, "_TC1", &sc->tz_state.tc1); 284 acpi_tz_getparam(sc, "_TC2", &sc->tz_state.tc2); 285 acpi_tz_getparam(sc, "_TSP", &sc->tz_state.tsp); 286 acpi_tz_getparam(sc, "_TZP", &sc->tz_state.tzp); 287 288 /* 289 * Sanity-check the values we've been given. 290 * 291 * XXX what do we do about systems that give us the same value for 292 * more than one of these setpoints? 293 */ 294 acpi_tz_sanity(sc, &sc->tz_state.crt, "_CRT"); 295 acpi_tz_sanity(sc, &sc->tz_state.hot, "_HOT"); 296 acpi_tz_sanity(sc, &sc->tz_state.psv, "_PSV"); 297 for (i = 0; i < TZ_NUMLEVELS; i++) 298 acpi_tz_sanity(sc, &sc->tz_state.ac[i], "_ACx"); 299 300 /* 301 * Power off everything that we've just been given. 302 */ 303 acpi_tz_all_off(sc); 304 305 /* 306 * The timeout routine always needs to run, since it may be involved 307 * in passive cooling. 308 */ 309 sc->tz_timeout = timeout(acpi_tz_timeout, sc, 0); 310 311 312 return_VALUE(0); 313 } 314 315 /* 316 * Evaluate the condition of a thermal zone, take appropriate actions. 317 */ 318 static void 319 acpi_tz_monitor(struct acpi_tz_softc *sc) 320 { 321 int temp; 322 int i; 323 int newactive, newflags; 324 325 FUNCTION_TRACE(__func__); 326 327 ACPI_ASSERTLOCK; 328 329 /* 330 * Get the current temperature. 331 */ 332 if ((acpi_EvaluateInteger(sc->tz_handle, "_TMP", &temp)) != AE_OK) { 333 device_printf(sc->tz_dev, "error fetching current temperature\n"); 334 /* XXX disable zone? go to max cooling? */ 335 return_VOID; 336 } 337 DEBUG_PRINT(TRACE_VALUES, ("got %d.%dC\n", TZ_KELVTOC(temp))); 338 sc->tz_temperature = temp; 339 340 /* 341 * Work out what we ought to be doing right now. 342 * 343 * Note that the _ACx levels sort from hot to cold. 344 */ 345 newactive = TZ_ACTIVE_NONE; 346 for (i = TZ_NUMLEVELS - 1; i >= 0; i--) 347 if ((sc->tz_state.ac[i] != -1) && (temp > sc->tz_state.ac[i])) 348 newactive = i; 349 350 newflags = TZ_FLAG_NONE; 351 if ((sc->tz_state.psv != -1) && (temp > sc->tz_state.psv)) 352 newflags |= TZ_FLAG_PSV; 353 if ((sc->tz_state.hot != -1) && (temp > sc->tz_state.hot)) 354 newflags |= TZ_FLAG_HOT; 355 if ((sc->tz_state.crt != -1) && (temp > sc->tz_state.crt)) 356 newflags |= TZ_FLAG_CRT; 357 358 /* 359 * If the active cooling state has changed, we have to switch things. 360 */ 361 if (newactive != sc->tz_active) { 362 363 /* turn off the cooling devices that are on, if any are */ 364 if (sc->tz_active != TZ_ACTIVE_NONE) 365 acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_state.al[sc->tz_active].Pointer, 366 acpi_tz_switch_cooler_off, sc); 367 368 /* turn on cooling devices that are required, if any are */ 369 if (newactive != TZ_ACTIVE_NONE) 370 acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_state.al[newactive].Pointer, 371 acpi_tz_switch_cooler_on, sc); 372 sc->tz_active = newactive; 373 } 374 375 /* 376 * XXX (de)activate any passive cooling that may be required. 377 */ 378 379 /* 380 * If we have just become _HOT or _CRT, warn the user. 381 * 382 * We should actually shut down at this point, but it's not clear 383 * that some systems don't actually map _CRT to the same value as _AC0. 384 */ 385 if ((newflags & (TZ_FLAG_HOT | TZ_FLAG_CRT)) && 386 !(sc->tz_flags & (TZ_FLAG_HOT | TZ_FLAG_CRT))) { 387 device_printf(sc->tz_dev, "WARNING - current temperature (%d.%dC) exceeds system limits\n", 388 TZ_KELVTOC(sc->tz_temperature), sc->tz_temperature); 389 /* shutdown_nice(RB_POWEROFF);*/ 390 } 391 sc->tz_flags = newflags; 392 393 return_VOID; 394 } 395 396 /* 397 * Turn off all the cooling devices. 398 */ 399 static void 400 acpi_tz_all_off(struct acpi_tz_softc *sc) 401 { 402 int i; 403 404 FUNCTION_TRACE(__func__); 405 406 ACPI_ASSERTLOCK; 407 408 /* 409 * Scan all the _AL objects, and turn them all off. 410 */ 411 for (i = 0; i < TZ_NUMLEVELS; i++) { 412 if (sc->tz_state.al[i].Pointer == NULL) 413 continue; 414 acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_state.al[i].Pointer, 415 acpi_tz_switch_cooler_off, sc); 416 } 417 418 /* 419 * XXX revert any passive-cooling options. 420 */ 421 422 sc->tz_active = TZ_ACTIVE_NONE; 423 sc->tz_flags = TZ_FLAG_NONE; 424 return_VOID; 425 } 426 427 /* 428 * Given an object, verify that it's a reference to a device of some sort, 429 * and try to switch it off. 430 */ 431 static void 432 acpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg) 433 { 434 struct acpi_tz_softc *sc = (struct acpi_tz_softc *)arg; 435 ACPI_HANDLE cooler; 436 437 FUNCTION_TRACE(__func__); 438 439 ACPI_ASSERTLOCK; 440 441 switch(obj->Type) { 442 case ACPI_TYPE_STRING: 443 DEBUG_PRINT(TRACE_OBJECTS, ("called to turn %s off\n", obj->String.Pointer)); 444 445 /* 446 * Find the handle for the device and turn it off. 447 * The String object here seems to contain a fully-qualified path, so we 448 * don't have to search for it in our parents. 449 * 450 * XXX This may not always be the case. 451 */ 452 if (AcpiGetHandle(obj->String.Pointer, NULL, &cooler) == AE_OK) 453 acpi_pwr_switch_consumer(cooler, ACPI_STATE_D3); 454 break; 455 456 default: 457 DEBUG_PRINT(TRACE_OBJECTS, ("called to handle unsupported object type %d\n", 458 obj->Type)); 459 break; 460 } 461 return_VOID; 462 } 463 464 /* 465 * Given an object, verify that it's a reference to a device of some sort, 466 * and try to switch it on. 467 * 468 * XXX replication of off/on function code is bad, mmmkay? 469 */ 470 static void 471 acpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg) 472 { 473 struct acpi_tz_softc *sc = (struct acpi_tz_softc *)arg; 474 ACPI_HANDLE cooler; 475 476 FUNCTION_TRACE(__func__); 477 478 ACPI_ASSERTLOCK; 479 480 switch(obj->Type) { 481 case ACPI_TYPE_STRING: 482 DEBUG_PRINT(TRACE_OBJECTS, ("called to turn %s off\n", obj->String.Pointer)); 483 484 /* 485 * Find the handle for the device and turn it off. 486 * The String object here seems to contain a fully-qualified path, so we 487 * don't have to search for it in our parents. 488 * 489 * XXX This may not always be the case. 490 */ 491 if (AcpiGetHandle(obj->String.Pointer, NULL, &cooler) == AE_OK) 492 acpi_pwr_switch_consumer(cooler, ACPI_STATE_D0); 493 break; 494 495 default: 496 DEBUG_PRINT(TRACE_OBJECTS, ("called to handle unsupported object type %d\n", 497 obj->Type)); 498 break; 499 } 500 return_VOID; 501 } 502 503 /* 504 * Read/debug-print a parameter, default it to -1. 505 */ 506 static void 507 acpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data) 508 { 509 510 FUNCTION_TRACE(__func__); 511 512 ACPI_ASSERTLOCK; 513 514 if (acpi_EvaluateInteger(sc->tz_handle, node, data) != AE_OK) { 515 *data = -1; 516 } else { 517 DEBUG_PRINT(TRACE_VALUES, ("%s.%s = %d\n", acpi_name(sc->tz_handle), 518 node, *data)); 519 } 520 return_VOID; 521 } 522 523 /* 524 * Sanity-check a temperature value. Assume that setpoints 525 * should be between 0C and 150C. 526 */ 527 static void 528 acpi_tz_sanity(struct acpi_tz_softc *sc, int *val, char *what) 529 { 530 if ((*val != -1) && ((*val < TZ_ZEROC) || (*val > (TZ_ZEROC + 1500)))) { 531 device_printf(sc->tz_dev, "%s value is absurd, ignored (%d.%dC)\n", 532 what, TZ_KELVTOC(*val)); 533 *val = -1; 534 } 535 } 536 537 /* 538 * Respond to a Notify event sent to the zone. 539 */ 540 static void 541 acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context) 542 { 543 struct acpi_tz_softc *sc = (struct acpi_tz_softc *)context; 544 545 FUNCTION_TRACE(__func__); 546 547 ACPI_ASSERTLOCK; 548 549 switch(notify) { 550 case TZ_NOTIFY_TEMPERATURE: 551 /* temperature change occurred */ 552 AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_monitor, sc); 553 break; 554 case TZ_NOTIFY_DEVICES: 555 case TZ_NOTIFY_LEVELS: 556 /* zone devices/setpoints changed */ 557 AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_establish, sc); 558 break; 559 default: 560 device_printf(sc->tz_dev, "unknown Notify event 0x%x\n", notify); 561 break; 562 } 563 return_VOID; 564 } 565 566 /* 567 * Poll the thermal zone. 568 */ 569 static void 570 acpi_tz_timeout(void *arg) 571 { 572 struct acpi_tz_softc *sc = (struct acpi_tz_softc *)arg; 573 574 ACPI_LOCK; 575 576 /* check temperature, take action */ 577 AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_monitor, sc); 578 579 /* XXX passive cooling actions? */ 580 581 /* re-register ourself */ 582 sc->tz_timeout = timeout(acpi_tz_timeout, sc, TZ_POLLRATE); 583 584 ACPI_UNLOCK; 585 } 586