linux/drivers/scsi/scsi_pm.c
<<
>>
Prefs
   1/*
   2 *      scsi_pm.c       Copyright (C) 2010 Alan Stern
   3 *
   4 *      SCSI dynamic Power Management
   5 *              Initial version: Alan Stern <stern@rowland.harvard.edu>
   6 */
   7
   8#include <linux/pm_runtime.h>
   9#include <linux/export.h>
  10#include <linux/async.h>
  11
  12#include <scsi/scsi.h>
  13#include <scsi/scsi_device.h>
  14#include <scsi/scsi_driver.h>
  15#include <scsi/scsi_host.h>
  16
  17#include "scsi_priv.h"
  18
  19static int scsi_dev_type_suspend(struct device *dev, int (*cb)(struct device *))
  20{
  21        int err;
  22
  23        err = scsi_device_quiesce(to_scsi_device(dev));
  24        if (err == 0) {
  25                if (cb) {
  26                        err = cb(dev);
  27                        if (err)
  28                                scsi_device_resume(to_scsi_device(dev));
  29                }
  30        }
  31        dev_dbg(dev, "scsi suspend: %d\n", err);
  32        return err;
  33}
  34
  35static int scsi_dev_type_resume(struct device *dev, int (*cb)(struct device *))
  36{
  37        int err = 0;
  38
  39        if (cb)
  40                err = cb(dev);
  41        scsi_device_resume(to_scsi_device(dev));
  42        dev_dbg(dev, "scsi resume: %d\n", err);
  43        return err;
  44}
  45
  46#ifdef CONFIG_PM_SLEEP
  47
  48static int
  49scsi_bus_suspend_common(struct device *dev, int (*cb)(struct device *))
  50{
  51        int err = 0;
  52
  53        if (scsi_is_sdev_device(dev)) {
  54                /*
  55                 * All the high-level SCSI drivers that implement runtime
  56                 * PM treat runtime suspend, system suspend, and system
  57                 * hibernate identically.
  58                 */
  59                if (pm_runtime_suspended(dev))
  60                        return 0;
  61
  62                err = scsi_dev_type_suspend(dev, cb);
  63        }
  64
  65        return err;
  66}
  67
  68static int
  69scsi_bus_resume_common(struct device *dev, int (*cb)(struct device *))
  70{
  71        int err = 0;
  72
  73        if (scsi_is_sdev_device(dev))
  74                err = scsi_dev_type_resume(dev, cb);
  75
  76        if (err == 0) {
  77                pm_runtime_disable(dev);
  78                pm_runtime_set_active(dev);
  79                pm_runtime_enable(dev);
  80        }
  81        return err;
  82}
  83
  84static int scsi_bus_prepare(struct device *dev)
  85{
  86        if (scsi_is_sdev_device(dev)) {
  87                /* sd probing uses async_schedule.  Wait until it finishes. */
  88                async_synchronize_full_domain(&scsi_sd_probe_domain);
  89
  90        } else if (scsi_is_host_device(dev)) {
  91                /* Wait until async scanning is finished */
  92                scsi_complete_async_scans();
  93        }
  94        return 0;
  95}
  96
  97static int scsi_bus_suspend(struct device *dev)
  98{
  99        const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
 100        return scsi_bus_suspend_common(dev, pm ? pm->suspend : NULL);
 101}
 102
 103static int scsi_bus_resume(struct device *dev)
 104{
 105        const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
 106        return scsi_bus_resume_common(dev, pm ? pm->resume : NULL);
 107}
 108
 109static int scsi_bus_freeze(struct device *dev)
 110{
 111        const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
 112        return scsi_bus_suspend_common(dev, pm ? pm->freeze : NULL);
 113}
 114
 115static int scsi_bus_thaw(struct device *dev)
 116{
 117        const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
 118        return scsi_bus_resume_common(dev, pm ? pm->thaw : NULL);
 119}
 120
 121static int scsi_bus_poweroff(struct device *dev)
 122{
 123        const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
 124        return scsi_bus_suspend_common(dev, pm ? pm->poweroff : NULL);
 125}
 126
 127static int scsi_bus_restore(struct device *dev)
 128{
 129        const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
 130        return scsi_bus_resume_common(dev, pm ? pm->restore : NULL);
 131}
 132
 133#else /* CONFIG_PM_SLEEP */
 134
 135#define scsi_bus_prepare                NULL
 136#define scsi_bus_suspend                NULL
 137#define scsi_bus_resume                 NULL
 138#define scsi_bus_freeze                 NULL
 139#define scsi_bus_thaw                   NULL
 140#define scsi_bus_poweroff               NULL
 141#define scsi_bus_restore                NULL
 142
 143#endif /* CONFIG_PM_SLEEP */
 144
 145#ifdef CONFIG_PM_RUNTIME
 146
 147static int scsi_runtime_suspend(struct device *dev)
 148{
 149        int err = 0;
 150        const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
 151
 152        dev_dbg(dev, "scsi_runtime_suspend\n");
 153        if (scsi_is_sdev_device(dev)) {
 154                err = scsi_dev_type_suspend(dev,
 155                                pm ? pm->runtime_suspend : NULL);
 156                if (err == -EAGAIN)
 157                        pm_schedule_suspend(dev, jiffies_to_msecs(
 158                                round_jiffies_up_relative(HZ/10)));
 159        }
 160
 161        /* Insert hooks here for targets, hosts, and transport classes */
 162
 163        return err;
 164}
 165
 166static int scsi_runtime_resume(struct device *dev)
 167{
 168        int err = 0;
 169        const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
 170
 171        dev_dbg(dev, "scsi_runtime_resume\n");
 172        if (scsi_is_sdev_device(dev))
 173                err = scsi_dev_type_resume(dev, pm ? pm->runtime_resume : NULL);
 174
 175        /* Insert hooks here for targets, hosts, and transport classes */
 176
 177        return err;
 178}
 179
 180static int scsi_runtime_idle(struct device *dev)
 181{
 182        int err;
 183
 184        dev_dbg(dev, "scsi_runtime_idle\n");
 185
 186        /* Insert hooks here for targets, hosts, and transport classes */
 187
 188        if (scsi_is_sdev_device(dev))
 189                err = pm_schedule_suspend(dev, 100);
 190        else
 191                err = pm_runtime_suspend(dev);
 192        return err;
 193}
 194
 195int scsi_autopm_get_device(struct scsi_device *sdev)
 196{
 197        int     err;
 198
 199        err = pm_runtime_get_sync(&sdev->sdev_gendev);
 200        if (err < 0 && err !=-EACCES)
 201                pm_runtime_put_sync(&sdev->sdev_gendev);
 202        else
 203                err = 0;
 204        return err;
 205}
 206EXPORT_SYMBOL_GPL(scsi_autopm_get_device);
 207
 208void scsi_autopm_put_device(struct scsi_device *sdev)
 209{
 210        pm_runtime_put_sync(&sdev->sdev_gendev);
 211}
 212EXPORT_SYMBOL_GPL(scsi_autopm_put_device);
 213
 214void scsi_autopm_get_target(struct scsi_target *starget)
 215{
 216        pm_runtime_get_sync(&starget->dev);
 217}
 218
 219void scsi_autopm_put_target(struct scsi_target *starget)
 220{
 221        pm_runtime_put_sync(&starget->dev);
 222}
 223
 224int scsi_autopm_get_host(struct Scsi_Host *shost)
 225{
 226        int     err;
 227
 228        err = pm_runtime_get_sync(&shost->shost_gendev);
 229        if (err < 0 && err !=-EACCES)
 230                pm_runtime_put_sync(&shost->shost_gendev);
 231        else
 232                err = 0;
 233        return err;
 234}
 235
 236void scsi_autopm_put_host(struct Scsi_Host *shost)
 237{
 238        pm_runtime_put_sync(&shost->shost_gendev);
 239}
 240
 241#else
 242
 243#define scsi_runtime_suspend    NULL
 244#define scsi_runtime_resume     NULL
 245#define scsi_runtime_idle       NULL
 246
 247#endif /* CONFIG_PM_RUNTIME */
 248
 249const struct dev_pm_ops scsi_bus_pm_ops = {
 250        .prepare =              scsi_bus_prepare,
 251        .suspend =              scsi_bus_suspend,
 252        .resume =               scsi_bus_resume,
 253        .freeze =               scsi_bus_freeze,
 254        .thaw =                 scsi_bus_thaw,
 255        .poweroff =             scsi_bus_poweroff,
 256        .restore =              scsi_bus_restore,
 257        .runtime_suspend =      scsi_runtime_suspend,
 258        .runtime_resume =       scsi_runtime_resume,
 259        .runtime_idle =         scsi_runtime_idle,
 260};
 261
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.