diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2013-04-28 01:54:00 +0200 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2013-04-28 01:54:00 +0200 |
commit | 34bdb1a458baaf3e56843c36deb04283c26a8099 (patch) | |
tree | 353acc5fb0aafecb81a14d0c3c970cc39b81af9a /drivers/acpi | |
parent | 0ad4991cae47c0d3ae93e1531ba5572d223d700c (diff) | |
parent | 41a2a4665d401f79912a3da83ff9ef3e502bc12e (diff) |
Merge branch 'acpi-pm'
* acpi-pm:
ACPI / PM: Expose lists of device wakeup power resources to user space
ACPI / PM: Fix potential problem in acpi_device_get_power()
Diffstat (limited to 'drivers/acpi')
-rw-r--r-- | drivers/acpi/device_pm.c | 39 | ||||
-rw-r--r-- | drivers/acpi/power.c | 58 |
2 files changed, 63 insertions, 34 deletions
diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index dd314ef9bff1..96de787e6104 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -145,27 +145,36 @@ int acpi_device_get_power(struct acpi_device *device, int *state) } /* - * Get the device's power state either directly (via _PSC) or - * indirectly (via power resources). + * Get the device's power state from power resources settings and _PSC, + * if available. */ + if (device->power.flags.power_resources) { + int error = acpi_power_get_inferred_state(device, &result); + if (error) + return error; + } if (device->power.flags.explicit_get) { + acpi_handle handle = device->handle; unsigned long long psc; - acpi_status status = acpi_evaluate_integer(device->handle, - "_PSC", NULL, &psc); + acpi_status status; + + status = acpi_evaluate_integer(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; + /* + * The power resources settings may indicate a power state + * shallower than the actual power state of the device. + * + * Moreover, on systems predating ACPI 4.0, if the device + * doesn't depend on any power resources and _PSC returns 3, + * that means "power off". We need to maintain compatibility + * with those systems. + */ + if (psc > result && psc < ACPI_STATE_D3_COLD) + result = psc; + else if (result == ACPI_STATE_UNKNOWN) + result = psc > ACPI_STATE_D2 ? ACPI_STATE_D3_COLD : psc; } /* diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 0481b1b1d2aa..f962047c6c85 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -459,57 +459,79 @@ static struct attribute_group attr_groups[] = { }, }; -static void acpi_power_hide_list(struct acpi_device *adev, int state) +static struct attribute_group wakeup_attr_group = { + .name = "power_resources_wakeup", + .attrs = attrs, +}; + +static void acpi_power_hide_list(struct acpi_device *adev, + struct list_head *resources, + struct attribute_group *attr_group) { - struct acpi_device_power_state *ps = &adev->power.states[state]; struct acpi_power_resource_entry *entry; - if (list_empty(&ps->resources)) + if (list_empty(resources)) return; - list_for_each_entry_reverse(entry, &ps->resources, node) { + list_for_each_entry_reverse(entry, resources, node) { struct acpi_device *res_dev = &entry->resource->device; sysfs_remove_link_from_group(&adev->dev.kobj, - attr_groups[state].name, + attr_group->name, dev_name(&res_dev->dev)); } - sysfs_remove_group(&adev->dev.kobj, &attr_groups[state]); + sysfs_remove_group(&adev->dev.kobj, attr_group); } -static void acpi_power_expose_list(struct acpi_device *adev, int state) +static void acpi_power_expose_list(struct acpi_device *adev, + struct list_head *resources, + struct attribute_group *attr_group) { - struct acpi_device_power_state *ps = &adev->power.states[state]; struct acpi_power_resource_entry *entry; int ret; - if (list_empty(&ps->resources)) + if (list_empty(resources)) return; - ret = sysfs_create_group(&adev->dev.kobj, &attr_groups[state]); + ret = sysfs_create_group(&adev->dev.kobj, attr_group); if (ret) return; - list_for_each_entry(entry, &ps->resources, node) { + list_for_each_entry(entry, resources, node) { struct acpi_device *res_dev = &entry->resource->device; ret = sysfs_add_link_to_group(&adev->dev.kobj, - attr_groups[state].name, + attr_group->name, &res_dev->dev.kobj, dev_name(&res_dev->dev)); if (ret) { - acpi_power_hide_list(adev, state); + acpi_power_hide_list(adev, resources, attr_group); break; } } } +static void acpi_power_expose_hide(struct acpi_device *adev, + struct list_head *resources, + struct attribute_group *attr_group, + bool expose) +{ + if (expose) + acpi_power_expose_list(adev, resources, attr_group); + else + acpi_power_hide_list(adev, resources, attr_group); +} + void acpi_power_add_remove_device(struct acpi_device *adev, bool add) { struct acpi_device_power_state *ps; struct acpi_power_resource_entry *entry; int state; + if (adev->wakeup.flags.valid) + acpi_power_expose_hide(adev, &adev->wakeup.resources, + &wakeup_attr_group, add); + if (!adev->power.flags.power_resources) return; @@ -523,12 +545,10 @@ void acpi_power_add_remove_device(struct acpi_device *adev, bool add) acpi_power_remove_dependent(resource, adev); } - for (state = ACPI_STATE_D0; state <= ACPI_STATE_D3_HOT; state++) { - if (add) - acpi_power_expose_list(adev, state); - else - acpi_power_hide_list(adev, state); - } + for (state = ACPI_STATE_D0; state <= ACPI_STATE_D3_HOT; state++) + acpi_power_expose_hide(adev, + &adev->power.states[state].resources, + &attr_groups[state], add); } int acpi_power_wakeup_list_init(struct list_head *list, int *system_level_p) |