/* SPDX-License-Identifier: GPL-2.0 */ /* * (C) COPYRIGHT 2021 Samsung Electronics Inc. All rights reserved. * * This program is free software and is provided to you under the terms of the * GNU General Public License version 2 as published by the Free Software * Foundation, and any use by you of this program is subject to the terms * of such GNU licence. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, you can access it online at * http://www.gnu.org/licenses/gpl-2.0.html. */ #include #include #include #include #include #include #define CPU_MAX INT_MAX static gpu_dt dt_info; static struct _dt_clock_item *clock_table; static struct _dt_clqos_item *clqos_table; static int gpexbe_devicetree_read_u32(const char *of_string, u32 *of_data) { int ret = 0; if (!of_string || !of_data) { GPU_LOG(MALI_EXYNOS_ERROR, "NULL: failed to get item from dt\n"); return -EINVAL; } ret = of_property_read_u32(dt_info.dev->of_node, of_string, of_data); if (ret) { GPU_LOG(MALI_EXYNOS_ERROR, "%s: failed to get item from dt. Data will be set to 0.\n", of_string); *of_data = 0; } return ret; } /* TODO: document in the interface header. Return value same as of_property_read_string */ static int gpexbe_devicetree_read_string(const char *of_string, const char **of_data) { int ret = 0; if (!of_string || !of_data) { GPU_LOG(MALI_EXYNOS_ERROR, "NULL: failed to get item from dt\n"); return -EINVAL; } ret = of_property_read_string(dt_info.dev->of_node, of_string, of_data); if (ret) { GPU_LOG(MALI_EXYNOS_ERROR, "%s: failed to get item from dt. Data will be set to NULL.\n", of_string); *of_data = NULL; } return ret; } static int gpexbe_devicetree_read_u32_array(const char *of_string, int *of_data, int sz) { int ret = 0; if (!of_string || !of_data || sz < 0) { GPU_LOG(MALI_EXYNOS_ERROR, "NULL: failed to get item from dt. Invalid params\n"); return -EINVAL; } ret = of_property_read_u32_array(dt_info.dev->of_node, of_string, of_data, sz); if (ret) GPU_LOG(MALI_EXYNOS_ERROR, "%s: failed to get item from dt (u32 array)\n", of_string); return ret; } static int read_interactive_info_array(void) { int interactive_info[3] = { 0, 0, 0 }; gpexbe_devicetree_read_u32_array("interactive_info", interactive_info, 3); /* TODO: change default value to something more sensible */ dt_info.interactive_info.highspeed_clock = interactive_info[0] == 0 ? 500 : (u32)interactive_info[0]; dt_info.interactive_info.highspeed_load = interactive_info[1] == 0 ? 100 : (u32)interactive_info[1]; dt_info.interactive_info.highspeed_delay = interactive_info[2] == 0 ? 0 : (u32)interactive_info[2]; return 0; } unsigned int custom_clock[] = {1209000, 1105000, 1001000, 897000, 806000, 702000, 611000, 507000, 403000, 312000, 208000, 104000}; unsigned int custom_min_threshold[] = {90, 87, 85, 82, 80, 79, 78, 70, 60, 50, 30, 0}; unsigned int custom_max_threshold[] = {100, 96 ,95, 95, 95, 95, 95, 90, 80, 70, 60, 40}; unsigned int custom_staycount[] = {5, 5, 5, 5, 5, 5, 5, 3, 3, 2, 2, 1}; unsigned int custom_mem_freq[] = {2000000, 1794000, 1794000, 1794000, 1539000, 1352000, 1352000, 1014000, 1014000, 845000, 676000, 676000}; unsigned int custom_lit[] = {1536000, 1440000, 1248000, 1056000, 1056000, 1056000, 1056000, 1056000, 0, 0, 0, 0}; unsigned int custom_mid = 0; unsigned int custom_big = CPU_MAX; int custom_array_size = sizeof(custom_clock) / sizeof(custom_clock[0]); static int build_clk_table(void) { int array_size = custom_array_size; int i = 0; if (array_size <= 0) { return -EINVAL; } clock_table = kcalloc(array_size, sizeof(*clock_table), GFP_KERNEL); for (i = 0; i < array_size; i++) { clock_table[i].clock = custom_clock[i]; clock_table[i].min_threshold = custom_min_threshold[i]; clock_table[i].max_threshold = custom_max_threshold[i]; clock_table[i].down_staycount = custom_staycount[i]; clock_table[i].mem_freq = custom_mem_freq[i]; clock_table[i].cpu_little_min_freq = custom_lit[i]; if (dt_info.gpu_pmqos_cpu_cluster_num == 3) { clock_table[i].cpu_middle_min_freq = custom_mid; clock_table[i].cpu_big_max_freq = custom_big; GPU_LOG(MALI_EXYNOS_INFO, "up [%d] down [%d] staycnt [%d] mif [%d] lit [%d] mid [%d] big [%d]\n", clock_table[i].max_threshold, clock_table[i].min_threshold, clock_table[i].down_staycount, clock_table[i].mem_freq, clock_table[i].cpu_little_min_freq, clock_table[i].cpu_middle_min_freq, clock_table[i].cpu_big_max_freq); } else { // Assuming cpu cluster number is 2 clock_table[i].cpu_big_max_freq = custom_big; GPU_LOG(MALI_EXYNOS_INFO, "up [%d] down [%d] staycnt [%d] mif [%d] lit [%d] big [%d]\n", clock_table[i].max_threshold, clock_table[i].min_threshold, clock_table[i].down_staycount, clock_table[i].mem_freq, clock_table[i].cpu_little_min_freq, clock_table[i].cpu_big_max_freq); } } return 0; } static int build_cl_pmqos_table(void) { int array_size = dt_info.gpu_cl_pmqos_table_size.row * dt_info.gpu_cl_pmqos_table_size.col; u32 *raw_table; int row = 0; if (array_size <= 0) { int num_rows = dt_info.gpu_dvfs_table_size.row; clqos_table = kcalloc(num_rows, sizeof(*clqos_table), GFP_KERNEL); for (row = 0; row < num_rows; row++) clqos_table[row].clock = clock_table[row].clock; dt_info.gpu_cl_pmqos_table_size.row = num_rows; dt_info.gpu_cl_pmqos_table_size.col = 1; return 0; } raw_table = kcalloc(array_size, sizeof(*raw_table), GFP_KERNEL); if (!raw_table) return -ENOMEM; gpexbe_devicetree_read_u32_array("gpu_cl_pmqos_table", raw_table, array_size); clqos_table = kcalloc(dt_info.gpu_cl_pmqos_table_size.row, sizeof(*clqos_table), GFP_KERNEL); for (row = 0; row < dt_info.gpu_cl_pmqos_table_size.row; row++) { int table_idx = row * dt_info.gpu_cl_pmqos_table_size.col; clqos_table[row].clock = raw_table[table_idx]; clqos_table[row].mif_min = raw_table[table_idx + 1]; clqos_table[row].little_min = raw_table[table_idx + 2]; clqos_table[row].middle_min = raw_table[table_idx + 3]; clqos_table[row].big_max = raw_table[table_idx + 4]; } kfree(raw_table); return 0; } static void read_from_dt(void) { /* clock backend */ gpexbe_devicetree_read_u32("g3d_cmu_cal_id", &dt_info.g3d_cmu_cal_id); /* PM backend */ gpexbe_devicetree_read_string("g3d_genpd_name", &dt_info.g3d_genpd_name); /* CLOCK */ dt_info.gpu_max_clock = custom_clock[0]; dt_info.gpu_min_clock = custom_clock[custom_array_size - 1]; gpexbe_devicetree_read_u32("gpu_pmqos_cpu_cluster_num", &dt_info.gpu_pmqos_cpu_cluster_num); dt_info.gpu_dvfs_table_size.col = 8; // 8 values for each freq dt_info.gpu_dvfs_table_size.row = custom_array_size; gpexbe_devicetree_read_u32_array("gpu_cl_pmqos_table_size", (int *)&dt_info.gpu_cl_pmqos_table_size, 2); /* DEBUG_BACKEND */ gpexbe_devicetree_read_u32("gpu_ess_id_type", &dt_info.gpu_ess_id_type); /* LEGACY DVFS */ gpexbe_devicetree_read_string("governor", &dt_info.governor); gpexbe_devicetree_read_u32("gpu_dvfs_bl_config_clock", &dt_info.gpu_dvfs_bl_config_clock); gpexbe_devicetree_read_u32("gpu_dvfs_polling_time", &dt_info.gpu_dvfs_polling_time); /* IFPO */ gpexbe_devicetree_read_u32("gpu_inter_frame_pm", &dt_info.gpu_inter_frame_pm); /* PM BACKEND */ gpexbe_devicetree_read_u32("gpu_pmu_status_reg_offset", &dt_info.gpu_pmu_status_reg_offset); gpexbe_devicetree_read_u32("gpu_pmu_status_local_pwr_mask", &dt_info.gpu_pmu_status_local_pwr_mask); /* PM */ gpexbe_devicetree_read_u32("gpu_runtime_pm_delay_time", &dt_info.gpu_runtime_pm_delay_time); /* QOS */ gpexbe_devicetree_read_u32("gpu_bts_support", &dt_info.gpu_bts_support); gpexbe_devicetree_read_u32("gpu_mo_min_clock", &dt_info.gpu_mo_min_clock); /* UTILS */ gpexbe_devicetree_read_u32("gpu_debug_level", &dt_info.gpu_debug_level); /* HCM */ gpexbe_devicetree_read_u32("gpu_heavy_compute_cpu0_min_clock;", &dt_info.gpu_heavy_compute_cpu0_min_clock); gpexbe_devicetree_read_u32("gpu_heavy_compute_vk_cpu0_min_clock;", &dt_info.gpu_heavy_compute_vk_cpu0_min_clock); /* TSG */ gpexbe_devicetree_read_u32("gpu_weight_table_idx_0", &dt_info.gpu_weight_table_idx_0); gpexbe_devicetree_read_u32("gpu_weight_table_idx_1", &dt_info.gpu_weight_table_idx_1); } /****************** * GETTERS * ***************/ dt_clock_item *gpexbe_devicetree_get_clock_table(void) { return clock_table; } /******************************************** * Helper Functions. (Must remain non static) * ******************************************/ int gpexbe_devicetree_get_int_internal(size_t offset, const char *str) { //pr_warn("mali: %s %s %d\n", __func__, str, *(int*)((u8*)&dt_info + offset)); //GPU_LOG(MALI_EXYNOS_INFO, "%s: %s\n", of_string, *of_data); return *(int *)((u8 *)&dt_info + offset); } char *gpexbe_devicetree_get_str_internal(size_t offset, const char *str) { //pr_warn("mali: %s %s %s\n", __func__, str, *(char**)((u8*)&dt_info + offset)); //GPU_LOG(MALI_EXYNOS_INFO, "%s: %s\n", of_string, *of_data); return *(char **)((u8 *)&dt_info + offset); } gpu_dt *gpexbe_devicetree_get_gpu_dt(void) { return &dt_info; } /************************************************************************ * INIT and TERM functions ************************************************************************/ int gpexbe_devicetree_init(struct device *dev) { dt_info.dev = dev; read_from_dt(); build_clk_table(); build_cl_pmqos_table(); read_interactive_info_array(); dt_info.gpu_dvfs_table = clock_table; dt_info.gpu_cl_pmqos_table = clqos_table; dt_info.initialized = 1; return 0; } void gpexbe_devicetree_term(void) { kfree(clock_table); kfree(clqos_table); clock_table = NULL; clqos_table = NULL; memset(&dt_info, 0, sizeof(gpu_dt)); return; }