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, pm_message_t msg)
  20{
  21        struct device_driver *drv;
  22        int err;
  23
  24        err = scsi_device_quiesce(to_scsi_device(dev));
  25        if (err == 0) {
  26                drv = dev->driver;
  27                if (drv && drv->suspend) {
  28                        err = drv->suspend(dev, msg);
  29                        if (err)
  30                                scsi_device_resume(to_scsi_device(dev));
  31                }
  32        }
  33        dev_dbg(dev, "scsi suspend: %d\n", err);
  34        return err;
  35}
  36
  37static int scsi_dev_type_resume(struct device *dev)
  38{
  39        struct device_driver *drv;
  40        int err = 0;
  41
  42        drv = dev->driver;
  43        if (drv && drv->resume)
  44                err = drv->resume(dev);
  45        scsi_device_resume(to_scsi_device(dev));
  46        dev_dbg(dev, "scsi resume: %d\n", err);
  47        return err;
  48}
  49
  50#ifdef CONFIG_PM_SLEEP
  51
  52static int scsi_bus_suspend_common(struct device *dev, pm_message_t msg)
  53{
  54        int err = 0;
  55
  56        if (scsi_is_sdev_device(dev)) {
  57                /*
  58                 * sd is the only high-level SCSI driver to implement runtime
  59                 * PM, and sd treats runtime suspend, system suspend, and
  60                 * system hibernate identically (but not system freeze).
  61                 */
  62                if (pm_runtime_suspended(dev)) {
  63                        if (msg.event == PM_EVENT_SUSPEND ||
  64                            msg.event == PM_EVENT_HIBERNATE)
  65                                return 0;       /* already suspended */
  66
  67                        /* wake up device so that FREEZE will succeed */
  68                        pm_runtime_resume(dev);
  69                }
  70                err = scsi_dev_type_suspend(dev, msg);
  71        }
  72        return err;
  73}
  74
  75static int scsi_bus_resume_common(struct device *dev)
  76{
  77        int err = 0;
  78
  79        /*
  80         * Parent device may have runtime suspended as soon as
  81         * it is woken up during the system resume.
  82         *
  83         * Resume it on behalf of child.
  84         */
  85        pm_runtime_get_sync(dev->parent);
  86
  87        if (scsi_is_sdev_device(dev))
  88                err = scsi_dev_type_resume(dev);
  89        if (err == 0) {
  90                pm_runtime_disable(dev);
  91                pm_runtime_set_active(dev);
  92                pm_runtime_enable(dev);
  93        }
  94
  95        pm_runtime_put_sync(dev->parent);
  96
  97        return err;
  98}
  99
 100static int scsi_bus_prepare(struct device *dev)
 101{
 102        if (scsi_is_sdev_device(dev)) {
 103                /* sd probing uses async_schedule.  Wait until it finishes. */
 104                async_synchronize_full_domain(&scsi_sd_probe_domain);
 105
 106        } else if (scsi_is_host_device(dev)) {
 107                /* Wait until async scanning is finished */
 108                scsi_complete_async_scans();
 109        }
 110        return 0;
 111}
 112
 113static int scsi_bus_suspend(struct device *dev)
 114{
 115        return scsi_bus_suspend_common(dev, PMSG_SUSPEND);
 116}
 117
 118static int scsi_bus_freeze(struct device *dev)
 119{
 120        return scsi_bus_suspend_common(dev, PMSG_FREEZE);
 121}
 122
 123static int scsi_bus_poweroff(struct device *dev)
 124{
 125        return scsi_bus_suspend_common(dev, PMSG_HIBERNATE);
 126}
 127
 128#else /* CONFIG_PM_SLEEP */
 129
 130#define scsi_bus_resume_common          NULL
 131#define scsi_bus_prepare                NULL
 132#define scsi_bus_suspend                NULL
 133#define scsi_bus_freeze                 NULL
 134#define scsi_bus_poweroff               NULL
 135
 136#endif /* CONFIG_PM_SLEEP */
 137
 138#ifdef CONFIG_PM_RUNTIME
 139
 140static int scsi_runtime_suspend(struct device *dev)
 141{
 142        int err = 0;
 143
 144        dev_dbg(dev, "scsi_runtime_suspend\n");
 145        if (scsi_is_sdev_device(dev)) {
 146                err = scsi_dev_type_suspend(dev, PMSG_AUTO_SUSPEND);
 147                if (err == -EAGAIN)
 148                        pm_schedule_suspend(dev, jiffies_to_msecs(
 149                                round_jiffies_up_relative(HZ/10)));
 150        }
 151
 152        /* Insert hooks here for targets, hosts, and transport classes */
 153
 154        return err;
 155}
 156
 157static int scsi_runtime_resume(struct device *dev)
 158{
 159        int err = 0;
 160
 161        dev_dbg(dev, "scsi_runtime_resume\n");
 162        if (scsi_is_sdev_device(dev))
 163                err = scsi_dev_type_resume(dev);
 164
 165        /* Insert hooks here for targets, hosts, and transport classes */
 166
 167        return err;
 168}
 169
 170static int scsi_runtime_idle(struct device *dev)
 171{
 172        int err;
 173
 174        dev_dbg(dev, "scsi_runtime_idle\n");
 175
 176        /* Insert hooks here for targets, hosts, and transport classes */
 177
 178        if (scsi_is_sdev_device(dev))
 179                err = pm_schedule_suspend(dev, 100);
 180        else
 181                err = pm_runtime_suspend(dev);
 182        return err;
 183}
 184
 185int scsi_autopm_get_device(struct scsi_device *sdev)
 186{
 187        int     err;
 188
 189        err = pm_runtime_get_sync(&sdev->sdev_gendev);
 190        if (err < 0 && err !=-EACCES)
 191                pm_runtime_put_sync(&sdev->sdev_gendev);
 192        else
 193                err = 0;
 194        return err;
 195}
 196EXPORT_SYMBOL_GPL(scsi_autopm_get_device);
 197
 198void scsi_autopm_put_device(struct scsi_device *sdev)
 199{
 200        pm_runtime_put_sync(&sdev->sdev_gendev);
 201}
 202EXPORT_SYMBOL_GPL(scsi_autopm_put_device);
 203
 204void scsi_autopm_get_target(struct scsi_target *starget)
 205{
 206        pm_runtime_get_sync(&starget->dev);
 207}
 208
 209void scsi_autopm_put_target(struct scsi_target *starget)
 210{
 211        pm_runtime_put_sync(&starget->dev);
 212}
 213
 214int scsi_autopm_get_host(struct Scsi_Host *shost)
 215{
 216        int     err;
 217
 218        err = pm_runtime_get_sync(&shost->shost_gendev);
 219        if (err < 0 && err !=-EACCES)
 220                pm_runtime_put_sync(&shost->shost_gendev);
 221        else
 222                err = 0;
 223        return err;
 224}
 225
 226void scsi_autopm_put_host(struct Scsi_Host *shost)
 227{
 228        pm_runtime_put_sync(&shost->shost_gendev);
 229}
 230
 231#else
 232
 233#define scsi_runtime_suspend    NULL
 234#define scsi_runtime_resume     NULL
 235#define scsi_runtime_idle       NULL
 236
 237#endif /* CONFIG_PM_RUNTIME */
 238
 239const struct dev_pm_ops scsi_bus_pm_ops = {
 240        .prepare =              scsi_bus_prepare,
 241        .suspend =              scsi_bus_suspend,
 242        .resume =               scsi_bus_resume_common,
 243        .freeze =               scsi_bus_freeze,
 244        .thaw =                 scsi_bus_resume_common,
 245        .poweroff =             scsi_bus_poweroff,
 246        .restore =              scsi_bus_resume_common,
 247        .runtime_suspend =      scsi_runtime_suspend,
 248        .runtime_resume =       scsi_runtime_resume,
 249        .runtime_idle =         scsi_runtime_idle,
 250};
 251
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.