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_TMU_REG_CONTROL1 0x024 100488c7455SChanwoo Choi #define EXYNOS5433_TMU_SAMPLING_INTERVAL 0x02c 101488c7455SChanwoo Choi #define EXYNOS5433_TMU_COUNTER_VALUE0 0x030 102488c7455SChanwoo Choi #define EXYNOS5433_TMU_COUNTER_VALUE1 0x034 103488c7455SChanwoo Choi #define EXYNOS5433_TMU_REG_CURRENT_TEMP1 0x044 104488c7455SChanwoo Choi #define EXYNOS5433_THD_TEMP_RISE3_0 0x050 105488c7455SChanwoo Choi #define EXYNOS5433_THD_TEMP_RISE7_4 0x054 106488c7455SChanwoo Choi #define EXYNOS5433_THD_TEMP_FALL3_0 0x060 107488c7455SChanwoo Choi #define EXYNOS5433_THD_TEMP_FALL7_4 0x064 108488c7455SChanwoo Choi #define EXYNOS5433_TMU_REG_INTEN 0x0c0 109488c7455SChanwoo Choi #define EXYNOS5433_TMU_REG_INTPEND 0x0c8 110488c7455SChanwoo Choi #define EXYNOS5433_TMU_EMUL_CON 0x110 111488c7455SChanwoo Choi #define EXYNOS5433_TMU_PD_DET_EN 0x130 112488c7455SChanwoo Choi 113488c7455SChanwoo Choi #define EXYNOS5433_TRIMINFO_SENSOR_ID_SHIFT 16 114488c7455SChanwoo Choi #define EXYNOS5433_TRIMINFO_CALIB_SEL_SHIFT 23 115488c7455SChanwoo Choi #define EXYNOS5433_TRIMINFO_SENSOR_ID_MASK \ 116488c7455SChanwoo Choi (0xf << EXYNOS5433_TRIMINFO_SENSOR_ID_SHIFT) 117488c7455SChanwoo Choi #define EXYNOS5433_TRIMINFO_CALIB_SEL_MASK BIT(23) 118488c7455SChanwoo Choi 119488c7455SChanwoo Choi #define EXYNOS5433_TRIMINFO_ONE_POINT_TRIMMING 0 120488c7455SChanwoo Choi #define EXYNOS5433_TRIMINFO_TWO_POINT_TRIMMING 1 121488c7455SChanwoo Choi 122488c7455SChanwoo Choi #define EXYNOS5433_PD_DET_EN 1 123488c7455SChanwoo Choi 12461020d18SBartlomiej Zolnierkiewicz #define EXYNOS5433_G3D_BASE 0x10070000 12561020d18SBartlomiej Zolnierkiewicz 1266c247393SAbhilash Kesavan /* Exynos7 specific registers */ 1276c247393SAbhilash Kesavan #define EXYNOS7_THD_TEMP_RISE7_6 0x50 1286c247393SAbhilash Kesavan #define EXYNOS7_THD_TEMP_FALL7_6 0x60 1296c247393SAbhilash Kesavan #define EXYNOS7_TMU_REG_INTEN 0x110 1306c247393SAbhilash Kesavan #define EXYNOS7_TMU_REG_INTPEND 0x118 1316c247393SAbhilash Kesavan #define EXYNOS7_TMU_REG_EMUL_CON 0x160 1326c247393SAbhilash Kesavan 1336c247393SAbhilash Kesavan #define EXYNOS7_TMU_TEMP_MASK 0x1ff 1346c247393SAbhilash Kesavan #define EXYNOS7_PD_DET_EN_SHIFT 23 1356c247393SAbhilash Kesavan #define EXYNOS7_TMU_INTEN_RISE0_SHIFT 0 1366c247393SAbhilash Kesavan #define EXYNOS7_EMUL_DATA_SHIFT 7 1376c247393SAbhilash Kesavan #define EXYNOS7_EMUL_DATA_MASK 0x1ff 1386c247393SAbhilash Kesavan 139718b4ca1SBartlomiej Zolnierkiewicz #define EXYNOS_FIRST_POINT_TRIM 25 140718b4ca1SBartlomiej Zolnierkiewicz #define EXYNOS_SECOND_POINT_TRIM 85 141718b4ca1SBartlomiej Zolnierkiewicz 14209d29426SBartlomiej Zolnierkiewicz #define EXYNOS_NOISE_CANCEL_MODE 4 14309d29426SBartlomiej Zolnierkiewicz 1443b6a1a80SLukasz Majewski #define MCELSIUS 1000 1457efd18a2SBartlomiej Zolnierkiewicz 1467efd18a2SBartlomiej Zolnierkiewicz enum soc_type { 1477efd18a2SBartlomiej Zolnierkiewicz SOC_ARCH_EXYNOS3250 = 1, 1487efd18a2SBartlomiej Zolnierkiewicz SOC_ARCH_EXYNOS4210, 1497efd18a2SBartlomiej Zolnierkiewicz SOC_ARCH_EXYNOS4412, 1507efd18a2SBartlomiej Zolnierkiewicz SOC_ARCH_EXYNOS5250, 1517efd18a2SBartlomiej Zolnierkiewicz SOC_ARCH_EXYNOS5260, 1527efd18a2SBartlomiej Zolnierkiewicz SOC_ARCH_EXYNOS5420, 1537efd18a2SBartlomiej Zolnierkiewicz SOC_ARCH_EXYNOS5420_TRIMINFO, 1547efd18a2SBartlomiej Zolnierkiewicz SOC_ARCH_EXYNOS5433, 1557efd18a2SBartlomiej Zolnierkiewicz SOC_ARCH_EXYNOS7, 1567efd18a2SBartlomiej Zolnierkiewicz }; 1577efd18a2SBartlomiej Zolnierkiewicz 158cebe7373SAmit Daniel Kachhap /** 159cebe7373SAmit Daniel Kachhap * struct exynos_tmu_data : A structure to hold the private data of the TMU 160cebe7373SAmit Daniel Kachhap driver 161cebe7373SAmit Daniel Kachhap * @id: identifier of the one instance of the TMU controller. 162cebe7373SAmit Daniel Kachhap * @base: base address of the single instance of the TMU controller. 1639025d563SNaveen Krishna Chatradhi * @base_second: base address of the common registers of the TMU controller. 164cebe7373SAmit Daniel Kachhap * @irq: irq number of the TMU controller. 165cebe7373SAmit Daniel Kachhap * @soc: id of the SOC type. 166cebe7373SAmit Daniel Kachhap * @irq_work: pointer to the irq work structure. 167cebe7373SAmit Daniel Kachhap * @lock: lock to implement synchronization. 168cebe7373SAmit Daniel Kachhap * @clk: pointer to the clock structure. 16914a11dc7SNaveen Krishna Chatradhi * @clk_sec: pointer to the clock structure for accessing the base_second. 1706c247393SAbhilash Kesavan * @sclk: pointer to the clock structure for accessing the tmu special clk. 171199b3e3cSBartlomiej Zolnierkiewicz * @cal_type: calibration type for temperature 172e3ed3649SBartlomiej Zolnierkiewicz * @efuse_value: SoC defined fuse value 173e3ed3649SBartlomiej Zolnierkiewicz * @min_efuse_value: minimum valid trimming data 174e3ed3649SBartlomiej Zolnierkiewicz * @max_efuse_value: maximum valid trimming data 175cebe7373SAmit Daniel Kachhap * @temp_error1: fused value of the first point trim. 176cebe7373SAmit Daniel Kachhap * @temp_error2: fused value of the second point trim. 177fccfe099SBartlomiej Zolnierkiewicz * @gain: gain of amplifier in the positive-TC generator block 178fccfe099SBartlomiej Zolnierkiewicz * 0 < gain <= 15 17961020d18SBartlomiej Zolnierkiewicz * @reference_voltage: reference voltage of amplifier 18061020d18SBartlomiej Zolnierkiewicz * in the positive-TC generator block 18161020d18SBartlomiej Zolnierkiewicz * 0 < reference_voltage <= 31 182498d22f6SAmit Daniel Kachhap * @regulator: pointer to the TMU regulator structure. 183cebe7373SAmit Daniel Kachhap * @reg_conf: pointer to structure to register with core thermal. 1843a3a5f15SKrzysztof Kozlowski * @ntrip: number of supported trip points. 1850eb875d8SMarek Szyprowski * @enabled: current status of TMU device 18672d1100bSBartlomiej Zolnierkiewicz * @tmu_initialize: SoC specific TMU initialization method 18737f9034fSBartlomiej Zolnierkiewicz * @tmu_control: SoC specific TMU control method 188b79985caSBartlomiej Zolnierkiewicz * @tmu_read: SoC specific TMU temperature read method 189285d994aSBartlomiej Zolnierkiewicz * @tmu_set_emulation: SoC specific TMU emulation setting method 190a7331f72SBartlomiej Zolnierkiewicz * @tmu_clear_irqs: SoC specific TMU interrupts clearing method 191cebe7373SAmit Daniel Kachhap */ 19259dfa54cSAmit Daniel Kachhap struct exynos_tmu_data { 193cebe7373SAmit Daniel Kachhap int id; 19459dfa54cSAmit Daniel Kachhap void __iomem *base; 1959025d563SNaveen Krishna Chatradhi void __iomem *base_second; 19659dfa54cSAmit Daniel Kachhap int irq; 19759dfa54cSAmit Daniel Kachhap enum soc_type soc; 19859dfa54cSAmit Daniel Kachhap struct work_struct irq_work; 19959dfa54cSAmit Daniel Kachhap struct mutex lock; 2006c247393SAbhilash Kesavan struct clk *clk, *clk_sec, *sclk; 201199b3e3cSBartlomiej Zolnierkiewicz u32 cal_type; 202e3ed3649SBartlomiej Zolnierkiewicz u32 efuse_value; 203e3ed3649SBartlomiej Zolnierkiewicz u32 min_efuse_value; 204e3ed3649SBartlomiej Zolnierkiewicz u32 max_efuse_value; 2056c247393SAbhilash Kesavan u16 temp_error1, temp_error2; 206fccfe099SBartlomiej Zolnierkiewicz u8 gain; 20761020d18SBartlomiej Zolnierkiewicz u8 reference_voltage; 208498d22f6SAmit Daniel Kachhap struct regulator *regulator; 2093b6a1a80SLukasz Majewski struct thermal_zone_device *tzd; 2103a3a5f15SKrzysztof Kozlowski unsigned int ntrip; 2110eb875d8SMarek Szyprowski bool enabled; 2123b6a1a80SLukasz Majewski 213c8f8f768SBartlomiej Zolnierkiewicz void (*tmu_set_trip_temp)(struct exynos_tmu_data *data, int trip, 214c8f8f768SBartlomiej Zolnierkiewicz u8 temp); 215c8f8f768SBartlomiej Zolnierkiewicz void (*tmu_set_trip_hyst)(struct exynos_tmu_data *data, int trip, 216c8f8f768SBartlomiej Zolnierkiewicz u8 temp, u8 hyst); 217c35268f5SBartlomiej Zolnierkiewicz void (*tmu_initialize)(struct platform_device *pdev); 21837f9034fSBartlomiej Zolnierkiewicz void (*tmu_control)(struct platform_device *pdev, bool on); 219b79985caSBartlomiej Zolnierkiewicz int (*tmu_read)(struct exynos_tmu_data *data); 22017e8351aSSascha Hauer void (*tmu_set_emulation)(struct exynos_tmu_data *data, int temp); 221a7331f72SBartlomiej Zolnierkiewicz void (*tmu_clear_irqs)(struct exynos_tmu_data *data); 22259dfa54cSAmit Daniel Kachhap }; 22359dfa54cSAmit Daniel Kachhap 2243b6a1a80SLukasz Majewski static void exynos_report_trigger(struct exynos_tmu_data *p) 2253b6a1a80SLukasz Majewski { 2263b6a1a80SLukasz Majewski char data[10], *envp[] = { data, NULL }; 2273b6a1a80SLukasz Majewski struct thermal_zone_device *tz = p->tzd; 22817e8351aSSascha Hauer int temp; 2293b6a1a80SLukasz Majewski unsigned int i; 2303b6a1a80SLukasz Majewski 231eccb6014SLukasz Majewski if (!tz) { 232eccb6014SLukasz Majewski pr_err("No thermal zone device defined\n"); 2333b6a1a80SLukasz Majewski return; 2343b6a1a80SLukasz Majewski } 2353b6a1a80SLukasz Majewski 2360e70f466SSrinivas Pandruvada thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); 2373b6a1a80SLukasz Majewski 2383b6a1a80SLukasz Majewski mutex_lock(&tz->lock); 2393b6a1a80SLukasz Majewski /* Find the level for which trip happened */ 2403b6a1a80SLukasz Majewski for (i = 0; i < of_thermal_get_ntrips(tz); i++) { 2413b6a1a80SLukasz Majewski tz->ops->get_trip_temp(tz, i, &temp); 2423b6a1a80SLukasz Majewski if (tz->last_temperature < temp) 2433b6a1a80SLukasz Majewski break; 2443b6a1a80SLukasz Majewski } 2453b6a1a80SLukasz Majewski 2463b6a1a80SLukasz Majewski snprintf(data, sizeof(data), "%u", i); 2473b6a1a80SLukasz Majewski kobject_uevent_env(&tz->device.kobj, KOBJ_CHANGE, envp); 2483b6a1a80SLukasz Majewski mutex_unlock(&tz->lock); 2493b6a1a80SLukasz Majewski } 2503b6a1a80SLukasz Majewski 25159dfa54cSAmit Daniel Kachhap /* 25259dfa54cSAmit Daniel Kachhap * TMU treats temperature as a mapped temperature code. 25359dfa54cSAmit Daniel Kachhap * The temperature is converted differently depending on the calibration type. 25459dfa54cSAmit Daniel Kachhap */ 25559dfa54cSAmit Daniel Kachhap static int temp_to_code(struct exynos_tmu_data *data, u8 temp) 25659dfa54cSAmit Daniel Kachhap { 257199b3e3cSBartlomiej Zolnierkiewicz if (data->cal_type == TYPE_ONE_POINT_TRIMMING) 258718b4ca1SBartlomiej Zolnierkiewicz return temp + data->temp_error1 - EXYNOS_FIRST_POINT_TRIM; 2599c933b1bSBartlomiej Zolnierkiewicz 260718b4ca1SBartlomiej Zolnierkiewicz return (temp - EXYNOS_FIRST_POINT_TRIM) * 26159dfa54cSAmit Daniel Kachhap (data->temp_error2 - data->temp_error1) / 262718b4ca1SBartlomiej Zolnierkiewicz (EXYNOS_SECOND_POINT_TRIM - EXYNOS_FIRST_POINT_TRIM) + 263bb34b4c8SAmit Daniel Kachhap data->temp_error1; 26459dfa54cSAmit Daniel Kachhap } 26559dfa54cSAmit Daniel Kachhap 26659dfa54cSAmit Daniel Kachhap /* 26759dfa54cSAmit Daniel Kachhap * Calculate a temperature value from a temperature code. 26859dfa54cSAmit Daniel Kachhap * The unit of the temperature is degree Celsius. 26959dfa54cSAmit Daniel Kachhap */ 2706c247393SAbhilash Kesavan static int code_to_temp(struct exynos_tmu_data *data, u16 temp_code) 27159dfa54cSAmit Daniel Kachhap { 272199b3e3cSBartlomiej Zolnierkiewicz if (data->cal_type == TYPE_ONE_POINT_TRIMMING) 273718b4ca1SBartlomiej Zolnierkiewicz return temp_code - data->temp_error1 + EXYNOS_FIRST_POINT_TRIM; 2749c933b1bSBartlomiej Zolnierkiewicz 2759c933b1bSBartlomiej Zolnierkiewicz return (temp_code - data->temp_error1) * 276718b4ca1SBartlomiej Zolnierkiewicz (EXYNOS_SECOND_POINT_TRIM - EXYNOS_FIRST_POINT_TRIM) / 277bb34b4c8SAmit Daniel Kachhap (data->temp_error2 - data->temp_error1) + 278718b4ca1SBartlomiej Zolnierkiewicz EXYNOS_FIRST_POINT_TRIM; 27959dfa54cSAmit Daniel Kachhap } 28059dfa54cSAmit Daniel Kachhap 2818328a4b1SBartlomiej Zolnierkiewicz static void sanitize_temp_error(struct exynos_tmu_data *data, u32 trim_info) 282b835ced1SBartlomiej Zolnierkiewicz { 283aef27b65SBartlomiej Zolnierkiewicz u16 tmu_temp_mask = 284aef27b65SBartlomiej Zolnierkiewicz (data->soc == SOC_ARCH_EXYNOS7) ? EXYNOS7_TMU_TEMP_MASK 285aef27b65SBartlomiej Zolnierkiewicz : EXYNOS_TMU_TEMP_MASK; 286aef27b65SBartlomiej Zolnierkiewicz 287aef27b65SBartlomiej Zolnierkiewicz data->temp_error1 = trim_info & tmu_temp_mask; 28899d67fb9SBartlomiej Zolnierkiewicz data->temp_error2 = ((trim_info >> EXYNOS_TRIMINFO_85_SHIFT) & 289b8d582b9SAmit Daniel Kachhap EXYNOS_TMU_TEMP_MASK); 29059dfa54cSAmit Daniel Kachhap 2915000806cSAmit Daniel Kachhap if (!data->temp_error1 || 292e3ed3649SBartlomiej Zolnierkiewicz (data->min_efuse_value > data->temp_error1) || 293e3ed3649SBartlomiej Zolnierkiewicz (data->temp_error1 > data->max_efuse_value)) 294e3ed3649SBartlomiej Zolnierkiewicz data->temp_error1 = data->efuse_value & EXYNOS_TMU_TEMP_MASK; 2955000806cSAmit Daniel Kachhap 2965000806cSAmit Daniel Kachhap if (!data->temp_error2) 2975000806cSAmit Daniel Kachhap data->temp_error2 = 298e3ed3649SBartlomiej Zolnierkiewicz (data->efuse_value >> EXYNOS_TRIMINFO_85_SHIFT) & 2995000806cSAmit Daniel Kachhap EXYNOS_TMU_TEMP_MASK; 3008328a4b1SBartlomiej Zolnierkiewicz } 30159dfa54cSAmit Daniel Kachhap 30259dfa54cSAmit Daniel Kachhap static int exynos_tmu_initialize(struct platform_device *pdev) 30359dfa54cSAmit Daniel Kachhap { 30459dfa54cSAmit Daniel Kachhap struct exynos_tmu_data *data = platform_get_drvdata(pdev); 30575e0f100SBartlomiej Zolnierkiewicz struct thermal_zone_device *tzd = data->tzd; 30675e0f100SBartlomiej Zolnierkiewicz const struct thermal_trip * const trips = 30775e0f100SBartlomiej Zolnierkiewicz of_thermal_get_trip_points(tzd); 30897b3881bSBartlomiej Zolnierkiewicz unsigned int status; 309c8f8f768SBartlomiej Zolnierkiewicz int ret = 0, temp, hyst; 3107ca04e58SAmit Daniel Kachhap 31175e0f100SBartlomiej Zolnierkiewicz if (!trips) { 31275e0f100SBartlomiej Zolnierkiewicz dev_err(&pdev->dev, 31375e0f100SBartlomiej Zolnierkiewicz "Cannot get trip points from device tree!\n"); 31475e0f100SBartlomiej Zolnierkiewicz return -ENODEV; 31575e0f100SBartlomiej Zolnierkiewicz } 31675e0f100SBartlomiej Zolnierkiewicz 3178f1c404bSBartlomiej Zolnierkiewicz if (data->soc != SOC_ARCH_EXYNOS5433) /* FIXME */ 3188f1c404bSBartlomiej Zolnierkiewicz ret = tzd->ops->get_crit_temp(tzd, &temp); 3198f1c404bSBartlomiej Zolnierkiewicz if (ret) { 3208f1c404bSBartlomiej Zolnierkiewicz dev_err(&pdev->dev, 3218f1c404bSBartlomiej Zolnierkiewicz "No CRITICAL trip point defined in device tree!\n"); 3228f1c404bSBartlomiej Zolnierkiewicz goto out; 3238f1c404bSBartlomiej Zolnierkiewicz } 3248f1c404bSBartlomiej Zolnierkiewicz 32575e0f100SBartlomiej Zolnierkiewicz if (of_thermal_get_ntrips(tzd) > data->ntrip) { 3263a3a5f15SKrzysztof Kozlowski dev_info(&pdev->dev, 3273a3a5f15SKrzysztof Kozlowski "More trip points than supported by this TMU.\n"); 3283a3a5f15SKrzysztof Kozlowski dev_info(&pdev->dev, 3293a3a5f15SKrzysztof Kozlowski "%d trip points should be configured in polling mode.\n", 33075e0f100SBartlomiej Zolnierkiewicz (of_thermal_get_ntrips(tzd) - data->ntrip)); 3313a3a5f15SKrzysztof Kozlowski } 3323a3a5f15SKrzysztof Kozlowski 33359dfa54cSAmit Daniel Kachhap mutex_lock(&data->lock); 33459dfa54cSAmit Daniel Kachhap clk_enable(data->clk); 33559dfa54cSAmit Daniel Kachhap if (!IS_ERR(data->clk_sec)) 33659dfa54cSAmit Daniel Kachhap clk_enable(data->clk_sec); 33797b3881bSBartlomiej Zolnierkiewicz 33897b3881bSBartlomiej Zolnierkiewicz status = readb(data->base + EXYNOS_TMU_REG_STATUS); 339fac36bacSBartlomiej Zolnierkiewicz if (!status) { 34097b3881bSBartlomiej Zolnierkiewicz ret = -EBUSY; 341fac36bacSBartlomiej Zolnierkiewicz } else { 342c8f8f768SBartlomiej Zolnierkiewicz int i, ntrips = 343c8f8f768SBartlomiej Zolnierkiewicz min_t(int, of_thermal_get_ntrips(tzd), data->ntrip); 344c8f8f768SBartlomiej Zolnierkiewicz 345c35268f5SBartlomiej Zolnierkiewicz data->tmu_initialize(pdev); 346c8f8f768SBartlomiej Zolnierkiewicz 347c8f8f768SBartlomiej Zolnierkiewicz /* Write temperature code for rising and falling threshold */ 348c8f8f768SBartlomiej Zolnierkiewicz for (i = 0; i < ntrips; i++) { 349c8f8f768SBartlomiej Zolnierkiewicz /* Write temperature code for rising threshold */ 35089335c20SBartlomiej Zolnierkiewicz ret = tzd->ops->get_trip_temp(tzd, i, &temp); 35189335c20SBartlomiej Zolnierkiewicz if (ret) 35289335c20SBartlomiej Zolnierkiewicz goto err; 353c8f8f768SBartlomiej Zolnierkiewicz temp /= MCELSIUS; 354c8f8f768SBartlomiej Zolnierkiewicz data->tmu_set_trip_temp(data, i, temp); 355c8f8f768SBartlomiej Zolnierkiewicz 356c8f8f768SBartlomiej Zolnierkiewicz /* Write temperature code for falling threshold */ 35789335c20SBartlomiej Zolnierkiewicz ret = tzd->ops->get_trip_hyst(tzd, i, &hyst); 35889335c20SBartlomiej Zolnierkiewicz if (ret) 35989335c20SBartlomiej Zolnierkiewicz goto err; 360c8f8f768SBartlomiej Zolnierkiewicz hyst /= MCELSIUS; 361c8f8f768SBartlomiej Zolnierkiewicz data->tmu_set_trip_hyst(data, i, temp, hyst); 362c8f8f768SBartlomiej Zolnierkiewicz } 363c8f8f768SBartlomiej Zolnierkiewicz 364fac36bacSBartlomiej Zolnierkiewicz data->tmu_clear_irqs(data); 365fac36bacSBartlomiej Zolnierkiewicz } 36689335c20SBartlomiej Zolnierkiewicz err: 36759dfa54cSAmit Daniel Kachhap clk_disable(data->clk); 36859dfa54cSAmit Daniel Kachhap mutex_unlock(&data->lock); 36914a11dc7SNaveen Krishna Chatradhi if (!IS_ERR(data->clk_sec)) 37014a11dc7SNaveen Krishna Chatradhi clk_disable(data->clk_sec); 3718f1c404bSBartlomiej Zolnierkiewicz out: 37259dfa54cSAmit Daniel Kachhap return ret; 37359dfa54cSAmit Daniel Kachhap } 37459dfa54cSAmit Daniel Kachhap 375d00671c3SBartlomiej Zolnierkiewicz static u32 get_con_reg(struct exynos_tmu_data *data, u32 con) 37659dfa54cSAmit Daniel Kachhap { 3777575983cSBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS4412 || 3787575983cSBartlomiej Zolnierkiewicz data->soc == SOC_ARCH_EXYNOS3250) 3797575983cSBartlomiej Zolnierkiewicz con |= (EXYNOS4412_MUX_ADDR_VALUE << EXYNOS4412_MUX_ADDR_SHIFT); 38086f5362eSLukasz Majewski 38199d67fb9SBartlomiej Zolnierkiewicz con &= ~(EXYNOS_TMU_REF_VOLTAGE_MASK << EXYNOS_TMU_REF_VOLTAGE_SHIFT); 38261020d18SBartlomiej Zolnierkiewicz con |= data->reference_voltage << EXYNOS_TMU_REF_VOLTAGE_SHIFT; 383d0a0ce3eSAmit Daniel Kachhap 38499d67fb9SBartlomiej Zolnierkiewicz con &= ~(EXYNOS_TMU_BUF_SLOPE_SEL_MASK << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT); 385fccfe099SBartlomiej Zolnierkiewicz con |= (data->gain << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT); 386d0a0ce3eSAmit Daniel Kachhap 387b9504a6aSBartlomiej Zolnierkiewicz con &= ~(EXYNOS_TMU_TRIP_MODE_MASK << EXYNOS_TMU_TRIP_MODE_SHIFT); 38809d29426SBartlomiej Zolnierkiewicz con |= (EXYNOS_NOISE_CANCEL_MODE << EXYNOS_TMU_TRIP_MODE_SHIFT); 38959dfa54cSAmit Daniel Kachhap 390d00671c3SBartlomiej Zolnierkiewicz return con; 391d00671c3SBartlomiej Zolnierkiewicz } 392d00671c3SBartlomiej Zolnierkiewicz 393d00671c3SBartlomiej Zolnierkiewicz static void exynos_tmu_control(struct platform_device *pdev, bool on) 394d00671c3SBartlomiej Zolnierkiewicz { 395d00671c3SBartlomiej Zolnierkiewicz struct exynos_tmu_data *data = platform_get_drvdata(pdev); 396d00671c3SBartlomiej Zolnierkiewicz 397d00671c3SBartlomiej Zolnierkiewicz mutex_lock(&data->lock); 398d00671c3SBartlomiej Zolnierkiewicz clk_enable(data->clk); 39937f9034fSBartlomiej Zolnierkiewicz data->tmu_control(pdev, on); 4000eb875d8SMarek Szyprowski data->enabled = on; 40159dfa54cSAmit Daniel Kachhap clk_disable(data->clk); 40259dfa54cSAmit Daniel Kachhap mutex_unlock(&data->lock); 40359dfa54cSAmit Daniel Kachhap } 40459dfa54cSAmit Daniel Kachhap 405a503a10fSBartlomiej Zolnierkiewicz static void exynos4210_tmu_set_trip_temp(struct exynos_tmu_data *data, 406a503a10fSBartlomiej Zolnierkiewicz int trip, u8 temp) 407a503a10fSBartlomiej Zolnierkiewicz { 408a503a10fSBartlomiej Zolnierkiewicz const struct thermal_trip * const trips = 409a503a10fSBartlomiej Zolnierkiewicz of_thermal_get_trip_points(data->tzd); 410a503a10fSBartlomiej Zolnierkiewicz u8 ref, th_code; 411a503a10fSBartlomiej Zolnierkiewicz 412a503a10fSBartlomiej Zolnierkiewicz ref = trips[0].temperature / MCELSIUS; 413a503a10fSBartlomiej Zolnierkiewicz 414a503a10fSBartlomiej Zolnierkiewicz if (trip == 0) { 415a503a10fSBartlomiej Zolnierkiewicz th_code = temp_to_code(data, ref); 416a503a10fSBartlomiej Zolnierkiewicz writeb(th_code, data->base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP); 417a503a10fSBartlomiej Zolnierkiewicz } 418a503a10fSBartlomiej Zolnierkiewicz 419a503a10fSBartlomiej Zolnierkiewicz temp -= ref; 420a503a10fSBartlomiej Zolnierkiewicz writeb(temp, data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL0 + trip * 4); 421a503a10fSBartlomiej Zolnierkiewicz } 422a503a10fSBartlomiej Zolnierkiewicz 423c8f8f768SBartlomiej Zolnierkiewicz /* failing thresholds are not supported on Exynos4210 */ 424c8f8f768SBartlomiej Zolnierkiewicz static void exynos4210_tmu_set_trip_hyst(struct exynos_tmu_data *data, 425c8f8f768SBartlomiej Zolnierkiewicz int trip, u8 temp, u8 hyst) 426c8f8f768SBartlomiej Zolnierkiewicz { 427c8f8f768SBartlomiej Zolnierkiewicz } 428c8f8f768SBartlomiej Zolnierkiewicz 429c35268f5SBartlomiej Zolnierkiewicz static void exynos4210_tmu_initialize(struct platform_device *pdev) 43072d1100bSBartlomiej Zolnierkiewicz { 43172d1100bSBartlomiej Zolnierkiewicz struct exynos_tmu_data *data = platform_get_drvdata(pdev); 43272d1100bSBartlomiej Zolnierkiewicz 43372d1100bSBartlomiej Zolnierkiewicz sanitize_temp_error(data, readl(data->base + EXYNOS_TMU_REG_TRIMINFO)); 43472d1100bSBartlomiej Zolnierkiewicz } 43572d1100bSBartlomiej Zolnierkiewicz 436a503a10fSBartlomiej Zolnierkiewicz static void exynos4412_tmu_set_trip_temp(struct exynos_tmu_data *data, 437a503a10fSBartlomiej Zolnierkiewicz int trip, u8 temp) 438a503a10fSBartlomiej Zolnierkiewicz { 439a503a10fSBartlomiej Zolnierkiewicz u32 th, con; 440a503a10fSBartlomiej Zolnierkiewicz 441a503a10fSBartlomiej Zolnierkiewicz th = readl(data->base + EXYNOS_THD_TEMP_RISE); 442a503a10fSBartlomiej Zolnierkiewicz th &= ~(0xff << 8 * trip); 443a503a10fSBartlomiej Zolnierkiewicz th |= temp_to_code(data, temp) << 8 * trip; 444a503a10fSBartlomiej Zolnierkiewicz writel(th, data->base + EXYNOS_THD_TEMP_RISE); 445a503a10fSBartlomiej Zolnierkiewicz 446a503a10fSBartlomiej Zolnierkiewicz if (trip == 3) { 447a503a10fSBartlomiej Zolnierkiewicz con = readl(data->base + EXYNOS_TMU_REG_CONTROL); 448a503a10fSBartlomiej Zolnierkiewicz con |= (1 << EXYNOS_TMU_THERM_TRIP_EN_SHIFT); 449a503a10fSBartlomiej Zolnierkiewicz writel(con, data->base + EXYNOS_TMU_REG_CONTROL); 450a503a10fSBartlomiej Zolnierkiewicz } 451a503a10fSBartlomiej Zolnierkiewicz } 452a503a10fSBartlomiej Zolnierkiewicz 453a503a10fSBartlomiej Zolnierkiewicz static void exynos4412_tmu_set_trip_hyst(struct exynos_tmu_data *data, 454a503a10fSBartlomiej Zolnierkiewicz int trip, u8 temp, u8 hyst) 455a503a10fSBartlomiej Zolnierkiewicz { 456a503a10fSBartlomiej Zolnierkiewicz u32 th; 457a503a10fSBartlomiej Zolnierkiewicz 458a503a10fSBartlomiej Zolnierkiewicz th = readl(data->base + EXYNOS_THD_TEMP_FALL); 459a503a10fSBartlomiej Zolnierkiewicz th &= ~(0xff << 8 * trip); 460a503a10fSBartlomiej Zolnierkiewicz if (hyst) 461a503a10fSBartlomiej Zolnierkiewicz th |= temp_to_code(data, temp - hyst) << 8 * trip; 462a503a10fSBartlomiej Zolnierkiewicz writel(th, data->base + EXYNOS_THD_TEMP_FALL); 463a503a10fSBartlomiej Zolnierkiewicz } 464a503a10fSBartlomiej Zolnierkiewicz 465c35268f5SBartlomiej Zolnierkiewicz static void exynos4412_tmu_initialize(struct platform_device *pdev) 46672d1100bSBartlomiej Zolnierkiewicz { 46772d1100bSBartlomiej Zolnierkiewicz struct exynos_tmu_data *data = platform_get_drvdata(pdev); 468a503a10fSBartlomiej Zolnierkiewicz unsigned int trim_info, ctrl; 46972d1100bSBartlomiej Zolnierkiewicz 47072d1100bSBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS3250 || 47172d1100bSBartlomiej Zolnierkiewicz data->soc == SOC_ARCH_EXYNOS4412 || 47272d1100bSBartlomiej Zolnierkiewicz data->soc == SOC_ARCH_EXYNOS5250) { 47372d1100bSBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS3250) { 47472d1100bSBartlomiej Zolnierkiewicz ctrl = readl(data->base + EXYNOS_TMU_TRIMINFO_CON1); 47572d1100bSBartlomiej Zolnierkiewicz ctrl |= EXYNOS_TRIMINFO_RELOAD_ENABLE; 47672d1100bSBartlomiej Zolnierkiewicz writel(ctrl, data->base + EXYNOS_TMU_TRIMINFO_CON1); 47772d1100bSBartlomiej Zolnierkiewicz } 47872d1100bSBartlomiej Zolnierkiewicz ctrl = readl(data->base + EXYNOS_TMU_TRIMINFO_CON2); 47972d1100bSBartlomiej Zolnierkiewicz ctrl |= EXYNOS_TRIMINFO_RELOAD_ENABLE; 48072d1100bSBartlomiej Zolnierkiewicz writel(ctrl, data->base + EXYNOS_TMU_TRIMINFO_CON2); 48172d1100bSBartlomiej Zolnierkiewicz } 48272d1100bSBartlomiej Zolnierkiewicz 48372d1100bSBartlomiej Zolnierkiewicz /* On exynos5420 the triminfo register is in the shared space */ 48472d1100bSBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) 48572d1100bSBartlomiej Zolnierkiewicz trim_info = readl(data->base_second + EXYNOS_TMU_REG_TRIMINFO); 48672d1100bSBartlomiej Zolnierkiewicz else 48772d1100bSBartlomiej Zolnierkiewicz trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); 48872d1100bSBartlomiej Zolnierkiewicz 48972d1100bSBartlomiej Zolnierkiewicz sanitize_temp_error(data, trim_info); 4903b6a1a80SLukasz Majewski } 4913b6a1a80SLukasz Majewski 492a503a10fSBartlomiej Zolnierkiewicz static void exynos5433_tmu_set_trip_temp(struct exynos_tmu_data *data, 493a503a10fSBartlomiej Zolnierkiewicz int trip, u8 temp) 494a503a10fSBartlomiej Zolnierkiewicz { 495a503a10fSBartlomiej Zolnierkiewicz unsigned int reg_off, j; 496a503a10fSBartlomiej Zolnierkiewicz u32 th; 497a503a10fSBartlomiej Zolnierkiewicz 498a503a10fSBartlomiej Zolnierkiewicz if (trip > 3) { 499a503a10fSBartlomiej Zolnierkiewicz reg_off = EXYNOS5433_THD_TEMP_RISE7_4; 500a503a10fSBartlomiej Zolnierkiewicz j = trip - 4; 501a503a10fSBartlomiej Zolnierkiewicz } else { 502a503a10fSBartlomiej Zolnierkiewicz reg_off = EXYNOS5433_THD_TEMP_RISE3_0; 503a503a10fSBartlomiej Zolnierkiewicz j = trip; 504a503a10fSBartlomiej Zolnierkiewicz } 505a503a10fSBartlomiej Zolnierkiewicz 506a503a10fSBartlomiej Zolnierkiewicz th = readl(data->base + reg_off); 507a503a10fSBartlomiej Zolnierkiewicz th &= ~(0xff << j * 8); 508a503a10fSBartlomiej Zolnierkiewicz th |= (temp_to_code(data, temp) << j * 8); 509a503a10fSBartlomiej Zolnierkiewicz writel(th, data->base + reg_off); 510a503a10fSBartlomiej Zolnierkiewicz } 511a503a10fSBartlomiej Zolnierkiewicz 512a503a10fSBartlomiej Zolnierkiewicz static void exynos5433_tmu_set_trip_hyst(struct exynos_tmu_data *data, 513a503a10fSBartlomiej Zolnierkiewicz int trip, u8 temp, u8 hyst) 514a503a10fSBartlomiej Zolnierkiewicz { 515a503a10fSBartlomiej Zolnierkiewicz unsigned int reg_off, j; 516a503a10fSBartlomiej Zolnierkiewicz u32 th; 517a503a10fSBartlomiej Zolnierkiewicz 518a503a10fSBartlomiej Zolnierkiewicz if (trip > 3) { 519a503a10fSBartlomiej Zolnierkiewicz reg_off = EXYNOS5433_THD_TEMP_FALL7_4; 520a503a10fSBartlomiej Zolnierkiewicz j = trip - 4; 521a503a10fSBartlomiej Zolnierkiewicz } else { 522a503a10fSBartlomiej Zolnierkiewicz reg_off = EXYNOS5433_THD_TEMP_FALL3_0; 523a503a10fSBartlomiej Zolnierkiewicz j = trip; 524a503a10fSBartlomiej Zolnierkiewicz } 525a503a10fSBartlomiej Zolnierkiewicz 526a503a10fSBartlomiej Zolnierkiewicz th = readl(data->base + reg_off); 527a503a10fSBartlomiej Zolnierkiewicz th &= ~(0xff << j * 8); 528a503a10fSBartlomiej Zolnierkiewicz th |= (temp_to_code(data, temp - hyst) << j * 8); 529a503a10fSBartlomiej Zolnierkiewicz writel(th, data->base + reg_off); 53072d1100bSBartlomiej Zolnierkiewicz } 53172d1100bSBartlomiej Zolnierkiewicz 532c35268f5SBartlomiej Zolnierkiewicz static void exynos5433_tmu_initialize(struct platform_device *pdev) 533488c7455SChanwoo Choi { 534488c7455SChanwoo Choi struct exynos_tmu_data *data = platform_get_drvdata(pdev); 53597b3881bSBartlomiej Zolnierkiewicz unsigned int trim_info; 536c8f8f768SBartlomiej Zolnierkiewicz int sensor_id, cal_type; 537488c7455SChanwoo Choi 538488c7455SChanwoo Choi trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); 539488c7455SChanwoo Choi sanitize_temp_error(data, trim_info); 540488c7455SChanwoo Choi 541488c7455SChanwoo Choi /* Read the temperature sensor id */ 542488c7455SChanwoo Choi sensor_id = (trim_info & EXYNOS5433_TRIMINFO_SENSOR_ID_MASK) 543488c7455SChanwoo Choi >> EXYNOS5433_TRIMINFO_SENSOR_ID_SHIFT; 544488c7455SChanwoo Choi dev_info(&pdev->dev, "Temperature sensor ID: 0x%x\n", sensor_id); 545488c7455SChanwoo Choi 546488c7455SChanwoo Choi /* Read the calibration mode */ 547488c7455SChanwoo Choi writel(trim_info, data->base + EXYNOS_TMU_REG_TRIMINFO); 548488c7455SChanwoo Choi cal_type = (trim_info & EXYNOS5433_TRIMINFO_CALIB_SEL_MASK) 549488c7455SChanwoo Choi >> EXYNOS5433_TRIMINFO_CALIB_SEL_SHIFT; 550488c7455SChanwoo Choi 551488c7455SChanwoo Choi switch (cal_type) { 552488c7455SChanwoo Choi case EXYNOS5433_TRIMINFO_TWO_POINT_TRIMMING: 553199b3e3cSBartlomiej Zolnierkiewicz data->cal_type = TYPE_TWO_POINT_TRIMMING; 554488c7455SChanwoo Choi break; 555199b3e3cSBartlomiej Zolnierkiewicz case EXYNOS5433_TRIMINFO_ONE_POINT_TRIMMING: 556488c7455SChanwoo Choi default: 557199b3e3cSBartlomiej Zolnierkiewicz data->cal_type = TYPE_ONE_POINT_TRIMMING; 558488c7455SChanwoo Choi break; 559baba1ebbSKrzysztof Kozlowski } 560488c7455SChanwoo Choi 561488c7455SChanwoo Choi dev_info(&pdev->dev, "Calibration type is %d-point calibration\n", 562488c7455SChanwoo Choi cal_type ? 2 : 1); 563488c7455SChanwoo Choi } 564488c7455SChanwoo Choi 565a503a10fSBartlomiej Zolnierkiewicz static void exynos7_tmu_set_trip_temp(struct exynos_tmu_data *data, 566a503a10fSBartlomiej Zolnierkiewicz int trip, u8 temp) 567a503a10fSBartlomiej Zolnierkiewicz { 568a503a10fSBartlomiej Zolnierkiewicz unsigned int reg_off, bit_off; 569a503a10fSBartlomiej Zolnierkiewicz u32 th; 570a503a10fSBartlomiej Zolnierkiewicz 571a503a10fSBartlomiej Zolnierkiewicz reg_off = ((7 - trip) / 2) * 4; 572a503a10fSBartlomiej Zolnierkiewicz bit_off = ((8 - trip) % 2); 573a503a10fSBartlomiej Zolnierkiewicz 574a503a10fSBartlomiej Zolnierkiewicz th = readl(data->base + EXYNOS7_THD_TEMP_RISE7_6 + reg_off); 575a503a10fSBartlomiej Zolnierkiewicz th &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off)); 576a503a10fSBartlomiej Zolnierkiewicz th |= temp_to_code(data, temp) << (16 * bit_off); 577a503a10fSBartlomiej Zolnierkiewicz writel(th, data->base + EXYNOS7_THD_TEMP_RISE7_6 + reg_off); 578a503a10fSBartlomiej Zolnierkiewicz } 579a503a10fSBartlomiej Zolnierkiewicz 580a503a10fSBartlomiej Zolnierkiewicz static void exynos7_tmu_set_trip_hyst(struct exynos_tmu_data *data, 581a503a10fSBartlomiej Zolnierkiewicz int trip, u8 temp, u8 hyst) 582a503a10fSBartlomiej Zolnierkiewicz { 583a503a10fSBartlomiej Zolnierkiewicz unsigned int reg_off, bit_off; 584a503a10fSBartlomiej Zolnierkiewicz u32 th; 585a503a10fSBartlomiej Zolnierkiewicz 586a503a10fSBartlomiej Zolnierkiewicz reg_off = ((7 - trip) / 2) * 4; 587a503a10fSBartlomiej Zolnierkiewicz bit_off = ((8 - trip) % 2); 588a503a10fSBartlomiej Zolnierkiewicz 589a503a10fSBartlomiej Zolnierkiewicz th = readl(data->base + EXYNOS7_THD_TEMP_FALL7_6 + reg_off); 590a503a10fSBartlomiej Zolnierkiewicz th &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off)); 591a503a10fSBartlomiej Zolnierkiewicz th |= temp_to_code(data, temp - hyst) << (16 * bit_off); 592a503a10fSBartlomiej Zolnierkiewicz writel(th, data->base + EXYNOS7_THD_TEMP_FALL7_6 + reg_off); 593a503a10fSBartlomiej Zolnierkiewicz } 594a503a10fSBartlomiej Zolnierkiewicz 595c35268f5SBartlomiej Zolnierkiewicz static void exynos7_tmu_initialize(struct platform_device *pdev) 5966c247393SAbhilash Kesavan { 5976c247393SAbhilash Kesavan struct exynos_tmu_data *data = platform_get_drvdata(pdev); 59897b3881bSBartlomiej Zolnierkiewicz unsigned int trim_info; 5996c247393SAbhilash Kesavan 6006c247393SAbhilash Kesavan trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); 601aef27b65SBartlomiej Zolnierkiewicz sanitize_temp_error(data, trim_info); 6026c247393SAbhilash Kesavan } 6036c247393SAbhilash Kesavan 60437f9034fSBartlomiej Zolnierkiewicz static void exynos4210_tmu_control(struct platform_device *pdev, bool on) 60537f9034fSBartlomiej Zolnierkiewicz { 60637f9034fSBartlomiej Zolnierkiewicz struct exynos_tmu_data *data = platform_get_drvdata(pdev); 6073b6a1a80SLukasz Majewski struct thermal_zone_device *tz = data->tzd; 608*64e94192SBartlomiej Zolnierkiewicz unsigned int con, interrupt_en = 0, i; 60937f9034fSBartlomiej Zolnierkiewicz 61037f9034fSBartlomiej Zolnierkiewicz con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL)); 61137f9034fSBartlomiej Zolnierkiewicz 61259dfa54cSAmit Daniel Kachhap if (on) { 613*64e94192SBartlomiej Zolnierkiewicz for (i = 0; i < data->ntrip; i++) { 614*64e94192SBartlomiej Zolnierkiewicz if (!of_thermal_is_trip_valid(tz, i)) 615*64e94192SBartlomiej Zolnierkiewicz continue; 616*64e94192SBartlomiej Zolnierkiewicz 617*64e94192SBartlomiej Zolnierkiewicz interrupt_en |= 618*64e94192SBartlomiej Zolnierkiewicz (1 << (EXYNOS_TMU_INTEN_RISE0_SHIFT + i * 4)); 619*64e94192SBartlomiej Zolnierkiewicz } 6203b6a1a80SLukasz Majewski 621e0761533SBartlomiej Zolnierkiewicz if (data->soc != SOC_ARCH_EXYNOS4210) 62259dfa54cSAmit Daniel Kachhap interrupt_en |= 62337f9034fSBartlomiej Zolnierkiewicz interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT; 624*64e94192SBartlomiej Zolnierkiewicz 625*64e94192SBartlomiej Zolnierkiewicz con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); 62659dfa54cSAmit Daniel Kachhap } else { 62759dfa54cSAmit Daniel Kachhap con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); 62859dfa54cSAmit Daniel Kachhap } 629*64e94192SBartlomiej Zolnierkiewicz 63037f9034fSBartlomiej Zolnierkiewicz writel(interrupt_en, data->base + EXYNOS_TMU_REG_INTEN); 63137f9034fSBartlomiej Zolnierkiewicz writel(con, data->base + EXYNOS_TMU_REG_CONTROL); 63237f9034fSBartlomiej Zolnierkiewicz } 63359dfa54cSAmit Daniel Kachhap 634488c7455SChanwoo Choi static void exynos5433_tmu_control(struct platform_device *pdev, bool on) 635488c7455SChanwoo Choi { 636488c7455SChanwoo Choi struct exynos_tmu_data *data = platform_get_drvdata(pdev); 637488c7455SChanwoo Choi struct thermal_zone_device *tz = data->tzd; 638*64e94192SBartlomiej Zolnierkiewicz unsigned int con, interrupt_en = 0, pd_det_en, i; 639488c7455SChanwoo Choi 640488c7455SChanwoo Choi con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL)); 641488c7455SChanwoo Choi 642488c7455SChanwoo Choi if (on) { 643*64e94192SBartlomiej Zolnierkiewicz for (i = 0; i < data->ntrip; i++) { 644*64e94192SBartlomiej Zolnierkiewicz if (!of_thermal_is_trip_valid(tz, i)) 645*64e94192SBartlomiej Zolnierkiewicz continue; 646*64e94192SBartlomiej Zolnierkiewicz 647*64e94192SBartlomiej Zolnierkiewicz interrupt_en |= 648*64e94192SBartlomiej Zolnierkiewicz (1 << (EXYNOS7_TMU_INTEN_RISE0_SHIFT + i)); 649*64e94192SBartlomiej Zolnierkiewicz } 650488c7455SChanwoo Choi 651488c7455SChanwoo Choi interrupt_en |= 652488c7455SChanwoo Choi interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT; 653*64e94192SBartlomiej Zolnierkiewicz 654*64e94192SBartlomiej Zolnierkiewicz con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); 655*64e94192SBartlomiej Zolnierkiewicz } else 656488c7455SChanwoo Choi con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); 657488c7455SChanwoo Choi 658488c7455SChanwoo Choi pd_det_en = on ? EXYNOS5433_PD_DET_EN : 0; 659488c7455SChanwoo Choi 660488c7455SChanwoo Choi writel(pd_det_en, data->base + EXYNOS5433_TMU_PD_DET_EN); 661488c7455SChanwoo Choi writel(interrupt_en, data->base + EXYNOS5433_TMU_REG_INTEN); 662488c7455SChanwoo Choi writel(con, data->base + EXYNOS_TMU_REG_CONTROL); 663488c7455SChanwoo Choi } 664488c7455SChanwoo Choi 6656c247393SAbhilash Kesavan static void exynos7_tmu_control(struct platform_device *pdev, bool on) 6666c247393SAbhilash Kesavan { 6676c247393SAbhilash Kesavan struct exynos_tmu_data *data = platform_get_drvdata(pdev); 6686c247393SAbhilash Kesavan struct thermal_zone_device *tz = data->tzd; 669*64e94192SBartlomiej Zolnierkiewicz unsigned int con, interrupt_en = 0, i; 6706c247393SAbhilash Kesavan 6716c247393SAbhilash Kesavan con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL)); 6726c247393SAbhilash Kesavan 6736c247393SAbhilash Kesavan if (on) { 674*64e94192SBartlomiej Zolnierkiewicz for (i = 0; i < data->ntrip; i++) { 675*64e94192SBartlomiej Zolnierkiewicz if (!of_thermal_is_trip_valid(tz, i)) 676*64e94192SBartlomiej Zolnierkiewicz continue; 677*64e94192SBartlomiej Zolnierkiewicz 678*64e94192SBartlomiej Zolnierkiewicz interrupt_en |= 679*64e94192SBartlomiej Zolnierkiewicz (1 << (EXYNOS7_TMU_INTEN_RISE0_SHIFT + i)); 680*64e94192SBartlomiej Zolnierkiewicz } 6816c247393SAbhilash Kesavan 6826c247393SAbhilash Kesavan interrupt_en |= 6836c247393SAbhilash Kesavan interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT; 684*64e94192SBartlomiej Zolnierkiewicz 685*64e94192SBartlomiej Zolnierkiewicz con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); 686*64e94192SBartlomiej Zolnierkiewicz con |= (1 << EXYNOS7_PD_DET_EN_SHIFT); 6876c247393SAbhilash Kesavan } else { 6886c247393SAbhilash Kesavan con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); 68942b696e8SChanwoo Choi con &= ~(1 << EXYNOS7_PD_DET_EN_SHIFT); 6906c247393SAbhilash Kesavan } 6916c247393SAbhilash Kesavan 6926c247393SAbhilash Kesavan writel(interrupt_en, data->base + EXYNOS7_TMU_REG_INTEN); 6936c247393SAbhilash Kesavan writel(con, data->base + EXYNOS_TMU_REG_CONTROL); 6946c247393SAbhilash Kesavan } 6956c247393SAbhilash Kesavan 69617e8351aSSascha Hauer static int exynos_get_temp(void *p, int *temp) 69759dfa54cSAmit Daniel Kachhap { 6983b6a1a80SLukasz Majewski struct exynos_tmu_data *data = p; 69908d725cdSMarek Szyprowski int value, ret = 0; 7003b6a1a80SLukasz Majewski 7010eb875d8SMarek Szyprowski if (!data || !data->tmu_read || !data->enabled) 7023b6a1a80SLukasz Majewski return -EINVAL; 70359dfa54cSAmit Daniel Kachhap 70459dfa54cSAmit Daniel Kachhap mutex_lock(&data->lock); 70559dfa54cSAmit Daniel Kachhap clk_enable(data->clk); 7063b6a1a80SLukasz Majewski 70708d725cdSMarek Szyprowski value = data->tmu_read(data); 70808d725cdSMarek Szyprowski if (value < 0) 70908d725cdSMarek Szyprowski ret = value; 71008d725cdSMarek Szyprowski else 71108d725cdSMarek Szyprowski *temp = code_to_temp(data, value) * MCELSIUS; 7123b6a1a80SLukasz Majewski 71359dfa54cSAmit Daniel Kachhap clk_disable(data->clk); 71459dfa54cSAmit Daniel Kachhap mutex_unlock(&data->lock); 71559dfa54cSAmit Daniel Kachhap 71608d725cdSMarek Szyprowski return ret; 71759dfa54cSAmit Daniel Kachhap } 71859dfa54cSAmit Daniel Kachhap 71959dfa54cSAmit Daniel Kachhap #ifdef CONFIG_THERMAL_EMULATION 720154013eaSBartlomiej Zolnierkiewicz static u32 get_emul_con_reg(struct exynos_tmu_data *data, unsigned int val, 72117e8351aSSascha Hauer int temp) 722154013eaSBartlomiej Zolnierkiewicz { 723154013eaSBartlomiej Zolnierkiewicz if (temp) { 724154013eaSBartlomiej Zolnierkiewicz temp /= MCELSIUS; 725154013eaSBartlomiej Zolnierkiewicz 726154013eaSBartlomiej Zolnierkiewicz val &= ~(EXYNOS_EMUL_TIME_MASK << EXYNOS_EMUL_TIME_SHIFT); 727154013eaSBartlomiej Zolnierkiewicz val |= (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT); 7286c247393SAbhilash Kesavan if (data->soc == SOC_ARCH_EXYNOS7) { 7296c247393SAbhilash Kesavan val &= ~(EXYNOS7_EMUL_DATA_MASK << 7306c247393SAbhilash Kesavan EXYNOS7_EMUL_DATA_SHIFT); 7316c247393SAbhilash Kesavan val |= (temp_to_code(data, temp) << 7326c247393SAbhilash Kesavan EXYNOS7_EMUL_DATA_SHIFT) | 733154013eaSBartlomiej Zolnierkiewicz EXYNOS_EMUL_ENABLE; 734154013eaSBartlomiej Zolnierkiewicz } else { 7356c247393SAbhilash Kesavan val &= ~(EXYNOS_EMUL_DATA_MASK << 7366c247393SAbhilash Kesavan EXYNOS_EMUL_DATA_SHIFT); 7376c247393SAbhilash Kesavan val |= (temp_to_code(data, temp) << 7386c247393SAbhilash Kesavan EXYNOS_EMUL_DATA_SHIFT) | 7396c247393SAbhilash Kesavan EXYNOS_EMUL_ENABLE; 7406c247393SAbhilash Kesavan } 7416c247393SAbhilash Kesavan } else { 742154013eaSBartlomiej Zolnierkiewicz val &= ~EXYNOS_EMUL_ENABLE; 743154013eaSBartlomiej Zolnierkiewicz } 744154013eaSBartlomiej Zolnierkiewicz 745154013eaSBartlomiej Zolnierkiewicz return val; 746154013eaSBartlomiej Zolnierkiewicz } 747154013eaSBartlomiej Zolnierkiewicz 748285d994aSBartlomiej Zolnierkiewicz static void exynos4412_tmu_set_emulation(struct exynos_tmu_data *data, 74917e8351aSSascha Hauer int temp) 750285d994aSBartlomiej Zolnierkiewicz { 751285d994aSBartlomiej Zolnierkiewicz unsigned int val; 752285d994aSBartlomiej Zolnierkiewicz u32 emul_con; 753285d994aSBartlomiej Zolnierkiewicz 754285d994aSBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS5260) 755285d994aSBartlomiej Zolnierkiewicz emul_con = EXYNOS5260_EMUL_CON; 756b28fec13SSudip Mukherjee else if (data->soc == SOC_ARCH_EXYNOS5433) 757488c7455SChanwoo Choi emul_con = EXYNOS5433_TMU_EMUL_CON; 7586c247393SAbhilash Kesavan else if (data->soc == SOC_ARCH_EXYNOS7) 7596c247393SAbhilash Kesavan emul_con = EXYNOS7_TMU_REG_EMUL_CON; 760285d994aSBartlomiej Zolnierkiewicz else 761285d994aSBartlomiej Zolnierkiewicz emul_con = EXYNOS_EMUL_CON; 762285d994aSBartlomiej Zolnierkiewicz 763285d994aSBartlomiej Zolnierkiewicz val = readl(data->base + emul_con); 764285d994aSBartlomiej Zolnierkiewicz val = get_emul_con_reg(data, val, temp); 765285d994aSBartlomiej Zolnierkiewicz writel(val, data->base + emul_con); 766285d994aSBartlomiej Zolnierkiewicz } 767285d994aSBartlomiej Zolnierkiewicz 76817e8351aSSascha Hauer static int exynos_tmu_set_emulation(void *drv_data, int temp) 76959dfa54cSAmit Daniel Kachhap { 77059dfa54cSAmit Daniel Kachhap struct exynos_tmu_data *data = drv_data; 77159dfa54cSAmit Daniel Kachhap int ret = -EINVAL; 77259dfa54cSAmit Daniel Kachhap 773ef3f80fcSBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS4210) 77459dfa54cSAmit Daniel Kachhap goto out; 77559dfa54cSAmit Daniel Kachhap 77659dfa54cSAmit Daniel Kachhap if (temp && temp < MCELSIUS) 77759dfa54cSAmit Daniel Kachhap goto out; 77859dfa54cSAmit Daniel Kachhap 77959dfa54cSAmit Daniel Kachhap mutex_lock(&data->lock); 78059dfa54cSAmit Daniel Kachhap clk_enable(data->clk); 781285d994aSBartlomiej Zolnierkiewicz data->tmu_set_emulation(data, temp); 78259dfa54cSAmit Daniel Kachhap clk_disable(data->clk); 78359dfa54cSAmit Daniel Kachhap mutex_unlock(&data->lock); 78459dfa54cSAmit Daniel Kachhap return 0; 78559dfa54cSAmit Daniel Kachhap out: 78659dfa54cSAmit Daniel Kachhap return ret; 78759dfa54cSAmit Daniel Kachhap } 78859dfa54cSAmit Daniel Kachhap #else 789285d994aSBartlomiej Zolnierkiewicz #define exynos4412_tmu_set_emulation NULL 79017e8351aSSascha Hauer static int exynos_tmu_set_emulation(void *drv_data, int temp) 79159dfa54cSAmit Daniel Kachhap { return -EINVAL; } 79259dfa54cSAmit Daniel Kachhap #endif /* CONFIG_THERMAL_EMULATION */ 79359dfa54cSAmit Daniel Kachhap 794b79985caSBartlomiej Zolnierkiewicz static int exynos4210_tmu_read(struct exynos_tmu_data *data) 795b79985caSBartlomiej Zolnierkiewicz { 796b79985caSBartlomiej Zolnierkiewicz int ret = readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP); 797b79985caSBartlomiej Zolnierkiewicz 798b79985caSBartlomiej Zolnierkiewicz /* "temp_code" should range between 75 and 175 */ 799b79985caSBartlomiej Zolnierkiewicz return (ret < 75 || ret > 175) ? -ENODATA : ret; 800b79985caSBartlomiej Zolnierkiewicz } 801b79985caSBartlomiej Zolnierkiewicz 802b79985caSBartlomiej Zolnierkiewicz static int exynos4412_tmu_read(struct exynos_tmu_data *data) 803b79985caSBartlomiej Zolnierkiewicz { 804b79985caSBartlomiej Zolnierkiewicz return readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP); 805b79985caSBartlomiej Zolnierkiewicz } 806b79985caSBartlomiej Zolnierkiewicz 8076c247393SAbhilash Kesavan static int exynos7_tmu_read(struct exynos_tmu_data *data) 8086c247393SAbhilash Kesavan { 8096c247393SAbhilash Kesavan return readw(data->base + EXYNOS_TMU_REG_CURRENT_TEMP) & 8106c247393SAbhilash Kesavan EXYNOS7_TMU_TEMP_MASK; 8116c247393SAbhilash Kesavan } 8126c247393SAbhilash Kesavan 81359dfa54cSAmit Daniel Kachhap static void exynos_tmu_work(struct work_struct *work) 81459dfa54cSAmit Daniel Kachhap { 81559dfa54cSAmit Daniel Kachhap struct exynos_tmu_data *data = container_of(work, 81659dfa54cSAmit Daniel Kachhap struct exynos_tmu_data, irq_work); 817a0395eeeSAmit Daniel Kachhap 81814a11dc7SNaveen Krishna Chatradhi if (!IS_ERR(data->clk_sec)) 81914a11dc7SNaveen Krishna Chatradhi clk_enable(data->clk_sec); 82014a11dc7SNaveen Krishna Chatradhi if (!IS_ERR(data->clk_sec)) 82114a11dc7SNaveen Krishna Chatradhi clk_disable(data->clk_sec); 82259dfa54cSAmit Daniel Kachhap 8233b6a1a80SLukasz Majewski exynos_report_trigger(data); 82459dfa54cSAmit Daniel Kachhap mutex_lock(&data->lock); 82559dfa54cSAmit Daniel Kachhap clk_enable(data->clk); 826b8d582b9SAmit Daniel Kachhap 827a4463c4fSAmit Daniel Kachhap /* TODO: take action based on particular interrupt */ 828a7331f72SBartlomiej Zolnierkiewicz data->tmu_clear_irqs(data); 829b8d582b9SAmit Daniel Kachhap 83059dfa54cSAmit Daniel Kachhap clk_disable(data->clk); 83159dfa54cSAmit Daniel Kachhap mutex_unlock(&data->lock); 83259dfa54cSAmit Daniel Kachhap enable_irq(data->irq); 83359dfa54cSAmit Daniel Kachhap } 83459dfa54cSAmit Daniel Kachhap 835a7331f72SBartlomiej Zolnierkiewicz static void exynos4210_tmu_clear_irqs(struct exynos_tmu_data *data) 836a7331f72SBartlomiej Zolnierkiewicz { 837a7331f72SBartlomiej Zolnierkiewicz unsigned int val_irq; 838a7331f72SBartlomiej Zolnierkiewicz u32 tmu_intstat, tmu_intclear; 839a7331f72SBartlomiej Zolnierkiewicz 840a7331f72SBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS5260) { 841a7331f72SBartlomiej Zolnierkiewicz tmu_intstat = EXYNOS5260_TMU_REG_INTSTAT; 842a7331f72SBartlomiej Zolnierkiewicz tmu_intclear = EXYNOS5260_TMU_REG_INTCLEAR; 8436c247393SAbhilash Kesavan } else if (data->soc == SOC_ARCH_EXYNOS7) { 8446c247393SAbhilash Kesavan tmu_intstat = EXYNOS7_TMU_REG_INTPEND; 8456c247393SAbhilash Kesavan tmu_intclear = EXYNOS7_TMU_REG_INTPEND; 846488c7455SChanwoo Choi } else if (data->soc == SOC_ARCH_EXYNOS5433) { 847488c7455SChanwoo Choi tmu_intstat = EXYNOS5433_TMU_REG_INTPEND; 848488c7455SChanwoo Choi tmu_intclear = EXYNOS5433_TMU_REG_INTPEND; 849a7331f72SBartlomiej Zolnierkiewicz } else { 850a7331f72SBartlomiej Zolnierkiewicz tmu_intstat = EXYNOS_TMU_REG_INTSTAT; 851a7331f72SBartlomiej Zolnierkiewicz tmu_intclear = EXYNOS_TMU_REG_INTCLEAR; 852a7331f72SBartlomiej Zolnierkiewicz } 853a7331f72SBartlomiej Zolnierkiewicz 854a7331f72SBartlomiej Zolnierkiewicz val_irq = readl(data->base + tmu_intstat); 855a7331f72SBartlomiej Zolnierkiewicz /* 856a7331f72SBartlomiej Zolnierkiewicz * Clear the interrupts. Please note that the documentation for 857a7331f72SBartlomiej Zolnierkiewicz * Exynos3250, Exynos4412, Exynos5250 and Exynos5260 incorrectly 858a7331f72SBartlomiej Zolnierkiewicz * states that INTCLEAR register has a different placing of bits 859a7331f72SBartlomiej Zolnierkiewicz * responsible for FALL IRQs than INTSTAT register. Exynos5420 860a7331f72SBartlomiej Zolnierkiewicz * and Exynos5440 documentation is correct (Exynos4210 doesn't 861a7331f72SBartlomiej Zolnierkiewicz * support FALL IRQs at all). 862a7331f72SBartlomiej Zolnierkiewicz */ 863a7331f72SBartlomiej Zolnierkiewicz writel(val_irq, data->base + tmu_intclear); 864a7331f72SBartlomiej Zolnierkiewicz } 865a7331f72SBartlomiej Zolnierkiewicz 86659dfa54cSAmit Daniel Kachhap static irqreturn_t exynos_tmu_irq(int irq, void *id) 86759dfa54cSAmit Daniel Kachhap { 86859dfa54cSAmit Daniel Kachhap struct exynos_tmu_data *data = id; 86959dfa54cSAmit Daniel Kachhap 87059dfa54cSAmit Daniel Kachhap disable_irq_nosync(irq); 87159dfa54cSAmit Daniel Kachhap schedule_work(&data->irq_work); 87259dfa54cSAmit Daniel Kachhap 87359dfa54cSAmit Daniel Kachhap return IRQ_HANDLED; 87459dfa54cSAmit Daniel Kachhap } 87559dfa54cSAmit Daniel Kachhap 87659dfa54cSAmit Daniel Kachhap static const struct of_device_id exynos_tmu_match[] = { 877fee88e2bSMaciej Purski { 878fee88e2bSMaciej Purski .compatible = "samsung,exynos3250-tmu", 879fee88e2bSMaciej Purski .data = (const void *)SOC_ARCH_EXYNOS3250, 880fee88e2bSMaciej Purski }, { 881fee88e2bSMaciej Purski .compatible = "samsung,exynos4210-tmu", 882fee88e2bSMaciej Purski .data = (const void *)SOC_ARCH_EXYNOS4210, 883fee88e2bSMaciej Purski }, { 884fee88e2bSMaciej Purski .compatible = "samsung,exynos4412-tmu", 885fee88e2bSMaciej Purski .data = (const void *)SOC_ARCH_EXYNOS4412, 886fee88e2bSMaciej Purski }, { 887fee88e2bSMaciej Purski .compatible = "samsung,exynos5250-tmu", 888fee88e2bSMaciej Purski .data = (const void *)SOC_ARCH_EXYNOS5250, 889fee88e2bSMaciej Purski }, { 890fee88e2bSMaciej Purski .compatible = "samsung,exynos5260-tmu", 891fee88e2bSMaciej Purski .data = (const void *)SOC_ARCH_EXYNOS5260, 892fee88e2bSMaciej Purski }, { 893fee88e2bSMaciej Purski .compatible = "samsung,exynos5420-tmu", 894fee88e2bSMaciej Purski .data = (const void *)SOC_ARCH_EXYNOS5420, 895fee88e2bSMaciej Purski }, { 896fee88e2bSMaciej Purski .compatible = "samsung,exynos5420-tmu-ext-triminfo", 897fee88e2bSMaciej Purski .data = (const void *)SOC_ARCH_EXYNOS5420_TRIMINFO, 898fee88e2bSMaciej Purski }, { 899fee88e2bSMaciej Purski .compatible = "samsung,exynos5433-tmu", 900fee88e2bSMaciej Purski .data = (const void *)SOC_ARCH_EXYNOS5433, 901fee88e2bSMaciej Purski }, { 902fee88e2bSMaciej Purski .compatible = "samsung,exynos7-tmu", 903fee88e2bSMaciej Purski .data = (const void *)SOC_ARCH_EXYNOS7, 904fee88e2bSMaciej Purski }, 905fee88e2bSMaciej Purski { }, 90659dfa54cSAmit Daniel Kachhap }; 90759dfa54cSAmit Daniel Kachhap MODULE_DEVICE_TABLE(of, exynos_tmu_match); 90859dfa54cSAmit Daniel Kachhap 909cebe7373SAmit Daniel Kachhap static int exynos_map_dt_data(struct platform_device *pdev) 91059dfa54cSAmit Daniel Kachhap { 911cebe7373SAmit Daniel Kachhap struct exynos_tmu_data *data = platform_get_drvdata(pdev); 912cebe7373SAmit Daniel Kachhap struct resource res; 91359dfa54cSAmit Daniel Kachhap 91473b5b1d7SSachin Kamat if (!data || !pdev->dev.of_node) 915cebe7373SAmit Daniel Kachhap return -ENODEV; 91659dfa54cSAmit Daniel Kachhap 917cebe7373SAmit Daniel Kachhap data->id = of_alias_get_id(pdev->dev.of_node, "tmuctrl"); 918cebe7373SAmit Daniel Kachhap if (data->id < 0) 919cebe7373SAmit Daniel Kachhap data->id = 0; 920cebe7373SAmit Daniel Kachhap 921cebe7373SAmit Daniel Kachhap data->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); 922cebe7373SAmit Daniel Kachhap if (data->irq <= 0) { 923cebe7373SAmit Daniel Kachhap dev_err(&pdev->dev, "failed to get IRQ\n"); 924cebe7373SAmit Daniel Kachhap return -ENODEV; 925cebe7373SAmit Daniel Kachhap } 926cebe7373SAmit Daniel Kachhap 927cebe7373SAmit Daniel Kachhap if (of_address_to_resource(pdev->dev.of_node, 0, &res)) { 928cebe7373SAmit Daniel Kachhap dev_err(&pdev->dev, "failed to get Resource 0\n"); 929cebe7373SAmit Daniel Kachhap return -ENODEV; 930cebe7373SAmit Daniel Kachhap } 931cebe7373SAmit Daniel Kachhap 932cebe7373SAmit Daniel Kachhap data->base = devm_ioremap(&pdev->dev, res.start, resource_size(&res)); 933cebe7373SAmit Daniel Kachhap if (!data->base) { 934cebe7373SAmit Daniel Kachhap dev_err(&pdev->dev, "Failed to ioremap memory\n"); 935cebe7373SAmit Daniel Kachhap return -EADDRNOTAVAIL; 936cebe7373SAmit Daniel Kachhap } 937cebe7373SAmit Daniel Kachhap 938fee88e2bSMaciej Purski data->soc = (enum soc_type)of_device_get_match_data(&pdev->dev); 93956adb9efSBartlomiej Zolnierkiewicz 94056adb9efSBartlomiej Zolnierkiewicz switch (data->soc) { 94156adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS4210: 942c8f8f768SBartlomiej Zolnierkiewicz data->tmu_set_trip_temp = exynos4210_tmu_set_trip_temp; 943c8f8f768SBartlomiej Zolnierkiewicz data->tmu_set_trip_hyst = exynos4210_tmu_set_trip_hyst; 94456adb9efSBartlomiej Zolnierkiewicz data->tmu_initialize = exynos4210_tmu_initialize; 94556adb9efSBartlomiej Zolnierkiewicz data->tmu_control = exynos4210_tmu_control; 94656adb9efSBartlomiej Zolnierkiewicz data->tmu_read = exynos4210_tmu_read; 94756adb9efSBartlomiej Zolnierkiewicz data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; 9483a3a5f15SKrzysztof Kozlowski data->ntrip = 4; 949fccfe099SBartlomiej Zolnierkiewicz data->gain = 15; 95061020d18SBartlomiej Zolnierkiewicz data->reference_voltage = 7; 951e3ed3649SBartlomiej Zolnierkiewicz data->efuse_value = 55; 952e3ed3649SBartlomiej Zolnierkiewicz data->min_efuse_value = 40; 953e3ed3649SBartlomiej Zolnierkiewicz data->max_efuse_value = 100; 95456adb9efSBartlomiej Zolnierkiewicz break; 95556adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS3250: 95656adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS4412: 95756adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS5250: 95856adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS5260: 95956adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS5420: 96056adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS5420_TRIMINFO: 961c8f8f768SBartlomiej Zolnierkiewicz data->tmu_set_trip_temp = exynos4412_tmu_set_trip_temp; 962c8f8f768SBartlomiej Zolnierkiewicz data->tmu_set_trip_hyst = exynos4412_tmu_set_trip_hyst; 96356adb9efSBartlomiej Zolnierkiewicz data->tmu_initialize = exynos4412_tmu_initialize; 96456adb9efSBartlomiej Zolnierkiewicz data->tmu_control = exynos4210_tmu_control; 96556adb9efSBartlomiej Zolnierkiewicz data->tmu_read = exynos4412_tmu_read; 96656adb9efSBartlomiej Zolnierkiewicz data->tmu_set_emulation = exynos4412_tmu_set_emulation; 96756adb9efSBartlomiej Zolnierkiewicz data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; 9683a3a5f15SKrzysztof Kozlowski data->ntrip = 4; 969fccfe099SBartlomiej Zolnierkiewicz data->gain = 8; 97061020d18SBartlomiej Zolnierkiewicz data->reference_voltage = 16; 971e3ed3649SBartlomiej Zolnierkiewicz data->efuse_value = 55; 972e3ed3649SBartlomiej Zolnierkiewicz if (data->soc != SOC_ARCH_EXYNOS5420 && 973e3ed3649SBartlomiej Zolnierkiewicz data->soc != SOC_ARCH_EXYNOS5420_TRIMINFO) 974e3ed3649SBartlomiej Zolnierkiewicz data->min_efuse_value = 40; 975e3ed3649SBartlomiej Zolnierkiewicz else 976e3ed3649SBartlomiej Zolnierkiewicz data->min_efuse_value = 0; 977e3ed3649SBartlomiej Zolnierkiewicz data->max_efuse_value = 100; 97856adb9efSBartlomiej Zolnierkiewicz break; 979488c7455SChanwoo Choi case SOC_ARCH_EXYNOS5433: 980c8f8f768SBartlomiej Zolnierkiewicz data->tmu_set_trip_temp = exynos5433_tmu_set_trip_temp; 981c8f8f768SBartlomiej Zolnierkiewicz data->tmu_set_trip_hyst = exynos5433_tmu_set_trip_hyst; 982488c7455SChanwoo Choi data->tmu_initialize = exynos5433_tmu_initialize; 983488c7455SChanwoo Choi data->tmu_control = exynos5433_tmu_control; 984488c7455SChanwoo Choi data->tmu_read = exynos4412_tmu_read; 985488c7455SChanwoo Choi data->tmu_set_emulation = exynos4412_tmu_set_emulation; 986488c7455SChanwoo Choi data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; 9873a3a5f15SKrzysztof Kozlowski data->ntrip = 8; 988fccfe099SBartlomiej Zolnierkiewicz data->gain = 8; 98961020d18SBartlomiej Zolnierkiewicz if (res.start == EXYNOS5433_G3D_BASE) 99061020d18SBartlomiej Zolnierkiewicz data->reference_voltage = 23; 99161020d18SBartlomiej Zolnierkiewicz else 99261020d18SBartlomiej Zolnierkiewicz data->reference_voltage = 16; 993e3ed3649SBartlomiej Zolnierkiewicz data->efuse_value = 75; 994e3ed3649SBartlomiej Zolnierkiewicz data->min_efuse_value = 40; 995e3ed3649SBartlomiej Zolnierkiewicz data->max_efuse_value = 150; 996488c7455SChanwoo Choi break; 9976c247393SAbhilash Kesavan case SOC_ARCH_EXYNOS7: 998c8f8f768SBartlomiej Zolnierkiewicz data->tmu_set_trip_temp = exynos7_tmu_set_trip_temp; 999c8f8f768SBartlomiej Zolnierkiewicz data->tmu_set_trip_hyst = exynos7_tmu_set_trip_hyst; 10006c247393SAbhilash Kesavan data->tmu_initialize = exynos7_tmu_initialize; 10016c247393SAbhilash Kesavan data->tmu_control = exynos7_tmu_control; 10026c247393SAbhilash Kesavan data->tmu_read = exynos7_tmu_read; 10036c247393SAbhilash Kesavan data->tmu_set_emulation = exynos4412_tmu_set_emulation; 10046c247393SAbhilash Kesavan data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; 10053a3a5f15SKrzysztof Kozlowski data->ntrip = 8; 1006fccfe099SBartlomiej Zolnierkiewicz data->gain = 9; 100761020d18SBartlomiej Zolnierkiewicz data->reference_voltage = 17; 1008e3ed3649SBartlomiej Zolnierkiewicz data->efuse_value = 75; 1009e3ed3649SBartlomiej Zolnierkiewicz data->min_efuse_value = 15; 1010e3ed3649SBartlomiej Zolnierkiewicz data->max_efuse_value = 100; 10116c247393SAbhilash Kesavan break; 101256adb9efSBartlomiej Zolnierkiewicz default: 101356adb9efSBartlomiej Zolnierkiewicz dev_err(&pdev->dev, "Platform not supported\n"); 101456adb9efSBartlomiej Zolnierkiewicz return -EINVAL; 101556adb9efSBartlomiej Zolnierkiewicz } 101656adb9efSBartlomiej Zolnierkiewicz 1017199b3e3cSBartlomiej Zolnierkiewicz data->cal_type = TYPE_ONE_POINT_TRIMMING; 1018199b3e3cSBartlomiej Zolnierkiewicz 1019d9b6ee14SAmit Daniel Kachhap /* 1020d9b6ee14SAmit Daniel Kachhap * Check if the TMU shares some registers and then try to map the 1021d9b6ee14SAmit Daniel Kachhap * memory of common registers. 1022d9b6ee14SAmit Daniel Kachhap */ 10238014220dSKrzysztof Kozlowski if (data->soc != SOC_ARCH_EXYNOS5420_TRIMINFO) 1024d9b6ee14SAmit Daniel Kachhap return 0; 1025d9b6ee14SAmit Daniel Kachhap 1026d9b6ee14SAmit Daniel Kachhap if (of_address_to_resource(pdev->dev.of_node, 1, &res)) { 1027d9b6ee14SAmit Daniel Kachhap dev_err(&pdev->dev, "failed to get Resource 1\n"); 1028d9b6ee14SAmit Daniel Kachhap return -ENODEV; 1029d9b6ee14SAmit Daniel Kachhap } 1030d9b6ee14SAmit Daniel Kachhap 10319025d563SNaveen Krishna Chatradhi data->base_second = devm_ioremap(&pdev->dev, res.start, 1032d9b6ee14SAmit Daniel Kachhap resource_size(&res)); 10339025d563SNaveen Krishna Chatradhi if (!data->base_second) { 1034d9b6ee14SAmit Daniel Kachhap dev_err(&pdev->dev, "Failed to ioremap memory\n"); 1035d9b6ee14SAmit Daniel Kachhap return -ENOMEM; 1036d9b6ee14SAmit Daniel Kachhap } 1037cebe7373SAmit Daniel Kachhap 1038cebe7373SAmit Daniel Kachhap return 0; 1039cebe7373SAmit Daniel Kachhap } 1040cebe7373SAmit Daniel Kachhap 1041c3c04d9dSJulia Lawall static const struct thermal_zone_of_device_ops exynos_sensor_ops = { 10423b6a1a80SLukasz Majewski .get_temp = exynos_get_temp, 10433b6a1a80SLukasz Majewski .set_emul_temp = exynos_tmu_set_emulation, 10443b6a1a80SLukasz Majewski }; 10453b6a1a80SLukasz Majewski 1046cebe7373SAmit Daniel Kachhap static int exynos_tmu_probe(struct platform_device *pdev) 1047cebe7373SAmit Daniel Kachhap { 10483b6a1a80SLukasz Majewski struct exynos_tmu_data *data; 10493b6a1a80SLukasz Majewski int ret; 1050cebe7373SAmit Daniel Kachhap 105159dfa54cSAmit Daniel Kachhap data = devm_kzalloc(&pdev->dev, sizeof(struct exynos_tmu_data), 105259dfa54cSAmit Daniel Kachhap GFP_KERNEL); 10532a9675b3SJingoo Han if (!data) 105459dfa54cSAmit Daniel Kachhap return -ENOMEM; 105559dfa54cSAmit Daniel Kachhap 1056cebe7373SAmit Daniel Kachhap platform_set_drvdata(pdev, data); 1057cebe7373SAmit Daniel Kachhap mutex_init(&data->lock); 1058cebe7373SAmit Daniel Kachhap 1059824ead03SKrzysztof Kozlowski /* 1060824ead03SKrzysztof Kozlowski * Try enabling the regulator if found 1061824ead03SKrzysztof Kozlowski * TODO: Add regulator as an SOC feature, so that regulator enable 1062824ead03SKrzysztof Kozlowski * is a compulsory call. 1063824ead03SKrzysztof Kozlowski */ 10644d3583cdSJavier Martinez Canillas data->regulator = devm_regulator_get_optional(&pdev->dev, "vtmu"); 1065824ead03SKrzysztof Kozlowski if (!IS_ERR(data->regulator)) { 1066824ead03SKrzysztof Kozlowski ret = regulator_enable(data->regulator); 1067824ead03SKrzysztof Kozlowski if (ret) { 1068824ead03SKrzysztof Kozlowski dev_err(&pdev->dev, "failed to enable vtmu\n"); 1069824ead03SKrzysztof Kozlowski return ret; 10703b6a1a80SLukasz Majewski } 1071824ead03SKrzysztof Kozlowski } else { 1072ccb361d2SJavier Martinez Canillas if (PTR_ERR(data->regulator) == -EPROBE_DEFER) 1073ccb361d2SJavier Martinez Canillas return -EPROBE_DEFER; 1074824ead03SKrzysztof Kozlowski dev_info(&pdev->dev, "Regulator node (vtmu) not found\n"); 1075824ead03SKrzysztof Kozlowski } 1076824ead03SKrzysztof Kozlowski 1077cebe7373SAmit Daniel Kachhap ret = exynos_map_dt_data(pdev); 1078cebe7373SAmit Daniel Kachhap if (ret) 10793b6a1a80SLukasz Majewski goto err_sensor; 1080cebe7373SAmit Daniel Kachhap 108159dfa54cSAmit Daniel Kachhap INIT_WORK(&data->irq_work, exynos_tmu_work); 108259dfa54cSAmit Daniel Kachhap 108359dfa54cSAmit Daniel Kachhap data->clk = devm_clk_get(&pdev->dev, "tmu_apbif"); 108459dfa54cSAmit Daniel Kachhap if (IS_ERR(data->clk)) { 108559dfa54cSAmit Daniel Kachhap dev_err(&pdev->dev, "Failed to get clock\n"); 10863b6a1a80SLukasz Majewski ret = PTR_ERR(data->clk); 10873b6a1a80SLukasz Majewski goto err_sensor; 108859dfa54cSAmit Daniel Kachhap } 108959dfa54cSAmit Daniel Kachhap 109014a11dc7SNaveen Krishna Chatradhi data->clk_sec = devm_clk_get(&pdev->dev, "tmu_triminfo_apbif"); 109114a11dc7SNaveen Krishna Chatradhi if (IS_ERR(data->clk_sec)) { 109214a11dc7SNaveen Krishna Chatradhi if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) { 109314a11dc7SNaveen Krishna Chatradhi dev_err(&pdev->dev, "Failed to get triminfo clock\n"); 10943b6a1a80SLukasz Majewski ret = PTR_ERR(data->clk_sec); 10953b6a1a80SLukasz Majewski goto err_sensor; 109614a11dc7SNaveen Krishna Chatradhi } 109714a11dc7SNaveen Krishna Chatradhi } else { 109814a11dc7SNaveen Krishna Chatradhi ret = clk_prepare(data->clk_sec); 109914a11dc7SNaveen Krishna Chatradhi if (ret) { 110014a11dc7SNaveen Krishna Chatradhi dev_err(&pdev->dev, "Failed to get clock\n"); 11013b6a1a80SLukasz Majewski goto err_sensor; 110214a11dc7SNaveen Krishna Chatradhi } 110314a11dc7SNaveen Krishna Chatradhi } 110414a11dc7SNaveen Krishna Chatradhi 110514a11dc7SNaveen Krishna Chatradhi ret = clk_prepare(data->clk); 110614a11dc7SNaveen Krishna Chatradhi if (ret) { 110714a11dc7SNaveen Krishna Chatradhi dev_err(&pdev->dev, "Failed to get clock\n"); 110814a11dc7SNaveen Krishna Chatradhi goto err_clk_sec; 110914a11dc7SNaveen Krishna Chatradhi } 111059dfa54cSAmit Daniel Kachhap 1111488c7455SChanwoo Choi switch (data->soc) { 1112488c7455SChanwoo Choi case SOC_ARCH_EXYNOS5433: 1113488c7455SChanwoo Choi case SOC_ARCH_EXYNOS7: 11146c247393SAbhilash Kesavan data->sclk = devm_clk_get(&pdev->dev, "tmu_sclk"); 11156c247393SAbhilash Kesavan if (IS_ERR(data->sclk)) { 11166c247393SAbhilash Kesavan dev_err(&pdev->dev, "Failed to get sclk\n"); 11176c247393SAbhilash Kesavan goto err_clk; 11186c247393SAbhilash Kesavan } else { 11196c247393SAbhilash Kesavan ret = clk_prepare_enable(data->sclk); 11206c247393SAbhilash Kesavan if (ret) { 11216c247393SAbhilash Kesavan dev_err(&pdev->dev, "Failed to enable sclk\n"); 11226c247393SAbhilash Kesavan goto err_clk; 11236c247393SAbhilash Kesavan } 11246c247393SAbhilash Kesavan } 1125488c7455SChanwoo Choi break; 1126488c7455SChanwoo Choi default: 1127488c7455SChanwoo Choi break; 1128baba1ebbSKrzysztof Kozlowski } 11296c247393SAbhilash Kesavan 11309e4249b4SKrzysztof Kozlowski /* 11319e4249b4SKrzysztof Kozlowski * data->tzd must be registered before calling exynos_tmu_initialize(), 11329e4249b4SKrzysztof Kozlowski * requesting irq and calling exynos_tmu_control(). 11339e4249b4SKrzysztof Kozlowski */ 11349e4249b4SKrzysztof Kozlowski data->tzd = thermal_zone_of_sensor_register(&pdev->dev, 0, data, 11359e4249b4SKrzysztof Kozlowski &exynos_sensor_ops); 11369e4249b4SKrzysztof Kozlowski if (IS_ERR(data->tzd)) { 11379e4249b4SKrzysztof Kozlowski ret = PTR_ERR(data->tzd); 11389e4249b4SKrzysztof Kozlowski dev_err(&pdev->dev, "Failed to register sensor: %d\n", ret); 11399e4249b4SKrzysztof Kozlowski goto err_sclk; 11409e4249b4SKrzysztof Kozlowski } 114159dfa54cSAmit Daniel Kachhap 114259dfa54cSAmit Daniel Kachhap ret = exynos_tmu_initialize(pdev); 114359dfa54cSAmit Daniel Kachhap if (ret) { 114459dfa54cSAmit Daniel Kachhap dev_err(&pdev->dev, "Failed to initialize TMU\n"); 11459e4249b4SKrzysztof Kozlowski goto err_thermal; 114659dfa54cSAmit Daniel Kachhap } 114759dfa54cSAmit Daniel Kachhap 1148cebe7373SAmit Daniel Kachhap ret = devm_request_irq(&pdev->dev, data->irq, exynos_tmu_irq, 1149cebe7373SAmit Daniel Kachhap IRQF_TRIGGER_RISING | IRQF_SHARED, dev_name(&pdev->dev), data); 1150cebe7373SAmit Daniel Kachhap if (ret) { 1151cebe7373SAmit Daniel Kachhap dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq); 11529e4249b4SKrzysztof Kozlowski goto err_thermal; 1153cebe7373SAmit Daniel Kachhap } 115459dfa54cSAmit Daniel Kachhap 11553b6a1a80SLukasz Majewski exynos_tmu_control(pdev, true); 115659dfa54cSAmit Daniel Kachhap return 0; 11579e4249b4SKrzysztof Kozlowski 11589e4249b4SKrzysztof Kozlowski err_thermal: 11599e4249b4SKrzysztof Kozlowski thermal_zone_of_sensor_unregister(&pdev->dev, data->tzd); 11606c247393SAbhilash Kesavan err_sclk: 11616c247393SAbhilash Kesavan clk_disable_unprepare(data->sclk); 116259dfa54cSAmit Daniel Kachhap err_clk: 116359dfa54cSAmit Daniel Kachhap clk_unprepare(data->clk); 116414a11dc7SNaveen Krishna Chatradhi err_clk_sec: 116514a11dc7SNaveen Krishna Chatradhi if (!IS_ERR(data->clk_sec)) 116614a11dc7SNaveen Krishna Chatradhi clk_unprepare(data->clk_sec); 11673b6a1a80SLukasz Majewski err_sensor: 1168bfa26838SKrzysztof Kozlowski if (!IS_ERR(data->regulator)) 11695f09a5cbSKrzysztof Kozlowski regulator_disable(data->regulator); 11703b6a1a80SLukasz Majewski 117159dfa54cSAmit Daniel Kachhap return ret; 117259dfa54cSAmit Daniel Kachhap } 117359dfa54cSAmit Daniel Kachhap 117459dfa54cSAmit Daniel Kachhap static int exynos_tmu_remove(struct platform_device *pdev) 117559dfa54cSAmit Daniel Kachhap { 117659dfa54cSAmit Daniel Kachhap struct exynos_tmu_data *data = platform_get_drvdata(pdev); 11773b6a1a80SLukasz Majewski struct thermal_zone_device *tzd = data->tzd; 117859dfa54cSAmit Daniel Kachhap 11793b6a1a80SLukasz Majewski thermal_zone_of_sensor_unregister(&pdev->dev, tzd); 11804215688eSBartlomiej Zolnierkiewicz exynos_tmu_control(pdev, false); 11814215688eSBartlomiej Zolnierkiewicz 11826c247393SAbhilash Kesavan clk_disable_unprepare(data->sclk); 118359dfa54cSAmit Daniel Kachhap clk_unprepare(data->clk); 118414a11dc7SNaveen Krishna Chatradhi if (!IS_ERR(data->clk_sec)) 118514a11dc7SNaveen Krishna Chatradhi clk_unprepare(data->clk_sec); 118659dfa54cSAmit Daniel Kachhap 1187498d22f6SAmit Daniel Kachhap if (!IS_ERR(data->regulator)) 1188498d22f6SAmit Daniel Kachhap regulator_disable(data->regulator); 1189498d22f6SAmit Daniel Kachhap 119059dfa54cSAmit Daniel Kachhap return 0; 119159dfa54cSAmit Daniel Kachhap } 119259dfa54cSAmit Daniel Kachhap 119359dfa54cSAmit Daniel Kachhap #ifdef CONFIG_PM_SLEEP 119459dfa54cSAmit Daniel Kachhap static int exynos_tmu_suspend(struct device *dev) 119559dfa54cSAmit Daniel Kachhap { 119659dfa54cSAmit Daniel Kachhap exynos_tmu_control(to_platform_device(dev), false); 119759dfa54cSAmit Daniel Kachhap 119859dfa54cSAmit Daniel Kachhap return 0; 119959dfa54cSAmit Daniel Kachhap } 120059dfa54cSAmit Daniel Kachhap 120159dfa54cSAmit Daniel Kachhap static int exynos_tmu_resume(struct device *dev) 120259dfa54cSAmit Daniel Kachhap { 120359dfa54cSAmit Daniel Kachhap struct platform_device *pdev = to_platform_device(dev); 120459dfa54cSAmit Daniel Kachhap 120559dfa54cSAmit Daniel Kachhap exynos_tmu_initialize(pdev); 120659dfa54cSAmit Daniel Kachhap exynos_tmu_control(pdev, true); 120759dfa54cSAmit Daniel Kachhap 120859dfa54cSAmit Daniel Kachhap return 0; 120959dfa54cSAmit Daniel Kachhap } 121059dfa54cSAmit Daniel Kachhap 121159dfa54cSAmit Daniel Kachhap static SIMPLE_DEV_PM_OPS(exynos_tmu_pm, 121259dfa54cSAmit Daniel Kachhap exynos_tmu_suspend, exynos_tmu_resume); 121359dfa54cSAmit Daniel Kachhap #define EXYNOS_TMU_PM (&exynos_tmu_pm) 121459dfa54cSAmit Daniel Kachhap #else 121559dfa54cSAmit Daniel Kachhap #define EXYNOS_TMU_PM NULL 121659dfa54cSAmit Daniel Kachhap #endif 121759dfa54cSAmit Daniel Kachhap 121859dfa54cSAmit Daniel Kachhap static struct platform_driver exynos_tmu_driver = { 121959dfa54cSAmit Daniel Kachhap .driver = { 122059dfa54cSAmit Daniel Kachhap .name = "exynos-tmu", 122159dfa54cSAmit Daniel Kachhap .pm = EXYNOS_TMU_PM, 122273b5b1d7SSachin Kamat .of_match_table = exynos_tmu_match, 122359dfa54cSAmit Daniel Kachhap }, 122459dfa54cSAmit Daniel Kachhap .probe = exynos_tmu_probe, 122559dfa54cSAmit Daniel Kachhap .remove = exynos_tmu_remove, 122659dfa54cSAmit Daniel Kachhap }; 122759dfa54cSAmit Daniel Kachhap 122859dfa54cSAmit Daniel Kachhap module_platform_driver(exynos_tmu_driver); 122959dfa54cSAmit Daniel Kachhap 123059dfa54cSAmit Daniel Kachhap MODULE_DESCRIPTION("EXYNOS TMU Driver"); 123159dfa54cSAmit Daniel Kachhap MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>"); 123259dfa54cSAmit Daniel Kachhap MODULE_LICENSE("GPL"); 123359dfa54cSAmit Daniel Kachhap MODULE_ALIAS("platform:exynos-tmu"); 1234