| /* |
| * scaling.c |
| * |
| * Copyright (C) 2017 Amlogic, Inc. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| * more details. |
| * |
| */ |
| |
| /** |
| * @file arm_core_scaling.c |
| * Example core scaling policy. |
| */ |
| |
| #include <linux/version.h> |
| #include <linux/module.h> |
| #include <linux/workqueue.h> |
| |
| #if AMLOGIC_GPU_USE_GPPLL |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 16) |
| #include <linux/amlogic/amports/gp_pll.h> |
| #elif (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) |
| #include <linux/amlogic/media/clk/gp_pll.h> |
| #endif |
| #endif |
| |
| #define LOG_MALI_SCALING 1 |
| #include "meson_main2.h" |
| #include "mali_clock.h" |
| |
| static int currentStep; |
| #ifndef CONFIG_MALI_DVFS |
| static int num_cores_enabled; |
| static int lastStep; |
| static struct work_struct wq_work; |
| static mali_plat_info_t* pmali_plat = NULL; |
| #endif |
| static int scaling_mode = MALI_PP_FS_SCALING; |
| extern int mali_pm_statue; |
| //static int scaling_mode = MALI_SCALING_DISABLE; |
| //static int scaling_mode = MALI_PP_SCALING; |
| |
| #if AMLOGIC_GPU_USE_GPPLL |
| static struct gp_pll_user_handle_s *gp_pll_user_gpu; |
| static int is_gp_pll_get; |
| static int is_gp_pll_put; |
| #endif |
| static unsigned scaling_dbg_level = 0; |
| module_param(scaling_dbg_level, uint, 0644); |
| MODULE_PARM_DESC(scaling_dbg_level , "scaling debug level"); |
| |
| #define scalingdbg(level, fmt, arg...) \ |
| do { \ |
| if (scaling_dbg_level >= (level)) \ |
| printk(fmt , ## arg); \ |
| } while (0) |
| |
| #ifndef CONFIG_MALI_DVFS |
| static int mali_stay_count = 0; |
| static inline void mali_clk_exected(void) |
| { |
| mali_dvfs_threshold_table * pdvfs = pmali_plat->dvfs_table; |
| uint32_t execStep = currentStep; |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) |
| mali_dvfs_threshold_table *dvfs_tbl = &pmali_plat->dvfs_table[currentStep]; |
| #endif |
| |
| //if (pdvfs[currentStep].freq_index == pdvfs[lastStep].freq_index) return; |
| if ((pdvfs[execStep].freq_index == pdvfs[lastStep].freq_index) || |
| (pdvfs[execStep].clk_freq == pdvfs[lastStep].clk_freq)){ |
| return; |
| } |
| |
| #if AMLOGIC_GPU_USE_GPPLL |
| if (0 == strcmp(dvfs_tbl->clk_parent, "gp0_pll")) { |
| gp_pll_request(gp_pll_user_gpu); |
| if (!is_gp_pll_get) { |
| //printk("not get pll\n"); |
| execStep = currentStep - 1; |
| } |
| } else { |
| //not get the gp pll, do need put |
| is_gp_pll_get = 0; |
| is_gp_pll_put = 0; |
| gp_pll_release(gp_pll_user_gpu); |
| } |
| #elif (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) |
| if ((0 == strcmp(dvfs_tbl->clk_parent, "gp0_pll")) && |
| !IS_ERR(dvfs_tbl->clkp_handle) && |
| (0 != dvfs_tbl->clkp_freq)) { |
| clk_prepare_enable(dvfs_tbl->clkp_handle); |
| clk_set_rate(dvfs_tbl->clkp_handle, dvfs_tbl->clkp_freq); |
| } |
| |
| #endif |
| //mali_dev_pause(); |
| mali_clock_set(pdvfs[execStep].freq_index); |
| //mali_dev_resume(); |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) |
| #if AMLOGIC_GPU_USE_GPPLL==0 |
| if ((0 == strcmp(pdvfs[lastStep].clk_parent,"gp0_pll")) && |
| (0 != strcmp(pdvfs[execStep].clk_parent, "gp0_pll"))) { |
| clk_disable_unprepare(pdvfs[lastStep].clkp_handle); |
| } |
| #endif |
| #endif |
| |
| lastStep = execStep; |
| #if AMLOGIC_GPU_USE_GPPLL |
| if (is_gp_pll_put) { |
| //printk("release gp0 pll\n"); |
| gp_pll_release(gp_pll_user_gpu); |
| gp_pll_request(gp_pll_user_gpu); |
| is_gp_pll_get = 0; |
| is_gp_pll_put = 0; |
| } |
| #endif |
| |
| } |
| #if AMLOGIC_GPU_USE_GPPLL |
| static int gp_pll_user_cb_gpu(struct gp_pll_user_handle_s *user, |
| int event) |
| { |
| if (event == GP_PLL_USER_EVENT_GRANT) { |
| //printk("granted\n"); |
| is_gp_pll_get = 1; |
| is_gp_pll_put = 0; |
| schedule_work(&wq_work); |
| } else if (event == GP_PLL_USER_EVENT_YIELD) { |
| //printk("ask for yield\n"); |
| is_gp_pll_get = 0; |
| is_gp_pll_put = 1; |
| schedule_work(&wq_work); |
| } |
| |
| return 0; |
| } |
| #endif |
| |
| int mali_perf_set_num_pp_cores(int cores) |
| { |
| cores = cores; |
| return 0; |
| } |
| |
| static void do_scaling(struct work_struct *work) |
| { |
| mali_dvfs_threshold_table * pdvfs = pmali_plat->dvfs_table; |
| int err = mali_perf_set_num_pp_cores(num_cores_enabled); |
| if (err < 0) scalingdbg(1, "set pp failed"); |
| |
| scalingdbg(1, "set pp cores to %d\n", num_cores_enabled); |
| scalingdbg(1, "pdvfs[%d].freq_index=%d, pdvfs[%d].freq_index=%d\n", |
| currentStep, pdvfs[currentStep].freq_index, |
| lastStep, pdvfs[lastStep].freq_index); |
| mali_clk_exected(); |
| #ifdef CONFIG_MALI400_PROFILING |
| _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_SINGLE | |
| MALI_PROFILING_EVENT_CHANNEL_GPU | |
| MALI_PROFILING_EVENT_REASON_SINGLE_GPU_FREQ_VOLT_CHANGE, |
| get_current_frequency(), |
| 0, 0, 0, 0); |
| #endif |
| } |
| #endif |
| |
| u32 revise_set_clk(u32 val, u32 flush) |
| { |
| u32 ret = 0; |
| #ifndef CONFIG_MALI_DVFS |
| mali_scale_info_t* pinfo; |
| |
| pinfo = &pmali_plat->scale_info; |
| |
| if (val < pinfo->minclk) |
| val = pinfo->minclk; |
| else if (val > pinfo->maxclk) |
| val = pinfo->maxclk; |
| |
| if (val != currentStep) { |
| currentStep = val; |
| if (flush) |
| schedule_work(&wq_work); |
| else |
| ret = 1; |
| } |
| #endif |
| return ret; |
| } |
| |
| void get_mali_rt_clkpp(u32* clk, u32* pp) |
| { |
| #ifndef CONFIG_MALI_DVFS |
| *clk = currentStep; |
| *pp = num_cores_enabled; |
| #endif |
| } |
| |
| u32 set_mali_rt_clkpp(u32 clk, u32 pp, u32 flush) |
| { |
| u32 ret = 0; |
| #ifndef CONFIG_MALI_DVFS |
| mali_scale_info_t* pinfo; |
| u32 flush_work = 0; |
| |
| pinfo = &pmali_plat->scale_info; |
| if (clk < pinfo->minclk) |
| clk = pinfo->minclk; |
| else if (clk > pinfo->maxclk) |
| clk = pinfo->maxclk; |
| |
| if (clk != currentStep) { |
| currentStep = clk; |
| if (flush) |
| flush_work++; |
| else |
| ret = 1; |
| } |
| |
| if (flush) |
| mali_stay_count = pmali_plat->dvfs_table[currentStep].keep_count; |
| |
| if (pp < pinfo->minpp) |
| pp = pinfo->minpp; |
| else if (pp > pinfo->maxpp) |
| pp = pinfo->maxpp; |
| |
| if (pp != num_cores_enabled) { |
| num_cores_enabled = pp; |
| if (flush) |
| flush_work++; |
| else |
| ret = 1; |
| } |
| |
| if (flush_work) |
| schedule_work(&wq_work); |
| #endif |
| return ret; |
| } |
| |
| void revise_mali_rt(void) |
| { |
| #ifndef CONFIG_MALI_DVFS |
| set_mali_rt_clkpp(currentStep, num_cores_enabled, 1); |
| #endif |
| } |
| |
| void flush_scaling_job(void) |
| { |
| #ifndef CONFIG_MALI_DVFS |
| cancel_work_sync(&wq_work); |
| #endif |
| } |
| |
| #ifndef CONFIG_MALI_DVFS |
| static u32 enable_one_core(void) |
| { |
| scalingdbg(2, "meson: one more pp, curent has %d pp cores\n", num_cores_enabled + 1); |
| return set_mali_rt_clkpp(currentStep, num_cores_enabled + 1, 0); |
| } |
| |
| static u32 disable_one_core(void) |
| { |
| scalingdbg(2, "meson: disable one pp, current has %d pp cores\n", num_cores_enabled - 1); |
| return set_mali_rt_clkpp(currentStep, num_cores_enabled - 1, 0); |
| } |
| |
| static u32 enable_max_num_cores(void) |
| { |
| return set_mali_rt_clkpp(currentStep, pmali_plat->scale_info.maxpp, 0); |
| } |
| |
| static u32 enable_pp_cores(u32 val) |
| { |
| scalingdbg(2, "meson: enable %d pp cores\n", val); |
| return set_mali_rt_clkpp(currentStep, val, 0); |
| } |
| #endif |
| |
| int mali_core_scaling_init(mali_plat_info_t *mali_plat) |
| { |
| #ifndef CONFIG_MALI_DVFS |
| if (mali_plat == NULL) { |
| scalingdbg(2, " Mali platform data is NULL!!!\n"); |
| return -1; |
| } |
| |
| pmali_plat = mali_plat; |
| num_cores_enabled = pmali_plat->sc_mpp; |
| #if AMLOGIC_GPU_USE_GPPLL |
| gp_pll_user_gpu = gp_pll_user_register("gpu", 1, |
| gp_pll_user_cb_gpu); |
| //not get the gp pll, do need put |
| is_gp_pll_get = 0; |
| is_gp_pll_put = 0; |
| if (gp_pll_user_gpu == NULL) printk("register gp pll user for gpu failed\n"); |
| #endif |
| |
| currentStep = pmali_plat->def_clock; |
| lastStep = currentStep; |
| INIT_WORK(&wq_work, do_scaling); |
| #endif |
| return 0; |
| /* NOTE: Mali is not fully initialized at this point. */ |
| } |
| |
| void mali_core_scaling_term(void) |
| { |
| #ifndef CONFIG_MALI_DVFS |
| flush_scheduled_work(); |
| #if AMLOGIC_GPU_USE_GPPLL |
| gp_pll_user_unregister(gp_pll_user_gpu); |
| #endif |
| #endif |
| } |
| |
| #ifndef CONFIG_MALI_DVFS |
| static u32 mali_threshold [] = { |
| 40, /* 40% */ |
| 50, /* 50% */ |
| 90, /* 90% */ |
| }; |
| #endif |
| |
| void mali_pp_scaling_update(int utilization_pp) |
| { |
| #ifndef CONFIG_MALI_DVFS |
| int ret = 0; |
| |
| if (mali_threshold[2] < utilization_pp) |
| ret = enable_max_num_cores(); |
| else if (mali_threshold[1]< utilization_pp) |
| ret = enable_one_core(); |
| else if (0 < utilization_pp) |
| ret = disable_one_core(); |
| if (ret == 1) |
| schedule_work(&wq_work); |
| #endif |
| } |
| |
| #if LOG_MALI_SCALING |
| void trace_utilization(int utilization_gpu, u32 current_idx, u32 next, |
| u32 current_pp, u32 next_pp) |
| { |
| char direction; |
| if (next > current_idx) |
| direction = '>'; |
| else if ((current_idx > pmali_plat->scale_info.minpp) && (next < current_idx)) |
| direction = '<'; |
| else |
| direction = '~'; |
| |
| scalingdbg(2, "[SCALING]%c (%3d-->%3d)@%3d{%3d - %3d}. pp:(%d-->%d)\n", |
| direction, |
| get_mali_freq(current_idx), |
| get_mali_freq(next), |
| utilization_gpu, |
| pmali_plat->dvfs_table[current_idx].downthreshold, |
| pmali_plat->dvfs_table[current_idx].upthreshold, |
| current_pp, next_pp); |
| } |
| #endif |
| |
| #ifndef CONFIG_MALI_DVFS |
| static void mali_decide_next_status(int utilization_pp, int* next_fs_idx, |
| int* pp_change_flag) |
| { |
| u32 mali_up_limit, decided_fs_idx; |
| u32 ld_left, ld_right; |
| u32 ld_up, ld_down; |
| u32 change_mode; |
| |
| *pp_change_flag = 0; |
| change_mode = 0; |
| |
| scalingdbg(5, "line(%d), scaling_mode=%d, MALI_TURBO_MODE=%d, turbo=%d, maxclk=%d\n", |
| __LINE__, scaling_mode, MALI_TURBO_MODE, |
| pmali_plat->turbo_clock, pmali_plat->scale_info.maxclk); |
| |
| mali_up_limit = (scaling_mode == MALI_TURBO_MODE) ? |
| pmali_plat->turbo_clock : pmali_plat->scale_info.maxclk; |
| decided_fs_idx = currentStep; |
| |
| ld_up = pmali_plat->dvfs_table[currentStep].upthreshold; |
| ld_down = pmali_plat->dvfs_table[currentStep].downthreshold; |
| |
| scalingdbg(2, "utilization=%d, ld_up=%d\n ", utilization_pp, ld_up); |
| if (utilization_pp >= ld_up) { /* go up */ |
| |
| scalingdbg(2, "currentStep=%d, mali_up_limit=%d\n ", currentStep, mali_up_limit); |
| if (currentStep < mali_up_limit) { |
| change_mode = 1; |
| if ((currentStep < pmali_plat->def_clock) && (utilization_pp > pmali_plat->bst_gpu)) |
| decided_fs_idx = pmali_plat->def_clock; |
| else |
| decided_fs_idx++; |
| } |
| if ((utilization_pp >= ld_up) && |
| (num_cores_enabled < pmali_plat->scale_info.maxpp)) { |
| if ((num_cores_enabled < pmali_plat->sc_mpp) && (utilization_pp >= pmali_plat->bst_pp)) { |
| *pp_change_flag = 1; |
| change_mode = 1; |
| } else if (change_mode == 0) { |
| *pp_change_flag = 2; |
| change_mode = 1; |
| } |
| } |
| #if LOG_MALI_SCALING |
| scalingdbg(2, "[nexting..] [LD:%d]-> FS[CRNT:%d LMT:%d NEXT:%d] PP[NUM:%d LMT:%d MD:%d][F:%d]\n", |
| utilization_pp, currentStep, mali_up_limit, decided_fs_idx, |
| num_cores_enabled, pmali_plat->scale_info.maxpp, *pp_change_flag, change_mode); |
| #endif |
| } else if (utilization_pp <= ld_down) { /* go down */ |
| if (mali_stay_count > 0) { |
| *next_fs_idx = decided_fs_idx; |
| mali_stay_count--; |
| return; |
| } |
| |
| if (num_cores_enabled > pmali_plat->sc_mpp) { |
| change_mode = 1; |
| if (utilization_pp <= ld_down) { |
| ld_left = utilization_pp * num_cores_enabled; |
| ld_right = (pmali_plat->dvfs_table[currentStep].upthreshold) * |
| (num_cores_enabled - 1); |
| if (ld_left < ld_right) { |
| change_mode = 2; |
| } |
| } |
| } else if (currentStep > pmali_plat->scale_info.minclk) { |
| change_mode = 1; |
| } else if (num_cores_enabled > 1) { /* decrease PPS */ |
| if (utilization_pp <= ld_down) { |
| ld_left = utilization_pp * num_cores_enabled; |
| ld_right = (pmali_plat->dvfs_table[currentStep].upthreshold) * |
| (num_cores_enabled - 1); |
| scalingdbg(2, "ld_left=%d, ld_right=%d\n", ld_left, ld_right); |
| if (ld_left < ld_right) { |
| change_mode = 2; |
| } |
| } |
| } |
| |
| if (change_mode == 1) { |
| decided_fs_idx--; |
| } else if (change_mode == 2) { /* decrease PPS */ |
| *pp_change_flag = -1; |
| } |
| } else { |
| mali_stay_count = pmali_plat->dvfs_table[currentStep].keep_count; |
| scalingdbg(1, "reset to %d, decided_fs_idx=%d, mali_stay_count=%d\n", |
| currentStep, decided_fs_idx, mali_stay_count); |
| } |
| |
| if (decided_fs_idx < 0 ) { |
| printk("gpu debug, next index below 0\n"); |
| decided_fs_idx = 0; |
| } |
| if (decided_fs_idx > pmali_plat->scale_info.maxclk) { |
| decided_fs_idx = pmali_plat->scale_info.maxclk; |
| printk("gpu debug, next index above max-1, set to %d\n", decided_fs_idx); |
| } |
| |
| if (change_mode) |
| mali_stay_count = pmali_plat->dvfs_table[decided_fs_idx].keep_count; |
| |
| *next_fs_idx = decided_fs_idx; |
| scalingdbg(1, "mali_stay_count=%d\n", mali_stay_count); |
| } |
| #endif |
| |
| void mali_pp_fs_scaling_update(int utilization_pp) |
| { |
| #ifndef CONFIG_MALI_DVFS |
| int ret = 0; |
| int pp_change_flag = 0; |
| u32 next_idx = 0; |
| |
| #if LOG_MALI_SCALING |
| u32 last_pp = num_cores_enabled; |
| #endif |
| mali_decide_next_status(utilization_pp, &next_idx, &pp_change_flag); |
| |
| if (pp_change_flag == 1) |
| ret = enable_pp_cores(pmali_plat->sc_mpp); |
| else if (pp_change_flag == 2) |
| ret = enable_one_core(); |
| else if (pp_change_flag == -1) { |
| ret = disable_one_core(); |
| } |
| |
| #if LOG_MALI_SCALING |
| if (pp_change_flag || (next_idx != currentStep)) |
| trace_utilization(utilization_pp, currentStep, next_idx, last_pp, num_cores_enabled); |
| #endif |
| |
| if (next_idx != currentStep) { |
| ret = 1; |
| currentStep = next_idx; |
| } |
| |
| if (ret == 1) |
| schedule_work(&wq_work); |
| #ifdef CONFIG_MALI400_PROFILING |
| else |
| _mali_osk_profiling_add_event(MALI_PROFILING_EVENT_TYPE_SINGLE | |
| MALI_PROFILING_EVENT_CHANNEL_GPU | |
| MALI_PROFILING_EVENT_REASON_SINGLE_GPU_FREQ_VOLT_CHANGE, |
| get_current_frequency(), |
| 0, 0, 0, 0); |
| #endif |
| #endif |
| } |
| |
| u32 get_mali_schel_mode(void) |
| { |
| return scaling_mode; |
| } |
| |
| void set_mali_schel_mode(u32 mode) |
| { |
| #ifndef CONFIG_MALI_DVFS |
| if (mode >= MALI_SCALING_MODE_MAX) |
| return; |
| scaling_mode = mode; |
| |
| //disable thermal in turbo mode |
| if (scaling_mode == MALI_TURBO_MODE) { |
| pmali_plat->limit_on = 0; |
| } else { |
| pmali_plat->limit_on = 1; |
| } |
| /* set default performance range. */ |
| pmali_plat->scale_info.minclk = pmali_plat->cfg_min_clock; |
| pmali_plat->scale_info.maxclk = pmali_plat->cfg_clock; |
| pmali_plat->scale_info.minpp = pmali_plat->cfg_min_pp; |
| pmali_plat->scale_info.maxpp = pmali_plat->cfg_pp; |
| |
| /* set current status and tune max freq */ |
| if (scaling_mode == MALI_PP_FS_SCALING) { |
| pmali_plat->scale_info.maxclk = pmali_plat->cfg_clock; |
| enable_pp_cores(pmali_plat->sc_mpp); |
| } else if (scaling_mode == MALI_SCALING_DISABLE) { |
| pmali_plat->scale_info.maxclk = pmali_plat->cfg_clock; |
| enable_max_num_cores(); |
| } else if (scaling_mode == MALI_TURBO_MODE) { |
| pmali_plat->scale_info.maxclk = pmali_plat->turbo_clock; |
| enable_max_num_cores(); |
| } |
| currentStep = pmali_plat->scale_info.maxclk; |
| schedule_work(&wq_work); |
| #endif |
| } |
| |
| u32 get_current_frequency(void) |
| { |
| return get_mali_freq(currentStep); |
| } |
| |
| void mali_gpu_utilization_callback(int utilization_pp) |
| { |
| #ifndef CONFIG_MALI_DVFS |
| if (mali_pm_statue) |
| return; |
| |
| switch (scaling_mode) { |
| case MALI_PP_FS_SCALING: |
| mali_pp_fs_scaling_update(utilization_pp); |
| break; |
| case MALI_PP_SCALING: |
| mali_pp_scaling_update(utilization_pp); |
| break; |
| default: |
| break; |
| } |
| #endif |
| } |
| static u32 clk_cntl_save = 0; |
| void mali_dev_freeze(void) |
| { |
| clk_cntl_save = mplt_read(HHI_MALI_CLK_CNTL); |
| } |
| |
| void mali_dev_restore(void) |
| { |
| u32 reg = 0; |
| if (!pmali_plat || !pmali_plat->pdev) { |
| printk("error: init clock failed, pmali_plat=%p, pmali_plat->pdev=%p\n", |
| pmali_plat, pmali_plat == NULL ? NULL: pmali_plat->pdev); |
| return ; |
| } |
| |
| reg = pmali_plat->clk_cntl_reg; |
| |
| mplt_write(reg, clk_cntl_save); |
| mali_clock_init_clk_tree(pmali_plat->pdev); |
| } |