acpi_powerres.c revision 119418
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 119418 2003-08-24 17:55:58Z obrien $"); 29119418Sobrien 3078915Smsmith#include "opt_acpi.h" /* XXX trim includes */ 3178915Smsmith#include <sys/param.h> 3278915Smsmith#include <sys/kernel.h> 3378915Smsmith#include <sys/proc.h> 3478915Smsmith#include <sys/malloc.h> 3578915Smsmith#include <sys/bus.h> 3678915Smsmith#include <sys/conf.h> 3778915Smsmith#include <sys/ioccom.h> 3878915Smsmith#include <sys/reboot.h> 3978915Smsmith#include <sys/sysctl.h> 4078915Smsmith#include <sys/systm.h> 4178915Smsmith#include <sys/ctype.h> 4278915Smsmith 4378915Smsmith#include <machine/clock.h> 4478915Smsmith 4578915Smsmith#include <machine/resource.h> 4678915Smsmith 4778915Smsmith#include "acpi.h" 4878915Smsmith 4978915Smsmith#include <dev/acpica/acpivar.h> 5078915Smsmith#include <dev/acpica/acpiio.h> 5178915Smsmith 5278915Smsmith/* 5378915Smsmith * ACPI power resource management. 5478915Smsmith * 5578915Smsmith * Power resource behaviour is slightly complicated by the fact that 5678915Smsmith * a single power resource may provide power for more than one device. 5778915Smsmith * Thus, we must track the device(s) being powered by a given power 5878915Smsmith * resource, and only deactivate it when there are no powered devices. 5978915Smsmith * 6078915Smsmith * Note that this only manages resources for known devices. There is an 6178915Smsmith * ugly case where we may turn of power to a device which is in use because 6278915Smsmith * we don't know that it depends on a given resource. We should perhaps 6378915Smsmith * try to be smarter about this, but a more complete solution would involve 6478915Smsmith * scanning all of the ACPI namespace to find devices we're not currently 6578915Smsmith * aware of, and this raises questions about whether they should be left 6678915Smsmith * on, turned off, etc. 6778915Smsmith * 6878915Smsmith * XXX locking 6978915Smsmith */ 7078915Smsmith 7178915SmsmithMALLOC_DEFINE(M_ACPIPWR, "acpipwr", "ACPI power resources"); 7278915Smsmith 7378915Smsmith/* 7478915Smsmith * Hooks for the ACPI CA debugging infrastructure 7578915Smsmith */ 7678993Smsmith#define _COMPONENT ACPI_POWER 7791125SmsmithACPI_MODULE_NAME("POWERRES") 7878915Smsmith 7979357Smsmith/* return values from _STA on a power resource */ 8079357Smsmith#define ACPI_PWR_OFF 0 8179357Smsmith#define ACPI_PWR_ON 1 8279357Smsmith 8378915Smsmith/* 8478915Smsmith * A relationship between a power resource and a consumer. 8578915Smsmith */ 8678915Smsmithstruct acpi_powerreference { 8778915Smsmith struct acpi_powerconsumer *ar_consumer; 8878915Smsmith struct acpi_powerresource *ar_resource; 8978915Smsmith TAILQ_ENTRY(acpi_powerreference) ar_rlink; /* link on resource list */ 9078915Smsmith TAILQ_ENTRY(acpi_powerreference) ar_clink; /* link on consumer */ 9178915Smsmith}; 9278915Smsmith 9378915Smsmith/* 9478915Smsmith * A power-managed device. 9578915Smsmith */ 9678915Smsmithstruct acpi_powerconsumer { 9778915Smsmith ACPI_HANDLE ac_consumer; /* device which is powered */ 9878915Smsmith int ac_state; 9978915Smsmith TAILQ_ENTRY(acpi_powerconsumer) ac_link; 10078915Smsmith TAILQ_HEAD(,acpi_powerreference) ac_references; 10178915Smsmith}; 10278915Smsmith 10378915Smsmith/* 10478915Smsmith * A power resource. 10578915Smsmith */ 10678915Smsmithstruct acpi_powerresource { 10778915Smsmith TAILQ_ENTRY(acpi_powerresource) ap_link; 10878915Smsmith TAILQ_HEAD(,acpi_powerreference) ap_references; 10978915Smsmith ACPI_HANDLE ap_resource; /* the resource's handle */ 11078915Smsmith ACPI_INTEGER ap_systemlevel; 11178915Smsmith ACPI_INTEGER ap_order; 11278915Smsmith}; 11378915Smsmith 11489054Smsmithstatic TAILQ_HEAD(acpi_powerresource_list, acpi_powerresource) acpi_powerresources; 11589054Smsmithstatic TAILQ_HEAD(acpi_powerconsumer_list, acpi_powerconsumer) acpi_powerconsumers; 11678915Smsmith 11778915Smsmithstatic ACPI_STATUS acpi_pwr_register_consumer(ACPI_HANDLE consumer); 11878915Smsmithstatic ACPI_STATUS acpi_pwr_deregister_consumer(ACPI_HANDLE consumer); 11978915Smsmithstatic ACPI_STATUS acpi_pwr_register_resource(ACPI_HANDLE res); 12078915Smsmithstatic ACPI_STATUS acpi_pwr_deregister_resource(ACPI_HANDLE res); 12178915Smsmithstatic void acpi_pwr_reference_resource(ACPI_OBJECT *obj, void *arg); 12278915Smsmithstatic ACPI_STATUS acpi_pwr_switch_power(void); 12378915Smsmithstatic struct acpi_powerresource *acpi_pwr_find_resource(ACPI_HANDLE res); 12478915Smsmithstatic struct acpi_powerconsumer *acpi_pwr_find_consumer(ACPI_HANDLE consumer); 12578915Smsmith 12678915Smsmith/* 12778915Smsmith * Initialise our lists. 12878915Smsmith */ 12978915Smsmithstatic void 13078915Smsmithacpi_pwr_init(void *junk) 13178915Smsmith{ 13278915Smsmith TAILQ_INIT(&acpi_powerresources); 13378915Smsmith TAILQ_INIT(&acpi_powerconsumers); 13478915Smsmith} 13578915SmsmithSYSINIT(acpi_powerresource, SI_SUB_TUNABLES, SI_ORDER_ANY, acpi_pwr_init, NULL); 13678915Smsmith 13778915Smsmith/* 13878915Smsmith * Register a power resource. 13978915Smsmith * 14078915Smsmith * It's OK to call this if we already know about the resource. 14178915Smsmith */ 14278915Smsmithstatic ACPI_STATUS 14378915Smsmithacpi_pwr_register_resource(ACPI_HANDLE res) 14478915Smsmith{ 14578915Smsmith ACPI_STATUS status; 14678915Smsmith ACPI_BUFFER buf; 14779493Smsmith ACPI_OBJECT *obj; 14878915Smsmith struct acpi_powerresource *rp, *srp; 14978915Smsmith 15096926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 15178915Smsmith 15278915Smsmith rp = NULL; 15391125Smsmith buf.Pointer = NULL; 15478915Smsmith 15578915Smsmith /* look to see if we know about this resource */ 15678915Smsmith if (acpi_pwr_find_resource(res) != NULL) 15778915Smsmith return_ACPI_STATUS(AE_OK); /* already know about it */ 15878915Smsmith 15978915Smsmith /* allocate a new resource */ 16078915Smsmith if ((rp = malloc(sizeof(*rp), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) { 16178915Smsmith status = AE_NO_MEMORY; 16278915Smsmith goto out; 16378915Smsmith } 16478915Smsmith TAILQ_INIT(&rp->ap_references); 16578915Smsmith rp->ap_resource = res; 16678915Smsmith 16778915Smsmith /* get the Power Resource object */ 16891125Smsmith buf.Length = ACPI_ALLOCATE_BUFFER; 16991125Smsmith if (ACPI_FAILURE(status = AcpiEvaluateObject(res, NULL, NULL, &buf))) { 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: 19991125Smsmith if (buf.Pointer != NULL) 20091125Smsmith AcpiOsFree(buf.Pointer); 20191125Smsmith if (ACPI_FAILURE(status) && (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 21496926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__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 24596926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__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 27696926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__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 30996926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 31078915Smsmith 31178915Smsmith /* find the consumer */ 31278915Smsmith if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) { 31391125Smsmith if (ACPI_FAILURE(status = acpi_pwr_register_consumer(consumer))) 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 */ 35891125Smsmith reslist_buffer.Pointer = NULL; 35982084Siwasaki reslist_object = NULL; 36091125Smsmith if (ACPI_FAILURE(AcpiGetHandle(consumer, method_name, &method_handle))) 36178915Smsmith method_handle = NULL; 36291125Smsmith if (ACPI_FAILURE(AcpiGetHandle(consumer, reslist_name, &reslist_handle))) 36378915Smsmith reslist_handle = NULL; 36478915Smsmith if ((reslist_handle == NULL) && (method_handle == NULL)) { 36578915Smsmith if (state == ACPI_STATE_D0) { 36678915Smsmith pc->ac_state = ACPI_STATE_D0; 36778915Smsmith return_ACPI_STATUS(AE_OK); 36878915Smsmith } 36982084Siwasaki if (state != ACPI_STATE_D3) { 37082084Siwasaki goto bad; 37182084Siwasaki } 37282084Siwasaki 37382084Siwasaki /* turn off the resources listed in _PR0 to go to D3. */ 37491125Smsmith if (ACPI_FAILURE(AcpiGetHandle(consumer, "_PR0", &pr0_handle))) { 37582084Siwasaki goto bad; 37682084Siwasaki } 37791125Smsmith reslist_buffer.Length = ACPI_ALLOCATE_BUFFER; 37891125Smsmith if (ACPI_FAILURE(status = AcpiEvaluateObject(pr0_handle, NULL, NULL, &reslist_buffer))) { 37982084Siwasaki goto bad; 38082084Siwasaki } 38182084Siwasaki reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer; 38282084Siwasaki if ((reslist_object->Type != ACPI_TYPE_PACKAGE) || 38382084Siwasaki (reslist_object->Package.Count == 0)) { 38482084Siwasaki goto bad; 38582084Siwasaki } 38691125Smsmith AcpiOsFree(reslist_buffer.Pointer); 38791125Smsmith reslist_buffer.Pointer = NULL; 38891125Smsmith reslist_object = NULL; 38978915Smsmith } 39078915Smsmith 39178915Smsmith /* 39278915Smsmith * Check that we can actually fetch the list of power resources 39378915Smsmith */ 39478915Smsmith if (reslist_handle != NULL) { 39591125Smsmith reslist_buffer.Length = ACPI_ALLOCATE_BUFFER; 39691125Smsmith if (ACPI_FAILURE(status = AcpiEvaluateObject(reslist_handle, NULL, NULL, &reslist_buffer))) { 39782372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't evaluate resource list %s\n", 39882372Smsmith acpi_name(reslist_handle))); 39991125Smsmith goto out; 40078915Smsmith } 40178915Smsmith reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer; 40278915Smsmith if (reslist_object->Type != ACPI_TYPE_PACKAGE) { 40382372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "resource list is not ACPI_TYPE_PACKAGE (%d)\n", 40482372Smsmith reslist_object->Type)); 40591125Smsmith status = AE_TYPE; 40691125Smsmith goto out; 40778915Smsmith } 40878915Smsmith } 40978915Smsmith 41078915Smsmith /* 41178915Smsmith * Now we are ready to switch, so kill off any current power resource references. 41278915Smsmith */ 41378915Smsmith res_changed = 0; 41479357Smsmith while((pr = TAILQ_FIRST(&pc->ac_references)) != NULL) { 41579357Smsmith res_changed = 1; 41682372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "removing reference to %s\n", acpi_name(pr->ar_resource->ap_resource))); 41778915Smsmith TAILQ_REMOVE(&pr->ar_resource->ap_references, pr, ar_rlink); 41879357Smsmith TAILQ_REMOVE(&pc->ac_references, pr, ar_clink); 41979357Smsmith free(pr, M_ACPIPWR); 42078915Smsmith } 42178915Smsmith 42278915Smsmith /* 42378915Smsmith * Add new power resource references, if we have any. Traverse the 42478915Smsmith * package that we got from evaluating reslist_handle, and look up each 42578915Smsmith * of the resources that are referenced. 42678915Smsmith */ 42778915Smsmith if (reslist_object != NULL) { 42882372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "referencing %d new resources\n", 42982372Smsmith reslist_object->Package.Count)); 43078915Smsmith acpi_ForeachPackageObject(reslist_object, acpi_pwr_reference_resource, pc); 43178915Smsmith res_changed = 1; 43278915Smsmith } 43378915Smsmith 43478915Smsmith /* 43578915Smsmith * If we changed anything in the resource list, we need to run a switch 43678915Smsmith * pass now. 43778915Smsmith */ 43891125Smsmith if (ACPI_FAILURE(status = acpi_pwr_switch_power())) { 43982372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to correctly switch resources to move %s to D%d\n", 44082372Smsmith acpi_name(consumer), state)); 44191125Smsmith goto out; /* XXX is this appropriate? Should we return to previous state? */ 44278915Smsmith } 44378915Smsmith 44478915Smsmith /* invoke power state switch method (if present) */ 44578915Smsmith if (method_handle != NULL) { 44682372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "invoking state transition method %s\n", 44782372Smsmith acpi_name(method_handle))); 44891125Smsmith if (ACPI_FAILURE(status = AcpiEvaluateObject(method_handle, NULL, NULL, NULL))) { 44991125Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to set state - %s\n", 45091125Smsmith AcpiFormatException(status))); 45191125Smsmith pc->ac_state = ACPI_STATE_UNKNOWN; 45291125Smsmith goto out; /* XXX Should we return to previous state? */ 45391125Smsmith } 45478915Smsmith } 45591125Smsmith 45678915Smsmith /* transition was successful */ 45778915Smsmith pc->ac_state = state; 45878915Smsmith return_ACPI_STATUS(AE_OK); 45982084Siwasaki 46082084Siwasaki bad: 46182372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "attempt to set unsupported state D%d\n", 46282372Smsmith state)); 46391125Smsmith status = AE_BAD_PARAMETER; 46491125Smsmith 46591125Smsmith out: 46691125Smsmith if (reslist_buffer.Pointer != NULL) 46791125Smsmith AcpiOsFree(reslist_buffer.Pointer); 46891125Smsmith return_ACPI_STATUS(status); 46978915Smsmith} 47078915Smsmith 47178915Smsmith/* 47278915Smsmith * Called to create a reference between a power consumer and a power resource 47378915Smsmith * identified in the object. 47478915Smsmith */ 47578915Smsmithstatic void 47678915Smsmithacpi_pwr_reference_resource(ACPI_OBJECT *obj, void *arg) 47778915Smsmith{ 47878915Smsmith struct acpi_powerconsumer *pc = (struct acpi_powerconsumer *)arg; 47979357Smsmith struct acpi_powerreference *pr; 48079357Smsmith struct acpi_powerresource *rp; 48179357Smsmith ACPI_HANDLE res; 48279357Smsmith ACPI_STATUS status; 48378915Smsmith 48496926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 48578915Smsmith 48679357Smsmith /* check the object type */ 487102470Siwasaki switch (obj->Type) { 488102470Siwasaki case ACPI_TYPE_ANY: 489102470Siwasaki ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "building reference from %s to %s\n", 490102470Siwasaki acpi_name(pc->ac_consumer), acpi_name(obj->Reference.Handle))); 491102470Siwasaki 492102470Siwasaki res = obj->Reference.Handle; 493102470Siwasaki break; 494102470Siwasaki 495102470Siwasaki case ACPI_TYPE_STRING: 496102470Siwasaki ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "building reference from %s to %s\n", 497102470Siwasaki acpi_name(pc->ac_consumer), obj->String.Pointer)); 498102470Siwasaki 499102470Siwasaki /* get the handle of the resource */ 500102470Siwasaki if (ACPI_FAILURE(status = AcpiGetHandle(NULL, obj->String.Pointer, &res))) { 501102470Siwasaki ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "couldn't find power resource %s\n", 502102470Siwasaki obj->String.Pointer)); 503102470Siwasaki return_VOID; 504102470Siwasaki } 505102470Siwasaki break; 506102470Siwasaki 507102470Siwasaki default: 50882372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "don't know how to create a power reference to object type %d\n", 50982372Smsmith obj->Type)); 51079357Smsmith return_VOID; 51179357Smsmith } 51278915Smsmith 51379357Smsmith /* create/look up the resource */ 51479357Smsmith if (ACPI_FAILURE(status = acpi_pwr_register_resource(res))) { 51582372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "couldn't register power resource %s - %s\n", 51682372Smsmith obj->String.Pointer, AcpiFormatException(status))); 51779357Smsmith return_VOID; 51879357Smsmith } 51979357Smsmith if ((rp = acpi_pwr_find_resource(res)) == NULL) { 52082372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "power resource list corrupted\n")); 52179357Smsmith return_VOID; 52279357Smsmith } 52382372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "found power resource %s\n", acpi_name(rp->ap_resource))); 52479357Smsmith 52579357Smsmith /* create a reference between the consumer and resource */ 52679357Smsmith if ((pr = malloc(sizeof(*pr), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) { 52782372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "couldn't allocate memory for a power consumer reference\n")); 52879357Smsmith return_VOID; 52979357Smsmith } 53079357Smsmith pr->ar_consumer = pc; 53179357Smsmith pr->ar_resource = rp; 53279357Smsmith TAILQ_INSERT_TAIL(&pc->ac_references, pr, ar_clink); 53379357Smsmith TAILQ_INSERT_TAIL(&rp->ap_references, pr, ar_rlink); 53479357Smsmith 53578915Smsmith return_VOID; 53678915Smsmith} 53778915Smsmith 53878915Smsmith 53978915Smsmith/* 54078915Smsmith * Switch power resources to conform to the desired state. 54178915Smsmith * 54278915Smsmith * Consumers may have modified the power resource list in an arbitrary 54378915Smsmith * fashion; we sweep it in sequence order. 54478915Smsmith */ 54578915Smsmithstatic ACPI_STATUS 54678915Smsmithacpi_pwr_switch_power(void) 54778915Smsmith{ 54878915Smsmith struct acpi_powerresource *rp; 54978915Smsmith ACPI_STATUS status; 55078915Smsmith int cur; 55178915Smsmith 55296926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 55378915Smsmith 55478915Smsmith /* 55578915Smsmith * Sweep the list forwards turning things on. 55678915Smsmith */ 55778915Smsmith TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) { 55879357Smsmith if (TAILQ_FIRST(&rp->ap_references) == NULL) { 55982372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s has no references, not turning on\n", 56082372Smsmith acpi_name(rp->ap_resource))); 56179357Smsmith continue; 56279357Smsmith } 56378915Smsmith 56478915Smsmith /* we could cache this if we trusted it not to change under us */ 56591125Smsmith if (ACPI_FAILURE(status = acpi_EvaluateInteger(rp->ap_resource, "_STA", &cur))) { 56682372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n", 56782372Smsmith acpi_name(rp->ap_resource), status)); 56878915Smsmith continue; /* XXX is this correct? Always switch if in doubt? */ 56978915Smsmith } 57078915Smsmith 57178915Smsmith /* 57278915Smsmith * Switch if required. Note that we ignore the result of the switch 57378915Smsmith * effort; we don't know what to do if it fails, so checking wouldn't 57478915Smsmith * help much. 57578915Smsmith */ 57679357Smsmith if (cur != ACPI_PWR_ON) { 57779357Smsmith if (ACPI_FAILURE(status = AcpiEvaluateObject(rp->ap_resource, "_ON", NULL, NULL))) { 57882372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to switch %s on - %s\n", 57982372Smsmith acpi_name(rp->ap_resource), AcpiFormatException(status))); 58079357Smsmith } else { 58182372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s on\n", acpi_name(rp->ap_resource))); 58279357Smsmith } 58379357Smsmith } else { 58482372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already on\n", acpi_name(rp->ap_resource))); 58579357Smsmith } 58678915Smsmith } 58778915Smsmith 58878915Smsmith /* 58978915Smsmith * Sweep the list backwards turning things off. 59078915Smsmith */ 59178915Smsmith TAILQ_FOREACH_REVERSE(rp, &acpi_powerresources, acpi_powerresource_list, ap_link) { 59279357Smsmith if (TAILQ_FIRST(&rp->ap_references) != NULL) { 59382372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s has references, not turning off\n", 59482372Smsmith acpi_name(rp->ap_resource))); 59579357Smsmith continue; 59679357Smsmith } 59778915Smsmith 59878915Smsmith /* we could cache this if we trusted it not to change under us */ 59991125Smsmith if (ACPI_FAILURE(status = acpi_EvaluateInteger(rp->ap_resource, "_STA", &cur))) { 60082372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n", 60182372Smsmith acpi_name(rp->ap_resource), status)); 60278915Smsmith continue; /* XXX is this correct? Always switch if in doubt? */ 60378915Smsmith } 60478915Smsmith 60578915Smsmith /* 60678915Smsmith * Switch if required. Note that we ignore the result of the switch 60778915Smsmith * effort; we don't know what to do if it fails, so checking wouldn't 60878915Smsmith * help much. 60978915Smsmith */ 61079357Smsmith if (cur != ACPI_PWR_OFF) { 61179357Smsmith if (ACPI_FAILURE(status = AcpiEvaluateObject(rp->ap_resource, "_OFF", NULL, NULL))) { 61282372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to switch %s off - %s\n", 61382372Smsmith acpi_name(rp->ap_resource), AcpiFormatException(status))); 61479357Smsmith } else { 61582372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s off\n", acpi_name(rp->ap_resource))); 61679357Smsmith } 61779357Smsmith } else { 61882372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already off\n", acpi_name(rp->ap_resource))); 61979357Smsmith } 62078915Smsmith } 62178915Smsmith return_ACPI_STATUS(AE_OK); 62278915Smsmith} 62378915Smsmith 62478915Smsmith/* 62578915Smsmith * Find a power resource's control structure. 62678915Smsmith */ 62778915Smsmithstatic struct acpi_powerresource * 62878915Smsmithacpi_pwr_find_resource(ACPI_HANDLE res) 62978915Smsmith{ 63078915Smsmith struct acpi_powerresource *rp; 63178915Smsmith 63296926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 63378915Smsmith 63478915Smsmith TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) 63578915Smsmith if (rp->ap_resource == res) 63678915Smsmith break; 63784445Sdfr return_PTR(rp); 63878915Smsmith} 63978915Smsmith 64078915Smsmith/* 64178915Smsmith * Find a power consumer's control structure. 64278915Smsmith */ 64378915Smsmithstatic struct acpi_powerconsumer * 64478915Smsmithacpi_pwr_find_consumer(ACPI_HANDLE consumer) 64578915Smsmith{ 64678915Smsmith struct acpi_powerconsumer *pc; 64778915Smsmith 64896926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 64978915Smsmith 65078915Smsmith TAILQ_FOREACH(pc, &acpi_powerconsumers, ac_link) 65178915Smsmith if (pc->ac_consumer == consumer) 65278915Smsmith break; 65384445Sdfr return_PTR(pc); 65478915Smsmith} 65578915Smsmith 656