aboutsummaryrefslogtreecommitdiffstats
path: root/arch/um/kernel/um_arch.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2020-12-02 20:58:07 +0100
committerRichard Weinberger <richard@nod.at>2020-12-13 22:22:49 +0100
commita374b7cb1ea648a27ceaa2dea19aa967725e938b (patch)
tree3caccdb36c1cc214f10a2f2d2e267acee1f101a6 /arch/um/kernel/um_arch.c
parentum: Allow PM with suspend-to-idle (diff)
downloadlinux-dev-a374b7cb1ea648a27ceaa2dea19aa967725e938b.tar.xz
linux-dev-a374b7cb1ea648a27ceaa2dea19aa967725e938b.zip
um: Support suspend to RAM
With all the previous bits in place, we can now also support suspend to RAM, in the sense that everything is suspended, not just most, including userspace, processes like in s2idle. Since um_idle_sleep() now waits forever, we can simply call that to "suspend" the system. As before, you can wake it up using SIGUSR1 since we're just in a pause() call that only needs to return. In order to implement selective resume from certain devices, and not have any arbitrary device interrupt wake up, suspend interrupts by removing SIGIO notification (O_ASYNC) from all the FDs that are not supposed to wake up the system. However, swap out the handler so we don't actually handle the SIGIO as an interrupt. Since we're in pause(), the mere act of receiving SIGIO wakes us up, and then after things have been restored enough, re-set O_ASYNC for all previously suspended FDs, reinstall the proper SIGIO handler, and send SIGIO to self to process anything that might now be pending. Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: Richard Weinberger <richard@nod.at>
Diffstat (limited to 'arch/um/kernel/um_arch.c')
-rw-r--r--arch/um/kernel/um_arch.c42
1 files changed, 42 insertions, 0 deletions
diff --git a/arch/um/kernel/um_arch.c b/arch/um/kernel/um_arch.c
index 237a8d73a096..9c7e6d7ea1b3 100644
--- a/arch/um/kernel/um_arch.c
+++ b/arch/um/kernel/um_arch.c
@@ -385,6 +385,45 @@ void uml_pm_wake(void)
pm_system_wakeup();
}
+static int um_suspend_valid(suspend_state_t state)
+{
+ return state == PM_SUSPEND_MEM;
+}
+
+static int um_suspend_prepare(void)
+{
+ um_irqs_suspend();
+ return 0;
+}
+
+static int um_suspend_enter(suspend_state_t state)
+{
+ if (WARN_ON(state != PM_SUSPEND_MEM))
+ return -EINVAL;
+
+ /*
+ * This is identical to the idle sleep, but we've just
+ * (during suspend) turned off all interrupt sources
+ * except for the ones we want, so now we can only wake
+ * up on something we actually want to wake up on. All
+ * timing has also been suspended.
+ */
+ um_idle_sleep();
+ return 0;
+}
+
+static void um_suspend_finish(void)
+{
+ um_irqs_resume();
+}
+
+const struct platform_suspend_ops um_suspend_ops = {
+ .valid = um_suspend_valid,
+ .prepare = um_suspend_prepare,
+ .enter = um_suspend_enter,
+ .finish = um_suspend_finish,
+};
+
static int init_pm_wake_signal(void)
{
/*
@@ -397,6 +436,9 @@ static int init_pm_wake_signal(void)
*/
if (time_travel_mode != TT_MODE_EXTERNAL)
register_pm_wake_signal();
+
+ suspend_set_ops(&um_suspend_ops);
+
return 0;
}