1240 lines · 33.8 KB · C
RawDownload
/* * drivers/base/power/runtime.c - Helper functions for device run-time 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/pm_runtime.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;	int delta; 	delta = now - dev->power.accounting_timestamp; 	if (delta < 0)		delta = 0; 	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 (atomic_read(&dev->power.usage_count) > 0	    || dev->power.disable_depth > 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_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 run-time 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; 	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->pwr_domain)		callback = dev->pwr_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) {		spin_unlock_irq(&dev->power.lock); 		callback(dev); 		spin_lock_irq(&dev->power.lock);	} 	dev->power.idle_notification = false;	wake_up_all(&dev->power.wait_queue);  out:	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 (!cb)		return -ENOSYS; 	if (dev->power.irq_safe) {		retval = cb(dev);	} else {		spin_unlock_irq(&dev->power.lock); 		retval = cb(dev); 		spin_lock_irq(&dev->power.lock);	}	dev->power.runtime_error = retval;	return retval;} /** * rpm_suspend - Carry out run-time suspend of given device. * @dev: Device to suspend. * @rpmflags: Flag bits. * * Check if the device's run-time PM status allows it to be suspended.  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.  Cancel a * pending idle notification.  If the RPM_ASYNC flag is set then queue a * suspend request; otherwise run the ->runtime_suspend() callback directly. * If a deferred resume was requested while the callback was running then carry * it out; otherwise send an idle notification for the device (if the suspend * failed) or for its parent (if the suspend succeeded). * * 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;	int retval; 	dev_dbg(dev, "%s flags 0x%x\n", __func__, 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;		} 		/* 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;	} 	__update_runtime_status(dev, RPM_SUSPENDING); 	if (dev->pwr_domain)		callback = dev->pwr_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; 	retval = rpm_callback(callback, dev);	if (retval) {		__update_runtime_status(dev, RPM_ACTIVE);		dev->power.deferred_resume = 0;		if (retval == -EAGAIN || retval == -EBUSY)			dev->power.runtime_error = 0;		else			pm_runtime_cancel_pending(dev);	} else { no_callback:		__update_runtime_status(dev, RPM_SUSPENDED);		pm_runtime_deactivate_timer(dev); 		if (dev->parent) {			parent = dev->parent;			atomic_add_unless(&parent->power.child_count, -1, 0);		}	}	wake_up_all(&dev->power.wait_queue); 	if (dev->power.deferred_resume) {		rpm_resume(dev, 0);		retval = -EAGAIN;		goto out;	} 	/* Maybe the parent is now able to suspend. */	if (parent && !parent->power.ignore_children && !dev->power.irq_safe) {		spin_unlock(&dev->power.lock); 		spin_lock(&parent->power.lock);		rpm_idle(parent, RPM_ASYNC);		spin_unlock(&parent->power.lock); 		spin_lock(&dev->power.lock);	}  out:	dev_dbg(dev, "%s returns %d\n", __func__, retval); 	return retval;} /** * rpm_resume - Carry out run-time resume of given device. * @dev: Device to resume. * @rpmflags: Flag bits. * * Check if the device's run-time PM status allows it to be resumed.  Cancel * any scheduled or pending requests.  If another resume has been started * earlier, either return immediately or wait for it to finish, depending on the * RPM_NOWAIT and RPM_ASYNC flags.  Similarly, if there's a suspend running in * parallel with this function, either tell the other process to resume after * suspending (deferred_resume) or wait for it to finish.  If the RPM_ASYNC * flag is set then queue a resume request; otherwise run the * ->runtime_resume() callback directly.  Queue an idle notification for the * device if the resume succeeded. * * This function must be called under dev->power.lock with interrupts disabled. */static int rpm_resume(struct device *dev, int rpmflags)	__releases(&dev->power.lock) __acquires(&dev->power.lock){	int (*callback)(struct device *);	struct device *parent = NULL;	int retval = 0; 	dev_dbg(dev, "%s flags 0x%x\n", __func__, rpmflags);  repeat:	if (dev->power.runtime_error)		retval = -EINVAL;	else if (dev->power.disable_depth > 0)		retval = -EAGAIN;	if (retval)		goto out; 	/*	 * Other scheduled or pending requests need to be canceled.  Small	 * optimization: If an autosuspend timer is running, leave it running	 * rather than cancelling it now only to restart it again in the near	 * future.	 */	dev->power.request = RPM_REQ_NONE;	if (!dev->power.timer_autosuspends)		pm_runtime_deactivate_timer(dev); 	if (dev->power.runtime_status == RPM_ACTIVE) {		retval = 1;		goto out;	} 	if (dev->power.runtime_status == RPM_RESUMING	    || dev->power.runtime_status == RPM_SUSPENDING) {		DEFINE_WAIT(wait); 		if (rpmflags & (RPM_ASYNC | RPM_NOWAIT)) {			if (dev->power.runtime_status == RPM_SUSPENDING)				dev->power.deferred_resume = true;			else				retval = -EINPROGRESS;			goto out;		} 		/* Wait for the operation carried out in parallel with us. */		for (;;) {			prepare_to_wait(&dev->power.wait_queue, &wait,					TASK_UNINTERRUPTIBLE);			if (dev->power.runtime_status != RPM_RESUMING			    && dev->power.runtime_status != RPM_SUSPENDING)				break; 			spin_unlock_irq(&dev->power.lock);

Lines 1–500 of 1240.