acpi_powerres.c revision 150003
1214503Srpaulo/*- 2214503Srpaulo * Copyright (c) 2001 Michael Smith 3214503Srpaulo * All rights reserved. 4214503Srpaulo * 5214503Srpaulo * Redistribution and use in source and binary forms, with or without 6214503Srpaulo * modification, are permitted provided that the following conditions 7214503Srpaulo * are met: 8214503Srpaulo * 1. Redistributions of source code must retain the above copyright 9214503Srpaulo * notice, this list of conditions and the following disclaimer. 10214503Srpaulo * 2. Redistributions in binary form must reproduce the above copyright 11214503Srpaulo * notice, this list of conditions and the following disclaimer in the 12214503Srpaulo * documentation and/or other materials provided with the distribution. 13214503Srpaulo * 14214503Srpaulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15214503Srpaulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16214503Srpaulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17214503Srpaulo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18214503Srpaulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19214503Srpaulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20214503Srpaulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21214503Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22214503Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23214503Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24214503Srpaulo * SUCH DAMAGE. 25214503Srpaulo */ 26214503Srpaulo 27214503Srpaulo#include <sys/cdefs.h> 28214503Srpaulo__FBSDID("$FreeBSD: head/sys/dev/acpica/acpi_powerres.c 150003 2005-09-11 18:39:03Z obrien $"); 29214503Srpaulo 30214503Srpaulo#include "opt_acpi.h" 31214503Srpaulo#include <sys/param.h> 32214503Srpaulo#include <sys/kernel.h> 33214503Srpaulo#include <sys/malloc.h> 34214503Srpaulo#include <sys/bus.h> 35214503Srpaulo 36214503Srpaulo#include <contrib/dev/acpica/acpi.h> 37214503Srpaulo#include <dev/acpica/acpivar.h> 38214503Srpaulo 39214503Srpaulo/* 40214503Srpaulo * ACPI power resource management. 41214503Srpaulo * 42214503Srpaulo * Power resource behaviour is slightly complicated by the fact that 43214503Srpaulo * a single power resource may provide power for more than one device. 44214503Srpaulo * Thus, we must track the device(s) being powered by a given power 45214503Srpaulo * resource, and only deactivate it when there are no powered devices. 46214503Srpaulo * 47214503Srpaulo * Note that this only manages resources for known devices. There is an 48214503Srpaulo * ugly case where we may turn of power to a device which is in use because 49214503Srpaulo * we don't know that it depends on a given resource. We should perhaps 50214503Srpaulo * try to be smarter about this, but a more complete solution would involve 51214503Srpaulo * scanning all of the ACPI namespace to find devices we're not currently 52214503Srpaulo * aware of, and this raises questions about whether they should be left 53214503Srpaulo * on, turned off, etc. 54214503Srpaulo */ 55214503Srpaulo 56214503SrpauloMALLOC_DEFINE(M_ACPIPWR, "acpipwr", "ACPI power resources"); 57214503Srpaulo 58214503Srpaulo/* Hooks for the ACPI CA debugging infrastructure */ 59214503Srpaulo#define _COMPONENT ACPI_POWERRES 60214503SrpauloACPI_MODULE_NAME("POWERRES") 61214503Srpaulo 62214503Srpaulo/* Return values from _STA on a power resource */ 63214503Srpaulo#define ACPI_PWR_OFF 0 64214503Srpaulo#define ACPI_PWR_ON 1 65214503Srpaulo#define ACPI_PWR_UNK (-1) 66214503Srpaulo 67214503Srpaulo/* A relationship between a power resource and a consumer. */ 68214503Srpaulostruct acpi_powerreference { 69214503Srpaulo struct acpi_powerconsumer *ar_consumer; 70214503Srpaulo struct acpi_powerresource *ar_resource; 71214503Srpaulo TAILQ_ENTRY(acpi_powerreference) ar_rlink; /* link on resource list */ 72214503Srpaulo TAILQ_ENTRY(acpi_powerreference) ar_clink; /* link on consumer */ 73214503Srpaulo}; 74214503Srpaulo 75214503Srpaulo/* A power-managed device. */ 76214503Srpaulostruct acpi_powerconsumer { 77214503Srpaulo /* Device which is powered */ 78214503Srpaulo ACPI_HANDLE ac_consumer; 79214503Srpaulo int ac_state; 80214503Srpaulo TAILQ_ENTRY(acpi_powerconsumer) ac_link; 81214503Srpaulo TAILQ_HEAD(,acpi_powerreference) ac_references; 82214503Srpaulo}; 83214503Srpaulo 84214503Srpaulo/* A power resource. */ 85214503Srpaulostruct acpi_powerresource { 86214503Srpaulo TAILQ_ENTRY(acpi_powerresource) ap_link; 87252726Srpaulo TAILQ_HEAD(,acpi_powerreference) ap_references; 88252726Srpaulo ACPI_HANDLE ap_resource; 89252726Srpaulo ACPI_INTEGER ap_systemlevel; 90252726Srpaulo ACPI_INTEGER ap_order; 91252726Srpaulo int ap_state; 92214503Srpaulo}; 93252726Srpaulo 94252726Srpaulostatic TAILQ_HEAD(acpi_powerresource_list, acpi_powerresource) 95252726Srpaulo acpi_powerresources; 96214503Srpaulostatic TAILQ_HEAD(acpi_powerconsumer_list, acpi_powerconsumer) 97214503Srpaulo acpi_powerconsumers; 98214503SrpauloACPI_SERIAL_DECL(powerres, "ACPI power resources"); 99214503Srpaulo 100214503Srpaulostatic ACPI_STATUS acpi_pwr_register_consumer(ACPI_HANDLE consumer); 101214503Srpaulo#ifdef notyet 102214503Srpaulostatic ACPI_STATUS acpi_pwr_deregister_consumer(ACPI_HANDLE consumer); 103214503Srpaulo#endif /* notyet */ 104214503Srpaulostatic ACPI_STATUS acpi_pwr_register_resource(ACPI_HANDLE res); 105214503Srpaulo#ifdef notyet 106214503Srpaulostatic ACPI_STATUS acpi_pwr_deregister_resource(ACPI_HANDLE res); 107214503Srpaulo#endif /* notyet */ 108214503Srpaulostatic void acpi_pwr_reference_resource(ACPI_OBJECT *obj, 109252726Srpaulo void *arg); 110252726Srpaulostatic int acpi_pwr_dereference_resource(struct acpi_powerconsumer 111214503Srpaulo *pc); 112252726Srpaulostatic ACPI_STATUS acpi_pwr_switch_power(void); 113214503Srpaulostatic struct acpi_powerresource 114214503Srpaulo *acpi_pwr_find_resource(ACPI_HANDLE res); 115214503Srpaulostatic struct acpi_powerconsumer 116252726Srpaulo *acpi_pwr_find_consumer(ACPI_HANDLE consumer); 117252726Srpaulo 118252726Srpaulo/* Initialise our lists. */ 119214503Srpaulostatic void 120214503Srpauloacpi_pwr_init(void *junk) 121214503Srpaulo{ 122214503Srpaulo TAILQ_INIT(&acpi_powerresources); 123252726Srpaulo TAILQ_INIT(&acpi_powerconsumers); 124214503Srpaulo} 125214503SrpauloSYSINIT(acpi_powerresource, SI_SUB_TUNABLES, SI_ORDER_ANY, acpi_pwr_init, NULL); 126214503Srpaulo 127214503Srpaulo/* 128214503Srpaulo * Register a power resource. 129214503Srpaulo * 130214503Srpaulo * It's OK to call this if we already know about the resource. 131214503Srpaulo */ 132214503Srpaulostatic ACPI_STATUS 133214503Srpauloacpi_pwr_register_resource(ACPI_HANDLE res) 134214503Srpaulo{ 135214503Srpaulo ACPI_STATUS status; 136214503Srpaulo ACPI_BUFFER buf; 137214503Srpaulo ACPI_OBJECT *obj; 138214503Srpaulo struct acpi_powerresource *rp, *srp; 139214503Srpaulo 140214503Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 141214503Srpaulo ACPI_SERIAL_ASSERT(powerres); 142214503Srpaulo 143214503Srpaulo rp = NULL; 144214503Srpaulo buf.Pointer = NULL; 145214503Srpaulo 146214503Srpaulo /* Look to see if we know about this resource */ 147214503Srpaulo if (acpi_pwr_find_resource(res) != NULL) 148214503Srpaulo return_ACPI_STATUS (AE_OK); /* already know about it */ 149214503Srpaulo 150214503Srpaulo /* Allocate a new resource */ 151214503Srpaulo if ((rp = malloc(sizeof(*rp), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) { 152214503Srpaulo status = AE_NO_MEMORY; 153214503Srpaulo goto out; 154214503Srpaulo } 155214503Srpaulo TAILQ_INIT(&rp->ap_references); 156214503Srpaulo rp->ap_resource = res; 157214503Srpaulo 158214503Srpaulo /* Get the Power Resource object */ 159214503Srpaulo buf.Length = ACPI_ALLOCATE_BUFFER; 160214503Srpaulo if (ACPI_FAILURE(status = AcpiEvaluateObject(res, NULL, NULL, &buf))) { 161214503Srpaulo ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "no power resource object\n")); 162214503Srpaulo goto out; 163214503Srpaulo } 164214503Srpaulo obj = buf.Pointer; 165214503Srpaulo if (obj->Type != ACPI_TYPE_POWER) { 166214503Srpaulo ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 167214503Srpaulo "questionable power resource object %s\n", 168214503Srpaulo acpi_name(res))); 169214503Srpaulo status = AE_TYPE; 170214503Srpaulo goto out; 171214503Srpaulo } 172214503Srpaulo rp->ap_systemlevel = obj->PowerResource.SystemLevel; 173214503Srpaulo rp->ap_order = obj->PowerResource.ResourceOrder; 174214503Srpaulo rp->ap_state = ACPI_PWR_UNK; 175214503Srpaulo 176214503Srpaulo /* Sort the resource into the list */ 177214503Srpaulo status = AE_OK; 178214503Srpaulo srp = TAILQ_FIRST(&acpi_powerresources); 179214503Srpaulo if (srp == NULL || rp->ap_order < srp->ap_order) { 180214503Srpaulo TAILQ_INSERT_HEAD(&acpi_powerresources, rp, ap_link); 181214503Srpaulo goto done; 182214503Srpaulo } 183214503Srpaulo TAILQ_FOREACH(srp, &acpi_powerresources, ap_link) { 184214503Srpaulo if (rp->ap_order < srp->ap_order) { 185214503Srpaulo TAILQ_INSERT_BEFORE(srp, rp, ap_link); 186214503Srpaulo goto done; 187214503Srpaulo } 188214503Srpaulo } 189214503Srpaulo TAILQ_INSERT_TAIL(&acpi_powerresources, rp, ap_link); 190214503Srpaulo 191214503Srpaulo done: 192214503Srpaulo ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 193214503Srpaulo "registered power resource %s\n", acpi_name(res))); 194214503Srpaulo 195214503Srpaulo out: 196214503Srpaulo if (buf.Pointer != NULL) 197214503Srpaulo AcpiOsFree(buf.Pointer); 198214503Srpaulo if (ACPI_FAILURE(status) && rp != NULL) 199214503Srpaulo free(rp, M_ACPIPWR); 200214503Srpaulo return_ACPI_STATUS (status); 201214503Srpaulo} 202214503Srpaulo 203214503Srpaulo#ifdef notyet 204214503Srpaulo/* 205214503Srpaulo * Deregister a power resource. 206214503Srpaulo */ 207214503Srpaulostatic ACPI_STATUS 208214503Srpauloacpi_pwr_deregister_resource(ACPI_HANDLE res) 209252726Srpaulo{ 210252726Srpaulo struct acpi_powerresource *rp; 211252726Srpaulo 212252726Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 213252726Srpaulo ACPI_SERIAL_ASSERT(powerres); 214252726Srpaulo 215252726Srpaulo rp = NULL; 216214503Srpaulo 217214503Srpaulo /* Find the resource */ 218214503Srpaulo if ((rp = acpi_pwr_find_resource(res)) == NULL) 219214503Srpaulo return_ACPI_STATUS (AE_BAD_PARAMETER); 220214503Srpaulo 221214503Srpaulo /* Check that there are no consumers referencing this resource */ 222214503Srpaulo if (TAILQ_FIRST(&rp->ap_references) != NULL) 223214503Srpaulo return_ACPI_STATUS (AE_BAD_PARAMETER); 224214503Srpaulo 225214503Srpaulo /* Pull it off the list and free it */ 226214503Srpaulo TAILQ_REMOVE(&acpi_powerresources, rp, ap_link); 227214503Srpaulo free(rp, M_ACPIPWR); 228214503Srpaulo 229214503Srpaulo ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power resource %s\n", 230214503Srpaulo acpi_name(res))); 231214503Srpaulo 232214503Srpaulo return_ACPI_STATUS (AE_OK); 233214503Srpaulo} 234214503Srpaulo#endif /* notyet */ 235214503Srpaulo 236214503Srpaulo/* 237214503Srpaulo * Register a power consumer. 238214503Srpaulo * 239214503Srpaulo * It's OK to call this if we already know about the consumer. 240214503Srpaulo */ 241214503Srpaulostatic ACPI_STATUS 242214503Srpauloacpi_pwr_register_consumer(ACPI_HANDLE consumer) 243214503Srpaulo{ 244214503Srpaulo struct acpi_powerconsumer *pc; 245214503Srpaulo 246214503Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 247214503Srpaulo ACPI_SERIAL_ASSERT(powerres); 248214503Srpaulo 249214503Srpaulo /* Check to see whether we know about this consumer already */ 250214503Srpaulo if ((pc = acpi_pwr_find_consumer(consumer)) != NULL) 251214503Srpaulo return_ACPI_STATUS (AE_OK); 252214503Srpaulo 253214503Srpaulo /* Allocate a new power consumer */ 254214503Srpaulo if ((pc = malloc(sizeof(*pc), M_ACPIPWR, M_NOWAIT)) == NULL) 255214503Srpaulo return_ACPI_STATUS (AE_NO_MEMORY); 256214503Srpaulo TAILQ_INSERT_HEAD(&acpi_powerconsumers, pc, ac_link); 257214503Srpaulo TAILQ_INIT(&pc->ac_references); 258214503Srpaulo pc->ac_consumer = consumer; 259214503Srpaulo 260214503Srpaulo /* XXX we should try to find its current state */ 261214503Srpaulo pc->ac_state = ACPI_STATE_UNKNOWN; 262214503Srpaulo 263214503Srpaulo ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "registered power consumer %s\n", 264214503Srpaulo acpi_name(consumer))); 265214503Srpaulo 266214503Srpaulo return_ACPI_STATUS (AE_OK); 267214503Srpaulo} 268214503Srpaulo 269214503Srpaulo#ifdef notyet 270214503Srpaulo/* 271214503Srpaulo * Deregister a power consumer. 272214503Srpaulo * 273214503Srpaulo * This should only be done once the consumer has been powered off. 274214503Srpaulo * (XXX is this correct? Check once implemented) 275214503Srpaulo */ 276214503Srpaulostatic ACPI_STATUS 277214503Srpauloacpi_pwr_deregister_consumer(ACPI_HANDLE consumer) 278214503Srpaulo{ 279214503Srpaulo struct acpi_powerconsumer *pc; 280214503Srpaulo 281214503Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 282214503Srpaulo ACPI_SERIAL_ASSERT(powerres); 283214503Srpaulo 284214503Srpaulo /* Find the consumer */ 285214503Srpaulo if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) 286214503Srpaulo return_ACPI_STATUS (AE_BAD_PARAMETER); 287214503Srpaulo 288214503Srpaulo /* Make sure the consumer's not referencing anything right now */ 289214503Srpaulo if (TAILQ_FIRST(&pc->ac_references) != NULL) 290214503Srpaulo return_ACPI_STATUS (AE_BAD_PARAMETER); 291214503Srpaulo 292214503Srpaulo /* Pull the consumer off the list and free it */ 293214503Srpaulo TAILQ_REMOVE(&acpi_powerconsumers, pc, ac_link); 294214503Srpaulo free(pc, M_ACPIPWR); 295214503Srpaulo 296214503Srpaulo ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power consumer %s\n", 297214503Srpaulo acpi_name(consumer))); 298214503Srpaulo 299214503Srpaulo return_ACPI_STATUS (AE_OK); 300214503Srpaulo} 301214503Srpaulo#endif /* notyet */ 302214503Srpaulo 303214503Srpaulo/* 304214503Srpaulo * Set a power consumer to a particular power state. 305214503Srpaulo */ 306214503SrpauloACPI_STATUS 307214503Srpauloacpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state) 308214503Srpaulo{ 309214503Srpaulo struct acpi_powerconsumer *pc; 310214503Srpaulo ACPI_HANDLE method_handle, reslist_handle, pr0_handle; 311214503Srpaulo ACPI_BUFFER reslist_buffer; 312214503Srpaulo ACPI_OBJECT *reslist_object; 313214503Srpaulo ACPI_STATUS status; 314214503Srpaulo char *method_name, *reslist_name; 315214503Srpaulo int res_changed; 316214503Srpaulo 317214503Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 318214503Srpaulo 319214503Srpaulo /* It's never ok to switch a non-existent consumer. */ 320214503Srpaulo if (consumer == NULL) 321214503Srpaulo return_ACPI_STATUS (AE_NOT_FOUND); 322214503Srpaulo reslist_buffer.Pointer = NULL; 323214503Srpaulo reslist_object = NULL; 324214503Srpaulo ACPI_SERIAL_BEGIN(powerres); 325214503Srpaulo 326214503Srpaulo /* Find the consumer */ 327214503Srpaulo if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) { 328214503Srpaulo if (ACPI_FAILURE(status = acpi_pwr_register_consumer(consumer))) 329214503Srpaulo goto out; 330214503Srpaulo if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) 331214503Srpaulo panic("acpi added power consumer but can't find it"); 332214503Srpaulo } 333214503Srpaulo 334214503Srpaulo /* Check for valid transitions. We can only go to D0 from D3. */ 335214503Srpaulo status = AE_BAD_PARAMETER; 336214503Srpaulo if (pc->ac_state == ACPI_STATE_D3 && state != ACPI_STATE_D0) 337214503Srpaulo goto out; 338214503Srpaulo 339214503Srpaulo /* Find transition mechanism(s) */ 340214503Srpaulo switch (state) { 341214503Srpaulo case ACPI_STATE_D0: 342214503Srpaulo method_name = "_PS0"; 343214503Srpaulo reslist_name = "_PR0"; 344214503Srpaulo break; 345214503Srpaulo case ACPI_STATE_D1: 346214503Srpaulo method_name = "_PS1"; 347214503Srpaulo reslist_name = "_PR1"; 348214503Srpaulo break; 349214503Srpaulo case ACPI_STATE_D2: 350214503Srpaulo method_name = "_PS2"; 351214503Srpaulo reslist_name = "_PR2"; 352214503Srpaulo break; 353214503Srpaulo case ACPI_STATE_D3: 354214503Srpaulo method_name = "_PS3"; 355214503Srpaulo reslist_name = "_PR3"; 356214503Srpaulo break; 357214503Srpaulo default: 358214503Srpaulo goto out; 359252726Srpaulo } 360252726Srpaulo ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "setup to switch %s D%d -> D%d\n", 361252726Srpaulo acpi_name(consumer), pc->ac_state, state)); 362252726Srpaulo 363252726Srpaulo /* 364252726Srpaulo * Verify that this state is supported, ie. one of method or 365214503Srpaulo * reslist must be present. We need to do this before we go 366252726Srpaulo * dereferencing resources (since we might be trying to go to 367252726Srpaulo * a state we don't support). 368252726Srpaulo * 369252726Srpaulo * Note that if any states are supported, the device has to 370252726Srpaulo * support D0 and D3. It's never an error to try to go to 371214503Srpaulo * D0. 372214503Srpaulo */ 373214503Srpaulo if (ACPI_FAILURE(AcpiGetHandle(consumer, method_name, &method_handle))) 374214503Srpaulo method_handle = NULL; 375214503Srpaulo if (ACPI_FAILURE(AcpiGetHandle(consumer, reslist_name, &reslist_handle))) 376214503Srpaulo reslist_handle = NULL; 377214503Srpaulo if (reslist_handle == NULL && method_handle == NULL) { 378214503Srpaulo if (state == ACPI_STATE_D0) { 379214503Srpaulo pc->ac_state = ACPI_STATE_D0; 380214503Srpaulo status = AE_OK; 381252726Srpaulo goto out; 382252726Srpaulo } 383252726Srpaulo if (state != ACPI_STATE_D3) { 384252726Srpaulo ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 385252726Srpaulo "attempt to set unsupported state D%d\n", state)); 386252726Srpaulo goto out; 387252726Srpaulo } 388252726Srpaulo 389252726Srpaulo /* 390214503Srpaulo * Turn off the resources listed in _PR0 to go to D3. If there is 391214503Srpaulo * no _PR0 method, this object doesn't support ACPI power states. 392214503Srpaulo */ 393214503Srpaulo if (ACPI_FAILURE(AcpiGetHandle(consumer, "_PR0", &pr0_handle))) { 394214503Srpaulo status = AE_NOT_FOUND; 395214503Srpaulo ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 396214503Srpaulo "device missing _PR0 (desired state was D%d)\n", state)); 397214503Srpaulo goto out; 398214503Srpaulo } 399214503Srpaulo reslist_buffer.Length = ACPI_ALLOCATE_BUFFER; 400214503Srpaulo status = AcpiEvaluateObject(pr0_handle, NULL, NULL, &reslist_buffer); 401214503Srpaulo if (ACPI_FAILURE(status)) { 402214503Srpaulo ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 403214503Srpaulo "can't evaluate _PR0 for device %s, state D%d\n", 404214503Srpaulo acpi_name(consumer), state)); 405214503Srpaulo goto out; 406214503Srpaulo } 407214503Srpaulo reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer; 408214503Srpaulo if (!ACPI_PKG_VALID(reslist_object, 1)) { 409214503Srpaulo ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 410214503Srpaulo "invalid package object for state D%d\n", state)); 411214503Srpaulo status = AE_TYPE; 412214503Srpaulo goto out; 413214503Srpaulo } 414214503Srpaulo AcpiOsFree(reslist_buffer.Pointer); 415214503Srpaulo reslist_buffer.Pointer = NULL; 416214503Srpaulo reslist_object = NULL; 417214503Srpaulo } 418214503Srpaulo 419214503Srpaulo /* 420214503Srpaulo * Check that we can actually fetch the list of power resources 421214503Srpaulo */ 422214503Srpaulo if (reslist_handle != NULL) { 423214503Srpaulo reslist_buffer.Length = ACPI_ALLOCATE_BUFFER; 424214503Srpaulo status = AcpiEvaluateObject(reslist_handle, NULL, NULL, 425214503Srpaulo &reslist_buffer); 426214503Srpaulo if (ACPI_FAILURE(status)) { 427214503Srpaulo ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 428214503Srpaulo "can't evaluate resource list %s\n", 429214503Srpaulo acpi_name(reslist_handle))); 430214503Srpaulo goto out; 431214503Srpaulo } 432214503Srpaulo reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer; 433252726Srpaulo if (reslist_object->Type != ACPI_TYPE_PACKAGE) { 434252726Srpaulo ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 435252726Srpaulo "resource list is not ACPI_TYPE_PACKAGE (%d)\n", 436252726Srpaulo reslist_object->Type)); 437252726Srpaulo status = AE_TYPE; 438252726Srpaulo goto out; 439252726Srpaulo } 440252726Srpaulo } 441252726Srpaulo 442252726Srpaulo /* 443252726Srpaulo * Now we are ready to switch, so kill off any current power 444252726Srpaulo * resource references. 445252726Srpaulo */ 446252726Srpaulo res_changed = acpi_pwr_dereference_resource(pc); 447252726Srpaulo 448252726Srpaulo /* 449252726Srpaulo * Add new power resource references, if we have any. Traverse the 450252726Srpaulo * package that we got from evaluating reslist_handle, and look up each 451252726Srpaulo * of the resources that are referenced. 452252726Srpaulo */ 453252726Srpaulo if (reslist_object != NULL) { 454252726Srpaulo ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "referencing %d new resources\n", 455252726Srpaulo reslist_object->Package.Count)); 456252726Srpaulo acpi_ForeachPackageObject(reslist_object, acpi_pwr_reference_resource, 457252726Srpaulo pc); 458252726Srpaulo res_changed = 1; 459252726Srpaulo } 460252726Srpaulo 461252726Srpaulo /* 462252726Srpaulo * If we changed anything in the resource list, we need to run a switch 463252726Srpaulo * pass now. 464252726Srpaulo */ 465252726Srpaulo if (ACPI_FAILURE(status = acpi_pwr_switch_power())) { 466252726Srpaulo ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 467252726Srpaulo "failed to switch resources from %s to D%d\n", 468252726Srpaulo acpi_name(consumer), state)); 469252726Srpaulo 470252726Srpaulo /* XXX is this appropriate? Should we return to previous state? */ 471252726Srpaulo goto out; 472252726Srpaulo } 473252726Srpaulo 474252726Srpaulo /* Invoke power state switch method (if present) */ 475252726Srpaulo if (method_handle != NULL) { 476252726Srpaulo ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 477252726Srpaulo "invoking state transition method %s\n", 478252726Srpaulo acpi_name(method_handle))); 479252726Srpaulo status = AcpiEvaluateObject(method_handle, NULL, NULL, NULL); 480252726Srpaulo if (ACPI_FAILURE(status)) { 481252726Srpaulo ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to set state - %s\n", 482252726Srpaulo AcpiFormatException(status))); 483252726Srpaulo pc->ac_state = ACPI_STATE_UNKNOWN; 484252726Srpaulo 485252726Srpaulo /* XXX Should we return to previous state? */ 486252726Srpaulo goto out; 487252726Srpaulo } 488252726Srpaulo } 489252726Srpaulo 490252726Srpaulo /* Transition was successful */ 491252726Srpaulo pc->ac_state = state; 492252726Srpaulo status = AE_OK; 493252726Srpaulo 494252726Srpauloout: 495252726Srpaulo ACPI_SERIAL_END(powerres); 496252726Srpaulo if (reslist_buffer.Pointer != NULL) 497252726Srpaulo AcpiOsFree(reslist_buffer.Pointer); 498252726Srpaulo return_ACPI_STATUS (status); 499252726Srpaulo} 500252726Srpaulo 501252726Srpaulo/* Enable or disable a power resource for wake */ 502252726SrpauloACPI_STATUS 503252726Srpauloacpi_pwr_wake_enable(ACPI_HANDLE consumer, int enable) 504252726Srpaulo{ 505252726Srpaulo ACPI_STATUS status; 506252726Srpaulo struct acpi_powerconsumer *pc; 507252726Srpaulo struct acpi_prw_data prw; 508252726Srpaulo int i; 509252726Srpaulo 510252726Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 511252726Srpaulo 512252726Srpaulo if (consumer == NULL) 513252726Srpaulo return (AE_BAD_PARAMETER); 514252726Srpaulo 515252726Srpaulo ACPI_SERIAL_BEGIN(powerres); 516252726Srpaulo if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) { 517252726Srpaulo if (ACPI_FAILURE(status = acpi_pwr_register_consumer(consumer))) 518252726Srpaulo goto out; 519252726Srpaulo if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) 520252726Srpaulo panic("acpi wake added power consumer but can't find it"); 521252726Srpaulo } 522252726Srpaulo 523252726Srpaulo status = AE_OK; 524252726Srpaulo if (acpi_parse_prw(consumer, &prw) != 0) 525252726Srpaulo goto out; 526252726Srpaulo for (i = 0; i < prw.power_res_count; i++) 527252726Srpaulo if (enable) 528252726Srpaulo acpi_pwr_reference_resource(&prw.power_res[i], pc); 529252726Srpaulo else 530252726Srpaulo acpi_pwr_dereference_resource(pc); 531252726Srpaulo 532252726Srpaulo if (prw.power_res_count > 0) 533252726Srpaulo acpi_pwr_switch_power(); 534252726Srpaulo 535252726Srpauloout: 536252726Srpaulo ACPI_SERIAL_END(powerres); 537252726Srpaulo return (status); 538252726Srpaulo} 539252726Srpaulo 540252726Srpaulo/* 541252726Srpaulo * Called to create a reference between a power consumer and a power resource 542252726Srpaulo * identified in the object. 543252726Srpaulo */ 544252726Srpaulostatic void 545252726Srpauloacpi_pwr_reference_resource(ACPI_OBJECT *obj, void *arg) 546252726Srpaulo{ 547252726Srpaulo struct acpi_powerconsumer *pc = (struct acpi_powerconsumer *)arg; 548252726Srpaulo struct acpi_powerreference *pr; 549252726Srpaulo struct acpi_powerresource *rp; 550252726Srpaulo ACPI_HANDLE res; 551252726Srpaulo ACPI_STATUS status; 552252726Srpaulo 553252726Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 554252726Srpaulo ACPI_SERIAL_ASSERT(powerres); 555252726Srpaulo 556252726Srpaulo res = acpi_GetReference(NULL, obj); 557252726Srpaulo if (res == NULL) { 558252726Srpaulo ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 559252726Srpaulo "can't create a power reference for object type %d\n", 560252726Srpaulo obj->Type)); 561252726Srpaulo return_VOID; 562252726Srpaulo } 563252726Srpaulo 564252726Srpaulo /* Create/look up the resource */ 565252726Srpaulo if (ACPI_FAILURE(status = acpi_pwr_register_resource(res))) { 566252726Srpaulo ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 567252726Srpaulo "couldn't register power resource %s - %s\n", 568252726Srpaulo obj->String.Pointer, AcpiFormatException(status))); 569252726Srpaulo return_VOID; 570252726Srpaulo } 571252726Srpaulo if ((rp = acpi_pwr_find_resource(res)) == NULL) { 572252726Srpaulo ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "power resource list corrupted\n")); 573252726Srpaulo return_VOID; 574252726Srpaulo } 575252726Srpaulo ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "found power resource %s\n", 576252726Srpaulo acpi_name(rp->ap_resource))); 577252726Srpaulo 578252726Srpaulo /* Create a reference between the consumer and resource */ 579252726Srpaulo if ((pr = malloc(sizeof(*pr), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) { 580252726Srpaulo ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 581252726Srpaulo "allocation failed for a power consumer reference\n")); 582252726Srpaulo return_VOID; 583252726Srpaulo } 584252726Srpaulo pr->ar_consumer = pc; 585252726Srpaulo pr->ar_resource = rp; 586252726Srpaulo TAILQ_INSERT_TAIL(&pc->ac_references, pr, ar_clink); 587214503Srpaulo TAILQ_INSERT_TAIL(&rp->ap_references, pr, ar_rlink); 588214503Srpaulo 589214503Srpaulo return_VOID; 590214503Srpaulo} 591214503Srpaulo 592214503Srpaulostatic int 593214503Srpauloacpi_pwr_dereference_resource(struct acpi_powerconsumer *pc) 594214503Srpaulo{ 595214503Srpaulo struct acpi_powerreference *pr; 596214503Srpaulo int changed; 597214503Srpaulo 598214503Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 599214503Srpaulo ACPI_SERIAL_ASSERT(powerres); 600214503Srpaulo 601214503Srpaulo changed = 0; 602214503Srpaulo while ((pr = TAILQ_FIRST(&pc->ac_references)) != NULL) { 603214503Srpaulo ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "removing reference to %s\n", 604214503Srpaulo acpi_name(pr->ar_resource->ap_resource))); 605214503Srpaulo TAILQ_REMOVE(&pr->ar_resource->ap_references, pr, ar_rlink); 606214503Srpaulo TAILQ_REMOVE(&pc->ac_references, pr, ar_clink); 607214503Srpaulo free(pr, M_ACPIPWR); 608214503Srpaulo changed = 1; 609214503Srpaulo } 610214503Srpaulo 611214503Srpaulo return (changed); 612214503Srpaulo} 613214503Srpaulo 614214503Srpaulo/* 615214503Srpaulo * Switch power resources to conform to the desired state. 616214503Srpaulo * 617214503Srpaulo * Consumers may have modified the power resource list in an arbitrary 618214503Srpaulo * fashion; we sweep it in sequence order. 619214503Srpaulo */ 620214503Srpaulostatic ACPI_STATUS 621214503Srpauloacpi_pwr_switch_power(void) 622214503Srpaulo{ 623214503Srpaulo struct acpi_powerresource *rp; 624214503Srpaulo ACPI_STATUS status; 625214503Srpaulo int cur; 626214503Srpaulo 627214503Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 628214503Srpaulo ACPI_SERIAL_ASSERT(powerres); 629214503Srpaulo 630214503Srpaulo /* 631214503Srpaulo * Sweep the list forwards turning things on. 632214503Srpaulo */ 633214503Srpaulo TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) { 634214503Srpaulo if (TAILQ_FIRST(&rp->ap_references) == NULL) { 635214503Srpaulo ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 636214503Srpaulo "%s has no references, not turning on\n", 637214503Srpaulo acpi_name(rp->ap_resource))); 638214503Srpaulo continue; 639214503Srpaulo } 640214503Srpaulo 641214503Srpaulo /* We could cache this if we trusted it not to change under us */ 642214503Srpaulo status = acpi_GetInteger(rp->ap_resource, "_STA", &cur); 643252726Srpaulo if (ACPI_FAILURE(status)) { 644252726Srpaulo ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n", 645214503Srpaulo acpi_name(rp->ap_resource), status)); 646214503Srpaulo /* XXX is this correct? Always switch if in doubt? */ 647214503Srpaulo continue; 648214503Srpaulo } else if (rp->ap_state == ACPI_PWR_UNK) 649214503Srpaulo rp->ap_state = cur; 650214503Srpaulo 651214503Srpaulo /* 652214503Srpaulo * Switch if required. Note that we ignore the result of the switch 653214503Srpaulo * effort; we don't know what to do if it fails, so checking wouldn't 654214503Srpaulo * help much. 655214503Srpaulo */ 656214503Srpaulo if (rp->ap_state != ACPI_PWR_ON) { 657214503Srpaulo status = AcpiEvaluateObject(rp->ap_resource, "_ON", NULL, NULL); 658214503Srpaulo if (ACPI_FAILURE(status)) { 659214503Srpaulo ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 660214503Srpaulo "failed to switch %s on - %s\n", 661214503Srpaulo acpi_name(rp->ap_resource), 662214503Srpaulo AcpiFormatException(status))); 663214503Srpaulo } else { 664214503Srpaulo rp->ap_state = ACPI_PWR_ON; 665214503Srpaulo ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s on\n", 666214503Srpaulo acpi_name(rp->ap_resource))); 667214503Srpaulo } 668214503Srpaulo } else { 669214503Srpaulo ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already on\n", 670214503Srpaulo acpi_name(rp->ap_resource))); 671214503Srpaulo } 672214503Srpaulo } 673214503Srpaulo 674214503Srpaulo /* Sweep the list backwards turning things off. */ 675214503Srpaulo TAILQ_FOREACH_REVERSE(rp, &acpi_powerresources, acpi_powerresource_list, 676214503Srpaulo ap_link) { 677214503Srpaulo 678214503Srpaulo if (TAILQ_FIRST(&rp->ap_references) != NULL) { 679214503Srpaulo ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 680214503Srpaulo "%s has references, not turning off\n", 681214503Srpaulo acpi_name(rp->ap_resource))); 682214503Srpaulo continue; 683214503Srpaulo } 684214503Srpaulo 685214503Srpaulo /* We could cache this if we trusted it not to change under us */ 686252726Srpaulo status = acpi_GetInteger(rp->ap_resource, "_STA", &cur); 687252726Srpaulo if (ACPI_FAILURE(status)) { 688252726Srpaulo ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n", 689252726Srpaulo acpi_name(rp->ap_resource), status)); 690252726Srpaulo /* XXX is this correct? Always switch if in doubt? */ 691252726Srpaulo continue; 692252726Srpaulo } else if (rp->ap_state == ACPI_PWR_UNK) 693214503Srpaulo rp->ap_state = cur; 694214503Srpaulo 695214503Srpaulo /* 696214503Srpaulo * Switch if required. Note that we ignore the result of the switch 697252726Srpaulo * effort; we don't know what to do if it fails, so checking wouldn't 698252726Srpaulo * help much. 699214503Srpaulo */ 700252726Srpaulo if (rp->ap_state != ACPI_PWR_OFF) { 701214503Srpaulo status = AcpiEvaluateObject(rp->ap_resource, "_OFF", NULL, NULL); 702214503Srpaulo if (ACPI_FAILURE(status)) { 703214503Srpaulo ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 704214503Srpaulo "failed to switch %s off - %s\n", 705214503Srpaulo acpi_name(rp->ap_resource), 706214503Srpaulo AcpiFormatException(status))); 707214503Srpaulo } else { 708214503Srpaulo rp->ap_state = ACPI_PWR_OFF; 709214503Srpaulo ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s off\n", 710214503Srpaulo acpi_name(rp->ap_resource))); 711214503Srpaulo } 712214503Srpaulo } else { 713214503Srpaulo ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already off\n", 714214503Srpaulo acpi_name(rp->ap_resource))); 715214503Srpaulo } 716214503Srpaulo } 717214503Srpaulo 718214503Srpaulo return_ACPI_STATUS (AE_OK); 719214503Srpaulo} 720214503Srpaulo 721214503Srpaulo/* 722214503Srpaulo * Find a power resource's control structure. 723214503Srpaulo */ 724214503Srpaulostatic struct acpi_powerresource * 725214503Srpauloacpi_pwr_find_resource(ACPI_HANDLE res) 726214503Srpaulo{ 727214503Srpaulo struct acpi_powerresource *rp; 728214503Srpaulo 729214503Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 730214503Srpaulo ACPI_SERIAL_ASSERT(powerres); 731214503Srpaulo 732214503Srpaulo TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) { 733214503Srpaulo if (rp->ap_resource == res) 734214503Srpaulo break; 735214503Srpaulo } 736214503Srpaulo 737214503Srpaulo return_PTR (rp); 738214503Srpaulo} 739214503Srpaulo 740214503Srpaulo/* 741214503Srpaulo * Find a power consumer's control structure. 742214503Srpaulo */ 743214503Srpaulostatic struct acpi_powerconsumer * 744214503Srpauloacpi_pwr_find_consumer(ACPI_HANDLE consumer) 745214503Srpaulo{ 746214503Srpaulo struct acpi_powerconsumer *pc; 747214503Srpaulo 748214503Srpaulo ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 749214503Srpaulo ACPI_SERIAL_ASSERT(powerres); 750214503Srpaulo 751214503Srpaulo TAILQ_FOREACH(pc, &acpi_powerconsumers, ac_link) { 752214503Srpaulo if (pc->ac_consumer == consumer) 753214503Srpaulo break; 754214503Srpaulo } 755214503Srpaulo 756214503Srpaulo return_PTR (pc); 757214503Srpaulo} 758214503Srpaulo