diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2014-07-22 19:00:45 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2014-07-22 19:00:45 -0400 |
commit | c072530f391e33bd22ed0638c08f07528f154493 (patch) | |
tree | 559f1730fb64797831086b2e41feded7d72e7226 /drivers/acpi/device_pm.c | |
parent | 28cb5ef16e578bbca0a562b09f12c8c98ca92720 (diff) |
ACPI / PM: Revork the handling of ACPI device wakeup notifications
Since ACPI wakeup GPEs are going to be enabled during system suspend
as well as for runtime wakeup by a subsequent patch and the same
notify handlers will be used in both cases, rework the ACPI device
wakeup notification framework so that the part specific to physical
devices is always run asynchronously from the PM workqueue. This
prevents runtime resume callbacks for those devices from being
run during system suspend and resume which may not be appropriate,
among other things.
Also make ACPI device wakeup notification handling a bit more robust
agaist subsequent removal of ACPI device objects, whould that ever
happen, and create a wakeup source object for each ACPI device
configured for wakeup so that wakeup notifications for those
devices can wake up the system from the "freeze" sleep state.
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/acpi/device_pm.c')
-rw-r--r-- | drivers/acpi/device_pm.c | 80 |
1 files changed, 58 insertions, 22 deletions
diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 49a51277f81d..366de0b0c39b 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c | |||
@@ -367,29 +367,61 @@ EXPORT_SYMBOL(acpi_bus_power_manageable); | |||
367 | #ifdef CONFIG_PM | 367 | #ifdef CONFIG_PM |
368 | static DEFINE_MUTEX(acpi_pm_notifier_lock); | 368 | static DEFINE_MUTEX(acpi_pm_notifier_lock); |
369 | 369 | ||
370 | static void acpi_pm_notify_handler(acpi_handle handle, u32 val, void *not_used) | ||
371 | { | ||
372 | struct acpi_device *adev; | ||
373 | |||
374 | if (val != ACPI_NOTIFY_DEVICE_WAKE) | ||
375 | return; | ||
376 | |||
377 | adev = acpi_bus_get_acpi_device(handle); | ||
378 | if (!adev) | ||
379 | return; | ||
380 | |||
381 | mutex_lock(&acpi_pm_notifier_lock); | ||
382 | |||
383 | if (adev->wakeup.flags.notifier_present) { | ||
384 | __pm_wakeup_event(adev->wakeup.ws, 0); | ||
385 | if (adev->wakeup.context.work.func) | ||
386 | queue_pm_work(&adev->wakeup.context.work); | ||
387 | } | ||
388 | |||
389 | mutex_unlock(&acpi_pm_notifier_lock); | ||
390 | |||
391 | acpi_bus_put_acpi_device(adev); | ||
392 | } | ||
393 | |||
370 | /** | 394 | /** |
371 | * acpi_add_pm_notifier - Register PM notifier for given ACPI device. | 395 | * acpi_add_pm_notifier - Register PM notify handler for given ACPI device. |
372 | * @adev: ACPI device to add the notifier for. | 396 | * @adev: ACPI device to add the notify handler for. |
373 | * @context: Context information to pass to the notifier routine. | 397 | * @dev: Device to generate a wakeup event for while handling the notification. |
398 | * @work_func: Work function to execute when handling the notification. | ||
374 | * | 399 | * |
375 | * NOTE: @adev need not be a run-wake or wakeup device to be a valid source of | 400 | * NOTE: @adev need not be a run-wake or wakeup device to be a valid source of |
376 | * PM wakeup events. For example, wakeup events may be generated for bridges | 401 | * PM wakeup events. For example, wakeup events may be generated for bridges |
377 | * if one of the devices below the bridge is signaling wakeup, even if the | 402 | * if one of the devices below the bridge is signaling wakeup, even if the |
378 | * bridge itself doesn't have a wakeup GPE associated with it. | 403 | * bridge itself doesn't have a wakeup GPE associated with it. |
379 | */ | 404 | */ |
380 | acpi_status acpi_add_pm_notifier(struct acpi_device *adev, | 405 | acpi_status acpi_add_pm_notifier(struct acpi_device *adev, struct device *dev, |
381 | acpi_notify_handler handler, void *context) | 406 | void (*work_func)(struct work_struct *work)) |
382 | { | 407 | { |
383 | acpi_status status = AE_ALREADY_EXISTS; | 408 | acpi_status status = AE_ALREADY_EXISTS; |
384 | 409 | ||
410 | if (!dev && !work_func) | ||
411 | return AE_BAD_PARAMETER; | ||
412 | |||
385 | mutex_lock(&acpi_pm_notifier_lock); | 413 | mutex_lock(&acpi_pm_notifier_lock); |
386 | 414 | ||
387 | if (adev->wakeup.flags.notifier_present) | 415 | if (adev->wakeup.flags.notifier_present) |
388 | goto out; | 416 | goto out; |
389 | 417 | ||
390 | status = acpi_install_notify_handler(adev->handle, | 418 | adev->wakeup.ws = wakeup_source_register(dev_name(&adev->dev)); |
391 | ACPI_SYSTEM_NOTIFY, | 419 | adev->wakeup.context.dev = dev; |
392 | handler, context); | 420 | if (work_func) |
421 | INIT_WORK(&adev->wakeup.context.work, work_func); | ||
422 | |||
423 | status = acpi_install_notify_handler(adev->handle, ACPI_SYSTEM_NOTIFY, | ||
424 | acpi_pm_notify_handler, NULL); | ||
393 | if (ACPI_FAILURE(status)) | 425 | if (ACPI_FAILURE(status)) |
394 | goto out; | 426 | goto out; |
395 | 427 | ||
@@ -404,8 +436,7 @@ acpi_status acpi_add_pm_notifier(struct acpi_device *adev, | |||
404 | * acpi_remove_pm_notifier - Unregister PM notifier from given ACPI device. | 436 | * acpi_remove_pm_notifier - Unregister PM notifier from given ACPI device. |
405 | * @adev: ACPI device to remove the notifier from. | 437 | * @adev: ACPI device to remove the notifier from. |
406 | */ | 438 | */ |
407 | acpi_status acpi_remove_pm_notifier(struct acpi_device *adev, | 439 | acpi_status acpi_remove_pm_notifier(struct acpi_device *adev) |
408 | acpi_notify_handler handler) | ||
409 | { | 440 | { |
410 | acpi_status status = AE_BAD_PARAMETER; | 441 | acpi_status status = AE_BAD_PARAMETER; |
411 | 442 | ||
@@ -416,10 +447,17 @@ acpi_status acpi_remove_pm_notifier(struct acpi_device *adev, | |||
416 | 447 | ||
417 | status = acpi_remove_notify_handler(adev->handle, | 448 | status = acpi_remove_notify_handler(adev->handle, |
418 | ACPI_SYSTEM_NOTIFY, | 449 | ACPI_SYSTEM_NOTIFY, |
419 | handler); | 450 | acpi_pm_notify_handler); |
420 | if (ACPI_FAILURE(status)) | 451 | if (ACPI_FAILURE(status)) |
421 | goto out; | 452 | goto out; |
422 | 453 | ||
454 | if (adev->wakeup.context.work.func) { | ||
455 | cancel_work_sync(&adev->wakeup.context.work); | ||
456 | adev->wakeup.context.work.func = NULL; | ||
457 | } | ||
458 | adev->wakeup.context.dev = NULL; | ||
459 | wakeup_source_unregister(adev->wakeup.ws); | ||
460 | |||
423 | adev->wakeup.flags.notifier_present = false; | 461 | adev->wakeup.flags.notifier_present = false; |
424 | 462 | ||
425 | out: | 463 | out: |
@@ -602,16 +640,15 @@ EXPORT_SYMBOL(acpi_pm_device_sleep_state); | |||
602 | 640 | ||
603 | #ifdef CONFIG_PM_RUNTIME | 641 | #ifdef CONFIG_PM_RUNTIME |
604 | /** | 642 | /** |
605 | * acpi_wakeup_device - Wakeup notification handler for ACPI devices. | 643 | * acpi_pm_notify_work_func - ACPI devices wakeup notification work function. |
606 | * @handle: ACPI handle of the device the notification is for. | 644 | * @work: Work item to handle. |
607 | * @event: Type of the signaled event. | ||
608 | * @context: Device corresponding to @handle. | ||
609 | */ | 645 | */ |
610 | static void acpi_wakeup_device(acpi_handle handle, u32 event, void *context) | 646 | static void acpi_pm_notify_work_func(struct work_struct *work) |
611 | { | 647 | { |
612 | struct device *dev = context; | 648 | struct device *dev; |
613 | 649 | ||
614 | if (event == ACPI_NOTIFY_DEVICE_WAKE && dev) { | 650 | dev = container_of(work, struct acpi_device_wakeup_context, work)->dev; |
651 | if (dev) { | ||
615 | pm_wakeup_event(dev, 0); | 652 | pm_wakeup_event(dev, 0); |
616 | pm_runtime_resume(dev); | 653 | pm_runtime_resume(dev); |
617 | } | 654 | } |
@@ -677,8 +714,7 @@ int acpi_pm_device_run_wake(struct device *phys_dev, bool enable) | |||
677 | } | 714 | } |
678 | EXPORT_SYMBOL(acpi_pm_device_run_wake); | 715 | EXPORT_SYMBOL(acpi_pm_device_run_wake); |
679 | #else | 716 | #else |
680 | static inline void acpi_wakeup_device(acpi_handle handle, u32 event, | 717 | static inline void acpi_pm_notify_work_func(struct work_struct *work) {} |
681 | void *context) {} | ||
682 | #endif /* CONFIG_PM_RUNTIME */ | 718 | #endif /* CONFIG_PM_RUNTIME */ |
683 | 719 | ||
684 | #ifdef CONFIG_PM_SLEEP | 720 | #ifdef CONFIG_PM_SLEEP |
@@ -1048,7 +1084,7 @@ int acpi_dev_pm_attach(struct device *dev, bool power_on) | |||
1048 | if (dev->pm_domain) | 1084 | if (dev->pm_domain) |
1049 | return -EEXIST; | 1085 | return -EEXIST; |
1050 | 1086 | ||
1051 | acpi_add_pm_notifier(adev, acpi_wakeup_device, dev); | 1087 | acpi_add_pm_notifier(adev, dev, acpi_pm_notify_work_func); |
1052 | dev->pm_domain = &acpi_general_pm_domain; | 1088 | dev->pm_domain = &acpi_general_pm_domain; |
1053 | if (power_on) { | 1089 | if (power_on) { |
1054 | acpi_dev_pm_full_power(adev); | 1090 | acpi_dev_pm_full_power(adev); |
@@ -1076,7 +1112,7 @@ void acpi_dev_pm_detach(struct device *dev, bool power_off) | |||
1076 | 1112 | ||
1077 | if (adev && dev->pm_domain == &acpi_general_pm_domain) { | 1113 | if (adev && dev->pm_domain == &acpi_general_pm_domain) { |
1078 | dev->pm_domain = NULL; | 1114 | dev->pm_domain = NULL; |
1079 | acpi_remove_pm_notifier(adev, acpi_wakeup_device); | 1115 | acpi_remove_pm_notifier(adev); |
1080 | if (power_off) { | 1116 | if (power_off) { |
1081 | /* | 1117 | /* |
1082 | * If the device's PM QoS resume latency limit or flags | 1118 | * If the device's PM QoS resume latency limit or flags |