unplugged-kernel/drivers/misc/mediatek/thermal/common/mtk_thermal_platform.c

547 lines
13 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/uaccess.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/dmi.h>
#include <linux/acpi.h>
#include <linux/thermal.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
#include <linux/err.h>
#include <linux/syscalls.h>
#include <linux/time.h>
#include <linux/string.h>
#include <linux/mutex.h>
#include <linux/bug.h>
#include <linux/workqueue.h>
#include <mach/mtk_thermal.h>
#include <mt-plat/aee.h>
#include <mt-plat/mtk_thermal_platform.h>
#include <tscpu_settings.h>
/* ************************************ */
/* Definition */
/* ************************************ */
/* Number of CPU CORE */
#define NUMBER_OF_CORE (8)
/* This function pointer is for GPU LKM to register
* a function to get GPU loading.
*/
unsigned long (*mtk_thermal_get_gpu_loading_fp)(void) = NULL;
EXPORT_SYMBOL(mtk_thermal_get_gpu_loading_fp);
bool __attribute__ ((weak))
mtk_get_gpu_loading(unsigned int *pLoading)
{
#ifdef CONFIG_MTK_GPU_SUPPORT
pr_notice("E_WF: %s doesn't exist\n", __func__);
#endif
return 0;
}
int __attribute__ ((weak))
force_get_tbat(void)
{
pr_notice("E_WF: %s doesn't exist\n", __func__);
return 30;
}
unsigned int __attribute__ ((weak))
mt_gpufreq_get_cur_freq(void)
{
return 0;
}
/* ************************************ */
/* Global Variable */
/* ************************************ */
static bool enable_ThermalMonitor;
static DEFINE_MUTEX(MTM_SYSINFO_LOCK);
/* ************************************ */
/* Macro */
/* ************************************ */
#define THRML_LOG(fmt, args...) \
do { \
if (enable_ThermalMonitor)\
pr_notice("THERMAL/PLATFORM" fmt, ##args); \
} while (0)
#define THRML_ERROR_LOG(fmt, args...) \
pr_notice("THERMAL/PLATFORM" fmt, ##args)
/* ************************************ */
/* Define */
/* ************************************ */
/* ********************************************* */
/* For get_sys_cpu_usage_info_ex() */
/* ********************************************* */
#define CPU_USAGE_CURRENT_FIELD (0)
#define CPU_USAGE_SAVE_FIELD (1)
#define CPU_USAGE_FRAME_FIELD (2)
struct cpu_index_st {
unsigned long u[3];
unsigned long s[3];
unsigned long n[3];
unsigned long i[3];
unsigned long w[3];
unsigned long q[3];
unsigned long sq[3];
unsigned long tot_frme;
unsigned long tz;
int usage;
int freq;
};
struct gpu_index_st {
int usage;
int freq;
};
#define NO_CPU_CORES (TZCPU_NO_CPU_CORES)
/* /< 4-Core is maximum */
static struct cpu_index_st cpu_index_list[NO_CPU_CORES];
static int cpufreqs[NO_CPU_CORES];
static int cpuloadings[NO_CPU_CORES];
#define SEEK_BUFF(x, c) \
do { \
while (*x != c)\
x++; \
x++; \
} while (0)
#define TRIMz_ex(tz, x) (tz = (unsigned long long)(x))
/* ********************************************* */
/* CPU Index */
/* ********************************************* */
#include <linux/kernel_stat.h>
#include <linux/cpumask.h>
#include <linux/sched.h>
#include <linux/tick.h>
#include <linux/time.h>
#ifdef arch_idle_time
static cputime64_t get_idle_time(int cpu)
{
cputime64_t idle;
idle = kcpustat_cpu(cpu).cpustat[CPUTIME_IDLE];
if (cpu_online(cpu) && !nr_iowait_cpu(cpu))
idle += arch_idle_time(cpu);
return idle;
}
static cputime64_t get_iowait_time(int cpu)
{
cputime64_t iowait;
iowait = kcpustat_cpu(cpu).cpustat[CPUTIME_IOWAIT];
if (cpu_online(cpu) && nr_iowait_cpu(cpu))
iowait += arch_idle_time(cpu);
return iowait;
}
#else
static u64 get_idle_time(int cpu)
{
u64 idle, idle_time = -1ULL;
if (cpu_online(cpu))
idle_time = get_cpu_idle_time_us(cpu, NULL);
if (idle_time == -1ULL)
/* !NO_HZ or cpu offline so we can rely on cpustat.idle */
idle = kcpustat_cpu(cpu).cpustat[CPUTIME_IDLE];
else
idle = idle_time * NSEC_PER_USEC;
return idle;
}
static u64 get_iowait_time(int cpu)
{
u64 iowait, iowait_time = -1ULL;
if (cpu_online(cpu))
iowait_time = get_cpu_iowait_time_us(cpu, NULL);
if (iowait_time == -1ULL)
/* !NO_HZ or cpu offline so we can rely on cpustat.iowait */
iowait = kcpustat_cpu(cpu).cpustat[CPUTIME_IOWAIT];
else
iowait = iowait_time * NSEC_PER_USEC;
return iowait;
}
#endif
static int get_sys_cpu_usage_info_ex(void)
{
int nCoreIndex = 0, i;
for (i = 0; i < NO_CPU_CORES; i++)
cpuloadings[i] = 0;
for_each_online_cpu(nCoreIndex) {
if (nCoreIndex >= NO_CPU_CORES) {
#ifdef CONFIG_MTK_AEE_FEATURE
aee_kernel_warning("thermal",
"nCoreIndex %d over NO_CPU_CORES %d\n",
nCoreIndex, NO_CPU_CORES);
#endif
return 0;
}
/* Get CPU Info */
cpu_index_list[nCoreIndex].u[CPU_USAGE_CURRENT_FIELD] =
kcpustat_cpu(nCoreIndex).cpustat[CPUTIME_USER];
cpu_index_list[nCoreIndex].n[CPU_USAGE_CURRENT_FIELD] =
kcpustat_cpu(nCoreIndex).cpustat[CPUTIME_NICE];
cpu_index_list[nCoreIndex].s[CPU_USAGE_CURRENT_FIELD] =
kcpustat_cpu(nCoreIndex).cpustat[CPUTIME_SYSTEM];
cpu_index_list[nCoreIndex].i[CPU_USAGE_CURRENT_FIELD] =
get_idle_time(nCoreIndex);
cpu_index_list[nCoreIndex].w[CPU_USAGE_CURRENT_FIELD] =
get_iowait_time(nCoreIndex);
cpu_index_list[nCoreIndex].q[CPU_USAGE_CURRENT_FIELD] =
kcpustat_cpu(nCoreIndex).cpustat[CPUTIME_IRQ];
cpu_index_list[nCoreIndex].sq[CPU_USAGE_CURRENT_FIELD] =
kcpustat_cpu(nCoreIndex).cpustat[CPUTIME_SOFTIRQ];
/* Frame */
cpu_index_list[nCoreIndex].u[CPU_USAGE_FRAME_FIELD] =
cpu_index_list[nCoreIndex].u[CPU_USAGE_CURRENT_FIELD] -
cpu_index_list[nCoreIndex].u[CPU_USAGE_SAVE_FIELD];
cpu_index_list[nCoreIndex].n[CPU_USAGE_FRAME_FIELD] =
cpu_index_list[nCoreIndex].n[CPU_USAGE_CURRENT_FIELD] -
cpu_index_list[nCoreIndex].n[CPU_USAGE_SAVE_FIELD];
cpu_index_list[nCoreIndex].s[CPU_USAGE_FRAME_FIELD] =
cpu_index_list[nCoreIndex].s[CPU_USAGE_CURRENT_FIELD] -
cpu_index_list[nCoreIndex].s[CPU_USAGE_SAVE_FIELD];
cpu_index_list[nCoreIndex].i[CPU_USAGE_FRAME_FIELD] =
TRIMz_ex(cpu_index_list[nCoreIndex].tz,
(cpu_index_list[nCoreIndex].i[CPU_USAGE_CURRENT_FIELD] -
cpu_index_list[nCoreIndex].i[CPU_USAGE_SAVE_FIELD]));
cpu_index_list[nCoreIndex].w[CPU_USAGE_FRAME_FIELD] =
cpu_index_list[nCoreIndex].w[CPU_USAGE_CURRENT_FIELD] -
cpu_index_list[nCoreIndex].w[CPU_USAGE_SAVE_FIELD];
cpu_index_list[nCoreIndex].q[CPU_USAGE_FRAME_FIELD] =
cpu_index_list[nCoreIndex].q[CPU_USAGE_CURRENT_FIELD] -
cpu_index_list[nCoreIndex].q[CPU_USAGE_SAVE_FIELD];
cpu_index_list[nCoreIndex].sq[CPU_USAGE_FRAME_FIELD] =
cpu_index_list[nCoreIndex].sq[CPU_USAGE_CURRENT_FIELD] -
cpu_index_list[nCoreIndex].sq[CPU_USAGE_SAVE_FIELD];
/* Total Frame */
cpu_index_list[nCoreIndex].tot_frme =
cpu_index_list[nCoreIndex].u[CPU_USAGE_FRAME_FIELD] +
cpu_index_list[nCoreIndex].n[CPU_USAGE_FRAME_FIELD] +
cpu_index_list[nCoreIndex].s[CPU_USAGE_FRAME_FIELD] +
cpu_index_list[nCoreIndex].i[CPU_USAGE_FRAME_FIELD] +
cpu_index_list[nCoreIndex].w[CPU_USAGE_FRAME_FIELD] +
cpu_index_list[nCoreIndex].q[CPU_USAGE_FRAME_FIELD] +
cpu_index_list[nCoreIndex].sq[CPU_USAGE_FRAME_FIELD];
/* CPU Usage */
if (cpu_index_list[nCoreIndex].tot_frme > 0) {
cpuloadings[nCoreIndex] =
(100 -
(((int)cpu_index_list[nCoreIndex]
.i[CPU_USAGE_FRAME_FIELD] * 100) /
(int)cpu_index_list[nCoreIndex].tot_frme));
} else {
/* CPU unplug case */
cpuloadings[nCoreIndex] = 0;
}
cpu_index_list[nCoreIndex].u[CPU_USAGE_SAVE_FIELD] =
cpu_index_list[nCoreIndex].u[CPU_USAGE_CURRENT_FIELD];
cpu_index_list[nCoreIndex].n[CPU_USAGE_SAVE_FIELD] =
cpu_index_list[nCoreIndex].n[CPU_USAGE_CURRENT_FIELD];
cpu_index_list[nCoreIndex].s[CPU_USAGE_SAVE_FIELD] =
cpu_index_list[nCoreIndex].s[CPU_USAGE_CURRENT_FIELD];
cpu_index_list[nCoreIndex].i[CPU_USAGE_SAVE_FIELD] =
cpu_index_list[nCoreIndex].i[CPU_USAGE_CURRENT_FIELD];
cpu_index_list[nCoreIndex].w[CPU_USAGE_SAVE_FIELD] =
cpu_index_list[nCoreIndex].w[CPU_USAGE_CURRENT_FIELD];
cpu_index_list[nCoreIndex].q[CPU_USAGE_SAVE_FIELD] =
cpu_index_list[nCoreIndex].q[CPU_USAGE_CURRENT_FIELD];
cpu_index_list[nCoreIndex].sq[CPU_USAGE_SAVE_FIELD] =
cpu_index_list[nCoreIndex].sq[CPU_USAGE_CURRENT_FIELD];
THRML_LOG("CPU%d Frame:%lu USAGE:%d\n", nCoreIndex,
cpu_index_list[nCoreIndex].tot_frme,
cpuloadings[nCoreIndex]);
for (i = 0; i < 3; i++) {
THRML_LOG(
"Index %d [u:%lu] [n:%lu] [s:%lu] [i:%lu] [w:%lu] [q:%lu] [sq:%lu]\n",
i, cpu_index_list[nCoreIndex].u[i],
cpu_index_list[nCoreIndex].n[i],
cpu_index_list[nCoreIndex].s[i],
cpu_index_list[nCoreIndex].i[i],
cpu_index_list[nCoreIndex].w[i],
cpu_index_list[nCoreIndex].q[i],
cpu_index_list[nCoreIndex].sq[i]);
}
}
return 0;
}
static bool dmips_limit_warned;
static int check_dmips_limit;
#include <linux/cpufreq.h>
static int get_sys_all_cpu_freq_info(void)
{
int i;
int cpu_total_dmips = 0;
for (i = 0; i < NO_CPU_CORES; i++) {
cpufreqs[i] = cpufreq_quick_get(i) / 1000; /* MHz */
cpu_total_dmips += cpufreqs[i];
}
cpu_total_dmips /= 1000;
/* TODO: think a way to easy start and stop, and start for only once */
if (check_dmips_limit == 1) {
if (cpu_total_dmips > mtktscpu_limited_dmips) {
THRML_ERROR_LOG("cpu %d over limit %d\n",
cpu_total_dmips,
mtktscpu_limited_dmips);
if (dmips_limit_warned == false) {
#ifdef CONFIG_MTK_AEE_FEATURE
aee_kernel_warning("thermal",
"cpu %d over limit %d\n",
cpu_total_dmips,
mtktscpu_limited_dmips);
#endif
dmips_limit_warned = true;
}
}
}
return 0;
}
static int mtk_thermal_validation_rd(struct seq_file *m, void *v)
{
seq_printf(m, "%d\n", check_dmips_limit);
return 0;
}
static ssize_t mtk_thermal_validation_wr
(struct file *file, const char __user *buffer, size_t count, loff_t *data)
{
char desc[32];
int check_switch;
int len = 0;
len = (count < (sizeof(desc) - 1)) ? count : (sizeof(desc) - 1);
if (copy_from_user(desc, buffer, len))
return 0;
desc[len] = '\0';
if (kstrtoint(desc, 10, &check_switch) == 0) {
if (check_switch == 1) {
dmips_limit_warned = false;
check_dmips_limit = check_switch;
} else if (check_switch == 0) {
check_dmips_limit = check_switch;
}
return count;
}
THRML_ERROR_LOG("[%s] bad argument\n", __func__);
return -EINVAL;
}
static int mtk_thermal_validation_open(struct inode *inode, struct file *file)
{
return single_open(file, mtk_thermal_validation_rd, NULL);
}
static const struct file_operations mtk_thermal_validation_fops = {
.owner = THIS_MODULE,
.open = mtk_thermal_validation_open,
.read = seq_read,
.llseek = seq_lseek,
.write = mtk_thermal_validation_wr,
.release = single_release,
};
/* Init */
static int __init mtk_thermal_platform_init(void)
{
int err = 0;
struct proc_dir_entry *entry;
entry = proc_create("driver/tm_validation", 0644, NULL,
&mtk_thermal_validation_fops);
if (!entry) {
THRML_ERROR_LOG(
"[%s] Can not create /proc/driver/tm_validation\n",
__func__);
}
return err;
}
/* Exit */
static void __exit mtk_thermal_platform_exit(void)
{
}
int mtk_thermal_get_cpu_info(int *nocores, int **cpufreq, int **cpuloading)
{
/* ****************** */
/* CPU Usage */
/* ****************** */
mutex_lock(&MTM_SYSINFO_LOCK);
/* Read CPU Usage Information */
get_sys_cpu_usage_info_ex();
get_sys_all_cpu_freq_info();
mutex_unlock(&MTM_SYSINFO_LOCK);
if (nocores)
*nocores = NO_CPU_CORES;
if (cpufreq)
*cpufreq = cpufreqs;
if (cpuloading)
*cpuloading = cpuloadings;
return 0;
}
EXPORT_SYMBOL(mtk_thermal_get_cpu_info);
#define NO_GPU_CORES (1)
static int gpufreqs[NO_GPU_CORES];
static int gpuloadings[NO_GPU_CORES];
int mtk_thermal_get_gpu_info(int *nocores, int **gpufreq, int **gpuloading)
{
/* ****************** */
/* GPU Index */
/* ****************** */
THRML_LOG("[%s]\n", __func__);
if (nocores)
*nocores = NO_GPU_CORES;
if (gpufreq) {
gpufreqs[0] = mt_gpufreq_get_cur_freq() / 1000; /* MHz */
*gpufreq = gpufreqs;
}
if (gpuloading) {
unsigned int rd_gpu_loading = 0;
if (mtk_get_gpu_loading(&rd_gpu_loading)) {
gpuloadings[0] = (int)rd_gpu_loading;
*gpuloading = gpuloadings;
}
}
return 0;
}
EXPORT_SYMBOL(mtk_thermal_get_gpu_info);
/* ********************************************* */
/* Get Extra Info */
/* ********************************************* */
enum {
/* TXPWR_MD1 = 0,
* TXPWR_MD2 =1,
* RFTEMP_2G_MD1 =2,
* RFTEMP_2G_MD2 = 3,
* RFTEMP_3G_MD1 = 4,
* RFTEMP_3G_MD2 = 5,
*/
WiFi_TP = 6,
Mobile_TP = 7,
NO_EXTRA_THERMAL_ATTR
};
int mtk_thermal_force_get_batt_temp(void)
{
int ret = 0;
ret = force_get_tbat();
return ret;
}
EXPORT_SYMBOL(mtk_thermal_force_get_batt_temp);
static unsigned int _thermal_scen;
unsigned int mtk_thermal_set_user_scenarios(unsigned int mask)
{
/* only one scen is handled now... */
if ((mask & MTK_THERMAL_SCEN_CALL)) {
/* make mtk_ts_cpu.c aware of call scenario */
set_taklking_flag(true);
_thermal_scen |= (unsigned int)MTK_THERMAL_SCEN_CALL;
}
return _thermal_scen;
}
EXPORT_SYMBOL(mtk_thermal_set_user_scenarios);
unsigned int mtk_thermal_clear_user_scenarios(unsigned int mask)
{
/* only one scen is handled now... */
if ((mask & MTK_THERMAL_SCEN_CALL)) {
/* make mtk_ts_cpu.c aware of call scenario */
set_taklking_flag(false);
_thermal_scen &= ~((unsigned int)MTK_THERMAL_SCEN_CALL);
}
return _thermal_scen;
}
EXPORT_SYMBOL(mtk_thermal_clear_user_scenarios);
module_init(mtk_thermal_platform_init);
module_exit(mtk_thermal_platform_exit);