acpi_powerres.c revision 131366
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: head/sys/dev/acpica/acpi_powerres.c 131366 2004-06-30 20:31:13Z njl $"); 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 3678915Smsmith#include "acpi.h" 3778915Smsmith#include <dev/acpica/acpivar.h> 3878915Smsmith 3978915Smsmith/* 4078915Smsmith * ACPI power resource management. 4178915Smsmith * 4278915Smsmith * Power resource behaviour is slightly complicated by the fact that 4378915Smsmith * a single power resource may provide power for more than one device. 4478915Smsmith * Thus, we must track the device(s) being powered by a given power 4578915Smsmith * resource, and only deactivate it when there are no powered devices. 4678915Smsmith * 4778915Smsmith * Note that this only manages resources for known devices. There is an 4878915Smsmith * ugly case where we may turn of power to a device which is in use because 4978915Smsmith * we don't know that it depends on a given resource. We should perhaps 5078915Smsmith * try to be smarter about this, but a more complete solution would involve 5178915Smsmith * scanning all of the ACPI namespace to find devices we're not currently 5278915Smsmith * aware of, and this raises questions about whether they should be left 5378915Smsmith * on, turned off, etc. 5478915Smsmith * 5578915Smsmith * XXX locking 5678915Smsmith */ 5778915Smsmith 5878915SmsmithMALLOC_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; 90119529Snjl ACPI_INTEGER ap_systemlevel; 91119529Snjl ACPI_INTEGER 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; 9878915Smsmith 99119529Snjlstatic ACPI_STATUS acpi_pwr_register_consumer(ACPI_HANDLE consumer); 100128248Snjl#ifdef notyet 101119529Snjlstatic ACPI_STATUS acpi_pwr_deregister_consumer(ACPI_HANDLE consumer); 102128248Snjl#endif /* notyet */ 103119529Snjlstatic ACPI_STATUS acpi_pwr_register_resource(ACPI_HANDLE res); 104128248Snjl#ifdef notyet 105119529Snjlstatic ACPI_STATUS acpi_pwr_deregister_resource(ACPI_HANDLE res); 106128248Snjl#endif /* notyet */ 107119529Snjlstatic void acpi_pwr_reference_resource(ACPI_OBJECT *obj, 108119529Snjl void *arg); 109131340Snjlstatic int acpi_pwr_dereference_resource(struct acpi_powerconsumer 110131340Snjl *pc); 111119529Snjlstatic ACPI_STATUS acpi_pwr_switch_power(void); 112119529Snjlstatic struct acpi_powerresource 113119529Snjl *acpi_pwr_find_resource(ACPI_HANDLE res); 114119529Snjlstatic struct acpi_powerconsumer 115119529Snjl *acpi_pwr_find_consumer(ACPI_HANDLE consumer); 11678915Smsmith 117119529Snjl/* Initialise our lists. */ 11878915Smsmithstatic void 11978915Smsmithacpi_pwr_init(void *junk) 12078915Smsmith{ 12178915Smsmith TAILQ_INIT(&acpi_powerresources); 12278915Smsmith TAILQ_INIT(&acpi_powerconsumers); 12378915Smsmith} 12478915SmsmithSYSINIT(acpi_powerresource, SI_SUB_TUNABLES, SI_ORDER_ANY, acpi_pwr_init, NULL); 12578915Smsmith 12678915Smsmith/* 12778915Smsmith * Register a power resource. 12878915Smsmith * 12978915Smsmith * It's OK to call this if we already know about the resource. 13078915Smsmith */ 13178915Smsmithstatic ACPI_STATUS 13278915Smsmithacpi_pwr_register_resource(ACPI_HANDLE res) 13378915Smsmith{ 13478915Smsmith ACPI_STATUS status; 13578915Smsmith ACPI_BUFFER buf; 13679493Smsmith ACPI_OBJECT *obj; 13778915Smsmith struct acpi_powerresource *rp, *srp; 13878915Smsmith 13996926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 14078915Smsmith 14178915Smsmith rp = NULL; 14291125Smsmith buf.Pointer = NULL; 14378915Smsmith 144119529Snjl /* Look to see if we know about this resource */ 14578915Smsmith if (acpi_pwr_find_resource(res) != NULL) 146119529Snjl return_ACPI_STATUS (AE_OK); /* already know about it */ 14778915Smsmith 148119529Snjl /* Allocate a new resource */ 14978915Smsmith if ((rp = malloc(sizeof(*rp), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) { 15078915Smsmith status = AE_NO_MEMORY; 15178915Smsmith goto out; 15278915Smsmith } 15378915Smsmith TAILQ_INIT(&rp->ap_references); 15478915Smsmith rp->ap_resource = res; 15578915Smsmith 156119529Snjl /* Get the Power Resource object */ 15791125Smsmith buf.Length = ACPI_ALLOCATE_BUFFER; 15891125Smsmith if (ACPI_FAILURE(status = AcpiEvaluateObject(res, NULL, NULL, &buf))) { 15982372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "no power resource object\n")); 16078915Smsmith goto out; 16178915Smsmith } 16278915Smsmith obj = buf.Pointer; 16379493Smsmith if (obj->Type != ACPI_TYPE_POWER) { 164119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 165119529Snjl "questionable power resource object %s\n", 166119529Snjl acpi_name(res))); 16779493Smsmith status = AE_TYPE; 16879493Smsmith goto out; 16978915Smsmith } 17079493Smsmith rp->ap_systemlevel = obj->PowerResource.SystemLevel; 17179493Smsmith rp->ap_order = obj->PowerResource.ResourceOrder; 17278915Smsmith 173119529Snjl /* Sort the resource into the list */ 17478915Smsmith status = AE_OK; 17578915Smsmith srp = TAILQ_FIRST(&acpi_powerresources); 176119529Snjl if (srp == NULL || rp->ap_order < srp->ap_order) { 17778915Smsmith TAILQ_INSERT_HEAD(&acpi_powerresources, rp, ap_link); 17879357Smsmith goto done; 17978915Smsmith } 180119529Snjl TAILQ_FOREACH(srp, &acpi_powerresources, ap_link) { 18178915Smsmith if (rp->ap_order < srp->ap_order) { 18278915Smsmith TAILQ_INSERT_BEFORE(srp, rp, ap_link); 18379357Smsmith goto done; 18478915Smsmith } 185119529Snjl } 18678915Smsmith TAILQ_INSERT_TAIL(&acpi_powerresources, rp, ap_link); 18779357Smsmith 18879357Smsmith done: 189119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 190119529Snjl "registered power resource %s\n", acpi_name(res))); 191119529Snjl 19278915Smsmith out: 19391125Smsmith if (buf.Pointer != NULL) 19491125Smsmith AcpiOsFree(buf.Pointer); 195119529Snjl if (ACPI_FAILURE(status) && rp != NULL) 19678915Smsmith free(rp, M_ACPIPWR); 197119529Snjl return_ACPI_STATUS (status); 19878915Smsmith} 19978915Smsmith 200128248Snjl#ifdef notyet 20178915Smsmith/* 20278915Smsmith * Deregister a power resource. 20378915Smsmith */ 20478915Smsmithstatic ACPI_STATUS 20578915Smsmithacpi_pwr_deregister_resource(ACPI_HANDLE res) 20678915Smsmith{ 20778915Smsmith struct acpi_powerresource *rp; 20878915Smsmith 20996926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 21078915Smsmith 21178915Smsmith rp = NULL; 21278915Smsmith 213119529Snjl /* Find the resource */ 21478915Smsmith if ((rp = acpi_pwr_find_resource(res)) == NULL) 215119529Snjl return_ACPI_STATUS (AE_BAD_PARAMETER); 21678915Smsmith 217119529Snjl /* Check that there are no consumers referencing this resource */ 21878915Smsmith if (TAILQ_FIRST(&rp->ap_references) != NULL) 219119529Snjl return_ACPI_STATUS (AE_BAD_PARAMETER); 22078915Smsmith 221119529Snjl /* Pull it off the list and free it */ 22278915Smsmith TAILQ_REMOVE(&acpi_powerresources, rp, ap_link); 22378915Smsmith free(rp, M_ACPIPWR); 22478915Smsmith 225119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power resource %s\n", 226119529Snjl acpi_name(res))); 22778915Smsmith 228119529Snjl return_ACPI_STATUS (AE_OK); 22978915Smsmith} 230128248Snjl#endif /* notyet */ 23178915Smsmith 23278915Smsmith/* 23378915Smsmith * Register a power consumer. 23478915Smsmith * 23578915Smsmith * It's OK to call this if we already know about the consumer. 23678915Smsmith */ 23778915Smsmithstatic ACPI_STATUS 23878915Smsmithacpi_pwr_register_consumer(ACPI_HANDLE consumer) 23978915Smsmith{ 24078915Smsmith struct acpi_powerconsumer *pc; 24178915Smsmith 24296926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 24378915Smsmith 244119529Snjl /* Check to see whether we know about this consumer already */ 24578915Smsmith if ((pc = acpi_pwr_find_consumer(consumer)) != NULL) 246119529Snjl return_ACPI_STATUS (AE_OK); 24778915Smsmith 248119529Snjl /* Allocate a new power consumer */ 24978915Smsmith if ((pc = malloc(sizeof(*pc), M_ACPIPWR, M_NOWAIT)) == NULL) 250119529Snjl return_ACPI_STATUS (AE_NO_MEMORY); 25178915Smsmith TAILQ_INSERT_HEAD(&acpi_powerconsumers, pc, ac_link); 25278915Smsmith TAILQ_INIT(&pc->ac_references); 25378915Smsmith pc->ac_consumer = consumer; 25478915Smsmith 255119529Snjl /* XXX we should try to find its current state */ 256119529Snjl pc->ac_state = ACPI_STATE_UNKNOWN; 25778915Smsmith 258119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "registered power consumer %s\n", 259119529Snjl acpi_name(consumer))); 26078915Smsmith 261119529Snjl return_ACPI_STATUS (AE_OK); 26278915Smsmith} 26378915Smsmith 264128248Snjl#ifdef notyet 26578915Smsmith/* 26678915Smsmith * Deregister a power consumer. 26778915Smsmith * 26878915Smsmith * This should only be done once the consumer has been powered off. 26978915Smsmith * (XXX is this correct? Check once implemented) 27078915Smsmith */ 27178915Smsmithstatic ACPI_STATUS 27278915Smsmithacpi_pwr_deregister_consumer(ACPI_HANDLE consumer) 27378915Smsmith{ 27478915Smsmith struct acpi_powerconsumer *pc; 27578915Smsmith 27696926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 27778915Smsmith 278119529Snjl /* Find the consumer */ 27978915Smsmith if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) 280119529Snjl return_ACPI_STATUS (AE_BAD_PARAMETER); 28178915Smsmith 282119529Snjl /* Make sure the consumer's not referencing anything right now */ 28378915Smsmith if (TAILQ_FIRST(&pc->ac_references) != NULL) 284119529Snjl return_ACPI_STATUS (AE_BAD_PARAMETER); 28578915Smsmith 286119529Snjl /* Pull the consumer off the list and free it */ 28778915Smsmith TAILQ_REMOVE(&acpi_powerconsumers, pc, ac_link); 28878915Smsmith 289119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power consumer %s\n", 290119529Snjl acpi_name(consumer))); 29178915Smsmith 292119529Snjl return_ACPI_STATUS (AE_OK); 29378915Smsmith} 294128248Snjl#endif /* notyet */ 29578915Smsmith 29678915Smsmith/* 29778915Smsmith * Set a power consumer to a particular power state. 29878915Smsmith */ 29978915SmsmithACPI_STATUS 30078915Smsmithacpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state) 30178915Smsmith{ 30278915Smsmith struct acpi_powerconsumer *pc; 30382084Siwasaki ACPI_HANDLE method_handle, reslist_handle, pr0_handle; 30478915Smsmith ACPI_BUFFER reslist_buffer; 30578915Smsmith ACPI_OBJECT *reslist_object; 30678915Smsmith ACPI_STATUS status; 30778915Smsmith char *method_name, *reslist_name; 30878915Smsmith int res_changed; 30978915Smsmith 31096926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 31178915Smsmith 312128252Snjl /* It's never ok to switch a non-existent consumer. */ 313128252Snjl if (consumer == NULL) 314128252Snjl return_ACPI_STATUS (AE_NOT_FOUND); 315128252Snjl 316119529Snjl /* Find the consumer */ 31778915Smsmith if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) { 31891125Smsmith if (ACPI_FAILURE(status = acpi_pwr_register_consumer(consumer))) 319119529Snjl return_ACPI_STATUS (status); 32078915Smsmith if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) { 321119529Snjl return_ACPI_STATUS (AE_ERROR); /* something very wrong */ 32278915Smsmith } 32378915Smsmith } 32478915Smsmith 325119529Snjl /* Check for valid transitions */ 326119529Snjl if (pc->ac_state == ACPI_STATE_D3 && state != ACPI_STATE_D0) 327119529Snjl return_ACPI_STATUS (AE_BAD_PARAMETER); /* can only go to D0 from D3 */ 32878915Smsmith 329119529Snjl /* Find transition mechanism(s) */ 330130208Snjl switch (state) { 33178915Smsmith case ACPI_STATE_D0: 33278915Smsmith method_name = "_PS0"; 33378915Smsmith reslist_name = "_PR0"; 33478915Smsmith break; 33578915Smsmith case ACPI_STATE_D1: 33678915Smsmith method_name = "_PS1"; 33778915Smsmith reslist_name = "_PR1"; 33878915Smsmith break; 33978915Smsmith case ACPI_STATE_D2: 34078915Smsmith method_name = "_PS2"; 34178915Smsmith reslist_name = "_PR2"; 34278915Smsmith break; 34378915Smsmith case ACPI_STATE_D3: 34478915Smsmith method_name = "_PS3"; 34578915Smsmith reslist_name = "_PR3"; 34678915Smsmith break; 34778915Smsmith default: 348119529Snjl return_ACPI_STATUS (AE_BAD_PARAMETER); 34978915Smsmith } 35082372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "setup to switch %s D%d -> D%d\n", 351119529Snjl acpi_name(consumer), pc->ac_state, state)); 35278915Smsmith 35378915Smsmith /* 35478915Smsmith * Verify that this state is supported, ie. one of method or 35578915Smsmith * reslist must be present. We need to do this before we go 35678915Smsmith * dereferencing resources (since we might be trying to go to 35778915Smsmith * a state we don't support). 35878915Smsmith * 35978915Smsmith * Note that if any states are supported, the device has to 36078915Smsmith * support D0 and D3. It's never an error to try to go to 36178915Smsmith * D0. 36278915Smsmith */ 363130208Snjl status = AE_BAD_PARAMETER; 36491125Smsmith reslist_buffer.Pointer = NULL; 36582084Siwasaki reslist_object = NULL; 36691125Smsmith if (ACPI_FAILURE(AcpiGetHandle(consumer, method_name, &method_handle))) 36778915Smsmith method_handle = NULL; 36891125Smsmith if (ACPI_FAILURE(AcpiGetHandle(consumer, reslist_name, &reslist_handle))) 36978915Smsmith reslist_handle = NULL; 370119529Snjl if (reslist_handle == NULL && method_handle == NULL) { 37178915Smsmith if (state == ACPI_STATE_D0) { 37278915Smsmith pc->ac_state = ACPI_STATE_D0; 373119529Snjl return_ACPI_STATUS (AE_OK); 37478915Smsmith } 375119529Snjl if (state != ACPI_STATE_D3) 37682084Siwasaki goto bad; 37782084Siwasaki 378130208Snjl /* 379130208Snjl * Turn off the resources listed in _PR0 to go to D3. If there is 380130208Snjl * no _PR0 method, this object doesn't support ACPI power states. 381130208Snjl */ 382130208Snjl if (ACPI_FAILURE(AcpiGetHandle(consumer, "_PR0", &pr0_handle))) { 383130208Snjl status = AE_NOT_FOUND; 38482084Siwasaki goto bad; 385130208Snjl } 38691125Smsmith reslist_buffer.Length = ACPI_ALLOCATE_BUFFER; 387119529Snjl status = AcpiEvaluateObject(pr0_handle, NULL, NULL, &reslist_buffer); 388119529Snjl if (ACPI_FAILURE(status)) 38982084Siwasaki goto bad; 39082084Siwasaki reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer; 391119529Snjl if (reslist_object->Type != ACPI_TYPE_PACKAGE || 392130208Snjl reslist_object->Package.Count == 0) 39382084Siwasaki goto bad; 39491125Smsmith AcpiOsFree(reslist_buffer.Pointer); 39591125Smsmith reslist_buffer.Pointer = NULL; 39691125Smsmith reslist_object = NULL; 39778915Smsmith } 39878915Smsmith 39978915Smsmith /* 40078915Smsmith * Check that we can actually fetch the list of power resources 40178915Smsmith */ 40278915Smsmith if (reslist_handle != NULL) { 40391125Smsmith reslist_buffer.Length = ACPI_ALLOCATE_BUFFER; 404119529Snjl status = AcpiEvaluateObject(reslist_handle, NULL, NULL, 405119529Snjl &reslist_buffer); 406119529Snjl if (ACPI_FAILURE(status)) { 407119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 408119529Snjl "can't evaluate resource list %s\n", 409119529Snjl acpi_name(reslist_handle))); 41091125Smsmith goto out; 41178915Smsmith } 41278915Smsmith reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer; 41378915Smsmith if (reslist_object->Type != ACPI_TYPE_PACKAGE) { 414119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 415119529Snjl "resource list is not ACPI_TYPE_PACKAGE (%d)\n", 416119529Snjl reslist_object->Type)); 41791125Smsmith status = AE_TYPE; 41891125Smsmith goto out; 41978915Smsmith } 42078915Smsmith } 42178915Smsmith 42278915Smsmith /* 423125745Sjhb * Now we are ready to switch, so kill off any current power 424119529Snjl * resource references. 42578915Smsmith */ 426131340Snjl res_changed = acpi_pwr_dereference_resource(pc); 42778915Smsmith 42878915Smsmith /* 42978915Smsmith * Add new power resource references, if we have any. Traverse the 43078915Smsmith * package that we got from evaluating reslist_handle, and look up each 43178915Smsmith * of the resources that are referenced. 43278915Smsmith */ 43378915Smsmith if (reslist_object != NULL) { 43482372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "referencing %d new resources\n", 43582372Smsmith reslist_object->Package.Count)); 436119529Snjl acpi_ForeachPackageObject(reslist_object, acpi_pwr_reference_resource, 437119529Snjl pc); 43878915Smsmith res_changed = 1; 43978915Smsmith } 44078915Smsmith 44178915Smsmith /* 44278915Smsmith * If we changed anything in the resource list, we need to run a switch 44378915Smsmith * pass now. 44478915Smsmith */ 44591125Smsmith if (ACPI_FAILURE(status = acpi_pwr_switch_power())) { 446119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 447119529Snjl "failed to switch resources from %s to D%d\n", 44882372Smsmith acpi_name(consumer), state)); 449119529Snjl 450119529Snjl /* XXX is this appropriate? Should we return to previous state? */ 451119529Snjl goto out; 45278915Smsmith } 45378915Smsmith 454119529Snjl /* Invoke power state switch method (if present) */ 45578915Smsmith if (method_handle != NULL) { 456119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 457119529Snjl "invoking state transition method %s\n", 458119529Snjl acpi_name(method_handle))); 459119529Snjl status = AcpiEvaluateObject(method_handle, NULL, NULL, NULL); 460119529Snjl if (ACPI_FAILURE(status)) { 46191125Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to set state - %s\n", 462119529Snjl AcpiFormatException(status))); 46391125Smsmith pc->ac_state = ACPI_STATE_UNKNOWN; 464119529Snjl 465119529Snjl /* XXX Should we return to previous state? */ 466119529Snjl goto out; 46791125Smsmith } 46878915Smsmith } 46991125Smsmith 470119529Snjl /* Transition was successful */ 47178915Smsmith pc->ac_state = state; 472119529Snjl return_ACPI_STATUS (AE_OK); 47382084Siwasaki 47482084Siwasaki bad: 475119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 476119529Snjl "attempt to set unsupported state D%d\n", state)); 47791125Smsmith 47891125Smsmith out: 47991125Smsmith if (reslist_buffer.Pointer != NULL) 48091125Smsmith AcpiOsFree(reslist_buffer.Pointer); 481119529Snjl return_ACPI_STATUS (status); 48278915Smsmith} 48378915Smsmith 484131340Snjl/* Enable or disable a power resource for wake */ 485131340SnjlACPI_STATUS 486131340Snjlacpi_pwr_wake_enable(ACPI_HANDLE consumer, int enable) 487131340Snjl{ 488131340Snjl ACPI_STATUS status; 489131340Snjl struct acpi_powerconsumer *pc; 490131340Snjl struct acpi_prw_data prw; 491131340Snjl int i; 492131340Snjl 493131366Snjl ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 494131366Snjl 495131340Snjl if (consumer == NULL) 496131340Snjl return (AE_BAD_PARAMETER); 497131340Snjl 498131340Snjl if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) { 499131340Snjl if (ACPI_FAILURE(status = acpi_pwr_register_consumer(consumer))) 500131340Snjl return_ACPI_STATUS (status); 501131340Snjl if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) { 502131340Snjl return_ACPI_STATUS (AE_ERROR); /* something very wrong */ 503131340Snjl } 504131340Snjl } 505131340Snjl 506131340Snjl if (acpi_parse_prw(consumer, &prw) != 0) 507131340Snjl return (AE_OK); 508131340Snjl for (i = 0; i < prw.power_res_count; i++) 509131340Snjl if (enable) 510131340Snjl acpi_pwr_reference_resource(&prw.power_res[i], pc); 511131340Snjl else 512131340Snjl acpi_pwr_dereference_resource(pc); 513131340Snjl 514131340Snjl if (prw.power_res_count > 0) 515131340Snjl acpi_pwr_switch_power(); 516131340Snjl 517131340Snjl return (AE_OK); 518131340Snjl} 519131340Snjl 52078915Smsmith/* 52178915Smsmith * Called to create a reference between a power consumer and a power resource 52278915Smsmith * identified in the object. 52378915Smsmith */ 52478915Smsmithstatic void 52578915Smsmithacpi_pwr_reference_resource(ACPI_OBJECT *obj, void *arg) 52678915Smsmith{ 52778915Smsmith struct acpi_powerconsumer *pc = (struct acpi_powerconsumer *)arg; 52879357Smsmith struct acpi_powerreference *pr; 52979357Smsmith struct acpi_powerresource *rp; 53079357Smsmith ACPI_HANDLE res; 53179357Smsmith ACPI_STATUS status; 53278915Smsmith 53396926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 53478915Smsmith 535128047Snjl res = acpi_GetReference(NULL, obj); 536128047Snjl if (res == NULL) { 537119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 538128047Snjl "can't create a power reference for object type %d\n", 539119529Snjl obj->Type)); 54079357Smsmith return_VOID; 54179357Smsmith } 54278915Smsmith 543119529Snjl /* Create/look up the resource */ 54479357Smsmith if (ACPI_FAILURE(status = acpi_pwr_register_resource(res))) { 545119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 546119529Snjl "couldn't register power resource %s - %s\n", 547119529Snjl obj->String.Pointer, AcpiFormatException(status))); 54879357Smsmith return_VOID; 54979357Smsmith } 55079357Smsmith if ((rp = acpi_pwr_find_resource(res)) == NULL) { 55182372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "power resource list corrupted\n")); 55279357Smsmith return_VOID; 55379357Smsmith } 554119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "found power resource %s\n", 555119529Snjl acpi_name(rp->ap_resource))); 55679357Smsmith 557119529Snjl /* Create a reference between the consumer and resource */ 55879357Smsmith if ((pr = malloc(sizeof(*pr), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) { 559119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 560119529Snjl "allocation failed for a power consumer reference\n")); 56179357Smsmith return_VOID; 56279357Smsmith } 56379357Smsmith pr->ar_consumer = pc; 56479357Smsmith pr->ar_resource = rp; 56579357Smsmith TAILQ_INSERT_TAIL(&pc->ac_references, pr, ar_clink); 56679357Smsmith TAILQ_INSERT_TAIL(&rp->ap_references, pr, ar_rlink); 567131340Snjl 56878915Smsmith return_VOID; 56978915Smsmith} 57078915Smsmith 571131340Snjlstatic int 572131340Snjlacpi_pwr_dereference_resource(struct acpi_powerconsumer *pc) 573131340Snjl{ 574131340Snjl struct acpi_powerreference *pr; 575131340Snjl int changed; 57678915Smsmith 577131366Snjl ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 578131366Snjl 579131340Snjl changed = 0; 580131340Snjl while ((pr = TAILQ_FIRST(&pc->ac_references)) != NULL) { 581131340Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "removing reference to %s\n", 582131340Snjl acpi_name(pr->ar_resource->ap_resource))); 583131340Snjl TAILQ_REMOVE(&pr->ar_resource->ap_references, pr, ar_rlink); 584131340Snjl TAILQ_REMOVE(&pc->ac_references, pr, ar_clink); 585131340Snjl free(pr, M_ACPIPWR); 586131340Snjl changed = 1; 587131340Snjl } 588131340Snjl 589131340Snjl return (changed); 590131340Snjl} 591131340Snjl 59278915Smsmith/* 59378915Smsmith * Switch power resources to conform to the desired state. 59478915Smsmith * 59578915Smsmith * Consumers may have modified the power resource list in an arbitrary 59678915Smsmith * fashion; we sweep it in sequence order. 59778915Smsmith */ 59878915Smsmithstatic ACPI_STATUS 59978915Smsmithacpi_pwr_switch_power(void) 60078915Smsmith{ 60178915Smsmith struct acpi_powerresource *rp; 60278915Smsmith ACPI_STATUS status; 60378915Smsmith int cur; 60478915Smsmith 60596926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 60678915Smsmith 60778915Smsmith /* 60878915Smsmith * Sweep the list forwards turning things on. 60978915Smsmith */ 61078915Smsmith TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) { 61179357Smsmith if (TAILQ_FIRST(&rp->ap_references) == NULL) { 612119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 613119529Snjl "%s has no references, not turning on\n", 614119529Snjl acpi_name(rp->ap_resource))); 61579357Smsmith continue; 61679357Smsmith } 61778915Smsmith 618119529Snjl /* We could cache this if we trusted it not to change under us */ 619126560Snjl status = acpi_GetInteger(rp->ap_resource, "_STA", &cur); 620119529Snjl if (ACPI_FAILURE(status)) { 62182372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n", 62282372Smsmith acpi_name(rp->ap_resource), status)); 623119529Snjl 624119529Snjl /* XXX is this correct? Always switch if in doubt? */ 625119529Snjl continue; 62678915Smsmith } 62778915Smsmith 62878915Smsmith /* 62978915Smsmith * Switch if required. Note that we ignore the result of the switch 63078915Smsmith * effort; we don't know what to do if it fails, so checking wouldn't 63178915Smsmith * help much. 63278915Smsmith */ 63379357Smsmith if (cur != ACPI_PWR_ON) { 634119529Snjl status = AcpiEvaluateObject(rp->ap_resource, "_ON", NULL, NULL); 635119529Snjl if (ACPI_FAILURE(status)) { 636119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 637119529Snjl "failed to switch %s on - %s\n", 638119529Snjl acpi_name(rp->ap_resource), 639119529Snjl AcpiFormatException(status))); 64079357Smsmith } else { 641119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s on\n", 642119529Snjl acpi_name(rp->ap_resource))); 64379357Smsmith } 64479357Smsmith } else { 645119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already on\n", 646119529Snjl acpi_name(rp->ap_resource))); 64779357Smsmith } 64878915Smsmith } 64978915Smsmith 650119529Snjl /* Sweep the list backwards turning things off. */ 651119529Snjl TAILQ_FOREACH_REVERSE(rp, &acpi_powerresources, acpi_powerresource_list, 652119529Snjl ap_link) { 653119529Snjl 65479357Smsmith if (TAILQ_FIRST(&rp->ap_references) != NULL) { 655119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 656119529Snjl "%s has references, not turning off\n", 657119529Snjl acpi_name(rp->ap_resource))); 65879357Smsmith continue; 65979357Smsmith } 66078915Smsmith 661119529Snjl /* We could cache this if we trusted it not to change under us */ 662126560Snjl status = acpi_GetInteger(rp->ap_resource, "_STA", &cur); 663119529Snjl if (ACPI_FAILURE(status)) { 66482372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n", 66582372Smsmith acpi_name(rp->ap_resource), status)); 666119529Snjl /* XXX is this correct? Always switch if in doubt? */ 667119529Snjl continue; 66878915Smsmith } 66978915Smsmith 67078915Smsmith /* 67178915Smsmith * Switch if required. Note that we ignore the result of the switch 67278915Smsmith * effort; we don't know what to do if it fails, so checking wouldn't 67378915Smsmith * help much. 67478915Smsmith */ 67579357Smsmith if (cur != ACPI_PWR_OFF) { 676119529Snjl status = AcpiEvaluateObject(rp->ap_resource, "_OFF", NULL, NULL); 677119529Snjl if (ACPI_FAILURE(status)) { 678119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 679119529Snjl "failed to switch %s off - %s\n", 680119529Snjl acpi_name(rp->ap_resource), 681119529Snjl AcpiFormatException(status))); 68279357Smsmith } else { 683119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s off\n", 684119529Snjl acpi_name(rp->ap_resource))); 68579357Smsmith } 68679357Smsmith } else { 687119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already off\n", 688119529Snjl acpi_name(rp->ap_resource))); 68979357Smsmith } 69078915Smsmith } 691119529Snjl 692119529Snjl return_ACPI_STATUS (AE_OK); 69378915Smsmith} 69478915Smsmith 69578915Smsmith/* 69678915Smsmith * Find a power resource's control structure. 69778915Smsmith */ 69878915Smsmithstatic struct acpi_powerresource * 69978915Smsmithacpi_pwr_find_resource(ACPI_HANDLE res) 70078915Smsmith{ 70178915Smsmith struct acpi_powerresource *rp; 70278915Smsmith 70396926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 70478915Smsmith 705119529Snjl TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) { 70678915Smsmith if (rp->ap_resource == res) 70778915Smsmith break; 708119529Snjl } 709119529Snjl 710119529Snjl return_PTR (rp); 71178915Smsmith} 71278915Smsmith 71378915Smsmith/* 71478915Smsmith * Find a power consumer's control structure. 71578915Smsmith */ 71678915Smsmithstatic struct acpi_powerconsumer * 71778915Smsmithacpi_pwr_find_consumer(ACPI_HANDLE consumer) 71878915Smsmith{ 71978915Smsmith struct acpi_powerconsumer *pc; 72078915Smsmith 72196926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 72278915Smsmith 723119529Snjl TAILQ_FOREACH(pc, &acpi_powerconsumers, ac_link) { 72478915Smsmith if (pc->ac_consumer == consumer) 72578915Smsmith break; 726119529Snjl } 727119529Snjl 728119529Snjl return_PTR (pc); 72978915Smsmith} 730