15db2f26eSSascha Wildner /*- 25db2f26eSSascha Wildner * Copyright (c) 2001 Michael Smith 35db2f26eSSascha Wildner * All rights reserved. 45db2f26eSSascha Wildner * 55db2f26eSSascha Wildner * Redistribution and use in source and binary forms, with or without 65db2f26eSSascha Wildner * modification, are permitted provided that the following conditions 75db2f26eSSascha Wildner * are met: 85db2f26eSSascha Wildner * 1. Redistributions of source code must retain the above copyright 95db2f26eSSascha Wildner * notice, this list of conditions and the following disclaimer. 105db2f26eSSascha Wildner * 2. Redistributions in binary form must reproduce the above copyright 115db2f26eSSascha Wildner * notice, this list of conditions and the following disclaimer in the 125db2f26eSSascha Wildner * documentation and/or other materials provided with the distribution. 135db2f26eSSascha Wildner * 145db2f26eSSascha Wildner * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 155db2f26eSSascha Wildner * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 165db2f26eSSascha Wildner * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 175db2f26eSSascha Wildner * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 185db2f26eSSascha Wildner * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 195db2f26eSSascha Wildner * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 205db2f26eSSascha Wildner * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 215db2f26eSSascha Wildner * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 225db2f26eSSascha Wildner * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 235db2f26eSSascha Wildner * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 245db2f26eSSascha Wildner * SUCH DAMAGE. 255db2f26eSSascha Wildner * 265db2f26eSSascha Wildner * $FreeBSD: src/sys/dev/acpica/acpi_powerres.c,v 1.30.8.1 2009/04/15 03:14:26 kensmith Exp $ 275db2f26eSSascha Wildner */ 285db2f26eSSascha Wildner 295db2f26eSSascha Wildner #include "opt_acpi.h" 305db2f26eSSascha Wildner #include <sys/param.h> 315db2f26eSSascha Wildner #include <sys/kernel.h> 325db2f26eSSascha Wildner #include <sys/malloc.h> 335db2f26eSSascha Wildner #include <sys/bus.h> 345db2f26eSSascha Wildner 355db2f26eSSascha Wildner #include "acpi.h" 365db2f26eSSascha Wildner #include <dev/acpica/acpivar.h> 375db2f26eSSascha Wildner 385db2f26eSSascha Wildner /* 395db2f26eSSascha Wildner * ACPI power resource management. 405db2f26eSSascha Wildner * 415db2f26eSSascha Wildner * Power resource behaviour is slightly complicated by the fact that 425db2f26eSSascha Wildner * a single power resource may provide power for more than one device. 435db2f26eSSascha Wildner * Thus, we must track the device(s) being powered by a given power 445db2f26eSSascha Wildner * resource, and only deactivate it when there are no powered devices. 455db2f26eSSascha Wildner * 465db2f26eSSascha Wildner * Note that this only manages resources for known devices. There is an 475db2f26eSSascha Wildner * ugly case where we may turn of power to a device which is in use because 485db2f26eSSascha Wildner * we don't know that it depends on a given resource. We should perhaps 495db2f26eSSascha Wildner * try to be smarter about this, but a more complete solution would involve 505db2f26eSSascha Wildner * scanning all of the ACPI namespace to find devices we're not currently 515db2f26eSSascha Wildner * aware of, and this raises questions about whether they should be left 525db2f26eSSascha Wildner * on, turned off, etc. 535db2f26eSSascha Wildner */ 545db2f26eSSascha Wildner 555db2f26eSSascha Wildner MALLOC_DEFINE(M_ACPIPWR, "acpipwr", "ACPI power resources"); 565db2f26eSSascha Wildner 575db2f26eSSascha Wildner /* Hooks for the ACPI CA debugging infrastructure */ 585db2f26eSSascha Wildner #define _COMPONENT ACPI_POWERRES 595db2f26eSSascha Wildner ACPI_MODULE_NAME("POWERRES") 605db2f26eSSascha Wildner 615db2f26eSSascha Wildner /* Return values from _STA on a power resource */ 625db2f26eSSascha Wildner #define ACPI_PWR_OFF 0 635db2f26eSSascha Wildner #define ACPI_PWR_ON 1 645db2f26eSSascha Wildner #define ACPI_PWR_UNK (-1) 655db2f26eSSascha Wildner 665db2f26eSSascha Wildner /* A relationship between a power resource and a consumer. */ 675db2f26eSSascha Wildner struct acpi_powerreference { 685db2f26eSSascha Wildner struct acpi_powerconsumer *ar_consumer; 695db2f26eSSascha Wildner struct acpi_powerresource *ar_resource; 705db2f26eSSascha Wildner TAILQ_ENTRY(acpi_powerreference) ar_rlink; /* link on resource list */ 715db2f26eSSascha Wildner TAILQ_ENTRY(acpi_powerreference) ar_clink; /* link on consumer */ 725db2f26eSSascha Wildner }; 735db2f26eSSascha Wildner 745db2f26eSSascha Wildner /* A power-managed device. */ 755db2f26eSSascha Wildner struct acpi_powerconsumer { 765db2f26eSSascha Wildner /* Device which is powered */ 775db2f26eSSascha Wildner ACPI_HANDLE ac_consumer; 785db2f26eSSascha Wildner int ac_state; 795db2f26eSSascha Wildner TAILQ_ENTRY(acpi_powerconsumer) ac_link; 805db2f26eSSascha Wildner TAILQ_HEAD(,acpi_powerreference) ac_references; 815db2f26eSSascha Wildner }; 825db2f26eSSascha Wildner 835db2f26eSSascha Wildner /* A power resource. */ 845db2f26eSSascha Wildner struct acpi_powerresource { 855db2f26eSSascha Wildner TAILQ_ENTRY(acpi_powerresource) ap_link; 865db2f26eSSascha Wildner TAILQ_HEAD(,acpi_powerreference) ap_references; 875db2f26eSSascha Wildner ACPI_HANDLE ap_resource; 88*417dc5a4SSascha Wildner UINT64 ap_systemlevel; 89*417dc5a4SSascha Wildner UINT64 ap_order; 905db2f26eSSascha Wildner int ap_state; 915db2f26eSSascha Wildner }; 925db2f26eSSascha Wildner 935db2f26eSSascha Wildner static TAILQ_HEAD(acpi_powerresource_list, acpi_powerresource) 945db2f26eSSascha Wildner acpi_powerresources; 955db2f26eSSascha Wildner static TAILQ_HEAD(acpi_powerconsumer_list, acpi_powerconsumer) 965db2f26eSSascha Wildner acpi_powerconsumers; 975db2f26eSSascha Wildner ACPI_SERIAL_DECL(powerres, "ACPI power resources"); 985db2f26eSSascha Wildner 995db2f26eSSascha Wildner static ACPI_STATUS acpi_pwr_register_consumer(ACPI_HANDLE consumer); 1005db2f26eSSascha Wildner #ifdef notyet 1015db2f26eSSascha Wildner static ACPI_STATUS acpi_pwr_deregister_consumer(ACPI_HANDLE consumer); 1025db2f26eSSascha Wildner #endif /* notyet */ 1035db2f26eSSascha Wildner static ACPI_STATUS acpi_pwr_register_resource(ACPI_HANDLE res); 1045db2f26eSSascha Wildner #ifdef notyet 1055db2f26eSSascha Wildner static ACPI_STATUS acpi_pwr_deregister_resource(ACPI_HANDLE res); 1065db2f26eSSascha Wildner #endif /* notyet */ 1075db2f26eSSascha Wildner static void acpi_pwr_reference_resource(ACPI_OBJECT *obj, 1085db2f26eSSascha Wildner void *arg); 1095db2f26eSSascha Wildner static int acpi_pwr_dereference_resource(struct acpi_powerconsumer 1105db2f26eSSascha Wildner *pc); 1115db2f26eSSascha Wildner static ACPI_STATUS acpi_pwr_switch_power(void); 1125db2f26eSSascha Wildner static struct acpi_powerresource 1135db2f26eSSascha Wildner *acpi_pwr_find_resource(ACPI_HANDLE res); 1145db2f26eSSascha Wildner static struct acpi_powerconsumer 1155db2f26eSSascha Wildner *acpi_pwr_find_consumer(ACPI_HANDLE consumer); 1165db2f26eSSascha Wildner 1175db2f26eSSascha Wildner /* Initialise our lists. */ 1185db2f26eSSascha Wildner static void 1195db2f26eSSascha Wildner acpi_pwr_init(void *junk) 1205db2f26eSSascha Wildner { 1215db2f26eSSascha Wildner ACPI_SERIAL_INIT(powerres); 1225db2f26eSSascha Wildner TAILQ_INIT(&acpi_powerresources); 1235db2f26eSSascha Wildner TAILQ_INIT(&acpi_powerconsumers); 1245db2f26eSSascha Wildner } 1255db2f26eSSascha Wildner SYSINIT(acpi_powerresource, SI_BOOT1_TUNABLES, SI_ORDER_ANY, acpi_pwr_init, NULL); 1265db2f26eSSascha Wildner 1275db2f26eSSascha Wildner /* 1285db2f26eSSascha Wildner * Register a power resource. 1295db2f26eSSascha Wildner * 1305db2f26eSSascha Wildner * It's OK to call this if we already know about the resource. 1315db2f26eSSascha Wildner */ 1325db2f26eSSascha Wildner static ACPI_STATUS 1335db2f26eSSascha Wildner acpi_pwr_register_resource(ACPI_HANDLE res) 1345db2f26eSSascha Wildner { 1355db2f26eSSascha Wildner ACPI_STATUS status; 1365db2f26eSSascha Wildner ACPI_BUFFER buf; 1375db2f26eSSascha Wildner ACPI_OBJECT *obj; 1385db2f26eSSascha Wildner struct acpi_powerresource *rp, *srp; 1395db2f26eSSascha Wildner 1405db2f26eSSascha Wildner ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1415db2f26eSSascha Wildner ACPI_SERIAL_ASSERT(powerres); 1425db2f26eSSascha Wildner 1435db2f26eSSascha Wildner rp = NULL; 1445db2f26eSSascha Wildner buf.Pointer = NULL; 1455db2f26eSSascha Wildner 1465db2f26eSSascha Wildner /* Look to see if we know about this resource */ 1475db2f26eSSascha Wildner if (acpi_pwr_find_resource(res) != NULL) 1485db2f26eSSascha Wildner return_ACPI_STATUS (AE_OK); /* already know about it */ 1495db2f26eSSascha Wildner 1505db2f26eSSascha Wildner /* Allocate a new resource */ 1515db2f26eSSascha Wildner if ((rp = kmalloc(sizeof(*rp), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) { 1525db2f26eSSascha Wildner status = AE_NO_MEMORY; 1535db2f26eSSascha Wildner goto out; 1545db2f26eSSascha Wildner } 1555db2f26eSSascha Wildner TAILQ_INIT(&rp->ap_references); 1565db2f26eSSascha Wildner rp->ap_resource = res; 1575db2f26eSSascha Wildner 1585db2f26eSSascha Wildner /* Get the Power Resource object */ 1595db2f26eSSascha Wildner buf.Length = ACPI_ALLOCATE_BUFFER; 1605db2f26eSSascha Wildner if (ACPI_FAILURE(status = AcpiEvaluateObject(res, NULL, NULL, &buf))) { 1615db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "no power resource object\n")); 1625db2f26eSSascha Wildner goto out; 1635db2f26eSSascha Wildner } 1645db2f26eSSascha Wildner obj = buf.Pointer; 1655db2f26eSSascha Wildner if (obj->Type != ACPI_TYPE_POWER) { 1665db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 1675db2f26eSSascha Wildner "questionable power resource object %s\n", 1685db2f26eSSascha Wildner acpi_name(res))); 1695db2f26eSSascha Wildner status = AE_TYPE; 1705db2f26eSSascha Wildner goto out; 1715db2f26eSSascha Wildner } 1725db2f26eSSascha Wildner rp->ap_systemlevel = obj->PowerResource.SystemLevel; 1735db2f26eSSascha Wildner rp->ap_order = obj->PowerResource.ResourceOrder; 1745db2f26eSSascha Wildner rp->ap_state = ACPI_PWR_UNK; 1755db2f26eSSascha Wildner 1765db2f26eSSascha Wildner /* Sort the resource into the list */ 1775db2f26eSSascha Wildner status = AE_OK; 1785db2f26eSSascha Wildner srp = TAILQ_FIRST(&acpi_powerresources); 1795db2f26eSSascha Wildner if (srp == NULL || rp->ap_order < srp->ap_order) { 1805db2f26eSSascha Wildner TAILQ_INSERT_HEAD(&acpi_powerresources, rp, ap_link); 1815db2f26eSSascha Wildner goto done; 1825db2f26eSSascha Wildner } 1835db2f26eSSascha Wildner TAILQ_FOREACH(srp, &acpi_powerresources, ap_link) { 1845db2f26eSSascha Wildner if (rp->ap_order < srp->ap_order) { 1855db2f26eSSascha Wildner TAILQ_INSERT_BEFORE(srp, rp, ap_link); 1865db2f26eSSascha Wildner goto done; 1875db2f26eSSascha Wildner } 1885db2f26eSSascha Wildner } 1895db2f26eSSascha Wildner TAILQ_INSERT_TAIL(&acpi_powerresources, rp, ap_link); 1905db2f26eSSascha Wildner 1915db2f26eSSascha Wildner done: 1925db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 1935db2f26eSSascha Wildner "registered power resource %s\n", acpi_name(res))); 1945db2f26eSSascha Wildner 1955db2f26eSSascha Wildner out: 1965db2f26eSSascha Wildner if (buf.Pointer != NULL) 1975db2f26eSSascha Wildner AcpiOsFree(buf.Pointer); 1985db2f26eSSascha Wildner if (ACPI_FAILURE(status) && rp != NULL) 1995db2f26eSSascha Wildner kfree(rp, M_ACPIPWR); 2005db2f26eSSascha Wildner return_ACPI_STATUS (status); 2015db2f26eSSascha Wildner } 2025db2f26eSSascha Wildner 2035db2f26eSSascha Wildner #ifdef notyet 2045db2f26eSSascha Wildner /* 2055db2f26eSSascha Wildner * Deregister a power resource. 2065db2f26eSSascha Wildner */ 2075db2f26eSSascha Wildner static ACPI_STATUS 2085db2f26eSSascha Wildner acpi_pwr_deregister_resource(ACPI_HANDLE res) 2095db2f26eSSascha Wildner { 2105db2f26eSSascha Wildner struct acpi_powerresource *rp; 2115db2f26eSSascha Wildner 2125db2f26eSSascha Wildner ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 2135db2f26eSSascha Wildner ACPI_SERIAL_ASSERT(powerres); 2145db2f26eSSascha Wildner 2155db2f26eSSascha Wildner rp = NULL; 2165db2f26eSSascha Wildner 2175db2f26eSSascha Wildner /* Find the resource */ 2185db2f26eSSascha Wildner if ((rp = acpi_pwr_find_resource(res)) == NULL) 2195db2f26eSSascha Wildner return_ACPI_STATUS (AE_BAD_PARAMETER); 2205db2f26eSSascha Wildner 2215db2f26eSSascha Wildner /* Check that there are no consumers referencing this resource */ 2225db2f26eSSascha Wildner if (TAILQ_FIRST(&rp->ap_references) != NULL) 2235db2f26eSSascha Wildner return_ACPI_STATUS (AE_BAD_PARAMETER); 2245db2f26eSSascha Wildner 2255db2f26eSSascha Wildner /* Pull it off the list and kfree it */ 2265db2f26eSSascha Wildner TAILQ_REMOVE(&acpi_powerresources, rp, ap_link); 2275db2f26eSSascha Wildner kfree(rp, M_ACPIPWR); 2285db2f26eSSascha Wildner 2295db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power resource %s\n", 2305db2f26eSSascha Wildner acpi_name(res))); 2315db2f26eSSascha Wildner 2325db2f26eSSascha Wildner return_ACPI_STATUS (AE_OK); 2335db2f26eSSascha Wildner } 2345db2f26eSSascha Wildner #endif /* notyet */ 2355db2f26eSSascha Wildner 2365db2f26eSSascha Wildner /* 2375db2f26eSSascha Wildner * Register a power consumer. 2385db2f26eSSascha Wildner * 2395db2f26eSSascha Wildner * It's OK to call this if we already know about the consumer. 2405db2f26eSSascha Wildner */ 2415db2f26eSSascha Wildner static ACPI_STATUS 2425db2f26eSSascha Wildner acpi_pwr_register_consumer(ACPI_HANDLE consumer) 2435db2f26eSSascha Wildner { 2445db2f26eSSascha Wildner struct acpi_powerconsumer *pc; 2455db2f26eSSascha Wildner 2465db2f26eSSascha Wildner ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 2475db2f26eSSascha Wildner ACPI_SERIAL_ASSERT(powerres); 2485db2f26eSSascha Wildner 2495db2f26eSSascha Wildner /* Check to see whether we know about this consumer already */ 2505db2f26eSSascha Wildner if (acpi_pwr_find_consumer(consumer) != NULL) 2515db2f26eSSascha Wildner return_ACPI_STATUS (AE_OK); 2525db2f26eSSascha Wildner 2535db2f26eSSascha Wildner /* Allocate a new power consumer */ 2545db2f26eSSascha Wildner if ((pc = kmalloc(sizeof(*pc), M_ACPIPWR, M_NOWAIT)) == NULL) 2555db2f26eSSascha Wildner return_ACPI_STATUS (AE_NO_MEMORY); 2565db2f26eSSascha Wildner TAILQ_INSERT_HEAD(&acpi_powerconsumers, pc, ac_link); 2575db2f26eSSascha Wildner TAILQ_INIT(&pc->ac_references); 2585db2f26eSSascha Wildner pc->ac_consumer = consumer; 2595db2f26eSSascha Wildner 2605db2f26eSSascha Wildner /* XXX we should try to find its current state */ 2615db2f26eSSascha Wildner pc->ac_state = ACPI_STATE_UNKNOWN; 2625db2f26eSSascha Wildner 2635db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "registered power consumer %s\n", 2645db2f26eSSascha Wildner acpi_name(consumer))); 2655db2f26eSSascha Wildner 2665db2f26eSSascha Wildner return_ACPI_STATUS (AE_OK); 2675db2f26eSSascha Wildner } 2685db2f26eSSascha Wildner 2695db2f26eSSascha Wildner #ifdef notyet 2705db2f26eSSascha Wildner /* 2715db2f26eSSascha Wildner * Deregister a power consumer. 2725db2f26eSSascha Wildner * 2735db2f26eSSascha Wildner * This should only be done once the consumer has been powered off. 2745db2f26eSSascha Wildner * (XXX is this correct? Check once implemented) 2755db2f26eSSascha Wildner */ 2765db2f26eSSascha Wildner static ACPI_STATUS 2775db2f26eSSascha Wildner acpi_pwr_deregister_consumer(ACPI_HANDLE consumer) 2785db2f26eSSascha Wildner { 2795db2f26eSSascha Wildner struct acpi_powerconsumer *pc; 2805db2f26eSSascha Wildner 2815db2f26eSSascha Wildner ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 2825db2f26eSSascha Wildner ACPI_SERIAL_ASSERT(powerres); 2835db2f26eSSascha Wildner 2845db2f26eSSascha Wildner /* Find the consumer */ 2855db2f26eSSascha Wildner if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) 2865db2f26eSSascha Wildner return_ACPI_STATUS (AE_BAD_PARAMETER); 2875db2f26eSSascha Wildner 2885db2f26eSSascha Wildner /* Make sure the consumer's not referencing anything right now */ 2895db2f26eSSascha Wildner if (TAILQ_FIRST(&pc->ac_references) != NULL) 2905db2f26eSSascha Wildner return_ACPI_STATUS (AE_BAD_PARAMETER); 2915db2f26eSSascha Wildner 2925db2f26eSSascha Wildner /* Pull the consumer off the list and kfree it */ 2935db2f26eSSascha Wildner TAILQ_REMOVE(&acpi_powerconsumers, pc, ac_link); 2945db2f26eSSascha Wildner kfree(pc, M_ACPIPWR); 2955db2f26eSSascha Wildner 2965db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power consumer %s\n", 2975db2f26eSSascha Wildner acpi_name(consumer))); 2985db2f26eSSascha Wildner 2995db2f26eSSascha Wildner return_ACPI_STATUS (AE_OK); 3005db2f26eSSascha Wildner } 3015db2f26eSSascha Wildner #endif /* notyet */ 3025db2f26eSSascha Wildner 3035db2f26eSSascha Wildner /* 3045db2f26eSSascha Wildner * Set a power consumer to a particular power state. 3055db2f26eSSascha Wildner */ 3065db2f26eSSascha Wildner ACPI_STATUS 3075db2f26eSSascha Wildner acpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state) 3085db2f26eSSascha Wildner { 3095db2f26eSSascha Wildner struct acpi_powerconsumer *pc; 3105db2f26eSSascha Wildner ACPI_HANDLE method_handle, reslist_handle, pr0_handle; 3115db2f26eSSascha Wildner ACPI_BUFFER reslist_buffer; 3125db2f26eSSascha Wildner ACPI_OBJECT *reslist_object; 3135db2f26eSSascha Wildner ACPI_STATUS status; 3145db2f26eSSascha Wildner char *method_name, *reslist_name; 3155db2f26eSSascha Wildner 3165db2f26eSSascha Wildner ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 3175db2f26eSSascha Wildner 3185db2f26eSSascha Wildner /* It's never ok to switch a non-existent consumer. */ 3195db2f26eSSascha Wildner if (consumer == NULL) 3205db2f26eSSascha Wildner return_ACPI_STATUS (AE_NOT_FOUND); 3215db2f26eSSascha Wildner reslist_buffer.Pointer = NULL; 3225db2f26eSSascha Wildner reslist_object = NULL; 3235db2f26eSSascha Wildner ACPI_SERIAL_BEGIN(powerres); 3245db2f26eSSascha Wildner 3255db2f26eSSascha Wildner /* Find the consumer */ 3265db2f26eSSascha Wildner if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) { 3275db2f26eSSascha Wildner if (ACPI_FAILURE(status = acpi_pwr_register_consumer(consumer))) 3285db2f26eSSascha Wildner goto out; 3295db2f26eSSascha Wildner if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) 3305db2f26eSSascha Wildner panic("acpi added power consumer but can't find it"); 3315db2f26eSSascha Wildner } 3325db2f26eSSascha Wildner 3335db2f26eSSascha Wildner /* Check for valid transitions. We can only go to D0 from D3. */ 3345db2f26eSSascha Wildner status = AE_BAD_PARAMETER; 3355db2f26eSSascha Wildner if (pc->ac_state == ACPI_STATE_D3 && state != ACPI_STATE_D0) 3365db2f26eSSascha Wildner goto out; 3375db2f26eSSascha Wildner 3385db2f26eSSascha Wildner /* Find transition mechanism(s) */ 3395db2f26eSSascha Wildner switch (state) { 3405db2f26eSSascha Wildner case ACPI_STATE_D0: 3415db2f26eSSascha Wildner method_name = "_PS0"; 3425db2f26eSSascha Wildner reslist_name = "_PR0"; 3435db2f26eSSascha Wildner break; 3445db2f26eSSascha Wildner case ACPI_STATE_D1: 3455db2f26eSSascha Wildner method_name = "_PS1"; 3465db2f26eSSascha Wildner reslist_name = "_PR1"; 3475db2f26eSSascha Wildner break; 3485db2f26eSSascha Wildner case ACPI_STATE_D2: 3495db2f26eSSascha Wildner method_name = "_PS2"; 3505db2f26eSSascha Wildner reslist_name = "_PR2"; 3515db2f26eSSascha Wildner break; 3525db2f26eSSascha Wildner case ACPI_STATE_D3: 3535db2f26eSSascha Wildner method_name = "_PS3"; 3545db2f26eSSascha Wildner reslist_name = "_PR3"; 3555db2f26eSSascha Wildner break; 3565db2f26eSSascha Wildner default: 3575db2f26eSSascha Wildner goto out; 3585db2f26eSSascha Wildner } 3595db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "setup to switch %s D%d -> D%d\n", 3605db2f26eSSascha Wildner acpi_name(consumer), pc->ac_state, state)); 3615db2f26eSSascha Wildner 3625db2f26eSSascha Wildner /* 3635db2f26eSSascha Wildner * Verify that this state is supported, ie. one of method or 3645db2f26eSSascha Wildner * reslist must be present. We need to do this before we go 3655db2f26eSSascha Wildner * dereferencing resources (since we might be trying to go to 3665db2f26eSSascha Wildner * a state we don't support). 3675db2f26eSSascha Wildner * 3685db2f26eSSascha Wildner * Note that if any states are supported, the device has to 3695db2f26eSSascha Wildner * support D0 and D3. It's never an error to try to go to 3705db2f26eSSascha Wildner * D0. 3715db2f26eSSascha Wildner */ 3725db2f26eSSascha Wildner if (ACPI_FAILURE(AcpiGetHandle(consumer, method_name, &method_handle))) 3735db2f26eSSascha Wildner method_handle = NULL; 3745db2f26eSSascha Wildner if (ACPI_FAILURE(AcpiGetHandle(consumer, reslist_name, &reslist_handle))) 3755db2f26eSSascha Wildner reslist_handle = NULL; 3765db2f26eSSascha Wildner if (reslist_handle == NULL && method_handle == NULL) { 3775db2f26eSSascha Wildner if (state == ACPI_STATE_D0) { 3785db2f26eSSascha Wildner pc->ac_state = ACPI_STATE_D0; 3795db2f26eSSascha Wildner status = AE_OK; 3805db2f26eSSascha Wildner goto out; 3815db2f26eSSascha Wildner } 3825db2f26eSSascha Wildner if (state != ACPI_STATE_D3) { 3835db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 3845db2f26eSSascha Wildner "attempt to set unsupported state D%d\n", state)); 3855db2f26eSSascha Wildner goto out; 3865db2f26eSSascha Wildner } 3875db2f26eSSascha Wildner 3885db2f26eSSascha Wildner /* 3895db2f26eSSascha Wildner * Turn off the resources listed in _PR0 to go to D3. If there is 3905db2f26eSSascha Wildner * no _PR0 method, this object doesn't support ACPI power states. 3915db2f26eSSascha Wildner */ 3925db2f26eSSascha Wildner if (ACPI_FAILURE(AcpiGetHandle(consumer, "_PR0", &pr0_handle))) { 3935db2f26eSSascha Wildner status = AE_NOT_FOUND; 3945db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 3955db2f26eSSascha Wildner "device missing _PR0 (desired state was D%d)\n", state)); 3965db2f26eSSascha Wildner goto out; 3975db2f26eSSascha Wildner } 3985db2f26eSSascha Wildner reslist_buffer.Length = ACPI_ALLOCATE_BUFFER; 3995db2f26eSSascha Wildner status = AcpiEvaluateObject(pr0_handle, NULL, NULL, &reslist_buffer); 4005db2f26eSSascha Wildner if (ACPI_FAILURE(status)) { 4015db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 4025db2f26eSSascha Wildner "can't evaluate _PR0 for device %s, state D%d\n", 4035db2f26eSSascha Wildner acpi_name(consumer), state)); 4045db2f26eSSascha Wildner goto out; 4055db2f26eSSascha Wildner } 4065db2f26eSSascha Wildner reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer; 4075db2f26eSSascha Wildner if (!ACPI_PKG_VALID(reslist_object, 1)) { 4085db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 4095db2f26eSSascha Wildner "invalid package object for state D%d\n", state)); 4105db2f26eSSascha Wildner status = AE_TYPE; 4115db2f26eSSascha Wildner goto out; 4125db2f26eSSascha Wildner } 4135db2f26eSSascha Wildner AcpiOsFree(reslist_buffer.Pointer); 4145db2f26eSSascha Wildner reslist_buffer.Pointer = NULL; 4155db2f26eSSascha Wildner reslist_object = NULL; 4165db2f26eSSascha Wildner } 4175db2f26eSSascha Wildner 4185db2f26eSSascha Wildner /* 4195db2f26eSSascha Wildner * Check that we can actually fetch the list of power resources 4205db2f26eSSascha Wildner */ 4215db2f26eSSascha Wildner if (reslist_handle != NULL) { 4225db2f26eSSascha Wildner reslist_buffer.Length = ACPI_ALLOCATE_BUFFER; 4235db2f26eSSascha Wildner status = AcpiEvaluateObject(reslist_handle, NULL, NULL, 4245db2f26eSSascha Wildner &reslist_buffer); 4255db2f26eSSascha Wildner if (ACPI_FAILURE(status)) { 4265db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 4275db2f26eSSascha Wildner "can't evaluate resource list %s\n", 4285db2f26eSSascha Wildner acpi_name(reslist_handle))); 4295db2f26eSSascha Wildner goto out; 4305db2f26eSSascha Wildner } 4315db2f26eSSascha Wildner reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer; 4325db2f26eSSascha Wildner if (reslist_object->Type != ACPI_TYPE_PACKAGE) { 4335db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 4345db2f26eSSascha Wildner "resource list is not ACPI_TYPE_PACKAGE (%d)\n", 4355db2f26eSSascha Wildner reslist_object->Type)); 4365db2f26eSSascha Wildner status = AE_TYPE; 4375db2f26eSSascha Wildner goto out; 4385db2f26eSSascha Wildner } 4395db2f26eSSascha Wildner } 4405db2f26eSSascha Wildner 4415db2f26eSSascha Wildner /* 4425db2f26eSSascha Wildner * Now we are ready to switch, so kill off any current power 4435db2f26eSSascha Wildner * resource references. 4445db2f26eSSascha Wildner */ 445b5f5419dSSepherosa Ziehau acpi_pwr_dereference_resource(pc); 4465db2f26eSSascha Wildner 4475db2f26eSSascha Wildner /* 4485db2f26eSSascha Wildner * Add new power resource references, if we have any. Traverse the 4495db2f26eSSascha Wildner * package that we got from evaluating reslist_handle, and look up each 4505db2f26eSSascha Wildner * of the resources that are referenced. 4515db2f26eSSascha Wildner */ 4525db2f26eSSascha Wildner if (reslist_object != NULL) { 4535db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "referencing %d new resources\n", 4545db2f26eSSascha Wildner reslist_object->Package.Count)); 4555db2f26eSSascha Wildner acpi_ForeachPackageObject(reslist_object, acpi_pwr_reference_resource, 4565db2f26eSSascha Wildner pc); 4575db2f26eSSascha Wildner } 4585db2f26eSSascha Wildner 4595db2f26eSSascha Wildner /* 4605db2f26eSSascha Wildner * If we changed anything in the resource list, we need to run a switch 4615db2f26eSSascha Wildner * pass now. 4625db2f26eSSascha Wildner */ 4635db2f26eSSascha Wildner if (ACPI_FAILURE(status = acpi_pwr_switch_power())) { 4645db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 4655db2f26eSSascha Wildner "failed to switch resources from %s to D%d\n", 4665db2f26eSSascha Wildner acpi_name(consumer), state)); 4675db2f26eSSascha Wildner 4685db2f26eSSascha Wildner /* XXX is this appropriate? Should we return to previous state? */ 4695db2f26eSSascha Wildner goto out; 4705db2f26eSSascha Wildner } 4715db2f26eSSascha Wildner 4725db2f26eSSascha Wildner /* Invoke power state switch method (if present) */ 4735db2f26eSSascha Wildner if (method_handle != NULL) { 4745db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 4755db2f26eSSascha Wildner "invoking state transition method %s\n", 4765db2f26eSSascha Wildner acpi_name(method_handle))); 4775db2f26eSSascha Wildner status = AcpiEvaluateObject(method_handle, NULL, NULL, NULL); 4785db2f26eSSascha Wildner if (ACPI_FAILURE(status)) { 4795db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to set state - %s\n", 4805db2f26eSSascha Wildner AcpiFormatException(status))); 4815db2f26eSSascha Wildner pc->ac_state = ACPI_STATE_UNKNOWN; 4825db2f26eSSascha Wildner 4835db2f26eSSascha Wildner /* XXX Should we return to previous state? */ 4845db2f26eSSascha Wildner goto out; 4855db2f26eSSascha Wildner } 4865db2f26eSSascha Wildner } 4875db2f26eSSascha Wildner 4885db2f26eSSascha Wildner /* Transition was successful */ 4895db2f26eSSascha Wildner pc->ac_state = state; 4905db2f26eSSascha Wildner status = AE_OK; 4915db2f26eSSascha Wildner 4925db2f26eSSascha Wildner out: 4935db2f26eSSascha Wildner ACPI_SERIAL_END(powerres); 4945db2f26eSSascha Wildner if (reslist_buffer.Pointer != NULL) 4955db2f26eSSascha Wildner AcpiOsFree(reslist_buffer.Pointer); 4965db2f26eSSascha Wildner return_ACPI_STATUS (status); 4975db2f26eSSascha Wildner } 4985db2f26eSSascha Wildner 4995db2f26eSSascha Wildner /* Enable or disable a power resource for wake */ 5005db2f26eSSascha Wildner ACPI_STATUS 5015db2f26eSSascha Wildner acpi_pwr_wake_enable(ACPI_HANDLE consumer, int enable) 5025db2f26eSSascha Wildner { 5035db2f26eSSascha Wildner ACPI_STATUS status; 5045db2f26eSSascha Wildner struct acpi_powerconsumer *pc; 5055db2f26eSSascha Wildner struct acpi_prw_data prw; 5065db2f26eSSascha Wildner int i; 5075db2f26eSSascha Wildner 5085db2f26eSSascha Wildner ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 5095db2f26eSSascha Wildner 5105db2f26eSSascha Wildner if (consumer == NULL) 5115db2f26eSSascha Wildner return (AE_BAD_PARAMETER); 5125db2f26eSSascha Wildner 5135db2f26eSSascha Wildner ACPI_SERIAL_BEGIN(powerres); 5145db2f26eSSascha Wildner if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) { 5155db2f26eSSascha Wildner if (ACPI_FAILURE(status = acpi_pwr_register_consumer(consumer))) 5165db2f26eSSascha Wildner goto out; 5175db2f26eSSascha Wildner if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) 5185db2f26eSSascha Wildner panic("acpi wake added power consumer but can't find it"); 5195db2f26eSSascha Wildner } 5205db2f26eSSascha Wildner 5215db2f26eSSascha Wildner status = AE_OK; 5225db2f26eSSascha Wildner if (acpi_parse_prw(consumer, &prw) != 0) 5235db2f26eSSascha Wildner goto out; 5245db2f26eSSascha Wildner for (i = 0; i < prw.power_res_count; i++) 5255db2f26eSSascha Wildner if (enable) 5265db2f26eSSascha Wildner acpi_pwr_reference_resource(&prw.power_res[i], pc); 5275db2f26eSSascha Wildner else 5285db2f26eSSascha Wildner acpi_pwr_dereference_resource(pc); 5295db2f26eSSascha Wildner 5305db2f26eSSascha Wildner if (prw.power_res_count > 0) 5315db2f26eSSascha Wildner acpi_pwr_switch_power(); 5325db2f26eSSascha Wildner 5335db2f26eSSascha Wildner out: 5345db2f26eSSascha Wildner ACPI_SERIAL_END(powerres); 5355db2f26eSSascha Wildner return (status); 5365db2f26eSSascha Wildner } 5375db2f26eSSascha Wildner 5385db2f26eSSascha Wildner /* 5395db2f26eSSascha Wildner * Called to create a reference between a power consumer and a power resource 5405db2f26eSSascha Wildner * identified in the object. 5415db2f26eSSascha Wildner */ 5425db2f26eSSascha Wildner static void 5435db2f26eSSascha Wildner acpi_pwr_reference_resource(ACPI_OBJECT *obj, void *arg) 5445db2f26eSSascha Wildner { 5455db2f26eSSascha Wildner struct acpi_powerconsumer *pc = (struct acpi_powerconsumer *)arg; 5465db2f26eSSascha Wildner struct acpi_powerreference *pr; 5475db2f26eSSascha Wildner struct acpi_powerresource *rp; 5485db2f26eSSascha Wildner ACPI_HANDLE res; 5495db2f26eSSascha Wildner ACPI_STATUS status; 5505db2f26eSSascha Wildner 5515db2f26eSSascha Wildner ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 5525db2f26eSSascha Wildner ACPI_SERIAL_ASSERT(powerres); 5535db2f26eSSascha Wildner 5545db2f26eSSascha Wildner res = acpi_GetReference(NULL, obj); 5555db2f26eSSascha Wildner if (res == NULL) { 5565db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 5575db2f26eSSascha Wildner "can't create a power reference for object type %d\n", 5585db2f26eSSascha Wildner obj->Type)); 5595db2f26eSSascha Wildner return_VOID; 5605db2f26eSSascha Wildner } 5615db2f26eSSascha Wildner 5625db2f26eSSascha Wildner /* Create/look up the resource */ 5635db2f26eSSascha Wildner if (ACPI_FAILURE(status = acpi_pwr_register_resource(res))) { 5645db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 5655db2f26eSSascha Wildner "couldn't register power resource %s - %s\n", 5665db2f26eSSascha Wildner obj->String.Pointer, AcpiFormatException(status))); 5675db2f26eSSascha Wildner return_VOID; 5685db2f26eSSascha Wildner } 5695db2f26eSSascha Wildner if ((rp = acpi_pwr_find_resource(res)) == NULL) { 5705db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "power resource list corrupted\n")); 5715db2f26eSSascha Wildner return_VOID; 5725db2f26eSSascha Wildner } 5735db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "found power resource %s\n", 5745db2f26eSSascha Wildner acpi_name(rp->ap_resource))); 5755db2f26eSSascha Wildner 5765db2f26eSSascha Wildner /* Create a reference between the consumer and resource */ 5775db2f26eSSascha Wildner if ((pr = kmalloc(sizeof(*pr), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) { 5785db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 5795db2f26eSSascha Wildner "allocation failed for a power consumer reference\n")); 5805db2f26eSSascha Wildner return_VOID; 5815db2f26eSSascha Wildner } 5825db2f26eSSascha Wildner pr->ar_consumer = pc; 5835db2f26eSSascha Wildner pr->ar_resource = rp; 5845db2f26eSSascha Wildner TAILQ_INSERT_TAIL(&pc->ac_references, pr, ar_clink); 5855db2f26eSSascha Wildner TAILQ_INSERT_TAIL(&rp->ap_references, pr, ar_rlink); 5865db2f26eSSascha Wildner 5875db2f26eSSascha Wildner return_VOID; 5885db2f26eSSascha Wildner } 5895db2f26eSSascha Wildner 5905db2f26eSSascha Wildner static int 5915db2f26eSSascha Wildner acpi_pwr_dereference_resource(struct acpi_powerconsumer *pc) 5925db2f26eSSascha Wildner { 5935db2f26eSSascha Wildner struct acpi_powerreference *pr; 5945db2f26eSSascha Wildner int changed; 5955db2f26eSSascha Wildner 5965db2f26eSSascha Wildner ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 5975db2f26eSSascha Wildner ACPI_SERIAL_ASSERT(powerres); 5985db2f26eSSascha Wildner 5995db2f26eSSascha Wildner changed = 0; 6005db2f26eSSascha Wildner while ((pr = TAILQ_FIRST(&pc->ac_references)) != NULL) { 6015db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "removing reference to %s\n", 6025db2f26eSSascha Wildner acpi_name(pr->ar_resource->ap_resource))); 6035db2f26eSSascha Wildner TAILQ_REMOVE(&pr->ar_resource->ap_references, pr, ar_rlink); 6045db2f26eSSascha Wildner TAILQ_REMOVE(&pc->ac_references, pr, ar_clink); 6055db2f26eSSascha Wildner kfree(pr, M_ACPIPWR); 6065db2f26eSSascha Wildner changed = 1; 6075db2f26eSSascha Wildner } 6085db2f26eSSascha Wildner 6095db2f26eSSascha Wildner return (changed); 6105db2f26eSSascha Wildner } 6115db2f26eSSascha Wildner 6125db2f26eSSascha Wildner /* 6135db2f26eSSascha Wildner * Switch power resources to conform to the desired state. 6145db2f26eSSascha Wildner * 6155db2f26eSSascha Wildner * Consumers may have modified the power resource list in an arbitrary 6165db2f26eSSascha Wildner * fashion; we sweep it in sequence order. 6175db2f26eSSascha Wildner */ 6185db2f26eSSascha Wildner static ACPI_STATUS 6195db2f26eSSascha Wildner acpi_pwr_switch_power(void) 6205db2f26eSSascha Wildner { 6215db2f26eSSascha Wildner struct acpi_powerresource *rp; 6225db2f26eSSascha Wildner ACPI_STATUS status; 6235db2f26eSSascha Wildner int cur; 6245db2f26eSSascha Wildner 6255db2f26eSSascha Wildner ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 6265db2f26eSSascha Wildner ACPI_SERIAL_ASSERT(powerres); 6275db2f26eSSascha Wildner 6285db2f26eSSascha Wildner /* 6295db2f26eSSascha Wildner * Sweep the list forwards turning things on. 6305db2f26eSSascha Wildner */ 6315db2f26eSSascha Wildner TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) { 6325db2f26eSSascha Wildner if (TAILQ_FIRST(&rp->ap_references) == NULL) { 6335db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 6345db2f26eSSascha Wildner "%s has no references, not turning on\n", 6355db2f26eSSascha Wildner acpi_name(rp->ap_resource))); 6365db2f26eSSascha Wildner continue; 6375db2f26eSSascha Wildner } 6385db2f26eSSascha Wildner 6395db2f26eSSascha Wildner /* We could cache this if we trusted it not to change under us */ 6405db2f26eSSascha Wildner status = acpi_GetInteger(rp->ap_resource, "_STA", &cur); 6415db2f26eSSascha Wildner if (ACPI_FAILURE(status)) { 6425db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n", 6435db2f26eSSascha Wildner acpi_name(rp->ap_resource), status)); 6445db2f26eSSascha Wildner /* XXX is this correct? Always switch if in doubt? */ 6455db2f26eSSascha Wildner continue; 6465db2f26eSSascha Wildner } else if (rp->ap_state == ACPI_PWR_UNK) 6475db2f26eSSascha Wildner rp->ap_state = cur; 6485db2f26eSSascha Wildner 6495db2f26eSSascha Wildner /* 6505db2f26eSSascha Wildner * Switch if required. Note that we ignore the result of the switch 6515db2f26eSSascha Wildner * effort; we don't know what to do if it fails, so checking wouldn't 6525db2f26eSSascha Wildner * help much. 6535db2f26eSSascha Wildner */ 6545db2f26eSSascha Wildner if (rp->ap_state != ACPI_PWR_ON) { 6555db2f26eSSascha Wildner status = AcpiEvaluateObject(rp->ap_resource, "_ON", NULL, NULL); 6565db2f26eSSascha Wildner if (ACPI_FAILURE(status)) { 6575db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 6585db2f26eSSascha Wildner "failed to switch %s on - %s\n", 6595db2f26eSSascha Wildner acpi_name(rp->ap_resource), 6605db2f26eSSascha Wildner AcpiFormatException(status))); 6615db2f26eSSascha Wildner } else { 6625db2f26eSSascha Wildner rp->ap_state = ACPI_PWR_ON; 6635db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s on\n", 6645db2f26eSSascha Wildner acpi_name(rp->ap_resource))); 6655db2f26eSSascha Wildner } 6665db2f26eSSascha Wildner } else { 6675db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already on\n", 6685db2f26eSSascha Wildner acpi_name(rp->ap_resource))); 6695db2f26eSSascha Wildner } 6705db2f26eSSascha Wildner } 6715db2f26eSSascha Wildner 6725db2f26eSSascha Wildner /* Sweep the list backwards turning things off. */ 6735db2f26eSSascha Wildner TAILQ_FOREACH_REVERSE(rp, &acpi_powerresources, acpi_powerresource_list, 6745db2f26eSSascha Wildner ap_link) { 6755db2f26eSSascha Wildner 6765db2f26eSSascha Wildner if (TAILQ_FIRST(&rp->ap_references) != NULL) { 6775db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 6785db2f26eSSascha Wildner "%s has references, not turning off\n", 6795db2f26eSSascha Wildner acpi_name(rp->ap_resource))); 6805db2f26eSSascha Wildner continue; 6815db2f26eSSascha Wildner } 6825db2f26eSSascha Wildner 6835db2f26eSSascha Wildner /* We could cache this if we trusted it not to change under us */ 6845db2f26eSSascha Wildner status = acpi_GetInteger(rp->ap_resource, "_STA", &cur); 6855db2f26eSSascha Wildner if (ACPI_FAILURE(status)) { 6865db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n", 6875db2f26eSSascha Wildner acpi_name(rp->ap_resource), status)); 6885db2f26eSSascha Wildner /* XXX is this correct? Always switch if in doubt? */ 6895db2f26eSSascha Wildner continue; 6905db2f26eSSascha Wildner } else if (rp->ap_state == ACPI_PWR_UNK) 6915db2f26eSSascha Wildner rp->ap_state = cur; 6925db2f26eSSascha Wildner 6935db2f26eSSascha Wildner /* 6945db2f26eSSascha Wildner * Switch if required. Note that we ignore the result of the switch 6955db2f26eSSascha Wildner * effort; we don't know what to do if it fails, so checking wouldn't 6965db2f26eSSascha Wildner * help much. 6975db2f26eSSascha Wildner */ 6985db2f26eSSascha Wildner if (rp->ap_state != ACPI_PWR_OFF) { 6995db2f26eSSascha Wildner status = AcpiEvaluateObject(rp->ap_resource, "_OFF", NULL, NULL); 7005db2f26eSSascha Wildner if (ACPI_FAILURE(status)) { 7015db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, 7025db2f26eSSascha Wildner "failed to switch %s off - %s\n", 7035db2f26eSSascha Wildner acpi_name(rp->ap_resource), 7045db2f26eSSascha Wildner AcpiFormatException(status))); 7055db2f26eSSascha Wildner } else { 7065db2f26eSSascha Wildner rp->ap_state = ACPI_PWR_OFF; 7075db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s off\n", 7085db2f26eSSascha Wildner acpi_name(rp->ap_resource))); 7095db2f26eSSascha Wildner } 7105db2f26eSSascha Wildner } else { 7115db2f26eSSascha Wildner ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already off\n", 7125db2f26eSSascha Wildner acpi_name(rp->ap_resource))); 7135db2f26eSSascha Wildner } 7145db2f26eSSascha Wildner } 7155db2f26eSSascha Wildner 7165db2f26eSSascha Wildner return_ACPI_STATUS (AE_OK); 7175db2f26eSSascha Wildner } 7185db2f26eSSascha Wildner 7195db2f26eSSascha Wildner /* 7205db2f26eSSascha Wildner * Find a power resource's control structure. 7215db2f26eSSascha Wildner */ 7225db2f26eSSascha Wildner static struct acpi_powerresource * 7235db2f26eSSascha Wildner acpi_pwr_find_resource(ACPI_HANDLE res) 7245db2f26eSSascha Wildner { 7255db2f26eSSascha Wildner struct acpi_powerresource *rp; 7265db2f26eSSascha Wildner 7275db2f26eSSascha Wildner ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 7285db2f26eSSascha Wildner ACPI_SERIAL_ASSERT(powerres); 7295db2f26eSSascha Wildner 7305db2f26eSSascha Wildner TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) { 7315db2f26eSSascha Wildner if (rp->ap_resource == res) 7325db2f26eSSascha Wildner break; 7335db2f26eSSascha Wildner } 7345db2f26eSSascha Wildner 7355db2f26eSSascha Wildner return_PTR (rp); 7365db2f26eSSascha Wildner } 7375db2f26eSSascha Wildner 7385db2f26eSSascha Wildner /* 7395db2f26eSSascha Wildner * Find a power consumer's control structure. 7405db2f26eSSascha Wildner */ 7415db2f26eSSascha Wildner static struct acpi_powerconsumer * 7425db2f26eSSascha Wildner acpi_pwr_find_consumer(ACPI_HANDLE consumer) 7435db2f26eSSascha Wildner { 7445db2f26eSSascha Wildner struct acpi_powerconsumer *pc; 7455db2f26eSSascha Wildner 7465db2f26eSSascha Wildner ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 7475db2f26eSSascha Wildner ACPI_SERIAL_ASSERT(powerres); 7485db2f26eSSascha Wildner 7495db2f26eSSascha Wildner TAILQ_FOREACH(pc, &acpi_powerconsumers, ac_link) { 7505db2f26eSSascha Wildner if (pc->ac_consumer == consumer) 7515db2f26eSSascha Wildner break; 7525db2f26eSSascha Wildner } 7535db2f26eSSascha Wildner 7545db2f26eSSascha Wildner return_PTR (pc); 7555db2f26eSSascha Wildner } 756