PM: Change dpm watchdog to support async suspend
[linux-2.6.git] / drivers / base / power / main.c
index 1caaec9..18d6662 100644 (file)
@@ -51,11 +51,10 @@ static DEFINE_MUTEX(dpm_list_mtx);
 static pm_message_t pm_transition;
 
 static void dpm_drv_timeout(unsigned long data);
-static DEFINE_TIMER(dpm_drv_wd, dpm_drv_timeout, 0, 0);
-static struct {
+struct dpm_drv_wd_data {
        struct device *dev;
        struct task_struct *tsk;
-} dpm_drv_wd_data;
+};
 
 static int async_error;
 
@@ -611,8 +610,9 @@ static bool is_async(struct device *dev)
  */
 static void dpm_drv_timeout(unsigned long data)
 {
-       struct device *dev = dpm_drv_wd_data.dev;
-       struct task_struct *tsk = dpm_drv_wd_data.tsk;
+       struct dpm_drv_wd_data *wd_data = (void *)data;
+       struct device *dev = wd_data->dev;
+       struct task_struct *tsk = wd_data->tsk;
 
        printk(KERN_EMERG "**** DPM device timeout: %s (%s)\n", dev_name(dev),
               (dev->driver ? dev->driver->name : "no driver"));
@@ -624,29 +624,6 @@ static void dpm_drv_timeout(unsigned long data)
 }
 
 /**
- *     dpm_drv_wdset - Sets up driver suspend/resume watchdog timer.
- *     @dev: struct device which we're guarding.
- *
- */
-static void dpm_drv_wdset(struct device *dev)
-{
-       dpm_drv_wd_data.dev = dev;
-       dpm_drv_wd_data.tsk = get_current();
-       dpm_drv_wd.data = (unsigned long) &dpm_drv_wd_data;
-       mod_timer(&dpm_drv_wd, jiffies + (HZ * 3));
-}
-
-/**
- *     dpm_drv_wdclr - clears driver suspend/resume watchdog timer.
- *     @dev: struct device which we're no longer guarding.
- *
- */
-static void dpm_drv_wdclr(struct device *dev)
-{
-       del_timer_sync(&dpm_drv_wd);
-}
-
-/**
  * dpm_resume - Execute "resume" callbacks for non-sysdev devices.
  * @state: PM transition of the system being carried out.
  *
@@ -904,9 +881,19 @@ static int legacy_suspend(struct device *dev, pm_message_t state,
 static int __device_suspend(struct device *dev, pm_message_t state, bool async)
 {
        int error = 0;
+       struct timer_list timer;
+       struct dpm_drv_wd_data data;
 
        dpm_wait_for_children(dev, async);
 
+       data.dev = dev;
+       data.tsk = get_current();
+       init_timer_on_stack(&timer);
+       timer.expires = jiffies + HZ * 3;
+       timer.function = dpm_drv_timeout;
+       timer.data = (unsigned long)&data;
+       add_timer(&timer);
+
        if (async_error)
                return 0;
 
@@ -960,6 +947,10 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
        dev->power.is_suspended = !error;
 
        device_unlock(dev);
+
+       del_timer_sync(&timer);
+       destroy_timer_on_stack(&timer);
+
        complete_all(&dev->power.completion);
 
        if (error) {
@@ -1017,9 +1008,7 @@ int dpm_suspend(pm_message_t state)
                get_device(dev);
                mutex_unlock(&dpm_list_mtx);
 
-               dpm_drv_wdset(dev);
                error = device_suspend(dev);
-               dpm_drv_wdclr(dev);
 
                mutex_lock(&dpm_list_mtx);
                if (error) {