diff options
Diffstat (limited to 'drivers/scsi/scsi_pm.c')
-rw-r--r-- | drivers/scsi/scsi_pm.c | 110 |
1 files changed, 110 insertions, 0 deletions
diff --git a/drivers/scsi/scsi_pm.c b/drivers/scsi/scsi_pm.c index cd83758ce0a..d70e91ae60a 100644 --- a/drivers/scsi/scsi_pm.c +++ b/drivers/scsi/scsi_pm.c @@ -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, }; |