162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * exynos_tmu.c - Samsung Exynos TMU (Thermal Management Unit) 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2014 Samsung Electronics 662306a36Sopenharmony_ci * Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> 762306a36Sopenharmony_ci * Lukasz Majewski <l.majewski@samsung.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Copyright (C) 2011 Samsung Electronics 1062306a36Sopenharmony_ci * Donggeun Kim <dg77.kim@samsung.com> 1162306a36Sopenharmony_ci * Amit Daniel Kachhap <amit.kachhap@linaro.org> 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/clk.h> 1562306a36Sopenharmony_ci#include <linux/io.h> 1662306a36Sopenharmony_ci#include <linux/interrupt.h> 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci#include <linux/of.h> 1962306a36Sopenharmony_ci#include <linux/of_address.h> 2062306a36Sopenharmony_ci#include <linux/of_irq.h> 2162306a36Sopenharmony_ci#include <linux/platform_device.h> 2262306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 2362306a36Sopenharmony_ci#include <linux/thermal.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <dt-bindings/thermal/thermal_exynos.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* Exynos generic registers */ 2862306a36Sopenharmony_ci#define EXYNOS_TMU_REG_TRIMINFO 0x0 2962306a36Sopenharmony_ci#define EXYNOS_TMU_REG_CONTROL 0x20 3062306a36Sopenharmony_ci#define EXYNOS_TMU_REG_STATUS 0x28 3162306a36Sopenharmony_ci#define EXYNOS_TMU_REG_CURRENT_TEMP 0x40 3262306a36Sopenharmony_ci#define EXYNOS_TMU_REG_INTEN 0x70 3362306a36Sopenharmony_ci#define EXYNOS_TMU_REG_INTSTAT 0x74 3462306a36Sopenharmony_ci#define EXYNOS_TMU_REG_INTCLEAR 0x78 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define EXYNOS_TMU_TEMP_MASK 0xff 3762306a36Sopenharmony_ci#define EXYNOS_TMU_REF_VOLTAGE_SHIFT 24 3862306a36Sopenharmony_ci#define EXYNOS_TMU_REF_VOLTAGE_MASK 0x1f 3962306a36Sopenharmony_ci#define EXYNOS_TMU_BUF_SLOPE_SEL_MASK 0xf 4062306a36Sopenharmony_ci#define EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT 8 4162306a36Sopenharmony_ci#define EXYNOS_TMU_CORE_EN_SHIFT 0 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* Exynos3250 specific registers */ 4462306a36Sopenharmony_ci#define EXYNOS_TMU_TRIMINFO_CON1 0x10 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* Exynos4210 specific registers */ 4762306a36Sopenharmony_ci#define EXYNOS4210_TMU_REG_THRESHOLD_TEMP 0x44 4862306a36Sopenharmony_ci#define EXYNOS4210_TMU_REG_TRIG_LEVEL0 0x50 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* Exynos5250, Exynos4412, Exynos3250 specific registers */ 5162306a36Sopenharmony_ci#define EXYNOS_TMU_TRIMINFO_CON2 0x14 5262306a36Sopenharmony_ci#define EXYNOS_THD_TEMP_RISE 0x50 5362306a36Sopenharmony_ci#define EXYNOS_THD_TEMP_FALL 0x54 5462306a36Sopenharmony_ci#define EXYNOS_EMUL_CON 0x80 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#define EXYNOS_TRIMINFO_RELOAD_ENABLE 1 5762306a36Sopenharmony_ci#define EXYNOS_TRIMINFO_25_SHIFT 0 5862306a36Sopenharmony_ci#define EXYNOS_TRIMINFO_85_SHIFT 8 5962306a36Sopenharmony_ci#define EXYNOS_TMU_TRIP_MODE_SHIFT 13 6062306a36Sopenharmony_ci#define EXYNOS_TMU_TRIP_MODE_MASK 0x7 6162306a36Sopenharmony_ci#define EXYNOS_TMU_THERM_TRIP_EN_SHIFT 12 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci#define EXYNOS_TMU_INTEN_RISE0_SHIFT 0 6462306a36Sopenharmony_ci#define EXYNOS_TMU_INTEN_FALL0_SHIFT 16 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#define EXYNOS_EMUL_TIME 0x57F0 6762306a36Sopenharmony_ci#define EXYNOS_EMUL_TIME_MASK 0xffff 6862306a36Sopenharmony_ci#define EXYNOS_EMUL_TIME_SHIFT 16 6962306a36Sopenharmony_ci#define EXYNOS_EMUL_DATA_SHIFT 8 7062306a36Sopenharmony_ci#define EXYNOS_EMUL_DATA_MASK 0xFF 7162306a36Sopenharmony_ci#define EXYNOS_EMUL_ENABLE 0x1 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/* Exynos5260 specific */ 7462306a36Sopenharmony_ci#define EXYNOS5260_TMU_REG_INTEN 0xC0 7562306a36Sopenharmony_ci#define EXYNOS5260_TMU_REG_INTSTAT 0xC4 7662306a36Sopenharmony_ci#define EXYNOS5260_TMU_REG_INTCLEAR 0xC8 7762306a36Sopenharmony_ci#define EXYNOS5260_EMUL_CON 0x100 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* Exynos4412 specific */ 8062306a36Sopenharmony_ci#define EXYNOS4412_MUX_ADDR_VALUE 6 8162306a36Sopenharmony_ci#define EXYNOS4412_MUX_ADDR_SHIFT 20 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci/* Exynos5433 specific registers */ 8462306a36Sopenharmony_ci#define EXYNOS5433_THD_TEMP_RISE3_0 0x050 8562306a36Sopenharmony_ci#define EXYNOS5433_THD_TEMP_RISE7_4 0x054 8662306a36Sopenharmony_ci#define EXYNOS5433_THD_TEMP_FALL3_0 0x060 8762306a36Sopenharmony_ci#define EXYNOS5433_THD_TEMP_FALL7_4 0x064 8862306a36Sopenharmony_ci#define EXYNOS5433_TMU_REG_INTEN 0x0c0 8962306a36Sopenharmony_ci#define EXYNOS5433_TMU_REG_INTPEND 0x0c8 9062306a36Sopenharmony_ci#define EXYNOS5433_TMU_EMUL_CON 0x110 9162306a36Sopenharmony_ci#define EXYNOS5433_TMU_PD_DET_EN 0x130 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci#define EXYNOS5433_TRIMINFO_SENSOR_ID_SHIFT 16 9462306a36Sopenharmony_ci#define EXYNOS5433_TRIMINFO_CALIB_SEL_SHIFT 23 9562306a36Sopenharmony_ci#define EXYNOS5433_TRIMINFO_SENSOR_ID_MASK \ 9662306a36Sopenharmony_ci (0xf << EXYNOS5433_TRIMINFO_SENSOR_ID_SHIFT) 9762306a36Sopenharmony_ci#define EXYNOS5433_TRIMINFO_CALIB_SEL_MASK BIT(23) 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci#define EXYNOS5433_TRIMINFO_ONE_POINT_TRIMMING 0 10062306a36Sopenharmony_ci#define EXYNOS5433_TRIMINFO_TWO_POINT_TRIMMING 1 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci#define EXYNOS5433_PD_DET_EN 1 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci#define EXYNOS5433_G3D_BASE 0x10070000 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/* Exynos7 specific registers */ 10762306a36Sopenharmony_ci#define EXYNOS7_THD_TEMP_RISE7_6 0x50 10862306a36Sopenharmony_ci#define EXYNOS7_THD_TEMP_FALL7_6 0x60 10962306a36Sopenharmony_ci#define EXYNOS7_TMU_REG_INTEN 0x110 11062306a36Sopenharmony_ci#define EXYNOS7_TMU_REG_INTPEND 0x118 11162306a36Sopenharmony_ci#define EXYNOS7_TMU_REG_EMUL_CON 0x160 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci#define EXYNOS7_TMU_TEMP_MASK 0x1ff 11462306a36Sopenharmony_ci#define EXYNOS7_PD_DET_EN_SHIFT 23 11562306a36Sopenharmony_ci#define EXYNOS7_TMU_INTEN_RISE0_SHIFT 0 11662306a36Sopenharmony_ci#define EXYNOS7_EMUL_DATA_SHIFT 7 11762306a36Sopenharmony_ci#define EXYNOS7_EMUL_DATA_MASK 0x1ff 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci#define EXYNOS_FIRST_POINT_TRIM 25 12062306a36Sopenharmony_ci#define EXYNOS_SECOND_POINT_TRIM 85 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci#define EXYNOS_NOISE_CANCEL_MODE 4 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci#define MCELSIUS 1000 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cienum soc_type { 12762306a36Sopenharmony_ci SOC_ARCH_EXYNOS3250 = 1, 12862306a36Sopenharmony_ci SOC_ARCH_EXYNOS4210, 12962306a36Sopenharmony_ci SOC_ARCH_EXYNOS4412, 13062306a36Sopenharmony_ci SOC_ARCH_EXYNOS5250, 13162306a36Sopenharmony_ci SOC_ARCH_EXYNOS5260, 13262306a36Sopenharmony_ci SOC_ARCH_EXYNOS5420, 13362306a36Sopenharmony_ci SOC_ARCH_EXYNOS5420_TRIMINFO, 13462306a36Sopenharmony_ci SOC_ARCH_EXYNOS5433, 13562306a36Sopenharmony_ci SOC_ARCH_EXYNOS7, 13662306a36Sopenharmony_ci}; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci/** 13962306a36Sopenharmony_ci * struct exynos_tmu_data : A structure to hold the private data of the TMU 14062306a36Sopenharmony_ci * driver 14162306a36Sopenharmony_ci * @id: identifier of the one instance of the TMU controller. 14262306a36Sopenharmony_ci * @base: base address of the single instance of the TMU controller. 14362306a36Sopenharmony_ci * @base_second: base address of the common registers of the TMU controller. 14462306a36Sopenharmony_ci * @irq: irq number of the TMU controller. 14562306a36Sopenharmony_ci * @soc: id of the SOC type. 14662306a36Sopenharmony_ci * @irq_work: pointer to the irq work structure. 14762306a36Sopenharmony_ci * @lock: lock to implement synchronization. 14862306a36Sopenharmony_ci * @clk: pointer to the clock structure. 14962306a36Sopenharmony_ci * @clk_sec: pointer to the clock structure for accessing the base_second. 15062306a36Sopenharmony_ci * @sclk: pointer to the clock structure for accessing the tmu special clk. 15162306a36Sopenharmony_ci * @cal_type: calibration type for temperature 15262306a36Sopenharmony_ci * @efuse_value: SoC defined fuse value 15362306a36Sopenharmony_ci * @min_efuse_value: minimum valid trimming data 15462306a36Sopenharmony_ci * @max_efuse_value: maximum valid trimming data 15562306a36Sopenharmony_ci * @temp_error1: fused value of the first point trim. 15662306a36Sopenharmony_ci * @temp_error2: fused value of the second point trim. 15762306a36Sopenharmony_ci * @gain: gain of amplifier in the positive-TC generator block 15862306a36Sopenharmony_ci * 0 < gain <= 15 15962306a36Sopenharmony_ci * @reference_voltage: reference voltage of amplifier 16062306a36Sopenharmony_ci * in the positive-TC generator block 16162306a36Sopenharmony_ci * 0 < reference_voltage <= 31 16262306a36Sopenharmony_ci * @regulator: pointer to the TMU regulator structure. 16362306a36Sopenharmony_ci * @reg_conf: pointer to structure to register with core thermal. 16462306a36Sopenharmony_ci * @tzd: pointer to thermal_zone_device structure 16562306a36Sopenharmony_ci * @ntrip: number of supported trip points. 16662306a36Sopenharmony_ci * @enabled: current status of TMU device 16762306a36Sopenharmony_ci * @tmu_set_trip_temp: SoC specific method to set trip (rising threshold) 16862306a36Sopenharmony_ci * @tmu_set_trip_hyst: SoC specific to set hysteresis (falling threshold) 16962306a36Sopenharmony_ci * @tmu_initialize: SoC specific TMU initialization method 17062306a36Sopenharmony_ci * @tmu_control: SoC specific TMU control method 17162306a36Sopenharmony_ci * @tmu_read: SoC specific TMU temperature read method 17262306a36Sopenharmony_ci * @tmu_set_emulation: SoC specific TMU emulation setting method 17362306a36Sopenharmony_ci * @tmu_clear_irqs: SoC specific TMU interrupts clearing method 17462306a36Sopenharmony_ci */ 17562306a36Sopenharmony_cistruct exynos_tmu_data { 17662306a36Sopenharmony_ci int id; 17762306a36Sopenharmony_ci void __iomem *base; 17862306a36Sopenharmony_ci void __iomem *base_second; 17962306a36Sopenharmony_ci int irq; 18062306a36Sopenharmony_ci enum soc_type soc; 18162306a36Sopenharmony_ci struct work_struct irq_work; 18262306a36Sopenharmony_ci struct mutex lock; 18362306a36Sopenharmony_ci struct clk *clk, *clk_sec, *sclk; 18462306a36Sopenharmony_ci u32 cal_type; 18562306a36Sopenharmony_ci u32 efuse_value; 18662306a36Sopenharmony_ci u32 min_efuse_value; 18762306a36Sopenharmony_ci u32 max_efuse_value; 18862306a36Sopenharmony_ci u16 temp_error1, temp_error2; 18962306a36Sopenharmony_ci u8 gain; 19062306a36Sopenharmony_ci u8 reference_voltage; 19162306a36Sopenharmony_ci struct regulator *regulator; 19262306a36Sopenharmony_ci struct thermal_zone_device *tzd; 19362306a36Sopenharmony_ci unsigned int ntrip; 19462306a36Sopenharmony_ci bool enabled; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci void (*tmu_set_trip_temp)(struct exynos_tmu_data *data, int trip, 19762306a36Sopenharmony_ci u8 temp); 19862306a36Sopenharmony_ci void (*tmu_set_trip_hyst)(struct exynos_tmu_data *data, int trip, 19962306a36Sopenharmony_ci u8 temp, u8 hyst); 20062306a36Sopenharmony_ci void (*tmu_initialize)(struct platform_device *pdev); 20162306a36Sopenharmony_ci void (*tmu_control)(struct platform_device *pdev, bool on); 20262306a36Sopenharmony_ci int (*tmu_read)(struct exynos_tmu_data *data); 20362306a36Sopenharmony_ci void (*tmu_set_emulation)(struct exynos_tmu_data *data, int temp); 20462306a36Sopenharmony_ci void (*tmu_clear_irqs)(struct exynos_tmu_data *data); 20562306a36Sopenharmony_ci}; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci/* 20862306a36Sopenharmony_ci * TMU treats temperature as a mapped temperature code. 20962306a36Sopenharmony_ci * The temperature is converted differently depending on the calibration type. 21062306a36Sopenharmony_ci */ 21162306a36Sopenharmony_cistatic int temp_to_code(struct exynos_tmu_data *data, u8 temp) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci if (data->cal_type == TYPE_ONE_POINT_TRIMMING) 21462306a36Sopenharmony_ci return temp + data->temp_error1 - EXYNOS_FIRST_POINT_TRIM; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci return (temp - EXYNOS_FIRST_POINT_TRIM) * 21762306a36Sopenharmony_ci (data->temp_error2 - data->temp_error1) / 21862306a36Sopenharmony_ci (EXYNOS_SECOND_POINT_TRIM - EXYNOS_FIRST_POINT_TRIM) + 21962306a36Sopenharmony_ci data->temp_error1; 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci/* 22362306a36Sopenharmony_ci * Calculate a temperature value from a temperature code. 22462306a36Sopenharmony_ci * The unit of the temperature is degree Celsius. 22562306a36Sopenharmony_ci */ 22662306a36Sopenharmony_cistatic int code_to_temp(struct exynos_tmu_data *data, u16 temp_code) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci if (data->cal_type == TYPE_ONE_POINT_TRIMMING) 22962306a36Sopenharmony_ci return temp_code - data->temp_error1 + EXYNOS_FIRST_POINT_TRIM; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci return (temp_code - data->temp_error1) * 23262306a36Sopenharmony_ci (EXYNOS_SECOND_POINT_TRIM - EXYNOS_FIRST_POINT_TRIM) / 23362306a36Sopenharmony_ci (data->temp_error2 - data->temp_error1) + 23462306a36Sopenharmony_ci EXYNOS_FIRST_POINT_TRIM; 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic void sanitize_temp_error(struct exynos_tmu_data *data, u32 trim_info) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci u16 tmu_temp_mask = 24062306a36Sopenharmony_ci (data->soc == SOC_ARCH_EXYNOS7) ? EXYNOS7_TMU_TEMP_MASK 24162306a36Sopenharmony_ci : EXYNOS_TMU_TEMP_MASK; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci data->temp_error1 = trim_info & tmu_temp_mask; 24462306a36Sopenharmony_ci data->temp_error2 = ((trim_info >> EXYNOS_TRIMINFO_85_SHIFT) & 24562306a36Sopenharmony_ci EXYNOS_TMU_TEMP_MASK); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (!data->temp_error1 || 24862306a36Sopenharmony_ci (data->min_efuse_value > data->temp_error1) || 24962306a36Sopenharmony_ci (data->temp_error1 > data->max_efuse_value)) 25062306a36Sopenharmony_ci data->temp_error1 = data->efuse_value & EXYNOS_TMU_TEMP_MASK; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (!data->temp_error2) 25362306a36Sopenharmony_ci data->temp_error2 = 25462306a36Sopenharmony_ci (data->efuse_value >> EXYNOS_TRIMINFO_85_SHIFT) & 25562306a36Sopenharmony_ci EXYNOS_TMU_TEMP_MASK; 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_cistatic int exynos_tmu_initialize(struct platform_device *pdev) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci struct exynos_tmu_data *data = platform_get_drvdata(pdev); 26162306a36Sopenharmony_ci struct thermal_zone_device *tzd = data->tzd; 26262306a36Sopenharmony_ci int num_trips = thermal_zone_get_num_trips(tzd); 26362306a36Sopenharmony_ci unsigned int status; 26462306a36Sopenharmony_ci int ret = 0, temp; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci ret = thermal_zone_get_crit_temp(tzd, &temp); 26762306a36Sopenharmony_ci if (ret && data->soc != SOC_ARCH_EXYNOS5433) { /* FIXME */ 26862306a36Sopenharmony_ci dev_err(&pdev->dev, 26962306a36Sopenharmony_ci "No CRITICAL trip point defined in device tree!\n"); 27062306a36Sopenharmony_ci goto out; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci if (num_trips > data->ntrip) { 27462306a36Sopenharmony_ci dev_info(&pdev->dev, 27562306a36Sopenharmony_ci "More trip points than supported by this TMU.\n"); 27662306a36Sopenharmony_ci dev_info(&pdev->dev, 27762306a36Sopenharmony_ci "%d trip points should be configured in polling mode.\n", 27862306a36Sopenharmony_ci num_trips - data->ntrip); 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci mutex_lock(&data->lock); 28262306a36Sopenharmony_ci clk_enable(data->clk); 28362306a36Sopenharmony_ci if (!IS_ERR(data->clk_sec)) 28462306a36Sopenharmony_ci clk_enable(data->clk_sec); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci status = readb(data->base + EXYNOS_TMU_REG_STATUS); 28762306a36Sopenharmony_ci if (!status) { 28862306a36Sopenharmony_ci ret = -EBUSY; 28962306a36Sopenharmony_ci } else { 29062306a36Sopenharmony_ci int i, ntrips = 29162306a36Sopenharmony_ci min_t(int, num_trips, data->ntrip); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci data->tmu_initialize(pdev); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci /* Write temperature code for rising and falling threshold */ 29662306a36Sopenharmony_ci for (i = 0; i < ntrips; i++) { 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci struct thermal_trip trip; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci ret = thermal_zone_get_trip(tzd, i, &trip); 30162306a36Sopenharmony_ci if (ret) 30262306a36Sopenharmony_ci goto err; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci data->tmu_set_trip_temp(data, i, trip.temperature / MCELSIUS); 30562306a36Sopenharmony_ci data->tmu_set_trip_hyst(data, i, trip.temperature / MCELSIUS, 30662306a36Sopenharmony_ci trip.hysteresis / MCELSIUS); 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci data->tmu_clear_irqs(data); 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_cierr: 31262306a36Sopenharmony_ci clk_disable(data->clk); 31362306a36Sopenharmony_ci mutex_unlock(&data->lock); 31462306a36Sopenharmony_ci if (!IS_ERR(data->clk_sec)) 31562306a36Sopenharmony_ci clk_disable(data->clk_sec); 31662306a36Sopenharmony_ciout: 31762306a36Sopenharmony_ci return ret; 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic u32 get_con_reg(struct exynos_tmu_data *data, u32 con) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci if (data->soc == SOC_ARCH_EXYNOS4412 || 32362306a36Sopenharmony_ci data->soc == SOC_ARCH_EXYNOS3250) 32462306a36Sopenharmony_ci con |= (EXYNOS4412_MUX_ADDR_VALUE << EXYNOS4412_MUX_ADDR_SHIFT); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci con &= ~(EXYNOS_TMU_REF_VOLTAGE_MASK << EXYNOS_TMU_REF_VOLTAGE_SHIFT); 32762306a36Sopenharmony_ci con |= data->reference_voltage << EXYNOS_TMU_REF_VOLTAGE_SHIFT; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci con &= ~(EXYNOS_TMU_BUF_SLOPE_SEL_MASK << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT); 33062306a36Sopenharmony_ci con |= (data->gain << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci con &= ~(EXYNOS_TMU_TRIP_MODE_MASK << EXYNOS_TMU_TRIP_MODE_SHIFT); 33362306a36Sopenharmony_ci con |= (EXYNOS_NOISE_CANCEL_MODE << EXYNOS_TMU_TRIP_MODE_SHIFT); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci return con; 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic void exynos_tmu_control(struct platform_device *pdev, bool on) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci struct exynos_tmu_data *data = platform_get_drvdata(pdev); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci mutex_lock(&data->lock); 34362306a36Sopenharmony_ci clk_enable(data->clk); 34462306a36Sopenharmony_ci data->tmu_control(pdev, on); 34562306a36Sopenharmony_ci data->enabled = on; 34662306a36Sopenharmony_ci clk_disable(data->clk); 34762306a36Sopenharmony_ci mutex_unlock(&data->lock); 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic void exynos4210_tmu_set_trip_temp(struct exynos_tmu_data *data, 35162306a36Sopenharmony_ci int trip_id, u8 temp) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci struct thermal_trip trip; 35462306a36Sopenharmony_ci u8 ref, th_code; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci if (thermal_zone_get_trip(data->tzd, 0, &trip)) 35762306a36Sopenharmony_ci return; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci ref = trip.temperature / MCELSIUS; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (trip_id == 0) { 36262306a36Sopenharmony_ci th_code = temp_to_code(data, ref); 36362306a36Sopenharmony_ci writeb(th_code, data->base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP); 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci temp -= ref; 36762306a36Sopenharmony_ci writeb(temp, data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL0 + trip_id * 4); 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci/* failing thresholds are not supported on Exynos4210 */ 37162306a36Sopenharmony_cistatic void exynos4210_tmu_set_trip_hyst(struct exynos_tmu_data *data, 37262306a36Sopenharmony_ci int trip, u8 temp, u8 hyst) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_cistatic void exynos4210_tmu_initialize(struct platform_device *pdev) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci struct exynos_tmu_data *data = platform_get_drvdata(pdev); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci sanitize_temp_error(data, readl(data->base + EXYNOS_TMU_REG_TRIMINFO)); 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic void exynos4412_tmu_set_trip_temp(struct exynos_tmu_data *data, 38462306a36Sopenharmony_ci int trip, u8 temp) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci u32 th, con; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci th = readl(data->base + EXYNOS_THD_TEMP_RISE); 38962306a36Sopenharmony_ci th &= ~(0xff << 8 * trip); 39062306a36Sopenharmony_ci th |= temp_to_code(data, temp) << 8 * trip; 39162306a36Sopenharmony_ci writel(th, data->base + EXYNOS_THD_TEMP_RISE); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci if (trip == 3) { 39462306a36Sopenharmony_ci con = readl(data->base + EXYNOS_TMU_REG_CONTROL); 39562306a36Sopenharmony_ci con |= (1 << EXYNOS_TMU_THERM_TRIP_EN_SHIFT); 39662306a36Sopenharmony_ci writel(con, data->base + EXYNOS_TMU_REG_CONTROL); 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic void exynos4412_tmu_set_trip_hyst(struct exynos_tmu_data *data, 40162306a36Sopenharmony_ci int trip, u8 temp, u8 hyst) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci u32 th; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci th = readl(data->base + EXYNOS_THD_TEMP_FALL); 40662306a36Sopenharmony_ci th &= ~(0xff << 8 * trip); 40762306a36Sopenharmony_ci if (hyst) 40862306a36Sopenharmony_ci th |= temp_to_code(data, temp - hyst) << 8 * trip; 40962306a36Sopenharmony_ci writel(th, data->base + EXYNOS_THD_TEMP_FALL); 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistatic void exynos4412_tmu_initialize(struct platform_device *pdev) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci struct exynos_tmu_data *data = platform_get_drvdata(pdev); 41562306a36Sopenharmony_ci unsigned int trim_info, ctrl; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci if (data->soc == SOC_ARCH_EXYNOS3250 || 41862306a36Sopenharmony_ci data->soc == SOC_ARCH_EXYNOS4412 || 41962306a36Sopenharmony_ci data->soc == SOC_ARCH_EXYNOS5250) { 42062306a36Sopenharmony_ci if (data->soc == SOC_ARCH_EXYNOS3250) { 42162306a36Sopenharmony_ci ctrl = readl(data->base + EXYNOS_TMU_TRIMINFO_CON1); 42262306a36Sopenharmony_ci ctrl |= EXYNOS_TRIMINFO_RELOAD_ENABLE; 42362306a36Sopenharmony_ci writel(ctrl, data->base + EXYNOS_TMU_TRIMINFO_CON1); 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci ctrl = readl(data->base + EXYNOS_TMU_TRIMINFO_CON2); 42662306a36Sopenharmony_ci ctrl |= EXYNOS_TRIMINFO_RELOAD_ENABLE; 42762306a36Sopenharmony_ci writel(ctrl, data->base + EXYNOS_TMU_TRIMINFO_CON2); 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci /* On exynos5420 the triminfo register is in the shared space */ 43162306a36Sopenharmony_ci if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) 43262306a36Sopenharmony_ci trim_info = readl(data->base_second + EXYNOS_TMU_REG_TRIMINFO); 43362306a36Sopenharmony_ci else 43462306a36Sopenharmony_ci trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci sanitize_temp_error(data, trim_info); 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_cistatic void exynos5433_tmu_set_trip_temp(struct exynos_tmu_data *data, 44062306a36Sopenharmony_ci int trip, u8 temp) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci unsigned int reg_off, j; 44362306a36Sopenharmony_ci u32 th; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci if (trip > 3) { 44662306a36Sopenharmony_ci reg_off = EXYNOS5433_THD_TEMP_RISE7_4; 44762306a36Sopenharmony_ci j = trip - 4; 44862306a36Sopenharmony_ci } else { 44962306a36Sopenharmony_ci reg_off = EXYNOS5433_THD_TEMP_RISE3_0; 45062306a36Sopenharmony_ci j = trip; 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci th = readl(data->base + reg_off); 45462306a36Sopenharmony_ci th &= ~(0xff << j * 8); 45562306a36Sopenharmony_ci th |= (temp_to_code(data, temp) << j * 8); 45662306a36Sopenharmony_ci writel(th, data->base + reg_off); 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_cistatic void exynos5433_tmu_set_trip_hyst(struct exynos_tmu_data *data, 46062306a36Sopenharmony_ci int trip, u8 temp, u8 hyst) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci unsigned int reg_off, j; 46362306a36Sopenharmony_ci u32 th; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if (trip > 3) { 46662306a36Sopenharmony_ci reg_off = EXYNOS5433_THD_TEMP_FALL7_4; 46762306a36Sopenharmony_ci j = trip - 4; 46862306a36Sopenharmony_ci } else { 46962306a36Sopenharmony_ci reg_off = EXYNOS5433_THD_TEMP_FALL3_0; 47062306a36Sopenharmony_ci j = trip; 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci th = readl(data->base + reg_off); 47462306a36Sopenharmony_ci th &= ~(0xff << j * 8); 47562306a36Sopenharmony_ci th |= (temp_to_code(data, temp - hyst) << j * 8); 47662306a36Sopenharmony_ci writel(th, data->base + reg_off); 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic void exynos5433_tmu_initialize(struct platform_device *pdev) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci struct exynos_tmu_data *data = platform_get_drvdata(pdev); 48262306a36Sopenharmony_ci unsigned int trim_info; 48362306a36Sopenharmony_ci int sensor_id, cal_type; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); 48662306a36Sopenharmony_ci sanitize_temp_error(data, trim_info); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci /* Read the temperature sensor id */ 48962306a36Sopenharmony_ci sensor_id = (trim_info & EXYNOS5433_TRIMINFO_SENSOR_ID_MASK) 49062306a36Sopenharmony_ci >> EXYNOS5433_TRIMINFO_SENSOR_ID_SHIFT; 49162306a36Sopenharmony_ci dev_info(&pdev->dev, "Temperature sensor ID: 0x%x\n", sensor_id); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci /* Read the calibration mode */ 49462306a36Sopenharmony_ci writel(trim_info, data->base + EXYNOS_TMU_REG_TRIMINFO); 49562306a36Sopenharmony_ci cal_type = (trim_info & EXYNOS5433_TRIMINFO_CALIB_SEL_MASK) 49662306a36Sopenharmony_ci >> EXYNOS5433_TRIMINFO_CALIB_SEL_SHIFT; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci switch (cal_type) { 49962306a36Sopenharmony_ci case EXYNOS5433_TRIMINFO_TWO_POINT_TRIMMING: 50062306a36Sopenharmony_ci data->cal_type = TYPE_TWO_POINT_TRIMMING; 50162306a36Sopenharmony_ci break; 50262306a36Sopenharmony_ci case EXYNOS5433_TRIMINFO_ONE_POINT_TRIMMING: 50362306a36Sopenharmony_ci default: 50462306a36Sopenharmony_ci data->cal_type = TYPE_ONE_POINT_TRIMMING; 50562306a36Sopenharmony_ci break; 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci dev_info(&pdev->dev, "Calibration type is %d-point calibration\n", 50962306a36Sopenharmony_ci cal_type ? 2 : 1); 51062306a36Sopenharmony_ci} 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_cistatic void exynos7_tmu_set_trip_temp(struct exynos_tmu_data *data, 51362306a36Sopenharmony_ci int trip, u8 temp) 51462306a36Sopenharmony_ci{ 51562306a36Sopenharmony_ci unsigned int reg_off, bit_off; 51662306a36Sopenharmony_ci u32 th; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci reg_off = ((7 - trip) / 2) * 4; 51962306a36Sopenharmony_ci bit_off = ((8 - trip) % 2); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci th = readl(data->base + EXYNOS7_THD_TEMP_RISE7_6 + reg_off); 52262306a36Sopenharmony_ci th &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off)); 52362306a36Sopenharmony_ci th |= temp_to_code(data, temp) << (16 * bit_off); 52462306a36Sopenharmony_ci writel(th, data->base + EXYNOS7_THD_TEMP_RISE7_6 + reg_off); 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_cistatic void exynos7_tmu_set_trip_hyst(struct exynos_tmu_data *data, 52862306a36Sopenharmony_ci int trip, u8 temp, u8 hyst) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci unsigned int reg_off, bit_off; 53162306a36Sopenharmony_ci u32 th; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci reg_off = ((7 - trip) / 2) * 4; 53462306a36Sopenharmony_ci bit_off = ((8 - trip) % 2); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci th = readl(data->base + EXYNOS7_THD_TEMP_FALL7_6 + reg_off); 53762306a36Sopenharmony_ci th &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off)); 53862306a36Sopenharmony_ci th |= temp_to_code(data, temp - hyst) << (16 * bit_off); 53962306a36Sopenharmony_ci writel(th, data->base + EXYNOS7_THD_TEMP_FALL7_6 + reg_off); 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistatic void exynos7_tmu_initialize(struct platform_device *pdev) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci struct exynos_tmu_data *data = platform_get_drvdata(pdev); 54562306a36Sopenharmony_ci unsigned int trim_info; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); 54862306a36Sopenharmony_ci sanitize_temp_error(data, trim_info); 54962306a36Sopenharmony_ci} 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_cistatic void exynos4210_tmu_control(struct platform_device *pdev, bool on) 55262306a36Sopenharmony_ci{ 55362306a36Sopenharmony_ci struct exynos_tmu_data *data = platform_get_drvdata(pdev); 55462306a36Sopenharmony_ci struct thermal_zone_device *tz = data->tzd; 55562306a36Sopenharmony_ci struct thermal_trip trip; 55662306a36Sopenharmony_ci unsigned int con, interrupt_en = 0, i; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL)); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci if (on) { 56162306a36Sopenharmony_ci for (i = 0; i < data->ntrip; i++) { 56262306a36Sopenharmony_ci if (thermal_zone_get_trip(tz, i, &trip)) 56362306a36Sopenharmony_ci continue; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci interrupt_en |= 56662306a36Sopenharmony_ci (1 << (EXYNOS_TMU_INTEN_RISE0_SHIFT + i * 4)); 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci if (data->soc != SOC_ARCH_EXYNOS4210) 57062306a36Sopenharmony_ci interrupt_en |= 57162306a36Sopenharmony_ci interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); 57462306a36Sopenharmony_ci } else { 57562306a36Sopenharmony_ci con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci writel(interrupt_en, data->base + EXYNOS_TMU_REG_INTEN); 57962306a36Sopenharmony_ci writel(con, data->base + EXYNOS_TMU_REG_CONTROL); 58062306a36Sopenharmony_ci} 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_cistatic void exynos5433_tmu_control(struct platform_device *pdev, bool on) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci struct exynos_tmu_data *data = platform_get_drvdata(pdev); 58562306a36Sopenharmony_ci struct thermal_zone_device *tz = data->tzd; 58662306a36Sopenharmony_ci struct thermal_trip trip; 58762306a36Sopenharmony_ci unsigned int con, interrupt_en = 0, pd_det_en, i; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL)); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci if (on) { 59262306a36Sopenharmony_ci for (i = 0; i < data->ntrip; i++) { 59362306a36Sopenharmony_ci if (thermal_zone_get_trip(tz, i, &trip)) 59462306a36Sopenharmony_ci continue; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci interrupt_en |= 59762306a36Sopenharmony_ci (1 << (EXYNOS7_TMU_INTEN_RISE0_SHIFT + i)); 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci interrupt_en |= 60162306a36Sopenharmony_ci interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); 60462306a36Sopenharmony_ci } else 60562306a36Sopenharmony_ci con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci pd_det_en = on ? EXYNOS5433_PD_DET_EN : 0; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci writel(pd_det_en, data->base + EXYNOS5433_TMU_PD_DET_EN); 61062306a36Sopenharmony_ci writel(interrupt_en, data->base + EXYNOS5433_TMU_REG_INTEN); 61162306a36Sopenharmony_ci writel(con, data->base + EXYNOS_TMU_REG_CONTROL); 61262306a36Sopenharmony_ci} 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_cistatic void exynos7_tmu_control(struct platform_device *pdev, bool on) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci struct exynos_tmu_data *data = platform_get_drvdata(pdev); 61762306a36Sopenharmony_ci struct thermal_zone_device *tz = data->tzd; 61862306a36Sopenharmony_ci struct thermal_trip trip; 61962306a36Sopenharmony_ci unsigned int con, interrupt_en = 0, i; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL)); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci if (on) { 62462306a36Sopenharmony_ci for (i = 0; i < data->ntrip; i++) { 62562306a36Sopenharmony_ci if (thermal_zone_get_trip(tz, i, &trip)) 62662306a36Sopenharmony_ci continue; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci interrupt_en |= 62962306a36Sopenharmony_ci (1 << (EXYNOS7_TMU_INTEN_RISE0_SHIFT + i)); 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci interrupt_en |= 63362306a36Sopenharmony_ci interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); 63662306a36Sopenharmony_ci con |= (1 << EXYNOS7_PD_DET_EN_SHIFT); 63762306a36Sopenharmony_ci } else { 63862306a36Sopenharmony_ci con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); 63962306a36Sopenharmony_ci con &= ~(1 << EXYNOS7_PD_DET_EN_SHIFT); 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci writel(interrupt_en, data->base + EXYNOS7_TMU_REG_INTEN); 64362306a36Sopenharmony_ci writel(con, data->base + EXYNOS_TMU_REG_CONTROL); 64462306a36Sopenharmony_ci} 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_cistatic int exynos_get_temp(struct thermal_zone_device *tz, int *temp) 64762306a36Sopenharmony_ci{ 64862306a36Sopenharmony_ci struct exynos_tmu_data *data = thermal_zone_device_priv(tz); 64962306a36Sopenharmony_ci int value, ret = 0; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci if (!data || !data->tmu_read) 65262306a36Sopenharmony_ci return -EINVAL; 65362306a36Sopenharmony_ci else if (!data->enabled) 65462306a36Sopenharmony_ci /* 65562306a36Sopenharmony_ci * Called too early, probably 65662306a36Sopenharmony_ci * from thermal_zone_of_sensor_register(). 65762306a36Sopenharmony_ci */ 65862306a36Sopenharmony_ci return -EAGAIN; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci mutex_lock(&data->lock); 66162306a36Sopenharmony_ci clk_enable(data->clk); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci value = data->tmu_read(data); 66462306a36Sopenharmony_ci if (value < 0) 66562306a36Sopenharmony_ci ret = value; 66662306a36Sopenharmony_ci else 66762306a36Sopenharmony_ci *temp = code_to_temp(data, value) * MCELSIUS; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci clk_disable(data->clk); 67062306a36Sopenharmony_ci mutex_unlock(&data->lock); 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci return ret; 67362306a36Sopenharmony_ci} 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci#ifdef CONFIG_THERMAL_EMULATION 67662306a36Sopenharmony_cistatic u32 get_emul_con_reg(struct exynos_tmu_data *data, unsigned int val, 67762306a36Sopenharmony_ci int temp) 67862306a36Sopenharmony_ci{ 67962306a36Sopenharmony_ci if (temp) { 68062306a36Sopenharmony_ci temp /= MCELSIUS; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci val &= ~(EXYNOS_EMUL_TIME_MASK << EXYNOS_EMUL_TIME_SHIFT); 68362306a36Sopenharmony_ci val |= (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT); 68462306a36Sopenharmony_ci if (data->soc == SOC_ARCH_EXYNOS7) { 68562306a36Sopenharmony_ci val &= ~(EXYNOS7_EMUL_DATA_MASK << 68662306a36Sopenharmony_ci EXYNOS7_EMUL_DATA_SHIFT); 68762306a36Sopenharmony_ci val |= (temp_to_code(data, temp) << 68862306a36Sopenharmony_ci EXYNOS7_EMUL_DATA_SHIFT) | 68962306a36Sopenharmony_ci EXYNOS_EMUL_ENABLE; 69062306a36Sopenharmony_ci } else { 69162306a36Sopenharmony_ci val &= ~(EXYNOS_EMUL_DATA_MASK << 69262306a36Sopenharmony_ci EXYNOS_EMUL_DATA_SHIFT); 69362306a36Sopenharmony_ci val |= (temp_to_code(data, temp) << 69462306a36Sopenharmony_ci EXYNOS_EMUL_DATA_SHIFT) | 69562306a36Sopenharmony_ci EXYNOS_EMUL_ENABLE; 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci } else { 69862306a36Sopenharmony_ci val &= ~EXYNOS_EMUL_ENABLE; 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci return val; 70262306a36Sopenharmony_ci} 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_cistatic void exynos4412_tmu_set_emulation(struct exynos_tmu_data *data, 70562306a36Sopenharmony_ci int temp) 70662306a36Sopenharmony_ci{ 70762306a36Sopenharmony_ci unsigned int val; 70862306a36Sopenharmony_ci u32 emul_con; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci if (data->soc == SOC_ARCH_EXYNOS5260) 71162306a36Sopenharmony_ci emul_con = EXYNOS5260_EMUL_CON; 71262306a36Sopenharmony_ci else if (data->soc == SOC_ARCH_EXYNOS5433) 71362306a36Sopenharmony_ci emul_con = EXYNOS5433_TMU_EMUL_CON; 71462306a36Sopenharmony_ci else if (data->soc == SOC_ARCH_EXYNOS7) 71562306a36Sopenharmony_ci emul_con = EXYNOS7_TMU_REG_EMUL_CON; 71662306a36Sopenharmony_ci else 71762306a36Sopenharmony_ci emul_con = EXYNOS_EMUL_CON; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci val = readl(data->base + emul_con); 72062306a36Sopenharmony_ci val = get_emul_con_reg(data, val, temp); 72162306a36Sopenharmony_ci writel(val, data->base + emul_con); 72262306a36Sopenharmony_ci} 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_cistatic int exynos_tmu_set_emulation(struct thermal_zone_device *tz, int temp) 72562306a36Sopenharmony_ci{ 72662306a36Sopenharmony_ci struct exynos_tmu_data *data = thermal_zone_device_priv(tz); 72762306a36Sopenharmony_ci int ret = -EINVAL; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci if (data->soc == SOC_ARCH_EXYNOS4210) 73062306a36Sopenharmony_ci goto out; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci if (temp && temp < MCELSIUS) 73362306a36Sopenharmony_ci goto out; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci mutex_lock(&data->lock); 73662306a36Sopenharmony_ci clk_enable(data->clk); 73762306a36Sopenharmony_ci data->tmu_set_emulation(data, temp); 73862306a36Sopenharmony_ci clk_disable(data->clk); 73962306a36Sopenharmony_ci mutex_unlock(&data->lock); 74062306a36Sopenharmony_ci return 0; 74162306a36Sopenharmony_ciout: 74262306a36Sopenharmony_ci return ret; 74362306a36Sopenharmony_ci} 74462306a36Sopenharmony_ci#else 74562306a36Sopenharmony_ci#define exynos4412_tmu_set_emulation NULL 74662306a36Sopenharmony_cistatic int exynos_tmu_set_emulation(struct thermal_zone_device *tz, int temp) 74762306a36Sopenharmony_ci { return -EINVAL; } 74862306a36Sopenharmony_ci#endif /* CONFIG_THERMAL_EMULATION */ 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_cistatic int exynos4210_tmu_read(struct exynos_tmu_data *data) 75162306a36Sopenharmony_ci{ 75262306a36Sopenharmony_ci int ret = readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci /* "temp_code" should range between 75 and 175 */ 75562306a36Sopenharmony_ci return (ret < 75 || ret > 175) ? -ENODATA : ret; 75662306a36Sopenharmony_ci} 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_cistatic int exynos4412_tmu_read(struct exynos_tmu_data *data) 75962306a36Sopenharmony_ci{ 76062306a36Sopenharmony_ci return readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP); 76162306a36Sopenharmony_ci} 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_cistatic int exynos7_tmu_read(struct exynos_tmu_data *data) 76462306a36Sopenharmony_ci{ 76562306a36Sopenharmony_ci return readw(data->base + EXYNOS_TMU_REG_CURRENT_TEMP) & 76662306a36Sopenharmony_ci EXYNOS7_TMU_TEMP_MASK; 76762306a36Sopenharmony_ci} 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_cistatic void exynos_tmu_work(struct work_struct *work) 77062306a36Sopenharmony_ci{ 77162306a36Sopenharmony_ci struct exynos_tmu_data *data = container_of(work, 77262306a36Sopenharmony_ci struct exynos_tmu_data, irq_work); 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci thermal_zone_device_update(data->tzd, THERMAL_EVENT_UNSPECIFIED); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci mutex_lock(&data->lock); 77762306a36Sopenharmony_ci clk_enable(data->clk); 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci /* TODO: take action based on particular interrupt */ 78062306a36Sopenharmony_ci data->tmu_clear_irqs(data); 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci clk_disable(data->clk); 78362306a36Sopenharmony_ci mutex_unlock(&data->lock); 78462306a36Sopenharmony_ci enable_irq(data->irq); 78562306a36Sopenharmony_ci} 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_cistatic void exynos4210_tmu_clear_irqs(struct exynos_tmu_data *data) 78862306a36Sopenharmony_ci{ 78962306a36Sopenharmony_ci unsigned int val_irq; 79062306a36Sopenharmony_ci u32 tmu_intstat, tmu_intclear; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci if (data->soc == SOC_ARCH_EXYNOS5260) { 79362306a36Sopenharmony_ci tmu_intstat = EXYNOS5260_TMU_REG_INTSTAT; 79462306a36Sopenharmony_ci tmu_intclear = EXYNOS5260_TMU_REG_INTCLEAR; 79562306a36Sopenharmony_ci } else if (data->soc == SOC_ARCH_EXYNOS7) { 79662306a36Sopenharmony_ci tmu_intstat = EXYNOS7_TMU_REG_INTPEND; 79762306a36Sopenharmony_ci tmu_intclear = EXYNOS7_TMU_REG_INTPEND; 79862306a36Sopenharmony_ci } else if (data->soc == SOC_ARCH_EXYNOS5433) { 79962306a36Sopenharmony_ci tmu_intstat = EXYNOS5433_TMU_REG_INTPEND; 80062306a36Sopenharmony_ci tmu_intclear = EXYNOS5433_TMU_REG_INTPEND; 80162306a36Sopenharmony_ci } else { 80262306a36Sopenharmony_ci tmu_intstat = EXYNOS_TMU_REG_INTSTAT; 80362306a36Sopenharmony_ci tmu_intclear = EXYNOS_TMU_REG_INTCLEAR; 80462306a36Sopenharmony_ci } 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci val_irq = readl(data->base + tmu_intstat); 80762306a36Sopenharmony_ci /* 80862306a36Sopenharmony_ci * Clear the interrupts. Please note that the documentation for 80962306a36Sopenharmony_ci * Exynos3250, Exynos4412, Exynos5250 and Exynos5260 incorrectly 81062306a36Sopenharmony_ci * states that INTCLEAR register has a different placing of bits 81162306a36Sopenharmony_ci * responsible for FALL IRQs than INTSTAT register. Exynos5420 81262306a36Sopenharmony_ci * and Exynos5440 documentation is correct (Exynos4210 doesn't 81362306a36Sopenharmony_ci * support FALL IRQs at all). 81462306a36Sopenharmony_ci */ 81562306a36Sopenharmony_ci writel(val_irq, data->base + tmu_intclear); 81662306a36Sopenharmony_ci} 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_cistatic irqreturn_t exynos_tmu_irq(int irq, void *id) 81962306a36Sopenharmony_ci{ 82062306a36Sopenharmony_ci struct exynos_tmu_data *data = id; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci disable_irq_nosync(irq); 82362306a36Sopenharmony_ci schedule_work(&data->irq_work); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci return IRQ_HANDLED; 82662306a36Sopenharmony_ci} 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_cistatic const struct of_device_id exynos_tmu_match[] = { 82962306a36Sopenharmony_ci { 83062306a36Sopenharmony_ci .compatible = "samsung,exynos3250-tmu", 83162306a36Sopenharmony_ci .data = (const void *)SOC_ARCH_EXYNOS3250, 83262306a36Sopenharmony_ci }, { 83362306a36Sopenharmony_ci .compatible = "samsung,exynos4210-tmu", 83462306a36Sopenharmony_ci .data = (const void *)SOC_ARCH_EXYNOS4210, 83562306a36Sopenharmony_ci }, { 83662306a36Sopenharmony_ci .compatible = "samsung,exynos4412-tmu", 83762306a36Sopenharmony_ci .data = (const void *)SOC_ARCH_EXYNOS4412, 83862306a36Sopenharmony_ci }, { 83962306a36Sopenharmony_ci .compatible = "samsung,exynos5250-tmu", 84062306a36Sopenharmony_ci .data = (const void *)SOC_ARCH_EXYNOS5250, 84162306a36Sopenharmony_ci }, { 84262306a36Sopenharmony_ci .compatible = "samsung,exynos5260-tmu", 84362306a36Sopenharmony_ci .data = (const void *)SOC_ARCH_EXYNOS5260, 84462306a36Sopenharmony_ci }, { 84562306a36Sopenharmony_ci .compatible = "samsung,exynos5420-tmu", 84662306a36Sopenharmony_ci .data = (const void *)SOC_ARCH_EXYNOS5420, 84762306a36Sopenharmony_ci }, { 84862306a36Sopenharmony_ci .compatible = "samsung,exynos5420-tmu-ext-triminfo", 84962306a36Sopenharmony_ci .data = (const void *)SOC_ARCH_EXYNOS5420_TRIMINFO, 85062306a36Sopenharmony_ci }, { 85162306a36Sopenharmony_ci .compatible = "samsung,exynos5433-tmu", 85262306a36Sopenharmony_ci .data = (const void *)SOC_ARCH_EXYNOS5433, 85362306a36Sopenharmony_ci }, { 85462306a36Sopenharmony_ci .compatible = "samsung,exynos7-tmu", 85562306a36Sopenharmony_ci .data = (const void *)SOC_ARCH_EXYNOS7, 85662306a36Sopenharmony_ci }, 85762306a36Sopenharmony_ci { }, 85862306a36Sopenharmony_ci}; 85962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, exynos_tmu_match); 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_cistatic int exynos_map_dt_data(struct platform_device *pdev) 86262306a36Sopenharmony_ci{ 86362306a36Sopenharmony_ci struct exynos_tmu_data *data = platform_get_drvdata(pdev); 86462306a36Sopenharmony_ci struct resource res; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci if (!data || !pdev->dev.of_node) 86762306a36Sopenharmony_ci return -ENODEV; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci data->id = of_alias_get_id(pdev->dev.of_node, "tmuctrl"); 87062306a36Sopenharmony_ci if (data->id < 0) 87162306a36Sopenharmony_ci data->id = 0; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci data->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); 87462306a36Sopenharmony_ci if (data->irq <= 0) { 87562306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to get IRQ\n"); 87662306a36Sopenharmony_ci return -ENODEV; 87762306a36Sopenharmony_ci } 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci if (of_address_to_resource(pdev->dev.of_node, 0, &res)) { 88062306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to get Resource 0\n"); 88162306a36Sopenharmony_ci return -ENODEV; 88262306a36Sopenharmony_ci } 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci data->base = devm_ioremap(&pdev->dev, res.start, resource_size(&res)); 88562306a36Sopenharmony_ci if (!data->base) { 88662306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to ioremap memory\n"); 88762306a36Sopenharmony_ci return -EADDRNOTAVAIL; 88862306a36Sopenharmony_ci } 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci data->soc = (uintptr_t)of_device_get_match_data(&pdev->dev); 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci switch (data->soc) { 89362306a36Sopenharmony_ci case SOC_ARCH_EXYNOS4210: 89462306a36Sopenharmony_ci data->tmu_set_trip_temp = exynos4210_tmu_set_trip_temp; 89562306a36Sopenharmony_ci data->tmu_set_trip_hyst = exynos4210_tmu_set_trip_hyst; 89662306a36Sopenharmony_ci data->tmu_initialize = exynos4210_tmu_initialize; 89762306a36Sopenharmony_ci data->tmu_control = exynos4210_tmu_control; 89862306a36Sopenharmony_ci data->tmu_read = exynos4210_tmu_read; 89962306a36Sopenharmony_ci data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; 90062306a36Sopenharmony_ci data->ntrip = 4; 90162306a36Sopenharmony_ci data->gain = 15; 90262306a36Sopenharmony_ci data->reference_voltage = 7; 90362306a36Sopenharmony_ci data->efuse_value = 55; 90462306a36Sopenharmony_ci data->min_efuse_value = 40; 90562306a36Sopenharmony_ci data->max_efuse_value = 100; 90662306a36Sopenharmony_ci break; 90762306a36Sopenharmony_ci case SOC_ARCH_EXYNOS3250: 90862306a36Sopenharmony_ci case SOC_ARCH_EXYNOS4412: 90962306a36Sopenharmony_ci case SOC_ARCH_EXYNOS5250: 91062306a36Sopenharmony_ci case SOC_ARCH_EXYNOS5260: 91162306a36Sopenharmony_ci case SOC_ARCH_EXYNOS5420: 91262306a36Sopenharmony_ci case SOC_ARCH_EXYNOS5420_TRIMINFO: 91362306a36Sopenharmony_ci data->tmu_set_trip_temp = exynos4412_tmu_set_trip_temp; 91462306a36Sopenharmony_ci data->tmu_set_trip_hyst = exynos4412_tmu_set_trip_hyst; 91562306a36Sopenharmony_ci data->tmu_initialize = exynos4412_tmu_initialize; 91662306a36Sopenharmony_ci data->tmu_control = exynos4210_tmu_control; 91762306a36Sopenharmony_ci data->tmu_read = exynos4412_tmu_read; 91862306a36Sopenharmony_ci data->tmu_set_emulation = exynos4412_tmu_set_emulation; 91962306a36Sopenharmony_ci data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; 92062306a36Sopenharmony_ci data->ntrip = 4; 92162306a36Sopenharmony_ci data->gain = 8; 92262306a36Sopenharmony_ci data->reference_voltage = 16; 92362306a36Sopenharmony_ci data->efuse_value = 55; 92462306a36Sopenharmony_ci if (data->soc != SOC_ARCH_EXYNOS5420 && 92562306a36Sopenharmony_ci data->soc != SOC_ARCH_EXYNOS5420_TRIMINFO) 92662306a36Sopenharmony_ci data->min_efuse_value = 40; 92762306a36Sopenharmony_ci else 92862306a36Sopenharmony_ci data->min_efuse_value = 0; 92962306a36Sopenharmony_ci data->max_efuse_value = 100; 93062306a36Sopenharmony_ci break; 93162306a36Sopenharmony_ci case SOC_ARCH_EXYNOS5433: 93262306a36Sopenharmony_ci data->tmu_set_trip_temp = exynos5433_tmu_set_trip_temp; 93362306a36Sopenharmony_ci data->tmu_set_trip_hyst = exynos5433_tmu_set_trip_hyst; 93462306a36Sopenharmony_ci data->tmu_initialize = exynos5433_tmu_initialize; 93562306a36Sopenharmony_ci data->tmu_control = exynos5433_tmu_control; 93662306a36Sopenharmony_ci data->tmu_read = exynos4412_tmu_read; 93762306a36Sopenharmony_ci data->tmu_set_emulation = exynos4412_tmu_set_emulation; 93862306a36Sopenharmony_ci data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; 93962306a36Sopenharmony_ci data->ntrip = 8; 94062306a36Sopenharmony_ci data->gain = 8; 94162306a36Sopenharmony_ci if (res.start == EXYNOS5433_G3D_BASE) 94262306a36Sopenharmony_ci data->reference_voltage = 23; 94362306a36Sopenharmony_ci else 94462306a36Sopenharmony_ci data->reference_voltage = 16; 94562306a36Sopenharmony_ci data->efuse_value = 75; 94662306a36Sopenharmony_ci data->min_efuse_value = 40; 94762306a36Sopenharmony_ci data->max_efuse_value = 150; 94862306a36Sopenharmony_ci break; 94962306a36Sopenharmony_ci case SOC_ARCH_EXYNOS7: 95062306a36Sopenharmony_ci data->tmu_set_trip_temp = exynos7_tmu_set_trip_temp; 95162306a36Sopenharmony_ci data->tmu_set_trip_hyst = exynos7_tmu_set_trip_hyst; 95262306a36Sopenharmony_ci data->tmu_initialize = exynos7_tmu_initialize; 95362306a36Sopenharmony_ci data->tmu_control = exynos7_tmu_control; 95462306a36Sopenharmony_ci data->tmu_read = exynos7_tmu_read; 95562306a36Sopenharmony_ci data->tmu_set_emulation = exynos4412_tmu_set_emulation; 95662306a36Sopenharmony_ci data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; 95762306a36Sopenharmony_ci data->ntrip = 8; 95862306a36Sopenharmony_ci data->gain = 9; 95962306a36Sopenharmony_ci data->reference_voltage = 17; 96062306a36Sopenharmony_ci data->efuse_value = 75; 96162306a36Sopenharmony_ci data->min_efuse_value = 15; 96262306a36Sopenharmony_ci data->max_efuse_value = 100; 96362306a36Sopenharmony_ci break; 96462306a36Sopenharmony_ci default: 96562306a36Sopenharmony_ci dev_err(&pdev->dev, "Platform not supported\n"); 96662306a36Sopenharmony_ci return -EINVAL; 96762306a36Sopenharmony_ci } 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci data->cal_type = TYPE_ONE_POINT_TRIMMING; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci /* 97262306a36Sopenharmony_ci * Check if the TMU shares some registers and then try to map the 97362306a36Sopenharmony_ci * memory of common registers. 97462306a36Sopenharmony_ci */ 97562306a36Sopenharmony_ci if (data->soc != SOC_ARCH_EXYNOS5420_TRIMINFO) 97662306a36Sopenharmony_ci return 0; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci if (of_address_to_resource(pdev->dev.of_node, 1, &res)) { 97962306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to get Resource 1\n"); 98062306a36Sopenharmony_ci return -ENODEV; 98162306a36Sopenharmony_ci } 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci data->base_second = devm_ioremap(&pdev->dev, res.start, 98462306a36Sopenharmony_ci resource_size(&res)); 98562306a36Sopenharmony_ci if (!data->base_second) { 98662306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to ioremap memory\n"); 98762306a36Sopenharmony_ci return -ENOMEM; 98862306a36Sopenharmony_ci } 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci return 0; 99162306a36Sopenharmony_ci} 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_cistatic const struct thermal_zone_device_ops exynos_sensor_ops = { 99462306a36Sopenharmony_ci .get_temp = exynos_get_temp, 99562306a36Sopenharmony_ci .set_emul_temp = exynos_tmu_set_emulation, 99662306a36Sopenharmony_ci}; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_cistatic int exynos_tmu_probe(struct platform_device *pdev) 99962306a36Sopenharmony_ci{ 100062306a36Sopenharmony_ci struct exynos_tmu_data *data; 100162306a36Sopenharmony_ci int ret; 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci data = devm_kzalloc(&pdev->dev, sizeof(struct exynos_tmu_data), 100462306a36Sopenharmony_ci GFP_KERNEL); 100562306a36Sopenharmony_ci if (!data) 100662306a36Sopenharmony_ci return -ENOMEM; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci platform_set_drvdata(pdev, data); 100962306a36Sopenharmony_ci mutex_init(&data->lock); 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci /* 101262306a36Sopenharmony_ci * Try enabling the regulator if found 101362306a36Sopenharmony_ci * TODO: Add regulator as an SOC feature, so that regulator enable 101462306a36Sopenharmony_ci * is a compulsory call. 101562306a36Sopenharmony_ci */ 101662306a36Sopenharmony_ci data->regulator = devm_regulator_get_optional(&pdev->dev, "vtmu"); 101762306a36Sopenharmony_ci if (!IS_ERR(data->regulator)) { 101862306a36Sopenharmony_ci ret = regulator_enable(data->regulator); 101962306a36Sopenharmony_ci if (ret) { 102062306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to enable vtmu\n"); 102162306a36Sopenharmony_ci return ret; 102262306a36Sopenharmony_ci } 102362306a36Sopenharmony_ci } else { 102462306a36Sopenharmony_ci if (PTR_ERR(data->regulator) == -EPROBE_DEFER) 102562306a36Sopenharmony_ci return -EPROBE_DEFER; 102662306a36Sopenharmony_ci dev_info(&pdev->dev, "Regulator node (vtmu) not found\n"); 102762306a36Sopenharmony_ci } 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci ret = exynos_map_dt_data(pdev); 103062306a36Sopenharmony_ci if (ret) 103162306a36Sopenharmony_ci goto err_sensor; 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci INIT_WORK(&data->irq_work, exynos_tmu_work); 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci data->clk = devm_clk_get(&pdev->dev, "tmu_apbif"); 103662306a36Sopenharmony_ci if (IS_ERR(data->clk)) { 103762306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to get clock\n"); 103862306a36Sopenharmony_ci ret = PTR_ERR(data->clk); 103962306a36Sopenharmony_ci goto err_sensor; 104062306a36Sopenharmony_ci } 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci data->clk_sec = devm_clk_get(&pdev->dev, "tmu_triminfo_apbif"); 104362306a36Sopenharmony_ci if (IS_ERR(data->clk_sec)) { 104462306a36Sopenharmony_ci if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) { 104562306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to get triminfo clock\n"); 104662306a36Sopenharmony_ci ret = PTR_ERR(data->clk_sec); 104762306a36Sopenharmony_ci goto err_sensor; 104862306a36Sopenharmony_ci } 104962306a36Sopenharmony_ci } else { 105062306a36Sopenharmony_ci ret = clk_prepare(data->clk_sec); 105162306a36Sopenharmony_ci if (ret) { 105262306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to get clock\n"); 105362306a36Sopenharmony_ci goto err_sensor; 105462306a36Sopenharmony_ci } 105562306a36Sopenharmony_ci } 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci ret = clk_prepare(data->clk); 105862306a36Sopenharmony_ci if (ret) { 105962306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to get clock\n"); 106062306a36Sopenharmony_ci goto err_clk_sec; 106162306a36Sopenharmony_ci } 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci switch (data->soc) { 106462306a36Sopenharmony_ci case SOC_ARCH_EXYNOS5433: 106562306a36Sopenharmony_ci case SOC_ARCH_EXYNOS7: 106662306a36Sopenharmony_ci data->sclk = devm_clk_get(&pdev->dev, "tmu_sclk"); 106762306a36Sopenharmony_ci if (IS_ERR(data->sclk)) { 106862306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to get sclk\n"); 106962306a36Sopenharmony_ci ret = PTR_ERR(data->sclk); 107062306a36Sopenharmony_ci goto err_clk; 107162306a36Sopenharmony_ci } else { 107262306a36Sopenharmony_ci ret = clk_prepare_enable(data->sclk); 107362306a36Sopenharmony_ci if (ret) { 107462306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to enable sclk\n"); 107562306a36Sopenharmony_ci goto err_clk; 107662306a36Sopenharmony_ci } 107762306a36Sopenharmony_ci } 107862306a36Sopenharmony_ci break; 107962306a36Sopenharmony_ci default: 108062306a36Sopenharmony_ci break; 108162306a36Sopenharmony_ci } 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci /* 108462306a36Sopenharmony_ci * data->tzd must be registered before calling exynos_tmu_initialize(), 108562306a36Sopenharmony_ci * requesting irq and calling exynos_tmu_control(). 108662306a36Sopenharmony_ci */ 108762306a36Sopenharmony_ci data->tzd = devm_thermal_of_zone_register(&pdev->dev, 0, data, 108862306a36Sopenharmony_ci &exynos_sensor_ops); 108962306a36Sopenharmony_ci if (IS_ERR(data->tzd)) { 109062306a36Sopenharmony_ci ret = PTR_ERR(data->tzd); 109162306a36Sopenharmony_ci if (ret != -EPROBE_DEFER) 109262306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to register sensor: %d\n", 109362306a36Sopenharmony_ci ret); 109462306a36Sopenharmony_ci goto err_sclk; 109562306a36Sopenharmony_ci } 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci ret = exynos_tmu_initialize(pdev); 109862306a36Sopenharmony_ci if (ret) { 109962306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to initialize TMU\n"); 110062306a36Sopenharmony_ci goto err_sclk; 110162306a36Sopenharmony_ci } 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci ret = devm_request_irq(&pdev->dev, data->irq, exynos_tmu_irq, 110462306a36Sopenharmony_ci IRQF_TRIGGER_RISING | IRQF_SHARED, dev_name(&pdev->dev), data); 110562306a36Sopenharmony_ci if (ret) { 110662306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq); 110762306a36Sopenharmony_ci goto err_sclk; 110862306a36Sopenharmony_ci } 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci exynos_tmu_control(pdev, true); 111162306a36Sopenharmony_ci return 0; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_cierr_sclk: 111462306a36Sopenharmony_ci clk_disable_unprepare(data->sclk); 111562306a36Sopenharmony_cierr_clk: 111662306a36Sopenharmony_ci clk_unprepare(data->clk); 111762306a36Sopenharmony_cierr_clk_sec: 111862306a36Sopenharmony_ci if (!IS_ERR(data->clk_sec)) 111962306a36Sopenharmony_ci clk_unprepare(data->clk_sec); 112062306a36Sopenharmony_cierr_sensor: 112162306a36Sopenharmony_ci if (!IS_ERR(data->regulator)) 112262306a36Sopenharmony_ci regulator_disable(data->regulator); 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci return ret; 112562306a36Sopenharmony_ci} 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_cistatic int exynos_tmu_remove(struct platform_device *pdev) 112862306a36Sopenharmony_ci{ 112962306a36Sopenharmony_ci struct exynos_tmu_data *data = platform_get_drvdata(pdev); 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci exynos_tmu_control(pdev, false); 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci clk_disable_unprepare(data->sclk); 113462306a36Sopenharmony_ci clk_unprepare(data->clk); 113562306a36Sopenharmony_ci if (!IS_ERR(data->clk_sec)) 113662306a36Sopenharmony_ci clk_unprepare(data->clk_sec); 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci if (!IS_ERR(data->regulator)) 113962306a36Sopenharmony_ci regulator_disable(data->regulator); 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci return 0; 114262306a36Sopenharmony_ci} 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 114562306a36Sopenharmony_cistatic int exynos_tmu_suspend(struct device *dev) 114662306a36Sopenharmony_ci{ 114762306a36Sopenharmony_ci exynos_tmu_control(to_platform_device(dev), false); 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci return 0; 115062306a36Sopenharmony_ci} 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_cistatic int exynos_tmu_resume(struct device *dev) 115362306a36Sopenharmony_ci{ 115462306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci exynos_tmu_initialize(pdev); 115762306a36Sopenharmony_ci exynos_tmu_control(pdev, true); 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci return 0; 116062306a36Sopenharmony_ci} 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(exynos_tmu_pm, 116362306a36Sopenharmony_ci exynos_tmu_suspend, exynos_tmu_resume); 116462306a36Sopenharmony_ci#define EXYNOS_TMU_PM (&exynos_tmu_pm) 116562306a36Sopenharmony_ci#else 116662306a36Sopenharmony_ci#define EXYNOS_TMU_PM NULL 116762306a36Sopenharmony_ci#endif 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_cistatic struct platform_driver exynos_tmu_driver = { 117062306a36Sopenharmony_ci .driver = { 117162306a36Sopenharmony_ci .name = "exynos-tmu", 117262306a36Sopenharmony_ci .pm = EXYNOS_TMU_PM, 117362306a36Sopenharmony_ci .of_match_table = exynos_tmu_match, 117462306a36Sopenharmony_ci }, 117562306a36Sopenharmony_ci .probe = exynos_tmu_probe, 117662306a36Sopenharmony_ci .remove = exynos_tmu_remove, 117762306a36Sopenharmony_ci}; 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_cimodule_platform_driver(exynos_tmu_driver); 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ciMODULE_DESCRIPTION("Exynos TMU Driver"); 118262306a36Sopenharmony_ciMODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>"); 118362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 118462306a36Sopenharmony_ciMODULE_ALIAS("platform:exynos-tmu"); 1185