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 1002845f6ecSBartlomiej Zolnierkiewicz /*exynos5440 specific registers*/ 1012845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5440_TMU_S0_7_TRIM 0x000 1022845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5440_TMU_S0_7_CTRL 0x020 1032845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5440_TMU_S0_7_DEBUG 0x040 1042845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5440_TMU_S0_7_TEMP 0x0f0 1052845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5440_TMU_S0_7_TH0 0x110 1062845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5440_TMU_S0_7_TH1 0x130 1072845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5440_TMU_S0_7_TH2 0x150 1082845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5440_TMU_S0_7_IRQEN 0x210 1092845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5440_TMU_S0_7_IRQ 0x230 1102845f6ecSBartlomiej Zolnierkiewicz /* exynos5440 common registers */ 1112845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5440_TMU_IRQ_STATUS 0x000 1122845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5440_TMU_PMIN 0x004 1132845f6ecSBartlomiej Zolnierkiewicz 1142845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5440_TMU_INTEN_RISE0_SHIFT 0 1152845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5440_TMU_INTEN_RISE1_SHIFT 1 1162845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5440_TMU_INTEN_RISE2_SHIFT 2 1172845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5440_TMU_INTEN_RISE3_SHIFT 3 1182845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5440_TMU_INTEN_FALL0_SHIFT 4 1192845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5440_TMU_TH_RISE4_SHIFT 24 1202845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5440_EFUSE_SWAP_OFFSET 8 12159dfa54cSAmit Daniel Kachhap 1226c247393SAbhilash Kesavan /* Exynos7 specific registers */ 1236c247393SAbhilash Kesavan #define EXYNOS7_THD_TEMP_RISE7_6 0x50 1246c247393SAbhilash Kesavan #define EXYNOS7_THD_TEMP_FALL7_6 0x60 1256c247393SAbhilash Kesavan #define EXYNOS7_TMU_REG_INTEN 0x110 1266c247393SAbhilash Kesavan #define EXYNOS7_TMU_REG_INTPEND 0x118 1276c247393SAbhilash Kesavan #define EXYNOS7_TMU_REG_EMUL_CON 0x160 1286c247393SAbhilash Kesavan 1296c247393SAbhilash Kesavan #define EXYNOS7_TMU_TEMP_MASK 0x1ff 1306c247393SAbhilash Kesavan #define EXYNOS7_PD_DET_EN_SHIFT 23 1316c247393SAbhilash Kesavan #define EXYNOS7_TMU_INTEN_RISE0_SHIFT 0 1326c247393SAbhilash Kesavan #define EXYNOS7_TMU_INTEN_RISE1_SHIFT 1 1336c247393SAbhilash Kesavan #define EXYNOS7_TMU_INTEN_RISE2_SHIFT 2 1346c247393SAbhilash Kesavan #define EXYNOS7_TMU_INTEN_RISE3_SHIFT 3 1356c247393SAbhilash Kesavan #define EXYNOS7_TMU_INTEN_RISE4_SHIFT 4 1366c247393SAbhilash Kesavan #define EXYNOS7_TMU_INTEN_RISE5_SHIFT 5 1376c247393SAbhilash Kesavan #define EXYNOS7_TMU_INTEN_RISE6_SHIFT 6 1386c247393SAbhilash Kesavan #define EXYNOS7_TMU_INTEN_RISE7_SHIFT 7 1396c247393SAbhilash Kesavan #define EXYNOS7_EMUL_DATA_SHIFT 7 1406c247393SAbhilash Kesavan #define EXYNOS7_EMUL_DATA_MASK 0x1ff 1416c247393SAbhilash Kesavan 1423b6a1a80SLukasz Majewski #define MCELSIUS 1000 143cebe7373SAmit Daniel Kachhap /** 144cebe7373SAmit Daniel Kachhap * struct exynos_tmu_data : A structure to hold the private data of the TMU 145cebe7373SAmit Daniel Kachhap driver 146cebe7373SAmit Daniel Kachhap * @id: identifier of the one instance of the TMU controller. 147cebe7373SAmit Daniel Kachhap * @pdata: pointer to the tmu platform/configuration data 148cebe7373SAmit Daniel Kachhap * @base: base address of the single instance of the TMU controller. 1499025d563SNaveen Krishna Chatradhi * @base_second: base address of the common registers of the TMU controller. 150cebe7373SAmit Daniel Kachhap * @irq: irq number of the TMU controller. 151cebe7373SAmit Daniel Kachhap * @soc: id of the SOC type. 152cebe7373SAmit Daniel Kachhap * @irq_work: pointer to the irq work structure. 153cebe7373SAmit Daniel Kachhap * @lock: lock to implement synchronization. 154cebe7373SAmit Daniel Kachhap * @clk: pointer to the clock structure. 15514a11dc7SNaveen Krishna Chatradhi * @clk_sec: pointer to the clock structure for accessing the base_second. 1566c247393SAbhilash Kesavan * @sclk: pointer to the clock structure for accessing the tmu special clk. 157cebe7373SAmit Daniel Kachhap * @temp_error1: fused value of the first point trim. 158cebe7373SAmit Daniel Kachhap * @temp_error2: fused value of the second point trim. 159498d22f6SAmit Daniel Kachhap * @regulator: pointer to the TMU regulator structure. 160cebe7373SAmit Daniel Kachhap * @reg_conf: pointer to structure to register with core thermal. 16172d1100bSBartlomiej Zolnierkiewicz * @tmu_initialize: SoC specific TMU initialization method 16237f9034fSBartlomiej Zolnierkiewicz * @tmu_control: SoC specific TMU control method 163b79985caSBartlomiej Zolnierkiewicz * @tmu_read: SoC specific TMU temperature read method 164285d994aSBartlomiej Zolnierkiewicz * @tmu_set_emulation: SoC specific TMU emulation setting method 165a7331f72SBartlomiej Zolnierkiewicz * @tmu_clear_irqs: SoC specific TMU interrupts clearing method 166cebe7373SAmit Daniel Kachhap */ 16759dfa54cSAmit Daniel Kachhap struct exynos_tmu_data { 168cebe7373SAmit Daniel Kachhap int id; 16959dfa54cSAmit Daniel Kachhap struct exynos_tmu_platform_data *pdata; 17059dfa54cSAmit Daniel Kachhap void __iomem *base; 1719025d563SNaveen Krishna Chatradhi void __iomem *base_second; 17259dfa54cSAmit Daniel Kachhap int irq; 17359dfa54cSAmit Daniel Kachhap enum soc_type soc; 17459dfa54cSAmit Daniel Kachhap struct work_struct irq_work; 17559dfa54cSAmit Daniel Kachhap struct mutex lock; 1766c247393SAbhilash Kesavan struct clk *clk, *clk_sec, *sclk; 1776c247393SAbhilash Kesavan u16 temp_error1, temp_error2; 178498d22f6SAmit Daniel Kachhap struct regulator *regulator; 1793b6a1a80SLukasz Majewski struct thermal_zone_device *tzd; 1803b6a1a80SLukasz Majewski 18172d1100bSBartlomiej Zolnierkiewicz int (*tmu_initialize)(struct platform_device *pdev); 18237f9034fSBartlomiej Zolnierkiewicz void (*tmu_control)(struct platform_device *pdev, bool on); 183b79985caSBartlomiej Zolnierkiewicz int (*tmu_read)(struct exynos_tmu_data *data); 184285d994aSBartlomiej Zolnierkiewicz void (*tmu_set_emulation)(struct exynos_tmu_data *data, 185285d994aSBartlomiej Zolnierkiewicz unsigned long temp); 186a7331f72SBartlomiej Zolnierkiewicz void (*tmu_clear_irqs)(struct exynos_tmu_data *data); 18759dfa54cSAmit Daniel Kachhap }; 18859dfa54cSAmit Daniel Kachhap 1893b6a1a80SLukasz Majewski static void exynos_report_trigger(struct exynos_tmu_data *p) 1903b6a1a80SLukasz Majewski { 1913b6a1a80SLukasz Majewski char data[10], *envp[] = { data, NULL }; 1923b6a1a80SLukasz Majewski struct thermal_zone_device *tz = p->tzd; 1933b6a1a80SLukasz Majewski unsigned long temp; 1943b6a1a80SLukasz Majewski unsigned int i; 1953b6a1a80SLukasz Majewski 196eccb6014SLukasz Majewski if (!tz) { 197eccb6014SLukasz Majewski pr_err("No thermal zone device defined\n"); 1983b6a1a80SLukasz Majewski return; 1993b6a1a80SLukasz Majewski } 2003b6a1a80SLukasz Majewski 2013b6a1a80SLukasz Majewski thermal_zone_device_update(tz); 2023b6a1a80SLukasz Majewski 2033b6a1a80SLukasz Majewski mutex_lock(&tz->lock); 2043b6a1a80SLukasz Majewski /* Find the level for which trip happened */ 2053b6a1a80SLukasz Majewski for (i = 0; i < of_thermal_get_ntrips(tz); i++) { 2063b6a1a80SLukasz Majewski tz->ops->get_trip_temp(tz, i, &temp); 2073b6a1a80SLukasz Majewski if (tz->last_temperature < temp) 2083b6a1a80SLukasz Majewski break; 2093b6a1a80SLukasz Majewski } 2103b6a1a80SLukasz Majewski 2113b6a1a80SLukasz Majewski snprintf(data, sizeof(data), "%u", i); 2123b6a1a80SLukasz Majewski kobject_uevent_env(&tz->device.kobj, KOBJ_CHANGE, envp); 2133b6a1a80SLukasz Majewski mutex_unlock(&tz->lock); 2143b6a1a80SLukasz Majewski } 2153b6a1a80SLukasz Majewski 21659dfa54cSAmit Daniel Kachhap /* 21759dfa54cSAmit Daniel Kachhap * TMU treats temperature as a mapped temperature code. 21859dfa54cSAmit Daniel Kachhap * The temperature is converted differently depending on the calibration type. 21959dfa54cSAmit Daniel Kachhap */ 22059dfa54cSAmit Daniel Kachhap static int temp_to_code(struct exynos_tmu_data *data, u8 temp) 22159dfa54cSAmit Daniel Kachhap { 22259dfa54cSAmit Daniel Kachhap struct exynos_tmu_platform_data *pdata = data->pdata; 22359dfa54cSAmit Daniel Kachhap int temp_code; 22459dfa54cSAmit Daniel Kachhap 22559dfa54cSAmit Daniel Kachhap switch (pdata->cal_type) { 22659dfa54cSAmit Daniel Kachhap case TYPE_TWO_POINT_TRIMMING: 227bb34b4c8SAmit Daniel Kachhap temp_code = (temp - pdata->first_point_trim) * 22859dfa54cSAmit Daniel Kachhap (data->temp_error2 - data->temp_error1) / 229bb34b4c8SAmit Daniel Kachhap (pdata->second_point_trim - pdata->first_point_trim) + 230bb34b4c8SAmit Daniel Kachhap data->temp_error1; 23159dfa54cSAmit Daniel Kachhap break; 23259dfa54cSAmit Daniel Kachhap case TYPE_ONE_POINT_TRIMMING: 233bb34b4c8SAmit Daniel Kachhap temp_code = temp + data->temp_error1 - pdata->first_point_trim; 23459dfa54cSAmit Daniel Kachhap break; 23559dfa54cSAmit Daniel Kachhap default: 236bb34b4c8SAmit Daniel Kachhap temp_code = temp + pdata->default_temp_offset; 23759dfa54cSAmit Daniel Kachhap break; 23859dfa54cSAmit Daniel Kachhap } 239ddb31d43SBartlomiej Zolnierkiewicz 24059dfa54cSAmit Daniel Kachhap return temp_code; 24159dfa54cSAmit Daniel Kachhap } 24259dfa54cSAmit Daniel Kachhap 24359dfa54cSAmit Daniel Kachhap /* 24459dfa54cSAmit Daniel Kachhap * Calculate a temperature value from a temperature code. 24559dfa54cSAmit Daniel Kachhap * The unit of the temperature is degree Celsius. 24659dfa54cSAmit Daniel Kachhap */ 2476c247393SAbhilash Kesavan static int code_to_temp(struct exynos_tmu_data *data, u16 temp_code) 24859dfa54cSAmit Daniel Kachhap { 24959dfa54cSAmit Daniel Kachhap struct exynos_tmu_platform_data *pdata = data->pdata; 25059dfa54cSAmit Daniel Kachhap int temp; 25159dfa54cSAmit Daniel Kachhap 25259dfa54cSAmit Daniel Kachhap switch (pdata->cal_type) { 25359dfa54cSAmit Daniel Kachhap case TYPE_TWO_POINT_TRIMMING: 254bb34b4c8SAmit Daniel Kachhap temp = (temp_code - data->temp_error1) * 255bb34b4c8SAmit Daniel Kachhap (pdata->second_point_trim - pdata->first_point_trim) / 256bb34b4c8SAmit Daniel Kachhap (data->temp_error2 - data->temp_error1) + 257bb34b4c8SAmit Daniel Kachhap pdata->first_point_trim; 25859dfa54cSAmit Daniel Kachhap break; 25959dfa54cSAmit Daniel Kachhap case TYPE_ONE_POINT_TRIMMING: 260bb34b4c8SAmit Daniel Kachhap temp = temp_code - data->temp_error1 + pdata->first_point_trim; 26159dfa54cSAmit Daniel Kachhap break; 26259dfa54cSAmit Daniel Kachhap default: 263bb34b4c8SAmit Daniel Kachhap temp = temp_code - pdata->default_temp_offset; 26459dfa54cSAmit Daniel Kachhap break; 26559dfa54cSAmit Daniel Kachhap } 266ddb31d43SBartlomiej Zolnierkiewicz 26759dfa54cSAmit Daniel Kachhap return temp; 26859dfa54cSAmit Daniel Kachhap } 26959dfa54cSAmit Daniel Kachhap 2708328a4b1SBartlomiej Zolnierkiewicz static void sanitize_temp_error(struct exynos_tmu_data *data, u32 trim_info) 271b835ced1SBartlomiej Zolnierkiewicz { 27259dfa54cSAmit Daniel Kachhap struct exynos_tmu_platform_data *pdata = data->pdata; 27359dfa54cSAmit Daniel Kachhap 274b8d582b9SAmit Daniel Kachhap data->temp_error1 = trim_info & EXYNOS_TMU_TEMP_MASK; 27599d67fb9SBartlomiej Zolnierkiewicz data->temp_error2 = ((trim_info >> EXYNOS_TRIMINFO_85_SHIFT) & 276b8d582b9SAmit Daniel Kachhap EXYNOS_TMU_TEMP_MASK); 27759dfa54cSAmit Daniel Kachhap 2785000806cSAmit Daniel Kachhap if (!data->temp_error1 || 2795000806cSAmit Daniel Kachhap (pdata->min_efuse_value > data->temp_error1) || 2805000806cSAmit Daniel Kachhap (data->temp_error1 > pdata->max_efuse_value)) 2815000806cSAmit Daniel Kachhap data->temp_error1 = pdata->efuse_value & EXYNOS_TMU_TEMP_MASK; 2825000806cSAmit Daniel Kachhap 2835000806cSAmit Daniel Kachhap if (!data->temp_error2) 2845000806cSAmit Daniel Kachhap data->temp_error2 = 28599d67fb9SBartlomiej Zolnierkiewicz (pdata->efuse_value >> EXYNOS_TRIMINFO_85_SHIFT) & 2865000806cSAmit Daniel Kachhap EXYNOS_TMU_TEMP_MASK; 2878328a4b1SBartlomiej Zolnierkiewicz } 28859dfa54cSAmit Daniel Kachhap 289fe87789cSBartlomiej Zolnierkiewicz static u32 get_th_reg(struct exynos_tmu_data *data, u32 threshold, bool falling) 290fe87789cSBartlomiej Zolnierkiewicz { 2913b6a1a80SLukasz Majewski struct thermal_zone_device *tz = data->tzd; 2923b6a1a80SLukasz Majewski const struct thermal_trip * const trips = 2933b6a1a80SLukasz Majewski of_thermal_get_trip_points(tz); 2943b6a1a80SLukasz Majewski unsigned long temp; 295fe87789cSBartlomiej Zolnierkiewicz int i; 296c65d3473STushar Behera 2973b6a1a80SLukasz Majewski if (!trips) { 2983b6a1a80SLukasz Majewski pr_err("%s: Cannot get trip points from of-thermal.c!\n", 2993b6a1a80SLukasz Majewski __func__); 3003b6a1a80SLukasz Majewski return 0; 3013b6a1a80SLukasz Majewski } 302fe87789cSBartlomiej Zolnierkiewicz 3033b6a1a80SLukasz Majewski for (i = 0; i < of_thermal_get_ntrips(tz); i++) { 3043b6a1a80SLukasz Majewski if (trips[i].type == THERMAL_TRIP_CRITICAL) 3053b6a1a80SLukasz Majewski continue; 3063b6a1a80SLukasz Majewski 3073b6a1a80SLukasz Majewski temp = trips[i].temperature / MCELSIUS; 308fe87789cSBartlomiej Zolnierkiewicz if (falling) 3093b6a1a80SLukasz Majewski temp -= (trips[i].hysteresis / MCELSIUS); 310fe87789cSBartlomiej Zolnierkiewicz else 311fe87789cSBartlomiej Zolnierkiewicz threshold &= ~(0xff << 8 * i); 312fe87789cSBartlomiej Zolnierkiewicz 313fe87789cSBartlomiej Zolnierkiewicz threshold |= temp_to_code(data, temp) << 8 * i; 31459dfa54cSAmit Daniel Kachhap } 31559dfa54cSAmit Daniel Kachhap 316fe87789cSBartlomiej Zolnierkiewicz return threshold; 317fe87789cSBartlomiej Zolnierkiewicz } 31859dfa54cSAmit Daniel Kachhap 31959dfa54cSAmit Daniel Kachhap static int exynos_tmu_initialize(struct platform_device *pdev) 32059dfa54cSAmit Daniel Kachhap { 32159dfa54cSAmit Daniel Kachhap struct exynos_tmu_data *data = platform_get_drvdata(pdev); 32272d1100bSBartlomiej Zolnierkiewicz int ret; 3237ca04e58SAmit Daniel Kachhap 32459dfa54cSAmit Daniel Kachhap mutex_lock(&data->lock); 32559dfa54cSAmit Daniel Kachhap clk_enable(data->clk); 32659dfa54cSAmit Daniel Kachhap if (!IS_ERR(data->clk_sec)) 32759dfa54cSAmit Daniel Kachhap clk_enable(data->clk_sec); 32872d1100bSBartlomiej Zolnierkiewicz ret = data->tmu_initialize(pdev); 32959dfa54cSAmit Daniel Kachhap clk_disable(data->clk); 33059dfa54cSAmit Daniel Kachhap mutex_unlock(&data->lock); 33114a11dc7SNaveen Krishna Chatradhi if (!IS_ERR(data->clk_sec)) 33214a11dc7SNaveen Krishna Chatradhi clk_disable(data->clk_sec); 33359dfa54cSAmit Daniel Kachhap 33459dfa54cSAmit Daniel Kachhap return ret; 33559dfa54cSAmit Daniel Kachhap } 33659dfa54cSAmit Daniel Kachhap 337d00671c3SBartlomiej Zolnierkiewicz static u32 get_con_reg(struct exynos_tmu_data *data, u32 con) 33859dfa54cSAmit Daniel Kachhap { 33959dfa54cSAmit Daniel Kachhap struct exynos_tmu_platform_data *pdata = data->pdata; 34059dfa54cSAmit Daniel Kachhap 3417575983cSBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS4412 || 3427575983cSBartlomiej Zolnierkiewicz data->soc == SOC_ARCH_EXYNOS3250) 3437575983cSBartlomiej Zolnierkiewicz con |= (EXYNOS4412_MUX_ADDR_VALUE << EXYNOS4412_MUX_ADDR_SHIFT); 34486f5362eSLukasz Majewski 34599d67fb9SBartlomiej Zolnierkiewicz con &= ~(EXYNOS_TMU_REF_VOLTAGE_MASK << EXYNOS_TMU_REF_VOLTAGE_SHIFT); 34699d67fb9SBartlomiej Zolnierkiewicz con |= pdata->reference_voltage << EXYNOS_TMU_REF_VOLTAGE_SHIFT; 347d0a0ce3eSAmit Daniel Kachhap 34899d67fb9SBartlomiej Zolnierkiewicz con &= ~(EXYNOS_TMU_BUF_SLOPE_SEL_MASK << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT); 34999d67fb9SBartlomiej Zolnierkiewicz con |= (pdata->gain << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT); 350d0a0ce3eSAmit Daniel Kachhap 351d0a0ce3eSAmit Daniel Kachhap if (pdata->noise_cancel_mode) { 352b9504a6aSBartlomiej Zolnierkiewicz con &= ~(EXYNOS_TMU_TRIP_MODE_MASK << EXYNOS_TMU_TRIP_MODE_SHIFT); 353b9504a6aSBartlomiej Zolnierkiewicz con |= (pdata->noise_cancel_mode << EXYNOS_TMU_TRIP_MODE_SHIFT); 35459dfa54cSAmit Daniel Kachhap } 35559dfa54cSAmit Daniel Kachhap 356d00671c3SBartlomiej Zolnierkiewicz return con; 357d00671c3SBartlomiej Zolnierkiewicz } 358d00671c3SBartlomiej Zolnierkiewicz 359d00671c3SBartlomiej Zolnierkiewicz static void exynos_tmu_control(struct platform_device *pdev, bool on) 360d00671c3SBartlomiej Zolnierkiewicz { 361d00671c3SBartlomiej Zolnierkiewicz struct exynos_tmu_data *data = platform_get_drvdata(pdev); 362d00671c3SBartlomiej Zolnierkiewicz 363d00671c3SBartlomiej Zolnierkiewicz mutex_lock(&data->lock); 364d00671c3SBartlomiej Zolnierkiewicz clk_enable(data->clk); 36537f9034fSBartlomiej Zolnierkiewicz data->tmu_control(pdev, on); 36659dfa54cSAmit Daniel Kachhap clk_disable(data->clk); 36759dfa54cSAmit Daniel Kachhap mutex_unlock(&data->lock); 36859dfa54cSAmit Daniel Kachhap } 36959dfa54cSAmit Daniel Kachhap 37072d1100bSBartlomiej Zolnierkiewicz static int exynos4210_tmu_initialize(struct platform_device *pdev) 37172d1100bSBartlomiej Zolnierkiewicz { 37272d1100bSBartlomiej Zolnierkiewicz struct exynos_tmu_data *data = platform_get_drvdata(pdev); 3733b6a1a80SLukasz Majewski struct thermal_zone_device *tz = data->tzd; 3743b6a1a80SLukasz Majewski const struct thermal_trip * const trips = 3753b6a1a80SLukasz Majewski of_thermal_get_trip_points(tz); 37672d1100bSBartlomiej Zolnierkiewicz int ret = 0, threshold_code, i; 3773b6a1a80SLukasz Majewski unsigned long reference, temp; 3783b6a1a80SLukasz Majewski unsigned int status; 3793b6a1a80SLukasz Majewski 3803b6a1a80SLukasz Majewski if (!trips) { 3813b6a1a80SLukasz Majewski pr_err("%s: Cannot get trip points from of-thermal.c!\n", 3823b6a1a80SLukasz Majewski __func__); 3833b6a1a80SLukasz Majewski ret = -ENODEV; 3843b6a1a80SLukasz Majewski goto out; 3853b6a1a80SLukasz Majewski } 38672d1100bSBartlomiej Zolnierkiewicz 38772d1100bSBartlomiej Zolnierkiewicz status = readb(data->base + EXYNOS_TMU_REG_STATUS); 38872d1100bSBartlomiej Zolnierkiewicz if (!status) { 38972d1100bSBartlomiej Zolnierkiewicz ret = -EBUSY; 39072d1100bSBartlomiej Zolnierkiewicz goto out; 39172d1100bSBartlomiej Zolnierkiewicz } 39272d1100bSBartlomiej Zolnierkiewicz 39372d1100bSBartlomiej Zolnierkiewicz sanitize_temp_error(data, readl(data->base + EXYNOS_TMU_REG_TRIMINFO)); 39472d1100bSBartlomiej Zolnierkiewicz 39572d1100bSBartlomiej Zolnierkiewicz /* Write temperature code for threshold */ 3963b6a1a80SLukasz Majewski reference = trips[0].temperature / MCELSIUS; 3973b6a1a80SLukasz Majewski threshold_code = temp_to_code(data, reference); 3983b6a1a80SLukasz Majewski if (threshold_code < 0) { 3993b6a1a80SLukasz Majewski ret = threshold_code; 4003b6a1a80SLukasz Majewski goto out; 4013b6a1a80SLukasz Majewski } 40272d1100bSBartlomiej Zolnierkiewicz writeb(threshold_code, data->base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP); 40372d1100bSBartlomiej Zolnierkiewicz 4043b6a1a80SLukasz Majewski for (i = 0; i < of_thermal_get_ntrips(tz); i++) { 4053b6a1a80SLukasz Majewski temp = trips[i].temperature / MCELSIUS; 4063b6a1a80SLukasz Majewski writeb(temp - reference, data->base + 40772d1100bSBartlomiej Zolnierkiewicz EXYNOS4210_TMU_REG_TRIG_LEVEL0 + i * 4); 4083b6a1a80SLukasz Majewski } 40972d1100bSBartlomiej Zolnierkiewicz 410a7331f72SBartlomiej Zolnierkiewicz data->tmu_clear_irqs(data); 41172d1100bSBartlomiej Zolnierkiewicz out: 41272d1100bSBartlomiej Zolnierkiewicz return ret; 41372d1100bSBartlomiej Zolnierkiewicz } 41472d1100bSBartlomiej Zolnierkiewicz 41572d1100bSBartlomiej Zolnierkiewicz static int exynos4412_tmu_initialize(struct platform_device *pdev) 41672d1100bSBartlomiej Zolnierkiewicz { 41772d1100bSBartlomiej Zolnierkiewicz struct exynos_tmu_data *data = platform_get_drvdata(pdev); 4183b6a1a80SLukasz Majewski const struct thermal_trip * const trips = 4193b6a1a80SLukasz Majewski of_thermal_get_trip_points(data->tzd); 42072d1100bSBartlomiej Zolnierkiewicz unsigned int status, trim_info, con, ctrl, rising_threshold; 42172d1100bSBartlomiej Zolnierkiewicz int ret = 0, threshold_code, i; 4223b6a1a80SLukasz Majewski unsigned long crit_temp = 0; 42372d1100bSBartlomiej Zolnierkiewicz 42472d1100bSBartlomiej Zolnierkiewicz status = readb(data->base + EXYNOS_TMU_REG_STATUS); 42572d1100bSBartlomiej Zolnierkiewicz if (!status) { 42672d1100bSBartlomiej Zolnierkiewicz ret = -EBUSY; 42772d1100bSBartlomiej Zolnierkiewicz goto out; 42872d1100bSBartlomiej Zolnierkiewicz } 42972d1100bSBartlomiej Zolnierkiewicz 43072d1100bSBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS3250 || 43172d1100bSBartlomiej Zolnierkiewicz data->soc == SOC_ARCH_EXYNOS4412 || 43272d1100bSBartlomiej Zolnierkiewicz data->soc == SOC_ARCH_EXYNOS5250) { 43372d1100bSBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS3250) { 43472d1100bSBartlomiej Zolnierkiewicz ctrl = readl(data->base + EXYNOS_TMU_TRIMINFO_CON1); 43572d1100bSBartlomiej Zolnierkiewicz ctrl |= EXYNOS_TRIMINFO_RELOAD_ENABLE; 43672d1100bSBartlomiej Zolnierkiewicz writel(ctrl, data->base + EXYNOS_TMU_TRIMINFO_CON1); 43772d1100bSBartlomiej Zolnierkiewicz } 43872d1100bSBartlomiej Zolnierkiewicz ctrl = readl(data->base + EXYNOS_TMU_TRIMINFO_CON2); 43972d1100bSBartlomiej Zolnierkiewicz ctrl |= EXYNOS_TRIMINFO_RELOAD_ENABLE; 44072d1100bSBartlomiej Zolnierkiewicz writel(ctrl, data->base + EXYNOS_TMU_TRIMINFO_CON2); 44172d1100bSBartlomiej Zolnierkiewicz } 44272d1100bSBartlomiej Zolnierkiewicz 44372d1100bSBartlomiej Zolnierkiewicz /* On exynos5420 the triminfo register is in the shared space */ 44472d1100bSBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) 44572d1100bSBartlomiej Zolnierkiewicz trim_info = readl(data->base_second + EXYNOS_TMU_REG_TRIMINFO); 44672d1100bSBartlomiej Zolnierkiewicz else 44772d1100bSBartlomiej Zolnierkiewicz trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); 44872d1100bSBartlomiej Zolnierkiewicz 44972d1100bSBartlomiej Zolnierkiewicz sanitize_temp_error(data, trim_info); 45072d1100bSBartlomiej Zolnierkiewicz 45172d1100bSBartlomiej Zolnierkiewicz /* Write temperature code for rising and falling threshold */ 45272d1100bSBartlomiej Zolnierkiewicz rising_threshold = readl(data->base + EXYNOS_THD_TEMP_RISE); 45372d1100bSBartlomiej Zolnierkiewicz rising_threshold = get_th_reg(data, rising_threshold, false); 45472d1100bSBartlomiej Zolnierkiewicz writel(rising_threshold, data->base + EXYNOS_THD_TEMP_RISE); 45572d1100bSBartlomiej Zolnierkiewicz writel(get_th_reg(data, 0, true), data->base + EXYNOS_THD_TEMP_FALL); 45672d1100bSBartlomiej Zolnierkiewicz 457a7331f72SBartlomiej Zolnierkiewicz data->tmu_clear_irqs(data); 45872d1100bSBartlomiej Zolnierkiewicz 45972d1100bSBartlomiej Zolnierkiewicz /* if last threshold limit is also present */ 4603b6a1a80SLukasz Majewski for (i = 0; i < of_thermal_get_ntrips(data->tzd); i++) { 4613b6a1a80SLukasz Majewski if (trips[i].type == THERMAL_TRIP_CRITICAL) { 4623b6a1a80SLukasz Majewski crit_temp = trips[i].temperature; 4633b6a1a80SLukasz Majewski break; 4643b6a1a80SLukasz Majewski } 4653b6a1a80SLukasz Majewski } 4663b6a1a80SLukasz Majewski 4673b6a1a80SLukasz Majewski if (i == of_thermal_get_ntrips(data->tzd)) { 4683b6a1a80SLukasz Majewski pr_err("%s: No CRITICAL trip point defined at of-thermal.c!\n", 4693b6a1a80SLukasz Majewski __func__); 4703b6a1a80SLukasz Majewski ret = -EINVAL; 4713b6a1a80SLukasz Majewski goto out; 4723b6a1a80SLukasz Majewski } 4733b6a1a80SLukasz Majewski 4743b6a1a80SLukasz Majewski threshold_code = temp_to_code(data, crit_temp / MCELSIUS); 47572d1100bSBartlomiej Zolnierkiewicz /* 1-4 level to be assigned in th0 reg */ 47672d1100bSBartlomiej Zolnierkiewicz rising_threshold &= ~(0xff << 8 * i); 47772d1100bSBartlomiej Zolnierkiewicz rising_threshold |= threshold_code << 8 * i; 47872d1100bSBartlomiej Zolnierkiewicz writel(rising_threshold, data->base + EXYNOS_THD_TEMP_RISE); 47972d1100bSBartlomiej Zolnierkiewicz con = readl(data->base + EXYNOS_TMU_REG_CONTROL); 48072d1100bSBartlomiej Zolnierkiewicz con |= (1 << EXYNOS_TMU_THERM_TRIP_EN_SHIFT); 48172d1100bSBartlomiej Zolnierkiewicz writel(con, data->base + EXYNOS_TMU_REG_CONTROL); 4823b6a1a80SLukasz Majewski 48372d1100bSBartlomiej Zolnierkiewicz out: 48472d1100bSBartlomiej Zolnierkiewicz return ret; 48572d1100bSBartlomiej Zolnierkiewicz } 48672d1100bSBartlomiej Zolnierkiewicz 48772d1100bSBartlomiej Zolnierkiewicz static int exynos5440_tmu_initialize(struct platform_device *pdev) 48872d1100bSBartlomiej Zolnierkiewicz { 48972d1100bSBartlomiej Zolnierkiewicz struct exynos_tmu_data *data = platform_get_drvdata(pdev); 49072d1100bSBartlomiej Zolnierkiewicz unsigned int trim_info = 0, con, rising_threshold; 4913b6a1a80SLukasz Majewski int ret = 0, threshold_code; 4923b6a1a80SLukasz Majewski unsigned long crit_temp = 0; 49372d1100bSBartlomiej Zolnierkiewicz 49472d1100bSBartlomiej Zolnierkiewicz /* 49572d1100bSBartlomiej Zolnierkiewicz * For exynos5440 soc triminfo value is swapped between TMU0 and 49672d1100bSBartlomiej Zolnierkiewicz * TMU2, so the below logic is needed. 49772d1100bSBartlomiej Zolnierkiewicz */ 49872d1100bSBartlomiej Zolnierkiewicz switch (data->id) { 49972d1100bSBartlomiej Zolnierkiewicz case 0: 50072d1100bSBartlomiej Zolnierkiewicz trim_info = readl(data->base + EXYNOS5440_EFUSE_SWAP_OFFSET + 50172d1100bSBartlomiej Zolnierkiewicz EXYNOS5440_TMU_S0_7_TRIM); 50272d1100bSBartlomiej Zolnierkiewicz break; 50372d1100bSBartlomiej Zolnierkiewicz case 1: 50472d1100bSBartlomiej Zolnierkiewicz trim_info = readl(data->base + EXYNOS5440_TMU_S0_7_TRIM); 50572d1100bSBartlomiej Zolnierkiewicz break; 50672d1100bSBartlomiej Zolnierkiewicz case 2: 50772d1100bSBartlomiej Zolnierkiewicz trim_info = readl(data->base - EXYNOS5440_EFUSE_SWAP_OFFSET + 50872d1100bSBartlomiej Zolnierkiewicz EXYNOS5440_TMU_S0_7_TRIM); 50972d1100bSBartlomiej Zolnierkiewicz } 51072d1100bSBartlomiej Zolnierkiewicz sanitize_temp_error(data, trim_info); 51172d1100bSBartlomiej Zolnierkiewicz 51272d1100bSBartlomiej Zolnierkiewicz /* Write temperature code for rising and falling threshold */ 51372d1100bSBartlomiej Zolnierkiewicz rising_threshold = readl(data->base + EXYNOS5440_TMU_S0_7_TH0); 51472d1100bSBartlomiej Zolnierkiewicz rising_threshold = get_th_reg(data, rising_threshold, false); 51572d1100bSBartlomiej Zolnierkiewicz writel(rising_threshold, data->base + EXYNOS5440_TMU_S0_7_TH0); 51672d1100bSBartlomiej Zolnierkiewicz writel(0, data->base + EXYNOS5440_TMU_S0_7_TH1); 51772d1100bSBartlomiej Zolnierkiewicz 518a7331f72SBartlomiej Zolnierkiewicz data->tmu_clear_irqs(data); 51972d1100bSBartlomiej Zolnierkiewicz 52072d1100bSBartlomiej Zolnierkiewicz /* if last threshold limit is also present */ 5213b6a1a80SLukasz Majewski if (!data->tzd->ops->get_crit_temp(data->tzd, &crit_temp)) { 5223b6a1a80SLukasz Majewski threshold_code = temp_to_code(data, crit_temp / MCELSIUS); 52372d1100bSBartlomiej Zolnierkiewicz /* 5th level to be assigned in th2 reg */ 52472d1100bSBartlomiej Zolnierkiewicz rising_threshold = 52572d1100bSBartlomiej Zolnierkiewicz threshold_code << EXYNOS5440_TMU_TH_RISE4_SHIFT; 52672d1100bSBartlomiej Zolnierkiewicz writel(rising_threshold, data->base + EXYNOS5440_TMU_S0_7_TH2); 52772d1100bSBartlomiej Zolnierkiewicz con = readl(data->base + EXYNOS5440_TMU_S0_7_CTRL); 52872d1100bSBartlomiej Zolnierkiewicz con |= (1 << EXYNOS_TMU_THERM_TRIP_EN_SHIFT); 52972d1100bSBartlomiej Zolnierkiewicz writel(con, data->base + EXYNOS5440_TMU_S0_7_CTRL); 53072d1100bSBartlomiej Zolnierkiewicz } 53172d1100bSBartlomiej Zolnierkiewicz /* Clear the PMIN in the common TMU register */ 53272d1100bSBartlomiej Zolnierkiewicz if (!data->id) 53372d1100bSBartlomiej Zolnierkiewicz writel(0, data->base_second + EXYNOS5440_TMU_PMIN); 53472d1100bSBartlomiej Zolnierkiewicz return ret; 53572d1100bSBartlomiej Zolnierkiewicz } 53672d1100bSBartlomiej Zolnierkiewicz 5376c247393SAbhilash Kesavan static int exynos7_tmu_initialize(struct platform_device *pdev) 5386c247393SAbhilash Kesavan { 5396c247393SAbhilash Kesavan struct exynos_tmu_data *data = platform_get_drvdata(pdev); 5406c247393SAbhilash Kesavan struct thermal_zone_device *tz = data->tzd; 5416c247393SAbhilash Kesavan struct exynos_tmu_platform_data *pdata = data->pdata; 5426c247393SAbhilash Kesavan unsigned int status, trim_info; 5436c247393SAbhilash Kesavan unsigned int rising_threshold = 0, falling_threshold = 0; 5446c247393SAbhilash Kesavan int ret = 0, threshold_code, i; 5456c247393SAbhilash Kesavan unsigned long temp, temp_hist; 5466c247393SAbhilash Kesavan unsigned int reg_off, bit_off; 5476c247393SAbhilash Kesavan 5486c247393SAbhilash Kesavan status = readb(data->base + EXYNOS_TMU_REG_STATUS); 5496c247393SAbhilash Kesavan if (!status) { 5506c247393SAbhilash Kesavan ret = -EBUSY; 5516c247393SAbhilash Kesavan goto out; 5526c247393SAbhilash Kesavan } 5536c247393SAbhilash Kesavan 5546c247393SAbhilash Kesavan trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); 5556c247393SAbhilash Kesavan 5566c247393SAbhilash Kesavan data->temp_error1 = trim_info & EXYNOS7_TMU_TEMP_MASK; 5576c247393SAbhilash Kesavan if (!data->temp_error1 || 5586c247393SAbhilash Kesavan (pdata->min_efuse_value > data->temp_error1) || 5596c247393SAbhilash Kesavan (data->temp_error1 > pdata->max_efuse_value)) 5606c247393SAbhilash Kesavan data->temp_error1 = pdata->efuse_value & EXYNOS_TMU_TEMP_MASK; 5616c247393SAbhilash Kesavan 5626c247393SAbhilash Kesavan /* Write temperature code for rising and falling threshold */ 5636c247393SAbhilash Kesavan for (i = (of_thermal_get_ntrips(tz) - 1); i >= 0; i--) { 5646c247393SAbhilash Kesavan /* 5656c247393SAbhilash Kesavan * On exynos7 there are 4 rising and 4 falling threshold 5666c247393SAbhilash Kesavan * registers (0x50-0x5c and 0x60-0x6c respectively). Each 5676c247393SAbhilash Kesavan * register holds the value of two threshold levels (at bit 5686c247393SAbhilash Kesavan * offsets 0 and 16). Based on the fact that there are atmost 5696c247393SAbhilash Kesavan * eight possible trigger levels, calculate the register and 5706c247393SAbhilash Kesavan * bit offsets where the threshold levels are to be written. 5716c247393SAbhilash Kesavan * 5726c247393SAbhilash Kesavan * e.g. EXYNOS7_THD_TEMP_RISE7_6 (0x50) 5736c247393SAbhilash Kesavan * [24:16] - Threshold level 7 5746c247393SAbhilash Kesavan * [8:0] - Threshold level 6 5756c247393SAbhilash Kesavan * e.g. EXYNOS7_THD_TEMP_RISE5_4 (0x54) 5766c247393SAbhilash Kesavan * [24:16] - Threshold level 5 5776c247393SAbhilash Kesavan * [8:0] - Threshold level 4 5786c247393SAbhilash Kesavan * 5796c247393SAbhilash Kesavan * and similarly for falling thresholds. 5806c247393SAbhilash Kesavan * 5816c247393SAbhilash Kesavan * Based on the above, calculate the register and bit offsets 5826c247393SAbhilash Kesavan * for rising/falling threshold levels and populate them. 5836c247393SAbhilash Kesavan */ 5846c247393SAbhilash Kesavan reg_off = ((7 - i) / 2) * 4; 5856c247393SAbhilash Kesavan bit_off = ((8 - i) % 2); 5866c247393SAbhilash Kesavan 5876c247393SAbhilash Kesavan tz->ops->get_trip_temp(tz, i, &temp); 5886c247393SAbhilash Kesavan temp /= MCELSIUS; 5896c247393SAbhilash Kesavan 5906c247393SAbhilash Kesavan tz->ops->get_trip_hyst(tz, i, &temp_hist); 5916c247393SAbhilash Kesavan temp_hist = temp - (temp_hist / MCELSIUS); 5926c247393SAbhilash Kesavan 5936c247393SAbhilash Kesavan /* Set 9-bit temperature code for rising threshold levels */ 5946c247393SAbhilash Kesavan threshold_code = temp_to_code(data, temp); 5956c247393SAbhilash Kesavan rising_threshold = readl(data->base + 5966c247393SAbhilash Kesavan EXYNOS7_THD_TEMP_RISE7_6 + reg_off); 5976c247393SAbhilash Kesavan rising_threshold &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off)); 5986c247393SAbhilash Kesavan rising_threshold |= threshold_code << (16 * bit_off); 5996c247393SAbhilash Kesavan writel(rising_threshold, 6006c247393SAbhilash Kesavan data->base + EXYNOS7_THD_TEMP_RISE7_6 + reg_off); 6016c247393SAbhilash Kesavan 6026c247393SAbhilash Kesavan /* Set 9-bit temperature code for falling threshold levels */ 6036c247393SAbhilash Kesavan threshold_code = temp_to_code(data, temp_hist); 6046c247393SAbhilash Kesavan falling_threshold &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off)); 6056c247393SAbhilash Kesavan falling_threshold |= threshold_code << (16 * bit_off); 6066c247393SAbhilash Kesavan writel(falling_threshold, 6076c247393SAbhilash Kesavan data->base + EXYNOS7_THD_TEMP_FALL7_6 + reg_off); 6086c247393SAbhilash Kesavan } 6096c247393SAbhilash Kesavan 6106c247393SAbhilash Kesavan data->tmu_clear_irqs(data); 6116c247393SAbhilash Kesavan out: 6126c247393SAbhilash Kesavan return ret; 6136c247393SAbhilash Kesavan } 6146c247393SAbhilash Kesavan 61537f9034fSBartlomiej Zolnierkiewicz static void exynos4210_tmu_control(struct platform_device *pdev, bool on) 61637f9034fSBartlomiej Zolnierkiewicz { 61737f9034fSBartlomiej Zolnierkiewicz struct exynos_tmu_data *data = platform_get_drvdata(pdev); 6183b6a1a80SLukasz Majewski struct thermal_zone_device *tz = data->tzd; 61937f9034fSBartlomiej Zolnierkiewicz unsigned int con, interrupt_en; 62037f9034fSBartlomiej Zolnierkiewicz 62137f9034fSBartlomiej Zolnierkiewicz con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL)); 62237f9034fSBartlomiej Zolnierkiewicz 62359dfa54cSAmit Daniel Kachhap if (on) { 62459dfa54cSAmit Daniel Kachhap con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); 62559dfa54cSAmit Daniel Kachhap interrupt_en = 6263b6a1a80SLukasz Majewski (of_thermal_is_trip_valid(tz, 3) 6273b6a1a80SLukasz Majewski << EXYNOS_TMU_INTEN_RISE3_SHIFT) | 6283b6a1a80SLukasz Majewski (of_thermal_is_trip_valid(tz, 2) 6293b6a1a80SLukasz Majewski << EXYNOS_TMU_INTEN_RISE2_SHIFT) | 6303b6a1a80SLukasz Majewski (of_thermal_is_trip_valid(tz, 1) 6313b6a1a80SLukasz Majewski << EXYNOS_TMU_INTEN_RISE1_SHIFT) | 6323b6a1a80SLukasz Majewski (of_thermal_is_trip_valid(tz, 0) 6333b6a1a80SLukasz Majewski << EXYNOS_TMU_INTEN_RISE0_SHIFT); 6343b6a1a80SLukasz Majewski 635e0761533SBartlomiej Zolnierkiewicz if (data->soc != SOC_ARCH_EXYNOS4210) 63659dfa54cSAmit Daniel Kachhap interrupt_en |= 63737f9034fSBartlomiej Zolnierkiewicz interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT; 63859dfa54cSAmit Daniel Kachhap } else { 63959dfa54cSAmit Daniel Kachhap con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); 64059dfa54cSAmit Daniel Kachhap interrupt_en = 0; /* Disable all interrupts */ 64159dfa54cSAmit Daniel Kachhap } 64237f9034fSBartlomiej Zolnierkiewicz writel(interrupt_en, data->base + EXYNOS_TMU_REG_INTEN); 64337f9034fSBartlomiej Zolnierkiewicz writel(con, data->base + EXYNOS_TMU_REG_CONTROL); 64437f9034fSBartlomiej Zolnierkiewicz } 64559dfa54cSAmit Daniel Kachhap 64637f9034fSBartlomiej Zolnierkiewicz static void exynos5440_tmu_control(struct platform_device *pdev, bool on) 64737f9034fSBartlomiej Zolnierkiewicz { 64837f9034fSBartlomiej Zolnierkiewicz struct exynos_tmu_data *data = platform_get_drvdata(pdev); 6493b6a1a80SLukasz Majewski struct thermal_zone_device *tz = data->tzd; 65037f9034fSBartlomiej Zolnierkiewicz unsigned int con, interrupt_en; 65137f9034fSBartlomiej Zolnierkiewicz 65237f9034fSBartlomiej Zolnierkiewicz con = get_con_reg(data, readl(data->base + EXYNOS5440_TMU_S0_7_CTRL)); 65337f9034fSBartlomiej Zolnierkiewicz 65437f9034fSBartlomiej Zolnierkiewicz if (on) { 65537f9034fSBartlomiej Zolnierkiewicz con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); 65637f9034fSBartlomiej Zolnierkiewicz interrupt_en = 6573b6a1a80SLukasz Majewski (of_thermal_is_trip_valid(tz, 3) 6583b6a1a80SLukasz Majewski << EXYNOS5440_TMU_INTEN_RISE3_SHIFT) | 6593b6a1a80SLukasz Majewski (of_thermal_is_trip_valid(tz, 2) 6603b6a1a80SLukasz Majewski << EXYNOS5440_TMU_INTEN_RISE2_SHIFT) | 6613b6a1a80SLukasz Majewski (of_thermal_is_trip_valid(tz, 1) 6623b6a1a80SLukasz Majewski << EXYNOS5440_TMU_INTEN_RISE1_SHIFT) | 6633b6a1a80SLukasz Majewski (of_thermal_is_trip_valid(tz, 0) 6643b6a1a80SLukasz Majewski << EXYNOS5440_TMU_INTEN_RISE0_SHIFT); 6653b6a1a80SLukasz Majewski interrupt_en |= 6663b6a1a80SLukasz Majewski interrupt_en << EXYNOS5440_TMU_INTEN_FALL0_SHIFT; 66737f9034fSBartlomiej Zolnierkiewicz } else { 66837f9034fSBartlomiej Zolnierkiewicz con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); 66937f9034fSBartlomiej Zolnierkiewicz interrupt_en = 0; /* Disable all interrupts */ 67037f9034fSBartlomiej Zolnierkiewicz } 67137f9034fSBartlomiej Zolnierkiewicz writel(interrupt_en, data->base + EXYNOS5440_TMU_S0_7_IRQEN); 67237f9034fSBartlomiej Zolnierkiewicz writel(con, data->base + EXYNOS5440_TMU_S0_7_CTRL); 67359dfa54cSAmit Daniel Kachhap } 67459dfa54cSAmit Daniel Kachhap 6756c247393SAbhilash Kesavan static void exynos7_tmu_control(struct platform_device *pdev, bool on) 6766c247393SAbhilash Kesavan { 6776c247393SAbhilash Kesavan struct exynos_tmu_data *data = platform_get_drvdata(pdev); 6786c247393SAbhilash Kesavan struct thermal_zone_device *tz = data->tzd; 6796c247393SAbhilash Kesavan unsigned int con, interrupt_en; 6806c247393SAbhilash Kesavan 6816c247393SAbhilash Kesavan con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL)); 6826c247393SAbhilash Kesavan 6836c247393SAbhilash Kesavan if (on) { 6846c247393SAbhilash Kesavan con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); 6856c247393SAbhilash Kesavan interrupt_en = 6866c247393SAbhilash Kesavan (of_thermal_is_trip_valid(tz, 7) 6876c247393SAbhilash Kesavan << EXYNOS7_TMU_INTEN_RISE7_SHIFT) | 6886c247393SAbhilash Kesavan (of_thermal_is_trip_valid(tz, 6) 6896c247393SAbhilash Kesavan << EXYNOS7_TMU_INTEN_RISE6_SHIFT) | 6906c247393SAbhilash Kesavan (of_thermal_is_trip_valid(tz, 5) 6916c247393SAbhilash Kesavan << EXYNOS7_TMU_INTEN_RISE5_SHIFT) | 6926c247393SAbhilash Kesavan (of_thermal_is_trip_valid(tz, 4) 6936c247393SAbhilash Kesavan << EXYNOS7_TMU_INTEN_RISE4_SHIFT) | 6946c247393SAbhilash Kesavan (of_thermal_is_trip_valid(tz, 3) 6956c247393SAbhilash Kesavan << EXYNOS7_TMU_INTEN_RISE3_SHIFT) | 6966c247393SAbhilash Kesavan (of_thermal_is_trip_valid(tz, 2) 6976c247393SAbhilash Kesavan << EXYNOS7_TMU_INTEN_RISE2_SHIFT) | 6986c247393SAbhilash Kesavan (of_thermal_is_trip_valid(tz, 1) 6996c247393SAbhilash Kesavan << EXYNOS7_TMU_INTEN_RISE1_SHIFT) | 7006c247393SAbhilash Kesavan (of_thermal_is_trip_valid(tz, 0) 7016c247393SAbhilash Kesavan << EXYNOS7_TMU_INTEN_RISE0_SHIFT); 7026c247393SAbhilash Kesavan 7036c247393SAbhilash Kesavan interrupt_en |= 7046c247393SAbhilash Kesavan interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT; 7056c247393SAbhilash Kesavan } else { 7066c247393SAbhilash Kesavan con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); 7076c247393SAbhilash Kesavan interrupt_en = 0; /* Disable all interrupts */ 7086c247393SAbhilash Kesavan } 7096c247393SAbhilash Kesavan con |= 1 << EXYNOS7_PD_DET_EN_SHIFT; 7106c247393SAbhilash Kesavan 7116c247393SAbhilash Kesavan writel(interrupt_en, data->base + EXYNOS7_TMU_REG_INTEN); 7126c247393SAbhilash Kesavan writel(con, data->base + EXYNOS_TMU_REG_CONTROL); 7136c247393SAbhilash Kesavan } 7146c247393SAbhilash Kesavan 7153b6a1a80SLukasz Majewski static int exynos_get_temp(void *p, long *temp) 71659dfa54cSAmit Daniel Kachhap { 7173b6a1a80SLukasz Majewski struct exynos_tmu_data *data = p; 7183b6a1a80SLukasz Majewski 719*4531fa16SLukasz Majewski if (!data || !data->tmu_read) 7203b6a1a80SLukasz Majewski return -EINVAL; 72159dfa54cSAmit Daniel Kachhap 72259dfa54cSAmit Daniel Kachhap mutex_lock(&data->lock); 72359dfa54cSAmit Daniel Kachhap clk_enable(data->clk); 7243b6a1a80SLukasz Majewski 7253b6a1a80SLukasz Majewski *temp = code_to_temp(data, data->tmu_read(data)) * MCELSIUS; 7263b6a1a80SLukasz Majewski 72759dfa54cSAmit Daniel Kachhap clk_disable(data->clk); 72859dfa54cSAmit Daniel Kachhap mutex_unlock(&data->lock); 72959dfa54cSAmit Daniel Kachhap 7303b6a1a80SLukasz Majewski return 0; 73159dfa54cSAmit Daniel Kachhap } 73259dfa54cSAmit Daniel Kachhap 73359dfa54cSAmit Daniel Kachhap #ifdef CONFIG_THERMAL_EMULATION 734154013eaSBartlomiej Zolnierkiewicz static u32 get_emul_con_reg(struct exynos_tmu_data *data, unsigned int val, 735154013eaSBartlomiej Zolnierkiewicz unsigned long temp) 736154013eaSBartlomiej Zolnierkiewicz { 737154013eaSBartlomiej Zolnierkiewicz if (temp) { 738154013eaSBartlomiej Zolnierkiewicz temp /= MCELSIUS; 739154013eaSBartlomiej Zolnierkiewicz 740d564b55aSBartlomiej Zolnierkiewicz if (data->soc != SOC_ARCH_EXYNOS5440) { 741154013eaSBartlomiej Zolnierkiewicz val &= ~(EXYNOS_EMUL_TIME_MASK << EXYNOS_EMUL_TIME_SHIFT); 742154013eaSBartlomiej Zolnierkiewicz val |= (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT); 743154013eaSBartlomiej Zolnierkiewicz } 7446c247393SAbhilash Kesavan if (data->soc == SOC_ARCH_EXYNOS7) { 7456c247393SAbhilash Kesavan val &= ~(EXYNOS7_EMUL_DATA_MASK << 7466c247393SAbhilash Kesavan EXYNOS7_EMUL_DATA_SHIFT); 7476c247393SAbhilash Kesavan val |= (temp_to_code(data, temp) << 7486c247393SAbhilash Kesavan EXYNOS7_EMUL_DATA_SHIFT) | 749154013eaSBartlomiej Zolnierkiewicz EXYNOS_EMUL_ENABLE; 750154013eaSBartlomiej Zolnierkiewicz } else { 7516c247393SAbhilash Kesavan val &= ~(EXYNOS_EMUL_DATA_MASK << 7526c247393SAbhilash Kesavan EXYNOS_EMUL_DATA_SHIFT); 7536c247393SAbhilash Kesavan val |= (temp_to_code(data, temp) << 7546c247393SAbhilash Kesavan EXYNOS_EMUL_DATA_SHIFT) | 7556c247393SAbhilash Kesavan EXYNOS_EMUL_ENABLE; 7566c247393SAbhilash Kesavan } 7576c247393SAbhilash Kesavan } else { 758154013eaSBartlomiej Zolnierkiewicz val &= ~EXYNOS_EMUL_ENABLE; 759154013eaSBartlomiej Zolnierkiewicz } 760154013eaSBartlomiej Zolnierkiewicz 761154013eaSBartlomiej Zolnierkiewicz return val; 762154013eaSBartlomiej Zolnierkiewicz } 763154013eaSBartlomiej Zolnierkiewicz 764285d994aSBartlomiej Zolnierkiewicz static void exynos4412_tmu_set_emulation(struct exynos_tmu_data *data, 765285d994aSBartlomiej Zolnierkiewicz unsigned long temp) 766285d994aSBartlomiej Zolnierkiewicz { 767285d994aSBartlomiej Zolnierkiewicz unsigned int val; 768285d994aSBartlomiej Zolnierkiewicz u32 emul_con; 769285d994aSBartlomiej Zolnierkiewicz 770285d994aSBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS5260) 771285d994aSBartlomiej Zolnierkiewicz emul_con = EXYNOS5260_EMUL_CON; 7726c247393SAbhilash Kesavan else if (data->soc == SOC_ARCH_EXYNOS7) 7736c247393SAbhilash Kesavan emul_con = EXYNOS7_TMU_REG_EMUL_CON; 774285d994aSBartlomiej Zolnierkiewicz else 775285d994aSBartlomiej Zolnierkiewicz emul_con = EXYNOS_EMUL_CON; 776285d994aSBartlomiej Zolnierkiewicz 777285d994aSBartlomiej Zolnierkiewicz val = readl(data->base + emul_con); 778285d994aSBartlomiej Zolnierkiewicz val = get_emul_con_reg(data, val, temp); 779285d994aSBartlomiej Zolnierkiewicz writel(val, data->base + emul_con); 780285d994aSBartlomiej Zolnierkiewicz } 781285d994aSBartlomiej Zolnierkiewicz 782285d994aSBartlomiej Zolnierkiewicz static void exynos5440_tmu_set_emulation(struct exynos_tmu_data *data, 783285d994aSBartlomiej Zolnierkiewicz unsigned long temp) 784285d994aSBartlomiej Zolnierkiewicz { 785285d994aSBartlomiej Zolnierkiewicz unsigned int val; 786285d994aSBartlomiej Zolnierkiewicz 787285d994aSBartlomiej Zolnierkiewicz val = readl(data->base + EXYNOS5440_TMU_S0_7_DEBUG); 788285d994aSBartlomiej Zolnierkiewicz val = get_emul_con_reg(data, val, temp); 789285d994aSBartlomiej Zolnierkiewicz writel(val, data->base + EXYNOS5440_TMU_S0_7_DEBUG); 790285d994aSBartlomiej Zolnierkiewicz } 791285d994aSBartlomiej Zolnierkiewicz 79259dfa54cSAmit Daniel Kachhap static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp) 79359dfa54cSAmit Daniel Kachhap { 79459dfa54cSAmit Daniel Kachhap struct exynos_tmu_data *data = drv_data; 79559dfa54cSAmit Daniel Kachhap int ret = -EINVAL; 79659dfa54cSAmit Daniel Kachhap 797ef3f80fcSBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS4210) 79859dfa54cSAmit Daniel Kachhap goto out; 79959dfa54cSAmit Daniel Kachhap 80059dfa54cSAmit Daniel Kachhap if (temp && temp < MCELSIUS) 80159dfa54cSAmit Daniel Kachhap goto out; 80259dfa54cSAmit Daniel Kachhap 80359dfa54cSAmit Daniel Kachhap mutex_lock(&data->lock); 80459dfa54cSAmit Daniel Kachhap clk_enable(data->clk); 805285d994aSBartlomiej Zolnierkiewicz data->tmu_set_emulation(data, temp); 80659dfa54cSAmit Daniel Kachhap clk_disable(data->clk); 80759dfa54cSAmit Daniel Kachhap mutex_unlock(&data->lock); 80859dfa54cSAmit Daniel Kachhap return 0; 80959dfa54cSAmit Daniel Kachhap out: 81059dfa54cSAmit Daniel Kachhap return ret; 81159dfa54cSAmit Daniel Kachhap } 81259dfa54cSAmit Daniel Kachhap #else 813285d994aSBartlomiej Zolnierkiewicz #define exynos4412_tmu_set_emulation NULL 814285d994aSBartlomiej Zolnierkiewicz #define exynos5440_tmu_set_emulation NULL 81559dfa54cSAmit Daniel Kachhap static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp) 81659dfa54cSAmit Daniel Kachhap { return -EINVAL; } 81759dfa54cSAmit Daniel Kachhap #endif /* CONFIG_THERMAL_EMULATION */ 81859dfa54cSAmit Daniel Kachhap 819b79985caSBartlomiej Zolnierkiewicz static int exynos4210_tmu_read(struct exynos_tmu_data *data) 820b79985caSBartlomiej Zolnierkiewicz { 821b79985caSBartlomiej Zolnierkiewicz int ret = readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP); 822b79985caSBartlomiej Zolnierkiewicz 823b79985caSBartlomiej Zolnierkiewicz /* "temp_code" should range between 75 and 175 */ 824b79985caSBartlomiej Zolnierkiewicz return (ret < 75 || ret > 175) ? -ENODATA : ret; 825b79985caSBartlomiej Zolnierkiewicz } 826b79985caSBartlomiej Zolnierkiewicz 827b79985caSBartlomiej Zolnierkiewicz static int exynos4412_tmu_read(struct exynos_tmu_data *data) 828b79985caSBartlomiej Zolnierkiewicz { 829b79985caSBartlomiej Zolnierkiewicz return readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP); 830b79985caSBartlomiej Zolnierkiewicz } 831b79985caSBartlomiej Zolnierkiewicz 832b79985caSBartlomiej Zolnierkiewicz static int exynos5440_tmu_read(struct exynos_tmu_data *data) 833b79985caSBartlomiej Zolnierkiewicz { 834b79985caSBartlomiej Zolnierkiewicz return readb(data->base + EXYNOS5440_TMU_S0_7_TEMP); 835b79985caSBartlomiej Zolnierkiewicz } 836b79985caSBartlomiej Zolnierkiewicz 8376c247393SAbhilash Kesavan static int exynos7_tmu_read(struct exynos_tmu_data *data) 8386c247393SAbhilash Kesavan { 8396c247393SAbhilash Kesavan return readw(data->base + EXYNOS_TMU_REG_CURRENT_TEMP) & 8406c247393SAbhilash Kesavan EXYNOS7_TMU_TEMP_MASK; 8416c247393SAbhilash Kesavan } 8426c247393SAbhilash Kesavan 84359dfa54cSAmit Daniel Kachhap static void exynos_tmu_work(struct work_struct *work) 84459dfa54cSAmit Daniel Kachhap { 84559dfa54cSAmit Daniel Kachhap struct exynos_tmu_data *data = container_of(work, 84659dfa54cSAmit Daniel Kachhap struct exynos_tmu_data, irq_work); 847b835ced1SBartlomiej Zolnierkiewicz unsigned int val_type; 848a0395eeeSAmit Daniel Kachhap 84914a11dc7SNaveen Krishna Chatradhi if (!IS_ERR(data->clk_sec)) 85014a11dc7SNaveen Krishna Chatradhi clk_enable(data->clk_sec); 851a0395eeeSAmit Daniel Kachhap /* Find which sensor generated this interrupt */ 852421d5d12SBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS5440) { 853421d5d12SBartlomiej Zolnierkiewicz val_type = readl(data->base_second + EXYNOS5440_TMU_IRQ_STATUS); 854a0395eeeSAmit Daniel Kachhap if (!((val_type >> data->id) & 0x1)) 855a0395eeeSAmit Daniel Kachhap goto out; 856a0395eeeSAmit Daniel Kachhap } 85714a11dc7SNaveen Krishna Chatradhi if (!IS_ERR(data->clk_sec)) 85814a11dc7SNaveen Krishna Chatradhi clk_disable(data->clk_sec); 85959dfa54cSAmit Daniel Kachhap 8603b6a1a80SLukasz Majewski exynos_report_trigger(data); 86159dfa54cSAmit Daniel Kachhap mutex_lock(&data->lock); 86259dfa54cSAmit Daniel Kachhap clk_enable(data->clk); 863b8d582b9SAmit Daniel Kachhap 864a4463c4fSAmit Daniel Kachhap /* TODO: take action based on particular interrupt */ 865a7331f72SBartlomiej Zolnierkiewicz data->tmu_clear_irqs(data); 866b8d582b9SAmit Daniel Kachhap 86759dfa54cSAmit Daniel Kachhap clk_disable(data->clk); 86859dfa54cSAmit Daniel Kachhap mutex_unlock(&data->lock); 869a0395eeeSAmit Daniel Kachhap out: 87059dfa54cSAmit Daniel Kachhap enable_irq(data->irq); 87159dfa54cSAmit Daniel Kachhap } 87259dfa54cSAmit Daniel Kachhap 873a7331f72SBartlomiej Zolnierkiewicz static void exynos4210_tmu_clear_irqs(struct exynos_tmu_data *data) 874a7331f72SBartlomiej Zolnierkiewicz { 875a7331f72SBartlomiej Zolnierkiewicz unsigned int val_irq; 876a7331f72SBartlomiej Zolnierkiewicz u32 tmu_intstat, tmu_intclear; 877a7331f72SBartlomiej Zolnierkiewicz 878a7331f72SBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS5260) { 879a7331f72SBartlomiej Zolnierkiewicz tmu_intstat = EXYNOS5260_TMU_REG_INTSTAT; 880a7331f72SBartlomiej Zolnierkiewicz tmu_intclear = EXYNOS5260_TMU_REG_INTCLEAR; 8816c247393SAbhilash Kesavan } else if (data->soc == SOC_ARCH_EXYNOS7) { 8826c247393SAbhilash Kesavan tmu_intstat = EXYNOS7_TMU_REG_INTPEND; 8836c247393SAbhilash Kesavan tmu_intclear = EXYNOS7_TMU_REG_INTPEND; 884a7331f72SBartlomiej Zolnierkiewicz } else { 885a7331f72SBartlomiej Zolnierkiewicz tmu_intstat = EXYNOS_TMU_REG_INTSTAT; 886a7331f72SBartlomiej Zolnierkiewicz tmu_intclear = EXYNOS_TMU_REG_INTCLEAR; 887a7331f72SBartlomiej Zolnierkiewicz } 888a7331f72SBartlomiej Zolnierkiewicz 889a7331f72SBartlomiej Zolnierkiewicz val_irq = readl(data->base + tmu_intstat); 890a7331f72SBartlomiej Zolnierkiewicz /* 891a7331f72SBartlomiej Zolnierkiewicz * Clear the interrupts. Please note that the documentation for 892a7331f72SBartlomiej Zolnierkiewicz * Exynos3250, Exynos4412, Exynos5250 and Exynos5260 incorrectly 893a7331f72SBartlomiej Zolnierkiewicz * states that INTCLEAR register has a different placing of bits 894a7331f72SBartlomiej Zolnierkiewicz * responsible for FALL IRQs than INTSTAT register. Exynos5420 895a7331f72SBartlomiej Zolnierkiewicz * and Exynos5440 documentation is correct (Exynos4210 doesn't 896a7331f72SBartlomiej Zolnierkiewicz * support FALL IRQs at all). 897a7331f72SBartlomiej Zolnierkiewicz */ 898a7331f72SBartlomiej Zolnierkiewicz writel(val_irq, data->base + tmu_intclear); 899a7331f72SBartlomiej Zolnierkiewicz } 900a7331f72SBartlomiej Zolnierkiewicz 901a7331f72SBartlomiej Zolnierkiewicz static void exynos5440_tmu_clear_irqs(struct exynos_tmu_data *data) 902a7331f72SBartlomiej Zolnierkiewicz { 903a7331f72SBartlomiej Zolnierkiewicz unsigned int val_irq; 904a7331f72SBartlomiej Zolnierkiewicz 905a7331f72SBartlomiej Zolnierkiewicz val_irq = readl(data->base + EXYNOS5440_TMU_S0_7_IRQ); 906a7331f72SBartlomiej Zolnierkiewicz /* clear the interrupts */ 907a7331f72SBartlomiej Zolnierkiewicz writel(val_irq, data->base + EXYNOS5440_TMU_S0_7_IRQ); 908a7331f72SBartlomiej Zolnierkiewicz } 909a7331f72SBartlomiej Zolnierkiewicz 91059dfa54cSAmit Daniel Kachhap static irqreturn_t exynos_tmu_irq(int irq, void *id) 91159dfa54cSAmit Daniel Kachhap { 91259dfa54cSAmit Daniel Kachhap struct exynos_tmu_data *data = id; 91359dfa54cSAmit Daniel Kachhap 91459dfa54cSAmit Daniel Kachhap disable_irq_nosync(irq); 91559dfa54cSAmit Daniel Kachhap schedule_work(&data->irq_work); 91659dfa54cSAmit Daniel Kachhap 91759dfa54cSAmit Daniel Kachhap return IRQ_HANDLED; 91859dfa54cSAmit Daniel Kachhap } 91959dfa54cSAmit Daniel Kachhap 92059dfa54cSAmit Daniel Kachhap static const struct of_device_id exynos_tmu_match[] = { 92159dfa54cSAmit Daniel Kachhap { 9221fe56dc1SChanwoo Choi .compatible = "samsung,exynos3250-tmu", 9231fe56dc1SChanwoo Choi }, 9241fe56dc1SChanwoo Choi { 92559dfa54cSAmit Daniel Kachhap .compatible = "samsung,exynos4210-tmu", 92659dfa54cSAmit Daniel Kachhap }, 92759dfa54cSAmit Daniel Kachhap { 92859dfa54cSAmit Daniel Kachhap .compatible = "samsung,exynos4412-tmu", 92959dfa54cSAmit Daniel Kachhap }, 93059dfa54cSAmit Daniel Kachhap { 93159dfa54cSAmit Daniel Kachhap .compatible = "samsung,exynos5250-tmu", 93259dfa54cSAmit Daniel Kachhap }, 93390542546SAmit Daniel Kachhap { 934923488a5SNaveen Krishna Chatradhi .compatible = "samsung,exynos5260-tmu", 935923488a5SNaveen Krishna Chatradhi }, 936923488a5SNaveen Krishna Chatradhi { 93714a11dc7SNaveen Krishna Chatradhi .compatible = "samsung,exynos5420-tmu", 93814a11dc7SNaveen Krishna Chatradhi }, 93914a11dc7SNaveen Krishna Chatradhi { 94014a11dc7SNaveen Krishna Chatradhi .compatible = "samsung,exynos5420-tmu-ext-triminfo", 94114a11dc7SNaveen Krishna Chatradhi }, 94214a11dc7SNaveen Krishna Chatradhi { 94390542546SAmit Daniel Kachhap .compatible = "samsung,exynos5440-tmu", 94490542546SAmit Daniel Kachhap }, 9456c247393SAbhilash Kesavan { 9466c247393SAbhilash Kesavan .compatible = "samsung,exynos7-tmu", 9476c247393SAbhilash Kesavan }, 94859dfa54cSAmit Daniel Kachhap {}, 94959dfa54cSAmit Daniel Kachhap }; 95059dfa54cSAmit Daniel Kachhap MODULE_DEVICE_TABLE(of, exynos_tmu_match); 95159dfa54cSAmit Daniel Kachhap 9523b6a1a80SLukasz Majewski static int exynos_of_get_soc_type(struct device_node *np) 95359dfa54cSAmit Daniel Kachhap { 9543b6a1a80SLukasz Majewski if (of_device_is_compatible(np, "samsung,exynos3250-tmu")) 9553b6a1a80SLukasz Majewski return SOC_ARCH_EXYNOS3250; 9563b6a1a80SLukasz Majewski else if (of_device_is_compatible(np, "samsung,exynos4210-tmu")) 9573b6a1a80SLukasz Majewski return SOC_ARCH_EXYNOS4210; 9583b6a1a80SLukasz Majewski else if (of_device_is_compatible(np, "samsung,exynos4412-tmu")) 9593b6a1a80SLukasz Majewski return SOC_ARCH_EXYNOS4412; 9603b6a1a80SLukasz Majewski else if (of_device_is_compatible(np, "samsung,exynos5250-tmu")) 9613b6a1a80SLukasz Majewski return SOC_ARCH_EXYNOS5250; 9623b6a1a80SLukasz Majewski else if (of_device_is_compatible(np, "samsung,exynos5260-tmu")) 9633b6a1a80SLukasz Majewski return SOC_ARCH_EXYNOS5260; 9643b6a1a80SLukasz Majewski else if (of_device_is_compatible(np, "samsung,exynos5420-tmu")) 9653b6a1a80SLukasz Majewski return SOC_ARCH_EXYNOS5420; 9663b6a1a80SLukasz Majewski else if (of_device_is_compatible(np, 9673b6a1a80SLukasz Majewski "samsung,exynos5420-tmu-ext-triminfo")) 9683b6a1a80SLukasz Majewski return SOC_ARCH_EXYNOS5420_TRIMINFO; 9693b6a1a80SLukasz Majewski else if (of_device_is_compatible(np, "samsung,exynos5440-tmu")) 9703b6a1a80SLukasz Majewski return SOC_ARCH_EXYNOS5440; 9716c247393SAbhilash Kesavan else if (of_device_is_compatible(np, "samsung,exynos7-tmu")) 9726c247393SAbhilash Kesavan return SOC_ARCH_EXYNOS7; 97373b5b1d7SSachin Kamat 9743b6a1a80SLukasz Majewski return -EINVAL; 9753b6a1a80SLukasz Majewski } 9763b6a1a80SLukasz Majewski 9773b6a1a80SLukasz Majewski static int exynos_of_sensor_conf(struct device_node *np, 9783b6a1a80SLukasz Majewski struct exynos_tmu_platform_data *pdata) 9793b6a1a80SLukasz Majewski { 9803b6a1a80SLukasz Majewski u32 value; 9813b6a1a80SLukasz Majewski int ret; 9823b6a1a80SLukasz Majewski 9833b6a1a80SLukasz Majewski of_node_get(np); 9843b6a1a80SLukasz Majewski 9853b6a1a80SLukasz Majewski ret = of_property_read_u32(np, "samsung,tmu_gain", &value); 9863b6a1a80SLukasz Majewski pdata->gain = (u8)value; 9873b6a1a80SLukasz Majewski of_property_read_u32(np, "samsung,tmu_reference_voltage", &value); 9883b6a1a80SLukasz Majewski pdata->reference_voltage = (u8)value; 9893b6a1a80SLukasz Majewski of_property_read_u32(np, "samsung,tmu_noise_cancel_mode", &value); 9903b6a1a80SLukasz Majewski pdata->noise_cancel_mode = (u8)value; 9913b6a1a80SLukasz Majewski 9923b6a1a80SLukasz Majewski of_property_read_u32(np, "samsung,tmu_efuse_value", 9933b6a1a80SLukasz Majewski &pdata->efuse_value); 9943b6a1a80SLukasz Majewski of_property_read_u32(np, "samsung,tmu_min_efuse_value", 9953b6a1a80SLukasz Majewski &pdata->min_efuse_value); 9963b6a1a80SLukasz Majewski of_property_read_u32(np, "samsung,tmu_max_efuse_value", 9973b6a1a80SLukasz Majewski &pdata->max_efuse_value); 9983b6a1a80SLukasz Majewski 9993b6a1a80SLukasz Majewski of_property_read_u32(np, "samsung,tmu_first_point_trim", &value); 10003b6a1a80SLukasz Majewski pdata->first_point_trim = (u8)value; 10013b6a1a80SLukasz Majewski of_property_read_u32(np, "samsung,tmu_second_point_trim", &value); 10023b6a1a80SLukasz Majewski pdata->second_point_trim = (u8)value; 10033b6a1a80SLukasz Majewski of_property_read_u32(np, "samsung,tmu_default_temp_offset", &value); 10043b6a1a80SLukasz Majewski pdata->default_temp_offset = (u8)value; 10053b6a1a80SLukasz Majewski 10063b6a1a80SLukasz Majewski of_property_read_u32(np, "samsung,tmu_cal_type", &pdata->cal_type); 10073b6a1a80SLukasz Majewski of_property_read_u32(np, "samsung,tmu_cal_mode", &pdata->cal_mode); 10083b6a1a80SLukasz Majewski 10093b6a1a80SLukasz Majewski of_node_put(np); 10103b6a1a80SLukasz Majewski return 0; 101159dfa54cSAmit Daniel Kachhap } 101259dfa54cSAmit Daniel Kachhap 1013cebe7373SAmit Daniel Kachhap static int exynos_map_dt_data(struct platform_device *pdev) 101459dfa54cSAmit Daniel Kachhap { 1015cebe7373SAmit Daniel Kachhap struct exynos_tmu_data *data = platform_get_drvdata(pdev); 1016cebe7373SAmit Daniel Kachhap struct exynos_tmu_platform_data *pdata; 1017cebe7373SAmit Daniel Kachhap struct resource res; 1018498d22f6SAmit Daniel Kachhap int ret; 101959dfa54cSAmit Daniel Kachhap 102073b5b1d7SSachin Kamat if (!data || !pdev->dev.of_node) 1021cebe7373SAmit Daniel Kachhap return -ENODEV; 102259dfa54cSAmit Daniel Kachhap 1023498d22f6SAmit Daniel Kachhap /* 1024498d22f6SAmit Daniel Kachhap * Try enabling the regulator if found 1025498d22f6SAmit Daniel Kachhap * TODO: Add regulator as an SOC feature, so that regulator enable 1026498d22f6SAmit Daniel Kachhap * is a compulsory call. 1027498d22f6SAmit Daniel Kachhap */ 1028498d22f6SAmit Daniel Kachhap data->regulator = devm_regulator_get(&pdev->dev, "vtmu"); 1029498d22f6SAmit Daniel Kachhap if (!IS_ERR(data->regulator)) { 1030498d22f6SAmit Daniel Kachhap ret = regulator_enable(data->regulator); 1031498d22f6SAmit Daniel Kachhap if (ret) { 1032498d22f6SAmit Daniel Kachhap dev_err(&pdev->dev, "failed to enable vtmu\n"); 1033498d22f6SAmit Daniel Kachhap return ret; 1034498d22f6SAmit Daniel Kachhap } 1035498d22f6SAmit Daniel Kachhap } else { 1036498d22f6SAmit Daniel Kachhap dev_info(&pdev->dev, "Regulator node (vtmu) not found\n"); 1037498d22f6SAmit Daniel Kachhap } 1038498d22f6SAmit Daniel Kachhap 1039cebe7373SAmit Daniel Kachhap data->id = of_alias_get_id(pdev->dev.of_node, "tmuctrl"); 1040cebe7373SAmit Daniel Kachhap if (data->id < 0) 1041cebe7373SAmit Daniel Kachhap data->id = 0; 1042cebe7373SAmit Daniel Kachhap 1043cebe7373SAmit Daniel Kachhap data->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); 1044cebe7373SAmit Daniel Kachhap if (data->irq <= 0) { 1045cebe7373SAmit Daniel Kachhap dev_err(&pdev->dev, "failed to get IRQ\n"); 1046cebe7373SAmit Daniel Kachhap return -ENODEV; 1047cebe7373SAmit Daniel Kachhap } 1048cebe7373SAmit Daniel Kachhap 1049cebe7373SAmit Daniel Kachhap if (of_address_to_resource(pdev->dev.of_node, 0, &res)) { 1050cebe7373SAmit Daniel Kachhap dev_err(&pdev->dev, "failed to get Resource 0\n"); 1051cebe7373SAmit Daniel Kachhap return -ENODEV; 1052cebe7373SAmit Daniel Kachhap } 1053cebe7373SAmit Daniel Kachhap 1054cebe7373SAmit Daniel Kachhap data->base = devm_ioremap(&pdev->dev, res.start, resource_size(&res)); 1055cebe7373SAmit Daniel Kachhap if (!data->base) { 1056cebe7373SAmit Daniel Kachhap dev_err(&pdev->dev, "Failed to ioremap memory\n"); 1057cebe7373SAmit Daniel Kachhap return -EADDRNOTAVAIL; 1058cebe7373SAmit Daniel Kachhap } 1059cebe7373SAmit Daniel Kachhap 10603b6a1a80SLukasz Majewski pdata = devm_kzalloc(&pdev->dev, 10613b6a1a80SLukasz Majewski sizeof(struct exynos_tmu_platform_data), 10623b6a1a80SLukasz Majewski GFP_KERNEL); 10633b6a1a80SLukasz Majewski if (!pdata) 10643b6a1a80SLukasz Majewski return -ENOMEM; 106556adb9efSBartlomiej Zolnierkiewicz 10663b6a1a80SLukasz Majewski exynos_of_sensor_conf(pdev->dev.of_node, pdata); 1067cebe7373SAmit Daniel Kachhap data->pdata = pdata; 10683b6a1a80SLukasz Majewski data->soc = exynos_of_get_soc_type(pdev->dev.of_node); 106956adb9efSBartlomiej Zolnierkiewicz 107056adb9efSBartlomiej Zolnierkiewicz switch (data->soc) { 107156adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS4210: 107256adb9efSBartlomiej Zolnierkiewicz data->tmu_initialize = exynos4210_tmu_initialize; 107356adb9efSBartlomiej Zolnierkiewicz data->tmu_control = exynos4210_tmu_control; 107456adb9efSBartlomiej Zolnierkiewicz data->tmu_read = exynos4210_tmu_read; 107556adb9efSBartlomiej Zolnierkiewicz data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; 107656adb9efSBartlomiej Zolnierkiewicz break; 107756adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS3250: 107856adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS4412: 107956adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS5250: 108056adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS5260: 108156adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS5420: 108256adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS5420_TRIMINFO: 108356adb9efSBartlomiej Zolnierkiewicz data->tmu_initialize = exynos4412_tmu_initialize; 108456adb9efSBartlomiej Zolnierkiewicz data->tmu_control = exynos4210_tmu_control; 108556adb9efSBartlomiej Zolnierkiewicz data->tmu_read = exynos4412_tmu_read; 108656adb9efSBartlomiej Zolnierkiewicz data->tmu_set_emulation = exynos4412_tmu_set_emulation; 108756adb9efSBartlomiej Zolnierkiewicz data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; 108856adb9efSBartlomiej Zolnierkiewicz break; 108956adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS5440: 109056adb9efSBartlomiej Zolnierkiewicz data->tmu_initialize = exynos5440_tmu_initialize; 109156adb9efSBartlomiej Zolnierkiewicz data->tmu_control = exynos5440_tmu_control; 109256adb9efSBartlomiej Zolnierkiewicz data->tmu_read = exynos5440_tmu_read; 109356adb9efSBartlomiej Zolnierkiewicz data->tmu_set_emulation = exynos5440_tmu_set_emulation; 109456adb9efSBartlomiej Zolnierkiewicz data->tmu_clear_irqs = exynos5440_tmu_clear_irqs; 109556adb9efSBartlomiej Zolnierkiewicz break; 10966c247393SAbhilash Kesavan case SOC_ARCH_EXYNOS7: 10976c247393SAbhilash Kesavan data->tmu_initialize = exynos7_tmu_initialize; 10986c247393SAbhilash Kesavan data->tmu_control = exynos7_tmu_control; 10996c247393SAbhilash Kesavan data->tmu_read = exynos7_tmu_read; 11006c247393SAbhilash Kesavan data->tmu_set_emulation = exynos4412_tmu_set_emulation; 11016c247393SAbhilash Kesavan data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; 11026c247393SAbhilash Kesavan break; 110356adb9efSBartlomiej Zolnierkiewicz default: 110456adb9efSBartlomiej Zolnierkiewicz dev_err(&pdev->dev, "Platform not supported\n"); 110556adb9efSBartlomiej Zolnierkiewicz return -EINVAL; 110656adb9efSBartlomiej Zolnierkiewicz } 110756adb9efSBartlomiej Zolnierkiewicz 1108d9b6ee14SAmit Daniel Kachhap /* 1109d9b6ee14SAmit Daniel Kachhap * Check if the TMU shares some registers and then try to map the 1110d9b6ee14SAmit Daniel Kachhap * memory of common registers. 1111d9b6ee14SAmit Daniel Kachhap */ 111256adb9efSBartlomiej Zolnierkiewicz if (data->soc != SOC_ARCH_EXYNOS5420_TRIMINFO && 111356adb9efSBartlomiej Zolnierkiewicz data->soc != SOC_ARCH_EXYNOS5440) 1114d9b6ee14SAmit Daniel Kachhap return 0; 1115d9b6ee14SAmit Daniel Kachhap 1116d9b6ee14SAmit Daniel Kachhap if (of_address_to_resource(pdev->dev.of_node, 1, &res)) { 1117d9b6ee14SAmit Daniel Kachhap dev_err(&pdev->dev, "failed to get Resource 1\n"); 1118d9b6ee14SAmit Daniel Kachhap return -ENODEV; 1119d9b6ee14SAmit Daniel Kachhap } 1120d9b6ee14SAmit Daniel Kachhap 11219025d563SNaveen Krishna Chatradhi data->base_second = devm_ioremap(&pdev->dev, res.start, 1122d9b6ee14SAmit Daniel Kachhap resource_size(&res)); 11239025d563SNaveen Krishna Chatradhi if (!data->base_second) { 1124d9b6ee14SAmit Daniel Kachhap dev_err(&pdev->dev, "Failed to ioremap memory\n"); 1125d9b6ee14SAmit Daniel Kachhap return -ENOMEM; 1126d9b6ee14SAmit Daniel Kachhap } 1127cebe7373SAmit Daniel Kachhap 1128cebe7373SAmit Daniel Kachhap return 0; 1129cebe7373SAmit Daniel Kachhap } 1130cebe7373SAmit Daniel Kachhap 11313b6a1a80SLukasz Majewski static struct thermal_zone_of_device_ops exynos_sensor_ops = { 11323b6a1a80SLukasz Majewski .get_temp = exynos_get_temp, 11333b6a1a80SLukasz Majewski .set_emul_temp = exynos_tmu_set_emulation, 11343b6a1a80SLukasz Majewski }; 11353b6a1a80SLukasz Majewski 1136cebe7373SAmit Daniel Kachhap static int exynos_tmu_probe(struct platform_device *pdev) 1137cebe7373SAmit Daniel Kachhap { 1138cebe7373SAmit Daniel Kachhap struct exynos_tmu_platform_data *pdata; 11393b6a1a80SLukasz Majewski struct exynos_tmu_data *data; 11403b6a1a80SLukasz Majewski int ret; 1141cebe7373SAmit Daniel Kachhap 114259dfa54cSAmit Daniel Kachhap data = devm_kzalloc(&pdev->dev, sizeof(struct exynos_tmu_data), 114359dfa54cSAmit Daniel Kachhap GFP_KERNEL); 11442a9675b3SJingoo Han if (!data) 114559dfa54cSAmit Daniel Kachhap return -ENOMEM; 114659dfa54cSAmit Daniel Kachhap 1147cebe7373SAmit Daniel Kachhap platform_set_drvdata(pdev, data); 1148cebe7373SAmit Daniel Kachhap mutex_init(&data->lock); 1149cebe7373SAmit Daniel Kachhap 11503b6a1a80SLukasz Majewski data->tzd = thermal_zone_of_sensor_register(&pdev->dev, 0, data, 11513b6a1a80SLukasz Majewski &exynos_sensor_ops); 11523b6a1a80SLukasz Majewski if (IS_ERR(data->tzd)) { 11533b6a1a80SLukasz Majewski pr_err("thermal: tz: %p ERROR\n", data->tzd); 11543b6a1a80SLukasz Majewski return PTR_ERR(data->tzd); 11553b6a1a80SLukasz Majewski } 1156cebe7373SAmit Daniel Kachhap ret = exynos_map_dt_data(pdev); 1157cebe7373SAmit Daniel Kachhap if (ret) 11583b6a1a80SLukasz Majewski goto err_sensor; 1159cebe7373SAmit Daniel Kachhap 1160cebe7373SAmit Daniel Kachhap pdata = data->pdata; 116159dfa54cSAmit Daniel Kachhap 116259dfa54cSAmit Daniel Kachhap INIT_WORK(&data->irq_work, exynos_tmu_work); 116359dfa54cSAmit Daniel Kachhap 116459dfa54cSAmit Daniel Kachhap data->clk = devm_clk_get(&pdev->dev, "tmu_apbif"); 116559dfa54cSAmit Daniel Kachhap if (IS_ERR(data->clk)) { 116659dfa54cSAmit Daniel Kachhap dev_err(&pdev->dev, "Failed to get clock\n"); 11673b6a1a80SLukasz Majewski ret = PTR_ERR(data->clk); 11683b6a1a80SLukasz Majewski goto err_sensor; 116959dfa54cSAmit Daniel Kachhap } 117059dfa54cSAmit Daniel Kachhap 117114a11dc7SNaveen Krishna Chatradhi data->clk_sec = devm_clk_get(&pdev->dev, "tmu_triminfo_apbif"); 117214a11dc7SNaveen Krishna Chatradhi if (IS_ERR(data->clk_sec)) { 117314a11dc7SNaveen Krishna Chatradhi if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) { 117414a11dc7SNaveen Krishna Chatradhi dev_err(&pdev->dev, "Failed to get triminfo clock\n"); 11753b6a1a80SLukasz Majewski ret = PTR_ERR(data->clk_sec); 11763b6a1a80SLukasz Majewski goto err_sensor; 117714a11dc7SNaveen Krishna Chatradhi } 117814a11dc7SNaveen Krishna Chatradhi } else { 117914a11dc7SNaveen Krishna Chatradhi ret = clk_prepare(data->clk_sec); 118014a11dc7SNaveen Krishna Chatradhi if (ret) { 118114a11dc7SNaveen Krishna Chatradhi dev_err(&pdev->dev, "Failed to get clock\n"); 11823b6a1a80SLukasz Majewski goto err_sensor; 118314a11dc7SNaveen Krishna Chatradhi } 118414a11dc7SNaveen Krishna Chatradhi } 118514a11dc7SNaveen Krishna Chatradhi 118614a11dc7SNaveen Krishna Chatradhi ret = clk_prepare(data->clk); 118714a11dc7SNaveen Krishna Chatradhi if (ret) { 118814a11dc7SNaveen Krishna Chatradhi dev_err(&pdev->dev, "Failed to get clock\n"); 118914a11dc7SNaveen Krishna Chatradhi goto err_clk_sec; 119014a11dc7SNaveen Krishna Chatradhi } 119159dfa54cSAmit Daniel Kachhap 11926c247393SAbhilash Kesavan if (data->soc == SOC_ARCH_EXYNOS7) { 11936c247393SAbhilash Kesavan data->sclk = devm_clk_get(&pdev->dev, "tmu_sclk"); 11946c247393SAbhilash Kesavan if (IS_ERR(data->sclk)) { 11956c247393SAbhilash Kesavan dev_err(&pdev->dev, "Failed to get sclk\n"); 11966c247393SAbhilash Kesavan goto err_clk; 11976c247393SAbhilash Kesavan } else { 11986c247393SAbhilash Kesavan ret = clk_prepare_enable(data->sclk); 11996c247393SAbhilash Kesavan if (ret) { 12006c247393SAbhilash Kesavan dev_err(&pdev->dev, "Failed to enable sclk\n"); 12016c247393SAbhilash Kesavan goto err_clk; 12026c247393SAbhilash Kesavan } 12036c247393SAbhilash Kesavan } 12046c247393SAbhilash Kesavan } 12056c247393SAbhilash Kesavan 120659dfa54cSAmit Daniel Kachhap ret = exynos_tmu_initialize(pdev); 120759dfa54cSAmit Daniel Kachhap if (ret) { 120859dfa54cSAmit Daniel Kachhap dev_err(&pdev->dev, "Failed to initialize TMU\n"); 12096c247393SAbhilash Kesavan goto err_sclk; 121059dfa54cSAmit Daniel Kachhap } 121159dfa54cSAmit Daniel Kachhap 1212cebe7373SAmit Daniel Kachhap ret = devm_request_irq(&pdev->dev, data->irq, exynos_tmu_irq, 1213cebe7373SAmit Daniel Kachhap IRQF_TRIGGER_RISING | IRQF_SHARED, dev_name(&pdev->dev), data); 1214cebe7373SAmit Daniel Kachhap if (ret) { 1215cebe7373SAmit Daniel Kachhap dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq); 12166c247393SAbhilash Kesavan goto err_sclk; 1217cebe7373SAmit Daniel Kachhap } 121859dfa54cSAmit Daniel Kachhap 12193b6a1a80SLukasz Majewski exynos_tmu_control(pdev, true); 122059dfa54cSAmit Daniel Kachhap return 0; 12216c247393SAbhilash Kesavan err_sclk: 12226c247393SAbhilash Kesavan clk_disable_unprepare(data->sclk); 122359dfa54cSAmit Daniel Kachhap err_clk: 122459dfa54cSAmit Daniel Kachhap clk_unprepare(data->clk); 122514a11dc7SNaveen Krishna Chatradhi err_clk_sec: 122614a11dc7SNaveen Krishna Chatradhi if (!IS_ERR(data->clk_sec)) 122714a11dc7SNaveen Krishna Chatradhi clk_unprepare(data->clk_sec); 12283b6a1a80SLukasz Majewski err_sensor: 12293b6a1a80SLukasz Majewski thermal_zone_of_sensor_unregister(&pdev->dev, data->tzd); 12303b6a1a80SLukasz Majewski 123159dfa54cSAmit Daniel Kachhap return ret; 123259dfa54cSAmit Daniel Kachhap } 123359dfa54cSAmit Daniel Kachhap 123459dfa54cSAmit Daniel Kachhap static int exynos_tmu_remove(struct platform_device *pdev) 123559dfa54cSAmit Daniel Kachhap { 123659dfa54cSAmit Daniel Kachhap struct exynos_tmu_data *data = platform_get_drvdata(pdev); 12373b6a1a80SLukasz Majewski struct thermal_zone_device *tzd = data->tzd; 123859dfa54cSAmit Daniel Kachhap 12393b6a1a80SLukasz Majewski thermal_zone_of_sensor_unregister(&pdev->dev, tzd); 12404215688eSBartlomiej Zolnierkiewicz exynos_tmu_control(pdev, false); 12414215688eSBartlomiej Zolnierkiewicz 12426c247393SAbhilash Kesavan clk_disable_unprepare(data->sclk); 124359dfa54cSAmit Daniel Kachhap clk_unprepare(data->clk); 124414a11dc7SNaveen Krishna Chatradhi if (!IS_ERR(data->clk_sec)) 124514a11dc7SNaveen Krishna Chatradhi clk_unprepare(data->clk_sec); 124659dfa54cSAmit Daniel Kachhap 1247498d22f6SAmit Daniel Kachhap if (!IS_ERR(data->regulator)) 1248498d22f6SAmit Daniel Kachhap regulator_disable(data->regulator); 1249498d22f6SAmit Daniel Kachhap 125059dfa54cSAmit Daniel Kachhap return 0; 125159dfa54cSAmit Daniel Kachhap } 125259dfa54cSAmit Daniel Kachhap 125359dfa54cSAmit Daniel Kachhap #ifdef CONFIG_PM_SLEEP 125459dfa54cSAmit Daniel Kachhap static int exynos_tmu_suspend(struct device *dev) 125559dfa54cSAmit Daniel Kachhap { 125659dfa54cSAmit Daniel Kachhap exynos_tmu_control(to_platform_device(dev), false); 125759dfa54cSAmit Daniel Kachhap 125859dfa54cSAmit Daniel Kachhap return 0; 125959dfa54cSAmit Daniel Kachhap } 126059dfa54cSAmit Daniel Kachhap 126159dfa54cSAmit Daniel Kachhap static int exynos_tmu_resume(struct device *dev) 126259dfa54cSAmit Daniel Kachhap { 126359dfa54cSAmit Daniel Kachhap struct platform_device *pdev = to_platform_device(dev); 126459dfa54cSAmit Daniel Kachhap 126559dfa54cSAmit Daniel Kachhap exynos_tmu_initialize(pdev); 126659dfa54cSAmit Daniel Kachhap exynos_tmu_control(pdev, true); 126759dfa54cSAmit Daniel Kachhap 126859dfa54cSAmit Daniel Kachhap return 0; 126959dfa54cSAmit Daniel Kachhap } 127059dfa54cSAmit Daniel Kachhap 127159dfa54cSAmit Daniel Kachhap static SIMPLE_DEV_PM_OPS(exynos_tmu_pm, 127259dfa54cSAmit Daniel Kachhap exynos_tmu_suspend, exynos_tmu_resume); 127359dfa54cSAmit Daniel Kachhap #define EXYNOS_TMU_PM (&exynos_tmu_pm) 127459dfa54cSAmit Daniel Kachhap #else 127559dfa54cSAmit Daniel Kachhap #define EXYNOS_TMU_PM NULL 127659dfa54cSAmit Daniel Kachhap #endif 127759dfa54cSAmit Daniel Kachhap 127859dfa54cSAmit Daniel Kachhap static struct platform_driver exynos_tmu_driver = { 127959dfa54cSAmit Daniel Kachhap .driver = { 128059dfa54cSAmit Daniel Kachhap .name = "exynos-tmu", 128159dfa54cSAmit Daniel Kachhap .pm = EXYNOS_TMU_PM, 128273b5b1d7SSachin Kamat .of_match_table = exynos_tmu_match, 128359dfa54cSAmit Daniel Kachhap }, 128459dfa54cSAmit Daniel Kachhap .probe = exynos_tmu_probe, 128559dfa54cSAmit Daniel Kachhap .remove = exynos_tmu_remove, 128659dfa54cSAmit Daniel Kachhap }; 128759dfa54cSAmit Daniel Kachhap 128859dfa54cSAmit Daniel Kachhap module_platform_driver(exynos_tmu_driver); 128959dfa54cSAmit Daniel Kachhap 129059dfa54cSAmit Daniel Kachhap MODULE_DESCRIPTION("EXYNOS TMU Driver"); 129159dfa54cSAmit Daniel Kachhap MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>"); 129259dfa54cSAmit Daniel Kachhap MODULE_LICENSE("GPL"); 129359dfa54cSAmit Daniel Kachhap MODULE_ALIAS("platform:exynos-tmu"); 1294