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

Commit 0015afaa authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

Merge branch 'pm-runtime' into pm-for-linus

* pm-runtime:
  PM / Runtime: Use device PM QoS constraints (v2)
parents b7ba68c4 00dc9ad1
Loading
Loading
Loading
Loading
+16 −8
Original line number Diff line number Diff line
@@ -47,21 +47,29 @@ static DEFINE_MUTEX(dev_pm_qos_mtx);
static BLOCKING_NOTIFIER_HEAD(dev_pm_notifiers);

/**
 * dev_pm_qos_read_value - Get PM QoS constraint for a given device.
 * __dev_pm_qos_read_value - Get PM QoS constraint for a given device.
 * @dev: Device to get the PM QoS constraint value for.
 *
 * This routine must be called with dev->power.lock held.
 */
s32 __dev_pm_qos_read_value(struct device *dev)
{
	struct pm_qos_constraints *c = dev->power.constraints;

	return c ? pm_qos_read_value(c) : 0;
}

/**
 * dev_pm_qos_read_value - Get PM QoS constraint for a given device (locked).
 * @dev: Device to get the PM QoS constraint value for.
 */
s32 dev_pm_qos_read_value(struct device *dev)
{
	struct pm_qos_constraints *c;
	unsigned long flags;
	s32 ret = 0;
	s32 ret;

	spin_lock_irqsave(&dev->power.lock, flags);

	c = dev->power.constraints;
	if (c)
		ret = pm_qos_read_value(c);

	ret = __dev_pm_qos_read_value(dev);
	spin_unlock_irqrestore(&dev->power.lock, flags);

	return ret;
+128 −20
Original line number Diff line number Diff line
@@ -282,6 +282,47 @@ static int rpm_callback(int (*cb)(struct device *), struct device *dev)
	return retval != -EACCES ? retval : -EIO;
}

struct rpm_qos_data {
	ktime_t time_now;
	s64 constraint_ns;
};

/**
 * rpm_update_qos_constraint - Update a given PM QoS constraint data.
 * @dev: Device whose timing data to use.
 * @data: PM QoS constraint data to update.
 *
 * Use the suspend timing data of @dev to update PM QoS constraint data pointed
 * to by @data.
 */
static int rpm_update_qos_constraint(struct device *dev, void *data)
{
	struct rpm_qos_data *qos = data;
	unsigned long flags;
	s64 delta_ns;
	int ret = 0;

	spin_lock_irqsave(&dev->power.lock, flags);

	if (dev->power.max_time_suspended_ns < 0)
		goto out;

	delta_ns = dev->power.max_time_suspended_ns -
		ktime_to_ns(ktime_sub(qos->time_now, dev->power.suspend_time));
	if (delta_ns <= 0) {
		ret = -EBUSY;
		goto out;
	}

	if (qos->constraint_ns > delta_ns || qos->constraint_ns == 0)
		qos->constraint_ns = delta_ns;

 out:
	spin_unlock_irqrestore(&dev->power.lock, flags);

	return ret;
}

/**
 * rpm_suspend - Carry out runtime suspend of given device.
 * @dev: Device to suspend.
@@ -308,6 +349,7 @@ static int rpm_suspend(struct device *dev, int rpmflags)
{
	int (*callback)(struct device *);
	struct device *parent = NULL;
	struct rpm_qos_data qos;
	int retval;

	trace_rpm_suspend(dev, rpmflags);
@@ -403,8 +445,38 @@ static int rpm_suspend(struct device *dev, int rpmflags)
		goto out;
	}

	qos.constraint_ns = __dev_pm_qos_read_value(dev);
	if (qos.constraint_ns < 0) {
		/* Negative constraint means "never suspend". */
		retval = -EPERM;
		goto out;
	}
	qos.constraint_ns *= NSEC_PER_USEC;
	qos.time_now = ktime_get();

	__update_runtime_status(dev, RPM_SUSPENDING);

	if (!dev->power.ignore_children) {
		if (dev->power.irq_safe)
			spin_unlock(&dev->power.lock);
		else
			spin_unlock_irq(&dev->power.lock);

		retval = device_for_each_child(dev, &qos,
					       rpm_update_qos_constraint);

		if (dev->power.irq_safe)
			spin_lock(&dev->power.lock);
		else
			spin_lock_irq(&dev->power.lock);

		if (retval)
			goto fail;
	}

	dev->power.suspend_time = qos.time_now;
	dev->power.max_time_suspended_ns = qos.constraint_ns ? : -1;

	if (dev->pm_domain)
		callback = dev->pm_domain->ops.runtime_suspend;
	else if (dev->type && dev->type->pm)
@@ -420,27 +492,9 @@ static int rpm_suspend(struct device *dev, int rpmflags)
		callback = dev->driver->pm->runtime_suspend;

	retval = rpm_callback(callback, dev);
	if (retval) {
		__update_runtime_status(dev, RPM_ACTIVE);
		dev->power.deferred_resume = false;
		if (retval == -EAGAIN || retval == -EBUSY) {
			dev->power.runtime_error = 0;
	if (retval)
		goto fail;

			/*
			 * If the callback routine failed an autosuspend, and
			 * if the last_busy time has been updated so that there
			 * is a new autosuspend expiration time, automatically
			 * reschedule another autosuspend.
			 */
			if ((rpmflags & RPM_AUTO) &&
			    pm_runtime_autosuspend_expiration(dev) != 0)
				goto repeat;
		} else {
			pm_runtime_cancel_pending(dev);
		}
		wake_up_all(&dev->power.wait_queue);
		goto out;
	}
 no_callback:
	__update_runtime_status(dev, RPM_SUSPENDED);
	pm_runtime_deactivate_timer(dev);
@@ -472,6 +526,29 @@ static int rpm_suspend(struct device *dev, int rpmflags)
	trace_rpm_return_int(dev, _THIS_IP_, retval);

	return retval;

 fail:
	__update_runtime_status(dev, RPM_ACTIVE);
	dev->power.suspend_time = ktime_set(0, 0);
	dev->power.max_time_suspended_ns = -1;
	dev->power.deferred_resume = false;
	if (retval == -EAGAIN || retval == -EBUSY) {
		dev->power.runtime_error = 0;

		/*
		 * If the callback routine failed an autosuspend, and
		 * if the last_busy time has been updated so that there
		 * is a new autosuspend expiration time, automatically
		 * reschedule another autosuspend.
		 */
		if ((rpmflags & RPM_AUTO) &&
		    pm_runtime_autosuspend_expiration(dev) != 0)
			goto repeat;
	} else {
		pm_runtime_cancel_pending(dev);
	}
	wake_up_all(&dev->power.wait_queue);
	goto out;
}

/**
@@ -626,6 +703,9 @@ static int rpm_resume(struct device *dev, int rpmflags)
	if (dev->power.no_callbacks)
		goto no_callback;	/* Assume success. */

	dev->power.suspend_time = ktime_set(0, 0);
	dev->power.max_time_suspended_ns = -1;

	__update_runtime_status(dev, RPM_RESUMING);

	if (dev->pm_domain)
@@ -1288,6 +1368,9 @@ void pm_runtime_init(struct device *dev)
	setup_timer(&dev->power.suspend_timer, pm_suspend_timer_fn,
			(unsigned long)dev);

	dev->power.suspend_time = ktime_set(0, 0);
	dev->power.max_time_suspended_ns = -1;

	init_waitqueue_head(&dev->power.wait_queue);
}

@@ -1305,3 +1388,28 @@ void pm_runtime_remove(struct device *dev)
	if (dev->power.irq_safe && dev->parent)
		pm_runtime_put_sync(dev->parent);
}

/**
 * pm_runtime_update_max_time_suspended - Update device's suspend time data.
 * @dev: Device to handle.
 * @delta_ns: Value to subtract from the device's max_time_suspended_ns field.
 *
 * Update the device's power.max_time_suspended_ns field by subtracting
 * @delta_ns from it.  The resulting value of power.max_time_suspended_ns is
 * never negative.
 */
void pm_runtime_update_max_time_suspended(struct device *dev, s64 delta_ns)
{
	unsigned long flags;

	spin_lock_irqsave(&dev->power.lock, flags);

	if (delta_ns > 0 && dev->power.max_time_suspended_ns > 0) {
		if (dev->power.max_time_suspended_ns > delta_ns)
			dev->power.max_time_suspended_ns -= delta_ns;
		else
			dev->power.max_time_suspended_ns = 0;
	}

	spin_unlock_irqrestore(&dev->power.lock, flags);
}
+2 −0
Original line number Diff line number Diff line
@@ -508,6 +508,8 @@ struct dev_pm_info {
	unsigned long		active_jiffies;
	unsigned long		suspended_jiffies;
	unsigned long		accounting_timestamp;
	ktime_t			suspend_time;
	s64			max_time_suspended_ns;
#endif
	struct pm_subsys_data	*subsys_data;  /* Owned by the subsystem. */
	struct pm_qos_constraints *constraints;
+3 −0
Original line number Diff line number Diff line
@@ -78,6 +78,7 @@ int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier);
int pm_qos_request_active(struct pm_qos_request *req);
s32 pm_qos_read_value(struct pm_qos_constraints *c);

s32 __dev_pm_qos_read_value(struct device *dev);
s32 dev_pm_qos_read_value(struct device *dev);
int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
			   s32 value);
@@ -119,6 +120,8 @@ static inline int pm_qos_request_active(struct pm_qos_request *req)
static inline s32 pm_qos_read_value(struct pm_qos_constraints *c)
			{ return 0; }

static inline s32 __dev_pm_qos_read_value(struct device *dev)
			{ return 0; }
static inline s32 dev_pm_qos_read_value(struct device *dev)
			{ return 0; }
static inline int dev_pm_qos_add_request(struct device *dev,
+5 −0
Original line number Diff line number Diff line
@@ -45,6 +45,8 @@ extern void pm_runtime_irq_safe(struct device *dev);
extern void __pm_runtime_use_autosuspend(struct device *dev, bool use);
extern void pm_runtime_set_autosuspend_delay(struct device *dev, int delay);
extern unsigned long pm_runtime_autosuspend_expiration(struct device *dev);
extern void pm_runtime_update_max_time_suspended(struct device *dev,
						 s64 delta_ns);

static inline bool pm_children_suspended(struct device *dev)
{
@@ -148,6 +150,9 @@ static inline void pm_runtime_set_autosuspend_delay(struct device *dev,
static inline unsigned long pm_runtime_autosuspend_expiration(
				struct device *dev) { return 0; }

static inline void pm_runtime_update_max_time_suspended(struct device *dev,
							s64 delta_ns) {}

#endif /* !CONFIG_PM_RUNTIME */

static inline int pm_runtime_idle(struct device *dev)