// 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* ************************************ */ /* 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 * * .. * * 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 * ... * is string format * 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);