diff options
Diffstat (limited to 'drivers/acpi/power.c')
-rw-r--r-- | drivers/acpi/power.c | 166 |
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 | * */ | ||
86 | struct acpi_power_managed_device { | ||
87 | struct device *dev; /* The physical device */ | ||
88 | acpi_handle *handle; | ||
89 | }; | ||
90 | |||
91 | struct acpi_power_resource_device { | ||
92 | struct acpi_power_managed_device *device; | ||
93 | struct acpi_power_resource_device *next; | ||
94 | }; | ||
95 | |||
80 | struct acpi_power_resource { | 96 | struct 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 | ||
89 | static struct list_head acpi_power_resource_list; | 108 | static 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 */ | ||
206 | static 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 | |||
186 | static int __acpi_power_on(struct acpi_power_resource *resource) | 222 | static 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 | ||
345 | static 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 */ | ||
375 | void 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 | |||
394 | static 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 */ | ||
421 | int 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 | |||
459 | no_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. */ |