acpi_powerres.c revision 91125
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 91125 2002-02-23 05:28:22Z 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 7891125SmsmithACPI_MODULE_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 11589054Smsmithstatic TAILQ_HEAD(acpi_powerresource_list, acpi_powerresource) acpi_powerresources; 11689054Smsmithstatic TAILQ_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 15191125Smsmith ACPI_FUNCTION_TRACE(__func__); 15278915Smsmith 15378915Smsmith rp = NULL; 15491125Smsmith buf.Pointer = 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 */ 16991125Smsmith buf.Length = ACPI_ALLOCATE_BUFFER; 17091125Smsmith if (ACPI_FAILURE(status = AcpiEvaluateObject(res, NULL, NULL, &buf))) { 17182372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "no power resource object\n")); 17278915Smsmith goto out; 17378915Smsmith } 17478915Smsmith obj = buf.Pointer; 17579493Smsmith if (obj->Type != ACPI_TYPE_POWER) { 17682372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "questionable power resource object %s\n", acpi_name(res))); 17779493Smsmith status = AE_TYPE; 17879493Smsmith goto out; 17978915Smsmith } 18079493Smsmith rp->ap_systemlevel = obj->PowerResource.SystemLevel; 18179493Smsmith rp->ap_order = obj->PowerResource.ResourceOrder; 18278915Smsmith 18378915Smsmith /* sort the resource into the list */ 18478915Smsmith status = AE_OK; 18578915Smsmith srp = TAILQ_FIRST(&acpi_powerresources); 18678915Smsmith if ((srp == NULL) || (rp->ap_order < srp->ap_order)) { 18778915Smsmith TAILQ_INSERT_HEAD(&acpi_powerresources, rp, ap_link); 18879357Smsmith goto done; 18978915Smsmith } 19078915Smsmith TAILQ_FOREACH(srp, &acpi_powerresources, ap_link) 19178915Smsmith if (rp->ap_order < srp->ap_order) { 19278915Smsmith TAILQ_INSERT_BEFORE(srp, rp, ap_link); 19379357Smsmith goto done; 19478915Smsmith } 19578915Smsmith TAILQ_INSERT_TAIL(&acpi_powerresources, rp, ap_link); 19679357Smsmith 19779357Smsmith done: 19882372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "registered power resource %s\n", acpi_name(res))); 19978915Smsmith out: 20091125Smsmith if (buf.Pointer != NULL) 20191125Smsmith AcpiOsFree(buf.Pointer); 20291125Smsmith if (ACPI_FAILURE(status) && (rp != NULL)) 20378915Smsmith free(rp, M_ACPIPWR); 20478915Smsmith return_ACPI_STATUS(status); 20578915Smsmith} 20678915Smsmith 20778915Smsmith/* 20878915Smsmith * Deregister a power resource. 20978915Smsmith */ 21078915Smsmithstatic ACPI_STATUS 21178915Smsmithacpi_pwr_deregister_resource(ACPI_HANDLE res) 21278915Smsmith{ 21378915Smsmith struct acpi_powerresource *rp; 21478915Smsmith 21591125Smsmith ACPI_FUNCTION_TRACE(__func__); 21678915Smsmith 21778915Smsmith rp = NULL; 21878915Smsmith 21978915Smsmith /* find the resource */ 22078915Smsmith if ((rp = acpi_pwr_find_resource(res)) == NULL) 22178915Smsmith return_ACPI_STATUS(AE_BAD_PARAMETER); 22278915Smsmith 22378915Smsmith /* check that there are no consumers referencing this resource */ 22478915Smsmith if (TAILQ_FIRST(&rp->ap_references) != NULL) 22578915Smsmith return_ACPI_STATUS(AE_BAD_PARAMETER); 22678915Smsmith 22778915Smsmith /* pull it off the list and free it */ 22878915Smsmith TAILQ_REMOVE(&acpi_powerresources, rp, ap_link); 22978915Smsmith free(rp, M_ACPIPWR); 23078915Smsmith 23182372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power resource %s\n", acpi_name(res))); 23278915Smsmith 23378915Smsmith return_ACPI_STATUS(AE_OK); 23478915Smsmith} 23578915Smsmith 23678915Smsmith/* 23778915Smsmith * Register a power consumer. 23878915Smsmith * 23978915Smsmith * It's OK to call this if we already know about the consumer. 24078915Smsmith */ 24178915Smsmithstatic ACPI_STATUS 24278915Smsmithacpi_pwr_register_consumer(ACPI_HANDLE consumer) 24378915Smsmith{ 24478915Smsmith struct acpi_powerconsumer *pc; 24578915Smsmith 24691125Smsmith ACPI_FUNCTION_TRACE(__func__); 24778915Smsmith 24878915Smsmith /* check to see whether we know about this consumer already */ 24978915Smsmith if ((pc = acpi_pwr_find_consumer(consumer)) != NULL) 25078915Smsmith return_ACPI_STATUS(AE_OK); 25178915Smsmith 25278915Smsmith /* allocate a new power consumer */ 25378915Smsmith if ((pc = malloc(sizeof(*pc), M_ACPIPWR, M_NOWAIT)) == NULL) 25478915Smsmith 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 25978915Smsmith pc->ac_state = ACPI_STATE_UNKNOWN; /* XXX we should try to find its current state */ 26078915Smsmith 26182372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "registered power consumer %s\n", acpi_name(consumer))); 26278915Smsmith 26378915Smsmith return_ACPI_STATUS(AE_OK); 26478915Smsmith} 26578915Smsmith 26678915Smsmith/* 26778915Smsmith * Deregister a power consumer. 26878915Smsmith * 26978915Smsmith * This should only be done once the consumer has been powered off. 27078915Smsmith * (XXX is this correct? Check once implemented) 27178915Smsmith */ 27278915Smsmithstatic ACPI_STATUS 27378915Smsmithacpi_pwr_deregister_consumer(ACPI_HANDLE consumer) 27478915Smsmith{ 27578915Smsmith struct acpi_powerconsumer *pc; 27678915Smsmith 27791125Smsmith ACPI_FUNCTION_TRACE(__func__); 27878915Smsmith 27978915Smsmith /* find the consumer */ 28078915Smsmith if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) 28178915Smsmith return_ACPI_STATUS(AE_BAD_PARAMETER); 28278915Smsmith 28378915Smsmith /* make sure the consumer's not referencing anything right now */ 28478915Smsmith if (TAILQ_FIRST(&pc->ac_references) != NULL) 28578915Smsmith return_ACPI_STATUS(AE_BAD_PARAMETER); 28678915Smsmith 28778915Smsmith /* pull the consumer off the list and free it */ 28878915Smsmith TAILQ_REMOVE(&acpi_powerconsumers, pc, ac_link); 28978915Smsmith 29082372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power consumer %s\n", acpi_name(consumer))); 29178915Smsmith 29278915Smsmith return_ACPI_STATUS(AE_OK); 29378915Smsmith} 29478915Smsmith 29578915Smsmith/* 29678915Smsmith * Set a power consumer to a particular power state. 29778915Smsmith */ 29878915SmsmithACPI_STATUS 29978915Smsmithacpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state) 30078915Smsmith{ 30178915Smsmith struct acpi_powerconsumer *pc; 30278915Smsmith struct acpi_powerreference *pr; 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 31091125Smsmith ACPI_FUNCTION_TRACE(__func__); 31178915Smsmith 31278915Smsmith /* find the consumer */ 31378915Smsmith if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) { 31491125Smsmith if (ACPI_FAILURE(status = acpi_pwr_register_consumer(consumer))) 31578915Smsmith return_ACPI_STATUS(status); 31678915Smsmith if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) { 31778915Smsmith return_ACPI_STATUS(AE_ERROR); /* something very wrong */ 31878915Smsmith } 31978915Smsmith } 32078915Smsmith 32178915Smsmith /* check for valid transitions */ 32278915Smsmith if ((pc->ac_state == ACPI_STATE_D3) && (state != ACPI_STATE_D0)) 32378915Smsmith return_ACPI_STATUS(AE_BAD_PARAMETER); /* can only go to D0 from D3 */ 32478915Smsmith 32578915Smsmith /* find transition mechanism(s) */ 32678915Smsmith switch(state) { 32778915Smsmith case ACPI_STATE_D0: 32878915Smsmith method_name = "_PS0"; 32978915Smsmith reslist_name = "_PR0"; 33078915Smsmith break; 33178915Smsmith case ACPI_STATE_D1: 33278915Smsmith method_name = "_PS1"; 33378915Smsmith reslist_name = "_PR1"; 33478915Smsmith break; 33578915Smsmith case ACPI_STATE_D2: 33678915Smsmith method_name = "_PS2"; 33778915Smsmith reslist_name = "_PR2"; 33878915Smsmith break; 33978915Smsmith case ACPI_STATE_D3: 34078915Smsmith method_name = "_PS3"; 34178915Smsmith reslist_name = "_PR3"; 34278915Smsmith break; 34378915Smsmith default: 34478915Smsmith return_ACPI_STATUS(AE_BAD_PARAMETER); 34578915Smsmith } 34682372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "setup to switch %s D%d -> D%d\n", 34782372Smsmith acpi_name(consumer), pc->ac_state, state)); 34878915Smsmith 34978915Smsmith /* 35078915Smsmith * Verify that this state is supported, ie. one of method or 35178915Smsmith * reslist must be present. We need to do this before we go 35278915Smsmith * dereferencing resources (since we might be trying to go to 35378915Smsmith * a state we don't support). 35478915Smsmith * 35578915Smsmith * Note that if any states are supported, the device has to 35678915Smsmith * support D0 and D3. It's never an error to try to go to 35778915Smsmith * D0. 35878915Smsmith */ 35991125Smsmith reslist_buffer.Pointer = NULL; 36082084Siwasaki reslist_object = NULL; 36191125Smsmith if (ACPI_FAILURE(AcpiGetHandle(consumer, method_name, &method_handle))) 36278915Smsmith method_handle = NULL; 36391125Smsmith if (ACPI_FAILURE(AcpiGetHandle(consumer, reslist_name, &reslist_handle))) 36478915Smsmith reslist_handle = NULL; 36578915Smsmith if ((reslist_handle == NULL) && (method_handle == NULL)) { 36678915Smsmith if (state == ACPI_STATE_D0) { 36778915Smsmith pc->ac_state = ACPI_STATE_D0; 36878915Smsmith return_ACPI_STATUS(AE_OK); 36978915Smsmith } 37082084Siwasaki if (state != ACPI_STATE_D3) { 37182084Siwasaki goto bad; 37282084Siwasaki } 37382084Siwasaki 37482084Siwasaki /* turn off the resources listed in _PR0 to go to D3. */ 37591125Smsmith if (ACPI_FAILURE(AcpiGetHandle(consumer, "_PR0", &pr0_handle))) { 37682084Siwasaki goto bad; 37782084Siwasaki } 37891125Smsmith reslist_buffer.Length = ACPI_ALLOCATE_BUFFER; 37991125Smsmith if (ACPI_FAILURE(status = AcpiEvaluateObject(pr0_handle, NULL, NULL, &reslist_buffer))) { 38082084Siwasaki goto bad; 38182084Siwasaki } 38282084Siwasaki reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer; 38382084Siwasaki if ((reslist_object->Type != ACPI_TYPE_PACKAGE) || 38482084Siwasaki (reslist_object->Package.Count == 0)) { 38582084Siwasaki goto bad; 38682084Siwasaki } 38791125Smsmith AcpiOsFree(reslist_buffer.Pointer); 38891125Smsmith reslist_buffer.Pointer = NULL; 38991125Smsmith reslist_object = NULL; 39078915Smsmith } 39178915Smsmith 39278915Smsmith /* 39378915Smsmith * Check that we can actually fetch the list of power resources 39478915Smsmith */ 39578915Smsmith if (reslist_handle != NULL) { 39691125Smsmith reslist_buffer.Length = ACPI_ALLOCATE_BUFFER; 39791125Smsmith if (ACPI_FAILURE(status = AcpiEvaluateObject(reslist_handle, NULL, NULL, &reslist_buffer))) { 39882372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't evaluate resource list %s\n", 39982372Smsmith acpi_name(reslist_handle))); 40091125Smsmith goto out; 40178915Smsmith } 40278915Smsmith reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer; 40378915Smsmith if (reslist_object->Type != ACPI_TYPE_PACKAGE) { 40482372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "resource list is not ACPI_TYPE_PACKAGE (%d)\n", 40582372Smsmith reslist_object->Type)); 40691125Smsmith status = AE_TYPE; 40791125Smsmith goto out; 40878915Smsmith } 40978915Smsmith } 41078915Smsmith 41178915Smsmith /* 41278915Smsmith * Now we are ready to switch, so kill off any current power resource references. 41378915Smsmith */ 41478915Smsmith res_changed = 0; 41579357Smsmith while((pr = TAILQ_FIRST(&pc->ac_references)) != NULL) { 41679357Smsmith res_changed = 1; 41782372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "removing reference to %s\n", acpi_name(pr->ar_resource->ap_resource))); 41878915Smsmith TAILQ_REMOVE(&pr->ar_resource->ap_references, pr, ar_rlink); 41979357Smsmith TAILQ_REMOVE(&pc->ac_references, pr, ar_clink); 42079357Smsmith free(pr, M_ACPIPWR); 42178915Smsmith } 42278915Smsmith 42378915Smsmith /* 42478915Smsmith * Add new power resource references, if we have any. Traverse the 42578915Smsmith * package that we got from evaluating reslist_handle, and look up each 42678915Smsmith * of the resources that are referenced. 42778915Smsmith */ 42878915Smsmith if (reslist_object != NULL) { 42982372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "referencing %d new resources\n", 43082372Smsmith reslist_object->Package.Count)); 43178915Smsmith acpi_ForeachPackageObject(reslist_object, acpi_pwr_reference_resource, pc); 43278915Smsmith res_changed = 1; 43378915Smsmith } 43478915Smsmith 43578915Smsmith /* 43678915Smsmith * If we changed anything in the resource list, we need to run a switch 43778915Smsmith * pass now. 43878915Smsmith */ 43991125Smsmith if (ACPI_FAILURE(status = acpi_pwr_switch_power())) { 44082372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to correctly switch resources to move %s to D%d\n", 44182372Smsmith acpi_name(consumer), state)); 44291125Smsmith goto out; /* XXX is this appropriate? Should we return to previous state? */ 44378915Smsmith } 44478915Smsmith 44578915Smsmith /* invoke power state switch method (if present) */ 44678915Smsmith if (method_handle != NULL) { 44782372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "invoking state transition method %s\n", 44882372Smsmith acpi_name(method_handle))); 44991125Smsmith if (ACPI_FAILURE(status = AcpiEvaluateObject(method_handle, NULL, NULL, NULL))) { 45091125Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to set state - %s\n", 45191125Smsmith AcpiFormatException(status))); 45291125Smsmith pc->ac_state = ACPI_STATE_UNKNOWN; 45391125Smsmith goto out; /* XXX Should we return to previous state? */ 45491125Smsmith } 45578915Smsmith } 45691125Smsmith 45778915Smsmith /* transition was successful */ 45878915Smsmith pc->ac_state = state; 45978915Smsmith return_ACPI_STATUS(AE_OK); 46082084Siwasaki 46182084Siwasaki bad: 46282372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "attempt to set unsupported state D%d\n", 46382372Smsmith state)); 46491125Smsmith status = AE_BAD_PARAMETER; 46591125Smsmith 46691125Smsmith out: 46791125Smsmith if (reslist_buffer.Pointer != NULL) 46891125Smsmith AcpiOsFree(reslist_buffer.Pointer); 46991125Smsmith return_ACPI_STATUS(status); 47078915Smsmith} 47178915Smsmith 47278915Smsmith/* 47378915Smsmith * Called to create a reference between a power consumer and a power resource 47478915Smsmith * identified in the object. 47578915Smsmith */ 47678915Smsmithstatic void 47778915Smsmithacpi_pwr_reference_resource(ACPI_OBJECT *obj, void *arg) 47878915Smsmith{ 47978915Smsmith struct acpi_powerconsumer *pc = (struct acpi_powerconsumer *)arg; 48079357Smsmith struct acpi_powerreference *pr; 48179357Smsmith struct acpi_powerresource *rp; 48279357Smsmith ACPI_HANDLE res; 48379357Smsmith ACPI_STATUS status; 48478915Smsmith 48591125Smsmith ACPI_FUNCTION_TRACE(__func__); 48678915Smsmith 48779357Smsmith /* check the object type */ 48879357Smsmith if (obj->Type != ACPI_TYPE_STRING) { 48982372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "don't know how to create a power reference to object type %d\n", 49082372Smsmith obj->Type)); 49179357Smsmith return_VOID; 49279357Smsmith } 49378915Smsmith 49482372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "building reference from %s to %s\n", 49582372Smsmith acpi_name(pc->ac_consumer), obj->String.Pointer)); 49679357Smsmith 49779357Smsmith /* get the handle of the resource */ 49879357Smsmith if (ACPI_FAILURE(status = AcpiGetHandle(NULL, obj->String.Pointer, &res))) { 49982372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "couldn't find power resource %s\n", 50082372Smsmith obj->String.Pointer)); 50179357Smsmith return_VOID; 50279357Smsmith } 50379357Smsmith 50479357Smsmith /* create/look up the resource */ 50579357Smsmith if (ACPI_FAILURE(status = acpi_pwr_register_resource(res))) { 50682372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "couldn't register power resource %s - %s\n", 50782372Smsmith obj->String.Pointer, AcpiFormatException(status))); 50879357Smsmith return_VOID; 50979357Smsmith } 51079357Smsmith if ((rp = acpi_pwr_find_resource(res)) == NULL) { 51182372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "power resource list corrupted\n")); 51279357Smsmith return_VOID; 51379357Smsmith } 51482372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "found power resource %s\n", acpi_name(rp->ap_resource))); 51579357Smsmith 51679357Smsmith /* create a reference between the consumer and resource */ 51779357Smsmith if ((pr = malloc(sizeof(*pr), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) { 51882372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "couldn't allocate memory for a power consumer reference\n")); 51979357Smsmith return_VOID; 52079357Smsmith } 52179357Smsmith pr->ar_consumer = pc; 52279357Smsmith pr->ar_resource = rp; 52379357Smsmith TAILQ_INSERT_TAIL(&pc->ac_references, pr, ar_clink); 52479357Smsmith TAILQ_INSERT_TAIL(&rp->ap_references, pr, ar_rlink); 52579357Smsmith 52678915Smsmith return_VOID; 52778915Smsmith} 52878915Smsmith 52978915Smsmith 53078915Smsmith/* 53178915Smsmith * Switch power resources to conform to the desired state. 53278915Smsmith * 53378915Smsmith * Consumers may have modified the power resource list in an arbitrary 53478915Smsmith * fashion; we sweep it in sequence order. 53578915Smsmith */ 53678915Smsmithstatic ACPI_STATUS 53778915Smsmithacpi_pwr_switch_power(void) 53878915Smsmith{ 53978915Smsmith struct acpi_powerresource *rp; 54078915Smsmith ACPI_STATUS status; 54178915Smsmith int cur; 54278915Smsmith 54391125Smsmith ACPI_FUNCTION_TRACE(__func__); 54478915Smsmith 54578915Smsmith /* 54678915Smsmith * Sweep the list forwards turning things on. 54778915Smsmith */ 54878915Smsmith TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) { 54979357Smsmith if (TAILQ_FIRST(&rp->ap_references) == NULL) { 55082372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s has no references, not turning on\n", 55182372Smsmith acpi_name(rp->ap_resource))); 55279357Smsmith continue; 55379357Smsmith } 55478915Smsmith 55578915Smsmith /* we could cache this if we trusted it not to change under us */ 55691125Smsmith if (ACPI_FAILURE(status = acpi_EvaluateInteger(rp->ap_resource, "_STA", &cur))) { 55782372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n", 55882372Smsmith acpi_name(rp->ap_resource), status)); 55978915Smsmith continue; /* XXX is this correct? Always switch if in doubt? */ 56078915Smsmith } 56178915Smsmith 56278915Smsmith /* 56378915Smsmith * Switch if required. Note that we ignore the result of the switch 56478915Smsmith * effort; we don't know what to do if it fails, so checking wouldn't 56578915Smsmith * help much. 56678915Smsmith */ 56779357Smsmith if (cur != ACPI_PWR_ON) { 56879357Smsmith if (ACPI_FAILURE(status = AcpiEvaluateObject(rp->ap_resource, "_ON", NULL, NULL))) { 56982372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to switch %s on - %s\n", 57082372Smsmith acpi_name(rp->ap_resource), AcpiFormatException(status))); 57179357Smsmith } else { 57282372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s on\n", acpi_name(rp->ap_resource))); 57379357Smsmith } 57479357Smsmith } else { 57582372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already on\n", acpi_name(rp->ap_resource))); 57679357Smsmith } 57778915Smsmith } 57878915Smsmith 57978915Smsmith /* 58078915Smsmith * Sweep the list backwards turning things off. 58178915Smsmith */ 58278915Smsmith TAILQ_FOREACH_REVERSE(rp, &acpi_powerresources, acpi_powerresource_list, ap_link) { 58379357Smsmith if (TAILQ_FIRST(&rp->ap_references) != NULL) { 58482372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s has references, not turning off\n", 58582372Smsmith acpi_name(rp->ap_resource))); 58679357Smsmith continue; 58779357Smsmith } 58878915Smsmith 58978915Smsmith /* we could cache this if we trusted it not to change under us */ 59091125Smsmith if (ACPI_FAILURE(status = acpi_EvaluateInteger(rp->ap_resource, "_STA", &cur))) { 59182372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n", 59282372Smsmith acpi_name(rp->ap_resource), status)); 59378915Smsmith continue; /* XXX is this correct? Always switch if in doubt? */ 59478915Smsmith } 59578915Smsmith 59678915Smsmith /* 59778915Smsmith * Switch if required. Note that we ignore the result of the switch 59878915Smsmith * effort; we don't know what to do if it fails, so checking wouldn't 59978915Smsmith * help much. 60078915Smsmith */ 60179357Smsmith if (cur != ACPI_PWR_OFF) { 60279357Smsmith if (ACPI_FAILURE(status = AcpiEvaluateObject(rp->ap_resource, "_OFF", NULL, NULL))) { 60382372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to switch %s off - %s\n", 60482372Smsmith acpi_name(rp->ap_resource), AcpiFormatException(status))); 60579357Smsmith } else { 60682372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s off\n", acpi_name(rp->ap_resource))); 60779357Smsmith } 60879357Smsmith } else { 60982372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already off\n", acpi_name(rp->ap_resource))); 61079357Smsmith } 61178915Smsmith } 61278915Smsmith return_ACPI_STATUS(AE_OK); 61378915Smsmith} 61478915Smsmith 61578915Smsmith/* 61678915Smsmith * Find a power resource's control structure. 61778915Smsmith */ 61878915Smsmithstatic struct acpi_powerresource * 61978915Smsmithacpi_pwr_find_resource(ACPI_HANDLE res) 62078915Smsmith{ 62178915Smsmith struct acpi_powerresource *rp; 62278915Smsmith 62391125Smsmith ACPI_FUNCTION_TRACE(__func__); 62478915Smsmith 62578915Smsmith TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) 62678915Smsmith if (rp->ap_resource == res) 62778915Smsmith break; 62884445Sdfr return_PTR(rp); 62978915Smsmith} 63078915Smsmith 63178915Smsmith/* 63278915Smsmith * Find a power consumer's control structure. 63378915Smsmith */ 63478915Smsmithstatic struct acpi_powerconsumer * 63578915Smsmithacpi_pwr_find_consumer(ACPI_HANDLE consumer) 63678915Smsmith{ 63778915Smsmith struct acpi_powerconsumer *pc; 63878915Smsmith 63991125Smsmith ACPI_FUNCTION_TRACE(__func__); 64078915Smsmith 64178915Smsmith TAILQ_FOREACH(pc, &acpi_powerconsumers, ac_link) 64278915Smsmith if (pc->ac_consumer == consumer) 64378915Smsmith break; 64484445Sdfr return_PTR(pc); 64578915Smsmith} 64678915Smsmith 647