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
2407f44ac3SKefeng Wang /*
2507f44ac3SKefeng Wang * The following functions are used by the suspend/hibernate code to temporarily
2607f44ac3SKefeng Wang * change gfp_allowed_mask in order to avoid using I/O during memory allocations
2707f44ac3SKefeng Wang * while devices are suspended. To avoid races with the suspend/hibernate code,
2807f44ac3SKefeng Wang * they should always be called with system_transition_mutex held
2907f44ac3SKefeng Wang * (gfp_allowed_mask also should only be modified with system_transition_mutex
3007f44ac3SKefeng Wang * held, unless the suspend/hibernate code is guaranteed not to run in parallel
3107f44ac3SKefeng Wang * with that modification).
3207f44ac3SKefeng Wang */
3307f44ac3SKefeng Wang static gfp_t saved_gfp_mask;
3407f44ac3SKefeng Wang
pm_restore_gfp_mask(void)3507f44ac3SKefeng Wang void pm_restore_gfp_mask(void)
3607f44ac3SKefeng Wang {
3707f44ac3SKefeng Wang WARN_ON(!mutex_is_locked(&system_transition_mutex));
3807f44ac3SKefeng Wang if (saved_gfp_mask) {
3907f44ac3SKefeng Wang gfp_allowed_mask = saved_gfp_mask;
4007f44ac3SKefeng Wang saved_gfp_mask = 0;
4107f44ac3SKefeng Wang }
4207f44ac3SKefeng Wang }
4307f44ac3SKefeng Wang
pm_restrict_gfp_mask(void)4407f44ac3SKefeng Wang void pm_restrict_gfp_mask(void)
4507f44ac3SKefeng Wang {
4607f44ac3SKefeng Wang WARN_ON(!mutex_is_locked(&system_transition_mutex));
4707f44ac3SKefeng Wang WARN_ON(saved_gfp_mask);
4807f44ac3SKefeng Wang saved_gfp_mask = gfp_allowed_mask;
4907f44ac3SKefeng Wang gfp_allowed_mask &= ~(__GFP_IO | __GFP_FS);
5007f44ac3SKefeng Wang }
51cd51e61cSRafael J. Wysocki
lock_system_sleep(void)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
unlock_system_sleep(unsigned int flags)615950e5d5SPeter Zijlstra void unlock_system_sleep(unsigned int flags)
624bf236a3SBart Van Assche {
63f5d39b02SPeter Zijlstra if (!(flags & PF_NOFREEZE))
64f5d39b02SPeter Zijlstra current->flags &= ~PF_NOFREEZE;
6555f2503cSPingfan Liu mutex_unlock(&system_transition_mutex);
664bf236a3SBart Van Assche }
674bf236a3SBart Van Assche EXPORT_SYMBOL_GPL(unlock_system_sleep);
684bf236a3SBart Van Assche
ksys_sync_helper(void)69b5dee313SHarry Pan void ksys_sync_helper(void)
70b5dee313SHarry Pan {
71c64546b1SHarry Pan ktime_t start;
72c64546b1SHarry Pan long elapsed_msecs;
73c64546b1SHarry Pan
74c64546b1SHarry Pan start = ktime_get();
75b5dee313SHarry Pan ksys_sync();
76c64546b1SHarry Pan elapsed_msecs = ktime_to_ms(ktime_sub(ktime_get(), start));
77c64546b1SHarry Pan pr_info("Filesystems sync: %ld.%03ld seconds\n",
78c64546b1SHarry Pan elapsed_msecs / MSEC_PER_SEC, elapsed_msecs % MSEC_PER_SEC);
79b5dee313SHarry Pan }
80b5dee313SHarry Pan EXPORT_SYMBOL_GPL(ksys_sync_helper);
81b5dee313SHarry Pan
8282525756SAlan Stern /* Routines for PM-transition notifications */
8382525756SAlan Stern
8482525756SAlan Stern static BLOCKING_NOTIFIER_HEAD(pm_chain_head);
8582525756SAlan Stern
register_pm_notifier(struct notifier_block * nb)8682525756SAlan Stern int register_pm_notifier(struct notifier_block *nb)
8782525756SAlan Stern {
8882525756SAlan Stern return blocking_notifier_chain_register(&pm_chain_head, nb);
8982525756SAlan Stern }
9082525756SAlan Stern EXPORT_SYMBOL_GPL(register_pm_notifier);
9182525756SAlan Stern
unregister_pm_notifier(struct notifier_block * nb)9282525756SAlan Stern int unregister_pm_notifier(struct notifier_block *nb)
9382525756SAlan Stern {
9482525756SAlan Stern return blocking_notifier_chain_unregister(&pm_chain_head, nb);
9582525756SAlan Stern }
9682525756SAlan Stern EXPORT_SYMBOL_GPL(unregister_pm_notifier);
9782525756SAlan Stern
pm_notifier_call_chain_robust(unsigned long val_up,unsigned long val_down)9870d93298SPeter Zijlstra int pm_notifier_call_chain_robust(unsigned long val_up, unsigned long val_down)
9982525756SAlan Stern {
100ea00f4f4SLianwei Wang int ret;
101ea00f4f4SLianwei Wang
10270d93298SPeter Zijlstra ret = blocking_notifier_call_chain_robust(&pm_chain_head, val_up, val_down, NULL);
103f0c077a8SAkinobu Mita
104f0c077a8SAkinobu Mita return notifier_to_errno(ret);
10582525756SAlan Stern }
10670d93298SPeter Zijlstra
pm_notifier_call_chain(unsigned long val)107ea00f4f4SLianwei Wang int pm_notifier_call_chain(unsigned long val)
108ea00f4f4SLianwei Wang {
10970d93298SPeter Zijlstra return blocking_notifier_call_chain(&pm_chain_head, val, NULL);
110ea00f4f4SLianwei Wang }
11182525756SAlan Stern
1120e06b4a8SRafael J. Wysocki /* If set, devices may be suspended and resumed asynchronously. */
1130e06b4a8SRafael J. Wysocki int pm_async_enabled = 1;
1140e06b4a8SRafael J. Wysocki
pm_async_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)1150e06b4a8SRafael J. Wysocki static ssize_t pm_async_show(struct kobject *kobj, struct kobj_attribute *attr,
1160e06b4a8SRafael J. Wysocki char *buf)
1170e06b4a8SRafael J. Wysocki {
118*a712f03fSXueqin Luo return sysfs_emit(buf, "%d\n", pm_async_enabled);
1190e06b4a8SRafael J. Wysocki }
1200e06b4a8SRafael J. Wysocki
pm_async_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t n)1210e06b4a8SRafael J. Wysocki static ssize_t pm_async_store(struct kobject *kobj, struct kobj_attribute *attr,
1220e06b4a8SRafael J. Wysocki const char *buf, size_t n)
1230e06b4a8SRafael J. Wysocki {
1240e06b4a8SRafael J. Wysocki unsigned long val;
1250e06b4a8SRafael J. Wysocki
126883ee4f7SDaniel Walter if (kstrtoul(buf, 10, &val))
1270e06b4a8SRafael J. Wysocki return -EINVAL;
1280e06b4a8SRafael J. Wysocki
1290e06b4a8SRafael J. Wysocki if (val > 1)
1300e06b4a8SRafael J. Wysocki return -EINVAL;
1310e06b4a8SRafael J. Wysocki
1320e06b4a8SRafael J. Wysocki pm_async_enabled = val;
1330e06b4a8SRafael J. Wysocki return n;
1340e06b4a8SRafael J. Wysocki }
1350e06b4a8SRafael J. Wysocki
1360e06b4a8SRafael J. Wysocki power_attr(pm_async);
1370e06b4a8SRafael J. Wysocki
138406e7938SRafael J. Wysocki #ifdef CONFIG_SUSPEND
mem_sleep_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)139406e7938SRafael J. Wysocki static ssize_t mem_sleep_show(struct kobject *kobj, struct kobj_attribute *attr,
140406e7938SRafael J. Wysocki char *buf)
141406e7938SRafael J. Wysocki {
142*a712f03fSXueqin Luo ssize_t count = 0;
143406e7938SRafael J. Wysocki suspend_state_t i;
144406e7938SRafael J. Wysocki
1459ea4dcf4SDan Williams for (i = PM_SUSPEND_MIN; i < PM_SUSPEND_MAX; i++) {
1469ea4dcf4SDan Williams if (i >= PM_SUSPEND_MEM && cxl_mem_active())
1479ea4dcf4SDan Williams continue;
148406e7938SRafael J. Wysocki if (mem_sleep_states[i]) {
149406e7938SRafael J. Wysocki const char *label = mem_sleep_states[i];
150406e7938SRafael J. Wysocki
151406e7938SRafael J. Wysocki if (mem_sleep_current == i)
152*a712f03fSXueqin Luo count += sysfs_emit_at(buf, count, "[%s] ", label);
153406e7938SRafael J. Wysocki else
154*a712f03fSXueqin Luo count += sysfs_emit_at(buf, count, "%s ", label);
155406e7938SRafael J. Wysocki }
1569ea4dcf4SDan Williams }
157406e7938SRafael J. Wysocki
158406e7938SRafael J. Wysocki /* Convert the last space to a newline if needed. */
159*a712f03fSXueqin Luo if (count > 0)
160*a712f03fSXueqin Luo buf[count - 1] = '\n';
161406e7938SRafael J. Wysocki
162*a712f03fSXueqin Luo return count;
163406e7938SRafael J. Wysocki }
164406e7938SRafael J. Wysocki
decode_suspend_state(const char * buf,size_t n)165406e7938SRafael J. Wysocki static suspend_state_t decode_suspend_state(const char *buf, size_t n)
166406e7938SRafael J. Wysocki {
167406e7938SRafael J. Wysocki suspend_state_t state;
168406e7938SRafael J. Wysocki char *p;
169406e7938SRafael J. Wysocki int len;
170406e7938SRafael J. Wysocki
171406e7938SRafael J. Wysocki p = memchr(buf, '\n', n);
172406e7938SRafael J. Wysocki len = p ? p - buf : n;
173406e7938SRafael J. Wysocki
174406e7938SRafael J. Wysocki for (state = PM_SUSPEND_MIN; state < PM_SUSPEND_MAX; state++) {
175406e7938SRafael J. Wysocki const char *label = mem_sleep_states[state];
176406e7938SRafael J. Wysocki
177406e7938SRafael J. Wysocki if (label && len == strlen(label) && !strncmp(buf, label, len))
178406e7938SRafael J. Wysocki return state;
179406e7938SRafael J. Wysocki }
180406e7938SRafael J. Wysocki
181406e7938SRafael J. Wysocki return PM_SUSPEND_ON;
182406e7938SRafael J. Wysocki }
183406e7938SRafael J. Wysocki
mem_sleep_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t n)184406e7938SRafael J. Wysocki static ssize_t mem_sleep_store(struct kobject *kobj, struct kobj_attribute *attr,
185406e7938SRafael J. Wysocki const char *buf, size_t n)
186406e7938SRafael J. Wysocki {
187406e7938SRafael J. Wysocki suspend_state_t state;
188406e7938SRafael J. Wysocki int error;
189406e7938SRafael J. Wysocki
190406e7938SRafael J. Wysocki error = pm_autosleep_lock();
191406e7938SRafael J. Wysocki if (error)
192406e7938SRafael J. Wysocki return error;
193406e7938SRafael J. Wysocki
194406e7938SRafael J. Wysocki if (pm_autosleep_state() > PM_SUSPEND_ON) {
195406e7938SRafael J. Wysocki error = -EBUSY;
196406e7938SRafael J. Wysocki goto out;
197406e7938SRafael J. Wysocki }
198406e7938SRafael J. Wysocki
199406e7938SRafael J. Wysocki state = decode_suspend_state(buf, n);
200406e7938SRafael J. Wysocki if (state < PM_SUSPEND_MAX && state > PM_SUSPEND_ON)
201406e7938SRafael J. Wysocki mem_sleep_current = state;
202406e7938SRafael J. Wysocki else
203406e7938SRafael J. Wysocki error = -EINVAL;
204406e7938SRafael J. Wysocki
205406e7938SRafael J. Wysocki out:
206406e7938SRafael J. Wysocki pm_autosleep_unlock();
207406e7938SRafael J. Wysocki return error ? error : n;
208406e7938SRafael J. Wysocki }
209406e7938SRafael J. Wysocki
210406e7938SRafael J. Wysocki power_attr(mem_sleep);
211c052bf82SJonas Meurer
212c052bf82SJonas Meurer /*
213c052bf82SJonas Meurer * sync_on_suspend: invoke ksys_sync_helper() before suspend.
214c052bf82SJonas Meurer *
215c052bf82SJonas Meurer * show() returns whether ksys_sync_helper() is invoked before suspend.
216c052bf82SJonas Meurer * store() accepts 0 or 1. 0 disables ksys_sync_helper() and 1 enables it.
217c052bf82SJonas Meurer */
218c052bf82SJonas Meurer bool sync_on_suspend_enabled = !IS_ENABLED(CONFIG_SUSPEND_SKIP_SYNC);
219c052bf82SJonas Meurer
sync_on_suspend_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)220c052bf82SJonas Meurer static ssize_t sync_on_suspend_show(struct kobject *kobj,
221c052bf82SJonas Meurer struct kobj_attribute *attr, char *buf)
222c052bf82SJonas Meurer {
223*a712f03fSXueqin Luo return sysfs_emit(buf, "%d\n", sync_on_suspend_enabled);
224c052bf82SJonas Meurer }
225c052bf82SJonas Meurer
sync_on_suspend_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t n)226c052bf82SJonas Meurer static ssize_t sync_on_suspend_store(struct kobject *kobj,
227c052bf82SJonas Meurer struct kobj_attribute *attr,
228c052bf82SJonas Meurer const char *buf, size_t n)
229c052bf82SJonas Meurer {
230c052bf82SJonas Meurer unsigned long val;
231c052bf82SJonas Meurer
232c052bf82SJonas Meurer if (kstrtoul(buf, 10, &val))
233c052bf82SJonas Meurer return -EINVAL;
234c052bf82SJonas Meurer
235c052bf82SJonas Meurer if (val > 1)
236c052bf82SJonas Meurer return -EINVAL;
237c052bf82SJonas Meurer
238c052bf82SJonas Meurer sync_on_suspend_enabled = !!val;
239c052bf82SJonas Meurer return n;
240c052bf82SJonas Meurer }
241c052bf82SJonas Meurer
242c052bf82SJonas Meurer power_attr(sync_on_suspend);
243406e7938SRafael J. Wysocki #endif /* CONFIG_SUSPEND */
244406e7938SRafael J. Wysocki
245e516a1dbSRafael J. Wysocki #ifdef CONFIG_PM_SLEEP_DEBUG
2460e7d56e3SRafael J. Wysocki int pm_test_level = TEST_NONE;
2470e7d56e3SRafael J. Wysocki
2480e7d56e3SRafael J. Wysocki static const char * const pm_tests[__TEST_AFTER_LAST] = {
2490e7d56e3SRafael J. Wysocki [TEST_NONE] = "none",
2500e7d56e3SRafael J. Wysocki [TEST_CORE] = "core",
2510e7d56e3SRafael J. Wysocki [TEST_CPUS] = "processors",
2520e7d56e3SRafael J. Wysocki [TEST_PLATFORM] = "platform",
2530e7d56e3SRafael J. Wysocki [TEST_DEVICES] = "devices",
2540e7d56e3SRafael J. Wysocki [TEST_FREEZER] = "freezer",
2550e7d56e3SRafael J. Wysocki };
2560e7d56e3SRafael J. Wysocki
pm_test_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)257039a75c6SRafael J. Wysocki static ssize_t pm_test_show(struct kobject *kobj, struct kobj_attribute *attr,
258039a75c6SRafael J. Wysocki char *buf)
2590e7d56e3SRafael J. Wysocki {
260*a712f03fSXueqin Luo ssize_t count = 0;
2610e7d56e3SRafael J. Wysocki int level;
2620e7d56e3SRafael J. Wysocki
2630e7d56e3SRafael J. Wysocki for (level = TEST_FIRST; level <= TEST_MAX; level++)
2640e7d56e3SRafael J. Wysocki if (pm_tests[level]) {
2650e7d56e3SRafael J. Wysocki if (level == pm_test_level)
266*a712f03fSXueqin Luo count += sysfs_emit_at(buf, count, "[%s] ", pm_tests[level]);
2670e7d56e3SRafael J. Wysocki else
268*a712f03fSXueqin Luo count += sysfs_emit_at(buf, count, "%s ", pm_tests[level]);
2690e7d56e3SRafael J. Wysocki }
2700e7d56e3SRafael J. Wysocki
271*a712f03fSXueqin Luo /* Convert the last space to a newline if needed. */
272*a712f03fSXueqin Luo if (count > 0)
273*a712f03fSXueqin Luo buf[count - 1] = '\n';
2740e7d56e3SRafael J. Wysocki
275*a712f03fSXueqin Luo return count;
2760e7d56e3SRafael J. Wysocki }
2770e7d56e3SRafael J. Wysocki
pm_test_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t n)278039a75c6SRafael J. Wysocki static ssize_t pm_test_store(struct kobject *kobj, struct kobj_attribute *attr,
279039a75c6SRafael J. Wysocki const char *buf, size_t n)
2800e7d56e3SRafael J. Wysocki {
2815950e5d5SPeter Zijlstra unsigned int sleep_flags;
2820e7d56e3SRafael J. Wysocki const char * const *s;
2835950e5d5SPeter Zijlstra int error = -EINVAL;
2840e7d56e3SRafael J. Wysocki int level;
2850e7d56e3SRafael J. Wysocki char *p;
2860e7d56e3SRafael J. Wysocki int len;
2870e7d56e3SRafael J. Wysocki
2880e7d56e3SRafael J. Wysocki p = memchr(buf, '\n', n);
2890e7d56e3SRafael J. Wysocki len = p ? p - buf : n;
2900e7d56e3SRafael J. Wysocki
2915950e5d5SPeter Zijlstra sleep_flags = lock_system_sleep();
2920e7d56e3SRafael J. Wysocki
2930e7d56e3SRafael J. Wysocki level = TEST_FIRST;
2940e7d56e3SRafael J. Wysocki for (s = &pm_tests[level]; level <= TEST_MAX; s++, level++)
2950e7d56e3SRafael J. Wysocki if (*s && len == strlen(*s) && !strncmp(buf, *s, len)) {
2960e7d56e3SRafael J. Wysocki pm_test_level = level;
2970e7d56e3SRafael J. Wysocki error = 0;
2980e7d56e3SRafael J. Wysocki break;
2990e7d56e3SRafael J. Wysocki }
3000e7d56e3SRafael J. Wysocki
3015950e5d5SPeter Zijlstra unlock_system_sleep(sleep_flags);
3020e7d56e3SRafael J. Wysocki
3030e7d56e3SRafael J. Wysocki return error ? error : n;
3040e7d56e3SRafael J. Wysocki }
3050e7d56e3SRafael J. Wysocki
3060e7d56e3SRafael J. Wysocki power_attr(pm_test);
307e516a1dbSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP_DEBUG */
3080e7d56e3SRafael J. Wysocki
3099ff544faSRafael J. Wysocki #define SUSPEND_NR_STEPS SUSPEND_RESUME
3109ff544faSRafael J. Wysocki #define REC_FAILED_NUM 2
3119ff544faSRafael J. Wysocki
3129ff544faSRafael J. Wysocki struct suspend_stats {
3139ff544faSRafael J. Wysocki unsigned int step_failures[SUSPEND_NR_STEPS];
3149ff544faSRafael J. Wysocki unsigned int success;
3159ff544faSRafael J. Wysocki unsigned int fail;
3169ff544faSRafael J. Wysocki int last_failed_dev;
3179ff544faSRafael J. Wysocki char failed_devs[REC_FAILED_NUM][40];
3189ff544faSRafael J. Wysocki int last_failed_errno;
3199ff544faSRafael J. Wysocki int errno[REC_FAILED_NUM];
3209ff544faSRafael J. Wysocki int last_failed_step;
3219ff544faSRafael J. Wysocki u64 last_hw_sleep;
3229ff544faSRafael J. Wysocki u64 total_hw_sleep;
3239ff544faSRafael J. Wysocki u64 max_hw_sleep;
3249ff544faSRafael J. Wysocki enum suspend_stat_step failed_steps[REC_FAILED_NUM];
3259ff544faSRafael J. Wysocki };
3269ff544faSRafael J. Wysocki
3279ff544faSRafael J. Wysocki static struct suspend_stats suspend_stats;
328a6d38e99SRafael J. Wysocki static DEFINE_MUTEX(suspend_stats_lock);
3299ff544faSRafael J. Wysocki
dpm_save_failed_dev(const char * name)3309ff544faSRafael J. Wysocki void dpm_save_failed_dev(const char *name)
3319ff544faSRafael J. Wysocki {
332a6d38e99SRafael J. Wysocki mutex_lock(&suspend_stats_lock);
333a6d38e99SRafael J. Wysocki
3349ff544faSRafael J. Wysocki strscpy(suspend_stats.failed_devs[suspend_stats.last_failed_dev],
3359ff544faSRafael J. Wysocki name, sizeof(suspend_stats.failed_devs[0]));
3369ff544faSRafael J. Wysocki suspend_stats.last_failed_dev++;
3379ff544faSRafael J. Wysocki suspend_stats.last_failed_dev %= REC_FAILED_NUM;
338a6d38e99SRafael J. Wysocki
339a6d38e99SRafael J. Wysocki mutex_unlock(&suspend_stats_lock);
3409ff544faSRafael J. Wysocki }
3419ff544faSRafael J. Wysocki
dpm_save_failed_step(enum suspend_stat_step step)3429ff544faSRafael J. Wysocki void dpm_save_failed_step(enum suspend_stat_step step)
3439ff544faSRafael J. Wysocki {
3449ff544faSRafael J. Wysocki suspend_stats.step_failures[step-1]++;
3459ff544faSRafael J. Wysocki suspend_stats.failed_steps[suspend_stats.last_failed_step] = step;
3469ff544faSRafael J. Wysocki suspend_stats.last_failed_step++;
3479ff544faSRafael J. Wysocki suspend_stats.last_failed_step %= REC_FAILED_NUM;
3489ff544faSRafael J. Wysocki }
3499ff544faSRafael J. Wysocki
dpm_save_errno(int err)3509ff544faSRafael J. Wysocki void dpm_save_errno(int err)
3519ff544faSRafael J. Wysocki {
3529ff544faSRafael J. Wysocki if (!err) {
3539ff544faSRafael J. Wysocki suspend_stats.success++;
3549ff544faSRafael J. Wysocki return;
3559ff544faSRafael J. Wysocki }
3569ff544faSRafael J. Wysocki
3579ff544faSRafael J. Wysocki suspend_stats.fail++;
3589ff544faSRafael J. Wysocki
3599ff544faSRafael J. Wysocki suspend_stats.errno[suspend_stats.last_failed_errno] = err;
3609ff544faSRafael J. Wysocki suspend_stats.last_failed_errno++;
3619ff544faSRafael J. Wysocki suspend_stats.last_failed_errno %= REC_FAILED_NUM;
3629ff544faSRafael J. Wysocki }
3639ff544faSRafael J. Wysocki
pm_report_hw_sleep_time(u64 t)3649ff544faSRafael J. Wysocki void pm_report_hw_sleep_time(u64 t)
3659ff544faSRafael J. Wysocki {
3669ff544faSRafael J. Wysocki suspend_stats.last_hw_sleep = t;
3679ff544faSRafael J. Wysocki suspend_stats.total_hw_sleep += t;
3689ff544faSRafael J. Wysocki }
3699ff544faSRafael J. Wysocki EXPORT_SYMBOL_GPL(pm_report_hw_sleep_time);
3709ff544faSRafael J. Wysocki
pm_report_max_hw_sleep(u64 t)3719ff544faSRafael J. Wysocki void pm_report_max_hw_sleep(u64 t)
3729ff544faSRafael J. Wysocki {
3739ff544faSRafael J. Wysocki suspend_stats.max_hw_sleep = t;
3749ff544faSRafael J. Wysocki }
3759ff544faSRafael J. Wysocki EXPORT_SYMBOL_GPL(pm_report_max_hw_sleep);
3769ff544faSRafael J. Wysocki
377bc88528cSRafael J. Wysocki static const char * const suspend_step_names[] = {
378bc88528cSRafael J. Wysocki [SUSPEND_WORKING] = "",
379bc88528cSRafael J. Wysocki [SUSPEND_FREEZE] = "freeze",
380bc88528cSRafael J. Wysocki [SUSPEND_PREPARE] = "prepare",
381bc88528cSRafael J. Wysocki [SUSPEND_SUSPEND] = "suspend",
382bc88528cSRafael J. Wysocki [SUSPEND_SUSPEND_LATE] = "suspend_late",
383bc88528cSRafael J. Wysocki [SUSPEND_SUSPEND_NOIRQ] = "suspend_noirq",
384bc88528cSRafael J. Wysocki [SUSPEND_RESUME_NOIRQ] = "resume_noirq",
385bc88528cSRafael J. Wysocki [SUSPEND_RESUME_EARLY] = "resume_early",
386bc88528cSRafael J. Wysocki [SUSPEND_RESUME] = "resume",
387bc88528cSRafael J. Wysocki };
3882a77c46dSShuoX Liu
389b52124a7SMario Limonciello #define suspend_attr(_name, format_str) \
3902c8db5beSKalesh Singh static ssize_t _name##_show(struct kobject *kobj, \
3912c8db5beSKalesh Singh struct kobj_attribute *attr, char *buf) \
3922c8db5beSKalesh Singh { \
393*a712f03fSXueqin Luo return sysfs_emit(buf, format_str, suspend_stats._name);\
3942c8db5beSKalesh Singh } \
3952c8db5beSKalesh Singh static struct kobj_attribute _name = __ATTR_RO(_name)
3962c8db5beSKalesh Singh
3972231f78dSRafael J. Wysocki suspend_attr(success, "%u\n");
3982231f78dSRafael J. Wysocki suspend_attr(fail, "%u\n");
399b52124a7SMario Limonciello suspend_attr(last_hw_sleep, "%llu\n");
400b52124a7SMario Limonciello suspend_attr(total_hw_sleep, "%llu\n");
401b52124a7SMario Limonciello suspend_attr(max_hw_sleep, "%llu\n");
4022c8db5beSKalesh Singh
403b730bab0SRafael J. Wysocki #define suspend_step_attr(_name, step) \
404b730bab0SRafael J. Wysocki static ssize_t _name##_show(struct kobject *kobj, \
405b730bab0SRafael J. Wysocki struct kobj_attribute *attr, char *buf) \
406b730bab0SRafael J. Wysocki { \
407*a712f03fSXueqin Luo return sysfs_emit(buf, "%u\n", \
408b730bab0SRafael J. Wysocki suspend_stats.step_failures[step-1]); \
409b730bab0SRafael J. Wysocki } \
410b730bab0SRafael J. Wysocki static struct kobj_attribute _name = __ATTR_RO(_name)
411b730bab0SRafael J. Wysocki
412b730bab0SRafael J. Wysocki suspend_step_attr(failed_freeze, SUSPEND_FREEZE);
413b730bab0SRafael J. Wysocki suspend_step_attr(failed_prepare, SUSPEND_PREPARE);
414b730bab0SRafael J. Wysocki suspend_step_attr(failed_suspend, SUSPEND_SUSPEND);
415b730bab0SRafael J. Wysocki suspend_step_attr(failed_suspend_late, SUSPEND_SUSPEND_LATE);
416b730bab0SRafael J. Wysocki suspend_step_attr(failed_suspend_noirq, SUSPEND_SUSPEND_NOIRQ);
417b730bab0SRafael J. Wysocki suspend_step_attr(failed_resume, SUSPEND_RESUME);
418b730bab0SRafael J. Wysocki suspend_step_attr(failed_resume_early, SUSPEND_RESUME_EARLY);
419b730bab0SRafael J. Wysocki suspend_step_attr(failed_resume_noirq, SUSPEND_RESUME_NOIRQ);
420b730bab0SRafael J. Wysocki
last_failed_dev_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)4212c8db5beSKalesh Singh static ssize_t last_failed_dev_show(struct kobject *kobj,
4222c8db5beSKalesh Singh struct kobj_attribute *attr, char *buf)
4232c8db5beSKalesh Singh {
4242c8db5beSKalesh Singh int index;
4252c8db5beSKalesh Singh char *last_failed_dev = NULL;
4262c8db5beSKalesh Singh
4272c8db5beSKalesh Singh index = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1;
4282c8db5beSKalesh Singh index %= REC_FAILED_NUM;
4292c8db5beSKalesh Singh last_failed_dev = suspend_stats.failed_devs[index];
4302c8db5beSKalesh Singh
431*a712f03fSXueqin Luo return sysfs_emit(buf, "%s\n", last_failed_dev);
4322c8db5beSKalesh Singh }
4332c8db5beSKalesh Singh static struct kobj_attribute last_failed_dev = __ATTR_RO(last_failed_dev);
4342c8db5beSKalesh Singh
last_failed_errno_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)4352c8db5beSKalesh Singh static ssize_t last_failed_errno_show(struct kobject *kobj,
4362c8db5beSKalesh Singh struct kobj_attribute *attr, char *buf)
4372c8db5beSKalesh Singh {
4382c8db5beSKalesh Singh int index;
4392c8db5beSKalesh Singh int last_failed_errno;
4402c8db5beSKalesh Singh
4412c8db5beSKalesh Singh index = suspend_stats.last_failed_errno + REC_FAILED_NUM - 1;
4422c8db5beSKalesh Singh index %= REC_FAILED_NUM;
4432c8db5beSKalesh Singh last_failed_errno = suspend_stats.errno[index];
4442c8db5beSKalesh Singh
445*a712f03fSXueqin Luo return sysfs_emit(buf, "%d\n", last_failed_errno);
4462c8db5beSKalesh Singh }
4472c8db5beSKalesh Singh static struct kobj_attribute last_failed_errno = __ATTR_RO(last_failed_errno);
4482c8db5beSKalesh Singh
last_failed_step_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)4492c8db5beSKalesh Singh static ssize_t last_failed_step_show(struct kobject *kobj,
4502c8db5beSKalesh Singh struct kobj_attribute *attr, char *buf)
4512c8db5beSKalesh Singh {
4522c8db5beSKalesh Singh enum suspend_stat_step step;
453bc88528cSRafael J. Wysocki int index;
4542c8db5beSKalesh Singh
4552c8db5beSKalesh Singh index = suspend_stats.last_failed_step + REC_FAILED_NUM - 1;
4562c8db5beSKalesh Singh index %= REC_FAILED_NUM;
4572c8db5beSKalesh Singh step = suspend_stats.failed_steps[index];
4582c8db5beSKalesh Singh
459*a712f03fSXueqin Luo return sysfs_emit(buf, "%s\n", suspend_step_names[step]);
4602c8db5beSKalesh Singh }
4612c8db5beSKalesh Singh static struct kobj_attribute last_failed_step = __ATTR_RO(last_failed_step);
4622c8db5beSKalesh Singh
4632c8db5beSKalesh Singh static struct attribute *suspend_attrs[] = {
4642c8db5beSKalesh Singh &success.attr,
4652c8db5beSKalesh Singh &fail.attr,
4662c8db5beSKalesh Singh &failed_freeze.attr,
4672c8db5beSKalesh Singh &failed_prepare.attr,
4682c8db5beSKalesh Singh &failed_suspend.attr,
4692c8db5beSKalesh Singh &failed_suspend_late.attr,
4702c8db5beSKalesh Singh &failed_suspend_noirq.attr,
4712c8db5beSKalesh Singh &failed_resume.attr,
4722c8db5beSKalesh Singh &failed_resume_early.attr,
4732c8db5beSKalesh Singh &failed_resume_noirq.attr,
4742c8db5beSKalesh Singh &last_failed_dev.attr,
4752c8db5beSKalesh Singh &last_failed_errno.attr,
4762c8db5beSKalesh Singh &last_failed_step.attr,
477b52124a7SMario Limonciello &last_hw_sleep.attr,
478b52124a7SMario Limonciello &total_hw_sleep.attr,
479b52124a7SMario Limonciello &max_hw_sleep.attr,
4802c8db5beSKalesh Singh NULL,
4812c8db5beSKalesh Singh };
4822c8db5beSKalesh Singh
suspend_attr_is_visible(struct kobject * kobj,struct attribute * attr,int idx)483b52124a7SMario Limonciello static umode_t suspend_attr_is_visible(struct kobject *kobj, struct attribute *attr, int idx)
484b52124a7SMario Limonciello {
485b52124a7SMario Limonciello if (attr != &last_hw_sleep.attr &&
486b52124a7SMario Limonciello attr != &total_hw_sleep.attr &&
487b52124a7SMario Limonciello attr != &max_hw_sleep.attr)
488b52124a7SMario Limonciello return 0444;
489b52124a7SMario Limonciello
490b52124a7SMario Limonciello #ifdef CONFIG_ACPI
491b52124a7SMario Limonciello if (acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0)
492b52124a7SMario Limonciello return 0444;
493b52124a7SMario Limonciello #endif
494b52124a7SMario Limonciello return 0;
495b52124a7SMario Limonciello }
496b52124a7SMario Limonciello
49715560574SRikard Falkeborn static const struct attribute_group suspend_attr_group = {
4982c8db5beSKalesh Singh .name = "suspend_stats",
4992c8db5beSKalesh Singh .attrs = suspend_attrs,
500b52124a7SMario Limonciello .is_visible = suspend_attr_is_visible,
5012c8db5beSKalesh Singh };
5022c8db5beSKalesh Singh
5032c8db5beSKalesh Singh #ifdef CONFIG_DEBUG_FS
suspend_stats_show(struct seq_file * s,void * unused)5042a77c46dSShuoX Liu static int suspend_stats_show(struct seq_file *s, void *unused)
5052a77c46dSShuoX Liu {
5062a77c46dSShuoX Liu int i, index, last_dev, last_errno, last_step;
507b730bab0SRafael J. Wysocki enum suspend_stat_step step;
5082a77c46dSShuoX Liu
5092a77c46dSShuoX Liu last_dev = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1;
5102a77c46dSShuoX Liu last_dev %= REC_FAILED_NUM;
5112a77c46dSShuoX Liu last_errno = suspend_stats.last_failed_errno + REC_FAILED_NUM - 1;
5122a77c46dSShuoX Liu last_errno %= REC_FAILED_NUM;
5132a77c46dSShuoX Liu last_step = suspend_stats.last_failed_step + REC_FAILED_NUM - 1;
5142a77c46dSShuoX Liu last_step %= REC_FAILED_NUM;
515b730bab0SRafael J. Wysocki
5162231f78dSRafael J. Wysocki seq_printf(s, "success: %u\nfail: %u\n",
517b730bab0SRafael J. Wysocki suspend_stats.success, suspend_stats.fail);
518b730bab0SRafael J. Wysocki
519b730bab0SRafael J. Wysocki for (step = SUSPEND_FREEZE; step <= SUSPEND_NR_STEPS; step++)
520b730bab0SRafael J. Wysocki seq_printf(s, "failed_%s: %u\n", suspend_step_names[step],
521b730bab0SRafael J. Wysocki suspend_stats.step_failures[step-1]);
522b730bab0SRafael J. Wysocki
5232a77c46dSShuoX Liu seq_printf(s, "failures:\n last_failed_dev:\t%-s\n",
5242a77c46dSShuoX Liu suspend_stats.failed_devs[last_dev]);
5252a77c46dSShuoX Liu for (i = 1; i < REC_FAILED_NUM; i++) {
5262a77c46dSShuoX Liu index = last_dev + REC_FAILED_NUM - i;
5272a77c46dSShuoX Liu index %= REC_FAILED_NUM;
528bc88528cSRafael J. Wysocki seq_printf(s, "\t\t\t%-s\n", suspend_stats.failed_devs[index]);
5292a77c46dSShuoX Liu }
5302a77c46dSShuoX Liu seq_printf(s, " last_failed_errno:\t%-d\n",
5312a77c46dSShuoX Liu suspend_stats.errno[last_errno]);
5322a77c46dSShuoX Liu for (i = 1; i < REC_FAILED_NUM; i++) {
5332a77c46dSShuoX Liu index = last_errno + REC_FAILED_NUM - i;
5342a77c46dSShuoX Liu index %= REC_FAILED_NUM;
535bc88528cSRafael J. Wysocki seq_printf(s, "\t\t\t%-d\n", suspend_stats.errno[index]);
5362a77c46dSShuoX Liu }
5372a77c46dSShuoX Liu seq_printf(s, " last_failed_step:\t%-s\n",
538bc88528cSRafael J. Wysocki suspend_step_names[suspend_stats.failed_steps[last_step]]);
5392a77c46dSShuoX Liu for (i = 1; i < REC_FAILED_NUM; i++) {
5402a77c46dSShuoX Liu index = last_step + REC_FAILED_NUM - i;
5412a77c46dSShuoX Liu index %= REC_FAILED_NUM;
5422a77c46dSShuoX Liu seq_printf(s, "\t\t\t%-s\n",
543bc88528cSRafael J. Wysocki suspend_step_names[suspend_stats.failed_steps[index]]);
5442a77c46dSShuoX Liu }
5452a77c46dSShuoX Liu
5462a77c46dSShuoX Liu return 0;
5472a77c46dSShuoX Liu }
548943a10f8SYangtao Li DEFINE_SHOW_ATTRIBUTE(suspend_stats);
5492a77c46dSShuoX Liu
pm_debugfs_init(void)5502a77c46dSShuoX Liu static int __init pm_debugfs_init(void)
5512a77c46dSShuoX Liu {
5522a77c46dSShuoX Liu debugfs_create_file("suspend_stats", S_IFREG | S_IRUGO,
553943a10f8SYangtao Li NULL, NULL, &suspend_stats_fops);
5542a77c46dSShuoX Liu return 0;
5552a77c46dSShuoX Liu }
5562a77c46dSShuoX Liu
5572a77c46dSShuoX Liu late_initcall(pm_debugfs_init);
5582a77c46dSShuoX Liu #endif /* CONFIG_DEBUG_FS */
5592a77c46dSShuoX Liu
560ca123102SRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */
561ca123102SRafael J. Wysocki
562b2df1d4fSRafael J. Wysocki #ifdef CONFIG_PM_SLEEP_DEBUG
563b2df1d4fSRafael J. Wysocki /*
564b2df1d4fSRafael J. Wysocki * pm_print_times: print time taken by devices to suspend and resume.
565b2df1d4fSRafael J. Wysocki *
566b2df1d4fSRafael J. Wysocki * show() returns whether printing of suspend and resume times is enabled.
567b2df1d4fSRafael J. Wysocki * store() accepts 0 or 1. 0 disables printing and 1 enables it.
568b2df1d4fSRafael J. Wysocki */
569b2df1d4fSRafael J. Wysocki bool pm_print_times_enabled;
570b2df1d4fSRafael J. Wysocki
pm_print_times_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)571b2df1d4fSRafael J. Wysocki static ssize_t pm_print_times_show(struct kobject *kobj,
572b2df1d4fSRafael J. Wysocki struct kobj_attribute *attr, char *buf)
573b2df1d4fSRafael J. Wysocki {
574*a712f03fSXueqin Luo return sysfs_emit(buf, "%d\n", pm_print_times_enabled);
575b2df1d4fSRafael J. Wysocki }
576b2df1d4fSRafael J. Wysocki
pm_print_times_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t n)577b2df1d4fSRafael J. Wysocki static ssize_t pm_print_times_store(struct kobject *kobj,
578b2df1d4fSRafael J. Wysocki struct kobj_attribute *attr,
579b2df1d4fSRafael J. Wysocki const char *buf, size_t n)
580b2df1d4fSRafael J. Wysocki {
581b2df1d4fSRafael J. Wysocki unsigned long val;
582b2df1d4fSRafael J. Wysocki
583b2df1d4fSRafael J. Wysocki if (kstrtoul(buf, 10, &val))
584b2df1d4fSRafael J. Wysocki return -EINVAL;
585b2df1d4fSRafael J. Wysocki
586b2df1d4fSRafael J. Wysocki if (val > 1)
587b2df1d4fSRafael J. Wysocki return -EINVAL;
588b2df1d4fSRafael J. Wysocki
589b2df1d4fSRafael J. Wysocki pm_print_times_enabled = !!val;
590b2df1d4fSRafael J. Wysocki return n;
591b2df1d4fSRafael J. Wysocki }
592b2df1d4fSRafael J. Wysocki
593b2df1d4fSRafael J. Wysocki power_attr(pm_print_times);
594b2df1d4fSRafael J. Wysocki
pm_print_times_init(void)595b2df1d4fSRafael J. Wysocki static inline void pm_print_times_init(void)
596b2df1d4fSRafael J. Wysocki {
597b2df1d4fSRafael J. Wysocki pm_print_times_enabled = !!initcall_debug;
598b2df1d4fSRafael J. Wysocki }
599a6f5f0ddSAlexandra Yates
pm_wakeup_irq_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)600a6f5f0ddSAlexandra Yates static ssize_t pm_wakeup_irq_show(struct kobject *kobj,
601a6f5f0ddSAlexandra Yates struct kobj_attribute *attr,
602a6f5f0ddSAlexandra Yates char *buf)
603a6f5f0ddSAlexandra Yates {
604cb1f65c1SRafael J. Wysocki if (!pm_wakeup_irq())
605cb1f65c1SRafael J. Wysocki return -ENODATA;
606cb1f65c1SRafael J. Wysocki
607*a712f03fSXueqin Luo return sysfs_emit(buf, "%u\n", pm_wakeup_irq());
608a6f5f0ddSAlexandra Yates }
609a6f5f0ddSAlexandra Yates
610a1e9ca69SRafael J. Wysocki power_attr_ro(pm_wakeup_irq);
611a6f5f0ddSAlexandra Yates
612726fb6b4SSrinivas Pandruvada bool pm_debug_messages_on __read_mostly;
6138d8b2441SRafael J. Wysocki
pm_debug_messages_should_print(void)614cdb8c100SMario Limonciello bool pm_debug_messages_should_print(void)
615cdb8c100SMario Limonciello {
616cdb8c100SMario Limonciello return pm_debug_messages_on && pm_suspend_target_state != PM_SUSPEND_ON;
617cdb8c100SMario Limonciello }
618cdb8c100SMario Limonciello EXPORT_SYMBOL_GPL(pm_debug_messages_should_print);
619cdb8c100SMario Limonciello
pm_debug_messages_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)6208d8b2441SRafael J. Wysocki static ssize_t pm_debug_messages_show(struct kobject *kobj,
6218d8b2441SRafael J. Wysocki struct kobj_attribute *attr, char *buf)
6228d8b2441SRafael J. Wysocki {
623*a712f03fSXueqin Luo return sysfs_emit(buf, "%d\n", pm_debug_messages_on);
6248d8b2441SRafael J. Wysocki }
6258d8b2441SRafael J. Wysocki
pm_debug_messages_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t n)6268d8b2441SRafael J. Wysocki static ssize_t pm_debug_messages_store(struct kobject *kobj,
6278d8b2441SRafael J. Wysocki struct kobj_attribute *attr,
6288d8b2441SRafael J. Wysocki const char *buf, size_t n)
6298d8b2441SRafael J. Wysocki {
6308d8b2441SRafael J. Wysocki unsigned long val;
6318d8b2441SRafael J. Wysocki
6328d8b2441SRafael J. Wysocki if (kstrtoul(buf, 10, &val))
6338d8b2441SRafael J. Wysocki return -EINVAL;
6348d8b2441SRafael J. Wysocki
6358d8b2441SRafael J. Wysocki if (val > 1)
6368d8b2441SRafael J. Wysocki return -EINVAL;
6378d8b2441SRafael J. Wysocki
6388d8b2441SRafael J. Wysocki pm_debug_messages_on = !!val;
6398d8b2441SRafael J. Wysocki return n;
6408d8b2441SRafael J. Wysocki }
6418d8b2441SRafael J. Wysocki
6428d8b2441SRafael J. Wysocki power_attr(pm_debug_messages);
6438d8b2441SRafael J. Wysocki
pm_debug_messages_setup(char * str)644db96a759SChen Yu static int __init pm_debug_messages_setup(char *str)
645db96a759SChen Yu {
646db96a759SChen Yu pm_debug_messages_on = true;
647db96a759SChen Yu return 1;
648db96a759SChen Yu }
649db96a759SChen Yu __setup("pm_debug_messages", pm_debug_messages_setup);
650db96a759SChen Yu
651819b1bb3SRafael J. Wysocki #else /* !CONFIG_PM_SLEEP_DEBUG */
pm_print_times_init(void)652b2df1d4fSRafael J. Wysocki static inline void pm_print_times_init(void) {}
653b2df1d4fSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP_DEBUG */
654b2df1d4fSRafael J. Wysocki
655d76e15fbSGreg Kroah-Hartman struct kobject *power_kobj;
6561da177e4SLinus Torvalds
657dbcfa715SRandy Dunlap /*
6580399d4dbSRafael J. Wysocki * state - control system sleep states.
6591da177e4SLinus Torvalds *
6600399d4dbSRafael J. Wysocki * show() returns available sleep state labels, which may be "mem", "standby",
66144348e8aSMauro Carvalho Chehab * "freeze" and "disk" (hibernation).
66244348e8aSMauro Carvalho Chehab * See Documentation/admin-guide/pm/sleep-states.rst for a description of
66344348e8aSMauro Carvalho Chehab * what they mean.
6641da177e4SLinus Torvalds *
6650399d4dbSRafael J. Wysocki * store() accepts one of those strings, translates it into the proper
6660399d4dbSRafael J. Wysocki * enumerated value, and initiates a suspend transition.
6671da177e4SLinus Torvalds */
state_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)668386f275fSKay Sievers static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
669386f275fSKay Sievers char *buf)
6701da177e4SLinus Torvalds {
671*a712f03fSXueqin Luo ssize_t count = 0;
672296699deSRafael J. Wysocki #ifdef CONFIG_SUSPEND
67327ddcc65SRafael J. Wysocki suspend_state_t i;
6741da177e4SLinus Torvalds
67527ddcc65SRafael J. Wysocki for (i = PM_SUSPEND_MIN; i < PM_SUSPEND_MAX; i++)
676d431cbc5SRafael J. Wysocki if (pm_states[i])
677*a712f03fSXueqin Luo count += sysfs_emit_at(buf, count, "%s ", pm_states[i]);
67827ddcc65SRafael J. Wysocki
679296699deSRafael J. Wysocki #endif
680a6e15a39SKees Cook if (hibernation_available())
681*a712f03fSXueqin Luo count += sysfs_emit_at(buf, count, "disk ");
682*a712f03fSXueqin Luo
683*a712f03fSXueqin Luo /* Convert the last space to a newline if needed. */
684*a712f03fSXueqin Luo if (count > 0)
685*a712f03fSXueqin Luo buf[count - 1] = '\n';
686*a712f03fSXueqin Luo
687*a712f03fSXueqin Luo return count;
6881da177e4SLinus Torvalds }
6891da177e4SLinus Torvalds
decode_state(const char * buf,size_t n)6907483b4a4SRafael J. Wysocki static suspend_state_t decode_state(const char *buf, size_t n)
6911da177e4SLinus Torvalds {
692296699deSRafael J. Wysocki #ifdef CONFIG_SUSPEND
693d431cbc5SRafael J. Wysocki suspend_state_t state;
694296699deSRafael J. Wysocki #endif
6951da177e4SLinus Torvalds char *p;
6961da177e4SLinus Torvalds int len;
6971da177e4SLinus Torvalds
6981da177e4SLinus Torvalds p = memchr(buf, '\n', n);
6991da177e4SLinus Torvalds len = p ? p - buf : n;
7001da177e4SLinus Torvalds
7017483b4a4SRafael J. Wysocki /* Check hibernation first. */
702d30bdfc0SChuhong Yuan if (len == 4 && str_has_prefix(buf, "disk"))
7037483b4a4SRafael J. Wysocki return PM_SUSPEND_MAX;
704a3d25c27SRafael J. Wysocki
705296699deSRafael J. Wysocki #ifdef CONFIG_SUSPEND
706d431cbc5SRafael J. Wysocki for (state = PM_SUSPEND_MIN; state < PM_SUSPEND_MAX; state++) {
707d431cbc5SRafael J. Wysocki const char *label = pm_states[state];
708d431cbc5SRafael J. Wysocki
709d431cbc5SRafael J. Wysocki if (label && len == strlen(label) && !strncmp(buf, label, len))
710d431cbc5SRafael J. Wysocki return state;
711d431cbc5SRafael J. Wysocki }
712296699deSRafael J. Wysocki #endif
713296699deSRafael J. Wysocki
7147483b4a4SRafael J. Wysocki return PM_SUSPEND_ON;
7157483b4a4SRafael J. Wysocki }
7167483b4a4SRafael J. Wysocki
state_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t n)7177483b4a4SRafael J. Wysocki static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
7187483b4a4SRafael J. Wysocki const char *buf, size_t n)
7197483b4a4SRafael J. Wysocki {
7207483b4a4SRafael J. Wysocki suspend_state_t state;
7217483b4a4SRafael J. Wysocki int error;
7227483b4a4SRafael J. Wysocki
7237483b4a4SRafael J. Wysocki error = pm_autosleep_lock();
7247483b4a4SRafael J. Wysocki if (error)
7257483b4a4SRafael J. Wysocki return error;
7267483b4a4SRafael J. Wysocki
7277483b4a4SRafael J. Wysocki if (pm_autosleep_state() > PM_SUSPEND_ON) {
7287483b4a4SRafael J. Wysocki error = -EBUSY;
7297483b4a4SRafael J. Wysocki goto out;
7307483b4a4SRafael J. Wysocki }
7317483b4a4SRafael J. Wysocki
7327483b4a4SRafael J. Wysocki state = decode_state(buf, n);
733406e7938SRafael J. Wysocki if (state < PM_SUSPEND_MAX) {
734406e7938SRafael J. Wysocki if (state == PM_SUSPEND_MEM)
735406e7938SRafael J. Wysocki state = mem_sleep_current;
736406e7938SRafael J. Wysocki
7377483b4a4SRafael J. Wysocki error = pm_suspend(state);
738406e7938SRafael J. Wysocki } else if (state == PM_SUSPEND_MAX) {
7397483b4a4SRafael J. Wysocki error = hibernate();
740406e7938SRafael J. Wysocki } else {
7417483b4a4SRafael J. Wysocki error = -EINVAL;
742406e7938SRafael J. Wysocki }
7437483b4a4SRafael J. Wysocki
7447483b4a4SRafael J. Wysocki out:
7457483b4a4SRafael J. Wysocki pm_autosleep_unlock();
7461da177e4SLinus Torvalds return error ? error : n;
7471da177e4SLinus Torvalds }
7481da177e4SLinus Torvalds
7491da177e4SLinus Torvalds power_attr(state);
7501da177e4SLinus Torvalds
751c125e96fSRafael J. Wysocki #ifdef CONFIG_PM_SLEEP
752c125e96fSRafael J. Wysocki /*
753c125e96fSRafael J. Wysocki * The 'wakeup_count' attribute, along with the functions defined in
754c125e96fSRafael J. Wysocki * drivers/base/power/wakeup.c, provides a means by which wakeup events can be
755c125e96fSRafael J. Wysocki * handled in a non-racy way.
756c125e96fSRafael J. Wysocki *
757c125e96fSRafael J. Wysocki * If a wakeup event occurs when the system is in a sleep state, it simply is
758c125e96fSRafael J. Wysocki * woken up. In turn, if an event that would wake the system up from a sleep
759c125e96fSRafael J. Wysocki * state occurs when it is undergoing a transition to that sleep state, the
760c125e96fSRafael J. Wysocki * transition should be aborted. Moreover, if such an event occurs when the
761c125e96fSRafael J. Wysocki * system is in the working state, an attempt to start a transition to the
762c125e96fSRafael J. Wysocki * given sleep state should fail during certain period after the detection of
763c125e96fSRafael J. Wysocki * the event. Using the 'state' attribute alone is not sufficient to satisfy
764c125e96fSRafael J. Wysocki * these requirements, because a wakeup event may occur exactly when 'state'
765c125e96fSRafael J. Wysocki * is being written to and may be delivered to user space right before it is
766c125e96fSRafael J. Wysocki * frozen, so the event will remain only partially processed until the system is
767c125e96fSRafael J. Wysocki * woken up by another event. In particular, it won't cause the transition to
768c125e96fSRafael J. Wysocki * a sleep state to be aborted.
769c125e96fSRafael J. Wysocki *
770c125e96fSRafael J. Wysocki * This difficulty may be overcome if user space uses 'wakeup_count' before
771c125e96fSRafael J. Wysocki * writing to 'state'. It first should read from 'wakeup_count' and store
772c125e96fSRafael J. Wysocki * the read value. Then, after carrying out its own preparations for the system
773c125e96fSRafael J. Wysocki * transition to a sleep state, it should write the stored value to
77425985edcSLucas De Marchi * 'wakeup_count'. If that fails, at least one wakeup event has occurred since
775c125e96fSRafael J. Wysocki * 'wakeup_count' was read and 'state' should not be written to. Otherwise, it
776c125e96fSRafael J. Wysocki * is allowed to write to 'state', but the transition will be aborted if there
777c125e96fSRafael J. Wysocki * are any wakeup events detected after 'wakeup_count' was written to.
778c125e96fSRafael J. Wysocki */
779c125e96fSRafael J. Wysocki
wakeup_count_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)780c125e96fSRafael J. Wysocki static ssize_t wakeup_count_show(struct kobject *kobj,
781c125e96fSRafael J. Wysocki struct kobj_attribute *attr,
782c125e96fSRafael J. Wysocki char *buf)
783c125e96fSRafael J. Wysocki {
784074037ecSRafael J. Wysocki unsigned int val;
785c125e96fSRafael J. Wysocki
7867483b4a4SRafael J. Wysocki return pm_get_wakeup_count(&val, true) ?
787*a712f03fSXueqin Luo sysfs_emit(buf, "%u\n", val) : -EINTR;
788c125e96fSRafael J. Wysocki }
789c125e96fSRafael J. Wysocki
wakeup_count_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t n)790c125e96fSRafael J. Wysocki static ssize_t wakeup_count_store(struct kobject *kobj,
791c125e96fSRafael J. Wysocki struct kobj_attribute *attr,
792c125e96fSRafael J. Wysocki const char *buf, size_t n)
793c125e96fSRafael J. Wysocki {
794074037ecSRafael J. Wysocki unsigned int val;
7957483b4a4SRafael J. Wysocki int error;
796c125e96fSRafael J. Wysocki
7977483b4a4SRafael J. Wysocki error = pm_autosleep_lock();
7987483b4a4SRafael J. Wysocki if (error)
7997483b4a4SRafael J. Wysocki return error;
8007483b4a4SRafael J. Wysocki
8017483b4a4SRafael J. Wysocki if (pm_autosleep_state() > PM_SUSPEND_ON) {
8027483b4a4SRafael J. Wysocki error = -EBUSY;
8037483b4a4SRafael J. Wysocki goto out;
8047483b4a4SRafael J. Wysocki }
8057483b4a4SRafael J. Wysocki
8067483b4a4SRafael J. Wysocki error = -EINVAL;
807074037ecSRafael J. Wysocki if (sscanf(buf, "%u", &val) == 1) {
808c125e96fSRafael J. Wysocki if (pm_save_wakeup_count(val))
8097483b4a4SRafael J. Wysocki error = n;
810bb177fedSJulius Werner else
811bb177fedSJulius Werner pm_print_active_wakeup_sources();
812c125e96fSRafael J. Wysocki }
8137483b4a4SRafael J. Wysocki
8147483b4a4SRafael J. Wysocki out:
8157483b4a4SRafael J. Wysocki pm_autosleep_unlock();
8167483b4a4SRafael J. Wysocki return error;
817c125e96fSRafael J. Wysocki }
818c125e96fSRafael J. Wysocki
819c125e96fSRafael J. Wysocki power_attr(wakeup_count);
8207483b4a4SRafael J. Wysocki
8217483b4a4SRafael J. Wysocki #ifdef CONFIG_PM_AUTOSLEEP
autosleep_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)8227483b4a4SRafael J. Wysocki static ssize_t autosleep_show(struct kobject *kobj,
8237483b4a4SRafael J. Wysocki struct kobj_attribute *attr,
8247483b4a4SRafael J. Wysocki char *buf)
8257483b4a4SRafael J. Wysocki {
8267483b4a4SRafael J. Wysocki suspend_state_t state = pm_autosleep_state();
8277483b4a4SRafael J. Wysocki
8287483b4a4SRafael J. Wysocki if (state == PM_SUSPEND_ON)
829*a712f03fSXueqin Luo return sysfs_emit(buf, "off\n");
8307483b4a4SRafael J. Wysocki
8317483b4a4SRafael J. Wysocki #ifdef CONFIG_SUSPEND
8327483b4a4SRafael J. Wysocki if (state < PM_SUSPEND_MAX)
833*a712f03fSXueqin Luo return sysfs_emit(buf, "%s\n", pm_states[state] ?
834d431cbc5SRafael J. Wysocki pm_states[state] : "error");
8357483b4a4SRafael J. Wysocki #endif
8367483b4a4SRafael J. Wysocki #ifdef CONFIG_HIBERNATION
837*a712f03fSXueqin Luo return sysfs_emit(buf, "disk\n");
8387483b4a4SRafael J. Wysocki #else
839*a712f03fSXueqin Luo return sysfs_emit(buf, "error\n");
8407483b4a4SRafael J. Wysocki #endif
8417483b4a4SRafael J. Wysocki }
8427483b4a4SRafael J. Wysocki
autosleep_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t n)8437483b4a4SRafael J. Wysocki static ssize_t autosleep_store(struct kobject *kobj,
8447483b4a4SRafael J. Wysocki struct kobj_attribute *attr,
8457483b4a4SRafael J. Wysocki const char *buf, size_t n)
8467483b4a4SRafael J. Wysocki {
8477483b4a4SRafael J. Wysocki suspend_state_t state = decode_state(buf, n);
8487483b4a4SRafael J. Wysocki int error;
8497483b4a4SRafael J. Wysocki
8507483b4a4SRafael J. Wysocki if (state == PM_SUSPEND_ON
851040e5bf6SArve Hjønnevåg && strcmp(buf, "off") && strcmp(buf, "off\n"))
8527483b4a4SRafael J. Wysocki return -EINVAL;
8537483b4a4SRafael J. Wysocki
854406e7938SRafael J. Wysocki if (state == PM_SUSPEND_MEM)
855406e7938SRafael J. Wysocki state = mem_sleep_current;
856406e7938SRafael J. Wysocki
8577483b4a4SRafael J. Wysocki error = pm_autosleep_set_state(state);
8587483b4a4SRafael J. Wysocki return error ? error : n;
8597483b4a4SRafael J. Wysocki }
8607483b4a4SRafael J. Wysocki
8617483b4a4SRafael J. Wysocki power_attr(autosleep);
8627483b4a4SRafael J. Wysocki #endif /* CONFIG_PM_AUTOSLEEP */
863b86ff982SRafael J. Wysocki
864b86ff982SRafael J. Wysocki #ifdef CONFIG_PM_WAKELOCKS
wake_lock_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)865b86ff982SRafael J. Wysocki static ssize_t wake_lock_show(struct kobject *kobj,
866b86ff982SRafael J. Wysocki struct kobj_attribute *attr,
867b86ff982SRafael J. Wysocki char *buf)
868b86ff982SRafael J. Wysocki {
869b86ff982SRafael J. Wysocki return pm_show_wakelocks(buf, true);
870b86ff982SRafael J. Wysocki }
871b86ff982SRafael J. Wysocki
wake_lock_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t n)872b86ff982SRafael J. Wysocki static ssize_t wake_lock_store(struct kobject *kobj,
873b86ff982SRafael J. Wysocki struct kobj_attribute *attr,
874b86ff982SRafael J. Wysocki const char *buf, size_t n)
875b86ff982SRafael J. Wysocki {
876b86ff982SRafael J. Wysocki int error = pm_wake_lock(buf);
877b86ff982SRafael J. Wysocki return error ? error : n;
878b86ff982SRafael J. Wysocki }
879b86ff982SRafael J. Wysocki
880b86ff982SRafael J. Wysocki power_attr(wake_lock);
881b86ff982SRafael J. Wysocki
wake_unlock_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)882b86ff982SRafael J. Wysocki static ssize_t wake_unlock_show(struct kobject *kobj,
883b86ff982SRafael J. Wysocki struct kobj_attribute *attr,
884b86ff982SRafael J. Wysocki char *buf)
885b86ff982SRafael J. Wysocki {
886b86ff982SRafael J. Wysocki return pm_show_wakelocks(buf, false);
887b86ff982SRafael J. Wysocki }
888b86ff982SRafael J. Wysocki
wake_unlock_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t n)889b86ff982SRafael J. Wysocki static ssize_t wake_unlock_store(struct kobject *kobj,
890b86ff982SRafael J. Wysocki struct kobj_attribute *attr,
891b86ff982SRafael J. Wysocki const char *buf, size_t n)
892b86ff982SRafael J. Wysocki {
893b86ff982SRafael J. Wysocki int error = pm_wake_unlock(buf);
894b86ff982SRafael J. Wysocki return error ? error : n;
895b86ff982SRafael J. Wysocki }
896b86ff982SRafael J. Wysocki
897b86ff982SRafael J. Wysocki power_attr(wake_unlock);
898b86ff982SRafael J. Wysocki
899b86ff982SRafael J. Wysocki #endif /* CONFIG_PM_WAKELOCKS */
900c125e96fSRafael J. Wysocki #endif /* CONFIG_PM_SLEEP */
901c125e96fSRafael J. Wysocki
902c5c6ba4eSRafael J. Wysocki #ifdef CONFIG_PM_TRACE
903c5c6ba4eSRafael J. Wysocki int pm_trace_enabled;
904c5c6ba4eSRafael J. Wysocki
pm_trace_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)905386f275fSKay Sievers static ssize_t pm_trace_show(struct kobject *kobj, struct kobj_attribute *attr,
906386f275fSKay Sievers char *buf)
907c5c6ba4eSRafael J. Wysocki {
908*a712f03fSXueqin Luo return sysfs_emit(buf, "%d\n", pm_trace_enabled);
909c5c6ba4eSRafael J. Wysocki }
910c5c6ba4eSRafael J. Wysocki
911c5c6ba4eSRafael J. Wysocki static ssize_t
pm_trace_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t n)912386f275fSKay Sievers pm_trace_store(struct kobject *kobj, struct kobj_attribute *attr,
913386f275fSKay Sievers const char *buf, size_t n)
914c5c6ba4eSRafael J. Wysocki {
915c5c6ba4eSRafael J. Wysocki int val;
916c5c6ba4eSRafael J. Wysocki
917c5c6ba4eSRafael J. Wysocki if (sscanf(buf, "%d", &val) == 1) {
918c5c6ba4eSRafael J. Wysocki pm_trace_enabled = !!val;
9199dceefe4SShuah Khan if (pm_trace_enabled) {
9209dceefe4SShuah Khan pr_warn("PM: Enabling pm_trace changes system date and time during resume.\n"
9219dceefe4SShuah Khan "PM: Correct system time has to be restored manually after resume.\n");
9229dceefe4SShuah Khan }
923c5c6ba4eSRafael J. Wysocki return n;
924c5c6ba4eSRafael J. Wysocki }
925c5c6ba4eSRafael J. Wysocki return -EINVAL;
926c5c6ba4eSRafael J. Wysocki }
927c5c6ba4eSRafael J. Wysocki
928c5c6ba4eSRafael J. Wysocki power_attr(pm_trace);
929d33ac60bSJames Hogan
pm_trace_dev_match_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)930d33ac60bSJames Hogan static ssize_t pm_trace_dev_match_show(struct kobject *kobj,
931d33ac60bSJames Hogan struct kobj_attribute *attr,
932d33ac60bSJames Hogan char *buf)
933d33ac60bSJames Hogan {
934d33ac60bSJames Hogan return show_trace_dev_match(buf, PAGE_SIZE);
935d33ac60bSJames Hogan }
936d33ac60bSJames Hogan
937a1e9ca69SRafael J. Wysocki power_attr_ro(pm_trace_dev_match);
938d33ac60bSJames Hogan
9390e7d56e3SRafael J. Wysocki #endif /* CONFIG_PM_TRACE */
940c5c6ba4eSRafael J. Wysocki
941957d1282SLi Fei #ifdef CONFIG_FREEZER
pm_freeze_timeout_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)942957d1282SLi Fei static ssize_t pm_freeze_timeout_show(struct kobject *kobj,
943957d1282SLi Fei struct kobj_attribute *attr, char *buf)
944957d1282SLi Fei {
945*a712f03fSXueqin Luo return sysfs_emit(buf, "%u\n", freeze_timeout_msecs);
946957d1282SLi Fei }
947957d1282SLi Fei
pm_freeze_timeout_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t n)948957d1282SLi Fei static ssize_t pm_freeze_timeout_store(struct kobject *kobj,
949957d1282SLi Fei struct kobj_attribute *attr,
950957d1282SLi Fei const char *buf, size_t n)
951957d1282SLi Fei {
952957d1282SLi Fei unsigned long val;
953957d1282SLi Fei
954957d1282SLi Fei if (kstrtoul(buf, 10, &val))
955957d1282SLi Fei return -EINVAL;
956957d1282SLi Fei
957957d1282SLi Fei freeze_timeout_msecs = val;
958957d1282SLi Fei return n;
959957d1282SLi Fei }
960957d1282SLi Fei
961957d1282SLi Fei power_attr(pm_freeze_timeout);
962957d1282SLi Fei
963957d1282SLi Fei #endif /* CONFIG_FREEZER*/
964957d1282SLi Fei
965c5c6ba4eSRafael J. Wysocki static struct attribute * g[] = {
966c5c6ba4eSRafael J. Wysocki &state_attr.attr,
9670e7d56e3SRafael J. Wysocki #ifdef CONFIG_PM_TRACE
968c5c6ba4eSRafael J. Wysocki &pm_trace_attr.attr,
969d33ac60bSJames Hogan &pm_trace_dev_match_attr.attr,
9700e7d56e3SRafael J. Wysocki #endif
9710e06b4a8SRafael J. Wysocki #ifdef CONFIG_PM_SLEEP
9720e06b4a8SRafael J. Wysocki &pm_async_attr.attr,
973c125e96fSRafael J. Wysocki &wakeup_count_attr.attr,
974406e7938SRafael J. Wysocki #ifdef CONFIG_SUSPEND
975406e7938SRafael J. Wysocki &mem_sleep_attr.attr,
976c052bf82SJonas Meurer &sync_on_suspend_attr.attr,
977406e7938SRafael J. Wysocki #endif
9787483b4a4SRafael J. Wysocki #ifdef CONFIG_PM_AUTOSLEEP
9797483b4a4SRafael J. Wysocki &autosleep_attr.attr,
9807483b4a4SRafael J. Wysocki #endif
981b86ff982SRafael J. Wysocki #ifdef CONFIG_PM_WAKELOCKS
982b86ff982SRafael J. Wysocki &wake_lock_attr.attr,
983b86ff982SRafael J. Wysocki &wake_unlock_attr.attr,
984b86ff982SRafael J. Wysocki #endif
985b2df1d4fSRafael J. Wysocki #ifdef CONFIG_PM_SLEEP_DEBUG
986e516a1dbSRafael J. Wysocki &pm_test_attr.attr,
9874b7760baSSameer Nanda &pm_print_times_attr.attr,
988a6f5f0ddSAlexandra Yates &pm_wakeup_irq_attr.attr,
9898d8b2441SRafael J. Wysocki &pm_debug_messages_attr.attr,
9900e7d56e3SRafael J. Wysocki #endif
9910e06b4a8SRafael J. Wysocki #endif
992957d1282SLi Fei #ifdef CONFIG_FREEZER
993957d1282SLi Fei &pm_freeze_timeout_attr.attr,
994957d1282SLi Fei #endif
995c5c6ba4eSRafael J. Wysocki NULL,
996c5c6ba4eSRafael J. Wysocki };
9971da177e4SLinus Torvalds
9981d0c6e59SArvind Yadav static const struct attribute_group attr_group = {
9991da177e4SLinus Torvalds .attrs = g,
10001da177e4SLinus Torvalds };
10011da177e4SLinus Torvalds
10022c8db5beSKalesh Singh static const struct attribute_group *attr_groups[] = {
10032c8db5beSKalesh Singh &attr_group,
10042c8db5beSKalesh Singh #ifdef CONFIG_PM_SLEEP
10052c8db5beSKalesh Singh &suspend_attr_group,
10062c8db5beSKalesh Singh #endif
10072c8db5beSKalesh Singh NULL,
10082c8db5beSKalesh Singh };
10092c8db5beSKalesh Singh
10105e928f77SRafael J. Wysocki struct workqueue_struct *pm_wq;
10117b199ca2SAlan Stern EXPORT_SYMBOL_GPL(pm_wq);
10125e928f77SRafael J. Wysocki
pm_start_workqueue(void)10135e928f77SRafael J. Wysocki static int __init pm_start_workqueue(void)
10145e928f77SRafael J. Wysocki {
101558a69cb4STejun Heo pm_wq = alloc_workqueue("pm", WQ_FREEZABLE, 0);
10165e928f77SRafael J. Wysocki
10175e928f77SRafael J. Wysocki return pm_wq ? 0 : -ENOMEM;
10185e928f77SRafael J. Wysocki }
10195e928f77SRafael J. Wysocki
pm_init(void)10201da177e4SLinus Torvalds static int __init pm_init(void)
10211da177e4SLinus Torvalds {
10225e928f77SRafael J. Wysocki int error = pm_start_workqueue();
10235e928f77SRafael J. Wysocki if (error)
10245e928f77SRafael J. Wysocki return error;
1025ac5c24ecSRafael J. Wysocki hibernate_image_size_init();
1026ddeb6487SRafael J. Wysocki hibernate_reserved_size_init();
1027fa7fd6faSSudeep Holla pm_states_init();
1028d76e15fbSGreg Kroah-Hartman power_kobj = kobject_create_and_add("power", NULL);
1029d76e15fbSGreg Kroah-Hartman if (!power_kobj)
1030039a5dcdSGreg Kroah-Hartman return -ENOMEM;
10312c8db5beSKalesh Singh error = sysfs_create_groups(power_kobj, attr_groups);
10327483b4a4SRafael J. Wysocki if (error)
10337483b4a4SRafael J. Wysocki return error;
1034b2df1d4fSRafael J. Wysocki pm_print_times_init();
10357483b4a4SRafael J. Wysocki return pm_autosleep_init();
10361da177e4SLinus Torvalds }
10371da177e4SLinus Torvalds
10381da177e4SLinus Torvalds core_initcall(pm_init);
1039