Commit d690b2cd222afc75320b9b8e9da7df02e9e630ca

Authored by Rafael J. Wysocki
1 parent 87d1b3e60b

PM: Provide generic subsystem-level callbacks

There are subsystems whose power management callbacks only need to
invoke the callbacks provided by device drivers.  Still, their system
sleep PM callbacks should play well with the runtime PM callbacks,
so that devices suspended at run time can be left in that state for
a system sleep transition.

Provide a set of generic PM callbacks for such subsystems and
define convenience macros for populating dev_pm_ops structures.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>

Showing 5 changed files with 350 additions and 6 deletions Side-by-side Diff

Documentation/power/runtime_pm.txt
... ... @@ -335,6 +335,10 @@
335 335 'power.runtime_error' is set or 'power.disable_depth' is greater than
336 336 zero)
337 337  
  338 + bool pm_runtime_suspended(struct device *dev);
  339 + - return true if the device's runtime PM status is 'suspended', or false
  340 + otherwise
  341 +
338 342 void pm_runtime_allow(struct device *dev);
339 343 - set the power.runtime_auto flag for the device and decrease its usage
340 344 counter (used by the /sys/devices/.../power/control interface to
... ... @@ -459,4 +463,65 @@
459 463 ->prepare() callback and decrements it after calling the ->complete() callback.
460 464 Hence disabling run-time PM temporarily like this will not cause any run-time
461 465 suspend callbacks to be lost.
  466 +
  467 +7. Generic subsystem callbacks
  468 +
  469 +Subsystems may wish to conserve code space by using the set of generic power
  470 +management callbacks provided by the PM core, defined in
  471 +driver/base/power/generic_ops.c:
  472 +
  473 + int pm_generic_runtime_idle(struct device *dev);
  474 + - invoke the ->runtime_idle() callback provided by the driver of this
  475 + device, if defined, and call pm_runtime_suspend() for this device if the
  476 + return value is 0 or the callback is not defined
  477 +
  478 + int pm_generic_runtime_suspend(struct device *dev);
  479 + - invoke the ->runtime_suspend() callback provided by the driver of this
  480 + device and return its result, or return -EINVAL if not defined
  481 +
  482 + int pm_generic_runtime_resume(struct device *dev);
  483 + - invoke the ->runtime_resume() callback provided by the driver of this
  484 + device and return its result, or return -EINVAL if not defined
  485 +
  486 + int pm_generic_suspend(struct device *dev);
  487 + - if the device has not been suspended at run time, invoke the ->suspend()
  488 + callback provided by its driver and return its result, or return 0 if not
  489 + defined
  490 +
  491 + int pm_generic_resume(struct device *dev);
  492 + - invoke the ->resume() callback provided by the driver of this device and,
  493 + if successful, change the device's runtime PM status to 'active'
  494 +
  495 + int pm_generic_freeze(struct device *dev);
  496 + - if the device has not been suspended at run time, invoke the ->freeze()
  497 + callback provided by its driver and return its result, or return 0 if not
  498 + defined
  499 +
  500 + int pm_generic_thaw(struct device *dev);
  501 + - if the device has not been suspended at run time, invoke the ->thaw()
  502 + callback provided by its driver and return its result, or return 0 if not
  503 + defined
  504 +
  505 + int pm_generic_poweroff(struct device *dev);
  506 + - if the device has not been suspended at run time, invoke the ->poweroff()
  507 + callback provided by its driver and return its result, or return 0 if not
  508 + defined
  509 +
  510 + int pm_generic_restore(struct device *dev);
  511 + - invoke the ->restore() callback provided by the driver of this device and,
  512 + if successful, change the device's runtime PM status to 'active'
  513 +
  514 +These functions can be assigned to the ->runtime_idle(), ->runtime_suspend(),
  515 +->runtime_resume(), ->suspend(), ->resume(), ->freeze(), ->thaw(), ->poweroff(),
  516 +or ->restore() callback pointers in the subsystem-level dev_pm_ops structures.
  517 +
  518 +If a subsystem wishes to use all of them at the same time, it can simply assign
  519 +the GENERIC_SUBSYS_PM_OPS macro, defined in include/linux/pm.h, to its
  520 +dev_pm_ops structure pointer.
  521 +
  522 +Device drivers that wish to use the same function as a system suspend, freeze,
  523 +poweroff and run-time suspend callback, and similarly for system resume, thaw,
  524 +restore, and run-time resume, can achieve this with the help of the
  525 +UNIVERSAL_DEV_PM_OPS macro defined in include/linux/pm.h (possibly setting its
  526 +last argument to NULL).
drivers/base/power/Makefile
1 1 obj-$(CONFIG_PM) += sysfs.o
2 2 obj-$(CONFIG_PM_SLEEP) += main.o
3 3 obj-$(CONFIG_PM_RUNTIME) += runtime.o
  4 +obj-$(CONFIG_PM_OPS) += generic_ops.o
4 5 obj-$(CONFIG_PM_TRACE_RTC) += trace.o
5 6  
6 7 ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
drivers/base/power/generic_ops.c
  1 +/*
  2 + * drivers/base/power/generic_ops.c - Generic PM callbacks for subsystems
  3 + *
  4 + * Copyright (c) 2010 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
  5 + *
  6 + * This file is released under the GPLv2.
  7 + */
  8 +
  9 +#include <linux/pm.h>
  10 +#include <linux/pm_runtime.h>
  11 +
  12 +#ifdef CONFIG_PM_RUNTIME
  13 +/**
  14 + * pm_generic_runtime_idle - Generic runtime idle callback for subsystems.
  15 + * @dev: Device to handle.
  16 + *
  17 + * If PM operations are defined for the @dev's driver and they include
  18 + * ->runtime_idle(), execute it and return its error code, if nonzero.
  19 + * Otherwise, execute pm_runtime_suspend() for the device and return 0.
  20 + */
  21 +int pm_generic_runtime_idle(struct device *dev)
  22 +{
  23 + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
  24 +
  25 + if (pm && pm->runtime_idle) {
  26 + int ret = pm->runtime_idle(dev);
  27 + if (ret)
  28 + return ret;
  29 + }
  30 +
  31 + pm_runtime_suspend(dev);
  32 + return 0;
  33 +}
  34 +EXPORT_SYMBOL_GPL(pm_generic_runtime_idle);
  35 +
  36 +/**
  37 + * pm_generic_runtime_suspend - Generic runtime suspend callback for subsystems.
  38 + * @dev: Device to suspend.
  39 + *
  40 + * If PM operations are defined for the @dev's driver and they include
  41 + * ->runtime_suspend(), execute it and return its error code. Otherwise,
  42 + * return -EINVAL.
  43 + */
  44 +int pm_generic_runtime_suspend(struct device *dev)
  45 +{
  46 + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
  47 + int ret;
  48 +
  49 + ret = pm && pm->runtime_suspend ? pm->runtime_suspend(dev) : -EINVAL;
  50 +
  51 + return ret;
  52 +}
  53 +EXPORT_SYMBOL_GPL(pm_generic_runtime_suspend);
  54 +
  55 +/**
  56 + * pm_generic_runtime_resume - Generic runtime resume callback for subsystems.
  57 + * @dev: Device to resume.
  58 + *
  59 + * If PM operations are defined for the @dev's driver and they include
  60 + * ->runtime_resume(), execute it and return its error code. Otherwise,
  61 + * return -EINVAL.
  62 + */
  63 +int pm_generic_runtime_resume(struct device *dev)
  64 +{
  65 + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
  66 + int ret;
  67 +
  68 + ret = pm && pm->runtime_resume ? pm->runtime_resume(dev) : -EINVAL;
  69 +
  70 + return ret;
  71 +}
  72 +EXPORT_SYMBOL_GPL(pm_generic_runtime_resume);
  73 +#endif /* CONFIG_PM_RUNTIME */
  74 +
  75 +#ifdef CONFIG_PM_SLEEP
  76 +/**
  77 + * __pm_generic_call - Generic suspend/freeze/poweroff/thaw subsystem callback.
  78 + * @dev: Device to handle.
  79 + * @event: PM transition of the system under way.
  80 + *
  81 + * If the device has not been suspended at run time, execute the
  82 + * suspend/freeze/poweroff/thaw callback provided by its driver, if defined, and
  83 + * return its error code. Otherwise, return zero.
  84 + */
  85 +static int __pm_generic_call(struct device *dev, int event)
  86 +{
  87 + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
  88 + int (*callback)(struct device *);
  89 +
  90 + if (!pm || pm_runtime_suspended(dev))
  91 + return 0;
  92 +
  93 + switch (event) {
  94 + case PM_EVENT_SUSPEND:
  95 + callback = pm->suspend;
  96 + break;
  97 + case PM_EVENT_FREEZE:
  98 + callback = pm->freeze;
  99 + break;
  100 + case PM_EVENT_HIBERNATE:
  101 + callback = pm->poweroff;
  102 + break;
  103 + case PM_EVENT_THAW:
  104 + callback = pm->thaw;
  105 + break;
  106 + default:
  107 + callback = NULL;
  108 + break;
  109 + }
  110 +
  111 + return callback ? callback(dev) : 0;
  112 +}
  113 +
  114 +/**
  115 + * pm_generic_suspend - Generic suspend callback for subsystems.
  116 + * @dev: Device to suspend.
  117 + */
  118 +int pm_generic_suspend(struct device *dev)
  119 +{
  120 + return __pm_generic_call(dev, PM_EVENT_SUSPEND);
  121 +}
  122 +EXPORT_SYMBOL_GPL(pm_generic_suspend);
  123 +
  124 +/**
  125 + * pm_generic_freeze - Generic freeze callback for subsystems.
  126 + * @dev: Device to freeze.
  127 + */
  128 +int pm_generic_freeze(struct device *dev)
  129 +{
  130 + return __pm_generic_call(dev, PM_EVENT_FREEZE);
  131 +}
  132 +EXPORT_SYMBOL_GPL(pm_generic_freeze);
  133 +
  134 +/**
  135 + * pm_generic_poweroff - Generic poweroff callback for subsystems.
  136 + * @dev: Device to handle.
  137 + */
  138 +int pm_generic_poweroff(struct device *dev)
  139 +{
  140 + return __pm_generic_call(dev, PM_EVENT_HIBERNATE);
  141 +}
  142 +EXPORT_SYMBOL_GPL(pm_generic_poweroff);
  143 +
  144 +/**
  145 + * pm_generic_thaw - Generic thaw callback for subsystems.
  146 + * @dev: Device to thaw.
  147 + */
  148 +int pm_generic_thaw(struct device *dev)
  149 +{
  150 + return __pm_generic_call(dev, PM_EVENT_THAW);
  151 +}
  152 +EXPORT_SYMBOL_GPL(pm_generic_thaw);
  153 +
  154 +/**
  155 + * __pm_generic_resume - Generic resume/restore callback for subsystems.
  156 + * @dev: Device to handle.
  157 + * @event: PM transition of the system under way.
  158 + *
  159 + * Execute the resume/resotre callback provided by the @dev's driver, if
  160 + * defined. If it returns 0, change the device's runtime PM status to 'active'.
  161 + * Return the callback's error code.
  162 + */
  163 +static int __pm_generic_resume(struct device *dev, int event)
  164 +{
  165 + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
  166 + int (*callback)(struct device *);
  167 + int ret;
  168 +
  169 + if (!pm)
  170 + return 0;
  171 +
  172 + switch (event) {
  173 + case PM_EVENT_RESUME:
  174 + callback = pm->resume;
  175 + break;
  176 + case PM_EVENT_RESTORE:
  177 + callback = pm->restore;
  178 + break;
  179 + default:
  180 + callback = NULL;
  181 + break;
  182 + }
  183 +
  184 + if (!callback)
  185 + return 0;
  186 +
  187 + ret = callback(dev);
  188 + if (!ret) {
  189 + pm_runtime_disable(dev);
  190 + pm_runtime_set_active(dev);
  191 + pm_runtime_enable(dev);
  192 + }
  193 +
  194 + return ret;
  195 +}
  196 +
  197 +/**
  198 + * pm_generic_resume - Generic resume callback for subsystems.
  199 + * @dev: Device to resume.
  200 + */
  201 +int pm_generic_resume(struct device *dev)
  202 +{
  203 + return __pm_generic_resume(dev, PM_EVENT_RESUME);
  204 +}
  205 +EXPORT_SYMBOL_GPL(pm_generic_resume);
  206 +
  207 +/**
  208 + * pm_generic_restore - Generic restore callback for subsystems.
  209 + * @dev: Device to restore.
  210 + */
  211 +int pm_generic_restore(struct device *dev)
  212 +{
  213 + return __pm_generic_resume(dev, PM_EVENT_RESTORE);
  214 +}
  215 +EXPORT_SYMBOL_GPL(pm_generic_restore);
  216 +#endif /* CONFIG_PM_SLEEP */
  217 +
  218 +struct dev_pm_ops generic_subsys_pm_ops = {
  219 +#ifdef CONFIG_PM_SLEEP
  220 + .suspend = pm_generic_suspend,
  221 + .resume = pm_generic_resume,
  222 + .freeze = pm_generic_freeze,
  223 + .thaw = pm_generic_thaw,
  224 + .poweroff = pm_generic_poweroff,
  225 + .restore = pm_generic_restore,
  226 +#endif
  227 +#ifdef CONFIG_PM_RUNTIME
  228 + .runtime_suspend = pm_generic_runtime_suspend,
  229 + .runtime_resume = pm_generic_runtime_resume,
  230 + .runtime_idle = pm_generic_runtime_idle,
  231 +#endif
  232 +};
  233 +EXPORT_SYMBOL_GPL(generic_subsys_pm_ops);
... ... @@ -215,19 +215,58 @@
215 215 int (*runtime_idle)(struct device *dev);
216 216 };
217 217  
  218 +#ifdef CONFIG_PM_SLEEP
  219 +#define SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \
  220 + .suspend = suspend_fn, \
  221 + .resume = resume_fn, \
  222 + .freeze = suspend_fn, \
  223 + .thaw = resume_fn, \
  224 + .poweroff = suspend_fn, \
  225 + .restore = resume_fn,
  226 +#else
  227 +#define SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn)
  228 +#endif
  229 +
  230 +#ifdef CONFIG_PM_RUNTIME
  231 +#define SET_RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn) \
  232 + .runtime_suspend = suspend_fn, \
  233 + .runtime_resume = resume_fn, \
  234 + .runtime_idle = idle_fn,
  235 +#else
  236 +#define SET_RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn)
  237 +#endif
  238 +
218 239 /*
219 240 * Use this if you want to use the same suspend and resume callbacks for suspend
220 241 * to RAM and hibernation.
221 242 */
222 243 #define SIMPLE_DEV_PM_OPS(name, suspend_fn, resume_fn) \
223 244 const struct dev_pm_ops name = { \
224   - .suspend = suspend_fn, \
225   - .resume = resume_fn, \
226   - .freeze = suspend_fn, \
227   - .thaw = resume_fn, \
228   - .poweroff = suspend_fn, \
229   - .restore = resume_fn, \
  245 + SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \
230 246 }
  247 +
  248 +/*
  249 + * Use this for defining a set of PM operations to be used in all situations
  250 + * (sustem suspend, hibernation or runtime PM).
  251 + */
  252 +#define UNIVERSAL_DEV_PM_OPS(name, suspend_fn, resume_fn, idle_fn) \
  253 +const struct dev_pm_ops name = { \
  254 + SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) \
  255 + SET_RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn) \
  256 +}
  257 +
  258 +/*
  259 + * Use this for subsystems (bus types, device types, device classes) that don't
  260 + * need any special suspend/resume handling in addition to invoking the PM
  261 + * callbacks provided by device drivers supporting both the system sleep PM and
  262 + * runtime PM, make the pm member point to generic_subsys_pm_ops.
  263 + */
  264 +#ifdef CONFIG_PM_OPS
  265 +extern struct dev_pm_ops generic_subsys_pm_ops;
  266 +#define GENERIC_SUBSYS_PM_OPS (&generic_subsys_pm_ops)
  267 +#else
  268 +#define GENERIC_SUBSYS_PM_OPS NULL
  269 +#endif
231 270  
232 271 /**
233 272 * PM_EVENT_ messages
include/linux/pm_runtime.h
... ... @@ -62,6 +62,11 @@
62 62 dev->power.run_wake = enable;
63 63 }
64 64  
  65 +static inline bool pm_runtime_suspended(struct device *dev)
  66 +{
  67 + return dev->power.runtime_status == RPM_SUSPENDED;
  68 +}
  69 +
65 70 #else /* !CONFIG_PM_RUNTIME */
66 71  
67 72 static inline int pm_runtime_idle(struct device *dev) { return -ENOSYS; }
... ... @@ -89,6 +94,7 @@
89 94 static inline void pm_runtime_put_noidle(struct device *dev) {}
90 95 static inline bool device_run_wake(struct device *dev) { return false; }
91 96 static inline void device_set_run_wake(struct device *dev, bool enable) {}
  97 +static inline bool pm_runtime_suspended(struct device *dev) { return false; }
92 98  
93 99 #endif /* !CONFIG_PM_RUNTIME */
94 100