// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2019 MediaTek Inc. */ #include #include #include #include #include #include #include #include #include "mt-plat/mtk_thermal_monitor.h" #include "mach/mtk_thermal.h" #if defined(CONFIG_MTK_CLKMGR) #include #else #include #endif #include #include #include #include #include #if defined(THERMAL_VPU_SUPPORT) #if defined(CONFIG_MTK_APUSYS_SUPPORT) #include "apu_power_table.h" #else #include "vpu_dvfs.h" #endif #endif #if defined(THERMAL_MDLA_SUPPORT) #if defined(CONFIG_MTK_APUSYS_SUPPORT) #include "apu_power_table.h" #else #include "mdla_dvfs.h" #endif #endif /*============================================================= *Local variable definition *============================================================= */ #if defined(THERMAL_VPU_SUPPORT) || defined(THERMAL_MDLA_SUPPORT) static kuid_t uid = KUIDT_INIT(0); static kgid_t gid = KGIDT_INIT(1000); #endif int tscpu_cpu_dmips[CPU_COOLER_NUM] = { 0 }; int mtktscpu_limited_dmips = 1; /* Use in mtk_thermal_platform.c */ static int previous_step = -1; static unsigned int *cl_dev_state; static int Num_of_OPP; static struct thermal_cooling_device **cl_dev; static char *cooler_name; static unsigned int prv_stc_cpu_pwr_lim; static unsigned int prv_stc_gpu_pwr_lim; unsigned int static_cpu_power_limit = 0x7FFFFFFF; unsigned int static_gpu_power_limit = 0x7FFFFFFF; #if defined(THERMAL_VPU_SUPPORT) static unsigned int prv_stc_vpu_pwr_lim; unsigned int static_vpu_power_limit = 0x7FFFFFFF; #endif #if defined(THERMAL_MDLA_SUPPORT) static unsigned int prv_stc_mdla_pwr_lim; unsigned int static_mdla_power_limit = 0x7FFFFFFF; #endif static struct apthermolmt_user ap_dtm; static char *ap_dtm_log = "ap_dtm"; /*============================================================= *Local function prototype *============================================================= */ static void set_static_cpu_power_limit(unsigned int limit); static void set_static_gpu_power_limit(unsigned int limit); /*============================================================= *Weak functions *============================================================= */ /*============================================================= */ static void set_static_cpu_power_limit(unsigned int limit) { prv_stc_cpu_pwr_lim = static_cpu_power_limit; static_cpu_power_limit = (limit != 0) ? limit : 0x7FFFFFFF; if (prv_stc_cpu_pwr_lim != static_cpu_power_limit) { #ifdef FAST_RESPONSE_ATM tscpu_printk("%s %d, T=%d\n", __func__, (static_cpu_power_limit != 0x7FFFFFFF) ? static_cpu_power_limit : 0, tscpu_get_curr_max_ts_temp()); #else tscpu_printk("%s %d, T=%d\n", __func__, (static_cpu_power_limit != 0x7FFFFFFF) ? static_cpu_power_limit : 0, tscpu_get_curr_temp()); #endif apthermolmt_set_cpu_power_limit(&ap_dtm, static_cpu_power_limit); } } static void set_static_gpu_power_limit(unsigned int limit) { prv_stc_gpu_pwr_lim = static_gpu_power_limit; static_gpu_power_limit = (limit != 0) ? limit : 0x7FFFFFFF; if (prv_stc_gpu_pwr_lim != static_gpu_power_limit) { tscpu_printk("%s %d\n", __func__, (static_gpu_power_limit != 0x7FFFFFFF) ? static_gpu_power_limit : 0); apthermolmt_set_gpu_power_limit(&ap_dtm, static_gpu_power_limit); } } #if defined(THERMAL_VPU_SUPPORT) static void set_static_vpu_power_limit(unsigned int limit) { prv_stc_vpu_pwr_lim = static_vpu_power_limit; static_vpu_power_limit = (limit != 0) ? limit : 0x7FFFFFFF; if (prv_stc_vpu_pwr_lim != static_vpu_power_limit) { tscpu_printk("%s %d\n", __func__, (static_vpu_power_limit != 0x7FFFFFFF) ? static_vpu_power_limit : 0); apthermolmt_set_vpu_power_limit(&ap_dtm, static_vpu_power_limit); } } #endif #if defined(THERMAL_MDLA_SUPPORT) static void set_static_mdla_power_limit(unsigned int limit) { prv_stc_mdla_pwr_lim = static_mdla_power_limit; static_mdla_power_limit = (limit != 0) ? limit : 0x7FFFFFFF; if (prv_stc_mdla_pwr_lim != static_mdla_power_limit) { tscpu_printk("%s %d\n", __func__, (static_mdla_power_limit != 0x7FFFFFFF) ? static_mdla_power_limit : 0); apthermolmt_set_mdla_power_limit(&ap_dtm, static_mdla_power_limit); } } #endif static int tscpu_set_power_consumption_state(void) { int i = 0; int power = 0; tscpu_dprintk("%s Num_of_OPP=%d\n", __func__, Num_of_OPP); /* in 92, Num_of_OPP=34 */ for (i = 0; i < Num_of_OPP; i++) { if (cl_dev_state[i] == 1) { if (i != previous_step) { tscpu_printk("%s prev=%d curr=%d\n", __func__, previous_step, i); previous_step = i; mtktscpu_limited_dmips = tscpu_cpu_dmips[previous_step]; if (Num_of_GPU_OPP == 3) { power = (i * 100 + 700) - mtk_gpu_power[Num_of_GPU_OPP-1] .gpufreq_power; set_static_cpu_power_limit(power); set_static_gpu_power_limit( mtk_gpu_power[Num_of_GPU_OPP-1] .gpufreq_power); tscpu_dprintk( "Num_of_GPU_OPP=%d, gpufreq_power=%d, power=%d\n", Num_of_GPU_OPP, mtk_gpu_power[Num_of_GPU_OPP-1] .gpufreq_power, power); } else if (Num_of_GPU_OPP == 2) { power = (i * 100 + 700) - mtk_gpu_power[1].gpufreq_power; set_static_cpu_power_limit(power); set_static_gpu_power_limit( mtk_gpu_power[1].gpufreq_power); tscpu_dprintk( "Num_of_GPU_OPP=%d, gpufreq_power=%d, power=%d\n", Num_of_GPU_OPP, mtk_gpu_power[1].gpufreq_power, power); } else if (Num_of_GPU_OPP == 1) { #if 0 /* 653mW,GPU 500Mhz,1V * (preloader default) */ /* 1016mW,GPU 700Mhz,1.1V */ power = (i * 100 + 700) - 653; #else power = (i * 100 + 700) - mtk_gpu_power[0].gpufreq_power; #endif set_static_cpu_power_limit(power); tscpu_dprintk( "Num_of_GPU_OPP=%d, gpufreq_power=%d, power=%d\n", Num_of_GPU_OPP, mtk_gpu_power[0].gpufreq_power, power); } else {/* TODO: fix this, temp solution * , this project has over 5 GPU OPP... */ power = (i * 100 + 700); set_static_cpu_power_limit(power); tscpu_dprintk( "Num_of_GPU_OPP=%d, gpufreq_power=%d, power=%d\n", Num_of_GPU_OPP, mtk_gpu_power[0].gpufreq_power, power); } } break; } } /* If temp drop to our expect value, * we need to restore initial cpu freq setting */ if (i == Num_of_OPP) { if (previous_step != -1) { tscpu_printk( "Free all static thermal limit, previous_opp=%d\n", previous_step); previous_step = -1; mtktscpu_limited_dmips = /* highest dmips */ tscpu_cpu_dmips[CPU_COOLER_NUM - 1]; set_static_cpu_power_limit(0); set_static_gpu_power_limit(0); } } return 0; } static int dtm_cpu_get_max_state (struct thermal_cooling_device *cdev, unsigned long *state) { *state = 1; return 0; } static int dtm_cpu_get_cur_state (struct thermal_cooling_device *cdev, unsigned long *state) { int i = 0; for (i = 0; i < Num_of_OPP; i++) { if (!strcmp(cdev->type, &cooler_name[i * 20])) *state = cl_dev_state[i]; } return 0; } static int dtm_cpu_set_cur_state (struct thermal_cooling_device *cdev, unsigned long state) { int i = 0; for (i = 0; i < Num_of_OPP; i++) { if (!strcmp(cdev->type, &cooler_name[i * 20])) { cl_dev_state[i] = state; tscpu_set_power_consumption_state(); break; } } return 0; } static struct thermal_cooling_device_ops mtktscpu_cooling_F0x2_ops = { .get_max_state = dtm_cpu_get_max_state, .get_cur_state = dtm_cpu_get_cur_state, .set_cur_state = dtm_cpu_set_cur_state, }; #if defined(THERMAL_VPU_SUPPORT) static ssize_t clvpu_opp_proc_write (struct file *filp, const char __user *buf, size_t len, loff_t *data) { int vpu_upper_opp = -1; unsigned int vpu_power = 0; char tmp[32] = {0}; len = (len < (sizeof(tmp) - 1)) ? len : (sizeof(tmp) - 1); /* write data to the buffer */ if (copy_from_user(tmp, buf, len)) return -EFAULT; if (kstrtoint(tmp, 10, &vpu_upper_opp) == 0) { if (vpu_upper_opp == -1) vpu_power = 0; #ifdef CONFIG_MTK_APUSYS_SUPPORT else if (vpu_upper_opp >= APU_OPP_0 && vpu_upper_opp < APU_OPP_NUM) #else else if (vpu_upper_opp >= VPU_OPP_0 && vpu_upper_opp < VPU_OPP_NUM) #endif vpu_power = vpu_power_table[vpu_upper_opp].power; else vpu_power = 0; set_static_vpu_power_limit(vpu_power); tscpu_printk("[%s] = %d\n", __func__, vpu_power); return len; } tscpu_dprintk("[%s] invalid input\n", __func__); return -EINVAL; } static int clvpu_opp_proc_read(struct seq_file *m, void *v) { seq_printf(m, "%d,%d\n", prv_stc_vpu_pwr_lim, static_vpu_power_limit); tscpu_dprintk("[%s] %d\n", __func__, static_vpu_power_limit); return 0; } static int clvpu_opp_proc_open(struct inode *inode, struct file *file) { return single_open(file, clvpu_opp_proc_read, PDE_DATA(inode)); } static const struct file_operations clvpu_opp_fops = { .owner = THIS_MODULE, .open = clvpu_opp_proc_open, .read = seq_read, .llseek = seq_lseek, .write = clvpu_opp_proc_write, .release = single_release, }; static void thermal_vpu_init(void) { struct proc_dir_entry *dir_entry = NULL; struct proc_dir_entry *entry = NULL; dir_entry = mtk_thermal_get_proc_drv_therm_dir_entry(); if (!dir_entry) tscpu_printk("[%s]: mkdir /proc/driver/thermal failed\n", __func__); else { entry = proc_create("thermal_vpu_limit", 0664, dir_entry, &clvpu_opp_fops); if (entry) proc_set_user(entry, uid, gid); } } #endif #if defined(THERMAL_MDLA_SUPPORT) static ssize_t clmdla_opp_proc_write( struct file *filp, const char __user *buf, size_t len, loff_t *data) { int mdla_upper_opp = -1; unsigned int mdla_power = 0; char tmp[32] = {0}; len = (len < (sizeof(tmp) - 1)) ? len : (sizeof(tmp) - 1); /* write data to the buffer */ if (copy_from_user(tmp, buf, len)) return -EFAULT; if (kstrtoint(tmp, 10, &mdla_upper_opp) == 0) { if (mdla_upper_opp == -1) mdla_power = 0; #ifdef CONFIG_MTK_APUSYS_SUPPORT else if (mdla_upper_opp >= APU_OPP_0 && mdla_upper_opp < APU_OPP_NUM) #else else if (mdla_upper_opp >= MDLA_OPP_0 && mdla_upper_opp < MDLA_OPP_NUM) #endif mdla_power = mdla_power_table[mdla_upper_opp].power; else mdla_power = 0; set_static_mdla_power_limit(mdla_power); tscpu_printk("[%s] = %d\n", __func__, mdla_power); return len; } tscpu_dprintk("[%s] invalid input\n", __func__); return -EINVAL; } static int clmdla_opp_proc_read(struct seq_file *m, void *v) { seq_printf(m, "%d,%d\n", prv_stc_mdla_pwr_lim, static_mdla_power_limit); tscpu_dprintk("[%s] %d\n", __func__, static_mdla_power_limit); return 0; } static int clmdla_opp_proc_open(struct inode *inode, struct file *file) { return single_open(file, clmdla_opp_proc_read, PDE_DATA(inode)); } static const struct file_operations clmdla_opp_fops = { .owner = THIS_MODULE, .open = clmdla_opp_proc_open, .read = seq_read, .llseek = seq_lseek, .write = clmdla_opp_proc_write, .release = single_release, }; static void thermal_mdla_init(void) { struct proc_dir_entry *dir_entry = NULL; struct proc_dir_entry *entry = NULL; dir_entry = mtk_thermal_get_proc_drv_therm_dir_entry(); if (!dir_entry) tscpu_printk("[%s]: mkdir /proc/driver/thermal failed\n", __func__); else { entry = proc_create("thermal_mdla_limit", 0664, dir_entry, &clmdla_opp_fops); if (entry) proc_set_user(entry, uid, gid); } } #endif /* Init local structure for AP coolers */ static int init_cooler(void) { int i, ret = -ENOMEM; int num = CPU_COOLER_NUM; /* 700~4000, 92 */ cl_dev_state = kzalloc((num) * sizeof(unsigned int), GFP_KERNEL); if (cl_dev_state == NULL) return -ENOMEM; cl_dev = kzalloc((num) * sizeof(struct thermal_cooling_device *), GFP_KERNEL); if (cl_dev == NULL) goto free_cl_dev_state; cooler_name = kzalloc((num) * sizeof(char) * 20, GFP_KERNEL); if (cooler_name == NULL) goto free_cl_dev; for (i = 0; i < num; i++) { /* using index=>0=700,1=800 ~ 33=4000 */ ret = sprintf(cooler_name + (i * 20), "cpu%02d", i); if (ret != 5) { ret = -EIO; goto free_cooler_name; } } Num_of_OPP = num; /* CPU COOLER COUNT, not CPU OPP count */ return 0; free_cooler_name: kfree(cooler_name); free_cl_dev: kfree(cl_dev); free_cl_dev_state: kfree(cl_dev_state); return ret; } static int __init mtk_cooler_dtm_init(void) { int err = 0, i; tscpu_dprintk("%s start\n", __func__); err = apthermolmt_register_user(&ap_dtm, ap_dtm_log); if (err < 0) return err; err = init_cooler(); if (err) { tscpu_printk("%s fail\n", __func__); return err; } for (i = 0; i < Num_of_OPP; i++) { cl_dev[i] = mtk_thermal_cooling_device_register( &cooler_name[i * 20], NULL, &mtktscpu_cooling_F0x2_ops); } #if defined(THERMAL_VPU_SUPPORT) thermal_vpu_init(); #endif #if defined(THERMAL_MDLA_SUPPORT) thermal_mdla_init(); #endif /* * if (err) { * tscpu_printk( * "tscpu_register_DVFS_hotplug_cooler fail\n"); * return err; * } */ tscpu_dprintk("%s end\n", __func__); return 0; } static void __exit mtk_cooler_dtm_exit(void) { int i; tscpu_dprintk("%s\n", __func__); for (i = 0; i < Num_of_OPP; i++) { if (cl_dev[i]) { mtk_thermal_cooling_device_unregister(cl_dev[i]); cl_dev[i] = NULL; } } apthermolmt_unregister_user(&ap_dtm); } module_init(mtk_cooler_dtm_init); module_exit(mtk_cooler_dtm_exit);