Commit | Line | Data |
---|---|---|
9d97e5c8 | 1 | /* |
59dfa54c | 2 | * exynos_tmu.c - Samsung EXYNOS TMU (Thermal Management Unit) |
9d97e5c8 DK |
3 | * |
4 | * Copyright (C) 2011 Samsung Electronics | |
5 | * Donggeun Kim <dg77.kim@samsung.com> | |
c48cbba6 | 6 | * Amit Daniel Kachhap <amit.kachhap@linaro.org> |
9d97e5c8 DK |
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 | ||
9d97e5c8 | 24 | #include <linux/clk.h> |
9d97e5c8 | 25 | #include <linux/io.h> |
1b678641 ADK |
26 | #include <linux/interrupt.h> |
27 | #include <linux/module.h> | |
f22d9c03 | 28 | #include <linux/of.h> |
cebe7373 ADK |
29 | #include <linux/of_address.h> |
30 | #include <linux/of_irq.h> | |
1b678641 | 31 | #include <linux/platform_device.h> |
498d22f6 | 32 | #include <linux/regulator/consumer.h> |
1b678641 ADK |
33 | |
34 | #include "exynos_thermal_common.h" | |
0c1836a6 | 35 | #include "exynos_tmu.h" |
2845f6ec BZ |
36 | |
37 | /* Exynos generic registers */ | |
38 | #define EXYNOS_TMU_REG_TRIMINFO 0x0 | |
39 | #define EXYNOS_TMU_REG_CONTROL 0x20 | |
40 | #define EXYNOS_TMU_REG_STATUS 0x28 | |
41 | #define EXYNOS_TMU_REG_CURRENT_TEMP 0x40 | |
42 | #define EXYNOS_TMU_REG_INTEN 0x70 | |
43 | #define EXYNOS_TMU_REG_INTSTAT 0x74 | |
44 | #define EXYNOS_TMU_REG_INTCLEAR 0x78 | |
45 | ||
46 | #define EXYNOS_TMU_TEMP_MASK 0xff | |
47 | #define EXYNOS_TMU_REF_VOLTAGE_SHIFT 24 | |
48 | #define EXYNOS_TMU_REF_VOLTAGE_MASK 0x1f | |
49 | #define EXYNOS_TMU_BUF_SLOPE_SEL_MASK 0xf | |
50 | #define EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT 8 | |
51 | #define EXYNOS_TMU_CORE_EN_SHIFT 0 | |
52 | ||
53 | /* Exynos3250 specific registers */ | |
54 | #define EXYNOS_TMU_TRIMINFO_CON1 0x10 | |
55 | ||
56 | /* Exynos4210 specific registers */ | |
57 | #define EXYNOS4210_TMU_REG_THRESHOLD_TEMP 0x44 | |
58 | #define EXYNOS4210_TMU_REG_TRIG_LEVEL0 0x50 | |
59 | ||
60 | /* Exynos5250, Exynos4412, Exynos3250 specific registers */ | |
61 | #define EXYNOS_TMU_TRIMINFO_CON2 0x14 | |
62 | #define EXYNOS_THD_TEMP_RISE 0x50 | |
63 | #define EXYNOS_THD_TEMP_FALL 0x54 | |
64 | #define EXYNOS_EMUL_CON 0x80 | |
65 | ||
66 | #define EXYNOS_TRIMINFO_RELOAD_ENABLE 1 | |
67 | #define EXYNOS_TRIMINFO_25_SHIFT 0 | |
68 | #define EXYNOS_TRIMINFO_85_SHIFT 8 | |
69 | #define EXYNOS_TMU_TRIP_MODE_SHIFT 13 | |
70 | #define EXYNOS_TMU_TRIP_MODE_MASK 0x7 | |
71 | #define EXYNOS_TMU_THERM_TRIP_EN_SHIFT 12 | |
72 | ||
73 | #define EXYNOS_TMU_INTEN_RISE0_SHIFT 0 | |
74 | #define EXYNOS_TMU_INTEN_RISE1_SHIFT 4 | |
75 | #define EXYNOS_TMU_INTEN_RISE2_SHIFT 8 | |
76 | #define EXYNOS_TMU_INTEN_RISE3_SHIFT 12 | |
77 | #define EXYNOS_TMU_INTEN_FALL0_SHIFT 16 | |
78 | ||
79 | #define EXYNOS_EMUL_TIME 0x57F0 | |
80 | #define EXYNOS_EMUL_TIME_MASK 0xffff | |
81 | #define EXYNOS_EMUL_TIME_SHIFT 16 | |
82 | #define EXYNOS_EMUL_DATA_SHIFT 8 | |
83 | #define EXYNOS_EMUL_DATA_MASK 0xFF | |
84 | #define EXYNOS_EMUL_ENABLE 0x1 | |
85 | ||
86 | /* Exynos5260 specific */ | |
87 | #define EXYNOS5260_TMU_REG_INTEN 0xC0 | |
88 | #define EXYNOS5260_TMU_REG_INTSTAT 0xC4 | |
89 | #define EXYNOS5260_TMU_REG_INTCLEAR 0xC8 | |
90 | #define EXYNOS5260_EMUL_CON 0x100 | |
91 | ||
92 | /* Exynos4412 specific */ | |
93 | #define EXYNOS4412_MUX_ADDR_VALUE 6 | |
94 | #define EXYNOS4412_MUX_ADDR_SHIFT 20 | |
95 | ||
96 | /*exynos5440 specific registers*/ | |
97 | #define EXYNOS5440_TMU_S0_7_TRIM 0x000 | |
98 | #define EXYNOS5440_TMU_S0_7_CTRL 0x020 | |
99 | #define EXYNOS5440_TMU_S0_7_DEBUG 0x040 | |
100 | #define EXYNOS5440_TMU_S0_7_TEMP 0x0f0 | |
101 | #define EXYNOS5440_TMU_S0_7_TH0 0x110 | |
102 | #define EXYNOS5440_TMU_S0_7_TH1 0x130 | |
103 | #define EXYNOS5440_TMU_S0_7_TH2 0x150 | |
104 | #define EXYNOS5440_TMU_S0_7_IRQEN 0x210 | |
105 | #define EXYNOS5440_TMU_S0_7_IRQ 0x230 | |
106 | /* exynos5440 common registers */ | |
107 | #define EXYNOS5440_TMU_IRQ_STATUS 0x000 | |
108 | #define EXYNOS5440_TMU_PMIN 0x004 | |
109 | ||
110 | #define EXYNOS5440_TMU_INTEN_RISE0_SHIFT 0 | |
111 | #define EXYNOS5440_TMU_INTEN_RISE1_SHIFT 1 | |
112 | #define EXYNOS5440_TMU_INTEN_RISE2_SHIFT 2 | |
113 | #define EXYNOS5440_TMU_INTEN_RISE3_SHIFT 3 | |
114 | #define EXYNOS5440_TMU_INTEN_FALL0_SHIFT 4 | |
115 | #define EXYNOS5440_TMU_TH_RISE4_SHIFT 24 | |
116 | #define EXYNOS5440_EFUSE_SWAP_OFFSET 8 | |
f22d9c03 | 117 | |
cebe7373 ADK |
118 | /** |
119 | * struct exynos_tmu_data : A structure to hold the private data of the TMU | |
120 | driver | |
121 | * @id: identifier of the one instance of the TMU controller. | |
122 | * @pdata: pointer to the tmu platform/configuration data | |
123 | * @base: base address of the single instance of the TMU controller. | |
9025d563 | 124 | * @base_second: base address of the common registers of the TMU controller. |
cebe7373 ADK |
125 | * @irq: irq number of the TMU controller. |
126 | * @soc: id of the SOC type. | |
127 | * @irq_work: pointer to the irq work structure. | |
128 | * @lock: lock to implement synchronization. | |
129 | * @clk: pointer to the clock structure. | |
14a11dc7 | 130 | * @clk_sec: pointer to the clock structure for accessing the base_second. |
cebe7373 ADK |
131 | * @temp_error1: fused value of the first point trim. |
132 | * @temp_error2: fused value of the second point trim. | |
498d22f6 | 133 | * @regulator: pointer to the TMU regulator structure. |
cebe7373 | 134 | * @reg_conf: pointer to structure to register with core thermal. |
72d1100b | 135 | * @tmu_initialize: SoC specific TMU initialization method |
37f9034f | 136 | * @tmu_control: SoC specific TMU control method |
b79985ca | 137 | * @tmu_read: SoC specific TMU temperature read method |
285d994a | 138 | * @tmu_set_emulation: SoC specific TMU emulation setting method |
a7331f72 | 139 | * @tmu_clear_irqs: SoC specific TMU interrupts clearing method |
cebe7373 | 140 | */ |
f22d9c03 | 141 | struct exynos_tmu_data { |
cebe7373 | 142 | int id; |
f22d9c03 | 143 | struct exynos_tmu_platform_data *pdata; |
9d97e5c8 | 144 | void __iomem *base; |
9025d563 | 145 | void __iomem *base_second; |
9d97e5c8 | 146 | int irq; |
f22d9c03 | 147 | enum soc_type soc; |
9d97e5c8 DK |
148 | struct work_struct irq_work; |
149 | struct mutex lock; | |
14a11dc7 | 150 | struct clk *clk, *clk_sec; |
9d97e5c8 | 151 | u8 temp_error1, temp_error2; |
498d22f6 | 152 | struct regulator *regulator; |
cebe7373 | 153 | struct thermal_sensor_conf *reg_conf; |
72d1100b | 154 | int (*tmu_initialize)(struct platform_device *pdev); |
37f9034f | 155 | void (*tmu_control)(struct platform_device *pdev, bool on); |
b79985ca | 156 | int (*tmu_read)(struct exynos_tmu_data *data); |
285d994a BZ |
157 | void (*tmu_set_emulation)(struct exynos_tmu_data *data, |
158 | unsigned long temp); | |
a7331f72 | 159 | void (*tmu_clear_irqs)(struct exynos_tmu_data *data); |
9d97e5c8 DK |
160 | }; |
161 | ||
162 | /* | |
163 | * TMU treats temperature as a mapped temperature code. | |
164 | * The temperature is converted differently depending on the calibration type. | |
165 | */ | |
f22d9c03 | 166 | static int temp_to_code(struct exynos_tmu_data *data, u8 temp) |
9d97e5c8 | 167 | { |
f22d9c03 | 168 | struct exynos_tmu_platform_data *pdata = data->pdata; |
9d97e5c8 DK |
169 | int temp_code; |
170 | ||
9d97e5c8 DK |
171 | switch (pdata->cal_type) { |
172 | case TYPE_TWO_POINT_TRIMMING: | |
bb34b4c8 ADK |
173 | temp_code = (temp - pdata->first_point_trim) * |
174 | (data->temp_error2 - data->temp_error1) / | |
175 | (pdata->second_point_trim - pdata->first_point_trim) + | |
176 | data->temp_error1; | |
9d97e5c8 DK |
177 | break; |
178 | case TYPE_ONE_POINT_TRIMMING: | |
bb34b4c8 | 179 | temp_code = temp + data->temp_error1 - pdata->first_point_trim; |
9d97e5c8 DK |
180 | break; |
181 | default: | |
bb34b4c8 | 182 | temp_code = temp + pdata->default_temp_offset; |
9d97e5c8 DK |
183 | break; |
184 | } | |
ddb31d43 | 185 | |
9d97e5c8 DK |
186 | return temp_code; |
187 | } | |
188 | ||
189 | /* | |
190 | * Calculate a temperature value from a temperature code. | |
191 | * The unit of the temperature is degree Celsius. | |
192 | */ | |
f22d9c03 | 193 | static int code_to_temp(struct exynos_tmu_data *data, u8 temp_code) |
9d97e5c8 | 194 | { |
f22d9c03 | 195 | struct exynos_tmu_platform_data *pdata = data->pdata; |
9d97e5c8 DK |
196 | int temp; |
197 | ||
9d97e5c8 DK |
198 | switch (pdata->cal_type) { |
199 | case TYPE_TWO_POINT_TRIMMING: | |
bb34b4c8 ADK |
200 | temp = (temp_code - data->temp_error1) * |
201 | (pdata->second_point_trim - pdata->first_point_trim) / | |
202 | (data->temp_error2 - data->temp_error1) + | |
203 | pdata->first_point_trim; | |
9d97e5c8 DK |
204 | break; |
205 | case TYPE_ONE_POINT_TRIMMING: | |
bb34b4c8 | 206 | temp = temp_code - data->temp_error1 + pdata->first_point_trim; |
9d97e5c8 DK |
207 | break; |
208 | default: | |
bb34b4c8 | 209 | temp = temp_code - pdata->default_temp_offset; |
9d97e5c8 DK |
210 | break; |
211 | } | |
ddb31d43 | 212 | |
9d97e5c8 DK |
213 | return temp; |
214 | } | |
215 | ||
8328a4b1 | 216 | static void sanitize_temp_error(struct exynos_tmu_data *data, u32 trim_info) |
9d97e5c8 | 217 | { |
f22d9c03 | 218 | struct exynos_tmu_platform_data *pdata = data->pdata; |
9d97e5c8 | 219 | |
b8d582b9 | 220 | data->temp_error1 = trim_info & EXYNOS_TMU_TEMP_MASK; |
99d67fb9 | 221 | data->temp_error2 = ((trim_info >> EXYNOS_TRIMINFO_85_SHIFT) & |
b8d582b9 | 222 | EXYNOS_TMU_TEMP_MASK); |
f22d9c03 | 223 | |
5000806c ADK |
224 | if (!data->temp_error1 || |
225 | (pdata->min_efuse_value > data->temp_error1) || | |
226 | (data->temp_error1 > pdata->max_efuse_value)) | |
227 | data->temp_error1 = pdata->efuse_value & EXYNOS_TMU_TEMP_MASK; | |
228 | ||
229 | if (!data->temp_error2) | |
230 | data->temp_error2 = | |
99d67fb9 | 231 | (pdata->efuse_value >> EXYNOS_TRIMINFO_85_SHIFT) & |
5000806c | 232 | EXYNOS_TMU_TEMP_MASK; |
8328a4b1 | 233 | } |
f22d9c03 | 234 | |
fe87789c BZ |
235 | static u32 get_th_reg(struct exynos_tmu_data *data, u32 threshold, bool falling) |
236 | { | |
237 | struct exynos_tmu_platform_data *pdata = data->pdata; | |
238 | int i; | |
c65d3473 | 239 | |
fe87789c BZ |
240 | for (i = 0; i < pdata->non_hw_trigger_levels; i++) { |
241 | u8 temp = pdata->trigger_levels[i]; | |
f22d9c03 | 242 | |
fe87789c BZ |
243 | if (falling) |
244 | temp -= pdata->threshold_falling; | |
245 | else | |
246 | threshold &= ~(0xff << 8 * i); | |
f22d9c03 | 247 | |
fe87789c | 248 | threshold |= temp_to_code(data, temp) << 8 * i; |
9d97e5c8 | 249 | } |
fe87789c BZ |
250 | |
251 | return threshold; | |
252 | } | |
253 | ||
f22d9c03 | 254 | static int exynos_tmu_initialize(struct platform_device *pdev) |
9d97e5c8 | 255 | { |
f22d9c03 | 256 | struct exynos_tmu_data *data = platform_get_drvdata(pdev); |
72d1100b | 257 | int ret; |
9d97e5c8 DK |
258 | |
259 | mutex_lock(&data->lock); | |
260 | clk_enable(data->clk); | |
14a11dc7 NKC |
261 | if (!IS_ERR(data->clk_sec)) |
262 | clk_enable(data->clk_sec); | |
72d1100b | 263 | ret = data->tmu_initialize(pdev); |
9d97e5c8 DK |
264 | clk_disable(data->clk); |
265 | mutex_unlock(&data->lock); | |
14a11dc7 NKC |
266 | if (!IS_ERR(data->clk_sec)) |
267 | clk_disable(data->clk_sec); | |
9d97e5c8 DK |
268 | |
269 | return ret; | |
270 | } | |
271 | ||
d00671c3 | 272 | static u32 get_con_reg(struct exynos_tmu_data *data, u32 con) |
9d97e5c8 | 273 | { |
f22d9c03 | 274 | struct exynos_tmu_platform_data *pdata = data->pdata; |
9d97e5c8 | 275 | |
7575983c BZ |
276 | if (data->soc == SOC_ARCH_EXYNOS4412 || |
277 | data->soc == SOC_ARCH_EXYNOS3250) | |
278 | con |= (EXYNOS4412_MUX_ADDR_VALUE << EXYNOS4412_MUX_ADDR_SHIFT); | |
86f5362e | 279 | |
99d67fb9 BZ |
280 | con &= ~(EXYNOS_TMU_REF_VOLTAGE_MASK << EXYNOS_TMU_REF_VOLTAGE_SHIFT); |
281 | con |= pdata->reference_voltage << EXYNOS_TMU_REF_VOLTAGE_SHIFT; | |
d0a0ce3e | 282 | |
99d67fb9 BZ |
283 | con &= ~(EXYNOS_TMU_BUF_SLOPE_SEL_MASK << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT); |
284 | con |= (pdata->gain << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT); | |
d0a0ce3e ADK |
285 | |
286 | if (pdata->noise_cancel_mode) { | |
b9504a6a BZ |
287 | con &= ~(EXYNOS_TMU_TRIP_MODE_MASK << EXYNOS_TMU_TRIP_MODE_SHIFT); |
288 | con |= (pdata->noise_cancel_mode << EXYNOS_TMU_TRIP_MODE_SHIFT); | |
f22d9c03 ADK |
289 | } |
290 | ||
d00671c3 BZ |
291 | return con; |
292 | } | |
293 | ||
294 | static void exynos_tmu_control(struct platform_device *pdev, bool on) | |
295 | { | |
296 | struct exynos_tmu_data *data = platform_get_drvdata(pdev); | |
9d97e5c8 | 297 | |
d00671c3 BZ |
298 | mutex_lock(&data->lock); |
299 | clk_enable(data->clk); | |
37f9034f | 300 | data->tmu_control(pdev, on); |
9d97e5c8 DK |
301 | clk_disable(data->clk); |
302 | mutex_unlock(&data->lock); | |
303 | } | |
304 | ||
72d1100b | 305 | static int exynos4210_tmu_initialize(struct platform_device *pdev) |
9d97e5c8 | 306 | { |
72d1100b | 307 | struct exynos_tmu_data *data = platform_get_drvdata(pdev); |
b8d582b9 | 308 | struct exynos_tmu_platform_data *pdata = data->pdata; |
72d1100b BZ |
309 | unsigned int status; |
310 | int ret = 0, threshold_code, i; | |
9d97e5c8 | 311 | |
72d1100b BZ |
312 | status = readb(data->base + EXYNOS_TMU_REG_STATUS); |
313 | if (!status) { | |
314 | ret = -EBUSY; | |
315 | goto out; | |
316 | } | |
9d97e5c8 | 317 | |
72d1100b | 318 | sanitize_temp_error(data, readl(data->base + EXYNOS_TMU_REG_TRIMINFO)); |
9d97e5c8 | 319 | |
72d1100b BZ |
320 | /* Write temperature code for threshold */ |
321 | threshold_code = temp_to_code(data, pdata->threshold); | |
322 | writeb(threshold_code, data->base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP); | |
323 | ||
324 | for (i = 0; i < pdata->non_hw_trigger_levels; i++) | |
325 | writeb(pdata->trigger_levels[i], data->base + | |
326 | EXYNOS4210_TMU_REG_TRIG_LEVEL0 + i * 4); | |
327 | ||
a7331f72 | 328 | data->tmu_clear_irqs(data); |
72d1100b BZ |
329 | out: |
330 | return ret; | |
331 | } | |
332 | ||
333 | static int exynos4412_tmu_initialize(struct platform_device *pdev) | |
334 | { | |
335 | struct exynos_tmu_data *data = platform_get_drvdata(pdev); | |
336 | struct exynos_tmu_platform_data *pdata = data->pdata; | |
337 | unsigned int status, trim_info, con, ctrl, rising_threshold; | |
338 | int ret = 0, threshold_code, i; | |
339 | ||
340 | status = readb(data->base + EXYNOS_TMU_REG_STATUS); | |
341 | if (!status) { | |
342 | ret = -EBUSY; | |
343 | goto out; | |
344 | } | |
345 | ||
346 | if (data->soc == SOC_ARCH_EXYNOS3250 || | |
347 | data->soc == SOC_ARCH_EXYNOS4412 || | |
348 | data->soc == SOC_ARCH_EXYNOS5250) { | |
349 | if (data->soc == SOC_ARCH_EXYNOS3250) { | |
350 | ctrl = readl(data->base + EXYNOS_TMU_TRIMINFO_CON1); | |
351 | ctrl |= EXYNOS_TRIMINFO_RELOAD_ENABLE; | |
352 | writel(ctrl, data->base + EXYNOS_TMU_TRIMINFO_CON1); | |
ddb31d43 | 353 | } |
72d1100b BZ |
354 | ctrl = readl(data->base + EXYNOS_TMU_TRIMINFO_CON2); |
355 | ctrl |= EXYNOS_TRIMINFO_RELOAD_ENABLE; | |
356 | writel(ctrl, data->base + EXYNOS_TMU_TRIMINFO_CON2); | |
357 | } | |
ddb31d43 | 358 | |
72d1100b BZ |
359 | /* On exynos5420 the triminfo register is in the shared space */ |
360 | if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) | |
361 | trim_info = readl(data->base_second + EXYNOS_TMU_REG_TRIMINFO); | |
362 | else | |
363 | trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); | |
364 | ||
365 | sanitize_temp_error(data, trim_info); | |
366 | ||
367 | /* Write temperature code for rising and falling threshold */ | |
368 | rising_threshold = readl(data->base + EXYNOS_THD_TEMP_RISE); | |
369 | rising_threshold = get_th_reg(data, rising_threshold, false); | |
370 | writel(rising_threshold, data->base + EXYNOS_THD_TEMP_RISE); | |
371 | writel(get_th_reg(data, 0, true), data->base + EXYNOS_THD_TEMP_FALL); | |
372 | ||
a7331f72 | 373 | data->tmu_clear_irqs(data); |
72d1100b BZ |
374 | |
375 | /* if last threshold limit is also present */ | |
376 | i = pdata->max_trigger_level - 1; | |
377 | if (pdata->trigger_levels[i] && pdata->trigger_type[i] == HW_TRIP) { | |
378 | threshold_code = temp_to_code(data, pdata->trigger_levels[i]); | |
379 | /* 1-4 level to be assigned in th0 reg */ | |
380 | rising_threshold &= ~(0xff << 8 * i); | |
381 | rising_threshold |= threshold_code << 8 * i; | |
382 | writel(rising_threshold, data->base + EXYNOS_THD_TEMP_RISE); | |
383 | con = readl(data->base + EXYNOS_TMU_REG_CONTROL); | |
384 | con |= (1 << EXYNOS_TMU_THERM_TRIP_EN_SHIFT); | |
385 | writel(con, data->base + EXYNOS_TMU_REG_CONTROL); | |
386 | } | |
ddb31d43 | 387 | out: |
72d1100b BZ |
388 | return ret; |
389 | } | |
9d97e5c8 | 390 | |
72d1100b BZ |
391 | static int exynos5440_tmu_initialize(struct platform_device *pdev) |
392 | { | |
393 | struct exynos_tmu_data *data = platform_get_drvdata(pdev); | |
394 | struct exynos_tmu_platform_data *pdata = data->pdata; | |
395 | unsigned int trim_info = 0, con, rising_threshold; | |
396 | int ret = 0, threshold_code, i; | |
397 | ||
398 | /* | |
399 | * For exynos5440 soc triminfo value is swapped between TMU0 and | |
400 | * TMU2, so the below logic is needed. | |
401 | */ | |
402 | switch (data->id) { | |
403 | case 0: | |
404 | trim_info = readl(data->base + EXYNOS5440_EFUSE_SWAP_OFFSET + | |
405 | EXYNOS5440_TMU_S0_7_TRIM); | |
406 | break; | |
407 | case 1: | |
408 | trim_info = readl(data->base + EXYNOS5440_TMU_S0_7_TRIM); | |
409 | break; | |
410 | case 2: | |
411 | trim_info = readl(data->base - EXYNOS5440_EFUSE_SWAP_OFFSET + | |
412 | EXYNOS5440_TMU_S0_7_TRIM); | |
413 | } | |
414 | sanitize_temp_error(data, trim_info); | |
415 | ||
416 | /* Write temperature code for rising and falling threshold */ | |
417 | rising_threshold = readl(data->base + EXYNOS5440_TMU_S0_7_TH0); | |
418 | rising_threshold = get_th_reg(data, rising_threshold, false); | |
419 | writel(rising_threshold, data->base + EXYNOS5440_TMU_S0_7_TH0); | |
420 | writel(0, data->base + EXYNOS5440_TMU_S0_7_TH1); | |
421 | ||
a7331f72 | 422 | data->tmu_clear_irqs(data); |
72d1100b BZ |
423 | |
424 | /* if last threshold limit is also present */ | |
425 | i = pdata->max_trigger_level - 1; | |
426 | if (pdata->trigger_levels[i] && pdata->trigger_type[i] == HW_TRIP) { | |
427 | threshold_code = temp_to_code(data, pdata->trigger_levels[i]); | |
428 | /* 5th level to be assigned in th2 reg */ | |
429 | rising_threshold = | |
430 | threshold_code << EXYNOS5440_TMU_TH_RISE4_SHIFT; | |
431 | writel(rising_threshold, data->base + EXYNOS5440_TMU_S0_7_TH2); | |
432 | con = readl(data->base + EXYNOS5440_TMU_S0_7_CTRL); | |
433 | con |= (1 << EXYNOS_TMU_THERM_TRIP_EN_SHIFT); | |
434 | writel(con, data->base + EXYNOS5440_TMU_S0_7_CTRL); | |
435 | } | |
436 | /* Clear the PMIN in the common TMU register */ | |
437 | if (!data->id) | |
438 | writel(0, data->base_second + EXYNOS5440_TMU_PMIN); | |
439 | return ret; | |
9d97e5c8 DK |
440 | } |
441 | ||
37f9034f | 442 | static void exynos4210_tmu_control(struct platform_device *pdev, bool on) |
bffd1f8a | 443 | { |
37f9034f | 444 | struct exynos_tmu_data *data = platform_get_drvdata(pdev); |
b8d582b9 | 445 | struct exynos_tmu_platform_data *pdata = data->pdata; |
37f9034f | 446 | unsigned int con, interrupt_en; |
bffd1f8a | 447 | |
37f9034f | 448 | con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL)); |
bffd1f8a | 449 | |
37f9034f BZ |
450 | if (on) { |
451 | con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); | |
452 | interrupt_en = | |
453 | pdata->trigger_enable[3] << EXYNOS_TMU_INTEN_RISE3_SHIFT | | |
454 | pdata->trigger_enable[2] << EXYNOS_TMU_INTEN_RISE2_SHIFT | | |
455 | pdata->trigger_enable[1] << EXYNOS_TMU_INTEN_RISE1_SHIFT | | |
456 | pdata->trigger_enable[0] << EXYNOS_TMU_INTEN_RISE0_SHIFT; | |
e0761533 | 457 | if (data->soc != SOC_ARCH_EXYNOS4210) |
37f9034f BZ |
458 | interrupt_en |= |
459 | interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT; | |
460 | } else { | |
461 | con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); | |
462 | interrupt_en = 0; /* Disable all interrupts */ | |
463 | } | |
464 | writel(interrupt_en, data->base + EXYNOS_TMU_REG_INTEN); | |
465 | writel(con, data->base + EXYNOS_TMU_REG_CONTROL); | |
466 | } | |
467 | ||
468 | static void exynos5440_tmu_control(struct platform_device *pdev, bool on) | |
469 | { | |
470 | struct exynos_tmu_data *data = platform_get_drvdata(pdev); | |
471 | struct exynos_tmu_platform_data *pdata = data->pdata; | |
472 | unsigned int con, interrupt_en; | |
473 | ||
474 | con = get_con_reg(data, readl(data->base + EXYNOS5440_TMU_S0_7_CTRL)); | |
475 | ||
476 | if (on) { | |
477 | con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); | |
478 | interrupt_en = | |
479 | pdata->trigger_enable[3] << EXYNOS5440_TMU_INTEN_RISE3_SHIFT | | |
480 | pdata->trigger_enable[2] << EXYNOS5440_TMU_INTEN_RISE2_SHIFT | | |
481 | pdata->trigger_enable[1] << EXYNOS5440_TMU_INTEN_RISE1_SHIFT | | |
482 | pdata->trigger_enable[0] << EXYNOS5440_TMU_INTEN_RISE0_SHIFT; | |
e0761533 | 483 | interrupt_en |= interrupt_en << EXYNOS5440_TMU_INTEN_FALL0_SHIFT; |
37f9034f BZ |
484 | } else { |
485 | con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); | |
486 | interrupt_en = 0; /* Disable all interrupts */ | |
487 | } | |
488 | writel(interrupt_en, data->base + EXYNOS5440_TMU_S0_7_IRQEN); | |
489 | writel(con, data->base + EXYNOS5440_TMU_S0_7_CTRL); | |
490 | } | |
491 | ||
f22d9c03 | 492 | static int exynos_tmu_read(struct exynos_tmu_data *data) |
9d97e5c8 | 493 | { |
b79985ca | 494 | int ret; |
bffd1f8a ADK |
495 | |
496 | mutex_lock(&data->lock); | |
497 | clk_enable(data->clk); | |
b79985ca BZ |
498 | ret = data->tmu_read(data); |
499 | if (ret >= 0) | |
500 | ret = code_to_temp(data, ret); | |
9d97e5c8 DK |
501 | clk_disable(data->clk); |
502 | mutex_unlock(&data->lock); | |
bffd1f8a | 503 | |
b79985ca | 504 | return ret; |
9d97e5c8 | 505 | } |
bffd1f8a | 506 | |
bffd1f8a | 507 | #ifdef CONFIG_THERMAL_EMULATION |
154013ea BZ |
508 | static u32 get_emul_con_reg(struct exynos_tmu_data *data, unsigned int val, |
509 | unsigned long temp) | |
510 | { | |
bffd1f8a ADK |
511 | if (temp) { |
512 | temp /= MCELSIUS; | |
513 | ||
d564b55a | 514 | if (data->soc != SOC_ARCH_EXYNOS5440) { |
154013ea BZ |
515 | val &= ~(EXYNOS_EMUL_TIME_MASK << EXYNOS_EMUL_TIME_SHIFT); |
516 | val |= (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT); | |
f4dae753 | 517 | } |
154013ea BZ |
518 | val &= ~(EXYNOS_EMUL_DATA_MASK << EXYNOS_EMUL_DATA_SHIFT); |
519 | val |= (temp_to_code(data, temp) << EXYNOS_EMUL_DATA_SHIFT) | | |
f4dae753 | 520 | EXYNOS_EMUL_ENABLE; |
bffd1f8a | 521 | } else { |
b8d582b9 | 522 | val &= ~EXYNOS_EMUL_ENABLE; |
bffd1f8a ADK |
523 | } |
524 | ||
154013ea BZ |
525 | return val; |
526 | } | |
527 | ||
285d994a BZ |
528 | static void exynos4412_tmu_set_emulation(struct exynos_tmu_data *data, |
529 | unsigned long temp) | |
530 | { | |
531 | unsigned int val; | |
532 | u32 emul_con; | |
533 | ||
534 | if (data->soc == SOC_ARCH_EXYNOS5260) | |
535 | emul_con = EXYNOS5260_EMUL_CON; | |
536 | else | |
537 | emul_con = EXYNOS_EMUL_CON; | |
538 | ||
539 | val = readl(data->base + emul_con); | |
540 | val = get_emul_con_reg(data, val, temp); | |
541 | writel(val, data->base + emul_con); | |
542 | } | |
543 | ||
544 | static void exynos5440_tmu_set_emulation(struct exynos_tmu_data *data, | |
545 | unsigned long temp) | |
546 | { | |
547 | unsigned int val; | |
548 | ||
549 | val = readl(data->base + EXYNOS5440_TMU_S0_7_DEBUG); | |
550 | val = get_emul_con_reg(data, val, temp); | |
551 | writel(val, data->base + EXYNOS5440_TMU_S0_7_DEBUG); | |
552 | } | |
553 | ||
bffd1f8a ADK |
554 | static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp) |
555 | { | |
556 | struct exynos_tmu_data *data = drv_data; | |
bffd1f8a ADK |
557 | int ret = -EINVAL; |
558 | ||
ef3f80fc | 559 | if (data->soc == SOC_ARCH_EXYNOS4210) |
bffd1f8a | 560 | goto out; |
bffd1f8a | 561 | |
bffd1f8a ADK |
562 | if (temp && temp < MCELSIUS) |
563 | goto out; | |
564 | ||
565 | mutex_lock(&data->lock); | |
566 | clk_enable(data->clk); | |
285d994a | 567 | data->tmu_set_emulation(data, temp); |
bffd1f8a ADK |
568 | clk_disable(data->clk); |
569 | mutex_unlock(&data->lock); | |
570 | return 0; | |
571 | out: | |
572 | return ret; | |
573 | } | |
574 | #else | |
285d994a BZ |
575 | #define exynos4412_tmu_set_emulation NULL |
576 | #define exynos5440_tmu_set_emulation NULL | |
bffd1f8a ADK |
577 | static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp) |
578 | { return -EINVAL; } | |
579 | #endif/*CONFIG_THERMAL_EMULATION*/ | |
580 | ||
b79985ca BZ |
581 | static int exynos4210_tmu_read(struct exynos_tmu_data *data) |
582 | { | |
583 | int ret = readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP); | |
584 | ||
585 | /* "temp_code" should range between 75 and 175 */ | |
586 | return (ret < 75 || ret > 175) ? -ENODATA : ret; | |
587 | } | |
588 | ||
589 | static int exynos4412_tmu_read(struct exynos_tmu_data *data) | |
590 | { | |
591 | return readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP); | |
592 | } | |
593 | ||
594 | static int exynos5440_tmu_read(struct exynos_tmu_data *data) | |
595 | { | |
596 | return readb(data->base + EXYNOS5440_TMU_S0_7_TEMP); | |
597 | } | |
598 | ||
f22d9c03 | 599 | static void exynos_tmu_work(struct work_struct *work) |
9d97e5c8 | 600 | { |
f22d9c03 ADK |
601 | struct exynos_tmu_data *data = container_of(work, |
602 | struct exynos_tmu_data, irq_work); | |
b835ced1 | 603 | unsigned int val_type; |
a0395eee | 604 | |
14a11dc7 NKC |
605 | if (!IS_ERR(data->clk_sec)) |
606 | clk_enable(data->clk_sec); | |
a0395eee | 607 | /* Find which sensor generated this interrupt */ |
421d5d12 BZ |
608 | if (data->soc == SOC_ARCH_EXYNOS5440) { |
609 | val_type = readl(data->base_second + EXYNOS5440_TMU_IRQ_STATUS); | |
a0395eee ADK |
610 | if (!((val_type >> data->id) & 0x1)) |
611 | goto out; | |
612 | } | |
14a11dc7 NKC |
613 | if (!IS_ERR(data->clk_sec)) |
614 | clk_disable(data->clk_sec); | |
9d97e5c8 | 615 | |
cebe7373 | 616 | exynos_report_trigger(data->reg_conf); |
9d97e5c8 DK |
617 | mutex_lock(&data->lock); |
618 | clk_enable(data->clk); | |
b8d582b9 | 619 | |
a4463c4f | 620 | /* TODO: take action based on particular interrupt */ |
a7331f72 | 621 | data->tmu_clear_irqs(data); |
b8d582b9 | 622 | |
9d97e5c8 DK |
623 | clk_disable(data->clk); |
624 | mutex_unlock(&data->lock); | |
a0395eee | 625 | out: |
f22d9c03 | 626 | enable_irq(data->irq); |
9d97e5c8 DK |
627 | } |
628 | ||
a7331f72 BZ |
629 | static void exynos4210_tmu_clear_irqs(struct exynos_tmu_data *data) |
630 | { | |
631 | unsigned int val_irq; | |
632 | u32 tmu_intstat, tmu_intclear; | |
633 | ||
634 | if (data->soc == SOC_ARCH_EXYNOS5260) { | |
635 | tmu_intstat = EXYNOS5260_TMU_REG_INTSTAT; | |
636 | tmu_intclear = EXYNOS5260_TMU_REG_INTCLEAR; | |
637 | } else { | |
638 | tmu_intstat = EXYNOS_TMU_REG_INTSTAT; | |
639 | tmu_intclear = EXYNOS_TMU_REG_INTCLEAR; | |
640 | } | |
641 | ||
642 | val_irq = readl(data->base + tmu_intstat); | |
643 | /* | |
644 | * Clear the interrupts. Please note that the documentation for | |
645 | * Exynos3250, Exynos4412, Exynos5250 and Exynos5260 incorrectly | |
646 | * states that INTCLEAR register has a different placing of bits | |
647 | * responsible for FALL IRQs than INTSTAT register. Exynos5420 | |
648 | * and Exynos5440 documentation is correct (Exynos4210 doesn't | |
649 | * support FALL IRQs at all). | |
650 | */ | |
651 | writel(val_irq, data->base + tmu_intclear); | |
652 | } | |
653 | ||
654 | static void exynos5440_tmu_clear_irqs(struct exynos_tmu_data *data) | |
655 | { | |
656 | unsigned int val_irq; | |
657 | ||
658 | val_irq = readl(data->base + EXYNOS5440_TMU_S0_7_IRQ); | |
659 | /* clear the interrupts */ | |
660 | writel(val_irq, data->base + EXYNOS5440_TMU_S0_7_IRQ); | |
661 | } | |
662 | ||
f22d9c03 | 663 | static irqreturn_t exynos_tmu_irq(int irq, void *id) |
9d97e5c8 | 664 | { |
f22d9c03 | 665 | struct exynos_tmu_data *data = id; |
9d97e5c8 DK |
666 | |
667 | disable_irq_nosync(irq); | |
668 | schedule_work(&data->irq_work); | |
669 | ||
670 | return IRQ_HANDLED; | |
671 | } | |
17be868e | 672 | |
17be868e | 673 | static const struct of_device_id exynos_tmu_match[] = { |
1fe56dc1 CC |
674 | { |
675 | .compatible = "samsung,exynos3250-tmu", | |
4c4680a1 | 676 | .data = &exynos3250_default_tmu_data, |
1fe56dc1 | 677 | }, |
17be868e ADK |
678 | { |
679 | .compatible = "samsung,exynos4210-tmu", | |
4c4680a1 | 680 | .data = &exynos4210_default_tmu_data, |
17be868e | 681 | }, |
b6cee53c SK |
682 | { |
683 | .compatible = "samsung,exynos4412-tmu", | |
4c4680a1 | 684 | .data = &exynos4412_default_tmu_data, |
b6cee53c | 685 | }, |
17be868e ADK |
686 | { |
687 | .compatible = "samsung,exynos5250-tmu", | |
4c4680a1 | 688 | .data = &exynos5250_default_tmu_data, |
17be868e | 689 | }, |
923488a5 NKC |
690 | { |
691 | .compatible = "samsung,exynos5260-tmu", | |
4c4680a1 | 692 | .data = &exynos5260_default_tmu_data, |
923488a5 | 693 | }, |
14a11dc7 NKC |
694 | { |
695 | .compatible = "samsung,exynos5420-tmu", | |
4c4680a1 | 696 | .data = &exynos5420_default_tmu_data, |
14a11dc7 NKC |
697 | }, |
698 | { | |
699 | .compatible = "samsung,exynos5420-tmu-ext-triminfo", | |
4c4680a1 | 700 | .data = &exynos5420_default_tmu_data, |
14a11dc7 | 701 | }, |
90542546 ADK |
702 | { |
703 | .compatible = "samsung,exynos5440-tmu", | |
4c4680a1 | 704 | .data = &exynos5440_default_tmu_data, |
90542546 | 705 | }, |
17be868e ADK |
706 | {}, |
707 | }; | |
708 | MODULE_DEVICE_TABLE(of, exynos_tmu_match); | |
17be868e | 709 | |
17be868e | 710 | static inline struct exynos_tmu_platform_data *exynos_get_driver_data( |
cebe7373 | 711 | struct platform_device *pdev, int id) |
17be868e | 712 | { |
cebe7373 ADK |
713 | struct exynos_tmu_init_data *data_table; |
714 | struct exynos_tmu_platform_data *tmu_data; | |
73b5b1d7 SK |
715 | const struct of_device_id *match; |
716 | ||
717 | match = of_match_node(exynos_tmu_match, pdev->dev.of_node); | |
718 | if (!match) | |
719 | return NULL; | |
720 | data_table = (struct exynos_tmu_init_data *) match->data; | |
721 | if (!data_table || id >= data_table->tmu_count) | |
722 | return NULL; | |
723 | tmu_data = data_table->tmu_data; | |
724 | return (struct exynos_tmu_platform_data *) (tmu_data + id); | |
7e0b55e6 | 725 | } |
bbf63be4 | 726 | |
cebe7373 | 727 | static int exynos_map_dt_data(struct platform_device *pdev) |
9d97e5c8 | 728 | { |
cebe7373 ADK |
729 | struct exynos_tmu_data *data = platform_get_drvdata(pdev); |
730 | struct exynos_tmu_platform_data *pdata; | |
731 | struct resource res; | |
498d22f6 | 732 | int ret; |
cebe7373 | 733 | |
73b5b1d7 | 734 | if (!data || !pdev->dev.of_node) |
cebe7373 | 735 | return -ENODEV; |
9d97e5c8 | 736 | |
498d22f6 ADK |
737 | /* |
738 | * Try enabling the regulator if found | |
739 | * TODO: Add regulator as an SOC feature, so that regulator enable | |
740 | * is a compulsory call. | |
741 | */ | |
742 | data->regulator = devm_regulator_get(&pdev->dev, "vtmu"); | |
743 | if (!IS_ERR(data->regulator)) { | |
744 | ret = regulator_enable(data->regulator); | |
745 | if (ret) { | |
746 | dev_err(&pdev->dev, "failed to enable vtmu\n"); | |
747 | return ret; | |
748 | } | |
749 | } else { | |
750 | dev_info(&pdev->dev, "Regulator node (vtmu) not found\n"); | |
751 | } | |
752 | ||
cebe7373 ADK |
753 | data->id = of_alias_get_id(pdev->dev.of_node, "tmuctrl"); |
754 | if (data->id < 0) | |
755 | data->id = 0; | |
17be868e | 756 | |
cebe7373 ADK |
757 | data->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); |
758 | if (data->irq <= 0) { | |
759 | dev_err(&pdev->dev, "failed to get IRQ\n"); | |
760 | return -ENODEV; | |
761 | } | |
762 | ||
763 | if (of_address_to_resource(pdev->dev.of_node, 0, &res)) { | |
764 | dev_err(&pdev->dev, "failed to get Resource 0\n"); | |
765 | return -ENODEV; | |
766 | } | |
767 | ||
768 | data->base = devm_ioremap(&pdev->dev, res.start, resource_size(&res)); | |
769 | if (!data->base) { | |
770 | dev_err(&pdev->dev, "Failed to ioremap memory\n"); | |
771 | return -EADDRNOTAVAIL; | |
772 | } | |
773 | ||
774 | pdata = exynos_get_driver_data(pdev, data->id); | |
9d97e5c8 DK |
775 | if (!pdata) { |
776 | dev_err(&pdev->dev, "No platform init data supplied.\n"); | |
777 | return -ENODEV; | |
778 | } | |
56adb9ef | 779 | |
cebe7373 | 780 | data->pdata = pdata; |
56adb9ef BZ |
781 | data->soc = pdata->type; |
782 | ||
783 | switch (data->soc) { | |
784 | case SOC_ARCH_EXYNOS4210: | |
785 | data->tmu_initialize = exynos4210_tmu_initialize; | |
786 | data->tmu_control = exynos4210_tmu_control; | |
787 | data->tmu_read = exynos4210_tmu_read; | |
788 | data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; | |
789 | break; | |
790 | case SOC_ARCH_EXYNOS3250: | |
791 | case SOC_ARCH_EXYNOS4412: | |
792 | case SOC_ARCH_EXYNOS5250: | |
793 | case SOC_ARCH_EXYNOS5260: | |
794 | case SOC_ARCH_EXYNOS5420: | |
795 | case SOC_ARCH_EXYNOS5420_TRIMINFO: | |
796 | data->tmu_initialize = exynos4412_tmu_initialize; | |
797 | data->tmu_control = exynos4210_tmu_control; | |
798 | data->tmu_read = exynos4412_tmu_read; | |
799 | data->tmu_set_emulation = exynos4412_tmu_set_emulation; | |
800 | data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; | |
801 | break; | |
802 | case SOC_ARCH_EXYNOS5440: | |
803 | data->tmu_initialize = exynos5440_tmu_initialize; | |
804 | data->tmu_control = exynos5440_tmu_control; | |
805 | data->tmu_read = exynos5440_tmu_read; | |
806 | data->tmu_set_emulation = exynos5440_tmu_set_emulation; | |
807 | data->tmu_clear_irqs = exynos5440_tmu_clear_irqs; | |
808 | break; | |
809 | default: | |
810 | dev_err(&pdev->dev, "Platform not supported\n"); | |
811 | return -EINVAL; | |
812 | } | |
813 | ||
d9b6ee14 ADK |
814 | /* |
815 | * Check if the TMU shares some registers and then try to map the | |
816 | * memory of common registers. | |
817 | */ | |
56adb9ef BZ |
818 | if (data->soc != SOC_ARCH_EXYNOS5420_TRIMINFO && |
819 | data->soc != SOC_ARCH_EXYNOS5440) | |
d9b6ee14 ADK |
820 | return 0; |
821 | ||
822 | if (of_address_to_resource(pdev->dev.of_node, 1, &res)) { | |
823 | dev_err(&pdev->dev, "failed to get Resource 1\n"); | |
824 | return -ENODEV; | |
825 | } | |
826 | ||
9025d563 | 827 | data->base_second = devm_ioremap(&pdev->dev, res.start, |
d9b6ee14 | 828 | resource_size(&res)); |
9025d563 | 829 | if (!data->base_second) { |
d9b6ee14 ADK |
830 | dev_err(&pdev->dev, "Failed to ioremap memory\n"); |
831 | return -ENOMEM; | |
832 | } | |
cebe7373 ADK |
833 | |
834 | return 0; | |
835 | } | |
836 | ||
837 | static int exynos_tmu_probe(struct platform_device *pdev) | |
838 | { | |
839 | struct exynos_tmu_data *data; | |
840 | struct exynos_tmu_platform_data *pdata; | |
841 | struct thermal_sensor_conf *sensor_conf; | |
842 | int ret, i; | |
843 | ||
79e093c3 ADK |
844 | data = devm_kzalloc(&pdev->dev, sizeof(struct exynos_tmu_data), |
845 | GFP_KERNEL); | |
2a9675b3 | 846 | if (!data) |
9d97e5c8 | 847 | return -ENOMEM; |
9d97e5c8 | 848 | |
cebe7373 ADK |
849 | platform_set_drvdata(pdev, data); |
850 | mutex_init(&data->lock); | |
9d97e5c8 | 851 | |
cebe7373 ADK |
852 | ret = exynos_map_dt_data(pdev); |
853 | if (ret) | |
854 | return ret; | |
9d97e5c8 | 855 | |
cebe7373 | 856 | pdata = data->pdata; |
9d97e5c8 | 857 | |
cebe7373 | 858 | INIT_WORK(&data->irq_work, exynos_tmu_work); |
9d97e5c8 | 859 | |
2a16279c | 860 | data->clk = devm_clk_get(&pdev->dev, "tmu_apbif"); |
9d97e5c8 | 861 | if (IS_ERR(data->clk)) { |
9d97e5c8 | 862 | dev_err(&pdev->dev, "Failed to get clock\n"); |
79e093c3 | 863 | return PTR_ERR(data->clk); |
9d97e5c8 DK |
864 | } |
865 | ||
14a11dc7 NKC |
866 | data->clk_sec = devm_clk_get(&pdev->dev, "tmu_triminfo_apbif"); |
867 | if (IS_ERR(data->clk_sec)) { | |
868 | if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) { | |
869 | dev_err(&pdev->dev, "Failed to get triminfo clock\n"); | |
870 | return PTR_ERR(data->clk_sec); | |
871 | } | |
872 | } else { | |
873 | ret = clk_prepare(data->clk_sec); | |
874 | if (ret) { | |
875 | dev_err(&pdev->dev, "Failed to get clock\n"); | |
876 | return ret; | |
877 | } | |
878 | } | |
879 | ||
2a16279c | 880 | ret = clk_prepare(data->clk); |
14a11dc7 NKC |
881 | if (ret) { |
882 | dev_err(&pdev->dev, "Failed to get clock\n"); | |
883 | goto err_clk_sec; | |
884 | } | |
2a16279c | 885 | |
f22d9c03 | 886 | ret = exynos_tmu_initialize(pdev); |
9d97e5c8 DK |
887 | if (ret) { |
888 | dev_err(&pdev->dev, "Failed to initialize TMU\n"); | |
889 | goto err_clk; | |
890 | } | |
891 | ||
f22d9c03 | 892 | exynos_tmu_control(pdev, true); |
9d97e5c8 | 893 | |
cebe7373 ADK |
894 | /* Allocate a structure to register with the exynos core thermal */ |
895 | sensor_conf = devm_kzalloc(&pdev->dev, | |
896 | sizeof(struct thermal_sensor_conf), GFP_KERNEL); | |
897 | if (!sensor_conf) { | |
cebe7373 ADK |
898 | ret = -ENOMEM; |
899 | goto err_clk; | |
900 | } | |
901 | sprintf(sensor_conf->name, "therm_zone%d", data->id); | |
902 | sensor_conf->read_temperature = (int (*)(void *))exynos_tmu_read; | |
903 | sensor_conf->write_emul_temp = | |
904 | (int (*)(void *, unsigned long))exynos_tmu_set_emulation; | |
905 | sensor_conf->driver_data = data; | |
906 | sensor_conf->trip_data.trip_count = pdata->trigger_enable[0] + | |
bb34b4c8 ADK |
907 | pdata->trigger_enable[1] + pdata->trigger_enable[2]+ |
908 | pdata->trigger_enable[3]; | |
7e0b55e6 | 909 | |
cebe7373 ADK |
910 | for (i = 0; i < sensor_conf->trip_data.trip_count; i++) { |
911 | sensor_conf->trip_data.trip_val[i] = | |
7e0b55e6 | 912 | pdata->threshold + pdata->trigger_levels[i]; |
cebe7373 | 913 | sensor_conf->trip_data.trip_type[i] = |
5c3cf552 ADK |
914 | pdata->trigger_type[i]; |
915 | } | |
7e0b55e6 | 916 | |
cebe7373 | 917 | sensor_conf->trip_data.trigger_falling = pdata->threshold_falling; |
4f0a6847 | 918 | |
cebe7373 | 919 | sensor_conf->cooling_data.freq_clip_count = pdata->freq_tab_count; |
7e0b55e6 | 920 | for (i = 0; i < pdata->freq_tab_count; i++) { |
cebe7373 | 921 | sensor_conf->cooling_data.freq_data[i].freq_clip_max = |
7e0b55e6 | 922 | pdata->freq_tab[i].freq_clip_max; |
cebe7373 | 923 | sensor_conf->cooling_data.freq_data[i].temp_level = |
7e0b55e6 ADK |
924 | pdata->freq_tab[i].temp_level; |
925 | } | |
cebe7373 ADK |
926 | sensor_conf->dev = &pdev->dev; |
927 | /* Register the sensor with thermal management interface */ | |
928 | ret = exynos_register_thermal(sensor_conf); | |
7e0b55e6 | 929 | if (ret) { |
0f1be51c EV |
930 | if (ret != -EPROBE_DEFER) |
931 | dev_err(&pdev->dev, | |
932 | "Failed to register thermal interface: %d\n", | |
933 | ret); | |
7e0b55e6 ADK |
934 | goto err_clk; |
935 | } | |
cebe7373 ADK |
936 | data->reg_conf = sensor_conf; |
937 | ||
938 | ret = devm_request_irq(&pdev->dev, data->irq, exynos_tmu_irq, | |
939 | IRQF_TRIGGER_RISING | IRQF_SHARED, dev_name(&pdev->dev), data); | |
940 | if (ret) { | |
941 | dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq); | |
942 | goto err_clk; | |
943 | } | |
bbf63be4 | 944 | |
9d97e5c8 | 945 | return 0; |
9d97e5c8 | 946 | err_clk: |
2a16279c | 947 | clk_unprepare(data->clk); |
14a11dc7 NKC |
948 | err_clk_sec: |
949 | if (!IS_ERR(data->clk_sec)) | |
950 | clk_unprepare(data->clk_sec); | |
9d97e5c8 DK |
951 | return ret; |
952 | } | |
953 | ||
4eab7a9e | 954 | static int exynos_tmu_remove(struct platform_device *pdev) |
9d97e5c8 | 955 | { |
f22d9c03 | 956 | struct exynos_tmu_data *data = platform_get_drvdata(pdev); |
9d97e5c8 | 957 | |
cebe7373 | 958 | exynos_unregister_thermal(data->reg_conf); |
7e0b55e6 | 959 | |
4215688e BZ |
960 | exynos_tmu_control(pdev, false); |
961 | ||
2a16279c | 962 | clk_unprepare(data->clk); |
14a11dc7 NKC |
963 | if (!IS_ERR(data->clk_sec)) |
964 | clk_unprepare(data->clk_sec); | |
9d97e5c8 | 965 | |
498d22f6 ADK |
966 | if (!IS_ERR(data->regulator)) |
967 | regulator_disable(data->regulator); | |
968 | ||
9d97e5c8 DK |
969 | return 0; |
970 | } | |
971 | ||
08cd6753 | 972 | #ifdef CONFIG_PM_SLEEP |
f22d9c03 | 973 | static int exynos_tmu_suspend(struct device *dev) |
9d97e5c8 | 974 | { |
f22d9c03 | 975 | exynos_tmu_control(to_platform_device(dev), false); |
9d97e5c8 DK |
976 | |
977 | return 0; | |
978 | } | |
979 | ||
f22d9c03 | 980 | static int exynos_tmu_resume(struct device *dev) |
9d97e5c8 | 981 | { |
08cd6753 RW |
982 | struct platform_device *pdev = to_platform_device(dev); |
983 | ||
f22d9c03 ADK |
984 | exynos_tmu_initialize(pdev); |
985 | exynos_tmu_control(pdev, true); | |
9d97e5c8 DK |
986 | |
987 | return 0; | |
988 | } | |
08cd6753 | 989 | |
f22d9c03 ADK |
990 | static SIMPLE_DEV_PM_OPS(exynos_tmu_pm, |
991 | exynos_tmu_suspend, exynos_tmu_resume); | |
992 | #define EXYNOS_TMU_PM (&exynos_tmu_pm) | |
9d97e5c8 | 993 | #else |
f22d9c03 | 994 | #define EXYNOS_TMU_PM NULL |
9d97e5c8 DK |
995 | #endif |
996 | ||
f22d9c03 | 997 | static struct platform_driver exynos_tmu_driver = { |
9d97e5c8 | 998 | .driver = { |
f22d9c03 | 999 | .name = "exynos-tmu", |
f22d9c03 | 1000 | .pm = EXYNOS_TMU_PM, |
73b5b1d7 | 1001 | .of_match_table = exynos_tmu_match, |
9d97e5c8 | 1002 | }, |
f22d9c03 | 1003 | .probe = exynos_tmu_probe, |
4eab7a9e | 1004 | .remove = exynos_tmu_remove, |
9d97e5c8 DK |
1005 | }; |
1006 | ||
f22d9c03 | 1007 | module_platform_driver(exynos_tmu_driver); |
9d97e5c8 | 1008 | |
f22d9c03 | 1009 | MODULE_DESCRIPTION("EXYNOS TMU Driver"); |
9d97e5c8 DK |
1010 | MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>"); |
1011 | MODULE_LICENSE("GPL"); | |
f22d9c03 | 1012 | MODULE_ALIAS("platform:exynos-tmu"); |