Commit 2f60ba706bd9af84c4eab704243b262e69556f2e
1 parent
ed77134bfc
Exists in
master
and in
4 other branches
i2c: Fix bus-level power management callbacks
There are three issues with the i2c bus type's power management callbacks at the moment. First, they don't include any hibernate callbacks, although they should at least include the .restore() callback (there's no guarantee that the driver will be present in memory before loading the image kernel and we must restore the pre-hibernation state of the device). Second, the "legacy" callbacks are not going to be invoked by the PM core since the bus type's pm object is not NULL. Finally, the system sleep PM (ie. suspend/resume) callbacks don't check if the device has been already suspended at run time, in which case they should skip suspending it. Also, it looks like the i2c bus type can use the generic subsystem-level runtime PM callbacks. For these reasons, rework the system sleep PM callbacks provided by the i2c bus type to handle hibernation correctly and to invoke the "legacy" callbacks for drivers that provide them. In addition to that make the i2c bus type use the generic subsystem-level runtime PM callbacks. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Acked-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Acked-by: Jean Delvare <khali@linux-fr.org>
Showing 2 changed files with 104 additions and 69 deletions Side-by-side Diff
drivers/i2c/i2c-core.c
... | ... | @@ -159,107 +159,131 @@ |
159 | 159 | driver->shutdown(client); |
160 | 160 | } |
161 | 161 | |
162 | -#ifdef CONFIG_SUSPEND | |
163 | -static int i2c_device_pm_suspend(struct device *dev) | |
162 | +#ifdef CONFIG_PM_SLEEP | |
163 | +static int i2c_legacy_suspend(struct device *dev, pm_message_t mesg) | |
164 | 164 | { |
165 | - const struct dev_pm_ops *pm; | |
165 | + struct i2c_client *client = i2c_verify_client(dev); | |
166 | + struct i2c_driver *driver; | |
166 | 167 | |
167 | - if (!dev->driver) | |
168 | + if (!client || !dev->driver) | |
168 | 169 | return 0; |
169 | - pm = dev->driver->pm; | |
170 | - if (!pm || !pm->suspend) | |
170 | + driver = to_i2c_driver(dev->driver); | |
171 | + if (!driver->suspend) | |
171 | 172 | return 0; |
172 | - return pm->suspend(dev); | |
173 | + return driver->suspend(client, mesg); | |
173 | 174 | } |
174 | 175 | |
175 | -static int i2c_device_pm_resume(struct device *dev) | |
176 | +static int i2c_legacy_resume(struct device *dev) | |
176 | 177 | { |
177 | - const struct dev_pm_ops *pm; | |
178 | + struct i2c_client *client = i2c_verify_client(dev); | |
179 | + struct i2c_driver *driver; | |
178 | 180 | |
179 | - if (!dev->driver) | |
181 | + if (!client || !dev->driver) | |
180 | 182 | return 0; |
181 | - pm = dev->driver->pm; | |
182 | - if (!pm || !pm->resume) | |
183 | + driver = to_i2c_driver(dev->driver); | |
184 | + if (!driver->resume) | |
183 | 185 | return 0; |
184 | - return pm->resume(dev); | |
186 | + return driver->resume(client); | |
185 | 187 | } |
186 | -#else | |
187 | -#define i2c_device_pm_suspend NULL | |
188 | -#define i2c_device_pm_resume NULL | |
189 | -#endif | |
190 | 188 | |
191 | -#ifdef CONFIG_PM_RUNTIME | |
192 | -static int i2c_device_runtime_suspend(struct device *dev) | |
189 | +static int i2c_device_pm_suspend(struct device *dev) | |
193 | 190 | { |
194 | - const struct dev_pm_ops *pm; | |
191 | + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; | |
195 | 192 | |
196 | - if (!dev->driver) | |
193 | + if (pm_runtime_suspended(dev)) | |
197 | 194 | return 0; |
198 | - pm = dev->driver->pm; | |
199 | - if (!pm || !pm->runtime_suspend) | |
200 | - return 0; | |
201 | - return pm->runtime_suspend(dev); | |
202 | -} | |
203 | 195 | |
204 | -static int i2c_device_runtime_resume(struct device *dev) | |
205 | -{ | |
206 | - const struct dev_pm_ops *pm; | |
196 | + if (pm) | |
197 | + return pm->suspend ? pm->suspend(dev) : 0; | |
207 | 198 | |
208 | - if (!dev->driver) | |
209 | - return 0; | |
210 | - pm = dev->driver->pm; | |
211 | - if (!pm || !pm->runtime_resume) | |
212 | - return 0; | |
213 | - return pm->runtime_resume(dev); | |
199 | + return i2c_legacy_suspend(dev, PMSG_SUSPEND); | |
214 | 200 | } |
215 | 201 | |
216 | -static int i2c_device_runtime_idle(struct device *dev) | |
202 | +static int i2c_device_pm_resume(struct device *dev) | |
217 | 203 | { |
218 | - const struct dev_pm_ops *pm = NULL; | |
204 | + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; | |
219 | 205 | int ret; |
220 | 206 | |
221 | - if (dev->driver) | |
222 | - pm = dev->driver->pm; | |
223 | - if (pm && pm->runtime_idle) { | |
224 | - ret = pm->runtime_idle(dev); | |
225 | - if (ret) | |
226 | - return ret; | |
207 | + if (pm) | |
208 | + ret = pm->resume ? pm->resume(dev) : 0; | |
209 | + else | |
210 | + ret = i2c_legacy_resume(dev); | |
211 | + | |
212 | + if (!ret) { | |
213 | + pm_runtime_disable(dev); | |
214 | + pm_runtime_set_active(dev); | |
215 | + pm_runtime_enable(dev); | |
227 | 216 | } |
228 | 217 | |
229 | - return pm_runtime_suspend(dev); | |
218 | + return ret; | |
230 | 219 | } |
231 | -#else | |
232 | -#define i2c_device_runtime_suspend NULL | |
233 | -#define i2c_device_runtime_resume NULL | |
234 | -#define i2c_device_runtime_idle NULL | |
235 | -#endif | |
236 | 220 | |
237 | -static int i2c_device_suspend(struct device *dev, pm_message_t mesg) | |
221 | +static int i2c_device_pm_freeze(struct device *dev) | |
238 | 222 | { |
239 | - struct i2c_client *client = i2c_verify_client(dev); | |
240 | - struct i2c_driver *driver; | |
223 | + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; | |
241 | 224 | |
242 | - if (!client || !dev->driver) | |
225 | + if (pm_runtime_suspended(dev)) | |
243 | 226 | return 0; |
244 | - driver = to_i2c_driver(dev->driver); | |
245 | - if (!driver->suspend) | |
227 | + | |
228 | + if (pm) | |
229 | + return pm->freeze ? pm->freeze(dev) : 0; | |
230 | + | |
231 | + return i2c_legacy_suspend(dev, PMSG_FREEZE); | |
232 | +} | |
233 | + | |
234 | +static int i2c_device_pm_thaw(struct device *dev) | |
235 | +{ | |
236 | + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; | |
237 | + | |
238 | + if (pm_runtime_suspended(dev)) | |
246 | 239 | return 0; |
247 | - return driver->suspend(client, mesg); | |
240 | + | |
241 | + if (pm) | |
242 | + return pm->thaw ? pm->thaw(dev) : 0; | |
243 | + | |
244 | + return i2c_legacy_resume(dev); | |
248 | 245 | } |
249 | 246 | |
250 | -static int i2c_device_resume(struct device *dev) | |
247 | +static int i2c_device_pm_poweroff(struct device *dev) | |
251 | 248 | { |
252 | - struct i2c_client *client = i2c_verify_client(dev); | |
253 | - struct i2c_driver *driver; | |
249 | + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; | |
254 | 250 | |
255 | - if (!client || !dev->driver) | |
251 | + if (pm_runtime_suspended(dev)) | |
256 | 252 | return 0; |
257 | - driver = to_i2c_driver(dev->driver); | |
258 | - if (!driver->resume) | |
259 | - return 0; | |
260 | - return driver->resume(client); | |
253 | + | |
254 | + if (pm) | |
255 | + return pm->poweroff ? pm->poweroff(dev) : 0; | |
256 | + | |
257 | + return i2c_legacy_suspend(dev, PMSG_HIBERNATE); | |
261 | 258 | } |
262 | 259 | |
260 | +static int i2c_device_pm_restore(struct device *dev) | |
261 | +{ | |
262 | + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; | |
263 | + int ret; | |
264 | + | |
265 | + if (pm) | |
266 | + ret = pm->restore ? pm->restore(dev) : 0; | |
267 | + else | |
268 | + ret = i2c_legacy_resume(dev); | |
269 | + | |
270 | + if (!ret) { | |
271 | + pm_runtime_disable(dev); | |
272 | + pm_runtime_set_active(dev); | |
273 | + pm_runtime_enable(dev); | |
274 | + } | |
275 | + | |
276 | + return ret; | |
277 | +} | |
278 | +#else /* !CONFIG_PM_SLEEP */ | |
279 | +#define i2c_device_pm_suspend NULL | |
280 | +#define i2c_device_pm_resume NULL | |
281 | +#define i2c_device_pm_freeze NULL | |
282 | +#define i2c_device_pm_thaw NULL | |
283 | +#define i2c_device_pm_poweroff NULL | |
284 | +#define i2c_device_pm_restore NULL | |
285 | +#endif /* !CONFIG_PM_SLEEP */ | |
286 | + | |
263 | 287 | static void i2c_client_dev_release(struct device *dev) |
264 | 288 | { |
265 | 289 | kfree(to_i2c_client(dev)); |
... | ... | @@ -301,9 +325,15 @@ |
301 | 325 | static const struct dev_pm_ops i2c_device_pm_ops = { |
302 | 326 | .suspend = i2c_device_pm_suspend, |
303 | 327 | .resume = i2c_device_pm_resume, |
304 | - .runtime_suspend = i2c_device_runtime_suspend, | |
305 | - .runtime_resume = i2c_device_runtime_resume, | |
306 | - .runtime_idle = i2c_device_runtime_idle, | |
328 | + .freeze = i2c_device_pm_freeze, | |
329 | + .thaw = i2c_device_pm_thaw, | |
330 | + .poweroff = i2c_device_pm_poweroff, | |
331 | + .restore = i2c_device_pm_restore, | |
332 | + SET_RUNTIME_PM_OPS( | |
333 | + pm_generic_runtime_suspend, | |
334 | + pm_generic_runtime_resume, | |
335 | + pm_generic_runtime_idle | |
336 | + ) | |
307 | 337 | }; |
308 | 338 | |
309 | 339 | struct bus_type i2c_bus_type = { |
... | ... | @@ -312,8 +342,6 @@ |
312 | 342 | .probe = i2c_device_probe, |
313 | 343 | .remove = i2c_device_remove, |
314 | 344 | .shutdown = i2c_device_shutdown, |
315 | - .suspend = i2c_device_suspend, | |
316 | - .resume = i2c_device_resume, | |
317 | 345 | .pm = &i2c_device_pm_ops, |
318 | 346 | }; |
319 | 347 | EXPORT_SYMBOL_GPL(i2c_bus_type); |
include/linux/pm_runtime.h
... | ... | @@ -30,6 +30,9 @@ |
30 | 30 | extern void __pm_runtime_disable(struct device *dev, bool check_resume); |
31 | 31 | extern void pm_runtime_allow(struct device *dev); |
32 | 32 | extern void pm_runtime_forbid(struct device *dev); |
33 | +extern int pm_generic_runtime_idle(struct device *dev); | |
34 | +extern int pm_generic_runtime_suspend(struct device *dev); | |
35 | +extern int pm_generic_runtime_resume(struct device *dev); | |
33 | 36 | |
34 | 37 | static inline bool pm_children_suspended(struct device *dev) |
35 | 38 | { |
... | ... | @@ -95,6 +98,10 @@ |
95 | 98 | static inline bool device_run_wake(struct device *dev) { return false; } |
96 | 99 | static inline void device_set_run_wake(struct device *dev, bool enable) {} |
97 | 100 | static inline bool pm_runtime_suspended(struct device *dev) { return false; } |
101 | + | |
102 | +static inline int pm_generic_runtime_idle(struct device *dev) { return 0; } | |
103 | +static inline int pm_generic_runtime_suspend(struct device *dev) { return 0; } | |
104 | +static inline int pm_generic_runtime_resume(struct device *dev) { return 0; } | |
98 | 105 | |
99 | 106 | #endif /* !CONFIG_PM_RUNTIME */ |
100 | 107 |