acpi_powerres.c revision 89054
1193323Sed/*- 2193323Sed * Copyright (c) 2001 Michael Smith 3193323Sed * All rights reserved. 4193323Sed * 5193323Sed * Redistribution and use in source and binary forms, with or without 6193323Sed * modification, are permitted provided that the following conditions 7193323Sed * are met: 8193323Sed * 1. Redistributions of source code must retain the above copyright 9193323Sed * notice, this list of conditions and the following disclaimer. 10193323Sed * 2. Redistributions in binary form must reproduce the above copyright 11193323Sed * notice, this list of conditions and the following disclaimer in the 12193323Sed * documentation and/or other materials provided with the distribution. 13193323Sed * 14193323Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15193323Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16193323Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17193323Sed * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18193323Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19193323Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20198090Srdivacky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21198090Srdivacky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22198090Srdivacky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23193323Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24193323Sed * SUCH DAMAGE. 25193323Sed * 26193323Sed * $FreeBSD: head/sys/dev/acpica/acpi_powerres.c 89054 2002-01-08 06:46:01Z msmith $ 27198090Srdivacky */ 28198090Srdivacky 29198090Srdivacky#include "opt_acpi.h" /* XXX trim includes */ 30198090Srdivacky#include <sys/param.h> 31198090Srdivacky#include <sys/kernel.h> 32193323Sed#include <sys/proc.h> 33193323Sed#include <sys/lock.h> 34193323Sed#include <sys/malloc.h> 35193323Sed#include <sys/mutex.h> 36193323Sed#include <sys/bus.h> 37193323Sed#include <sys/conf.h> 38193323Sed#include <sys/ioccom.h> 39193323Sed#include <sys/reboot.h> 40193323Sed#include <sys/sysctl.h> 41193323Sed#include <sys/systm.h> 42198090Srdivacky#include <sys/ctype.h> 43198090Srdivacky 44198090Srdivacky#include <machine/clock.h> 45198090Srdivacky 46193323Sed#include <machine/resource.h> 47193323Sed 48193323Sed#include "acpi.h" 49193323Sed 50193323Sed#include <dev/acpica/acpivar.h> 51193323Sed#include <dev/acpica/acpiio.h> 52193323Sed 53193323Sed/* 54193323Sed * ACPI power resource management. 55193323Sed * 56198090Srdivacky * Power resource behaviour is slightly complicated by the fact that 57198090Srdivacky * a single power resource may provide power for more than one device. 58193323Sed * Thus, we must track the device(s) being powered by a given power 59193323Sed * resource, and only deactivate it when there are no powered devices. 60193323Sed * 61193323Sed * Note that this only manages resources for known devices. There is an 62193323Sed * ugly case where we may turn of power to a device which is in use because 63193323Sed * we don't know that it depends on a given resource. We should perhaps 64193323Sed * try to be smarter about this, but a more complete solution would involve 65193323Sed * scanning all of the ACPI namespace to find devices we're not currently 66193323Sed * aware of, and this raises questions about whether they should be left 67193323Sed * on, turned off, etc. 68193323Sed * 69193323Sed * XXX locking 70198090Srdivacky */ 71198090Srdivacky 72198090SrdivackyMALLOC_DEFINE(M_ACPIPWR, "acpipwr", "ACPI power resources"); 73198090Srdivacky 74193323Sed/* 75198090Srdivacky * Hooks for the ACPI CA debugging infrastructure 76198090Srdivacky */ 77193323Sed#define _COMPONENT ACPI_POWER 78198090SrdivackyMODULE_NAME("POWERRES") 79193323Sed 80193323Sed/* return values from _STA on a power resource */ 81193323Sed#define ACPI_PWR_OFF 0 82193323Sed#define ACPI_PWR_ON 1 83193323Sed 84193323Sed/* 85198090Srdivacky * A relationship between a power resource and a consumer. 86198090Srdivacky */ 87198090Srdivackystruct acpi_powerreference { 88198090Srdivacky struct acpi_powerconsumer *ar_consumer; 89198090Srdivacky struct acpi_powerresource *ar_resource; 90198090Srdivacky TAILQ_ENTRY(acpi_powerreference) ar_rlink; /* link on resource list */ 91198090Srdivacky TAILQ_ENTRY(acpi_powerreference) ar_clink; /* link on consumer */ 92198090Srdivacky}; 93198090Srdivacky 94198090Srdivacky/* 95198090Srdivacky * A power-managed device. 96198090Srdivacky */ 97198090Srdivackystruct acpi_powerconsumer { 98198090Srdivacky ACPI_HANDLE ac_consumer; /* device which is powered */ 99198090Srdivacky int ac_state; 100198090Srdivacky TAILQ_ENTRY(acpi_powerconsumer) ac_link; 101198090Srdivacky TAILQ_HEAD(,acpi_powerreference) ac_references; 102193323Sed}; 103198090Srdivacky 104198090Srdivacky/* 105198090Srdivacky * A power resource. 106198090Srdivacky */ 107198090Srdivackystruct acpi_powerresource { 108198090Srdivacky TAILQ_ENTRY(acpi_powerresource) ap_link; 109198090Srdivacky TAILQ_HEAD(,acpi_powerreference) ap_references; 110198090Srdivacky ACPI_HANDLE ap_resource; /* the resource's handle */ 111193323Sed ACPI_INTEGER ap_systemlevel; 112198090Srdivacky ACPI_INTEGER ap_order; 113198090Srdivacky}; 114198090Srdivacky 115198090Srdivackystatic TAILQ_HEAD(acpi_powerresource_list, acpi_powerresource) acpi_powerresources; 116198090Srdivackystatic TAILQ_HEAD(acpi_powerconsumer_list, acpi_powerconsumer) acpi_powerconsumers; 117198090Srdivacky 118198090Srdivackystatic ACPI_STATUS acpi_pwr_register_consumer(ACPI_HANDLE consumer); 119198090Srdivackystatic ACPI_STATUS acpi_pwr_deregister_consumer(ACPI_HANDLE consumer); 120198090Srdivackystatic ACPI_STATUS acpi_pwr_register_resource(ACPI_HANDLE res); 121198090Srdivackystatic ACPI_STATUS acpi_pwr_deregister_resource(ACPI_HANDLE res); 122198090Srdivackystatic void acpi_pwr_reference_resource(ACPI_OBJECT *obj, void *arg); 123198090Srdivackystatic ACPI_STATUS acpi_pwr_switch_power(void); 124198090Srdivackystatic struct acpi_powerresource *acpi_pwr_find_resource(ACPI_HANDLE res); 125198090Srdivackystatic struct acpi_powerconsumer *acpi_pwr_find_consumer(ACPI_HANDLE consumer); 126198090Srdivacky 127198090Srdivacky/* 128198090Srdivacky * Initialise our lists. 129198090Srdivacky */ 130198090Srdivackystatic void 131198090Srdivackyacpi_pwr_init(void *junk) 132198090Srdivacky{ 133198090Srdivacky TAILQ_INIT(&acpi_powerresources); 134198090Srdivacky TAILQ_INIT(&acpi_powerconsumers); 135198090Srdivacky} 136198090SrdivackySYSINIT(acpi_powerresource, SI_SUB_TUNABLES, SI_ORDER_ANY, acpi_pwr_init, NULL); 137198090Srdivacky 138198090Srdivacky/* 139193323Sed * Register a power resource. 140193323Sed * 141193323Sed * It's OK to call this if we already know about the resource. 142193323Sed */ 143193323Sedstatic ACPI_STATUS 144193323Sedacpi_pwr_register_resource(ACPI_HANDLE res) 145193323Sed{ 146193323Sed ACPI_STATUS status; 147193323Sed ACPI_BUFFER buf; 148193323Sed ACPI_OBJECT *obj; 149193323Sed struct acpi_powerresource *rp, *srp; 150198090Srdivacky 151193323Sed FUNCTION_TRACE(__func__); 152193323Sed 153193323Sed rp = NULL; 154193323Sed obj = NULL; 155193323Sed 156193323Sed /* look to see if we know about this resource */ 157193323Sed if (acpi_pwr_find_resource(res) != NULL) 158193323Sed return_ACPI_STATUS(AE_OK); /* already know about it */ 159193323Sed 160193323Sed /* allocate a new resource */ 161193323Sed if ((rp = malloc(sizeof(*rp), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) { 162193323Sed status = AE_NO_MEMORY; 163193323Sed goto out; 164193323Sed } 165193323Sed TAILQ_INIT(&rp->ap_references); 166193323Sed rp->ap_resource = res; 167193323Sed 168193323Sed /* get the Power Resource object */ 169193323Sed bzero(&buf, sizeof(buf)); 170193323Sed if ((status = acpi_EvaluateIntoBuffer(res, NULL, NULL, &buf)) != AE_OK) { 171193323Sed ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "no power resource object\n")); 172193323Sed goto out; 173193323Sed } 174193323Sed obj = buf.Pointer; 175193323Sed if (obj->Type != ACPI_TYPE_POWER) { 176193323Sed ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "questionable power resource object %s\n", acpi_name(res))); 177193323Sed status = AE_TYPE; 178193323Sed goto out; 179193323Sed } 180193323Sed rp->ap_systemlevel = obj->PowerResource.SystemLevel; 181193323Sed rp->ap_order = obj->PowerResource.ResourceOrder; 182193323Sed 183193323Sed /* sort the resource into the list */ 184193323Sed status = AE_OK; 185193323Sed srp = TAILQ_FIRST(&acpi_powerresources); 186193323Sed if ((srp == NULL) || (rp->ap_order < srp->ap_order)) { 187193323Sed TAILQ_INSERT_HEAD(&acpi_powerresources, rp, ap_link); 188193323Sed goto done; 189193323Sed } 190193323Sed TAILQ_FOREACH(srp, &acpi_powerresources, ap_link) 191193323Sed if (rp->ap_order < srp->ap_order) { 192193323Sed TAILQ_INSERT_BEFORE(srp, rp, ap_link); 193193323Sed goto done; 194193323Sed } 195193323Sed TAILQ_INSERT_TAIL(&acpi_powerresources, rp, ap_link); 196193323Sed 197193323Sed done: 198193323Sed ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "registered power resource %s\n", acpi_name(res))); 199193323Sed out: 200193323Sed if (obj != NULL) 201193323Sed AcpiOsFree(obj); 202193323Sed if ((status != AE_OK) && (rp != NULL)) 203193323Sed free(rp, M_ACPIPWR); 204193323Sed return_ACPI_STATUS(status); 205193323Sed} 206193323Sed 207193323Sed/* 208193323Sed * Deregister a power resource. 209193323Sed */ 210193323Sedstatic ACPI_STATUS 211193323Sedacpi_pwr_deregister_resource(ACPI_HANDLE res) 212193323Sed{ 213193323Sed struct acpi_powerresource *rp; 214193323Sed 215193323Sed FUNCTION_TRACE(__func__); 216193323Sed 217193323Sed rp = NULL; 218193323Sed 219193323Sed /* find the resource */ 220193323Sed if ((rp = acpi_pwr_find_resource(res)) == NULL) 221198090Srdivacky return_ACPI_STATUS(AE_BAD_PARAMETER); 222198090Srdivacky 223193323Sed /* check that there are no consumers referencing this resource */ 224198090Srdivacky if (TAILQ_FIRST(&rp->ap_references) != NULL) 225198090Srdivacky return_ACPI_STATUS(AE_BAD_PARAMETER); 226193323Sed 227193323Sed /* pull it off the list and free it */ 228193323Sed TAILQ_REMOVE(&acpi_powerresources, rp, ap_link); 229193323Sed free(rp, M_ACPIPWR); 230193323Sed 231193323Sed ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power resource %s\n", acpi_name(res))); 232193323Sed 233198090Srdivacky return_ACPI_STATUS(AE_OK); 234193323Sed} 235193323Sed 236198090Srdivacky/* 237198090Srdivacky * Register a power consumer. 238193323Sed * 239193323Sed * It's OK to call this if we already know about the consumer. 240193323Sed */ 241193323Sedstatic ACPI_STATUS 242193323Sedacpi_pwr_register_consumer(ACPI_HANDLE consumer) 243193323Sed{ 244193323Sed struct acpi_powerconsumer *pc; 245193323Sed 246193323Sed FUNCTION_TRACE(__func__); 247193323Sed 248193323Sed /* check to see whether we know about this consumer already */ 249193323Sed if ((pc = acpi_pwr_find_consumer(consumer)) != NULL) 250193323Sed return_ACPI_STATUS(AE_OK); 251193323Sed 252193323Sed /* allocate a new power consumer */ 253193323Sed if ((pc = malloc(sizeof(*pc), M_ACPIPWR, M_NOWAIT)) == NULL) 254193323Sed return_ACPI_STATUS(AE_NO_MEMORY); 255193323Sed TAILQ_INSERT_HEAD(&acpi_powerconsumers, pc, ac_link); 256193323Sed TAILQ_INIT(&pc->ac_references); 257193323Sed pc->ac_consumer = consumer; 258193323Sed 259193323Sed pc->ac_state = ACPI_STATE_UNKNOWN; /* XXX we should try to find its current state */ 260193323Sed 261193323Sed ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "registered power consumer %s\n", acpi_name(consumer))); 262193323Sed 263193323Sed return_ACPI_STATUS(AE_OK); 264193323Sed} 265193323Sed 266193323Sed/* 267193323Sed * Deregister a power consumer. 268193323Sed * 269193323Sed * This should only be done once the consumer has been powered off. 270193323Sed * (XXX is this correct? Check once implemented) 271193323Sed */ 272193323Sedstatic ACPI_STATUS 273193323Sedacpi_pwr_deregister_consumer(ACPI_HANDLE consumer) 274193323Sed{ 275193323Sed struct acpi_powerconsumer *pc; 276193323Sed 277193323Sed FUNCTION_TRACE(__func__); 278193323Sed 279193323Sed /* find the consumer */ 280193323Sed if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) 281193323Sed return_ACPI_STATUS(AE_BAD_PARAMETER); 282193323Sed 283193323Sed /* make sure the consumer's not referencing anything right now */ 284193323Sed if (TAILQ_FIRST(&pc->ac_references) != NULL) 285193323Sed return_ACPI_STATUS(AE_BAD_PARAMETER); 286193323Sed 287193323Sed /* pull the consumer off the list and free it */ 288193323Sed TAILQ_REMOVE(&acpi_powerconsumers, pc, ac_link); 289193323Sed 290193323Sed ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power consumer %s\n", acpi_name(consumer))); 291193323Sed 292193323Sed return_ACPI_STATUS(AE_OK); 293193323Sed} 294193323Sed 295193323Sed/* 296193323Sed * Set a power consumer to a particular power state. 297193323Sed */ 298193323SedACPI_STATUS 299193323Sedacpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state) 300193323Sed{ 301193323Sed struct acpi_powerconsumer *pc; 302193323Sed struct acpi_powerreference *pr; 303193323Sed ACPI_HANDLE method_handle, reslist_handle, pr0_handle; 304193323Sed ACPI_BUFFER reslist_buffer; 305193323Sed ACPI_OBJECT *reslist_object; 306193323Sed ACPI_STATUS status; 307193323Sed char *method_name, *reslist_name; 308193323Sed int res_changed; 309193323Sed 310193323Sed FUNCTION_TRACE(__func__); 311193323Sed 312193323Sed /* find the consumer */ 313193323Sed if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) { 314193323Sed if ((status = acpi_pwr_register_consumer(consumer)) != AE_OK) 315193323Sed return_ACPI_STATUS(status); 316193323Sed if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) { 317193323Sed return_ACPI_STATUS(AE_ERROR); /* something very wrong */ 318193323Sed } 319193323Sed } 320193323Sed 321193323Sed /* check for valid transitions */ 322193323Sed if ((pc->ac_state == ACPI_STATE_D3) && (state != ACPI_STATE_D0)) 323193323Sed return_ACPI_STATUS(AE_BAD_PARAMETER); /* can only go to D0 from D3 */ 324193323Sed 325193323Sed /* find transition mechanism(s) */ 326193323Sed switch(state) { 327193323Sed case ACPI_STATE_D0: 328193323Sed method_name = "_PS0"; 329193323Sed reslist_name = "_PR0"; 330193323Sed break; 331193323Sed case ACPI_STATE_D1: 332193323Sed method_name = "_PS1"; 333193323Sed reslist_name = "_PR1"; 334193323Sed break; 335193323Sed case ACPI_STATE_D2: 336193323Sed method_name = "_PS2"; 337198090Srdivacky reslist_name = "_PR2"; 338193323Sed break; 339193323Sed case ACPI_STATE_D3: 340193323Sed method_name = "_PS3"; 341193323Sed reslist_name = "_PR3"; 342193323Sed break; 343193323Sed default: 344193323Sed return_ACPI_STATUS(AE_BAD_PARAMETER); 345193323Sed } 346193323Sed ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "setup to switch %s D%d -> D%d\n", 347193323Sed acpi_name(consumer), pc->ac_state, state)); 348193323Sed 349193323Sed /* 350193323Sed * Verify that this state is supported, ie. one of method or 351193323Sed * reslist must be present. We need to do this before we go 352193323Sed * dereferencing resources (since we might be trying to go to 353193323Sed * a state we don't support). 354193323Sed * 355193323Sed * Note that if any states are supported, the device has to 356193323Sed * support D0 and D3. It's never an error to try to go to 357193323Sed * D0. 358198090Srdivacky */ 359193323Sed reslist_object = NULL; 360193323Sed if (AcpiGetHandle(consumer, method_name, &method_handle) != AE_OK) 361193323Sed method_handle = NULL; 362193323Sed if (AcpiGetHandle(consumer, reslist_name, &reslist_handle) != AE_OK) 363193323Sed reslist_handle = NULL; 364193323Sed if ((reslist_handle == NULL) && (method_handle == NULL)) { 365193323Sed if (state == ACPI_STATE_D0) { 366193323Sed pc->ac_state = ACPI_STATE_D0; 367193323Sed return_ACPI_STATUS(AE_OK); 368193323Sed } 369193323Sed if (state != ACPI_STATE_D3) { 370193323Sed goto bad; 371193323Sed } 372193323Sed 373193323Sed /* turn off the resources listed in _PR0 to go to D3. */ 374198090Srdivacky if (AcpiGetHandle(consumer, "_PR0", &pr0_handle) != AE_OK) { 375198090Srdivacky goto bad; 376193323Sed } 377198090Srdivacky bzero(&reslist_buffer, sizeof(reslist_buffer)); 378198090Srdivacky status = acpi_EvaluateIntoBuffer(pr0_handle, NULL, NULL, &reslist_buffer); 379193323Sed if (status != AE_OK) { 380193323Sed goto bad; 381193323Sed } 382193323Sed reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer; 383193323Sed if ((reslist_object->Type != ACPI_TYPE_PACKAGE) || 384193323Sed (reslist_object->Package.Count == 0)) { 385193323Sed goto bad; 386193323Sed } 387193323Sed AcpiOsFree(reslist_object); 388193323Sed } 389193323Sed 390193323Sed /* 391193323Sed * Check that we can actually fetch the list of power resources 392193323Sed */ 393193323Sed if (reslist_handle != NULL) { 394193323Sed bzero(&reslist_buffer, sizeof(reslist_buffer)); 395193323Sed if ((status = acpi_EvaluateIntoBuffer(reslist_handle, NULL, NULL, &reslist_buffer)) != AE_OK) { 396193323Sed ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't evaluate resource list %s\n", 397193323Sed acpi_name(reslist_handle))); 398193323Sed return_ACPI_STATUS(status); 399193323Sed } 400193323Sed reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer; 401193323Sed if (reslist_object->Type != ACPI_TYPE_PACKAGE) { 402193323Sed ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "resource list is not ACPI_TYPE_PACKAGE (%d)\n", 403193323Sed reslist_object->Type)); 404198090Srdivacky return_ACPI_STATUS(AE_TYPE); 405193323Sed } 406193323Sed } else { 407193323Sed reslist_object = NULL; 408193323Sed } 409193323Sed 410193323Sed /* 411198090Srdivacky * Now we are ready to switch, so kill off any current power resource references. 412193323Sed */ 413193323Sed res_changed = 0; 414193323Sed while((pr = TAILQ_FIRST(&pc->ac_references)) != NULL) { 415193323Sed res_changed = 1; 416193323Sed ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "removing reference to %s\n", acpi_name(pr->ar_resource->ap_resource))); 417193323Sed TAILQ_REMOVE(&pr->ar_resource->ap_references, pr, ar_rlink); 418198090Srdivacky TAILQ_REMOVE(&pc->ac_references, pr, ar_clink); 419193323Sed free(pr, M_ACPIPWR); 420193323Sed } 421193323Sed 422198090Srdivacky /* 423198090Srdivacky * Add new power resource references, if we have any. Traverse the 424193323Sed * package that we got from evaluating reslist_handle, and look up each 425193323Sed * of the resources that are referenced. 426193323Sed */ 427193323Sed if (reslist_object != NULL) { 428193323Sed ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "referencing %d new resources\n", 429193323Sed reslist_object->Package.Count)); 430198090Srdivacky acpi_ForeachPackageObject(reslist_object, acpi_pwr_reference_resource, pc); 431193323Sed res_changed = 1; 432198090Srdivacky } 433193323Sed 434193323Sed /* 435193323Sed * If we changed anything in the resource list, we need to run a switch 436193323Sed * pass now. 437193323Sed */ 438193323Sed if ((status = acpi_pwr_switch_power()) != AE_OK) { 439193323Sed ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to correctly switch resources to move %s to D%d\n", 440193323Sed acpi_name(consumer), state)); 441193323Sed return_ACPI_STATUS(status); /* XXX is this appropriate? Should we return to previous state? */ 442193323Sed } 443193323Sed 444193323Sed /* invoke power state switch method (if present) */ 445193323Sed if (method_handle != NULL) { 446193323Sed ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "invoking state transition method %s\n", 447193323Sed acpi_name(method_handle))); 448193323Sed if ((status = AcpiEvaluateObject(method_handle, NULL, NULL, NULL)) != AE_OK) 449193323Sed pc->ac_state = ACPI_STATE_UNKNOWN; 450193323Sed return_ACPI_STATUS(status); /* XXX is this appropriate? Should we return to previous state? */ 451193323Sed } 452193323Sed 453193323Sed /* transition was successful */ 454193323Sed pc->ac_state = state; 455193323Sed return_ACPI_STATUS(AE_OK); 456193323Sed 457193323Sed bad: 458193323Sed ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "attempt to set unsupported state D%d\n", 459193323Sed state)); 460193323Sed if (reslist_object) 461193323Sed AcpiOsFree(reslist_object); 462193323Sed return_ACPI_STATUS(AE_BAD_PARAMETER); 463193323Sed} 464193323Sed 465193323Sed/* 466193323Sed * Called to create a reference between a power consumer and a power resource 467193323Sed * identified in the object. 468193323Sed */ 469193323Sedstatic void 470193323Sedacpi_pwr_reference_resource(ACPI_OBJECT *obj, void *arg) 471193323Sed{ 472193323Sed struct acpi_powerconsumer *pc = (struct acpi_powerconsumer *)arg; 473193323Sed struct acpi_powerreference *pr; 474193323Sed struct acpi_powerresource *rp; 475193323Sed ACPI_HANDLE res; 476193323Sed ACPI_STATUS status; 477193323Sed 478193323Sed FUNCTION_TRACE(__func__); 479193323Sed 480193323Sed /* check the object type */ 481193323Sed if (obj->Type != ACPI_TYPE_STRING) { 482193323Sed ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "don't know how to create a power reference to object type %d\n", 483193323Sed obj->Type)); 484193323Sed return_VOID; 485193323Sed } 486193323Sed 487193323Sed ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "building reference from %s to %s\n", 488193323Sed acpi_name(pc->ac_consumer), obj->String.Pointer)); 489193323Sed 490193323Sed /* get the handle of the resource */ 491193323Sed if (ACPI_FAILURE(status = AcpiGetHandle(NULL, obj->String.Pointer, &res))) { 492198090Srdivacky ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "couldn't find power resource %s\n", 493198090Srdivacky obj->String.Pointer)); 494193323Sed return_VOID; 495198090Srdivacky } 496198090Srdivacky 497193323Sed /* create/look up the resource */ 498198090Srdivacky if (ACPI_FAILURE(status = acpi_pwr_register_resource(res))) { 499193323Sed ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "couldn't register power resource %s - %s\n", 500193323Sed obj->String.Pointer, AcpiFormatException(status))); 501193323Sed return_VOID; 502193323Sed } 503193323Sed if ((rp = acpi_pwr_find_resource(res)) == NULL) { 504193323Sed ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "power resource list corrupted\n")); 505193323Sed return_VOID; 506198090Srdivacky } 507193323Sed ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "found power resource %s\n", acpi_name(rp->ap_resource))); 508198090Srdivacky 509198090Srdivacky /* create a reference between the consumer and resource */ 510193323Sed if ((pr = malloc(sizeof(*pr), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) { 511193323Sed ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "couldn't allocate memory for a power consumer reference\n")); 512193323Sed return_VOID; 513193323Sed } 514193323Sed pr->ar_consumer = pc; 515193323Sed pr->ar_resource = rp; 516193323Sed TAILQ_INSERT_TAIL(&pc->ac_references, pr, ar_clink); 517193323Sed TAILQ_INSERT_TAIL(&rp->ap_references, pr, ar_rlink); 518193323Sed 519193323Sed return_VOID; 520193323Sed} 521193323Sed 522193323Sed 523193323Sed/* 524198090Srdivacky * Switch power resources to conform to the desired state. 525193323Sed * 526193323Sed * Consumers may have modified the power resource list in an arbitrary 527193323Sed * fashion; we sweep it in sequence order. 528193323Sed */ 529193323Sedstatic ACPI_STATUS 530193323Sedacpi_pwr_switch_power(void) 531193323Sed{ 532193323Sed struct acpi_powerresource *rp; 533193323Sed ACPI_STATUS status; 534193323Sed int cur; 535193323Sed 536193323Sed FUNCTION_TRACE(__func__); 537193323Sed 538193323Sed /* 539193323Sed * Sweep the list forwards turning things on. 540193323Sed */ 541193323Sed TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) { 542198090Srdivacky if (TAILQ_FIRST(&rp->ap_references) == NULL) { 543198090Srdivacky ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s has no references, not turning on\n", 544193323Sed acpi_name(rp->ap_resource))); 545198090Srdivacky continue; 546198090Srdivacky } 547193323Sed 548193323Sed /* we could cache this if we trusted it not to change under us */ 549193323Sed if ((status = acpi_EvaluateInteger(rp->ap_resource, "_STA", &cur)) != AE_OK) { 550193323Sed ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n", 551193323Sed acpi_name(rp->ap_resource), status)); 552193323Sed continue; /* XXX is this correct? Always switch if in doubt? */ 553193323Sed } 554193323Sed 555193323Sed /* 556193323Sed * Switch if required. Note that we ignore the result of the switch 557193323Sed * effort; we don't know what to do if it fails, so checking wouldn't 558193323Sed * help much. 559193323Sed */ 560193323Sed if (cur != ACPI_PWR_ON) { 561193323Sed if (ACPI_FAILURE(status = AcpiEvaluateObject(rp->ap_resource, "_ON", NULL, NULL))) { 562193323Sed ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to switch %s on - %s\n", 563193323Sed acpi_name(rp->ap_resource), AcpiFormatException(status))); 564193323Sed } else { 565193323Sed ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s on\n", acpi_name(rp->ap_resource))); 566193323Sed } 567193323Sed } else { 568193323Sed ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already on\n", acpi_name(rp->ap_resource))); 569193323Sed } 570193323Sed } 571198090Srdivacky 572193323Sed /* 573193323Sed * Sweep the list backwards turning things off. 574193323Sed */ 575193323Sed TAILQ_FOREACH_REVERSE(rp, &acpi_powerresources, acpi_powerresource_list, ap_link) { 576198090Srdivacky if (TAILQ_FIRST(&rp->ap_references) != NULL) { 577193323Sed ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s has references, not turning off\n", 578193323Sed acpi_name(rp->ap_resource))); 579193323Sed continue; 580193323Sed } 581193323Sed 582193323Sed /* we could cache this if we trusted it not to change under us */ 583198090Srdivacky if ((status = acpi_EvaluateInteger(rp->ap_resource, "_STA", &cur)) != AE_OK) { 584193323Sed ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n", 585193323Sed acpi_name(rp->ap_resource), status)); 586193323Sed continue; /* XXX is this correct? Always switch if in doubt? */ 587198090Srdivacky } 588198090Srdivacky 589193323Sed /* 590193323Sed * Switch if required. Note that we ignore the result of the switch 591193323Sed * effort; we don't know what to do if it fails, so checking wouldn't 592193323Sed * help much. 593193323Sed */ 594193323Sed if (cur != ACPI_PWR_OFF) { 595198090Srdivacky if (ACPI_FAILURE(status = AcpiEvaluateObject(rp->ap_resource, "_OFF", NULL, NULL))) { 596198090Srdivacky ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to switch %s off - %s\n", 597193323Sed acpi_name(rp->ap_resource), AcpiFormatException(status))); 598198090Srdivacky } else { 599193323Sed ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s off\n", acpi_name(rp->ap_resource))); 600193323Sed } 601193323Sed } else { 602193323Sed ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already off\n", acpi_name(rp->ap_resource))); 603193323Sed } 604193323Sed } 605193323Sed return_ACPI_STATUS(AE_OK); 606193323Sed} 607198090Srdivacky 608198090Srdivacky/* 609198090Srdivacky * Find a power resource's control structure. 610198090Srdivacky */ 611198090Srdivackystatic struct acpi_powerresource * 612198090Srdivackyacpi_pwr_find_resource(ACPI_HANDLE res) 613198090Srdivacky{ 614198090Srdivacky struct acpi_powerresource *rp; 615198090Srdivacky 616198090Srdivacky FUNCTION_TRACE(__func__); 617198090Srdivacky 618198090Srdivacky TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) 619198090Srdivacky if (rp->ap_resource == res) 620198090Srdivacky break; 621198090Srdivacky return_PTR(rp); 622198090Srdivacky} 623198090Srdivacky 624193323Sed/* 625193323Sed * Find a power consumer's control structure. 626193323Sed */ 627193323Sedstatic struct acpi_powerconsumer * 628193323Sedacpi_pwr_find_consumer(ACPI_HANDLE consumer) 629193323Sed{ 630193323Sed struct acpi_powerconsumer *pc; 631193323Sed 632193323Sed FUNCTION_TRACE(__func__); 633193323Sed 634193323Sed TAILQ_FOREACH(pc, &acpi_powerconsumers, ac_link) 635193323Sed if (pc->ac_consumer == consumer) 636193323Sed break; 637193323Sed return_PTR(pc); 638193323Sed} 639193323Sed 640193323Sed