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