2. Linux电源管理基础
在嵌入式Linux系统中,电源管理(Power Management, PM)是决定设备续航、散热和性能的关键环节。本章将系统梳理Linux电源管理的三大支柱:ACPI(高级配置与电源接口)、DT(设备树)和PM子系统,并深入分析驱动开发与电源管理之间的协作关系。理解这些基础,是后续实现功耗感知调度与驱动节能方案的先决条件。
2.1 电源管理架构总览
Linux电源管理是一个分层、模块化的体系,从上到下依次为:用户空间策略、内核PM核心框架、硬件抽象层(ACPI/DT)和物理硬件。下图展示了这一架构的核心组件与数据流。
从上图可以看出,驱动位于内核PM核心框架与硬件抽象层之间,既需要调用PM核心API管理设备状态,也需要解析ACPI或DT中的电源描述信息。驱动开发者必须理解这三层之间的协作关系,才能写出真正节能的代码。
2.2 ACPI:高级配置与电源接口
ACPI(Advanced Configuration and Power Interface)是x86/ARM64服务器和PC领域的事实标准电源管理接口。它通过固件(BIOS/UEFI)提供一系列描述性表和方法,操作系统无需了解硬件细节即可管理电源。
2.2.1 ACPI核心概念
| 概念 | 说明 | 驱动关注点 |
|---|---|---|
| ACPI表 | DSDT、SSDT、FADT、MADT等,描述硬件拓扑和电源能力 | 驱动通过acpi_device_id匹配设备 |
| 控制方法 | AML字节码,如 _PS0(开启电源)、_PS3(关闭电源) | 驱动调用acpi_evaluate_object执行方法 |
| 设备电源状态 | D0(全开)~ D3cold(完全断电) | 驱动在suspend/resume回调中切换状态 |
| 处理器电源状态 | C0(运行)~ C3(深度睡眠) | cpuidle驱动使用_CST对象获取状态参数 |
| 性能状态 | P0(最高性能)~ Pn(最低性能) | cpufreq驱动使用_PPC/_PSS调节频率电压 |
2.2.2 驱动中的ACPI电源管理示例
一个典型的ACPI兼容驱动,需要在probe阶段获取设备句柄,并在suspend/resume中调用控制方法:
static int my_driver_suspend(struct device *dev)
{
acpi_handle handle = ACPI_HANDLE(dev);
if (handle) {
// 执行 _PS3 方法,将设备置于D3hot状态
acpi_evaluate_object(handle, "_PS3", NULL, NULL);
}
// 关闭设备时钟、电源轨
clk_disable_unprepare(my_clk);
regulator_disable(my_reg);
return 0;
}
static int my_driver_resume(struct device *dev)
{
acpi_handle handle = ACPI_HANDLE(dev);
// 恢复时钟和电源
regulator_enable(my_reg);
clk_prepare_enable(my_clk);
if (handle) {
// 执行 _PS0 方法,将设备恢复到D0状态
acpi_evaluate_object(handle, "_PS0", NULL, NULL);
}
return 0;
}
static SIMPLE_DEV_PM_OPS(my_pm_ops, my_driver_suspend, my_driver_resume);
acpi_device_set_power() 等封装函数,而非直接调用 _PSx 方法。封装函数会自动处理状态转换约束和依赖设备。
2.3 设备树(DT)与电源管理
设备树(Device Tree, DT)是ARM嵌入式Linux世界的主流硬件描述方式。与ACPI不同,DT是静态的、编译为二进制(DTB)的文本文件,由bootloader传递给内核。电源管理信息通过特定的属性和节点描述。
2.3.1 DT中关键的电源管理属性
| 属性/节点 | 用途 | 驱动解析方式 |
|---|---|---|
clocks |
指定设备使用的时钟源和时钟门控 | devm_clk_get() |
power-domains |
关联到电源管理域,实现整体电源门控 | dev_pm_domain_attach() |
regulators |
指定设备使用的稳压器(电压调节) | devm_regulator_get() |
wakeup-source |
标记设备可作为唤醒源 | device_init_wakeup() |
operating-points-v2 |
定义工作频率-电压对(OPP) | dev_pm_opp_of_add_table() |
2.3.2 DT电源管理域(PM Domain)示例
下图展示了一个典型的SoC电源管理域拓扑,以及设备如何挂载到不同域上。
在驱动中,通过 dev_pm_domain_attach() 将设备关联到电源域,内核会在设备suspend时自动关闭该域电源:
static int my_driver_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
// 关联到电源管理域
dev_pm_domain_attach(dev, true);
// 获取时钟和稳压器
my_clk = devm_clk_get(dev, "bus");
my_reg = devm_regulator_get(dev, "vdd");
// 启用运行时PM
pm_runtime_enable(dev);
return 0;
}
static int my_driver_runtime_suspend(struct device *dev)
{
// 关闭时钟和稳压器
clk_disable_unprepare(my_clk);
regulator_disable(my_reg);
return 0;
}
static int my_driver_runtime_resume(struct device *dev)
{
regulator_enable(my_reg);
clk_prepare_enable(my_clk);
return 0;
}
static const struct dev_pm_ops my_pm_ops = {
SET_RUNTIME_PM_OPS(my_driver_runtime_suspend,
my_driver_runtime_resume, NULL)
};
2.4 Linux PM子系统核心框架
Linux PM子系统是内核中负责电源管理策略和机制的核心代码集合,主要包括以下模块:
- PM核心(pm核心):管理系统级suspend/resume流程,提供
suspend_ops注册接口。 - 运行时PM(runtime PM):允许设备在不使用时自动进入低功耗状态,通过引用计数控制。
- CPUIdle:管理CPU空闲状态(C-states),在空闲时切换处理器到低功耗模式。
- CPUFreq:动态调节CPU频率和电压(DVFS),根据负载调整性能。
- 时钟框架(clk):管理设备时钟的开启/关闭和频率调整。
- 稳压器框架(regulator):管理电压调节器,控制设备供电电压。
- 功耗管理域(PM Domain):管理一组设备的整体电源门控。
2.4.1 运行时PM(Runtime PM)核心流程
运行时PM是驱动开发中最常接触的PM机制。其核心思想是:当设备不再被使用时,自动挂起;当需要访问时,自动唤醒。下图展示了运行时PM的状态机。
pm_runtime_enable(),在remove函数中调用 pm_runtime_disable()。每次访问硬件前调用 pm_runtime_get_sync(),访问完成后调用 pm_runtime_put()。这能确保设备在不使用时自动进入低功耗状态。
2.5 驱动与电源管理的关系
驱动是电源管理策略的最终执行者。无论上层的governor如何决策,最终都需要驱动来实现具体的电源操作。驱动与PM的关系可以归纳为以下三个层次:
| 层次 | 驱动职责 | 关键API/机制 |
|---|---|---|
| 设备级PM | 管理单个设备的时钟、电源轨、复位信号 | clk_prepare_enable() / clk_disable_unprepare()regulator_enable() / regulator_disable() |
| 运行时PM | 根据使用情况动态挂起/唤醒设备 | pm_runtime_get_sync() / pm_runtime_put()实现 runtime_suspend / runtime_resume 回调 |
| 系统级PM | 响应系统suspend/resume事件,保存/恢复设备状态 | 实现 suspend / resume 回调使用 dev_pm_ops 结构体注册 |
2.5.1 驱动PM回调的完整实现模式
一个符合最佳实践的驱动,应该同时实现系统级PM和运行时PM回调,并利用PM核心的自动休眠机制:
static int my_dev_suspend(struct device *dev)
{
// 1. 保存硬件状态(寄存器上下文)
my_dev_save_context(dev);
// 2. 关闭时钟
clk_disable_unprepare(my_clk);
// 3. 关闭电源轨
regulator_disable(my_reg);
// 4. 设置唤醒源(如果需要)
if (device_may_wakeup(dev))
enable_irq_wake(my_irq);
return 0;
}
static int my_dev_resume(struct device *dev)
{
// 1. 恢复电源轨
regulator_enable(my_reg);
// 2. 恢复时钟
clk_prepare_enable(my_clk);
// 3. 恢复硬件状态
my_dev_restore_context(dev);
// 4. 禁用唤醒源
if (device_may_wakeup(dev))
disable_irq_wake(my_irq);
return 0;
}
static int my_dev_runtime_suspend(struct device *dev)
{
// 运行时挂起:只关闭时钟,保留电源(快速唤醒)
clk_disable_unprepare(my_clk);
return 0;
}
static int my_dev_runtime_resume(struct device *dev)
{
clk_prepare_enable(my_clk);
return 0;
}
static const struct dev_pm_ops my_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(my_dev_suspend, my_dev_resume)
SET_RUNTIME_PM_OPS(my_dev_runtime_suspend,
my_dev_runtime_resume, NULL)
};
pm_runtime_autosuspend() 机制。
2.6 本章小结
本章建立了Linux电源管理的整体知识框架:
- ACPI 是x86/ARM64服务器的标准,通过AML方法描述电源状态,驱动通过
acpi_evaluate_object交互。 - 设备树 是嵌入式ARM的主流方案,通过
power-domains、clocks、regulators等属性描述电源拓扑。 - PM子系统 提供运行时PM、CPUIdle、CPUFreq等核心框架,驱动通过标准API接入。
- 驱动是PM的执行者,需要在probe/remove、suspend/resume、runtime PM回调中正确管理时钟、电源和状态。