From 9ddf2c516535a5aeee48cc6f488acdf8804e2aeb Mon Sep 17 00:00:00 2001 From: jruoho Date: Thu, 22 Apr 2010 18:40:09 +0000 Subject: Merge new code for ACPI power resources. The old code served us well, but a major overhaul would have been needed for it to cope with the increased demands of the code -- and the specifications. ok jmcneill@, pgoyette@ --- sys/dev/acpi/acpi.c | 32 +- sys/dev/acpi/acpi_power.c | 733 +++++++++++++++++++++++++++++++++++++++++++ sys/dev/acpi/acpi_power.h | 44 +++ sys/dev/acpi/acpi_powerres.c | 652 -------------------------------------- sys/dev/acpi/acpi_tz.c | 14 +- sys/dev/acpi/acpivar.h | 9 +- sys/dev/acpi/files.acpi | 4 +- 7 files changed, 813 insertions(+), 675 deletions(-) create mode 100644 sys/dev/acpi/acpi_power.c create mode 100644 sys/dev/acpi/acpi_power.h delete mode 100644 sys/dev/acpi/acpi_powerres.c (limited to 'sys/dev') diff --git a/sys/dev/acpi/acpi.c b/sys/dev/acpi/acpi.c index b73e1f77708..a8507bc8a3a 100644 --- a/sys/dev/acpi/acpi.c +++ b/sys/dev/acpi/acpi.c @@ -1,4 +1,4 @@ -/* $NetBSD: acpi.c,v 1.180 2010/04/22 14:50:30 jruoho Exp $ */ +/* $NetBSD: acpi.c,v 1.181 2010/04/22 18:40:09 jruoho Exp $ */ /*- * Copyright (c) 2003, 2007 The NetBSD Foundation, Inc. @@ -65,7 +65,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: acpi.c,v 1.180 2010/04/22 14:50:30 jruoho Exp $"); +__KERNEL_RCSID(0, "$NetBSD: acpi.c,v 1.181 2010/04/22 18:40:09 jruoho Exp $"); #include "opt_acpi.h" #include "opt_pcifixup.h" @@ -82,6 +82,7 @@ __KERNEL_RCSID(0, "$NetBSD: acpi.c,v 1.180 2010/04/22 14:50:30 jruoho Exp $"); #include #include #include +#include #include #include @@ -979,10 +980,17 @@ acpi_rescan_nodes(struct acpi_softc *sc) /* * Handled internally. */ - if (ad->ad_devinfo->Type == ACPI_TYPE_PROCESSOR || - ad->ad_devinfo->Type == ACPI_TYPE_POWER) + if (ad->ad_devinfo->Type == ACPI_TYPE_PROCESSOR) continue; + /* + * Ditto, but bind power resources. + */ + if (ad->ad_devinfo->Type == ACPI_TYPE_POWER) { + acpi_power_res_add(ad); + continue; + } + /* * Skip ignored HIDs. */ @@ -1019,14 +1027,16 @@ acpi_rescan_capabilities(struct acpi_softc *sc) /* * Scan power resource capabilities. + * + * If any power states are supported, + * at least _PR0 and _PR3 must be present. */ rv = AcpiGetHandle(ad->ad_handle, "_PR0", &tmp); - if (ACPI_FAILURE(rv)) - rv = AcpiGetHandle(ad->ad_handle, "_PSC", &tmp); - - if (ACPI_SUCCESS(rv)) + if (ACPI_SUCCESS(rv)) { ad->ad_flags |= ACPI_DEVICE_POWER; + (void)acpi_power_get(ad, NULL); + } /* * Scan wake-up capabilities. @@ -1041,12 +1051,12 @@ acpi_rescan_capabilities(struct acpi_softc *sc) if (ad->ad_flags != 0) { aprint_debug_dev(sc->sc_dev, "%-5s ", ad->ad_name); - if ((ad->ad_flags & ACPI_DEVICE_POWER) != 0) - aprint_debug("power "); - if ((ad->ad_flags & ACPI_DEVICE_WAKEUP) != 0) aprint_debug("wake-up "); + if ((ad->ad_flags & ACPI_DEVICE_POWER) != 0) + aprint_debug("power (D%d) ", ad->ad_state); + aprint_debug("\n"); } } diff --git a/sys/dev/acpi/acpi_power.c b/sys/dev/acpi/acpi_power.c new file mode 100644 index 00000000000..5e8220c990a --- /dev/null +++ b/sys/dev/acpi/acpi_power.c @@ -0,0 +1,733 @@ +/* $NetBSD: acpi_power.c,v 1.1 2010/04/22 18:40:09 jruoho Exp $ */ + +/*- + * Copyright (c) 2009, 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jukka Ruohonen. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/*- + * Copyright (c) 2001 Michael Smith + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__KERNEL_RCSID(0, "$NetBSD: acpi_power.c,v 1.1 2010/04/22 18:40:09 jruoho Exp $"); + +#include +#include +#include + +#include +#include +#include + +#define _COMPONENT ACPI_BUS_COMPONENT +ACPI_MODULE_NAME ("acpi_power") + +/* + * References. + */ +struct acpi_power_ref { + ACPI_HANDLE ref_handle; + + SIMPLEQ_ENTRY(acpi_power_ref) ref_list; +}; + +/* + * Resources. + */ +struct acpi_power_res { + ACPI_HANDLE res_handle; + ACPI_INTEGER res_level; + ACPI_INTEGER res_order; + char res_name[5]; + kmutex_t res_mutex; + + TAILQ_ENTRY(acpi_power_res) res_list; + SIMPLEQ_HEAD(, acpi_power_ref) ref_head; +}; + +static TAILQ_HEAD(, acpi_power_res) res_head = + TAILQ_HEAD_INITIALIZER(res_head); + +static struct acpi_power_res *acpi_power_res_init(ACPI_HANDLE); +static struct acpi_power_res *acpi_power_res_get(ACPI_HANDLE); + +#if 0 +static ACPI_STATUS acpi_power_get_direct(struct acpi_devnode *); +#endif + +static ACPI_STATUS acpi_power_get_indirect(struct acpi_devnode *); +static ACPI_STATUS acpi_power_switch(struct acpi_devnode *, + int, bool); +static ACPI_STATUS acpi_power_res_on(ACPI_OBJECT *, void *); +static ACPI_STATUS acpi_power_res_off(ACPI_OBJECT *, void *); +static ACPI_STATUS acpi_power_res_ref(struct acpi_power_res *, + ACPI_HANDLE); +static ACPI_STATUS acpi_power_res_deref(struct acpi_power_res *, + ACPI_HANDLE); +static ACPI_STATUS acpi_power_res_sta(ACPI_OBJECT *, void *); + +static ACPI_OBJECT *acpi_power_pkg_get(ACPI_HANDLE, int); +static const char *acpi_xname(ACPI_HANDLE); + +void +acpi_power_res_add(struct acpi_devnode *ad) +{ + struct acpi_power_res *res; + + KASSERT(ad != NULL && ad->ad_root != NULL); + KASSERT(ad->ad_devinfo->Type == ACPI_TYPE_POWER); + + res = acpi_power_res_init(ad->ad_handle); + + if (res == NULL) + aprint_error_dev(ad->ad_root, "failed to " + "add power resource %s\n", ad->ad_name); +} + +static struct acpi_power_res * +acpi_power_res_init(ACPI_HANDLE hdl) +{ + struct acpi_power_res *tmp = NULL; + struct acpi_power_res *res = NULL; + ACPI_OBJECT *obj; + ACPI_BUFFER buf; + ACPI_STATUS rv; + + rv = acpi_eval_struct(hdl, NULL, &buf); + + if (ACPI_FAILURE(rv)) + goto out; + + obj = buf.Pointer; + + if (obj->Type != ACPI_TYPE_POWER) { + rv = AE_TYPE; + goto out; + } + + res = kmem_zalloc(sizeof(*res), KM_SLEEP); + + if (res == NULL) { + rv = AE_NO_MEMORY; + goto out; + } + + res->res_handle = hdl; + res->res_level = obj->PowerResource.SystemLevel; + res->res_order = obj->PowerResource.ResourceOrder; + + (void)strlcpy(res->res_name, + acpi_xname(hdl), sizeof(res->res_name)); + + SIMPLEQ_INIT(&res->ref_head); + mutex_init(&res->res_mutex, MUTEX_DEFAULT, IPL_NONE); + + /* + * Power resources should be ordered. + * + * These *should* be enabled from low values to high + * values and disabled from high values to low values. + */ + TAILQ_FOREACH(tmp, &res_head, res_list) { + + if (res->res_order < tmp->res_order) { + TAILQ_INSERT_BEFORE(tmp, res, res_list); + break; + } + } + + if (tmp == NULL) + TAILQ_INSERT_TAIL(&res_head, res, res_list); + +out: + if (buf.Pointer != NULL) + ACPI_FREE(buf.Pointer); + + return res; +} + +static struct acpi_power_res * +acpi_power_res_get(ACPI_HANDLE hdl) +{ + struct acpi_power_res *res; + + TAILQ_FOREACH(res, &res_head, res_list) { + + if (res->res_handle == hdl) + return res; + } + + return acpi_power_res_init(hdl); +} + +bool +acpi_power_register(struct acpi_devnode *ad) +{ + + KASSERT(ad != NULL && ad->ad_root != NULL); + + if ((ad->ad_flags & ACPI_DEVICE_POWER) == 0) + return false; + + return true; +} + +void +acpi_power_deregister(struct acpi_devnode *ad) +{ + struct acpi_power_res *res; + + KASSERT(ad != NULL && ad->ad_root != NULL); + + if ((ad->ad_flags & ACPI_DEVICE_POWER) == 0) + return; + + /* + * Remove all references in each resource. + */ + TAILQ_FOREACH(res, &res_head, res_list) + (void)acpi_power_res_deref(res, ad->ad_handle); +} + +/* + * Get the D-state of an ACPI device node. + */ +bool +acpi_power_get(struct acpi_devnode *ad, int *state) +{ + ACPI_STATUS rv; + + if ((ad->ad_flags & ACPI_DEVICE_POWER) == 0) { + rv = AE_SUPPORT; + goto fail; + } + + rv = acpi_power_get_indirect(ad); + + if (ACPI_FAILURE(rv)) + goto fail; + + KASSERT(ad->ad_state != ACPI_STATE_ERROR); + + if (ad->ad_state < ACPI_STATE_D0 || ad->ad_state > ACPI_STATE_D3) { + rv = AE_BAD_VALUE; + goto fail; + } + + if (state != NULL) + *state = ad->ad_state; + + return true; + +fail: + ad->ad_state = ACPI_STATE_ERROR; + + if (state != NULL) + *state = ad->ad_state; + + aprint_error_dev(ad->ad_root, "failed to get power state " + "for %s: %s\n", ad->ad_name, AcpiFormatException(rv)); + + return false; +} + +#if 0 +/* + * Unfortunately, comparable to _STA, there are systems + * where the convenient _PSC is implemented incorrectly. + */ +static ACPI_STATUS +acpi_power_get_direct(struct acpi_devnode *ad) +{ + ACPI_INTEGER val = 0; + ACPI_STATUS rv; + + rv = acpi_eval_integer(ad->ad_handle, "_PSC", &val); + + KDASSERT((uint64_t)val < INT_MAX); + + ad->ad_state = (int)val; + + return rv; +} +#endif + +static ACPI_STATUS +acpi_power_get_indirect(struct acpi_devnode *ad) +{ + ACPI_OBJECT *pkg; + ACPI_STATUS rv; + int i; + + CTASSERT(ACPI_STATE_D0 == 0 && ACPI_STATE_D1 == 1); + CTASSERT(ACPI_STATE_D2 == 2 && ACPI_STATE_D3 == 3); + + /* + * The device is in a given D-state if all resources are on. + * To derive this, evaluate all elements in each _PRx package + * (x = 0 ... 3) and break if the noted condition becomes true. + */ + for (ad->ad_state = ACPI_STATE_D3, i = 0; i < ACPI_STATE_D3; i++) { + + pkg = acpi_power_pkg_get(ad->ad_handle, i); + + if (pkg == NULL) + continue; + + /* + * For each element in the _PRx package, evaluate _STA + * and return AE_OK only if all power resources are on. + */ + rv = acpi_foreach_package_object(pkg, acpi_power_res_sta, ad); + + if (ACPI_FAILURE(rv) && rv != AE_ABORT_METHOD) + goto out; + + if (ACPI_SUCCESS(rv)) { + ad->ad_state = i; + goto out; + } + + ACPI_FREE(pkg); pkg = NULL; + } + + KASSERT(ad->ad_state == ACPI_STATE_D3); + + return AE_OK; + +out: + ACPI_FREE(pkg); + + return rv; +} + +/* + * Set the D-state of an ACPI device node. + */ +bool +acpi_power_set(struct acpi_devnode *ad, int state) +{ + ACPI_STATUS rv; + char path[5]; + int old; + + KASSERT(ad != NULL && ad->ad_root != NULL); + + if (state < ACPI_STATE_D0 || state > ACPI_STATE_D3) { + rv = AE_BAD_PARAMETER; + goto fail; + } + + if ((ad->ad_flags & ACPI_DEVICE_POWER) == 0) { + rv = AE_SUPPORT; + goto fail; + } + + if (acpi_power_get(ad, &old) != true) { + rv = AE_NOT_FOUND; + goto fail; + } + + KASSERT(ad->ad_state == old); + KASSERT(ad->ad_state != ACPI_STATE_ERROR); + + if (ad->ad_state == state) { + rv = AE_ALREADY_EXISTS; + goto fail; + } + + /* + * It is only possible to go to D0 ("on") from D3 ("off"). + */ + if (ad->ad_state == ACPI_STATE_D3 && state != ACPI_STATE_D0) { + rv = AE_BAD_PARAMETER; + goto fail; + } + + /* + * We first sweep through the resources required + * for the target state, turning things on and + * building references. After this we dereference + * the resources required for the current state, + * turning the resources off as we go. + */ + rv = acpi_power_switch(ad, state, true); + + if (ACPI_FAILURE(rv)) + goto fail; + + rv = acpi_power_switch(ad, ad->ad_state, false); + + if (ACPI_FAILURE(rv)) + goto fail; + + ad->ad_state = state; + + /* + * Last but not least, invoke the power + * state switch method, if available. + */ + (void)snprintf(path, sizeof(path), "_PS%d", state); + (void)AcpiEvaluateObject(ad->ad_handle, path, NULL, NULL); + + aprint_debug_dev(ad->ad_root, "%s turned from " + "D%d to D%d\n", ad->ad_name, old, state); + + return true; + +fail: + /* + * It is never an error to go to D0. + */ + if (state != ACPI_STATE_D0) { + + aprint_error_dev(ad->ad_root, + "failed to set power state to D%d for %s: %s\n", + state, ad->ad_name, AcpiFormatException(rv)); + + ad->ad_state = ACPI_STATE_ERROR; + + return false; + } + + return true; +} + +/* + * Set the D-state of an ACPI device node from a handle. + */ +bool +acpi_power_set_from_handle(ACPI_HANDLE hdl, int state) +{ + struct acpi_softc *sc = acpi_softc; /* XXX. */ + struct acpi_devnode *ad; + + if (sc == NULL) + return false; + + SIMPLEQ_FOREACH(ad, &sc->ad_head, ad_list) { + + if (ad->ad_handle == hdl) + return acpi_power_set(ad, state); + } + + aprint_error_dev(sc->sc_dev, "%s: failed to " + "find node %s\n", __func__, ad->ad_name); + + return false; +} + +static ACPI_STATUS +acpi_power_switch(struct acpi_devnode *ad, int state, bool on) +{ + ACPI_OBJECT *pkg; + ACPI_STATUS rv; + + pkg = acpi_power_pkg_get(ad->ad_handle, state); + + if (pkg == NULL) + return AE_NOT_EXIST; + + if (on != false) + rv = acpi_foreach_package_object(pkg, acpi_power_res_on, ad); + else + rv = acpi_foreach_package_object(pkg, acpi_power_res_off, ad); + + ACPI_FREE(pkg); + + return rv; +} + +static ACPI_STATUS +acpi_power_res_on(ACPI_OBJECT *elm, void *arg) +{ + struct acpi_devnode *ad = arg; + struct acpi_power_res *res; + ACPI_HANDLE hdl; + ACPI_STATUS rv; + + /* + * For each element in the _PRx package, first + * fetch the reference handle and then search + * for this handle from the power resource list. + */ + rv = acpi_eval_reference_handle(elm, &hdl); + + if (ACPI_FAILURE(rv)) + return rv; + + res = acpi_power_res_get(hdl); + + if (res == NULL) + return AE_NOT_FOUND; + + /* + * Reference the resource. + */ + rv = acpi_power_res_ref(res, ad->ad_handle); + + if (ACPI_FAILURE(rv)) + return rv; + + /* + * Turn the resource on. + */ + return AcpiEvaluateObject(res->res_handle, "_ON", NULL, NULL); +} + +static ACPI_STATUS +acpi_power_res_off(ACPI_OBJECT *elm, void *arg) +{ + struct acpi_devnode *ad = arg; + struct acpi_power_res *res; + ACPI_HANDLE hdl; + ACPI_STATUS rv; + + rv = acpi_eval_reference_handle(elm, &hdl); + + if (ACPI_FAILURE(rv)) + return rv; + + res = acpi_power_res_get(hdl); + + if (res == NULL) + return AE_NOT_FOUND; + + rv = acpi_power_res_deref(res, ad->ad_handle); + + if (ACPI_FAILURE(rv)) + return rv; + + return AcpiEvaluateObject(res->res_handle, "_OFF", NULL, NULL); +} + +static ACPI_STATUS +acpi_power_res_ref(struct acpi_power_res *res, ACPI_HANDLE hdl) +{ + struct acpi_power_ref *ref, *tmp; + + ref = kmem_zalloc(sizeof(*ref), KM_SLEEP); + + if (ref == NULL) + return AE_NO_MEMORY; + + mutex_enter(&res->res_mutex); + + SIMPLEQ_FOREACH(tmp, &res->ref_head, ref_list) { + + if (tmp->ref_handle == hdl) + goto out; + } + + ref->ref_handle = hdl; + SIMPLEQ_INSERT_TAIL(&res->ref_head, ref, ref_list); + mutex_exit(&res->res_mutex); + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s: %s referenced " + "by %s?\n", __func__, res->res_name, acpi_xname(hdl))); + + return AE_OK; + +out: + mutex_exit(&res->res_mutex); + kmem_free(ref, sizeof(*ref)); + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s: %s already referenced " + "by %s?\n", __func__, res->res_name, acpi_xname(hdl))); + + return AE_OK; +} + +static ACPI_STATUS +acpi_power_res_deref(struct acpi_power_res *res, ACPI_HANDLE hdl) +{ + struct acpi_power_ref *ref; + + mutex_enter(&res->res_mutex); + + if (SIMPLEQ_EMPTY(&res->ref_head) != 0) { + mutex_exit(&res->res_mutex); + return AE_OK; + } + + SIMPLEQ_FOREACH(ref, &res->ref_head, ref_list) { + + if (ref->ref_handle == hdl) { + SIMPLEQ_REMOVE(&res->ref_head, + ref, acpi_power_ref, ref_list); + mutex_exit(&res->res_mutex); + kmem_free(ref, sizeof(*ref)); + mutex_enter(&res->res_mutex); + break; + } + } + + /* + * If the queue remains non-empty, + * something else is using the resource + * and hence it can not be turned off. + */ + if (SIMPLEQ_EMPTY(&res->ref_head) == 0) { + mutex_exit(&res->res_mutex); + return AE_ABORT_METHOD; + } + + mutex_exit(&res->res_mutex); + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s: %s dereferenced " + "by %s?\n", __func__, res->res_name, acpi_xname(hdl))); + + return AE_OK; +} + +static ACPI_STATUS +acpi_power_res_sta(ACPI_OBJECT *elm, void *arg) +{ + ACPI_INTEGER val; + ACPI_HANDLE hdl; + ACPI_STATUS rv; + + rv = acpi_eval_reference_handle(elm, &hdl); + + if (ACPI_FAILURE(rv)) + goto fail; + + rv = acpi_eval_integer(hdl, "_STA", &val); + + if (ACPI_FAILURE(rv)) + goto fail; + + KDASSERT((uint64_t)val < INT_MAX); + + if ((int)val != ACPI_STA_POW_ON && (int)val != ACPI_STA_POW_OFF) + return AE_BAD_VALUE; + + if ((int)val != ACPI_STA_POW_ON) + return AE_ABORT_METHOD; /* XXX: Not an error. */ + + return AE_OK; + +fail: + if (rv == AE_ABORT_METHOD) + rv = AE_ERROR; + + ACPI_DEBUG_PRINT((ACPI_DB_DEBUG_OBJECT, "%s: failed to " + "evaluate _STA for %s: %s\n", __func__, + acpi_xname(hdl), AcpiFormatException(rv))); + + return rv; +} + +static ACPI_OBJECT * +acpi_power_pkg_get(ACPI_HANDLE hdl, int state) +{ + char path[5] = "_PR?"; + ACPI_OBJECT *obj; + ACPI_BUFFER buf; + ACPI_STATUS rv; + + path[3] = '0' + state; + + rv = acpi_eval_struct(hdl, path, &buf); + + if (ACPI_FAILURE(rv)) + goto fail; + + if (buf.Length == 0) { + rv = AE_LIMIT; + goto fail; + } + + obj = buf.Pointer; + + if (obj->Type != ACPI_TYPE_PACKAGE) { + rv = AE_TYPE; + goto fail; + } + + if (obj->Package.Count == 0) { + rv = AE_LIMIT; + goto fail; + } + + return obj; + +fail: + if (buf.Pointer != NULL) + ACPI_FREE(buf.Pointer); + + ACPI_DEBUG_PRINT((ACPI_DB_DEBUG_OBJECT, "%s: failed to " + "evaluate %s for %s: %s\n", __func__, path, + acpi_xname(hdl), AcpiFormatException(rv))); + + return NULL; +} + +/* + * XXX: Move this to acpi_util.c by refactoring + * acpi_name() to optionally return a single name. + */ +static const char * +acpi_xname(ACPI_HANDLE hdl) +{ + static char str[5]; + ACPI_BUFFER buf; + ACPI_STATUS rv; + + buf.Pointer = str; + buf.Length = sizeof(str); + + rv = AcpiGetName(hdl, ACPI_SINGLE_NAME, &buf); + + if (ACPI_FAILURE(rv)) + return "????"; + + return str; +} diff --git a/sys/dev/acpi/acpi_power.h b/sys/dev/acpi/acpi_power.h new file mode 100644 index 00000000000..a0495e7aadb --- /dev/null +++ b/sys/dev/acpi/acpi_power.h @@ -0,0 +1,44 @@ +/* $NetBSD: acpi_power.h,v 1.1 2010/04/22 18:40:09 jruoho Exp $ */ + +/*- + * Copyright (c) 2009, 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jukka Ruohonen. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_DEV_ACPI_ACPI_POWER_H +#define _SYS_DEV_ACPI_ACPI_POWER_H + +#define ACPI_STATE_ERROR -1 + +void acpi_power_res_add(struct acpi_devnode *); +bool acpi_power_register(struct acpi_devnode *); +void acpi_power_deregister(struct acpi_devnode *); +bool acpi_power_get(struct acpi_devnode *, int *); +bool acpi_power_set(struct acpi_devnode *, int); +bool acpi_power_set_from_handle(ACPI_HANDLE, int); + +#endif /* !_SYS_DEV_ACPI_ACPI_POWER_H */ diff --git a/sys/dev/acpi/acpi_powerres.c b/sys/dev/acpi/acpi_powerres.c deleted file mode 100644 index fe78b985143..00000000000 --- a/sys/dev/acpi/acpi_powerres.c +++ /dev/null @@ -1,652 +0,0 @@ -/* $NetBSD: acpi_powerres.c,v 1.12 2010/04/10 18:32:13 jruoho Exp $ */ - -/*- - * Copyright (c) 2001 Michael Smith - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $FreeBSD: src/sys/dev/acpica/acpi_powerres.c,v 1.14 2002/10/16 17:28:52 jhb Exp $ - */ - -#include -__KERNEL_RCSID(0, "$NetBSD: acpi_powerres.c,v 1.12 2010/04/10 18:32:13 jruoho Exp $"); - -#include -#include - -#include -#include - -/* - * ACPI power resource management. - * - * Power resource behaviour is slightly complicated by the fact that - * a single power resource may provide power for more than one device. - * Thus, we must track the device(s) being powered by a given power - * resource, and only deactivate it when there are no powered devices. - * - * Note that this only manages resources for known devices. There is an - * ugly case where we may turn of power to a device which is in use because - * we don't know that it depends on a given resource. We should perhaps - * try to be smarter about this, but a more complete solution would involve - * scanning all of the ACPI namespace to find devices we're not currently - * aware of, and this raises questions about whether they should be left - * on, turned off, etc. - * - * XXX locking - */ - -MALLOC_DEFINE(M_ACPIPWR, "acpipwr", "ACPI power resources"); - -/* - * Hooks for the ACPI CA debugging infrastructure - */ -#define _COMPONENT ACPI_BUS_COMPONENT -ACPI_MODULE_NAME("POWERRES") - -/* - * A relationship between a power resource and a consumer. - */ -struct acpi_powerreference { - struct acpi_powerconsumer *ar_consumer; - struct acpi_powerresource *ar_resource; - TAILQ_ENTRY(acpi_powerreference) ar_rlink; /* link on resource list */ - TAILQ_ENTRY(acpi_powerreference) ar_clink; /* link on consumer */ -}; - -/* - * A power-managed device. - */ -struct acpi_powerconsumer { - ACPI_HANDLE ac_consumer; /* device which is powered */ - int ac_state; - TAILQ_ENTRY(acpi_powerconsumer) ac_link; - TAILQ_HEAD(,acpi_powerreference) ac_references; -}; - -/* - * A power resource. - */ -struct acpi_powerresource { - TAILQ_ENTRY(acpi_powerresource) ap_link; - TAILQ_HEAD(,acpi_powerreference) ap_references; - ACPI_HANDLE ap_resource; /* the resource's handle */ - ACPI_INTEGER ap_systemlevel; - ACPI_INTEGER ap_order; -}; - -static TAILQ_HEAD(acpi_powerresource_list, acpi_powerresource) - acpi_powerresources = TAILQ_HEAD_INITIALIZER(acpi_powerresources); -static TAILQ_HEAD(acpi_powerconsumer_list, acpi_powerconsumer) - acpi_powerconsumers = TAILQ_HEAD_INITIALIZER(acpi_powerconsumers); - -static ACPI_STATUS acpi_pwr_register_consumer(ACPI_HANDLE); -#if 0 -static ACPI_STATUS acpi_pwr_register_resource(ACPI_HANDLE); -static ACPI_STATUS acpi_pwr_deregister_consumer(ACPI_HANDLE); -static ACPI_STATUS acpi_pwr_deregister_resource(ACPI_HANDLE); -#endif -static ACPI_STATUS acpi_pwr_reference_resource(ACPI_OBJECT *, void *); -static ACPI_STATUS acpi_pwr_switch_power(void); -static struct acpi_powerresource *acpi_pwr_find_resource(ACPI_HANDLE); -static struct acpi_powerconsumer *acpi_pwr_find_consumer(ACPI_HANDLE); - -/* - * Register a power resource. - * - * It's OK to call this if we already know about the resource. - */ -static ACPI_STATUS -acpi_pwr_register_resource(ACPI_HANDLE res) -{ - ACPI_STATUS status; - ACPI_BUFFER buf; - ACPI_OBJECT *obj; - struct acpi_powerresource *rp, *srp; - - ACPI_FUNCTION_TRACE(__func__); - - rp = NULL; - buf.Pointer = NULL; - - /* look to see if we know about this resource */ - if (acpi_pwr_find_resource(res) != NULL) - return_ACPI_STATUS(AE_OK); /* already know about it */ - - /* allocate a new resource */ - if ((rp = malloc(sizeof(*rp), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) { - status = AE_NO_MEMORY; - goto out; - } - TAILQ_INIT(&rp->ap_references); - rp->ap_resource = res; - - /* get the Power Resource object */ - status = acpi_eval_struct(res, NULL, &buf); - if (ACPI_FAILURE(status)) { - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, - "no power resource object\n")); - goto out; - } - obj = buf.Pointer; - if (obj->Type != ACPI_TYPE_POWER) { - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, - "questionable power resource object %s\n", - acpi_name(res))); - status = AE_TYPE; - goto out; - } - rp->ap_systemlevel = obj->PowerResource.SystemLevel; - rp->ap_order = obj->PowerResource.ResourceOrder; - - /* sort the resource into the list */ - status = AE_OK; - srp = TAILQ_FIRST(&acpi_powerresources); - if ((srp == NULL) || (rp->ap_order < srp->ap_order)) { - TAILQ_INSERT_HEAD(&acpi_powerresources, rp, ap_link); - goto done; - } - TAILQ_FOREACH(srp, &acpi_powerresources, ap_link) - if (rp->ap_order < srp->ap_order) { - TAILQ_INSERT_BEFORE(srp, rp, ap_link); - goto done; - } - TAILQ_INSERT_TAIL(&acpi_powerresources, rp, ap_link); - - done: - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "registered power resource %s\n", - acpi_name(res))); - out: - if (buf.Pointer != NULL) - ACPI_FREE(buf.Pointer); - if (ACPI_FAILURE(status) && (rp != NULL)) - free(rp, M_ACPIPWR); - return_ACPI_STATUS(status); -} - -#if 0 -/* - * Deregister a power resource. - */ -static ACPI_STATUS -acpi_pwr_deregister_resource(ACPI_HANDLE res) -{ - struct acpi_powerresource *rp; - - ACPI_FUNCTION_TRACE(__func__); - - rp = NULL; - - /* find the resource */ - if ((rp = acpi_pwr_find_resource(res)) == NULL) - return_ACPI_STATUS(AE_BAD_PARAMETER); - - /* check that there are no consumers referencing this resource */ - if (TAILQ_FIRST(&rp->ap_references) != NULL) - return_ACPI_STATUS(AE_BAD_PARAMETER); - - /* pull it off the list and free it */ - TAILQ_REMOVE(&acpi_powerresources, rp, ap_link); - free(rp, M_ACPIPWR); - - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power resource %s\n", - acpi_name(res))); - - return_ACPI_STATUS(AE_OK); -} -#endif - -/* - * Register a power consumer. - * - * It's OK to call this if we already know about the consumer. - */ -static ACPI_STATUS -acpi_pwr_register_consumer(ACPI_HANDLE consumer) -{ - struct acpi_powerconsumer *pc; - - ACPI_FUNCTION_TRACE(__func__); - - /* check to see whether we know about this consumer already */ - if ((pc = acpi_pwr_find_consumer(consumer)) != NULL) - return_ACPI_STATUS(AE_OK); - - /* allocate a new power consumer */ - if ((pc = malloc(sizeof(*pc), M_ACPIPWR, M_NOWAIT)) == NULL) - return_ACPI_STATUS(AE_NO_MEMORY); - TAILQ_INSERT_HEAD(&acpi_powerconsumers, pc, ac_link); - TAILQ_INIT(&pc->ac_references); - pc->ac_consumer = consumer; - - /* XXX we should try to find its current state */ - pc->ac_state = ACPI_STATE_UNKNOWN; - - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "registered power consumer %s\n", - acpi_name(consumer))); - - return_ACPI_STATUS(AE_OK); -} - -#if 0 -/* - * Deregister a power consumer. - * - * This should only be done once the consumer has been powered off. - * (XXX is this correct? Check once implemented) - */ -static ACPI_STATUS -acpi_pwr_deregister_consumer(ACPI_HANDLE consumer) -{ - struct acpi_powerconsumer *pc; - - ACPI_FUNCTION_TRACE(__func__); - - /* find the consumer */ - if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) - return_ACPI_STATUS(AE_BAD_PARAMETER); - - /* make sure the consumer's not referencing anything right now */ - if (TAILQ_FIRST(&pc->ac_references) != NULL) - return_ACPI_STATUS(AE_BAD_PARAMETER); - - /* pull the consumer off the list and free it */ - TAILQ_REMOVE(&acpi_powerconsumers, pc, ac_link); - - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power consumer %s\n", - acpi_name(consumer))); - - return_ACPI_STATUS(AE_OK); -} -#endif - -/* - * Set a power consumer to a particular power state. - */ -ACPI_STATUS -acpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state) -{ - struct acpi_powerconsumer *pc; - struct acpi_powerreference *pr; - ACPI_HANDLE method_handle, reslist_handle, pr0_handle; - ACPI_BUFFER reslist_buffer; - ACPI_OBJECT *reslist_object; - ACPI_STATUS status; - const char *method_name, *reslist_name; - int res_changed; - - ACPI_FUNCTION_TRACE(__func__); - - /* find the consumer */ - if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) { - status = acpi_pwr_register_consumer(consumer); - if (ACPI_FAILURE(status)) - return_ACPI_STATUS(status); - if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) { - return_ACPI_STATUS(AE_ERROR); /* something very wrong */ - } - } - - /* check for valid transitions */ - if ((pc->ac_state == ACPI_STATE_D3) && (state != ACPI_STATE_D0)) - return_ACPI_STATUS(AE_BAD_PARAMETER); /* can only go to D0 from D3 */ - - /* find transition mechanism(s) */ - switch(state) { - case ACPI_STATE_D0: - method_name = "_PS0"; - reslist_name = "_PR0"; - break; - case ACPI_STATE_D1: - method_name = "_PS1"; - reslist_name = "_PR1"; - break; - case ACPI_STATE_D2: - method_name = "_PS2"; - reslist_name = "_PR2"; - break; - case ACPI_STATE_D3: - method_name = "_PS3"; - reslist_name = "_PR3"; - break; - default: - return_ACPI_STATUS(AE_BAD_PARAMETER); - } - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "setup to switch %s D%d -> D%d\n", - acpi_name(consumer), pc->ac_state, state)); - - /* - * Verify that this state is supported, ie. one of method or - * reslist must be present. We need to do this before we go - * dereferencing resources (since we might be trying to go to - * a state we don't support). - * - * Note that if any states are supported, the device has to - * support D0 and D3. It's never an error to try to go to - * D0. - */ - reslist_buffer.Pointer = NULL; - reslist_object = NULL; - if (ACPI_FAILURE(AcpiGetHandle(consumer, method_name, &method_handle))) - method_handle = NULL; - if (ACPI_FAILURE(AcpiGetHandle(consumer, reslist_name, &reslist_handle))) - reslist_handle = NULL; - if ((reslist_handle == NULL) && (method_handle == NULL)) { - if (state == ACPI_STATE_D0) { - pc->ac_state = ACPI_STATE_D0; - return_ACPI_STATUS(AE_OK); - } - if (state != ACPI_STATE_D3) { - goto bad; - } - - /* turn off the resources listed in _PR0 to go to D3. */ - if (ACPI_FAILURE(AcpiGetHandle(consumer, "_PR0", &pr0_handle))) { - goto bad; - } - status = acpi_eval_struct(pr0_handle, NULL, &reslist_buffer); - if (ACPI_FAILURE(status)) - goto bad; - reslist_object = reslist_buffer.Pointer; - if ((reslist_object->Type != ACPI_TYPE_PACKAGE) || - (reslist_object->Package.Count == 0)) { - goto bad; - } - ACPI_FREE(reslist_buffer.Pointer); - reslist_buffer.Pointer = NULL; - reslist_object = NULL; - } - - /* - * Check that we can actually fetch the list of power resources - */ - if (reslist_handle != NULL) { - status = acpi_eval_struct(reslist_handle, - NULL, &reslist_buffer); - if (ACPI_FAILURE(status)) { - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't evaluate resource list %s\n", - acpi_name(reslist_handle))); - goto out; - } - reslist_object = reslist_buffer.Pointer; - if (reslist_object->Type != ACPI_TYPE_PACKAGE) { - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "resource list is not ACPI_TYPE_PACKAGE (%u)\n", - reslist_object->Type)); - status = AE_TYPE; - goto out; - } - } - - /* - * Now we are ready to switch, so kill off any current power resource references. - */ - res_changed = 0; - while((pr = TAILQ_FIRST(&pc->ac_references)) != NULL) { - res_changed = 1; - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, - "removing reference to %s\n", - acpi_name(pr->ar_resource->ap_resource))); - TAILQ_REMOVE(&pr->ar_resource->ap_references, pr, ar_rlink); - TAILQ_REMOVE(&pc->ac_references, pr, ar_clink); - free(pr, M_ACPIPWR); - } - - /* - * Add new power resource references, if we have any. Traverse the - * package that we got from evaluating reslist_handle, and look up each - * of the resources that are referenced. - */ - if (reslist_object != NULL) { - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, - "referencing %u new resources\n", - reslist_object->Package.Count)); - acpi_foreach_package_object(reslist_object, - acpi_pwr_reference_resource, pc); - res_changed = 1; - } - - /* - * If we changed anything in the resource list, we need to run a switch - * pass now. - */ - status = acpi_pwr_switch_power(); - if (ACPI_FAILURE(status)) { - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to correctly switch resources to move %s to D%d\n", - acpi_name(consumer), state)); - goto out; /* XXX is this appropriate? Should we return to previous state? */ - } - - /* invoke power state switch method (if present) */ - if (method_handle != NULL) { - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, - "invoking state transition method %s\n", - acpi_name(method_handle))); - status = AcpiEvaluateObject(method_handle, NULL, NULL, NULL); - if (ACPI_FAILURE(status)) { - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, - "failed to set state - %s\n", - AcpiFormatException(status))); - pc->ac_state = ACPI_STATE_UNKNOWN; - goto out; /* XXX Should we return to previous state? */ - } - } - - /* transition was successful */ - pc->ac_state = state; - return_ACPI_STATUS(AE_OK); - - bad: - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, - "attempt to set unsupported state D%d\n", - state)); - status = AE_BAD_PARAMETER; - - out: - if (reslist_buffer.Pointer != NULL) - ACPI_FREE(reslist_buffer.Pointer); - return_ACPI_STATUS(status); -} - -/* - * Called to create a reference between a power consumer and a power resource - * identified in the object. - */ -static ACPI_STATUS -acpi_pwr_reference_resource(ACPI_OBJECT *obj, void *arg) -{ - struct acpi_powerconsumer *pc = (struct acpi_powerconsumer *)arg; - struct acpi_powerreference *pr; - struct acpi_powerresource *rp; - ACPI_HANDLE res; - ACPI_STATUS status; - - ACPI_FUNCTION_TRACE(__func__); - - status = acpi_eval_reference_handle(obj, &res); - - if (ACPI_FAILURE(status)) { - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to get handle\n")); - return_ACPI_STATUS(AE_OK); - } - - /* create/look up the resource */ - status = acpi_pwr_register_resource(res); - if (ACPI_FAILURE(status)) { - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, - "couldn't register power resource %s - %s\n", - obj->String.Pointer, - AcpiFormatException(status))); - return_ACPI_STATUS(AE_OK); - } - if ((rp = acpi_pwr_find_resource(res)) == NULL) { - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, - "power resource list corrupted\n")); - return_ACPI_STATUS(AE_OK); - } - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "found power resource %s\n", - acpi_name(rp->ap_resource))); - - /* create a reference between the consumer and resource */ - if ((pr = malloc(sizeof(*pr), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) { - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "couldn't allocate memory for a power consumer reference\n")); - return_ACPI_STATUS(AE_OK); - } - pr->ar_consumer = pc; - pr->ar_resource = rp; - TAILQ_INSERT_TAIL(&pc->ac_references, pr, ar_clink); - TAILQ_INSERT_TAIL(&rp->ap_references, pr, ar_rlink); - - return_ACPI_STATUS(AE_OK); -} - - -/* - * Switch power resources to conform to the desired state. - * - * Consumers may have modified the power resource list in an arbitrary - * fashion; we sweep it in sequence order. - */ -static ACPI_STATUS -acpi_pwr_switch_power(void) -{ - struct acpi_powerresource *rp; - ACPI_STATUS status; - ACPI_INTEGER cur; - - ACPI_FUNCTION_TRACE(__func__); - - /* - * Sweep the list forwards turning things on. - */ - TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) { - if (TAILQ_FIRST(&rp->ap_references) == NULL) { - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s has no references, not turning on\n", - acpi_name(rp->ap_resource))); - continue; - } - - /* we could cache this if we trusted it not to change under us */ - status = acpi_eval_integer(rp->ap_resource, "_STA", &cur); - if (ACPI_FAILURE(status)) { - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, - "can't get status of %s - %s\n", - acpi_name(rp->ap_resource), - AcpiFormatException(status))); - /* XXX is this correct? Always switch if in doubt? */ - continue; - } - - /* - * Switch if required. Note that we ignore the result - * of the switch effort; we don't know what to do if - * it fails, so checking wouldn't help much. - */ - if (cur != ACPI_STA_POW_ON) { - status = AcpiEvaluateObject(rp->ap_resource, "_ON", - NULL, NULL); - if (ACPI_FAILURE(status)) { - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to switch %s on - %s\n", - acpi_name(rp->ap_resource), - AcpiFormatException(status))); - } else { - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s on\n", acpi_name(rp->ap_resource))); - } - } else { - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, - "%s is already on\n", - acpi_name(rp->ap_resource))); - } - } - - /* - * Sweep the list backwards turning things off. - */ - TAILQ_FOREACH_REVERSE(rp, &acpi_powerresources, acpi_powerresource_list, ap_link) { - if (TAILQ_FIRST(&rp->ap_references) != NULL) { - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s has references, not turning off\n", - acpi_name(rp->ap_resource))); - continue; - } - - /* we could cache this if we trusted it not to change under us */ - status = acpi_eval_integer(rp->ap_resource, "_STA", &cur); - if (ACPI_FAILURE(status)) { - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n", - acpi_name(rp->ap_resource), status)); - continue; /* XXX is this correct? Always switch if in doubt? */ - } - - /* - * Switch if required. Note that we ignore the result - * of the switch effort; we don't know what to do if - * it fails, so checking wouldn't help much. - */ - if (cur != ACPI_STA_POW_OFF) { - status = AcpiEvaluateObject(rp->ap_resource, "_OFF", - NULL, NULL); - if (ACPI_FAILURE(status)) { - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to switch %s off - %s\n", - acpi_name(rp->ap_resource), AcpiFormatException(status))); - } else { - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s off\n", - acpi_name(rp->ap_resource))); - } - } else { - ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already off\n", - acpi_name(rp->ap_resource))); - } - } - return_ACPI_STATUS(AE_OK); -} - -/* - * Find a power resource's control structure. - */ -static struct acpi_powerresource * -acpi_pwr_find_resource(ACPI_HANDLE res) -{ - struct acpi_powerresource *rp; - - ACPI_FUNCTION_TRACE(__func__); - - TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) - if (rp->ap_resource == res) - break; - return_PTR(rp); -} - -/* - * Find a power consumer's control structure. - */ -static struct acpi_powerconsumer * -acpi_pwr_find_consumer(ACPI_HANDLE consumer) -{ - struct acpi_powerconsumer *pc; - - ACPI_FUNCTION_TRACE(__func__); - - TAILQ_FOREACH(pc, &acpi_powerconsumers, ac_link) - if (pc->ac_consumer == consumer) - break; - return_PTR(pc); -} diff --git a/sys/dev/acpi/acpi_tz.c b/sys/dev/acpi/acpi_tz.c index 38bbaf21c67..361957f17fb 100644 --- a/sys/dev/acpi/acpi_tz.c +++ b/sys/dev/acpi/acpi_tz.c @@ -1,4 +1,4 @@ -/* $NetBSD: acpi_tz.c,v 1.66 2010/04/16 01:52:54 christos Exp $ */ +/* $NetBSD: acpi_tz.c,v 1.67 2010/04/22 18:40:09 jruoho Exp $ */ /* * Copyright (c) 2003 Jared D. McNeill @@ -30,7 +30,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: acpi_tz.c,v 1.66 2010/04/16 01:52:54 christos Exp $"); +__KERNEL_RCSID(0, "$NetBSD: acpi_tz.c,v 1.67 2010/04/22 18:40:09 jruoho Exp $"); #include #include @@ -40,6 +40,7 @@ __KERNEL_RCSID(0, "$NetBSD: acpi_tz.c,v 1.66 2010/04/16 01:52:54 christos Exp $" #include #include +#include #define _COMPONENT ACPI_TZ_COMPONENT ACPI_MODULE_NAME ("acpi_tz") @@ -211,6 +212,7 @@ acpitz_attach(device_t parent, device_t self, void *aux) acpitz_get_zone(self, 1); acpitz_get_status(self); + (void)acpi_power_register(sc->sc_devnode); (void)acpi_register_notify(sc->sc_devnode, acpitz_notify_handler); callout_init(&sc->sc_callout, CALLOUT_MPSAFE); @@ -373,8 +375,8 @@ static ACPI_STATUS acpitz_switch_cooler(ACPI_OBJECT *obj, void *arg) { ACPI_HANDLE cooler; - ACPI_STATUS rv; int pwr_state, flag; + ACPI_STATUS rv; flag = *(int *)arg; @@ -384,15 +386,13 @@ acpitz_switch_cooler(ACPI_OBJECT *obj, void *arg) pwr_state = ACPI_STATE_D3; rv = acpi_eval_reference_handle(obj, &cooler); + if (ACPI_FAILURE(rv)) { aprint_error("%s: failed to get handle\n", __func__); return rv; } - rv = acpi_pwr_switch_consumer(cooler, pwr_state); - if (rv != AE_BAD_PARAMETER && ACPI_FAILURE(rv)) - aprint_error("%s: failed to change state for %s: %s\n", - __func__, acpi_name(cooler), AcpiFormatException(rv)); + (void)acpi_power_set_from_handle(cooler, pwr_state); return AE_OK; } diff --git a/sys/dev/acpi/acpivar.h b/sys/dev/acpi/acpivar.h index 5638c20d4f1..7696e38492d 100644 --- a/sys/dev/acpi/acpivar.h +++ b/sys/dev/acpi/acpivar.h @@ -1,4 +1,4 @@ -/* $NetBSD: acpivar.h,v 1.50 2010/04/22 14:50:31 jruoho Exp $ */ +/* $NetBSD: acpivar.h,v 1.51 2010/04/22 18:40:09 jruoho Exp $ */ /* * Copyright 2001 Wasabi Systems, Inc. @@ -87,11 +87,13 @@ struct acpi_pci_info { * * Remarks: * - * ad_root never NULL - * ad_parent only NULL if the root of the tree ("\"). * ad_device NULL if no device has attached to the node + * ad_root never NULL + * ad_parent only NULL if root of the tree ("\") * ad_pciinfo NULL if not a PCI device * ad_notify NULL if there is no notify handler + * ad_devinfo never NULL + * ad_handle never NULL */ struct acpi_devnode { device_t ad_device; /* Device */ @@ -104,6 +106,7 @@ struct acpi_devnode { char ad_name[5]; /* Device name */ uint32_t ad_flags; /* Device flags */ uint32_t ad_type; /* Device type */ + int ad_state; /* Device power state */ int ad_wake; /* Device wakeup */ SIMPLEQ_ENTRY(acpi_devnode) ad_list; diff --git a/sys/dev/acpi/files.acpi b/sys/dev/acpi/files.acpi index ed4457641c0..61ab3041849 100644 --- a/sys/dev/acpi/files.acpi +++ b/sys/dev/acpi/files.acpi @@ -1,4 +1,4 @@ -# $NetBSD: files.acpi,v 1.73 2010/04/14 17:12:14 jruoho Exp $ +# $NetBSD: files.acpi,v 1.74 2010/04/22 18:40:09 jruoho Exp $ include "dev/acpi/acpica/files.acpica" @@ -17,7 +17,7 @@ file dev/acpi/acpi_debug.c acpi file dev/acpi/acpi_madt.c acpi file dev/acpi/acpi_pci.c acpi file dev/acpi/acpi_pci_link.c acpi -file dev/acpi/acpi_powerres.c acpi +file dev/acpi/acpi_power.c acpi file dev/acpi/acpi_quirks.c acpi file dev/acpi/acpi_resource.c acpi file dev/acpi/acpi_srat.c acpi -- cgit