patch-2.4.6 linux/drivers/acpi/ospm/busmgr/bmpm.c
Next file: linux/drivers/acpi/ospm/busmgr/bmpower.c
Previous file: linux/drivers/acpi/ospm/busmgr/bmnotify.c
Back to the patch index
Back to the overall index
- Lines: 443
- Date:
Wed Jun 20 17:47:40 2001
- Orig file:
v2.4.5/linux/drivers/acpi/ospm/busmgr/bmpm.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.4.5/linux/drivers/acpi/ospm/busmgr/bmpm.c linux/drivers/acpi/ospm/busmgr/bmpm.c
@@ -0,0 +1,442 @@
+/*****************************************************************************
+ *
+ * Module Name: bmpm.c
+ * $Revision: 10 $
+ *
+ *****************************************************************************/
+
+/*
+ * Copyright (C) 2000, 2001 Andrew Grover
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#include <acpi.h>
+#include "bm.h"
+#include "bmpower.h"
+
+
+#define _COMPONENT ACPI_BUS
+ MODULE_NAME ("bmpm")
+
+
+/****************************************************************************
+ * Internal Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ *
+ * FUNCTION: bm_get_inferred_power_state
+ *
+ * PARAMETERS:
+ *
+ * RETURN:
+ *
+ * DESCRIPTION:
+ *
+ ****************************************************************************/
+
+ACPI_STATUS
+bm_get_inferred_power_state (
+ BM_DEVICE *device)
+{
+ ACPI_STATUS status = AE_OK;
+ BM_HANDLE_LIST pr_list;
+ BM_POWER_STATE list_state = ACPI_STATE_UNKNOWN;
+ char object_name[5] = {'_','P','R','0','\0'};
+ u32 i = 0;
+
+ FUNCTION_TRACE("bm_get_inferred_power_state");
+
+ if (!device) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ MEMSET(&pr_list, 0, sizeof(BM_HANDLE_LIST));
+
+ device->power.state = ACPI_STATE_D3;
+
+ /*
+ * Calculate Power State:
+ * ----------------------
+ * Try to infer the devices's power state by checking the state of
+ * the devices's power resources. We start by evaluating _PR0
+ * (resource requirements at D0) and work through _PR1 and _PR2.
+ * We know the current devices power state when all resources (for
+ * a give Dx state) are ON. If no power resources are on then the
+ * device is assumed to be off (D3).
+ */
+ for (i=ACPI_STATE_D0; i<ACPI_STATE_D3; i++) {
+
+ object_name[3] = '0' + i;
+
+ status = bm_evaluate_reference_list(device->acpi_handle,
+ object_name, &pr_list);
+
+ if (ACPI_SUCCESS(status)) {
+
+ status = bm_pr_list_get_state(&pr_list, &list_state);
+
+ if (ACPI_SUCCESS(status)) {
+
+ if (list_state == ACPI_STATE_D0) {
+ device->power.state = i;
+ break;
+ }
+ }
+ }
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
+
+
+/****************************************************************************
+ * External Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ *
+ * FUNCTION: bm_get_power_state
+ *
+ * PARAMETERS:
+ *
+ * RETURN:
+ *
+ * DESCRIPTION:
+ *
+ ****************************************************************************/
+
+ACPI_STATUS
+bm_get_power_state (
+ BM_NODE *node)
+{
+ ACPI_STATUS status = AE_OK;
+ BM_DEVICE *device = NULL;
+
+ FUNCTION_TRACE("bm_get_power_state");
+
+ if (!node || !node->parent) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ device = &(node->device);
+
+ device->power.state = ACPI_STATE_UNKNOWN;
+
+ /*
+ * Power Control?
+ * --------------
+ * If this device isn't directly power manageable (e.g. doesn't
+ * include _PR0/_PS0) then there's nothing to do (state is static).
+ */
+ if (!BM_IS_POWER_CONTROL(device)) {
+ return_ACPI_STATUS(AE_OK);
+ }
+
+ /*
+ * Parent Present?
+ * ---------------
+ * Make sure the parent is present before mucking with the child.
+ */
+ if (!BM_NODE_PRESENT(node->parent)) {
+ return_ACPI_STATUS(AE_NOT_EXIST);
+ }
+
+ /*
+ * Get Power State:
+ * ----------------
+ * Either directly (via _PSC) or inferred (via power resource
+ * dependencies).
+ */
+ if (BM_IS_POWER_STATE(device)) {
+ status = bm_evaluate_simple_integer(device->acpi_handle,
+ "_PSC", &(device->power.state));
+ }
+ else {
+ status = bm_get_inferred_power_state(device);
+ }
+
+ if (ACPI_SUCCESS(status)) {
+ DEBUG_PRINT(ACPI_INFO, ("Device [%02x] is at power state [D%d].\n", device->handle, device->power.state));
+ }
+ else {
+ DEBUG_PRINT(ACPI_INFO, ("Error getting power state for device [%02x]\n", device->handle));
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+
+/****************************************************************************
+ *
+ * FUNCTION: bm_set_power_state
+ *
+ * PARAMETERS:
+ *
+ * RETURN:
+ *
+ * DESCRIPTION:
+ *
+ ****************************************************************************/
+
+ACPI_STATUS
+bm_set_power_state (
+ BM_NODE *node,
+ BM_POWER_STATE state)
+{
+ ACPI_STATUS status = AE_OK;
+ BM_DEVICE *device = NULL;
+ BM_DEVICE *parent_device = NULL;
+ BM_HANDLE_LIST current_list;
+ BM_HANDLE_LIST target_list;
+ char object_name[5] = {'_','P','R','0','\0'};
+
+ FUNCTION_TRACE("bm_set_power_state");
+
+ if (!node || !node->parent || (state > ACPI_STATE_D3)) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ MEMSET(¤t_list, 0, sizeof(BM_HANDLE_LIST));
+ MEMSET(&target_list, 0, sizeof(BM_HANDLE_LIST));
+
+ device = &(node->device);
+ parent_device = &(node->parent->device);
+
+ /*
+ * Power Control?
+ * --------------
+ * If this device isn't directly power manageable (e.g. doesn't
+ * include _PR0/_PS0) then return an error (can't set state).
+ */
+ if (!BM_IS_POWER_CONTROL(device)) {
+ return_ACPI_STATUS(AE_ERROR);
+ }
+
+ /*
+ * Parent Present?
+ * ---------------
+ * Make sure the parent is present before mucking with the child.
+ */
+ if (!BM_NODE_PRESENT(node->parent)) {
+ return_ACPI_STATUS(AE_NOT_EXIST);
+ }
+
+ /*
+ * Check Parent's Power State:
+ * ---------------------------
+ * Can't be in a higher power state (lower Dx value) than parent.
+ */
+ if (state < parent_device->power.state) {
+ DEBUG_PRINT(ACPI_WARN, ("Cannot set device [%02x] to a higher-powered state than parent_device.\n", device->handle));
+ return_ACPI_STATUS(AE_ERROR);
+ }
+
+ /*
+ * Get Resources:
+ * --------------
+ * Get the power resources associated with the device's current
+ * and target power states.
+ */
+ if (device->power.state != ACPI_STATE_UNKNOWN) {
+ object_name[3] = '0' + device->power.state;
+ bm_evaluate_reference_list(device->acpi_handle,
+ object_name, ¤t_list);
+ }
+
+ object_name[3] = '0' + state;
+ bm_evaluate_reference_list(device->acpi_handle, object_name,
+ &target_list);
+
+ /*
+ * Transition Resources:
+ * ---------------------
+ * Transition all power resources referenced by this device to
+ * the correct power state (taking into consideration sequencing
+ * and dependencies to other devices).
+ */
+ if (current_list.count || target_list.count) {
+ status = bm_pr_list_transition(¤t_list, &target_list);
+ }
+ if (ACPI_FAILURE(status)) {
+ return_ACPI_STATUS(status);
+ }
+
+ /*
+ * Execute _PSx:
+ * -------------
+ * Execute the _PSx method corresponding to the target Dx state,
+ * if it exists.
+ */
+ object_name[2] = 'S';
+ object_name[3] = '0' + state;
+ bm_evaluate_object(device->acpi_handle, object_name, NULL, NULL);
+
+ if (ACPI_SUCCESS(status)) {
+ DEBUG_PRINT(ACPI_INFO, ("Device [%02x] is now at [D%d].\n", device->handle, state));
+ device->power.state = state;
+ }
+
+ return_ACPI_STATUS(status);
+}
+
+
+/****************************************************************************
+ *
+ * FUNCTION: bm_get_pm_capabilities
+ *
+ * PARAMETERS:
+ *
+ * RETURN:
+ *
+ * DESCRIPTION:
+ *
+ ****************************************************************************/
+
+ACPI_STATUS
+bm_get_pm_capabilities (
+ BM_NODE *node)
+{
+ ACPI_STATUS status = AE_OK;
+ BM_DEVICE *device = NULL;
+ BM_DEVICE *parent_device = NULL;
+ ACPI_HANDLE acpi_handle = NULL;
+ BM_POWER_STATE dx_supported = ACPI_STATE_UNKNOWN;
+ char object_name[5] = {'_','S','0','D','\0'};
+ u32 i = 0;
+
+ FUNCTION_TRACE("bm_get_pm_capabilities");
+
+ if (!node || !node->parent) {
+ return_ACPI_STATUS(AE_BAD_PARAMETER);
+ }
+
+ device = &(node->device);
+ parent_device = &(node->parent->device);
+
+ /*
+ * Power Management Flags:
+ * -----------------------
+ */
+ if (ACPI_SUCCESS(acpi_get_handle(device->acpi_handle, "_PSC",
+ &acpi_handle))) {
+ device->power.flags |= BM_FLAGS_POWER_STATE;
+ }
+
+ if (ACPI_SUCCESS(acpi_get_handle(device->acpi_handle, "_IRC",
+ &acpi_handle))) {
+ device->power.flags |= BM_FLAGS_INRUSH_CURRENT;
+ }
+
+ if (ACPI_SUCCESS(acpi_get_handle(device->acpi_handle, "_PRW",
+ &acpi_handle))) {
+ device->power.flags |= BM_FLAGS_WAKE_CAPABLE;
+ }
+
+ /*
+ * Device Power State:
+ * -------------------
+ * Note that we can't get the device's power state until we've
+ * initialized all power resources, so for now we just set to
+ * unknown.
+ */
+ device->power.state = ACPI_STATE_UNKNOWN;
+
+ /*
+ * Dx Supported in S0:
+ * -------------------
+ * Figure out which Dx states are supported by this device for the
+ * S0 (working) state. Note that D0 and D3 are required (assumed).
+ */
+ device->power.dx_supported[ACPI_STATE_S0] = BM_FLAGS_D0_SUPPORT |
+ BM_FLAGS_D3_SUPPORT;
+
+ if ((ACPI_SUCCESS(acpi_get_handle(device->acpi_handle, "_PR1",
+ &acpi_handle))) ||
+ (ACPI_SUCCESS(acpi_get_handle(device->acpi_handle, "_PS1",
+ &acpi_handle)))) {
+ device->power.dx_supported[ACPI_STATE_S0] |=
+ BM_FLAGS_D1_SUPPORT;
+ }
+
+ if ((ACPI_SUCCESS(acpi_get_handle(device->acpi_handle, "_PR2",
+ &acpi_handle))) ||
+ (ACPI_SUCCESS(acpi_get_handle(device->acpi_handle, "_PS2",
+ &acpi_handle)))) {
+ device->power.dx_supported[ACPI_STATE_S0] |=
+ BM_FLAGS_D2_SUPPORT;
+ }
+
+ /*
+ * Dx Supported in S1-S5:
+ * ----------------------
+ * Figure out which Dx states are supported by this device for
+ * all other Sx states.
+ */
+ for (i = ACPI_STATE_S1; i <= ACPI_STATE_S5; i++) {
+
+ /*
+ * D3 support is assumed (off is always possible!).
+ */
+ device->power.dx_supported[i] = BM_FLAGS_D3_SUPPORT;
+
+ /*
+ * Evalute _SxD:
+ * -------------
+ * Which returns the highest (power) Dx state supported in
+ * this system (Sx) state. We convert this value to a bit
+ * mask of supported states (conceptually simpler).
+ */
+ status = bm_evaluate_simple_integer(device->acpi_handle,
+ object_name, &dx_supported);
+ if (ACPI_SUCCESS(status)) {
+ switch (dx_supported) {
+ case 0:
+ device->power.dx_supported[i] |=
+ BM_FLAGS_D0_SUPPORT;
+ /* fall through */
+ case 1:
+ device->power.dx_supported[i] |=
+ BM_FLAGS_D1_SUPPORT;
+ /* fall through */
+ case 2:
+ device->power.dx_supported[i] |=
+ BM_FLAGS_D2_SUPPORT;
+ /* fall through */
+ case 3:
+ device->power.dx_supported[i] |=
+ BM_FLAGS_D3_SUPPORT;
+ break;
+ }
+
+ /*
+ * Validate:
+ * ---------
+ * Mask of any states that _Sx_d falsely advertises
+ * (e.g.claims D1 support but neither _PR2 or _PS2
+ * exist). In other words, S1-S5 can't offer a Dx
+ * state that isn't supported by S0.
+ */
+ device->power.dx_supported[i] &=
+ device->power.dx_supported[ACPI_STATE_S0];
+ }
+
+ object_name[2]++;
+ }
+
+ return_ACPI_STATUS(AE_OK);
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)