kernel_samsung_a53x/drivers/gpu/arm/exynos/backend/gpexbe_devicetree.c
2024-11-17 20:56:28 +01:00

325 lines
10 KiB
C
Executable file

/* 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 <linux/device.h>
#include <linux/of.h>
#include <linux/sysfs.h>
#include <linux/slab.h>
#include <gpex_utils.h>
#include <gpexbe_devicetree.h>
#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[] = {2093000, 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;
}