本次我们来说一下CPU动态调频子系统.
首先来看一下三星Exynos 4412的datasheet,如下:
上图就是Exynos 4412的时钟分布图,可以看到CPU的频率可以在1.4GHz~200MHz之间调整,频率的调整意味着功耗的变化,从datasheet上可以看到,CPU电压的幅度变化是0.875V~1.30V,也就是工作在200MHz时,电压为0.875V,工作在1.4G时,则电压为1.30V,工作800MHz时,电压为1.0V,1GHz时,电压为1.1V,从这个现象,我们可以获得这样的启发,当电流固定之后,我们通过降低电压的方式,可以功耗,这就有了现在器件电压的发展,从最初的51单片机的电压是5v,后边发展了32位机,比如STM32,电压则降为了3.3V,再到Exynos 4412的1.3V,上次参加一个电源大会,已经有一些采用0.8v了.
然后,回到主题,CPU动态调频可以达到相对较好的功耗比,所以,动态调频技术应运而生,而不同的CPU厂商,由于技术能力不同,架构差异等等多方面原因,可能导致一些厂商的CPU动态调频技术会后所区别,少了还好,要是多了,就会出现乱套的情况,比如这家的调频代码放在通用字符设备,那个放在杂项字符设备,,另一个放在块设备里,为了对这种情况进行有效的管控,Linux cpufreq子系统就产生了,专门用来管理CPU动态调频技术,通过驱动框架,把通用驱动部分和CPU具体驱动分离开来,这样,对上层用户就可以统一接口,CPU厂商只需实现,特定CPU的部分即可.本次,我们就来实现Exynos 4412的动态调频代码.下面是子系统的部分驱动架构.
从代码角度大体上分为3层,1,驱动适配器层(特定厂商CPU相关代码,exynos-cpufreq.c), 2,驱动核心层(cpufreq.c), 3,驱动控制层(常见的有6种:cpufreq_conservative.c, cpufreq_interactive.c, cpufreq_performance.c等等一些模式,还有一些其它代码没列出来).其中1,驱动适配器层,我们代码写代码实现,3,一些控制模式我们下面来讲一下:
1.performance
顾名思义只注重效率,将CPU频率固定工作在其支持的最高运行频率上,而不动态调节。
2.powersave
将CPU频率设置为最低的所谓“省电”模式,CPU会固定工作在其支持的最低运行频率上。因此这两种governors 都属于静态governor,即在使用它们时CPU 的运行频率不会根据系统运行时负载的变化动态作出调整。这两种governors 对应的是两种极端的应用场景,使用performance governor 是对系统高性能的最大追求,而使用powersave governor 则是对系统低功耗的最大追求。
3.Userspace
最早的cpufreq 子系统通过userspace governor为用户提供了这种灵活性。系统将变频策略的决策权交给了用户态应用程序,并提供了相应的接口供用户态应用程序调节CPU 运行频率使用。也就是长期以来都在用的那个模式。可以通过手动编辑配置文件进行配置
4.ondemand
按需快速动态调整CPU频率, 一有cpu计算量的任务,就会立即达到最大频率运行,等执行完毕就立即回到最低频率;ondemand:userspace是内核态的检测,用户态调整,效率低。而ondemand正是人们长期以来希望看到的一个完全在内核态下工作并且能够以更加细粒度的时间间隔对系统负载情况进行采样分析的governor。 在 ondemand governor 监测到系统负载超过 up_threshold 所设定的百分比时,说明用户当前需要 CPU 提供更强大的处理能力,因此 ondemand governor 会将CPU设置在最高频率上运行。但是当 ondemand governor 监测到系统负载下降,可以降低 CPU 的运行频率时,到底应该降低到哪个频率呢? ondemand governor 的最初实现是在可选的频率范围内调低至下一个可用频率,例如 CPU 支持三个可选频率,分别为 1.67GHz、 1.33GHz 和 1GHz ,如果 CPU 运行在 1.67GHz 时 ondemand governor 发现可以降低运行频率,那么 1.33GHz 将被选作降频的目标频率。
5.conservative
与ondemand不同,平滑地调整CPU频率,频率的升降是渐变式的,会自动在频率上下限调整,和ondemand的区别 在于它会按需分配频率,而不是一味追求最高频率。
6,interactive
类似于conservative吧,升降频变化幅度较大.
下面,我们来实现一下驱动适配器层的代码,因为已经写了很多注释,所以就不讲了:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/cpufreq.h>
#include <linux/suspend.h>
#include <linux/regulator/consumer.h>
#include <mach/cpufreq.h>
#include <plat/cpu.h>
// 构建设备对象
struct exynos_cpufreq_object{
struct cpufreq_freqs freqs;
unsigned int locking_frequency;
bool frequency_locked;
int lock_count;
};
struct exynos_dvfs_info *exynos_info;
struct regulator *arm_regulator;
struct exynos_cpufreq_object *cpufreq_obj = NULL;
static DEFINE_MUTEX(cpufreq_lock);
int
cpufreq_frequency_table_target_old(struct cpufreq_policy *policy,
struct cpufreq_frequency_table *table,
unsigned int target_freq,
unsigned int relation,
unsigned int *index)
{
struct cpufreq_frequency_table optimal = {
.index = ~0,
.frequency = 0,
}, suboptimal = {
.index =