1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
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> |
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_SLEEP |
21 | |
22 | static 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 | |
27 | static 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 | |
32 | static 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 | |
37 | static 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 | |
42 | static 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 | |
47 | static 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 | |
52 | static 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 | |
68 | static int |
69 | scsi_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 | |
78 | static 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 | |
94 | static 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 | |
103 | static int scsi_bus_suspend(struct device *dev) |
104 | { |
105 | return scsi_bus_suspend_common(dev, cb: do_scsi_suspend); |
106 | } |
107 | |
108 | static int scsi_bus_resume(struct device *dev) |
109 | { |
110 | return scsi_bus_resume_common(dev, cb: do_scsi_resume); |
111 | } |
112 | |
113 | static int scsi_bus_freeze(struct device *dev) |
114 | { |
115 | return scsi_bus_suspend_common(dev, cb: do_scsi_freeze); |
116 | } |
117 | |
118 | static int scsi_bus_thaw(struct device *dev) |
119 | { |
120 | return scsi_bus_resume_common(dev, cb: do_scsi_thaw); |
121 | } |
122 | |
123 | static int scsi_bus_poweroff(struct device *dev) |
124 | { |
125 | return scsi_bus_suspend_common(dev, cb: do_scsi_poweroff); |
126 | } |
127 | |
128 | static int scsi_bus_restore(struct device *dev) |
129 | { |
130 | return scsi_bus_resume_common(dev, cb: do_scsi_restore); |
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 | static 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(q: 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(q: sdev->request_queue, err); |
157 | |
158 | return err; |
159 | } |
160 | |
161 | static 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 | |
174 | static 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(q: sdev->request_queue); |
181 | if (pm && pm->runtime_resume) |
182 | err = pm->runtime_resume(dev); |
183 | blk_post_runtime_resume(q: sdev->request_queue); |
184 | |
185 | return err; |
186 | } |
187 | |
188 | static 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 | |
201 | static 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 | |
216 | int scsi_autopm_get_device(struct scsi_device *sdev) |
217 | { |
218 | int err; |
219 | |
220 | err = pm_runtime_get_sync(dev: &sdev->sdev_gendev); |
221 | if (err < 0 && err !=-EACCES) |
222 | pm_runtime_put_sync(dev: &sdev->sdev_gendev); |
223 | else |
224 | err = 0; |
225 | return err; |
226 | } |
227 | EXPORT_SYMBOL_GPL(scsi_autopm_get_device); |
228 | |
229 | void scsi_autopm_put_device(struct scsi_device *sdev) |
230 | { |
231 | pm_runtime_put_sync(dev: &sdev->sdev_gendev); |
232 | } |
233 | EXPORT_SYMBOL_GPL(scsi_autopm_put_device); |
234 | |
235 | void scsi_autopm_get_target(struct scsi_target *starget) |
236 | { |
237 | pm_runtime_get_sync(dev: &starget->dev); |
238 | } |
239 | |
240 | void scsi_autopm_put_target(struct scsi_target *starget) |
241 | { |
242 | pm_runtime_put_sync(dev: &starget->dev); |
243 | } |
244 | |
245 | int scsi_autopm_get_host(struct Scsi_Host *shost) |
246 | { |
247 | int err; |
248 | |
249 | err = pm_runtime_get_sync(dev: &shost->shost_gendev); |
250 | if (err < 0 && err !=-EACCES) |
251 | pm_runtime_put_sync(dev: &shost->shost_gendev); |
252 | else |
253 | err = 0; |
254 | return err; |
255 | } |
256 | |
257 | void scsi_autopm_put_host(struct Scsi_Host *shost) |
258 | { |
259 | pm_runtime_put_sync(dev: &shost->shost_gendev); |
260 | } |
261 | |
262 | const 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 | |