1*39d182d3SAkshay Saraswat /* 2*39d182d3SAkshay Saraswat * Copyright (c) 2012 Samsung Electronics Co., Ltd. 3*39d182d3SAkshay Saraswat * http://www.samsung.com 4*39d182d3SAkshay Saraswat * Akshay Saraswat <akshay.s@samsung.com> 5*39d182d3SAkshay Saraswat * 6*39d182d3SAkshay Saraswat * EXYNOS - Thermal Management Unit 7*39d182d3SAkshay Saraswat * 8*39d182d3SAkshay Saraswat * See file CREDITS for list of people who contributed to this 9*39d182d3SAkshay Saraswat * project. 10*39d182d3SAkshay Saraswat * 11*39d182d3SAkshay Saraswat * This program is free software; you can redistribute it and/or modify 12*39d182d3SAkshay Saraswat * it under the terms of the GNU General Public License version 2 as 13*39d182d3SAkshay Saraswat * published by the Free Software Foundation. 14*39d182d3SAkshay Saraswat * You should have received a copy of the GNU General Public License 15*39d182d3SAkshay Saraswat * along with this program; if not, write to the Free Software 16*39d182d3SAkshay Saraswat * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 17*39d182d3SAkshay Saraswat * MA 02111-1307 USA 18*39d182d3SAkshay Saraswat */ 19*39d182d3SAkshay Saraswat 20*39d182d3SAkshay Saraswat #include <common.h> 21*39d182d3SAkshay Saraswat #include <errno.h> 22*39d182d3SAkshay Saraswat #include <fdtdec.h> 23*39d182d3SAkshay Saraswat #include <tmu.h> 24*39d182d3SAkshay Saraswat #include <asm/arch/tmu.h> 25*39d182d3SAkshay Saraswat 26*39d182d3SAkshay Saraswat #define TRIMINFO_RELOAD 1 27*39d182d3SAkshay Saraswat #define CORE_EN 1 28*39d182d3SAkshay Saraswat 29*39d182d3SAkshay Saraswat #define INTEN_RISE0 1 30*39d182d3SAkshay Saraswat #define INTEN_RISE1 (1 << 4) 31*39d182d3SAkshay Saraswat #define INTEN_RISE2 (1 << 8) 32*39d182d3SAkshay Saraswat #define INTEN_FALL0 (1 << 16) 33*39d182d3SAkshay Saraswat #define INTEN_FALL1 (1 << 20) 34*39d182d3SAkshay Saraswat #define INTEN_FALL2 (1 << 24) 35*39d182d3SAkshay Saraswat 36*39d182d3SAkshay Saraswat #define TRIM_INFO_MASK 0xff 37*39d182d3SAkshay Saraswat 38*39d182d3SAkshay Saraswat #define INTCLEAR_RISE0 1 39*39d182d3SAkshay Saraswat #define INTCLEAR_RISE1 (1 << 4) 40*39d182d3SAkshay Saraswat #define INTCLEAR_RISE2 (1 << 8) 41*39d182d3SAkshay Saraswat #define INTCLEAR_FALL0 (1 << 16) 42*39d182d3SAkshay Saraswat #define INTCLEAR_FALL1 (1 << 20) 43*39d182d3SAkshay Saraswat #define INTCLEAR_FALL2 (1 << 24) 44*39d182d3SAkshay Saraswat #define INTCLEARALL (INTCLEAR_RISE0 | INTCLEAR_RISE1 | \ 45*39d182d3SAkshay Saraswat INTCLEAR_RISE2 | INTCLEAR_FALL0 | \ 46*39d182d3SAkshay Saraswat INTCLEAR_FALL1 | INTCLEAR_FALL2) 47*39d182d3SAkshay Saraswat 48*39d182d3SAkshay Saraswat /* Tmeperature threshold values for various thermal events */ 49*39d182d3SAkshay Saraswat struct temperature_params { 50*39d182d3SAkshay Saraswat /* minimum value in temperature code range */ 51*39d182d3SAkshay Saraswat unsigned int min_val; 52*39d182d3SAkshay Saraswat /* maximum value in temperature code range */ 53*39d182d3SAkshay Saraswat unsigned int max_val; 54*39d182d3SAkshay Saraswat /* temperature threshold to start warning */ 55*39d182d3SAkshay Saraswat unsigned int start_warning; 56*39d182d3SAkshay Saraswat /* temperature threshold CPU tripping */ 57*39d182d3SAkshay Saraswat unsigned int start_tripping; 58*39d182d3SAkshay Saraswat }; 59*39d182d3SAkshay Saraswat 60*39d182d3SAkshay Saraswat /* Pre-defined values and thresholds for calibration of current temperature */ 61*39d182d3SAkshay Saraswat struct tmu_data { 62*39d182d3SAkshay Saraswat /* pre-defined temperature thresholds */ 63*39d182d3SAkshay Saraswat struct temperature_params ts; 64*39d182d3SAkshay Saraswat /* pre-defined efuse range minimum value */ 65*39d182d3SAkshay Saraswat unsigned int efuse_min_value; 66*39d182d3SAkshay Saraswat /* pre-defined efuse value for temperature calibration */ 67*39d182d3SAkshay Saraswat unsigned int efuse_value; 68*39d182d3SAkshay Saraswat /* pre-defined efuse range maximum value */ 69*39d182d3SAkshay Saraswat unsigned int efuse_max_value; 70*39d182d3SAkshay Saraswat /* current temperature sensing slope */ 71*39d182d3SAkshay Saraswat unsigned int slope; 72*39d182d3SAkshay Saraswat }; 73*39d182d3SAkshay Saraswat 74*39d182d3SAkshay Saraswat /* TMU device specific details and status */ 75*39d182d3SAkshay Saraswat struct tmu_info { 76*39d182d3SAkshay Saraswat /* base Address for the TMU */ 77*39d182d3SAkshay Saraswat unsigned tmu_base; 78*39d182d3SAkshay Saraswat /* pre-defined values for calibration and thresholds */ 79*39d182d3SAkshay Saraswat struct tmu_data data; 80*39d182d3SAkshay Saraswat /* value required for triminfo_25 calibration */ 81*39d182d3SAkshay Saraswat unsigned int te1; 82*39d182d3SAkshay Saraswat /* value required for triminfo_85 calibration */ 83*39d182d3SAkshay Saraswat unsigned int te2; 84*39d182d3SAkshay Saraswat /* Value for measured data calibration */ 85*39d182d3SAkshay Saraswat int dc_value; 86*39d182d3SAkshay Saraswat /* enum value indicating status of the TMU */ 87*39d182d3SAkshay Saraswat int tmu_state; 88*39d182d3SAkshay Saraswat }; 89*39d182d3SAkshay Saraswat 90*39d182d3SAkshay Saraswat /* Global struct tmu_info variable to store init values */ 91*39d182d3SAkshay Saraswat static struct tmu_info gbl_info; 92*39d182d3SAkshay Saraswat 93*39d182d3SAkshay Saraswat /* 94*39d182d3SAkshay Saraswat * Get current temperature code from register, 95*39d182d3SAkshay Saraswat * then calculate and calibrate it's value 96*39d182d3SAkshay Saraswat * in degree celsius. 97*39d182d3SAkshay Saraswat * 98*39d182d3SAkshay Saraswat * @return current temperature of the chip as sensed by TMU 99*39d182d3SAkshay Saraswat */ 100*39d182d3SAkshay Saraswat static int get_cur_temp(struct tmu_info *info) 101*39d182d3SAkshay Saraswat { 102*39d182d3SAkshay Saraswat int cur_temp; 103*39d182d3SAkshay Saraswat struct exynos5_tmu_reg *reg = (struct exynos5_tmu_reg *)info->tmu_base; 104*39d182d3SAkshay Saraswat 105*39d182d3SAkshay Saraswat /* 106*39d182d3SAkshay Saraswat * Temperature code range between min 25 and max 125. 107*39d182d3SAkshay Saraswat * May run more than once for first call as initial sensing 108*39d182d3SAkshay Saraswat * has not yet happened. 109*39d182d3SAkshay Saraswat */ 110*39d182d3SAkshay Saraswat do { 111*39d182d3SAkshay Saraswat cur_temp = readl(®->current_temp) & 0xff; 112*39d182d3SAkshay Saraswat } while (cur_temp == 0 && info->tmu_state == TMU_STATUS_NORMAL); 113*39d182d3SAkshay Saraswat 114*39d182d3SAkshay Saraswat /* Calibrate current temperature */ 115*39d182d3SAkshay Saraswat cur_temp = cur_temp - info->te1 + info->dc_value; 116*39d182d3SAkshay Saraswat 117*39d182d3SAkshay Saraswat return cur_temp; 118*39d182d3SAkshay Saraswat } 119*39d182d3SAkshay Saraswat 120*39d182d3SAkshay Saraswat /* 121*39d182d3SAkshay Saraswat * Monitors status of the TMU device and exynos temperature 122*39d182d3SAkshay Saraswat * 123*39d182d3SAkshay Saraswat * @param temp pointer to the current temperature value 124*39d182d3SAkshay Saraswat * @return enum tmu_status_t value, code indicating event to execute 125*39d182d3SAkshay Saraswat */ 126*39d182d3SAkshay Saraswat enum tmu_status_t tmu_monitor(int *temp) 127*39d182d3SAkshay Saraswat { 128*39d182d3SAkshay Saraswat int cur_temp; 129*39d182d3SAkshay Saraswat struct tmu_data *data = &gbl_info.data; 130*39d182d3SAkshay Saraswat 131*39d182d3SAkshay Saraswat if (gbl_info.tmu_state == TMU_STATUS_INIT) 132*39d182d3SAkshay Saraswat return TMU_STATUS_INIT; 133*39d182d3SAkshay Saraswat 134*39d182d3SAkshay Saraswat /* Read current temperature of the SOC */ 135*39d182d3SAkshay Saraswat cur_temp = get_cur_temp(&gbl_info); 136*39d182d3SAkshay Saraswat *temp = cur_temp; 137*39d182d3SAkshay Saraswat 138*39d182d3SAkshay Saraswat /* Temperature code lies between min 25 and max 125 */ 139*39d182d3SAkshay Saraswat if (cur_temp >= data->ts.start_tripping && 140*39d182d3SAkshay Saraswat cur_temp <= data->ts.max_val) { 141*39d182d3SAkshay Saraswat return TMU_STATUS_TRIPPED; 142*39d182d3SAkshay Saraswat } else if (cur_temp >= data->ts.start_warning) { 143*39d182d3SAkshay Saraswat return TMU_STATUS_WARNING; 144*39d182d3SAkshay Saraswat } else if (cur_temp < data->ts.start_warning && 145*39d182d3SAkshay Saraswat cur_temp >= data->ts.min_val) { 146*39d182d3SAkshay Saraswat return TMU_STATUS_NORMAL; 147*39d182d3SAkshay Saraswat } else { 148*39d182d3SAkshay Saraswat /* Temperature code does not lie between min 25 and max 125 */ 149*39d182d3SAkshay Saraswat gbl_info.tmu_state = TMU_STATUS_INIT; 150*39d182d3SAkshay Saraswat debug("EXYNOS_TMU: Thermal reading failed\n"); 151*39d182d3SAkshay Saraswat return TMU_STATUS_INIT; 152*39d182d3SAkshay Saraswat } 153*39d182d3SAkshay Saraswat } 154*39d182d3SAkshay Saraswat 155*39d182d3SAkshay Saraswat /* 156*39d182d3SAkshay Saraswat * Get TMU specific pre-defined values from FDT 157*39d182d3SAkshay Saraswat * 158*39d182d3SAkshay Saraswat * @param info pointer to the tmu_info struct 159*39d182d3SAkshay Saraswat * @param blob FDT blob 160*39d182d3SAkshay Saraswat * @return int value, 0 for success 161*39d182d3SAkshay Saraswat */ 162*39d182d3SAkshay Saraswat static int get_tmu_fdt_values(struct tmu_info *info, const void *blob) 163*39d182d3SAkshay Saraswat { 164*39d182d3SAkshay Saraswat #ifdef CONFIG_OF_CONTROL 165*39d182d3SAkshay Saraswat int node; 166*39d182d3SAkshay Saraswat int error = 0; 167*39d182d3SAkshay Saraswat 168*39d182d3SAkshay Saraswat /* Get the node from FDT for TMU */ 169*39d182d3SAkshay Saraswat node = fdtdec_next_compatible(blob, 0, 170*39d182d3SAkshay Saraswat COMPAT_SAMSUNG_EXYNOS_TMU); 171*39d182d3SAkshay Saraswat if (node < 0) { 172*39d182d3SAkshay Saraswat debug("EXYNOS_TMU: No node for tmu in device tree\n"); 173*39d182d3SAkshay Saraswat return -1; 174*39d182d3SAkshay Saraswat } 175*39d182d3SAkshay Saraswat 176*39d182d3SAkshay Saraswat /* 177*39d182d3SAkshay Saraswat * Get the pre-defined TMU specific values from FDT. 178*39d182d3SAkshay Saraswat * All of these are expected to be correct otherwise 179*39d182d3SAkshay Saraswat * miscalculation of register values in tmu_setup_parameters 180*39d182d3SAkshay Saraswat * may result in misleading current temperature. 181*39d182d3SAkshay Saraswat */ 182*39d182d3SAkshay Saraswat info->tmu_base = fdtdec_get_addr(blob, node, "reg"); 183*39d182d3SAkshay Saraswat if (info->tmu_base == FDT_ADDR_T_NONE) { 184*39d182d3SAkshay Saraswat debug("%s: Missing tmu-base\n", __func__); 185*39d182d3SAkshay Saraswat return -1; 186*39d182d3SAkshay Saraswat } 187*39d182d3SAkshay Saraswat info->data.ts.min_val = fdtdec_get_int(blob, 188*39d182d3SAkshay Saraswat node, "samsung,min-temp", -1); 189*39d182d3SAkshay Saraswat error |= info->data.ts.min_val; 190*39d182d3SAkshay Saraswat info->data.ts.max_val = fdtdec_get_int(blob, 191*39d182d3SAkshay Saraswat node, "samsung,max-temp", -1); 192*39d182d3SAkshay Saraswat error |= info->data.ts.max_val; 193*39d182d3SAkshay Saraswat info->data.ts.start_warning = fdtdec_get_int(blob, 194*39d182d3SAkshay Saraswat node, "samsung,start-warning", -1); 195*39d182d3SAkshay Saraswat error |= info->data.ts.start_warning; 196*39d182d3SAkshay Saraswat info->data.ts.start_tripping = fdtdec_get_int(blob, 197*39d182d3SAkshay Saraswat node, "samsung,start-tripping", -1); 198*39d182d3SAkshay Saraswat error |= info->data.ts.start_tripping; 199*39d182d3SAkshay Saraswat info->data.efuse_min_value = fdtdec_get_int(blob, 200*39d182d3SAkshay Saraswat node, "samsung,efuse-min-value", -1); 201*39d182d3SAkshay Saraswat error |= info->data.efuse_min_value; 202*39d182d3SAkshay Saraswat info->data.efuse_value = fdtdec_get_int(blob, 203*39d182d3SAkshay Saraswat node, "samsung,efuse-value", -1); 204*39d182d3SAkshay Saraswat error |= info->data.efuse_value; 205*39d182d3SAkshay Saraswat info->data.efuse_max_value = fdtdec_get_int(blob, 206*39d182d3SAkshay Saraswat node, "samsung,efuse-max-value", -1); 207*39d182d3SAkshay Saraswat error |= info->data.efuse_max_value; 208*39d182d3SAkshay Saraswat info->data.slope = fdtdec_get_int(blob, 209*39d182d3SAkshay Saraswat node, "samsung,slope", -1); 210*39d182d3SAkshay Saraswat error |= info->data.slope; 211*39d182d3SAkshay Saraswat info->dc_value = fdtdec_get_int(blob, 212*39d182d3SAkshay Saraswat node, "samsung,dc-value", -1); 213*39d182d3SAkshay Saraswat error |= info->dc_value; 214*39d182d3SAkshay Saraswat 215*39d182d3SAkshay Saraswat if (error == -1) { 216*39d182d3SAkshay Saraswat debug("fail to get tmu node properties\n"); 217*39d182d3SAkshay Saraswat return -1; 218*39d182d3SAkshay Saraswat } 219*39d182d3SAkshay Saraswat #endif 220*39d182d3SAkshay Saraswat 221*39d182d3SAkshay Saraswat return 0; 222*39d182d3SAkshay Saraswat } 223*39d182d3SAkshay Saraswat 224*39d182d3SAkshay Saraswat /* 225*39d182d3SAkshay Saraswat * Calibrate and calculate threshold values and 226*39d182d3SAkshay Saraswat * enable interrupt levels 227*39d182d3SAkshay Saraswat * 228*39d182d3SAkshay Saraswat * @param info pointer to the tmu_info struct 229*39d182d3SAkshay Saraswat */ 230*39d182d3SAkshay Saraswat static void tmu_setup_parameters(struct tmu_info *info) 231*39d182d3SAkshay Saraswat { 232*39d182d3SAkshay Saraswat unsigned int te_code, con; 233*39d182d3SAkshay Saraswat unsigned int warning_code, trip_code; 234*39d182d3SAkshay Saraswat unsigned int cooling_temp; 235*39d182d3SAkshay Saraswat unsigned int rising_value; 236*39d182d3SAkshay Saraswat struct tmu_data *data = &info->data; 237*39d182d3SAkshay Saraswat struct exynos5_tmu_reg *reg = (struct exynos5_tmu_reg *)info->tmu_base; 238*39d182d3SAkshay Saraswat 239*39d182d3SAkshay Saraswat /* Must reload for reading efuse value from triminfo register */ 240*39d182d3SAkshay Saraswat writel(TRIMINFO_RELOAD, ®->triminfo_control); 241*39d182d3SAkshay Saraswat 242*39d182d3SAkshay Saraswat /* Get the compensation parameter */ 243*39d182d3SAkshay Saraswat te_code = readl(®->triminfo); 244*39d182d3SAkshay Saraswat info->te1 = te_code & TRIM_INFO_MASK; 245*39d182d3SAkshay Saraswat info->te2 = ((te_code >> 8) & TRIM_INFO_MASK); 246*39d182d3SAkshay Saraswat 247*39d182d3SAkshay Saraswat if ((data->efuse_min_value > info->te1) || 248*39d182d3SAkshay Saraswat (info->te1 > data->efuse_max_value) 249*39d182d3SAkshay Saraswat || (info->te2 != 0)) 250*39d182d3SAkshay Saraswat info->te1 = data->efuse_value; 251*39d182d3SAkshay Saraswat 252*39d182d3SAkshay Saraswat /* Get RISING & FALLING Threshold value */ 253*39d182d3SAkshay Saraswat warning_code = data->ts.start_warning 254*39d182d3SAkshay Saraswat + info->te1 - info->dc_value; 255*39d182d3SAkshay Saraswat trip_code = data->ts.start_tripping 256*39d182d3SAkshay Saraswat + info->te1 - info->dc_value; 257*39d182d3SAkshay Saraswat cooling_temp = 0; 258*39d182d3SAkshay Saraswat 259*39d182d3SAkshay Saraswat rising_value = ((warning_code << 8) | (trip_code << 16)); 260*39d182d3SAkshay Saraswat 261*39d182d3SAkshay Saraswat /* Set interrupt level */ 262*39d182d3SAkshay Saraswat writel(rising_value, ®->threshold_temp_rise); 263*39d182d3SAkshay Saraswat writel(cooling_temp, ®->threshold_temp_fall); 264*39d182d3SAkshay Saraswat 265*39d182d3SAkshay Saraswat /* 266*39d182d3SAkshay Saraswat * Init TMU control tuning parameters 267*39d182d3SAkshay Saraswat * [28:24] VREF - Voltage reference 268*39d182d3SAkshay Saraswat * [15:13] THERM_TRIP_MODE - Tripping mode 269*39d182d3SAkshay Saraswat * [12] THERM_TRIP_EN - Thermal tripping enable 270*39d182d3SAkshay Saraswat * [11:8] BUF_SLOPE_SEL - Gain of amplifier 271*39d182d3SAkshay Saraswat * [6] THERM_TRIP_BY_TQ_EN - Tripping by TQ pin 272*39d182d3SAkshay Saraswat */ 273*39d182d3SAkshay Saraswat writel(data->slope, ®->tmu_control); 274*39d182d3SAkshay Saraswat 275*39d182d3SAkshay Saraswat writel(INTCLEARALL, ®->intclear); 276*39d182d3SAkshay Saraswat 277*39d182d3SAkshay Saraswat /* TMU core enable */ 278*39d182d3SAkshay Saraswat con = readl(®->tmu_control); 279*39d182d3SAkshay Saraswat con |= CORE_EN; 280*39d182d3SAkshay Saraswat 281*39d182d3SAkshay Saraswat writel(con, ®->tmu_control); 282*39d182d3SAkshay Saraswat 283*39d182d3SAkshay Saraswat /* LEV0 LEV1 LEV2 interrupt enable */ 284*39d182d3SAkshay Saraswat writel(INTEN_RISE0 | INTEN_RISE1 | INTEN_RISE2, ®->inten); 285*39d182d3SAkshay Saraswat } 286*39d182d3SAkshay Saraswat 287*39d182d3SAkshay Saraswat /* 288*39d182d3SAkshay Saraswat * Initialize TMU device 289*39d182d3SAkshay Saraswat * 290*39d182d3SAkshay Saraswat * @param blob FDT blob 291*39d182d3SAkshay Saraswat * @return int value, 0 for success 292*39d182d3SAkshay Saraswat */ 293*39d182d3SAkshay Saraswat int tmu_init(const void *blob) 294*39d182d3SAkshay Saraswat { 295*39d182d3SAkshay Saraswat gbl_info.tmu_state = TMU_STATUS_INIT; 296*39d182d3SAkshay Saraswat if (get_tmu_fdt_values(&gbl_info, blob) < 0) 297*39d182d3SAkshay Saraswat goto ret; 298*39d182d3SAkshay Saraswat 299*39d182d3SAkshay Saraswat tmu_setup_parameters(&gbl_info); 300*39d182d3SAkshay Saraswat gbl_info.tmu_state = TMU_STATUS_NORMAL; 301*39d182d3SAkshay Saraswat ret: 302*39d182d3SAkshay Saraswat 303*39d182d3SAkshay Saraswat return gbl_info.tmu_state; 304*39d182d3SAkshay Saraswat } 305