aboutsummaryrefslogtreecommitdiff
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.c67
1 files changed, 67 insertions, 0 deletions
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
index 79715e7fa43e..ebe6c7310c85 100644
--- a/drivers/base/power/wakeup.c
+++ b/drivers/base/power/wakeup.c
@@ -14,6 +14,7 @@
#include <linux/suspend.h>
#include <linux/seq_file.h>
#include <linux/debugfs.h>
+#include <linux/types.h>
#include <trace/events/power.h>
#include "power.h"
@@ -53,6 +54,8 @@ static LIST_HEAD(wakeup_sources);
static DECLARE_WAIT_QUEUE_HEAD(wakeup_count_wait_queue);
+static ktime_t last_read_time;
+
/**
* wakeup_source_prepare - Prepare a new wakeup source for initialization.
* @ws: Wakeup source to prepare.
@@ -342,6 +345,20 @@ int device_set_wakeup_enable(struct device *dev, bool enable)
}
EXPORT_SYMBOL_GPL(device_set_wakeup_enable);
+/**
+ * wakeup_source_not_registered - validate the given wakeup source.
+ * @ws: Wakeup source to be validated.
+ */
+static bool wakeup_source_not_registered(struct wakeup_source *ws)
+{
+ /*
+ * Use timer struct to check if the given source is initialized
+ * by wakeup_source_add.
+ */
+ return ws->timer.function != pm_wakeup_timer_fn ||
+ ws->timer.data != (unsigned long)ws;
+}
+
/*
* The functions below use the observation that each wakeup event starts a
* period in which the system should not be suspended. The moment this period
@@ -382,6 +399,10 @@ static void wakeup_source_activate(struct wakeup_source *ws)
{
unsigned int cec;
+ if (WARN(wakeup_source_not_registered(ws),
+ "unregistered wakeup source\n"))
+ return;
+
/*
* active wakeup source should bring the system
* out of PM_SUSPEND_FREEZE state
@@ -659,6 +680,37 @@ void pm_wakeup_event(struct device *dev, unsigned int msec)
}
EXPORT_SYMBOL_GPL(pm_wakeup_event);
+void pm_get_active_wakeup_sources(char *pending_wakeup_source, size_t max)
+{
+ struct wakeup_source *ws, *last_active_ws = NULL;
+ int len = 0;
+ bool active = false;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
+ if (ws->active) {
+ if (!active)
+ len += scnprintf(pending_wakeup_source, max,
+ "Pending Wakeup Sources: ");
+ len += scnprintf(pending_wakeup_source + len, max - len,
+ "%s ", ws->name);
+ active = true;
+ } else if (!active &&
+ (!last_active_ws ||
+ ktime_to_ns(ws->last_time) >
+ ktime_to_ns(last_active_ws->last_time))) {
+ last_active_ws = ws;
+ }
+ }
+ if (!active && last_active_ws) {
+ scnprintf(pending_wakeup_source, max,
+ "Last active Wakeup Source: %s",
+ last_active_ws->name);
+ }
+ rcu_read_unlock();
+}
+EXPORT_SYMBOL_GPL(pm_get_active_wakeup_sources);
+
static void print_active_wakeup_sources(void)
{
struct wakeup_source *ws;
@@ -728,10 +780,15 @@ bool pm_wakeup_pending(void)
bool pm_get_wakeup_count(unsigned int *count, bool block)
{
unsigned int cnt, inpr;
+ unsigned long flags;
if (block) {
DEFINE_WAIT(wait);
+ spin_lock_irqsave(&events_lock, flags);
+ last_read_time = ktime_get();
+ spin_unlock_irqrestore(&events_lock, flags);
+
for (;;) {
prepare_to_wait(&wakeup_count_wait_queue, &wait,
TASK_INTERRUPTIBLE);
@@ -763,6 +820,7 @@ bool pm_save_wakeup_count(unsigned int count)
{
unsigned int cnt, inpr;
unsigned long flags;
+ struct wakeup_source *ws;
events_check_enabled = false;
spin_lock_irqsave(&events_lock, flags);
@@ -770,6 +828,15 @@ bool pm_save_wakeup_count(unsigned int count)
if (cnt == count && inpr == 0) {
saved_count = count;
events_check_enabled = true;
+ } else {
+ rcu_read_lock();
+ list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
+ if (ws->active ||
+ ktime_compare(ws->last_time, last_read_time) > 0) {
+ ws->wakeup_count++;
+ }
+ }
+ rcu_read_unlock();
}
spin_unlock_irqrestore(&events_lock, flags);
return events_check_enabled;