Module Name: src Committed By: jruoho Date: Thu Apr 22 18:40:10 UTC 2010
Modified Files: src/sys/dev/acpi: acpi.c acpi_tz.c acpivar.h files.acpi Added Files: src/sys/dev/acpi: acpi_power.c acpi_power.h Removed Files: src/sys/dev/acpi: acpi_powerres.c Log Message: 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@ To generate a diff of this commit: cvs rdiff -u -r1.180 -r1.181 src/sys/dev/acpi/acpi.c cvs rdiff -u -r0 -r1.1 src/sys/dev/acpi/acpi_power.c \ src/sys/dev/acpi/acpi_power.h cvs rdiff -u -r1.12 -r0 src/sys/dev/acpi/acpi_powerres.c cvs rdiff -u -r1.66 -r1.67 src/sys/dev/acpi/acpi_tz.c cvs rdiff -u -r1.50 -r1.51 src/sys/dev/acpi/acpivar.h cvs rdiff -u -r1.73 -r1.74 src/sys/dev/acpi/files.acpi Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/acpi/acpi.c diff -u src/sys/dev/acpi/acpi.c:1.180 src/sys/dev/acpi/acpi.c:1.181 --- src/sys/dev/acpi/acpi.c:1.180 Thu Apr 22 14:50:30 2010 +++ src/sys/dev/acpi/acpi.c Thu Apr 22 18:40:09 2010 @@ -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 <sys/cdefs.h> -__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 @@ #include <dev/acpi/acpivar.h> #include <dev/acpi/acpi_osd.h> #include <dev/acpi/acpi_pci.h> +#include <dev/acpi/acpi_power.h> #include <dev/acpi/acpi_timer.h> #include <dev/acpi/acpi_wakedev.h> @@ -979,11 +980,18 @@ /* * 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. */ if (acpi_match_hid(ad->ad_devinfo, acpi_ignored_ids)) @@ -1019,14 +1027,16 @@ /* * 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 @@ 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"); } } Index: src/sys/dev/acpi/acpi_tz.c diff -u src/sys/dev/acpi/acpi_tz.c:1.66 src/sys/dev/acpi/acpi_tz.c:1.67 --- src/sys/dev/acpi/acpi_tz.c:1.66 Fri Apr 16 01:52:54 2010 +++ src/sys/dev/acpi/acpi_tz.c Thu Apr 22 18:40:09 2010 @@ -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 <jmcne...@invisible.ca> @@ -30,7 +30,7 @@ */ #include <sys/cdefs.h> -__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 <sys/param.h> #include <sys/device.h> @@ -40,6 +40,7 @@ #include <dev/acpi/acpireg.h> #include <dev/acpi/acpivar.h> +#include <dev/acpi/acpi_power.h> #define _COMPONENT ACPI_TZ_COMPONENT ACPI_MODULE_NAME ("acpi_tz") @@ -211,6 +212,7 @@ 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 @@ 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 @@ 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; } Index: src/sys/dev/acpi/acpivar.h diff -u src/sys/dev/acpi/acpivar.h:1.50 src/sys/dev/acpi/acpivar.h:1.51 --- src/sys/dev/acpi/acpivar.h:1.50 Thu Apr 22 14:50:31 2010 +++ src/sys/dev/acpi/acpivar.h Thu Apr 22 18:40:09 2010 @@ -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 @@ * * 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 @@ 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; Index: src/sys/dev/acpi/files.acpi diff -u src/sys/dev/acpi/files.acpi:1.73 src/sys/dev/acpi/files.acpi:1.74 --- src/sys/dev/acpi/files.acpi:1.73 Wed Apr 14 17:12:14 2010 +++ src/sys/dev/acpi/files.acpi Thu Apr 22 18:40:09 2010 @@ -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_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 Added files: Index: src/sys/dev/acpi/acpi_power.c diff -u /dev/null src/sys/dev/acpi/acpi_power.c:1.1 --- /dev/null Thu Apr 22 18:40:10 2010 +++ src/sys/dev/acpi/acpi_power.c Thu Apr 22 18:40:09 2010 @@ -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 <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: acpi_power.c,v 1.1 2010/04/22 18:40:09 jruoho Exp $"); + +#include <sys/param.h> +#include <sys/kmem.h> +#include <sys/mutex.h> + +#include <dev/acpi/acpireg.h> +#include <dev/acpi/acpivar.h> +#include <dev/acpi/acpi_power.h> + +#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; +} Index: src/sys/dev/acpi/acpi_power.h diff -u /dev/null src/sys/dev/acpi/acpi_power.h:1.1 --- /dev/null Thu Apr 22 18:40:10 2010 +++ src/sys/dev/acpi/acpi_power.h Thu Apr 22 18:40:09 2010 @@ -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 */