summaryrefslogtreecommitdiffstats
path: root/drivers/acpi
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-02-26 06:25:17 +0100
committerLinus Torvalds <torvalds@linux-foundation.org>2013-02-26 06:25:17 +0100
commitc41b3810c09e60664433548c5218cc6ece6a8903 (patch)
treecf9d822e3216d57a0ba3a184e9d586ad7bdf370c /drivers/acpi
parentMerge tag 'pci-v3.9-changes' of git://git.kernel.org/pub/scm/linux/kernel/git... (diff)
parentMerge branch 'acpi-pm' into fixes (diff)
downloadlinux-c41b3810c09e60664433548c5218cc6ece6a8903.tar.xz
linux-c41b3810c09e60664433548c5218cc6ece6a8903.zip
Merge tag 'pm+acpi-fixes-3.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
Pull ACPI and power management fixes from Rafael Wysocki: - Fixes for blackfin and microblaze build problems introduced by the removal of global pm_idle. From Lars-Peter Clausen. - OPP core build fix from Shawn Guo. - Error condition check fix for the new imx6q-cpufreq driver from Wei Yongjun. - Fix for an AER driver crash related to the lack of APEI initialization for acpi=off. From Rafael J Wysocki. - Fix for a USB breakage on Thinkpad T430 related to ACPI power resources and PCI wakeup from Rafael J. Wysocki. * tag 'pm+acpi-fixes-3.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: ACPI / PM: Take unusual configurations of power resources into account imx6q-cpufreq: fix return value check in imx6q_cpufreq_probe() PM / OPP: fix condition for empty of_init_opp_table() ACPI / APEI: Fix crash in apei_hest_parse() for acpi=off microblaze idle: Fix compile error blackfin idle: Fix compile error
Diffstat (limited to 'drivers/acpi')
-rw-r--r--drivers/acpi/apei/hest.c5
-rw-r--r--drivers/acpi/internal.h2
-rw-r--r--drivers/acpi/power.c112
-rw-r--r--drivers/acpi/scan.c9
4 files changed, 95 insertions, 33 deletions
diff --git a/drivers/acpi/apei/hest.c b/drivers/acpi/apei/hest.c
index 7f00cf38098f..f5ef5d54e4ac 100644
--- a/drivers/acpi/apei/hest.c
+++ b/drivers/acpi/apei/hest.c
@@ -89,7 +89,7 @@ int apei_hest_parse(apei_hest_func_t func, void *data)
struct acpi_hest_header *hest_hdr;
int i, rc, len;
- if (hest_disable)
+ if (hest_disable || !hest_tab)
return -EINVAL;
hest_hdr = (struct acpi_hest_header *)(hest_tab + 1);
@@ -216,9 +216,6 @@ void __init acpi_hest_init(void)
return;
}
- if (acpi_disabled)
- goto err;
-
status = acpi_get_table(ACPI_SIG_HEST, 0,
(struct acpi_table_header **)&hest_tab);
if (status == AE_NOT_FOUND)
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index c8b70b5b2814..3c94a732b4b3 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -71,7 +71,7 @@ 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_power_wakeup_list_init(struct list_head *list, int *system_level);
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);
diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c
index b820528a5fa3..34f5ef11d427 100644
--- a/drivers/acpi/power.c
+++ b/drivers/acpi/power.c
@@ -73,6 +73,7 @@ struct acpi_power_resource {
u32 system_level;
u32 order;
unsigned int ref_count;
+ bool wakeup_enabled;
struct mutex resource_lock;
};
@@ -272,11 +273,9 @@ static int __acpi_power_on(struct acpi_power_resource *resource)
return 0;
}
-static int acpi_power_on(struct acpi_power_resource *resource)
+static int acpi_power_on_unlocked(struct acpi_power_resource *resource)
{
- int result = 0;;
-
- mutex_lock(&resource->resource_lock);
+ int result = 0;
if (resource->ref_count++) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
@@ -293,9 +292,16 @@ static int acpi_power_on(struct acpi_power_resource *resource)
schedule_work(&dep->work);
}
}
+ return result;
+}
- mutex_unlock(&resource->resource_lock);
+static int acpi_power_on(struct acpi_power_resource *resource)
+{
+ int result;
+ mutex_lock(&resource->resource_lock);
+ result = acpi_power_on_unlocked(resource);
+ mutex_unlock(&resource->resource_lock);
return result;
}
@@ -313,17 +319,15 @@ static int __acpi_power_off(struct acpi_power_resource *resource)
return 0;
}
-static int acpi_power_off(struct acpi_power_resource *resource)
+static int acpi_power_off_unlocked(struct acpi_power_resource *resource)
{
int result = 0;
- mutex_lock(&resource->resource_lock);
-
if (!resource->ref_count) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Power resource [%s] already off",
resource->name));
- goto unlock;
+ return 0;
}
if (--resource->ref_count) {
@@ -335,10 +339,16 @@ static int acpi_power_off(struct acpi_power_resource *resource)
if (result)
resource->ref_count++;
}
+ return result;
+}
- unlock:
- mutex_unlock(&resource->resource_lock);
+static int acpi_power_off(struct acpi_power_resource *resource)
+{
+ int result;
+ mutex_lock(&resource->resource_lock);
+ result = acpi_power_off_unlocked(resource);
+ mutex_unlock(&resource->resource_lock);
return result;
}
@@ -521,18 +531,35 @@ void acpi_power_add_remove_device(struct acpi_device *adev, bool add)
}
}
-int acpi_power_min_system_level(struct list_head *list)
+int acpi_power_wakeup_list_init(struct list_head *list, int *system_level_p)
{
struct acpi_power_resource_entry *entry;
int system_level = 5;
list_for_each_entry(entry, list, node) {
struct acpi_power_resource *resource = entry->resource;
+ acpi_handle handle = resource->device.handle;
+ int result;
+ int state;
+ mutex_lock(&resource->resource_lock);
+
+ result = acpi_power_get_state(handle, &state);
+ if (result) {
+ mutex_unlock(&resource->resource_lock);
+ return result;
+ }
+ if (state == ACPI_POWER_RESOURCE_STATE_ON) {
+ resource->ref_count++;
+ resource->wakeup_enabled = true;
+ }
if (system_level > resource->system_level)
system_level = resource->system_level;
+
+ mutex_unlock(&resource->resource_lock);
}
- return system_level;
+ *system_level_p = system_level;
+ return 0;
}
/* --------------------------------------------------------------------------
@@ -610,6 +637,7 @@ int acpi_device_sleep_wake(struct acpi_device *dev,
*/
int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state)
{
+ struct acpi_power_resource_entry *entry;
int err = 0;
if (!dev || !dev->wakeup.flags.valid)
@@ -620,17 +648,31 @@ int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state)
if (dev->wakeup.prepare_count++)
goto out;
- err = acpi_power_on_list(&dev->wakeup.resources);
- if (err) {
- dev_err(&dev->dev, "Cannot turn wakeup power resources on\n");
- dev->wakeup.flags.valid = 0;
- } else {
- /*
- * Passing 3 as the third argument below means the device may be
- * put into arbitrary power state afterward.
- */
- err = acpi_device_sleep_wake(dev, 1, sleep_state, 3);
+ list_for_each_entry(entry, &dev->wakeup.resources, node) {
+ struct acpi_power_resource *resource = entry->resource;
+
+ mutex_lock(&resource->resource_lock);
+
+ if (!resource->wakeup_enabled) {
+ err = acpi_power_on_unlocked(resource);
+ if (!err)
+ resource->wakeup_enabled = true;
+ }
+
+ mutex_unlock(&resource->resource_lock);
+
+ if (err) {
+ dev_err(&dev->dev,
+ "Cannot turn wakeup power resources on\n");
+ dev->wakeup.flags.valid = 0;
+ goto out;
+ }
}
+ /*
+ * Passing 3 as the third argument below means the device may be
+ * put into arbitrary power state afterward.
+ */
+ err = acpi_device_sleep_wake(dev, 1, sleep_state, 3);
if (err)
dev->wakeup.prepare_count = 0;
@@ -647,6 +689,7 @@ int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state)
*/
int acpi_disable_wakeup_device_power(struct acpi_device *dev)
{
+ struct acpi_power_resource_entry *entry;
int err = 0;
if (!dev || !dev->wakeup.flags.valid)
@@ -668,10 +711,25 @@ int acpi_disable_wakeup_device_power(struct acpi_device *dev)
if (err)
goto out;
- err = acpi_power_off_list(&dev->wakeup.resources);
- if (err) {
- dev_err(&dev->dev, "Cannot turn wakeup power resources off\n");
- dev->wakeup.flags.valid = 0;
+ list_for_each_entry(entry, &dev->wakeup.resources, node) {
+ struct acpi_power_resource *resource = entry->resource;
+
+ mutex_lock(&resource->resource_lock);
+
+ if (resource->wakeup_enabled) {
+ err = acpi_power_off_unlocked(resource);
+ if (!err)
+ resource->wakeup_enabled = false;
+ }
+
+ mutex_unlock(&resource->resource_lock);
+
+ if (err) {
+ dev_err(&dev->dev,
+ "Cannot turn wakeup power resources off\n");
+ dev->wakeup.flags.valid = 0;
+ break;
+ }
}
out:
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 4be408756adc..5e7e991717d7 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -1002,7 +1002,14 @@ static int acpi_bus_extract_wakeup_device_power_package(acpi_handle handle,
if (!list_empty(&wakeup->resources)) {
int sleep_state;
- sleep_state = acpi_power_min_system_level(&wakeup->resources);
+ err = acpi_power_wakeup_list_init(&wakeup->resources,
+ &sleep_state);
+ if (err) {
+ acpi_handle_warn(handle, "Retrieving current states "
+ "of wakeup power resources failed\n");
+ acpi_power_resources_list_free(&wakeup->resources);
+ goto out;
+ }
if (sleep_state < wakeup->sleep_state) {
acpi_handle_warn(handle, "Overriding _PRW sleep state "
"(S%d) by S%d from power resources\n",