18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * drivers/base/power/wakeup.c - System wakeup events framework 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2010 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "PM: " fmt 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/device.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/sched/signal.h> 128c2ecf20Sopenharmony_ci#include <linux/capability.h> 138c2ecf20Sopenharmony_ci#include <linux/export.h> 148c2ecf20Sopenharmony_ci#include <linux/suspend.h> 158c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 168c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 178c2ecf20Sopenharmony_ci#include <linux/pm_wakeirq.h> 188c2ecf20Sopenharmony_ci#include <trace/events/power.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include "power.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#ifndef CONFIG_SUSPEND 238c2ecf20Sopenharmony_cisuspend_state_t pm_suspend_target_state; 248c2ecf20Sopenharmony_ci#define pm_suspend_target_state (PM_SUSPEND_ON) 258c2ecf20Sopenharmony_ci#endif 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define list_for_each_entry_rcu_locked(pos, head, member) \ 288c2ecf20Sopenharmony_ci list_for_each_entry_rcu(pos, head, member, \ 298c2ecf20Sopenharmony_ci srcu_read_lock_held(&wakeup_srcu)) 308c2ecf20Sopenharmony_ci/* 318c2ecf20Sopenharmony_ci * If set, the suspend/hibernate code will abort transitions to a sleep state 328c2ecf20Sopenharmony_ci * if wakeup events are registered during or immediately before the transition. 338c2ecf20Sopenharmony_ci */ 348c2ecf20Sopenharmony_cibool events_check_enabled __read_mostly; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* First wakeup IRQ seen by the kernel in the last cycle. */ 378c2ecf20Sopenharmony_cistatic unsigned int wakeup_irq[2] __read_mostly; 388c2ecf20Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(wakeup_irq_lock); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* If greater than 0 and the system is suspending, terminate the suspend. */ 418c2ecf20Sopenharmony_cistatic atomic_t pm_abort_suspend __read_mostly; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* 448c2ecf20Sopenharmony_ci * Combined counters of registered wakeup events and wakeup events in progress. 458c2ecf20Sopenharmony_ci * They need to be modified together atomically, so it's better to use one 468c2ecf20Sopenharmony_ci * atomic variable to hold them both. 478c2ecf20Sopenharmony_ci */ 488c2ecf20Sopenharmony_cistatic atomic_t combined_event_count = ATOMIC_INIT(0); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#define IN_PROGRESS_BITS (sizeof(int) * 4) 518c2ecf20Sopenharmony_ci#define MAX_IN_PROGRESS ((1 << IN_PROGRESS_BITS) - 1) 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic void split_counters(unsigned int *cnt, unsigned int *inpr) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci unsigned int comb = atomic_read(&combined_event_count); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci *cnt = (comb >> IN_PROGRESS_BITS); 588c2ecf20Sopenharmony_ci *inpr = comb & MAX_IN_PROGRESS; 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/* A preserved old value of the events counter. */ 628c2ecf20Sopenharmony_cistatic unsigned int saved_count; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(events_lock); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic void pm_wakeup_timer_fn(struct timer_list *t); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic LIST_HEAD(wakeup_sources); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(wakeup_count_wait_queue); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ciDEFINE_STATIC_SRCU(wakeup_srcu); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic struct wakeup_source deleted_ws = { 758c2ecf20Sopenharmony_ci .name = "deleted", 768c2ecf20Sopenharmony_ci .lock = __SPIN_LOCK_UNLOCKED(deleted_ws.lock), 778c2ecf20Sopenharmony_ci}; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic DEFINE_IDA(wakeup_ida); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/** 828c2ecf20Sopenharmony_ci * wakeup_source_create - Create a struct wakeup_source object. 838c2ecf20Sopenharmony_ci * @name: Name of the new wakeup source. 848c2ecf20Sopenharmony_ci */ 858c2ecf20Sopenharmony_cistruct wakeup_source *wakeup_source_create(const char *name) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci struct wakeup_source *ws; 888c2ecf20Sopenharmony_ci const char *ws_name; 898c2ecf20Sopenharmony_ci int id; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci ws = kzalloc(sizeof(*ws), GFP_KERNEL); 928c2ecf20Sopenharmony_ci if (!ws) 938c2ecf20Sopenharmony_ci goto err_ws; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci ws_name = kstrdup_const(name, GFP_KERNEL); 968c2ecf20Sopenharmony_ci if (!ws_name) 978c2ecf20Sopenharmony_ci goto err_name; 988c2ecf20Sopenharmony_ci ws->name = ws_name; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci id = ida_alloc(&wakeup_ida, GFP_KERNEL); 1018c2ecf20Sopenharmony_ci if (id < 0) 1028c2ecf20Sopenharmony_ci goto err_id; 1038c2ecf20Sopenharmony_ci ws->id = id; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci return ws; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cierr_id: 1088c2ecf20Sopenharmony_ci kfree_const(ws->name); 1098c2ecf20Sopenharmony_cierr_name: 1108c2ecf20Sopenharmony_ci kfree(ws); 1118c2ecf20Sopenharmony_cierr_ws: 1128c2ecf20Sopenharmony_ci return NULL; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wakeup_source_create); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci/* 1178c2ecf20Sopenharmony_ci * Record wakeup_source statistics being deleted into a dummy wakeup_source. 1188c2ecf20Sopenharmony_ci */ 1198c2ecf20Sopenharmony_cistatic void wakeup_source_record(struct wakeup_source *ws) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci unsigned long flags; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci spin_lock_irqsave(&deleted_ws.lock, flags); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (ws->event_count) { 1268c2ecf20Sopenharmony_ci deleted_ws.total_time = 1278c2ecf20Sopenharmony_ci ktime_add(deleted_ws.total_time, ws->total_time); 1288c2ecf20Sopenharmony_ci deleted_ws.prevent_sleep_time = 1298c2ecf20Sopenharmony_ci ktime_add(deleted_ws.prevent_sleep_time, 1308c2ecf20Sopenharmony_ci ws->prevent_sleep_time); 1318c2ecf20Sopenharmony_ci deleted_ws.max_time = 1328c2ecf20Sopenharmony_ci ktime_compare(deleted_ws.max_time, ws->max_time) > 0 ? 1338c2ecf20Sopenharmony_ci deleted_ws.max_time : ws->max_time; 1348c2ecf20Sopenharmony_ci deleted_ws.event_count += ws->event_count; 1358c2ecf20Sopenharmony_ci deleted_ws.active_count += ws->active_count; 1368c2ecf20Sopenharmony_ci deleted_ws.relax_count += ws->relax_count; 1378c2ecf20Sopenharmony_ci deleted_ws.expire_count += ws->expire_count; 1388c2ecf20Sopenharmony_ci deleted_ws.wakeup_count += ws->wakeup_count; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&deleted_ws.lock, flags); 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic void wakeup_source_free(struct wakeup_source *ws) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci ida_free(&wakeup_ida, ws->id); 1478c2ecf20Sopenharmony_ci kfree_const(ws->name); 1488c2ecf20Sopenharmony_ci kfree(ws); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci/** 1528c2ecf20Sopenharmony_ci * wakeup_source_destroy - Destroy a struct wakeup_source object. 1538c2ecf20Sopenharmony_ci * @ws: Wakeup source to destroy. 1548c2ecf20Sopenharmony_ci * 1558c2ecf20Sopenharmony_ci * Use only for wakeup source objects created with wakeup_source_create(). 1568c2ecf20Sopenharmony_ci */ 1578c2ecf20Sopenharmony_civoid wakeup_source_destroy(struct wakeup_source *ws) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci if (!ws) 1608c2ecf20Sopenharmony_ci return; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci __pm_relax(ws); 1638c2ecf20Sopenharmony_ci wakeup_source_record(ws); 1648c2ecf20Sopenharmony_ci wakeup_source_free(ws); 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wakeup_source_destroy); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci/** 1698c2ecf20Sopenharmony_ci * wakeup_source_add - Add given object to the list of wakeup sources. 1708c2ecf20Sopenharmony_ci * @ws: Wakeup source object to add to the list. 1718c2ecf20Sopenharmony_ci */ 1728c2ecf20Sopenharmony_civoid wakeup_source_add(struct wakeup_source *ws) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci unsigned long flags; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci if (WARN_ON(!ws)) 1778c2ecf20Sopenharmony_ci return; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci spin_lock_init(&ws->lock); 1808c2ecf20Sopenharmony_ci timer_setup(&ws->timer, pm_wakeup_timer_fn, 0); 1818c2ecf20Sopenharmony_ci ws->active = false; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&events_lock, flags); 1848c2ecf20Sopenharmony_ci list_add_rcu(&ws->entry, &wakeup_sources); 1858c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&events_lock, flags); 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wakeup_source_add); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci/** 1908c2ecf20Sopenharmony_ci * wakeup_source_remove - Remove given object from the wakeup sources list. 1918c2ecf20Sopenharmony_ci * @ws: Wakeup source object to remove from the list. 1928c2ecf20Sopenharmony_ci */ 1938c2ecf20Sopenharmony_civoid wakeup_source_remove(struct wakeup_source *ws) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci unsigned long flags; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci if (WARN_ON(!ws)) 1988c2ecf20Sopenharmony_ci return; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&events_lock, flags); 2018c2ecf20Sopenharmony_ci list_del_rcu(&ws->entry); 2028c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&events_lock, flags); 2038c2ecf20Sopenharmony_ci synchronize_srcu(&wakeup_srcu); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci del_timer_sync(&ws->timer); 2068c2ecf20Sopenharmony_ci /* 2078c2ecf20Sopenharmony_ci * Clear timer.function to make wakeup_source_not_registered() treat 2088c2ecf20Sopenharmony_ci * this wakeup source as not registered. 2098c2ecf20Sopenharmony_ci */ 2108c2ecf20Sopenharmony_ci ws->timer.function = NULL; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wakeup_source_remove); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci/** 2158c2ecf20Sopenharmony_ci * wakeup_source_register - Create wakeup source and add it to the list. 2168c2ecf20Sopenharmony_ci * @dev: Device this wakeup source is associated with (or NULL if virtual). 2178c2ecf20Sopenharmony_ci * @name: Name of the wakeup source to register. 2188c2ecf20Sopenharmony_ci */ 2198c2ecf20Sopenharmony_cistruct wakeup_source *wakeup_source_register(struct device *dev, 2208c2ecf20Sopenharmony_ci const char *name) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci struct wakeup_source *ws; 2238c2ecf20Sopenharmony_ci int ret; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci ws = wakeup_source_create(name); 2268c2ecf20Sopenharmony_ci if (ws) { 2278c2ecf20Sopenharmony_ci if (!dev || device_is_registered(dev)) { 2288c2ecf20Sopenharmony_ci ret = wakeup_source_sysfs_add(dev, ws); 2298c2ecf20Sopenharmony_ci if (ret) { 2308c2ecf20Sopenharmony_ci wakeup_source_free(ws); 2318c2ecf20Sopenharmony_ci return NULL; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci wakeup_source_add(ws); 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci return ws; 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wakeup_source_register); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci/** 2418c2ecf20Sopenharmony_ci * wakeup_source_unregister - Remove wakeup source from the list and remove it. 2428c2ecf20Sopenharmony_ci * @ws: Wakeup source object to unregister. 2438c2ecf20Sopenharmony_ci */ 2448c2ecf20Sopenharmony_civoid wakeup_source_unregister(struct wakeup_source *ws) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci if (ws) { 2478c2ecf20Sopenharmony_ci wakeup_source_remove(ws); 2488c2ecf20Sopenharmony_ci if (ws->dev) 2498c2ecf20Sopenharmony_ci wakeup_source_sysfs_remove(ws); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci wakeup_source_destroy(ws); 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wakeup_source_unregister); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci/** 2578c2ecf20Sopenharmony_ci * wakeup_sources_read_lock - Lock wakeup source list for read. 2588c2ecf20Sopenharmony_ci * 2598c2ecf20Sopenharmony_ci * Returns an index of srcu lock for struct wakeup_srcu. 2608c2ecf20Sopenharmony_ci * This index must be passed to the matching wakeup_sources_read_unlock(). 2618c2ecf20Sopenharmony_ci */ 2628c2ecf20Sopenharmony_ciint wakeup_sources_read_lock(void) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci return srcu_read_lock(&wakeup_srcu); 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wakeup_sources_read_lock); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci/** 2698c2ecf20Sopenharmony_ci * wakeup_sources_read_unlock - Unlock wakeup source list. 2708c2ecf20Sopenharmony_ci * @idx: return value from corresponding wakeup_sources_read_lock() 2718c2ecf20Sopenharmony_ci */ 2728c2ecf20Sopenharmony_civoid wakeup_sources_read_unlock(int idx) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci srcu_read_unlock(&wakeup_srcu, idx); 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wakeup_sources_read_unlock); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci/** 2798c2ecf20Sopenharmony_ci * wakeup_sources_walk_start - Begin a walk on wakeup source list 2808c2ecf20Sopenharmony_ci * 2818c2ecf20Sopenharmony_ci * Returns first object of the list of wakeup sources. 2828c2ecf20Sopenharmony_ci * 2838c2ecf20Sopenharmony_ci * Note that to be safe, wakeup sources list needs to be locked by calling 2848c2ecf20Sopenharmony_ci * wakeup_source_read_lock() for this. 2858c2ecf20Sopenharmony_ci */ 2868c2ecf20Sopenharmony_cistruct wakeup_source *wakeup_sources_walk_start(void) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci struct list_head *ws_head = &wakeup_sources; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci return list_entry_rcu(ws_head->next, struct wakeup_source, entry); 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wakeup_sources_walk_start); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci/** 2958c2ecf20Sopenharmony_ci * wakeup_sources_walk_next - Get next wakeup source from the list 2968c2ecf20Sopenharmony_ci * @ws: Previous wakeup source object 2978c2ecf20Sopenharmony_ci * 2988c2ecf20Sopenharmony_ci * Note that to be safe, wakeup sources list needs to be locked by calling 2998c2ecf20Sopenharmony_ci * wakeup_source_read_lock() for this. 3008c2ecf20Sopenharmony_ci */ 3018c2ecf20Sopenharmony_cistruct wakeup_source *wakeup_sources_walk_next(struct wakeup_source *ws) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci struct list_head *ws_head = &wakeup_sources; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci return list_next_or_null_rcu(ws_head, &ws->entry, 3068c2ecf20Sopenharmony_ci struct wakeup_source, entry); 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wakeup_sources_walk_next); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci/** 3118c2ecf20Sopenharmony_ci * device_wakeup_attach - Attach a wakeup source object to a device object. 3128c2ecf20Sopenharmony_ci * @dev: Device to handle. 3138c2ecf20Sopenharmony_ci * @ws: Wakeup source object to attach to @dev. 3148c2ecf20Sopenharmony_ci * 3158c2ecf20Sopenharmony_ci * This causes @dev to be treated as a wakeup device. 3168c2ecf20Sopenharmony_ci */ 3178c2ecf20Sopenharmony_cistatic int device_wakeup_attach(struct device *dev, struct wakeup_source *ws) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci spin_lock_irq(&dev->power.lock); 3208c2ecf20Sopenharmony_ci if (dev->power.wakeup) { 3218c2ecf20Sopenharmony_ci spin_unlock_irq(&dev->power.lock); 3228c2ecf20Sopenharmony_ci return -EEXIST; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci dev->power.wakeup = ws; 3258c2ecf20Sopenharmony_ci if (dev->power.wakeirq) 3268c2ecf20Sopenharmony_ci device_wakeup_attach_irq(dev, dev->power.wakeirq); 3278c2ecf20Sopenharmony_ci spin_unlock_irq(&dev->power.lock); 3288c2ecf20Sopenharmony_ci return 0; 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci/** 3328c2ecf20Sopenharmony_ci * device_wakeup_enable - Enable given device to be a wakeup source. 3338c2ecf20Sopenharmony_ci * @dev: Device to handle. 3348c2ecf20Sopenharmony_ci * 3358c2ecf20Sopenharmony_ci * Create a wakeup source object, register it and attach it to @dev. 3368c2ecf20Sopenharmony_ci */ 3378c2ecf20Sopenharmony_ciint device_wakeup_enable(struct device *dev) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci struct wakeup_source *ws; 3408c2ecf20Sopenharmony_ci int ret; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci if (!dev || !dev->power.can_wakeup) 3438c2ecf20Sopenharmony_ci return -EINVAL; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if (pm_suspend_target_state != PM_SUSPEND_ON) 3468c2ecf20Sopenharmony_ci dev_dbg(dev, "Suspicious %s() during system transition!\n", __func__); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci ws = wakeup_source_register(dev, dev_name(dev)); 3498c2ecf20Sopenharmony_ci if (!ws) 3508c2ecf20Sopenharmony_ci return -ENOMEM; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci ret = device_wakeup_attach(dev, ws); 3538c2ecf20Sopenharmony_ci if (ret) 3548c2ecf20Sopenharmony_ci wakeup_source_unregister(ws); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci return ret; 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(device_wakeup_enable); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci/** 3618c2ecf20Sopenharmony_ci * device_wakeup_attach_irq - Attach a wakeirq to a wakeup source 3628c2ecf20Sopenharmony_ci * @dev: Device to handle 3638c2ecf20Sopenharmony_ci * @wakeirq: Device specific wakeirq entry 3648c2ecf20Sopenharmony_ci * 3658c2ecf20Sopenharmony_ci * Attach a device wakeirq to the wakeup source so the device 3668c2ecf20Sopenharmony_ci * wake IRQ can be configured automatically for suspend and 3678c2ecf20Sopenharmony_ci * resume. 3688c2ecf20Sopenharmony_ci * 3698c2ecf20Sopenharmony_ci * Call under the device's power.lock lock. 3708c2ecf20Sopenharmony_ci */ 3718c2ecf20Sopenharmony_civoid device_wakeup_attach_irq(struct device *dev, 3728c2ecf20Sopenharmony_ci struct wake_irq *wakeirq) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci struct wakeup_source *ws; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci ws = dev->power.wakeup; 3778c2ecf20Sopenharmony_ci if (!ws) 3788c2ecf20Sopenharmony_ci return; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci if (ws->wakeirq) 3818c2ecf20Sopenharmony_ci dev_err(dev, "Leftover wakeup IRQ found, overriding\n"); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci ws->wakeirq = wakeirq; 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci/** 3878c2ecf20Sopenharmony_ci * device_wakeup_detach_irq - Detach a wakeirq from a wakeup source 3888c2ecf20Sopenharmony_ci * @dev: Device to handle 3898c2ecf20Sopenharmony_ci * 3908c2ecf20Sopenharmony_ci * Removes a device wakeirq from the wakeup source. 3918c2ecf20Sopenharmony_ci * 3928c2ecf20Sopenharmony_ci * Call under the device's power.lock lock. 3938c2ecf20Sopenharmony_ci */ 3948c2ecf20Sopenharmony_civoid device_wakeup_detach_irq(struct device *dev) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci struct wakeup_source *ws; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci ws = dev->power.wakeup; 3998c2ecf20Sopenharmony_ci if (ws) 4008c2ecf20Sopenharmony_ci ws->wakeirq = NULL; 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci/** 4048c2ecf20Sopenharmony_ci * device_wakeup_arm_wake_irqs(void) 4058c2ecf20Sopenharmony_ci * 4068c2ecf20Sopenharmony_ci * Itereates over the list of device wakeirqs to arm them. 4078c2ecf20Sopenharmony_ci */ 4088c2ecf20Sopenharmony_civoid device_wakeup_arm_wake_irqs(void) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci struct wakeup_source *ws; 4118c2ecf20Sopenharmony_ci int srcuidx; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci srcuidx = srcu_read_lock(&wakeup_srcu); 4148c2ecf20Sopenharmony_ci list_for_each_entry_rcu_locked(ws, &wakeup_sources, entry) 4158c2ecf20Sopenharmony_ci dev_pm_arm_wake_irq(ws->wakeirq); 4168c2ecf20Sopenharmony_ci srcu_read_unlock(&wakeup_srcu, srcuidx); 4178c2ecf20Sopenharmony_ci} 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci/** 4208c2ecf20Sopenharmony_ci * device_wakeup_disarm_wake_irqs(void) 4218c2ecf20Sopenharmony_ci * 4228c2ecf20Sopenharmony_ci * Itereates over the list of device wakeirqs to disarm them. 4238c2ecf20Sopenharmony_ci */ 4248c2ecf20Sopenharmony_civoid device_wakeup_disarm_wake_irqs(void) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci struct wakeup_source *ws; 4278c2ecf20Sopenharmony_ci int srcuidx; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci srcuidx = srcu_read_lock(&wakeup_srcu); 4308c2ecf20Sopenharmony_ci list_for_each_entry_rcu_locked(ws, &wakeup_sources, entry) 4318c2ecf20Sopenharmony_ci dev_pm_disarm_wake_irq(ws->wakeirq); 4328c2ecf20Sopenharmony_ci srcu_read_unlock(&wakeup_srcu, srcuidx); 4338c2ecf20Sopenharmony_ci} 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci/** 4368c2ecf20Sopenharmony_ci * device_wakeup_detach - Detach a device's wakeup source object from it. 4378c2ecf20Sopenharmony_ci * @dev: Device to detach the wakeup source object from. 4388c2ecf20Sopenharmony_ci * 4398c2ecf20Sopenharmony_ci * After it returns, @dev will not be treated as a wakeup device any more. 4408c2ecf20Sopenharmony_ci */ 4418c2ecf20Sopenharmony_cistatic struct wakeup_source *device_wakeup_detach(struct device *dev) 4428c2ecf20Sopenharmony_ci{ 4438c2ecf20Sopenharmony_ci struct wakeup_source *ws; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci spin_lock_irq(&dev->power.lock); 4468c2ecf20Sopenharmony_ci ws = dev->power.wakeup; 4478c2ecf20Sopenharmony_ci dev->power.wakeup = NULL; 4488c2ecf20Sopenharmony_ci spin_unlock_irq(&dev->power.lock); 4498c2ecf20Sopenharmony_ci return ws; 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci/** 4538c2ecf20Sopenharmony_ci * device_wakeup_disable - Do not regard a device as a wakeup source any more. 4548c2ecf20Sopenharmony_ci * @dev: Device to handle. 4558c2ecf20Sopenharmony_ci * 4568c2ecf20Sopenharmony_ci * Detach the @dev's wakeup source object from it, unregister this wakeup source 4578c2ecf20Sopenharmony_ci * object and destroy it. 4588c2ecf20Sopenharmony_ci */ 4598c2ecf20Sopenharmony_ciint device_wakeup_disable(struct device *dev) 4608c2ecf20Sopenharmony_ci{ 4618c2ecf20Sopenharmony_ci struct wakeup_source *ws; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci if (!dev || !dev->power.can_wakeup) 4648c2ecf20Sopenharmony_ci return -EINVAL; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci ws = device_wakeup_detach(dev); 4678c2ecf20Sopenharmony_ci wakeup_source_unregister(ws); 4688c2ecf20Sopenharmony_ci return 0; 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(device_wakeup_disable); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci/** 4738c2ecf20Sopenharmony_ci * device_set_wakeup_capable - Set/reset device wakeup capability flag. 4748c2ecf20Sopenharmony_ci * @dev: Device to handle. 4758c2ecf20Sopenharmony_ci * @capable: Whether or not @dev is capable of waking up the system from sleep. 4768c2ecf20Sopenharmony_ci * 4778c2ecf20Sopenharmony_ci * If @capable is set, set the @dev's power.can_wakeup flag and add its 4788c2ecf20Sopenharmony_ci * wakeup-related attributes to sysfs. Otherwise, unset the @dev's 4798c2ecf20Sopenharmony_ci * power.can_wakeup flag and remove its wakeup-related attributes from sysfs. 4808c2ecf20Sopenharmony_ci * 4818c2ecf20Sopenharmony_ci * This function may sleep and it can't be called from any context where 4828c2ecf20Sopenharmony_ci * sleeping is not allowed. 4838c2ecf20Sopenharmony_ci */ 4848c2ecf20Sopenharmony_civoid device_set_wakeup_capable(struct device *dev, bool capable) 4858c2ecf20Sopenharmony_ci{ 4868c2ecf20Sopenharmony_ci if (!!dev->power.can_wakeup == !!capable) 4878c2ecf20Sopenharmony_ci return; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci dev->power.can_wakeup = capable; 4908c2ecf20Sopenharmony_ci if (device_is_registered(dev) && !list_empty(&dev->power.entry)) { 4918c2ecf20Sopenharmony_ci if (capable) { 4928c2ecf20Sopenharmony_ci int ret = wakeup_sysfs_add(dev); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci if (ret) 4958c2ecf20Sopenharmony_ci dev_info(dev, "Wakeup sysfs attributes not added\n"); 4968c2ecf20Sopenharmony_ci } else { 4978c2ecf20Sopenharmony_ci wakeup_sysfs_remove(dev); 4988c2ecf20Sopenharmony_ci } 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(device_set_wakeup_capable); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci/** 5048c2ecf20Sopenharmony_ci * device_init_wakeup - Device wakeup initialization. 5058c2ecf20Sopenharmony_ci * @dev: Device to handle. 5068c2ecf20Sopenharmony_ci * @enable: Whether or not to enable @dev as a wakeup device. 5078c2ecf20Sopenharmony_ci * 5088c2ecf20Sopenharmony_ci * By default, most devices should leave wakeup disabled. The exceptions are 5098c2ecf20Sopenharmony_ci * devices that everyone expects to be wakeup sources: keyboards, power buttons, 5108c2ecf20Sopenharmony_ci * possibly network interfaces, etc. Also, devices that don't generate their 5118c2ecf20Sopenharmony_ci * own wakeup requests but merely forward requests from one bus to another 5128c2ecf20Sopenharmony_ci * (like PCI bridges) should have wakeup enabled by default. 5138c2ecf20Sopenharmony_ci */ 5148c2ecf20Sopenharmony_ciint device_init_wakeup(struct device *dev, bool enable) 5158c2ecf20Sopenharmony_ci{ 5168c2ecf20Sopenharmony_ci int ret = 0; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci if (!dev) 5198c2ecf20Sopenharmony_ci return -EINVAL; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci if (enable) { 5228c2ecf20Sopenharmony_ci device_set_wakeup_capable(dev, true); 5238c2ecf20Sopenharmony_ci ret = device_wakeup_enable(dev); 5248c2ecf20Sopenharmony_ci } else { 5258c2ecf20Sopenharmony_ci device_wakeup_disable(dev); 5268c2ecf20Sopenharmony_ci device_set_wakeup_capable(dev, false); 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci return ret; 5308c2ecf20Sopenharmony_ci} 5318c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(device_init_wakeup); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci/** 5348c2ecf20Sopenharmony_ci * device_set_wakeup_enable - Enable or disable a device to wake up the system. 5358c2ecf20Sopenharmony_ci * @dev: Device to handle. 5368c2ecf20Sopenharmony_ci */ 5378c2ecf20Sopenharmony_ciint device_set_wakeup_enable(struct device *dev, bool enable) 5388c2ecf20Sopenharmony_ci{ 5398c2ecf20Sopenharmony_ci return enable ? device_wakeup_enable(dev) : device_wakeup_disable(dev); 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(device_set_wakeup_enable); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci/** 5448c2ecf20Sopenharmony_ci * wakeup_source_not_registered - validate the given wakeup source. 5458c2ecf20Sopenharmony_ci * @ws: Wakeup source to be validated. 5468c2ecf20Sopenharmony_ci */ 5478c2ecf20Sopenharmony_cistatic bool wakeup_source_not_registered(struct wakeup_source *ws) 5488c2ecf20Sopenharmony_ci{ 5498c2ecf20Sopenharmony_ci /* 5508c2ecf20Sopenharmony_ci * Use timer struct to check if the given source is initialized 5518c2ecf20Sopenharmony_ci * by wakeup_source_add. 5528c2ecf20Sopenharmony_ci */ 5538c2ecf20Sopenharmony_ci return ws->timer.function != pm_wakeup_timer_fn; 5548c2ecf20Sopenharmony_ci} 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci/* 5578c2ecf20Sopenharmony_ci * The functions below use the observation that each wakeup event starts a 5588c2ecf20Sopenharmony_ci * period in which the system should not be suspended. The moment this period 5598c2ecf20Sopenharmony_ci * will end depends on how the wakeup event is going to be processed after being 5608c2ecf20Sopenharmony_ci * detected and all of the possible cases can be divided into two distinct 5618c2ecf20Sopenharmony_ci * groups. 5628c2ecf20Sopenharmony_ci * 5638c2ecf20Sopenharmony_ci * First, a wakeup event may be detected by the same functional unit that will 5648c2ecf20Sopenharmony_ci * carry out the entire processing of it and possibly will pass it to user space 5658c2ecf20Sopenharmony_ci * for further processing. In that case the functional unit that has detected 5668c2ecf20Sopenharmony_ci * the event may later "close" the "no suspend" period associated with it 5678c2ecf20Sopenharmony_ci * directly as soon as it has been dealt with. The pair of pm_stay_awake() and 5688c2ecf20Sopenharmony_ci * pm_relax(), balanced with each other, is supposed to be used in such 5698c2ecf20Sopenharmony_ci * situations. 5708c2ecf20Sopenharmony_ci * 5718c2ecf20Sopenharmony_ci * Second, a wakeup event may be detected by one functional unit and processed 5728c2ecf20Sopenharmony_ci * by another one. In that case the unit that has detected it cannot really 5738c2ecf20Sopenharmony_ci * "close" the "no suspend" period associated with it, unless it knows in 5748c2ecf20Sopenharmony_ci * advance what's going to happen to the event during processing. This 5758c2ecf20Sopenharmony_ci * knowledge, however, may not be available to it, so it can simply specify time 5768c2ecf20Sopenharmony_ci * to wait before the system can be suspended and pass it as the second 5778c2ecf20Sopenharmony_ci * argument of pm_wakeup_event(). 5788c2ecf20Sopenharmony_ci * 5798c2ecf20Sopenharmony_ci * It is valid to call pm_relax() after pm_wakeup_event(), in which case the 5808c2ecf20Sopenharmony_ci * "no suspend" period will be ended either by the pm_relax(), or by the timer 5818c2ecf20Sopenharmony_ci * function executed when the timer expires, whichever comes first. 5828c2ecf20Sopenharmony_ci */ 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci/** 5858c2ecf20Sopenharmony_ci * wakup_source_activate - Mark given wakeup source as active. 5868c2ecf20Sopenharmony_ci * @ws: Wakeup source to handle. 5878c2ecf20Sopenharmony_ci * 5888c2ecf20Sopenharmony_ci * Update the @ws' statistics and, if @ws has just been activated, notify the PM 5898c2ecf20Sopenharmony_ci * core of the event by incrementing the counter of of wakeup events being 5908c2ecf20Sopenharmony_ci * processed. 5918c2ecf20Sopenharmony_ci */ 5928c2ecf20Sopenharmony_cistatic void wakeup_source_activate(struct wakeup_source *ws) 5938c2ecf20Sopenharmony_ci{ 5948c2ecf20Sopenharmony_ci unsigned int cec; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci if (WARN_ONCE(wakeup_source_not_registered(ws), 5978c2ecf20Sopenharmony_ci "unregistered wakeup source\n")) 5988c2ecf20Sopenharmony_ci return; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci ws->active = true; 6018c2ecf20Sopenharmony_ci ws->active_count++; 6028c2ecf20Sopenharmony_ci ws->last_time = ktime_get(); 6038c2ecf20Sopenharmony_ci if (ws->autosleep_enabled) 6048c2ecf20Sopenharmony_ci ws->start_prevent_time = ws->last_time; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci /* Increment the counter of events in progress. */ 6078c2ecf20Sopenharmony_ci cec = atomic_inc_return(&combined_event_count); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci trace_wakeup_source_activate(ws->name, cec); 6108c2ecf20Sopenharmony_ci} 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci/** 6138c2ecf20Sopenharmony_ci * wakeup_source_report_event - Report wakeup event using the given source. 6148c2ecf20Sopenharmony_ci * @ws: Wakeup source to report the event for. 6158c2ecf20Sopenharmony_ci * @hard: If set, abort suspends in progress and wake up from suspend-to-idle. 6168c2ecf20Sopenharmony_ci */ 6178c2ecf20Sopenharmony_cistatic void wakeup_source_report_event(struct wakeup_source *ws, bool hard) 6188c2ecf20Sopenharmony_ci{ 6198c2ecf20Sopenharmony_ci ws->event_count++; 6208c2ecf20Sopenharmony_ci /* This is racy, but the counter is approximate anyway. */ 6218c2ecf20Sopenharmony_ci if (events_check_enabled) 6228c2ecf20Sopenharmony_ci ws->wakeup_count++; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci if (!ws->active) 6258c2ecf20Sopenharmony_ci wakeup_source_activate(ws); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci if (hard) 6288c2ecf20Sopenharmony_ci pm_system_wakeup(); 6298c2ecf20Sopenharmony_ci} 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci/** 6328c2ecf20Sopenharmony_ci * __pm_stay_awake - Notify the PM core of a wakeup event. 6338c2ecf20Sopenharmony_ci * @ws: Wakeup source object associated with the source of the event. 6348c2ecf20Sopenharmony_ci * 6358c2ecf20Sopenharmony_ci * It is safe to call this function from interrupt context. 6368c2ecf20Sopenharmony_ci */ 6378c2ecf20Sopenharmony_civoid __pm_stay_awake(struct wakeup_source *ws) 6388c2ecf20Sopenharmony_ci{ 6398c2ecf20Sopenharmony_ci unsigned long flags; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci if (!ws) 6428c2ecf20Sopenharmony_ci return; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci spin_lock_irqsave(&ws->lock, flags); 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci wakeup_source_report_event(ws, false); 6478c2ecf20Sopenharmony_ci del_timer(&ws->timer); 6488c2ecf20Sopenharmony_ci ws->timer_expires = 0; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ws->lock, flags); 6518c2ecf20Sopenharmony_ci} 6528c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__pm_stay_awake); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci/** 6558c2ecf20Sopenharmony_ci * pm_stay_awake - Notify the PM core that a wakeup event is being processed. 6568c2ecf20Sopenharmony_ci * @dev: Device the wakeup event is related to. 6578c2ecf20Sopenharmony_ci * 6588c2ecf20Sopenharmony_ci * Notify the PM core of a wakeup event (signaled by @dev) by calling 6598c2ecf20Sopenharmony_ci * __pm_stay_awake for the @dev's wakeup source object. 6608c2ecf20Sopenharmony_ci * 6618c2ecf20Sopenharmony_ci * Call this function after detecting of a wakeup event if pm_relax() is going 6628c2ecf20Sopenharmony_ci * to be called directly after processing the event (and possibly passing it to 6638c2ecf20Sopenharmony_ci * user space for further processing). 6648c2ecf20Sopenharmony_ci */ 6658c2ecf20Sopenharmony_civoid pm_stay_awake(struct device *dev) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci unsigned long flags; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci if (!dev) 6708c2ecf20Sopenharmony_ci return; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->power.lock, flags); 6738c2ecf20Sopenharmony_ci __pm_stay_awake(dev->power.wakeup); 6748c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->power.lock, flags); 6758c2ecf20Sopenharmony_ci} 6768c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pm_stay_awake); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_AUTOSLEEP 6798c2ecf20Sopenharmony_cistatic void update_prevent_sleep_time(struct wakeup_source *ws, ktime_t now) 6808c2ecf20Sopenharmony_ci{ 6818c2ecf20Sopenharmony_ci ktime_t delta = ktime_sub(now, ws->start_prevent_time); 6828c2ecf20Sopenharmony_ci ws->prevent_sleep_time = ktime_add(ws->prevent_sleep_time, delta); 6838c2ecf20Sopenharmony_ci} 6848c2ecf20Sopenharmony_ci#else 6858c2ecf20Sopenharmony_cistatic inline void update_prevent_sleep_time(struct wakeup_source *ws, 6868c2ecf20Sopenharmony_ci ktime_t now) {} 6878c2ecf20Sopenharmony_ci#endif 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci/** 6908c2ecf20Sopenharmony_ci * wakup_source_deactivate - Mark given wakeup source as inactive. 6918c2ecf20Sopenharmony_ci * @ws: Wakeup source to handle. 6928c2ecf20Sopenharmony_ci * 6938c2ecf20Sopenharmony_ci * Update the @ws' statistics and notify the PM core that the wakeup source has 6948c2ecf20Sopenharmony_ci * become inactive by decrementing the counter of wakeup events being processed 6958c2ecf20Sopenharmony_ci * and incrementing the counter of registered wakeup events. 6968c2ecf20Sopenharmony_ci */ 6978c2ecf20Sopenharmony_cistatic void wakeup_source_deactivate(struct wakeup_source *ws) 6988c2ecf20Sopenharmony_ci{ 6998c2ecf20Sopenharmony_ci unsigned int cnt, inpr, cec; 7008c2ecf20Sopenharmony_ci ktime_t duration; 7018c2ecf20Sopenharmony_ci ktime_t now; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci ws->relax_count++; 7048c2ecf20Sopenharmony_ci /* 7058c2ecf20Sopenharmony_ci * __pm_relax() may be called directly or from a timer function. 7068c2ecf20Sopenharmony_ci * If it is called directly right after the timer function has been 7078c2ecf20Sopenharmony_ci * started, but before the timer function calls __pm_relax(), it is 7088c2ecf20Sopenharmony_ci * possible that __pm_stay_awake() will be called in the meantime and 7098c2ecf20Sopenharmony_ci * will set ws->active. Then, ws->active may be cleared immediately 7108c2ecf20Sopenharmony_ci * by the __pm_relax() called from the timer function, but in such a 7118c2ecf20Sopenharmony_ci * case ws->relax_count will be different from ws->active_count. 7128c2ecf20Sopenharmony_ci */ 7138c2ecf20Sopenharmony_ci if (ws->relax_count != ws->active_count) { 7148c2ecf20Sopenharmony_ci ws->relax_count--; 7158c2ecf20Sopenharmony_ci return; 7168c2ecf20Sopenharmony_ci } 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci ws->active = false; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci now = ktime_get(); 7218c2ecf20Sopenharmony_ci duration = ktime_sub(now, ws->last_time); 7228c2ecf20Sopenharmony_ci ws->total_time = ktime_add(ws->total_time, duration); 7238c2ecf20Sopenharmony_ci if (ktime_to_ns(duration) > ktime_to_ns(ws->max_time)) 7248c2ecf20Sopenharmony_ci ws->max_time = duration; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci ws->last_time = now; 7278c2ecf20Sopenharmony_ci del_timer(&ws->timer); 7288c2ecf20Sopenharmony_ci ws->timer_expires = 0; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci if (ws->autosleep_enabled) 7318c2ecf20Sopenharmony_ci update_prevent_sleep_time(ws, now); 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci /* 7348c2ecf20Sopenharmony_ci * Increment the counter of registered wakeup events and decrement the 7358c2ecf20Sopenharmony_ci * couter of wakeup events in progress simultaneously. 7368c2ecf20Sopenharmony_ci */ 7378c2ecf20Sopenharmony_ci cec = atomic_add_return(MAX_IN_PROGRESS, &combined_event_count); 7388c2ecf20Sopenharmony_ci trace_wakeup_source_deactivate(ws->name, cec); 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci split_counters(&cnt, &inpr); 7418c2ecf20Sopenharmony_ci if (!inpr && waitqueue_active(&wakeup_count_wait_queue)) 7428c2ecf20Sopenharmony_ci wake_up(&wakeup_count_wait_queue); 7438c2ecf20Sopenharmony_ci} 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci/** 7468c2ecf20Sopenharmony_ci * __pm_relax - Notify the PM core that processing of a wakeup event has ended. 7478c2ecf20Sopenharmony_ci * @ws: Wakeup source object associated with the source of the event. 7488c2ecf20Sopenharmony_ci * 7498c2ecf20Sopenharmony_ci * Call this function for wakeup events whose processing started with calling 7508c2ecf20Sopenharmony_ci * __pm_stay_awake(). 7518c2ecf20Sopenharmony_ci * 7528c2ecf20Sopenharmony_ci * It is safe to call it from interrupt context. 7538c2ecf20Sopenharmony_ci */ 7548c2ecf20Sopenharmony_civoid __pm_relax(struct wakeup_source *ws) 7558c2ecf20Sopenharmony_ci{ 7568c2ecf20Sopenharmony_ci unsigned long flags; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci if (!ws) 7598c2ecf20Sopenharmony_ci return; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci spin_lock_irqsave(&ws->lock, flags); 7628c2ecf20Sopenharmony_ci if (ws->active) 7638c2ecf20Sopenharmony_ci wakeup_source_deactivate(ws); 7648c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ws->lock, flags); 7658c2ecf20Sopenharmony_ci} 7668c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__pm_relax); 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci/** 7698c2ecf20Sopenharmony_ci * pm_relax - Notify the PM core that processing of a wakeup event has ended. 7708c2ecf20Sopenharmony_ci * @dev: Device that signaled the event. 7718c2ecf20Sopenharmony_ci * 7728c2ecf20Sopenharmony_ci * Execute __pm_relax() for the @dev's wakeup source object. 7738c2ecf20Sopenharmony_ci */ 7748c2ecf20Sopenharmony_civoid pm_relax(struct device *dev) 7758c2ecf20Sopenharmony_ci{ 7768c2ecf20Sopenharmony_ci unsigned long flags; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci if (!dev) 7798c2ecf20Sopenharmony_ci return; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->power.lock, flags); 7828c2ecf20Sopenharmony_ci __pm_relax(dev->power.wakeup); 7838c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->power.lock, flags); 7848c2ecf20Sopenharmony_ci} 7858c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pm_relax); 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci/** 7888c2ecf20Sopenharmony_ci * pm_wakeup_timer_fn - Delayed finalization of a wakeup event. 7898c2ecf20Sopenharmony_ci * @data: Address of the wakeup source object associated with the event source. 7908c2ecf20Sopenharmony_ci * 7918c2ecf20Sopenharmony_ci * Call wakeup_source_deactivate() for the wakeup source whose address is stored 7928c2ecf20Sopenharmony_ci * in @data if it is currently active and its timer has not been canceled and 7938c2ecf20Sopenharmony_ci * the expiration time of the timer is not in future. 7948c2ecf20Sopenharmony_ci */ 7958c2ecf20Sopenharmony_cistatic void pm_wakeup_timer_fn(struct timer_list *t) 7968c2ecf20Sopenharmony_ci{ 7978c2ecf20Sopenharmony_ci struct wakeup_source *ws = from_timer(ws, t, timer); 7988c2ecf20Sopenharmony_ci unsigned long flags; 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci spin_lock_irqsave(&ws->lock, flags); 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci if (ws->active && ws->timer_expires 8038c2ecf20Sopenharmony_ci && time_after_eq(jiffies, ws->timer_expires)) { 8048c2ecf20Sopenharmony_ci wakeup_source_deactivate(ws); 8058c2ecf20Sopenharmony_ci ws->expire_count++; 8068c2ecf20Sopenharmony_ci } 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ws->lock, flags); 8098c2ecf20Sopenharmony_ci} 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci/** 8128c2ecf20Sopenharmony_ci * pm_wakeup_ws_event - Notify the PM core of a wakeup event. 8138c2ecf20Sopenharmony_ci * @ws: Wakeup source object associated with the event source. 8148c2ecf20Sopenharmony_ci * @msec: Anticipated event processing time (in milliseconds). 8158c2ecf20Sopenharmony_ci * @hard: If set, abort suspends in progress and wake up from suspend-to-idle. 8168c2ecf20Sopenharmony_ci * 8178c2ecf20Sopenharmony_ci * Notify the PM core of a wakeup event whose source is @ws that will take 8188c2ecf20Sopenharmony_ci * approximately @msec milliseconds to be processed by the kernel. If @ws is 8198c2ecf20Sopenharmony_ci * not active, activate it. If @msec is nonzero, set up the @ws' timer to 8208c2ecf20Sopenharmony_ci * execute pm_wakeup_timer_fn() in future. 8218c2ecf20Sopenharmony_ci * 8228c2ecf20Sopenharmony_ci * It is safe to call this function from interrupt context. 8238c2ecf20Sopenharmony_ci */ 8248c2ecf20Sopenharmony_civoid pm_wakeup_ws_event(struct wakeup_source *ws, unsigned int msec, bool hard) 8258c2ecf20Sopenharmony_ci{ 8268c2ecf20Sopenharmony_ci unsigned long flags; 8278c2ecf20Sopenharmony_ci unsigned long expires; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci if (!ws) 8308c2ecf20Sopenharmony_ci return; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci spin_lock_irqsave(&ws->lock, flags); 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci wakeup_source_report_event(ws, hard); 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci if (!msec) { 8378c2ecf20Sopenharmony_ci wakeup_source_deactivate(ws); 8388c2ecf20Sopenharmony_ci goto unlock; 8398c2ecf20Sopenharmony_ci } 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci expires = jiffies + msecs_to_jiffies(msec); 8428c2ecf20Sopenharmony_ci if (!expires) 8438c2ecf20Sopenharmony_ci expires = 1; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci if (!ws->timer_expires || time_after(expires, ws->timer_expires)) { 8468c2ecf20Sopenharmony_ci mod_timer(&ws->timer, expires); 8478c2ecf20Sopenharmony_ci ws->timer_expires = expires; 8488c2ecf20Sopenharmony_ci } 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci unlock: 8518c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ws->lock, flags); 8528c2ecf20Sopenharmony_ci} 8538c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pm_wakeup_ws_event); 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci/** 8568c2ecf20Sopenharmony_ci * pm_wakeup_dev_event - Notify the PM core of a wakeup event. 8578c2ecf20Sopenharmony_ci * @dev: Device the wakeup event is related to. 8588c2ecf20Sopenharmony_ci * @msec: Anticipated event processing time (in milliseconds). 8598c2ecf20Sopenharmony_ci * @hard: If set, abort suspends in progress and wake up from suspend-to-idle. 8608c2ecf20Sopenharmony_ci * 8618c2ecf20Sopenharmony_ci * Call pm_wakeup_ws_event() for the @dev's wakeup source object. 8628c2ecf20Sopenharmony_ci */ 8638c2ecf20Sopenharmony_civoid pm_wakeup_dev_event(struct device *dev, unsigned int msec, bool hard) 8648c2ecf20Sopenharmony_ci{ 8658c2ecf20Sopenharmony_ci unsigned long flags; 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci if (!dev) 8688c2ecf20Sopenharmony_ci return; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->power.lock, flags); 8718c2ecf20Sopenharmony_ci pm_wakeup_ws_event(dev->power.wakeup, msec, hard); 8728c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->power.lock, flags); 8738c2ecf20Sopenharmony_ci} 8748c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pm_wakeup_dev_event); 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_civoid pm_print_active_wakeup_sources(void) 8778c2ecf20Sopenharmony_ci{ 8788c2ecf20Sopenharmony_ci struct wakeup_source *ws; 8798c2ecf20Sopenharmony_ci int srcuidx, active = 0; 8808c2ecf20Sopenharmony_ci struct wakeup_source *last_activity_ws = NULL; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci srcuidx = srcu_read_lock(&wakeup_srcu); 8838c2ecf20Sopenharmony_ci list_for_each_entry_rcu_locked(ws, &wakeup_sources, entry) { 8848c2ecf20Sopenharmony_ci if (ws->active) { 8858c2ecf20Sopenharmony_ci pm_pr_dbg("active wakeup source: %s\n", ws->name); 8868c2ecf20Sopenharmony_ci active = 1; 8878c2ecf20Sopenharmony_ci } else if (!active && 8888c2ecf20Sopenharmony_ci (!last_activity_ws || 8898c2ecf20Sopenharmony_ci ktime_to_ns(ws->last_time) > 8908c2ecf20Sopenharmony_ci ktime_to_ns(last_activity_ws->last_time))) { 8918c2ecf20Sopenharmony_ci last_activity_ws = ws; 8928c2ecf20Sopenharmony_ci } 8938c2ecf20Sopenharmony_ci } 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci if (!active && last_activity_ws) 8968c2ecf20Sopenharmony_ci pm_pr_dbg("last active wakeup source: %s\n", 8978c2ecf20Sopenharmony_ci last_activity_ws->name); 8988c2ecf20Sopenharmony_ci srcu_read_unlock(&wakeup_srcu, srcuidx); 8998c2ecf20Sopenharmony_ci} 9008c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pm_print_active_wakeup_sources); 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci/** 9038c2ecf20Sopenharmony_ci * pm_wakeup_pending - Check if power transition in progress should be aborted. 9048c2ecf20Sopenharmony_ci * 9058c2ecf20Sopenharmony_ci * Compare the current number of registered wakeup events with its preserved 9068c2ecf20Sopenharmony_ci * value from the past and return true if new wakeup events have been registered 9078c2ecf20Sopenharmony_ci * since the old value was stored. Also return true if the current number of 9088c2ecf20Sopenharmony_ci * wakeup events being processed is different from zero. 9098c2ecf20Sopenharmony_ci */ 9108c2ecf20Sopenharmony_cibool pm_wakeup_pending(void) 9118c2ecf20Sopenharmony_ci{ 9128c2ecf20Sopenharmony_ci unsigned long flags; 9138c2ecf20Sopenharmony_ci bool ret = false; 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&events_lock, flags); 9168c2ecf20Sopenharmony_ci if (events_check_enabled) { 9178c2ecf20Sopenharmony_ci unsigned int cnt, inpr; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci split_counters(&cnt, &inpr); 9208c2ecf20Sopenharmony_ci ret = (cnt != saved_count || inpr > 0); 9218c2ecf20Sopenharmony_ci events_check_enabled = !ret; 9228c2ecf20Sopenharmony_ci } 9238c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&events_lock, flags); 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci if (ret) { 9268c2ecf20Sopenharmony_ci pm_pr_dbg("Wakeup pending, aborting suspend\n"); 9278c2ecf20Sopenharmony_ci pm_print_active_wakeup_sources(); 9288c2ecf20Sopenharmony_ci } 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci return ret || atomic_read(&pm_abort_suspend) > 0; 9318c2ecf20Sopenharmony_ci} 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_civoid pm_system_wakeup(void) 9348c2ecf20Sopenharmony_ci{ 9358c2ecf20Sopenharmony_ci atomic_inc(&pm_abort_suspend); 9368c2ecf20Sopenharmony_ci s2idle_wake(); 9378c2ecf20Sopenharmony_ci} 9388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pm_system_wakeup); 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_civoid pm_system_cancel_wakeup(void) 9418c2ecf20Sopenharmony_ci{ 9428c2ecf20Sopenharmony_ci atomic_dec_if_positive(&pm_abort_suspend); 9438c2ecf20Sopenharmony_ci} 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_civoid pm_wakeup_clear(unsigned int irq_number) 9468c2ecf20Sopenharmony_ci{ 9478c2ecf20Sopenharmony_ci raw_spin_lock_irq(&wakeup_irq_lock); 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci if (irq_number && wakeup_irq[0] == irq_number) 9508c2ecf20Sopenharmony_ci wakeup_irq[0] = wakeup_irq[1]; 9518c2ecf20Sopenharmony_ci else 9528c2ecf20Sopenharmony_ci wakeup_irq[0] = 0; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci wakeup_irq[1] = 0; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci raw_spin_unlock_irq(&wakeup_irq_lock); 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci if (!irq_number) 9598c2ecf20Sopenharmony_ci atomic_set(&pm_abort_suspend, 0); 9608c2ecf20Sopenharmony_ci} 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_civoid pm_system_irq_wakeup(unsigned int irq_number) 9638c2ecf20Sopenharmony_ci{ 9648c2ecf20Sopenharmony_ci unsigned long flags; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&wakeup_irq_lock, flags); 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci if (wakeup_irq[0] == 0) 9698c2ecf20Sopenharmony_ci wakeup_irq[0] = irq_number; 9708c2ecf20Sopenharmony_ci else if (wakeup_irq[1] == 0) 9718c2ecf20Sopenharmony_ci wakeup_irq[1] = irq_number; 9728c2ecf20Sopenharmony_ci else 9738c2ecf20Sopenharmony_ci irq_number = 0; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&wakeup_irq_lock, flags); 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci if (irq_number) 9788c2ecf20Sopenharmony_ci pm_system_wakeup(); 9798c2ecf20Sopenharmony_ci} 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ciunsigned int pm_wakeup_irq(void) 9828c2ecf20Sopenharmony_ci{ 9838c2ecf20Sopenharmony_ci return wakeup_irq[0]; 9848c2ecf20Sopenharmony_ci} 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci/** 9878c2ecf20Sopenharmony_ci * pm_get_wakeup_count - Read the number of registered wakeup events. 9888c2ecf20Sopenharmony_ci * @count: Address to store the value at. 9898c2ecf20Sopenharmony_ci * @block: Whether or not to block. 9908c2ecf20Sopenharmony_ci * 9918c2ecf20Sopenharmony_ci * Store the number of registered wakeup events at the address in @count. If 9928c2ecf20Sopenharmony_ci * @block is set, block until the current number of wakeup events being 9938c2ecf20Sopenharmony_ci * processed is zero. 9948c2ecf20Sopenharmony_ci * 9958c2ecf20Sopenharmony_ci * Return 'false' if the current number of wakeup events being processed is 9968c2ecf20Sopenharmony_ci * nonzero. Otherwise return 'true'. 9978c2ecf20Sopenharmony_ci */ 9988c2ecf20Sopenharmony_cibool pm_get_wakeup_count(unsigned int *count, bool block) 9998c2ecf20Sopenharmony_ci{ 10008c2ecf20Sopenharmony_ci unsigned int cnt, inpr; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci if (block) { 10038c2ecf20Sopenharmony_ci DEFINE_WAIT(wait); 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci for (;;) { 10068c2ecf20Sopenharmony_ci prepare_to_wait(&wakeup_count_wait_queue, &wait, 10078c2ecf20Sopenharmony_ci TASK_INTERRUPTIBLE); 10088c2ecf20Sopenharmony_ci split_counters(&cnt, &inpr); 10098c2ecf20Sopenharmony_ci if (inpr == 0 || signal_pending(current)) 10108c2ecf20Sopenharmony_ci break; 10118c2ecf20Sopenharmony_ci pm_print_active_wakeup_sources(); 10128c2ecf20Sopenharmony_ci schedule(); 10138c2ecf20Sopenharmony_ci } 10148c2ecf20Sopenharmony_ci finish_wait(&wakeup_count_wait_queue, &wait); 10158c2ecf20Sopenharmony_ci } 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci split_counters(&cnt, &inpr); 10188c2ecf20Sopenharmony_ci *count = cnt; 10198c2ecf20Sopenharmony_ci return !inpr; 10208c2ecf20Sopenharmony_ci} 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci/** 10238c2ecf20Sopenharmony_ci * pm_save_wakeup_count - Save the current number of registered wakeup events. 10248c2ecf20Sopenharmony_ci * @count: Value to compare with the current number of registered wakeup events. 10258c2ecf20Sopenharmony_ci * 10268c2ecf20Sopenharmony_ci * If @count is equal to the current number of registered wakeup events and the 10278c2ecf20Sopenharmony_ci * current number of wakeup events being processed is zero, store @count as the 10288c2ecf20Sopenharmony_ci * old number of registered wakeup events for pm_check_wakeup_events(), enable 10298c2ecf20Sopenharmony_ci * wakeup events detection and return 'true'. Otherwise disable wakeup events 10308c2ecf20Sopenharmony_ci * detection and return 'false'. 10318c2ecf20Sopenharmony_ci */ 10328c2ecf20Sopenharmony_cibool pm_save_wakeup_count(unsigned int count) 10338c2ecf20Sopenharmony_ci{ 10348c2ecf20Sopenharmony_ci unsigned int cnt, inpr; 10358c2ecf20Sopenharmony_ci unsigned long flags; 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci events_check_enabled = false; 10388c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&events_lock, flags); 10398c2ecf20Sopenharmony_ci split_counters(&cnt, &inpr); 10408c2ecf20Sopenharmony_ci if (cnt == count && inpr == 0) { 10418c2ecf20Sopenharmony_ci saved_count = count; 10428c2ecf20Sopenharmony_ci events_check_enabled = true; 10438c2ecf20Sopenharmony_ci } 10448c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&events_lock, flags); 10458c2ecf20Sopenharmony_ci return events_check_enabled; 10468c2ecf20Sopenharmony_ci} 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_AUTOSLEEP 10498c2ecf20Sopenharmony_ci/** 10508c2ecf20Sopenharmony_ci * pm_wakep_autosleep_enabled - Modify autosleep_enabled for all wakeup sources. 10518c2ecf20Sopenharmony_ci * @enabled: Whether to set or to clear the autosleep_enabled flags. 10528c2ecf20Sopenharmony_ci */ 10538c2ecf20Sopenharmony_civoid pm_wakep_autosleep_enabled(bool set) 10548c2ecf20Sopenharmony_ci{ 10558c2ecf20Sopenharmony_ci struct wakeup_source *ws; 10568c2ecf20Sopenharmony_ci ktime_t now = ktime_get(); 10578c2ecf20Sopenharmony_ci int srcuidx; 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci srcuidx = srcu_read_lock(&wakeup_srcu); 10608c2ecf20Sopenharmony_ci list_for_each_entry_rcu_locked(ws, &wakeup_sources, entry) { 10618c2ecf20Sopenharmony_ci spin_lock_irq(&ws->lock); 10628c2ecf20Sopenharmony_ci if (ws->autosleep_enabled != set) { 10638c2ecf20Sopenharmony_ci ws->autosleep_enabled = set; 10648c2ecf20Sopenharmony_ci if (ws->active) { 10658c2ecf20Sopenharmony_ci if (set) 10668c2ecf20Sopenharmony_ci ws->start_prevent_time = now; 10678c2ecf20Sopenharmony_ci else 10688c2ecf20Sopenharmony_ci update_prevent_sleep_time(ws, now); 10698c2ecf20Sopenharmony_ci } 10708c2ecf20Sopenharmony_ci } 10718c2ecf20Sopenharmony_ci spin_unlock_irq(&ws->lock); 10728c2ecf20Sopenharmony_ci } 10738c2ecf20Sopenharmony_ci srcu_read_unlock(&wakeup_srcu, srcuidx); 10748c2ecf20Sopenharmony_ci} 10758c2ecf20Sopenharmony_ci#endif /* CONFIG_PM_AUTOSLEEP */ 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci/** 10788c2ecf20Sopenharmony_ci * print_wakeup_source_stats - Print wakeup source statistics information. 10798c2ecf20Sopenharmony_ci * @m: seq_file to print the statistics into. 10808c2ecf20Sopenharmony_ci * @ws: Wakeup source object to print the statistics for. 10818c2ecf20Sopenharmony_ci */ 10828c2ecf20Sopenharmony_cistatic int print_wakeup_source_stats(struct seq_file *m, 10838c2ecf20Sopenharmony_ci struct wakeup_source *ws) 10848c2ecf20Sopenharmony_ci{ 10858c2ecf20Sopenharmony_ci unsigned long flags; 10868c2ecf20Sopenharmony_ci ktime_t total_time; 10878c2ecf20Sopenharmony_ci ktime_t max_time; 10888c2ecf20Sopenharmony_ci unsigned long active_count; 10898c2ecf20Sopenharmony_ci ktime_t active_time; 10908c2ecf20Sopenharmony_ci ktime_t prevent_sleep_time; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci spin_lock_irqsave(&ws->lock, flags); 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci total_time = ws->total_time; 10958c2ecf20Sopenharmony_ci max_time = ws->max_time; 10968c2ecf20Sopenharmony_ci prevent_sleep_time = ws->prevent_sleep_time; 10978c2ecf20Sopenharmony_ci active_count = ws->active_count; 10988c2ecf20Sopenharmony_ci if (ws->active) { 10998c2ecf20Sopenharmony_ci ktime_t now = ktime_get(); 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci active_time = ktime_sub(now, ws->last_time); 11028c2ecf20Sopenharmony_ci total_time = ktime_add(total_time, active_time); 11038c2ecf20Sopenharmony_ci if (active_time > max_time) 11048c2ecf20Sopenharmony_ci max_time = active_time; 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci if (ws->autosleep_enabled) 11078c2ecf20Sopenharmony_ci prevent_sleep_time = ktime_add(prevent_sleep_time, 11088c2ecf20Sopenharmony_ci ktime_sub(now, ws->start_prevent_time)); 11098c2ecf20Sopenharmony_ci } else { 11108c2ecf20Sopenharmony_ci active_time = 0; 11118c2ecf20Sopenharmony_ci } 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci seq_printf(m, "%-12s\t%lu\t\t%lu\t\t%lu\t\t%lu\t\t%lld\t\t%lld\t\t%lld\t\t%lld\t\t%lld\n", 11148c2ecf20Sopenharmony_ci ws->name, active_count, ws->event_count, 11158c2ecf20Sopenharmony_ci ws->wakeup_count, ws->expire_count, 11168c2ecf20Sopenharmony_ci ktime_to_ms(active_time), ktime_to_ms(total_time), 11178c2ecf20Sopenharmony_ci ktime_to_ms(max_time), ktime_to_ms(ws->last_time), 11188c2ecf20Sopenharmony_ci ktime_to_ms(prevent_sleep_time)); 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ws->lock, flags); 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci return 0; 11238c2ecf20Sopenharmony_ci} 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_cistatic void *wakeup_sources_stats_seq_start(struct seq_file *m, 11268c2ecf20Sopenharmony_ci loff_t *pos) 11278c2ecf20Sopenharmony_ci{ 11288c2ecf20Sopenharmony_ci struct wakeup_source *ws; 11298c2ecf20Sopenharmony_ci loff_t n = *pos; 11308c2ecf20Sopenharmony_ci int *srcuidx = m->private; 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci if (n == 0) { 11338c2ecf20Sopenharmony_ci seq_puts(m, "name\t\tactive_count\tevent_count\twakeup_count\t" 11348c2ecf20Sopenharmony_ci "expire_count\tactive_since\ttotal_time\tmax_time\t" 11358c2ecf20Sopenharmony_ci "last_change\tprevent_suspend_time\n"); 11368c2ecf20Sopenharmony_ci } 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci *srcuidx = srcu_read_lock(&wakeup_srcu); 11398c2ecf20Sopenharmony_ci list_for_each_entry_rcu_locked(ws, &wakeup_sources, entry) { 11408c2ecf20Sopenharmony_ci if (n-- <= 0) 11418c2ecf20Sopenharmony_ci return ws; 11428c2ecf20Sopenharmony_ci } 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci return NULL; 11458c2ecf20Sopenharmony_ci} 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_cistatic void *wakeup_sources_stats_seq_next(struct seq_file *m, 11488c2ecf20Sopenharmony_ci void *v, loff_t *pos) 11498c2ecf20Sopenharmony_ci{ 11508c2ecf20Sopenharmony_ci struct wakeup_source *ws = v; 11518c2ecf20Sopenharmony_ci struct wakeup_source *next_ws = NULL; 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci ++(*pos); 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci list_for_each_entry_continue_rcu(ws, &wakeup_sources, entry) { 11568c2ecf20Sopenharmony_ci next_ws = ws; 11578c2ecf20Sopenharmony_ci break; 11588c2ecf20Sopenharmony_ci } 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci if (!next_ws) 11618c2ecf20Sopenharmony_ci print_wakeup_source_stats(m, &deleted_ws); 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci return next_ws; 11648c2ecf20Sopenharmony_ci} 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_cistatic void wakeup_sources_stats_seq_stop(struct seq_file *m, void *v) 11678c2ecf20Sopenharmony_ci{ 11688c2ecf20Sopenharmony_ci int *srcuidx = m->private; 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci srcu_read_unlock(&wakeup_srcu, *srcuidx); 11718c2ecf20Sopenharmony_ci} 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci/** 11748c2ecf20Sopenharmony_ci * wakeup_sources_stats_seq_show - Print wakeup sources statistics information. 11758c2ecf20Sopenharmony_ci * @m: seq_file to print the statistics into. 11768c2ecf20Sopenharmony_ci * @v: wakeup_source of each iteration 11778c2ecf20Sopenharmony_ci */ 11788c2ecf20Sopenharmony_cistatic int wakeup_sources_stats_seq_show(struct seq_file *m, void *v) 11798c2ecf20Sopenharmony_ci{ 11808c2ecf20Sopenharmony_ci struct wakeup_source *ws = v; 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci print_wakeup_source_stats(m, ws); 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci return 0; 11858c2ecf20Sopenharmony_ci} 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_cistatic const struct seq_operations wakeup_sources_stats_seq_ops = { 11888c2ecf20Sopenharmony_ci .start = wakeup_sources_stats_seq_start, 11898c2ecf20Sopenharmony_ci .next = wakeup_sources_stats_seq_next, 11908c2ecf20Sopenharmony_ci .stop = wakeup_sources_stats_seq_stop, 11918c2ecf20Sopenharmony_ci .show = wakeup_sources_stats_seq_show, 11928c2ecf20Sopenharmony_ci}; 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_cistatic int wakeup_sources_stats_open(struct inode *inode, struct file *file) 11958c2ecf20Sopenharmony_ci{ 11968c2ecf20Sopenharmony_ci return seq_open_private(file, &wakeup_sources_stats_seq_ops, sizeof(int)); 11978c2ecf20Sopenharmony_ci} 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_cistatic const struct file_operations wakeup_sources_stats_fops = { 12008c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 12018c2ecf20Sopenharmony_ci .open = wakeup_sources_stats_open, 12028c2ecf20Sopenharmony_ci .read = seq_read, 12038c2ecf20Sopenharmony_ci .llseek = seq_lseek, 12048c2ecf20Sopenharmony_ci .release = seq_release_private, 12058c2ecf20Sopenharmony_ci}; 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_cistatic int __init wakeup_sources_debugfs_init(void) 12088c2ecf20Sopenharmony_ci{ 12098c2ecf20Sopenharmony_ci debugfs_create_file("wakeup_sources", S_IRUGO, NULL, NULL, 12108c2ecf20Sopenharmony_ci &wakeup_sources_stats_fops); 12118c2ecf20Sopenharmony_ci return 0; 12128c2ecf20Sopenharmony_ci} 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_cipostcore_initcall(wakeup_sources_debugfs_init); 1215