aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/power.c
diff options
context:
space:
mode:
authorLin Ming <ming.m.lin@intel.com>2012-03-29 02:09:39 -0400
committerLen Brown <len.brown@intel.com>2012-03-30 01:47:20 -0400
commit0090def6c37c8ea29508a435e581f2ef26fea10f (patch)
treec8790571fb6cfe5ce8890706b5b44aed8005e900 /drivers/acpi/power.c
parent3ebc81b8937d2bc1d0d0064bed29434dfce490aa (diff)
ACPI: Add interface to register/unregister device to/from power resources
Devices may share same list of power resources in _PR0, for example Device(Dev0) { Name (_PR0, Package (0x01) { P0PR, P1PR }) } Device(Dev1) { Name (_PR0, Package (0x01) { P0PR, P1PR } } Assume Dev0 and Dev1 were runtime suspended. Then Dev0 is resumed first and it goes into D0 state. But Dev1 is left in D0_Uninitialised state. This is wrong. In this case, Dev1 must be resumed too. In order to hand this case, each power resource maintains a list of devices which relies on it. When power resource is ON, it will check if the devices on its list can be resumed. The device can only be resumed when all the power resouces of its _PR0 are ON. Signed-off-by: Lin Ming <ming.m.lin@intel.com> Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/acpi/power.c')
-rw-r--r--drivers/acpi/power.c162
1 files changed, 162 insertions, 0 deletions
diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c
index 0d681fb7428..7049a7d27c4 100644
--- a/drivers/acpi/power.c
+++ b/drivers/acpi/power.c
@@ -40,9 +40,11 @@
40#include <linux/init.h> 40#include <linux/init.h>
41#include <linux/types.h> 41#include <linux/types.h>
42#include <linux/slab.h> 42#include <linux/slab.h>
43#include <linux/pm_runtime.h>
43#include <acpi/acpi_bus.h> 44#include <acpi/acpi_bus.h>
44#include <acpi/acpi_drivers.h> 45#include <acpi/acpi_drivers.h>
45#include "sleep.h" 46#include "sleep.h"
47#include "internal.h"
46 48
47#define PREFIX "ACPI: " 49#define PREFIX "ACPI: "
48 50
@@ -77,6 +79,20 @@ static struct acpi_driver acpi_power_driver = {
77 }, 79 },
78}; 80};
79 81
82/*
83 * A power managed device
84 * A device may rely on multiple power resources.
85 * */
86struct acpi_power_managed_device {
87 struct device *dev; /* The physical device */
88 acpi_handle *handle;
89};
90
91struct acpi_power_resource_device {
92 struct acpi_power_managed_device *device;
93 struct acpi_power_resource_device *next;
94};
95
80struct acpi_power_resource { 96struct acpi_power_resource {
81 struct acpi_device * device; 97 struct acpi_device * device;
82 acpi_bus_id name; 98 acpi_bus_id name;
@@ -84,6 +100,9 @@ struct acpi_power_resource {
84 u32 order; 100 u32 order;
85 unsigned int ref_count; 101 unsigned int ref_count;
86 struct mutex resource_lock; 102 struct mutex resource_lock;
103
104 /* List of devices relying on this power resource */
105 struct acpi_power_resource_device *devices;
87}; 106};
88 107
89static struct list_head acpi_power_resource_list; 108static struct list_head acpi_power_resource_list;
@@ -183,8 +202,26 @@ static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state)
183 return 0; 202 return 0;
184} 203}
185 204
205/* Resume the device when all power resources in _PR0 are on */
206static void acpi_power_on_device(struct acpi_power_managed_device *device)
207{
208 struct acpi_device *acpi_dev;
209 acpi_handle handle = device->handle;
210 int state;
211
212 if (acpi_bus_get_device(handle, &acpi_dev))
213 return;
214
215 if(acpi_power_get_inferred_state(acpi_dev, &state))
216 return;
217
218 if (state == ACPI_STATE_D0 && pm_runtime_suspended(device->dev))
219 pm_request_resume(device->dev);
220}
221
186static int __acpi_power_on(struct acpi_power_resource *resource) 222static int __acpi_power_on(struct acpi_power_resource *resource)
187{ 223{
224 struct acpi_power_resource_device *device_list = resource->devices;
188 acpi_status status = AE_OK; 225 acpi_status status = AE_OK;
189 226
190 status = acpi_evaluate_object(resource->device->handle, "_ON", NULL, NULL); 227 status = acpi_evaluate_object(resource->device->handle, "_ON", NULL, NULL);
@@ -197,6 +234,12 @@ static int __acpi_power_on(struct acpi_power_resource *resource)
197 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Power resource [%s] turned on\n", 234 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Power resource [%s] turned on\n",
198 resource->name)); 235 resource->name));
199 236
237 while (device_list) {
238 acpi_power_on_device(device_list->device);
239
240 device_list = device_list->next;
241 }
242
200 return 0; 243 return 0;
201} 244}
202 245
@@ -299,6 +342,125 @@ static int acpi_power_on_list(struct acpi_handle_list *list)
299 return result; 342 return result;
300} 343}
301 344
345static void __acpi_power_resource_unregister_device(struct device *dev,
346 acpi_handle res_handle)
347{
348 struct acpi_power_resource *resource = NULL;
349 struct acpi_power_resource_device *prev, *curr;
350
351 if (acpi_power_get_context(res_handle, &resource))
352 return;
353
354 mutex_lock(&resource->resource_lock);
355 prev = NULL;
356 curr = resource->devices;
357 while (curr) {
358 if (curr->device->dev == dev) {
359 if (!prev)
360 resource->devices = curr->next;
361 else
362 prev->next = curr->next;
363
364 kfree(curr);
365 break;
366 }
367
368 prev = curr;
369 curr = curr->next;
370 }
371 mutex_unlock(&resource->resource_lock);
372}
373
374/* Unlink dev from all power resources in _PR0 */
375void acpi_power_resource_unregister_device(struct device *dev, acpi_handle handle)
376{
377 struct acpi_device *acpi_dev;
378 struct acpi_handle_list *list;
379 int i;
380
381 if (!dev || !handle)
382 return;
383
384 if (acpi_bus_get_device(handle, &acpi_dev))
385 return;
386
387 list = &acpi_dev->power.states[ACPI_STATE_D0].resources;
388
389 for (i = 0; i < list->count; i++)
390 __acpi_power_resource_unregister_device(dev,
391 list->handles[i]);
392}
393
394static int __acpi_power_resource_register_device(
395 struct acpi_power_managed_device *powered_device, acpi_handle handle)
396{
397 struct acpi_power_resource *resource = NULL;
398 struct acpi_power_resource_device *power_resource_device;
399 int result;
400
401 result = acpi_power_get_context(handle, &resource);
402 if (result)
403 return result;
404
405 power_resource_device = kzalloc(
406 sizeof(*power_resource_device), GFP_KERNEL);
407 if (!power_resource_device)
408 return -ENOMEM;
409
410 power_resource_device->device = powered_device;
411
412 mutex_lock(&resource->resource_lock);
413 power_resource_device->next = resource->devices;
414 resource->devices = power_resource_device;
415 mutex_unlock(&resource->resource_lock);
416
417 return 0;
418}
419
420/* Link dev to all power resources in _PR0 */
421int acpi_power_resource_register_device(struct device *dev, acpi_handle handle)
422{
423 struct acpi_device *acpi_dev;
424 struct acpi_handle_list *list;
425 struct acpi_power_managed_device *powered_device;
426 int i, ret;
427
428 if (!dev || !handle)
429 return -ENODEV;
430
431 ret = acpi_bus_get_device(handle, &acpi_dev);
432 if (ret)
433 goto no_power_resource;
434
435 if (!acpi_dev->power.flags.power_resources)
436 goto no_power_resource;
437
438 powered_device = kzalloc(sizeof(*powered_device), GFP_KERNEL);
439 if (!powered_device)
440 return -ENOMEM;
441
442 powered_device->dev = dev;
443 powered_device->handle = handle;
444
445 list = &acpi_dev->power.states[ACPI_STATE_D0].resources;
446
447 for (i = 0; i < list->count; i++) {
448 ret = __acpi_power_resource_register_device(powered_device,
449 list->handles[i]);
450
451 if (ret) {
452 acpi_power_resource_unregister_device(dev, handle);
453 break;
454 }
455 }
456
457 return ret;
458
459no_power_resource:
460 printk(KERN_WARNING PREFIX "Invalid Power Resource to register!");
461 return -ENODEV;
462}
463
302/** 464/**
303 * acpi_device_sleep_wake - execute _DSW (Device Sleep Wake) or (deprecated in 465 * acpi_device_sleep_wake - execute _DSW (Device Sleep Wake) or (deprecated in
304 * ACPI 3.0) _PSW (Power State Wake) 466 * ACPI 3.0) _PSW (Power State Wake)