159dfa54cSAmit Daniel Kachhap /* 259dfa54cSAmit Daniel Kachhap * exynos_tmu.c - Samsung EXYNOS TMU (Thermal Management Unit) 359dfa54cSAmit Daniel Kachhap * 43b6a1a80SLukasz Majewski * Copyright (C) 2014 Samsung Electronics 53b6a1a80SLukasz Majewski * Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> 63b6a1a80SLukasz Majewski * Lukasz Majewski <l.majewski@samsung.com> 73b6a1a80SLukasz Majewski * 859dfa54cSAmit Daniel Kachhap * Copyright (C) 2011 Samsung Electronics 959dfa54cSAmit Daniel Kachhap * Donggeun Kim <dg77.kim@samsung.com> 1059dfa54cSAmit Daniel Kachhap * Amit Daniel Kachhap <amit.kachhap@linaro.org> 1159dfa54cSAmit Daniel Kachhap * 1259dfa54cSAmit Daniel Kachhap * This program is free software; you can redistribute it and/or modify 1359dfa54cSAmit Daniel Kachhap * it under the terms of the GNU General Public License as published by 1459dfa54cSAmit Daniel Kachhap * the Free Software Foundation; either version 2 of the License, or 1559dfa54cSAmit Daniel Kachhap * (at your option) any later version. 1659dfa54cSAmit Daniel Kachhap * 1759dfa54cSAmit Daniel Kachhap * This program is distributed in the hope that it will be useful, 1859dfa54cSAmit Daniel Kachhap * but WITHOUT ANY WARRANTY; without even the implied warranty of 1959dfa54cSAmit Daniel Kachhap * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 2059dfa54cSAmit Daniel Kachhap * GNU General Public License for more details. 2159dfa54cSAmit Daniel Kachhap * 2259dfa54cSAmit Daniel Kachhap * You should have received a copy of the GNU General Public License 2359dfa54cSAmit Daniel Kachhap * along with this program; if not, write to the Free Software 2459dfa54cSAmit Daniel Kachhap * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 2559dfa54cSAmit Daniel Kachhap * 2659dfa54cSAmit Daniel Kachhap */ 2759dfa54cSAmit Daniel Kachhap 2859dfa54cSAmit Daniel Kachhap #include <linux/clk.h> 2959dfa54cSAmit Daniel Kachhap #include <linux/io.h> 3059dfa54cSAmit Daniel Kachhap #include <linux/interrupt.h> 3159dfa54cSAmit Daniel Kachhap #include <linux/module.h> 32fee88e2bSMaciej Purski #include <linux/of_device.h> 33cebe7373SAmit Daniel Kachhap #include <linux/of_address.h> 34cebe7373SAmit Daniel Kachhap #include <linux/of_irq.h> 3559dfa54cSAmit Daniel Kachhap #include <linux/platform_device.h> 36498d22f6SAmit Daniel Kachhap #include <linux/regulator/consumer.h> 3759dfa54cSAmit Daniel Kachhap 387efd18a2SBartlomiej Zolnierkiewicz #include <dt-bindings/thermal/thermal_exynos.h> 397efd18a2SBartlomiej Zolnierkiewicz 403b6a1a80SLukasz Majewski #include "../thermal_core.h" 412845f6ecSBartlomiej Zolnierkiewicz 422845f6ecSBartlomiej Zolnierkiewicz /* Exynos generic registers */ 432845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_REG_TRIMINFO 0x0 442845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_REG_CONTROL 0x20 452845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_REG_STATUS 0x28 462845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_REG_CURRENT_TEMP 0x40 472845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_REG_INTEN 0x70 482845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_REG_INTSTAT 0x74 492845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_REG_INTCLEAR 0x78 502845f6ecSBartlomiej Zolnierkiewicz 512845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_TEMP_MASK 0xff 522845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_REF_VOLTAGE_SHIFT 24 532845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_REF_VOLTAGE_MASK 0x1f 542845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_BUF_SLOPE_SEL_MASK 0xf 552845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT 8 562845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_CORE_EN_SHIFT 0 572845f6ecSBartlomiej Zolnierkiewicz 582845f6ecSBartlomiej Zolnierkiewicz /* Exynos3250 specific registers */ 592845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_TRIMINFO_CON1 0x10 602845f6ecSBartlomiej Zolnierkiewicz 612845f6ecSBartlomiej Zolnierkiewicz /* Exynos4210 specific registers */ 622845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS4210_TMU_REG_THRESHOLD_TEMP 0x44 632845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS4210_TMU_REG_TRIG_LEVEL0 0x50 642845f6ecSBartlomiej Zolnierkiewicz 652845f6ecSBartlomiej Zolnierkiewicz /* Exynos5250, Exynos4412, Exynos3250 specific registers */ 662845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_TRIMINFO_CON2 0x14 672845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_THD_TEMP_RISE 0x50 682845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_THD_TEMP_FALL 0x54 692845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_EMUL_CON 0x80 702845f6ecSBartlomiej Zolnierkiewicz 712845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TRIMINFO_RELOAD_ENABLE 1 722845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TRIMINFO_25_SHIFT 0 732845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TRIMINFO_85_SHIFT 8 742845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_TRIP_MODE_SHIFT 13 752845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_TRIP_MODE_MASK 0x7 762845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_THERM_TRIP_EN_SHIFT 12 772845f6ecSBartlomiej Zolnierkiewicz 782845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_INTEN_RISE0_SHIFT 0 792845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_INTEN_FALL0_SHIFT 16 802845f6ecSBartlomiej Zolnierkiewicz 812845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_EMUL_TIME 0x57F0 822845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_EMUL_TIME_MASK 0xffff 832845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_EMUL_TIME_SHIFT 16 842845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_EMUL_DATA_SHIFT 8 852845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_EMUL_DATA_MASK 0xFF 862845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_EMUL_ENABLE 0x1 872845f6ecSBartlomiej Zolnierkiewicz 882845f6ecSBartlomiej Zolnierkiewicz /* Exynos5260 specific */ 892845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5260_TMU_REG_INTEN 0xC0 902845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5260_TMU_REG_INTSTAT 0xC4 912845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5260_TMU_REG_INTCLEAR 0xC8 922845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5260_EMUL_CON 0x100 932845f6ecSBartlomiej Zolnierkiewicz 942845f6ecSBartlomiej Zolnierkiewicz /* Exynos4412 specific */ 952845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS4412_MUX_ADDR_VALUE 6 962845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS4412_MUX_ADDR_SHIFT 20 972845f6ecSBartlomiej Zolnierkiewicz 98488c7455SChanwoo Choi /* Exynos5433 specific registers */ 99488c7455SChanwoo Choi #define EXYNOS5433_THD_TEMP_RISE3_0 0x050 100488c7455SChanwoo Choi #define EXYNOS5433_THD_TEMP_RISE7_4 0x054 101488c7455SChanwoo Choi #define EXYNOS5433_THD_TEMP_FALL3_0 0x060 102488c7455SChanwoo Choi #define EXYNOS5433_THD_TEMP_FALL7_4 0x064 103488c7455SChanwoo Choi #define EXYNOS5433_TMU_REG_INTEN 0x0c0 104488c7455SChanwoo Choi #define EXYNOS5433_TMU_REG_INTPEND 0x0c8 105488c7455SChanwoo Choi #define EXYNOS5433_TMU_EMUL_CON 0x110 106488c7455SChanwoo Choi #define EXYNOS5433_TMU_PD_DET_EN 0x130 107488c7455SChanwoo Choi 108488c7455SChanwoo Choi #define EXYNOS5433_TRIMINFO_SENSOR_ID_SHIFT 16 109488c7455SChanwoo Choi #define EXYNOS5433_TRIMINFO_CALIB_SEL_SHIFT 23 110488c7455SChanwoo Choi #define EXYNOS5433_TRIMINFO_SENSOR_ID_MASK \ 111488c7455SChanwoo Choi (0xf << EXYNOS5433_TRIMINFO_SENSOR_ID_SHIFT) 112488c7455SChanwoo Choi #define EXYNOS5433_TRIMINFO_CALIB_SEL_MASK BIT(23) 113488c7455SChanwoo Choi 114488c7455SChanwoo Choi #define EXYNOS5433_TRIMINFO_ONE_POINT_TRIMMING 0 115488c7455SChanwoo Choi #define EXYNOS5433_TRIMINFO_TWO_POINT_TRIMMING 1 116488c7455SChanwoo Choi 117488c7455SChanwoo Choi #define EXYNOS5433_PD_DET_EN 1 118488c7455SChanwoo Choi 11961020d18SBartlomiej Zolnierkiewicz #define EXYNOS5433_G3D_BASE 0x10070000 12061020d18SBartlomiej Zolnierkiewicz 1216c247393SAbhilash Kesavan /* Exynos7 specific registers */ 1226c247393SAbhilash Kesavan #define EXYNOS7_THD_TEMP_RISE7_6 0x50 1236c247393SAbhilash Kesavan #define EXYNOS7_THD_TEMP_FALL7_6 0x60 1246c247393SAbhilash Kesavan #define EXYNOS7_TMU_REG_INTEN 0x110 1256c247393SAbhilash Kesavan #define EXYNOS7_TMU_REG_INTPEND 0x118 1266c247393SAbhilash Kesavan #define EXYNOS7_TMU_REG_EMUL_CON 0x160 1276c247393SAbhilash Kesavan 1286c247393SAbhilash Kesavan #define EXYNOS7_TMU_TEMP_MASK 0x1ff 1296c247393SAbhilash Kesavan #define EXYNOS7_PD_DET_EN_SHIFT 23 1306c247393SAbhilash Kesavan #define EXYNOS7_TMU_INTEN_RISE0_SHIFT 0 1316c247393SAbhilash Kesavan #define EXYNOS7_EMUL_DATA_SHIFT 7 1326c247393SAbhilash Kesavan #define EXYNOS7_EMUL_DATA_MASK 0x1ff 1336c247393SAbhilash Kesavan 134718b4ca1SBartlomiej Zolnierkiewicz #define EXYNOS_FIRST_POINT_TRIM 25 135718b4ca1SBartlomiej Zolnierkiewicz #define EXYNOS_SECOND_POINT_TRIM 85 136718b4ca1SBartlomiej Zolnierkiewicz 13709d29426SBartlomiej Zolnierkiewicz #define EXYNOS_NOISE_CANCEL_MODE 4 13809d29426SBartlomiej Zolnierkiewicz 1393b6a1a80SLukasz Majewski #define MCELSIUS 1000 1407efd18a2SBartlomiej Zolnierkiewicz 1417efd18a2SBartlomiej Zolnierkiewicz enum soc_type { 1427efd18a2SBartlomiej Zolnierkiewicz SOC_ARCH_EXYNOS3250 = 1, 1437efd18a2SBartlomiej Zolnierkiewicz SOC_ARCH_EXYNOS4210, 1447efd18a2SBartlomiej Zolnierkiewicz SOC_ARCH_EXYNOS4412, 1457efd18a2SBartlomiej Zolnierkiewicz SOC_ARCH_EXYNOS5250, 1467efd18a2SBartlomiej Zolnierkiewicz SOC_ARCH_EXYNOS5260, 1477efd18a2SBartlomiej Zolnierkiewicz SOC_ARCH_EXYNOS5420, 1487efd18a2SBartlomiej Zolnierkiewicz SOC_ARCH_EXYNOS5420_TRIMINFO, 1497efd18a2SBartlomiej Zolnierkiewicz SOC_ARCH_EXYNOS5433, 1507efd18a2SBartlomiej Zolnierkiewicz SOC_ARCH_EXYNOS7, 1517efd18a2SBartlomiej Zolnierkiewicz }; 1527efd18a2SBartlomiej Zolnierkiewicz 153cebe7373SAmit Daniel Kachhap /** 154cebe7373SAmit Daniel Kachhap * struct exynos_tmu_data : A structure to hold the private data of the TMU 155cebe7373SAmit Daniel Kachhap driver 156cebe7373SAmit Daniel Kachhap * @id: identifier of the one instance of the TMU controller. 157cebe7373SAmit Daniel Kachhap * @base: base address of the single instance of the TMU controller. 1589025d563SNaveen Krishna Chatradhi * @base_second: base address of the common registers of the TMU controller. 159cebe7373SAmit Daniel Kachhap * @irq: irq number of the TMU controller. 160cebe7373SAmit Daniel Kachhap * @soc: id of the SOC type. 161cebe7373SAmit Daniel Kachhap * @irq_work: pointer to the irq work structure. 162cebe7373SAmit Daniel Kachhap * @lock: lock to implement synchronization. 163cebe7373SAmit Daniel Kachhap * @clk: pointer to the clock structure. 16414a11dc7SNaveen Krishna Chatradhi * @clk_sec: pointer to the clock structure for accessing the base_second. 1656c247393SAbhilash Kesavan * @sclk: pointer to the clock structure for accessing the tmu special clk. 166199b3e3cSBartlomiej Zolnierkiewicz * @cal_type: calibration type for temperature 167e3ed3649SBartlomiej Zolnierkiewicz * @efuse_value: SoC defined fuse value 168e3ed3649SBartlomiej Zolnierkiewicz * @min_efuse_value: minimum valid trimming data 169e3ed3649SBartlomiej Zolnierkiewicz * @max_efuse_value: maximum valid trimming data 170cebe7373SAmit Daniel Kachhap * @temp_error1: fused value of the first point trim. 171cebe7373SAmit Daniel Kachhap * @temp_error2: fused value of the second point trim. 172fccfe099SBartlomiej Zolnierkiewicz * @gain: gain of amplifier in the positive-TC generator block 173fccfe099SBartlomiej Zolnierkiewicz * 0 < gain <= 15 17461020d18SBartlomiej Zolnierkiewicz * @reference_voltage: reference voltage of amplifier 17561020d18SBartlomiej Zolnierkiewicz * in the positive-TC generator block 17661020d18SBartlomiej Zolnierkiewicz * 0 < reference_voltage <= 31 177498d22f6SAmit Daniel Kachhap * @regulator: pointer to the TMU regulator structure. 178cebe7373SAmit Daniel Kachhap * @reg_conf: pointer to structure to register with core thermal. 1793a3a5f15SKrzysztof Kozlowski * @ntrip: number of supported trip points. 1800eb875d8SMarek Szyprowski * @enabled: current status of TMU device 18172d1100bSBartlomiej Zolnierkiewicz * @tmu_initialize: SoC specific TMU initialization method 18237f9034fSBartlomiej Zolnierkiewicz * @tmu_control: SoC specific TMU control method 183b79985caSBartlomiej Zolnierkiewicz * @tmu_read: SoC specific TMU temperature read method 184285d994aSBartlomiej Zolnierkiewicz * @tmu_set_emulation: SoC specific TMU emulation setting method 185a7331f72SBartlomiej Zolnierkiewicz * @tmu_clear_irqs: SoC specific TMU interrupts clearing method 186cebe7373SAmit Daniel Kachhap */ 18759dfa54cSAmit Daniel Kachhap struct exynos_tmu_data { 188cebe7373SAmit Daniel Kachhap int id; 18959dfa54cSAmit Daniel Kachhap void __iomem *base; 1909025d563SNaveen Krishna Chatradhi void __iomem *base_second; 19159dfa54cSAmit Daniel Kachhap int irq; 19259dfa54cSAmit Daniel Kachhap enum soc_type soc; 19359dfa54cSAmit Daniel Kachhap struct work_struct irq_work; 19459dfa54cSAmit Daniel Kachhap struct mutex lock; 1956c247393SAbhilash Kesavan struct clk *clk, *clk_sec, *sclk; 196199b3e3cSBartlomiej Zolnierkiewicz u32 cal_type; 197e3ed3649SBartlomiej Zolnierkiewicz u32 efuse_value; 198e3ed3649SBartlomiej Zolnierkiewicz u32 min_efuse_value; 199e3ed3649SBartlomiej Zolnierkiewicz u32 max_efuse_value; 2006c247393SAbhilash Kesavan u16 temp_error1, temp_error2; 201fccfe099SBartlomiej Zolnierkiewicz u8 gain; 20261020d18SBartlomiej Zolnierkiewicz u8 reference_voltage; 203498d22f6SAmit Daniel Kachhap struct regulator *regulator; 2043b6a1a80SLukasz Majewski struct thermal_zone_device *tzd; 2053a3a5f15SKrzysztof Kozlowski unsigned int ntrip; 2060eb875d8SMarek Szyprowski bool enabled; 2073b6a1a80SLukasz Majewski 208c8f8f768SBartlomiej Zolnierkiewicz void (*tmu_set_trip_temp)(struct exynos_tmu_data *data, int trip, 209c8f8f768SBartlomiej Zolnierkiewicz u8 temp); 210c8f8f768SBartlomiej Zolnierkiewicz void (*tmu_set_trip_hyst)(struct exynos_tmu_data *data, int trip, 211c8f8f768SBartlomiej Zolnierkiewicz u8 temp, u8 hyst); 212c35268f5SBartlomiej Zolnierkiewicz void (*tmu_initialize)(struct platform_device *pdev); 21337f9034fSBartlomiej Zolnierkiewicz void (*tmu_control)(struct platform_device *pdev, bool on); 214b79985caSBartlomiej Zolnierkiewicz int (*tmu_read)(struct exynos_tmu_data *data); 21517e8351aSSascha Hauer void (*tmu_set_emulation)(struct exynos_tmu_data *data, int temp); 216a7331f72SBartlomiej Zolnierkiewicz void (*tmu_clear_irqs)(struct exynos_tmu_data *data); 21759dfa54cSAmit Daniel Kachhap }; 21859dfa54cSAmit Daniel Kachhap 21959dfa54cSAmit Daniel Kachhap /* 22059dfa54cSAmit Daniel Kachhap * TMU treats temperature as a mapped temperature code. 22159dfa54cSAmit Daniel Kachhap * The temperature is converted differently depending on the calibration type. 22259dfa54cSAmit Daniel Kachhap */ 22359dfa54cSAmit Daniel Kachhap static int temp_to_code(struct exynos_tmu_data *data, u8 temp) 22459dfa54cSAmit Daniel Kachhap { 225199b3e3cSBartlomiej Zolnierkiewicz if (data->cal_type == TYPE_ONE_POINT_TRIMMING) 226718b4ca1SBartlomiej Zolnierkiewicz return temp + data->temp_error1 - EXYNOS_FIRST_POINT_TRIM; 2279c933b1bSBartlomiej Zolnierkiewicz 228718b4ca1SBartlomiej Zolnierkiewicz return (temp - EXYNOS_FIRST_POINT_TRIM) * 22959dfa54cSAmit Daniel Kachhap (data->temp_error2 - data->temp_error1) / 230718b4ca1SBartlomiej Zolnierkiewicz (EXYNOS_SECOND_POINT_TRIM - EXYNOS_FIRST_POINT_TRIM) + 231bb34b4c8SAmit Daniel Kachhap data->temp_error1; 23259dfa54cSAmit Daniel Kachhap } 23359dfa54cSAmit Daniel Kachhap 23459dfa54cSAmit Daniel Kachhap /* 23559dfa54cSAmit Daniel Kachhap * Calculate a temperature value from a temperature code. 23659dfa54cSAmit Daniel Kachhap * The unit of the temperature is degree Celsius. 23759dfa54cSAmit Daniel Kachhap */ 2386c247393SAbhilash Kesavan static int code_to_temp(struct exynos_tmu_data *data, u16 temp_code) 23959dfa54cSAmit Daniel Kachhap { 240199b3e3cSBartlomiej Zolnierkiewicz if (data->cal_type == TYPE_ONE_POINT_TRIMMING) 241718b4ca1SBartlomiej Zolnierkiewicz return temp_code - data->temp_error1 + EXYNOS_FIRST_POINT_TRIM; 2429c933b1bSBartlomiej Zolnierkiewicz 2439c933b1bSBartlomiej Zolnierkiewicz return (temp_code - data->temp_error1) * 244718b4ca1SBartlomiej Zolnierkiewicz (EXYNOS_SECOND_POINT_TRIM - EXYNOS_FIRST_POINT_TRIM) / 245bb34b4c8SAmit Daniel Kachhap (data->temp_error2 - data->temp_error1) + 246718b4ca1SBartlomiej Zolnierkiewicz EXYNOS_FIRST_POINT_TRIM; 24759dfa54cSAmit Daniel Kachhap } 24859dfa54cSAmit Daniel Kachhap 2498328a4b1SBartlomiej Zolnierkiewicz static void sanitize_temp_error(struct exynos_tmu_data *data, u32 trim_info) 250b835ced1SBartlomiej Zolnierkiewicz { 251aef27b65SBartlomiej Zolnierkiewicz u16 tmu_temp_mask = 252aef27b65SBartlomiej Zolnierkiewicz (data->soc == SOC_ARCH_EXYNOS7) ? EXYNOS7_TMU_TEMP_MASK 253aef27b65SBartlomiej Zolnierkiewicz : EXYNOS_TMU_TEMP_MASK; 254aef27b65SBartlomiej Zolnierkiewicz 255aef27b65SBartlomiej Zolnierkiewicz data->temp_error1 = trim_info & tmu_temp_mask; 25699d67fb9SBartlomiej Zolnierkiewicz data->temp_error2 = ((trim_info >> EXYNOS_TRIMINFO_85_SHIFT) & 257b8d582b9SAmit Daniel Kachhap EXYNOS_TMU_TEMP_MASK); 25859dfa54cSAmit Daniel Kachhap 2595000806cSAmit Daniel Kachhap if (!data->temp_error1 || 260e3ed3649SBartlomiej Zolnierkiewicz (data->min_efuse_value > data->temp_error1) || 261e3ed3649SBartlomiej Zolnierkiewicz (data->temp_error1 > data->max_efuse_value)) 262e3ed3649SBartlomiej Zolnierkiewicz data->temp_error1 = data->efuse_value & EXYNOS_TMU_TEMP_MASK; 2635000806cSAmit Daniel Kachhap 2645000806cSAmit Daniel Kachhap if (!data->temp_error2) 2655000806cSAmit Daniel Kachhap data->temp_error2 = 266e3ed3649SBartlomiej Zolnierkiewicz (data->efuse_value >> EXYNOS_TRIMINFO_85_SHIFT) & 2675000806cSAmit Daniel Kachhap EXYNOS_TMU_TEMP_MASK; 2688328a4b1SBartlomiej Zolnierkiewicz } 26959dfa54cSAmit Daniel Kachhap 27059dfa54cSAmit Daniel Kachhap static int exynos_tmu_initialize(struct platform_device *pdev) 27159dfa54cSAmit Daniel Kachhap { 27259dfa54cSAmit Daniel Kachhap struct exynos_tmu_data *data = platform_get_drvdata(pdev); 27375e0f100SBartlomiej Zolnierkiewicz struct thermal_zone_device *tzd = data->tzd; 27475e0f100SBartlomiej Zolnierkiewicz const struct thermal_trip * const trips = 27575e0f100SBartlomiej Zolnierkiewicz of_thermal_get_trip_points(tzd); 27697b3881bSBartlomiej Zolnierkiewicz unsigned int status; 277c8f8f768SBartlomiej Zolnierkiewicz int ret = 0, temp, hyst; 2787ca04e58SAmit Daniel Kachhap 27975e0f100SBartlomiej Zolnierkiewicz if (!trips) { 28075e0f100SBartlomiej Zolnierkiewicz dev_err(&pdev->dev, 28175e0f100SBartlomiej Zolnierkiewicz "Cannot get trip points from device tree!\n"); 28275e0f100SBartlomiej Zolnierkiewicz return -ENODEV; 28375e0f100SBartlomiej Zolnierkiewicz } 28475e0f100SBartlomiej Zolnierkiewicz 2858f1c404bSBartlomiej Zolnierkiewicz if (data->soc != SOC_ARCH_EXYNOS5433) /* FIXME */ 2868f1c404bSBartlomiej Zolnierkiewicz ret = tzd->ops->get_crit_temp(tzd, &temp); 2878f1c404bSBartlomiej Zolnierkiewicz if (ret) { 2888f1c404bSBartlomiej Zolnierkiewicz dev_err(&pdev->dev, 2898f1c404bSBartlomiej Zolnierkiewicz "No CRITICAL trip point defined in device tree!\n"); 2908f1c404bSBartlomiej Zolnierkiewicz goto out; 2918f1c404bSBartlomiej Zolnierkiewicz } 2928f1c404bSBartlomiej Zolnierkiewicz 29375e0f100SBartlomiej Zolnierkiewicz if (of_thermal_get_ntrips(tzd) > data->ntrip) { 2943a3a5f15SKrzysztof Kozlowski dev_info(&pdev->dev, 2953a3a5f15SKrzysztof Kozlowski "More trip points than supported by this TMU.\n"); 2963a3a5f15SKrzysztof Kozlowski dev_info(&pdev->dev, 2973a3a5f15SKrzysztof Kozlowski "%d trip points should be configured in polling mode.\n", 29875e0f100SBartlomiej Zolnierkiewicz (of_thermal_get_ntrips(tzd) - data->ntrip)); 2993a3a5f15SKrzysztof Kozlowski } 3003a3a5f15SKrzysztof Kozlowski 30159dfa54cSAmit Daniel Kachhap mutex_lock(&data->lock); 30259dfa54cSAmit Daniel Kachhap clk_enable(data->clk); 30359dfa54cSAmit Daniel Kachhap if (!IS_ERR(data->clk_sec)) 30459dfa54cSAmit Daniel Kachhap clk_enable(data->clk_sec); 30597b3881bSBartlomiej Zolnierkiewicz 30697b3881bSBartlomiej Zolnierkiewicz status = readb(data->base + EXYNOS_TMU_REG_STATUS); 307fac36bacSBartlomiej Zolnierkiewicz if (!status) { 30897b3881bSBartlomiej Zolnierkiewicz ret = -EBUSY; 309fac36bacSBartlomiej Zolnierkiewicz } else { 310c8f8f768SBartlomiej Zolnierkiewicz int i, ntrips = 311c8f8f768SBartlomiej Zolnierkiewicz min_t(int, of_thermal_get_ntrips(tzd), data->ntrip); 312c8f8f768SBartlomiej Zolnierkiewicz 313c35268f5SBartlomiej Zolnierkiewicz data->tmu_initialize(pdev); 314c8f8f768SBartlomiej Zolnierkiewicz 315c8f8f768SBartlomiej Zolnierkiewicz /* Write temperature code for rising and falling threshold */ 316c8f8f768SBartlomiej Zolnierkiewicz for (i = 0; i < ntrips; i++) { 317c8f8f768SBartlomiej Zolnierkiewicz /* Write temperature code for rising threshold */ 31889335c20SBartlomiej Zolnierkiewicz ret = tzd->ops->get_trip_temp(tzd, i, &temp); 31989335c20SBartlomiej Zolnierkiewicz if (ret) 32089335c20SBartlomiej Zolnierkiewicz goto err; 321c8f8f768SBartlomiej Zolnierkiewicz temp /= MCELSIUS; 322c8f8f768SBartlomiej Zolnierkiewicz data->tmu_set_trip_temp(data, i, temp); 323c8f8f768SBartlomiej Zolnierkiewicz 324c8f8f768SBartlomiej Zolnierkiewicz /* Write temperature code for falling threshold */ 32589335c20SBartlomiej Zolnierkiewicz ret = tzd->ops->get_trip_hyst(tzd, i, &hyst); 32689335c20SBartlomiej Zolnierkiewicz if (ret) 32789335c20SBartlomiej Zolnierkiewicz goto err; 328c8f8f768SBartlomiej Zolnierkiewicz hyst /= MCELSIUS; 329c8f8f768SBartlomiej Zolnierkiewicz data->tmu_set_trip_hyst(data, i, temp, hyst); 330c8f8f768SBartlomiej Zolnierkiewicz } 331c8f8f768SBartlomiej Zolnierkiewicz 332fac36bacSBartlomiej Zolnierkiewicz data->tmu_clear_irqs(data); 333fac36bacSBartlomiej Zolnierkiewicz } 33489335c20SBartlomiej Zolnierkiewicz err: 33559dfa54cSAmit Daniel Kachhap clk_disable(data->clk); 33659dfa54cSAmit Daniel Kachhap mutex_unlock(&data->lock); 33714a11dc7SNaveen Krishna Chatradhi if (!IS_ERR(data->clk_sec)) 33814a11dc7SNaveen Krishna Chatradhi clk_disable(data->clk_sec); 3398f1c404bSBartlomiej Zolnierkiewicz out: 34059dfa54cSAmit Daniel Kachhap return ret; 34159dfa54cSAmit Daniel Kachhap } 34259dfa54cSAmit Daniel Kachhap 343d00671c3SBartlomiej Zolnierkiewicz static u32 get_con_reg(struct exynos_tmu_data *data, u32 con) 34459dfa54cSAmit Daniel Kachhap { 3457575983cSBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS4412 || 3467575983cSBartlomiej Zolnierkiewicz data->soc == SOC_ARCH_EXYNOS3250) 3477575983cSBartlomiej Zolnierkiewicz con |= (EXYNOS4412_MUX_ADDR_VALUE << EXYNOS4412_MUX_ADDR_SHIFT); 34886f5362eSLukasz Majewski 34999d67fb9SBartlomiej Zolnierkiewicz con &= ~(EXYNOS_TMU_REF_VOLTAGE_MASK << EXYNOS_TMU_REF_VOLTAGE_SHIFT); 35061020d18SBartlomiej Zolnierkiewicz con |= data->reference_voltage << EXYNOS_TMU_REF_VOLTAGE_SHIFT; 351d0a0ce3eSAmit Daniel Kachhap 35299d67fb9SBartlomiej Zolnierkiewicz con &= ~(EXYNOS_TMU_BUF_SLOPE_SEL_MASK << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT); 353fccfe099SBartlomiej Zolnierkiewicz con |= (data->gain << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT); 354d0a0ce3eSAmit Daniel Kachhap 355b9504a6aSBartlomiej Zolnierkiewicz con &= ~(EXYNOS_TMU_TRIP_MODE_MASK << EXYNOS_TMU_TRIP_MODE_SHIFT); 35609d29426SBartlomiej Zolnierkiewicz con |= (EXYNOS_NOISE_CANCEL_MODE << EXYNOS_TMU_TRIP_MODE_SHIFT); 35759dfa54cSAmit Daniel Kachhap 358d00671c3SBartlomiej Zolnierkiewicz return con; 359d00671c3SBartlomiej Zolnierkiewicz } 360d00671c3SBartlomiej Zolnierkiewicz 361d00671c3SBartlomiej Zolnierkiewicz static void exynos_tmu_control(struct platform_device *pdev, bool on) 362d00671c3SBartlomiej Zolnierkiewicz { 363d00671c3SBartlomiej Zolnierkiewicz struct exynos_tmu_data *data = platform_get_drvdata(pdev); 364d00671c3SBartlomiej Zolnierkiewicz 365d00671c3SBartlomiej Zolnierkiewicz mutex_lock(&data->lock); 366d00671c3SBartlomiej Zolnierkiewicz clk_enable(data->clk); 36737f9034fSBartlomiej Zolnierkiewicz data->tmu_control(pdev, on); 3680eb875d8SMarek Szyprowski data->enabled = on; 36959dfa54cSAmit Daniel Kachhap clk_disable(data->clk); 37059dfa54cSAmit Daniel Kachhap mutex_unlock(&data->lock); 37159dfa54cSAmit Daniel Kachhap } 37259dfa54cSAmit Daniel Kachhap 373a503a10fSBartlomiej Zolnierkiewicz static void exynos4210_tmu_set_trip_temp(struct exynos_tmu_data *data, 374a503a10fSBartlomiej Zolnierkiewicz int trip, u8 temp) 375a503a10fSBartlomiej Zolnierkiewicz { 376a503a10fSBartlomiej Zolnierkiewicz const struct thermal_trip * const trips = 377a503a10fSBartlomiej Zolnierkiewicz of_thermal_get_trip_points(data->tzd); 378a503a10fSBartlomiej Zolnierkiewicz u8 ref, th_code; 379a503a10fSBartlomiej Zolnierkiewicz 380a503a10fSBartlomiej Zolnierkiewicz ref = trips[0].temperature / MCELSIUS; 381a503a10fSBartlomiej Zolnierkiewicz 382a503a10fSBartlomiej Zolnierkiewicz if (trip == 0) { 383a503a10fSBartlomiej Zolnierkiewicz th_code = temp_to_code(data, ref); 384a503a10fSBartlomiej Zolnierkiewicz writeb(th_code, data->base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP); 385a503a10fSBartlomiej Zolnierkiewicz } 386a503a10fSBartlomiej Zolnierkiewicz 387a503a10fSBartlomiej Zolnierkiewicz temp -= ref; 388a503a10fSBartlomiej Zolnierkiewicz writeb(temp, data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL0 + trip * 4); 389a503a10fSBartlomiej Zolnierkiewicz } 390a503a10fSBartlomiej Zolnierkiewicz 391c8f8f768SBartlomiej Zolnierkiewicz /* failing thresholds are not supported on Exynos4210 */ 392c8f8f768SBartlomiej Zolnierkiewicz static void exynos4210_tmu_set_trip_hyst(struct exynos_tmu_data *data, 393c8f8f768SBartlomiej Zolnierkiewicz int trip, u8 temp, u8 hyst) 394c8f8f768SBartlomiej Zolnierkiewicz { 395c8f8f768SBartlomiej Zolnierkiewicz } 396c8f8f768SBartlomiej Zolnierkiewicz 397c35268f5SBartlomiej Zolnierkiewicz static void exynos4210_tmu_initialize(struct platform_device *pdev) 39872d1100bSBartlomiej Zolnierkiewicz { 39972d1100bSBartlomiej Zolnierkiewicz struct exynos_tmu_data *data = platform_get_drvdata(pdev); 40072d1100bSBartlomiej Zolnierkiewicz 40172d1100bSBartlomiej Zolnierkiewicz sanitize_temp_error(data, readl(data->base + EXYNOS_TMU_REG_TRIMINFO)); 40272d1100bSBartlomiej Zolnierkiewicz } 40372d1100bSBartlomiej Zolnierkiewicz 404a503a10fSBartlomiej Zolnierkiewicz static void exynos4412_tmu_set_trip_temp(struct exynos_tmu_data *data, 405a503a10fSBartlomiej Zolnierkiewicz int trip, u8 temp) 406a503a10fSBartlomiej Zolnierkiewicz { 407a503a10fSBartlomiej Zolnierkiewicz u32 th, con; 408a503a10fSBartlomiej Zolnierkiewicz 409a503a10fSBartlomiej Zolnierkiewicz th = readl(data->base + EXYNOS_THD_TEMP_RISE); 410a503a10fSBartlomiej Zolnierkiewicz th &= ~(0xff << 8 * trip); 411a503a10fSBartlomiej Zolnierkiewicz th |= temp_to_code(data, temp) << 8 * trip; 412a503a10fSBartlomiej Zolnierkiewicz writel(th, data->base + EXYNOS_THD_TEMP_RISE); 413a503a10fSBartlomiej Zolnierkiewicz 414a503a10fSBartlomiej Zolnierkiewicz if (trip == 3) { 415a503a10fSBartlomiej Zolnierkiewicz con = readl(data->base + EXYNOS_TMU_REG_CONTROL); 416a503a10fSBartlomiej Zolnierkiewicz con |= (1 << EXYNOS_TMU_THERM_TRIP_EN_SHIFT); 417a503a10fSBartlomiej Zolnierkiewicz writel(con, data->base + EXYNOS_TMU_REG_CONTROL); 418a503a10fSBartlomiej Zolnierkiewicz } 419a503a10fSBartlomiej Zolnierkiewicz } 420a503a10fSBartlomiej Zolnierkiewicz 421a503a10fSBartlomiej Zolnierkiewicz static void exynos4412_tmu_set_trip_hyst(struct exynos_tmu_data *data, 422a503a10fSBartlomiej Zolnierkiewicz int trip, u8 temp, u8 hyst) 423a503a10fSBartlomiej Zolnierkiewicz { 424a503a10fSBartlomiej Zolnierkiewicz u32 th; 425a503a10fSBartlomiej Zolnierkiewicz 426a503a10fSBartlomiej Zolnierkiewicz th = readl(data->base + EXYNOS_THD_TEMP_FALL); 427a503a10fSBartlomiej Zolnierkiewicz th &= ~(0xff << 8 * trip); 428a503a10fSBartlomiej Zolnierkiewicz if (hyst) 429a503a10fSBartlomiej Zolnierkiewicz th |= temp_to_code(data, temp - hyst) << 8 * trip; 430a503a10fSBartlomiej Zolnierkiewicz writel(th, data->base + EXYNOS_THD_TEMP_FALL); 431a503a10fSBartlomiej Zolnierkiewicz } 432a503a10fSBartlomiej Zolnierkiewicz 433c35268f5SBartlomiej Zolnierkiewicz static void exynos4412_tmu_initialize(struct platform_device *pdev) 43472d1100bSBartlomiej Zolnierkiewicz { 43572d1100bSBartlomiej Zolnierkiewicz struct exynos_tmu_data *data = platform_get_drvdata(pdev); 436a503a10fSBartlomiej Zolnierkiewicz unsigned int trim_info, ctrl; 43772d1100bSBartlomiej Zolnierkiewicz 43872d1100bSBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS3250 || 43972d1100bSBartlomiej Zolnierkiewicz data->soc == SOC_ARCH_EXYNOS4412 || 44072d1100bSBartlomiej Zolnierkiewicz data->soc == SOC_ARCH_EXYNOS5250) { 44172d1100bSBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS3250) { 44272d1100bSBartlomiej Zolnierkiewicz ctrl = readl(data->base + EXYNOS_TMU_TRIMINFO_CON1); 44372d1100bSBartlomiej Zolnierkiewicz ctrl |= EXYNOS_TRIMINFO_RELOAD_ENABLE; 44472d1100bSBartlomiej Zolnierkiewicz writel(ctrl, data->base + EXYNOS_TMU_TRIMINFO_CON1); 44572d1100bSBartlomiej Zolnierkiewicz } 44672d1100bSBartlomiej Zolnierkiewicz ctrl = readl(data->base + EXYNOS_TMU_TRIMINFO_CON2); 44772d1100bSBartlomiej Zolnierkiewicz ctrl |= EXYNOS_TRIMINFO_RELOAD_ENABLE; 44872d1100bSBartlomiej Zolnierkiewicz writel(ctrl, data->base + EXYNOS_TMU_TRIMINFO_CON2); 44972d1100bSBartlomiej Zolnierkiewicz } 45072d1100bSBartlomiej Zolnierkiewicz 45172d1100bSBartlomiej Zolnierkiewicz /* On exynos5420 the triminfo register is in the shared space */ 45272d1100bSBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) 45372d1100bSBartlomiej Zolnierkiewicz trim_info = readl(data->base_second + EXYNOS_TMU_REG_TRIMINFO); 45472d1100bSBartlomiej Zolnierkiewicz else 45572d1100bSBartlomiej Zolnierkiewicz trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); 45672d1100bSBartlomiej Zolnierkiewicz 45772d1100bSBartlomiej Zolnierkiewicz sanitize_temp_error(data, trim_info); 4583b6a1a80SLukasz Majewski } 4593b6a1a80SLukasz Majewski 460a503a10fSBartlomiej Zolnierkiewicz static void exynos5433_tmu_set_trip_temp(struct exynos_tmu_data *data, 461a503a10fSBartlomiej Zolnierkiewicz int trip, u8 temp) 462a503a10fSBartlomiej Zolnierkiewicz { 463a503a10fSBartlomiej Zolnierkiewicz unsigned int reg_off, j; 464a503a10fSBartlomiej Zolnierkiewicz u32 th; 465a503a10fSBartlomiej Zolnierkiewicz 466a503a10fSBartlomiej Zolnierkiewicz if (trip > 3) { 467a503a10fSBartlomiej Zolnierkiewicz reg_off = EXYNOS5433_THD_TEMP_RISE7_4; 468a503a10fSBartlomiej Zolnierkiewicz j = trip - 4; 469a503a10fSBartlomiej Zolnierkiewicz } else { 470a503a10fSBartlomiej Zolnierkiewicz reg_off = EXYNOS5433_THD_TEMP_RISE3_0; 471a503a10fSBartlomiej Zolnierkiewicz j = trip; 472a503a10fSBartlomiej Zolnierkiewicz } 473a503a10fSBartlomiej Zolnierkiewicz 474a503a10fSBartlomiej Zolnierkiewicz th = readl(data->base + reg_off); 475a503a10fSBartlomiej Zolnierkiewicz th &= ~(0xff << j * 8); 476a503a10fSBartlomiej Zolnierkiewicz th |= (temp_to_code(data, temp) << j * 8); 477a503a10fSBartlomiej Zolnierkiewicz writel(th, data->base + reg_off); 478a503a10fSBartlomiej Zolnierkiewicz } 479a503a10fSBartlomiej Zolnierkiewicz 480a503a10fSBartlomiej Zolnierkiewicz static void exynos5433_tmu_set_trip_hyst(struct exynos_tmu_data *data, 481a503a10fSBartlomiej Zolnierkiewicz int trip, u8 temp, u8 hyst) 482a503a10fSBartlomiej Zolnierkiewicz { 483a503a10fSBartlomiej Zolnierkiewicz unsigned int reg_off, j; 484a503a10fSBartlomiej Zolnierkiewicz u32 th; 485a503a10fSBartlomiej Zolnierkiewicz 486a503a10fSBartlomiej Zolnierkiewicz if (trip > 3) { 487a503a10fSBartlomiej Zolnierkiewicz reg_off = EXYNOS5433_THD_TEMP_FALL7_4; 488a503a10fSBartlomiej Zolnierkiewicz j = trip - 4; 489a503a10fSBartlomiej Zolnierkiewicz } else { 490a503a10fSBartlomiej Zolnierkiewicz reg_off = EXYNOS5433_THD_TEMP_FALL3_0; 491a503a10fSBartlomiej Zolnierkiewicz j = trip; 492a503a10fSBartlomiej Zolnierkiewicz } 493a503a10fSBartlomiej Zolnierkiewicz 494a503a10fSBartlomiej Zolnierkiewicz th = readl(data->base + reg_off); 495a503a10fSBartlomiej Zolnierkiewicz th &= ~(0xff << j * 8); 496a503a10fSBartlomiej Zolnierkiewicz th |= (temp_to_code(data, temp - hyst) << j * 8); 497a503a10fSBartlomiej Zolnierkiewicz writel(th, data->base + reg_off); 49872d1100bSBartlomiej Zolnierkiewicz } 49972d1100bSBartlomiej Zolnierkiewicz 500c35268f5SBartlomiej Zolnierkiewicz static void exynos5433_tmu_initialize(struct platform_device *pdev) 501488c7455SChanwoo Choi { 502488c7455SChanwoo Choi struct exynos_tmu_data *data = platform_get_drvdata(pdev); 50397b3881bSBartlomiej Zolnierkiewicz unsigned int trim_info; 504c8f8f768SBartlomiej Zolnierkiewicz int sensor_id, cal_type; 505488c7455SChanwoo Choi 506488c7455SChanwoo Choi trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); 507488c7455SChanwoo Choi sanitize_temp_error(data, trim_info); 508488c7455SChanwoo Choi 509488c7455SChanwoo Choi /* Read the temperature sensor id */ 510488c7455SChanwoo Choi sensor_id = (trim_info & EXYNOS5433_TRIMINFO_SENSOR_ID_MASK) 511488c7455SChanwoo Choi >> EXYNOS5433_TRIMINFO_SENSOR_ID_SHIFT; 512488c7455SChanwoo Choi dev_info(&pdev->dev, "Temperature sensor ID: 0x%x\n", sensor_id); 513488c7455SChanwoo Choi 514488c7455SChanwoo Choi /* Read the calibration mode */ 515488c7455SChanwoo Choi writel(trim_info, data->base + EXYNOS_TMU_REG_TRIMINFO); 516488c7455SChanwoo Choi cal_type = (trim_info & EXYNOS5433_TRIMINFO_CALIB_SEL_MASK) 517488c7455SChanwoo Choi >> EXYNOS5433_TRIMINFO_CALIB_SEL_SHIFT; 518488c7455SChanwoo Choi 519488c7455SChanwoo Choi switch (cal_type) { 520488c7455SChanwoo Choi case EXYNOS5433_TRIMINFO_TWO_POINT_TRIMMING: 521199b3e3cSBartlomiej Zolnierkiewicz data->cal_type = TYPE_TWO_POINT_TRIMMING; 522488c7455SChanwoo Choi break; 523199b3e3cSBartlomiej Zolnierkiewicz case EXYNOS5433_TRIMINFO_ONE_POINT_TRIMMING: 524488c7455SChanwoo Choi default: 525199b3e3cSBartlomiej Zolnierkiewicz data->cal_type = TYPE_ONE_POINT_TRIMMING; 526488c7455SChanwoo Choi break; 527baba1ebbSKrzysztof Kozlowski } 528488c7455SChanwoo Choi 529488c7455SChanwoo Choi dev_info(&pdev->dev, "Calibration type is %d-point calibration\n", 530488c7455SChanwoo Choi cal_type ? 2 : 1); 531488c7455SChanwoo Choi } 532488c7455SChanwoo Choi 533a503a10fSBartlomiej Zolnierkiewicz static void exynos7_tmu_set_trip_temp(struct exynos_tmu_data *data, 534a503a10fSBartlomiej Zolnierkiewicz int trip, u8 temp) 535a503a10fSBartlomiej Zolnierkiewicz { 536a503a10fSBartlomiej Zolnierkiewicz unsigned int reg_off, bit_off; 537a503a10fSBartlomiej Zolnierkiewicz u32 th; 538a503a10fSBartlomiej Zolnierkiewicz 539a503a10fSBartlomiej Zolnierkiewicz reg_off = ((7 - trip) / 2) * 4; 540a503a10fSBartlomiej Zolnierkiewicz bit_off = ((8 - trip) % 2); 541a503a10fSBartlomiej Zolnierkiewicz 542a503a10fSBartlomiej Zolnierkiewicz th = readl(data->base + EXYNOS7_THD_TEMP_RISE7_6 + reg_off); 543a503a10fSBartlomiej Zolnierkiewicz th &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off)); 544a503a10fSBartlomiej Zolnierkiewicz th |= temp_to_code(data, temp) << (16 * bit_off); 545a503a10fSBartlomiej Zolnierkiewicz writel(th, data->base + EXYNOS7_THD_TEMP_RISE7_6 + reg_off); 546a503a10fSBartlomiej Zolnierkiewicz } 547a503a10fSBartlomiej Zolnierkiewicz 548a503a10fSBartlomiej Zolnierkiewicz static void exynos7_tmu_set_trip_hyst(struct exynos_tmu_data *data, 549a503a10fSBartlomiej Zolnierkiewicz int trip, u8 temp, u8 hyst) 550a503a10fSBartlomiej Zolnierkiewicz { 551a503a10fSBartlomiej Zolnierkiewicz unsigned int reg_off, bit_off; 552a503a10fSBartlomiej Zolnierkiewicz u32 th; 553a503a10fSBartlomiej Zolnierkiewicz 554a503a10fSBartlomiej Zolnierkiewicz reg_off = ((7 - trip) / 2) * 4; 555a503a10fSBartlomiej Zolnierkiewicz bit_off = ((8 - trip) % 2); 556a503a10fSBartlomiej Zolnierkiewicz 557a503a10fSBartlomiej Zolnierkiewicz th = readl(data->base + EXYNOS7_THD_TEMP_FALL7_6 + reg_off); 558a503a10fSBartlomiej Zolnierkiewicz th &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off)); 559a503a10fSBartlomiej Zolnierkiewicz th |= temp_to_code(data, temp - hyst) << (16 * bit_off); 560a503a10fSBartlomiej Zolnierkiewicz writel(th, data->base + EXYNOS7_THD_TEMP_FALL7_6 + reg_off); 561a503a10fSBartlomiej Zolnierkiewicz } 562a503a10fSBartlomiej Zolnierkiewicz 563c35268f5SBartlomiej Zolnierkiewicz static void exynos7_tmu_initialize(struct platform_device *pdev) 5646c247393SAbhilash Kesavan { 5656c247393SAbhilash Kesavan struct exynos_tmu_data *data = platform_get_drvdata(pdev); 56697b3881bSBartlomiej Zolnierkiewicz unsigned int trim_info; 5676c247393SAbhilash Kesavan 5686c247393SAbhilash Kesavan trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); 569aef27b65SBartlomiej Zolnierkiewicz sanitize_temp_error(data, trim_info); 5706c247393SAbhilash Kesavan } 5716c247393SAbhilash Kesavan 57237f9034fSBartlomiej Zolnierkiewicz static void exynos4210_tmu_control(struct platform_device *pdev, bool on) 57337f9034fSBartlomiej Zolnierkiewicz { 57437f9034fSBartlomiej Zolnierkiewicz struct exynos_tmu_data *data = platform_get_drvdata(pdev); 5753b6a1a80SLukasz Majewski struct thermal_zone_device *tz = data->tzd; 57664e94192SBartlomiej Zolnierkiewicz unsigned int con, interrupt_en = 0, i; 57737f9034fSBartlomiej Zolnierkiewicz 57837f9034fSBartlomiej Zolnierkiewicz con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL)); 57937f9034fSBartlomiej Zolnierkiewicz 58059dfa54cSAmit Daniel Kachhap if (on) { 58164e94192SBartlomiej Zolnierkiewicz for (i = 0; i < data->ntrip; i++) { 58264e94192SBartlomiej Zolnierkiewicz if (!of_thermal_is_trip_valid(tz, i)) 58364e94192SBartlomiej Zolnierkiewicz continue; 58464e94192SBartlomiej Zolnierkiewicz 58564e94192SBartlomiej Zolnierkiewicz interrupt_en |= 58664e94192SBartlomiej Zolnierkiewicz (1 << (EXYNOS_TMU_INTEN_RISE0_SHIFT + i * 4)); 58764e94192SBartlomiej Zolnierkiewicz } 5883b6a1a80SLukasz Majewski 589e0761533SBartlomiej Zolnierkiewicz if (data->soc != SOC_ARCH_EXYNOS4210) 59059dfa54cSAmit Daniel Kachhap interrupt_en |= 59137f9034fSBartlomiej Zolnierkiewicz interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT; 59264e94192SBartlomiej Zolnierkiewicz 59364e94192SBartlomiej Zolnierkiewicz con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); 59459dfa54cSAmit Daniel Kachhap } else { 59559dfa54cSAmit Daniel Kachhap con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); 59659dfa54cSAmit Daniel Kachhap } 59764e94192SBartlomiej Zolnierkiewicz 59837f9034fSBartlomiej Zolnierkiewicz writel(interrupt_en, data->base + EXYNOS_TMU_REG_INTEN); 59937f9034fSBartlomiej Zolnierkiewicz writel(con, data->base + EXYNOS_TMU_REG_CONTROL); 60037f9034fSBartlomiej Zolnierkiewicz } 60159dfa54cSAmit Daniel Kachhap 602488c7455SChanwoo Choi static void exynos5433_tmu_control(struct platform_device *pdev, bool on) 603488c7455SChanwoo Choi { 604488c7455SChanwoo Choi struct exynos_tmu_data *data = platform_get_drvdata(pdev); 605488c7455SChanwoo Choi struct thermal_zone_device *tz = data->tzd; 60664e94192SBartlomiej Zolnierkiewicz unsigned int con, interrupt_en = 0, pd_det_en, i; 607488c7455SChanwoo Choi 608488c7455SChanwoo Choi con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL)); 609488c7455SChanwoo Choi 610488c7455SChanwoo Choi if (on) { 61164e94192SBartlomiej Zolnierkiewicz for (i = 0; i < data->ntrip; i++) { 61264e94192SBartlomiej Zolnierkiewicz if (!of_thermal_is_trip_valid(tz, i)) 61364e94192SBartlomiej Zolnierkiewicz continue; 61464e94192SBartlomiej Zolnierkiewicz 61564e94192SBartlomiej Zolnierkiewicz interrupt_en |= 61664e94192SBartlomiej Zolnierkiewicz (1 << (EXYNOS7_TMU_INTEN_RISE0_SHIFT + i)); 61764e94192SBartlomiej Zolnierkiewicz } 618488c7455SChanwoo Choi 619488c7455SChanwoo Choi interrupt_en |= 620488c7455SChanwoo Choi interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT; 62164e94192SBartlomiej Zolnierkiewicz 62264e94192SBartlomiej Zolnierkiewicz con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); 62364e94192SBartlomiej Zolnierkiewicz } else 624488c7455SChanwoo Choi con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); 625488c7455SChanwoo Choi 626488c7455SChanwoo Choi pd_det_en = on ? EXYNOS5433_PD_DET_EN : 0; 627488c7455SChanwoo Choi 628488c7455SChanwoo Choi writel(pd_det_en, data->base + EXYNOS5433_TMU_PD_DET_EN); 629488c7455SChanwoo Choi writel(interrupt_en, data->base + EXYNOS5433_TMU_REG_INTEN); 630488c7455SChanwoo Choi writel(con, data->base + EXYNOS_TMU_REG_CONTROL); 631488c7455SChanwoo Choi } 632488c7455SChanwoo Choi 6336c247393SAbhilash Kesavan static void exynos7_tmu_control(struct platform_device *pdev, bool on) 6346c247393SAbhilash Kesavan { 6356c247393SAbhilash Kesavan struct exynos_tmu_data *data = platform_get_drvdata(pdev); 6366c247393SAbhilash Kesavan struct thermal_zone_device *tz = data->tzd; 63764e94192SBartlomiej Zolnierkiewicz unsigned int con, interrupt_en = 0, i; 6386c247393SAbhilash Kesavan 6396c247393SAbhilash Kesavan con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL)); 6406c247393SAbhilash Kesavan 6416c247393SAbhilash Kesavan if (on) { 64264e94192SBartlomiej Zolnierkiewicz for (i = 0; i < data->ntrip; i++) { 64364e94192SBartlomiej Zolnierkiewicz if (!of_thermal_is_trip_valid(tz, i)) 64464e94192SBartlomiej Zolnierkiewicz continue; 64564e94192SBartlomiej Zolnierkiewicz 64664e94192SBartlomiej Zolnierkiewicz interrupt_en |= 64764e94192SBartlomiej Zolnierkiewicz (1 << (EXYNOS7_TMU_INTEN_RISE0_SHIFT + i)); 64864e94192SBartlomiej Zolnierkiewicz } 6496c247393SAbhilash Kesavan 6506c247393SAbhilash Kesavan interrupt_en |= 6516c247393SAbhilash Kesavan interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT; 65264e94192SBartlomiej Zolnierkiewicz 65364e94192SBartlomiej Zolnierkiewicz con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); 65464e94192SBartlomiej Zolnierkiewicz con |= (1 << EXYNOS7_PD_DET_EN_SHIFT); 6556c247393SAbhilash Kesavan } else { 6566c247393SAbhilash Kesavan con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); 65742b696e8SChanwoo Choi con &= ~(1 << EXYNOS7_PD_DET_EN_SHIFT); 6586c247393SAbhilash Kesavan } 6596c247393SAbhilash Kesavan 6606c247393SAbhilash Kesavan writel(interrupt_en, data->base + EXYNOS7_TMU_REG_INTEN); 6616c247393SAbhilash Kesavan writel(con, data->base + EXYNOS_TMU_REG_CONTROL); 6626c247393SAbhilash Kesavan } 6636c247393SAbhilash Kesavan 66417e8351aSSascha Hauer static int exynos_get_temp(void *p, int *temp) 66559dfa54cSAmit Daniel Kachhap { 6663b6a1a80SLukasz Majewski struct exynos_tmu_data *data = p; 66708d725cdSMarek Szyprowski int value, ret = 0; 6683b6a1a80SLukasz Majewski 669*ffe6e16fSKrzysztof Kozlowski if (!data || !data->tmu_read) 6703b6a1a80SLukasz Majewski return -EINVAL; 671*ffe6e16fSKrzysztof Kozlowski else if (!data->enabled) 672*ffe6e16fSKrzysztof Kozlowski /* 673*ffe6e16fSKrzysztof Kozlowski * Called too early, probably 674*ffe6e16fSKrzysztof Kozlowski * from thermal_zone_of_sensor_register(). 675*ffe6e16fSKrzysztof Kozlowski */ 676*ffe6e16fSKrzysztof Kozlowski return -EAGAIN; 67759dfa54cSAmit Daniel Kachhap 67859dfa54cSAmit Daniel Kachhap mutex_lock(&data->lock); 67959dfa54cSAmit Daniel Kachhap clk_enable(data->clk); 6803b6a1a80SLukasz Majewski 68108d725cdSMarek Szyprowski value = data->tmu_read(data); 68208d725cdSMarek Szyprowski if (value < 0) 68308d725cdSMarek Szyprowski ret = value; 68408d725cdSMarek Szyprowski else 68508d725cdSMarek Szyprowski *temp = code_to_temp(data, value) * MCELSIUS; 6863b6a1a80SLukasz Majewski 68759dfa54cSAmit Daniel Kachhap clk_disable(data->clk); 68859dfa54cSAmit Daniel Kachhap mutex_unlock(&data->lock); 68959dfa54cSAmit Daniel Kachhap 69008d725cdSMarek Szyprowski return ret; 69159dfa54cSAmit Daniel Kachhap } 69259dfa54cSAmit Daniel Kachhap 69359dfa54cSAmit Daniel Kachhap #ifdef CONFIG_THERMAL_EMULATION 694154013eaSBartlomiej Zolnierkiewicz static u32 get_emul_con_reg(struct exynos_tmu_data *data, unsigned int val, 69517e8351aSSascha Hauer int temp) 696154013eaSBartlomiej Zolnierkiewicz { 697154013eaSBartlomiej Zolnierkiewicz if (temp) { 698154013eaSBartlomiej Zolnierkiewicz temp /= MCELSIUS; 699154013eaSBartlomiej Zolnierkiewicz 700154013eaSBartlomiej Zolnierkiewicz val &= ~(EXYNOS_EMUL_TIME_MASK << EXYNOS_EMUL_TIME_SHIFT); 701154013eaSBartlomiej Zolnierkiewicz val |= (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT); 7026c247393SAbhilash Kesavan if (data->soc == SOC_ARCH_EXYNOS7) { 7036c247393SAbhilash Kesavan val &= ~(EXYNOS7_EMUL_DATA_MASK << 7046c247393SAbhilash Kesavan EXYNOS7_EMUL_DATA_SHIFT); 7056c247393SAbhilash Kesavan val |= (temp_to_code(data, temp) << 7066c247393SAbhilash Kesavan EXYNOS7_EMUL_DATA_SHIFT) | 707154013eaSBartlomiej Zolnierkiewicz EXYNOS_EMUL_ENABLE; 708154013eaSBartlomiej Zolnierkiewicz } else { 7096c247393SAbhilash Kesavan val &= ~(EXYNOS_EMUL_DATA_MASK << 7106c247393SAbhilash Kesavan EXYNOS_EMUL_DATA_SHIFT); 7116c247393SAbhilash Kesavan val |= (temp_to_code(data, temp) << 7126c247393SAbhilash Kesavan EXYNOS_EMUL_DATA_SHIFT) | 7136c247393SAbhilash Kesavan EXYNOS_EMUL_ENABLE; 7146c247393SAbhilash Kesavan } 7156c247393SAbhilash Kesavan } else { 716154013eaSBartlomiej Zolnierkiewicz val &= ~EXYNOS_EMUL_ENABLE; 717154013eaSBartlomiej Zolnierkiewicz } 718154013eaSBartlomiej Zolnierkiewicz 719154013eaSBartlomiej Zolnierkiewicz return val; 720154013eaSBartlomiej Zolnierkiewicz } 721154013eaSBartlomiej Zolnierkiewicz 722285d994aSBartlomiej Zolnierkiewicz static void exynos4412_tmu_set_emulation(struct exynos_tmu_data *data, 72317e8351aSSascha Hauer int temp) 724285d994aSBartlomiej Zolnierkiewicz { 725285d994aSBartlomiej Zolnierkiewicz unsigned int val; 726285d994aSBartlomiej Zolnierkiewicz u32 emul_con; 727285d994aSBartlomiej Zolnierkiewicz 728285d994aSBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS5260) 729285d994aSBartlomiej Zolnierkiewicz emul_con = EXYNOS5260_EMUL_CON; 730b28fec13SSudip Mukherjee else if (data->soc == SOC_ARCH_EXYNOS5433) 731488c7455SChanwoo Choi emul_con = EXYNOS5433_TMU_EMUL_CON; 7326c247393SAbhilash Kesavan else if (data->soc == SOC_ARCH_EXYNOS7) 7336c247393SAbhilash Kesavan emul_con = EXYNOS7_TMU_REG_EMUL_CON; 734285d994aSBartlomiej Zolnierkiewicz else 735285d994aSBartlomiej Zolnierkiewicz emul_con = EXYNOS_EMUL_CON; 736285d994aSBartlomiej Zolnierkiewicz 737285d994aSBartlomiej Zolnierkiewicz val = readl(data->base + emul_con); 738285d994aSBartlomiej Zolnierkiewicz val = get_emul_con_reg(data, val, temp); 739285d994aSBartlomiej Zolnierkiewicz writel(val, data->base + emul_con); 740285d994aSBartlomiej Zolnierkiewicz } 741285d994aSBartlomiej Zolnierkiewicz 74217e8351aSSascha Hauer static int exynos_tmu_set_emulation(void *drv_data, int temp) 74359dfa54cSAmit Daniel Kachhap { 74459dfa54cSAmit Daniel Kachhap struct exynos_tmu_data *data = drv_data; 74559dfa54cSAmit Daniel Kachhap int ret = -EINVAL; 74659dfa54cSAmit Daniel Kachhap 747ef3f80fcSBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS4210) 74859dfa54cSAmit Daniel Kachhap goto out; 74959dfa54cSAmit Daniel Kachhap 75059dfa54cSAmit Daniel Kachhap if (temp && temp < MCELSIUS) 75159dfa54cSAmit Daniel Kachhap goto out; 75259dfa54cSAmit Daniel Kachhap 75359dfa54cSAmit Daniel Kachhap mutex_lock(&data->lock); 75459dfa54cSAmit Daniel Kachhap clk_enable(data->clk); 755285d994aSBartlomiej Zolnierkiewicz data->tmu_set_emulation(data, temp); 75659dfa54cSAmit Daniel Kachhap clk_disable(data->clk); 75759dfa54cSAmit Daniel Kachhap mutex_unlock(&data->lock); 75859dfa54cSAmit Daniel Kachhap return 0; 75959dfa54cSAmit Daniel Kachhap out: 76059dfa54cSAmit Daniel Kachhap return ret; 76159dfa54cSAmit Daniel Kachhap } 76259dfa54cSAmit Daniel Kachhap #else 763285d994aSBartlomiej Zolnierkiewicz #define exynos4412_tmu_set_emulation NULL 76417e8351aSSascha Hauer static int exynos_tmu_set_emulation(void *drv_data, int temp) 76559dfa54cSAmit Daniel Kachhap { return -EINVAL; } 76659dfa54cSAmit Daniel Kachhap #endif /* CONFIG_THERMAL_EMULATION */ 76759dfa54cSAmit Daniel Kachhap 768b79985caSBartlomiej Zolnierkiewicz static int exynos4210_tmu_read(struct exynos_tmu_data *data) 769b79985caSBartlomiej Zolnierkiewicz { 770b79985caSBartlomiej Zolnierkiewicz int ret = readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP); 771b79985caSBartlomiej Zolnierkiewicz 772b79985caSBartlomiej Zolnierkiewicz /* "temp_code" should range between 75 and 175 */ 773b79985caSBartlomiej Zolnierkiewicz return (ret < 75 || ret > 175) ? -ENODATA : ret; 774b79985caSBartlomiej Zolnierkiewicz } 775b79985caSBartlomiej Zolnierkiewicz 776b79985caSBartlomiej Zolnierkiewicz static int exynos4412_tmu_read(struct exynos_tmu_data *data) 777b79985caSBartlomiej Zolnierkiewicz { 778b79985caSBartlomiej Zolnierkiewicz return readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP); 779b79985caSBartlomiej Zolnierkiewicz } 780b79985caSBartlomiej Zolnierkiewicz 7816c247393SAbhilash Kesavan static int exynos7_tmu_read(struct exynos_tmu_data *data) 7826c247393SAbhilash Kesavan { 7836c247393SAbhilash Kesavan return readw(data->base + EXYNOS_TMU_REG_CURRENT_TEMP) & 7846c247393SAbhilash Kesavan EXYNOS7_TMU_TEMP_MASK; 7856c247393SAbhilash Kesavan } 7866c247393SAbhilash Kesavan 78759dfa54cSAmit Daniel Kachhap static void exynos_tmu_work(struct work_struct *work) 78859dfa54cSAmit Daniel Kachhap { 78959dfa54cSAmit Daniel Kachhap struct exynos_tmu_data *data = container_of(work, 79059dfa54cSAmit Daniel Kachhap struct exynos_tmu_data, irq_work); 791a0395eeeSAmit Daniel Kachhap 79214a11dc7SNaveen Krishna Chatradhi if (!IS_ERR(data->clk_sec)) 79314a11dc7SNaveen Krishna Chatradhi clk_enable(data->clk_sec); 79414a11dc7SNaveen Krishna Chatradhi if (!IS_ERR(data->clk_sec)) 79514a11dc7SNaveen Krishna Chatradhi clk_disable(data->clk_sec); 79659dfa54cSAmit Daniel Kachhap 797b43e3cfeSBartlomiej Zolnierkiewicz thermal_zone_device_update(data->tzd, THERMAL_EVENT_UNSPECIFIED); 798b43e3cfeSBartlomiej Zolnierkiewicz 79959dfa54cSAmit Daniel Kachhap mutex_lock(&data->lock); 80059dfa54cSAmit Daniel Kachhap clk_enable(data->clk); 801b8d582b9SAmit Daniel Kachhap 802a4463c4fSAmit Daniel Kachhap /* TODO: take action based on particular interrupt */ 803a7331f72SBartlomiej Zolnierkiewicz data->tmu_clear_irqs(data); 804b8d582b9SAmit Daniel Kachhap 80559dfa54cSAmit Daniel Kachhap clk_disable(data->clk); 80659dfa54cSAmit Daniel Kachhap mutex_unlock(&data->lock); 80759dfa54cSAmit Daniel Kachhap enable_irq(data->irq); 80859dfa54cSAmit Daniel Kachhap } 80959dfa54cSAmit Daniel Kachhap 810a7331f72SBartlomiej Zolnierkiewicz static void exynos4210_tmu_clear_irqs(struct exynos_tmu_data *data) 811a7331f72SBartlomiej Zolnierkiewicz { 812a7331f72SBartlomiej Zolnierkiewicz unsigned int val_irq; 813a7331f72SBartlomiej Zolnierkiewicz u32 tmu_intstat, tmu_intclear; 814a7331f72SBartlomiej Zolnierkiewicz 815a7331f72SBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS5260) { 816a7331f72SBartlomiej Zolnierkiewicz tmu_intstat = EXYNOS5260_TMU_REG_INTSTAT; 817a7331f72SBartlomiej Zolnierkiewicz tmu_intclear = EXYNOS5260_TMU_REG_INTCLEAR; 8186c247393SAbhilash Kesavan } else if (data->soc == SOC_ARCH_EXYNOS7) { 8196c247393SAbhilash Kesavan tmu_intstat = EXYNOS7_TMU_REG_INTPEND; 8206c247393SAbhilash Kesavan tmu_intclear = EXYNOS7_TMU_REG_INTPEND; 821488c7455SChanwoo Choi } else if (data->soc == SOC_ARCH_EXYNOS5433) { 822488c7455SChanwoo Choi tmu_intstat = EXYNOS5433_TMU_REG_INTPEND; 823488c7455SChanwoo Choi tmu_intclear = EXYNOS5433_TMU_REG_INTPEND; 824a7331f72SBartlomiej Zolnierkiewicz } else { 825a7331f72SBartlomiej Zolnierkiewicz tmu_intstat = EXYNOS_TMU_REG_INTSTAT; 826a7331f72SBartlomiej Zolnierkiewicz tmu_intclear = EXYNOS_TMU_REG_INTCLEAR; 827a7331f72SBartlomiej Zolnierkiewicz } 828a7331f72SBartlomiej Zolnierkiewicz 829a7331f72SBartlomiej Zolnierkiewicz val_irq = readl(data->base + tmu_intstat); 830a7331f72SBartlomiej Zolnierkiewicz /* 831a7331f72SBartlomiej Zolnierkiewicz * Clear the interrupts. Please note that the documentation for 832a7331f72SBartlomiej Zolnierkiewicz * Exynos3250, Exynos4412, Exynos5250 and Exynos5260 incorrectly 833a7331f72SBartlomiej Zolnierkiewicz * states that INTCLEAR register has a different placing of bits 834a7331f72SBartlomiej Zolnierkiewicz * responsible for FALL IRQs than INTSTAT register. Exynos5420 835a7331f72SBartlomiej Zolnierkiewicz * and Exynos5440 documentation is correct (Exynos4210 doesn't 836a7331f72SBartlomiej Zolnierkiewicz * support FALL IRQs at all). 837a7331f72SBartlomiej Zolnierkiewicz */ 838a7331f72SBartlomiej Zolnierkiewicz writel(val_irq, data->base + tmu_intclear); 839a7331f72SBartlomiej Zolnierkiewicz } 840a7331f72SBartlomiej Zolnierkiewicz 84159dfa54cSAmit Daniel Kachhap static irqreturn_t exynos_tmu_irq(int irq, void *id) 84259dfa54cSAmit Daniel Kachhap { 84359dfa54cSAmit Daniel Kachhap struct exynos_tmu_data *data = id; 84459dfa54cSAmit Daniel Kachhap 84559dfa54cSAmit Daniel Kachhap disable_irq_nosync(irq); 84659dfa54cSAmit Daniel Kachhap schedule_work(&data->irq_work); 84759dfa54cSAmit Daniel Kachhap 84859dfa54cSAmit Daniel Kachhap return IRQ_HANDLED; 84959dfa54cSAmit Daniel Kachhap } 85059dfa54cSAmit Daniel Kachhap 85159dfa54cSAmit Daniel Kachhap static const struct of_device_id exynos_tmu_match[] = { 852fee88e2bSMaciej Purski { 853fee88e2bSMaciej Purski .compatible = "samsung,exynos3250-tmu", 854fee88e2bSMaciej Purski .data = (const void *)SOC_ARCH_EXYNOS3250, 855fee88e2bSMaciej Purski }, { 856fee88e2bSMaciej Purski .compatible = "samsung,exynos4210-tmu", 857fee88e2bSMaciej Purski .data = (const void *)SOC_ARCH_EXYNOS4210, 858fee88e2bSMaciej Purski }, { 859fee88e2bSMaciej Purski .compatible = "samsung,exynos4412-tmu", 860fee88e2bSMaciej Purski .data = (const void *)SOC_ARCH_EXYNOS4412, 861fee88e2bSMaciej Purski }, { 862fee88e2bSMaciej Purski .compatible = "samsung,exynos5250-tmu", 863fee88e2bSMaciej Purski .data = (const void *)SOC_ARCH_EXYNOS5250, 864fee88e2bSMaciej Purski }, { 865fee88e2bSMaciej Purski .compatible = "samsung,exynos5260-tmu", 866fee88e2bSMaciej Purski .data = (const void *)SOC_ARCH_EXYNOS5260, 867fee88e2bSMaciej Purski }, { 868fee88e2bSMaciej Purski .compatible = "samsung,exynos5420-tmu", 869fee88e2bSMaciej Purski .data = (const void *)SOC_ARCH_EXYNOS5420, 870fee88e2bSMaciej Purski }, { 871fee88e2bSMaciej Purski .compatible = "samsung,exynos5420-tmu-ext-triminfo", 872fee88e2bSMaciej Purski .data = (const void *)SOC_ARCH_EXYNOS5420_TRIMINFO, 873fee88e2bSMaciej Purski }, { 874fee88e2bSMaciej Purski .compatible = "samsung,exynos5433-tmu", 875fee88e2bSMaciej Purski .data = (const void *)SOC_ARCH_EXYNOS5433, 876fee88e2bSMaciej Purski }, { 877fee88e2bSMaciej Purski .compatible = "samsung,exynos7-tmu", 878fee88e2bSMaciej Purski .data = (const void *)SOC_ARCH_EXYNOS7, 879fee88e2bSMaciej Purski }, 880fee88e2bSMaciej Purski { }, 88159dfa54cSAmit Daniel Kachhap }; 88259dfa54cSAmit Daniel Kachhap MODULE_DEVICE_TABLE(of, exynos_tmu_match); 88359dfa54cSAmit Daniel Kachhap 884cebe7373SAmit Daniel Kachhap static int exynos_map_dt_data(struct platform_device *pdev) 88559dfa54cSAmit Daniel Kachhap { 886cebe7373SAmit Daniel Kachhap struct exynos_tmu_data *data = platform_get_drvdata(pdev); 887cebe7373SAmit Daniel Kachhap struct resource res; 88859dfa54cSAmit Daniel Kachhap 88973b5b1d7SSachin Kamat if (!data || !pdev->dev.of_node) 890cebe7373SAmit Daniel Kachhap return -ENODEV; 89159dfa54cSAmit Daniel Kachhap 892cebe7373SAmit Daniel Kachhap data->id = of_alias_get_id(pdev->dev.of_node, "tmuctrl"); 893cebe7373SAmit Daniel Kachhap if (data->id < 0) 894cebe7373SAmit Daniel Kachhap data->id = 0; 895cebe7373SAmit Daniel Kachhap 896cebe7373SAmit Daniel Kachhap data->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); 897cebe7373SAmit Daniel Kachhap if (data->irq <= 0) { 898cebe7373SAmit Daniel Kachhap dev_err(&pdev->dev, "failed to get IRQ\n"); 899cebe7373SAmit Daniel Kachhap return -ENODEV; 900cebe7373SAmit Daniel Kachhap } 901cebe7373SAmit Daniel Kachhap 902cebe7373SAmit Daniel Kachhap if (of_address_to_resource(pdev->dev.of_node, 0, &res)) { 903cebe7373SAmit Daniel Kachhap dev_err(&pdev->dev, "failed to get Resource 0\n"); 904cebe7373SAmit Daniel Kachhap return -ENODEV; 905cebe7373SAmit Daniel Kachhap } 906cebe7373SAmit Daniel Kachhap 907cebe7373SAmit Daniel Kachhap data->base = devm_ioremap(&pdev->dev, res.start, resource_size(&res)); 908cebe7373SAmit Daniel Kachhap if (!data->base) { 909cebe7373SAmit Daniel Kachhap dev_err(&pdev->dev, "Failed to ioremap memory\n"); 910cebe7373SAmit Daniel Kachhap return -EADDRNOTAVAIL; 911cebe7373SAmit Daniel Kachhap } 912cebe7373SAmit Daniel Kachhap 913fee88e2bSMaciej Purski data->soc = (enum soc_type)of_device_get_match_data(&pdev->dev); 91456adb9efSBartlomiej Zolnierkiewicz 91556adb9efSBartlomiej Zolnierkiewicz switch (data->soc) { 91656adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS4210: 917c8f8f768SBartlomiej Zolnierkiewicz data->tmu_set_trip_temp = exynos4210_tmu_set_trip_temp; 918c8f8f768SBartlomiej Zolnierkiewicz data->tmu_set_trip_hyst = exynos4210_tmu_set_trip_hyst; 91956adb9efSBartlomiej Zolnierkiewicz data->tmu_initialize = exynos4210_tmu_initialize; 92056adb9efSBartlomiej Zolnierkiewicz data->tmu_control = exynos4210_tmu_control; 92156adb9efSBartlomiej Zolnierkiewicz data->tmu_read = exynos4210_tmu_read; 92256adb9efSBartlomiej Zolnierkiewicz data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; 9233a3a5f15SKrzysztof Kozlowski data->ntrip = 4; 924fccfe099SBartlomiej Zolnierkiewicz data->gain = 15; 92561020d18SBartlomiej Zolnierkiewicz data->reference_voltage = 7; 926e3ed3649SBartlomiej Zolnierkiewicz data->efuse_value = 55; 927e3ed3649SBartlomiej Zolnierkiewicz data->min_efuse_value = 40; 928e3ed3649SBartlomiej Zolnierkiewicz data->max_efuse_value = 100; 92956adb9efSBartlomiej Zolnierkiewicz break; 93056adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS3250: 93156adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS4412: 93256adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS5250: 93356adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS5260: 93456adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS5420: 93556adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS5420_TRIMINFO: 936c8f8f768SBartlomiej Zolnierkiewicz data->tmu_set_trip_temp = exynos4412_tmu_set_trip_temp; 937c8f8f768SBartlomiej Zolnierkiewicz data->tmu_set_trip_hyst = exynos4412_tmu_set_trip_hyst; 93856adb9efSBartlomiej Zolnierkiewicz data->tmu_initialize = exynos4412_tmu_initialize; 93956adb9efSBartlomiej Zolnierkiewicz data->tmu_control = exynos4210_tmu_control; 94056adb9efSBartlomiej Zolnierkiewicz data->tmu_read = exynos4412_tmu_read; 94156adb9efSBartlomiej Zolnierkiewicz data->tmu_set_emulation = exynos4412_tmu_set_emulation; 94256adb9efSBartlomiej Zolnierkiewicz data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; 9433a3a5f15SKrzysztof Kozlowski data->ntrip = 4; 944fccfe099SBartlomiej Zolnierkiewicz data->gain = 8; 94561020d18SBartlomiej Zolnierkiewicz data->reference_voltage = 16; 946e3ed3649SBartlomiej Zolnierkiewicz data->efuse_value = 55; 947e3ed3649SBartlomiej Zolnierkiewicz if (data->soc != SOC_ARCH_EXYNOS5420 && 948e3ed3649SBartlomiej Zolnierkiewicz data->soc != SOC_ARCH_EXYNOS5420_TRIMINFO) 949e3ed3649SBartlomiej Zolnierkiewicz data->min_efuse_value = 40; 950e3ed3649SBartlomiej Zolnierkiewicz else 951e3ed3649SBartlomiej Zolnierkiewicz data->min_efuse_value = 0; 952e3ed3649SBartlomiej Zolnierkiewicz data->max_efuse_value = 100; 95356adb9efSBartlomiej Zolnierkiewicz break; 954488c7455SChanwoo Choi case SOC_ARCH_EXYNOS5433: 955c8f8f768SBartlomiej Zolnierkiewicz data->tmu_set_trip_temp = exynos5433_tmu_set_trip_temp; 956c8f8f768SBartlomiej Zolnierkiewicz data->tmu_set_trip_hyst = exynos5433_tmu_set_trip_hyst; 957488c7455SChanwoo Choi data->tmu_initialize = exynos5433_tmu_initialize; 958488c7455SChanwoo Choi data->tmu_control = exynos5433_tmu_control; 959488c7455SChanwoo Choi data->tmu_read = exynos4412_tmu_read; 960488c7455SChanwoo Choi data->tmu_set_emulation = exynos4412_tmu_set_emulation; 961488c7455SChanwoo Choi data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; 9623a3a5f15SKrzysztof Kozlowski data->ntrip = 8; 963fccfe099SBartlomiej Zolnierkiewicz data->gain = 8; 96461020d18SBartlomiej Zolnierkiewicz if (res.start == EXYNOS5433_G3D_BASE) 96561020d18SBartlomiej Zolnierkiewicz data->reference_voltage = 23; 96661020d18SBartlomiej Zolnierkiewicz else 96761020d18SBartlomiej Zolnierkiewicz data->reference_voltage = 16; 968e3ed3649SBartlomiej Zolnierkiewicz data->efuse_value = 75; 969e3ed3649SBartlomiej Zolnierkiewicz data->min_efuse_value = 40; 970e3ed3649SBartlomiej Zolnierkiewicz data->max_efuse_value = 150; 971488c7455SChanwoo Choi break; 9726c247393SAbhilash Kesavan case SOC_ARCH_EXYNOS7: 973c8f8f768SBartlomiej Zolnierkiewicz data->tmu_set_trip_temp = exynos7_tmu_set_trip_temp; 974c8f8f768SBartlomiej Zolnierkiewicz data->tmu_set_trip_hyst = exynos7_tmu_set_trip_hyst; 9756c247393SAbhilash Kesavan data->tmu_initialize = exynos7_tmu_initialize; 9766c247393SAbhilash Kesavan data->tmu_control = exynos7_tmu_control; 9776c247393SAbhilash Kesavan data->tmu_read = exynos7_tmu_read; 9786c247393SAbhilash Kesavan data->tmu_set_emulation = exynos4412_tmu_set_emulation; 9796c247393SAbhilash Kesavan data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; 9803a3a5f15SKrzysztof Kozlowski data->ntrip = 8; 981fccfe099SBartlomiej Zolnierkiewicz data->gain = 9; 98261020d18SBartlomiej Zolnierkiewicz data->reference_voltage = 17; 983e3ed3649SBartlomiej Zolnierkiewicz data->efuse_value = 75; 984e3ed3649SBartlomiej Zolnierkiewicz data->min_efuse_value = 15; 985e3ed3649SBartlomiej Zolnierkiewicz data->max_efuse_value = 100; 9866c247393SAbhilash Kesavan break; 98756adb9efSBartlomiej Zolnierkiewicz default: 98856adb9efSBartlomiej Zolnierkiewicz dev_err(&pdev->dev, "Platform not supported\n"); 98956adb9efSBartlomiej Zolnierkiewicz return -EINVAL; 99056adb9efSBartlomiej Zolnierkiewicz } 99156adb9efSBartlomiej Zolnierkiewicz 992199b3e3cSBartlomiej Zolnierkiewicz data->cal_type = TYPE_ONE_POINT_TRIMMING; 993199b3e3cSBartlomiej Zolnierkiewicz 994d9b6ee14SAmit Daniel Kachhap /* 995d9b6ee14SAmit Daniel Kachhap * Check if the TMU shares some registers and then try to map the 996d9b6ee14SAmit Daniel Kachhap * memory of common registers. 997d9b6ee14SAmit Daniel Kachhap */ 9988014220dSKrzysztof Kozlowski if (data->soc != SOC_ARCH_EXYNOS5420_TRIMINFO) 999d9b6ee14SAmit Daniel Kachhap return 0; 1000d9b6ee14SAmit Daniel Kachhap 1001d9b6ee14SAmit Daniel Kachhap if (of_address_to_resource(pdev->dev.of_node, 1, &res)) { 1002d9b6ee14SAmit Daniel Kachhap dev_err(&pdev->dev, "failed to get Resource 1\n"); 1003d9b6ee14SAmit Daniel Kachhap return -ENODEV; 1004d9b6ee14SAmit Daniel Kachhap } 1005d9b6ee14SAmit Daniel Kachhap 10069025d563SNaveen Krishna Chatradhi data->base_second = devm_ioremap(&pdev->dev, res.start, 1007d9b6ee14SAmit Daniel Kachhap resource_size(&res)); 10089025d563SNaveen Krishna Chatradhi if (!data->base_second) { 1009d9b6ee14SAmit Daniel Kachhap dev_err(&pdev->dev, "Failed to ioremap memory\n"); 1010d9b6ee14SAmit Daniel Kachhap return -ENOMEM; 1011d9b6ee14SAmit Daniel Kachhap } 1012cebe7373SAmit Daniel Kachhap 1013cebe7373SAmit Daniel Kachhap return 0; 1014cebe7373SAmit Daniel Kachhap } 1015cebe7373SAmit Daniel Kachhap 1016c3c04d9dSJulia Lawall static const struct thermal_zone_of_device_ops exynos_sensor_ops = { 10173b6a1a80SLukasz Majewski .get_temp = exynos_get_temp, 10183b6a1a80SLukasz Majewski .set_emul_temp = exynos_tmu_set_emulation, 10193b6a1a80SLukasz Majewski }; 10203b6a1a80SLukasz Majewski 1021cebe7373SAmit Daniel Kachhap static int exynos_tmu_probe(struct platform_device *pdev) 1022cebe7373SAmit Daniel Kachhap { 10233b6a1a80SLukasz Majewski struct exynos_tmu_data *data; 10243b6a1a80SLukasz Majewski int ret; 1025cebe7373SAmit Daniel Kachhap 102659dfa54cSAmit Daniel Kachhap data = devm_kzalloc(&pdev->dev, sizeof(struct exynos_tmu_data), 102759dfa54cSAmit Daniel Kachhap GFP_KERNEL); 10282a9675b3SJingoo Han if (!data) 102959dfa54cSAmit Daniel Kachhap return -ENOMEM; 103059dfa54cSAmit Daniel Kachhap 1031cebe7373SAmit Daniel Kachhap platform_set_drvdata(pdev, data); 1032cebe7373SAmit Daniel Kachhap mutex_init(&data->lock); 1033cebe7373SAmit Daniel Kachhap 1034824ead03SKrzysztof Kozlowski /* 1035824ead03SKrzysztof Kozlowski * Try enabling the regulator if found 1036824ead03SKrzysztof Kozlowski * TODO: Add regulator as an SOC feature, so that regulator enable 1037824ead03SKrzysztof Kozlowski * is a compulsory call. 1038824ead03SKrzysztof Kozlowski */ 10394d3583cdSJavier Martinez Canillas data->regulator = devm_regulator_get_optional(&pdev->dev, "vtmu"); 1040824ead03SKrzysztof Kozlowski if (!IS_ERR(data->regulator)) { 1041824ead03SKrzysztof Kozlowski ret = regulator_enable(data->regulator); 1042824ead03SKrzysztof Kozlowski if (ret) { 1043824ead03SKrzysztof Kozlowski dev_err(&pdev->dev, "failed to enable vtmu\n"); 1044824ead03SKrzysztof Kozlowski return ret; 10453b6a1a80SLukasz Majewski } 1046824ead03SKrzysztof Kozlowski } else { 1047ccb361d2SJavier Martinez Canillas if (PTR_ERR(data->regulator) == -EPROBE_DEFER) 1048ccb361d2SJavier Martinez Canillas return -EPROBE_DEFER; 1049824ead03SKrzysztof Kozlowski dev_info(&pdev->dev, "Regulator node (vtmu) not found\n"); 1050824ead03SKrzysztof Kozlowski } 1051824ead03SKrzysztof Kozlowski 1052cebe7373SAmit Daniel Kachhap ret = exynos_map_dt_data(pdev); 1053cebe7373SAmit Daniel Kachhap if (ret) 10543b6a1a80SLukasz Majewski goto err_sensor; 1055cebe7373SAmit Daniel Kachhap 105659dfa54cSAmit Daniel Kachhap INIT_WORK(&data->irq_work, exynos_tmu_work); 105759dfa54cSAmit Daniel Kachhap 105859dfa54cSAmit Daniel Kachhap data->clk = devm_clk_get(&pdev->dev, "tmu_apbif"); 105959dfa54cSAmit Daniel Kachhap if (IS_ERR(data->clk)) { 106059dfa54cSAmit Daniel Kachhap dev_err(&pdev->dev, "Failed to get clock\n"); 10613b6a1a80SLukasz Majewski ret = PTR_ERR(data->clk); 10623b6a1a80SLukasz Majewski goto err_sensor; 106359dfa54cSAmit Daniel Kachhap } 106459dfa54cSAmit Daniel Kachhap 106514a11dc7SNaveen Krishna Chatradhi data->clk_sec = devm_clk_get(&pdev->dev, "tmu_triminfo_apbif"); 106614a11dc7SNaveen Krishna Chatradhi if (IS_ERR(data->clk_sec)) { 106714a11dc7SNaveen Krishna Chatradhi if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) { 106814a11dc7SNaveen Krishna Chatradhi dev_err(&pdev->dev, "Failed to get triminfo clock\n"); 10693b6a1a80SLukasz Majewski ret = PTR_ERR(data->clk_sec); 10703b6a1a80SLukasz Majewski goto err_sensor; 107114a11dc7SNaveen Krishna Chatradhi } 107214a11dc7SNaveen Krishna Chatradhi } else { 107314a11dc7SNaveen Krishna Chatradhi ret = clk_prepare(data->clk_sec); 107414a11dc7SNaveen Krishna Chatradhi if (ret) { 107514a11dc7SNaveen Krishna Chatradhi dev_err(&pdev->dev, "Failed to get clock\n"); 10763b6a1a80SLukasz Majewski goto err_sensor; 107714a11dc7SNaveen Krishna Chatradhi } 107814a11dc7SNaveen Krishna Chatradhi } 107914a11dc7SNaveen Krishna Chatradhi 108014a11dc7SNaveen Krishna Chatradhi ret = clk_prepare(data->clk); 108114a11dc7SNaveen Krishna Chatradhi if (ret) { 108214a11dc7SNaveen Krishna Chatradhi dev_err(&pdev->dev, "Failed to get clock\n"); 108314a11dc7SNaveen Krishna Chatradhi goto err_clk_sec; 108414a11dc7SNaveen Krishna Chatradhi } 108559dfa54cSAmit Daniel Kachhap 1086488c7455SChanwoo Choi switch (data->soc) { 1087488c7455SChanwoo Choi case SOC_ARCH_EXYNOS5433: 1088488c7455SChanwoo Choi case SOC_ARCH_EXYNOS7: 10896c247393SAbhilash Kesavan data->sclk = devm_clk_get(&pdev->dev, "tmu_sclk"); 10906c247393SAbhilash Kesavan if (IS_ERR(data->sclk)) { 10916c247393SAbhilash Kesavan dev_err(&pdev->dev, "Failed to get sclk\n"); 10926c247393SAbhilash Kesavan goto err_clk; 10936c247393SAbhilash Kesavan } else { 10946c247393SAbhilash Kesavan ret = clk_prepare_enable(data->sclk); 10956c247393SAbhilash Kesavan if (ret) { 10966c247393SAbhilash Kesavan dev_err(&pdev->dev, "Failed to enable sclk\n"); 10976c247393SAbhilash Kesavan goto err_clk; 10986c247393SAbhilash Kesavan } 10996c247393SAbhilash Kesavan } 1100488c7455SChanwoo Choi break; 1101488c7455SChanwoo Choi default: 1102488c7455SChanwoo Choi break; 1103baba1ebbSKrzysztof Kozlowski } 11046c247393SAbhilash Kesavan 11059e4249b4SKrzysztof Kozlowski /* 11069e4249b4SKrzysztof Kozlowski * data->tzd must be registered before calling exynos_tmu_initialize(), 11079e4249b4SKrzysztof Kozlowski * requesting irq and calling exynos_tmu_control(). 11089e4249b4SKrzysztof Kozlowski */ 11099e4249b4SKrzysztof Kozlowski data->tzd = thermal_zone_of_sensor_register(&pdev->dev, 0, data, 11109e4249b4SKrzysztof Kozlowski &exynos_sensor_ops); 11119e4249b4SKrzysztof Kozlowski if (IS_ERR(data->tzd)) { 11129e4249b4SKrzysztof Kozlowski ret = PTR_ERR(data->tzd); 11139e4249b4SKrzysztof Kozlowski dev_err(&pdev->dev, "Failed to register sensor: %d\n", ret); 11149e4249b4SKrzysztof Kozlowski goto err_sclk; 11159e4249b4SKrzysztof Kozlowski } 111659dfa54cSAmit Daniel Kachhap 111759dfa54cSAmit Daniel Kachhap ret = exynos_tmu_initialize(pdev); 111859dfa54cSAmit Daniel Kachhap if (ret) { 111959dfa54cSAmit Daniel Kachhap dev_err(&pdev->dev, "Failed to initialize TMU\n"); 11209e4249b4SKrzysztof Kozlowski goto err_thermal; 112159dfa54cSAmit Daniel Kachhap } 112259dfa54cSAmit Daniel Kachhap 1123cebe7373SAmit Daniel Kachhap ret = devm_request_irq(&pdev->dev, data->irq, exynos_tmu_irq, 1124cebe7373SAmit Daniel Kachhap IRQF_TRIGGER_RISING | IRQF_SHARED, dev_name(&pdev->dev), data); 1125cebe7373SAmit Daniel Kachhap if (ret) { 1126cebe7373SAmit Daniel Kachhap dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq); 11279e4249b4SKrzysztof Kozlowski goto err_thermal; 1128cebe7373SAmit Daniel Kachhap } 112959dfa54cSAmit Daniel Kachhap 11303b6a1a80SLukasz Majewski exynos_tmu_control(pdev, true); 113159dfa54cSAmit Daniel Kachhap return 0; 11329e4249b4SKrzysztof Kozlowski 11339e4249b4SKrzysztof Kozlowski err_thermal: 11349e4249b4SKrzysztof Kozlowski thermal_zone_of_sensor_unregister(&pdev->dev, data->tzd); 11356c247393SAbhilash Kesavan err_sclk: 11366c247393SAbhilash Kesavan clk_disable_unprepare(data->sclk); 113759dfa54cSAmit Daniel Kachhap err_clk: 113859dfa54cSAmit Daniel Kachhap clk_unprepare(data->clk); 113914a11dc7SNaveen Krishna Chatradhi err_clk_sec: 114014a11dc7SNaveen Krishna Chatradhi if (!IS_ERR(data->clk_sec)) 114114a11dc7SNaveen Krishna Chatradhi clk_unprepare(data->clk_sec); 11423b6a1a80SLukasz Majewski err_sensor: 1143bfa26838SKrzysztof Kozlowski if (!IS_ERR(data->regulator)) 11445f09a5cbSKrzysztof Kozlowski regulator_disable(data->regulator); 11453b6a1a80SLukasz Majewski 114659dfa54cSAmit Daniel Kachhap return ret; 114759dfa54cSAmit Daniel Kachhap } 114859dfa54cSAmit Daniel Kachhap 114959dfa54cSAmit Daniel Kachhap static int exynos_tmu_remove(struct platform_device *pdev) 115059dfa54cSAmit Daniel Kachhap { 115159dfa54cSAmit Daniel Kachhap struct exynos_tmu_data *data = platform_get_drvdata(pdev); 11523b6a1a80SLukasz Majewski struct thermal_zone_device *tzd = data->tzd; 115359dfa54cSAmit Daniel Kachhap 11543b6a1a80SLukasz Majewski thermal_zone_of_sensor_unregister(&pdev->dev, tzd); 11554215688eSBartlomiej Zolnierkiewicz exynos_tmu_control(pdev, false); 11564215688eSBartlomiej Zolnierkiewicz 11576c247393SAbhilash Kesavan clk_disable_unprepare(data->sclk); 115859dfa54cSAmit Daniel Kachhap clk_unprepare(data->clk); 115914a11dc7SNaveen Krishna Chatradhi if (!IS_ERR(data->clk_sec)) 116014a11dc7SNaveen Krishna Chatradhi clk_unprepare(data->clk_sec); 116159dfa54cSAmit Daniel Kachhap 1162498d22f6SAmit Daniel Kachhap if (!IS_ERR(data->regulator)) 1163498d22f6SAmit Daniel Kachhap regulator_disable(data->regulator); 1164498d22f6SAmit Daniel Kachhap 116559dfa54cSAmit Daniel Kachhap return 0; 116659dfa54cSAmit Daniel Kachhap } 116759dfa54cSAmit Daniel Kachhap 116859dfa54cSAmit Daniel Kachhap #ifdef CONFIG_PM_SLEEP 116959dfa54cSAmit Daniel Kachhap static int exynos_tmu_suspend(struct device *dev) 117059dfa54cSAmit Daniel Kachhap { 117159dfa54cSAmit Daniel Kachhap exynos_tmu_control(to_platform_device(dev), false); 117259dfa54cSAmit Daniel Kachhap 117359dfa54cSAmit Daniel Kachhap return 0; 117459dfa54cSAmit Daniel Kachhap } 117559dfa54cSAmit Daniel Kachhap 117659dfa54cSAmit Daniel Kachhap static int exynos_tmu_resume(struct device *dev) 117759dfa54cSAmit Daniel Kachhap { 117859dfa54cSAmit Daniel Kachhap struct platform_device *pdev = to_platform_device(dev); 117959dfa54cSAmit Daniel Kachhap 118059dfa54cSAmit Daniel Kachhap exynos_tmu_initialize(pdev); 118159dfa54cSAmit Daniel Kachhap exynos_tmu_control(pdev, true); 118259dfa54cSAmit Daniel Kachhap 118359dfa54cSAmit Daniel Kachhap return 0; 118459dfa54cSAmit Daniel Kachhap } 118559dfa54cSAmit Daniel Kachhap 118659dfa54cSAmit Daniel Kachhap static SIMPLE_DEV_PM_OPS(exynos_tmu_pm, 118759dfa54cSAmit Daniel Kachhap exynos_tmu_suspend, exynos_tmu_resume); 118859dfa54cSAmit Daniel Kachhap #define EXYNOS_TMU_PM (&exynos_tmu_pm) 118959dfa54cSAmit Daniel Kachhap #else 119059dfa54cSAmit Daniel Kachhap #define EXYNOS_TMU_PM NULL 119159dfa54cSAmit Daniel Kachhap #endif 119259dfa54cSAmit Daniel Kachhap 119359dfa54cSAmit Daniel Kachhap static struct platform_driver exynos_tmu_driver = { 119459dfa54cSAmit Daniel Kachhap .driver = { 119559dfa54cSAmit Daniel Kachhap .name = "exynos-tmu", 119659dfa54cSAmit Daniel Kachhap .pm = EXYNOS_TMU_PM, 119773b5b1d7SSachin Kamat .of_match_table = exynos_tmu_match, 119859dfa54cSAmit Daniel Kachhap }, 119959dfa54cSAmit Daniel Kachhap .probe = exynos_tmu_probe, 120059dfa54cSAmit Daniel Kachhap .remove = exynos_tmu_remove, 120159dfa54cSAmit Daniel Kachhap }; 120259dfa54cSAmit Daniel Kachhap 120359dfa54cSAmit Daniel Kachhap module_platform_driver(exynos_tmu_driver); 120459dfa54cSAmit Daniel Kachhap 120559dfa54cSAmit Daniel Kachhap MODULE_DESCRIPTION("EXYNOS TMU Driver"); 120659dfa54cSAmit Daniel Kachhap MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>"); 120759dfa54cSAmit Daniel Kachhap MODULE_LICENSE("GPL"); 120859dfa54cSAmit Daniel Kachhap MODULE_ALIAS("platform:exynos-tmu"); 1209