aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/base/power/wakeup.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/base/power/wakeup.c')
-rw-r--r--drivers/base/power/wakeup.c109
1 files changed, 76 insertions, 33 deletions
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
index 8ec406d8f548..4573c83df6dd 100644
--- a/drivers/base/power/wakeup.c
+++ b/drivers/base/power/wakeup.c
@@ -24,12 +24,26 @@
24 */ 24 */
25bool events_check_enabled; 25bool events_check_enabled;
26 26
27/* The counter of registered wakeup events. */ 27/*
28static atomic_t event_count = ATOMIC_INIT(0); 28 * Combined counters of registered wakeup events and wakeup events in progress.
29/* A preserved old value of event_count. */ 29 * They need to be modified together atomically, so it's better to use one
30 * atomic variable to hold them both.
31 */
32static atomic_t combined_event_count = ATOMIC_INIT(0);
33
34#define IN_PROGRESS_BITS (sizeof(int) * 4)
35#define MAX_IN_PROGRESS ((1 << IN_PROGRESS_BITS) - 1)
36
37static void split_counters(unsigned int *cnt, unsigned int *inpr)
38{
39 unsigned int comb = atomic_read(&combined_event_count);
40
41 *cnt = (comb >> IN_PROGRESS_BITS);
42 *inpr = comb & MAX_IN_PROGRESS;
43}
44
45/* A preserved old value of the events counter. */
30static unsigned int saved_count; 46static unsigned int saved_count;
31/* The counter of wakeup events being processed. */
32static atomic_t events_in_progress = ATOMIC_INIT(0);
33 47
34static DEFINE_SPINLOCK(events_lock); 48static DEFINE_SPINLOCK(events_lock);
35 49
@@ -228,6 +242,35 @@ int device_wakeup_disable(struct device *dev)
228EXPORT_SYMBOL_GPL(device_wakeup_disable); 242EXPORT_SYMBOL_GPL(device_wakeup_disable);
229 243
230/** 244/**
245 * device_set_wakeup_capable - Set/reset device wakeup capability flag.
246 * @dev: Device to handle.
247 * @capable: Whether or not @dev is capable of waking up the system from sleep.
248 *
249 * If @capable is set, set the @dev's power.can_wakeup flag and add its
250 * wakeup-related attributes to sysfs. Otherwise, unset the @dev's
251 * power.can_wakeup flag and remove its wakeup-related attributes from sysfs.
252 *
253 * This function may sleep and it can't be called from any context where
254 * sleeping is not allowed.
255 */
256void device_set_wakeup_capable(struct device *dev, bool capable)
257{
258 if (!!dev->power.can_wakeup == !!capable)
259 return;
260
261 if (device_is_registered(dev)) {
262 if (capable) {
263 if (wakeup_sysfs_add(dev))
264 return;
265 } else {
266 wakeup_sysfs_remove(dev);
267 }
268 }
269 dev->power.can_wakeup = capable;
270}
271EXPORT_SYMBOL_GPL(device_set_wakeup_capable);
272
273/**
231 * device_init_wakeup - Device wakeup initialization. 274 * device_init_wakeup - Device wakeup initialization.
232 * @dev: Device to handle. 275 * @dev: Device to handle.
233 * @enable: Whether or not to enable @dev as a wakeup device. 276 * @enable: Whether or not to enable @dev as a wakeup device.
@@ -307,7 +350,8 @@ static void wakeup_source_activate(struct wakeup_source *ws)
307 ws->timer_expires = jiffies; 350 ws->timer_expires = jiffies;
308 ws->last_time = ktime_get(); 351 ws->last_time = ktime_get();
309 352
310 atomic_inc(&events_in_progress); 353 /* Increment the counter of events in progress. */
354 atomic_inc(&combined_event_count);
311} 355}
312 356
313/** 357/**
@@ -394,14 +438,10 @@ static void wakeup_source_deactivate(struct wakeup_source *ws)
394 del_timer(&ws->timer); 438 del_timer(&ws->timer);
395 439
396 /* 440 /*
397 * event_count has to be incremented before events_in_progress is 441 * Increment the counter of registered wakeup events and decrement the
398 * modified, so that the callers of pm_check_wakeup_events() and 442 * couter of wakeup events in progress simultaneously.
399 * pm_save_wakeup_count() don't see the old value of event_count and
400 * events_in_progress equal to zero at the same time.
401 */ 443 */
402 atomic_inc(&event_count); 444 atomic_add(MAX_IN_PROGRESS, &combined_event_count);
403 smp_mb__before_atomic_dec();
404 atomic_dec(&events_in_progress);
405} 445}
406 446
407/** 447/**
@@ -556,8 +596,10 @@ bool pm_wakeup_pending(void)
556 596
557 spin_lock_irqsave(&events_lock, flags); 597 spin_lock_irqsave(&events_lock, flags);
558 if (events_check_enabled) { 598 if (events_check_enabled) {
559 ret = ((unsigned int)atomic_read(&event_count) != saved_count) 599 unsigned int cnt, inpr;
560 || atomic_read(&events_in_progress); 600
601 split_counters(&cnt, &inpr);
602 ret = (cnt != saved_count || inpr > 0);
561 events_check_enabled = !ret; 603 events_check_enabled = !ret;
562 } 604 }
563 spin_unlock_irqrestore(&events_lock, flags); 605 spin_unlock_irqrestore(&events_lock, flags);
@@ -573,25 +615,25 @@ bool pm_wakeup_pending(void)
573 * Store the number of registered wakeup events at the address in @count. Block 615 * Store the number of registered wakeup events at the address in @count. Block
574 * if the current number of wakeup events being processed is nonzero. 616 * if the current number of wakeup events being processed is nonzero.
575 * 617 *
576 * Return false if the wait for the number of wakeup events being processed to 618 * Return 'false' if the wait for the number of wakeup events being processed to
577 * drop down to zero has been interrupted by a signal (and the current number 619 * drop down to zero has been interrupted by a signal (and the current number
578 * of wakeup events being processed is still nonzero). Otherwise return true. 620 * of wakeup events being processed is still nonzero). Otherwise return 'true'.
579 */ 621 */
580bool pm_get_wakeup_count(unsigned int *count) 622bool pm_get_wakeup_count(unsigned int *count)
581{ 623{
582 bool ret; 624 unsigned int cnt, inpr;
583
584 if (capable(CAP_SYS_ADMIN))
585 events_check_enabled = false;
586 625
587 while (atomic_read(&events_in_progress) && !signal_pending(current)) { 626 for (;;) {
627 split_counters(&cnt, &inpr);
628 if (inpr == 0 || signal_pending(current))
629 break;
588 pm_wakeup_update_hit_counts(); 630 pm_wakeup_update_hit_counts();
589 schedule_timeout_interruptible(msecs_to_jiffies(TIMEOUT)); 631 schedule_timeout_interruptible(msecs_to_jiffies(TIMEOUT));
590 } 632 }
591 633
592 ret = !atomic_read(&events_in_progress); 634 split_counters(&cnt, &inpr);
593 *count = atomic_read(&event_count); 635 *count = cnt;
594 return ret; 636 return !inpr;
595} 637}
596 638
597/** 639/**
@@ -600,24 +642,25 @@ bool pm_get_wakeup_count(unsigned int *count)
600 * 642 *
601 * If @count is equal to the current number of registered wakeup events and the 643 * If @count is equal to the current number of registered wakeup events and the
602 * current number of wakeup events being processed is zero, store @count as the 644 * current number of wakeup events being processed is zero, store @count as the
603 * old number of registered wakeup events to be used by pm_check_wakeup_events() 645 * old number of registered wakeup events for pm_check_wakeup_events(), enable
604 * and return true. Otherwise return false. 646 * wakeup events detection and return 'true'. Otherwise disable wakeup events
647 * detection and return 'false'.
605 */ 648 */
606bool pm_save_wakeup_count(unsigned int count) 649bool pm_save_wakeup_count(unsigned int count)
607{ 650{
608 bool ret = false; 651 unsigned int cnt, inpr;
609 652
653 events_check_enabled = false;
610 spin_lock_irq(&events_lock); 654 spin_lock_irq(&events_lock);
611 if (count == (unsigned int)atomic_read(&event_count) 655 split_counters(&cnt, &inpr);
612 && !atomic_read(&events_in_progress)) { 656 if (cnt == count && inpr == 0) {
613 saved_count = count; 657 saved_count = count;
614 events_check_enabled = true; 658 events_check_enabled = true;
615 ret = true;
616 } 659 }
617 spin_unlock_irq(&events_lock); 660 spin_unlock_irq(&events_lock);
618 if (!ret) 661 if (!events_check_enabled)
619 pm_wakeup_update_hit_counts(); 662 pm_wakeup_update_hit_counts();
620 return ret; 663 return events_check_enabled;
621} 664}
622 665
623static struct dentry *wakeup_sources_stats_dentry; 666static struct dentry *wakeup_sources_stats_dentry;