diff options
Diffstat (limited to 'drivers/base/power/main.c')
| -rw-r--r-- | drivers/base/power/main.c | 73 | 
1 files changed, 73 insertions, 0 deletions
| diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 9f098a82cf04..ee039afe9078 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -30,6 +30,8 @@  #include <linux/suspend.h>  #include <trace/events/power.h>  #include <linux/cpuidle.h> +#include <linux/timer.h> +  #include "../base.h"  #include "power.h" @@ -390,6 +392,71 @@ static int dpm_run_callback(pm_callback_t cb, struct device *dev,  	return error;  } +#ifdef CONFIG_DPM_WATCHDOG +struct dpm_watchdog { +	struct device		*dev; +	struct task_struct	*tsk; +	struct timer_list	timer; +}; + +#define DECLARE_DPM_WATCHDOG_ON_STACK(wd) \ +	struct dpm_watchdog wd + +/** + * dpm_watchdog_handler - Driver suspend / resume watchdog handler. + * @data: Watchdog object address. + * + * Called when a driver has timed out suspending or resuming. + * There's not much we can do here to recover so panic() to + * capture a crash-dump in pstore. + */ +static void dpm_watchdog_handler(unsigned long data) +{ +	struct dpm_watchdog *wd = (void *)data; + +	dev_emerg(wd->dev, "**** DPM device timeout ****\n"); +	show_stack(wd->tsk, NULL); +	panic("%s %s: unrecoverable failure\n", +		dev_driver_string(wd->dev), dev_name(wd->dev)); +} + +/** + * dpm_watchdog_set - Enable pm watchdog for given device. + * @wd: Watchdog. Must be allocated on the stack. + * @dev: Device to handle. + */ +static void dpm_watchdog_set(struct dpm_watchdog *wd, struct device *dev) +{ +	struct timer_list *timer = &wd->timer; + +	wd->dev = dev; +	wd->tsk = current; + +	init_timer_on_stack(timer); +	/* use same timeout value for both suspend and resume */ +	timer->expires = jiffies + HZ * CONFIG_DPM_WATCHDOG_TIMEOUT; +	timer->function = dpm_watchdog_handler; +	timer->data = (unsigned long)wd; +	add_timer(timer); +} + +/** + * dpm_watchdog_clear - Disable suspend/resume watchdog. + * @wd: Watchdog to disable. + */ +static void dpm_watchdog_clear(struct dpm_watchdog *wd) +{ +	struct timer_list *timer = &wd->timer; + +	del_timer_sync(timer); +	destroy_timer_on_stack(timer); +} +#else +#define DECLARE_DPM_WATCHDOG_ON_STACK(wd) +#define dpm_watchdog_set(x, y) +#define dpm_watchdog_clear(x) +#endif +  /*------------------------- Resume routines -------------------------*/  /** @@ -576,6 +643,7 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)  	pm_callback_t callback = NULL;  	char *info = NULL;  	int error = 0; +	DECLARE_DPM_WATCHDOG_ON_STACK(wd);  	TRACE_DEVICE(dev);  	TRACE_RESUME(0); @@ -584,6 +652,7 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)  		goto Complete;  	dpm_wait(dev->parent, async); +	dpm_watchdog_set(&wd, dev);  	device_lock(dev);  	/* @@ -642,6 +711,7 @@ static int device_resume(struct device *dev, pm_message_t state, bool async)   Unlock:  	device_unlock(dev); +	dpm_watchdog_clear(&wd);   Complete:  	complete_all(&dev->power.completion); @@ -1060,6 +1130,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)  	pm_callback_t callback = NULL;  	char *info = NULL;  	int error = 0; +	DECLARE_DPM_WATCHDOG_ON_STACK(wd);  	dpm_wait_for_children(dev, async); @@ -1083,6 +1154,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)  	if (dev->power.syscore)  		goto Complete; +	dpm_watchdog_set(&wd, dev);  	device_lock(dev);  	if (dev->pm_domain) { @@ -1139,6 +1211,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)  	}  	device_unlock(dev); +	dpm_watchdog_clear(&wd);   Complete:  	complete_all(&dev->power.completion); |