1*5db2f26eSSascha Wildner /*- 2*5db2f26eSSascha Wildner * Copyright (c) 2001 Michael Smith 3*5db2f26eSSascha Wildner * All rights reserved. 4*5db2f26eSSascha Wildner * 5*5db2f26eSSascha Wildner * Redistribution and use in source and binary forms, with or without 6*5db2f26eSSascha Wildner * modification, are permitted provided that the following conditions 7*5db2f26eSSascha Wildner * are met: 8*5db2f26eSSascha Wildner * 1. Redistributions of source code must retain the above copyright 9*5db2f26eSSascha Wildner * notice, this list of conditions and the following disclaimer. 10*5db2f26eSSascha Wildner * 2. Redistributions in binary form must reproduce the above copyright 11*5db2f26eSSascha Wildner * notice, this list of conditions and the following disclaimer in the 12*5db2f26eSSascha Wildner * documentation and/or other materials provided with the distribution. 13*5db2f26eSSascha Wildner * 14*5db2f26eSSascha Wildner * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15*5db2f26eSSascha Wildner * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16*5db2f26eSSascha Wildner * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17*5db2f26eSSascha Wildner * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18*5db2f26eSSascha Wildner * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19*5db2f26eSSascha Wildner * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20*5db2f26eSSascha Wildner * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21*5db2f26eSSascha Wildner * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22*5db2f26eSSascha Wildner * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23*5db2f26eSSascha Wildner * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24*5db2f26eSSascha Wildner * SUCH DAMAGE. 25*5db2f26eSSascha Wildner * 26*5db2f26eSSascha Wildner * $FreeBSD: src/sys/dev/acpica/acpi_powerres.c,v 1.30.8.1 2009/04/15 03:14:26 kensmith Exp $ 27*5db2f26eSSascha Wildner */ 28*5db2f26eSSascha Wildner 29*5db2f26eSSascha Wildner #include "opt_acpi.h" 30*5db2f26eSSascha Wildner #include <sys/param.h> 31*5db2f26eSSascha Wildner #include <sys/kernel.h> 32*5db2f26eSSascha Wildner #include <sys/malloc.h> 33*5db2f26eSSascha Wildner #include <sys/bus.h> 34*5db2f26eSSascha Wildner 35*5db2f26eSSascha Wildner #include "acpi.h" 36*5db2f26eSSascha Wildner #include <dev/acpica/acpivar.h> 37*5db2f26eSSascha Wildner 38*5db2f26eSSascha Wildner /* 39*5db2f26eSSascha Wildner * ACPI power resource management. 40*5db2f26eSSascha Wildner * 41*5db2f26eSSascha Wildner * Power resource behaviour is slightly complicated by the fact that 42*5db2f26eSSascha Wildner * a single power resource may provide power for more than one device. 43*5db2f26eSSascha Wildner * Thus, we must track the device(s) being powered by a given power 44*5db2f26eSSascha Wildner * resource, and only deactivate it when there are no powered devices. 45*5db2f26eSSascha Wildner * 46*5db2f26eSSascha Wildner * Note that this only manages resources for known devices. There is an 47*5db2f26eSSascha Wildner * ugly case where we may turn of power to a device which is in use because 48*5db2f26eSSascha Wildner * we don't know that it depends on a given resource. We should perhaps 49*5db2f26eSSascha Wildner * try to be smarter about this, but a more complete solution would involve 50*5db2f26eSSascha Wildner * scanning all of the ACPI namespace to find devices we're not currently 51*5db2f26eSSascha Wildner * aware of, and this raises questions about whether they should be left 52*5db2f26eSSascha Wildner * on, turned off, etc. 53*5db2f26eSSascha Wildner */ 54*5db2f26eSSascha Wildner 55*5db2f26eSSascha Wildner MALLOC_DEFINE(M_ACPIPWR, "acpipwr", "ACPI power resources"); 56*5db2f26eSSascha Wildner 57*5db2f26eSSascha Wildner /* Hooks for the ACPI CA debugging infrastructure */ 58*5db2f26eSSascha Wildner #define _COMPONENT ACPI_POWERRES 59*5db2f26eSSascha Wildner ACPI_MODULE_NAME("POWERRES") 60*5db2f26eSSascha Wildner 61*5db2f26eSSascha Wildner /* Return values from _STA on a power resource */ 62*5db2f26eSSascha Wildner #define ACPI_PWR_OFF 0 63*5db2f26eSSascha Wildner #define ACPI_PWR_ON 1 64*5db2f26eSSascha Wildner #define ACPI_PWR_UNK (-1) 65*5db2f26eSSascha Wildner 66*5db2f26eSSascha Wildner /* A relationship between a power resource and a consumer. */ 67*5db2f26eSSascha Wildner struct acpi_powerreference { 68*5db2f26eSSascha Wildner struct acpi_powerconsumer *ar_consumer; 69*5db2f26eSSascha Wildner struct acpi_powerresource *ar_resource; 70*5db2f26eSSascha Wildner TAILQ_ENTRY(acpi_powerreference) ar_rlink; /* link on resource list */ 71*5db2f26eSSascha Wildner TAILQ_ENTRY(acpi_powerreference) ar_clink; /* link on consumer */ 72*5db2f26eSSascha Wildner }; 73*5db2f26eSSascha Wildner 74*5db2f26eSSascha Wildner /* A power-managed device. */ 75*5db2f26eSSascha Wildner struct acpi_powerconsumer { 76*5db2f26eSSascha Wildner /* Device which is powered */ 77*5db2f26eSSascha Wildner ACPI_HANDLE ac_consumer; 78*5db2f26eSSascha Wildner int ac_state; 79*5db2f26eSSascha Wildner TAILQ_ENTRY(acpi_powerconsumer) ac_link; 80*5db2f26eSSascha Wildner TAILQ_HEAD(,acpi_powerreference) ac_references; 81*5db2f26eSSascha Wildner }; 82*5db2f26eSSascha Wildner 83*5db2f26eSSascha Wildner /* A power resource. */ 84*5db2f26eSSascha Wildner struct acpi_powerresource { 85*5db2f26eSSascha Wildner TAILQ_ENTRY(acpi_powerresource) ap_link; 86*5db2f26eSSascha Wildner TAILQ_HEAD(,acpi_powerreference) ap_references; 87*5db2f26eSSascha Wildner ACPI_HANDLE ap_resource; 88*5db2f26eSSascha Wildner ACPI_INTEGER ap_systemlevel; 89*5db2f26eSSascha Wildner ACPI_INTEGER ap_order; 90*5db2f26eSSascha Wildner int ap_state; 91*5db2f26eSSascha Wildner }; 92*5db2f26eSSascha Wildner 93*5db2f26eSSascha Wildner static TAILQ_HEAD(acpi_powerresource_list, acpi_powerresource) 94*5db2f26eSSascha Wildner acpi_powerresources; 95*5db2f26eSSascha Wildner static TAILQ_HEAD(acpi_powerconsumer_list, acpi_powerconsumer) 96*5db2f26eSSascha Wildner acpi_powerconsumers; 97*5db2f26eSSascha Wildner ACPI_SERIAL_DECL(powerres, "ACPI power resources"); 98*5db2f26eSSascha Wildner 99*5db2f26eSSascha Wildner static ACPI_STATUS acpi_pwr_register_consumer(ACPI_HANDLE consumer); 100*5db2f26eSSascha Wildner #ifdef notyet 101*5db2f26eSSascha Wildner static ACPI_STATUS acpi_pwr_deregister_consumer(ACPI_HANDLE consumer); 102*5db2f26eSSascha Wildner #endif /* notyet */ 103*5db2f26eSSascha Wildner static ACPI_STATUS acpi_pwr_register_resource(ACPI_HANDLE res); 104*5db2f26eSSascha Wildner #ifdef notyet 105*5db2f26eSSascha Wildner static ACPI_STATUS acpi_pwr_deregister_resource(ACPI_HANDLE res); 106*5db2f26eSSascha Wildner #endif /* notyet */ 107*5db2f26eSSascha Wildner static void acpi_pwr_reference_resource(ACPI_OBJECT *obj, 108*5db2f26eSSascha Wildner void *arg); 109*5db2f26eSSascha Wildner static int acpi_pwr_dereference_resource(struct acpi_powerconsumer 110*5db2f26eSSascha Wildner *pc); 111*5db2f26eSSascha Wildner static ACPI_STATUS acpi_pwr_switch_power(void); 112*5db2f26eSSascha Wildner static struct acpi_powerresource 113*5db2f26eSSascha Wildner *acpi_pwr_find_resource(ACPI_HANDLE res); 114*5db2f26eSSascha Wildner static struct acpi_powerconsumer 115*5db2f26eSSascha Wildner *acpi_pwr_find_consumer(ACPI_HANDLE consumer); 116*5db2f26eSSascha Wildner 117*5db2f26eSSascha Wildner /* Initialise our lists. */ 118*5db2f26eSSascha Wildner static void 119*5db2f26eSSascha Wildner acpi_pwr_init(void *junk) 120*5db2f26eSSascha Wildner { 121*5db2f26eSSascha Wildner ACPI_SERIAL_INIT(powerres); 122*5db2f26eSSascha Wildner TAILQ_INIT(&acpi_powerresources); 123*5db2f26eSSascha Wildner TAILQ_INIT(&acpi_powerconsumers); 124*5db2f26eSSascha Wildner } 125*5db2f26eSSascha Wildner SYSINIT(acpi_powerresource, SI_BOOT1_TUNABLES, SI_ORDER_ANY, acpi_pwr_init, NULL); 126*5db2f26eSSascha Wildner 127*5db2f26eSSascha Wildner /* 128*5db2f26eSSascha Wildner * Register a power resource. 129*5db2f26eSSascha Wildner * 130*5db2f26eSSascha Wildner * It's OK to call this if we already know about the resource. 131*5db2f26eSSascha Wildner */ 132*5db2f26eSSascha Wildner static ACPI_STATUS 133*5db2f26eSSascha Wildner acpi_pwr_register_resource(ACPI_HANDLE res) 134*5db2f26eSSascha Wildner { 135*5db2f26eSSascha Wildner ACPI_STATUS status; 136*5db2f26eSSascha Wildner ACPI_BUFFER buf; 137*5db2f26eSSascha Wildner ACPI_OBJECT *obj; 138*5db2f26eSSascha Wildner struct acpi_powerresource *rp, *srp; 139*5db2f26eSSascha Wildner 140*5db2f26eSSascha Wildner ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 141*5db2f26eSSascha Wildner ACPI_SERIAL_ASSERT(powerres); 142*5db2f26eSSascha Wildner 143*5db2f26eSSascha Wildner rp = NULL; 144*5db2f26eSSascha Wildner buf.Pointer = NULL; 145*5db2f26eSSascha Wildner 146*5db2f26eSSascha Wildner /* Look to see if we know about this resource */ 147*5db2f26eSSascha Wildner if (acpi_pwr_find_resource(res) != NULL) 148*5db2f26eSSascha Wildner return_ACPI_STATUS (AE_OK); /* already know about it */ 149*5db2f26eSSascha Wildner 150*5db2f26eSSascha Wildner /* Allocate a new resource */ 151*5db2f26eSSascha Wildner if ((rp = kmalloc(sizeof(*rp), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) { 152*5db2f26eSSascha Wildner status = AE_NO_MEMORY; 153*5db2f26eSSascha Wildner goto out; 154*5db2f26eSSascha Wildner } 155*5db2f26eSSascha Wildner TAILQ_INIT(&rp->ap_references); 156*5db2f26eSSascha Wildner rp->ap_resource = res; 157*5db2f26eSSascha Wildner 158*5db2f26eSSascha Wildner /* Get the Power Resource object */ 159*5db2f26eSSascha Wildner buf.Length = ACPI_ALLOCATE_BUFFER; 160*5db2f26eSSascha Wildner if (ACPI_FAILURE(status = AcpiEvaluateObject(res, NULL, NULL, &buf))) { 161*5db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "no power resource object\n")); 162*5db2f26eSSascha Wildner goto out; 163*5db2f26eSSascha Wildner } 164*5db2f26eSSascha Wildner obj = buf.Pointer; 165*5db2f26eSSascha Wildner if (obj->Type != ACPI_TYPE_POWER) { 166*5db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 167*5db2f26eSSascha Wildner "questionable power resource object %s\n", 168*5db2f26eSSascha Wildner acpi_name(res))); 169*5db2f26eSSascha Wildner status = AE_TYPE; 170*5db2f26eSSascha Wildner goto out; 171*5db2f26eSSascha Wildner } 172*5db2f26eSSascha Wildner rp->ap_systemlevel = obj->PowerResource.SystemLevel; 173*5db2f26eSSascha Wildner rp->ap_order = obj->PowerResource.ResourceOrder; 174*5db2f26eSSascha Wildner rp->ap_state = ACPI_PWR_UNK; 175*5db2f26eSSascha Wildner 176*5db2f26eSSascha Wildner /* Sort the resource into the list */ 177*5db2f26eSSascha Wildner status = AE_OK; 178*5db2f26eSSascha Wildner srp = TAILQ_FIRST(&acpi_powerresources); 179*5db2f26eSSascha Wildner if (srp == NULL || rp->ap_order < srp->ap_order) { 180*5db2f26eSSascha Wildner TAILQ_INSERT_HEAD(&acpi_powerresources, rp, ap_link); 181*5db2f26eSSascha Wildner goto done; 182*5db2f26eSSascha Wildner } 183*5db2f26eSSascha Wildner TAILQ_FOREACH(srp, &acpi_powerresources, ap_link) { 184*5db2f26eSSascha Wildner if (rp->ap_order < srp->ap_order) { 185*5db2f26eSSascha Wildner TAILQ_INSERT_BEFORE(srp, rp, ap_link); 186*5db2f26eSSascha Wildner goto done; 187*5db2f26eSSascha Wildner } 188*5db2f26eSSascha Wildner } 189*5db2f26eSSascha Wildner TAILQ_INSERT_TAIL(&acpi_powerresources, rp, ap_link); 190*5db2f26eSSascha Wildner 191*5db2f26eSSascha Wildner done: 192*5db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 193*5db2f26eSSascha Wildner "registered power resource %s\n", acpi_name(res))); 194*5db2f26eSSascha Wildner 195*5db2f26eSSascha Wildner out: 196*5db2f26eSSascha Wildner if (buf.Pointer != NULL) 197*5db2f26eSSascha Wildner AcpiOsFree(buf.Pointer); 198*5db2f26eSSascha Wildner if (ACPI_FAILURE(status) && rp != NULL) 199*5db2f26eSSascha Wildner kfree(rp, M_ACPIPWR); 200*5db2f26eSSascha Wildner return_ACPI_STATUS (status); 201*5db2f26eSSascha Wildner } 202*5db2f26eSSascha Wildner 203*5db2f26eSSascha Wildner #ifdef notyet 204*5db2f26eSSascha Wildner /* 205*5db2f26eSSascha Wildner * Deregister a power resource. 206*5db2f26eSSascha Wildner */ 207*5db2f26eSSascha Wildner static ACPI_STATUS 208*5db2f26eSSascha Wildner acpi_pwr_deregister_resource(ACPI_HANDLE res) 209*5db2f26eSSascha Wildner { 210*5db2f26eSSascha Wildner struct acpi_powerresource *rp; 211*5db2f26eSSascha Wildner 212*5db2f26eSSascha Wildner ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 213*5db2f26eSSascha Wildner ACPI_SERIAL_ASSERT(powerres); 214*5db2f26eSSascha Wildner 215*5db2f26eSSascha Wildner rp = NULL; 216*5db2f26eSSascha Wildner 217*5db2f26eSSascha Wildner /* Find the resource */ 218*5db2f26eSSascha Wildner if ((rp = acpi_pwr_find_resource(res)) == NULL) 219*5db2f26eSSascha Wildner return_ACPI_STATUS (AE_BAD_PARAMETER); 220*5db2f26eSSascha Wildner 221*5db2f26eSSascha Wildner /* Check that there are no consumers referencing this resource */ 222*5db2f26eSSascha Wildner if (TAILQ_FIRST(&rp->ap_references) != NULL) 223*5db2f26eSSascha Wildner return_ACPI_STATUS (AE_BAD_PARAMETER); 224*5db2f26eSSascha Wildner 225*5db2f26eSSascha Wildner /* Pull it off the list and kfree it */ 226*5db2f26eSSascha Wildner TAILQ_REMOVE(&acpi_powerresources, rp, ap_link); 227*5db2f26eSSascha Wildner kfree(rp, M_ACPIPWR); 228*5db2f26eSSascha Wildner 229*5db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power resource %s\n", 230*5db2f26eSSascha Wildner acpi_name(res))); 231*5db2f26eSSascha Wildner 232*5db2f26eSSascha Wildner return_ACPI_STATUS (AE_OK); 233*5db2f26eSSascha Wildner } 234*5db2f26eSSascha Wildner #endif /* notyet */ 235*5db2f26eSSascha Wildner 236*5db2f26eSSascha Wildner /* 237*5db2f26eSSascha Wildner * Register a power consumer. 238*5db2f26eSSascha Wildner * 239*5db2f26eSSascha Wildner * It's OK to call this if we already know about the consumer. 240*5db2f26eSSascha Wildner */ 241*5db2f26eSSascha Wildner static ACPI_STATUS 242*5db2f26eSSascha Wildner acpi_pwr_register_consumer(ACPI_HANDLE consumer) 243*5db2f26eSSascha Wildner { 244*5db2f26eSSascha Wildner struct acpi_powerconsumer *pc; 245*5db2f26eSSascha Wildner 246*5db2f26eSSascha Wildner ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 247*5db2f26eSSascha Wildner ACPI_SERIAL_ASSERT(powerres); 248*5db2f26eSSascha Wildner 249*5db2f26eSSascha Wildner /* Check to see whether we know about this consumer already */ 250*5db2f26eSSascha Wildner if (acpi_pwr_find_consumer(consumer) != NULL) 251*5db2f26eSSascha Wildner return_ACPI_STATUS (AE_OK); 252*5db2f26eSSascha Wildner 253*5db2f26eSSascha Wildner /* Allocate a new power consumer */ 254*5db2f26eSSascha Wildner if ((pc = kmalloc(sizeof(*pc), M_ACPIPWR, M_NOWAIT)) == NULL) 255*5db2f26eSSascha Wildner return_ACPI_STATUS (AE_NO_MEMORY); 256*5db2f26eSSascha Wildner TAILQ_INSERT_HEAD(&acpi_powerconsumers, pc, ac_link); 257*5db2f26eSSascha Wildner TAILQ_INIT(&pc->ac_references); 258*5db2f26eSSascha Wildner pc->ac_consumer = consumer; 259*5db2f26eSSascha Wildner 260*5db2f26eSSascha Wildner /* XXX we should try to find its current state */ 261*5db2f26eSSascha Wildner pc->ac_state = ACPI_STATE_UNKNOWN; 262*5db2f26eSSascha Wildner 263*5db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "registered power consumer %s\n", 264*5db2f26eSSascha Wildner acpi_name(consumer))); 265*5db2f26eSSascha Wildner 266*5db2f26eSSascha Wildner return_ACPI_STATUS (AE_OK); 267*5db2f26eSSascha Wildner } 268*5db2f26eSSascha Wildner 269*5db2f26eSSascha Wildner #ifdef notyet 270*5db2f26eSSascha Wildner /* 271*5db2f26eSSascha Wildner * Deregister a power consumer. 272*5db2f26eSSascha Wildner * 273*5db2f26eSSascha Wildner * This should only be done once the consumer has been powered off. 274*5db2f26eSSascha Wildner * (XXX is this correct? Check once implemented) 275*5db2f26eSSascha Wildner */ 276*5db2f26eSSascha Wildner static ACPI_STATUS 277*5db2f26eSSascha Wildner acpi_pwr_deregister_consumer(ACPI_HANDLE consumer) 278*5db2f26eSSascha Wildner { 279*5db2f26eSSascha Wildner struct acpi_powerconsumer *pc; 280*5db2f26eSSascha Wildner 281*5db2f26eSSascha Wildner ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 282*5db2f26eSSascha Wildner ACPI_SERIAL_ASSERT(powerres); 283*5db2f26eSSascha Wildner 284*5db2f26eSSascha Wildner /* Find the consumer */ 285*5db2f26eSSascha Wildner if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) 286*5db2f26eSSascha Wildner return_ACPI_STATUS (AE_BAD_PARAMETER); 287*5db2f26eSSascha Wildner 288*5db2f26eSSascha Wildner /* Make sure the consumer's not referencing anything right now */ 289*5db2f26eSSascha Wildner if (TAILQ_FIRST(&pc->ac_references) != NULL) 290*5db2f26eSSascha Wildner return_ACPI_STATUS (AE_BAD_PARAMETER); 291*5db2f26eSSascha Wildner 292*5db2f26eSSascha Wildner /* Pull the consumer off the list and kfree it */ 293*5db2f26eSSascha Wildner TAILQ_REMOVE(&acpi_powerconsumers, pc, ac_link); 294*5db2f26eSSascha Wildner kfree(pc, M_ACPIPWR); 295*5db2f26eSSascha Wildner 296*5db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power consumer %s\n", 297*5db2f26eSSascha Wildner acpi_name(consumer))); 298*5db2f26eSSascha Wildner 299*5db2f26eSSascha Wildner return_ACPI_STATUS (AE_OK); 300*5db2f26eSSascha Wildner } 301*5db2f26eSSascha Wildner #endif /* notyet */ 302*5db2f26eSSascha Wildner 303*5db2f26eSSascha Wildner /* 304*5db2f26eSSascha Wildner * Set a power consumer to a particular power state. 305*5db2f26eSSascha Wildner */ 306*5db2f26eSSascha Wildner ACPI_STATUS 307*5db2f26eSSascha Wildner acpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state) 308*5db2f26eSSascha Wildner { 309*5db2f26eSSascha Wildner struct acpi_powerconsumer *pc; 310*5db2f26eSSascha Wildner ACPI_HANDLE method_handle, reslist_handle, pr0_handle; 311*5db2f26eSSascha Wildner ACPI_BUFFER reslist_buffer; 312*5db2f26eSSascha Wildner ACPI_OBJECT *reslist_object; 313*5db2f26eSSascha Wildner ACPI_STATUS status; 314*5db2f26eSSascha Wildner char *method_name, *reslist_name; 315*5db2f26eSSascha Wildner int res_changed; 316*5db2f26eSSascha Wildner 317*5db2f26eSSascha Wildner ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 318*5db2f26eSSascha Wildner 319*5db2f26eSSascha Wildner /* It's never ok to switch a non-existent consumer. */ 320*5db2f26eSSascha Wildner if (consumer == NULL) 321*5db2f26eSSascha Wildner return_ACPI_STATUS (AE_NOT_FOUND); 322*5db2f26eSSascha Wildner reslist_buffer.Pointer = NULL; 323*5db2f26eSSascha Wildner reslist_object = NULL; 324*5db2f26eSSascha Wildner ACPI_SERIAL_BEGIN(powerres); 325*5db2f26eSSascha Wildner 326*5db2f26eSSascha Wildner /* Find the consumer */ 327*5db2f26eSSascha Wildner if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) { 328*5db2f26eSSascha Wildner if (ACPI_FAILURE(status = acpi_pwr_register_consumer(consumer))) 329*5db2f26eSSascha Wildner goto out; 330*5db2f26eSSascha Wildner if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) 331*5db2f26eSSascha Wildner panic("acpi added power consumer but can't find it"); 332*5db2f26eSSascha Wildner } 333*5db2f26eSSascha Wildner 334*5db2f26eSSascha Wildner /* Check for valid transitions. We can only go to D0 from D3. */ 335*5db2f26eSSascha Wildner status = AE_BAD_PARAMETER; 336*5db2f26eSSascha Wildner if (pc->ac_state == ACPI_STATE_D3 && state != ACPI_STATE_D0) 337*5db2f26eSSascha Wildner goto out; 338*5db2f26eSSascha Wildner 339*5db2f26eSSascha Wildner /* Find transition mechanism(s) */ 340*5db2f26eSSascha Wildner switch (state) { 341*5db2f26eSSascha Wildner case ACPI_STATE_D0: 342*5db2f26eSSascha Wildner method_name = "_PS0"; 343*5db2f26eSSascha Wildner reslist_name = "_PR0"; 344*5db2f26eSSascha Wildner break; 345*5db2f26eSSascha Wildner case ACPI_STATE_D1: 346*5db2f26eSSascha Wildner method_name = "_PS1"; 347*5db2f26eSSascha Wildner reslist_name = "_PR1"; 348*5db2f26eSSascha Wildner break; 349*5db2f26eSSascha Wildner case ACPI_STATE_D2: 350*5db2f26eSSascha Wildner method_name = "_PS2"; 351*5db2f26eSSascha Wildner reslist_name = "_PR2"; 352*5db2f26eSSascha Wildner break; 353*5db2f26eSSascha Wildner case ACPI_STATE_D3: 354*5db2f26eSSascha Wildner method_name = "_PS3"; 355*5db2f26eSSascha Wildner reslist_name = "_PR3"; 356*5db2f26eSSascha Wildner break; 357*5db2f26eSSascha Wildner default: 358*5db2f26eSSascha Wildner goto out; 359*5db2f26eSSascha Wildner } 360*5db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "setup to switch %s D%d -> D%d\n", 361*5db2f26eSSascha Wildner acpi_name(consumer), pc->ac_state, state)); 362*5db2f26eSSascha Wildner 363*5db2f26eSSascha Wildner /* 364*5db2f26eSSascha Wildner * Verify that this state is supported, ie. one of method or 365*5db2f26eSSascha Wildner * reslist must be present. We need to do this before we go 366*5db2f26eSSascha Wildner * dereferencing resources (since we might be trying to go to 367*5db2f26eSSascha Wildner * a state we don't support). 368*5db2f26eSSascha Wildner * 369*5db2f26eSSascha Wildner * Note that if any states are supported, the device has to 370*5db2f26eSSascha Wildner * support D0 and D3. It's never an error to try to go to 371*5db2f26eSSascha Wildner * D0. 372*5db2f26eSSascha Wildner */ 373*5db2f26eSSascha Wildner if (ACPI_FAILURE(AcpiGetHandle(consumer, method_name, &method_handle))) 374*5db2f26eSSascha Wildner method_handle = NULL; 375*5db2f26eSSascha Wildner if (ACPI_FAILURE(AcpiGetHandle(consumer, reslist_name, &reslist_handle))) 376*5db2f26eSSascha Wildner reslist_handle = NULL; 377*5db2f26eSSascha Wildner if (reslist_handle == NULL && method_handle == NULL) { 378*5db2f26eSSascha Wildner if (state == ACPI_STATE_D0) { 379*5db2f26eSSascha Wildner pc->ac_state = ACPI_STATE_D0; 380*5db2f26eSSascha Wildner status = AE_OK; 381*5db2f26eSSascha Wildner goto out; 382*5db2f26eSSascha Wildner } 383*5db2f26eSSascha Wildner if (state != ACPI_STATE_D3) { 384*5db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 385*5db2f26eSSascha Wildner "attempt to set unsupported state D%d\n", state)); 386*5db2f26eSSascha Wildner goto out; 387*5db2f26eSSascha Wildner } 388*5db2f26eSSascha Wildner 389*5db2f26eSSascha Wildner /* 390*5db2f26eSSascha Wildner * Turn off the resources listed in _PR0 to go to D3. If there is 391*5db2f26eSSascha Wildner * no _PR0 method, this object doesn't support ACPI power states. 392*5db2f26eSSascha Wildner */ 393*5db2f26eSSascha Wildner if (ACPI_FAILURE(AcpiGetHandle(consumer, "_PR0", &pr0_handle))) { 394*5db2f26eSSascha Wildner status = AE_NOT_FOUND; 395*5db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 396*5db2f26eSSascha Wildner "device missing _PR0 (desired state was D%d)\n", state)); 397*5db2f26eSSascha Wildner goto out; 398*5db2f26eSSascha Wildner } 399*5db2f26eSSascha Wildner reslist_buffer.Length = ACPI_ALLOCATE_BUFFER; 400*5db2f26eSSascha Wildner status = AcpiEvaluateObject(pr0_handle, NULL, NULL, &reslist_buffer); 401*5db2f26eSSascha Wildner if (ACPI_FAILURE(status)) { 402*5db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 403*5db2f26eSSascha Wildner "can't evaluate _PR0 for device %s, state D%d\n", 404*5db2f26eSSascha Wildner acpi_name(consumer), state)); 405*5db2f26eSSascha Wildner goto out; 406*5db2f26eSSascha Wildner } 407*5db2f26eSSascha Wildner reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer; 408*5db2f26eSSascha Wildner if (!ACPI_PKG_VALID(reslist_object, 1)) { 409*5db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 410*5db2f26eSSascha Wildner "invalid package object for state D%d\n", state)); 411*5db2f26eSSascha Wildner status = AE_TYPE; 412*5db2f26eSSascha Wildner goto out; 413*5db2f26eSSascha Wildner } 414*5db2f26eSSascha Wildner AcpiOsFree(reslist_buffer.Pointer); 415*5db2f26eSSascha Wildner reslist_buffer.Pointer = NULL; 416*5db2f26eSSascha Wildner reslist_object = NULL; 417*5db2f26eSSascha Wildner } 418*5db2f26eSSascha Wildner 419*5db2f26eSSascha Wildner /* 420*5db2f26eSSascha Wildner * Check that we can actually fetch the list of power resources 421*5db2f26eSSascha Wildner */ 422*5db2f26eSSascha Wildner if (reslist_handle != NULL) { 423*5db2f26eSSascha Wildner reslist_buffer.Length = ACPI_ALLOCATE_BUFFER; 424*5db2f26eSSascha Wildner status = AcpiEvaluateObject(reslist_handle, NULL, NULL, 425*5db2f26eSSascha Wildner &reslist_buffer); 426*5db2f26eSSascha Wildner if (ACPI_FAILURE(status)) { 427*5db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 428*5db2f26eSSascha Wildner "can't evaluate resource list %s\n", 429*5db2f26eSSascha Wildner acpi_name(reslist_handle))); 430*5db2f26eSSascha Wildner goto out; 431*5db2f26eSSascha Wildner } 432*5db2f26eSSascha Wildner reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer; 433*5db2f26eSSascha Wildner if (reslist_object->Type != ACPI_TYPE_PACKAGE) { 434*5db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 435*5db2f26eSSascha Wildner "resource list is not ACPI_TYPE_PACKAGE (%d)\n", 436*5db2f26eSSascha Wildner reslist_object->Type)); 437*5db2f26eSSascha Wildner status = AE_TYPE; 438*5db2f26eSSascha Wildner goto out; 439*5db2f26eSSascha Wildner } 440*5db2f26eSSascha Wildner } 441*5db2f26eSSascha Wildner 442*5db2f26eSSascha Wildner /* 443*5db2f26eSSascha Wildner * Now we are ready to switch, so kill off any current power 444*5db2f26eSSascha Wildner * resource references. 445*5db2f26eSSascha Wildner */ 446*5db2f26eSSascha Wildner res_changed = acpi_pwr_dereference_resource(pc); 447*5db2f26eSSascha Wildner 448*5db2f26eSSascha Wildner /* 449*5db2f26eSSascha Wildner * Add new power resource references, if we have any. Traverse the 450*5db2f26eSSascha Wildner * package that we got from evaluating reslist_handle, and look up each 451*5db2f26eSSascha Wildner * of the resources that are referenced. 452*5db2f26eSSascha Wildner */ 453*5db2f26eSSascha Wildner if (reslist_object != NULL) { 454*5db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "referencing %d new resources\n", 455*5db2f26eSSascha Wildner reslist_object->Package.Count)); 456*5db2f26eSSascha Wildner acpi_ForeachPackageObject(reslist_object, acpi_pwr_reference_resource, 457*5db2f26eSSascha Wildner pc); 458*5db2f26eSSascha Wildner res_changed = 1; 459*5db2f26eSSascha Wildner } 460*5db2f26eSSascha Wildner 461*5db2f26eSSascha Wildner /* 462*5db2f26eSSascha Wildner * If we changed anything in the resource list, we need to run a switch 463*5db2f26eSSascha Wildner * pass now. 464*5db2f26eSSascha Wildner */ 465*5db2f26eSSascha Wildner if (ACPI_FAILURE(status = acpi_pwr_switch_power())) { 466*5db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 467*5db2f26eSSascha Wildner "failed to switch resources from %s to D%d\n", 468*5db2f26eSSascha Wildner acpi_name(consumer), state)); 469*5db2f26eSSascha Wildner 470*5db2f26eSSascha Wildner /* XXX is this appropriate? Should we return to previous state? */ 471*5db2f26eSSascha Wildner goto out; 472*5db2f26eSSascha Wildner } 473*5db2f26eSSascha Wildner 474*5db2f26eSSascha Wildner /* Invoke power state switch method (if present) */ 475*5db2f26eSSascha Wildner if (method_handle != NULL) { 476*5db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 477*5db2f26eSSascha Wildner "invoking state transition method %s\n", 478*5db2f26eSSascha Wildner acpi_name(method_handle))); 479*5db2f26eSSascha Wildner status = AcpiEvaluateObject(method_handle, NULL, NULL, NULL); 480*5db2f26eSSascha Wildner if (ACPI_FAILURE(status)) { 481*5db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to set state - %s\n", 482*5db2f26eSSascha Wildner AcpiFormatException(status))); 483*5db2f26eSSascha Wildner pc->ac_state = ACPI_STATE_UNKNOWN; 484*5db2f26eSSascha Wildner 485*5db2f26eSSascha Wildner /* XXX Should we return to previous state? */ 486*5db2f26eSSascha Wildner goto out; 487*5db2f26eSSascha Wildner } 488*5db2f26eSSascha Wildner } 489*5db2f26eSSascha Wildner 490*5db2f26eSSascha Wildner /* Transition was successful */ 491*5db2f26eSSascha Wildner pc->ac_state = state; 492*5db2f26eSSascha Wildner status = AE_OK; 493*5db2f26eSSascha Wildner 494*5db2f26eSSascha Wildner out: 495*5db2f26eSSascha Wildner ACPI_SERIAL_END(powerres); 496*5db2f26eSSascha Wildner if (reslist_buffer.Pointer != NULL) 497*5db2f26eSSascha Wildner AcpiOsFree(reslist_buffer.Pointer); 498*5db2f26eSSascha Wildner return_ACPI_STATUS (status); 499*5db2f26eSSascha Wildner } 500*5db2f26eSSascha Wildner 501*5db2f26eSSascha Wildner /* Enable or disable a power resource for wake */ 502*5db2f26eSSascha Wildner ACPI_STATUS 503*5db2f26eSSascha Wildner acpi_pwr_wake_enable(ACPI_HANDLE consumer, int enable) 504*5db2f26eSSascha Wildner { 505*5db2f26eSSascha Wildner ACPI_STATUS status; 506*5db2f26eSSascha Wildner struct acpi_powerconsumer *pc; 507*5db2f26eSSascha Wildner struct acpi_prw_data prw; 508*5db2f26eSSascha Wildner int i; 509*5db2f26eSSascha Wildner 510*5db2f26eSSascha Wildner ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 511*5db2f26eSSascha Wildner 512*5db2f26eSSascha Wildner if (consumer == NULL) 513*5db2f26eSSascha Wildner return (AE_BAD_PARAMETER); 514*5db2f26eSSascha Wildner 515*5db2f26eSSascha Wildner ACPI_SERIAL_BEGIN(powerres); 516*5db2f26eSSascha Wildner if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) { 517*5db2f26eSSascha Wildner if (ACPI_FAILURE(status = acpi_pwr_register_consumer(consumer))) 518*5db2f26eSSascha Wildner goto out; 519*5db2f26eSSascha Wildner if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) 520*5db2f26eSSascha Wildner panic("acpi wake added power consumer but can't find it"); 521*5db2f26eSSascha Wildner } 522*5db2f26eSSascha Wildner 523*5db2f26eSSascha Wildner status = AE_OK; 524*5db2f26eSSascha Wildner if (acpi_parse_prw(consumer, &prw) != 0) 525*5db2f26eSSascha Wildner goto out; 526*5db2f26eSSascha Wildner for (i = 0; i < prw.power_res_count; i++) 527*5db2f26eSSascha Wildner if (enable) 528*5db2f26eSSascha Wildner acpi_pwr_reference_resource(&prw.power_res[i], pc); 529*5db2f26eSSascha Wildner else 530*5db2f26eSSascha Wildner acpi_pwr_dereference_resource(pc); 531*5db2f26eSSascha Wildner 532*5db2f26eSSascha Wildner if (prw.power_res_count > 0) 533*5db2f26eSSascha Wildner acpi_pwr_switch_power(); 534*5db2f26eSSascha Wildner 535*5db2f26eSSascha Wildner out: 536*5db2f26eSSascha Wildner ACPI_SERIAL_END(powerres); 537*5db2f26eSSascha Wildner return (status); 538*5db2f26eSSascha Wildner } 539*5db2f26eSSascha Wildner 540*5db2f26eSSascha Wildner /* 541*5db2f26eSSascha Wildner * Called to create a reference between a power consumer and a power resource 542*5db2f26eSSascha Wildner * identified in the object. 543*5db2f26eSSascha Wildner */ 544*5db2f26eSSascha Wildner static void 545*5db2f26eSSascha Wildner acpi_pwr_reference_resource(ACPI_OBJECT *obj, void *arg) 546*5db2f26eSSascha Wildner { 547*5db2f26eSSascha Wildner struct acpi_powerconsumer *pc = (struct acpi_powerconsumer *)arg; 548*5db2f26eSSascha Wildner struct acpi_powerreference *pr; 549*5db2f26eSSascha Wildner struct acpi_powerresource *rp; 550*5db2f26eSSascha Wildner ACPI_HANDLE res; 551*5db2f26eSSascha Wildner ACPI_STATUS status; 552*5db2f26eSSascha Wildner 553*5db2f26eSSascha Wildner ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 554*5db2f26eSSascha Wildner ACPI_SERIAL_ASSERT(powerres); 555*5db2f26eSSascha Wildner 556*5db2f26eSSascha Wildner res = acpi_GetReference(NULL, obj); 557*5db2f26eSSascha Wildner if (res == NULL) { 558*5db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 559*5db2f26eSSascha Wildner "can't create a power reference for object type %d\n", 560*5db2f26eSSascha Wildner obj->Type)); 561*5db2f26eSSascha Wildner return_VOID; 562*5db2f26eSSascha Wildner } 563*5db2f26eSSascha Wildner 564*5db2f26eSSascha Wildner /* Create/look up the resource */ 565*5db2f26eSSascha Wildner if (ACPI_FAILURE(status = acpi_pwr_register_resource(res))) { 566*5db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 567*5db2f26eSSascha Wildner "couldn't register power resource %s - %s\n", 568*5db2f26eSSascha Wildner obj->String.Pointer, AcpiFormatException(status))); 569*5db2f26eSSascha Wildner return_VOID; 570*5db2f26eSSascha Wildner } 571*5db2f26eSSascha Wildner if ((rp = acpi_pwr_find_resource(res)) == NULL) { 572*5db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "power resource list corrupted\n")); 573*5db2f26eSSascha Wildner return_VOID; 574*5db2f26eSSascha Wildner } 575*5db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "found power resource %s\n", 576*5db2f26eSSascha Wildner acpi_name(rp->ap_resource))); 577*5db2f26eSSascha Wildner 578*5db2f26eSSascha Wildner /* Create a reference between the consumer and resource */ 579*5db2f26eSSascha Wildner if ((pr = kmalloc(sizeof(*pr), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) { 580*5db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 581*5db2f26eSSascha Wildner "allocation failed for a power consumer reference\n")); 582*5db2f26eSSascha Wildner return_VOID; 583*5db2f26eSSascha Wildner } 584*5db2f26eSSascha Wildner pr->ar_consumer = pc; 585*5db2f26eSSascha Wildner pr->ar_resource = rp; 586*5db2f26eSSascha Wildner TAILQ_INSERT_TAIL(&pc->ac_references, pr, ar_clink); 587*5db2f26eSSascha Wildner TAILQ_INSERT_TAIL(&rp->ap_references, pr, ar_rlink); 588*5db2f26eSSascha Wildner 589*5db2f26eSSascha Wildner return_VOID; 590*5db2f26eSSascha Wildner } 591*5db2f26eSSascha Wildner 592*5db2f26eSSascha Wildner static int 593*5db2f26eSSascha Wildner acpi_pwr_dereference_resource(struct acpi_powerconsumer *pc) 594*5db2f26eSSascha Wildner { 595*5db2f26eSSascha Wildner struct acpi_powerreference *pr; 596*5db2f26eSSascha Wildner int changed; 597*5db2f26eSSascha Wildner 598*5db2f26eSSascha Wildner ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 599*5db2f26eSSascha Wildner ACPI_SERIAL_ASSERT(powerres); 600*5db2f26eSSascha Wildner 601*5db2f26eSSascha Wildner changed = 0; 602*5db2f26eSSascha Wildner while ((pr = TAILQ_FIRST(&pc->ac_references)) != NULL) { 603*5db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "removing reference to %s\n", 604*5db2f26eSSascha Wildner acpi_name(pr->ar_resource->ap_resource))); 605*5db2f26eSSascha Wildner TAILQ_REMOVE(&pr->ar_resource->ap_references, pr, ar_rlink); 606*5db2f26eSSascha Wildner TAILQ_REMOVE(&pc->ac_references, pr, ar_clink); 607*5db2f26eSSascha Wildner kfree(pr, M_ACPIPWR); 608*5db2f26eSSascha Wildner changed = 1; 609*5db2f26eSSascha Wildner } 610*5db2f26eSSascha Wildner 611*5db2f26eSSascha Wildner return (changed); 612*5db2f26eSSascha Wildner } 613*5db2f26eSSascha Wildner 614*5db2f26eSSascha Wildner /* 615*5db2f26eSSascha Wildner * Switch power resources to conform to the desired state. 616*5db2f26eSSascha Wildner * 617*5db2f26eSSascha Wildner * Consumers may have modified the power resource list in an arbitrary 618*5db2f26eSSascha Wildner * fashion; we sweep it in sequence order. 619*5db2f26eSSascha Wildner */ 620*5db2f26eSSascha Wildner static ACPI_STATUS 621*5db2f26eSSascha Wildner acpi_pwr_switch_power(void) 622*5db2f26eSSascha Wildner { 623*5db2f26eSSascha Wildner struct acpi_powerresource *rp; 624*5db2f26eSSascha Wildner ACPI_STATUS status; 625*5db2f26eSSascha Wildner int cur; 626*5db2f26eSSascha Wildner 627*5db2f26eSSascha Wildner ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 628*5db2f26eSSascha Wildner ACPI_SERIAL_ASSERT(powerres); 629*5db2f26eSSascha Wildner 630*5db2f26eSSascha Wildner /* 631*5db2f26eSSascha Wildner * Sweep the list forwards turning things on. 632*5db2f26eSSascha Wildner */ 633*5db2f26eSSascha Wildner TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) { 634*5db2f26eSSascha Wildner if (TAILQ_FIRST(&rp->ap_references) == NULL) { 635*5db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 636*5db2f26eSSascha Wildner "%s has no references, not turning on\n", 637*5db2f26eSSascha Wildner acpi_name(rp->ap_resource))); 638*5db2f26eSSascha Wildner continue; 639*5db2f26eSSascha Wildner } 640*5db2f26eSSascha Wildner 641*5db2f26eSSascha Wildner /* We could cache this if we trusted it not to change under us */ 642*5db2f26eSSascha Wildner status = acpi_GetInteger(rp->ap_resource, "_STA", &cur); 643*5db2f26eSSascha Wildner if (ACPI_FAILURE(status)) { 644*5db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n", 645*5db2f26eSSascha Wildner acpi_name(rp->ap_resource), status)); 646*5db2f26eSSascha Wildner /* XXX is this correct? Always switch if in doubt? */ 647*5db2f26eSSascha Wildner continue; 648*5db2f26eSSascha Wildner } else if (rp->ap_state == ACPI_PWR_UNK) 649*5db2f26eSSascha Wildner rp->ap_state = cur; 650*5db2f26eSSascha Wildner 651*5db2f26eSSascha Wildner /* 652*5db2f26eSSascha Wildner * Switch if required. Note that we ignore the result of the switch 653*5db2f26eSSascha Wildner * effort; we don't know what to do if it fails, so checking wouldn't 654*5db2f26eSSascha Wildner * help much. 655*5db2f26eSSascha Wildner */ 656*5db2f26eSSascha Wildner if (rp->ap_state != ACPI_PWR_ON) { 657*5db2f26eSSascha Wildner status = AcpiEvaluateObject(rp->ap_resource, "_ON", NULL, NULL); 658*5db2f26eSSascha Wildner if (ACPI_FAILURE(status)) { 659*5db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 660*5db2f26eSSascha Wildner "failed to switch %s on - %s\n", 661*5db2f26eSSascha Wildner acpi_name(rp->ap_resource), 662*5db2f26eSSascha Wildner AcpiFormatException(status))); 663*5db2f26eSSascha Wildner } else { 664*5db2f26eSSascha Wildner rp->ap_state = ACPI_PWR_ON; 665*5db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s on\n", 666*5db2f26eSSascha Wildner acpi_name(rp->ap_resource))); 667*5db2f26eSSascha Wildner } 668*5db2f26eSSascha Wildner } else { 669*5db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already on\n", 670*5db2f26eSSascha Wildner acpi_name(rp->ap_resource))); 671*5db2f26eSSascha Wildner } 672*5db2f26eSSascha Wildner } 673*5db2f26eSSascha Wildner 674*5db2f26eSSascha Wildner /* Sweep the list backwards turning things off. */ 675*5db2f26eSSascha Wildner TAILQ_FOREACH_REVERSE(rp, &acpi_powerresources, acpi_powerresource_list, 676*5db2f26eSSascha Wildner ap_link) { 677*5db2f26eSSascha Wildner 678*5db2f26eSSascha Wildner if (TAILQ_FIRST(&rp->ap_references) != NULL) { 679*5db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 680*5db2f26eSSascha Wildner "%s has references, not turning off\n", 681*5db2f26eSSascha Wildner acpi_name(rp->ap_resource))); 682*5db2f26eSSascha Wildner continue; 683*5db2f26eSSascha Wildner } 684*5db2f26eSSascha Wildner 685*5db2f26eSSascha Wildner /* We could cache this if we trusted it not to change under us */ 686*5db2f26eSSascha Wildner status = acpi_GetInteger(rp->ap_resource, "_STA", &cur); 687*5db2f26eSSascha Wildner if (ACPI_FAILURE(status)) { 688*5db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n", 689*5db2f26eSSascha Wildner acpi_name(rp->ap_resource), status)); 690*5db2f26eSSascha Wildner /* XXX is this correct? Always switch if in doubt? */ 691*5db2f26eSSascha Wildner continue; 692*5db2f26eSSascha Wildner } else if (rp->ap_state == ACPI_PWR_UNK) 693*5db2f26eSSascha Wildner rp->ap_state = cur; 694*5db2f26eSSascha Wildner 695*5db2f26eSSascha Wildner /* 696*5db2f26eSSascha Wildner * Switch if required. Note that we ignore the result of the switch 697*5db2f26eSSascha Wildner * effort; we don't know what to do if it fails, so checking wouldn't 698*5db2f26eSSascha Wildner * help much. 699*5db2f26eSSascha Wildner */ 700*5db2f26eSSascha Wildner if (rp->ap_state != ACPI_PWR_OFF) { 701*5db2f26eSSascha Wildner status = AcpiEvaluateObject(rp->ap_resource, "_OFF", NULL, NULL); 702*5db2f26eSSascha Wildner if (ACPI_FAILURE(status)) { 703*5db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 704*5db2f26eSSascha Wildner "failed to switch %s off - %s\n", 705*5db2f26eSSascha Wildner acpi_name(rp->ap_resource), 706*5db2f26eSSascha Wildner AcpiFormatException(status))); 707*5db2f26eSSascha Wildner } else { 708*5db2f26eSSascha Wildner rp->ap_state = ACPI_PWR_OFF; 709*5db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s off\n", 710*5db2f26eSSascha Wildner acpi_name(rp->ap_resource))); 711*5db2f26eSSascha Wildner } 712*5db2f26eSSascha Wildner } else { 713*5db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already off\n", 714*5db2f26eSSascha Wildner acpi_name(rp->ap_resource))); 715*5db2f26eSSascha Wildner } 716*5db2f26eSSascha Wildner } 717*5db2f26eSSascha Wildner 718*5db2f26eSSascha Wildner return_ACPI_STATUS (AE_OK); 719*5db2f26eSSascha Wildner } 720*5db2f26eSSascha Wildner 721*5db2f26eSSascha Wildner /* 722*5db2f26eSSascha Wildner * Find a power resource's control structure. 723*5db2f26eSSascha Wildner */ 724*5db2f26eSSascha Wildner static struct acpi_powerresource * 725*5db2f26eSSascha Wildner acpi_pwr_find_resource(ACPI_HANDLE res) 726*5db2f26eSSascha Wildner { 727*5db2f26eSSascha Wildner struct acpi_powerresource *rp; 728*5db2f26eSSascha Wildner 729*5db2f26eSSascha Wildner ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 730*5db2f26eSSascha Wildner ACPI_SERIAL_ASSERT(powerres); 731*5db2f26eSSascha Wildner 732*5db2f26eSSascha Wildner TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) { 733*5db2f26eSSascha Wildner if (rp->ap_resource == res) 734*5db2f26eSSascha Wildner break; 735*5db2f26eSSascha Wildner } 736*5db2f26eSSascha Wildner 737*5db2f26eSSascha Wildner return_PTR (rp); 738*5db2f26eSSascha Wildner } 739*5db2f26eSSascha Wildner 740*5db2f26eSSascha Wildner /* 741*5db2f26eSSascha Wildner * Find a power consumer's control structure. 742*5db2f26eSSascha Wildner */ 743*5db2f26eSSascha Wildner static struct acpi_powerconsumer * 744*5db2f26eSSascha Wildner acpi_pwr_find_consumer(ACPI_HANDLE consumer) 745*5db2f26eSSascha Wildner { 746*5db2f26eSSascha Wildner struct acpi_powerconsumer *pc; 747*5db2f26eSSascha Wildner 748*5db2f26eSSascha Wildner ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 749*5db2f26eSSascha Wildner ACPI_SERIAL_ASSERT(powerres); 750*5db2f26eSSascha Wildner 751*5db2f26eSSascha Wildner TAILQ_FOREACH(pc, &acpi_powerconsumers, ac_link) { 752*5db2f26eSSascha Wildner if (pc->ac_consumer == consumer) 753*5db2f26eSSascha Wildner break; 754*5db2f26eSSascha Wildner } 755*5db2f26eSSascha Wildner 756*5db2f26eSSascha Wildner return_PTR (pc); 757*5db2f26eSSascha Wildner } 758