.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
---|
1 | 2 | /* |
---|
2 | 3 | * drivers/base/power/wakeup.c - System wakeup events framework |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (c) 2010 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc. |
---|
5 | | - * |
---|
6 | | - * This file is released under the GPLv2. |
---|
7 | 6 | */ |
---|
| 7 | +#define pr_fmt(fmt) "PM: " fmt |
---|
8 | 8 | |
---|
9 | 9 | #include <linux/device.h> |
---|
10 | 10 | #include <linux/slab.h> |
---|
.. | .. |
---|
27 | 27 | #define pm_suspend_target_state (PM_SUSPEND_ON) |
---|
28 | 28 | #endif |
---|
29 | 29 | |
---|
| 30 | +#define list_for_each_entry_rcu_locked(pos, head, member) \ |
---|
| 31 | + list_for_each_entry_rcu(pos, head, member, \ |
---|
| 32 | + srcu_read_lock_held(&wakeup_srcu)) |
---|
30 | 33 | /* |
---|
31 | 34 | * If set, the suspend/hibernate code will abort transitions to a sleep state |
---|
32 | 35 | * if wakeup events are registered during or immediately before the transition. |
---|
.. | .. |
---|
34 | 37 | bool events_check_enabled __read_mostly; |
---|
35 | 38 | |
---|
36 | 39 | /* First wakeup IRQ seen by the kernel in the last cycle. */ |
---|
37 | | -unsigned int pm_wakeup_irq __read_mostly; |
---|
| 40 | +static unsigned int wakeup_irq[2] __read_mostly; |
---|
| 41 | +static DEFINE_RAW_SPINLOCK(wakeup_irq_lock); |
---|
38 | 42 | |
---|
39 | 43 | /* If greater than 0 and the system is suspending, terminate the suspend. */ |
---|
40 | 44 | static atomic_t pm_abort_suspend __read_mostly; |
---|
.. | .. |
---|
253 | 257 | EXPORT_SYMBOL_GPL(wakeup_source_unregister); |
---|
254 | 258 | |
---|
255 | 259 | /** |
---|
| 260 | + * wakeup_sources_read_lock - Lock wakeup source list for read. |
---|
| 261 | + * |
---|
| 262 | + * Returns an index of srcu lock for struct wakeup_srcu. |
---|
| 263 | + * This index must be passed to the matching wakeup_sources_read_unlock(). |
---|
| 264 | + */ |
---|
| 265 | +int wakeup_sources_read_lock(void) |
---|
| 266 | +{ |
---|
| 267 | + return srcu_read_lock(&wakeup_srcu); |
---|
| 268 | +} |
---|
| 269 | +EXPORT_SYMBOL_GPL(wakeup_sources_read_lock); |
---|
| 270 | + |
---|
| 271 | +/** |
---|
| 272 | + * wakeup_sources_read_unlock - Unlock wakeup source list. |
---|
| 273 | + * @idx: return value from corresponding wakeup_sources_read_lock() |
---|
| 274 | + */ |
---|
| 275 | +void wakeup_sources_read_unlock(int idx) |
---|
| 276 | +{ |
---|
| 277 | + srcu_read_unlock(&wakeup_srcu, idx); |
---|
| 278 | +} |
---|
| 279 | +EXPORT_SYMBOL_GPL(wakeup_sources_read_unlock); |
---|
| 280 | + |
---|
| 281 | +/** |
---|
| 282 | + * wakeup_sources_walk_start - Begin a walk on wakeup source list |
---|
| 283 | + * |
---|
| 284 | + * Returns first object of the list of wakeup sources. |
---|
| 285 | + * |
---|
| 286 | + * Note that to be safe, wakeup sources list needs to be locked by calling |
---|
| 287 | + * wakeup_source_read_lock() for this. |
---|
| 288 | + */ |
---|
| 289 | +struct wakeup_source *wakeup_sources_walk_start(void) |
---|
| 290 | +{ |
---|
| 291 | + struct list_head *ws_head = &wakeup_sources; |
---|
| 292 | + |
---|
| 293 | + return list_entry_rcu(ws_head->next, struct wakeup_source, entry); |
---|
| 294 | +} |
---|
| 295 | +EXPORT_SYMBOL_GPL(wakeup_sources_walk_start); |
---|
| 296 | + |
---|
| 297 | +/** |
---|
| 298 | + * wakeup_sources_walk_next - Get next wakeup source from the list |
---|
| 299 | + * @ws: Previous wakeup source object |
---|
| 300 | + * |
---|
| 301 | + * Note that to be safe, wakeup sources list needs to be locked by calling |
---|
| 302 | + * wakeup_source_read_lock() for this. |
---|
| 303 | + */ |
---|
| 304 | +struct wakeup_source *wakeup_sources_walk_next(struct wakeup_source *ws) |
---|
| 305 | +{ |
---|
| 306 | + struct list_head *ws_head = &wakeup_sources; |
---|
| 307 | + |
---|
| 308 | + return list_next_or_null_rcu(ws_head, &ws->entry, |
---|
| 309 | + struct wakeup_source, entry); |
---|
| 310 | +} |
---|
| 311 | +EXPORT_SYMBOL_GPL(wakeup_sources_walk_next); |
---|
| 312 | + |
---|
| 313 | +/** |
---|
256 | 314 | * device_wakeup_attach - Attach a wakeup source object to a device object. |
---|
257 | 315 | * @dev: Device to handle. |
---|
258 | 316 | * @ws: Wakeup source object to attach to @dev. |
---|
.. | .. |
---|
356 | 414 | int srcuidx; |
---|
357 | 415 | |
---|
358 | 416 | srcuidx = srcu_read_lock(&wakeup_srcu); |
---|
359 | | - list_for_each_entry_rcu(ws, &wakeup_sources, entry) |
---|
| 417 | + list_for_each_entry_rcu_locked(ws, &wakeup_sources, entry) |
---|
360 | 418 | dev_pm_arm_wake_irq(ws->wakeirq); |
---|
361 | 419 | srcu_read_unlock(&wakeup_srcu, srcuidx); |
---|
362 | 420 | } |
---|
.. | .. |
---|
372 | 430 | int srcuidx; |
---|
373 | 431 | |
---|
374 | 432 | srcuidx = srcu_read_lock(&wakeup_srcu); |
---|
375 | | - list_for_each_entry_rcu(ws, &wakeup_sources, entry) |
---|
| 433 | + list_for_each_entry_rcu_locked(ws, &wakeup_sources, entry) |
---|
376 | 434 | dev_pm_disarm_wake_irq(ws->wakeirq); |
---|
377 | 435 | srcu_read_unlock(&wakeup_srcu, srcuidx); |
---|
378 | 436 | } |
---|
.. | .. |
---|
798 | 856 | EXPORT_SYMBOL_GPL(pm_wakeup_ws_event); |
---|
799 | 857 | |
---|
800 | 858 | /** |
---|
801 | | - * pm_wakeup_event - Notify the PM core of a wakeup event. |
---|
| 859 | + * pm_wakeup_dev_event - Notify the PM core of a wakeup event. |
---|
802 | 860 | * @dev: Device the wakeup event is related to. |
---|
803 | 861 | * @msec: Anticipated event processing time (in milliseconds). |
---|
804 | 862 | * @hard: If set, abort suspends in progress and wake up from suspend-to-idle. |
---|
.. | .. |
---|
856 | 914 | struct wakeup_source *last_activity_ws = NULL; |
---|
857 | 915 | |
---|
858 | 916 | srcuidx = srcu_read_lock(&wakeup_srcu); |
---|
859 | | - list_for_each_entry_rcu(ws, &wakeup_sources, entry) { |
---|
| 917 | + list_for_each_entry_rcu_locked(ws, &wakeup_sources, entry) { |
---|
860 | 918 | if (ws->active) { |
---|
861 | | - pr_debug("active wakeup source: %s\n", ws->name); |
---|
| 919 | + pm_pr_dbg("active wakeup source: %s\n", ws->name); |
---|
862 | 920 | active = 1; |
---|
863 | 921 | } else if (!active && |
---|
864 | 922 | (!last_activity_ws || |
---|
.. | .. |
---|
869 | 927 | } |
---|
870 | 928 | |
---|
871 | 929 | if (!active && last_activity_ws) |
---|
872 | | - pr_debug("last active wakeup source: %s\n", |
---|
| 930 | + pm_pr_dbg("last active wakeup source: %s\n", |
---|
873 | 931 | last_activity_ws->name); |
---|
874 | 932 | srcu_read_unlock(&wakeup_srcu, srcuidx); |
---|
875 | 933 | } |
---|
.. | .. |
---|
900 | 958 | raw_spin_unlock_irqrestore(&events_lock, flags); |
---|
901 | 959 | |
---|
902 | 960 | if (ret) { |
---|
| 961 | + pm_pr_dbg("Wakeup pending, aborting suspend\n"); |
---|
| 962 | + pm_print_active_wakeup_sources(); |
---|
903 | 963 | pm_get_active_wakeup_sources(suspend_abort, |
---|
904 | 964 | MAX_SUSPEND_ABORT_LEN); |
---|
905 | 965 | log_suspend_abort_reason(suspend_abort); |
---|
.. | .. |
---|
921 | 981 | atomic_dec_if_positive(&pm_abort_suspend); |
---|
922 | 982 | } |
---|
923 | 983 | |
---|
924 | | -void pm_wakeup_clear(bool reset) |
---|
| 984 | +void pm_wakeup_clear(unsigned int irq_number) |
---|
925 | 985 | { |
---|
926 | | - pm_wakeup_irq = 0; |
---|
927 | | - if (reset) |
---|
| 986 | + raw_spin_lock_irq(&wakeup_irq_lock); |
---|
| 987 | + |
---|
| 988 | + if (irq_number && wakeup_irq[0] == irq_number) |
---|
| 989 | + wakeup_irq[0] = wakeup_irq[1]; |
---|
| 990 | + else |
---|
| 991 | + wakeup_irq[0] = 0; |
---|
| 992 | + |
---|
| 993 | + wakeup_irq[1] = 0; |
---|
| 994 | + |
---|
| 995 | + raw_spin_unlock_irq(&wakeup_irq_lock); |
---|
| 996 | + |
---|
| 997 | + if (!irq_number) |
---|
928 | 998 | atomic_set(&pm_abort_suspend, 0); |
---|
929 | 999 | } |
---|
930 | 1000 | |
---|
931 | 1001 | void pm_system_irq_wakeup(unsigned int irq_number) |
---|
932 | 1002 | { |
---|
933 | | - if (pm_wakeup_irq == 0) { |
---|
| 1003 | + unsigned long flags; |
---|
| 1004 | + |
---|
| 1005 | + raw_spin_lock_irqsave(&wakeup_irq_lock, flags); |
---|
| 1006 | + |
---|
| 1007 | + if (wakeup_irq[0] == 0) |
---|
| 1008 | + wakeup_irq[0] = irq_number; |
---|
| 1009 | + else if (wakeup_irq[1] == 0) |
---|
| 1010 | + wakeup_irq[1] = irq_number; |
---|
| 1011 | + else |
---|
| 1012 | + irq_number = 0; |
---|
| 1013 | + |
---|
| 1014 | + raw_spin_unlock_irqrestore(&wakeup_irq_lock, flags); |
---|
| 1015 | + |
---|
| 1016 | + if (irq_number) { |
---|
934 | 1017 | struct irq_desc *desc; |
---|
935 | 1018 | const char *name = "null"; |
---|
936 | 1019 | |
---|
.. | .. |
---|
942 | 1025 | |
---|
943 | 1026 | log_irq_wakeup_reason(irq_number); |
---|
944 | 1027 | pr_warn("%s: %d triggered %s\n", __func__, irq_number, name); |
---|
945 | | - |
---|
946 | | - pm_wakeup_irq = irq_number; |
---|
947 | 1028 | pm_system_wakeup(); |
---|
948 | 1029 | } |
---|
| 1030 | +} |
---|
| 1031 | + |
---|
| 1032 | +unsigned int pm_wakeup_irq(void) |
---|
| 1033 | +{ |
---|
| 1034 | + return wakeup_irq[0]; |
---|
949 | 1035 | } |
---|
950 | 1036 | |
---|
951 | 1037 | /** |
---|
.. | .. |
---|
1022 | 1108 | int srcuidx; |
---|
1023 | 1109 | |
---|
1024 | 1110 | srcuidx = srcu_read_lock(&wakeup_srcu); |
---|
1025 | | - list_for_each_entry_rcu(ws, &wakeup_sources, entry) { |
---|
| 1111 | + list_for_each_entry_rcu_locked(ws, &wakeup_sources, entry) { |
---|
1026 | 1112 | spin_lock_irq(&ws->lock); |
---|
1027 | 1113 | if (ws->autosleep_enabled != set) { |
---|
1028 | 1114 | ws->autosleep_enabled = set; |
---|
.. | .. |
---|
1038 | 1124 | srcu_read_unlock(&wakeup_srcu, srcuidx); |
---|
1039 | 1125 | } |
---|
1040 | 1126 | #endif /* CONFIG_PM_AUTOSLEEP */ |
---|
1041 | | - |
---|
1042 | | -static struct dentry *wakeup_sources_stats_dentry; |
---|
1043 | 1127 | |
---|
1044 | 1128 | /** |
---|
1045 | 1129 | * print_wakeup_source_stats - Print wakeup source statistics information. |
---|
.. | .. |
---|
1103 | 1187 | } |
---|
1104 | 1188 | |
---|
1105 | 1189 | *srcuidx = srcu_read_lock(&wakeup_srcu); |
---|
1106 | | - list_for_each_entry_rcu(ws, &wakeup_sources, entry) { |
---|
| 1190 | + list_for_each_entry_rcu_locked(ws, &wakeup_sources, entry) { |
---|
1107 | 1191 | if (n-- <= 0) |
---|
1108 | 1192 | return ws; |
---|
1109 | 1193 | } |
---|
.. | .. |
---|
1123 | 1207 | next_ws = ws; |
---|
1124 | 1208 | break; |
---|
1125 | 1209 | } |
---|
| 1210 | + |
---|
| 1211 | + if (!next_ws) |
---|
| 1212 | + print_wakeup_source_stats(m, &deleted_ws); |
---|
1126 | 1213 | |
---|
1127 | 1214 | return next_ws; |
---|
1128 | 1215 | } |
---|
.. | .. |
---|
1170 | 1257 | |
---|
1171 | 1258 | static int __init wakeup_sources_debugfs_init(void) |
---|
1172 | 1259 | { |
---|
1173 | | - wakeup_sources_stats_dentry = debugfs_create_file("wakeup_sources", |
---|
1174 | | - S_IRUGO, NULL, NULL, &wakeup_sources_stats_fops); |
---|
| 1260 | + debugfs_create_file("wakeup_sources", S_IRUGO, NULL, NULL, |
---|
| 1261 | + &wakeup_sources_stats_fops); |
---|
1175 | 1262 | return 0; |
---|
1176 | 1263 | } |
---|
1177 | 1264 | |
---|