diff options
Diffstat (limited to 'drivers/thermal/samsung/exynos_tmu.c')
| -rw-r--r-- | drivers/thermal/samsung/exynos_tmu.c | 1252 |
1 files changed, 473 insertions, 779 deletions
diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c index 7b8ef09d2b3c..47a99b3c5395 100644 --- a/drivers/thermal/samsung/exynos_tmu.c +++ b/drivers/thermal/samsung/exynos_tmu.c @@ -1,5 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* - * exynos_tmu.c - Samsung EXYNOS TMU (Thermal Management Unit) + * exynos_tmu.c - Samsung Exynos TMU (Thermal Management Unit) * * Copyright (C) 2014 Samsung Electronics * Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> @@ -8,21 +9,6 @@ * Copyright (C) 2011 Samsung Electronics * Donggeun Kim <dg77.kim@samsung.com> * Amit Daniel Kachhap <amit.kachhap@linaro.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ #include <linux/clk.h> @@ -34,9 +20,9 @@ #include <linux/of_irq.h> #include <linux/platform_device.h> #include <linux/regulator/consumer.h> +#include <linux/thermal.h> -#include "exynos_tmu.h" -#include "../thermal_core.h" +#include <dt-bindings/thermal/thermal_exynos.h> /* Exynos generic registers */ #define EXYNOS_TMU_REG_TRIMINFO 0x0 @@ -75,9 +61,6 @@ #define EXYNOS_TMU_THERM_TRIP_EN_SHIFT 12 #define EXYNOS_TMU_INTEN_RISE0_SHIFT 0 -#define EXYNOS_TMU_INTEN_RISE1_SHIFT 4 -#define EXYNOS_TMU_INTEN_RISE2_SHIFT 8 -#define EXYNOS_TMU_INTEN_RISE3_SHIFT 12 #define EXYNOS_TMU_INTEN_FALL0_SHIFT 16 #define EXYNOS_EMUL_TIME 0x57F0 @@ -98,11 +81,6 @@ #define EXYNOS4412_MUX_ADDR_SHIFT 20 /* Exynos5433 specific registers */ -#define EXYNOS5433_TMU_REG_CONTROL1 0x024 -#define EXYNOS5433_TMU_SAMPLING_INTERVAL 0x02c -#define EXYNOS5433_TMU_COUNTER_VALUE0 0x030 -#define EXYNOS5433_TMU_COUNTER_VALUE1 0x034 -#define EXYNOS5433_TMU_REG_CURRENT_TEMP1 0x044 #define EXYNOS5433_THD_TEMP_RISE3_0 0x050 #define EXYNOS5433_THD_TEMP_RISE7_4 0x054 #define EXYNOS5433_THD_TEMP_FALL3_0 0x060 @@ -123,27 +101,7 @@ #define EXYNOS5433_PD_DET_EN 1 -/*exynos5440 specific registers*/ -#define EXYNOS5440_TMU_S0_7_TRIM 0x000 -#define EXYNOS5440_TMU_S0_7_CTRL 0x020 -#define EXYNOS5440_TMU_S0_7_DEBUG 0x040 -#define EXYNOS5440_TMU_S0_7_TEMP 0x0f0 -#define EXYNOS5440_TMU_S0_7_TH0 0x110 -#define EXYNOS5440_TMU_S0_7_TH1 0x130 -#define EXYNOS5440_TMU_S0_7_TH2 0x150 -#define EXYNOS5440_TMU_S0_7_IRQEN 0x210 -#define EXYNOS5440_TMU_S0_7_IRQ 0x230 -/* exynos5440 common registers */ -#define EXYNOS5440_TMU_IRQ_STATUS 0x000 -#define EXYNOS5440_TMU_PMIN 0x004 - -#define EXYNOS5440_TMU_INTEN_RISE0_SHIFT 0 -#define EXYNOS5440_TMU_INTEN_RISE1_SHIFT 1 -#define EXYNOS5440_TMU_INTEN_RISE2_SHIFT 2 -#define EXYNOS5440_TMU_INTEN_RISE3_SHIFT 3 -#define EXYNOS5440_TMU_INTEN_FALL0_SHIFT 4 -#define EXYNOS5440_TMU_TH_RISE4_SHIFT 24 -#define EXYNOS5440_EFUSE_SWAP_OFFSET 8 +#define EXYNOS5433_G3D_BASE 0x10070000 /* Exynos7 specific registers */ #define EXYNOS7_THD_TEMP_RISE7_6 0x50 @@ -155,36 +113,57 @@ #define EXYNOS7_TMU_TEMP_MASK 0x1ff #define EXYNOS7_PD_DET_EN_SHIFT 23 #define EXYNOS7_TMU_INTEN_RISE0_SHIFT 0 -#define EXYNOS7_TMU_INTEN_RISE1_SHIFT 1 -#define EXYNOS7_TMU_INTEN_RISE2_SHIFT 2 -#define EXYNOS7_TMU_INTEN_RISE3_SHIFT 3 -#define EXYNOS7_TMU_INTEN_RISE4_SHIFT 4 -#define EXYNOS7_TMU_INTEN_RISE5_SHIFT 5 -#define EXYNOS7_TMU_INTEN_RISE6_SHIFT 6 -#define EXYNOS7_TMU_INTEN_RISE7_SHIFT 7 #define EXYNOS7_EMUL_DATA_SHIFT 7 #define EXYNOS7_EMUL_DATA_MASK 0x1ff +#define EXYNOS_FIRST_POINT_TRIM 25 +#define EXYNOS_SECOND_POINT_TRIM 85 + +#define EXYNOS_NOISE_CANCEL_MODE 4 + #define MCELSIUS 1000 + +enum soc_type { + SOC_ARCH_EXYNOS3250 = 1, + SOC_ARCH_EXYNOS4210, + SOC_ARCH_EXYNOS4412, + SOC_ARCH_EXYNOS5250, + SOC_ARCH_EXYNOS5260, + SOC_ARCH_EXYNOS5420, + SOC_ARCH_EXYNOS5420_TRIMINFO, + SOC_ARCH_EXYNOS5433, + SOC_ARCH_EXYNOS7, +}; + /** * struct exynos_tmu_data : A structure to hold the private data of the TMU - driver - * @id: identifier of the one instance of the TMU controller. - * @pdata: pointer to the tmu platform/configuration data + * driver * @base: base address of the single instance of the TMU controller. * @base_second: base address of the common registers of the TMU controller. * @irq: irq number of the TMU controller. * @soc: id of the SOC type. - * @irq_work: pointer to the irq work structure. * @lock: lock to implement synchronization. * @clk: pointer to the clock structure. * @clk_sec: pointer to the clock structure for accessing the base_second. * @sclk: pointer to the clock structure for accessing the tmu special clk. + * @cal_type: calibration type for temperature + * @efuse_value: SoC defined fuse value + * @min_efuse_value: minimum valid trimming data + * @max_efuse_value: maximum valid trimming data * @temp_error1: fused value of the first point trim. * @temp_error2: fused value of the second point trim. - * @regulator: pointer to the TMU regulator structure. - * @reg_conf: pointer to structure to register with core thermal. - * @ntrip: number of supported trip points. + * @gain: gain of amplifier in the positive-TC generator block + * 0 < gain <= 15 + * @reference_voltage: reference voltage of amplifier + * in the positive-TC generator block + * 0 < reference_voltage <= 31 + * @tzd: pointer to thermal_zone_device structure + * @enabled: current status of TMU device + * @tmu_set_low_temp: SoC specific method to set trip (falling threshold) + * @tmu_set_high_temp: SoC specific method to set trip (rising threshold) + * @tmu_set_crit_temp: SoC specific method to set critical temperature + * @tmu_disable_low: SoC specific method to disable an interrupt (falling threshold) + * @tmu_disable_high: SoC specific method to disable an interrupt (rising threshold) * @tmu_initialize: SoC specific TMU initialization method * @tmu_control: SoC specific TMU control method * @tmu_read: SoC specific TMU temperature read method @@ -192,79 +171,47 @@ * @tmu_clear_irqs: SoC specific TMU interrupts clearing method */ struct exynos_tmu_data { - int id; - struct exynos_tmu_platform_data *pdata; void __iomem *base; void __iomem *base_second; int irq; enum soc_type soc; - struct work_struct irq_work; struct mutex lock; struct clk *clk, *clk_sec, *sclk; + u32 cal_type; + u32 efuse_value; + u32 min_efuse_value; + u32 max_efuse_value; u16 temp_error1, temp_error2; - struct regulator *regulator; + u8 gain; + u8 reference_voltage; struct thermal_zone_device *tzd; - unsigned int ntrip; - - int (*tmu_initialize)(struct platform_device *pdev); + bool enabled; + + void (*tmu_set_low_temp)(struct exynos_tmu_data *data, u8 temp); + void (*tmu_set_high_temp)(struct exynos_tmu_data *data, u8 temp); + void (*tmu_set_crit_temp)(struct exynos_tmu_data *data, u8 temp); + void (*tmu_disable_low)(struct exynos_tmu_data *data); + void (*tmu_disable_high)(struct exynos_tmu_data *data); + void (*tmu_initialize)(struct platform_device *pdev); void (*tmu_control)(struct platform_device *pdev, bool on); int (*tmu_read)(struct exynos_tmu_data *data); void (*tmu_set_emulation)(struct exynos_tmu_data *data, int temp); void (*tmu_clear_irqs)(struct exynos_tmu_data *data); }; -static void exynos_report_trigger(struct exynos_tmu_data *p) -{ - char data[10], *envp[] = { data, NULL }; - struct thermal_zone_device *tz = p->tzd; - int temp; - unsigned int i; - - if (!tz) { - pr_err("No thermal zone device defined\n"); - return; - } - - thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); - - mutex_lock(&tz->lock); - /* Find the level for which trip happened */ - for (i = 0; i < of_thermal_get_ntrips(tz); i++) { - tz->ops->get_trip_temp(tz, i, &temp); - if (tz->last_temperature < temp) - break; - } - - snprintf(data, sizeof(data), "%u", i); - kobject_uevent_env(&tz->device.kobj, KOBJ_CHANGE, envp); - mutex_unlock(&tz->lock); -} - /* * TMU treats temperature as a mapped temperature code. * The temperature is converted differently depending on the calibration type. */ static int temp_to_code(struct exynos_tmu_data *data, u8 temp) { - struct exynos_tmu_platform_data *pdata = data->pdata; - int temp_code; - - switch (pdata->cal_type) { - case TYPE_TWO_POINT_TRIMMING: - temp_code = (temp - pdata->first_point_trim) * - (data->temp_error2 - data->temp_error1) / - (pdata->second_point_trim - pdata->first_point_trim) + - data->temp_error1; - break; - case TYPE_ONE_POINT_TRIMMING: - temp_code = temp + data->temp_error1 - pdata->first_point_trim; - break; - default: - temp_code = temp + pdata->default_temp_offset; - break; - } + if (data->cal_type == TYPE_ONE_POINT_TRIMMING) + return temp + data->temp_error1 - EXYNOS_FIRST_POINT_TRIM; - return temp_code; + return (temp - EXYNOS_FIRST_POINT_TRIM) * + (data->temp_error2 - data->temp_error1) / + (EXYNOS_SECOND_POINT_TRIM - EXYNOS_FIRST_POINT_TRIM) + + data->temp_error1; } /* @@ -273,120 +220,105 @@ static int temp_to_code(struct exynos_tmu_data *data, u8 temp) */ static int code_to_temp(struct exynos_tmu_data *data, u16 temp_code) { - struct exynos_tmu_platform_data *pdata = data->pdata; - int temp; - - switch (pdata->cal_type) { - case TYPE_TWO_POINT_TRIMMING: - temp = (temp_code - data->temp_error1) * - (pdata->second_point_trim - pdata->first_point_trim) / - (data->temp_error2 - data->temp_error1) + - pdata->first_point_trim; - break; - case TYPE_ONE_POINT_TRIMMING: - temp = temp_code - data->temp_error1 + pdata->first_point_trim; - break; - default: - temp = temp_code - pdata->default_temp_offset; - break; - } + if (data->cal_type == TYPE_ONE_POINT_TRIMMING) + return temp_code - data->temp_error1 + EXYNOS_FIRST_POINT_TRIM; - return temp; + return (temp_code - data->temp_error1) * + (EXYNOS_SECOND_POINT_TRIM - EXYNOS_FIRST_POINT_TRIM) / + (data->temp_error2 - data->temp_error1) + + EXYNOS_FIRST_POINT_TRIM; } static void sanitize_temp_error(struct exynos_tmu_data *data, u32 trim_info) { - struct exynos_tmu_platform_data *pdata = data->pdata; + u16 tmu_temp_mask = + (data->soc == SOC_ARCH_EXYNOS7) ? EXYNOS7_TMU_TEMP_MASK + : EXYNOS_TMU_TEMP_MASK; - data->temp_error1 = trim_info & EXYNOS_TMU_TEMP_MASK; + data->temp_error1 = trim_info & tmu_temp_mask; data->temp_error2 = ((trim_info >> EXYNOS_TRIMINFO_85_SHIFT) & EXYNOS_TMU_TEMP_MASK); if (!data->temp_error1 || - (pdata->min_efuse_value > data->temp_error1) || - (data->temp_error1 > pdata->max_efuse_value)) - data->temp_error1 = pdata->efuse_value & EXYNOS_TMU_TEMP_MASK; + (data->min_efuse_value > data->temp_error1) || + (data->temp_error1 > data->max_efuse_value)) + data->temp_error1 = data->efuse_value & EXYNOS_TMU_TEMP_MASK; if (!data->temp_error2) data->temp_error2 = - (pdata->efuse_value >> EXYNOS_TRIMINFO_85_SHIFT) & + (data->efuse_value >> EXYNOS_TRIMINFO_85_SHIFT) & EXYNOS_TMU_TEMP_MASK; } -static u32 get_th_reg(struct exynos_tmu_data *data, u32 threshold, bool falling) +static int exynos_tmu_initialize(struct platform_device *pdev) { - struct thermal_zone_device *tz = data->tzd; - const struct thermal_trip * const trips = - of_thermal_get_trip_points(tz); - unsigned long temp; - int i; - - if (!trips) { - pr_err("%s: Cannot get trip points from of-thermal.c!\n", - __func__); - return 0; - } - - for (i = 0; i < of_thermal_get_ntrips(tz); i++) { - if (trips[i].type == THERMAL_TRIP_CRITICAL) - continue; + struct exynos_tmu_data *data = platform_get_drvdata(pdev); + unsigned int status; + int ret = 0; - temp = trips[i].temperature / MCELSIUS; - if (falling) - temp -= (trips[i].hysteresis / MCELSIUS); - else - threshold &= ~(0xff << 8 * i); + mutex_lock(&data->lock); + clk_enable(data->clk); + if (!IS_ERR(data->clk_sec)) + clk_enable(data->clk_sec); - threshold |= temp_to_code(data, temp) << 8 * i; + status = readb(data->base + EXYNOS_TMU_REG_STATUS); + if (!status) { + ret = -EBUSY; + } else { + data->tmu_initialize(pdev); + data->tmu_clear_irqs(data); } - return threshold; + if (!IS_ERR(data->clk_sec)) + clk_disable(data->clk_sec); + clk_disable(data->clk); + mutex_unlock(&data->lock); + + return ret; } -static int exynos_tmu_initialize(struct platform_device *pdev) +static int exynos_thermal_zone_configure(struct platform_device *pdev) { struct exynos_tmu_data *data = platform_get_drvdata(pdev); - int ret; + struct thermal_zone_device *tzd = data->tzd; + int ret, temp; - if (of_thermal_get_ntrips(data->tzd) > data->ntrip) { - dev_info(&pdev->dev, - "More trip points than supported by this TMU.\n"); - dev_info(&pdev->dev, - "%d trip points should be configured in polling mode.\n", - (of_thermal_get_ntrips(data->tzd) - data->ntrip)); + ret = thermal_zone_get_crit_temp(tzd, &temp); + if (ret) { + /* FIXME: Remove this special case */ + if (data->soc == SOC_ARCH_EXYNOS5433) + return 0; + + dev_err(&pdev->dev, + "No CRITICAL trip point defined in device tree!\n"); + return ret; } mutex_lock(&data->lock); clk_enable(data->clk); - if (!IS_ERR(data->clk_sec)) - clk_enable(data->clk_sec); - ret = data->tmu_initialize(pdev); + + data->tmu_set_crit_temp(data, temp / MCELSIUS); + clk_disable(data->clk); mutex_unlock(&data->lock); - if (!IS_ERR(data->clk_sec)) - clk_disable(data->clk_sec); - return ret; + return 0; } static u32 get_con_reg(struct exynos_tmu_data *data, u32 con) { - struct exynos_tmu_platform_data *pdata = data->pdata; - if (data->soc == SOC_ARCH_EXYNOS4412 || data->soc == SOC_ARCH_EXYNOS3250) con |= (EXYNOS4412_MUX_ADDR_VALUE << EXYNOS4412_MUX_ADDR_SHIFT); con &= ~(EXYNOS_TMU_REF_VOLTAGE_MASK << EXYNOS_TMU_REF_VOLTAGE_SHIFT); - con |= pdata->reference_voltage << EXYNOS_TMU_REF_VOLTAGE_SHIFT; + con |= data->reference_voltage << EXYNOS_TMU_REF_VOLTAGE_SHIFT; con &= ~(EXYNOS_TMU_BUF_SLOPE_SEL_MASK << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT); - con |= (pdata->gain << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT); + con |= (data->gain << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT); - if (pdata->noise_cancel_mode) { - con &= ~(EXYNOS_TMU_TRIP_MODE_MASK << EXYNOS_TMU_TRIP_MODE_SHIFT); - con |= (pdata->noise_cancel_mode << EXYNOS_TMU_TRIP_MODE_SHIFT); - } + con &= ~(EXYNOS_TMU_TRIP_MODE_MASK << EXYNOS_TMU_TRIP_MODE_SHIFT); + con |= (EXYNOS_NOISE_CANCEL_MODE << EXYNOS_TMU_TRIP_MODE_SHIFT); return con; } @@ -398,69 +330,121 @@ static void exynos_tmu_control(struct platform_device *pdev, bool on) mutex_lock(&data->lock); clk_enable(data->clk); data->tmu_control(pdev, on); + data->enabled = on; clk_disable(data->clk); mutex_unlock(&data->lock); } -static int exynos4210_tmu_initialize(struct platform_device *pdev) +static void exynos_tmu_update_bit(struct exynos_tmu_data *data, int reg_off, + int bit_off, bool enable) { - struct exynos_tmu_data *data = platform_get_drvdata(pdev); - struct thermal_zone_device *tz = data->tzd; - const struct thermal_trip * const trips = - of_thermal_get_trip_points(tz); - int ret = 0, threshold_code, i; - unsigned long reference, temp; - unsigned int status; + u32 interrupt_en; - if (!trips) { - pr_err("%s: Cannot get trip points from of-thermal.c!\n", - __func__); - ret = -ENODEV; - goto out; - } + interrupt_en = readl(data->base + reg_off); + if (enable) + interrupt_en |= BIT(bit_off); + else + interrupt_en &= ~BIT(bit_off); + writel(interrupt_en, data->base + reg_off); +} - status = readb(data->base + EXYNOS_TMU_REG_STATUS); - if (!status) { - ret = -EBUSY; - goto out; - } +static void exynos_tmu_update_temp(struct exynos_tmu_data *data, int reg_off, + int bit_off, u8 temp) +{ + u16 tmu_temp_mask; + u32 th; - sanitize_temp_error(data, readl(data->base + EXYNOS_TMU_REG_TRIMINFO)); + tmu_temp_mask = + (data->soc == SOC_ARCH_EXYNOS7) ? EXYNOS7_TMU_TEMP_MASK + : EXYNOS_TMU_TEMP_MASK; - /* Write temperature code for threshold */ - reference = trips[0].temperature / MCELSIUS; - threshold_code = temp_to_code(data, reference); - if (threshold_code < 0) { - ret = threshold_code; - goto out; - } - writeb(threshold_code, data->base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP); + th = readl(data->base + reg_off); + th &= ~(tmu_temp_mask << bit_off); + th |= temp_to_code(data, temp) << bit_off; + writel(th, data->base + reg_off); +} - for (i = 0; i < of_thermal_get_ntrips(tz); i++) { - temp = trips[i].temperature / MCELSIUS; - writeb(temp - reference, data->base + - EXYNOS4210_TMU_REG_TRIG_LEVEL0 + i * 4); - } +static void exynos4210_tmu_set_low_temp(struct exynos_tmu_data *data, u8 temp) +{ + /* + * Failing thresholds are not supported on Exynos 4210. + * We use polling instead. + */ +} - data->tmu_clear_irqs(data); -out: - return ret; +static void exynos4210_tmu_set_high_temp(struct exynos_tmu_data *data, u8 temp) +{ + temp = temp_to_code(data, temp); + writeb(temp, data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL0 + 4); + exynos_tmu_update_bit(data, EXYNOS_TMU_REG_INTEN, + EXYNOS_TMU_INTEN_RISE0_SHIFT + 4, true); +} + +static void exynos4210_tmu_disable_low(struct exynos_tmu_data *data) +{ + /* Again, this is handled by polling. */ +} + +static void exynos4210_tmu_disable_high(struct exynos_tmu_data *data) +{ + exynos_tmu_update_bit(data, EXYNOS_TMU_REG_INTEN, + EXYNOS_TMU_INTEN_RISE0_SHIFT + 4, false); +} + +static void exynos4210_tmu_set_crit_temp(struct exynos_tmu_data *data, u8 temp) +{ + /* + * Hardware critical temperature handling is not supported on Exynos 4210. + * We still set the critical temperature threshold, but this is only to + * make sure it is handled as soon as possible. It is just a normal interrupt. + */ + + temp = temp_to_code(data, temp); + writeb(temp, data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL0 + 12); + exynos_tmu_update_bit(data, EXYNOS_TMU_REG_INTEN, + EXYNOS_TMU_INTEN_RISE0_SHIFT + 12, true); } -static int exynos4412_tmu_initialize(struct platform_device *pdev) +static void exynos4210_tmu_initialize(struct platform_device *pdev) { struct exynos_tmu_data *data = platform_get_drvdata(pdev); - const struct thermal_trip * const trips = - of_thermal_get_trip_points(data->tzd); - unsigned int status, trim_info, con, ctrl, rising_threshold; - int ret = 0, threshold_code, i; - unsigned long crit_temp = 0; - status = readb(data->base + EXYNOS_TMU_REG_STATUS); - if (!status) { - ret = -EBUSY; - goto out; - } + sanitize_temp_error(data, readl(data->base + EXYNOS_TMU_REG_TRIMINFO)); + + writeb(0, data->base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP); +} + +static void exynos4412_tmu_set_low_temp(struct exynos_tmu_data *data, u8 temp) +{ + exynos_tmu_update_temp(data, EXYNOS_THD_TEMP_FALL, 0, temp); + exynos_tmu_update_bit(data, EXYNOS_TMU_REG_INTEN, + EXYNOS_TMU_INTEN_FALL0_SHIFT, true); +} + +static void exynos4412_tmu_set_high_temp(struct exynos_tmu_data *data, u8 temp) +{ + exynos_tmu_update_temp(data, EXYNOS_THD_TEMP_RISE, 8, temp); + exynos_tmu_update_bit(data, EXYNOS_TMU_REG_INTEN, + EXYNOS_TMU_INTEN_RISE0_SHIFT + 4, true); +} + +static void exynos4412_tmu_disable_low(struct exynos_tmu_data *data) +{ + exynos_tmu_update_bit(data, EXYNOS_TMU_REG_INTEN, + EXYNOS_TMU_INTEN_FALL0_SHIFT, false); +} + +static void exynos4412_tmu_set_crit_temp(struct exynos_tmu_data *data, u8 temp) +{ + exynos_tmu_update_temp(data, EXYNOS_THD_TEMP_RISE, 24, temp); + exynos_tmu_update_bit(data, EXYNOS_TMU_REG_CONTROL, + EXYNOS_TMU_THERM_TRIP_EN_SHIFT, true); +} + +static void exynos4412_tmu_initialize(struct platform_device *pdev) +{ + struct exynos_tmu_data *data = platform_get_drvdata(pdev); + unsigned int trim_info, ctrl; if (data->soc == SOC_ARCH_EXYNOS3250 || data->soc == SOC_ARCH_EXYNOS4412 || @@ -482,58 +466,48 @@ static int exynos4412_tmu_initialize(struct platform_device *pdev) trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); sanitize_temp_error(data, trim_info); +} - /* Write temperature code for rising and falling threshold */ - rising_threshold = readl(data->base + EXYNOS_THD_TEMP_RISE); - rising_threshold = get_th_reg(data, rising_threshold, false); - writel(rising_threshold, data->base + EXYNOS_THD_TEMP_RISE); - writel(get_th_reg(data, 0, true), data->base + EXYNOS_THD_TEMP_FALL); - - data->tmu_clear_irqs(data); +static void exynos5433_tmu_set_low_temp(struct exynos_tmu_data *data, u8 temp) +{ + exynos_tmu_update_temp(data, EXYNOS5433_THD_TEMP_FALL3_0, 0, temp); + exynos_tmu_update_bit(data, EXYNOS5433_TMU_REG_INTEN, + EXYNOS_TMU_INTEN_FALL0_SHIFT, true); +} - /* if last threshold limit is also present */ - for (i = 0; i < of_thermal_get_ntrips(data->tzd); i++) { - if (trips[i].type == THERMAL_TRIP_CRITICAL) { - crit_temp = trips[i].temperature; - break; - } - } +static void exynos5433_tmu_set_high_temp(struct exynos_tmu_data *data, u8 temp) +{ + exynos_tmu_update_temp(data, EXYNOS5433_THD_TEMP_RISE3_0, 8, temp); + exynos_tmu_update_bit(data, EXYNOS5433_TMU_REG_INTEN, + EXYNOS7_TMU_INTEN_RISE0_SHIFT + 1, true); +} - if (i == of_thermal_get_ntrips(data->tzd)) { - pr_err("%s: No CRITICAL trip point defined at of-thermal.c!\n", - __func__); - ret = -EINVAL; - goto out; - } +static void exynos5433_tmu_disable_low(struct exynos_tmu_data *data) +{ + exynos_tmu_update_bit(data, EXYNOS5433_TMU_REG_INTEN, + EXYNOS_TMU_INTEN_FALL0_SHIFT, false); +} - threshold_code = temp_to_code(data, crit_temp / MCELSIUS); - /* 1-4 level to be assigned in th0 reg */ - rising_threshold &= ~(0xff << 8 * i); - rising_threshold |= threshold_code << 8 * i; - writel(rising_threshold, data->base + EXYNOS_THD_TEMP_RISE); - con = readl(data->base + EXYNOS_TMU_REG_CONTROL); - con |= (1 << EXYNOS_TMU_THERM_TRIP_EN_SHIFT); - writel(con, data->base + EXYNOS_TMU_REG_CONTROL); +static void exynos5433_tmu_disable_high(struct exynos_tmu_data *data) +{ + exynos_tmu_update_bit(data, EXYNOS5433_TMU_REG_INTEN, + EXYNOS7_TMU_INTEN_RISE0_SHIFT + 1, false); +} -out: - return ret; +static void exynos5433_tmu_set_crit_temp(struct exynos_tmu_data *data, u8 temp) +{ + exynos_tmu_update_temp(data, EXYNOS5433_THD_TEMP_RISE7_4, 24, temp); + exynos_tmu_update_bit(data, EXYNOS_TMU_REG_CONTROL, + EXYNOS_TMU_THERM_TRIP_EN_SHIFT, true); + exynos_tmu_update_bit(data, EXYNOS5433_TMU_REG_INTEN, + EXYNOS7_TMU_INTEN_RISE0_SHIFT + 7, true); } -static int exynos5433_tmu_initialize(struct platform_device *pdev) +static void exynos5433_tmu_initialize(struct platform_device *pdev) { struct exynos_tmu_data *data = platform_get_drvdata(pdev); - struct exynos_tmu_platform_data *pdata = data->pdata; - struct thermal_zone_device *tz = data->tzd; - unsigned int status, trim_info; - unsigned int rising_threshold = 0, falling_threshold = 0; - int temp, temp_hist; - int ret = 0, threshold_code, i, sensor_id, cal_type; - - status = readb(data->base + EXYNOS_TMU_REG_STATUS); - if (!status) { - ret = -EBUSY; - goto out; - } + unsigned int trim_info; + int sensor_id, cal_type; trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); sanitize_temp_error(data, trim_info); @@ -549,359 +523,143 @@ static int exynos5433_tmu_initialize(struct platform_device *pdev) >> EXYNOS5433_TRIMINFO_CALIB_SEL_SHIFT; switch (cal_type) { - case EXYNOS5433_TRIMINFO_ONE_POINT_TRIMMING: - pdata->cal_type = TYPE_ONE_POINT_TRIMMING; - break; case EXYNOS5433_TRIMINFO_TWO_POINT_TRIMMING: - pdata->cal_type = TYPE_TWO_POINT_TRIMMING; + data->cal_type = TYPE_TWO_POINT_TRIMMING; break; + case EXYNOS5433_TRIMINFO_ONE_POINT_TRIMMING: default: - pdata->cal_type = TYPE_ONE_POINT_TRIMMING; + data->cal_type = TYPE_ONE_POINT_TRIMMING; break; } dev_info(&pdev->dev, "Calibration type is %d-point calibration\n", cal_type ? 2 : 1); +} - /* Write temperature code for rising and falling threshold */ - for (i = 0; i < of_thermal_get_ntrips(tz); i++) { - int rising_reg_offset, falling_reg_offset; - int j = 0; - - switch (i) { - case 0: - case 1: - case 2: - case 3: - rising_reg_offset = EXYNOS5433_THD_TEMP_RISE3_0; - falling_reg_offset = EXYNOS5433_THD_TEMP_FALL3_0; - j = i; - break; - case 4: - case 5: - case 6: - case 7: - rising_reg_offset = EXYNOS5433_THD_TEMP_RISE7_4; - falling_reg_offset = EXYNOS5433_THD_TEMP_FALL7_4; - j = i - 4; - break; - default: - continue; - } - - /* Write temperature code for rising threshold */ - tz->ops->get_trip_temp(tz, i, &temp); - temp /= MCELSIUS; - threshold_code = temp_to_code(data, temp); - - rising_threshold = readl(data->base + rising_reg_offset); - rising_threshold |= (threshold_code << j * 8); - writel(rising_threshold, data->base + rising_reg_offset); - - /* Write temperature code for falling threshold */ - tz->ops->get_trip_hyst(tz, i, &temp_hist); - temp_hist = temp - (temp_hist / MCELSIUS); - threshold_code = temp_to_code(data, temp_hist); +static void exynos7_tmu_set_low_temp(struct exynos_tmu_data *data, u8 temp) +{ + exynos_tmu_update_temp(data, EXYNOS7_THD_TEMP_FALL7_6 + 12, 0, temp); + exynos_tmu_update_bit(data, EXYNOS7_TMU_REG_INTEN, + EXYNOS_TMU_INTEN_FALL0_SHIFT + 0, true); +} - falling_threshold = readl(data->base + falling_reg_offset); - falling_threshold &= ~(0xff << j * 8); - falling_threshold |= (threshold_code << j * 8); - writel(falling_threshold, data->base + falling_reg_offset); - } +static void exynos7_tmu_set_high_temp(struct exynos_tmu_data *data, u8 temp) +{ + exynos_tmu_update_temp(data, EXYNOS7_THD_TEMP_RISE7_6 + 12, 16, temp); + exynos_tmu_update_bit(data, EXYNOS7_TMU_REG_INTEN, + EXYNOS7_TMU_INTEN_RISE0_SHIFT + 1, true); +} - data->tmu_clear_irqs(data); -out: - return ret; +static void exynos7_tmu_disable_low(struct exynos_tmu_data *data) +{ + exynos_tmu_update_bit(data, EXYNOS7_TMU_REG_INTEN, + EXYNOS_TMU_INTEN_FALL0_SHIFT + 0, false); } -static int exynos5440_tmu_initialize(struct platform_device *pdev) +static void exynos7_tmu_disable_high(struct exynos_tmu_data *data) { - struct exynos_tmu_data *data = platform_get_drvdata(pdev); - unsigned int trim_info = 0, con, rising_threshold; - int threshold_code; - int crit_temp = 0; + exynos_tmu_update_bit(data, EXYNOS7_TMU_REG_INTEN, + EXYNOS7_TMU_INTEN_RISE0_SHIFT + 1, false); +} +static void exynos7_tmu_set_crit_temp(struct exynos_tmu_data *data, u8 temp) +{ /* - * For exynos5440 soc triminfo value is swapped between TMU0 and - * TMU2, so the below logic is needed. + * Like Exynos 4210, Exynos 7 does not seem to support critical temperature + * handling in hardware. Again, we still set a separate interrupt for it. */ - switch (data->id) { - case 0: - trim_info = readl(data->base + EXYNOS5440_EFUSE_SWAP_OFFSET + - EXYNOS5440_TMU_S0_7_TRIM); - break; - case 1: - trim_info = readl(data->base + EXYNOS5440_TMU_S0_7_TRIM); - break; - case 2: - trim_info = readl(data->base - EXYNOS5440_EFUSE_SWAP_OFFSET + - EXYNOS5440_TMU_S0_7_TRIM); - } - sanitize_temp_error(data, trim_info); - - /* Write temperature code for rising and falling threshold */ - rising_threshold = readl(data->base + EXYNOS5440_TMU_S0_7_TH0); - rising_threshold = get_th_reg(data, rising_threshold, false); - writel(rising_threshold, data->base + EXYNOS5440_TMU_S0_7_TH0); - writel(0, data->base + EXYNOS5440_TMU_S0_7_TH1); - - data->tmu_clear_irqs(data); - - /* if last threshold limit is also present */ - if (!data->tzd->ops->get_crit_temp(data->tzd, &crit_temp)) { - threshold_code = temp_to_code(data, crit_temp / MCELSIUS); - /* 5th level to be assigned in th2 reg */ - rising_threshold = - threshold_code << EXYNOS5440_TMU_TH_RISE4_SHIFT; - writel(rising_threshold, data->base + EXYNOS5440_TMU_S0_7_TH2); - con = readl(data->base + EXYNOS5440_TMU_S0_7_CTRL); - con |= (1 << EXYNOS_TMU_THERM_TRIP_EN_SHIFT); - writel(con, data->base + EXYNOS5440_TMU_S0_7_CTRL); - } - /* Clear the PMIN in the common TMU register */ - if (!data->id) - writel(0, data->base_second + EXYNOS5440_TMU_PMIN); - - return 0; + exynos_tmu_update_temp(data, EXYNOS7_THD_TEMP_RISE7_6 + 0, 16, temp); + exynos_tmu_update_bit(data, EXYNOS7_TMU_REG_INTEN, + EXYNOS7_TMU_INTEN_RISE0_SHIFT + 7, true); } -static int exynos7_tmu_initialize(struct platform_device *pdev) +static void exynos7_tmu_initialize(struct platform_device *pdev) { struct exynos_tmu_data *data = platform_get_drvdata(pdev); - struct thermal_zone_device *tz = data->tzd; - struct exynos_tmu_platform_data *pdata = data->pdata; - unsigned int status, trim_info; - unsigned int rising_threshold = 0, falling_threshold = 0; - int ret = 0, threshold_code, i; - int temp, temp_hist; - unsigned int reg_off, bit_off; - - status = readb(data->base + EXYNOS_TMU_REG_STATUS); - if (!status) { - ret = -EBUSY; - goto out; - } + unsigned int trim_info; trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); - - data->temp_error1 = trim_info & EXYNOS7_TMU_TEMP_MASK; - if (!data->temp_error1 || - (pdata->min_efuse_value > data->temp_error1) || - (data->temp_error1 > pdata->max_efuse_value)) - data->temp_error1 = pdata->efuse_value & EXYNOS_TMU_TEMP_MASK; - - /* Write temperature code for rising and falling threshold */ - for (i = (of_thermal_get_ntrips(tz) - 1); i >= 0; i--) { - /* - * On exynos7 there are 4 rising and 4 falling threshold - * registers (0x50-0x5c and 0x60-0x6c respectively). Each - * register holds the value of two threshold levels (at bit - * offsets 0 and 16). Based on the fact that there are atmost - * eight possible trigger levels, calculate the register and - * bit offsets where the threshold levels are to be written. - * - * e.g. EXYNOS7_THD_TEMP_RISE7_6 (0x50) - * [24:16] - Threshold level 7 - * [8:0] - Threshold level 6 - * e.g. EXYNOS7_THD_TEMP_RISE5_4 (0x54) - * [24:16] - Threshold level 5 - * [8:0] - Threshold level 4 - * - * and similarly for falling thresholds. - * - * Based on the above, calculate the register and bit offsets - * for rising/falling threshold levels and populate them. - */ - reg_off = ((7 - i) / 2) * 4; - bit_off = ((8 - i) % 2); - - tz->ops->get_trip_temp(tz, i, &temp); - temp /= MCELSIUS; - - tz->ops->get_trip_hyst(tz, i, &temp_hist); - temp_hist = temp - (temp_hist / MCELSIUS); - - /* Set 9-bit temperature code for rising threshold levels */ - threshold_code = temp_to_code(data, temp); - rising_threshold = readl(data->base + - EXYNOS7_THD_TEMP_RISE7_6 + reg_off); - rising_threshold &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off)); - rising_threshold |= threshold_code << (16 * bit_off); - writel(rising_threshold, - data->base + EXYNOS7_THD_TEMP_RISE7_6 + reg_off); - - /* Set 9-bit temperature code for falling threshold levels */ - threshold_code = temp_to_code(data, temp_hist); - falling_threshold &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off)); - falling_threshold |= threshold_code << (16 * bit_off); - writel(falling_threshold, - data->base + EXYNOS7_THD_TEMP_FALL7_6 + reg_off); - } - - data->tmu_clear_irqs(data); -out: - return ret; + sanitize_temp_error(data, trim_info); } static void exynos4210_tmu_control(struct platform_device *pdev, bool on) { struct exynos_tmu_data *data = platform_get_drvdata(pdev); - struct thermal_zone_device *tz = data->tzd; - unsigned int con, interrupt_en; + unsigned int con; con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL)); - if (on) { - con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); - interrupt_en = - (of_thermal_is_trip_valid(tz, 3) - << EXYNOS_TMU_INTEN_RISE3_SHIFT) | - (of_thermal_is_trip_valid(tz, 2) - << EXYNOS_TMU_INTEN_RISE2_SHIFT) | - (of_thermal_is_trip_valid(tz, 1) - << EXYNOS_TMU_INTEN_RISE1_SHIFT) | - (of_thermal_is_trip_valid(tz, 0) - << EXYNOS_TMU_INTEN_RISE0_SHIFT); - - if (data->soc != SOC_ARCH_EXYNOS4210) - interrupt_en |= - interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT; - } else { - con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); - interrupt_en = 0; /* Disable all interrupts */ - } - writel(interrupt_en, data->base + EXYNOS_TMU_REG_INTEN); + if (on) + con |= BIT(EXYNOS_TMU_CORE_EN_SHIFT); + else + con &= ~BIT(EXYNOS_TMU_CORE_EN_SHIFT); + writel(con, data->base + EXYNOS_TMU_REG_CONTROL); } static void exynos5433_tmu_control(struct platform_device *pdev, bool on) { struct exynos_tmu_data *data = platform_get_drvdata(pdev); - struct thermal_zone_device *tz = data->tzd; - unsigned int con, interrupt_en, pd_det_en; + unsigned int con, pd_det_en; con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL)); - if (on) { - con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); - interrupt_en = - (of_thermal_is_trip_valid(tz, 7) - << EXYNOS7_TMU_INTEN_RISE7_SHIFT) | - (of_thermal_is_trip_valid(tz, 6) - << EXYNOS7_TMU_INTEN_RISE6_SHIFT) | - (of_thermal_is_trip_valid(tz, 5) - << EXYNOS7_TMU_INTEN_RISE5_SHIFT) | - (of_thermal_is_trip_valid(tz, 4) - << EXYNOS7_TMU_INTEN_RISE4_SHIFT) | - (of_thermal_is_trip_valid(tz, 3) - << EXYNOS7_TMU_INTEN_RISE3_SHIFT) | - (of_thermal_is_trip_valid(tz, 2) - << EXYNOS7_TMU_INTEN_RISE2_SHIFT) | - (of_thermal_is_trip_valid(tz, 1) - << EXYNOS7_TMU_INTEN_RISE1_SHIFT) | - (of_thermal_is_trip_valid(tz, 0) - << EXYNOS7_TMU_INTEN_RISE0_SHIFT); - - interrupt_en |= - interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT; - } else { - con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); - interrupt_en = 0; /* Disable all interrupts */ - } + if (on) + con |= BIT(EXYNOS_TMU_CORE_EN_SHIFT); + else + con &= ~BIT(EXYNOS_TMU_CORE_EN_SHIFT); pd_det_en = on ? EXYNOS5433_PD_DET_EN : 0; writel(pd_det_en, data->base + EXYNOS5433_TMU_PD_DET_EN); - writel(interrupt_en, data->base + EXYNOS5433_TMU_REG_INTEN); writel(con, data->base + EXYNOS_TMU_REG_CONTROL); } -static void exynos5440_tmu_control(struct platform_device *pdev, bool on) -{ - struct exynos_tmu_data *data = platform_get_drvdata(pdev); - struct thermal_zone_device *tz = data->tzd; - unsigned int con, interrupt_en; - - con = get_con_reg(data, readl(data->base + EXYNOS5440_TMU_S0_7_CTRL)); - - if (on) { - con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); - interrupt_en = - (of_thermal_is_trip_valid(tz, 3) - << EXYNOS5440_TMU_INTEN_RISE3_SHIFT) | - (of_thermal_is_trip_valid(tz, 2) - << EXYNOS5440_TMU_INTEN_RISE2_SHIFT) | - (of_thermal_is_trip_valid(tz, 1) - << EXYNOS5440_TMU_INTEN_RISE1_SHIFT) | - (of_thermal_is_trip_valid(tz, 0) - << EXYNOS5440_TMU_INTEN_RISE0_SHIFT); - interrupt_en |= - interrupt_en << EXYNOS5440_TMU_INTEN_FALL0_SHIFT; - } else { - con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); - interrupt_en = 0; /* Disable all interrupts */ - } - writel(interrupt_en, data->base + EXYNOS5440_TMU_S0_7_IRQEN); - writel(con, data->base + EXYNOS5440_TMU_S0_7_CTRL); -} - static void exynos7_tmu_control(struct platform_device *pdev, bool on) { struct exynos_tmu_data *data = platform_get_drvdata(pdev); - struct thermal_zone_device *tz = data->tzd; - unsigned int con, interrupt_en; + unsigned int con; con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL)); if (on) { - con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); - con |= (1 << EXYNOS7_PD_DET_EN_SHIFT); - interrupt_en = - (of_thermal_is_trip_valid(tz, 7) - << EXYNOS7_TMU_INTEN_RISE7_SHIFT) | - (of_thermal_is_trip_valid(tz, 6) - << EXYNOS7_TMU_INTEN_RISE6_SHIFT) | - (of_thermal_is_trip_valid(tz, 5) - << EXYNOS7_TMU_INTEN_RISE5_SHIFT) | - (of_thermal_is_trip_valid(tz, 4) - << EXYNOS7_TMU_INTEN_RISE4_SHIFT) | - (of_thermal_is_trip_valid(tz, 3) - << EXYNOS7_TMU_INTEN_RISE3_SHIFT) | - (of_thermal_is_trip_valid(tz, 2) - << EXYNOS7_TMU_INTEN_RISE2_SHIFT) | - (of_thermal_is_trip_valid(tz, 1) - << EXYNOS7_TMU_INTEN_RISE1_SHIFT) | - (of_thermal_is_trip_valid(tz, 0) - << EXYNOS7_TMU_INTEN_RISE0_SHIFT); - - interrupt_en |= - interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT; + con |= BIT(EXYNOS_TMU_CORE_EN_SHIFT); + con |= BIT(EXYNOS7_PD_DET_EN_SHIFT); } else { - con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); - con &= ~(1 << EXYNOS7_PD_DET_EN_SHIFT); - interrupt_en = 0; /* Disable all interrupts */ + con &= ~BIT(EXYNOS_TMU_CORE_EN_SHIFT); + con &= ~BIT(EXYNOS7_PD_DET_EN_SHIFT); } - writel(interrupt_en, data->base + EXYNOS7_TMU_REG_INTEN); writel(con, data->base + EXYNOS_TMU_REG_CONTROL); } -static int exynos_get_temp(void *p, int *temp) +static int exynos_get_temp(struct thermal_zone_device *tz, int *temp) { - struct exynos_tmu_data *data = p; + struct exynos_tmu_data *data = thermal_zone_device_priv(tz); + int value, ret = 0; if (!data || !data->tmu_read) return -EINVAL; + else if (!data->enabled) + /* + * Called too early, probably + * from thermal_zone_of_sensor_register(). + */ + return -EAGAIN; mutex_lock(&data->lock); clk_enable(data->clk); - *temp = code_to_temp(data, data->tmu_read(data)) * MCELSIUS; + value = data->tmu_read(data); + if (value < 0) + ret = value; + else + *temp = code_to_temp(data, value) * MCELSIUS; clk_disable(data->clk); mutex_unlock(&data->lock); - return 0; + return ret; } #ifdef CONFIG_THERMAL_EMULATION @@ -911,10 +669,8 @@ static u32 get_emul_con_reg(struct exynos_tmu_data *data, unsigned int val, if (temp) { temp /= MCELSIUS; - if (data->soc != SOC_ARCH_EXYNOS5440) { - val &= ~(EXYNOS_EMUL_TIME_MASK << EXYNOS_EMUL_TIME_SHIFT); - val |= (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT); - } + val &= ~(EXYNOS_EMUL_TIME_MASK << EXYNOS_EMUL_TIME_SHIFT); + val |= (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT); if (data->soc == SOC_ARCH_EXYNOS7) { val &= ~(EXYNOS7_EMUL_DATA_MASK << EXYNOS7_EMUL_DATA_SHIFT); @@ -955,19 +711,9 @@ static void exynos4412_tmu_set_emulation(struct exynos_tmu_data *data, writel(val, data->base + emul_con); } -static void exynos5440_tmu_set_emulation(struct exynos_tmu_data *data, - int temp) -{ - unsigned int val; - - val = readl(data->base + EXYNOS5440_TMU_S0_7_DEBUG); - val = get_emul_con_reg(data, val, temp); - writel(val, data->base + EXYNOS5440_TMU_S0_7_DEBUG); -} - -static int exynos_tmu_set_emulation(void *drv_data, int temp) +static int exynos_tmu_set_emulation(struct thermal_zone_device *tz, int temp) { - struct exynos_tmu_data *data = drv_data; + struct exynos_tmu_data *data = thermal_zone_device_priv(tz); int ret = -EINVAL; if (data->soc == SOC_ARCH_EXYNOS4210) @@ -987,8 +733,7 @@ out: } #else #define exynos4412_tmu_set_emulation NULL -#define exynos5440_tmu_set_emulation NULL -static int exynos_tmu_set_emulation(void *drv_data, int temp) +static int exynos_tmu_set_emulation(struct thermal_zone_device *tz, int temp) { return -EINVAL; } #endif /* CONFIG_THERMAL_EMULATION */ @@ -1005,35 +750,18 @@ static int exynos4412_tmu_read(struct exynos_tmu_data *data) return readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP); } -static int exynos5440_tmu_read(struct exynos_tmu_data *data) -{ - return readb(data->base + EXYNOS5440_TMU_S0_7_TEMP); -} - static int exynos7_tmu_read(struct exynos_tmu_data *data) { return readw(data->base + EXYNOS_TMU_REG_CURRENT_TEMP) & EXYNOS7_TMU_TEMP_MASK; } -static void exynos_tmu_work(struct work_struct *work) +static irqreturn_t exynos_tmu_threaded_irq(int irq, void *id) { - struct exynos_tmu_data *data = container_of(work, - struct exynos_tmu_data, irq_work); - unsigned int val_type; + struct exynos_tmu_data *data = id; - if (!IS_ERR(data->clk_sec)) - clk_enable(data->clk_sec); - /* Find which sensor generated this interrupt */ - if (data->soc == SOC_ARCH_EXYNOS5440) { - val_type = readl(data->base_second + EXYNOS5440_TMU_IRQ_STATUS); - if (!((val_type >> data->id) & 0x1)) - goto out; - } - if (!IS_ERR(data->clk_sec)) - clk_disable(data->clk_sec); + thermal_zone_device_update(data->tzd, THERMAL_EVENT_UNSPECIFIED); - exynos_report_trigger(data); mutex_lock(&data->lock); clk_enable(data->clk); @@ -1042,8 +770,8 @@ static void exynos_tmu_work(struct work_struct *work) clk_disable(data->clk); mutex_unlock(&data->lock); -out: - enable_irq(data->irq); + + return IRQ_HANDLED; } static void exynos4210_tmu_clear_irqs(struct exynos_tmu_data *data) @@ -1077,115 +805,47 @@ static void exynos4210_tmu_clear_irqs(struct exynos_tmu_data *data) writel(val_irq, data->base + tmu_intclear); } -static void exynos5440_tmu_clear_irqs(struct exynos_tmu_data *data) -{ - unsigned int val_irq; - - val_irq = readl(data->base + EXYNOS5440_TMU_S0_7_IRQ); - /* clear the interrupts */ - writel(val_irq, data->base + EXYNOS5440_TMU_S0_7_IRQ); -} - -static irqreturn_t exynos_tmu_irq(int irq, void *id) -{ - struct exynos_tmu_data *data = id; - - disable_irq_nosync(irq); - schedule_work(&data->irq_work); - - return IRQ_HANDLED; -} - static const struct of_device_id exynos_tmu_match[] = { - { .compatible = "samsung,exynos3250-tmu", }, - { .compatible = "samsung,exynos4210-tmu", }, - { .compatible = "samsung,exynos4412-tmu", }, - { .compatible = "samsung,exynos5250-tmu", }, - { .compatible = "samsung,exynos5260-tmu", }, - { .compatible = "samsung,exynos5420-tmu", }, - { .compatible = "samsung,exynos5420-tmu-ext-triminfo", }, - { .compatible = "samsung,exynos5433-tmu", }, - { .compatible = "samsung,exynos5440-tmu", }, - { .compatible = "samsung,exynos7-tmu", }, - { /* sentinel */ }, + { + .compatible = "samsung,exynos3250-tmu", + .data = (const void *)SOC_ARCH_EXYNOS3250, + }, { + .compatible = "samsung,exynos4210-tmu", + .data = (const void *)SOC_ARCH_EXYNOS4210, + }, { + .compatible = "samsung,exynos4412-tmu", + .data = (const void *)SOC_ARCH_EXYNOS4412, + }, { + .compatible = "samsung,exynos5250-tmu", + .data = (const void *)SOC_ARCH_EXYNOS5250, + }, { + .compatible = "samsung,exynos5260-tmu", + .data = (const void *)SOC_ARCH_EXYNOS5260, + }, { + .compatible = "samsung,exynos5420-tmu", + .data = (const void *)SOC_ARCH_EXYNOS5420, + }, { + .compatible = "samsung,exynos5420-tmu-ext-triminfo", + .data = (const void *)SOC_ARCH_EXYNOS5420_TRIMINFO, + }, { + .compatible = "samsung,exynos5433-tmu", + .data = (const void *)SOC_ARCH_EXYNOS5433, + }, { + .compatible = "samsung,exynos7-tmu", + .data = (const void *)SOC_ARCH_EXYNOS7, + }, + { }, }; MODULE_DEVICE_TABLE(of, exynos_tmu_match); -static int exynos_of_get_soc_type(struct device_node *np) -{ - if (of_device_is_compatible(np, "samsung,exynos3250-tmu")) - return SOC_ARCH_EXYNOS3250; - else if (of_device_is_compatible(np, "samsung,exynos4210-tmu")) - return SOC_ARCH_EXYNOS4210; - else if (of_device_is_compatible(np, "samsung,exynos4412-tmu")) - return SOC_ARCH_EXYNOS4412; - else if (of_device_is_compatible(np, "samsung,exynos5250-tmu")) - return SOC_ARCH_EXYNOS5250; - else if (of_device_is_compatible(np, "samsung,exynos5260-tmu")) - return SOC_ARCH_EXYNOS5260; - else if (of_device_is_compatible(np, "samsung,exynos5420-tmu")) - return SOC_ARCH_EXYNOS5420; - else if (of_device_is_compatible(np, - "samsung,exynos5420-tmu-ext-triminfo")) - return SOC_ARCH_EXYNOS5420_TRIMINFO; - else if (of_device_is_compatible(np, "samsung,exynos5433-tmu")) - return SOC_ARCH_EXYNOS5433; - else if (of_device_is_compatible(np, "samsung,exynos5440-tmu")) - return SOC_ARCH_EXYNOS5440; - else if (of_device_is_compatible(np, "samsung,exynos7-tmu")) - return SOC_ARCH_EXYNOS7; - - return -EINVAL; -} - -static int exynos_of_sensor_conf(struct device_node *np, - struct exynos_tmu_platform_data *pdata) -{ - u32 value; - int ret; - - of_node_get(np); - - ret = of_property_read_u32(np, "samsung,tmu_gain", &value); - pdata->gain = (u8)value; - of_property_read_u32(np, "samsung,tmu_reference_voltage", &value); - pdata->reference_voltage = (u8)value; - of_property_read_u32(np, "samsung,tmu_noise_cancel_mode", &value); - pdata->noise_cancel_mode = (u8)value; - - of_property_read_u32(np, "samsung,tmu_efuse_value", - &pdata->efuse_value); - of_property_read_u32(np, "samsung,tmu_min_efuse_value", - &pdata->min_efuse_value); - of_property_read_u32(np, "samsung,tmu_max_efuse_value", - &pdata->max_efuse_value); - - of_property_read_u32(np, "samsung,tmu_first_point_trim", &value); - pdata->first_point_trim = (u8)value; - of_property_read_u32(np, "samsung,tmu_second_point_trim", &value); - pdata->second_point_trim = (u8)value; - of_property_read_u32(np, "samsung,tmu_default_temp_offset", &value); - pdata->default_temp_offset = (u8)value; - - of_property_read_u32(np, "samsung,tmu_cal_type", &pdata->cal_type); - - of_node_put(np); - return 0; -} - static int exynos_map_dt_data(struct platform_device *pdev) { struct exynos_tmu_data *data = platform_get_drvdata(pdev); - struct exynos_tmu_platform_data *pdata; struct resource res; if (!data || !pdev->dev.of_node) return -ENODEV; - data->id = of_alias_get_id(pdev->dev.of_node, "tmuctrl"); - if (data->id < 0) - data->id = 0; - data->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); if (data->irq <= 0) { dev_err(&pdev->dev, "failed to get IRQ\n"); @@ -1203,23 +863,24 @@ static int exynos_map_dt_data(struct platform_device *pdev) return -EADDRNOTAVAIL; } - pdata = devm_kzalloc(&pdev->dev, - sizeof(struct exynos_tmu_platform_data), - GFP_KERNEL); - if (!pdata) - return -ENOMEM; - - exynos_of_sensor_conf(pdev->dev.of_node, pdata); - data->pdata = pdata; - data->soc = exynos_of_get_soc_type(pdev->dev.of_node); + data->soc = (uintptr_t)of_device_get_match_data(&pdev->dev); switch (data->soc) { case SOC_ARCH_EXYNOS4210: + data->tmu_set_low_temp = exynos4210_tmu_set_low_temp; + data->tmu_set_high_temp = exynos4210_tmu_set_high_temp; + data->tmu_disable_low = exynos4210_tmu_disable_low; + data->tmu_disable_high = exynos4210_tmu_disable_high; + data->tmu_set_crit_temp = exynos4210_tmu_set_crit_temp; data->tmu_initialize = exynos4210_tmu_initialize; data->tmu_control = exynos4210_tmu_control; data->tmu_read = exynos4210_tmu_read; data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; - data->ntrip = 4; + data->gain = 15; + data->reference_voltage = 7; + data->efuse_value = 55; + data->min_efuse_value = 40; + data->max_efuse_value = 100; break; case SOC_ARCH_EXYNOS3250: case SOC_ARCH_EXYNOS4412: @@ -1227,48 +888,75 @@ static int exynos_map_dt_data(struct platform_device *pdev) case SOC_ARCH_EXYNOS5260: case SOC_ARCH_EXYNOS5420: case SOC_ARCH_EXYNOS5420_TRIMINFO: + data->tmu_set_low_temp = exynos4412_tmu_set_low_temp; + data->tmu_set_high_temp = exynos4412_tmu_set_high_temp; + data->tmu_disable_low = exynos4412_tmu_disable_low; + data->tmu_disable_high = exynos4210_tmu_disable_high; + data->tmu_set_crit_temp = exynos4412_tmu_set_crit_temp; data->tmu_initialize = exynos4412_tmu_initialize; data->tmu_control = exynos4210_tmu_control; data->tmu_read = exynos4412_tmu_read; data->tmu_set_emulation = exynos4412_tmu_set_emulation; data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; - data->ntrip = 4; + data->gain = 8; + data->reference_voltage = 16; + data->efuse_value = 55; + if (data->soc != SOC_ARCH_EXYNOS5420 && + data->soc != SOC_ARCH_EXYNOS5420_TRIMINFO) + data->min_efuse_value = 40; + else + data->min_efuse_value = 0; + data->max_efuse_value = 100; break; case SOC_ARCH_EXYNOS5433: + data->tmu_set_low_temp = exynos5433_tmu_set_low_temp; + data->tmu_set_high_temp = exynos5433_tmu_set_high_temp; + data->tmu_disable_low = exynos5433_tmu_disable_low; + data->tmu_disable_high = exynos5433_tmu_disable_high; + data->tmu_set_crit_temp = exynos5433_tmu_set_crit_temp; data->tmu_initialize = exynos5433_tmu_initialize; data->tmu_control = exynos5433_tmu_control; data->tmu_read = exynos4412_tmu_read; data->tmu_set_emulation = exynos4412_tmu_set_emulation; data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; - data->ntrip = 8; - break; - case SOC_ARCH_EXYNOS5440: - data->tmu_initialize = exynos5440_tmu_initialize; - data->tmu_control = exynos5440_tmu_control; - data->tmu_read = exynos5440_tmu_read; - data->tmu_set_emulation = exynos5440_tmu_set_emulation; - data->tmu_clear_irqs = exynos5440_tmu_clear_irqs; - data->ntrip = 4; + data->gain = 8; + if (res.start == EXYNOS5433_G3D_BASE) + data->reference_voltage = 23; + else + data->reference_voltage = 16; + data->efuse_value = 75; + data->min_efuse_value = 40; + data->max_efuse_value = 150; break; case SOC_ARCH_EXYNOS7: + data->tmu_set_low_temp = exynos7_tmu_set_low_temp; + data->tmu_set_high_temp = exynos7_tmu_set_high_temp; + data->tmu_disable_low = exynos7_tmu_disable_low; + data->tmu_disable_high = exynos7_tmu_disable_high; + data->tmu_set_crit_temp = exynos7_tmu_set_crit_temp; data->tmu_initialize = exynos7_tmu_initialize; data->tmu_control = exynos7_tmu_control; data->tmu_read = exynos7_tmu_read; data->tmu_set_emulation = exynos4412_tmu_set_emulation; data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; - data->ntrip = 8; + data->gain = 9; + data->reference_voltage = 17; + data->efuse_value = 75; + data->min_efuse_value = 15; + data->max_efuse_value = 100; break; default: dev_err(&pdev->dev, "Platform not supported\n"); return -EINVAL; } + data->cal_type = TYPE_ONE_POINT_TRIMMING; + /* * Check if the TMU shares some registers and then try to map the * memory of common registers. */ - if (data->soc != SOC_ARCH_EXYNOS5420_TRIMINFO && - data->soc != SOC_ARCH_EXYNOS5440) + if (data->soc != SOC_ARCH_EXYNOS5420_TRIMINFO) return 0; if (of_address_to_resource(pdev->dev.of_node, 1, &res)) { @@ -1286,18 +974,41 @@ static int exynos_map_dt_data(struct platform_device *pdev) return 0; } -static struct thermal_zone_of_device_ops exynos_sensor_ops = { +static int exynos_set_trips(struct thermal_zone_device *tz, int low, int high) +{ + struct exynos_tmu_data *data = thermal_zone_device_priv(tz); + + mutex_lock(&data->lock); + clk_enable(data->clk); + + if (low > INT_MIN) + data->tmu_set_low_temp(data, low / MCELSIUS); + else + data->tmu_disable_low(data); + if (high < INT_MAX) + data->tmu_set_high_temp(data, high / MCELSIUS); + else + data->tmu_disable_high(data); + + clk_disable(data->clk); + mutex_unlock(&data->lock); + + return 0; +} + +static const struct thermal_zone_device_ops exynos_sensor_ops = { .get_temp = exynos_get_temp, .set_emul_temp = exynos_tmu_set_emulation, + .set_trips = exynos_set_trips, }; static int exynos_tmu_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct exynos_tmu_data *data; int ret; - data = devm_kzalloc(&pdev->dev, sizeof(struct exynos_tmu_data), - GFP_KERNEL); + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; @@ -1309,64 +1020,56 @@ static int exynos_tmu_probe(struct platform_device *pdev) * TODO: Add regulator as an SOC feature, so that regulator enable * is a compulsory call. */ - data->regulator = devm_regulator_get_optional(&pdev->dev, "vtmu"); - if (!IS_ERR(data->regulator)) { - ret = regulator_enable(data->regulator); - if (ret) { - dev_err(&pdev->dev, "failed to enable vtmu\n"); - return ret; - } - } else { - if (PTR_ERR(data->regulator) == -EPROBE_DEFER) - return -EPROBE_DEFER; - dev_info(&pdev->dev, "Regulator node (vtmu) not found\n"); + ret = devm_regulator_get_enable_optional(dev, "vtmu"); + switch (ret) { + case 0: + case -ENODEV: + break; + case -EPROBE_DEFER: + return -EPROBE_DEFER; + default: + dev_err(dev, "Failed to get enabled regulator: %d\n", ret); + return ret; } ret = exynos_map_dt_data(pdev); if (ret) - goto err_sensor; + return ret; - INIT_WORK(&data->irq_work, exynos_tmu_work); + data->clk = devm_clk_get(dev, "tmu_apbif"); + if (IS_ERR(data->clk)) + return dev_err_probe(dev, PTR_ERR(data->clk), "Failed to get clock\n"); - data->clk = devm_clk_get(&pdev->dev, "tmu_apbif"); - if (IS_ERR(data->clk)) { - dev_err(&pdev->dev, "Failed to get clock\n"); - ret = PTR_ERR(data->clk); - goto err_sensor; - } - - data->clk_sec = devm_clk_get(&pdev->dev, "tmu_triminfo_apbif"); + data->clk_sec = devm_clk_get(dev, "tmu_triminfo_apbif"); if (IS_ERR(data->clk_sec)) { - if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) { - dev_err(&pdev->dev, "Failed to get triminfo clock\n"); - ret = PTR_ERR(data->clk_sec); - goto err_sensor; - } + if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) + return dev_err_probe(dev, PTR_ERR(data->clk_sec), + "Failed to get triminfo clock\n"); } else { ret = clk_prepare(data->clk_sec); if (ret) { - dev_err(&pdev->dev, "Failed to get clock\n"); - goto err_sensor; + dev_err(dev, "Failed to get clock\n"); + return ret; } } ret = clk_prepare(data->clk); if (ret) { - dev_err(&pdev->dev, "Failed to get clock\n"); + dev_err(dev, "Failed to get clock\n"); goto err_clk_sec; } switch (data->soc) { case SOC_ARCH_EXYNOS5433: case SOC_ARCH_EXYNOS7: - data->sclk = devm_clk_get(&pdev->dev, "tmu_sclk"); + data->sclk = devm_clk_get(dev, "tmu_sclk"); if (IS_ERR(data->sclk)) { - dev_err(&pdev->dev, "Failed to get sclk\n"); + ret = dev_err_probe(dev, PTR_ERR(data->sclk), "Failed to get sclk\n"); goto err_clk; } else { ret = clk_prepare_enable(data->sclk); if (ret) { - dev_err(&pdev->dev, "Failed to enable sclk\n"); + dev_err(dev, "Failed to enable sclk\n"); goto err_clk; } } @@ -1375,36 +1078,38 @@ static int exynos_tmu_probe(struct platform_device *pdev) break; } - /* - * data->tzd must be registered before calling exynos_tmu_initialize(), - * requesting irq and calling exynos_tmu_control(). - */ - data->tzd = thermal_zone_of_sensor_register(&pdev->dev, 0, data, - &exynos_sensor_ops); + ret = exynos_tmu_initialize(pdev); + if (ret) { + dev_err(dev, "Failed to initialize TMU\n"); + goto err_sclk; + } + + data->tzd = devm_thermal_of_zone_register(dev, 0, data, + &exynos_sensor_ops); if (IS_ERR(data->tzd)) { - ret = PTR_ERR(data->tzd); - dev_err(&pdev->dev, "Failed to register sensor: %d\n", ret); + ret = dev_err_probe(dev, PTR_ERR(data->tzd), "Failed to register sensor\n"); goto err_sclk; } - ret = exynos_tmu_initialize(pdev); + ret = exynos_thermal_zone_configure(pdev); if (ret) { - dev_err(&pdev->dev, "Failed to initialize TMU\n"); - goto err_thermal; + dev_err(dev, "Failed to configure the thermal zone\n"); + goto err_sclk; } - ret = devm_request_irq(&pdev->dev, data->irq, exynos_tmu_irq, - IRQF_TRIGGER_RISING | IRQF_SHARED, dev_name(&pdev->dev), data); + ret = devm_request_threaded_irq(dev, data->irq, NULL, + exynos_tmu_threaded_irq, + IRQF_TRIGGER_RISING + | IRQF_SHARED | IRQF_ONESHOT, + dev_name(dev), data); if (ret) { - dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq); - goto err_thermal; + dev_err(dev, "Failed to request irq: %d\n", data->irq); + goto err_sclk; } exynos_tmu_control(pdev, true); return 0; -err_thermal: - thermal_zone_of_sensor_unregister(&pdev->dev, data->tzd); err_sclk: clk_disable_unprepare(data->sclk); err_clk: @@ -1412,30 +1117,19 @@ err_clk: err_clk_sec: if (!IS_ERR(data->clk_sec)) clk_unprepare(data->clk_sec); -err_sensor: - if (!IS_ERR(data->regulator)) - regulator_disable(data->regulator); - return ret; } -static int exynos_tmu_remove(struct platform_device *pdev) +static void exynos_tmu_remove(struct platform_device *pdev) { struct exynos_tmu_data *data = platform_get_drvdata(pdev); - struct thermal_zone_device *tzd = data->tzd; - thermal_zone_of_sensor_unregister(&pdev->dev, tzd); exynos_tmu_control(pdev, false); clk_disable_unprepare(data->sclk); clk_unprepare(data->clk); if (!IS_ERR(data->clk_sec)) clk_unprepare(data->clk_sec); - - if (!IS_ERR(data->regulator)) - regulator_disable(data->regulator); - - return 0; } #ifdef CONFIG_PM_SLEEP @@ -1470,12 +1164,12 @@ static struct platform_driver exynos_tmu_driver = { .of_match_table = exynos_tmu_match, }, .probe = exynos_tmu_probe, - .remove = exynos_tmu_remove, + .remove = exynos_tmu_remove, }; module_platform_driver(exynos_tmu_driver); -MODULE_DESCRIPTION("EXYNOS TMU Driver"); +MODULE_DESCRIPTION("Exynos TMU Driver"); MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:exynos-tmu"); |
