]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/scsi/scsi_pm.c
[SCSI] implement runtime Power Management
[karo-tx-linux.git] / drivers / scsi / scsi_pm.c
index cd83758ce0a2695ab62669d6379b1248ec59230a..d70e91ae60af199d8e64a027fa4382026e553f90 100644 (file)
@@ -59,6 +59,12 @@ static int scsi_bus_resume_common(struct device *dev)
 
        if (scsi_is_sdev_device(dev))
                err = scsi_dev_type_resume(dev);
+
+       if (err == 0) {
+               pm_runtime_disable(dev);
+               pm_runtime_set_active(dev);
+               pm_runtime_enable(dev);
+       }
        return err;
 }
 
@@ -86,6 +92,107 @@ static int scsi_bus_poweroff(struct device *dev)
 
 #endif /* CONFIG_PM_SLEEP */
 
+#ifdef CONFIG_PM_RUNTIME
+
+static int scsi_runtime_suspend(struct device *dev)
+{
+       int err = 0;
+
+       dev_dbg(dev, "scsi_runtime_suspend\n");
+       if (scsi_is_sdev_device(dev)) {
+               err = scsi_dev_type_suspend(dev, PMSG_AUTO_SUSPEND);
+               if (err == -EAGAIN)
+                       pm_schedule_suspend(dev, jiffies_to_msecs(
+                               round_jiffies_up_relative(HZ/10)));
+       }
+
+       /* Insert hooks here for targets, hosts, and transport classes */
+
+       return err;
+}
+
+static int scsi_runtime_resume(struct device *dev)
+{
+       int err = 0;
+
+       dev_dbg(dev, "scsi_runtime_resume\n");
+       if (scsi_is_sdev_device(dev))
+               err = scsi_dev_type_resume(dev);
+
+       /* Insert hooks here for targets, hosts, and transport classes */
+
+       return err;
+}
+
+static int scsi_runtime_idle(struct device *dev)
+{
+       int err;
+
+       dev_dbg(dev, "scsi_runtime_idle\n");
+
+       /* Insert hooks here for targets, hosts, and transport classes */
+
+       if (scsi_is_sdev_device(dev))
+               err = pm_schedule_suspend(dev, 100);
+       else
+               err = pm_runtime_suspend(dev);
+       return err;
+}
+
+int scsi_autopm_get_device(struct scsi_device *sdev)
+{
+       int     err;
+
+       err = pm_runtime_get_sync(&sdev->sdev_gendev);
+       if (err < 0)
+               pm_runtime_put_sync(&sdev->sdev_gendev);
+       else if (err > 0)
+               err = 0;
+       return err;
+}
+EXPORT_SYMBOL_GPL(scsi_autopm_get_device);
+
+void scsi_autopm_put_device(struct scsi_device *sdev)
+{
+       pm_runtime_put_sync(&sdev->sdev_gendev);
+}
+EXPORT_SYMBOL_GPL(scsi_autopm_put_device);
+
+void scsi_autopm_get_target(struct scsi_target *starget)
+{
+       pm_runtime_get_sync(&starget->dev);
+}
+
+void scsi_autopm_put_target(struct scsi_target *starget)
+{
+       pm_runtime_put_sync(&starget->dev);
+}
+
+int scsi_autopm_get_host(struct Scsi_Host *shost)
+{
+       int     err;
+
+       err = pm_runtime_get_sync(&shost->shost_gendev);
+       if (err < 0)
+               pm_runtime_put_sync(&shost->shost_gendev);
+       else if (err > 0)
+               err = 0;
+       return err;
+}
+
+void scsi_autopm_put_host(struct Scsi_Host *shost)
+{
+       pm_runtime_put_sync(&shost->shost_gendev);
+}
+
+#else
+
+#define scsi_runtime_suspend   NULL
+#define scsi_runtime_resume    NULL
+#define scsi_runtime_idle      NULL
+
+#endif /* CONFIG_PM_RUNTIME */
+
 const struct dev_pm_ops scsi_bus_pm_ops = {
        .suspend =              scsi_bus_suspend,
        .resume =               scsi_bus_resume_common,
@@ -93,4 +200,7 @@ const struct dev_pm_ops scsi_bus_pm_ops = {
        .thaw =                 scsi_bus_resume_common,
        .poweroff =             scsi_bus_poweroff,
        .restore =              scsi_bus_resume_common,
+       .runtime_suspend =      scsi_runtime_suspend,
+       .runtime_resume =       scsi_runtime_resume,
+       .runtime_idle =         scsi_runtime_idle,
 };