Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1e54ec87 authored by Tomeu Vizoso's avatar Tomeu Vizoso Committed by Amit Pundir
Browse files

BACKPORT: PM / sleep: Go direct_complete if driver has no callbacks



Backport notes: This resolves clk warnings in the designware i2c
driver on HiKey seen during suspend/resume.

Cherrypicked from: aa8e54b559479d0cb7eb632ba443b8cacd20cd4b

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.

Change-Id: Ia773afb4b266f012336b99fc8cf87453839e078b
Signed-off-by: default avatarTomeu Vizoso <tomeu.vizoso@collabora.com>
Reviewed-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
[jstultz: Backported to 4.4]
Signed-off-by: default avatarJohn Stultz <john.stultz@linaro.org>
parent 6a32bc7e
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -205,6 +205,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
@@ -697,6 +699,7 @@ static void __device_release_driver(struct device *dev)
			dev->pm_domain->dismiss(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,
+35 −0
Original line number Diff line number Diff line
@@ -126,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",
@@ -148,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);
}

/**
@@ -1575,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;
@@ -1597,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) {
@@ -1725,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);
}
+3 −0
Original line number Diff line number Diff line
@@ -123,6 +123,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 */

@@ -141,6 +142,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)
+1 −0
Original line number Diff line number Diff line
@@ -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