unplugged-kernel/drivers/misc/mediatek/thermal/mtk_thermal_monitor.c

2093 lines
52 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifdef pr_fmt
#undef pr_fmt
#endif
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/uaccess.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/printk.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 <linux/slab.h>
#include <mt-plat/mtk_thermal_monitor.h>
#include <mt-plat/mtk_thermal_platform.h>
#include <linux/uidgid.h>
/* ************************************ */
/* Definition */
/* ************************************ */
/**
* \def MTK_THERMAL_MONITOR_MEASURE_GET_TEMP_OVERHEAD
* 1 to enable
* 0 to disable
*/
#define MTK_THERMAL_MONITOR_MEASURE_GET_TEMP_OVERHEAD (0)
#define MTK_THERMAL_MONITOR_COOLER_MAX_EXTRA_CONDITIONS (3)
#define MTK_THERMAL_MONITOR_CONDITIONAL_COOLING (1)
/**
* \def MTK_MAX_STEP_SMA_LEN
* If not defined as 1, multi-step temperature SMA len is supported.
* For example, MTK_MAX_STEP_SMA_LEN is defined as 4.
* Users can set 4 different SMA len for a thermal zone and assign
* a high threshold for each.
* SMA len in the next step is applied if temp of the TZ reaches high
* threshold.
* Represent this in a simple figure as below:
* -infinite HT(0)|<- sma_len(0) ->|HT(1)|<- sma_len(1) ->|HT(2)|<- sma_len(2)
* ->|HT(3)|<- sma_len(3)-> |+infinite HT(4)
* In temp range between HT(i) and HT(i+1), sma_len(i) is applied.
* HT(i) < HT(i+1), eq is not allowed since meaningless
* sma_len(i) in [1, 60]
*/
#define MAX_STEP_MA_LEN (4)
#define MSMA_MAX_HT (1000000)
#define MSMA_MIN_HT (-275000)
struct mtk_thermal_cooler_data {
struct thermal_zone_device *tz;
struct thermal_cooling_device_ops *ops;
struct thermal_cooling_device_ops_extra *ops_ext;
void *devdata;
int trip;
char conditions
[MTK_THERMAL_MONITOR_COOLER_MAX_EXTRA_CONDITIONS]
[THERMAL_NAME_LENGTH];
int *condition_last_value
[MTK_THERMAL_MONITOR_COOLER_MAX_EXTRA_CONDITIONS];
int threshold[MTK_THERMAL_MONITOR_COOLER_MAX_EXTRA_CONDITIONS];
int exit_threshold;
int id;
};
struct mtk_thermal_tz_data {
struct thermal_zone_device_ops *ops;
unsigned int ma_len; /* max 60 */
unsigned int ma_counter;
long ma[60];
#if (MAX_STEP_MA_LEN > 1)
unsigned int curr_idx_ma_len;
unsigned int ma_lens[MAX_STEP_MA_LEN];
long msma_ht[MAX_STEP_MA_LEN];
/**< multi-step moving avg. high threshold array. */
#endif
long fake_temp;
/* to store the Tfake, range from -275000 to MAX positive of int...
*-275000 is a special number to turn off Tfake
*/
struct mutex ma_lock; /* protect moving avg. vars... */
};
struct proc_dir_entry *mtk_thermal_get_proc_drv_therm_dir_entry(void);
static DEFINE_MUTEX(MTM_GET_TEMP_LOCK);
static int *tz_last_values[MTK_THERMAL_SENSOR_COUNT] = { NULL };
/* ************************************ */
/* Global Variable */
/* ************************************ */
struct thermal_zone_device_ops *g_SysinfoAttachOps;
static bool enable_ThermalMonitor;
static bool enable_ThermalMonitorXlog;
static int g_nStartRealTime;
/* lock by MTM_COOLER_PROC_DIR_LOCK */
static struct proc_dir_entry *proc_cooler_dir_entry;
/* lock by MTK_TZ_PROC_DIR_LOCK */
static struct proc_dir_entry *proc_tz_dir_entry;
static struct proc_dir_entry *proc_drv_therm_dir_entry;
/**
* write to nBattCurrentCnsmpt, nCPU0_usage,
* and nCPU1_usage are locked by MTM_SYSINFO_LOCK
*/
/* static int nModem_TxPower = -127; ///< Indicate invalid value */
/* For enabling time based thermal protection
* under phone call+AP suspend scenario.
*/
static int g_mtm_phone_call_ongoing;
static DEFINE_MUTEX(MTM_COOLER_LOCK);
static DEFINE_MUTEX(MTM_SYSINFO_LOCK);
static DEFINE_MUTEX(MTM_COOLER_PROC_DIR_LOCK);
static DEFINE_MUTEX(MTM_TZ_PROC_DIR_LOCK);
static DEFINE_MUTEX(MTM_DRV_THERM_PROC_DIR_LOCK);
static struct delayed_work _mtm_sysinfo_poll_queue;
static kuid_t uid = KUIDT_INIT(0);
static kgid_t gid = KGIDT_INIT(1000);
/* ************************************ */
/* Macro */
/* ************************************ */
#ifdef CONFIG_MTK_MT_LOGGER
#define THRML_STORAGE_LOG(msg_id, func_name, ...) \
do { \
if (unlikely(is_dump_mthermal()) && enable_ThermalMonitor) { \
AddThrmlTrace(msg_id, func_name, __VA_ARGS__); \
} \
} while (0)
#else
#define THRML_STORAGE_LOG(msg_id, func_name, ...)
#endif
#define THRML_LOG(fmt, args...) \
do { \
if (unlikely(enable_ThermalMonitorXlog)) { \
pr_notice("THERMAL/MONITOR " fmt, ##args); \
} \
} while (0)
#define THRML_ERROR_LOG(fmt, args...) pr_notice("THERMAL/MONITOR " fmt, ##args)
/* ************************************ */
/* Define */
/* ************************************ */
/* thermal_zone_device * sysinfo_monitor_register(int nPollingTime); */
/* int sysinfo_monitor_unregister(void); */
#define SYSINFO_ATTACH_DEV_NAME "mtktscpu"
/* ************************************ */
/* Thermal Monitor API */
/* ************************************ */
#if MTK_THERMAL_MONITOR_MEASURE_GET_TEMP_OVERHEAD
static long int _get_current_time_us(void)
{
struct timeval t;
do_gettimeofday(&t);
return (t.tv_sec & 0xFFF) * 1000000 + t.tv_usec;
}
#endif
static int mtk_thermal_get_tz_idx(char *type)
{
if (strncmp(type, "mtktscpu", 8) == 0)
return MTK_THERMAL_SENSOR_CPU;
else if (strncmp(type, "mtktsabb", 8) == 0)
return MTK_THERMAL_SENSOR_ABB;
else if (strncmp(type, "mtktspmic", 9) == 0)
return MTK_THERMAL_SENSOR_PMIC;
else if (strncmp(type, "mtktsbattery2", 13) == 0)
return MTK_THERMAL_SENSOR_BATTERY2;
else if (strncmp(type, "mtktsbattery", 12) == 0)
return MTK_THERMAL_SENSOR_BATTERY;
else if (strncmp(type, "mtktspa", 7) == 0)
return MTK_THERMAL_SENSOR_MD1;
else if (strncmp(type, "mtktstdpa", 9) == 0)
return MTK_THERMAL_SENSOR_MD2;
else if (strncmp(type, "mtktswmt", 8) == 0)
return MTK_THERMAL_SENSOR_WIFI;
else if (strncmp(type, "mtktsbuck", 9) == 0)
return MTK_THERMAL_SENSOR_BUCK;
else if (strncmp(type, "mtktsAP", 7) == 0)
return MTK_THERMAL_SENSOR_AP;
else if (strncmp(type, "mtktspcb1", 9) == 0)
return MTK_THERMAL_SENSOR_PCB1;
else if (strncmp(type, "mtktspcb2", 9) == 0)
return MTK_THERMAL_SENSOR_PCB2;
else if (strncmp(type, "mtktsskin", 9) == 0)
return MTK_THERMAL_SENSOR_SKIN;
else if (strncmp(type, "mtktsxtal", 9) == 0)
return MTK_THERMAL_SENSOR_XTAL;
else if (strncmp(type, "mtktsbtsmdpa", 12) == 0)
return MTK_THERMAL_SENSOR_MD_PA;
else if (strncmp(type, "mtktsbtsnrpa", 12) == 0)
return MTK_THERMAL_SENSOR_NR_PA;
else if (strncmp(type, "mtktsdctm", 9) == 0)
return MTK_THERMAL_SENSOR_DCTM;
else if (strncmp(type, "mtktscharger", 12) == 0)
return MTK_THERMAL_SENSOR_CHARGER;
return -1;
}
static struct proc_dir_entry *_get_proc_cooler_dir_entry(void)
{
mutex_lock(&MTM_COOLER_PROC_DIR_LOCK);
if (proc_cooler_dir_entry == NULL) {
proc_cooler_dir_entry = proc_mkdir("mtkcooler", NULL);
if (proc_cooler_dir_entry == NULL)
THRML_ERROR_LOG("%s mkdir /proc/mtkcooler failed\n",
__func__);
}
mutex_unlock(&MTM_COOLER_PROC_DIR_LOCK);
return proc_cooler_dir_entry;
}
static struct proc_dir_entry *_get_proc_tz_dir_entry(void)
{
mutex_lock(&MTM_TZ_PROC_DIR_LOCK);
if (proc_tz_dir_entry == NULL) {
proc_tz_dir_entry = proc_mkdir("mtktz", NULL);
if (proc_tz_dir_entry == NULL)
THRML_ERROR_LOG("%s mkdir /proc/mtktz failed\n",
__func__);
}
mutex_unlock(&MTM_TZ_PROC_DIR_LOCK);
return proc_tz_dir_entry;
}
static struct thermal_cooling_device_ops *recoveryClientCooler
(struct thermal_cooling_device *cdev, struct mtk_thermal_cooler_data **mcdata)
{
*mcdata = cdev->devdata;
cdev->devdata = (*mcdata)->devdata;
return (*mcdata)->ops;
}
/* Lookup List to get Client's Thermal Zone OPS */
static struct thermal_zone_device_ops *getClientZoneOps
(struct thermal_zone_device *zdev)
{
struct thermal_zone_device_ops *ret = NULL;
struct mtk_thermal_tz_data *tzdata;
if ((zdev == NULL) || (zdev->devdata == NULL)) {
WARN_ON_ONCE(1);
return NULL;
}
tzdata = zdev->devdata;
mutex_lock(&tzdata->ma_lock);
ret = tzdata->ops;
mutex_unlock(&tzdata->ma_lock);
return ret;
}
#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;
};
static struct cpu_index_st cpu_index_list[8]; /* /< 8-Core is maximum */
#define SEEK_BUFF(x, c) \
do { \
while (*x != c)\
x++; \
x++; \
} while (0)
#define TRIMz_ex(tz, x) ((tz = (unsigned long long)(x)) < 0 ? 0 : tz)
static int _mtm_interval;
static void _mtm_update_sysinfo(struct work_struct *work)
{
cancel_delayed_work(&_mtm_sysinfo_poll_queue);
if (_mtm_interval != 0)
queue_delayed_work(system_freezable_power_efficient_wq,
&_mtm_sysinfo_poll_queue,
msecs_to_jiffies(_mtm_interval));
}
static void _mtm_decide_new_delay(void)
{
int new_interval = 0;
if (true == enable_ThermalMonitor)
new_interval = 1000;
if (_mtm_interval == 0 && new_interval != 0) {
_mtm_interval = new_interval;
_mtm_update_sysinfo(NULL);
} else {
_mtm_interval = new_interval;
}
}
/* ************************************ */
/* Thermal Host Driver Interface */
/* ************************************ */
/* Read */
static int mtkthermal_read(struct seq_file *m, void *v)
{
seq_puts(m, "\r\n[Thermal Monitor debug flag]\r\n");
seq_puts(m, "=========================================\r\n");
seq_printf(m, "enable_ThermalMonitor = %d\r\n", enable_ThermalMonitor);
seq_printf(m, "enable_ThermalMonitorXlog = %d\r\n",
enable_ThermalMonitorXlog);
seq_printf(m, "g_nStartRealTime = %d\r\n", g_nStartRealTime);
THRML_LOG("%s enable_ThermalMonitor:%d\n", __func__,
enable_ThermalMonitor);
return 0;
}
/* Write */
static ssize_t mtkthermal_write
(struct file *file, const char __user *buffer, size_t count,
loff_t *data)
{
int len = 0, nCtrlCmd = 0, nReadTime = 0;
char desc[32];
len = (count < (sizeof(desc) - 1)) ? count : (sizeof(desc) - 1);
if (copy_from_user(desc, buffer, len))
return 0;
desc[len] = '\0';
if (sscanf(desc, "%d %d", &nCtrlCmd, &nReadTime) == 2) {
/* Bit 0; Enable Thermal Monitor. */
if ((nCtrlCmd >> 0) & 0x01) {
/* Reset Global CPU Info Variable */
memset(&cpu_index_list, 0x00, sizeof(cpu_index_list));
enable_ThermalMonitor = true;
} else {
enable_ThermalMonitor = false;
}
_mtm_decide_new_delay();
/* Bit 1: Enable Thermal Monitor xlog */
enable_ThermalMonitorXlog =
((nCtrlCmd >> 1) & 0x01) ? true : false;
/*
* Get Real Time from user input
* Format: hhmmss 113901=> 11:39:01
*/
g_nStartRealTime = nReadTime;
THRML_STORAGE_LOG(THRML_LOGGER_MSG_DEC_NUM, get_real_time,
"[realtime]", g_nStartRealTime);
THRML_ERROR_LOG(
"%s nCtrlCmd=%d enable_ThermalMonitor=%d g_nStartRealTime=%d\n",
__func__, nCtrlCmd, (int)enable_ThermalMonitor,
g_nStartRealTime);
return count;
}
if (kstrtoint(desc, 10, &nCtrlCmd) == 0) {
/* Bit 0; Enable Thermal Monitor. */
if ((nCtrlCmd >> 0) & 0x01) {
/* Reset Global CPU Info Variable */
memset(&cpu_index_list, 0x00, sizeof(cpu_index_list));
enable_ThermalMonitor = true;
} else {
enable_ThermalMonitor = false;
}
_mtm_decide_new_delay();
/* Bit 1: Enable Thermal Monitor xlog */
enable_ThermalMonitorXlog =
((nCtrlCmd >> 1) & 0x01) ? true : false;
THRML_ERROR_LOG("%s nCtrlCmd=%d enable_ThermalMonitor=%d\n",
__func__, nCtrlCmd, (int)enable_ThermalMonitor);
return count;
}
THRML_LOG("%s bad arg\n", __func__);
return -EINVAL;
}
static int mtkthermal_open(struct inode *inode, struct file *file)
{
return single_open(file, mtkthermal_read, NULL);
}
static const struct file_operations mtkthermal_fops = {
.owner = THIS_MODULE,
.open = mtkthermal_open,
.read = seq_read,
.llseek = seq_lseek,
.write = mtkthermal_write,
.release = single_release,
};
static int _mtkthermal_check_cooler_conditions
(struct mtk_thermal_cooler_data *cldata)
{
int ret = 0;
if (cldata != NULL) {
int i = 0;
for (; i < MTK_THERMAL_MONITOR_COOLER_MAX_EXTRA_CONDITIONS;
i++){
if (cldata->condition_last_value[i] == NULL) {
if (cldata->conditions[i][0] == 0x0)
ret++;
else if (strncmp(
cldata->conditions[i], "EXIT", 4) == 0)
ret++;
} else {
#if 1 /* [FIX ME] Special case for "condifion of MOBILE" */
if (strncmp(cldata->conditions[i], "MOBILE", 5)
== 0) {
if (*cldata->condition_last_value[i]
< cldata->threshold[i]){
THRML_LOG(
"%s MOBILE Condition=%s, last_value=%d, threshold=%d\n",
__func__, cldata->conditions[i],
(*cldata
->condition_last_value[i]),
cldata->threshold[i]);
ret++;
}
} else if (*cldata->condition_last_value[i]
> cldata->threshold[i])
ret++;
#else
if (*cldata->condition_last_value[i]
> cldata->threshold[i])
ret++;
#endif
}
}
}
return ret;
}
static void _mtkthermal_clear_cooler_conditions
(struct mtk_thermal_cooler_data *cldata)
{
int i = 0;
cldata->exit_threshold = 0;
for (; i < MTK_THERMAL_MONITOR_COOLER_MAX_EXTRA_CONDITIONS; i++) {
cldata->conditions[i][0] = 0x0;
cldata->condition_last_value[i] = NULL;
cldata->threshold[i] = 0;
}
_mtm_decide_new_delay();
}
static int _mtkthermal_is_cooler_conditions_valid(char *condition)
{
if (condition[0] == 0x0
|| mtk_thermal_get_tz_idx(condition) >= 0
|| strncmp(condition, "EXIT", strlen("EXIT")) == 0
|| strncmp(condition, "CPU0", strlen("CPU0")) == 0
|| strncmp(condition, "Wifi", strlen("Wifi")) == 0
|| strncmp(condition, "Mobile", strlen("Mobile")) == 0)
return 1;
return 0;
}
static int _mtkthermal_cooler_read(struct seq_file *m, void *v)
{
struct mtk_thermal_cooler_data *mcdata;
/**
* The format to print out
* <condition_name_1> <condition_value_1> <thershold_1> <state_1>
* ..
* <condition_name_n> <condition_value_n> <thershold_n> <state_n>
* PS: n is MTK_THERMAL_MONITOR_COOLER_MAX_EXTRA_CONDITIONS
*/
if (m->private == NULL) {
THRML_ERROR_LOG("%s null data\n", __func__);
} else {
int i = 0;
/* TODO: we may not need to lock here... */
mutex_lock(&MTM_COOLER_LOCK);
mcdata = (struct mtk_thermal_cooler_data *)m->private;
mutex_unlock(&MTM_COOLER_LOCK);
for (; i < MTK_THERMAL_MONITOR_COOLER_MAX_EXTRA_CONDITIONS;
i++) {
if (mcdata->conditions[i][0] == 0x0)
continue; /* no condition */
/* TODO: consider the case that tz is unregistered... */
seq_printf(m, "%s val=%d threshold=%d %s",
mcdata->conditions[i],
(NULL ==
mcdata->condition_last_value[i]) ? 0 :
*(mcdata->condition_last_value[i]),
mcdata->threshold[i],
(mcdata->condition_last_value[i] == NULL) ?
"error\n" : "\n");
}
}
return 0;
}
static ssize_t _mtkthermal_cooler_write
(struct file *file, const char __user *buffer, size_t count, loff_t *data)
{
int len = 0;
char desc[128];
struct mtk_thermal_cooler_data *mcdata;
char conditions
[MTK_THERMAL_MONITOR_COOLER_MAX_EXTRA_CONDITIONS]
[THERMAL_NAME_LENGTH] = {{0} };
int threshold[MTK_THERMAL_MONITOR_COOLER_MAX_EXTRA_CONDITIONS] = {0};
len = (count < (sizeof(desc) - 1)) ? count : (sizeof(desc) - 1);
if (copy_from_user(desc, buffer, len))
return 0;
desc[len] = '\0';
/**
* sscanf format
* <condition_1> <threshold_1> ... <condition_n> <threshold_n>
* <condition_i> is string format
* <threshold_i> is integer format
* n is MTK_THERMAL_MONITOR_COOLER_MAX_EXTRA_CONDITIONS
*/
/* TODO: we may not need to lock here... */
mutex_lock(&MTM_COOLER_LOCK);
mcdata = (struct mtk_thermal_cooler_data *)PDE_DATA(file_inode(file));
mutex_unlock(&MTM_COOLER_LOCK);
if (mcdata == NULL) {
THRML_ERROR_LOG("%s null data\n", __func__);
return -EINVAL;
}
/* WARNING: Modify here if
* MTK_THERMAL_MONITOR_COOLER_MAX_EXTRA_CONDITIONS
* is changed to other than 3
*/
#if (MTK_THERMAL_MONITOR_COOLER_MAX_EXTRA_CONDITIONS == 3)
if (sscanf(desc, "%19s %d %19s %d %19s %d",
&conditions[0][0], &threshold[0],
&conditions[1][0], &threshold[1],
&conditions[2][0], &threshold[2]) >= 2){
int i = 0;
_mtkthermal_clear_cooler_conditions(mcdata);
for (; i < MTK_THERMAL_MONITOR_COOLER_MAX_EXTRA_CONDITIONS;
i++) {
if (!_mtkthermal_is_cooler_conditions_valid(
conditions[i])) {
/* ignore invalid conditions */
THRML_ERROR_LOG(
"%s %s invalid condition: %s\n",
__func__, mcdata->tz->type,
conditions[i]);
continue;
}
strncpy(mcdata->conditions[i], conditions[i],
THERMAL_NAME_LENGTH);
mcdata->conditions[i][THERMAL_NAME_LENGTH - 1] = '\0';
mcdata->threshold[i] = threshold[i];
if (strncmp(mcdata->conditions[i], "EXIT", 4) == 0) {
mcdata->exit_threshold = mcdata->threshold[i];
} else {
/* normal thermal zones */
mcdata->condition_last_value[i] = NULL;
}
THRML_LOG("%s %s %d: %s %d %p %d.\n", __func__,
mcdata->tz->type, i,
&mcdata->conditions[i][0],
mcdata->conditions[i][0],
mcdata->condition_last_value[i],
mcdata->threshold[0]);
}
_mtm_decide_new_delay();
return count;
} else if (conditions[0][0] == 0x0) {
/* No condition */
_mtkthermal_clear_cooler_conditions(mcdata);
THRML_LOG("%s %s No condition!\n",
__func__, mcdata->tz->type);
return count;
}
#else
#error "Change correspondent part when changing MTK_THERMAL_MONITOR_ skip..."
/* Change correspondent part when changing
* MTK_THERMAL_MONITOR_COOLER_MAX_EXTRA_CONDITIONS!
*/
#endif
THRML_ERROR_LOG("%s %s bad arg: %s\n",
__func__, mcdata->tz->type, desc);
return -EINVAL;
}
static int _mtkthermal_cooler_open(struct inode *inode, struct file *file)
{
return single_open(file, _mtkthermal_cooler_read, PDE_DATA(inode));
}
static const struct file_operations _mtkthermal_cooler_fops = {
.owner = THIS_MODULE,
.open = _mtkthermal_cooler_open,
.read = seq_read,
.llseek = seq_lseek,
.write = _mtkthermal_cooler_write,
.release = single_release,
};
static int _mtkthermal_tz_read(struct seq_file *m, void *v)
{
struct thermal_zone_device *tz = NULL;
if (m->private == NULL) {
THRML_ERROR_LOG("%s null data\n", __func__);
} else {
tz = (struct thermal_zone_device *)m->private;
/* TODO: consider the case that tz is unregistered... */
seq_printf(m, "%d\n", tz->temperature);
{
struct mtk_thermal_tz_data *tzdata = NULL;
int ma_len = 0;
int fake_temp = 0;
tzdata = tz->devdata;
if (!tzdata)
WARN_ON_ONCE(1);
#if (MAX_STEP_MA_LEN > 1)
mutex_lock(&tzdata->ma_lock);
ma_len = tzdata->ma_len;
fake_temp = tzdata->fake_temp;
seq_printf(m, "ma_len=%d\n", ma_len);
seq_printf(m, "%d ", tzdata->ma_lens[0]);
{
int i = 1;
for (; i < MAX_STEP_MA_LEN; i++)
seq_printf(m, "(%ld,%d) ",
tzdata->msma_ht[i - 1],
tzdata->ma_lens[i]);
}
mutex_unlock(&tzdata->ma_lock);
seq_puts(m, "\n");
#else
mutex_lock(&tzdata->ma_lock);
ma_len = tzdata->ma_len;
fake_temp = tzdata->fake_temp;
mutex_unlock(&tzdata->ma_lock);
seq_printf(m, "ma_len=%d\n", ma_len);
#endif
if (-275000 < fake_temp) {
/* print Tfake only when fake_temp > -275000 */
seq_printf(m, "Tfake=%d\n", fake_temp);
}
}
}
return 0;
}
static ssize_t _mtkthermal_tz_write
(struct file *file, const char __user *buffer, size_t count, loff_t *data)
{
int len = 0;
char desc[128];
char trailing[128] = { 0 };
int check = 0;
struct thermal_zone_device *tz;
char arg_name[32] = { 0 };
int arg_val = 0;
len = (count < (sizeof(desc) - 1)) ? count : (sizeof(desc) - 1);
if (copy_from_user(desc, buffer, len))
return 0;
desc[len] = '\0';
tz = (struct thermal_zone_device *)PDE_DATA(file_inode(file));
if (tz == NULL) {
THRML_ERROR_LOG("%s null data\n", __func__);
return -EINVAL;
}
if (sscanf(desc, "%31s %d %127s", arg_name, &arg_val, trailing) >= 2) {
if ((strncmp(arg_name, "ma_len", 6) == 0)
&& (arg_val >= 1) && (arg_val <= 60)) {
struct mtk_thermal_tz_data *tzdata = NULL;
tzdata = tz->devdata;
if (!tzdata)
WARN_ON_ONCE(1);
/* THRML_ERROR_LOG(
* "%s trailing=%s\n", __func__, trailing);
*/
/* reset MA len and lock
*/
#if (MAX_STEP_MA_LEN > 1)
mutex_lock(&tzdata->ma_lock);
tzdata->ma_len = arg_val;
tzdata->ma_counter = 0;
tzdata->curr_idx_ma_len = 0;
tzdata->ma_lens[0] = arg_val;
tzdata->msma_ht[0] = MSMA_MAX_HT;
/* THRML_ERROR_LOG(
* "%s %s ma_len=%d.\n", __func__, tz->type,
* tzdata->ma_len);
*/
#if (MAX_STEP_MA_LEN == 4)
/* reset */
tzdata->msma_ht[1] = tzdata->msma_ht[2] =
tzdata->msma_ht[3] = MSMA_MAX_HT;
tzdata->ma_lens[1] = tzdata->ma_lens[2] =
tzdata->ma_lens[3] = 1;
check = sscanf(trailing, "%ld,%d;%ld,%d;%ld,%d;",
&tzdata->msma_ht[0], &tzdata->ma_lens[1],
&tzdata->msma_ht[1], &tzdata->ma_lens[2],
&tzdata->msma_ht[2], &tzdata->ma_lens[3]);
/* THRML_ERROR_LOG(
* "%s %s (%ld, %d), (%ld, %d), (%ld, %d)\n", __func__,
* tz->type, tzdata->msma_ht[0], tzdata->ma_lens[1],
* tzdata->msma_ht[1], tzdata->ma_lens[2],
* tzdata->msma_ht[2], tzdata->ma_lens[3]);
*/
#else
#error
#endif
mutex_unlock(&tzdata->ma_lock);
#else
mutex_lock(&tzdata->ma_lock);
tzdata->ma_len = arg_val;
tzdata->ma_counter = 0;
mutex_unlock(&tzdata->ma_lock);
THRML_ERROR_LOG("%s %s ma_len=%d.\n", __func__,
tz->type, tzdata->ma_len);
#endif
} else if ((strncmp(arg_name, "Tfake", 5) == 0)
&& (arg_val >= -275000)) {
/* only accept for[-275000, max positive value of int]*/
struct mtk_thermal_tz_data *tzdata = NULL;
tzdata = tz->devdata;
if (!tzdata)
WARN_ON_ONCE(1);
mutex_lock(&tzdata->ma_lock);
tzdata->fake_temp = (long)arg_val;
mutex_unlock(&tzdata->ma_lock);
THRML_ERROR_LOG("%s %s Tfake=%ld.\n", __func__,
tz->type, tzdata->fake_temp);
}
return count;
} else {
return -EINVAL;
}
}
static int _mtkthermal_tz_open(struct inode *inode, struct file *file)
{
return single_open(file, _mtkthermal_tz_read, PDE_DATA(inode));
}
static const struct file_operations _mtkthermal_tz_fops = {
.owner = THIS_MODULE,
.open = _mtkthermal_tz_open,
.read = seq_read,
.llseek = seq_lseek,
.write = _mtkthermal_tz_write,
.release = single_release,
};
#define MIN(_a_, _b_) ((_a_) < (_b_) ? (_a_) : (_b_))
/* No parameter check in this internal function */
static long _mtkthermal_update_and_get_sma
(struct mtk_thermal_tz_data *tzdata, long latest_val)
{
long ret = 0;
if (tzdata == NULL) {
WARN_ON_ONCE(1);
return latest_val;
}
mutex_lock(&tzdata->ma_lock);
/* Use Tfake if set... */
latest_val =
(-275000 < tzdata->fake_temp) ? tzdata->fake_temp : latest_val;
if (tzdata->ma_len == 1) {
ret = latest_val;
} else if (tzdata->ma_len > 1) {
int i = 0;
tzdata->ma[(tzdata->ma_counter) %
(tzdata->ma_len)] = latest_val;
tzdata->ma_counter++;
for (i = 0; i < MIN(tzdata->ma_counter, tzdata->ma_len); i++)
ret += tzdata->ma[i];
ret = ret / ((long)MIN(tzdata->ma_counter, tzdata->ma_len));
}
#if (MAX_STEP_MA_LEN > 1)
/*
* 2. Move to correct region if ma_counter == 1
* a. For (i=0;SMA >= high_threshold[i];i++) ;
* b. if (curr_idx_sma_len != i) {ma_counter = 0;
* ma_len = sma_len[curr_idx_sma_len = i]; }
* 3. Check if need to change region if ma_counter > 1
* a. if SMA >= high_threshold[curr_idx_sma_len]
* { Move upward: ma_counter = 0;
* ma_len = sma_len[++curr_idx_sma_len]; }
* b. else if curr_idx_sma_len >0
* && SMA < high_threshold[curr_idx_sma_len-1]
* { Move downward: ma_counter =0;
* ma_len = sma_len[--curr_idx_sma_len]; }
*/
if (tzdata->ma_counter == 1) {
int i = 0;
for (; ret >= tzdata->msma_ht[i]; i++)
;
if (tzdata->curr_idx_ma_len != i) {
tzdata->ma_counter = 0;
tzdata->ma_len =
tzdata->ma_lens[tzdata->curr_idx_ma_len = i];
THRML_LOG("%s 2b ma_len: %d curr_idx_ma_len: %d\n",
__func__, tzdata->ma_len, tzdata->curr_idx_ma_len);
}
} else {
if (ret >= tzdata->msma_ht[tzdata->curr_idx_ma_len]) {
tzdata->ma_counter = 0;
tzdata->ma_len =
tzdata->ma_lens[++(tzdata->curr_idx_ma_len)];
THRML_LOG("%s 3a ma_len: %d curr_idx_ma_len: %d\n",
__func__, tzdata->ma_len, tzdata->curr_idx_ma_len);
} else if (tzdata->curr_idx_ma_len > 0
&& ret < tzdata->msma_ht[tzdata->curr_idx_ma_len - 1]) {
tzdata->ma_counter = 0;
tzdata->ma_len =
tzdata->ma_lens[--(tzdata->curr_idx_ma_len)];
THRML_LOG("%s 3b ma_len: %d curr_idx_ma_len: %d\n",
__func__, tzdata->ma_len, tzdata->curr_idx_ma_len);
}
}
#endif
mutex_unlock(&tzdata->ma_lock);
return ret;
}
/**
* 0: means please do not show thermal limit in "Show CPU Usage" panel.
* 1: means show thermal limit and CPU temp only
* 2: means show all all tz temp besides thermal limit and CPU temp
*/
static unsigned int g_thermal_indicator_mode;
/**
* delay in milliseconds.
*/
static unsigned int g_thermal_indicator_delay;
/* Read */
static int _mtkthermal_indicator_read(struct seq_file *m, void *v)
{
seq_printf(m, "%d\n%d\n", g_thermal_indicator_mode,
g_thermal_indicator_delay);
return 0;
}
/* Write */
static ssize_t _mtkthermal_indicator_write
(struct file *file, const char __user *buffer, size_t count, loff_t *data)
{
int len = 0, thermal_indicator_mode = 0, thermal_indicator_delay = 0;
char desc[32];
len = (count < (sizeof(desc) - 1)) ? count : (sizeof(desc) - 1);
if (copy_from_user(desc, buffer, len))
return 0;
desc[len] = '\0';
if (sscanf(desc, "%d %d", &thermal_indicator_mode,
&thermal_indicator_delay) == 2) {
if ((thermal_indicator_mode >= 0)
&& (thermal_indicator_mode <= 3))
g_thermal_indicator_mode = thermal_indicator_mode;
g_thermal_indicator_delay = thermal_indicator_delay;
return count;
} else {
return 0;
}
}
static int _mtkthermal_indicator_open(struct inode *inode, struct file *file)
{
return single_open(file, _mtkthermal_indicator_read, NULL);
}
static const struct file_operations _mtkthermal_indicator_fops = {
.owner = THIS_MODULE,
.open = _mtkthermal_indicator_open,
.read = seq_read,
.llseek = seq_lseek,
.write = _mtkthermal_indicator_write,
.release = single_release,
};
/* Read */
static int _mtm_scen_call_read(struct seq_file *m, void *v)
{
seq_printf(m, "%d\n", g_mtm_phone_call_ongoing);
return 0;
}
/* Write */
static ssize_t _mtm_scen_call_write
(struct file *file, const char __user *buffer, size_t count, loff_t *data)
{
int len = 0, mtm_phone_call_ongoing = 0;
char desc[32];
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, &mtm_phone_call_ongoing) == 0) {
if ((mtm_phone_call_ongoing == 0)
|| (mtm_phone_call_ongoing == 1)) {
g_mtm_phone_call_ongoing = mtm_phone_call_ongoing;
if (mtm_phone_call_ongoing == 1)
mtk_thermal_set_user_scenarios(
MTK_THERMAL_SCEN_CALL);
else if (mtm_phone_call_ongoing == 0)
mtk_thermal_clear_user_scenarios(
MTK_THERMAL_SCEN_CALL);
}
return count;
}
return 0;
}
static int _mtm_scen_call_open(struct inode *inode, struct file *file)
{
return single_open(file, _mtm_scen_call_read, NULL);
}
static const struct file_operations _mtm_scen_call_fops = {
.owner = THIS_MODULE,
.open = _mtm_scen_call_open,
.read = seq_read,
.llseek = seq_lseek,
.write = _mtm_scen_call_write,
.release = single_release,
};
/* Init */
static int __init mtkthermal_init(void)
{
int err = 0;
struct proc_dir_entry *entry;
struct proc_dir_entry *dir_entry =
mtk_thermal_get_proc_drv_therm_dir_entry();
THRML_LOG("%s\n", __func__);
entry = proc_create("mtm_monitor",
0664, dir_entry, &mtkthermal_fops);
if (!entry)
THRML_ERROR_LOG("%s Can not create mtm_monitor\n", __func__);
else
proc_set_user(entry, uid, gid);
entry = proc_create("mtm_indicator",
0644, dir_entry, &_mtkthermal_indicator_fops);
if (!entry)
THRML_ERROR_LOG("%s Can not create mtm_indicator\n", __func__);
entry = proc_create("mtm_scen_call",
0664, dir_entry, &_mtm_scen_call_fops);
if (!entry)
THRML_ERROR_LOG("%s Can not create mtm_scen_call\n", __func__);
else
proc_set_user(entry, uid, gid);
/* create /proc/cooler folder */
/* WARNING! This is not gauranteed to be invoked before
* mtk_ts_cpu's functions...
*/
proc_cooler_dir_entry =
(proc_cooler_dir_entry == NULL) ?
proc_mkdir("mtkcooler", NULL) : proc_cooler_dir_entry;
if (proc_cooler_dir_entry == NULL)
THRML_ERROR_LOG("%s mkdir /proc/mtkcooler failed\n", __func__);
/* create /proc/tz folder */
/* WARNING! This is not gauranteed to be invoked before
* mtk_ts_cpu's functions...
*/
proc_tz_dir_entry =
(proc_tz_dir_entry == NULL) ?
proc_mkdir("mtktz", NULL) : proc_tz_dir_entry;
if (proc_tz_dir_entry == NULL)
THRML_ERROR_LOG("%s mkdir /proc/mtktz failed\n", __func__);
INIT_DELAYED_WORK(&_mtm_sysinfo_poll_queue, _mtm_update_sysinfo);
_mtm_update_sysinfo(NULL);
return err;
}
/* Exit */
static void __exit mtkthermal_exit(void)
{
THRML_LOG("%s\n", __func__);
}
/* ************************************
* thermal_zone_device_ops Wrapper
* ************************************
*/
/*
* .bind wrapper: bind the thermal zone device with a thermal cooling device.
*/
static int mtk_thermal_wrapper_bind
(struct thermal_zone_device *thermal, struct thermal_cooling_device *cdev)
{
int ret = 0;
struct thermal_zone_device_ops *ops;
/* WARNING! bind will invoke
* mtk_thermal_zone_bind_cooling_device_wrapper(),
* so don't rollback cooler's devdata in this bind...
*/
#if MTK_THERMAL_MONITOR_CONDITIONAL_COOLING
{
int i = 0;
struct mtk_thermal_cooler_data *cldata = NULL;
mutex_lock(&MTM_COOLER_LOCK);
cldata = cdev->devdata;
mutex_unlock(&MTM_COOLER_LOCK);
for (; i < MTK_THERMAL_MONITOR_COOLER_MAX_EXTRA_CONDITIONS;
i++) {
if ((cldata->conditions[i][0] != 0x0) &&
(cldata->condition_last_value[i]) == NULL) {
if (strncmp(cldata->conditions[i],
thermal->type, 20) == 0) {
cldata->condition_last_value[i] =
&(thermal->temperature);
THRML_LOG(
"[.bind]condition+ tz: %s cdev: %s condition: %s\n",
thermal->type, cdev->type,
cldata->conditions[i]);
}
}
}
}
#endif
/* Bind Relationship to StoreLogger */
THRML_LOG("[.bind]+ tz: %s cdev: %s tz_data:%p cl_data:%p\n",
thermal->type, cdev->type, thermal->devdata, cdev->devdata);
ops = getClientZoneOps(thermal);
if (!ops) {
THRML_ERROR_LOG(
"[.bind]E tz: %s unregistered.\n", thermal->type);
return 1;
}
if (ops->bind)
ops->bind(thermal, cdev);
/* Bind Relationship to StoreLogger */
THRML_LOG("[.bind]- tz: %s cdev: %s tz_data:%p cl_data:%p\n",
thermal->type, cdev->type, thermal->devdata, cdev->devdata);
/* Log in mtk_thermal_zone_bind_cooling_device_wrapper() */
/* THRML_STORAGE_LOG(THRML_LOGGER_MSG_BIND, bind, thermal->type,
* cdev->type);
*/
return ret;
}
/*
*.unbind wrapper: unbind the thermal zone device with a thermal cooling device.
*/
static int mtk_thermal_wrapper_unbind
(struct thermal_zone_device *thermal, struct thermal_cooling_device *cdev)
{
int ret = 0;
struct thermal_zone_device_ops *ops;
#if MTK_THERMAL_MONITOR_CONDITIONAL_COOLING
{
int i = 0;
struct mtk_thermal_cooler_data *cldata = NULL;
mutex_lock(&MTM_COOLER_LOCK);
cldata = cdev->devdata;
mutex_unlock(&MTM_COOLER_LOCK);
/* Clear cldata->tz */
if (thermal == cldata->tz) {
/* clear the state of cooler bounded first... */
if (cdev->ops) {
int tmp_exit_pt = cldata->exit_threshold;
cldata->exit_threshold = 0;
cdev->ops->set_cur_state(cdev, 0);
cldata->exit_threshold = tmp_exit_pt;
}
cldata->tz = NULL;
cldata->trip = 0;
}
for (; i < MTK_THERMAL_MONITOR_COOLER_MAX_EXTRA_CONDITIONS;
i++) {
if ((cldata->condition_last_value[i] != NULL) &&
(&(thermal->temperature) ==
cldata->condition_last_value[i])) {
cldata->condition_last_value[i] = NULL;
THRML_LOG(
"[.unbind]condition- tz: %s cdev: %s condition: %s\n",
thermal->type, cdev->type,
cldata->conditions[i]);
}
}
}
#endif
THRML_LOG("[.unbind]+ tz: %s cdev: %s\n", thermal->type, cdev->type);
ops = getClientZoneOps(thermal);
if (!ops) {
THRML_ERROR_LOG("[.unbind]E tz: %s unregistered.\n",
thermal->type);
return 1;
}
if (ops->unbind)
ret = ops->unbind(thermal, cdev);
THRML_LOG("[.unbind]- tz: %s cdev: %s\n", thermal->type, cdev->type);
return ret;
}
/*
* .get_temp wrapper: get the current temperature of the thermal zone.
*/
static int mtk_thermal_wrapper_get_temp
(struct thermal_zone_device *thermal, int *temperature)
{
int ret = 0;
struct thermal_zone_device_ops *ops;
int nTemperature;
int raw_temp = 0;
#if MTK_THERMAL_MONITOR_MEASURE_GET_TEMP_OVERHEAD
long int t = _get_current_time_us();
long int dur = 0;
#endif
ops = getClientZoneOps(thermal);
if (!ops) {
THRML_ERROR_LOG("[.get_temp] tz: %s unregistered.\n",
thermal->type);
return 1;
}
if (ops->get_temp)
ret = ops->get_temp(thermal, &raw_temp);
nTemperature = (int)raw_temp; /* /< Long cast to INT. */
if (ret == 0) {
*temperature =
_mtkthermal_update_and_get_sma(thermal->devdata, raw_temp);
/* No strong type cast... */
} else {
THRML_ERROR_LOG("[.get_temp] tz: %s invalid temp\n",
thermal->type);
*temperature = nTemperature;
}
/* Monitor Temperature to StoreLogger */
THRML_STORAGE_LOG(THRML_LOGGER_MSG_ZONE_TEMP, get_temp,
thermal->type, (int)*temperature);
THRML_LOG("[.get_temp] tz: %s raw: %d sma: %ld\n",
thermal->type, nTemperature,
(long)*temperature);
#if MTK_THERMAL_MONITOR_MEASURE_GET_TEMP_OVERHEAD
dur = _get_current_time_us() - t;
if (dur > 10000) /* over 10msec, log it */
THRML_ERROR_LOG("[.get_temp] tz: %s dur: %ld\n",
thermal->type, dur);
#endif
return ret;
}
/*
* .get_mode wrapper: get the current mode (user/kernel) of the thermal zone.
* - "kernel" means thermal management is done in kernel.
* - "user" will prevent kernel thermal driver actions upon trip points
*/
static int mtk_thermal_wrapper_get_mode
(struct thermal_zone_device *thermal, enum thermal_device_mode *mode)
{
int ret = 0;
struct thermal_zone_device_ops *ops;
THRML_LOG("[.get_mode] tz: %s mode: %d\n", thermal->type, *mode);
ops = getClientZoneOps(thermal);
if (!ops) {
THRML_ERROR_LOG("[.get_mode] tz: %s unregistered.\n",
thermal->type);
return 1;
}
if (ops->get_mode)
ret = ops->get_mode(thermal, mode);
return ret;
}
/*
* .set_mode wrapper: set the mode (user/kernel) of the thermal zone.
*/
static int mtk_thermal_wrapper_set_mode
(struct thermal_zone_device *thermal, enum thermal_device_mode mode)
{
int ret = 0;
struct thermal_zone_device_ops *ops;
THRML_LOG("[.set_mode] tz: %s mode: %d\n", thermal->type, mode);
ops = getClientZoneOps(thermal);
if (!ops) {
THRML_ERROR_LOG("[.set_mode] tz: %s unregistered.\n",
thermal->type);
return 1;
}
if (ops->set_mode)
ret = ops->set_mode(thermal, mode);
return ret;
}
/*
* .get_trip_type wrapper: get the type of certain trip point.
*/
static int mtk_thermal_wrapper_get_trip_type
(struct thermal_zone_device *thermal, int trip, enum thermal_trip_type *type)
{
int ret = 0;
struct thermal_zone_device_ops *ops;
ops = getClientZoneOps(thermal);
if (!ops) {
THRML_ERROR_LOG("[.get_trip_type] tz: %s unregistered.\n",
thermal->type);
return 1;
}
if (ops->get_trip_type)
ret = ops->get_trip_type(thermal, trip, type);
THRML_LOG("[.get_trip_type] tz: %s trip: %d type: %d\n",
thermal->type, trip, *type);
return ret;
}
/*
* .get_trip_temp wrapper:get the temperature above which the certain trip point
* will be fired.
*/
static int mtk_thermal_wrapper_get_trip_temp
(struct thermal_zone_device *thermal, int trip, int *temperature)
{
int ret = 0;
struct thermal_zone_device_ops *ops;
ops = getClientZoneOps(thermal);
if (!ops) {
THRML_ERROR_LOG("[.get_trip_temp] tz: %s unregistered.\n",
thermal->type);
return 1;
}
if (ops->get_trip_temp)
ret = ops->get_trip_temp(thermal, trip, temperature);
THRML_LOG("[.get_trip_temp] tz: %s trip: %d temp: %d\n",
thermal->type, trip, (int)*temperature);
THRML_STORAGE_LOG(THRML_LOGGER_MSG_TRIP_POINT, get_trip_temp,
thermal->type, trip, *temperature);
return ret;
}
/*
* .get_crit_temp wrapper:
*/
static int mtk_thermal_wrapper_get_crit_temp
(struct thermal_zone_device *thermal, int *temperature)
{
int ret = 0;
struct thermal_zone_device_ops *ops;
ops = getClientZoneOps(thermal);
if (!ops) {
THRML_ERROR_LOG("[.get_crit_temp] tz: %s unregistered.\n",
thermal->type);
return 1;
}
if (ops->get_crit_temp)
ret = ops->get_crit_temp(thermal, temperature);
THRML_LOG("[.get_crit_temp] tz: %s temp: %d\n", thermal->type,
(int)*temperature);
return ret;
}
static int mtk_thermal_wrapper_notify(
struct thermal_zone_device *thermal, int trip, enum thermal_trip_type type)
{
int ret = 0;
struct thermal_zone_device_ops *ops;
ops = getClientZoneOps(thermal);
if (!ops) {
THRML_ERROR_LOG("[.notify] tz: %s unregistered.\n",
thermal->type);
return 1;
}
if (ops->notify)
ret = ops->notify(thermal, trip, type);
return ret;
}
/* *************************************** */
/* MTK thermal zone register/unregister */
/* *************************************** */
/* Wrapper callback OPS */
static struct thermal_zone_device_ops mtk_thermal_wrapper_dev_ops = {
.bind = mtk_thermal_wrapper_bind,
.unbind = mtk_thermal_wrapper_unbind,
.get_temp = mtk_thermal_wrapper_get_temp,
.get_mode = mtk_thermal_wrapper_get_mode,
.set_mode = mtk_thermal_wrapper_set_mode,
.get_trip_type = mtk_thermal_wrapper_get_trip_type,
.get_trip_temp = mtk_thermal_wrapper_get_trip_temp,
.get_crit_temp = mtk_thermal_wrapper_get_crit_temp,
.notify = mtk_thermal_wrapper_notify,
};
/*mtk thermal zone register function */
struct thermal_zone_device *mtk_thermal_zone_device_register_wrapper(
char *type, int trips, void *devdata,
const struct thermal_zone_device_ops *ops,
int tc1, int tc2, int passive_delay, int polling_delay)
{
struct thermal_zone_device *tz = NULL;
struct mtk_thermal_tz_data *tzdata = NULL;
int tzidx;
THRML_LOG("%s tz: %s trips: %d passive_delay: %d polling_delay: %d\n",
__func__, type, trips, passive_delay, polling_delay);
if (strcmp(SYSINFO_ATTACH_DEV_NAME, type) == 0)
g_SysinfoAttachOps = (struct thermal_zone_device_ops *)ops;
tzdata = kzalloc(sizeof(struct mtk_thermal_tz_data), GFP_KERNEL);
if (!tzdata) {
THRML_ERROR_LOG("%s tzdata kzalloc fail.\n", __func__);
return ERR_PTR(-ENOMEM);
}
mutex_init(&tzdata->ma_lock);
mutex_lock(&tzdata->ma_lock);
tzdata->ops = (struct thermal_zone_device_ops *)ops;
tzdata->ma_len = 1;
tzdata->ma_counter = 0;
tzdata->fake_temp = -275000; /* init to -275000 */
#if (MAX_STEP_MA_LEN > 1)
tzdata->curr_idx_ma_len = 0;
tzdata->ma_lens[0] = 1;
tzdata->msma_ht[0] = MSMA_MAX_HT;
#endif
mutex_unlock(&tzdata->ma_lock);
tz = thermal_zone_device_register(type,
trips, /* /< total number of trip points */
0, /* /< mask */
/* (void*)ops, ///< invoker's ops pass to devdata */
(void *)tzdata,
&mtk_thermal_wrapper_dev_ops, /* /< use wrapper ops. */
NULL, /* /< tzp */
passive_delay, polling_delay);
tzidx = mtk_thermal_get_tz_idx(type);
/* registered the last_temperature to local arra */
mutex_lock(&MTM_GET_TEMP_LOCK);
{
if (tzidx >= 0 && tzidx < MTK_THERMAL_SENSOR_COUNT)
tz_last_values[tzidx] = &(tz->temperature);
}
mutex_unlock(&MTM_GET_TEMP_LOCK);
/* create a proc for this tz... */
if (_get_proc_tz_dir_entry() != NULL) {
struct proc_dir_entry *entry;
entry = proc_create_data((const char *)type, 0664,
proc_tz_dir_entry,
&_mtkthermal_tz_fops,
tz);
if (!entry) {
THRML_ERROR_LOG("%s proc file not created: %p\n",
__func__, tz);
} else {
proc_set_user(entry, uid, gid);
THRML_LOG("%s proc file created: %p\n", __func__, tz);
}
}
/* This interface function adds a new thermal zone device */
return tz;
}
EXPORT_SYMBOL(mtk_thermal_zone_device_register_wrapper);
/*mtk thermal zone unregister function */
void mtk_thermal_zone_device_unregister_wrapper(struct thermal_zone_device *tz)
{
char type[32] = { 0 };
struct mtk_thermal_tz_data *tzdata = NULL;
int tzidx;
strncpy(type, tz->type, 20);
tzdata = (struct mtk_thermal_tz_data *)tz->devdata;
/* delete the proc file entry from proc */
if (proc_tz_dir_entry != NULL)
remove_proc_entry((const char *)type, proc_tz_dir_entry);
tzidx = mtk_thermal_get_tz_idx(tz->type);
/* unregistered the last_temperature from local array */
mutex_lock(&MTM_GET_TEMP_LOCK);
{
if (tzidx >= 0 && tzidx < MTK_THERMAL_SENSOR_COUNT)
tz_last_values[tzidx] = NULL;
}
mutex_unlock(&MTM_GET_TEMP_LOCK);
THRML_LOG("%s+ tz : %s\n", __func__, type);
thermal_zone_device_unregister(tz);
THRML_LOG("%s- tz: %s\n", __func__, type);
/* free memory */
if (tzdata != NULL) {
mutex_lock(&tzdata->ma_lock);
tzdata->ops = NULL;
mutex_unlock(&tzdata->ma_lock);
mutex_destroy(&tzdata->ma_lock);
kfree(tzdata);
}
}
EXPORT_SYMBOL(mtk_thermal_zone_device_unregister_wrapper);
int mtk_thermal_zone_bind_cooling_device_wrapper(
struct thermal_zone_device *thermal,
int trip, struct thermal_cooling_device *cdev)
{
struct mtk_thermal_cooler_data *mcdata;
int ret = 0;
THRML_LOG("%s thermal_type:%s trip:%d cdev_type:%s ret:%d\n", __func__,
thermal->type, trip, cdev->type, ret);
ret =
thermal_zone_bind_cooling_device(thermal, trip, cdev,
THERMAL_NO_LIMIT, THERMAL_NO_LIMIT,
THERMAL_WEIGHT_DEFAULT);
if (ret) {
/* THRML_ERROR_LOG("thermal_zone_bind_cooling_device Fail.
* Code(%d)\n", ret);
*/
} else {
/* TODO: think of a way don't do this here...
* Or cannot rollback devdata in bind ops...
*/
/* Init mtk Cooler Data */
mcdata = cdev->devdata;
mcdata->trip = trip;
mcdata->tz = thermal;
}
THRML_LOG("%s thermal_type:%s trip:%d cdev_type:%s ret:%d\n", __func__,
thermal->type, trip, cdev->type, ret);
THRML_STORAGE_LOG(THRML_LOGGER_MSG_BIND, bind,
thermal->type, trip, cdev->type);
return ret;
}
EXPORT_SYMBOL(mtk_thermal_zone_bind_cooling_device_wrapper);
/* ********************************************* */
/* MTK cooling dev register/unregister */
/* ********************************************* */
/* .get_max_state */
static int mtk_cooling_wrapper_get_max_state
(struct thermal_cooling_device *cdev, unsigned long *state)
{
int ret = 0;
struct thermal_cooling_device_ops *ops;
struct mtk_thermal_cooler_data *mcdata;
mutex_lock(&MTM_COOLER_LOCK);
/* Recovery client's devdata */
ops = recoveryClientCooler(cdev, &mcdata);
if (ops->get_max_state)
ret = ops->get_max_state(cdev, state);
THRML_LOG("[.get_max_state] cdev_type:%s state:%lu\n",
cdev->type, *state);
cdev->devdata = mcdata;
mutex_unlock(&MTM_COOLER_LOCK);
return ret;
}
/* .get_cur_state */
static int mtk_cooling_wrapper_get_cur_state
(struct thermal_cooling_device *cdev, unsigned long *state)
{
int ret = 0;
struct thermal_cooling_device_ops *ops;
struct mtk_thermal_cooler_data *mcdata;
mutex_lock(&MTM_COOLER_LOCK);
/* Recovery client's devdata */
ops = recoveryClientCooler(cdev, &mcdata);
if (ops->get_cur_state)
ret = ops->get_cur_state(cdev, state);
THRML_LOG("[.get_cur_state] cdev_type:%s state:%lu\n",
cdev->type, *state);
/* reset devdata to mcdata */
cdev->devdata = mcdata;
mutex_unlock(&MTM_COOLER_LOCK);
return ret;
}
/* set_cur_state */
static int mtk_cooling_wrapper_set_cur_state
(struct thermal_cooling_device *cdev, unsigned long state)
{
struct thermal_cooling_device_ops *ops;
struct thermal_cooling_device_ops_extra *ops_ext;
struct mtk_thermal_cooler_data *mcdata;
int ret = 0;
unsigned long cur_state = 0;
mutex_lock(&MTM_COOLER_LOCK);
/* Recovery client's devdata */
ops = recoveryClientCooler(cdev, &mcdata);
ops_ext = mcdata->ops_ext;
if (ops == NULL) {
THRML_ERROR_LOG("[.set_cur_state]E no cdev ops.\n");
mutex_unlock(&MTM_COOLER_LOCK);
return -1;
}
if (ops->get_cur_state)
ret = ops->get_cur_state(cdev, &cur_state);
/* check conditions */
#if MTK_THERMAL_MONITOR_CONDITIONAL_COOLING
if (state != 0) {
/* here check conditions for setting the cooler... */
if (MTK_THERMAL_MONITOR_COOLER_MAX_EXTRA_CONDITIONS ==
_mtkthermal_check_cooler_conditions(mcdata)) {
/* pass */
} else {
THRML_LOG(
"[.set_cur_state]condition check failed tz_type:%s cdev_type:%s trip:%d state:%lu\n",
mcdata->tz->type, cdev->type,
mcdata->trip, state);
state = 0;
}
}
if (state == 0) {
int last_temp = 0;
int trip_temp = 0;
struct thermal_zone_device_ops *tz_ops;
if ((mcdata->exit_threshold > 0) && (mcdata->tz != NULL)) {
/* if exit point is set and if this cooler
* is still bound...
*/
THRML_LOG("[.set_cur_state] cur_state:%lu\n",
cur_state);
if (cur_state > 0) {
THRML_LOG("[.set_cur_state] tz:%p devdata:%p\n",
mcdata->tz, mcdata->tz->devdata);
if (mcdata->tz)
last_temp = mcdata->tz->temperature;
THRML_LOG("[.set_cur_state] last_temp:%d\n",
last_temp);
tz_ops = getClientZoneOps(mcdata->tz);
if (tz_ops == NULL) {
THRML_ERROR_LOG(
"[.set_cur_state]tz_ops null\n");
mutex_unlock(&MTM_COOLER_LOCK);
return -1;
}
if (!ops) {
THRML_ERROR_LOG(
"[.set_cur_state]E tz unregistered.\n");
/* WARN_ON_ONCE(1); */
trip_temp = 120000;
} else {
if (tz_ops->get_trip_temp) {
tz_ops->get_trip_temp(
mcdata->tz, mcdata->trip
, &trip_temp);
THRML_LOG(
"[.set_cur_state] trip_temp:%ld\n",
(long)trip_temp);
} else {
WARN_ON_ONCE(1);
}
}
if ((last_temp >= (int)trip_temp)
|| (((int)trip_temp - last_temp)
< mcdata->exit_threshold)) {
THRML_LOG(
"[.set_cur_state]not exit yet tz_type:%s cdev_type:%s trip:%d state:%lu\n",
mcdata->tz->type, cdev->type,
mcdata->trip, state);
state = cur_state;
}
}
}
}
#endif
THRML_LOG("[.set_cur_state] tz_type:%s cdev_type:%s trip:%d state:%lu\n"
, mcdata->tz->type, cdev->type, mcdata->trip, state);
THRML_STORAGE_LOG(THRML_LOGGER_MSG_COOL_STAE, set_cur_state,
mcdata->tz->type, mcdata->trip, cdev->type, state);
if (ops->set_cur_state)
ret = ops->set_cur_state(cdev, state);
if (ops_ext && ops_ext->set_cur_temp && mcdata->tz)
ops_ext->set_cur_temp(cdev, mcdata->tz->temperature);
/* reset devdata to mcdata */
cdev->devdata = mcdata;
mutex_unlock(&MTM_COOLER_LOCK);
return ret;
}
/* Cooling callbacks OPS */
static struct thermal_cooling_device_ops mtk_cooling_wrapper_dev_ops = {
.get_max_state = mtk_cooling_wrapper_get_max_state,
.get_cur_state = mtk_cooling_wrapper_get_cur_state,
.set_cur_state = mtk_cooling_wrapper_set_cur_state,
};
/*
* MTK Cooling Register
*/
struct thermal_cooling_device *mtk_thermal_cooling_device_register_wrapper
(char *type, void *devdata, const struct thermal_cooling_device_ops *ops)
{
struct mtk_thermal_cooler_data *mcdata = NULL;
struct thermal_cooling_device *ret = NULL;
int i = 0;
THRML_LOG("%s type:%s\n", __func__, type);
mcdata = kzalloc(sizeof(struct mtk_thermal_cooler_data), GFP_KERNEL);
if (!mcdata) {
THRML_ERROR_LOG("%s mcdata kzalloc fail.\n", __func__);
return ERR_PTR(-ENOMEM);
}
mcdata->ops = (struct thermal_cooling_device_ops *)ops;
mcdata->ops_ext = NULL;
mcdata->devdata = devdata;
mcdata->exit_threshold = 0;
for (; i < MTK_THERMAL_MONITOR_COOLER_MAX_EXTRA_CONDITIONS; i++) {
mcdata->conditions[i][0] = 0x0;
mcdata->condition_last_value[i] = NULL;
mcdata->threshold[i] = 0;
}
/* create a proc for this cooler... */
if (_get_proc_cooler_dir_entry() != NULL) {
struct proc_dir_entry *entry;
entry = proc_create_data((const char *)type, 0664,
proc_cooler_dir_entry,
&_mtkthermal_cooler_fops,
mcdata);
if (!entry) {
THRML_ERROR_LOG("%s proc file not created: %p\n",
__func__, mcdata);
} else {
proc_set_user(entry, uid, gid);
THRML_LOG("%s proc file created: %p\n",
__func__, mcdata);
}
}
ret = thermal_cooling_device_register(type, mcdata,
&mtk_cooling_wrapper_dev_ops);
mcdata->id = ret->id; /* Used for CPU usage flag... */
return ret;
}
EXPORT_SYMBOL(mtk_thermal_cooling_device_register_wrapper);
struct thermal_cooling_device *mtk_thermal_cooling_device_register_wrapper_extra
(char *type, void *devdata, const struct thermal_cooling_device_ops *ops,
const struct thermal_cooling_device_ops_extra *ops_ext)
{
struct mtk_thermal_cooler_data *mcdata = NULL;
struct thermal_cooling_device *ret = NULL;
int i = 0;
THRML_LOG("%s type:%s\n", __func__, type);
mcdata = kzalloc(sizeof(struct mtk_thermal_cooler_data), GFP_KERNEL);
if (!mcdata) {
THRML_ERROR_LOG("%s mcdata kzalloc fail.\n", __func__);
return ERR_PTR(-ENOMEM);
}
mcdata->ops = (struct thermal_cooling_device_ops *)ops;
/* The only difference to mtk_thermal_cooling_device_register_wrapper */
mcdata->ops_ext = (struct thermal_cooling_device_ops_extra *)ops_ext;
mcdata->devdata = devdata;
mcdata->exit_threshold = 0;
for (; i < MTK_THERMAL_MONITOR_COOLER_MAX_EXTRA_CONDITIONS; i++) {
mcdata->conditions[i][0] = 0x0;
mcdata->condition_last_value[i] = NULL;
mcdata->threshold[i] = 0;
}
/* create a proc for this cooler... */
if (_get_proc_cooler_dir_entry() != NULL) {
struct proc_dir_entry *entry;
entry = proc_create_data((const char *)type, 0664,
proc_cooler_dir_entry,
&_mtkthermal_cooler_fops,
mcdata);
if (!entry) {
THRML_ERROR_LOG("%s proc file not created: %p\n",
__func__, mcdata);
} else {
proc_set_user(entry, uid, gid);
THRML_LOG("%s proc file created: %p\n",
__func__, mcdata);
}
}
ret = thermal_cooling_device_register(type, mcdata,
&mtk_cooling_wrapper_dev_ops);
mcdata->id = ret->id; /* Used for CPU usage flag... */
return ret;
}
EXPORT_SYMBOL(mtk_thermal_cooling_device_register_wrapper_extra);
int mtk_thermal_cooling_device_add_exit_point(
struct thermal_cooling_device *cdev, int exit_point)
{
struct mtk_thermal_cooler_data *mcdata;
if (!cdev)
return -1;
mutex_lock(&MTM_COOLER_LOCK);
mcdata = cdev->devdata;
if (!mcdata) {
mutex_unlock(&MTM_COOLER_LOCK);
return -1;
}
mcdata->exit_threshold = exit_point;
mutex_unlock(&MTM_COOLER_LOCK);
THRML_LOG("%s type:%s exit:%d\n", __func__, cdev->type, exit_point);
return 0;
}
EXPORT_SYMBOL(mtk_thermal_cooling_device_add_exit_point);
/*
* MTK Cooling Unregister
*/
void mtk_thermal_cooling_device_unregister_wrapper(
struct thermal_cooling_device *cdev)
{
struct mtk_thermal_cooler_data *mcdata;
char type[32] = { 0 };
strncpy(type, cdev->type, 20);
THRML_LOG("%s+ cdev:%p devdata:%p cdev:%s\n", __func__,
cdev, cdev->devdata, type);
/* delete the proc file entry from proc */
if (proc_cooler_dir_entry != NULL)
remove_proc_entry((const char *)type, proc_cooler_dir_entry);
/* TODO: consider error handling... */
mutex_lock(&MTM_COOLER_LOCK);
/* free mtk cooler data */
mcdata = cdev->devdata;
mutex_unlock(&MTM_COOLER_LOCK);
THRML_LOG("%s- mcdata:%p\n", __func__, mcdata);
thermal_cooling_device_unregister(cdev);
/* free mtk cooler data */
kfree(mcdata);
THRML_LOG("%s- cdev: %s\n", __func__, type);
}
EXPORT_SYMBOL(mtk_thermal_cooling_device_unregister_wrapper);
int mtk_thermal_zone_bind_trigger_trip(
struct thermal_zone_device *tz, int trip, int mode)
{
THRML_LOG("%s trip %d\n", __func__, trip);
schedule_delayed_work(&(tz->poll_queue), 0);
return 0;
}
EXPORT_SYMBOL(mtk_thermal_zone_bind_trigger_trip);
int mtk_thermal_get_temp(enum mtk_thermal_sensor_id id)
{
int ret = 0;
if (id < 0 || id >= MTK_THERMAL_SENSOR_COUNT)
return -127000;
mutex_lock(&MTM_GET_TEMP_LOCK);
if (tz_last_values[id] == NULL) {
mutex_unlock(&MTM_GET_TEMP_LOCK);
return -127000;
}
ret = *tz_last_values[id];
mutex_unlock(&MTM_GET_TEMP_LOCK);
return ret;
}
EXPORT_SYMBOL(mtk_thermal_get_temp);
struct proc_dir_entry *mtk_thermal_get_proc_drv_therm_dir_entry(void)
{
mutex_lock(&MTM_DRV_THERM_PROC_DIR_LOCK);
if (proc_drv_therm_dir_entry == NULL) {
proc_drv_therm_dir_entry = proc_mkdir("driver/thermal", NULL);
if (proc_drv_therm_dir_entry == NULL)
THRML_ERROR_LOG(
"[%s]: mkdir /proc/driver/thermal failed\n",
__func__);
}
mutex_unlock(&MTM_DRV_THERM_PROC_DIR_LOCK);
return proc_drv_therm_dir_entry;
}
EXPORT_SYMBOL(mtk_thermal_get_proc_drv_therm_dir_entry);
module_init(mtkthermal_init);
module_exit(mtkthermal_exit);