diff --git a/drivers/base/syscore.c b/drivers/base/syscore.c
index 90af294..1a7e08d 100644
--- a/drivers/base/syscore.c
+++ b/drivers/base/syscore.c
@@ -95,6 +95,42 @@ void syscore_resume(void)
 				"Interrupts enabled after %pF\n", ops->resume);
 		}
 }
+
+/**
+ * syscore_suspend_again - Execute all the registeres system core suspend_again
+ *			callbacks. If at least one returns
+ *			SUSPEND_AGAIN_CONTINUE and no one returns
+ *			SUSPEND_AGAIN_STOP, syscore_suspend_again let the system
+ *			enter suspend again.
+ */
+bool syscore_suspend_again(void)
+{
+	struct syscore_ops *ops;
+	enum suspend_again_cond condition = SUSPEND_AGAIN_NC;
+
+	list_for_each_entry(ops, &syscore_ops_list, node)
+		if (ops->suspend_again) {
+			switch (ops->suspend_again()) {
+			case SUSPEND_AGAIN_NC:
+				break;
+			case SUSPEND_AGAIN_CONTINUE:
+				if (condition == SUSPEND_AGAIN_NC)
+					condition = SUSPEND_AGAIN_CONTINUE;
+				break;
+			case SUSPEND_AGAIN_STOP:
+				condition = SUSPEND_AGAIN_STOP;
+				break;
+			default:
+				pr_warn("PM: incorrect return from %pF\n",
+					ops->suspend_again);
+			}
+		}
+
+	if (condition == SUSPEND_AGAIN_CONTINUE)
+		return true;
+
+	return false;
+}
 #endif /* CONFIG_PM_SLEEP */
 
 /**
diff --git a/include/linux/syscore_ops.h b/include/linux/syscore_ops.h
index 27b3b0b..bf9bc4e 100644
--- a/include/linux/syscore_ops.h
+++ b/include/linux/syscore_ops.h
@@ -11,10 +11,16 @@
 
 #include <linux/list.h>
 
+enum suspend_again_cond {
+	SUSPEND_AGAIN_NC = 0, /* Do Not Care */
+	SUSPEND_AGAIN_CONTINUE, /* Start or keep the again */
+	SUSPEND_AGAIN_STOP, /* Stop or do not start. Override CONTINUE */
+};
 struct syscore_ops {
 	struct list_head node;
 	int (*suspend)(void);
 	void (*resume)(void);
+	enum suspend_again_cond (*suspend_again)(void);
 	void (*shutdown)(void);
 };
 
@@ -23,6 +29,7 @@ extern void unregister_syscore_ops(struct syscore_ops *ops);
 #ifdef CONFIG_PM_SLEEP
 extern int syscore_suspend(void);
 extern void syscore_resume(void);
+extern bool syscore_suspend_again(void);
 #endif
 extern void syscore_shutdown(void);
 
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
index 2814c32..aa6a3d1 100644
--- a/kernel/power/suspend.c
+++ b/kernel/power/suspend.c
@@ -202,43 +202,48 @@ static int suspend_enter(suspend_state_t state)
 int suspend_devices_and_enter(suspend_state_t state)
 {
 	int error;
+	bool recover = false;
 
 	if (!suspend_ops)
 		return -ENOSYS;
 
-	trace_machine_suspend(state);
-	if (suspend_ops->begin) {
-		error = suspend_ops->begin(state);
-		if (error)
-			goto Close;
-	}
-	suspend_console();
-	pm_restrict_gfp_mask();
-	suspend_test_start();
-	error = dpm_suspend_start(PMSG_SUSPEND);
-	if (error) {
-		printk(KERN_ERR "PM: Some devices failed to suspend\n");
-		goto Recover_platform;
-	}
-	suspend_test_finish("suspend devices");
-	if (suspend_test(TEST_DEVICES))
-		goto Recover_platform;
+	do {
+		trace_machine_suspend(state);
+		if (suspend_ops->begin) {
+			error = suspend_ops->begin(state);
+			if (error)
+				goto Close;
+		}
+		suspend_console();
+		pm_restrict_gfp_mask();
+		suspend_test_start();
+		error = dpm_suspend_start(PMSG_SUSPEND);
+		if (error) {
+			printk(KERN_ERR "PM: Some devices failed to suspend\n");
+			goto Recover_platform;
+		}
+		suspend_test_finish("suspend devices");
+		if (suspend_test(TEST_DEVICES))
+			goto Recover_platform;
 
-	suspend_enter(state);
+		error = suspend_enter(state);
 
  Resume_devices:
-	suspend_test_start();
-	dpm_resume_end(PMSG_RESUME);
-	suspend_test_finish("resume devices");
-	pm_restore_gfp_mask();
-	resume_console();
+		suspend_test_start();
+		dpm_resume_end(PMSG_RESUME);
+		suspend_test_finish("resume devices");
+		pm_restore_gfp_mask();
+		resume_console();
  Close:
-	if (suspend_ops->end)
-		suspend_ops->end();
-	trace_machine_suspend(PWR_EVENT_EXIT);
+		if (suspend_ops->end)
+			suspend_ops->end();
+		trace_machine_suspend(PWR_EVENT_EXIT);
+	} while (syscore_suspend_again() && !error && !recover);
+
 	return error;
 
  Recover_platform:
+	recover = true;
 	if (suspend_ops->recover)
 		suspend_ops->recover();
 	goto Resume_devices;
