acpi_powerres.c revision 82372
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 * $FreeBSD: head/sys/dev/acpica/acpi_powerres.c 82372 2001-08-26 22:50:15Z msmith $ 2778915Smsmith */ 2878915Smsmith 2978915Smsmith#include "opt_acpi.h" /* XXX trim includes */ 3078915Smsmith#include <sys/param.h> 3178915Smsmith#include <sys/kernel.h> 3278915Smsmith#include <sys/proc.h> 3378915Smsmith#include <sys/lock.h> 3478915Smsmith#include <sys/malloc.h> 3578915Smsmith#include <sys/mutex.h> 3678915Smsmith#include <sys/bus.h> 3778915Smsmith#include <sys/conf.h> 3878915Smsmith#include <sys/ioccom.h> 3978915Smsmith#include <sys/reboot.h> 4078915Smsmith#include <sys/sysctl.h> 4178915Smsmith#include <sys/systm.h> 4278915Smsmith#include <sys/ctype.h> 4378915Smsmith 4478915Smsmith#include <machine/clock.h> 4578915Smsmith 4678915Smsmith#include <machine/resource.h> 4778915Smsmith 4878915Smsmith#include "acpi.h" 4978915Smsmith 5078915Smsmith#include <dev/acpica/acpivar.h> 5178915Smsmith#include <dev/acpica/acpiio.h> 5278915Smsmith 5378915Smsmith/* 5478915Smsmith * ACPI power resource management. 5578915Smsmith * 5678915Smsmith * Power resource behaviour is slightly complicated by the fact that 5778915Smsmith * a single power resource may provide power for more than one device. 5878915Smsmith * Thus, we must track the device(s) being powered by a given power 5978915Smsmith * resource, and only deactivate it when there are no powered devices. 6078915Smsmith * 6178915Smsmith * Note that this only manages resources for known devices. There is an 6278915Smsmith * ugly case where we may turn of power to a device which is in use because 6378915Smsmith * we don't know that it depends on a given resource. We should perhaps 6478915Smsmith * try to be smarter about this, but a more complete solution would involve 6578915Smsmith * scanning all of the ACPI namespace to find devices we're not currently 6678915Smsmith * aware of, and this raises questions about whether they should be left 6778915Smsmith * on, turned off, etc. 6878915Smsmith * 6978915Smsmith * XXX locking 7078915Smsmith */ 7178915Smsmith 7278915SmsmithMALLOC_DEFINE(M_ACPIPWR, "acpipwr", "ACPI power resources"); 7378915Smsmith 7478915Smsmith/* 7578915Smsmith * Hooks for the ACPI CA debugging infrastructure 7678915Smsmith */ 7778993Smsmith#define _COMPONENT ACPI_POWER 7878915SmsmithMODULE_NAME("POWERRES") 7978915Smsmith 8079357Smsmith/* return values from _STA on a power resource */ 8179357Smsmith#define ACPI_PWR_OFF 0 8279357Smsmith#define ACPI_PWR_ON 1 8379357Smsmith 8478915Smsmith/* 8578915Smsmith * A relationship between a power resource and a consumer. 8678915Smsmith */ 8778915Smsmithstruct acpi_powerreference { 8878915Smsmith struct acpi_powerconsumer *ar_consumer; 8978915Smsmith struct acpi_powerresource *ar_resource; 9078915Smsmith TAILQ_ENTRY(acpi_powerreference) ar_rlink; /* link on resource list */ 9178915Smsmith TAILQ_ENTRY(acpi_powerreference) ar_clink; /* link on consumer */ 9278915Smsmith}; 9378915Smsmith 9478915Smsmith/* 9578915Smsmith * A power-managed device. 9678915Smsmith */ 9778915Smsmithstruct acpi_powerconsumer { 9878915Smsmith ACPI_HANDLE ac_consumer; /* device which is powered */ 9978915Smsmith int ac_state; 10078915Smsmith TAILQ_ENTRY(acpi_powerconsumer) ac_link; 10178915Smsmith TAILQ_HEAD(,acpi_powerreference) ac_references; 10278915Smsmith}; 10378915Smsmith 10478915Smsmith/* 10578915Smsmith * A power resource. 10678915Smsmith */ 10778915Smsmithstruct acpi_powerresource { 10878915Smsmith TAILQ_ENTRY(acpi_powerresource) ap_link; 10978915Smsmith TAILQ_HEAD(,acpi_powerreference) ap_references; 11078915Smsmith ACPI_HANDLE ap_resource; /* the resource's handle */ 11178915Smsmith ACPI_INTEGER ap_systemlevel; 11278915Smsmith ACPI_INTEGER ap_order; 11378915Smsmith}; 11478915Smsmith 11578915SmsmithTAILQ_HEAD(acpi_powerresource_list, acpi_powerresource) acpi_powerresources; 11678915SmsmithTAILQ_HEAD(acpi_powerconsumer_list, acpi_powerconsumer) acpi_powerconsumers; 11778915Smsmith 11878915Smsmithstatic ACPI_STATUS acpi_pwr_register_consumer(ACPI_HANDLE consumer); 11978915Smsmithstatic ACPI_STATUS acpi_pwr_deregister_consumer(ACPI_HANDLE consumer); 12078915Smsmithstatic ACPI_STATUS acpi_pwr_register_resource(ACPI_HANDLE res); 12178915Smsmithstatic ACPI_STATUS acpi_pwr_deregister_resource(ACPI_HANDLE res); 12278915Smsmithstatic void acpi_pwr_reference_resource(ACPI_OBJECT *obj, void *arg); 12378915Smsmithstatic ACPI_STATUS acpi_pwr_switch_power(void); 12478915Smsmithstatic struct acpi_powerresource *acpi_pwr_find_resource(ACPI_HANDLE res); 12578915Smsmithstatic struct acpi_powerconsumer *acpi_pwr_find_consumer(ACPI_HANDLE consumer); 12678915Smsmith 12778915Smsmith/* 12878915Smsmith * Initialise our lists. 12978915Smsmith */ 13078915Smsmithstatic void 13178915Smsmithacpi_pwr_init(void *junk) 13278915Smsmith{ 13378915Smsmith TAILQ_INIT(&acpi_powerresources); 13478915Smsmith TAILQ_INIT(&acpi_powerconsumers); 13578915Smsmith} 13678915SmsmithSYSINIT(acpi_powerresource, SI_SUB_TUNABLES, SI_ORDER_ANY, acpi_pwr_init, NULL); 13778915Smsmith 13878915Smsmith/* 13978915Smsmith * Register a power resource. 14078915Smsmith * 14178915Smsmith * It's OK to call this if we already know about the resource. 14278915Smsmith */ 14378915Smsmithstatic ACPI_STATUS 14478915Smsmithacpi_pwr_register_resource(ACPI_HANDLE res) 14578915Smsmith{ 14678915Smsmith ACPI_STATUS status; 14778915Smsmith ACPI_BUFFER buf; 14879493Smsmith ACPI_OBJECT *obj; 14978915Smsmith struct acpi_powerresource *rp, *srp; 15078915Smsmith 15178915Smsmith FUNCTION_TRACE(__func__); 15278915Smsmith 15378915Smsmith rp = NULL; 15478915Smsmith obj = NULL; 15578915Smsmith 15678915Smsmith /* look to see if we know about this resource */ 15778915Smsmith if (acpi_pwr_find_resource(res) != NULL) 15878915Smsmith return_ACPI_STATUS(AE_OK); /* already know about it */ 15978915Smsmith 16078915Smsmith /* allocate a new resource */ 16178915Smsmith if ((rp = malloc(sizeof(*rp), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) { 16278915Smsmith status = AE_NO_MEMORY; 16378915Smsmith goto out; 16478915Smsmith } 16578915Smsmith TAILQ_INIT(&rp->ap_references); 16678915Smsmith rp->ap_resource = res; 16778915Smsmith 16878915Smsmith /* get the Power Resource object */ 16978915Smsmith if ((status = acpi_EvaluateIntoBuffer(res, NULL, NULL, &buf)) != AE_OK) { 17082372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "no power resource object\n")); 17178915Smsmith goto out; 17278915Smsmith } 17378915Smsmith obj = buf.Pointer; 17479493Smsmith if (obj->Type != ACPI_TYPE_POWER) { 17582372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "questionable power resource object %s\n", acpi_name(res))); 17679493Smsmith status = AE_TYPE; 17779493Smsmith goto out; 17878915Smsmith } 17979493Smsmith rp->ap_systemlevel = obj->PowerResource.SystemLevel; 18079493Smsmith rp->ap_order = obj->PowerResource.ResourceOrder; 18178915Smsmith 18278915Smsmith /* sort the resource into the list */ 18378915Smsmith status = AE_OK; 18478915Smsmith srp = TAILQ_FIRST(&acpi_powerresources); 18578915Smsmith if ((srp == NULL) || (rp->ap_order < srp->ap_order)) { 18678915Smsmith TAILQ_INSERT_HEAD(&acpi_powerresources, rp, ap_link); 18779357Smsmith goto done; 18878915Smsmith } 18978915Smsmith TAILQ_FOREACH(srp, &acpi_powerresources, ap_link) 19078915Smsmith if (rp->ap_order < srp->ap_order) { 19178915Smsmith TAILQ_INSERT_BEFORE(srp, rp, ap_link); 19279357Smsmith goto done; 19378915Smsmith } 19478915Smsmith TAILQ_INSERT_TAIL(&acpi_powerresources, rp, ap_link); 19579357Smsmith 19679357Smsmith done: 19782372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "registered power resource %s\n", acpi_name(res))); 19878915Smsmith out: 19978915Smsmith if (obj != NULL) 20078915Smsmith AcpiOsFree(obj); 20178915Smsmith if ((status != AE_OK) && (rp != NULL)) 20278915Smsmith free(rp, M_ACPIPWR); 20378915Smsmith return_ACPI_STATUS(status); 20478915Smsmith} 20578915Smsmith 20678915Smsmith/* 20778915Smsmith * Deregister a power resource. 20878915Smsmith */ 20978915Smsmithstatic ACPI_STATUS 21078915Smsmithacpi_pwr_deregister_resource(ACPI_HANDLE res) 21178915Smsmith{ 21278915Smsmith struct acpi_powerresource *rp; 21378915Smsmith 21478915Smsmith FUNCTION_TRACE(__func__); 21578915Smsmith 21678915Smsmith rp = NULL; 21778915Smsmith 21878915Smsmith /* find the resource */ 21978915Smsmith if ((rp = acpi_pwr_find_resource(res)) == NULL) 22078915Smsmith return_ACPI_STATUS(AE_BAD_PARAMETER); 22178915Smsmith 22278915Smsmith /* check that there are no consumers referencing this resource */ 22378915Smsmith if (TAILQ_FIRST(&rp->ap_references) != NULL) 22478915Smsmith return_ACPI_STATUS(AE_BAD_PARAMETER); 22578915Smsmith 22678915Smsmith /* pull it off the list and free it */ 22778915Smsmith TAILQ_REMOVE(&acpi_powerresources, rp, ap_link); 22878915Smsmith free(rp, M_ACPIPWR); 22978915Smsmith 23082372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power resource %s\n", acpi_name(res))); 23178915Smsmith 23278915Smsmith return_ACPI_STATUS(AE_OK); 23378915Smsmith} 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 24578915Smsmith FUNCTION_TRACE(__func__); 24678915Smsmith 24778915Smsmith /* check to see whether we know about this consumer already */ 24878915Smsmith if ((pc = acpi_pwr_find_consumer(consumer)) != NULL) 24978915Smsmith return_ACPI_STATUS(AE_OK); 25078915Smsmith 25178915Smsmith /* allocate a new power consumer */ 25278915Smsmith if ((pc = malloc(sizeof(*pc), M_ACPIPWR, M_NOWAIT)) == NULL) 25378915Smsmith return_ACPI_STATUS(AE_NO_MEMORY); 25478915Smsmith TAILQ_INSERT_HEAD(&acpi_powerconsumers, pc, ac_link); 25578915Smsmith TAILQ_INIT(&pc->ac_references); 25678915Smsmith pc->ac_consumer = consumer; 25778915Smsmith 25878915Smsmith pc->ac_state = ACPI_STATE_UNKNOWN; /* XXX we should try to find its current state */ 25978915Smsmith 26082372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "registered power consumer %s\n", acpi_name(consumer))); 26178915Smsmith 26278915Smsmith return_ACPI_STATUS(AE_OK); 26378915Smsmith} 26478915Smsmith 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 27678915Smsmith FUNCTION_TRACE(__func__); 27778915Smsmith 27878915Smsmith /* find the consumer */ 27978915Smsmith if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) 28078915Smsmith return_ACPI_STATUS(AE_BAD_PARAMETER); 28178915Smsmith 28278915Smsmith /* make sure the consumer's not referencing anything right now */ 28378915Smsmith if (TAILQ_FIRST(&pc->ac_references) != NULL) 28478915Smsmith return_ACPI_STATUS(AE_BAD_PARAMETER); 28578915Smsmith 28678915Smsmith /* pull the consumer off the list and free it */ 28778915Smsmith TAILQ_REMOVE(&acpi_powerconsumers, pc, ac_link); 28878915Smsmith 28982372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power consumer %s\n", acpi_name(consumer))); 29078915Smsmith 29178915Smsmith return_ACPI_STATUS(AE_OK); 29278915Smsmith} 29378915Smsmith 29478915Smsmith/* 29578915Smsmith * Set a power consumer to a particular power state. 29678915Smsmith */ 29778915SmsmithACPI_STATUS 29878915Smsmithacpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state) 29978915Smsmith{ 30078915Smsmith struct acpi_powerconsumer *pc; 30178915Smsmith struct acpi_powerreference *pr; 30282084Siwasaki ACPI_HANDLE method_handle, reslist_handle, pr0_handle; 30378915Smsmith ACPI_BUFFER reslist_buffer; 30478915Smsmith ACPI_OBJECT *reslist_object; 30578915Smsmith ACPI_STATUS status; 30678915Smsmith char *method_name, *reslist_name; 30778915Smsmith int res_changed; 30878915Smsmith 30978915Smsmith FUNCTION_TRACE(__func__); 31078915Smsmith 31178915Smsmith /* find the consumer */ 31278915Smsmith if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) { 31378915Smsmith if ((status = acpi_pwr_register_consumer(consumer)) != AE_OK) 31478915Smsmith return_ACPI_STATUS(status); 31578915Smsmith if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) { 31678915Smsmith return_ACPI_STATUS(AE_ERROR); /* something very wrong */ 31778915Smsmith } 31878915Smsmith } 31978915Smsmith 32078915Smsmith /* check for valid transitions */ 32178915Smsmith if ((pc->ac_state == ACPI_STATE_D3) && (state != ACPI_STATE_D0)) 32278915Smsmith return_ACPI_STATUS(AE_BAD_PARAMETER); /* can only go to D0 from D3 */ 32378915Smsmith 32478915Smsmith /* find transition mechanism(s) */ 32578915Smsmith switch(state) { 32678915Smsmith case ACPI_STATE_D0: 32778915Smsmith method_name = "_PS0"; 32878915Smsmith reslist_name = "_PR0"; 32978915Smsmith break; 33078915Smsmith case ACPI_STATE_D1: 33178915Smsmith method_name = "_PS1"; 33278915Smsmith reslist_name = "_PR1"; 33378915Smsmith break; 33478915Smsmith case ACPI_STATE_D2: 33578915Smsmith method_name = "_PS2"; 33678915Smsmith reslist_name = "_PR2"; 33778915Smsmith break; 33878915Smsmith case ACPI_STATE_D3: 33978915Smsmith method_name = "_PS3"; 34078915Smsmith reslist_name = "_PR3"; 34178915Smsmith break; 34278915Smsmith default: 34378915Smsmith return_ACPI_STATUS(AE_BAD_PARAMETER); 34478915Smsmith } 34582372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "setup to switch %s D%d -> D%d\n", 34682372Smsmith acpi_name(consumer), pc->ac_state, state)); 34778915Smsmith 34878915Smsmith /* 34978915Smsmith * Verify that this state is supported, ie. one of method or 35078915Smsmith * reslist must be present. We need to do this before we go 35178915Smsmith * dereferencing resources (since we might be trying to go to 35278915Smsmith * a state we don't support). 35378915Smsmith * 35478915Smsmith * Note that if any states are supported, the device has to 35578915Smsmith * support D0 and D3. It's never an error to try to go to 35678915Smsmith * D0. 35778915Smsmith */ 35882084Siwasaki reslist_object = NULL; 35978915Smsmith if (AcpiGetHandle(consumer, method_name, &method_handle) != AE_OK) 36078915Smsmith method_handle = NULL; 36178915Smsmith if (AcpiGetHandle(consumer, reslist_name, &reslist_handle) != AE_OK) 36278915Smsmith reslist_handle = NULL; 36378915Smsmith if ((reslist_handle == NULL) && (method_handle == NULL)) { 36478915Smsmith if (state == ACPI_STATE_D0) { 36578915Smsmith pc->ac_state = ACPI_STATE_D0; 36678915Smsmith return_ACPI_STATUS(AE_OK); 36778915Smsmith } 36882084Siwasaki if (state != ACPI_STATE_D3) { 36982084Siwasaki goto bad; 37082084Siwasaki } 37182084Siwasaki 37282084Siwasaki /* turn off the resources listed in _PR0 to go to D3. */ 37382084Siwasaki if (AcpiGetHandle(consumer, "_PR0", &pr0_handle) != AE_OK) { 37482084Siwasaki goto bad; 37582084Siwasaki } 37682084Siwasaki status = acpi_EvaluateIntoBuffer(pr0_handle, NULL, NULL, &reslist_buffer); 37782084Siwasaki if (status != AE_OK) { 37882084Siwasaki goto bad; 37982084Siwasaki } 38082084Siwasaki reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer; 38182084Siwasaki if ((reslist_object->Type != ACPI_TYPE_PACKAGE) || 38282084Siwasaki (reslist_object->Package.Count == 0)) { 38382084Siwasaki goto bad; 38482084Siwasaki } 38582084Siwasaki AcpiOsFree(reslist_object); 38678915Smsmith } 38778915Smsmith 38878915Smsmith /* 38978915Smsmith * Check that we can actually fetch the list of power resources 39078915Smsmith */ 39178915Smsmith if (reslist_handle != NULL) { 39278915Smsmith if ((status = acpi_EvaluateIntoBuffer(reslist_handle, NULL, NULL, &reslist_buffer)) != AE_OK) { 39382372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't evaluate resource list %s\n", 39482372Smsmith acpi_name(reslist_handle))); 39578915Smsmith return_ACPI_STATUS(status); 39678915Smsmith } 39778915Smsmith reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer; 39878915Smsmith if (reslist_object->Type != ACPI_TYPE_PACKAGE) { 39982372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "resource list is not ACPI_TYPE_PACKAGE (%d)\n", 40082372Smsmith reslist_object->Type)); 40178915Smsmith return_ACPI_STATUS(AE_TYPE); 40278915Smsmith } 40378915Smsmith } else { 40478915Smsmith reslist_object = NULL; 40578915Smsmith } 40678915Smsmith 40778915Smsmith /* 40878915Smsmith * Now we are ready to switch, so kill off any current power resource references. 40978915Smsmith */ 41078915Smsmith res_changed = 0; 41179357Smsmith while((pr = TAILQ_FIRST(&pc->ac_references)) != NULL) { 41279357Smsmith res_changed = 1; 41382372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "removing reference to %s\n", acpi_name(pr->ar_resource->ap_resource))); 41478915Smsmith TAILQ_REMOVE(&pr->ar_resource->ap_references, pr, ar_rlink); 41579357Smsmith TAILQ_REMOVE(&pc->ac_references, pr, ar_clink); 41679357Smsmith free(pr, M_ACPIPWR); 41778915Smsmith } 41878915Smsmith 41978915Smsmith /* 42078915Smsmith * Add new power resource references, if we have any. Traverse the 42178915Smsmith * package that we got from evaluating reslist_handle, and look up each 42278915Smsmith * of the resources that are referenced. 42378915Smsmith */ 42478915Smsmith if (reslist_object != NULL) { 42582372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "referencing %d new resources\n", 42682372Smsmith reslist_object->Package.Count)); 42778915Smsmith acpi_ForeachPackageObject(reslist_object, acpi_pwr_reference_resource, pc); 42878915Smsmith res_changed = 1; 42978915Smsmith } 43078915Smsmith 43178915Smsmith /* 43278915Smsmith * If we changed anything in the resource list, we need to run a switch 43378915Smsmith * pass now. 43478915Smsmith */ 43578915Smsmith if ((status = acpi_pwr_switch_power()) != AE_OK) { 43682372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to correctly switch resources to move %s to D%d\n", 43782372Smsmith acpi_name(consumer), state)); 43878915Smsmith return_ACPI_STATUS(status); /* XXX is this appropriate? Should we return to previous state? */ 43978915Smsmith } 44078915Smsmith 44178915Smsmith /* invoke power state switch method (if present) */ 44278915Smsmith if (method_handle != NULL) { 44382372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "invoking state transition method %s\n", 44482372Smsmith acpi_name(method_handle))); 44578915Smsmith if ((status = AcpiEvaluateObject(method_handle, NULL, NULL, NULL)) != AE_OK) 44678915Smsmith pc->ac_state = ACPI_STATE_UNKNOWN; 44778915Smsmith return_ACPI_STATUS(status); /* XXX is this appropriate? Should we return to previous state? */ 44878915Smsmith } 44978915Smsmith 45078915Smsmith /* transition was successful */ 45178915Smsmith pc->ac_state = state; 45278915Smsmith return_ACPI_STATUS(AE_OK); 45382084Siwasaki 45482084Siwasaki bad: 45582372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "attempt to set unsupported state D%d\n", 45682372Smsmith state)); 45782084Siwasaki if (reslist_object) 45882084Siwasaki AcpiOsFree(reslist_object); 45982084Siwasaki return_ACPI_STATUS(AE_BAD_PARAMETER); 46078915Smsmith} 46178915Smsmith 46278915Smsmith/* 46378915Smsmith * Called to create a reference between a power consumer and a power resource 46478915Smsmith * identified in the object. 46578915Smsmith */ 46678915Smsmithstatic void 46778915Smsmithacpi_pwr_reference_resource(ACPI_OBJECT *obj, void *arg) 46878915Smsmith{ 46978915Smsmith struct acpi_powerconsumer *pc = (struct acpi_powerconsumer *)arg; 47079357Smsmith struct acpi_powerreference *pr; 47179357Smsmith struct acpi_powerresource *rp; 47279357Smsmith ACPI_HANDLE res; 47379357Smsmith ACPI_STATUS status; 47478915Smsmith 47578915Smsmith FUNCTION_TRACE(__func__); 47678915Smsmith 47779357Smsmith /* check the object type */ 47879357Smsmith if (obj->Type != ACPI_TYPE_STRING) { 47982372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "don't know how to create a power reference to object type %d\n", 48082372Smsmith obj->Type)); 48179357Smsmith return_VOID; 48279357Smsmith } 48378915Smsmith 48482372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "building reference from %s to %s\n", 48582372Smsmith acpi_name(pc->ac_consumer), obj->String.Pointer)); 48679357Smsmith 48779357Smsmith /* get the handle of the resource */ 48879357Smsmith if (ACPI_FAILURE(status = AcpiGetHandle(NULL, obj->String.Pointer, &res))) { 48982372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "couldn't find power resource %s\n", 49082372Smsmith obj->String.Pointer)); 49179357Smsmith return_VOID; 49279357Smsmith } 49379357Smsmith 49479357Smsmith /* create/look up the resource */ 49579357Smsmith if (ACPI_FAILURE(status = acpi_pwr_register_resource(res))) { 49682372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "couldn't register power resource %s - %s\n", 49782372Smsmith obj->String.Pointer, AcpiFormatException(status))); 49879357Smsmith return_VOID; 49979357Smsmith } 50079357Smsmith if ((rp = acpi_pwr_find_resource(res)) == NULL) { 50182372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "power resource list corrupted\n")); 50279357Smsmith return_VOID; 50379357Smsmith } 50482372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "found power resource %s\n", acpi_name(rp->ap_resource))); 50579357Smsmith 50679357Smsmith /* create a reference between the consumer and resource */ 50779357Smsmith if ((pr = malloc(sizeof(*pr), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) { 50882372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "couldn't allocate memory for a power consumer reference\n")); 50979357Smsmith return_VOID; 51079357Smsmith } 51179357Smsmith pr->ar_consumer = pc; 51279357Smsmith pr->ar_resource = rp; 51379357Smsmith TAILQ_INSERT_TAIL(&pc->ac_references, pr, ar_clink); 51479357Smsmith TAILQ_INSERT_TAIL(&rp->ap_references, pr, ar_rlink); 51579357Smsmith 51678915Smsmith return_VOID; 51778915Smsmith} 51878915Smsmith 51978915Smsmith 52078915Smsmith/* 52178915Smsmith * Switch power resources to conform to the desired state. 52278915Smsmith * 52378915Smsmith * Consumers may have modified the power resource list in an arbitrary 52478915Smsmith * fashion; we sweep it in sequence order. 52578915Smsmith */ 52678915Smsmithstatic ACPI_STATUS 52778915Smsmithacpi_pwr_switch_power(void) 52878915Smsmith{ 52978915Smsmith struct acpi_powerresource *rp; 53078915Smsmith ACPI_STATUS status; 53178915Smsmith int cur; 53278915Smsmith 53378915Smsmith FUNCTION_TRACE(__func__); 53478915Smsmith 53578915Smsmith /* 53678915Smsmith * Sweep the list forwards turning things on. 53778915Smsmith */ 53878915Smsmith TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) { 53979357Smsmith if (TAILQ_FIRST(&rp->ap_references) == NULL) { 54082372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s has no references, not turning on\n", 54182372Smsmith acpi_name(rp->ap_resource))); 54279357Smsmith continue; 54379357Smsmith } 54478915Smsmith 54578915Smsmith /* we could cache this if we trusted it not to change under us */ 54678915Smsmith if ((status = acpi_EvaluateInteger(rp->ap_resource, "_STA", &cur)) != AE_OK) { 54782372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n", 54882372Smsmith acpi_name(rp->ap_resource), status)); 54978915Smsmith continue; /* XXX is this correct? Always switch if in doubt? */ 55078915Smsmith } 55178915Smsmith 55278915Smsmith /* 55378915Smsmith * Switch if required. Note that we ignore the result of the switch 55478915Smsmith * effort; we don't know what to do if it fails, so checking wouldn't 55578915Smsmith * help much. 55678915Smsmith */ 55779357Smsmith if (cur != ACPI_PWR_ON) { 55879357Smsmith if (ACPI_FAILURE(status = AcpiEvaluateObject(rp->ap_resource, "_ON", NULL, NULL))) { 55982372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to switch %s on - %s\n", 56082372Smsmith acpi_name(rp->ap_resource), AcpiFormatException(status))); 56179357Smsmith } else { 56282372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s on\n", acpi_name(rp->ap_resource))); 56379357Smsmith } 56479357Smsmith } else { 56582372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already on\n", acpi_name(rp->ap_resource))); 56679357Smsmith } 56778915Smsmith } 56878915Smsmith 56978915Smsmith /* 57078915Smsmith * Sweep the list backwards turning things off. 57178915Smsmith */ 57278915Smsmith TAILQ_FOREACH_REVERSE(rp, &acpi_powerresources, acpi_powerresource_list, ap_link) { 57379357Smsmith if (TAILQ_FIRST(&rp->ap_references) != NULL) { 57482372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s has references, not turning off\n", 57582372Smsmith acpi_name(rp->ap_resource))); 57679357Smsmith continue; 57779357Smsmith } 57878915Smsmith 57978915Smsmith /* we could cache this if we trusted it not to change under us */ 58078915Smsmith if ((status = acpi_EvaluateInteger(rp->ap_resource, "_STA", &cur)) != AE_OK) { 58182372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n", 58282372Smsmith acpi_name(rp->ap_resource), status)); 58378915Smsmith continue; /* XXX is this correct? Always switch if in doubt? */ 58478915Smsmith } 58578915Smsmith 58678915Smsmith /* 58778915Smsmith * Switch if required. Note that we ignore the result of the switch 58878915Smsmith * effort; we don't know what to do if it fails, so checking wouldn't 58978915Smsmith * help much. 59078915Smsmith */ 59179357Smsmith if (cur != ACPI_PWR_OFF) { 59279357Smsmith if (ACPI_FAILURE(status = AcpiEvaluateObject(rp->ap_resource, "_OFF", NULL, NULL))) { 59382372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to switch %s off - %s\n", 59482372Smsmith acpi_name(rp->ap_resource), AcpiFormatException(status))); 59579357Smsmith } else { 59682372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s off\n", acpi_name(rp->ap_resource))); 59779357Smsmith } 59879357Smsmith } else { 59982372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already off\n", acpi_name(rp->ap_resource))); 60079357Smsmith } 60178915Smsmith } 60278915Smsmith return_ACPI_STATUS(AE_OK); 60378915Smsmith} 60478915Smsmith 60578915Smsmith/* 60678915Smsmith * Find a power resource's control structure. 60778915Smsmith */ 60878915Smsmithstatic struct acpi_powerresource * 60978915Smsmithacpi_pwr_find_resource(ACPI_HANDLE res) 61078915Smsmith{ 61178915Smsmith struct acpi_powerresource *rp; 61278915Smsmith 61378915Smsmith FUNCTION_TRACE(__func__); 61478915Smsmith 61578915Smsmith TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) 61678915Smsmith if (rp->ap_resource == res) 61778915Smsmith break; 61878915Smsmith return_VALUE(rp); 61978915Smsmith} 62078915Smsmith 62178915Smsmith/* 62278915Smsmith * Find a power consumer's control structure. 62378915Smsmith */ 62478915Smsmithstatic struct acpi_powerconsumer * 62578915Smsmithacpi_pwr_find_consumer(ACPI_HANDLE consumer) 62678915Smsmith{ 62778915Smsmith struct acpi_powerconsumer *pc; 62878915Smsmith 62978915Smsmith FUNCTION_TRACE(__func__); 63078915Smsmith 63178915Smsmith TAILQ_FOREACH(pc, &acpi_powerconsumers, ac_link) 63278915Smsmith if (pc->ac_consumer == consumer) 63378915Smsmith break; 63478915Smsmith return_VALUE(pc); 63578915Smsmith} 63678915Smsmith 637