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