diff options
Diffstat (limited to 'drivers/base/power/main.c')
| -rw-r--r-- | drivers/base/power/main.c | 174 | 
1 files changed, 62 insertions, 112 deletions
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index fa5d98ee590..83404973f97 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -26,6 +26,7 @@  #include <linux/interrupt.h>  #include <linux/sched.h>  #include <linux/async.h> +#include <linux/suspend.h>  #include "../base.h"  #include "power.h" @@ -41,16 +42,13 @@   */  LIST_HEAD(dpm_list); +LIST_HEAD(dpm_prepared_list); +LIST_HEAD(dpm_suspended_list); +LIST_HEAD(dpm_noirq_list);  static DEFINE_MUTEX(dpm_list_mtx);  static pm_message_t pm_transition; -/* - * Set once the preparation of devices for a PM transition has started, reset - * before starting to resume devices.  Protected by dpm_list_mtx. - */ -static bool transition_started; -  static int async_error;  /** @@ -59,7 +57,7 @@ static int async_error;   */  void device_pm_init(struct device *dev)  { -	dev->power.status = DPM_ON; +	dev->power.in_suspend = false;  	init_completion(&dev->power.completion);  	complete_all(&dev->power.completion);  	dev->power.wakeup = NULL; @@ -90,22 +88,11 @@ void device_pm_unlock(void)  void device_pm_add(struct device *dev)  {  	pr_debug("PM: Adding info for %s:%s\n", -		 dev->bus ? dev->bus->name : "No Bus", -		 kobject_name(&dev->kobj)); +		 dev->bus ? dev->bus->name : "No Bus", dev_name(dev));  	mutex_lock(&dpm_list_mtx); -	if (dev->parent) { -		if (dev->parent->power.status >= DPM_SUSPENDING) -			dev_warn(dev, "parent %s should not be sleeping\n", -				 dev_name(dev->parent)); -	} else if (transition_started) { -		/* -		 * We refuse to register parentless devices while a PM -		 * transition is in progress in order to avoid leaving them -		 * unhandled down the road -		 */ -		dev_WARN(dev, "Parentless device registered during a PM transaction\n"); -	} - +	if (dev->parent && dev->parent->power.in_suspend) +		dev_warn(dev, "parent %s should not be sleeping\n", +			dev_name(dev->parent));  	list_add_tail(&dev->power.entry, &dpm_list);  	mutex_unlock(&dpm_list_mtx);  } @@ -117,8 +104,7 @@ void device_pm_add(struct device *dev)  void device_pm_remove(struct device *dev)  {  	pr_debug("PM: Removing info for %s:%s\n", -		 dev->bus ? dev->bus->name : "No Bus", -		 kobject_name(&dev->kobj)); +		 dev->bus ? dev->bus->name : "No Bus", dev_name(dev));  	complete_all(&dev->power.completion);  	mutex_lock(&dpm_list_mtx);  	list_del_init(&dev->power.entry); @@ -135,10 +121,8 @@ void device_pm_remove(struct device *dev)  void device_pm_move_before(struct device *deva, struct device *devb)  {  	pr_debug("PM: Moving %s:%s before %s:%s\n", -		 deva->bus ? deva->bus->name : "No Bus", -		 kobject_name(&deva->kobj), -		 devb->bus ? devb->bus->name : "No Bus", -		 kobject_name(&devb->kobj)); +		 deva->bus ? deva->bus->name : "No Bus", dev_name(deva), +		 devb->bus ? devb->bus->name : "No Bus", dev_name(devb));  	/* Delete deva from dpm_list and reinsert before devb. */  	list_move_tail(&deva->power.entry, &devb->power.entry);  } @@ -151,10 +135,8 @@ void device_pm_move_before(struct device *deva, struct device *devb)  void device_pm_move_after(struct device *deva, struct device *devb)  {  	pr_debug("PM: Moving %s:%s after %s:%s\n", -		 deva->bus ? deva->bus->name : "No Bus", -		 kobject_name(&deva->kobj), -		 devb->bus ? devb->bus->name : "No Bus", -		 kobject_name(&devb->kobj)); +		 deva->bus ? deva->bus->name : "No Bus", dev_name(deva), +		 devb->bus ? devb->bus->name : "No Bus", dev_name(devb));  	/* Delete deva from dpm_list and reinsert after devb. */  	list_move(&deva->power.entry, &devb->power.entry);  } @@ -166,8 +148,7 @@ void device_pm_move_after(struct device *deva, struct device *devb)  void device_pm_move_last(struct device *dev)  {  	pr_debug("PM: Moving %s:%s to end of list\n", -		 dev->bus ? dev->bus->name : "No Bus", -		 kobject_name(&dev->kobj)); +		 dev->bus ? dev->bus->name : "No Bus", dev_name(dev));  	list_move_tail(&dev->power.entry, &dpm_list);  } @@ -303,7 +284,7 @@ static int pm_noirq_op(struct device *dev,  			pm_message_t state)  {  	int error = 0; -	ktime_t calltime, delta, rettime; +	ktime_t calltime = ktime_set(0, 0), delta, rettime;  	if (initcall_debug) {  		pr_info("calling  %s+ @ %i, parent: %s\n", @@ -405,7 +386,7 @@ static void pm_dev_err(struct device *dev, pm_message_t state, char *info,  			int error)  {  	printk(KERN_ERR "PM: Device %s failed to %s%s: error %d\n", -		kobject_name(&dev->kobj), pm_verb(state.event), info, error); +		dev_name(dev), pm_verb(state.event), info, error);  }  static void dpm_show_time(ktime_t starttime, pm_message_t state, char *info) @@ -475,33 +456,24 @@ End:   */  void dpm_resume_noirq(pm_message_t state)  { -	struct list_head list;  	ktime_t starttime = ktime_get(); -	INIT_LIST_HEAD(&list);  	mutex_lock(&dpm_list_mtx); -	transition_started = false; -	while (!list_empty(&dpm_list)) { -		struct device *dev = to_device(dpm_list.next); +	while (!list_empty(&dpm_noirq_list)) { +		struct device *dev = to_device(dpm_noirq_list.next); +		int error;  		get_device(dev); -		if (dev->power.status > DPM_OFF) { -			int error; - -			dev->power.status = DPM_OFF; -			mutex_unlock(&dpm_list_mtx); +		list_move_tail(&dev->power.entry, &dpm_suspended_list); +		mutex_unlock(&dpm_list_mtx); -			error = device_resume_noirq(dev, state); +		error = device_resume_noirq(dev, state); +		if (error) +			pm_dev_err(dev, state, " early", error); -			mutex_lock(&dpm_list_mtx); -			if (error) -				pm_dev_err(dev, state, " early", error); -		} -		if (!list_empty(&dev->power.entry)) -			list_move_tail(&dev->power.entry, &list); +		mutex_lock(&dpm_list_mtx);  		put_device(dev);  	} -	list_splice(&list, &dpm_list);  	mutex_unlock(&dpm_list_mtx);  	dpm_show_time(starttime, state, "early");  	resume_device_irqs(); @@ -544,7 +516,7 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)  	dpm_wait(dev->parent, async);  	device_lock(dev); -	dev->power.status = DPM_RESUMING; +	dev->power.in_suspend = false;  	if (dev->bus) {  		if (dev->bus->pm) { @@ -610,19 +582,14 @@ static bool is_async(struct device *dev)   */  static void dpm_resume(pm_message_t state)  { -	struct list_head list;  	struct device *dev;  	ktime_t starttime = ktime_get(); -	INIT_LIST_HEAD(&list);  	mutex_lock(&dpm_list_mtx);  	pm_transition = state;  	async_error = 0; -	list_for_each_entry(dev, &dpm_list, power.entry) { -		if (dev->power.status < DPM_OFF) -			continue; - +	list_for_each_entry(dev, &dpm_suspended_list, power.entry) {  		INIT_COMPLETION(dev->power.completion);  		if (is_async(dev)) {  			get_device(dev); @@ -630,28 +597,24 @@ static void dpm_resume(pm_message_t state)  		}  	} -	while (!list_empty(&dpm_list)) { -		dev = to_device(dpm_list.next); +	while (!list_empty(&dpm_suspended_list)) { +		dev = to_device(dpm_suspended_list.next);  		get_device(dev); -		if (dev->power.status >= DPM_OFF && !is_async(dev)) { +		if (!is_async(dev)) {  			int error;  			mutex_unlock(&dpm_list_mtx);  			error = device_resume(dev, state, false); - -			mutex_lock(&dpm_list_mtx);  			if (error)  				pm_dev_err(dev, state, "", error); -		} else if (dev->power.status == DPM_SUSPENDING) { -			/* Allow new children of the device to be registered */ -			dev->power.status = DPM_RESUMING; + +			mutex_lock(&dpm_list_mtx);  		}  		if (!list_empty(&dev->power.entry)) -			list_move_tail(&dev->power.entry, &list); +			list_move_tail(&dev->power.entry, &dpm_prepared_list);  		put_device(dev);  	} -	list_splice(&list, &dpm_list);  	mutex_unlock(&dpm_list_mtx);  	async_synchronize_full();  	dpm_show_time(starttime, state, NULL); @@ -697,22 +660,18 @@ static void dpm_complete(pm_message_t state)  	INIT_LIST_HEAD(&list);  	mutex_lock(&dpm_list_mtx); -	transition_started = false; -	while (!list_empty(&dpm_list)) { -		struct device *dev = to_device(dpm_list.prev); +	while (!list_empty(&dpm_prepared_list)) { +		struct device *dev = to_device(dpm_prepared_list.prev);  		get_device(dev); -		if (dev->power.status > DPM_ON) { -			dev->power.status = DPM_ON; -			mutex_unlock(&dpm_list_mtx); +		dev->power.in_suspend = false; +		list_move(&dev->power.entry, &list); +		mutex_unlock(&dpm_list_mtx); -			device_complete(dev, state); -			pm_runtime_put_sync(dev); +		device_complete(dev, state); +		pm_runtime_put_sync(dev); -			mutex_lock(&dpm_list_mtx); -		} -		if (!list_empty(&dev->power.entry)) -			list_move(&dev->power.entry, &list); +		mutex_lock(&dpm_list_mtx);  		put_device(dev);  	}  	list_splice(&list, &dpm_list); @@ -802,15 +761,13 @@ End:   */  int dpm_suspend_noirq(pm_message_t state)  { -	struct list_head list;  	ktime_t starttime = ktime_get();  	int error = 0; -	INIT_LIST_HEAD(&list);  	suspend_device_irqs();  	mutex_lock(&dpm_list_mtx); -	while (!list_empty(&dpm_list)) { -		struct device *dev = to_device(dpm_list.prev); +	while (!list_empty(&dpm_suspended_list)) { +		struct device *dev = to_device(dpm_suspended_list.prev);  		get_device(dev);  		mutex_unlock(&dpm_list_mtx); @@ -823,12 +780,10 @@ int dpm_suspend_noirq(pm_message_t state)  			put_device(dev);  			break;  		} -		dev->power.status = DPM_OFF_IRQ;  		if (!list_empty(&dev->power.entry)) -			list_move(&dev->power.entry, &list); +			list_move(&dev->power.entry, &dpm_noirq_list);  		put_device(dev);  	} -	list_splice_tail(&list, &dpm_list);  	mutex_unlock(&dpm_list_mtx);  	if (error)  		dpm_resume_noirq(resume_event(state)); @@ -876,6 +831,11 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)  	if (async_error)  		goto End; +	if (pm_wakeup_pending()) { +		async_error = -EBUSY; +		goto End; +	} +  	if (dev->class) {  		if (dev->class->pm) {  			pm_dev_dbg(dev, state, "class "); @@ -907,9 +867,6 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)  		}  	} -	if (!error) -		dev->power.status = DPM_OFF; -   End:  	device_unlock(dev);  	complete_all(&dev->power.completion); @@ -951,16 +908,14 @@ static int device_suspend(struct device *dev)   */  static int dpm_suspend(pm_message_t state)  { -	struct list_head list;  	ktime_t starttime = ktime_get();  	int error = 0; -	INIT_LIST_HEAD(&list);  	mutex_lock(&dpm_list_mtx);  	pm_transition = state;  	async_error = 0; -	while (!list_empty(&dpm_list)) { -		struct device *dev = to_device(dpm_list.prev); +	while (!list_empty(&dpm_prepared_list)) { +		struct device *dev = to_device(dpm_prepared_list.prev);  		get_device(dev);  		mutex_unlock(&dpm_list_mtx); @@ -974,12 +929,11 @@ static int dpm_suspend(pm_message_t state)  			break;  		}  		if (!list_empty(&dev->power.entry)) -			list_move(&dev->power.entry, &list); +			list_move(&dev->power.entry, &dpm_suspended_list);  		put_device(dev);  		if (async_error)  			break;  	} -	list_splice(&list, dpm_list.prev);  	mutex_unlock(&dpm_list_mtx);  	async_synchronize_full();  	if (!error) @@ -1038,22 +992,20 @@ static int device_prepare(struct device *dev, pm_message_t state)   */  static int dpm_prepare(pm_message_t state)  { -	struct list_head list;  	int error = 0; -	INIT_LIST_HEAD(&list);  	mutex_lock(&dpm_list_mtx); -	transition_started = true;  	while (!list_empty(&dpm_list)) {  		struct device *dev = to_device(dpm_list.next);  		get_device(dev); -		dev->power.status = DPM_PREPARING;  		mutex_unlock(&dpm_list_mtx);  		pm_runtime_get_noresume(dev); -		if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) { -			/* Wake-up requested during system sleep transition. */ +		if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) +			pm_wakeup_event(dev, 0); + +		if (pm_wakeup_pending()) {  			pm_runtime_put_sync(dev);  			error = -EBUSY;  		} else { @@ -1062,24 +1014,22 @@ static int dpm_prepare(pm_message_t state)  		mutex_lock(&dpm_list_mtx);  		if (error) { -			dev->power.status = DPM_ON;  			if (error == -EAGAIN) {  				put_device(dev);  				error = 0;  				continue;  			} -			printk(KERN_ERR "PM: Failed to prepare device %s " -				"for power transition: error %d\n", -				kobject_name(&dev->kobj), error); +			printk(KERN_INFO "PM: Device %s not prepared " +				"for power transition: code %d\n", +				dev_name(dev), error);  			put_device(dev);  			break;  		} -		dev->power.status = DPM_SUSPENDING; +		dev->power.in_suspend = true;  		if (!list_empty(&dev->power.entry)) -			list_move_tail(&dev->power.entry, &list); +			list_move_tail(&dev->power.entry, &dpm_prepared_list);  		put_device(dev);  	} -	list_splice(&list, &dpm_list);  	mutex_unlock(&dpm_list_mtx);  	return error;  }  |