11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 259dfa54cSAmit Daniel Kachhap /* 3ca07ee4eSKrzysztof Kozlowski * exynos_tmu.c - Samsung Exynos TMU (Thermal Management Unit) 459dfa54cSAmit Daniel Kachhap * 53b6a1a80SLukasz Majewski * Copyright (C) 2014 Samsung Electronics 63b6a1a80SLukasz Majewski * Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> 73b6a1a80SLukasz Majewski * Lukasz Majewski <l.majewski@samsung.com> 83b6a1a80SLukasz Majewski * 959dfa54cSAmit Daniel Kachhap * Copyright (C) 2011 Samsung Electronics 1059dfa54cSAmit Daniel Kachhap * Donggeun Kim <dg77.kim@samsung.com> 1159dfa54cSAmit Daniel Kachhap * Amit Daniel Kachhap <amit.kachhap@linaro.org> 1259dfa54cSAmit Daniel Kachhap */ 1359dfa54cSAmit Daniel Kachhap 1459dfa54cSAmit Daniel Kachhap #include <linux/clk.h> 1559dfa54cSAmit Daniel Kachhap #include <linux/io.h> 1659dfa54cSAmit Daniel Kachhap #include <linux/interrupt.h> 1759dfa54cSAmit Daniel Kachhap #include <linux/module.h> 18f6a756e8SRob Herring #include <linux/of.h> 19cebe7373SAmit Daniel Kachhap #include <linux/of_address.h> 20cebe7373SAmit Daniel Kachhap #include <linux/of_irq.h> 2159dfa54cSAmit Daniel Kachhap #include <linux/platform_device.h> 22498d22f6SAmit Daniel Kachhap #include <linux/regulator/consumer.h> 239272d2d4SDaniel Lezcano #include <linux/thermal.h> 2459dfa54cSAmit Daniel Kachhap 257efd18a2SBartlomiej Zolnierkiewicz #include <dt-bindings/thermal/thermal_exynos.h> 267efd18a2SBartlomiej Zolnierkiewicz 272845f6ecSBartlomiej Zolnierkiewicz /* Exynos generic registers */ 282845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_REG_TRIMINFO 0x0 292845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_REG_CONTROL 0x20 302845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_REG_STATUS 0x28 312845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_REG_CURRENT_TEMP 0x40 322845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_REG_INTEN 0x70 332845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_REG_INTSTAT 0x74 342845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_REG_INTCLEAR 0x78 352845f6ecSBartlomiej Zolnierkiewicz 362845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_TEMP_MASK 0xff 372845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_REF_VOLTAGE_SHIFT 24 382845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_REF_VOLTAGE_MASK 0x1f 392845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_BUF_SLOPE_SEL_MASK 0xf 402845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT 8 412845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_CORE_EN_SHIFT 0 422845f6ecSBartlomiej Zolnierkiewicz 432845f6ecSBartlomiej Zolnierkiewicz /* Exynos3250 specific registers */ 442845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_TRIMINFO_CON1 0x10 452845f6ecSBartlomiej Zolnierkiewicz 462845f6ecSBartlomiej Zolnierkiewicz /* Exynos4210 specific registers */ 472845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS4210_TMU_REG_THRESHOLD_TEMP 0x44 482845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS4210_TMU_REG_TRIG_LEVEL0 0x50 492845f6ecSBartlomiej Zolnierkiewicz 502845f6ecSBartlomiej Zolnierkiewicz /* Exynos5250, Exynos4412, Exynos3250 specific registers */ 512845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_TRIMINFO_CON2 0x14 522845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_THD_TEMP_RISE 0x50 532845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_THD_TEMP_FALL 0x54 542845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_EMUL_CON 0x80 552845f6ecSBartlomiej Zolnierkiewicz 562845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TRIMINFO_RELOAD_ENABLE 1 572845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TRIMINFO_25_SHIFT 0 582845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TRIMINFO_85_SHIFT 8 592845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_TRIP_MODE_SHIFT 13 602845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_TRIP_MODE_MASK 0x7 612845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_THERM_TRIP_EN_SHIFT 12 622845f6ecSBartlomiej Zolnierkiewicz 632845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_INTEN_RISE0_SHIFT 0 642845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_TMU_INTEN_FALL0_SHIFT 16 652845f6ecSBartlomiej Zolnierkiewicz 662845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_EMUL_TIME 0x57F0 672845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_EMUL_TIME_MASK 0xffff 682845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_EMUL_TIME_SHIFT 16 692845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_EMUL_DATA_SHIFT 8 702845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_EMUL_DATA_MASK 0xFF 712845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS_EMUL_ENABLE 0x1 722845f6ecSBartlomiej Zolnierkiewicz 732845f6ecSBartlomiej Zolnierkiewicz /* Exynos5260 specific */ 742845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5260_TMU_REG_INTEN 0xC0 752845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5260_TMU_REG_INTSTAT 0xC4 762845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5260_TMU_REG_INTCLEAR 0xC8 772845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS5260_EMUL_CON 0x100 782845f6ecSBartlomiej Zolnierkiewicz 792845f6ecSBartlomiej Zolnierkiewicz /* Exynos4412 specific */ 802845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS4412_MUX_ADDR_VALUE 6 812845f6ecSBartlomiej Zolnierkiewicz #define EXYNOS4412_MUX_ADDR_SHIFT 20 822845f6ecSBartlomiej Zolnierkiewicz 83488c7455SChanwoo Choi /* Exynos5433 specific registers */ 84488c7455SChanwoo Choi #define EXYNOS5433_THD_TEMP_RISE3_0 0x050 85488c7455SChanwoo Choi #define EXYNOS5433_THD_TEMP_RISE7_4 0x054 86488c7455SChanwoo Choi #define EXYNOS5433_THD_TEMP_FALL3_0 0x060 87488c7455SChanwoo Choi #define EXYNOS5433_THD_TEMP_FALL7_4 0x064 88488c7455SChanwoo Choi #define EXYNOS5433_TMU_REG_INTEN 0x0c0 89488c7455SChanwoo Choi #define EXYNOS5433_TMU_REG_INTPEND 0x0c8 90488c7455SChanwoo Choi #define EXYNOS5433_TMU_EMUL_CON 0x110 91488c7455SChanwoo Choi #define EXYNOS5433_TMU_PD_DET_EN 0x130 92488c7455SChanwoo Choi 93488c7455SChanwoo Choi #define EXYNOS5433_TRIMINFO_SENSOR_ID_SHIFT 16 94488c7455SChanwoo Choi #define EXYNOS5433_TRIMINFO_CALIB_SEL_SHIFT 23 95488c7455SChanwoo Choi #define EXYNOS5433_TRIMINFO_SENSOR_ID_MASK \ 96488c7455SChanwoo Choi (0xf << EXYNOS5433_TRIMINFO_SENSOR_ID_SHIFT) 97488c7455SChanwoo Choi #define EXYNOS5433_TRIMINFO_CALIB_SEL_MASK BIT(23) 98488c7455SChanwoo Choi 99488c7455SChanwoo Choi #define EXYNOS5433_TRIMINFO_ONE_POINT_TRIMMING 0 100488c7455SChanwoo Choi #define EXYNOS5433_TRIMINFO_TWO_POINT_TRIMMING 1 101488c7455SChanwoo Choi 102488c7455SChanwoo Choi #define EXYNOS5433_PD_DET_EN 1 103488c7455SChanwoo Choi 10461020d18SBartlomiej Zolnierkiewicz #define EXYNOS5433_G3D_BASE 0x10070000 10559dfa54cSAmit Daniel Kachhap 1066c247393SAbhilash Kesavan /* Exynos7 specific registers */ 1076c247393SAbhilash Kesavan #define EXYNOS7_THD_TEMP_RISE7_6 0x50 1086c247393SAbhilash Kesavan #define EXYNOS7_THD_TEMP_FALL7_6 0x60 1096c247393SAbhilash Kesavan #define EXYNOS7_TMU_REG_INTEN 0x110 1106c247393SAbhilash Kesavan #define EXYNOS7_TMU_REG_INTPEND 0x118 1116c247393SAbhilash Kesavan #define EXYNOS7_TMU_REG_EMUL_CON 0x160 1126c247393SAbhilash Kesavan 1136c247393SAbhilash Kesavan #define EXYNOS7_TMU_TEMP_MASK 0x1ff 1146c247393SAbhilash Kesavan #define EXYNOS7_PD_DET_EN_SHIFT 23 1156c247393SAbhilash Kesavan #define EXYNOS7_TMU_INTEN_RISE0_SHIFT 0 1166c247393SAbhilash Kesavan #define EXYNOS7_EMUL_DATA_SHIFT 7 1176c247393SAbhilash Kesavan #define EXYNOS7_EMUL_DATA_MASK 0x1ff 1186c247393SAbhilash Kesavan 119718b4ca1SBartlomiej Zolnierkiewicz #define EXYNOS_FIRST_POINT_TRIM 25 120718b4ca1SBartlomiej Zolnierkiewicz #define EXYNOS_SECOND_POINT_TRIM 85 121718b4ca1SBartlomiej Zolnierkiewicz 12209d29426SBartlomiej Zolnierkiewicz #define EXYNOS_NOISE_CANCEL_MODE 4 12309d29426SBartlomiej Zolnierkiewicz 1243b6a1a80SLukasz Majewski #define MCELSIUS 1000 1257efd18a2SBartlomiej Zolnierkiewicz 1267efd18a2SBartlomiej Zolnierkiewicz enum soc_type { 1277efd18a2SBartlomiej Zolnierkiewicz SOC_ARCH_EXYNOS3250 = 1, 1287efd18a2SBartlomiej Zolnierkiewicz SOC_ARCH_EXYNOS4210, 1297efd18a2SBartlomiej Zolnierkiewicz SOC_ARCH_EXYNOS4412, 1307efd18a2SBartlomiej Zolnierkiewicz SOC_ARCH_EXYNOS5250, 1317efd18a2SBartlomiej Zolnierkiewicz SOC_ARCH_EXYNOS5260, 1327efd18a2SBartlomiej Zolnierkiewicz SOC_ARCH_EXYNOS5420, 1337efd18a2SBartlomiej Zolnierkiewicz SOC_ARCH_EXYNOS5420_TRIMINFO, 1347efd18a2SBartlomiej Zolnierkiewicz SOC_ARCH_EXYNOS5433, 1357efd18a2SBartlomiej Zolnierkiewicz SOC_ARCH_EXYNOS7, 1367efd18a2SBartlomiej Zolnierkiewicz }; 1377efd18a2SBartlomiej Zolnierkiewicz 138cebe7373SAmit Daniel Kachhap /** 139cebe7373SAmit Daniel Kachhap * struct exynos_tmu_data : A structure to hold the private data of the TMU 1409625e9e6SAmit Kucheria * driver 141cebe7373SAmit Daniel Kachhap * @base: base address of the single instance of the TMU controller. 1429025d563SNaveen Krishna Chatradhi * @base_second: base address of the common registers of the TMU controller. 143cebe7373SAmit Daniel Kachhap * @irq: irq number of the TMU controller. 144cebe7373SAmit Daniel Kachhap * @soc: id of the SOC type. 145cebe7373SAmit Daniel Kachhap * @lock: lock to implement synchronization. 146cebe7373SAmit Daniel Kachhap * @clk: pointer to the clock structure. 14714a11dc7SNaveen Krishna Chatradhi * @clk_sec: pointer to the clock structure for accessing the base_second. 1486c247393SAbhilash Kesavan * @sclk: pointer to the clock structure for accessing the tmu special clk. 149199b3e3cSBartlomiej Zolnierkiewicz * @cal_type: calibration type for temperature 150e3ed3649SBartlomiej Zolnierkiewicz * @efuse_value: SoC defined fuse value 151e3ed3649SBartlomiej Zolnierkiewicz * @min_efuse_value: minimum valid trimming data 152e3ed3649SBartlomiej Zolnierkiewicz * @max_efuse_value: maximum valid trimming data 153cebe7373SAmit Daniel Kachhap * @temp_error1: fused value of the first point trim. 154cebe7373SAmit Daniel Kachhap * @temp_error2: fused value of the second point trim. 155fccfe099SBartlomiej Zolnierkiewicz * @gain: gain of amplifier in the positive-TC generator block 156fccfe099SBartlomiej Zolnierkiewicz * 0 < gain <= 15 15761020d18SBartlomiej Zolnierkiewicz * @reference_voltage: reference voltage of amplifier 15861020d18SBartlomiej Zolnierkiewicz * in the positive-TC generator block 15961020d18SBartlomiej Zolnierkiewicz * 0 < reference_voltage <= 31 1609625e9e6SAmit Kucheria * @tzd: pointer to thermal_zone_device structure 1613a3a5f15SKrzysztof Kozlowski * @ntrip: number of supported trip points. 16288fc6f73SMarek Szyprowski * @enabled: current status of TMU device 1639625e9e6SAmit Kucheria * @tmu_set_trip_temp: SoC specific method to set trip (rising threshold) 1649625e9e6SAmit Kucheria * @tmu_set_trip_hyst: SoC specific to set hysteresis (falling threshold) 16572d1100bSBartlomiej Zolnierkiewicz * @tmu_initialize: SoC specific TMU initialization method 16637f9034fSBartlomiej Zolnierkiewicz * @tmu_control: SoC specific TMU control method 167b79985caSBartlomiej Zolnierkiewicz * @tmu_read: SoC specific TMU temperature read method 168285d994aSBartlomiej Zolnierkiewicz * @tmu_set_emulation: SoC specific TMU emulation setting method 169a7331f72SBartlomiej Zolnierkiewicz * @tmu_clear_irqs: SoC specific TMU interrupts clearing method 170cebe7373SAmit Daniel Kachhap */ 17159dfa54cSAmit Daniel Kachhap struct exynos_tmu_data { 17259dfa54cSAmit Daniel Kachhap void __iomem *base; 1739025d563SNaveen Krishna Chatradhi void __iomem *base_second; 17459dfa54cSAmit Daniel Kachhap int irq; 17559dfa54cSAmit Daniel Kachhap enum soc_type soc; 17659dfa54cSAmit Daniel Kachhap struct mutex lock; 1776c247393SAbhilash Kesavan struct clk *clk, *clk_sec, *sclk; 178199b3e3cSBartlomiej Zolnierkiewicz u32 cal_type; 179e3ed3649SBartlomiej Zolnierkiewicz u32 efuse_value; 180e3ed3649SBartlomiej Zolnierkiewicz u32 min_efuse_value; 181e3ed3649SBartlomiej Zolnierkiewicz u32 max_efuse_value; 1826c247393SAbhilash Kesavan u16 temp_error1, temp_error2; 183fccfe099SBartlomiej Zolnierkiewicz u8 gain; 18461020d18SBartlomiej Zolnierkiewicz u8 reference_voltage; 1853b6a1a80SLukasz Majewski struct thermal_zone_device *tzd; 1863a3a5f15SKrzysztof Kozlowski unsigned int ntrip; 18788fc6f73SMarek Szyprowski bool enabled; 1883b6a1a80SLukasz Majewski 189c8f8f768SBartlomiej Zolnierkiewicz void (*tmu_set_trip_temp)(struct exynos_tmu_data *data, int trip, 190c8f8f768SBartlomiej Zolnierkiewicz u8 temp); 191c8f8f768SBartlomiej Zolnierkiewicz void (*tmu_set_trip_hyst)(struct exynos_tmu_data *data, int trip, 192c8f8f768SBartlomiej Zolnierkiewicz u8 temp, u8 hyst); 193c35268f5SBartlomiej Zolnierkiewicz void (*tmu_initialize)(struct platform_device *pdev); 19437f9034fSBartlomiej Zolnierkiewicz void (*tmu_control)(struct platform_device *pdev, bool on); 195b79985caSBartlomiej Zolnierkiewicz int (*tmu_read)(struct exynos_tmu_data *data); 19617e8351aSSascha Hauer void (*tmu_set_emulation)(struct exynos_tmu_data *data, int temp); 197a7331f72SBartlomiej Zolnierkiewicz void (*tmu_clear_irqs)(struct exynos_tmu_data *data); 19859dfa54cSAmit Daniel Kachhap }; 19959dfa54cSAmit Daniel Kachhap 20059dfa54cSAmit Daniel Kachhap /* 20159dfa54cSAmit Daniel Kachhap * TMU treats temperature as a mapped temperature code. 20259dfa54cSAmit Daniel Kachhap * The temperature is converted differently depending on the calibration type. 20359dfa54cSAmit Daniel Kachhap */ 20459dfa54cSAmit Daniel Kachhap static int temp_to_code(struct exynos_tmu_data *data, u8 temp) 20559dfa54cSAmit Daniel Kachhap { 206199b3e3cSBartlomiej Zolnierkiewicz if (data->cal_type == TYPE_ONE_POINT_TRIMMING) 207718b4ca1SBartlomiej Zolnierkiewicz return temp + data->temp_error1 - EXYNOS_FIRST_POINT_TRIM; 20859dfa54cSAmit Daniel Kachhap 209718b4ca1SBartlomiej Zolnierkiewicz return (temp - EXYNOS_FIRST_POINT_TRIM) * 21059dfa54cSAmit Daniel Kachhap (data->temp_error2 - data->temp_error1) / 211718b4ca1SBartlomiej Zolnierkiewicz (EXYNOS_SECOND_POINT_TRIM - EXYNOS_FIRST_POINT_TRIM) + 212bb34b4c8SAmit Daniel Kachhap data->temp_error1; 21359dfa54cSAmit Daniel Kachhap } 21459dfa54cSAmit Daniel Kachhap 21559dfa54cSAmit Daniel Kachhap /* 21659dfa54cSAmit Daniel Kachhap * Calculate a temperature value from a temperature code. 21759dfa54cSAmit Daniel Kachhap * The unit of the temperature is degree Celsius. 21859dfa54cSAmit Daniel Kachhap */ 2196c247393SAbhilash Kesavan static int code_to_temp(struct exynos_tmu_data *data, u16 temp_code) 22059dfa54cSAmit Daniel Kachhap { 221199b3e3cSBartlomiej Zolnierkiewicz if (data->cal_type == TYPE_ONE_POINT_TRIMMING) 222718b4ca1SBartlomiej Zolnierkiewicz return temp_code - data->temp_error1 + EXYNOS_FIRST_POINT_TRIM; 22359dfa54cSAmit Daniel Kachhap 2249c933b1bSBartlomiej Zolnierkiewicz return (temp_code - data->temp_error1) * 225718b4ca1SBartlomiej Zolnierkiewicz (EXYNOS_SECOND_POINT_TRIM - EXYNOS_FIRST_POINT_TRIM) / 226bb34b4c8SAmit Daniel Kachhap (data->temp_error2 - data->temp_error1) + 227718b4ca1SBartlomiej Zolnierkiewicz EXYNOS_FIRST_POINT_TRIM; 22859dfa54cSAmit Daniel Kachhap } 22959dfa54cSAmit Daniel Kachhap 2308328a4b1SBartlomiej Zolnierkiewicz static void sanitize_temp_error(struct exynos_tmu_data *data, u32 trim_info) 231b835ced1SBartlomiej Zolnierkiewicz { 232aef27b65SBartlomiej Zolnierkiewicz u16 tmu_temp_mask = 233aef27b65SBartlomiej Zolnierkiewicz (data->soc == SOC_ARCH_EXYNOS7) ? EXYNOS7_TMU_TEMP_MASK 234aef27b65SBartlomiej Zolnierkiewicz : EXYNOS_TMU_TEMP_MASK; 23559dfa54cSAmit Daniel Kachhap 236aef27b65SBartlomiej Zolnierkiewicz data->temp_error1 = trim_info & tmu_temp_mask; 23799d67fb9SBartlomiej Zolnierkiewicz data->temp_error2 = ((trim_info >> EXYNOS_TRIMINFO_85_SHIFT) & 238b8d582b9SAmit Daniel Kachhap EXYNOS_TMU_TEMP_MASK); 23959dfa54cSAmit Daniel Kachhap 2405000806cSAmit Daniel Kachhap if (!data->temp_error1 || 241e3ed3649SBartlomiej Zolnierkiewicz (data->min_efuse_value > data->temp_error1) || 242e3ed3649SBartlomiej Zolnierkiewicz (data->temp_error1 > data->max_efuse_value)) 243e3ed3649SBartlomiej Zolnierkiewicz data->temp_error1 = data->efuse_value & EXYNOS_TMU_TEMP_MASK; 2445000806cSAmit Daniel Kachhap 2455000806cSAmit Daniel Kachhap if (!data->temp_error2) 2465000806cSAmit Daniel Kachhap data->temp_error2 = 247e3ed3649SBartlomiej Zolnierkiewicz (data->efuse_value >> EXYNOS_TRIMINFO_85_SHIFT) & 2485000806cSAmit Daniel Kachhap EXYNOS_TMU_TEMP_MASK; 2498328a4b1SBartlomiej Zolnierkiewicz } 25059dfa54cSAmit Daniel Kachhap 25159dfa54cSAmit Daniel Kachhap static int exynos_tmu_initialize(struct platform_device *pdev) 25259dfa54cSAmit Daniel Kachhap { 25359dfa54cSAmit Daniel Kachhap struct exynos_tmu_data *data = platform_get_drvdata(pdev); 25475e0f100SBartlomiej Zolnierkiewicz struct thermal_zone_device *tzd = data->tzd; 255a3b3dd38SDaniel Lezcano int num_trips = thermal_zone_get_num_trips(tzd); 25697b3881bSBartlomiej Zolnierkiewicz unsigned int status; 257ca38255eSDaniel Lezcano int ret = 0, temp; 25875e0f100SBartlomiej Zolnierkiewicz 259a1c30637SDaniel Lezcano ret = thermal_zone_get_crit_temp(tzd, &temp); 260a1c30637SDaniel Lezcano if (ret && data->soc != SOC_ARCH_EXYNOS5433) { /* FIXME */ 2618f1c404bSBartlomiej Zolnierkiewicz dev_err(&pdev->dev, 2628f1c404bSBartlomiej Zolnierkiewicz "No CRITICAL trip point defined in device tree!\n"); 2638f1c404bSBartlomiej Zolnierkiewicz goto out; 2648f1c404bSBartlomiej Zolnierkiewicz } 2658f1c404bSBartlomiej Zolnierkiewicz 266a3b3dd38SDaniel Lezcano if (num_trips > data->ntrip) { 2673a3a5f15SKrzysztof Kozlowski dev_info(&pdev->dev, 2683a3a5f15SKrzysztof Kozlowski "More trip points than supported by this TMU.\n"); 2693a3a5f15SKrzysztof Kozlowski dev_info(&pdev->dev, 2703a3a5f15SKrzysztof Kozlowski "%d trip points should be configured in polling mode.\n", 271a3b3dd38SDaniel Lezcano num_trips - data->ntrip); 2723a3a5f15SKrzysztof Kozlowski } 2733a3a5f15SKrzysztof Kozlowski 27459dfa54cSAmit Daniel Kachhap mutex_lock(&data->lock); 27559dfa54cSAmit Daniel Kachhap clk_enable(data->clk); 27659dfa54cSAmit Daniel Kachhap if (!IS_ERR(data->clk_sec)) 27759dfa54cSAmit Daniel Kachhap clk_enable(data->clk_sec); 27897b3881bSBartlomiej Zolnierkiewicz 27997b3881bSBartlomiej Zolnierkiewicz status = readb(data->base + EXYNOS_TMU_REG_STATUS); 280fac36bacSBartlomiej Zolnierkiewicz if (!status) { 28197b3881bSBartlomiej Zolnierkiewicz ret = -EBUSY; 282fac36bacSBartlomiej Zolnierkiewicz } else { 283c8f8f768SBartlomiej Zolnierkiewicz int i, ntrips = 284a3b3dd38SDaniel Lezcano min_t(int, num_trips, data->ntrip); 285c8f8f768SBartlomiej Zolnierkiewicz 286c35268f5SBartlomiej Zolnierkiewicz data->tmu_initialize(pdev); 287c8f8f768SBartlomiej Zolnierkiewicz 288c8f8f768SBartlomiej Zolnierkiewicz /* Write temperature code for rising and falling threshold */ 289c8f8f768SBartlomiej Zolnierkiewicz for (i = 0; i < ntrips; i++) { 290c8f8f768SBartlomiej Zolnierkiewicz 291ca38255eSDaniel Lezcano struct thermal_trip trip; 292ca38255eSDaniel Lezcano 293ca38255eSDaniel Lezcano ret = thermal_zone_get_trip(tzd, i, &trip); 29489335c20SBartlomiej Zolnierkiewicz if (ret) 29589335c20SBartlomiej Zolnierkiewicz goto err; 296ca38255eSDaniel Lezcano 297ca38255eSDaniel Lezcano data->tmu_set_trip_temp(data, i, trip.temperature / MCELSIUS); 298ca38255eSDaniel Lezcano data->tmu_set_trip_hyst(data, i, trip.temperature / MCELSIUS, 299ca38255eSDaniel Lezcano trip.hysteresis / MCELSIUS); 300c8f8f768SBartlomiej Zolnierkiewicz } 301c8f8f768SBartlomiej Zolnierkiewicz 302fac36bacSBartlomiej Zolnierkiewicz data->tmu_clear_irqs(data); 303fac36bacSBartlomiej Zolnierkiewicz } 30489335c20SBartlomiej Zolnierkiewicz err: 30559dfa54cSAmit Daniel Kachhap clk_disable(data->clk); 30659dfa54cSAmit Daniel Kachhap mutex_unlock(&data->lock); 30714a11dc7SNaveen Krishna Chatradhi if (!IS_ERR(data->clk_sec)) 30814a11dc7SNaveen Krishna Chatradhi clk_disable(data->clk_sec); 3098f1c404bSBartlomiej Zolnierkiewicz out: 31059dfa54cSAmit Daniel Kachhap return ret; 31159dfa54cSAmit Daniel Kachhap } 31259dfa54cSAmit Daniel Kachhap 313d00671c3SBartlomiej Zolnierkiewicz static u32 get_con_reg(struct exynos_tmu_data *data, u32 con) 31459dfa54cSAmit Daniel Kachhap { 3157575983cSBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS4412 || 3167575983cSBartlomiej Zolnierkiewicz data->soc == SOC_ARCH_EXYNOS3250) 3177575983cSBartlomiej Zolnierkiewicz con |= (EXYNOS4412_MUX_ADDR_VALUE << EXYNOS4412_MUX_ADDR_SHIFT); 31886f5362eSLukasz Majewski 31999d67fb9SBartlomiej Zolnierkiewicz con &= ~(EXYNOS_TMU_REF_VOLTAGE_MASK << EXYNOS_TMU_REF_VOLTAGE_SHIFT); 32061020d18SBartlomiej Zolnierkiewicz con |= data->reference_voltage << EXYNOS_TMU_REF_VOLTAGE_SHIFT; 321d0a0ce3eSAmit Daniel Kachhap 32299d67fb9SBartlomiej Zolnierkiewicz con &= ~(EXYNOS_TMU_BUF_SLOPE_SEL_MASK << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT); 323fccfe099SBartlomiej Zolnierkiewicz con |= (data->gain << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT); 324d0a0ce3eSAmit Daniel Kachhap 325b9504a6aSBartlomiej Zolnierkiewicz con &= ~(EXYNOS_TMU_TRIP_MODE_MASK << EXYNOS_TMU_TRIP_MODE_SHIFT); 32609d29426SBartlomiej Zolnierkiewicz con |= (EXYNOS_NOISE_CANCEL_MODE << EXYNOS_TMU_TRIP_MODE_SHIFT); 32759dfa54cSAmit Daniel Kachhap 328d00671c3SBartlomiej Zolnierkiewicz return con; 329d00671c3SBartlomiej Zolnierkiewicz } 330d00671c3SBartlomiej Zolnierkiewicz 331d00671c3SBartlomiej Zolnierkiewicz static void exynos_tmu_control(struct platform_device *pdev, bool on) 332d00671c3SBartlomiej Zolnierkiewicz { 333d00671c3SBartlomiej Zolnierkiewicz struct exynos_tmu_data *data = platform_get_drvdata(pdev); 334d00671c3SBartlomiej Zolnierkiewicz 335d00671c3SBartlomiej Zolnierkiewicz mutex_lock(&data->lock); 336d00671c3SBartlomiej Zolnierkiewicz clk_enable(data->clk); 33737f9034fSBartlomiej Zolnierkiewicz data->tmu_control(pdev, on); 33888fc6f73SMarek Szyprowski data->enabled = on; 33959dfa54cSAmit Daniel Kachhap clk_disable(data->clk); 34059dfa54cSAmit Daniel Kachhap mutex_unlock(&data->lock); 34159dfa54cSAmit Daniel Kachhap } 34259dfa54cSAmit Daniel Kachhap 343a503a10fSBartlomiej Zolnierkiewicz static void exynos4210_tmu_set_trip_temp(struct exynos_tmu_data *data, 344ca38255eSDaniel Lezcano int trip_id, u8 temp) 34572d1100bSBartlomiej Zolnierkiewicz { 346*d7a5b431SMateusz Majewski temp = temp_to_code(data, temp); 347ca38255eSDaniel Lezcano writeb(temp, data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL0 + trip_id * 4); 348a503a10fSBartlomiej Zolnierkiewicz } 349a503a10fSBartlomiej Zolnierkiewicz 350c8f8f768SBartlomiej Zolnierkiewicz /* failing thresholds are not supported on Exynos4210 */ 351c8f8f768SBartlomiej Zolnierkiewicz static void exynos4210_tmu_set_trip_hyst(struct exynos_tmu_data *data, 352c8f8f768SBartlomiej Zolnierkiewicz int trip, u8 temp, u8 hyst) 353c8f8f768SBartlomiej Zolnierkiewicz { 354c8f8f768SBartlomiej Zolnierkiewicz } 355c8f8f768SBartlomiej Zolnierkiewicz 356c35268f5SBartlomiej Zolnierkiewicz static void exynos4210_tmu_initialize(struct platform_device *pdev) 35772d1100bSBartlomiej Zolnierkiewicz { 35872d1100bSBartlomiej Zolnierkiewicz struct exynos_tmu_data *data = platform_get_drvdata(pdev); 35972d1100bSBartlomiej Zolnierkiewicz 36072d1100bSBartlomiej Zolnierkiewicz sanitize_temp_error(data, readl(data->base + EXYNOS_TMU_REG_TRIMINFO)); 361*d7a5b431SMateusz Majewski 362*d7a5b431SMateusz Majewski writeb(0, data->base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP); 36372d1100bSBartlomiej Zolnierkiewicz } 36472d1100bSBartlomiej Zolnierkiewicz 365a503a10fSBartlomiej Zolnierkiewicz static void exynos4412_tmu_set_trip_temp(struct exynos_tmu_data *data, 366a503a10fSBartlomiej Zolnierkiewicz int trip, u8 temp) 367a503a10fSBartlomiej Zolnierkiewicz { 368a503a10fSBartlomiej Zolnierkiewicz u32 th, con; 369a503a10fSBartlomiej Zolnierkiewicz 370a503a10fSBartlomiej Zolnierkiewicz th = readl(data->base + EXYNOS_THD_TEMP_RISE); 371a503a10fSBartlomiej Zolnierkiewicz th &= ~(0xff << 8 * trip); 372a503a10fSBartlomiej Zolnierkiewicz th |= temp_to_code(data, temp) << 8 * trip; 373a503a10fSBartlomiej Zolnierkiewicz writel(th, data->base + EXYNOS_THD_TEMP_RISE); 374a503a10fSBartlomiej Zolnierkiewicz 375a503a10fSBartlomiej Zolnierkiewicz if (trip == 3) { 376a503a10fSBartlomiej Zolnierkiewicz con = readl(data->base + EXYNOS_TMU_REG_CONTROL); 377a503a10fSBartlomiej Zolnierkiewicz con |= (1 << EXYNOS_TMU_THERM_TRIP_EN_SHIFT); 378a503a10fSBartlomiej Zolnierkiewicz writel(con, data->base + EXYNOS_TMU_REG_CONTROL); 379a503a10fSBartlomiej Zolnierkiewicz } 380a503a10fSBartlomiej Zolnierkiewicz } 381a503a10fSBartlomiej Zolnierkiewicz 382a503a10fSBartlomiej Zolnierkiewicz static void exynos4412_tmu_set_trip_hyst(struct exynos_tmu_data *data, 383a503a10fSBartlomiej Zolnierkiewicz int trip, u8 temp, u8 hyst) 384a503a10fSBartlomiej Zolnierkiewicz { 385a503a10fSBartlomiej Zolnierkiewicz u32 th; 386a503a10fSBartlomiej Zolnierkiewicz 387a503a10fSBartlomiej Zolnierkiewicz th = readl(data->base + EXYNOS_THD_TEMP_FALL); 388a503a10fSBartlomiej Zolnierkiewicz th &= ~(0xff << 8 * trip); 389a503a10fSBartlomiej Zolnierkiewicz if (hyst) 390a503a10fSBartlomiej Zolnierkiewicz th |= temp_to_code(data, temp - hyst) << 8 * trip; 391a503a10fSBartlomiej Zolnierkiewicz writel(th, data->base + EXYNOS_THD_TEMP_FALL); 392a503a10fSBartlomiej Zolnierkiewicz } 393a503a10fSBartlomiej Zolnierkiewicz 394c35268f5SBartlomiej Zolnierkiewicz static void exynos4412_tmu_initialize(struct platform_device *pdev) 39572d1100bSBartlomiej Zolnierkiewicz { 39672d1100bSBartlomiej Zolnierkiewicz struct exynos_tmu_data *data = platform_get_drvdata(pdev); 397a503a10fSBartlomiej Zolnierkiewicz unsigned int trim_info, ctrl; 39872d1100bSBartlomiej Zolnierkiewicz 39972d1100bSBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS3250 || 40072d1100bSBartlomiej Zolnierkiewicz data->soc == SOC_ARCH_EXYNOS4412 || 40172d1100bSBartlomiej Zolnierkiewicz data->soc == SOC_ARCH_EXYNOS5250) { 40272d1100bSBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS3250) { 40372d1100bSBartlomiej Zolnierkiewicz ctrl = readl(data->base + EXYNOS_TMU_TRIMINFO_CON1); 40472d1100bSBartlomiej Zolnierkiewicz ctrl |= EXYNOS_TRIMINFO_RELOAD_ENABLE; 40572d1100bSBartlomiej Zolnierkiewicz writel(ctrl, data->base + EXYNOS_TMU_TRIMINFO_CON1); 40672d1100bSBartlomiej Zolnierkiewicz } 40772d1100bSBartlomiej Zolnierkiewicz ctrl = readl(data->base + EXYNOS_TMU_TRIMINFO_CON2); 40872d1100bSBartlomiej Zolnierkiewicz ctrl |= EXYNOS_TRIMINFO_RELOAD_ENABLE; 40972d1100bSBartlomiej Zolnierkiewicz writel(ctrl, data->base + EXYNOS_TMU_TRIMINFO_CON2); 41072d1100bSBartlomiej Zolnierkiewicz } 41172d1100bSBartlomiej Zolnierkiewicz 41272d1100bSBartlomiej Zolnierkiewicz /* On exynos5420 the triminfo register is in the shared space */ 41372d1100bSBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) 41472d1100bSBartlomiej Zolnierkiewicz trim_info = readl(data->base_second + EXYNOS_TMU_REG_TRIMINFO); 41572d1100bSBartlomiej Zolnierkiewicz else 41672d1100bSBartlomiej Zolnierkiewicz trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); 41772d1100bSBartlomiej Zolnierkiewicz 41872d1100bSBartlomiej Zolnierkiewicz sanitize_temp_error(data, trim_info); 4193b6a1a80SLukasz Majewski } 4203b6a1a80SLukasz Majewski 421a503a10fSBartlomiej Zolnierkiewicz static void exynos5433_tmu_set_trip_temp(struct exynos_tmu_data *data, 422a503a10fSBartlomiej Zolnierkiewicz int trip, u8 temp) 423a503a10fSBartlomiej Zolnierkiewicz { 424a503a10fSBartlomiej Zolnierkiewicz unsigned int reg_off, j; 425a503a10fSBartlomiej Zolnierkiewicz u32 th; 426a503a10fSBartlomiej Zolnierkiewicz 427a503a10fSBartlomiej Zolnierkiewicz if (trip > 3) { 428a503a10fSBartlomiej Zolnierkiewicz reg_off = EXYNOS5433_THD_TEMP_RISE7_4; 429a503a10fSBartlomiej Zolnierkiewicz j = trip - 4; 430a503a10fSBartlomiej Zolnierkiewicz } else { 431a503a10fSBartlomiej Zolnierkiewicz reg_off = EXYNOS5433_THD_TEMP_RISE3_0; 432a503a10fSBartlomiej Zolnierkiewicz j = trip; 4333b6a1a80SLukasz Majewski } 4343b6a1a80SLukasz Majewski 435a503a10fSBartlomiej Zolnierkiewicz th = readl(data->base + reg_off); 436a503a10fSBartlomiej Zolnierkiewicz th &= ~(0xff << j * 8); 437a503a10fSBartlomiej Zolnierkiewicz th |= (temp_to_code(data, temp) << j * 8); 438a503a10fSBartlomiej Zolnierkiewicz writel(th, data->base + reg_off); 43972d1100bSBartlomiej Zolnierkiewicz } 44072d1100bSBartlomiej Zolnierkiewicz 441a503a10fSBartlomiej Zolnierkiewicz static void exynos5433_tmu_set_trip_hyst(struct exynos_tmu_data *data, 442a503a10fSBartlomiej Zolnierkiewicz int trip, u8 temp, u8 hyst) 443a503a10fSBartlomiej Zolnierkiewicz { 444a503a10fSBartlomiej Zolnierkiewicz unsigned int reg_off, j; 445a503a10fSBartlomiej Zolnierkiewicz u32 th; 446a503a10fSBartlomiej Zolnierkiewicz 447a503a10fSBartlomiej Zolnierkiewicz if (trip > 3) { 448a503a10fSBartlomiej Zolnierkiewicz reg_off = EXYNOS5433_THD_TEMP_FALL7_4; 449a503a10fSBartlomiej Zolnierkiewicz j = trip - 4; 450a503a10fSBartlomiej Zolnierkiewicz } else { 451a503a10fSBartlomiej Zolnierkiewicz reg_off = EXYNOS5433_THD_TEMP_FALL3_0; 452a503a10fSBartlomiej Zolnierkiewicz j = trip; 453a503a10fSBartlomiej Zolnierkiewicz } 454a503a10fSBartlomiej Zolnierkiewicz 455a503a10fSBartlomiej Zolnierkiewicz th = readl(data->base + reg_off); 456a503a10fSBartlomiej Zolnierkiewicz th &= ~(0xff << j * 8); 457a503a10fSBartlomiej Zolnierkiewicz th |= (temp_to_code(data, temp - hyst) << j * 8); 458a503a10fSBartlomiej Zolnierkiewicz writel(th, data->base + reg_off); 45972d1100bSBartlomiej Zolnierkiewicz } 46072d1100bSBartlomiej Zolnierkiewicz 461c35268f5SBartlomiej Zolnierkiewicz static void exynos5433_tmu_initialize(struct platform_device *pdev) 462488c7455SChanwoo Choi { 463488c7455SChanwoo Choi struct exynos_tmu_data *data = platform_get_drvdata(pdev); 46497b3881bSBartlomiej Zolnierkiewicz unsigned int trim_info; 465c8f8f768SBartlomiej Zolnierkiewicz int sensor_id, cal_type; 466488c7455SChanwoo Choi 467488c7455SChanwoo Choi trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); 468488c7455SChanwoo Choi sanitize_temp_error(data, trim_info); 469488c7455SChanwoo Choi 470488c7455SChanwoo Choi /* Read the temperature sensor id */ 471488c7455SChanwoo Choi sensor_id = (trim_info & EXYNOS5433_TRIMINFO_SENSOR_ID_MASK) 472488c7455SChanwoo Choi >> EXYNOS5433_TRIMINFO_SENSOR_ID_SHIFT; 473488c7455SChanwoo Choi dev_info(&pdev->dev, "Temperature sensor ID: 0x%x\n", sensor_id); 474488c7455SChanwoo Choi 475488c7455SChanwoo Choi /* Read the calibration mode */ 476488c7455SChanwoo Choi writel(trim_info, data->base + EXYNOS_TMU_REG_TRIMINFO); 477488c7455SChanwoo Choi cal_type = (trim_info & EXYNOS5433_TRIMINFO_CALIB_SEL_MASK) 478488c7455SChanwoo Choi >> EXYNOS5433_TRIMINFO_CALIB_SEL_SHIFT; 479488c7455SChanwoo Choi 480488c7455SChanwoo Choi switch (cal_type) { 481488c7455SChanwoo Choi case EXYNOS5433_TRIMINFO_TWO_POINT_TRIMMING: 482199b3e3cSBartlomiej Zolnierkiewicz data->cal_type = TYPE_TWO_POINT_TRIMMING; 483488c7455SChanwoo Choi break; 484199b3e3cSBartlomiej Zolnierkiewicz case EXYNOS5433_TRIMINFO_ONE_POINT_TRIMMING: 485488c7455SChanwoo Choi default: 486199b3e3cSBartlomiej Zolnierkiewicz data->cal_type = TYPE_ONE_POINT_TRIMMING; 487488c7455SChanwoo Choi break; 488baba1ebbSKrzysztof Kozlowski } 489488c7455SChanwoo Choi 490488c7455SChanwoo Choi dev_info(&pdev->dev, "Calibration type is %d-point calibration\n", 491488c7455SChanwoo Choi cal_type ? 2 : 1); 492488c7455SChanwoo Choi } 493488c7455SChanwoo Choi 494a503a10fSBartlomiej Zolnierkiewicz static void exynos7_tmu_set_trip_temp(struct exynos_tmu_data *data, 495a503a10fSBartlomiej Zolnierkiewicz int trip, u8 temp) 49672d1100bSBartlomiej Zolnierkiewicz { 4976c247393SAbhilash Kesavan unsigned int reg_off, bit_off; 498a503a10fSBartlomiej Zolnierkiewicz u32 th; 4996c247393SAbhilash Kesavan 500a503a10fSBartlomiej Zolnierkiewicz reg_off = ((7 - trip) / 2) * 4; 501a503a10fSBartlomiej Zolnierkiewicz bit_off = ((8 - trip) % 2); 502a503a10fSBartlomiej Zolnierkiewicz 503a503a10fSBartlomiej Zolnierkiewicz th = readl(data->base + EXYNOS7_THD_TEMP_RISE7_6 + reg_off); 504a503a10fSBartlomiej Zolnierkiewicz th &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off)); 505a503a10fSBartlomiej Zolnierkiewicz th |= temp_to_code(data, temp) << (16 * bit_off); 506a503a10fSBartlomiej Zolnierkiewicz writel(th, data->base + EXYNOS7_THD_TEMP_RISE7_6 + reg_off); 5076c247393SAbhilash Kesavan } 5086c247393SAbhilash Kesavan 509a503a10fSBartlomiej Zolnierkiewicz static void exynos7_tmu_set_trip_hyst(struct exynos_tmu_data *data, 510a503a10fSBartlomiej Zolnierkiewicz int trip, u8 temp, u8 hyst) 511a503a10fSBartlomiej Zolnierkiewicz { 512a503a10fSBartlomiej Zolnierkiewicz unsigned int reg_off, bit_off; 513a503a10fSBartlomiej Zolnierkiewicz u32 th; 514a503a10fSBartlomiej Zolnierkiewicz 515a503a10fSBartlomiej Zolnierkiewicz reg_off = ((7 - trip) / 2) * 4; 516a503a10fSBartlomiej Zolnierkiewicz bit_off = ((8 - trip) % 2); 517a503a10fSBartlomiej Zolnierkiewicz 518a503a10fSBartlomiej Zolnierkiewicz th = readl(data->base + EXYNOS7_THD_TEMP_FALL7_6 + reg_off); 519a503a10fSBartlomiej Zolnierkiewicz th &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off)); 520a503a10fSBartlomiej Zolnierkiewicz th |= temp_to_code(data, temp - hyst) << (16 * bit_off); 521a503a10fSBartlomiej Zolnierkiewicz writel(th, data->base + EXYNOS7_THD_TEMP_FALL7_6 + reg_off); 522a503a10fSBartlomiej Zolnierkiewicz } 523a503a10fSBartlomiej Zolnierkiewicz 524c35268f5SBartlomiej Zolnierkiewicz static void exynos7_tmu_initialize(struct platform_device *pdev) 5256c247393SAbhilash Kesavan { 5266c247393SAbhilash Kesavan struct exynos_tmu_data *data = platform_get_drvdata(pdev); 52797b3881bSBartlomiej Zolnierkiewicz unsigned int trim_info; 5286c247393SAbhilash Kesavan 5296c247393SAbhilash Kesavan trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); 530aef27b65SBartlomiej Zolnierkiewicz sanitize_temp_error(data, trim_info); 5316c247393SAbhilash Kesavan } 5326c247393SAbhilash Kesavan 53337f9034fSBartlomiej Zolnierkiewicz static void exynos4210_tmu_control(struct platform_device *pdev, bool on) 53437f9034fSBartlomiej Zolnierkiewicz { 53537f9034fSBartlomiej Zolnierkiewicz struct exynos_tmu_data *data = platform_get_drvdata(pdev); 5363b6a1a80SLukasz Majewski struct thermal_zone_device *tz = data->tzd; 53703ef4855SDaniel Lezcano struct thermal_trip trip; 53864e94192SBartlomiej Zolnierkiewicz unsigned int con, interrupt_en = 0, i; 53937f9034fSBartlomiej Zolnierkiewicz 54037f9034fSBartlomiej Zolnierkiewicz con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL)); 54137f9034fSBartlomiej Zolnierkiewicz 54259dfa54cSAmit Daniel Kachhap if (on) { 54364e94192SBartlomiej Zolnierkiewicz for (i = 0; i < data->ntrip; i++) { 54403ef4855SDaniel Lezcano if (thermal_zone_get_trip(tz, i, &trip)) 54564e94192SBartlomiej Zolnierkiewicz continue; 54664e94192SBartlomiej Zolnierkiewicz 54764e94192SBartlomiej Zolnierkiewicz interrupt_en |= 54864e94192SBartlomiej Zolnierkiewicz (1 << (EXYNOS_TMU_INTEN_RISE0_SHIFT + i * 4)); 54964e94192SBartlomiej Zolnierkiewicz } 5503b6a1a80SLukasz Majewski 551e0761533SBartlomiej Zolnierkiewicz if (data->soc != SOC_ARCH_EXYNOS4210) 55259dfa54cSAmit Daniel Kachhap interrupt_en |= 55337f9034fSBartlomiej Zolnierkiewicz interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT; 55464e94192SBartlomiej Zolnierkiewicz 55564e94192SBartlomiej Zolnierkiewicz con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); 55659dfa54cSAmit Daniel Kachhap } else { 55759dfa54cSAmit Daniel Kachhap con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); 55859dfa54cSAmit Daniel Kachhap } 55964e94192SBartlomiej Zolnierkiewicz 56037f9034fSBartlomiej Zolnierkiewicz writel(interrupt_en, data->base + EXYNOS_TMU_REG_INTEN); 56137f9034fSBartlomiej Zolnierkiewicz writel(con, data->base + EXYNOS_TMU_REG_CONTROL); 56237f9034fSBartlomiej Zolnierkiewicz } 56359dfa54cSAmit Daniel Kachhap 564488c7455SChanwoo Choi static void exynos5433_tmu_control(struct platform_device *pdev, bool on) 565488c7455SChanwoo Choi { 566488c7455SChanwoo Choi struct exynos_tmu_data *data = platform_get_drvdata(pdev); 567488c7455SChanwoo Choi struct thermal_zone_device *tz = data->tzd; 56803ef4855SDaniel Lezcano struct thermal_trip trip; 56964e94192SBartlomiej Zolnierkiewicz unsigned int con, interrupt_en = 0, pd_det_en, i; 570488c7455SChanwoo Choi 571488c7455SChanwoo Choi con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL)); 572488c7455SChanwoo Choi 573488c7455SChanwoo Choi if (on) { 57464e94192SBartlomiej Zolnierkiewicz for (i = 0; i < data->ntrip; i++) { 57503ef4855SDaniel Lezcano if (thermal_zone_get_trip(tz, i, &trip)) 57664e94192SBartlomiej Zolnierkiewicz continue; 57764e94192SBartlomiej Zolnierkiewicz 57864e94192SBartlomiej Zolnierkiewicz interrupt_en |= 57964e94192SBartlomiej Zolnierkiewicz (1 << (EXYNOS7_TMU_INTEN_RISE0_SHIFT + i)); 58064e94192SBartlomiej Zolnierkiewicz } 581488c7455SChanwoo Choi 582488c7455SChanwoo Choi interrupt_en |= 583488c7455SChanwoo Choi interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT; 58464e94192SBartlomiej Zolnierkiewicz 58564e94192SBartlomiej Zolnierkiewicz con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); 58664e94192SBartlomiej Zolnierkiewicz } else 587488c7455SChanwoo Choi con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); 588488c7455SChanwoo Choi 589488c7455SChanwoo Choi pd_det_en = on ? EXYNOS5433_PD_DET_EN : 0; 590488c7455SChanwoo Choi 591488c7455SChanwoo Choi writel(pd_det_en, data->base + EXYNOS5433_TMU_PD_DET_EN); 592488c7455SChanwoo Choi writel(interrupt_en, data->base + EXYNOS5433_TMU_REG_INTEN); 593488c7455SChanwoo Choi writel(con, data->base + EXYNOS_TMU_REG_CONTROL); 594488c7455SChanwoo Choi } 595488c7455SChanwoo Choi 5966c247393SAbhilash Kesavan static void exynos7_tmu_control(struct platform_device *pdev, bool on) 5976c247393SAbhilash Kesavan { 5986c247393SAbhilash Kesavan struct exynos_tmu_data *data = platform_get_drvdata(pdev); 5996c247393SAbhilash Kesavan struct thermal_zone_device *tz = data->tzd; 60003ef4855SDaniel Lezcano struct thermal_trip trip; 60164e94192SBartlomiej Zolnierkiewicz unsigned int con, interrupt_en = 0, i; 6026c247393SAbhilash Kesavan 6036c247393SAbhilash Kesavan con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL)); 6046c247393SAbhilash Kesavan 6056c247393SAbhilash Kesavan if (on) { 60664e94192SBartlomiej Zolnierkiewicz for (i = 0; i < data->ntrip; i++) { 60703ef4855SDaniel Lezcano if (thermal_zone_get_trip(tz, i, &trip)) 60864e94192SBartlomiej Zolnierkiewicz continue; 60964e94192SBartlomiej Zolnierkiewicz 61064e94192SBartlomiej Zolnierkiewicz interrupt_en |= 61164e94192SBartlomiej Zolnierkiewicz (1 << (EXYNOS7_TMU_INTEN_RISE0_SHIFT + i)); 61264e94192SBartlomiej Zolnierkiewicz } 6136c247393SAbhilash Kesavan 6146c247393SAbhilash Kesavan interrupt_en |= 6156c247393SAbhilash Kesavan interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT; 61664e94192SBartlomiej Zolnierkiewicz 61764e94192SBartlomiej Zolnierkiewicz con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); 61864e94192SBartlomiej Zolnierkiewicz con |= (1 << EXYNOS7_PD_DET_EN_SHIFT); 6196c247393SAbhilash Kesavan } else { 6206c247393SAbhilash Kesavan con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); 62142b696e8SChanwoo Choi con &= ~(1 << EXYNOS7_PD_DET_EN_SHIFT); 6226c247393SAbhilash Kesavan } 6236c247393SAbhilash Kesavan 6246c247393SAbhilash Kesavan writel(interrupt_en, data->base + EXYNOS7_TMU_REG_INTEN); 6256c247393SAbhilash Kesavan writel(con, data->base + EXYNOS_TMU_REG_CONTROL); 6266c247393SAbhilash Kesavan } 6276c247393SAbhilash Kesavan 6287ea98f70SDaniel Lezcano static int exynos_get_temp(struct thermal_zone_device *tz, int *temp) 62959dfa54cSAmit Daniel Kachhap { 6305f68d078SDaniel Lezcano struct exynos_tmu_data *data = thermal_zone_device_priv(tz); 631c8da6cdeSMarek Szyprowski int value, ret = 0; 6323b6a1a80SLukasz Majewski 6333b5236ccSMarek Szyprowski if (!data || !data->tmu_read) 6343b6a1a80SLukasz Majewski return -EINVAL; 635ffe6e16fSKrzysztof Kozlowski else if (!data->enabled) 636ffe6e16fSKrzysztof Kozlowski /* 637ffe6e16fSKrzysztof Kozlowski * Called too early, probably 638ffe6e16fSKrzysztof Kozlowski * from thermal_zone_of_sensor_register(). 639ffe6e16fSKrzysztof Kozlowski */ 640ffe6e16fSKrzysztof Kozlowski return -EAGAIN; 64159dfa54cSAmit Daniel Kachhap 64259dfa54cSAmit Daniel Kachhap mutex_lock(&data->lock); 64359dfa54cSAmit Daniel Kachhap clk_enable(data->clk); 6443b6a1a80SLukasz Majewski 645c8da6cdeSMarek Szyprowski value = data->tmu_read(data); 646c8da6cdeSMarek Szyprowski if (value < 0) 647c8da6cdeSMarek Szyprowski ret = value; 648c8da6cdeSMarek Szyprowski else 649c8da6cdeSMarek Szyprowski *temp = code_to_temp(data, value) * MCELSIUS; 6503b6a1a80SLukasz Majewski 65159dfa54cSAmit Daniel Kachhap clk_disable(data->clk); 65259dfa54cSAmit Daniel Kachhap mutex_unlock(&data->lock); 65359dfa54cSAmit Daniel Kachhap 654c8da6cdeSMarek Szyprowski return ret; 65559dfa54cSAmit Daniel Kachhap } 65659dfa54cSAmit Daniel Kachhap 65759dfa54cSAmit Daniel Kachhap #ifdef CONFIG_THERMAL_EMULATION 658154013eaSBartlomiej Zolnierkiewicz static u32 get_emul_con_reg(struct exynos_tmu_data *data, unsigned int val, 65917e8351aSSascha Hauer int temp) 660154013eaSBartlomiej Zolnierkiewicz { 661154013eaSBartlomiej Zolnierkiewicz if (temp) { 662154013eaSBartlomiej Zolnierkiewicz temp /= MCELSIUS; 663154013eaSBartlomiej Zolnierkiewicz 664154013eaSBartlomiej Zolnierkiewicz val &= ~(EXYNOS_EMUL_TIME_MASK << EXYNOS_EMUL_TIME_SHIFT); 665154013eaSBartlomiej Zolnierkiewicz val |= (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT); 6666c247393SAbhilash Kesavan if (data->soc == SOC_ARCH_EXYNOS7) { 6676c247393SAbhilash Kesavan val &= ~(EXYNOS7_EMUL_DATA_MASK << 6686c247393SAbhilash Kesavan EXYNOS7_EMUL_DATA_SHIFT); 6696c247393SAbhilash Kesavan val |= (temp_to_code(data, temp) << 6706c247393SAbhilash Kesavan EXYNOS7_EMUL_DATA_SHIFT) | 671154013eaSBartlomiej Zolnierkiewicz EXYNOS_EMUL_ENABLE; 672154013eaSBartlomiej Zolnierkiewicz } else { 6736c247393SAbhilash Kesavan val &= ~(EXYNOS_EMUL_DATA_MASK << 6746c247393SAbhilash Kesavan EXYNOS_EMUL_DATA_SHIFT); 6756c247393SAbhilash Kesavan val |= (temp_to_code(data, temp) << 6766c247393SAbhilash Kesavan EXYNOS_EMUL_DATA_SHIFT) | 6776c247393SAbhilash Kesavan EXYNOS_EMUL_ENABLE; 6786c247393SAbhilash Kesavan } 6796c247393SAbhilash Kesavan } else { 680154013eaSBartlomiej Zolnierkiewicz val &= ~EXYNOS_EMUL_ENABLE; 681154013eaSBartlomiej Zolnierkiewicz } 682154013eaSBartlomiej Zolnierkiewicz 683154013eaSBartlomiej Zolnierkiewicz return val; 684154013eaSBartlomiej Zolnierkiewicz } 685154013eaSBartlomiej Zolnierkiewicz 686285d994aSBartlomiej Zolnierkiewicz static void exynos4412_tmu_set_emulation(struct exynos_tmu_data *data, 68717e8351aSSascha Hauer int temp) 688285d994aSBartlomiej Zolnierkiewicz { 689285d994aSBartlomiej Zolnierkiewicz unsigned int val; 690285d994aSBartlomiej Zolnierkiewicz u32 emul_con; 691285d994aSBartlomiej Zolnierkiewicz 692285d994aSBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS5260) 693285d994aSBartlomiej Zolnierkiewicz emul_con = EXYNOS5260_EMUL_CON; 694b28fec13SSudip Mukherjee else if (data->soc == SOC_ARCH_EXYNOS5433) 695488c7455SChanwoo Choi emul_con = EXYNOS5433_TMU_EMUL_CON; 6966c247393SAbhilash Kesavan else if (data->soc == SOC_ARCH_EXYNOS7) 6976c247393SAbhilash Kesavan emul_con = EXYNOS7_TMU_REG_EMUL_CON; 698285d994aSBartlomiej Zolnierkiewicz else 699285d994aSBartlomiej Zolnierkiewicz emul_con = EXYNOS_EMUL_CON; 700285d994aSBartlomiej Zolnierkiewicz 701285d994aSBartlomiej Zolnierkiewicz val = readl(data->base + emul_con); 702285d994aSBartlomiej Zolnierkiewicz val = get_emul_con_reg(data, val, temp); 703285d994aSBartlomiej Zolnierkiewicz writel(val, data->base + emul_con); 704285d994aSBartlomiej Zolnierkiewicz } 705285d994aSBartlomiej Zolnierkiewicz 7067ea98f70SDaniel Lezcano static int exynos_tmu_set_emulation(struct thermal_zone_device *tz, int temp) 70759dfa54cSAmit Daniel Kachhap { 7085f68d078SDaniel Lezcano struct exynos_tmu_data *data = thermal_zone_device_priv(tz); 70959dfa54cSAmit Daniel Kachhap int ret = -EINVAL; 71059dfa54cSAmit Daniel Kachhap 711ef3f80fcSBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS4210) 71259dfa54cSAmit Daniel Kachhap goto out; 71359dfa54cSAmit Daniel Kachhap 71459dfa54cSAmit Daniel Kachhap if (temp && temp < MCELSIUS) 71559dfa54cSAmit Daniel Kachhap goto out; 71659dfa54cSAmit Daniel Kachhap 71759dfa54cSAmit Daniel Kachhap mutex_lock(&data->lock); 71859dfa54cSAmit Daniel Kachhap clk_enable(data->clk); 719285d994aSBartlomiej Zolnierkiewicz data->tmu_set_emulation(data, temp); 72059dfa54cSAmit Daniel Kachhap clk_disable(data->clk); 72159dfa54cSAmit Daniel Kachhap mutex_unlock(&data->lock); 72259dfa54cSAmit Daniel Kachhap return 0; 72359dfa54cSAmit Daniel Kachhap out: 72459dfa54cSAmit Daniel Kachhap return ret; 72559dfa54cSAmit Daniel Kachhap } 72659dfa54cSAmit Daniel Kachhap #else 727285d994aSBartlomiej Zolnierkiewicz #define exynos4412_tmu_set_emulation NULL 7287ea98f70SDaniel Lezcano static int exynos_tmu_set_emulation(struct thermal_zone_device *tz, int temp) 72959dfa54cSAmit Daniel Kachhap { return -EINVAL; } 73059dfa54cSAmit Daniel Kachhap #endif /* CONFIG_THERMAL_EMULATION */ 73159dfa54cSAmit Daniel Kachhap 732b79985caSBartlomiej Zolnierkiewicz static int exynos4210_tmu_read(struct exynos_tmu_data *data) 733b79985caSBartlomiej Zolnierkiewicz { 734b79985caSBartlomiej Zolnierkiewicz int ret = readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP); 735b79985caSBartlomiej Zolnierkiewicz 736b79985caSBartlomiej Zolnierkiewicz /* "temp_code" should range between 75 and 175 */ 737b79985caSBartlomiej Zolnierkiewicz return (ret < 75 || ret > 175) ? -ENODATA : ret; 738b79985caSBartlomiej Zolnierkiewicz } 739b79985caSBartlomiej Zolnierkiewicz 740b79985caSBartlomiej Zolnierkiewicz static int exynos4412_tmu_read(struct exynos_tmu_data *data) 741b79985caSBartlomiej Zolnierkiewicz { 742b79985caSBartlomiej Zolnierkiewicz return readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP); 743b79985caSBartlomiej Zolnierkiewicz } 744b79985caSBartlomiej Zolnierkiewicz 7456c247393SAbhilash Kesavan static int exynos7_tmu_read(struct exynos_tmu_data *data) 7466c247393SAbhilash Kesavan { 7476c247393SAbhilash Kesavan return readw(data->base + EXYNOS_TMU_REG_CURRENT_TEMP) & 7486c247393SAbhilash Kesavan EXYNOS7_TMU_TEMP_MASK; 7496c247393SAbhilash Kesavan } 7506c247393SAbhilash Kesavan 75120009a81SMateusz Majewski static irqreturn_t exynos_tmu_threaded_irq(int irq, void *id) 75259dfa54cSAmit Daniel Kachhap { 75320009a81SMateusz Majewski struct exynos_tmu_data *data = id; 754a0395eeeSAmit Daniel Kachhap 755b43e3cfeSBartlomiej Zolnierkiewicz thermal_zone_device_update(data->tzd, THERMAL_EVENT_UNSPECIFIED); 756b43e3cfeSBartlomiej Zolnierkiewicz 75759dfa54cSAmit Daniel Kachhap mutex_lock(&data->lock); 75859dfa54cSAmit Daniel Kachhap clk_enable(data->clk); 759b8d582b9SAmit Daniel Kachhap 760a4463c4fSAmit Daniel Kachhap /* TODO: take action based on particular interrupt */ 761a7331f72SBartlomiej Zolnierkiewicz data->tmu_clear_irqs(data); 762b8d582b9SAmit Daniel Kachhap 76359dfa54cSAmit Daniel Kachhap clk_disable(data->clk); 76459dfa54cSAmit Daniel Kachhap mutex_unlock(&data->lock); 76520009a81SMateusz Majewski 76620009a81SMateusz Majewski return IRQ_HANDLED; 76759dfa54cSAmit Daniel Kachhap } 76859dfa54cSAmit Daniel Kachhap 769a7331f72SBartlomiej Zolnierkiewicz static void exynos4210_tmu_clear_irqs(struct exynos_tmu_data *data) 770a7331f72SBartlomiej Zolnierkiewicz { 771a7331f72SBartlomiej Zolnierkiewicz unsigned int val_irq; 772a7331f72SBartlomiej Zolnierkiewicz u32 tmu_intstat, tmu_intclear; 773a7331f72SBartlomiej Zolnierkiewicz 774a7331f72SBartlomiej Zolnierkiewicz if (data->soc == SOC_ARCH_EXYNOS5260) { 775a7331f72SBartlomiej Zolnierkiewicz tmu_intstat = EXYNOS5260_TMU_REG_INTSTAT; 776a7331f72SBartlomiej Zolnierkiewicz tmu_intclear = EXYNOS5260_TMU_REG_INTCLEAR; 7776c247393SAbhilash Kesavan } else if (data->soc == SOC_ARCH_EXYNOS7) { 7786c247393SAbhilash Kesavan tmu_intstat = EXYNOS7_TMU_REG_INTPEND; 7796c247393SAbhilash Kesavan tmu_intclear = EXYNOS7_TMU_REG_INTPEND; 780488c7455SChanwoo Choi } else if (data->soc == SOC_ARCH_EXYNOS5433) { 781488c7455SChanwoo Choi tmu_intstat = EXYNOS5433_TMU_REG_INTPEND; 782488c7455SChanwoo Choi tmu_intclear = EXYNOS5433_TMU_REG_INTPEND; 783a7331f72SBartlomiej Zolnierkiewicz } else { 784a7331f72SBartlomiej Zolnierkiewicz tmu_intstat = EXYNOS_TMU_REG_INTSTAT; 785a7331f72SBartlomiej Zolnierkiewicz tmu_intclear = EXYNOS_TMU_REG_INTCLEAR; 786a7331f72SBartlomiej Zolnierkiewicz } 787a7331f72SBartlomiej Zolnierkiewicz 788a7331f72SBartlomiej Zolnierkiewicz val_irq = readl(data->base + tmu_intstat); 789a7331f72SBartlomiej Zolnierkiewicz /* 790a7331f72SBartlomiej Zolnierkiewicz * Clear the interrupts. Please note that the documentation for 791a7331f72SBartlomiej Zolnierkiewicz * Exynos3250, Exynos4412, Exynos5250 and Exynos5260 incorrectly 792a7331f72SBartlomiej Zolnierkiewicz * states that INTCLEAR register has a different placing of bits 793a7331f72SBartlomiej Zolnierkiewicz * responsible for FALL IRQs than INTSTAT register. Exynos5420 794a7331f72SBartlomiej Zolnierkiewicz * and Exynos5440 documentation is correct (Exynos4210 doesn't 795a7331f72SBartlomiej Zolnierkiewicz * support FALL IRQs at all). 796a7331f72SBartlomiej Zolnierkiewicz */ 797a7331f72SBartlomiej Zolnierkiewicz writel(val_irq, data->base + tmu_intclear); 798a7331f72SBartlomiej Zolnierkiewicz } 799a7331f72SBartlomiej Zolnierkiewicz 80059dfa54cSAmit Daniel Kachhap static const struct of_device_id exynos_tmu_match[] = { 801fee88e2bSMaciej Purski { 802fee88e2bSMaciej Purski .compatible = "samsung,exynos3250-tmu", 803fee88e2bSMaciej Purski .data = (const void *)SOC_ARCH_EXYNOS3250, 804fee88e2bSMaciej Purski }, { 805fee88e2bSMaciej Purski .compatible = "samsung,exynos4210-tmu", 806fee88e2bSMaciej Purski .data = (const void *)SOC_ARCH_EXYNOS4210, 807fee88e2bSMaciej Purski }, { 808fee88e2bSMaciej Purski .compatible = "samsung,exynos4412-tmu", 809fee88e2bSMaciej Purski .data = (const void *)SOC_ARCH_EXYNOS4412, 810fee88e2bSMaciej Purski }, { 811fee88e2bSMaciej Purski .compatible = "samsung,exynos5250-tmu", 812fee88e2bSMaciej Purski .data = (const void *)SOC_ARCH_EXYNOS5250, 813fee88e2bSMaciej Purski }, { 814fee88e2bSMaciej Purski .compatible = "samsung,exynos5260-tmu", 815fee88e2bSMaciej Purski .data = (const void *)SOC_ARCH_EXYNOS5260, 816fee88e2bSMaciej Purski }, { 817fee88e2bSMaciej Purski .compatible = "samsung,exynos5420-tmu", 818fee88e2bSMaciej Purski .data = (const void *)SOC_ARCH_EXYNOS5420, 819fee88e2bSMaciej Purski }, { 820fee88e2bSMaciej Purski .compatible = "samsung,exynos5420-tmu-ext-triminfo", 821fee88e2bSMaciej Purski .data = (const void *)SOC_ARCH_EXYNOS5420_TRIMINFO, 822fee88e2bSMaciej Purski }, { 823fee88e2bSMaciej Purski .compatible = "samsung,exynos5433-tmu", 824fee88e2bSMaciej Purski .data = (const void *)SOC_ARCH_EXYNOS5433, 825fee88e2bSMaciej Purski }, { 826fee88e2bSMaciej Purski .compatible = "samsung,exynos7-tmu", 827fee88e2bSMaciej Purski .data = (const void *)SOC_ARCH_EXYNOS7, 828fee88e2bSMaciej Purski }, 829fee88e2bSMaciej Purski { }, 83059dfa54cSAmit Daniel Kachhap }; 83159dfa54cSAmit Daniel Kachhap MODULE_DEVICE_TABLE(of, exynos_tmu_match); 83259dfa54cSAmit Daniel Kachhap 833cebe7373SAmit Daniel Kachhap static int exynos_map_dt_data(struct platform_device *pdev) 83459dfa54cSAmit Daniel Kachhap { 835cebe7373SAmit Daniel Kachhap struct exynos_tmu_data *data = platform_get_drvdata(pdev); 836cebe7373SAmit Daniel Kachhap struct resource res; 83759dfa54cSAmit Daniel Kachhap 83873b5b1d7SSachin Kamat if (!data || !pdev->dev.of_node) 839cebe7373SAmit Daniel Kachhap return -ENODEV; 84059dfa54cSAmit Daniel Kachhap 841cebe7373SAmit Daniel Kachhap data->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); 842cebe7373SAmit Daniel Kachhap if (data->irq <= 0) { 843cebe7373SAmit Daniel Kachhap dev_err(&pdev->dev, "failed to get IRQ\n"); 844cebe7373SAmit Daniel Kachhap return -ENODEV; 845cebe7373SAmit Daniel Kachhap } 846cebe7373SAmit Daniel Kachhap 847cebe7373SAmit Daniel Kachhap if (of_address_to_resource(pdev->dev.of_node, 0, &res)) { 848cebe7373SAmit Daniel Kachhap dev_err(&pdev->dev, "failed to get Resource 0\n"); 849cebe7373SAmit Daniel Kachhap return -ENODEV; 850cebe7373SAmit Daniel Kachhap } 851cebe7373SAmit Daniel Kachhap 852cebe7373SAmit Daniel Kachhap data->base = devm_ioremap(&pdev->dev, res.start, resource_size(&res)); 853cebe7373SAmit Daniel Kachhap if (!data->base) { 854cebe7373SAmit Daniel Kachhap dev_err(&pdev->dev, "Failed to ioremap memory\n"); 855cebe7373SAmit Daniel Kachhap return -EADDRNOTAVAIL; 856cebe7373SAmit Daniel Kachhap } 857cebe7373SAmit Daniel Kachhap 8581892f9f0SKrzysztof Kozlowski data->soc = (uintptr_t)of_device_get_match_data(&pdev->dev); 85956adb9efSBartlomiej Zolnierkiewicz 86056adb9efSBartlomiej Zolnierkiewicz switch (data->soc) { 86156adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS4210: 862c8f8f768SBartlomiej Zolnierkiewicz data->tmu_set_trip_temp = exynos4210_tmu_set_trip_temp; 863c8f8f768SBartlomiej Zolnierkiewicz data->tmu_set_trip_hyst = exynos4210_tmu_set_trip_hyst; 86456adb9efSBartlomiej Zolnierkiewicz data->tmu_initialize = exynos4210_tmu_initialize; 86556adb9efSBartlomiej Zolnierkiewicz data->tmu_control = exynos4210_tmu_control; 86656adb9efSBartlomiej Zolnierkiewicz data->tmu_read = exynos4210_tmu_read; 86756adb9efSBartlomiej Zolnierkiewicz data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; 8683a3a5f15SKrzysztof Kozlowski data->ntrip = 4; 869fccfe099SBartlomiej Zolnierkiewicz data->gain = 15; 87061020d18SBartlomiej Zolnierkiewicz data->reference_voltage = 7; 871e3ed3649SBartlomiej Zolnierkiewicz data->efuse_value = 55; 872e3ed3649SBartlomiej Zolnierkiewicz data->min_efuse_value = 40; 873e3ed3649SBartlomiej Zolnierkiewicz data->max_efuse_value = 100; 87456adb9efSBartlomiej Zolnierkiewicz break; 87556adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS3250: 87656adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS4412: 87756adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS5250: 87856adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS5260: 87956adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS5420: 88056adb9efSBartlomiej Zolnierkiewicz case SOC_ARCH_EXYNOS5420_TRIMINFO: 881c8f8f768SBartlomiej Zolnierkiewicz data->tmu_set_trip_temp = exynos4412_tmu_set_trip_temp; 882c8f8f768SBartlomiej Zolnierkiewicz data->tmu_set_trip_hyst = exynos4412_tmu_set_trip_hyst; 88356adb9efSBartlomiej Zolnierkiewicz data->tmu_initialize = exynos4412_tmu_initialize; 88456adb9efSBartlomiej Zolnierkiewicz data->tmu_control = exynos4210_tmu_control; 88556adb9efSBartlomiej Zolnierkiewicz data->tmu_read = exynos4412_tmu_read; 88656adb9efSBartlomiej Zolnierkiewicz data->tmu_set_emulation = exynos4412_tmu_set_emulation; 88756adb9efSBartlomiej Zolnierkiewicz data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; 8883a3a5f15SKrzysztof Kozlowski data->ntrip = 4; 889fccfe099SBartlomiej Zolnierkiewicz data->gain = 8; 89061020d18SBartlomiej Zolnierkiewicz data->reference_voltage = 16; 891e3ed3649SBartlomiej Zolnierkiewicz data->efuse_value = 55; 892e3ed3649SBartlomiej Zolnierkiewicz if (data->soc != SOC_ARCH_EXYNOS5420 && 893e3ed3649SBartlomiej Zolnierkiewicz data->soc != SOC_ARCH_EXYNOS5420_TRIMINFO) 894e3ed3649SBartlomiej Zolnierkiewicz data->min_efuse_value = 40; 895e3ed3649SBartlomiej Zolnierkiewicz else 896e3ed3649SBartlomiej Zolnierkiewicz data->min_efuse_value = 0; 897e3ed3649SBartlomiej Zolnierkiewicz data->max_efuse_value = 100; 89856adb9efSBartlomiej Zolnierkiewicz break; 899488c7455SChanwoo Choi case SOC_ARCH_EXYNOS5433: 900c8f8f768SBartlomiej Zolnierkiewicz data->tmu_set_trip_temp = exynos5433_tmu_set_trip_temp; 901c8f8f768SBartlomiej Zolnierkiewicz data->tmu_set_trip_hyst = exynos5433_tmu_set_trip_hyst; 902488c7455SChanwoo Choi data->tmu_initialize = exynos5433_tmu_initialize; 903488c7455SChanwoo Choi data->tmu_control = exynos5433_tmu_control; 904488c7455SChanwoo Choi data->tmu_read = exynos4412_tmu_read; 905488c7455SChanwoo Choi data->tmu_set_emulation = exynos4412_tmu_set_emulation; 906488c7455SChanwoo Choi data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; 9073a3a5f15SKrzysztof Kozlowski data->ntrip = 8; 908fccfe099SBartlomiej Zolnierkiewicz data->gain = 8; 90961020d18SBartlomiej Zolnierkiewicz if (res.start == EXYNOS5433_G3D_BASE) 91061020d18SBartlomiej Zolnierkiewicz data->reference_voltage = 23; 91161020d18SBartlomiej Zolnierkiewicz else 91261020d18SBartlomiej Zolnierkiewicz data->reference_voltage = 16; 913e3ed3649SBartlomiej Zolnierkiewicz data->efuse_value = 75; 914e3ed3649SBartlomiej Zolnierkiewicz data->min_efuse_value = 40; 915e3ed3649SBartlomiej Zolnierkiewicz data->max_efuse_value = 150; 91656adb9efSBartlomiej Zolnierkiewicz break; 9176c247393SAbhilash Kesavan case SOC_ARCH_EXYNOS7: 918c8f8f768SBartlomiej Zolnierkiewicz data->tmu_set_trip_temp = exynos7_tmu_set_trip_temp; 919c8f8f768SBartlomiej Zolnierkiewicz data->tmu_set_trip_hyst = exynos7_tmu_set_trip_hyst; 9206c247393SAbhilash Kesavan data->tmu_initialize = exynos7_tmu_initialize; 9216c247393SAbhilash Kesavan data->tmu_control = exynos7_tmu_control; 9226c247393SAbhilash Kesavan data->tmu_read = exynos7_tmu_read; 9236c247393SAbhilash Kesavan data->tmu_set_emulation = exynos4412_tmu_set_emulation; 9246c247393SAbhilash Kesavan data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; 9253a3a5f15SKrzysztof Kozlowski data->ntrip = 8; 926fccfe099SBartlomiej Zolnierkiewicz data->gain = 9; 92761020d18SBartlomiej Zolnierkiewicz data->reference_voltage = 17; 928e3ed3649SBartlomiej Zolnierkiewicz data->efuse_value = 75; 929e3ed3649SBartlomiej Zolnierkiewicz data->min_efuse_value = 15; 930e3ed3649SBartlomiej Zolnierkiewicz data->max_efuse_value = 100; 9316c247393SAbhilash Kesavan break; 93256adb9efSBartlomiej Zolnierkiewicz default: 93356adb9efSBartlomiej Zolnierkiewicz dev_err(&pdev->dev, "Platform not supported\n"); 93456adb9efSBartlomiej Zolnierkiewicz return -EINVAL; 93556adb9efSBartlomiej Zolnierkiewicz } 93656adb9efSBartlomiej Zolnierkiewicz 937199b3e3cSBartlomiej Zolnierkiewicz data->cal_type = TYPE_ONE_POINT_TRIMMING; 938199b3e3cSBartlomiej Zolnierkiewicz 939d9b6ee14SAmit Daniel Kachhap /* 940d9b6ee14SAmit Daniel Kachhap * Check if the TMU shares some registers and then try to map the 941d9b6ee14SAmit Daniel Kachhap * memory of common registers. 942d9b6ee14SAmit Daniel Kachhap */ 9438014220dSKrzysztof Kozlowski if (data->soc != SOC_ARCH_EXYNOS5420_TRIMINFO) 944d9b6ee14SAmit Daniel Kachhap return 0; 945d9b6ee14SAmit Daniel Kachhap 946d9b6ee14SAmit Daniel Kachhap if (of_address_to_resource(pdev->dev.of_node, 1, &res)) { 947d9b6ee14SAmit Daniel Kachhap dev_err(&pdev->dev, "failed to get Resource 1\n"); 948d9b6ee14SAmit Daniel Kachhap return -ENODEV; 949d9b6ee14SAmit Daniel Kachhap } 950d9b6ee14SAmit Daniel Kachhap 9519025d563SNaveen Krishna Chatradhi data->base_second = devm_ioremap(&pdev->dev, res.start, 952d9b6ee14SAmit Daniel Kachhap resource_size(&res)); 9539025d563SNaveen Krishna Chatradhi if (!data->base_second) { 954d9b6ee14SAmit Daniel Kachhap dev_err(&pdev->dev, "Failed to ioremap memory\n"); 955d9b6ee14SAmit Daniel Kachhap return -ENOMEM; 956d9b6ee14SAmit Daniel Kachhap } 957cebe7373SAmit Daniel Kachhap 958cebe7373SAmit Daniel Kachhap return 0; 959cebe7373SAmit Daniel Kachhap } 960cebe7373SAmit Daniel Kachhap 9617ea98f70SDaniel Lezcano static const struct thermal_zone_device_ops exynos_sensor_ops = { 9623b6a1a80SLukasz Majewski .get_temp = exynos_get_temp, 9633b6a1a80SLukasz Majewski .set_emul_temp = exynos_tmu_set_emulation, 9643b6a1a80SLukasz Majewski }; 9653b6a1a80SLukasz Majewski 966cebe7373SAmit Daniel Kachhap static int exynos_tmu_probe(struct platform_device *pdev) 967cebe7373SAmit Daniel Kachhap { 9683b6a1a80SLukasz Majewski struct exynos_tmu_data *data; 9693b6a1a80SLukasz Majewski int ret; 970cebe7373SAmit Daniel Kachhap 97159dfa54cSAmit Daniel Kachhap data = devm_kzalloc(&pdev->dev, sizeof(struct exynos_tmu_data), 97259dfa54cSAmit Daniel Kachhap GFP_KERNEL); 9732a9675b3SJingoo Han if (!data) 97459dfa54cSAmit Daniel Kachhap return -ENOMEM; 97559dfa54cSAmit Daniel Kachhap 976cebe7373SAmit Daniel Kachhap platform_set_drvdata(pdev, data); 977cebe7373SAmit Daniel Kachhap mutex_init(&data->lock); 978cebe7373SAmit Daniel Kachhap 979824ead03SKrzysztof Kozlowski /* 980824ead03SKrzysztof Kozlowski * Try enabling the regulator if found 981824ead03SKrzysztof Kozlowski * TODO: Add regulator as an SOC feature, so that regulator enable 982824ead03SKrzysztof Kozlowski * is a compulsory call. 983824ead03SKrzysztof Kozlowski */ 9845d6976d0SMateusz Majewski ret = devm_regulator_get_enable_optional(&pdev->dev, "vtmu"); 98552ef6f56SMateusz Majewski switch (ret) { 9865d6976d0SMateusz Majewski case 0: 98752ef6f56SMateusz Majewski case -ENODEV: 98852ef6f56SMateusz Majewski break; 98952ef6f56SMateusz Majewski case -EPROBE_DEFER: 990ccb361d2SJavier Martinez Canillas return -EPROBE_DEFER; 99152ef6f56SMateusz Majewski default: 9925d6976d0SMateusz Majewski dev_err(&pdev->dev, "Failed to get enabled regulator: %d\n", 99352ef6f56SMateusz Majewski ret); 99452ef6f56SMateusz Majewski return ret; 99552ef6f56SMateusz Majewski } 996824ead03SKrzysztof Kozlowski 997cebe7373SAmit Daniel Kachhap ret = exynos_map_dt_data(pdev); 998cebe7373SAmit Daniel Kachhap if (ret) 9995d6976d0SMateusz Majewski return ret; 1000cebe7373SAmit Daniel Kachhap 100159dfa54cSAmit Daniel Kachhap data->clk = devm_clk_get(&pdev->dev, "tmu_apbif"); 100259dfa54cSAmit Daniel Kachhap if (IS_ERR(data->clk)) { 100359dfa54cSAmit Daniel Kachhap dev_err(&pdev->dev, "Failed to get clock\n"); 10045d6976d0SMateusz Majewski return PTR_ERR(data->clk); 100559dfa54cSAmit Daniel Kachhap } 100659dfa54cSAmit Daniel Kachhap 100714a11dc7SNaveen Krishna Chatradhi data->clk_sec = devm_clk_get(&pdev->dev, "tmu_triminfo_apbif"); 100814a11dc7SNaveen Krishna Chatradhi if (IS_ERR(data->clk_sec)) { 100914a11dc7SNaveen Krishna Chatradhi if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) { 101014a11dc7SNaveen Krishna Chatradhi dev_err(&pdev->dev, "Failed to get triminfo clock\n"); 10115d6976d0SMateusz Majewski return PTR_ERR(data->clk_sec); 101214a11dc7SNaveen Krishna Chatradhi } 101314a11dc7SNaveen Krishna Chatradhi } else { 101414a11dc7SNaveen Krishna Chatradhi ret = clk_prepare(data->clk_sec); 101514a11dc7SNaveen Krishna Chatradhi if (ret) { 101614a11dc7SNaveen Krishna Chatradhi dev_err(&pdev->dev, "Failed to get clock\n"); 10175d6976d0SMateusz Majewski return ret; 101814a11dc7SNaveen Krishna Chatradhi } 101914a11dc7SNaveen Krishna Chatradhi } 102014a11dc7SNaveen Krishna Chatradhi 102114a11dc7SNaveen Krishna Chatradhi ret = clk_prepare(data->clk); 102214a11dc7SNaveen Krishna Chatradhi if (ret) { 102314a11dc7SNaveen Krishna Chatradhi dev_err(&pdev->dev, "Failed to get clock\n"); 102414a11dc7SNaveen Krishna Chatradhi goto err_clk_sec; 102514a11dc7SNaveen Krishna Chatradhi } 102659dfa54cSAmit Daniel Kachhap 1027488c7455SChanwoo Choi switch (data->soc) { 1028488c7455SChanwoo Choi case SOC_ARCH_EXYNOS5433: 1029488c7455SChanwoo Choi case SOC_ARCH_EXYNOS7: 10306c247393SAbhilash Kesavan data->sclk = devm_clk_get(&pdev->dev, "tmu_sclk"); 10316c247393SAbhilash Kesavan if (IS_ERR(data->sclk)) { 10326c247393SAbhilash Kesavan dev_err(&pdev->dev, "Failed to get sclk\n"); 103302d438f6SDan Carpenter ret = PTR_ERR(data->sclk); 10346c247393SAbhilash Kesavan goto err_clk; 10356c247393SAbhilash Kesavan } else { 10366c247393SAbhilash Kesavan ret = clk_prepare_enable(data->sclk); 10376c247393SAbhilash Kesavan if (ret) { 10386c247393SAbhilash Kesavan dev_err(&pdev->dev, "Failed to enable sclk\n"); 10396c247393SAbhilash Kesavan goto err_clk; 10406c247393SAbhilash Kesavan } 10416c247393SAbhilash Kesavan } 1042488c7455SChanwoo Choi break; 1043488c7455SChanwoo Choi default: 1044488c7455SChanwoo Choi break; 1045baba1ebbSKrzysztof Kozlowski } 10466c247393SAbhilash Kesavan 10479e4249b4SKrzysztof Kozlowski /* 10489e4249b4SKrzysztof Kozlowski * data->tzd must be registered before calling exynos_tmu_initialize(), 10499e4249b4SKrzysztof Kozlowski * requesting irq and calling exynos_tmu_control(). 10509e4249b4SKrzysztof Kozlowski */ 10517ea98f70SDaniel Lezcano data->tzd = devm_thermal_of_zone_register(&pdev->dev, 0, data, 10529e4249b4SKrzysztof Kozlowski &exynos_sensor_ops); 10539e4249b4SKrzysztof Kozlowski if (IS_ERR(data->tzd)) { 10549e4249b4SKrzysztof Kozlowski ret = PTR_ERR(data->tzd); 105582bdde8eSMarek Szyprowski if (ret != -EPROBE_DEFER) 105682bdde8eSMarek Szyprowski dev_err(&pdev->dev, "Failed to register sensor: %d\n", 105782bdde8eSMarek Szyprowski ret); 10589e4249b4SKrzysztof Kozlowski goto err_sclk; 10599e4249b4SKrzysztof Kozlowski } 106059dfa54cSAmit Daniel Kachhap 106159dfa54cSAmit Daniel Kachhap ret = exynos_tmu_initialize(pdev); 106259dfa54cSAmit Daniel Kachhap if (ret) { 106359dfa54cSAmit Daniel Kachhap dev_err(&pdev->dev, "Failed to initialize TMU\n"); 10647ea98f70SDaniel Lezcano goto err_sclk; 106559dfa54cSAmit Daniel Kachhap } 106659dfa54cSAmit Daniel Kachhap 106720009a81SMateusz Majewski ret = devm_request_threaded_irq(&pdev->dev, data->irq, NULL, 106820009a81SMateusz Majewski exynos_tmu_threaded_irq, 106920009a81SMateusz Majewski IRQF_TRIGGER_RISING 107020009a81SMateusz Majewski | IRQF_SHARED | IRQF_ONESHOT, 107120009a81SMateusz Majewski dev_name(&pdev->dev), data); 1072cebe7373SAmit Daniel Kachhap if (ret) { 1073cebe7373SAmit Daniel Kachhap dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq); 10747ea98f70SDaniel Lezcano goto err_sclk; 1075cebe7373SAmit Daniel Kachhap } 107659dfa54cSAmit Daniel Kachhap 10773b6a1a80SLukasz Majewski exynos_tmu_control(pdev, true); 107859dfa54cSAmit Daniel Kachhap return 0; 10799e4249b4SKrzysztof Kozlowski 10806c247393SAbhilash Kesavan err_sclk: 10816c247393SAbhilash Kesavan clk_disable_unprepare(data->sclk); 108259dfa54cSAmit Daniel Kachhap err_clk: 108359dfa54cSAmit Daniel Kachhap clk_unprepare(data->clk); 108414a11dc7SNaveen Krishna Chatradhi err_clk_sec: 108514a11dc7SNaveen Krishna Chatradhi if (!IS_ERR(data->clk_sec)) 108614a11dc7SNaveen Krishna Chatradhi clk_unprepare(data->clk_sec); 108759dfa54cSAmit Daniel Kachhap return ret; 108859dfa54cSAmit Daniel Kachhap } 108959dfa54cSAmit Daniel Kachhap 10900b478d7bSUwe Kleine-König static void exynos_tmu_remove(struct platform_device *pdev) 109159dfa54cSAmit Daniel Kachhap { 109259dfa54cSAmit Daniel Kachhap struct exynos_tmu_data *data = platform_get_drvdata(pdev); 109359dfa54cSAmit Daniel Kachhap 10944215688eSBartlomiej Zolnierkiewicz exynos_tmu_control(pdev, false); 10954215688eSBartlomiej Zolnierkiewicz 10966c247393SAbhilash Kesavan clk_disable_unprepare(data->sclk); 109759dfa54cSAmit Daniel Kachhap clk_unprepare(data->clk); 109814a11dc7SNaveen Krishna Chatradhi if (!IS_ERR(data->clk_sec)) 109914a11dc7SNaveen Krishna Chatradhi clk_unprepare(data->clk_sec); 110059dfa54cSAmit Daniel Kachhap } 110159dfa54cSAmit Daniel Kachhap 110259dfa54cSAmit Daniel Kachhap #ifdef CONFIG_PM_SLEEP 110359dfa54cSAmit Daniel Kachhap static int exynos_tmu_suspend(struct device *dev) 110459dfa54cSAmit Daniel Kachhap { 110559dfa54cSAmit Daniel Kachhap exynos_tmu_control(to_platform_device(dev), false); 110659dfa54cSAmit Daniel Kachhap 110759dfa54cSAmit Daniel Kachhap return 0; 110859dfa54cSAmit Daniel Kachhap } 110959dfa54cSAmit Daniel Kachhap 111059dfa54cSAmit Daniel Kachhap static int exynos_tmu_resume(struct device *dev) 111159dfa54cSAmit Daniel Kachhap { 111259dfa54cSAmit Daniel Kachhap struct platform_device *pdev = to_platform_device(dev); 111359dfa54cSAmit Daniel Kachhap 111459dfa54cSAmit Daniel Kachhap exynos_tmu_initialize(pdev); 111559dfa54cSAmit Daniel Kachhap exynos_tmu_control(pdev, true); 111659dfa54cSAmit Daniel Kachhap 111759dfa54cSAmit Daniel Kachhap return 0; 111859dfa54cSAmit Daniel Kachhap } 111959dfa54cSAmit Daniel Kachhap 112059dfa54cSAmit Daniel Kachhap static SIMPLE_DEV_PM_OPS(exynos_tmu_pm, 112159dfa54cSAmit Daniel Kachhap exynos_tmu_suspend, exynos_tmu_resume); 112259dfa54cSAmit Daniel Kachhap #define EXYNOS_TMU_PM (&exynos_tmu_pm) 112359dfa54cSAmit Daniel Kachhap #else 112459dfa54cSAmit Daniel Kachhap #define EXYNOS_TMU_PM NULL 112559dfa54cSAmit Daniel Kachhap #endif 112659dfa54cSAmit Daniel Kachhap 112759dfa54cSAmit Daniel Kachhap static struct platform_driver exynos_tmu_driver = { 112859dfa54cSAmit Daniel Kachhap .driver = { 112959dfa54cSAmit Daniel Kachhap .name = "exynos-tmu", 113059dfa54cSAmit Daniel Kachhap .pm = EXYNOS_TMU_PM, 113173b5b1d7SSachin Kamat .of_match_table = exynos_tmu_match, 113259dfa54cSAmit Daniel Kachhap }, 113359dfa54cSAmit Daniel Kachhap .probe = exynos_tmu_probe, 11340b478d7bSUwe Kleine-König .remove_new = exynos_tmu_remove, 113559dfa54cSAmit Daniel Kachhap }; 113659dfa54cSAmit Daniel Kachhap 113759dfa54cSAmit Daniel Kachhap module_platform_driver(exynos_tmu_driver); 113859dfa54cSAmit Daniel Kachhap 1139ca07ee4eSKrzysztof Kozlowski MODULE_DESCRIPTION("Exynos TMU Driver"); 114059dfa54cSAmit Daniel Kachhap MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>"); 114159dfa54cSAmit Daniel Kachhap MODULE_LICENSE("GPL"); 114259dfa54cSAmit Daniel Kachhap MODULE_ALIAS("platform:exynos-tmu"); 1143