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_RISE1_SHIFT 4 802845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_INTEN_RISE2_SHIFT 8 812845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_INTEN_RISE3_SHIFT 12 822845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_INTEN_FALL0_SHIFT 16 832845f6ecSBartlomiej Zolnierkiewicz 842845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_EMUL_TIME 0x57F0 852845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_EMUL_TIME_MASK 0xffff 862845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_EMUL_TIME_SHIFT 16 872845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_EMUL_DATA_SHIFT 8 882845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_EMUL_DATA_MASK 0xFF 892845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_EMUL_ENABLE 0x1 902845f6ecSBartlomiej Zolnierkiewicz 912845f6ecSBartlomiej Zolnierkiewicz /* Exynos5260 specific */ 922845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5260_TMU_REG_INTEN 0xC0 932845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5260_TMU_REG_INTSTAT 0xC4 942845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5260_TMU_REG_INTCLEAR 0xC8 952845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5260_EMUL_CON 0x100 962845f6ecSBartlomiej Zolnierkiewicz 972845f6ecSBartlomiej Zolnierkiewicz /* Exynos4412 specific */ 982845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS4412_MUX_ADDR_VALUE 6 992845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS4412_MUX_ADDR_SHIFT 20 1002845f6ecSBartlomiej Zolnierkiewicz 101488c7455SChanwoo Choi /* Exynos5433 specific registers */ 102488c7455SChanwoo Choi #define EXYNOS5433_TMU_REG_CONTROL1 0x024 103488c7455SChanwoo Choi #define EXYNOS5433_TMU_SAMPLING_INTERVAL 0x02c 104488c7455SChanwoo Choi #define EXYNOS5433_TMU_COUNTER_VALUE0 0x030 105488c7455SChanwoo Choi #define EXYNOS5433_TMU_COUNTER_VALUE1 0x034 106488c7455SChanwoo Choi #define EXYNOS5433_TMU_REG_CURRENT_TEMP1 0x044 107488c7455SChanwoo Choi #define EXYNOS5433_THD_TEMP_RISE3_0 0x050 108488c7455SChanwoo Choi #define EXYNOS5433_THD_TEMP_RISE7_4 0x054 109488c7455SChanwoo Choi #define EXYNOS5433_THD_TEMP_FALL3_0 0x060 110488c7455SChanwoo Choi #define EXYNOS5433_THD_TEMP_FALL7_4 0x064 111488c7455SChanwoo Choi #define EXYNOS5433_TMU_REG_INTEN 0x0c0 112488c7455SChanwoo Choi #define EXYNOS5433_TMU_REG_INTPEND 0x0c8 113488c7455SChanwoo Choi #define EXYNOS5433_TMU_EMUL_CON 0x110 114488c7455SChanwoo Choi #define EXYNOS5433_TMU_PD_DET_EN 0x130 115488c7455SChanwoo Choi 116488c7455SChanwoo Choi #define EXYNOS5433_TRIMINFO_SENSOR_ID_SHIFT 16 117488c7455SChanwoo Choi #define EXYNOS5433_TRIMINFO_CALIB_SEL_SHIFT 23 118488c7455SChanwoo Choi #define EXYNOS5433_TRIMINFO_SENSOR_ID_MASK \ 119488c7455SChanwoo Choi (0xf << EXYNOS5433_TRIMINFO_SENSOR_ID_SHIFT) 120488c7455SChanwoo Choi #define EXYNOS5433_TRIMINFO_CALIB_SEL_MASK BIT(23) 121488c7455SChanwoo Choi 122488c7455SChanwoo Choi #define EXYNOS5433_TRIMINFO_ONE_POINT_TRIMMING 0 123488c7455SChanwoo Choi #define EXYNOS5433_TRIMINFO_TWO_POINT_TRIMMING 1 124488c7455SChanwoo Choi 125488c7455SChanwoo Choi #define EXYNOS5433_PD_DET_EN 1 126488c7455SChanwoo Choi 12761020d18SBartlomiej Zolnierkiewicz #define EXYNOS5433_G3D_BASE 0x10070000 12861020d18SBartlomiej Zolnierkiewicz 1296c247393SAbhilash Kesavan /* Exynos7 specific registers */ 1306c247393SAbhilash Kesavan #define EXYNOS7_THD_TEMP_RISE7_6 0x50 1316c247393SAbhilash Kesavan #define EXYNOS7_THD_TEMP_FALL7_6 0x60 1326c247393SAbhilash Kesavan #define EXYNOS7_TMU_REG_INTEN 0x110 1336c247393SAbhilash Kesavan #define EXYNOS7_TMU_REG_INTPEND 0x118 1346c247393SAbhilash Kesavan #define EXYNOS7_TMU_REG_EMUL_CON 0x160 1356c247393SAbhilash Kesavan 1366c247393SAbhilash Kesavan #define EXYNOS7_TMU_TEMP_MASK 0x1ff 1376c247393SAbhilash Kesavan #define EXYNOS7_PD_DET_EN_SHIFT 23 1386c247393SAbhilash Kesavan #define EXYNOS7_TMU_INTEN_RISE0_SHIFT 0 1396c247393SAbhilash Kesavan #define EXYNOS7_TMU_INTEN_RISE1_SHIFT 1 1406c247393SAbhilash Kesavan #define EXYNOS7_TMU_INTEN_RISE2_SHIFT 2 1416c247393SAbhilash Kesavan #define EXYNOS7_TMU_INTEN_RISE3_SHIFT 3 1426c247393SAbhilash Kesavan #define EXYNOS7_TMU_INTEN_RISE4_SHIFT 4 1436c247393SAbhilash Kesavan #define EXYNOS7_TMU_INTEN_RISE5_SHIFT 5 1446c247393SAbhilash Kesavan #define EXYNOS7_TMU_INTEN_RISE6_SHIFT 6 1456c247393SAbhilash Kesavan #define EXYNOS7_TMU_INTEN_RISE7_SHIFT 7 1466c247393SAbhilash Kesavan #define EXYNOS7_EMUL_DATA_SHIFT 7 1476c247393SAbhilash Kesavan #define EXYNOS7_EMUL_DATA_MASK 0x1ff 1486c247393SAbhilash Kesavan 149718b4ca1SBartlomiej Zolnierkiewicz #define EXYNOS_FIRST_POINT_TRIM 25 150718b4ca1SBartlomiej Zolnierkiewicz #define EXYNOS_SECOND_POINT_TRIM 85 151718b4ca1SBartlomiej Zolnierkiewicz 15209d29426SBartlomiej Zolnierkiewicz #define EXYNOS_NOISE_CANCEL_MODE 4 15309d29426SBartlomiej Zolnierkiewicz 1543b6a1a80SLukasz Majewski #define MCELSIUS 1000 1557efd18a2SBartlomiej Zolnierkiewicz 1567efd18a2SBartlomiej Zolnierkiewicz enum soc_type { 1577efd18a2SBartlomiej Zolnierkiewicz SOC_ARCH_EXYNOS3250 = 1, 1587efd18a2SBartlomiej Zolnierkiewicz SOC_ARCH_EXYNOS4210, 1597efd18a2SBartlomiej Zolnierkiewicz SOC_ARCH_EXYNOS4412, 1607efd18a2SBartlomiej Zolnierkiewicz SOC_ARCH_EXYNOS5250, 1617efd18a2SBartlomiej Zolnierkiewicz SOC_ARCH_EXYNOS5260, 1627efd18a2SBartlomiej Zolnierkiewicz SOC_ARCH_EXYNOS5420, 1637efd18a2SBartlomiej Zolnierkiewicz SOC_ARCH_EXYNOS5420_TRIMINFO, 1647efd18a2SBartlomiej Zolnierkiewicz SOC_ARCH_EXYNOS5433, 1657efd18a2SBartlomiej Zolnierkiewicz SOC_ARCH_EXYNOS7, 1667efd18a2SBartlomiej Zolnierkiewicz }; 1677efd18a2SBartlomiej Zolnierkiewicz 168cebe7373SAmit Daniel Kachhap /** 169cebe7373SAmit Daniel Kachhap * struct exynos_tmu_data : A structure to hold the private data of the TMU 170cebe7373SAmit Daniel Kachhap driver 171cebe7373SAmit Daniel Kachhap * @id: identifier of the one instance of the TMU controller. 172cebe7373SAmit Daniel Kachhap * @base: base address of the single instance of the TMU controller. 1739025d563SNaveen Krishna Chatradhi * @base_second: base address of the common registers of the TMU controller. 174cebe7373SAmit Daniel Kachhap * @irq: irq number of the TMU controller. 175cebe7373SAmit Daniel Kachhap * @soc: id of the SOC type. 176cebe7373SAmit Daniel Kachhap * @irq_work: pointer to the irq work structure. 177cebe7373SAmit Daniel Kachhap * @lock: lock to implement synchronization. 178cebe7373SAmit Daniel Kachhap * @clk: pointer to the clock structure. 17914a11dc7SNaveen Krishna Chatradhi * @clk_sec: pointer to the clock structure for accessing the base_second. 1806c247393SAbhilash Kesavan * @sclk: pointer to the clock structure for accessing the tmu special clk. 181199b3e3cSBartlomiej Zolnierkiewicz * @cal_type: calibration type for temperature 182e3ed3649SBartlomiej Zolnierkiewicz * @efuse_value: SoC defined fuse value 183e3ed3649SBartlomiej Zolnierkiewicz * @min_efuse_value: minimum valid trimming data 184e3ed3649SBartlomiej Zolnierkiewicz * @max_efuse_value: maximum valid trimming data 185cebe7373SAmit Daniel Kachhap * @temp_error1: fused value of the first point trim. 186cebe7373SAmit Daniel Kachhap * @temp_error2: fused value of the second point trim. 187fccfe099SBartlomiej Zolnierkiewicz * @gain: gain of amplifier in the positive-TC generator block 188fccfe099SBartlomiej Zolnierkiewicz * 0 < gain <= 15 18961020d18SBartlomiej Zolnierkiewicz * @reference_voltage: reference voltage of amplifier 19061020d18SBartlomiej Zolnierkiewicz * in the positive-TC generator block 19161020d18SBartlomiej Zolnierkiewicz * 0 < reference_voltage <= 31 192498d22f6SAmit Daniel Kachhap * @regulator: pointer to the TMU regulator structure. 193cebe7373SAmit Daniel Kachhap * @reg_conf: pointer to structure to register with core thermal. 1943a3a5f15SKrzysztof Kozlowski * @ntrip: number of supported trip points. 1950eb875d8SMarek Szyprowski * @enabled: current status of TMU device 19672d1100bSBartlomiej Zolnierkiewicz * @tmu_initialize: SoC specific TMU initialization method 19737f9034fSBartlomiej Zolnierkiewicz * @tmu_control: SoC specific TMU control method 198b79985caSBartlomiej Zolnierkiewicz * @tmu_read: SoC specific TMU temperature read method 199285d994aSBartlomiej Zolnierkiewicz * @tmu_set_emulation: SoC specific TMU emulation setting method 200a7331f72SBartlomiej Zolnierkiewicz * @tmu_clear_irqs: SoC specific TMU interrupts clearing method 201cebe7373SAmit Daniel Kachhap */ 20259dfa54cSAmit Daniel Kachhap struct exynos_tmu_data { 203cebe7373SAmit Daniel Kachhap int id; 20459dfa54cSAmit Daniel Kachhap void __iomem *base; 2059025d563SNaveen Krishna Chatradhi void __iomem *base_second; 20659dfa54cSAmit Daniel Kachhap int irq; 20759dfa54cSAmit Daniel Kachhap enum soc_type soc; 20859dfa54cSAmit Daniel Kachhap struct work_struct irq_work; 20959dfa54cSAmit Daniel Kachhap struct mutex lock; 2106c247393SAbhilash Kesavan struct clk *clk, *clk_sec, *sclk; 211199b3e3cSBartlomiej Zolnierkiewicz u32 cal_type; 212e3ed3649SBartlomiej Zolnierkiewicz u32 efuse_value; 213e3ed3649SBartlomiej Zolnierkiewicz u32 min_efuse_value; 214e3ed3649SBartlomiej Zolnierkiewicz u32 max_efuse_value; 2156c247393SAbhilash Kesavan u16 temp_error1, temp_error2; 216fccfe099SBartlomiej Zolnierkiewicz u8 gain; 21761020d18SBartlomiej Zolnierkiewicz u8 reference_voltage; 218498d22f6SAmit Daniel Kachhap struct regulator *regulator; 2193b6a1a80SLukasz Majewski struct thermal_zone_device *tzd; 2203a3a5f15SKrzysztof Kozlowski unsigned int ntrip; 2210eb875d8SMarek Szyprowski bool enabled; 2223b6a1a80SLukasz Majewski 22372d1100bSBartlomiej Zolnierkiewicz int (*tmu_initialize)(struct platform_device *pdev); 22437f9034fSBartlomiej Zolnierkiewicz void (*tmu_control)(struct platform_device *pdev, bool on); 225b79985caSBartlomiej Zolnierkiewicz int (*tmu_read)(struct exynos_tmu_data *data); 22617e8351aSSascha Hauer void (*tmu_set_emulation)(struct exynos_tmu_data *data, int temp); 227a7331f72SBartlomiej Zolnierkiewicz void (*tmu_clear_irqs)(struct exynos_tmu_data *data); 22859dfa54cSAmit Daniel Kachhap }; 22959dfa54cSAmit Daniel Kachhap 2303b6a1a80SLukasz Majewski static void exynos_report_trigger(struct exynos_tmu_data *p) 2313b6a1a80SLukasz Majewski { 2323b6a1a80SLukasz Majewski char data[10], *envp[] = { data, NULL }; 2333b6a1a80SLukasz Majewski struct thermal_zone_device *tz = p->tzd; 23417e8351aSSascha Hauer int temp; 2353b6a1a80SLukasz Majewski unsigned int i; 2363b6a1a80SLukasz Majewski 237eccb6014SLukasz Majewski if (!tz) { 238eccb6014SLukasz Majewski pr_err("No thermal zone device defined\n"); 2393b6a1a80SLukasz Majewski return; 2403b6a1a80SLukasz Majewski } 2413b6a1a80SLukasz Majewski 2420e70f466SSrinivas Pandruvada thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); 2433b6a1a80SLukasz Majewski 2443b6a1a80SLukasz Majewski mutex_lock(&tz->lock); 2453b6a1a80SLukasz Majewski /* Find the level for which trip happened */ 2463b6a1a80SLukasz Majewski for (i = 0; i < of_thermal_get_ntrips(tz); i++) { 2473b6a1a80SLukasz Majewski tz->ops->get_trip_temp(tz, i, &temp); 2483b6a1a80SLukasz Majewski if (tz->last_temperature < temp) 2493b6a1a80SLukasz Majewski break; 2503b6a1a80SLukasz Majewski } 2513b6a1a80SLukasz Majewski 2523b6a1a80SLukasz Majewski snprintf(data, sizeof(data), "%u", i); 2533b6a1a80SLukasz Majewski kobject_uevent_env(&tz->device.kobj, KOBJ_CHANGE, envp); 2543b6a1a80SLukasz Majewski mutex_unlock(&tz->lock); 2553b6a1a80SLukasz Majewski } 2563b6a1a80SLukasz Majewski 25759dfa54cSAmit Daniel Kachhap /* 25859dfa54cSAmit Daniel Kachhap * TMU treats temperature as a mapped temperature code. 25959dfa54cSAmit Daniel Kachhap * The temperature is converted differently depending on the calibration type. 26059dfa54cSAmit Daniel Kachhap */ 26159dfa54cSAmit Daniel Kachhap static int temp_to_code(struct exynos_tmu_data *data, u8 temp) 26259dfa54cSAmit Daniel Kachhap { 263199b3e3cSBartlomiej Zolnierkiewicz if (data->cal_type == TYPE_ONE_POINT_TRIMMING) 264718b4ca1SBartlomiej Zolnierkiewicz return temp + data->temp_error1 - EXYNOS_FIRST_POINT_TRIM; 2659c933b1bSBartlomiej Zolnierkiewicz 266718b4ca1SBartlomiej Zolnierkiewicz return (temp - EXYNOS_FIRST_POINT_TRIM) * 26759dfa54cSAmit Daniel Kachhap (data->temp_error2 - data->temp_error1) / 268718b4ca1SBartlomiej Zolnierkiewicz (EXYNOS_SECOND_POINT_TRIM - EXYNOS_FIRST_POINT_TRIM) + 269bb34b4c8SAmit Daniel Kachhap data->temp_error1; 27059dfa54cSAmit Daniel Kachhap } 27159dfa54cSAmit Daniel Kachhap 27259dfa54cSAmit Daniel Kachhap /* 27359dfa54cSAmit Daniel Kachhap * Calculate a temperature value from a temperature code. 27459dfa54cSAmit Daniel Kachhap * The unit of the temperature is degree Celsius. 27559dfa54cSAmit Daniel Kachhap */ 2766c247393SAbhilash Kesavan static int code_to_temp(struct exynos_tmu_data *data, u16 temp_code) 27759dfa54cSAmit Daniel Kachhap { 278199b3e3cSBartlomiej Zolnierkiewicz if (data->cal_type == TYPE_ONE_POINT_TRIMMING) 279718b4ca1SBartlomiej Zolnierkiewicz return temp_code - data->temp_error1 + EXYNOS_FIRST_POINT_TRIM; 2809c933b1bSBartlomiej Zolnierkiewicz 2819c933b1bSBartlomiej Zolnierkiewicz return (temp_code - data->temp_error1) * 282718b4ca1SBartlomiej Zolnierkiewicz (EXYNOS_SECOND_POINT_TRIM - EXYNOS_FIRST_POINT_TRIM) / 283bb34b4c8SAmit Daniel Kachhap (data->temp_error2 - data->temp_error1) + 284718b4ca1SBartlomiej Zolnierkiewicz EXYNOS_FIRST_POINT_TRIM; 28559dfa54cSAmit Daniel Kachhap } 28659dfa54cSAmit Daniel Kachhap 2878328a4b1SBartlomiej Zolnierkiewicz static void sanitize_temp_error(struct exynos_tmu_data *data, u32 trim_info) 288b835ced1SBartlomiej Zolnierkiewicz { 289b8d582b9SAmit Daniel Kachhap data->temp_error1 = trim_info & EXYNOS_TMU_TEMP_MASK; 29099d67fb9SBartlomiej Zolnierkiewicz data->temp_error2 = ((trim_info >> EXYNOS_TRIMINFO_85_SHIFT) & 291b8d582b9SAmit Daniel Kachhap EXYNOS_TMU_TEMP_MASK); 29259dfa54cSAmit Daniel Kachhap 2935000806cSAmit Daniel Kachhap if (!data->temp_error1 || 294e3ed3649SBartlomiej Zolnierkiewicz (data->min_efuse_value > data->temp_error1) || 295e3ed3649SBartlomiej Zolnierkiewicz (data->temp_error1 > data->max_efuse_value)) 296e3ed3649SBartlomiej Zolnierkiewicz data->temp_error1 = data->efuse_value & EXYNOS_TMU_TEMP_MASK; 2975000806cSAmit Daniel Kachhap 2985000806cSAmit Daniel Kachhap if (!data->temp_error2) 2995000806cSAmit Daniel Kachhap data->temp_error2 = 300e3ed3649SBartlomiej Zolnierkiewicz (data->efuse_value >> EXYNOS_TRIMINFO_85_SHIFT) & 3015000806cSAmit Daniel Kachhap EXYNOS_TMU_TEMP_MASK; 3028328a4b1SBartlomiej Zolnierkiewicz } 30359dfa54cSAmit Daniel Kachhap 304fe87789cSBartlomiej Zolnierkiewicz static u32 get_th_reg(struct exynos_tmu_data *data, u32 threshold, bool falling) 305fe87789cSBartlomiej Zolnierkiewicz { 3063b6a1a80SLukasz Majewski struct thermal_zone_device *tz = data->tzd; 3073b6a1a80SLukasz Majewski const struct thermal_trip * const trips = 3083b6a1a80SLukasz Majewski of_thermal_get_trip_points(tz); 3093b6a1a80SLukasz Majewski unsigned long temp; 310fe87789cSBartlomiej Zolnierkiewicz int i; 311c65d3473STushar Behera 3123b6a1a80SLukasz Majewski for (i = 0; i < of_thermal_get_ntrips(tz); i++) { 3133b6a1a80SLukasz Majewski if (trips[i].type == THERMAL_TRIP_CRITICAL) 3143b6a1a80SLukasz Majewski continue; 3153b6a1a80SLukasz Majewski 3163b6a1a80SLukasz Majewski temp = trips[i].temperature / MCELSIUS; 317fe87789cSBartlomiej Zolnierkiewicz if (falling) 3183b6a1a80SLukasz Majewski temp -= (trips[i].hysteresis / MCELSIUS); 319fe87789cSBartlomiej Zolnierkiewicz else 320fe87789cSBartlomiej Zolnierkiewicz threshold &= ~(0xff << 8 * i); 321fe87789cSBartlomiej Zolnierkiewicz 322fe87789cSBartlomiej Zolnierkiewicz threshold |= temp_to_code(data, temp) << 8 * i; 32359dfa54cSAmit Daniel Kachhap } 32459dfa54cSAmit Daniel Kachhap 325fe87789cSBartlomiej Zolnierkiewicz return threshold; 326fe87789cSBartlomiej Zolnierkiewicz } 32759dfa54cSAmit Daniel Kachhap 32859dfa54cSAmit Daniel Kachhap static int exynos_tmu_initialize(struct platform_device *pdev) 32959dfa54cSAmit Daniel Kachhap { 33059dfa54cSAmit Daniel Kachhap struct exynos_tmu_data *data = platform_get_drvdata(pdev); 33175e0f100SBartlomiej Zolnierkiewicz struct thermal_zone_device *tzd = data->tzd; 33275e0f100SBartlomiej Zolnierkiewicz const struct thermal_trip * const trips = 33375e0f100SBartlomiej Zolnierkiewicz of_thermal_get_trip_points(tzd); 334*8f1c404bSBartlomiej Zolnierkiewicz int ret = 0, temp; 3357ca04e58SAmit Daniel Kachhap 33675e0f100SBartlomiej Zolnierkiewicz if (!trips) { 33775e0f100SBartlomiej Zolnierkiewicz dev_err(&pdev->dev, 33875e0f100SBartlomiej Zolnierkiewicz "Cannot get trip points from device tree!\n"); 33975e0f100SBartlomiej Zolnierkiewicz return -ENODEV; 34075e0f100SBartlomiej Zolnierkiewicz } 34175e0f100SBartlomiej Zolnierkiewicz 342*8f1c404bSBartlomiej Zolnierkiewicz if (data->soc != SOC_ARCH_EXYNOS5433) /* FIXME */ 343*8f1c404bSBartlomiej Zolnierkiewicz ret = tzd->ops->get_crit_temp(tzd, &temp); 344*8f1c404bSBartlomiej Zolnierkiewicz if (ret) { 345*8f1c404bSBartlomiej Zolnierkiewicz dev_err(&pdev->dev, 346*8f1c404bSBartlomiej Zolnierkiewicz "No CRITICAL trip point defined in device tree!\n"); 347*8f1c404bSBartlomiej Zolnierkiewicz goto out; 348*8f1c404bSBartlomiej Zolnierkiewicz } 349*8f1c404bSBartlomiej Zolnierkiewicz 35075e0f100SBartlomiej Zolnierkiewicz if (of_thermal_get_ntrips(tzd) > data->ntrip) { 3513a3a5f15SKrzysztof Kozlowski dev_info(&pdev->dev, 3523a3a5f15SKrzysztof Kozlowski "More trip points than supported by this TMU.\n"); 3533a3a5f15SKrzysztof Kozlowski dev_info(&pdev->dev, 3543a3a5f15SKrzysztof Kozlowski "%d trip points should be configured in polling mode.\n", 35575e0f100SBartlomiej Zolnierkiewicz (of_thermal_get_ntrips(tzd) - data->ntrip)); 3563a3a5f15SKrzysztof Kozlowski } 3573a3a5f15SKrzysztof Kozlowski 35859dfa54cSAmit Daniel Kachhap mutex_lock(&data->lock); 35959dfa54cSAmit Daniel Kachhap clk_enable(data->clk); 36059dfa54cSAmit Daniel Kachhap if (!IS_ERR(data->clk_sec)) 36159dfa54cSAmit Daniel Kachhap clk_enable(data->clk_sec); 36272d1100bSBartlomiej Zolnierkiewicz ret = data->tmu_initialize(pdev); 36359dfa54cSAmit Daniel Kachhap clk_disable(data->clk); 36459dfa54cSAmit Daniel Kachhap mutex_unlock(&data->lock); 36514a11dc7SNaveen Krishna Chatradhi if (!IS_ERR(data->clk_sec)) 36614a11dc7SNaveen Krishna Chatradhi clk_disable(data->clk_sec); 367*8f1c404bSBartlomiej Zolnierkiewicz out: 36859dfa54cSAmit Daniel Kachhap return ret; 36959dfa54cSAmit Daniel Kachhap } 37059dfa54cSAmit Daniel Kachhap 371d00671c3SBartlomiej Zolnierkiewicz static u32 get_con_reg(struct exynos_tmu_data *data, u32 con) 37259dfa54cSAmit Daniel Kachhap { 3737575983cSBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS4412 || 3747575983cSBartlomiej Zolnierkiewicz data->soc == SOC_ARCH_EXYNOS3250) 3757575983cSBartlomiej Zolnierkiewicz con |= (EXYNOS4412_MUX_ADDR_VALUE << EXYNOS4412_MUX_ADDR_SHIFT); 37686f5362eSLukasz Majewski 37799d67fb9SBartlomiej Zolnierkiewicz con &= ~(EXYNOS_TMU_REF_VOLTAGE_MASK << EXYNOS_TMU_REF_VOLTAGE_SHIFT); 37861020d18SBartlomiej Zolnierkiewicz con |= data->reference_voltage << EXYNOS_TMU_REF_VOLTAGE_SHIFT; 379d0a0ce3eSAmit Daniel Kachhap 38099d67fb9SBartlomiej Zolnierkiewicz con &= ~(EXYNOS_TMU_BUF_SLOPE_SEL_MASK << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT); 381fccfe099SBartlomiej Zolnierkiewicz con |= (data->gain << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT); 382d0a0ce3eSAmit Daniel Kachhap 383b9504a6aSBartlomiej Zolnierkiewicz con &= ~(EXYNOS_TMU_TRIP_MODE_MASK << EXYNOS_TMU_TRIP_MODE_SHIFT); 38409d29426SBartlomiej Zolnierkiewicz con |= (EXYNOS_NOISE_CANCEL_MODE << EXYNOS_TMU_TRIP_MODE_SHIFT); 38559dfa54cSAmit Daniel Kachhap 386d00671c3SBartlomiej Zolnierkiewicz return con; 387d00671c3SBartlomiej Zolnierkiewicz } 388d00671c3SBartlomiej Zolnierkiewicz 389d00671c3SBartlomiej Zolnierkiewicz static void exynos_tmu_control(struct platform_device *pdev, bool on) 390d00671c3SBartlomiej Zolnierkiewicz { 391d00671c3SBartlomiej Zolnierkiewicz struct exynos_tmu_data *data = platform_get_drvdata(pdev); 392d00671c3SBartlomiej Zolnierkiewicz 393d00671c3SBartlomiej Zolnierkiewicz mutex_lock(&data->lock); 394d00671c3SBartlomiej Zolnierkiewicz clk_enable(data->clk); 39537f9034fSBartlomiej Zolnierkiewicz data->tmu_control(pdev, on); 3960eb875d8SMarek Szyprowski data->enabled = on; 39759dfa54cSAmit Daniel Kachhap clk_disable(data->clk); 39859dfa54cSAmit Daniel Kachhap mutex_unlock(&data->lock); 39959dfa54cSAmit Daniel Kachhap } 40059dfa54cSAmit Daniel Kachhap 40172d1100bSBartlomiej Zolnierkiewicz static int exynos4210_tmu_initialize(struct platform_device *pdev) 40272d1100bSBartlomiej Zolnierkiewicz { 40372d1100bSBartlomiej Zolnierkiewicz struct exynos_tmu_data *data = platform_get_drvdata(pdev); 4043b6a1a80SLukasz Majewski struct thermal_zone_device *tz = data->tzd; 4053b6a1a80SLukasz Majewski const struct thermal_trip * const trips = 4063b6a1a80SLukasz Majewski of_thermal_get_trip_points(tz); 40772d1100bSBartlomiej Zolnierkiewicz int ret = 0, threshold_code, i; 4083b6a1a80SLukasz Majewski unsigned long reference, temp; 4093b6a1a80SLukasz Majewski unsigned int status; 4103b6a1a80SLukasz Majewski 41172d1100bSBartlomiej Zolnierkiewicz status = readb(data->base + EXYNOS_TMU_REG_STATUS); 41272d1100bSBartlomiej Zolnierkiewicz if (!status) { 41372d1100bSBartlomiej Zolnierkiewicz ret = -EBUSY; 41472d1100bSBartlomiej Zolnierkiewicz goto out; 41572d1100bSBartlomiej Zolnierkiewicz } 41672d1100bSBartlomiej Zolnierkiewicz 41772d1100bSBartlomiej Zolnierkiewicz sanitize_temp_error(data, readl(data->base + EXYNOS_TMU_REG_TRIMINFO)); 41872d1100bSBartlomiej Zolnierkiewicz 41972d1100bSBartlomiej Zolnierkiewicz /* Write temperature code for threshold */ 4203b6a1a80SLukasz Majewski reference = trips[0].temperature / MCELSIUS; 4213b6a1a80SLukasz Majewski threshold_code = temp_to_code(data, reference); 4223b6a1a80SLukasz Majewski if (threshold_code < 0) { 4233b6a1a80SLukasz Majewski ret = threshold_code; 4243b6a1a80SLukasz Majewski goto out; 4253b6a1a80SLukasz Majewski } 42672d1100bSBartlomiej Zolnierkiewicz writeb(threshold_code, data->base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP); 42772d1100bSBartlomiej Zolnierkiewicz 4283b6a1a80SLukasz Majewski for (i = 0; i < of_thermal_get_ntrips(tz); i++) { 4293b6a1a80SLukasz Majewski temp = trips[i].temperature / MCELSIUS; 4303b6a1a80SLukasz Majewski writeb(temp - reference, data->base + 43172d1100bSBartlomiej Zolnierkiewicz EXYNOS4210_TMU_REG_TRIG_LEVEL0 + i * 4); 4323b6a1a80SLukasz Majewski } 43372d1100bSBartlomiej Zolnierkiewicz 434a7331f72SBartlomiej Zolnierkiewicz data->tmu_clear_irqs(data); 43572d1100bSBartlomiej Zolnierkiewicz out: 43672d1100bSBartlomiej Zolnierkiewicz return ret; 43772d1100bSBartlomiej Zolnierkiewicz } 43872d1100bSBartlomiej Zolnierkiewicz 43972d1100bSBartlomiej Zolnierkiewicz static int exynos4412_tmu_initialize(struct platform_device *pdev) 44072d1100bSBartlomiej Zolnierkiewicz { 44172d1100bSBartlomiej Zolnierkiewicz struct exynos_tmu_data *data = platform_get_drvdata(pdev); 4423b6a1a80SLukasz Majewski const struct thermal_trip * const trips = 4433b6a1a80SLukasz Majewski of_thermal_get_trip_points(data->tzd); 44472d1100bSBartlomiej Zolnierkiewicz unsigned int status, trim_info, con, ctrl, rising_threshold; 44572d1100bSBartlomiej Zolnierkiewicz int ret = 0, threshold_code, i; 4463b6a1a80SLukasz Majewski unsigned long crit_temp = 0; 44772d1100bSBartlomiej Zolnierkiewicz 44872d1100bSBartlomiej Zolnierkiewicz status = readb(data->base + EXYNOS_TMU_REG_STATUS); 44972d1100bSBartlomiej Zolnierkiewicz if (!status) { 45072d1100bSBartlomiej Zolnierkiewicz ret = -EBUSY; 45172d1100bSBartlomiej Zolnierkiewicz goto out; 45272d1100bSBartlomiej Zolnierkiewicz } 45372d1100bSBartlomiej Zolnierkiewicz 45472d1100bSBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS3250 || 45572d1100bSBartlomiej Zolnierkiewicz data->soc == SOC_ARCH_EXYNOS4412 || 45672d1100bSBartlomiej Zolnierkiewicz data->soc == SOC_ARCH_EXYNOS5250) { 45772d1100bSBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS3250) { 45872d1100bSBartlomiej Zolnierkiewicz ctrl = readl(data->base + EXYNOS_TMU_TRIMINFO_CON1); 45972d1100bSBartlomiej Zolnierkiewicz ctrl |= EXYNOS_TRIMINFO_RELOAD_ENABLE; 46072d1100bSBartlomiej Zolnierkiewicz writel(ctrl, data->base + EXYNOS_TMU_TRIMINFO_CON1); 46172d1100bSBartlomiej Zolnierkiewicz } 46272d1100bSBartlomiej Zolnierkiewicz ctrl = readl(data->base + EXYNOS_TMU_TRIMINFO_CON2); 46372d1100bSBartlomiej Zolnierkiewicz ctrl |= EXYNOS_TRIMINFO_RELOAD_ENABLE; 46472d1100bSBartlomiej Zolnierkiewicz writel(ctrl, data->base + EXYNOS_TMU_TRIMINFO_CON2); 46572d1100bSBartlomiej Zolnierkiewicz } 46672d1100bSBartlomiej Zolnierkiewicz 46772d1100bSBartlomiej Zolnierkiewicz /* On exynos5420 the triminfo register is in the shared space */ 46872d1100bSBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) 46972d1100bSBartlomiej Zolnierkiewicz trim_info = readl(data->base_second + EXYNOS_TMU_REG_TRIMINFO); 47072d1100bSBartlomiej Zolnierkiewicz else 47172d1100bSBartlomiej Zolnierkiewicz trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); 47272d1100bSBartlomiej Zolnierkiewicz 47372d1100bSBartlomiej Zolnierkiewicz sanitize_temp_error(data, trim_info); 47472d1100bSBartlomiej Zolnierkiewicz 47572d1100bSBartlomiej Zolnierkiewicz /* Write temperature code for rising and falling threshold */ 47672d1100bSBartlomiej Zolnierkiewicz rising_threshold = readl(data->base + EXYNOS_THD_TEMP_RISE); 47772d1100bSBartlomiej Zolnierkiewicz rising_threshold = get_th_reg(data, rising_threshold, false); 47872d1100bSBartlomiej Zolnierkiewicz writel(rising_threshold, data->base + EXYNOS_THD_TEMP_RISE); 47972d1100bSBartlomiej Zolnierkiewicz writel(get_th_reg(data, 0, true), data->base + EXYNOS_THD_TEMP_FALL); 48072d1100bSBartlomiej Zolnierkiewicz 481a7331f72SBartlomiej Zolnierkiewicz data->tmu_clear_irqs(data); 48272d1100bSBartlomiej Zolnierkiewicz 48372d1100bSBartlomiej Zolnierkiewicz /* if last threshold limit is also present */ 4843b6a1a80SLukasz Majewski for (i = 0; i < of_thermal_get_ntrips(data->tzd); i++) { 4853b6a1a80SLukasz Majewski if (trips[i].type == THERMAL_TRIP_CRITICAL) { 4863b6a1a80SLukasz Majewski crit_temp = trips[i].temperature; 4873b6a1a80SLukasz Majewski break; 4883b6a1a80SLukasz Majewski } 4893b6a1a80SLukasz Majewski } 4903b6a1a80SLukasz Majewski 4913b6a1a80SLukasz Majewski threshold_code = temp_to_code(data, crit_temp / MCELSIUS); 49272d1100bSBartlomiej Zolnierkiewicz /* 1-4 level to be assigned in th0 reg */ 49372d1100bSBartlomiej Zolnierkiewicz rising_threshold &= ~(0xff << 8 * i); 49472d1100bSBartlomiej Zolnierkiewicz rising_threshold |= threshold_code << 8 * i; 49572d1100bSBartlomiej Zolnierkiewicz writel(rising_threshold, data->base + EXYNOS_THD_TEMP_RISE); 49672d1100bSBartlomiej Zolnierkiewicz con = readl(data->base + EXYNOS_TMU_REG_CONTROL); 49772d1100bSBartlomiej Zolnierkiewicz con |= (1 << EXYNOS_TMU_THERM_TRIP_EN_SHIFT); 49872d1100bSBartlomiej Zolnierkiewicz writel(con, data->base + EXYNOS_TMU_REG_CONTROL); 4993b6a1a80SLukasz Majewski 50072d1100bSBartlomiej Zolnierkiewicz out: 50172d1100bSBartlomiej Zolnierkiewicz return ret; 50272d1100bSBartlomiej Zolnierkiewicz } 50372d1100bSBartlomiej Zolnierkiewicz 504488c7455SChanwoo Choi static int exynos5433_tmu_initialize(struct platform_device *pdev) 505488c7455SChanwoo Choi { 506488c7455SChanwoo Choi struct exynos_tmu_data *data = platform_get_drvdata(pdev); 507488c7455SChanwoo Choi struct thermal_zone_device *tz = data->tzd; 508488c7455SChanwoo Choi unsigned int status, trim_info; 509488c7455SChanwoo Choi unsigned int rising_threshold = 0, falling_threshold = 0; 51017e8351aSSascha Hauer int temp, temp_hist; 511488c7455SChanwoo Choi int ret = 0, threshold_code, i, sensor_id, cal_type; 512488c7455SChanwoo Choi 513488c7455SChanwoo Choi status = readb(data->base + EXYNOS_TMU_REG_STATUS); 514488c7455SChanwoo Choi if (!status) { 515488c7455SChanwoo Choi ret = -EBUSY; 516488c7455SChanwoo Choi goto out; 517488c7455SChanwoo Choi } 518488c7455SChanwoo Choi 519488c7455SChanwoo Choi trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); 520488c7455SChanwoo Choi sanitize_temp_error(data, trim_info); 521488c7455SChanwoo Choi 522488c7455SChanwoo Choi /* Read the temperature sensor id */ 523488c7455SChanwoo Choi sensor_id = (trim_info & EXYNOS5433_TRIMINFO_SENSOR_ID_MASK) 524488c7455SChanwoo Choi >> EXYNOS5433_TRIMINFO_SENSOR_ID_SHIFT; 525488c7455SChanwoo Choi dev_info(&pdev->dev, "Temperature sensor ID: 0x%x\n", sensor_id); 526488c7455SChanwoo Choi 527488c7455SChanwoo Choi /* Read the calibration mode */ 528488c7455SChanwoo Choi writel(trim_info, data->base + EXYNOS_TMU_REG_TRIMINFO); 529488c7455SChanwoo Choi cal_type = (trim_info & EXYNOS5433_TRIMINFO_CALIB_SEL_MASK) 530488c7455SChanwoo Choi >> EXYNOS5433_TRIMINFO_CALIB_SEL_SHIFT; 531488c7455SChanwoo Choi 532488c7455SChanwoo Choi switch (cal_type) { 533488c7455SChanwoo Choi case EXYNOS5433_TRIMINFO_TWO_POINT_TRIMMING: 534199b3e3cSBartlomiej Zolnierkiewicz data->cal_type = TYPE_TWO_POINT_TRIMMING; 535488c7455SChanwoo Choi break; 536199b3e3cSBartlomiej Zolnierkiewicz case EXYNOS5433_TRIMINFO_ONE_POINT_TRIMMING: 537488c7455SChanwoo Choi default: 538199b3e3cSBartlomiej Zolnierkiewicz data->cal_type = TYPE_ONE_POINT_TRIMMING; 539488c7455SChanwoo Choi break; 540baba1ebbSKrzysztof Kozlowski } 541488c7455SChanwoo Choi 542488c7455SChanwoo Choi dev_info(&pdev->dev, "Calibration type is %d-point calibration\n", 543488c7455SChanwoo Choi cal_type ? 2 : 1); 544488c7455SChanwoo Choi 545488c7455SChanwoo Choi /* Write temperature code for rising and falling threshold */ 546488c7455SChanwoo Choi for (i = 0; i < of_thermal_get_ntrips(tz); i++) { 547488c7455SChanwoo Choi int rising_reg_offset, falling_reg_offset; 548488c7455SChanwoo Choi int j = 0; 549488c7455SChanwoo Choi 550488c7455SChanwoo Choi switch (i) { 551488c7455SChanwoo Choi case 0: 552488c7455SChanwoo Choi case 1: 553488c7455SChanwoo Choi case 2: 554488c7455SChanwoo Choi case 3: 555488c7455SChanwoo Choi rising_reg_offset = EXYNOS5433_THD_TEMP_RISE3_0; 556488c7455SChanwoo Choi falling_reg_offset = EXYNOS5433_THD_TEMP_FALL3_0; 557488c7455SChanwoo Choi j = i; 558488c7455SChanwoo Choi break; 559488c7455SChanwoo Choi case 4: 560488c7455SChanwoo Choi case 5: 561488c7455SChanwoo Choi case 6: 562488c7455SChanwoo Choi case 7: 563488c7455SChanwoo Choi rising_reg_offset = EXYNOS5433_THD_TEMP_RISE7_4; 564488c7455SChanwoo Choi falling_reg_offset = EXYNOS5433_THD_TEMP_FALL7_4; 565488c7455SChanwoo Choi j = i - 4; 566488c7455SChanwoo Choi break; 567488c7455SChanwoo Choi default: 568488c7455SChanwoo Choi continue; 569488c7455SChanwoo Choi } 570488c7455SChanwoo Choi 571488c7455SChanwoo Choi /* Write temperature code for rising threshold */ 572488c7455SChanwoo Choi tz->ops->get_trip_temp(tz, i, &temp); 573488c7455SChanwoo Choi temp /= MCELSIUS; 574488c7455SChanwoo Choi threshold_code = temp_to_code(data, temp); 575488c7455SChanwoo Choi 576488c7455SChanwoo Choi rising_threshold = readl(data->base + rising_reg_offset); 5778bfc218dSBartlomiej Zolnierkiewicz rising_threshold &= ~(0xff << j * 8); 578488c7455SChanwoo Choi rising_threshold |= (threshold_code << j * 8); 579488c7455SChanwoo Choi writel(rising_threshold, data->base + rising_reg_offset); 580488c7455SChanwoo Choi 581488c7455SChanwoo Choi /* Write temperature code for falling threshold */ 582488c7455SChanwoo Choi tz->ops->get_trip_hyst(tz, i, &temp_hist); 583488c7455SChanwoo Choi temp_hist = temp - (temp_hist / MCELSIUS); 584488c7455SChanwoo Choi threshold_code = temp_to_code(data, temp_hist); 585488c7455SChanwoo Choi 586488c7455SChanwoo Choi falling_threshold = readl(data->base + falling_reg_offset); 587488c7455SChanwoo Choi falling_threshold &= ~(0xff << j * 8); 588488c7455SChanwoo Choi falling_threshold |= (threshold_code << j * 8); 589488c7455SChanwoo Choi writel(falling_threshold, data->base + falling_reg_offset); 590488c7455SChanwoo Choi } 591488c7455SChanwoo Choi 592488c7455SChanwoo Choi data->tmu_clear_irqs(data); 593488c7455SChanwoo Choi out: 594488c7455SChanwoo Choi return ret; 595488c7455SChanwoo Choi } 596488c7455SChanwoo Choi 5976c247393SAbhilash Kesavan static int exynos7_tmu_initialize(struct platform_device *pdev) 5986c247393SAbhilash Kesavan { 5996c247393SAbhilash Kesavan struct exynos_tmu_data *data = platform_get_drvdata(pdev); 6006c247393SAbhilash Kesavan struct thermal_zone_device *tz = data->tzd; 6016c247393SAbhilash Kesavan unsigned int status, trim_info; 6026c247393SAbhilash Kesavan unsigned int rising_threshold = 0, falling_threshold = 0; 6036c247393SAbhilash Kesavan int ret = 0, threshold_code, i; 60417e8351aSSascha Hauer int temp, temp_hist; 6056c247393SAbhilash Kesavan unsigned int reg_off, bit_off; 6066c247393SAbhilash Kesavan 6076c247393SAbhilash Kesavan status = readb(data->base + EXYNOS_TMU_REG_STATUS); 6086c247393SAbhilash Kesavan if (!status) { 6096c247393SAbhilash Kesavan ret = -EBUSY; 6106c247393SAbhilash Kesavan goto out; 6116c247393SAbhilash Kesavan } 6126c247393SAbhilash Kesavan 6136c247393SAbhilash Kesavan trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); 6146c247393SAbhilash Kesavan 6156c247393SAbhilash Kesavan data->temp_error1 = trim_info & EXYNOS7_TMU_TEMP_MASK; 6166c247393SAbhilash Kesavan if (!data->temp_error1 || 617e3ed3649SBartlomiej Zolnierkiewicz (data->min_efuse_value > data->temp_error1) || 618e3ed3649SBartlomiej Zolnierkiewicz (data->temp_error1 > data->max_efuse_value)) 619e3ed3649SBartlomiej Zolnierkiewicz data->temp_error1 = data->efuse_value & EXYNOS_TMU_TEMP_MASK; 6206c247393SAbhilash Kesavan 6216c247393SAbhilash Kesavan /* Write temperature code for rising and falling threshold */ 6226c247393SAbhilash Kesavan for (i = (of_thermal_get_ntrips(tz) - 1); i >= 0; i--) { 6236c247393SAbhilash Kesavan /* 6246c247393SAbhilash Kesavan * On exynos7 there are 4 rising and 4 falling threshold 6256c247393SAbhilash Kesavan * registers (0x50-0x5c and 0x60-0x6c respectively). Each 6266c247393SAbhilash Kesavan * register holds the value of two threshold levels (at bit 6276c247393SAbhilash Kesavan * offsets 0 and 16). Based on the fact that there are atmost 6286c247393SAbhilash Kesavan * eight possible trigger levels, calculate the register and 6296c247393SAbhilash Kesavan * bit offsets where the threshold levels are to be written. 6306c247393SAbhilash Kesavan * 6316c247393SAbhilash Kesavan * e.g. EXYNOS7_THD_TEMP_RISE7_6 (0x50) 6326c247393SAbhilash Kesavan * [24:16] - Threshold level 7 6336c247393SAbhilash Kesavan * [8:0] - Threshold level 6 6346c247393SAbhilash Kesavan * e.g. EXYNOS7_THD_TEMP_RISE5_4 (0x54) 6356c247393SAbhilash Kesavan * [24:16] - Threshold level 5 6366c247393SAbhilash Kesavan * [8:0] - Threshold level 4 6376c247393SAbhilash Kesavan * 6386c247393SAbhilash Kesavan * and similarly for falling thresholds. 6396c247393SAbhilash Kesavan * 6406c247393SAbhilash Kesavan * Based on the above, calculate the register and bit offsets 6416c247393SAbhilash Kesavan * for rising/falling threshold levels and populate them. 6426c247393SAbhilash Kesavan */ 6436c247393SAbhilash Kesavan reg_off = ((7 - i) / 2) * 4; 6446c247393SAbhilash Kesavan bit_off = ((8 - i) % 2); 6456c247393SAbhilash Kesavan 6466c247393SAbhilash Kesavan tz->ops->get_trip_temp(tz, i, &temp); 6476c247393SAbhilash Kesavan temp /= MCELSIUS; 6486c247393SAbhilash Kesavan 6496c247393SAbhilash Kesavan tz->ops->get_trip_hyst(tz, i, &temp_hist); 6506c247393SAbhilash Kesavan temp_hist = temp - (temp_hist / MCELSIUS); 6516c247393SAbhilash Kesavan 6526c247393SAbhilash Kesavan /* Set 9-bit temperature code for rising threshold levels */ 6536c247393SAbhilash Kesavan threshold_code = temp_to_code(data, temp); 6546c247393SAbhilash Kesavan rising_threshold = readl(data->base + 6556c247393SAbhilash Kesavan EXYNOS7_THD_TEMP_RISE7_6 + reg_off); 6566c247393SAbhilash Kesavan rising_threshold &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off)); 6576c247393SAbhilash Kesavan rising_threshold |= threshold_code << (16 * bit_off); 6586c247393SAbhilash Kesavan writel(rising_threshold, 6596c247393SAbhilash Kesavan data->base + EXYNOS7_THD_TEMP_RISE7_6 + reg_off); 6606c247393SAbhilash Kesavan 6616c247393SAbhilash Kesavan /* Set 9-bit temperature code for falling threshold levels */ 6626c247393SAbhilash Kesavan threshold_code = temp_to_code(data, temp_hist); 6636c247393SAbhilash Kesavan falling_threshold &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off)); 6646c247393SAbhilash Kesavan falling_threshold |= threshold_code << (16 * bit_off); 6656c247393SAbhilash Kesavan writel(falling_threshold, 6666c247393SAbhilash Kesavan data->base + EXYNOS7_THD_TEMP_FALL7_6 + reg_off); 6676c247393SAbhilash Kesavan } 6686c247393SAbhilash Kesavan 6696c247393SAbhilash Kesavan data->tmu_clear_irqs(data); 6706c247393SAbhilash Kesavan out: 6716c247393SAbhilash Kesavan return ret; 6726c247393SAbhilash Kesavan } 6736c247393SAbhilash Kesavan 67437f9034fSBartlomiej Zolnierkiewicz static void exynos4210_tmu_control(struct platform_device *pdev, bool on) 67537f9034fSBartlomiej Zolnierkiewicz { 67637f9034fSBartlomiej Zolnierkiewicz struct exynos_tmu_data *data = platform_get_drvdata(pdev); 6773b6a1a80SLukasz Majewski struct thermal_zone_device *tz = data->tzd; 67837f9034fSBartlomiej Zolnierkiewicz unsigned int con, interrupt_en; 67937f9034fSBartlomiej Zolnierkiewicz 68037f9034fSBartlomiej Zolnierkiewicz con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL)); 68137f9034fSBartlomiej Zolnierkiewicz 68259dfa54cSAmit Daniel Kachhap if (on) { 68359dfa54cSAmit Daniel Kachhap con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); 68459dfa54cSAmit Daniel Kachhap interrupt_en = 6853b6a1a80SLukasz Majewski (of_thermal_is_trip_valid(tz, 3) 6863b6a1a80SLukasz Majewski << EXYNOS_TMU_INTEN_RISE3_SHIFT) | 6873b6a1a80SLukasz Majewski (of_thermal_is_trip_valid(tz, 2) 6883b6a1a80SLukasz Majewski << EXYNOS_TMU_INTEN_RISE2_SHIFT) | 6893b6a1a80SLukasz Majewski (of_thermal_is_trip_valid(tz, 1) 6903b6a1a80SLukasz Majewski << EXYNOS_TMU_INTEN_RISE1_SHIFT) | 6913b6a1a80SLukasz Majewski (of_thermal_is_trip_valid(tz, 0) 6923b6a1a80SLukasz Majewski << EXYNOS_TMU_INTEN_RISE0_SHIFT); 6933b6a1a80SLukasz Majewski 694e0761533SBartlomiej Zolnierkiewicz if (data->soc != SOC_ARCH_EXYNOS4210) 69559dfa54cSAmit Daniel Kachhap interrupt_en |= 69637f9034fSBartlomiej Zolnierkiewicz interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT; 69759dfa54cSAmit Daniel Kachhap } else { 69859dfa54cSAmit Daniel Kachhap con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); 69959dfa54cSAmit Daniel Kachhap interrupt_en = 0; /* Disable all interrupts */ 70059dfa54cSAmit Daniel Kachhap } 70137f9034fSBartlomiej Zolnierkiewicz writel(interrupt_en, data->base + EXYNOS_TMU_REG_INTEN); 70237f9034fSBartlomiej Zolnierkiewicz writel(con, data->base + EXYNOS_TMU_REG_CONTROL); 70337f9034fSBartlomiej Zolnierkiewicz } 70459dfa54cSAmit Daniel Kachhap 705488c7455SChanwoo Choi static void exynos5433_tmu_control(struct platform_device *pdev, bool on) 706488c7455SChanwoo Choi { 707488c7455SChanwoo Choi struct exynos_tmu_data *data = platform_get_drvdata(pdev); 708488c7455SChanwoo Choi struct thermal_zone_device *tz = data->tzd; 709488c7455SChanwoo Choi unsigned int con, interrupt_en, pd_det_en; 710488c7455SChanwoo Choi 711488c7455SChanwoo Choi con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL)); 712488c7455SChanwoo Choi 713488c7455SChanwoo Choi if (on) { 714488c7455SChanwoo Choi con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); 715488c7455SChanwoo Choi interrupt_en = 716488c7455SChanwoo Choi (of_thermal_is_trip_valid(tz, 7) 717488c7455SChanwoo Choi << EXYNOS7_TMU_INTEN_RISE7_SHIFT) | 718488c7455SChanwoo Choi (of_thermal_is_trip_valid(tz, 6) 719488c7455SChanwoo Choi << EXYNOS7_TMU_INTEN_RISE6_SHIFT) | 720488c7455SChanwoo Choi (of_thermal_is_trip_valid(tz, 5) 721488c7455SChanwoo Choi << EXYNOS7_TMU_INTEN_RISE5_SHIFT) | 722488c7455SChanwoo Choi (of_thermal_is_trip_valid(tz, 4) 723488c7455SChanwoo Choi << EXYNOS7_TMU_INTEN_RISE4_SHIFT) | 724488c7455SChanwoo Choi (of_thermal_is_trip_valid(tz, 3) 725488c7455SChanwoo Choi << EXYNOS7_TMU_INTEN_RISE3_SHIFT) | 726488c7455SChanwoo Choi (of_thermal_is_trip_valid(tz, 2) 727488c7455SChanwoo Choi << EXYNOS7_TMU_INTEN_RISE2_SHIFT) | 728488c7455SChanwoo Choi (of_thermal_is_trip_valid(tz, 1) 729488c7455SChanwoo Choi << EXYNOS7_TMU_INTEN_RISE1_SHIFT) | 730488c7455SChanwoo Choi (of_thermal_is_trip_valid(tz, 0) 731488c7455SChanwoo Choi << EXYNOS7_TMU_INTEN_RISE0_SHIFT); 732488c7455SChanwoo Choi 733488c7455SChanwoo Choi interrupt_en |= 734488c7455SChanwoo Choi interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT; 735488c7455SChanwoo Choi } else { 736488c7455SChanwoo Choi con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); 737488c7455SChanwoo Choi interrupt_en = 0; /* Disable all interrupts */ 738488c7455SChanwoo Choi } 739488c7455SChanwoo Choi 740488c7455SChanwoo Choi pd_det_en = on ? EXYNOS5433_PD_DET_EN : 0; 741488c7455SChanwoo Choi 742488c7455SChanwoo Choi writel(pd_det_en, data->base + EXYNOS5433_TMU_PD_DET_EN); 743488c7455SChanwoo Choi writel(interrupt_en, data->base + EXYNOS5433_TMU_REG_INTEN); 744488c7455SChanwoo Choi writel(con, data->base + EXYNOS_TMU_REG_CONTROL); 745488c7455SChanwoo Choi } 746488c7455SChanwoo Choi 7476c247393SAbhilash Kesavan static void exynos7_tmu_control(struct platform_device *pdev, bool on) 7486c247393SAbhilash Kesavan { 7496c247393SAbhilash Kesavan struct exynos_tmu_data *data = platform_get_drvdata(pdev); 7506c247393SAbhilash Kesavan struct thermal_zone_device *tz = data->tzd; 7516c247393SAbhilash Kesavan unsigned int con, interrupt_en; 7526c247393SAbhilash Kesavan 7536c247393SAbhilash Kesavan con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL)); 7546c247393SAbhilash Kesavan 7556c247393SAbhilash Kesavan if (on) { 7566c247393SAbhilash Kesavan con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); 75742b696e8SChanwoo Choi con |= (1 << EXYNOS7_PD_DET_EN_SHIFT); 7586c247393SAbhilash Kesavan interrupt_en = 7596c247393SAbhilash Kesavan (of_thermal_is_trip_valid(tz, 7) 7606c247393SAbhilash Kesavan << EXYNOS7_TMU_INTEN_RISE7_SHIFT) | 7616c247393SAbhilash Kesavan (of_thermal_is_trip_valid(tz, 6) 7626c247393SAbhilash Kesavan << EXYNOS7_TMU_INTEN_RISE6_SHIFT) | 7636c247393SAbhilash Kesavan (of_thermal_is_trip_valid(tz, 5) 7646c247393SAbhilash Kesavan << EXYNOS7_TMU_INTEN_RISE5_SHIFT) | 7656c247393SAbhilash Kesavan (of_thermal_is_trip_valid(tz, 4) 7666c247393SAbhilash Kesavan << EXYNOS7_TMU_INTEN_RISE4_SHIFT) | 7676c247393SAbhilash Kesavan (of_thermal_is_trip_valid(tz, 3) 7686c247393SAbhilash Kesavan << EXYNOS7_TMU_INTEN_RISE3_SHIFT) | 7696c247393SAbhilash Kesavan (of_thermal_is_trip_valid(tz, 2) 7706c247393SAbhilash Kesavan << EXYNOS7_TMU_INTEN_RISE2_SHIFT) | 7716c247393SAbhilash Kesavan (of_thermal_is_trip_valid(tz, 1) 7726c247393SAbhilash Kesavan << EXYNOS7_TMU_INTEN_RISE1_SHIFT) | 7736c247393SAbhilash Kesavan (of_thermal_is_trip_valid(tz, 0) 7746c247393SAbhilash Kesavan << EXYNOS7_TMU_INTEN_RISE0_SHIFT); 7756c247393SAbhilash Kesavan 7766c247393SAbhilash Kesavan interrupt_en |= 7776c247393SAbhilash Kesavan interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT; 7786c247393SAbhilash Kesavan } else { 7796c247393SAbhilash Kesavan con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); 78042b696e8SChanwoo Choi con &= ~(1 << EXYNOS7_PD_DET_EN_SHIFT); 7816c247393SAbhilash Kesavan interrupt_en = 0; /* Disable all interrupts */ 7826c247393SAbhilash Kesavan } 7836c247393SAbhilash Kesavan 7846c247393SAbhilash Kesavan writel(interrupt_en, data->base + EXYNOS7_TMU_REG_INTEN); 7856c247393SAbhilash Kesavan writel(con, data->base + EXYNOS_TMU_REG_CONTROL); 7866c247393SAbhilash Kesavan } 7876c247393SAbhilash Kesavan 78817e8351aSSascha Hauer static int exynos_get_temp(void *p, int *temp) 78959dfa54cSAmit Daniel Kachhap { 7903b6a1a80SLukasz Majewski struct exynos_tmu_data *data = p; 79108d725cdSMarek Szyprowski int value, ret = 0; 7923b6a1a80SLukasz Majewski 7930eb875d8SMarek Szyprowski if (!data || !data->tmu_read || !data->enabled) 7943b6a1a80SLukasz Majewski return -EINVAL; 79559dfa54cSAmit Daniel Kachhap 79659dfa54cSAmit Daniel Kachhap mutex_lock(&data->lock); 79759dfa54cSAmit Daniel Kachhap clk_enable(data->clk); 7983b6a1a80SLukasz Majewski 79908d725cdSMarek Szyprowski value = data->tmu_read(data); 80008d725cdSMarek Szyprowski if (value < 0) 80108d725cdSMarek Szyprowski ret = value; 80208d725cdSMarek Szyprowski else 80308d725cdSMarek Szyprowski *temp = code_to_temp(data, value) * MCELSIUS; 8043b6a1a80SLukasz Majewski 80559dfa54cSAmit Daniel Kachhap clk_disable(data->clk); 80659dfa54cSAmit Daniel Kachhap mutex_unlock(&data->lock); 80759dfa54cSAmit Daniel Kachhap 80808d725cdSMarek Szyprowski return ret; 80959dfa54cSAmit Daniel Kachhap } 81059dfa54cSAmit Daniel Kachhap 81159dfa54cSAmit Daniel Kachhap #ifdef CONFIG_THERMAL_EMULATION 812154013eaSBartlomiej Zolnierkiewicz static u32 get_emul_con_reg(struct exynos_tmu_data *data, unsigned int val, 81317e8351aSSascha Hauer int temp) 814154013eaSBartlomiej Zolnierkiewicz { 815154013eaSBartlomiej Zolnierkiewicz if (temp) { 816154013eaSBartlomiej Zolnierkiewicz temp /= MCELSIUS; 817154013eaSBartlomiej Zolnierkiewicz 818154013eaSBartlomiej Zolnierkiewicz val &= ~(EXYNOS_EMUL_TIME_MASK << EXYNOS_EMUL_TIME_SHIFT); 819154013eaSBartlomiej Zolnierkiewicz val |= (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT); 8206c247393SAbhilash Kesavan if (data->soc == SOC_ARCH_EXYNOS7) { 8216c247393SAbhilash Kesavan val &= ~(EXYNOS7_EMUL_DATA_MASK << 8226c247393SAbhilash Kesavan EXYNOS7_EMUL_DATA_SHIFT); 8236c247393SAbhilash Kesavan val |= (temp_to_code(data, temp) << 8246c247393SAbhilash Kesavan EXYNOS7_EMUL_DATA_SHIFT) | 825154013eaSBartlomiej Zolnierkiewicz EXYNOS_EMUL_ENABLE; 826154013eaSBartlomiej Zolnierkiewicz } else { 8276c247393SAbhilash Kesavan val &= ~(EXYNOS_EMUL_DATA_MASK << 8286c247393SAbhilash Kesavan EXYNOS_EMUL_DATA_SHIFT); 8296c247393SAbhilash Kesavan val |= (temp_to_code(data, temp) << 8306c247393SAbhilash Kesavan EXYNOS_EMUL_DATA_SHIFT) | 8316c247393SAbhilash Kesavan EXYNOS_EMUL_ENABLE; 8326c247393SAbhilash Kesavan } 8336c247393SAbhilash Kesavan } else { 834154013eaSBartlomiej Zolnierkiewicz val &= ~EXYNOS_EMUL_ENABLE; 835154013eaSBartlomiej Zolnierkiewicz } 836154013eaSBartlomiej Zolnierkiewicz 837154013eaSBartlomiej Zolnierkiewicz return val; 838154013eaSBartlomiej Zolnierkiewicz } 839154013eaSBartlomiej Zolnierkiewicz 840285d994aSBartlomiej Zolnierkiewicz static void exynos4412_tmu_set_emulation(struct exynos_tmu_data *data, 84117e8351aSSascha Hauer int temp) 842285d994aSBartlomiej Zolnierkiewicz { 843285d994aSBartlomiej Zolnierkiewicz unsigned int val; 844285d994aSBartlomiej Zolnierkiewicz u32 emul_con; 845285d994aSBartlomiej Zolnierkiewicz 846285d994aSBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS5260) 847285d994aSBartlomiej Zolnierkiewicz emul_con = EXYNOS5260_EMUL_CON; 848b28fec13SSudip Mukherjee else if (data->soc == SOC_ARCH_EXYNOS5433) 849488c7455SChanwoo Choi emul_con = EXYNOS5433_TMU_EMUL_CON; 8506c247393SAbhilash Kesavan else if (data->soc == SOC_ARCH_EXYNOS7) 8516c247393SAbhilash Kesavan emul_con = EXYNOS7_TMU_REG_EMUL_CON; 852285d994aSBartlomiej Zolnierkiewicz else 853285d994aSBartlomiej Zolnierkiewicz emul_con = EXYNOS_EMUL_CON; 854285d994aSBartlomiej Zolnierkiewicz 855285d994aSBartlomiej Zolnierkiewicz val = readl(data->base + emul_con); 856285d994aSBartlomiej Zolnierkiewicz val = get_emul_con_reg(data, val, temp); 857285d994aSBartlomiej Zolnierkiewicz writel(val, data->base + emul_con); 858285d994aSBartlomiej Zolnierkiewicz } 859285d994aSBartlomiej Zolnierkiewicz 86017e8351aSSascha Hauer static int exynos_tmu_set_emulation(void *drv_data, int temp) 86159dfa54cSAmit Daniel Kachhap { 86259dfa54cSAmit Daniel Kachhap struct exynos_tmu_data *data = drv_data; 86359dfa54cSAmit Daniel Kachhap int ret = -EINVAL; 86459dfa54cSAmit Daniel Kachhap 865ef3f80fcSBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS4210) 86659dfa54cSAmit Daniel Kachhap goto out; 86759dfa54cSAmit Daniel Kachhap 86859dfa54cSAmit Daniel Kachhap if (temp && temp < MCELSIUS) 86959dfa54cSAmit Daniel Kachhap goto out; 87059dfa54cSAmit Daniel Kachhap 87159dfa54cSAmit Daniel Kachhap mutex_lock(&data->lock); 87259dfa54cSAmit Daniel Kachhap clk_enable(data->clk); 873285d994aSBartlomiej Zolnierkiewicz data->tmu_set_emulation(data, temp); 87459dfa54cSAmit Daniel Kachhap clk_disable(data->clk); 87559dfa54cSAmit Daniel Kachhap mutex_unlock(&data->lock); 87659dfa54cSAmit Daniel Kachhap return 0; 87759dfa54cSAmit Daniel Kachhap out: 87859dfa54cSAmit Daniel Kachhap return ret; 87959dfa54cSAmit Daniel Kachhap } 88059dfa54cSAmit Daniel Kachhap #else 881285d994aSBartlomiej Zolnierkiewicz #define exynos4412_tmu_set_emulation NULL 88217e8351aSSascha Hauer static int exynos_tmu_set_emulation(void *drv_data, int temp) 88359dfa54cSAmit Daniel Kachhap { return -EINVAL; } 88459dfa54cSAmit Daniel Kachhap #endif /* CONFIG_THERMAL_EMULATION */ 88559dfa54cSAmit Daniel Kachhap 886b79985caSBartlomiej Zolnierkiewicz static int exynos4210_tmu_read(struct exynos_tmu_data *data) 887b79985caSBartlomiej Zolnierkiewicz { 888b79985caSBartlomiej Zolnierkiewicz int ret = readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP); 889b79985caSBartlomiej Zolnierkiewicz 890b79985caSBartlomiej Zolnierkiewicz /* "temp_code" should range between 75 and 175 */ 891b79985caSBartlomiej Zolnierkiewicz return (ret < 75 || ret > 175) ? -ENODATA : ret; 892b79985caSBartlomiej Zolnierkiewicz } 893b79985caSBartlomiej Zolnierkiewicz 894b79985caSBartlomiej Zolnierkiewicz static int exynos4412_tmu_read(struct exynos_tmu_data *data) 895b79985caSBartlomiej Zolnierkiewicz { 896b79985caSBartlomiej Zolnierkiewicz return readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP); 897b79985caSBartlomiej Zolnierkiewicz } 898b79985caSBartlomiej Zolnierkiewicz 8996c247393SAbhilash Kesavan static int exynos7_tmu_read(struct exynos_tmu_data *data) 9006c247393SAbhilash Kesavan { 9016c247393SAbhilash Kesavan return readw(data->base + EXYNOS_TMU_REG_CURRENT_TEMP) & 9026c247393SAbhilash Kesavan EXYNOS7_TMU_TEMP_MASK; 9036c247393SAbhilash Kesavan } 9046c247393SAbhilash Kesavan 90559dfa54cSAmit Daniel Kachhap static void exynos_tmu_work(struct work_struct *work) 90659dfa54cSAmit Daniel Kachhap { 90759dfa54cSAmit Daniel Kachhap struct exynos_tmu_data *data = container_of(work, 90859dfa54cSAmit Daniel Kachhap struct exynos_tmu_data, irq_work); 909a0395eeeSAmit Daniel Kachhap 91014a11dc7SNaveen Krishna Chatradhi if (!IS_ERR(data->clk_sec)) 91114a11dc7SNaveen Krishna Chatradhi clk_enable(data->clk_sec); 91214a11dc7SNaveen Krishna Chatradhi if (!IS_ERR(data->clk_sec)) 91314a11dc7SNaveen Krishna Chatradhi clk_disable(data->clk_sec); 91459dfa54cSAmit Daniel Kachhap 9153b6a1a80SLukasz Majewski exynos_report_trigger(data); 91659dfa54cSAmit Daniel Kachhap mutex_lock(&data->lock); 91759dfa54cSAmit Daniel Kachhap clk_enable(data->clk); 918b8d582b9SAmit Daniel Kachhap 919a4463c4fSAmit Daniel Kachhap /* TODO: take action based on particular interrupt */ 920a7331f72SBartlomiej Zolnierkiewicz data->tmu_clear_irqs(data); 921b8d582b9SAmit Daniel Kachhap 92259dfa54cSAmit Daniel Kachhap clk_disable(data->clk); 92359dfa54cSAmit Daniel Kachhap mutex_unlock(&data->lock); 92459dfa54cSAmit Daniel Kachhap enable_irq(data->irq); 92559dfa54cSAmit Daniel Kachhap } 92659dfa54cSAmit Daniel Kachhap 927a7331f72SBartlomiej Zolnierkiewicz static void exynos4210_tmu_clear_irqs(struct exynos_tmu_data *data) 928a7331f72SBartlomiej Zolnierkiewicz { 929a7331f72SBartlomiej Zolnierkiewicz unsigned int val_irq; 930a7331f72SBartlomiej Zolnierkiewicz u32 tmu_intstat, tmu_intclear; 931a7331f72SBartlomiej Zolnierkiewicz 932a7331f72SBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS5260) { 933a7331f72SBartlomiej Zolnierkiewicz tmu_intstat = EXYNOS5260_TMU_REG_INTSTAT; 934a7331f72SBartlomiej Zolnierkiewicz tmu_intclear = EXYNOS5260_TMU_REG_INTCLEAR; 9356c247393SAbhilash Kesavan } else if (data->soc == SOC_ARCH_EXYNOS7) { 9366c247393SAbhilash Kesavan tmu_intstat = EXYNOS7_TMU_REG_INTPEND; 9376c247393SAbhilash Kesavan tmu_intclear = EXYNOS7_TMU_REG_INTPEND; 938488c7455SChanwoo Choi } else if (data->soc == SOC_ARCH_EXYNOS5433) { 939488c7455SChanwoo Choi tmu_intstat = EXYNOS5433_TMU_REG_INTPEND; 940488c7455SChanwoo Choi tmu_intclear = EXYNOS5433_TMU_REG_INTPEND; 941a7331f72SBartlomiej Zolnierkiewicz } else { 942a7331f72SBartlomiej Zolnierkiewicz tmu_intstat = EXYNOS_TMU_REG_INTSTAT; 943a7331f72SBartlomiej Zolnierkiewicz tmu_intclear = EXYNOS_TMU_REG_INTCLEAR; 944a7331f72SBartlomiej Zolnierkiewicz } 945a7331f72SBartlomiej Zolnierkiewicz 946a7331f72SBartlomiej Zolnierkiewicz val_irq = readl(data->base + tmu_intstat); 947a7331f72SBartlomiej Zolnierkiewicz /* 948a7331f72SBartlomiej Zolnierkiewicz * Clear the interrupts. Please note that the documentation for 949a7331f72SBartlomiej Zolnierkiewicz * Exynos3250, Exynos4412, Exynos5250 and Exynos5260 incorrectly 950a7331f72SBartlomiej Zolnierkiewicz * states that INTCLEAR register has a different placing of bits 951a7331f72SBartlomiej Zolnierkiewicz * responsible for FALL IRQs than INTSTAT register. Exynos5420 952a7331f72SBartlomiej Zolnierkiewicz * and Exynos5440 documentation is correct (Exynos4210 doesn't 953a7331f72SBartlomiej Zolnierkiewicz * support FALL IRQs at all). 954a7331f72SBartlomiej Zolnierkiewicz */ 955a7331f72SBartlomiej Zolnierkiewicz writel(val_irq, data->base + tmu_intclear); 956a7331f72SBartlomiej Zolnierkiewicz } 957a7331f72SBartlomiej Zolnierkiewicz 95859dfa54cSAmit Daniel Kachhap static irqreturn_t exynos_tmu_irq(int irq, void *id) 95959dfa54cSAmit Daniel Kachhap { 96059dfa54cSAmit Daniel Kachhap struct exynos_tmu_data *data = id; 96159dfa54cSAmit Daniel Kachhap 96259dfa54cSAmit Daniel Kachhap disable_irq_nosync(irq); 96359dfa54cSAmit Daniel Kachhap schedule_work(&data->irq_work); 96459dfa54cSAmit Daniel Kachhap 96559dfa54cSAmit Daniel Kachhap return IRQ_HANDLED; 96659dfa54cSAmit Daniel Kachhap } 96759dfa54cSAmit Daniel Kachhap 96859dfa54cSAmit Daniel Kachhap static const struct of_device_id exynos_tmu_match[] = { 969fee88e2bSMaciej Purski { 970fee88e2bSMaciej Purski .compatible = "samsung,exynos3250-tmu", 971fee88e2bSMaciej Purski .data = (const void *)SOC_ARCH_EXYNOS3250, 972fee88e2bSMaciej Purski }, { 973fee88e2bSMaciej Purski .compatible = "samsung,exynos4210-tmu", 974fee88e2bSMaciej Purski .data = (const void *)SOC_ARCH_EXYNOS4210, 975fee88e2bSMaciej Purski }, { 976fee88e2bSMaciej Purski .compatible = "samsung,exynos4412-tmu", 977fee88e2bSMaciej Purski .data = (const void *)SOC_ARCH_EXYNOS4412, 978fee88e2bSMaciej Purski }, { 979fee88e2bSMaciej Purski .compatible = "samsung,exynos5250-tmu", 980fee88e2bSMaciej Purski .data = (const void *)SOC_ARCH_EXYNOS5250, 981fee88e2bSMaciej Purski }, { 982fee88e2bSMaciej Purski .compatible = "samsung,exynos5260-tmu", 983fee88e2bSMaciej Purski .data = (const void *)SOC_ARCH_EXYNOS5260, 984fee88e2bSMaciej Purski }, { 985fee88e2bSMaciej Purski .compatible = "samsung,exynos5420-tmu", 986fee88e2bSMaciej Purski .data = (const void *)SOC_ARCH_EXYNOS5420, 987fee88e2bSMaciej Purski }, { 988fee88e2bSMaciej Purski .compatible = "samsung,exynos5420-tmu-ext-triminfo", 989fee88e2bSMaciej Purski .data = (const void *)SOC_ARCH_EXYNOS5420_TRIMINFO, 990fee88e2bSMaciej Purski }, { 991fee88e2bSMaciej Purski .compatible = "samsung,exynos5433-tmu", 992fee88e2bSMaciej Purski .data = (const void *)SOC_ARCH_EXYNOS5433, 993fee88e2bSMaciej Purski }, { 994fee88e2bSMaciej Purski .compatible = "samsung,exynos7-tmu", 995fee88e2bSMaciej Purski .data = (const void *)SOC_ARCH_EXYNOS7, 996fee88e2bSMaciej Purski }, 997fee88e2bSMaciej Purski { }, 99859dfa54cSAmit Daniel Kachhap }; 99959dfa54cSAmit Daniel Kachhap MODULE_DEVICE_TABLE(of, exynos_tmu_match); 100059dfa54cSAmit Daniel Kachhap 1001cebe7373SAmit Daniel Kachhap static int exynos_map_dt_data(struct platform_device *pdev) 100259dfa54cSAmit Daniel Kachhap { 1003cebe7373SAmit Daniel Kachhap struct exynos_tmu_data *data = platform_get_drvdata(pdev); 1004cebe7373SAmit Daniel Kachhap struct resource res; 100559dfa54cSAmit Daniel Kachhap 100673b5b1d7SSachin Kamat if (!data || !pdev->dev.of_node) 1007cebe7373SAmit Daniel Kachhap return -ENODEV; 100859dfa54cSAmit Daniel Kachhap 1009cebe7373SAmit Daniel Kachhap data->id = of_alias_get_id(pdev->dev.of_node, "tmuctrl"); 1010cebe7373SAmit Daniel Kachhap if (data->id < 0) 1011cebe7373SAmit Daniel Kachhap data->id = 0; 1012cebe7373SAmit Daniel Kachhap 1013cebe7373SAmit Daniel Kachhap data->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); 1014cebe7373SAmit Daniel Kachhap if (data->irq <= 0) { 1015cebe7373SAmit Daniel Kachhap dev_err(&pdev->dev, "failed to get IRQ\n"); 1016cebe7373SAmit Daniel Kachhap return -ENODEV; 1017cebe7373SAmit Daniel Kachhap } 1018cebe7373SAmit Daniel Kachhap 1019cebe7373SAmit Daniel Kachhap if (of_address_to_resource(pdev->dev.of_node, 0, &res)) { 1020cebe7373SAmit Daniel Kachhap dev_err(&pdev->dev, "failed to get Resource 0\n"); 1021cebe7373SAmit Daniel Kachhap return -ENODEV; 1022cebe7373SAmit Daniel Kachhap } 1023cebe7373SAmit Daniel Kachhap 1024cebe7373SAmit Daniel Kachhap data->base = devm_ioremap(&pdev->dev, res.start, resource_size(&res)); 1025cebe7373SAmit Daniel Kachhap if (!data->base) { 1026cebe7373SAmit Daniel Kachhap dev_err(&pdev->dev, "Failed to ioremap memory\n"); 1027cebe7373SAmit Daniel Kachhap return -EADDRNOTAVAIL; 1028cebe7373SAmit Daniel Kachhap } 1029cebe7373SAmit Daniel Kachhap 1030fee88e2bSMaciej Purski data->soc = (enum soc_type)of_device_get_match_data(&pdev->dev); 103156adb9efSBartlomiej Zolnierkiewicz 103256adb9efSBartlomiej Zolnierkiewicz switch (data->soc) { 103356adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS4210: 103456adb9efSBartlomiej Zolnierkiewicz data->tmu_initialize = exynos4210_tmu_initialize; 103556adb9efSBartlomiej Zolnierkiewicz data->tmu_control = exynos4210_tmu_control; 103656adb9efSBartlomiej Zolnierkiewicz data->tmu_read = exynos4210_tmu_read; 103756adb9efSBartlomiej Zolnierkiewicz data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; 10383a3a5f15SKrzysztof Kozlowski data->ntrip = 4; 1039fccfe099SBartlomiej Zolnierkiewicz data->gain = 15; 104061020d18SBartlomiej Zolnierkiewicz data->reference_voltage = 7; 1041e3ed3649SBartlomiej Zolnierkiewicz data->efuse_value = 55; 1042e3ed3649SBartlomiej Zolnierkiewicz data->min_efuse_value = 40; 1043e3ed3649SBartlomiej Zolnierkiewicz data->max_efuse_value = 100; 104456adb9efSBartlomiej Zolnierkiewicz break; 104556adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS3250: 104656adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS4412: 104756adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS5250: 104856adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS5260: 104956adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS5420: 105056adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS5420_TRIMINFO: 105156adb9efSBartlomiej Zolnierkiewicz data->tmu_initialize = exynos4412_tmu_initialize; 105256adb9efSBartlomiej Zolnierkiewicz data->tmu_control = exynos4210_tmu_control; 105356adb9efSBartlomiej Zolnierkiewicz data->tmu_read = exynos4412_tmu_read; 105456adb9efSBartlomiej Zolnierkiewicz data->tmu_set_emulation = exynos4412_tmu_set_emulation; 105556adb9efSBartlomiej Zolnierkiewicz data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; 10563a3a5f15SKrzysztof Kozlowski data->ntrip = 4; 1057fccfe099SBartlomiej Zolnierkiewicz data->gain = 8; 105861020d18SBartlomiej Zolnierkiewicz data->reference_voltage = 16; 1059e3ed3649SBartlomiej Zolnierkiewicz data->efuse_value = 55; 1060e3ed3649SBartlomiej Zolnierkiewicz if (data->soc != SOC_ARCH_EXYNOS5420 && 1061e3ed3649SBartlomiej Zolnierkiewicz data->soc != SOC_ARCH_EXYNOS5420_TRIMINFO) 1062e3ed3649SBartlomiej Zolnierkiewicz data->min_efuse_value = 40; 1063e3ed3649SBartlomiej Zolnierkiewicz else 1064e3ed3649SBartlomiej Zolnierkiewicz data->min_efuse_value = 0; 1065e3ed3649SBartlomiej Zolnierkiewicz data->max_efuse_value = 100; 106656adb9efSBartlomiej Zolnierkiewicz break; 1067488c7455SChanwoo Choi case SOC_ARCH_EXYNOS5433: 1068488c7455SChanwoo Choi data->tmu_initialize = exynos5433_tmu_initialize; 1069488c7455SChanwoo Choi data->tmu_control = exynos5433_tmu_control; 1070488c7455SChanwoo Choi data->tmu_read = exynos4412_tmu_read; 1071488c7455SChanwoo Choi data->tmu_set_emulation = exynos4412_tmu_set_emulation; 1072488c7455SChanwoo Choi data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; 10733a3a5f15SKrzysztof Kozlowski data->ntrip = 8; 1074fccfe099SBartlomiej Zolnierkiewicz data->gain = 8; 107561020d18SBartlomiej Zolnierkiewicz if (res.start == EXYNOS5433_G3D_BASE) 107661020d18SBartlomiej Zolnierkiewicz data->reference_voltage = 23; 107761020d18SBartlomiej Zolnierkiewicz else 107861020d18SBartlomiej Zolnierkiewicz data->reference_voltage = 16; 1079e3ed3649SBartlomiej Zolnierkiewicz data->efuse_value = 75; 1080e3ed3649SBartlomiej Zolnierkiewicz data->min_efuse_value = 40; 1081e3ed3649SBartlomiej Zolnierkiewicz data->max_efuse_value = 150; 1082488c7455SChanwoo Choi break; 10836c247393SAbhilash Kesavan case SOC_ARCH_EXYNOS7: 10846c247393SAbhilash Kesavan data->tmu_initialize = exynos7_tmu_initialize; 10856c247393SAbhilash Kesavan data->tmu_control = exynos7_tmu_control; 10866c247393SAbhilash Kesavan data->tmu_read = exynos7_tmu_read; 10876c247393SAbhilash Kesavan data->tmu_set_emulation = exynos4412_tmu_set_emulation; 10886c247393SAbhilash Kesavan data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; 10893a3a5f15SKrzysztof Kozlowski data->ntrip = 8; 1090fccfe099SBartlomiej Zolnierkiewicz data->gain = 9; 109161020d18SBartlomiej Zolnierkiewicz data->reference_voltage = 17; 1092e3ed3649SBartlomiej Zolnierkiewicz data->efuse_value = 75; 1093e3ed3649SBartlomiej Zolnierkiewicz data->min_efuse_value = 15; 1094e3ed3649SBartlomiej Zolnierkiewicz data->max_efuse_value = 100; 10956c247393SAbhilash Kesavan break; 109656adb9efSBartlomiej Zolnierkiewicz default: 109756adb9efSBartlomiej Zolnierkiewicz dev_err(&pdev->dev, "Platform not supported\n"); 109856adb9efSBartlomiej Zolnierkiewicz return -EINVAL; 109956adb9efSBartlomiej Zolnierkiewicz } 110056adb9efSBartlomiej Zolnierkiewicz 1101199b3e3cSBartlomiej Zolnierkiewicz data->cal_type = TYPE_ONE_POINT_TRIMMING; 1102199b3e3cSBartlomiej Zolnierkiewicz 1103d9b6ee14SAmit Daniel Kachhap /* 1104d9b6ee14SAmit Daniel Kachhap * Check if the TMU shares some registers and then try to map the 1105d9b6ee14SAmit Daniel Kachhap * memory of common registers. 1106d9b6ee14SAmit Daniel Kachhap */ 11078014220dSKrzysztof Kozlowski if (data->soc != SOC_ARCH_EXYNOS5420_TRIMINFO) 1108d9b6ee14SAmit Daniel Kachhap return 0; 1109d9b6ee14SAmit Daniel Kachhap 1110d9b6ee14SAmit Daniel Kachhap if (of_address_to_resource(pdev->dev.of_node, 1, &res)) { 1111d9b6ee14SAmit Daniel Kachhap dev_err(&pdev->dev, "failed to get Resource 1\n"); 1112d9b6ee14SAmit Daniel Kachhap return -ENODEV; 1113d9b6ee14SAmit Daniel Kachhap } 1114d9b6ee14SAmit Daniel Kachhap 11159025d563SNaveen Krishna Chatradhi data->base_second = devm_ioremap(&pdev->dev, res.start, 1116d9b6ee14SAmit Daniel Kachhap resource_size(&res)); 11179025d563SNaveen Krishna Chatradhi if (!data->base_second) { 1118d9b6ee14SAmit Daniel Kachhap dev_err(&pdev->dev, "Failed to ioremap memory\n"); 1119d9b6ee14SAmit Daniel Kachhap return -ENOMEM; 1120d9b6ee14SAmit Daniel Kachhap } 1121cebe7373SAmit Daniel Kachhap 1122cebe7373SAmit Daniel Kachhap return 0; 1123cebe7373SAmit Daniel Kachhap } 1124cebe7373SAmit Daniel Kachhap 1125c3c04d9dSJulia Lawall static const struct thermal_zone_of_device_ops exynos_sensor_ops = { 11263b6a1a80SLukasz Majewski .get_temp = exynos_get_temp, 11273b6a1a80SLukasz Majewski .set_emul_temp = exynos_tmu_set_emulation, 11283b6a1a80SLukasz Majewski }; 11293b6a1a80SLukasz Majewski 1130cebe7373SAmit Daniel Kachhap static int exynos_tmu_probe(struct platform_device *pdev) 1131cebe7373SAmit Daniel Kachhap { 11323b6a1a80SLukasz Majewski struct exynos_tmu_data *data; 11333b6a1a80SLukasz Majewski int ret; 1134cebe7373SAmit Daniel Kachhap 113559dfa54cSAmit Daniel Kachhap data = devm_kzalloc(&pdev->dev, sizeof(struct exynos_tmu_data), 113659dfa54cSAmit Daniel Kachhap GFP_KERNEL); 11372a9675b3SJingoo Han if (!data) 113859dfa54cSAmit Daniel Kachhap return -ENOMEM; 113959dfa54cSAmit Daniel Kachhap 1140cebe7373SAmit Daniel Kachhap platform_set_drvdata(pdev, data); 1141cebe7373SAmit Daniel Kachhap mutex_init(&data->lock); 1142cebe7373SAmit Daniel Kachhap 1143824ead03SKrzysztof Kozlowski /* 1144824ead03SKrzysztof Kozlowski * Try enabling the regulator if found 1145824ead03SKrzysztof Kozlowski * TODO: Add regulator as an SOC feature, so that regulator enable 1146824ead03SKrzysztof Kozlowski * is a compulsory call. 1147824ead03SKrzysztof Kozlowski */ 11484d3583cdSJavier Martinez Canillas data->regulator = devm_regulator_get_optional(&pdev->dev, "vtmu"); 1149824ead03SKrzysztof Kozlowski if (!IS_ERR(data->regulator)) { 1150824ead03SKrzysztof Kozlowski ret = regulator_enable(data->regulator); 1151824ead03SKrzysztof Kozlowski if (ret) { 1152824ead03SKrzysztof Kozlowski dev_err(&pdev->dev, "failed to enable vtmu\n"); 1153824ead03SKrzysztof Kozlowski return ret; 11543b6a1a80SLukasz Majewski } 1155824ead03SKrzysztof Kozlowski } else { 1156ccb361d2SJavier Martinez Canillas if (PTR_ERR(data->regulator) == -EPROBE_DEFER) 1157ccb361d2SJavier Martinez Canillas return -EPROBE_DEFER; 1158824ead03SKrzysztof Kozlowski dev_info(&pdev->dev, "Regulator node (vtmu) not found\n"); 1159824ead03SKrzysztof Kozlowski } 1160824ead03SKrzysztof Kozlowski 1161cebe7373SAmit Daniel Kachhap ret = exynos_map_dt_data(pdev); 1162cebe7373SAmit Daniel Kachhap if (ret) 11633b6a1a80SLukasz Majewski goto err_sensor; 1164cebe7373SAmit Daniel Kachhap 116559dfa54cSAmit Daniel Kachhap INIT_WORK(&data->irq_work, exynos_tmu_work); 116659dfa54cSAmit Daniel Kachhap 116759dfa54cSAmit Daniel Kachhap data->clk = devm_clk_get(&pdev->dev, "tmu_apbif"); 116859dfa54cSAmit Daniel Kachhap if (IS_ERR(data->clk)) { 116959dfa54cSAmit Daniel Kachhap dev_err(&pdev->dev, "Failed to get clock\n"); 11703b6a1a80SLukasz Majewski ret = PTR_ERR(data->clk); 11713b6a1a80SLukasz Majewski goto err_sensor; 117259dfa54cSAmit Daniel Kachhap } 117359dfa54cSAmit Daniel Kachhap 117414a11dc7SNaveen Krishna Chatradhi data->clk_sec = devm_clk_get(&pdev->dev, "tmu_triminfo_apbif"); 117514a11dc7SNaveen Krishna Chatradhi if (IS_ERR(data->clk_sec)) { 117614a11dc7SNaveen Krishna Chatradhi if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) { 117714a11dc7SNaveen Krishna Chatradhi dev_err(&pdev->dev, "Failed to get triminfo clock\n"); 11783b6a1a80SLukasz Majewski ret = PTR_ERR(data->clk_sec); 11793b6a1a80SLukasz Majewski goto err_sensor; 118014a11dc7SNaveen Krishna Chatradhi } 118114a11dc7SNaveen Krishna Chatradhi } else { 118214a11dc7SNaveen Krishna Chatradhi ret = clk_prepare(data->clk_sec); 118314a11dc7SNaveen Krishna Chatradhi if (ret) { 118414a11dc7SNaveen Krishna Chatradhi dev_err(&pdev->dev, "Failed to get clock\n"); 11853b6a1a80SLukasz Majewski goto err_sensor; 118614a11dc7SNaveen Krishna Chatradhi } 118714a11dc7SNaveen Krishna Chatradhi } 118814a11dc7SNaveen Krishna Chatradhi 118914a11dc7SNaveen Krishna Chatradhi ret = clk_prepare(data->clk); 119014a11dc7SNaveen Krishna Chatradhi if (ret) { 119114a11dc7SNaveen Krishna Chatradhi dev_err(&pdev->dev, "Failed to get clock\n"); 119214a11dc7SNaveen Krishna Chatradhi goto err_clk_sec; 119314a11dc7SNaveen Krishna Chatradhi } 119459dfa54cSAmit Daniel Kachhap 1195488c7455SChanwoo Choi switch (data->soc) { 1196488c7455SChanwoo Choi case SOC_ARCH_EXYNOS5433: 1197488c7455SChanwoo Choi case SOC_ARCH_EXYNOS7: 11986c247393SAbhilash Kesavan data->sclk = devm_clk_get(&pdev->dev, "tmu_sclk"); 11996c247393SAbhilash Kesavan if (IS_ERR(data->sclk)) { 12006c247393SAbhilash Kesavan dev_err(&pdev->dev, "Failed to get sclk\n"); 12016c247393SAbhilash Kesavan goto err_clk; 12026c247393SAbhilash Kesavan } else { 12036c247393SAbhilash Kesavan ret = clk_prepare_enable(data->sclk); 12046c247393SAbhilash Kesavan if (ret) { 12056c247393SAbhilash Kesavan dev_err(&pdev->dev, "Failed to enable sclk\n"); 12066c247393SAbhilash Kesavan goto err_clk; 12076c247393SAbhilash Kesavan } 12086c247393SAbhilash Kesavan } 1209488c7455SChanwoo Choi break; 1210488c7455SChanwoo Choi default: 1211488c7455SChanwoo Choi break; 1212baba1ebbSKrzysztof Kozlowski } 12136c247393SAbhilash Kesavan 12149e4249b4SKrzysztof Kozlowski /* 12159e4249b4SKrzysztof Kozlowski * data->tzd must be registered before calling exynos_tmu_initialize(), 12169e4249b4SKrzysztof Kozlowski * requesting irq and calling exynos_tmu_control(). 12179e4249b4SKrzysztof Kozlowski */ 12189e4249b4SKrzysztof Kozlowski data->tzd = thermal_zone_of_sensor_register(&pdev->dev, 0, data, 12199e4249b4SKrzysztof Kozlowski &exynos_sensor_ops); 12209e4249b4SKrzysztof Kozlowski if (IS_ERR(data->tzd)) { 12219e4249b4SKrzysztof Kozlowski ret = PTR_ERR(data->tzd); 12229e4249b4SKrzysztof Kozlowski dev_err(&pdev->dev, "Failed to register sensor: %d\n", ret); 12239e4249b4SKrzysztof Kozlowski goto err_sclk; 12249e4249b4SKrzysztof Kozlowski } 122559dfa54cSAmit Daniel Kachhap 122659dfa54cSAmit Daniel Kachhap ret = exynos_tmu_initialize(pdev); 122759dfa54cSAmit Daniel Kachhap if (ret) { 122859dfa54cSAmit Daniel Kachhap dev_err(&pdev->dev, "Failed to initialize TMU\n"); 12299e4249b4SKrzysztof Kozlowski goto err_thermal; 123059dfa54cSAmit Daniel Kachhap } 123159dfa54cSAmit Daniel Kachhap 1232cebe7373SAmit Daniel Kachhap ret = devm_request_irq(&pdev->dev, data->irq, exynos_tmu_irq, 1233cebe7373SAmit Daniel Kachhap IRQF_TRIGGER_RISING | IRQF_SHARED, dev_name(&pdev->dev), data); 1234cebe7373SAmit Daniel Kachhap if (ret) { 1235cebe7373SAmit Daniel Kachhap dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq); 12369e4249b4SKrzysztof Kozlowski goto err_thermal; 1237cebe7373SAmit Daniel Kachhap } 123859dfa54cSAmit Daniel Kachhap 12393b6a1a80SLukasz Majewski exynos_tmu_control(pdev, true); 124059dfa54cSAmit Daniel Kachhap return 0; 12419e4249b4SKrzysztof Kozlowski 12429e4249b4SKrzysztof Kozlowski err_thermal: 12439e4249b4SKrzysztof Kozlowski thermal_zone_of_sensor_unregister(&pdev->dev, data->tzd); 12446c247393SAbhilash Kesavan err_sclk: 12456c247393SAbhilash Kesavan clk_disable_unprepare(data->sclk); 124659dfa54cSAmit Daniel Kachhap err_clk: 124759dfa54cSAmit Daniel Kachhap clk_unprepare(data->clk); 124814a11dc7SNaveen Krishna Chatradhi err_clk_sec: 124914a11dc7SNaveen Krishna Chatradhi if (!IS_ERR(data->clk_sec)) 125014a11dc7SNaveen Krishna Chatradhi clk_unprepare(data->clk_sec); 12513b6a1a80SLukasz Majewski err_sensor: 1252bfa26838SKrzysztof Kozlowski if (!IS_ERR(data->regulator)) 12535f09a5cbSKrzysztof Kozlowski regulator_disable(data->regulator); 12543b6a1a80SLukasz Majewski 125559dfa54cSAmit Daniel Kachhap return ret; 125659dfa54cSAmit Daniel Kachhap } 125759dfa54cSAmit Daniel Kachhap 125859dfa54cSAmit Daniel Kachhap static int exynos_tmu_remove(struct platform_device *pdev) 125959dfa54cSAmit Daniel Kachhap { 126059dfa54cSAmit Daniel Kachhap struct exynos_tmu_data *data = platform_get_drvdata(pdev); 12613b6a1a80SLukasz Majewski struct thermal_zone_device *tzd = data->tzd; 126259dfa54cSAmit Daniel Kachhap 12633b6a1a80SLukasz Majewski thermal_zone_of_sensor_unregister(&pdev->dev, tzd); 12644215688eSBartlomiej Zolnierkiewicz exynos_tmu_control(pdev, false); 12654215688eSBartlomiej Zolnierkiewicz 12666c247393SAbhilash Kesavan clk_disable_unprepare(data->sclk); 126759dfa54cSAmit Daniel Kachhap clk_unprepare(data->clk); 126814a11dc7SNaveen Krishna Chatradhi if (!IS_ERR(data->clk_sec)) 126914a11dc7SNaveen Krishna Chatradhi clk_unprepare(data->clk_sec); 127059dfa54cSAmit Daniel Kachhap 1271498d22f6SAmit Daniel Kachhap if (!IS_ERR(data->regulator)) 1272498d22f6SAmit Daniel Kachhap regulator_disable(data->regulator); 1273498d22f6SAmit Daniel Kachhap 127459dfa54cSAmit Daniel Kachhap return 0; 127559dfa54cSAmit Daniel Kachhap } 127659dfa54cSAmit Daniel Kachhap 127759dfa54cSAmit Daniel Kachhap #ifdef CONFIG_PM_SLEEP 127859dfa54cSAmit Daniel Kachhap static int exynos_tmu_suspend(struct device *dev) 127959dfa54cSAmit Daniel Kachhap { 128059dfa54cSAmit Daniel Kachhap exynos_tmu_control(to_platform_device(dev), false); 128159dfa54cSAmit Daniel Kachhap 128259dfa54cSAmit Daniel Kachhap return 0; 128359dfa54cSAmit Daniel Kachhap } 128459dfa54cSAmit Daniel Kachhap 128559dfa54cSAmit Daniel Kachhap static int exynos_tmu_resume(struct device *dev) 128659dfa54cSAmit Daniel Kachhap { 128759dfa54cSAmit Daniel Kachhap struct platform_device *pdev = to_platform_device(dev); 128859dfa54cSAmit Daniel Kachhap 128959dfa54cSAmit Daniel Kachhap exynos_tmu_initialize(pdev); 129059dfa54cSAmit Daniel Kachhap exynos_tmu_control(pdev, true); 129159dfa54cSAmit Daniel Kachhap 129259dfa54cSAmit Daniel Kachhap return 0; 129359dfa54cSAmit Daniel Kachhap } 129459dfa54cSAmit Daniel Kachhap 129559dfa54cSAmit Daniel Kachhap static SIMPLE_DEV_PM_OPS(exynos_tmu_pm, 129659dfa54cSAmit Daniel Kachhap exynos_tmu_suspend, exynos_tmu_resume); 129759dfa54cSAmit Daniel Kachhap #define EXYNOS_TMU_PM (&exynos_tmu_pm) 129859dfa54cSAmit Daniel Kachhap #else 129959dfa54cSAmit Daniel Kachhap #define EXYNOS_TMU_PM NULL 130059dfa54cSAmit Daniel Kachhap #endif 130159dfa54cSAmit Daniel Kachhap 130259dfa54cSAmit Daniel Kachhap static struct platform_driver exynos_tmu_driver = { 130359dfa54cSAmit Daniel Kachhap .driver = { 130459dfa54cSAmit Daniel Kachhap .name = "exynos-tmu", 130559dfa54cSAmit Daniel Kachhap .pm = EXYNOS_TMU_PM, 130673b5b1d7SSachin Kamat .of_match_table = exynos_tmu_match, 130759dfa54cSAmit Daniel Kachhap }, 130859dfa54cSAmit Daniel Kachhap .probe = exynos_tmu_probe, 130959dfa54cSAmit Daniel Kachhap .remove = exynos_tmu_remove, 131059dfa54cSAmit Daniel Kachhap }; 131159dfa54cSAmit Daniel Kachhap 131259dfa54cSAmit Daniel Kachhap module_platform_driver(exynos_tmu_driver); 131359dfa54cSAmit Daniel Kachhap 131459dfa54cSAmit Daniel Kachhap MODULE_DESCRIPTION("EXYNOS TMU Driver"); 131559dfa54cSAmit Daniel Kachhap MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>"); 131659dfa54cSAmit Daniel Kachhap MODULE_LICENSE("GPL"); 131759dfa54cSAmit Daniel Kachhap MODULE_ALIAS("platform:exynos-tmu"); 1318