blob: 51d8cc70b6d66df727de77eb263ee66204f191c4 [file] [log] [blame]
Donggeun Kim9d97e5c2011-09-07 18:49:08 +09001/*
Amit Daniel Kachhap59dfa542013-06-24 16:20:26 +05302 * exynos_tmu.c - Samsung EXYNOS TMU (Thermal Management Unit)
Donggeun Kim9d97e5c2011-09-07 18:49:08 +09003 *
4 * Copyright (C) 2011 Samsung Electronics
5 * Donggeun Kim <dg77.kim@samsung.com>
Amit Daniel Kachhapc48cbba2012-08-16 17:11:41 +05306 * Amit Daniel Kachhap <amit.kachhap@linaro.org>
Donggeun Kim9d97e5c2011-09-07 18:49:08 +09007 *
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
Donggeun Kim9d97e5c2011-09-07 18:49:08 +090024#include <linux/clk.h>
Donggeun Kim9d97e5c2011-09-07 18:49:08 +090025#include <linux/io.h>
Amit Daniel Kachhap1b678642013-06-24 16:20:25 +053026#include <linux/interrupt.h>
27#include <linux/module.h>
Amit Daniel Kachhapf22d9c02012-08-16 17:11:42 +053028#include <linux/of.h>
Amit Daniel Kachhapcebe7372013-06-24 16:20:39 +053029#include <linux/of_address.h>
30#include <linux/of_irq.h>
Amit Daniel Kachhap1b678642013-06-24 16:20:25 +053031#include <linux/platform_device.h>
Amit Daniel Kachhap498d22f2013-06-24 16:20:47 +053032#include <linux/regulator/consumer.h>
Amit Daniel Kachhap1b678642013-06-24 16:20:25 +053033
34#include "exynos_thermal_common.h"
Amit Daniel Kachhap0c1836a2013-06-24 16:20:27 +053035#include "exynos_tmu.h"
Amit Daniel Kachhape6b79912013-06-24 16:20:28 +053036#include "exynos_tmu_data.h"
Donggeun Kim9d97e5c2011-09-07 18:49:08 +090037
Amit Daniel Kachhapcebe7372013-06-24 16:20:39 +053038/**
39 * struct exynos_tmu_data : A structure to hold the private data of the TMU
40 driver
41 * @id: identifier of the one instance of the TMU controller.
42 * @pdata: pointer to the tmu platform/configuration data
43 * @base: base address of the single instance of the TMU controller.
Naveen Krishna Chatradhi9025d562013-12-19 11:36:08 +053044 * @base_second: base address of the common registers of the TMU controller.
Amit Daniel Kachhapcebe7372013-06-24 16:20:39 +053045 * @irq: irq number of the TMU controller.
46 * @soc: id of the SOC type.
47 * @irq_work: pointer to the irq work structure.
48 * @lock: lock to implement synchronization.
49 * @clk: pointer to the clock structure.
Naveen Krishna Chatradhi14a11dc2013-12-19 11:36:31 +053050 * @clk_sec: pointer to the clock structure for accessing the base_second.
Amit Daniel Kachhapcebe7372013-06-24 16:20:39 +053051 * @temp_error1: fused value of the first point trim.
52 * @temp_error2: fused value of the second point trim.
Amit Daniel Kachhap498d22f2013-06-24 16:20:47 +053053 * @regulator: pointer to the TMU regulator structure.
Amit Daniel Kachhapcebe7372013-06-24 16:20:39 +053054 * @reg_conf: pointer to structure to register with core thermal.
Bartlomiej Zolnierkiewicz72d11002014-11-13 16:01:13 +010055 * @tmu_initialize: SoC specific TMU initialization method
Bartlomiej Zolnierkiewicz37f90342014-11-13 16:01:15 +010056 * @tmu_control: SoC specific TMU control method
Bartlomiej Zolnierkiewiczb79985c2014-11-13 16:01:16 +010057 * @tmu_read: SoC specific TMU temperature read method
Bartlomiej Zolnierkiewicz285d9942014-11-13 16:01:18 +010058 * @tmu_set_emulation: SoC specific TMU emulation setting method
Bartlomiej Zolnierkiewicza7331f72014-11-13 16:01:19 +010059 * @tmu_clear_irqs: SoC specific TMU interrupts clearing method
Amit Daniel Kachhapcebe7372013-06-24 16:20:39 +053060 */
Amit Daniel Kachhapf22d9c02012-08-16 17:11:42 +053061struct exynos_tmu_data {
Amit Daniel Kachhapcebe7372013-06-24 16:20:39 +053062 int id;
Amit Daniel Kachhapf22d9c02012-08-16 17:11:42 +053063 struct exynos_tmu_platform_data *pdata;
Donggeun Kim9d97e5c2011-09-07 18:49:08 +090064 void __iomem *base;
Naveen Krishna Chatradhi9025d562013-12-19 11:36:08 +053065 void __iomem *base_second;
Donggeun Kim9d97e5c2011-09-07 18:49:08 +090066 int irq;
Amit Daniel Kachhapf22d9c02012-08-16 17:11:42 +053067 enum soc_type soc;
Donggeun Kim9d97e5c2011-09-07 18:49:08 +090068 struct work_struct irq_work;
69 struct mutex lock;
Naveen Krishna Chatradhi14a11dc2013-12-19 11:36:31 +053070 struct clk *clk, *clk_sec;
Donggeun Kim9d97e5c2011-09-07 18:49:08 +090071 u8 temp_error1, temp_error2;
Amit Daniel Kachhap498d22f2013-06-24 16:20:47 +053072 struct regulator *regulator;
Amit Daniel Kachhapcebe7372013-06-24 16:20:39 +053073 struct thermal_sensor_conf *reg_conf;
Bartlomiej Zolnierkiewicz72d11002014-11-13 16:01:13 +010074 int (*tmu_initialize)(struct platform_device *pdev);
Bartlomiej Zolnierkiewicz37f90342014-11-13 16:01:15 +010075 void (*tmu_control)(struct platform_device *pdev, bool on);
Bartlomiej Zolnierkiewiczb79985c2014-11-13 16:01:16 +010076 int (*tmu_read)(struct exynos_tmu_data *data);
Bartlomiej Zolnierkiewicz285d9942014-11-13 16:01:18 +010077 void (*tmu_set_emulation)(struct exynos_tmu_data *data,
78 unsigned long temp);
Bartlomiej Zolnierkiewicza7331f72014-11-13 16:01:19 +010079 void (*tmu_clear_irqs)(struct exynos_tmu_data *data);
Donggeun Kim9d97e5c2011-09-07 18:49:08 +090080};
81
82/*
83 * TMU treats temperature as a mapped temperature code.
84 * The temperature is converted differently depending on the calibration type.
85 */
Amit Daniel Kachhapf22d9c02012-08-16 17:11:42 +053086static int temp_to_code(struct exynos_tmu_data *data, u8 temp)
Donggeun Kim9d97e5c2011-09-07 18:49:08 +090087{
Amit Daniel Kachhapf22d9c02012-08-16 17:11:42 +053088 struct exynos_tmu_platform_data *pdata = data->pdata;
Donggeun Kim9d97e5c2011-09-07 18:49:08 +090089 int temp_code;
90
Donggeun Kim9d97e5c2011-09-07 18:49:08 +090091 switch (pdata->cal_type) {
92 case TYPE_TWO_POINT_TRIMMING:
Amit Daniel Kachhapbb34b4c2013-06-24 16:20:30 +053093 temp_code = (temp - pdata->first_point_trim) *
94 (data->temp_error2 - data->temp_error1) /
95 (pdata->second_point_trim - pdata->first_point_trim) +
96 data->temp_error1;
Donggeun Kim9d97e5c2011-09-07 18:49:08 +090097 break;
98 case TYPE_ONE_POINT_TRIMMING:
Amit Daniel Kachhapbb34b4c2013-06-24 16:20:30 +053099 temp_code = temp + data->temp_error1 - pdata->first_point_trim;
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900100 break;
101 default:
Amit Daniel Kachhapbb34b4c2013-06-24 16:20:30 +0530102 temp_code = temp + pdata->default_temp_offset;
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900103 break;
104 }
Bartlomiej Zolnierkiewiczddb31d42014-07-31 19:11:03 +0200105
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900106 return temp_code;
107}
108
109/*
110 * Calculate a temperature value from a temperature code.
111 * The unit of the temperature is degree Celsius.
112 */
Amit Daniel Kachhapf22d9c02012-08-16 17:11:42 +0530113static int code_to_temp(struct exynos_tmu_data *data, u8 temp_code)
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900114{
Amit Daniel Kachhapf22d9c02012-08-16 17:11:42 +0530115 struct exynos_tmu_platform_data *pdata = data->pdata;
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900116 int temp;
117
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900118 switch (pdata->cal_type) {
119 case TYPE_TWO_POINT_TRIMMING:
Amit Daniel Kachhapbb34b4c2013-06-24 16:20:30 +0530120 temp = (temp_code - data->temp_error1) *
121 (pdata->second_point_trim - pdata->first_point_trim) /
122 (data->temp_error2 - data->temp_error1) +
123 pdata->first_point_trim;
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900124 break;
125 case TYPE_ONE_POINT_TRIMMING:
Amit Daniel Kachhapbb34b4c2013-06-24 16:20:30 +0530126 temp = temp_code - data->temp_error1 + pdata->first_point_trim;
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900127 break;
128 default:
Amit Daniel Kachhapbb34b4c2013-06-24 16:20:30 +0530129 temp = temp_code - pdata->default_temp_offset;
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900130 break;
131 }
Bartlomiej Zolnierkiewiczddb31d42014-07-31 19:11:03 +0200132
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900133 return temp;
134}
135
Bartlomiej Zolnierkiewicz8328a4b2014-11-13 16:01:11 +0100136static void sanitize_temp_error(struct exynos_tmu_data *data, u32 trim_info)
137{
138 struct exynos_tmu_platform_data *pdata = data->pdata;
139
140 data->temp_error1 = trim_info & EXYNOS_TMU_TEMP_MASK;
141 data->temp_error2 = ((trim_info >> EXYNOS_TRIMINFO_85_SHIFT) &
142 EXYNOS_TMU_TEMP_MASK);
143
144 if (!data->temp_error1 ||
145 (pdata->min_efuse_value > data->temp_error1) ||
146 (data->temp_error1 > pdata->max_efuse_value))
147 data->temp_error1 = pdata->efuse_value & EXYNOS_TMU_TEMP_MASK;
148
149 if (!data->temp_error2)
150 data->temp_error2 =
151 (pdata->efuse_value >> EXYNOS_TRIMINFO_85_SHIFT) &
152 EXYNOS_TMU_TEMP_MASK;
153}
154
Bartlomiej Zolnierkiewiczfe877892014-11-13 16:01:12 +0100155static u32 get_th_reg(struct exynos_tmu_data *data, u32 threshold, bool falling)
156{
157 struct exynos_tmu_platform_data *pdata = data->pdata;
158 int i;
159
160 for (i = 0; i < pdata->non_hw_trigger_levels; i++) {
161 u8 temp = pdata->trigger_levels[i];
162
163 if (falling)
164 temp -= pdata->threshold_falling;
165 else
166 threshold &= ~(0xff << 8 * i);
167
168 threshold |= temp_to_code(data, temp) << 8 * i;
169 }
170
171 return threshold;
172}
173
Amit Daniel Kachhapf22d9c02012-08-16 17:11:42 +0530174static int exynos_tmu_initialize(struct platform_device *pdev)
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900175{
Amit Daniel Kachhapf22d9c02012-08-16 17:11:42 +0530176 struct exynos_tmu_data *data = platform_get_drvdata(pdev);
Bartlomiej Zolnierkiewicz72d11002014-11-13 16:01:13 +0100177 int ret;
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900178
179 mutex_lock(&data->lock);
180 clk_enable(data->clk);
Naveen Krishna Chatradhi14a11dc2013-12-19 11:36:31 +0530181 if (!IS_ERR(data->clk_sec))
182 clk_enable(data->clk_sec);
Bartlomiej Zolnierkiewicz72d11002014-11-13 16:01:13 +0100183 ret = data->tmu_initialize(pdev);
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900184 clk_disable(data->clk);
185 mutex_unlock(&data->lock);
Naveen Krishna Chatradhi14a11dc2013-12-19 11:36:31 +0530186 if (!IS_ERR(data->clk_sec))
187 clk_disable(data->clk_sec);
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900188
189 return ret;
190}
191
Bartlomiej Zolnierkiewiczd00671c2014-11-13 16:01:14 +0100192static u32 get_con_reg(struct exynos_tmu_data *data, u32 con)
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900193{
Amit Daniel Kachhapf22d9c02012-08-16 17:11:42 +0530194 struct exynos_tmu_platform_data *pdata = data->pdata;
Amit Daniel Kachhapf22d9c02012-08-16 17:11:42 +0530195
Lukasz Majewski86f53622013-10-09 08:29:52 +0200196 if (pdata->test_mux)
Bartlomiej Zolnierkiewiczbfb2b882014-11-13 16:01:00 +0100197 con |= (pdata->test_mux << EXYNOS4412_MUX_ADDR_SHIFT);
Lukasz Majewski86f53622013-10-09 08:29:52 +0200198
Bartlomiej Zolnierkiewicz99d67fb2014-07-31 19:11:06 +0200199 con &= ~(EXYNOS_TMU_REF_VOLTAGE_MASK << EXYNOS_TMU_REF_VOLTAGE_SHIFT);
200 con |= pdata->reference_voltage << EXYNOS_TMU_REF_VOLTAGE_SHIFT;
Amit Daniel Kachhapd0a0ce32013-06-24 16:20:29 +0530201
Bartlomiej Zolnierkiewicz99d67fb2014-07-31 19:11:06 +0200202 con &= ~(EXYNOS_TMU_BUF_SLOPE_SEL_MASK << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT);
203 con |= (pdata->gain << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT);
Amit Daniel Kachhapd0a0ce32013-06-24 16:20:29 +0530204
205 if (pdata->noise_cancel_mode) {
Bartlomiej Zolnierkiewiczb9504a62014-11-13 16:01:01 +0100206 con &= ~(EXYNOS_TMU_TRIP_MODE_MASK << EXYNOS_TMU_TRIP_MODE_SHIFT);
207 con |= (pdata->noise_cancel_mode << EXYNOS_TMU_TRIP_MODE_SHIFT);
Amit Daniel Kachhapf22d9c02012-08-16 17:11:42 +0530208 }
209
Bartlomiej Zolnierkiewiczd00671c2014-11-13 16:01:14 +0100210 return con;
211}
212
213static void exynos_tmu_control(struct platform_device *pdev, bool on)
214{
215 struct exynos_tmu_data *data = platform_get_drvdata(pdev);
Bartlomiej Zolnierkiewiczd00671c2014-11-13 16:01:14 +0100216
217 mutex_lock(&data->lock);
218 clk_enable(data->clk);
Bartlomiej Zolnierkiewicz37f90342014-11-13 16:01:15 +0100219 data->tmu_control(pdev, on);
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900220 clk_disable(data->clk);
221 mutex_unlock(&data->lock);
222}
223
Bartlomiej Zolnierkiewicz72d11002014-11-13 16:01:13 +0100224static int exynos4210_tmu_initialize(struct platform_device *pdev)
225{
226 struct exynos_tmu_data *data = platform_get_drvdata(pdev);
227 struct exynos_tmu_platform_data *pdata = data->pdata;
228 unsigned int status;
229 int ret = 0, threshold_code, i;
230
231 status = readb(data->base + EXYNOS_TMU_REG_STATUS);
232 if (!status) {
233 ret = -EBUSY;
234 goto out;
235 }
236
237 sanitize_temp_error(data, readl(data->base + EXYNOS_TMU_REG_TRIMINFO));
238
239 /* Write temperature code for threshold */
240 threshold_code = temp_to_code(data, pdata->threshold);
241 writeb(threshold_code, data->base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP);
242
243 for (i = 0; i < pdata->non_hw_trigger_levels; i++)
244 writeb(pdata->trigger_levels[i], data->base +
245 EXYNOS4210_TMU_REG_TRIG_LEVEL0 + i * 4);
246
Bartlomiej Zolnierkiewicza7331f72014-11-13 16:01:19 +0100247 data->tmu_clear_irqs(data);
Bartlomiej Zolnierkiewicz72d11002014-11-13 16:01:13 +0100248out:
249 return ret;
250}
251
252static int exynos4412_tmu_initialize(struct platform_device *pdev)
253{
254 struct exynos_tmu_data *data = platform_get_drvdata(pdev);
255 struct exynos_tmu_platform_data *pdata = data->pdata;
256 unsigned int status, trim_info, con, ctrl, rising_threshold;
257 int ret = 0, threshold_code, i;
258
259 status = readb(data->base + EXYNOS_TMU_REG_STATUS);
260 if (!status) {
261 ret = -EBUSY;
262 goto out;
263 }
264
265 if (data->soc == SOC_ARCH_EXYNOS3250 ||
266 data->soc == SOC_ARCH_EXYNOS4412 ||
267 data->soc == SOC_ARCH_EXYNOS5250) {
268 if (data->soc == SOC_ARCH_EXYNOS3250) {
269 ctrl = readl(data->base + EXYNOS_TMU_TRIMINFO_CON1);
270 ctrl |= EXYNOS_TRIMINFO_RELOAD_ENABLE;
271 writel(ctrl, data->base + EXYNOS_TMU_TRIMINFO_CON1);
272 }
273 ctrl = readl(data->base + EXYNOS_TMU_TRIMINFO_CON2);
274 ctrl |= EXYNOS_TRIMINFO_RELOAD_ENABLE;
275 writel(ctrl, data->base + EXYNOS_TMU_TRIMINFO_CON2);
276 }
277
278 /* On exynos5420 the triminfo register is in the shared space */
279 if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO)
280 trim_info = readl(data->base_second + EXYNOS_TMU_REG_TRIMINFO);
281 else
282 trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO);
283
284 sanitize_temp_error(data, trim_info);
285
286 /* Write temperature code for rising and falling threshold */
287 rising_threshold = readl(data->base + EXYNOS_THD_TEMP_RISE);
288 rising_threshold = get_th_reg(data, rising_threshold, false);
289 writel(rising_threshold, data->base + EXYNOS_THD_TEMP_RISE);
290 writel(get_th_reg(data, 0, true), data->base + EXYNOS_THD_TEMP_FALL);
291
Bartlomiej Zolnierkiewicza7331f72014-11-13 16:01:19 +0100292 data->tmu_clear_irqs(data);
Bartlomiej Zolnierkiewicz72d11002014-11-13 16:01:13 +0100293
294 /* if last threshold limit is also present */
295 i = pdata->max_trigger_level - 1;
296 if (pdata->trigger_levels[i] && pdata->trigger_type[i] == HW_TRIP) {
297 threshold_code = temp_to_code(data, pdata->trigger_levels[i]);
298 /* 1-4 level to be assigned in th0 reg */
299 rising_threshold &= ~(0xff << 8 * i);
300 rising_threshold |= threshold_code << 8 * i;
301 writel(rising_threshold, data->base + EXYNOS_THD_TEMP_RISE);
302 con = readl(data->base + EXYNOS_TMU_REG_CONTROL);
303 con |= (1 << EXYNOS_TMU_THERM_TRIP_EN_SHIFT);
304 writel(con, data->base + EXYNOS_TMU_REG_CONTROL);
305 }
306out:
307 return ret;
308}
309
310static int exynos5440_tmu_initialize(struct platform_device *pdev)
311{
312 struct exynos_tmu_data *data = platform_get_drvdata(pdev);
313 struct exynos_tmu_platform_data *pdata = data->pdata;
314 unsigned int trim_info = 0, con, rising_threshold;
315 int ret = 0, threshold_code, i;
316
317 /*
318 * For exynos5440 soc triminfo value is swapped between TMU0 and
319 * TMU2, so the below logic is needed.
320 */
321 switch (data->id) {
322 case 0:
323 trim_info = readl(data->base + EXYNOS5440_EFUSE_SWAP_OFFSET +
324 EXYNOS5440_TMU_S0_7_TRIM);
325 break;
326 case 1:
327 trim_info = readl(data->base + EXYNOS5440_TMU_S0_7_TRIM);
328 break;
329 case 2:
330 trim_info = readl(data->base - EXYNOS5440_EFUSE_SWAP_OFFSET +
331 EXYNOS5440_TMU_S0_7_TRIM);
332 }
333 sanitize_temp_error(data, trim_info);
334
335 /* Write temperature code for rising and falling threshold */
336 rising_threshold = readl(data->base + EXYNOS5440_TMU_S0_7_TH0);
337 rising_threshold = get_th_reg(data, rising_threshold, false);
338 writel(rising_threshold, data->base + EXYNOS5440_TMU_S0_7_TH0);
339 writel(0, data->base + EXYNOS5440_TMU_S0_7_TH1);
340
Bartlomiej Zolnierkiewicza7331f72014-11-13 16:01:19 +0100341 data->tmu_clear_irqs(data);
Bartlomiej Zolnierkiewicz72d11002014-11-13 16:01:13 +0100342
343 /* if last threshold limit is also present */
344 i = pdata->max_trigger_level - 1;
345 if (pdata->trigger_levels[i] && pdata->trigger_type[i] == HW_TRIP) {
346 threshold_code = temp_to_code(data, pdata->trigger_levels[i]);
347 /* 5th level to be assigned in th2 reg */
348 rising_threshold =
349 threshold_code << EXYNOS5440_TMU_TH_RISE4_SHIFT;
350 writel(rising_threshold, data->base + EXYNOS5440_TMU_S0_7_TH2);
351 con = readl(data->base + EXYNOS5440_TMU_S0_7_CTRL);
352 con |= (1 << EXYNOS_TMU_THERM_TRIP_EN_SHIFT);
353 writel(con, data->base + EXYNOS5440_TMU_S0_7_CTRL);
354 }
355 /* Clear the PMIN in the common TMU register */
356 if (!data->id)
357 writel(0, data->base_second + EXYNOS5440_TMU_PMIN);
358 return ret;
359}
360
Bartlomiej Zolnierkiewicz37f90342014-11-13 16:01:15 +0100361static void exynos4210_tmu_control(struct platform_device *pdev, bool on)
362{
363 struct exynos_tmu_data *data = platform_get_drvdata(pdev);
364 struct exynos_tmu_platform_data *pdata = data->pdata;
365 unsigned int con, interrupt_en;
366
367 con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL));
368
369 if (on) {
370 con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT);
371 interrupt_en =
372 pdata->trigger_enable[3] << EXYNOS_TMU_INTEN_RISE3_SHIFT |
373 pdata->trigger_enable[2] << EXYNOS_TMU_INTEN_RISE2_SHIFT |
374 pdata->trigger_enable[1] << EXYNOS_TMU_INTEN_RISE1_SHIFT |
375 pdata->trigger_enable[0] << EXYNOS_TMU_INTEN_RISE0_SHIFT;
Bartlomiej Zolnierkiewicze0761532014-11-13 16:01:20 +0100376 if (data->soc != SOC_ARCH_EXYNOS4210)
Bartlomiej Zolnierkiewicz37f90342014-11-13 16:01:15 +0100377 interrupt_en |=
378 interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT;
379 } else {
380 con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT);
381 interrupt_en = 0; /* Disable all interrupts */
382 }
383 writel(interrupt_en, data->base + EXYNOS_TMU_REG_INTEN);
384 writel(con, data->base + EXYNOS_TMU_REG_CONTROL);
385}
386
387static void exynos5440_tmu_control(struct platform_device *pdev, bool on)
388{
389 struct exynos_tmu_data *data = platform_get_drvdata(pdev);
390 struct exynos_tmu_platform_data *pdata = data->pdata;
391 unsigned int con, interrupt_en;
392
393 con = get_con_reg(data, readl(data->base + EXYNOS5440_TMU_S0_7_CTRL));
394
395 if (on) {
396 con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT);
397 interrupt_en =
398 pdata->trigger_enable[3] << EXYNOS5440_TMU_INTEN_RISE3_SHIFT |
399 pdata->trigger_enable[2] << EXYNOS5440_TMU_INTEN_RISE2_SHIFT |
400 pdata->trigger_enable[1] << EXYNOS5440_TMU_INTEN_RISE1_SHIFT |
401 pdata->trigger_enable[0] << EXYNOS5440_TMU_INTEN_RISE0_SHIFT;
Bartlomiej Zolnierkiewicze0761532014-11-13 16:01:20 +0100402 interrupt_en |= interrupt_en << EXYNOS5440_TMU_INTEN_FALL0_SHIFT;
Bartlomiej Zolnierkiewicz37f90342014-11-13 16:01:15 +0100403 } else {
404 con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT);
405 interrupt_en = 0; /* Disable all interrupts */
406 }
407 writel(interrupt_en, data->base + EXYNOS5440_TMU_S0_7_IRQEN);
408 writel(con, data->base + EXYNOS5440_TMU_S0_7_CTRL);
409}
410
Amit Daniel Kachhapf22d9c02012-08-16 17:11:42 +0530411static int exynos_tmu_read(struct exynos_tmu_data *data)
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900412{
Bartlomiej Zolnierkiewiczb79985c2014-11-13 16:01:16 +0100413 int ret;
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900414
415 mutex_lock(&data->lock);
416 clk_enable(data->clk);
Bartlomiej Zolnierkiewiczb79985c2014-11-13 16:01:16 +0100417 ret = data->tmu_read(data);
418 if (ret >= 0)
419 ret = code_to_temp(data, ret);
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900420 clk_disable(data->clk);
421 mutex_unlock(&data->lock);
422
Bartlomiej Zolnierkiewiczb79985c2014-11-13 16:01:16 +0100423 return ret;
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900424}
425
Amit Daniel Kachhapbffd1f82013-02-11 03:54:23 +0000426#ifdef CONFIG_THERMAL_EMULATION
Bartlomiej Zolnierkiewicz154013e2014-11-13 16:01:17 +0100427static u32 get_emul_con_reg(struct exynos_tmu_data *data, unsigned int val,
428 unsigned long temp)
429{
Bartlomiej Zolnierkiewicz154013e2014-11-13 16:01:17 +0100430 if (temp) {
431 temp /= MCELSIUS;
432
Bartlomiej Zolnierkiewiczd564b552014-11-13 16:01:21 +0100433 if (data->soc != SOC_ARCH_EXYNOS5440) {
Bartlomiej Zolnierkiewicz154013e2014-11-13 16:01:17 +0100434 val &= ~(EXYNOS_EMUL_TIME_MASK << EXYNOS_EMUL_TIME_SHIFT);
435 val |= (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT);
436 }
437 val &= ~(EXYNOS_EMUL_DATA_MASK << EXYNOS_EMUL_DATA_SHIFT);
438 val |= (temp_to_code(data, temp) << EXYNOS_EMUL_DATA_SHIFT) |
439 EXYNOS_EMUL_ENABLE;
440 } else {
441 val &= ~EXYNOS_EMUL_ENABLE;
442 }
443
444 return val;
445}
446
Bartlomiej Zolnierkiewicz285d9942014-11-13 16:01:18 +0100447static void exynos4412_tmu_set_emulation(struct exynos_tmu_data *data,
448 unsigned long temp)
449{
450 unsigned int val;
451 u32 emul_con;
452
453 if (data->soc == SOC_ARCH_EXYNOS5260)
454 emul_con = EXYNOS5260_EMUL_CON;
455 else
456 emul_con = EXYNOS_EMUL_CON;
457
458 val = readl(data->base + emul_con);
459 val = get_emul_con_reg(data, val, temp);
460 writel(val, data->base + emul_con);
461}
462
463static void exynos5440_tmu_set_emulation(struct exynos_tmu_data *data,
464 unsigned long temp)
465{
466 unsigned int val;
467
468 val = readl(data->base + EXYNOS5440_TMU_S0_7_DEBUG);
469 val = get_emul_con_reg(data, val, temp);
470 writel(val, data->base + EXYNOS5440_TMU_S0_7_DEBUG);
471}
472
Amit Daniel Kachhapbffd1f82013-02-11 03:54:23 +0000473static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp)
474{
475 struct exynos_tmu_data *data = drv_data;
Amit Daniel Kachhapb8d582b2013-06-24 16:20:31 +0530476 struct exynos_tmu_platform_data *pdata = data->pdata;
Amit Daniel Kachhapbffd1f82013-02-11 03:54:23 +0000477 int ret = -EINVAL;
478
Bartlomiej Zolnierkiewiczef3f80f2014-11-13 16:01:22 +0100479 if (data->soc == SOC_ARCH_EXYNOS4210)
Amit Daniel Kachhapbffd1f82013-02-11 03:54:23 +0000480 goto out;
481
482 if (temp && temp < MCELSIUS)
483 goto out;
484
485 mutex_lock(&data->lock);
486 clk_enable(data->clk);
Bartlomiej Zolnierkiewicz285d9942014-11-13 16:01:18 +0100487 data->tmu_set_emulation(data, temp);
Amit Daniel Kachhapbffd1f82013-02-11 03:54:23 +0000488 clk_disable(data->clk);
489 mutex_unlock(&data->lock);
490 return 0;
491out:
492 return ret;
493}
494#else
Bartlomiej Zolnierkiewicz285d9942014-11-13 16:01:18 +0100495#define exynos4412_tmu_set_emulation NULL
496#define exynos5440_tmu_set_emulation NULL
Amit Daniel Kachhapbffd1f82013-02-11 03:54:23 +0000497static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp)
498 { return -EINVAL; }
499#endif/*CONFIG_THERMAL_EMULATION*/
500
Bartlomiej Zolnierkiewiczb79985c2014-11-13 16:01:16 +0100501static int exynos4210_tmu_read(struct exynos_tmu_data *data)
502{
503 int ret = readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP);
504
505 /* "temp_code" should range between 75 and 175 */
506 return (ret < 75 || ret > 175) ? -ENODATA : ret;
507}
508
509static int exynos4412_tmu_read(struct exynos_tmu_data *data)
510{
511 return readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP);
512}
513
514static int exynos5440_tmu_read(struct exynos_tmu_data *data)
515{
516 return readb(data->base + EXYNOS5440_TMU_S0_7_TEMP);
517}
518
Amit Daniel Kachhapf22d9c02012-08-16 17:11:42 +0530519static void exynos_tmu_work(struct work_struct *work)
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900520{
Amit Daniel Kachhapf22d9c02012-08-16 17:11:42 +0530521 struct exynos_tmu_data *data = container_of(work,
522 struct exynos_tmu_data, irq_work);
Bartlomiej Zolnierkiewiczb835ced2014-10-03 18:17:17 +0200523 unsigned int val_type;
Amit Daniel Kachhapa0395ee2013-06-24 16:20:43 +0530524
Naveen Krishna Chatradhi14a11dc2013-12-19 11:36:31 +0530525 if (!IS_ERR(data->clk_sec))
526 clk_enable(data->clk_sec);
Amit Daniel Kachhapa0395ee2013-06-24 16:20:43 +0530527 /* Find which sensor generated this interrupt */
Bartlomiej Zolnierkiewicz421d5d12014-11-13 16:01:05 +0100528 if (data->soc == SOC_ARCH_EXYNOS5440) {
529 val_type = readl(data->base_second + EXYNOS5440_TMU_IRQ_STATUS);
Amit Daniel Kachhapa0395ee2013-06-24 16:20:43 +0530530 if (!((val_type >> data->id) & 0x1))
531 goto out;
532 }
Naveen Krishna Chatradhi14a11dc2013-12-19 11:36:31 +0530533 if (!IS_ERR(data->clk_sec))
534 clk_disable(data->clk_sec);
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900535
Amit Daniel Kachhapcebe7372013-06-24 16:20:39 +0530536 exynos_report_trigger(data->reg_conf);
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900537 mutex_lock(&data->lock);
538 clk_enable(data->clk);
Amit Daniel Kachhapb8d582b2013-06-24 16:20:31 +0530539
Amit Daniel Kachhapa4463c42013-06-24 16:20:33 +0530540 /* TODO: take action based on particular interrupt */
Bartlomiej Zolnierkiewicza7331f72014-11-13 16:01:19 +0100541 data->tmu_clear_irqs(data);
Amit Daniel Kachhapb8d582b2013-06-24 16:20:31 +0530542
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900543 clk_disable(data->clk);
544 mutex_unlock(&data->lock);
Amit Daniel Kachhapa0395ee2013-06-24 16:20:43 +0530545out:
Amit Daniel Kachhapf22d9c02012-08-16 17:11:42 +0530546 enable_irq(data->irq);
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900547}
548
Bartlomiej Zolnierkiewicza7331f72014-11-13 16:01:19 +0100549static void exynos4210_tmu_clear_irqs(struct exynos_tmu_data *data)
550{
551 unsigned int val_irq;
552 u32 tmu_intstat, tmu_intclear;
553
554 if (data->soc == SOC_ARCH_EXYNOS5260) {
555 tmu_intstat = EXYNOS5260_TMU_REG_INTSTAT;
556 tmu_intclear = EXYNOS5260_TMU_REG_INTCLEAR;
557 } else {
558 tmu_intstat = EXYNOS_TMU_REG_INTSTAT;
559 tmu_intclear = EXYNOS_TMU_REG_INTCLEAR;
560 }
561
562 val_irq = readl(data->base + tmu_intstat);
563 /*
564 * Clear the interrupts. Please note that the documentation for
565 * Exynos3250, Exynos4412, Exynos5250 and Exynos5260 incorrectly
566 * states that INTCLEAR register has a different placing of bits
567 * responsible for FALL IRQs than INTSTAT register. Exynos5420
568 * and Exynos5440 documentation is correct (Exynos4210 doesn't
569 * support FALL IRQs at all).
570 */
571 writel(val_irq, data->base + tmu_intclear);
572}
573
574static void exynos5440_tmu_clear_irqs(struct exynos_tmu_data *data)
575{
576 unsigned int val_irq;
577
578 val_irq = readl(data->base + EXYNOS5440_TMU_S0_7_IRQ);
579 /* clear the interrupts */
580 writel(val_irq, data->base + EXYNOS5440_TMU_S0_7_IRQ);
581}
582
Amit Daniel Kachhapf22d9c02012-08-16 17:11:42 +0530583static irqreturn_t exynos_tmu_irq(int irq, void *id)
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900584{
Amit Daniel Kachhapf22d9c02012-08-16 17:11:42 +0530585 struct exynos_tmu_data *data = id;
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900586
587 disable_irq_nosync(irq);
588 schedule_work(&data->irq_work);
589
590 return IRQ_HANDLED;
591}
Amit Daniel Kachhap17be8682012-08-16 17:11:44 +0530592
Amit Daniel Kachhap17be8682012-08-16 17:11:44 +0530593static const struct of_device_id exynos_tmu_match[] = {
594 {
Chanwoo Choi1fe56dc2014-07-01 09:33:19 +0900595 .compatible = "samsung,exynos3250-tmu",
596 .data = (void *)EXYNOS3250_TMU_DRV_DATA,
597 },
598 {
Amit Daniel Kachhap17be8682012-08-16 17:11:44 +0530599 .compatible = "samsung,exynos4210-tmu",
600 .data = (void *)EXYNOS4210_TMU_DRV_DATA,
601 },
602 {
Sachin Kamatb6cee532013-04-18 11:37:59 +0000603 .compatible = "samsung,exynos4412-tmu",
Lukasz Majewski14ddfae2013-10-09 08:29:51 +0200604 .data = (void *)EXYNOS4412_TMU_DRV_DATA,
Sachin Kamatb6cee532013-04-18 11:37:59 +0000605 },
606 {
Amit Daniel Kachhap17be8682012-08-16 17:11:44 +0530607 .compatible = "samsung,exynos5250-tmu",
Amit Daniel Kachhape6b79912013-06-24 16:20:28 +0530608 .data = (void *)EXYNOS5250_TMU_DRV_DATA,
Amit Daniel Kachhap17be8682012-08-16 17:11:44 +0530609 },
Amit Daniel Kachhap90542542013-06-24 16:20:44 +0530610 {
Naveen Krishna Chatradhi923488a2013-12-20 17:49:10 +0530611 .compatible = "samsung,exynos5260-tmu",
612 .data = (void *)EXYNOS5260_TMU_DRV_DATA,
613 },
614 {
Naveen Krishna Chatradhi14a11dc2013-12-19 11:36:31 +0530615 .compatible = "samsung,exynos5420-tmu",
616 .data = (void *)EXYNOS5420_TMU_DRV_DATA,
617 },
618 {
619 .compatible = "samsung,exynos5420-tmu-ext-triminfo",
620 .data = (void *)EXYNOS5420_TMU_DRV_DATA,
621 },
622 {
Amit Daniel Kachhap90542542013-06-24 16:20:44 +0530623 .compatible = "samsung,exynos5440-tmu",
624 .data = (void *)EXYNOS5440_TMU_DRV_DATA,
625 },
Amit Daniel Kachhap17be8682012-08-16 17:11:44 +0530626 {},
627};
628MODULE_DEVICE_TABLE(of, exynos_tmu_match);
Amit Daniel Kachhap17be8682012-08-16 17:11:44 +0530629
Amit Daniel Kachhap17be8682012-08-16 17:11:44 +0530630static inline struct exynos_tmu_platform_data *exynos_get_driver_data(
Amit Daniel Kachhapcebe7372013-06-24 16:20:39 +0530631 struct platform_device *pdev, int id)
Amit Daniel Kachhap17be8682012-08-16 17:11:44 +0530632{
Amit Daniel Kachhapcebe7372013-06-24 16:20:39 +0530633 struct exynos_tmu_init_data *data_table;
634 struct exynos_tmu_platform_data *tmu_data;
Sachin Kamat73b5b1d2013-08-19 11:58:43 +0530635 const struct of_device_id *match;
636
637 match = of_match_node(exynos_tmu_match, pdev->dev.of_node);
638 if (!match)
639 return NULL;
640 data_table = (struct exynos_tmu_init_data *) match->data;
641 if (!data_table || id >= data_table->tmu_count)
642 return NULL;
643 tmu_data = data_table->tmu_data;
644 return (struct exynos_tmu_platform_data *) (tmu_data + id);
Amit Daniel Kachhap7e0b55e2012-08-16 17:11:43 +0530645}
Jonghwa Leebbf63be2012-11-21 13:31:01 +0900646
Amit Daniel Kachhapcebe7372013-06-24 16:20:39 +0530647static int exynos_map_dt_data(struct platform_device *pdev)
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900648{
Amit Daniel Kachhapcebe7372013-06-24 16:20:39 +0530649 struct exynos_tmu_data *data = platform_get_drvdata(pdev);
650 struct exynos_tmu_platform_data *pdata;
651 struct resource res;
Amit Daniel Kachhap498d22f2013-06-24 16:20:47 +0530652 int ret;
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900653
Sachin Kamat73b5b1d2013-08-19 11:58:43 +0530654 if (!data || !pdev->dev.of_node)
Amit Daniel Kachhapcebe7372013-06-24 16:20:39 +0530655 return -ENODEV;
Amit Daniel Kachhap17be8682012-08-16 17:11:44 +0530656
Amit Daniel Kachhap498d22f2013-06-24 16:20:47 +0530657 /*
658 * Try enabling the regulator if found
659 * TODO: Add regulator as an SOC feature, so that regulator enable
660 * is a compulsory call.
661 */
662 data->regulator = devm_regulator_get(&pdev->dev, "vtmu");
663 if (!IS_ERR(data->regulator)) {
664 ret = regulator_enable(data->regulator);
665 if (ret) {
666 dev_err(&pdev->dev, "failed to enable vtmu\n");
667 return ret;
668 }
669 } else {
670 dev_info(&pdev->dev, "Regulator node (vtmu) not found\n");
671 }
672
Amit Daniel Kachhapcebe7372013-06-24 16:20:39 +0530673 data->id = of_alias_get_id(pdev->dev.of_node, "tmuctrl");
674 if (data->id < 0)
675 data->id = 0;
676
677 data->irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
678 if (data->irq <= 0) {
679 dev_err(&pdev->dev, "failed to get IRQ\n");
680 return -ENODEV;
681 }
682
683 if (of_address_to_resource(pdev->dev.of_node, 0, &res)) {
684 dev_err(&pdev->dev, "failed to get Resource 0\n");
685 return -ENODEV;
686 }
687
688 data->base = devm_ioremap(&pdev->dev, res.start, resource_size(&res));
689 if (!data->base) {
690 dev_err(&pdev->dev, "Failed to ioremap memory\n");
691 return -EADDRNOTAVAIL;
692 }
693
694 pdata = exynos_get_driver_data(pdev, data->id);
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900695 if (!pdata) {
696 dev_err(&pdev->dev, "No platform init data supplied.\n");
697 return -ENODEV;
698 }
Amit Daniel Kachhapcebe7372013-06-24 16:20:39 +0530699 data->pdata = pdata;
Amit Daniel Kachhapd9b6ee12013-06-24 16:20:42 +0530700 /*
701 * Check if the TMU shares some registers and then try to map the
702 * memory of common registers.
703 */
Naveen Krishna Chatradhi9025d562013-12-19 11:36:08 +0530704 if (!TMU_SUPPORTS(pdata, ADDRESS_MULTIPLE))
Amit Daniel Kachhapd9b6ee12013-06-24 16:20:42 +0530705 return 0;
706
707 if (of_address_to_resource(pdev->dev.of_node, 1, &res)) {
708 dev_err(&pdev->dev, "failed to get Resource 1\n");
709 return -ENODEV;
710 }
711
Naveen Krishna Chatradhi9025d562013-12-19 11:36:08 +0530712 data->base_second = devm_ioremap(&pdev->dev, res.start,
Amit Daniel Kachhapd9b6ee12013-06-24 16:20:42 +0530713 resource_size(&res));
Naveen Krishna Chatradhi9025d562013-12-19 11:36:08 +0530714 if (!data->base_second) {
Amit Daniel Kachhapd9b6ee12013-06-24 16:20:42 +0530715 dev_err(&pdev->dev, "Failed to ioremap memory\n");
716 return -ENOMEM;
717 }
Amit Daniel Kachhapcebe7372013-06-24 16:20:39 +0530718
719 return 0;
720}
721
722static int exynos_tmu_probe(struct platform_device *pdev)
723{
724 struct exynos_tmu_data *data;
725 struct exynos_tmu_platform_data *pdata;
726 struct thermal_sensor_conf *sensor_conf;
727 int ret, i;
728
Amit Daniel Kachhap79e093c2012-08-16 05:41:45 -0600729 data = devm_kzalloc(&pdev->dev, sizeof(struct exynos_tmu_data),
730 GFP_KERNEL);
Jingoo Han2a9675b2014-05-07 15:04:48 +0900731 if (!data)
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900732 return -ENOMEM;
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900733
Amit Daniel Kachhapcebe7372013-06-24 16:20:39 +0530734 platform_set_drvdata(pdev, data);
735 mutex_init(&data->lock);
736
737 ret = exynos_map_dt_data(pdev);
738 if (ret)
739 return ret;
740
741 pdata = data->pdata;
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900742
Amit Daniel Kachhapf22d9c02012-08-16 17:11:42 +0530743 INIT_WORK(&data->irq_work, exynos_tmu_work);
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900744
Sachin Kamat2a162792013-04-18 11:37:58 +0000745 data->clk = devm_clk_get(&pdev->dev, "tmu_apbif");
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900746 if (IS_ERR(data->clk)) {
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900747 dev_err(&pdev->dev, "Failed to get clock\n");
Amit Daniel Kachhap79e093c2012-08-16 05:41:45 -0600748 return PTR_ERR(data->clk);
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900749 }
750
Naveen Krishna Chatradhi14a11dc2013-12-19 11:36:31 +0530751 data->clk_sec = devm_clk_get(&pdev->dev, "tmu_triminfo_apbif");
752 if (IS_ERR(data->clk_sec)) {
753 if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) {
754 dev_err(&pdev->dev, "Failed to get triminfo clock\n");
755 return PTR_ERR(data->clk_sec);
756 }
757 } else {
758 ret = clk_prepare(data->clk_sec);
759 if (ret) {
760 dev_err(&pdev->dev, "Failed to get clock\n");
761 return ret;
762 }
763 }
764
Sachin Kamat2a162792013-04-18 11:37:58 +0000765 ret = clk_prepare(data->clk);
Naveen Krishna Chatradhi14a11dc2013-12-19 11:36:31 +0530766 if (ret) {
767 dev_err(&pdev->dev, "Failed to get clock\n");
768 goto err_clk_sec;
769 }
Sachin Kamat2a162792013-04-18 11:37:58 +0000770
Bartlomiej Zolnierkiewicz72d11002014-11-13 16:01:13 +0100771 data->soc = pdata->type;
772
773 switch (data->soc) {
774 case SOC_ARCH_EXYNOS4210:
775 data->tmu_initialize = exynos4210_tmu_initialize;
Bartlomiej Zolnierkiewicz37f90342014-11-13 16:01:15 +0100776 data->tmu_control = exynos4210_tmu_control;
Bartlomiej Zolnierkiewiczb79985c2014-11-13 16:01:16 +0100777 data->tmu_read = exynos4210_tmu_read;
Bartlomiej Zolnierkiewicza7331f72014-11-13 16:01:19 +0100778 data->tmu_clear_irqs = exynos4210_tmu_clear_irqs;
Bartlomiej Zolnierkiewicz72d11002014-11-13 16:01:13 +0100779 break;
780 case SOC_ARCH_EXYNOS3250:
781 case SOC_ARCH_EXYNOS4412:
782 case SOC_ARCH_EXYNOS5250:
783 case SOC_ARCH_EXYNOS5260:
784 case SOC_ARCH_EXYNOS5420:
785 case SOC_ARCH_EXYNOS5420_TRIMINFO:
786 data->tmu_initialize = exynos4412_tmu_initialize;
Bartlomiej Zolnierkiewicz37f90342014-11-13 16:01:15 +0100787 data->tmu_control = exynos4210_tmu_control;
Bartlomiej Zolnierkiewiczb79985c2014-11-13 16:01:16 +0100788 data->tmu_read = exynos4412_tmu_read;
Bartlomiej Zolnierkiewicz285d9942014-11-13 16:01:18 +0100789 data->tmu_set_emulation = exynos4412_tmu_set_emulation;
Bartlomiej Zolnierkiewicza7331f72014-11-13 16:01:19 +0100790 data->tmu_clear_irqs = exynos4210_tmu_clear_irqs;
Bartlomiej Zolnierkiewicz72d11002014-11-13 16:01:13 +0100791 break;
792 case SOC_ARCH_EXYNOS5440:
793 data->tmu_initialize = exynos5440_tmu_initialize;
Bartlomiej Zolnierkiewicz37f90342014-11-13 16:01:15 +0100794 data->tmu_control = exynos5440_tmu_control;
Bartlomiej Zolnierkiewiczb79985c2014-11-13 16:01:16 +0100795 data->tmu_read = exynos5440_tmu_read;
Bartlomiej Zolnierkiewicz285d9942014-11-13 16:01:18 +0100796 data->tmu_set_emulation = exynos5440_tmu_set_emulation;
Bartlomiej Zolnierkiewicza7331f72014-11-13 16:01:19 +0100797 data->tmu_clear_irqs = exynos5440_tmu_clear_irqs;
Bartlomiej Zolnierkiewicz72d11002014-11-13 16:01:13 +0100798 break;
799 default:
Amit Daniel Kachhapf22d9c02012-08-16 17:11:42 +0530800 ret = -EINVAL;
801 dev_err(&pdev->dev, "Platform not supported\n");
802 goto err_clk;
803 }
804
Amit Daniel Kachhapf22d9c02012-08-16 17:11:42 +0530805 ret = exynos_tmu_initialize(pdev);
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900806 if (ret) {
807 dev_err(&pdev->dev, "Failed to initialize TMU\n");
808 goto err_clk;
809 }
810
Amit Daniel Kachhapf22d9c02012-08-16 17:11:42 +0530811 exynos_tmu_control(pdev, true);
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900812
Amit Daniel Kachhapcebe7372013-06-24 16:20:39 +0530813 /* Allocate a structure to register with the exynos core thermal */
814 sensor_conf = devm_kzalloc(&pdev->dev,
815 sizeof(struct thermal_sensor_conf), GFP_KERNEL);
816 if (!sensor_conf) {
Amit Daniel Kachhapcebe7372013-06-24 16:20:39 +0530817 ret = -ENOMEM;
818 goto err_clk;
819 }
820 sprintf(sensor_conf->name, "therm_zone%d", data->id);
821 sensor_conf->read_temperature = (int (*)(void *))exynos_tmu_read;
822 sensor_conf->write_emul_temp =
823 (int (*)(void *, unsigned long))exynos_tmu_set_emulation;
824 sensor_conf->driver_data = data;
825 sensor_conf->trip_data.trip_count = pdata->trigger_enable[0] +
Amit Daniel Kachhapbb34b4c2013-06-24 16:20:30 +0530826 pdata->trigger_enable[1] + pdata->trigger_enable[2]+
827 pdata->trigger_enable[3];
Amit Daniel Kachhap7e0b55e2012-08-16 17:11:43 +0530828
Amit Daniel Kachhapcebe7372013-06-24 16:20:39 +0530829 for (i = 0; i < sensor_conf->trip_data.trip_count; i++) {
830 sensor_conf->trip_data.trip_val[i] =
Amit Daniel Kachhap7e0b55e2012-08-16 17:11:43 +0530831 pdata->threshold + pdata->trigger_levels[i];
Amit Daniel Kachhapcebe7372013-06-24 16:20:39 +0530832 sensor_conf->trip_data.trip_type[i] =
Amit Daniel Kachhap5c3cf552013-06-24 16:20:37 +0530833 pdata->trigger_type[i];
834 }
Amit Daniel Kachhap7e0b55e2012-08-16 17:11:43 +0530835
Amit Daniel Kachhapcebe7372013-06-24 16:20:39 +0530836 sensor_conf->trip_data.trigger_falling = pdata->threshold_falling;
Jonghwa Lee4f0a6842013-02-08 01:13:06 +0000837
Amit Daniel Kachhapcebe7372013-06-24 16:20:39 +0530838 sensor_conf->cooling_data.freq_clip_count = pdata->freq_tab_count;
Amit Daniel Kachhap7e0b55e2012-08-16 17:11:43 +0530839 for (i = 0; i < pdata->freq_tab_count; i++) {
Amit Daniel Kachhapcebe7372013-06-24 16:20:39 +0530840 sensor_conf->cooling_data.freq_data[i].freq_clip_max =
Amit Daniel Kachhap7e0b55e2012-08-16 17:11:43 +0530841 pdata->freq_tab[i].freq_clip_max;
Amit Daniel Kachhapcebe7372013-06-24 16:20:39 +0530842 sensor_conf->cooling_data.freq_data[i].temp_level =
Amit Daniel Kachhap7e0b55e2012-08-16 17:11:43 +0530843 pdata->freq_tab[i].temp_level;
844 }
Amit Daniel Kachhapcebe7372013-06-24 16:20:39 +0530845 sensor_conf->dev = &pdev->dev;
846 /* Register the sensor with thermal management interface */
847 ret = exynos_register_thermal(sensor_conf);
Amit Daniel Kachhap7e0b55e2012-08-16 17:11:43 +0530848 if (ret) {
849 dev_err(&pdev->dev, "Failed to register thermal interface\n");
850 goto err_clk;
851 }
Amit Daniel Kachhapcebe7372013-06-24 16:20:39 +0530852 data->reg_conf = sensor_conf;
853
854 ret = devm_request_irq(&pdev->dev, data->irq, exynos_tmu_irq,
855 IRQF_TRIGGER_RISING | IRQF_SHARED, dev_name(&pdev->dev), data);
856 if (ret) {
857 dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq);
858 goto err_clk;
859 }
Jonghwa Leebbf63be2012-11-21 13:31:01 +0900860
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900861 return 0;
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900862err_clk:
Sachin Kamat2a162792013-04-18 11:37:58 +0000863 clk_unprepare(data->clk);
Naveen Krishna Chatradhi14a11dc2013-12-19 11:36:31 +0530864err_clk_sec:
865 if (!IS_ERR(data->clk_sec))
866 clk_unprepare(data->clk_sec);
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900867 return ret;
868}
869
Greg Kroah-Hartman4eab7a92012-12-21 13:15:52 -0800870static int exynos_tmu_remove(struct platform_device *pdev)
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900871{
Amit Daniel Kachhapf22d9c02012-08-16 17:11:42 +0530872 struct exynos_tmu_data *data = platform_get_drvdata(pdev);
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900873
Amit Daniel Kachhapcebe7372013-06-24 16:20:39 +0530874 exynos_unregister_thermal(data->reg_conf);
Amit Daniel Kachhap7e0b55e2012-08-16 17:11:43 +0530875
Bartlomiej Zolnierkiewicz42156882014-07-08 15:09:56 +0200876 exynos_tmu_control(pdev, false);
877
Sachin Kamat2a162792013-04-18 11:37:58 +0000878 clk_unprepare(data->clk);
Naveen Krishna Chatradhi14a11dc2013-12-19 11:36:31 +0530879 if (!IS_ERR(data->clk_sec))
880 clk_unprepare(data->clk_sec);
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900881
Amit Daniel Kachhap498d22f2013-06-24 16:20:47 +0530882 if (!IS_ERR(data->regulator))
883 regulator_disable(data->regulator);
884
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900885 return 0;
886}
887
Rafael J. Wysocki08cd6752012-07-08 21:48:15 +0200888#ifdef CONFIG_PM_SLEEP
Amit Daniel Kachhapf22d9c02012-08-16 17:11:42 +0530889static int exynos_tmu_suspend(struct device *dev)
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900890{
Amit Daniel Kachhapf22d9c02012-08-16 17:11:42 +0530891 exynos_tmu_control(to_platform_device(dev), false);
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900892
893 return 0;
894}
895
Amit Daniel Kachhapf22d9c02012-08-16 17:11:42 +0530896static int exynos_tmu_resume(struct device *dev)
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900897{
Rafael J. Wysocki08cd6752012-07-08 21:48:15 +0200898 struct platform_device *pdev = to_platform_device(dev);
899
Amit Daniel Kachhapf22d9c02012-08-16 17:11:42 +0530900 exynos_tmu_initialize(pdev);
901 exynos_tmu_control(pdev, true);
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900902
903 return 0;
904}
Rafael J. Wysocki08cd6752012-07-08 21:48:15 +0200905
Amit Daniel Kachhapf22d9c02012-08-16 17:11:42 +0530906static SIMPLE_DEV_PM_OPS(exynos_tmu_pm,
907 exynos_tmu_suspend, exynos_tmu_resume);
908#define EXYNOS_TMU_PM (&exynos_tmu_pm)
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900909#else
Amit Daniel Kachhapf22d9c02012-08-16 17:11:42 +0530910#define EXYNOS_TMU_PM NULL
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900911#endif
912
Amit Daniel Kachhapf22d9c02012-08-16 17:11:42 +0530913static struct platform_driver exynos_tmu_driver = {
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900914 .driver = {
Amit Daniel Kachhapf22d9c02012-08-16 17:11:42 +0530915 .name = "exynos-tmu",
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900916 .owner = THIS_MODULE,
Amit Daniel Kachhapf22d9c02012-08-16 17:11:42 +0530917 .pm = EXYNOS_TMU_PM,
Sachin Kamat73b5b1d2013-08-19 11:58:43 +0530918 .of_match_table = exynos_tmu_match,
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900919 },
Amit Daniel Kachhapf22d9c02012-08-16 17:11:42 +0530920 .probe = exynos_tmu_probe,
Greg Kroah-Hartman4eab7a92012-12-21 13:15:52 -0800921 .remove = exynos_tmu_remove,
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900922};
923
Amit Daniel Kachhapf22d9c02012-08-16 17:11:42 +0530924module_platform_driver(exynos_tmu_driver);
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900925
Amit Daniel Kachhapf22d9c02012-08-16 17:11:42 +0530926MODULE_DESCRIPTION("EXYNOS TMU Driver");
Donggeun Kim9d97e5c2011-09-07 18:49:08 +0900927MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
928MODULE_LICENSE("GPL");
Amit Daniel Kachhapf22d9c02012-08-16 17:11:42 +0530929MODULE_ALIAS("platform:exynos-tmu");