This chapter shows how to implement power management in your driver.
dev_pm_ops Structure
#include<linux/pm.h>staticintmy_suspend(structdevice*dev){structmy_dev*mydev=dev_get_drvdata(dev);/* Save device state *//* Disable interrupts *//* Stop DMA */return0;}staticintmy_resume(structdevice*dev){structmy_dev*mydev=dev_get_drvdata(dev);/* Restore device state *//* Re-enable interrupts */return0;}staticintmy_runtime_suspend(structdevice*dev){structmy_dev*mydev=dev_get_drvdata(dev);/* Disable clocks, power down */clk_disable_unprepare(mydev->clk);return0;}staticintmy_runtime_resume(structdevice*dev){structmy_dev*mydev=dev_get_drvdata(dev);/* Enable clocks, power up */clk_prepare_enable(mydev->clk);return0;}staticconststructdev_pm_opsmy_pm_ops={SET_SYSTEM_SLEEP_PM_OPS(my_suspend,my_resume)SET_RUNTIME_PM_OPS(my_runtime_suspend,my_runtime_resume,NULL)};
Registering PM Ops
staticstructplatform_drivermy_driver={.probe=my_probe,.remove=my_remove,.driver={.name="my_device",.pm=&my_pm_ops,/* Add PM ops here */},};
Runtime PM Setup in Probe
staticintmy_probe(structplatform_device*pdev){structmy_dev*dev;/* ... allocate and initialize ... *//* Enable runtime PM */pm_runtime_enable(&pdev->dev);/* Optional: set autosuspend delay */pm_runtime_set_autosuspend_delay(&pdev->dev,1000);pm_runtime_use_autosuspend(&pdev->dev);/* Mark as active initially */pm_runtime_get_noresume(&pdev->dev);pm_runtime_set_active(&pdev->dev);pm_runtime_put(&pdev->dev);/* Allow suspend when idle */return0;}staticvoidmy_remove(structplatform_device*pdev){/* Disable runtime PM before removing */pm_runtime_disable(&pdev->dev);}
Using Runtime PM in Your Code
staticintmy_read_data(structmy_dev*dev){intret,data;/* Ensure device is active before accessing hardware */ret=pm_runtime_get_sync(dev->dev);if(ret<0){pm_runtime_put_noidle(dev->dev);returnret;}/* Now safe to access hardware */data=readl(dev->regs+DATA_REG);/* Mark as busy and allow suspend after delay */pm_runtime_mark_last_busy(dev->dev);pm_runtime_put_autosuspend(dev->dev);returndata;}
staticintmy_suspend(structdevice*dev){/* Use runtime PM to do the actual work */returnpm_runtime_force_suspend(dev);}staticintmy_resume(structdevice*dev){returnpm_runtime_force_resume(dev);}staticconststructdev_pm_opsmy_pm_ops={SET_SYSTEM_SLEEP_PM_OPS(my_suspend,my_resume)SET_RUNTIME_PM_OPS(my_runtime_suspend,my_runtime_resume,NULL)};
Pattern 3: IRQ Wake Source
staticintmy_suspend(structdevice*dev){structmy_dev*mydev=dev_get_drvdata(dev);/* Enable wake from this device */if(device_may_wakeup(dev))enable_irq_wake(mydev->irq);return0;}staticintmy_resume(structdevice*dev){structmy_dev*mydev=dev_get_drvdata(dev);if(device_may_wakeup(dev))disable_irq_wake(mydev->irq);return0;}/* In probe */device_init_wakeup(&pdev->dev,true);
PM Helper Macros
Macro
Creates
SET_SYSTEM_SLEEP_PM_OPS(s, r)
.suspend, .resume, .freeze, .thaw
SET_RUNTIME_PM_OPS(s, r, i)
.runtime_suspend, .runtime_resume, .runtime_idle
DEFINE_SIMPLE_DEV_PM_OPS(name, s, r)
Complete pm_ops for simple case
Debug Runtime PM
# Check PM statecat /sys/devices/.../power/runtime_status
# Force suspend (for testing)echo auto > /sys/devices/.../power/control
# Disable runtime PMecho on > /sys/devices/.../power/control