summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTomeu Vizoso <tomeu.vizoso@collabora.com>2016-01-07 16:46:14 +0100
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2016-01-08 01:12:06 +0100
commitaa8e54b559479d0cb7eb632ba443b8cacd20cd4b (patch)
tree73e96c085acbcd7170f0acfabcaee69b54d83a23
parent989561de9b5112999475b406557d9c7e9e59c041 (diff)
downloadop-kernel-dev-aa8e54b559479d0cb7eb632ba443b8cacd20cd4b.zip
op-kernel-dev-aa8e54b559479d0cb7eb632ba443b8cacd20cd4b.tar.gz
PM / sleep: Go direct_complete if driver has no callbacks
If a suitable prepare callback cannot be found for a given device and its driver has no PM callbacks at all, assume that it can go direct to complete when the system goes to sleep. The reason for this is that there's lots of devices in a system that do no PM at all and there's no reason for them to prevent their ancestors to do direct_complete if they can support it. Signed-off-by: Tomeu Vizoso <tomeu.vizoso@collabora.com> Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r--drivers/base/dd.c3
-rw-r--r--drivers/base/power/common.c3
-rw-r--r--drivers/base/power/domain.c2
-rw-r--r--drivers/base/power/main.c35
-rw-r--r--drivers/base/power/power.h3
-rw-r--r--include/linux/pm.h1
6 files changed, 47 insertions, 0 deletions
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 13a0d66..0499421 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -250,6 +250,8 @@ static void driver_bound(struct device *dev)
klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);
+ device_pm_check_callbacks(dev);
+
/*
* Make sure the device is no longer in one of the deferred lists and
* kick off retrying all pending devices
@@ -766,6 +768,7 @@ static void __device_release_driver(struct device *dev)
pm_runtime_reinit(dev);
klist_remove(&dev->p->knode_driver);
+ device_pm_check_callbacks(dev);
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_UNBOUND_DRIVER,
diff --git a/drivers/base/power/common.c b/drivers/base/power/common.c
index 02812bc..93ed14c 100644
--- a/drivers/base/power/common.c
+++ b/drivers/base/power/common.c
@@ -14,6 +14,8 @@
#include <linux/acpi.h>
#include <linux/pm_domain.h>
+#include "power.h"
+
/**
* dev_pm_get_subsys_data - Create or refcount power.subsys_data for device.
* @dev: Device to handle.
@@ -147,5 +149,6 @@ void dev_pm_domain_set(struct device *dev, struct dev_pm_domain *pd)
WARN(device_is_bound(dev),
"PM domains can only be changed for unbound devices\n");
dev->pm_domain = pd;
+ device_pm_check_callbacks(dev);
}
EXPORT_SYMBOL_GPL(dev_pm_domain_set);
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index abbac6f..33a5f4b 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -20,6 +20,8 @@
#include <linux/suspend.h>
#include <linux/export.h>
+#include "power.h"
+
#define GENPD_RETRY_MAX_MS 250 /* Approximate */
#define GENPD_DEV_CALLBACK(genpd, type, callback, dev) \
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 9d626ac..6e7c3cc 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -125,6 +125,7 @@ void device_pm_add(struct device *dev)
{
pr_debug("PM: Adding info for %s:%s\n",
dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
+ device_pm_check_callbacks(dev);
mutex_lock(&dpm_list_mtx);
if (dev->parent && dev->parent->power.is_prepared)
dev_warn(dev, "parent %s should not be sleeping\n",
@@ -147,6 +148,7 @@ void device_pm_remove(struct device *dev)
mutex_unlock(&dpm_list_mtx);
device_wakeup_disable(dev);
pm_runtime_remove(dev);
+ device_pm_check_callbacks(dev);
}
/**
@@ -1572,6 +1574,11 @@ static int device_prepare(struct device *dev, pm_message_t state)
dev->power.wakeup_path = device_may_wakeup(dev);
+ if (dev->power.no_pm_callbacks) {
+ ret = 1; /* Let device go direct_complete */
+ goto unlock;
+ }
+
if (dev->pm_domain) {
info = "preparing power domain ";
callback = dev->pm_domain->ops.prepare;
@@ -1594,6 +1601,7 @@ static int device_prepare(struct device *dev, pm_message_t state)
if (callback)
ret = callback(dev);
+unlock:
device_unlock(dev);
if (ret < 0) {
@@ -1736,3 +1744,30 @@ void dpm_for_each_dev(void *data, void (*fn)(struct device *, void *))
device_pm_unlock();
}
EXPORT_SYMBOL_GPL(dpm_for_each_dev);
+
+static bool pm_ops_is_empty(const struct dev_pm_ops *ops)
+{
+ if (!ops)
+ return true;
+
+ return !ops->prepare &&
+ !ops->suspend &&
+ !ops->suspend_late &&
+ !ops->suspend_noirq &&
+ !ops->resume_noirq &&
+ !ops->resume_early &&
+ !ops->resume &&
+ !ops->complete;
+}
+
+void device_pm_check_callbacks(struct device *dev)
+{
+ spin_lock_irq(&dev->power.lock);
+ dev->power.no_pm_callbacks =
+ (!dev->bus || pm_ops_is_empty(dev->bus->pm)) &&
+ (!dev->class || pm_ops_is_empty(dev->class->pm)) &&
+ (!dev->type || pm_ops_is_empty(dev->type->pm)) &&
+ (!dev->pm_domain || pm_ops_is_empty(&dev->pm_domain->ops)) &&
+ (!dev->driver || pm_ops_is_empty(dev->driver->pm));
+ spin_unlock_irq(&dev->power.lock);
+}
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
index 8b06193..50e30e7 100644
--- a/drivers/base/power/power.h
+++ b/drivers/base/power/power.h
@@ -125,6 +125,7 @@ extern void device_pm_remove(struct device *);
extern void device_pm_move_before(struct device *, struct device *);
extern void device_pm_move_after(struct device *, struct device *);
extern void device_pm_move_last(struct device *);
+extern void device_pm_check_callbacks(struct device *dev);
#else /* !CONFIG_PM_SLEEP */
@@ -143,6 +144,8 @@ static inline void device_pm_move_after(struct device *deva,
struct device *devb) {}
static inline void device_pm_move_last(struct device *dev) {}
+static inline void device_pm_check_callbacks(struct device *dev) {}
+
#endif /* !CONFIG_PM_SLEEP */
static inline void device_pm_init(struct device *dev)
diff --git a/include/linux/pm.h b/include/linux/pm.h
index 528be67..6a5d654 100644
--- a/include/linux/pm.h
+++ b/include/linux/pm.h
@@ -573,6 +573,7 @@ struct dev_pm_info {
struct wakeup_source *wakeup;
bool wakeup_path:1;
bool syscore:1;
+ bool no_pm_callbacks:1; /* Owned by the PM core */
#else
unsigned int should_wakeup:1;
#endif
OpenPOWER on IntegriCloud