Merge branch 'acpi-pm'

* acpi-pm: (35 commits)
  ACPI / PM: Handle missing _PSC in acpi_bus_update_power()
  ACPI / PM: Do not power manage devices in unknown initial states
  ACPI / PM: Fix acpi_bus_get_device() check in drivers/acpi/device_pm.c
  ACPI / PM: Fix /proc/acpi/wakeup for devices w/o bus or parent
  ACPI / PM: Fix consistency check for power resources during resume
  ACPI / PM: Expose lists of device power resources to user space
  sysfs: Functions for adding/removing symlinks to/from attribute groups
  ACPI / PM: Expose current status of ACPI power resources
  ACPI / PM: Expose power states of ACPI devices to user space
  ACPI / scan: Prevent device add uevents from racing with user space
  ACPI / PM: Fix device power state value after transitions to D3cold
  ACPI / PM: Use string "D3cold" to represent ACPI_STATE_D3_COLD
  ACPI / PM: Sanitize checks in acpi_power_on_resources()
  ACPI / PM: Always evaluate _PSn after setting power resources
  ACPI / PM: Introduce helper for executing _PSn methods
  ACPI / PM: Make acpi_bus_init_power() more robust
  ACPI / PM: Fix build for unusual combination of Kconfig options
  ACPI / PM: remove leading whitespace from #ifdef
  ACPI / PM: Consolidate suspend-specific and hibernate-specific code
  ACPI / PM: Move device power management functions to device_pm.c
  ...
This commit is contained in:
Rafael J. Wysocki 2013-02-11 13:20:56 +01:00
commit a9834cb205
23 changed files with 1329 additions and 901 deletions

View file

@ -178,276 +178,6 @@ int acpi_bus_get_private_data(acpi_handle handle, void **data)
}
EXPORT_SYMBOL(acpi_bus_get_private_data);
/* --------------------------------------------------------------------------
Power Management
-------------------------------------------------------------------------- */
static const char *state_string(int state)
{
switch (state) {
case ACPI_STATE_D0:
return "D0";
case ACPI_STATE_D1:
return "D1";
case ACPI_STATE_D2:
return "D2";
case ACPI_STATE_D3_HOT:
return "D3hot";
case ACPI_STATE_D3_COLD:
return "D3";
default:
return "(unknown)";
}
}
static int __acpi_bus_get_power(struct acpi_device *device, int *state)
{
int result = ACPI_STATE_UNKNOWN;
if (!device || !state)
return -EINVAL;
if (!device->flags.power_manageable) {
/* TBD: Non-recursive algorithm for walking up hierarchy. */
*state = device->parent ?
device->parent->power.state : ACPI_STATE_D0;
goto out;
}
/*
* Get the device's power state either directly (via _PSC) or
* indirectly (via power resources).
*/
if (device->power.flags.explicit_get) {
unsigned long long psc;
acpi_status status = acpi_evaluate_integer(device->handle,
"_PSC", NULL, &psc);
if (ACPI_FAILURE(status))
return -ENODEV;
result = psc;
}
/* The test below covers ACPI_STATE_UNKNOWN too. */
if (result <= ACPI_STATE_D2) {
; /* Do nothing. */
} else if (device->power.flags.power_resources) {
int error = acpi_power_get_inferred_state(device, &result);
if (error)
return error;
} else if (result == ACPI_STATE_D3_HOT) {
result = ACPI_STATE_D3;
}
/*
* If we were unsure about the device parent's power state up to this
* point, the fact that the device is in D0 implies that the parent has
* to be in D0 too.
*/
if (device->parent && device->parent->power.state == ACPI_STATE_UNKNOWN
&& result == ACPI_STATE_D0)
device->parent->power.state = ACPI_STATE_D0;
*state = result;
out:
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] power state is %s\n",
device->pnp.bus_id, state_string(*state)));
return 0;
}
/**
* acpi_device_set_power - Set power state of an ACPI device.
* @device: Device to set the power state of.
* @state: New power state to set.
*
* Callers must ensure that the device is power manageable before using this
* function.
*/
int acpi_device_set_power(struct acpi_device *device, int state)
{
int result = 0;
acpi_status status = AE_OK;
char object_name[5] = { '_', 'P', 'S', '0' + state, '\0' };
if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD))
return -EINVAL;
/* Make sure this is a valid target state */
if (state == device->power.state) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device is already at %s\n",
state_string(state)));
return 0;
}
if (!device->power.states[state].flags.valid) {
printk(KERN_WARNING PREFIX "Device does not support %s\n",
state_string(state));
return -ENODEV;
}
if (device->parent && (state < device->parent->power.state)) {
printk(KERN_WARNING PREFIX
"Cannot set device to a higher-powered"
" state than parent\n");
return -ENODEV;
}
/* For D3cold we should execute _PS3, not _PS4. */
if (state == ACPI_STATE_D3_COLD)
object_name[3] = '3';
/*
* Transition Power
* ----------------
* On transitions to a high-powered state we first apply power (via
* power resources) then evalute _PSx. Conversly for transitions to
* a lower-powered state.
*/
if (state < device->power.state) {
if (device->power.state >= ACPI_STATE_D3_HOT &&
state != ACPI_STATE_D0) {
printk(KERN_WARNING PREFIX
"Cannot transition to non-D0 state from D3\n");
return -ENODEV;
}
if (device->power.flags.power_resources) {
result = acpi_power_transition(device, state);
if (result)
goto end;
}
if (device->power.states[state].flags.explicit_set) {
status = acpi_evaluate_object(device->handle,
object_name, NULL, NULL);
if (ACPI_FAILURE(status)) {
result = -ENODEV;
goto end;
}
}
} else {
if (device->power.states[state].flags.explicit_set) {
status = acpi_evaluate_object(device->handle,
object_name, NULL, NULL);
if (ACPI_FAILURE(status)) {
result = -ENODEV;
goto end;
}
}
if (device->power.flags.power_resources) {
result = acpi_power_transition(device, state);
if (result)
goto end;
}
}
end:
if (result)
printk(KERN_WARNING PREFIX
"Device [%s] failed to transition to %s\n",
device->pnp.bus_id, state_string(state));
else {
device->power.state = state;
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Device [%s] transitioned to %s\n",
device->pnp.bus_id, state_string(state)));
}
return result;
}
EXPORT_SYMBOL(acpi_device_set_power);
int acpi_bus_set_power(acpi_handle handle, int state)
{
struct acpi_device *device;
int result;
result = acpi_bus_get_device(handle, &device);
if (result)
return result;
if (!device->flags.power_manageable) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Device [%s] is not power manageable\n",
dev_name(&device->dev)));
return -ENODEV;
}
return acpi_device_set_power(device, state);
}
EXPORT_SYMBOL(acpi_bus_set_power);
int acpi_bus_init_power(struct acpi_device *device)
{
int state;
int result;
if (!device)
return -EINVAL;
device->power.state = ACPI_STATE_UNKNOWN;
result = __acpi_bus_get_power(device, &state);
if (result)
return result;
if (device->power.flags.power_resources)
result = acpi_power_on_resources(device, state);
if (!result)
device->power.state = state;
return result;
}
int acpi_bus_update_power(acpi_handle handle, int *state_p)
{
struct acpi_device *device;
int state;
int result;
result = acpi_bus_get_device(handle, &device);
if (result)
return result;
result = __acpi_bus_get_power(device, &state);
if (result)
return result;
result = acpi_device_set_power(device, state);
if (!result && state_p)
*state_p = state;
return result;
}
EXPORT_SYMBOL_GPL(acpi_bus_update_power);
bool acpi_bus_power_manageable(acpi_handle handle)
{
struct acpi_device *device;
int result;
result = acpi_bus_get_device(handle, &device);
return result ? false : device->flags.power_manageable;
}
EXPORT_SYMBOL(acpi_bus_power_manageable);
bool acpi_bus_can_wakeup(acpi_handle handle)
{
struct acpi_device *device;
int result;
result = acpi_bus_get_device(handle, &device);
return result ? false : device->wakeup.flags.valid;
}
EXPORT_SYMBOL(acpi_bus_can_wakeup);
static void acpi_print_osc_error(acpi_handle handle,
struct acpi_osc_context *context, char *error)
{

View file

@ -30,6 +30,12 @@
#include <acpi/acpi.h>
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>
#include "internal.h"
#define _COMPONENT ACPI_POWER_COMPONENT
ACPI_MODULE_NAME("device_pm");
static DEFINE_MUTEX(acpi_pm_notifier_lock);
@ -93,6 +99,293 @@ acpi_status acpi_remove_pm_notifier(struct acpi_device *adev,
return status;
}
/**
* acpi_power_state_string - String representation of ACPI device power state.
* @state: ACPI device power state to return the string representation of.
*/
const char *acpi_power_state_string(int state)
{
switch (state) {
case ACPI_STATE_D0:
return "D0";
case ACPI_STATE_D1:
return "D1";
case ACPI_STATE_D2:
return "D2";
case ACPI_STATE_D3_HOT:
return "D3hot";
case ACPI_STATE_D3_COLD:
return "D3cold";
default:
return "(unknown)";
}
}
/**
* acpi_device_get_power - Get power state of an ACPI device.
* @device: Device to get the power state of.
* @state: Place to store the power state of the device.
*
* This function does not update the device's power.state field, but it may
* update its parent's power.state field (when the parent's power state is
* unknown and the device's power state turns out to be D0).
*/
int acpi_device_get_power(struct acpi_device *device, int *state)
{
int result = ACPI_STATE_UNKNOWN;
if (!device || !state)
return -EINVAL;
if (!device->flags.power_manageable) {
/* TBD: Non-recursive algorithm for walking up hierarchy. */
*state = device->parent ?
device->parent->power.state : ACPI_STATE_D0;
goto out;
}
/*
* Get the device's power state either directly (via _PSC) or
* indirectly (via power resources).
*/
if (device->power.flags.explicit_get) {
unsigned long long psc;
acpi_status status = acpi_evaluate_integer(device->handle,
"_PSC", NULL, &psc);
if (ACPI_FAILURE(status))
return -ENODEV;
result = psc;
}
/* The test below covers ACPI_STATE_UNKNOWN too. */
if (result <= ACPI_STATE_D2) {
; /* Do nothing. */
} else if (device->power.flags.power_resources) {
int error = acpi_power_get_inferred_state(device, &result);
if (error)
return error;
} else if (result == ACPI_STATE_D3_HOT) {
result = ACPI_STATE_D3;
}
/*
* If we were unsure about the device parent's power state up to this
* point, the fact that the device is in D0 implies that the parent has
* to be in D0 too.
*/
if (device->parent && device->parent->power.state == ACPI_STATE_UNKNOWN
&& result == ACPI_STATE_D0)
device->parent->power.state = ACPI_STATE_D0;
*state = result;
out:
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] power state is %s\n",
device->pnp.bus_id, acpi_power_state_string(*state)));
return 0;
}
static int acpi_dev_pm_explicit_set(struct acpi_device *adev, int state)
{
if (adev->power.states[state].flags.explicit_set) {
char method[5] = { '_', 'P', 'S', '0' + state, '\0' };
acpi_status status;
status = acpi_evaluate_object(adev->handle, method, NULL, NULL);
if (ACPI_FAILURE(status))
return -ENODEV;
}
return 0;
}
/**
* acpi_device_set_power - Set power state of an ACPI device.
* @device: Device to set the power state of.
* @state: New power state to set.
*
* Callers must ensure that the device is power manageable before using this
* function.
*/
int acpi_device_set_power(struct acpi_device *device, int state)
{
int result = 0;
bool cut_power = false;
if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD))
return -EINVAL;
/* Make sure this is a valid target state */
if (state == device->power.state) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device is already at %s\n",
acpi_power_state_string(state)));
return 0;
}
if (!device->power.states[state].flags.valid) {
printk(KERN_WARNING PREFIX "Device does not support %s\n",
acpi_power_state_string(state));
return -ENODEV;
}
if (device->parent && (state < device->parent->power.state)) {
printk(KERN_WARNING PREFIX
"Cannot set device to a higher-powered"
" state than parent\n");
return -ENODEV;
}
/* For D3cold we should first transition into D3hot. */
if (state == ACPI_STATE_D3_COLD
&& device->power.states[ACPI_STATE_D3_COLD].flags.os_accessible) {
state = ACPI_STATE_D3_HOT;
cut_power = true;
}
if (state < device->power.state && state != ACPI_STATE_D0
&& device->power.state >= ACPI_STATE_D3_HOT) {
printk(KERN_WARNING PREFIX
"Cannot transition to non-D0 state from D3\n");
return -ENODEV;
}
/*
* Transition Power
* ----------------
* In accordance with the ACPI specification first apply power (via
* power resources) and then evalute _PSx.
*/
if (device->power.flags.power_resources) {
result = acpi_power_transition(device, state);
if (result)
goto end;
}
result = acpi_dev_pm_explicit_set(device, state);
if (result)
goto end;
if (cut_power) {
device->power.state = state;
state = ACPI_STATE_D3_COLD;
result = acpi_power_transition(device, state);
}
end:
if (result) {
printk(KERN_WARNING PREFIX
"Device [%s] failed to transition to %s\n",
device->pnp.bus_id,
acpi_power_state_string(state));
} else {
device->power.state = state;
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Device [%s] transitioned to %s\n",
device->pnp.bus_id,
acpi_power_state_string(state)));
}
return result;
}
EXPORT_SYMBOL(acpi_device_set_power);
int acpi_bus_set_power(acpi_handle handle, int state)
{
struct acpi_device *device;
int result;
result = acpi_bus_get_device(handle, &device);
if (result)
return result;
if (!device->flags.power_manageable) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Device [%s] is not power manageable\n",
dev_name(&device->dev)));
return -ENODEV;
}
return acpi_device_set_power(device, state);
}
EXPORT_SYMBOL(acpi_bus_set_power);
int acpi_bus_init_power(struct acpi_device *device)
{
int state;
int result;
if (!device)
return -EINVAL;
device->power.state = ACPI_STATE_UNKNOWN;
result = acpi_device_get_power(device, &state);
if (result)
return result;
if (state < ACPI_STATE_D3_COLD && device->power.flags.power_resources) {
result = acpi_power_on_resources(device, state);
if (result)
return result;
result = acpi_dev_pm_explicit_set(device, state);
if (result)
return result;
} else if (state == ACPI_STATE_UNKNOWN) {
/* No power resources and missing _PSC? Try to force D0. */
state = ACPI_STATE_D0;
result = acpi_dev_pm_explicit_set(device, state);
if (result)
return result;
}
device->power.state = state;
return 0;
}
int acpi_bus_update_power(acpi_handle handle, int *state_p)
{
struct acpi_device *device;
int state;
int result;
result = acpi_bus_get_device(handle, &device);
if (result)
return result;
result = acpi_device_get_power(device, &state);
if (result)
return result;
if (state == ACPI_STATE_UNKNOWN)
state = ACPI_STATE_D0;
result = acpi_device_set_power(device, state);
if (!result && state_p)
*state_p = state;
return result;
}
EXPORT_SYMBOL_GPL(acpi_bus_update_power);
bool acpi_bus_power_manageable(acpi_handle handle)
{
struct acpi_device *device;
int result;
result = acpi_bus_get_device(handle, &device);
return result ? false : device->flags.power_manageable;
}
EXPORT_SYMBOL(acpi_bus_power_manageable);
bool acpi_bus_can_wakeup(acpi_handle handle)
{
struct acpi_device *device;
int result;
result = acpi_bus_get_device(handle, &device);
return result ? false : device->wakeup.flags.valid;
}
EXPORT_SYMBOL(acpi_bus_can_wakeup);
/**
* acpi_device_power_state - Get preferred power state of ACPI device.
* @dev: Device whose preferred target power state to return.
@ -213,7 +506,7 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p, int d_max_in)
acpi_handle handle = DEVICE_ACPI_HANDLE(dev);
struct acpi_device *adev;
if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &adev))) {
if (!handle || acpi_bus_get_device(handle, &adev)) {
dev_dbg(dev, "ACPI handle without context in %s!\n", __func__);
return -ENODEV;
}
@ -290,7 +583,7 @@ int acpi_pm_device_run_wake(struct device *phys_dev, bool enable)
return -EINVAL;
handle = DEVICE_ACPI_HANDLE(phys_dev);
if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &adev))) {
if (!handle || acpi_bus_get_device(handle, &adev)) {
dev_dbg(phys_dev, "ACPI handle without context in %s!\n",
__func__);
return -ENODEV;
@ -304,7 +597,7 @@ static inline void acpi_wakeup_device(acpi_handle handle, u32 event,
void *context) {}
#endif /* CONFIG_PM_RUNTIME */
#ifdef CONFIG_PM_SLEEP
#ifdef CONFIG_PM_SLEEP
/**
* __acpi_device_sleep_wake - Enable or disable device to wake up the system.
* @dev: Device to enable/desible to wake up the system.
@ -334,7 +627,7 @@ int acpi_pm_device_sleep_wake(struct device *dev, bool enable)
return -EINVAL;
handle = DEVICE_ACPI_HANDLE(dev);
if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &adev))) {
if (!handle || acpi_bus_get_device(handle, &adev)) {
dev_dbg(dev, "ACPI handle without context in %s!\n", __func__);
return -ENODEV;
}
@ -665,3 +958,59 @@ void acpi_dev_pm_detach(struct device *dev, bool power_off)
}
}
EXPORT_SYMBOL_GPL(acpi_dev_pm_detach);
/**
* acpi_dev_pm_add_dependent - Add physical device depending for PM.
* @handle: Handle of ACPI device node.
* @depdev: Device depending on that node for PM.
*/
void acpi_dev_pm_add_dependent(acpi_handle handle, struct device *depdev)
{
struct acpi_device_physical_node *dep;
struct acpi_device *adev;
if (!depdev || acpi_bus_get_device(handle, &adev))
return;
mutex_lock(&adev->physical_node_lock);
list_for_each_entry(dep, &adev->power_dependent, node)
if (dep->dev == depdev)
goto out;
dep = kzalloc(sizeof(*dep), GFP_KERNEL);
if (dep) {
dep->dev = depdev;
list_add_tail(&dep->node, &adev->power_dependent);
}
out:
mutex_unlock(&adev->physical_node_lock);
}
EXPORT_SYMBOL_GPL(acpi_dev_pm_add_dependent);
/**
* acpi_dev_pm_remove_dependent - Remove physical device depending for PM.
* @handle: Handle of ACPI device node.
* @depdev: Device depending on that node for PM.
*/
void acpi_dev_pm_remove_dependent(acpi_handle handle, struct device *depdev)
{
struct acpi_device_physical_node *dep;
struct acpi_device *adev;
if (!depdev || acpi_bus_get_device(handle, &adev))
return;
mutex_lock(&adev->physical_node_lock);
list_for_each_entry(dep, &adev->power_dependent, node)
if (dep->dev == depdev) {
list_del(&dep->node);
kfree(dep);
break;
}
mutex_unlock(&adev->physical_node_lock);
}
EXPORT_SYMBOL_GPL(acpi_dev_pm_remove_dependent);

View file

@ -34,16 +34,34 @@ int acpi_debugfs_init(void);
static inline void acpi_debugfs_init(void) { return; }
#endif
/* --------------------------------------------------------------------------
Device Node Initialization / Removal
-------------------------------------------------------------------------- */
#define ACPI_STA_DEFAULT (ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED | \
ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING)
int acpi_device_add(struct acpi_device *device,
void (*release)(struct device *));
void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
int type, unsigned long long sta);
void acpi_device_add_finalize(struct acpi_device *device);
void acpi_free_ids(struct acpi_device *device);
/* --------------------------------------------------------------------------
Power Resource
-------------------------------------------------------------------------- */
int acpi_power_init(void);
void acpi_power_resources_list_free(struct list_head *list);
int acpi_extract_power_resources(union acpi_object *package, unsigned int start,
struct list_head *list);
int acpi_add_power_resource(acpi_handle handle);
void acpi_power_add_remove_device(struct acpi_device *adev, bool add);
int acpi_power_min_system_level(struct list_head *list);
int acpi_device_sleep_wake(struct acpi_device *dev,
int enable, int sleep_state, int dev_state);
int acpi_power_get_inferred_state(struct acpi_device *device, int *state);
int acpi_power_on_resources(struct acpi_device *device, int state);
int acpi_power_transition(struct acpi_device *device, int state);
int acpi_bus_init_power(struct acpi_device *device);
int acpi_wakeup_device_init(void);
void acpi_early_processor_set_pdc(void);

File diff suppressed because it is too large Load diff

View file

@ -311,11 +311,12 @@ acpi_system_wakeup_device_seq_show(struct seq_file *seq, void *offset)
dev->pnp.bus_id,
(u32) dev->wakeup.sleep_state);
if (!dev->physical_node_count)
if (!dev->physical_node_count) {
seq_printf(seq, "%c%-8s\n",
dev->wakeup.flags.run_wake ?
'*' : ' ', "disabled");
else {
dev->wakeup.flags.run_wake ? '*' : ' ',
device_may_wakeup(&dev->dev) ?
"enabled" : "disabled");
} else {
struct device *ldev;
list_for_each_entry(entry, &dev->physical_node_list,
node) {

View file

@ -174,6 +174,32 @@ err_out:
}
EXPORT_SYMBOL(acpi_bus_hot_remove_device);
static ssize_t real_power_state_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct acpi_device *adev = to_acpi_device(dev);
int state;
int ret;
ret = acpi_device_get_power(adev, &state);
if (ret)
return ret;
return sprintf(buf, "%s\n", acpi_power_state_string(state));
}
static DEVICE_ATTR(real_power_state, 0444, real_power_state_show, NULL);
static ssize_t power_state_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct acpi_device *adev = to_acpi_device(dev);
return sprintf(buf, "%s\n", acpi_power_state_string(adev->power.state));
}
static DEVICE_ATTR(power_state, 0444, power_state_show, NULL);
static ssize_t
acpi_eject_store(struct device *d, struct device_attribute *attr,
const char *buf, size_t count)
@ -365,8 +391,22 @@ static int acpi_device_setup_files(struct acpi_device *dev)
* hot-removal function from userland.
*/
status = acpi_get_handle(dev->handle, "_EJ0", &temp);
if (ACPI_SUCCESS(status))
if (ACPI_SUCCESS(status)) {
result = device_create_file(&dev->dev, &dev_attr_eject);
if (result)
return result;
}
if (dev->flags.power_manageable) {
result = device_create_file(&dev->dev, &dev_attr_power_state);
if (result)
return result;
if (dev->power.flags.power_resources)
result = device_create_file(&dev->dev,
&dev_attr_real_power_state);
}
end:
return result;
}
@ -376,6 +416,13 @@ static void acpi_device_remove_files(struct acpi_device *dev)
acpi_status status;
acpi_handle temp;
if (dev->flags.power_manageable) {
device_remove_file(&dev->dev, &dev_attr_power_state);
if (dev->power.flags.power_resources)
device_remove_file(&dev->dev,
&dev_attr_real_power_state);
}
/*
* If device has _STR, remove 'description' file
*/
@ -460,7 +507,7 @@ int acpi_match_device_ids(struct acpi_device *device,
}
EXPORT_SYMBOL(acpi_match_device_ids);
static void acpi_free_ids(struct acpi_device *device)
void acpi_free_ids(struct acpi_device *device)
{
struct acpi_hardware_id *id, *tmp;
@ -468,6 +515,23 @@ static void acpi_free_ids(struct acpi_device *device)
kfree(id->id);
kfree(id);
}
kfree(device->pnp.unique_id);
}
static void acpi_free_power_resources_lists(struct acpi_device *device)
{
int i;
if (device->wakeup.flags.valid)
acpi_power_resources_list_free(&device->wakeup.resources);
if (!device->flags.power_manageable)
return;
for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) {
struct acpi_device_power_state *ps = &device->power.states[i];
acpi_power_resources_list_free(&ps->resources);
}
}
static void acpi_device_release(struct device *dev)
@ -475,7 +539,7 @@ static void acpi_device_release(struct device *dev)
struct acpi_device *acpi_dev = to_acpi_device(dev);
acpi_free_ids(acpi_dev);
kfree(acpi_dev->pnp.unique_id);
acpi_free_power_resources_lists(acpi_dev);
kfree(acpi_dev);
}
@ -613,12 +677,25 @@ struct bus_type acpi_bus_type = {
.uevent = acpi_device_uevent,
};
static int acpi_device_register(struct acpi_device *device)
int acpi_device_add(struct acpi_device *device,
void (*release)(struct device *))
{
int result;
struct acpi_device_bus_id *acpi_device_bus_id, *new_bus_id;
int found = 0;
if (device->handle) {
acpi_status status;
status = acpi_attach_data(device->handle, acpi_bus_data_handler,
device);
if (ACPI_FAILURE(status)) {
acpi_handle_err(device->handle,
"Unable to attach device data\n");
return -ENODEV;
}
}
/*
* Linkage
* -------
@ -629,11 +706,13 @@ static int acpi_device_register(struct acpi_device *device)
INIT_LIST_HEAD(&device->wakeup_list);
INIT_LIST_HEAD(&device->physical_node_list);
mutex_init(&device->physical_node_lock);
INIT_LIST_HEAD(&device->power_dependent);
new_bus_id = kzalloc(sizeof(struct acpi_device_bus_id), GFP_KERNEL);
if (!new_bus_id) {
printk(KERN_ERR PREFIX "Memory allocation error\n");
return -ENOMEM;
pr_err(PREFIX "Memory allocation error\n");
result = -ENOMEM;
goto err_detach;
}
mutex_lock(&acpi_device_lock);
@ -668,11 +747,11 @@ static int acpi_device_register(struct acpi_device *device)
if (device->parent)
device->dev.parent = &device->parent->dev;
device->dev.bus = &acpi_bus_type;
device->dev.release = &acpi_device_release;
result = device_register(&device->dev);
device->dev.release = release;
result = device_add(&device->dev);
if (result) {
dev_err(&device->dev, "Error registering device\n");
goto end;
goto err;
}
result = acpi_device_setup_files(device);
@ -682,12 +761,16 @@ static int acpi_device_register(struct acpi_device *device)
device->removal_type = ACPI_BUS_REMOVAL_NORMAL;
return 0;
end:
err:
mutex_lock(&acpi_device_lock);
if (device->parent)
list_del(&device->node);
list_del(&device->wakeup_list);
mutex_unlock(&acpi_device_lock);
err_detach:
acpi_detach_data(device->handle, acpi_bus_data_handler);
return result;
}
@ -702,8 +785,18 @@ static void acpi_device_unregister(struct acpi_device *device)
acpi_detach_data(device->handle, acpi_bus_data_handler);
acpi_power_add_remove_device(device, false);
acpi_device_remove_files(device);
device_unregister(&device->dev);
if (device->remove)
device->remove(device);
device_del(&device->dev);
/*
* Drop the reference counts of all power resources the device depends
* on and turn off the ones that have no more references.
*/
acpi_power_transition(device, ACPI_STATE_D3_COLD);
put_device(&device->dev);
}
/* --------------------------------------------------------------------------
@ -846,52 +939,43 @@ void acpi_bus_data_handler(acpi_handle handle, void *context)
return;
}
static int acpi_bus_get_perf_flags(struct acpi_device *device)
{
device->performance.state = ACPI_STATE_UNKNOWN;
return 0;
}
static acpi_status
acpi_bus_extract_wakeup_device_power_package(acpi_handle handle,
struct acpi_device_wakeup *wakeup)
static int acpi_bus_extract_wakeup_device_power_package(acpi_handle handle,
struct acpi_device_wakeup *wakeup)
{
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *package = NULL;
union acpi_object *element = NULL;
acpi_status status;
int i = 0;
int err = -ENODATA;
if (!wakeup)
return AE_BAD_PARAMETER;
return -EINVAL;
INIT_LIST_HEAD(&wakeup->resources);
/* _PRW */
status = acpi_evaluate_object(handle, "_PRW", NULL, &buffer);
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PRW"));
return status;
return err;
}
package = (union acpi_object *)buffer.pointer;
if (!package || (package->package.count < 2)) {
status = AE_BAD_DATA;
if (!package || package->package.count < 2)
goto out;
}
element = &(package->package.elements[0]);
if (!element) {
status = AE_BAD_DATA;
if (!element)
goto out;
}
if (element->type == ACPI_TYPE_PACKAGE) {
if ((element->package.count < 2) ||
(element->package.elements[0].type !=
ACPI_TYPE_LOCAL_REFERENCE)
|| (element->package.elements[1].type != ACPI_TYPE_INTEGER)) {
status = AE_BAD_DATA;
|| (element->package.elements[1].type != ACPI_TYPE_INTEGER))
goto out;
}
wakeup->gpe_device =
element->package.elements[0].reference.handle;
wakeup->gpe_number =
@ -900,38 +984,35 @@ acpi_bus_extract_wakeup_device_power_package(acpi_handle handle,
wakeup->gpe_device = NULL;
wakeup->gpe_number = element->integer.value;
} else {
status = AE_BAD_DATA;
goto out;
}
element = &(package->package.elements[1]);
if (element->type != ACPI_TYPE_INTEGER) {
status = AE_BAD_DATA;
if (element->type != ACPI_TYPE_INTEGER)
goto out;
}
wakeup->sleep_state = element->integer.value;
if ((package->package.count - 2) > ACPI_MAX_HANDLES) {
status = AE_NO_MEMORY;
err = acpi_extract_power_resources(package, 2, &wakeup->resources);
if (err)
goto out;
}
wakeup->resources.count = package->package.count - 2;
for (i = 0; i < wakeup->resources.count; i++) {
element = &(package->package.elements[i + 2]);
if (element->type != ACPI_TYPE_LOCAL_REFERENCE) {
status = AE_BAD_DATA;
goto out;
if (!list_empty(&wakeup->resources)) {
int sleep_state;
sleep_state = acpi_power_min_system_level(&wakeup->resources);
if (sleep_state < wakeup->sleep_state) {
acpi_handle_warn(handle, "Overriding _PRW sleep state "
"(S%d) by S%d from power resources\n",
(int)wakeup->sleep_state, sleep_state);
wakeup->sleep_state = sleep_state;
}
wakeup->resources.handles[i] = element->reference.handle;
}
acpi_setup_gpe_for_wake(handle, wakeup->gpe_device, wakeup->gpe_number);
out:
kfree(buffer.pointer);
return status;
return err;
}
static void acpi_bus_set_run_wake_flags(struct acpi_device *device)
@ -971,17 +1052,17 @@ static void acpi_bus_get_wakeup_device_flags(struct acpi_device *device)
{
acpi_handle temp;
acpi_status status = 0;
int psw_error;
int err;
/* Presence of _PRW indicates wake capable */
status = acpi_get_handle(device->handle, "_PRW", &temp);
if (ACPI_FAILURE(status))
return;
status = acpi_bus_extract_wakeup_device_power_package(device->handle,
&device->wakeup);
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status, "Extracting _PRW package"));
err = acpi_bus_extract_wakeup_device_power_package(device->handle,
&device->wakeup);
if (err) {
dev_err(&device->dev, "_PRW evaluation error: %d\n", err);
return;
}
@ -994,20 +1075,73 @@ static void acpi_bus_get_wakeup_device_flags(struct acpi_device *device)
* So it is necessary to call _DSW object first. Only when it is not
* present will the _PSW object used.
*/
psw_error = acpi_device_sleep_wake(device, 0, 0, 0);
if (psw_error)
err = acpi_device_sleep_wake(device, 0, 0, 0);
if (err)
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"error in _DSW or _PSW evaluation\n"));
}
static void acpi_bus_add_power_resource(acpi_handle handle);
static int acpi_bus_get_power_flags(struct acpi_device *device)
static void acpi_bus_init_power_state(struct acpi_device *device, int state)
{
acpi_status status = 0;
acpi_handle handle = NULL;
u32 i = 0;
struct acpi_device_power_state *ps = &device->power.states[state];
char pathname[5] = { '_', 'P', 'R', '0' + state, '\0' };
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
acpi_handle handle;
acpi_status status;
INIT_LIST_HEAD(&ps->resources);
/* Evaluate "_PRx" to get referenced power resources */
status = acpi_evaluate_object(device->handle, pathname, NULL, &buffer);
if (ACPI_SUCCESS(status)) {
union acpi_object *package = buffer.pointer;
if (buffer.length && package
&& package->type == ACPI_TYPE_PACKAGE
&& package->package.count) {
int err = acpi_extract_power_resources(package, 0,
&ps->resources);
if (!err)
device->power.flags.power_resources = 1;
}
ACPI_FREE(buffer.pointer);
}
/* Evaluate "_PSx" to see if we can do explicit sets */
pathname[2] = 'S';
status = acpi_get_handle(device->handle, pathname, &handle);
if (ACPI_SUCCESS(status))
ps->flags.explicit_set = 1;
/*
* State is valid if there are means to put the device into it.
* D3hot is only valid if _PR3 present.
*/
if (!list_empty(&ps->resources)
|| (ps->flags.explicit_set && state < ACPI_STATE_D3_HOT)) {
ps->flags.valid = 1;
ps->flags.os_accessible = 1;
}
ps->power = -1; /* Unknown - driver assigned */
ps->latency = -1; /* Unknown - driver assigned */
}
static void acpi_bus_get_power_flags(struct acpi_device *device)
{
acpi_status status;
acpi_handle handle;
u32 i;
/* Presence of _PS0|_PR0 indicates 'power manageable' */
status = acpi_get_handle(device->handle, "_PS0", &handle);
if (ACPI_FAILURE(status)) {
status = acpi_get_handle(device->handle, "_PR0", &handle);
if (ACPI_FAILURE(status))
return;
}
device->flags.power_manageable = 1;
/*
* Power Management Flags
@ -1022,40 +1156,10 @@ static int acpi_bus_get_power_flags(struct acpi_device *device)
/*
* Enumerate supported power management states
*/
for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) {
struct acpi_device_power_state *ps = &device->power.states[i];
char object_name[5] = { '_', 'P', 'R', '0' + i, '\0' };
for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++)
acpi_bus_init_power_state(device, i);
/* Evaluate "_PRx" to se if power resources are referenced */
acpi_evaluate_reference(device->handle, object_name, NULL,
&ps->resources);
if (ps->resources.count) {
int j;
device->power.flags.power_resources = 1;
for (j = 0; j < ps->resources.count; j++)
acpi_bus_add_power_resource(ps->resources.handles[j]);
}
/* Evaluate "_PSx" to see if we can do explicit sets */
object_name[2] = 'S';
status = acpi_get_handle(device->handle, object_name, &handle);
if (ACPI_SUCCESS(status))
ps->flags.explicit_set = 1;
/*
* State is valid if there are means to put the device into it.
* D3hot is only valid if _PR3 present.
*/
if (ps->resources.count ||
(ps->flags.explicit_set && i < ACPI_STATE_D3_HOT)) {
ps->flags.valid = 1;
ps->flags.os_accessible = 1;
}
ps->power = -1; /* Unknown - driver assigned */
ps->latency = -1; /* Unknown - driver assigned */
}
INIT_LIST_HEAD(&device->power.states[ACPI_STATE_D3_COLD].resources);
/* Set defaults for D0 and D3 states (always valid) */
device->power.states[ACPI_STATE_D0].flags.valid = 1;
@ -1072,17 +1176,17 @@ static int acpi_bus_get_power_flags(struct acpi_device *device)
device->power.flags.power_resources)
device->power.states[ACPI_STATE_D3_COLD].flags.os_accessible = 1;
acpi_bus_init_power(device);
return 0;
if (acpi_bus_init_power(device)) {
acpi_free_power_resources_lists(device);
device->flags.power_manageable = 0;
}
}
static int acpi_bus_get_flags(struct acpi_device *device)
static void acpi_bus_get_flags(struct acpi_device *device)
{
acpi_status status = AE_OK;
acpi_handle temp = NULL;
/* Presence of _STA indicates 'dynamic_status' */
status = acpi_get_handle(device->handle, "_STA", &temp);
if (ACPI_SUCCESS(status))
@ -1102,21 +1206,6 @@ static int acpi_bus_get_flags(struct acpi_device *device)
if (ACPI_SUCCESS(status))
device->flags.ejectable = 1;
}
/* Power resources cannot be power manageable. */
if (device->device_type == ACPI_BUS_TYPE_POWER)
return 0;
/* Presence of _PS0|_PR0 indicates 'power manageable' */
status = acpi_get_handle(device->handle, "_PS0", &temp);
if (ACPI_FAILURE(status))
status = acpi_get_handle(device->handle, "_PR0", &temp);
if (ACPI_SUCCESS(status))
device->flags.power_manageable = 1;
/* TBD: Performance management */
return 0;
}
static void acpi_device_get_busid(struct acpi_device *device)
@ -1341,27 +1430,25 @@ static void acpi_device_set_id(struct acpi_device *device)
}
}
static int acpi_device_set_context(struct acpi_device *device)
void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
int type, unsigned long long sta)
{
acpi_status status;
INIT_LIST_HEAD(&device->pnp.ids);
device->device_type = type;
device->handle = handle;
device->parent = acpi_bus_get_parent(handle);
STRUCT_TO_INT(device->status) = sta;
acpi_device_get_busid(device);
acpi_device_set_id(device);
acpi_bus_get_flags(device);
device_initialize(&device->dev);
dev_set_uevent_suppress(&device->dev, true);
}
/*
* Context
* -------
* Attach this 'struct acpi_device' to the ACPI object. This makes
* resolutions from handle->device very efficient. Fixed hardware
* devices have no handles, so we skip them.
*/
if (!device->handle)
return 0;
status = acpi_attach_data(device->handle,
acpi_bus_data_handler, device);
if (ACPI_SUCCESS(status))
return 0;
printk(KERN_ERR PREFIX "Error attaching device data\n");
return -ENODEV;
void acpi_device_add_finalize(struct acpi_device *device)
{
dev_set_uevent_suppress(&device->dev, false);
kobject_uevent(&device->dev.kobj, KOBJ_ADD);
}
static int acpi_add_single_object(struct acpi_device **child,
@ -1378,90 +1465,26 @@ static int acpi_add_single_object(struct acpi_device **child,
return -ENOMEM;
}
INIT_LIST_HEAD(&device->pnp.ids);
device->device_type = type;
device->handle = handle;
device->parent = acpi_bus_get_parent(handle);
STRUCT_TO_INT(device->status) = sta;
acpi_device_get_busid(device);
/*
* Flags
* -----
* Note that we only look for object handles -- cannot evaluate objects
* until we know the device is present and properly initialized.
*/
result = acpi_bus_get_flags(device);
if (result)
goto end;
/*
* Initialize Device
* -----------------
* TBD: Synch with Core's enumeration/initialization process.
*/
acpi_device_set_id(device);
/*
* Power Management
* ----------------
*/
if (device->flags.power_manageable) {
result = acpi_bus_get_power_flags(device);
if (result)
goto end;
}
/*
* Wakeup device management
*-----------------------
*/
acpi_init_device_object(device, handle, type, sta);
acpi_bus_get_power_flags(device);
acpi_bus_get_wakeup_device_flags(device);
/*
* Performance Management
* ----------------------
*/
if (device->flags.performance_manageable) {
result = acpi_bus_get_perf_flags(device);
if (result)
goto end;
device->flags.match_driver = match_driver;
result = acpi_device_add(device, acpi_device_release);
if (result) {
acpi_device_release(&device->dev);
return result;
}
if ((result = acpi_device_set_context(device)))
goto end;
device->flags.match_driver = match_driver;
result = acpi_device_register(device);
end:
if (!result) {
acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Adding %s [%s] parent %s\n", dev_name(&device->dev),
(char *) buffer.pointer,
device->parent ? dev_name(&device->parent->dev) :
"(null)"));
kfree(buffer.pointer);
*child = device;
} else
acpi_device_release(&device->dev);
return result;
}
#define ACPI_STA_DEFAULT (ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED | \
ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING)
static void acpi_bus_add_power_resource(acpi_handle handle)
{
struct acpi_device *device = NULL;
acpi_bus_get_device(handle, &device);
if (!device)
acpi_add_single_object(&device, handle, ACPI_BUS_TYPE_POWER,
ACPI_STA_DEFAULT, true);
acpi_power_add_remove_device(device, true);
acpi_device_add_finalize(device);
acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Added %s [%s] parent %s\n",
dev_name(&device->dev), (char *) buffer.pointer,
device->parent ? dev_name(&device->parent->dev) : "(null)"));
kfree(buffer.pointer);
*child = device;
return 0;
}
static int acpi_bus_type_and_status(acpi_handle handle, int *type,
@ -1520,20 +1543,26 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used,
if (result)
return AE_OK;
if (type == ACPI_BUS_TYPE_POWER) {
acpi_add_power_resource(handle);
return AE_OK;
}
if (!(sta & ACPI_STA_DEVICE_PRESENT) &&
!(sta & ACPI_STA_DEVICE_FUNCTIONING)) {
struct acpi_device_wakeup wakeup;
acpi_handle temp;
status = acpi_get_handle(handle, "_PRW", &temp);
if (ACPI_SUCCESS(status))
if (ACPI_SUCCESS(status)) {
acpi_bus_extract_wakeup_device_power_package(handle,
&wakeup);
acpi_power_resources_list_free(&wakeup.resources);
}
return AE_CTRL_DEPTH;
}
acpi_add_single_object(&device, handle, type, sta,
type == ACPI_BUS_TYPE_POWER);
acpi_add_single_object(&device, handle, type, sta, false);
if (!device)
return AE_CTRL_DEPTH;
@ -1687,7 +1716,6 @@ int __init acpi_scan_init(void)
printk(KERN_ERR PREFIX "Could not register bus type\n");
}
acpi_power_init();
acpi_pci_root_init();
/*

View file

@ -386,6 +386,8 @@ static void acpi_pm_finish(void)
acpi_target_sleep_state = ACPI_STATE_S0;
acpi_resume_power_resources();
/* If we were woken with the fixed power button, provide a small
* hint to userspace in the form of a wakeup event on the fixed power
* button device (if it can be found).
@ -577,7 +579,28 @@ static const struct platform_suspend_ops acpi_suspend_ops_old = {
.end = acpi_pm_end,
.recover = acpi_pm_finish,
};
#endif /* CONFIG_SUSPEND */
static void acpi_sleep_suspend_setup(void)
{
int i;
for (i = ACPI_STATE_S1; i < ACPI_STATE_S4; i++) {
acpi_status status;
u8 type_a, type_b;
status = acpi_get_sleep_type_data(i, &type_a, &type_b);
if (ACPI_SUCCESS(status)) {
sleep_states[i] = 1;
pr_cont(" S%d", i);
}
}
suspend_set_ops(old_suspend_ordering ?
&acpi_suspend_ops_old : &acpi_suspend_ops);
}
#else /* !CONFIG_SUSPEND */
static inline void acpi_sleep_suspend_setup(void) {}
#endif /* !CONFIG_SUSPEND */
#ifdef CONFIG_HIBERNATION
static unsigned long s4_hardware_signature;
@ -698,7 +721,30 @@ static const struct platform_hibernation_ops acpi_hibernation_ops_old = {
.restore_cleanup = acpi_pm_thaw,
.recover = acpi_pm_finish,
};
#endif /* CONFIG_HIBERNATION */
static void acpi_sleep_hibernate_setup(void)
{
acpi_status status;
u8 type_a, type_b;
status = acpi_get_sleep_type_data(ACPI_STATE_S4, &type_a, &type_b);
if (ACPI_FAILURE(status))
return;
hibernation_set_ops(old_suspend_ordering ?
&acpi_hibernation_ops_old : &acpi_hibernation_ops);
sleep_states[ACPI_STATE_S4] = 1;
pr_cont(KERN_CONT " S4");
if (nosigcheck)
return;
acpi_get_table(ACPI_SIG_FACS, 1, (struct acpi_table_header **)&facs);
if (facs)
s4_hardware_signature = facs->hardware_signature;
}
#else /* !CONFIG_HIBERNATION */
static inline void acpi_sleep_hibernate_setup(void) {}
#endif /* !CONFIG_HIBERNATION */
int acpi_suspend(u32 acpi_state)
{
@ -734,9 +780,6 @@ int __init acpi_sleep_init(void)
{
acpi_status status;
u8 type_a, type_b;
#ifdef CONFIG_SUSPEND
int i = 0;
#endif
if (acpi_disabled)
return 0;
@ -744,45 +787,19 @@ int __init acpi_sleep_init(void)
acpi_sleep_dmi_check();
sleep_states[ACPI_STATE_S0] = 1;
printk(KERN_INFO PREFIX "(supports S0");
pr_info(PREFIX "(supports S0");
#ifdef CONFIG_SUSPEND
for (i = ACPI_STATE_S1; i < ACPI_STATE_S4; i++) {
status = acpi_get_sleep_type_data(i, &type_a, &type_b);
if (ACPI_SUCCESS(status)) {
sleep_states[i] = 1;
printk(KERN_CONT " S%d", i);
}
}
acpi_sleep_suspend_setup();
acpi_sleep_hibernate_setup();
suspend_set_ops(old_suspend_ordering ?
&acpi_suspend_ops_old : &acpi_suspend_ops);
#endif
#ifdef CONFIG_HIBERNATION
status = acpi_get_sleep_type_data(ACPI_STATE_S4, &type_a, &type_b);
if (ACPI_SUCCESS(status)) {
hibernation_set_ops(old_suspend_ordering ?
&acpi_hibernation_ops_old : &acpi_hibernation_ops);
sleep_states[ACPI_STATE_S4] = 1;
printk(KERN_CONT " S4");
if (!nosigcheck) {
acpi_get_table(ACPI_SIG_FACS, 1,
(struct acpi_table_header **)&facs);
if (facs)
s4_hardware_signature =
facs->hardware_signature;
}
}
#endif
status = acpi_get_sleep_type_data(ACPI_STATE_S5, &type_a, &type_b);
if (ACPI_SUCCESS(status)) {
sleep_states[ACPI_STATE_S5] = 1;
printk(KERN_CONT " S5");
pr_cont(" S5");
pm_power_off_prepare = acpi_power_off_prepare;
pm_power_off = acpi_power_off;
}
printk(KERN_CONT ")\n");
pr_cont(")\n");
/*
* Register the tts_notifier to reboot notifier list so that the _TTS
* object can also be evaluated when the system enters S5.

View file

@ -6,3 +6,5 @@ extern void acpi_disable_wakeup_devices(u8 sleep_state);
extern struct list_head acpi_wakeup_device_list;
extern struct mutex acpi_device_lock;
extern void acpi_resume_power_resources(void);