Commit 2f60ba706bd9af84c4eab704243b262e69556f2e

Authored by Rafael J. Wysocki
1 parent ed77134bfc

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