PM / OPP: fix the errors occurred during getting or setting voltage
[firefly-linux-kernel-4.4.55.git] / drivers / base / power / main.c
index a54d810f29666292845380961eed247d068d4f15..c03fb5020650c978e334c8056726ac76751a419c 100644 (file)
@@ -60,12 +60,6 @@ struct suspend_stats suspend_stats;
 static DEFINE_MUTEX(dpm_list_mtx);
 static pm_message_t pm_transition;
 
-static void dpm_drv_timeout(unsigned long data);
-struct dpm_drv_wd_data {
-       struct device *dev;
-       struct task_struct *tsk;
-};
-
 static int async_error;
 
 static char *pm_verb(int event)
@@ -132,6 +126,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",
@@ -154,6 +149,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);
 }
 
 /**
@@ -195,14 +191,14 @@ void device_pm_move_last(struct device *dev)
        list_move_tail(&dev->power.entry, &dpm_list);
 }
 
-static ktime_t initcall_debug_start(struct device *dev)
+static ktime_t initcall_debug_start(struct device *dev, void *cb)
 {
        ktime_t calltime = ktime_set(0, 0);
 
        if (pm_print_times_enabled) {
-               pr_info("calling  %s+ @ %i, parent: %s\n",
+               pr_info("calling  %s+ @ %i, parent: %s, cb: %pf\n",
                        dev_name(dev), task_pid_nr(current),
-                       dev->parent ? dev_name(dev->parent) : "none");
+                       dev->parent ? dev_name(dev->parent) : "none", cb);
                calltime = ktime_get();
        }
 
@@ -389,7 +385,7 @@ static int dpm_run_callback(pm_callback_t cb, struct device *dev,
        if (!cb)
                return 0;
 
-       calltime = initcall_debug_start(dev);
+       calltime = initcall_debug_start(dev, cb);
 
        pm_dev_dbg(dev, state, info);
        trace_device_pm_callback_start(dev, info, state.event);
@@ -836,30 +832,6 @@ static void async_resume(void *data, async_cookie_t cookie)
        put_device(dev);
 }
 
-/**
- *     dpm_drv_timeout - Driver suspend / resume watchdog handler
- *     @data: struct device which timed out
- *
- *     Called when a driver has timed out suspending or resuming.
- *     There's not much we can do here to recover so
- *     BUG() out for a crash-dump
- *
- */
-static void dpm_drv_timeout(unsigned long data)
-{
-       struct dpm_drv_wd_data *wd_data = (void *)data;
-       struct device *dev = wd_data->dev;
-       struct task_struct *tsk = wd_data->tsk;
-
-       printk(KERN_EMERG "**** DPM device timeout: %s (%s)\n", dev_name(dev),
-              (dev->driver ? dev->driver->name : "no driver"));
-
-       printk(KERN_EMERG "dpm suspend stack:\n");
-       show_stack(tsk, NULL);
-
-       BUG();
-}
-
 /**
  * dpm_resume - Execute "resume" callbacks for non-sysdev devices.
  * @state: PM transition of the system being carried out.
@@ -1053,6 +1025,8 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a
        TRACE_DEVICE(dev);
        TRACE_SUSPEND(0);
 
+       dpm_wait_for_children(dev, async);
+
        if (async_error)
                goto Complete;
 
@@ -1064,8 +1038,6 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a
        if (dev->power.syscore || dev->power.direct_complete)
                goto Complete;
 
-       dpm_wait_for_children(dev, async);
-
        if (dev->pm_domain) {
                info = "noirq power domain ";
                callback = pm_noirq_op(&dev->pm_domain->ops, state);
@@ -1200,6 +1172,8 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as
 
        __pm_runtime_disable(dev, false);
 
+       dpm_wait_for_children(dev, async);
+
        if (async_error)
                goto Complete;
 
@@ -1211,8 +1185,6 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as
        if (dev->power.syscore || dev->power.direct_complete)
                goto Complete;
 
-       dpm_wait_for_children(dev, async);
-
        if (dev->pm_domain) {
                info = "late power domain ";
                callback = pm_late_early_op(&dev->pm_domain->ops, state);
@@ -1293,14 +1265,15 @@ int dpm_suspend_late(pm_message_t state)
                error = device_suspend_late(dev);
 
                mutex_lock(&dpm_list_mtx);
+               if (!list_empty(&dev->power.entry))
+                       list_move(&dev->power.entry, &dpm_late_early_list);
+
                if (error) {
                        pm_dev_err(dev, state, " late", error);
                        dpm_save_failed_dev(dev_name(dev));
                        put_device(dev);
                        break;
                }
-               if (!list_empty(&dev->power.entry))
-                       list_move(&dev->power.entry, &dpm_late_early_list);
                put_device(dev);
 
                if (async_error)
@@ -1355,7 +1328,7 @@ static int legacy_suspend(struct device *dev, pm_message_t state,
        int error;
        ktime_t calltime;
 
-       calltime = initcall_debug_start(dev);
+       calltime = initcall_debug_start(dev, cb);
 
        trace_device_pm_callback_start(dev, info, state.event);
        error = cb(dev, state);
@@ -1378,8 +1351,6 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
        pm_callback_t callback = NULL;
        char *info = NULL;
        int error = 0;
-       struct timer_list timer;
-       struct dpm_drv_wd_data data;
        char suspend_abort[MAX_SUSPEND_ABORT_LEN];
        DECLARE_DPM_WATCHDOG_ON_STACK(wd);
 
@@ -1410,14 +1381,6 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
 
        if (dev->power.syscore)
                goto Complete;
-       
-       data.dev = dev;
-       data.tsk = get_current();
-       init_timer_on_stack(&timer);
-       timer.expires = jiffies + HZ * 12;
-       timer.function = dpm_drv_timeout;
-       timer.data = (unsigned long)&data;
-       add_timer(&timer);
 
        if (dev->power.direct_complete) {
                if (pm_runtime_status_suspended(dev)) {
@@ -1498,9 +1461,6 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
        device_unlock(dev);
        dpm_watchdog_clear(&wd);
 
-       del_timer_sync(&timer);
-       destroy_timer_on_stack(&timer);
-
  Complete:
        complete_all(&dev->power.completion);
        if (error)
@@ -1617,6 +1577,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;
@@ -1639,6 +1604,7 @@ static int device_prepare(struct device *dev, pm_message_t state)
        if (callback)
                ret = callback(dev);
 
+unlock:
        device_unlock(dev);
 
        if (ret < 0) {
@@ -1767,3 +1733,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);
+}