跳到主要内容

CPUF-DVFS 开发指南

文件标识:RK-KF-YF-012

发布版本:V1.0.1

日期:2021-05-27

文件密级:□绝密 □秘密 □内部资料 ■公开

免责声明

本文档按“现状”提供,瑞芯微电子股份有限公司(“本公司”,下同)不对本文档的任何陈述、信息和内容的准确性、可靠性、完整性、适销性、特定目的性和非侵权性提供任何明示或暗示的声明或保证。本文档仅作为使用指导的参考。

由于产品版本升级或其他原因,本文档将可能在未经任何通知的情况下,不定期进行更新或修改。

商标声明

“Rockchip”、“瑞芯微”、“瑞芯”均为本公司的注册商标,归本公司所有。

本文档可能提及的其他所有注册商标或商标,由其各自拥有者所有。

版权所有 © 2021 瑞芯微电子股份有限公司

超越合理使用范畴,非经本公司书面许可,任何单位和个人不得擅自摘抄、复制本文档内容的部分或全部,并不得以任何形式传播。

瑞芯微电子股份有限公司

Rockchip Electronics Co., Ltd.

地址: 福建省福州市铜盘路软件园A区18号

网址: www.rock-chips.com

客户服务电话: +86-4007-700-590

客户服务传真: +86-591-83951833

客户服务邮箱: fae@rock-chips.com


前言

概述

本章主要描述 CPUFreq-DVFS 的相关的重要概念、配置方法和调试接口。

产品版本

产品名称内核版本
RK312xLinux3.10
RK322xLinux3.10
RK3288Linux3.10
RK3368Linux3.10
RK3328Linux3.10

读者对象

本文档(本指南)主要适用于以下工程师:

技术支持工程师

软件开发工程师

修订记录

版本号作者修改日期修改说明
V1.0.0肖锋/陈亮2017-02-17初始版本
V1.0.1黄莹2021-05-27修改格式,增加版权信息

目录

[TOC]


重要概念

在 CMOS 电路中功率消耗主要可以分为动态功率消耗和静态功率消耗,公式如下:

power-formula

其中 C 代表负载电容的容值,V 是工作电压,α 是当前频率下的翻转率,f 为工作频率,I_dq 代表静态电流。公式的前部分代表的是动态功率消耗,后部分则代表的是静态功率消耗。从公式中可以看出,想要降低动态功率消耗可以从 C、V、α、f 着手,对于软件来讲常用的调节方式只涉及到 V、f 两个因素。

DVFS(Dynamic Voltage and Frequency Scaling)动态电压频率调节,是一种实时的电压和频率调节技术。目前 Rockchip Linux3.10 内核中支持 DVFS 的模块有 CPU、GPU、DDR。

CPUFreq 是内核开发者定义的一套支持动态调整 CPU 频率的框架模型。它能有效的降低 CPU 的功耗,同时兼顾 CPU 的性能。

CPUFreq 通过不同的变频管理器,选择一个合适的频率供 CPU 使用,目前的内核版本提供了以下几种变频管理器:

  • interactive:根据 CPU 负载动态调频调压;

  • conservative:保守策略,逐级调整频率和电压;

  • ondemand:根据 CPU 负载动态调频调压,比 interactive 策略反应慢;

  • userspace:用户自己设置电压和频率,系统不会自动调整;

  • powersave:功耗优先,始终将频率设置在最低值;

  • performance:性能优先,始终将频率设置为最高值。

DVFS 为 CPUFreq 提供了底层驱动,CPUFreq-DVFS framework 如下:

DVFS-framework

Voltage domain 表示电压域,可以独立调节电压,简称 VD。

Power domain 表示电源域,只能开关,电压大小等于 VD 的电压,简称 PD。一个 VD 包括一个或多个 PD,一个 PD 又包含一个或多个 Module。

Opp-table 表示频率电压表,支持 DVFS 的模块都有一个 opp-table 来描述各个频率点需要的运行电压。

Voltage/power domain framework 如下:

Voltage-power-domain-framework

配置方法

DVFS 节点介绍

DVFS 的主要设计思路是:一个 VD 下面可以包括多个 PD,一个 PD 下面可以包含多个 CLK,每个 CLK 对应的模块,都有一个需求电压,但是一个 VD 最终只有一个电压值,为了满足它下面所以模块的需求,调节电压时,需要遍历 VD 下面的所有模块,找到最大电压值。

目前常用的 VD 有三种:vdd_arm、vdd_gpu 和 vdd_logic,vdd_arm 给 arm 核供电,vdd_gpu 给 gpu 供电,vdd_logic 给 soc 的各个外设供电,包括 DDR\I2C\USB\GMAC 等。由于电源设计方案的不同,可能会把这三个 VD 合并在一起,在配置 DVFS 节点的时候,常见的有如下几种情况:

CPU、GPU、DDR 三路分开供电

比如 RK3288 上,CPU 使用 vdd_arm 供电,GPU 使用 vdd_gpu 供电,DDR 使用 vdd_logic 供电,所以 dvfs 节点下有三个并列的子节点,如下:

arch/arm/boot/dts/rk3288.dtsi
dvfs {
vd_arm: vd_arm {
regulator_name = "vdd_arm";
pd_core {
clk_core_dvfs_table: clk_core {
};
};
};
vd_logic: vd_logic {
regulator_name = "vdd_logic";
pd_ddr {
clk_ddr_dvfs_table: clk_ddr{
};
};
};
vd_gpu: vd_gpu {
regulator_name = "vdd_gpu";
pd_gpu {
clk_gpu_dvfs_table: clk_gpu {
};
};
};
};

CPU 单独供电,GPU 和 DDR 共用一路供电

比如 RK312X 上,CPU 使用 vdd_arm 供电,GPU 和 DDR 共同使用 vdd_logic 供电,如下:

dvfs {
vd_arm: vd_arm {
regulator_name = "vdd_arm";
pd_core {
clk_core_dvfs_table: clk_core {
};
};
};
vd_logic: vd_logic {
regulator_name = "vdd_logic";
pd_ddr {
clk_ddr_dvfs_table: clk_ddr {
};
};
pd_gpu {
clk_gpu_dvfs_table: clk_gpu {
};
};
};
};

CPU、GPU 和 DDR 三路共用一路供电

比如RK3126 86V的样机上,CPU、GPU和DDR共同使用vdd\_arm供电,如下:
arch/arm/boot/dts/rk3126-86v.dts
dvfs {
vd_arm: vd_arm {
regulator_name = "vdd_arm";
pd_core {
clk_core_dvfs_table: clk_core {
};
};
pd_ddr {
clk_ddr_dvfs_table: clk_ddr {
};
};
pd_gpu {
clk_gpu_dvfs_table: clk_gpu {
};
};
};
};

CPU DVFS 节点配置

CPU DVFS 节点包含频率电压表、leakage 调压(可选)、pvtm 调压(可选)和温控。

clk_core_dvfs_table: clk_core {
/* 正常的频率电压表 */
operating-points = <
/* KHz uV */
408000 900000
600000 900000
696000 950000
816000 1000000
1008000 1050000
1200000 1100000
>;
/*
支持根据pvtm调整电压表。(可选)
如果增加了以下两个属性,即使support-pvtm 为0,
operating-points中的电压表都无效,使用pvtm-operating-points
中的电压表。如果support-pvtm为1,则代码中还会根据pvtm值
调整电压表。
*/
support-pvtm = <0>;
pvtm-operating-points = <
/* KHz uV margin(uV)*/
408000 900000 25000
600000 900000 25000
696000 950000 25000
816000 1000000 25000
1008000 1050000 25000
1200000 1100000 25000
>;

/* 支持根据leakage调整电压表。(可选) */
lkg_adjust_volt_en = <1>; /* 1表示开启调整电压表功能,0 关闭 */
channel = <0>; /* 0表示获取cpu的leakage 值 */
def_table_lkg = <35>; /* leakage的参考值或分割线 */
min_adjust_freq = <216000>; /* 大于216M的频率,对应的电压将会被调整 */
/*
1、表格中lkg值大于def_table_lkg的,后面的volt会变成负值
2、每行是一个区间,所以表中的意思是:
0<lkg<=14, 电压增加25mV
15<lkg<=35, 电压不变
35<lkg<=60, 电压降低25mV
*/
lkg_adjust_volt_table = <
/*lkg(mA) volt(uV)*/
0 25000
14 0
60 25000
>;

/* 温控 */
temp-limit-enable = <1>; /* 1表示开启温控,0关闭 */
tsadc-ch = <1>; /* 获取温度的通道 */
target-temp = <80>; /* 目标温度 */
min_temp_limit = <48>; /* 温控会降频,但必须大于这个最低值*/
/*
normal温控策略的配置表,策略如下:
温度超过目标温度3度,每个采样周期,最高频率降低96M,以此类推。
温度低于目标温度3度,每个采样周期,最高频率升高96M,以此类推。
*/
normal-temp-limit = <
/* delta-temp delta-freq */
3 96000
6 144000
9 192000
15 384000
>;
/*
performance温控策略的配置表,策略如下:
温度超过100度,将最高频率降到816M。
*/
performance-temp-limit = < /
/* temp freq */
100 816000
>;
};

GPU DVFS 节点配置

GPU 也可以支持 leakage 调压,pvtm 调压,不过电压的收益很小,一直没用。GPU 温控对 GPU 性能也有影响,一般也不建议开启。所以 GPU 的 DVFS 节点一般只有频率电压表。

clk_gpu_dvfs_table: clk_gpu{
/* 正常的频率电压表 */
operating-points = <
/* KHz uV */
300000 950000
420000 1050000
500000 1150000
>;
};

DDR DVFS 节点配置

DDR 部分包含频率电压表、场景变频(可选)、负载变频(可选)。

clk_gpu_dvfs_table: clk_gpu{
/* 正常的频率电压表 */
operating-points = <
/* KHz uV */
200000 1050000
300000 1050000
400000 1100000
533000 1150000
>;

/* 场景变频 */
freq-table = <
/*status freq(KHz)*/
SYS_STATUS_NORMAL 400000 /*普通场景,没有进入任何特殊场景 */
SYS_STATUS_SUSPEND 200000 /* 一级待机灭屏*/
SYS_STATUS_VIDEO_1080P 240000 /* 1080P视频 */
SYS_STATUS_VIDEO_4K 400000 /* 4K视频 */
SYS_STATUS_PERFORMANCE 528000 /* 跑分 */
SYS_STATUS_DUALVIEW 400000 /* 接入HDMI后,双屏显示的场景 */
SYS_STATUS_BOOST 324000 /* 用于负载变频打开后,有触屏动作时,立刻抬高DDR的频率,减小响应时间 */
SYS_STATUS_ISP 400000 /* 拍照场景 */
>;

/*
负载变频,开启后,场景变频中的NORMAL失效,
其他场景依然有效,而且优先级高于负载变频。
*/
bd-freq-table = <
/* 根据上层显示对ddr带宽的需求,调整ddr频率 */
/* bandwidth freq */
5000 800000
3500 456000
2600 396000
2000 324000
>;
auto-freq-table = <
/* 根据ddr利用率 ,调整ddr频率 */
240000
324000
396000
528000
>;
auto-freq=<1>; /* 1表示开启负载变频,0表示关闭 */
};

代码使用接口

DVFS 接口函数定义在 include/linux/rockchip/dvfs.h,常用的函数如下:

/* 获取一个clk的DVFS节点*/
struct dvfs_node *clk_get_dvfs_node(char *clk_name);

/* 释放一个clk的DVFS节点 */
void clk_put_dvfs_node(struct dvfs_node *clk_dvfs_node);

/* 使能一个系统clk的DVFS功能 */
int clk_enable_dvfs(struct clk *clk);

/* 关闭一个系统clk的DVFS功能 */
int clk_disable_dvfs(struct clk *clk);

/* 注册一个调频调压回调函数,使该clk不通过系统默认的调频调压接口 */
void dvfs_clk_register_set_rate_callback(struct clk *clk, clk_dvfs_target_callback clk_dvfs_target);

/* DVFS 变频入口函数 */
int dvfs_clk_set_rate(struct dvfs_node *clk_dvfs_node, unsigned long rate);

调试接口

dvfs_tree 查看

通过命令 cat /sys/dvfs/dvfs_tree 可以查看当前频率电压的相关信息。

 -------------DVFS TREE-----------
|- voltage domain:vd_logic /* vd_ogic 电压由 GPU和DDR中较大值 1050000*/
|- current voltage:1050000
|- current regu_mode:UNKNOWN
| |
| |- power domain:pd_gpu, status = OFF, current volt = 900000
| | | /* GPU 当前频率和所需电压 */
| | |- clock: clk_gpu current: rate 200000, volt = 900000
| | |- clk limit(enable):[200000000, 492000000]; last set rate = 200000
| | | |- freq = 200000, volt = 900000 /* GPU频率电压表 */
| | | |- freq = 300000, volt = 950000
| | | |- freq = 400000, volt = 1025000
| | | |- freq = 492000, volt = 1100000
| | |- clock: clk_gpu current: rate 200000, regu_mode = UNKNOWN,
| |
| |- power domain:pd_ddr, status = OFF, current volt = 1050000
| | | /* DDR当前频率和所需电压 */
| | |- clock: clk_ddr current: rate 792000, volt = 1050000
| | |- clk limit(enable):[400000000, 800000000]; last set rate = 792000
| | | |- freq = 400000, volt = 900000 /* DDR频率电压表 */
| | | |- freq = 800000, volt = 1050000
| | |- clock: clk_ddr current: rate 792000, regu_mode = UNKNOWN,
|
|- voltage domain:vd_arm /* vd_arm电压只有cpu决定950000*/
|- current voltage:950000
|- current regu_mode:UNKNOWN
| |
| |- power domain:pd_core, status = OFF, current volt = 950000
| | | /* CPU当前频率和所需电压 */
| | |- clock: clk_core current: rate 408000,volt = 950000
| | |- clk limit(enable):[408000000, 1296000000]; last set rate = 408000
| | | |- freq = 408000, volt = 950000 /* CPU频率电压表 */
| | | |- freq = 600000, volt = 950000
| | | |- freq = 816000, volt = 1000000
| | | |- freq = 1008000, volt = 1100000
| | | |- freq = 1200000, volt = 1225000
| | | |- freq = 1296000, volt = 1300000
| | |- clock: clk_core current: rate 408000, regu_mode = UNKNOWN,

-------------DVFS TREE END------------

pm_tests 节点使用方法

make ARCH=arm64 menuconfig 或者 make menuconfig

platform-selection

sys-pm_test-support

重新编译烧写,可以看到/sys/pm_tests/节点,主要有如下功能:

/sys/pm_tests/clk_rate       /* 用于设置频率和获取频率 */
/sys/pm_tests/clk_volt /* 用于设置电压和获取电压 */
/sys/pm_tests/cpu_usage /* 用于CPU的高负载测试 */
/sys/pm_tests/pvtm /* 用于获取PVTM值 */

比较常用的是频率和电压的修改:

/* 设置频率 */
echo set clk_ddr 300000000 > /sys/pm_tests/clk_rate
echo set clk_gpu 297000000 > /sys/pm_tests/clk_rate
echo set clk_core 816000000 > /sys/pm_tests/clk_rate
/* 获取频率 */
echo get clk_ddr > /sys/pm_tests/clk_rate
echo get clk_gpu> /sys/pm_tests/clk_rate
echo get clk_core > /sys/pm_tests/clk_rate

/* 设置电压 */
echo set vdd_logic 950000 > /sys/pm_tests/clk_volt
echo set vdd_gpu 950000 > /sys/pm_tests/clk_volt
echo set vdd_arm 950000 > /sys/pm_tests/clk_volt
/* 获取电压 */
echo get vdd_logic> /sys/pm_tests/clk_volt
echo get vdd_gpu > /sys/pm_tests/clk_volt
echo get vdd_arm> /sys/pm_tests/clk_volt

需要注意:

1、clk 和 vdd 的名字不同的平台可能不一样,根据实际情况修改。比如 RK3368 上,大核 A53 的 clk 名字是 clk_core_b,小核 A53 的是 clk_core_l。

2、测试的过程中,如果是升频,需要先提高 clk 对应的 vdd 的电压。

cpufreq 节点使用方法

在/sys/devices/system/cpu/下有每个 cpu 对应的节点,如 cpu0/cpufreq/、cpu1/cpufreq/等等。

目前有的芯片支持大小核的架构,如 RK3368,芯片内包含两个 cluster(即包含两组 cpu)。有的芯片不支持大小核架构,即只有一个 cluster,比如 RK312x、RK3288。每个 cluster 下的 cpu 共用一个 clk,所以只要对同个 cluster 下的其中一个 cpu 操作即可。对于 RK3288,只要操作 cpu0 即可。对于 RK3368,可以分别操作 cpu0(小核)和 cpu4(大核)。

每个 cpufreq 节点下有如下子节点:

related_cpus          /* 同个cluster下的所有cpu */
affected_cpus /* 同个cluster下未关的cpu */
cpuinfo_transition_latency /* 两个不同频率之间切换时所需要的时间,单位ns */
cpuinfo_max_freq /* CPU 硬件所支持的最高运行频率 */
cpuinfo_min_freq /* CPU 硬件所支持的最低运行频率 */
cpuinfo_cur_freq /* 硬件寄存器中读取 CPU 当前所处的运行频率*/
scaling_available_frequencies /* 系统支持的频率*/
scaling_available_governors /* 系统支持的变频策略*/
scaling_governor /* 当前使用的变频策略*/
scaling_cur_freq /* 当前频率*/
scaling_max_freq /* 软件上限制的最高频率*/
scaling_min_freq /* 软件上限制的最低频率*/
scaling_setspeed /* 需将governor类型切换为userspace,才会出现,可以通过该节点修改频率*/

比较常用的是:

1、查看系统支持的频率,在串口中输入如下命令:

cd sys/devices/system/cpu/cpu0/cpufreq/
catscaling_available_frequencies

2、cpu 定频,如 cpu0 定频 216MHz,在串口中输入如下命令:

cd sys/devices/system/cpu/cpu0/cpufreq/
echo userspace > scaling_governor
echo 216000 > scaling_setspeed

设置完后,查看当前频率:

cat scaling_cur_freq

3、限制最高最低频,如限制 cpu 最高频 1200MHz,最低频 216MHz,在串口中输入如下命令:

cd sys/devices/system/cpu/cpu0/cpufreq/
echo 216000 >scaling_min_freq
echo 1200000 >scaling_max_freq

设置完后,查看是否生效:

cat scaling_min_freq
cat scaling_max_freq

调试方法

软件上调试主要开启打印。将 include/linux/rockchip/dvfs.h 中的 DVFS_DBG 开启。

#if 1
#define DVFS_DBG(fmt, args...) printk(KERN_INFO "DVFS DBG:\t"fmt, ##args)
#else
#define DVFS_DBG(fmt, args...) {while(0);}
#endif

若确定死机与 dvfs 有关,注意采集现场信息,包括:

当前 arm 电压、log 电压、ddr 电压;

死机画面表现;

死机操作步骤以及概率,场景;

记录当前的固件所使用的 dvfs 列表(arm、gpu、ddr);

若及时发现死机,那么注意触摸下主控,确定温度是否过高(注意安全);

各产品最高主频

产品名称ARM 核最高主频
RK312x4 * A71200MHz
RK322x4 * A71464MHz
RK32884 * A171608MHz
RK33688 * A531512MHz(big)\1200MHz(little)
RK33284 * A531296MHz