aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/power.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/acpi/power.c')
-rw-r--r--drivers/acpi/power.c166
1 files changed, 164 insertions, 2 deletions
diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c
index 9ac2a9fa90ff..7049a7d27c4f 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)
@@ -500,14 +662,14 @@ int acpi_power_transition(struct acpi_device *device, int state)
500{ 662{
501 int result; 663 int result;
502 664
503 if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3)) 665 if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD))
504 return -EINVAL; 666 return -EINVAL;
505 667
506 if (device->power.state == state) 668 if (device->power.state == state)
507 return 0; 669 return 0;
508 670
509 if ((device->power.state < ACPI_STATE_D0) 671 if ((device->power.state < ACPI_STATE_D0)
510 || (device->power.state > ACPI_STATE_D3)) 672 || (device->power.state > ACPI_STATE_D3_COLD))
511 return -ENODEV; 673 return -ENODEV;
512 674
513 /* TBD: Resources must be ordered. */ 675 /* TBD: Resources must be ordered. */