linux电源管理非常复杂,牵扯到系统机的待机、频率电压变换、系统空闲时间的处理以及每个设备驱动对系统待机的支持和每个设备的运行时runtime电源管理,可以说它和系统中的每个设备驱动都息息相关。
linux内核电源管理的整体架构,
1)CPU在运行时根据系统负载进行动态电压和频率变换的CPUFreq
2) CPU在系统空闲时根据空闲的情况进行低功耗模式的CPUIdle
3)多核系统内下CPU的热插拔支持
4)系统和设备针对延迟的特别需求而提出申请的PM QoS,它会作用于CPUIdle的具体策略
5)设备驱动针对系统挂起到RAM/硬盘的一系列入口函数
6)SoC进入挂起状态,SDRAM自刷新的入口。
7)设备运行时动态电源管理,根据使用情况动态开关设备。
8)底层的时钟,稳压器,频率/电压表(OPP模块完成)支撑,个驱动子系统都可能用到。
CPUFreq驱动
CPUFreq子系统位于drivers/cpufreq目录下,复杂进行运行过程中CPU频率和电压的动态调整,即DVFS(Dynamic Voltage Frequency Scaling,动态电压频率调整)。运行时进行CPU电压和频率调整的原因是:CMOS电路的功耗与电压的平方成正比、与频率成正比,因此降低电压和频率可降低功耗。
CPUFreq的核心层位于drivers/cpufreq/cpufreq.c下,它为各个SoC的CPUFreq驱动的实现提供了一套统一的接口,并实现了一套notifier机制,可以在CPUFrq的策略和频率改变的时候向其他模块发出通知。另外,在CPU运行频率发送变化的时候,内核的loops_per_jiffy
常数也会发生相应的变化。
SoC的CPUFreq驱动实现
每个SoC的具体CPUFreq驱动实例只需要实现电压、频率表,以及从硬件层面完整这些变化。
CPUFreq核心层提供了如下API以供SoC注册自身的CPUFreq驱动:
int cpufreq_register_driver(struct cpufreq_driver *driver_data)
其参数为一个cpufreq_driver
结构体指针,实际上,cpufreq_driver
封装了一个具体的SoC的CPUFreq驱动的主体,该结构体代码如下:
struct cpufreq_driver {
char name[CPUFREQ_NAME_LEN];
u8 flags;
void *driver_data;
/* needed by all drivers */
int (*init)(struct cpufreq_policy *policy);
int (*verify)(struct cpufreq_policy *policy);
/* define one out of two */
int (*setpolicy)(struct cpufreq_policy *policy);
/*
* On failure, should always restore frequency to policy->restore_freq
* (i.e. old freq).
*/
int (*target)(struct cpufreq_policy *policy,
unsigned int target_freq,
unsigned int relation); /* Deprecated */
int (*target_index)(struct cpufreq_policy *policy,
unsigned int index);
unsigned int (*fast_switch)(struct cpufreq_policy *policy,
unsigned int target_freq);
/*
* Only for drivers with target_index() and CPUFREQ_ASYNC_NOTIFICATION
* unset.
*
* get_intermediate should return a stable intermediate frequency
* platform wants to switch to and target_intermediate() should set CPU
* to to that frequency, before jumping to the frequency corresponding
* to 'index'. Core will take care of sending notifications and driver
* doesn't have to handle them in target_intermediate() or
* target_index().
*
* Drivers can return '0' from get_intermediate() in case they don't
* wish to switch to intermediate frequency for some target frequency.
* In that case core will directly call ->target_index().
*/
unsigned int (*get_intermediate)(struct cpufreq_policy *policy,
unsigned int index);
int (*target_intermediate)(struct cpufreq_policy *policy,
unsigned int index);
/* should be defined, if possible */
unsigned int (*get)(unsigned int cpu);
/* optional */
int (*bios_limit)(int cpu, unsigned int *limit);
int (*exit)(struct cpufreq_policy *policy);
void (*stop_cpu)(struct cpufreq_policy *policy);
int (*suspend)(struct cpufreq_policy *policy);
int (*resume)(struct cpufreq_policy *policy);
/* Will be called after the driver is fully initialized */
void (*ready)(struct cpufreq_policy *policy);
struct freq_attr **attr;
/* platform specific boost support code */
bool boost_enabled;
int (*set_boost)(int state);
};
其中name成员是CPUFreq驱动的名字,如drivers/cpufreq/s5pv210-cpufreq.c设置那么为s5pv210,drivers/cpufreq/omap-cpufreq.c设置name为omap;
init()
成员是一个per-CPU初始化函数指针,每当一个新的CPU被注册进系统的时候,该函数就被调用,该函数接受一个cpufreq_policy
的指针参数。
verify()
成员函数用于对用户的CPUFreq策略设置进行有效性验证和数据修正,每当用户设定一个新策略时,该函数根据老的策略和新的策略,检验新策略设置的有效性并对无效设置进行表要的修正,该成员函数的具体实现,常用到如下辅助函数
static inline void cpufreq_verify_within_limits(struct cpufreq_policy *policy,
unsigned int min, unsigned int max)
setpolicy()
成员函数接受一个policy参数,实现了这个成员函数的CPU一遍具备在一个范围里的自耦东调整频率的能力。
target()
成员函数用于将频率调整到一个指定的值。
cpufreq_register_driver
函数,具体操作内容如下: