diff options
| -rw-r--r-- | drivers/acpi/power.c | 166 | ||||
| -rw-r--r-- | drivers/acpi/scan.c | 7 | ||||
| -rw-r--r-- | drivers/acpi/sleep.c | 35 | ||||
| -rw-r--r-- | drivers/pci/pci-acpi.c | 40 | ||||
| -rw-r--r-- | include/acpi/acpi_bus.h | 7 |
5 files changed, 216 insertions, 39 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. */ |
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 5d24a17aa854..767e2dcb9616 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c | |||
| @@ -884,6 +884,13 @@ static int acpi_bus_get_power_flags(struct acpi_device *device) | |||
| 884 | acpi_bus_add_power_resource(ps->resources.handles[j]); | 884 | acpi_bus_add_power_resource(ps->resources.handles[j]); |
| 885 | } | 885 | } |
| 886 | 886 | ||
| 887 | /* The exist of _PR3 indicates D3Cold support */ | ||
| 888 | if (i == ACPI_STATE_D3) { | ||
| 889 | status = acpi_get_handle(device->handle, object_name, &handle); | ||
| 890 | if (ACPI_SUCCESS(status)) | ||
| 891 | device->power.states[ACPI_STATE_D3_COLD].flags.valid = 1; | ||
| 892 | } | ||
| 893 | |||
| 887 | /* Evaluate "_PSx" to see if we can do explicit sets */ | 894 | /* Evaluate "_PSx" to see if we can do explicit sets */ |
| 888 | object_name[2] = 'S'; | 895 | object_name[2] = 'S'; |
| 889 | status = acpi_get_handle(device->handle, object_name, &handle); | 896 | status = acpi_get_handle(device->handle, object_name, &handle); |
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index acf81fe86b53..1d661b5c3287 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c | |||
| @@ -18,6 +18,7 @@ | |||
| 18 | #include <linux/reboot.h> | 18 | #include <linux/reboot.h> |
| 19 | #include <linux/acpi.h> | 19 | #include <linux/acpi.h> |
| 20 | #include <linux/module.h> | 20 | #include <linux/module.h> |
| 21 | #include <linux/pm_runtime.h> | ||
| 21 | 22 | ||
| 22 | #include <asm/io.h> | 23 | #include <asm/io.h> |
| 23 | 24 | ||
| @@ -753,6 +754,40 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p) | |||
| 753 | 754 | ||
| 754 | #ifdef CONFIG_PM_SLEEP | 755 | #ifdef CONFIG_PM_SLEEP |
| 755 | /** | 756 | /** |
| 757 | * acpi_pm_device_run_wake - Enable/disable wake-up for given device. | ||
| 758 | * @phys_dev: Device to enable/disable the platform to wake-up the system for. | ||
| 759 | * @enable: Whether enable or disable the wake-up functionality. | ||
| 760 | * | ||
| 761 | * Find the ACPI device object corresponding to @pci_dev and try to | ||
| 762 | * enable/disable the GPE associated with it. | ||
| 763 | */ | ||
| 764 | int acpi_pm_device_run_wake(struct device *phys_dev, bool enable) | ||
| 765 | { | ||
| 766 | struct acpi_device *dev; | ||
| 767 | acpi_handle handle; | ||
| 768 | |||
| 769 | if (!device_run_wake(phys_dev)) | ||
| 770 | return -EINVAL; | ||
| 771 | |||
| 772 | handle = DEVICE_ACPI_HANDLE(phys_dev); | ||
| 773 | if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &dev))) { | ||
| 774 | dev_dbg(phys_dev, "ACPI handle has no context in %s!\n", | ||
| 775 | __func__); | ||
| 776 | return -ENODEV; | ||
| 777 | } | ||
| 778 | |||
| 779 | if (enable) { | ||
| 780 | acpi_enable_wakeup_device_power(dev, ACPI_STATE_S0); | ||
| 781 | acpi_enable_gpe(dev->wakeup.gpe_device, dev->wakeup.gpe_number); | ||
| 782 | } else { | ||
| 783 | acpi_disable_gpe(dev->wakeup.gpe_device, dev->wakeup.gpe_number); | ||
| 784 | acpi_disable_wakeup_device_power(dev); | ||
| 785 | } | ||
| 786 | |||
| 787 | return 0; | ||
| 788 | } | ||
| 789 | |||
| 790 | /** | ||
| 756 | * acpi_pm_device_sleep_wake - enable or disable the system wake-up | 791 | * acpi_pm_device_sleep_wake - enable or disable the system wake-up |
| 757 | * capability of given device | 792 | * capability of given device |
| 758 | * @dev: device to handle | 793 | * @dev: device to handle |
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 060fd22a1103..0f150f271c2a 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c | |||
| @@ -277,40 +277,6 @@ static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable) | |||
| 277 | return 0; | 277 | return 0; |
| 278 | } | 278 | } |
| 279 | 279 | ||
| 280 | /** | ||
| 281 | * acpi_dev_run_wake - Enable/disable wake-up for given device. | ||
| 282 | * @phys_dev: Device to enable/disable the platform to wake-up the system for. | ||
| 283 | * @enable: Whether enable or disable the wake-up functionality. | ||
| 284 | * | ||
| 285 | * Find the ACPI device object corresponding to @pci_dev and try to | ||
| 286 | * enable/disable the GPE associated with it. | ||
| 287 | */ | ||
| 288 | static int acpi_dev_run_wake(struct device *phys_dev, bool enable) | ||
| 289 | { | ||
| 290 | struct acpi_device *dev; | ||
| 291 | acpi_handle handle; | ||
| 292 | |||
| 293 | if (!device_run_wake(phys_dev)) | ||
| 294 | return -EINVAL; | ||
| 295 | |||
| 296 | handle = DEVICE_ACPI_HANDLE(phys_dev); | ||
| 297 | if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &dev))) { | ||
| 298 | dev_dbg(phys_dev, "ACPI handle has no context in %s!\n", | ||
| 299 | __func__); | ||
| 300 | return -ENODEV; | ||
| 301 | } | ||
| 302 | |||
| 303 | if (enable) { | ||
| 304 | acpi_enable_wakeup_device_power(dev, ACPI_STATE_S0); | ||
| 305 | acpi_enable_gpe(dev->wakeup.gpe_device, dev->wakeup.gpe_number); | ||
| 306 | } else { | ||
| 307 | acpi_disable_gpe(dev->wakeup.gpe_device, dev->wakeup.gpe_number); | ||
| 308 | acpi_disable_wakeup_device_power(dev); | ||
| 309 | } | ||
| 310 | |||
| 311 | return 0; | ||
| 312 | } | ||
| 313 | |||
| 314 | static void acpi_pci_propagate_run_wake(struct pci_bus *bus, bool enable) | 280 | static void acpi_pci_propagate_run_wake(struct pci_bus *bus, bool enable) |
| 315 | { | 281 | { |
| 316 | while (bus->parent) { | 282 | while (bus->parent) { |
| @@ -318,14 +284,14 @@ static void acpi_pci_propagate_run_wake(struct pci_bus *bus, bool enable) | |||
| 318 | 284 | ||
| 319 | if (bridge->pme_interrupt) | 285 | if (bridge->pme_interrupt) |
| 320 | return; | 286 | return; |
| 321 | if (!acpi_dev_run_wake(&bridge->dev, enable)) | 287 | if (!acpi_pm_device_run_wake(&bridge->dev, enable)) |
| 322 | return; | 288 | return; |
| 323 | bus = bus->parent; | 289 | bus = bus->parent; |
| 324 | } | 290 | } |
| 325 | 291 | ||
| 326 | /* We have reached the root bus. */ | 292 | /* We have reached the root bus. */ |
| 327 | if (bus->bridge) | 293 | if (bus->bridge) |
| 328 | acpi_dev_run_wake(bus->bridge, enable); | 294 | acpi_pm_device_run_wake(bus->bridge, enable); |
| 329 | } | 295 | } |
| 330 | 296 | ||
| 331 | static int acpi_pci_run_wake(struct pci_dev *dev, bool enable) | 297 | static int acpi_pci_run_wake(struct pci_dev *dev, bool enable) |
| @@ -333,7 +299,7 @@ static int acpi_pci_run_wake(struct pci_dev *dev, bool enable) | |||
| 333 | if (dev->pme_interrupt) | 299 | if (dev->pme_interrupt) |
| 334 | return 0; | 300 | return 0; |
| 335 | 301 | ||
| 336 | if (!acpi_dev_run_wake(&dev->dev, enable)) | 302 | if (!acpi_pm_device_run_wake(&dev->dev, enable)) |
| 337 | return 0; | 303 | return 0; |
| 338 | 304 | ||
| 339 | acpi_pci_propagate_run_wake(dev->bus, enable); | 305 | acpi_pci_propagate_run_wake(dev->bus, enable); |
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 6cd5b6403a7b..f1c8ca60e824 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h | |||
| @@ -323,6 +323,8 @@ int acpi_bus_set_power(acpi_handle handle, int state); | |||
| 323 | int acpi_bus_update_power(acpi_handle handle, int *state_p); | 323 | int acpi_bus_update_power(acpi_handle handle, int *state_p); |
| 324 | bool acpi_bus_power_manageable(acpi_handle handle); | 324 | bool acpi_bus_power_manageable(acpi_handle handle); |
| 325 | bool acpi_bus_can_wakeup(acpi_handle handle); | 325 | bool acpi_bus_can_wakeup(acpi_handle handle); |
| 326 | int acpi_power_resource_register_device(struct device *dev, acpi_handle handle); | ||
| 327 | void acpi_power_resource_unregister_device(struct device *dev, acpi_handle handle); | ||
| 326 | #ifdef CONFIG_ACPI_PROC_EVENT | 328 | #ifdef CONFIG_ACPI_PROC_EVENT |
| 327 | int acpi_bus_generate_proc_event(struct acpi_device *device, u8 type, int data); | 329 | int acpi_bus_generate_proc_event(struct acpi_device *device, u8 type, int data); |
| 328 | int acpi_bus_generate_proc_event4(const char *class, const char *bid, u8 type, int data); | 330 | int acpi_bus_generate_proc_event4(const char *class, const char *bid, u8 type, int data); |
| @@ -392,8 +394,13 @@ static inline int acpi_pm_device_sleep_state(struct device *d, int *p) | |||
| 392 | #endif | 394 | #endif |
| 393 | 395 | ||
| 394 | #ifdef CONFIG_PM_SLEEP | 396 | #ifdef CONFIG_PM_SLEEP |
| 397 | int acpi_pm_device_run_wake(struct device *, bool); | ||
| 395 | int acpi_pm_device_sleep_wake(struct device *, bool); | 398 | int acpi_pm_device_sleep_wake(struct device *, bool); |
| 396 | #else | 399 | #else |
| 400 | static inline int acpi_pm_device_run_wake(struct device *dev, bool enable) | ||
| 401 | { | ||
| 402 | return -ENODEV; | ||
| 403 | } | ||
| 397 | static inline int acpi_pm_device_sleep_wake(struct device *dev, bool enable) | 404 | static inline int acpi_pm_device_sleep_wake(struct device *dev, bool enable) |
| 398 | { | 405 | { |
| 399 | return -ENODEV; | 406 | return -ENODEV; |
