Commit 59dfa54c5edf4955e6e5469fd7cb8c0a5a813818
Committed by
Eduardo Valentin
1 parent
1b678641c2
Exists in
smarc-imx_3.14.28_1.0.0_ga
and in
1 other branch
thermal: exynos: Rename exynos_thermal.c to exynos_tmu.c
This patch renames exynos_thermal.c to exynos_tmu.c. This change is needed as this file now just contains exynos tmu driver related codes and thermal zone or cpufreq cooling registration related changes are not there anymore. Acked-by: Kukjin Kim <kgene.kim@samsung.com> Acked-by: Jonghwa Lee <jonghwa3.lee@samsung.com> Acked-by: Eduardo Valentin <eduardo.valentin@ti.com> Signed-off-by: Amit Daniel Kachhap <amit.daniel@samsung.com> Signed-off-by: Eduardo Valentin <eduardo.valentin@ti.com>
Showing 3 changed files with 655 additions and 655 deletions Side-by-side Diff
drivers/thermal/samsung/Makefile
1 | 1 | # |
2 | 2 | # Samsung thermal specific Makefile |
3 | 3 | # |
4 | -obj-$(CONFIG_EXYNOS_THERMAL) += exynos_soc_thermal.o | |
5 | -exynos_soc_thermal-y := exynos_thermal.o | |
6 | -exynos_soc_thermal-$(CONFIG_EXYNOS_THERMAL_CORE) += exynos_thermal_common.o | |
4 | +obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o | |
5 | +exynos_thermal-y := exynos_tmu.o | |
6 | +exynos_thermal-$(CONFIG_EXYNOS_THERMAL_CORE) += exynos_thermal_common.o |
drivers/thermal/samsung/exynos_thermal.c
1 | -/* | |
2 | - * exynos_thermal.c - Samsung EXYNOS TMU (Thermal Management Unit) | |
3 | - * | |
4 | - * Copyright (C) 2011 Samsung Electronics | |
5 | - * Donggeun Kim <dg77.kim@samsung.com> | |
6 | - * Amit Daniel Kachhap <amit.kachhap@linaro.org> | |
7 | - * | |
8 | - * This program is free software; you can redistribute it and/or modify | |
9 | - * it under the terms of the GNU General Public License as published by | |
10 | - * the Free Software Foundation; either version 2 of the License, or | |
11 | - * (at your option) any later version. | |
12 | - * | |
13 | - * This program is distributed in the hope that it will be useful, | |
14 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | - * GNU General Public License for more details. | |
17 | - * | |
18 | - * You should have received a copy of the GNU General Public License | |
19 | - * along with this program; if not, write to the Free Software | |
20 | - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
21 | - * | |
22 | - */ | |
23 | - | |
24 | -#include <linux/clk.h> | |
25 | -#include <linux/io.h> | |
26 | -#include <linux/interrupt.h> | |
27 | -#include <linux/module.h> | |
28 | -#include <linux/of.h> | |
29 | -#include <linux/platform_device.h> | |
30 | -#include <linux/platform_data/exynos_thermal.h> | |
31 | - | |
32 | -#include "exynos_thermal_common.h" | |
33 | - | |
34 | -/* Exynos generic registers */ | |
35 | -#define EXYNOS_TMU_REG_TRIMINFO 0x0 | |
36 | -#define EXYNOS_TMU_REG_CONTROL 0x20 | |
37 | -#define EXYNOS_TMU_REG_STATUS 0x28 | |
38 | -#define EXYNOS_TMU_REG_CURRENT_TEMP 0x40 | |
39 | -#define EXYNOS_TMU_REG_INTEN 0x70 | |
40 | -#define EXYNOS_TMU_REG_INTSTAT 0x74 | |
41 | -#define EXYNOS_TMU_REG_INTCLEAR 0x78 | |
42 | - | |
43 | -#define EXYNOS_TMU_TRIM_TEMP_MASK 0xff | |
44 | -#define EXYNOS_TMU_GAIN_SHIFT 8 | |
45 | -#define EXYNOS_TMU_REF_VOLTAGE_SHIFT 24 | |
46 | -#define EXYNOS_TMU_CORE_ON 3 | |
47 | -#define EXYNOS_TMU_CORE_OFF 2 | |
48 | -#define EXYNOS_TMU_DEF_CODE_TO_TEMP_OFFSET 50 | |
49 | - | |
50 | -/* Exynos4210 specific registers */ | |
51 | -#define EXYNOS4210_TMU_REG_THRESHOLD_TEMP 0x44 | |
52 | -#define EXYNOS4210_TMU_REG_TRIG_LEVEL0 0x50 | |
53 | -#define EXYNOS4210_TMU_REG_TRIG_LEVEL1 0x54 | |
54 | -#define EXYNOS4210_TMU_REG_TRIG_LEVEL2 0x58 | |
55 | -#define EXYNOS4210_TMU_REG_TRIG_LEVEL3 0x5C | |
56 | -#define EXYNOS4210_TMU_REG_PAST_TEMP0 0x60 | |
57 | -#define EXYNOS4210_TMU_REG_PAST_TEMP1 0x64 | |
58 | -#define EXYNOS4210_TMU_REG_PAST_TEMP2 0x68 | |
59 | -#define EXYNOS4210_TMU_REG_PAST_TEMP3 0x6C | |
60 | - | |
61 | -#define EXYNOS4210_TMU_TRIG_LEVEL0_MASK 0x1 | |
62 | -#define EXYNOS4210_TMU_TRIG_LEVEL1_MASK 0x10 | |
63 | -#define EXYNOS4210_TMU_TRIG_LEVEL2_MASK 0x100 | |
64 | -#define EXYNOS4210_TMU_TRIG_LEVEL3_MASK 0x1000 | |
65 | -#define EXYNOS4210_TMU_INTCLEAR_VAL 0x1111 | |
66 | - | |
67 | -/* Exynos5250 and Exynos4412 specific registers */ | |
68 | -#define EXYNOS_TMU_TRIMINFO_CON 0x14 | |
69 | -#define EXYNOS_THD_TEMP_RISE 0x50 | |
70 | -#define EXYNOS_THD_TEMP_FALL 0x54 | |
71 | -#define EXYNOS_EMUL_CON 0x80 | |
72 | - | |
73 | -#define EXYNOS_TRIMINFO_RELOAD 0x1 | |
74 | -#define EXYNOS_TMU_CLEAR_RISE_INT 0x111 | |
75 | -#define EXYNOS_TMU_CLEAR_FALL_INT (0x111 << 12) | |
76 | -#define EXYNOS_MUX_ADDR_VALUE 6 | |
77 | -#define EXYNOS_MUX_ADDR_SHIFT 20 | |
78 | -#define EXYNOS_TMU_TRIP_MODE_SHIFT 13 | |
79 | - | |
80 | -#define EFUSE_MIN_VALUE 40 | |
81 | -#define EFUSE_MAX_VALUE 100 | |
82 | - | |
83 | -#ifdef CONFIG_THERMAL_EMULATION | |
84 | -#define EXYNOS_EMUL_TIME 0x57F0 | |
85 | -#define EXYNOS_EMUL_TIME_SHIFT 16 | |
86 | -#define EXYNOS_EMUL_DATA_SHIFT 8 | |
87 | -#define EXYNOS_EMUL_DATA_MASK 0xFF | |
88 | -#define EXYNOS_EMUL_ENABLE 0x1 | |
89 | -#endif /* CONFIG_THERMAL_EMULATION */ | |
90 | - | |
91 | -struct exynos_tmu_data { | |
92 | - struct exynos_tmu_platform_data *pdata; | |
93 | - struct resource *mem; | |
94 | - void __iomem *base; | |
95 | - int irq; | |
96 | - enum soc_type soc; | |
97 | - struct work_struct irq_work; | |
98 | - struct mutex lock; | |
99 | - struct clk *clk; | |
100 | - u8 temp_error1, temp_error2; | |
101 | -}; | |
102 | - | |
103 | -/* | |
104 | - * TMU treats temperature as a mapped temperature code. | |
105 | - * The temperature is converted differently depending on the calibration type. | |
106 | - */ | |
107 | -static int temp_to_code(struct exynos_tmu_data *data, u8 temp) | |
108 | -{ | |
109 | - struct exynos_tmu_platform_data *pdata = data->pdata; | |
110 | - int temp_code; | |
111 | - | |
112 | - if (data->soc == SOC_ARCH_EXYNOS4210) | |
113 | - /* temp should range between 25 and 125 */ | |
114 | - if (temp < 25 || temp > 125) { | |
115 | - temp_code = -EINVAL; | |
116 | - goto out; | |
117 | - } | |
118 | - | |
119 | - switch (pdata->cal_type) { | |
120 | - case TYPE_TWO_POINT_TRIMMING: | |
121 | - temp_code = (temp - 25) * | |
122 | - (data->temp_error2 - data->temp_error1) / | |
123 | - (85 - 25) + data->temp_error1; | |
124 | - break; | |
125 | - case TYPE_ONE_POINT_TRIMMING: | |
126 | - temp_code = temp + data->temp_error1 - 25; | |
127 | - break; | |
128 | - default: | |
129 | - temp_code = temp + EXYNOS_TMU_DEF_CODE_TO_TEMP_OFFSET; | |
130 | - break; | |
131 | - } | |
132 | -out: | |
133 | - return temp_code; | |
134 | -} | |
135 | - | |
136 | -/* | |
137 | - * Calculate a temperature value from a temperature code. | |
138 | - * The unit of the temperature is degree Celsius. | |
139 | - */ | |
140 | -static int code_to_temp(struct exynos_tmu_data *data, u8 temp_code) | |
141 | -{ | |
142 | - struct exynos_tmu_platform_data *pdata = data->pdata; | |
143 | - int temp; | |
144 | - | |
145 | - if (data->soc == SOC_ARCH_EXYNOS4210) | |
146 | - /* temp_code should range between 75 and 175 */ | |
147 | - if (temp_code < 75 || temp_code > 175) { | |
148 | - temp = -ENODATA; | |
149 | - goto out; | |
150 | - } | |
151 | - | |
152 | - switch (pdata->cal_type) { | |
153 | - case TYPE_TWO_POINT_TRIMMING: | |
154 | - temp = (temp_code - data->temp_error1) * (85 - 25) / | |
155 | - (data->temp_error2 - data->temp_error1) + 25; | |
156 | - break; | |
157 | - case TYPE_ONE_POINT_TRIMMING: | |
158 | - temp = temp_code - data->temp_error1 + 25; | |
159 | - break; | |
160 | - default: | |
161 | - temp = temp_code - EXYNOS_TMU_DEF_CODE_TO_TEMP_OFFSET; | |
162 | - break; | |
163 | - } | |
164 | -out: | |
165 | - return temp; | |
166 | -} | |
167 | - | |
168 | -static int exynos_tmu_initialize(struct platform_device *pdev) | |
169 | -{ | |
170 | - struct exynos_tmu_data *data = platform_get_drvdata(pdev); | |
171 | - struct exynos_tmu_platform_data *pdata = data->pdata; | |
172 | - unsigned int status, trim_info; | |
173 | - unsigned int rising_threshold = 0, falling_threshold = 0; | |
174 | - int ret = 0, threshold_code, i, trigger_levs = 0; | |
175 | - | |
176 | - mutex_lock(&data->lock); | |
177 | - clk_enable(data->clk); | |
178 | - | |
179 | - status = readb(data->base + EXYNOS_TMU_REG_STATUS); | |
180 | - if (!status) { | |
181 | - ret = -EBUSY; | |
182 | - goto out; | |
183 | - } | |
184 | - | |
185 | - if (data->soc == SOC_ARCH_EXYNOS) { | |
186 | - __raw_writel(EXYNOS_TRIMINFO_RELOAD, | |
187 | - data->base + EXYNOS_TMU_TRIMINFO_CON); | |
188 | - } | |
189 | - /* Save trimming info in order to perform calibration */ | |
190 | - trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); | |
191 | - data->temp_error1 = trim_info & EXYNOS_TMU_TRIM_TEMP_MASK; | |
192 | - data->temp_error2 = ((trim_info >> 8) & EXYNOS_TMU_TRIM_TEMP_MASK); | |
193 | - | |
194 | - if ((EFUSE_MIN_VALUE > data->temp_error1) || | |
195 | - (data->temp_error1 > EFUSE_MAX_VALUE) || | |
196 | - (data->temp_error2 != 0)) | |
197 | - data->temp_error1 = pdata->efuse_value; | |
198 | - | |
199 | - /* Count trigger levels to be enabled */ | |
200 | - for (i = 0; i < MAX_THRESHOLD_LEVS; i++) | |
201 | - if (pdata->trigger_levels[i]) | |
202 | - trigger_levs++; | |
203 | - | |
204 | - if (data->soc == SOC_ARCH_EXYNOS4210) { | |
205 | - /* Write temperature code for threshold */ | |
206 | - threshold_code = temp_to_code(data, pdata->threshold); | |
207 | - if (threshold_code < 0) { | |
208 | - ret = threshold_code; | |
209 | - goto out; | |
210 | - } | |
211 | - writeb(threshold_code, | |
212 | - data->base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP); | |
213 | - for (i = 0; i < trigger_levs; i++) | |
214 | - writeb(pdata->trigger_levels[i], | |
215 | - data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL0 + i * 4); | |
216 | - | |
217 | - writel(EXYNOS4210_TMU_INTCLEAR_VAL, | |
218 | - data->base + EXYNOS_TMU_REG_INTCLEAR); | |
219 | - } else if (data->soc == SOC_ARCH_EXYNOS) { | |
220 | - /* Write temperature code for rising and falling threshold */ | |
221 | - for (i = 0; i < trigger_levs; i++) { | |
222 | - threshold_code = temp_to_code(data, | |
223 | - pdata->trigger_levels[i]); | |
224 | - if (threshold_code < 0) { | |
225 | - ret = threshold_code; | |
226 | - goto out; | |
227 | - } | |
228 | - rising_threshold |= threshold_code << 8 * i; | |
229 | - if (pdata->threshold_falling) { | |
230 | - threshold_code = temp_to_code(data, | |
231 | - pdata->trigger_levels[i] - | |
232 | - pdata->threshold_falling); | |
233 | - if (threshold_code > 0) | |
234 | - falling_threshold |= | |
235 | - threshold_code << 8 * i; | |
236 | - } | |
237 | - } | |
238 | - | |
239 | - writel(rising_threshold, | |
240 | - data->base + EXYNOS_THD_TEMP_RISE); | |
241 | - writel(falling_threshold, | |
242 | - data->base + EXYNOS_THD_TEMP_FALL); | |
243 | - | |
244 | - writel(EXYNOS_TMU_CLEAR_RISE_INT | EXYNOS_TMU_CLEAR_FALL_INT, | |
245 | - data->base + EXYNOS_TMU_REG_INTCLEAR); | |
246 | - } | |
247 | -out: | |
248 | - clk_disable(data->clk); | |
249 | - mutex_unlock(&data->lock); | |
250 | - | |
251 | - return ret; | |
252 | -} | |
253 | - | |
254 | -static void exynos_tmu_control(struct platform_device *pdev, bool on) | |
255 | -{ | |
256 | - struct exynos_tmu_data *data = platform_get_drvdata(pdev); | |
257 | - struct exynos_tmu_platform_data *pdata = data->pdata; | |
258 | - unsigned int con, interrupt_en; | |
259 | - | |
260 | - mutex_lock(&data->lock); | |
261 | - clk_enable(data->clk); | |
262 | - | |
263 | - con = pdata->reference_voltage << EXYNOS_TMU_REF_VOLTAGE_SHIFT | | |
264 | - pdata->gain << EXYNOS_TMU_GAIN_SHIFT; | |
265 | - | |
266 | - if (data->soc == SOC_ARCH_EXYNOS) { | |
267 | - con |= pdata->noise_cancel_mode << EXYNOS_TMU_TRIP_MODE_SHIFT; | |
268 | - con |= (EXYNOS_MUX_ADDR_VALUE << EXYNOS_MUX_ADDR_SHIFT); | |
269 | - } | |
270 | - | |
271 | - if (on) { | |
272 | - con |= EXYNOS_TMU_CORE_ON; | |
273 | - interrupt_en = pdata->trigger_level3_en << 12 | | |
274 | - pdata->trigger_level2_en << 8 | | |
275 | - pdata->trigger_level1_en << 4 | | |
276 | - pdata->trigger_level0_en; | |
277 | - if (pdata->threshold_falling) | |
278 | - interrupt_en |= interrupt_en << 16; | |
279 | - } else { | |
280 | - con |= EXYNOS_TMU_CORE_OFF; | |
281 | - interrupt_en = 0; /* Disable all interrupts */ | |
282 | - } | |
283 | - writel(interrupt_en, data->base + EXYNOS_TMU_REG_INTEN); | |
284 | - writel(con, data->base + EXYNOS_TMU_REG_CONTROL); | |
285 | - | |
286 | - clk_disable(data->clk); | |
287 | - mutex_unlock(&data->lock); | |
288 | -} | |
289 | - | |
290 | -static int exynos_tmu_read(struct exynos_tmu_data *data) | |
291 | -{ | |
292 | - u8 temp_code; | |
293 | - int temp; | |
294 | - | |
295 | - mutex_lock(&data->lock); | |
296 | - clk_enable(data->clk); | |
297 | - | |
298 | - temp_code = readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP); | |
299 | - temp = code_to_temp(data, temp_code); | |
300 | - | |
301 | - clk_disable(data->clk); | |
302 | - mutex_unlock(&data->lock); | |
303 | - | |
304 | - return temp; | |
305 | -} | |
306 | - | |
307 | -#ifdef CONFIG_THERMAL_EMULATION | |
308 | -static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp) | |
309 | -{ | |
310 | - struct exynos_tmu_data *data = drv_data; | |
311 | - unsigned int reg; | |
312 | - int ret = -EINVAL; | |
313 | - | |
314 | - if (data->soc == SOC_ARCH_EXYNOS4210) | |
315 | - goto out; | |
316 | - | |
317 | - if (temp && temp < MCELSIUS) | |
318 | - goto out; | |
319 | - | |
320 | - mutex_lock(&data->lock); | |
321 | - clk_enable(data->clk); | |
322 | - | |
323 | - reg = readl(data->base + EXYNOS_EMUL_CON); | |
324 | - | |
325 | - if (temp) { | |
326 | - temp /= MCELSIUS; | |
327 | - | |
328 | - reg = (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT) | | |
329 | - (temp_to_code(data, temp) | |
330 | - << EXYNOS_EMUL_DATA_SHIFT) | EXYNOS_EMUL_ENABLE; | |
331 | - } else { | |
332 | - reg &= ~EXYNOS_EMUL_ENABLE; | |
333 | - } | |
334 | - | |
335 | - writel(reg, data->base + EXYNOS_EMUL_CON); | |
336 | - | |
337 | - clk_disable(data->clk); | |
338 | - mutex_unlock(&data->lock); | |
339 | - return 0; | |
340 | -out: | |
341 | - return ret; | |
342 | -} | |
343 | -#else | |
344 | -static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp) | |
345 | - { return -EINVAL; } | |
346 | -#endif/*CONFIG_THERMAL_EMULATION*/ | |
347 | - | |
348 | -static void exynos_tmu_work(struct work_struct *work) | |
349 | -{ | |
350 | - struct exynos_tmu_data *data = container_of(work, | |
351 | - struct exynos_tmu_data, irq_work); | |
352 | - | |
353 | - exynos_report_trigger(); | |
354 | - mutex_lock(&data->lock); | |
355 | - clk_enable(data->clk); | |
356 | - if (data->soc == SOC_ARCH_EXYNOS) | |
357 | - writel(EXYNOS_TMU_CLEAR_RISE_INT | | |
358 | - EXYNOS_TMU_CLEAR_FALL_INT, | |
359 | - data->base + EXYNOS_TMU_REG_INTCLEAR); | |
360 | - else | |
361 | - writel(EXYNOS4210_TMU_INTCLEAR_VAL, | |
362 | - data->base + EXYNOS_TMU_REG_INTCLEAR); | |
363 | - clk_disable(data->clk); | |
364 | - mutex_unlock(&data->lock); | |
365 | - | |
366 | - enable_irq(data->irq); | |
367 | -} | |
368 | - | |
369 | -static irqreturn_t exynos_tmu_irq(int irq, void *id) | |
370 | -{ | |
371 | - struct exynos_tmu_data *data = id; | |
372 | - | |
373 | - disable_irq_nosync(irq); | |
374 | - schedule_work(&data->irq_work); | |
375 | - | |
376 | - return IRQ_HANDLED; | |
377 | -} | |
378 | -static struct thermal_sensor_conf exynos_sensor_conf = { | |
379 | - .name = "exynos-therm", | |
380 | - .read_temperature = (int (*)(void *))exynos_tmu_read, | |
381 | - .write_emul_temp = exynos_tmu_set_emulation, | |
382 | -}; | |
383 | - | |
384 | -#if defined(CONFIG_CPU_EXYNOS4210) | |
385 | -static struct exynos_tmu_platform_data const exynos4210_default_tmu_data = { | |
386 | - .threshold = 80, | |
387 | - .trigger_levels[0] = 5, | |
388 | - .trigger_levels[1] = 20, | |
389 | - .trigger_levels[2] = 30, | |
390 | - .trigger_level0_en = 1, | |
391 | - .trigger_level1_en = 1, | |
392 | - .trigger_level2_en = 1, | |
393 | - .trigger_level3_en = 0, | |
394 | - .gain = 15, | |
395 | - .reference_voltage = 7, | |
396 | - .cal_type = TYPE_ONE_POINT_TRIMMING, | |
397 | - .freq_tab[0] = { | |
398 | - .freq_clip_max = 800 * 1000, | |
399 | - .temp_level = 85, | |
400 | - }, | |
401 | - .freq_tab[1] = { | |
402 | - .freq_clip_max = 200 * 1000, | |
403 | - .temp_level = 100, | |
404 | - }, | |
405 | - .freq_tab_count = 2, | |
406 | - .type = SOC_ARCH_EXYNOS4210, | |
407 | -}; | |
408 | -#define EXYNOS4210_TMU_DRV_DATA (&exynos4210_default_tmu_data) | |
409 | -#else | |
410 | -#define EXYNOS4210_TMU_DRV_DATA (NULL) | |
411 | -#endif | |
412 | - | |
413 | -#if defined(CONFIG_SOC_EXYNOS5250) || defined(CONFIG_SOC_EXYNOS4412) || \ | |
414 | - defined(CONFIG_SOC_EXYNOS4212) | |
415 | -static struct exynos_tmu_platform_data const exynos_default_tmu_data = { | |
416 | - .threshold_falling = 10, | |
417 | - .trigger_levels[0] = 85, | |
418 | - .trigger_levels[1] = 103, | |
419 | - .trigger_levels[2] = 110, | |
420 | - .trigger_level0_en = 1, | |
421 | - .trigger_level1_en = 1, | |
422 | - .trigger_level2_en = 1, | |
423 | - .trigger_level3_en = 0, | |
424 | - .gain = 8, | |
425 | - .reference_voltage = 16, | |
426 | - .noise_cancel_mode = 4, | |
427 | - .cal_type = TYPE_ONE_POINT_TRIMMING, | |
428 | - .efuse_value = 55, | |
429 | - .freq_tab[0] = { | |
430 | - .freq_clip_max = 800 * 1000, | |
431 | - .temp_level = 85, | |
432 | - }, | |
433 | - .freq_tab[1] = { | |
434 | - .freq_clip_max = 200 * 1000, | |
435 | - .temp_level = 103, | |
436 | - }, | |
437 | - .freq_tab_count = 2, | |
438 | - .type = SOC_ARCH_EXYNOS, | |
439 | -}; | |
440 | -#define EXYNOS_TMU_DRV_DATA (&exynos_default_tmu_data) | |
441 | -#else | |
442 | -#define EXYNOS_TMU_DRV_DATA (NULL) | |
443 | -#endif | |
444 | - | |
445 | -#ifdef CONFIG_OF | |
446 | -static const struct of_device_id exynos_tmu_match[] = { | |
447 | - { | |
448 | - .compatible = "samsung,exynos4210-tmu", | |
449 | - .data = (void *)EXYNOS4210_TMU_DRV_DATA, | |
450 | - }, | |
451 | - { | |
452 | - .compatible = "samsung,exynos4412-tmu", | |
453 | - .data = (void *)EXYNOS_TMU_DRV_DATA, | |
454 | - }, | |
455 | - { | |
456 | - .compatible = "samsung,exynos5250-tmu", | |
457 | - .data = (void *)EXYNOS_TMU_DRV_DATA, | |
458 | - }, | |
459 | - {}, | |
460 | -}; | |
461 | -MODULE_DEVICE_TABLE(of, exynos_tmu_match); | |
462 | -#endif | |
463 | - | |
464 | -static struct platform_device_id exynos_tmu_driver_ids[] = { | |
465 | - { | |
466 | - .name = "exynos4210-tmu", | |
467 | - .driver_data = (kernel_ulong_t)EXYNOS4210_TMU_DRV_DATA, | |
468 | - }, | |
469 | - { | |
470 | - .name = "exynos5250-tmu", | |
471 | - .driver_data = (kernel_ulong_t)EXYNOS_TMU_DRV_DATA, | |
472 | - }, | |
473 | - { }, | |
474 | -}; | |
475 | -MODULE_DEVICE_TABLE(platform, exynos_tmu_driver_ids); | |
476 | - | |
477 | -static inline struct exynos_tmu_platform_data *exynos_get_driver_data( | |
478 | - struct platform_device *pdev) | |
479 | -{ | |
480 | -#ifdef CONFIG_OF | |
481 | - if (pdev->dev.of_node) { | |
482 | - const struct of_device_id *match; | |
483 | - match = of_match_node(exynos_tmu_match, pdev->dev.of_node); | |
484 | - if (!match) | |
485 | - return NULL; | |
486 | - return (struct exynos_tmu_platform_data *) match->data; | |
487 | - } | |
488 | -#endif | |
489 | - return (struct exynos_tmu_platform_data *) | |
490 | - platform_get_device_id(pdev)->driver_data; | |
491 | -} | |
492 | - | |
493 | -static int exynos_tmu_probe(struct platform_device *pdev) | |
494 | -{ | |
495 | - struct exynos_tmu_data *data; | |
496 | - struct exynos_tmu_platform_data *pdata = pdev->dev.platform_data; | |
497 | - int ret, i; | |
498 | - | |
499 | - if (!pdata) | |
500 | - pdata = exynos_get_driver_data(pdev); | |
501 | - | |
502 | - if (!pdata) { | |
503 | - dev_err(&pdev->dev, "No platform init data supplied.\n"); | |
504 | - return -ENODEV; | |
505 | - } | |
506 | - data = devm_kzalloc(&pdev->dev, sizeof(struct exynos_tmu_data), | |
507 | - GFP_KERNEL); | |
508 | - if (!data) { | |
509 | - dev_err(&pdev->dev, "Failed to allocate driver structure\n"); | |
510 | - return -ENOMEM; | |
511 | - } | |
512 | - | |
513 | - data->irq = platform_get_irq(pdev, 0); | |
514 | - if (data->irq < 0) { | |
515 | - dev_err(&pdev->dev, "Failed to get platform irq\n"); | |
516 | - return data->irq; | |
517 | - } | |
518 | - | |
519 | - INIT_WORK(&data->irq_work, exynos_tmu_work); | |
520 | - | |
521 | - data->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
522 | - data->base = devm_ioremap_resource(&pdev->dev, data->mem); | |
523 | - if (IS_ERR(data->base)) | |
524 | - return PTR_ERR(data->base); | |
525 | - | |
526 | - ret = devm_request_irq(&pdev->dev, data->irq, exynos_tmu_irq, | |
527 | - IRQF_TRIGGER_RISING, "exynos-tmu", data); | |
528 | - if (ret) { | |
529 | - dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq); | |
530 | - return ret; | |
531 | - } | |
532 | - | |
533 | - data->clk = devm_clk_get(&pdev->dev, "tmu_apbif"); | |
534 | - if (IS_ERR(data->clk)) { | |
535 | - dev_err(&pdev->dev, "Failed to get clock\n"); | |
536 | - return PTR_ERR(data->clk); | |
537 | - } | |
538 | - | |
539 | - ret = clk_prepare(data->clk); | |
540 | - if (ret) | |
541 | - return ret; | |
542 | - | |
543 | - if (pdata->type == SOC_ARCH_EXYNOS || | |
544 | - pdata->type == SOC_ARCH_EXYNOS4210) | |
545 | - data->soc = pdata->type; | |
546 | - else { | |
547 | - ret = -EINVAL; | |
548 | - dev_err(&pdev->dev, "Platform not supported\n"); | |
549 | - goto err_clk; | |
550 | - } | |
551 | - | |
552 | - data->pdata = pdata; | |
553 | - platform_set_drvdata(pdev, data); | |
554 | - mutex_init(&data->lock); | |
555 | - | |
556 | - ret = exynos_tmu_initialize(pdev); | |
557 | - if (ret) { | |
558 | - dev_err(&pdev->dev, "Failed to initialize TMU\n"); | |
559 | - goto err_clk; | |
560 | - } | |
561 | - | |
562 | - exynos_tmu_control(pdev, true); | |
563 | - | |
564 | - /* Register the sensor with thermal management interface */ | |
565 | - (&exynos_sensor_conf)->private_data = data; | |
566 | - exynos_sensor_conf.trip_data.trip_count = pdata->trigger_level0_en + | |
567 | - pdata->trigger_level1_en + pdata->trigger_level2_en + | |
568 | - pdata->trigger_level3_en; | |
569 | - | |
570 | - for (i = 0; i < exynos_sensor_conf.trip_data.trip_count; i++) | |
571 | - exynos_sensor_conf.trip_data.trip_val[i] = | |
572 | - pdata->threshold + pdata->trigger_levels[i]; | |
573 | - | |
574 | - exynos_sensor_conf.trip_data.trigger_falling = pdata->threshold_falling; | |
575 | - | |
576 | - exynos_sensor_conf.cooling_data.freq_clip_count = | |
577 | - pdata->freq_tab_count; | |
578 | - for (i = 0; i < pdata->freq_tab_count; i++) { | |
579 | - exynos_sensor_conf.cooling_data.freq_data[i].freq_clip_max = | |
580 | - pdata->freq_tab[i].freq_clip_max; | |
581 | - exynos_sensor_conf.cooling_data.freq_data[i].temp_level = | |
582 | - pdata->freq_tab[i].temp_level; | |
583 | - } | |
584 | - | |
585 | - ret = exynos_register_thermal(&exynos_sensor_conf); | |
586 | - if (ret) { | |
587 | - dev_err(&pdev->dev, "Failed to register thermal interface\n"); | |
588 | - goto err_clk; | |
589 | - } | |
590 | - | |
591 | - return 0; | |
592 | -err_clk: | |
593 | - clk_unprepare(data->clk); | |
594 | - return ret; | |
595 | -} | |
596 | - | |
597 | -static int exynos_tmu_remove(struct platform_device *pdev) | |
598 | -{ | |
599 | - struct exynos_tmu_data *data = platform_get_drvdata(pdev); | |
600 | - | |
601 | - exynos_tmu_control(pdev, false); | |
602 | - | |
603 | - exynos_unregister_thermal(); | |
604 | - | |
605 | - clk_unprepare(data->clk); | |
606 | - | |
607 | - return 0; | |
608 | -} | |
609 | - | |
610 | -#ifdef CONFIG_PM_SLEEP | |
611 | -static int exynos_tmu_suspend(struct device *dev) | |
612 | -{ | |
613 | - exynos_tmu_control(to_platform_device(dev), false); | |
614 | - | |
615 | - return 0; | |
616 | -} | |
617 | - | |
618 | -static int exynos_tmu_resume(struct device *dev) | |
619 | -{ | |
620 | - struct platform_device *pdev = to_platform_device(dev); | |
621 | - | |
622 | - exynos_tmu_initialize(pdev); | |
623 | - exynos_tmu_control(pdev, true); | |
624 | - | |
625 | - return 0; | |
626 | -} | |
627 | - | |
628 | -static SIMPLE_DEV_PM_OPS(exynos_tmu_pm, | |
629 | - exynos_tmu_suspend, exynos_tmu_resume); | |
630 | -#define EXYNOS_TMU_PM (&exynos_tmu_pm) | |
631 | -#else | |
632 | -#define EXYNOS_TMU_PM NULL | |
633 | -#endif | |
634 | - | |
635 | -static struct platform_driver exynos_tmu_driver = { | |
636 | - .driver = { | |
637 | - .name = "exynos-tmu", | |
638 | - .owner = THIS_MODULE, | |
639 | - .pm = EXYNOS_TMU_PM, | |
640 | - .of_match_table = of_match_ptr(exynos_tmu_match), | |
641 | - }, | |
642 | - .probe = exynos_tmu_probe, | |
643 | - .remove = exynos_tmu_remove, | |
644 | - .id_table = exynos_tmu_driver_ids, | |
645 | -}; | |
646 | - | |
647 | -module_platform_driver(exynos_tmu_driver); | |
648 | - | |
649 | -MODULE_DESCRIPTION("EXYNOS TMU Driver"); | |
650 | -MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>"); | |
651 | -MODULE_LICENSE("GPL"); | |
652 | -MODULE_ALIAS("platform:exynos-tmu"); |
drivers/thermal/samsung/exynos_tmu.c
1 | +/* | |
2 | + * exynos_tmu.c - Samsung EXYNOS TMU (Thermal Management Unit) | |
3 | + * | |
4 | + * Copyright (C) 2011 Samsung Electronics | |
5 | + * Donggeun Kim <dg77.kim@samsung.com> | |
6 | + * Amit Daniel Kachhap <amit.kachhap@linaro.org> | |
7 | + * | |
8 | + * This program is free software; you can redistribute it and/or modify | |
9 | + * it under the terms of the GNU General Public License as published by | |
10 | + * the Free Software Foundation; either version 2 of the License, or | |
11 | + * (at your option) any later version. | |
12 | + * | |
13 | + * This program is distributed in the hope that it will be useful, | |
14 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | + * GNU General Public License for more details. | |
17 | + * | |
18 | + * You should have received a copy of the GNU General Public License | |
19 | + * along with this program; if not, write to the Free Software | |
20 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
21 | + * | |
22 | + */ | |
23 | + | |
24 | +#include <linux/clk.h> | |
25 | +#include <linux/io.h> | |
26 | +#include <linux/interrupt.h> | |
27 | +#include <linux/module.h> | |
28 | +#include <linux/of.h> | |
29 | +#include <linux/platform_device.h> | |
30 | +#include <linux/platform_data/exynos_thermal.h> | |
31 | + | |
32 | +#include "exynos_thermal_common.h" | |
33 | + | |
34 | +/* Exynos generic registers */ | |
35 | +#define EXYNOS_TMU_REG_TRIMINFO 0x0 | |
36 | +#define EXYNOS_TMU_REG_CONTROL 0x20 | |
37 | +#define EXYNOS_TMU_REG_STATUS 0x28 | |
38 | +#define EXYNOS_TMU_REG_CURRENT_TEMP 0x40 | |
39 | +#define EXYNOS_TMU_REG_INTEN 0x70 | |
40 | +#define EXYNOS_TMU_REG_INTSTAT 0x74 | |
41 | +#define EXYNOS_TMU_REG_INTCLEAR 0x78 | |
42 | + | |
43 | +#define EXYNOS_TMU_TRIM_TEMP_MASK 0xff | |
44 | +#define EXYNOS_TMU_GAIN_SHIFT 8 | |
45 | +#define EXYNOS_TMU_REF_VOLTAGE_SHIFT 24 | |
46 | +#define EXYNOS_TMU_CORE_ON 3 | |
47 | +#define EXYNOS_TMU_CORE_OFF 2 | |
48 | +#define EXYNOS_TMU_DEF_CODE_TO_TEMP_OFFSET 50 | |
49 | + | |
50 | +/* Exynos4210 specific registers */ | |
51 | +#define EXYNOS4210_TMU_REG_THRESHOLD_TEMP 0x44 | |
52 | +#define EXYNOS4210_TMU_REG_TRIG_LEVEL0 0x50 | |
53 | +#define EXYNOS4210_TMU_REG_TRIG_LEVEL1 0x54 | |
54 | +#define EXYNOS4210_TMU_REG_TRIG_LEVEL2 0x58 | |
55 | +#define EXYNOS4210_TMU_REG_TRIG_LEVEL3 0x5C | |
56 | +#define EXYNOS4210_TMU_REG_PAST_TEMP0 0x60 | |
57 | +#define EXYNOS4210_TMU_REG_PAST_TEMP1 0x64 | |
58 | +#define EXYNOS4210_TMU_REG_PAST_TEMP2 0x68 | |
59 | +#define EXYNOS4210_TMU_REG_PAST_TEMP3 0x6C | |
60 | + | |
61 | +#define EXYNOS4210_TMU_TRIG_LEVEL0_MASK 0x1 | |
62 | +#define EXYNOS4210_TMU_TRIG_LEVEL1_MASK 0x10 | |
63 | +#define EXYNOS4210_TMU_TRIG_LEVEL2_MASK 0x100 | |
64 | +#define EXYNOS4210_TMU_TRIG_LEVEL3_MASK 0x1000 | |
65 | +#define EXYNOS4210_TMU_INTCLEAR_VAL 0x1111 | |
66 | + | |
67 | +/* Exynos5250 and Exynos4412 specific registers */ | |
68 | +#define EXYNOS_TMU_TRIMINFO_CON 0x14 | |
69 | +#define EXYNOS_THD_TEMP_RISE 0x50 | |
70 | +#define EXYNOS_THD_TEMP_FALL 0x54 | |
71 | +#define EXYNOS_EMUL_CON 0x80 | |
72 | + | |
73 | +#define EXYNOS_TRIMINFO_RELOAD 0x1 | |
74 | +#define EXYNOS_TMU_CLEAR_RISE_INT 0x111 | |
75 | +#define EXYNOS_TMU_CLEAR_FALL_INT (0x111 << 12) | |
76 | +#define EXYNOS_MUX_ADDR_VALUE 6 | |
77 | +#define EXYNOS_MUX_ADDR_SHIFT 20 | |
78 | +#define EXYNOS_TMU_TRIP_MODE_SHIFT 13 | |
79 | + | |
80 | +#define EFUSE_MIN_VALUE 40 | |
81 | +#define EFUSE_MAX_VALUE 100 | |
82 | + | |
83 | +#ifdef CONFIG_THERMAL_EMULATION | |
84 | +#define EXYNOS_EMUL_TIME 0x57F0 | |
85 | +#define EXYNOS_EMUL_TIME_SHIFT 16 | |
86 | +#define EXYNOS_EMUL_DATA_SHIFT 8 | |
87 | +#define EXYNOS_EMUL_DATA_MASK 0xFF | |
88 | +#define EXYNOS_EMUL_ENABLE 0x1 | |
89 | +#endif /* CONFIG_THERMAL_EMULATION */ | |
90 | + | |
91 | +struct exynos_tmu_data { | |
92 | + struct exynos_tmu_platform_data *pdata; | |
93 | + struct resource *mem; | |
94 | + void __iomem *base; | |
95 | + int irq; | |
96 | + enum soc_type soc; | |
97 | + struct work_struct irq_work; | |
98 | + struct mutex lock; | |
99 | + struct clk *clk; | |
100 | + u8 temp_error1, temp_error2; | |
101 | +}; | |
102 | + | |
103 | +/* | |
104 | + * TMU treats temperature as a mapped temperature code. | |
105 | + * The temperature is converted differently depending on the calibration type. | |
106 | + */ | |
107 | +static int temp_to_code(struct exynos_tmu_data *data, u8 temp) | |
108 | +{ | |
109 | + struct exynos_tmu_platform_data *pdata = data->pdata; | |
110 | + int temp_code; | |
111 | + | |
112 | + if (data->soc == SOC_ARCH_EXYNOS4210) | |
113 | + /* temp should range between 25 and 125 */ | |
114 | + if (temp < 25 || temp > 125) { | |
115 | + temp_code = -EINVAL; | |
116 | + goto out; | |
117 | + } | |
118 | + | |
119 | + switch (pdata->cal_type) { | |
120 | + case TYPE_TWO_POINT_TRIMMING: | |
121 | + temp_code = (temp - 25) * | |
122 | + (data->temp_error2 - data->temp_error1) / | |
123 | + (85 - 25) + data->temp_error1; | |
124 | + break; | |
125 | + case TYPE_ONE_POINT_TRIMMING: | |
126 | + temp_code = temp + data->temp_error1 - 25; | |
127 | + break; | |
128 | + default: | |
129 | + temp_code = temp + EXYNOS_TMU_DEF_CODE_TO_TEMP_OFFSET; | |
130 | + break; | |
131 | + } | |
132 | +out: | |
133 | + return temp_code; | |
134 | +} | |
135 | + | |
136 | +/* | |
137 | + * Calculate a temperature value from a temperature code. | |
138 | + * The unit of the temperature is degree Celsius. | |
139 | + */ | |
140 | +static int code_to_temp(struct exynos_tmu_data *data, u8 temp_code) | |
141 | +{ | |
142 | + struct exynos_tmu_platform_data *pdata = data->pdata; | |
143 | + int temp; | |
144 | + | |
145 | + if (data->soc == SOC_ARCH_EXYNOS4210) | |
146 | + /* temp_code should range between 75 and 175 */ | |
147 | + if (temp_code < 75 || temp_code > 175) { | |
148 | + temp = -ENODATA; | |
149 | + goto out; | |
150 | + } | |
151 | + | |
152 | + switch (pdata->cal_type) { | |
153 | + case TYPE_TWO_POINT_TRIMMING: | |
154 | + temp = (temp_code - data->temp_error1) * (85 - 25) / | |
155 | + (data->temp_error2 - data->temp_error1) + 25; | |
156 | + break; | |
157 | + case TYPE_ONE_POINT_TRIMMING: | |
158 | + temp = temp_code - data->temp_error1 + 25; | |
159 | + break; | |
160 | + default: | |
161 | + temp = temp_code - EXYNOS_TMU_DEF_CODE_TO_TEMP_OFFSET; | |
162 | + break; | |
163 | + } | |
164 | +out: | |
165 | + return temp; | |
166 | +} | |
167 | + | |
168 | +static int exynos_tmu_initialize(struct platform_device *pdev) | |
169 | +{ | |
170 | + struct exynos_tmu_data *data = platform_get_drvdata(pdev); | |
171 | + struct exynos_tmu_platform_data *pdata = data->pdata; | |
172 | + unsigned int status, trim_info; | |
173 | + unsigned int rising_threshold = 0, falling_threshold = 0; | |
174 | + int ret = 0, threshold_code, i, trigger_levs = 0; | |
175 | + | |
176 | + mutex_lock(&data->lock); | |
177 | + clk_enable(data->clk); | |
178 | + | |
179 | + status = readb(data->base + EXYNOS_TMU_REG_STATUS); | |
180 | + if (!status) { | |
181 | + ret = -EBUSY; | |
182 | + goto out; | |
183 | + } | |
184 | + | |
185 | + if (data->soc == SOC_ARCH_EXYNOS) { | |
186 | + __raw_writel(EXYNOS_TRIMINFO_RELOAD, | |
187 | + data->base + EXYNOS_TMU_TRIMINFO_CON); | |
188 | + } | |
189 | + /* Save trimming info in order to perform calibration */ | |
190 | + trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); | |
191 | + data->temp_error1 = trim_info & EXYNOS_TMU_TRIM_TEMP_MASK; | |
192 | + data->temp_error2 = ((trim_info >> 8) & EXYNOS_TMU_TRIM_TEMP_MASK); | |
193 | + | |
194 | + if ((EFUSE_MIN_VALUE > data->temp_error1) || | |
195 | + (data->temp_error1 > EFUSE_MAX_VALUE) || | |
196 | + (data->temp_error2 != 0)) | |
197 | + data->temp_error1 = pdata->efuse_value; | |
198 | + | |
199 | + /* Count trigger levels to be enabled */ | |
200 | + for (i = 0; i < MAX_THRESHOLD_LEVS; i++) | |
201 | + if (pdata->trigger_levels[i]) | |
202 | + trigger_levs++; | |
203 | + | |
204 | + if (data->soc == SOC_ARCH_EXYNOS4210) { | |
205 | + /* Write temperature code for threshold */ | |
206 | + threshold_code = temp_to_code(data, pdata->threshold); | |
207 | + if (threshold_code < 0) { | |
208 | + ret = threshold_code; | |
209 | + goto out; | |
210 | + } | |
211 | + writeb(threshold_code, | |
212 | + data->base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP); | |
213 | + for (i = 0; i < trigger_levs; i++) | |
214 | + writeb(pdata->trigger_levels[i], | |
215 | + data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL0 + i * 4); | |
216 | + | |
217 | + writel(EXYNOS4210_TMU_INTCLEAR_VAL, | |
218 | + data->base + EXYNOS_TMU_REG_INTCLEAR); | |
219 | + } else if (data->soc == SOC_ARCH_EXYNOS) { | |
220 | + /* Write temperature code for rising and falling threshold */ | |
221 | + for (i = 0; i < trigger_levs; i++) { | |
222 | + threshold_code = temp_to_code(data, | |
223 | + pdata->trigger_levels[i]); | |
224 | + if (threshold_code < 0) { | |
225 | + ret = threshold_code; | |
226 | + goto out; | |
227 | + } | |
228 | + rising_threshold |= threshold_code << 8 * i; | |
229 | + if (pdata->threshold_falling) { | |
230 | + threshold_code = temp_to_code(data, | |
231 | + pdata->trigger_levels[i] - | |
232 | + pdata->threshold_falling); | |
233 | + if (threshold_code > 0) | |
234 | + falling_threshold |= | |
235 | + threshold_code << 8 * i; | |
236 | + } | |
237 | + } | |
238 | + | |
239 | + writel(rising_threshold, | |
240 | + data->base + EXYNOS_THD_TEMP_RISE); | |
241 | + writel(falling_threshold, | |
242 | + data->base + EXYNOS_THD_TEMP_FALL); | |
243 | + | |
244 | + writel(EXYNOS_TMU_CLEAR_RISE_INT | EXYNOS_TMU_CLEAR_FALL_INT, | |
245 | + data->base + EXYNOS_TMU_REG_INTCLEAR); | |
246 | + } | |
247 | +out: | |
248 | + clk_disable(data->clk); | |
249 | + mutex_unlock(&data->lock); | |
250 | + | |
251 | + return ret; | |
252 | +} | |
253 | + | |
254 | +static void exynos_tmu_control(struct platform_device *pdev, bool on) | |
255 | +{ | |
256 | + struct exynos_tmu_data *data = platform_get_drvdata(pdev); | |
257 | + struct exynos_tmu_platform_data *pdata = data->pdata; | |
258 | + unsigned int con, interrupt_en; | |
259 | + | |
260 | + mutex_lock(&data->lock); | |
261 | + clk_enable(data->clk); | |
262 | + | |
263 | + con = pdata->reference_voltage << EXYNOS_TMU_REF_VOLTAGE_SHIFT | | |
264 | + pdata->gain << EXYNOS_TMU_GAIN_SHIFT; | |
265 | + | |
266 | + if (data->soc == SOC_ARCH_EXYNOS) { | |
267 | + con |= pdata->noise_cancel_mode << EXYNOS_TMU_TRIP_MODE_SHIFT; | |
268 | + con |= (EXYNOS_MUX_ADDR_VALUE << EXYNOS_MUX_ADDR_SHIFT); | |
269 | + } | |
270 | + | |
271 | + if (on) { | |
272 | + con |= EXYNOS_TMU_CORE_ON; | |
273 | + interrupt_en = pdata->trigger_level3_en << 12 | | |
274 | + pdata->trigger_level2_en << 8 | | |
275 | + pdata->trigger_level1_en << 4 | | |
276 | + pdata->trigger_level0_en; | |
277 | + if (pdata->threshold_falling) | |
278 | + interrupt_en |= interrupt_en << 16; | |
279 | + } else { | |
280 | + con |= EXYNOS_TMU_CORE_OFF; | |
281 | + interrupt_en = 0; /* Disable all interrupts */ | |
282 | + } | |
283 | + writel(interrupt_en, data->base + EXYNOS_TMU_REG_INTEN); | |
284 | + writel(con, data->base + EXYNOS_TMU_REG_CONTROL); | |
285 | + | |
286 | + clk_disable(data->clk); | |
287 | + mutex_unlock(&data->lock); | |
288 | +} | |
289 | + | |
290 | +static int exynos_tmu_read(struct exynos_tmu_data *data) | |
291 | +{ | |
292 | + u8 temp_code; | |
293 | + int temp; | |
294 | + | |
295 | + mutex_lock(&data->lock); | |
296 | + clk_enable(data->clk); | |
297 | + | |
298 | + temp_code = readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP); | |
299 | + temp = code_to_temp(data, temp_code); | |
300 | + | |
301 | + clk_disable(data->clk); | |
302 | + mutex_unlock(&data->lock); | |
303 | + | |
304 | + return temp; | |
305 | +} | |
306 | + | |
307 | +#ifdef CONFIG_THERMAL_EMULATION | |
308 | +static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp) | |
309 | +{ | |
310 | + struct exynos_tmu_data *data = drv_data; | |
311 | + unsigned int reg; | |
312 | + int ret = -EINVAL; | |
313 | + | |
314 | + if (data->soc == SOC_ARCH_EXYNOS4210) | |
315 | + goto out; | |
316 | + | |
317 | + if (temp && temp < MCELSIUS) | |
318 | + goto out; | |
319 | + | |
320 | + mutex_lock(&data->lock); | |
321 | + clk_enable(data->clk); | |
322 | + | |
323 | + reg = readl(data->base + EXYNOS_EMUL_CON); | |
324 | + | |
325 | + if (temp) { | |
326 | + temp /= MCELSIUS; | |
327 | + | |
328 | + reg = (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT) | | |
329 | + (temp_to_code(data, temp) | |
330 | + << EXYNOS_EMUL_DATA_SHIFT) | EXYNOS_EMUL_ENABLE; | |
331 | + } else { | |
332 | + reg &= ~EXYNOS_EMUL_ENABLE; | |
333 | + } | |
334 | + | |
335 | + writel(reg, data->base + EXYNOS_EMUL_CON); | |
336 | + | |
337 | + clk_disable(data->clk); | |
338 | + mutex_unlock(&data->lock); | |
339 | + return 0; | |
340 | +out: | |
341 | + return ret; | |
342 | +} | |
343 | +#else | |
344 | +static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp) | |
345 | + { return -EINVAL; } | |
346 | +#endif/*CONFIG_THERMAL_EMULATION*/ | |
347 | + | |
348 | +static void exynos_tmu_work(struct work_struct *work) | |
349 | +{ | |
350 | + struct exynos_tmu_data *data = container_of(work, | |
351 | + struct exynos_tmu_data, irq_work); | |
352 | + | |
353 | + exynos_report_trigger(); | |
354 | + mutex_lock(&data->lock); | |
355 | + clk_enable(data->clk); | |
356 | + if (data->soc == SOC_ARCH_EXYNOS) | |
357 | + writel(EXYNOS_TMU_CLEAR_RISE_INT | | |
358 | + EXYNOS_TMU_CLEAR_FALL_INT, | |
359 | + data->base + EXYNOS_TMU_REG_INTCLEAR); | |
360 | + else | |
361 | + writel(EXYNOS4210_TMU_INTCLEAR_VAL, | |
362 | + data->base + EXYNOS_TMU_REG_INTCLEAR); | |
363 | + clk_disable(data->clk); | |
364 | + mutex_unlock(&data->lock); | |
365 | + | |
366 | + enable_irq(data->irq); | |
367 | +} | |
368 | + | |
369 | +static irqreturn_t exynos_tmu_irq(int irq, void *id) | |
370 | +{ | |
371 | + struct exynos_tmu_data *data = id; | |
372 | + | |
373 | + disable_irq_nosync(irq); | |
374 | + schedule_work(&data->irq_work); | |
375 | + | |
376 | + return IRQ_HANDLED; | |
377 | +} | |
378 | +static struct thermal_sensor_conf exynos_sensor_conf = { | |
379 | + .name = "exynos-therm", | |
380 | + .read_temperature = (int (*)(void *))exynos_tmu_read, | |
381 | + .write_emul_temp = exynos_tmu_set_emulation, | |
382 | +}; | |
383 | + | |
384 | +#if defined(CONFIG_CPU_EXYNOS4210) | |
385 | +static struct exynos_tmu_platform_data const exynos4210_default_tmu_data = { | |
386 | + .threshold = 80, | |
387 | + .trigger_levels[0] = 5, | |
388 | + .trigger_levels[1] = 20, | |
389 | + .trigger_levels[2] = 30, | |
390 | + .trigger_level0_en = 1, | |
391 | + .trigger_level1_en = 1, | |
392 | + .trigger_level2_en = 1, | |
393 | + .trigger_level3_en = 0, | |
394 | + .gain = 15, | |
395 | + .reference_voltage = 7, | |
396 | + .cal_type = TYPE_ONE_POINT_TRIMMING, | |
397 | + .freq_tab[0] = { | |
398 | + .freq_clip_max = 800 * 1000, | |
399 | + .temp_level = 85, | |
400 | + }, | |
401 | + .freq_tab[1] = { | |
402 | + .freq_clip_max = 200 * 1000, | |
403 | + .temp_level = 100, | |
404 | + }, | |
405 | + .freq_tab_count = 2, | |
406 | + .type = SOC_ARCH_EXYNOS4210, | |
407 | +}; | |
408 | +#define EXYNOS4210_TMU_DRV_DATA (&exynos4210_default_tmu_data) | |
409 | +#else | |
410 | +#define EXYNOS4210_TMU_DRV_DATA (NULL) | |
411 | +#endif | |
412 | + | |
413 | +#if defined(CONFIG_SOC_EXYNOS5250) || defined(CONFIG_SOC_EXYNOS4412) || \ | |
414 | + defined(CONFIG_SOC_EXYNOS4212) | |
415 | +static struct exynos_tmu_platform_data const exynos_default_tmu_data = { | |
416 | + .threshold_falling = 10, | |
417 | + .trigger_levels[0] = 85, | |
418 | + .trigger_levels[1] = 103, | |
419 | + .trigger_levels[2] = 110, | |
420 | + .trigger_level0_en = 1, | |
421 | + .trigger_level1_en = 1, | |
422 | + .trigger_level2_en = 1, | |
423 | + .trigger_level3_en = 0, | |
424 | + .gain = 8, | |
425 | + .reference_voltage = 16, | |
426 | + .noise_cancel_mode = 4, | |
427 | + .cal_type = TYPE_ONE_POINT_TRIMMING, | |
428 | + .efuse_value = 55, | |
429 | + .freq_tab[0] = { | |
430 | + .freq_clip_max = 800 * 1000, | |
431 | + .temp_level = 85, | |
432 | + }, | |
433 | + .freq_tab[1] = { | |
434 | + .freq_clip_max = 200 * 1000, | |
435 | + .temp_level = 103, | |
436 | + }, | |
437 | + .freq_tab_count = 2, | |
438 | + .type = SOC_ARCH_EXYNOS, | |
439 | +}; | |
440 | +#define EXYNOS_TMU_DRV_DATA (&exynos_default_tmu_data) | |
441 | +#else | |
442 | +#define EXYNOS_TMU_DRV_DATA (NULL) | |
443 | +#endif | |
444 | + | |
445 | +#ifdef CONFIG_OF | |
446 | +static const struct of_device_id exynos_tmu_match[] = { | |
447 | + { | |
448 | + .compatible = "samsung,exynos4210-tmu", | |
449 | + .data = (void *)EXYNOS4210_TMU_DRV_DATA, | |
450 | + }, | |
451 | + { | |
452 | + .compatible = "samsung,exynos4412-tmu", | |
453 | + .data = (void *)EXYNOS_TMU_DRV_DATA, | |
454 | + }, | |
455 | + { | |
456 | + .compatible = "samsung,exynos5250-tmu", | |
457 | + .data = (void *)EXYNOS_TMU_DRV_DATA, | |
458 | + }, | |
459 | + {}, | |
460 | +}; | |
461 | +MODULE_DEVICE_TABLE(of, exynos_tmu_match); | |
462 | +#endif | |
463 | + | |
464 | +static struct platform_device_id exynos_tmu_driver_ids[] = { | |
465 | + { | |
466 | + .name = "exynos4210-tmu", | |
467 | + .driver_data = (kernel_ulong_t)EXYNOS4210_TMU_DRV_DATA, | |
468 | + }, | |
469 | + { | |
470 | + .name = "exynos5250-tmu", | |
471 | + .driver_data = (kernel_ulong_t)EXYNOS_TMU_DRV_DATA, | |
472 | + }, | |
473 | + { }, | |
474 | +}; | |
475 | +MODULE_DEVICE_TABLE(platform, exynos_tmu_driver_ids); | |
476 | + | |
477 | +static inline struct exynos_tmu_platform_data *exynos_get_driver_data( | |
478 | + struct platform_device *pdev) | |
479 | +{ | |
480 | +#ifdef CONFIG_OF | |
481 | + if (pdev->dev.of_node) { | |
482 | + const struct of_device_id *match; | |
483 | + match = of_match_node(exynos_tmu_match, pdev->dev.of_node); | |
484 | + if (!match) | |
485 | + return NULL; | |
486 | + return (struct exynos_tmu_platform_data *) match->data; | |
487 | + } | |
488 | +#endif | |
489 | + return (struct exynos_tmu_platform_data *) | |
490 | + platform_get_device_id(pdev)->driver_data; | |
491 | +} | |
492 | + | |
493 | +static int exynos_tmu_probe(struct platform_device *pdev) | |
494 | +{ | |
495 | + struct exynos_tmu_data *data; | |
496 | + struct exynos_tmu_platform_data *pdata = pdev->dev.platform_data; | |
497 | + int ret, i; | |
498 | + | |
499 | + if (!pdata) | |
500 | + pdata = exynos_get_driver_data(pdev); | |
501 | + | |
502 | + if (!pdata) { | |
503 | + dev_err(&pdev->dev, "No platform init data supplied.\n"); | |
504 | + return -ENODEV; | |
505 | + } | |
506 | + data = devm_kzalloc(&pdev->dev, sizeof(struct exynos_tmu_data), | |
507 | + GFP_KERNEL); | |
508 | + if (!data) { | |
509 | + dev_err(&pdev->dev, "Failed to allocate driver structure\n"); | |
510 | + return -ENOMEM; | |
511 | + } | |
512 | + | |
513 | + data->irq = platform_get_irq(pdev, 0); | |
514 | + if (data->irq < 0) { | |
515 | + dev_err(&pdev->dev, "Failed to get platform irq\n"); | |
516 | + return data->irq; | |
517 | + } | |
518 | + | |
519 | + INIT_WORK(&data->irq_work, exynos_tmu_work); | |
520 | + | |
521 | + data->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
522 | + data->base = devm_ioremap_resource(&pdev->dev, data->mem); | |
523 | + if (IS_ERR(data->base)) | |
524 | + return PTR_ERR(data->base); | |
525 | + | |
526 | + ret = devm_request_irq(&pdev->dev, data->irq, exynos_tmu_irq, | |
527 | + IRQF_TRIGGER_RISING, "exynos-tmu", data); | |
528 | + if (ret) { | |
529 | + dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq); | |
530 | + return ret; | |
531 | + } | |
532 | + | |
533 | + data->clk = devm_clk_get(&pdev->dev, "tmu_apbif"); | |
534 | + if (IS_ERR(data->clk)) { | |
535 | + dev_err(&pdev->dev, "Failed to get clock\n"); | |
536 | + return PTR_ERR(data->clk); | |
537 | + } | |
538 | + | |
539 | + ret = clk_prepare(data->clk); | |
540 | + if (ret) | |
541 | + return ret; | |
542 | + | |
543 | + if (pdata->type == SOC_ARCH_EXYNOS || | |
544 | + pdata->type == SOC_ARCH_EXYNOS4210) | |
545 | + data->soc = pdata->type; | |
546 | + else { | |
547 | + ret = -EINVAL; | |
548 | + dev_err(&pdev->dev, "Platform not supported\n"); | |
549 | + goto err_clk; | |
550 | + } | |
551 | + | |
552 | + data->pdata = pdata; | |
553 | + platform_set_drvdata(pdev, data); | |
554 | + mutex_init(&data->lock); | |
555 | + | |
556 | + ret = exynos_tmu_initialize(pdev); | |
557 | + if (ret) { | |
558 | + dev_err(&pdev->dev, "Failed to initialize TMU\n"); | |
559 | + goto err_clk; | |
560 | + } | |
561 | + | |
562 | + exynos_tmu_control(pdev, true); | |
563 | + | |
564 | + /* Register the sensor with thermal management interface */ | |
565 | + (&exynos_sensor_conf)->private_data = data; | |
566 | + exynos_sensor_conf.trip_data.trip_count = pdata->trigger_level0_en + | |
567 | + pdata->trigger_level1_en + pdata->trigger_level2_en + | |
568 | + pdata->trigger_level3_en; | |
569 | + | |
570 | + for (i = 0; i < exynos_sensor_conf.trip_data.trip_count; i++) | |
571 | + exynos_sensor_conf.trip_data.trip_val[i] = | |
572 | + pdata->threshold + pdata->trigger_levels[i]; | |
573 | + | |
574 | + exynos_sensor_conf.trip_data.trigger_falling = pdata->threshold_falling; | |
575 | + | |
576 | + exynos_sensor_conf.cooling_data.freq_clip_count = | |
577 | + pdata->freq_tab_count; | |
578 | + for (i = 0; i < pdata->freq_tab_count; i++) { | |
579 | + exynos_sensor_conf.cooling_data.freq_data[i].freq_clip_max = | |
580 | + pdata->freq_tab[i].freq_clip_max; | |
581 | + exynos_sensor_conf.cooling_data.freq_data[i].temp_level = | |
582 | + pdata->freq_tab[i].temp_level; | |
583 | + } | |
584 | + | |
585 | + ret = exynos_register_thermal(&exynos_sensor_conf); | |
586 | + if (ret) { | |
587 | + dev_err(&pdev->dev, "Failed to register thermal interface\n"); | |
588 | + goto err_clk; | |
589 | + } | |
590 | + | |
591 | + return 0; | |
592 | +err_clk: | |
593 | + clk_unprepare(data->clk); | |
594 | + return ret; | |
595 | +} | |
596 | + | |
597 | +static int exynos_tmu_remove(struct platform_device *pdev) | |
598 | +{ | |
599 | + struct exynos_tmu_data *data = platform_get_drvdata(pdev); | |
600 | + | |
601 | + exynos_tmu_control(pdev, false); | |
602 | + | |
603 | + exynos_unregister_thermal(); | |
604 | + | |
605 | + clk_unprepare(data->clk); | |
606 | + | |
607 | + return 0; | |
608 | +} | |
609 | + | |
610 | +#ifdef CONFIG_PM_SLEEP | |
611 | +static int exynos_tmu_suspend(struct device *dev) | |
612 | +{ | |
613 | + exynos_tmu_control(to_platform_device(dev), false); | |
614 | + | |
615 | + return 0; | |
616 | +} | |
617 | + | |
618 | +static int exynos_tmu_resume(struct device *dev) | |
619 | +{ | |
620 | + struct platform_device *pdev = to_platform_device(dev); | |
621 | + | |
622 | + exynos_tmu_initialize(pdev); | |
623 | + exynos_tmu_control(pdev, true); | |
624 | + | |
625 | + return 0; | |
626 | +} | |
627 | + | |
628 | +static SIMPLE_DEV_PM_OPS(exynos_tmu_pm, | |
629 | + exynos_tmu_suspend, exynos_tmu_resume); | |
630 | +#define EXYNOS_TMU_PM (&exynos_tmu_pm) | |
631 | +#else | |
632 | +#define EXYNOS_TMU_PM NULL | |
633 | +#endif | |
634 | + | |
635 | +static struct platform_driver exynos_tmu_driver = { | |
636 | + .driver = { | |
637 | + .name = "exynos-tmu", | |
638 | + .owner = THIS_MODULE, | |
639 | + .pm = EXYNOS_TMU_PM, | |
640 | + .of_match_table = of_match_ptr(exynos_tmu_match), | |
641 | + }, | |
642 | + .probe = exynos_tmu_probe, | |
643 | + .remove = exynos_tmu_remove, | |
644 | + .id_table = exynos_tmu_driver_ids, | |
645 | +}; | |
646 | + | |
647 | +module_platform_driver(exynos_tmu_driver); | |
648 | + | |
649 | +MODULE_DESCRIPTION("EXYNOS TMU Driver"); | |
650 | +MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>"); | |
651 | +MODULE_LICENSE("GPL"); | |
652 | +MODULE_ALIAS("platform:exynos-tmu"); |