|
@@ -30,12 +30,38 @@
|
|
|
#include "power.h"
|
|
|
|
|
|
const char *const pm_states[PM_SUSPEND_MAX] = {
|
|
|
+ [PM_SUSPEND_FREEZE] = "freeze",
|
|
|
[PM_SUSPEND_STANDBY] = "standby",
|
|
|
[PM_SUSPEND_MEM] = "mem",
|
|
|
};
|
|
|
|
|
|
static const struct platform_suspend_ops *suspend_ops;
|
|
|
|
|
|
+static bool need_suspend_ops(suspend_state_t state)
|
|
|
+{
|
|
|
+ return !!(state > PM_SUSPEND_FREEZE);
|
|
|
+}
|
|
|
+
|
|
|
+static DECLARE_WAIT_QUEUE_HEAD(suspend_freeze_wait_head);
|
|
|
+static bool suspend_freeze_wake;
|
|
|
+
|
|
|
+static void freeze_begin(void)
|
|
|
+{
|
|
|
+ suspend_freeze_wake = false;
|
|
|
+}
|
|
|
+
|
|
|
+static void freeze_enter(void)
|
|
|
+{
|
|
|
+ wait_event(suspend_freeze_wait_head, suspend_freeze_wake);
|
|
|
+}
|
|
|
+
|
|
|
+void freeze_wake(void)
|
|
|
+{
|
|
|
+ suspend_freeze_wake = true;
|
|
|
+ wake_up(&suspend_freeze_wait_head);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(freeze_wake);
|
|
|
+
|
|
|
/**
|
|
|
* suspend_set_ops - Set the global suspend method table.
|
|
|
* @ops: Suspend operations to use.
|
|
@@ -50,8 +76,11 @@ EXPORT_SYMBOL_GPL(suspend_set_ops);
|
|
|
|
|
|
bool valid_state(suspend_state_t state)
|
|
|
{
|
|
|
+ if (state == PM_SUSPEND_FREEZE)
|
|
|
+ return true;
|
|
|
/*
|
|
|
- * All states need lowlevel support and need to be valid to the lowlevel
|
|
|
+ * PM_SUSPEND_STANDBY and PM_SUSPEND_MEMORY states need lowlevel
|
|
|
+ * support and need to be valid to the lowlevel
|
|
|
* implementation, no valid callback implies that none are valid.
|
|
|
*/
|
|
|
return suspend_ops && suspend_ops->valid && suspend_ops->valid(state);
|
|
@@ -89,11 +118,11 @@ static int suspend_test(int level)
|
|
|
* hibernation). Run suspend notifiers, allocate the "suspend" console and
|
|
|
* freeze processes.
|
|
|
*/
|
|
|
-static int suspend_prepare(void)
|
|
|
+static int suspend_prepare(suspend_state_t state)
|
|
|
{
|
|
|
int error;
|
|
|
|
|
|
- if (!suspend_ops || !suspend_ops->enter)
|
|
|
+ if (need_suspend_ops(state) && (!suspend_ops || !suspend_ops->enter))
|
|
|
return -EPERM;
|
|
|
|
|
|
pm_prepare_console();
|
|
@@ -137,7 +166,7 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
|
|
|
{
|
|
|
int error;
|
|
|
|
|
|
- if (suspend_ops->prepare) {
|
|
|
+ if (need_suspend_ops(state) && suspend_ops->prepare) {
|
|
|
error = suspend_ops->prepare();
|
|
|
if (error)
|
|
|
goto Platform_finish;
|
|
@@ -149,12 +178,23 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
|
|
|
goto Platform_finish;
|
|
|
}
|
|
|
|
|
|
- if (suspend_ops->prepare_late) {
|
|
|
+ if (need_suspend_ops(state) && suspend_ops->prepare_late) {
|
|
|
error = suspend_ops->prepare_late();
|
|
|
if (error)
|
|
|
goto Platform_wake;
|
|
|
}
|
|
|
|
|
|
+ /*
|
|
|
+ * PM_SUSPEND_FREEZE equals
|
|
|
+ * frozen processes + suspended devices + idle processors.
|
|
|
+ * Thus we should invoke freeze_enter() soon after
|
|
|
+ * all the devices are suspended.
|
|
|
+ */
|
|
|
+ if (state == PM_SUSPEND_FREEZE) {
|
|
|
+ freeze_enter();
|
|
|
+ goto Platform_wake;
|
|
|
+ }
|
|
|
+
|
|
|
if (suspend_test(TEST_PLATFORM))
|
|
|
goto Platform_wake;
|
|
|
|
|
@@ -182,13 +222,13 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
|
|
|
enable_nonboot_cpus();
|
|
|
|
|
|
Platform_wake:
|
|
|
- if (suspend_ops->wake)
|
|
|
+ if (need_suspend_ops(state) && suspend_ops->wake)
|
|
|
suspend_ops->wake();
|
|
|
|
|
|
dpm_resume_start(PMSG_RESUME);
|
|
|
|
|
|
Platform_finish:
|
|
|
- if (suspend_ops->finish)
|
|
|
+ if (need_suspend_ops(state) && suspend_ops->finish)
|
|
|
suspend_ops->finish();
|
|
|
|
|
|
return error;
|
|
@@ -203,11 +243,11 @@ int suspend_devices_and_enter(suspend_state_t state)
|
|
|
int error;
|
|
|
bool wakeup = false;
|
|
|
|
|
|
- if (!suspend_ops)
|
|
|
+ if (need_suspend_ops(state) && !suspend_ops)
|
|
|
return -ENOSYS;
|
|
|
|
|
|
trace_machine_suspend(state);
|
|
|
- if (suspend_ops->begin) {
|
|
|
+ if (need_suspend_ops(state) && suspend_ops->begin) {
|
|
|
error = suspend_ops->begin(state);
|
|
|
if (error)
|
|
|
goto Close;
|
|
@@ -226,7 +266,7 @@ int suspend_devices_and_enter(suspend_state_t state)
|
|
|
|
|
|
do {
|
|
|
error = suspend_enter(state, &wakeup);
|
|
|
- } while (!error && !wakeup
|
|
|
+ } while (!error && !wakeup && need_suspend_ops(state)
|
|
|
&& suspend_ops->suspend_again && suspend_ops->suspend_again());
|
|
|
|
|
|
Resume_devices:
|
|
@@ -236,13 +276,13 @@ int suspend_devices_and_enter(suspend_state_t state)
|
|
|
ftrace_start();
|
|
|
resume_console();
|
|
|
Close:
|
|
|
- if (suspend_ops->end)
|
|
|
+ if (need_suspend_ops(state) && suspend_ops->end)
|
|
|
suspend_ops->end();
|
|
|
trace_machine_suspend(PWR_EVENT_EXIT);
|
|
|
return error;
|
|
|
|
|
|
Recover_platform:
|
|
|
- if (suspend_ops->recover)
|
|
|
+ if (need_suspend_ops(state) && suspend_ops->recover)
|
|
|
suspend_ops->recover();
|
|
|
goto Resume_devices;
|
|
|
}
|
|
@@ -278,12 +318,15 @@ static int enter_state(suspend_state_t state)
|
|
|
if (!mutex_trylock(&pm_mutex))
|
|
|
return -EBUSY;
|
|
|
|
|
|
+ if (state == PM_SUSPEND_FREEZE)
|
|
|
+ freeze_begin();
|
|
|
+
|
|
|
printk(KERN_INFO "PM: Syncing filesystems ... ");
|
|
|
sys_sync();
|
|
|
printk("done.\n");
|
|
|
|
|
|
pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);
|
|
|
- error = suspend_prepare();
|
|
|
+ error = suspend_prepare(state);
|
|
|
if (error)
|
|
|
goto Unlock;
|
|
|
|