《Linux 设备驱动开发详解:基于最新的 Linux 4.0 内核》
第 22 章 Linux 电源管理
参考:宋宝华 著,机械工业出版社,2015年版
22.1 Linux 电源管理概述
22.1.1 电源管理的重要性
电源管理是现代嵌入式系统和移动设备的核心需求:
电源管理的重要性:
移动设备(手机、平板):
- 电池容量有限,需要最大化续航时间
- 用户不使用时,设备应尽量降低功耗
- 屏幕、Wi-Fi、GPS 等是主要耗电大户
嵌入式系统(IoT 设备):
- 可能使用电池供电,需要长期运行
- 某些设备需要超低功耗(μA 级别)
服务器:
- 降低电费成本
- 减少散热需求
- 提高数据中心能效
Linux 电源管理的目标:
✓ 在不影响性能的前提下降低功耗
✓ 根据负载动态调整 CPU 频率和电压
✓ 空闲时让 CPU 进入低功耗状态
✓ 不使用的设备自动断电
✓ 系统挂起/休眠时最大化节能
22.1.2 Linux 电源管理框架
Linux 电源管理框架全景:
┌─────────────────────────────────────────────────────────────┐
│ 用户空间 │
│ PowerTOP / TLP / pm-utils / systemd-sleep │
└──────────────────────────┬──────────────────────────────────┘
│
┌──────────────────────────▼──────────────────────────────────┐
│ Linux 电源管理框架 │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ CPUFreq │ │ CPUIdle │ │ System Suspend │ │
│ │ CPU 频率 │ │ CPU 空闲 │ │ 系统挂起/休眠 │ │
│ │ 动态调节 │ │ 状态管理 │ │ │ │
│ └──────────────┘ └──────────────┘ └──────────────────┘ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ Runtime PM │ │ Regulator │ │ Thermal │ │
│ │ 设备运行时 │ │ 电压/电流 │ │ 温度管理 │ │
│ │ 电源管理 │ │ 调节框架 │ │ │ │
│ └──────────────┘ └──────────────┘ └──────────────────┘ │
└──────────────────────────┬──────────────────────────────────┘
│
┌──────────────────────────▼──────────────────────────────────┐
│ 硬件层 │
│ CPU(DVFS)/ 电源管理芯片(PMIC)/ 各外设 │
└─────────────────────────────────────────────────────────────┘
22.1.3 功耗的来源
系统功耗的主要来源:
动态功耗(Dynamic Power):
P_dynamic = α × C × V² × f
α:活动因子(电路翻转率)
C:负载电容
V:工作电压
f:工作频率
→ 降低电压和频率可以显著降低动态功耗
静态功耗(Static Power):
P_static = V × I_leakage
I_leakage:漏电流
→ 关闭不使用的电源域可以消除静态功耗
主要耗电组件(以手机为例):
屏幕:30~50%
CPU/GPU:20~30%
Wi-Fi/蜂窝:15~25%
音频:5~10%
其他外设:5~10%
22.2 CPUFreq
22.2.1 CPUFreq 的概念
CPUFreq(CPU Frequency Scaling)是 Linux 内核的 CPU 频率动态调节子系统,通过 DVFS(Dynamic Voltage and Frequency Scaling,动态电压频率调节)技术,根据系统负载动态调整 CPU 的工作频率和电压:
DVFS 工作原理:
高负载时:
CPU 频率 ↑ → 处理能力 ↑ → 功耗 ↑
CPU 电压 ↑(维持稳定运行)
低负载时:
CPU 频率 ↓ → 处理能力 ↓ → 功耗 ↓↓
CPU 电压 ↓(进一步降低功耗)
功耗与频率/电压的关系:
P ∝ V² × f
频率降低一半:功耗降低约 50%
电压降低 20%:功耗降低约 36%
两者同时降低:功耗可降低约 60%
22.2.2 CPUFreq 架构
CPUFreq 架构:
┌─────────────────────────────────────────────────────────────┐
│ CPUFreq 核心层 │
│ cpufreq_register_driver() / cpufreq_register_governor() │
└──────────────┬──────────────────────────┬───────────────────┘
│ │
┌──────────────▼──────────┐ ┌───────────▼───────────────────┐
│ CPUFreq 驱动 │ │ CPUFreq 调速器(Governor) │
│ (平台相关) │ │ │
│ imx6ul-cpufreq │ │ performance(最高频率) │
│ arm_big_little │ │ powersave(最低频率) │
│ acpi-cpufreq(x86) │ │ ondemand(按需调节) │
│ │ │ conservative(保守调节) │
│ 实现: │ │ schedutil(调度器驱动) │
│ .target() │ │ userspace(用户控制) │
│ .get() │ │ │
│ .init() │ │ 决定何时切换频率 │
└──────────────────────────┘ └───────────────────────────────┘
22.2.3 CPUFreq 调速器
CPUFreq 调速器(Governor)详解:
1. performance(性能优先)
始终使用最高频率
适用:需要最大性能的场景(游戏、编译)
功耗:最高
2. powersave(节能优先)
始终使用最低频率
适用:对性能要求极低的场景
功耗:最低
3. ondemand(按需调节,最常用)
根据 CPU 使用率动态调节频率
CPU 使用率高 → 立即提升到最高频率
CPU 使用率低 → 逐步降低频率
适用:通用场景(桌面、服务器)
4. conservative(保守调节)
类似 ondemand,但频率变化更平缓
避免频率剧烈波动
适用:对延迟不敏感的场景
5. schedutil(调度器驱动,Linux 4.7+)
基于调度器的负载信息调节频率
比 ondemand 更精准
适用:现代 Linux 系统(推荐)
6. userspace(用户控制)
允许用户空间程序直接设置频率
适用:测试和调试
22.2.4 CPUFreq 的使用
bash
# 查看 CPU 频率信息
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies
# 1200000 1000000 800000 600000 400000 200000
# 单位:kHz
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq
# 800000 ← 当前频率 800MHz
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq
# 200000 ← 最低频率 200MHz
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq
# 1200000 ← 最高频率 1.2GHz
# 查看可用的调速器
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors
# conservative ondemand userspace powersave performance schedutil
# 查看当前调速器
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
# ondemand
# 切换调速器
echo performance > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
# 使用 userspace 调速器手动设置频率
echo userspace > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
echo 600000 > /sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed
# 使用 cpupower 工具(推荐)
cpupower frequency-info
cpupower frequency-set -g ondemand
cpupower frequency-set -f 800MHz
22.2.5 CPUFreq 驱动实现
c
/*
* 简化的 CPUFreq 驱动框架
* 以 i.MX6ULL 为例
*/
#include <linux/cpufreq.h>
#include <linux/clk.h>
#include <linux/regulator/consumer.h>
/* OPP(Operating Performance Point):频率-电压对 */
struct imx6ul_opp {
unsigned long freq; /* 频率(Hz)*/
unsigned long volt; /* 电压(μV)*/
};
static struct imx6ul_opp imx6ul_opps[] = {
{ 198000000, 950000 }, /* 198MHz @ 0.95V */
{ 396000000, 950000 }, /* 396MHz @ 0.95V */
{ 528000000, 1150000 }, /* 528MHz @ 1.15V */
{ 696000000, 1250000 }, /* 696MHz @ 1.25V */
};
struct imx6ul_cpufreq {
struct clk *arm_clk;
struct regulator *arm_reg;
struct cpufreq_frequency_table *freq_table;
};
static struct imx6ul_cpufreq *cpufreq_data;
/* 获取当前频率 */
static unsigned int imx6ul_cpufreq_get(unsigned int cpu)
{
return clk_get_rate(cpufreq_data->arm_clk) / 1000; /* 返回 kHz */
}
/* 切换到目标频率 */
static int imx6ul_cpufreq_target(struct cpufreq_policy *policy,
unsigned int target_freq,
unsigned int relation)
{
struct cpufreq_freqs freqs;
unsigned long new_freq, new_volt;
int i, ret;
/* 找到目标频率对应的 OPP */
for (i = 0; i < ARRAY_SIZE(imx6ul_opps); i++) {
if (imx6ul_opps[i].freq / 1000 == target_freq) {
new_freq = imx6ul_opps[i].freq;
new_volt = imx6ul_opps[i].volt;
break;
}
}
freqs.old = imx6ul_cpufreq_get(policy->cpu);
freqs.new = target_freq;
/* 通知频率变化开始 */
cpufreq_freq_transition_begin(policy, &freqs);
if (new_freq > clk_get_rate(cpufreq_data->arm_clk)) {
/* 升频:先升压,再升频 */
ret = regulator_set_voltage(cpufreq_data->arm_reg,
new_volt, new_volt + 50000);
if (ret) goto out;
ret = clk_set_rate(cpufreq_data->arm_clk, new_freq);
} else {
/* 降频:先降频,再降压 */
ret = clk_set_rate(cpufreq_data->arm_clk, new_freq);
if (ret) goto out;
ret = regulator_set_voltage(cpufreq_data->arm_reg,
new_volt, new_volt + 50000);
}
out:
/* 通知频率变化结束 */
cpufreq_freq_transition_end(policy, &freqs, ret);
return ret;
}
/* 初始化 CPUFreq 策略 */
static int imx6ul_cpufreq_init(struct cpufreq_policy *policy)
{
policy->min = imx6ul_opps[0].freq / 1000;
policy->max = imx6ul_opps[ARRAY_SIZE(imx6ul_opps)-1].freq / 1000;
policy->cur = imx6ul_cpufreq_get(policy->cpu);
policy->cpuinfo.transition_latency = 50 * NSEC_PER_USEC; /* 50μs */
/* 设置频率表 */
cpufreq_table_validate_and_show(policy, cpufreq_data->freq_table);
return 0;
}
static struct cpufreq_driver imx6ul_cpufreq_driver = {
.name = "imx6ul-cpufreq",
.flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK,
.init = imx6ul_cpufreq_init,
.verify = cpufreq_generic_frequency_table_verify,
.target_index = imx6ul_cpufreq_target,
.get = imx6ul_cpufreq_get,
.attr = cpufreq_generic_attr,
};
static int __init imx6ul_cpufreq_driver_init(void)
{
return cpufreq_register_driver(&imx6ul_cpufreq_driver);
}
module_init(imx6ul_cpufreq_driver_init);
22.3 CPUIdle
22.3.1 CPUIdle 的概念
CPUIdle 是 Linux 内核的 CPU 空闲状态管理子系统。当 CPU 没有任务可执行时,CPUIdle 选择合适的低功耗状态(C-State)让 CPU 进入休眠:
CPU 空闲状态(C-State):
C0:运行状态(Active)
CPU 正常执行指令
功耗:最高
C1:停止时钟(Halt)
CPU 停止执行指令,但保持上下文
退出延迟:< 1μs
功耗:略低于 C0
C2:停止时钟(Stop Grant)
CPU 进入更深的睡眠
退出延迟:< 10μs
功耗:中等
C3:睡眠(Sleep)
CPU 缓存可能失效,需要刷新
退出延迟:< 100μs
功耗:较低
C6:深度睡眠(Deep Sleep)
CPU 核心断电,状态保存到 LLC
退出延迟:< 1ms
功耗:极低
ARM 对应状态:
WFI(Wait For Interrupt):等待中断,类似 C1
WFE(Wait For Event):等待事件
Cluster Power Down:整个 CPU 簇断电,类似 C3/C6
22.3.2 CPUIdle 框架
c
#include <linux/cpuidle.h>
/*
* cpuidle_driver:CPUIdle 驱动
* 描述 CPU 支持的所有空闲状态
*/
struct cpuidle_driver {
const char *name;
struct module *owner;
/* 空闲状态数组 */
struct cpuidle_state states[CPUIDLE_STATE_MAX];
int state_count;
/* 安全状态(系统挂起时使用)*/
int safe_state_index;
};
/*
* cpuidle_state:一个空闲状态的描述
*/
struct cpuidle_state {
char name[CPUIDLE_NAME_LEN]; /* 状态名称(如 "C1")*/
char desc[CPUIDLE_DESC_LEN]; /* 状态描述 */
unsigned int flags; /* 标志 */
unsigned int exit_latency; /* 退出延迟(μs)*/
int power_usage; /* 功耗(mW)*/
unsigned int target_residency; /* 最小驻留时间(μs)*/
/* 进入此状态的函数 */
int (*enter)(struct cpuidle_device *dev,
struct cpuidle_driver *drv,
int index);
};
/* ARM 平台的 CPUIdle 驱动示例 */
static struct cpuidle_driver arm_idle_driver = {
.name = "arm_idle",
.owner = THIS_MODULE,
.states = {
{
/* C1:WFI 状态 */
.name = "C1",
.desc = "ARM WFI",
.exit_latency = 1, /* 1μs 退出延迟 */
.target_residency = 1,
.power_usage = 100, /* 100mW */
.enter = arm_enter_idle_simple,
},
{
/* C2:Cluster 断电 */
.name = "C2",
.desc = "Cluster Power Down",
.flags = CPUIDLE_FLAG_TIMER_STOP,
.exit_latency = 500, /* 500μs 退出延迟 */
.target_residency = 1000, /* 至少驻留 1ms */
.power_usage = 10, /* 10mW */
.enter = arm_enter_idle_cluster_off,
},
},
.state_count = 2,
.safe_state_index = 0,
};
/* 进入 WFI 状态 */
static int arm_enter_idle_simple(struct cpuidle_device *dev,
struct cpuidle_driver *drv,
int index)
{
/* 执行 WFI 指令,等待中断唤醒 */
cpu_do_idle();
return index;
}
/* 注册 CPUIdle 驱动 */
static int __init arm_idle_init(void)
{
return cpuidle_register_driver(&arm_idle_driver);
}
22.3.3 CPUIdle 的使用
bash
# 查看 CPUIdle 信息
cat /sys/devices/system/cpu/cpu0/cpuidle/state0/name
# C1
cat /sys/devices/system/cpu/cpu0/cpuidle/state0/latency
# 1 ← 退出延迟 1μs
cat /sys/devices/system/cpu/cpu0/cpuidle/state0/usage
# 12345 ← 进入此状态的次数
cat /sys/devices/system/cpu/cpu0/cpuidle/state0/time
# 1234567 ← 在此状态停留的总时间(μs)
# 禁用某个空闲状态
echo 1 > /sys/devices/system/cpu/cpu0/cpuidle/state2/disable
# 使用 PowerTOP 分析 CPU 空闲状态
powertop
# 查看 CPUIdle 调速器
cat /sys/module/cpuidle/parameters/governor
# menu ← 默认调速器
22.4 设备电源管理
22.4.1 设备电源管理的基本概念
Linux 设备模型为每个设备提供了电源管理接口,驱动程序通过实现 dev_pm_ops 中的回调函数来支持电源管理:
c
#include <linux/pm.h>
/*
* dev_pm_ops:设备电源管理操作函数集
* 驱动通过实现这些函数来支持各种电源管理操作
*/
struct dev_pm_ops {
/* 系统挂起相关 */
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); /* 冻结(休眠)*/
int (*thaw)(struct device *dev); /* 解冻 */
int (*poweroff)(struct device *dev); /* 断电 */
int (*restore)(struct device *dev); /* 恢复(休眠后)*/
/* 运行时电源管理 */
int (*runtime_suspend)(struct device *dev); /* 运行时挂起 */
int (*runtime_resume)(struct device *dev); /* 运行时恢复 */
int (*runtime_idle)(struct device *dev); /* 运行时空闲 */
};
/* 在驱动中使用 dev_pm_ops */
static const struct dev_pm_ops my_driver_pm_ops = {
.suspend = my_driver_suspend,
.resume = my_driver_resume,
.runtime_suspend = my_driver_runtime_suspend,
.runtime_resume = my_driver_runtime_resume,
};
/* 在 platform_driver 中注册 */
static struct platform_driver my_driver = {
.driver = {
.name = "my_device",
.pm = &my_driver_pm_ops,
},
.probe = my_probe,
.remove = my_remove,
};
/* 简化宏:SET_SYSTEM_SLEEP_PM_OPS 和 SET_RUNTIME_PM_OPS */
static const struct dev_pm_ops my_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(my_suspend, my_resume)
SET_RUNTIME_PM_OPS(my_runtime_suspend, my_runtime_resume, NULL)
};
22.4.2 系统挂起时的设备电源管理
c
/*
* 设备挂起/恢复的实现示例
*/
static int my_driver_suspend(struct device *dev)
{
struct my_priv *priv = dev_get_drvdata(dev);
dev_info(dev, "设备挂起\n");
/* 1. 停止设备操作 */
my_device_stop(priv);
/* 2. 保存设备状态(寄存器等)*/
my_device_save_state(priv);
/* 3. 关闭设备时钟 */
clk_disable_unprepare(priv->clk);
/* 4. 关闭设备电源(可选)*/
if (priv->power_reg)
regulator_disable(priv->power_reg);
return 0;
}
static int my_driver_resume(struct device *dev)
{
struct my_priv *priv = dev_get_drvdata(dev);
int ret;
dev_info(dev, "设备恢复\n");
/* 1. 恢复设备电源 */
if (priv->power_reg) {
ret = regulator_enable(priv->power_reg);
if (ret) return ret;
}
/* 2. 使能设备时钟 */
ret = clk_prepare_enable(priv->clk);
if (ret) return ret;
/* 3. 恢复设备状态 */
my_device_restore_state(priv);
/* 4. 重新启动设备 */
my_device_start(priv);
return 0;
}
22.5 运行时电源管理(Runtime PM)
22.5.1 Runtime PM 的概念
Runtime PM(运行时电源管理)允许设备在系统运行期间(不是挂起时)根据使用情况自动进入低功耗状态:
Runtime PM 的工作原理:
设备使用中:
pm_runtime_get_sync(dev) ← 增加引用计数,确保设备上电
使用设备...
pm_runtime_put(dev) ← 减少引用计数
引用计数为 0 时:
经过 autosuspend_delay 延迟后
内核调用驱动的 runtime_suspend 回调
设备进入低功耗状态
设备再次被使用时:
pm_runtime_get_sync(dev)
内核调用驱动的 runtime_resume 回调
设备恢复正常工作
优点:
✓ 设备不使用时自动断电,节省功耗
✓ 对驱动的其他部分透明(通过 get/put 管理)
✓ 支持自动挂起延迟(避免频繁开关)
22.5.2 Runtime PM API
c
#include <linux/pm_runtime.h>
/* ── 初始化 Runtime PM ──────────────────────────────────────── */
/* 使能设备的 Runtime PM */
pm_runtime_enable(dev);
/* 禁用设备的 Runtime PM */
pm_runtime_disable(dev);
/* 设置自动挂起延迟(ms)*/
pm_runtime_set_autosuspend_delay(dev, 2000); /* 2秒后自动挂起 */
/* 使能自动挂起 */
pm_runtime_use_autosuspend(dev);
/* 设置设备初始状态为活跃 */
pm_runtime_set_active(dev);
/* 设置设备初始状态为挂起 */
pm_runtime_set_suspended(dev);
/* ── 使用设备时 ──────────────────────────────────────────────── */
/* 增加引用计数(同步,等待设备恢复)*/
int ret = pm_runtime_get_sync(dev);
if (ret < 0) {
dev_err(dev, "设备恢复失败\n");
return ret;
}
/* 增加引用计数(异步,不等待)*/
pm_runtime_get(dev);
/* 减少引用计数(异步,可能触发自动挂起)*/
pm_runtime_put(dev);
/* 减少引用计数(同步,立即挂起)*/
pm_runtime_put_sync(dev);
/* 减少引用计数(使用自动挂起延迟)*/
pm_runtime_put_autosuspend(dev);
/* ── 查询状态 ──────────────────────────────────────────────── */
/* 检查设备是否处于活跃状态 */
bool active = pm_runtime_active(dev);
/* 检查设备是否处于挂起状态 */
bool suspended = pm_runtime_suspended(dev);
/* 获取当前引用计数 */
int count = atomic_read(&dev->power.usage_count);
22.5.3 Runtime PM 驱动实现
c
/*
* Runtime PM 完整实现示例
* 以 I2C 传感器驱动为例
*/
#include <linux/i2c.h>
#include <linux/pm_runtime.h>
struct sensor_priv {
struct i2c_client *client;
struct regulator *vdd; /* 电源调节器 */
struct clk *clk; /* 时钟 */
bool powered; /* 当前电源状态 */
};
/* ── Runtime PM 回调函数 ──────────────────────────────────────── */
static int sensor_runtime_suspend(struct device *dev)
{
struct sensor_priv *priv = dev_get_drvdata(dev);
dev_dbg(dev, "运行时挂起\n");
/* 关闭传感器 */
i2c_smbus_write_byte_data(priv->client, SENSOR_REG_CTRL, 0x00);
/* 关闭时钟 */
clk_disable_unprepare(priv->clk);
/* 关闭电源 */
regulator_disable(priv->vdd);
priv->powered = false;
return 0;
}
static int sensor_runtime_resume(struct device *dev)
{
struct sensor_priv *priv = dev_get_drvdata(dev);
int ret;
dev_dbg(dev, "运行时恢复\n");
/* 开启电源 */
ret = regulator_enable(priv->vdd);
if (ret) return ret;
/* 等待电源稳定 */
usleep_range(1000, 2000);
/* 开启时钟 */
ret = clk_prepare_enable(priv->clk);
if (ret) {
regulator_disable(priv->vdd);
return ret;
}
/* 初始化传感器 */
i2c_smbus_write_byte_data(priv->client, SENSOR_REG_CTRL, 0x01);
priv->powered = true;
return 0;
}
/* ── 读取传感器数据(使用 Runtime PM)──────────────────────── */
static int sensor_read_data(struct sensor_priv *priv, int *value)
{
int ret;
/* 增加引用计数,确保设备上电 */
ret = pm_runtime_get_sync(&priv->client->dev);
if (ret < 0) {
pm_runtime_put_noidle(&priv->client->dev);
return ret;
}
/* 读取传感器数据 */
ret = i2c_smbus_read_word_data(priv->client, SENSOR_REG_DATA);
if (ret < 0) {
pm_runtime_put_autosuspend(&priv->client->dev);
return ret;
}
*value = ret;
/* 减少引用计数(使用自动挂起延迟)*/
pm_runtime_mark_last_busy(&priv->client->dev);
pm_runtime_put_autosuspend(&priv->client->dev);
return 0;
}
/* ── probe 函数 ──────────────────────────────────────────────── */
static int sensor_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct sensor_priv *priv;
int ret;
priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->client = client;
/* 获取电源和时钟 */
priv->vdd = devm_regulator_get(&client->dev, "vdd");
priv->clk = devm_clk_get(&client->dev, NULL);
i2c_set_clientdata(client, priv);
/* 初始化 Runtime PM */
pm_runtime_set_autosuspend_delay(&client->dev, 2000); /* 2秒自动挂起 */
pm_runtime_use_autosuspend(&client->dev);
pm_runtime_set_active(&client->dev);
pm_runtime_enable(&client->dev);
/* 初始化设备(此时设备处于活跃状态)*/
ret = sensor_init(priv);
if (ret) {
pm_runtime_disable(&client->dev);
return ret;
}
/* 允许设备进入运行时挂起 */
pm_runtime_put_autosuspend(&client->dev);
dev_info(&client->dev, "传感器驱动加载成功(支持 Runtime PM)\n");
return 0;
}
static int sensor_remove(struct i2c_client *client)
{
/* 禁用 Runtime PM */
pm_runtime_disable(&client->dev);
pm_runtime_set_suspended(&client->dev);
return 0;
}
static const struct dev_pm_ops sensor_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(sensor_runtime_suspend,
sensor_runtime_resume,
NULL)
};
static struct i2c_driver sensor_driver = {
.driver = {
.name = "my-sensor",
.pm = &sensor_pm_ops,
},
.probe = sensor_probe,
.remove = sensor_remove,
};
module_i2c_driver(sensor_driver);
22.6 系统级电源管理(Suspend/Hibernate)
22.6.1 系统挂起状态
Linux 支持多种系统级电源管理状态:
Linux 系统电源状态:
S0:正常运行(Running)
系统完全运行
功耗:最高
S1/freeze:冻结(Freeze,Linux 特有)
冻结所有进程和设备驱动
CPU 进入空闲状态
内存保持供电
功耗:较低
恢复时间:极快(< 1秒)
S3/mem:挂起到内存(Suspend to RAM,STR)
系统状态保存在 RAM 中
大部分硬件断电,RAM 保持供电
功耗:极低(约 1~5W)
恢复时间:快(1~3秒)
对应 ACPI S3 状态
S4/disk:挂起到磁盘(Suspend to Disk,Hibernate)
系统状态保存到磁盘(swap 分区)
所有硬件断电
功耗:零(完全断电)
恢复时间:慢(10~30秒)
对应 ACPI S4 状态
查看支持的挂起状态:
cat /sys/power/state
# freeze mem disk
22.6.2 系统挂起的流程
系统挂起(Suspend to RAM)流程:
用户执行:echo mem > /sys/power/state
↓
1. 冻结用户空间进程(freeze_processes)
↓
2. 调用所有设备的 prepare 回调
↓
3. 挂起所有设备(suspend 回调,从叶子节点到根节点)
↓
4. 挂起 CPU(进入低功耗状态)
↓
5. 等待唤醒事件(按键、网络包、RTC 闹钟等)
↓
6. 恢复 CPU
↓
7. 恢复所有设备(resume 回调,从根节点到叶子节点)
↓
8. 调用所有设备的 complete 回调
↓
9. 解冻用户空间进程(thaw_processes)
↓
系统恢复正常运行
22.6.3 系统挂起的使用
bash
# 查看支持的挂起状态
cat /sys/power/state
# freeze mem disk
# 挂起到内存(S3)
echo mem > /sys/power/state
# 冻结(freeze)
echo freeze > /sys/power/state
# 休眠到磁盘(S4,需要 swap 分区)
echo disk > /sys/power/state
# 设置唤醒源
# 使能 RTC 唤醒(30秒后唤醒)
echo +30 > /sys/class/rtc/rtc0/wakealarm
echo mem > /sys/power/state
# 查看唤醒源
cat /sys/power/wakeup_count
cat /proc/acpi/wakeup
# 使用 systemd 管理挂起
systemctl suspend # 挂起到内存
systemctl hibernate # 休眠到磁盘
systemctl hybrid-sleep # 混合睡眠
# 查看挂起/恢复日志
dmesg | grep -E "PM:|suspend|resume"
journalctl -b -1 | grep -E "suspend|resume"
22.6.4 驱动中的挂起/恢复实现
c
/*
* 完整的挂起/恢复实现
* 包含系统挂起和 Runtime PM 的统一处理
*/
static int my_driver_suspend(struct device *dev)
{
struct my_priv *priv = dev_get_drvdata(dev);
dev_info(dev, "系统挂起\n");
/* 如果设备已经通过 Runtime PM 挂起,直接返回 */
if (pm_runtime_suspended(dev)) {
dev_info(dev, "设备已处于运行时挂起状态\n");
return 0;
}
/* 停止设备操作 */
my_device_stop(priv);
/* 保存寄存器状态 */
priv->saved_regs[0] = readl(priv->base + REG0);
priv->saved_regs[1] = readl(priv->base + REG1);
/* 关闭时钟和电源 */
clk_disable_unprepare(priv->clk);
regulator_disable(priv->vdd);
return 0;
}
static int my_driver_resume(struct device *dev)
{
struct my_priv *priv = dev_get_drvdata(dev);
int ret;
dev_info(dev, "系统恢复\n");
/* 如果设备通过 Runtime PM 挂起,由 Runtime PM 处理恢复 */
if (pm_runtime_suspended(dev))
return 0;
/* 恢复电源和时钟 */
ret = regulator_enable(priv->vdd);
if (ret) return ret;
ret = clk_prepare_enable(priv->clk);
if (ret) {
regulator_disable(priv->vdd);
return ret;
}
/* 恢复寄存器状态 */
writel(priv->saved_regs[0], priv->base + REG0);
writel(priv->saved_regs[1], priv->base + REG1);
/* 重新启动设备 */
my_device_start(priv);
return 0;
}
/* 使用 SIMPLE_DEV_PM_OPS 简化 */
static SIMPLE_DEV_PM_OPS(my_pm_ops, my_driver_suspend, my_driver_resume);
static struct platform_driver my_driver = {
.driver = {
.name = "my_device",
.pm = &my_pm_ops,
},
};
22.7 Regulator 框架
22.7.1 Regulator 的概念
Regulator(调节器)框架是 Linux 内核中管理电压和电流调节器的子系统,用于控制 PMIC(Power Management IC,电源管理芯片):
Regulator 框架的作用:
硬件层面:
PMIC(如 TPS65217、AXP209)提供多路可调电压输出
每路输出连接到不同的芯片或模块(CPU、DDR、LCD 等)
软件层面:
Regulator 框架统一管理所有电压/电流调节器
驱动通过 API 请求所需电压,无需关心具体 PMIC 型号
支持引用计数(多个消费者共享同一调节器)
支持电压约束(防止设置超出安全范围的电压)
典型应用:
CPU 核心电压(VCORE):随频率动态调节(DVFS)
DDR 电压(VDDR):固定电压
LCD 背光电压(VLED):可调(控制亮度)
传感器电源(VSENSOR):按需开关
22.7.2 Regulator 框架架构
Regulator 框架架构:
┌─────────────────────────────────────────────────────────────┐
│ 消费者驱动(Consumer) │
│ CPU 驱动 / LCD 驱动 / 传感器驱动 / Wi-Fi 驱动 │
│ 通过 regulator_get() 获取调节器 │
│ 通过 regulator_enable/disable/set_voltage() 控制 │
└──────────────────────────┬──────────────────────────────────┘
│
┌──────────────────────────▼──────────────────────────────────┐
│ Regulator 核心层 │
│ 引用计数管理 / 电压约束检查 / 依赖关系管理 │
└──────────────────────────┬──────────────────────────────────┘
│
┌──────────────────────────▼──────────────────────────────────┐
│ Regulator 驱动(Provider) │
│ PMIC 驱动(tps65217 / axp209 / da9063 等) │
│ 实现 regulator_ops 中的操作函数 │
└──────────────────────────┬──────────────────────────────────┘
│ I2C/SPI
┌──────────────────────────▼──────────────────────────────────┐
│ PMIC 硬件 │
└─────────────────────────────────────────────────────────────┘
22.7.3 消费者 API
c
#include <linux/regulator/consumer.h>
/* ── 获取和释放调节器 ──────────────────────────────────────── */
/*
* regulator_get:获取调节器
* dev:消费者设备
* id:调节器 ID(与设备树中的 supply 名称对应)
*/
struct regulator *reg = regulator_get(dev, "vdd");
if (IS_ERR(reg)) {
dev_err(dev, "获取 vdd 调节器失败\n");
return PTR_ERR(reg);
}
/* devm 版本(自动释放)*/
struct regulator *reg = devm_regulator_get(dev, "vdd");
/* 释放调节器 */
regulator_put(reg);
/* ── 使能和禁用 ──────────────────────────────────────────────── */
/* 使能调节器(引用计数 +1)*/
int ret = regulator_enable(reg);
/* 禁用调节器(引用计数 -1,为0时真正关闭)*/
regulator_disable(reg);
/* 检查调节器是否使能 */
int enabled = regulator_is_enabled(reg);
/* ── 电压控制 ──────────────────────────────────────────────── */
/* 设置电压范围(min_uV ~ max_uV,单位:微伏)*/
ret = regulator_set_voltage(reg, 1800000, 1800000); /* 固定 1.8V */
ret = regulator_set_voltage(reg, 950000, 1250000); /* 0.95V ~ 1.25V */
/* 获取当前电压 */
int volt = regulator_get_voltage(reg); /* 返回微伏 */
pr_info("当前电压:%d mV\n", volt / 1000);
/* ── 电流控制 ──────────────────────────────────────────────── */
/* 设置电流限制 */
ret = regulator_set_current_limit(reg, 100000, 500000); /* 100mA ~ 500mA */
/* 获取当前电流 */
int curr = regulator_get_current_limit(reg);
/* ── 完整使用示例 ──────────────────────────────────────────── */
static int my_sensor_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct regulator *vdd, *vio;
int ret;
/* 获取电源调节器 */
vdd = devm_regulator_get(&client->dev, "vdd");
if (IS_ERR(vdd)) {
dev_err(&client->dev, "获取 vdd 失败\n");
return PTR_ERR(vdd);
}
vio = devm_regulator_get(&client->dev, "vio");
if (IS_ERR(vio)) {
dev_err(&client->dev, "获取 vio 失败\n");
return PTR_ERR(vio);
}
/* 设置电压 */
ret = regulator_set_voltage(vdd, 2800000, 2800000); /* 2.8V */
if (ret) return ret;
ret = regulator_set_voltage(vio, 1800000, 1800000); /* 1.8V */
if (ret) return ret;
/* 使能电源 */
ret = regulator_enable(vdd);
if (ret) return ret;
ret = regulator_enable(vio);
if (ret) {
regulator_disable(vdd);
return ret;
}
/* 等待电源稳定 */
usleep_range(1000, 2000);
/* 初始化传感器 */
/* ... */
return 0;
}
22.7.4 设备树中的 Regulator 配置
dts
/* PMIC 设备树配置(以 TPS65217 为例)*/
&i2c0 {
tps65217: pmic@24 {
compatible = "ti,tps65217";
reg = <0x24>;
interrupts = <7>;
regulators {
/* DCDC1:CPU 核心电压 */
dcdc1_reg: regulator@0 {
regulator-name = "vdd_mpu";
regulator-min-microvolt = <900000>;
regulator-max-microvolt = <1500000>;
regulator-boot-on;
regulator-always-on;
};
/* DCDC2:DDR 电压 */
dcdc2_reg: regulator@1 {
regulator-name = "vdd_core";
regulator-min-microvolt = <925000>;
regulator-max-microvolt = <1150000>;
regulator-boot-on;
regulator-always-on;
};
/* LDO1:传感器电源 */
ldo1_reg: regulator@4 {
regulator-name = "vdd_sensor";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
/* 不设置 always-on,按需开关 */
};
};
};
};
/* 传感器设备树配置(引用调节器)*/
&i2c1 {
my_sensor: sensor@48 {
compatible = "myvendor,my-sensor";
reg = <0x48>;
/* 声明所需的电源 */
vdd-supply = <&ldo1_reg>; /* 连接到 LDO1 */
vio-supply = <&dcdc2_reg>; /* 连接到 DCDC2 */
};
};
22.7.5 Regulator 驱动实现
c
/*
* 简化的 Regulator 驱动框架
* 以一个简单的 I2C PMIC 为例
*/
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
/* 调节器操作函数集 */
static int my_pmic_enable(struct regulator_dev *rdev)
{
/* 通过 I2C 使能对应的 LDO/DCDC */
return i2c_smbus_write_byte_data(rdev_get_drvdata(rdev),
PMIC_ENABLE_REG,
PMIC_ENABLE_BIT);
}
static int my_pmic_disable(struct regulator_dev *rdev)
{
return i2c_smbus_write_byte_data(rdev_get_drvdata(rdev),
PMIC_ENABLE_REG,
0);
}
static int my_pmic_is_enabled(struct regulator_dev *rdev)
{
int val = i2c_smbus_read_byte_data(rdev_get_drvdata(rdev),
PMIC_ENABLE_REG);
return (val & PMIC_ENABLE_BIT) ? 1 : 0;
}
static int my_pmic_set_voltage(struct regulator_dev *rdev,
int min_uV, int max_uV,
unsigned *selector)
{
/* 根据电压值计算寄存器设置值 */
int sel = (min_uV - 900000) / 25000; /* 步进 25mV */
*selector = sel;
return i2c_smbus_write_byte_data(rdev_get_drvdata(rdev),
PMIC_VOLT_REG, sel);
}
static int my_pmic_get_voltage(struct regulator_dev *rdev)
{
int sel = i2c_smbus_read_byte_data(rdev_get_drvdata(rdev),
PMIC_VOLT_REG);
return 900000 + sel * 25000; /* 转换为微伏 */
}
static const struct regulator_ops my_pmic_ops = {
.enable = my_pmic_enable,
.disable = my_pmic_disable,
.is_enabled = my_pmic_is_enabled,
.set_voltage = my_pmic_set_voltage,
.get_voltage = my_pmic_get_voltage,
};
/* 调节器描述符 */
static const struct regulator_desc my_pmic_desc = {
.name = "my-pmic-ldo1",
.id = 0,
.ops = &my_pmic_ops,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.min_uV = 900000, /* 最低 0.9V */
.uV_step = 25000, /* 步进 25mV */
.n_voltages = 25, /* 25 个档位 */
};
static int my_pmic_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct regulator_config config = {};
struct regulator_dev *rdev;
config.dev = &client->dev;
config.driver_data = client;
config.of_node = client->dev.of_node;
/* 注册调节器 */
rdev = devm_regulator_register(&client->dev, &my_pmic_desc, &config);
if (IS_ERR(rdev)) {
dev_err(&client->dev, "注册调节器失败\n");
return PTR_ERR(rdev);
}
dev_info(&client->dev, "PMIC 调节器注册成功\n");
return 0;
}
本章小结
| 章节 | 核心知识点 | 关键 API |
|---|---|---|
| 22.1 电源管理概述 | 电源管理重要性;Linux电源管理框架全景图;动态/静态功耗来源;DVFS原理 | 概念理解 |
| 22.2 CPUFreq | DVFS工作原理;CPUFreq架构(驱动+调速器);6种调速器详解(performance/powersave/ondemand/conservative/schedutil/userspace);CPUFreq驱动实现(target/get/init) | cpufreq_register_driver()、/sys/devices/system/cpu/cpu0/cpufreq/ |
| 22.3 CPUIdle | CPU空闲状态(C-State);CPUIdle框架;cpuidle_driver/cpuidle_state结构体;ARM WFI/Cluster断电 | cpuidle_register_driver()、/sys/devices/system/cpu/cpu0/cpuidle/ |
| 22.4 设备电源管理 | dev_pm_ops结构体;系统挂起时的设备suspend/resume;SET_SYSTEM_SLEEP_PM_OPS宏 | dev_pm_ops、SET_SYSTEM_SLEEP_PM_OPS() |
| 22.5 Runtime PM | 运行时电源管理原理;pm_runtime_get_sync/put/enable/disable;自动挂起延迟;完整传感器驱动案例 | pm_runtime_enable()、pm_runtime_get_sync()、pm_runtime_put_autosuspend() |
| 22.6 系统级电源管理 | 系统电源状态(freeze/mem/disk);挂起到内存流程(9步);挂起/恢复驱动实现;SIMPLE_DEV_PM_OPS宏 | echo mem > /sys/power/state、SIMPLE_DEV_PM_OPS() |
| 22.7 Regulator框架 | Regulator架构(消费者/核心/驱动);消费者API(get/enable/disable/set_voltage);设备树配置(supply属性);Regulator驱动实现 | regulator_get()、regulator_enable()、regulator_set_voltage() |
电源管理驱动开发要点
1. CPUFreq 驱动
实现 target/get/init 三个核心函数
升频时先升压再升频,降频时先降频再降压
使用 OPP(Operating Performance Point)管理频率-电压对
2. Runtime PM
probe 中调用 pm_runtime_enable() 使能
使用设备前调用 pm_runtime_get_sync()
使用完后调用 pm_runtime_put_autosuspend()
设置合理的自动挂起延迟(避免频繁开关)
3. 系统挂起/恢复
suspend 中保存寄存器状态,关闭时钟和电源
resume 中恢复电源和时钟,恢复寄存器状态
检查 pm_runtime_suspended() 避免重复操作
4. Regulator 使用
使用 devm_regulator_get() 获取调节器
设置电压后再使能
使用完后禁用(引用计数管理)
5. 调试工具
PowerTOP:分析系统功耗
cpupower:管理 CPUFreq
/sys/power/:系统电源管理接口
/sys/class/regulator/:调节器状态
参考文献:宋宝华《Linux设备驱动开发详解:基于最新的Linux 4.0内核》,机械工业出版社,2015年