xref: /netbsd-src/sys/dev/acpi/acpi_power.c (revision ef019da374e5714657161f58c14162f46ac415d7)
1*ef019da3Smrg /* $NetBSD: acpi_power.c,v 1.36 2022/05/31 20:28:57 mrg Exp $ */
29ddf2c51Sjruoho 
39ddf2c51Sjruoho /*-
443337e89Sjruoho  * Copyright (c) 2009, 2010, 2011 The NetBSD Foundation, Inc.
59ddf2c51Sjruoho  * All rights reserved.
69ddf2c51Sjruoho  *
79ddf2c51Sjruoho  * This code is derived from software contributed to The NetBSD Foundation
89ddf2c51Sjruoho  * by Jukka Ruohonen.
99ddf2c51Sjruoho  *
109ddf2c51Sjruoho  * Redistribution and use in source and binary forms, with or without
119ddf2c51Sjruoho  * modification, are permitted provided that the following conditions
129ddf2c51Sjruoho  * are met:
139ddf2c51Sjruoho  * 1. Redistributions of source code must retain the above copyright
149ddf2c51Sjruoho  *    notice, this list of conditions and the following disclaimer.
159ddf2c51Sjruoho  * 2. Redistributions in binary form must reproduce the above copyright
169ddf2c51Sjruoho  *    notice, this list of conditions and the following disclaimer in the
179ddf2c51Sjruoho  *    documentation and/or other materials provided with the distribution.
189ddf2c51Sjruoho  *
199ddf2c51Sjruoho  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
209ddf2c51Sjruoho  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
219ddf2c51Sjruoho  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
229ddf2c51Sjruoho  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
239ddf2c51Sjruoho  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
249ddf2c51Sjruoho  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
259ddf2c51Sjruoho  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
269ddf2c51Sjruoho  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
279ddf2c51Sjruoho  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
289ddf2c51Sjruoho  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
299ddf2c51Sjruoho  * POSSIBILITY OF SUCH DAMAGE.
309ddf2c51Sjruoho  */
319ddf2c51Sjruoho 
329ddf2c51Sjruoho /*-
339ddf2c51Sjruoho  * Copyright (c) 2001 Michael Smith
349ddf2c51Sjruoho  * All rights reserved.
359ddf2c51Sjruoho  *
369ddf2c51Sjruoho  * Redistribution and use in source and binary forms, with or without
379ddf2c51Sjruoho  * modification, are permitted provided that the following conditions
389ddf2c51Sjruoho  * are met:
399ddf2c51Sjruoho  * 1. Redistributions of source code must retain the above copyright
409ddf2c51Sjruoho  *    notice, this list of conditions and the following disclaimer.
419ddf2c51Sjruoho  * 2. Redistributions in binary form must reproduce the above copyright
429ddf2c51Sjruoho  *    notice, this list of conditions and the following disclaimer in the
439ddf2c51Sjruoho  *    documentation and/or other materials provided with the distribution.
449ddf2c51Sjruoho  *
459ddf2c51Sjruoho  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
469ddf2c51Sjruoho  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
479ddf2c51Sjruoho  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
489ddf2c51Sjruoho  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
499ddf2c51Sjruoho  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
509ddf2c51Sjruoho  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
519ddf2c51Sjruoho  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
529ddf2c51Sjruoho  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
539ddf2c51Sjruoho  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
549ddf2c51Sjruoho  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
559ddf2c51Sjruoho  * SUCH DAMAGE.
569ddf2c51Sjruoho  */
579ddf2c51Sjruoho 
589ddf2c51Sjruoho #include <sys/cdefs.h>
59*ef019da3Smrg __KERNEL_RCSID(0, "$NetBSD: acpi_power.c,v 1.36 2022/05/31 20:28:57 mrg Exp $");
60*ef019da3Smrg 
61*ef019da3Smrg #include "pci.h"
629ddf2c51Sjruoho 
639ddf2c51Sjruoho #include <sys/param.h>
649ddf2c51Sjruoho #include <sys/kmem.h>
659ddf2c51Sjruoho #include <sys/mutex.h>
6616bdc3ffSjruoho #include <sys/sysctl.h>
679ddf2c51Sjruoho 
689ddf2c51Sjruoho #include <dev/acpi/acpireg.h>
699ddf2c51Sjruoho #include <dev/acpi/acpivar.h>
70e008cf16Sjruoho #include <dev/acpi/acpi_pci.h>
719ddf2c51Sjruoho #include <dev/acpi/acpi_power.h>
729ddf2c51Sjruoho 
739ddf2c51Sjruoho #define _COMPONENT			ACPI_BUS_COMPONENT
749ddf2c51Sjruoho ACPI_MODULE_NAME			("acpi_power")
759ddf2c51Sjruoho 
76d5124a18Sjruoho #define	ACPI_STA_POW_OFF		0x00
77d5124a18Sjruoho #define	ACPI_STA_POW_ON			0x01
78d5124a18Sjruoho 
799ddf2c51Sjruoho struct acpi_power_res {
809ddf2c51Sjruoho 	ACPI_HANDLE			res_handle;
819ddf2c51Sjruoho 	ACPI_INTEGER			res_level;
829ddf2c51Sjruoho 	ACPI_INTEGER			res_order;
8343337e89Sjruoho 	ACPI_HANDLE			res_ref[5];
849ddf2c51Sjruoho 	char				res_name[5];
859ddf2c51Sjruoho 	kmutex_t			res_mutex;
869ddf2c51Sjruoho 
879ddf2c51Sjruoho 	TAILQ_ENTRY(acpi_power_res)	res_list;
889ddf2c51Sjruoho };
899ddf2c51Sjruoho 
909ddf2c51Sjruoho static TAILQ_HEAD(, acpi_power_res) res_head =
919ddf2c51Sjruoho 	TAILQ_HEAD_INITIALIZER(res_head);
929ddf2c51Sjruoho 
93646cb7e2Sgsutre static int32_t acpi_power_acpinode = CTL_EOL;
94646cb7e2Sgsutre static int32_t acpi_power_powernode = CTL_EOL;
9516bdc3ffSjruoho 
969ddf2c51Sjruoho static struct acpi_power_res	*acpi_power_res_init(ACPI_HANDLE);
979ddf2c51Sjruoho static struct acpi_power_res	*acpi_power_res_get(ACPI_HANDLE);
989ddf2c51Sjruoho 
999ddf2c51Sjruoho static ACPI_STATUS	 acpi_power_get_direct(struct acpi_devnode *);
1009ddf2c51Sjruoho static ACPI_STATUS	 acpi_power_get_indirect(struct acpi_devnode *);
1019ddf2c51Sjruoho static ACPI_STATUS	 acpi_power_switch(struct acpi_devnode *,
1029ddf2c51Sjruoho 						   int, bool);
1039ddf2c51Sjruoho static ACPI_STATUS	 acpi_power_res_ref(struct acpi_power_res *,
1049ddf2c51Sjruoho 					    ACPI_HANDLE);
1059ddf2c51Sjruoho static ACPI_STATUS	 acpi_power_res_deref(struct acpi_power_res *,
1069ddf2c51Sjruoho 					      ACPI_HANDLE);
1079ddf2c51Sjruoho static ACPI_STATUS	 acpi_power_res_sta(ACPI_OBJECT *, void *);
1089ddf2c51Sjruoho 
1099ddf2c51Sjruoho static ACPI_OBJECT	*acpi_power_pkg_get(ACPI_HANDLE, int);
1102a35c4cfSjruoho static int		 acpi_power_sysctl(SYSCTLFN_PROTO);
1119ddf2c51Sjruoho static const char	*acpi_xname(ACPI_HANDLE);
1129ddf2c51Sjruoho 
1139ddf2c51Sjruoho static struct acpi_power_res *
acpi_power_res_init(ACPI_HANDLE hdl)1149ddf2c51Sjruoho acpi_power_res_init(ACPI_HANDLE hdl)
1159ddf2c51Sjruoho {
1169ddf2c51Sjruoho 	struct acpi_power_res *tmp = NULL;
1179ddf2c51Sjruoho 	struct acpi_power_res *res = NULL;
1189ddf2c51Sjruoho 	ACPI_OBJECT *obj;
1199ddf2c51Sjruoho 	ACPI_BUFFER buf;
1209ddf2c51Sjruoho 	ACPI_STATUS rv;
12143337e89Sjruoho 	size_t i;
1229ddf2c51Sjruoho 
1239ddf2c51Sjruoho 	rv = acpi_eval_struct(hdl, NULL, &buf);
1249ddf2c51Sjruoho 
1259ddf2c51Sjruoho 	if (ACPI_FAILURE(rv))
1269ddf2c51Sjruoho 		goto out;
1279ddf2c51Sjruoho 
1289ddf2c51Sjruoho 	obj = buf.Pointer;
1299ddf2c51Sjruoho 
1309ddf2c51Sjruoho 	if (obj->Type != ACPI_TYPE_POWER) {
1319ddf2c51Sjruoho 		rv = AE_TYPE;
1329ddf2c51Sjruoho 		goto out;
1339ddf2c51Sjruoho 	}
1349ddf2c51Sjruoho 
1359ddf2c51Sjruoho 	res = kmem_zalloc(sizeof(*res), KM_SLEEP);
1369ddf2c51Sjruoho 	res->res_handle = hdl;
1379ddf2c51Sjruoho 	res->res_level = obj->PowerResource.SystemLevel;
1389ddf2c51Sjruoho 	res->res_order = obj->PowerResource.ResourceOrder;
1399ddf2c51Sjruoho 
1409ddf2c51Sjruoho 	(void)strlcpy(res->res_name,
1419ddf2c51Sjruoho 	    acpi_xname(hdl), sizeof(res->res_name));
1429ddf2c51Sjruoho 
14343337e89Sjruoho 	for (i = 0; i < __arraycount(res->res_ref); i++)
14443337e89Sjruoho 		res->res_ref[i] = NULL;
14543337e89Sjruoho 
1469ddf2c51Sjruoho 	mutex_init(&res->res_mutex, MUTEX_DEFAULT, IPL_NONE);
1479ddf2c51Sjruoho 
1489ddf2c51Sjruoho 	/*
1499ddf2c51Sjruoho 	 * Power resources should be ordered.
1509ddf2c51Sjruoho 	 *
1519ddf2c51Sjruoho 	 * These *should* be enabled from low values to high
1529ddf2c51Sjruoho 	 * values and disabled from high values to low values.
1539ddf2c51Sjruoho 	 */
1549ddf2c51Sjruoho 	TAILQ_FOREACH(tmp, &res_head, res_list) {
1559ddf2c51Sjruoho 
1569ddf2c51Sjruoho 		if (res->res_order < tmp->res_order) {
1579ddf2c51Sjruoho 			TAILQ_INSERT_BEFORE(tmp, res, res_list);
1589ddf2c51Sjruoho 			break;
1599ddf2c51Sjruoho 		}
1609ddf2c51Sjruoho 	}
1619ddf2c51Sjruoho 
1629ddf2c51Sjruoho 	if (tmp == NULL)
1639ddf2c51Sjruoho 		TAILQ_INSERT_TAIL(&res_head, res, res_list);
1649ddf2c51Sjruoho 
16543330869Sjruoho 	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s added to the "
16643330869Sjruoho 		"power resource queue\n", res->res_name));
16743330869Sjruoho 
1689ddf2c51Sjruoho out:
1699ddf2c51Sjruoho 	if (buf.Pointer != NULL)
1709ddf2c51Sjruoho 		ACPI_FREE(buf.Pointer);
1719ddf2c51Sjruoho 
1729ddf2c51Sjruoho 	return res;
1739ddf2c51Sjruoho }
1749ddf2c51Sjruoho 
1759ddf2c51Sjruoho static struct acpi_power_res *
acpi_power_res_get(ACPI_HANDLE hdl)1769ddf2c51Sjruoho acpi_power_res_get(ACPI_HANDLE hdl)
1779ddf2c51Sjruoho {
1789ddf2c51Sjruoho 	struct acpi_power_res *res;
1799ddf2c51Sjruoho 
1809ddf2c51Sjruoho 	TAILQ_FOREACH(res, &res_head, res_list) {
1819ddf2c51Sjruoho 
1829ddf2c51Sjruoho 		if (res->res_handle == hdl)
1839ddf2c51Sjruoho 			return res;
1849ddf2c51Sjruoho 	}
1859ddf2c51Sjruoho 
1869ddf2c51Sjruoho 	return acpi_power_res_init(hdl);
1879ddf2c51Sjruoho }
1889ddf2c51Sjruoho 
1899ddf2c51Sjruoho bool
acpi_power_register(ACPI_HANDLE hdl)19020525938Sjruoho acpi_power_register(ACPI_HANDLE hdl)
1919ddf2c51Sjruoho {
1929ddf2c51Sjruoho 	return true;
1939ddf2c51Sjruoho }
1949ddf2c51Sjruoho 
1959ddf2c51Sjruoho void
acpi_power_deregister(ACPI_HANDLE hdl)19620525938Sjruoho acpi_power_deregister(ACPI_HANDLE hdl)
1979ddf2c51Sjruoho {
198a9db528eSjruoho 	struct acpi_devnode *ad = acpi_match_node(hdl);
1999ddf2c51Sjruoho 	struct acpi_power_res *res;
2009ddf2c51Sjruoho 
20120525938Sjruoho 	if (ad == NULL)
20220525938Sjruoho 		return;
2039ddf2c51Sjruoho 
2049ddf2c51Sjruoho 	/*
2059ddf2c51Sjruoho 	 * Remove all references in each resource.
2069ddf2c51Sjruoho 	 */
2079ddf2c51Sjruoho 	TAILQ_FOREACH(res, &res_head, res_list)
2089ddf2c51Sjruoho 	    (void)acpi_power_res_deref(res, ad->ad_handle);
2099ddf2c51Sjruoho }
2109ddf2c51Sjruoho 
2119ddf2c51Sjruoho /*
2129ddf2c51Sjruoho  * Get the D-state of an ACPI device node.
2139ddf2c51Sjruoho  */
2149ddf2c51Sjruoho bool
acpi_power_get(ACPI_HANDLE hdl,int * state)21520525938Sjruoho acpi_power_get(ACPI_HANDLE hdl, int *state)
2169ddf2c51Sjruoho {
217a9db528eSjruoho 	struct acpi_devnode *ad = acpi_match_node(hdl);
2189ddf2c51Sjruoho 	ACPI_STATUS rv;
2199ddf2c51Sjruoho 
22020525938Sjruoho 	if (ad == NULL)
22120525938Sjruoho 		return false;
22220525938Sjruoho 
223a24d1186Sjruoho 	/*
224ef2b1087Sjruoho 	 * As _PSC may be broken, first try to
225ef2b1087Sjruoho 	 * retrieve the power state indirectly
226ef2b1087Sjruoho 	 * via power resources.
227a24d1186Sjruoho 	 */
2289ddf2c51Sjruoho 	rv = acpi_power_get_indirect(ad);
2299ddf2c51Sjruoho 
2309ddf2c51Sjruoho 	if (ACPI_FAILURE(rv))
231a24d1186Sjruoho 		rv = acpi_power_get_direct(ad);
232a24d1186Sjruoho 
233a24d1186Sjruoho 	if (ACPI_FAILURE(rv))
2349ddf2c51Sjruoho 		goto fail;
2359ddf2c51Sjruoho 
2369ddf2c51Sjruoho 	KASSERT(ad->ad_state != ACPI_STATE_ERROR);
2379ddf2c51Sjruoho 
2389ddf2c51Sjruoho 	if (ad->ad_state < ACPI_STATE_D0 || ad->ad_state > ACPI_STATE_D3) {
2399ddf2c51Sjruoho 		rv = AE_BAD_VALUE;
2409ddf2c51Sjruoho 		goto fail;
2419ddf2c51Sjruoho 	}
2429ddf2c51Sjruoho 
2439ddf2c51Sjruoho 	if (state != NULL)
2449ddf2c51Sjruoho 		*state = ad->ad_state;
2459ddf2c51Sjruoho 
2469ddf2c51Sjruoho 	return true;
2479ddf2c51Sjruoho 
2489ddf2c51Sjruoho fail:
2499ddf2c51Sjruoho 	ad->ad_state = ACPI_STATE_ERROR;
2509ddf2c51Sjruoho 
2519ddf2c51Sjruoho 	if (state != NULL)
2529ddf2c51Sjruoho 		*state = ad->ad_state;
2539ddf2c51Sjruoho 
254f640132dSjruoho 	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "failed to get power state "
255f640132dSjruoho 		"for %s: %s\n", ad->ad_name, AcpiFormatException(rv)));
2569ddf2c51Sjruoho 
2579ddf2c51Sjruoho 	return false;
2589ddf2c51Sjruoho }
2599ddf2c51Sjruoho 
2609ddf2c51Sjruoho static ACPI_STATUS
acpi_power_get_direct(struct acpi_devnode * ad)2619ddf2c51Sjruoho acpi_power_get_direct(struct acpi_devnode *ad)
2629ddf2c51Sjruoho {
2639ddf2c51Sjruoho 	ACPI_INTEGER val = 0;
2649ddf2c51Sjruoho 	ACPI_STATUS rv;
2659ddf2c51Sjruoho 
2669ddf2c51Sjruoho 	rv = acpi_eval_integer(ad->ad_handle, "_PSC", &val);
2679ddf2c51Sjruoho 
2689ddf2c51Sjruoho 	KDASSERT((uint64_t)val < INT_MAX);
2699ddf2c51Sjruoho 
2709ddf2c51Sjruoho 	ad->ad_state = (int)val;
2719ddf2c51Sjruoho 
2729ddf2c51Sjruoho 	return rv;
2739ddf2c51Sjruoho }
2749ddf2c51Sjruoho 
2759ddf2c51Sjruoho static ACPI_STATUS
acpi_power_get_indirect(struct acpi_devnode * ad)2769ddf2c51Sjruoho acpi_power_get_indirect(struct acpi_devnode *ad)
2779ddf2c51Sjruoho {
2789ddf2c51Sjruoho 	ACPI_OBJECT *pkg;
2799ddf2c51Sjruoho 	ACPI_STATUS rv;
2809ddf2c51Sjruoho 	int i;
2819ddf2c51Sjruoho 
2829ddf2c51Sjruoho 	CTASSERT(ACPI_STATE_D0 == 0 && ACPI_STATE_D1 == 1);
2839ddf2c51Sjruoho 	CTASSERT(ACPI_STATE_D2 == 2 && ACPI_STATE_D3 == 3);
2849ddf2c51Sjruoho 
2859ddf2c51Sjruoho 	/*
2869ddf2c51Sjruoho 	 * The device is in a given D-state if all resources are on.
2879ddf2c51Sjruoho 	 * To derive this, evaluate all elements in each _PRx package
2889ddf2c51Sjruoho 	 * (x = 0 ... 3) and break if the noted condition becomes true.
2899ddf2c51Sjruoho 	 */
2909ddf2c51Sjruoho 	for (ad->ad_state = ACPI_STATE_D3, i = 0; i < ACPI_STATE_D3; i++) {
2919ddf2c51Sjruoho 
2929ddf2c51Sjruoho 		pkg = acpi_power_pkg_get(ad->ad_handle, i);
2939ddf2c51Sjruoho 
2949ddf2c51Sjruoho 		if (pkg == NULL)
2959ddf2c51Sjruoho 			continue;
2969ddf2c51Sjruoho 
2979ddf2c51Sjruoho 		/*
2989ddf2c51Sjruoho 		 * For each element in the _PRx package, evaluate _STA
2999ddf2c51Sjruoho 		 * and return AE_OK only if all power resources are on.
3009ddf2c51Sjruoho 		 */
3019ddf2c51Sjruoho 		rv = acpi_foreach_package_object(pkg, acpi_power_res_sta, ad);
3029ddf2c51Sjruoho 
303696f7c4bSjruoho 		if (ACPI_FAILURE(rv) && rv != AE_CTRL_FALSE)
3049ddf2c51Sjruoho 			goto out;
3059ddf2c51Sjruoho 
3069ddf2c51Sjruoho 		if (ACPI_SUCCESS(rv)) {
3079ddf2c51Sjruoho 			ad->ad_state = i;
3089ddf2c51Sjruoho 			goto out;
3099ddf2c51Sjruoho 		}
3109ddf2c51Sjruoho 
3119ddf2c51Sjruoho 		ACPI_FREE(pkg); pkg = NULL;
3129ddf2c51Sjruoho 	}
3139ddf2c51Sjruoho 
3149ddf2c51Sjruoho 	KASSERT(ad->ad_state == ACPI_STATE_D3);
3159ddf2c51Sjruoho 
3169ddf2c51Sjruoho 	return AE_OK;
3179ddf2c51Sjruoho 
3189ddf2c51Sjruoho out:
3199ddf2c51Sjruoho 	ACPI_FREE(pkg);
3209ddf2c51Sjruoho 
3219ddf2c51Sjruoho 	return rv;
3229ddf2c51Sjruoho }
3239ddf2c51Sjruoho 
3249ddf2c51Sjruoho /*
3259ddf2c51Sjruoho  * Set the D-state of an ACPI device node.
3269ddf2c51Sjruoho  */
3279ddf2c51Sjruoho bool
acpi_power_set(ACPI_HANDLE hdl,int state)32820525938Sjruoho acpi_power_set(ACPI_HANDLE hdl, int state)
3299ddf2c51Sjruoho {
330a9db528eSjruoho 	struct acpi_devnode *ad = acpi_match_node(hdl);
3319ddf2c51Sjruoho 	ACPI_STATUS rv;
3329ddf2c51Sjruoho 	char path[5];
3339ddf2c51Sjruoho 	int old;
3349ddf2c51Sjruoho 
33520525938Sjruoho 	if (ad == NULL)
33620525938Sjruoho 		return false;
3379ddf2c51Sjruoho 
3385b0c1990Sjruoho 	if (state < ACPI_STATE_D0 || state > ACPI_STATE_D3) {
3395b0c1990Sjruoho 		rv = AE_BAD_PARAMETER;
3409ddf2c51Sjruoho 		goto fail;
3419ddf2c51Sjruoho 	}
3429ddf2c51Sjruoho 
3439cb2ef6cSjruoho 	if (acpi_power_get(ad->ad_handle, &old) != true) {
3449ddf2c51Sjruoho 		rv = AE_NOT_FOUND;
3459ddf2c51Sjruoho 		goto fail;
3469ddf2c51Sjruoho 	}
3479ddf2c51Sjruoho 
3489ddf2c51Sjruoho 	KASSERT(ad->ad_state == old);
3499ddf2c51Sjruoho 	KASSERT(ad->ad_state != ACPI_STATE_ERROR);
3509ddf2c51Sjruoho 
3519ddf2c51Sjruoho 	if (ad->ad_state == state) {
3529ddf2c51Sjruoho 		rv = AE_ALREADY_EXISTS;
3539ddf2c51Sjruoho 		goto fail;
3549ddf2c51Sjruoho 	}
3559ddf2c51Sjruoho 
3569ddf2c51Sjruoho 	/*
3579ddf2c51Sjruoho 	 * It is only possible to go to D0 ("on") from D3 ("off").
3589ddf2c51Sjruoho 	 */
3599ddf2c51Sjruoho 	if (ad->ad_state == ACPI_STATE_D3 && state != ACPI_STATE_D0) {
3609ddf2c51Sjruoho 		rv = AE_BAD_PARAMETER;
3619ddf2c51Sjruoho 		goto fail;
3629ddf2c51Sjruoho 	}
3639ddf2c51Sjruoho 
3649ddf2c51Sjruoho 	/*
365fc9a58b3Sjruoho 	 * As noted in ACPI 4.0 (appendix A.2.1), the bus power state
366fc9a58b3Sjruoho 	 * should never be lower than the highest state of one of its
367fc9a58b3Sjruoho 	 * devices. Consequently, we cannot set the state to a lower
368fc9a58b3Sjruoho 	 * (i.e. higher power) state than the parent device's state.
369fc9a58b3Sjruoho 	 */
37015a42e9fSjruoho 	if ((ad->ad_parent != NULL) &&
37115a42e9fSjruoho 	    (ad->ad_parent->ad_flags & ACPI_DEVICE_POWER) != 0) {
37215a42e9fSjruoho 
37315a42e9fSjruoho 		if (ad->ad_parent->ad_state > state) {
374fc9a58b3Sjruoho 			rv = AE_ABORT_METHOD;
375fc9a58b3Sjruoho 			goto fail;
376fc9a58b3Sjruoho 		}
37715a42e9fSjruoho 	}
378fc9a58b3Sjruoho 
379fc9a58b3Sjruoho 	/*
380a24d1186Sjruoho 	 * We first sweep through the resources required for the target
381a24d1186Sjruoho 	 * state, turning things on and building references. After this
382a24d1186Sjruoho 	 * we dereference the resources required for the current state,
3839ddf2c51Sjruoho 	 * turning the resources off as we go.
3849ddf2c51Sjruoho 	 */
3859ddf2c51Sjruoho 	rv = acpi_power_switch(ad, state, true);
3869ddf2c51Sjruoho 
387a24d1186Sjruoho 	if (ACPI_FAILURE(rv) && rv != AE_CTRL_CONTINUE)
3889ddf2c51Sjruoho 		goto fail;
3899ddf2c51Sjruoho 
3909ddf2c51Sjruoho 	rv = acpi_power_switch(ad, ad->ad_state, false);
3919ddf2c51Sjruoho 
392a24d1186Sjruoho 	if (ACPI_FAILURE(rv) && rv != AE_CTRL_CONTINUE)
3939ddf2c51Sjruoho 		goto fail;
3949ddf2c51Sjruoho 
3959ddf2c51Sjruoho 	/*
396a24d1186Sjruoho 	 * Last but not least, invoke the power state switch method,
397a24d1186Sjruoho 	 * if available. Because some systems use only _PSx for the
398a24d1186Sjruoho 	 * power state transitions, we do this even if there is no _PRx.
3999ddf2c51Sjruoho 	 */
4009ddf2c51Sjruoho 	(void)snprintf(path, sizeof(path), "_PS%d", state);
4019ddf2c51Sjruoho 	(void)AcpiEvaluateObject(ad->ad_handle, path, NULL, NULL);
4029ddf2c51Sjruoho 
403fc9a58b3Sjruoho 	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s turned from "
404fc9a58b3Sjruoho 		"D%d to D%d\n", ad->ad_name, old, state));
4059ddf2c51Sjruoho 
406a24d1186Sjruoho 	ad->ad_state = state;
407a24d1186Sjruoho 
4089ddf2c51Sjruoho 	return true;
4099ddf2c51Sjruoho 
4109ddf2c51Sjruoho fail:
4119ddf2c51Sjruoho 	ad->ad_state = ACPI_STATE_ERROR;
4129ddf2c51Sjruoho 
4138e7819f7Sjruoho 	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "failed to set power state to D%d "
4148e7819f7Sjruoho 		"for %s: %s\n", state, ad->ad_name, AcpiFormatException(rv)));
4159ddf2c51Sjruoho 
4167e3b451fSjruoho 	return false;
4179ddf2c51Sjruoho }
4189ddf2c51Sjruoho 
4199ddf2c51Sjruoho static ACPI_STATUS
acpi_power_switch(struct acpi_devnode * ad,int state,bool on)4209ddf2c51Sjruoho acpi_power_switch(struct acpi_devnode *ad, int state, bool on)
4219ddf2c51Sjruoho {
4229c09c7fcSjruoho 	ACPI_OBJECT *elm, *pkg;
4239c09c7fcSjruoho 	ACPI_STATUS rv = AE_OK;
4249c09c7fcSjruoho 	ACPI_HANDLE hdl;
4259c09c7fcSjruoho 	uint32_t i, n;
4269ddf2c51Sjruoho 
4279c09c7fcSjruoho 	/*
4289c09c7fcSjruoho 	 * For each element in the _PRx package, fetch
4299c09c7fcSjruoho 	 * the reference handle, search for this handle
4309c09c7fcSjruoho 	 * from the power resource queue, and turn the
4319c09c7fcSjruoho 	 * resource behind the handle on or off.
4329c09c7fcSjruoho 	 */
4339ddf2c51Sjruoho 	pkg = acpi_power_pkg_get(ad->ad_handle, state);
4349ddf2c51Sjruoho 
4359ddf2c51Sjruoho 	if (pkg == NULL)
436a24d1186Sjruoho 		return AE_CTRL_CONTINUE;
4379ddf2c51Sjruoho 
4389c09c7fcSjruoho 	n = pkg->Package.Count;
4399ddf2c51Sjruoho 
4409c09c7fcSjruoho 	for (i = 0; i < n; i++) {
4419c09c7fcSjruoho 
4429c09c7fcSjruoho 		elm = &pkg->Package.Elements[i];
4439c09c7fcSjruoho 		rv = acpi_eval_reference_handle(elm, &hdl);
4449c09c7fcSjruoho 
4459c09c7fcSjruoho 		if (ACPI_FAILURE(rv))
4469c09c7fcSjruoho 			continue;
4479c09c7fcSjruoho 
4489c09c7fcSjruoho 		(void)acpi_power_res(hdl, ad->ad_handle, on);
4499c09c7fcSjruoho 	}
450a24d1186Sjruoho 
4519ddf2c51Sjruoho 	ACPI_FREE(pkg);
4529ddf2c51Sjruoho 
4539ddf2c51Sjruoho 	return rv;
4549ddf2c51Sjruoho }
4559ddf2c51Sjruoho 
4569c09c7fcSjruoho ACPI_STATUS
acpi_power_res(ACPI_HANDLE hdl,ACPI_HANDLE ref,bool on)4579c09c7fcSjruoho acpi_power_res(ACPI_HANDLE hdl, ACPI_HANDLE ref, bool on)
4589ddf2c51Sjruoho {
4599ddf2c51Sjruoho 	struct acpi_power_res *res;
4609c09c7fcSjruoho 	const char *str;
4619ddf2c51Sjruoho 	ACPI_STATUS rv;
4629ddf2c51Sjruoho 
4639ddf2c51Sjruoho 	/*
4649c09c7fcSjruoho 	 * Search for the resource.
4659ddf2c51Sjruoho 	 */
4669ddf2c51Sjruoho 	res = acpi_power_res_get(hdl);
4679ddf2c51Sjruoho 
4689ddf2c51Sjruoho 	if (res == NULL)
4699ddf2c51Sjruoho 		return AE_NOT_FOUND;
4709ddf2c51Sjruoho 
47143337e89Sjruoho 	if (ref == NULL)
47243337e89Sjruoho 		return AE_BAD_PARAMETER;
47343337e89Sjruoho 
4749ddf2c51Sjruoho 	/*
47543337e89Sjruoho 	 * Adjust the reference counting. This is
47643337e89Sjruoho 	 * necessary since a single power resource
47743337e89Sjruoho 	 * can be shared by multiple devices.
4789ddf2c51Sjruoho 	 */
47956a181efSjoerg 	if (on) {
4809c09c7fcSjruoho 		rv = acpi_power_res_ref(res, ref);
4819c09c7fcSjruoho 		str = "_ON";
48256a181efSjoerg 	} else {
4839c09c7fcSjruoho 		rv = acpi_power_res_deref(res, ref);
4849c09c7fcSjruoho 		str = "_OFF";
4859ddf2c51Sjruoho 	}
4869ddf2c51Sjruoho 
4879ddf2c51Sjruoho 	if (ACPI_FAILURE(rv))
4889ddf2c51Sjruoho 		return rv;
4899ddf2c51Sjruoho 
4909c09c7fcSjruoho 	/*
4919c09c7fcSjruoho 	 * Turn the resource on or off.
4929c09c7fcSjruoho 	 */
4939c09c7fcSjruoho 	return AcpiEvaluateObject(res->res_handle, str, NULL, NULL);
4949ddf2c51Sjruoho }
4959ddf2c51Sjruoho 
4969ddf2c51Sjruoho static ACPI_STATUS
acpi_power_res_ref(struct acpi_power_res * res,ACPI_HANDLE ref)49743337e89Sjruoho acpi_power_res_ref(struct acpi_power_res *res, ACPI_HANDLE ref)
4989ddf2c51Sjruoho {
49943337e89Sjruoho 	size_t i, j = SIZE_MAX;
5009ddf2c51Sjruoho 
5019ddf2c51Sjruoho 	mutex_enter(&res->res_mutex);
5029ddf2c51Sjruoho 
50343337e89Sjruoho 	for (i = 0; i < __arraycount(res->res_ref); i++) {
5049ddf2c51Sjruoho 
50543337e89Sjruoho 		/*
50643337e89Sjruoho 		 * Do not error out if the handle
50743337e89Sjruoho 		 * has already been referenced.
50843337e89Sjruoho 		 */
50943337e89Sjruoho 		if (res->res_ref[i] == ref) {
51043337e89Sjruoho 			mutex_exit(&res->res_mutex);
51143337e89Sjruoho 			return AE_OK;
5129ddf2c51Sjruoho 		}
5139ddf2c51Sjruoho 
51443337e89Sjruoho 		if (j == SIZE_MAX && res->res_ref[i] == NULL)
51543337e89Sjruoho 			j = i;
51643337e89Sjruoho 	}
51743337e89Sjruoho 
51843337e89Sjruoho 	if (j == SIZE_MAX) {
51943337e89Sjruoho 		mutex_exit(&res->res_mutex);
52043337e89Sjruoho 		return AE_LIMIT;
52143337e89Sjruoho 	}
52243337e89Sjruoho 
52343337e89Sjruoho 	res->res_ref[j] = ref;
5249ddf2c51Sjruoho 	mutex_exit(&res->res_mutex);
5259ddf2c51Sjruoho 
526a84baa02Sjruoho 	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s referenced "
52743337e89Sjruoho 		"by %s\n", res->res_name, acpi_xname(ref)));
5289ddf2c51Sjruoho 
5299ddf2c51Sjruoho 	return AE_OK;
5309ddf2c51Sjruoho }
5319ddf2c51Sjruoho 
5329ddf2c51Sjruoho static ACPI_STATUS
acpi_power_res_deref(struct acpi_power_res * res,ACPI_HANDLE ref)53343337e89Sjruoho acpi_power_res_deref(struct acpi_power_res *res, ACPI_HANDLE ref)
5349ddf2c51Sjruoho {
53543337e89Sjruoho 	size_t i;
5369ddf2c51Sjruoho 
5379ddf2c51Sjruoho 	mutex_enter(&res->res_mutex);
5389ddf2c51Sjruoho 
53943337e89Sjruoho 	for (i = 0; i < __arraycount(res->res_ref); i++) {
5409ddf2c51Sjruoho 
54143337e89Sjruoho 		if (res->res_ref[i] != ref)
54243337e89Sjruoho 			continue;
5439ddf2c51Sjruoho 
54443337e89Sjruoho 		res->res_ref[i] = NULL;
5459ddf2c51Sjruoho 		mutex_exit(&res->res_mutex);
5469ddf2c51Sjruoho 
547a84baa02Sjruoho 		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s dereferenced "
54843337e89Sjruoho 			"by %s\n", res->res_name, acpi_xname(ref)));
5499ddf2c51Sjruoho 
5509ddf2c51Sjruoho 		return AE_OK;
5519ddf2c51Sjruoho 	}
5529ddf2c51Sjruoho 
55343337e89Sjruoho 	/*
55443337e89Sjruoho 	 * If the array remains to be non-empty,
55543337e89Sjruoho 	 * something else is using the resource
55643337e89Sjruoho 	 * and hence it can not be turned off.
55743337e89Sjruoho 	 */
55843337e89Sjruoho 	mutex_exit(&res->res_mutex);
55943337e89Sjruoho 
56043337e89Sjruoho 	return AE_ABORT_METHOD;
56143337e89Sjruoho }
56243337e89Sjruoho 
5639ddf2c51Sjruoho static ACPI_STATUS
acpi_power_res_sta(ACPI_OBJECT * elm,void * arg)5649ddf2c51Sjruoho acpi_power_res_sta(ACPI_OBJECT *elm, void *arg)
5659ddf2c51Sjruoho {
5669ddf2c51Sjruoho 	ACPI_INTEGER val;
5679ddf2c51Sjruoho 	ACPI_HANDLE hdl;
5689ddf2c51Sjruoho 	ACPI_STATUS rv;
5699ddf2c51Sjruoho 
5709ddf2c51Sjruoho 	rv = acpi_eval_reference_handle(elm, &hdl);
5719ddf2c51Sjruoho 
5729ddf2c51Sjruoho 	if (ACPI_FAILURE(rv))
5739ddf2c51Sjruoho 		goto fail;
5749ddf2c51Sjruoho 
5759ddf2c51Sjruoho 	rv = acpi_eval_integer(hdl, "_STA", &val);
5769ddf2c51Sjruoho 
5779ddf2c51Sjruoho 	if (ACPI_FAILURE(rv))
5789ddf2c51Sjruoho 		goto fail;
5799ddf2c51Sjruoho 
5809ddf2c51Sjruoho 	KDASSERT((uint64_t)val < INT_MAX);
5819ddf2c51Sjruoho 
5829ddf2c51Sjruoho 	if ((int)val != ACPI_STA_POW_ON && (int)val != ACPI_STA_POW_OFF)
5839ddf2c51Sjruoho 		return AE_BAD_VALUE;
5849ddf2c51Sjruoho 
5859ddf2c51Sjruoho 	if ((int)val != ACPI_STA_POW_ON)
586696f7c4bSjruoho 		return AE_CTRL_FALSE;		/* XXX: Not an error. */
5879ddf2c51Sjruoho 
5889ddf2c51Sjruoho 	return AE_OK;
5899ddf2c51Sjruoho 
5909ddf2c51Sjruoho fail:
591f655bf2aSjruoho 	if (rv == AE_CTRL_FALSE)
5929ddf2c51Sjruoho 		rv = AE_ERROR;
5939ddf2c51Sjruoho 
594a84baa02Sjruoho 	ACPI_DEBUG_PRINT((ACPI_DB_DEBUG_OBJECT, "failed to evaluate _STA "
595a84baa02Sjruoho 		"for %s: %s\n", acpi_xname(hdl), AcpiFormatException(rv)));
5969ddf2c51Sjruoho 
5979ddf2c51Sjruoho 	return rv;
5989ddf2c51Sjruoho }
5999ddf2c51Sjruoho 
6009ddf2c51Sjruoho static ACPI_OBJECT *
acpi_power_pkg_get(ACPI_HANDLE hdl,int state)6019ddf2c51Sjruoho acpi_power_pkg_get(ACPI_HANDLE hdl, int state)
6029ddf2c51Sjruoho {
6039ddf2c51Sjruoho 	char path[5] = "_PR?";
6049ddf2c51Sjruoho 	ACPI_OBJECT *obj;
6059ddf2c51Sjruoho 	ACPI_BUFFER buf;
6069ddf2c51Sjruoho 	ACPI_STATUS rv;
6079ddf2c51Sjruoho 
6089ddf2c51Sjruoho 	path[3] = '0' + state;
6099ddf2c51Sjruoho 
6109ddf2c51Sjruoho 	rv = acpi_eval_struct(hdl, path, &buf);
6119ddf2c51Sjruoho 
6129ddf2c51Sjruoho 	if (ACPI_FAILURE(rv))
6139ddf2c51Sjruoho 		goto fail;
6149ddf2c51Sjruoho 
6159ddf2c51Sjruoho 	if (buf.Length == 0) {
6169ddf2c51Sjruoho 		rv = AE_LIMIT;
6179ddf2c51Sjruoho 		goto fail;
6189ddf2c51Sjruoho 	}
6199ddf2c51Sjruoho 
6209ddf2c51Sjruoho 	obj = buf.Pointer;
6219ddf2c51Sjruoho 
6229ddf2c51Sjruoho 	if (obj->Type != ACPI_TYPE_PACKAGE) {
6239ddf2c51Sjruoho 		rv = AE_TYPE;
6249ddf2c51Sjruoho 		goto fail;
6259ddf2c51Sjruoho 	}
6269ddf2c51Sjruoho 
6279ddf2c51Sjruoho 	if (obj->Package.Count == 0) {
6289ddf2c51Sjruoho 		rv = AE_LIMIT;
6299ddf2c51Sjruoho 		goto fail;
6309ddf2c51Sjruoho 	}
6319ddf2c51Sjruoho 
6329ddf2c51Sjruoho 	return obj;
6339ddf2c51Sjruoho 
6349ddf2c51Sjruoho fail:
6359ddf2c51Sjruoho 	if (buf.Pointer != NULL)
6369ddf2c51Sjruoho 		ACPI_FREE(buf.Pointer);
6379ddf2c51Sjruoho 
638a84baa02Sjruoho 	ACPI_DEBUG_PRINT((ACPI_DB_DEBUG_OBJECT, "failed to evaluate %s for "
639a84baa02Sjruoho 		"%s: %s\n", path, acpi_xname(hdl), AcpiFormatException(rv)));
6409ddf2c51Sjruoho 
6419ddf2c51Sjruoho 	return NULL;
6429ddf2c51Sjruoho }
6439ddf2c51Sjruoho 
64416bdc3ffSjruoho SYSCTL_SETUP(sysctl_acpi_power_setup, "sysctl hw.acpi.power subtree setup")
64516bdc3ffSjruoho {
646646cb7e2Sgsutre 	const struct sysctlnode *anode;
64716bdc3ffSjruoho 	int err;
64816bdc3ffSjruoho 
64916bdc3ffSjruoho 	err = sysctl_createv(NULL, 0, NULL, &anode,
65016bdc3ffSjruoho 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "acpi",
65116bdc3ffSjruoho 	    NULL, NULL, 0, NULL, 0,
6524f6fb3bfSpooka 	    CTL_HW, CTL_CREATE, CTL_EOL);
65316bdc3ffSjruoho 
65416bdc3ffSjruoho 	if (err != 0)
655646cb7e2Sgsutre 		return;
656646cb7e2Sgsutre 
657646cb7e2Sgsutre 	acpi_power_acpinode = anode->sysctl_num;
65816bdc3ffSjruoho 
65916bdc3ffSjruoho 	err = sysctl_createv(NULL, 0, &anode, &anode,
66016bdc3ffSjruoho 	    CTLFLAG_PERMANENT, CTLTYPE_NODE,
66116bdc3ffSjruoho 	    "power", SYSCTL_DESCR("ACPI device power states"),
66216bdc3ffSjruoho 	    NULL, 0, NULL, 0,
66316bdc3ffSjruoho 	    CTL_CREATE, CTL_EOL);
66416bdc3ffSjruoho 
66516bdc3ffSjruoho 	if (err != 0)
66616bdc3ffSjruoho 		return;
66716bdc3ffSjruoho 
668646cb7e2Sgsutre 	acpi_power_powernode = anode->sysctl_num;
66916bdc3ffSjruoho }
67016bdc3ffSjruoho 
67116bdc3ffSjruoho void
acpi_power_add(struct acpi_devnode * ad)67216bdc3ffSjruoho acpi_power_add(struct acpi_devnode *ad)
67316bdc3ffSjruoho {
674e008cf16Sjruoho 	const char *str = NULL;
67516bdc3ffSjruoho 	int err;
67616bdc3ffSjruoho 
67716bdc3ffSjruoho 	KASSERT(ad != NULL && ad->ad_root != NULL);
67816bdc3ffSjruoho 	KASSERT((ad->ad_flags & ACPI_DEVICE_POWER) != 0);
67916bdc3ffSjruoho 
680646cb7e2Sgsutre 	if (acpi_power_acpinode == CTL_EOL ||
681646cb7e2Sgsutre 	    acpi_power_powernode == CTL_EOL)
68216bdc3ffSjruoho 		return;
68316bdc3ffSjruoho 
684e008cf16Sjruoho 	if (ad->ad_device != NULL)
685e008cf16Sjruoho 		str = device_xname(ad->ad_device);
686*ef019da3Smrg #if NPCI > 0
687e008cf16Sjruoho 	else {
688*ef019da3Smrg 		device_t dev = acpi_pcidev_find_dev(ad);
689e008cf16Sjruoho 
690e008cf16Sjruoho 		if (dev != NULL)
691e008cf16Sjruoho 			str = device_xname(dev);
692e008cf16Sjruoho 	}
693*ef019da3Smrg #endif
694e008cf16Sjruoho 
695e008cf16Sjruoho 	if (str == NULL)
696e008cf16Sjruoho 		return;
697e008cf16Sjruoho 
698646cb7e2Sgsutre 	err = sysctl_createv(NULL, 0, NULL, NULL,
699e008cf16Sjruoho 	    CTLFLAG_READONLY, CTLTYPE_STRING, str,
700e21a34c2Sdsl 	    NULL, acpi_power_sysctl, 0, (void *)ad, 0, CTL_HW,
701e008cf16Sjruoho 	    acpi_power_acpinode, acpi_power_powernode,
70216bdc3ffSjruoho 	    CTL_CREATE, CTL_EOL);
70316bdc3ffSjruoho 
70416bdc3ffSjruoho 	if (err != 0)
70516bdc3ffSjruoho 		aprint_error_dev(ad->ad_root, "sysctl_createv"
7066248a51dSjruoho 		    "(hw.acpi.power.%s) failed (err %d)\n", str, err);
70716bdc3ffSjruoho }
70816bdc3ffSjruoho 
70916bdc3ffSjruoho static int
acpi_power_sysctl(SYSCTLFN_ARGS)71016bdc3ffSjruoho acpi_power_sysctl(SYSCTLFN_ARGS)
71116bdc3ffSjruoho {
712ba4f1cd0Sjruoho 	struct acpi_devnode *ad;
71316bdc3ffSjruoho 	struct sysctlnode node;
71416bdc3ffSjruoho 	int err, state;
71516bdc3ffSjruoho 	char t[3];
71616bdc3ffSjruoho 
71716bdc3ffSjruoho 	node = *rnode;
718ba4f1cd0Sjruoho 	ad = rnode->sysctl_data;
719ba4f1cd0Sjruoho 
7209cb2ef6cSjruoho 	if (acpi_power_get(ad->ad_handle, &state) != true)
721ba4f1cd0Sjruoho 		state = 0;
72216bdc3ffSjruoho 
72316bdc3ffSjruoho 	(void)memset(t, '\0', sizeof(t));
72416bdc3ffSjruoho 	(void)snprintf(t, sizeof(t), "D%d", state);
72516bdc3ffSjruoho 
72616bdc3ffSjruoho 	node.sysctl_data = &t;
72716bdc3ffSjruoho 
72816bdc3ffSjruoho 	err = sysctl_lookup(SYSCTLFN_CALL(&node));
72916bdc3ffSjruoho 
73016bdc3ffSjruoho 	if (err || newp == NULL)
73116bdc3ffSjruoho 		return err;
73216bdc3ffSjruoho 
73316bdc3ffSjruoho 	return 0;
73416bdc3ffSjruoho }
73516bdc3ffSjruoho 
7369ddf2c51Sjruoho /*
7379ddf2c51Sjruoho  * XXX: Move this to acpi_util.c by refactoring
7389ddf2c51Sjruoho  *	acpi_name() to optionally return a single name.
7399ddf2c51Sjruoho  */
7409ddf2c51Sjruoho static const char *
acpi_xname(ACPI_HANDLE hdl)7419ddf2c51Sjruoho acpi_xname(ACPI_HANDLE hdl)
7429ddf2c51Sjruoho {
7439ddf2c51Sjruoho 	static char str[5];
7449ddf2c51Sjruoho 	ACPI_BUFFER buf;
7459ddf2c51Sjruoho 	ACPI_STATUS rv;
7469ddf2c51Sjruoho 
7479ddf2c51Sjruoho 	buf.Pointer = str;
7489ddf2c51Sjruoho 	buf.Length = sizeof(str);
7499ddf2c51Sjruoho 
7509ddf2c51Sjruoho 	rv = AcpiGetName(hdl, ACPI_SINGLE_NAME, &buf);
7519ddf2c51Sjruoho 
7529ddf2c51Sjruoho 	if (ACPI_FAILURE(rv))
7539ddf2c51Sjruoho 		return "????";
7549ddf2c51Sjruoho 
7559ddf2c51Sjruoho 	return str;
7569ddf2c51Sjruoho }
757