aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTomeu Vizoso <tomeu.vizoso@collabora.com>2016-01-07 10:46:14 -0500
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2016-01-07 19:12:06 -0500
commitaa8e54b559479d0cb7eb632ba443b8cacd20cd4b (patch)
tree73e96c085acbcd7170f0acfabcaee69b54d83a23
parent989561de9b5112999475b406557d9c7e9e59c041 (diff)
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 13a0d66e5782..049942176b00 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -250,6 +250,8 @@ static void driver_bound(struct device *dev)
250 250
251 klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices); 251 klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);
252 252
253 device_pm_check_callbacks(dev);
254
253 /* 255 /*
254 * Make sure the device is no longer in one of the deferred lists and 256 * Make sure the device is no longer in one of the deferred lists and
255 * kick off retrying all pending devices 257 * kick off retrying all pending devices
@@ -766,6 +768,7 @@ static void __device_release_driver(struct device *dev)
766 pm_runtime_reinit(dev); 768 pm_runtime_reinit(dev);
767 769
768 klist_remove(&dev->p->knode_driver); 770 klist_remove(&dev->p->knode_driver);
771 device_pm_check_callbacks(dev);
769 if (dev->bus) 772 if (dev->bus)
770 blocking_notifier_call_chain(&dev->bus->p->bus_notifier, 773 blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
771 BUS_NOTIFY_UNBOUND_DRIVER, 774 BUS_NOTIFY_UNBOUND_DRIVER,
diff --git a/drivers/base/power/common.c b/drivers/base/power/common.c
index 02812bcabcac..93ed14cc2252 100644
--- a/drivers/base/power/common.c
+++ b/drivers/base/power/common.c
@@ -14,6 +14,8 @@
14#include <linux/acpi.h> 14#include <linux/acpi.h>
15#include <linux/pm_domain.h> 15#include <linux/pm_domain.h>
16 16
17#include "power.h"
18
17/** 19/**
18 * dev_pm_get_subsys_data - Create or refcount power.subsys_data for device. 20 * dev_pm_get_subsys_data - Create or refcount power.subsys_data for device.
19 * @dev: Device to handle. 21 * @dev: Device to handle.
@@ -147,5 +149,6 @@ void dev_pm_domain_set(struct device *dev, struct dev_pm_domain *pd)
147 WARN(device_is_bound(dev), 149 WARN(device_is_bound(dev),
148 "PM domains can only be changed for unbound devices\n"); 150 "PM domains can only be changed for unbound devices\n");
149 dev->pm_domain = pd; 151 dev->pm_domain = pd;
152 device_pm_check_callbacks(dev);
150} 153}
151EXPORT_SYMBOL_GPL(dev_pm_domain_set); 154EXPORT_SYMBOL_GPL(dev_pm_domain_set);
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index abbac6fe8fd5..33a5f4b752ed 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -20,6 +20,8 @@
20#include <linux/suspend.h> 20#include <linux/suspend.h>
21#include <linux/export.h> 21#include <linux/export.h>
22 22
23#include "power.h"
24
23#define GENPD_RETRY_MAX_MS 250 /* Approximate */ 25#define GENPD_RETRY_MAX_MS 250 /* Approximate */
24 26
25#define GENPD_DEV_CALLBACK(genpd, type, callback, dev) \ 27#define GENPD_DEV_CALLBACK(genpd, type, callback, dev) \
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 9d626ac08d9c..6e7c3ccea24b 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -125,6 +125,7 @@ void device_pm_add(struct device *dev)
125{ 125{
126 pr_debug("PM: Adding info for %s:%s\n", 126 pr_debug("PM: Adding info for %s:%s\n",
127 dev->bus ? dev->bus->name : "No Bus", dev_name(dev)); 127 dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
128 device_pm_check_callbacks(dev);
128 mutex_lock(&dpm_list_mtx); 129 mutex_lock(&dpm_list_mtx);
129 if (dev->parent && dev->parent->power.is_prepared) 130 if (dev->parent && dev->parent->power.is_prepared)
130 dev_warn(dev, "parent %s should not be sleeping\n", 131 dev_warn(dev, "parent %s should not be sleeping\n",
@@ -147,6 +148,7 @@ void device_pm_remove(struct device *dev)
147 mutex_unlock(&dpm_list_mtx); 148 mutex_unlock(&dpm_list_mtx);
148 device_wakeup_disable(dev); 149 device_wakeup_disable(dev);
149 pm_runtime_remove(dev); 150 pm_runtime_remove(dev);
151 device_pm_check_callbacks(dev);
150} 152}
151 153
152/** 154/**
@@ -1572,6 +1574,11 @@ static int device_prepare(struct device *dev, pm_message_t state)
1572 1574
1573 dev->power.wakeup_path = device_may_wakeup(dev); 1575 dev->power.wakeup_path = device_may_wakeup(dev);
1574 1576
1577 if (dev->power.no_pm_callbacks) {
1578 ret = 1; /* Let device go direct_complete */
1579 goto unlock;
1580 }
1581
1575 if (dev->pm_domain) { 1582 if (dev->pm_domain) {
1576 info = "preparing power domain "; 1583 info = "preparing power domain ";
1577 callback = dev->pm_domain->ops.prepare; 1584 callback = dev->pm_domain->ops.prepare;
@@ -1594,6 +1601,7 @@ static int device_prepare(struct device *dev, pm_message_t state)
1594 if (callback) 1601 if (callback)
1595 ret = callback(dev); 1602 ret = callback(dev);
1596 1603
1604unlock:
1597 device_unlock(dev); 1605 device_unlock(dev);
1598 1606
1599 if (ret < 0) { 1607 if (ret < 0) {
@@ -1736,3 +1744,30 @@ void dpm_for_each_dev(void *data, void (*fn)(struct device *, void *))
1736 device_pm_unlock(); 1744 device_pm_unlock();
1737} 1745}
1738EXPORT_SYMBOL_GPL(dpm_for_each_dev); 1746EXPORT_SYMBOL_GPL(dpm_for_each_dev);
1747
1748static bool pm_ops_is_empty(const struct dev_pm_ops *ops)
1749{
1750 if (!ops)
1751 return true;
1752
1753 return !ops->prepare &&
1754 !ops->suspend &&
1755 !ops->suspend_late &&
1756 !ops->suspend_noirq &&
1757 !ops->resume_noirq &&
1758 !ops->resume_early &&
1759 !ops->resume &&
1760 !ops->complete;
1761}
1762
1763void device_pm_check_callbacks(struct device *dev)
1764{
1765 spin_lock_irq(&dev->power.lock);
1766 dev->power.no_pm_callbacks =
1767 (!dev->bus || pm_ops_is_empty(dev->bus->pm)) &&
1768 (!dev->class || pm_ops_is_empty(dev->class->pm)) &&
1769 (!dev->type || pm_ops_is_empty(dev->type->pm)) &&
1770 (!dev->pm_domain || pm_ops_is_empty(&dev->pm_domain->ops)) &&
1771 (!dev->driver || pm_ops_is_empty(dev->driver->pm));
1772 spin_unlock_irq(&dev->power.lock);
1773}
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
index 8b06193d4a5e..50e30e7b059d 100644
--- a/drivers/base/power/power.h
+++ b/drivers/base/power/power.h
@@ -125,6 +125,7 @@ extern void device_pm_remove(struct device *);
125extern void device_pm_move_before(struct device *, struct device *); 125extern void device_pm_move_before(struct device *, struct device *);
126extern void device_pm_move_after(struct device *, struct device *); 126extern void device_pm_move_after(struct device *, struct device *);
127extern void device_pm_move_last(struct device *); 127extern void device_pm_move_last(struct device *);
128extern void device_pm_check_callbacks(struct device *dev);
128 129
129#else /* !CONFIG_PM_SLEEP */ 130#else /* !CONFIG_PM_SLEEP */
130 131
@@ -143,6 +144,8 @@ static inline void device_pm_move_after(struct device *deva,
143 struct device *devb) {} 144 struct device *devb) {}
144static inline void device_pm_move_last(struct device *dev) {} 145static inline void device_pm_move_last(struct device *dev) {}
145 146
147static inline void device_pm_check_callbacks(struct device *dev) {}
148
146#endif /* !CONFIG_PM_SLEEP */ 149#endif /* !CONFIG_PM_SLEEP */
147 150
148static inline void device_pm_init(struct device *dev) 151static inline void device_pm_init(struct device *dev)
diff --git a/include/linux/pm.h b/include/linux/pm.h
index 528be6787796..6a5d654f4447 100644
--- a/include/linux/pm.h
+++ b/include/linux/pm.h
@@ -573,6 +573,7 @@ struct dev_pm_info {
573 struct wakeup_source *wakeup; 573 struct wakeup_source *wakeup;
574 bool wakeup_path:1; 574 bool wakeup_path:1;
575 bool syscore:1; 575 bool syscore:1;
576 bool no_pm_callbacks:1; /* Owned by the PM core */
576#else 577#else
577 unsigned int should_wakeup:1; 578 unsigned int should_wakeup:1;
578#endif 579#endif