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