139d182d3SAkshay Saraswat /* 239d182d3SAkshay Saraswat * Copyright (c) 2012 Samsung Electronics Co., Ltd. 339d182d3SAkshay Saraswat * http://www.samsung.com 439d182d3SAkshay Saraswat * Akshay Saraswat <akshay.s@samsung.com> 539d182d3SAkshay Saraswat * 639d182d3SAkshay Saraswat * EXYNOS - Thermal Management Unit 739d182d3SAkshay Saraswat * 839d182d3SAkshay Saraswat * See file CREDITS for list of people who contributed to this 939d182d3SAkshay Saraswat * project. 1039d182d3SAkshay Saraswat * 1139d182d3SAkshay Saraswat * This program is free software; you can redistribute it and/or modify 1239d182d3SAkshay Saraswat * it under the terms of the GNU General Public License version 2 as 1339d182d3SAkshay Saraswat * published by the Free Software Foundation. 1439d182d3SAkshay Saraswat * You should have received a copy of the GNU General Public License 1539d182d3SAkshay Saraswat * along with this program; if not, write to the Free Software 1639d182d3SAkshay Saraswat * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 1739d182d3SAkshay Saraswat * MA 02111-1307 USA 1839d182d3SAkshay Saraswat */ 1939d182d3SAkshay Saraswat 2039d182d3SAkshay Saraswat #include <common.h> 2139d182d3SAkshay Saraswat #include <errno.h> 2239d182d3SAkshay Saraswat #include <fdtdec.h> 2339d182d3SAkshay Saraswat #include <tmu.h> 2439d182d3SAkshay Saraswat #include <asm/arch/tmu.h> 253a0b1daeSAkshay Saraswat #include <asm/arch/power.h> 2639d182d3SAkshay Saraswat 2739d182d3SAkshay Saraswat #define TRIMINFO_RELOAD 1 2839d182d3SAkshay Saraswat #define CORE_EN 1 293a0b1daeSAkshay Saraswat #define THERM_TRIP_EN (1 << 12) 3039d182d3SAkshay Saraswat 3139d182d3SAkshay Saraswat #define INTEN_RISE0 1 3239d182d3SAkshay Saraswat #define INTEN_RISE1 (1 << 4) 3339d182d3SAkshay Saraswat #define INTEN_RISE2 (1 << 8) 3439d182d3SAkshay Saraswat #define INTEN_FALL0 (1 << 16) 3539d182d3SAkshay Saraswat #define INTEN_FALL1 (1 << 20) 3639d182d3SAkshay Saraswat #define INTEN_FALL2 (1 << 24) 3739d182d3SAkshay Saraswat 3839d182d3SAkshay Saraswat #define TRIM_INFO_MASK 0xff 3939d182d3SAkshay Saraswat 4039d182d3SAkshay Saraswat #define INTCLEAR_RISE0 1 4139d182d3SAkshay Saraswat #define INTCLEAR_RISE1 (1 << 4) 4239d182d3SAkshay Saraswat #define INTCLEAR_RISE2 (1 << 8) 4339d182d3SAkshay Saraswat #define INTCLEAR_FALL0 (1 << 16) 4439d182d3SAkshay Saraswat #define INTCLEAR_FALL1 (1 << 20) 4539d182d3SAkshay Saraswat #define INTCLEAR_FALL2 (1 << 24) 4639d182d3SAkshay Saraswat #define INTCLEARALL (INTCLEAR_RISE0 | INTCLEAR_RISE1 | \ 4739d182d3SAkshay Saraswat INTCLEAR_RISE2 | INTCLEAR_FALL0 | \ 4839d182d3SAkshay Saraswat INTCLEAR_FALL1 | INTCLEAR_FALL2) 4939d182d3SAkshay Saraswat 5039d182d3SAkshay Saraswat /* Tmeperature threshold values for various thermal events */ 5139d182d3SAkshay Saraswat struct temperature_params { 5239d182d3SAkshay Saraswat /* minimum value in temperature code range */ 53*1149ca00SNaveen Krishna Chatradhi unsigned min_val; 5439d182d3SAkshay Saraswat /* maximum value in temperature code range */ 55*1149ca00SNaveen Krishna Chatradhi unsigned max_val; 5639d182d3SAkshay Saraswat /* temperature threshold to start warning */ 57*1149ca00SNaveen Krishna Chatradhi unsigned start_warning; 5839d182d3SAkshay Saraswat /* temperature threshold CPU tripping */ 59*1149ca00SNaveen Krishna Chatradhi unsigned start_tripping; 603a0b1daeSAkshay Saraswat /* temperature threshold for HW tripping */ 61*1149ca00SNaveen Krishna Chatradhi unsigned hardware_tripping; 6239d182d3SAkshay Saraswat }; 6339d182d3SAkshay Saraswat 6439d182d3SAkshay Saraswat /* Pre-defined values and thresholds for calibration of current temperature */ 6539d182d3SAkshay Saraswat struct tmu_data { 6639d182d3SAkshay Saraswat /* pre-defined temperature thresholds */ 6739d182d3SAkshay Saraswat struct temperature_params ts; 6839d182d3SAkshay Saraswat /* pre-defined efuse range minimum value */ 69*1149ca00SNaveen Krishna Chatradhi unsigned efuse_min_value; 7039d182d3SAkshay Saraswat /* pre-defined efuse value for temperature calibration */ 71*1149ca00SNaveen Krishna Chatradhi unsigned efuse_value; 7239d182d3SAkshay Saraswat /* pre-defined efuse range maximum value */ 73*1149ca00SNaveen Krishna Chatradhi unsigned efuse_max_value; 7439d182d3SAkshay Saraswat /* current temperature sensing slope */ 75*1149ca00SNaveen Krishna Chatradhi unsigned slope; 7639d182d3SAkshay Saraswat }; 7739d182d3SAkshay Saraswat 7839d182d3SAkshay Saraswat /* TMU device specific details and status */ 7939d182d3SAkshay Saraswat struct tmu_info { 8039d182d3SAkshay Saraswat /* base Address for the TMU */ 81*1149ca00SNaveen Krishna Chatradhi struct exynos5_tmu_reg *tmu_base; 8239d182d3SAkshay Saraswat /* pre-defined values for calibration and thresholds */ 8339d182d3SAkshay Saraswat struct tmu_data data; 8439d182d3SAkshay Saraswat /* value required for triminfo_25 calibration */ 85*1149ca00SNaveen Krishna Chatradhi unsigned te1; 8639d182d3SAkshay Saraswat /* value required for triminfo_85 calibration */ 87*1149ca00SNaveen Krishna Chatradhi unsigned te2; 8839d182d3SAkshay Saraswat /* Value for measured data calibration */ 8939d182d3SAkshay Saraswat int dc_value; 9039d182d3SAkshay Saraswat /* enum value indicating status of the TMU */ 9139d182d3SAkshay Saraswat int tmu_state; 9239d182d3SAkshay Saraswat }; 9339d182d3SAkshay Saraswat 9439d182d3SAkshay Saraswat /* Global struct tmu_info variable to store init values */ 9539d182d3SAkshay Saraswat static struct tmu_info gbl_info; 9639d182d3SAkshay Saraswat 9739d182d3SAkshay Saraswat /* 9839d182d3SAkshay Saraswat * Get current temperature code from register, 9939d182d3SAkshay Saraswat * then calculate and calibrate it's value 10039d182d3SAkshay Saraswat * in degree celsius. 10139d182d3SAkshay Saraswat * 10239d182d3SAkshay Saraswat * @return current temperature of the chip as sensed by TMU 10339d182d3SAkshay Saraswat */ 10439d182d3SAkshay Saraswat static int get_cur_temp(struct tmu_info *info) 10539d182d3SAkshay Saraswat { 106*1149ca00SNaveen Krishna Chatradhi struct exynos5_tmu_reg *reg = info->tmu_base; 107*1149ca00SNaveen Krishna Chatradhi ulong start; 108*1149ca00SNaveen Krishna Chatradhi int cur_temp = 0; 10939d182d3SAkshay Saraswat 11039d182d3SAkshay Saraswat /* 11139d182d3SAkshay Saraswat * Temperature code range between min 25 and max 125. 11239d182d3SAkshay Saraswat * May run more than once for first call as initial sensing 11339d182d3SAkshay Saraswat * has not yet happened. 11439d182d3SAkshay Saraswat */ 115*1149ca00SNaveen Krishna Chatradhi if (info->tmu_state == TMU_STATUS_NORMAL) { 116*1149ca00SNaveen Krishna Chatradhi start = get_timer(0); 11739d182d3SAkshay Saraswat do { 11839d182d3SAkshay Saraswat cur_temp = readl(®->current_temp) & 0xff; 119*1149ca00SNaveen Krishna Chatradhi } while ((cur_temp == 0) || (get_timer(start) > 100)); 120*1149ca00SNaveen Krishna Chatradhi } 121*1149ca00SNaveen Krishna Chatradhi 122*1149ca00SNaveen Krishna Chatradhi if (cur_temp == 0) 123*1149ca00SNaveen Krishna Chatradhi return cur_temp; 12439d182d3SAkshay Saraswat 12539d182d3SAkshay Saraswat /* Calibrate current temperature */ 12639d182d3SAkshay Saraswat cur_temp = cur_temp - info->te1 + info->dc_value; 12739d182d3SAkshay Saraswat 12839d182d3SAkshay Saraswat return cur_temp; 12939d182d3SAkshay Saraswat } 13039d182d3SAkshay Saraswat 13139d182d3SAkshay Saraswat /* 13239d182d3SAkshay Saraswat * Monitors status of the TMU device and exynos temperature 13339d182d3SAkshay Saraswat * 13439d182d3SAkshay Saraswat * @param temp pointer to the current temperature value 13539d182d3SAkshay Saraswat * @return enum tmu_status_t value, code indicating event to execute 13639d182d3SAkshay Saraswat */ 13739d182d3SAkshay Saraswat enum tmu_status_t tmu_monitor(int *temp) 13839d182d3SAkshay Saraswat { 13939d182d3SAkshay Saraswat int cur_temp; 14039d182d3SAkshay Saraswat struct tmu_data *data = &gbl_info.data; 14139d182d3SAkshay Saraswat 14239d182d3SAkshay Saraswat if (gbl_info.tmu_state == TMU_STATUS_INIT) 14339d182d3SAkshay Saraswat return TMU_STATUS_INIT; 14439d182d3SAkshay Saraswat 14539d182d3SAkshay Saraswat /* Read current temperature of the SOC */ 14639d182d3SAkshay Saraswat cur_temp = get_cur_temp(&gbl_info); 147*1149ca00SNaveen Krishna Chatradhi 148*1149ca00SNaveen Krishna Chatradhi if (!cur_temp) 149*1149ca00SNaveen Krishna Chatradhi goto out; 150*1149ca00SNaveen Krishna Chatradhi 15139d182d3SAkshay Saraswat *temp = cur_temp; 15239d182d3SAkshay Saraswat 15339d182d3SAkshay Saraswat /* Temperature code lies between min 25 and max 125 */ 154*1149ca00SNaveen Krishna Chatradhi if ((cur_temp >= data->ts.start_tripping) && 155*1149ca00SNaveen Krishna Chatradhi (cur_temp <= data->ts.max_val)) 15639d182d3SAkshay Saraswat return TMU_STATUS_TRIPPED; 157*1149ca00SNaveen Krishna Chatradhi 158*1149ca00SNaveen Krishna Chatradhi if (cur_temp >= data->ts.start_warning) 15939d182d3SAkshay Saraswat return TMU_STATUS_WARNING; 160*1149ca00SNaveen Krishna Chatradhi 161*1149ca00SNaveen Krishna Chatradhi if ((cur_temp < data->ts.start_warning) && 162*1149ca00SNaveen Krishna Chatradhi (cur_temp >= data->ts.min_val)) 16339d182d3SAkshay Saraswat return TMU_STATUS_NORMAL; 164*1149ca00SNaveen Krishna Chatradhi 165*1149ca00SNaveen Krishna Chatradhi out: 16639d182d3SAkshay Saraswat /* Temperature code does not lie between min 25 and max 125 */ 16739d182d3SAkshay Saraswat gbl_info.tmu_state = TMU_STATUS_INIT; 16839d182d3SAkshay Saraswat debug("EXYNOS_TMU: Thermal reading failed\n"); 16939d182d3SAkshay Saraswat return TMU_STATUS_INIT; 17039d182d3SAkshay Saraswat } 17139d182d3SAkshay Saraswat 17239d182d3SAkshay Saraswat /* 17339d182d3SAkshay Saraswat * Get TMU specific pre-defined values from FDT 17439d182d3SAkshay Saraswat * 17539d182d3SAkshay Saraswat * @param info pointer to the tmu_info struct 17639d182d3SAkshay Saraswat * @param blob FDT blob 17739d182d3SAkshay Saraswat * @return int value, 0 for success 17839d182d3SAkshay Saraswat */ 17939d182d3SAkshay Saraswat static int get_tmu_fdt_values(struct tmu_info *info, const void *blob) 18039d182d3SAkshay Saraswat { 18139d182d3SAkshay Saraswat #ifdef CONFIG_OF_CONTROL 182*1149ca00SNaveen Krishna Chatradhi fdt_addr_t addr; 18339d182d3SAkshay Saraswat int node; 18439d182d3SAkshay Saraswat int error = 0; 18539d182d3SAkshay Saraswat 18639d182d3SAkshay Saraswat /* Get the node from FDT for TMU */ 18739d182d3SAkshay Saraswat node = fdtdec_next_compatible(blob, 0, 18839d182d3SAkshay Saraswat COMPAT_SAMSUNG_EXYNOS_TMU); 18939d182d3SAkshay Saraswat if (node < 0) { 19039d182d3SAkshay Saraswat debug("EXYNOS_TMU: No node for tmu in device tree\n"); 19139d182d3SAkshay Saraswat return -1; 19239d182d3SAkshay Saraswat } 19339d182d3SAkshay Saraswat 19439d182d3SAkshay Saraswat /* 19539d182d3SAkshay Saraswat * Get the pre-defined TMU specific values from FDT. 19639d182d3SAkshay Saraswat * All of these are expected to be correct otherwise 19739d182d3SAkshay Saraswat * miscalculation of register values in tmu_setup_parameters 19839d182d3SAkshay Saraswat * may result in misleading current temperature. 19939d182d3SAkshay Saraswat */ 200*1149ca00SNaveen Krishna Chatradhi addr = fdtdec_get_addr(blob, node, "reg"); 201*1149ca00SNaveen Krishna Chatradhi if (addr == FDT_ADDR_T_NONE) { 20239d182d3SAkshay Saraswat debug("%s: Missing tmu-base\n", __func__); 20339d182d3SAkshay Saraswat return -1; 20439d182d3SAkshay Saraswat } 205*1149ca00SNaveen Krishna Chatradhi info->tmu_base = (struct exynos5_tmu_reg *)addr; 206*1149ca00SNaveen Krishna Chatradhi 20739d182d3SAkshay Saraswat info->data.ts.min_val = fdtdec_get_int(blob, 20839d182d3SAkshay Saraswat node, "samsung,min-temp", -1); 209*1149ca00SNaveen Krishna Chatradhi error |= (info->data.ts.min_val == -1); 21039d182d3SAkshay Saraswat info->data.ts.max_val = fdtdec_get_int(blob, 21139d182d3SAkshay Saraswat node, "samsung,max-temp", -1); 212*1149ca00SNaveen Krishna Chatradhi error |= (info->data.ts.max_val == -1); 21339d182d3SAkshay Saraswat info->data.ts.start_warning = fdtdec_get_int(blob, 21439d182d3SAkshay Saraswat node, "samsung,start-warning", -1); 215*1149ca00SNaveen Krishna Chatradhi error |= (info->data.ts.start_warning == -1); 21639d182d3SAkshay Saraswat info->data.ts.start_tripping = fdtdec_get_int(blob, 21739d182d3SAkshay Saraswat node, "samsung,start-tripping", -1); 218*1149ca00SNaveen Krishna Chatradhi error |= (info->data.ts.start_tripping == -1); 2193a0b1daeSAkshay Saraswat info->data.ts.hardware_tripping = fdtdec_get_int(blob, 2203a0b1daeSAkshay Saraswat node, "samsung,hw-tripping", -1); 221*1149ca00SNaveen Krishna Chatradhi error |= (info->data.ts.hardware_tripping == -1); 22239d182d3SAkshay Saraswat info->data.efuse_min_value = fdtdec_get_int(blob, 22339d182d3SAkshay Saraswat node, "samsung,efuse-min-value", -1); 224*1149ca00SNaveen Krishna Chatradhi error |= (info->data.efuse_min_value == -1); 22539d182d3SAkshay Saraswat info->data.efuse_value = fdtdec_get_int(blob, 22639d182d3SAkshay Saraswat node, "samsung,efuse-value", -1); 227*1149ca00SNaveen Krishna Chatradhi error |= (info->data.efuse_value == -1); 22839d182d3SAkshay Saraswat info->data.efuse_max_value = fdtdec_get_int(blob, 22939d182d3SAkshay Saraswat node, "samsung,efuse-max-value", -1); 230*1149ca00SNaveen Krishna Chatradhi error |= (info->data.efuse_max_value == -1); 23139d182d3SAkshay Saraswat info->data.slope = fdtdec_get_int(blob, 23239d182d3SAkshay Saraswat node, "samsung,slope", -1); 233*1149ca00SNaveen Krishna Chatradhi error |= (info->data.slope == -1); 23439d182d3SAkshay Saraswat info->dc_value = fdtdec_get_int(blob, 23539d182d3SAkshay Saraswat node, "samsung,dc-value", -1); 236*1149ca00SNaveen Krishna Chatradhi error |= (info->dc_value == -1); 23739d182d3SAkshay Saraswat 238*1149ca00SNaveen Krishna Chatradhi if (error) { 23939d182d3SAkshay Saraswat debug("fail to get tmu node properties\n"); 24039d182d3SAkshay Saraswat return -1; 24139d182d3SAkshay Saraswat } 242*1149ca00SNaveen Krishna Chatradhi #else 243*1149ca00SNaveen Krishna Chatradhi /* Non DT support may never be added. Just in case */ 244*1149ca00SNaveen Krishna Chatradhi return -1; 24539d182d3SAkshay Saraswat #endif 24639d182d3SAkshay Saraswat 24739d182d3SAkshay Saraswat return 0; 24839d182d3SAkshay Saraswat } 24939d182d3SAkshay Saraswat 25039d182d3SAkshay Saraswat /* 25139d182d3SAkshay Saraswat * Calibrate and calculate threshold values and 25239d182d3SAkshay Saraswat * enable interrupt levels 25339d182d3SAkshay Saraswat * 25439d182d3SAkshay Saraswat * @param info pointer to the tmu_info struct 25539d182d3SAkshay Saraswat */ 25639d182d3SAkshay Saraswat static void tmu_setup_parameters(struct tmu_info *info) 25739d182d3SAkshay Saraswat { 258*1149ca00SNaveen Krishna Chatradhi unsigned te_code, con; 259*1149ca00SNaveen Krishna Chatradhi unsigned warning_code, trip_code, hwtrip_code; 260*1149ca00SNaveen Krishna Chatradhi unsigned cooling_temp; 261*1149ca00SNaveen Krishna Chatradhi unsigned rising_value; 26239d182d3SAkshay Saraswat struct tmu_data *data = &info->data; 263*1149ca00SNaveen Krishna Chatradhi struct exynos5_tmu_reg *reg = info->tmu_base; 26439d182d3SAkshay Saraswat 26539d182d3SAkshay Saraswat /* Must reload for reading efuse value from triminfo register */ 26639d182d3SAkshay Saraswat writel(TRIMINFO_RELOAD, ®->triminfo_control); 26739d182d3SAkshay Saraswat 26839d182d3SAkshay Saraswat /* Get the compensation parameter */ 26939d182d3SAkshay Saraswat te_code = readl(®->triminfo); 27039d182d3SAkshay Saraswat info->te1 = te_code & TRIM_INFO_MASK; 27139d182d3SAkshay Saraswat info->te2 = ((te_code >> 8) & TRIM_INFO_MASK); 27239d182d3SAkshay Saraswat 27339d182d3SAkshay Saraswat if ((data->efuse_min_value > info->te1) || 27439d182d3SAkshay Saraswat (info->te1 > data->efuse_max_value) 27539d182d3SAkshay Saraswat || (info->te2 != 0)) 27639d182d3SAkshay Saraswat info->te1 = data->efuse_value; 27739d182d3SAkshay Saraswat 27839d182d3SAkshay Saraswat /* Get RISING & FALLING Threshold value */ 27939d182d3SAkshay Saraswat warning_code = data->ts.start_warning 28039d182d3SAkshay Saraswat + info->te1 - info->dc_value; 28139d182d3SAkshay Saraswat trip_code = data->ts.start_tripping 28239d182d3SAkshay Saraswat + info->te1 - info->dc_value; 2833a0b1daeSAkshay Saraswat hwtrip_code = data->ts.hardware_tripping 2843a0b1daeSAkshay Saraswat + info->te1 - info->dc_value; 2853a0b1daeSAkshay Saraswat 28639d182d3SAkshay Saraswat cooling_temp = 0; 28739d182d3SAkshay Saraswat 2883a0b1daeSAkshay Saraswat rising_value = ((warning_code << 8) | 2893a0b1daeSAkshay Saraswat (trip_code << 16) | 2903a0b1daeSAkshay Saraswat (hwtrip_code << 24)); 29139d182d3SAkshay Saraswat 29239d182d3SAkshay Saraswat /* Set interrupt level */ 29339d182d3SAkshay Saraswat writel(rising_value, ®->threshold_temp_rise); 29439d182d3SAkshay Saraswat writel(cooling_temp, ®->threshold_temp_fall); 29539d182d3SAkshay Saraswat 29639d182d3SAkshay Saraswat /* 29739d182d3SAkshay Saraswat * Init TMU control tuning parameters 29839d182d3SAkshay Saraswat * [28:24] VREF - Voltage reference 29939d182d3SAkshay Saraswat * [15:13] THERM_TRIP_MODE - Tripping mode 30039d182d3SAkshay Saraswat * [12] THERM_TRIP_EN - Thermal tripping enable 30139d182d3SAkshay Saraswat * [11:8] BUF_SLOPE_SEL - Gain of amplifier 30239d182d3SAkshay Saraswat * [6] THERM_TRIP_BY_TQ_EN - Tripping by TQ pin 30339d182d3SAkshay Saraswat */ 30439d182d3SAkshay Saraswat writel(data->slope, ®->tmu_control); 30539d182d3SAkshay Saraswat 30639d182d3SAkshay Saraswat writel(INTCLEARALL, ®->intclear); 30739d182d3SAkshay Saraswat 30839d182d3SAkshay Saraswat /* TMU core enable */ 30939d182d3SAkshay Saraswat con = readl(®->tmu_control); 3103a0b1daeSAkshay Saraswat con |= THERM_TRIP_EN | CORE_EN; 31139d182d3SAkshay Saraswat 31239d182d3SAkshay Saraswat writel(con, ®->tmu_control); 31339d182d3SAkshay Saraswat 3143a0b1daeSAkshay Saraswat /* Enable HW thermal trip */ 3153a0b1daeSAkshay Saraswat set_hw_thermal_trip(); 3163a0b1daeSAkshay Saraswat 3173a0b1daeSAkshay Saraswat /* LEV1 LEV2 interrupt enable */ 3183a0b1daeSAkshay Saraswat writel(INTEN_RISE1 | INTEN_RISE2, ®->inten); 31939d182d3SAkshay Saraswat } 32039d182d3SAkshay Saraswat 32139d182d3SAkshay Saraswat /* 32239d182d3SAkshay Saraswat * Initialize TMU device 32339d182d3SAkshay Saraswat * 32439d182d3SAkshay Saraswat * @param blob FDT blob 32539d182d3SAkshay Saraswat * @return int value, 0 for success 32639d182d3SAkshay Saraswat */ 32739d182d3SAkshay Saraswat int tmu_init(const void *blob) 32839d182d3SAkshay Saraswat { 32939d182d3SAkshay Saraswat gbl_info.tmu_state = TMU_STATUS_INIT; 33039d182d3SAkshay Saraswat if (get_tmu_fdt_values(&gbl_info, blob) < 0) 33139d182d3SAkshay Saraswat goto ret; 33239d182d3SAkshay Saraswat 33339d182d3SAkshay Saraswat tmu_setup_parameters(&gbl_info); 33439d182d3SAkshay Saraswat gbl_info.tmu_state = TMU_STATUS_NORMAL; 33539d182d3SAkshay Saraswat ret: 33639d182d3SAkshay Saraswat return gbl_info.tmu_state; 33739d182d3SAkshay Saraswat } 338