155716d26SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 21da177e4SLinus Torvalds /* 31da177e4SLinus Torvalds * kernel/power/main.c - PM subsystem core functionality. 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * Copyright (c) 2003 Patrick Mochel 61da177e4SLinus Torvalds * Copyright (c) 2003 Open Source Development Lab 71da177e4SLinus Torvalds */ 81da177e4SLinus Torvalds 9b52124a7SMario Limonciello #include <linux/acpi.h> 106e5fdeedSPaul Gortmaker #include <linux/export.h> 111da177e4SLinus Torvalds #include <linux/kobject.h> 121da177e4SLinus Torvalds #include <linux/string.h> 13431d452aSZhonghui Fu #include <linux/pm-trace.h> 145e928f77SRafael J. Wysocki #include <linux/workqueue.h> 152a77c46dSShuoX Liu #include <linux/debugfs.h> 162a77c46dSShuoX Liu #include <linux/seq_file.h> 1755f2503cSPingfan Liu #include <linux/suspend.h> 18b5dee313SHarry Pan #include <linux/syscalls.h> 19f49249d5SBen Dooks #include <linux/pm_runtime.h> 201da177e4SLinus Torvalds 211da177e4SLinus Torvalds #include "power.h" 221da177e4SLinus Torvalds 23cd51e61cSRafael J. Wysocki #ifdef CONFIG_PM_SLEEP 24*07f44ac3SKefeng Wang /* 25*07f44ac3SKefeng Wang * The following functions are used by the suspend/hibernate code to temporarily 26*07f44ac3SKefeng Wang * change gfp_allowed_mask in order to avoid using I/O during memory allocations 27*07f44ac3SKefeng Wang * while devices are suspended. To avoid races with the suspend/hibernate code, 28*07f44ac3SKefeng Wang * they should always be called with system_transition_mutex held 29*07f44ac3SKefeng Wang * (gfp_allowed_mask also should only be modified with system_transition_mutex 30*07f44ac3SKefeng Wang * held, unless the suspend/hibernate code is guaranteed not to run in parallel 31*07f44ac3SKefeng Wang * with that modification). 32*07f44ac3SKefeng Wang */ 33*07f44ac3SKefeng Wang static gfp_t saved_gfp_mask; 34*07f44ac3SKefeng Wang 35*07f44ac3SKefeng Wang void pm_restore_gfp_mask(void) 36*07f44ac3SKefeng Wang { 37*07f44ac3SKefeng Wang WARN_ON(!mutex_is_locked(&system_transition_mutex)); 38*07f44ac3SKefeng Wang if (saved_gfp_mask) { 39*07f44ac3SKefeng Wang gfp_allowed_mask = saved_gfp_mask; 40*07f44ac3SKefeng Wang saved_gfp_mask = 0; 41*07f44ac3SKefeng Wang } 42*07f44ac3SKefeng Wang } 43*07f44ac3SKefeng Wang 44*07f44ac3SKefeng Wang void pm_restrict_gfp_mask(void) 45*07f44ac3SKefeng Wang { 46*07f44ac3SKefeng Wang WARN_ON(!mutex_is_locked(&system_transition_mutex)); 47*07f44ac3SKefeng Wang WARN_ON(saved_gfp_mask); 48*07f44ac3SKefeng Wang saved_gfp_mask = gfp_allowed_mask; 49*07f44ac3SKefeng Wang gfp_allowed_mask &= ~(__GFP_IO | __GFP_FS); 50*07f44ac3SKefeng Wang } 51cd51e61cSRafael J. Wysocki 525950e5d5SPeter Zijlstra unsigned int lock_system_sleep(void) 534bf236a3SBart Van Assche { 545950e5d5SPeter Zijlstra unsigned int flags = current->flags; 55f5d39b02SPeter Zijlstra current->flags |= PF_NOFREEZE; 5655f2503cSPingfan Liu mutex_lock(&system_transition_mutex); 575950e5d5SPeter Zijlstra return flags; 584bf236a3SBart Van Assche } 594bf236a3SBart Van Assche EXPORT_SYMBOL_GPL(lock_system_sleep); 604bf236a3SBart Van Assche 615950e5d5SPeter Zijlstra void unlock_system_sleep(unsigned int flags) 624bf236a3SBart Van Assche { 634bf236a3SBart Van Assche /* 644bf236a3SBart Van Assche * Don't use freezer_count() because we don't want the call to 654bf236a3SBart Van Assche * try_to_freeze() here. 664bf236a3SBart Van Assche * 674bf236a3SBart Van Assche * Reason: 684bf236a3SBart Van Assche * Fundamentally, we just don't need it, because freezing condition 6955f2503cSPingfan Liu * doesn't come into effect until we release the 7055f2503cSPingfan Liu * system_transition_mutex lock, since the freezer always works with 7155f2503cSPingfan Liu * system_transition_mutex held. 724bf236a3SBart Van Assche * 734bf236a3SBart Van Assche * More importantly, in the case of hibernation, 744bf236a3SBart Van Assche * unlock_system_sleep() gets called in snapshot_read() and 754bf236a3SBart Van Assche * snapshot_write() when the freezing condition is still in effect. 764bf236a3SBart Van Assche * Which means, if we use try_to_freeze() here, it would make them 774bf236a3SBart Van Assche * enter the refrigerator, thus causing hibernation to lockup. 784bf236a3SBart Van Assche */ 79f5d39b02SPeter Zijlstra if (!(flags & PF_NOFREEZE)) 80f5d39b02SPeter Zijlstra current->flags &= ~PF_NOFREEZE; 8155f2503cSPingfan Liu mutex_unlock(&system_transition_mutex); 824bf236a3SBart Van Assche } 834bf236a3SBart Van Assche EXPORT_SYMBOL_GPL(unlock_system_sleep); 844bf236a3SBart Van Assche 85b5dee313SHarry Pan void ksys_sync_helper(void) 86b5dee313SHarry Pan { 87c64546b1SHarry Pan ktime_t start; 88c64546b1SHarry Pan long elapsed_msecs; 89c64546b1SHarry Pan 90c64546b1SHarry Pan start = ktime_get(); 91b5dee313SHarry Pan ksys_sync(); 92c64546b1SHarry Pan elapsed_msecs = ktime_to_ms(ktime_sub(ktime_get(), start)); 93c64546b1SHarry Pan pr_info("Filesystems sync: %ld.%03ld seconds\n", 94c64546b1SHarry Pan elapsed_msecs / MSEC_PER_SEC, elapsed_msecs % MSEC_PER_SEC); 95b5dee313SHarry Pan } 96b5dee313SHarry Pan EXPORT_SYMBOL_GPL(ksys_sync_helper); 97b5dee313SHarry Pan 9882525756SAlan Stern /* Routines for PM-transition notifications */ 9982525756SAlan Stern 10082525756SAlan Stern static BLOCKING_NOTIFIER_HEAD(pm_chain_head); 10182525756SAlan Stern 10282525756SAlan Stern int register_pm_notifier(struct notifier_block *nb) 10382525756SAlan Stern { 10482525756SAlan Stern return blocking_notifier_chain_register(&pm_chain_head, nb); 10582525756SAlan Stern } 10682525756SAlan Stern EXPORT_SYMBOL_GPL(register_pm_notifier); 10782525756SAlan Stern 10882525756SAlan Stern int unregister_pm_notifier(struct notifier_block *nb) 10982525756SAlan Stern { 11082525756SAlan Stern return blocking_notifier_chain_unregister(&pm_chain_head, nb); 11182525756SAlan Stern } 11282525756SAlan Stern EXPORT_SYMBOL_GPL(unregister_pm_notifier); 11382525756SAlan Stern 114b52124a7SMario Limonciello void pm_report_hw_sleep_time(u64 t) 115b52124a7SMario Limonciello { 116b52124a7SMario Limonciello suspend_stats.last_hw_sleep = t; 117b52124a7SMario Limonciello suspend_stats.total_hw_sleep += t; 118b52124a7SMario Limonciello } 119b52124a7SMario Limonciello EXPORT_SYMBOL_GPL(pm_report_hw_sleep_time); 120b52124a7SMario Limonciello 121b52124a7SMario Limonciello void pm_report_max_hw_sleep(u64 t) 122b52124a7SMario Limonciello { 123b52124a7SMario Limonciello suspend_stats.max_hw_sleep = t; 124b52124a7SMario Limonciello } 125b52124a7SMario Limonciello EXPORT_SYMBOL_GPL(pm_report_max_hw_sleep); 126b52124a7SMario Limonciello 12770d93298SPeter Zijlstra int pm_notifier_call_chain_robust(unsigned long val_up, unsigned long val_down) 12882525756SAlan Stern { 129ea00f4f4SLianwei Wang int ret; 130ea00f4f4SLianwei Wang 13170d93298SPeter Zijlstra ret = blocking_notifier_call_chain_robust(&pm_chain_head, val_up, val_down, NULL); 132f0c077a8SAkinobu Mita 133f0c077a8SAkinobu Mita return notifier_to_errno(ret); 13482525756SAlan Stern } 13570d93298SPeter Zijlstra 136ea00f4f4SLianwei Wang int pm_notifier_call_chain(unsigned long val) 137ea00f4f4SLianwei Wang { 13870d93298SPeter Zijlstra return blocking_notifier_call_chain(&pm_chain_head, val, NULL); 139ea00f4f4SLianwei Wang } 14082525756SAlan Stern 1410e06b4a8SRafael J. Wysocki /* If set, devices may be suspended and resumed asynchronously. */ 1420e06b4a8SRafael J. Wysocki int pm_async_enabled = 1; 1430e06b4a8SRafael J. Wysocki 1440e06b4a8SRafael J. Wysocki static ssize_t pm_async_show(struct kobject *kobj, struct kobj_attribute *attr, 1450e06b4a8SRafael J. Wysocki char *buf) 1460e06b4a8SRafael J. Wysocki { 1470e06b4a8SRafael J. Wysocki return sprintf(buf, "%d\n", pm_async_enabled); 1480e06b4a8SRafael J. Wysocki } 1490e06b4a8SRafael J. Wysocki 1500e06b4a8SRafael J. Wysocki static ssize_t pm_async_store(struct kobject *kobj, struct kobj_attribute *attr, 1510e06b4a8SRafael J. Wysocki const char *buf, size_t n) 1520e06b4a8SRafael J. Wysocki { 1530e06b4a8SRafael J. Wysocki unsigned long val; 1540e06b4a8SRafael J. Wysocki 155883ee4f7SDaniel Walter if (kstrtoul(buf, 10, &val)) 1560e06b4a8SRafael J. Wysocki return -EINVAL; 1570e06b4a8SRafael J. Wysocki 1580e06b4a8SRafael J. Wysocki if (val > 1) 1590e06b4a8SRafael J. Wysocki return -EINVAL; 1600e06b4a8SRafael J. Wysocki 1610e06b4a8SRafael J. Wysocki pm_async_enabled = val; 1620e06b4a8SRafael J. Wysocki return n; 1630e06b4a8SRafael J. Wysocki } 1640e06b4a8SRafael J. Wysocki 1650e06b4a8SRafael J. Wysocki power_attr(pm_async); 1660e06b4a8SRafael J. Wysocki 167406e7938SRafael J. Wysocki #ifdef CONFIG_SUSPEND 168406e7938SRafael J. Wysocki static ssize_t mem_sleep_show(struct kobject *kobj, struct kobj_attribute *attr, 169406e7938SRafael J. Wysocki char *buf) 170406e7938SRafael J. Wysocki { 171406e7938SRafael J. Wysocki char *s = buf; 172406e7938SRafael J. Wysocki suspend_state_t i; 173406e7938SRafael J. Wysocki 1749ea4dcf4SDan Williams for (i = PM_SUSPEND_MIN; i < PM_SUSPEND_MAX; i++) { 1759ea4dcf4SDan Williams if (i >= PM_SUSPEND_MEM && cxl_mem_active()) 1769ea4dcf4SDan Williams continue; 177406e7938SRafael J. Wysocki if (mem_sleep_states[i]) { 178406e7938SRafael J. Wysocki const char *label = mem_sleep_states[i]; 179406e7938SRafael J. Wysocki 180406e7938SRafael J. Wysocki if (mem_sleep_current == i) 181406e7938SRafael J. Wysocki s += sprintf(s, "[%s] ", label); 182406e7938SRafael J. Wysocki else 183406e7938SRafael J. Wysocki s += sprintf(s, "%s ", label); 184406e7938SRafael J. Wysocki } 1859ea4dcf4SDan Williams } 186406e7938SRafael J. Wysocki 187406e7938SRafael J. Wysocki /* Convert the last space to a newline if needed. */ 188406e7938SRafael J. Wysocki if (s != buf) 189406e7938SRafael J. Wysocki *(s-1) = '\n'; 190406e7938SRafael J. Wysocki 191406e7938SRafael J. Wysocki return (s - buf); 192406e7938SRafael J. Wysocki } 193406e7938SRafael J. Wysocki 194406e7938SRafael J. Wysocki static suspend_state_t decode_suspend_state(const char *buf, size_t n) 195406e7938SRafael J. Wysocki { 196406e7938SRafael J. Wysocki suspend_state_t state; 197406e7938SRafael J. Wysocki char *p; 198406e7938SRafael J. Wysocki int len; 199406e7938SRafael J. Wysocki 200406e7938SRafael J. Wysocki p = memchr(buf, '\n', n); 201406e7938SRafael J. Wysocki len = p ? p - buf : n; 202406e7938SRafael J. Wysocki 203406e7938SRafael J. Wysocki for (state = PM_SUSPEND_MIN; state < PM_SUSPEND_MAX; state++) { 204406e7938SRafael J. Wysocki const char *label = mem_sleep_states[state]; 205406e7938SRafael J. Wysocki 206406e7938SRafael J. Wysocki if (label && len == strlen(label) && !strncmp(buf, label, len)) 207406e7938SRafael J. Wysocki return state; 208406e7938SRafael J. Wysocki } 209406e7938SRafael J. Wysocki 210406e7938SRafael J. Wysocki return PM_SUSPEND_ON; 211406e7938SRafael J. Wysocki } 212406e7938SRafael J. Wysocki 213406e7938SRafael J. Wysocki static ssize_t mem_sleep_store(struct kobject *kobj, struct kobj_attribute *attr, 214406e7938SRafael J. Wysocki const char *buf, size_t n) 215406e7938SRafael J. Wysocki { 216406e7938SRafael J. Wysocki suspend_state_t state; 217406e7938SRafael J. Wysocki int error; 218406e7938SRafael J. Wysocki 219406e7938SRafael J. Wysocki error = pm_autosleep_lock(); 220406e7938SRafael J. Wysocki if (error) 221406e7938SRafael J. Wysocki return error; 222406e7938SRafael J. Wysocki 223406e7938SRafael J. Wysocki if (pm_autosleep_state() > PM_SUSPEND_ON) { 224406e7938SRafael J. Wysocki error = -EBUSY; 225406e7938SRafael J. Wysocki goto out; 226406e7938SRafael J. Wysocki } 227406e7938SRafael J. Wysocki 228406e7938SRafael J. Wysocki state = decode_suspend_state(buf, n); 229406e7938SRafael J. Wysocki if (state < PM_SUSPEND_MAX && state > PM_SUSPEND_ON) 230406e7938SRafael J. Wysocki mem_sleep_current = state; 231406e7938SRafael J. Wysocki else 232406e7938SRafael J. Wysocki error = -EINVAL; 233406e7938SRafael J. Wysocki 234406e7938SRafael J. Wysocki out: 235406e7938SRafael J. Wysocki pm_autosleep_unlock(); 236406e7938SRafael J. Wysocki return error ? error : n; 237406e7938SRafael J. Wysocki } 238406e7938SRafael J. Wysocki 239406e7938SRafael J. Wysocki power_attr(mem_sleep); 240c052bf82SJonas Meurer 241c052bf82SJonas Meurer /* 242c052bf82SJonas Meurer * sync_on_suspend: invoke ksys_sync_helper() before suspend. 243c052bf82SJonas Meurer * 244c052bf82SJonas Meurer * show() returns whether ksys_sync_helper() is invoked before suspend. 245c052bf82SJonas Meurer * store() accepts 0 or 1. 0 disables ksys_sync_helper() and 1 enables it. 246c052bf82SJonas Meurer */ 247c052bf82SJonas Meurer bool sync_on_suspend_enabled = !IS_ENABLED(CONFIG_SUSPEND_SKIP_SYNC); 248c052bf82SJonas Meurer 249c052bf82SJonas Meurer static ssize_t sync_on_suspend_show(struct kobject *kobj, 250c052bf82SJonas Meurer struct kobj_attribute *attr, char *buf) 251c052bf82SJonas Meurer { 252c052bf82SJonas Meurer return sprintf(buf, "%d\n", sync_on_suspend_enabled); 253c052bf82SJonas Meurer } 254c052bf82SJonas Meurer 255c052bf82SJonas Meurer static ssize_t sync_on_suspend_store(struct kobject *kobj, 256c052bf82SJonas Meurer struct kobj_attribute *attr, 257c052bf82SJonas Meurer const char *buf, size_t n) 258c052bf82SJonas Meurer { 259c052bf82SJonas Meurer unsigned long val; 260c052bf82SJonas Meurer 261c052bf82SJonas Meurer if (kstrtoul(buf, 10, &val)) 262c052bf82SJonas Meurer return -EINVAL; 263c052bf82SJonas Meurer 264c052bf82SJonas Meurer if (val > 1) 265c052bf82SJonas Meurer return -EINVAL; 266c052bf82SJonas Meurer 267c052bf82SJonas Meurer sync_on_suspend_enabled = !!val; 268c052bf82SJonas Meurer return n; 269c052bf82SJonas Meurer } 270c052bf82SJonas Meurer 271c052bf82SJonas Meurer power_attr(sync_on_suspend); 272406e7938SRafael J. Wysocki #endif /* CONFIG_SUSPEND */ 273406e7938SRafael J. Wysocki 274e516a1dbSRafael J. Wysocki #ifdef CONFIG_PM_SLEEP_DEBUG 2750e7d56e3SRafael J. Wysocki int pm_test_level = TEST_NONE; 2760e7d56e3SRafael J. Wysocki 2770e7d56e3SRafael J. Wysocki static const char * const pm_tests[__TEST_AFTER_LAST] = { 2780e7d56e3SRafael J. Wysocki [TEST_NONE] = "none", 2790e7d56e3SRafael J. Wysocki [TEST_CORE] = "core", 2800e7d56e3SRafael J. Wysocki [TEST_CPUS] = "processors", 2810e7d56e3SRafael J. Wysocki [TEST_PLATFORM] = "platform", 2820e7d56e3SRafael J. Wysocki [TEST_DEVICES] = "devices", 2830e7d56e3SRafael J. Wysocki [TEST_FREEZER] = "freezer", 2840e7d56e3SRafael J. Wysocki }; 2850e7d56e3SRafael J. Wysocki 286039a75c6SRafael J. Wysocki static ssize_t pm_test_show(struct kobject *kobj, struct kobj_attribute *attr, 287039a75c6SRafael J. Wysocki char *buf) 2880e7d56e3SRafael J. Wysocki { 2890e7d56e3SRafael J. Wysocki char *s = buf; 2900e7d56e3SRafael J. Wysocki int level; 2910e7d56e3SRafael J. Wysocki 2920e7d56e3SRafael J. Wysocki for (level = TEST_FIRST; level <= TEST_MAX; level++) 2930e7d56e3SRafael J. Wysocki if (pm_tests[level]) { 2940e7d56e3SRafael J. Wysocki if (level == pm_test_level) 2950e7d56e3SRafael J. Wysocki s += sprintf(s, "[%s] ", pm_tests[level]); 2960e7d56e3SRafael J. Wysocki else 2970e7d56e3SRafael J. Wysocki s += sprintf(s, "%s ", pm_tests[level]); 2980e7d56e3SRafael J. Wysocki } 2990e7d56e3SRafael J. Wysocki 3000e7d56e3SRafael J. Wysocki if (s != buf) 3010e7d56e3SRafael J. Wysocki /* convert the last space to a newline */ 3020e7d56e3SRafael J. Wysocki *(s-1) = '\n'; 3030e7d56e3SRafael J. Wysocki 3040e7d56e3SRafael J. Wysocki return (s - buf); 3050e7d56e3SRafael J. Wysocki } 3060e7d56e3SRafael J. Wysocki 307039a75c6SRafael J. Wysocki static ssize_t pm_test_store(struct kobject *kobj, struct kobj_attribute *attr, 308039a75c6SRafael J. Wysocki const char *buf, size_t n) 3090e7d56e3SRafael J. Wysocki { 3105950e5d5SPeter Zijlstra unsigned int sleep_flags; 3110e7d56e3SRafael J. Wysocki const char * const *s; 3125950e5d5SPeter Zijlstra int error = -EINVAL; 3130e7d56e3SRafael J. Wysocki int level; 3140e7d56e3SRafael J. Wysocki char *p; 3150e7d56e3SRafael J. Wysocki int len; 3160e7d56e3SRafael J. Wysocki 3170e7d56e3SRafael J. Wysocki p = memchr(buf, '\n', n); 3180e7d56e3SRafael J. Wysocki len = p ? p - buf : n; 3190e7d56e3SRafael J. Wysocki 3205950e5d5SPeter Zijlstra sleep_flags = lock_system_sleep(); 3210e7d56e3SRafael J. Wysocki 3220e7d56e3SRafael J. Wysocki level = TEST_FIRST; 3230e7d56e3SRafael J. Wysocki for (s = &pm_tests[level]; level <= TEST_MAX; s++, level++) 3240e7d56e3SRafael J. Wysocki if (*s && len == strlen(*s) && !strncmp(buf, *s, len)) { 3250e7d56e3SRafael J. Wysocki pm_test_level = level; 3260e7d56e3SRafael J. Wysocki error = 0; 3270e7d56e3SRafael J. Wysocki break; 3280e7d56e3SRafael J. Wysocki } 3290e7d56e3SRafael J. Wysocki 3305950e5d5SPeter Zijlstra unlock_system_sleep(sleep_flags); 3310e7d56e3SRafael J. Wysocki 3320e7d56e3SRafael J. Wysocki return error ? error : n; 3330e7d56e3SRafael J. Wysocki } 3340e7d56e3SRafael J. Wysocki 3350e7d56e3SRafael J. Wysocki power_attr(pm_test); 336e516a1dbSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP_DEBUG */ 3370e7d56e3SRafael J. Wysocki 3382a77c46dSShuoX Liu static char *suspend_step_name(enum suspend_stat_step step) 3392a77c46dSShuoX Liu { 3402a77c46dSShuoX Liu switch (step) { 3412a77c46dSShuoX Liu case SUSPEND_FREEZE: 3422a77c46dSShuoX Liu return "freeze"; 3432a77c46dSShuoX Liu case SUSPEND_PREPARE: 3442a77c46dSShuoX Liu return "prepare"; 3452a77c46dSShuoX Liu case SUSPEND_SUSPEND: 3462a77c46dSShuoX Liu return "suspend"; 3472a77c46dSShuoX Liu case SUSPEND_SUSPEND_NOIRQ: 3482a77c46dSShuoX Liu return "suspend_noirq"; 3492a77c46dSShuoX Liu case SUSPEND_RESUME_NOIRQ: 3502a77c46dSShuoX Liu return "resume_noirq"; 3512a77c46dSShuoX Liu case SUSPEND_RESUME: 3522a77c46dSShuoX Liu return "resume"; 3532a77c46dSShuoX Liu default: 3542a77c46dSShuoX Liu return ""; 3552a77c46dSShuoX Liu } 3562a77c46dSShuoX Liu } 3572a77c46dSShuoX Liu 358b52124a7SMario Limonciello #define suspend_attr(_name, format_str) \ 3592c8db5beSKalesh Singh static ssize_t _name##_show(struct kobject *kobj, \ 3602c8db5beSKalesh Singh struct kobj_attribute *attr, char *buf) \ 3612c8db5beSKalesh Singh { \ 362b52124a7SMario Limonciello return sprintf(buf, format_str, suspend_stats._name); \ 3632c8db5beSKalesh Singh } \ 3642c8db5beSKalesh Singh static struct kobj_attribute _name = __ATTR_RO(_name) 3652c8db5beSKalesh Singh 366b52124a7SMario Limonciello suspend_attr(success, "%d\n"); 367b52124a7SMario Limonciello suspend_attr(fail, "%d\n"); 368b52124a7SMario Limonciello suspend_attr(failed_freeze, "%d\n"); 369b52124a7SMario Limonciello suspend_attr(failed_prepare, "%d\n"); 370b52124a7SMario Limonciello suspend_attr(failed_suspend, "%d\n"); 371b52124a7SMario Limonciello suspend_attr(failed_suspend_late, "%d\n"); 372b52124a7SMario Limonciello suspend_attr(failed_suspend_noirq, "%d\n"); 373b52124a7SMario Limonciello suspend_attr(failed_resume, "%d\n"); 374b52124a7SMario Limonciello suspend_attr(failed_resume_early, "%d\n"); 375b52124a7SMario Limonciello suspend_attr(failed_resume_noirq, "%d\n"); 376b52124a7SMario Limonciello suspend_attr(last_hw_sleep, "%llu\n"); 377b52124a7SMario Limonciello suspend_attr(total_hw_sleep, "%llu\n"); 378b52124a7SMario Limonciello suspend_attr(max_hw_sleep, "%llu\n"); 3792c8db5beSKalesh Singh 3802c8db5beSKalesh Singh static ssize_t last_failed_dev_show(struct kobject *kobj, 3812c8db5beSKalesh Singh struct kobj_attribute *attr, char *buf) 3822c8db5beSKalesh Singh { 3832c8db5beSKalesh Singh int index; 3842c8db5beSKalesh Singh char *last_failed_dev = NULL; 3852c8db5beSKalesh Singh 3862c8db5beSKalesh Singh index = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1; 3872c8db5beSKalesh Singh index %= REC_FAILED_NUM; 3882c8db5beSKalesh Singh last_failed_dev = suspend_stats.failed_devs[index]; 3892c8db5beSKalesh Singh 3902c8db5beSKalesh Singh return sprintf(buf, "%s\n", last_failed_dev); 3912c8db5beSKalesh Singh } 3922c8db5beSKalesh Singh static struct kobj_attribute last_failed_dev = __ATTR_RO(last_failed_dev); 3932c8db5beSKalesh Singh 3942c8db5beSKalesh Singh static ssize_t last_failed_errno_show(struct kobject *kobj, 3952c8db5beSKalesh Singh struct kobj_attribute *attr, char *buf) 3962c8db5beSKalesh Singh { 3972c8db5beSKalesh Singh int index; 3982c8db5beSKalesh Singh int last_failed_errno; 3992c8db5beSKalesh Singh 4002c8db5beSKalesh Singh index = suspend_stats.last_failed_errno + REC_FAILED_NUM - 1; 4012c8db5beSKalesh Singh index %= REC_FAILED_NUM; 4022c8db5beSKalesh Singh last_failed_errno = suspend_stats.errno[index]; 4032c8db5beSKalesh Singh 4042c8db5beSKalesh Singh return sprintf(buf, "%d\n", last_failed_errno); 4052c8db5beSKalesh Singh } 4062c8db5beSKalesh Singh static struct kobj_attribute last_failed_errno = __ATTR_RO(last_failed_errno); 4072c8db5beSKalesh Singh 4082c8db5beSKalesh Singh static ssize_t last_failed_step_show(struct kobject *kobj, 4092c8db5beSKalesh Singh struct kobj_attribute *attr, char *buf) 4102c8db5beSKalesh Singh { 4112c8db5beSKalesh Singh int index; 4122c8db5beSKalesh Singh enum suspend_stat_step step; 4132c8db5beSKalesh Singh char *last_failed_step = NULL; 4142c8db5beSKalesh Singh 4152c8db5beSKalesh Singh index = suspend_stats.last_failed_step + REC_FAILED_NUM - 1; 4162c8db5beSKalesh Singh index %= REC_FAILED_NUM; 4172c8db5beSKalesh Singh step = suspend_stats.failed_steps[index]; 4182c8db5beSKalesh Singh last_failed_step = suspend_step_name(step); 4192c8db5beSKalesh Singh 4202c8db5beSKalesh Singh return sprintf(buf, "%s\n", last_failed_step); 4212c8db5beSKalesh Singh } 4222c8db5beSKalesh Singh static struct kobj_attribute last_failed_step = __ATTR_RO(last_failed_step); 4232c8db5beSKalesh Singh 4242c8db5beSKalesh Singh static struct attribute *suspend_attrs[] = { 4252c8db5beSKalesh Singh &success.attr, 4262c8db5beSKalesh Singh &fail.attr, 4272c8db5beSKalesh Singh &failed_freeze.attr, 4282c8db5beSKalesh Singh &failed_prepare.attr, 4292c8db5beSKalesh Singh &failed_suspend.attr, 4302c8db5beSKalesh Singh &failed_suspend_late.attr, 4312c8db5beSKalesh Singh &failed_suspend_noirq.attr, 4322c8db5beSKalesh Singh &failed_resume.attr, 4332c8db5beSKalesh Singh &failed_resume_early.attr, 4342c8db5beSKalesh Singh &failed_resume_noirq.attr, 4352c8db5beSKalesh Singh &last_failed_dev.attr, 4362c8db5beSKalesh Singh &last_failed_errno.attr, 4372c8db5beSKalesh Singh &last_failed_step.attr, 438b52124a7SMario Limonciello &last_hw_sleep.attr, 439b52124a7SMario Limonciello &total_hw_sleep.attr, 440b52124a7SMario Limonciello &max_hw_sleep.attr, 4412c8db5beSKalesh Singh NULL, 4422c8db5beSKalesh Singh }; 4432c8db5beSKalesh Singh 444b52124a7SMario Limonciello static umode_t suspend_attr_is_visible(struct kobject *kobj, struct attribute *attr, int idx) 445b52124a7SMario Limonciello { 446b52124a7SMario Limonciello if (attr != &last_hw_sleep.attr && 447b52124a7SMario Limonciello attr != &total_hw_sleep.attr && 448b52124a7SMario Limonciello attr != &max_hw_sleep.attr) 449b52124a7SMario Limonciello return 0444; 450b52124a7SMario Limonciello 451b52124a7SMario Limonciello #ifdef CONFIG_ACPI 452b52124a7SMario Limonciello if (acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0) 453b52124a7SMario Limonciello return 0444; 454b52124a7SMario Limonciello #endif 455b52124a7SMario Limonciello return 0; 456b52124a7SMario Limonciello } 457b52124a7SMario Limonciello 45815560574SRikard Falkeborn static const struct attribute_group suspend_attr_group = { 4592c8db5beSKalesh Singh .name = "suspend_stats", 4602c8db5beSKalesh Singh .attrs = suspend_attrs, 461b52124a7SMario Limonciello .is_visible = suspend_attr_is_visible, 4622c8db5beSKalesh Singh }; 4632c8db5beSKalesh Singh 4642c8db5beSKalesh Singh #ifdef CONFIG_DEBUG_FS 4652a77c46dSShuoX Liu static int suspend_stats_show(struct seq_file *s, void *unused) 4662a77c46dSShuoX Liu { 4672a77c46dSShuoX Liu int i, index, last_dev, last_errno, last_step; 4682a77c46dSShuoX Liu 4692a77c46dSShuoX Liu last_dev = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1; 4702a77c46dSShuoX Liu last_dev %= REC_FAILED_NUM; 4712a77c46dSShuoX Liu last_errno = suspend_stats.last_failed_errno + REC_FAILED_NUM - 1; 4722a77c46dSShuoX Liu last_errno %= REC_FAILED_NUM; 4732a77c46dSShuoX Liu last_step = suspend_stats.last_failed_step + REC_FAILED_NUM - 1; 4742a77c46dSShuoX Liu last_step %= REC_FAILED_NUM; 475cf579dfbSRafael J. Wysocki seq_printf(s, "%s: %d\n%s: %d\n%s: %d\n%s: %d\n%s: %d\n" 476cf579dfbSRafael J. Wysocki "%s: %d\n%s: %d\n%s: %d\n%s: %d\n%s: %d\n", 4772a77c46dSShuoX Liu "success", suspend_stats.success, 4782a77c46dSShuoX Liu "fail", suspend_stats.fail, 4792a77c46dSShuoX Liu "failed_freeze", suspend_stats.failed_freeze, 4802a77c46dSShuoX Liu "failed_prepare", suspend_stats.failed_prepare, 4812a77c46dSShuoX Liu "failed_suspend", suspend_stats.failed_suspend, 482cf579dfbSRafael J. Wysocki "failed_suspend_late", 483cf579dfbSRafael J. Wysocki suspend_stats.failed_suspend_late, 4842a77c46dSShuoX Liu "failed_suspend_noirq", 4852a77c46dSShuoX Liu suspend_stats.failed_suspend_noirq, 4862a77c46dSShuoX Liu "failed_resume", suspend_stats.failed_resume, 487cf579dfbSRafael J. Wysocki "failed_resume_early", 488cf579dfbSRafael J. Wysocki suspend_stats.failed_resume_early, 4892a77c46dSShuoX Liu "failed_resume_noirq", 4902a77c46dSShuoX Liu suspend_stats.failed_resume_noirq); 4912a77c46dSShuoX Liu seq_printf(s, "failures:\n last_failed_dev:\t%-s\n", 4922a77c46dSShuoX Liu suspend_stats.failed_devs[last_dev]); 4932a77c46dSShuoX Liu for (i = 1; i < REC_FAILED_NUM; i++) { 4942a77c46dSShuoX Liu index = last_dev + REC_FAILED_NUM - i; 4952a77c46dSShuoX Liu index %= REC_FAILED_NUM; 4962a77c46dSShuoX Liu seq_printf(s, "\t\t\t%-s\n", 4972a77c46dSShuoX Liu suspend_stats.failed_devs[index]); 4982a77c46dSShuoX Liu } 4992a77c46dSShuoX Liu seq_printf(s, " last_failed_errno:\t%-d\n", 5002a77c46dSShuoX Liu suspend_stats.errno[last_errno]); 5012a77c46dSShuoX Liu for (i = 1; i < REC_FAILED_NUM; i++) { 5022a77c46dSShuoX Liu index = last_errno + REC_FAILED_NUM - i; 5032a77c46dSShuoX Liu index %= REC_FAILED_NUM; 5042a77c46dSShuoX Liu seq_printf(s, "\t\t\t%-d\n", 5052a77c46dSShuoX Liu suspend_stats.errno[index]); 5062a77c46dSShuoX Liu } 5072a77c46dSShuoX Liu seq_printf(s, " last_failed_step:\t%-s\n", 5082a77c46dSShuoX Liu suspend_step_name( 5092a77c46dSShuoX Liu suspend_stats.failed_steps[last_step])); 5102a77c46dSShuoX Liu for (i = 1; i < REC_FAILED_NUM; i++) { 5112a77c46dSShuoX Liu index = last_step + REC_FAILED_NUM - i; 5122a77c46dSShuoX Liu index %= REC_FAILED_NUM; 5132a77c46dSShuoX Liu seq_printf(s, "\t\t\t%-s\n", 5142a77c46dSShuoX Liu suspend_step_name( 5152a77c46dSShuoX Liu suspend_stats.failed_steps[index])); 5162a77c46dSShuoX Liu } 5172a77c46dSShuoX Liu 5182a77c46dSShuoX Liu return 0; 5192a77c46dSShuoX Liu } 520943a10f8SYangtao Li DEFINE_SHOW_ATTRIBUTE(suspend_stats); 5212a77c46dSShuoX Liu 5222a77c46dSShuoX Liu static int __init pm_debugfs_init(void) 5232a77c46dSShuoX Liu { 5242a77c46dSShuoX Liu debugfs_create_file("suspend_stats", S_IFREG | S_IRUGO, 525943a10f8SYangtao Li NULL, NULL, &suspend_stats_fops); 5262a77c46dSShuoX Liu return 0; 5272a77c46dSShuoX Liu } 5282a77c46dSShuoX Liu 5292a77c46dSShuoX Liu late_initcall(pm_debugfs_init); 5302a77c46dSShuoX Liu #endif /* CONFIG_DEBUG_FS */ 5312a77c46dSShuoX Liu 532ca123102SRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */ 533ca123102SRafael J. Wysocki 534b2df1d4fSRafael J. Wysocki #ifdef CONFIG_PM_SLEEP_DEBUG 535b2df1d4fSRafael J. Wysocki /* 536b2df1d4fSRafael J. Wysocki * pm_print_times: print time taken by devices to suspend and resume. 537b2df1d4fSRafael J. Wysocki * 538b2df1d4fSRafael J. Wysocki * show() returns whether printing of suspend and resume times is enabled. 539b2df1d4fSRafael J. Wysocki * store() accepts 0 or 1. 0 disables printing and 1 enables it. 540b2df1d4fSRafael J. Wysocki */ 541b2df1d4fSRafael J. Wysocki bool pm_print_times_enabled; 542b2df1d4fSRafael J. Wysocki 543b2df1d4fSRafael J. Wysocki static ssize_t pm_print_times_show(struct kobject *kobj, 544b2df1d4fSRafael J. Wysocki struct kobj_attribute *attr, char *buf) 545b2df1d4fSRafael J. Wysocki { 546b2df1d4fSRafael J. Wysocki return sprintf(buf, "%d\n", pm_print_times_enabled); 547b2df1d4fSRafael J. Wysocki } 548b2df1d4fSRafael J. Wysocki 549b2df1d4fSRafael J. Wysocki static ssize_t pm_print_times_store(struct kobject *kobj, 550b2df1d4fSRafael J. Wysocki struct kobj_attribute *attr, 551b2df1d4fSRafael J. Wysocki const char *buf, size_t n) 552b2df1d4fSRafael J. Wysocki { 553b2df1d4fSRafael J. Wysocki unsigned long val; 554b2df1d4fSRafael J. Wysocki 555b2df1d4fSRafael J. Wysocki if (kstrtoul(buf, 10, &val)) 556b2df1d4fSRafael J. Wysocki return -EINVAL; 557b2df1d4fSRafael J. Wysocki 558b2df1d4fSRafael J. Wysocki if (val > 1) 559b2df1d4fSRafael J. Wysocki return -EINVAL; 560b2df1d4fSRafael J. Wysocki 561b2df1d4fSRafael J. Wysocki pm_print_times_enabled = !!val; 562b2df1d4fSRafael J. Wysocki return n; 563b2df1d4fSRafael J. Wysocki } 564b2df1d4fSRafael J. Wysocki 565b2df1d4fSRafael J. Wysocki power_attr(pm_print_times); 566b2df1d4fSRafael J. Wysocki 567b2df1d4fSRafael J. Wysocki static inline void pm_print_times_init(void) 568b2df1d4fSRafael J. Wysocki { 569b2df1d4fSRafael J. Wysocki pm_print_times_enabled = !!initcall_debug; 570b2df1d4fSRafael J. Wysocki } 571a6f5f0ddSAlexandra Yates 572a6f5f0ddSAlexandra Yates static ssize_t pm_wakeup_irq_show(struct kobject *kobj, 573a6f5f0ddSAlexandra Yates struct kobj_attribute *attr, 574a6f5f0ddSAlexandra Yates char *buf) 575a6f5f0ddSAlexandra Yates { 576cb1f65c1SRafael J. Wysocki if (!pm_wakeup_irq()) 577cb1f65c1SRafael J. Wysocki return -ENODATA; 578cb1f65c1SRafael J. Wysocki 579cb1f65c1SRafael J. Wysocki return sprintf(buf, "%u\n", pm_wakeup_irq()); 580a6f5f0ddSAlexandra Yates } 581a6f5f0ddSAlexandra Yates 582a1e9ca69SRafael J. Wysocki power_attr_ro(pm_wakeup_irq); 583a6f5f0ddSAlexandra Yates 584726fb6b4SSrinivas Pandruvada bool pm_debug_messages_on __read_mostly; 5858d8b2441SRafael J. Wysocki 5868d8b2441SRafael J. Wysocki static ssize_t pm_debug_messages_show(struct kobject *kobj, 5878d8b2441SRafael J. Wysocki struct kobj_attribute *attr, char *buf) 5888d8b2441SRafael J. Wysocki { 5898d8b2441SRafael J. Wysocki return sprintf(buf, "%d\n", pm_debug_messages_on); 5908d8b2441SRafael J. Wysocki } 5918d8b2441SRafael J. Wysocki 5928d8b2441SRafael J. Wysocki static ssize_t pm_debug_messages_store(struct kobject *kobj, 5938d8b2441SRafael J. Wysocki struct kobj_attribute *attr, 5948d8b2441SRafael J. Wysocki const char *buf, size_t n) 5958d8b2441SRafael J. Wysocki { 5968d8b2441SRafael J. Wysocki unsigned long val; 5978d8b2441SRafael J. Wysocki 5988d8b2441SRafael J. Wysocki if (kstrtoul(buf, 10, &val)) 5998d8b2441SRafael J. Wysocki return -EINVAL; 6008d8b2441SRafael J. Wysocki 6018d8b2441SRafael J. Wysocki if (val > 1) 6028d8b2441SRafael J. Wysocki return -EINVAL; 6038d8b2441SRafael J. Wysocki 6048d8b2441SRafael J. Wysocki pm_debug_messages_on = !!val; 6058d8b2441SRafael J. Wysocki return n; 6068d8b2441SRafael J. Wysocki } 6078d8b2441SRafael J. Wysocki 6088d8b2441SRafael J. Wysocki power_attr(pm_debug_messages); 6098d8b2441SRafael J. Wysocki 610db96a759SChen Yu static int __init pm_debug_messages_setup(char *str) 611db96a759SChen Yu { 612db96a759SChen Yu pm_debug_messages_on = true; 613db96a759SChen Yu return 1; 614db96a759SChen Yu } 615db96a759SChen Yu __setup("pm_debug_messages", pm_debug_messages_setup); 616db96a759SChen Yu 617819b1bb3SRafael J. Wysocki #else /* !CONFIG_PM_SLEEP_DEBUG */ 618b2df1d4fSRafael J. Wysocki static inline void pm_print_times_init(void) {} 619b2df1d4fSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP_DEBUG */ 620b2df1d4fSRafael J. Wysocki 621d76e15fbSGreg Kroah-Hartman struct kobject *power_kobj; 6221da177e4SLinus Torvalds 623dbcfa715SRandy Dunlap /* 6240399d4dbSRafael J. Wysocki * state - control system sleep states. 6251da177e4SLinus Torvalds * 6260399d4dbSRafael J. Wysocki * show() returns available sleep state labels, which may be "mem", "standby", 62744348e8aSMauro Carvalho Chehab * "freeze" and "disk" (hibernation). 62844348e8aSMauro Carvalho Chehab * See Documentation/admin-guide/pm/sleep-states.rst for a description of 62944348e8aSMauro Carvalho Chehab * what they mean. 6301da177e4SLinus Torvalds * 6310399d4dbSRafael J. Wysocki * store() accepts one of those strings, translates it into the proper 6320399d4dbSRafael J. Wysocki * enumerated value, and initiates a suspend transition. 6331da177e4SLinus Torvalds */ 634386f275fSKay Sievers static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr, 635386f275fSKay Sievers char *buf) 6361da177e4SLinus Torvalds { 6371da177e4SLinus Torvalds char *s = buf; 638296699deSRafael J. Wysocki #ifdef CONFIG_SUSPEND 63927ddcc65SRafael J. Wysocki suspend_state_t i; 6401da177e4SLinus Torvalds 64127ddcc65SRafael J. Wysocki for (i = PM_SUSPEND_MIN; i < PM_SUSPEND_MAX; i++) 642d431cbc5SRafael J. Wysocki if (pm_states[i]) 643d431cbc5SRafael J. Wysocki s += sprintf(s,"%s ", pm_states[i]); 64427ddcc65SRafael J. Wysocki 645296699deSRafael J. Wysocki #endif 646a6e15a39SKees Cook if (hibernation_available()) 647a6e15a39SKees Cook s += sprintf(s, "disk "); 648a3d25c27SRafael J. Wysocki if (s != buf) 649a3d25c27SRafael J. Wysocki /* convert the last space to a newline */ 650a3d25c27SRafael J. Wysocki *(s-1) = '\n'; 6511da177e4SLinus Torvalds return (s - buf); 6521da177e4SLinus Torvalds } 6531da177e4SLinus Torvalds 6547483b4a4SRafael J. Wysocki static suspend_state_t decode_state(const char *buf, size_t n) 6551da177e4SLinus Torvalds { 656296699deSRafael J. Wysocki #ifdef CONFIG_SUSPEND 657d431cbc5SRafael J. Wysocki suspend_state_t state; 658296699deSRafael J. Wysocki #endif 6591da177e4SLinus Torvalds char *p; 6601da177e4SLinus Torvalds int len; 6611da177e4SLinus Torvalds 6621da177e4SLinus Torvalds p = memchr(buf, '\n', n); 6631da177e4SLinus Torvalds len = p ? p - buf : n; 6641da177e4SLinus Torvalds 6657483b4a4SRafael J. Wysocki /* Check hibernation first. */ 666d30bdfc0SChuhong Yuan if (len == 4 && str_has_prefix(buf, "disk")) 6677483b4a4SRafael J. Wysocki return PM_SUSPEND_MAX; 668a3d25c27SRafael J. Wysocki 669296699deSRafael J. Wysocki #ifdef CONFIG_SUSPEND 670d431cbc5SRafael J. Wysocki for (state = PM_SUSPEND_MIN; state < PM_SUSPEND_MAX; state++) { 671d431cbc5SRafael J. Wysocki const char *label = pm_states[state]; 672d431cbc5SRafael J. Wysocki 673d431cbc5SRafael J. Wysocki if (label && len == strlen(label) && !strncmp(buf, label, len)) 674d431cbc5SRafael J. Wysocki return state; 675d431cbc5SRafael J. Wysocki } 676296699deSRafael J. Wysocki #endif 677296699deSRafael J. Wysocki 6787483b4a4SRafael J. Wysocki return PM_SUSPEND_ON; 6797483b4a4SRafael J. Wysocki } 6807483b4a4SRafael J. Wysocki 6817483b4a4SRafael J. Wysocki static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr, 6827483b4a4SRafael J. Wysocki const char *buf, size_t n) 6837483b4a4SRafael J. Wysocki { 6847483b4a4SRafael J. Wysocki suspend_state_t state; 6857483b4a4SRafael J. Wysocki int error; 6867483b4a4SRafael J. Wysocki 6877483b4a4SRafael J. Wysocki error = pm_autosleep_lock(); 6887483b4a4SRafael J. Wysocki if (error) 6897483b4a4SRafael J. Wysocki return error; 6907483b4a4SRafael J. Wysocki 6917483b4a4SRafael J. Wysocki if (pm_autosleep_state() > PM_SUSPEND_ON) { 6927483b4a4SRafael J. Wysocki error = -EBUSY; 6937483b4a4SRafael J. Wysocki goto out; 6947483b4a4SRafael J. Wysocki } 6957483b4a4SRafael J. Wysocki 6967483b4a4SRafael J. Wysocki state = decode_state(buf, n); 697406e7938SRafael J. Wysocki if (state < PM_SUSPEND_MAX) { 698406e7938SRafael J. Wysocki if (state == PM_SUSPEND_MEM) 699406e7938SRafael J. Wysocki state = mem_sleep_current; 700406e7938SRafael J. Wysocki 7017483b4a4SRafael J. Wysocki error = pm_suspend(state); 702406e7938SRafael J. Wysocki } else if (state == PM_SUSPEND_MAX) { 7037483b4a4SRafael J. Wysocki error = hibernate(); 704406e7938SRafael J. Wysocki } else { 7057483b4a4SRafael J. Wysocki error = -EINVAL; 706406e7938SRafael J. Wysocki } 7077483b4a4SRafael J. Wysocki 7087483b4a4SRafael J. Wysocki out: 7097483b4a4SRafael J. Wysocki pm_autosleep_unlock(); 7101da177e4SLinus Torvalds return error ? error : n; 7111da177e4SLinus Torvalds } 7121da177e4SLinus Torvalds 7131da177e4SLinus Torvalds power_attr(state); 7141da177e4SLinus Torvalds 715c125e96fSRafael J. Wysocki #ifdef CONFIG_PM_SLEEP 716c125e96fSRafael J. Wysocki /* 717c125e96fSRafael J. Wysocki * The 'wakeup_count' attribute, along with the functions defined in 718c125e96fSRafael J. Wysocki * drivers/base/power/wakeup.c, provides a means by which wakeup events can be 719c125e96fSRafael J. Wysocki * handled in a non-racy way. 720c125e96fSRafael J. Wysocki * 721c125e96fSRafael J. Wysocki * If a wakeup event occurs when the system is in a sleep state, it simply is 722c125e96fSRafael J. Wysocki * woken up. In turn, if an event that would wake the system up from a sleep 723c125e96fSRafael J. Wysocki * state occurs when it is undergoing a transition to that sleep state, the 724c125e96fSRafael J. Wysocki * transition should be aborted. Moreover, if such an event occurs when the 725c125e96fSRafael J. Wysocki * system is in the working state, an attempt to start a transition to the 726c125e96fSRafael J. Wysocki * given sleep state should fail during certain period after the detection of 727c125e96fSRafael J. Wysocki * the event. Using the 'state' attribute alone is not sufficient to satisfy 728c125e96fSRafael J. Wysocki * these requirements, because a wakeup event may occur exactly when 'state' 729c125e96fSRafael J. Wysocki * is being written to and may be delivered to user space right before it is 730c125e96fSRafael J. Wysocki * frozen, so the event will remain only partially processed until the system is 731c125e96fSRafael J. Wysocki * woken up by another event. In particular, it won't cause the transition to 732c125e96fSRafael J. Wysocki * a sleep state to be aborted. 733c125e96fSRafael J. Wysocki * 734c125e96fSRafael J. Wysocki * This difficulty may be overcome if user space uses 'wakeup_count' before 735c125e96fSRafael J. Wysocki * writing to 'state'. It first should read from 'wakeup_count' and store 736c125e96fSRafael J. Wysocki * the read value. Then, after carrying out its own preparations for the system 737c125e96fSRafael J. Wysocki * transition to a sleep state, it should write the stored value to 73825985edcSLucas De Marchi * 'wakeup_count'. If that fails, at least one wakeup event has occurred since 739c125e96fSRafael J. Wysocki * 'wakeup_count' was read and 'state' should not be written to. Otherwise, it 740c125e96fSRafael J. Wysocki * is allowed to write to 'state', but the transition will be aborted if there 741c125e96fSRafael J. Wysocki * are any wakeup events detected after 'wakeup_count' was written to. 742c125e96fSRafael J. Wysocki */ 743c125e96fSRafael J. Wysocki 744c125e96fSRafael J. Wysocki static ssize_t wakeup_count_show(struct kobject *kobj, 745c125e96fSRafael J. Wysocki struct kobj_attribute *attr, 746c125e96fSRafael J. Wysocki char *buf) 747c125e96fSRafael J. Wysocki { 748074037ecSRafael J. Wysocki unsigned int val; 749c125e96fSRafael J. Wysocki 7507483b4a4SRafael J. Wysocki return pm_get_wakeup_count(&val, true) ? 7517483b4a4SRafael J. Wysocki sprintf(buf, "%u\n", val) : -EINTR; 752c125e96fSRafael J. Wysocki } 753c125e96fSRafael J. Wysocki 754c125e96fSRafael J. Wysocki static ssize_t wakeup_count_store(struct kobject *kobj, 755c125e96fSRafael J. Wysocki struct kobj_attribute *attr, 756c125e96fSRafael J. Wysocki const char *buf, size_t n) 757c125e96fSRafael J. Wysocki { 758074037ecSRafael J. Wysocki unsigned int val; 7597483b4a4SRafael J. Wysocki int error; 760c125e96fSRafael J. Wysocki 7617483b4a4SRafael J. Wysocki error = pm_autosleep_lock(); 7627483b4a4SRafael J. Wysocki if (error) 7637483b4a4SRafael J. Wysocki return error; 7647483b4a4SRafael J. Wysocki 7657483b4a4SRafael J. Wysocki if (pm_autosleep_state() > PM_SUSPEND_ON) { 7667483b4a4SRafael J. Wysocki error = -EBUSY; 7677483b4a4SRafael J. Wysocki goto out; 7687483b4a4SRafael J. Wysocki } 7697483b4a4SRafael J. Wysocki 7707483b4a4SRafael J. Wysocki error = -EINVAL; 771074037ecSRafael J. Wysocki if (sscanf(buf, "%u", &val) == 1) { 772c125e96fSRafael J. Wysocki if (pm_save_wakeup_count(val)) 7737483b4a4SRafael J. Wysocki error = n; 774bb177fedSJulius Werner else 775bb177fedSJulius Werner pm_print_active_wakeup_sources(); 776c125e96fSRafael J. Wysocki } 7777483b4a4SRafael J. Wysocki 7787483b4a4SRafael J. Wysocki out: 7797483b4a4SRafael J. Wysocki pm_autosleep_unlock(); 7807483b4a4SRafael J. Wysocki return error; 781c125e96fSRafael J. Wysocki } 782c125e96fSRafael J. Wysocki 783c125e96fSRafael J. Wysocki power_attr(wakeup_count); 7847483b4a4SRafael J. Wysocki 7857483b4a4SRafael J. Wysocki #ifdef CONFIG_PM_AUTOSLEEP 7867483b4a4SRafael J. Wysocki static ssize_t autosleep_show(struct kobject *kobj, 7877483b4a4SRafael J. Wysocki struct kobj_attribute *attr, 7887483b4a4SRafael J. Wysocki char *buf) 7897483b4a4SRafael J. Wysocki { 7907483b4a4SRafael J. Wysocki suspend_state_t state = pm_autosleep_state(); 7917483b4a4SRafael J. Wysocki 7927483b4a4SRafael J. Wysocki if (state == PM_SUSPEND_ON) 7937483b4a4SRafael J. Wysocki return sprintf(buf, "off\n"); 7947483b4a4SRafael J. Wysocki 7957483b4a4SRafael J. Wysocki #ifdef CONFIG_SUSPEND 7967483b4a4SRafael J. Wysocki if (state < PM_SUSPEND_MAX) 797d431cbc5SRafael J. Wysocki return sprintf(buf, "%s\n", pm_states[state] ? 798d431cbc5SRafael J. Wysocki pm_states[state] : "error"); 7997483b4a4SRafael J. Wysocki #endif 8007483b4a4SRafael J. Wysocki #ifdef CONFIG_HIBERNATION 8017483b4a4SRafael J. Wysocki return sprintf(buf, "disk\n"); 8027483b4a4SRafael J. Wysocki #else 8037483b4a4SRafael J. Wysocki return sprintf(buf, "error"); 8047483b4a4SRafael J. Wysocki #endif 8057483b4a4SRafael J. Wysocki } 8067483b4a4SRafael J. Wysocki 8077483b4a4SRafael J. Wysocki static ssize_t autosleep_store(struct kobject *kobj, 8087483b4a4SRafael J. Wysocki struct kobj_attribute *attr, 8097483b4a4SRafael J. Wysocki const char *buf, size_t n) 8107483b4a4SRafael J. Wysocki { 8117483b4a4SRafael J. Wysocki suspend_state_t state = decode_state(buf, n); 8127483b4a4SRafael J. Wysocki int error; 8137483b4a4SRafael J. Wysocki 8147483b4a4SRafael J. Wysocki if (state == PM_SUSPEND_ON 815040e5bf6SArve Hjønnevåg && strcmp(buf, "off") && strcmp(buf, "off\n")) 8167483b4a4SRafael J. Wysocki return -EINVAL; 8177483b4a4SRafael J. Wysocki 818406e7938SRafael J. Wysocki if (state == PM_SUSPEND_MEM) 819406e7938SRafael J. Wysocki state = mem_sleep_current; 820406e7938SRafael J. Wysocki 8217483b4a4SRafael J. Wysocki error = pm_autosleep_set_state(state); 8227483b4a4SRafael J. Wysocki return error ? error : n; 8237483b4a4SRafael J. Wysocki } 8247483b4a4SRafael J. Wysocki 8257483b4a4SRafael J. Wysocki power_attr(autosleep); 8267483b4a4SRafael J. Wysocki #endif /* CONFIG_PM_AUTOSLEEP */ 827b86ff982SRafael J. Wysocki 828b86ff982SRafael J. Wysocki #ifdef CONFIG_PM_WAKELOCKS 829b86ff982SRafael J. Wysocki static ssize_t wake_lock_show(struct kobject *kobj, 830b86ff982SRafael J. Wysocki struct kobj_attribute *attr, 831b86ff982SRafael J. Wysocki char *buf) 832b86ff982SRafael J. Wysocki { 833b86ff982SRafael J. Wysocki return pm_show_wakelocks(buf, true); 834b86ff982SRafael J. Wysocki } 835b86ff982SRafael J. Wysocki 836b86ff982SRafael J. Wysocki static ssize_t wake_lock_store(struct kobject *kobj, 837b86ff982SRafael J. Wysocki struct kobj_attribute *attr, 838b86ff982SRafael J. Wysocki const char *buf, size_t n) 839b86ff982SRafael J. Wysocki { 840b86ff982SRafael J. Wysocki int error = pm_wake_lock(buf); 841b86ff982SRafael J. Wysocki return error ? error : n; 842b86ff982SRafael J. Wysocki } 843b86ff982SRafael J. Wysocki 844b86ff982SRafael J. Wysocki power_attr(wake_lock); 845b86ff982SRafael J. Wysocki 846b86ff982SRafael J. Wysocki static ssize_t wake_unlock_show(struct kobject *kobj, 847b86ff982SRafael J. Wysocki struct kobj_attribute *attr, 848b86ff982SRafael J. Wysocki char *buf) 849b86ff982SRafael J. Wysocki { 850b86ff982SRafael J. Wysocki return pm_show_wakelocks(buf, false); 851b86ff982SRafael J. Wysocki } 852b86ff982SRafael J. Wysocki 853b86ff982SRafael J. Wysocki static ssize_t wake_unlock_store(struct kobject *kobj, 854b86ff982SRafael J. Wysocki struct kobj_attribute *attr, 855b86ff982SRafael J. Wysocki const char *buf, size_t n) 856b86ff982SRafael J. Wysocki { 857b86ff982SRafael J. Wysocki int error = pm_wake_unlock(buf); 858b86ff982SRafael J. Wysocki return error ? error : n; 859b86ff982SRafael J. Wysocki } 860b86ff982SRafael J. Wysocki 861b86ff982SRafael J. Wysocki power_attr(wake_unlock); 862b86ff982SRafael J. Wysocki 863b86ff982SRafael J. Wysocki #endif /* CONFIG_PM_WAKELOCKS */ 864c125e96fSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */ 865c125e96fSRafael J. Wysocki 866c5c6ba4eSRafael J. Wysocki #ifdef CONFIG_PM_TRACE 867c5c6ba4eSRafael J. Wysocki int pm_trace_enabled; 868c5c6ba4eSRafael J. Wysocki 869386f275fSKay Sievers static ssize_t pm_trace_show(struct kobject *kobj, struct kobj_attribute *attr, 870386f275fSKay Sievers char *buf) 871c5c6ba4eSRafael J. Wysocki { 872c5c6ba4eSRafael J. Wysocki return sprintf(buf, "%d\n", pm_trace_enabled); 873c5c6ba4eSRafael J. Wysocki } 874c5c6ba4eSRafael J. Wysocki 875c5c6ba4eSRafael J. Wysocki static ssize_t 876386f275fSKay Sievers pm_trace_store(struct kobject *kobj, struct kobj_attribute *attr, 877386f275fSKay Sievers const char *buf, size_t n) 878c5c6ba4eSRafael J. Wysocki { 879c5c6ba4eSRafael J. Wysocki int val; 880c5c6ba4eSRafael J. Wysocki 881c5c6ba4eSRafael J. Wysocki if (sscanf(buf, "%d", &val) == 1) { 882c5c6ba4eSRafael J. Wysocki pm_trace_enabled = !!val; 8839dceefe4SShuah Khan if (pm_trace_enabled) { 8849dceefe4SShuah Khan pr_warn("PM: Enabling pm_trace changes system date and time during resume.\n" 8859dceefe4SShuah Khan "PM: Correct system time has to be restored manually after resume.\n"); 8869dceefe4SShuah Khan } 887c5c6ba4eSRafael J. Wysocki return n; 888c5c6ba4eSRafael J. Wysocki } 889c5c6ba4eSRafael J. Wysocki return -EINVAL; 890c5c6ba4eSRafael J. Wysocki } 891c5c6ba4eSRafael J. Wysocki 892c5c6ba4eSRafael J. Wysocki power_attr(pm_trace); 893d33ac60bSJames Hogan 894d33ac60bSJames Hogan static ssize_t pm_trace_dev_match_show(struct kobject *kobj, 895d33ac60bSJames Hogan struct kobj_attribute *attr, 896d33ac60bSJames Hogan char *buf) 897d33ac60bSJames Hogan { 898d33ac60bSJames Hogan return show_trace_dev_match(buf, PAGE_SIZE); 899d33ac60bSJames Hogan } 900d33ac60bSJames Hogan 901a1e9ca69SRafael J. Wysocki power_attr_ro(pm_trace_dev_match); 902d33ac60bSJames Hogan 9030e7d56e3SRafael J. Wysocki #endif /* CONFIG_PM_TRACE */ 904c5c6ba4eSRafael J. Wysocki 905957d1282SLi Fei #ifdef CONFIG_FREEZER 906957d1282SLi Fei static ssize_t pm_freeze_timeout_show(struct kobject *kobj, 907957d1282SLi Fei struct kobj_attribute *attr, char *buf) 908957d1282SLi Fei { 909957d1282SLi Fei return sprintf(buf, "%u\n", freeze_timeout_msecs); 910957d1282SLi Fei } 911957d1282SLi Fei 912957d1282SLi Fei static ssize_t pm_freeze_timeout_store(struct kobject *kobj, 913957d1282SLi Fei struct kobj_attribute *attr, 914957d1282SLi Fei const char *buf, size_t n) 915957d1282SLi Fei { 916957d1282SLi Fei unsigned long val; 917957d1282SLi Fei 918957d1282SLi Fei if (kstrtoul(buf, 10, &val)) 919957d1282SLi Fei return -EINVAL; 920957d1282SLi Fei 921957d1282SLi Fei freeze_timeout_msecs = val; 922957d1282SLi Fei return n; 923957d1282SLi Fei } 924957d1282SLi Fei 925957d1282SLi Fei power_attr(pm_freeze_timeout); 926957d1282SLi Fei 927957d1282SLi Fei #endif /* CONFIG_FREEZER*/ 928957d1282SLi Fei 929c5c6ba4eSRafael J. Wysocki static struct attribute * g[] = { 930c5c6ba4eSRafael J. Wysocki &state_attr.attr, 9310e7d56e3SRafael J. Wysocki #ifdef CONFIG_PM_TRACE 932c5c6ba4eSRafael J. Wysocki &pm_trace_attr.attr, 933d33ac60bSJames Hogan &pm_trace_dev_match_attr.attr, 9340e7d56e3SRafael J. Wysocki #endif 9350e06b4a8SRafael J. Wysocki #ifdef CONFIG_PM_SLEEP 9360e06b4a8SRafael J. Wysocki &pm_async_attr.attr, 937c125e96fSRafael J. Wysocki &wakeup_count_attr.attr, 938406e7938SRafael J. Wysocki #ifdef CONFIG_SUSPEND 939406e7938SRafael J. Wysocki &mem_sleep_attr.attr, 940c052bf82SJonas Meurer &sync_on_suspend_attr.attr, 941406e7938SRafael J. Wysocki #endif 9427483b4a4SRafael J. Wysocki #ifdef CONFIG_PM_AUTOSLEEP 9437483b4a4SRafael J. Wysocki &autosleep_attr.attr, 9447483b4a4SRafael J. Wysocki #endif 945b86ff982SRafael J. Wysocki #ifdef CONFIG_PM_WAKELOCKS 946b86ff982SRafael J. Wysocki &wake_lock_attr.attr, 947b86ff982SRafael J. Wysocki &wake_unlock_attr.attr, 948b86ff982SRafael J. Wysocki #endif 949b2df1d4fSRafael J. Wysocki #ifdef CONFIG_PM_SLEEP_DEBUG 950e516a1dbSRafael J. Wysocki &pm_test_attr.attr, 9514b7760baSSameer Nanda &pm_print_times_attr.attr, 952a6f5f0ddSAlexandra Yates &pm_wakeup_irq_attr.attr, 9538d8b2441SRafael J. Wysocki &pm_debug_messages_attr.attr, 9540e7d56e3SRafael J. Wysocki #endif 9550e06b4a8SRafael J. Wysocki #endif 956957d1282SLi Fei #ifdef CONFIG_FREEZER 957957d1282SLi Fei &pm_freeze_timeout_attr.attr, 958957d1282SLi Fei #endif 959c5c6ba4eSRafael J. Wysocki NULL, 960c5c6ba4eSRafael J. Wysocki }; 9611da177e4SLinus Torvalds 9621d0c6e59SArvind Yadav static const struct attribute_group attr_group = { 9631da177e4SLinus Torvalds .attrs = g, 9641da177e4SLinus Torvalds }; 9651da177e4SLinus Torvalds 9662c8db5beSKalesh Singh static const struct attribute_group *attr_groups[] = { 9672c8db5beSKalesh Singh &attr_group, 9682c8db5beSKalesh Singh #ifdef CONFIG_PM_SLEEP 9692c8db5beSKalesh Singh &suspend_attr_group, 9702c8db5beSKalesh Singh #endif 9712c8db5beSKalesh Singh NULL, 9722c8db5beSKalesh Singh }; 9732c8db5beSKalesh Singh 9745e928f77SRafael J. Wysocki struct workqueue_struct *pm_wq; 9757b199ca2SAlan Stern EXPORT_SYMBOL_GPL(pm_wq); 9765e928f77SRafael J. Wysocki 9775e928f77SRafael J. Wysocki static int __init pm_start_workqueue(void) 9785e928f77SRafael J. Wysocki { 97958a69cb4STejun Heo pm_wq = alloc_workqueue("pm", WQ_FREEZABLE, 0); 9805e928f77SRafael J. Wysocki 9815e928f77SRafael J. Wysocki return pm_wq ? 0 : -ENOMEM; 9825e928f77SRafael J. Wysocki } 9835e928f77SRafael J. Wysocki 9841da177e4SLinus Torvalds static int __init pm_init(void) 9851da177e4SLinus Torvalds { 9865e928f77SRafael J. Wysocki int error = pm_start_workqueue(); 9875e928f77SRafael J. Wysocki if (error) 9885e928f77SRafael J. Wysocki return error; 989ac5c24ecSRafael J. Wysocki hibernate_image_size_init(); 990ddeb6487SRafael J. Wysocki hibernate_reserved_size_init(); 991fa7fd6faSSudeep Holla pm_states_init(); 992d76e15fbSGreg Kroah-Hartman power_kobj = kobject_create_and_add("power", NULL); 993d76e15fbSGreg Kroah-Hartman if (!power_kobj) 994039a5dcdSGreg Kroah-Hartman return -ENOMEM; 9952c8db5beSKalesh Singh error = sysfs_create_groups(power_kobj, attr_groups); 9967483b4a4SRafael J. Wysocki if (error) 9977483b4a4SRafael J. Wysocki return error; 998b2df1d4fSRafael J. Wysocki pm_print_times_init(); 9997483b4a4SRafael J. Wysocki return pm_autosleep_init(); 10001da177e4SLinus Torvalds } 10011da177e4SLinus Torvalds 10021da177e4SLinus Torvalds core_initcall(pm_init); 1003