13d0407baSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 23d0407baSopenharmony_ci/* 33d0407baSopenharmony_ci * kernel/power/suspend.c - Suspend to RAM and standby functionality. 43d0407baSopenharmony_ci * 53d0407baSopenharmony_ci * Copyright (c) 2003 Patrick Mochel 63d0407baSopenharmony_ci * Copyright (c) 2003 Open Source Development Lab 73d0407baSopenharmony_ci * Copyright (c) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc. 83d0407baSopenharmony_ci */ 93d0407baSopenharmony_ci 103d0407baSopenharmony_ci#define pr_fmt(fmt) "PM: " fmt 113d0407baSopenharmony_ci 123d0407baSopenharmony_ci#include <linux/string.h> 133d0407baSopenharmony_ci#include <linux/delay.h> 143d0407baSopenharmony_ci#include <linux/errno.h> 153d0407baSopenharmony_ci#include <linux/init.h> 163d0407baSopenharmony_ci#include <linux/console.h> 173d0407baSopenharmony_ci#include <linux/cpu.h> 183d0407baSopenharmony_ci#include <linux/cpuidle.h> 193d0407baSopenharmony_ci#include <linux/gfp.h> 203d0407baSopenharmony_ci#include <linux/io.h> 213d0407baSopenharmony_ci#include <linux/kernel.h> 223d0407baSopenharmony_ci#include <linux/list.h> 233d0407baSopenharmony_ci#include <linux/mm.h> 243d0407baSopenharmony_ci#include <linux/slab.h> 253d0407baSopenharmony_ci#include <linux/export.h> 263d0407baSopenharmony_ci#include <linux/suspend.h> 273d0407baSopenharmony_ci#include <linux/syscore_ops.h> 283d0407baSopenharmony_ci#include <linux/swait.h> 293d0407baSopenharmony_ci#include <linux/ftrace.h> 303d0407baSopenharmony_ci#include <trace/events/power.h> 313d0407baSopenharmony_ci#include <linux/compiler.h> 323d0407baSopenharmony_ci#include <linux/moduleparam.h> 333d0407baSopenharmony_ci#include <linux/wakeup_reason.h> 343d0407baSopenharmony_ci 353d0407baSopenharmony_ci#include "power.h" 363d0407baSopenharmony_ci 373d0407baSopenharmony_ciconst char *const pm_labels[] = { 383d0407baSopenharmony_ci [PM_SUSPEND_TO_IDLE] = "freeze", 393d0407baSopenharmony_ci [PM_SUSPEND_STANDBY] = "standby", 403d0407baSopenharmony_ci [PM_SUSPEND_MEM] = "mem", 413d0407baSopenharmony_ci}; 423d0407baSopenharmony_ciconst char *pm_states[PM_SUSPEND_MAX]; 433d0407baSopenharmony_cistatic const char *const mem_sleep_labels[] = { 443d0407baSopenharmony_ci [PM_SUSPEND_TO_IDLE] = "s2idle", 453d0407baSopenharmony_ci [PM_SUSPEND_STANDBY] = "shallow", 463d0407baSopenharmony_ci [PM_SUSPEND_MEM] = "deep", 473d0407baSopenharmony_ci}; 483d0407baSopenharmony_ciconst char *mem_sleep_states[PM_SUSPEND_MAX]; 493d0407baSopenharmony_ci 503d0407baSopenharmony_cisuspend_state_t mem_sleep_current = PM_SUSPEND_TO_IDLE; 513d0407baSopenharmony_cisuspend_state_t mem_sleep_default = PM_SUSPEND_MAX; 523d0407baSopenharmony_cisuspend_state_t pm_suspend_target_state; 533d0407baSopenharmony_ciEXPORT_SYMBOL_GPL(pm_suspend_target_state); 543d0407baSopenharmony_ci 553d0407baSopenharmony_ciunsigned int pm_suspend_global_flags; 563d0407baSopenharmony_ciEXPORT_SYMBOL_GPL(pm_suspend_global_flags); 573d0407baSopenharmony_ci 583d0407baSopenharmony_cistatic const struct platform_suspend_ops *suspend_ops; 593d0407baSopenharmony_cistatic const struct platform_s2idle_ops *s2idle_ops; 603d0407baSopenharmony_cistatic DECLARE_SWAIT_QUEUE_HEAD(s2idle_wait_head); 613d0407baSopenharmony_ci 623d0407baSopenharmony_cienum s2idle_states __read_mostly s2idle_state; 633d0407baSopenharmony_cistatic DEFINE_RAW_SPINLOCK(s2idle_lock); 643d0407baSopenharmony_ci 653d0407baSopenharmony_ci/** 663d0407baSopenharmony_ci * pm_suspend_default_s2idle - Check if suspend-to-idle is the default suspend. 673d0407baSopenharmony_ci * 683d0407baSopenharmony_ci * Return 'true' if suspend-to-idle has been selected as the default system 693d0407baSopenharmony_ci * suspend method. 703d0407baSopenharmony_ci */ 713d0407baSopenharmony_cibool pm_suspend_default_s2idle(void) 723d0407baSopenharmony_ci{ 733d0407baSopenharmony_ci return mem_sleep_current == PM_SUSPEND_TO_IDLE; 743d0407baSopenharmony_ci} 753d0407baSopenharmony_ciEXPORT_SYMBOL_GPL(pm_suspend_default_s2idle); 763d0407baSopenharmony_ci 773d0407baSopenharmony_civoid s2idle_set_ops(const struct platform_s2idle_ops *ops) 783d0407baSopenharmony_ci{ 793d0407baSopenharmony_ci lock_system_sleep(); 803d0407baSopenharmony_ci s2idle_ops = ops; 813d0407baSopenharmony_ci unlock_system_sleep(); 823d0407baSopenharmony_ci} 833d0407baSopenharmony_ci 843d0407baSopenharmony_cistatic void s2idle_begin(void) 853d0407baSopenharmony_ci{ 863d0407baSopenharmony_ci s2idle_state = S2IDLE_STATE_NONE; 873d0407baSopenharmony_ci} 883d0407baSopenharmony_ci 893d0407baSopenharmony_cistatic void s2idle_enter(void) 903d0407baSopenharmony_ci{ 913d0407baSopenharmony_ci trace_suspend_resume(TPS("machine_suspend"), PM_SUSPEND_TO_IDLE, true); 923d0407baSopenharmony_ci 933d0407baSopenharmony_ci raw_spin_lock_irq(&s2idle_lock); 943d0407baSopenharmony_ci if (pm_wakeup_pending()) { 953d0407baSopenharmony_ci goto out; 963d0407baSopenharmony_ci } 973d0407baSopenharmony_ci 983d0407baSopenharmony_ci s2idle_state = S2IDLE_STATE_ENTER; 993d0407baSopenharmony_ci raw_spin_unlock_irq(&s2idle_lock); 1003d0407baSopenharmony_ci 1013d0407baSopenharmony_ci get_online_cpus(); 1023d0407baSopenharmony_ci cpuidle_resume(); 1033d0407baSopenharmony_ci 1043d0407baSopenharmony_ci /* Push all the CPUs into the idle loop. */ 1053d0407baSopenharmony_ci wake_up_all_idle_cpus(); 1063d0407baSopenharmony_ci /* Make the current CPU wait so it can enter the idle loop too. */ 1073d0407baSopenharmony_ci swait_event_exclusive(s2idle_wait_head, s2idle_state == S2IDLE_STATE_WAKE); 1083d0407baSopenharmony_ci 1093d0407baSopenharmony_ci cpuidle_pause(); 1103d0407baSopenharmony_ci put_online_cpus(); 1113d0407baSopenharmony_ci 1123d0407baSopenharmony_ci raw_spin_lock_irq(&s2idle_lock); 1133d0407baSopenharmony_ci 1143d0407baSopenharmony_ciout: 1153d0407baSopenharmony_ci s2idle_state = S2IDLE_STATE_NONE; 1163d0407baSopenharmony_ci raw_spin_unlock_irq(&s2idle_lock); 1173d0407baSopenharmony_ci 1183d0407baSopenharmony_ci trace_suspend_resume(TPS("machine_suspend"), PM_SUSPEND_TO_IDLE, false); 1193d0407baSopenharmony_ci} 1203d0407baSopenharmony_ci 1213d0407baSopenharmony_cistatic void s2idle_loop(void) 1223d0407baSopenharmony_ci{ 1233d0407baSopenharmony_ci pm_pr_dbg("suspend-to-idle\n"); 1243d0407baSopenharmony_ci 1253d0407baSopenharmony_ci /* 1263d0407baSopenharmony_ci * Suspend-to-idle equals: 1273d0407baSopenharmony_ci * frozen processes + suspended devices + idle processors. 1283d0407baSopenharmony_ci * Thus s2idle_enter() should be called right after all devices have 1293d0407baSopenharmony_ci * been suspended. 1303d0407baSopenharmony_ci * 1313d0407baSopenharmony_ci * Wakeups during the noirq suspend of devices may be spurious, so try 1323d0407baSopenharmony_ci * to avoid them upfront. 1333d0407baSopenharmony_ci */ 1343d0407baSopenharmony_ci for (;;) { 1353d0407baSopenharmony_ci if (s2idle_ops && s2idle_ops->wake) { 1363d0407baSopenharmony_ci if (s2idle_ops->wake()) { 1373d0407baSopenharmony_ci break; 1383d0407baSopenharmony_ci } 1393d0407baSopenharmony_ci } else if (pm_wakeup_pending()) { 1403d0407baSopenharmony_ci break; 1413d0407baSopenharmony_ci } 1423d0407baSopenharmony_ci 1433d0407baSopenharmony_ci clear_wakeup_reasons(); 1443d0407baSopenharmony_ci 1453d0407baSopenharmony_ci s2idle_enter(); 1463d0407baSopenharmony_ci } 1473d0407baSopenharmony_ci 1483d0407baSopenharmony_ci pm_pr_dbg("resume from suspend-to-idle\n"); 1493d0407baSopenharmony_ci} 1503d0407baSopenharmony_ci 1513d0407baSopenharmony_civoid s2idle_wake(void) 1523d0407baSopenharmony_ci{ 1533d0407baSopenharmony_ci unsigned long flags; 1543d0407baSopenharmony_ci 1553d0407baSopenharmony_ci raw_spin_lock_irqsave(&s2idle_lock, flags); 1563d0407baSopenharmony_ci if (s2idle_state > S2IDLE_STATE_NONE) { 1573d0407baSopenharmony_ci s2idle_state = S2IDLE_STATE_WAKE; 1583d0407baSopenharmony_ci swake_up_one(&s2idle_wait_head); 1593d0407baSopenharmony_ci } 1603d0407baSopenharmony_ci raw_spin_unlock_irqrestore(&s2idle_lock, flags); 1613d0407baSopenharmony_ci} 1623d0407baSopenharmony_ciEXPORT_SYMBOL_GPL(s2idle_wake); 1633d0407baSopenharmony_ci 1643d0407baSopenharmony_cistatic bool valid_state(suspend_state_t state) 1653d0407baSopenharmony_ci{ 1663d0407baSopenharmony_ci /* 1673d0407baSopenharmony_ci * PM_SUSPEND_STANDBY and PM_SUSPEND_MEM states need low level 1683d0407baSopenharmony_ci * support and need to be valid to the low level 1693d0407baSopenharmony_ci * implementation, no valid callback implies that none are valid. 1703d0407baSopenharmony_ci */ 1713d0407baSopenharmony_ci return suspend_ops && suspend_ops->valid && suspend_ops->valid(state); 1723d0407baSopenharmony_ci} 1733d0407baSopenharmony_ci 1743d0407baSopenharmony_civoid __init pm_states_init(void) 1753d0407baSopenharmony_ci{ 1763d0407baSopenharmony_ci /* "mem" and "freeze" are always present in /sys/power/state. */ 1773d0407baSopenharmony_ci pm_states[PM_SUSPEND_MEM] = pm_labels[PM_SUSPEND_MEM]; 1783d0407baSopenharmony_ci pm_states[PM_SUSPEND_TO_IDLE] = pm_labels[PM_SUSPEND_TO_IDLE]; 1793d0407baSopenharmony_ci /* 1803d0407baSopenharmony_ci * Suspend-to-idle should be supported even without any suspend_ops, 1813d0407baSopenharmony_ci * initialize mem_sleep_states[] accordingly here. 1823d0407baSopenharmony_ci */ 1833d0407baSopenharmony_ci mem_sleep_states[PM_SUSPEND_TO_IDLE] = mem_sleep_labels[PM_SUSPEND_TO_IDLE]; 1843d0407baSopenharmony_ci} 1853d0407baSopenharmony_ci 1863d0407baSopenharmony_cistatic int __init mem_sleep_default_setup(char *str) 1873d0407baSopenharmony_ci{ 1883d0407baSopenharmony_ci suspend_state_t state; 1893d0407baSopenharmony_ci 1903d0407baSopenharmony_ci for (state = PM_SUSPEND_TO_IDLE; state <= PM_SUSPEND_MEM; state++) { 1913d0407baSopenharmony_ci if (mem_sleep_labels[state] && !strcmp(str, mem_sleep_labels[state])) { 1923d0407baSopenharmony_ci mem_sleep_default = state; 1933d0407baSopenharmony_ci break; 1943d0407baSopenharmony_ci } 1953d0407baSopenharmony_ci } 1963d0407baSopenharmony_ci 1973d0407baSopenharmony_ci return 1; 1983d0407baSopenharmony_ci} 1993d0407baSopenharmony_ci__setup("mem_sleep_default=", mem_sleep_default_setup); 2003d0407baSopenharmony_ci 2013d0407baSopenharmony_ci/** 2023d0407baSopenharmony_ci * suspend_set_ops - Set the global suspend method table. 2033d0407baSopenharmony_ci * @ops: Suspend operations to use. 2043d0407baSopenharmony_ci */ 2053d0407baSopenharmony_civoid suspend_set_ops(const struct platform_suspend_ops *ops) 2063d0407baSopenharmony_ci{ 2073d0407baSopenharmony_ci lock_system_sleep(); 2083d0407baSopenharmony_ci 2093d0407baSopenharmony_ci suspend_ops = ops; 2103d0407baSopenharmony_ci 2113d0407baSopenharmony_ci if (valid_state(PM_SUSPEND_STANDBY)) { 2123d0407baSopenharmony_ci mem_sleep_states[PM_SUSPEND_STANDBY] = mem_sleep_labels[PM_SUSPEND_STANDBY]; 2133d0407baSopenharmony_ci pm_states[PM_SUSPEND_STANDBY] = pm_labels[PM_SUSPEND_STANDBY]; 2143d0407baSopenharmony_ci if (mem_sleep_default == PM_SUSPEND_STANDBY) { 2153d0407baSopenharmony_ci mem_sleep_current = PM_SUSPEND_STANDBY; 2163d0407baSopenharmony_ci } 2173d0407baSopenharmony_ci } 2183d0407baSopenharmony_ci if (valid_state(PM_SUSPEND_MEM)) { 2193d0407baSopenharmony_ci mem_sleep_states[PM_SUSPEND_MEM] = mem_sleep_labels[PM_SUSPEND_MEM]; 2203d0407baSopenharmony_ci if (mem_sleep_default >= PM_SUSPEND_MEM) { 2213d0407baSopenharmony_ci mem_sleep_current = PM_SUSPEND_MEM; 2223d0407baSopenharmony_ci } 2233d0407baSopenharmony_ci } 2243d0407baSopenharmony_ci 2253d0407baSopenharmony_ci unlock_system_sleep(); 2263d0407baSopenharmony_ci} 2273d0407baSopenharmony_ciEXPORT_SYMBOL_GPL(suspend_set_ops); 2283d0407baSopenharmony_ci 2293d0407baSopenharmony_ci/** 2303d0407baSopenharmony_ci * suspend_valid_only_mem - Generic memory-only valid callback. 2313d0407baSopenharmony_ci * 2323d0407baSopenharmony_ci * Platform drivers that implement mem suspend only and only need to check for 2333d0407baSopenharmony_ci * that in their .valid() callback can use this instead of rolling their own 2343d0407baSopenharmony_ci * .valid() callback. 2353d0407baSopenharmony_ci */ 2363d0407baSopenharmony_ciint suspend_valid_only_mem(suspend_state_t state) 2373d0407baSopenharmony_ci{ 2383d0407baSopenharmony_ci return state == PM_SUSPEND_MEM; 2393d0407baSopenharmony_ci} 2403d0407baSopenharmony_ciEXPORT_SYMBOL_GPL(suspend_valid_only_mem); 2413d0407baSopenharmony_ci 2423d0407baSopenharmony_cistatic bool sleep_state_supported(suspend_state_t state) 2433d0407baSopenharmony_ci{ 2443d0407baSopenharmony_ci return state == PM_SUSPEND_TO_IDLE || (suspend_ops && suspend_ops->enter); 2453d0407baSopenharmony_ci} 2463d0407baSopenharmony_ci 2473d0407baSopenharmony_cistatic int platform_suspend_prepare(suspend_state_t state) 2483d0407baSopenharmony_ci{ 2493d0407baSopenharmony_ci return state != PM_SUSPEND_TO_IDLE && suspend_ops->prepare ? suspend_ops->prepare() : 0; 2503d0407baSopenharmony_ci} 2513d0407baSopenharmony_ci 2523d0407baSopenharmony_cistatic int platform_suspend_prepare_late(suspend_state_t state) 2533d0407baSopenharmony_ci{ 2543d0407baSopenharmony_ci return state == PM_SUSPEND_TO_IDLE && s2idle_ops && s2idle_ops->prepare ? s2idle_ops->prepare() : 0; 2553d0407baSopenharmony_ci} 2563d0407baSopenharmony_ci 2573d0407baSopenharmony_cistatic int platform_suspend_prepare_noirq(suspend_state_t state) 2583d0407baSopenharmony_ci{ 2593d0407baSopenharmony_ci if (state == PM_SUSPEND_TO_IDLE) { 2603d0407baSopenharmony_ci return s2idle_ops && s2idle_ops->prepare_late ? s2idle_ops->prepare_late() : 0; 2613d0407baSopenharmony_ci } 2623d0407baSopenharmony_ci 2633d0407baSopenharmony_ci return suspend_ops->prepare_late ? suspend_ops->prepare_late() : 0; 2643d0407baSopenharmony_ci} 2653d0407baSopenharmony_ci 2663d0407baSopenharmony_cistatic void platform_resume_noirq(suspend_state_t state) 2673d0407baSopenharmony_ci{ 2683d0407baSopenharmony_ci if (state == PM_SUSPEND_TO_IDLE) { 2693d0407baSopenharmony_ci if (s2idle_ops && s2idle_ops->restore_early) { 2703d0407baSopenharmony_ci s2idle_ops->restore_early(); 2713d0407baSopenharmony_ci } 2723d0407baSopenharmony_ci } else if (suspend_ops->wake) { 2733d0407baSopenharmony_ci suspend_ops->wake(); 2743d0407baSopenharmony_ci } 2753d0407baSopenharmony_ci} 2763d0407baSopenharmony_ci 2773d0407baSopenharmony_cistatic void platform_resume_early(suspend_state_t state) 2783d0407baSopenharmony_ci{ 2793d0407baSopenharmony_ci if (state == PM_SUSPEND_TO_IDLE && s2idle_ops && s2idle_ops->restore) { 2803d0407baSopenharmony_ci s2idle_ops->restore(); 2813d0407baSopenharmony_ci } 2823d0407baSopenharmony_ci} 2833d0407baSopenharmony_ci 2843d0407baSopenharmony_cistatic void platform_resume_finish(suspend_state_t state) 2853d0407baSopenharmony_ci{ 2863d0407baSopenharmony_ci if (state != PM_SUSPEND_TO_IDLE && suspend_ops->finish) { 2873d0407baSopenharmony_ci suspend_ops->finish(); 2883d0407baSopenharmony_ci } 2893d0407baSopenharmony_ci} 2903d0407baSopenharmony_ci 2913d0407baSopenharmony_cistatic int platform_suspend_begin(suspend_state_t state) 2923d0407baSopenharmony_ci{ 2933d0407baSopenharmony_ci if (state == PM_SUSPEND_TO_IDLE && s2idle_ops && s2idle_ops->begin) { 2943d0407baSopenharmony_ci return s2idle_ops->begin(); 2953d0407baSopenharmony_ci } else if (suspend_ops && suspend_ops->begin) { 2963d0407baSopenharmony_ci return suspend_ops->begin(state); 2973d0407baSopenharmony_ci } else { 2983d0407baSopenharmony_ci return 0; 2993d0407baSopenharmony_ci } 3003d0407baSopenharmony_ci} 3013d0407baSopenharmony_ci 3023d0407baSopenharmony_cistatic void platform_resume_end(suspend_state_t state) 3033d0407baSopenharmony_ci{ 3043d0407baSopenharmony_ci if (state == PM_SUSPEND_TO_IDLE && s2idle_ops && s2idle_ops->end) { 3053d0407baSopenharmony_ci s2idle_ops->end(); 3063d0407baSopenharmony_ci } else if (suspend_ops && suspend_ops->end) { 3073d0407baSopenharmony_ci suspend_ops->end(); 3083d0407baSopenharmony_ci } 3093d0407baSopenharmony_ci} 3103d0407baSopenharmony_ci 3113d0407baSopenharmony_cistatic void platform_recover(suspend_state_t state) 3123d0407baSopenharmony_ci{ 3133d0407baSopenharmony_ci if (state != PM_SUSPEND_TO_IDLE && suspend_ops->recover) { 3143d0407baSopenharmony_ci suspend_ops->recover(); 3153d0407baSopenharmony_ci } 3163d0407baSopenharmony_ci} 3173d0407baSopenharmony_ci 3183d0407baSopenharmony_cistatic bool platform_suspend_again(suspend_state_t state) 3193d0407baSopenharmony_ci{ 3203d0407baSopenharmony_ci return state != PM_SUSPEND_TO_IDLE && suspend_ops->suspend_again ? suspend_ops->suspend_again() : false; 3213d0407baSopenharmony_ci} 3223d0407baSopenharmony_ci 3233d0407baSopenharmony_ci#ifdef CONFIG_PM_DEBUG 3243d0407baSopenharmony_cistatic unsigned int pm_test_delay = 5; 3253d0407baSopenharmony_cimodule_param(pm_test_delay, uint, 0644); 3263d0407baSopenharmony_ciMODULE_PARM_DESC(pm_test_delay, "Number of seconds to wait before resuming from suspend test"); 3273d0407baSopenharmony_ci#endif 3283d0407baSopenharmony_ci 3293d0407baSopenharmony_cistatic int suspend_test(int level) 3303d0407baSopenharmony_ci{ 3313d0407baSopenharmony_ci#ifdef CONFIG_PM_DEBUG 3323d0407baSopenharmony_ci if (pm_test_level == level) { 3333d0407baSopenharmony_ci pr_info("suspend debug: Waiting for %d second(s).\n", pm_test_delay); 3343d0407baSopenharmony_ci mdelay(pm_test_delay * 1000); 3353d0407baSopenharmony_ci return 1; 3363d0407baSopenharmony_ci } 3373d0407baSopenharmony_ci#endif /* !CONFIG_PM_DEBUG */ 3383d0407baSopenharmony_ci return 0; 3393d0407baSopenharmony_ci} 3403d0407baSopenharmony_ci 3413d0407baSopenharmony_ci/** 3423d0407baSopenharmony_ci * suspend_prepare - Prepare for entering system sleep state. 3433d0407baSopenharmony_ci * 3443d0407baSopenharmony_ci * Common code run for every system sleep state that can be entered (except for 3453d0407baSopenharmony_ci * hibernation). Run suspend notifiers, allocate the "suspend" console and 3463d0407baSopenharmony_ci * freeze processes. 3473d0407baSopenharmony_ci */ 3483d0407baSopenharmony_cistatic int suspend_prepare(suspend_state_t state) 3493d0407baSopenharmony_ci{ 3503d0407baSopenharmony_ci int error; 3513d0407baSopenharmony_ci 3523d0407baSopenharmony_ci if (!sleep_state_supported(state)) { 3533d0407baSopenharmony_ci return -EPERM; 3543d0407baSopenharmony_ci } 3553d0407baSopenharmony_ci 3563d0407baSopenharmony_ci pm_prepare_console(); 3573d0407baSopenharmony_ci 3583d0407baSopenharmony_ci error = pm_notifier_call_chain_robust(PM_SUSPEND_PREPARE, PM_POST_SUSPEND); 3593d0407baSopenharmony_ci if (error) { 3603d0407baSopenharmony_ci goto Restore; 3613d0407baSopenharmony_ci } 3623d0407baSopenharmony_ci 3633d0407baSopenharmony_ci trace_suspend_resume(TPS("freeze_processes"), 0, true); 3643d0407baSopenharmony_ci error = suspend_freeze_processes(); 3653d0407baSopenharmony_ci trace_suspend_resume(TPS("freeze_processes"), 0, false); 3663d0407baSopenharmony_ci if (!error) { 3673d0407baSopenharmony_ci return 0; 3683d0407baSopenharmony_ci } 3693d0407baSopenharmony_ci 3703d0407baSopenharmony_ci log_suspend_abort_reason("One or more tasks refusing to freeze"); 3713d0407baSopenharmony_ci suspend_stats.failed_freeze++; 3723d0407baSopenharmony_ci dpm_save_failed_step(SUSPEND_FREEZE); 3733d0407baSopenharmony_ci pm_notifier_call_chain(PM_POST_SUSPEND); 3743d0407baSopenharmony_ciRestore: 3753d0407baSopenharmony_ci pm_restore_console(); 3763d0407baSopenharmony_ci return error; 3773d0407baSopenharmony_ci} 3783d0407baSopenharmony_ci 3793d0407baSopenharmony_ci/* default implementation */ 3803d0407baSopenharmony_civoid __weak arch_suspend_disable_irqs(void) 3813d0407baSopenharmony_ci{ 3823d0407baSopenharmony_ci local_irq_disable(); 3833d0407baSopenharmony_ci} 3843d0407baSopenharmony_ci 3853d0407baSopenharmony_ci/* default implementation */ 3863d0407baSopenharmony_civoid __weak arch_suspend_enable_irqs(void) 3873d0407baSopenharmony_ci{ 3883d0407baSopenharmony_ci local_irq_enable(); 3893d0407baSopenharmony_ci} 3903d0407baSopenharmony_ci 3913d0407baSopenharmony_ci/** 3923d0407baSopenharmony_ci * suspend_enter - Make the system enter the given sleep state. 3933d0407baSopenharmony_ci * @state: System sleep state to enter. 3943d0407baSopenharmony_ci * @wakeup: Returns information that the sleep state should not be re-entered. 3953d0407baSopenharmony_ci * 3963d0407baSopenharmony_ci * This function should be called after devices have been suspended. 3973d0407baSopenharmony_ci */ 3983d0407baSopenharmony_cistatic int suspend_enter(suspend_state_t state, bool *wakeup) 3993d0407baSopenharmony_ci{ 4003d0407baSopenharmony_ci int error, last_dev; 4013d0407baSopenharmony_ci 4023d0407baSopenharmony_ci error = platform_suspend_prepare(state); 4033d0407baSopenharmony_ci if (error) { 4043d0407baSopenharmony_ci goto Platform_finish; 4053d0407baSopenharmony_ci } 4063d0407baSopenharmony_ci 4073d0407baSopenharmony_ci error = dpm_suspend_late(PMSG_SUSPEND); 4083d0407baSopenharmony_ci if (error) { 4093d0407baSopenharmony_ci last_dev = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1; 4103d0407baSopenharmony_ci last_dev %= REC_FAILED_NUM; 4113d0407baSopenharmony_ci pr_err("late suspend of devices failed\n"); 4123d0407baSopenharmony_ci log_suspend_abort_reason("late suspend of %s device failed", suspend_stats.failed_devs[last_dev]); 4133d0407baSopenharmony_ci goto Platform_finish; 4143d0407baSopenharmony_ci } 4153d0407baSopenharmony_ci error = platform_suspend_prepare_late(state); 4163d0407baSopenharmony_ci if (error) { 4173d0407baSopenharmony_ci goto Devices_early_resume; 4183d0407baSopenharmony_ci } 4193d0407baSopenharmony_ci 4203d0407baSopenharmony_ci error = dpm_suspend_noirq(PMSG_SUSPEND); 4213d0407baSopenharmony_ci if (error) { 4223d0407baSopenharmony_ci last_dev = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1; 4233d0407baSopenharmony_ci last_dev %= REC_FAILED_NUM; 4243d0407baSopenharmony_ci pr_err("noirq suspend of devices failed\n"); 4253d0407baSopenharmony_ci log_suspend_abort_reason("noirq suspend of %s device failed", suspend_stats.failed_devs[last_dev]); 4263d0407baSopenharmony_ci goto Platform_early_resume; 4273d0407baSopenharmony_ci } 4283d0407baSopenharmony_ci error = platform_suspend_prepare_noirq(state); 4293d0407baSopenharmony_ci if (error) { 4303d0407baSopenharmony_ci goto Platform_wake; 4313d0407baSopenharmony_ci } 4323d0407baSopenharmony_ci 4333d0407baSopenharmony_ci if (suspend_test(TEST_PLATFORM)) { 4343d0407baSopenharmony_ci goto Platform_wake; 4353d0407baSopenharmony_ci } 4363d0407baSopenharmony_ci 4373d0407baSopenharmony_ci if (state == PM_SUSPEND_TO_IDLE) { 4383d0407baSopenharmony_ci s2idle_loop(); 4393d0407baSopenharmony_ci goto Platform_wake; 4403d0407baSopenharmony_ci } 4413d0407baSopenharmony_ci 4423d0407baSopenharmony_ci error = suspend_disable_secondary_cpus(); 4433d0407baSopenharmony_ci if (error || suspend_test(TEST_CPUS)) { 4443d0407baSopenharmony_ci log_suspend_abort_reason("Disabling non-boot cpus failed"); 4453d0407baSopenharmony_ci goto Enable_cpus; 4463d0407baSopenharmony_ci } 4473d0407baSopenharmony_ci 4483d0407baSopenharmony_ci arch_suspend_disable_irqs(); 4493d0407baSopenharmony_ci BUG_ON(!irqs_disabled()); 4503d0407baSopenharmony_ci 4513d0407baSopenharmony_ci system_state = SYSTEM_SUSPEND; 4523d0407baSopenharmony_ci 4533d0407baSopenharmony_ci error = syscore_suspend(); 4543d0407baSopenharmony_ci if (!error) { 4553d0407baSopenharmony_ci *wakeup = pm_wakeup_pending(); 4563d0407baSopenharmony_ci if (!(suspend_test(TEST_CORE) || *wakeup)) { 4573d0407baSopenharmony_ci trace_suspend_resume(TPS("machine_suspend"), state, true); 4583d0407baSopenharmony_ci error = suspend_ops->enter(state); 4593d0407baSopenharmony_ci trace_suspend_resume(TPS("machine_suspend"), state, false); 4603d0407baSopenharmony_ci } else if (*wakeup) { 4613d0407baSopenharmony_ci error = -EBUSY; 4623d0407baSopenharmony_ci } 4633d0407baSopenharmony_ci syscore_resume(); 4643d0407baSopenharmony_ci } 4653d0407baSopenharmony_ci 4663d0407baSopenharmony_ci system_state = SYSTEM_RUNNING; 4673d0407baSopenharmony_ci 4683d0407baSopenharmony_ci arch_suspend_enable_irqs(); 4693d0407baSopenharmony_ci BUG_ON(irqs_disabled()); 4703d0407baSopenharmony_ci 4713d0407baSopenharmony_ciEnable_cpus: 4723d0407baSopenharmony_ci suspend_enable_secondary_cpus(); 4733d0407baSopenharmony_ci 4743d0407baSopenharmony_ciPlatform_wake: 4753d0407baSopenharmony_ci platform_resume_noirq(state); 4763d0407baSopenharmony_ci dpm_resume_noirq(PMSG_RESUME); 4773d0407baSopenharmony_ci 4783d0407baSopenharmony_ciPlatform_early_resume: 4793d0407baSopenharmony_ci platform_resume_early(state); 4803d0407baSopenharmony_ci 4813d0407baSopenharmony_ciDevices_early_resume: 4823d0407baSopenharmony_ci dpm_resume_early(PMSG_RESUME); 4833d0407baSopenharmony_ci 4843d0407baSopenharmony_ciPlatform_finish: 4853d0407baSopenharmony_ci platform_resume_finish(state); 4863d0407baSopenharmony_ci return error; 4873d0407baSopenharmony_ci} 4883d0407baSopenharmony_ci 4893d0407baSopenharmony_ci/** 4903d0407baSopenharmony_ci * suspend_devices_and_enter - Suspend devices and enter system sleep state. 4913d0407baSopenharmony_ci * @state: System sleep state to enter. 4923d0407baSopenharmony_ci */ 4933d0407baSopenharmony_ciint suspend_devices_and_enter(suspend_state_t state) 4943d0407baSopenharmony_ci{ 4953d0407baSopenharmony_ci int error; 4963d0407baSopenharmony_ci bool wakeup = false; 4973d0407baSopenharmony_ci 4983d0407baSopenharmony_ci if (!sleep_state_supported(state)) { 4993d0407baSopenharmony_ci return -ENOSYS; 5003d0407baSopenharmony_ci } 5013d0407baSopenharmony_ci 5023d0407baSopenharmony_ci pm_suspend_target_state = state; 5033d0407baSopenharmony_ci 5043d0407baSopenharmony_ci if (state == PM_SUSPEND_TO_IDLE) { 5053d0407baSopenharmony_ci pm_set_suspend_no_platform(); 5063d0407baSopenharmony_ci } 5073d0407baSopenharmony_ci 5083d0407baSopenharmony_ci error = platform_suspend_begin(state); 5093d0407baSopenharmony_ci if (error) { 5103d0407baSopenharmony_ci goto Close; 5113d0407baSopenharmony_ci } 5123d0407baSopenharmony_ci 5133d0407baSopenharmony_ci suspend_console(); 5143d0407baSopenharmony_ci suspend_test_start(); 5153d0407baSopenharmony_ci error = dpm_suspend_start(PMSG_SUSPEND); 5163d0407baSopenharmony_ci if (error) { 5173d0407baSopenharmony_ci pr_err("Some devices failed to suspend, or early wake event detected\n"); 5183d0407baSopenharmony_ci log_suspend_abort_reason("Some devices failed to suspend, or early wake event detected"); 5193d0407baSopenharmony_ci goto Recover_platform; 5203d0407baSopenharmony_ci } 5213d0407baSopenharmony_ci suspend_test_finish("suspend devices"); 5223d0407baSopenharmony_ci if (suspend_test(TEST_DEVICES)) { 5233d0407baSopenharmony_ci goto Recover_platform; 5243d0407baSopenharmony_ci } 5253d0407baSopenharmony_ci 5263d0407baSopenharmony_ci do { 5273d0407baSopenharmony_ci error = suspend_enter(state, &wakeup); 5283d0407baSopenharmony_ci } while (!error && !wakeup && platform_suspend_again(state)); 5293d0407baSopenharmony_ci 5303d0407baSopenharmony_ciResume_devices: 5313d0407baSopenharmony_ci suspend_test_start(); 5323d0407baSopenharmony_ci dpm_resume_end(PMSG_RESUME); 5333d0407baSopenharmony_ci suspend_test_finish("resume devices"); 5343d0407baSopenharmony_ci trace_suspend_resume(TPS("resume_console"), state, true); 5353d0407baSopenharmony_ci resume_console(); 5363d0407baSopenharmony_ci trace_suspend_resume(TPS("resume_console"), state, false); 5373d0407baSopenharmony_ci 5383d0407baSopenharmony_ciClose: 5393d0407baSopenharmony_ci platform_resume_end(state); 5403d0407baSopenharmony_ci pm_suspend_target_state = PM_SUSPEND_ON; 5413d0407baSopenharmony_ci return error; 5423d0407baSopenharmony_ci 5433d0407baSopenharmony_ciRecover_platform: 5443d0407baSopenharmony_ci platform_recover(state); 5453d0407baSopenharmony_ci goto Resume_devices; 5463d0407baSopenharmony_ci} 5473d0407baSopenharmony_ci 5483d0407baSopenharmony_ci/** 5493d0407baSopenharmony_ci * suspend_finish - Clean up before finishing the suspend sequence. 5503d0407baSopenharmony_ci * 5513d0407baSopenharmony_ci * Call platform code to clean up, restart processes, and free the console that 5523d0407baSopenharmony_ci * we've allocated. This routine is not called for hibernation. 5533d0407baSopenharmony_ci */ 5543d0407baSopenharmony_cistatic void suspend_finish(void) 5553d0407baSopenharmony_ci{ 5563d0407baSopenharmony_ci suspend_thaw_processes(); 5573d0407baSopenharmony_ci pm_notifier_call_chain(PM_POST_SUSPEND); 5583d0407baSopenharmony_ci pm_restore_console(); 5593d0407baSopenharmony_ci} 5603d0407baSopenharmony_ci 5613d0407baSopenharmony_ci/** 5623d0407baSopenharmony_ci * enter_state - Do common work needed to enter system sleep state. 5633d0407baSopenharmony_ci * @state: System sleep state to enter. 5643d0407baSopenharmony_ci * 5653d0407baSopenharmony_ci * Make sure that no one else is trying to put the system into a sleep state. 5663d0407baSopenharmony_ci * Fail if that's not the case. Otherwise, prepare for system suspend, make the 5673d0407baSopenharmony_ci * system enter the given sleep state and clean up after wakeup. 5683d0407baSopenharmony_ci */ 5693d0407baSopenharmony_cistatic int enter_state(suspend_state_t state) 5703d0407baSopenharmony_ci{ 5713d0407baSopenharmony_ci int error; 5723d0407baSopenharmony_ci 5733d0407baSopenharmony_ci trace_suspend_resume(TPS("suspend_enter"), state, true); 5743d0407baSopenharmony_ci if (state == PM_SUSPEND_TO_IDLE) { 5753d0407baSopenharmony_ci#ifdef CONFIG_PM_DEBUG 5763d0407baSopenharmony_ci if (pm_test_level != TEST_NONE && pm_test_level <= TEST_CPUS) { 5773d0407baSopenharmony_ci pr_warn("Unsupported test mode for suspend to idle, please choose none/freezer/devices/platform.\n"); 5783d0407baSopenharmony_ci return -EAGAIN; 5793d0407baSopenharmony_ci } 5803d0407baSopenharmony_ci#endif 5813d0407baSopenharmony_ci } else if (!valid_state(state)) { 5823d0407baSopenharmony_ci return -EINVAL; 5833d0407baSopenharmony_ci } 5843d0407baSopenharmony_ci if (!mutex_trylock(&system_transition_mutex)) { 5853d0407baSopenharmony_ci return -EBUSY; 5863d0407baSopenharmony_ci } 5873d0407baSopenharmony_ci 5883d0407baSopenharmony_ci if (state == PM_SUSPEND_TO_IDLE) { 5893d0407baSopenharmony_ci s2idle_begin(); 5903d0407baSopenharmony_ci } 5913d0407baSopenharmony_ci 5923d0407baSopenharmony_ci if (sync_on_suspend_enabled) { 5933d0407baSopenharmony_ci trace_suspend_resume(TPS("sync_filesystems"), 0, true); 5943d0407baSopenharmony_ci ksys_sync_helper(); 5953d0407baSopenharmony_ci trace_suspend_resume(TPS("sync_filesystems"), 0, false); 5963d0407baSopenharmony_ci } 5973d0407baSopenharmony_ci 5983d0407baSopenharmony_ci pm_pr_dbg("Preparing system for sleep (%s)\n", mem_sleep_labels[state]); 5993d0407baSopenharmony_ci pm_suspend_clear_flags(); 6003d0407baSopenharmony_ci error = suspend_prepare(state); 6013d0407baSopenharmony_ci if (error) { 6023d0407baSopenharmony_ci goto Unlock; 6033d0407baSopenharmony_ci } 6043d0407baSopenharmony_ci 6053d0407baSopenharmony_ci if (suspend_test(TEST_FREEZER)) { 6063d0407baSopenharmony_ci goto Finish; 6073d0407baSopenharmony_ci } 6083d0407baSopenharmony_ci 6093d0407baSopenharmony_ci trace_suspend_resume(TPS("suspend_enter"), state, false); 6103d0407baSopenharmony_ci pm_pr_dbg("Suspending system (%s)\n", mem_sleep_labels[state]); 6113d0407baSopenharmony_ci pm_restrict_gfp_mask(); 6123d0407baSopenharmony_ci error = suspend_devices_and_enter(state); 6133d0407baSopenharmony_ci pm_restore_gfp_mask(); 6143d0407baSopenharmony_ci 6153d0407baSopenharmony_ciFinish: 6163d0407baSopenharmony_ci events_check_enabled = false; 6173d0407baSopenharmony_ci pm_pr_dbg("Finishing wakeup.\n"); 6183d0407baSopenharmony_ci suspend_finish(); 6193d0407baSopenharmony_ciUnlock: 6203d0407baSopenharmony_ci mutex_unlock(&system_transition_mutex); 6213d0407baSopenharmony_ci return error; 6223d0407baSopenharmony_ci} 6233d0407baSopenharmony_ci 6243d0407baSopenharmony_ci/** 6253d0407baSopenharmony_ci * pm_suspend - Externally visible function for suspending the system. 6263d0407baSopenharmony_ci * @state: System sleep state to enter. 6273d0407baSopenharmony_ci * 6283d0407baSopenharmony_ci * Check if the value of @state represents one of the supported states, 6293d0407baSopenharmony_ci * execute enter_state() and update system suspend statistics. 6303d0407baSopenharmony_ci */ 6313d0407baSopenharmony_ciint pm_suspend(suspend_state_t state) 6323d0407baSopenharmony_ci{ 6333d0407baSopenharmony_ci int error; 6343d0407baSopenharmony_ci 6353d0407baSopenharmony_ci if (state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX) { 6363d0407baSopenharmony_ci return -EINVAL; 6373d0407baSopenharmony_ci } 6383d0407baSopenharmony_ci 6393d0407baSopenharmony_ci pr_info("suspend entry (%s)\n", mem_sleep_labels[state]); 6403d0407baSopenharmony_ci error = enter_state(state); 6413d0407baSopenharmony_ci if (error) { 6423d0407baSopenharmony_ci suspend_stats.fail++; 6433d0407baSopenharmony_ci dpm_save_failed_errno(error); 6443d0407baSopenharmony_ci } else { 6453d0407baSopenharmony_ci suspend_stats.success++; 6463d0407baSopenharmony_ci } 6473d0407baSopenharmony_ci pr_info("suspend exit\n"); 6483d0407baSopenharmony_ci return error; 6493d0407baSopenharmony_ci} 6503d0407baSopenharmony_ciEXPORT_SYMBOL(pm_suspend); 651