274 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * scsi_pm.c Copyright (C) 2010 Alan Stern4 *5 * SCSI dynamic Power Management6 * Initial version: Alan Stern <stern@rowland.harvard.edu>7 */8 9#include <linux/pm_runtime.h>10#include <linux/export.h>11#include <linux/blk-pm.h>12 13#include <scsi/scsi.h>14#include <scsi/scsi_device.h>15#include <scsi/scsi_driver.h>16#include <scsi/scsi_host.h>17 18#include "scsi_priv.h"19 20#ifdef CONFIG_PM_SLEEP21 22static int do_scsi_suspend(struct device *dev, const struct dev_pm_ops *pm)23{24 return pm && pm->suspend ? pm->suspend(dev) : 0;25}26 27static int do_scsi_freeze(struct device *dev, const struct dev_pm_ops *pm)28{29 return pm && pm->freeze ? pm->freeze(dev) : 0;30}31 32static int do_scsi_poweroff(struct device *dev, const struct dev_pm_ops *pm)33{34 return pm && pm->poweroff ? pm->poweroff(dev) : 0;35}36 37static int do_scsi_resume(struct device *dev, const struct dev_pm_ops *pm)38{39 return pm && pm->resume ? pm->resume(dev) : 0;40}41 42static int do_scsi_thaw(struct device *dev, const struct dev_pm_ops *pm)43{44 return pm && pm->thaw ? pm->thaw(dev) : 0;45}46 47static int do_scsi_restore(struct device *dev, const struct dev_pm_ops *pm)48{49 return pm && pm->restore ? pm->restore(dev) : 0;50}51 52static int scsi_dev_type_suspend(struct device *dev,53 int (*cb)(struct device *, const struct dev_pm_ops *))54{55 const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;56 int err;57 58 err = scsi_device_quiesce(to_scsi_device(dev));59 if (err == 0) {60 err = cb(dev, pm);61 if (err)62 scsi_device_resume(to_scsi_device(dev));63 }64 dev_dbg(dev, "scsi suspend: %d\n", err);65 return err;66}67 68static int69scsi_bus_suspend_common(struct device *dev,70 int (*cb)(struct device *, const struct dev_pm_ops *))71{72 if (!scsi_is_sdev_device(dev))73 return 0;74 75 return scsi_dev_type_suspend(dev, cb);76}77 78static int scsi_bus_resume_common(struct device *dev,79 int (*cb)(struct device *, const struct dev_pm_ops *))80{81 const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;82 int err;83 84 if (!scsi_is_sdev_device(dev))85 return 0;86 87 err = cb(dev, pm);88 scsi_device_resume(to_scsi_device(dev));89 dev_dbg(dev, "scsi resume: %d\n", err);90 91 return err;92}93 94static int scsi_bus_prepare(struct device *dev)95{96 if (scsi_is_host_device(dev)) {97 /* Wait until async scanning is finished */98 scsi_complete_async_scans();99 }100 return 0;101}102 103static int scsi_bus_suspend(struct device *dev)104{105 return scsi_bus_suspend_common(dev, do_scsi_suspend);106}107 108static int scsi_bus_resume(struct device *dev)109{110 return scsi_bus_resume_common(dev, do_scsi_resume);111}112 113static int scsi_bus_freeze(struct device *dev)114{115 return scsi_bus_suspend_common(dev, do_scsi_freeze);116}117 118static int scsi_bus_thaw(struct device *dev)119{120 return scsi_bus_resume_common(dev, do_scsi_thaw);121}122 123static int scsi_bus_poweroff(struct device *dev)124{125 return scsi_bus_suspend_common(dev, do_scsi_poweroff);126}127 128static int scsi_bus_restore(struct device *dev)129{130 return scsi_bus_resume_common(dev, do_scsi_restore);131}132 133#else /* CONFIG_PM_SLEEP */134 135#define scsi_bus_prepare NULL136#define scsi_bus_suspend NULL137#define scsi_bus_resume NULL138#define scsi_bus_freeze NULL139#define scsi_bus_thaw NULL140#define scsi_bus_poweroff NULL141#define scsi_bus_restore NULL142 143#endif /* CONFIG_PM_SLEEP */144 145static int sdev_runtime_suspend(struct device *dev)146{147 const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;148 struct scsi_device *sdev = to_scsi_device(dev);149 int err = 0;150 151 err = blk_pre_runtime_suspend(sdev->request_queue);152 if (err)153 return err;154 if (pm && pm->runtime_suspend)155 err = pm->runtime_suspend(dev);156 blk_post_runtime_suspend(sdev->request_queue, err);157 158 return err;159}160 161static int scsi_runtime_suspend(struct device *dev)162{163 int err = 0;164 165 dev_dbg(dev, "scsi_runtime_suspend\n");166 if (scsi_is_sdev_device(dev))167 err = sdev_runtime_suspend(dev);168 169 /* Insert hooks here for targets, hosts, and transport classes */170 171 return err;172}173 174static int sdev_runtime_resume(struct device *dev)175{176 struct scsi_device *sdev = to_scsi_device(dev);177 const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;178 int err = 0;179 180 blk_pre_runtime_resume(sdev->request_queue);181 if (pm && pm->runtime_resume)182 err = pm->runtime_resume(dev);183 blk_post_runtime_resume(sdev->request_queue);184 185 return err;186}187 188static int scsi_runtime_resume(struct device *dev)189{190 int err = 0;191 192 dev_dbg(dev, "scsi_runtime_resume\n");193 if (scsi_is_sdev_device(dev))194 err = sdev_runtime_resume(dev);195 196 /* Insert hooks here for targets, hosts, and transport classes */197 198 return err;199}200 201static int scsi_runtime_idle(struct device *dev)202{203 dev_dbg(dev, "scsi_runtime_idle\n");204 205 /* Insert hooks here for targets, hosts, and transport classes */206 207 if (scsi_is_sdev_device(dev)) {208 pm_runtime_mark_last_busy(dev);209 pm_runtime_autosuspend(dev);210 return -EBUSY;211 }212 213 return 0;214}215 216int scsi_autopm_get_device(struct scsi_device *sdev)217{218 int err;219 220 err = pm_runtime_get_sync(&sdev->sdev_gendev);221 if (err < 0 && err !=-EACCES)222 pm_runtime_put_sync(&sdev->sdev_gendev);223 else224 err = 0;225 return err;226}227EXPORT_SYMBOL_GPL(scsi_autopm_get_device);228 229void scsi_autopm_put_device(struct scsi_device *sdev)230{231 pm_runtime_put_sync(&sdev->sdev_gendev);232}233EXPORT_SYMBOL_GPL(scsi_autopm_put_device);234 235void scsi_autopm_get_target(struct scsi_target *starget)236{237 pm_runtime_get_sync(&starget->dev);238}239 240void scsi_autopm_put_target(struct scsi_target *starget)241{242 pm_runtime_put_sync(&starget->dev);243}244 245int scsi_autopm_get_host(struct Scsi_Host *shost)246{247 int err;248 249 err = pm_runtime_get_sync(&shost->shost_gendev);250 if (err < 0 && err !=-EACCES)251 pm_runtime_put_sync(&shost->shost_gendev);252 else253 err = 0;254 return err;255}256 257void scsi_autopm_put_host(struct Scsi_Host *shost)258{259 pm_runtime_put_sync(&shost->shost_gendev);260}261 262const struct dev_pm_ops scsi_bus_pm_ops = {263 .prepare = scsi_bus_prepare,264 .suspend = scsi_bus_suspend,265 .resume = scsi_bus_resume,266 .freeze = scsi_bus_freeze,267 .thaw = scsi_bus_thaw,268 .poweroff = scsi_bus_poweroff,269 .restore = scsi_bus_restore,270 .runtime_suspend = scsi_runtime_suspend,271 .runtime_resume = scsi_runtime_resume,272 .runtime_idle = scsi_runtime_idle,273};274