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