xref: /linux/drivers/thermal/samsung/exynos_tmu.c (revision 82bdde8ecdd7e593c08f456dbb7e2e08b9b01bb5)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
259dfa54cSAmit Daniel Kachhap /*
3ca07ee4eSKrzysztof Kozlowski  * exynos_tmu.c - Samsung Exynos TMU (Thermal Management Unit)
459dfa54cSAmit Daniel Kachhap  *
53b6a1a80SLukasz Majewski  *  Copyright (C) 2014 Samsung Electronics
63b6a1a80SLukasz Majewski  *  Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
73b6a1a80SLukasz Majewski  *  Lukasz Majewski <l.majewski@samsung.com>
83b6a1a80SLukasz Majewski  *
959dfa54cSAmit Daniel Kachhap  *  Copyright (C) 2011 Samsung Electronics
1059dfa54cSAmit Daniel Kachhap  *  Donggeun Kim <dg77.kim@samsung.com>
1159dfa54cSAmit Daniel Kachhap  *  Amit Daniel Kachhap <amit.kachhap@linaro.org>
1259dfa54cSAmit Daniel Kachhap  */
1359dfa54cSAmit Daniel Kachhap 
1459dfa54cSAmit Daniel Kachhap #include <linux/clk.h>
1559dfa54cSAmit Daniel Kachhap #include <linux/io.h>
1659dfa54cSAmit Daniel Kachhap #include <linux/interrupt.h>
1759dfa54cSAmit Daniel Kachhap #include <linux/module.h>
18fee88e2bSMaciej Purski #include <linux/of_device.h>
19cebe7373SAmit Daniel Kachhap #include <linux/of_address.h>
20cebe7373SAmit Daniel Kachhap #include <linux/of_irq.h>
2159dfa54cSAmit Daniel Kachhap #include <linux/platform_device.h>
22498d22f6SAmit Daniel Kachhap #include <linux/regulator/consumer.h>
2359dfa54cSAmit Daniel Kachhap 
247efd18a2SBartlomiej Zolnierkiewicz #include <dt-bindings/thermal/thermal_exynos.h>
257efd18a2SBartlomiej Zolnierkiewicz 
263b6a1a80SLukasz Majewski #include "../thermal_core.h"
272845f6ecSBartlomiej Zolnierkiewicz 
282845f6ecSBartlomiej Zolnierkiewicz /* Exynos generic registers */
292845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_REG_TRIMINFO		0x0
302845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_REG_CONTROL		0x20
312845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_REG_STATUS		0x28
322845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_REG_CURRENT_TEMP	0x40
332845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_REG_INTEN		0x70
342845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_REG_INTSTAT		0x74
352845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_REG_INTCLEAR		0x78
362845f6ecSBartlomiej Zolnierkiewicz 
372845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_TEMP_MASK		0xff
382845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_REF_VOLTAGE_SHIFT	24
392845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_REF_VOLTAGE_MASK	0x1f
402845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_BUF_SLOPE_SEL_MASK	0xf
412845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT	8
422845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_CORE_EN_SHIFT	0
432845f6ecSBartlomiej Zolnierkiewicz 
442845f6ecSBartlomiej Zolnierkiewicz /* Exynos3250 specific registers */
452845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_TRIMINFO_CON1	0x10
462845f6ecSBartlomiej Zolnierkiewicz 
472845f6ecSBartlomiej Zolnierkiewicz /* Exynos4210 specific registers */
482845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS4210_TMU_REG_THRESHOLD_TEMP	0x44
492845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS4210_TMU_REG_TRIG_LEVEL0	0x50
502845f6ecSBartlomiej Zolnierkiewicz 
512845f6ecSBartlomiej Zolnierkiewicz /* Exynos5250, Exynos4412, Exynos3250 specific registers */
522845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_TRIMINFO_CON2	0x14
532845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_THD_TEMP_RISE		0x50
542845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_THD_TEMP_FALL		0x54
552845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_EMUL_CON		0x80
562845f6ecSBartlomiej Zolnierkiewicz 
572845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TRIMINFO_RELOAD_ENABLE	1
582845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TRIMINFO_25_SHIFT	0
592845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TRIMINFO_85_SHIFT	8
602845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_TRIP_MODE_SHIFT	13
612845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_TRIP_MODE_MASK	0x7
622845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_THERM_TRIP_EN_SHIFT	12
632845f6ecSBartlomiej Zolnierkiewicz 
642845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_INTEN_RISE0_SHIFT	0
652845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_INTEN_FALL0_SHIFT	16
662845f6ecSBartlomiej Zolnierkiewicz 
672845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_EMUL_TIME	0x57F0
682845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_EMUL_TIME_MASK	0xffff
692845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_EMUL_TIME_SHIFT	16
702845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_EMUL_DATA_SHIFT	8
712845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_EMUL_DATA_MASK	0xFF
722845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_EMUL_ENABLE	0x1
732845f6ecSBartlomiej Zolnierkiewicz 
742845f6ecSBartlomiej Zolnierkiewicz /* Exynos5260 specific */
752845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5260_TMU_REG_INTEN		0xC0
762845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5260_TMU_REG_INTSTAT		0xC4
772845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5260_TMU_REG_INTCLEAR		0xC8
782845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5260_EMUL_CON			0x100
792845f6ecSBartlomiej Zolnierkiewicz 
802845f6ecSBartlomiej Zolnierkiewicz /* Exynos4412 specific */
812845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS4412_MUX_ADDR_VALUE          6
822845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS4412_MUX_ADDR_SHIFT          20
832845f6ecSBartlomiej Zolnierkiewicz 
84488c7455SChanwoo Choi /* Exynos5433 specific registers */
85488c7455SChanwoo Choi #define EXYNOS5433_THD_TEMP_RISE3_0		0x050
86488c7455SChanwoo Choi #define EXYNOS5433_THD_TEMP_RISE7_4		0x054
87488c7455SChanwoo Choi #define EXYNOS5433_THD_TEMP_FALL3_0		0x060
88488c7455SChanwoo Choi #define EXYNOS5433_THD_TEMP_FALL7_4		0x064
89488c7455SChanwoo Choi #define EXYNOS5433_TMU_REG_INTEN		0x0c0
90488c7455SChanwoo Choi #define EXYNOS5433_TMU_REG_INTPEND		0x0c8
91488c7455SChanwoo Choi #define EXYNOS5433_TMU_EMUL_CON			0x110
92488c7455SChanwoo Choi #define EXYNOS5433_TMU_PD_DET_EN		0x130
93488c7455SChanwoo Choi 
94488c7455SChanwoo Choi #define EXYNOS5433_TRIMINFO_SENSOR_ID_SHIFT	16
95488c7455SChanwoo Choi #define EXYNOS5433_TRIMINFO_CALIB_SEL_SHIFT	23
96488c7455SChanwoo Choi #define EXYNOS5433_TRIMINFO_SENSOR_ID_MASK	\
97488c7455SChanwoo Choi 			(0xf << EXYNOS5433_TRIMINFO_SENSOR_ID_SHIFT)
98488c7455SChanwoo Choi #define EXYNOS5433_TRIMINFO_CALIB_SEL_MASK	BIT(23)
99488c7455SChanwoo Choi 
100488c7455SChanwoo Choi #define EXYNOS5433_TRIMINFO_ONE_POINT_TRIMMING	0
101488c7455SChanwoo Choi #define EXYNOS5433_TRIMINFO_TWO_POINT_TRIMMING	1
102488c7455SChanwoo Choi 
103488c7455SChanwoo Choi #define EXYNOS5433_PD_DET_EN			1
104488c7455SChanwoo Choi 
10561020d18SBartlomiej Zolnierkiewicz #define EXYNOS5433_G3D_BASE			0x10070000
10659dfa54cSAmit Daniel Kachhap 
1076c247393SAbhilash Kesavan /* Exynos7 specific registers */
1086c247393SAbhilash Kesavan #define EXYNOS7_THD_TEMP_RISE7_6		0x50
1096c247393SAbhilash Kesavan #define EXYNOS7_THD_TEMP_FALL7_6		0x60
1106c247393SAbhilash Kesavan #define EXYNOS7_TMU_REG_INTEN			0x110
1116c247393SAbhilash Kesavan #define EXYNOS7_TMU_REG_INTPEND			0x118
1126c247393SAbhilash Kesavan #define EXYNOS7_TMU_REG_EMUL_CON		0x160
1136c247393SAbhilash Kesavan 
1146c247393SAbhilash Kesavan #define EXYNOS7_TMU_TEMP_MASK			0x1ff
1156c247393SAbhilash Kesavan #define EXYNOS7_PD_DET_EN_SHIFT			23
1166c247393SAbhilash Kesavan #define EXYNOS7_TMU_INTEN_RISE0_SHIFT		0
1176c247393SAbhilash Kesavan #define EXYNOS7_EMUL_DATA_SHIFT			7
1186c247393SAbhilash Kesavan #define EXYNOS7_EMUL_DATA_MASK			0x1ff
1196c247393SAbhilash Kesavan 
120718b4ca1SBartlomiej Zolnierkiewicz #define EXYNOS_FIRST_POINT_TRIM			25
121718b4ca1SBartlomiej Zolnierkiewicz #define EXYNOS_SECOND_POINT_TRIM		85
122718b4ca1SBartlomiej Zolnierkiewicz 
12309d29426SBartlomiej Zolnierkiewicz #define EXYNOS_NOISE_CANCEL_MODE		4
12409d29426SBartlomiej Zolnierkiewicz 
1253b6a1a80SLukasz Majewski #define MCELSIUS	1000
1267efd18a2SBartlomiej Zolnierkiewicz 
1277efd18a2SBartlomiej Zolnierkiewicz enum soc_type {
1287efd18a2SBartlomiej Zolnierkiewicz 	SOC_ARCH_EXYNOS3250 = 1,
1297efd18a2SBartlomiej Zolnierkiewicz 	SOC_ARCH_EXYNOS4210,
1307efd18a2SBartlomiej Zolnierkiewicz 	SOC_ARCH_EXYNOS4412,
1317efd18a2SBartlomiej Zolnierkiewicz 	SOC_ARCH_EXYNOS5250,
1327efd18a2SBartlomiej Zolnierkiewicz 	SOC_ARCH_EXYNOS5260,
1337efd18a2SBartlomiej Zolnierkiewicz 	SOC_ARCH_EXYNOS5420,
1347efd18a2SBartlomiej Zolnierkiewicz 	SOC_ARCH_EXYNOS5420_TRIMINFO,
1357efd18a2SBartlomiej Zolnierkiewicz 	SOC_ARCH_EXYNOS5433,
1367efd18a2SBartlomiej Zolnierkiewicz 	SOC_ARCH_EXYNOS7,
1377efd18a2SBartlomiej Zolnierkiewicz };
1387efd18a2SBartlomiej Zolnierkiewicz 
139cebe7373SAmit Daniel Kachhap /**
140cebe7373SAmit Daniel Kachhap  * struct exynos_tmu_data : A structure to hold the private data of the TMU
1419625e9e6SAmit Kucheria  *			    driver
142cebe7373SAmit Daniel Kachhap  * @id: identifier of the one instance of the TMU controller.
143cebe7373SAmit Daniel Kachhap  * @base: base address of the single instance of the TMU controller.
1449025d563SNaveen Krishna Chatradhi  * @base_second: base address of the common registers of the TMU controller.
145cebe7373SAmit Daniel Kachhap  * @irq: irq number of the TMU controller.
146cebe7373SAmit Daniel Kachhap  * @soc: id of the SOC type.
147cebe7373SAmit Daniel Kachhap  * @irq_work: pointer to the irq work structure.
148cebe7373SAmit Daniel Kachhap  * @lock: lock to implement synchronization.
149cebe7373SAmit Daniel Kachhap  * @clk: pointer to the clock structure.
15014a11dc7SNaveen Krishna Chatradhi  * @clk_sec: pointer to the clock structure for accessing the base_second.
1516c247393SAbhilash Kesavan  * @sclk: pointer to the clock structure for accessing the tmu special clk.
152199b3e3cSBartlomiej Zolnierkiewicz  * @cal_type: calibration type for temperature
153e3ed3649SBartlomiej Zolnierkiewicz  * @efuse_value: SoC defined fuse value
154e3ed3649SBartlomiej Zolnierkiewicz  * @min_efuse_value: minimum valid trimming data
155e3ed3649SBartlomiej Zolnierkiewicz  * @max_efuse_value: maximum valid trimming data
156cebe7373SAmit Daniel Kachhap  * @temp_error1: fused value of the first point trim.
157cebe7373SAmit Daniel Kachhap  * @temp_error2: fused value of the second point trim.
158fccfe099SBartlomiej Zolnierkiewicz  * @gain: gain of amplifier in the positive-TC generator block
159fccfe099SBartlomiej Zolnierkiewicz  *	0 < gain <= 15
16061020d18SBartlomiej Zolnierkiewicz  * @reference_voltage: reference voltage of amplifier
16161020d18SBartlomiej Zolnierkiewicz  *	in the positive-TC generator block
16261020d18SBartlomiej Zolnierkiewicz  *	0 < reference_voltage <= 31
163498d22f6SAmit Daniel Kachhap  * @regulator: pointer to the TMU regulator structure.
164cebe7373SAmit Daniel Kachhap  * @reg_conf: pointer to structure to register with core thermal.
1659625e9e6SAmit Kucheria  * @tzd: pointer to thermal_zone_device structure
1663a3a5f15SKrzysztof Kozlowski  * @ntrip: number of supported trip points.
16788fc6f73SMarek Szyprowski  * @enabled: current status of TMU device
1689625e9e6SAmit Kucheria  * @tmu_set_trip_temp: SoC specific method to set trip (rising threshold)
1699625e9e6SAmit Kucheria  * @tmu_set_trip_hyst: SoC specific to set hysteresis (falling threshold)
17072d1100bSBartlomiej Zolnierkiewicz  * @tmu_initialize: SoC specific TMU initialization method
17137f9034fSBartlomiej Zolnierkiewicz  * @tmu_control: SoC specific TMU control method
172b79985caSBartlomiej Zolnierkiewicz  * @tmu_read: SoC specific TMU temperature read method
173285d994aSBartlomiej Zolnierkiewicz  * @tmu_set_emulation: SoC specific TMU emulation setting method
174a7331f72SBartlomiej Zolnierkiewicz  * @tmu_clear_irqs: SoC specific TMU interrupts clearing method
175cebe7373SAmit Daniel Kachhap  */
17659dfa54cSAmit Daniel Kachhap struct exynos_tmu_data {
177cebe7373SAmit Daniel Kachhap 	int id;
17859dfa54cSAmit Daniel Kachhap 	void __iomem *base;
1799025d563SNaveen Krishna Chatradhi 	void __iomem *base_second;
18059dfa54cSAmit Daniel Kachhap 	int irq;
18159dfa54cSAmit Daniel Kachhap 	enum soc_type soc;
18259dfa54cSAmit Daniel Kachhap 	struct work_struct irq_work;
18359dfa54cSAmit Daniel Kachhap 	struct mutex lock;
1846c247393SAbhilash Kesavan 	struct clk *clk, *clk_sec, *sclk;
185199b3e3cSBartlomiej Zolnierkiewicz 	u32 cal_type;
186e3ed3649SBartlomiej Zolnierkiewicz 	u32 efuse_value;
187e3ed3649SBartlomiej Zolnierkiewicz 	u32 min_efuse_value;
188e3ed3649SBartlomiej Zolnierkiewicz 	u32 max_efuse_value;
1896c247393SAbhilash Kesavan 	u16 temp_error1, temp_error2;
190fccfe099SBartlomiej Zolnierkiewicz 	u8 gain;
19161020d18SBartlomiej Zolnierkiewicz 	u8 reference_voltage;
192498d22f6SAmit Daniel Kachhap 	struct regulator *regulator;
1933b6a1a80SLukasz Majewski 	struct thermal_zone_device *tzd;
1943a3a5f15SKrzysztof Kozlowski 	unsigned int ntrip;
19588fc6f73SMarek Szyprowski 	bool enabled;
1963b6a1a80SLukasz Majewski 
197c8f8f768SBartlomiej Zolnierkiewicz 	void (*tmu_set_trip_temp)(struct exynos_tmu_data *data, int trip,
198c8f8f768SBartlomiej Zolnierkiewicz 				 u8 temp);
199c8f8f768SBartlomiej Zolnierkiewicz 	void (*tmu_set_trip_hyst)(struct exynos_tmu_data *data, int trip,
200c8f8f768SBartlomiej Zolnierkiewicz 				 u8 temp, u8 hyst);
201c35268f5SBartlomiej Zolnierkiewicz 	void (*tmu_initialize)(struct platform_device *pdev);
20237f9034fSBartlomiej Zolnierkiewicz 	void (*tmu_control)(struct platform_device *pdev, bool on);
203b79985caSBartlomiej Zolnierkiewicz 	int (*tmu_read)(struct exynos_tmu_data *data);
20417e8351aSSascha Hauer 	void (*tmu_set_emulation)(struct exynos_tmu_data *data, int temp);
205a7331f72SBartlomiej Zolnierkiewicz 	void (*tmu_clear_irqs)(struct exynos_tmu_data *data);
20659dfa54cSAmit Daniel Kachhap };
20759dfa54cSAmit Daniel Kachhap 
20859dfa54cSAmit Daniel Kachhap /*
20959dfa54cSAmit Daniel Kachhap  * TMU treats temperature as a mapped temperature code.
21059dfa54cSAmit Daniel Kachhap  * The temperature is converted differently depending on the calibration type.
21159dfa54cSAmit Daniel Kachhap  */
21259dfa54cSAmit Daniel Kachhap static int temp_to_code(struct exynos_tmu_data *data, u8 temp)
21359dfa54cSAmit Daniel Kachhap {
214199b3e3cSBartlomiej Zolnierkiewicz 	if (data->cal_type == TYPE_ONE_POINT_TRIMMING)
215718b4ca1SBartlomiej Zolnierkiewicz 		return temp + data->temp_error1 - EXYNOS_FIRST_POINT_TRIM;
21659dfa54cSAmit Daniel Kachhap 
217718b4ca1SBartlomiej Zolnierkiewicz 	return (temp - EXYNOS_FIRST_POINT_TRIM) *
21859dfa54cSAmit Daniel Kachhap 		(data->temp_error2 - data->temp_error1) /
219718b4ca1SBartlomiej Zolnierkiewicz 		(EXYNOS_SECOND_POINT_TRIM - EXYNOS_FIRST_POINT_TRIM) +
220bb34b4c8SAmit Daniel Kachhap 		data->temp_error1;
22159dfa54cSAmit Daniel Kachhap }
22259dfa54cSAmit Daniel Kachhap 
22359dfa54cSAmit Daniel Kachhap /*
22459dfa54cSAmit Daniel Kachhap  * Calculate a temperature value from a temperature code.
22559dfa54cSAmit Daniel Kachhap  * The unit of the temperature is degree Celsius.
22659dfa54cSAmit Daniel Kachhap  */
2276c247393SAbhilash Kesavan static int code_to_temp(struct exynos_tmu_data *data, u16 temp_code)
22859dfa54cSAmit Daniel Kachhap {
229199b3e3cSBartlomiej Zolnierkiewicz 	if (data->cal_type == TYPE_ONE_POINT_TRIMMING)
230718b4ca1SBartlomiej Zolnierkiewicz 		return temp_code - data->temp_error1 + EXYNOS_FIRST_POINT_TRIM;
23159dfa54cSAmit Daniel Kachhap 
2329c933b1bSBartlomiej Zolnierkiewicz 	return (temp_code - data->temp_error1) *
233718b4ca1SBartlomiej Zolnierkiewicz 		(EXYNOS_SECOND_POINT_TRIM - EXYNOS_FIRST_POINT_TRIM) /
234bb34b4c8SAmit Daniel Kachhap 		(data->temp_error2 - data->temp_error1) +
235718b4ca1SBartlomiej Zolnierkiewicz 		EXYNOS_FIRST_POINT_TRIM;
23659dfa54cSAmit Daniel Kachhap }
23759dfa54cSAmit Daniel Kachhap 
2388328a4b1SBartlomiej Zolnierkiewicz static void sanitize_temp_error(struct exynos_tmu_data *data, u32 trim_info)
239b835ced1SBartlomiej Zolnierkiewicz {
240aef27b65SBartlomiej Zolnierkiewicz 	u16 tmu_temp_mask =
241aef27b65SBartlomiej Zolnierkiewicz 		(data->soc == SOC_ARCH_EXYNOS7) ? EXYNOS7_TMU_TEMP_MASK
242aef27b65SBartlomiej Zolnierkiewicz 						: EXYNOS_TMU_TEMP_MASK;
24359dfa54cSAmit Daniel Kachhap 
244aef27b65SBartlomiej Zolnierkiewicz 	data->temp_error1 = trim_info & tmu_temp_mask;
24599d67fb9SBartlomiej Zolnierkiewicz 	data->temp_error2 = ((trim_info >> EXYNOS_TRIMINFO_85_SHIFT) &
246b8d582b9SAmit Daniel Kachhap 				EXYNOS_TMU_TEMP_MASK);
24759dfa54cSAmit Daniel Kachhap 
2485000806cSAmit Daniel Kachhap 	if (!data->temp_error1 ||
249e3ed3649SBartlomiej Zolnierkiewicz 	    (data->min_efuse_value > data->temp_error1) ||
250e3ed3649SBartlomiej Zolnierkiewicz 	    (data->temp_error1 > data->max_efuse_value))
251e3ed3649SBartlomiej Zolnierkiewicz 		data->temp_error1 = data->efuse_value & EXYNOS_TMU_TEMP_MASK;
2525000806cSAmit Daniel Kachhap 
2535000806cSAmit Daniel Kachhap 	if (!data->temp_error2)
2545000806cSAmit Daniel Kachhap 		data->temp_error2 =
255e3ed3649SBartlomiej Zolnierkiewicz 			(data->efuse_value >> EXYNOS_TRIMINFO_85_SHIFT) &
2565000806cSAmit Daniel Kachhap 			EXYNOS_TMU_TEMP_MASK;
2578328a4b1SBartlomiej Zolnierkiewicz }
25859dfa54cSAmit Daniel Kachhap 
25959dfa54cSAmit Daniel Kachhap static int exynos_tmu_initialize(struct platform_device *pdev)
26059dfa54cSAmit Daniel Kachhap {
26159dfa54cSAmit Daniel Kachhap 	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
26275e0f100SBartlomiej Zolnierkiewicz 	struct thermal_zone_device *tzd = data->tzd;
26375e0f100SBartlomiej Zolnierkiewicz 	const struct thermal_trip * const trips =
26475e0f100SBartlomiej Zolnierkiewicz 		of_thermal_get_trip_points(tzd);
26597b3881bSBartlomiej Zolnierkiewicz 	unsigned int status;
266c8f8f768SBartlomiej Zolnierkiewicz 	int ret = 0, temp, hyst;
2677ca04e58SAmit Daniel Kachhap 
26875e0f100SBartlomiej Zolnierkiewicz 	if (!trips) {
26975e0f100SBartlomiej Zolnierkiewicz 		dev_err(&pdev->dev,
27075e0f100SBartlomiej Zolnierkiewicz 			"Cannot get trip points from device tree!\n");
27175e0f100SBartlomiej Zolnierkiewicz 		return -ENODEV;
27275e0f100SBartlomiej Zolnierkiewicz 	}
27375e0f100SBartlomiej Zolnierkiewicz 
2748f1c404bSBartlomiej Zolnierkiewicz 	if (data->soc != SOC_ARCH_EXYNOS5433) /* FIXME */
2758f1c404bSBartlomiej Zolnierkiewicz 		ret = tzd->ops->get_crit_temp(tzd, &temp);
2768f1c404bSBartlomiej Zolnierkiewicz 	if (ret) {
2778f1c404bSBartlomiej Zolnierkiewicz 		dev_err(&pdev->dev,
2788f1c404bSBartlomiej Zolnierkiewicz 			"No CRITICAL trip point defined in device tree!\n");
2798f1c404bSBartlomiej Zolnierkiewicz 		goto out;
2808f1c404bSBartlomiej Zolnierkiewicz 	}
2818f1c404bSBartlomiej Zolnierkiewicz 
28275e0f100SBartlomiej Zolnierkiewicz 	if (of_thermal_get_ntrips(tzd) > data->ntrip) {
2833a3a5f15SKrzysztof Kozlowski 		dev_info(&pdev->dev,
2843a3a5f15SKrzysztof Kozlowski 			 "More trip points than supported by this TMU.\n");
2853a3a5f15SKrzysztof Kozlowski 		dev_info(&pdev->dev,
2863a3a5f15SKrzysztof Kozlowski 			 "%d trip points should be configured in polling mode.\n",
28775e0f100SBartlomiej Zolnierkiewicz 			 (of_thermal_get_ntrips(tzd) - data->ntrip));
2883a3a5f15SKrzysztof Kozlowski 	}
2893a3a5f15SKrzysztof Kozlowski 
29059dfa54cSAmit Daniel Kachhap 	mutex_lock(&data->lock);
29159dfa54cSAmit Daniel Kachhap 	clk_enable(data->clk);
29259dfa54cSAmit Daniel Kachhap 	if (!IS_ERR(data->clk_sec))
29359dfa54cSAmit Daniel Kachhap 		clk_enable(data->clk_sec);
29497b3881bSBartlomiej Zolnierkiewicz 
29597b3881bSBartlomiej Zolnierkiewicz 	status = readb(data->base + EXYNOS_TMU_REG_STATUS);
296fac36bacSBartlomiej Zolnierkiewicz 	if (!status) {
29797b3881bSBartlomiej Zolnierkiewicz 		ret = -EBUSY;
298fac36bacSBartlomiej Zolnierkiewicz 	} else {
299c8f8f768SBartlomiej Zolnierkiewicz 		int i, ntrips =
300c8f8f768SBartlomiej Zolnierkiewicz 			min_t(int, of_thermal_get_ntrips(tzd), data->ntrip);
301c8f8f768SBartlomiej Zolnierkiewicz 
302c35268f5SBartlomiej Zolnierkiewicz 		data->tmu_initialize(pdev);
303c8f8f768SBartlomiej Zolnierkiewicz 
304c8f8f768SBartlomiej Zolnierkiewicz 		/* Write temperature code for rising and falling threshold */
305c8f8f768SBartlomiej Zolnierkiewicz 		for (i = 0; i < ntrips; i++) {
306c8f8f768SBartlomiej Zolnierkiewicz 			/* Write temperature code for rising threshold */
30789335c20SBartlomiej Zolnierkiewicz 			ret = tzd->ops->get_trip_temp(tzd, i, &temp);
30889335c20SBartlomiej Zolnierkiewicz 			if (ret)
30989335c20SBartlomiej Zolnierkiewicz 				goto err;
310c8f8f768SBartlomiej Zolnierkiewicz 			temp /= MCELSIUS;
311c8f8f768SBartlomiej Zolnierkiewicz 			data->tmu_set_trip_temp(data, i, temp);
312c8f8f768SBartlomiej Zolnierkiewicz 
313c8f8f768SBartlomiej Zolnierkiewicz 			/* Write temperature code for falling threshold */
31489335c20SBartlomiej Zolnierkiewicz 			ret = tzd->ops->get_trip_hyst(tzd, i, &hyst);
31589335c20SBartlomiej Zolnierkiewicz 			if (ret)
31689335c20SBartlomiej Zolnierkiewicz 				goto err;
317c8f8f768SBartlomiej Zolnierkiewicz 			hyst /= MCELSIUS;
318c8f8f768SBartlomiej Zolnierkiewicz 			data->tmu_set_trip_hyst(data, i, temp, hyst);
319c8f8f768SBartlomiej Zolnierkiewicz 		}
320c8f8f768SBartlomiej Zolnierkiewicz 
321fac36bacSBartlomiej Zolnierkiewicz 		data->tmu_clear_irqs(data);
322fac36bacSBartlomiej Zolnierkiewicz 	}
32389335c20SBartlomiej Zolnierkiewicz err:
32459dfa54cSAmit Daniel Kachhap 	clk_disable(data->clk);
32559dfa54cSAmit Daniel Kachhap 	mutex_unlock(&data->lock);
32614a11dc7SNaveen Krishna Chatradhi 	if (!IS_ERR(data->clk_sec))
32714a11dc7SNaveen Krishna Chatradhi 		clk_disable(data->clk_sec);
3288f1c404bSBartlomiej Zolnierkiewicz out:
32959dfa54cSAmit Daniel Kachhap 	return ret;
33059dfa54cSAmit Daniel Kachhap }
33159dfa54cSAmit Daniel Kachhap 
332d00671c3SBartlomiej Zolnierkiewicz static u32 get_con_reg(struct exynos_tmu_data *data, u32 con)
33359dfa54cSAmit Daniel Kachhap {
3347575983cSBartlomiej Zolnierkiewicz 	if (data->soc == SOC_ARCH_EXYNOS4412 ||
3357575983cSBartlomiej Zolnierkiewicz 	    data->soc == SOC_ARCH_EXYNOS3250)
3367575983cSBartlomiej Zolnierkiewicz 		con |= (EXYNOS4412_MUX_ADDR_VALUE << EXYNOS4412_MUX_ADDR_SHIFT);
33786f5362eSLukasz Majewski 
33899d67fb9SBartlomiej Zolnierkiewicz 	con &= ~(EXYNOS_TMU_REF_VOLTAGE_MASK << EXYNOS_TMU_REF_VOLTAGE_SHIFT);
33961020d18SBartlomiej Zolnierkiewicz 	con |= data->reference_voltage << EXYNOS_TMU_REF_VOLTAGE_SHIFT;
340d0a0ce3eSAmit Daniel Kachhap 
34199d67fb9SBartlomiej Zolnierkiewicz 	con &= ~(EXYNOS_TMU_BUF_SLOPE_SEL_MASK << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT);
342fccfe099SBartlomiej Zolnierkiewicz 	con |= (data->gain << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT);
343d0a0ce3eSAmit Daniel Kachhap 
344b9504a6aSBartlomiej Zolnierkiewicz 	con &= ~(EXYNOS_TMU_TRIP_MODE_MASK << EXYNOS_TMU_TRIP_MODE_SHIFT);
34509d29426SBartlomiej Zolnierkiewicz 	con |= (EXYNOS_NOISE_CANCEL_MODE << EXYNOS_TMU_TRIP_MODE_SHIFT);
34659dfa54cSAmit Daniel Kachhap 
347d00671c3SBartlomiej Zolnierkiewicz 	return con;
348d00671c3SBartlomiej Zolnierkiewicz }
349d00671c3SBartlomiej Zolnierkiewicz 
350d00671c3SBartlomiej Zolnierkiewicz static void exynos_tmu_control(struct platform_device *pdev, bool on)
351d00671c3SBartlomiej Zolnierkiewicz {
352d00671c3SBartlomiej Zolnierkiewicz 	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
353d00671c3SBartlomiej Zolnierkiewicz 
354d00671c3SBartlomiej Zolnierkiewicz 	mutex_lock(&data->lock);
355d00671c3SBartlomiej Zolnierkiewicz 	clk_enable(data->clk);
35637f9034fSBartlomiej Zolnierkiewicz 	data->tmu_control(pdev, on);
35788fc6f73SMarek Szyprowski 	data->enabled = on;
35859dfa54cSAmit Daniel Kachhap 	clk_disable(data->clk);
35959dfa54cSAmit Daniel Kachhap 	mutex_unlock(&data->lock);
36059dfa54cSAmit Daniel Kachhap }
36159dfa54cSAmit Daniel Kachhap 
362a503a10fSBartlomiej Zolnierkiewicz static void exynos4210_tmu_set_trip_temp(struct exynos_tmu_data *data,
363a503a10fSBartlomiej Zolnierkiewicz 					 int trip, u8 temp)
36472d1100bSBartlomiej Zolnierkiewicz {
3653b6a1a80SLukasz Majewski 	const struct thermal_trip * const trips =
3663b6a1a80SLukasz Majewski 		of_thermal_get_trip_points(data->tzd);
367a503a10fSBartlomiej Zolnierkiewicz 	u8 ref, th_code;
36872d1100bSBartlomiej Zolnierkiewicz 
369a503a10fSBartlomiej Zolnierkiewicz 	ref = trips[0].temperature / MCELSIUS;
370a503a10fSBartlomiej Zolnierkiewicz 
371a503a10fSBartlomiej Zolnierkiewicz 	if (trip == 0) {
372a503a10fSBartlomiej Zolnierkiewicz 		th_code = temp_to_code(data, ref);
373a503a10fSBartlomiej Zolnierkiewicz 		writeb(th_code, data->base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP);
37472d1100bSBartlomiej Zolnierkiewicz 	}
37572d1100bSBartlomiej Zolnierkiewicz 
376a503a10fSBartlomiej Zolnierkiewicz 	temp -= ref;
377a503a10fSBartlomiej Zolnierkiewicz 	writeb(temp, data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL0 + trip * 4);
378a503a10fSBartlomiej Zolnierkiewicz }
379a503a10fSBartlomiej Zolnierkiewicz 
380c8f8f768SBartlomiej Zolnierkiewicz /* failing thresholds are not supported on Exynos4210 */
381c8f8f768SBartlomiej Zolnierkiewicz static void exynos4210_tmu_set_trip_hyst(struct exynos_tmu_data *data,
382c8f8f768SBartlomiej Zolnierkiewicz 					 int trip, u8 temp, u8 hyst)
383c8f8f768SBartlomiej Zolnierkiewicz {
384c8f8f768SBartlomiej Zolnierkiewicz }
385c8f8f768SBartlomiej Zolnierkiewicz 
386c35268f5SBartlomiej Zolnierkiewicz static void exynos4210_tmu_initialize(struct platform_device *pdev)
38759dfa54cSAmit Daniel Kachhap {
38859dfa54cSAmit Daniel Kachhap 	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
38972d1100bSBartlomiej Zolnierkiewicz 
39072d1100bSBartlomiej Zolnierkiewicz 	sanitize_temp_error(data, readl(data->base + EXYNOS_TMU_REG_TRIMINFO));
39172d1100bSBartlomiej Zolnierkiewicz }
39272d1100bSBartlomiej Zolnierkiewicz 
393a503a10fSBartlomiej Zolnierkiewicz static void exynos4412_tmu_set_trip_temp(struct exynos_tmu_data *data,
394a503a10fSBartlomiej Zolnierkiewicz 					 int trip, u8 temp)
395a503a10fSBartlomiej Zolnierkiewicz {
396a503a10fSBartlomiej Zolnierkiewicz 	u32 th, con;
397a503a10fSBartlomiej Zolnierkiewicz 
398a503a10fSBartlomiej Zolnierkiewicz 	th = readl(data->base + EXYNOS_THD_TEMP_RISE);
399a503a10fSBartlomiej Zolnierkiewicz 	th &= ~(0xff << 8 * trip);
400a503a10fSBartlomiej Zolnierkiewicz 	th |= temp_to_code(data, temp) << 8 * trip;
401a503a10fSBartlomiej Zolnierkiewicz 	writel(th, data->base + EXYNOS_THD_TEMP_RISE);
402a503a10fSBartlomiej Zolnierkiewicz 
403a503a10fSBartlomiej Zolnierkiewicz 	if (trip == 3) {
404a503a10fSBartlomiej Zolnierkiewicz 		con = readl(data->base + EXYNOS_TMU_REG_CONTROL);
405a503a10fSBartlomiej Zolnierkiewicz 		con |= (1 << EXYNOS_TMU_THERM_TRIP_EN_SHIFT);
406a503a10fSBartlomiej Zolnierkiewicz 		writel(con, data->base + EXYNOS_TMU_REG_CONTROL);
407a503a10fSBartlomiej Zolnierkiewicz 	}
408a503a10fSBartlomiej Zolnierkiewicz }
409a503a10fSBartlomiej Zolnierkiewicz 
410a503a10fSBartlomiej Zolnierkiewicz static void exynos4412_tmu_set_trip_hyst(struct exynos_tmu_data *data,
411a503a10fSBartlomiej Zolnierkiewicz 					 int trip, u8 temp, u8 hyst)
412a503a10fSBartlomiej Zolnierkiewicz {
413a503a10fSBartlomiej Zolnierkiewicz 	u32 th;
414a503a10fSBartlomiej Zolnierkiewicz 
415a503a10fSBartlomiej Zolnierkiewicz 	th = readl(data->base + EXYNOS_THD_TEMP_FALL);
416a503a10fSBartlomiej Zolnierkiewicz 	th &= ~(0xff << 8 * trip);
417a503a10fSBartlomiej Zolnierkiewicz 	if (hyst)
418a503a10fSBartlomiej Zolnierkiewicz 		th |= temp_to_code(data, temp - hyst) << 8 * trip;
419a503a10fSBartlomiej Zolnierkiewicz 	writel(th, data->base + EXYNOS_THD_TEMP_FALL);
420a503a10fSBartlomiej Zolnierkiewicz }
421a503a10fSBartlomiej Zolnierkiewicz 
422c35268f5SBartlomiej Zolnierkiewicz static void exynos4412_tmu_initialize(struct platform_device *pdev)
42372d1100bSBartlomiej Zolnierkiewicz {
42472d1100bSBartlomiej Zolnierkiewicz 	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
425a503a10fSBartlomiej Zolnierkiewicz 	unsigned int trim_info, ctrl;
42672d1100bSBartlomiej Zolnierkiewicz 
42772d1100bSBartlomiej Zolnierkiewicz 	if (data->soc == SOC_ARCH_EXYNOS3250 ||
42872d1100bSBartlomiej Zolnierkiewicz 	    data->soc == SOC_ARCH_EXYNOS4412 ||
42972d1100bSBartlomiej Zolnierkiewicz 	    data->soc == SOC_ARCH_EXYNOS5250) {
43072d1100bSBartlomiej Zolnierkiewicz 		if (data->soc == SOC_ARCH_EXYNOS3250) {
43172d1100bSBartlomiej Zolnierkiewicz 			ctrl = readl(data->base + EXYNOS_TMU_TRIMINFO_CON1);
43272d1100bSBartlomiej Zolnierkiewicz 			ctrl |= EXYNOS_TRIMINFO_RELOAD_ENABLE;
43372d1100bSBartlomiej Zolnierkiewicz 			writel(ctrl, data->base + EXYNOS_TMU_TRIMINFO_CON1);
43472d1100bSBartlomiej Zolnierkiewicz 		}
43572d1100bSBartlomiej Zolnierkiewicz 		ctrl = readl(data->base + EXYNOS_TMU_TRIMINFO_CON2);
43672d1100bSBartlomiej Zolnierkiewicz 		ctrl |= EXYNOS_TRIMINFO_RELOAD_ENABLE;
43772d1100bSBartlomiej Zolnierkiewicz 		writel(ctrl, data->base + EXYNOS_TMU_TRIMINFO_CON2);
43872d1100bSBartlomiej Zolnierkiewicz 	}
43972d1100bSBartlomiej Zolnierkiewicz 
44072d1100bSBartlomiej Zolnierkiewicz 	/* On exynos5420 the triminfo register is in the shared space */
44172d1100bSBartlomiej Zolnierkiewicz 	if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO)
44272d1100bSBartlomiej Zolnierkiewicz 		trim_info = readl(data->base_second + EXYNOS_TMU_REG_TRIMINFO);
44372d1100bSBartlomiej Zolnierkiewicz 	else
44472d1100bSBartlomiej Zolnierkiewicz 		trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO);
44572d1100bSBartlomiej Zolnierkiewicz 
44672d1100bSBartlomiej Zolnierkiewicz 	sanitize_temp_error(data, trim_info);
4473b6a1a80SLukasz Majewski }
4483b6a1a80SLukasz Majewski 
449a503a10fSBartlomiej Zolnierkiewicz static void exynos5433_tmu_set_trip_temp(struct exynos_tmu_data *data,
450a503a10fSBartlomiej Zolnierkiewicz 					 int trip, u8 temp)
451a503a10fSBartlomiej Zolnierkiewicz {
452a503a10fSBartlomiej Zolnierkiewicz 	unsigned int reg_off, j;
453a503a10fSBartlomiej Zolnierkiewicz 	u32 th;
454a503a10fSBartlomiej Zolnierkiewicz 
455a503a10fSBartlomiej Zolnierkiewicz 	if (trip > 3) {
456a503a10fSBartlomiej Zolnierkiewicz 		reg_off = EXYNOS5433_THD_TEMP_RISE7_4;
457a503a10fSBartlomiej Zolnierkiewicz 		j = trip - 4;
458a503a10fSBartlomiej Zolnierkiewicz 	} else {
459a503a10fSBartlomiej Zolnierkiewicz 		reg_off = EXYNOS5433_THD_TEMP_RISE3_0;
460a503a10fSBartlomiej Zolnierkiewicz 		j = trip;
4613b6a1a80SLukasz Majewski 	}
4623b6a1a80SLukasz Majewski 
463a503a10fSBartlomiej Zolnierkiewicz 	th = readl(data->base + reg_off);
464a503a10fSBartlomiej Zolnierkiewicz 	th &= ~(0xff << j * 8);
465a503a10fSBartlomiej Zolnierkiewicz 	th |= (temp_to_code(data, temp) << j * 8);
466a503a10fSBartlomiej Zolnierkiewicz 	writel(th, data->base + reg_off);
46772d1100bSBartlomiej Zolnierkiewicz }
46872d1100bSBartlomiej Zolnierkiewicz 
469a503a10fSBartlomiej Zolnierkiewicz static void exynos5433_tmu_set_trip_hyst(struct exynos_tmu_data *data,
470a503a10fSBartlomiej Zolnierkiewicz 					 int trip, u8 temp, u8 hyst)
471a503a10fSBartlomiej Zolnierkiewicz {
472a503a10fSBartlomiej Zolnierkiewicz 	unsigned int reg_off, j;
473a503a10fSBartlomiej Zolnierkiewicz 	u32 th;
474a503a10fSBartlomiej Zolnierkiewicz 
475a503a10fSBartlomiej Zolnierkiewicz 	if (trip > 3) {
476a503a10fSBartlomiej Zolnierkiewicz 		reg_off = EXYNOS5433_THD_TEMP_FALL7_4;
477a503a10fSBartlomiej Zolnierkiewicz 		j = trip - 4;
478a503a10fSBartlomiej Zolnierkiewicz 	} else {
479a503a10fSBartlomiej Zolnierkiewicz 		reg_off = EXYNOS5433_THD_TEMP_FALL3_0;
480a503a10fSBartlomiej Zolnierkiewicz 		j = trip;
481a503a10fSBartlomiej Zolnierkiewicz 	}
482a503a10fSBartlomiej Zolnierkiewicz 
483a503a10fSBartlomiej Zolnierkiewicz 	th = readl(data->base + reg_off);
484a503a10fSBartlomiej Zolnierkiewicz 	th &= ~(0xff << j * 8);
485a503a10fSBartlomiej Zolnierkiewicz 	th |= (temp_to_code(data, temp - hyst) << j * 8);
486a503a10fSBartlomiej Zolnierkiewicz 	writel(th, data->base + reg_off);
48772d1100bSBartlomiej Zolnierkiewicz }
48872d1100bSBartlomiej Zolnierkiewicz 
489c35268f5SBartlomiej Zolnierkiewicz static void exynos5433_tmu_initialize(struct platform_device *pdev)
490488c7455SChanwoo Choi {
491488c7455SChanwoo Choi 	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
49297b3881bSBartlomiej Zolnierkiewicz 	unsigned int trim_info;
493c8f8f768SBartlomiej Zolnierkiewicz 	int sensor_id, cal_type;
494488c7455SChanwoo Choi 
495488c7455SChanwoo Choi 	trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO);
496488c7455SChanwoo Choi 	sanitize_temp_error(data, trim_info);
497488c7455SChanwoo Choi 
498488c7455SChanwoo Choi 	/* Read the temperature sensor id */
499488c7455SChanwoo Choi 	sensor_id = (trim_info & EXYNOS5433_TRIMINFO_SENSOR_ID_MASK)
500488c7455SChanwoo Choi 				>> EXYNOS5433_TRIMINFO_SENSOR_ID_SHIFT;
501488c7455SChanwoo Choi 	dev_info(&pdev->dev, "Temperature sensor ID: 0x%x\n", sensor_id);
502488c7455SChanwoo Choi 
503488c7455SChanwoo Choi 	/* Read the calibration mode */
504488c7455SChanwoo Choi 	writel(trim_info, data->base + EXYNOS_TMU_REG_TRIMINFO);
505488c7455SChanwoo Choi 	cal_type = (trim_info & EXYNOS5433_TRIMINFO_CALIB_SEL_MASK)
506488c7455SChanwoo Choi 				>> EXYNOS5433_TRIMINFO_CALIB_SEL_SHIFT;
507488c7455SChanwoo Choi 
508488c7455SChanwoo Choi 	switch (cal_type) {
509488c7455SChanwoo Choi 	case EXYNOS5433_TRIMINFO_TWO_POINT_TRIMMING:
510199b3e3cSBartlomiej Zolnierkiewicz 		data->cal_type = TYPE_TWO_POINT_TRIMMING;
511488c7455SChanwoo Choi 		break;
512199b3e3cSBartlomiej Zolnierkiewicz 	case EXYNOS5433_TRIMINFO_ONE_POINT_TRIMMING:
513488c7455SChanwoo Choi 	default:
514199b3e3cSBartlomiej Zolnierkiewicz 		data->cal_type = TYPE_ONE_POINT_TRIMMING;
515488c7455SChanwoo Choi 		break;
516baba1ebbSKrzysztof Kozlowski 	}
517488c7455SChanwoo Choi 
518488c7455SChanwoo Choi 	dev_info(&pdev->dev, "Calibration type is %d-point calibration\n",
519488c7455SChanwoo Choi 			cal_type ?  2 : 1);
520488c7455SChanwoo Choi }
521488c7455SChanwoo Choi 
522a503a10fSBartlomiej Zolnierkiewicz static void exynos7_tmu_set_trip_temp(struct exynos_tmu_data *data,
523a503a10fSBartlomiej Zolnierkiewicz 				      int trip, u8 temp)
52472d1100bSBartlomiej Zolnierkiewicz {
5256c247393SAbhilash Kesavan 	unsigned int reg_off, bit_off;
526a503a10fSBartlomiej Zolnierkiewicz 	u32 th;
5276c247393SAbhilash Kesavan 
528a503a10fSBartlomiej Zolnierkiewicz 	reg_off = ((7 - trip) / 2) * 4;
529a503a10fSBartlomiej Zolnierkiewicz 	bit_off = ((8 - trip) % 2);
530a503a10fSBartlomiej Zolnierkiewicz 
531a503a10fSBartlomiej Zolnierkiewicz 	th = readl(data->base + EXYNOS7_THD_TEMP_RISE7_6 + reg_off);
532a503a10fSBartlomiej Zolnierkiewicz 	th &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off));
533a503a10fSBartlomiej Zolnierkiewicz 	th |= temp_to_code(data, temp) << (16 * bit_off);
534a503a10fSBartlomiej Zolnierkiewicz 	writel(th, data->base + EXYNOS7_THD_TEMP_RISE7_6 + reg_off);
5356c247393SAbhilash Kesavan }
5366c247393SAbhilash Kesavan 
537a503a10fSBartlomiej Zolnierkiewicz static void exynos7_tmu_set_trip_hyst(struct exynos_tmu_data *data,
538a503a10fSBartlomiej Zolnierkiewicz 				      int trip, u8 temp, u8 hyst)
539a503a10fSBartlomiej Zolnierkiewicz {
540a503a10fSBartlomiej Zolnierkiewicz 	unsigned int reg_off, bit_off;
541a503a10fSBartlomiej Zolnierkiewicz 	u32 th;
542a503a10fSBartlomiej Zolnierkiewicz 
543a503a10fSBartlomiej Zolnierkiewicz 	reg_off = ((7 - trip) / 2) * 4;
544a503a10fSBartlomiej Zolnierkiewicz 	bit_off = ((8 - trip) % 2);
545a503a10fSBartlomiej Zolnierkiewicz 
546a503a10fSBartlomiej Zolnierkiewicz 	th = readl(data->base + EXYNOS7_THD_TEMP_FALL7_6 + reg_off);
547a503a10fSBartlomiej Zolnierkiewicz 	th &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off));
548a503a10fSBartlomiej Zolnierkiewicz 	th |= temp_to_code(data, temp - hyst) << (16 * bit_off);
549a503a10fSBartlomiej Zolnierkiewicz 	writel(th, data->base + EXYNOS7_THD_TEMP_FALL7_6 + reg_off);
550a503a10fSBartlomiej Zolnierkiewicz }
551a503a10fSBartlomiej Zolnierkiewicz 
552c35268f5SBartlomiej Zolnierkiewicz static void exynos7_tmu_initialize(struct platform_device *pdev)
5536c247393SAbhilash Kesavan {
5546c247393SAbhilash Kesavan 	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
55597b3881bSBartlomiej Zolnierkiewicz 	unsigned int trim_info;
5566c247393SAbhilash Kesavan 
5576c247393SAbhilash Kesavan 	trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO);
558aef27b65SBartlomiej Zolnierkiewicz 	sanitize_temp_error(data, trim_info);
5596c247393SAbhilash Kesavan }
5606c247393SAbhilash Kesavan 
56137f9034fSBartlomiej Zolnierkiewicz static void exynos4210_tmu_control(struct platform_device *pdev, bool on)
56237f9034fSBartlomiej Zolnierkiewicz {
56337f9034fSBartlomiej Zolnierkiewicz 	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
5643b6a1a80SLukasz Majewski 	struct thermal_zone_device *tz = data->tzd;
56564e94192SBartlomiej Zolnierkiewicz 	unsigned int con, interrupt_en = 0, i;
56637f9034fSBartlomiej Zolnierkiewicz 
56737f9034fSBartlomiej Zolnierkiewicz 	con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL));
56837f9034fSBartlomiej Zolnierkiewicz 
56959dfa54cSAmit Daniel Kachhap 	if (on) {
57064e94192SBartlomiej Zolnierkiewicz 		for (i = 0; i < data->ntrip; i++) {
57164e94192SBartlomiej Zolnierkiewicz 			if (!of_thermal_is_trip_valid(tz, i))
57264e94192SBartlomiej Zolnierkiewicz 				continue;
57364e94192SBartlomiej Zolnierkiewicz 
57464e94192SBartlomiej Zolnierkiewicz 			interrupt_en |=
57564e94192SBartlomiej Zolnierkiewicz 				(1 << (EXYNOS_TMU_INTEN_RISE0_SHIFT + i * 4));
57664e94192SBartlomiej Zolnierkiewicz 		}
5773b6a1a80SLukasz Majewski 
578e0761533SBartlomiej Zolnierkiewicz 		if (data->soc != SOC_ARCH_EXYNOS4210)
57959dfa54cSAmit Daniel Kachhap 			interrupt_en |=
58037f9034fSBartlomiej Zolnierkiewicz 				interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT;
58164e94192SBartlomiej Zolnierkiewicz 
58264e94192SBartlomiej Zolnierkiewicz 		con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT);
58359dfa54cSAmit Daniel Kachhap 	} else {
58459dfa54cSAmit Daniel Kachhap 		con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT);
58559dfa54cSAmit Daniel Kachhap 	}
58664e94192SBartlomiej Zolnierkiewicz 
58737f9034fSBartlomiej Zolnierkiewicz 	writel(interrupt_en, data->base + EXYNOS_TMU_REG_INTEN);
58837f9034fSBartlomiej Zolnierkiewicz 	writel(con, data->base + EXYNOS_TMU_REG_CONTROL);
58937f9034fSBartlomiej Zolnierkiewicz }
59059dfa54cSAmit Daniel Kachhap 
591488c7455SChanwoo Choi static void exynos5433_tmu_control(struct platform_device *pdev, bool on)
592488c7455SChanwoo Choi {
593488c7455SChanwoo Choi 	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
594488c7455SChanwoo Choi 	struct thermal_zone_device *tz = data->tzd;
59564e94192SBartlomiej Zolnierkiewicz 	unsigned int con, interrupt_en = 0, pd_det_en, i;
596488c7455SChanwoo Choi 
597488c7455SChanwoo Choi 	con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL));
598488c7455SChanwoo Choi 
599488c7455SChanwoo Choi 	if (on) {
60064e94192SBartlomiej Zolnierkiewicz 		for (i = 0; i < data->ntrip; i++) {
60164e94192SBartlomiej Zolnierkiewicz 			if (!of_thermal_is_trip_valid(tz, i))
60264e94192SBartlomiej Zolnierkiewicz 				continue;
60364e94192SBartlomiej Zolnierkiewicz 
60464e94192SBartlomiej Zolnierkiewicz 			interrupt_en |=
60564e94192SBartlomiej Zolnierkiewicz 				(1 << (EXYNOS7_TMU_INTEN_RISE0_SHIFT + i));
60664e94192SBartlomiej Zolnierkiewicz 		}
607488c7455SChanwoo Choi 
608488c7455SChanwoo Choi 		interrupt_en |=
609488c7455SChanwoo Choi 			interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT;
61064e94192SBartlomiej Zolnierkiewicz 
61164e94192SBartlomiej Zolnierkiewicz 		con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT);
61264e94192SBartlomiej Zolnierkiewicz 	} else
613488c7455SChanwoo Choi 		con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT);
614488c7455SChanwoo Choi 
615488c7455SChanwoo Choi 	pd_det_en = on ? EXYNOS5433_PD_DET_EN : 0;
616488c7455SChanwoo Choi 
617488c7455SChanwoo Choi 	writel(pd_det_en, data->base + EXYNOS5433_TMU_PD_DET_EN);
618488c7455SChanwoo Choi 	writel(interrupt_en, data->base + EXYNOS5433_TMU_REG_INTEN);
619488c7455SChanwoo Choi 	writel(con, data->base + EXYNOS_TMU_REG_CONTROL);
620488c7455SChanwoo Choi }
621488c7455SChanwoo Choi 
6226c247393SAbhilash Kesavan static void exynos7_tmu_control(struct platform_device *pdev, bool on)
6236c247393SAbhilash Kesavan {
6246c247393SAbhilash Kesavan 	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
6256c247393SAbhilash Kesavan 	struct thermal_zone_device *tz = data->tzd;
62664e94192SBartlomiej Zolnierkiewicz 	unsigned int con, interrupt_en = 0, i;
6276c247393SAbhilash Kesavan 
6286c247393SAbhilash Kesavan 	con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL));
6296c247393SAbhilash Kesavan 
6306c247393SAbhilash Kesavan 	if (on) {
63164e94192SBartlomiej Zolnierkiewicz 		for (i = 0; i < data->ntrip; i++) {
63264e94192SBartlomiej Zolnierkiewicz 			if (!of_thermal_is_trip_valid(tz, i))
63364e94192SBartlomiej Zolnierkiewicz 				continue;
63464e94192SBartlomiej Zolnierkiewicz 
63564e94192SBartlomiej Zolnierkiewicz 			interrupt_en |=
63664e94192SBartlomiej Zolnierkiewicz 				(1 << (EXYNOS7_TMU_INTEN_RISE0_SHIFT + i));
63764e94192SBartlomiej Zolnierkiewicz 		}
6386c247393SAbhilash Kesavan 
6396c247393SAbhilash Kesavan 		interrupt_en |=
6406c247393SAbhilash Kesavan 			interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT;
64164e94192SBartlomiej Zolnierkiewicz 
64264e94192SBartlomiej Zolnierkiewicz 		con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT);
64364e94192SBartlomiej Zolnierkiewicz 		con |= (1 << EXYNOS7_PD_DET_EN_SHIFT);
6446c247393SAbhilash Kesavan 	} else {
6456c247393SAbhilash Kesavan 		con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT);
64642b696e8SChanwoo Choi 		con &= ~(1 << EXYNOS7_PD_DET_EN_SHIFT);
6476c247393SAbhilash Kesavan 	}
6486c247393SAbhilash Kesavan 
6496c247393SAbhilash Kesavan 	writel(interrupt_en, data->base + EXYNOS7_TMU_REG_INTEN);
6506c247393SAbhilash Kesavan 	writel(con, data->base + EXYNOS_TMU_REG_CONTROL);
6516c247393SAbhilash Kesavan }
6526c247393SAbhilash Kesavan 
65317e8351aSSascha Hauer static int exynos_get_temp(void *p, int *temp)
65459dfa54cSAmit Daniel Kachhap {
6553b6a1a80SLukasz Majewski 	struct exynos_tmu_data *data = p;
656c8da6cdeSMarek Szyprowski 	int value, ret = 0;
6573b6a1a80SLukasz Majewski 
6583b5236ccSMarek Szyprowski 	if (!data || !data->tmu_read)
6593b6a1a80SLukasz Majewski 		return -EINVAL;
660ffe6e16fSKrzysztof Kozlowski 	else if (!data->enabled)
661ffe6e16fSKrzysztof Kozlowski 		/*
662ffe6e16fSKrzysztof Kozlowski 		 * Called too early, probably
663ffe6e16fSKrzysztof Kozlowski 		 * from thermal_zone_of_sensor_register().
664ffe6e16fSKrzysztof Kozlowski 		 */
665ffe6e16fSKrzysztof Kozlowski 		return -EAGAIN;
66659dfa54cSAmit Daniel Kachhap 
66759dfa54cSAmit Daniel Kachhap 	mutex_lock(&data->lock);
66859dfa54cSAmit Daniel Kachhap 	clk_enable(data->clk);
6693b6a1a80SLukasz Majewski 
670c8da6cdeSMarek Szyprowski 	value = data->tmu_read(data);
671c8da6cdeSMarek Szyprowski 	if (value < 0)
672c8da6cdeSMarek Szyprowski 		ret = value;
673c8da6cdeSMarek Szyprowski 	else
674c8da6cdeSMarek Szyprowski 		*temp = code_to_temp(data, value) * MCELSIUS;
6753b6a1a80SLukasz Majewski 
67659dfa54cSAmit Daniel Kachhap 	clk_disable(data->clk);
67759dfa54cSAmit Daniel Kachhap 	mutex_unlock(&data->lock);
67859dfa54cSAmit Daniel Kachhap 
679c8da6cdeSMarek Szyprowski 	return ret;
68059dfa54cSAmit Daniel Kachhap }
68159dfa54cSAmit Daniel Kachhap 
68259dfa54cSAmit Daniel Kachhap #ifdef CONFIG_THERMAL_EMULATION
683154013eaSBartlomiej Zolnierkiewicz static u32 get_emul_con_reg(struct exynos_tmu_data *data, unsigned int val,
68417e8351aSSascha Hauer 			    int temp)
685154013eaSBartlomiej Zolnierkiewicz {
686154013eaSBartlomiej Zolnierkiewicz 	if (temp) {
687154013eaSBartlomiej Zolnierkiewicz 		temp /= MCELSIUS;
688154013eaSBartlomiej Zolnierkiewicz 
689154013eaSBartlomiej Zolnierkiewicz 		val &= ~(EXYNOS_EMUL_TIME_MASK << EXYNOS_EMUL_TIME_SHIFT);
690154013eaSBartlomiej Zolnierkiewicz 		val |= (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT);
6916c247393SAbhilash Kesavan 		if (data->soc == SOC_ARCH_EXYNOS7) {
6926c247393SAbhilash Kesavan 			val &= ~(EXYNOS7_EMUL_DATA_MASK <<
6936c247393SAbhilash Kesavan 				EXYNOS7_EMUL_DATA_SHIFT);
6946c247393SAbhilash Kesavan 			val |= (temp_to_code(data, temp) <<
6956c247393SAbhilash Kesavan 				EXYNOS7_EMUL_DATA_SHIFT) |
696154013eaSBartlomiej Zolnierkiewicz 				EXYNOS_EMUL_ENABLE;
697154013eaSBartlomiej Zolnierkiewicz 		} else {
6986c247393SAbhilash Kesavan 			val &= ~(EXYNOS_EMUL_DATA_MASK <<
6996c247393SAbhilash Kesavan 				EXYNOS_EMUL_DATA_SHIFT);
7006c247393SAbhilash Kesavan 			val |= (temp_to_code(data, temp) <<
7016c247393SAbhilash Kesavan 				EXYNOS_EMUL_DATA_SHIFT) |
7026c247393SAbhilash Kesavan 				EXYNOS_EMUL_ENABLE;
7036c247393SAbhilash Kesavan 		}
7046c247393SAbhilash Kesavan 	} else {
705154013eaSBartlomiej Zolnierkiewicz 		val &= ~EXYNOS_EMUL_ENABLE;
706154013eaSBartlomiej Zolnierkiewicz 	}
707154013eaSBartlomiej Zolnierkiewicz 
708154013eaSBartlomiej Zolnierkiewicz 	return val;
709154013eaSBartlomiej Zolnierkiewicz }
710154013eaSBartlomiej Zolnierkiewicz 
711285d994aSBartlomiej Zolnierkiewicz static void exynos4412_tmu_set_emulation(struct exynos_tmu_data *data,
71217e8351aSSascha Hauer 					 int temp)
713285d994aSBartlomiej Zolnierkiewicz {
714285d994aSBartlomiej Zolnierkiewicz 	unsigned int val;
715285d994aSBartlomiej Zolnierkiewicz 	u32 emul_con;
716285d994aSBartlomiej Zolnierkiewicz 
717285d994aSBartlomiej Zolnierkiewicz 	if (data->soc == SOC_ARCH_EXYNOS5260)
718285d994aSBartlomiej Zolnierkiewicz 		emul_con = EXYNOS5260_EMUL_CON;
719b28fec13SSudip Mukherjee 	else if (data->soc == SOC_ARCH_EXYNOS5433)
720488c7455SChanwoo Choi 		emul_con = EXYNOS5433_TMU_EMUL_CON;
7216c247393SAbhilash Kesavan 	else if (data->soc == SOC_ARCH_EXYNOS7)
7226c247393SAbhilash Kesavan 		emul_con = EXYNOS7_TMU_REG_EMUL_CON;
723285d994aSBartlomiej Zolnierkiewicz 	else
724285d994aSBartlomiej Zolnierkiewicz 		emul_con = EXYNOS_EMUL_CON;
725285d994aSBartlomiej Zolnierkiewicz 
726285d994aSBartlomiej Zolnierkiewicz 	val = readl(data->base + emul_con);
727285d994aSBartlomiej Zolnierkiewicz 	val = get_emul_con_reg(data, val, temp);
728285d994aSBartlomiej Zolnierkiewicz 	writel(val, data->base + emul_con);
729285d994aSBartlomiej Zolnierkiewicz }
730285d994aSBartlomiej Zolnierkiewicz 
73117e8351aSSascha Hauer static int exynos_tmu_set_emulation(void *drv_data, int temp)
73259dfa54cSAmit Daniel Kachhap {
73359dfa54cSAmit Daniel Kachhap 	struct exynos_tmu_data *data = drv_data;
73459dfa54cSAmit Daniel Kachhap 	int ret = -EINVAL;
73559dfa54cSAmit Daniel Kachhap 
736ef3f80fcSBartlomiej Zolnierkiewicz 	if (data->soc == SOC_ARCH_EXYNOS4210)
73759dfa54cSAmit Daniel Kachhap 		goto out;
73859dfa54cSAmit Daniel Kachhap 
73959dfa54cSAmit Daniel Kachhap 	if (temp && temp < MCELSIUS)
74059dfa54cSAmit Daniel Kachhap 		goto out;
74159dfa54cSAmit Daniel Kachhap 
74259dfa54cSAmit Daniel Kachhap 	mutex_lock(&data->lock);
74359dfa54cSAmit Daniel Kachhap 	clk_enable(data->clk);
744285d994aSBartlomiej Zolnierkiewicz 	data->tmu_set_emulation(data, temp);
74559dfa54cSAmit Daniel Kachhap 	clk_disable(data->clk);
74659dfa54cSAmit Daniel Kachhap 	mutex_unlock(&data->lock);
74759dfa54cSAmit Daniel Kachhap 	return 0;
74859dfa54cSAmit Daniel Kachhap out:
74959dfa54cSAmit Daniel Kachhap 	return ret;
75059dfa54cSAmit Daniel Kachhap }
75159dfa54cSAmit Daniel Kachhap #else
752285d994aSBartlomiej Zolnierkiewicz #define exynos4412_tmu_set_emulation NULL
75317e8351aSSascha Hauer static int exynos_tmu_set_emulation(void *drv_data, int temp)
75459dfa54cSAmit Daniel Kachhap 	{ return -EINVAL; }
75559dfa54cSAmit Daniel Kachhap #endif /* CONFIG_THERMAL_EMULATION */
75659dfa54cSAmit Daniel Kachhap 
757b79985caSBartlomiej Zolnierkiewicz static int exynos4210_tmu_read(struct exynos_tmu_data *data)
758b79985caSBartlomiej Zolnierkiewicz {
759b79985caSBartlomiej Zolnierkiewicz 	int ret = readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP);
760b79985caSBartlomiej Zolnierkiewicz 
761b79985caSBartlomiej Zolnierkiewicz 	/* "temp_code" should range between 75 and 175 */
762b79985caSBartlomiej Zolnierkiewicz 	return (ret < 75 || ret > 175) ? -ENODATA : ret;
763b79985caSBartlomiej Zolnierkiewicz }
764b79985caSBartlomiej Zolnierkiewicz 
765b79985caSBartlomiej Zolnierkiewicz static int exynos4412_tmu_read(struct exynos_tmu_data *data)
766b79985caSBartlomiej Zolnierkiewicz {
767b79985caSBartlomiej Zolnierkiewicz 	return readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP);
768b79985caSBartlomiej Zolnierkiewicz }
769b79985caSBartlomiej Zolnierkiewicz 
7706c247393SAbhilash Kesavan static int exynos7_tmu_read(struct exynos_tmu_data *data)
7716c247393SAbhilash Kesavan {
7726c247393SAbhilash Kesavan 	return readw(data->base + EXYNOS_TMU_REG_CURRENT_TEMP) &
7736c247393SAbhilash Kesavan 		EXYNOS7_TMU_TEMP_MASK;
7746c247393SAbhilash Kesavan }
7756c247393SAbhilash Kesavan 
77659dfa54cSAmit Daniel Kachhap static void exynos_tmu_work(struct work_struct *work)
77759dfa54cSAmit Daniel Kachhap {
77859dfa54cSAmit Daniel Kachhap 	struct exynos_tmu_data *data = container_of(work,
77959dfa54cSAmit Daniel Kachhap 			struct exynos_tmu_data, irq_work);
780a0395eeeSAmit Daniel Kachhap 
781b43e3cfeSBartlomiej Zolnierkiewicz 	thermal_zone_device_update(data->tzd, THERMAL_EVENT_UNSPECIFIED);
782b43e3cfeSBartlomiej Zolnierkiewicz 
78359dfa54cSAmit Daniel Kachhap 	mutex_lock(&data->lock);
78459dfa54cSAmit Daniel Kachhap 	clk_enable(data->clk);
785b8d582b9SAmit Daniel Kachhap 
786a4463c4fSAmit Daniel Kachhap 	/* TODO: take action based on particular interrupt */
787a7331f72SBartlomiej Zolnierkiewicz 	data->tmu_clear_irqs(data);
788b8d582b9SAmit Daniel Kachhap 
78959dfa54cSAmit Daniel Kachhap 	clk_disable(data->clk);
79059dfa54cSAmit Daniel Kachhap 	mutex_unlock(&data->lock);
79159dfa54cSAmit Daniel Kachhap 	enable_irq(data->irq);
79259dfa54cSAmit Daniel Kachhap }
79359dfa54cSAmit Daniel Kachhap 
794a7331f72SBartlomiej Zolnierkiewicz static void exynos4210_tmu_clear_irqs(struct exynos_tmu_data *data)
795a7331f72SBartlomiej Zolnierkiewicz {
796a7331f72SBartlomiej Zolnierkiewicz 	unsigned int val_irq;
797a7331f72SBartlomiej Zolnierkiewicz 	u32 tmu_intstat, tmu_intclear;
798a7331f72SBartlomiej Zolnierkiewicz 
799a7331f72SBartlomiej Zolnierkiewicz 	if (data->soc == SOC_ARCH_EXYNOS5260) {
800a7331f72SBartlomiej Zolnierkiewicz 		tmu_intstat = EXYNOS5260_TMU_REG_INTSTAT;
801a7331f72SBartlomiej Zolnierkiewicz 		tmu_intclear = EXYNOS5260_TMU_REG_INTCLEAR;
8026c247393SAbhilash Kesavan 	} else if (data->soc == SOC_ARCH_EXYNOS7) {
8036c247393SAbhilash Kesavan 		tmu_intstat = EXYNOS7_TMU_REG_INTPEND;
8046c247393SAbhilash Kesavan 		tmu_intclear = EXYNOS7_TMU_REG_INTPEND;
805488c7455SChanwoo Choi 	} else if (data->soc == SOC_ARCH_EXYNOS5433) {
806488c7455SChanwoo Choi 		tmu_intstat = EXYNOS5433_TMU_REG_INTPEND;
807488c7455SChanwoo Choi 		tmu_intclear = EXYNOS5433_TMU_REG_INTPEND;
808a7331f72SBartlomiej Zolnierkiewicz 	} else {
809a7331f72SBartlomiej Zolnierkiewicz 		tmu_intstat = EXYNOS_TMU_REG_INTSTAT;
810a7331f72SBartlomiej Zolnierkiewicz 		tmu_intclear = EXYNOS_TMU_REG_INTCLEAR;
811a7331f72SBartlomiej Zolnierkiewicz 	}
812a7331f72SBartlomiej Zolnierkiewicz 
813a7331f72SBartlomiej Zolnierkiewicz 	val_irq = readl(data->base + tmu_intstat);
814a7331f72SBartlomiej Zolnierkiewicz 	/*
815a7331f72SBartlomiej Zolnierkiewicz 	 * Clear the interrupts.  Please note that the documentation for
816a7331f72SBartlomiej Zolnierkiewicz 	 * Exynos3250, Exynos4412, Exynos5250 and Exynos5260 incorrectly
817a7331f72SBartlomiej Zolnierkiewicz 	 * states that INTCLEAR register has a different placing of bits
818a7331f72SBartlomiej Zolnierkiewicz 	 * responsible for FALL IRQs than INTSTAT register.  Exynos5420
819a7331f72SBartlomiej Zolnierkiewicz 	 * and Exynos5440 documentation is correct (Exynos4210 doesn't
820a7331f72SBartlomiej Zolnierkiewicz 	 * support FALL IRQs at all).
821a7331f72SBartlomiej Zolnierkiewicz 	 */
822a7331f72SBartlomiej Zolnierkiewicz 	writel(val_irq, data->base + tmu_intclear);
823a7331f72SBartlomiej Zolnierkiewicz }
824a7331f72SBartlomiej Zolnierkiewicz 
82559dfa54cSAmit Daniel Kachhap static irqreturn_t exynos_tmu_irq(int irq, void *id)
82659dfa54cSAmit Daniel Kachhap {
82759dfa54cSAmit Daniel Kachhap 	struct exynos_tmu_data *data = id;
82859dfa54cSAmit Daniel Kachhap 
82959dfa54cSAmit Daniel Kachhap 	disable_irq_nosync(irq);
83059dfa54cSAmit Daniel Kachhap 	schedule_work(&data->irq_work);
83159dfa54cSAmit Daniel Kachhap 
83259dfa54cSAmit Daniel Kachhap 	return IRQ_HANDLED;
83359dfa54cSAmit Daniel Kachhap }
83459dfa54cSAmit Daniel Kachhap 
83559dfa54cSAmit Daniel Kachhap static const struct of_device_id exynos_tmu_match[] = {
836fee88e2bSMaciej Purski 	{
837fee88e2bSMaciej Purski 		.compatible = "samsung,exynos3250-tmu",
838fee88e2bSMaciej Purski 		.data = (const void *)SOC_ARCH_EXYNOS3250,
839fee88e2bSMaciej Purski 	}, {
840fee88e2bSMaciej Purski 		.compatible = "samsung,exynos4210-tmu",
841fee88e2bSMaciej Purski 		.data = (const void *)SOC_ARCH_EXYNOS4210,
842fee88e2bSMaciej Purski 	}, {
843fee88e2bSMaciej Purski 		.compatible = "samsung,exynos4412-tmu",
844fee88e2bSMaciej Purski 		.data = (const void *)SOC_ARCH_EXYNOS4412,
845fee88e2bSMaciej Purski 	}, {
846fee88e2bSMaciej Purski 		.compatible = "samsung,exynos5250-tmu",
847fee88e2bSMaciej Purski 		.data = (const void *)SOC_ARCH_EXYNOS5250,
848fee88e2bSMaciej Purski 	}, {
849fee88e2bSMaciej Purski 		.compatible = "samsung,exynos5260-tmu",
850fee88e2bSMaciej Purski 		.data = (const void *)SOC_ARCH_EXYNOS5260,
851fee88e2bSMaciej Purski 	}, {
852fee88e2bSMaciej Purski 		.compatible = "samsung,exynos5420-tmu",
853fee88e2bSMaciej Purski 		.data = (const void *)SOC_ARCH_EXYNOS5420,
854fee88e2bSMaciej Purski 	}, {
855fee88e2bSMaciej Purski 		.compatible = "samsung,exynos5420-tmu-ext-triminfo",
856fee88e2bSMaciej Purski 		.data = (const void *)SOC_ARCH_EXYNOS5420_TRIMINFO,
857fee88e2bSMaciej Purski 	}, {
858fee88e2bSMaciej Purski 		.compatible = "samsung,exynos5433-tmu",
859fee88e2bSMaciej Purski 		.data = (const void *)SOC_ARCH_EXYNOS5433,
860fee88e2bSMaciej Purski 	}, {
861fee88e2bSMaciej Purski 		.compatible = "samsung,exynos7-tmu",
862fee88e2bSMaciej Purski 		.data = (const void *)SOC_ARCH_EXYNOS7,
863fee88e2bSMaciej Purski 	},
864fee88e2bSMaciej Purski 	{ },
86559dfa54cSAmit Daniel Kachhap };
86659dfa54cSAmit Daniel Kachhap MODULE_DEVICE_TABLE(of, exynos_tmu_match);
86759dfa54cSAmit Daniel Kachhap 
868cebe7373SAmit Daniel Kachhap static int exynos_map_dt_data(struct platform_device *pdev)
86959dfa54cSAmit Daniel Kachhap {
870cebe7373SAmit Daniel Kachhap 	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
871cebe7373SAmit Daniel Kachhap 	struct resource res;
87259dfa54cSAmit Daniel Kachhap 
87373b5b1d7SSachin Kamat 	if (!data || !pdev->dev.of_node)
874cebe7373SAmit Daniel Kachhap 		return -ENODEV;
87559dfa54cSAmit Daniel Kachhap 
876cebe7373SAmit Daniel Kachhap 	data->id = of_alias_get_id(pdev->dev.of_node, "tmuctrl");
877cebe7373SAmit Daniel Kachhap 	if (data->id < 0)
878cebe7373SAmit Daniel Kachhap 		data->id = 0;
879cebe7373SAmit Daniel Kachhap 
880cebe7373SAmit Daniel Kachhap 	data->irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
881cebe7373SAmit Daniel Kachhap 	if (data->irq <= 0) {
882cebe7373SAmit Daniel Kachhap 		dev_err(&pdev->dev, "failed to get IRQ\n");
883cebe7373SAmit Daniel Kachhap 		return -ENODEV;
884cebe7373SAmit Daniel Kachhap 	}
885cebe7373SAmit Daniel Kachhap 
886cebe7373SAmit Daniel Kachhap 	if (of_address_to_resource(pdev->dev.of_node, 0, &res)) {
887cebe7373SAmit Daniel Kachhap 		dev_err(&pdev->dev, "failed to get Resource 0\n");
888cebe7373SAmit Daniel Kachhap 		return -ENODEV;
889cebe7373SAmit Daniel Kachhap 	}
890cebe7373SAmit Daniel Kachhap 
891cebe7373SAmit Daniel Kachhap 	data->base = devm_ioremap(&pdev->dev, res.start, resource_size(&res));
892cebe7373SAmit Daniel Kachhap 	if (!data->base) {
893cebe7373SAmit Daniel Kachhap 		dev_err(&pdev->dev, "Failed to ioremap memory\n");
894cebe7373SAmit Daniel Kachhap 		return -EADDRNOTAVAIL;
895cebe7373SAmit Daniel Kachhap 	}
896cebe7373SAmit Daniel Kachhap 
897fee88e2bSMaciej Purski 	data->soc = (enum soc_type)of_device_get_match_data(&pdev->dev);
89856adb9efSBartlomiej Zolnierkiewicz 
89956adb9efSBartlomiej Zolnierkiewicz 	switch (data->soc) {
90056adb9efSBartlomiej Zolnierkiewicz 	case SOC_ARCH_EXYNOS4210:
901c8f8f768SBartlomiej Zolnierkiewicz 		data->tmu_set_trip_temp = exynos4210_tmu_set_trip_temp;
902c8f8f768SBartlomiej Zolnierkiewicz 		data->tmu_set_trip_hyst = exynos4210_tmu_set_trip_hyst;
90356adb9efSBartlomiej Zolnierkiewicz 		data->tmu_initialize = exynos4210_tmu_initialize;
90456adb9efSBartlomiej Zolnierkiewicz 		data->tmu_control = exynos4210_tmu_control;
90556adb9efSBartlomiej Zolnierkiewicz 		data->tmu_read = exynos4210_tmu_read;
90656adb9efSBartlomiej Zolnierkiewicz 		data->tmu_clear_irqs = exynos4210_tmu_clear_irqs;
9073a3a5f15SKrzysztof Kozlowski 		data->ntrip = 4;
908fccfe099SBartlomiej Zolnierkiewicz 		data->gain = 15;
90961020d18SBartlomiej Zolnierkiewicz 		data->reference_voltage = 7;
910e3ed3649SBartlomiej Zolnierkiewicz 		data->efuse_value = 55;
911e3ed3649SBartlomiej Zolnierkiewicz 		data->min_efuse_value = 40;
912e3ed3649SBartlomiej Zolnierkiewicz 		data->max_efuse_value = 100;
91356adb9efSBartlomiej Zolnierkiewicz 		break;
91456adb9efSBartlomiej Zolnierkiewicz 	case SOC_ARCH_EXYNOS3250:
91556adb9efSBartlomiej Zolnierkiewicz 	case SOC_ARCH_EXYNOS4412:
91656adb9efSBartlomiej Zolnierkiewicz 	case SOC_ARCH_EXYNOS5250:
91756adb9efSBartlomiej Zolnierkiewicz 	case SOC_ARCH_EXYNOS5260:
91856adb9efSBartlomiej Zolnierkiewicz 	case SOC_ARCH_EXYNOS5420:
91956adb9efSBartlomiej Zolnierkiewicz 	case SOC_ARCH_EXYNOS5420_TRIMINFO:
920c8f8f768SBartlomiej Zolnierkiewicz 		data->tmu_set_trip_temp = exynos4412_tmu_set_trip_temp;
921c8f8f768SBartlomiej Zolnierkiewicz 		data->tmu_set_trip_hyst = exynos4412_tmu_set_trip_hyst;
92256adb9efSBartlomiej Zolnierkiewicz 		data->tmu_initialize = exynos4412_tmu_initialize;
92356adb9efSBartlomiej Zolnierkiewicz 		data->tmu_control = exynos4210_tmu_control;
92456adb9efSBartlomiej Zolnierkiewicz 		data->tmu_read = exynos4412_tmu_read;
92556adb9efSBartlomiej Zolnierkiewicz 		data->tmu_set_emulation = exynos4412_tmu_set_emulation;
92656adb9efSBartlomiej Zolnierkiewicz 		data->tmu_clear_irqs = exynos4210_tmu_clear_irqs;
9273a3a5f15SKrzysztof Kozlowski 		data->ntrip = 4;
928fccfe099SBartlomiej Zolnierkiewicz 		data->gain = 8;
92961020d18SBartlomiej Zolnierkiewicz 		data->reference_voltage = 16;
930e3ed3649SBartlomiej Zolnierkiewicz 		data->efuse_value = 55;
931e3ed3649SBartlomiej Zolnierkiewicz 		if (data->soc != SOC_ARCH_EXYNOS5420 &&
932e3ed3649SBartlomiej Zolnierkiewicz 		    data->soc != SOC_ARCH_EXYNOS5420_TRIMINFO)
933e3ed3649SBartlomiej Zolnierkiewicz 			data->min_efuse_value = 40;
934e3ed3649SBartlomiej Zolnierkiewicz 		else
935e3ed3649SBartlomiej Zolnierkiewicz 			data->min_efuse_value = 0;
936e3ed3649SBartlomiej Zolnierkiewicz 		data->max_efuse_value = 100;
93756adb9efSBartlomiej Zolnierkiewicz 		break;
938488c7455SChanwoo Choi 	case SOC_ARCH_EXYNOS5433:
939c8f8f768SBartlomiej Zolnierkiewicz 		data->tmu_set_trip_temp = exynos5433_tmu_set_trip_temp;
940c8f8f768SBartlomiej Zolnierkiewicz 		data->tmu_set_trip_hyst = exynos5433_tmu_set_trip_hyst;
941488c7455SChanwoo Choi 		data->tmu_initialize = exynos5433_tmu_initialize;
942488c7455SChanwoo Choi 		data->tmu_control = exynos5433_tmu_control;
943488c7455SChanwoo Choi 		data->tmu_read = exynos4412_tmu_read;
944488c7455SChanwoo Choi 		data->tmu_set_emulation = exynos4412_tmu_set_emulation;
945488c7455SChanwoo Choi 		data->tmu_clear_irqs = exynos4210_tmu_clear_irqs;
9463a3a5f15SKrzysztof Kozlowski 		data->ntrip = 8;
947fccfe099SBartlomiej Zolnierkiewicz 		data->gain = 8;
94861020d18SBartlomiej Zolnierkiewicz 		if (res.start == EXYNOS5433_G3D_BASE)
94961020d18SBartlomiej Zolnierkiewicz 			data->reference_voltage = 23;
95061020d18SBartlomiej Zolnierkiewicz 		else
95161020d18SBartlomiej Zolnierkiewicz 			data->reference_voltage = 16;
952e3ed3649SBartlomiej Zolnierkiewicz 		data->efuse_value = 75;
953e3ed3649SBartlomiej Zolnierkiewicz 		data->min_efuse_value = 40;
954e3ed3649SBartlomiej Zolnierkiewicz 		data->max_efuse_value = 150;
95556adb9efSBartlomiej Zolnierkiewicz 		break;
9566c247393SAbhilash Kesavan 	case SOC_ARCH_EXYNOS7:
957c8f8f768SBartlomiej Zolnierkiewicz 		data->tmu_set_trip_temp = exynos7_tmu_set_trip_temp;
958c8f8f768SBartlomiej Zolnierkiewicz 		data->tmu_set_trip_hyst = exynos7_tmu_set_trip_hyst;
9596c247393SAbhilash Kesavan 		data->tmu_initialize = exynos7_tmu_initialize;
9606c247393SAbhilash Kesavan 		data->tmu_control = exynos7_tmu_control;
9616c247393SAbhilash Kesavan 		data->tmu_read = exynos7_tmu_read;
9626c247393SAbhilash Kesavan 		data->tmu_set_emulation = exynos4412_tmu_set_emulation;
9636c247393SAbhilash Kesavan 		data->tmu_clear_irqs = exynos4210_tmu_clear_irqs;
9643a3a5f15SKrzysztof Kozlowski 		data->ntrip = 8;
965fccfe099SBartlomiej Zolnierkiewicz 		data->gain = 9;
96661020d18SBartlomiej Zolnierkiewicz 		data->reference_voltage = 17;
967e3ed3649SBartlomiej Zolnierkiewicz 		data->efuse_value = 75;
968e3ed3649SBartlomiej Zolnierkiewicz 		data->min_efuse_value = 15;
969e3ed3649SBartlomiej Zolnierkiewicz 		data->max_efuse_value = 100;
9706c247393SAbhilash Kesavan 		break;
97156adb9efSBartlomiej Zolnierkiewicz 	default:
97256adb9efSBartlomiej Zolnierkiewicz 		dev_err(&pdev->dev, "Platform not supported\n");
97356adb9efSBartlomiej Zolnierkiewicz 		return -EINVAL;
97456adb9efSBartlomiej Zolnierkiewicz 	}
97556adb9efSBartlomiej Zolnierkiewicz 
976199b3e3cSBartlomiej Zolnierkiewicz 	data->cal_type = TYPE_ONE_POINT_TRIMMING;
977199b3e3cSBartlomiej Zolnierkiewicz 
978d9b6ee14SAmit Daniel Kachhap 	/*
979d9b6ee14SAmit Daniel Kachhap 	 * Check if the TMU shares some registers and then try to map the
980d9b6ee14SAmit Daniel Kachhap 	 * memory of common registers.
981d9b6ee14SAmit Daniel Kachhap 	 */
9828014220dSKrzysztof Kozlowski 	if (data->soc != SOC_ARCH_EXYNOS5420_TRIMINFO)
983d9b6ee14SAmit Daniel Kachhap 		return 0;
984d9b6ee14SAmit Daniel Kachhap 
985d9b6ee14SAmit Daniel Kachhap 	if (of_address_to_resource(pdev->dev.of_node, 1, &res)) {
986d9b6ee14SAmit Daniel Kachhap 		dev_err(&pdev->dev, "failed to get Resource 1\n");
987d9b6ee14SAmit Daniel Kachhap 		return -ENODEV;
988d9b6ee14SAmit Daniel Kachhap 	}
989d9b6ee14SAmit Daniel Kachhap 
9909025d563SNaveen Krishna Chatradhi 	data->base_second = devm_ioremap(&pdev->dev, res.start,
991d9b6ee14SAmit Daniel Kachhap 					resource_size(&res));
9929025d563SNaveen Krishna Chatradhi 	if (!data->base_second) {
993d9b6ee14SAmit Daniel Kachhap 		dev_err(&pdev->dev, "Failed to ioremap memory\n");
994d9b6ee14SAmit Daniel Kachhap 		return -ENOMEM;
995d9b6ee14SAmit Daniel Kachhap 	}
996cebe7373SAmit Daniel Kachhap 
997cebe7373SAmit Daniel Kachhap 	return 0;
998cebe7373SAmit Daniel Kachhap }
999cebe7373SAmit Daniel Kachhap 
1000c3c04d9dSJulia Lawall static const struct thermal_zone_of_device_ops exynos_sensor_ops = {
10013b6a1a80SLukasz Majewski 	.get_temp = exynos_get_temp,
10023b6a1a80SLukasz Majewski 	.set_emul_temp = exynos_tmu_set_emulation,
10033b6a1a80SLukasz Majewski };
10043b6a1a80SLukasz Majewski 
1005cebe7373SAmit Daniel Kachhap static int exynos_tmu_probe(struct platform_device *pdev)
1006cebe7373SAmit Daniel Kachhap {
10073b6a1a80SLukasz Majewski 	struct exynos_tmu_data *data;
10083b6a1a80SLukasz Majewski 	int ret;
1009cebe7373SAmit Daniel Kachhap 
101059dfa54cSAmit Daniel Kachhap 	data = devm_kzalloc(&pdev->dev, sizeof(struct exynos_tmu_data),
101159dfa54cSAmit Daniel Kachhap 					GFP_KERNEL);
10122a9675b3SJingoo Han 	if (!data)
101359dfa54cSAmit Daniel Kachhap 		return -ENOMEM;
101459dfa54cSAmit Daniel Kachhap 
1015cebe7373SAmit Daniel Kachhap 	platform_set_drvdata(pdev, data);
1016cebe7373SAmit Daniel Kachhap 	mutex_init(&data->lock);
1017cebe7373SAmit Daniel Kachhap 
1018824ead03SKrzysztof Kozlowski 	/*
1019824ead03SKrzysztof Kozlowski 	 * Try enabling the regulator if found
1020824ead03SKrzysztof Kozlowski 	 * TODO: Add regulator as an SOC feature, so that regulator enable
1021824ead03SKrzysztof Kozlowski 	 * is a compulsory call.
1022824ead03SKrzysztof Kozlowski 	 */
10234d3583cdSJavier Martinez Canillas 	data->regulator = devm_regulator_get_optional(&pdev->dev, "vtmu");
1024824ead03SKrzysztof Kozlowski 	if (!IS_ERR(data->regulator)) {
1025824ead03SKrzysztof Kozlowski 		ret = regulator_enable(data->regulator);
1026824ead03SKrzysztof Kozlowski 		if (ret) {
1027824ead03SKrzysztof Kozlowski 			dev_err(&pdev->dev, "failed to enable vtmu\n");
1028824ead03SKrzysztof Kozlowski 			return ret;
10293b6a1a80SLukasz Majewski 		}
1030824ead03SKrzysztof Kozlowski 	} else {
1031ccb361d2SJavier Martinez Canillas 		if (PTR_ERR(data->regulator) == -EPROBE_DEFER)
1032ccb361d2SJavier Martinez Canillas 			return -EPROBE_DEFER;
1033824ead03SKrzysztof Kozlowski 		dev_info(&pdev->dev, "Regulator node (vtmu) not found\n");
1034824ead03SKrzysztof Kozlowski 	}
1035824ead03SKrzysztof Kozlowski 
1036cebe7373SAmit Daniel Kachhap 	ret = exynos_map_dt_data(pdev);
1037cebe7373SAmit Daniel Kachhap 	if (ret)
10383b6a1a80SLukasz Majewski 		goto err_sensor;
1039cebe7373SAmit Daniel Kachhap 
104059dfa54cSAmit Daniel Kachhap 	INIT_WORK(&data->irq_work, exynos_tmu_work);
104159dfa54cSAmit Daniel Kachhap 
104259dfa54cSAmit Daniel Kachhap 	data->clk = devm_clk_get(&pdev->dev, "tmu_apbif");
104359dfa54cSAmit Daniel Kachhap 	if (IS_ERR(data->clk)) {
104459dfa54cSAmit Daniel Kachhap 		dev_err(&pdev->dev, "Failed to get clock\n");
10453b6a1a80SLukasz Majewski 		ret = PTR_ERR(data->clk);
10463b6a1a80SLukasz Majewski 		goto err_sensor;
104759dfa54cSAmit Daniel Kachhap 	}
104859dfa54cSAmit Daniel Kachhap 
104914a11dc7SNaveen Krishna Chatradhi 	data->clk_sec = devm_clk_get(&pdev->dev, "tmu_triminfo_apbif");
105014a11dc7SNaveen Krishna Chatradhi 	if (IS_ERR(data->clk_sec)) {
105114a11dc7SNaveen Krishna Chatradhi 		if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) {
105214a11dc7SNaveen Krishna Chatradhi 			dev_err(&pdev->dev, "Failed to get triminfo clock\n");
10533b6a1a80SLukasz Majewski 			ret = PTR_ERR(data->clk_sec);
10543b6a1a80SLukasz Majewski 			goto err_sensor;
105514a11dc7SNaveen Krishna Chatradhi 		}
105614a11dc7SNaveen Krishna Chatradhi 	} else {
105714a11dc7SNaveen Krishna Chatradhi 		ret = clk_prepare(data->clk_sec);
105814a11dc7SNaveen Krishna Chatradhi 		if (ret) {
105914a11dc7SNaveen Krishna Chatradhi 			dev_err(&pdev->dev, "Failed to get clock\n");
10603b6a1a80SLukasz Majewski 			goto err_sensor;
106114a11dc7SNaveen Krishna Chatradhi 		}
106214a11dc7SNaveen Krishna Chatradhi 	}
106314a11dc7SNaveen Krishna Chatradhi 
106414a11dc7SNaveen Krishna Chatradhi 	ret = clk_prepare(data->clk);
106514a11dc7SNaveen Krishna Chatradhi 	if (ret) {
106614a11dc7SNaveen Krishna Chatradhi 		dev_err(&pdev->dev, "Failed to get clock\n");
106714a11dc7SNaveen Krishna Chatradhi 		goto err_clk_sec;
106814a11dc7SNaveen Krishna Chatradhi 	}
106959dfa54cSAmit Daniel Kachhap 
1070488c7455SChanwoo Choi 	switch (data->soc) {
1071488c7455SChanwoo Choi 	case SOC_ARCH_EXYNOS5433:
1072488c7455SChanwoo Choi 	case SOC_ARCH_EXYNOS7:
10736c247393SAbhilash Kesavan 		data->sclk = devm_clk_get(&pdev->dev, "tmu_sclk");
10746c247393SAbhilash Kesavan 		if (IS_ERR(data->sclk)) {
10756c247393SAbhilash Kesavan 			dev_err(&pdev->dev, "Failed to get sclk\n");
10766c247393SAbhilash Kesavan 			goto err_clk;
10776c247393SAbhilash Kesavan 		} else {
10786c247393SAbhilash Kesavan 			ret = clk_prepare_enable(data->sclk);
10796c247393SAbhilash Kesavan 			if (ret) {
10806c247393SAbhilash Kesavan 				dev_err(&pdev->dev, "Failed to enable sclk\n");
10816c247393SAbhilash Kesavan 				goto err_clk;
10826c247393SAbhilash Kesavan 			}
10836c247393SAbhilash Kesavan 		}
1084488c7455SChanwoo Choi 		break;
1085488c7455SChanwoo Choi 	default:
1086488c7455SChanwoo Choi 		break;
1087baba1ebbSKrzysztof Kozlowski 	}
10886c247393SAbhilash Kesavan 
10899e4249b4SKrzysztof Kozlowski 	/*
10909e4249b4SKrzysztof Kozlowski 	 * data->tzd must be registered before calling exynos_tmu_initialize(),
10919e4249b4SKrzysztof Kozlowski 	 * requesting irq and calling exynos_tmu_control().
10929e4249b4SKrzysztof Kozlowski 	 */
10939e4249b4SKrzysztof Kozlowski 	data->tzd = thermal_zone_of_sensor_register(&pdev->dev, 0, data,
10949e4249b4SKrzysztof Kozlowski 						    &exynos_sensor_ops);
10959e4249b4SKrzysztof Kozlowski 	if (IS_ERR(data->tzd)) {
10969e4249b4SKrzysztof Kozlowski 		ret = PTR_ERR(data->tzd);
1097*82bdde8eSMarek Szyprowski 		if (ret != -EPROBE_DEFER)
1098*82bdde8eSMarek Szyprowski 			dev_err(&pdev->dev, "Failed to register sensor: %d\n",
1099*82bdde8eSMarek Szyprowski 				ret);
11009e4249b4SKrzysztof Kozlowski 		goto err_sclk;
11019e4249b4SKrzysztof Kozlowski 	}
110259dfa54cSAmit Daniel Kachhap 
110359dfa54cSAmit Daniel Kachhap 	ret = exynos_tmu_initialize(pdev);
110459dfa54cSAmit Daniel Kachhap 	if (ret) {
110559dfa54cSAmit Daniel Kachhap 		dev_err(&pdev->dev, "Failed to initialize TMU\n");
11069e4249b4SKrzysztof Kozlowski 		goto err_thermal;
110759dfa54cSAmit Daniel Kachhap 	}
110859dfa54cSAmit Daniel Kachhap 
1109cebe7373SAmit Daniel Kachhap 	ret = devm_request_irq(&pdev->dev, data->irq, exynos_tmu_irq,
1110cebe7373SAmit Daniel Kachhap 		IRQF_TRIGGER_RISING | IRQF_SHARED, dev_name(&pdev->dev), data);
1111cebe7373SAmit Daniel Kachhap 	if (ret) {
1112cebe7373SAmit Daniel Kachhap 		dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq);
11139e4249b4SKrzysztof Kozlowski 		goto err_thermal;
1114cebe7373SAmit Daniel Kachhap 	}
111559dfa54cSAmit Daniel Kachhap 
11163b6a1a80SLukasz Majewski 	exynos_tmu_control(pdev, true);
111759dfa54cSAmit Daniel Kachhap 	return 0;
11189e4249b4SKrzysztof Kozlowski 
11199e4249b4SKrzysztof Kozlowski err_thermal:
11209e4249b4SKrzysztof Kozlowski 	thermal_zone_of_sensor_unregister(&pdev->dev, data->tzd);
11216c247393SAbhilash Kesavan err_sclk:
11226c247393SAbhilash Kesavan 	clk_disable_unprepare(data->sclk);
112359dfa54cSAmit Daniel Kachhap err_clk:
112459dfa54cSAmit Daniel Kachhap 	clk_unprepare(data->clk);
112514a11dc7SNaveen Krishna Chatradhi err_clk_sec:
112614a11dc7SNaveen Krishna Chatradhi 	if (!IS_ERR(data->clk_sec))
112714a11dc7SNaveen Krishna Chatradhi 		clk_unprepare(data->clk_sec);
11283b6a1a80SLukasz Majewski err_sensor:
1129bfa26838SKrzysztof Kozlowski 	if (!IS_ERR(data->regulator))
11305f09a5cbSKrzysztof Kozlowski 		regulator_disable(data->regulator);
11313b6a1a80SLukasz Majewski 
113259dfa54cSAmit Daniel Kachhap 	return ret;
113359dfa54cSAmit Daniel Kachhap }
113459dfa54cSAmit Daniel Kachhap 
113559dfa54cSAmit Daniel Kachhap static int exynos_tmu_remove(struct platform_device *pdev)
113659dfa54cSAmit Daniel Kachhap {
113759dfa54cSAmit Daniel Kachhap 	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
11383b6a1a80SLukasz Majewski 	struct thermal_zone_device *tzd = data->tzd;
113959dfa54cSAmit Daniel Kachhap 
11403b6a1a80SLukasz Majewski 	thermal_zone_of_sensor_unregister(&pdev->dev, tzd);
11414215688eSBartlomiej Zolnierkiewicz 	exynos_tmu_control(pdev, false);
11424215688eSBartlomiej Zolnierkiewicz 
11436c247393SAbhilash Kesavan 	clk_disable_unprepare(data->sclk);
114459dfa54cSAmit Daniel Kachhap 	clk_unprepare(data->clk);
114514a11dc7SNaveen Krishna Chatradhi 	if (!IS_ERR(data->clk_sec))
114614a11dc7SNaveen Krishna Chatradhi 		clk_unprepare(data->clk_sec);
114759dfa54cSAmit Daniel Kachhap 
1148498d22f6SAmit Daniel Kachhap 	if (!IS_ERR(data->regulator))
1149498d22f6SAmit Daniel Kachhap 		regulator_disable(data->regulator);
1150498d22f6SAmit Daniel Kachhap 
115159dfa54cSAmit Daniel Kachhap 	return 0;
115259dfa54cSAmit Daniel Kachhap }
115359dfa54cSAmit Daniel Kachhap 
115459dfa54cSAmit Daniel Kachhap #ifdef CONFIG_PM_SLEEP
115559dfa54cSAmit Daniel Kachhap static int exynos_tmu_suspend(struct device *dev)
115659dfa54cSAmit Daniel Kachhap {
115759dfa54cSAmit Daniel Kachhap 	exynos_tmu_control(to_platform_device(dev), false);
115859dfa54cSAmit Daniel Kachhap 
115959dfa54cSAmit Daniel Kachhap 	return 0;
116059dfa54cSAmit Daniel Kachhap }
116159dfa54cSAmit Daniel Kachhap 
116259dfa54cSAmit Daniel Kachhap static int exynos_tmu_resume(struct device *dev)
116359dfa54cSAmit Daniel Kachhap {
116459dfa54cSAmit Daniel Kachhap 	struct platform_device *pdev = to_platform_device(dev);
116559dfa54cSAmit Daniel Kachhap 
116659dfa54cSAmit Daniel Kachhap 	exynos_tmu_initialize(pdev);
116759dfa54cSAmit Daniel Kachhap 	exynos_tmu_control(pdev, true);
116859dfa54cSAmit Daniel Kachhap 
116959dfa54cSAmit Daniel Kachhap 	return 0;
117059dfa54cSAmit Daniel Kachhap }
117159dfa54cSAmit Daniel Kachhap 
117259dfa54cSAmit Daniel Kachhap static SIMPLE_DEV_PM_OPS(exynos_tmu_pm,
117359dfa54cSAmit Daniel Kachhap 			 exynos_tmu_suspend, exynos_tmu_resume);
117459dfa54cSAmit Daniel Kachhap #define EXYNOS_TMU_PM	(&exynos_tmu_pm)
117559dfa54cSAmit Daniel Kachhap #else
117659dfa54cSAmit Daniel Kachhap #define EXYNOS_TMU_PM	NULL
117759dfa54cSAmit Daniel Kachhap #endif
117859dfa54cSAmit Daniel Kachhap 
117959dfa54cSAmit Daniel Kachhap static struct platform_driver exynos_tmu_driver = {
118059dfa54cSAmit Daniel Kachhap 	.driver = {
118159dfa54cSAmit Daniel Kachhap 		.name   = "exynos-tmu",
118259dfa54cSAmit Daniel Kachhap 		.pm     = EXYNOS_TMU_PM,
118373b5b1d7SSachin Kamat 		.of_match_table = exynos_tmu_match,
118459dfa54cSAmit Daniel Kachhap 	},
118559dfa54cSAmit Daniel Kachhap 	.probe = exynos_tmu_probe,
118659dfa54cSAmit Daniel Kachhap 	.remove	= exynos_tmu_remove,
118759dfa54cSAmit Daniel Kachhap };
118859dfa54cSAmit Daniel Kachhap 
118959dfa54cSAmit Daniel Kachhap module_platform_driver(exynos_tmu_driver);
119059dfa54cSAmit Daniel Kachhap 
1191ca07ee4eSKrzysztof Kozlowski MODULE_DESCRIPTION("Exynos TMU Driver");
119259dfa54cSAmit Daniel Kachhap MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
119359dfa54cSAmit Daniel Kachhap MODULE_LICENSE("GPL");
119459dfa54cSAmit Daniel Kachhap MODULE_ALIAS("platform:exynos-tmu");
1195