1. 基本介绍
1) 调整CPU运行频率是一个节能的好方法,CPU运行频率越低,CPU功耗越小。
2) 下面的我现在正在使用的CPU为例进行说明。触发CPU频率调整的有两个源:
1)根据CPU负荷进行调整(代码位于:kernel/drivers/cpufreq,下面以cpufreq_interactive.c为例,当/sys/drivers/system/cpu/cpu0/cpufreq/scaling_governor中的值为interactive时生效)
2)根据CPU的温度时行调整(需要CPU内有温度传感器,且在cpufreq_driver中实现,它由各个芯片厂家实现)
2. 根据CPU负荷进行调整的调用流程
cpufreq_interactive_up_task(或cpufreq_interactive_freq_down
或cpufreq_governor_interactive :cpufreq_interactive.c)->
__cpufreq_driver_target->
cpufreq_driver->target(mycpu_cpufreq_driver.target=mycpu_target)->
mycpu_target->
clk_set_rate->
dvfs_set_rate->
dvfs_target_cpu (或dvfs_target_core)->
dvfs_clk_get_ref_volt(获得与频率对应的参考电压)
dvfs_scale_volt_direct(设置电压)
clk_set_rate_locked->clk_set_rate_nolock->clk->set_rate(设置频率)
3. cpufreq_interactive工作流程
1) 驱动入口:cpufreq_interactive_init
a) 注册CPU timer函数:cpufreq_interactive_timer
b) 创建线程:kinteractiveup,入口函数:cpufreq_interactive_up_task
c) 创建workqueue “knteractive_down”,其工作处理函数为:cpufreq_interactive_freq_down
d) 注册idle notify函数cpufreq_interactive_idle_nb
e) 注册cpufreq_governor cpufreq_gov_interactive,其中的governor函数为:cpufreq_governor_interactive
2) cpufreq_interactive_timer
这个函数通过timer触发,它执行cpu负荷计算。timer的默认间隔为20ms。根据CPU负荷计算CPU的频率的代码如下:
/* * Once pcpu->timer_run_time is updated to >= pcpu->idle_exit_time, * this lets idle exit know the current idle time sample has * been processed, and idle exit can generate a new sample and * re-arm the timer. This prevents a concurrent idle * exit on that CPU from writing a new set of info at the same time * the timer function runs (the timer function can't use that info * until more time passes). */ time_in_idle = pcpu->time_in_idle; idle_exit_time = pcpu->idle_exit_time; now_idle = get_cpu_idle_time_us(data, &pcpu->timer_run_time); smp_wmb(); /* If we raced with cancelling a timer, skip. */ if (!idle_exit_time) goto exit; delta_idle = (unsigned int) cputime64_sub(now_idle, time_in_idle); delta_time = (unsigned int) cputime64_sub(pcpu->timer_run_time, idle_exit_time); /* * If timer ran less than 1ms after short-term sample started, retry. */ if (delta_time < 1000) goto rearm; if (delta_idle > delta_time) cpu_load = 0; else cpu_load = 100 * (delta_time - delta_idle) / delta_time; delta_idle = (unsigned int) cputime64_sub(now_idle, pcpu->freq_change_time_in_idle); delta_time = (unsigned int) cputime64_sub(pcpu->timer_run_time, pcpu->freq_change_time); if ((delta_time == 0) || (delta_idle > delta_time)) load_since_change = 0; else load_since_change = 100 * (delta_time - delta_idle) / delta_time; /* * Choose greater of short-term load (since last idle timer * started or timer function re-armed itself) or long-term load * (since last frequency change). */ if (load_since_change > cpu_load) cpu_load = load_since_change; if (cpu_load >= go_hispeed_load) { if (pcpu->policy->cur == pcpu->policy->min) new_freq = hispeed_freq; else new_freq = pcpu->policy->max * cpu_load / 100; } else { new_freq = pcpu->policy->cur * cpu_load / 100; } if (cpufreq_frequency_table_target(pcpu->policy, pcpu->freq_table, new_freq, CPUFREQ_RELATION_H, &index)) { pr_warn_once("timer %d: cpufreq_frequency_table_target error\n", (int) data); goto rearm; } new_freq = pcpu->freq_table[index].frequency;
a) 如果新的频率大于当前工作频率,则唤醒进程kinteractiveup进行处理,此进程调用__cpufreq_driver_target设置新的工作频率。
b) 如果新的频率小于当前工作频率,则把一个工作放于workqueue “knteractive_down”,它调用__cpufreq_driver_target设置新的工作频率。
4. 根据温度调节CPU频率
把温度检测和频率调整工作放于一个work中, 然后加入一个workqueue,delay 2ms再执行。其调用流程如下:
温度检测及频率计算函数->
cpufreq_driver_target->
__cpufreq_driver_target->
...与第2.中的一样。