3.如何实现?
首先需要干一些杂活,修改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
4.效果
在sys下开启ondeman模式,串上电流表:
1. 板级电流从220mA调至160mA(因为此时内核检测系统无负载,降频)
2. 执行一个nandflash的拷贝命令,拷贝一个5M左右的文件到其他文件夹,
3. 在拷贝执行时间在3秒时(我给内核设的扫描周期为2.5秒)系统发现有负载,升频,电流从160mA变为220mA(可见已是系统最高主频)
4. 此后的拷贝的整个过程中电流保持为220mA
5. 在拷贝结束后不久(2-3s内),系统电流又跳变至160mA。