概述
由于嵌入式设备自身的特点,设备运行功耗问题一直是产品设计首先要考虑的问题,尤其对于非长电设备。
本文主要讲述linux内核(2.6+),设备、系统休眠过程,理解的内核休眠过程,才能够更好的增加产品外设休眠逻辑和硬件平台休眠模块。
其主要分为以下几个方面:
1、soc电源管理,位于平台代码中,一般由厂家提供
2、外设电源管理,位于外设驱动中,按需增加接口
3、linux电源管理核心框架
以下分别进行描述
注册平台电源管理接口
一般位于arch/soc/pm*.c中,通过suspend_set_ops注册
static const struct platform_suspend_ops xxxx_pm_ops = {
.valid = xxxx_pm_valid,
.begin = xxxx_pm_begin,
.enter = xxxx_pm_enter,
.end = xxxx_pm_end,
.prepare = xxxx_suspend_prepare,
.finish = xxxx_suspend_finish,
};
static int __init pm_init(void)
{
suspend_set_ops(&xxxx_pm_ops);
return 0;
}
suspend_set_ops过程如下:
kernel/power/suspend.c
suspend_set_ops(const struct platform_suspend_ops *ops)
// 将ops赋给全局suspend_ops,用于获取平台pm_ops回调接口
suspend_ops = ops;
// 初始化内核支持的休眠模式,即cat /sys/power/state得到的内容
pm_states[i] = pm_labels[j++];
注册外设驱动电源管理接口
外设驱动通过struct device_driver.pm将外设电源管理接口注册到内核中,具体如下:
1、外设驱动中实现struct device_driver.pm接口
static const struct dev_pm_ops xxxx_sdmmc_pm_ops = {
.suspend = xxxx_sdmmc_suspend,
.resume = xxxx_sdmmc_resume,
};
2、注册设备
device_register
//初始化device结构体
device_initialize
device_pm_init(dev);
// 初始化struct device.power结构体
device_pm_sleep_init
// 创建了dev->power.entry列表
device_pm_sleep_init
device_add
device_pm_add(dev);
// 将上面创建的dev->power.entry加入到dpm_list中,即将该device加入电源管理
list_add_tail(&dev->power.entry, &dpm_list);
dpm_list是内核中用于设备电源管理的链表,如果该列表中存在某个外设,则调用外设的pm相关接口,进行休眠操作
linux内核休眠整体过程
上面讲到将各个pm注册到内核的过程,那么何时调用,如何执行上述pm接口呢?
以echo mem > /sys/power/state,休眠设备为例,具体过程如下:
kernel/power/main.c
state_store
//解析传入的"mem"
decode_state(buf, n);
// 挂起系统
pm_suspend
enter_state
// 同步磁盘
sys_sync
suspend_prepare
// 挂起进程和内核线程
suspend_freeze_processes
suspend_devices_and_enter
// 进入平台代码中pm.c注册的ops.begin中
platform_suspend_begin
// 挂起控制台
suspend_console
// 挂起外设
dpm_suspend_start
// 执行device_driver.pm.prepare,即设备挂起前的准备工作
dpm_prepare
//判断device是否加入dpm_list中了
while (!list_empty(&dpm_list)) {
struct device *dev = to_device(dpm_list.next);
return container_of(entry, struct device, power.entry);
//如果加入了,则执行device.driver.pm.prepare
device_prepare
callback = dev->driver->pm->prepare;
if (callback)
ret = callback(dev);
// 挂起外设
dpm_suspend
//遍历所有设备
while (!list_empty(&dpm_prepared_list)) {
device_suspend
__device_suspend
//根据state得到driver.pm.suspend
callback = pm_op(dev->driver->pm, state);
// 调用driver.pm.suspend
dpm_run_callback(callback, dev, state, info);
//打印“PM: suspend of devices complete after” ,标志所有device suspend结束
dpm_show_time(starttime, state, NULL);
//函数在设备挂起之后调用,用于挂起系统
suspend_enter
// 执行平台pm.prepare
platform_suspend_prepare
// 执行设备的suspend_late,然后又将这些设备加入到dpm_late_early_list链表中。如果出现失败,则跳到platform_finish做恢复工作。
dpm_suspend_late
// 执行平台的prepare_late,做最后的准备工作
platform_suspend_prepare_noirq(state);
// 关闭非boot cpu(first cpu)
disable_nonboot_cpus
// 关中断
arch_suspend_disable_irqs
// 执行所有系统的suspend接口
syscore_suspend
// 执行平台的pm.enter接口
suspend_ops->enter