summaryrefslogtreecommitdiff
path: root/drivers/acpi/power.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/acpi/power.c')
-rw-r--r--drivers/acpi/power.c105
1 files changed, 66 insertions, 39 deletions
diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c
index 32974b575e46..eba7785047ca 100644
--- a/drivers/acpi/power.c
+++ b/drivers/acpi/power.c
@@ -52,6 +52,7 @@ struct acpi_power_resource {
u32 system_level;
u32 order;
unsigned int ref_count;
+ u8 state;
bool wakeup_enabled;
struct mutex resource_lock;
struct list_head dependents;
@@ -147,6 +148,7 @@ int acpi_extract_power_resources(union acpi_object *package, unsigned int start,
for (i = start; i < package->package.count; i++) {
union acpi_object *element = &package->package.elements[i];
+ struct acpi_device *rdev;
acpi_handle rhandle;
if (element->type != ACPI_TYPE_LOCAL_REFERENCE) {
@@ -163,10 +165,11 @@ int acpi_extract_power_resources(union acpi_object *package, unsigned int start,
if (acpi_power_resource_is_dup(package, start, i))
continue;
- err = acpi_add_power_resource(rhandle);
- if (err)
+ rdev = acpi_add_power_resource(rhandle);
+ if (!rdev) {
+ err = -ENODEV;
break;
-
+ }
err = acpi_power_resources_list_add(rhandle, list);
if (err)
break;
@@ -177,44 +180,54 @@ int acpi_extract_power_resources(union acpi_object *package, unsigned int start,
return err;
}
-static int acpi_power_get_state(acpi_handle handle, int *state)
+static int __get_state(acpi_handle handle, u8 *state)
{
acpi_status status = AE_OK;
unsigned long long sta = 0;
-
- if (!handle || !state)
- return -EINVAL;
+ u8 cur_state;
status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
if (ACPI_FAILURE(status))
return -ENODEV;
- *state = (sta & 0x01)?ACPI_POWER_RESOURCE_STATE_ON:
- ACPI_POWER_RESOURCE_STATE_OFF;
+ cur_state = sta & ACPI_POWER_RESOURCE_STATE_ON;
acpi_handle_debug(handle, "Power resource is %s\n",
- *state ? "on" : "off");
+ cur_state ? "on" : "off");
+
+ *state = cur_state;
+ return 0;
+}
+
+static int acpi_power_get_state(struct acpi_power_resource *resource, u8 *state)
+{
+ if (resource->state == ACPI_POWER_RESOURCE_STATE_UNKNOWN) {
+ int ret;
+ ret = __get_state(resource->device.handle, &resource->state);
+ if (ret)
+ return ret;
+ }
+
+ *state = resource->state;
return 0;
}
-static int acpi_power_get_list_state(struct list_head *list, int *state)
+static int acpi_power_get_list_state(struct list_head *list, u8 *state)
{
struct acpi_power_resource_entry *entry;
- int cur_state;
+ u8 cur_state = ACPI_POWER_RESOURCE_STATE_OFF;
if (!list || !state)
return -EINVAL;
/* The state of the list is 'on' IFF all resources are 'on'. */
- cur_state = 0;
list_for_each_entry(entry, list, node) {
struct acpi_power_resource *resource = entry->resource;
- acpi_handle handle = resource->device.handle;
int result;
mutex_lock(&resource->resource_lock);
- result = acpi_power_get_state(handle, &cur_state);
+ result = acpi_power_get_state(resource, &cur_state);
mutex_unlock(&resource->resource_lock);
if (result)
return result;
@@ -347,8 +360,12 @@ static int __acpi_power_on(struct acpi_power_resource *resource)
acpi_status status = AE_OK;
status = acpi_evaluate_object(resource->device.handle, "_ON", NULL, NULL);
- if (ACPI_FAILURE(status))
+ if (ACPI_FAILURE(status)) {
+ resource->state = ACPI_POWER_RESOURCE_STATE_UNKNOWN;
return -ENODEV;
+ }
+
+ resource->state = ACPI_POWER_RESOURCE_STATE_ON;
pr_debug("Power resource [%s] turned on\n", resource->name);
@@ -400,8 +417,12 @@ static int __acpi_power_off(struct acpi_power_resource *resource)
status = acpi_evaluate_object(resource->device.handle, "_OFF",
NULL, NULL);
- if (ACPI_FAILURE(status))
+ if (ACPI_FAILURE(status)) {
+ resource->state = ACPI_POWER_RESOURCE_STATE_UNKNOWN;
return -ENODEV;
+ }
+
+ resource->state = ACPI_POWER_RESOURCE_STATE_OFF;
pr_debug("Power resource [%s] turned off\n", resource->name);
@@ -585,13 +606,12 @@ int acpi_power_wakeup_list_init(struct list_head *list, int *system_level_p)
list_for_each_entry(entry, list, node) {
struct acpi_power_resource *resource = entry->resource;
- acpi_handle handle = resource->device.handle;
int result;
- int state;
+ u8 state;
mutex_lock(&resource->resource_lock);
- result = acpi_power_get_state(handle, &state);
+ result = acpi_power_get_state(resource, &state);
if (result) {
mutex_unlock(&resource->resource_lock);
return result;
@@ -784,8 +804,8 @@ int acpi_disable_wakeup_device_power(struct acpi_device *dev)
int acpi_power_get_inferred_state(struct acpi_device *device, int *state)
{
+ u8 list_state = ACPI_POWER_RESOURCE_STATE_OFF;
int result = 0;
- int list_state = 0;
int i = 0;
if (!device || !state)
@@ -907,22 +927,22 @@ static void acpi_power_add_resource_to_list(struct acpi_power_resource *resource
mutex_unlock(&power_resource_list_lock);
}
-int acpi_add_power_resource(acpi_handle handle)
+struct acpi_device *acpi_add_power_resource(acpi_handle handle)
{
struct acpi_power_resource *resource;
struct acpi_device *device = NULL;
union acpi_object acpi_object;
struct acpi_buffer buffer = { sizeof(acpi_object), &acpi_object };
acpi_status status;
- int state, result = -ENODEV;
+ int result;
acpi_bus_get_device(handle, &device);
if (device)
- return 0;
+ return device;
resource = kzalloc(sizeof(*resource), GFP_KERNEL);
if (!resource)
- return -ENOMEM;
+ return NULL;
device = &resource->device;
acpi_init_device_object(device, handle, ACPI_BUS_TYPE_POWER);
@@ -941,13 +961,9 @@ int acpi_add_power_resource(acpi_handle handle)
resource->system_level = acpi_object.power_resource.system_level;
resource->order = acpi_object.power_resource.resource_order;
+ resource->state = ACPI_POWER_RESOURCE_STATE_UNKNOWN;
- result = acpi_power_get_state(handle, &state);
- if (result)
- goto err;
-
- pr_info("%s [%s] (%s)\n", acpi_device_name(device),
- acpi_device_bid(device), state ? "on" : "off");
+ pr_info("%s [%s]\n", acpi_device_name(device), acpi_device_bid(device));
device->flags.match_driver = true;
result = acpi_device_add(device, acpi_release_power_resource);
@@ -959,11 +975,11 @@ int acpi_add_power_resource(acpi_handle handle)
acpi_power_add_resource_to_list(resource);
acpi_device_add_finalize(device);
- return 0;
+ return device;
err:
acpi_release_power_resource(&device->dev);
- return result;
+ return NULL;
}
#ifdef CONFIG_ACPI_SLEEP
@@ -974,11 +990,13 @@ void acpi_resume_power_resources(void)
mutex_lock(&power_resource_list_lock);
list_for_each_entry(resource, &acpi_power_resource_list, list_node) {
- int result, state;
+ int result;
+ u8 state;
mutex_lock(&resource->resource_lock);
- result = acpi_power_get_state(resource->device.handle, &state);
+ resource->state = ACPI_POWER_RESOURCE_STATE_UNKNOWN;
+ result = acpi_power_get_state(resource, &state);
if (result) {
mutex_unlock(&resource->resource_lock);
continue;
@@ -986,7 +1004,7 @@ void acpi_resume_power_resources(void)
if (state == ACPI_POWER_RESOURCE_STATE_OFF
&& resource->ref_count) {
- dev_info(&resource->device.dev, "Turning ON\n");
+ dev_dbg(&resource->device.dev, "Turning ON\n");
__acpi_power_on(resource);
}
@@ -995,7 +1013,11 @@ void acpi_resume_power_resources(void)
mutex_unlock(&power_resource_list_lock);
}
+#endif
+/**
+ * acpi_turn_off_unused_power_resources - Turn off power resources not in use.
+ */
void acpi_turn_off_unused_power_resources(void)
{
struct acpi_power_resource *resource;
@@ -1005,8 +1027,14 @@ void acpi_turn_off_unused_power_resources(void)
list_for_each_entry_reverse(resource, &acpi_power_resource_list, list_node) {
mutex_lock(&resource->resource_lock);
- if (!resource->ref_count) {
- dev_info(&resource->device.dev, "Turning OFF\n");
+ /*
+ * Turn off power resources in an unknown state too, because the
+ * platform firmware on some system expects the OS to turn off
+ * power resources without any users unconditionally.
+ */
+ if (!resource->ref_count &&
+ resource->state != ACPI_POWER_RESOURCE_STATE_OFF) {
+ dev_dbg(&resource->device.dev, "Turning OFF\n");
__acpi_power_off(resource);
}
@@ -1015,4 +1043,3 @@ void acpi_turn_off_unused_power_resources(void)
mutex_unlock(&power_resource_list_lock);
}
-#endif