]> Pileus Git - ~andy/linux/blobdiff - drivers/scsi/scsi_pm.c
Merge branch 'for-3.8/drivers' of git://git.kernel.dk/linux-block
[~andy/linux] / drivers / scsi / scsi_pm.c
index dc0ad85853e20e30f90ae0045f20f789e4585599..8f6b12cbd224801e2828571beb6938808d87b26c 100644 (file)
 
 #include "scsi_priv.h"
 
-static int scsi_dev_type_suspend(struct device *dev, pm_message_t msg)
+static int scsi_dev_type_suspend(struct device *dev, int (*cb)(struct device *))
 {
-       struct device_driver *drv;
        int err;
 
        err = scsi_device_quiesce(to_scsi_device(dev));
        if (err == 0) {
-               drv = dev->driver;
-               if (drv && drv->suspend) {
-                       err = drv->suspend(dev, msg);
+               if (cb) {
+                       err = cb(dev);
                        if (err)
                                scsi_device_resume(to_scsi_device(dev));
                }
@@ -34,14 +32,12 @@ static int scsi_dev_type_suspend(struct device *dev, pm_message_t msg)
        return err;
 }
 
-static int scsi_dev_type_resume(struct device *dev)
+static int scsi_dev_type_resume(struct device *dev, int (*cb)(struct device *))
 {
-       struct device_driver *drv;
        int err = 0;
 
-       drv = dev->driver;
-       if (drv && drv->resume)
-               err = drv->resume(dev);
+       if (cb)
+               err = cb(dev);
        scsi_device_resume(to_scsi_device(dev));
        dev_dbg(dev, "scsi resume: %d\n", err);
        return err;
@@ -49,51 +45,39 @@ static int scsi_dev_type_resume(struct device *dev)
 
 #ifdef CONFIG_PM_SLEEP
 
-static int scsi_bus_suspend_common(struct device *dev, pm_message_t msg)
+static int
+scsi_bus_suspend_common(struct device *dev, int (*cb)(struct device *))
 {
        int err = 0;
 
        if (scsi_is_sdev_device(dev)) {
                /*
-                * sd is the only high-level SCSI driver to implement runtime
-                * PM, and sd treats runtime suspend, system suspend, and
-                * system hibernate identically (but not system freeze).
+                * All the high-level SCSI drivers that implement runtime
+                * PM treat runtime suspend, system suspend, and system
+                * hibernate identically.
                 */
-               if (pm_runtime_suspended(dev)) {
-                       if (msg.event == PM_EVENT_SUSPEND ||
-                           msg.event == PM_EVENT_HIBERNATE)
-                               return 0;       /* already suspended */
+               if (pm_runtime_suspended(dev))
+                       return 0;
 
-                       /* wake up device so that FREEZE will succeed */
-                       pm_runtime_resume(dev);
-               }
-               err = scsi_dev_type_suspend(dev, msg);
+               err = scsi_dev_type_suspend(dev, cb);
        }
+
        return err;
 }
 
-static int scsi_bus_resume_common(struct device *dev)
+static int
+scsi_bus_resume_common(struct device *dev, int (*cb)(struct device *))
 {
        int err = 0;
 
-       /*
-        * Parent device may have runtime suspended as soon as
-        * it is woken up during the system resume.
-        *
-        * Resume it on behalf of child.
-        */
-       pm_runtime_get_sync(dev->parent);
-
        if (scsi_is_sdev_device(dev))
-               err = scsi_dev_type_resume(dev);
+               err = scsi_dev_type_resume(dev, cb);
+
        if (err == 0) {
                pm_runtime_disable(dev);
                pm_runtime_set_active(dev);
                pm_runtime_enable(dev);
        }
-
-       pm_runtime_put_sync(dev->parent);
-
        return err;
 }
 
@@ -112,26 +96,49 @@ static int scsi_bus_prepare(struct device *dev)
 
 static int scsi_bus_suspend(struct device *dev)
 {
-       return scsi_bus_suspend_common(dev, PMSG_SUSPEND);
+       const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+       return scsi_bus_suspend_common(dev, pm ? pm->suspend : NULL);
+}
+
+static int scsi_bus_resume(struct device *dev)
+{
+       const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+       return scsi_bus_resume_common(dev, pm ? pm->resume : NULL);
 }
 
 static int scsi_bus_freeze(struct device *dev)
 {
-       return scsi_bus_suspend_common(dev, PMSG_FREEZE);
+       const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+       return scsi_bus_suspend_common(dev, pm ? pm->freeze : NULL);
+}
+
+static int scsi_bus_thaw(struct device *dev)
+{
+       const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+       return scsi_bus_resume_common(dev, pm ? pm->thaw : NULL);
 }
 
 static int scsi_bus_poweroff(struct device *dev)
 {
-       return scsi_bus_suspend_common(dev, PMSG_HIBERNATE);
+       const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+       return scsi_bus_suspend_common(dev, pm ? pm->poweroff : NULL);
+}
+
+static int scsi_bus_restore(struct device *dev)
+{
+       const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+       return scsi_bus_resume_common(dev, pm ? pm->restore : NULL);
 }
 
 #else /* CONFIG_PM_SLEEP */
 
-#define scsi_bus_resume_common         NULL
 #define scsi_bus_prepare               NULL
 #define scsi_bus_suspend               NULL
+#define scsi_bus_resume                        NULL
 #define scsi_bus_freeze                        NULL
+#define scsi_bus_thaw                  NULL
 #define scsi_bus_poweroff              NULL
+#define scsi_bus_restore               NULL
 
 #endif /* CONFIG_PM_SLEEP */
 
@@ -140,10 +147,12 @@ static int scsi_bus_poweroff(struct device *dev)
 static int scsi_runtime_suspend(struct device *dev)
 {
        int err = 0;
+       const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
 
        dev_dbg(dev, "scsi_runtime_suspend\n");
        if (scsi_is_sdev_device(dev)) {
-               err = scsi_dev_type_suspend(dev, PMSG_AUTO_SUSPEND);
+               err = scsi_dev_type_suspend(dev,
+                               pm ? pm->runtime_suspend : NULL);
                if (err == -EAGAIN)
                        pm_schedule_suspend(dev, jiffies_to_msecs(
                                round_jiffies_up_relative(HZ/10)));
@@ -157,10 +166,11 @@ static int scsi_runtime_suspend(struct device *dev)
 static int scsi_runtime_resume(struct device *dev)
 {
        int err = 0;
+       const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
 
        dev_dbg(dev, "scsi_runtime_resume\n");
        if (scsi_is_sdev_device(dev))
-               err = scsi_dev_type_resume(dev);
+               err = scsi_dev_type_resume(dev, pm ? pm->runtime_resume : NULL);
 
        /* Insert hooks here for targets, hosts, and transport classes */
 
@@ -239,11 +249,11 @@ void scsi_autopm_put_host(struct Scsi_Host *shost)
 const struct dev_pm_ops scsi_bus_pm_ops = {
        .prepare =              scsi_bus_prepare,
        .suspend =              scsi_bus_suspend,
-       .resume =               scsi_bus_resume_common,
+       .resume =               scsi_bus_resume,
        .freeze =               scsi_bus_freeze,
-       .thaw =                 scsi_bus_resume_common,
+       .thaw =                 scsi_bus_thaw,
        .poweroff =             scsi_bus_poweroff,
-       .restore =              scsi_bus_resume_common,
+       .restore =              scsi_bus_restore,
        .runtime_suspend =      scsi_runtime_suspend,
        .runtime_resume =       scsi_runtime_resume,
        .runtime_idle =         scsi_runtime_idle,