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 36 #include "acpi.h" 37 38 #include <dev/acpica/acpivar.h> 39 40 /* 41 * Hooks for the ACPI CA debugging infrastructure 42 */ 43 #define _COMPONENT ACPI_THERMAL 44 MODULE_NAME("THERMAL") 45 46 #define TZ_ZEROC 2732 47 #define TZ_KELVTOC(x) (((x) - TZ_ZEROC) / 10), (((x) - TZ_ZEROC) % 10) 48 49 #define TZ_NOTIFY_TEMPERATURE 0x80 50 #define TZ_NOTIFY_DEVICES 0x81 51 #define TZ_NOTIFY_LEVELS 0x82 52 53 #define TZ_POLLRATE (hz * 10) /* every ten seconds */ 54 55 #define TZ_NUMLEVELS 10 /* defined by ACPI spec */ 56 struct acpi_tz_state { 57 int ac[TZ_NUMLEVELS]; 58 ACPI_BUFFER al[TZ_NUMLEVELS]; 59 int crt; 60 int hot; 61 ACPI_BUFFER psl; 62 int psv; 63 int tc1; 64 int tc2; 65 int tsp; 66 int tzp; 67 }; 68 69 70 struct acpi_tz_softc { 71 device_t tz_dev; 72 ACPI_HANDLE tz_handle; 73 struct callout_handle tz_timeout; 74 int tz_current; 75 #define TZ_STATE_NONE 0 76 #define TZ_STATE_PSV 1 77 #define TZ_STATE_AC0 2 78 #define TZ_STATE_HOT (TZ_STATE_AC0 + TZ_NUMLEVELS) 79 #define TZ_STATE_CRT (TZ_STATE_AC0 + TZ_NUMLEVELS + 1) 80 81 struct acpi_tz_state tz_state; 82 }; 83 84 static int acpi_tz_probe(device_t dev); 85 static int acpi_tz_attach(device_t dev); 86 static int acpi_tz_establish(struct acpi_tz_softc *sc); 87 static void acpi_tz_monitor(struct acpi_tz_softc *sc); 88 static void acpi_tz_all_off(struct acpi_tz_softc *sc); 89 static void acpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg); 90 static void acpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg); 91 static void acpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data); 92 static void acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context); 93 static void acpi_tz_timeout(void *arg); 94 95 static device_method_t acpi_tz_methods[] = { 96 /* Device interface */ 97 DEVMETHOD(device_probe, acpi_tz_probe), 98 DEVMETHOD(device_attach, acpi_tz_attach), 99 100 {0, 0} 101 }; 102 103 static driver_t acpi_tz_driver = { 104 "acpi_tz", 105 acpi_tz_methods, 106 sizeof(struct acpi_tz_softc), 107 }; 108 109 devclass_t acpi_tz_devclass; 110 DRIVER_MODULE(acpi_tz, acpi, acpi_tz_driver, acpi_tz_devclass, 0, 0); 111 112 /* 113 * Match an ACPI thermal zone. 114 */ 115 static int 116 acpi_tz_probe(device_t dev) 117 { 118 int result; 119 120 ACPI_LOCK; 121 122 /* no FUNCTION_TRACE - too noisy */ 123 124 if ((acpi_get_type(dev) == ACPI_TYPE_THERMAL) && 125 !acpi_disabled("thermal")) { 126 device_set_desc(dev, "thermal zone"); 127 result = -10; 128 } else { 129 result = ENXIO; 130 } 131 ACPI_UNLOCK; 132 return(result); 133 } 134 135 /* 136 * Attach to an ACPI thermal zone. 137 */ 138 static int 139 acpi_tz_attach(device_t dev) 140 { 141 struct acpi_tz_softc *sc; 142 int error; 143 144 FUNCTION_TRACE(__func__); 145 146 ACPI_LOCK; 147 148 sc = device_get_softc(dev); 149 sc->tz_dev = dev; 150 sc->tz_handle = acpi_get_handle(dev); 151 152 /* 153 * Parse the current state of the thermal zone and build control 154 * structures. 155 */ 156 if ((error = acpi_tz_establish(sc)) != 0) 157 goto out; 158 159 /* 160 * Register for any Notify events sent to this zone. 161 */ 162 AcpiInstallNotifyHandler(sc->tz_handle, ACPI_DEVICE_NOTIFY, 163 acpi_tz_notify_handler, sc); 164 165 /* 166 * Don't bother evaluating/printing the temperature at this point; 167 * on many systems it'll be bogus until the EC is running. 168 */ 169 170 out: 171 ACPI_UNLOCK; 172 return_VALUE(error); 173 } 174 175 /* 176 * Parse the current state of this thermal zone and set up to use it. 177 * 178 * Note that we may have previous state, which will have to be discarded. 179 */ 180 static int 181 acpi_tz_establish(struct acpi_tz_softc *sc) 182 { 183 ACPI_OBJECT *obj; 184 int i; 185 char nbuf[8]; 186 187 FUNCTION_TRACE(__func__); 188 189 ACPI_ASSERTLOCK; 190 191 /* 192 * Power everything off and erase any existing state. 193 */ 194 acpi_tz_all_off(sc); 195 for (i = 0; i < TZ_NUMLEVELS; i++) 196 if (sc->tz_state.al[i].Pointer != NULL) 197 AcpiOsFree(sc->tz_state.al[i].Pointer); 198 if (sc->tz_state.psl.Pointer != NULL) 199 AcpiOsFree(sc->tz_state.psl.Pointer); 200 bzero(&sc->tz_state, sizeof(sc->tz_state)); 201 202 /* kill the timeout (harmless if not running */ 203 untimeout(acpi_tz_timeout, sc, sc->tz_timeout); 204 205 /* 206 * Evaluate thermal zone parameters. 207 */ 208 for (i = 0; i < TZ_NUMLEVELS; i++) { 209 sprintf(nbuf, "_AC%d", i); 210 acpi_tz_getparam(sc, nbuf, &sc->tz_state.ac[i]); 211 sprintf(nbuf, "_AL%d", i); 212 acpi_EvaluateIntoBuffer(sc->tz_handle, nbuf, NULL, &sc->tz_state.al[i]); 213 obj = (ACPI_OBJECT *)sc->tz_state.al[i].Pointer; 214 if (obj != NULL) { 215 /* should be a package containing a list of power objects */ 216 if (obj->Type != ACPI_TYPE_PACKAGE) { 217 device_printf(sc->tz_dev, "%s has unknown object type %d, rejecting\n", 218 nbuf, obj->Type); 219 return_VALUE(ENXIO); 220 } 221 } 222 } 223 acpi_tz_getparam(sc, "_CRT", &sc->tz_state.crt); 224 acpi_tz_getparam(sc, "_HOT", &sc->tz_state.hot); 225 acpi_EvaluateIntoBuffer(sc->tz_handle, "_PSL", NULL, &sc->tz_state.psl); 226 acpi_tz_getparam(sc, "_PSV", &sc->tz_state.psv); 227 acpi_tz_getparam(sc, "_TC1", &sc->tz_state.tc1); 228 acpi_tz_getparam(sc, "_TC2", &sc->tz_state.tc2); 229 acpi_tz_getparam(sc, "_TSP", &sc->tz_state.tsp); 230 acpi_tz_getparam(sc, "_TZP", &sc->tz_state.tzp); 231 232 /* 233 * Power off everything that we've just been given. 234 */ 235 acpi_tz_all_off(sc); 236 237 /* 238 * Do we need to poll the thermal zone? Ignore the suggested 239 * rate. 240 */ 241 if (sc->tz_state.tzp != 0) 242 sc->tz_timeout = timeout(acpi_tz_timeout, sc, TZ_POLLRATE); 243 244 245 return_VALUE(0); 246 } 247 248 /* 249 * Evaluate the condition of a thermal zone, take appropriate actions. 250 */ 251 static void 252 acpi_tz_monitor(struct acpi_tz_softc *sc) 253 { 254 int temp, new; 255 int i; 256 257 FUNCTION_TRACE(__func__); 258 259 ACPI_ASSERTLOCK; 260 261 /* 262 * Get the current temperature. 263 */ 264 if ((acpi_EvaluateInteger(sc->tz_handle, "_TMP", &temp)) != AE_OK) { 265 device_printf(sc->tz_dev, "error fetching current temperature\n"); 266 /* XXX disable zone? go to max cooling? */ 267 return_VOID; 268 } 269 DEBUG_PRINT(TRACE_VALUES, ("got %d.%dC\n", TZ_KELVTOC(temp))); 270 271 /* 272 * Work out what we ought to be doing right now. 273 */ 274 new = TZ_STATE_NONE; 275 if ((sc->tz_state.psv != -1) && (temp > sc->tz_state.psv)) 276 new = TZ_STATE_PSV; 277 for (i = 0; i < TZ_NUMLEVELS; i++) 278 if ((sc->tz_state.ac[i] != -1) && (temp > sc->tz_state.ac[i])) 279 new = TZ_STATE_AC0 + i; 280 if ((sc->tz_state.hot != -1) && (temp > sc->tz_state.hot)) 281 new = TZ_STATE_HOT; 282 if ((sc->tz_state.crt != -1) && (temp > sc->tz_state.crt)) 283 new = TZ_STATE_CRT; 284 285 /* 286 * If our state has not changed, do nothing. 287 */ 288 if (new == sc->tz_current) 289 return_VOID; 290 291 /* 292 * XXX if we're in a passive-cooling mode, revert to full-speed operation. 293 */ 294 if (sc->tz_current == TZ_STATE_PSV) { 295 /* XXX implement */ 296 } 297 298 /* 299 * If we're in an active-cooling mode, turn off the current cooler(s). 300 */ 301 if ((sc->tz_current >= TZ_STATE_AC0) && (sc->tz_current < (TZ_STATE_AC0 + TZ_NUMLEVELS))) 302 acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_state.al[sc->tz_current - TZ_STATE_AC0].Pointer, 303 acpi_tz_switch_cooler_off, sc); 304 305 /* 306 * XXX If the new mode is passive-cooling, make appropriate adjustments. 307 */ 308 309 /* 310 * If the new mode is an active-cooling mode, turn on the new cooler(s). 311 */ 312 if ((new >= TZ_STATE_AC0) && (new < (TZ_STATE_AC0 + TZ_NUMLEVELS))) 313 acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_state.al[new - TZ_STATE_AC0].Pointer, 314 acpi_tz_switch_cooler_on, sc); 315 316 /* 317 * If we're _HOT or _CRT, shut down now! 318 */ 319 if ((new == TZ_STATE_HOT) || (new == TZ_STATE_CRT)) { 320 device_printf(sc->tz_dev, "WARNING - emergency thermal shutdown in progress.\n"); 321 shutdown_nice(RB_POWEROFF); 322 } 323 324 /* gone to new state */ 325 sc->tz_current = new; 326 327 return_VOID; 328 } 329 330 /* 331 * Turn off all the cooling devices. 332 */ 333 static void 334 acpi_tz_all_off(struct acpi_tz_softc *sc) 335 { 336 int i; 337 338 FUNCTION_TRACE(__func__); 339 340 ACPI_ASSERTLOCK; 341 342 /* 343 * Scan all the _AL objects, and turn them all off. 344 */ 345 for (i = 0; i < TZ_NUMLEVELS; i++) { 346 if (sc->tz_state.al[i].Pointer == NULL) 347 continue; 348 acpi_ForeachPackageObject((ACPI_OBJECT *)sc->tz_state.al[i].Pointer, 349 acpi_tz_switch_cooler_off, sc); 350 } 351 352 /* 353 * XXX revert any passive-cooling options. 354 */ 355 356 sc->tz_current = TZ_STATE_NONE; 357 return_VOID; 358 } 359 360 /* 361 * Given an object, verify that it's a reference to a device of some sort, 362 * and try to switch it off. 363 */ 364 static void 365 acpi_tz_switch_cooler_off(ACPI_OBJECT *obj, void *arg) 366 { 367 struct acpi_tz_softc *sc = (struct acpi_tz_softc *)arg; 368 ACPI_HANDLE cooler; 369 370 FUNCTION_TRACE(__func__); 371 372 ACPI_ASSERTLOCK; 373 374 switch(obj->Type) { 375 case ACPI_TYPE_STRING: 376 DEBUG_PRINT(TRACE_OBJECTS, ("called to turn %s off\n", obj->String.Pointer)); 377 378 /* 379 * Find the handle for the device and turn it off. 380 * The String object here seems to contain a fully-qualified path, so we 381 * don't have to search for it in our parents. 382 * 383 * XXX This may not always be the case. 384 */ 385 if (AcpiGetHandle(obj->String.Pointer, NULL, &cooler) == AE_OK) 386 acpi_pwr_switch_consumer(cooler, ACPI_STATE_D3); 387 break; 388 389 default: 390 DEBUG_PRINT(TRACE_OBJECTS, ("called to handle unsupported object type %d\n", 391 obj->Type)); 392 break; 393 } 394 return_VOID; 395 } 396 397 /* 398 * Given an object, verify that it's a reference to a device of some sort, 399 * and try to switch it on. 400 * 401 * XXX replication of off/on function code is bad, mmmkay? 402 */ 403 static void 404 acpi_tz_switch_cooler_on(ACPI_OBJECT *obj, void *arg) 405 { 406 struct acpi_tz_softc *sc = (struct acpi_tz_softc *)arg; 407 ACPI_HANDLE cooler; 408 409 FUNCTION_TRACE(__func__); 410 411 ACPI_ASSERTLOCK; 412 413 switch(obj->Type) { 414 case ACPI_TYPE_STRING: 415 DEBUG_PRINT(TRACE_OBJECTS, ("called to turn %s off\n", obj->String.Pointer)); 416 417 /* 418 * Find the handle for the device and turn it off. 419 * The String object here seems to contain a fully-qualified path, so we 420 * don't have to search for it in our parents. 421 * 422 * XXX This may not always be the case. 423 */ 424 if (AcpiGetHandle(obj->String.Pointer, NULL, &cooler) == AE_OK) 425 acpi_pwr_switch_consumer(cooler, ACPI_STATE_D0); 426 break; 427 428 default: 429 DEBUG_PRINT(TRACE_OBJECTS, ("called to handle unsupported object type %d\n", 430 obj->Type)); 431 break; 432 } 433 return_VOID; 434 } 435 436 /* 437 * Read/debug-print a parameter, default it to -1. 438 */ 439 static void 440 acpi_tz_getparam(struct acpi_tz_softc *sc, char *node, int *data) 441 { 442 443 FUNCTION_TRACE(__func__); 444 445 ACPI_ASSERTLOCK; 446 447 if (acpi_EvaluateInteger(sc->tz_handle, node, data) != AE_OK) { 448 *data = -1; 449 } else { 450 DEBUG_PRINT(TRACE_VALUES, ("%s.%s = %d\n", acpi_name(sc->tz_handle), 451 node, *data)); 452 } 453 return_VOID; 454 } 455 456 /* 457 * Respond to a Notify event sent to the zone. 458 */ 459 static void 460 acpi_tz_notify_handler(ACPI_HANDLE h, UINT32 notify, void *context) 461 { 462 struct acpi_tz_softc *sc = (struct acpi_tz_softc *)context; 463 464 FUNCTION_TRACE(__func__); 465 466 ACPI_ASSERTLOCK; 467 468 switch(notify) { 469 case TZ_NOTIFY_TEMPERATURE: 470 /* temperature change occurred */ 471 AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_monitor, sc); 472 break; 473 case TZ_NOTIFY_DEVICES: 474 case TZ_NOTIFY_LEVELS: 475 /* zone devices/setpoints changed */ 476 AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_establish, sc); 477 break; 478 default: 479 device_printf(sc->tz_dev, "unknown Notify event 0x%x\n", notify); 480 break; 481 } 482 return_VOID; 483 } 484 485 /* 486 * Poll the thermal zone. 487 */ 488 static void 489 acpi_tz_timeout(void *arg) 490 { 491 struct acpi_tz_softc *sc = (struct acpi_tz_softc *)arg; 492 493 ACPI_LOCK; 494 495 /* check temperature, take action */ 496 AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, (OSD_EXECUTION_CALLBACK)acpi_tz_monitor, sc); 497 498 /* XXX passive cooling actions? */ 499 500 /* re-register ourself */ 501 sc->tz_timeout = timeout(acpi_tz_timeout, sc, TZ_POLLRATE); 502 503 ACPI_UNLOCK; 504 } 505