435 lines
11 KiB
C
435 lines
11 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2019 MediaTek Inc.
|
|
*/
|
|
|
|
#include <linux/version.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/types.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/time.h>
|
|
#include <linux/jiffies.h>
|
|
#include <mach/mtk_thermal.h> /* needed by tscpu_settings.h */
|
|
#include <tscpu_settings.h> /* needed by tscpu_warn */
|
|
#include <ap_thermal_limit.h>
|
|
#include <mt-plat/aee.h>
|
|
|
|
|
|
#if defined(ATM_USES_PPM)
|
|
#if defined(CONFIG_MTK_PPM)
|
|
#include "mtk_ppm_api.h"
|
|
#endif
|
|
#else
|
|
#include "mt_cpufreq.h"
|
|
#endif
|
|
#if defined(THERMAL_VPU_SUPPORT)
|
|
#if defined(CONFIG_MTK_APUSYS_SUPPORT)
|
|
#include "apu_power_table.h"
|
|
#else
|
|
#include "vpu_dvfs.h"
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(THERMAL_MDLA_SUPPORT)
|
|
#if defined(CONFIG_MTK_APUSYS_SUPPORT)
|
|
#include "apu_power_table.h"
|
|
#else
|
|
#include "mdla_dvfs.h"
|
|
#endif
|
|
#endif
|
|
|
|
/*=============================================================
|
|
* Local variable definition
|
|
*=============================================================
|
|
*/
|
|
#define AP_THERMO_LMT_MAX_USERS (5)
|
|
|
|
/*=============================================================
|
|
* Local variable definition
|
|
*=============================================================
|
|
*/
|
|
static unsigned int apthermolmt_prev_cpu_pwr_lim;
|
|
static unsigned int apthermolmt_curr_cpu_pwr_lim = 0x7FFFFFFF;
|
|
#if defined(THERMAL_VPU_SUPPORT)
|
|
static unsigned int apthermolmt_prev_vpu_pwr_lim;
|
|
static unsigned int apthermolmt_curr_vpu_pwr_lim = 0x7FFFFFFF;
|
|
#endif
|
|
#if defined(THERMAL_MDLA_SUPPORT)
|
|
static unsigned int apthermolmt_prev_mdla_pwr_lim;
|
|
static unsigned int apthermolmt_curr_mdla_pwr_lim = 0x7FFFFFFF;
|
|
#endif
|
|
static unsigned int apthermolmt_prev_gpu_pwr_lim;
|
|
static unsigned int apthermolmt_curr_gpu_pwr_lim = 0x7FFFFFFF;
|
|
|
|
static struct apthermolmt_user _dummy = {
|
|
.log = "dummy ",
|
|
.cpu_limit = 0x7FFFFFFF,
|
|
.vpu_limit = 0x7FFFFFFF,
|
|
.mdla_limit = 0x7FFFFFFF,
|
|
.gpu_limit = 0x7FFFFFFF,
|
|
.ptr = &_dummy
|
|
};
|
|
|
|
static struct apthermolmt_user _gp = {
|
|
.log = "set_gp_power ",
|
|
.cpu_limit = 0x7FFFFFFF,
|
|
.vpu_limit = 0x7FFFFFFF,
|
|
.mdla_limit = 0x7FFFFFFF,
|
|
.gpu_limit = 0x7FFFFFFF,
|
|
.ptr = &_gp
|
|
};
|
|
static struct apthermolmt_user *_users[AP_THERMO_LMT_MAX_USERS] = {
|
|
&_gp, &_dummy, &_dummy, &_dummy, &_dummy};
|
|
|
|
static unsigned int gp_prev_cpu_pwr_limit;
|
|
static unsigned int gp_curr_cpu_pwr_limit;
|
|
static unsigned int gp_prev_gpu_pwr_limit;
|
|
static unsigned int gp_curr_gpu_pwr_limit;
|
|
|
|
static DEFINE_MUTEX(apthermolmt_cpu_mutex);
|
|
|
|
/*=============================================================
|
|
* Weak functions
|
|
*=============================================================
|
|
*/
|
|
#if defined(ATM_USES_PPM)
|
|
void __attribute__ ((weak))
|
|
mt_ppm_cpu_thermal_protect(unsigned int limited_power)
|
|
{
|
|
pr_notice(TSCPU_LOG_TAG "E_WF: %s doesn't exist\n", __func__);
|
|
}
|
|
#else
|
|
void __attribute__ ((weak))
|
|
mt_cpufreq_thermal_protect(unsigned int limited_power)
|
|
{
|
|
pr_notice(TSCPU_LOG_TAG "E_WF: %s doesn't exist\n", __func__);
|
|
}
|
|
#endif
|
|
|
|
|
|
void __attribute__ ((weak))
|
|
mt_gpufreq_thermal_protect(unsigned int limited_power)
|
|
{
|
|
pr_notice(TSCPU_LOG_TAG "E_WF: %s doesn't exist\n", __func__);
|
|
}
|
|
|
|
|
|
/*=============================================================
|
|
* Local function prototype
|
|
*=============================================================
|
|
*/
|
|
|
|
/*=============================================================
|
|
* Function definitions
|
|
*=============================================================
|
|
*/
|
|
int apthermolmt_register_user(struct apthermolmt_user *handle, char *log)
|
|
{
|
|
int i = 1;
|
|
|
|
if (!handle || !log)
|
|
return -1;
|
|
|
|
for (; i < AP_THERMO_LMT_MAX_USERS; i++) {
|
|
if (_users[i] == &_dummy) {
|
|
_users[i] = handle;
|
|
handle->log = log;
|
|
handle->cpu_limit = 0x7FFFFFFF;
|
|
handle->vpu_limit = 0x7FFFFFFF;
|
|
handle->mdla_limit = 0x7FFFFFFF;
|
|
handle->gpu_limit = 0x7FFFFFFF;
|
|
handle->ptr = &_users[i];
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
EXPORT_SYMBOL(apthermolmt_register_user);
|
|
|
|
int apthermolmt_unregister_user(struct apthermolmt_user *handle)
|
|
{
|
|
int i = 1;
|
|
|
|
if (!handle)
|
|
return -1;
|
|
|
|
for (; i < AP_THERMO_LMT_MAX_USERS; i++) {
|
|
if (handle->ptr == &_users[i]) {
|
|
_users[i] = &_dummy;
|
|
handle->ptr = NULL;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
EXPORT_SYMBOL(apthermolmt_unregister_user);
|
|
|
|
void apthermolmt_set_cpu_power_limit(
|
|
struct apthermolmt_user *handle, unsigned int limit)
|
|
{
|
|
unsigned int final_limit;
|
|
|
|
if (!handle || !(handle->ptr))
|
|
return;
|
|
|
|
/* decide min CPU limit */
|
|
handle->cpu_limit = limit;
|
|
|
|
mutex_lock(&apthermolmt_cpu_mutex);
|
|
|
|
#if AP_THERMO_LMT_MAX_USERS == 5
|
|
final_limit = MIN(_users[0]->cpu_limit, _users[1]->cpu_limit);
|
|
final_limit = MIN(final_limit, _users[2]->cpu_limit);
|
|
final_limit = MIN(final_limit, _users[3]->cpu_limit);
|
|
final_limit = MIN(final_limit, _users[4]->cpu_limit);
|
|
#else
|
|
#error "handle this!"
|
|
#endif
|
|
|
|
apthermolmt_prev_cpu_pwr_lim = apthermolmt_curr_cpu_pwr_lim;
|
|
apthermolmt_curr_cpu_pwr_lim = final_limit;
|
|
|
|
if (apthermolmt_prev_cpu_pwr_lim != apthermolmt_curr_cpu_pwr_lim) {
|
|
unsigned long timeout;
|
|
|
|
tscpu_dprintk("%s %u\n", __func__, final_limit);
|
|
#if (CONFIG_THERMAL_AEE_RR_REC == 1)
|
|
aee_rr_rec_thermal_ATM_status(ATM_CPULIMIT);
|
|
#endif
|
|
timeout = jiffies + msecs_to_jiffies(100);
|
|
#if defined(ATM_USES_PPM)
|
|
mt_ppm_cpu_thermal_protect((final_limit != 0x7FFFFFFF) ?
|
|
final_limit : 0);
|
|
#else
|
|
mt_cpufreq_thermal_protect((final_limit != 0x7FFFFFFF) ?
|
|
final_limit : 0);
|
|
#endif
|
|
if (time_after(jiffies, timeout))
|
|
tscpu_warn("blocked in cpu limit %u over 100ms\n",
|
|
apthermolmt_curr_cpu_pwr_lim);
|
|
}
|
|
|
|
mutex_unlock(&apthermolmt_cpu_mutex);
|
|
}
|
|
EXPORT_SYMBOL(apthermolmt_set_cpu_power_limit);
|
|
|
|
#if defined(THERMAL_VPU_SUPPORT)
|
|
void apthermolmt_set_vpu_power_limit(
|
|
struct apthermolmt_user *handle, unsigned int limit)
|
|
{
|
|
unsigned int final_limit;
|
|
|
|
if (!handle || !(handle->ptr))
|
|
return;
|
|
|
|
/* decide min VPU limit */
|
|
handle->vpu_limit = limit;
|
|
|
|
#if AP_THERMO_LMT_MAX_USERS == 5
|
|
final_limit = MIN(_users[0]->vpu_limit, _users[1]->vpu_limit);
|
|
final_limit = MIN(final_limit, _users[2]->vpu_limit);
|
|
final_limit = MIN(final_limit, _users[3]->vpu_limit);
|
|
final_limit = MIN(final_limit, _users[4]->vpu_limit);
|
|
#else
|
|
#error "handle this!"
|
|
#endif
|
|
|
|
apthermolmt_prev_vpu_pwr_lim = apthermolmt_curr_vpu_pwr_lim;
|
|
apthermolmt_curr_vpu_pwr_lim = final_limit;
|
|
|
|
if (apthermolmt_prev_vpu_pwr_lim != apthermolmt_curr_vpu_pwr_lim) {
|
|
int opp = 0;
|
|
|
|
if (final_limit != 0x7FFFFFFF) {
|
|
#ifdef CONFIG_MTK_APUSYS_SUPPORT
|
|
for (opp = 0; opp < APU_OPP_NUM - 1; opp++) {
|
|
if (final_limit >= vpu_power_table[opp].power)
|
|
break;
|
|
}
|
|
apusys_thermal_en_throttle_cb(VPU0, opp);
|
|
} else
|
|
apusys_thermal_dis_throttle_cb(VPU0);
|
|
#else
|
|
for (opp = 0; opp < VPU_OPP_NUM - 1; opp++) {
|
|
if (final_limit >= vpu_power_table[opp].power)
|
|
break;
|
|
}
|
|
vpu_thermal_en_throttle_cb(0xff, opp);
|
|
} else
|
|
vpu_thermal_dis_throttle_cb();
|
|
#endif
|
|
tscpu_dprintk("%s %u\n", __func__, final_limit);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(apthermolmt_set_vpu_power_limit);
|
|
#endif
|
|
|
|
#if defined(THERMAL_MDLA_SUPPORT)
|
|
void apthermolmt_set_mdla_power_limit(
|
|
struct apthermolmt_user *handle, unsigned int limit)
|
|
{
|
|
unsigned int final_limit;
|
|
|
|
if (!handle || !(handle->ptr))
|
|
return;
|
|
|
|
/* decide min MDLA limit */
|
|
handle->mdla_limit = limit;
|
|
|
|
#if AP_THERMO_LMT_MAX_USERS == 5
|
|
final_limit = MIN(_users[0]->mdla_limit, _users[1]->mdla_limit);
|
|
final_limit = MIN(final_limit, _users[2]->mdla_limit);
|
|
final_limit = MIN(final_limit, _users[3]->mdla_limit);
|
|
final_limit = MIN(final_limit, _users[4]->mdla_limit);
|
|
#else
|
|
#error "handle this!"
|
|
#endif
|
|
|
|
apthermolmt_prev_mdla_pwr_lim = apthermolmt_curr_mdla_pwr_lim;
|
|
apthermolmt_curr_mdla_pwr_lim = final_limit;
|
|
|
|
if (apthermolmt_prev_mdla_pwr_lim != apthermolmt_curr_mdla_pwr_lim) {
|
|
int opp = 0;
|
|
if (final_limit != 0x7FFFFFFF) {
|
|
#ifdef CONFIG_MTK_APUSYS_SUPPORT
|
|
for (opp = 0; opp < APU_OPP_NUM - 1; opp++) {
|
|
if (final_limit >= mdla_power_table[opp].power)
|
|
break;
|
|
}
|
|
apusys_thermal_en_throttle_cb(MDLA0, opp);
|
|
} else
|
|
apusys_thermal_dis_throttle_cb(MDLA0);
|
|
#else
|
|
for (opp = 0; opp < MDLA_OPP_NUM - 1; opp++) {
|
|
if (final_limit >= mdla_power_table[opp].power)
|
|
break;
|
|
}
|
|
mdla_thermal_en_throttle_cb(0xff, opp);
|
|
} else
|
|
mdla_thermal_dis_throttle_cb();
|
|
#endif
|
|
tscpu_dprintk("%s %u\n", __func__, final_limit);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(apthermolmt_set_mdla_power_limit);
|
|
#endif
|
|
|
|
void apthermolmt_set_gpu_power_limit(
|
|
struct apthermolmt_user *handle, unsigned int limit)
|
|
{
|
|
unsigned int final_limit;
|
|
|
|
if (!handle || !(handle->ptr))
|
|
return;
|
|
|
|
/* decide min GPU limit */
|
|
handle->gpu_limit = limit;
|
|
|
|
#if AP_THERMO_LMT_MAX_USERS == 5
|
|
final_limit = MIN(_users[0]->gpu_limit, _users[1]->gpu_limit);
|
|
final_limit = MIN(final_limit, _users[2]->gpu_limit);
|
|
final_limit = MIN(final_limit, _users[3]->gpu_limit);
|
|
final_limit = MIN(final_limit, _users[4]->gpu_limit);
|
|
#else
|
|
#error "handle this!"
|
|
#endif
|
|
|
|
apthermolmt_prev_gpu_pwr_lim = apthermolmt_curr_gpu_pwr_lim;
|
|
apthermolmt_curr_gpu_pwr_lim = final_limit;
|
|
|
|
if (apthermolmt_prev_gpu_pwr_lim != apthermolmt_curr_gpu_pwr_lim) {
|
|
tscpu_dprintk("%s %d\n", __func__, final_limit);
|
|
#if (CONFIG_THERMAL_AEE_RR_REC == 1)
|
|
aee_rr_rec_thermal_ATM_status(ATM_GPULIMIT);
|
|
#endif
|
|
mt_gpufreq_thermal_protect((final_limit != 0x7FFFFFFF) ?
|
|
final_limit : 0);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(apthermolmt_set_gpu_power_limit);
|
|
|
|
void apthermolmt_set_general_cpu_power_limit(unsigned int limit)
|
|
{
|
|
gp_prev_cpu_pwr_limit = gp_curr_cpu_pwr_limit;
|
|
gp_curr_cpu_pwr_limit = (limit != 0) ? limit : 0x7FFFFFFF;
|
|
|
|
if (gp_prev_cpu_pwr_limit != gp_curr_cpu_pwr_limit) {
|
|
tscpu_warn("%s %d\n", __func__, gp_curr_cpu_pwr_limit);
|
|
|
|
apthermolmt_set_cpu_power_limit(&_gp, gp_curr_cpu_pwr_limit);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(apthermolmt_set_general_cpu_power_limit);
|
|
|
|
void apthermolmt_set_general_gpu_power_limit(unsigned int limit)
|
|
{
|
|
gp_prev_gpu_pwr_limit = gp_curr_gpu_pwr_limit;
|
|
gp_curr_gpu_pwr_limit = (limit != 0) ? limit : 0x7FFFFFFF;
|
|
|
|
if (gp_prev_gpu_pwr_limit != gp_curr_gpu_pwr_limit) {
|
|
tscpu_warn("%s %d\n", __func__, gp_curr_gpu_pwr_limit);
|
|
|
|
apthermolmt_set_gpu_power_limit(&_gp, gp_curr_gpu_pwr_limit);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(apthermolmt_set_general_gpu_power_limit);
|
|
|
|
unsigned int apthermolmt_get_cpu_power_limit(void)
|
|
{
|
|
return apthermolmt_curr_cpu_pwr_lim;
|
|
}
|
|
EXPORT_SYMBOL(apthermolmt_get_cpu_power_limit);
|
|
|
|
unsigned int apthermolmt_get_cpu_min_power(void)
|
|
{
|
|
return tscpu_get_min_cpu_pwr();
|
|
}
|
|
EXPORT_SYMBOL(apthermolmt_get_cpu_min_power);
|
|
|
|
unsigned int apthermolmt_get_gpu_power_limit(void)
|
|
{
|
|
return apthermolmt_curr_gpu_pwr_lim;
|
|
}
|
|
EXPORT_SYMBOL(apthermolmt_get_gpu_power_limit);
|
|
|
|
unsigned int apthermolmt_get_gpu_min_power(void)
|
|
{
|
|
return tscpu_get_min_gpu_pwr();
|
|
}
|
|
EXPORT_SYMBOL(apthermolmt_get_gpu_min_power);
|
|
|
|
#if defined(THERMAL_VPU_SUPPORT)
|
|
unsigned int apthermolmt_get_vpu_power_limit(void)
|
|
{
|
|
return apthermolmt_curr_vpu_pwr_lim;
|
|
}
|
|
EXPORT_SYMBOL(apthermolmt_get_vpu_power_limit);
|
|
|
|
unsigned int apthermolmt_get_vpu_min_power(void)
|
|
{
|
|
return tscpu_get_min_vpu_pwr();
|
|
}
|
|
EXPORT_SYMBOL(apthermolmt_get_vpu_min_power);
|
|
#endif
|
|
|
|
#if defined(THERMAL_MDLA_SUPPORT)
|
|
unsigned int apthermolmt_get_mdla_power_limit(void)
|
|
{
|
|
return apthermolmt_curr_mdla_pwr_lim;
|
|
}
|
|
EXPORT_SYMBOL(apthermolmt_get_mdla_power_limit);
|
|
|
|
unsigned int apthermolmt_get_mdla_min_power(void)
|
|
{
|
|
return tscpu_get_min_mdla_pwr();
|
|
}
|
|
EXPORT_SYMBOL(apthermolmt_get_mdla_min_power);
|
|
#endif
|
|
|