diff options
Diffstat (limited to 'drivers/scsi/scsi_pm.c')
-rw-r--r-- | drivers/scsi/scsi_pm.c | 128 |
1 files changed, 99 insertions, 29 deletions
diff --git a/drivers/scsi/scsi_pm.c b/drivers/scsi/scsi_pm.c index 001e9ceda4c3..7454498c4091 100644 --- a/drivers/scsi/scsi_pm.c +++ b/drivers/scsi/scsi_pm.c | |||
@@ -18,35 +18,77 @@ | |||
18 | 18 | ||
19 | #ifdef CONFIG_PM_SLEEP | 19 | #ifdef CONFIG_PM_SLEEP |
20 | 20 | ||
21 | static int scsi_dev_type_suspend(struct device *dev, int (*cb)(struct device *)) | 21 | static int do_scsi_suspend(struct device *dev, const struct dev_pm_ops *pm) |
22 | { | 22 | { |
23 | return pm && pm->suspend ? pm->suspend(dev) : 0; | ||
24 | } | ||
25 | |||
26 | static int do_scsi_freeze(struct device *dev, const struct dev_pm_ops *pm) | ||
27 | { | ||
28 | return pm && pm->freeze ? pm->freeze(dev) : 0; | ||
29 | } | ||
30 | |||
31 | static int do_scsi_poweroff(struct device *dev, const struct dev_pm_ops *pm) | ||
32 | { | ||
33 | return pm && pm->poweroff ? pm->poweroff(dev) : 0; | ||
34 | } | ||
35 | |||
36 | static int do_scsi_resume(struct device *dev, const struct dev_pm_ops *pm) | ||
37 | { | ||
38 | return pm && pm->resume ? pm->resume(dev) : 0; | ||
39 | } | ||
40 | |||
41 | static int do_scsi_thaw(struct device *dev, const struct dev_pm_ops *pm) | ||
42 | { | ||
43 | return pm && pm->thaw ? pm->thaw(dev) : 0; | ||
44 | } | ||
45 | |||
46 | static int do_scsi_restore(struct device *dev, const struct dev_pm_ops *pm) | ||
47 | { | ||
48 | return pm && pm->restore ? pm->restore(dev) : 0; | ||
49 | } | ||
50 | |||
51 | static int scsi_dev_type_suspend(struct device *dev, | ||
52 | int (*cb)(struct device *, const struct dev_pm_ops *)) | ||
53 | { | ||
54 | const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; | ||
23 | int err; | 55 | int err; |
24 | 56 | ||
57 | /* flush pending in-flight resume operations, suspend is synchronous */ | ||
58 | async_synchronize_full_domain(&scsi_sd_pm_domain); | ||
59 | |||
25 | err = scsi_device_quiesce(to_scsi_device(dev)); | 60 | err = scsi_device_quiesce(to_scsi_device(dev)); |
26 | if (err == 0) { | 61 | if (err == 0) { |
27 | if (cb) { | 62 | err = cb(dev, pm); |
28 | err = cb(dev); | 63 | if (err) |
29 | if (err) | 64 | scsi_device_resume(to_scsi_device(dev)); |
30 | scsi_device_resume(to_scsi_device(dev)); | ||
31 | } | ||
32 | } | 65 | } |
33 | dev_dbg(dev, "scsi suspend: %d\n", err); | 66 | dev_dbg(dev, "scsi suspend: %d\n", err); |
34 | return err; | 67 | return err; |
35 | } | 68 | } |
36 | 69 | ||
37 | static int scsi_dev_type_resume(struct device *dev, int (*cb)(struct device *)) | 70 | static int scsi_dev_type_resume(struct device *dev, |
71 | int (*cb)(struct device *, const struct dev_pm_ops *)) | ||
38 | { | 72 | { |
73 | const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; | ||
39 | int err = 0; | 74 | int err = 0; |
40 | 75 | ||
41 | if (cb) | 76 | err = cb(dev, pm); |
42 | err = cb(dev); | ||
43 | scsi_device_resume(to_scsi_device(dev)); | 77 | scsi_device_resume(to_scsi_device(dev)); |
44 | dev_dbg(dev, "scsi resume: %d\n", err); | 78 | dev_dbg(dev, "scsi resume: %d\n", err); |
79 | |||
80 | if (err == 0) { | ||
81 | pm_runtime_disable(dev); | ||
82 | pm_runtime_set_active(dev); | ||
83 | pm_runtime_enable(dev); | ||
84 | } | ||
85 | |||
45 | return err; | 86 | return err; |
46 | } | 87 | } |
47 | 88 | ||
48 | static int | 89 | static int |
49 | scsi_bus_suspend_common(struct device *dev, int (*cb)(struct device *)) | 90 | scsi_bus_suspend_common(struct device *dev, |
91 | int (*cb)(struct device *, const struct dev_pm_ops *)) | ||
50 | { | 92 | { |
51 | int err = 0; | 93 | int err = 0; |
52 | 94 | ||
@@ -66,20 +108,54 @@ scsi_bus_suspend_common(struct device *dev, int (*cb)(struct device *)) | |||
66 | return err; | 108 | return err; |
67 | } | 109 | } |
68 | 110 | ||
69 | static int | 111 | static void async_sdev_resume(void *dev, async_cookie_t cookie) |
70 | scsi_bus_resume_common(struct device *dev, int (*cb)(struct device *)) | ||
71 | { | 112 | { |
72 | int err = 0; | 113 | scsi_dev_type_resume(dev, do_scsi_resume); |
114 | } | ||
73 | 115 | ||
74 | if (scsi_is_sdev_device(dev)) | 116 | static void async_sdev_thaw(void *dev, async_cookie_t cookie) |
75 | err = scsi_dev_type_resume(dev, cb); | 117 | { |
118 | scsi_dev_type_resume(dev, do_scsi_thaw); | ||
119 | } | ||
76 | 120 | ||
77 | if (err == 0) { | 121 | static void async_sdev_restore(void *dev, async_cookie_t cookie) |
122 | { | ||
123 | scsi_dev_type_resume(dev, do_scsi_restore); | ||
124 | } | ||
125 | |||
126 | static int scsi_bus_resume_common(struct device *dev, | ||
127 | int (*cb)(struct device *, const struct dev_pm_ops *)) | ||
128 | { | ||
129 | async_func_t fn; | ||
130 | |||
131 | if (!scsi_is_sdev_device(dev)) | ||
132 | fn = NULL; | ||
133 | else if (cb == do_scsi_resume) | ||
134 | fn = async_sdev_resume; | ||
135 | else if (cb == do_scsi_thaw) | ||
136 | fn = async_sdev_thaw; | ||
137 | else if (cb == do_scsi_restore) | ||
138 | fn = async_sdev_restore; | ||
139 | else | ||
140 | fn = NULL; | ||
141 | |||
142 | if (fn) { | ||
143 | async_schedule_domain(fn, dev, &scsi_sd_pm_domain); | ||
144 | |||
145 | /* | ||
146 | * If a user has disabled async probing a likely reason | ||
147 | * is due to a storage enclosure that does not inject | ||
148 | * staggered spin-ups. For safety, make resume | ||
149 | * synchronous as well in that case. | ||
150 | */ | ||
151 | if (strncmp(scsi_scan_type, "async", 5) != 0) | ||
152 | async_synchronize_full_domain(&scsi_sd_pm_domain); | ||
153 | } else { | ||
78 | pm_runtime_disable(dev); | 154 | pm_runtime_disable(dev); |
79 | pm_runtime_set_active(dev); | 155 | pm_runtime_set_active(dev); |
80 | pm_runtime_enable(dev); | 156 | pm_runtime_enable(dev); |
81 | } | 157 | } |
82 | return err; | 158 | return 0; |
83 | } | 159 | } |
84 | 160 | ||
85 | static int scsi_bus_prepare(struct device *dev) | 161 | static int scsi_bus_prepare(struct device *dev) |
@@ -97,38 +173,32 @@ static int scsi_bus_prepare(struct device *dev) | |||
97 | 173 | ||
98 | static int scsi_bus_suspend(struct device *dev) | 174 | static int scsi_bus_suspend(struct device *dev) |
99 | { | 175 | { |
100 | const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; | 176 | return scsi_bus_suspend_common(dev, do_scsi_suspend); |
101 | return scsi_bus_suspend_common(dev, pm ? pm->suspend : NULL); | ||
102 | } | 177 | } |
103 | 178 | ||
104 | static int scsi_bus_resume(struct device *dev) | 179 | static int scsi_bus_resume(struct device *dev) |
105 | { | 180 | { |
106 | const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; | 181 | return scsi_bus_resume_common(dev, do_scsi_resume); |
107 | return scsi_bus_resume_common(dev, pm ? pm->resume : NULL); | ||
108 | } | 182 | } |
109 | 183 | ||
110 | static int scsi_bus_freeze(struct device *dev) | 184 | static int scsi_bus_freeze(struct device *dev) |
111 | { | 185 | { |
112 | const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; | 186 | return scsi_bus_suspend_common(dev, do_scsi_freeze); |
113 | return scsi_bus_suspend_common(dev, pm ? pm->freeze : NULL); | ||
114 | } | 187 | } |
115 | 188 | ||
116 | static int scsi_bus_thaw(struct device *dev) | 189 | static int scsi_bus_thaw(struct device *dev) |
117 | { | 190 | { |
118 | const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; | 191 | return scsi_bus_resume_common(dev, do_scsi_thaw); |
119 | return scsi_bus_resume_common(dev, pm ? pm->thaw : NULL); | ||
120 | } | 192 | } |
121 | 193 | ||
122 | static int scsi_bus_poweroff(struct device *dev) | 194 | static int scsi_bus_poweroff(struct device *dev) |
123 | { | 195 | { |
124 | const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; | 196 | return scsi_bus_suspend_common(dev, do_scsi_poweroff); |
125 | return scsi_bus_suspend_common(dev, pm ? pm->poweroff : NULL); | ||
126 | } | 197 | } |
127 | 198 | ||
128 | static int scsi_bus_restore(struct device *dev) | 199 | static int scsi_bus_restore(struct device *dev) |
129 | { | 200 | { |
130 | const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; | 201 | return scsi_bus_resume_common(dev, do_scsi_restore); |
131 | return scsi_bus_resume_common(dev, pm ? pm->restore : NULL); | ||
132 | } | 202 | } |
133 | 203 | ||
134 | #else /* CONFIG_PM_SLEEP */ | 204 | #else /* CONFIG_PM_SLEEP */ |