1
0
mirror of https://github.com/physwizz/a155-U-u1.git synced 2025-08-15 06:35:18 +00:00
Files
physwizz 99537be4e2 first
2024-03-11 06:53:12 +11:00

2030 lines
66 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2021 MediaTek Inc.
*/
/**
* @file mtk_gpufreq_v2.c
* @brief GPU-DVFS Driver Common Wrapper
*/
/**
* ===============================================
* Include
* ===============================================
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/export.h>
#include <linux/printk.h>
#include <linux/mutex.h>
#include <linux/types.h>
#include <linux/soc/mediatek/mtk_tinysys_ipi.h>
#include <gpufreq_v2.h>
#include <gpufreq_debug.h>
#include <gpu_misc.h>
#include <gpufreq_ipi.h>
#include <gpueb_ipi.h>
#include <gpueb_reserved_mem.h>
#include <gpueb_debug.h>
#include <mtk_gpu_utility.h>
#if IS_ENABLED(CONFIG_MTK_PBM)
#include <mtk_pbm_gpu_cb.h>
#endif
#if IS_ENABLED(CONFIG_MTK_BATTERY_OC_POWER_THROTTLING)
#include <mtk_battery_oc_throttling.h>
#endif
#if IS_ENABLED(CONFIG_MTK_BATTERY_PERCENT_THROTTLING)
#include <mtk_bp_thl.h>
#endif
#if IS_ENABLED(CONFIG_MTK_LOW_BATTERY_POWER_THROTTLING)
#include <mtk_low_battery_throttling.h>
#endif
/**
* ===============================================
* Local Function Declaration
* ===============================================
*/
static int gpufreq_wrapper_pdrv_probe(struct platform_device *pdev);
static int gpufreq_gpueb_init(void);
static void gpufreq_init_external_callback(void);
static int gpufreq_ipi_to_gpueb(struct gpufreq_ipi_data data);
static int gpufreq_validate_target(unsigned int *target);
static void gpufreq_dump_dvfs_status(void);
static void gpufreq_abort(void);
/**
* ===============================================
* Local Variable Definition
* ===============================================
*/
static const struct of_device_id g_gpufreq_wrapper_of_match[] = {
{ .compatible = "mediatek,gpufreq_wrapper" },
{ /* sentinel */ }
};
static struct platform_driver g_gpufreq_wrapper_pdrv = {
.probe = gpufreq_wrapper_pdrv_probe,
.remove = NULL,
.driver = {
.name = "gpufreq_wrapper",
.owner = THIS_MODULE,
.of_match_table = g_gpufreq_wrapper_of_match,
},
};
static int g_ipi_channel;
static unsigned int g_dual_buck;
static unsigned int g_gpueb_support;
static phys_addr_t g_status_shared_mem_va;
static phys_addr_t g_debug_shared_mem_va;
static struct gpufreq_platform_fp *gpufreq_fp;
static struct gpuppm_platform_fp *gpuppm_fp;
static struct gpufreq_ipi_data g_recv_msg;
/**
* ===============================================
* External Function Definition
* ===============================================
*/
/***********************************************************************************
* Function Name : gpufreq_bringup
* Inputs : -
* Outputs : -
* Returns : status - Current status of bring-up
* Description : Check GPUFREQ bring-up status
* If it's bring-up status, GPUFREQ is out-of-function
***********************************************************************************/
unsigned int gpufreq_bringup(void)
{
/* if bringup fp exists, it isn't bringup state */
if (gpufreq_fp && gpufreq_fp->bringup)
return false;
else
return true;
}
EXPORT_SYMBOL(gpufreq_bringup);
/***********************************************************************************
* Function Name : gpufreq_power_ctrl_enable
* Inputs : -
* Outputs : -
* Returns : status - Current status of power control
* Description : Check whether power control of GPU HW is enable
* If it's disabled, GPU HW is always power-on
***********************************************************************************/
unsigned int gpufreq_power_ctrl_enable(void)
{
struct gpufreq_shared_status *shared_status = NULL;
unsigned int power_control = false;
/* implement on EB */
if (g_gpueb_support) {
shared_status = (struct gpufreq_shared_status *)(uintptr_t)g_status_shared_mem_va;
if (shared_status)
power_control = shared_status->power_control;
/* implement on AP */
} else {
if (gpufreq_fp && gpufreq_fp->power_ctrl_enable)
power_control = gpufreq_fp->power_ctrl_enable();
else
GPUFREQ_LOGE("null gpufreq platform function pointer (ENOENT)");
}
return power_control;
}
EXPORT_SYMBOL(gpufreq_power_ctrl_enable);
/***********************************************************************************
* Function Name : gpufreq_get_power_state
* Inputs : -
* Outputs : -
* Returns : state - Current status of GPU power
* Description : Get current power state
***********************************************************************************/
unsigned int gpufreq_get_power_state(void)
{
struct gpufreq_shared_status *shared_status = NULL;
enum gpufreq_power_state power_state = POWER_OFF;
int power_count = 0;
/* implement on EB */
if (g_gpueb_support) {
shared_status = (struct gpufreq_shared_status *)(uintptr_t)g_status_shared_mem_va;
if (shared_status)
power_count = shared_status->power_count;
power_state = power_count > 0 ? POWER_ON : POWER_OFF;
/* implement on AP */
} else {
if (gpufreq_fp && gpufreq_fp->get_power_state)
power_state = gpufreq_fp->get_power_state();
else
GPUFREQ_LOGE("null gpufreq platform function pointer (ENOENT)");
}
return power_state;
}
EXPORT_SYMBOL(gpufreq_get_power_state);
/***********************************************************************************
* Function Name : gpufreq_get_dvfs_state
* Inputs : -
* Outputs : -
* Returns : state - Current status of GPU DVFS
* Description : Get current DVFS state
* If it isn't DVFS_FREE, then DVFS is fixed in some state
***********************************************************************************/
unsigned int gpufreq_get_dvfs_state(void)
{
struct gpufreq_shared_status *shared_status = NULL;
enum gpufreq_dvfs_state dvfs_state = DVFS_DISABLE;
/* implement on EB */
if (g_gpueb_support) {
shared_status = (struct gpufreq_shared_status *)(uintptr_t)g_status_shared_mem_va;
if (shared_status)
dvfs_state = shared_status->dvfs_state;
/* implement on AP */
} else {
if (gpufreq_fp && gpufreq_fp->get_dvfs_state)
dvfs_state = gpufreq_fp->get_dvfs_state();
else
GPUFREQ_LOGE("null gpufreq platform function pointer (ENOENT)");
}
return dvfs_state;
}
EXPORT_SYMBOL(gpufreq_get_dvfs_state);
/***********************************************************************************
* Function Name : gpufreq_get_shader_present
* Inputs : -
* Outputs : -
* Returns : shader_present - Current shader cores
* Description : Get GPU shader cores
* This is for Mali GPU DDK to control power domain of shader cores
***********************************************************************************/
unsigned int gpufreq_get_shader_present(void)
{
struct gpufreq_shared_status *shared_status = NULL;
unsigned int shader_present = 0;
/* implement on EB */
if (g_gpueb_support) {
shared_status = (struct gpufreq_shared_status *)(uintptr_t)g_status_shared_mem_va;
if (shared_status)
shader_present = shared_status->shader_present;
/* implement on AP */
} else {
if (gpufreq_fp && gpufreq_fp->get_shader_present)
shader_present = gpufreq_fp->get_shader_present();
else
GPUFREQ_LOGE("null gpufreq platform function pointer (ENOENT)");
}
return shader_present;
}
EXPORT_SYMBOL(gpufreq_get_shader_present);
/***********************************************************************************
* Function Name : gpufreq_set_timestamp
* Inputs : -
* Outputs : -
* Returns : -
* Description : Set timestamp for clGetEventProfilingInfo
***********************************************************************************/
void gpufreq_set_timestamp(void)
{
/* implement on AP */
if (gpufreq_fp && gpufreq_fp->set_timestamp)
gpufreq_fp->set_timestamp();
else
GPUFREQ_LOGE("null gpufreq platform function pointer (ENOENT)");
}
EXPORT_SYMBOL(gpufreq_set_timestamp);
/***********************************************************************************
* Function Name : gpufreq_check_bus_idle
* Inputs : -
* Outputs : -
* Returns : -
* Description : Check bus idle before GPU power-off
***********************************************************************************/
void gpufreq_check_bus_idle(void)
{
/* implement on AP */
if (gpufreq_fp && gpufreq_fp->check_bus_idle)
gpufreq_fp->check_bus_idle();
else
GPUFREQ_LOGE("null gpufreq platform function pointer (ENOENT)");
}
EXPORT_SYMBOL(gpufreq_check_bus_idle);
/***********************************************************************************
* Function Name : gpufreq_dump_infra_status
* Inputs : -
* Outputs : -
* Returns : -
* Description : Dump GPU related infra status
***********************************************************************************/
void gpufreq_dump_infra_status(void)
{
gpufreq_dump_dvfs_status();
/* implement on AP */
if (gpufreq_fp && gpufreq_fp->dump_infra_status)
gpufreq_fp->dump_infra_status();
else
GPUFREQ_LOGE("null gpufreq platform function pointer (ENOENT)");
}
EXPORT_SYMBOL(gpufreq_dump_infra_status);
/***********************************************************************************
* Function Name : gpufreq_get_cur_freq
* Inputs : target - Target of GPU DVFS (GPU, STACK, DEFAULT)
* Outputs : -
* Returns : freq - Current freq of given target
* Description : Query current frequency of the target
***********************************************************************************/
unsigned int gpufreq_get_cur_freq(enum gpufreq_target target)
{
struct gpufreq_shared_status *shared_status = NULL;
unsigned int freq = 0;
if (gpufreq_validate_target(&target))
goto done;
/* implement on EB */
if (g_gpueb_support) {
shared_status = (struct gpufreq_shared_status *)(uintptr_t)g_status_shared_mem_va;
if (target == TARGET_STACK && shared_status)
freq = shared_status->cur_fstack;
else if (target == TARGET_GPU && shared_status)
freq = shared_status->cur_fgpu;
goto done;
}
/* implement on AP */
if (target == TARGET_STACK && gpufreq_fp && gpufreq_fp->get_cur_fstack)
freq = gpufreq_fp->get_cur_fstack();
else if (target == TARGET_GPU && gpufreq_fp && gpufreq_fp->get_cur_fgpu)
freq = gpufreq_fp->get_cur_fgpu();
else
GPUFREQ_LOGE("null gpufreq platform function pointer (ENOENT)");
done:
GPUFREQ_LOGD("target: %s, current freq: %d",
target == TARGET_STACK ? "STACK" : "GPU",
freq);
return freq;
}
EXPORT_SYMBOL(gpufreq_get_cur_freq);
/***********************************************************************************
* Function Name : gpufreq_get_cur_volt
* Inputs : target - Target of GPU DVFS (GPU, STACK, DEFAULT)
* Outputs : -
* Returns : volt - Current volt of given target
* Description : Query current voltage of the target
***********************************************************************************/
unsigned int gpufreq_get_cur_volt(enum gpufreq_target target)
{
struct gpufreq_shared_status *shared_status = NULL;
unsigned int volt = 0;
if (gpufreq_validate_target(&target))
goto done;
/* implement on EB */
if (g_gpueb_support) {
shared_status = (struct gpufreq_shared_status *)(uintptr_t)g_status_shared_mem_va;
if (target == TARGET_STACK && shared_status)
volt = shared_status->cur_vstack;
else if (target == TARGET_GPU && shared_status)
volt = shared_status->cur_vgpu;
goto done;
}
/* implement on AP */
if (target == TARGET_STACK && gpufreq_fp && gpufreq_fp->get_cur_vstack)
volt = gpufreq_fp->get_cur_vstack();
else if (target == TARGET_GPU && gpufreq_fp && gpufreq_fp->get_cur_vgpu)
volt = gpufreq_fp->get_cur_vgpu();
else
GPUFREQ_LOGE("null gpufreq platform function pointer (ENOENT)");
done:
GPUFREQ_LOGD("target: %s, current volt: %d",
target == TARGET_STACK ? "STACK" : "GPU",
volt);
return volt;
}
EXPORT_SYMBOL(gpufreq_get_cur_volt);
/***********************************************************************************
* Function Name : gpufreq_get_cur_vsram
* Inputs : target - Target of GPU DVFS (GPU, STACK, DEFAULT)
* Outputs : -
* Returns : vsram - Current vsram volt of given target
* Description : Query current vsram voltage of the target
***********************************************************************************/
unsigned int gpufreq_get_cur_vsram(enum gpufreq_target target)
{
struct gpufreq_shared_status *shared_status = NULL;
unsigned int vsram = 0;
if (gpufreq_validate_target(&target))
goto done;
/* implement on EB */
if (g_gpueb_support) {
shared_status = (struct gpufreq_shared_status *)(uintptr_t)g_status_shared_mem_va;
if (target == TARGET_STACK && shared_status)
vsram = shared_status->cur_vsram_stack;
else if (target == TARGET_GPU && shared_status)
vsram = shared_status->cur_vsram_gpu;
goto done;
}
/* implement on AP */
if (target == TARGET_STACK && gpufreq_fp && gpufreq_fp->get_cur_vsram_stack)
vsram = gpufreq_fp->get_cur_vsram_stack();
else if (target == TARGET_GPU && gpufreq_fp && gpufreq_fp->get_cur_vsram_gpu)
vsram = gpufreq_fp->get_cur_vsram_gpu();
else
GPUFREQ_LOGE("null gpufreq platform function pointer (ENOENT)");
done:
GPUFREQ_LOGD("target: %s, current vsram: %d",
target == TARGET_STACK ? "STACK" : "GPU",
vsram);
return vsram;
}
EXPORT_SYMBOL(gpufreq_get_cur_vsram);
/***********************************************************************************
* Function Name : gpufreq_get_cur_power
* Inputs : target - Target of GPU DVFS (GPU, STACK, DEFAULT)
* Outputs : -
* Returns : power - Current power of given target
* Description : Query current power of the target
***********************************************************************************/
unsigned int gpufreq_get_cur_power(enum gpufreq_target target)
{
struct gpufreq_shared_status *shared_status = NULL;
unsigned int power = 0;
if (gpufreq_validate_target(&target))
goto done;
/* implement on EB */
if (g_gpueb_support) {
shared_status = (struct gpufreq_shared_status *)(uintptr_t)g_status_shared_mem_va;
if (target == TARGET_STACK && shared_status)
power = shared_status->cur_power_stack;
else if (target == TARGET_GPU && shared_status)
power = shared_status->cur_power_gpu;
goto done;
}
/* implement on AP */
if (target == TARGET_STACK && gpufreq_fp && gpufreq_fp->get_cur_pstack)
power = gpufreq_fp->get_cur_pstack();
else if (target == TARGET_GPU && gpufreq_fp && gpufreq_fp->get_cur_pgpu)
power = gpufreq_fp->get_cur_pgpu();
else
GPUFREQ_LOGE("null gpufreq platform function pointer (ENOENT)");
done:
GPUFREQ_LOGD("target: %s, current power: %d",
target == TARGET_STACK ? "STACK" : "GPU",
power);
return power;
}
EXPORT_SYMBOL(gpufreq_get_cur_power);
/***********************************************************************************
* Function Name : gpufreq_get_max_power
* Inputs : target - Target of GPU DVFS (GPU, STACK, DEFAULT)
* Outputs : -
* Returns : power - Max power of given target in working table
* Description : Query maximum power of the target
***********************************************************************************/
unsigned int gpufreq_get_max_power(enum gpufreq_target target)
{
struct gpufreq_shared_status *shared_status = NULL;
unsigned int power = 0;
if (gpufreq_validate_target(&target))
goto done;
/* implement on EB */
if (g_gpueb_support) {
shared_status = (struct gpufreq_shared_status *)(uintptr_t)g_status_shared_mem_va;
if (target == TARGET_STACK && shared_status)
power = shared_status->max_power_stack;
else if (target == TARGET_GPU && shared_status)
power = shared_status->max_power_gpu;
goto done;
}
/* implement on AP */
if (target == TARGET_STACK && gpufreq_fp && gpufreq_fp->get_max_pstack)
power = gpufreq_fp->get_max_pstack();
else if (target == TARGET_GPU && gpufreq_fp && gpufreq_fp->get_max_pgpu)
power = gpufreq_fp->get_max_pgpu();
else
GPUFREQ_LOGE("null gpufreq platform function pointer (ENOENT)");
done:
GPUFREQ_LOGD("target: %s, max power: %d",
target == TARGET_STACK ? "STACK" : "GPU",
power);
return power;
}
EXPORT_SYMBOL(gpufreq_get_max_power);
/***********************************************************************************
* Function Name : gpufreq_get_min_power
* Inputs : target - Target of GPU DVFS (GPU, STACK, DEFAULT)
* Outputs : -
* Returns : power - Min power of given target in working table
* Description : Query minimum power of the target
***********************************************************************************/
unsigned int gpufreq_get_min_power(enum gpufreq_target target)
{
struct gpufreq_shared_status *shared_status = NULL;
unsigned int power = 0;
if (gpufreq_validate_target(&target))
goto done;
/* implement on EB */
if (g_gpueb_support) {
shared_status = (struct gpufreq_shared_status *)(uintptr_t)g_status_shared_mem_va;
if (target == TARGET_STACK && shared_status)
power = shared_status->min_power_stack;
else if (target == TARGET_GPU && shared_status)
power = shared_status->min_power_gpu;
goto done;
}
/* implement on AP */
if (target == TARGET_STACK && gpufreq_fp && gpufreq_fp->get_min_pstack)
power = gpufreq_fp->get_min_pstack();
else if (target == TARGET_GPU && gpufreq_fp && gpufreq_fp->get_min_pgpu)
power = gpufreq_fp->get_min_pgpu();
else
GPUFREQ_LOGE("null gpufreq platform function pointer (ENOENT)");
done:
GPUFREQ_LOGD("target: %s, min power: %d",
target == TARGET_STACK ? "STACK" : "GPU",
power);
return power;
}
EXPORT_SYMBOL(gpufreq_get_min_power);
/***********************************************************************************
* Function Name : gpufreq_get_cur_oppidx
* Inputs : target - Target of GPU DVFS (GPU, STACK, DEFAULT)
* Outputs : -
* Returns : oppidx - Current working OPP index of given target
* Description : Query current working OPP index of the target
***********************************************************************************/
int gpufreq_get_cur_oppidx(enum gpufreq_target target)
{
struct gpufreq_shared_status *shared_status = NULL;
int oppidx = -1;
if (gpufreq_validate_target(&target))
goto done;
/* implement on EB */
if (g_gpueb_support) {
shared_status = (struct gpufreq_shared_status *)(uintptr_t)g_status_shared_mem_va;
if (target == TARGET_STACK && shared_status)
oppidx = shared_status->cur_oppidx_stack;
else if (target == TARGET_GPU && shared_status)
oppidx = shared_status->cur_oppidx_gpu;
goto done;
}
/* implement on AP */
if (target == TARGET_STACK && gpufreq_fp && gpufreq_fp->get_cur_idx_stack)
oppidx = gpufreq_fp->get_cur_idx_stack();
else if (target == TARGET_GPU && gpufreq_fp && gpufreq_fp->get_cur_idx_gpu)
oppidx = gpufreq_fp->get_cur_idx_gpu();
else
GPUFREQ_LOGE("null gpufreq platform function pointer (ENOENT)");
done:
GPUFREQ_LOGD("target: %s, current OPP index: %d",
target == TARGET_STACK ? "STACK" : "GPU",
oppidx);
return oppidx;
}
EXPORT_SYMBOL(gpufreq_get_cur_oppidx);
/***********************************************************************************
* Function Name : gpufreq_get_opp_num
* Inputs : target - Target of GPU DVFS (GPU, STACK, DEFAULT)
* Outputs : -
* Returns : opp_num - # of OPP index in working table of given target
* Description : Query number of OPP index in working table of the target
***********************************************************************************/
int gpufreq_get_opp_num(enum gpufreq_target target)
{
struct gpufreq_shared_status *shared_status = NULL;
int opp_num = -1;
if (gpufreq_validate_target(&target))
goto done;
/* implement on EB */
if (g_gpueb_support) {
shared_status = (struct gpufreq_shared_status *)(uintptr_t)g_status_shared_mem_va;
if (target == TARGET_STACK && shared_status)
opp_num = shared_status->opp_num_stack;
else if (target == TARGET_GPU && shared_status)
opp_num = shared_status->opp_num_gpu;
goto done;
}
/* implement on AP */
if (target == TARGET_STACK && gpufreq_fp && gpufreq_fp->get_opp_num_stack)
opp_num = gpufreq_fp->get_opp_num_stack();
else if (target == TARGET_GPU && gpufreq_fp && gpufreq_fp->get_opp_num_gpu)
opp_num = gpufreq_fp->get_opp_num_gpu();
//WA for print too much log will trigger kernel api dump
/*
else
GPUFREQ_LOGE("null gpufreq platform function pointer (ENOENT)");
*/
done:
GPUFREQ_LOGD("target: %s, # of OPP index: %d",
target == TARGET_STACK ? "STACK" : "GPU",
opp_num);
return opp_num;
}
EXPORT_SYMBOL(gpufreq_get_opp_num);
/***********************************************************************************
* Function Name : gpufreq_get_freq_by_idx
* Inputs : target - Target of GPU DVFS (GPU, STACK, DEFAULT)
* oppidx - Worknig OPP index of the target
* Outputs : -
* Returns : freq - Freq of given target at given working OPP index
* Description : Query freq of the target by working OPP index
***********************************************************************************/
unsigned int gpufreq_get_freq_by_idx(enum gpufreq_target target, int oppidx)
{
struct gpufreq_ipi_data send_msg = {};
unsigned int freq = 0;
if (gpufreq_validate_target(&target))
goto done;
/* implement on EB */
if (g_gpueb_support) {
send_msg.cmd_id = CMD_GET_FREQ_BY_IDX;
send_msg.target = target;
send_msg.u.oppidx = oppidx;
if (!gpufreq_ipi_to_gpueb(send_msg))
freq = g_recv_msg.u.freq;
goto done;
}
/* implement on AP */
if (target == TARGET_STACK && gpufreq_fp && gpufreq_fp->get_fstack_by_idx)
freq = gpufreq_fp->get_fstack_by_idx(oppidx);
else if (target == TARGET_GPU && gpufreq_fp && gpufreq_fp->get_fgpu_by_idx)
freq = gpufreq_fp->get_fgpu_by_idx(oppidx);
//WA for print too much log will trigger kernel api dump
/*
else
GPUFREQ_LOGE("null gpufreq platform function pointer (ENOENT)");
*/
done:
GPUFREQ_LOGD("target: %s, freq[%d]: %d",
target == TARGET_STACK ? "STACK" : "GPU",
oppidx, freq);
return freq;
}
EXPORT_SYMBOL(gpufreq_get_freq_by_idx);
/***********************************************************************************
* Function Name : gpufreq_get_power_by_idx
* Inputs : target - Target of GPU DVFS (GPU, STACK, DEFAULT)
* oppidx - Worknig OPP index of the target
* Outputs : -
* Returns : power - Power of given target at given working OPP index
* Description : Query volt of the target by working OPP index
***********************************************************************************/
unsigned int gpufreq_get_power_by_idx(enum gpufreq_target target, int oppidx)
{
struct gpufreq_ipi_data send_msg = {};
unsigned int power = 0;
if (gpufreq_validate_target(&target))
goto done;
/* implement on EB */
if (g_gpueb_support) {
send_msg.cmd_id = CMD_GET_POWER_BY_IDX;
send_msg.target = target;
send_msg.u.oppidx = oppidx;
if (!gpufreq_ipi_to_gpueb(send_msg))
power = g_recv_msg.u.power;
goto done;
}
/* implement on AP */
if (target == TARGET_STACK && gpufreq_fp && gpufreq_fp->get_pstack_by_idx)
power = gpufreq_fp->get_pstack_by_idx(oppidx);
else if (target == TARGET_GPU && gpufreq_fp && gpufreq_fp->get_pgpu_by_idx)
power = gpufreq_fp->get_pgpu_by_idx(oppidx);
else
GPUFREQ_LOGE("null gpufreq platform function pointer (ENOENT)");
done:
GPUFREQ_LOGD("target: %s, power[%d]: %d",
target == TARGET_STACK ? "STACK" : "GPU",
oppidx, power);
return power;
}
EXPORT_SYMBOL(gpufreq_get_power_by_idx);
/***********************************************************************************
* Function Name : gpufreq_get_oppidx_by_freq
* Inputs : target - Target of GPU DVFS (GPU, STACK, DEFAULT)
* freq - Freq of the target
* Outputs : -
* Returns : oppidx - Working OPP index of given target that has given freq
* Description : Query working OPP index of the target that has the frequency
***********************************************************************************/
int gpufreq_get_oppidx_by_freq(enum gpufreq_target target, unsigned int freq)
{
struct gpufreq_ipi_data send_msg = {};
int oppidx = -1;
if (gpufreq_validate_target(&target))
goto done;
/* implement on EB */
if (g_gpueb_support) {
send_msg.cmd_id = CMD_GET_OPPIDX_BY_FREQ;
send_msg.target = target;
send_msg.u.freq = freq;
if (!gpufreq_ipi_to_gpueb(send_msg))
oppidx = g_recv_msg.u.oppidx;
goto done;
}
/* implement on AP */
if (target == TARGET_STACK && gpufreq_fp && gpufreq_fp->get_idx_by_fstack)
oppidx = gpufreq_fp->get_idx_by_fstack(freq);
else if (target == TARGET_GPU && gpufreq_fp && gpufreq_fp->get_idx_by_fgpu)
oppidx = gpufreq_fp->get_idx_by_fgpu(freq);
else
GPUFREQ_LOGE("null gpufreq platform function pointer (ENOENT)");
done:
GPUFREQ_LOGD("target: %s, oppidx[%d]: %d",
target == TARGET_STACK ? "STACK" : "GPU",
freq, oppidx);
return oppidx;
}
EXPORT_SYMBOL(gpufreq_get_oppidx_by_freq);
/***********************************************************************************
* Function Name : gpufreq_get_leakage_power
* Inputs : target - Target of GPU DVFS (GPU, STACK, DEFAULT)
* volt - Voltage of the target
* Outputs : -
* Returns : p_leakage - Leakage power of given target computed by given volt
* Description : Compute leakage power of the target by the voltage
***********************************************************************************/
unsigned int gpufreq_get_leakage_power(enum gpufreq_target target, unsigned int volt)
{
struct gpufreq_ipi_data send_msg = {};
unsigned int p_leakage = 0;
if (gpufreq_validate_target(&target))
goto done;
/* implement on EB */
if (g_gpueb_support) {
send_msg.cmd_id = CMD_GET_LEAKAGE_POWER;
send_msg.target = target;
send_msg.u.volt = volt;
if (!gpufreq_ipi_to_gpueb(send_msg))
p_leakage = g_recv_msg.u.power;
goto done;
}
/* implement on AP */
if (target == TARGET_STACK && gpufreq_fp && gpufreq_fp->get_lkg_pstack)
p_leakage = gpufreq_fp->get_lkg_pstack(volt);
else if (target == TARGET_GPU && gpufreq_fp && gpufreq_fp->get_lkg_pgpu)
p_leakage = gpufreq_fp->get_lkg_pgpu(volt);
else
GPUFREQ_LOGE("null gpufreq platform function pointer (ENOENT)");
done:
GPUFREQ_LOGD("target: %s, p_leakage[v=%d]: %d",
target == TARGET_STACK ? "STACK" : "GPU",
volt, p_leakage);
return p_leakage;
}
EXPORT_SYMBOL(gpufreq_get_leakage_power);
/***********************************************************************************
* Function Name : gpufreq_get_dynamic_power
* Inputs : target - Target of GPU DVFS (GPU, STACK, DEFAULT)
* freq - Frequency of the target
* volt - Voltage of the target
* Outputs : -
* Returns : p_dynamic - Dynamic power of given target computed by given freq
* and volt
* Description : Compute dynamic power of the target by the frequency and voltage
***********************************************************************************/
unsigned int gpufreq_get_dynamic_power(enum gpufreq_target target,
unsigned int freq, unsigned int volt)
{
unsigned int p_dynamic = 0;
if (gpufreq_validate_target(&target))
goto done;
/* implement on AP */
if (target == TARGET_STACK && gpufreq_fp && gpufreq_fp->get_dyn_pstack)
p_dynamic = gpufreq_fp->get_dyn_pstack(freq, volt);
else if (target == TARGET_GPU && gpufreq_fp && gpufreq_fp->get_dyn_pgpu)
p_dynamic = gpufreq_fp->get_dyn_pgpu(freq, volt);
else
GPUFREQ_LOGE("null gpufreq platform function pointer (ENOENT)");
done:
GPUFREQ_LOGD("target: %s, p_dynamic[f=%d, v=%d]: %d",
target == TARGET_STACK ? "STACK" : "GPU",
freq, volt, p_dynamic);
return p_dynamic;
}
EXPORT_SYMBOL(gpufreq_get_dynamic_power);
/***********************************************************************************
* Function Name : gpufreq_power_control
* Inputs : power - Target power state
* Outputs : -
* Returns : power_count - Success
* GPUFREQ_EINVAL - Failure
* GPUFREQ_ENOENT - Null implementation
* Description : Control power state of whole MFG system
***********************************************************************************/
int gpufreq_power_control(enum gpufreq_power_state power)
{
struct gpufreq_ipi_data send_msg = {};
int ret = GPUFREQ_SUCCESS;
GPUFREQ_TRACE_START("power=%d", power);
if (!gpufreq_power_ctrl_enable()) {
GPUFREQ_LOGD("power control is disabled");
ret = GPUFREQ_SUCCESS;
goto done;
}
/* implement on EB */
if (g_gpueb_support) {
send_msg.cmd_id = CMD_POWER_CONTROL;
send_msg.u.power_state = power;
if (!gpufreq_ipi_to_gpueb(send_msg))
ret = g_recv_msg.u.return_value;
else
ret = GPUFREQ_EINVAL;
goto done;
}
/* implement on AP */
if (gpufreq_fp && gpufreq_fp->power_control)
ret = gpufreq_fp->power_control(power);
else {
ret = GPUFREQ_ENOENT;
GPUFREQ_LOGE("null gpufreq platform function pointer (ENOENT)");
}
done:
/* control DFD */
gpufreq_config_dfd(power);
if (unlikely(ret < 0))
GPUFREQ_LOGE("fail to control power state: %s (%d)",
power ? "POWER_ON" : "POWER_OFF",
ret);
GPUFREQ_TRACE_END();
return ret;
}
EXPORT_SYMBOL(gpufreq_power_control);
/***********************************************************************************
* Function Name : gpufreq_commit
* Inputs : target - Target of GPU DVFS (GPU, STACK, DEFAULT)
* oppidx - Working OPP index of the target
* Outputs : -
* Returns : GPUFREQ_SUCCESS - Success
* GPUFREQ_EINVAL - Failure
* GPUFREQ_ENOENT - Null implementation
* Description : Commit DVFS request to the target with working OPP index
* It will be constrained by the limit recorded in GPU PPM
***********************************************************************************/
int gpufreq_commit(enum gpufreq_target target, int oppidx)
{
struct gpufreq_ipi_data send_msg = {};
int ret = GPUFREQ_SUCCESS;
GPUFREQ_TRACE_START("target=%d, oppidx=%d", target, oppidx);
ret = gpufreq_validate_target(&target);
if (ret)
goto done;
GPUFREQ_LOGD("target: %s, oppidx: %d",
target == TARGET_STACK ? "STACK" : "GPU",
oppidx);
/* implement on EB */
if (g_gpueb_support) {
send_msg.cmd_id = CMD_COMMIT;
send_msg.target = target;
send_msg.u.oppidx = oppidx;
if (!gpufreq_ipi_to_gpueb(send_msg))
ret = g_recv_msg.u.return_value;
else
ret = GPUFREQ_EINVAL;
goto done;
}
/* implement on AP */
if (gpuppm_fp && gpuppm_fp->limited_commit)
ret = gpuppm_fp->limited_commit(target, oppidx);
else {
ret = GPUFREQ_ENOENT;
GPUFREQ_LOGE("null gpuppm platform function pointer (ENOENT)");
}
done:
if (unlikely(ret))
GPUFREQ_LOGE("fail to commit %s OPP index: %d (%d)",
target == TARGET_STACK ? "STACK" : "GPU",
oppidx, ret);
GPUFREQ_TRACE_END();
return ret;
}
EXPORT_SYMBOL(gpufreq_commit);
/***********************************************************************************
* Function Name : gpufreq_set_limit
* Inputs : target - Target of GPU DVFS (GPU, STACK, DEFAULT)
* limiter - Pre-defined user that set limit to GPU DVFS
* ceiling_info - Upper limit info (oppidx, freq, volt, power, ...)
* floor_info - Lower limit info (oppidx, freq, volt, power, ...)
* Outputs : -
* Returns : GPUFREQ_SUCCESS - Success
* GPUFREQ_EINVAL - Failure
* GPUFREQ_ENOENT - Null implementation
* Description : Set ceiling and floor limit to GPU DVFS by specified limiter
* It will immediately trigger DVFS if current OPP violates limit
***********************************************************************************/
int gpufreq_set_limit(enum gpufreq_target target,
enum gpuppm_limiter limiter, int ceiling_info, int floor_info)
{
struct gpufreq_ipi_data send_msg = {};
int ret = GPUFREQ_SUCCESS;
GPUFREQ_TRACE_START("target=%d, limiter=%d, ceiling_info=%d, floor_info=%d",
target, limiter, ceiling_info, floor_info);
ret = gpufreq_validate_target(&target);
if (ret)
goto done;
GPUFREQ_LOGD("target: %s, limiter: %d, ceiling_info: %d, floor_info: %d",
target == TARGET_STACK ? "STACK" : "GPU",
limiter, ceiling_info, floor_info);
/* implement on EB */
if (g_gpueb_support) {
send_msg.cmd_id = CMD_SET_LIMIT;
send_msg.target = target;
send_msg.u.setlimit.limiter = limiter;
send_msg.u.setlimit.ceiling_info = ceiling_info;
send_msg.u.setlimit.floor_info = floor_info;
if (!gpufreq_ipi_to_gpueb(send_msg))
ret = g_recv_msg.u.return_value;
else
ret = GPUFREQ_EINVAL;
goto done;
}
/* implement on AP */
if (gpuppm_fp && gpuppm_fp->set_limit)
ret = gpuppm_fp->set_limit(target, limiter, ceiling_info, floor_info);
else {
ret = GPUFREQ_ENOENT;
GPUFREQ_LOGE("null gpuppm platform function pointer (ENOENT)");
}
done:
if (unlikely(ret))
GPUFREQ_LOGE("fail to set %s limiter: %d, ceiling_info: %d, floor_info: %d (%d)",
target == TARGET_STACK ? "STACK" : "GPU",
limiter, ceiling_info, floor_info, ret);
GPUFREQ_TRACE_END();
return ret;
}
EXPORT_SYMBOL(gpufreq_set_limit);
/***********************************************************************************
* Function Name : gpufreq_get_cur_limit_idx
* Inputs : target - Target of GPU DVFS (GPU, STACK, DEFAULT)
* limit - Type of limit (GPUPPM_CEILING, GPUPPM_FLOOR)
* Outputs : -
* Returns : limit_idx - Working OPP index of ceiling or floor
* Description : Query current working OPP index of ceiling or floor limit
***********************************************************************************/
int gpufreq_get_cur_limit_idx(enum gpufreq_target target, enum gpuppm_limit_type limit)
{
struct gpufreq_shared_status *shared_status = NULL;
int limit_idx = -1;
if (gpufreq_validate_target(&target))
goto done;
if (limit >= GPUPPM_INVALID || limit < 0) {
GPUFREQ_LOGE("invalid limit target: %d (EINVAL)", limit);
goto done;
}
/* implement on EB */
if (g_gpueb_support) {
shared_status = (struct gpufreq_shared_status *)(uintptr_t)g_status_shared_mem_va;
if (target == TARGET_STACK && shared_status) {
if (limit == GPUPPM_CEILING)
limit_idx = shared_status->cur_ceiling_stack;
else if (limit == GPUPPM_FLOOR)
limit_idx = shared_status->cur_floor_stack;
} else if (target == TARGET_GPU && shared_status) {
if (limit == GPUPPM_CEILING)
limit_idx = shared_status->cur_ceiling_gpu;
else if (limit == GPUPPM_FLOOR)
limit_idx = shared_status->cur_floor_gpu;
}
goto done;
}
/* implement on AP */
if (limit == GPUPPM_CEILING && gpuppm_fp && gpuppm_fp->get_ceiling)
limit_idx = gpuppm_fp->get_ceiling(target);
else if (limit == GPUPPM_FLOOR && gpuppm_fp && gpuppm_fp->get_floor)
limit_idx = gpuppm_fp->get_floor(target);
else
GPUFREQ_LOGE("null gpuppm platform function pointer (ENOENT)");
done:
GPUFREQ_LOGD("target: %s, current %s index: %d",
target == TARGET_STACK ? "STACK" : "GPU",
limit == GPUPPM_CEILING ? "ceiling" : "floor",
limit_idx);
return limit_idx;
}
EXPORT_SYMBOL(gpufreq_get_cur_limit_idx);
/***********************************************************************************
* Function Name : gpufreq_get_cur_limiter
* Inputs : target - Target of GPU DVFS (GPU, STACK, DEFAULT)
* limit - Type of limit (GPUPPM_CEILING, GPUPPM_FLOOR)
* Outputs : -
* Returns : limiter - Pre-defined user that set limit to GPU DVFS
* Description : Query which user decide current ceiling and floor OPP index
***********************************************************************************/
unsigned int gpufreq_get_cur_limiter(enum gpufreq_target target, enum gpuppm_limit_type limit)
{
struct gpufreq_shared_status *shared_status = NULL;
unsigned int limiter = 0;
if (gpufreq_validate_target(&target))
goto done;
if (limit >= GPUPPM_INVALID || limit < 0) {
GPUFREQ_LOGE("invalid limit target: %d (EINVAL)", limit);
goto done;
}
/* implement on EB */
if (g_gpueb_support) {
shared_status = (struct gpufreq_shared_status *)(uintptr_t)g_status_shared_mem_va;
if (target == TARGET_STACK && shared_status) {
if (limit == GPUPPM_CEILING)
limiter = shared_status->cur_c_limiter_stack;
else if (limit == GPUPPM_FLOOR)
limiter = shared_status->cur_f_limiter_stack;
} else if (target == TARGET_GPU && shared_status) {
if (limit == GPUPPM_CEILING)
limiter = shared_status->cur_c_limiter_gpu;
else if (limit == GPUPPM_FLOOR)
limiter = shared_status->cur_f_limiter_gpu;
}
goto done;
}
/* implement on AP */
if (limit == GPUPPM_CEILING && gpuppm_fp && gpuppm_fp->get_c_limiter)
limiter = gpuppm_fp->get_c_limiter(target);
else if (limit == GPUPPM_FLOOR && gpuppm_fp && gpuppm_fp->get_f_limiter)
limiter = gpuppm_fp->get_f_limiter(target);
else
GPUFREQ_LOGE("null gpuppm platform function pointer (ENOENT)");
done:
GPUFREQ_LOGD("target: %s, current %s limiter: %d",
target == TARGET_STACK ? "STACK" : "GPU",
limit == GPUPPM_CEILING ? "ceiling" : "floor",
limiter);
return limiter;
}
EXPORT_SYMBOL(gpufreq_get_cur_limiter);
/***********************************************************************************
* Function Name : gpufreq_get_core_mask_table
* Inputs : -
* Outputs : -
* Returns : core_mask_table - Pointer to array of core mask structure
* Description : Get core mask table
* This is for DCS to scale the number of shader cores
***********************************************************************************/
struct gpufreq_core_mask_info *gpufreq_get_core_mask_table(void)
{
struct gpufreq_core_mask_info *core_mask_table = NULL;
/* implement only on AP */
if (gpufreq_fp && gpufreq_fp->get_core_mask_table)
core_mask_table = gpufreq_fp->get_core_mask_table();
else
GPUFREQ_LOGE("null gpufreq platform function pointer (ENOENT)");
return core_mask_table;
}
EXPORT_SYMBOL(gpufreq_get_core_mask_table);
/***********************************************************************************
* Function Name : gpufreq_get_core_num
* Inputs : -
* Outputs : -
* Returns : core_num - # of GPU Shader cores
* Description : Get number of GPU shader cores
* This is for DCS to scale the number of shader cores
***********************************************************************************/
unsigned int gpufreq_get_core_num(void)
{
unsigned int core_num = 0;
/* implement only on AP */
if (gpufreq_fp && gpufreq_fp->get_core_num)
core_num = gpufreq_fp->get_core_num();
else
GPUFREQ_LOGE("null gpufreq platform function pointer (ENOENT)");
return core_num;
}
EXPORT_SYMBOL(gpufreq_get_core_num);
/***********************************************************************************
* Function Name : gpufreq_pdc_control
* Inputs : power - Target power state
* Outputs : -
* Returns : -
* Description : Manually control PDCv2 setting
***********************************************************************************/
void gpufreq_pdc_control(enum gpufreq_power_state power)
{
/* implement only on AP */
if (gpufreq_fp && gpufreq_fp->pdc_control)
gpufreq_fp->pdc_control(power);
else
GPUFREQ_LOGE("null gpufreq platform function pointer (ENOENT)");
}
EXPORT_SYMBOL(gpufreq_pdc_control);
/***********************************************************************************
* Function Name : gpufreq_fake_spm_mtcmos_control
* Inputs : power - Target power state
* Outputs : -
* Returns : -
* Description : Fake PWR_CON value of SPM MFG register
***********************************************************************************/
void gpufreq_fake_spm_mtcmos_control(enum gpufreq_power_state power)
{
/* implement only on AP */
if (gpufreq_fp && gpufreq_fp->fake_spm_mtcmos_control)
gpufreq_fp->fake_spm_mtcmos_control(power);
else
GPUFREQ_LOGE("null gpufreq platform function pointer (ENOENT)");
}
EXPORT_SYMBOL(gpufreq_fake_spm_mtcmos_control);
/***********************************************************************************
* Function Name : gpufreq_get_debug_opp_info
* Description : Only for GPUFREQ internal debug purpose
***********************************************************************************/
struct gpufreq_debug_opp_info gpufreq_get_debug_opp_info(enum gpufreq_target target)
{
struct gpufreq_shared_status *shared_status = NULL;
struct gpufreq_ipi_data send_msg = {};
struct gpufreq_debug_opp_info opp_info = {};
if (gpufreq_validate_target(&target))
goto done;
/* implement on EB */
if (g_gpueb_support) {
shared_status = (struct gpufreq_shared_status *)(uintptr_t)g_status_shared_mem_va;
send_msg.cmd_id = CMD_GET_DEBUG_OPP_INFO;
send_msg.target = target;
if (!gpufreq_ipi_to_gpueb(send_msg) && shared_status)
opp_info = shared_status->opp_info;
goto done;
}
/* implement on AP */
if (target == TARGET_STACK && gpufreq_fp && gpufreq_fp->get_debug_opp_info_stack)
opp_info = gpufreq_fp->get_debug_opp_info_stack();
else if (target == TARGET_GPU && gpufreq_fp && gpufreq_fp->get_debug_opp_info_gpu)
opp_info = gpufreq_fp->get_debug_opp_info_gpu();
else
GPUFREQ_LOGE("null gpufreq platform function pointer (ENOENT)");
done:
return opp_info;
}
/***********************************************************************************
* Function Name : gpufreq_get_debug_limit_info
* Description : Only for GPUFREQ internal debug purpose
***********************************************************************************/
struct gpufreq_debug_limit_info gpufreq_get_debug_limit_info(enum gpufreq_target target)
{
struct gpufreq_shared_status *shared_status = NULL;
struct gpufreq_ipi_data send_msg = {};
struct gpufreq_debug_limit_info limit_info = {};
if (gpufreq_validate_target(&target))
goto done;
/* implement on EB */
if (g_gpueb_support) {
shared_status = (struct gpufreq_shared_status *)(uintptr_t)g_status_shared_mem_va;
send_msg.cmd_id = CMD_GET_DEBUG_LIMIT_INFO;
send_msg.target = target;
if (!gpufreq_ipi_to_gpueb(send_msg) && shared_status)
limit_info = shared_status->limit_info;
goto done;
}
/* implement on AP */
if (gpuppm_fp && gpuppm_fp->get_debug_limit_info)
limit_info = gpuppm_fp->get_debug_limit_info(target);
else
GPUFREQ_LOGE("null gpuppm platform function pointer (ENOENT)");
done:
return limit_info;
}
/***********************************************************************************
* Function Name : gpufreq_get_working_table
* Description : Only for GPUFREQ internal debug purpose
***********************************************************************************/
const struct gpufreq_opp_info *gpufreq_get_working_table(enum gpufreq_target target)
{
struct gpufreq_ipi_data send_msg = {};
const struct gpufreq_opp_info *opp_table = NULL;
int ret = GPUFREQ_SUCCESS;
if (gpufreq_validate_target(&target))
goto done;
/* implement on EB */
if (g_gpueb_support) {
send_msg.cmd_id = CMD_GET_WORKING_TABLE;
send_msg.target = target;
if (!gpufreq_ipi_to_gpueb(send_msg))
ret = g_recv_msg.u.return_value;
if (!ret)
opp_table = (struct gpufreq_opp_info *)(uintptr_t)g_debug_shared_mem_va;
goto done;
}
/* implement on AP */
if (target == TARGET_STACK && gpufreq_fp && gpufreq_fp->get_working_table_stack)
opp_table = gpufreq_fp->get_working_table_stack();
else if (target == TARGET_GPU && gpufreq_fp && gpufreq_fp->get_working_table_gpu)
opp_table = gpufreq_fp->get_working_table_gpu();
else
GPUFREQ_LOGE("null gpufreq platform function pointer (ENOENT)");
done:
return opp_table;
}
EXPORT_SYMBOL(gpufreq_get_working_table);
/***********************************************************************************
* Function Name : gpufreq_get_signed_table
* Description : Only for GPUFREQ internal debug purpose
***********************************************************************************/
const struct gpufreq_opp_info *gpufreq_get_signed_table(enum gpufreq_target target)
{
struct gpufreq_ipi_data send_msg = {};
const struct gpufreq_opp_info *opp_table = NULL;
int ret = GPUFREQ_SUCCESS;
if (gpufreq_validate_target(&target))
goto done;
/* implement on EB */
if (g_gpueb_support) {
send_msg.cmd_id = CMD_GET_SIGNED_TABLE;
send_msg.target = target;
if (!gpufreq_ipi_to_gpueb(send_msg))
ret = g_recv_msg.u.return_value;
if (!ret)
opp_table = (struct gpufreq_opp_info *)(uintptr_t)g_debug_shared_mem_va;
goto done;
}
/* implement on AP */
if (target == TARGET_STACK && gpufreq_fp && gpufreq_fp->get_signed_table_stack)
opp_table = gpufreq_fp->get_signed_table_stack();
else if (target == TARGET_GPU && gpufreq_fp && gpufreq_fp->get_signed_table_gpu)
opp_table = gpufreq_fp->get_signed_table_gpu();
else
GPUFREQ_LOGE("null gpufreq platform function pointer (ENOENT)");
done:
return opp_table;
}
/***********************************************************************************
* Function Name : gpufreq_get_limit_table
* Description : Only for GPUFREQ internal debug purpose
***********************************************************************************/
const struct gpuppm_limit_info *gpufreq_get_limit_table(enum gpufreq_target target)
{
struct gpufreq_ipi_data send_msg = {};
const struct gpuppm_limit_info *limit_table = NULL;
int ret = GPUFREQ_SUCCESS;
if (gpufreq_validate_target(&target))
goto done;
/* implement on EB */
if (g_gpueb_support) {
send_msg.cmd_id = CMD_GET_LIMIT_TABLE;
send_msg.target = target;
if (!gpufreq_ipi_to_gpueb(send_msg))
ret = g_recv_msg.u.return_value;
if (!ret)
limit_table = (struct gpuppm_limit_info *)(uintptr_t)g_debug_shared_mem_va;
goto done;
}
/* implement on AP */
if (gpuppm_fp && gpuppm_fp->get_limit_table)
limit_table = gpuppm_fp->get_limit_table(target);
else
GPUFREQ_LOGE("null gpuppm platform function pointer (ENOENT)");
done:
return limit_table;
}
/***********************************************************************************
* Function Name : gpufreq_switch_limit
* Description : Only for GPUFREQ internal debug purpose
***********************************************************************************/
int gpufreq_switch_limit(enum gpufreq_target target,
enum gpuppm_limiter limiter, int c_enable, int f_enable)
{
struct gpufreq_ipi_data send_msg = {};
int ret = GPUFREQ_SUCCESS;
ret = gpufreq_validate_target(&target);
if (ret)
goto done;
/* implement on EB */
if (g_gpueb_support) {
send_msg.cmd_id = CMD_SWITCH_LIMIT;
send_msg.target = target;
send_msg.u.setlimit.limiter = limiter;
send_msg.u.setlimit.ceiling_info = c_enable;
send_msg.u.setlimit.floor_info = f_enable;
if (!gpufreq_ipi_to_gpueb(send_msg))
ret = g_recv_msg.u.return_value;
else
ret = GPUFREQ_EINVAL;
goto done;
}
/* implement on AP */
if (gpuppm_fp && gpuppm_fp->switch_limit)
ret = gpuppm_fp->switch_limit(target, limiter, c_enable, f_enable);
else {
ret = GPUFREQ_ENOENT;
GPUFREQ_LOGE("null gpuppm platform function pointer (ENOENT)");
}
done:
if (unlikely(ret))
GPUFREQ_LOGE("fail to switch %s limiter: %d, c_enable: %d, f_enable: %d (%d)",
target == TARGET_STACK ? "STACK" : "GPU",
limiter, c_enable, f_enable, ret);
GPUFREQ_TRACE_END();
return ret;
}
/***********************************************************************************
* Function Name : gpufreq_fix_target_oppidx
* Description : Only for GPUFREQ internal debug purpose
***********************************************************************************/
int gpufreq_fix_target_oppidx(enum gpufreq_target target, int oppidx)
{
struct gpufreq_ipi_data send_msg = {};
int ret = GPUFREQ_SUCCESS;
GPUFREQ_TRACE_START("target=%d, oppidx=%d", target, oppidx);
ret = gpufreq_validate_target(&target);
if (ret)
goto done;
/* implement on EB */
if (g_gpueb_support) {
send_msg.cmd_id = CMD_FIX_TARGET_OPPIDX;
send_msg.target = target;
send_msg.u.oppidx = oppidx;
if (!gpufreq_ipi_to_gpueb(send_msg))
ret = g_recv_msg.u.return_value;
else
ret = GPUFREQ_EINVAL;
goto done;
}
/* implement on AP */
if (target == TARGET_STACK && gpufreq_fp && gpufreq_fp->fix_target_oppidx_stack)
ret = gpufreq_fp->fix_target_oppidx_stack(oppidx);
else if (target == TARGET_GPU && gpufreq_fp && gpufreq_fp->fix_target_oppidx_gpu)
ret = gpufreq_fp->fix_target_oppidx_gpu(oppidx);
else {
ret = GPUFREQ_ENOENT;
GPUFREQ_LOGE("null gpufreq platform function pointer (ENOENT)");
}
done:
if (unlikely(ret))
GPUFREQ_LOGE("fail to fix %s OPP index: %d (%d)",
target == TARGET_STACK ? "STACK" : "GPU",
oppidx, ret);
GPUFREQ_TRACE_END();
return ret;
}
/***********************************************************************************
* Function Name : gpufreq_fix_custom_freq_volt
* Description : Only for GPUFREQ internal debug purpose
***********************************************************************************/
int gpufreq_fix_custom_freq_volt(enum gpufreq_target target,
unsigned int freq, unsigned int volt)
{
struct gpufreq_ipi_data send_msg = {};
int ret = GPUFREQ_SUCCESS;
GPUFREQ_TRACE_START("target=%d, freq=%d, volt=%d", target, freq, volt);
ret = gpufreq_validate_target(&target);
if (ret)
goto done;
/* implement on EB */
if (g_gpueb_support) {
send_msg.cmd_id = CMD_FIX_CUSTOM_FREQ_VOLT;
send_msg.target = target;
send_msg.u.custom.freq = freq;
send_msg.u.custom.volt = volt;
if (!gpufreq_ipi_to_gpueb(send_msg))
ret = g_recv_msg.u.return_value;
else
ret = GPUFREQ_EINVAL;
goto done;
}
/* implement on AP */
if (target == TARGET_STACK && gpufreq_fp && gpufreq_fp->fix_custom_freq_volt_stack)
ret = gpufreq_fp->fix_custom_freq_volt_stack(freq, volt);
else if (target == TARGET_GPU && gpufreq_fp && gpufreq_fp->fix_custom_freq_volt_gpu)
ret = gpufreq_fp->fix_custom_freq_volt_gpu(freq, volt);
else {
ret = GPUFREQ_ENOENT;
GPUFREQ_LOGE("null gpufreq platform function pointer (ENOENT)");
}
done:
if (unlikely(ret))
GPUFREQ_LOGE("fail to fix %s freq: %d, volt: %d (%d)",
target == TARGET_STACK ? "STACK" : "GPU",
freq, volt, ret);
GPUFREQ_TRACE_END();
return ret;
}
/***********************************************************************************
* Function Name : gpufreq_set_stress_test
* Description : Only for GPUFREQ internal debug purpose
***********************************************************************************/
int gpufreq_set_stress_test(unsigned int mode)
{
struct gpufreq_ipi_data send_msg = {};
int ret = GPUFREQ_SUCCESS;
/* implement on EB */
if (g_gpueb_support) {
send_msg.cmd_id = CMD_SET_STRESS_TEST;
send_msg.u.mode = mode;
ret = gpufreq_ipi_to_gpueb(send_msg);
/* implement on AP */
} else {
if (gpufreq_fp && gpufreq_fp->set_stress_test)
gpufreq_fp->set_stress_test(mode);
else {
ret = GPUFREQ_ENOENT;
GPUFREQ_LOGE("null gpufreq platform function pointer (ENOENT)");
}
}
return ret;
}
/***********************************************************************************
* Function Name : gpufreq_set_aging_mode
* Description : Only for GPUFREQ internal debug purpose
***********************************************************************************/
int gpufreq_set_aging_mode(unsigned int mode)
{
struct gpufreq_ipi_data send_msg = {};
int ret = GPUFREQ_SUCCESS;
/* implement on EB */
if (g_gpueb_support) {
send_msg.cmd_id = CMD_SET_AGING_MODE;
send_msg.u.mode = mode;
if (!gpufreq_ipi_to_gpueb(send_msg))
ret = g_recv_msg.u.return_value;
else
ret = GPUFREQ_EINVAL;
/* implement on AP */
} else {
if (gpufreq_fp && gpufreq_fp->set_aging_mode)
ret = gpufreq_fp->set_aging_mode(mode);
else {
ret = GPUFREQ_ENOENT;
GPUFREQ_LOGE("null gpufreq platform function pointer (ENOENT)");
}
}
if (unlikely(ret))
GPUFREQ_LOGE("fail to set aging mode: %d (%d)", mode, ret);
return ret;
}
/***********************************************************************************
* Function Name : gpufreq_set_gpm_mode
* Description : Only for GPUFREQ internal debug purpose
***********************************************************************************/
int gpufreq_set_gpm_mode(unsigned int mode)
{
struct gpufreq_ipi_data send_msg = {};
int ret = GPUFREQ_SUCCESS;
/* implement on EB */
if (g_gpueb_support) {
send_msg.cmd_id = CMD_SET_GPM_MODE;
send_msg.u.mode = mode;
ret = gpufreq_ipi_to_gpueb(send_msg);
/* implement on AP */
} else {
if (gpufreq_fp && gpufreq_fp->set_gpm_mode)
gpufreq_fp->set_gpm_mode(mode);
else {
ret = GPUFREQ_ENOENT;
GPUFREQ_LOGE("null gpufreq platform function pointer (ENOENT)");
}
}
return ret;
}
/***********************************************************************************
* Function Name : gpufreq_set_test_mode
* Description : Only for GPUFREQ internal debug purpose
***********************************************************************************/
int gpufreq_set_test_mode(unsigned int mode)
{
struct gpufreq_ipi_data send_msg = {};
int ret = GPUFREQ_SUCCESS;
/* implement on EB */
if (g_gpueb_support) {
send_msg.cmd_id = CMD_SET_TEST_MODE;
send_msg.u.mode = mode;
if (!gpufreq_ipi_to_gpueb(send_msg))
ret = g_recv_msg.u.return_value;
else
ret = GPUFREQ_EINVAL;
}
return ret;
}
/***********************************************************************************
* Function Name : gpufreq_get_asensor_info
* Description : Only for GPUFREQ internal debug purpose
***********************************************************************************/
struct gpufreq_asensor_info gpufreq_get_asensor_info(void)
{
struct gpufreq_shared_status *shared_status = NULL;
struct gpufreq_asensor_info asensor_info = {};
/* implement on EB */
if (g_gpueb_support) {
shared_status = (struct gpufreq_shared_status *)(uintptr_t)g_status_shared_mem_va;
if (shared_status)
asensor_info = shared_status->asensor_info;
/* implement on AP */
} else {
if (gpufreq_fp && gpufreq_fp->get_asensor_info)
asensor_info = gpufreq_fp->get_asensor_info();
else
GPUFREQ_LOGE("null gpufreq platform function pointer (ENOENT)");
}
return asensor_info;
}
/***********************************************************************************
* Function Name : gpufreq_ipi_to_gpueb
* Description : Only for GPUFREQ internal IPI purpose
***********************************************************************************/
static int gpufreq_ipi_to_gpueb(struct gpufreq_ipi_data data)
{
int ret = GPUFREQ_SUCCESS;
if (data.cmd_id < 0 || data.cmd_id >= CMD_NUM) {
GPUFREQ_LOGE("invalid gpufreq IPI command: %d (EINVAL)", data.cmd_id);
ret = GPUFREQ_EINVAL;
goto done;
}
GPUFREQ_LOGD("channel: %d send IPI command: %s (%d)",
g_ipi_channel, gpufreq_ipi_cmd_name[data.cmd_id], data.cmd_id);
ret = mtk_ipi_send_compl(get_gpueb_ipidev(), g_ipi_channel, IPI_SEND_POLLING,
(void *)&data, GPUFREQ_IPI_DATA_LEN, IPI_TIMEOUT_MS);
if (unlikely(ret != IPI_ACTION_DONE)) {
GPUFREQ_LOGE("[ABORT] fail to send IPI command: %s (%d)",
gpufreq_ipi_cmd_name[data.cmd_id], ret);
gpufreq_abort();
goto done;
}
ret = GPUFREQ_SUCCESS;
GPUFREQ_LOGD("channel: %d receive IPI command: %s (%d)",
g_ipi_channel, gpufreq_ipi_cmd_name[g_recv_msg.cmd_id], g_recv_msg.cmd_id);
done:
return ret;
}
/***********************************************************************************
* Function Name : gpufreq_validate_target
* Description : Validate gpufreq target and re-assign default target
***********************************************************************************/
static int gpufreq_validate_target(unsigned int *target)
{
if (*target >= TARGET_INVALID || *target < 0 ||
(*target == TARGET_STACK && !g_dual_buck)) {
GPUFREQ_LOGE("invalid OPP target: %d (EINVAL)", *target);
return GPUFREQ_EINVAL;
}
if (*target == TARGET_DEFAULT) {
if (g_dual_buck)
*target = TARGET_STACK;
else
*target = TARGET_GPU;
}
return GPUFREQ_SUCCESS;
}
/***********************************************************************************
* Function Name : gpufreq_dump_dvfs_status
* Description : Dump DVFS status from shared memory
***********************************************************************************/
static void gpufreq_dump_dvfs_status(void)
{
struct gpufreq_shared_status *shared_status = NULL;
shared_status = (struct gpufreq_shared_status *)(uintptr_t)g_status_shared_mem_va;
if (shared_status) {
GPUFREQ_LOGI("== [GPUFREQ DVFS STATUS: 0x%llx] ==", shared_status);
GPUFREQ_LOGI("GPU[%d] Freq: %d, Volt: %d, Vsram: %d",
shared_status->cur_oppidx_gpu, shared_status->cur_fgpu,
shared_status->cur_vgpu, shared_status->cur_vsram_gpu);
GPUFREQ_LOGI("STACK[%d] Freq: %d, Volt: %d, Vsram: %d",
shared_status->cur_oppidx_stack, shared_status->cur_fstack,
shared_status->cur_vstack, shared_status->cur_vsram_stack);
GPUFREQ_LOGI("GPU Ceiling/Floor: %d/%d, Limiter: %d/%d",
shared_status->cur_ceiling_gpu, shared_status->cur_floor_gpu,
shared_status->cur_c_limiter_gpu, shared_status->cur_f_limiter_gpu);
GPUFREQ_LOGI("STACK Ceiling/Floor: %d/%d, Limiter: %d/%d",
shared_status->cur_ceiling_stack, shared_status->cur_floor_stack,
shared_status->cur_c_limiter_stack, shared_status->cur_f_limiter_stack);
GPUFREQ_LOGI("Power Count: %d, Aging Enable: %d, AVS Enable: %d",
shared_status->power_count, shared_status->aging_enable,
shared_status->avs_enable);
GPUFREQ_LOGI("GPU_SB_Version: 0x%04x, GPU_PTP_Version: 0x%04x",
shared_status->sb_version, shared_status->ptp_version);
}
}
/***********************************************************************************
* Function Name : gpufreq_abort
* Description : Trigger exception when fatal error and dump infra status
***********************************************************************************/
static void gpufreq_abort(void)
{
gpueb_dump_status();
gpufreq_dump_infra_status();
#if GPUFREQ_FORCE_WDT_ENABLE
gpueb_trigger_wdt("GPUFREQ");
#else
BUG_ON(1);
#endif /* GPUFREQ_FORCE_WDT_ENABLE */
}
#if IS_ENABLED(CONFIG_MTK_BATTERY_OC_POWER_THROTTLING)
static void gpufreq_batt_oc_callback(enum BATTERY_OC_LEVEL_TAG batt_oc_level)
{
int ret = GPUFREQ_SUCCESS;
ret = gpufreq_set_limit(TARGET_DEFAULT, LIMIT_BATT_OC, batt_oc_level, GPUPPM_KEEP_IDX);
if (unlikely(ret))
GPUFREQ_LOGE("fail to set LIMIT_BATT_OC limit level: %d (%d)",
batt_oc_level, ret);
}
#endif /* CONFIG_MTK_BATTERY_OC_POWER_THROTTLING */
#if IS_ENABLED(CONFIG_MTK_BATTERY_PERCENT_THROTTLING)
static void gpufreq_batt_percent_callback(enum BATTERY_PERCENT_LEVEL_TAG batt_percent_level)
{
int ret = GPUFREQ_SUCCESS;
ret = gpufreq_set_limit(TARGET_DEFAULT, LIMIT_BATT_PERCENT,
batt_percent_level, GPUPPM_KEEP_IDX);
if (unlikely(ret))
GPUFREQ_LOGE("fail to set LIMIT_BATT_PERCENT limit level: %d (%d)",
batt_percent_level, ret);
}
#endif /* CONFIG_MTK_BATTERY_PERCENT_THROTTLING */
#if IS_ENABLED(CONFIG_MTK_LOW_BATTERY_POWER_THROTTLING)
static void gpufreq_low_batt_callback(enum LOW_BATTERY_LEVEL_TAG low_batt_level)
{
int ret = GPUFREQ_SUCCESS;
ret = gpufreq_set_limit(TARGET_DEFAULT, LIMIT_LOW_BATT, low_batt_level, GPUPPM_KEEP_IDX);
if (unlikely(ret))
GPUFREQ_LOGE("fail to set LIMIT_LOW_BATT limit level: %d (%d)",
low_batt_level, ret);
}
#endif /* CONFIG_MTK_LOW_BATTERY_POWER_THROTTLING */
static void gpufreq_init_external_callback(void)
{
#if IS_ENABLED(CONFIG_MTK_PBM)
struct pbm_gpu_callback_table pbm_cb = {
.get_max_pb = gpufreq_get_max_power,
.get_min_pb = gpufreq_get_min_power,
.get_cur_pb = gpufreq_get_cur_power,
.get_cur_vol = gpufreq_get_cur_volt,
.set_limit = gpufreq_set_limit,
};
#endif /* CONFIG_MTK_PBM */
/* hook GPU HAL callback */
mtk_get_gpu_limit_index_fp = gpufreq_get_cur_limit_idx;
mtk_get_gpu_limiter_fp = gpufreq_get_cur_limiter;
mtk_get_gpu_cur_freq_fp = gpufreq_get_cur_freq;
mtk_get_gpu_cur_oppidx_fp = gpufreq_get_cur_oppidx;
/* register PBM callback */
#if IS_ENABLED(CONFIG_MTK_PBM)
register_pbm_gpu_notify(&pbm_cb);
#endif /* CONFIG_MTK_PBM */
/* register power throttling callback */
#if IS_ENABLED(CONFIG_MTK_LOW_BATTERY_POWER_THROTTLING)
register_low_battery_notify(&gpufreq_low_batt_callback, LOW_BATTERY_PRIO_GPU);
#endif /* CONFIG_MTK_LOW_BATTERY_POWER_THROTTLING */
#if IS_ENABLED(CONFIG_MTK_BATTERY_PERCENT_THROTTLING)
register_bp_thl_notify(&gpufreq_batt_percent_callback, BATTERY_PERCENT_PRIO_GPU);
#endif /* CONFIG_MTK_BATTERY_PERCENT_THROTTLING */
#if IS_ENABLED(CONFIG_MTK_BATTERY_OC_POWER_THROTTLING)
register_battery_oc_notify(&gpufreq_batt_oc_callback, BATTERY_OC_PRIO_GPU);
#endif /* CONFIG_MTK_BATTERY_OC_POWER_THROTTLING */
}
void gpufreq_register_gpufreq_fp(struct gpufreq_platform_fp *platform_fp)
{
if (!platform_fp) {
GPUFREQ_LOGE("null gpufreq platform function pointer (EINVAL)");
return;
}
gpufreq_fp = platform_fp;
}
EXPORT_SYMBOL(gpufreq_register_gpufreq_fp);
void gpufreq_register_gpuppm_fp(struct gpuppm_platform_fp *platform_fp)
{
if (!platform_fp) {
GPUFREQ_LOGE("null gpuppm platform function pointer (EINVAL)");
return;
}
gpuppm_fp = platform_fp;
/* all platform & gpuppm impl is ready after gpuppm func pointer registration */
/* register external callback function at here */
gpufreq_init_external_callback();
/* init gpufreq debug */
gpufreq_debug_init(g_dual_buck, g_gpueb_support);
/* workaround to dump mt6983/mt6895 critical volt */
if (gpufreq_fp && gpufreq_fp->get_critical_volt)
gpufreq_fp->get_critical_volt(gpufreq_get_signed_table(TARGET_STACK));
}
EXPORT_SYMBOL(gpufreq_register_gpuppm_fp);
static int gpufreq_gpueb_init(void)
{
struct gpufreq_ipi_data send_msg = {};
phys_addr_t shared_mem_pa = 0, shared_mem_va = 0, shared_mem_size = 0;
phys_addr_t status_shared_mem_pa = 0, status_shared_mem_size = 0;
phys_addr_t debug_shared_mem_pa = 0, debug_shared_mem_size = 0;
int ret = GPUFREQ_SUCCESS;
/* init ipi channel */
g_ipi_channel = gpueb_get_send_PIN_ID_by_name("IPI_ID_GPUFREQ");
if (unlikely(g_ipi_channel < 0)) {
GPUFREQ_LOGE("fail to get gpufreq IPI channel id (ENOENT)");
ret = GPUFREQ_ENOENT;
goto done;
}
mtk_ipi_register(get_gpueb_ipidev(), g_ipi_channel, NULL, NULL, (void *)&g_recv_msg);
/* init shared memory */
shared_mem_va = gpueb_get_reserve_mem_virt_by_name("MEM_ID_GPUFREQ");
if (!shared_mem_va) {
GPUFREQ_LOGE("fail to get gpufreq reserved memory virtual addr (ENOENT)");
ret = GPUFREQ_ENOENT;
goto done;
}
shared_mem_pa = gpueb_get_reserve_mem_phys_by_name("MEM_ID_GPUFREQ");
if (!shared_mem_pa) {
GPUFREQ_LOGE("fail to get gpufreq reserved memory physical addr (ENOENT)");
ret = GPUFREQ_ENOENT;
goto done;
}
shared_mem_size = gpueb_get_reserve_mem_size_by_name("MEM_ID_GPUFREQ");
if (!shared_mem_size) {
GPUFREQ_LOGE("fail to get gpufreq reserved memory size (ENOENT)");
ret = GPUFREQ_ENOENT;
goto done;
}
g_status_shared_mem_va = shared_mem_va;
status_shared_mem_pa = shared_mem_pa;
status_shared_mem_size = GPUFREQ_STATUS_MEM_SZ;
g_debug_shared_mem_va = shared_mem_va + GPUFREQ_STATUS_MEM_SZ;
debug_shared_mem_pa = shared_mem_pa + GPUFREQ_STATUS_MEM_SZ;
debug_shared_mem_size = shared_mem_size - GPUFREQ_STATUS_MEM_SZ;
send_msg.cmd_id = CMD_INIT_SHARED_MEM;
send_msg.u.addr.status_base = status_shared_mem_pa;
send_msg.u.addr.status_size = (uint32_t)status_shared_mem_size;
send_msg.u.addr.debug_base = debug_shared_mem_pa;
send_msg.u.addr.debug_size = (uint32_t)debug_shared_mem_size;
ret = gpufreq_ipi_to_gpueb(send_msg);
if (unlikely(ret)) {
GPUFREQ_LOGE("fail to init gpufreq shared memory (EINVAL)");
ret = GPUFREQ_EINVAL;
}
GPUFREQ_LOGI("status shared memory phy_addr: 0x%llx, virt_addr: 0x%llx, size: 0x%x",
status_shared_mem_pa, g_status_shared_mem_va, status_shared_mem_size);
GPUFREQ_LOGI("debug shared memory phy_addr: 0x%llx, virt_addr: 0x%llx, size: 0x%x",
debug_shared_mem_pa, g_debug_shared_mem_va, debug_shared_mem_size);
done:
return ret;
}
static int gpufreq_wrapper_pdrv_probe(struct platform_device *pdev)
{
struct device_node *of_wrapper = pdev->dev.of_node;
int ret = GPUFREQ_SUCCESS;
GPUFREQ_LOGI("start to probe gpufreq wrapper driver");
if (unlikely(!of_wrapper)) {
GPUFREQ_LOGE("fail to find gpufreq wrapper of_node (ENOENT)");
ret = GPUFREQ_ENOENT;
goto done;
}
of_property_read_u32(of_wrapper, "dual-buck", &g_dual_buck);
of_property_read_u32(of_wrapper, "gpueb-support", &g_gpueb_support);
/* init gpueb setting if gpueb is enabled */
if (g_gpueb_support) {
ret = gpufreq_gpueb_init();
if (unlikely(ret)) {
GPUFREQ_LOGE("fail to init gpueb IPI and shared memory (%d)", ret);
goto done;
}
}
GPUFREQ_LOGI("gpufreq wrapper driver probe done, dual_buck: %s, gpueb: %s",
g_dual_buck ? "true" : "false",
g_gpueb_support ? "on" : "off");
done:
return ret;
}
static int __init gpufreq_wrapper_init(void)
{
int ret = GPUFREQ_SUCCESS;
GPUFREQ_LOGI("start to init gpufreq wrapper driver");
/* register platform driver */
ret = platform_driver_register(&g_gpufreq_wrapper_pdrv);
if (unlikely(ret)) {
GPUFREQ_LOGE("fail to register gpufreq wrapper driver (%d)", ret);
goto done;
}
GPUFREQ_LOGI("gpufreq wrapper driver init done");
done:
return ret;
}
static void __exit gpufreq_wrapper_exit(void)
{
platform_driver_unregister(&g_gpufreq_wrapper_pdrv);
}
module_init(gpufreq_wrapper_init);
module_exit(gpufreq_wrapper_exit);
MODULE_DEVICE_TABLE(of, g_gpufreq_wrapper_of_match);
MODULE_DESCRIPTION("MediaTek GPU-DVFS wrapper driver");
MODULE_LICENSE("GPL");