分类: LINUX
2015-11-17 15:30:49
原文地址:Linux内核的cpufreq(变频)机制 作者:hbhuanggang
linux低功耗研究也有一段时间了,基本把低功耗的实现方式想清楚了(主要分成机制和策略),这段时间的工作主要在机制上。暂时想实现的主要的机制有:cpu级,设备驱动级,系统平台级。管理颗粒度不断递增,形成三驾马车齐驱的形势。
cpu级:主要实现比较容易的在系统处于目标在于频繁发生、更高粒度的电源状态改变,主要的实现方式为idle,包括今天的主要想讲的动态主频。
设备驱动级:主要实现对单个设备驱动的管理(suspend,resume等),通过系统监测将闲置的设备,通过从用户态对sys文件目录动态进行单个驱动设备的管理,置于省电模式。
系统平台级:目标在于管理较大的、非常见的重大电源状态改变,用于减少产品设备在长时间的空闲之后,减少电源消耗 。主要实现方式是依托linux内核所支持的apm技术,实现整个系统的睡眠/恢复(sleep)这几个层次其实并不是相互独立的,都是相互交叉的,比如系统平台级的睡眠不可避免会涉及到cpu的sleep模式和设备驱动的挂起,而动态主频的实现除了cpu本身的支持也需要外围驱动随着主频变化做出相应的适应活动。因此这里的分级只是一种粗范围的,逻辑上的分层。
前段时间还调研了一下IBM和Monta Vista搞得那套DPM(Dynamic Power Management)机制,看了不少论文和观点,总的感觉就是太过复杂而且也不是很实用,感觉噱头大过实际功效,(因此这套机制始终还不能进入内核的mainline),言归正传,还是重点讲述下cpufreq技术。
一、为什么要cpufreq?
关于要不要实现cpufreq技术,我也纠结过,一个原因是:当时对内核如何提供这么一套动态变频的机制还不了解,只觉得应该非常麻烦,因为涉及到外围驱动的参数更新,另外一个原因是:在SEP4020这种体量的处理器上跑linux,即使运行在最高频率时的处理能力可能也不是很富余,我再给它降频还有没有意义?挣扎之后还是觉得要实现它,我也给自己列了这么几条原因:
1. 虽然cpu在板级中已不是主要的耗电源,但是仍然占着举足轻重的位置,功耗机制到最后就是几毫安几毫安的扣了,降频肯定能在一定程序上节约功耗那我为什么不采用?
2. 细化功耗管理的颗粒度,为应用程序提供更多的功耗节省机制
3. 对普通的应用,系统可以运行在维持平台运作的最低频率,在有处理任务时,变频机制会自动切换到合适的高主频,并且在任务结束时重回省电的低主频,这样就解决了我之前的第二个疑惑。
4. 实现的一些工作是我们一直需要去做但是一直没有动力做的
5. 可行性论证没有问题:偶然看到armkiller同志提供的nand驱动代码中有变频的实现(这里非常感谢armkiller),网上这方面的文章很少,于是翻阅了linux内核源码中自带的/documentation/cpufreq后,对这种机制大概有一定的了解(linux中的documentation是个好东东),也看到了一些处理器厂商为自己的cpu已经实现了的代码,如sa1100,pxa系列。
二、内核所提供的这种cpufreq技术的机制
1. 目的:
变频技术是指CPU硬件本身支持在不同的频率下运行,系统在运行过程中可以根据随时可能发生变化的系统负载情况动态在这些不同的运行频率之间进行切换,从而达到对性能和功耗做到二者兼顾的目的。
2. 来源:
虽然多个处理器生产厂家都提供了对变频技术的支持,但是其硬件实现和使用方法必然存在着细微甚至巨大的差别。这就使得每个处理器生产厂家都需要按照其特殊的硬件实现和使用方法向内核中添加代码,从而让自己产品中的变频技术在Linux 中得到支持和使用。然而,这种内核开发模式所导致的后果是各个厂家的实现代码散落在 Linux 内核代码树的各个角落里,各种不同的实现之间没有任何代码是共享的,这给内核的维护以及将来添加对新的产品的支持都带来了巨大的开销,并直接导致了 cpufreq 内核子系统的诞生。
3. 管理策略:
Linux内部共有五种对频率的管理策略userspace,conservative,ondemand,powersave 和 performance
Ondemand降频更加激进,conservative降频比较缓慢保守,事实使用ondemand的效果也是比较好的。
4. Cpufreq在用户态所呈现的接口:
以下是将governor切换为ondemand后生成的ondemand文件夹下出现的配置文件。(conservative就不说了,不准备使用)
5. 使用方法:
cd sys/devices/system/cpu/cpu0/cpufreq/目录
echo 32000 > scaling_min_freq 设置最小工作频率(khz,32000~88000)
//若想使用userspace策略
# echo userspace > scaling_governor切换工作方式为userspace
echo 64000 > scaling_setspeed 设置成想要的工作频率(khz)
//若想使用ondemand策略
# echo ondemand > scaling_governor切换工作方式为ondemand
三、如何实现?
首先需要干一些杂活,修改kconfig makefile把系统屏蔽的cpufreq打开,对于我们来说主要的核心有两部分:
系统相关:主要有cpu,timer(变了频率一定要更新系统timer,否则系统时间就不准了),sdram等。
主要就是实现下面这个结构体:
static struct cpufreq_driver sep4020_driver =
{
.flags = CPUFREQ_STICKY,
.verify = sep4020_verify_speed,
.target = sep4020_target,
.get = sep4020_getspeed,
.init = sep4020_cpu_init,
.name = "SEP4020 Freq",
};
代码还是很简陋,很多细节都没考虑,所以具体的暂时先不讲了,大家可以先参考pxa和sa1100的实现。
然后就是收频率影响的驱动:
简单的来说就是:系统在变化cpu主频的时候会调用cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);函数,响挂载在这个cpu上所有的驱动发出一个信号,驱动接收到这个信号则调用相应的处理函数。
这里把串口部分的实现简化,如下:
#ifdef CONFIG_CPU_FREQ
static int sep4020_serial_cpufreq_transition(struct notifier_block *nb, unsigned long val, void *data)
{
// printk("in the serial cpufreq_transition\n");
int pmcr_pre;
unsigned long cpu_clk,baud,baudh,baudl;
pmcr_pre = *(volatile unsigned long*)PMU_PMCR_V;
if(pmcr_pre > 0x4000)
cpu_clk = (pmcr_pre-0x4000)*8000000;
else
cpu_clk = (pmcr_pre)*4000000;
baud = cpu_clk/16/115200;
baudh = baud >>8;
baudl = baud&0xff;
*(volatile unsigned char*)UART0_LCR_V |= (0x80);
*(volatile unsigned char*)UART0_DLBL_V = baudl;
*(volatile unsigned char*)UART0_DLBH_V = baudh;
*(volatile unsigned char*)UART0_LCR_V &= ~(0x80);
printk("in the serial cpufreq_transition\n");
return 0;
}
static inline int sep4020_serial_cpufreq_register(void)
{
sep4020_serial_freq_transition.notifier_call = sep4020_serial_cpufreq_transition;
return cpufreq_register_notifier(&sep4020_serial_freq_transition,
CPUFREQ_TRANSITION_NOTIFIER);
}
static inline void sep4020_serial_cpufreq_deregister(void)
{
cpufreq_unregister_notifier(&sep4020_serial_freq_transition,
CPUFREQ_TRANSITION_NOTIFIER);
}
#else
#endif
四、效果
在sys下开启ondeman模式,串上电流表:
1. 板级电流从220mA调至160mA(因为此时内核检测系统无负载,降频)
2. 执行一个nandflash的拷贝命令,拷贝一个5M左右的文件到其他文件夹,
3. 在拷贝执行时间在3秒时(我给内核设的扫描周期为2.5秒)系统发现有负载,升频,电流从160mA变为220mA(可见已是系统最高主频)
4. 此后的拷贝的整个过程中电流保持为220mA
5. 在拷贝结束后不久(2-3s内),系统电流又跳变至160mA。