acpi_powerres.c revision 267983
178915Smsmith/*- 278915Smsmith * Copyright (c) 2001 Michael Smith 378915Smsmith * All rights reserved. 478915Smsmith * 578915Smsmith * Redistribution and use in source and binary forms, with or without 678915Smsmith * modification, are permitted provided that the following conditions 778915Smsmith * are met: 878915Smsmith * 1. Redistributions of source code must retain the above copyright 978915Smsmith * notice, this list of conditions and the following disclaimer. 1078915Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1178915Smsmith * notice, this list of conditions and the following disclaimer in the 1278915Smsmith * documentation and/or other materials provided with the distribution. 1378915Smsmith * 1478915Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1578915Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1678915Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1778915Smsmith * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1878915Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1978915Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2078915Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2178915Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2278915Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2378915Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2478915Smsmith * SUCH DAMAGE. 2578915Smsmith */ 2678915Smsmith 27119418Sobrien#include <sys/cdefs.h> 28119418Sobrien__FBSDID("$FreeBSD: stable/10/sys/dev/acpica/acpi_powerres.c 267983 2014-06-27 20:57:12Z jhb $"); 29119418Sobrien 30119529Snjl#include "opt_acpi.h" 3178915Smsmith#include <sys/param.h> 3278915Smsmith#include <sys/kernel.h> 3378915Smsmith#include <sys/malloc.h> 3478915Smsmith#include <sys/bus.h> 3578915Smsmith 36193530Sjkim#include <contrib/dev/acpica/include/acpi.h> 37193530Sjkim#include <contrib/dev/acpica/include/accommon.h> 38193530Sjkim 3978915Smsmith#include <dev/acpica/acpivar.h> 4078915Smsmith 4178915Smsmith/* 4278915Smsmith * ACPI power resource management. 4378915Smsmith * 4478915Smsmith * Power resource behaviour is slightly complicated by the fact that 4578915Smsmith * a single power resource may provide power for more than one device. 4678915Smsmith * Thus, we must track the device(s) being powered by a given power 4778915Smsmith * resource, and only deactivate it when there are no powered devices. 4878915Smsmith * 4978915Smsmith * Note that this only manages resources for known devices. There is an 50248415Srpaulo * ugly case where we may turn off power to a device which is in use because 5178915Smsmith * we don't know that it depends on a given resource. We should perhaps 5278915Smsmith * try to be smarter about this, but a more complete solution would involve 5378915Smsmith * scanning all of the ACPI namespace to find devices we're not currently 5478915Smsmith * aware of, and this raises questions about whether they should be left 5578915Smsmith * on, turned off, etc. 5678915Smsmith */ 5778915Smsmith 58227293Sedstatic MALLOC_DEFINE(M_ACPIPWR, "acpipwr", "ACPI power resources"); 5978915Smsmith 60119529Snjl/* Hooks for the ACPI CA debugging infrastructure */ 61126517Snjl#define _COMPONENT ACPI_POWERRES 6291125SmsmithACPI_MODULE_NAME("POWERRES") 6378915Smsmith 64119529Snjl/* Return values from _STA on a power resource */ 6579357Smsmith#define ACPI_PWR_OFF 0 6679357Smsmith#define ACPI_PWR_ON 1 6779357Smsmith 68119529Snjl/* A relationship between a power resource and a consumer. */ 6978915Smsmithstruct acpi_powerreference { 70119529Snjl struct acpi_powerconsumer *ar_consumer; 71119529Snjl struct acpi_powerresource *ar_resource; 72119529Snjl TAILQ_ENTRY(acpi_powerreference) ar_rlink; /* link on resource list */ 73119529Snjl TAILQ_ENTRY(acpi_powerreference) ar_clink; /* link on consumer */ 7478915Smsmith}; 7578915Smsmith 76119529Snjl/* A power-managed device. */ 7778915Smsmithstruct acpi_powerconsumer { 78119529Snjl /* Device which is powered */ 79119529Snjl ACPI_HANDLE ac_consumer; 80119529Snjl int ac_state; 81119529Snjl TAILQ_ENTRY(acpi_powerconsumer) ac_link; 82119529Snjl TAILQ_HEAD(,acpi_powerreference) ac_references; 8378915Smsmith}; 8478915Smsmith 85119529Snjl/* A power resource. */ 8678915Smsmithstruct acpi_powerresource { 87119529Snjl TAILQ_ENTRY(acpi_powerresource) ap_link; 88119529Snjl TAILQ_HEAD(,acpi_powerreference) ap_references; 89119529Snjl ACPI_HANDLE ap_resource; 90202771Sjkim UINT64 ap_systemlevel; 91202771Sjkim UINT64 ap_order; 9278915Smsmith}; 9378915Smsmith 94119529Snjlstatic TAILQ_HEAD(acpi_powerresource_list, acpi_powerresource) 95119529Snjl acpi_powerresources; 96119529Snjlstatic TAILQ_HEAD(acpi_powerconsumer_list, acpi_powerconsumer) 97119529Snjl acpi_powerconsumers; 98133622SnjlACPI_SERIAL_DECL(powerres, "ACPI power resources"); 9978915Smsmith 100119529Snjlstatic ACPI_STATUS acpi_pwr_register_consumer(ACPI_HANDLE consumer); 101128248Snjl#ifdef notyet 102119529Snjlstatic ACPI_STATUS acpi_pwr_deregister_consumer(ACPI_HANDLE consumer); 103128248Snjl#endif /* notyet */ 104119529Snjlstatic ACPI_STATUS acpi_pwr_register_resource(ACPI_HANDLE res); 105128248Snjl#ifdef notyet 106119529Snjlstatic ACPI_STATUS acpi_pwr_deregister_resource(ACPI_HANDLE res); 107128248Snjl#endif /* notyet */ 108119529Snjlstatic void acpi_pwr_reference_resource(ACPI_OBJECT *obj, 109119529Snjl void *arg); 110131340Snjlstatic int acpi_pwr_dereference_resource(struct acpi_powerconsumer 111131340Snjl *pc); 112119529Snjlstatic ACPI_STATUS acpi_pwr_switch_power(void); 113119529Snjlstatic struct acpi_powerresource 114119529Snjl *acpi_pwr_find_resource(ACPI_HANDLE res); 115119529Snjlstatic struct acpi_powerconsumer 116119529Snjl *acpi_pwr_find_consumer(ACPI_HANDLE consumer); 11778915Smsmith 118119529Snjl/* Initialise our lists. */ 11978915Smsmithstatic void 12078915Smsmithacpi_pwr_init(void *junk) 12178915Smsmith{ 12278915Smsmith TAILQ_INIT(&acpi_powerresources); 12378915Smsmith TAILQ_INIT(&acpi_powerconsumers); 12478915Smsmith} 12578915SmsmithSYSINIT(acpi_powerresource, SI_SUB_TUNABLES, SI_ORDER_ANY, acpi_pwr_init, NULL); 12678915Smsmith 12778915Smsmith/* 12878915Smsmith * Register a power resource. 12978915Smsmith * 13078915Smsmith * It's OK to call this if we already know about the resource. 13178915Smsmith */ 13278915Smsmithstatic ACPI_STATUS 13378915Smsmithacpi_pwr_register_resource(ACPI_HANDLE res) 13478915Smsmith{ 13578915Smsmith ACPI_STATUS status; 13678915Smsmith ACPI_BUFFER buf; 13779493Smsmith ACPI_OBJECT *obj; 13878915Smsmith struct acpi_powerresource *rp, *srp; 13978915Smsmith 14096926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 141133622Snjl ACPI_SERIAL_ASSERT(powerres); 14278915Smsmith 14378915Smsmith rp = NULL; 14491125Smsmith buf.Pointer = NULL; 14578915Smsmith 146119529Snjl /* Look to see if we know about this resource */ 14778915Smsmith if (acpi_pwr_find_resource(res) != NULL) 148119529Snjl return_ACPI_STATUS (AE_OK); /* already know about it */ 14978915Smsmith 150119529Snjl /* Allocate a new resource */ 15178915Smsmith if ((rp = malloc(sizeof(*rp), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) { 15278915Smsmith status = AE_NO_MEMORY; 15378915Smsmith goto out; 15478915Smsmith } 15578915Smsmith TAILQ_INIT(&rp->ap_references); 15678915Smsmith rp->ap_resource = res; 15778915Smsmith 158119529Snjl /* Get the Power Resource object */ 15991125Smsmith buf.Length = ACPI_ALLOCATE_BUFFER; 16091125Smsmith if (ACPI_FAILURE(status = AcpiEvaluateObject(res, NULL, NULL, &buf))) { 16182372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "no power resource object\n")); 16278915Smsmith goto out; 16378915Smsmith } 16478915Smsmith obj = buf.Pointer; 16579493Smsmith if (obj->Type != ACPI_TYPE_POWER) { 166119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 167119529Snjl "questionable power resource object %s\n", 168119529Snjl acpi_name(res))); 16979493Smsmith status = AE_TYPE; 17079493Smsmith goto out; 17178915Smsmith } 17279493Smsmith rp->ap_systemlevel = obj->PowerResource.SystemLevel; 17379493Smsmith rp->ap_order = obj->PowerResource.ResourceOrder; 17478915Smsmith 175119529Snjl /* Sort the resource into the list */ 17678915Smsmith status = AE_OK; 17778915Smsmith srp = TAILQ_FIRST(&acpi_powerresources); 178119529Snjl if (srp == NULL || rp->ap_order < srp->ap_order) { 17978915Smsmith TAILQ_INSERT_HEAD(&acpi_powerresources, rp, ap_link); 18079357Smsmith goto done; 18178915Smsmith } 182119529Snjl TAILQ_FOREACH(srp, &acpi_powerresources, ap_link) { 18378915Smsmith if (rp->ap_order < srp->ap_order) { 18478915Smsmith TAILQ_INSERT_BEFORE(srp, rp, ap_link); 18579357Smsmith goto done; 18678915Smsmith } 187119529Snjl } 18878915Smsmith TAILQ_INSERT_TAIL(&acpi_powerresources, rp, ap_link); 18979357Smsmith 19079357Smsmith done: 191119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 192119529Snjl "registered power resource %s\n", acpi_name(res))); 193119529Snjl 19478915Smsmith out: 19591125Smsmith if (buf.Pointer != NULL) 19691125Smsmith AcpiOsFree(buf.Pointer); 197119529Snjl if (ACPI_FAILURE(status) && rp != NULL) 19878915Smsmith free(rp, M_ACPIPWR); 199119529Snjl return_ACPI_STATUS (status); 20078915Smsmith} 20178915Smsmith 202128248Snjl#ifdef notyet 20378915Smsmith/* 20478915Smsmith * Deregister a power resource. 20578915Smsmith */ 20678915Smsmithstatic ACPI_STATUS 20778915Smsmithacpi_pwr_deregister_resource(ACPI_HANDLE res) 20878915Smsmith{ 20978915Smsmith struct acpi_powerresource *rp; 21078915Smsmith 21196926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 212133622Snjl ACPI_SERIAL_ASSERT(powerres); 21378915Smsmith 21478915Smsmith rp = NULL; 21578915Smsmith 216119529Snjl /* Find the resource */ 21778915Smsmith if ((rp = acpi_pwr_find_resource(res)) == NULL) 218119529Snjl return_ACPI_STATUS (AE_BAD_PARAMETER); 21978915Smsmith 220119529Snjl /* Check that there are no consumers referencing this resource */ 22178915Smsmith if (TAILQ_FIRST(&rp->ap_references) != NULL) 222119529Snjl return_ACPI_STATUS (AE_BAD_PARAMETER); 22378915Smsmith 224119529Snjl /* Pull it off the list and free it */ 22578915Smsmith TAILQ_REMOVE(&acpi_powerresources, rp, ap_link); 22678915Smsmith free(rp, M_ACPIPWR); 22778915Smsmith 228119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power resource %s\n", 229119529Snjl acpi_name(res))); 23078915Smsmith 231119529Snjl return_ACPI_STATUS (AE_OK); 23278915Smsmith} 233128248Snjl#endif /* notyet */ 23478915Smsmith 23578915Smsmith/* 23678915Smsmith * Register a power consumer. 23778915Smsmith * 23878915Smsmith * It's OK to call this if we already know about the consumer. 23978915Smsmith */ 24078915Smsmithstatic ACPI_STATUS 24178915Smsmithacpi_pwr_register_consumer(ACPI_HANDLE consumer) 24278915Smsmith{ 24378915Smsmith struct acpi_powerconsumer *pc; 24478915Smsmith 24596926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 246133622Snjl ACPI_SERIAL_ASSERT(powerres); 24778915Smsmith 248119529Snjl /* Check to see whether we know about this consumer already */ 249157947Sjkim if (acpi_pwr_find_consumer(consumer) != NULL) 250119529Snjl return_ACPI_STATUS (AE_OK); 25178915Smsmith 252119529Snjl /* Allocate a new power consumer */ 25378915Smsmith if ((pc = malloc(sizeof(*pc), M_ACPIPWR, M_NOWAIT)) == NULL) 254119529Snjl return_ACPI_STATUS (AE_NO_MEMORY); 25578915Smsmith TAILQ_INSERT_HEAD(&acpi_powerconsumers, pc, ac_link); 25678915Smsmith TAILQ_INIT(&pc->ac_references); 25778915Smsmith pc->ac_consumer = consumer; 25878915Smsmith 259119529Snjl /* XXX we should try to find its current state */ 260119529Snjl pc->ac_state = ACPI_STATE_UNKNOWN; 26178915Smsmith 262119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "registered power consumer %s\n", 263119529Snjl acpi_name(consumer))); 26478915Smsmith 265119529Snjl return_ACPI_STATUS (AE_OK); 26678915Smsmith} 26778915Smsmith 268128248Snjl#ifdef notyet 26978915Smsmith/* 27078915Smsmith * Deregister a power consumer. 27178915Smsmith * 27278915Smsmith * This should only be done once the consumer has been powered off. 27378915Smsmith * (XXX is this correct? Check once implemented) 27478915Smsmith */ 27578915Smsmithstatic ACPI_STATUS 27678915Smsmithacpi_pwr_deregister_consumer(ACPI_HANDLE consumer) 27778915Smsmith{ 27878915Smsmith struct acpi_powerconsumer *pc; 27978915Smsmith 28096926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 281133622Snjl ACPI_SERIAL_ASSERT(powerres); 28278915Smsmith 283119529Snjl /* Find the consumer */ 28478915Smsmith if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) 285119529Snjl return_ACPI_STATUS (AE_BAD_PARAMETER); 28678915Smsmith 287119529Snjl /* Make sure the consumer's not referencing anything right now */ 28878915Smsmith if (TAILQ_FIRST(&pc->ac_references) != NULL) 289119529Snjl return_ACPI_STATUS (AE_BAD_PARAMETER); 29078915Smsmith 291119529Snjl /* Pull the consumer off the list and free it */ 29278915Smsmith TAILQ_REMOVE(&acpi_powerconsumers, pc, ac_link); 293133622Snjl free(pc, M_ACPIPWR); 29478915Smsmith 295119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power consumer %s\n", 296119529Snjl acpi_name(consumer))); 29778915Smsmith 298119529Snjl return_ACPI_STATUS (AE_OK); 29978915Smsmith} 300128248Snjl#endif /* notyet */ 30178915Smsmith 30278915Smsmith/* 30378915Smsmith * Set a power consumer to a particular power state. 30478915Smsmith */ 30578915SmsmithACPI_STATUS 30678915Smsmithacpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state) 30778915Smsmith{ 30878915Smsmith struct acpi_powerconsumer *pc; 30982084Siwasaki ACPI_HANDLE method_handle, reslist_handle, pr0_handle; 31078915Smsmith ACPI_BUFFER reslist_buffer; 31178915Smsmith ACPI_OBJECT *reslist_object; 31278915Smsmith ACPI_STATUS status; 31378915Smsmith char *method_name, *reslist_name; 31478915Smsmith 31596926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 31678915Smsmith 317128252Snjl /* It's never ok to switch a non-existent consumer. */ 318128252Snjl if (consumer == NULL) 319128252Snjl return_ACPI_STATUS (AE_NOT_FOUND); 320133622Snjl reslist_buffer.Pointer = NULL; 321133622Snjl reslist_object = NULL; 322133622Snjl ACPI_SERIAL_BEGIN(powerres); 323128252Snjl 324119529Snjl /* Find the consumer */ 32578915Smsmith if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) { 32691125Smsmith if (ACPI_FAILURE(status = acpi_pwr_register_consumer(consumer))) 327133622Snjl goto out; 328133622Snjl if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) 329133622Snjl panic("acpi added power consumer but can't find it"); 33078915Smsmith } 33178915Smsmith 332133622Snjl /* Check for valid transitions. We can only go to D0 from D3. */ 333133622Snjl status = AE_BAD_PARAMETER; 334119529Snjl if (pc->ac_state == ACPI_STATE_D3 && state != ACPI_STATE_D0) 335133622Snjl goto out; 33678915Smsmith 337119529Snjl /* Find transition mechanism(s) */ 338130208Snjl switch (state) { 33978915Smsmith case ACPI_STATE_D0: 34078915Smsmith method_name = "_PS0"; 34178915Smsmith reslist_name = "_PR0"; 34278915Smsmith break; 34378915Smsmith case ACPI_STATE_D1: 34478915Smsmith method_name = "_PS1"; 34578915Smsmith reslist_name = "_PR1"; 34678915Smsmith break; 34778915Smsmith case ACPI_STATE_D2: 34878915Smsmith method_name = "_PS2"; 34978915Smsmith reslist_name = "_PR2"; 35078915Smsmith break; 35178915Smsmith case ACPI_STATE_D3: 35278915Smsmith method_name = "_PS3"; 35378915Smsmith reslist_name = "_PR3"; 35478915Smsmith break; 35578915Smsmith default: 356133622Snjl goto out; 35778915Smsmith } 35882372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "setup to switch %s D%d -> D%d\n", 359119529Snjl acpi_name(consumer), pc->ac_state, state)); 36078915Smsmith 36178915Smsmith /* 36278915Smsmith * Verify that this state is supported, ie. one of method or 36378915Smsmith * reslist must be present. We need to do this before we go 36478915Smsmith * dereferencing resources (since we might be trying to go to 36578915Smsmith * a state we don't support). 36678915Smsmith * 36778915Smsmith * Note that if any states are supported, the device has to 36878915Smsmith * support D0 and D3. It's never an error to try to go to 36978915Smsmith * D0. 37078915Smsmith */ 37191125Smsmith if (ACPI_FAILURE(AcpiGetHandle(consumer, method_name, &method_handle))) 37278915Smsmith method_handle = NULL; 37391125Smsmith if (ACPI_FAILURE(AcpiGetHandle(consumer, reslist_name, &reslist_handle))) 37478915Smsmith reslist_handle = NULL; 375119529Snjl if (reslist_handle == NULL && method_handle == NULL) { 37678915Smsmith if (state == ACPI_STATE_D0) { 37778915Smsmith pc->ac_state = ACPI_STATE_D0; 378133622Snjl status = AE_OK; 379133622Snjl goto out; 38078915Smsmith } 381133622Snjl if (state != ACPI_STATE_D3) { 382133622Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 383133622Snjl "attempt to set unsupported state D%d\n", state)); 384133622Snjl goto out; 385133622Snjl } 38682084Siwasaki 387130208Snjl /* 388130208Snjl * Turn off the resources listed in _PR0 to go to D3. If there is 389130208Snjl * no _PR0 method, this object doesn't support ACPI power states. 390130208Snjl */ 391130208Snjl if (ACPI_FAILURE(AcpiGetHandle(consumer, "_PR0", &pr0_handle))) { 392130208Snjl status = AE_NOT_FOUND; 393133622Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 394133622Snjl "device missing _PR0 (desired state was D%d)\n", state)); 395133622Snjl goto out; 396130208Snjl } 39791125Smsmith reslist_buffer.Length = ACPI_ALLOCATE_BUFFER; 398119529Snjl status = AcpiEvaluateObject(pr0_handle, NULL, NULL, &reslist_buffer); 399133622Snjl if (ACPI_FAILURE(status)) { 400133622Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 401133622Snjl "can't evaluate _PR0 for device %s, state D%d\n", 402133622Snjl acpi_name(consumer), state)); 403133622Snjl goto out; 404133622Snjl } 40582084Siwasaki reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer; 406133622Snjl if (!ACPI_PKG_VALID(reslist_object, 1)) { 407133622Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 408133622Snjl "invalid package object for state D%d\n", state)); 409133622Snjl status = AE_TYPE; 410133622Snjl goto out; 411133622Snjl } 41291125Smsmith AcpiOsFree(reslist_buffer.Pointer); 41391125Smsmith reslist_buffer.Pointer = NULL; 41491125Smsmith reslist_object = NULL; 41578915Smsmith } 41678915Smsmith 41778915Smsmith /* 41878915Smsmith * Check that we can actually fetch the list of power resources 41978915Smsmith */ 42078915Smsmith if (reslist_handle != NULL) { 42191125Smsmith reslist_buffer.Length = ACPI_ALLOCATE_BUFFER; 422119529Snjl status = AcpiEvaluateObject(reslist_handle, NULL, NULL, 423119529Snjl &reslist_buffer); 424119529Snjl if (ACPI_FAILURE(status)) { 425119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 426119529Snjl "can't evaluate resource list %s\n", 427119529Snjl acpi_name(reslist_handle))); 42891125Smsmith goto out; 42978915Smsmith } 43078915Smsmith reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer; 43178915Smsmith if (reslist_object->Type != ACPI_TYPE_PACKAGE) { 432119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 433119529Snjl "resource list is not ACPI_TYPE_PACKAGE (%d)\n", 434119529Snjl reslist_object->Type)); 43591125Smsmith status = AE_TYPE; 43691125Smsmith goto out; 43778915Smsmith } 43878915Smsmith } 43978915Smsmith 44078915Smsmith /* 441125745Sjhb * Now we are ready to switch, so kill off any current power 442119529Snjl * resource references. 44378915Smsmith */ 444238199Seadler acpi_pwr_dereference_resource(pc); 44578915Smsmith 44678915Smsmith /* 44778915Smsmith * Add new power resource references, if we have any. Traverse the 44878915Smsmith * package that we got from evaluating reslist_handle, and look up each 44978915Smsmith * of the resources that are referenced. 45078915Smsmith */ 45178915Smsmith if (reslist_object != NULL) { 45282372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "referencing %d new resources\n", 45382372Smsmith reslist_object->Package.Count)); 454119529Snjl acpi_ForeachPackageObject(reslist_object, acpi_pwr_reference_resource, 455119529Snjl pc); 45678915Smsmith } 45778915Smsmith 45878915Smsmith /* 45978915Smsmith * If we changed anything in the resource list, we need to run a switch 46078915Smsmith * pass now. 46178915Smsmith */ 46291125Smsmith if (ACPI_FAILURE(status = acpi_pwr_switch_power())) { 463119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 464119529Snjl "failed to switch resources from %s to D%d\n", 46582372Smsmith acpi_name(consumer), state)); 466119529Snjl 467119529Snjl /* XXX is this appropriate? Should we return to previous state? */ 468139339Snjl goto out; 46978915Smsmith } 47078915Smsmith 471119529Snjl /* Invoke power state switch method (if present) */ 47278915Smsmith if (method_handle != NULL) { 473119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 474119529Snjl "invoking state transition method %s\n", 475119529Snjl acpi_name(method_handle))); 476119529Snjl status = AcpiEvaluateObject(method_handle, NULL, NULL, NULL); 477119529Snjl if (ACPI_FAILURE(status)) { 47891125Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to set state - %s\n", 479119529Snjl AcpiFormatException(status))); 48091125Smsmith pc->ac_state = ACPI_STATE_UNKNOWN; 481119529Snjl 482119529Snjl /* XXX Should we return to previous state? */ 483119529Snjl goto out; 48491125Smsmith } 48578915Smsmith } 486139339Snjl 487119529Snjl /* Transition was successful */ 48878915Smsmith pc->ac_state = state; 489133622Snjl status = AE_OK; 49082084Siwasaki 491133622Snjlout: 492133622Snjl ACPI_SERIAL_END(powerres); 49391125Smsmith if (reslist_buffer.Pointer != NULL) 49491125Smsmith AcpiOsFree(reslist_buffer.Pointer); 495119529Snjl return_ACPI_STATUS (status); 49678915Smsmith} 49778915Smsmith 498131340Snjl/* Enable or disable a power resource for wake */ 499131340SnjlACPI_STATUS 500131340Snjlacpi_pwr_wake_enable(ACPI_HANDLE consumer, int enable) 501131340Snjl{ 502131340Snjl ACPI_STATUS status; 503131340Snjl struct acpi_powerconsumer *pc; 504131340Snjl struct acpi_prw_data prw; 505131340Snjl int i; 506131340Snjl 507131366Snjl ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 508131366Snjl 509131340Snjl if (consumer == NULL) 510131340Snjl return (AE_BAD_PARAMETER); 511131340Snjl 512133622Snjl ACPI_SERIAL_BEGIN(powerres); 513131340Snjl if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) { 514131340Snjl if (ACPI_FAILURE(status = acpi_pwr_register_consumer(consumer))) 515133622Snjl goto out; 516133622Snjl if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) 517133622Snjl panic("acpi wake added power consumer but can't find it"); 518131340Snjl } 519131340Snjl 520133622Snjl status = AE_OK; 521131340Snjl if (acpi_parse_prw(consumer, &prw) != 0) 522133622Snjl goto out; 523131340Snjl for (i = 0; i < prw.power_res_count; i++) 524131340Snjl if (enable) 525131340Snjl acpi_pwr_reference_resource(&prw.power_res[i], pc); 526131340Snjl else 527131340Snjl acpi_pwr_dereference_resource(pc); 528131340Snjl 529131340Snjl if (prw.power_res_count > 0) 530131340Snjl acpi_pwr_switch_power(); 531131340Snjl 532133622Snjlout: 533133622Snjl ACPI_SERIAL_END(powerres); 534133622Snjl return (status); 535131340Snjl} 536131340Snjl 53778915Smsmith/* 53878915Smsmith * Called to create a reference between a power consumer and a power resource 53978915Smsmith * identified in the object. 54078915Smsmith */ 54178915Smsmithstatic void 54278915Smsmithacpi_pwr_reference_resource(ACPI_OBJECT *obj, void *arg) 54378915Smsmith{ 54478915Smsmith struct acpi_powerconsumer *pc = (struct acpi_powerconsumer *)arg; 54579357Smsmith struct acpi_powerreference *pr; 54679357Smsmith struct acpi_powerresource *rp; 54779357Smsmith ACPI_HANDLE res; 54879357Smsmith ACPI_STATUS status; 54978915Smsmith 55096926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 551133622Snjl ACPI_SERIAL_ASSERT(powerres); 55278915Smsmith 553128047Snjl res = acpi_GetReference(NULL, obj); 554128047Snjl if (res == NULL) { 555119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 556128047Snjl "can't create a power reference for object type %d\n", 557119529Snjl obj->Type)); 55879357Smsmith return_VOID; 55979357Smsmith } 56078915Smsmith 561119529Snjl /* Create/look up the resource */ 56279357Smsmith if (ACPI_FAILURE(status = acpi_pwr_register_resource(res))) { 563119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 564119529Snjl "couldn't register power resource %s - %s\n", 565119529Snjl obj->String.Pointer, AcpiFormatException(status))); 56679357Smsmith return_VOID; 56779357Smsmith } 56879357Smsmith if ((rp = acpi_pwr_find_resource(res)) == NULL) { 56982372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "power resource list corrupted\n")); 57079357Smsmith return_VOID; 57179357Smsmith } 572119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "found power resource %s\n", 573119529Snjl acpi_name(rp->ap_resource))); 57479357Smsmith 575119529Snjl /* Create a reference between the consumer and resource */ 57679357Smsmith if ((pr = malloc(sizeof(*pr), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) { 577119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 578119529Snjl "allocation failed for a power consumer reference\n")); 57979357Smsmith return_VOID; 58079357Smsmith } 58179357Smsmith pr->ar_consumer = pc; 58279357Smsmith pr->ar_resource = rp; 58379357Smsmith TAILQ_INSERT_TAIL(&pc->ac_references, pr, ar_clink); 58479357Smsmith TAILQ_INSERT_TAIL(&rp->ap_references, pr, ar_rlink); 585131340Snjl 58678915Smsmith return_VOID; 58778915Smsmith} 58878915Smsmith 589131340Snjlstatic int 590131340Snjlacpi_pwr_dereference_resource(struct acpi_powerconsumer *pc) 591131340Snjl{ 592131340Snjl struct acpi_powerreference *pr; 593131340Snjl int changed; 59478915Smsmith 595131366Snjl ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 596133622Snjl ACPI_SERIAL_ASSERT(powerres); 597131366Snjl 598131340Snjl changed = 0; 599131340Snjl while ((pr = TAILQ_FIRST(&pc->ac_references)) != NULL) { 600131340Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "removing reference to %s\n", 601131340Snjl acpi_name(pr->ar_resource->ap_resource))); 602131340Snjl TAILQ_REMOVE(&pr->ar_resource->ap_references, pr, ar_rlink); 603131340Snjl TAILQ_REMOVE(&pc->ac_references, pr, ar_clink); 604131340Snjl free(pr, M_ACPIPWR); 605131340Snjl changed = 1; 606131340Snjl } 607131340Snjl 608131340Snjl return (changed); 609131340Snjl} 610131340Snjl 61178915Smsmith/* 61278915Smsmith * Switch power resources to conform to the desired state. 61378915Smsmith * 61478915Smsmith * Consumers may have modified the power resource list in an arbitrary 61578915Smsmith * fashion; we sweep it in sequence order. 61678915Smsmith */ 61778915Smsmithstatic ACPI_STATUS 61878915Smsmithacpi_pwr_switch_power(void) 61978915Smsmith{ 62078915Smsmith struct acpi_powerresource *rp; 62178915Smsmith ACPI_STATUS status; 62278915Smsmith int cur; 62378915Smsmith 62496926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 625133622Snjl ACPI_SERIAL_ASSERT(powerres); 62678915Smsmith 62778915Smsmith /* 62878915Smsmith * Sweep the list forwards turning things on. 62978915Smsmith */ 63078915Smsmith TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) { 63179357Smsmith if (TAILQ_FIRST(&rp->ap_references) == NULL) { 632119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 633119529Snjl "%s has no references, not turning on\n", 634119529Snjl acpi_name(rp->ap_resource))); 63579357Smsmith continue; 63679357Smsmith } 63778915Smsmith 638126560Snjl status = acpi_GetInteger(rp->ap_resource, "_STA", &cur); 639119529Snjl if (ACPI_FAILURE(status)) { 64082372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n", 64182372Smsmith acpi_name(rp->ap_resource), status)); 642119529Snjl /* XXX is this correct? Always switch if in doubt? */ 643119529Snjl continue; 644267983Sjhb } 64578915Smsmith 64678915Smsmith /* 64778915Smsmith * Switch if required. Note that we ignore the result of the switch 64878915Smsmith * effort; we don't know what to do if it fails, so checking wouldn't 64978915Smsmith * help much. 65078915Smsmith */ 651267983Sjhb if (cur != ACPI_PWR_ON) { 652119529Snjl status = AcpiEvaluateObject(rp->ap_resource, "_ON", NULL, NULL); 653119529Snjl if (ACPI_FAILURE(status)) { 654119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 655119529Snjl "failed to switch %s on - %s\n", 656119529Snjl acpi_name(rp->ap_resource), 657119529Snjl AcpiFormatException(status))); 65879357Smsmith } else { 659119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s on\n", 660119529Snjl acpi_name(rp->ap_resource))); 66179357Smsmith } 66279357Smsmith } else { 663119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already on\n", 664119529Snjl acpi_name(rp->ap_resource))); 66579357Smsmith } 66678915Smsmith } 66778915Smsmith 668119529Snjl /* Sweep the list backwards turning things off. */ 669119529Snjl TAILQ_FOREACH_REVERSE(rp, &acpi_powerresources, acpi_powerresource_list, 670119529Snjl ap_link) { 671119529Snjl 67279357Smsmith if (TAILQ_FIRST(&rp->ap_references) != NULL) { 673119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 674119529Snjl "%s has references, not turning off\n", 675119529Snjl acpi_name(rp->ap_resource))); 67679357Smsmith continue; 67779357Smsmith } 67878915Smsmith 679126560Snjl status = acpi_GetInteger(rp->ap_resource, "_STA", &cur); 680119529Snjl if (ACPI_FAILURE(status)) { 68182372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n", 68282372Smsmith acpi_name(rp->ap_resource), status)); 683119529Snjl /* XXX is this correct? Always switch if in doubt? */ 684119529Snjl continue; 685267983Sjhb } 68678915Smsmith 68778915Smsmith /* 68878915Smsmith * Switch if required. Note that we ignore the result of the switch 68978915Smsmith * effort; we don't know what to do if it fails, so checking wouldn't 69078915Smsmith * help much. 69178915Smsmith */ 692267983Sjhb if (cur != ACPI_PWR_OFF) { 693119529Snjl status = AcpiEvaluateObject(rp->ap_resource, "_OFF", NULL, NULL); 694119529Snjl if (ACPI_FAILURE(status)) { 695119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 696119529Snjl "failed to switch %s off - %s\n", 697119529Snjl acpi_name(rp->ap_resource), 698119529Snjl AcpiFormatException(status))); 69979357Smsmith } else { 700119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s off\n", 701119529Snjl acpi_name(rp->ap_resource))); 70279357Smsmith } 70379357Smsmith } else { 704119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already off\n", 705119529Snjl acpi_name(rp->ap_resource))); 70679357Smsmith } 70778915Smsmith } 708119529Snjl 709119529Snjl return_ACPI_STATUS (AE_OK); 71078915Smsmith} 71178915Smsmith 71278915Smsmith/* 71378915Smsmith * Find a power resource's control structure. 71478915Smsmith */ 71578915Smsmithstatic struct acpi_powerresource * 71678915Smsmithacpi_pwr_find_resource(ACPI_HANDLE res) 71778915Smsmith{ 71878915Smsmith struct acpi_powerresource *rp; 71978915Smsmith 72096926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 721133622Snjl ACPI_SERIAL_ASSERT(powerres); 72278915Smsmith 723119529Snjl TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) { 72478915Smsmith if (rp->ap_resource == res) 72578915Smsmith break; 726119529Snjl } 727119529Snjl 728119529Snjl return_PTR (rp); 72978915Smsmith} 73078915Smsmith 73178915Smsmith/* 73278915Smsmith * Find a power consumer's control structure. 73378915Smsmith */ 73478915Smsmithstatic struct acpi_powerconsumer * 73578915Smsmithacpi_pwr_find_consumer(ACPI_HANDLE consumer) 73678915Smsmith{ 73778915Smsmith struct acpi_powerconsumer *pc; 73878915Smsmith 73996926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 740133622Snjl ACPI_SERIAL_ASSERT(powerres); 74178915Smsmith 742119529Snjl TAILQ_FOREACH(pc, &acpi_powerconsumers, ac_link) { 74378915Smsmith if (pc->ac_consumer == consumer) 74478915Smsmith break; 745119529Snjl } 746119529Snjl 747119529Snjl return_PTR (pc); 74878915Smsmith} 749