[RK3288][Android6.0] GPU DVFS控制策略小結
Platform: Rockchip
OS: Android 6.0
Kernel: 3.10.92
mali_device_driver 分為兩個部分 : platform_dependent_part 和 common_parts, 參見 mali_kbase_config_rk.c 開頭部分的註釋.
gpu dvfs核心控制在mali_kbase_dvfs.c中.
s_mali_dvfs_level_table 定義gpu所有可變的freq level以及對應的min utilisation和max utilisation.
static struct mali_dvfs_level_t s_mali_dvfs_level_table[] = {
{100000, 0, 70} ,
{160000, 50, 65},
{266000, 60, 78},
{350000, 65, 75},
{400000, 70, 75},
{500000, 90, 100},
};
不過它會被dts中的operating-points所覆蓋
clk_gpu_dvfs_table: clk_gpu {
operating-points = <
/* KHz uV */
200000 1200000
300000 1200000
400000 1200000
>;
};
gpu dvfs執行時, common parts (metrics system)會週期地(20ms)通知回撥 platform_dependent_part 中的函式 kbase_platform_dvfs_event(), 並傳入當前的 gpu utilization.
dvfs_callback -> kbase_pm_get_dvfs_action -> kbase_platform_dvfs_event
int kbase_platform_dvfs_event(struct kbase_device *kbdev,
u32 utilisation, /* calculated_utilisation. */
u32 util_gl_share_no_use,
u32 util_cl_share_no_use[2])
{
struct rk_dvfs_t *dvfs = get_rk_dvfs(kbdev);
dvfs->utilisation = utilisation;
if (dvfs->is_enabled) {
/* run 'mali_dvfs_work' in 'mali_dvfs_wq', on cpu0. */
queue_work_on(0, dvfs->mali_dvfs_wq, &(dvfs->mali_dvfs_work));
}
return 1;
}
該 event 被轉發到 mali_dvfs_event_proc() 中處理.
static void mali_dvfs_event_proc(struct work_struct *w)
{
int ret = 0;
struct rk_dvfs_t *dvfs = work_to_dvfs(w);
int temp = rockchip_tsadc_get_temp(1, 0);
if (INVALID_TEMP == temp) {
D("got invalid temp, reset to 0.");
temp = 0;
}
dvfs->sum_of_temps += temp;
dvfs->times_of_temp_measures++;
if (dvfs->times_of_temp_measures >= NUM_OF_TEMP_MEASURES_IN_A_SESSION) {
dvfs->temp = dvfs->sum_of_temps
/ NUM_OF_TEMP_MEASURES_IN_A_SESSION;
dvfs->times_of_temp_measures = 0;
dvfs->sum_of_temps = 0;
}
/*-------------------------------------------------------*/
mutex_lock(&(dvfs->dvfs_mutex));
if (is_overheated(dvfs)) {
if (could_jump_down(dvfs)) {
I("to jump down for overheated, temp:%d.",
dvfs->temp);
ret = jump_down_actually(dvfs);
if (ret)
E("fail to jump down, ret:%d.", ret);
} else {
W("overheated! temp:%d, but can't jump down anymore.",
dvfs->temp);
}
dvfs->temp = 0;
goto EXIT;
}
/* If calculated_utilisation asks current_level to jump up,
* and current_level could jump up,
* then .... */
if (dvfs->utilisation > get_util_max_threshold_of_curr_level(dvfs) &&
could_jump_up(dvfs)) {
inc_requests_to_jump_up(dvfs);
if (are_enough_jump_up_requests(dvfs)) {
V("to jump up to highest, util:%d, curr_level:%d.",
dvfs->utilisation,
get_current_level(dvfs));
ret = jump_up_to_highest_actually(dvfs);
if (ret) {
E("fail to jump up, ret:%d.", ret);
goto EXIT;
}
reset_requests_to_jump_up(dvfs);
}
reset_requests_to_jump_down(dvfs);
goto EXIT;
} else if (dvfs->utilisation
< get_util_min_threshold_of_curr_level(dvfs) &&
could_jump_down(dvfs)) {
inc_requests_to_jump_down(dvfs);
if (are_enough_jump_down_requests(dvfs)) {
V("to jump down actually, util:%d, curr_level:%d",
dvfs->utilisation,
get_current_level(dvfs));
jump_down_actually(dvfs);
reset_requests_to_jump_down(dvfs);
}
reset_requests_to_jump_up(dvfs);
goto EXIT;
} else {
reset_requests_to_jump_down(dvfs);
reset_requests_to_jump_up(dvfs);
V("stay in current_level, util:%d, curr_level:%d.",
dvfs->utilisation,
get_current_level(dvfs));
}
EXIT:
mutex_unlock(&(dvfs->dvfs_mutex));
}
a. 如果當前溫度過高,那麼執行jump_down_actually()下調頻率.
b. 如果當前utilization 大於 max_utilization_of_current_dvfs_level, 則直接上跳到最高頻率(jump_up_to_highest_actually() ).不過要滿足NUM_OF_REQUESTS_TO_PERFORM_ACTUAL_JUMP_UP的次數才上跳.
c. 如果當前utilization 小於max_utilization_of_current_dvfs_level,則一級級下跳(jump_down_actually()).不過要滿足NUM_OF_REQUESTS_TO_PERFORM_ACTUAL_JUMP_DOWN的次數才下跳.
如果覺得目前上跳策略太激進, 則可以在 mali_dvfs_event_proc() 進一步修改, 比如每次只上跳一級, 而不是直接到 highest.