[PATCH 11/11] PM: runtime: Add Example Driver Patterns section
From: Brian Norris <briannorris@chromium.org>
Date: 2026-09-04 21:20:47
Also in:
lkml
Subsystem:
suspend to ram, the rest · Maintainers:
"Rafael J. Wysocki", Linus Torvalds
The runtime PM API surface is pretty large, but there are a few common patterns that many drivers should follow. Add some illustrative examples, to help guide the most common audience for runtime PM docs -- driver writers. Signed-off-by: Brian Norris <briannorris@chromium.org> --- Documentation/power/runtime_pm.rst | 349 ++++++++++++++++++++++++++++- 1 file changed, 348 insertions(+), 1 deletion(-)
diff --git a/Documentation/power/runtime_pm.rst b/Documentation/power/runtime_pm.rst
index d34f846ec822..74df4e822fb3 100644
--- a/Documentation/power/runtime_pm.rst
+++ b/Documentation/power/runtime_pm.rst@@ -625,7 +625,9 @@ The implementation is well suited for asynchronous use in interrupt contexts. However such use inevitably involves races, because the PM core can't synchronize ->runtime_suspend() callbacks with the arrival of I/O requests. This synchronization must be handled by the driver, using its private lock. -Here is a schematic pseudo-code example:: +Here is a schematic pseudo-code example: + +.. code-block:: c foo_read_or_write(struct foo_priv *foo, void *data) {
@@ -691,3 +693,348 @@ pm_runtime_autosuspend_expiration() from within the ->runtime_suspend() callback while holding its private lock. If the function returns a nonzero value then the delay has not yet expired and the callback should return -EAGAIN. + +.. _Section 10: + +10. Example Driver Patterns +=========================== + +The runtime PM API is large and complex, but most device drivers follow a small +set of canonical patterns when interacting with runtime PM. This section +illustrates standard patterns for device probing, performing I/O, and handling +interrupts. + +Probe and Initialization +------------------------ + +Basic Probe +~~~~~~~~~~~ + +A driver that powers on its hardware during probe and does not use autosuspend +can initialize runtime PM using device-managed helpers: + +.. code-block:: c + + static int foo_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; + struct foo_priv *priv; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + /* Power on and initialize hardware registers... */ + + /* + * The code above left hardware powered on and operational, so + * tell the PM core that the device is active before enabling + * runtime PM. + */ + pm_runtime_set_active(dev); + + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + + /* + * Alternatively, the above two calls can be combined into: + * ret = devm_pm_runtime_set_active_enabled(dev); + * if (ret) + * return ret; + */ + + /* + * Upon successful return from ->probe(), the driver core + * automatically executes pm_request_idle(dev), allowing the + * device to suspend asynchronously if its usage counter is zero. + */ + return 0; + } + +Probe with Hardware Powered Off +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Many drivers prefer to keep hardware powered off or in low power until +actually needed, avoiding duplicate power sequencing logic between ->probe() +and ->runtime_resume(). Because the initial runtime PM state of a device is +suspended by default, the driver can enable runtime PM directly and rely on +pm_runtime_resume_and_get() to trigger the ->runtime_resume() callback when +probe needs to access hardware: + +.. code-block:: c + + static int foo_runtime_suspend(struct device *dev) + { + struct foo_priv *priv = dev_get_drvdata(dev); + + clk_disable_unprepare(priv->clk); + regulator_disable(priv->supply); + + return 0; + } + + static int foo_runtime_resume(struct device *dev) + { + struct foo_priv *priv = dev_get_drvdata(dev); + int ret; + + ret = regulator_enable(priv->supply); + if (ret) + return ret; + + ret = clk_prepare_enable(priv->clk); + if (ret) { + regulator_disable(priv->supply); + return ret; + } + + return 0; + } + + static int foo_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; + struct foo_priv *priv; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + + /* Acquire regulators, clocks, GPIOs, and register map... */ + + /* + * Hardware starts powered off. The default state is + * RPM_SUSPENDED, so no need for: + * pm_runtime_set_suspended(dev); + */ + + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + + /* + * Power on the device via ->runtime_resume() to verify device + * ID or perform initial hardware configuration. + */ + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + + ret = foo_verify_hardware_id(priv); + if (ret) { + pm_runtime_put(dev); + return ret; + } + + /* + * Drop the usage counter, allowing ->runtime_suspend() to + * power off the device until an I/O request arrives. + */ + pm_runtime_put(dev); + + return 0; + } + +Note that this pattern requires ``CONFIG_PM``. When ``CONFIG_PM`` is +disabled, pm_runtime_resume_and_get() returns 0 without calling +->runtime_resume(), leaving hardware unpowered. Drivers using this pattern +should typically depend on ``CONFIG_PM``. + +Autosuspend Probe +~~~~~~~~~~~~~~~~~ + +If the driver uses autosuspend, it configures the autosuspend delay and enables +autosuspend before enabling runtime PM: + +.. code-block:: c + + static int foo_probe(struct platform_device *pdev) + { + struct device *dev = &pdev->dev; + struct foo_priv *priv; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + /* Power on and initialize hardware registers... */ + + /* Set autosuspend delay (e.g. 2000 ms) and enable autosuspend */ + pm_runtime_set_autosuspend_delay(dev, 2000); + pm_runtime_use_autosuspend(dev); + + pm_runtime_set_active(dev); + + /* + * devm_pm_runtime_enable() ensures that pm_runtime_disable() + * and pm_runtime_dont_use_autosuspend() are called upon driver + * unbind. + */ + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + + /* + * Update last busy timestamp so the driver core's post-probe + * pm_request_idle() respects the autosuspend delay. + */ + pm_runtime_mark_last_busy(dev); + + return 0; + } + +Performing I/O Operations +------------------------- + +Before accessing hardware registers or initiating I/O transfers, drivers must +ensure the device is active by calling pm_runtime_resume_and_get() or similar. + +Basic I/O +~~~~~~~~~ + +For devices without autosuspend, work completion is signaled with +pm_runtime_put(), which drops the usage counter and queues an asynchronous idle +check once the counter reaches zero: + +.. code-block:: c + + int foo_do_transfer(struct foo_priv *priv, void *buf, size_t count) + { + int ret; + + ret = pm_runtime_resume_and_get(priv->dev); + if (ret < 0) + return ret; + + /* Access hardware registers or perform data transfer... */ + ret = foo_hardware_transfer(priv, buf, count); + + /* + * Drop usage counter and request asynchronous idle check (and + * suspend, if possible). + */ + pm_runtime_put(priv->dev); + + return ret; + } + +Autosuspend I/O +~~~~~~~~~~~~~~~ + +For devices using autosuspend, work completion is signaled with +pm_runtime_put_autosuspend(), which drops the usage counter and defers +suspension until the autosuspend delay expires: + +.. code-block:: c + + int foo_do_transfer(struct foo_priv *priv, void *buf, size_t count) + { + int ret; + + ret = pm_runtime_resume_and_get(priv->dev); + if (ret < 0) + return ret; + + /* Access hardware registers or perform data transfer... */ + ret = foo_hardware_transfer(priv, buf, count); + + /* + * Drop the usage counter and schedule an autosuspend once + * the delay expires. Note that pm_runtime_put_autosuspend() + * updates the last-access timestamp automatically. + */ + pm_runtime_put_autosuspend(priv->dev); + + return ret; + } + +Synchronous Completion +~~~~~~~~~~~~~~~~~~~~~~ + +When immediate suspension is desired -- such as before unregistering a +device or during shutdown -- synchronous put helpers can be used instead of +their asynchronous counterparts. Which helper to use depends on whether +autosuspend is configured: + +* For non-autosuspend devices, use pm_runtime_put_sync(). +* For devices that use autosuspend, use pm_runtime_put_sync_suspend(), which + ignores any configured autosuspend delay and forces immediate suspension. + +However, note several important caveats when relying on synchronous runtime +PM helpers for power-down: + +* **Parents and Suppliers**: While the target device itself is suspended + synchronously, the PM core handles idle notifications for parents and + device link suppliers asynchronously. As a result, parent devices or + power domain suppliers are not guaranteed to be powered off when the + function returns. +* **User Policy ("Forbidden")**: Runtime PM helpers respect system policy. + If user space has set ``/sys/devices/.../power/control`` to ``on`` + (pm_runtime_forbid()), the PM core holds an extra reference on the + device, meaning dropping the driver's usage counter will not trigger a + suspend. + +Because of these constraints, synchronous put helpers may not be suitable +when a driver functionally requires hardware to be powered off +synchronously (for example, to perform a hardware reset or power cycle). +Such requirements may necessitate other methods, such as disabling runtime +PM with pm_runtime_disable() and explicitly executing the hardware +power-down sequence. + +Interrupt Handling with Conditional Get +--------------------------------------- + +Interrupt handlers (especially in atomic or hardirq context) cannot typically +invoke pm_runtime_resume_and_get(), because runtime-resume may sleep. Moreover, +if an interrupt arrives while the device is suspended or transitioning to low +power (e.g., on a shared interrupt line or spurious wakeups), attempting to +read hardware registers could trigger a bus fault or system hang. + +To handle this safely, drivers can conditionally acquire a runtime PM reference +using pm_runtime_get_if_in_use() or pm_runtime_get_if_active(): + +.. code-block:: c + + static irqreturn_t foo_irq_handler(int irq, void *dev_id) + { + struct foo_priv *priv = dev_id; + irqreturn_t ret = IRQ_NONE; + + /* + * Check if the device is active before reading hardware + * registers. If the device is suspended, this interrupt + * cannot belong to us (or was already serviced). + * + * Note that this also will drop interrupts while runtime PM is + * disabled. + */ + if (pm_runtime_get_if_active(priv->dev) <= 0) + return IRQ_NONE; + + /* Hardware is active and usage count is incremented */ + if (foo_has_pending_irq(priv)) { + foo_service_irq(priv); + ret = IRQ_HANDLED; + } + + /* + * Release the reference acquired by pm_runtime_get_if_active(). + * For autosuspend devices, use pm_runtime_put_autosuspend(); + * for non-autosuspend devices, use pm_runtime_put(). + */ + pm_runtime_put_autosuspend(priv->dev); + + return ret; + } + +Both pm_runtime_get_if_in_use() and pm_runtime_get_if_active() are safe to use +from an interrupt routine. One example where a device might be active but not +"in use" is if autosuspend is used. A device will stay active for a while with +no users. If interrupts should still be serviced for a device in this state, +pm_runtime_get_if_active() should be used.
--
2.55.0.979.g7e5102b832-goog