diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2014-06-03 17:11:42 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2014-06-03 17:11:42 -0400 |
commit | a392f7d4afb850934851fde5a8e298912650a6b8 (patch) | |
tree | ed9901ba019615f6911c0f9f1a3af180b243bc8f | |
parent | f58c41cc0427115e3d750ec090020892acf0fb9c (diff) | |
parent | 4cf563c5d97c83d4b2fb3a778dd7d5e362cc3e34 (diff) |
Merge branch 'acpi-pm'
* acpi-pm:
ACPI / PM: Export rest of the subsys PM callbacks
ACPI / PM: Avoid resuming devices in ACPI PM domain during system suspend
ACPI / PM: Hold ACPI scan lock over the "freeze" sleep state
ACPI / PM: Export acpi_target_system_state() to modules
-rw-r--r-- | drivers/acpi/device_pm.c | 46 | ||||
-rw-r--r-- | drivers/acpi/scan.c | 4 | ||||
-rw-r--r-- | drivers/acpi/sleep.c | 19 | ||||
-rw-r--r-- | include/acpi/acpi_bus.h | 3 | ||||
-rw-r--r-- | include/linux/acpi.h | 6 | ||||
-rw-r--r-- | include/linux/suspend.h | 7 | ||||
-rw-r--r-- | kernel/power/suspend.c | 15 |
7 files changed, 92 insertions, 8 deletions
diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index d047739f3380..49a51277f81d 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c | |||
@@ -900,18 +900,47 @@ EXPORT_SYMBOL_GPL(acpi_dev_resume_early); | |||
900 | */ | 900 | */ |
901 | int acpi_subsys_prepare(struct device *dev) | 901 | int acpi_subsys_prepare(struct device *dev) |
902 | { | 902 | { |
903 | /* | 903 | struct acpi_device *adev = ACPI_COMPANION(dev); |
904 | * Devices having power.ignore_children set may still be necessary for | 904 | u32 sys_target; |
905 | * suspending their children in the next phase of device suspend. | 905 | int ret, state; |
906 | */ | 906 | |
907 | if (dev->power.ignore_children) | 907 | ret = pm_generic_prepare(dev); |
908 | pm_runtime_resume(dev); | 908 | if (ret < 0) |
909 | return ret; | ||
910 | |||
911 | if (!adev || !pm_runtime_suspended(dev) | ||
912 | || device_may_wakeup(dev) != !!adev->wakeup.prepare_count) | ||
913 | return 0; | ||
914 | |||
915 | sys_target = acpi_target_system_state(); | ||
916 | if (sys_target == ACPI_STATE_S0) | ||
917 | return 1; | ||
909 | 918 | ||
910 | return pm_generic_prepare(dev); | 919 | if (adev->power.flags.dsw_present) |
920 | return 0; | ||
921 | |||
922 | ret = acpi_dev_pm_get_state(dev, adev, sys_target, NULL, &state); | ||
923 | return !ret && state == adev->power.state; | ||
911 | } | 924 | } |
912 | EXPORT_SYMBOL_GPL(acpi_subsys_prepare); | 925 | EXPORT_SYMBOL_GPL(acpi_subsys_prepare); |
913 | 926 | ||
914 | /** | 927 | /** |
928 | * acpi_subsys_complete - Finalize device's resume during system resume. | ||
929 | * @dev: Device to handle. | ||
930 | */ | ||
931 | void acpi_subsys_complete(struct device *dev) | ||
932 | { | ||
933 | /* | ||
934 | * If the device had been runtime-suspended before the system went into | ||
935 | * the sleep state it is going out of and it has never been resumed till | ||
936 | * now, resume it in case the firmware powered it up. | ||
937 | */ | ||
938 | if (dev->power.direct_complete) | ||
939 | pm_request_resume(dev); | ||
940 | } | ||
941 | EXPORT_SYMBOL_GPL(acpi_subsys_complete); | ||
942 | |||
943 | /** | ||
915 | * acpi_subsys_suspend - Run the device driver's suspend callback. | 944 | * acpi_subsys_suspend - Run the device driver's suspend callback. |
916 | * @dev: Device to handle. | 945 | * @dev: Device to handle. |
917 | * | 946 | * |
@@ -923,6 +952,7 @@ int acpi_subsys_suspend(struct device *dev) | |||
923 | pm_runtime_resume(dev); | 952 | pm_runtime_resume(dev); |
924 | return pm_generic_suspend(dev); | 953 | return pm_generic_suspend(dev); |
925 | } | 954 | } |
955 | EXPORT_SYMBOL_GPL(acpi_subsys_suspend); | ||
926 | 956 | ||
927 | /** | 957 | /** |
928 | * acpi_subsys_suspend_late - Suspend device using ACPI. | 958 | * acpi_subsys_suspend_late - Suspend device using ACPI. |
@@ -968,6 +998,7 @@ int acpi_subsys_freeze(struct device *dev) | |||
968 | pm_runtime_resume(dev); | 998 | pm_runtime_resume(dev); |
969 | return pm_generic_freeze(dev); | 999 | return pm_generic_freeze(dev); |
970 | } | 1000 | } |
1001 | EXPORT_SYMBOL_GPL(acpi_subsys_freeze); | ||
971 | 1002 | ||
972 | #endif /* CONFIG_PM_SLEEP */ | 1003 | #endif /* CONFIG_PM_SLEEP */ |
973 | 1004 | ||
@@ -979,6 +1010,7 @@ static struct dev_pm_domain acpi_general_pm_domain = { | |||
979 | #endif | 1010 | #endif |
980 | #ifdef CONFIG_PM_SLEEP | 1011 | #ifdef CONFIG_PM_SLEEP |
981 | .prepare = acpi_subsys_prepare, | 1012 | .prepare = acpi_subsys_prepare, |
1013 | .complete = acpi_subsys_complete, | ||
982 | .suspend = acpi_subsys_suspend, | 1014 | .suspend = acpi_subsys_suspend, |
983 | .suspend_late = acpi_subsys_suspend_late, | 1015 | .suspend_late = acpi_subsys_suspend_late, |
984 | .resume_early = acpi_subsys_resume_early, | 1016 | .resume_early = acpi_subsys_resume_early, |
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index db5fc6f9628e..31c99f7148d0 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c | |||
@@ -1551,9 +1551,13 @@ static void acpi_bus_get_power_flags(struct acpi_device *device) | |||
1551 | */ | 1551 | */ |
1552 | if (acpi_has_method(device->handle, "_PSC")) | 1552 | if (acpi_has_method(device->handle, "_PSC")) |
1553 | device->power.flags.explicit_get = 1; | 1553 | device->power.flags.explicit_get = 1; |
1554 | |||
1554 | if (acpi_has_method(device->handle, "_IRC")) | 1555 | if (acpi_has_method(device->handle, "_IRC")) |
1555 | device->power.flags.inrush_current = 1; | 1556 | device->power.flags.inrush_current = 1; |
1556 | 1557 | ||
1558 | if (acpi_has_method(device->handle, "_DSW")) | ||
1559 | device->power.flags.dsw_present = 1; | ||
1560 | |||
1557 | /* | 1561 | /* |
1558 | * Enumerate supported power management states | 1562 | * Enumerate supported power management states |
1559 | */ | 1563 | */ |
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index c40fb2e81bbc..c11e3795431b 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c | |||
@@ -89,6 +89,7 @@ u32 acpi_target_system_state(void) | |||
89 | { | 89 | { |
90 | return acpi_target_sleep_state; | 90 | return acpi_target_sleep_state; |
91 | } | 91 | } |
92 | EXPORT_SYMBOL_GPL(acpi_target_system_state); | ||
92 | 93 | ||
93 | static bool pwr_btn_event_pending; | 94 | static bool pwr_btn_event_pending; |
94 | 95 | ||
@@ -611,6 +612,22 @@ static const struct platform_suspend_ops acpi_suspend_ops_old = { | |||
611 | .recover = acpi_pm_finish, | 612 | .recover = acpi_pm_finish, |
612 | }; | 613 | }; |
613 | 614 | ||
615 | static int acpi_freeze_begin(void) | ||
616 | { | ||
617 | acpi_scan_lock_acquire(); | ||
618 | return 0; | ||
619 | } | ||
620 | |||
621 | static void acpi_freeze_end(void) | ||
622 | { | ||
623 | acpi_scan_lock_release(); | ||
624 | } | ||
625 | |||
626 | static const struct platform_freeze_ops acpi_freeze_ops = { | ||
627 | .begin = acpi_freeze_begin, | ||
628 | .end = acpi_freeze_end, | ||
629 | }; | ||
630 | |||
614 | static void acpi_sleep_suspend_setup(void) | 631 | static void acpi_sleep_suspend_setup(void) |
615 | { | 632 | { |
616 | int i; | 633 | int i; |
@@ -621,7 +638,9 @@ static void acpi_sleep_suspend_setup(void) | |||
621 | 638 | ||
622 | suspend_set_ops(old_suspend_ordering ? | 639 | suspend_set_ops(old_suspend_ordering ? |
623 | &acpi_suspend_ops_old : &acpi_suspend_ops); | 640 | &acpi_suspend_ops_old : &acpi_suspend_ops); |
641 | freeze_set_ops(&acpi_freeze_ops); | ||
624 | } | 642 | } |
643 | |||
625 | #else /* !CONFIG_SUSPEND */ | 644 | #else /* !CONFIG_SUSPEND */ |
626 | static inline void acpi_sleep_suspend_setup(void) {} | 645 | static inline void acpi_sleep_suspend_setup(void) {} |
627 | #endif /* !CONFIG_SUSPEND */ | 646 | #endif /* !CONFIG_SUSPEND */ |
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index d2006ca31dba..fb6e00dcce74 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h | |||
@@ -261,7 +261,8 @@ struct acpi_device_power_flags { | |||
261 | u32 inrush_current:1; /* Serialize Dx->D0 */ | 261 | u32 inrush_current:1; /* Serialize Dx->D0 */ |
262 | u32 power_removed:1; /* Optimize Dx->D0 */ | 262 | u32 power_removed:1; /* Optimize Dx->D0 */ |
263 | u32 ignore_parent:1; /* Power is independent of parent power state */ | 263 | u32 ignore_parent:1; /* Power is independent of parent power state */ |
264 | u32 reserved:27; | 264 | u32 dsw_present:1; /* _DSW present? */ |
265 | u32 reserved:26; | ||
265 | }; | 266 | }; |
266 | 267 | ||
267 | struct acpi_device_power_state { | 268 | struct acpi_device_power_state { |
diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 0e2569031a6f..2ecdfcf98c0f 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h | |||
@@ -555,14 +555,20 @@ static inline int acpi_subsys_runtime_resume(struct device *dev) { return 0; } | |||
555 | int acpi_dev_suspend_late(struct device *dev); | 555 | int acpi_dev_suspend_late(struct device *dev); |
556 | int acpi_dev_resume_early(struct device *dev); | 556 | int acpi_dev_resume_early(struct device *dev); |
557 | int acpi_subsys_prepare(struct device *dev); | 557 | int acpi_subsys_prepare(struct device *dev); |
558 | void acpi_subsys_complete(struct device *dev); | ||
558 | int acpi_subsys_suspend_late(struct device *dev); | 559 | int acpi_subsys_suspend_late(struct device *dev); |
559 | int acpi_subsys_resume_early(struct device *dev); | 560 | int acpi_subsys_resume_early(struct device *dev); |
561 | int acpi_subsys_suspend(struct device *dev); | ||
562 | int acpi_subsys_freeze(struct device *dev); | ||
560 | #else | 563 | #else |
561 | static inline int acpi_dev_suspend_late(struct device *dev) { return 0; } | 564 | static inline int acpi_dev_suspend_late(struct device *dev) { return 0; } |
562 | static inline int acpi_dev_resume_early(struct device *dev) { return 0; } | 565 | static inline int acpi_dev_resume_early(struct device *dev) { return 0; } |
563 | static inline int acpi_subsys_prepare(struct device *dev) { return 0; } | 566 | static inline int acpi_subsys_prepare(struct device *dev) { return 0; } |
567 | static inline void acpi_subsys_complete(struct device *dev) {} | ||
564 | static inline int acpi_subsys_suspend_late(struct device *dev) { return 0; } | 568 | static inline int acpi_subsys_suspend_late(struct device *dev) { return 0; } |
565 | static inline int acpi_subsys_resume_early(struct device *dev) { return 0; } | 569 | static inline int acpi_subsys_resume_early(struct device *dev) { return 0; } |
570 | static inline int acpi_subsys_suspend(struct device *dev) { return 0; } | ||
571 | static inline int acpi_subsys_freeze(struct device *dev) { return 0; } | ||
566 | #endif | 572 | #endif |
567 | 573 | ||
568 | #if defined(CONFIG_ACPI) && defined(CONFIG_PM) | 574 | #if defined(CONFIG_ACPI) && defined(CONFIG_PM) |
diff --git a/include/linux/suspend.h b/include/linux/suspend.h index f73cabf59012..91d66fd8dce1 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h | |||
@@ -187,6 +187,11 @@ struct platform_suspend_ops { | |||
187 | void (*recover)(void); | 187 | void (*recover)(void); |
188 | }; | 188 | }; |
189 | 189 | ||
190 | struct platform_freeze_ops { | ||
191 | int (*begin)(void); | ||
192 | void (*end)(void); | ||
193 | }; | ||
194 | |||
190 | #ifdef CONFIG_SUSPEND | 195 | #ifdef CONFIG_SUSPEND |
191 | /** | 196 | /** |
192 | * suspend_set_ops - set platform dependent suspend operations | 197 | * suspend_set_ops - set platform dependent suspend operations |
@@ -194,6 +199,7 @@ struct platform_suspend_ops { | |||
194 | */ | 199 | */ |
195 | extern void suspend_set_ops(const struct platform_suspend_ops *ops); | 200 | extern void suspend_set_ops(const struct platform_suspend_ops *ops); |
196 | extern int suspend_valid_only_mem(suspend_state_t state); | 201 | extern int suspend_valid_only_mem(suspend_state_t state); |
202 | extern void freeze_set_ops(const struct platform_freeze_ops *ops); | ||
197 | extern void freeze_wake(void); | 203 | extern void freeze_wake(void); |
198 | 204 | ||
199 | /** | 205 | /** |
@@ -220,6 +226,7 @@ extern int pm_suspend(suspend_state_t state); | |||
220 | 226 | ||
221 | static inline void suspend_set_ops(const struct platform_suspend_ops *ops) {} | 227 | static inline void suspend_set_ops(const struct platform_suspend_ops *ops) {} |
222 | static inline int pm_suspend(suspend_state_t state) { return -ENOSYS; } | 228 | static inline int pm_suspend(suspend_state_t state) { return -ENOSYS; } |
229 | static inline void freeze_set_ops(const struct platform_freeze_ops *ops) {} | ||
223 | static inline void freeze_wake(void) {} | 230 | static inline void freeze_wake(void) {} |
224 | #endif /* !CONFIG_SUSPEND */ | 231 | #endif /* !CONFIG_SUSPEND */ |
225 | 232 | ||
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index 338a6f147974..963e6d0f050b 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c | |||
@@ -38,6 +38,7 @@ struct pm_sleep_state pm_states[PM_SUSPEND_MAX] = { | |||
38 | }; | 38 | }; |
39 | 39 | ||
40 | static const struct platform_suspend_ops *suspend_ops; | 40 | static const struct platform_suspend_ops *suspend_ops; |
41 | static const struct platform_freeze_ops *freeze_ops; | ||
41 | 42 | ||
42 | static bool need_suspend_ops(suspend_state_t state) | 43 | static bool need_suspend_ops(suspend_state_t state) |
43 | { | 44 | { |
@@ -47,6 +48,13 @@ static bool need_suspend_ops(suspend_state_t state) | |||
47 | static DECLARE_WAIT_QUEUE_HEAD(suspend_freeze_wait_head); | 48 | static DECLARE_WAIT_QUEUE_HEAD(suspend_freeze_wait_head); |
48 | static bool suspend_freeze_wake; | 49 | static bool suspend_freeze_wake; |
49 | 50 | ||
51 | void freeze_set_ops(const struct platform_freeze_ops *ops) | ||
52 | { | ||
53 | lock_system_sleep(); | ||
54 | freeze_ops = ops; | ||
55 | unlock_system_sleep(); | ||
56 | } | ||
57 | |||
50 | static void freeze_begin(void) | 58 | static void freeze_begin(void) |
51 | { | 59 | { |
52 | suspend_freeze_wake = false; | 60 | suspend_freeze_wake = false; |
@@ -291,6 +299,10 @@ int suspend_devices_and_enter(suspend_state_t state) | |||
291 | error = suspend_ops->begin(state); | 299 | error = suspend_ops->begin(state); |
292 | if (error) | 300 | if (error) |
293 | goto Close; | 301 | goto Close; |
302 | } else if (state == PM_SUSPEND_FREEZE && freeze_ops->begin) { | ||
303 | error = freeze_ops->begin(); | ||
304 | if (error) | ||
305 | goto Close; | ||
294 | } | 306 | } |
295 | suspend_console(); | 307 | suspend_console(); |
296 | suspend_test_start(); | 308 | suspend_test_start(); |
@@ -316,6 +328,9 @@ int suspend_devices_and_enter(suspend_state_t state) | |||
316 | Close: | 328 | Close: |
317 | if (need_suspend_ops(state) && suspend_ops->end) | 329 | if (need_suspend_ops(state) && suspend_ops->end) |
318 | suspend_ops->end(); | 330 | suspend_ops->end(); |
331 | else if (state == PM_SUSPEND_FREEZE && freeze_ops->end) | ||
332 | freeze_ops->end(); | ||
333 | |||
319 | trace_machine_suspend(PWR_EVENT_EXIT); | 334 | trace_machine_suspend(PWR_EVENT_EXIT); |
320 | return error; | 335 | return error; |
321 | 336 | ||