puppy居
puppy居士
posts - 41,comments - 27,trackbacks - 0

在手持设备设计中,电源管理历来为重要的研究课题之一。我们日常所说的省电就属于电源管理的范畴,这也是我们最关心的一个部分。通过挂起不必要的设备、降低CPU的频率或者其它方法,可以减少能量的消耗,达到省电的目的。电源管理实际上是一个系统工程,从应用程序到内核框架,再到设备驱动和硬件设备,都要参与进来,才能达到电源管理的最优化。本文介绍一下动态电源管理(DPM)

 

所谓的动态电源管理(DPM)是一种电源管理机制,它允许在系统运行时动态的管理电源,这可能是相对于传统的电源管理方式而言的,传统的电源管理方式要求系统要么挂起(suspend)以节省能源,要么恢复(resume)运行让程序正常工作,这个过程通常要用户参与(如按键),而且这种状态切换非常缓慢。在动态电源管理(DPM)中,系统一方面可以关闭暂时不使用的设备,比如关闭硬盘和显示器。另外一方面也可以根据负载的重轻,动态调整CPU和总线的频率,以达到节省能源的目的。这都是动态完成的,不需要用户的干预,而且状态之间的切换非常快(每秒数百次)

 

动态电源管理(DPM)是很一个广泛的概念,很多系统实际上都采用了动态电源管理(DPM)方式,本文要谈的是Linux下的动态电源管理(DPM)Linux很早就采用了动态电源管理,在driver目录下有个cpufreq的驱动程序,它就是用来动态调整CPU频率以降低能源消耗的。

 

不过cpufreq似乎不能用于嵌入式环境,主要原因是:在嵌入式系统中,与LCD显示屏等外设相比,CPU已经不是能源消耗的大户了,光调整CPU的频率用处不大。而且cpufreq还依赖于像ACPIPC环境,而嵌入式设备一般都没有BIOS,电源管理功能只能完全由操作系统实现。cpufreq的实现目前还不太清楚,我们会在后续的文章中继续研究。

 

就目前掌握的资料来看,用嵌入式Linux系统的动态电源管理只有IBM奥斯汀实验室和MontaVista联合开发的动态电源管理(DPM)http://dynamicpower.sourceforge.net/)。我们将对它的架构做简要分析,下面提到动态电源管理(DPM)实际上是特指这个解决方案及其实现。

 

我们先介绍几个重要概念:

1.         operating point: 它实际上就是电源管理的一组配置数据,这组配置数据一旦确定,能源消耗率和系统性能也就确定了。比如:

 dpm_operating_point

上图有三个operating point,第一个operating point的名称为”33/33”,它的配置为:Core Voltage=1.0vPLL 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函数库。这一部分主要是对内核提供的sysfsproc文件进行封装,提供更好用的接口函数。它提供的函数如下:

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);

 

 

如果明白了policyoperating stateoperating point等基本概念,上述函数不难理解:首先要创建一些operating point,即各种电源管理配置; 然后把这些operating point组合成class,接下来在operating stateclass/operating point之间建立映射关系,这是初始化过程要做的。在系统运行过程中,dpm会自动选择适当的模式,如果有多种policy,应用程序也可以激活适当的policy

 

2.         内核框架代码。它的主要功能包括对policy的管理,各种状态的切换等平台无关的操作,同时还提供了一些sysfsproc文件用来和用户空间的应用程序交互。它一部分的代码主要分布在下列文件中:

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.cdpm-idle.c是核心代码,dpm-ui.cproc.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上,要修改CCSRCCCRCLKCFG等寄存器。这一层要求实现下面几个接口函数:

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相关的章节,这里不再多说。

posted on 2008-09-10 12:37 puppy 阅读(1527) 评论(0)  编辑 收藏 引用 所属分类: LINUX
只有注册用户登录后才能发表评论。