blob: b5a858c29488aabb3dbf04e8c82fd11f5ab40a29 [file] [log] [blame]
Thomas Gleixner457c8992019-05-19 13:08:55 +01001// SPDX-License-Identifier: GPL-2.0-only
Alan Sterndb5bd1e2010-06-17 10:36:49 -04002/*
3 * scsi_pm.c Copyright (C) 2010 Alan Stern
4 *
5 * SCSI dynamic Power Management
6 * Initial version: Alan Stern <stern@rowland.harvard.edu>
7 */
8
9#include <linux/pm_runtime.h>
Paul Gortmaker09703662011-05-27 09:37:25 -040010#include <linux/export.h>
Alan Sternfea6d602012-02-17 16:25:08 -050011#include <linux/async.h>
Bart Van Asschebca6b062018-09-26 14:01:03 -070012#include <linux/blk-pm.h>
Alan Sterndb5bd1e2010-06-17 10:36:49 -040013
14#include <scsi/scsi.h>
15#include <scsi/scsi_device.h>
16#include <scsi/scsi_driver.h>
17#include <scsi/scsi_host.h>
18
19#include "scsi_priv.h"
20
Aaron Lu6627b382013-10-28 15:27:49 +080021#ifdef CONFIG_PM_SLEEP
22
Dan Williams3c31b522014-04-10 15:30:35 -070023static int do_scsi_suspend(struct device *dev, const struct dev_pm_ops *pm)
Alan Sterndb5bd1e2010-06-17 10:36:49 -040024{
Dan Williams3c31b522014-04-10 15:30:35 -070025 return pm && pm->suspend ? pm->suspend(dev) : 0;
26}
27
28static int do_scsi_freeze(struct device *dev, const struct dev_pm_ops *pm)
29{
30 return pm && pm->freeze ? pm->freeze(dev) : 0;
31}
32
33static int do_scsi_poweroff(struct device *dev, const struct dev_pm_ops *pm)
34{
35 return pm && pm->poweroff ? pm->poweroff(dev) : 0;
36}
37
38static int do_scsi_resume(struct device *dev, const struct dev_pm_ops *pm)
39{
40 return pm && pm->resume ? pm->resume(dev) : 0;
41}
42
43static int do_scsi_thaw(struct device *dev, const struct dev_pm_ops *pm)
44{
45 return pm && pm->thaw ? pm->thaw(dev) : 0;
46}
47
48static int do_scsi_restore(struct device *dev, const struct dev_pm_ops *pm)
49{
50 return pm && pm->restore ? pm->restore(dev) : 0;
51}
52
53static int scsi_dev_type_suspend(struct device *dev,
54 int (*cb)(struct device *, const struct dev_pm_ops *))
55{
56 const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
Alan Sterndb5bd1e2010-06-17 10:36:49 -040057 int err;
58
59 err = scsi_device_quiesce(to_scsi_device(dev));
60 if (err == 0) {
Dan Williams3c31b522014-04-10 15:30:35 -070061 err = cb(dev, pm);
62 if (err)
63 scsi_device_resume(to_scsi_device(dev));
Alan Sterndb5bd1e2010-06-17 10:36:49 -040064 }
65 dev_dbg(dev, "scsi suspend: %d\n", err);
66 return err;
67}
68
Aaron Lu80d2fd42012-11-09 15:27:54 +080069static int
Dan Williams3c31b522014-04-10 15:30:35 -070070scsi_bus_suspend_common(struct device *dev,
71 int (*cb)(struct device *, const struct dev_pm_ops *))
Alan Sterndb5bd1e2010-06-17 10:36:49 -040072{
Bart Van Assche9131bff2021-10-06 14:54:53 -070073 if (!scsi_is_sdev_device(dev))
74 return 0;
Alan Sterndb5bd1e2010-06-17 10:36:49 -040075
Bart Van Assche9131bff2021-10-06 14:54:53 -070076 return scsi_dev_type_suspend(dev, cb);
Alan Sterndb5bd1e2010-06-17 10:36:49 -040077}
78
Dan Williams3c31b522014-04-10 15:30:35 -070079static int scsi_bus_resume_common(struct device *dev,
80 int (*cb)(struct device *, const struct dev_pm_ops *))
81{
Bart Van Assche9131bff2021-10-06 14:54:53 -070082 const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
83 int err;
84
85 if (!scsi_is_sdev_device(dev))
86 return 0;
87
88 err = cb(dev, pm);
89 scsi_device_resume(to_scsi_device(dev));
90 dev_dbg(dev, "scsi resume: %d\n", err);
91
92 return err;
Alan Sterndb5bd1e2010-06-17 10:36:49 -040093}
94
Alan Sternfea6d602012-02-17 16:25:08 -050095static int scsi_bus_prepare(struct device *dev)
96{
Bart Van Asschef049cf12019-04-30 14:39:18 -070097 if (scsi_is_host_device(dev)) {
Alan Sternfea6d602012-02-17 16:25:08 -050098 /* Wait until async scanning is finished */
99 scsi_complete_async_scans();
100 }
101 return 0;
102}
103
Alan Sterndb5bd1e2010-06-17 10:36:49 -0400104static int scsi_bus_suspend(struct device *dev)
105{
Dan Williams3c31b522014-04-10 15:30:35 -0700106 return scsi_bus_suspend_common(dev, do_scsi_suspend);
Aaron Lu80d2fd42012-11-09 15:27:54 +0800107}
108
109static int scsi_bus_resume(struct device *dev)
110{
Dan Williams3c31b522014-04-10 15:30:35 -0700111 return scsi_bus_resume_common(dev, do_scsi_resume);
Alan Sterndb5bd1e2010-06-17 10:36:49 -0400112}
113
114static int scsi_bus_freeze(struct device *dev)
115{
Dan Williams3c31b522014-04-10 15:30:35 -0700116 return scsi_bus_suspend_common(dev, do_scsi_freeze);
Aaron Lu80d2fd42012-11-09 15:27:54 +0800117}
118
119static int scsi_bus_thaw(struct device *dev)
120{
Dan Williams3c31b522014-04-10 15:30:35 -0700121 return scsi_bus_resume_common(dev, do_scsi_thaw);
Alan Sterndb5bd1e2010-06-17 10:36:49 -0400122}
123
124static int scsi_bus_poweroff(struct device *dev)
125{
Dan Williams3c31b522014-04-10 15:30:35 -0700126 return scsi_bus_suspend_common(dev, do_scsi_poweroff);
Aaron Lu80d2fd42012-11-09 15:27:54 +0800127}
128
129static int scsi_bus_restore(struct device *dev)
130{
Dan Williams3c31b522014-04-10 15:30:35 -0700131 return scsi_bus_resume_common(dev, do_scsi_restore);
Alan Sterndb5bd1e2010-06-17 10:36:49 -0400132}
133
134#else /* CONFIG_PM_SLEEP */
135
Alan Sternfea6d602012-02-17 16:25:08 -0500136#define scsi_bus_prepare NULL
Alan Sterndb5bd1e2010-06-17 10:36:49 -0400137#define scsi_bus_suspend NULL
Aaron Lu80d2fd42012-11-09 15:27:54 +0800138#define scsi_bus_resume NULL
Alan Sterndb5bd1e2010-06-17 10:36:49 -0400139#define scsi_bus_freeze NULL
Aaron Lu80d2fd42012-11-09 15:27:54 +0800140#define scsi_bus_thaw NULL
Alan Sterndb5bd1e2010-06-17 10:36:49 -0400141#define scsi_bus_poweroff NULL
Aaron Lu80d2fd42012-11-09 15:27:54 +0800142#define scsi_bus_restore NULL
Alan Sterndb5bd1e2010-06-17 10:36:49 -0400143
144#endif /* CONFIG_PM_SLEEP */
145
Aaron Lu6627b382013-10-28 15:27:49 +0800146static int sdev_runtime_suspend(struct device *dev)
Lin Ming6df339a2013-03-23 11:42:28 +0800147{
Aaron Lu6627b382013-10-28 15:27:49 +0800148 const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
149 struct scsi_device *sdev = to_scsi_device(dev);
Alan Stern49718f02015-08-17 11:02:42 -0400150 int err = 0;
Lin Ming6df339a2013-03-23 11:42:28 +0800151
Ken Xue1c69d3b2015-12-01 14:45:23 +0800152 err = blk_pre_runtime_suspend(sdev->request_queue);
153 if (err)
154 return err;
155 if (pm && pm->runtime_suspend)
Aaron Lu6627b382013-10-28 15:27:49 +0800156 err = pm->runtime_suspend(dev);
Ken Xue1c69d3b2015-12-01 14:45:23 +0800157 blk_post_runtime_suspend(sdev->request_queue, err);
158
Lin Ming6df339a2013-03-23 11:42:28 +0800159 return err;
160}
161
Alan Sternbc4f2402010-06-17 10:41:42 -0400162static int scsi_runtime_suspend(struct device *dev)
163{
164 int err = 0;
165
166 dev_dbg(dev, "scsi_runtime_suspend\n");
Lin Ming6df339a2013-03-23 11:42:28 +0800167 if (scsi_is_sdev_device(dev))
168 err = sdev_runtime_suspend(dev);
Alan Sternbc4f2402010-06-17 10:41:42 -0400169
170 /* Insert hooks here for targets, hosts, and transport classes */
171
172 return err;
173}
174
Lin Ming6df339a2013-03-23 11:42:28 +0800175static int sdev_runtime_resume(struct device *dev)
176{
177 struct scsi_device *sdev = to_scsi_device(dev);
178 const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
Aaron Lu6627b382013-10-28 15:27:49 +0800179 int err = 0;
Lin Ming6df339a2013-03-23 11:42:28 +0800180
Ken Xue1c69d3b2015-12-01 14:45:23 +0800181 blk_pre_runtime_resume(sdev->request_queue);
182 if (pm && pm->runtime_resume)
Aaron Lu6627b382013-10-28 15:27:49 +0800183 err = pm->runtime_resume(dev);
Ken Xue1c69d3b2015-12-01 14:45:23 +0800184 blk_post_runtime_resume(sdev->request_queue, err);
185
Aaron Lu6627b382013-10-28 15:27:49 +0800186 return err;
Lin Ming6df339a2013-03-23 11:42:28 +0800187}
188
Alan Sternbc4f2402010-06-17 10:41:42 -0400189static int scsi_runtime_resume(struct device *dev)
190{
191 int err = 0;
192
193 dev_dbg(dev, "scsi_runtime_resume\n");
194 if (scsi_is_sdev_device(dev))
Lin Ming6df339a2013-03-23 11:42:28 +0800195 err = sdev_runtime_resume(dev);
Alan Sternbc4f2402010-06-17 10:41:42 -0400196
197 /* Insert hooks here for targets, hosts, and transport classes */
198
199 return err;
200}
201
202static int scsi_runtime_idle(struct device *dev)
203{
Alan Sternbc4f2402010-06-17 10:41:42 -0400204 dev_dbg(dev, "scsi_runtime_idle\n");
205
206 /* Insert hooks here for targets, hosts, and transport classes */
207
Lin Ming6df339a2013-03-23 11:42:28 +0800208 if (scsi_is_sdev_device(dev)) {
Aaron Lu6627b382013-10-28 15:27:49 +0800209 pm_runtime_mark_last_busy(dev);
210 pm_runtime_autosuspend(dev);
211 return -EBUSY;
Lin Ming6df339a2013-03-23 11:42:28 +0800212 }
Aaron Lu6627b382013-10-28 15:27:49 +0800213
Rafael J. Wysocki45f0a852013-06-03 21:49:52 +0200214 return 0;
Alan Sternbc4f2402010-06-17 10:41:42 -0400215}
216
217int scsi_autopm_get_device(struct scsi_device *sdev)
218{
219 int err;
220
221 err = pm_runtime_get_sync(&sdev->sdev_gendev);
Rafael J. Wysocki632e2702011-07-01 22:29:15 +0200222 if (err < 0 && err !=-EACCES)
Alan Sternbc4f2402010-06-17 10:41:42 -0400223 pm_runtime_put_sync(&sdev->sdev_gendev);
Rafael J. Wysocki632e2702011-07-01 22:29:15 +0200224 else
Alan Sternbc4f2402010-06-17 10:41:42 -0400225 err = 0;
226 return err;
227}
228EXPORT_SYMBOL_GPL(scsi_autopm_get_device);
229
230void scsi_autopm_put_device(struct scsi_device *sdev)
231{
232 pm_runtime_put_sync(&sdev->sdev_gendev);
233}
234EXPORT_SYMBOL_GPL(scsi_autopm_put_device);
235
236void scsi_autopm_get_target(struct scsi_target *starget)
237{
238 pm_runtime_get_sync(&starget->dev);
239}
240
241void scsi_autopm_put_target(struct scsi_target *starget)
242{
243 pm_runtime_put_sync(&starget->dev);
244}
245
246int scsi_autopm_get_host(struct Scsi_Host *shost)
247{
248 int err;
249
250 err = pm_runtime_get_sync(&shost->shost_gendev);
Rafael J. Wysocki632e2702011-07-01 22:29:15 +0200251 if (err < 0 && err !=-EACCES)
Alan Sternbc4f2402010-06-17 10:41:42 -0400252 pm_runtime_put_sync(&shost->shost_gendev);
Rafael J. Wysocki632e2702011-07-01 22:29:15 +0200253 else
Alan Sternbc4f2402010-06-17 10:41:42 -0400254 err = 0;
255 return err;
256}
257
258void scsi_autopm_put_host(struct Scsi_Host *shost)
259{
260 pm_runtime_put_sync(&shost->shost_gendev);
261}
262
Alan Sterndb5bd1e2010-06-17 10:36:49 -0400263const struct dev_pm_ops scsi_bus_pm_ops = {
Alan Sternfea6d602012-02-17 16:25:08 -0500264 .prepare = scsi_bus_prepare,
Alan Sterndb5bd1e2010-06-17 10:36:49 -0400265 .suspend = scsi_bus_suspend,
Aaron Lu80d2fd42012-11-09 15:27:54 +0800266 .resume = scsi_bus_resume,
Alan Sterndb5bd1e2010-06-17 10:36:49 -0400267 .freeze = scsi_bus_freeze,
Aaron Lu80d2fd42012-11-09 15:27:54 +0800268 .thaw = scsi_bus_thaw,
Alan Sterndb5bd1e2010-06-17 10:36:49 -0400269 .poweroff = scsi_bus_poweroff,
Aaron Lu80d2fd42012-11-09 15:27:54 +0800270 .restore = scsi_bus_restore,
Alan Sternbc4f2402010-06-17 10:41:42 -0400271 .runtime_suspend = scsi_runtime_suspend,
272 .runtime_resume = scsi_runtime_resume,
273 .runtime_idle = scsi_runtime_idle,
Alan Sterndb5bd1e2010-06-17 10:36:49 -0400274};