?
所谓的动态电源管理(DPM)是一种电源管理机制,它允许在系统运行时动态的管理电源,这可能是相对于传统的电源管理方式而言的,传统的电源管理方式要求系统要么挂起(suspend)以节省能源,要么恢复(resume)运行让程序正常工作,这个过程通常要用户参与(如按键),而且这种状态切换非常缓慢。在动态电源管理(DPM)中,系统一方面可以关闭暂时不使用的设备,比如关闭硬盘和显示器。另外一方面也可以根据负载的重轻,动态调整CPU和总线的频率,以达到节省能源的目的。这都是动态完成的,不需要用户的干预,而且状态之间的切换非常快(每秒数百次)。
?
动态电源管理(DPM)是很一个广泛的概念,很多系统实际上都采用了动态电源管理(DPM)方式,本文要谈的是Linux下的动态电源管理(DPM)。Linux很早就采用了动态电源管理,在driver目录下有个cpufreq的驱动程序,它就是用来动态调整CPU频率以降低能源消耗的。
?
不过cpufreq似乎不能用于嵌入式环境,主要原因是:在嵌入式系统中,与LCD显示屏等外设相比,CPU已经不是能源消耗的大户了,光调整CPU的频率用处不大。而且cpufreq还依赖于像ACPI等PC环境,而嵌入式设备一般都没有BIOS,电源管理功能只能完全由操作系统实现。cpufreq的实现目前还不太清楚,我们会在后续的文章中继续研究。
?
就目前掌握的资料来看,用嵌入式Linux系统的动态电源管理只有IBM奥斯汀实验室和MontaVista联合开发的动态电源管理(DPM)(http://dynamicpower.sourceforge.net/)。我们将对它的架构做简要分析,下面提到动态电源管理(DPM)实际上是特指这个解决方案及其实现。
?
我们先介绍几个重要概念:
1.?????????operating point:?它实际上就是电源管理的一组配置数据,这组配置数据一旦确定,能源消耗率和系统性能也就确定了。比如:
上图有三个operaTIng point,第一个operaTIng point的名称为”33/33”,它的配置为:Core Voltage=1.0v、PLL VCO = 800MHz等如表格第二列里的数据所示。
?
2.?????????operaTIng state:?它实际上就是系统的运行状态,比如工作状态和空闲状态。不过在dynamicpower中,状态可以有很多种。同是工作状态,有高性能工作状态、中等性能工作状态和低性能工作状态等,甚至更多,根据具体的情况而定。
?
3.?????????policy:?它是电源管理的一个高级抽象。它负责把operaTIng state映射到一个或者一组(class) operating point上。系统中可以有多个policy,但只一个policy处理激活状态。
?
4.?????????class:?代表一组operating point,在状态切换时,policy选取其中第一个满足约束条件的operating point作为有效operating point。
?
5.?????????constraint:它指设备的约束条件,即只有在满足约束条件下,设备才能正常工作,比如LCD要一定总线频率才能正常更新屏幕。在状态切换时,如果下一状态对应的operating point不满足设备的约束条件,有两种选择:要么强制关闭设备,要么状态切换失败,根据设置而定。
?
dynamicpower可以认为是一种典型的按照机制与策略分开的模式设计的,它只实现了动态电源管理这种机制,而所有策略完全由用户空间的应用程序去做实现。总的来说它分为三个层次:
?
1.?????????API函数库。这一部分主要是对内核提供的sysfs和proc文件进行封装,提供更好用的接口函数。它提供的函数如下:int?dpm_init(void);
int?dpm_terminate(void);
int?dpm_set_state(char?*statename);
int?dpm_create_op(char?*name,?char?*params);
int?dpm_set_op_param(char?*op,?char?*param,?int?value);
int?dpm_get_op_param(char?*opname,?char?*param,?char?*buf,?size_t?bufsiz);
int?dpm_create_class(char?*name,?char?*params);
int?dpm_create_policy(char?*name,?char?*params);
int?dpm_set_policy_state_map(char?*policy,?char?*state,?char?*opclass);
int?dpm_get_policy_state_map(char?*policy,?char?*state,?char?*buf,?size_t?bufsiz);
int?dpm_get_active_policy(char?*name,?size_t?namemax);
int?dpm_set_active_policy(char?*policy);
?如果明白了policy、operating state和operating point等基本概念,上述函数不难理解:首先要创建一些operating point,即各种电源管理配置;?然后把这些operating point组合成class,接下来在operating state和class/operating point之间建立映射关系,这是初始化过程要做的。在系统运行过程中,dpm会自动选择适当的模式,如果有多种policy,应用程序也可以激活适当的policy。
?
2.?????????内核框架代码。它的主要功能包括对policy的管理,各种状态的切换等平台无关的操作,同时还提供了一些sysfs和proc文件用来和用户空间的应用程序交互。它一部分的代码主要分布在下列文件中:drivers/base/core.c
drivers/base/power/Makefile
drivers/base/power/power-dpm.c
drivers/base/power/resume.c
drivers/base/power/suspend.c
drivers/base/power/sysfs.c
drivers/dpm/Kconfig
drivers/dpm/Makefile
drivers/dpm/dpm-idle.c
drivers/dpm/dpm-ui.c
drivers/dpm/dpm.c
drivers/dpm/proc.c
fs/proc/base.c
include/linux/device.h
include/linux/dpm-trace.h
include/linux/dpm.h
include/linux/init_task.h
include/linux/pm.h
include/linux/sched.h
kernel/sched.c
kernel/softirq.c
kernel/workqueue.c其中drivers/dpm/dpm.c和dpm-idle.c是核心代码,dpm-ui.c和proc.c主要是用于与用户空间应用程序交互的,其它代码则是用于与系统其它部分协调工作的。这一层代码不算太复杂,其中最重要的部分是状态切换,其主要过程如下:dpm_set_os:?切换到新状态运行。
dpm_enter_state:把dpm_active_state置为新状态。
dpm_resync:让新状态生效。
dpm_choose_opt:找到适当的operating point。
1.对于单个operating point:满足设备的约束(constraint)条件吗?满足则OK,否则再判断是否要强制切换。如果是则OK,否则切换失败。
2.对于一组operating point(class):?从列表中找到第一个满足约束条件的operating point,找到了则OK。否则切换失败。
dpm_set_opt:?让operating point生效。
dpm_md.set_opt:?调用依赖于具体平台的函数设置新的operating point。3.?????????平台相关代码。为了让operating point真正生效,通常要修改某些特定的寄存器,这是平台相关的。比如,在PXA27x上,要修改CCSR、CCCR和CLKCFG等寄存器。这一层要求实现下面几个接口函数:struct?dpm_md?{
????int?(*init_opt)(struct?dpm_opt *opt);
????int?(*set_opt)(struct?dpm_opt *cur,?struct?dpm_opt *new);
????int?(*get_opt)(struct?dpm_opt *opt);
????int?(*check_constraint)(struct?constraint_param *param,
????????????????????struct?dpm_opt *opt);
????void????(*idle)(void);
????void????(*startup)(void);
????void????(*cleanup)(void);
};要了解这一层的代码,先要熟读平台datasheet相关的章节,这里不再多说。