DVFS 即动态电压频率调整,动态技术则是根据芯片所运行的应用程序对计算能力的不同需要,动态调节芯片的运行频率和电压,从而达到节能的目的。
Sysfs接口:
Cpufreq提供接口位于/sys/devices/system/cpu/cpu0/cpufreq 目录
cat scaling_available_frequencies:打印当前软件支持的频率值,与board文件static struct dvfs_arm_tabledvfs_cpu_logic_table[]给出频率值一样
cat cpuinfo_cur_freq:打印当前系统运行频率值
Dvfs大致控制流程:
1.采集与系统负载有关的信号,计算当前的系统负载。
2.根据系统的当前负载,预测系统在下一时间段需要的性能。
3.将预测的性能转换成需要的频率,从而调整芯片的时钟设置。
4.根据新的频率计算相应的电压。通知电源管理模块调整给CPU的电压。
相应知识还可以参考:http://blog.csdn.net/droidphone/article/details/9346981
以rk3066boxcore为例。rk3066box采用了分立电源,DVFS调节系统的VDD_ARM、VDD_LOG电压是通过PWM模块控制;如果是采用PMU电源管理的话,那这二个电压是通过PMU Regulator来控制的。
一、DVFS 设备资源层代码实现:代码位于Board-rk30-box.c(arch\arm\mach-rk30)
1) rk3066box是通过DVS0_CTL信号调节VDD_ARM、DVS1_CTL信号调节VDD_LOG电压。pwm资源层代码实现:
static struct regulator_consumer_supplypwm_dcdc1_consumers[] = {
{
.supply= "vdd_cpu", //用户支持的接口映射
}
};
struct regulator_init_data pwm_regulator_init_dcdc[] =
{
{//Regulator操作约束,比如这里设置电压范围:0.6-1.8V
.constraints= {
.name= "PWM_DCDC1",
.min_uV= 600000,
.max_uV= 1800000, //0.6-1.8V
.apply_uV= true,
.valid_ops_mask= REGULATOR_CHANGE_STATUS | REGULATOR_CHANGE_VOLTAGE,
},
.num_consumer_supplies= ARRAY_SIZE(pwm_dcdc1_consumers),
.consumer_supplies= pwm_dcdc1_consumers,
},
};
static struct pwm_platform_data pwm_regulator_info[] ={
{//pwm资源设置,比如pwm GPIO口、挂起电压、参考电压、电压范围的设置
.pwm_id= 0,
.pwm_gpio= RK30_PIN0_PA3,
.pwm_iomux_name= GPIO0A3_PWM0_NAME,
.pwm_iomux_pwm= GPIO0A_PWM0,
.pwm_iomux_gpio= GPIO0A_GPIO0A3,
.pwm_voltage= 1300000,
.suspend_voltage= 1050000,
.min_uV= 950000,
.max_uV = 1400000,
.coefficient= 455, //45.5%
.pwm_voltage_map= pwm_voltage_map,
.init_data = &pwm_regulator_init_dcdc[0],
},
};
2) 动态调频的资源设置
static struct dvfs_arm_table dvfs_cpu_logic_table[] = {
//frequency为频率值、cpu_volt为arm电压值、logic_volt为log电压值
{.frequency = 504* 1000, .cpu_volt = 1150 * 1000, .logic_volt = 1125 *1000},//0.975V/1.000V
{.frequency =1200 * 1000, .cpu_volt = 1250 *1000, .logic_volt = 1250 *1000},//1.100V/1.050V
{.frequency =1608 * 1000, .cpu_volt = 1400 *1000, .logic_volt = 1350 *1000},//1.325V/1.175V
{.frequency =CPUFREQ_TABLE_END},
};
二、DVFS初始化流程:Dvfs.c(arch\arm\mach-rk30) 和 Dvfs.c (arch\arm\plat-rk)
int rk_dvfs_init(void)
{
int i = 0;
//创建并初始化DVFS工作队列
for (i = 0; i <ARRAY_SIZE(rk30_vds); i++) {
rk_regist_vd(rk30_vds[i]);
}
for (i = 0; i <ARRAY_SIZE(rk30_pds); i++) {
rk_regist_pd(&rk30_pds[i]);
}
for (i = 0; i <ARRAY_SIZE(rk30_clks); i++) {
rk_regist_clk(&rk30_clks[i]);
}
for (i = 0; i <ARRAY_SIZE(rk30_depends); i++) {
rk_regist_depends(&rk30_depends[i]);
}
//获得cpu系统时钟信号
dvfs_clk_cpu =dvfs_get_dvfs_clk_byname("cpu");
avs_board_init(&rk30_avs_ctr);
printk("rk30_dvfs_init\n");
return 0;
}
三、DVFS 动态调频流程
当发生了频率的调整,DVFS通过dvfs_set_rate()【Dvfs.c (arch\arm\plat-rk)】函数来改变频率值
int dvfs_set_rate(struct clk *clk,unsigned long rate)
{
int ret = 0;
struct vd_node *vd;
DVFS_DBG("%s(%s(%lu))\n",__func__, clk->name, rate);
if (!clk->dvfs_info) {
DVFS_ERR("%s :This clkdo not support dvfs!\n", __func__);
ret = -1;
} else {
vd =clk->dvfs_info->vd;
//mutex_lock(&vd->dvfs_mutex);
mutex_lock(&rk_dvfs_mutex);
ret =vd->vd_dvfs_target(clk, rate);
mutex_unlock(&rk_dvfs_mutex);
//mutex_unlock(&vd->dvfs_mutex);
}
DVFS_DBG("%s(%s(%lu)),isend\n", __func__, clk->name, rate);
return ret;
}
dvfs_set_rate()改变的频率值,通过cpufreq_notify_transition()【Cpufreq.c (drivers\cpufreq)】函数更新当前系统的频率值
voidcpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state)
{
}
VDD_ARM与VDD_LOG电压通过dvfs_scale_volt()【Dvfs.c (arch\arm\plat-rk)】来调整的。
int dvfs_scale_volt(struct vd_node*vd_clk, struct vd_node *vd_dep,
int volt_old, int volt_new,int volt_dep_old, int volt_dep_new, int clk_biger_than_dep, intdep_biger_than_clk)
{ …..
……
dvfs_regulator_set_voltage_readback(regulator,volt, volt); //调用这个函数,最终会调用pwm_regulator_set_voltage()调节电压
…….
}