18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * drivers/base/power/main.c - Where the driver meets power management. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2003 Patrick Mochel 68c2ecf20Sopenharmony_ci * Copyright (c) 2003 Open Source Development Lab 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * The driver model core calls device_pm_add() when a device is registered. 98c2ecf20Sopenharmony_ci * This will initialize the embedded device_pm_info object in the device 108c2ecf20Sopenharmony_ci * and add it to the list of power-controlled devices. sysfs entries for 118c2ecf20Sopenharmony_ci * controlling device power management will also be added. 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * A separate list is used for keeping track of power info, because the power 148c2ecf20Sopenharmony_ci * domain dependencies may differ from the ancestral dependencies that the 158c2ecf20Sopenharmony_ci * subsystem list maintains. 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "PM: " fmt 198c2ecf20Sopenharmony_ci#define dev_fmt pr_fmt 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <linux/device.h> 228c2ecf20Sopenharmony_ci#include <linux/export.h> 238c2ecf20Sopenharmony_ci#include <linux/mutex.h> 248c2ecf20Sopenharmony_ci#include <linux/pm.h> 258c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 268c2ecf20Sopenharmony_ci#include <linux/pm-trace.h> 278c2ecf20Sopenharmony_ci#include <linux/pm_wakeirq.h> 288c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 298c2ecf20Sopenharmony_ci#include <linux/sched.h> 308c2ecf20Sopenharmony_ci#include <linux/sched/debug.h> 318c2ecf20Sopenharmony_ci#include <linux/async.h> 328c2ecf20Sopenharmony_ci#include <linux/suspend.h> 338c2ecf20Sopenharmony_ci#include <trace/events/power.h> 348c2ecf20Sopenharmony_ci#include <linux/cpufreq.h> 358c2ecf20Sopenharmony_ci#include <linux/cpuidle.h> 368c2ecf20Sopenharmony_ci#include <linux/devfreq.h> 378c2ecf20Sopenharmony_ci#include <linux/timer.h> 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#include "../base.h" 408c2ecf20Sopenharmony_ci#include "power.h" 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_citypedef int (*pm_callback_t)(struct device *); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define list_for_each_entry_rcu_locked(pos, head, member) \ 458c2ecf20Sopenharmony_ci list_for_each_entry_rcu(pos, head, member, \ 468c2ecf20Sopenharmony_ci device_links_read_lock_held()) 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* 498c2ecf20Sopenharmony_ci * The entries in the dpm_list list are in a depth first order, simply 508c2ecf20Sopenharmony_ci * because children are guaranteed to be discovered after parents, and 518c2ecf20Sopenharmony_ci * are inserted at the back of the list on discovery. 528c2ecf20Sopenharmony_ci * 538c2ecf20Sopenharmony_ci * Since device_pm_add() may be called with a device lock held, 548c2ecf20Sopenharmony_ci * we must never try to acquire a device lock while holding 558c2ecf20Sopenharmony_ci * dpm_list_mutex. 568c2ecf20Sopenharmony_ci */ 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ciLIST_HEAD(dpm_list); 598c2ecf20Sopenharmony_cistatic LIST_HEAD(dpm_prepared_list); 608c2ecf20Sopenharmony_cistatic LIST_HEAD(dpm_suspended_list); 618c2ecf20Sopenharmony_cistatic LIST_HEAD(dpm_late_early_list); 628c2ecf20Sopenharmony_cistatic LIST_HEAD(dpm_noirq_list); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistruct suspend_stats suspend_stats; 658c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(dpm_list_mtx); 668c2ecf20Sopenharmony_cistatic pm_message_t pm_transition; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic int async_error; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic const char *pm_verb(int event) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci switch (event) { 738c2ecf20Sopenharmony_ci case PM_EVENT_SUSPEND: 748c2ecf20Sopenharmony_ci return "suspend"; 758c2ecf20Sopenharmony_ci case PM_EVENT_RESUME: 768c2ecf20Sopenharmony_ci return "resume"; 778c2ecf20Sopenharmony_ci case PM_EVENT_FREEZE: 788c2ecf20Sopenharmony_ci return "freeze"; 798c2ecf20Sopenharmony_ci case PM_EVENT_QUIESCE: 808c2ecf20Sopenharmony_ci return "quiesce"; 818c2ecf20Sopenharmony_ci case PM_EVENT_HIBERNATE: 828c2ecf20Sopenharmony_ci return "hibernate"; 838c2ecf20Sopenharmony_ci case PM_EVENT_THAW: 848c2ecf20Sopenharmony_ci return "thaw"; 858c2ecf20Sopenharmony_ci case PM_EVENT_RESTORE: 868c2ecf20Sopenharmony_ci return "restore"; 878c2ecf20Sopenharmony_ci case PM_EVENT_RECOVER: 888c2ecf20Sopenharmony_ci return "recover"; 898c2ecf20Sopenharmony_ci default: 908c2ecf20Sopenharmony_ci return "(unknown PM event)"; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci/** 958c2ecf20Sopenharmony_ci * device_pm_sleep_init - Initialize system suspend-related device fields. 968c2ecf20Sopenharmony_ci * @dev: Device object being initialized. 978c2ecf20Sopenharmony_ci */ 988c2ecf20Sopenharmony_civoid device_pm_sleep_init(struct device *dev) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci dev->power.is_prepared = false; 1018c2ecf20Sopenharmony_ci dev->power.is_suspended = false; 1028c2ecf20Sopenharmony_ci dev->power.is_noirq_suspended = false; 1038c2ecf20Sopenharmony_ci dev->power.is_late_suspended = false; 1048c2ecf20Sopenharmony_ci init_completion(&dev->power.completion); 1058c2ecf20Sopenharmony_ci complete_all(&dev->power.completion); 1068c2ecf20Sopenharmony_ci dev->power.wakeup = NULL; 1078c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dev->power.entry); 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci/** 1118c2ecf20Sopenharmony_ci * device_pm_lock - Lock the list of active devices used by the PM core. 1128c2ecf20Sopenharmony_ci */ 1138c2ecf20Sopenharmony_civoid device_pm_lock(void) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci mutex_lock(&dpm_list_mtx); 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci/** 1198c2ecf20Sopenharmony_ci * device_pm_unlock - Unlock the list of active devices used by the PM core. 1208c2ecf20Sopenharmony_ci */ 1218c2ecf20Sopenharmony_civoid device_pm_unlock(void) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci mutex_unlock(&dpm_list_mtx); 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci/** 1278c2ecf20Sopenharmony_ci * device_pm_add - Add a device to the PM core's list of active devices. 1288c2ecf20Sopenharmony_ci * @dev: Device to add to the list. 1298c2ecf20Sopenharmony_ci */ 1308c2ecf20Sopenharmony_civoid device_pm_add(struct device *dev) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci /* Skip PM setup/initialization. */ 1338c2ecf20Sopenharmony_ci if (device_pm_not_required(dev)) 1348c2ecf20Sopenharmony_ci return; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci pr_debug("Adding info for %s:%s\n", 1378c2ecf20Sopenharmony_ci dev->bus ? dev->bus->name : "No Bus", dev_name(dev)); 1388c2ecf20Sopenharmony_ci device_pm_check_callbacks(dev); 1398c2ecf20Sopenharmony_ci mutex_lock(&dpm_list_mtx); 1408c2ecf20Sopenharmony_ci if (dev->parent && dev->parent->power.is_prepared) 1418c2ecf20Sopenharmony_ci dev_warn(dev, "parent %s should not be sleeping\n", 1428c2ecf20Sopenharmony_ci dev_name(dev->parent)); 1438c2ecf20Sopenharmony_ci list_add_tail(&dev->power.entry, &dpm_list); 1448c2ecf20Sopenharmony_ci dev->power.in_dpm_list = true; 1458c2ecf20Sopenharmony_ci mutex_unlock(&dpm_list_mtx); 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci/** 1498c2ecf20Sopenharmony_ci * device_pm_remove - Remove a device from the PM core's list of active devices. 1508c2ecf20Sopenharmony_ci * @dev: Device to be removed from the list. 1518c2ecf20Sopenharmony_ci */ 1528c2ecf20Sopenharmony_civoid device_pm_remove(struct device *dev) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci if (device_pm_not_required(dev)) 1558c2ecf20Sopenharmony_ci return; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci pr_debug("Removing info for %s:%s\n", 1588c2ecf20Sopenharmony_ci dev->bus ? dev->bus->name : "No Bus", dev_name(dev)); 1598c2ecf20Sopenharmony_ci complete_all(&dev->power.completion); 1608c2ecf20Sopenharmony_ci mutex_lock(&dpm_list_mtx); 1618c2ecf20Sopenharmony_ci list_del_init(&dev->power.entry); 1628c2ecf20Sopenharmony_ci dev->power.in_dpm_list = false; 1638c2ecf20Sopenharmony_ci mutex_unlock(&dpm_list_mtx); 1648c2ecf20Sopenharmony_ci device_wakeup_disable(dev); 1658c2ecf20Sopenharmony_ci pm_runtime_remove(dev); 1668c2ecf20Sopenharmony_ci device_pm_check_callbacks(dev); 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci/** 1708c2ecf20Sopenharmony_ci * device_pm_move_before - Move device in the PM core's list of active devices. 1718c2ecf20Sopenharmony_ci * @deva: Device to move in dpm_list. 1728c2ecf20Sopenharmony_ci * @devb: Device @deva should come before. 1738c2ecf20Sopenharmony_ci */ 1748c2ecf20Sopenharmony_civoid device_pm_move_before(struct device *deva, struct device *devb) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci pr_debug("Moving %s:%s before %s:%s\n", 1778c2ecf20Sopenharmony_ci deva->bus ? deva->bus->name : "No Bus", dev_name(deva), 1788c2ecf20Sopenharmony_ci devb->bus ? devb->bus->name : "No Bus", dev_name(devb)); 1798c2ecf20Sopenharmony_ci /* Delete deva from dpm_list and reinsert before devb. */ 1808c2ecf20Sopenharmony_ci list_move_tail(&deva->power.entry, &devb->power.entry); 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci/** 1848c2ecf20Sopenharmony_ci * device_pm_move_after - Move device in the PM core's list of active devices. 1858c2ecf20Sopenharmony_ci * @deva: Device to move in dpm_list. 1868c2ecf20Sopenharmony_ci * @devb: Device @deva should come after. 1878c2ecf20Sopenharmony_ci */ 1888c2ecf20Sopenharmony_civoid device_pm_move_after(struct device *deva, struct device *devb) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci pr_debug("Moving %s:%s after %s:%s\n", 1918c2ecf20Sopenharmony_ci deva->bus ? deva->bus->name : "No Bus", dev_name(deva), 1928c2ecf20Sopenharmony_ci devb->bus ? devb->bus->name : "No Bus", dev_name(devb)); 1938c2ecf20Sopenharmony_ci /* Delete deva from dpm_list and reinsert after devb. */ 1948c2ecf20Sopenharmony_ci list_move(&deva->power.entry, &devb->power.entry); 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci/** 1988c2ecf20Sopenharmony_ci * device_pm_move_last - Move device to end of the PM core's list of devices. 1998c2ecf20Sopenharmony_ci * @dev: Device to move in dpm_list. 2008c2ecf20Sopenharmony_ci */ 2018c2ecf20Sopenharmony_civoid device_pm_move_last(struct device *dev) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci pr_debug("Moving %s:%s to end of list\n", 2048c2ecf20Sopenharmony_ci dev->bus ? dev->bus->name : "No Bus", dev_name(dev)); 2058c2ecf20Sopenharmony_ci list_move_tail(&dev->power.entry, &dpm_list); 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic ktime_t initcall_debug_start(struct device *dev, void *cb) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci if (!pm_print_times_enabled) 2118c2ecf20Sopenharmony_ci return 0; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci dev_info(dev, "calling %pS @ %i, parent: %s\n", cb, 2148c2ecf20Sopenharmony_ci task_pid_nr(current), 2158c2ecf20Sopenharmony_ci dev->parent ? dev_name(dev->parent) : "none"); 2168c2ecf20Sopenharmony_ci return ktime_get(); 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic void initcall_debug_report(struct device *dev, ktime_t calltime, 2208c2ecf20Sopenharmony_ci void *cb, int error) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci ktime_t rettime; 2238c2ecf20Sopenharmony_ci s64 nsecs; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (!pm_print_times_enabled) 2268c2ecf20Sopenharmony_ci return; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci rettime = ktime_get(); 2298c2ecf20Sopenharmony_ci nsecs = (s64) ktime_to_ns(ktime_sub(rettime, calltime)); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci dev_info(dev, "%pS returned %d after %Ld usecs\n", cb, error, 2328c2ecf20Sopenharmony_ci (unsigned long long)nsecs >> 10); 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci/** 2368c2ecf20Sopenharmony_ci * dpm_wait - Wait for a PM operation to complete. 2378c2ecf20Sopenharmony_ci * @dev: Device to wait for. 2388c2ecf20Sopenharmony_ci * @async: If unset, wait only if the device's power.async_suspend flag is set. 2398c2ecf20Sopenharmony_ci */ 2408c2ecf20Sopenharmony_cistatic void dpm_wait(struct device *dev, bool async) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci if (!dev) 2438c2ecf20Sopenharmony_ci return; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci if (async || (pm_async_enabled && dev->power.async_suspend)) 2468c2ecf20Sopenharmony_ci wait_for_completion(&dev->power.completion); 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic int dpm_wait_fn(struct device *dev, void *async_ptr) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci dpm_wait(dev, *((bool *)async_ptr)); 2528c2ecf20Sopenharmony_ci return 0; 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic void dpm_wait_for_children(struct device *dev, bool async) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci device_for_each_child(dev, &async, dpm_wait_fn); 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic void dpm_wait_for_suppliers(struct device *dev, bool async) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci struct device_link *link; 2638c2ecf20Sopenharmony_ci int idx; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci idx = device_links_read_lock(); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci /* 2688c2ecf20Sopenharmony_ci * If the supplier goes away right after we've checked the link to it, 2698c2ecf20Sopenharmony_ci * we'll wait for its completion to change the state, but that's fine, 2708c2ecf20Sopenharmony_ci * because the only things that will block as a result are the SRCU 2718c2ecf20Sopenharmony_ci * callbacks freeing the link objects for the links in the list we're 2728c2ecf20Sopenharmony_ci * walking. 2738c2ecf20Sopenharmony_ci */ 2748c2ecf20Sopenharmony_ci list_for_each_entry_rcu_locked(link, &dev->links.suppliers, c_node) 2758c2ecf20Sopenharmony_ci if (READ_ONCE(link->status) != DL_STATE_DORMANT) 2768c2ecf20Sopenharmony_ci dpm_wait(link->supplier, async); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci device_links_read_unlock(idx); 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic bool dpm_wait_for_superior(struct device *dev, bool async) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci struct device *parent; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci /* 2868c2ecf20Sopenharmony_ci * If the device is resumed asynchronously and the parent's callback 2878c2ecf20Sopenharmony_ci * deletes both the device and the parent itself, the parent object may 2888c2ecf20Sopenharmony_ci * be freed while this function is running, so avoid that by reference 2898c2ecf20Sopenharmony_ci * counting the parent once more unless the device has been deleted 2908c2ecf20Sopenharmony_ci * already (in which case return right away). 2918c2ecf20Sopenharmony_ci */ 2928c2ecf20Sopenharmony_ci mutex_lock(&dpm_list_mtx); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci if (!device_pm_initialized(dev)) { 2958c2ecf20Sopenharmony_ci mutex_unlock(&dpm_list_mtx); 2968c2ecf20Sopenharmony_ci return false; 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci parent = get_device(dev->parent); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci mutex_unlock(&dpm_list_mtx); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci dpm_wait(parent, async); 3048c2ecf20Sopenharmony_ci put_device(parent); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci dpm_wait_for_suppliers(dev, async); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci /* 3098c2ecf20Sopenharmony_ci * If the parent's callback has deleted the device, attempting to resume 3108c2ecf20Sopenharmony_ci * it would be invalid, so avoid doing that then. 3118c2ecf20Sopenharmony_ci */ 3128c2ecf20Sopenharmony_ci return device_pm_initialized(dev); 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic void dpm_wait_for_consumers(struct device *dev, bool async) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci struct device_link *link; 3188c2ecf20Sopenharmony_ci int idx; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci idx = device_links_read_lock(); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci /* 3238c2ecf20Sopenharmony_ci * The status of a device link can only be changed from "dormant" by a 3248c2ecf20Sopenharmony_ci * probe, but that cannot happen during system suspend/resume. In 3258c2ecf20Sopenharmony_ci * theory it can change to "dormant" at that time, but then it is 3268c2ecf20Sopenharmony_ci * reasonable to wait for the target device anyway (eg. if it goes 3278c2ecf20Sopenharmony_ci * away, it's better to wait for it to go away completely and then 3288c2ecf20Sopenharmony_ci * continue instead of trying to continue in parallel with its 3298c2ecf20Sopenharmony_ci * unregistration). 3308c2ecf20Sopenharmony_ci */ 3318c2ecf20Sopenharmony_ci list_for_each_entry_rcu_locked(link, &dev->links.consumers, s_node) 3328c2ecf20Sopenharmony_ci if (READ_ONCE(link->status) != DL_STATE_DORMANT) 3338c2ecf20Sopenharmony_ci dpm_wait(link->consumer, async); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci device_links_read_unlock(idx); 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic void dpm_wait_for_subordinate(struct device *dev, bool async) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci dpm_wait_for_children(dev, async); 3418c2ecf20Sopenharmony_ci dpm_wait_for_consumers(dev, async); 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci/** 3458c2ecf20Sopenharmony_ci * pm_op - Return the PM operation appropriate for given PM event. 3468c2ecf20Sopenharmony_ci * @ops: PM operations to choose from. 3478c2ecf20Sopenharmony_ci * @state: PM transition of the system being carried out. 3488c2ecf20Sopenharmony_ci */ 3498c2ecf20Sopenharmony_cistatic pm_callback_t pm_op(const struct dev_pm_ops *ops, pm_message_t state) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci switch (state.event) { 3528c2ecf20Sopenharmony_ci#ifdef CONFIG_SUSPEND 3538c2ecf20Sopenharmony_ci case PM_EVENT_SUSPEND: 3548c2ecf20Sopenharmony_ci return ops->suspend; 3558c2ecf20Sopenharmony_ci case PM_EVENT_RESUME: 3568c2ecf20Sopenharmony_ci return ops->resume; 3578c2ecf20Sopenharmony_ci#endif /* CONFIG_SUSPEND */ 3588c2ecf20Sopenharmony_ci#ifdef CONFIG_HIBERNATE_CALLBACKS 3598c2ecf20Sopenharmony_ci case PM_EVENT_FREEZE: 3608c2ecf20Sopenharmony_ci case PM_EVENT_QUIESCE: 3618c2ecf20Sopenharmony_ci return ops->freeze; 3628c2ecf20Sopenharmony_ci case PM_EVENT_HIBERNATE: 3638c2ecf20Sopenharmony_ci return ops->poweroff; 3648c2ecf20Sopenharmony_ci case PM_EVENT_THAW: 3658c2ecf20Sopenharmony_ci case PM_EVENT_RECOVER: 3668c2ecf20Sopenharmony_ci return ops->thaw; 3678c2ecf20Sopenharmony_ci case PM_EVENT_RESTORE: 3688c2ecf20Sopenharmony_ci return ops->restore; 3698c2ecf20Sopenharmony_ci#endif /* CONFIG_HIBERNATE_CALLBACKS */ 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci return NULL; 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci/** 3768c2ecf20Sopenharmony_ci * pm_late_early_op - Return the PM operation appropriate for given PM event. 3778c2ecf20Sopenharmony_ci * @ops: PM operations to choose from. 3788c2ecf20Sopenharmony_ci * @state: PM transition of the system being carried out. 3798c2ecf20Sopenharmony_ci * 3808c2ecf20Sopenharmony_ci * Runtime PM is disabled for @dev while this function is being executed. 3818c2ecf20Sopenharmony_ci */ 3828c2ecf20Sopenharmony_cistatic pm_callback_t pm_late_early_op(const struct dev_pm_ops *ops, 3838c2ecf20Sopenharmony_ci pm_message_t state) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci switch (state.event) { 3868c2ecf20Sopenharmony_ci#ifdef CONFIG_SUSPEND 3878c2ecf20Sopenharmony_ci case PM_EVENT_SUSPEND: 3888c2ecf20Sopenharmony_ci return ops->suspend_late; 3898c2ecf20Sopenharmony_ci case PM_EVENT_RESUME: 3908c2ecf20Sopenharmony_ci return ops->resume_early; 3918c2ecf20Sopenharmony_ci#endif /* CONFIG_SUSPEND */ 3928c2ecf20Sopenharmony_ci#ifdef CONFIG_HIBERNATE_CALLBACKS 3938c2ecf20Sopenharmony_ci case PM_EVENT_FREEZE: 3948c2ecf20Sopenharmony_ci case PM_EVENT_QUIESCE: 3958c2ecf20Sopenharmony_ci return ops->freeze_late; 3968c2ecf20Sopenharmony_ci case PM_EVENT_HIBERNATE: 3978c2ecf20Sopenharmony_ci return ops->poweroff_late; 3988c2ecf20Sopenharmony_ci case PM_EVENT_THAW: 3998c2ecf20Sopenharmony_ci case PM_EVENT_RECOVER: 4008c2ecf20Sopenharmony_ci return ops->thaw_early; 4018c2ecf20Sopenharmony_ci case PM_EVENT_RESTORE: 4028c2ecf20Sopenharmony_ci return ops->restore_early; 4038c2ecf20Sopenharmony_ci#endif /* CONFIG_HIBERNATE_CALLBACKS */ 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci return NULL; 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci/** 4108c2ecf20Sopenharmony_ci * pm_noirq_op - Return the PM operation appropriate for given PM event. 4118c2ecf20Sopenharmony_ci * @ops: PM operations to choose from. 4128c2ecf20Sopenharmony_ci * @state: PM transition of the system being carried out. 4138c2ecf20Sopenharmony_ci * 4148c2ecf20Sopenharmony_ci * The driver of @dev will not receive interrupts while this function is being 4158c2ecf20Sopenharmony_ci * executed. 4168c2ecf20Sopenharmony_ci */ 4178c2ecf20Sopenharmony_cistatic pm_callback_t pm_noirq_op(const struct dev_pm_ops *ops, pm_message_t state) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci switch (state.event) { 4208c2ecf20Sopenharmony_ci#ifdef CONFIG_SUSPEND 4218c2ecf20Sopenharmony_ci case PM_EVENT_SUSPEND: 4228c2ecf20Sopenharmony_ci return ops->suspend_noirq; 4238c2ecf20Sopenharmony_ci case PM_EVENT_RESUME: 4248c2ecf20Sopenharmony_ci return ops->resume_noirq; 4258c2ecf20Sopenharmony_ci#endif /* CONFIG_SUSPEND */ 4268c2ecf20Sopenharmony_ci#ifdef CONFIG_HIBERNATE_CALLBACKS 4278c2ecf20Sopenharmony_ci case PM_EVENT_FREEZE: 4288c2ecf20Sopenharmony_ci case PM_EVENT_QUIESCE: 4298c2ecf20Sopenharmony_ci return ops->freeze_noirq; 4308c2ecf20Sopenharmony_ci case PM_EVENT_HIBERNATE: 4318c2ecf20Sopenharmony_ci return ops->poweroff_noirq; 4328c2ecf20Sopenharmony_ci case PM_EVENT_THAW: 4338c2ecf20Sopenharmony_ci case PM_EVENT_RECOVER: 4348c2ecf20Sopenharmony_ci return ops->thaw_noirq; 4358c2ecf20Sopenharmony_ci case PM_EVENT_RESTORE: 4368c2ecf20Sopenharmony_ci return ops->restore_noirq; 4378c2ecf20Sopenharmony_ci#endif /* CONFIG_HIBERNATE_CALLBACKS */ 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci return NULL; 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cistatic void pm_dev_dbg(struct device *dev, pm_message_t state, const char *info) 4448c2ecf20Sopenharmony_ci{ 4458c2ecf20Sopenharmony_ci dev_dbg(dev, "%s%s%s\n", info, pm_verb(state.event), 4468c2ecf20Sopenharmony_ci ((state.event & PM_EVENT_SLEEP) && device_may_wakeup(dev)) ? 4478c2ecf20Sopenharmony_ci ", may wakeup" : ""); 4488c2ecf20Sopenharmony_ci} 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cistatic void pm_dev_err(struct device *dev, pm_message_t state, const char *info, 4518c2ecf20Sopenharmony_ci int error) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci dev_err(dev, "failed to %s%s: error %d\n", pm_verb(state.event), info, 4548c2ecf20Sopenharmony_ci error); 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic void dpm_show_time(ktime_t starttime, pm_message_t state, int error, 4588c2ecf20Sopenharmony_ci const char *info) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci ktime_t calltime; 4618c2ecf20Sopenharmony_ci u64 usecs64; 4628c2ecf20Sopenharmony_ci int usecs; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci calltime = ktime_get(); 4658c2ecf20Sopenharmony_ci usecs64 = ktime_to_ns(ktime_sub(calltime, starttime)); 4668c2ecf20Sopenharmony_ci do_div(usecs64, NSEC_PER_USEC); 4678c2ecf20Sopenharmony_ci usecs = usecs64; 4688c2ecf20Sopenharmony_ci if (usecs == 0) 4698c2ecf20Sopenharmony_ci usecs = 1; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci pm_pr_dbg("%s%s%s of devices %s after %ld.%03ld msecs\n", 4728c2ecf20Sopenharmony_ci info ?: "", info ? " " : "", pm_verb(state.event), 4738c2ecf20Sopenharmony_ci error ? "aborted" : "complete", 4748c2ecf20Sopenharmony_ci usecs / USEC_PER_MSEC, usecs % USEC_PER_MSEC); 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_cistatic int dpm_run_callback(pm_callback_t cb, struct device *dev, 4788c2ecf20Sopenharmony_ci pm_message_t state, const char *info) 4798c2ecf20Sopenharmony_ci{ 4808c2ecf20Sopenharmony_ci ktime_t calltime; 4818c2ecf20Sopenharmony_ci int error; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci if (!cb) 4848c2ecf20Sopenharmony_ci return 0; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci calltime = initcall_debug_start(dev, cb); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci pm_dev_dbg(dev, state, info); 4898c2ecf20Sopenharmony_ci trace_device_pm_callback_start(dev, info, state.event); 4908c2ecf20Sopenharmony_ci error = cb(dev); 4918c2ecf20Sopenharmony_ci trace_device_pm_callback_end(dev, error); 4928c2ecf20Sopenharmony_ci suspend_report_result(cb, error); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci initcall_debug_report(dev, calltime, cb, error); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci return error; 4978c2ecf20Sopenharmony_ci} 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci#ifdef CONFIG_DPM_WATCHDOG 5008c2ecf20Sopenharmony_cistruct dpm_watchdog { 5018c2ecf20Sopenharmony_ci struct device *dev; 5028c2ecf20Sopenharmony_ci struct task_struct *tsk; 5038c2ecf20Sopenharmony_ci struct timer_list timer; 5048c2ecf20Sopenharmony_ci}; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci#define DECLARE_DPM_WATCHDOG_ON_STACK(wd) \ 5078c2ecf20Sopenharmony_ci struct dpm_watchdog wd 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci/** 5108c2ecf20Sopenharmony_ci * dpm_watchdog_handler - Driver suspend / resume watchdog handler. 5118c2ecf20Sopenharmony_ci * @t: The timer that PM watchdog depends on. 5128c2ecf20Sopenharmony_ci * 5138c2ecf20Sopenharmony_ci * Called when a driver has timed out suspending or resuming. 5148c2ecf20Sopenharmony_ci * There's not much we can do here to recover so panic() to 5158c2ecf20Sopenharmony_ci * capture a crash-dump in pstore. 5168c2ecf20Sopenharmony_ci */ 5178c2ecf20Sopenharmony_cistatic void dpm_watchdog_handler(struct timer_list *t) 5188c2ecf20Sopenharmony_ci{ 5198c2ecf20Sopenharmony_ci struct dpm_watchdog *wd = from_timer(wd, t, timer); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci dev_emerg(wd->dev, "**** DPM device timeout ****\n"); 5228c2ecf20Sopenharmony_ci show_stack(wd->tsk, NULL, KERN_EMERG); 5238c2ecf20Sopenharmony_ci panic("%s %s: unrecoverable failure\n", 5248c2ecf20Sopenharmony_ci dev_driver_string(wd->dev), dev_name(wd->dev)); 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci/** 5288c2ecf20Sopenharmony_ci * dpm_watchdog_set - Enable pm watchdog for given device. 5298c2ecf20Sopenharmony_ci * @wd: Watchdog. Must be allocated on the stack. 5308c2ecf20Sopenharmony_ci * @dev: Device to handle. 5318c2ecf20Sopenharmony_ci */ 5328c2ecf20Sopenharmony_cistatic void dpm_watchdog_set(struct dpm_watchdog *wd, struct device *dev) 5338c2ecf20Sopenharmony_ci{ 5348c2ecf20Sopenharmony_ci struct timer_list *timer = &wd->timer; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci wd->dev = dev; 5378c2ecf20Sopenharmony_ci wd->tsk = current; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci timer_setup_on_stack(timer, dpm_watchdog_handler, 0); 5408c2ecf20Sopenharmony_ci /* use same timeout value for both suspend and resume */ 5418c2ecf20Sopenharmony_ci timer->expires = jiffies + HZ * CONFIG_DPM_WATCHDOG_TIMEOUT; 5428c2ecf20Sopenharmony_ci add_timer(timer); 5438c2ecf20Sopenharmony_ci} 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci/** 5468c2ecf20Sopenharmony_ci * dpm_watchdog_clear - Disable suspend/resume watchdog. 5478c2ecf20Sopenharmony_ci * @wd: Watchdog to disable. 5488c2ecf20Sopenharmony_ci */ 5498c2ecf20Sopenharmony_cistatic void dpm_watchdog_clear(struct dpm_watchdog *wd) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci struct timer_list *timer = &wd->timer; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci del_timer_sync(timer); 5548c2ecf20Sopenharmony_ci destroy_timer_on_stack(timer); 5558c2ecf20Sopenharmony_ci} 5568c2ecf20Sopenharmony_ci#else 5578c2ecf20Sopenharmony_ci#define DECLARE_DPM_WATCHDOG_ON_STACK(wd) 5588c2ecf20Sopenharmony_ci#define dpm_watchdog_set(x, y) 5598c2ecf20Sopenharmony_ci#define dpm_watchdog_clear(x) 5608c2ecf20Sopenharmony_ci#endif 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci/*------------------------- Resume routines -------------------------*/ 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci/** 5658c2ecf20Sopenharmony_ci * dev_pm_skip_resume - System-wide device resume optimization check. 5668c2ecf20Sopenharmony_ci * @dev: Target device. 5678c2ecf20Sopenharmony_ci * 5688c2ecf20Sopenharmony_ci * Return: 5698c2ecf20Sopenharmony_ci * - %false if the transition under way is RESTORE. 5708c2ecf20Sopenharmony_ci * - Return value of dev_pm_skip_suspend() if the transition under way is THAW. 5718c2ecf20Sopenharmony_ci * - The logical negation of %power.must_resume otherwise (that is, when the 5728c2ecf20Sopenharmony_ci * transition under way is RESUME). 5738c2ecf20Sopenharmony_ci */ 5748c2ecf20Sopenharmony_cibool dev_pm_skip_resume(struct device *dev) 5758c2ecf20Sopenharmony_ci{ 5768c2ecf20Sopenharmony_ci if (pm_transition.event == PM_EVENT_RESTORE) 5778c2ecf20Sopenharmony_ci return false; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci if (pm_transition.event == PM_EVENT_THAW) 5808c2ecf20Sopenharmony_ci return dev_pm_skip_suspend(dev); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci return !dev->power.must_resume; 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci/** 5868c2ecf20Sopenharmony_ci * __device_resume_noirq - Execute a "noirq resume" callback for given device. 5878c2ecf20Sopenharmony_ci * @dev: Device to handle. 5888c2ecf20Sopenharmony_ci * @state: PM transition of the system being carried out. 5898c2ecf20Sopenharmony_ci * @async: If true, the device is being resumed asynchronously. 5908c2ecf20Sopenharmony_ci * 5918c2ecf20Sopenharmony_ci * The driver of @dev will not receive interrupts while this function is being 5928c2ecf20Sopenharmony_ci * executed. 5938c2ecf20Sopenharmony_ci */ 5948c2ecf20Sopenharmony_cistatic void __device_resume_noirq(struct device *dev, pm_message_t state, bool async) 5958c2ecf20Sopenharmony_ci{ 5968c2ecf20Sopenharmony_ci pm_callback_t callback = NULL; 5978c2ecf20Sopenharmony_ci const char *info = NULL; 5988c2ecf20Sopenharmony_ci bool skip_resume; 5998c2ecf20Sopenharmony_ci int error = 0; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci TRACE_DEVICE(dev); 6028c2ecf20Sopenharmony_ci TRACE_RESUME(0); 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci if (dev->power.syscore || dev->power.direct_complete) 6058c2ecf20Sopenharmony_ci goto Out; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci if (!dev->power.is_noirq_suspended) 6088c2ecf20Sopenharmony_ci goto Out; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci if (!dpm_wait_for_superior(dev, async)) 6118c2ecf20Sopenharmony_ci goto Out; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci skip_resume = dev_pm_skip_resume(dev); 6148c2ecf20Sopenharmony_ci /* 6158c2ecf20Sopenharmony_ci * If the driver callback is skipped below or by the middle layer 6168c2ecf20Sopenharmony_ci * callback and device_resume_early() also skips the driver callback for 6178c2ecf20Sopenharmony_ci * this device later, it needs to appear as "suspended" to PM-runtime, 6188c2ecf20Sopenharmony_ci * so change its status accordingly. 6198c2ecf20Sopenharmony_ci * 6208c2ecf20Sopenharmony_ci * Otherwise, the device is going to be resumed, so set its PM-runtime 6218c2ecf20Sopenharmony_ci * status to "active", but do that only if DPM_FLAG_SMART_SUSPEND is set 6228c2ecf20Sopenharmony_ci * to avoid confusing drivers that don't use it. 6238c2ecf20Sopenharmony_ci */ 6248c2ecf20Sopenharmony_ci if (skip_resume) 6258c2ecf20Sopenharmony_ci pm_runtime_set_suspended(dev); 6268c2ecf20Sopenharmony_ci else if (dev_pm_skip_suspend(dev)) 6278c2ecf20Sopenharmony_ci pm_runtime_set_active(dev); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci if (dev->pm_domain) { 6308c2ecf20Sopenharmony_ci info = "noirq power domain "; 6318c2ecf20Sopenharmony_ci callback = pm_noirq_op(&dev->pm_domain->ops, state); 6328c2ecf20Sopenharmony_ci } else if (dev->type && dev->type->pm) { 6338c2ecf20Sopenharmony_ci info = "noirq type "; 6348c2ecf20Sopenharmony_ci callback = pm_noirq_op(dev->type->pm, state); 6358c2ecf20Sopenharmony_ci } else if (dev->class && dev->class->pm) { 6368c2ecf20Sopenharmony_ci info = "noirq class "; 6378c2ecf20Sopenharmony_ci callback = pm_noirq_op(dev->class->pm, state); 6388c2ecf20Sopenharmony_ci } else if (dev->bus && dev->bus->pm) { 6398c2ecf20Sopenharmony_ci info = "noirq bus "; 6408c2ecf20Sopenharmony_ci callback = pm_noirq_op(dev->bus->pm, state); 6418c2ecf20Sopenharmony_ci } 6428c2ecf20Sopenharmony_ci if (callback) 6438c2ecf20Sopenharmony_ci goto Run; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci if (skip_resume) 6468c2ecf20Sopenharmony_ci goto Skip; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci if (dev->driver && dev->driver->pm) { 6498c2ecf20Sopenharmony_ci info = "noirq driver "; 6508c2ecf20Sopenharmony_ci callback = pm_noirq_op(dev->driver->pm, state); 6518c2ecf20Sopenharmony_ci } 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ciRun: 6548c2ecf20Sopenharmony_ci error = dpm_run_callback(callback, dev, state, info); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ciSkip: 6578c2ecf20Sopenharmony_ci dev->power.is_noirq_suspended = false; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ciOut: 6608c2ecf20Sopenharmony_ci complete_all(&dev->power.completion); 6618c2ecf20Sopenharmony_ci TRACE_RESUME(error); 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci if (error) { 6648c2ecf20Sopenharmony_ci suspend_stats.failed_resume_noirq++; 6658c2ecf20Sopenharmony_ci dpm_save_failed_step(SUSPEND_RESUME_NOIRQ); 6668c2ecf20Sopenharmony_ci dpm_save_failed_dev(dev_name(dev)); 6678c2ecf20Sopenharmony_ci pm_dev_err(dev, state, async ? " async noirq" : " noirq", error); 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ci} 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_cistatic bool is_async(struct device *dev) 6728c2ecf20Sopenharmony_ci{ 6738c2ecf20Sopenharmony_ci return dev->power.async_suspend && pm_async_enabled 6748c2ecf20Sopenharmony_ci && !pm_trace_is_enabled(); 6758c2ecf20Sopenharmony_ci} 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_cistatic bool dpm_async_fn(struct device *dev, async_func_t func) 6788c2ecf20Sopenharmony_ci{ 6798c2ecf20Sopenharmony_ci reinit_completion(&dev->power.completion); 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci if (!is_async(dev)) 6828c2ecf20Sopenharmony_ci return false; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci get_device(dev); 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci if (async_schedule_dev_nocall(func, dev)) 6878c2ecf20Sopenharmony_ci return true; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci put_device(dev); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci return false; 6928c2ecf20Sopenharmony_ci} 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_cistatic void async_resume_noirq(void *data, async_cookie_t cookie) 6958c2ecf20Sopenharmony_ci{ 6968c2ecf20Sopenharmony_ci struct device *dev = data; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci __device_resume_noirq(dev, pm_transition, true); 6998c2ecf20Sopenharmony_ci put_device(dev); 7008c2ecf20Sopenharmony_ci} 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_cistatic void device_resume_noirq(struct device *dev) 7038c2ecf20Sopenharmony_ci{ 7048c2ecf20Sopenharmony_ci if (dpm_async_fn(dev, async_resume_noirq)) 7058c2ecf20Sopenharmony_ci return; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci __device_resume_noirq(dev, pm_transition, false); 7088c2ecf20Sopenharmony_ci} 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_cistatic void dpm_noirq_resume_devices(pm_message_t state) 7118c2ecf20Sopenharmony_ci{ 7128c2ecf20Sopenharmony_ci struct device *dev; 7138c2ecf20Sopenharmony_ci ktime_t starttime = ktime_get(); 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci trace_suspend_resume(TPS("dpm_resume_noirq"), state.event, true); 7168c2ecf20Sopenharmony_ci mutex_lock(&dpm_list_mtx); 7178c2ecf20Sopenharmony_ci pm_transition = state; 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci while (!list_empty(&dpm_noirq_list)) { 7208c2ecf20Sopenharmony_ci dev = to_device(dpm_noirq_list.next); 7218c2ecf20Sopenharmony_ci get_device(dev); 7228c2ecf20Sopenharmony_ci list_move_tail(&dev->power.entry, &dpm_late_early_list); 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci mutex_unlock(&dpm_list_mtx); 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci device_resume_noirq(dev); 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci put_device(dev); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci mutex_lock(&dpm_list_mtx); 7318c2ecf20Sopenharmony_ci } 7328c2ecf20Sopenharmony_ci mutex_unlock(&dpm_list_mtx); 7338c2ecf20Sopenharmony_ci async_synchronize_full(); 7348c2ecf20Sopenharmony_ci dpm_show_time(starttime, state, 0, "noirq"); 7358c2ecf20Sopenharmony_ci trace_suspend_resume(TPS("dpm_resume_noirq"), state.event, false); 7368c2ecf20Sopenharmony_ci} 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci/** 7398c2ecf20Sopenharmony_ci * dpm_resume_noirq - Execute "noirq resume" callbacks for all devices. 7408c2ecf20Sopenharmony_ci * @state: PM transition of the system being carried out. 7418c2ecf20Sopenharmony_ci * 7428c2ecf20Sopenharmony_ci * Invoke the "noirq" resume callbacks for all devices in dpm_noirq_list and 7438c2ecf20Sopenharmony_ci * allow device drivers' interrupt handlers to be called. 7448c2ecf20Sopenharmony_ci */ 7458c2ecf20Sopenharmony_civoid dpm_resume_noirq(pm_message_t state) 7468c2ecf20Sopenharmony_ci{ 7478c2ecf20Sopenharmony_ci dpm_noirq_resume_devices(state); 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci resume_device_irqs(); 7508c2ecf20Sopenharmony_ci device_wakeup_disarm_wake_irqs(); 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci cpuidle_resume(); 7538c2ecf20Sopenharmony_ci} 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci/** 7568c2ecf20Sopenharmony_ci * __device_resume_early - Execute an "early resume" callback for given device. 7578c2ecf20Sopenharmony_ci * @dev: Device to handle. 7588c2ecf20Sopenharmony_ci * @state: PM transition of the system being carried out. 7598c2ecf20Sopenharmony_ci * @async: If true, the device is being resumed asynchronously. 7608c2ecf20Sopenharmony_ci * 7618c2ecf20Sopenharmony_ci * Runtime PM is disabled for @dev while this function is being executed. 7628c2ecf20Sopenharmony_ci */ 7638c2ecf20Sopenharmony_cistatic void __device_resume_early(struct device *dev, pm_message_t state, bool async) 7648c2ecf20Sopenharmony_ci{ 7658c2ecf20Sopenharmony_ci pm_callback_t callback = NULL; 7668c2ecf20Sopenharmony_ci const char *info = NULL; 7678c2ecf20Sopenharmony_ci int error = 0; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci TRACE_DEVICE(dev); 7708c2ecf20Sopenharmony_ci TRACE_RESUME(0); 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci if (dev->power.syscore || dev->power.direct_complete) 7738c2ecf20Sopenharmony_ci goto Out; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci if (!dev->power.is_late_suspended) 7768c2ecf20Sopenharmony_ci goto Out; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci if (!dpm_wait_for_superior(dev, async)) 7798c2ecf20Sopenharmony_ci goto Out; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci if (dev->pm_domain) { 7828c2ecf20Sopenharmony_ci info = "early power domain "; 7838c2ecf20Sopenharmony_ci callback = pm_late_early_op(&dev->pm_domain->ops, state); 7848c2ecf20Sopenharmony_ci } else if (dev->type && dev->type->pm) { 7858c2ecf20Sopenharmony_ci info = "early type "; 7868c2ecf20Sopenharmony_ci callback = pm_late_early_op(dev->type->pm, state); 7878c2ecf20Sopenharmony_ci } else if (dev->class && dev->class->pm) { 7888c2ecf20Sopenharmony_ci info = "early class "; 7898c2ecf20Sopenharmony_ci callback = pm_late_early_op(dev->class->pm, state); 7908c2ecf20Sopenharmony_ci } else if (dev->bus && dev->bus->pm) { 7918c2ecf20Sopenharmony_ci info = "early bus "; 7928c2ecf20Sopenharmony_ci callback = pm_late_early_op(dev->bus->pm, state); 7938c2ecf20Sopenharmony_ci } 7948c2ecf20Sopenharmony_ci if (callback) 7958c2ecf20Sopenharmony_ci goto Run; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci if (dev_pm_skip_resume(dev)) 7988c2ecf20Sopenharmony_ci goto Skip; 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci if (dev->driver && dev->driver->pm) { 8018c2ecf20Sopenharmony_ci info = "early driver "; 8028c2ecf20Sopenharmony_ci callback = pm_late_early_op(dev->driver->pm, state); 8038c2ecf20Sopenharmony_ci } 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ciRun: 8068c2ecf20Sopenharmony_ci error = dpm_run_callback(callback, dev, state, info); 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ciSkip: 8098c2ecf20Sopenharmony_ci dev->power.is_late_suspended = false; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ciOut: 8128c2ecf20Sopenharmony_ci TRACE_RESUME(error); 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci pm_runtime_enable(dev); 8158c2ecf20Sopenharmony_ci complete_all(&dev->power.completion); 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci if (error) { 8188c2ecf20Sopenharmony_ci suspend_stats.failed_resume_early++; 8198c2ecf20Sopenharmony_ci dpm_save_failed_step(SUSPEND_RESUME_EARLY); 8208c2ecf20Sopenharmony_ci dpm_save_failed_dev(dev_name(dev)); 8218c2ecf20Sopenharmony_ci pm_dev_err(dev, state, async ? " async early" : " early", error); 8228c2ecf20Sopenharmony_ci } 8238c2ecf20Sopenharmony_ci} 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_cistatic void async_resume_early(void *data, async_cookie_t cookie) 8268c2ecf20Sopenharmony_ci{ 8278c2ecf20Sopenharmony_ci struct device *dev = data; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci __device_resume_early(dev, pm_transition, true); 8308c2ecf20Sopenharmony_ci put_device(dev); 8318c2ecf20Sopenharmony_ci} 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_cistatic void device_resume_early(struct device *dev) 8348c2ecf20Sopenharmony_ci{ 8358c2ecf20Sopenharmony_ci if (dpm_async_fn(dev, async_resume_early)) 8368c2ecf20Sopenharmony_ci return; 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci __device_resume_early(dev, pm_transition, false); 8398c2ecf20Sopenharmony_ci} 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci/** 8428c2ecf20Sopenharmony_ci * dpm_resume_early - Execute "early resume" callbacks for all devices. 8438c2ecf20Sopenharmony_ci * @state: PM transition of the system being carried out. 8448c2ecf20Sopenharmony_ci */ 8458c2ecf20Sopenharmony_civoid dpm_resume_early(pm_message_t state) 8468c2ecf20Sopenharmony_ci{ 8478c2ecf20Sopenharmony_ci struct device *dev; 8488c2ecf20Sopenharmony_ci ktime_t starttime = ktime_get(); 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci trace_suspend_resume(TPS("dpm_resume_early"), state.event, true); 8518c2ecf20Sopenharmony_ci mutex_lock(&dpm_list_mtx); 8528c2ecf20Sopenharmony_ci pm_transition = state; 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci while (!list_empty(&dpm_late_early_list)) { 8558c2ecf20Sopenharmony_ci dev = to_device(dpm_late_early_list.next); 8568c2ecf20Sopenharmony_ci get_device(dev); 8578c2ecf20Sopenharmony_ci list_move_tail(&dev->power.entry, &dpm_suspended_list); 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci mutex_unlock(&dpm_list_mtx); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci device_resume_early(dev); 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci put_device(dev); 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci mutex_lock(&dpm_list_mtx); 8668c2ecf20Sopenharmony_ci } 8678c2ecf20Sopenharmony_ci mutex_unlock(&dpm_list_mtx); 8688c2ecf20Sopenharmony_ci async_synchronize_full(); 8698c2ecf20Sopenharmony_ci dpm_show_time(starttime, state, 0, "early"); 8708c2ecf20Sopenharmony_ci trace_suspend_resume(TPS("dpm_resume_early"), state.event, false); 8718c2ecf20Sopenharmony_ci} 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci/** 8748c2ecf20Sopenharmony_ci * dpm_resume_start - Execute "noirq" and "early" device callbacks. 8758c2ecf20Sopenharmony_ci * @state: PM transition of the system being carried out. 8768c2ecf20Sopenharmony_ci */ 8778c2ecf20Sopenharmony_civoid dpm_resume_start(pm_message_t state) 8788c2ecf20Sopenharmony_ci{ 8798c2ecf20Sopenharmony_ci dpm_resume_noirq(state); 8808c2ecf20Sopenharmony_ci dpm_resume_early(state); 8818c2ecf20Sopenharmony_ci} 8828c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dpm_resume_start); 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci/** 8858c2ecf20Sopenharmony_ci * __device_resume - Execute "resume" callbacks for given device. 8868c2ecf20Sopenharmony_ci * @dev: Device to handle. 8878c2ecf20Sopenharmony_ci * @state: PM transition of the system being carried out. 8888c2ecf20Sopenharmony_ci * @async: If true, the device is being resumed asynchronously. 8898c2ecf20Sopenharmony_ci */ 8908c2ecf20Sopenharmony_cistatic void __device_resume(struct device *dev, pm_message_t state, bool async) 8918c2ecf20Sopenharmony_ci{ 8928c2ecf20Sopenharmony_ci pm_callback_t callback = NULL; 8938c2ecf20Sopenharmony_ci const char *info = NULL; 8948c2ecf20Sopenharmony_ci int error = 0; 8958c2ecf20Sopenharmony_ci DECLARE_DPM_WATCHDOG_ON_STACK(wd); 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci TRACE_DEVICE(dev); 8988c2ecf20Sopenharmony_ci TRACE_RESUME(0); 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci if (dev->power.syscore) 9018c2ecf20Sopenharmony_ci goto Complete; 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci if (dev->power.direct_complete) { 9048c2ecf20Sopenharmony_ci /* Match the pm_runtime_disable() in __device_suspend(). */ 9058c2ecf20Sopenharmony_ci pm_runtime_enable(dev); 9068c2ecf20Sopenharmony_ci goto Complete; 9078c2ecf20Sopenharmony_ci } 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci if (!dpm_wait_for_superior(dev, async)) 9108c2ecf20Sopenharmony_ci goto Complete; 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci dpm_watchdog_set(&wd, dev); 9138c2ecf20Sopenharmony_ci device_lock(dev); 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci /* 9168c2ecf20Sopenharmony_ci * This is a fib. But we'll allow new children to be added below 9178c2ecf20Sopenharmony_ci * a resumed device, even if the device hasn't been completed yet. 9188c2ecf20Sopenharmony_ci */ 9198c2ecf20Sopenharmony_ci dev->power.is_prepared = false; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci if (!dev->power.is_suspended) 9228c2ecf20Sopenharmony_ci goto Unlock; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci if (dev->pm_domain) { 9258c2ecf20Sopenharmony_ci info = "power domain "; 9268c2ecf20Sopenharmony_ci callback = pm_op(&dev->pm_domain->ops, state); 9278c2ecf20Sopenharmony_ci goto Driver; 9288c2ecf20Sopenharmony_ci } 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci if (dev->type && dev->type->pm) { 9318c2ecf20Sopenharmony_ci info = "type "; 9328c2ecf20Sopenharmony_ci callback = pm_op(dev->type->pm, state); 9338c2ecf20Sopenharmony_ci goto Driver; 9348c2ecf20Sopenharmony_ci } 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci if (dev->class && dev->class->pm) { 9378c2ecf20Sopenharmony_ci info = "class "; 9388c2ecf20Sopenharmony_ci callback = pm_op(dev->class->pm, state); 9398c2ecf20Sopenharmony_ci goto Driver; 9408c2ecf20Sopenharmony_ci } 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci if (dev->bus) { 9438c2ecf20Sopenharmony_ci if (dev->bus->pm) { 9448c2ecf20Sopenharmony_ci info = "bus "; 9458c2ecf20Sopenharmony_ci callback = pm_op(dev->bus->pm, state); 9468c2ecf20Sopenharmony_ci } else if (dev->bus->resume) { 9478c2ecf20Sopenharmony_ci info = "legacy bus "; 9488c2ecf20Sopenharmony_ci callback = dev->bus->resume; 9498c2ecf20Sopenharmony_ci goto End; 9508c2ecf20Sopenharmony_ci } 9518c2ecf20Sopenharmony_ci } 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci Driver: 9548c2ecf20Sopenharmony_ci if (!callback && dev->driver && dev->driver->pm) { 9558c2ecf20Sopenharmony_ci info = "driver "; 9568c2ecf20Sopenharmony_ci callback = pm_op(dev->driver->pm, state); 9578c2ecf20Sopenharmony_ci } 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci End: 9608c2ecf20Sopenharmony_ci error = dpm_run_callback(callback, dev, state, info); 9618c2ecf20Sopenharmony_ci dev->power.is_suspended = false; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci Unlock: 9648c2ecf20Sopenharmony_ci device_unlock(dev); 9658c2ecf20Sopenharmony_ci dpm_watchdog_clear(&wd); 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci Complete: 9688c2ecf20Sopenharmony_ci complete_all(&dev->power.completion); 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci TRACE_RESUME(error); 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci if (error) { 9738c2ecf20Sopenharmony_ci suspend_stats.failed_resume++; 9748c2ecf20Sopenharmony_ci dpm_save_failed_step(SUSPEND_RESUME); 9758c2ecf20Sopenharmony_ci dpm_save_failed_dev(dev_name(dev)); 9768c2ecf20Sopenharmony_ci pm_dev_err(dev, state, async ? " async" : "", error); 9778c2ecf20Sopenharmony_ci } 9788c2ecf20Sopenharmony_ci} 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_cistatic void async_resume(void *data, async_cookie_t cookie) 9818c2ecf20Sopenharmony_ci{ 9828c2ecf20Sopenharmony_ci struct device *dev = data; 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci __device_resume(dev, pm_transition, true); 9858c2ecf20Sopenharmony_ci put_device(dev); 9868c2ecf20Sopenharmony_ci} 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_cistatic void device_resume(struct device *dev) 9898c2ecf20Sopenharmony_ci{ 9908c2ecf20Sopenharmony_ci if (dpm_async_fn(dev, async_resume)) 9918c2ecf20Sopenharmony_ci return; 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci __device_resume(dev, pm_transition, false); 9948c2ecf20Sopenharmony_ci} 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci/** 9978c2ecf20Sopenharmony_ci * dpm_resume - Execute "resume" callbacks for non-sysdev devices. 9988c2ecf20Sopenharmony_ci * @state: PM transition of the system being carried out. 9998c2ecf20Sopenharmony_ci * 10008c2ecf20Sopenharmony_ci * Execute the appropriate "resume" callback for all devices whose status 10018c2ecf20Sopenharmony_ci * indicates that they are suspended. 10028c2ecf20Sopenharmony_ci */ 10038c2ecf20Sopenharmony_civoid dpm_resume(pm_message_t state) 10048c2ecf20Sopenharmony_ci{ 10058c2ecf20Sopenharmony_ci struct device *dev; 10068c2ecf20Sopenharmony_ci ktime_t starttime = ktime_get(); 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci trace_suspend_resume(TPS("dpm_resume"), state.event, true); 10098c2ecf20Sopenharmony_ci might_sleep(); 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci mutex_lock(&dpm_list_mtx); 10128c2ecf20Sopenharmony_ci pm_transition = state; 10138c2ecf20Sopenharmony_ci async_error = 0; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci while (!list_empty(&dpm_suspended_list)) { 10168c2ecf20Sopenharmony_ci dev = to_device(dpm_suspended_list.next); 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci get_device(dev); 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci mutex_unlock(&dpm_list_mtx); 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci device_resume(dev); 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci mutex_lock(&dpm_list_mtx); 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci if (!list_empty(&dev->power.entry)) 10278c2ecf20Sopenharmony_ci list_move_tail(&dev->power.entry, &dpm_prepared_list); 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci mutex_unlock(&dpm_list_mtx); 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci put_device(dev); 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci mutex_lock(&dpm_list_mtx); 10348c2ecf20Sopenharmony_ci } 10358c2ecf20Sopenharmony_ci mutex_unlock(&dpm_list_mtx); 10368c2ecf20Sopenharmony_ci async_synchronize_full(); 10378c2ecf20Sopenharmony_ci dpm_show_time(starttime, state, 0, NULL); 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci cpufreq_resume(); 10408c2ecf20Sopenharmony_ci devfreq_resume(); 10418c2ecf20Sopenharmony_ci trace_suspend_resume(TPS("dpm_resume"), state.event, false); 10428c2ecf20Sopenharmony_ci} 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci/** 10458c2ecf20Sopenharmony_ci * device_complete - Complete a PM transition for given device. 10468c2ecf20Sopenharmony_ci * @dev: Device to handle. 10478c2ecf20Sopenharmony_ci * @state: PM transition of the system being carried out. 10488c2ecf20Sopenharmony_ci */ 10498c2ecf20Sopenharmony_cistatic void device_complete(struct device *dev, pm_message_t state) 10508c2ecf20Sopenharmony_ci{ 10518c2ecf20Sopenharmony_ci void (*callback)(struct device *) = NULL; 10528c2ecf20Sopenharmony_ci const char *info = NULL; 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci if (dev->power.syscore) 10558c2ecf20Sopenharmony_ci goto out; 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci device_lock(dev); 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci if (dev->pm_domain) { 10608c2ecf20Sopenharmony_ci info = "completing power domain "; 10618c2ecf20Sopenharmony_ci callback = dev->pm_domain->ops.complete; 10628c2ecf20Sopenharmony_ci } else if (dev->type && dev->type->pm) { 10638c2ecf20Sopenharmony_ci info = "completing type "; 10648c2ecf20Sopenharmony_ci callback = dev->type->pm->complete; 10658c2ecf20Sopenharmony_ci } else if (dev->class && dev->class->pm) { 10668c2ecf20Sopenharmony_ci info = "completing class "; 10678c2ecf20Sopenharmony_ci callback = dev->class->pm->complete; 10688c2ecf20Sopenharmony_ci } else if (dev->bus && dev->bus->pm) { 10698c2ecf20Sopenharmony_ci info = "completing bus "; 10708c2ecf20Sopenharmony_ci callback = dev->bus->pm->complete; 10718c2ecf20Sopenharmony_ci } 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci if (!callback && dev->driver && dev->driver->pm) { 10748c2ecf20Sopenharmony_ci info = "completing driver "; 10758c2ecf20Sopenharmony_ci callback = dev->driver->pm->complete; 10768c2ecf20Sopenharmony_ci } 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci if (callback) { 10798c2ecf20Sopenharmony_ci pm_dev_dbg(dev, state, info); 10808c2ecf20Sopenharmony_ci callback(dev); 10818c2ecf20Sopenharmony_ci } 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci device_unlock(dev); 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ciout: 10868c2ecf20Sopenharmony_ci pm_runtime_put(dev); 10878c2ecf20Sopenharmony_ci} 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci/** 10908c2ecf20Sopenharmony_ci * dpm_complete - Complete a PM transition for all non-sysdev devices. 10918c2ecf20Sopenharmony_ci * @state: PM transition of the system being carried out. 10928c2ecf20Sopenharmony_ci * 10938c2ecf20Sopenharmony_ci * Execute the ->complete() callbacks for all devices whose PM status is not 10948c2ecf20Sopenharmony_ci * DPM_ON (this allows new devices to be registered). 10958c2ecf20Sopenharmony_ci */ 10968c2ecf20Sopenharmony_civoid dpm_complete(pm_message_t state) 10978c2ecf20Sopenharmony_ci{ 10988c2ecf20Sopenharmony_ci struct list_head list; 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci trace_suspend_resume(TPS("dpm_complete"), state.event, true); 11018c2ecf20Sopenharmony_ci might_sleep(); 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&list); 11048c2ecf20Sopenharmony_ci mutex_lock(&dpm_list_mtx); 11058c2ecf20Sopenharmony_ci while (!list_empty(&dpm_prepared_list)) { 11068c2ecf20Sopenharmony_ci struct device *dev = to_device(dpm_prepared_list.prev); 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci get_device(dev); 11098c2ecf20Sopenharmony_ci dev->power.is_prepared = false; 11108c2ecf20Sopenharmony_ci list_move(&dev->power.entry, &list); 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci mutex_unlock(&dpm_list_mtx); 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci trace_device_pm_callback_start(dev, "", state.event); 11158c2ecf20Sopenharmony_ci device_complete(dev, state); 11168c2ecf20Sopenharmony_ci trace_device_pm_callback_end(dev, 0); 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci put_device(dev); 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci mutex_lock(&dpm_list_mtx); 11218c2ecf20Sopenharmony_ci } 11228c2ecf20Sopenharmony_ci list_splice(&list, &dpm_list); 11238c2ecf20Sopenharmony_ci mutex_unlock(&dpm_list_mtx); 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci /* Allow device probing and trigger re-probing of deferred devices */ 11268c2ecf20Sopenharmony_ci device_unblock_probing(); 11278c2ecf20Sopenharmony_ci trace_suspend_resume(TPS("dpm_complete"), state.event, false); 11288c2ecf20Sopenharmony_ci} 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci/** 11318c2ecf20Sopenharmony_ci * dpm_resume_end - Execute "resume" callbacks and complete system transition. 11328c2ecf20Sopenharmony_ci * @state: PM transition of the system being carried out. 11338c2ecf20Sopenharmony_ci * 11348c2ecf20Sopenharmony_ci * Execute "resume" callbacks for all devices and complete the PM transition of 11358c2ecf20Sopenharmony_ci * the system. 11368c2ecf20Sopenharmony_ci */ 11378c2ecf20Sopenharmony_civoid dpm_resume_end(pm_message_t state) 11388c2ecf20Sopenharmony_ci{ 11398c2ecf20Sopenharmony_ci dpm_resume(state); 11408c2ecf20Sopenharmony_ci dpm_complete(state); 11418c2ecf20Sopenharmony_ci} 11428c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dpm_resume_end); 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci/*------------------------- Suspend routines -------------------------*/ 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci/** 11488c2ecf20Sopenharmony_ci * resume_event - Return a "resume" message for given "suspend" sleep state. 11498c2ecf20Sopenharmony_ci * @sleep_state: PM message representing a sleep state. 11508c2ecf20Sopenharmony_ci * 11518c2ecf20Sopenharmony_ci * Return a PM message representing the resume event corresponding to given 11528c2ecf20Sopenharmony_ci * sleep state. 11538c2ecf20Sopenharmony_ci */ 11548c2ecf20Sopenharmony_cistatic pm_message_t resume_event(pm_message_t sleep_state) 11558c2ecf20Sopenharmony_ci{ 11568c2ecf20Sopenharmony_ci switch (sleep_state.event) { 11578c2ecf20Sopenharmony_ci case PM_EVENT_SUSPEND: 11588c2ecf20Sopenharmony_ci return PMSG_RESUME; 11598c2ecf20Sopenharmony_ci case PM_EVENT_FREEZE: 11608c2ecf20Sopenharmony_ci case PM_EVENT_QUIESCE: 11618c2ecf20Sopenharmony_ci return PMSG_RECOVER; 11628c2ecf20Sopenharmony_ci case PM_EVENT_HIBERNATE: 11638c2ecf20Sopenharmony_ci return PMSG_RESTORE; 11648c2ecf20Sopenharmony_ci } 11658c2ecf20Sopenharmony_ci return PMSG_ON; 11668c2ecf20Sopenharmony_ci} 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_cistatic void dpm_superior_set_must_resume(struct device *dev) 11698c2ecf20Sopenharmony_ci{ 11708c2ecf20Sopenharmony_ci struct device_link *link; 11718c2ecf20Sopenharmony_ci int idx; 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci if (dev->parent) 11748c2ecf20Sopenharmony_ci dev->parent->power.must_resume = true; 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci idx = device_links_read_lock(); 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci list_for_each_entry_rcu_locked(link, &dev->links.suppliers, c_node) 11798c2ecf20Sopenharmony_ci link->supplier->power.must_resume = true; 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci device_links_read_unlock(idx); 11828c2ecf20Sopenharmony_ci} 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci/** 11858c2ecf20Sopenharmony_ci * __device_suspend_noirq - Execute a "noirq suspend" callback for given device. 11868c2ecf20Sopenharmony_ci * @dev: Device to handle. 11878c2ecf20Sopenharmony_ci * @state: PM transition of the system being carried out. 11888c2ecf20Sopenharmony_ci * @async: If true, the device is being suspended asynchronously. 11898c2ecf20Sopenharmony_ci * 11908c2ecf20Sopenharmony_ci * The driver of @dev will not receive interrupts while this function is being 11918c2ecf20Sopenharmony_ci * executed. 11928c2ecf20Sopenharmony_ci */ 11938c2ecf20Sopenharmony_cistatic int __device_suspend_noirq(struct device *dev, pm_message_t state, bool async) 11948c2ecf20Sopenharmony_ci{ 11958c2ecf20Sopenharmony_ci pm_callback_t callback = NULL; 11968c2ecf20Sopenharmony_ci const char *info = NULL; 11978c2ecf20Sopenharmony_ci int error = 0; 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci TRACE_DEVICE(dev); 12008c2ecf20Sopenharmony_ci TRACE_SUSPEND(0); 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci dpm_wait_for_subordinate(dev, async); 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci if (async_error) 12058c2ecf20Sopenharmony_ci goto Complete; 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci if (dev->power.syscore || dev->power.direct_complete) 12088c2ecf20Sopenharmony_ci goto Complete; 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci if (dev->pm_domain) { 12118c2ecf20Sopenharmony_ci info = "noirq power domain "; 12128c2ecf20Sopenharmony_ci callback = pm_noirq_op(&dev->pm_domain->ops, state); 12138c2ecf20Sopenharmony_ci } else if (dev->type && dev->type->pm) { 12148c2ecf20Sopenharmony_ci info = "noirq type "; 12158c2ecf20Sopenharmony_ci callback = pm_noirq_op(dev->type->pm, state); 12168c2ecf20Sopenharmony_ci } else if (dev->class && dev->class->pm) { 12178c2ecf20Sopenharmony_ci info = "noirq class "; 12188c2ecf20Sopenharmony_ci callback = pm_noirq_op(dev->class->pm, state); 12198c2ecf20Sopenharmony_ci } else if (dev->bus && dev->bus->pm) { 12208c2ecf20Sopenharmony_ci info = "noirq bus "; 12218c2ecf20Sopenharmony_ci callback = pm_noirq_op(dev->bus->pm, state); 12228c2ecf20Sopenharmony_ci } 12238c2ecf20Sopenharmony_ci if (callback) 12248c2ecf20Sopenharmony_ci goto Run; 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci if (dev_pm_skip_suspend(dev)) 12278c2ecf20Sopenharmony_ci goto Skip; 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci if (dev->driver && dev->driver->pm) { 12308c2ecf20Sopenharmony_ci info = "noirq driver "; 12318c2ecf20Sopenharmony_ci callback = pm_noirq_op(dev->driver->pm, state); 12328c2ecf20Sopenharmony_ci } 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ciRun: 12358c2ecf20Sopenharmony_ci error = dpm_run_callback(callback, dev, state, info); 12368c2ecf20Sopenharmony_ci if (error) { 12378c2ecf20Sopenharmony_ci async_error = error; 12388c2ecf20Sopenharmony_ci goto Complete; 12398c2ecf20Sopenharmony_ci } 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ciSkip: 12428c2ecf20Sopenharmony_ci dev->power.is_noirq_suspended = true; 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci /* 12458c2ecf20Sopenharmony_ci * Skipping the resume of devices that were in use right before the 12468c2ecf20Sopenharmony_ci * system suspend (as indicated by their PM-runtime usage counters) 12478c2ecf20Sopenharmony_ci * would be suboptimal. Also resume them if doing that is not allowed 12488c2ecf20Sopenharmony_ci * to be skipped. 12498c2ecf20Sopenharmony_ci */ 12508c2ecf20Sopenharmony_ci if (atomic_read(&dev->power.usage_count) > 1 || 12518c2ecf20Sopenharmony_ci !(dev_pm_test_driver_flags(dev, DPM_FLAG_MAY_SKIP_RESUME) && 12528c2ecf20Sopenharmony_ci dev->power.may_skip_resume)) 12538c2ecf20Sopenharmony_ci dev->power.must_resume = true; 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci if (dev->power.must_resume) 12568c2ecf20Sopenharmony_ci dpm_superior_set_must_resume(dev); 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ciComplete: 12598c2ecf20Sopenharmony_ci complete_all(&dev->power.completion); 12608c2ecf20Sopenharmony_ci TRACE_SUSPEND(error); 12618c2ecf20Sopenharmony_ci return error; 12628c2ecf20Sopenharmony_ci} 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_cistatic void async_suspend_noirq(void *data, async_cookie_t cookie) 12658c2ecf20Sopenharmony_ci{ 12668c2ecf20Sopenharmony_ci struct device *dev = data; 12678c2ecf20Sopenharmony_ci int error; 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci error = __device_suspend_noirq(dev, pm_transition, true); 12708c2ecf20Sopenharmony_ci if (error) { 12718c2ecf20Sopenharmony_ci dpm_save_failed_dev(dev_name(dev)); 12728c2ecf20Sopenharmony_ci pm_dev_err(dev, pm_transition, " async", error); 12738c2ecf20Sopenharmony_ci } 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci put_device(dev); 12768c2ecf20Sopenharmony_ci} 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_cistatic int device_suspend_noirq(struct device *dev) 12798c2ecf20Sopenharmony_ci{ 12808c2ecf20Sopenharmony_ci if (dpm_async_fn(dev, async_suspend_noirq)) 12818c2ecf20Sopenharmony_ci return 0; 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci return __device_suspend_noirq(dev, pm_transition, false); 12848c2ecf20Sopenharmony_ci} 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_cistatic int dpm_noirq_suspend_devices(pm_message_t state) 12878c2ecf20Sopenharmony_ci{ 12888c2ecf20Sopenharmony_ci ktime_t starttime = ktime_get(); 12898c2ecf20Sopenharmony_ci int error = 0; 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci trace_suspend_resume(TPS("dpm_suspend_noirq"), state.event, true); 12928c2ecf20Sopenharmony_ci mutex_lock(&dpm_list_mtx); 12938c2ecf20Sopenharmony_ci pm_transition = state; 12948c2ecf20Sopenharmony_ci async_error = 0; 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci while (!list_empty(&dpm_late_early_list)) { 12978c2ecf20Sopenharmony_ci struct device *dev = to_device(dpm_late_early_list.prev); 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci get_device(dev); 13008c2ecf20Sopenharmony_ci mutex_unlock(&dpm_list_mtx); 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci error = device_suspend_noirq(dev); 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci mutex_lock(&dpm_list_mtx); 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ci if (error) { 13078c2ecf20Sopenharmony_ci pm_dev_err(dev, state, " noirq", error); 13088c2ecf20Sopenharmony_ci dpm_save_failed_dev(dev_name(dev)); 13098c2ecf20Sopenharmony_ci } else if (!list_empty(&dev->power.entry)) { 13108c2ecf20Sopenharmony_ci list_move(&dev->power.entry, &dpm_noirq_list); 13118c2ecf20Sopenharmony_ci } 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci mutex_unlock(&dpm_list_mtx); 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci put_device(dev); 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci mutex_lock(&dpm_list_mtx); 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci if (error || async_error) 13208c2ecf20Sopenharmony_ci break; 13218c2ecf20Sopenharmony_ci } 13228c2ecf20Sopenharmony_ci mutex_unlock(&dpm_list_mtx); 13238c2ecf20Sopenharmony_ci async_synchronize_full(); 13248c2ecf20Sopenharmony_ci if (!error) 13258c2ecf20Sopenharmony_ci error = async_error; 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci if (error) { 13288c2ecf20Sopenharmony_ci suspend_stats.failed_suspend_noirq++; 13298c2ecf20Sopenharmony_ci dpm_save_failed_step(SUSPEND_SUSPEND_NOIRQ); 13308c2ecf20Sopenharmony_ci } 13318c2ecf20Sopenharmony_ci dpm_show_time(starttime, state, error, "noirq"); 13328c2ecf20Sopenharmony_ci trace_suspend_resume(TPS("dpm_suspend_noirq"), state.event, false); 13338c2ecf20Sopenharmony_ci return error; 13348c2ecf20Sopenharmony_ci} 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci/** 13378c2ecf20Sopenharmony_ci * dpm_suspend_noirq - Execute "noirq suspend" callbacks for all devices. 13388c2ecf20Sopenharmony_ci * @state: PM transition of the system being carried out. 13398c2ecf20Sopenharmony_ci * 13408c2ecf20Sopenharmony_ci * Prevent device drivers' interrupt handlers from being called and invoke 13418c2ecf20Sopenharmony_ci * "noirq" suspend callbacks for all non-sysdev devices. 13428c2ecf20Sopenharmony_ci */ 13438c2ecf20Sopenharmony_ciint dpm_suspend_noirq(pm_message_t state) 13448c2ecf20Sopenharmony_ci{ 13458c2ecf20Sopenharmony_ci int ret; 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci cpuidle_pause(); 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci device_wakeup_arm_wake_irqs(); 13508c2ecf20Sopenharmony_ci suspend_device_irqs(); 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci ret = dpm_noirq_suspend_devices(state); 13538c2ecf20Sopenharmony_ci if (ret) 13548c2ecf20Sopenharmony_ci dpm_resume_noirq(resume_event(state)); 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci return ret; 13578c2ecf20Sopenharmony_ci} 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_cistatic void dpm_propagate_wakeup_to_parent(struct device *dev) 13608c2ecf20Sopenharmony_ci{ 13618c2ecf20Sopenharmony_ci struct device *parent = dev->parent; 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci if (!parent) 13648c2ecf20Sopenharmony_ci return; 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci spin_lock_irq(&parent->power.lock); 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci if (dev->power.wakeup_path && !parent->power.ignore_children) 13698c2ecf20Sopenharmony_ci parent->power.wakeup_path = true; 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci spin_unlock_irq(&parent->power.lock); 13728c2ecf20Sopenharmony_ci} 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci/** 13758c2ecf20Sopenharmony_ci * __device_suspend_late - Execute a "late suspend" callback for given device. 13768c2ecf20Sopenharmony_ci * @dev: Device to handle. 13778c2ecf20Sopenharmony_ci * @state: PM transition of the system being carried out. 13788c2ecf20Sopenharmony_ci * @async: If true, the device is being suspended asynchronously. 13798c2ecf20Sopenharmony_ci * 13808c2ecf20Sopenharmony_ci * Runtime PM is disabled for @dev while this function is being executed. 13818c2ecf20Sopenharmony_ci */ 13828c2ecf20Sopenharmony_cistatic int __device_suspend_late(struct device *dev, pm_message_t state, bool async) 13838c2ecf20Sopenharmony_ci{ 13848c2ecf20Sopenharmony_ci pm_callback_t callback = NULL; 13858c2ecf20Sopenharmony_ci const char *info = NULL; 13868c2ecf20Sopenharmony_ci int error = 0; 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci TRACE_DEVICE(dev); 13898c2ecf20Sopenharmony_ci TRACE_SUSPEND(0); 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ci __pm_runtime_disable(dev, false); 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci dpm_wait_for_subordinate(dev, async); 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci if (async_error) 13968c2ecf20Sopenharmony_ci goto Complete; 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci if (pm_wakeup_pending()) { 13998c2ecf20Sopenharmony_ci async_error = -EBUSY; 14008c2ecf20Sopenharmony_ci goto Complete; 14018c2ecf20Sopenharmony_ci } 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci if (dev->power.syscore || dev->power.direct_complete) 14048c2ecf20Sopenharmony_ci goto Complete; 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci if (dev->pm_domain) { 14078c2ecf20Sopenharmony_ci info = "late power domain "; 14088c2ecf20Sopenharmony_ci callback = pm_late_early_op(&dev->pm_domain->ops, state); 14098c2ecf20Sopenharmony_ci } else if (dev->type && dev->type->pm) { 14108c2ecf20Sopenharmony_ci info = "late type "; 14118c2ecf20Sopenharmony_ci callback = pm_late_early_op(dev->type->pm, state); 14128c2ecf20Sopenharmony_ci } else if (dev->class && dev->class->pm) { 14138c2ecf20Sopenharmony_ci info = "late class "; 14148c2ecf20Sopenharmony_ci callback = pm_late_early_op(dev->class->pm, state); 14158c2ecf20Sopenharmony_ci } else if (dev->bus && dev->bus->pm) { 14168c2ecf20Sopenharmony_ci info = "late bus "; 14178c2ecf20Sopenharmony_ci callback = pm_late_early_op(dev->bus->pm, state); 14188c2ecf20Sopenharmony_ci } 14198c2ecf20Sopenharmony_ci if (callback) 14208c2ecf20Sopenharmony_ci goto Run; 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci if (dev_pm_skip_suspend(dev)) 14238c2ecf20Sopenharmony_ci goto Skip; 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci if (dev->driver && dev->driver->pm) { 14268c2ecf20Sopenharmony_ci info = "late driver "; 14278c2ecf20Sopenharmony_ci callback = pm_late_early_op(dev->driver->pm, state); 14288c2ecf20Sopenharmony_ci } 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_ciRun: 14318c2ecf20Sopenharmony_ci error = dpm_run_callback(callback, dev, state, info); 14328c2ecf20Sopenharmony_ci if (error) { 14338c2ecf20Sopenharmony_ci async_error = error; 14348c2ecf20Sopenharmony_ci goto Complete; 14358c2ecf20Sopenharmony_ci } 14368c2ecf20Sopenharmony_ci dpm_propagate_wakeup_to_parent(dev); 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ciSkip: 14398c2ecf20Sopenharmony_ci dev->power.is_late_suspended = true; 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ciComplete: 14428c2ecf20Sopenharmony_ci TRACE_SUSPEND(error); 14438c2ecf20Sopenharmony_ci complete_all(&dev->power.completion); 14448c2ecf20Sopenharmony_ci return error; 14458c2ecf20Sopenharmony_ci} 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_cistatic void async_suspend_late(void *data, async_cookie_t cookie) 14488c2ecf20Sopenharmony_ci{ 14498c2ecf20Sopenharmony_ci struct device *dev = data; 14508c2ecf20Sopenharmony_ci int error; 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci error = __device_suspend_late(dev, pm_transition, true); 14538c2ecf20Sopenharmony_ci if (error) { 14548c2ecf20Sopenharmony_ci dpm_save_failed_dev(dev_name(dev)); 14558c2ecf20Sopenharmony_ci pm_dev_err(dev, pm_transition, " async", error); 14568c2ecf20Sopenharmony_ci } 14578c2ecf20Sopenharmony_ci put_device(dev); 14588c2ecf20Sopenharmony_ci} 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_cistatic int device_suspend_late(struct device *dev) 14618c2ecf20Sopenharmony_ci{ 14628c2ecf20Sopenharmony_ci if (dpm_async_fn(dev, async_suspend_late)) 14638c2ecf20Sopenharmony_ci return 0; 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ci return __device_suspend_late(dev, pm_transition, false); 14668c2ecf20Sopenharmony_ci} 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci/** 14698c2ecf20Sopenharmony_ci * dpm_suspend_late - Execute "late suspend" callbacks for all devices. 14708c2ecf20Sopenharmony_ci * @state: PM transition of the system being carried out. 14718c2ecf20Sopenharmony_ci */ 14728c2ecf20Sopenharmony_ciint dpm_suspend_late(pm_message_t state) 14738c2ecf20Sopenharmony_ci{ 14748c2ecf20Sopenharmony_ci ktime_t starttime = ktime_get(); 14758c2ecf20Sopenharmony_ci int error = 0; 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ci trace_suspend_resume(TPS("dpm_suspend_late"), state.event, true); 14788c2ecf20Sopenharmony_ci mutex_lock(&dpm_list_mtx); 14798c2ecf20Sopenharmony_ci pm_transition = state; 14808c2ecf20Sopenharmony_ci async_error = 0; 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci while (!list_empty(&dpm_suspended_list)) { 14838c2ecf20Sopenharmony_ci struct device *dev = to_device(dpm_suspended_list.prev); 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci get_device(dev); 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci mutex_unlock(&dpm_list_mtx); 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci error = device_suspend_late(dev); 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci mutex_lock(&dpm_list_mtx); 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_ci if (!list_empty(&dev->power.entry)) 14948c2ecf20Sopenharmony_ci list_move(&dev->power.entry, &dpm_late_early_list); 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci if (error) { 14978c2ecf20Sopenharmony_ci pm_dev_err(dev, state, " late", error); 14988c2ecf20Sopenharmony_ci dpm_save_failed_dev(dev_name(dev)); 14998c2ecf20Sopenharmony_ci } 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci mutex_unlock(&dpm_list_mtx); 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci put_device(dev); 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci mutex_lock(&dpm_list_mtx); 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci if (error || async_error) 15088c2ecf20Sopenharmony_ci break; 15098c2ecf20Sopenharmony_ci } 15108c2ecf20Sopenharmony_ci mutex_unlock(&dpm_list_mtx); 15118c2ecf20Sopenharmony_ci async_synchronize_full(); 15128c2ecf20Sopenharmony_ci if (!error) 15138c2ecf20Sopenharmony_ci error = async_error; 15148c2ecf20Sopenharmony_ci if (error) { 15158c2ecf20Sopenharmony_ci suspend_stats.failed_suspend_late++; 15168c2ecf20Sopenharmony_ci dpm_save_failed_step(SUSPEND_SUSPEND_LATE); 15178c2ecf20Sopenharmony_ci dpm_resume_early(resume_event(state)); 15188c2ecf20Sopenharmony_ci } 15198c2ecf20Sopenharmony_ci dpm_show_time(starttime, state, error, "late"); 15208c2ecf20Sopenharmony_ci trace_suspend_resume(TPS("dpm_suspend_late"), state.event, false); 15218c2ecf20Sopenharmony_ci return error; 15228c2ecf20Sopenharmony_ci} 15238c2ecf20Sopenharmony_ci 15248c2ecf20Sopenharmony_ci/** 15258c2ecf20Sopenharmony_ci * dpm_suspend_end - Execute "late" and "noirq" device suspend callbacks. 15268c2ecf20Sopenharmony_ci * @state: PM transition of the system being carried out. 15278c2ecf20Sopenharmony_ci */ 15288c2ecf20Sopenharmony_ciint dpm_suspend_end(pm_message_t state) 15298c2ecf20Sopenharmony_ci{ 15308c2ecf20Sopenharmony_ci ktime_t starttime = ktime_get(); 15318c2ecf20Sopenharmony_ci int error; 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ci error = dpm_suspend_late(state); 15348c2ecf20Sopenharmony_ci if (error) 15358c2ecf20Sopenharmony_ci goto out; 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_ci error = dpm_suspend_noirq(state); 15388c2ecf20Sopenharmony_ci if (error) 15398c2ecf20Sopenharmony_ci dpm_resume_early(resume_event(state)); 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_ciout: 15428c2ecf20Sopenharmony_ci dpm_show_time(starttime, state, error, "end"); 15438c2ecf20Sopenharmony_ci return error; 15448c2ecf20Sopenharmony_ci} 15458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dpm_suspend_end); 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_ci/** 15488c2ecf20Sopenharmony_ci * legacy_suspend - Execute a legacy (bus or class) suspend callback for device. 15498c2ecf20Sopenharmony_ci * @dev: Device to suspend. 15508c2ecf20Sopenharmony_ci * @state: PM transition of the system being carried out. 15518c2ecf20Sopenharmony_ci * @cb: Suspend callback to execute. 15528c2ecf20Sopenharmony_ci * @info: string description of caller. 15538c2ecf20Sopenharmony_ci */ 15548c2ecf20Sopenharmony_cistatic int legacy_suspend(struct device *dev, pm_message_t state, 15558c2ecf20Sopenharmony_ci int (*cb)(struct device *dev, pm_message_t state), 15568c2ecf20Sopenharmony_ci const char *info) 15578c2ecf20Sopenharmony_ci{ 15588c2ecf20Sopenharmony_ci int error; 15598c2ecf20Sopenharmony_ci ktime_t calltime; 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ci calltime = initcall_debug_start(dev, cb); 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_ci trace_device_pm_callback_start(dev, info, state.event); 15648c2ecf20Sopenharmony_ci error = cb(dev, state); 15658c2ecf20Sopenharmony_ci trace_device_pm_callback_end(dev, error); 15668c2ecf20Sopenharmony_ci suspend_report_result(cb, error); 15678c2ecf20Sopenharmony_ci 15688c2ecf20Sopenharmony_ci initcall_debug_report(dev, calltime, cb, error); 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ci return error; 15718c2ecf20Sopenharmony_ci} 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_cistatic void dpm_clear_superiors_direct_complete(struct device *dev) 15748c2ecf20Sopenharmony_ci{ 15758c2ecf20Sopenharmony_ci struct device_link *link; 15768c2ecf20Sopenharmony_ci int idx; 15778c2ecf20Sopenharmony_ci 15788c2ecf20Sopenharmony_ci if (dev->parent) { 15798c2ecf20Sopenharmony_ci spin_lock_irq(&dev->parent->power.lock); 15808c2ecf20Sopenharmony_ci dev->parent->power.direct_complete = false; 15818c2ecf20Sopenharmony_ci spin_unlock_irq(&dev->parent->power.lock); 15828c2ecf20Sopenharmony_ci } 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci idx = device_links_read_lock(); 15858c2ecf20Sopenharmony_ci 15868c2ecf20Sopenharmony_ci list_for_each_entry_rcu_locked(link, &dev->links.suppliers, c_node) { 15878c2ecf20Sopenharmony_ci spin_lock_irq(&link->supplier->power.lock); 15888c2ecf20Sopenharmony_ci link->supplier->power.direct_complete = false; 15898c2ecf20Sopenharmony_ci spin_unlock_irq(&link->supplier->power.lock); 15908c2ecf20Sopenharmony_ci } 15918c2ecf20Sopenharmony_ci 15928c2ecf20Sopenharmony_ci device_links_read_unlock(idx); 15938c2ecf20Sopenharmony_ci} 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci/** 15968c2ecf20Sopenharmony_ci * __device_suspend - Execute "suspend" callbacks for given device. 15978c2ecf20Sopenharmony_ci * @dev: Device to handle. 15988c2ecf20Sopenharmony_ci * @state: PM transition of the system being carried out. 15998c2ecf20Sopenharmony_ci * @async: If true, the device is being suspended asynchronously. 16008c2ecf20Sopenharmony_ci */ 16018c2ecf20Sopenharmony_cistatic int __device_suspend(struct device *dev, pm_message_t state, bool async) 16028c2ecf20Sopenharmony_ci{ 16038c2ecf20Sopenharmony_ci pm_callback_t callback = NULL; 16048c2ecf20Sopenharmony_ci const char *info = NULL; 16058c2ecf20Sopenharmony_ci int error = 0; 16068c2ecf20Sopenharmony_ci DECLARE_DPM_WATCHDOG_ON_STACK(wd); 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_ci TRACE_DEVICE(dev); 16098c2ecf20Sopenharmony_ci TRACE_SUSPEND(0); 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci dpm_wait_for_subordinate(dev, async); 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_ci if (async_error) { 16148c2ecf20Sopenharmony_ci dev->power.direct_complete = false; 16158c2ecf20Sopenharmony_ci goto Complete; 16168c2ecf20Sopenharmony_ci } 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_ci /* 16198c2ecf20Sopenharmony_ci * Wait for possible runtime PM transitions of the device in progress 16208c2ecf20Sopenharmony_ci * to complete and if there's a runtime resume request pending for it, 16218c2ecf20Sopenharmony_ci * resume it before proceeding with invoking the system-wide suspend 16228c2ecf20Sopenharmony_ci * callbacks for it. 16238c2ecf20Sopenharmony_ci * 16248c2ecf20Sopenharmony_ci * If the system-wide suspend callbacks below change the configuration 16258c2ecf20Sopenharmony_ci * of the device, they must disable runtime PM for it or otherwise 16268c2ecf20Sopenharmony_ci * ensure that its runtime-resume callbacks will not be confused by that 16278c2ecf20Sopenharmony_ci * change in case they are invoked going forward. 16288c2ecf20Sopenharmony_ci */ 16298c2ecf20Sopenharmony_ci pm_runtime_barrier(dev); 16308c2ecf20Sopenharmony_ci 16318c2ecf20Sopenharmony_ci if (pm_wakeup_pending()) { 16328c2ecf20Sopenharmony_ci dev->power.direct_complete = false; 16338c2ecf20Sopenharmony_ci async_error = -EBUSY; 16348c2ecf20Sopenharmony_ci goto Complete; 16358c2ecf20Sopenharmony_ci } 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci if (dev->power.syscore) 16388c2ecf20Sopenharmony_ci goto Complete; 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_ci /* Avoid direct_complete to let wakeup_path propagate. */ 16418c2ecf20Sopenharmony_ci if (device_may_wakeup(dev) || dev->power.wakeup_path) 16428c2ecf20Sopenharmony_ci dev->power.direct_complete = false; 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci if (dev->power.direct_complete) { 16458c2ecf20Sopenharmony_ci if (pm_runtime_status_suspended(dev)) { 16468c2ecf20Sopenharmony_ci pm_runtime_disable(dev); 16478c2ecf20Sopenharmony_ci if (pm_runtime_status_suspended(dev)) { 16488c2ecf20Sopenharmony_ci pm_dev_dbg(dev, state, "direct-complete "); 16498c2ecf20Sopenharmony_ci goto Complete; 16508c2ecf20Sopenharmony_ci } 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_ci pm_runtime_enable(dev); 16538c2ecf20Sopenharmony_ci } 16548c2ecf20Sopenharmony_ci dev->power.direct_complete = false; 16558c2ecf20Sopenharmony_ci } 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ci dev->power.may_skip_resume = true; 16588c2ecf20Sopenharmony_ci dev->power.must_resume = !dev_pm_test_driver_flags(dev, DPM_FLAG_MAY_SKIP_RESUME); 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_ci dpm_watchdog_set(&wd, dev); 16618c2ecf20Sopenharmony_ci device_lock(dev); 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_ci if (dev->pm_domain) { 16648c2ecf20Sopenharmony_ci info = "power domain "; 16658c2ecf20Sopenharmony_ci callback = pm_op(&dev->pm_domain->ops, state); 16668c2ecf20Sopenharmony_ci goto Run; 16678c2ecf20Sopenharmony_ci } 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_ci if (dev->type && dev->type->pm) { 16708c2ecf20Sopenharmony_ci info = "type "; 16718c2ecf20Sopenharmony_ci callback = pm_op(dev->type->pm, state); 16728c2ecf20Sopenharmony_ci goto Run; 16738c2ecf20Sopenharmony_ci } 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci if (dev->class && dev->class->pm) { 16768c2ecf20Sopenharmony_ci info = "class "; 16778c2ecf20Sopenharmony_ci callback = pm_op(dev->class->pm, state); 16788c2ecf20Sopenharmony_ci goto Run; 16798c2ecf20Sopenharmony_ci } 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_ci if (dev->bus) { 16828c2ecf20Sopenharmony_ci if (dev->bus->pm) { 16838c2ecf20Sopenharmony_ci info = "bus "; 16848c2ecf20Sopenharmony_ci callback = pm_op(dev->bus->pm, state); 16858c2ecf20Sopenharmony_ci } else if (dev->bus->suspend) { 16868c2ecf20Sopenharmony_ci pm_dev_dbg(dev, state, "legacy bus "); 16878c2ecf20Sopenharmony_ci error = legacy_suspend(dev, state, dev->bus->suspend, 16888c2ecf20Sopenharmony_ci "legacy bus "); 16898c2ecf20Sopenharmony_ci goto End; 16908c2ecf20Sopenharmony_ci } 16918c2ecf20Sopenharmony_ci } 16928c2ecf20Sopenharmony_ci 16938c2ecf20Sopenharmony_ci Run: 16948c2ecf20Sopenharmony_ci if (!callback && dev->driver && dev->driver->pm) { 16958c2ecf20Sopenharmony_ci info = "driver "; 16968c2ecf20Sopenharmony_ci callback = pm_op(dev->driver->pm, state); 16978c2ecf20Sopenharmony_ci } 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_ci error = dpm_run_callback(callback, dev, state, info); 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_ci End: 17028c2ecf20Sopenharmony_ci if (!error) { 17038c2ecf20Sopenharmony_ci dev->power.is_suspended = true; 17048c2ecf20Sopenharmony_ci if (device_may_wakeup(dev)) 17058c2ecf20Sopenharmony_ci dev->power.wakeup_path = true; 17068c2ecf20Sopenharmony_ci 17078c2ecf20Sopenharmony_ci dpm_propagate_wakeup_to_parent(dev); 17088c2ecf20Sopenharmony_ci dpm_clear_superiors_direct_complete(dev); 17098c2ecf20Sopenharmony_ci } 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci device_unlock(dev); 17128c2ecf20Sopenharmony_ci dpm_watchdog_clear(&wd); 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_ci Complete: 17158c2ecf20Sopenharmony_ci if (error) 17168c2ecf20Sopenharmony_ci async_error = error; 17178c2ecf20Sopenharmony_ci 17188c2ecf20Sopenharmony_ci complete_all(&dev->power.completion); 17198c2ecf20Sopenharmony_ci TRACE_SUSPEND(error); 17208c2ecf20Sopenharmony_ci return error; 17218c2ecf20Sopenharmony_ci} 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_cistatic void async_suspend(void *data, async_cookie_t cookie) 17248c2ecf20Sopenharmony_ci{ 17258c2ecf20Sopenharmony_ci struct device *dev = data; 17268c2ecf20Sopenharmony_ci int error; 17278c2ecf20Sopenharmony_ci 17288c2ecf20Sopenharmony_ci error = __device_suspend(dev, pm_transition, true); 17298c2ecf20Sopenharmony_ci if (error) { 17308c2ecf20Sopenharmony_ci dpm_save_failed_dev(dev_name(dev)); 17318c2ecf20Sopenharmony_ci pm_dev_err(dev, pm_transition, " async", error); 17328c2ecf20Sopenharmony_ci } 17338c2ecf20Sopenharmony_ci 17348c2ecf20Sopenharmony_ci put_device(dev); 17358c2ecf20Sopenharmony_ci} 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_cistatic int device_suspend(struct device *dev) 17388c2ecf20Sopenharmony_ci{ 17398c2ecf20Sopenharmony_ci if (dpm_async_fn(dev, async_suspend)) 17408c2ecf20Sopenharmony_ci return 0; 17418c2ecf20Sopenharmony_ci 17428c2ecf20Sopenharmony_ci return __device_suspend(dev, pm_transition, false); 17438c2ecf20Sopenharmony_ci} 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_ci/** 17468c2ecf20Sopenharmony_ci * dpm_suspend - Execute "suspend" callbacks for all non-sysdev devices. 17478c2ecf20Sopenharmony_ci * @state: PM transition of the system being carried out. 17488c2ecf20Sopenharmony_ci */ 17498c2ecf20Sopenharmony_ciint dpm_suspend(pm_message_t state) 17508c2ecf20Sopenharmony_ci{ 17518c2ecf20Sopenharmony_ci ktime_t starttime = ktime_get(); 17528c2ecf20Sopenharmony_ci int error = 0; 17538c2ecf20Sopenharmony_ci 17548c2ecf20Sopenharmony_ci trace_suspend_resume(TPS("dpm_suspend"), state.event, true); 17558c2ecf20Sopenharmony_ci might_sleep(); 17568c2ecf20Sopenharmony_ci 17578c2ecf20Sopenharmony_ci devfreq_suspend(); 17588c2ecf20Sopenharmony_ci cpufreq_suspend(); 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_ci mutex_lock(&dpm_list_mtx); 17618c2ecf20Sopenharmony_ci pm_transition = state; 17628c2ecf20Sopenharmony_ci async_error = 0; 17638c2ecf20Sopenharmony_ci while (!list_empty(&dpm_prepared_list)) { 17648c2ecf20Sopenharmony_ci struct device *dev = to_device(dpm_prepared_list.prev); 17658c2ecf20Sopenharmony_ci 17668c2ecf20Sopenharmony_ci get_device(dev); 17678c2ecf20Sopenharmony_ci 17688c2ecf20Sopenharmony_ci mutex_unlock(&dpm_list_mtx); 17698c2ecf20Sopenharmony_ci 17708c2ecf20Sopenharmony_ci error = device_suspend(dev); 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_ci mutex_lock(&dpm_list_mtx); 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_ci if (error) { 17758c2ecf20Sopenharmony_ci pm_dev_err(dev, state, "", error); 17768c2ecf20Sopenharmony_ci dpm_save_failed_dev(dev_name(dev)); 17778c2ecf20Sopenharmony_ci } else if (!list_empty(&dev->power.entry)) { 17788c2ecf20Sopenharmony_ci list_move(&dev->power.entry, &dpm_suspended_list); 17798c2ecf20Sopenharmony_ci } 17808c2ecf20Sopenharmony_ci 17818c2ecf20Sopenharmony_ci mutex_unlock(&dpm_list_mtx); 17828c2ecf20Sopenharmony_ci 17838c2ecf20Sopenharmony_ci put_device(dev); 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_ci mutex_lock(&dpm_list_mtx); 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_ci if (error || async_error) 17888c2ecf20Sopenharmony_ci break; 17898c2ecf20Sopenharmony_ci } 17908c2ecf20Sopenharmony_ci mutex_unlock(&dpm_list_mtx); 17918c2ecf20Sopenharmony_ci async_synchronize_full(); 17928c2ecf20Sopenharmony_ci if (!error) 17938c2ecf20Sopenharmony_ci error = async_error; 17948c2ecf20Sopenharmony_ci if (error) { 17958c2ecf20Sopenharmony_ci suspend_stats.failed_suspend++; 17968c2ecf20Sopenharmony_ci dpm_save_failed_step(SUSPEND_SUSPEND); 17978c2ecf20Sopenharmony_ci } 17988c2ecf20Sopenharmony_ci dpm_show_time(starttime, state, error, NULL); 17998c2ecf20Sopenharmony_ci trace_suspend_resume(TPS("dpm_suspend"), state.event, false); 18008c2ecf20Sopenharmony_ci return error; 18018c2ecf20Sopenharmony_ci} 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_ci/** 18048c2ecf20Sopenharmony_ci * device_prepare - Prepare a device for system power transition. 18058c2ecf20Sopenharmony_ci * @dev: Device to handle. 18068c2ecf20Sopenharmony_ci * @state: PM transition of the system being carried out. 18078c2ecf20Sopenharmony_ci * 18088c2ecf20Sopenharmony_ci * Execute the ->prepare() callback(s) for given device. No new children of the 18098c2ecf20Sopenharmony_ci * device may be registered after this function has returned. 18108c2ecf20Sopenharmony_ci */ 18118c2ecf20Sopenharmony_cistatic int device_prepare(struct device *dev, pm_message_t state) 18128c2ecf20Sopenharmony_ci{ 18138c2ecf20Sopenharmony_ci int (*callback)(struct device *) = NULL; 18148c2ecf20Sopenharmony_ci int ret = 0; 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_ci /* 18178c2ecf20Sopenharmony_ci * If a device's parent goes into runtime suspend at the wrong time, 18188c2ecf20Sopenharmony_ci * it won't be possible to resume the device. To prevent this we 18198c2ecf20Sopenharmony_ci * block runtime suspend here, during the prepare phase, and allow 18208c2ecf20Sopenharmony_ci * it again during the complete phase. 18218c2ecf20Sopenharmony_ci */ 18228c2ecf20Sopenharmony_ci pm_runtime_get_noresume(dev); 18238c2ecf20Sopenharmony_ci 18248c2ecf20Sopenharmony_ci if (dev->power.syscore) 18258c2ecf20Sopenharmony_ci return 0; 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci device_lock(dev); 18288c2ecf20Sopenharmony_ci 18298c2ecf20Sopenharmony_ci dev->power.wakeup_path = false; 18308c2ecf20Sopenharmony_ci 18318c2ecf20Sopenharmony_ci if (dev->power.no_pm_callbacks) 18328c2ecf20Sopenharmony_ci goto unlock; 18338c2ecf20Sopenharmony_ci 18348c2ecf20Sopenharmony_ci if (dev->pm_domain) 18358c2ecf20Sopenharmony_ci callback = dev->pm_domain->ops.prepare; 18368c2ecf20Sopenharmony_ci else if (dev->type && dev->type->pm) 18378c2ecf20Sopenharmony_ci callback = dev->type->pm->prepare; 18388c2ecf20Sopenharmony_ci else if (dev->class && dev->class->pm) 18398c2ecf20Sopenharmony_ci callback = dev->class->pm->prepare; 18408c2ecf20Sopenharmony_ci else if (dev->bus && dev->bus->pm) 18418c2ecf20Sopenharmony_ci callback = dev->bus->pm->prepare; 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_ci if (!callback && dev->driver && dev->driver->pm) 18448c2ecf20Sopenharmony_ci callback = dev->driver->pm->prepare; 18458c2ecf20Sopenharmony_ci 18468c2ecf20Sopenharmony_ci if (callback) 18478c2ecf20Sopenharmony_ci ret = callback(dev); 18488c2ecf20Sopenharmony_ci 18498c2ecf20Sopenharmony_ciunlock: 18508c2ecf20Sopenharmony_ci device_unlock(dev); 18518c2ecf20Sopenharmony_ci 18528c2ecf20Sopenharmony_ci if (ret < 0) { 18538c2ecf20Sopenharmony_ci suspend_report_result(callback, ret); 18548c2ecf20Sopenharmony_ci pm_runtime_put(dev); 18558c2ecf20Sopenharmony_ci return ret; 18568c2ecf20Sopenharmony_ci } 18578c2ecf20Sopenharmony_ci /* 18588c2ecf20Sopenharmony_ci * A positive return value from ->prepare() means "this device appears 18598c2ecf20Sopenharmony_ci * to be runtime-suspended and its state is fine, so if it really is 18608c2ecf20Sopenharmony_ci * runtime-suspended, you can leave it in that state provided that you 18618c2ecf20Sopenharmony_ci * will do the same thing with all of its descendants". This only 18628c2ecf20Sopenharmony_ci * applies to suspend transitions, however. 18638c2ecf20Sopenharmony_ci */ 18648c2ecf20Sopenharmony_ci spin_lock_irq(&dev->power.lock); 18658c2ecf20Sopenharmony_ci dev->power.direct_complete = state.event == PM_EVENT_SUSPEND && 18668c2ecf20Sopenharmony_ci (ret > 0 || dev->power.no_pm_callbacks) && 18678c2ecf20Sopenharmony_ci !dev_pm_test_driver_flags(dev, DPM_FLAG_NO_DIRECT_COMPLETE); 18688c2ecf20Sopenharmony_ci spin_unlock_irq(&dev->power.lock); 18698c2ecf20Sopenharmony_ci return 0; 18708c2ecf20Sopenharmony_ci} 18718c2ecf20Sopenharmony_ci 18728c2ecf20Sopenharmony_ci/** 18738c2ecf20Sopenharmony_ci * dpm_prepare - Prepare all non-sysdev devices for a system PM transition. 18748c2ecf20Sopenharmony_ci * @state: PM transition of the system being carried out. 18758c2ecf20Sopenharmony_ci * 18768c2ecf20Sopenharmony_ci * Execute the ->prepare() callback(s) for all devices. 18778c2ecf20Sopenharmony_ci */ 18788c2ecf20Sopenharmony_ciint dpm_prepare(pm_message_t state) 18798c2ecf20Sopenharmony_ci{ 18808c2ecf20Sopenharmony_ci int error = 0; 18818c2ecf20Sopenharmony_ci 18828c2ecf20Sopenharmony_ci trace_suspend_resume(TPS("dpm_prepare"), state.event, true); 18838c2ecf20Sopenharmony_ci might_sleep(); 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_ci /* 18868c2ecf20Sopenharmony_ci * Give a chance for the known devices to complete their probes, before 18878c2ecf20Sopenharmony_ci * disable probing of devices. This sync point is important at least 18888c2ecf20Sopenharmony_ci * at boot time + hibernation restore. 18898c2ecf20Sopenharmony_ci */ 18908c2ecf20Sopenharmony_ci wait_for_device_probe(); 18918c2ecf20Sopenharmony_ci /* 18928c2ecf20Sopenharmony_ci * It is unsafe if probing of devices will happen during suspend or 18938c2ecf20Sopenharmony_ci * hibernation and system behavior will be unpredictable in this case. 18948c2ecf20Sopenharmony_ci * So, let's prohibit device's probing here and defer their probes 18958c2ecf20Sopenharmony_ci * instead. The normal behavior will be restored in dpm_complete(). 18968c2ecf20Sopenharmony_ci */ 18978c2ecf20Sopenharmony_ci device_block_probing(); 18988c2ecf20Sopenharmony_ci 18998c2ecf20Sopenharmony_ci mutex_lock(&dpm_list_mtx); 19008c2ecf20Sopenharmony_ci while (!list_empty(&dpm_list) && !error) { 19018c2ecf20Sopenharmony_ci struct device *dev = to_device(dpm_list.next); 19028c2ecf20Sopenharmony_ci 19038c2ecf20Sopenharmony_ci get_device(dev); 19048c2ecf20Sopenharmony_ci 19058c2ecf20Sopenharmony_ci mutex_unlock(&dpm_list_mtx); 19068c2ecf20Sopenharmony_ci 19078c2ecf20Sopenharmony_ci trace_device_pm_callback_start(dev, "", state.event); 19088c2ecf20Sopenharmony_ci error = device_prepare(dev, state); 19098c2ecf20Sopenharmony_ci trace_device_pm_callback_end(dev, error); 19108c2ecf20Sopenharmony_ci 19118c2ecf20Sopenharmony_ci mutex_lock(&dpm_list_mtx); 19128c2ecf20Sopenharmony_ci 19138c2ecf20Sopenharmony_ci if (!error) { 19148c2ecf20Sopenharmony_ci dev->power.is_prepared = true; 19158c2ecf20Sopenharmony_ci if (!list_empty(&dev->power.entry)) 19168c2ecf20Sopenharmony_ci list_move_tail(&dev->power.entry, &dpm_prepared_list); 19178c2ecf20Sopenharmony_ci } else if (error == -EAGAIN) { 19188c2ecf20Sopenharmony_ci error = 0; 19198c2ecf20Sopenharmony_ci } else { 19208c2ecf20Sopenharmony_ci dev_info(dev, "not prepared for power transition: code %d\n", 19218c2ecf20Sopenharmony_ci error); 19228c2ecf20Sopenharmony_ci } 19238c2ecf20Sopenharmony_ci 19248c2ecf20Sopenharmony_ci mutex_unlock(&dpm_list_mtx); 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_ci put_device(dev); 19278c2ecf20Sopenharmony_ci 19288c2ecf20Sopenharmony_ci mutex_lock(&dpm_list_mtx); 19298c2ecf20Sopenharmony_ci } 19308c2ecf20Sopenharmony_ci mutex_unlock(&dpm_list_mtx); 19318c2ecf20Sopenharmony_ci trace_suspend_resume(TPS("dpm_prepare"), state.event, false); 19328c2ecf20Sopenharmony_ci return error; 19338c2ecf20Sopenharmony_ci} 19348c2ecf20Sopenharmony_ci 19358c2ecf20Sopenharmony_ci/** 19368c2ecf20Sopenharmony_ci * dpm_suspend_start - Prepare devices for PM transition and suspend them. 19378c2ecf20Sopenharmony_ci * @state: PM transition of the system being carried out. 19388c2ecf20Sopenharmony_ci * 19398c2ecf20Sopenharmony_ci * Prepare all non-sysdev devices for system PM transition and execute "suspend" 19408c2ecf20Sopenharmony_ci * callbacks for them. 19418c2ecf20Sopenharmony_ci */ 19428c2ecf20Sopenharmony_ciint dpm_suspend_start(pm_message_t state) 19438c2ecf20Sopenharmony_ci{ 19448c2ecf20Sopenharmony_ci ktime_t starttime = ktime_get(); 19458c2ecf20Sopenharmony_ci int error; 19468c2ecf20Sopenharmony_ci 19478c2ecf20Sopenharmony_ci error = dpm_prepare(state); 19488c2ecf20Sopenharmony_ci if (error) { 19498c2ecf20Sopenharmony_ci suspend_stats.failed_prepare++; 19508c2ecf20Sopenharmony_ci dpm_save_failed_step(SUSPEND_PREPARE); 19518c2ecf20Sopenharmony_ci } else 19528c2ecf20Sopenharmony_ci error = dpm_suspend(state); 19538c2ecf20Sopenharmony_ci dpm_show_time(starttime, state, error, "start"); 19548c2ecf20Sopenharmony_ci return error; 19558c2ecf20Sopenharmony_ci} 19568c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dpm_suspend_start); 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_civoid __suspend_report_result(const char *function, void *fn, int ret) 19598c2ecf20Sopenharmony_ci{ 19608c2ecf20Sopenharmony_ci if (ret) 19618c2ecf20Sopenharmony_ci pr_err("%s(): %pS returns %d\n", function, fn, ret); 19628c2ecf20Sopenharmony_ci} 19638c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__suspend_report_result); 19648c2ecf20Sopenharmony_ci 19658c2ecf20Sopenharmony_ci/** 19668c2ecf20Sopenharmony_ci * device_pm_wait_for_dev - Wait for suspend/resume of a device to complete. 19678c2ecf20Sopenharmony_ci * @subordinate: Device that needs to wait for @dev. 19688c2ecf20Sopenharmony_ci * @dev: Device to wait for. 19698c2ecf20Sopenharmony_ci */ 19708c2ecf20Sopenharmony_ciint device_pm_wait_for_dev(struct device *subordinate, struct device *dev) 19718c2ecf20Sopenharmony_ci{ 19728c2ecf20Sopenharmony_ci dpm_wait(dev, subordinate->power.async_suspend); 19738c2ecf20Sopenharmony_ci return async_error; 19748c2ecf20Sopenharmony_ci} 19758c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(device_pm_wait_for_dev); 19768c2ecf20Sopenharmony_ci 19778c2ecf20Sopenharmony_ci/** 19788c2ecf20Sopenharmony_ci * dpm_for_each_dev - device iterator. 19798c2ecf20Sopenharmony_ci * @data: data for the callback. 19808c2ecf20Sopenharmony_ci * @fn: function to be called for each device. 19818c2ecf20Sopenharmony_ci * 19828c2ecf20Sopenharmony_ci * Iterate over devices in dpm_list, and call @fn for each device, 19838c2ecf20Sopenharmony_ci * passing it @data. 19848c2ecf20Sopenharmony_ci */ 19858c2ecf20Sopenharmony_civoid dpm_for_each_dev(void *data, void (*fn)(struct device *, void *)) 19868c2ecf20Sopenharmony_ci{ 19878c2ecf20Sopenharmony_ci struct device *dev; 19888c2ecf20Sopenharmony_ci 19898c2ecf20Sopenharmony_ci if (!fn) 19908c2ecf20Sopenharmony_ci return; 19918c2ecf20Sopenharmony_ci 19928c2ecf20Sopenharmony_ci device_pm_lock(); 19938c2ecf20Sopenharmony_ci list_for_each_entry(dev, &dpm_list, power.entry) 19948c2ecf20Sopenharmony_ci fn(dev, data); 19958c2ecf20Sopenharmony_ci device_pm_unlock(); 19968c2ecf20Sopenharmony_ci} 19978c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(dpm_for_each_dev); 19988c2ecf20Sopenharmony_ci 19998c2ecf20Sopenharmony_cistatic bool pm_ops_is_empty(const struct dev_pm_ops *ops) 20008c2ecf20Sopenharmony_ci{ 20018c2ecf20Sopenharmony_ci if (!ops) 20028c2ecf20Sopenharmony_ci return true; 20038c2ecf20Sopenharmony_ci 20048c2ecf20Sopenharmony_ci return !ops->prepare && 20058c2ecf20Sopenharmony_ci !ops->suspend && 20068c2ecf20Sopenharmony_ci !ops->suspend_late && 20078c2ecf20Sopenharmony_ci !ops->suspend_noirq && 20088c2ecf20Sopenharmony_ci !ops->resume_noirq && 20098c2ecf20Sopenharmony_ci !ops->resume_early && 20108c2ecf20Sopenharmony_ci !ops->resume && 20118c2ecf20Sopenharmony_ci !ops->complete; 20128c2ecf20Sopenharmony_ci} 20138c2ecf20Sopenharmony_ci 20148c2ecf20Sopenharmony_civoid device_pm_check_callbacks(struct device *dev) 20158c2ecf20Sopenharmony_ci{ 20168c2ecf20Sopenharmony_ci unsigned long flags; 20178c2ecf20Sopenharmony_ci 20188c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->power.lock, flags); 20198c2ecf20Sopenharmony_ci dev->power.no_pm_callbacks = 20208c2ecf20Sopenharmony_ci (!dev->bus || (pm_ops_is_empty(dev->bus->pm) && 20218c2ecf20Sopenharmony_ci !dev->bus->suspend && !dev->bus->resume)) && 20228c2ecf20Sopenharmony_ci (!dev->class || pm_ops_is_empty(dev->class->pm)) && 20238c2ecf20Sopenharmony_ci (!dev->type || pm_ops_is_empty(dev->type->pm)) && 20248c2ecf20Sopenharmony_ci (!dev->pm_domain || pm_ops_is_empty(&dev->pm_domain->ops)) && 20258c2ecf20Sopenharmony_ci (!dev->driver || (pm_ops_is_empty(dev->driver->pm) && 20268c2ecf20Sopenharmony_ci !dev->driver->suspend && !dev->driver->resume)); 20278c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->power.lock, flags); 20288c2ecf20Sopenharmony_ci} 20298c2ecf20Sopenharmony_ci 20308c2ecf20Sopenharmony_cibool dev_pm_skip_suspend(struct device *dev) 20318c2ecf20Sopenharmony_ci{ 20328c2ecf20Sopenharmony_ci return dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) && 20338c2ecf20Sopenharmony_ci pm_runtime_status_suspended(dev); 20348c2ecf20Sopenharmony_ci} 2035