// SPDX-License-Identifier: GPL-2.0-only /* * kernel/power/suspend.c - Suspend to RAM and standby functionality. * * Copyright (c) 2003 Patrick Mochel * Copyright (c) 2003 Open Source Development Lab * Copyright (c) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
*/
/** * pm_suspend_default_s2idle - Check if suspend-to-idle is the default suspend. * * Return 'true' if suspend-to-idle has been selected as the default system * suspend method.
*/ bool pm_suspend_default_s2idle(void)
{ return mem_sleep_current == PM_SUSPEND_TO_IDLE;
}
EXPORT_SYMBOL_GPL(pm_suspend_default_s2idle);
/* * The correctness of the code below depends on the number of online * CPUs being stable, but CPUs cannot be taken offline or put online * while it is running. * * The s2idle_lock must be acquired before the pending wakeup check to * prevent pm_system_wakeup() from running as a whole between that check * and the subsequent s2idle_state update in which case a wakeup event * would get lost.
*/
raw_spin_lock_irq(&s2idle_lock); if (pm_wakeup_pending()) goto out;
/* Push all the CPUs into the idle loop. */
wake_up_all_idle_cpus(); /* Make the current CPU wait so it can enter the idle loop too. */
swait_event_exclusive(s2idle_wait_head,
s2idle_state == S2IDLE_STATE_WAKE);
/* * Kick all CPUs to ensure that they resume their timers and restore * consistent system state.
*/
wake_up_all_idle_cpus();
/* * Suspend-to-idle equals: * frozen processes + suspended devices + idle processors. * Thus s2idle_enter() should be called right after all devices have * been suspended. * * Wakeups during the noirq suspend of devices may be spurious, so try * to avoid them upfront.
*/ for (;;) { if (s2idle_ops && s2idle_ops->wake) { if (s2idle_ops->wake()) break;
} elseif (pm_wakeup_pending()) { break;
}
if (s2idle_ops && s2idle_ops->check)
s2idle_ops->check();
staticbool valid_state(suspend_state_t state)
{ /* * The PM_SUSPEND_STANDBY and PM_SUSPEND_MEM states require low-level * support and need to be valid to the low-level implementation. * * No ->valid() or ->enter() callback implies that none are valid.
*/ return suspend_ops && suspend_ops->valid && suspend_ops->valid(state) &&
suspend_ops->enter;
}
void __init pm_states_init(void)
{ /* "mem" and "freeze" are always present in /sys/power/state. */
pm_states[PM_SUSPEND_MEM] = pm_labels[PM_SUSPEND_MEM];
pm_states[PM_SUSPEND_TO_IDLE] = pm_labels[PM_SUSPEND_TO_IDLE]; /* * Suspend-to-idle should be supported even without any suspend_ops, * initialize mem_sleep_states[] accordingly here.
*/
mem_sleep_states[PM_SUSPEND_TO_IDLE] = mem_sleep_labels[PM_SUSPEND_TO_IDLE];
}
/** * suspend_valid_only_mem - Generic memory-only valid callback. * @state: Target system sleep state. * * Platform drivers that implement mem suspend only and only need to check for * that in their .valid() callback can use this instead of rolling their own * .valid() callback.
*/ int suspend_valid_only_mem(suspend_state_t state)
{ return state == PM_SUSPEND_MEM;
}
EXPORT_SYMBOL_GPL(suspend_valid_only_mem);
/** * suspend_prepare - Prepare for entering system sleep state. * @state: Target system sleep state. * * Common code run for every system sleep state that can be entered (except for * hibernation). Run suspend notifiers, allocate the "suspend" console and * freeze processes.
*/ staticint suspend_prepare(suspend_state_t state)
{ int error;
if (!sleep_state_supported(state)) return -EPERM;
pm_prepare_console();
error = pm_notifier_call_chain_robust(PM_SUSPEND_PREPARE, PM_POST_SUSPEND); if (error) goto Restore;
if (filesystem_freeze_enabled)
filesystems_freeze();
trace_suspend_resume(TPS("freeze_processes"), 0, true);
error = suspend_freeze_processes();
trace_suspend_resume(TPS("freeze_processes"), 0, false); if (!error) return 0;
/** * suspend_enter - Make the system enter the given sleep state. * @state: System sleep state to enter. * @wakeup: Returns information that the sleep state should not be re-entered. * * This function should be called after devices have been suspended.
*/ staticint suspend_enter(suspend_state_t state, bool *wakeup)
{ int error;
error = platform_suspend_prepare(state); if (error) goto Platform_finish;
error = dpm_suspend_late(PMSG_SUSPEND); if (error) {
pr_err("late suspend of devices failed\n"); goto Platform_finish;
}
error = platform_suspend_prepare_late(state); if (error) goto Devices_early_resume;
error = dpm_suspend_noirq(PMSG_SUSPEND); if (error) {
pr_err("noirq suspend of devices failed\n"); goto Platform_early_resume;
}
error = platform_suspend_prepare_noirq(state); if (error) goto Platform_wake;
if (suspend_test(TEST_PLATFORM)) goto Platform_wake;
if (state == PM_SUSPEND_TO_IDLE) {
s2idle_loop(); goto Platform_wake;
}
error = pm_sleep_disable_secondary_cpus(); if (error || suspend_test(TEST_CPUS)) goto Enable_cpus;
/** * suspend_devices_and_enter - Suspend devices and enter system sleep state. * @state: System sleep state to enter.
*/ int suspend_devices_and_enter(suspend_state_t state)
{ int error; bool wakeup = false;
if (!sleep_state_supported(state)) return -ENOSYS;
pm_suspend_target_state = state;
if (state == PM_SUSPEND_TO_IDLE)
pm_set_suspend_no_platform();
error = platform_suspend_begin(state); if (error) goto Close;
console_suspend_all();
suspend_test_start();
error = dpm_suspend_start(PMSG_SUSPEND); if (error) {
pr_err("Some devices failed to suspend, or early wake event detected\n"); goto Recover_platform;
}
suspend_test_finish("suspend devices"); if (suspend_test(TEST_DEVICES)) goto Recover_platform;
do {
error = suspend_enter(state, &wakeup);
} while (!error && !wakeup && platform_suspend_again(state));
/** * suspend_finish - Clean up before finishing the suspend sequence. * * Call platform code to clean up, restart processes, and free the console that * we've allocated. This routine is not called for hibernation.
*/ staticvoid suspend_finish(void)
{
suspend_thaw_processes();
filesystems_thaw();
pm_notifier_call_chain(PM_POST_SUSPEND);
pm_restore_console();
}
/** * enter_state - Do common work needed to enter system sleep state. * @state: System sleep state to enter. * * Make sure that no one else is trying to put the system into a sleep state. * Fail if that's not the case. Otherwise, prepare for system suspend, make the * system enter the given sleep state and clean up after wakeup.
*/ staticint enter_state(suspend_state_t state)
{ int error;
trace_suspend_resume(TPS("suspend_enter"), state, true); if (state == PM_SUSPEND_TO_IDLE) { #ifdef CONFIG_PM_DEBUG if (pm_test_level != TEST_NONE && pm_test_level <= TEST_CPUS) {
pr_warn("Unsupported test mode for suspend to idle, please choose none/freezer/devices/platform.\n"); return -EAGAIN;
} #endif
} elseif (!valid_state(state)) { return -EINVAL;
} if (!mutex_trylock(&system_transition_mutex)) return -EBUSY;
pm_pr_dbg("Preparing system for sleep (%s)\n", mem_sleep_labels[state]);
pm_suspend_clear_flags();
error = suspend_prepare(state); if (error) goto Unlock;
if (suspend_test(TEST_FREEZER)) goto Finish;
trace_suspend_resume(TPS("suspend_enter"), state, false);
pm_pr_dbg("Suspending system (%s)\n", mem_sleep_labels[state]);
error = suspend_devices_and_enter(state);
/** * pm_suspend - Externally visible function for suspending the system. * @state: System sleep state to enter. * * Check if the value of @state represents one of the supported states, * execute enter_state() and update system suspend statistics.
*/ int pm_suspend(suspend_state_t state)
{ int error;
if (state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX) return -EINVAL;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.