15de363b6SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * drivers/base/power/main.c - Where the driver meets power management.
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * Copyright (c) 2003 Patrick Mochel
61da177e4SLinus Torvalds * Copyright (c) 2003 Open Source Development Lab
71da177e4SLinus Torvalds *
81da177e4SLinus Torvalds * The driver model core calls device_pm_add() when a device is registered.
9b595076aSUwe Kleine-König * This will initialize the embedded device_pm_info object in the device
101da177e4SLinus Torvalds * and add it to the list of power-controlled devices. sysfs entries for
111da177e4SLinus Torvalds * controlling device power management will also be added.
121da177e4SLinus Torvalds *
131eede070SRafael J. Wysocki * A separate list is used for keeping track of power info, because the power
141eede070SRafael J. Wysocki * domain dependencies may differ from the ancestral dependencies that the
151eede070SRafael J. Wysocki * subsystem list maintains.
161da177e4SLinus Torvalds */
171da177e4SLinus Torvalds
187a5bd127SJoe Perches #define pr_fmt(fmt) "PM: " fmt
19eb23d91aSBjorn Helgaas #define dev_fmt pr_fmt
207a5bd127SJoe Perches
211da177e4SLinus Torvalds #include <linux/device.h>
221b6bc32fSPaul Gortmaker #include <linux/export.h>
2311048dcfSMatthias Kaehlcke #include <linux/mutex.h>
24cd59abfcSAlan Stern #include <linux/pm.h>
255e928f77SRafael J. Wysocki #include <linux/pm_runtime.h>
26431d452aSZhonghui Fu #include <linux/pm-trace.h>
274990d4feSTony Lindgren #include <linux/pm_wakeirq.h>
282ed8d2b3SRafael J. Wysocki #include <linux/interrupt.h>
29f2511774SArjan van de Ven #include <linux/sched.h>
30b17b0153SIngo Molnar #include <linux/sched/debug.h>
315af84b82SRafael J. Wysocki #include <linux/async.h>
321e75227eSRafael J. Wysocki #include <linux/suspend.h>
3353644677SShuah Khan #include <trace/events/power.h>
342f0aea93SViresh Kumar #include <linux/cpufreq.h>
356e863844SLukasz Luba #include <linux/devfreq.h>
3670fea60dSBenoit Goby #include <linux/timer.h>
3770fea60dSBenoit Goby
38cd59abfcSAlan Stern #include "../base.h"
391da177e4SLinus Torvalds #include "power.h"
401da177e4SLinus Torvalds
419cf519d1SRafael J. Wysocki typedef int (*pm_callback_t)(struct device *);
429cf519d1SRafael J. Wysocki
4342beb82eSMadhuparna Bhowmik #define list_for_each_entry_rcu_locked(pos, head, member) \
4442beb82eSMadhuparna Bhowmik list_for_each_entry_rcu(pos, head, member, \
4542beb82eSMadhuparna Bhowmik device_links_read_lock_held())
4642beb82eSMadhuparna Bhowmik
47775b64d2SRafael J. Wysocki /*
481eede070SRafael J. Wysocki * The entries in the dpm_list list are in a depth first order, simply
49775b64d2SRafael J. Wysocki * because children are guaranteed to be discovered after parents, and
50775b64d2SRafael J. Wysocki * are inserted at the back of the list on discovery.
51775b64d2SRafael J. Wysocki *
528e9394ceSGreg Kroah-Hartman * Since device_pm_add() may be called with a device lock held,
538e9394ceSGreg Kroah-Hartman * we must never try to acquire a device lock while holding
54775b64d2SRafael J. Wysocki * dpm_list_mutex.
55775b64d2SRafael J. Wysocki */
56775b64d2SRafael J. Wysocki
571eede070SRafael J. Wysocki LIST_HEAD(dpm_list);
587664e969SSachin Kamat static LIST_HEAD(dpm_prepared_list);
597664e969SSachin Kamat static LIST_HEAD(dpm_suspended_list);
607664e969SSachin Kamat static LIST_HEAD(dpm_late_early_list);
617664e969SSachin Kamat static LIST_HEAD(dpm_noirq_list);
621da177e4SLinus Torvalds
632a77c46dSShuoX Liu struct suspend_stats suspend_stats;
64cd59abfcSAlan Stern static DEFINE_MUTEX(dpm_list_mtx);
655af84b82SRafael J. Wysocki static pm_message_t pm_transition;
661da177e4SLinus Torvalds
67098dff73SRafael J. Wysocki static int async_error;
68098dff73SRafael J. Wysocki
pm_verb(int event)69952856dbSKrzysztof Kozlowski static const char *pm_verb(int event)
7053644677SShuah Khan {
7153644677SShuah Khan switch (event) {
7253644677SShuah Khan case PM_EVENT_SUSPEND:
7353644677SShuah Khan return "suspend";
7453644677SShuah Khan case PM_EVENT_RESUME:
7553644677SShuah Khan return "resume";
7653644677SShuah Khan case PM_EVENT_FREEZE:
7753644677SShuah Khan return "freeze";
7853644677SShuah Khan case PM_EVENT_QUIESCE:
7953644677SShuah Khan return "quiesce";
8053644677SShuah Khan case PM_EVENT_HIBERNATE:
8153644677SShuah Khan return "hibernate";
8253644677SShuah Khan case PM_EVENT_THAW:
8353644677SShuah Khan return "thaw";
8453644677SShuah Khan case PM_EVENT_RESTORE:
8553644677SShuah Khan return "restore";
8653644677SShuah Khan case PM_EVENT_RECOVER:
8753644677SShuah Khan return "recover";
8853644677SShuah Khan default:
8953644677SShuah Khan return "(unknown PM event)";
9053644677SShuah Khan }
9153644677SShuah Khan }
9253644677SShuah Khan
931eede070SRafael J. Wysocki /**
94e91c11b1SRafael J. Wysocki * device_pm_sleep_init - Initialize system suspend-related device fields.
955e928f77SRafael J. Wysocki * @dev: Device object being initialized.
965e928f77SRafael J. Wysocki */
device_pm_sleep_init(struct device * dev)97e91c11b1SRafael J. Wysocki void device_pm_sleep_init(struct device *dev)
985e928f77SRafael J. Wysocki {
99f76b168bSAlan Stern dev->power.is_prepared = false;
1006d0e0e84SAlan Stern dev->power.is_suspended = false;
1013d2699bcSLiu, Chuansheng dev->power.is_noirq_suspended = false;
1023d2699bcSLiu, Chuansheng dev->power.is_late_suspended = false;
1035af84b82SRafael J. Wysocki init_completion(&dev->power.completion);
104152e1d59SColin Cross complete_all(&dev->power.completion);
105074037ecSRafael J. Wysocki dev->power.wakeup = NULL;
10622110fafSRafael J. Wysocki INIT_LIST_HEAD(&dev->power.entry);
1075e928f77SRafael J. Wysocki }
1085e928f77SRafael J. Wysocki
1095e928f77SRafael J. Wysocki /**
11020d652d7SRafael J. Wysocki * device_pm_lock - Lock the list of active devices used by the PM core.
1111eede070SRafael J. Wysocki */
device_pm_lock(void)1121eede070SRafael J. Wysocki void device_pm_lock(void)
1131eede070SRafael J. Wysocki {
1141eede070SRafael J. Wysocki mutex_lock(&dpm_list_mtx);
1151eede070SRafael J. Wysocki }
1161eede070SRafael J. Wysocki
1171eede070SRafael J. Wysocki /**
11820d652d7SRafael J. Wysocki * device_pm_unlock - Unlock the list of active devices used by the PM core.
1191eede070SRafael J. Wysocki */
device_pm_unlock(void)1201eede070SRafael J. Wysocki void device_pm_unlock(void)
1211eede070SRafael J. Wysocki {
1221eede070SRafael J. Wysocki mutex_unlock(&dpm_list_mtx);
1231eede070SRafael J. Wysocki }
124775b64d2SRafael J. Wysocki
125775b64d2SRafael J. Wysocki /**
12620d652d7SRafael J. Wysocki * device_pm_add - Add a device to the PM core's list of active devices.
12720d652d7SRafael J. Wysocki * @dev: Device to add to the list.
128775b64d2SRafael J. Wysocki */
device_pm_add(struct device * dev)1293b98aeafSAlan Stern void device_pm_add(struct device *dev)
1301da177e4SLinus Torvalds {
13185945c28SSudeep Holla /* Skip PM setup/initialization. */
13285945c28SSudeep Holla if (device_pm_not_required(dev))
13385945c28SSudeep Holla return;
13485945c28SSudeep Holla
1357a5bd127SJoe Perches pr_debug("Adding info for %s:%s\n",
1365c1a07abSRafael J. Wysocki dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
137aa8e54b5STomeu Vizoso device_pm_check_callbacks(dev);
13811048dcfSMatthias Kaehlcke mutex_lock(&dpm_list_mtx);
139f76b168bSAlan Stern if (dev->parent && dev->parent->power.is_prepared)
140f5a6d958SRafael J. Wysocki dev_warn(dev, "parent %s should not be sleeping\n",
1411e0b2cf9SKay Sievers dev_name(dev->parent));
1421eede070SRafael J. Wysocki list_add_tail(&dev->power.entry, &dpm_list);
1439ed98953SRafael J. Wysocki dev->power.in_dpm_list = true;
1441a9a9152SRafael J. Wysocki mutex_unlock(&dpm_list_mtx);
1451da177e4SLinus Torvalds }
1461da177e4SLinus Torvalds
147775b64d2SRafael J. Wysocki /**
14820d652d7SRafael J. Wysocki * device_pm_remove - Remove a device from the PM core's list of active devices.
14920d652d7SRafael J. Wysocki * @dev: Device to be removed from the list.
150775b64d2SRafael J. Wysocki */
device_pm_remove(struct device * dev)1511da177e4SLinus Torvalds void device_pm_remove(struct device *dev)
1521da177e4SLinus Torvalds {
15385945c28SSudeep Holla if (device_pm_not_required(dev))
15485945c28SSudeep Holla return;
15585945c28SSudeep Holla
1567a5bd127SJoe Perches pr_debug("Removing info for %s:%s\n",
1575c1a07abSRafael J. Wysocki dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
1585af84b82SRafael J. Wysocki complete_all(&dev->power.completion);
15911048dcfSMatthias Kaehlcke mutex_lock(&dpm_list_mtx);
1601da177e4SLinus Torvalds list_del_init(&dev->power.entry);
1619ed98953SRafael J. Wysocki dev->power.in_dpm_list = false;
16211048dcfSMatthias Kaehlcke mutex_unlock(&dpm_list_mtx);
163074037ecSRafael J. Wysocki device_wakeup_disable(dev);
1645e928f77SRafael J. Wysocki pm_runtime_remove(dev);
165aa8e54b5STomeu Vizoso device_pm_check_callbacks(dev);
166775b64d2SRafael J. Wysocki }
167775b64d2SRafael J. Wysocki
1681eede070SRafael J. Wysocki /**
16920d652d7SRafael J. Wysocki * device_pm_move_before - Move device in the PM core's list of active devices.
17020d652d7SRafael J. Wysocki * @deva: Device to move in dpm_list.
17120d652d7SRafael J. Wysocki * @devb: Device @deva should come before.
172ffa6a705SCornelia Huck */
device_pm_move_before(struct device * deva,struct device * devb)173ffa6a705SCornelia Huck void device_pm_move_before(struct device *deva, struct device *devb)
174ffa6a705SCornelia Huck {
1757a5bd127SJoe Perches pr_debug("Moving %s:%s before %s:%s\n",
1765c1a07abSRafael J. Wysocki deva->bus ? deva->bus->name : "No Bus", dev_name(deva),
1775c1a07abSRafael J. Wysocki devb->bus ? devb->bus->name : "No Bus", dev_name(devb));
178ffa6a705SCornelia Huck /* Delete deva from dpm_list and reinsert before devb. */
179ffa6a705SCornelia Huck list_move_tail(&deva->power.entry, &devb->power.entry);
180ffa6a705SCornelia Huck }
181ffa6a705SCornelia Huck
182ffa6a705SCornelia Huck /**
18320d652d7SRafael J. Wysocki * device_pm_move_after - Move device in the PM core's list of active devices.
18420d652d7SRafael J. Wysocki * @deva: Device to move in dpm_list.
18520d652d7SRafael J. Wysocki * @devb: Device @deva should come after.
186ffa6a705SCornelia Huck */
device_pm_move_after(struct device * deva,struct device * devb)187ffa6a705SCornelia Huck void device_pm_move_after(struct device *deva, struct device *devb)
188ffa6a705SCornelia Huck {
1897a5bd127SJoe Perches pr_debug("Moving %s:%s after %s:%s\n",
1905c1a07abSRafael J. Wysocki deva->bus ? deva->bus->name : "No Bus", dev_name(deva),
1915c1a07abSRafael J. Wysocki devb->bus ? devb->bus->name : "No Bus", dev_name(devb));
192ffa6a705SCornelia Huck /* Delete deva from dpm_list and reinsert after devb. */
193ffa6a705SCornelia Huck list_move(&deva->power.entry, &devb->power.entry);
194ffa6a705SCornelia Huck }
195ffa6a705SCornelia Huck
196ffa6a705SCornelia Huck /**
19720d652d7SRafael J. Wysocki * device_pm_move_last - Move device to end of the PM core's list of devices.
19820d652d7SRafael J. Wysocki * @dev: Device to move in dpm_list.
199ffa6a705SCornelia Huck */
device_pm_move_last(struct device * dev)200ffa6a705SCornelia Huck void device_pm_move_last(struct device *dev)
201ffa6a705SCornelia Huck {
2027a5bd127SJoe Perches pr_debug("Moving %s:%s to end of list\n",
2035c1a07abSRafael J. Wysocki dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
204ffa6a705SCornelia Huck list_move_tail(&dev->power.entry, &dpm_list);
205ffa6a705SCornelia Huck }
206ffa6a705SCornelia Huck
initcall_debug_start(struct device * dev,void * cb)2077f817ba9SBjorn Helgaas static ktime_t initcall_debug_start(struct device *dev, void *cb)
208875ab0b7SRafael J. Wysocki {
209143711f0SBjorn Helgaas if (!pm_print_times_enabled)
210143711f0SBjorn Helgaas return 0;
211875ab0b7SRafael J. Wysocki
212d75f773cSSakari Ailus dev_info(dev, "calling %pS @ %i, parent: %s\n", cb,
2137f817ba9SBjorn Helgaas task_pid_nr(current),
2140c6aebe3SRafael J. Wysocki dev->parent ? dev_name(dev->parent) : "none");
215143711f0SBjorn Helgaas return ktime_get();
216875ab0b7SRafael J. Wysocki }
217875ab0b7SRafael J. Wysocki
initcall_debug_report(struct device * dev,ktime_t calltime,void * cb,int error)218875ab0b7SRafael J. Wysocki static void initcall_debug_report(struct device *dev, ktime_t calltime,
2197f817ba9SBjorn Helgaas void *cb, int error)
220875ab0b7SRafael J. Wysocki {
22153644677SShuah Khan ktime_t rettime;
22253644677SShuah Khan
223143711f0SBjorn Helgaas if (!pm_print_times_enabled)
224143711f0SBjorn Helgaas return;
225143711f0SBjorn Helgaas
22653644677SShuah Khan rettime = ktime_get();
227d75f773cSSakari Ailus dev_info(dev, "%pS returned %d after %Ld usecs\n", cb, error,
22875674eb0SMark-PK Tsai (unsigned long long)ktime_us_delta(rettime, calltime));
229875ab0b7SRafael J. Wysocki }
230875ab0b7SRafael J. Wysocki
231ffa6a705SCornelia Huck /**
2325af84b82SRafael J. Wysocki * dpm_wait - Wait for a PM operation to complete.
2335af84b82SRafael J. Wysocki * @dev: Device to wait for.
2345af84b82SRafael J. Wysocki * @async: If unset, wait only if the device's power.async_suspend flag is set.
2355af84b82SRafael J. Wysocki */
dpm_wait(struct device * dev,bool async)2365af84b82SRafael J. Wysocki static void dpm_wait(struct device *dev, bool async)
2375af84b82SRafael J. Wysocki {
2385af84b82SRafael J. Wysocki if (!dev)
2395af84b82SRafael J. Wysocki return;
2405af84b82SRafael J. Wysocki
2410e06b4a8SRafael J. Wysocki if (async || (pm_async_enabled && dev->power.async_suspend))
2425af84b82SRafael J. Wysocki wait_for_completion(&dev->power.completion);
2435af84b82SRafael J. Wysocki }
2445af84b82SRafael J. Wysocki
dpm_wait_fn(struct device * dev,void * async_ptr)2455af84b82SRafael J. Wysocki static int dpm_wait_fn(struct device *dev, void *async_ptr)
2465af84b82SRafael J. Wysocki {
2475af84b82SRafael J. Wysocki dpm_wait(dev, *((bool *)async_ptr));
2485af84b82SRafael J. Wysocki return 0;
2495af84b82SRafael J. Wysocki }
2505af84b82SRafael J. Wysocki
dpm_wait_for_children(struct device * dev,bool async)2515af84b82SRafael J. Wysocki static void dpm_wait_for_children(struct device *dev, bool async)
2525af84b82SRafael J. Wysocki {
2535af84b82SRafael J. Wysocki device_for_each_child(dev, &async, dpm_wait_fn);
2545af84b82SRafael J. Wysocki }
2555af84b82SRafael J. Wysocki
dpm_wait_for_suppliers(struct device * dev,bool async)2568c73b428SRafael J. Wysocki static void dpm_wait_for_suppliers(struct device *dev, bool async)
2578c73b428SRafael J. Wysocki {
2588c73b428SRafael J. Wysocki struct device_link *link;
2598c73b428SRafael J. Wysocki int idx;
2608c73b428SRafael J. Wysocki
2618c73b428SRafael J. Wysocki idx = device_links_read_lock();
2628c73b428SRafael J. Wysocki
2638c73b428SRafael J. Wysocki /*
2648c73b428SRafael J. Wysocki * If the supplier goes away right after we've checked the link to it,
2658c73b428SRafael J. Wysocki * we'll wait for its completion to change the state, but that's fine,
2668c73b428SRafael J. Wysocki * because the only things that will block as a result are the SRCU
2678c73b428SRafael J. Wysocki * callbacks freeing the link objects for the links in the list we're
2688c73b428SRafael J. Wysocki * walking.
2698c73b428SRafael J. Wysocki */
27042beb82eSMadhuparna Bhowmik list_for_each_entry_rcu_locked(link, &dev->links.suppliers, c_node)
2718c73b428SRafael J. Wysocki if (READ_ONCE(link->status) != DL_STATE_DORMANT)
2728c73b428SRafael J. Wysocki dpm_wait(link->supplier, async);
2738c73b428SRafael J. Wysocki
2748c73b428SRafael J. Wysocki device_links_read_unlock(idx);
2758c73b428SRafael J. Wysocki }
2768c73b428SRafael J. Wysocki
dpm_wait_for_superior(struct device * dev,bool async)2770552e05fSRafael J. Wysocki static bool dpm_wait_for_superior(struct device *dev, bool async)
2788c73b428SRafael J. Wysocki {
2790552e05fSRafael J. Wysocki struct device *parent;
2800552e05fSRafael J. Wysocki
2810552e05fSRafael J. Wysocki /*
2820552e05fSRafael J. Wysocki * If the device is resumed asynchronously and the parent's callback
2830552e05fSRafael J. Wysocki * deletes both the device and the parent itself, the parent object may
2840552e05fSRafael J. Wysocki * be freed while this function is running, so avoid that by reference
2850552e05fSRafael J. Wysocki * counting the parent once more unless the device has been deleted
2860552e05fSRafael J. Wysocki * already (in which case return right away).
2870552e05fSRafael J. Wysocki */
2880552e05fSRafael J. Wysocki mutex_lock(&dpm_list_mtx);
2890552e05fSRafael J. Wysocki
2900552e05fSRafael J. Wysocki if (!device_pm_initialized(dev)) {
2910552e05fSRafael J. Wysocki mutex_unlock(&dpm_list_mtx);
2920552e05fSRafael J. Wysocki return false;
2930552e05fSRafael J. Wysocki }
2940552e05fSRafael J. Wysocki
2950552e05fSRafael J. Wysocki parent = get_device(dev->parent);
2960552e05fSRafael J. Wysocki
2970552e05fSRafael J. Wysocki mutex_unlock(&dpm_list_mtx);
2980552e05fSRafael J. Wysocki
2990552e05fSRafael J. Wysocki dpm_wait(parent, async);
3000552e05fSRafael J. Wysocki put_device(parent);
3010552e05fSRafael J. Wysocki
3028c73b428SRafael J. Wysocki dpm_wait_for_suppliers(dev, async);
3030552e05fSRafael J. Wysocki
3040552e05fSRafael J. Wysocki /*
3050552e05fSRafael J. Wysocki * If the parent's callback has deleted the device, attempting to resume
3060552e05fSRafael J. Wysocki * it would be invalid, so avoid doing that then.
3070552e05fSRafael J. Wysocki */
3080552e05fSRafael J. Wysocki return device_pm_initialized(dev);
3098c73b428SRafael J. Wysocki }
3108c73b428SRafael J. Wysocki
dpm_wait_for_consumers(struct device * dev,bool async)3118c73b428SRafael J. Wysocki static void dpm_wait_for_consumers(struct device *dev, bool async)
3128c73b428SRafael J. Wysocki {
3138c73b428SRafael J. Wysocki struct device_link *link;
3148c73b428SRafael J. Wysocki int idx;
3158c73b428SRafael J. Wysocki
3168c73b428SRafael J. Wysocki idx = device_links_read_lock();
3178c73b428SRafael J. Wysocki
3188c73b428SRafael J. Wysocki /*
3198c73b428SRafael J. Wysocki * The status of a device link can only be changed from "dormant" by a
3208c73b428SRafael J. Wysocki * probe, but that cannot happen during system suspend/resume. In
3218c73b428SRafael J. Wysocki * theory it can change to "dormant" at that time, but then it is
3228c73b428SRafael J. Wysocki * reasonable to wait for the target device anyway (eg. if it goes
3238c73b428SRafael J. Wysocki * away, it's better to wait for it to go away completely and then
3248c73b428SRafael J. Wysocki * continue instead of trying to continue in parallel with its
3258c73b428SRafael J. Wysocki * unregistration).
3268c73b428SRafael J. Wysocki */
32742beb82eSMadhuparna Bhowmik list_for_each_entry_rcu_locked(link, &dev->links.consumers, s_node)
3288c73b428SRafael J. Wysocki if (READ_ONCE(link->status) != DL_STATE_DORMANT)
3298c73b428SRafael J. Wysocki dpm_wait(link->consumer, async);
3308c73b428SRafael J. Wysocki
3318c73b428SRafael J. Wysocki device_links_read_unlock(idx);
3328c73b428SRafael J. Wysocki }
3338c73b428SRafael J. Wysocki
dpm_wait_for_subordinate(struct device * dev,bool async)3348c73b428SRafael J. Wysocki static void dpm_wait_for_subordinate(struct device *dev, bool async)
3358c73b428SRafael J. Wysocki {
3368c73b428SRafael J. Wysocki dpm_wait_for_children(dev, async);
3378c73b428SRafael J. Wysocki dpm_wait_for_consumers(dev, async);
3388c73b428SRafael J. Wysocki }
3398c73b428SRafael J. Wysocki
3405af84b82SRafael J. Wysocki /**
3419cf519d1SRafael J. Wysocki * pm_op - Return the PM operation appropriate for given PM event.
3421eede070SRafael J. Wysocki * @ops: PM operations to choose from.
3431eede070SRafael J. Wysocki * @state: PM transition of the system being carried out.
3441eede070SRafael J. Wysocki */
pm_op(const struct dev_pm_ops * ops,pm_message_t state)3459cf519d1SRafael J. Wysocki static pm_callback_t pm_op(const struct dev_pm_ops *ops, pm_message_t state)
3461eede070SRafael J. Wysocki {
3471eede070SRafael J. Wysocki switch (state.event) {
3481eede070SRafael J. Wysocki #ifdef CONFIG_SUSPEND
3491eede070SRafael J. Wysocki case PM_EVENT_SUSPEND:
3509cf519d1SRafael J. Wysocki return ops->suspend;
3511eede070SRafael J. Wysocki case PM_EVENT_RESUME:
3529cf519d1SRafael J. Wysocki return ops->resume;
3531eede070SRafael J. Wysocki #endif /* CONFIG_SUSPEND */
3541f112ceeSRafael J. Wysocki #ifdef CONFIG_HIBERNATE_CALLBACKS
3551eede070SRafael J. Wysocki case PM_EVENT_FREEZE:
3561eede070SRafael J. Wysocki case PM_EVENT_QUIESCE:
3579cf519d1SRafael J. Wysocki return ops->freeze;
3581eede070SRafael J. Wysocki case PM_EVENT_HIBERNATE:
3599cf519d1SRafael J. Wysocki return ops->poweroff;
3601eede070SRafael J. Wysocki case PM_EVENT_THAW:
3611eede070SRafael J. Wysocki case PM_EVENT_RECOVER:
3629cf519d1SRafael J. Wysocki return ops->thaw;
3631eede070SRafael J. Wysocki case PM_EVENT_RESTORE:
3649cf519d1SRafael J. Wysocki return ops->restore;
3651f112ceeSRafael J. Wysocki #endif /* CONFIG_HIBERNATE_CALLBACKS */
3661eede070SRafael J. Wysocki }
367f2511774SArjan van de Ven
3689cf519d1SRafael J. Wysocki return NULL;
3691eede070SRafael J. Wysocki }
3701eede070SRafael J. Wysocki
3711eede070SRafael J. Wysocki /**
372cf579dfbSRafael J. Wysocki * pm_late_early_op - Return the PM operation appropriate for given PM event.
373cf579dfbSRafael J. Wysocki * @ops: PM operations to choose from.
374cf579dfbSRafael J. Wysocki * @state: PM transition of the system being carried out.
375cf579dfbSRafael J. Wysocki *
376cf579dfbSRafael J. Wysocki * Runtime PM is disabled for @dev while this function is being executed.
377cf579dfbSRafael J. Wysocki */
pm_late_early_op(const struct dev_pm_ops * ops,pm_message_t state)378cf579dfbSRafael J. Wysocki static pm_callback_t pm_late_early_op(const struct dev_pm_ops *ops,
379cf579dfbSRafael J. Wysocki pm_message_t state)
380cf579dfbSRafael J. Wysocki {
381cf579dfbSRafael J. Wysocki switch (state.event) {
382cf579dfbSRafael J. Wysocki #ifdef CONFIG_SUSPEND
383cf579dfbSRafael J. Wysocki case PM_EVENT_SUSPEND:
384cf579dfbSRafael J. Wysocki return ops->suspend_late;
385cf579dfbSRafael J. Wysocki case PM_EVENT_RESUME:
386cf579dfbSRafael J. Wysocki return ops->resume_early;
387cf579dfbSRafael J. Wysocki #endif /* CONFIG_SUSPEND */
388cf579dfbSRafael J. Wysocki #ifdef CONFIG_HIBERNATE_CALLBACKS
389cf579dfbSRafael J. Wysocki case PM_EVENT_FREEZE:
390cf579dfbSRafael J. Wysocki case PM_EVENT_QUIESCE:
391cf579dfbSRafael J. Wysocki return ops->freeze_late;
392cf579dfbSRafael J. Wysocki case PM_EVENT_HIBERNATE:
393cf579dfbSRafael J. Wysocki return ops->poweroff_late;
394cf579dfbSRafael J. Wysocki case PM_EVENT_THAW:
395cf579dfbSRafael J. Wysocki case PM_EVENT_RECOVER:
396cf579dfbSRafael J. Wysocki return ops->thaw_early;
397cf579dfbSRafael J. Wysocki case PM_EVENT_RESTORE:
398cf579dfbSRafael J. Wysocki return ops->restore_early;
399cf579dfbSRafael J. Wysocki #endif /* CONFIG_HIBERNATE_CALLBACKS */
400cf579dfbSRafael J. Wysocki }
401cf579dfbSRafael J. Wysocki
402cf579dfbSRafael J. Wysocki return NULL;
403cf579dfbSRafael J. Wysocki }
404cf579dfbSRafael J. Wysocki
405cf579dfbSRafael J. Wysocki /**
4069cf519d1SRafael J. Wysocki * pm_noirq_op - Return the PM operation appropriate for given PM event.
4071eede070SRafael J. Wysocki * @ops: PM operations to choose from.
4081eede070SRafael J. Wysocki * @state: PM transition of the system being carried out.
4091eede070SRafael J. Wysocki *
41020d652d7SRafael J. Wysocki * The driver of @dev will not receive interrupts while this function is being
41120d652d7SRafael J. Wysocki * executed.
4121eede070SRafael J. Wysocki */
pm_noirq_op(const struct dev_pm_ops * ops,pm_message_t state)4139cf519d1SRafael J. Wysocki static pm_callback_t pm_noirq_op(const struct dev_pm_ops *ops, pm_message_t state)
4141eede070SRafael J. Wysocki {
4151eede070SRafael J. Wysocki switch (state.event) {
4161eede070SRafael J. Wysocki #ifdef CONFIG_SUSPEND
4171eede070SRafael J. Wysocki case PM_EVENT_SUSPEND:
4189cf519d1SRafael J. Wysocki return ops->suspend_noirq;
4191eede070SRafael J. Wysocki case PM_EVENT_RESUME:
4209cf519d1SRafael J. Wysocki return ops->resume_noirq;
4211eede070SRafael J. Wysocki #endif /* CONFIG_SUSPEND */
4221f112ceeSRafael J. Wysocki #ifdef CONFIG_HIBERNATE_CALLBACKS
4231eede070SRafael J. Wysocki case PM_EVENT_FREEZE:
4241eede070SRafael J. Wysocki case PM_EVENT_QUIESCE:
4259cf519d1SRafael J. Wysocki return ops->freeze_noirq;
4261eede070SRafael J. Wysocki case PM_EVENT_HIBERNATE:
4279cf519d1SRafael J. Wysocki return ops->poweroff_noirq;
4281eede070SRafael J. Wysocki case PM_EVENT_THAW:
4291eede070SRafael J. Wysocki case PM_EVENT_RECOVER:
4309cf519d1SRafael J. Wysocki return ops->thaw_noirq;
4311eede070SRafael J. Wysocki case PM_EVENT_RESTORE:
4329cf519d1SRafael J. Wysocki return ops->restore_noirq;
4331f112ceeSRafael J. Wysocki #endif /* CONFIG_HIBERNATE_CALLBACKS */
4341eede070SRafael J. Wysocki }
435f2511774SArjan van de Ven
4369cf519d1SRafael J. Wysocki return NULL;
4371eede070SRafael J. Wysocki }
4381eede070SRafael J. Wysocki
pm_dev_dbg(struct device * dev,pm_message_t state,const char * info)439e3771fa9SKrzysztof Kozlowski static void pm_dev_dbg(struct device *dev, pm_message_t state, const char *info)
4401eede070SRafael J. Wysocki {
441f39ee99fSChen Yu dev_dbg(dev, "%s%s%s driver flags: %x\n", info, pm_verb(state.event),
4421eede070SRafael J. Wysocki ((state.event & PM_EVENT_SLEEP) && device_may_wakeup(dev)) ?
443f39ee99fSChen Yu ", may wakeup" : "", dev->power.driver_flags);
4441eede070SRafael J. Wysocki }
4451eede070SRafael J. Wysocki
pm_dev_err(struct device * dev,pm_message_t state,const char * info,int error)446e3771fa9SKrzysztof Kozlowski static void pm_dev_err(struct device *dev, pm_message_t state, const char *info,
4471eede070SRafael J. Wysocki int error)
4481eede070SRafael J. Wysocki {
449eb23d91aSBjorn Helgaas dev_err(dev, "failed to %s%s: error %d\n", pm_verb(state.event), info,
450eb23d91aSBjorn Helgaas error);
4511eede070SRafael J. Wysocki }
4521eede070SRafael J. Wysocki
dpm_show_time(ktime_t starttime,pm_message_t state,int error,const char * info)45348059c09SRafael J. Wysocki static void dpm_show_time(ktime_t starttime, pm_message_t state, int error,
454e3771fa9SKrzysztof Kozlowski const char *info)
455ecf762b2SRafael J. Wysocki {
456ecf762b2SRafael J. Wysocki ktime_t calltime;
4570702d9eeSKevin Cernekee u64 usecs64;
458ecf762b2SRafael J. Wysocki int usecs;
459ecf762b2SRafael J. Wysocki
460ecf762b2SRafael J. Wysocki calltime = ktime_get();
461ecf762b2SRafael J. Wysocki usecs64 = ktime_to_ns(ktime_sub(calltime, starttime));
462ecf762b2SRafael J. Wysocki do_div(usecs64, NSEC_PER_USEC);
463ecf762b2SRafael J. Wysocki usecs = usecs64;
464ecf762b2SRafael J. Wysocki if (usecs == 0)
465ecf762b2SRafael J. Wysocki usecs = 1;
4668d8b2441SRafael J. Wysocki
46748059c09SRafael J. Wysocki pm_pr_dbg("%s%s%s of devices %s after %ld.%03ld msecs\n",
468ecf762b2SRafael J. Wysocki info ?: "", info ? " " : "", pm_verb(state.event),
46948059c09SRafael J. Wysocki error ? "aborted" : "complete",
470ecf762b2SRafael J. Wysocki usecs / USEC_PER_MSEC, usecs % USEC_PER_MSEC);
471ecf762b2SRafael J. Wysocki }
472ecf762b2SRafael J. Wysocki
dpm_run_callback(pm_callback_t cb,struct device * dev,pm_message_t state,const char * info)4739cf519d1SRafael J. Wysocki static int dpm_run_callback(pm_callback_t cb, struct device *dev,
474e3771fa9SKrzysztof Kozlowski pm_message_t state, const char *info)
4759cf519d1SRafael J. Wysocki {
4769cf519d1SRafael J. Wysocki ktime_t calltime;
4779cf519d1SRafael J. Wysocki int error;
4789cf519d1SRafael J. Wysocki
4799cf519d1SRafael J. Wysocki if (!cb)
4809cf519d1SRafael J. Wysocki return 0;
4819cf519d1SRafael J. Wysocki
4827f817ba9SBjorn Helgaas calltime = initcall_debug_start(dev, cb);
4839cf519d1SRafael J. Wysocki
4849cf519d1SRafael J. Wysocki pm_dev_dbg(dev, state, info);
485e8bca479STodd E Brandt trace_device_pm_callback_start(dev, info, state.event);
4869cf519d1SRafael J. Wysocki error = cb(dev);
487e8bca479STodd E Brandt trace_device_pm_callback_end(dev, error);
488a759de69SYoungjin Jang suspend_report_result(dev, cb, error);
4899cf519d1SRafael J. Wysocki
4907f817ba9SBjorn Helgaas initcall_debug_report(dev, calltime, cb, error);
4919cf519d1SRafael J. Wysocki
4929cf519d1SRafael J. Wysocki return error;
4939cf519d1SRafael J. Wysocki }
4949cf519d1SRafael J. Wysocki
49570fea60dSBenoit Goby #ifdef CONFIG_DPM_WATCHDOG
49670fea60dSBenoit Goby struct dpm_watchdog {
49770fea60dSBenoit Goby struct device *dev;
49870fea60dSBenoit Goby struct task_struct *tsk;
49970fea60dSBenoit Goby struct timer_list timer;
50070fea60dSBenoit Goby };
50170fea60dSBenoit Goby
50270fea60dSBenoit Goby #define DECLARE_DPM_WATCHDOG_ON_STACK(wd) \
50370fea60dSBenoit Goby struct dpm_watchdog wd
50470fea60dSBenoit Goby
50570fea60dSBenoit Goby /**
50670fea60dSBenoit Goby * dpm_watchdog_handler - Driver suspend / resume watchdog handler.
507c4a586fdSYangtao Li * @t: The timer that PM watchdog depends on.
50870fea60dSBenoit Goby *
50970fea60dSBenoit Goby * Called when a driver has timed out suspending or resuming.
51070fea60dSBenoit Goby * There's not much we can do here to recover so panic() to
51170fea60dSBenoit Goby * capture a crash-dump in pstore.
51270fea60dSBenoit Goby */
dpm_watchdog_handler(struct timer_list * t)5139c6c273aSKees Cook static void dpm_watchdog_handler(struct timer_list *t)
51470fea60dSBenoit Goby {
5159c6c273aSKees Cook struct dpm_watchdog *wd = from_timer(wd, t, timer);
51670fea60dSBenoit Goby
51770fea60dSBenoit Goby dev_emerg(wd->dev, "**** DPM device timeout ****\n");
5189cb8f069SDmitry Safonov show_stack(wd->tsk, NULL, KERN_EMERG);
51970fea60dSBenoit Goby panic("%s %s: unrecoverable failure\n",
52070fea60dSBenoit Goby dev_driver_string(wd->dev), dev_name(wd->dev));
52170fea60dSBenoit Goby }
52270fea60dSBenoit Goby
52370fea60dSBenoit Goby /**
52470fea60dSBenoit Goby * dpm_watchdog_set - Enable pm watchdog for given device.
52570fea60dSBenoit Goby * @wd: Watchdog. Must be allocated on the stack.
52670fea60dSBenoit Goby * @dev: Device to handle.
52770fea60dSBenoit Goby */
dpm_watchdog_set(struct dpm_watchdog * wd,struct device * dev)52870fea60dSBenoit Goby static void dpm_watchdog_set(struct dpm_watchdog *wd, struct device *dev)
52970fea60dSBenoit Goby {
53070fea60dSBenoit Goby struct timer_list *timer = &wd->timer;
53170fea60dSBenoit Goby
53270fea60dSBenoit Goby wd->dev = dev;
53370fea60dSBenoit Goby wd->tsk = current;
53470fea60dSBenoit Goby
5359c6c273aSKees Cook timer_setup_on_stack(timer, dpm_watchdog_handler, 0);
53670fea60dSBenoit Goby /* use same timeout value for both suspend and resume */
53770fea60dSBenoit Goby timer->expires = jiffies + HZ * CONFIG_DPM_WATCHDOG_TIMEOUT;
53870fea60dSBenoit Goby add_timer(timer);
53970fea60dSBenoit Goby }
54070fea60dSBenoit Goby
54170fea60dSBenoit Goby /**
54270fea60dSBenoit Goby * dpm_watchdog_clear - Disable suspend/resume watchdog.
54370fea60dSBenoit Goby * @wd: Watchdog to disable.
54470fea60dSBenoit Goby */
dpm_watchdog_clear(struct dpm_watchdog * wd)54570fea60dSBenoit Goby static void dpm_watchdog_clear(struct dpm_watchdog *wd)
54670fea60dSBenoit Goby {
54770fea60dSBenoit Goby struct timer_list *timer = &wd->timer;
54870fea60dSBenoit Goby
54970fea60dSBenoit Goby del_timer_sync(timer);
55070fea60dSBenoit Goby destroy_timer_on_stack(timer);
55170fea60dSBenoit Goby }
55270fea60dSBenoit Goby #else
55370fea60dSBenoit Goby #define DECLARE_DPM_WATCHDOG_ON_STACK(wd)
55470fea60dSBenoit Goby #define dpm_watchdog_set(x, y)
55570fea60dSBenoit Goby #define dpm_watchdog_clear(x)
55670fea60dSBenoit Goby #endif
55770fea60dSBenoit Goby
558cd59abfcSAlan Stern /*------------------------- Resume routines -------------------------*/
559cd59abfcSAlan Stern
560cd59abfcSAlan Stern /**
56176c70cb5SRafael J. Wysocki * dev_pm_skip_resume - System-wide device resume optimization check.
5620d4b54c6SRafael J. Wysocki * @dev: Target device.
5630d4b54c6SRafael J. Wysocki *
5646e176bf8SRafael J. Wysocki * Return:
5656e176bf8SRafael J. Wysocki * - %false if the transition under way is RESTORE.
566fa2bfeadSRafael J. Wysocki * - Return value of dev_pm_skip_suspend() if the transition under way is THAW.
5676e176bf8SRafael J. Wysocki * - The logical negation of %power.must_resume otherwise (that is, when the
5686e176bf8SRafael J. Wysocki * transition under way is RESUME).
5690d4b54c6SRafael J. Wysocki */
dev_pm_skip_resume(struct device * dev)57076c70cb5SRafael J. Wysocki bool dev_pm_skip_resume(struct device *dev)
5710d4b54c6SRafael J. Wysocki {
5726e176bf8SRafael J. Wysocki if (pm_transition.event == PM_EVENT_RESTORE)
5736e176bf8SRafael J. Wysocki return false;
5746e176bf8SRafael J. Wysocki
5756e176bf8SRafael J. Wysocki if (pm_transition.event == PM_EVENT_THAW)
576fa2bfeadSRafael J. Wysocki return dev_pm_skip_suspend(dev);
5776e176bf8SRafael J. Wysocki
5786e176bf8SRafael J. Wysocki return !dev->power.must_resume;
5790d4b54c6SRafael J. Wysocki }
5800d4b54c6SRafael J. Wysocki
5814fa3061aSRafael J. Wysocki /**
582*e681e29dSRafael J. Wysocki * __device_resume_noirq - Execute a "noirq resume" callback for given device.
5834fa3061aSRafael J. Wysocki * @dev: Device to handle.
5844fa3061aSRafael J. Wysocki * @state: PM transition of the system being carried out.
5854fa3061aSRafael J. Wysocki * @async: If true, the device is being resumed asynchronously.
5864fa3061aSRafael J. Wysocki *
5874fa3061aSRafael J. Wysocki * The driver of @dev will not receive interrupts while this function is being
5884fa3061aSRafael J. Wysocki * executed.
5894fa3061aSRafael J. Wysocki */
__device_resume_noirq(struct device * dev,pm_message_t state,bool async)590*e681e29dSRafael J. Wysocki static void __device_resume_noirq(struct device *dev, pm_message_t state, bool async)
5914fa3061aSRafael J. Wysocki {
59230205377SRafael J. Wysocki pm_callback_t callback = NULL;
59330205377SRafael J. Wysocki const char *info = NULL;
59432bfa56aSRafael J. Wysocki bool skip_resume;
5954fa3061aSRafael J. Wysocki int error = 0;
5964fa3061aSRafael J. Wysocki
5974fa3061aSRafael J. Wysocki TRACE_DEVICE(dev);
5984fa3061aSRafael J. Wysocki TRACE_RESUME(0);
5994fa3061aSRafael J. Wysocki
6004fa3061aSRafael J. Wysocki if (dev->power.syscore || dev->power.direct_complete)
6014fa3061aSRafael J. Wysocki goto Out;
6024fa3061aSRafael J. Wysocki
6034fa3061aSRafael J. Wysocki if (!dev->power.is_noirq_suspended)
6044fa3061aSRafael J. Wysocki goto Out;
6054fa3061aSRafael J. Wysocki
6060552e05fSRafael J. Wysocki if (!dpm_wait_for_superior(dev, async))
6070552e05fSRafael J. Wysocki goto Out;
6084fa3061aSRafael J. Wysocki
60976c70cb5SRafael J. Wysocki skip_resume = dev_pm_skip_resume(dev);
6106e176bf8SRafael J. Wysocki /*
6116e176bf8SRafael J. Wysocki * If the driver callback is skipped below or by the middle layer
6126e176bf8SRafael J. Wysocki * callback and device_resume_early() also skips the driver callback for
6136e176bf8SRafael J. Wysocki * this device later, it needs to appear as "suspended" to PM-runtime,
6146e176bf8SRafael J. Wysocki * so change its status accordingly.
6156e176bf8SRafael J. Wysocki *
6166e176bf8SRafael J. Wysocki * Otherwise, the device is going to be resumed, so set its PM-runtime
6176e176bf8SRafael J. Wysocki * status to "active", but do that only if DPM_FLAG_SMART_SUSPEND is set
6186e176bf8SRafael J. Wysocki * to avoid confusing drivers that don't use it.
6196e176bf8SRafael J. Wysocki */
6206e176bf8SRafael J. Wysocki if (skip_resume)
6216e176bf8SRafael J. Wysocki pm_runtime_set_suspended(dev);
622fa2bfeadSRafael J. Wysocki else if (dev_pm_skip_suspend(dev))
6236e176bf8SRafael J. Wysocki pm_runtime_set_active(dev);
62432bfa56aSRafael J. Wysocki
62530205377SRafael J. Wysocki if (dev->pm_domain) {
62630205377SRafael J. Wysocki info = "noirq power domain ";
62730205377SRafael J. Wysocki callback = pm_noirq_op(&dev->pm_domain->ops, state);
62830205377SRafael J. Wysocki } else if (dev->type && dev->type->pm) {
62930205377SRafael J. Wysocki info = "noirq type ";
63030205377SRafael J. Wysocki callback = pm_noirq_op(dev->type->pm, state);
63130205377SRafael J. Wysocki } else if (dev->class && dev->class->pm) {
63230205377SRafael J. Wysocki info = "noirq class ";
63330205377SRafael J. Wysocki callback = pm_noirq_op(dev->class->pm, state);
63430205377SRafael J. Wysocki } else if (dev->bus && dev->bus->pm) {
63530205377SRafael J. Wysocki info = "noirq bus ";
63630205377SRafael J. Wysocki callback = pm_noirq_op(dev->bus->pm, state);
63730205377SRafael J. Wysocki }
63875e94645SRafael J. Wysocki if (callback)
63975e94645SRafael J. Wysocki goto Run;
6404fa3061aSRafael J. Wysocki
64132bfa56aSRafael J. Wysocki if (skip_resume)
64232bfa56aSRafael J. Wysocki goto Skip;
64332bfa56aSRafael J. Wysocki
64475e94645SRafael J. Wysocki if (dev->driver && dev->driver->pm) {
645cf579dfbSRafael J. Wysocki info = "noirq driver ";
64635cd133cSRafael J. Wysocki callback = pm_noirq_op(dev->driver->pm, state);
64735cd133cSRafael J. Wysocki }
64835cd133cSRafael J. Wysocki
64975e94645SRafael J. Wysocki Run:
6509cf519d1SRafael J. Wysocki error = dpm_run_callback(callback, dev, state, info);
65175e94645SRafael J. Wysocki
65275e94645SRafael J. Wysocki Skip:
6533d2699bcSLiu, Chuansheng dev->power.is_noirq_suspended = false;
6549cf519d1SRafael J. Wysocki
655dbf37414SRafael J. Wysocki Out:
65676569faaSLiu, Chuansheng complete_all(&dev->power.completion);
657775b64d2SRafael J. Wysocki TRACE_RESUME(error);
658*e681e29dSRafael J. Wysocki
659*e681e29dSRafael J. Wysocki if (error) {
660*e681e29dSRafael J. Wysocki suspend_stats.failed_resume_noirq++;
661*e681e29dSRafael J. Wysocki dpm_save_failed_step(SUSPEND_RESUME_NOIRQ);
662*e681e29dSRafael J. Wysocki dpm_save_failed_dev(dev_name(dev));
663*e681e29dSRafael J. Wysocki pm_dev_err(dev, state, async ? " async noirq" : " noirq", error);
664*e681e29dSRafael J. Wysocki }
665775b64d2SRafael J. Wysocki }
666775b64d2SRafael J. Wysocki
is_async(struct device * dev)66776569faaSLiu, Chuansheng static bool is_async(struct device *dev)
66876569faaSLiu, Chuansheng {
66976569faaSLiu, Chuansheng return dev->power.async_suspend && pm_async_enabled
67076569faaSLiu, Chuansheng && !pm_trace_is_enabled();
67176569faaSLiu, Chuansheng }
67276569faaSLiu, Chuansheng
dpm_async_fn(struct device * dev,async_func_t func)673f2a424f6SYangtao Li static bool dpm_async_fn(struct device *dev, async_func_t func)
674f2a424f6SYangtao Li {
675f2a424f6SYangtao Li reinit_completion(&dev->power.completion);
676f2a424f6SYangtao Li
677*e681e29dSRafael J. Wysocki if (!is_async(dev))
678*e681e29dSRafael J. Wysocki return false;
679*e681e29dSRafael J. Wysocki
680f2a424f6SYangtao Li get_device(dev);
681*e681e29dSRafael J. Wysocki
682*e681e29dSRafael J. Wysocki if (async_schedule_dev_nocall(func, dev))
683f2a424f6SYangtao Li return true;
684*e681e29dSRafael J. Wysocki
685*e681e29dSRafael J. Wysocki put_device(dev);
686f2a424f6SYangtao Li
687f2a424f6SYangtao Li return false;
688f2a424f6SYangtao Li }
689f2a424f6SYangtao Li
async_resume_noirq(void * data,async_cookie_t cookie)69076569faaSLiu, Chuansheng static void async_resume_noirq(void *data, async_cookie_t cookie)
69176569faaSLiu, Chuansheng {
69273d73f5eSLi zeming struct device *dev = data;
69376569faaSLiu, Chuansheng
694*e681e29dSRafael J. Wysocki __device_resume_noirq(dev, pm_transition, true);
69576569faaSLiu, Chuansheng put_device(dev);
69676569faaSLiu, Chuansheng }
69776569faaSLiu, Chuansheng
device_resume_noirq(struct device * dev)698*e681e29dSRafael J. Wysocki static void device_resume_noirq(struct device *dev)
699*e681e29dSRafael J. Wysocki {
700*e681e29dSRafael J. Wysocki if (dpm_async_fn(dev, async_resume_noirq))
701*e681e29dSRafael J. Wysocki return;
702*e681e29dSRafael J. Wysocki
703*e681e29dSRafael J. Wysocki __device_resume_noirq(dev, pm_transition, false);
704*e681e29dSRafael J. Wysocki }
705*e681e29dSRafael J. Wysocki
dpm_noirq_resume_devices(pm_message_t state)706b605c44cSRafael J. Wysocki static void dpm_noirq_resume_devices(pm_message_t state)
707775b64d2SRafael J. Wysocki {
70876569faaSLiu, Chuansheng struct device *dev;
709ecf762b2SRafael J. Wysocki ktime_t starttime = ktime_get();
710775b64d2SRafael J. Wysocki
711bb3632c6STodd E Brandt trace_suspend_resume(TPS("dpm_resume_noirq"), state.event, true);
71232bdfac5SRafael J. Wysocki mutex_lock(&dpm_list_mtx);
71376569faaSLiu, Chuansheng pm_transition = state;
714775b64d2SRafael J. Wysocki
71576569faaSLiu, Chuansheng while (!list_empty(&dpm_noirq_list)) {
71676569faaSLiu, Chuansheng dev = to_device(dpm_noirq_list.next);
7175b219a51SRafael J. Wysocki get_device(dev);
718cf579dfbSRafael J. Wysocki list_move_tail(&dev->power.entry, &dpm_late_early_list);
7192aa36604SRafael J. Wysocki
720d08a5aceSRafael J. Wysocki mutex_unlock(&dpm_list_mtx);
721d08a5aceSRafael J. Wysocki
722*e681e29dSRafael J. Wysocki device_resume_noirq(dev);
723cf579dfbSRafael J. Wysocki
724cf579dfbSRafael J. Wysocki put_device(dev);
7252aa36604SRafael J. Wysocki
7262aa36604SRafael J. Wysocki mutex_lock(&dpm_list_mtx);
727cf579dfbSRafael J. Wysocki }
728cf579dfbSRafael J. Wysocki mutex_unlock(&dpm_list_mtx);
72976569faaSLiu, Chuansheng async_synchronize_full();
73048059c09SRafael J. Wysocki dpm_show_time(starttime, state, 0, "noirq");
731786f41fbSRafael J. Wysocki trace_suspend_resume(TPS("dpm_resume_noirq"), state.event, false);
732786f41fbSRafael J. Wysocki }
733786f41fbSRafael J. Wysocki
734786f41fbSRafael J. Wysocki /**
735786f41fbSRafael J. Wysocki * dpm_resume_noirq - Execute "noirq resume" callbacks for all devices.
736786f41fbSRafael J. Wysocki * @state: PM transition of the system being carried out.
737786f41fbSRafael J. Wysocki *
738786f41fbSRafael J. Wysocki * Invoke the "noirq" resume callbacks for all devices in dpm_noirq_list and
739786f41fbSRafael J. Wysocki * allow device drivers' interrupt handlers to be called.
740786f41fbSRafael J. Wysocki */
dpm_resume_noirq(pm_message_t state)741786f41fbSRafael J. Wysocki void dpm_resume_noirq(pm_message_t state)
742786f41fbSRafael J. Wysocki {
743786f41fbSRafael J. Wysocki dpm_noirq_resume_devices(state);
744b605c44cSRafael J. Wysocki
745b605c44cSRafael J. Wysocki resume_device_irqs();
746b605c44cSRafael J. Wysocki device_wakeup_disarm_wake_irqs();
747cf579dfbSRafael J. Wysocki }
748cf579dfbSRafael J. Wysocki
7494fa3061aSRafael J. Wysocki /**
750*e681e29dSRafael J. Wysocki * __device_resume_early - Execute an "early resume" callback for given device.
7514fa3061aSRafael J. Wysocki * @dev: Device to handle.
7524fa3061aSRafael J. Wysocki * @state: PM transition of the system being carried out.
7534fa3061aSRafael J. Wysocki * @async: If true, the device is being resumed asynchronously.
7544fa3061aSRafael J. Wysocki *
7554fa3061aSRafael J. Wysocki * Runtime PM is disabled for @dev while this function is being executed.
7564fa3061aSRafael J. Wysocki */
__device_resume_early(struct device * dev,pm_message_t state,bool async)757*e681e29dSRafael J. Wysocki static void __device_resume_early(struct device *dev, pm_message_t state, bool async)
7584fa3061aSRafael J. Wysocki {
75930205377SRafael J. Wysocki pm_callback_t callback = NULL;
76030205377SRafael J. Wysocki const char *info = NULL;
7614fa3061aSRafael J. Wysocki int error = 0;
7624fa3061aSRafael J. Wysocki
7634fa3061aSRafael J. Wysocki TRACE_DEVICE(dev);
7644fa3061aSRafael J. Wysocki TRACE_RESUME(0);
7654fa3061aSRafael J. Wysocki
7664fa3061aSRafael J. Wysocki if (dev->power.syscore || dev->power.direct_complete)
7674fa3061aSRafael J. Wysocki goto Out;
7684fa3061aSRafael J. Wysocki
7694fa3061aSRafael J. Wysocki if (!dev->power.is_late_suspended)
7704fa3061aSRafael J. Wysocki goto Out;
7714fa3061aSRafael J. Wysocki
7720552e05fSRafael J. Wysocki if (!dpm_wait_for_superior(dev, async))
7730552e05fSRafael J. Wysocki goto Out;
7744fa3061aSRafael J. Wysocki
77530205377SRafael J. Wysocki if (dev->pm_domain) {
77630205377SRafael J. Wysocki info = "early power domain ";
77730205377SRafael J. Wysocki callback = pm_late_early_op(&dev->pm_domain->ops, state);
77830205377SRafael J. Wysocki } else if (dev->type && dev->type->pm) {
77930205377SRafael J. Wysocki info = "early type ";
78030205377SRafael J. Wysocki callback = pm_late_early_op(dev->type->pm, state);
78130205377SRafael J. Wysocki } else if (dev->class && dev->class->pm) {
78230205377SRafael J. Wysocki info = "early class ";
78330205377SRafael J. Wysocki callback = pm_late_early_op(dev->class->pm, state);
78430205377SRafael J. Wysocki } else if (dev->bus && dev->bus->pm) {
78530205377SRafael J. Wysocki info = "early bus ";
78630205377SRafael J. Wysocki callback = pm_late_early_op(dev->bus->pm, state);
7876e176bf8SRafael J. Wysocki }
7886e176bf8SRafael J. Wysocki if (callback)
7896e176bf8SRafael J. Wysocki goto Run;
7904fa3061aSRafael J. Wysocki
79176c70cb5SRafael J. Wysocki if (dev_pm_skip_resume(dev))
7926e176bf8SRafael J. Wysocki goto Skip;
7936e176bf8SRafael J. Wysocki
7946e176bf8SRafael J. Wysocki if (dev->driver && dev->driver->pm) {
795cf579dfbSRafael J. Wysocki info = "early driver ";
796cf579dfbSRafael J. Wysocki callback = pm_late_early_op(dev->driver->pm, state);
797cf579dfbSRafael J. Wysocki }
798cf579dfbSRafael J. Wysocki
7996e176bf8SRafael J. Wysocki Run:
800cf579dfbSRafael J. Wysocki error = dpm_run_callback(callback, dev, state, info);
8016e176bf8SRafael J. Wysocki
8026e176bf8SRafael J. Wysocki Skip:
8033d2699bcSLiu, Chuansheng dev->power.is_late_suspended = false;
804cf579dfbSRafael J. Wysocki
805dbf37414SRafael J. Wysocki Out:
806cf579dfbSRafael J. Wysocki TRACE_RESUME(error);
8079f6d8f6aSRafael J. Wysocki
8089f6d8f6aSRafael J. Wysocki pm_runtime_enable(dev);
8099e5e7910SLiu, Chuansheng complete_all(&dev->power.completion);
810*e681e29dSRafael J. Wysocki
811*e681e29dSRafael J. Wysocki if (error) {
812*e681e29dSRafael J. Wysocki suspend_stats.failed_resume_early++;
813*e681e29dSRafael J. Wysocki dpm_save_failed_step(SUSPEND_RESUME_EARLY);
814*e681e29dSRafael J. Wysocki dpm_save_failed_dev(dev_name(dev));
815*e681e29dSRafael J. Wysocki pm_dev_err(dev, state, async ? " async early" : " early", error);
816*e681e29dSRafael J. Wysocki }
817cf579dfbSRafael J. Wysocki }
818cf579dfbSRafael J. Wysocki
async_resume_early(void * data,async_cookie_t cookie)8199e5e7910SLiu, Chuansheng static void async_resume_early(void *data, async_cookie_t cookie)
8209e5e7910SLiu, Chuansheng {
82173d73f5eSLi zeming struct device *dev = data;
8229e5e7910SLiu, Chuansheng
823*e681e29dSRafael J. Wysocki __device_resume_early(dev, pm_transition, true);
8249e5e7910SLiu, Chuansheng put_device(dev);
8259e5e7910SLiu, Chuansheng }
8269e5e7910SLiu, Chuansheng
device_resume_early(struct device * dev)827*e681e29dSRafael J. Wysocki static void device_resume_early(struct device *dev)
828*e681e29dSRafael J. Wysocki {
829*e681e29dSRafael J. Wysocki if (dpm_async_fn(dev, async_resume_early))
830*e681e29dSRafael J. Wysocki return;
831*e681e29dSRafael J. Wysocki
832*e681e29dSRafael J. Wysocki __device_resume_early(dev, pm_transition, false);
833*e681e29dSRafael J. Wysocki }
834*e681e29dSRafael J. Wysocki
835cf579dfbSRafael J. Wysocki /**
836cf579dfbSRafael J. Wysocki * dpm_resume_early - Execute "early resume" callbacks for all devices.
837cf579dfbSRafael J. Wysocki * @state: PM transition of the system being carried out.
838cf579dfbSRafael J. Wysocki */
dpm_resume_early(pm_message_t state)8392a8a8ce6SRafael J. Wysocki void dpm_resume_early(pm_message_t state)
840cf579dfbSRafael J. Wysocki {
8419e5e7910SLiu, Chuansheng struct device *dev;
842cf579dfbSRafael J. Wysocki ktime_t starttime = ktime_get();
843cf579dfbSRafael J. Wysocki
844bb3632c6STodd E Brandt trace_suspend_resume(TPS("dpm_resume_early"), state.event, true);
845cf579dfbSRafael J. Wysocki mutex_lock(&dpm_list_mtx);
8469e5e7910SLiu, Chuansheng pm_transition = state;
847cf579dfbSRafael J. Wysocki
8489e5e7910SLiu, Chuansheng while (!list_empty(&dpm_late_early_list)) {
8499e5e7910SLiu, Chuansheng dev = to_device(dpm_late_early_list.next);
850cf579dfbSRafael J. Wysocki get_device(dev);
851cf579dfbSRafael J. Wysocki list_move_tail(&dev->power.entry, &dpm_suspended_list);
8522aa36604SRafael J. Wysocki
853cf579dfbSRafael J. Wysocki mutex_unlock(&dpm_list_mtx);
854cf579dfbSRafael J. Wysocki
855*e681e29dSRafael J. Wysocki device_resume_early(dev);
8562aa36604SRafael J. Wysocki
857d08a5aceSRafael J. Wysocki put_device(dev);
8582aa36604SRafael J. Wysocki
8592aa36604SRafael J. Wysocki mutex_lock(&dpm_list_mtx);
860d08a5aceSRafael J. Wysocki }
86132bdfac5SRafael J. Wysocki mutex_unlock(&dpm_list_mtx);
8629e5e7910SLiu, Chuansheng async_synchronize_full();
86348059c09SRafael J. Wysocki dpm_show_time(starttime, state, 0, "early");
864bb3632c6STodd E Brandt trace_suspend_resume(TPS("dpm_resume_early"), state.event, false);
865775b64d2SRafael J. Wysocki }
866cf579dfbSRafael J. Wysocki
867cf579dfbSRafael J. Wysocki /**
868cf579dfbSRafael J. Wysocki * dpm_resume_start - Execute "noirq" and "early" device callbacks.
869cf579dfbSRafael J. Wysocki * @state: PM transition of the system being carried out.
870cf579dfbSRafael J. Wysocki */
dpm_resume_start(pm_message_t state)871cf579dfbSRafael J. Wysocki void dpm_resume_start(pm_message_t state)
872cf579dfbSRafael J. Wysocki {
873cf579dfbSRafael J. Wysocki dpm_resume_noirq(state);
874cf579dfbSRafael J. Wysocki dpm_resume_early(state);
875cf579dfbSRafael J. Wysocki }
876cf579dfbSRafael J. Wysocki EXPORT_SYMBOL_GPL(dpm_resume_start);
877775b64d2SRafael J. Wysocki
878775b64d2SRafael J. Wysocki /**
879*e681e29dSRafael J. Wysocki * __device_resume - Execute "resume" callbacks for given device.
88020d652d7SRafael J. Wysocki * @dev: Device to handle.
8811eede070SRafael J. Wysocki * @state: PM transition of the system being carried out.
8825af84b82SRafael J. Wysocki * @async: If true, the device is being resumed asynchronously.
883775b64d2SRafael J. Wysocki */
__device_resume(struct device * dev,pm_message_t state,bool async)884*e681e29dSRafael J. Wysocki static void __device_resume(struct device *dev, pm_message_t state, bool async)
885775b64d2SRafael J. Wysocki {
8869cf519d1SRafael J. Wysocki pm_callback_t callback = NULL;
887e3771fa9SKrzysztof Kozlowski const char *info = NULL;
888775b64d2SRafael J. Wysocki int error = 0;
88970fea60dSBenoit Goby DECLARE_DPM_WATCHDOG_ON_STACK(wd);
890775b64d2SRafael J. Wysocki
891775b64d2SRafael J. Wysocki TRACE_DEVICE(dev);
892775b64d2SRafael J. Wysocki TRACE_RESUME(0);
893cd59abfcSAlan Stern
894dbf37414SRafael J. Wysocki if (dev->power.syscore)
895dbf37414SRafael J. Wysocki goto Complete;
896dbf37414SRafael J. Wysocki
897aae4518bSRafael J. Wysocki if (dev->power.direct_complete) {
898aae4518bSRafael J. Wysocki /* Match the pm_runtime_disable() in __device_suspend(). */
899aae4518bSRafael J. Wysocki pm_runtime_enable(dev);
900aae4518bSRafael J. Wysocki goto Complete;
901aae4518bSRafael J. Wysocki }
902aae4518bSRafael J. Wysocki
9030552e05fSRafael J. Wysocki if (!dpm_wait_for_superior(dev, async))
9040552e05fSRafael J. Wysocki goto Complete;
9050552e05fSRafael J. Wysocki
90670fea60dSBenoit Goby dpm_watchdog_set(&wd, dev);
9078e9394ceSGreg Kroah-Hartman device_lock(dev);
9087a8d37a3SRafael J. Wysocki
909f76b168bSAlan Stern /*
910f76b168bSAlan Stern * This is a fib. But we'll allow new children to be added below
911f76b168bSAlan Stern * a resumed device, even if the device hasn't been completed yet.
912f76b168bSAlan Stern */
913f76b168bSAlan Stern dev->power.is_prepared = false;
91497df8c12SRafael J. Wysocki
9156d0e0e84SAlan Stern if (!dev->power.is_suspended)
9166d0e0e84SAlan Stern goto Unlock;
9176d0e0e84SAlan Stern
918564b905aSRafael J. Wysocki if (dev->pm_domain) {
9199cf519d1SRafael J. Wysocki info = "power domain ";
9209cf519d1SRafael J. Wysocki callback = pm_op(&dev->pm_domain->ops, state);
92135cd133cSRafael J. Wysocki goto Driver;
9227538e3dbSRafael J. Wysocki }
9237538e3dbSRafael J. Wysocki
9249659cc06SRafael J. Wysocki if (dev->type && dev->type->pm) {
9259cf519d1SRafael J. Wysocki info = "type ";
9269cf519d1SRafael J. Wysocki callback = pm_op(dev->type->pm, state);
92735cd133cSRafael J. Wysocki goto Driver;
9289659cc06SRafael J. Wysocki }
9299659cc06SRafael J. Wysocki
930a380f2edSRafael J. Wysocki if (dev->class && dev->class->pm) {
9319cf519d1SRafael J. Wysocki info = "class ";
9329cf519d1SRafael J. Wysocki callback = pm_op(dev->class->pm, state);
93335cd133cSRafael J. Wysocki goto Driver;
9349659cc06SRafael J. Wysocki }
9359659cc06SRafael J. Wysocki
9361eede070SRafael J. Wysocki if (dev->bus) {
9371eede070SRafael J. Wysocki if (dev->bus->pm) {
93835cd133cSRafael J. Wysocki info = "bus ";
9399cf519d1SRafael J. Wysocki callback = pm_op(dev->bus->pm, state);
9401eede070SRafael J. Wysocki } else if (dev->bus->resume) {
94135cd133cSRafael J. Wysocki info = "legacy bus ";
9429cf519d1SRafael J. Wysocki callback = dev->bus->resume;
94335cd133cSRafael J. Wysocki goto End;
944cd59abfcSAlan Stern }
9451eede070SRafael J. Wysocki }
946cd59abfcSAlan Stern
94735cd133cSRafael J. Wysocki Driver:
94835cd133cSRafael J. Wysocki if (!callback && dev->driver && dev->driver->pm) {
94935cd133cSRafael J. Wysocki info = "driver ";
95035cd133cSRafael J. Wysocki callback = pm_op(dev->driver->pm, state);
95135cd133cSRafael J. Wysocki }
95235cd133cSRafael J. Wysocki
9531eede070SRafael J. Wysocki End:
9549cf519d1SRafael J. Wysocki error = dpm_run_callback(callback, dev, state, info);
9556d0e0e84SAlan Stern dev->power.is_suspended = false;
9566d0e0e84SAlan Stern
9576d0e0e84SAlan Stern Unlock:
9588e9394ceSGreg Kroah-Hartman device_unlock(dev);
95970fea60dSBenoit Goby dpm_watchdog_clear(&wd);
960dbf37414SRafael J. Wysocki
961dbf37414SRafael J. Wysocki Complete:
9625af84b82SRafael J. Wysocki complete_all(&dev->power.completion);
9637a8d37a3SRafael J. Wysocki
964cd59abfcSAlan Stern TRACE_RESUME(error);
9651e2ef05bSRafael J. Wysocki
966*e681e29dSRafael J. Wysocki if (error) {
967*e681e29dSRafael J. Wysocki suspend_stats.failed_resume++;
968*e681e29dSRafael J. Wysocki dpm_save_failed_step(SUSPEND_RESUME);
969*e681e29dSRafael J. Wysocki dpm_save_failed_dev(dev_name(dev));
970*e681e29dSRafael J. Wysocki pm_dev_err(dev, state, async ? " async" : "", error);
971*e681e29dSRafael J. Wysocki }
972cd59abfcSAlan Stern }
973cd59abfcSAlan Stern
async_resume(void * data,async_cookie_t cookie)9745af84b82SRafael J. Wysocki static void async_resume(void *data, async_cookie_t cookie)
9755af84b82SRafael J. Wysocki {
97673d73f5eSLi zeming struct device *dev = data;
9775af84b82SRafael J. Wysocki
978*e681e29dSRafael J. Wysocki __device_resume(dev, pm_transition, true);
9795af84b82SRafael J. Wysocki put_device(dev);
9805af84b82SRafael J. Wysocki }
9815af84b82SRafael J. Wysocki
device_resume(struct device * dev)982*e681e29dSRafael J. Wysocki static void device_resume(struct device *dev)
983*e681e29dSRafael J. Wysocki {
984*e681e29dSRafael J. Wysocki if (dpm_async_fn(dev, async_resume))
985*e681e29dSRafael J. Wysocki return;
986*e681e29dSRafael J. Wysocki
987*e681e29dSRafael J. Wysocki __device_resume(dev, pm_transition, false);
988*e681e29dSRafael J. Wysocki }
989*e681e29dSRafael J. Wysocki
990775b64d2SRafael J. Wysocki /**
99120d652d7SRafael J. Wysocki * dpm_resume - Execute "resume" callbacks for non-sysdev devices.
9921eede070SRafael J. Wysocki * @state: PM transition of the system being carried out.
993775b64d2SRafael J. Wysocki *
99420d652d7SRafael J. Wysocki * Execute the appropriate "resume" callback for all devices whose status
99520d652d7SRafael J. Wysocki * indicates that they are suspended.
996cd59abfcSAlan Stern */
dpm_resume(pm_message_t state)99791e7c75bSRafael J. Wysocki void dpm_resume(pm_message_t state)
998cd59abfcSAlan Stern {
99997df8c12SRafael J. Wysocki struct device *dev;
1000ecf762b2SRafael J. Wysocki ktime_t starttime = ktime_get();
1001cd59abfcSAlan Stern
1002bb3632c6STodd E Brandt trace_suspend_resume(TPS("dpm_resume"), state.event, true);
100391e7c75bSRafael J. Wysocki might_sleep();
100491e7c75bSRafael J. Wysocki
10051eede070SRafael J. Wysocki mutex_lock(&dpm_list_mtx);
10065af84b82SRafael J. Wysocki pm_transition = state;
1007098dff73SRafael J. Wysocki async_error = 0;
10081eede070SRafael J. Wysocki
10098a43a9abSRafael J. Wysocki while (!list_empty(&dpm_suspended_list)) {
10108a43a9abSRafael J. Wysocki dev = to_device(dpm_suspended_list.next);
1011*e681e29dSRafael J. Wysocki
101297df8c12SRafael J. Wysocki get_device(dev);
10131eede070SRafael J. Wysocki
1014cd59abfcSAlan Stern mutex_unlock(&dpm_list_mtx);
10151eede070SRafael J. Wysocki
1016*e681e29dSRafael J. Wysocki device_resume(dev);
10175b219a51SRafael J. Wysocki
10185b219a51SRafael J. Wysocki mutex_lock(&dpm_list_mtx);
1019*e681e29dSRafael J. Wysocki
10201eede070SRafael J. Wysocki if (!list_empty(&dev->power.entry))
10218a43a9abSRafael J. Wysocki list_move_tail(&dev->power.entry, &dpm_prepared_list);
10222aa36604SRafael J. Wysocki
10232aa36604SRafael J. Wysocki mutex_unlock(&dpm_list_mtx);
10242aa36604SRafael J. Wysocki
10251eede070SRafael J. Wysocki put_device(dev);
10262aa36604SRafael J. Wysocki
10272aa36604SRafael J. Wysocki mutex_lock(&dpm_list_mtx);
10281eede070SRafael J. Wysocki }
10291eede070SRafael J. Wysocki mutex_unlock(&dpm_list_mtx);
10305af84b82SRafael J. Wysocki async_synchronize_full();
103148059c09SRafael J. Wysocki dpm_show_time(starttime, state, 0, NULL);
10322f0aea93SViresh Kumar
10332f0aea93SViresh Kumar cpufreq_resume();
10346e863844SLukasz Luba devfreq_resume();
1035bb3632c6STodd E Brandt trace_suspend_resume(TPS("dpm_resume"), state.event, false);
10361eede070SRafael J. Wysocki }
10371eede070SRafael J. Wysocki
10381eede070SRafael J. Wysocki /**
103920d652d7SRafael J. Wysocki * device_complete - Complete a PM transition for given device.
104020d652d7SRafael J. Wysocki * @dev: Device to handle.
10411eede070SRafael J. Wysocki * @state: PM transition of the system being carried out.
10421eede070SRafael J. Wysocki */
device_complete(struct device * dev,pm_message_t state)1043d1616302SAlan Stern static void device_complete(struct device *dev, pm_message_t state)
10441eede070SRafael J. Wysocki {
104535cd133cSRafael J. Wysocki void (*callback)(struct device *) = NULL;
1046e3771fa9SKrzysztof Kozlowski const char *info = NULL;
104735cd133cSRafael J. Wysocki
1048dbf37414SRafael J. Wysocki if (dev->power.syscore)
1049928265e3SRafael J. Wysocki goto out;
1050dbf37414SRafael J. Wysocki
10518e9394ceSGreg Kroah-Hartman device_lock(dev);
10521eede070SRafael J. Wysocki
1053564b905aSRafael J. Wysocki if (dev->pm_domain) {
105435cd133cSRafael J. Wysocki info = "completing power domain ";
105535cd133cSRafael J. Wysocki callback = dev->pm_domain->ops.complete;
10564d27e9dcSRafael J. Wysocki } else if (dev->type && dev->type->pm) {
105735cd133cSRafael J. Wysocki info = "completing type ";
105835cd133cSRafael J. Wysocki callback = dev->type->pm->complete;
10599659cc06SRafael J. Wysocki } else if (dev->class && dev->class->pm) {
106035cd133cSRafael J. Wysocki info = "completing class ";
106135cd133cSRafael J. Wysocki callback = dev->class->pm->complete;
10629659cc06SRafael J. Wysocki } else if (dev->bus && dev->bus->pm) {
106335cd133cSRafael J. Wysocki info = "completing bus ";
106435cd133cSRafael J. Wysocki callback = dev->bus->pm->complete;
106535cd133cSRafael J. Wysocki }
106635cd133cSRafael J. Wysocki
106735cd133cSRafael J. Wysocki if (!callback && dev->driver && dev->driver->pm) {
106835cd133cSRafael J. Wysocki info = "completing driver ";
106935cd133cSRafael J. Wysocki callback = dev->driver->pm->complete;
107035cd133cSRafael J. Wysocki }
107135cd133cSRafael J. Wysocki
107235cd133cSRafael J. Wysocki if (callback) {
107335cd133cSRafael J. Wysocki pm_dev_dbg(dev, state, info);
107435cd133cSRafael J. Wysocki callback(dev);
10751eede070SRafael J. Wysocki }
10761eede070SRafael J. Wysocki
10778e9394ceSGreg Kroah-Hartman device_unlock(dev);
107888d26136SAlan Stern
1079928265e3SRafael J. Wysocki out:
1080af939339SUlf Hansson pm_runtime_put(dev);
10811eede070SRafael J. Wysocki }
10821eede070SRafael J. Wysocki
10831eede070SRafael J. Wysocki /**
108420d652d7SRafael J. Wysocki * dpm_complete - Complete a PM transition for all non-sysdev devices.
10851eede070SRafael J. Wysocki * @state: PM transition of the system being carried out.
10861eede070SRafael J. Wysocki *
108720d652d7SRafael J. Wysocki * Execute the ->complete() callbacks for all devices whose PM status is not
108820d652d7SRafael J. Wysocki * DPM_ON (this allows new devices to be registered).
10891eede070SRafael J. Wysocki */
dpm_complete(pm_message_t state)109091e7c75bSRafael J. Wysocki void dpm_complete(pm_message_t state)
10911eede070SRafael J. Wysocki {
10921eede070SRafael J. Wysocki struct list_head list;
10931eede070SRafael J. Wysocki
1094bb3632c6STodd E Brandt trace_suspend_resume(TPS("dpm_complete"), state.event, true);
109591e7c75bSRafael J. Wysocki might_sleep();
109691e7c75bSRafael J. Wysocki
10971eede070SRafael J. Wysocki INIT_LIST_HEAD(&list);
10981eede070SRafael J. Wysocki mutex_lock(&dpm_list_mtx);
10998a43a9abSRafael J. Wysocki while (!list_empty(&dpm_prepared_list)) {
11008a43a9abSRafael J. Wysocki struct device *dev = to_device(dpm_prepared_list.prev);
11011eede070SRafael J. Wysocki
11021eede070SRafael J. Wysocki get_device(dev);
1103f76b168bSAlan Stern dev->power.is_prepared = false;
11045b219a51SRafael J. Wysocki list_move(&dev->power.entry, &list);
11052aa36604SRafael J. Wysocki
11061eede070SRafael J. Wysocki mutex_unlock(&dpm_list_mtx);
11071eede070SRafael J. Wysocki
110832e8d689STodd E Brandt trace_device_pm_callback_start(dev, "", state.event);
1109d1616302SAlan Stern device_complete(dev, state);
111032e8d689STodd E Brandt trace_device_pm_callback_end(dev, 0);
11111eede070SRafael J. Wysocki
11121eede070SRafael J. Wysocki put_device(dev);
11132aa36604SRafael J. Wysocki
11142aa36604SRafael J. Wysocki mutex_lock(&dpm_list_mtx);
11151eede070SRafael J. Wysocki }
11161eede070SRafael J. Wysocki list_splice(&list, &dpm_list);
1117cd59abfcSAlan Stern mutex_unlock(&dpm_list_mtx);
1118013c074fSStrashko, Grygorii
1119013c074fSStrashko, Grygorii /* Allow device probing and trigger re-probing of deferred devices */
1120013c074fSStrashko, Grygorii device_unblock_probing();
1121bb3632c6STodd E Brandt trace_suspend_resume(TPS("dpm_complete"), state.event, false);
1122cd59abfcSAlan Stern }
1123cd59abfcSAlan Stern
1124775b64d2SRafael J. Wysocki /**
112520d652d7SRafael J. Wysocki * dpm_resume_end - Execute "resume" callbacks and complete system transition.
11261eede070SRafael J. Wysocki * @state: PM transition of the system being carried out.
1127cd59abfcSAlan Stern *
112820d652d7SRafael J. Wysocki * Execute "resume" callbacks for all devices and complete the PM transition of
112920d652d7SRafael J. Wysocki * the system.
1130cd59abfcSAlan Stern */
dpm_resume_end(pm_message_t state)1131d1616302SAlan Stern void dpm_resume_end(pm_message_t state)
1132cd59abfcSAlan Stern {
11331eede070SRafael J. Wysocki dpm_resume(state);
11341eede070SRafael J. Wysocki dpm_complete(state);
1135cd59abfcSAlan Stern }
1136d1616302SAlan Stern EXPORT_SYMBOL_GPL(dpm_resume_end);
1137cd59abfcSAlan Stern
1138cd59abfcSAlan Stern
1139cd59abfcSAlan Stern /*------------------------- Suspend routines -------------------------*/
1140cd59abfcSAlan Stern
11411eede070SRafael J. Wysocki /**
114220d652d7SRafael J. Wysocki * resume_event - Return a "resume" message for given "suspend" sleep state.
11431eede070SRafael J. Wysocki * @sleep_state: PM message representing a sleep state.
114420d652d7SRafael J. Wysocki *
114520d652d7SRafael J. Wysocki * Return a PM message representing the resume event corresponding to given
114620d652d7SRafael J. Wysocki * sleep state.
11471eede070SRafael J. Wysocki */
resume_event(pm_message_t sleep_state)11481eede070SRafael J. Wysocki static pm_message_t resume_event(pm_message_t sleep_state)
1149cd59abfcSAlan Stern {
11501eede070SRafael J. Wysocki switch (sleep_state.event) {
11511eede070SRafael J. Wysocki case PM_EVENT_SUSPEND:
11521eede070SRafael J. Wysocki return PMSG_RESUME;
11531eede070SRafael J. Wysocki case PM_EVENT_FREEZE:
11541eede070SRafael J. Wysocki case PM_EVENT_QUIESCE:
11551eede070SRafael J. Wysocki return PMSG_RECOVER;
11561eede070SRafael J. Wysocki case PM_EVENT_HIBERNATE:
11571eede070SRafael J. Wysocki return PMSG_RESTORE;
1158cd59abfcSAlan Stern }
11591eede070SRafael J. Wysocki return PMSG_ON;
1160cd59abfcSAlan Stern }
1161cd59abfcSAlan Stern
dpm_superior_set_must_resume(struct device * dev)11620d4b54c6SRafael J. Wysocki static void dpm_superior_set_must_resume(struct device *dev)
11630d4b54c6SRafael J. Wysocki {
11640d4b54c6SRafael J. Wysocki struct device_link *link;
11650d4b54c6SRafael J. Wysocki int idx;
11660d4b54c6SRafael J. Wysocki
11670d4b54c6SRafael J. Wysocki if (dev->parent)
11680d4b54c6SRafael J. Wysocki dev->parent->power.must_resume = true;
11690d4b54c6SRafael J. Wysocki
11700d4b54c6SRafael J. Wysocki idx = device_links_read_lock();
11710d4b54c6SRafael J. Wysocki
117242beb82eSMadhuparna Bhowmik list_for_each_entry_rcu_locked(link, &dev->links.suppliers, c_node)
11730d4b54c6SRafael J. Wysocki link->supplier->power.must_resume = true;
11740d4b54c6SRafael J. Wysocki
11750d4b54c6SRafael J. Wysocki device_links_read_unlock(idx);
11760d4b54c6SRafael J. Wysocki }
11770d4b54c6SRafael J. Wysocki
1178cd59abfcSAlan Stern /**
1179b082ddd8SRafael J. Wysocki * __device_suspend_noirq - Execute a "noirq suspend" callback for given device.
118020d652d7SRafael J. Wysocki * @dev: Device to handle.
11811eede070SRafael J. Wysocki * @state: PM transition of the system being carried out.
118258c256a3SRandy Dunlap * @async: If true, the device is being suspended asynchronously.
1183775b64d2SRafael J. Wysocki *
118420d652d7SRafael J. Wysocki * The driver of @dev will not receive interrupts while this function is being
118520d652d7SRafael J. Wysocki * executed.
1186775b64d2SRafael J. Wysocki */
__device_suspend_noirq(struct device * dev,pm_message_t state,bool async)118728b6fd6eSLiu, Chuansheng static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool async)
1188775b64d2SRafael J. Wysocki {
118930205377SRafael J. Wysocki pm_callback_t callback = NULL;
119030205377SRafael J. Wysocki const char *info = NULL;
119128b6fd6eSLiu, Chuansheng int error = 0;
119228b6fd6eSLiu, Chuansheng
1193431d452aSZhonghui Fu TRACE_DEVICE(dev);
1194431d452aSZhonghui Fu TRACE_SUSPEND(0);
1195431d452aSZhonghui Fu
1196098c3055SLinus Torvalds dpm_wait_for_subordinate(dev, async);
11976f75c3fdSBrian Norris
119828b6fd6eSLiu, Chuansheng if (async_error)
119928b6fd6eSLiu, Chuansheng goto Complete;
120028b6fd6eSLiu, Chuansheng
1201aae4518bSRafael J. Wysocki if (dev->power.syscore || dev->power.direct_complete)
120228b6fd6eSLiu, Chuansheng goto Complete;
120328b6fd6eSLiu, Chuansheng
120430205377SRafael J. Wysocki if (dev->pm_domain) {
120530205377SRafael J. Wysocki info = "noirq power domain ";
120630205377SRafael J. Wysocki callback = pm_noirq_op(&dev->pm_domain->ops, state);
120730205377SRafael J. Wysocki } else if (dev->type && dev->type->pm) {
120830205377SRafael J. Wysocki info = "noirq type ";
120930205377SRafael J. Wysocki callback = pm_noirq_op(dev->type->pm, state);
121030205377SRafael J. Wysocki } else if (dev->class && dev->class->pm) {
121130205377SRafael J. Wysocki info = "noirq class ";
121230205377SRafael J. Wysocki callback = pm_noirq_op(dev->class->pm, state);
121330205377SRafael J. Wysocki } else if (dev->bus && dev->bus->pm) {
121430205377SRafael J. Wysocki info = "noirq bus ";
121530205377SRafael J. Wysocki callback = pm_noirq_op(dev->bus->pm, state);
121630205377SRafael J. Wysocki }
121775e94645SRafael J. Wysocki if (callback)
121875e94645SRafael J. Wysocki goto Run;
12197538e3dbSRafael J. Wysocki
1220fa2bfeadSRafael J. Wysocki if (dev_pm_skip_suspend(dev))
122175e94645SRafael J. Wysocki goto Skip;
122275e94645SRafael J. Wysocki
122375e94645SRafael J. Wysocki if (dev->driver && dev->driver->pm) {
1224cf579dfbSRafael J. Wysocki info = "noirq driver ";
122535cd133cSRafael J. Wysocki callback = pm_noirq_op(dev->driver->pm, state);
122635cd133cSRafael J. Wysocki }
122735cd133cSRafael J. Wysocki
122875e94645SRafael J. Wysocki Run:
12293d2699bcSLiu, Chuansheng error = dpm_run_callback(callback, dev, state, info);
12300d4b54c6SRafael J. Wysocki if (error) {
123128b6fd6eSLiu, Chuansheng async_error = error;
12320d4b54c6SRafael J. Wysocki goto Complete;
12330d4b54c6SRafael J. Wysocki }
12340d4b54c6SRafael J. Wysocki
123575e94645SRafael J. Wysocki Skip:
12360d4b54c6SRafael J. Wysocki dev->power.is_noirq_suspended = true;
12370d4b54c6SRafael J. Wysocki
1238107d47b2SRafael J. Wysocki /*
1239107d47b2SRafael J. Wysocki * Skipping the resume of devices that were in use right before the
1240107d47b2SRafael J. Wysocki * system suspend (as indicated by their PM-runtime usage counters)
1241107d47b2SRafael J. Wysocki * would be suboptimal. Also resume them if doing that is not allowed
1242107d47b2SRafael J. Wysocki * to be skipped.
1243107d47b2SRafael J. Wysocki */
1244107d47b2SRafael J. Wysocki if (atomic_read(&dev->power.usage_count) > 1 ||
12452a3f3475SRafael J. Wysocki !(dev_pm_test_driver_flags(dev, DPM_FLAG_MAY_SKIP_RESUME) &&
1246107d47b2SRafael J. Wysocki dev->power.may_skip_resume))
12470d4b54c6SRafael J. Wysocki dev->power.must_resume = true;
12480d4b54c6SRafael J. Wysocki
12490d4b54c6SRafael J. Wysocki if (dev->power.must_resume)
12500d4b54c6SRafael J. Wysocki dpm_superior_set_must_resume(dev);
12513d2699bcSLiu, Chuansheng
125228b6fd6eSLiu, Chuansheng Complete:
125328b6fd6eSLiu, Chuansheng complete_all(&dev->power.completion);
1254431d452aSZhonghui Fu TRACE_SUSPEND(error);
12553d2699bcSLiu, Chuansheng return error;
1256775b64d2SRafael J. Wysocki }
1257775b64d2SRafael J. Wysocki
async_suspend_noirq(void * data,async_cookie_t cookie)125828b6fd6eSLiu, Chuansheng static void async_suspend_noirq(void *data, async_cookie_t cookie)
125928b6fd6eSLiu, Chuansheng {
126073d73f5eSLi zeming struct device *dev = data;
126128b6fd6eSLiu, Chuansheng int error;
126228b6fd6eSLiu, Chuansheng
126328b6fd6eSLiu, Chuansheng error = __device_suspend_noirq(dev, pm_transition, true);
126428b6fd6eSLiu, Chuansheng if (error) {
126528b6fd6eSLiu, Chuansheng dpm_save_failed_dev(dev_name(dev));
126628b6fd6eSLiu, Chuansheng pm_dev_err(dev, pm_transition, " async", error);
126728b6fd6eSLiu, Chuansheng }
126828b6fd6eSLiu, Chuansheng
126928b6fd6eSLiu, Chuansheng put_device(dev);
127028b6fd6eSLiu, Chuansheng }
127128b6fd6eSLiu, Chuansheng
device_suspend_noirq(struct device * dev)127228b6fd6eSLiu, Chuansheng static int device_suspend_noirq(struct device *dev)
127328b6fd6eSLiu, Chuansheng {
1274f2a424f6SYangtao Li if (dpm_async_fn(dev, async_suspend_noirq))
127528b6fd6eSLiu, Chuansheng return 0;
1276f2a424f6SYangtao Li
127728b6fd6eSLiu, Chuansheng return __device_suspend_noirq(dev, pm_transition, false);
127828b6fd6eSLiu, Chuansheng }
127928b6fd6eSLiu, Chuansheng
dpm_noirq_suspend_devices(pm_message_t state)1280b605c44cSRafael J. Wysocki static int dpm_noirq_suspend_devices(pm_message_t state)
1281775b64d2SRafael J. Wysocki {
1282ecf762b2SRafael J. Wysocki ktime_t starttime = ktime_get();
1283775b64d2SRafael J. Wysocki int error = 0;
1284775b64d2SRafael J. Wysocki
1285bb3632c6STodd E Brandt trace_suspend_resume(TPS("dpm_suspend_noirq"), state.event, true);
128632bdfac5SRafael J. Wysocki mutex_lock(&dpm_list_mtx);
128728b6fd6eSLiu, Chuansheng pm_transition = state;
128828b6fd6eSLiu, Chuansheng async_error = 0;
128928b6fd6eSLiu, Chuansheng
1290cf579dfbSRafael J. Wysocki while (!list_empty(&dpm_late_early_list)) {
1291cf579dfbSRafael J. Wysocki struct device *dev = to_device(dpm_late_early_list.prev);
1292d08a5aceSRafael J. Wysocki
1293d08a5aceSRafael J. Wysocki get_device(dev);
1294d08a5aceSRafael J. Wysocki mutex_unlock(&dpm_list_mtx);
1295d08a5aceSRafael J. Wysocki
129628b6fd6eSLiu, Chuansheng error = device_suspend_noirq(dev);
1297d08a5aceSRafael J. Wysocki
1298d08a5aceSRafael J. Wysocki mutex_lock(&dpm_list_mtx);
12992aa36604SRafael J. Wysocki
1300775b64d2SRafael J. Wysocki if (error) {
1301cf579dfbSRafael J. Wysocki pm_dev_err(dev, state, " noirq", error);
13022a77c46dSShuoX Liu dpm_save_failed_dev(dev_name(dev));
13032aa36604SRafael J. Wysocki } else if (!list_empty(&dev->power.entry)) {
13048a43a9abSRafael J. Wysocki list_move(&dev->power.entry, &dpm_noirq_list);
13052aa36604SRafael J. Wysocki }
13062aa36604SRafael J. Wysocki
13072aa36604SRafael J. Wysocki mutex_unlock(&dpm_list_mtx);
13082aa36604SRafael J. Wysocki
1309d08a5aceSRafael J. Wysocki put_device(dev);
131052d136ccSRafael J. Wysocki
13112aa36604SRafael J. Wysocki mutex_lock(&dpm_list_mtx);
13122aa36604SRafael J. Wysocki
13132aa36604SRafael J. Wysocki if (error || async_error)
131452d136ccSRafael J. Wysocki break;
131552d136ccSRafael J. Wysocki }
131632bdfac5SRafael J. Wysocki mutex_unlock(&dpm_list_mtx);
131728b6fd6eSLiu, Chuansheng async_synchronize_full();
131828b6fd6eSLiu, Chuansheng if (!error)
131928b6fd6eSLiu, Chuansheng error = async_error;
132028b6fd6eSLiu, Chuansheng
132128b6fd6eSLiu, Chuansheng if (error) {
132228b6fd6eSLiu, Chuansheng suspend_stats.failed_suspend_noirq++;
132328b6fd6eSLiu, Chuansheng dpm_save_failed_step(SUSPEND_SUSPEND_NOIRQ);
132428b6fd6eSLiu, Chuansheng }
132548059c09SRafael J. Wysocki dpm_show_time(starttime, state, error, "noirq");
1326bb3632c6STodd E Brandt trace_suspend_resume(TPS("dpm_suspend_noirq"), state.event, false);
1327775b64d2SRafael J. Wysocki return error;
1328775b64d2SRafael J. Wysocki }
1329cf579dfbSRafael J. Wysocki
1330cf579dfbSRafael J. Wysocki /**
1331786f41fbSRafael J. Wysocki * dpm_suspend_noirq - Execute "noirq suspend" callbacks for all devices.
1332786f41fbSRafael J. Wysocki * @state: PM transition of the system being carried out.
1333786f41fbSRafael J. Wysocki *
1334786f41fbSRafael J. Wysocki * Prevent device drivers' interrupt handlers from being called and invoke
1335786f41fbSRafael J. Wysocki * "noirq" suspend callbacks for all non-sysdev devices.
1336786f41fbSRafael J. Wysocki */
dpm_suspend_noirq(pm_message_t state)1337786f41fbSRafael J. Wysocki int dpm_suspend_noirq(pm_message_t state)
1338786f41fbSRafael J. Wysocki {
1339786f41fbSRafael J. Wysocki int ret;
1340786f41fbSRafael J. Wysocki
1341b605c44cSRafael J. Wysocki device_wakeup_arm_wake_irqs();
1342b605c44cSRafael J. Wysocki suspend_device_irqs();
1343b605c44cSRafael J. Wysocki
1344786f41fbSRafael J. Wysocki ret = dpm_noirq_suspend_devices(state);
1345786f41fbSRafael J. Wysocki if (ret)
1346786f41fbSRafael J. Wysocki dpm_resume_noirq(resume_event(state));
1347786f41fbSRafael J. Wysocki
1348786f41fbSRafael J. Wysocki return ret;
1349786f41fbSRafael J. Wysocki }
1350786f41fbSRafael J. Wysocki
dpm_propagate_wakeup_to_parent(struct device * dev)13510a99d767SUlf Hansson static void dpm_propagate_wakeup_to_parent(struct device *dev)
13520a99d767SUlf Hansson {
13530a99d767SUlf Hansson struct device *parent = dev->parent;
13540a99d767SUlf Hansson
13550a99d767SUlf Hansson if (!parent)
13560a99d767SUlf Hansson return;
13570a99d767SUlf Hansson
13580a99d767SUlf Hansson spin_lock_irq(&parent->power.lock);
13590a99d767SUlf Hansson
13604e1d9a73SPatrice Chotard if (device_wakeup_path(dev) && !parent->power.ignore_children)
13610a99d767SUlf Hansson parent->power.wakeup_path = true;
13620a99d767SUlf Hansson
13630a99d767SUlf Hansson spin_unlock_irq(&parent->power.lock);
13640a99d767SUlf Hansson }
13650a99d767SUlf Hansson
1366786f41fbSRafael J. Wysocki /**
1367b082ddd8SRafael J. Wysocki * __device_suspend_late - Execute a "late suspend" callback for given device.
1368cf579dfbSRafael J. Wysocki * @dev: Device to handle.
1369cf579dfbSRafael J. Wysocki * @state: PM transition of the system being carried out.
137058c256a3SRandy Dunlap * @async: If true, the device is being suspended asynchronously.
1371cf579dfbSRafael J. Wysocki *
1372cf579dfbSRafael J. Wysocki * Runtime PM is disabled for @dev while this function is being executed.
1373cf579dfbSRafael J. Wysocki */
__device_suspend_late(struct device * dev,pm_message_t state,bool async)1374de377b39SLiu, Chuansheng static int __device_suspend_late(struct device *dev, pm_message_t state, bool async)
1375cf579dfbSRafael J. Wysocki {
137630205377SRafael J. Wysocki pm_callback_t callback = NULL;
137730205377SRafael J. Wysocki const char *info = NULL;
1378de377b39SLiu, Chuansheng int error = 0;
1379cf579dfbSRafael J. Wysocki
1380431d452aSZhonghui Fu TRACE_DEVICE(dev);
1381431d452aSZhonghui Fu TRACE_SUSPEND(0);
1382431d452aSZhonghui Fu
13839f6d8f6aSRafael J. Wysocki __pm_runtime_disable(dev, false);
13849f6d8f6aSRafael J. Wysocki
1385098c3055SLinus Torvalds dpm_wait_for_subordinate(dev, async);
13866f75c3fdSBrian Norris
1387de377b39SLiu, Chuansheng if (async_error)
1388de377b39SLiu, Chuansheng goto Complete;
1389de377b39SLiu, Chuansheng
1390de377b39SLiu, Chuansheng if (pm_wakeup_pending()) {
1391de377b39SLiu, Chuansheng async_error = -EBUSY;
1392de377b39SLiu, Chuansheng goto Complete;
1393de377b39SLiu, Chuansheng }
1394de377b39SLiu, Chuansheng
1395aae4518bSRafael J. Wysocki if (dev->power.syscore || dev->power.direct_complete)
1396de377b39SLiu, Chuansheng goto Complete;
1397de377b39SLiu, Chuansheng
139830205377SRafael J. Wysocki if (dev->pm_domain) {
139930205377SRafael J. Wysocki info = "late power domain ";
140030205377SRafael J. Wysocki callback = pm_late_early_op(&dev->pm_domain->ops, state);
140130205377SRafael J. Wysocki } else if (dev->type && dev->type->pm) {
140230205377SRafael J. Wysocki info = "late type ";
140330205377SRafael J. Wysocki callback = pm_late_early_op(dev->type->pm, state);
140430205377SRafael J. Wysocki } else if (dev->class && dev->class->pm) {
140530205377SRafael J. Wysocki info = "late class ";
140630205377SRafael J. Wysocki callback = pm_late_early_op(dev->class->pm, state);
140730205377SRafael J. Wysocki } else if (dev->bus && dev->bus->pm) {
140830205377SRafael J. Wysocki info = "late bus ";
140930205377SRafael J. Wysocki callback = pm_late_early_op(dev->bus->pm, state);
141030205377SRafael J. Wysocki }
141175e94645SRafael J. Wysocki if (callback)
141275e94645SRafael J. Wysocki goto Run;
1413cf579dfbSRafael J. Wysocki
1414fa2bfeadSRafael J. Wysocki if (dev_pm_skip_suspend(dev))
141575e94645SRafael J. Wysocki goto Skip;
141675e94645SRafael J. Wysocki
141775e94645SRafael J. Wysocki if (dev->driver && dev->driver->pm) {
1418cf579dfbSRafael J. Wysocki info = "late driver ";
1419cf579dfbSRafael J. Wysocki callback = pm_late_early_op(dev->driver->pm, state);
1420cf579dfbSRafael J. Wysocki }
1421cf579dfbSRafael J. Wysocki
142275e94645SRafael J. Wysocki Run:
14233d2699bcSLiu, Chuansheng error = dpm_run_callback(callback, dev, state, info);
142475e94645SRafael J. Wysocki if (error) {
1425de377b39SLiu, Chuansheng async_error = error;
142675e94645SRafael J. Wysocki goto Complete;
142775e94645SRafael J. Wysocki }
14280a99d767SUlf Hansson dpm_propagate_wakeup_to_parent(dev);
142975e94645SRafael J. Wysocki
143075e94645SRafael J. Wysocki Skip:
143175e94645SRafael J. Wysocki dev->power.is_late_suspended = true;
14323d2699bcSLiu, Chuansheng
1433de377b39SLiu, Chuansheng Complete:
1434431d452aSZhonghui Fu TRACE_SUSPEND(error);
1435de377b39SLiu, Chuansheng complete_all(&dev->power.completion);
14363d2699bcSLiu, Chuansheng return error;
1437cf579dfbSRafael J. Wysocki }
1438cf579dfbSRafael J. Wysocki
async_suspend_late(void * data,async_cookie_t cookie)1439de377b39SLiu, Chuansheng static void async_suspend_late(void *data, async_cookie_t cookie)
1440de377b39SLiu, Chuansheng {
144173d73f5eSLi zeming struct device *dev = data;
1442de377b39SLiu, Chuansheng int error;
1443de377b39SLiu, Chuansheng
1444de377b39SLiu, Chuansheng error = __device_suspend_late(dev, pm_transition, true);
1445de377b39SLiu, Chuansheng if (error) {
1446de377b39SLiu, Chuansheng dpm_save_failed_dev(dev_name(dev));
1447de377b39SLiu, Chuansheng pm_dev_err(dev, pm_transition, " async", error);
1448de377b39SLiu, Chuansheng }
1449de377b39SLiu, Chuansheng put_device(dev);
1450de377b39SLiu, Chuansheng }
1451de377b39SLiu, Chuansheng
device_suspend_late(struct device * dev)1452de377b39SLiu, Chuansheng static int device_suspend_late(struct device *dev)
1453de377b39SLiu, Chuansheng {
1454f2a424f6SYangtao Li if (dpm_async_fn(dev, async_suspend_late))
1455de377b39SLiu, Chuansheng return 0;
1456de377b39SLiu, Chuansheng
1457de377b39SLiu, Chuansheng return __device_suspend_late(dev, pm_transition, false);
1458de377b39SLiu, Chuansheng }
1459de377b39SLiu, Chuansheng
1460cf579dfbSRafael J. Wysocki /**
1461cf579dfbSRafael J. Wysocki * dpm_suspend_late - Execute "late suspend" callbacks for all devices.
1462cf579dfbSRafael J. Wysocki * @state: PM transition of the system being carried out.
1463cf579dfbSRafael J. Wysocki */
dpm_suspend_late(pm_message_t state)14642a8a8ce6SRafael J. Wysocki int dpm_suspend_late(pm_message_t state)
1465cf579dfbSRafael J. Wysocki {
1466cf579dfbSRafael J. Wysocki ktime_t starttime = ktime_get();
1467cf579dfbSRafael J. Wysocki int error = 0;
1468cf579dfbSRafael J. Wysocki
1469bb3632c6STodd E Brandt trace_suspend_resume(TPS("dpm_suspend_late"), state.event, true);
1470a2bd7be1SUlf Hansson wake_up_all_idle_cpus();
1471cf579dfbSRafael J. Wysocki mutex_lock(&dpm_list_mtx);
1472de377b39SLiu, Chuansheng pm_transition = state;
1473de377b39SLiu, Chuansheng async_error = 0;
1474de377b39SLiu, Chuansheng
1475cf579dfbSRafael J. Wysocki while (!list_empty(&dpm_suspended_list)) {
1476cf579dfbSRafael J. Wysocki struct device *dev = to_device(dpm_suspended_list.prev);
1477cf579dfbSRafael J. Wysocki
1478cf579dfbSRafael J. Wysocki get_device(dev);
14792aa36604SRafael J. Wysocki
1480cf579dfbSRafael J. Wysocki mutex_unlock(&dpm_list_mtx);
1481cf579dfbSRafael J. Wysocki
1482de377b39SLiu, Chuansheng error = device_suspend_late(dev);
1483cf579dfbSRafael J. Wysocki
1484cf579dfbSRafael J. Wysocki mutex_lock(&dpm_list_mtx);
14852aa36604SRafael J. Wysocki
14863a17fb32SRafael J. Wysocki if (!list_empty(&dev->power.entry))
14873a17fb32SRafael J. Wysocki list_move(&dev->power.entry, &dpm_late_early_list);
14883a17fb32SRafael J. Wysocki
1489cf579dfbSRafael J. Wysocki if (error) {
1490cf579dfbSRafael J. Wysocki pm_dev_err(dev, state, " late", error);
1491cf579dfbSRafael J. Wysocki dpm_save_failed_dev(dev_name(dev));
1492cf579dfbSRafael J. Wysocki }
14932aa36604SRafael J. Wysocki
14942aa36604SRafael J. Wysocki mutex_unlock(&dpm_list_mtx);
14952aa36604SRafael J. Wysocki
1496cf579dfbSRafael J. Wysocki put_device(dev);
149752d136ccSRafael J. Wysocki
14982aa36604SRafael J. Wysocki mutex_lock(&dpm_list_mtx);
14992aa36604SRafael J. Wysocki
15002aa36604SRafael J. Wysocki if (error || async_error)
150152d136ccSRafael J. Wysocki break;
150252d136ccSRafael J. Wysocki }
1503cf579dfbSRafael J. Wysocki mutex_unlock(&dpm_list_mtx);
1504de377b39SLiu, Chuansheng async_synchronize_full();
1505246ef766SImre Deak if (!error)
1506246ef766SImre Deak error = async_error;
1507de377b39SLiu, Chuansheng if (error) {
1508de377b39SLiu, Chuansheng suspend_stats.failed_suspend_late++;
1509de377b39SLiu, Chuansheng dpm_save_failed_step(SUSPEND_SUSPEND_LATE);
1510cf579dfbSRafael J. Wysocki dpm_resume_early(resume_event(state));
1511de377b39SLiu, Chuansheng }
151248059c09SRafael J. Wysocki dpm_show_time(starttime, state, error, "late");
1513bb3632c6STodd E Brandt trace_suspend_resume(TPS("dpm_suspend_late"), state.event, false);
1514cf579dfbSRafael J. Wysocki return error;
1515cf579dfbSRafael J. Wysocki }
1516cf579dfbSRafael J. Wysocki
1517cf579dfbSRafael J. Wysocki /**
1518cf579dfbSRafael J. Wysocki * dpm_suspend_end - Execute "late" and "noirq" device suspend callbacks.
1519cf579dfbSRafael J. Wysocki * @state: PM transition of the system being carried out.
1520cf579dfbSRafael J. Wysocki */
dpm_suspend_end(pm_message_t state)1521cf579dfbSRafael J. Wysocki int dpm_suspend_end(pm_message_t state)
1522cf579dfbSRafael J. Wysocki {
15233540d38dSBart Van Assche ktime_t starttime = ktime_get();
15243540d38dSBart Van Assche int error;
15253540d38dSBart Van Assche
15263540d38dSBart Van Assche error = dpm_suspend_late(state);
1527064b021fSColin Cross if (error)
15283540d38dSBart Van Assche goto out;
1529cf579dfbSRafael J. Wysocki
1530064b021fSColin Cross error = dpm_suspend_noirq(state);
153123f62d7aSRafael J. Wysocki if (error)
1532997a0311SFeng Hong dpm_resume_early(resume_event(state));
1533064b021fSColin Cross
15343540d38dSBart Van Assche out:
15353540d38dSBart Van Assche dpm_show_time(starttime, state, error, "end");
15363540d38dSBart Van Assche return error;
1537cf579dfbSRafael J. Wysocki }
1538cf579dfbSRafael J. Wysocki EXPORT_SYMBOL_GPL(dpm_suspend_end);
1539775b64d2SRafael J. Wysocki
1540775b64d2SRafael J. Wysocki /**
1541875ab0b7SRafael J. Wysocki * legacy_suspend - Execute a legacy (bus or class) suspend callback for device.
15420a884223SRandy Dunlap * @dev: Device to suspend.
15430a884223SRandy Dunlap * @state: PM transition of the system being carried out.
15440a884223SRandy Dunlap * @cb: Suspend callback to execute.
154558c256a3SRandy Dunlap * @info: string description of caller.
1546875ab0b7SRafael J. Wysocki */
legacy_suspend(struct device * dev,pm_message_t state,int (* cb)(struct device * dev,pm_message_t state),const char * info)1547875ab0b7SRafael J. Wysocki static int legacy_suspend(struct device *dev, pm_message_t state,
154853644677SShuah Khan int (*cb)(struct device *dev, pm_message_t state),
1549e3771fa9SKrzysztof Kozlowski const char *info)
1550875ab0b7SRafael J. Wysocki {
1551875ab0b7SRafael J. Wysocki int error;
1552875ab0b7SRafael J. Wysocki ktime_t calltime;
1553875ab0b7SRafael J. Wysocki
15547f817ba9SBjorn Helgaas calltime = initcall_debug_start(dev, cb);
1555875ab0b7SRafael J. Wysocki
1556e8bca479STodd E Brandt trace_device_pm_callback_start(dev, info, state.event);
1557875ab0b7SRafael J. Wysocki error = cb(dev, state);
1558e8bca479STodd E Brandt trace_device_pm_callback_end(dev, error);
1559a759de69SYoungjin Jang suspend_report_result(dev, cb, error);
1560875ab0b7SRafael J. Wysocki
15617f817ba9SBjorn Helgaas initcall_debug_report(dev, calltime, cb, error);
1562875ab0b7SRafael J. Wysocki
1563875ab0b7SRafael J. Wysocki return error;
1564875ab0b7SRafael J. Wysocki }
1565875ab0b7SRafael J. Wysocki
dpm_clear_superiors_direct_complete(struct device * dev)1566c23bd387SUlf Hansson static void dpm_clear_superiors_direct_complete(struct device *dev)
15678c73b428SRafael J. Wysocki {
15688c73b428SRafael J. Wysocki struct device_link *link;
15698c73b428SRafael J. Wysocki int idx;
15708c73b428SRafael J. Wysocki
1571c23bd387SUlf Hansson if (dev->parent) {
1572c23bd387SUlf Hansson spin_lock_irq(&dev->parent->power.lock);
1573c23bd387SUlf Hansson dev->parent->power.direct_complete = false;
1574c23bd387SUlf Hansson spin_unlock_irq(&dev->parent->power.lock);
1575c23bd387SUlf Hansson }
1576c23bd387SUlf Hansson
15778c73b428SRafael J. Wysocki idx = device_links_read_lock();
15788c73b428SRafael J. Wysocki
157942beb82eSMadhuparna Bhowmik list_for_each_entry_rcu_locked(link, &dev->links.suppliers, c_node) {
15808c73b428SRafael J. Wysocki spin_lock_irq(&link->supplier->power.lock);
15818c73b428SRafael J. Wysocki link->supplier->power.direct_complete = false;
15828c73b428SRafael J. Wysocki spin_unlock_irq(&link->supplier->power.lock);
15838c73b428SRafael J. Wysocki }
15848c73b428SRafael J. Wysocki
15858c73b428SRafael J. Wysocki device_links_read_unlock(idx);
15868c73b428SRafael J. Wysocki }
15878c73b428SRafael J. Wysocki
1588875ab0b7SRafael J. Wysocki /**
1589b082ddd8SRafael J. Wysocki * __device_suspend - Execute "suspend" callbacks for given device.
159020d652d7SRafael J. Wysocki * @dev: Device to handle.
15911eede070SRafael J. Wysocki * @state: PM transition of the system being carried out.
15925af84b82SRafael J. Wysocki * @async: If true, the device is being suspended asynchronously.
1593cd59abfcSAlan Stern */
__device_suspend(struct device * dev,pm_message_t state,bool async)15945af84b82SRafael J. Wysocki static int __device_suspend(struct device *dev, pm_message_t state, bool async)
1595cd59abfcSAlan Stern {
15969cf519d1SRafael J. Wysocki pm_callback_t callback = NULL;
1597e3771fa9SKrzysztof Kozlowski const char *info = NULL;
1598cd59abfcSAlan Stern int error = 0;
159970fea60dSBenoit Goby DECLARE_DPM_WATCHDOG_ON_STACK(wd);
1600cd59abfcSAlan Stern
1601431d452aSZhonghui Fu TRACE_DEVICE(dev);
1602431d452aSZhonghui Fu TRACE_SUSPEND(0);
1603431d452aSZhonghui Fu
16048c73b428SRafael J. Wysocki dpm_wait_for_subordinate(dev, async);
16057a8d37a3SRafael J. Wysocki
160669e445abSRafael J. Wysocki if (async_error) {
160769e445abSRafael J. Wysocki dev->power.direct_complete = false;
16081f758b23SMandeep Singh Baines goto Complete;
160969e445abSRafael J. Wysocki }
16101e2ef05bSRafael J. Wysocki
161188d26136SAlan Stern /*
1612e3eb6e8fSRafael J. Wysocki * Wait for possible runtime PM transitions of the device in progress
1613e3eb6e8fSRafael J. Wysocki * to complete and if there's a runtime resume request pending for it,
1614e3eb6e8fSRafael J. Wysocki * resume it before proceeding with invoking the system-wide suspend
1615e3eb6e8fSRafael J. Wysocki * callbacks for it.
1616e3eb6e8fSRafael J. Wysocki *
1617e3eb6e8fSRafael J. Wysocki * If the system-wide suspend callbacks below change the configuration
1618e3eb6e8fSRafael J. Wysocki * of the device, they must disable runtime PM for it or otherwise
1619e3eb6e8fSRafael J. Wysocki * ensure that its runtime-resume callbacks will not be confused by that
1620e3eb6e8fSRafael J. Wysocki * change in case they are invoked going forward.
162188d26136SAlan Stern */
1622e3eb6e8fSRafael J. Wysocki pm_runtime_barrier(dev);
16235af84b82SRafael J. Wysocki
1624d83f905eSRafael J. Wysocki if (pm_wakeup_pending()) {
162569e445abSRafael J. Wysocki dev->power.direct_complete = false;
1626d83f905eSRafael J. Wysocki async_error = -EBUSY;
16271f758b23SMandeep Singh Baines goto Complete;
1628d83f905eSRafael J. Wysocki }
1629d83f905eSRafael J. Wysocki
1630dbf37414SRafael J. Wysocki if (dev->power.syscore)
1631dbf37414SRafael J. Wysocki goto Complete;
1632dbf37414SRafael J. Wysocki
1633dc351d4cSUlf Hansson /* Avoid direct_complete to let wakeup_path propagate. */
16344e1d9a73SPatrice Chotard if (device_may_wakeup(dev) || device_wakeup_path(dev))
1635dc351d4cSUlf Hansson dev->power.direct_complete = false;
1636dc351d4cSUlf Hansson
1637aae4518bSRafael J. Wysocki if (dev->power.direct_complete) {
1638aae4518bSRafael J. Wysocki if (pm_runtime_status_suspended(dev)) {
1639aae4518bSRafael J. Wysocki pm_runtime_disable(dev);
16404a0fa9f9SRafael J. Wysocki if (pm_runtime_status_suspended(dev)) {
16414a0fa9f9SRafael J. Wysocki pm_dev_dbg(dev, state, "direct-complete ");
1642aae4518bSRafael J. Wysocki goto Complete;
16434a0fa9f9SRafael J. Wysocki }
1644aae4518bSRafael J. Wysocki
1645aae4518bSRafael J. Wysocki pm_runtime_enable(dev);
1646aae4518bSRafael J. Wysocki }
1647aae4518bSRafael J. Wysocki dev->power.direct_complete = false;
1648aae4518bSRafael J. Wysocki }
1649aae4518bSRafael J. Wysocki
16500fe8a1beSRafael J. Wysocki dev->power.may_skip_resume = true;
16514a9344cdSPrasad Sodagudi dev->power.must_resume = !dev_pm_test_driver_flags(dev, DPM_FLAG_MAY_SKIP_RESUME);
16520d4b54c6SRafael J. Wysocki
165370fea60dSBenoit Goby dpm_watchdog_set(&wd, dev);
16541e2ef05bSRafael J. Wysocki device_lock(dev);
16551e2ef05bSRafael J. Wysocki
1656564b905aSRafael J. Wysocki if (dev->pm_domain) {
16579cf519d1SRafael J. Wysocki info = "power domain ";
16589cf519d1SRafael J. Wysocki callback = pm_op(&dev->pm_domain->ops, state);
16599cf519d1SRafael J. Wysocki goto Run;
16604d27e9dcSRafael J. Wysocki }
16614d27e9dcSRafael J. Wysocki
16629659cc06SRafael J. Wysocki if (dev->type && dev->type->pm) {
16639cf519d1SRafael J. Wysocki info = "type ";
16649cf519d1SRafael J. Wysocki callback = pm_op(dev->type->pm, state);
16659cf519d1SRafael J. Wysocki goto Run;
16669659cc06SRafael J. Wysocki }
16679659cc06SRafael J. Wysocki
1668a380f2edSRafael J. Wysocki if (dev->class && dev->class->pm) {
16699cf519d1SRafael J. Wysocki info = "class ";
16709cf519d1SRafael J. Wysocki callback = pm_op(dev->class->pm, state);
16719cf519d1SRafael J. Wysocki goto Run;
16721eede070SRafael J. Wysocki }
1673cd59abfcSAlan Stern
16741eede070SRafael J. Wysocki if (dev->bus) {
16751eede070SRafael J. Wysocki if (dev->bus->pm) {
167635cd133cSRafael J. Wysocki info = "bus ";
16779cf519d1SRafael J. Wysocki callback = pm_op(dev->bus->pm, state);
16781eede070SRafael J. Wysocki } else if (dev->bus->suspend) {
167935cd133cSRafael J. Wysocki pm_dev_dbg(dev, state, "legacy bus ");
168053644677SShuah Khan error = legacy_suspend(dev, state, dev->bus->suspend,
168153644677SShuah Khan "legacy bus ");
16829cf519d1SRafael J. Wysocki goto End;
1683cd59abfcSAlan Stern }
16847538e3dbSRafael J. Wysocki }
16857538e3dbSRafael J. Wysocki
16869cf519d1SRafael J. Wysocki Run:
168735cd133cSRafael J. Wysocki if (!callback && dev->driver && dev->driver->pm) {
168835cd133cSRafael J. Wysocki info = "driver ";
168935cd133cSRafael J. Wysocki callback = pm_op(dev->driver->pm, state);
169035cd133cSRafael J. Wysocki }
169135cd133cSRafael J. Wysocki
16929cf519d1SRafael J. Wysocki error = dpm_run_callback(callback, dev, state, info);
16939cf519d1SRafael J. Wysocki
16941eede070SRafael J. Wysocki End:
16954ca46ff3SRafael J. Wysocki if (!error) {
16964ca46ff3SRafael J. Wysocki dev->power.is_suspended = true;
16978512220cSUlf Hansson if (device_may_wakeup(dev))
16988512220cSUlf Hansson dev->power.wakeup_path = true;
16998512220cSUlf Hansson
1700c23bd387SUlf Hansson dpm_propagate_wakeup_to_parent(dev);
1701c23bd387SUlf Hansson dpm_clear_superiors_direct_complete(dev);
17024ca46ff3SRafael J. Wysocki }
17036d0e0e84SAlan Stern
17048e9394ceSGreg Kroah-Hartman device_unlock(dev);
170570fea60dSBenoit Goby dpm_watchdog_clear(&wd);
17061f758b23SMandeep Singh Baines
17071f758b23SMandeep Singh Baines Complete:
170888d26136SAlan Stern if (error)
1709098dff73SRafael J. Wysocki async_error = error;
1710098dff73SRafael J. Wysocki
171105a92622SSahitya Tummala complete_all(&dev->power.completion);
1712431d452aSZhonghui Fu TRACE_SUSPEND(error);
1713cd59abfcSAlan Stern return error;
1714cd59abfcSAlan Stern }
1715cd59abfcSAlan Stern
async_suspend(void * data,async_cookie_t cookie)17165af84b82SRafael J. Wysocki static void async_suspend(void *data, async_cookie_t cookie)
17175af84b82SRafael J. Wysocki {
171873d73f5eSLi zeming struct device *dev = data;
17195af84b82SRafael J. Wysocki int error;
17205af84b82SRafael J. Wysocki
17215af84b82SRafael J. Wysocki error = __device_suspend(dev, pm_transition, true);
17222a77c46dSShuoX Liu if (error) {
17232a77c46dSShuoX Liu dpm_save_failed_dev(dev_name(dev));
17245af84b82SRafael J. Wysocki pm_dev_err(dev, pm_transition, " async", error);
17252a77c46dSShuoX Liu }
17265af84b82SRafael J. Wysocki
17275af84b82SRafael J. Wysocki put_device(dev);
17285af84b82SRafael J. Wysocki }
17295af84b82SRafael J. Wysocki
device_suspend(struct device * dev)17305af84b82SRafael J. Wysocki static int device_suspend(struct device *dev)
17315af84b82SRafael J. Wysocki {
1732f2a424f6SYangtao Li if (dpm_async_fn(dev, async_suspend))
17335af84b82SRafael J. Wysocki return 0;
17345af84b82SRafael J. Wysocki
17355af84b82SRafael J. Wysocki return __device_suspend(dev, pm_transition, false);
17365af84b82SRafael J. Wysocki }
17375af84b82SRafael J. Wysocki
1738cd59abfcSAlan Stern /**
173920d652d7SRafael J. Wysocki * dpm_suspend - Execute "suspend" callbacks for all non-sysdev devices.
17401eede070SRafael J. Wysocki * @state: PM transition of the system being carried out.
1741cd59abfcSAlan Stern */
dpm_suspend(pm_message_t state)174291e7c75bSRafael J. Wysocki int dpm_suspend(pm_message_t state)
1743cd59abfcSAlan Stern {
1744ecf762b2SRafael J. Wysocki ktime_t starttime = ktime_get();
1745cd59abfcSAlan Stern int error = 0;
1746cd59abfcSAlan Stern
1747bb3632c6STodd E Brandt trace_suspend_resume(TPS("dpm_suspend"), state.event, true);
174891e7c75bSRafael J. Wysocki might_sleep();
174991e7c75bSRafael J. Wysocki
17506e863844SLukasz Luba devfreq_suspend();
17512f0aea93SViresh Kumar cpufreq_suspend();
17522f0aea93SViresh Kumar
1753cd59abfcSAlan Stern mutex_lock(&dpm_list_mtx);
17545af84b82SRafael J. Wysocki pm_transition = state;
17555af84b82SRafael J. Wysocki async_error = 0;
17568a43a9abSRafael J. Wysocki while (!list_empty(&dpm_prepared_list)) {
17578a43a9abSRafael J. Wysocki struct device *dev = to_device(dpm_prepared_list.prev);
1758cd59abfcSAlan Stern
17591eede070SRafael J. Wysocki get_device(dev);
17602aa36604SRafael J. Wysocki
1761cd59abfcSAlan Stern mutex_unlock(&dpm_list_mtx);
17621eede070SRafael J. Wysocki
17635af84b82SRafael J. Wysocki error = device_suspend(dev);
17641eede070SRafael J. Wysocki
17651b3cbec1SAlan Stern mutex_lock(&dpm_list_mtx);
17662aa36604SRafael J. Wysocki
1767775b64d2SRafael J. Wysocki if (error) {
17681eede070SRafael J. Wysocki pm_dev_err(dev, state, "", error);
17692a77c46dSShuoX Liu dpm_save_failed_dev(dev_name(dev));
17702aa36604SRafael J. Wysocki } else if (!list_empty(&dev->power.entry)) {
17718a43a9abSRafael J. Wysocki list_move(&dev->power.entry, &dpm_suspended_list);
17722aa36604SRafael J. Wysocki }
17732aa36604SRafael J. Wysocki
17742aa36604SRafael J. Wysocki mutex_unlock(&dpm_list_mtx);
17752aa36604SRafael J. Wysocki
17761eede070SRafael J. Wysocki put_device(dev);
17772aa36604SRafael J. Wysocki
17782aa36604SRafael J. Wysocki mutex_lock(&dpm_list_mtx);
17792aa36604SRafael J. Wysocki
17802aa36604SRafael J. Wysocki if (error || async_error)
17815af84b82SRafael J. Wysocki break;
1782775b64d2SRafael J. Wysocki }
1783775b64d2SRafael J. Wysocki mutex_unlock(&dpm_list_mtx);
17845af84b82SRafael J. Wysocki async_synchronize_full();
17855af84b82SRafael J. Wysocki if (!error)
17865af84b82SRafael J. Wysocki error = async_error;
17872a77c46dSShuoX Liu if (error) {
17882a77c46dSShuoX Liu suspend_stats.failed_suspend++;
17892a77c46dSShuoX Liu dpm_save_failed_step(SUSPEND_SUSPEND);
179048059c09SRafael J. Wysocki }
179148059c09SRafael J. Wysocki dpm_show_time(starttime, state, error, NULL);
1792bb3632c6STodd E Brandt trace_suspend_resume(TPS("dpm_suspend"), state.event, false);
17931eede070SRafael J. Wysocki return error;
17941eede070SRafael J. Wysocki }
17951eede070SRafael J. Wysocki
17961eede070SRafael J. Wysocki /**
179720d652d7SRafael J. Wysocki * device_prepare - Prepare a device for system power transition.
179820d652d7SRafael J. Wysocki * @dev: Device to handle.
17991eede070SRafael J. Wysocki * @state: PM transition of the system being carried out.
180020d652d7SRafael J. Wysocki *
180120d652d7SRafael J. Wysocki * Execute the ->prepare() callback(s) for given device. No new children of the
180220d652d7SRafael J. Wysocki * device may be registered after this function has returned.
18031eede070SRafael J. Wysocki */
device_prepare(struct device * dev,pm_message_t state)1804d1616302SAlan Stern static int device_prepare(struct device *dev, pm_message_t state)
18051eede070SRafael J. Wysocki {
180635cd133cSRafael J. Wysocki int (*callback)(struct device *) = NULL;
1807aae4518bSRafael J. Wysocki int ret = 0;
18081eede070SRafael J. Wysocki
180988d26136SAlan Stern /*
181088d26136SAlan Stern * If a device's parent goes into runtime suspend at the wrong time,
181188d26136SAlan Stern * it won't be possible to resume the device. To prevent this we
181288d26136SAlan Stern * block runtime suspend here, during the prepare phase, and allow
181388d26136SAlan Stern * it again during the complete phase.
181488d26136SAlan Stern */
181588d26136SAlan Stern pm_runtime_get_noresume(dev);
181688d26136SAlan Stern
1817928265e3SRafael J. Wysocki if (dev->power.syscore)
1818928265e3SRafael J. Wysocki return 0;
1819928265e3SRafael J. Wysocki
18208e9394ceSGreg Kroah-Hartman device_lock(dev);
18211eede070SRafael J. Wysocki
18228512220cSUlf Hansson dev->power.wakeup_path = false;
18234ca46ff3SRafael J. Wysocki
1824c62ec461SRafael J. Wysocki if (dev->power.no_pm_callbacks)
1825aa8e54b5STomeu Vizoso goto unlock;
1826aa8e54b5STomeu Vizoso
1827fba1fbf5SThierry Reding if (dev->pm_domain)
182835cd133cSRafael J. Wysocki callback = dev->pm_domain->ops.prepare;
1829fba1fbf5SThierry Reding else if (dev->type && dev->type->pm)
183035cd133cSRafael J. Wysocki callback = dev->type->pm->prepare;
1831fba1fbf5SThierry Reding else if (dev->class && dev->class->pm)
183235cd133cSRafael J. Wysocki callback = dev->class->pm->prepare;
1833fba1fbf5SThierry Reding else if (dev->bus && dev->bus->pm)
183435cd133cSRafael J. Wysocki callback = dev->bus->pm->prepare;
183535cd133cSRafael J. Wysocki
1836fba1fbf5SThierry Reding if (!callback && dev->driver && dev->driver->pm)
183735cd133cSRafael J. Wysocki callback = dev->driver->pm->prepare;
183835cd133cSRafael J. Wysocki
183932e8d689STodd E Brandt if (callback)
1840aae4518bSRafael J. Wysocki ret = callback(dev);
18417538e3dbSRafael J. Wysocki
1842aa8e54b5STomeu Vizoso unlock:
18438e9394ceSGreg Kroah-Hartman device_unlock(dev);
1844775b64d2SRafael J. Wysocki
1845aae4518bSRafael J. Wysocki if (ret < 0) {
1846a759de69SYoungjin Jang suspend_report_result(dev, callback, ret);
1847aa1b9f13SUlf Hansson pm_runtime_put(dev);
1848aae4518bSRafael J. Wysocki return ret;
1849aae4518bSRafael J. Wysocki }
1850aae4518bSRafael J. Wysocki /*
1851aae4518bSRafael J. Wysocki * A positive return value from ->prepare() means "this device appears
1852aae4518bSRafael J. Wysocki * to be runtime-suspended and its state is fine, so if it really is
1853aae4518bSRafael J. Wysocki * runtime-suspended, you can leave it in that state provided that you
1854aae4518bSRafael J. Wysocki * will do the same thing with all of its descendants". This only
1855aae4518bSRafael J. Wysocki * applies to suspend transitions, however.
1856aae4518bSRafael J. Wysocki */
1857aae4518bSRafael J. Wysocki spin_lock_irq(&dev->power.lock);
185808810a41SRafael J. Wysocki dev->power.direct_complete = state.event == PM_EVENT_SUSPEND &&
1859b5252a6cSRafael J. Wysocki (ret > 0 || dev->power.no_pm_callbacks) &&
1860e0751556SRafael J. Wysocki !dev_pm_test_driver_flags(dev, DPM_FLAG_NO_DIRECT_COMPLETE);
1861aae4518bSRafael J. Wysocki spin_unlock_irq(&dev->power.lock);
1862aae4518bSRafael J. Wysocki return 0;
1863775b64d2SRafael J. Wysocki }
1864775b64d2SRafael J. Wysocki
1865775b64d2SRafael J. Wysocki /**
186620d652d7SRafael J. Wysocki * dpm_prepare - Prepare all non-sysdev devices for a system PM transition.
18671eede070SRafael J. Wysocki * @state: PM transition of the system being carried out.
1868cd59abfcSAlan Stern *
186920d652d7SRafael J. Wysocki * Execute the ->prepare() callback(s) for all devices.
18701eede070SRafael J. Wysocki */
dpm_prepare(pm_message_t state)187191e7c75bSRafael J. Wysocki int dpm_prepare(pm_message_t state)
18721eede070SRafael J. Wysocki {
18731eede070SRafael J. Wysocki int error = 0;
18741eede070SRafael J. Wysocki
1875bb3632c6STodd E Brandt trace_suspend_resume(TPS("dpm_prepare"), state.event, true);
187691e7c75bSRafael J. Wysocki might_sleep();
187791e7c75bSRafael J. Wysocki
1878013c074fSStrashko, Grygorii /*
1879013c074fSStrashko, Grygorii * Give a chance for the known devices to complete their probes, before
1880013c074fSStrashko, Grygorii * disable probing of devices. This sync point is important at least
1881013c074fSStrashko, Grygorii * at boot time + hibernation restore.
1882013c074fSStrashko, Grygorii */
1883013c074fSStrashko, Grygorii wait_for_device_probe();
1884013c074fSStrashko, Grygorii /*
1885013c074fSStrashko, Grygorii * It is unsafe if probing of devices will happen during suspend or
1886013c074fSStrashko, Grygorii * hibernation and system behavior will be unpredictable in this case.
1887013c074fSStrashko, Grygorii * So, let's prohibit device's probing here and defer their probes
1888013c074fSStrashko, Grygorii * instead. The normal behavior will be restored in dpm_complete().
1889013c074fSStrashko, Grygorii */
1890013c074fSStrashko, Grygorii device_block_probing();
1891013c074fSStrashko, Grygorii
18921eede070SRafael J. Wysocki mutex_lock(&dpm_list_mtx);
1893544e737dSRafael J. Wysocki while (!list_empty(&dpm_list) && !error) {
18941eede070SRafael J. Wysocki struct device *dev = to_device(dpm_list.next);
18951eede070SRafael J. Wysocki
18961eede070SRafael J. Wysocki get_device(dev);
18972aa36604SRafael J. Wysocki
18981eede070SRafael J. Wysocki mutex_unlock(&dpm_list_mtx);
18991eede070SRafael J. Wysocki
190032e8d689STodd E Brandt trace_device_pm_callback_start(dev, "", state.event);
19011e2ef05bSRafael J. Wysocki error = device_prepare(dev, state);
190232e8d689STodd E Brandt trace_device_pm_callback_end(dev, error);
19031eede070SRafael J. Wysocki
19041eede070SRafael J. Wysocki mutex_lock(&dpm_list_mtx);
19052aa36604SRafael J. Wysocki
19062aa36604SRafael J. Wysocki if (!error) {
1907f76b168bSAlan Stern dev->power.is_prepared = true;
19081eede070SRafael J. Wysocki if (!list_empty(&dev->power.entry))
19098a43a9abSRafael J. Wysocki list_move_tail(&dev->power.entry, &dpm_prepared_list);
19102aa36604SRafael J. Wysocki } else if (error == -EAGAIN) {
19112aa36604SRafael J. Wysocki error = 0;
19122aa36604SRafael J. Wysocki } else {
19132aa36604SRafael J. Wysocki dev_info(dev, "not prepared for power transition: code %d\n",
19142aa36604SRafael J. Wysocki error);
19152aa36604SRafael J. Wysocki }
19162aa36604SRafael J. Wysocki
19172aa36604SRafael J. Wysocki mutex_unlock(&dpm_list_mtx);
19182aa36604SRafael J. Wysocki
19191eede070SRafael J. Wysocki put_device(dev);
19202aa36604SRafael J. Wysocki
19212aa36604SRafael J. Wysocki mutex_lock(&dpm_list_mtx);
19221eede070SRafael J. Wysocki }
19231eede070SRafael J. Wysocki mutex_unlock(&dpm_list_mtx);
1924bb3632c6STodd E Brandt trace_suspend_resume(TPS("dpm_prepare"), state.event, false);
19251eede070SRafael J. Wysocki return error;
19261eede070SRafael J. Wysocki }
19271eede070SRafael J. Wysocki
19281eede070SRafael J. Wysocki /**
192920d652d7SRafael J. Wysocki * dpm_suspend_start - Prepare devices for PM transition and suspend them.
19301eede070SRafael J. Wysocki * @state: PM transition of the system being carried out.
19311eede070SRafael J. Wysocki *
193220d652d7SRafael J. Wysocki * Prepare all non-sysdev devices for system PM transition and execute "suspend"
193320d652d7SRafael J. Wysocki * callbacks for them.
1934cd59abfcSAlan Stern */
dpm_suspend_start(pm_message_t state)1935d1616302SAlan Stern int dpm_suspend_start(pm_message_t state)
1936cd59abfcSAlan Stern {
19373540d38dSBart Van Assche ktime_t starttime = ktime_get();
1938775b64d2SRafael J. Wysocki int error;
1939cd59abfcSAlan Stern
19401eede070SRafael J. Wysocki error = dpm_prepare(state);
19412a77c46dSShuoX Liu if (error) {
19422a77c46dSShuoX Liu suspend_stats.failed_prepare++;
19432a77c46dSShuoX Liu dpm_save_failed_step(SUSPEND_PREPARE);
19442a77c46dSShuoX Liu } else
1945775b64d2SRafael J. Wysocki error = dpm_suspend(state);
19463540d38dSBart Van Assche dpm_show_time(starttime, state, error, "start");
1947cd59abfcSAlan Stern return error;
1948cd59abfcSAlan Stern }
1949d1616302SAlan Stern EXPORT_SYMBOL_GPL(dpm_suspend_start);
1950cd59abfcSAlan Stern
__suspend_report_result(const char * function,struct device * dev,void * fn,int ret)1951a759de69SYoungjin Jang void __suspend_report_result(const char *function, struct device *dev, void *fn, int ret)
1952cd59abfcSAlan Stern {
1953c80cfb04SBjorn Helgaas if (ret)
1954a759de69SYoungjin Jang dev_err(dev, "%s(): %pS returns %d\n", function, fn, ret);
1955cd59abfcSAlan Stern }
1956cd59abfcSAlan Stern EXPORT_SYMBOL_GPL(__suspend_report_result);
1957f8824ceeSRafael J. Wysocki
1958f8824ceeSRafael J. Wysocki /**
1959f8824ceeSRafael J. Wysocki * device_pm_wait_for_dev - Wait for suspend/resume of a device to complete.
1960f8824ceeSRafael J. Wysocki * @subordinate: Device that needs to wait for @dev.
19610b237cb2SYangtao Li * @dev: Device to wait for.
1962f8824ceeSRafael J. Wysocki */
device_pm_wait_for_dev(struct device * subordinate,struct device * dev)1963098dff73SRafael J. Wysocki int device_pm_wait_for_dev(struct device *subordinate, struct device *dev)
1964f8824ceeSRafael J. Wysocki {
1965f8824ceeSRafael J. Wysocki dpm_wait(dev, subordinate->power.async_suspend);
1966098dff73SRafael J. Wysocki return async_error;
1967f8824ceeSRafael J. Wysocki }
1968f8824ceeSRafael J. Wysocki EXPORT_SYMBOL_GPL(device_pm_wait_for_dev);
1969dfe3212eSMing Lei
1970dfe3212eSMing Lei /**
1971dfe3212eSMing Lei * dpm_for_each_dev - device iterator.
1972dfe3212eSMing Lei * @data: data for the callback.
1973dfe3212eSMing Lei * @fn: function to be called for each device.
1974dfe3212eSMing Lei *
1975dfe3212eSMing Lei * Iterate over devices in dpm_list, and call @fn for each device,
1976dfe3212eSMing Lei * passing it @data.
1977dfe3212eSMing Lei */
dpm_for_each_dev(void * data,void (* fn)(struct device *,void *))1978dfe3212eSMing Lei void dpm_for_each_dev(void *data, void (*fn)(struct device *, void *))
1979dfe3212eSMing Lei {
1980dfe3212eSMing Lei struct device *dev;
1981dfe3212eSMing Lei
1982dfe3212eSMing Lei if (!fn)
1983dfe3212eSMing Lei return;
1984dfe3212eSMing Lei
1985dfe3212eSMing Lei device_pm_lock();
1986dfe3212eSMing Lei list_for_each_entry(dev, &dpm_list, power.entry)
1987dfe3212eSMing Lei fn(dev, data);
1988dfe3212eSMing Lei device_pm_unlock();
1989dfe3212eSMing Lei }
1990dfe3212eSMing Lei EXPORT_SYMBOL_GPL(dpm_for_each_dev);
1991aa8e54b5STomeu Vizoso
pm_ops_is_empty(const struct dev_pm_ops * ops)1992aa8e54b5STomeu Vizoso static bool pm_ops_is_empty(const struct dev_pm_ops *ops)
1993aa8e54b5STomeu Vizoso {
1994aa8e54b5STomeu Vizoso if (!ops)
1995aa8e54b5STomeu Vizoso return true;
1996aa8e54b5STomeu Vizoso
1997aa8e54b5STomeu Vizoso return !ops->prepare &&
1998aa8e54b5STomeu Vizoso !ops->suspend &&
1999aa8e54b5STomeu Vizoso !ops->suspend_late &&
2000aa8e54b5STomeu Vizoso !ops->suspend_noirq &&
2001aa8e54b5STomeu Vizoso !ops->resume_noirq &&
2002aa8e54b5STomeu Vizoso !ops->resume_early &&
2003aa8e54b5STomeu Vizoso !ops->resume &&
2004aa8e54b5STomeu Vizoso !ops->complete;
2005aa8e54b5STomeu Vizoso }
2006aa8e54b5STomeu Vizoso
device_pm_check_callbacks(struct device * dev)2007aa8e54b5STomeu Vizoso void device_pm_check_callbacks(struct device *dev)
2008aa8e54b5STomeu Vizoso {
2009524bb1daSDmitry Baryshkov unsigned long flags;
2010524bb1daSDmitry Baryshkov
2011524bb1daSDmitry Baryshkov spin_lock_irqsave(&dev->power.lock, flags);
2012aa8e54b5STomeu Vizoso dev->power.no_pm_callbacks =
2013157c460eSRafael J. Wysocki (!dev->bus || (pm_ops_is_empty(dev->bus->pm) &&
2014157c460eSRafael J. Wysocki !dev->bus->suspend && !dev->bus->resume)) &&
2015a380f2edSRafael J. Wysocki (!dev->class || pm_ops_is_empty(dev->class->pm)) &&
2016aa8e54b5STomeu Vizoso (!dev->type || pm_ops_is_empty(dev->type->pm)) &&
2017aa8e54b5STomeu Vizoso (!dev->pm_domain || pm_ops_is_empty(&dev->pm_domain->ops)) &&
2018157c460eSRafael J. Wysocki (!dev->driver || (pm_ops_is_empty(dev->driver->pm) &&
2019157c460eSRafael J. Wysocki !dev->driver->suspend && !dev->driver->resume));
2020524bb1daSDmitry Baryshkov spin_unlock_irqrestore(&dev->power.lock, flags);
2021aa8e54b5STomeu Vizoso }
2022c4b65157SRafael J. Wysocki
dev_pm_skip_suspend(struct device * dev)2023fa2bfeadSRafael J. Wysocki bool dev_pm_skip_suspend(struct device *dev)
2024c4b65157SRafael J. Wysocki {
2025c4b65157SRafael J. Wysocki return dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) &&
2026c4b65157SRafael J. Wysocki pm_runtime_status_suspended(dev);
2027c4b65157SRafael J. Wysocki }
2028