1 /*- 2 * Copyright (c) 2001 Michael Smith 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 29 #include "opt_acpi.h" /* XXX trim includes */ 30 #include <sys/param.h> 31 #include <sys/kernel.h> 32 #include <sys/proc.h> 33 #include <sys/malloc.h> 34 #include <sys/bus.h> 35 #include <sys/conf.h> 36 #include <sys/ioccom.h> 37 #include <sys/reboot.h> 38 #include <sys/sysctl.h> 39 #include <sys/systm.h> 40 #include <sys/ctype.h> 41 42 #include <machine/clock.h> 43 44 #include <machine/resource.h> 45 46 #include "acpi.h" 47 48 #include <dev/acpica/acpivar.h> 49 #include <dev/acpica/acpiio.h> 50 51 /* 52 * ACPI power resource management. 53 * 54 * Power resource behaviour is slightly complicated by the fact that 55 * a single power resource may provide power for more than one device. 56 * Thus, we must track the device(s) being powered by a given power 57 * resource, and only deactivate it when there are no powered devices. 58 * 59 * Note that this only manages resources for known devices. There is an 60 * ugly case where we may turn of power to a device which is in use because 61 * we don't know that it depends on a given resource. We should perhaps 62 * try to be smarter about this, but a more complete solution would involve 63 * scanning all of the ACPI namespace to find devices we're not currently 64 * aware of, and this raises questions about whether they should be left 65 * on, turned off, etc. 66 * 67 * XXX locking 68 */ 69 70 MALLOC_DEFINE(M_ACPIPWR, "acpipwr", "ACPI power resources"); 71 72 /* 73 * Hooks for the ACPI CA debugging infrastructure 74 */ 75 #define _COMPONENT ACPI_POWER 76 ACPI_MODULE_NAME("POWERRES") 77 78 /* return values from _STA on a power resource */ 79 #define ACPI_PWR_OFF 0 80 #define ACPI_PWR_ON 1 81 82 /* 83 * A relationship between a power resource and a consumer. 84 */ 85 struct acpi_powerreference { 86 struct acpi_powerconsumer *ar_consumer; 87 struct acpi_powerresource *ar_resource; 88 TAILQ_ENTRY(acpi_powerreference) ar_rlink; /* link on resource list */ 89 TAILQ_ENTRY(acpi_powerreference) ar_clink; /* link on consumer */ 90 }; 91 92 /* 93 * A power-managed device. 94 */ 95 struct acpi_powerconsumer { 96 ACPI_HANDLE ac_consumer; /* device which is powered */ 97 int ac_state; 98 TAILQ_ENTRY(acpi_powerconsumer) ac_link; 99 TAILQ_HEAD(,acpi_powerreference) ac_references; 100 }; 101 102 /* 103 * A power resource. 104 */ 105 struct acpi_powerresource { 106 TAILQ_ENTRY(acpi_powerresource) ap_link; 107 TAILQ_HEAD(,acpi_powerreference) ap_references; 108 ACPI_HANDLE ap_resource; /* the resource's handle */ 109 ACPI_INTEGER ap_systemlevel; 110 ACPI_INTEGER ap_order; 111 }; 112 113 static TAILQ_HEAD(acpi_powerresource_list, acpi_powerresource) acpi_powerresources; 114 static TAILQ_HEAD(acpi_powerconsumer_list, acpi_powerconsumer) acpi_powerconsumers; 115 116 static ACPI_STATUS acpi_pwr_register_consumer(ACPI_HANDLE consumer); 117 static ACPI_STATUS acpi_pwr_deregister_consumer(ACPI_HANDLE consumer); 118 static ACPI_STATUS acpi_pwr_register_resource(ACPI_HANDLE res); 119 static ACPI_STATUS acpi_pwr_deregister_resource(ACPI_HANDLE res); 120 static void acpi_pwr_reference_resource(ACPI_OBJECT *obj, void *arg); 121 static ACPI_STATUS acpi_pwr_switch_power(void); 122 static struct acpi_powerresource *acpi_pwr_find_resource(ACPI_HANDLE res); 123 static struct acpi_powerconsumer *acpi_pwr_find_consumer(ACPI_HANDLE consumer); 124 125 /* 126 * Initialise our lists. 127 */ 128 static void 129 acpi_pwr_init(void *junk) 130 { 131 TAILQ_INIT(&acpi_powerresources); 132 TAILQ_INIT(&acpi_powerconsumers); 133 } 134 SYSINIT(acpi_powerresource, SI_SUB_TUNABLES, SI_ORDER_ANY, acpi_pwr_init, NULL); 135 136 /* 137 * Register a power resource. 138 * 139 * It's OK to call this if we already know about the resource. 140 */ 141 static ACPI_STATUS 142 acpi_pwr_register_resource(ACPI_HANDLE res) 143 { 144 ACPI_STATUS status; 145 ACPI_BUFFER buf; 146 ACPI_OBJECT *obj; 147 struct acpi_powerresource *rp, *srp; 148 149 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 150 151 rp = NULL; 152 buf.Pointer = NULL; 153 154 /* look to see if we know about this resource */ 155 if (acpi_pwr_find_resource(res) != NULL) 156 return_ACPI_STATUS(AE_OK); /* already know about it */ 157 158 /* allocate a new resource */ 159 if ((rp = malloc(sizeof(*rp), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) { 160 status = AE_NO_MEMORY; 161 goto out; 162 } 163 TAILQ_INIT(&rp->ap_references); 164 rp->ap_resource = res; 165 166 /* get the Power Resource object */ 167 buf.Length = ACPI_ALLOCATE_BUFFER; 168 if (ACPI_FAILURE(status = AcpiEvaluateObject(res, NULL, NULL, &buf))) { 169 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "no power resource object\n")); 170 goto out; 171 } 172 obj = buf.Pointer; 173 if (obj->Type != ACPI_TYPE_POWER) { 174 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "questionable power resource object %s\n", acpi_name(res))); 175 status = AE_TYPE; 176 goto out; 177 } 178 rp->ap_systemlevel = obj->PowerResource.SystemLevel; 179 rp->ap_order = obj->PowerResource.ResourceOrder; 180 181 /* sort the resource into the list */ 182 status = AE_OK; 183 srp = TAILQ_FIRST(&acpi_powerresources); 184 if ((srp == NULL) || (rp->ap_order < srp->ap_order)) { 185 TAILQ_INSERT_HEAD(&acpi_powerresources, rp, ap_link); 186 goto done; 187 } 188 TAILQ_FOREACH(srp, &acpi_powerresources, ap_link) 189 if (rp->ap_order < srp->ap_order) { 190 TAILQ_INSERT_BEFORE(srp, rp, ap_link); 191 goto done; 192 } 193 TAILQ_INSERT_TAIL(&acpi_powerresources, rp, ap_link); 194 195 done: 196 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "registered power resource %s\n", acpi_name(res))); 197 out: 198 if (buf.Pointer != NULL) 199 AcpiOsFree(buf.Pointer); 200 if (ACPI_FAILURE(status) && (rp != NULL)) 201 free(rp, M_ACPIPWR); 202 return_ACPI_STATUS(status); 203 } 204 205 /* 206 * Deregister a power resource. 207 */ 208 static ACPI_STATUS 209 acpi_pwr_deregister_resource(ACPI_HANDLE res) 210 { 211 struct acpi_powerresource *rp; 212 213 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 214 215 rp = NULL; 216 217 /* find the resource */ 218 if ((rp = acpi_pwr_find_resource(res)) == NULL) 219 return_ACPI_STATUS(AE_BAD_PARAMETER); 220 221 /* check that there are no consumers referencing this resource */ 222 if (TAILQ_FIRST(&rp->ap_references) != NULL) 223 return_ACPI_STATUS(AE_BAD_PARAMETER); 224 225 /* pull it off the list and free it */ 226 TAILQ_REMOVE(&acpi_powerresources, rp, ap_link); 227 free(rp, M_ACPIPWR); 228 229 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power resource %s\n", acpi_name(res))); 230 231 return_ACPI_STATUS(AE_OK); 232 } 233 234 /* 235 * Register a power consumer. 236 * 237 * It's OK to call this if we already know about the consumer. 238 */ 239 static ACPI_STATUS 240 acpi_pwr_register_consumer(ACPI_HANDLE consumer) 241 { 242 struct acpi_powerconsumer *pc; 243 244 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 245 246 /* check to see whether we know about this consumer already */ 247 if ((pc = acpi_pwr_find_consumer(consumer)) != NULL) 248 return_ACPI_STATUS(AE_OK); 249 250 /* allocate a new power consumer */ 251 if ((pc = malloc(sizeof(*pc), M_ACPIPWR, M_NOWAIT)) == NULL) 252 return_ACPI_STATUS(AE_NO_MEMORY); 253 TAILQ_INSERT_HEAD(&acpi_powerconsumers, pc, ac_link); 254 TAILQ_INIT(&pc->ac_references); 255 pc->ac_consumer = consumer; 256 257 pc->ac_state = ACPI_STATE_UNKNOWN; /* XXX we should try to find its current state */ 258 259 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "registered power consumer %s\n", acpi_name(consumer))); 260 261 return_ACPI_STATUS(AE_OK); 262 } 263 264 /* 265 * Deregister a power consumer. 266 * 267 * This should only be done once the consumer has been powered off. 268 * (XXX is this correct? Check once implemented) 269 */ 270 static ACPI_STATUS 271 acpi_pwr_deregister_consumer(ACPI_HANDLE consumer) 272 { 273 struct acpi_powerconsumer *pc; 274 275 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 276 277 /* find the consumer */ 278 if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) 279 return_ACPI_STATUS(AE_BAD_PARAMETER); 280 281 /* make sure the consumer's not referencing anything right now */ 282 if (TAILQ_FIRST(&pc->ac_references) != NULL) 283 return_ACPI_STATUS(AE_BAD_PARAMETER); 284 285 /* pull the consumer off the list and free it */ 286 TAILQ_REMOVE(&acpi_powerconsumers, pc, ac_link); 287 288 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power consumer %s\n", acpi_name(consumer))); 289 290 return_ACPI_STATUS(AE_OK); 291 } 292 293 /* 294 * Set a power consumer to a particular power state. 295 */ 296 ACPI_STATUS 297 acpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state) 298 { 299 struct acpi_powerconsumer *pc; 300 struct acpi_powerreference *pr; 301 ACPI_HANDLE method_handle, reslist_handle, pr0_handle; 302 ACPI_BUFFER reslist_buffer; 303 ACPI_OBJECT *reslist_object; 304 ACPI_STATUS status; 305 char *method_name, *reslist_name; 306 int res_changed; 307 308 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 309 310 /* find the consumer */ 311 if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) { 312 if (ACPI_FAILURE(status = acpi_pwr_register_consumer(consumer))) 313 return_ACPI_STATUS(status); 314 if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) { 315 return_ACPI_STATUS(AE_ERROR); /* something very wrong */ 316 } 317 } 318 319 /* check for valid transitions */ 320 if ((pc->ac_state == ACPI_STATE_D3) && (state != ACPI_STATE_D0)) 321 return_ACPI_STATUS(AE_BAD_PARAMETER); /* can only go to D0 from D3 */ 322 323 /* find transition mechanism(s) */ 324 switch(state) { 325 case ACPI_STATE_D0: 326 method_name = "_PS0"; 327 reslist_name = "_PR0"; 328 break; 329 case ACPI_STATE_D1: 330 method_name = "_PS1"; 331 reslist_name = "_PR1"; 332 break; 333 case ACPI_STATE_D2: 334 method_name = "_PS2"; 335 reslist_name = "_PR2"; 336 break; 337 case ACPI_STATE_D3: 338 method_name = "_PS3"; 339 reslist_name = "_PR3"; 340 break; 341 default: 342 return_ACPI_STATUS(AE_BAD_PARAMETER); 343 } 344 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "setup to switch %s D%d -> D%d\n", 345 acpi_name(consumer), pc->ac_state, state)); 346 347 /* 348 * Verify that this state is supported, ie. one of method or 349 * reslist must be present. We need to do this before we go 350 * dereferencing resources (since we might be trying to go to 351 * a state we don't support). 352 * 353 * Note that if any states are supported, the device has to 354 * support D0 and D3. It's never an error to try to go to 355 * D0. 356 */ 357 reslist_buffer.Pointer = NULL; 358 reslist_object = NULL; 359 if (ACPI_FAILURE(AcpiGetHandle(consumer, method_name, &method_handle))) 360 method_handle = NULL; 361 if (ACPI_FAILURE(AcpiGetHandle(consumer, reslist_name, &reslist_handle))) 362 reslist_handle = NULL; 363 if ((reslist_handle == NULL) && (method_handle == NULL)) { 364 if (state == ACPI_STATE_D0) { 365 pc->ac_state = ACPI_STATE_D0; 366 return_ACPI_STATUS(AE_OK); 367 } 368 if (state != ACPI_STATE_D3) { 369 goto bad; 370 } 371 372 /* turn off the resources listed in _PR0 to go to D3. */ 373 if (ACPI_FAILURE(AcpiGetHandle(consumer, "_PR0", &pr0_handle))) { 374 goto bad; 375 } 376 reslist_buffer.Length = ACPI_ALLOCATE_BUFFER; 377 if (ACPI_FAILURE(status = AcpiEvaluateObject(pr0_handle, NULL, NULL, &reslist_buffer))) { 378 goto bad; 379 } 380 reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer; 381 if ((reslist_object->Type != ACPI_TYPE_PACKAGE) || 382 (reslist_object->Package.Count == 0)) { 383 goto bad; 384 } 385 AcpiOsFree(reslist_buffer.Pointer); 386 reslist_buffer.Pointer = NULL; 387 reslist_object = NULL; 388 } 389 390 /* 391 * Check that we can actually fetch the list of power resources 392 */ 393 if (reslist_handle != NULL) { 394 reslist_buffer.Length = ACPI_ALLOCATE_BUFFER; 395 if (ACPI_FAILURE(status = AcpiEvaluateObject(reslist_handle, NULL, NULL, &reslist_buffer))) { 396 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't evaluate resource list %s\n", 397 acpi_name(reslist_handle))); 398 goto out; 399 } 400 reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer; 401 if (reslist_object->Type != ACPI_TYPE_PACKAGE) { 402 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "resource list is not ACPI_TYPE_PACKAGE (%d)\n", 403 reslist_object->Type)); 404 status = AE_TYPE; 405 goto out; 406 } 407 } 408 409 /* 410 * Now we are ready to switch, so kill off any current power resource references. 411 */ 412 res_changed = 0; 413 while((pr = TAILQ_FIRST(&pc->ac_references)) != NULL) { 414 res_changed = 1; 415 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "removing reference to %s\n", acpi_name(pr->ar_resource->ap_resource))); 416 TAILQ_REMOVE(&pr->ar_resource->ap_references, pr, ar_rlink); 417 TAILQ_REMOVE(&pc->ac_references, pr, ar_clink); 418 free(pr, M_ACPIPWR); 419 } 420 421 /* 422 * Add new power resource references, if we have any. Traverse the 423 * package that we got from evaluating reslist_handle, and look up each 424 * of the resources that are referenced. 425 */ 426 if (reslist_object != NULL) { 427 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "referencing %d new resources\n", 428 reslist_object->Package.Count)); 429 acpi_ForeachPackageObject(reslist_object, acpi_pwr_reference_resource, pc); 430 res_changed = 1; 431 } 432 433 /* 434 * If we changed anything in the resource list, we need to run a switch 435 * pass now. 436 */ 437 if (ACPI_FAILURE(status = acpi_pwr_switch_power())) { 438 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to correctly switch resources to move %s to D%d\n", 439 acpi_name(consumer), state)); 440 goto out; /* XXX is this appropriate? Should we return to previous state? */ 441 } 442 443 /* invoke power state switch method (if present) */ 444 if (method_handle != NULL) { 445 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "invoking state transition method %s\n", 446 acpi_name(method_handle))); 447 if (ACPI_FAILURE(status = AcpiEvaluateObject(method_handle, NULL, NULL, NULL))) { 448 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to set state - %s\n", 449 AcpiFormatException(status))); 450 pc->ac_state = ACPI_STATE_UNKNOWN; 451 goto out; /* XXX Should we return to previous state? */ 452 } 453 } 454 455 /* transition was successful */ 456 pc->ac_state = state; 457 return_ACPI_STATUS(AE_OK); 458 459 bad: 460 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "attempt to set unsupported state D%d\n", 461 state)); 462 status = AE_BAD_PARAMETER; 463 464 out: 465 if (reslist_buffer.Pointer != NULL) 466 AcpiOsFree(reslist_buffer.Pointer); 467 return_ACPI_STATUS(status); 468 } 469 470 /* 471 * Called to create a reference between a power consumer and a power resource 472 * identified in the object. 473 */ 474 static void 475 acpi_pwr_reference_resource(ACPI_OBJECT *obj, void *arg) 476 { 477 struct acpi_powerconsumer *pc = (struct acpi_powerconsumer *)arg; 478 struct acpi_powerreference *pr; 479 struct acpi_powerresource *rp; 480 ACPI_HANDLE res; 481 ACPI_STATUS status; 482 483 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 484 485 /* check the object type */ 486 switch (obj->Type) { 487 case ACPI_TYPE_ANY: 488 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "building reference from %s to %s\n", 489 acpi_name(pc->ac_consumer), acpi_name(obj->Reference.Handle))); 490 491 res = obj->Reference.Handle; 492 break; 493 494 case ACPI_TYPE_STRING: 495 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "building reference from %s to %s\n", 496 acpi_name(pc->ac_consumer), obj->String.Pointer)); 497 498 /* get the handle of the resource */ 499 if (ACPI_FAILURE(status = AcpiGetHandle(NULL, obj->String.Pointer, &res))) { 500 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "couldn't find power resource %s\n", 501 obj->String.Pointer)); 502 return_VOID; 503 } 504 break; 505 506 default: 507 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "don't know how to create a power reference to object type %d\n", 508 obj->Type)); 509 return_VOID; 510 } 511 512 /* create/look up the resource */ 513 if (ACPI_FAILURE(status = acpi_pwr_register_resource(res))) { 514 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "couldn't register power resource %s - %s\n", 515 obj->String.Pointer, AcpiFormatException(status))); 516 return_VOID; 517 } 518 if ((rp = acpi_pwr_find_resource(res)) == NULL) { 519 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "power resource list corrupted\n")); 520 return_VOID; 521 } 522 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "found power resource %s\n", acpi_name(rp->ap_resource))); 523 524 /* create a reference between the consumer and resource */ 525 if ((pr = malloc(sizeof(*pr), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) { 526 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "couldn't allocate memory for a power consumer reference\n")); 527 return_VOID; 528 } 529 pr->ar_consumer = pc; 530 pr->ar_resource = rp; 531 TAILQ_INSERT_TAIL(&pc->ac_references, pr, ar_clink); 532 TAILQ_INSERT_TAIL(&rp->ap_references, pr, ar_rlink); 533 534 return_VOID; 535 } 536 537 538 /* 539 * Switch power resources to conform to the desired state. 540 * 541 * Consumers may have modified the power resource list in an arbitrary 542 * fashion; we sweep it in sequence order. 543 */ 544 static ACPI_STATUS 545 acpi_pwr_switch_power(void) 546 { 547 struct acpi_powerresource *rp; 548 ACPI_STATUS status; 549 int cur; 550 551 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 552 553 /* 554 * Sweep the list forwards turning things on. 555 */ 556 TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) { 557 if (TAILQ_FIRST(&rp->ap_references) == NULL) { 558 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s has no references, not turning on\n", 559 acpi_name(rp->ap_resource))); 560 continue; 561 } 562 563 /* we could cache this if we trusted it not to change under us */ 564 if (ACPI_FAILURE(status = acpi_EvaluateInteger(rp->ap_resource, "_STA", &cur))) { 565 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n", 566 acpi_name(rp->ap_resource), status)); 567 continue; /* XXX is this correct? Always switch if in doubt? */ 568 } 569 570 /* 571 * Switch if required. Note that we ignore the result of the switch 572 * effort; we don't know what to do if it fails, so checking wouldn't 573 * help much. 574 */ 575 if (cur != ACPI_PWR_ON) { 576 if (ACPI_FAILURE(status = AcpiEvaluateObject(rp->ap_resource, "_ON", NULL, NULL))) { 577 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to switch %s on - %s\n", 578 acpi_name(rp->ap_resource), AcpiFormatException(status))); 579 } else { 580 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s on\n", acpi_name(rp->ap_resource))); 581 } 582 } else { 583 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already on\n", acpi_name(rp->ap_resource))); 584 } 585 } 586 587 /* 588 * Sweep the list backwards turning things off. 589 */ 590 TAILQ_FOREACH_REVERSE(rp, &acpi_powerresources, acpi_powerresource_list, ap_link) { 591 if (TAILQ_FIRST(&rp->ap_references) != NULL) { 592 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s has references, not turning off\n", 593 acpi_name(rp->ap_resource))); 594 continue; 595 } 596 597 /* we could cache this if we trusted it not to change under us */ 598 if (ACPI_FAILURE(status = acpi_EvaluateInteger(rp->ap_resource, "_STA", &cur))) { 599 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n", 600 acpi_name(rp->ap_resource), status)); 601 continue; /* XXX is this correct? Always switch if in doubt? */ 602 } 603 604 /* 605 * Switch if required. Note that we ignore the result of the switch 606 * effort; we don't know what to do if it fails, so checking wouldn't 607 * help much. 608 */ 609 if (cur != ACPI_PWR_OFF) { 610 if (ACPI_FAILURE(status = AcpiEvaluateObject(rp->ap_resource, "_OFF", NULL, NULL))) { 611 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to switch %s off - %s\n", 612 acpi_name(rp->ap_resource), AcpiFormatException(status))); 613 } else { 614 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s off\n", acpi_name(rp->ap_resource))); 615 } 616 } else { 617 ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already off\n", acpi_name(rp->ap_resource))); 618 } 619 } 620 return_ACPI_STATUS(AE_OK); 621 } 622 623 /* 624 * Find a power resource's control structure. 625 */ 626 static struct acpi_powerresource * 627 acpi_pwr_find_resource(ACPI_HANDLE res) 628 { 629 struct acpi_powerresource *rp; 630 631 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 632 633 TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) 634 if (rp->ap_resource == res) 635 break; 636 return_PTR(rp); 637 } 638 639 /* 640 * Find a power consumer's control structure. 641 */ 642 static struct acpi_powerconsumer * 643 acpi_pwr_find_consumer(ACPI_HANDLE consumer) 644 { 645 struct acpi_powerconsumer *pc; 646 647 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 648 649 TAILQ_FOREACH(pc, &acpi_powerconsumers, ac_link) 650 if (pc->ac_consumer == consumer) 651 break; 652 return_PTR(pc); 653 } 654 655