PM / sleep: Go direct_complete if driver has no callbacks
[linux-2.6-block.git] / drivers / base / power / main.c
index 1710c26ba097d363873dfd8318ff1d949aeb4a86..6e7c3ccea24bbd3d2c10e199fe4b2018477a8a0c 100644 (file)
@@ -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);
 }
 
 /**
@@ -963,6 +965,9 @@ void dpm_complete(pm_message_t state)
        }
        list_splice(&list, &dpm_list);
        mutex_unlock(&dpm_list_mtx);
+
+       /* Allow device probing and trigger re-probing of deferred devices */
+       device_unblock_probing();
        trace_suspend_resume(TPS("dpm_complete"), state.event, false);
 }
 
@@ -1569,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;
@@ -1591,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) {
@@ -1624,6 +1635,20 @@ int dpm_prepare(pm_message_t state)
        trace_suspend_resume(TPS("dpm_prepare"), state.event, true);
        might_sleep();
 
+       /*
+        * Give a chance for the known devices to complete their probes, before
+        * disable probing of devices. This sync point is important at least
+        * at boot time + hibernation restore.
+        */
+       wait_for_device_probe();
+       /*
+        * It is unsafe if probing of devices will happen during suspend or
+        * hibernation and system behavior will be unpredictable in this case.
+        * So, let's prohibit device's probing here and defer their probes
+        * instead. The normal behavior will be restored in dpm_complete().
+        */
+       device_block_probing();
+
        mutex_lock(&dpm_list_mtx);
        while (!list_empty(&dpm_list)) {
                struct device *dev = to_device(dpm_list.next);
@@ -1719,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);
+}