Skip to main content
Linux power management for device drivers operates in two distinct modes: runtime PM, which suspends idle devices while the system is fully operational, and system sleep, which powers down devices during system-wide sleep transitions such as suspend-to-RAM and hibernation. Both modes use the same struct dev_pm_ops callback table defined in include/linux/pm.h.

Two models for device power management

System sleep

Devices enter low-power states as part of a system-wide transition (suspend-to-RAM, suspend-to-disk). The PM core coordinates ordering across the entire device hierarchy.

Runtime PM

Devices are individually suspended while the system is running and resumed on demand. Reduces power consumption without a full system sleep.
These models are not mutually exclusive. A driver that supports runtime PM must also handle the case where the device is already runtime-suspended when a system sleep transition begins.

struct dev_pm_ops

All PM callbacks are collected in struct dev_pm_ops. Declare one and reference it from your driver struct:
#include <linux/pm.h>
#include <linux/pm_runtime.h>

static int mydrv_runtime_suspend(struct device *dev)
{
    struct mydrv_priv *priv = dev_get_drvdata(dev);

    /* Quiesce hardware, gate clocks */
    mydrv_hw_disable(priv);
    clk_disable_unprepare(priv->clk);
    return 0;
}

static int mydrv_runtime_resume(struct device *dev)
{
    struct mydrv_priv *priv = dev_get_drvdata(dev);
    int ret;

    ret = clk_prepare_enable(priv->clk);
    if (ret)
        return ret;
    mydrv_hw_enable(priv);
    return 0;
}

static int mydrv_suspend(struct device *dev)
{
    /* System sleep: quiesce device, save state if needed */
    return pm_runtime_force_suspend(dev);
}

static int mydrv_resume(struct device *dev)
{
    /* System resume: restore state */
    return pm_runtime_force_resume(dev);
}

static const struct dev_pm_ops mydrv_pm_ops = {
    SET_SYSTEM_SLEEP_PM_OPS(mydrv_suspend, mydrv_resume)
    SET_RUNTIME_PM_OPS(mydrv_runtime_suspend, mydrv_runtime_resume, NULL)
};

static struct platform_driver mydrv = {
    .driver = {
        .name = "mydevice",
        .pm   = &mydrv_pm_ops,
    },
    .probe  = mydrv_probe,
    .remove = mydrv_remove,
};
SET_SYSTEM_SLEEP_PM_OPS and SET_RUNTIME_PM_OPS are convenience macros that populate the correct fields and compile out to nothing in non-PM kernel configurations.

Full dev_pm_ops callback reference

The system sleep callbacks are called in phases during suspend and resume:
PhaseSuspend callbackResume callbackNotes
1preparecompleteTop-down; no new children may register after prepare
2suspendresumeQuiesce I/O; may enable wakeup
3suspend_lateresume_earlyAfter runtime PM disabled; save register state
4suspend_noirqresume_noirqIRQs disabled; final hardware save
Runtime PM callbacks:
struct dev_pm_ops {
    /* System sleep */
    int (*prepare)(struct device *dev);
    void (*complete)(struct device *dev);
    int (*suspend)(struct device *dev);
    int (*resume)(struct device *dev);
    int (*freeze)(struct device *dev);      /* hibernation: before snapshot */
    int (*thaw)(struct device *dev);        /* hibernation: after snapshot */
    int (*poweroff)(struct device *dev);    /* hibernation: before power off */
    int (*restore)(struct device *dev);     /* hibernation: restore from disk */
    int (*suspend_late)(struct device *dev);
    int (*resume_early)(struct device *dev);
    int (*suspend_noirq)(struct device *dev);
    int (*resume_noirq)(struct device *dev);
    /* ... freeze_late, thaw_early, poweroff_late, etc. */

    /* Runtime PM */
    int (*runtime_suspend)(struct device *dev);
    int (*runtime_resume)(struct device *dev);
    int (*runtime_idle)(struct device *dev);
};

Runtime power management

Runtime PM allows the driver to suspend an idle device and resume it automatically when it is accessed. The PM core tracks a usage counter and an active-children counter; when both reach zero the runtime_idle callback fires, which by default triggers runtime_suspend.

Enabling runtime PM in probe()

static int mydrv_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    /* ... hardware init ... */

    /*
     * Mark the device as active and enable runtime PM.
     * The initial runtime PM state is RPM_SUSPENDED; set_active moves it
     * to RPM_ACTIVE so the first pm_runtime_put() starts the idle timer.
     */
    pm_runtime_set_active(dev);
    pm_runtime_enable(dev);

    /*
     * Drop the reference held since set_active so the device
     * can suspend after autosuspend_delay with no users.
     */
    pm_runtime_put_autosuspend(dev);

    return 0;
}

static int mydrv_remove(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;

    pm_runtime_get_sync(dev);   /* ensure device is resumed before shutdown */
    pm_runtime_disable(dev);    /* stop runtime PM; no more callbacks */
    pm_runtime_put_noidle(dev); /* release the reference taken above */

    /* ... hardware teardown ... */
    return 0;
}

Core runtime PM functions

#include <linux/pm_runtime.h>

/*
 * pm_runtime_get_sync - increment usage counter and synchronously resume.
 * Returns 0 (or positive) on success, negative on error.
 * Caller must call pm_runtime_put*() when done.
 */
int pm_runtime_get_sync(struct device *dev);

/*
 * pm_runtime_get_if_in_use - get only if already active; returns 1 if
 * successful, 0 if device was suspended, negative on error.
 */
int pm_runtime_get_if_in_use(struct device *dev);

/*
 * pm_runtime_put_sync - decrement usage counter and synchronously suspend
 * if the counter reaches zero.
 */
int pm_runtime_put_sync(struct device *dev);

/*
 * pm_runtime_put_autosuspend - decrement counter; schedule autosuspend
 * if counter reaches zero (respects autosuspend_delay).
 */
void pm_runtime_put_autosuspend(struct device *dev);

/* Non-blocking put — schedules suspend via the PM workqueue */
void pm_runtime_put(struct device *dev);

/* Mark current time as last busy — resets autosuspend timer */
void pm_runtime_mark_last_busy(struct device *dev);

/* Force suspend or resume regardless of usage counter */
int pm_runtime_force_suspend(struct device *dev);
int pm_runtime_force_resume(struct device *dev);

/* Enable / disable runtime PM (not ref-counted) */
void pm_runtime_enable(struct device *dev);
void pm_runtime_disable(struct device *dev);

/* Set initial runtime PM state without triggering callbacks */
void pm_runtime_set_active(struct device *dev);
void pm_runtime_set_suspended(struct device *dev);
pm_runtime_get_sync() can sleep and must not be called from atomic context. Use pm_runtime_get_if_in_use() in interrupt handlers or use pm_runtime_irq_safe() to configure the device for atomic-context callbacks.

Typical usage pattern

/* In any driver operation that accesses hardware */
static int mydrv_do_transfer(struct mydrv_priv *priv, ...)
{
    int ret;

    ret = pm_runtime_get_sync(priv->dev);
    if (ret < 0)
        return ret;

    /* Access hardware safely */
    ret = mydrv_hw_xfer(priv, ...);

    pm_runtime_mark_last_busy(priv->dev);
    pm_runtime_put_autosuspend(priv->dev);
    return ret;
}

Autosuspend

Autosuspend delays the runtime suspend until a configurable timeout has elapsed since the last time pm_runtime_mark_last_busy() was called. This prevents excessive suspend/resume cycling for devices that are used in short bursts.
#include <linux/pm_runtime.h>

/* Enable autosuspend for a device */
void pm_runtime_use_autosuspend(struct device *dev);
void pm_runtime_dont_use_autosuspend(struct device *dev);

/* Set the autosuspend delay in milliseconds */
void pm_runtime_set_autosuspend_delay(struct device *dev, int delay);
Setup in probe:
static int mydrv_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;

    /* ... hardware init ... */

    pm_runtime_set_autosuspend_delay(dev, 50);  /* 50 ms idle before suspend */
    pm_runtime_use_autosuspend(dev);
    pm_runtime_set_active(dev);
    pm_runtime_enable(dev);
    pm_runtime_put_autosuspend(dev);
    return 0;
}
Userspace can override the autosuspend delay through /sys/devices/.../power/autosuspend_delay_ms.

System sleep states

Suspend-to-RAM (S3)

Devices quiesce I/O, save register state if necessary, optionally enable wakeup events, and enter the lowest power state the hardware supports while maintaining context.
static int mydrv_suspend(struct device *dev)
{
    struct mydrv_priv *priv = dev_get_drvdata(dev);

    /*
     * If using runtime PM, pm_runtime_force_suspend() will call
     * runtime_suspend() if the device is currently active.
     */
    return pm_runtime_force_suspend(dev);
}

static int mydrv_resume(struct device *dev)
{
    struct mydrv_priv *priv = dev_get_drvdata(dev);

    return pm_runtime_force_resume(dev);
}

Hibernation (suspend-to-disk)

Hibernation requires additional callbacks because the device state must be preserved across a full power cycle:
static int mydrv_freeze(struct device *dev)
{
    /* Quiesce hardware; state will be saved in the hibernation image */
    return mydrv_suspend(dev);
}

static int mydrv_restore(struct device *dev)
{
    /* Full hardware re-initialization after image restore */
    return mydrv_resume(dev);
}

static const struct dev_pm_ops mydrv_pm_ops = {
    .suspend  = mydrv_suspend,
    .resume   = mydrv_resume,
    .freeze   = mydrv_freeze,
    .thaw     = mydrv_resume,    /* resume from failed hibernate attempt */
    .poweroff = mydrv_suspend,
    .restore  = mydrv_restore,
    SET_RUNTIME_PM_OPS(mydrv_runtime_suspend, mydrv_runtime_resume, NULL)
};

Callback priority

The PM core resolves which callback to invoke using the following priority order (highest first):
  1. dev->pm_domain->ops — PM domain callbacks
  2. dev->type->pm — device type callbacks
  3. dev->class->pm — device class callbacks
  4. dev->bus->pm — bus type callbacks
  5. dev->driver->pm — driver callbacks (fallback if none of the above provide the method)
This allows PM domains and device types to override bus-level callbacks when needed.

Device wakeup

Devices that can generate hardware wakeup events must be registered as wakeup sources:
#include <linux/pm_wakeup.h>

/*
 * device_set_wakeup_capable - mark a device as capable of waking the system.
 * Creates the /sys/devices/.../power/wakeup sysfs file.
 */
void device_set_wakeup_capable(struct device *dev, bool capable);

/*
 * device_set_wakeup_enable - enable/disable wakeup signalling.
 * Must only be called for wakeup-capable devices.
 */
int device_set_wakeup_enable(struct device *dev, bool enable);

/*
 * device_may_wakeup - returns true if wakeup is capable AND enabled.
 * Use this in suspend callbacks to decide whether to arm wakeup hardware.
 */
bool device_may_wakeup(struct device *dev);
Typical suspend callback with wakeup support:
static int mydrv_suspend(struct device *dev)
{
    struct mydrv_priv *priv = dev_get_drvdata(dev);

    if (device_may_wakeup(dev)) {
        /*
         * Enable the hardware wakeup interrupt so the device can
         * bring the system back up while it is in a sleep state.
         */
        enable_irq_wake(priv->irq);
    } else {
        disable_irq(priv->irq);
    }

    mydrv_hw_suspend(priv);
    return 0;
}

static int mydrv_resume(struct device *dev)
{
    struct mydrv_priv *priv = dev_get_drvdata(dev);

    mydrv_hw_resume(priv);

    if (device_may_wakeup(dev))
        disable_irq_wake(priv->irq);
    else
        enable_irq(priv->irq);

    return 0;
}
In probe, declare wakeup capability:
static int mydrv_probe(struct platform_device *pdev)
{
    /* ... */
    device_set_wakeup_capable(&pdev->dev, true);
    /* Wakeup is disabled by default; user enables via sysfs power/wakeup */
    return 0;
}

Power domains

Power domains group devices that share a power rail or clock gate. When the last device in a domain suspends, the domain itself can be powered off. The power domain framework provides callbacks via struct dev_pm_domain:
struct dev_pm_domain {
    struct dev_pm_ops ops;   /* same callback table as drivers */
    void (*detach)(struct device *dev, bool power_off);
    int  (*activate)(struct device *dev);
    void (*sync)(struct device *dev);
    void (*dismiss)(struct device *dev);
};
Drivers attach to a generic power domain using the device tree power-domains property. The genpd (Generic Power Domain) subsystem handles the rest:
/* Device tree */
cluster_pd: power-domain@1000 {
    compatible = "example,power-domain";
    reg = <0x1000 0x100>;
    #power-domain-cells = <0>;
};

mydevice@2000 {
    compatible = "vendor,mydevice";
    reg = <0x2000 0x100>;
    power-domains = <&cluster_pd>;
};
/* No driver changes needed — genpd hooks automatically */
/* The driver uses pm_runtime_get/put as normal */

Runtime PM sysfs interface

The kernel exposes runtime PM controls per device under /sys/devices/.../power/:
FileValuesDescription
controlauto / onauto allows runtime PM; on forces device active
runtime_statusactive / suspended / suspending / resumingCurrent runtime PM state
runtime_active_timemillisecondsTotal time device has been active
runtime_suspended_timemillisecondsTotal time device has been suspended
autosuspend_delay_msintegerAutosuspend delay; -1 to disable autosuspend
wakeupenabled / disabledSystem wakeup capability (wakeup-capable devices only)
# Check runtime PM status for a device
cat /sys/bus/platform/devices/mydevice.0/power/runtime_status

# Prevent runtime suspend (force device on)
echo on > /sys/bus/platform/devices/mydevice.0/power/control

# Re-enable autosuspend
echo auto > /sys/bus/platform/devices/mydevice.0/power/control

Summary

  1. Implement runtime_suspend and runtime_resume in dev_pm_ops.
  2. Call pm_runtime_set_active() then pm_runtime_enable() in probe().
  3. Call pm_runtime_get_sync() before every hardware access.
  4. Call pm_runtime_put_autosuspend() (with pm_runtime_mark_last_busy()) after each access.
  5. Call pm_runtime_disable() in remove() after ensuring the device is resumed.
  1. Implement suspend and resume (and freeze/restore for hibernation).
  2. In suspend, quiesce all I/O: stop DMA, mask IRQs, gate clocks.
  3. Use device_may_wakeup() to conditionally arm wakeup hardware.
  4. In resume, fully reinitialize hardware to operational state.
  5. Drivers using runtime PM can delegate to pm_runtime_force_suspend/resume().
  • Forgetting to call pm_runtime_enable() — all get/put calls will fail silently.
  • Not checking the return value of pm_runtime_get_sync() — the device may have failed to resume.
  • Calling pm_runtime_disable() in remove() before the device is fully quiesced.
  • Accessing hardware registers after pm_runtime_put() without holding a reference.
  • Returning an error from runtime_suspend for non-fatal conditions — the PM core treats most errors as fatal and stops future callbacks.

Build docs developers (and LLMs) love