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