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> 3259dfa54cSAmit Daniel Kachhap #include <linux/of.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 380c1836a6SAmit Daniel Kachhap #include "exynos_tmu.h" 393b6a1a80SLukasz Majewski #include "../thermal_core.h" 402845f6ecSBartlomiej Zolnierkiewicz 412845f6ecSBartlomiej Zolnierkiewicz /* Exynos generic registers */ 422845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_REG_TRIMINFO 0x0 432845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_REG_CONTROL 0x20 442845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_REG_STATUS 0x28 452845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_REG_CURRENT_TEMP 0x40 462845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_REG_INTEN 0x70 472845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_REG_INTSTAT 0x74 482845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_REG_INTCLEAR 0x78 492845f6ecSBartlomiej Zolnierkiewicz 502845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_TEMP_MASK 0xff 512845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_REF_VOLTAGE_SHIFT 24 522845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_REF_VOLTAGE_MASK 0x1f 532845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_BUF_SLOPE_SEL_MASK 0xf 542845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT 8 552845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_CORE_EN_SHIFT 0 562845f6ecSBartlomiej Zolnierkiewicz 572845f6ecSBartlomiej Zolnierkiewicz /* Exynos3250 specific registers */ 582845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_TRIMINFO_CON1 0x10 592845f6ecSBartlomiej Zolnierkiewicz 602845f6ecSBartlomiej Zolnierkiewicz /* Exynos4210 specific registers */ 612845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS4210_TMU_REG_THRESHOLD_TEMP 0x44 622845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS4210_TMU_REG_TRIG_LEVEL0 0x50 632845f6ecSBartlomiej Zolnierkiewicz 642845f6ecSBartlomiej Zolnierkiewicz /* Exynos5250, Exynos4412, Exynos3250 specific registers */ 652845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_TRIMINFO_CON2 0x14 662845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_THD_TEMP_RISE 0x50 672845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_THD_TEMP_FALL 0x54 682845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_EMUL_CON 0x80 692845f6ecSBartlomiej Zolnierkiewicz 702845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TRIMINFO_RELOAD_ENABLE 1 712845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TRIMINFO_25_SHIFT 0 722845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TRIMINFO_85_SHIFT 8 732845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_TRIP_MODE_SHIFT 13 742845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_TRIP_MODE_MASK 0x7 752845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_THERM_TRIP_EN_SHIFT 12 762845f6ecSBartlomiej Zolnierkiewicz 772845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_INTEN_RISE0_SHIFT 0 782845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_INTEN_RISE1_SHIFT 4 792845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_INTEN_RISE2_SHIFT 8 802845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_INTEN_RISE3_SHIFT 12 812845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_INTEN_FALL0_SHIFT 16 822845f6ecSBartlomiej Zolnierkiewicz 832845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_EMUL_TIME 0x57F0 842845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_EMUL_TIME_MASK 0xffff 852845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_EMUL_TIME_SHIFT 16 862845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_EMUL_DATA_SHIFT 8 872845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_EMUL_DATA_MASK 0xFF 882845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_EMUL_ENABLE 0x1 892845f6ecSBartlomiej Zolnierkiewicz 902845f6ecSBartlomiej Zolnierkiewicz /* Exynos5260 specific */ 912845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5260_TMU_REG_INTEN 0xC0 922845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5260_TMU_REG_INTSTAT 0xC4 932845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5260_TMU_REG_INTCLEAR 0xC8 942845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5260_EMUL_CON 0x100 952845f6ecSBartlomiej Zolnierkiewicz 962845f6ecSBartlomiej Zolnierkiewicz /* Exynos4412 specific */ 972845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS4412_MUX_ADDR_VALUE 6 982845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS4412_MUX_ADDR_SHIFT 20 992845f6ecSBartlomiej Zolnierkiewicz 100488c7455SChanwoo Choi /* Exynos5433 specific registers */ 101488c7455SChanwoo Choi #define EXYNOS5433_TMU_REG_CONTROL1 0x024 102488c7455SChanwoo Choi #define EXYNOS5433_TMU_SAMPLING_INTERVAL 0x02c 103488c7455SChanwoo Choi #define EXYNOS5433_TMU_COUNTER_VALUE0 0x030 104488c7455SChanwoo Choi #define EXYNOS5433_TMU_COUNTER_VALUE1 0x034 105488c7455SChanwoo Choi #define EXYNOS5433_TMU_REG_CURRENT_TEMP1 0x044 106488c7455SChanwoo Choi #define EXYNOS5433_THD_TEMP_RISE3_0 0x050 107488c7455SChanwoo Choi #define EXYNOS5433_THD_TEMP_RISE7_4 0x054 108488c7455SChanwoo Choi #define EXYNOS5433_THD_TEMP_FALL3_0 0x060 109488c7455SChanwoo Choi #define EXYNOS5433_THD_TEMP_FALL7_4 0x064 110488c7455SChanwoo Choi #define EXYNOS5433_TMU_REG_INTEN 0x0c0 111488c7455SChanwoo Choi #define EXYNOS5433_TMU_REG_INTPEND 0x0c8 112488c7455SChanwoo Choi #define EXYNOS5433_TMU_EMUL_CON 0x110 113488c7455SChanwoo Choi #define EXYNOS5433_TMU_PD_DET_EN 0x130 114488c7455SChanwoo Choi 115488c7455SChanwoo Choi #define EXYNOS5433_TRIMINFO_SENSOR_ID_SHIFT 16 116488c7455SChanwoo Choi #define EXYNOS5433_TRIMINFO_CALIB_SEL_SHIFT 23 117488c7455SChanwoo Choi #define EXYNOS5433_TRIMINFO_SENSOR_ID_MASK \ 118488c7455SChanwoo Choi (0xf << EXYNOS5433_TRIMINFO_SENSOR_ID_SHIFT) 119488c7455SChanwoo Choi #define EXYNOS5433_TRIMINFO_CALIB_SEL_MASK BIT(23) 120488c7455SChanwoo Choi 121488c7455SChanwoo Choi #define EXYNOS5433_TRIMINFO_ONE_POINT_TRIMMING 0 122488c7455SChanwoo Choi #define EXYNOS5433_TRIMINFO_TWO_POINT_TRIMMING 1 123488c7455SChanwoo Choi 124488c7455SChanwoo Choi #define EXYNOS5433_PD_DET_EN 1 125488c7455SChanwoo Choi 1262845f6ecSBartlomiej Zolnierkiewicz /*exynos5440 specific registers*/ 1272845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5440_TMU_S0_7_TRIM 0x000 1282845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5440_TMU_S0_7_CTRL 0x020 1292845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5440_TMU_S0_7_DEBUG 0x040 1302845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5440_TMU_S0_7_TEMP 0x0f0 1312845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5440_TMU_S0_7_TH0 0x110 1322845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5440_TMU_S0_7_TH1 0x130 1332845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5440_TMU_S0_7_TH2 0x150 1342845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5440_TMU_S0_7_IRQEN 0x210 1352845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5440_TMU_S0_7_IRQ 0x230 1362845f6ecSBartlomiej Zolnierkiewicz /* exynos5440 common registers */ 1372845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5440_TMU_IRQ_STATUS 0x000 1382845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5440_TMU_PMIN 0x004 1392845f6ecSBartlomiej Zolnierkiewicz 1402845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5440_TMU_INTEN_RISE0_SHIFT 0 1412845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5440_TMU_INTEN_RISE1_SHIFT 1 1422845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5440_TMU_INTEN_RISE2_SHIFT 2 1432845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5440_TMU_INTEN_RISE3_SHIFT 3 1442845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5440_TMU_INTEN_FALL0_SHIFT 4 1452845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5440_TMU_TH_RISE4_SHIFT 24 1462845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5440_EFUSE_SWAP_OFFSET 8 14759dfa54cSAmit Daniel Kachhap 1486c247393SAbhilash Kesavan /* Exynos7 specific registers */ 1496c247393SAbhilash Kesavan #define EXYNOS7_THD_TEMP_RISE7_6 0x50 1506c247393SAbhilash Kesavan #define EXYNOS7_THD_TEMP_FALL7_6 0x60 1516c247393SAbhilash Kesavan #define EXYNOS7_TMU_REG_INTEN 0x110 1526c247393SAbhilash Kesavan #define EXYNOS7_TMU_REG_INTPEND 0x118 1536c247393SAbhilash Kesavan #define EXYNOS7_TMU_REG_EMUL_CON 0x160 1546c247393SAbhilash Kesavan 1556c247393SAbhilash Kesavan #define EXYNOS7_TMU_TEMP_MASK 0x1ff 1566c247393SAbhilash Kesavan #define EXYNOS7_PD_DET_EN_SHIFT 23 1576c247393SAbhilash Kesavan #define EXYNOS7_TMU_INTEN_RISE0_SHIFT 0 1586c247393SAbhilash Kesavan #define EXYNOS7_TMU_INTEN_RISE1_SHIFT 1 1596c247393SAbhilash Kesavan #define EXYNOS7_TMU_INTEN_RISE2_SHIFT 2 1606c247393SAbhilash Kesavan #define EXYNOS7_TMU_INTEN_RISE3_SHIFT 3 1616c247393SAbhilash Kesavan #define EXYNOS7_TMU_INTEN_RISE4_SHIFT 4 1626c247393SAbhilash Kesavan #define EXYNOS7_TMU_INTEN_RISE5_SHIFT 5 1636c247393SAbhilash Kesavan #define EXYNOS7_TMU_INTEN_RISE6_SHIFT 6 1646c247393SAbhilash Kesavan #define EXYNOS7_TMU_INTEN_RISE7_SHIFT 7 1656c247393SAbhilash Kesavan #define EXYNOS7_EMUL_DATA_SHIFT 7 1666c247393SAbhilash Kesavan #define EXYNOS7_EMUL_DATA_MASK 0x1ff 1676c247393SAbhilash Kesavan 1683b6a1a80SLukasz Majewski #define MCELSIUS 1000 169cebe7373SAmit Daniel Kachhap /** 170cebe7373SAmit Daniel Kachhap * struct exynos_tmu_data : A structure to hold the private data of the TMU 171cebe7373SAmit Daniel Kachhap driver 172cebe7373SAmit Daniel Kachhap * @id: identifier of the one instance of the TMU controller. 173cebe7373SAmit Daniel Kachhap * @pdata: pointer to the tmu platform/configuration data 174cebe7373SAmit Daniel Kachhap * @base: base address of the single instance of the TMU controller. 1759025d563SNaveen Krishna Chatradhi * @base_second: base address of the common registers of the TMU controller. 176cebe7373SAmit Daniel Kachhap * @irq: irq number of the TMU controller. 177cebe7373SAmit Daniel Kachhap * @soc: id of the SOC type. 178cebe7373SAmit Daniel Kachhap * @irq_work: pointer to the irq work structure. 179cebe7373SAmit Daniel Kachhap * @lock: lock to implement synchronization. 180cebe7373SAmit Daniel Kachhap * @clk: pointer to the clock structure. 18114a11dc7SNaveen Krishna Chatradhi * @clk_sec: pointer to the clock structure for accessing the base_second. 1826c247393SAbhilash Kesavan * @sclk: pointer to the clock structure for accessing the tmu special clk. 183cebe7373SAmit Daniel Kachhap * @temp_error1: fused value of the first point trim. 184cebe7373SAmit Daniel Kachhap * @temp_error2: fused value of the second point trim. 185498d22f6SAmit Daniel Kachhap * @regulator: pointer to the TMU regulator structure. 186cebe7373SAmit Daniel Kachhap * @reg_conf: pointer to structure to register with core thermal. 1873a3a5f15SKrzysztof Kozlowski * @ntrip: number of supported trip points. 18888fc6f73SMarek Szyprowski * @enabled: current status of TMU device 18972d1100bSBartlomiej Zolnierkiewicz * @tmu_initialize: SoC specific TMU initialization method 19037f9034fSBartlomiej Zolnierkiewicz * @tmu_control: SoC specific TMU control method 191b79985caSBartlomiej Zolnierkiewicz * @tmu_read: SoC specific TMU temperature read method 192285d994aSBartlomiej Zolnierkiewicz * @tmu_set_emulation: SoC specific TMU emulation setting method 193a7331f72SBartlomiej Zolnierkiewicz * @tmu_clear_irqs: SoC specific TMU interrupts clearing method 194cebe7373SAmit Daniel Kachhap */ 19559dfa54cSAmit Daniel Kachhap struct exynos_tmu_data { 196cebe7373SAmit Daniel Kachhap int id; 19759dfa54cSAmit Daniel Kachhap struct exynos_tmu_platform_data *pdata; 19859dfa54cSAmit Daniel Kachhap void __iomem *base; 1999025d563SNaveen Krishna Chatradhi void __iomem *base_second; 20059dfa54cSAmit Daniel Kachhap int irq; 20159dfa54cSAmit Daniel Kachhap enum soc_type soc; 20259dfa54cSAmit Daniel Kachhap struct work_struct irq_work; 20359dfa54cSAmit Daniel Kachhap struct mutex lock; 2046c247393SAbhilash Kesavan struct clk *clk, *clk_sec, *sclk; 2056c247393SAbhilash Kesavan u16 temp_error1, temp_error2; 206498d22f6SAmit Daniel Kachhap struct regulator *regulator; 2073b6a1a80SLukasz Majewski struct thermal_zone_device *tzd; 2083a3a5f15SKrzysztof Kozlowski unsigned int ntrip; 20988fc6f73SMarek Szyprowski bool enabled; 2103b6a1a80SLukasz Majewski 21172d1100bSBartlomiej Zolnierkiewicz int (*tmu_initialize)(struct platform_device *pdev); 21237f9034fSBartlomiej Zolnierkiewicz void (*tmu_control)(struct platform_device *pdev, bool on); 213b79985caSBartlomiej Zolnierkiewicz int (*tmu_read)(struct exynos_tmu_data *data); 21417e8351aSSascha Hauer void (*tmu_set_emulation)(struct exynos_tmu_data *data, int temp); 215a7331f72SBartlomiej Zolnierkiewicz void (*tmu_clear_irqs)(struct exynos_tmu_data *data); 21659dfa54cSAmit Daniel Kachhap }; 21759dfa54cSAmit Daniel Kachhap 2183b6a1a80SLukasz Majewski static void exynos_report_trigger(struct exynos_tmu_data *p) 2193b6a1a80SLukasz Majewski { 2203b6a1a80SLukasz Majewski char data[10], *envp[] = { data, NULL }; 2213b6a1a80SLukasz Majewski struct thermal_zone_device *tz = p->tzd; 22217e8351aSSascha Hauer int temp; 2233b6a1a80SLukasz Majewski unsigned int i; 2243b6a1a80SLukasz Majewski 225eccb6014SLukasz Majewski if (!tz) { 226eccb6014SLukasz Majewski pr_err("No thermal zone device defined\n"); 2273b6a1a80SLukasz Majewski return; 2283b6a1a80SLukasz Majewski } 2293b6a1a80SLukasz Majewski 2300e70f466SSrinivas Pandruvada thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); 2313b6a1a80SLukasz Majewski 2323b6a1a80SLukasz Majewski mutex_lock(&tz->lock); 2333b6a1a80SLukasz Majewski /* Find the level for which trip happened */ 2343b6a1a80SLukasz Majewski for (i = 0; i < of_thermal_get_ntrips(tz); i++) { 2353b6a1a80SLukasz Majewski tz->ops->get_trip_temp(tz, i, &temp); 2363b6a1a80SLukasz Majewski if (tz->last_temperature < temp) 2373b6a1a80SLukasz Majewski break; 2383b6a1a80SLukasz Majewski } 2393b6a1a80SLukasz Majewski 2403b6a1a80SLukasz Majewski snprintf(data, sizeof(data), "%u", i); 2413b6a1a80SLukasz Majewski kobject_uevent_env(&tz->device.kobj, KOBJ_CHANGE, envp); 2423b6a1a80SLukasz Majewski mutex_unlock(&tz->lock); 2433b6a1a80SLukasz Majewski } 2443b6a1a80SLukasz Majewski 24559dfa54cSAmit Daniel Kachhap /* 24659dfa54cSAmit Daniel Kachhap * TMU treats temperature as a mapped temperature code. 24759dfa54cSAmit Daniel Kachhap * The temperature is converted differently depending on the calibration type. 24859dfa54cSAmit Daniel Kachhap */ 24959dfa54cSAmit Daniel Kachhap static int temp_to_code(struct exynos_tmu_data *data, u8 temp) 25059dfa54cSAmit Daniel Kachhap { 25159dfa54cSAmit Daniel Kachhap struct exynos_tmu_platform_data *pdata = data->pdata; 25259dfa54cSAmit Daniel Kachhap int temp_code; 25359dfa54cSAmit Daniel Kachhap 25459dfa54cSAmit Daniel Kachhap switch (pdata->cal_type) { 25559dfa54cSAmit Daniel Kachhap case TYPE_TWO_POINT_TRIMMING: 256bb34b4c8SAmit Daniel Kachhap temp_code = (temp - pdata->first_point_trim) * 25759dfa54cSAmit Daniel Kachhap (data->temp_error2 - data->temp_error1) / 258bb34b4c8SAmit Daniel Kachhap (pdata->second_point_trim - pdata->first_point_trim) + 259bb34b4c8SAmit Daniel Kachhap data->temp_error1; 26059dfa54cSAmit Daniel Kachhap break; 26159dfa54cSAmit Daniel Kachhap case TYPE_ONE_POINT_TRIMMING: 262bb34b4c8SAmit Daniel Kachhap temp_code = temp + data->temp_error1 - pdata->first_point_trim; 26359dfa54cSAmit Daniel Kachhap break; 26459dfa54cSAmit Daniel Kachhap default: 265bb34b4c8SAmit Daniel Kachhap temp_code = temp + pdata->default_temp_offset; 26659dfa54cSAmit Daniel Kachhap break; 26759dfa54cSAmit Daniel Kachhap } 268ddb31d43SBartlomiej Zolnierkiewicz 26959dfa54cSAmit Daniel Kachhap return temp_code; 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 { 27859dfa54cSAmit Daniel Kachhap struct exynos_tmu_platform_data *pdata = data->pdata; 27959dfa54cSAmit Daniel Kachhap int temp; 28059dfa54cSAmit Daniel Kachhap 28159dfa54cSAmit Daniel Kachhap switch (pdata->cal_type) { 28259dfa54cSAmit Daniel Kachhap case TYPE_TWO_POINT_TRIMMING: 283bb34b4c8SAmit Daniel Kachhap temp = (temp_code - data->temp_error1) * 284bb34b4c8SAmit Daniel Kachhap (pdata->second_point_trim - pdata->first_point_trim) / 285bb34b4c8SAmit Daniel Kachhap (data->temp_error2 - data->temp_error1) + 286bb34b4c8SAmit Daniel Kachhap pdata->first_point_trim; 28759dfa54cSAmit Daniel Kachhap break; 28859dfa54cSAmit Daniel Kachhap case TYPE_ONE_POINT_TRIMMING: 289bb34b4c8SAmit Daniel Kachhap temp = temp_code - data->temp_error1 + pdata->first_point_trim; 29059dfa54cSAmit Daniel Kachhap break; 29159dfa54cSAmit Daniel Kachhap default: 292bb34b4c8SAmit Daniel Kachhap temp = temp_code - pdata->default_temp_offset; 29359dfa54cSAmit Daniel Kachhap break; 29459dfa54cSAmit Daniel Kachhap } 295ddb31d43SBartlomiej Zolnierkiewicz 29659dfa54cSAmit Daniel Kachhap return temp; 29759dfa54cSAmit Daniel Kachhap } 29859dfa54cSAmit Daniel Kachhap 2998328a4b1SBartlomiej Zolnierkiewicz static void sanitize_temp_error(struct exynos_tmu_data *data, u32 trim_info) 300b835ced1SBartlomiej Zolnierkiewicz { 30159dfa54cSAmit Daniel Kachhap struct exynos_tmu_platform_data *pdata = data->pdata; 30259dfa54cSAmit Daniel Kachhap 303b8d582b9SAmit Daniel Kachhap data->temp_error1 = trim_info & EXYNOS_TMU_TEMP_MASK; 30499d67fb9SBartlomiej Zolnierkiewicz data->temp_error2 = ((trim_info >> EXYNOS_TRIMINFO_85_SHIFT) & 305b8d582b9SAmit Daniel Kachhap EXYNOS_TMU_TEMP_MASK); 30659dfa54cSAmit Daniel Kachhap 3075000806cSAmit Daniel Kachhap if (!data->temp_error1 || 3085000806cSAmit Daniel Kachhap (pdata->min_efuse_value > data->temp_error1) || 3095000806cSAmit Daniel Kachhap (data->temp_error1 > pdata->max_efuse_value)) 3105000806cSAmit Daniel Kachhap data->temp_error1 = pdata->efuse_value & EXYNOS_TMU_TEMP_MASK; 3115000806cSAmit Daniel Kachhap 3125000806cSAmit Daniel Kachhap if (!data->temp_error2) 3135000806cSAmit Daniel Kachhap data->temp_error2 = 31499d67fb9SBartlomiej Zolnierkiewicz (pdata->efuse_value >> EXYNOS_TRIMINFO_85_SHIFT) & 3155000806cSAmit Daniel Kachhap EXYNOS_TMU_TEMP_MASK; 3168328a4b1SBartlomiej Zolnierkiewicz } 31759dfa54cSAmit Daniel Kachhap 318fe87789cSBartlomiej Zolnierkiewicz static u32 get_th_reg(struct exynos_tmu_data *data, u32 threshold, bool falling) 319fe87789cSBartlomiej Zolnierkiewicz { 3203b6a1a80SLukasz Majewski struct thermal_zone_device *tz = data->tzd; 3213b6a1a80SLukasz Majewski const struct thermal_trip * const trips = 3223b6a1a80SLukasz Majewski of_thermal_get_trip_points(tz); 3233b6a1a80SLukasz Majewski unsigned long temp; 324fe87789cSBartlomiej Zolnierkiewicz int i; 325c65d3473STushar Behera 3263b6a1a80SLukasz Majewski if (!trips) { 3273b6a1a80SLukasz Majewski pr_err("%s: Cannot get trip points from of-thermal.c!\n", 3283b6a1a80SLukasz Majewski __func__); 3293b6a1a80SLukasz Majewski return 0; 3303b6a1a80SLukasz Majewski } 331fe87789cSBartlomiej Zolnierkiewicz 3323b6a1a80SLukasz Majewski for (i = 0; i < of_thermal_get_ntrips(tz); i++) { 3333b6a1a80SLukasz Majewski if (trips[i].type == THERMAL_TRIP_CRITICAL) 3343b6a1a80SLukasz Majewski continue; 3353b6a1a80SLukasz Majewski 3363b6a1a80SLukasz Majewski temp = trips[i].temperature / MCELSIUS; 337fe87789cSBartlomiej Zolnierkiewicz if (falling) 3383b6a1a80SLukasz Majewski temp -= (trips[i].hysteresis / MCELSIUS); 339fe87789cSBartlomiej Zolnierkiewicz else 340fe87789cSBartlomiej Zolnierkiewicz threshold &= ~(0xff << 8 * i); 341fe87789cSBartlomiej Zolnierkiewicz 342fe87789cSBartlomiej Zolnierkiewicz threshold |= temp_to_code(data, temp) << 8 * i; 34359dfa54cSAmit Daniel Kachhap } 34459dfa54cSAmit Daniel Kachhap 345fe87789cSBartlomiej Zolnierkiewicz return threshold; 346fe87789cSBartlomiej Zolnierkiewicz } 34759dfa54cSAmit Daniel Kachhap 34859dfa54cSAmit Daniel Kachhap static int exynos_tmu_initialize(struct platform_device *pdev) 34959dfa54cSAmit Daniel Kachhap { 35059dfa54cSAmit Daniel Kachhap struct exynos_tmu_data *data = platform_get_drvdata(pdev); 35172d1100bSBartlomiej Zolnierkiewicz int ret; 3527ca04e58SAmit Daniel Kachhap 3533a3a5f15SKrzysztof Kozlowski if (of_thermal_get_ntrips(data->tzd) > data->ntrip) { 3543a3a5f15SKrzysztof Kozlowski dev_info(&pdev->dev, 3553a3a5f15SKrzysztof Kozlowski "More trip points than supported by this TMU.\n"); 3563a3a5f15SKrzysztof Kozlowski dev_info(&pdev->dev, 3573a3a5f15SKrzysztof Kozlowski "%d trip points should be configured in polling mode.\n", 3583a3a5f15SKrzysztof Kozlowski (of_thermal_get_ntrips(data->tzd) - data->ntrip)); 3593a3a5f15SKrzysztof Kozlowski } 3603a3a5f15SKrzysztof Kozlowski 36159dfa54cSAmit Daniel Kachhap mutex_lock(&data->lock); 36259dfa54cSAmit Daniel Kachhap clk_enable(data->clk); 36359dfa54cSAmit Daniel Kachhap if (!IS_ERR(data->clk_sec)) 36459dfa54cSAmit Daniel Kachhap clk_enable(data->clk_sec); 36572d1100bSBartlomiej Zolnierkiewicz ret = data->tmu_initialize(pdev); 36659dfa54cSAmit Daniel Kachhap clk_disable(data->clk); 36759dfa54cSAmit Daniel Kachhap mutex_unlock(&data->lock); 36814a11dc7SNaveen Krishna Chatradhi if (!IS_ERR(data->clk_sec)) 36914a11dc7SNaveen Krishna Chatradhi clk_disable(data->clk_sec); 37059dfa54cSAmit Daniel Kachhap 37159dfa54cSAmit Daniel Kachhap return ret; 37259dfa54cSAmit Daniel Kachhap } 37359dfa54cSAmit Daniel Kachhap 374d00671c3SBartlomiej Zolnierkiewicz static u32 get_con_reg(struct exynos_tmu_data *data, u32 con) 37559dfa54cSAmit Daniel Kachhap { 37659dfa54cSAmit Daniel Kachhap struct exynos_tmu_platform_data *pdata = data->pdata; 37759dfa54cSAmit Daniel Kachhap 3787575983cSBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS4412 || 3797575983cSBartlomiej Zolnierkiewicz data->soc == SOC_ARCH_EXYNOS3250) 3807575983cSBartlomiej Zolnierkiewicz con |= (EXYNOS4412_MUX_ADDR_VALUE << EXYNOS4412_MUX_ADDR_SHIFT); 38186f5362eSLukasz Majewski 38299d67fb9SBartlomiej Zolnierkiewicz con &= ~(EXYNOS_TMU_REF_VOLTAGE_MASK << EXYNOS_TMU_REF_VOLTAGE_SHIFT); 38399d67fb9SBartlomiej Zolnierkiewicz con |= pdata->reference_voltage << EXYNOS_TMU_REF_VOLTAGE_SHIFT; 384d0a0ce3eSAmit Daniel Kachhap 38599d67fb9SBartlomiej Zolnierkiewicz con &= ~(EXYNOS_TMU_BUF_SLOPE_SEL_MASK << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT); 38699d67fb9SBartlomiej Zolnierkiewicz con |= (pdata->gain << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT); 387d0a0ce3eSAmit Daniel Kachhap 388d0a0ce3eSAmit Daniel Kachhap if (pdata->noise_cancel_mode) { 389b9504a6aSBartlomiej Zolnierkiewicz con &= ~(EXYNOS_TMU_TRIP_MODE_MASK << EXYNOS_TMU_TRIP_MODE_SHIFT); 390b9504a6aSBartlomiej Zolnierkiewicz con |= (pdata->noise_cancel_mode << EXYNOS_TMU_TRIP_MODE_SHIFT); 39159dfa54cSAmit Daniel Kachhap } 39259dfa54cSAmit Daniel Kachhap 393d00671c3SBartlomiej Zolnierkiewicz return con; 394d00671c3SBartlomiej Zolnierkiewicz } 395d00671c3SBartlomiej Zolnierkiewicz 396d00671c3SBartlomiej Zolnierkiewicz static void exynos_tmu_control(struct platform_device *pdev, bool on) 397d00671c3SBartlomiej Zolnierkiewicz { 398d00671c3SBartlomiej Zolnierkiewicz struct exynos_tmu_data *data = platform_get_drvdata(pdev); 399d00671c3SBartlomiej Zolnierkiewicz 400d00671c3SBartlomiej Zolnierkiewicz mutex_lock(&data->lock); 401d00671c3SBartlomiej Zolnierkiewicz clk_enable(data->clk); 40237f9034fSBartlomiej Zolnierkiewicz data->tmu_control(pdev, on); 40388fc6f73SMarek Szyprowski data->enabled = on; 40459dfa54cSAmit Daniel Kachhap clk_disable(data->clk); 40559dfa54cSAmit Daniel Kachhap mutex_unlock(&data->lock); 40659dfa54cSAmit Daniel Kachhap } 40759dfa54cSAmit Daniel Kachhap 40872d1100bSBartlomiej Zolnierkiewicz static int exynos4210_tmu_initialize(struct platform_device *pdev) 40972d1100bSBartlomiej Zolnierkiewicz { 41072d1100bSBartlomiej Zolnierkiewicz struct exynos_tmu_data *data = platform_get_drvdata(pdev); 4113b6a1a80SLukasz Majewski struct thermal_zone_device *tz = data->tzd; 4123b6a1a80SLukasz Majewski const struct thermal_trip * const trips = 4133b6a1a80SLukasz Majewski of_thermal_get_trip_points(tz); 41472d1100bSBartlomiej Zolnierkiewicz int ret = 0, threshold_code, i; 4153b6a1a80SLukasz Majewski unsigned long reference, temp; 4163b6a1a80SLukasz Majewski unsigned int status; 4173b6a1a80SLukasz Majewski 4183b6a1a80SLukasz Majewski if (!trips) { 4193b6a1a80SLukasz Majewski pr_err("%s: Cannot get trip points from of-thermal.c!\n", 4203b6a1a80SLukasz Majewski __func__); 4213b6a1a80SLukasz Majewski ret = -ENODEV; 4223b6a1a80SLukasz Majewski goto out; 4233b6a1a80SLukasz Majewski } 42472d1100bSBartlomiej Zolnierkiewicz 42572d1100bSBartlomiej Zolnierkiewicz status = readb(data->base + EXYNOS_TMU_REG_STATUS); 42672d1100bSBartlomiej Zolnierkiewicz if (!status) { 42772d1100bSBartlomiej Zolnierkiewicz ret = -EBUSY; 42872d1100bSBartlomiej Zolnierkiewicz goto out; 42972d1100bSBartlomiej Zolnierkiewicz } 43072d1100bSBartlomiej Zolnierkiewicz 43172d1100bSBartlomiej Zolnierkiewicz sanitize_temp_error(data, readl(data->base + EXYNOS_TMU_REG_TRIMINFO)); 43272d1100bSBartlomiej Zolnierkiewicz 43372d1100bSBartlomiej Zolnierkiewicz /* Write temperature code for threshold */ 4343b6a1a80SLukasz Majewski reference = trips[0].temperature / MCELSIUS; 4353b6a1a80SLukasz Majewski threshold_code = temp_to_code(data, reference); 4363b6a1a80SLukasz Majewski if (threshold_code < 0) { 4373b6a1a80SLukasz Majewski ret = threshold_code; 4383b6a1a80SLukasz Majewski goto out; 4393b6a1a80SLukasz Majewski } 44072d1100bSBartlomiej Zolnierkiewicz writeb(threshold_code, data->base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP); 44172d1100bSBartlomiej Zolnierkiewicz 4423b6a1a80SLukasz Majewski for (i = 0; i < of_thermal_get_ntrips(tz); i++) { 4433b6a1a80SLukasz Majewski temp = trips[i].temperature / MCELSIUS; 4443b6a1a80SLukasz Majewski writeb(temp - reference, data->base + 44572d1100bSBartlomiej Zolnierkiewicz EXYNOS4210_TMU_REG_TRIG_LEVEL0 + i * 4); 4463b6a1a80SLukasz Majewski } 44772d1100bSBartlomiej Zolnierkiewicz 448a7331f72SBartlomiej Zolnierkiewicz data->tmu_clear_irqs(data); 44972d1100bSBartlomiej Zolnierkiewicz out: 45072d1100bSBartlomiej Zolnierkiewicz return ret; 45172d1100bSBartlomiej Zolnierkiewicz } 45272d1100bSBartlomiej Zolnierkiewicz 45372d1100bSBartlomiej Zolnierkiewicz static int exynos4412_tmu_initialize(struct platform_device *pdev) 45472d1100bSBartlomiej Zolnierkiewicz { 45572d1100bSBartlomiej Zolnierkiewicz struct exynos_tmu_data *data = platform_get_drvdata(pdev); 4563b6a1a80SLukasz Majewski const struct thermal_trip * const trips = 4573b6a1a80SLukasz Majewski of_thermal_get_trip_points(data->tzd); 45872d1100bSBartlomiej Zolnierkiewicz unsigned int status, trim_info, con, ctrl, rising_threshold; 45972d1100bSBartlomiej Zolnierkiewicz int ret = 0, threshold_code, i; 4603b6a1a80SLukasz Majewski unsigned long crit_temp = 0; 46172d1100bSBartlomiej Zolnierkiewicz 46272d1100bSBartlomiej Zolnierkiewicz status = readb(data->base + EXYNOS_TMU_REG_STATUS); 46372d1100bSBartlomiej Zolnierkiewicz if (!status) { 46472d1100bSBartlomiej Zolnierkiewicz ret = -EBUSY; 46572d1100bSBartlomiej Zolnierkiewicz goto out; 46672d1100bSBartlomiej Zolnierkiewicz } 46772d1100bSBartlomiej Zolnierkiewicz 46872d1100bSBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS3250 || 46972d1100bSBartlomiej Zolnierkiewicz data->soc == SOC_ARCH_EXYNOS4412 || 47072d1100bSBartlomiej Zolnierkiewicz data->soc == SOC_ARCH_EXYNOS5250) { 47172d1100bSBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS3250) { 47272d1100bSBartlomiej Zolnierkiewicz ctrl = readl(data->base + EXYNOS_TMU_TRIMINFO_CON1); 47372d1100bSBartlomiej Zolnierkiewicz ctrl |= EXYNOS_TRIMINFO_RELOAD_ENABLE; 47472d1100bSBartlomiej Zolnierkiewicz writel(ctrl, data->base + EXYNOS_TMU_TRIMINFO_CON1); 47572d1100bSBartlomiej Zolnierkiewicz } 47672d1100bSBartlomiej Zolnierkiewicz ctrl = readl(data->base + EXYNOS_TMU_TRIMINFO_CON2); 47772d1100bSBartlomiej Zolnierkiewicz ctrl |= EXYNOS_TRIMINFO_RELOAD_ENABLE; 47872d1100bSBartlomiej Zolnierkiewicz writel(ctrl, data->base + EXYNOS_TMU_TRIMINFO_CON2); 47972d1100bSBartlomiej Zolnierkiewicz } 48072d1100bSBartlomiej Zolnierkiewicz 48172d1100bSBartlomiej Zolnierkiewicz /* On exynos5420 the triminfo register is in the shared space */ 48272d1100bSBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) 48372d1100bSBartlomiej Zolnierkiewicz trim_info = readl(data->base_second + EXYNOS_TMU_REG_TRIMINFO); 48472d1100bSBartlomiej Zolnierkiewicz else 48572d1100bSBartlomiej Zolnierkiewicz trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); 48672d1100bSBartlomiej Zolnierkiewicz 48772d1100bSBartlomiej Zolnierkiewicz sanitize_temp_error(data, trim_info); 48872d1100bSBartlomiej Zolnierkiewicz 48972d1100bSBartlomiej Zolnierkiewicz /* Write temperature code for rising and falling threshold */ 49072d1100bSBartlomiej Zolnierkiewicz rising_threshold = readl(data->base + EXYNOS_THD_TEMP_RISE); 49172d1100bSBartlomiej Zolnierkiewicz rising_threshold = get_th_reg(data, rising_threshold, false); 49272d1100bSBartlomiej Zolnierkiewicz writel(rising_threshold, data->base + EXYNOS_THD_TEMP_RISE); 49372d1100bSBartlomiej Zolnierkiewicz writel(get_th_reg(data, 0, true), data->base + EXYNOS_THD_TEMP_FALL); 49472d1100bSBartlomiej Zolnierkiewicz 495a7331f72SBartlomiej Zolnierkiewicz data->tmu_clear_irqs(data); 49672d1100bSBartlomiej Zolnierkiewicz 49772d1100bSBartlomiej Zolnierkiewicz /* if last threshold limit is also present */ 4983b6a1a80SLukasz Majewski for (i = 0; i < of_thermal_get_ntrips(data->tzd); i++) { 4993b6a1a80SLukasz Majewski if (trips[i].type == THERMAL_TRIP_CRITICAL) { 5003b6a1a80SLukasz Majewski crit_temp = trips[i].temperature; 5013b6a1a80SLukasz Majewski break; 5023b6a1a80SLukasz Majewski } 5033b6a1a80SLukasz Majewski } 5043b6a1a80SLukasz Majewski 5053b6a1a80SLukasz Majewski if (i == of_thermal_get_ntrips(data->tzd)) { 5063b6a1a80SLukasz Majewski pr_err("%s: No CRITICAL trip point defined at of-thermal.c!\n", 5073b6a1a80SLukasz Majewski __func__); 5083b6a1a80SLukasz Majewski ret = -EINVAL; 5093b6a1a80SLukasz Majewski goto out; 5103b6a1a80SLukasz Majewski } 5113b6a1a80SLukasz Majewski 5123b6a1a80SLukasz Majewski threshold_code = temp_to_code(data, crit_temp / MCELSIUS); 51372d1100bSBartlomiej Zolnierkiewicz /* 1-4 level to be assigned in th0 reg */ 51472d1100bSBartlomiej Zolnierkiewicz rising_threshold &= ~(0xff << 8 * i); 51572d1100bSBartlomiej Zolnierkiewicz rising_threshold |= threshold_code << 8 * i; 51672d1100bSBartlomiej Zolnierkiewicz writel(rising_threshold, data->base + EXYNOS_THD_TEMP_RISE); 51772d1100bSBartlomiej Zolnierkiewicz con = readl(data->base + EXYNOS_TMU_REG_CONTROL); 51872d1100bSBartlomiej Zolnierkiewicz con |= (1 << EXYNOS_TMU_THERM_TRIP_EN_SHIFT); 51972d1100bSBartlomiej Zolnierkiewicz writel(con, data->base + EXYNOS_TMU_REG_CONTROL); 5203b6a1a80SLukasz Majewski 52172d1100bSBartlomiej Zolnierkiewicz out: 52272d1100bSBartlomiej Zolnierkiewicz return ret; 52372d1100bSBartlomiej Zolnierkiewicz } 52472d1100bSBartlomiej Zolnierkiewicz 525488c7455SChanwoo Choi static int exynos5433_tmu_initialize(struct platform_device *pdev) 526488c7455SChanwoo Choi { 527488c7455SChanwoo Choi struct exynos_tmu_data *data = platform_get_drvdata(pdev); 528488c7455SChanwoo Choi struct exynos_tmu_platform_data *pdata = data->pdata; 529488c7455SChanwoo Choi struct thermal_zone_device *tz = data->tzd; 530488c7455SChanwoo Choi unsigned int status, trim_info; 531488c7455SChanwoo Choi unsigned int rising_threshold = 0, falling_threshold = 0; 53217e8351aSSascha Hauer int temp, temp_hist; 533488c7455SChanwoo Choi int ret = 0, threshold_code, i, sensor_id, cal_type; 534488c7455SChanwoo Choi 535488c7455SChanwoo Choi status = readb(data->base + EXYNOS_TMU_REG_STATUS); 536488c7455SChanwoo Choi if (!status) { 537488c7455SChanwoo Choi ret = -EBUSY; 538488c7455SChanwoo Choi goto out; 539488c7455SChanwoo Choi } 540488c7455SChanwoo Choi 541488c7455SChanwoo Choi trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); 542488c7455SChanwoo Choi sanitize_temp_error(data, trim_info); 543488c7455SChanwoo Choi 544488c7455SChanwoo Choi /* Read the temperature sensor id */ 545488c7455SChanwoo Choi sensor_id = (trim_info & EXYNOS5433_TRIMINFO_SENSOR_ID_MASK) 546488c7455SChanwoo Choi >> EXYNOS5433_TRIMINFO_SENSOR_ID_SHIFT; 547488c7455SChanwoo Choi dev_info(&pdev->dev, "Temperature sensor ID: 0x%x\n", sensor_id); 548488c7455SChanwoo Choi 549488c7455SChanwoo Choi /* Read the calibration mode */ 550488c7455SChanwoo Choi writel(trim_info, data->base + EXYNOS_TMU_REG_TRIMINFO); 551488c7455SChanwoo Choi cal_type = (trim_info & EXYNOS5433_TRIMINFO_CALIB_SEL_MASK) 552488c7455SChanwoo Choi >> EXYNOS5433_TRIMINFO_CALIB_SEL_SHIFT; 553488c7455SChanwoo Choi 554488c7455SChanwoo Choi switch (cal_type) { 555488c7455SChanwoo Choi case EXYNOS5433_TRIMINFO_ONE_POINT_TRIMMING: 556488c7455SChanwoo Choi pdata->cal_type = TYPE_ONE_POINT_TRIMMING; 557488c7455SChanwoo Choi break; 558488c7455SChanwoo Choi case EXYNOS5433_TRIMINFO_TWO_POINT_TRIMMING: 559488c7455SChanwoo Choi pdata->cal_type = TYPE_TWO_POINT_TRIMMING; 560488c7455SChanwoo Choi break; 561488c7455SChanwoo Choi default: 562488c7455SChanwoo Choi pdata->cal_type = TYPE_ONE_POINT_TRIMMING; 563488c7455SChanwoo Choi break; 564baba1ebbSKrzysztof Kozlowski } 565488c7455SChanwoo Choi 566488c7455SChanwoo Choi dev_info(&pdev->dev, "Calibration type is %d-point calibration\n", 567488c7455SChanwoo Choi cal_type ? 2 : 1); 568488c7455SChanwoo Choi 569488c7455SChanwoo Choi /* Write temperature code for rising and falling threshold */ 570488c7455SChanwoo Choi for (i = 0; i < of_thermal_get_ntrips(tz); i++) { 571488c7455SChanwoo Choi int rising_reg_offset, falling_reg_offset; 572488c7455SChanwoo Choi int j = 0; 573488c7455SChanwoo Choi 574488c7455SChanwoo Choi switch (i) { 575488c7455SChanwoo Choi case 0: 576488c7455SChanwoo Choi case 1: 577488c7455SChanwoo Choi case 2: 578488c7455SChanwoo Choi case 3: 579488c7455SChanwoo Choi rising_reg_offset = EXYNOS5433_THD_TEMP_RISE3_0; 580488c7455SChanwoo Choi falling_reg_offset = EXYNOS5433_THD_TEMP_FALL3_0; 581488c7455SChanwoo Choi j = i; 582488c7455SChanwoo Choi break; 583488c7455SChanwoo Choi case 4: 584488c7455SChanwoo Choi case 5: 585488c7455SChanwoo Choi case 6: 586488c7455SChanwoo Choi case 7: 587488c7455SChanwoo Choi rising_reg_offset = EXYNOS5433_THD_TEMP_RISE7_4; 588488c7455SChanwoo Choi falling_reg_offset = EXYNOS5433_THD_TEMP_FALL7_4; 589488c7455SChanwoo Choi j = i - 4; 590488c7455SChanwoo Choi break; 591488c7455SChanwoo Choi default: 592488c7455SChanwoo Choi continue; 593488c7455SChanwoo Choi } 594488c7455SChanwoo Choi 595488c7455SChanwoo Choi /* Write temperature code for rising threshold */ 596488c7455SChanwoo Choi tz->ops->get_trip_temp(tz, i, &temp); 597488c7455SChanwoo Choi temp /= MCELSIUS; 598488c7455SChanwoo Choi threshold_code = temp_to_code(data, temp); 599488c7455SChanwoo Choi 600488c7455SChanwoo Choi rising_threshold = readl(data->base + rising_reg_offset); 601488c7455SChanwoo Choi rising_threshold |= (threshold_code << j * 8); 602488c7455SChanwoo Choi writel(rising_threshold, data->base + rising_reg_offset); 603488c7455SChanwoo Choi 604488c7455SChanwoo Choi /* Write temperature code for falling threshold */ 605488c7455SChanwoo Choi tz->ops->get_trip_hyst(tz, i, &temp_hist); 606488c7455SChanwoo Choi temp_hist = temp - (temp_hist / MCELSIUS); 607488c7455SChanwoo Choi threshold_code = temp_to_code(data, temp_hist); 608488c7455SChanwoo Choi 609488c7455SChanwoo Choi falling_threshold = readl(data->base + falling_reg_offset); 610488c7455SChanwoo Choi falling_threshold &= ~(0xff << j * 8); 611488c7455SChanwoo Choi falling_threshold |= (threshold_code << j * 8); 612488c7455SChanwoo Choi writel(falling_threshold, data->base + falling_reg_offset); 613488c7455SChanwoo Choi } 614488c7455SChanwoo Choi 615488c7455SChanwoo Choi data->tmu_clear_irqs(data); 616488c7455SChanwoo Choi out: 617488c7455SChanwoo Choi return ret; 618488c7455SChanwoo Choi } 619488c7455SChanwoo Choi 62072d1100bSBartlomiej Zolnierkiewicz static int exynos5440_tmu_initialize(struct platform_device *pdev) 62172d1100bSBartlomiej Zolnierkiewicz { 62272d1100bSBartlomiej Zolnierkiewicz struct exynos_tmu_data *data = platform_get_drvdata(pdev); 62372d1100bSBartlomiej Zolnierkiewicz unsigned int trim_info = 0, con, rising_threshold; 624e35dbb4dSKrzysztof Kozlowski int threshold_code; 62517e8351aSSascha Hauer int crit_temp = 0; 62672d1100bSBartlomiej Zolnierkiewicz 62772d1100bSBartlomiej Zolnierkiewicz /* 62872d1100bSBartlomiej Zolnierkiewicz * For exynos5440 soc triminfo value is swapped between TMU0 and 62972d1100bSBartlomiej Zolnierkiewicz * TMU2, so the below logic is needed. 63072d1100bSBartlomiej Zolnierkiewicz */ 63172d1100bSBartlomiej Zolnierkiewicz switch (data->id) { 63272d1100bSBartlomiej Zolnierkiewicz case 0: 63372d1100bSBartlomiej Zolnierkiewicz trim_info = readl(data->base + EXYNOS5440_EFUSE_SWAP_OFFSET + 63472d1100bSBartlomiej Zolnierkiewicz EXYNOS5440_TMU_S0_7_TRIM); 63572d1100bSBartlomiej Zolnierkiewicz break; 63672d1100bSBartlomiej Zolnierkiewicz case 1: 63772d1100bSBartlomiej Zolnierkiewicz trim_info = readl(data->base + EXYNOS5440_TMU_S0_7_TRIM); 63872d1100bSBartlomiej Zolnierkiewicz break; 63972d1100bSBartlomiej Zolnierkiewicz case 2: 64072d1100bSBartlomiej Zolnierkiewicz trim_info = readl(data->base - EXYNOS5440_EFUSE_SWAP_OFFSET + 64172d1100bSBartlomiej Zolnierkiewicz EXYNOS5440_TMU_S0_7_TRIM); 64272d1100bSBartlomiej Zolnierkiewicz } 64372d1100bSBartlomiej Zolnierkiewicz sanitize_temp_error(data, trim_info); 64472d1100bSBartlomiej Zolnierkiewicz 64572d1100bSBartlomiej Zolnierkiewicz /* Write temperature code for rising and falling threshold */ 64672d1100bSBartlomiej Zolnierkiewicz rising_threshold = readl(data->base + EXYNOS5440_TMU_S0_7_TH0); 64772d1100bSBartlomiej Zolnierkiewicz rising_threshold = get_th_reg(data, rising_threshold, false); 64872d1100bSBartlomiej Zolnierkiewicz writel(rising_threshold, data->base + EXYNOS5440_TMU_S0_7_TH0); 64972d1100bSBartlomiej Zolnierkiewicz writel(0, data->base + EXYNOS5440_TMU_S0_7_TH1); 65072d1100bSBartlomiej Zolnierkiewicz 651a7331f72SBartlomiej Zolnierkiewicz data->tmu_clear_irqs(data); 65272d1100bSBartlomiej Zolnierkiewicz 65372d1100bSBartlomiej Zolnierkiewicz /* if last threshold limit is also present */ 6543b6a1a80SLukasz Majewski if (!data->tzd->ops->get_crit_temp(data->tzd, &crit_temp)) { 6553b6a1a80SLukasz Majewski threshold_code = temp_to_code(data, crit_temp / MCELSIUS); 65672d1100bSBartlomiej Zolnierkiewicz /* 5th level to be assigned in th2 reg */ 65772d1100bSBartlomiej Zolnierkiewicz rising_threshold = 65872d1100bSBartlomiej Zolnierkiewicz threshold_code << EXYNOS5440_TMU_TH_RISE4_SHIFT; 65972d1100bSBartlomiej Zolnierkiewicz writel(rising_threshold, data->base + EXYNOS5440_TMU_S0_7_TH2); 66072d1100bSBartlomiej Zolnierkiewicz con = readl(data->base + EXYNOS5440_TMU_S0_7_CTRL); 66172d1100bSBartlomiej Zolnierkiewicz con |= (1 << EXYNOS_TMU_THERM_TRIP_EN_SHIFT); 66272d1100bSBartlomiej Zolnierkiewicz writel(con, data->base + EXYNOS5440_TMU_S0_7_CTRL); 66372d1100bSBartlomiej Zolnierkiewicz } 66472d1100bSBartlomiej Zolnierkiewicz /* Clear the PMIN in the common TMU register */ 66572d1100bSBartlomiej Zolnierkiewicz if (!data->id) 66672d1100bSBartlomiej Zolnierkiewicz writel(0, data->base_second + EXYNOS5440_TMU_PMIN); 667e35dbb4dSKrzysztof Kozlowski 668e35dbb4dSKrzysztof Kozlowski return 0; 66972d1100bSBartlomiej Zolnierkiewicz } 67072d1100bSBartlomiej Zolnierkiewicz 6716c247393SAbhilash Kesavan static int exynos7_tmu_initialize(struct platform_device *pdev) 6726c247393SAbhilash Kesavan { 6736c247393SAbhilash Kesavan struct exynos_tmu_data *data = platform_get_drvdata(pdev); 6746c247393SAbhilash Kesavan struct thermal_zone_device *tz = data->tzd; 6756c247393SAbhilash Kesavan struct exynos_tmu_platform_data *pdata = data->pdata; 6766c247393SAbhilash Kesavan unsigned int status, trim_info; 6776c247393SAbhilash Kesavan unsigned int rising_threshold = 0, falling_threshold = 0; 6786c247393SAbhilash Kesavan int ret = 0, threshold_code, i; 67917e8351aSSascha Hauer int temp, temp_hist; 6806c247393SAbhilash Kesavan unsigned int reg_off, bit_off; 6816c247393SAbhilash Kesavan 6826c247393SAbhilash Kesavan status = readb(data->base + EXYNOS_TMU_REG_STATUS); 6836c247393SAbhilash Kesavan if (!status) { 6846c247393SAbhilash Kesavan ret = -EBUSY; 6856c247393SAbhilash Kesavan goto out; 6866c247393SAbhilash Kesavan } 6876c247393SAbhilash Kesavan 6886c247393SAbhilash Kesavan trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); 6896c247393SAbhilash Kesavan 6906c247393SAbhilash Kesavan data->temp_error1 = trim_info & EXYNOS7_TMU_TEMP_MASK; 6916c247393SAbhilash Kesavan if (!data->temp_error1 || 6926c247393SAbhilash Kesavan (pdata->min_efuse_value > data->temp_error1) || 6936c247393SAbhilash Kesavan (data->temp_error1 > pdata->max_efuse_value)) 6946c247393SAbhilash Kesavan data->temp_error1 = pdata->efuse_value & EXYNOS_TMU_TEMP_MASK; 6956c247393SAbhilash Kesavan 6966c247393SAbhilash Kesavan /* Write temperature code for rising and falling threshold */ 6976c247393SAbhilash Kesavan for (i = (of_thermal_get_ntrips(tz) - 1); i >= 0; i--) { 6986c247393SAbhilash Kesavan /* 6996c247393SAbhilash Kesavan * On exynos7 there are 4 rising and 4 falling threshold 7006c247393SAbhilash Kesavan * registers (0x50-0x5c and 0x60-0x6c respectively). Each 7016c247393SAbhilash Kesavan * register holds the value of two threshold levels (at bit 7026c247393SAbhilash Kesavan * offsets 0 and 16). Based on the fact that there are atmost 7036c247393SAbhilash Kesavan * eight possible trigger levels, calculate the register and 7046c247393SAbhilash Kesavan * bit offsets where the threshold levels are to be written. 7056c247393SAbhilash Kesavan * 7066c247393SAbhilash Kesavan * e.g. EXYNOS7_THD_TEMP_RISE7_6 (0x50) 7076c247393SAbhilash Kesavan * [24:16] - Threshold level 7 7086c247393SAbhilash Kesavan * [8:0] - Threshold level 6 7096c247393SAbhilash Kesavan * e.g. EXYNOS7_THD_TEMP_RISE5_4 (0x54) 7106c247393SAbhilash Kesavan * [24:16] - Threshold level 5 7116c247393SAbhilash Kesavan * [8:0] - Threshold level 4 7126c247393SAbhilash Kesavan * 7136c247393SAbhilash Kesavan * and similarly for falling thresholds. 7146c247393SAbhilash Kesavan * 7156c247393SAbhilash Kesavan * Based on the above, calculate the register and bit offsets 7166c247393SAbhilash Kesavan * for rising/falling threshold levels and populate them. 7176c247393SAbhilash Kesavan */ 7186c247393SAbhilash Kesavan reg_off = ((7 - i) / 2) * 4; 7196c247393SAbhilash Kesavan bit_off = ((8 - i) % 2); 7206c247393SAbhilash Kesavan 7216c247393SAbhilash Kesavan tz->ops->get_trip_temp(tz, i, &temp); 7226c247393SAbhilash Kesavan temp /= MCELSIUS; 7236c247393SAbhilash Kesavan 7246c247393SAbhilash Kesavan tz->ops->get_trip_hyst(tz, i, &temp_hist); 7256c247393SAbhilash Kesavan temp_hist = temp - (temp_hist / MCELSIUS); 7266c247393SAbhilash Kesavan 7276c247393SAbhilash Kesavan /* Set 9-bit temperature code for rising threshold levels */ 7286c247393SAbhilash Kesavan threshold_code = temp_to_code(data, temp); 7296c247393SAbhilash Kesavan rising_threshold = readl(data->base + 7306c247393SAbhilash Kesavan EXYNOS7_THD_TEMP_RISE7_6 + reg_off); 7316c247393SAbhilash Kesavan rising_threshold &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off)); 7326c247393SAbhilash Kesavan rising_threshold |= threshold_code << (16 * bit_off); 7336c247393SAbhilash Kesavan writel(rising_threshold, 7346c247393SAbhilash Kesavan data->base + EXYNOS7_THD_TEMP_RISE7_6 + reg_off); 7356c247393SAbhilash Kesavan 7366c247393SAbhilash Kesavan /* Set 9-bit temperature code for falling threshold levels */ 7376c247393SAbhilash Kesavan threshold_code = temp_to_code(data, temp_hist); 7386c247393SAbhilash Kesavan falling_threshold &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off)); 7396c247393SAbhilash Kesavan falling_threshold |= threshold_code << (16 * bit_off); 7406c247393SAbhilash Kesavan writel(falling_threshold, 7416c247393SAbhilash Kesavan data->base + EXYNOS7_THD_TEMP_FALL7_6 + reg_off); 7426c247393SAbhilash Kesavan } 7436c247393SAbhilash Kesavan 7446c247393SAbhilash Kesavan data->tmu_clear_irqs(data); 7456c247393SAbhilash Kesavan out: 7466c247393SAbhilash Kesavan return ret; 7476c247393SAbhilash Kesavan } 7486c247393SAbhilash Kesavan 74937f9034fSBartlomiej Zolnierkiewicz static void exynos4210_tmu_control(struct platform_device *pdev, bool on) 75037f9034fSBartlomiej Zolnierkiewicz { 75137f9034fSBartlomiej Zolnierkiewicz struct exynos_tmu_data *data = platform_get_drvdata(pdev); 7523b6a1a80SLukasz Majewski struct thermal_zone_device *tz = data->tzd; 75337f9034fSBartlomiej Zolnierkiewicz unsigned int con, interrupt_en; 75437f9034fSBartlomiej Zolnierkiewicz 75537f9034fSBartlomiej Zolnierkiewicz con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL)); 75637f9034fSBartlomiej Zolnierkiewicz 75759dfa54cSAmit Daniel Kachhap if (on) { 75859dfa54cSAmit Daniel Kachhap con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); 75959dfa54cSAmit Daniel Kachhap interrupt_en = 7603b6a1a80SLukasz Majewski (of_thermal_is_trip_valid(tz, 3) 7613b6a1a80SLukasz Majewski << EXYNOS_TMU_INTEN_RISE3_SHIFT) | 7623b6a1a80SLukasz Majewski (of_thermal_is_trip_valid(tz, 2) 7633b6a1a80SLukasz Majewski << EXYNOS_TMU_INTEN_RISE2_SHIFT) | 7643b6a1a80SLukasz Majewski (of_thermal_is_trip_valid(tz, 1) 7653b6a1a80SLukasz Majewski << EXYNOS_TMU_INTEN_RISE1_SHIFT) | 7663b6a1a80SLukasz Majewski (of_thermal_is_trip_valid(tz, 0) 7673b6a1a80SLukasz Majewski << EXYNOS_TMU_INTEN_RISE0_SHIFT); 7683b6a1a80SLukasz Majewski 769e0761533SBartlomiej Zolnierkiewicz if (data->soc != SOC_ARCH_EXYNOS4210) 77059dfa54cSAmit Daniel Kachhap interrupt_en |= 77137f9034fSBartlomiej Zolnierkiewicz interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT; 77259dfa54cSAmit Daniel Kachhap } else { 77359dfa54cSAmit Daniel Kachhap con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); 77459dfa54cSAmit Daniel Kachhap interrupt_en = 0; /* Disable all interrupts */ 77559dfa54cSAmit Daniel Kachhap } 77637f9034fSBartlomiej Zolnierkiewicz writel(interrupt_en, data->base + EXYNOS_TMU_REG_INTEN); 77737f9034fSBartlomiej Zolnierkiewicz writel(con, data->base + EXYNOS_TMU_REG_CONTROL); 77837f9034fSBartlomiej Zolnierkiewicz } 77959dfa54cSAmit Daniel Kachhap 780488c7455SChanwoo Choi static void exynos5433_tmu_control(struct platform_device *pdev, bool on) 781488c7455SChanwoo Choi { 782488c7455SChanwoo Choi struct exynos_tmu_data *data = platform_get_drvdata(pdev); 783488c7455SChanwoo Choi struct thermal_zone_device *tz = data->tzd; 784488c7455SChanwoo Choi unsigned int con, interrupt_en, pd_det_en; 785488c7455SChanwoo Choi 786488c7455SChanwoo Choi con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL)); 787488c7455SChanwoo Choi 788488c7455SChanwoo Choi if (on) { 789488c7455SChanwoo Choi con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); 790488c7455SChanwoo Choi interrupt_en = 791488c7455SChanwoo Choi (of_thermal_is_trip_valid(tz, 7) 792488c7455SChanwoo Choi << EXYNOS7_TMU_INTEN_RISE7_SHIFT) | 793488c7455SChanwoo Choi (of_thermal_is_trip_valid(tz, 6) 794488c7455SChanwoo Choi << EXYNOS7_TMU_INTEN_RISE6_SHIFT) | 795488c7455SChanwoo Choi (of_thermal_is_trip_valid(tz, 5) 796488c7455SChanwoo Choi << EXYNOS7_TMU_INTEN_RISE5_SHIFT) | 797488c7455SChanwoo Choi (of_thermal_is_trip_valid(tz, 4) 798488c7455SChanwoo Choi << EXYNOS7_TMU_INTEN_RISE4_SHIFT) | 799488c7455SChanwoo Choi (of_thermal_is_trip_valid(tz, 3) 800488c7455SChanwoo Choi << EXYNOS7_TMU_INTEN_RISE3_SHIFT) | 801488c7455SChanwoo Choi (of_thermal_is_trip_valid(tz, 2) 802488c7455SChanwoo Choi << EXYNOS7_TMU_INTEN_RISE2_SHIFT) | 803488c7455SChanwoo Choi (of_thermal_is_trip_valid(tz, 1) 804488c7455SChanwoo Choi << EXYNOS7_TMU_INTEN_RISE1_SHIFT) | 805488c7455SChanwoo Choi (of_thermal_is_trip_valid(tz, 0) 806488c7455SChanwoo Choi << EXYNOS7_TMU_INTEN_RISE0_SHIFT); 807488c7455SChanwoo Choi 808488c7455SChanwoo Choi interrupt_en |= 809488c7455SChanwoo Choi interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT; 810488c7455SChanwoo Choi } else { 811488c7455SChanwoo Choi con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); 812488c7455SChanwoo Choi interrupt_en = 0; /* Disable all interrupts */ 813488c7455SChanwoo Choi } 814488c7455SChanwoo Choi 815488c7455SChanwoo Choi pd_det_en = on ? EXYNOS5433_PD_DET_EN : 0; 816488c7455SChanwoo Choi 817488c7455SChanwoo Choi writel(pd_det_en, data->base + EXYNOS5433_TMU_PD_DET_EN); 818488c7455SChanwoo Choi writel(interrupt_en, data->base + EXYNOS5433_TMU_REG_INTEN); 819488c7455SChanwoo Choi writel(con, data->base + EXYNOS_TMU_REG_CONTROL); 820488c7455SChanwoo Choi } 821488c7455SChanwoo Choi 82237f9034fSBartlomiej Zolnierkiewicz static void exynos5440_tmu_control(struct platform_device *pdev, bool on) 82337f9034fSBartlomiej Zolnierkiewicz { 82437f9034fSBartlomiej Zolnierkiewicz struct exynos_tmu_data *data = platform_get_drvdata(pdev); 8253b6a1a80SLukasz Majewski struct thermal_zone_device *tz = data->tzd; 82637f9034fSBartlomiej Zolnierkiewicz unsigned int con, interrupt_en; 82737f9034fSBartlomiej Zolnierkiewicz 82837f9034fSBartlomiej Zolnierkiewicz con = get_con_reg(data, readl(data->base + EXYNOS5440_TMU_S0_7_CTRL)); 82937f9034fSBartlomiej Zolnierkiewicz 83037f9034fSBartlomiej Zolnierkiewicz if (on) { 83137f9034fSBartlomiej Zolnierkiewicz con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); 83237f9034fSBartlomiej Zolnierkiewicz interrupt_en = 8333b6a1a80SLukasz Majewski (of_thermal_is_trip_valid(tz, 3) 8343b6a1a80SLukasz Majewski << EXYNOS5440_TMU_INTEN_RISE3_SHIFT) | 8353b6a1a80SLukasz Majewski (of_thermal_is_trip_valid(tz, 2) 8363b6a1a80SLukasz Majewski << EXYNOS5440_TMU_INTEN_RISE2_SHIFT) | 8373b6a1a80SLukasz Majewski (of_thermal_is_trip_valid(tz, 1) 8383b6a1a80SLukasz Majewski << EXYNOS5440_TMU_INTEN_RISE1_SHIFT) | 8393b6a1a80SLukasz Majewski (of_thermal_is_trip_valid(tz, 0) 8403b6a1a80SLukasz Majewski << EXYNOS5440_TMU_INTEN_RISE0_SHIFT); 8413b6a1a80SLukasz Majewski interrupt_en |= 8423b6a1a80SLukasz Majewski interrupt_en << EXYNOS5440_TMU_INTEN_FALL0_SHIFT; 84337f9034fSBartlomiej Zolnierkiewicz } else { 84437f9034fSBartlomiej Zolnierkiewicz con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); 84537f9034fSBartlomiej Zolnierkiewicz interrupt_en = 0; /* Disable all interrupts */ 84637f9034fSBartlomiej Zolnierkiewicz } 84737f9034fSBartlomiej Zolnierkiewicz writel(interrupt_en, data->base + EXYNOS5440_TMU_S0_7_IRQEN); 84837f9034fSBartlomiej Zolnierkiewicz writel(con, data->base + EXYNOS5440_TMU_S0_7_CTRL); 84959dfa54cSAmit Daniel Kachhap } 85059dfa54cSAmit Daniel Kachhap 8516c247393SAbhilash Kesavan static void exynos7_tmu_control(struct platform_device *pdev, bool on) 8526c247393SAbhilash Kesavan { 8536c247393SAbhilash Kesavan struct exynos_tmu_data *data = platform_get_drvdata(pdev); 8546c247393SAbhilash Kesavan struct thermal_zone_device *tz = data->tzd; 8556c247393SAbhilash Kesavan unsigned int con, interrupt_en; 8566c247393SAbhilash Kesavan 8576c247393SAbhilash Kesavan con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL)); 8586c247393SAbhilash Kesavan 8596c247393SAbhilash Kesavan if (on) { 8606c247393SAbhilash Kesavan con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); 86142b696e8SChanwoo Choi con |= (1 << EXYNOS7_PD_DET_EN_SHIFT); 8626c247393SAbhilash Kesavan interrupt_en = 8636c247393SAbhilash Kesavan (of_thermal_is_trip_valid(tz, 7) 8646c247393SAbhilash Kesavan << EXYNOS7_TMU_INTEN_RISE7_SHIFT) | 8656c247393SAbhilash Kesavan (of_thermal_is_trip_valid(tz, 6) 8666c247393SAbhilash Kesavan << EXYNOS7_TMU_INTEN_RISE6_SHIFT) | 8676c247393SAbhilash Kesavan (of_thermal_is_trip_valid(tz, 5) 8686c247393SAbhilash Kesavan << EXYNOS7_TMU_INTEN_RISE5_SHIFT) | 8696c247393SAbhilash Kesavan (of_thermal_is_trip_valid(tz, 4) 8706c247393SAbhilash Kesavan << EXYNOS7_TMU_INTEN_RISE4_SHIFT) | 8716c247393SAbhilash Kesavan (of_thermal_is_trip_valid(tz, 3) 8726c247393SAbhilash Kesavan << EXYNOS7_TMU_INTEN_RISE3_SHIFT) | 8736c247393SAbhilash Kesavan (of_thermal_is_trip_valid(tz, 2) 8746c247393SAbhilash Kesavan << EXYNOS7_TMU_INTEN_RISE2_SHIFT) | 8756c247393SAbhilash Kesavan (of_thermal_is_trip_valid(tz, 1) 8766c247393SAbhilash Kesavan << EXYNOS7_TMU_INTEN_RISE1_SHIFT) | 8776c247393SAbhilash Kesavan (of_thermal_is_trip_valid(tz, 0) 8786c247393SAbhilash Kesavan << EXYNOS7_TMU_INTEN_RISE0_SHIFT); 8796c247393SAbhilash Kesavan 8806c247393SAbhilash Kesavan interrupt_en |= 8816c247393SAbhilash Kesavan interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT; 8826c247393SAbhilash Kesavan } else { 8836c247393SAbhilash Kesavan con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); 88442b696e8SChanwoo Choi con &= ~(1 << EXYNOS7_PD_DET_EN_SHIFT); 8856c247393SAbhilash Kesavan interrupt_en = 0; /* Disable all interrupts */ 8866c247393SAbhilash Kesavan } 8876c247393SAbhilash Kesavan 8886c247393SAbhilash Kesavan writel(interrupt_en, data->base + EXYNOS7_TMU_REG_INTEN); 8896c247393SAbhilash Kesavan writel(con, data->base + EXYNOS_TMU_REG_CONTROL); 8906c247393SAbhilash Kesavan } 8916c247393SAbhilash Kesavan 89217e8351aSSascha Hauer static int exynos_get_temp(void *p, int *temp) 89359dfa54cSAmit Daniel Kachhap { 8943b6a1a80SLukasz Majewski struct exynos_tmu_data *data = p; 895*c8da6cdeSMarek Szyprowski int value, ret = 0; 8963b6a1a80SLukasz Majewski 89788fc6f73SMarek Szyprowski if (!data || !data->tmu_read || !data->enabled) 8983b6a1a80SLukasz Majewski return -EINVAL; 89959dfa54cSAmit Daniel Kachhap 90059dfa54cSAmit Daniel Kachhap mutex_lock(&data->lock); 90159dfa54cSAmit Daniel Kachhap clk_enable(data->clk); 9023b6a1a80SLukasz Majewski 903*c8da6cdeSMarek Szyprowski value = data->tmu_read(data); 904*c8da6cdeSMarek Szyprowski if (value < 0) 905*c8da6cdeSMarek Szyprowski ret = value; 906*c8da6cdeSMarek Szyprowski else 907*c8da6cdeSMarek Szyprowski *temp = code_to_temp(data, value) * MCELSIUS; 9083b6a1a80SLukasz Majewski 90959dfa54cSAmit Daniel Kachhap clk_disable(data->clk); 91059dfa54cSAmit Daniel Kachhap mutex_unlock(&data->lock); 91159dfa54cSAmit Daniel Kachhap 912*c8da6cdeSMarek Szyprowski return ret; 91359dfa54cSAmit Daniel Kachhap } 91459dfa54cSAmit Daniel Kachhap 91559dfa54cSAmit Daniel Kachhap #ifdef CONFIG_THERMAL_EMULATION 916154013eaSBartlomiej Zolnierkiewicz static u32 get_emul_con_reg(struct exynos_tmu_data *data, unsigned int val, 91717e8351aSSascha Hauer int temp) 918154013eaSBartlomiej Zolnierkiewicz { 919154013eaSBartlomiej Zolnierkiewicz if (temp) { 920154013eaSBartlomiej Zolnierkiewicz temp /= MCELSIUS; 921154013eaSBartlomiej Zolnierkiewicz 922d564b55aSBartlomiej Zolnierkiewicz if (data->soc != SOC_ARCH_EXYNOS5440) { 923154013eaSBartlomiej Zolnierkiewicz val &= ~(EXYNOS_EMUL_TIME_MASK << EXYNOS_EMUL_TIME_SHIFT); 924154013eaSBartlomiej Zolnierkiewicz val |= (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT); 925154013eaSBartlomiej Zolnierkiewicz } 9266c247393SAbhilash Kesavan if (data->soc == SOC_ARCH_EXYNOS7) { 9276c247393SAbhilash Kesavan val &= ~(EXYNOS7_EMUL_DATA_MASK << 9286c247393SAbhilash Kesavan EXYNOS7_EMUL_DATA_SHIFT); 9296c247393SAbhilash Kesavan val |= (temp_to_code(data, temp) << 9306c247393SAbhilash Kesavan EXYNOS7_EMUL_DATA_SHIFT) | 931154013eaSBartlomiej Zolnierkiewicz EXYNOS_EMUL_ENABLE; 932154013eaSBartlomiej Zolnierkiewicz } else { 9336c247393SAbhilash Kesavan val &= ~(EXYNOS_EMUL_DATA_MASK << 9346c247393SAbhilash Kesavan EXYNOS_EMUL_DATA_SHIFT); 9356c247393SAbhilash Kesavan val |= (temp_to_code(data, temp) << 9366c247393SAbhilash Kesavan EXYNOS_EMUL_DATA_SHIFT) | 9376c247393SAbhilash Kesavan EXYNOS_EMUL_ENABLE; 9386c247393SAbhilash Kesavan } 9396c247393SAbhilash Kesavan } else { 940154013eaSBartlomiej Zolnierkiewicz val &= ~EXYNOS_EMUL_ENABLE; 941154013eaSBartlomiej Zolnierkiewicz } 942154013eaSBartlomiej Zolnierkiewicz 943154013eaSBartlomiej Zolnierkiewicz return val; 944154013eaSBartlomiej Zolnierkiewicz } 945154013eaSBartlomiej Zolnierkiewicz 946285d994aSBartlomiej Zolnierkiewicz static void exynos4412_tmu_set_emulation(struct exynos_tmu_data *data, 94717e8351aSSascha Hauer int temp) 948285d994aSBartlomiej Zolnierkiewicz { 949285d994aSBartlomiej Zolnierkiewicz unsigned int val; 950285d994aSBartlomiej Zolnierkiewicz u32 emul_con; 951285d994aSBartlomiej Zolnierkiewicz 952285d994aSBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS5260) 953285d994aSBartlomiej Zolnierkiewicz emul_con = EXYNOS5260_EMUL_CON; 954b28fec13SSudip Mukherjee else if (data->soc == SOC_ARCH_EXYNOS5433) 955488c7455SChanwoo Choi emul_con = EXYNOS5433_TMU_EMUL_CON; 9566c247393SAbhilash Kesavan else if (data->soc == SOC_ARCH_EXYNOS7) 9576c247393SAbhilash Kesavan emul_con = EXYNOS7_TMU_REG_EMUL_CON; 958285d994aSBartlomiej Zolnierkiewicz else 959285d994aSBartlomiej Zolnierkiewicz emul_con = EXYNOS_EMUL_CON; 960285d994aSBartlomiej Zolnierkiewicz 961285d994aSBartlomiej Zolnierkiewicz val = readl(data->base + emul_con); 962285d994aSBartlomiej Zolnierkiewicz val = get_emul_con_reg(data, val, temp); 963285d994aSBartlomiej Zolnierkiewicz writel(val, data->base + emul_con); 964285d994aSBartlomiej Zolnierkiewicz } 965285d994aSBartlomiej Zolnierkiewicz 966285d994aSBartlomiej Zolnierkiewicz static void exynos5440_tmu_set_emulation(struct exynos_tmu_data *data, 96717e8351aSSascha Hauer int temp) 968285d994aSBartlomiej Zolnierkiewicz { 969285d994aSBartlomiej Zolnierkiewicz unsigned int val; 970285d994aSBartlomiej Zolnierkiewicz 971285d994aSBartlomiej Zolnierkiewicz val = readl(data->base + EXYNOS5440_TMU_S0_7_DEBUG); 972285d994aSBartlomiej Zolnierkiewicz val = get_emul_con_reg(data, val, temp); 973285d994aSBartlomiej Zolnierkiewicz writel(val, data->base + EXYNOS5440_TMU_S0_7_DEBUG); 974285d994aSBartlomiej Zolnierkiewicz } 975285d994aSBartlomiej Zolnierkiewicz 97617e8351aSSascha Hauer static int exynos_tmu_set_emulation(void *drv_data, int temp) 97759dfa54cSAmit Daniel Kachhap { 97859dfa54cSAmit Daniel Kachhap struct exynos_tmu_data *data = drv_data; 97959dfa54cSAmit Daniel Kachhap int ret = -EINVAL; 98059dfa54cSAmit Daniel Kachhap 981ef3f80fcSBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS4210) 98259dfa54cSAmit Daniel Kachhap goto out; 98359dfa54cSAmit Daniel Kachhap 98459dfa54cSAmit Daniel Kachhap if (temp && temp < MCELSIUS) 98559dfa54cSAmit Daniel Kachhap goto out; 98659dfa54cSAmit Daniel Kachhap 98759dfa54cSAmit Daniel Kachhap mutex_lock(&data->lock); 98859dfa54cSAmit Daniel Kachhap clk_enable(data->clk); 989285d994aSBartlomiej Zolnierkiewicz data->tmu_set_emulation(data, temp); 99059dfa54cSAmit Daniel Kachhap clk_disable(data->clk); 99159dfa54cSAmit Daniel Kachhap mutex_unlock(&data->lock); 99259dfa54cSAmit Daniel Kachhap return 0; 99359dfa54cSAmit Daniel Kachhap out: 99459dfa54cSAmit Daniel Kachhap return ret; 99559dfa54cSAmit Daniel Kachhap } 99659dfa54cSAmit Daniel Kachhap #else 997285d994aSBartlomiej Zolnierkiewicz #define exynos4412_tmu_set_emulation NULL 998285d994aSBartlomiej Zolnierkiewicz #define exynos5440_tmu_set_emulation NULL 99917e8351aSSascha Hauer static int exynos_tmu_set_emulation(void *drv_data, int temp) 100059dfa54cSAmit Daniel Kachhap { return -EINVAL; } 100159dfa54cSAmit Daniel Kachhap #endif /* CONFIG_THERMAL_EMULATION */ 100259dfa54cSAmit Daniel Kachhap 1003b79985caSBartlomiej Zolnierkiewicz static int exynos4210_tmu_read(struct exynos_tmu_data *data) 1004b79985caSBartlomiej Zolnierkiewicz { 1005b79985caSBartlomiej Zolnierkiewicz int ret = readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP); 1006b79985caSBartlomiej Zolnierkiewicz 1007b79985caSBartlomiej Zolnierkiewicz /* "temp_code" should range between 75 and 175 */ 1008b79985caSBartlomiej Zolnierkiewicz return (ret < 75 || ret > 175) ? -ENODATA : ret; 1009b79985caSBartlomiej Zolnierkiewicz } 1010b79985caSBartlomiej Zolnierkiewicz 1011b79985caSBartlomiej Zolnierkiewicz static int exynos4412_tmu_read(struct exynos_tmu_data *data) 1012b79985caSBartlomiej Zolnierkiewicz { 1013b79985caSBartlomiej Zolnierkiewicz return readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP); 1014b79985caSBartlomiej Zolnierkiewicz } 1015b79985caSBartlomiej Zolnierkiewicz 1016b79985caSBartlomiej Zolnierkiewicz static int exynos5440_tmu_read(struct exynos_tmu_data *data) 1017b79985caSBartlomiej Zolnierkiewicz { 1018b79985caSBartlomiej Zolnierkiewicz return readb(data->base + EXYNOS5440_TMU_S0_7_TEMP); 1019b79985caSBartlomiej Zolnierkiewicz } 1020b79985caSBartlomiej Zolnierkiewicz 10216c247393SAbhilash Kesavan static int exynos7_tmu_read(struct exynos_tmu_data *data) 10226c247393SAbhilash Kesavan { 10236c247393SAbhilash Kesavan return readw(data->base + EXYNOS_TMU_REG_CURRENT_TEMP) & 10246c247393SAbhilash Kesavan EXYNOS7_TMU_TEMP_MASK; 10256c247393SAbhilash Kesavan } 10266c247393SAbhilash Kesavan 102759dfa54cSAmit Daniel Kachhap static void exynos_tmu_work(struct work_struct *work) 102859dfa54cSAmit Daniel Kachhap { 102959dfa54cSAmit Daniel Kachhap struct exynos_tmu_data *data = container_of(work, 103059dfa54cSAmit Daniel Kachhap struct exynos_tmu_data, irq_work); 1031b835ced1SBartlomiej Zolnierkiewicz unsigned int val_type; 1032a0395eeeSAmit Daniel Kachhap 103314a11dc7SNaveen Krishna Chatradhi if (!IS_ERR(data->clk_sec)) 103414a11dc7SNaveen Krishna Chatradhi clk_enable(data->clk_sec); 1035a0395eeeSAmit Daniel Kachhap /* Find which sensor generated this interrupt */ 1036421d5d12SBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS5440) { 1037421d5d12SBartlomiej Zolnierkiewicz val_type = readl(data->base_second + EXYNOS5440_TMU_IRQ_STATUS); 1038a0395eeeSAmit Daniel Kachhap if (!((val_type >> data->id) & 0x1)) 1039a0395eeeSAmit Daniel Kachhap goto out; 1040a0395eeeSAmit Daniel Kachhap } 104114a11dc7SNaveen Krishna Chatradhi if (!IS_ERR(data->clk_sec)) 104214a11dc7SNaveen Krishna Chatradhi clk_disable(data->clk_sec); 104359dfa54cSAmit Daniel Kachhap 10443b6a1a80SLukasz Majewski exynos_report_trigger(data); 104559dfa54cSAmit Daniel Kachhap mutex_lock(&data->lock); 104659dfa54cSAmit Daniel Kachhap clk_enable(data->clk); 1047b8d582b9SAmit Daniel Kachhap 1048a4463c4fSAmit Daniel Kachhap /* TODO: take action based on particular interrupt */ 1049a7331f72SBartlomiej Zolnierkiewicz data->tmu_clear_irqs(data); 1050b8d582b9SAmit Daniel Kachhap 105159dfa54cSAmit Daniel Kachhap clk_disable(data->clk); 105259dfa54cSAmit Daniel Kachhap mutex_unlock(&data->lock); 1053a0395eeeSAmit Daniel Kachhap out: 105459dfa54cSAmit Daniel Kachhap enable_irq(data->irq); 105559dfa54cSAmit Daniel Kachhap } 105659dfa54cSAmit Daniel Kachhap 1057a7331f72SBartlomiej Zolnierkiewicz static void exynos4210_tmu_clear_irqs(struct exynos_tmu_data *data) 1058a7331f72SBartlomiej Zolnierkiewicz { 1059a7331f72SBartlomiej Zolnierkiewicz unsigned int val_irq; 1060a7331f72SBartlomiej Zolnierkiewicz u32 tmu_intstat, tmu_intclear; 1061a7331f72SBartlomiej Zolnierkiewicz 1062a7331f72SBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS5260) { 1063a7331f72SBartlomiej Zolnierkiewicz tmu_intstat = EXYNOS5260_TMU_REG_INTSTAT; 1064a7331f72SBartlomiej Zolnierkiewicz tmu_intclear = EXYNOS5260_TMU_REG_INTCLEAR; 10656c247393SAbhilash Kesavan } else if (data->soc == SOC_ARCH_EXYNOS7) { 10666c247393SAbhilash Kesavan tmu_intstat = EXYNOS7_TMU_REG_INTPEND; 10676c247393SAbhilash Kesavan tmu_intclear = EXYNOS7_TMU_REG_INTPEND; 1068488c7455SChanwoo Choi } else if (data->soc == SOC_ARCH_EXYNOS5433) { 1069488c7455SChanwoo Choi tmu_intstat = EXYNOS5433_TMU_REG_INTPEND; 1070488c7455SChanwoo Choi tmu_intclear = EXYNOS5433_TMU_REG_INTPEND; 1071a7331f72SBartlomiej Zolnierkiewicz } else { 1072a7331f72SBartlomiej Zolnierkiewicz tmu_intstat = EXYNOS_TMU_REG_INTSTAT; 1073a7331f72SBartlomiej Zolnierkiewicz tmu_intclear = EXYNOS_TMU_REG_INTCLEAR; 1074a7331f72SBartlomiej Zolnierkiewicz } 1075a7331f72SBartlomiej Zolnierkiewicz 1076a7331f72SBartlomiej Zolnierkiewicz val_irq = readl(data->base + tmu_intstat); 1077a7331f72SBartlomiej Zolnierkiewicz /* 1078a7331f72SBartlomiej Zolnierkiewicz * Clear the interrupts. Please note that the documentation for 1079a7331f72SBartlomiej Zolnierkiewicz * Exynos3250, Exynos4412, Exynos5250 and Exynos5260 incorrectly 1080a7331f72SBartlomiej Zolnierkiewicz * states that INTCLEAR register has a different placing of bits 1081a7331f72SBartlomiej Zolnierkiewicz * responsible for FALL IRQs than INTSTAT register. Exynos5420 1082a7331f72SBartlomiej Zolnierkiewicz * and Exynos5440 documentation is correct (Exynos4210 doesn't 1083a7331f72SBartlomiej Zolnierkiewicz * support FALL IRQs at all). 1084a7331f72SBartlomiej Zolnierkiewicz */ 1085a7331f72SBartlomiej Zolnierkiewicz writel(val_irq, data->base + tmu_intclear); 1086a7331f72SBartlomiej Zolnierkiewicz } 1087a7331f72SBartlomiej Zolnierkiewicz 1088a7331f72SBartlomiej Zolnierkiewicz static void exynos5440_tmu_clear_irqs(struct exynos_tmu_data *data) 1089a7331f72SBartlomiej Zolnierkiewicz { 1090a7331f72SBartlomiej Zolnierkiewicz unsigned int val_irq; 1091a7331f72SBartlomiej Zolnierkiewicz 1092a7331f72SBartlomiej Zolnierkiewicz val_irq = readl(data->base + EXYNOS5440_TMU_S0_7_IRQ); 1093a7331f72SBartlomiej Zolnierkiewicz /* clear the interrupts */ 1094a7331f72SBartlomiej Zolnierkiewicz writel(val_irq, data->base + EXYNOS5440_TMU_S0_7_IRQ); 1095a7331f72SBartlomiej Zolnierkiewicz } 1096a7331f72SBartlomiej Zolnierkiewicz 109759dfa54cSAmit Daniel Kachhap static irqreturn_t exynos_tmu_irq(int irq, void *id) 109859dfa54cSAmit Daniel Kachhap { 109959dfa54cSAmit Daniel Kachhap struct exynos_tmu_data *data = id; 110059dfa54cSAmit Daniel Kachhap 110159dfa54cSAmit Daniel Kachhap disable_irq_nosync(irq); 110259dfa54cSAmit Daniel Kachhap schedule_work(&data->irq_work); 110359dfa54cSAmit Daniel Kachhap 110459dfa54cSAmit Daniel Kachhap return IRQ_HANDLED; 110559dfa54cSAmit Daniel Kachhap } 110659dfa54cSAmit Daniel Kachhap 110759dfa54cSAmit Daniel Kachhap static const struct of_device_id exynos_tmu_match[] = { 1108b71d399cSChanwoo Choi { .compatible = "samsung,exynos3250-tmu", }, 1109b71d399cSChanwoo Choi { .compatible = "samsung,exynos4210-tmu", }, 1110b71d399cSChanwoo Choi { .compatible = "samsung,exynos4412-tmu", }, 1111b71d399cSChanwoo Choi { .compatible = "samsung,exynos5250-tmu", }, 1112b71d399cSChanwoo Choi { .compatible = "samsung,exynos5260-tmu", }, 1113b71d399cSChanwoo Choi { .compatible = "samsung,exynos5420-tmu", }, 1114b71d399cSChanwoo Choi { .compatible = "samsung,exynos5420-tmu-ext-triminfo", }, 1115488c7455SChanwoo Choi { .compatible = "samsung,exynos5433-tmu", }, 1116b71d399cSChanwoo Choi { .compatible = "samsung,exynos5440-tmu", }, 1117b71d399cSChanwoo Choi { .compatible = "samsung,exynos7-tmu", }, 1118b71d399cSChanwoo Choi { /* sentinel */ }, 111959dfa54cSAmit Daniel Kachhap }; 112059dfa54cSAmit Daniel Kachhap MODULE_DEVICE_TABLE(of, exynos_tmu_match); 112159dfa54cSAmit Daniel Kachhap 11223b6a1a80SLukasz Majewski static int exynos_of_get_soc_type(struct device_node *np) 112359dfa54cSAmit Daniel Kachhap { 11243b6a1a80SLukasz Majewski if (of_device_is_compatible(np, "samsung,exynos3250-tmu")) 11253b6a1a80SLukasz Majewski return SOC_ARCH_EXYNOS3250; 11263b6a1a80SLukasz Majewski else if (of_device_is_compatible(np, "samsung,exynos4210-tmu")) 11273b6a1a80SLukasz Majewski return SOC_ARCH_EXYNOS4210; 11283b6a1a80SLukasz Majewski else if (of_device_is_compatible(np, "samsung,exynos4412-tmu")) 11293b6a1a80SLukasz Majewski return SOC_ARCH_EXYNOS4412; 11303b6a1a80SLukasz Majewski else if (of_device_is_compatible(np, "samsung,exynos5250-tmu")) 11313b6a1a80SLukasz Majewski return SOC_ARCH_EXYNOS5250; 11323b6a1a80SLukasz Majewski else if (of_device_is_compatible(np, "samsung,exynos5260-tmu")) 11333b6a1a80SLukasz Majewski return SOC_ARCH_EXYNOS5260; 11343b6a1a80SLukasz Majewski else if (of_device_is_compatible(np, "samsung,exynos5420-tmu")) 11353b6a1a80SLukasz Majewski return SOC_ARCH_EXYNOS5420; 11363b6a1a80SLukasz Majewski else if (of_device_is_compatible(np, 11373b6a1a80SLukasz Majewski "samsung,exynos5420-tmu-ext-triminfo")) 11383b6a1a80SLukasz Majewski return SOC_ARCH_EXYNOS5420_TRIMINFO; 1139488c7455SChanwoo Choi else if (of_device_is_compatible(np, "samsung,exynos5433-tmu")) 1140488c7455SChanwoo Choi return SOC_ARCH_EXYNOS5433; 11413b6a1a80SLukasz Majewski else if (of_device_is_compatible(np, "samsung,exynos5440-tmu")) 11423b6a1a80SLukasz Majewski return SOC_ARCH_EXYNOS5440; 11436c247393SAbhilash Kesavan else if (of_device_is_compatible(np, "samsung,exynos7-tmu")) 11446c247393SAbhilash Kesavan return SOC_ARCH_EXYNOS7; 114573b5b1d7SSachin Kamat 11463b6a1a80SLukasz Majewski return -EINVAL; 11473b6a1a80SLukasz Majewski } 11483b6a1a80SLukasz Majewski 11493b6a1a80SLukasz Majewski static int exynos_of_sensor_conf(struct device_node *np, 11503b6a1a80SLukasz Majewski struct exynos_tmu_platform_data *pdata) 11513b6a1a80SLukasz Majewski { 11523b6a1a80SLukasz Majewski u32 value; 11533b6a1a80SLukasz Majewski int ret; 11543b6a1a80SLukasz Majewski 11553b6a1a80SLukasz Majewski of_node_get(np); 11563b6a1a80SLukasz Majewski 11573b6a1a80SLukasz Majewski ret = of_property_read_u32(np, "samsung,tmu_gain", &value); 11583b6a1a80SLukasz Majewski pdata->gain = (u8)value; 11593b6a1a80SLukasz Majewski of_property_read_u32(np, "samsung,tmu_reference_voltage", &value); 11603b6a1a80SLukasz Majewski pdata->reference_voltage = (u8)value; 11613b6a1a80SLukasz Majewski of_property_read_u32(np, "samsung,tmu_noise_cancel_mode", &value); 11623b6a1a80SLukasz Majewski pdata->noise_cancel_mode = (u8)value; 11633b6a1a80SLukasz Majewski 11643b6a1a80SLukasz Majewski of_property_read_u32(np, "samsung,tmu_efuse_value", 11653b6a1a80SLukasz Majewski &pdata->efuse_value); 11663b6a1a80SLukasz Majewski of_property_read_u32(np, "samsung,tmu_min_efuse_value", 11673b6a1a80SLukasz Majewski &pdata->min_efuse_value); 11683b6a1a80SLukasz Majewski of_property_read_u32(np, "samsung,tmu_max_efuse_value", 11693b6a1a80SLukasz Majewski &pdata->max_efuse_value); 11703b6a1a80SLukasz Majewski 11713b6a1a80SLukasz Majewski of_property_read_u32(np, "samsung,tmu_first_point_trim", &value); 11723b6a1a80SLukasz Majewski pdata->first_point_trim = (u8)value; 11733b6a1a80SLukasz Majewski of_property_read_u32(np, "samsung,tmu_second_point_trim", &value); 11743b6a1a80SLukasz Majewski pdata->second_point_trim = (u8)value; 11753b6a1a80SLukasz Majewski of_property_read_u32(np, "samsung,tmu_default_temp_offset", &value); 11763b6a1a80SLukasz Majewski pdata->default_temp_offset = (u8)value; 11773b6a1a80SLukasz Majewski 11783b6a1a80SLukasz Majewski of_property_read_u32(np, "samsung,tmu_cal_type", &pdata->cal_type); 11793b6a1a80SLukasz Majewski 11803b6a1a80SLukasz Majewski of_node_put(np); 11813b6a1a80SLukasz Majewski return 0; 118259dfa54cSAmit Daniel Kachhap } 118359dfa54cSAmit Daniel Kachhap 1184cebe7373SAmit Daniel Kachhap static int exynos_map_dt_data(struct platform_device *pdev) 118559dfa54cSAmit Daniel Kachhap { 1186cebe7373SAmit Daniel Kachhap struct exynos_tmu_data *data = platform_get_drvdata(pdev); 1187cebe7373SAmit Daniel Kachhap struct exynos_tmu_platform_data *pdata; 1188cebe7373SAmit Daniel Kachhap struct resource res; 118959dfa54cSAmit Daniel Kachhap 119073b5b1d7SSachin Kamat if (!data || !pdev->dev.of_node) 1191cebe7373SAmit Daniel Kachhap return -ENODEV; 119259dfa54cSAmit Daniel Kachhap 1193cebe7373SAmit Daniel Kachhap data->id = of_alias_get_id(pdev->dev.of_node, "tmuctrl"); 1194cebe7373SAmit Daniel Kachhap if (data->id < 0) 1195cebe7373SAmit Daniel Kachhap data->id = 0; 1196cebe7373SAmit Daniel Kachhap 1197cebe7373SAmit Daniel Kachhap data->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); 1198cebe7373SAmit Daniel Kachhap if (data->irq <= 0) { 1199cebe7373SAmit Daniel Kachhap dev_err(&pdev->dev, "failed to get IRQ\n"); 1200cebe7373SAmit Daniel Kachhap return -ENODEV; 1201cebe7373SAmit Daniel Kachhap } 1202cebe7373SAmit Daniel Kachhap 1203cebe7373SAmit Daniel Kachhap if (of_address_to_resource(pdev->dev.of_node, 0, &res)) { 1204cebe7373SAmit Daniel Kachhap dev_err(&pdev->dev, "failed to get Resource 0\n"); 1205cebe7373SAmit Daniel Kachhap return -ENODEV; 1206cebe7373SAmit Daniel Kachhap } 1207cebe7373SAmit Daniel Kachhap 1208cebe7373SAmit Daniel Kachhap data->base = devm_ioremap(&pdev->dev, res.start, resource_size(&res)); 1209cebe7373SAmit Daniel Kachhap if (!data->base) { 1210cebe7373SAmit Daniel Kachhap dev_err(&pdev->dev, "Failed to ioremap memory\n"); 1211cebe7373SAmit Daniel Kachhap return -EADDRNOTAVAIL; 1212cebe7373SAmit Daniel Kachhap } 1213cebe7373SAmit Daniel Kachhap 12143b6a1a80SLukasz Majewski pdata = devm_kzalloc(&pdev->dev, 12153b6a1a80SLukasz Majewski sizeof(struct exynos_tmu_platform_data), 12163b6a1a80SLukasz Majewski GFP_KERNEL); 12173b6a1a80SLukasz Majewski if (!pdata) 12183b6a1a80SLukasz Majewski return -ENOMEM; 121956adb9efSBartlomiej Zolnierkiewicz 12203b6a1a80SLukasz Majewski exynos_of_sensor_conf(pdev->dev.of_node, pdata); 1221cebe7373SAmit Daniel Kachhap data->pdata = pdata; 12223b6a1a80SLukasz Majewski data->soc = exynos_of_get_soc_type(pdev->dev.of_node); 122356adb9efSBartlomiej Zolnierkiewicz 122456adb9efSBartlomiej Zolnierkiewicz switch (data->soc) { 122556adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS4210: 122656adb9efSBartlomiej Zolnierkiewicz data->tmu_initialize = exynos4210_tmu_initialize; 122756adb9efSBartlomiej Zolnierkiewicz data->tmu_control = exynos4210_tmu_control; 122856adb9efSBartlomiej Zolnierkiewicz data->tmu_read = exynos4210_tmu_read; 122956adb9efSBartlomiej Zolnierkiewicz data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; 12303a3a5f15SKrzysztof Kozlowski data->ntrip = 4; 123156adb9efSBartlomiej Zolnierkiewicz break; 123256adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS3250: 123356adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS4412: 123456adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS5250: 123556adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS5260: 123656adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS5420: 123756adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS5420_TRIMINFO: 123856adb9efSBartlomiej Zolnierkiewicz data->tmu_initialize = exynos4412_tmu_initialize; 123956adb9efSBartlomiej Zolnierkiewicz data->tmu_control = exynos4210_tmu_control; 124056adb9efSBartlomiej Zolnierkiewicz data->tmu_read = exynos4412_tmu_read; 124156adb9efSBartlomiej Zolnierkiewicz data->tmu_set_emulation = exynos4412_tmu_set_emulation; 124256adb9efSBartlomiej Zolnierkiewicz data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; 12433a3a5f15SKrzysztof Kozlowski data->ntrip = 4; 124456adb9efSBartlomiej Zolnierkiewicz break; 1245488c7455SChanwoo Choi case SOC_ARCH_EXYNOS5433: 1246488c7455SChanwoo Choi data->tmu_initialize = exynos5433_tmu_initialize; 1247488c7455SChanwoo Choi data->tmu_control = exynos5433_tmu_control; 1248488c7455SChanwoo Choi data->tmu_read = exynos4412_tmu_read; 1249488c7455SChanwoo Choi data->tmu_set_emulation = exynos4412_tmu_set_emulation; 1250488c7455SChanwoo Choi data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; 12513a3a5f15SKrzysztof Kozlowski data->ntrip = 8; 1252488c7455SChanwoo Choi break; 125356adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS5440: 125456adb9efSBartlomiej Zolnierkiewicz data->tmu_initialize = exynos5440_tmu_initialize; 125556adb9efSBartlomiej Zolnierkiewicz data->tmu_control = exynos5440_tmu_control; 125656adb9efSBartlomiej Zolnierkiewicz data->tmu_read = exynos5440_tmu_read; 125756adb9efSBartlomiej Zolnierkiewicz data->tmu_set_emulation = exynos5440_tmu_set_emulation; 125856adb9efSBartlomiej Zolnierkiewicz data->tmu_clear_irqs = exynos5440_tmu_clear_irqs; 12593a3a5f15SKrzysztof Kozlowski data->ntrip = 4; 126056adb9efSBartlomiej Zolnierkiewicz break; 12616c247393SAbhilash Kesavan case SOC_ARCH_EXYNOS7: 12626c247393SAbhilash Kesavan data->tmu_initialize = exynos7_tmu_initialize; 12636c247393SAbhilash Kesavan data->tmu_control = exynos7_tmu_control; 12646c247393SAbhilash Kesavan data->tmu_read = exynos7_tmu_read; 12656c247393SAbhilash Kesavan data->tmu_set_emulation = exynos4412_tmu_set_emulation; 12666c247393SAbhilash Kesavan data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; 12673a3a5f15SKrzysztof Kozlowski data->ntrip = 8; 12686c247393SAbhilash Kesavan break; 126956adb9efSBartlomiej Zolnierkiewicz default: 127056adb9efSBartlomiej Zolnierkiewicz dev_err(&pdev->dev, "Platform not supported\n"); 127156adb9efSBartlomiej Zolnierkiewicz return -EINVAL; 127256adb9efSBartlomiej Zolnierkiewicz } 127356adb9efSBartlomiej Zolnierkiewicz 1274d9b6ee14SAmit Daniel Kachhap /* 1275d9b6ee14SAmit Daniel Kachhap * Check if the TMU shares some registers and then try to map the 1276d9b6ee14SAmit Daniel Kachhap * memory of common registers. 1277d9b6ee14SAmit Daniel Kachhap */ 127856adb9efSBartlomiej Zolnierkiewicz if (data->soc != SOC_ARCH_EXYNOS5420_TRIMINFO && 127956adb9efSBartlomiej Zolnierkiewicz data->soc != SOC_ARCH_EXYNOS5440) 1280d9b6ee14SAmit Daniel Kachhap return 0; 1281d9b6ee14SAmit Daniel Kachhap 1282d9b6ee14SAmit Daniel Kachhap if (of_address_to_resource(pdev->dev.of_node, 1, &res)) { 1283d9b6ee14SAmit Daniel Kachhap dev_err(&pdev->dev, "failed to get Resource 1\n"); 1284d9b6ee14SAmit Daniel Kachhap return -ENODEV; 1285d9b6ee14SAmit Daniel Kachhap } 1286d9b6ee14SAmit Daniel Kachhap 12879025d563SNaveen Krishna Chatradhi data->base_second = devm_ioremap(&pdev->dev, res.start, 1288d9b6ee14SAmit Daniel Kachhap resource_size(&res)); 12899025d563SNaveen Krishna Chatradhi if (!data->base_second) { 1290d9b6ee14SAmit Daniel Kachhap dev_err(&pdev->dev, "Failed to ioremap memory\n"); 1291d9b6ee14SAmit Daniel Kachhap return -ENOMEM; 1292d9b6ee14SAmit Daniel Kachhap } 1293cebe7373SAmit Daniel Kachhap 1294cebe7373SAmit Daniel Kachhap return 0; 1295cebe7373SAmit Daniel Kachhap } 1296cebe7373SAmit Daniel Kachhap 1297c3c04d9dSJulia Lawall static const struct thermal_zone_of_device_ops exynos_sensor_ops = { 12983b6a1a80SLukasz Majewski .get_temp = exynos_get_temp, 12993b6a1a80SLukasz Majewski .set_emul_temp = exynos_tmu_set_emulation, 13003b6a1a80SLukasz Majewski }; 13013b6a1a80SLukasz Majewski 1302cebe7373SAmit Daniel Kachhap static int exynos_tmu_probe(struct platform_device *pdev) 1303cebe7373SAmit Daniel Kachhap { 13043b6a1a80SLukasz Majewski struct exynos_tmu_data *data; 13053b6a1a80SLukasz Majewski int ret; 1306cebe7373SAmit Daniel Kachhap 130759dfa54cSAmit Daniel Kachhap data = devm_kzalloc(&pdev->dev, sizeof(struct exynos_tmu_data), 130859dfa54cSAmit Daniel Kachhap GFP_KERNEL); 13092a9675b3SJingoo Han if (!data) 131059dfa54cSAmit Daniel Kachhap return -ENOMEM; 131159dfa54cSAmit Daniel Kachhap 1312cebe7373SAmit Daniel Kachhap platform_set_drvdata(pdev, data); 1313cebe7373SAmit Daniel Kachhap mutex_init(&data->lock); 1314cebe7373SAmit Daniel Kachhap 1315824ead03SKrzysztof Kozlowski /* 1316824ead03SKrzysztof Kozlowski * Try enabling the regulator if found 1317824ead03SKrzysztof Kozlowski * TODO: Add regulator as an SOC feature, so that regulator enable 1318824ead03SKrzysztof Kozlowski * is a compulsory call. 1319824ead03SKrzysztof Kozlowski */ 13204d3583cdSJavier Martinez Canillas data->regulator = devm_regulator_get_optional(&pdev->dev, "vtmu"); 1321824ead03SKrzysztof Kozlowski if (!IS_ERR(data->regulator)) { 1322824ead03SKrzysztof Kozlowski ret = regulator_enable(data->regulator); 1323824ead03SKrzysztof Kozlowski if (ret) { 1324824ead03SKrzysztof Kozlowski dev_err(&pdev->dev, "failed to enable vtmu\n"); 1325824ead03SKrzysztof Kozlowski return ret; 13263b6a1a80SLukasz Majewski } 1327824ead03SKrzysztof Kozlowski } else { 1328ccb361d2SJavier Martinez Canillas if (PTR_ERR(data->regulator) == -EPROBE_DEFER) 1329ccb361d2SJavier Martinez Canillas return -EPROBE_DEFER; 1330824ead03SKrzysztof Kozlowski dev_info(&pdev->dev, "Regulator node (vtmu) not found\n"); 1331824ead03SKrzysztof Kozlowski } 1332824ead03SKrzysztof Kozlowski 1333cebe7373SAmit Daniel Kachhap ret = exynos_map_dt_data(pdev); 1334cebe7373SAmit Daniel Kachhap if (ret) 13353b6a1a80SLukasz Majewski goto err_sensor; 1336cebe7373SAmit Daniel Kachhap 133759dfa54cSAmit Daniel Kachhap INIT_WORK(&data->irq_work, exynos_tmu_work); 133859dfa54cSAmit Daniel Kachhap 133959dfa54cSAmit Daniel Kachhap data->clk = devm_clk_get(&pdev->dev, "tmu_apbif"); 134059dfa54cSAmit Daniel Kachhap if (IS_ERR(data->clk)) { 134159dfa54cSAmit Daniel Kachhap dev_err(&pdev->dev, "Failed to get clock\n"); 13423b6a1a80SLukasz Majewski ret = PTR_ERR(data->clk); 13433b6a1a80SLukasz Majewski goto err_sensor; 134459dfa54cSAmit Daniel Kachhap } 134559dfa54cSAmit Daniel Kachhap 134614a11dc7SNaveen Krishna Chatradhi data->clk_sec = devm_clk_get(&pdev->dev, "tmu_triminfo_apbif"); 134714a11dc7SNaveen Krishna Chatradhi if (IS_ERR(data->clk_sec)) { 134814a11dc7SNaveen Krishna Chatradhi if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) { 134914a11dc7SNaveen Krishna Chatradhi dev_err(&pdev->dev, "Failed to get triminfo clock\n"); 13503b6a1a80SLukasz Majewski ret = PTR_ERR(data->clk_sec); 13513b6a1a80SLukasz Majewski goto err_sensor; 135214a11dc7SNaveen Krishna Chatradhi } 135314a11dc7SNaveen Krishna Chatradhi } else { 135414a11dc7SNaveen Krishna Chatradhi ret = clk_prepare(data->clk_sec); 135514a11dc7SNaveen Krishna Chatradhi if (ret) { 135614a11dc7SNaveen Krishna Chatradhi dev_err(&pdev->dev, "Failed to get clock\n"); 13573b6a1a80SLukasz Majewski goto err_sensor; 135814a11dc7SNaveen Krishna Chatradhi } 135914a11dc7SNaveen Krishna Chatradhi } 136014a11dc7SNaveen Krishna Chatradhi 136114a11dc7SNaveen Krishna Chatradhi ret = clk_prepare(data->clk); 136214a11dc7SNaveen Krishna Chatradhi if (ret) { 136314a11dc7SNaveen Krishna Chatradhi dev_err(&pdev->dev, "Failed to get clock\n"); 136414a11dc7SNaveen Krishna Chatradhi goto err_clk_sec; 136514a11dc7SNaveen Krishna Chatradhi } 136659dfa54cSAmit Daniel Kachhap 1367488c7455SChanwoo Choi switch (data->soc) { 1368488c7455SChanwoo Choi case SOC_ARCH_EXYNOS5433: 1369488c7455SChanwoo Choi case SOC_ARCH_EXYNOS7: 13706c247393SAbhilash Kesavan data->sclk = devm_clk_get(&pdev->dev, "tmu_sclk"); 13716c247393SAbhilash Kesavan if (IS_ERR(data->sclk)) { 13726c247393SAbhilash Kesavan dev_err(&pdev->dev, "Failed to get sclk\n"); 13736c247393SAbhilash Kesavan goto err_clk; 13746c247393SAbhilash Kesavan } else { 13756c247393SAbhilash Kesavan ret = clk_prepare_enable(data->sclk); 13766c247393SAbhilash Kesavan if (ret) { 13776c247393SAbhilash Kesavan dev_err(&pdev->dev, "Failed to enable sclk\n"); 13786c247393SAbhilash Kesavan goto err_clk; 13796c247393SAbhilash Kesavan } 13806c247393SAbhilash Kesavan } 1381488c7455SChanwoo Choi break; 1382488c7455SChanwoo Choi default: 1383488c7455SChanwoo Choi break; 1384baba1ebbSKrzysztof Kozlowski } 13856c247393SAbhilash Kesavan 13869e4249b4SKrzysztof Kozlowski /* 13879e4249b4SKrzysztof Kozlowski * data->tzd must be registered before calling exynos_tmu_initialize(), 13889e4249b4SKrzysztof Kozlowski * requesting irq and calling exynos_tmu_control(). 13899e4249b4SKrzysztof Kozlowski */ 13909e4249b4SKrzysztof Kozlowski data->tzd = thermal_zone_of_sensor_register(&pdev->dev, 0, data, 13919e4249b4SKrzysztof Kozlowski &exynos_sensor_ops); 13929e4249b4SKrzysztof Kozlowski if (IS_ERR(data->tzd)) { 13939e4249b4SKrzysztof Kozlowski ret = PTR_ERR(data->tzd); 13949e4249b4SKrzysztof Kozlowski dev_err(&pdev->dev, "Failed to register sensor: %d\n", ret); 13959e4249b4SKrzysztof Kozlowski goto err_sclk; 13969e4249b4SKrzysztof Kozlowski } 139759dfa54cSAmit Daniel Kachhap 139859dfa54cSAmit Daniel Kachhap ret = exynos_tmu_initialize(pdev); 139959dfa54cSAmit Daniel Kachhap if (ret) { 140059dfa54cSAmit Daniel Kachhap dev_err(&pdev->dev, "Failed to initialize TMU\n"); 14019e4249b4SKrzysztof Kozlowski goto err_thermal; 140259dfa54cSAmit Daniel Kachhap } 140359dfa54cSAmit Daniel Kachhap 1404cebe7373SAmit Daniel Kachhap ret = devm_request_irq(&pdev->dev, data->irq, exynos_tmu_irq, 1405cebe7373SAmit Daniel Kachhap IRQF_TRIGGER_RISING | IRQF_SHARED, dev_name(&pdev->dev), data); 1406cebe7373SAmit Daniel Kachhap if (ret) { 1407cebe7373SAmit Daniel Kachhap dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq); 14089e4249b4SKrzysztof Kozlowski goto err_thermal; 1409cebe7373SAmit Daniel Kachhap } 141059dfa54cSAmit Daniel Kachhap 14113b6a1a80SLukasz Majewski exynos_tmu_control(pdev, true); 141259dfa54cSAmit Daniel Kachhap return 0; 14139e4249b4SKrzysztof Kozlowski 14149e4249b4SKrzysztof Kozlowski err_thermal: 14159e4249b4SKrzysztof Kozlowski thermal_zone_of_sensor_unregister(&pdev->dev, data->tzd); 14166c247393SAbhilash Kesavan err_sclk: 14176c247393SAbhilash Kesavan clk_disable_unprepare(data->sclk); 141859dfa54cSAmit Daniel Kachhap err_clk: 141959dfa54cSAmit Daniel Kachhap clk_unprepare(data->clk); 142014a11dc7SNaveen Krishna Chatradhi err_clk_sec: 142114a11dc7SNaveen Krishna Chatradhi if (!IS_ERR(data->clk_sec)) 142214a11dc7SNaveen Krishna Chatradhi clk_unprepare(data->clk_sec); 14233b6a1a80SLukasz Majewski err_sensor: 1424bfa26838SKrzysztof Kozlowski if (!IS_ERR(data->regulator)) 14255f09a5cbSKrzysztof Kozlowski regulator_disable(data->regulator); 14263b6a1a80SLukasz Majewski 142759dfa54cSAmit Daniel Kachhap return ret; 142859dfa54cSAmit Daniel Kachhap } 142959dfa54cSAmit Daniel Kachhap 143059dfa54cSAmit Daniel Kachhap static int exynos_tmu_remove(struct platform_device *pdev) 143159dfa54cSAmit Daniel Kachhap { 143259dfa54cSAmit Daniel Kachhap struct exynos_tmu_data *data = platform_get_drvdata(pdev); 14333b6a1a80SLukasz Majewski struct thermal_zone_device *tzd = data->tzd; 143459dfa54cSAmit Daniel Kachhap 14353b6a1a80SLukasz Majewski thermal_zone_of_sensor_unregister(&pdev->dev, tzd); 14364215688eSBartlomiej Zolnierkiewicz exynos_tmu_control(pdev, false); 14374215688eSBartlomiej Zolnierkiewicz 14386c247393SAbhilash Kesavan clk_disable_unprepare(data->sclk); 143959dfa54cSAmit Daniel Kachhap clk_unprepare(data->clk); 144014a11dc7SNaveen Krishna Chatradhi if (!IS_ERR(data->clk_sec)) 144114a11dc7SNaveen Krishna Chatradhi clk_unprepare(data->clk_sec); 144259dfa54cSAmit Daniel Kachhap 1443498d22f6SAmit Daniel Kachhap if (!IS_ERR(data->regulator)) 1444498d22f6SAmit Daniel Kachhap regulator_disable(data->regulator); 1445498d22f6SAmit Daniel Kachhap 144659dfa54cSAmit Daniel Kachhap return 0; 144759dfa54cSAmit Daniel Kachhap } 144859dfa54cSAmit Daniel Kachhap 144959dfa54cSAmit Daniel Kachhap #ifdef CONFIG_PM_SLEEP 145059dfa54cSAmit Daniel Kachhap static int exynos_tmu_suspend(struct device *dev) 145159dfa54cSAmit Daniel Kachhap { 145259dfa54cSAmit Daniel Kachhap exynos_tmu_control(to_platform_device(dev), false); 145359dfa54cSAmit Daniel Kachhap 145459dfa54cSAmit Daniel Kachhap return 0; 145559dfa54cSAmit Daniel Kachhap } 145659dfa54cSAmit Daniel Kachhap 145759dfa54cSAmit Daniel Kachhap static int exynos_tmu_resume(struct device *dev) 145859dfa54cSAmit Daniel Kachhap { 145959dfa54cSAmit Daniel Kachhap struct platform_device *pdev = to_platform_device(dev); 146059dfa54cSAmit Daniel Kachhap 146159dfa54cSAmit Daniel Kachhap exynos_tmu_initialize(pdev); 146259dfa54cSAmit Daniel Kachhap exynos_tmu_control(pdev, true); 146359dfa54cSAmit Daniel Kachhap 146459dfa54cSAmit Daniel Kachhap return 0; 146559dfa54cSAmit Daniel Kachhap } 146659dfa54cSAmit Daniel Kachhap 146759dfa54cSAmit Daniel Kachhap static SIMPLE_DEV_PM_OPS(exynos_tmu_pm, 146859dfa54cSAmit Daniel Kachhap exynos_tmu_suspend, exynos_tmu_resume); 146959dfa54cSAmit Daniel Kachhap #define EXYNOS_TMU_PM (&exynos_tmu_pm) 147059dfa54cSAmit Daniel Kachhap #else 147159dfa54cSAmit Daniel Kachhap #define EXYNOS_TMU_PM NULL 147259dfa54cSAmit Daniel Kachhap #endif 147359dfa54cSAmit Daniel Kachhap 147459dfa54cSAmit Daniel Kachhap static struct platform_driver exynos_tmu_driver = { 147559dfa54cSAmit Daniel Kachhap .driver = { 147659dfa54cSAmit Daniel Kachhap .name = "exynos-tmu", 147759dfa54cSAmit Daniel Kachhap .pm = EXYNOS_TMU_PM, 147873b5b1d7SSachin Kamat .of_match_table = exynos_tmu_match, 147959dfa54cSAmit Daniel Kachhap }, 148059dfa54cSAmit Daniel Kachhap .probe = exynos_tmu_probe, 148159dfa54cSAmit Daniel Kachhap .remove = exynos_tmu_remove, 148259dfa54cSAmit Daniel Kachhap }; 148359dfa54cSAmit Daniel Kachhap 148459dfa54cSAmit Daniel Kachhap module_platform_driver(exynos_tmu_driver); 148559dfa54cSAmit Daniel Kachhap 148659dfa54cSAmit Daniel Kachhap MODULE_DESCRIPTION("EXYNOS TMU Driver"); 148759dfa54cSAmit Daniel Kachhap MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>"); 148859dfa54cSAmit Daniel Kachhap MODULE_LICENSE("GPL"); 148959dfa54cSAmit Daniel Kachhap MODULE_ALIAS("platform:exynos-tmu"); 1490