1415 lines · 38.3 KB · C
RawDownload
/* * drivers/base/power/runtime.c - Helper functions for device runtime PM * * Copyright (c) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc. * Copyright (C) 2010 Alan Stern <stern@rowland.harvard.edu> * * This file is released under the GPLv2. */ #include <linux/sched.h>#include <linux/export.h>#include <linux/pm_runtime.h>#include <trace/events/rpm.h>#include "power.h" static int rpm_resume(struct device *dev, int rpmflags);static int rpm_suspend(struct device *dev, int rpmflags); /** * update_pm_runtime_accounting - Update the time accounting of power states * @dev: Device to update the accounting for * * In order to be able to have time accounting of the various power states * (as used by programs such as PowerTOP to show the effectiveness of runtime * PM), we need to track the time spent in each state. * update_pm_runtime_accounting must be called each time before the * runtime_status field is updated, to account the time in the old state * correctly. */void update_pm_runtime_accounting(struct device *dev){	unsigned long now = jiffies;	unsigned long delta; 	delta = now - dev->power.accounting_timestamp; 	dev->power.accounting_timestamp = now; 	if (dev->power.disable_depth > 0)		return; 	if (dev->power.runtime_status == RPM_SUSPENDED)		dev->power.suspended_jiffies += delta;	else		dev->power.active_jiffies += delta;} static void __update_runtime_status(struct device *dev, enum rpm_status status){	update_pm_runtime_accounting(dev);	dev->power.runtime_status = status;} /** * pm_runtime_deactivate_timer - Deactivate given device's suspend timer. * @dev: Device to handle. */static void pm_runtime_deactivate_timer(struct device *dev){	if (dev->power.timer_expires > 0) {		del_timer(&dev->power.suspend_timer);		dev->power.timer_expires = 0;	}} /** * pm_runtime_cancel_pending - Deactivate suspend timer and cancel requests. * @dev: Device to handle. */static void pm_runtime_cancel_pending(struct device *dev){	pm_runtime_deactivate_timer(dev);	/*	 * In case there's a request pending, make sure its work function will	 * return without doing anything.	 */	dev->power.request = RPM_REQ_NONE;} /* * pm_runtime_autosuspend_expiration - Get a device's autosuspend-delay expiration time. * @dev: Device to handle. * * Compute the autosuspend-delay expiration time based on the device's * power.last_busy time.  If the delay has already expired or is disabled * (negative) or the power.use_autosuspend flag isn't set, return 0. * Otherwise return the expiration time in jiffies (adjusted to be nonzero). * * This function may be called either with or without dev->power.lock held. * Either way it can be racy, since power.last_busy may be updated at any time. */unsigned long pm_runtime_autosuspend_expiration(struct device *dev){	int autosuspend_delay;	long elapsed;	unsigned long last_busy;	unsigned long expires = 0; 	if (!dev->power.use_autosuspend)		goto out; 	autosuspend_delay = ACCESS_ONCE(dev->power.autosuspend_delay);	if (autosuspend_delay < 0)		goto out; 	last_busy = ACCESS_ONCE(dev->power.last_busy);	elapsed = jiffies - last_busy;	if (elapsed < 0)		goto out;	/* jiffies has wrapped around. */ 	/*	 * If the autosuspend_delay is >= 1 second, align the timer by rounding	 * up to the nearest second.	 */	expires = last_busy + msecs_to_jiffies(autosuspend_delay);	if (autosuspend_delay >= 1000)		expires = round_jiffies(expires);	expires += !expires;	if (elapsed >= expires - last_busy)		expires = 0;	/* Already expired. */  out:	return expires;}EXPORT_SYMBOL_GPL(pm_runtime_autosuspend_expiration); /** * rpm_check_suspend_allowed - Test whether a device may be suspended. * @dev: Device to test. */static int rpm_check_suspend_allowed(struct device *dev){	int retval = 0; 	if (dev->power.runtime_error)		retval = -EINVAL;	else if (dev->power.disable_depth > 0)		retval = -EACCES;	else if (atomic_read(&dev->power.usage_count) > 0)		retval = -EAGAIN;	else if (!pm_children_suspended(dev))		retval = -EBUSY; 	/* Pending resume requests take precedence over suspends. */	else if ((dev->power.deferred_resume			&& dev->power.runtime_status == RPM_SUSPENDING)	    || (dev->power.request_pending			&& dev->power.request == RPM_REQ_RESUME))		retval = -EAGAIN;	else if (dev->power.runtime_status == RPM_SUSPENDED)		retval = 1; 	return retval;} /** * __rpm_callback - Run a given runtime PM callback for a given device. * @cb: Runtime PM callback to run. * @dev: Device to run the callback for. */static int __rpm_callback(int (*cb)(struct device *), struct device *dev)	__releases(&dev->power.lock) __acquires(&dev->power.lock){	int retval; 	if (dev->power.irq_safe)		spin_unlock(&dev->power.lock);	else		spin_unlock_irq(&dev->power.lock); 	retval = cb(dev); 	if (dev->power.irq_safe)		spin_lock(&dev->power.lock);	else		spin_lock_irq(&dev->power.lock); 	return retval;} /** * rpm_idle - Notify device bus type if the device can be suspended. * @dev: Device to notify the bus type about. * @rpmflags: Flag bits. * * Check if the device's runtime PM status allows it to be suspended.  If * another idle notification has been started earlier, return immediately.  If * the RPM_ASYNC flag is set then queue an idle-notification request; otherwise * run the ->runtime_idle() callback directly. * * This function must be called under dev->power.lock with interrupts disabled. */static int rpm_idle(struct device *dev, int rpmflags){	int (*callback)(struct device *);	int retval; 	trace_rpm_idle(dev, rpmflags);	retval = rpm_check_suspend_allowed(dev);	if (retval < 0)		;	/* Conditions are wrong. */ 	/* Idle notifications are allowed only in the RPM_ACTIVE state. */	else if (dev->power.runtime_status != RPM_ACTIVE)		retval = -EAGAIN; 	/*	 * Any pending request other than an idle notification takes	 * precedence over us, except that the timer may be running.	 */	else if (dev->power.request_pending &&	    dev->power.request > RPM_REQ_IDLE)		retval = -EAGAIN; 	/* Act as though RPM_NOWAIT is always set. */	else if (dev->power.idle_notification)		retval = -EINPROGRESS;	if (retval)		goto out; 	/* Pending requests need to be canceled. */	dev->power.request = RPM_REQ_NONE; 	if (dev->power.no_callbacks) {		/* Assume ->runtime_idle() callback would have suspended. */		retval = rpm_suspend(dev, rpmflags);		goto out;	} 	/* Carry out an asynchronous or a synchronous idle notification. */	if (rpmflags & RPM_ASYNC) {		dev->power.request = RPM_REQ_IDLE;		if (!dev->power.request_pending) {			dev->power.request_pending = true;			queue_work(pm_wq, &dev->power.work);		}		goto out;	} 	dev->power.idle_notification = true; 	if (dev->pm_domain)		callback = dev->pm_domain->ops.runtime_idle;	else if (dev->type && dev->type->pm)		callback = dev->type->pm->runtime_idle;	else if (dev->class && dev->class->pm)		callback = dev->class->pm->runtime_idle;	else if (dev->bus && dev->bus->pm)		callback = dev->bus->pm->runtime_idle;	else		callback = NULL; 	if (!callback && dev->driver && dev->driver->pm)		callback = dev->driver->pm->runtime_idle; 	if (callback)		__rpm_callback(callback, dev); 	dev->power.idle_notification = false;	wake_up_all(&dev->power.wait_queue);  out:	trace_rpm_return_int(dev, _THIS_IP_, retval);	return retval;} /** * rpm_callback - Run a given runtime PM callback for a given device. * @cb: Runtime PM callback to run. * @dev: Device to run the callback for. */static int rpm_callback(int (*cb)(struct device *), struct device *dev){	int retval; 	if (!cb)		return -ENOSYS; 	retval = __rpm_callback(cb, dev); 	dev->power.runtime_error = retval;	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. * @rpmflags: Flag bits. * * Check if the device's runtime PM status allows it to be suspended. * Cancel a pending idle notification, autosuspend or suspend. If * another suspend has been started earlier, either return immediately * or wait for it to finish, depending on the RPM_NOWAIT and RPM_ASYNC * flags. If the RPM_ASYNC flag is set then queue a suspend request; * otherwise run the ->runtime_suspend() callback directly. When * ->runtime_suspend succeeded, if a deferred resume was requested while * the callback was running then carry it out, otherwise send an idle * notification for its parent (if the suspend succeeded and both * ignore_children of parent->power and irq_safe of dev->power are not set). * If ->runtime_suspend failed with -EAGAIN or -EBUSY, and if the RPM_AUTO * flag is set and the next autosuspend-delay expiration time is in the * future, schedule another autosuspend attempt. * * This function must be called under dev->power.lock with interrupts disabled. */static int rpm_suspend(struct device *dev, int rpmflags)	__releases(&dev->power.lock) __acquires(&dev->power.lock){	int (*callback)(struct device *);	struct device *parent = NULL;	struct rpm_qos_data qos;	int retval; 	trace_rpm_suspend(dev, rpmflags);  repeat:	retval = rpm_check_suspend_allowed(dev); 	if (retval < 0)		;	/* Conditions are wrong. */ 	/* Synchronous suspends are not allowed in the RPM_RESUMING state. */	else if (dev->power.runtime_status == RPM_RESUMING &&	    !(rpmflags & RPM_ASYNC))		retval = -EAGAIN;	if (retval)		goto out; 	/* If the autosuspend_delay time hasn't expired yet, reschedule. */	if ((rpmflags & RPM_AUTO)	    && dev->power.runtime_status != RPM_SUSPENDING) {		unsigned long expires = pm_runtime_autosuspend_expiration(dev); 		if (expires != 0) {			/* Pending requests need to be canceled. */			dev->power.request = RPM_REQ_NONE; 			/*			 * Optimization: If the timer is already running and is			 * set to expire at or before the autosuspend delay,			 * avoid the overhead of resetting it.  Just let it			 * expire; pm_suspend_timer_fn() will take care of the			 * rest.			 */			if (!(dev->power.timer_expires && time_before_eq(			    dev->power.timer_expires, expires))) {				dev->power.timer_expires = expires;				mod_timer(&dev->power.suspend_timer, expires);			}			dev->power.timer_autosuspends = 1;			goto out;		}	} 	/* Other scheduled or pending requests need to be canceled. */	pm_runtime_cancel_pending(dev); 	if (dev->power.runtime_status == RPM_SUSPENDING) {		DEFINE_WAIT(wait); 		if (rpmflags & (RPM_ASYNC | RPM_NOWAIT)) {			retval = -EINPROGRESS;			goto out;		} 		if (dev->power.irq_safe) {			spin_unlock(&dev->power.lock); 			cpu_relax(); 			spin_lock(&dev->power.lock);			goto repeat;		} 		/* Wait for the other suspend running in parallel with us. */		for (;;) {			prepare_to_wait(&dev->power.wait_queue, &wait,					TASK_UNINTERRUPTIBLE);			if (dev->power.runtime_status != RPM_SUSPENDING)				break; 			spin_unlock_irq(&dev->power.lock); 			schedule(); 			spin_lock_irq(&dev->power.lock);		}		finish_wait(&dev->power.wait_queue, &wait);		goto repeat;	} 	dev->power.deferred_resume = false;	if (dev->power.no_callbacks)		goto no_callback;	/* Assume success. */ 	/* Carry out an asynchronous or a synchronous suspend. */	if (rpmflags & RPM_ASYNC) {		dev->power.request = (rpmflags & RPM_AUTO) ?		    RPM_REQ_AUTOSUSPEND : RPM_REQ_SUSPEND;		if (!dev->power.request_pending) {			dev->power.request_pending = true;			queue_work(pm_wq, &dev->power.work);		}		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)		callback = dev->type->pm->runtime_suspend;	else if (dev->class && dev->class->pm)		callback = dev->class->pm->runtime_suspend;	else if (dev->bus && dev->bus->pm)		callback = dev->bus->pm->runtime_suspend;	else		callback = NULL; 	if (!callback && dev->driver && dev->driver->pm)		callback = dev->driver->pm->runtime_suspend; 	retval = rpm_callback(callback, dev);	if (retval)		goto fail;  no_callback:	__update_runtime_status(dev, RPM_SUSPENDED);	pm_runtime_deactivate_timer(dev);

Lines 1–500 of 1415.