Subject: [PATCH] [acpi power] Add power library functions - Create lib.c - Add functions that are used by the ACPI driver core - acpi_power_get_inferred_state() - acpi_power_transition() - And functions used by the sleep code - acpi_enable_wakeup_device_power() - acpi_disable_wakeup_device_power() Signed-off-by: Patrick Mochel --- drivers/acpi/drivers/power/Makefile | 1 drivers/acpi/drivers/power/lib.c | 351 +++++++++++++++++++++++++++++++++++ 2 files changed, 352 insertions(+), 0 deletions(-) create mode 100644 drivers/acpi/drivers/power/lib.c applies-to: d3dbddf176b1f76f55cbca998558ed55edf2c837 a6e3a8629aa469b09a4c1130ea26da81aaac2017 diff --git a/drivers/acpi/drivers/power/Makefile b/drivers/acpi/drivers/power/Makefile index 4b4c7c7..e09de7f 100644 --- a/drivers/acpi/drivers/power/Makefile +++ b/drivers/acpi/drivers/power/Makefile @@ -1,6 +1,7 @@ obj-$(CONFIG_ACPI_POWER) += power.o power-y := driver.o device.o event.o +power-y += lib.o power-$(CONFIG_ACPI_DM_PROC) += proc.o power-$(CONFIG_ACPI_DM_SYSFS) += sysfs.o diff --git a/drivers/acpi/drivers/power/lib.c b/drivers/acpi/drivers/power/lib.c new file mode 100644 index 0000000..8a1e592 --- /dev/null +++ b/drivers/acpi/drivers/power/lib.c @@ -0,0 +1,351 @@ +/*** + * drivers/acpi/drivers/power/lib.c - External functions for Power Resources + * + * Copyright (C) 2006 Patrick Mochel + * + * Based on drivers/acpi/power.c, which was + * + * Copyright (C) 2001, 2002 Andy Grover + * Copyright (C) 2001, 2002 Paul Diefenbaugh + * + * This file is released under the GPLv2. + */ + +#include "power.h" + +static int run_method(struct acpi_dev * ad, char * method) +{ + acpi_status status; + + status = acpi_evaluate_object(ad->acpi_device->handle, + method, NULL, NULL); + return ACPI_SUCCESS(status) ? 0 : -ENODEV; +} + + +static int set_int(struct acpi_dev * ad, char * method, u32 val) +{ + union acpi_object obj = { + .type = ACPI_TYPE_INTEGER, + .integer = { + .value = val, + }, + }; + struct acpi_object_list arg_list = { + .count = 1, + .pointer = &obj, + }; + acpi_status status; + unsigned long acpi_ret; + int ret; + + status = acpi_evaluate_integer(ad->acpi_device->handle, method, + &arg_list, &acpi_ret); + if (ACPI_SUCCESS(status)) + ret = acpi_ret ? -EINVAL : 0; + else + ret = -ENODEV; + return ret; +} + + +static struct acpi_power * get_power(acpi_handle handle) +{ + struct acpi_device * dev; + struct acpi_power * ap; + int ret; + + ret = acpi_bus_get_device(handle, &dev); + if (ret) { + err("Error getting context [%p]", handle); + return NULL; + } + + ap = dev_get_drvdata(&dev->ad->dev); + return ap; +} + +static int get_list_state(struct acpi_handle_list *list, int *state) +{ + struct acpi_power * ap; + int is_on = 1; + int ret; + int i; + + /* + * The state of the list is 'on' IFF all resources are 'on'. + */ + + for (i = 0; i < list->count && is_on; i++) { + ap = get_power(list->handles[i]); + if (!ap) + return -ENODEV; + + ret = power_get_state(ap); + if (ret) + return ret; + + if (ap->p_state != ACPI_POWER_RESOURCE_STATE_ON) + is_on = 0; + } + + dbg("Resource list is %s", is_on ? "on" : "off"); + *state = is_on ? ACPI_POWER_RESOURCE_STATE_ON : + ACPI_POWER_RESOURCE_STATE_OFF; + + return 0; +} + +static int power_on(acpi_handle handle) +{ + struct acpi_power * ap; + int ret; + + ap = get_power(handle); + if (!ap) + return -ENODEV; + + if (++ap->p_refs || ap->p_state == ACPI_POWER_RESOURCE_STATE_ON) { + dbg("Resource [%s] already on", acpi_dev_bid(ap->p_ad)); + return 0; + } + + ret = run_method(ap->p_ad, "_ON"); + if (ret) + return ret; + + ret = power_get_state(ap); + if (ret) + return ret; + + if (ap->p_state != ACPI_POWER_RESOURCE_STATE_ON) + return -ENOEXEC; + + /* + * Update the power resource's _device_ power state + */ + ap->p_ad->acpi_device->power.state = ACPI_STATE_D0; + + dbg("Resource [%s] turned on", acpi_dev_bid(ap)); + return 0; +} + +static int power_off(acpi_handle handle) +{ + struct acpi_power * ap; + int ret; + + ap = get_power(handle); + if (!ap) + return -ENODEV; + + if (--ap->p_refs) { + dbg("Resource [%s] is still in use, dereferencing", + acpi_dev_bid(ap->p_ad)); + return 0; + } + + if (ap->p_state == ACPI_POWER_RESOURCE_STATE_OFF) { + dbg("Resource [%s] already off", acpi_dev_bid(ap->p_ad)); + return 0; + } + + ret = run_method(ap->p_ad, "_OFF"); + if (ret) + return ret; + + ret = power_get_state(ap); + if (ret) + return ret; + + if (ap->p_state != ACPI_POWER_RESOURCE_STATE_OFF) + return -ENOEXEC; + + /* + * Update the power resource's _device_ power state + */ + ap->p_ad->acpi_device->power.state = ACPI_STATE_D3; + dbg("Resource [%s] turned off", resource->name); + return 0; +} + +/** + * acpi_enable_wakeup_device_power - Prepare a wakeup device + * @ad: The ACPI device we're working with + * + * This is used by the ACPI suspend/resume code, and works with + * two steps (Ref ACPI 2.0:P229): + * 1. Power on the power resources required for the wakeup device + * 2. Enable _PSW (power state wake) for the device if present + */ + +int acpi_enable_wakeup_device_power(struct acpi_device * dev) +{ + int ret; + int i; + + if (!dev) + return -EINVAL; + + if (!dev->wakeup.flags.valid) + return 0; + + /* + * Open power resource + */ + for (i = 0; i < dev->wakeup.resources.count; i++) { + ret = power_on(dev->wakeup.resources.handles[i]); + if (ret) { + err("opening power resource while enabling wake"); + goto Done; + } + } + + /* + * Execute PSW + */ + ret = set_int(dev->ad, "_PSW", 1); + + Done: + if (ret) + dev->wakeup.flags.valid = 0; + return 0; +} + +/** + * acpi_disable_wakeup_device_power - Shutdown a wakeup device + * @ad: The ACPI device we're working with + * + * This function is used by the ACPI suspend/resume code. It works + * by doing this: + * 1. Disable _PSW (power state wake) + * 2. Shutdown down the power resources + */ + +int acpi_disable_wakeup_device_power(struct acpi_device * dev) +{ + int ret; + int i; + + if (!dev) + return -EINVAL; + + if (!dev->wakeup.flags.valid) + return 0; + + /* + * Execute PSW + */ + ret = set_int(dev->ad, "_PSW", 0); + if (ret && ret != -ENODEV) { + err("disabling wake via _PSW"); + goto Done; + } + + /* + * Close power resource + */ + for (i = 0; i < dev->wakeup.resources.count; i++) { + ret = power_off(dev->wakeup.resources.handles[i]); + if (ret) { + err("Closing power resource while disabling wake"); + break; + } + } + Done: + if (ret) + dev->wakeup.flags.valid = 0; + return ret; +} + + +int acpi_power_get_inferred_state(struct acpi_device * dev) +{ + struct acpi_handle_list * list; + int list_state; + int ret = 0; + int i; + + if (!dev) + return -EINVAL; + + /* + * We know a device's inferred power state when all the resources + * required for a given D-state are 'on'. + */ + for (i = ACPI_STATE_D0; i < ACPI_STATE_D3; i++) { + list = &dev->power.states[i].resources; + if (list->count < 1) + continue; + + ret = get_list_state(list, &list_state); + if (ret) + break; + + if (list_state == ACPI_POWER_RESOURCE_STATE_ON) { + dev->power.state = i; + goto Done; + } + } + dev->power.state = ret ? ACPI_STATE_UNKNOWN : ACPI_STATE_D3; + + Done: + dbg("device [%s] power state is inferred: %d", + acpi_dev_hid(dev->ad), dev->power.state); + return ret; +} + + +int acpi_power_transition(struct acpi_device * dev, int state) +{ + struct acpi_handle_list * cl; /* Current Resources */ + struct acpi_handle_list * tl; /* Target Resources */ + int ret = 0; + int i; + + if (!dev) + return -EINVAL; + + if (state < ACPI_STATE_D0 || state > ACPI_STATE_D3) + return -EINVAL; + + cl = &dev->power.states[dev->power.state].resources; + tl = &dev->power.states[state].resources; + + dev->power.state = ACPI_STATE_UNKNOWN; + + if (!cl->count || !tl->count) { + ret = -ENODEV; + goto Done; + } + + /* TBD: Resources must be ordered. */ + + /* + * First we reference all power resources required in the target list + * (e.g. so the device doesn't lose power while transitioning). + */ + for (i = 0; i < tl->count; i++) { + ret = power_on(tl->handles[i]); + if (ret) + goto Done; + } + + /* + * Then we dereference all power resources used in the current list. + */ + for (i = 0; i < cl->count; i++) { + ret = power_off(cl->handles[i]); + if (ret) + goto Done; + } + + /* We shouldn't change the state till all above operations succeed */ + dev->power.state = state; + + Done: + if (ret) + dbg("transitioning device [%s] to D%d", + acpi_dev_bid(dev->ad), state); + return ret; +} --- 0.99.9.GIT