acpi_powerres.c revision 126517
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 126517 2004-03-03 03:02:17Z 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); 100119529Snjlstatic ACPI_STATUS acpi_pwr_deregister_consumer(ACPI_HANDLE consumer); 101119529Snjlstatic ACPI_STATUS acpi_pwr_register_resource(ACPI_HANDLE res); 102119529Snjlstatic ACPI_STATUS acpi_pwr_deregister_resource(ACPI_HANDLE res); 103119529Snjlstatic void acpi_pwr_reference_resource(ACPI_OBJECT *obj, 104119529Snjl void *arg); 105119529Snjlstatic ACPI_STATUS acpi_pwr_switch_power(void); 106119529Snjlstatic struct acpi_powerresource 107119529Snjl *acpi_pwr_find_resource(ACPI_HANDLE res); 108119529Snjlstatic struct acpi_powerconsumer 109119529Snjl *acpi_pwr_find_consumer(ACPI_HANDLE consumer); 11078915Smsmith 111119529Snjl/* Initialise our lists. */ 11278915Smsmithstatic void 11378915Smsmithacpi_pwr_init(void *junk) 11478915Smsmith{ 11578915Smsmith TAILQ_INIT(&acpi_powerresources); 11678915Smsmith TAILQ_INIT(&acpi_powerconsumers); 11778915Smsmith} 11878915SmsmithSYSINIT(acpi_powerresource, SI_SUB_TUNABLES, SI_ORDER_ANY, acpi_pwr_init, NULL); 11978915Smsmith 12078915Smsmith/* 12178915Smsmith * Register a power resource. 12278915Smsmith * 12378915Smsmith * It's OK to call this if we already know about the resource. 12478915Smsmith */ 12578915Smsmithstatic ACPI_STATUS 12678915Smsmithacpi_pwr_register_resource(ACPI_HANDLE res) 12778915Smsmith{ 12878915Smsmith ACPI_STATUS status; 12978915Smsmith ACPI_BUFFER buf; 13079493Smsmith ACPI_OBJECT *obj; 13178915Smsmith struct acpi_powerresource *rp, *srp; 13278915Smsmith 13396926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 13478915Smsmith 13578915Smsmith rp = NULL; 13691125Smsmith buf.Pointer = NULL; 13778915Smsmith 138119529Snjl /* Look to see if we know about this resource */ 13978915Smsmith if (acpi_pwr_find_resource(res) != NULL) 140119529Snjl return_ACPI_STATUS (AE_OK); /* already know about it */ 14178915Smsmith 142119529Snjl /* Allocate a new resource */ 14378915Smsmith if ((rp = malloc(sizeof(*rp), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) { 14478915Smsmith status = AE_NO_MEMORY; 14578915Smsmith goto out; 14678915Smsmith } 14778915Smsmith TAILQ_INIT(&rp->ap_references); 14878915Smsmith rp->ap_resource = res; 14978915Smsmith 150119529Snjl /* Get the Power Resource object */ 15191125Smsmith buf.Length = ACPI_ALLOCATE_BUFFER; 15291125Smsmith if (ACPI_FAILURE(status = AcpiEvaluateObject(res, NULL, NULL, &buf))) { 15382372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "no power resource object\n")); 15478915Smsmith goto out; 15578915Smsmith } 15678915Smsmith obj = buf.Pointer; 15779493Smsmith if (obj->Type != ACPI_TYPE_POWER) { 158119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 159119529Snjl "questionable power resource object %s\n", 160119529Snjl acpi_name(res))); 16179493Smsmith status = AE_TYPE; 16279493Smsmith goto out; 16378915Smsmith } 16479493Smsmith rp->ap_systemlevel = obj->PowerResource.SystemLevel; 16579493Smsmith rp->ap_order = obj->PowerResource.ResourceOrder; 16678915Smsmith 167119529Snjl /* Sort the resource into the list */ 16878915Smsmith status = AE_OK; 16978915Smsmith srp = TAILQ_FIRST(&acpi_powerresources); 170119529Snjl if (srp == NULL || rp->ap_order < srp->ap_order) { 17178915Smsmith TAILQ_INSERT_HEAD(&acpi_powerresources, rp, ap_link); 17279357Smsmith goto done; 17378915Smsmith } 174119529Snjl TAILQ_FOREACH(srp, &acpi_powerresources, ap_link) { 17578915Smsmith if (rp->ap_order < srp->ap_order) { 17678915Smsmith TAILQ_INSERT_BEFORE(srp, rp, ap_link); 17779357Smsmith goto done; 17878915Smsmith } 179119529Snjl } 18078915Smsmith TAILQ_INSERT_TAIL(&acpi_powerresources, rp, ap_link); 18179357Smsmith 18279357Smsmith done: 183119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 184119529Snjl "registered power resource %s\n", acpi_name(res))); 185119529Snjl 18678915Smsmith out: 18791125Smsmith if (buf.Pointer != NULL) 18891125Smsmith AcpiOsFree(buf.Pointer); 189119529Snjl if (ACPI_FAILURE(status) && rp != NULL) 19078915Smsmith free(rp, M_ACPIPWR); 191119529Snjl return_ACPI_STATUS (status); 19278915Smsmith} 19378915Smsmith 19478915Smsmith/* 19578915Smsmith * Deregister a power resource. 19678915Smsmith */ 19778915Smsmithstatic ACPI_STATUS 19878915Smsmithacpi_pwr_deregister_resource(ACPI_HANDLE res) 19978915Smsmith{ 20078915Smsmith struct acpi_powerresource *rp; 20178915Smsmith 20296926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 20378915Smsmith 20478915Smsmith rp = NULL; 20578915Smsmith 206119529Snjl /* Find the resource */ 20778915Smsmith if ((rp = acpi_pwr_find_resource(res)) == NULL) 208119529Snjl return_ACPI_STATUS (AE_BAD_PARAMETER); 20978915Smsmith 210119529Snjl /* Check that there are no consumers referencing this resource */ 21178915Smsmith if (TAILQ_FIRST(&rp->ap_references) != NULL) 212119529Snjl return_ACPI_STATUS (AE_BAD_PARAMETER); 21378915Smsmith 214119529Snjl /* Pull it off the list and free it */ 21578915Smsmith TAILQ_REMOVE(&acpi_powerresources, rp, ap_link); 21678915Smsmith free(rp, M_ACPIPWR); 21778915Smsmith 218119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power resource %s\n", 219119529Snjl acpi_name(res))); 22078915Smsmith 221119529Snjl return_ACPI_STATUS (AE_OK); 22278915Smsmith} 22378915Smsmith 22478915Smsmith/* 22578915Smsmith * Register a power consumer. 22678915Smsmith * 22778915Smsmith * It's OK to call this if we already know about the consumer. 22878915Smsmith */ 22978915Smsmithstatic ACPI_STATUS 23078915Smsmithacpi_pwr_register_consumer(ACPI_HANDLE consumer) 23178915Smsmith{ 23278915Smsmith struct acpi_powerconsumer *pc; 23378915Smsmith 23496926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 23578915Smsmith 236119529Snjl /* Check to see whether we know about this consumer already */ 23778915Smsmith if ((pc = acpi_pwr_find_consumer(consumer)) != NULL) 238119529Snjl return_ACPI_STATUS (AE_OK); 23978915Smsmith 240119529Snjl /* Allocate a new power consumer */ 24178915Smsmith if ((pc = malloc(sizeof(*pc), M_ACPIPWR, M_NOWAIT)) == NULL) 242119529Snjl return_ACPI_STATUS (AE_NO_MEMORY); 24378915Smsmith TAILQ_INSERT_HEAD(&acpi_powerconsumers, pc, ac_link); 24478915Smsmith TAILQ_INIT(&pc->ac_references); 24578915Smsmith pc->ac_consumer = consumer; 24678915Smsmith 247119529Snjl /* XXX we should try to find its current state */ 248119529Snjl pc->ac_state = ACPI_STATE_UNKNOWN; 24978915Smsmith 250119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "registered power consumer %s\n", 251119529Snjl acpi_name(consumer))); 25278915Smsmith 253119529Snjl return_ACPI_STATUS (AE_OK); 25478915Smsmith} 25578915Smsmith 25678915Smsmith/* 25778915Smsmith * Deregister a power consumer. 25878915Smsmith * 25978915Smsmith * This should only be done once the consumer has been powered off. 26078915Smsmith * (XXX is this correct? Check once implemented) 26178915Smsmith */ 26278915Smsmithstatic ACPI_STATUS 26378915Smsmithacpi_pwr_deregister_consumer(ACPI_HANDLE consumer) 26478915Smsmith{ 26578915Smsmith struct acpi_powerconsumer *pc; 26678915Smsmith 26796926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 26878915Smsmith 269119529Snjl /* Find the consumer */ 27078915Smsmith if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) 271119529Snjl return_ACPI_STATUS (AE_BAD_PARAMETER); 27278915Smsmith 273119529Snjl /* Make sure the consumer's not referencing anything right now */ 27478915Smsmith if (TAILQ_FIRST(&pc->ac_references) != NULL) 275119529Snjl return_ACPI_STATUS (AE_BAD_PARAMETER); 27678915Smsmith 277119529Snjl /* Pull the consumer off the list and free it */ 27878915Smsmith TAILQ_REMOVE(&acpi_powerconsumers, pc, ac_link); 27978915Smsmith 280119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power consumer %s\n", 281119529Snjl acpi_name(consumer))); 28278915Smsmith 283119529Snjl return_ACPI_STATUS (AE_OK); 28478915Smsmith} 28578915Smsmith 28678915Smsmith/* 28778915Smsmith * Set a power consumer to a particular power state. 28878915Smsmith */ 28978915SmsmithACPI_STATUS 29078915Smsmithacpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state) 29178915Smsmith{ 29278915Smsmith struct acpi_powerconsumer *pc; 29378915Smsmith struct acpi_powerreference *pr; 29482084Siwasaki ACPI_HANDLE method_handle, reslist_handle, pr0_handle; 29578915Smsmith ACPI_BUFFER reslist_buffer; 29678915Smsmith ACPI_OBJECT *reslist_object; 29778915Smsmith ACPI_STATUS status; 29878915Smsmith char *method_name, *reslist_name; 29978915Smsmith int res_changed; 30078915Smsmith 30196926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 30278915Smsmith 303119529Snjl /* Find the consumer */ 30478915Smsmith if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) { 30591125Smsmith if (ACPI_FAILURE(status = acpi_pwr_register_consumer(consumer))) 306119529Snjl return_ACPI_STATUS (status); 30778915Smsmith if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) { 308119529Snjl return_ACPI_STATUS (AE_ERROR); /* something very wrong */ 30978915Smsmith } 31078915Smsmith } 31178915Smsmith 312119529Snjl /* Check for valid transitions */ 313119529Snjl if (pc->ac_state == ACPI_STATE_D3 && state != ACPI_STATE_D0) 314119529Snjl return_ACPI_STATUS (AE_BAD_PARAMETER); /* can only go to D0 from D3 */ 31578915Smsmith 316119529Snjl /* Find transition mechanism(s) */ 31778915Smsmith switch(state) { 31878915Smsmith case ACPI_STATE_D0: 31978915Smsmith method_name = "_PS0"; 32078915Smsmith reslist_name = "_PR0"; 32178915Smsmith break; 32278915Smsmith case ACPI_STATE_D1: 32378915Smsmith method_name = "_PS1"; 32478915Smsmith reslist_name = "_PR1"; 32578915Smsmith break; 32678915Smsmith case ACPI_STATE_D2: 32778915Smsmith method_name = "_PS2"; 32878915Smsmith reslist_name = "_PR2"; 32978915Smsmith break; 33078915Smsmith case ACPI_STATE_D3: 33178915Smsmith method_name = "_PS3"; 33278915Smsmith reslist_name = "_PR3"; 33378915Smsmith break; 33478915Smsmith default: 335119529Snjl return_ACPI_STATUS (AE_BAD_PARAMETER); 33678915Smsmith } 33782372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "setup to switch %s D%d -> D%d\n", 338119529Snjl acpi_name(consumer), pc->ac_state, state)); 33978915Smsmith 34078915Smsmith /* 34178915Smsmith * Verify that this state is supported, ie. one of method or 34278915Smsmith * reslist must be present. We need to do this before we go 34378915Smsmith * dereferencing resources (since we might be trying to go to 34478915Smsmith * a state we don't support). 34578915Smsmith * 34678915Smsmith * Note that if any states are supported, the device has to 34778915Smsmith * support D0 and D3. It's never an error to try to go to 34878915Smsmith * D0. 34978915Smsmith */ 35091125Smsmith reslist_buffer.Pointer = NULL; 35182084Siwasaki reslist_object = NULL; 35291125Smsmith if (ACPI_FAILURE(AcpiGetHandle(consumer, method_name, &method_handle))) 35378915Smsmith method_handle = NULL; 35491125Smsmith if (ACPI_FAILURE(AcpiGetHandle(consumer, reslist_name, &reslist_handle))) 35578915Smsmith reslist_handle = NULL; 356119529Snjl if (reslist_handle == NULL && method_handle == NULL) { 35778915Smsmith if (state == ACPI_STATE_D0) { 35878915Smsmith pc->ac_state = ACPI_STATE_D0; 359119529Snjl return_ACPI_STATUS (AE_OK); 36078915Smsmith } 361119529Snjl if (state != ACPI_STATE_D3) 36282084Siwasaki goto bad; 36382084Siwasaki 364119529Snjl /* Turn off the resources listed in _PR0 to go to D3. */ 365119529Snjl if (ACPI_FAILURE(AcpiGetHandle(consumer, "_PR0", &pr0_handle))) 36682084Siwasaki goto bad; 36791125Smsmith reslist_buffer.Length = ACPI_ALLOCATE_BUFFER; 368119529Snjl status = AcpiEvaluateObject(pr0_handle, NULL, NULL, &reslist_buffer); 369119529Snjl if (ACPI_FAILURE(status)) 37082084Siwasaki goto bad; 37182084Siwasaki reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer; 372119529Snjl if (reslist_object->Type != ACPI_TYPE_PACKAGE || 373119529Snjl reslist_object->Package.Count == 0) { 374119529Snjl 37582084Siwasaki goto bad; 37682084Siwasaki } 37791125Smsmith AcpiOsFree(reslist_buffer.Pointer); 37891125Smsmith reslist_buffer.Pointer = NULL; 37991125Smsmith reslist_object = NULL; 38078915Smsmith } 38178915Smsmith 38278915Smsmith /* 38378915Smsmith * Check that we can actually fetch the list of power resources 38478915Smsmith */ 38578915Smsmith if (reslist_handle != NULL) { 38691125Smsmith reslist_buffer.Length = ACPI_ALLOCATE_BUFFER; 387119529Snjl status = AcpiEvaluateObject(reslist_handle, NULL, NULL, 388119529Snjl &reslist_buffer); 389119529Snjl if (ACPI_FAILURE(status)) { 390119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 391119529Snjl "can't evaluate resource list %s\n", 392119529Snjl acpi_name(reslist_handle))); 39391125Smsmith goto out; 39478915Smsmith } 39578915Smsmith reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer; 39678915Smsmith if (reslist_object->Type != ACPI_TYPE_PACKAGE) { 397119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 398119529Snjl "resource list is not ACPI_TYPE_PACKAGE (%d)\n", 399119529Snjl reslist_object->Type)); 40091125Smsmith status = AE_TYPE; 40191125Smsmith goto out; 40278915Smsmith } 40378915Smsmith } 40478915Smsmith 40578915Smsmith /* 406125745Sjhb * Now we are ready to switch, so kill off any current power 407119529Snjl * resource references. 40878915Smsmith */ 40978915Smsmith res_changed = 0; 41079357Smsmith while((pr = TAILQ_FIRST(&pc->ac_references)) != NULL) { 41179357Smsmith res_changed = 1; 412119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "removing reference to %s\n", 413119529Snjl 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)); 427119529Snjl acpi_ForeachPackageObject(reslist_object, acpi_pwr_reference_resource, 428119529Snjl pc); 42978915Smsmith res_changed = 1; 43078915Smsmith } 43178915Smsmith 43278915Smsmith /* 43378915Smsmith * If we changed anything in the resource list, we need to run a switch 43478915Smsmith * pass now. 43578915Smsmith */ 43691125Smsmith if (ACPI_FAILURE(status = acpi_pwr_switch_power())) { 437119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 438119529Snjl "failed to switch resources from %s to D%d\n", 43982372Smsmith acpi_name(consumer), state)); 440119529Snjl 441119529Snjl /* XXX is this appropriate? Should we return to previous state? */ 442119529Snjl goto out; 44378915Smsmith } 44478915Smsmith 445119529Snjl /* Invoke power state switch method (if present) */ 44678915Smsmith if (method_handle != NULL) { 447119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 448119529Snjl "invoking state transition method %s\n", 449119529Snjl acpi_name(method_handle))); 450119529Snjl status = AcpiEvaluateObject(method_handle, NULL, NULL, NULL); 451119529Snjl if (ACPI_FAILURE(status)) { 45291125Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to set state - %s\n", 453119529Snjl AcpiFormatException(status))); 45491125Smsmith pc->ac_state = ACPI_STATE_UNKNOWN; 455119529Snjl 456119529Snjl /* XXX Should we return to previous state? */ 457119529Snjl goto out; 45891125Smsmith } 45978915Smsmith } 46091125Smsmith 461119529Snjl /* Transition was successful */ 46278915Smsmith pc->ac_state = state; 463119529Snjl return_ACPI_STATUS (AE_OK); 46482084Siwasaki 46582084Siwasaki bad: 466119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 467119529Snjl "attempt to set unsupported state D%d\n", state)); 46891125Smsmith status = AE_BAD_PARAMETER; 46991125Smsmith 47091125Smsmith out: 47191125Smsmith if (reslist_buffer.Pointer != NULL) 47291125Smsmith AcpiOsFree(reslist_buffer.Pointer); 473119529Snjl return_ACPI_STATUS (status); 47478915Smsmith} 47578915Smsmith 47678915Smsmith/* 47778915Smsmith * Called to create a reference between a power consumer and a power resource 47878915Smsmith * identified in the object. 47978915Smsmith */ 48078915Smsmithstatic void 48178915Smsmithacpi_pwr_reference_resource(ACPI_OBJECT *obj, void *arg) 48278915Smsmith{ 48378915Smsmith struct acpi_powerconsumer *pc = (struct acpi_powerconsumer *)arg; 48479357Smsmith struct acpi_powerreference *pr; 48579357Smsmith struct acpi_powerresource *rp; 48679357Smsmith ACPI_HANDLE res; 48779357Smsmith ACPI_STATUS status; 48878915Smsmith 48996926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 49078915Smsmith 49179357Smsmith /* check the object type */ 492102470Siwasaki switch (obj->Type) { 493102470Siwasaki case ACPI_TYPE_ANY: 494102470Siwasaki ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "building reference from %s to %s\n", 495119529Snjl acpi_name(pc->ac_consumer), 496119529Snjl acpi_name(obj->Reference.Handle))); 497102470Siwasaki res = obj->Reference.Handle; 498102470Siwasaki break; 499102470Siwasaki case ACPI_TYPE_STRING: 500102470Siwasaki ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "building reference from %s to %s\n", 501102470Siwasaki acpi_name(pc->ac_consumer), obj->String.Pointer)); 502102470Siwasaki 503119529Snjl /* Get the handle of the resource */ 504119529Snjl status = AcpiGetHandle(NULL, obj->String.Pointer, &res); 505119529Snjl if (ACPI_FAILURE(status)) { 506119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 507119529Snjl "couldn't find power resource %s\n", 508119529Snjl obj->String.Pointer)); 509102470Siwasaki return_VOID; 510102470Siwasaki } 511102470Siwasaki break; 512102470Siwasaki default: 513119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 514119529Snjl "can't create a power reference for object type %d\n", 515119529Snjl obj->Type)); 51679357Smsmith return_VOID; 51779357Smsmith } 51878915Smsmith 519119529Snjl /* Create/look up the resource */ 52079357Smsmith if (ACPI_FAILURE(status = acpi_pwr_register_resource(res))) { 521119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 522119529Snjl "couldn't register power resource %s - %s\n", 523119529Snjl obj->String.Pointer, AcpiFormatException(status))); 52479357Smsmith return_VOID; 52579357Smsmith } 52679357Smsmith if ((rp = acpi_pwr_find_resource(res)) == NULL) { 52782372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "power resource list corrupted\n")); 52879357Smsmith return_VOID; 52979357Smsmith } 530119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "found power resource %s\n", 531119529Snjl acpi_name(rp->ap_resource))); 53279357Smsmith 533119529Snjl /* Create a reference between the consumer and resource */ 53479357Smsmith if ((pr = malloc(sizeof(*pr), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) { 535119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 536119529Snjl "allocation failed for a power consumer reference\n")); 53779357Smsmith return_VOID; 53879357Smsmith } 53979357Smsmith pr->ar_consumer = pc; 54079357Smsmith pr->ar_resource = rp; 54179357Smsmith TAILQ_INSERT_TAIL(&pc->ac_references, pr, ar_clink); 54279357Smsmith TAILQ_INSERT_TAIL(&rp->ap_references, pr, ar_rlink); 54379357Smsmith 54478915Smsmith return_VOID; 54578915Smsmith} 54678915Smsmith 54778915Smsmith 54878915Smsmith/* 54978915Smsmith * Switch power resources to conform to the desired state. 55078915Smsmith * 55178915Smsmith * Consumers may have modified the power resource list in an arbitrary 55278915Smsmith * fashion; we sweep it in sequence order. 55378915Smsmith */ 55478915Smsmithstatic ACPI_STATUS 55578915Smsmithacpi_pwr_switch_power(void) 55678915Smsmith{ 55778915Smsmith struct acpi_powerresource *rp; 55878915Smsmith ACPI_STATUS status; 55978915Smsmith int cur; 56078915Smsmith 56196926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 56278915Smsmith 56378915Smsmith /* 56478915Smsmith * Sweep the list forwards turning things on. 56578915Smsmith */ 56678915Smsmith TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) { 56779357Smsmith if (TAILQ_FIRST(&rp->ap_references) == NULL) { 568119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 569119529Snjl "%s has no references, not turning on\n", 570119529Snjl acpi_name(rp->ap_resource))); 57179357Smsmith continue; 57279357Smsmith } 57378915Smsmith 574119529Snjl /* We could cache this if we trusted it not to change under us */ 575119529Snjl status = acpi_EvaluateInteger(rp->ap_resource, "_STA", &cur); 576119529Snjl if (ACPI_FAILURE(status)) { 57782372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n", 57882372Smsmith acpi_name(rp->ap_resource), status)); 579119529Snjl 580119529Snjl /* XXX is this correct? Always switch if in doubt? */ 581119529Snjl continue; 58278915Smsmith } 58378915Smsmith 58478915Smsmith /* 58578915Smsmith * Switch if required. Note that we ignore the result of the switch 58678915Smsmith * effort; we don't know what to do if it fails, so checking wouldn't 58778915Smsmith * help much. 58878915Smsmith */ 58979357Smsmith if (cur != ACPI_PWR_ON) { 590119529Snjl status = AcpiEvaluateObject(rp->ap_resource, "_ON", NULL, NULL); 591119529Snjl if (ACPI_FAILURE(status)) { 592119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 593119529Snjl "failed to switch %s on - %s\n", 594119529Snjl acpi_name(rp->ap_resource), 595119529Snjl AcpiFormatException(status))); 59679357Smsmith } else { 597119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s on\n", 598119529Snjl acpi_name(rp->ap_resource))); 59979357Smsmith } 60079357Smsmith } else { 601119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already on\n", 602119529Snjl acpi_name(rp->ap_resource))); 60379357Smsmith } 60478915Smsmith } 60578915Smsmith 606119529Snjl /* Sweep the list backwards turning things off. */ 607119529Snjl TAILQ_FOREACH_REVERSE(rp, &acpi_powerresources, acpi_powerresource_list, 608119529Snjl ap_link) { 609119529Snjl 61079357Smsmith if (TAILQ_FIRST(&rp->ap_references) != NULL) { 611119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 612119529Snjl "%s has references, not turning off\n", 613119529Snjl acpi_name(rp->ap_resource))); 61479357Smsmith continue; 61579357Smsmith } 61678915Smsmith 617119529Snjl /* We could cache this if we trusted it not to change under us */ 618119529Snjl status = acpi_EvaluateInteger(rp->ap_resource, "_STA", &cur); 619119529Snjl if (ACPI_FAILURE(status)) { 62082372Smsmith ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n", 62182372Smsmith acpi_name(rp->ap_resource), status)); 622119529Snjl /* XXX is this correct? Always switch if in doubt? */ 623119529Snjl continue; 62478915Smsmith } 62578915Smsmith 62678915Smsmith /* 62778915Smsmith * Switch if required. Note that we ignore the result of the switch 62878915Smsmith * effort; we don't know what to do if it fails, so checking wouldn't 62978915Smsmith * help much. 63078915Smsmith */ 63179357Smsmith if (cur != ACPI_PWR_OFF) { 632119529Snjl status = AcpiEvaluateObject(rp->ap_resource, "_OFF", NULL, NULL); 633119529Snjl if (ACPI_FAILURE(status)) { 634119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 635119529Snjl "failed to switch %s off - %s\n", 636119529Snjl acpi_name(rp->ap_resource), 637119529Snjl AcpiFormatException(status))); 63879357Smsmith } else { 639119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s off\n", 640119529Snjl acpi_name(rp->ap_resource))); 64179357Smsmith } 64279357Smsmith } else { 643119529Snjl ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already off\n", 644119529Snjl acpi_name(rp->ap_resource))); 64579357Smsmith } 64678915Smsmith } 647119529Snjl 648119529Snjl return_ACPI_STATUS (AE_OK); 64978915Smsmith} 65078915Smsmith 65178915Smsmith/* 65278915Smsmith * Find a power resource's control structure. 65378915Smsmith */ 65478915Smsmithstatic struct acpi_powerresource * 65578915Smsmithacpi_pwr_find_resource(ACPI_HANDLE res) 65678915Smsmith{ 65778915Smsmith struct acpi_powerresource *rp; 65878915Smsmith 65996926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 66078915Smsmith 661119529Snjl TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) { 66278915Smsmith if (rp->ap_resource == res) 66378915Smsmith break; 664119529Snjl } 665119529Snjl 666119529Snjl return_PTR (rp); 66778915Smsmith} 66878915Smsmith 66978915Smsmith/* 67078915Smsmith * Find a power consumer's control structure. 67178915Smsmith */ 67278915Smsmithstatic struct acpi_powerconsumer * 67378915Smsmithacpi_pwr_find_consumer(ACPI_HANDLE consumer) 67478915Smsmith{ 67578915Smsmith struct acpi_powerconsumer *pc; 67678915Smsmith 67796926Speter ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 67878915Smsmith 679119529Snjl TAILQ_FOREACH(pc, &acpi_powerconsumers, ac_link) { 68078915Smsmith if (pc->ac_consumer == consumer) 68178915Smsmith break; 682119529Snjl } 683119529Snjl 684119529Snjl return_PTR (pc); 68578915Smsmith} 686