kernel-brax3-ubuntu-touch/drivers/gpu/mediatek/gpufreq/v2/gpuppm.c
erascape f319b992b1 kernel-5.15: Initial import brax3 UT kernel
* halium configs enabled

Signed-off-by: erascape <erascape@proton.me>
2025-09-23 15:17:10 +00:00

605 lines
17 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2021 MediaTek Inc.
*/
/**
* @file mtk_gpuppm.c
* @brief Performance power management for GPU-DVFS
*/
/**
* ===============================================
* Include
* ===============================================
*/
#include <linux/mutex.h>
#include <gpufreq_v2.h>
#include <gpuppm.h>
#include <gpufreq_common.h>
#include <gpufreq_history_common.h>
/**
* ===============================================
* Local Function Declaration
* ===============================================
*/
static void __gpuppm_sort_limit(void);
static int __gpuppm_limit_effective(enum gpufreq_target target);
static int __gpuppm_convert_limit_to_idx(enum gpufreq_target target, enum gpuppm_limiter limiter,
int ceiling_info, int floor_info, int *ceiling_idx, int *floor_idx);
static void __gpuppm_update_gpuppm_info(void);
/**
* ===============================================
* Local Variable Definition
* ===============================================
*/
static DEFINE_MUTEX(gpuppm_lock);
static struct gpuppm_status g_ppm;
static unsigned int g_gpueb_support;
static unsigned int g_stress_test;
static struct gpufreq_shared_status *g_shared_status;
static struct gpuppm_limit_info g_limit_table[] = {
LIMITOP(LIMIT_SEGMENT, "SEGMENT", GPUPPM_PRIO_9,
GPUPPM_DEFAULT_IDX, LIMIT_ENABLE,
GPUPPM_DEFAULT_IDX, LIMIT_ENABLE),
LIMITOP(LIMIT_DEBUG, "DEBUG", GPUPPM_PRIO_8,
GPUPPM_DEFAULT_IDX, LIMIT_ENABLE,
GPUPPM_DEFAULT_IDX, LIMIT_ENABLE),
LIMITOP(LIMIT_GPM3, "GPM_3.0", GPUPPM_PRIO_7,
GPUPPM_DEFAULT_IDX, LIMIT_ENABLE,
GPUPPM_DEFAULT_IDX, LIMIT_ENABLE),
LIMITOP(LIMIT_TEMPER_COMP, "TEMPER_COMP", GPUPPM_PRIO_7,
GPUPPM_DEFAULT_IDX, LIMIT_ENABLE,
GPUPPM_DEFAULT_IDX, LIMIT_ENABLE),
LIMITOP(LIMIT_PEAK_POWER_AP, "PEAK_POWER_AP", GPUPPM_PRIO_7,
GPUPPM_DEFAULT_IDX, LIMIT_ENABLE,
GPUPPM_DEFAULT_IDX, LIMIT_ENABLE),
LIMITOP(LIMIT_PEAK_POWER_EB, "PEAK_POWER_EB", GPUPPM_PRIO_7,
GPUPPM_DEFAULT_IDX, LIMIT_ENABLE,
GPUPPM_DEFAULT_IDX, LIMIT_ENABLE),
LIMITOP(LIMIT_THERMAL_AP, "THERMAL_AP", GPUPPM_PRIO_6,
GPUPPM_DEFAULT_IDX, LIMIT_ENABLE,
GPUPPM_DEFAULT_IDX, LIMIT_ENABLE),
LIMITOP(LIMIT_THERMAL_EB, "THERMAL_EB", GPUPPM_PRIO_6,
GPUPPM_DEFAULT_IDX, LIMIT_ENABLE,
GPUPPM_DEFAULT_IDX, LIMIT_ENABLE),
LIMITOP(LIMIT_SRAMRC, "SRAMRC", GPUPPM_PRIO_5,
GPUPPM_DEFAULT_IDX, LIMIT_ENABLE,
GPUPPM_DEFAULT_IDX, LIMIT_ENABLE),
LIMITOP(LIMIT_BATT_OC, "BATT_OC", GPUPPM_PRIO_4,
GPUPPM_DEFAULT_IDX, LIMIT_ENABLE,
GPUPPM_DEFAULT_IDX, LIMIT_ENABLE),
LIMITOP(LIMIT_BATT_PERCENT, "BATT_PERCENT", GPUPPM_PRIO_4,
GPUPPM_DEFAULT_IDX, LIMIT_ENABLE,
GPUPPM_DEFAULT_IDX, LIMIT_ENABLE),
LIMITOP(LIMIT_LOW_BATT, "LOW_BATT", GPUPPM_PRIO_4,
GPUPPM_DEFAULT_IDX, LIMIT_ENABLE,
GPUPPM_DEFAULT_IDX, LIMIT_ENABLE),
LIMITOP(LIMIT_PBM, "PBM", GPUPPM_PRIO_4,
GPUPPM_DEFAULT_IDX, LIMIT_ENABLE,
GPUPPM_DEFAULT_IDX, LIMIT_ENABLE),
LIMITOP(LIMIT_APIBOOST, "APIBOOST", GPUPPM_PRIO_3,
GPUPPM_DEFAULT_IDX, LIMIT_ENABLE,
GPUPPM_DEFAULT_IDX, LIMIT_ENABLE),
LIMITOP(LIMIT_FPSGO, "FPSGO", GPUPPM_PRIO_2,
GPUPPM_DEFAULT_IDX, LIMIT_ENABLE,
GPUPPM_DEFAULT_IDX, LIMIT_ENABLE),
};
static struct gpuppm_platform_fp platform_ap_fp = {
.set_shared_status = gpuppm_set_shared_status,
.limited_commit = gpuppm_limited_commit,
.set_limit = gpuppm_set_limit,
.switch_limit = gpuppm_switch_limit,
.get_ceiling = gpuppm_get_ceiling,
.get_floor = gpuppm_get_floor,
.get_c_limiter = gpuppm_get_c_limiter,
.get_f_limiter = gpuppm_get_f_limiter,
};
static struct gpuppm_platform_fp platform_eb_fp = {};
/**
* ===============================================
* Function Definition
* ===============================================
*/
static void __gpuppm_sort_limit(void)
{
struct gpuppm_limit_info *limit_table = NULL;
int cur_ceiling = 0, cur_floor = 0;
int max_oppidx = 0, min_oppidx = 0;
unsigned int cur_c_limiter = LIMIT_NUM;
unsigned int cur_f_limiter = LIMIT_NUM;
unsigned int cur_c_priority = GPUPPM_PRIO_NONE;
unsigned int cur_f_priority = GPUPPM_PRIO_NONE;
int i = 0;
limit_table = g_limit_table;
cur_ceiling = -1;
cur_floor = g_ppm.opp_num;
max_oppidx = 0;
min_oppidx = g_ppm.opp_num - 1;
/* sort ceiling among valid limiters except SEGMENT */
for (i = 1; i < LIMIT_NUM; i++) {
/* skip default value and check enable */
if (limit_table[i].ceiling != GPUPPM_DEFAULT_IDX &&
limit_table[i].ceiling != max_oppidx &&
limit_table[i].c_enable == LIMIT_ENABLE) {
/* use the largest ceiling with its limiter */
if (limit_table[i].ceiling > cur_ceiling) {
cur_ceiling = limit_table[i].ceiling;
cur_c_limiter = limit_table[i].limiter;
}
/* use the largest priority to cover all valid limiters */
if (limit_table[i].priority > cur_c_priority)
cur_c_priority = limit_table[i].priority;
}
}
/* sort floor among valid limiters except SEGMENT */
for (i = 1; i < LIMIT_NUM; i++) {
/* skip default value and check enable */
if (limit_table[i].floor != GPUPPM_DEFAULT_IDX &&
limit_table[i].floor != min_oppidx &&
limit_table[i].f_enable == LIMIT_ENABLE) {
/* use the smallest floor with its limiter */
if (limit_table[i].floor < cur_floor) {
cur_floor = limit_table[i].floor;
cur_f_limiter = limit_table[i].limiter;
}
/* use the largest priority to cover all valid limiters */
if (limit_table[i].priority > cur_f_priority)
cur_f_priority = limit_table[i].priority;
}
}
/* if no valid limitation, use SEGMENT */
if (cur_ceiling == -1) {
cur_ceiling = limit_table[LIMIT_SEGMENT].ceiling;
cur_c_limiter = limit_table[LIMIT_SEGMENT].limiter;
cur_c_priority = limit_table[LIMIT_SEGMENT].priority;
}
if (cur_floor == g_ppm.opp_num) {
cur_floor = limit_table[LIMIT_SEGMENT].floor;
cur_f_limiter = limit_table[LIMIT_SEGMENT].limiter;
cur_f_priority = limit_table[LIMIT_SEGMENT].priority;
}
/* if limit interval with intersection */
if (cur_ceiling > cur_floor) {
GPUFREQ_LOGD("exist intersection in limited interval: [%d, %d]",
cur_ceiling, cur_floor);
/*
* floor_priority > ceiling_priority: align floor
* floor_priority < ceiling_priority: align ceiling
* floor_priority = ceiling_priority: align ceiling
*/
if (cur_f_priority > cur_c_priority)
cur_ceiling = cur_floor;
else
cur_floor = cur_ceiling;
}
/* only update if both limiter of ceiling/floor are found */
if (cur_c_limiter != LIMIT_NUM && cur_f_limiter != LIMIT_NUM) {
GPUFREQ_LOGD("[ceiling] index: %d, limiter: %s, priority: %d",
cur_ceiling, limit_table[cur_c_limiter].name, cur_c_priority);
GPUFREQ_LOGD("[floor] index: %d, limiter: %s, priority: %d",
cur_floor, limit_table[cur_f_limiter].name, cur_f_priority);
g_ppm.ceiling = cur_ceiling;
g_ppm.c_limiter = cur_c_limiter;
g_ppm.c_priority = cur_c_priority;
g_ppm.floor = cur_floor;
g_ppm.f_limiter = cur_f_limiter;
g_ppm.f_priority = cur_f_priority;
}
}
static int __gpuppm_limit_effective(enum gpufreq_target target)
{
int cur_oppidx = 0, cur_ceiling = 0, cur_floor = 0;
int ret = GPUFREQ_SUCCESS;
cur_ceiling = g_ppm.ceiling;
cur_floor = g_ppm.floor;
if (target == TARGET_STACK) {
cur_oppidx = __gpufreq_get_cur_idx_stack();
if (cur_oppidx < cur_ceiling)
ret = __gpufreq_generic_commit_stack(cur_ceiling, DVFS_FREE);
else if (cur_oppidx > cur_floor)
ret = __gpufreq_generic_commit_stack(cur_floor, DVFS_FREE);
} else {
cur_oppidx = __gpufreq_get_cur_idx_gpu();
if (cur_oppidx < cur_ceiling)
ret = __gpufreq_generic_commit_gpu(cur_ceiling, DVFS_FREE);
else if (cur_oppidx > cur_floor)
ret = __gpufreq_generic_commit_gpu(cur_floor, DVFS_FREE);
}
return ret;
}
static int __gpuppm_convert_limit_to_idx(enum gpufreq_target target, enum gpuppm_limiter limiter,
int ceiling_info, int floor_info, int *ceiling_idx, int *floor_idx)
{
int ret = GPUFREQ_SUCCESS;
switch (limiter) {
case LIMIT_SEGMENT:
/* limit info: OPP index */
*ceiling_idx = ceiling_info;
*floor_idx = floor_info;
break;
case LIMIT_DEBUG:
/* limit info: OPP index */
*ceiling_idx = ceiling_info;
*floor_idx = floor_info;
break;
case LIMIT_GPM3:
/* limit info: OPP index */
*ceiling_idx = ceiling_info;
*floor_idx = GPUPPM_KEEP_IDX;
break;
case LIMIT_TEMPER_COMP:
/* limit info: OPP index */
*ceiling_idx = GPUPPM_KEEP_IDX;
*floor_idx = floor_info;
break;
case LIMIT_PEAK_POWER_AP:
/* limit info: freq */
if (ceiling_info > 0) {
if (target == TARGET_STACK)
*ceiling_idx = __gpufreq_get_idx_by_fstack(
(unsigned int)ceiling_info);
else
*ceiling_idx = __gpufreq_get_idx_by_fgpu(
(unsigned int)ceiling_info);
} else
*ceiling_idx = GPUPPM_RESET_IDX;
*floor_idx = GPUPPM_KEEP_IDX;
break;
case LIMIT_PEAK_POWER_EB:
/* limit info: freq */
if (ceiling_info > 0) {
if (target == TARGET_STACK)
*ceiling_idx = __gpufreq_get_idx_by_fstack(
(unsigned int)ceiling_info);
else
*ceiling_idx = __gpufreq_get_idx_by_fgpu(
(unsigned int)ceiling_info);
} else
*ceiling_idx = GPUPPM_RESET_IDX;
*floor_idx = GPUPPM_KEEP_IDX;
break;
case LIMIT_THERMAL_AP:
/* limit info: freq */
if (ceiling_info > 0) {
if (target == TARGET_STACK)
*ceiling_idx = __gpufreq_get_idx_by_fstack(
(unsigned int)ceiling_info);
else
*ceiling_idx = __gpufreq_get_idx_by_fgpu(
(unsigned int)ceiling_info);
} else
*ceiling_idx = GPUPPM_RESET_IDX;
*floor_idx = GPUPPM_KEEP_IDX;
break;
case LIMIT_THERMAL_EB:
/* limit info: OPP index */
*ceiling_idx = ceiling_info;
*floor_idx = GPUPPM_KEEP_IDX;
break;
case LIMIT_SRAMRC:
/* limit info: volt */
*ceiling_idx = GPUPPM_KEEP_IDX;
if (floor_info > 0) {
if (target == TARGET_STACK)
*floor_idx = __gpufreq_get_idx_by_vstack(
(unsigned int)floor_info);
else
*floor_idx = __gpufreq_get_idx_by_vgpu(
(unsigned int)floor_info);
} else
*floor_idx = GPUPPM_RESET_IDX;
break;
case LIMIT_BATT_OC:
/* limit info: batt_oc_level */
*ceiling_idx = __gpufreq_get_batt_oc_idx(ceiling_info);
*floor_idx = GPUPPM_KEEP_IDX;
break;
case LIMIT_BATT_PERCENT:
/* limit info: batt_percent_level */
*ceiling_idx = __gpufreq_get_batt_percent_idx(ceiling_info);
*floor_idx = GPUPPM_KEEP_IDX;
break;
case LIMIT_LOW_BATT:
/* limit info: low_batt_level */
*ceiling_idx = __gpufreq_get_low_batt_idx(ceiling_info);
*floor_idx = GPUPPM_KEEP_IDX;
break;
case LIMIT_PBM:
/* limit info: power */
if (ceiling_info > 0) {
if (target == TARGET_STACK)
*ceiling_idx = __gpufreq_get_idx_by_pstack(
(unsigned int)ceiling_info);
else
*ceiling_idx = __gpufreq_get_idx_by_pgpu(
(unsigned int)ceiling_info);
} else
*ceiling_idx = GPUPPM_RESET_IDX;
*floor_idx = GPUPPM_KEEP_IDX;
break;
case LIMIT_APIBOOST:
/* limit info: OPP index */
*ceiling_idx = ceiling_info;
*floor_idx = floor_info;
break;
case LIMIT_FPSGO:
/* limit info: OPP index */
*ceiling_idx = ceiling_info;
*floor_idx = floor_info;
break;
default:
GPUFREQ_LOGE("invalid limiter: %d (EINVAL)", limiter);
ret = GPUFREQ_EINVAL;
break;
}
GPUFREQ_LOGD("[%s limiter: %d] ceiling_info: %d (idx: %d), floor_info: %d, (idx: %d)",
(target == TARGET_STACK) ? "STACK" : "GPU",
limiter, ceiling_info, *ceiling_idx, floor_info, *floor_idx);
return ret;
}
static void __gpuppm_update_gpuppm_info(void)
{
unsigned int copy_size = 0;
if (!g_shared_status)
return;
/* update current status to shared memory */
g_shared_status->cur_ceiling = g_ppm.ceiling;
g_shared_status->cur_floor = g_ppm.floor;
g_shared_status->cur_c_limiter = g_ppm.c_limiter;
g_shared_status->cur_f_limiter = g_ppm.f_limiter;
g_shared_status->cur_c_priority = g_ppm.c_priority;
g_shared_status->cur_f_priority = g_ppm.f_priority;
copy_size = sizeof(struct gpuppm_limit_info) * LIMIT_NUM;
memcpy(g_shared_status->limit_table, g_limit_table, copy_size);
}
void gpuppm_set_stress_test(unsigned int val)
{
g_stress_test = val;
/* update current status to shared memory */
if (g_shared_status)
g_shared_status->stress_test = g_stress_test;
}
int gpuppm_get_ceiling(void)
{
return g_ppm.ceiling;
}
int gpuppm_get_floor(void)
{
return g_ppm.floor;
}
unsigned int gpuppm_get_c_limiter(void)
{
return g_ppm.c_limiter;
}
unsigned int gpuppm_get_f_limiter(void)
{
return g_ppm.f_limiter;
}
int gpuppm_set_limit(enum gpufreq_target target, enum gpuppm_limiter limiter,
int ceiling_info, int floor_info)
{
struct gpuppm_limit_info *limit_table = NULL;
int opp_num = 0, ceiling_idx = 0, floor_idx = 0;
int ret = GPUFREQ_SUCCESS;
GPUFREQ_TRACE_START("target=%d, limiter=%d, ceiling_info=%d, floor_info=%d",
target, limiter, ceiling_info, floor_info);
if (limiter < 0 || limiter >= LIMIT_NUM) {
GPUFREQ_LOGE("invalid limiter: %d (EINVAL)", limiter);
ret = GPUFREQ_EINVAL;
goto done;
}
limit_table = g_limit_table;
opp_num = g_ppm.opp_num;
mutex_lock(&gpuppm_lock);
/* convert input limit info to OPP index */
ret = __gpuppm_convert_limit_to_idx(target, limiter,
ceiling_info, floor_info, &ceiling_idx, &floor_idx);
if (unlikely(ret)) {
GPUFREQ_LOGE("fail to convert limit info to OPP index (%d)", ret);
goto done;
}
if (ceiling_idx == GPUPPM_RESET_IDX || ceiling_idx == GPUPPM_DEFAULT_IDX)
limit_table[limiter].ceiling = GPUPPM_DEFAULT_IDX;
else if (ceiling_idx >= 0 && ceiling_idx < opp_num)
limit_table[limiter].ceiling = ceiling_idx;
if (floor_idx == GPUPPM_RESET_IDX || floor_idx == GPUPPM_DEFAULT_IDX)
limit_table[limiter].floor = GPUPPM_DEFAULT_IDX;
else if (floor_idx >= 0 && floor_idx < opp_num)
limit_table[limiter].floor = floor_idx;
/* update current limit status */
__gpuppm_sort_limit();
/* update current status to shared memory */
if (g_shared_status)
__gpuppm_update_gpuppm_info();
/* update current OPP if necessary */
ret = __gpuppm_limit_effective(target);
mutex_unlock(&gpuppm_lock);
done:
GPUFREQ_TRACE_END();
return ret;
}
int gpuppm_switch_limit(enum gpufreq_target target, enum gpuppm_limiter limiter,
int c_enable, int f_enable)
{
struct gpuppm_limit_info *limit_table = NULL;
int ret = GPUFREQ_SUCCESS;
if (limiter < 0 || limiter >= LIMIT_NUM) {
GPUFREQ_LOGE("invalid limiter: %d (EINVAL)", limiter);
ret = GPUFREQ_EINVAL;
goto done;
}
limit_table = g_limit_table;
mutex_lock(&gpuppm_lock);
if (c_enable == LIMIT_ENABLE)
limit_table[limiter].c_enable = LIMIT_ENABLE;
else if (c_enable == LIMIT_DISABLE)
limit_table[limiter].c_enable = LIMIT_DISABLE;
if (f_enable == LIMIT_ENABLE)
limit_table[limiter].f_enable = LIMIT_ENABLE;
else if (f_enable == LIMIT_DISABLE)
limit_table[limiter].f_enable = LIMIT_DISABLE;
/* update current limit status */
__gpuppm_sort_limit();
/* update current status to shared memory */
if (g_shared_status)
__gpuppm_update_gpuppm_info();
/* update current OPP if necessary */
ret = __gpuppm_limit_effective(target);
mutex_unlock(&gpuppm_lock);
done:
return ret;
}
int gpuppm_limited_commit(enum gpufreq_target target, int oppidx)
{
int limited_idx = 0;
int cur_ceiling = 0, cur_floor = 0;
int ret = GPUFREQ_SUCCESS;
GPUFREQ_TRACE_START("target=%d, oppidx=%d", target, oppidx);
mutex_lock(&gpuppm_lock);
/* fit to limited interval */
cur_ceiling = g_ppm.ceiling;
cur_floor = g_ppm.floor;
/* randomly replace target OPP index */
if (g_stress_test) {
get_random_bytes(&oppidx, sizeof(oppidx));
oppidx = oppidx < 0 ? (oppidx * -1) : oppidx;
oppidx = (oppidx % (cur_floor - cur_ceiling + 1)) + cur_ceiling;
}
if (oppidx < cur_ceiling)
limited_idx = cur_ceiling;
else if (oppidx > cur_floor)
limited_idx = cur_floor;
else
limited_idx = oppidx;
GPUFREQ_LOGD("[%s] restrict OPP index: (%d->%d), limited interval: [%d, %d]",
(target == TARGET_STACK) ? "STACK" : "GPU",
oppidx, limited_idx, cur_ceiling, cur_floor);
#if GPUFREQ_HISTORY_ENABLE
gpufreq_set_history_target_opp(target, oppidx);
#endif /* GPUFREQ_HISTORY_ENABLE */
if (target == TARGET_STACK)
ret = __gpufreq_generic_commit_stack(limited_idx, DVFS_FREE);
else
ret = __gpufreq_generic_commit_gpu(limited_idx, DVFS_FREE);
mutex_unlock(&gpuppm_lock);
GPUFREQ_TRACE_END();
return ret;
}
void gpuppm_set_shared_status(struct gpufreq_shared_status *shared_status)
{
mutex_lock(&gpuppm_lock);
if (shared_status)
g_shared_status = shared_status;
else
__gpufreq_abort("null gpufreq shared status: 0x%llx", shared_status);
/* update current status to shared memory */
if (g_shared_status) {
g_shared_status->stress_test = g_stress_test;
__gpuppm_update_gpuppm_info();
}
mutex_unlock(&gpuppm_lock);
}
int gpuppm_init(enum gpufreq_target target,
unsigned int gpueb_support, unsigned int sramrc_vsafe)
{
int max_oppidx = 0, min_oppidx = 0, opp_num = 0;
int ret = GPUFREQ_SUCCESS;
g_gpueb_support = gpueb_support;
if (g_gpueb_support)
gpufreq_register_gpuppm_fp(&platform_eb_fp);
/* init only in AP mode */
else {
if (target == TARGET_STACK)
opp_num = __gpufreq_get_opp_num_stack();
else
opp_num = __gpufreq_get_opp_num_gpu();
max_oppidx = 0;
min_oppidx = opp_num - 1;
g_ppm.opp_num = opp_num;
/* set basic limit at boot time */
gpuppm_set_limit(target, LIMIT_SEGMENT, max_oppidx, min_oppidx);
gpuppm_set_limit(target, LIMIT_SRAMRC, GPUPPM_KEEP_IDX, sramrc_vsafe);
gpufreq_register_gpuppm_fp(&platform_ap_fp);
}
return ret;
}