18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * exynos_tmu.c - Samsung Exynos TMU (Thermal Management Unit) 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2014 Samsung Electronics 68c2ecf20Sopenharmony_ci * Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> 78c2ecf20Sopenharmony_ci * Lukasz Majewski <l.majewski@samsung.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Copyright (C) 2011 Samsung Electronics 108c2ecf20Sopenharmony_ci * Donggeun Kim <dg77.kim@samsung.com> 118c2ecf20Sopenharmony_ci * Amit Daniel Kachhap <amit.kachhap@linaro.org> 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/clk.h> 158c2ecf20Sopenharmony_ci#include <linux/io.h> 168c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/of_device.h> 198c2ecf20Sopenharmony_ci#include <linux/of_address.h> 208c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 218c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 228c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include <dt-bindings/thermal/thermal_exynos.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "../thermal_core.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* Exynos generic registers */ 298c2ecf20Sopenharmony_ci#define EXYNOS_TMU_REG_TRIMINFO 0x0 308c2ecf20Sopenharmony_ci#define EXYNOS_TMU_REG_CONTROL 0x20 318c2ecf20Sopenharmony_ci#define EXYNOS_TMU_REG_STATUS 0x28 328c2ecf20Sopenharmony_ci#define EXYNOS_TMU_REG_CURRENT_TEMP 0x40 338c2ecf20Sopenharmony_ci#define EXYNOS_TMU_REG_INTEN 0x70 348c2ecf20Sopenharmony_ci#define EXYNOS_TMU_REG_INTSTAT 0x74 358c2ecf20Sopenharmony_ci#define EXYNOS_TMU_REG_INTCLEAR 0x78 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define EXYNOS_TMU_TEMP_MASK 0xff 388c2ecf20Sopenharmony_ci#define EXYNOS_TMU_REF_VOLTAGE_SHIFT 24 398c2ecf20Sopenharmony_ci#define EXYNOS_TMU_REF_VOLTAGE_MASK 0x1f 408c2ecf20Sopenharmony_ci#define EXYNOS_TMU_BUF_SLOPE_SEL_MASK 0xf 418c2ecf20Sopenharmony_ci#define EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT 8 428c2ecf20Sopenharmony_ci#define EXYNOS_TMU_CORE_EN_SHIFT 0 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* Exynos3250 specific registers */ 458c2ecf20Sopenharmony_ci#define EXYNOS_TMU_TRIMINFO_CON1 0x10 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* Exynos4210 specific registers */ 488c2ecf20Sopenharmony_ci#define EXYNOS4210_TMU_REG_THRESHOLD_TEMP 0x44 498c2ecf20Sopenharmony_ci#define EXYNOS4210_TMU_REG_TRIG_LEVEL0 0x50 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* Exynos5250, Exynos4412, Exynos3250 specific registers */ 528c2ecf20Sopenharmony_ci#define EXYNOS_TMU_TRIMINFO_CON2 0x14 538c2ecf20Sopenharmony_ci#define EXYNOS_THD_TEMP_RISE 0x50 548c2ecf20Sopenharmony_ci#define EXYNOS_THD_TEMP_FALL 0x54 558c2ecf20Sopenharmony_ci#define EXYNOS_EMUL_CON 0x80 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#define EXYNOS_TRIMINFO_RELOAD_ENABLE 1 588c2ecf20Sopenharmony_ci#define EXYNOS_TRIMINFO_25_SHIFT 0 598c2ecf20Sopenharmony_ci#define EXYNOS_TRIMINFO_85_SHIFT 8 608c2ecf20Sopenharmony_ci#define EXYNOS_TMU_TRIP_MODE_SHIFT 13 618c2ecf20Sopenharmony_ci#define EXYNOS_TMU_TRIP_MODE_MASK 0x7 628c2ecf20Sopenharmony_ci#define EXYNOS_TMU_THERM_TRIP_EN_SHIFT 12 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#define EXYNOS_TMU_INTEN_RISE0_SHIFT 0 658c2ecf20Sopenharmony_ci#define EXYNOS_TMU_INTEN_FALL0_SHIFT 16 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci#define EXYNOS_EMUL_TIME 0x57F0 688c2ecf20Sopenharmony_ci#define EXYNOS_EMUL_TIME_MASK 0xffff 698c2ecf20Sopenharmony_ci#define EXYNOS_EMUL_TIME_SHIFT 16 708c2ecf20Sopenharmony_ci#define EXYNOS_EMUL_DATA_SHIFT 8 718c2ecf20Sopenharmony_ci#define EXYNOS_EMUL_DATA_MASK 0xFF 728c2ecf20Sopenharmony_ci#define EXYNOS_EMUL_ENABLE 0x1 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci/* Exynos5260 specific */ 758c2ecf20Sopenharmony_ci#define EXYNOS5260_TMU_REG_INTEN 0xC0 768c2ecf20Sopenharmony_ci#define EXYNOS5260_TMU_REG_INTSTAT 0xC4 778c2ecf20Sopenharmony_ci#define EXYNOS5260_TMU_REG_INTCLEAR 0xC8 788c2ecf20Sopenharmony_ci#define EXYNOS5260_EMUL_CON 0x100 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/* Exynos4412 specific */ 818c2ecf20Sopenharmony_ci#define EXYNOS4412_MUX_ADDR_VALUE 6 828c2ecf20Sopenharmony_ci#define EXYNOS4412_MUX_ADDR_SHIFT 20 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci/* Exynos5433 specific registers */ 858c2ecf20Sopenharmony_ci#define EXYNOS5433_THD_TEMP_RISE3_0 0x050 868c2ecf20Sopenharmony_ci#define EXYNOS5433_THD_TEMP_RISE7_4 0x054 878c2ecf20Sopenharmony_ci#define EXYNOS5433_THD_TEMP_FALL3_0 0x060 888c2ecf20Sopenharmony_ci#define EXYNOS5433_THD_TEMP_FALL7_4 0x064 898c2ecf20Sopenharmony_ci#define EXYNOS5433_TMU_REG_INTEN 0x0c0 908c2ecf20Sopenharmony_ci#define EXYNOS5433_TMU_REG_INTPEND 0x0c8 918c2ecf20Sopenharmony_ci#define EXYNOS5433_TMU_EMUL_CON 0x110 928c2ecf20Sopenharmony_ci#define EXYNOS5433_TMU_PD_DET_EN 0x130 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci#define EXYNOS5433_TRIMINFO_SENSOR_ID_SHIFT 16 958c2ecf20Sopenharmony_ci#define EXYNOS5433_TRIMINFO_CALIB_SEL_SHIFT 23 968c2ecf20Sopenharmony_ci#define EXYNOS5433_TRIMINFO_SENSOR_ID_MASK \ 978c2ecf20Sopenharmony_ci (0xf << EXYNOS5433_TRIMINFO_SENSOR_ID_SHIFT) 988c2ecf20Sopenharmony_ci#define EXYNOS5433_TRIMINFO_CALIB_SEL_MASK BIT(23) 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci#define EXYNOS5433_TRIMINFO_ONE_POINT_TRIMMING 0 1018c2ecf20Sopenharmony_ci#define EXYNOS5433_TRIMINFO_TWO_POINT_TRIMMING 1 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci#define EXYNOS5433_PD_DET_EN 1 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci#define EXYNOS5433_G3D_BASE 0x10070000 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci/* Exynos7 specific registers */ 1088c2ecf20Sopenharmony_ci#define EXYNOS7_THD_TEMP_RISE7_6 0x50 1098c2ecf20Sopenharmony_ci#define EXYNOS7_THD_TEMP_FALL7_6 0x60 1108c2ecf20Sopenharmony_ci#define EXYNOS7_TMU_REG_INTEN 0x110 1118c2ecf20Sopenharmony_ci#define EXYNOS7_TMU_REG_INTPEND 0x118 1128c2ecf20Sopenharmony_ci#define EXYNOS7_TMU_REG_EMUL_CON 0x160 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci#define EXYNOS7_TMU_TEMP_MASK 0x1ff 1158c2ecf20Sopenharmony_ci#define EXYNOS7_PD_DET_EN_SHIFT 23 1168c2ecf20Sopenharmony_ci#define EXYNOS7_TMU_INTEN_RISE0_SHIFT 0 1178c2ecf20Sopenharmony_ci#define EXYNOS7_EMUL_DATA_SHIFT 7 1188c2ecf20Sopenharmony_ci#define EXYNOS7_EMUL_DATA_MASK 0x1ff 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci#define EXYNOS_FIRST_POINT_TRIM 25 1218c2ecf20Sopenharmony_ci#define EXYNOS_SECOND_POINT_TRIM 85 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci#define EXYNOS_NOISE_CANCEL_MODE 4 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci#define MCELSIUS 1000 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cienum soc_type { 1288c2ecf20Sopenharmony_ci SOC_ARCH_EXYNOS3250 = 1, 1298c2ecf20Sopenharmony_ci SOC_ARCH_EXYNOS4210, 1308c2ecf20Sopenharmony_ci SOC_ARCH_EXYNOS4412, 1318c2ecf20Sopenharmony_ci SOC_ARCH_EXYNOS5250, 1328c2ecf20Sopenharmony_ci SOC_ARCH_EXYNOS5260, 1338c2ecf20Sopenharmony_ci SOC_ARCH_EXYNOS5420, 1348c2ecf20Sopenharmony_ci SOC_ARCH_EXYNOS5420_TRIMINFO, 1358c2ecf20Sopenharmony_ci SOC_ARCH_EXYNOS5433, 1368c2ecf20Sopenharmony_ci SOC_ARCH_EXYNOS7, 1378c2ecf20Sopenharmony_ci}; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci/** 1408c2ecf20Sopenharmony_ci * struct exynos_tmu_data : A structure to hold the private data of the TMU 1418c2ecf20Sopenharmony_ci * driver 1428c2ecf20Sopenharmony_ci * @id: identifier of the one instance of the TMU controller. 1438c2ecf20Sopenharmony_ci * @base: base address of the single instance of the TMU controller. 1448c2ecf20Sopenharmony_ci * @base_second: base address of the common registers of the TMU controller. 1458c2ecf20Sopenharmony_ci * @irq: irq number of the TMU controller. 1468c2ecf20Sopenharmony_ci * @soc: id of the SOC type. 1478c2ecf20Sopenharmony_ci * @irq_work: pointer to the irq work structure. 1488c2ecf20Sopenharmony_ci * @lock: lock to implement synchronization. 1498c2ecf20Sopenharmony_ci * @clk: pointer to the clock structure. 1508c2ecf20Sopenharmony_ci * @clk_sec: pointer to the clock structure for accessing the base_second. 1518c2ecf20Sopenharmony_ci * @sclk: pointer to the clock structure for accessing the tmu special clk. 1528c2ecf20Sopenharmony_ci * @cal_type: calibration type for temperature 1538c2ecf20Sopenharmony_ci * @efuse_value: SoC defined fuse value 1548c2ecf20Sopenharmony_ci * @min_efuse_value: minimum valid trimming data 1558c2ecf20Sopenharmony_ci * @max_efuse_value: maximum valid trimming data 1568c2ecf20Sopenharmony_ci * @temp_error1: fused value of the first point trim. 1578c2ecf20Sopenharmony_ci * @temp_error2: fused value of the second point trim. 1588c2ecf20Sopenharmony_ci * @gain: gain of amplifier in the positive-TC generator block 1598c2ecf20Sopenharmony_ci * 0 < gain <= 15 1608c2ecf20Sopenharmony_ci * @reference_voltage: reference voltage of amplifier 1618c2ecf20Sopenharmony_ci * in the positive-TC generator block 1628c2ecf20Sopenharmony_ci * 0 < reference_voltage <= 31 1638c2ecf20Sopenharmony_ci * @regulator: pointer to the TMU regulator structure. 1648c2ecf20Sopenharmony_ci * @reg_conf: pointer to structure to register with core thermal. 1658c2ecf20Sopenharmony_ci * @tzd: pointer to thermal_zone_device structure 1668c2ecf20Sopenharmony_ci * @ntrip: number of supported trip points. 1678c2ecf20Sopenharmony_ci * @enabled: current status of TMU device 1688c2ecf20Sopenharmony_ci * @tmu_set_trip_temp: SoC specific method to set trip (rising threshold) 1698c2ecf20Sopenharmony_ci * @tmu_set_trip_hyst: SoC specific to set hysteresis (falling threshold) 1708c2ecf20Sopenharmony_ci * @tmu_initialize: SoC specific TMU initialization method 1718c2ecf20Sopenharmony_ci * @tmu_control: SoC specific TMU control method 1728c2ecf20Sopenharmony_ci * @tmu_read: SoC specific TMU temperature read method 1738c2ecf20Sopenharmony_ci * @tmu_set_emulation: SoC specific TMU emulation setting method 1748c2ecf20Sopenharmony_ci * @tmu_clear_irqs: SoC specific TMU interrupts clearing method 1758c2ecf20Sopenharmony_ci */ 1768c2ecf20Sopenharmony_cistruct exynos_tmu_data { 1778c2ecf20Sopenharmony_ci int id; 1788c2ecf20Sopenharmony_ci void __iomem *base; 1798c2ecf20Sopenharmony_ci void __iomem *base_second; 1808c2ecf20Sopenharmony_ci int irq; 1818c2ecf20Sopenharmony_ci enum soc_type soc; 1828c2ecf20Sopenharmony_ci struct work_struct irq_work; 1838c2ecf20Sopenharmony_ci struct mutex lock; 1848c2ecf20Sopenharmony_ci struct clk *clk, *clk_sec, *sclk; 1858c2ecf20Sopenharmony_ci u32 cal_type; 1868c2ecf20Sopenharmony_ci u32 efuse_value; 1878c2ecf20Sopenharmony_ci u32 min_efuse_value; 1888c2ecf20Sopenharmony_ci u32 max_efuse_value; 1898c2ecf20Sopenharmony_ci u16 temp_error1, temp_error2; 1908c2ecf20Sopenharmony_ci u8 gain; 1918c2ecf20Sopenharmony_ci u8 reference_voltage; 1928c2ecf20Sopenharmony_ci struct regulator *regulator; 1938c2ecf20Sopenharmony_ci struct thermal_zone_device *tzd; 1948c2ecf20Sopenharmony_ci unsigned int ntrip; 1958c2ecf20Sopenharmony_ci bool enabled; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci void (*tmu_set_trip_temp)(struct exynos_tmu_data *data, int trip, 1988c2ecf20Sopenharmony_ci u8 temp); 1998c2ecf20Sopenharmony_ci void (*tmu_set_trip_hyst)(struct exynos_tmu_data *data, int trip, 2008c2ecf20Sopenharmony_ci u8 temp, u8 hyst); 2018c2ecf20Sopenharmony_ci void (*tmu_initialize)(struct platform_device *pdev); 2028c2ecf20Sopenharmony_ci void (*tmu_control)(struct platform_device *pdev, bool on); 2038c2ecf20Sopenharmony_ci int (*tmu_read)(struct exynos_tmu_data *data); 2048c2ecf20Sopenharmony_ci void (*tmu_set_emulation)(struct exynos_tmu_data *data, int temp); 2058c2ecf20Sopenharmony_ci void (*tmu_clear_irqs)(struct exynos_tmu_data *data); 2068c2ecf20Sopenharmony_ci}; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci/* 2098c2ecf20Sopenharmony_ci * TMU treats temperature as a mapped temperature code. 2108c2ecf20Sopenharmony_ci * The temperature is converted differently depending on the calibration type. 2118c2ecf20Sopenharmony_ci */ 2128c2ecf20Sopenharmony_cistatic int temp_to_code(struct exynos_tmu_data *data, u8 temp) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci if (data->cal_type == TYPE_ONE_POINT_TRIMMING) 2158c2ecf20Sopenharmony_ci return temp + data->temp_error1 - EXYNOS_FIRST_POINT_TRIM; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci return (temp - EXYNOS_FIRST_POINT_TRIM) * 2188c2ecf20Sopenharmony_ci (data->temp_error2 - data->temp_error1) / 2198c2ecf20Sopenharmony_ci (EXYNOS_SECOND_POINT_TRIM - EXYNOS_FIRST_POINT_TRIM) + 2208c2ecf20Sopenharmony_ci data->temp_error1; 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci/* 2248c2ecf20Sopenharmony_ci * Calculate a temperature value from a temperature code. 2258c2ecf20Sopenharmony_ci * The unit of the temperature is degree Celsius. 2268c2ecf20Sopenharmony_ci */ 2278c2ecf20Sopenharmony_cistatic int code_to_temp(struct exynos_tmu_data *data, u16 temp_code) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci if (data->cal_type == TYPE_ONE_POINT_TRIMMING) 2308c2ecf20Sopenharmony_ci return temp_code - data->temp_error1 + EXYNOS_FIRST_POINT_TRIM; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci return (temp_code - data->temp_error1) * 2338c2ecf20Sopenharmony_ci (EXYNOS_SECOND_POINT_TRIM - EXYNOS_FIRST_POINT_TRIM) / 2348c2ecf20Sopenharmony_ci (data->temp_error2 - data->temp_error1) + 2358c2ecf20Sopenharmony_ci EXYNOS_FIRST_POINT_TRIM; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic void sanitize_temp_error(struct exynos_tmu_data *data, u32 trim_info) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci u16 tmu_temp_mask = 2418c2ecf20Sopenharmony_ci (data->soc == SOC_ARCH_EXYNOS7) ? EXYNOS7_TMU_TEMP_MASK 2428c2ecf20Sopenharmony_ci : EXYNOS_TMU_TEMP_MASK; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci data->temp_error1 = trim_info & tmu_temp_mask; 2458c2ecf20Sopenharmony_ci data->temp_error2 = ((trim_info >> EXYNOS_TRIMINFO_85_SHIFT) & 2468c2ecf20Sopenharmony_ci EXYNOS_TMU_TEMP_MASK); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci if (!data->temp_error1 || 2498c2ecf20Sopenharmony_ci (data->min_efuse_value > data->temp_error1) || 2508c2ecf20Sopenharmony_ci (data->temp_error1 > data->max_efuse_value)) 2518c2ecf20Sopenharmony_ci data->temp_error1 = data->efuse_value & EXYNOS_TMU_TEMP_MASK; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (!data->temp_error2) 2548c2ecf20Sopenharmony_ci data->temp_error2 = 2558c2ecf20Sopenharmony_ci (data->efuse_value >> EXYNOS_TRIMINFO_85_SHIFT) & 2568c2ecf20Sopenharmony_ci EXYNOS_TMU_TEMP_MASK; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic int exynos_tmu_initialize(struct platform_device *pdev) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci struct exynos_tmu_data *data = platform_get_drvdata(pdev); 2628c2ecf20Sopenharmony_ci struct thermal_zone_device *tzd = data->tzd; 2638c2ecf20Sopenharmony_ci const struct thermal_trip * const trips = 2648c2ecf20Sopenharmony_ci of_thermal_get_trip_points(tzd); 2658c2ecf20Sopenharmony_ci unsigned int status; 2668c2ecf20Sopenharmony_ci int ret = 0, temp, hyst; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci if (!trips) { 2698c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 2708c2ecf20Sopenharmony_ci "Cannot get trip points from device tree!\n"); 2718c2ecf20Sopenharmony_ci return -ENODEV; 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci if (data->soc != SOC_ARCH_EXYNOS5433) /* FIXME */ 2758c2ecf20Sopenharmony_ci ret = tzd->ops->get_crit_temp(tzd, &temp); 2768c2ecf20Sopenharmony_ci if (ret) { 2778c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 2788c2ecf20Sopenharmony_ci "No CRITICAL trip point defined in device tree!\n"); 2798c2ecf20Sopenharmony_ci goto out; 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (of_thermal_get_ntrips(tzd) > data->ntrip) { 2838c2ecf20Sopenharmony_ci dev_info(&pdev->dev, 2848c2ecf20Sopenharmony_ci "More trip points than supported by this TMU.\n"); 2858c2ecf20Sopenharmony_ci dev_info(&pdev->dev, 2868c2ecf20Sopenharmony_ci "%d trip points should be configured in polling mode.\n", 2878c2ecf20Sopenharmony_ci (of_thermal_get_ntrips(tzd) - data->ntrip)); 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 2918c2ecf20Sopenharmony_ci clk_enable(data->clk); 2928c2ecf20Sopenharmony_ci if (!IS_ERR(data->clk_sec)) 2938c2ecf20Sopenharmony_ci clk_enable(data->clk_sec); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci status = readb(data->base + EXYNOS_TMU_REG_STATUS); 2968c2ecf20Sopenharmony_ci if (!status) { 2978c2ecf20Sopenharmony_ci ret = -EBUSY; 2988c2ecf20Sopenharmony_ci } else { 2998c2ecf20Sopenharmony_ci int i, ntrips = 3008c2ecf20Sopenharmony_ci min_t(int, of_thermal_get_ntrips(tzd), data->ntrip); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci data->tmu_initialize(pdev); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci /* Write temperature code for rising and falling threshold */ 3058c2ecf20Sopenharmony_ci for (i = 0; i < ntrips; i++) { 3068c2ecf20Sopenharmony_ci /* Write temperature code for rising threshold */ 3078c2ecf20Sopenharmony_ci ret = tzd->ops->get_trip_temp(tzd, i, &temp); 3088c2ecf20Sopenharmony_ci if (ret) 3098c2ecf20Sopenharmony_ci goto err; 3108c2ecf20Sopenharmony_ci temp /= MCELSIUS; 3118c2ecf20Sopenharmony_ci data->tmu_set_trip_temp(data, i, temp); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci /* Write temperature code for falling threshold */ 3148c2ecf20Sopenharmony_ci ret = tzd->ops->get_trip_hyst(tzd, i, &hyst); 3158c2ecf20Sopenharmony_ci if (ret) 3168c2ecf20Sopenharmony_ci goto err; 3178c2ecf20Sopenharmony_ci hyst /= MCELSIUS; 3188c2ecf20Sopenharmony_ci data->tmu_set_trip_hyst(data, i, temp, hyst); 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci data->tmu_clear_irqs(data); 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_cierr: 3248c2ecf20Sopenharmony_ci clk_disable(data->clk); 3258c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 3268c2ecf20Sopenharmony_ci if (!IS_ERR(data->clk_sec)) 3278c2ecf20Sopenharmony_ci clk_disable(data->clk_sec); 3288c2ecf20Sopenharmony_ciout: 3298c2ecf20Sopenharmony_ci return ret; 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic u32 get_con_reg(struct exynos_tmu_data *data, u32 con) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci if (data->soc == SOC_ARCH_EXYNOS4412 || 3358c2ecf20Sopenharmony_ci data->soc == SOC_ARCH_EXYNOS3250) 3368c2ecf20Sopenharmony_ci con |= (EXYNOS4412_MUX_ADDR_VALUE << EXYNOS4412_MUX_ADDR_SHIFT); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci con &= ~(EXYNOS_TMU_REF_VOLTAGE_MASK << EXYNOS_TMU_REF_VOLTAGE_SHIFT); 3398c2ecf20Sopenharmony_ci con |= data->reference_voltage << EXYNOS_TMU_REF_VOLTAGE_SHIFT; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci con &= ~(EXYNOS_TMU_BUF_SLOPE_SEL_MASK << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT); 3428c2ecf20Sopenharmony_ci con |= (data->gain << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci con &= ~(EXYNOS_TMU_TRIP_MODE_MASK << EXYNOS_TMU_TRIP_MODE_SHIFT); 3458c2ecf20Sopenharmony_ci con |= (EXYNOS_NOISE_CANCEL_MODE << EXYNOS_TMU_TRIP_MODE_SHIFT); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci return con; 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cistatic void exynos_tmu_control(struct platform_device *pdev, bool on) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci struct exynos_tmu_data *data = platform_get_drvdata(pdev); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 3558c2ecf20Sopenharmony_ci clk_enable(data->clk); 3568c2ecf20Sopenharmony_ci data->tmu_control(pdev, on); 3578c2ecf20Sopenharmony_ci data->enabled = on; 3588c2ecf20Sopenharmony_ci clk_disable(data->clk); 3598c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_cistatic void exynos4210_tmu_set_trip_temp(struct exynos_tmu_data *data, 3638c2ecf20Sopenharmony_ci int trip, u8 temp) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci const struct thermal_trip * const trips = 3668c2ecf20Sopenharmony_ci of_thermal_get_trip_points(data->tzd); 3678c2ecf20Sopenharmony_ci u8 ref, th_code; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci ref = trips[0].temperature / MCELSIUS; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci if (trip == 0) { 3728c2ecf20Sopenharmony_ci th_code = temp_to_code(data, ref); 3738c2ecf20Sopenharmony_ci writeb(th_code, data->base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP); 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci temp -= ref; 3778c2ecf20Sopenharmony_ci writeb(temp, data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL0 + trip * 4); 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci/* failing thresholds are not supported on Exynos4210 */ 3818c2ecf20Sopenharmony_cistatic void exynos4210_tmu_set_trip_hyst(struct exynos_tmu_data *data, 3828c2ecf20Sopenharmony_ci int trip, u8 temp, u8 hyst) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistatic void exynos4210_tmu_initialize(struct platform_device *pdev) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci struct exynos_tmu_data *data = platform_get_drvdata(pdev); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci sanitize_temp_error(data, readl(data->base + EXYNOS_TMU_REG_TRIMINFO)); 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_cistatic void exynos4412_tmu_set_trip_temp(struct exynos_tmu_data *data, 3948c2ecf20Sopenharmony_ci int trip, u8 temp) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci u32 th, con; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci th = readl(data->base + EXYNOS_THD_TEMP_RISE); 3998c2ecf20Sopenharmony_ci th &= ~(0xff << 8 * trip); 4008c2ecf20Sopenharmony_ci th |= temp_to_code(data, temp) << 8 * trip; 4018c2ecf20Sopenharmony_ci writel(th, data->base + EXYNOS_THD_TEMP_RISE); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci if (trip == 3) { 4048c2ecf20Sopenharmony_ci con = readl(data->base + EXYNOS_TMU_REG_CONTROL); 4058c2ecf20Sopenharmony_ci con |= (1 << EXYNOS_TMU_THERM_TRIP_EN_SHIFT); 4068c2ecf20Sopenharmony_ci writel(con, data->base + EXYNOS_TMU_REG_CONTROL); 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic void exynos4412_tmu_set_trip_hyst(struct exynos_tmu_data *data, 4118c2ecf20Sopenharmony_ci int trip, u8 temp, u8 hyst) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci u32 th; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci th = readl(data->base + EXYNOS_THD_TEMP_FALL); 4168c2ecf20Sopenharmony_ci th &= ~(0xff << 8 * trip); 4178c2ecf20Sopenharmony_ci if (hyst) 4188c2ecf20Sopenharmony_ci th |= temp_to_code(data, temp - hyst) << 8 * trip; 4198c2ecf20Sopenharmony_ci writel(th, data->base + EXYNOS_THD_TEMP_FALL); 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic void exynos4412_tmu_initialize(struct platform_device *pdev) 4238c2ecf20Sopenharmony_ci{ 4248c2ecf20Sopenharmony_ci struct exynos_tmu_data *data = platform_get_drvdata(pdev); 4258c2ecf20Sopenharmony_ci unsigned int trim_info, ctrl; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci if (data->soc == SOC_ARCH_EXYNOS3250 || 4288c2ecf20Sopenharmony_ci data->soc == SOC_ARCH_EXYNOS4412 || 4298c2ecf20Sopenharmony_ci data->soc == SOC_ARCH_EXYNOS5250) { 4308c2ecf20Sopenharmony_ci if (data->soc == SOC_ARCH_EXYNOS3250) { 4318c2ecf20Sopenharmony_ci ctrl = readl(data->base + EXYNOS_TMU_TRIMINFO_CON1); 4328c2ecf20Sopenharmony_ci ctrl |= EXYNOS_TRIMINFO_RELOAD_ENABLE; 4338c2ecf20Sopenharmony_ci writel(ctrl, data->base + EXYNOS_TMU_TRIMINFO_CON1); 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci ctrl = readl(data->base + EXYNOS_TMU_TRIMINFO_CON2); 4368c2ecf20Sopenharmony_ci ctrl |= EXYNOS_TRIMINFO_RELOAD_ENABLE; 4378c2ecf20Sopenharmony_ci writel(ctrl, data->base + EXYNOS_TMU_TRIMINFO_CON2); 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci /* On exynos5420 the triminfo register is in the shared space */ 4418c2ecf20Sopenharmony_ci if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) 4428c2ecf20Sopenharmony_ci trim_info = readl(data->base_second + EXYNOS_TMU_REG_TRIMINFO); 4438c2ecf20Sopenharmony_ci else 4448c2ecf20Sopenharmony_ci trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci sanitize_temp_error(data, trim_info); 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic void exynos5433_tmu_set_trip_temp(struct exynos_tmu_data *data, 4508c2ecf20Sopenharmony_ci int trip, u8 temp) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci unsigned int reg_off, j; 4538c2ecf20Sopenharmony_ci u32 th; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci if (trip > 3) { 4568c2ecf20Sopenharmony_ci reg_off = EXYNOS5433_THD_TEMP_RISE7_4; 4578c2ecf20Sopenharmony_ci j = trip - 4; 4588c2ecf20Sopenharmony_ci } else { 4598c2ecf20Sopenharmony_ci reg_off = EXYNOS5433_THD_TEMP_RISE3_0; 4608c2ecf20Sopenharmony_ci j = trip; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci th = readl(data->base + reg_off); 4648c2ecf20Sopenharmony_ci th &= ~(0xff << j * 8); 4658c2ecf20Sopenharmony_ci th |= (temp_to_code(data, temp) << j * 8); 4668c2ecf20Sopenharmony_ci writel(th, data->base + reg_off); 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_cistatic void exynos5433_tmu_set_trip_hyst(struct exynos_tmu_data *data, 4708c2ecf20Sopenharmony_ci int trip, u8 temp, u8 hyst) 4718c2ecf20Sopenharmony_ci{ 4728c2ecf20Sopenharmony_ci unsigned int reg_off, j; 4738c2ecf20Sopenharmony_ci u32 th; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci if (trip > 3) { 4768c2ecf20Sopenharmony_ci reg_off = EXYNOS5433_THD_TEMP_FALL7_4; 4778c2ecf20Sopenharmony_ci j = trip - 4; 4788c2ecf20Sopenharmony_ci } else { 4798c2ecf20Sopenharmony_ci reg_off = EXYNOS5433_THD_TEMP_FALL3_0; 4808c2ecf20Sopenharmony_ci j = trip; 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci th = readl(data->base + reg_off); 4848c2ecf20Sopenharmony_ci th &= ~(0xff << j * 8); 4858c2ecf20Sopenharmony_ci th |= (temp_to_code(data, temp - hyst) << j * 8); 4868c2ecf20Sopenharmony_ci writel(th, data->base + reg_off); 4878c2ecf20Sopenharmony_ci} 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_cistatic void exynos5433_tmu_initialize(struct platform_device *pdev) 4908c2ecf20Sopenharmony_ci{ 4918c2ecf20Sopenharmony_ci struct exynos_tmu_data *data = platform_get_drvdata(pdev); 4928c2ecf20Sopenharmony_ci unsigned int trim_info; 4938c2ecf20Sopenharmony_ci int sensor_id, cal_type; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); 4968c2ecf20Sopenharmony_ci sanitize_temp_error(data, trim_info); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci /* Read the temperature sensor id */ 4998c2ecf20Sopenharmony_ci sensor_id = (trim_info & EXYNOS5433_TRIMINFO_SENSOR_ID_MASK) 5008c2ecf20Sopenharmony_ci >> EXYNOS5433_TRIMINFO_SENSOR_ID_SHIFT; 5018c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "Temperature sensor ID: 0x%x\n", sensor_id); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci /* Read the calibration mode */ 5048c2ecf20Sopenharmony_ci writel(trim_info, data->base + EXYNOS_TMU_REG_TRIMINFO); 5058c2ecf20Sopenharmony_ci cal_type = (trim_info & EXYNOS5433_TRIMINFO_CALIB_SEL_MASK) 5068c2ecf20Sopenharmony_ci >> EXYNOS5433_TRIMINFO_CALIB_SEL_SHIFT; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci switch (cal_type) { 5098c2ecf20Sopenharmony_ci case EXYNOS5433_TRIMINFO_TWO_POINT_TRIMMING: 5108c2ecf20Sopenharmony_ci data->cal_type = TYPE_TWO_POINT_TRIMMING; 5118c2ecf20Sopenharmony_ci break; 5128c2ecf20Sopenharmony_ci case EXYNOS5433_TRIMINFO_ONE_POINT_TRIMMING: 5138c2ecf20Sopenharmony_ci default: 5148c2ecf20Sopenharmony_ci data->cal_type = TYPE_ONE_POINT_TRIMMING; 5158c2ecf20Sopenharmony_ci break; 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "Calibration type is %d-point calibration\n", 5198c2ecf20Sopenharmony_ci cal_type ? 2 : 1); 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_cistatic void exynos7_tmu_set_trip_temp(struct exynos_tmu_data *data, 5238c2ecf20Sopenharmony_ci int trip, u8 temp) 5248c2ecf20Sopenharmony_ci{ 5258c2ecf20Sopenharmony_ci unsigned int reg_off, bit_off; 5268c2ecf20Sopenharmony_ci u32 th; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci reg_off = ((7 - trip) / 2) * 4; 5298c2ecf20Sopenharmony_ci bit_off = ((8 - trip) % 2); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci th = readl(data->base + EXYNOS7_THD_TEMP_RISE7_6 + reg_off); 5328c2ecf20Sopenharmony_ci th &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off)); 5338c2ecf20Sopenharmony_ci th |= temp_to_code(data, temp) << (16 * bit_off); 5348c2ecf20Sopenharmony_ci writel(th, data->base + EXYNOS7_THD_TEMP_RISE7_6 + reg_off); 5358c2ecf20Sopenharmony_ci} 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_cistatic void exynos7_tmu_set_trip_hyst(struct exynos_tmu_data *data, 5388c2ecf20Sopenharmony_ci int trip, u8 temp, u8 hyst) 5398c2ecf20Sopenharmony_ci{ 5408c2ecf20Sopenharmony_ci unsigned int reg_off, bit_off; 5418c2ecf20Sopenharmony_ci u32 th; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci reg_off = ((7 - trip) / 2) * 4; 5448c2ecf20Sopenharmony_ci bit_off = ((8 - trip) % 2); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci th = readl(data->base + EXYNOS7_THD_TEMP_FALL7_6 + reg_off); 5478c2ecf20Sopenharmony_ci th &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off)); 5488c2ecf20Sopenharmony_ci th |= temp_to_code(data, temp - hyst) << (16 * bit_off); 5498c2ecf20Sopenharmony_ci writel(th, data->base + EXYNOS7_THD_TEMP_FALL7_6 + reg_off); 5508c2ecf20Sopenharmony_ci} 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_cistatic void exynos7_tmu_initialize(struct platform_device *pdev) 5538c2ecf20Sopenharmony_ci{ 5548c2ecf20Sopenharmony_ci struct exynos_tmu_data *data = platform_get_drvdata(pdev); 5558c2ecf20Sopenharmony_ci unsigned int trim_info; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); 5588c2ecf20Sopenharmony_ci sanitize_temp_error(data, trim_info); 5598c2ecf20Sopenharmony_ci} 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_cistatic void exynos4210_tmu_control(struct platform_device *pdev, bool on) 5628c2ecf20Sopenharmony_ci{ 5638c2ecf20Sopenharmony_ci struct exynos_tmu_data *data = platform_get_drvdata(pdev); 5648c2ecf20Sopenharmony_ci struct thermal_zone_device *tz = data->tzd; 5658c2ecf20Sopenharmony_ci unsigned int con, interrupt_en = 0, i; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL)); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci if (on) { 5708c2ecf20Sopenharmony_ci for (i = 0; i < data->ntrip; i++) { 5718c2ecf20Sopenharmony_ci if (!of_thermal_is_trip_valid(tz, i)) 5728c2ecf20Sopenharmony_ci continue; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci interrupt_en |= 5758c2ecf20Sopenharmony_ci (1 << (EXYNOS_TMU_INTEN_RISE0_SHIFT + i * 4)); 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci if (data->soc != SOC_ARCH_EXYNOS4210) 5798c2ecf20Sopenharmony_ci interrupt_en |= 5808c2ecf20Sopenharmony_ci interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); 5838c2ecf20Sopenharmony_ci } else { 5848c2ecf20Sopenharmony_ci con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci writel(interrupt_en, data->base + EXYNOS_TMU_REG_INTEN); 5888c2ecf20Sopenharmony_ci writel(con, data->base + EXYNOS_TMU_REG_CONTROL); 5898c2ecf20Sopenharmony_ci} 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_cistatic void exynos5433_tmu_control(struct platform_device *pdev, bool on) 5928c2ecf20Sopenharmony_ci{ 5938c2ecf20Sopenharmony_ci struct exynos_tmu_data *data = platform_get_drvdata(pdev); 5948c2ecf20Sopenharmony_ci struct thermal_zone_device *tz = data->tzd; 5958c2ecf20Sopenharmony_ci unsigned int con, interrupt_en = 0, pd_det_en, i; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL)); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci if (on) { 6008c2ecf20Sopenharmony_ci for (i = 0; i < data->ntrip; i++) { 6018c2ecf20Sopenharmony_ci if (!of_thermal_is_trip_valid(tz, i)) 6028c2ecf20Sopenharmony_ci continue; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci interrupt_en |= 6058c2ecf20Sopenharmony_ci (1 << (EXYNOS7_TMU_INTEN_RISE0_SHIFT + i)); 6068c2ecf20Sopenharmony_ci } 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci interrupt_en |= 6098c2ecf20Sopenharmony_ci interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); 6128c2ecf20Sopenharmony_ci } else 6138c2ecf20Sopenharmony_ci con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci pd_det_en = on ? EXYNOS5433_PD_DET_EN : 0; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci writel(pd_det_en, data->base + EXYNOS5433_TMU_PD_DET_EN); 6188c2ecf20Sopenharmony_ci writel(interrupt_en, data->base + EXYNOS5433_TMU_REG_INTEN); 6198c2ecf20Sopenharmony_ci writel(con, data->base + EXYNOS_TMU_REG_CONTROL); 6208c2ecf20Sopenharmony_ci} 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_cistatic void exynos7_tmu_control(struct platform_device *pdev, bool on) 6238c2ecf20Sopenharmony_ci{ 6248c2ecf20Sopenharmony_ci struct exynos_tmu_data *data = platform_get_drvdata(pdev); 6258c2ecf20Sopenharmony_ci struct thermal_zone_device *tz = data->tzd; 6268c2ecf20Sopenharmony_ci unsigned int con, interrupt_en = 0, i; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL)); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci if (on) { 6318c2ecf20Sopenharmony_ci for (i = 0; i < data->ntrip; i++) { 6328c2ecf20Sopenharmony_ci if (!of_thermal_is_trip_valid(tz, i)) 6338c2ecf20Sopenharmony_ci continue; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci interrupt_en |= 6368c2ecf20Sopenharmony_ci (1 << (EXYNOS7_TMU_INTEN_RISE0_SHIFT + i)); 6378c2ecf20Sopenharmony_ci } 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci interrupt_en |= 6408c2ecf20Sopenharmony_ci interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); 6438c2ecf20Sopenharmony_ci con |= (1 << EXYNOS7_PD_DET_EN_SHIFT); 6448c2ecf20Sopenharmony_ci } else { 6458c2ecf20Sopenharmony_ci con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); 6468c2ecf20Sopenharmony_ci con &= ~(1 << EXYNOS7_PD_DET_EN_SHIFT); 6478c2ecf20Sopenharmony_ci } 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci writel(interrupt_en, data->base + EXYNOS7_TMU_REG_INTEN); 6508c2ecf20Sopenharmony_ci writel(con, data->base + EXYNOS_TMU_REG_CONTROL); 6518c2ecf20Sopenharmony_ci} 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_cistatic int exynos_get_temp(void *p, int *temp) 6548c2ecf20Sopenharmony_ci{ 6558c2ecf20Sopenharmony_ci struct exynos_tmu_data *data = p; 6568c2ecf20Sopenharmony_ci int value, ret = 0; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci if (!data || !data->tmu_read) 6598c2ecf20Sopenharmony_ci return -EINVAL; 6608c2ecf20Sopenharmony_ci else if (!data->enabled) 6618c2ecf20Sopenharmony_ci /* 6628c2ecf20Sopenharmony_ci * Called too early, probably 6638c2ecf20Sopenharmony_ci * from thermal_zone_of_sensor_register(). 6648c2ecf20Sopenharmony_ci */ 6658c2ecf20Sopenharmony_ci return -EAGAIN; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 6688c2ecf20Sopenharmony_ci clk_enable(data->clk); 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci value = data->tmu_read(data); 6718c2ecf20Sopenharmony_ci if (value < 0) 6728c2ecf20Sopenharmony_ci ret = value; 6738c2ecf20Sopenharmony_ci else 6748c2ecf20Sopenharmony_ci *temp = code_to_temp(data, value) * MCELSIUS; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci clk_disable(data->clk); 6778c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci return ret; 6808c2ecf20Sopenharmony_ci} 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci#ifdef CONFIG_THERMAL_EMULATION 6838c2ecf20Sopenharmony_cistatic u32 get_emul_con_reg(struct exynos_tmu_data *data, unsigned int val, 6848c2ecf20Sopenharmony_ci int temp) 6858c2ecf20Sopenharmony_ci{ 6868c2ecf20Sopenharmony_ci if (temp) { 6878c2ecf20Sopenharmony_ci temp /= MCELSIUS; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci val &= ~(EXYNOS_EMUL_TIME_MASK << EXYNOS_EMUL_TIME_SHIFT); 6908c2ecf20Sopenharmony_ci val |= (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT); 6918c2ecf20Sopenharmony_ci if (data->soc == SOC_ARCH_EXYNOS7) { 6928c2ecf20Sopenharmony_ci val &= ~(EXYNOS7_EMUL_DATA_MASK << 6938c2ecf20Sopenharmony_ci EXYNOS7_EMUL_DATA_SHIFT); 6948c2ecf20Sopenharmony_ci val |= (temp_to_code(data, temp) << 6958c2ecf20Sopenharmony_ci EXYNOS7_EMUL_DATA_SHIFT) | 6968c2ecf20Sopenharmony_ci EXYNOS_EMUL_ENABLE; 6978c2ecf20Sopenharmony_ci } else { 6988c2ecf20Sopenharmony_ci val &= ~(EXYNOS_EMUL_DATA_MASK << 6998c2ecf20Sopenharmony_ci EXYNOS_EMUL_DATA_SHIFT); 7008c2ecf20Sopenharmony_ci val |= (temp_to_code(data, temp) << 7018c2ecf20Sopenharmony_ci EXYNOS_EMUL_DATA_SHIFT) | 7028c2ecf20Sopenharmony_ci EXYNOS_EMUL_ENABLE; 7038c2ecf20Sopenharmony_ci } 7048c2ecf20Sopenharmony_ci } else { 7058c2ecf20Sopenharmony_ci val &= ~EXYNOS_EMUL_ENABLE; 7068c2ecf20Sopenharmony_ci } 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci return val; 7098c2ecf20Sopenharmony_ci} 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_cistatic void exynos4412_tmu_set_emulation(struct exynos_tmu_data *data, 7128c2ecf20Sopenharmony_ci int temp) 7138c2ecf20Sopenharmony_ci{ 7148c2ecf20Sopenharmony_ci unsigned int val; 7158c2ecf20Sopenharmony_ci u32 emul_con; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci if (data->soc == SOC_ARCH_EXYNOS5260) 7188c2ecf20Sopenharmony_ci emul_con = EXYNOS5260_EMUL_CON; 7198c2ecf20Sopenharmony_ci else if (data->soc == SOC_ARCH_EXYNOS5433) 7208c2ecf20Sopenharmony_ci emul_con = EXYNOS5433_TMU_EMUL_CON; 7218c2ecf20Sopenharmony_ci else if (data->soc == SOC_ARCH_EXYNOS7) 7228c2ecf20Sopenharmony_ci emul_con = EXYNOS7_TMU_REG_EMUL_CON; 7238c2ecf20Sopenharmony_ci else 7248c2ecf20Sopenharmony_ci emul_con = EXYNOS_EMUL_CON; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci val = readl(data->base + emul_con); 7278c2ecf20Sopenharmony_ci val = get_emul_con_reg(data, val, temp); 7288c2ecf20Sopenharmony_ci writel(val, data->base + emul_con); 7298c2ecf20Sopenharmony_ci} 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_cistatic int exynos_tmu_set_emulation(void *drv_data, int temp) 7328c2ecf20Sopenharmony_ci{ 7338c2ecf20Sopenharmony_ci struct exynos_tmu_data *data = drv_data; 7348c2ecf20Sopenharmony_ci int ret = -EINVAL; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci if (data->soc == SOC_ARCH_EXYNOS4210) 7378c2ecf20Sopenharmony_ci goto out; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci if (temp && temp < MCELSIUS) 7408c2ecf20Sopenharmony_ci goto out; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 7438c2ecf20Sopenharmony_ci clk_enable(data->clk); 7448c2ecf20Sopenharmony_ci data->tmu_set_emulation(data, temp); 7458c2ecf20Sopenharmony_ci clk_disable(data->clk); 7468c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 7478c2ecf20Sopenharmony_ci return 0; 7488c2ecf20Sopenharmony_ciout: 7498c2ecf20Sopenharmony_ci return ret; 7508c2ecf20Sopenharmony_ci} 7518c2ecf20Sopenharmony_ci#else 7528c2ecf20Sopenharmony_ci#define exynos4412_tmu_set_emulation NULL 7538c2ecf20Sopenharmony_cistatic int exynos_tmu_set_emulation(void *drv_data, int temp) 7548c2ecf20Sopenharmony_ci { return -EINVAL; } 7558c2ecf20Sopenharmony_ci#endif /* CONFIG_THERMAL_EMULATION */ 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_cistatic int exynos4210_tmu_read(struct exynos_tmu_data *data) 7588c2ecf20Sopenharmony_ci{ 7598c2ecf20Sopenharmony_ci int ret = readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP); 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci /* "temp_code" should range between 75 and 175 */ 7628c2ecf20Sopenharmony_ci return (ret < 75 || ret > 175) ? -ENODATA : ret; 7638c2ecf20Sopenharmony_ci} 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_cistatic int exynos4412_tmu_read(struct exynos_tmu_data *data) 7668c2ecf20Sopenharmony_ci{ 7678c2ecf20Sopenharmony_ci return readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP); 7688c2ecf20Sopenharmony_ci} 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_cistatic int exynos7_tmu_read(struct exynos_tmu_data *data) 7718c2ecf20Sopenharmony_ci{ 7728c2ecf20Sopenharmony_ci return readw(data->base + EXYNOS_TMU_REG_CURRENT_TEMP) & 7738c2ecf20Sopenharmony_ci EXYNOS7_TMU_TEMP_MASK; 7748c2ecf20Sopenharmony_ci} 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_cistatic void exynos_tmu_work(struct work_struct *work) 7778c2ecf20Sopenharmony_ci{ 7788c2ecf20Sopenharmony_ci struct exynos_tmu_data *data = container_of(work, 7798c2ecf20Sopenharmony_ci struct exynos_tmu_data, irq_work); 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci thermal_zone_device_update(data->tzd, THERMAL_EVENT_UNSPECIFIED); 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 7848c2ecf20Sopenharmony_ci clk_enable(data->clk); 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci /* TODO: take action based on particular interrupt */ 7878c2ecf20Sopenharmony_ci data->tmu_clear_irqs(data); 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci clk_disable(data->clk); 7908c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 7918c2ecf20Sopenharmony_ci enable_irq(data->irq); 7928c2ecf20Sopenharmony_ci} 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_cistatic void exynos4210_tmu_clear_irqs(struct exynos_tmu_data *data) 7958c2ecf20Sopenharmony_ci{ 7968c2ecf20Sopenharmony_ci unsigned int val_irq; 7978c2ecf20Sopenharmony_ci u32 tmu_intstat, tmu_intclear; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci if (data->soc == SOC_ARCH_EXYNOS5260) { 8008c2ecf20Sopenharmony_ci tmu_intstat = EXYNOS5260_TMU_REG_INTSTAT; 8018c2ecf20Sopenharmony_ci tmu_intclear = EXYNOS5260_TMU_REG_INTCLEAR; 8028c2ecf20Sopenharmony_ci } else if (data->soc == SOC_ARCH_EXYNOS7) { 8038c2ecf20Sopenharmony_ci tmu_intstat = EXYNOS7_TMU_REG_INTPEND; 8048c2ecf20Sopenharmony_ci tmu_intclear = EXYNOS7_TMU_REG_INTPEND; 8058c2ecf20Sopenharmony_ci } else if (data->soc == SOC_ARCH_EXYNOS5433) { 8068c2ecf20Sopenharmony_ci tmu_intstat = EXYNOS5433_TMU_REG_INTPEND; 8078c2ecf20Sopenharmony_ci tmu_intclear = EXYNOS5433_TMU_REG_INTPEND; 8088c2ecf20Sopenharmony_ci } else { 8098c2ecf20Sopenharmony_ci tmu_intstat = EXYNOS_TMU_REG_INTSTAT; 8108c2ecf20Sopenharmony_ci tmu_intclear = EXYNOS_TMU_REG_INTCLEAR; 8118c2ecf20Sopenharmony_ci } 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci val_irq = readl(data->base + tmu_intstat); 8148c2ecf20Sopenharmony_ci /* 8158c2ecf20Sopenharmony_ci * Clear the interrupts. Please note that the documentation for 8168c2ecf20Sopenharmony_ci * Exynos3250, Exynos4412, Exynos5250 and Exynos5260 incorrectly 8178c2ecf20Sopenharmony_ci * states that INTCLEAR register has a different placing of bits 8188c2ecf20Sopenharmony_ci * responsible for FALL IRQs than INTSTAT register. Exynos5420 8198c2ecf20Sopenharmony_ci * and Exynos5440 documentation is correct (Exynos4210 doesn't 8208c2ecf20Sopenharmony_ci * support FALL IRQs at all). 8218c2ecf20Sopenharmony_ci */ 8228c2ecf20Sopenharmony_ci writel(val_irq, data->base + tmu_intclear); 8238c2ecf20Sopenharmony_ci} 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_cistatic irqreturn_t exynos_tmu_irq(int irq, void *id) 8268c2ecf20Sopenharmony_ci{ 8278c2ecf20Sopenharmony_ci struct exynos_tmu_data *data = id; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci disable_irq_nosync(irq); 8308c2ecf20Sopenharmony_ci schedule_work(&data->irq_work); 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci return IRQ_HANDLED; 8338c2ecf20Sopenharmony_ci} 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_cistatic const struct of_device_id exynos_tmu_match[] = { 8368c2ecf20Sopenharmony_ci { 8378c2ecf20Sopenharmony_ci .compatible = "samsung,exynos3250-tmu", 8388c2ecf20Sopenharmony_ci .data = (const void *)SOC_ARCH_EXYNOS3250, 8398c2ecf20Sopenharmony_ci }, { 8408c2ecf20Sopenharmony_ci .compatible = "samsung,exynos4210-tmu", 8418c2ecf20Sopenharmony_ci .data = (const void *)SOC_ARCH_EXYNOS4210, 8428c2ecf20Sopenharmony_ci }, { 8438c2ecf20Sopenharmony_ci .compatible = "samsung,exynos4412-tmu", 8448c2ecf20Sopenharmony_ci .data = (const void *)SOC_ARCH_EXYNOS4412, 8458c2ecf20Sopenharmony_ci }, { 8468c2ecf20Sopenharmony_ci .compatible = "samsung,exynos5250-tmu", 8478c2ecf20Sopenharmony_ci .data = (const void *)SOC_ARCH_EXYNOS5250, 8488c2ecf20Sopenharmony_ci }, { 8498c2ecf20Sopenharmony_ci .compatible = "samsung,exynos5260-tmu", 8508c2ecf20Sopenharmony_ci .data = (const void *)SOC_ARCH_EXYNOS5260, 8518c2ecf20Sopenharmony_ci }, { 8528c2ecf20Sopenharmony_ci .compatible = "samsung,exynos5420-tmu", 8538c2ecf20Sopenharmony_ci .data = (const void *)SOC_ARCH_EXYNOS5420, 8548c2ecf20Sopenharmony_ci }, { 8558c2ecf20Sopenharmony_ci .compatible = "samsung,exynos5420-tmu-ext-triminfo", 8568c2ecf20Sopenharmony_ci .data = (const void *)SOC_ARCH_EXYNOS5420_TRIMINFO, 8578c2ecf20Sopenharmony_ci }, { 8588c2ecf20Sopenharmony_ci .compatible = "samsung,exynos5433-tmu", 8598c2ecf20Sopenharmony_ci .data = (const void *)SOC_ARCH_EXYNOS5433, 8608c2ecf20Sopenharmony_ci }, { 8618c2ecf20Sopenharmony_ci .compatible = "samsung,exynos7-tmu", 8628c2ecf20Sopenharmony_ci .data = (const void *)SOC_ARCH_EXYNOS7, 8638c2ecf20Sopenharmony_ci }, 8648c2ecf20Sopenharmony_ci { }, 8658c2ecf20Sopenharmony_ci}; 8668c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, exynos_tmu_match); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_cistatic int exynos_map_dt_data(struct platform_device *pdev) 8698c2ecf20Sopenharmony_ci{ 8708c2ecf20Sopenharmony_ci struct exynos_tmu_data *data = platform_get_drvdata(pdev); 8718c2ecf20Sopenharmony_ci struct resource res; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci if (!data || !pdev->dev.of_node) 8748c2ecf20Sopenharmony_ci return -ENODEV; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci data->id = of_alias_get_id(pdev->dev.of_node, "tmuctrl"); 8778c2ecf20Sopenharmony_ci if (data->id < 0) 8788c2ecf20Sopenharmony_ci data->id = 0; 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci data->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); 8818c2ecf20Sopenharmony_ci if (data->irq <= 0) { 8828c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get IRQ\n"); 8838c2ecf20Sopenharmony_ci return -ENODEV; 8848c2ecf20Sopenharmony_ci } 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci if (of_address_to_resource(pdev->dev.of_node, 0, &res)) { 8878c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get Resource 0\n"); 8888c2ecf20Sopenharmony_ci return -ENODEV; 8898c2ecf20Sopenharmony_ci } 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci data->base = devm_ioremap(&pdev->dev, res.start, resource_size(&res)); 8928c2ecf20Sopenharmony_ci if (!data->base) { 8938c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to ioremap memory\n"); 8948c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 8958c2ecf20Sopenharmony_ci } 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci data->soc = (enum soc_type)of_device_get_match_data(&pdev->dev); 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci switch (data->soc) { 9008c2ecf20Sopenharmony_ci case SOC_ARCH_EXYNOS4210: 9018c2ecf20Sopenharmony_ci data->tmu_set_trip_temp = exynos4210_tmu_set_trip_temp; 9028c2ecf20Sopenharmony_ci data->tmu_set_trip_hyst = exynos4210_tmu_set_trip_hyst; 9038c2ecf20Sopenharmony_ci data->tmu_initialize = exynos4210_tmu_initialize; 9048c2ecf20Sopenharmony_ci data->tmu_control = exynos4210_tmu_control; 9058c2ecf20Sopenharmony_ci data->tmu_read = exynos4210_tmu_read; 9068c2ecf20Sopenharmony_ci data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; 9078c2ecf20Sopenharmony_ci data->ntrip = 4; 9088c2ecf20Sopenharmony_ci data->gain = 15; 9098c2ecf20Sopenharmony_ci data->reference_voltage = 7; 9108c2ecf20Sopenharmony_ci data->efuse_value = 55; 9118c2ecf20Sopenharmony_ci data->min_efuse_value = 40; 9128c2ecf20Sopenharmony_ci data->max_efuse_value = 100; 9138c2ecf20Sopenharmony_ci break; 9148c2ecf20Sopenharmony_ci case SOC_ARCH_EXYNOS3250: 9158c2ecf20Sopenharmony_ci case SOC_ARCH_EXYNOS4412: 9168c2ecf20Sopenharmony_ci case SOC_ARCH_EXYNOS5250: 9178c2ecf20Sopenharmony_ci case SOC_ARCH_EXYNOS5260: 9188c2ecf20Sopenharmony_ci case SOC_ARCH_EXYNOS5420: 9198c2ecf20Sopenharmony_ci case SOC_ARCH_EXYNOS5420_TRIMINFO: 9208c2ecf20Sopenharmony_ci data->tmu_set_trip_temp = exynos4412_tmu_set_trip_temp; 9218c2ecf20Sopenharmony_ci data->tmu_set_trip_hyst = exynos4412_tmu_set_trip_hyst; 9228c2ecf20Sopenharmony_ci data->tmu_initialize = exynos4412_tmu_initialize; 9238c2ecf20Sopenharmony_ci data->tmu_control = exynos4210_tmu_control; 9248c2ecf20Sopenharmony_ci data->tmu_read = exynos4412_tmu_read; 9258c2ecf20Sopenharmony_ci data->tmu_set_emulation = exynos4412_tmu_set_emulation; 9268c2ecf20Sopenharmony_ci data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; 9278c2ecf20Sopenharmony_ci data->ntrip = 4; 9288c2ecf20Sopenharmony_ci data->gain = 8; 9298c2ecf20Sopenharmony_ci data->reference_voltage = 16; 9308c2ecf20Sopenharmony_ci data->efuse_value = 55; 9318c2ecf20Sopenharmony_ci if (data->soc != SOC_ARCH_EXYNOS5420 && 9328c2ecf20Sopenharmony_ci data->soc != SOC_ARCH_EXYNOS5420_TRIMINFO) 9338c2ecf20Sopenharmony_ci data->min_efuse_value = 40; 9348c2ecf20Sopenharmony_ci else 9358c2ecf20Sopenharmony_ci data->min_efuse_value = 0; 9368c2ecf20Sopenharmony_ci data->max_efuse_value = 100; 9378c2ecf20Sopenharmony_ci break; 9388c2ecf20Sopenharmony_ci case SOC_ARCH_EXYNOS5433: 9398c2ecf20Sopenharmony_ci data->tmu_set_trip_temp = exynos5433_tmu_set_trip_temp; 9408c2ecf20Sopenharmony_ci data->tmu_set_trip_hyst = exynos5433_tmu_set_trip_hyst; 9418c2ecf20Sopenharmony_ci data->tmu_initialize = exynos5433_tmu_initialize; 9428c2ecf20Sopenharmony_ci data->tmu_control = exynos5433_tmu_control; 9438c2ecf20Sopenharmony_ci data->tmu_read = exynos4412_tmu_read; 9448c2ecf20Sopenharmony_ci data->tmu_set_emulation = exynos4412_tmu_set_emulation; 9458c2ecf20Sopenharmony_ci data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; 9468c2ecf20Sopenharmony_ci data->ntrip = 8; 9478c2ecf20Sopenharmony_ci data->gain = 8; 9488c2ecf20Sopenharmony_ci if (res.start == EXYNOS5433_G3D_BASE) 9498c2ecf20Sopenharmony_ci data->reference_voltage = 23; 9508c2ecf20Sopenharmony_ci else 9518c2ecf20Sopenharmony_ci data->reference_voltage = 16; 9528c2ecf20Sopenharmony_ci data->efuse_value = 75; 9538c2ecf20Sopenharmony_ci data->min_efuse_value = 40; 9548c2ecf20Sopenharmony_ci data->max_efuse_value = 150; 9558c2ecf20Sopenharmony_ci break; 9568c2ecf20Sopenharmony_ci case SOC_ARCH_EXYNOS7: 9578c2ecf20Sopenharmony_ci data->tmu_set_trip_temp = exynos7_tmu_set_trip_temp; 9588c2ecf20Sopenharmony_ci data->tmu_set_trip_hyst = exynos7_tmu_set_trip_hyst; 9598c2ecf20Sopenharmony_ci data->tmu_initialize = exynos7_tmu_initialize; 9608c2ecf20Sopenharmony_ci data->tmu_control = exynos7_tmu_control; 9618c2ecf20Sopenharmony_ci data->tmu_read = exynos7_tmu_read; 9628c2ecf20Sopenharmony_ci data->tmu_set_emulation = exynos4412_tmu_set_emulation; 9638c2ecf20Sopenharmony_ci data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; 9648c2ecf20Sopenharmony_ci data->ntrip = 8; 9658c2ecf20Sopenharmony_ci data->gain = 9; 9668c2ecf20Sopenharmony_ci data->reference_voltage = 17; 9678c2ecf20Sopenharmony_ci data->efuse_value = 75; 9688c2ecf20Sopenharmony_ci data->min_efuse_value = 15; 9698c2ecf20Sopenharmony_ci data->max_efuse_value = 100; 9708c2ecf20Sopenharmony_ci break; 9718c2ecf20Sopenharmony_ci default: 9728c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Platform not supported\n"); 9738c2ecf20Sopenharmony_ci return -EINVAL; 9748c2ecf20Sopenharmony_ci } 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci data->cal_type = TYPE_ONE_POINT_TRIMMING; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci /* 9798c2ecf20Sopenharmony_ci * Check if the TMU shares some registers and then try to map the 9808c2ecf20Sopenharmony_ci * memory of common registers. 9818c2ecf20Sopenharmony_ci */ 9828c2ecf20Sopenharmony_ci if (data->soc != SOC_ARCH_EXYNOS5420_TRIMINFO) 9838c2ecf20Sopenharmony_ci return 0; 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci if (of_address_to_resource(pdev->dev.of_node, 1, &res)) { 9868c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get Resource 1\n"); 9878c2ecf20Sopenharmony_ci return -ENODEV; 9888c2ecf20Sopenharmony_ci } 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci data->base_second = devm_ioremap(&pdev->dev, res.start, 9918c2ecf20Sopenharmony_ci resource_size(&res)); 9928c2ecf20Sopenharmony_ci if (!data->base_second) { 9938c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to ioremap memory\n"); 9948c2ecf20Sopenharmony_ci return -ENOMEM; 9958c2ecf20Sopenharmony_ci } 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci return 0; 9988c2ecf20Sopenharmony_ci} 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_cistatic const struct thermal_zone_of_device_ops exynos_sensor_ops = { 10018c2ecf20Sopenharmony_ci .get_temp = exynos_get_temp, 10028c2ecf20Sopenharmony_ci .set_emul_temp = exynos_tmu_set_emulation, 10038c2ecf20Sopenharmony_ci}; 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_cistatic int exynos_tmu_probe(struct platform_device *pdev) 10068c2ecf20Sopenharmony_ci{ 10078c2ecf20Sopenharmony_ci struct exynos_tmu_data *data; 10088c2ecf20Sopenharmony_ci int ret; 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci data = devm_kzalloc(&pdev->dev, sizeof(struct exynos_tmu_data), 10118c2ecf20Sopenharmony_ci GFP_KERNEL); 10128c2ecf20Sopenharmony_ci if (!data) 10138c2ecf20Sopenharmony_ci return -ENOMEM; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, data); 10168c2ecf20Sopenharmony_ci mutex_init(&data->lock); 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci /* 10198c2ecf20Sopenharmony_ci * Try enabling the regulator if found 10208c2ecf20Sopenharmony_ci * TODO: Add regulator as an SOC feature, so that regulator enable 10218c2ecf20Sopenharmony_ci * is a compulsory call. 10228c2ecf20Sopenharmony_ci */ 10238c2ecf20Sopenharmony_ci data->regulator = devm_regulator_get_optional(&pdev->dev, "vtmu"); 10248c2ecf20Sopenharmony_ci if (!IS_ERR(data->regulator)) { 10258c2ecf20Sopenharmony_ci ret = regulator_enable(data->regulator); 10268c2ecf20Sopenharmony_ci if (ret) { 10278c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to enable vtmu\n"); 10288c2ecf20Sopenharmony_ci return ret; 10298c2ecf20Sopenharmony_ci } 10308c2ecf20Sopenharmony_ci } else { 10318c2ecf20Sopenharmony_ci if (PTR_ERR(data->regulator) == -EPROBE_DEFER) 10328c2ecf20Sopenharmony_ci return -EPROBE_DEFER; 10338c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "Regulator node (vtmu) not found\n"); 10348c2ecf20Sopenharmony_ci } 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci ret = exynos_map_dt_data(pdev); 10378c2ecf20Sopenharmony_ci if (ret) 10388c2ecf20Sopenharmony_ci goto err_sensor; 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci INIT_WORK(&data->irq_work, exynos_tmu_work); 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci data->clk = devm_clk_get(&pdev->dev, "tmu_apbif"); 10438c2ecf20Sopenharmony_ci if (IS_ERR(data->clk)) { 10448c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to get clock\n"); 10458c2ecf20Sopenharmony_ci ret = PTR_ERR(data->clk); 10468c2ecf20Sopenharmony_ci goto err_sensor; 10478c2ecf20Sopenharmony_ci } 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci data->clk_sec = devm_clk_get(&pdev->dev, "tmu_triminfo_apbif"); 10508c2ecf20Sopenharmony_ci if (IS_ERR(data->clk_sec)) { 10518c2ecf20Sopenharmony_ci if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) { 10528c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to get triminfo clock\n"); 10538c2ecf20Sopenharmony_ci ret = PTR_ERR(data->clk_sec); 10548c2ecf20Sopenharmony_ci goto err_sensor; 10558c2ecf20Sopenharmony_ci } 10568c2ecf20Sopenharmony_ci } else { 10578c2ecf20Sopenharmony_ci ret = clk_prepare(data->clk_sec); 10588c2ecf20Sopenharmony_ci if (ret) { 10598c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to get clock\n"); 10608c2ecf20Sopenharmony_ci goto err_sensor; 10618c2ecf20Sopenharmony_ci } 10628c2ecf20Sopenharmony_ci } 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci ret = clk_prepare(data->clk); 10658c2ecf20Sopenharmony_ci if (ret) { 10668c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to get clock\n"); 10678c2ecf20Sopenharmony_ci goto err_clk_sec; 10688c2ecf20Sopenharmony_ci } 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci switch (data->soc) { 10718c2ecf20Sopenharmony_ci case SOC_ARCH_EXYNOS5433: 10728c2ecf20Sopenharmony_ci case SOC_ARCH_EXYNOS7: 10738c2ecf20Sopenharmony_ci data->sclk = devm_clk_get(&pdev->dev, "tmu_sclk"); 10748c2ecf20Sopenharmony_ci if (IS_ERR(data->sclk)) { 10758c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to get sclk\n"); 10768c2ecf20Sopenharmony_ci ret = PTR_ERR(data->sclk); 10778c2ecf20Sopenharmony_ci goto err_clk; 10788c2ecf20Sopenharmony_ci } else { 10798c2ecf20Sopenharmony_ci ret = clk_prepare_enable(data->sclk); 10808c2ecf20Sopenharmony_ci if (ret) { 10818c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to enable sclk\n"); 10828c2ecf20Sopenharmony_ci goto err_clk; 10838c2ecf20Sopenharmony_ci } 10848c2ecf20Sopenharmony_ci } 10858c2ecf20Sopenharmony_ci break; 10868c2ecf20Sopenharmony_ci default: 10878c2ecf20Sopenharmony_ci break; 10888c2ecf20Sopenharmony_ci } 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci /* 10918c2ecf20Sopenharmony_ci * data->tzd must be registered before calling exynos_tmu_initialize(), 10928c2ecf20Sopenharmony_ci * requesting irq and calling exynos_tmu_control(). 10938c2ecf20Sopenharmony_ci */ 10948c2ecf20Sopenharmony_ci data->tzd = thermal_zone_of_sensor_register(&pdev->dev, 0, data, 10958c2ecf20Sopenharmony_ci &exynos_sensor_ops); 10968c2ecf20Sopenharmony_ci if (IS_ERR(data->tzd)) { 10978c2ecf20Sopenharmony_ci ret = PTR_ERR(data->tzd); 10988c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 10998c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to register sensor: %d\n", 11008c2ecf20Sopenharmony_ci ret); 11018c2ecf20Sopenharmony_ci goto err_sclk; 11028c2ecf20Sopenharmony_ci } 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci ret = exynos_tmu_initialize(pdev); 11058c2ecf20Sopenharmony_ci if (ret) { 11068c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to initialize TMU\n"); 11078c2ecf20Sopenharmony_ci goto err_thermal; 11088c2ecf20Sopenharmony_ci } 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci ret = devm_request_irq(&pdev->dev, data->irq, exynos_tmu_irq, 11118c2ecf20Sopenharmony_ci IRQF_TRIGGER_RISING | IRQF_SHARED, dev_name(&pdev->dev), data); 11128c2ecf20Sopenharmony_ci if (ret) { 11138c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq); 11148c2ecf20Sopenharmony_ci goto err_thermal; 11158c2ecf20Sopenharmony_ci } 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci exynos_tmu_control(pdev, true); 11188c2ecf20Sopenharmony_ci return 0; 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_cierr_thermal: 11218c2ecf20Sopenharmony_ci thermal_zone_of_sensor_unregister(&pdev->dev, data->tzd); 11228c2ecf20Sopenharmony_cierr_sclk: 11238c2ecf20Sopenharmony_ci clk_disable_unprepare(data->sclk); 11248c2ecf20Sopenharmony_cierr_clk: 11258c2ecf20Sopenharmony_ci clk_unprepare(data->clk); 11268c2ecf20Sopenharmony_cierr_clk_sec: 11278c2ecf20Sopenharmony_ci if (!IS_ERR(data->clk_sec)) 11288c2ecf20Sopenharmony_ci clk_unprepare(data->clk_sec); 11298c2ecf20Sopenharmony_cierr_sensor: 11308c2ecf20Sopenharmony_ci if (!IS_ERR(data->regulator)) 11318c2ecf20Sopenharmony_ci regulator_disable(data->regulator); 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci return ret; 11348c2ecf20Sopenharmony_ci} 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_cistatic int exynos_tmu_remove(struct platform_device *pdev) 11378c2ecf20Sopenharmony_ci{ 11388c2ecf20Sopenharmony_ci struct exynos_tmu_data *data = platform_get_drvdata(pdev); 11398c2ecf20Sopenharmony_ci struct thermal_zone_device *tzd = data->tzd; 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci thermal_zone_of_sensor_unregister(&pdev->dev, tzd); 11428c2ecf20Sopenharmony_ci exynos_tmu_control(pdev, false); 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci clk_disable_unprepare(data->sclk); 11458c2ecf20Sopenharmony_ci clk_unprepare(data->clk); 11468c2ecf20Sopenharmony_ci if (!IS_ERR(data->clk_sec)) 11478c2ecf20Sopenharmony_ci clk_unprepare(data->clk_sec); 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci if (!IS_ERR(data->regulator)) 11508c2ecf20Sopenharmony_ci regulator_disable(data->regulator); 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci return 0; 11538c2ecf20Sopenharmony_ci} 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 11568c2ecf20Sopenharmony_cistatic int exynos_tmu_suspend(struct device *dev) 11578c2ecf20Sopenharmony_ci{ 11588c2ecf20Sopenharmony_ci exynos_tmu_control(to_platform_device(dev), false); 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci return 0; 11618c2ecf20Sopenharmony_ci} 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_cistatic int exynos_tmu_resume(struct device *dev) 11648c2ecf20Sopenharmony_ci{ 11658c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci exynos_tmu_initialize(pdev); 11688c2ecf20Sopenharmony_ci exynos_tmu_control(pdev, true); 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci return 0; 11718c2ecf20Sopenharmony_ci} 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(exynos_tmu_pm, 11748c2ecf20Sopenharmony_ci exynos_tmu_suspend, exynos_tmu_resume); 11758c2ecf20Sopenharmony_ci#define EXYNOS_TMU_PM (&exynos_tmu_pm) 11768c2ecf20Sopenharmony_ci#else 11778c2ecf20Sopenharmony_ci#define EXYNOS_TMU_PM NULL 11788c2ecf20Sopenharmony_ci#endif 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_cistatic struct platform_driver exynos_tmu_driver = { 11818c2ecf20Sopenharmony_ci .driver = { 11828c2ecf20Sopenharmony_ci .name = "exynos-tmu", 11838c2ecf20Sopenharmony_ci .pm = EXYNOS_TMU_PM, 11848c2ecf20Sopenharmony_ci .of_match_table = exynos_tmu_match, 11858c2ecf20Sopenharmony_ci }, 11868c2ecf20Sopenharmony_ci .probe = exynos_tmu_probe, 11878c2ecf20Sopenharmony_ci .remove = exynos_tmu_remove, 11888c2ecf20Sopenharmony_ci}; 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_cimodule_platform_driver(exynos_tmu_driver); 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Exynos TMU Driver"); 11938c2ecf20Sopenharmony_ciMODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>"); 11948c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 11958c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:exynos-tmu"); 1196