thermal/drivers/exynos: Use set_trips ops
authorMateusz Majewski <m.majewski2@samsung.com>
Fri, 1 Dec 2023 09:56:25 +0000 (10:56 +0100)
committerDaniel Lezcano <daniel.lezcano@linaro.org>
Tue, 2 Jan 2024 08:33:19 +0000 (09:33 +0100)
Currently, each trip point defined in the device tree corresponds to a
single hardware interrupt. This commit instead switches to using two
hardware interrupts, whose values are set dynamically using the
set_trips callback. Additionally, the critical temperature threshold is
handled specifically.

Setting interrupts in this way also fixes a long-standing lockdep
warning, which was caused by calling thermal_zone_get_trips with our
lock being held. Do note that this requires TMU initialization to be
split into two parts, as done by the parent commit: parts of the
initialization call into the thermal_zone_device structure and so must
be done after its registration, but the initialization is also
responsible for setting up calibration, which must be done before
thermal_zone_device registration, which will call set_trips for the
first time; if the calibration is not done in time, the interrupt values
will be silently wrong!

Reviewed-by: Lukasz Luba <lukasz.luba@arm.com>
Signed-off-by: Mateusz Majewski <m.majewski2@samsung.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Link: https://lore.kernel.org/r/20231201095625.301884-10-m.majewski2@samsung.com
drivers/thermal/samsung/exynos_tmu.c

index ca1b1cec0300cc2646ba23f4f40215e6021f74ca..6482513bfe66981a14cfb5f90cd0a4865fccc90c 100644 (file)
@@ -158,10 +158,12 @@ enum soc_type {
  *     in the positive-TC generator block
  *     0 < reference_voltage <= 31
  * @tzd: pointer to thermal_zone_device structure
- * @ntrip: number of supported trip points.
  * @enabled: current status of TMU device
- * @tmu_set_trip_temp: SoC specific method to set trip (rising threshold)
- * @tmu_set_trip_hyst: SoC specific to set hysteresis (falling threshold)
+ * @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
@@ -183,13 +185,13 @@ struct exynos_tmu_data {
        u8 gain;
        u8 reference_voltage;
        struct thermal_zone_device *tzd;
-       unsigned int ntrip;
        bool enabled;
 
-       void (*tmu_set_trip_temp)(struct exynos_tmu_data *data, int trip,
-                                u8 temp);
-       void (*tmu_set_trip_hyst)(struct exynos_tmu_data *data, int trip,
-                                u8 temp, u8 hyst);
+       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);
@@ -279,49 +281,28 @@ static int exynos_thermal_zone_configure(struct platform_device *pdev)
 {
        struct exynos_tmu_data *data = platform_get_drvdata(pdev);
        struct thermal_zone_device *tzd = data->tzd;
-       int i, num_trips = thermal_zone_get_num_trips(tzd);
-       int ret = 0, temp;
+       int ret, temp;
 
        ret = thermal_zone_get_crit_temp(tzd, &temp);
+       if (ret) {
+               /* FIXME: Remove this special case */
+               if (data->soc == SOC_ARCH_EXYNOS5433)
+                       return 0;
 
-       if (ret && data->soc != SOC_ARCH_EXYNOS5433) { /* FIXME */
                dev_err(&pdev->dev,
                        "No CRITICAL trip point defined in device tree!\n");
-               goto out;
+               return ret;
        }
 
        mutex_lock(&data->lock);
-
-       if (num_trips > 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",
-                        num_trips - data->ntrip);
-       }
-
        clk_enable(data->clk);
 
-       num_trips = min_t(int, num_trips, data->ntrip);
-
-       /* Write temperature code for rising and falling threshold */
-       for (i = 0; i < num_trips; i++) {
-               struct thermal_trip trip;
+       data->tmu_set_crit_temp(data, temp / MCELSIUS);
 
-               ret = thermal_zone_get_trip(tzd, i, &trip);
-               if (ret)
-                       goto err;
-
-               data->tmu_set_trip_temp(data, i, trip.temperature / MCELSIUS);
-               data->tmu_set_trip_hyst(data, i, trip.temperature / MCELSIUS,
-                                       trip.hysteresis / MCELSIUS);
-       }
-
-err:
        clk_disable(data->clk);
        mutex_unlock(&data->lock);
-out:
-       return ret;
+
+       return 0;
 }
 
 static u32 get_con_reg(struct exynos_tmu_data *data, u32 con)
@@ -354,17 +335,74 @@ static void exynos_tmu_control(struct platform_device *pdev, bool on)
        mutex_unlock(&data->lock);
 }
 
-static void exynos4210_tmu_set_trip_temp(struct exynos_tmu_data *data,
-                                        int trip_id, u8 temp)
+static void exynos_tmu_update_bit(struct exynos_tmu_data *data, int reg_off,
+                                 int bit_off, bool enable)
+{
+       u32 interrupt_en;
+
+       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);
+}
+
+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;
+
+       tmu_temp_mask =
+               (data->soc == SOC_ARCH_EXYNOS7) ? EXYNOS7_TMU_TEMP_MASK
+                                               : EXYNOS_TMU_TEMP_MASK;
+
+       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);
+}
+
+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.
+        */
+}
+
+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 + trip_id * 4);
+       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. */
 }
 
-/* failing thresholds are not supported on Exynos4210 */
-static void exynos4210_tmu_set_trip_hyst(struct exynos_tmu_data *data,
-                                        int trip, u8 temp, u8 hyst)
+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 void exynos4210_tmu_initialize(struct platform_device *pdev)
@@ -376,33 +414,31 @@ static void exynos4210_tmu_initialize(struct platform_device *pdev)
        writeb(0, data->base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP);
 }
 
-static void exynos4412_tmu_set_trip_temp(struct exynos_tmu_data *data,
-                                        int trip, u8 temp)
+static void exynos4412_tmu_set_low_temp(struct exynos_tmu_data *data, u8 temp)
 {
-       u32 th, con;
-
-       th = readl(data->base + EXYNOS_THD_TEMP_RISE);
-       th &= ~(0xff << 8 * trip);
-       th |= temp_to_code(data, temp) << 8 * trip;
-       writel(th, data->base + EXYNOS_THD_TEMP_RISE);
+       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);
+}
 
-       if (trip == 3) {
-               con = readl(data->base + EXYNOS_TMU_REG_CONTROL);
-               con |= BIT(EXYNOS_TMU_THERM_TRIP_EN_SHIFT);
-               writel(con, data->base + EXYNOS_TMU_REG_CONTROL);
-       }
+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_set_trip_hyst(struct exynos_tmu_data *data,
-                                        int trip, u8 temp, u8 hyst)
+static void exynos4412_tmu_disable_low(struct exynos_tmu_data *data)
 {
-       u32 th;
+       exynos_tmu_update_bit(data, EXYNOS_TMU_REG_INTEN,
+                             EXYNOS_TMU_INTEN_FALL0_SHIFT, false);
+}
 
-       th = readl(data->base + EXYNOS_THD_TEMP_FALL);
-       th &= ~(0xff << 8 * trip);
-       if (hyst)
-               th |= temp_to_code(data, temp - hyst) << 8 * trip;
-       writel(th, data->base + EXYNOS_THD_TEMP_FALL);
+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)
@@ -432,44 +468,39 @@ static void exynos4412_tmu_initialize(struct platform_device *pdev)
        sanitize_temp_error(data, trim_info);
 }
 
-static void exynos5433_tmu_set_trip_temp(struct exynos_tmu_data *data,
-                                        int trip, u8 temp)
+static void exynos5433_tmu_set_low_temp(struct exynos_tmu_data *data, u8 temp)
 {
-       unsigned int reg_off, j;
-       u32 th;
-
-       if (trip > 3) {
-               reg_off = EXYNOS5433_THD_TEMP_RISE7_4;
-               j = trip - 4;
-       } else {
-               reg_off = EXYNOS5433_THD_TEMP_RISE3_0;
-               j = trip;
-       }
+       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);
+}
 
-       th = readl(data->base + reg_off);
-       th &= ~(0xff << j * 8);
-       th |= (temp_to_code(data, temp) << j * 8);
-       writel(th, data->base + reg_off);
+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);
 }
 
-static void exynos5433_tmu_set_trip_hyst(struct exynos_tmu_data *data,
-                                        int trip, u8 temp, u8 hyst)
+static void exynos5433_tmu_disable_low(struct exynos_tmu_data *data)
 {
-       unsigned int reg_off, j;
-       u32 th;
+       exynos_tmu_update_bit(data, EXYNOS5433_TMU_REG_INTEN,
+                             EXYNOS_TMU_INTEN_FALL0_SHIFT, false);
+}
 
-       if (trip > 3) {
-               reg_off = EXYNOS5433_THD_TEMP_FALL7_4;
-               j = trip - 4;
-       } else {
-               reg_off = EXYNOS5433_THD_TEMP_FALL3_0;
-               j = trip;
-       }
+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);
+}
 
-       th = readl(data->base + reg_off);
-       th &= ~(0xff << j * 8);
-       th |= (temp_to_code(data, temp - hyst) << j * 8);
-       writel(th, data->base + reg_off);
+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 void exynos5433_tmu_initialize(struct platform_device *pdev)
@@ -505,34 +536,41 @@ static void exynos5433_tmu_initialize(struct platform_device *pdev)
                        cal_type ?  2 : 1);
 }
 
-static void exynos7_tmu_set_trip_temp(struct exynos_tmu_data *data,
-                                     int trip, u8 temp)
+static void exynos7_tmu_set_low_temp(struct exynos_tmu_data *data, u8 temp)
 {
-       unsigned int reg_off, bit_off;
-       u32 th;
-
-       reg_off = ((7 - trip) / 2) * 4;
-       bit_off = ((8 - trip) % 2);
+       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);
+}
 
-       th = readl(data->base + EXYNOS7_THD_TEMP_RISE7_6 + reg_off);
-       th &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off));
-       th |= temp_to_code(data, temp) << (16 * bit_off);
-       writel(th, data->base + EXYNOS7_THD_TEMP_RISE7_6 + reg_off);
+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);
 }
 
-static void exynos7_tmu_set_trip_hyst(struct exynos_tmu_data *data,
-                                     int trip, u8 temp, u8 hyst)
+static void exynos7_tmu_disable_low(struct exynos_tmu_data *data)
 {
-       unsigned int reg_off, bit_off;
-       u32 th;
+       exynos_tmu_update_bit(data, EXYNOS7_TMU_REG_INTEN,
+                             EXYNOS_TMU_INTEN_FALL0_SHIFT + 0, false);
+}
 
-       reg_off = ((7 - trip) / 2) * 4;
-       bit_off = ((8 - trip) % 2);
+static void exynos7_tmu_disable_high(struct exynos_tmu_data *data)
+{
+       exynos_tmu_update_bit(data, EXYNOS7_TMU_REG_INTEN,
+                             EXYNOS7_TMU_INTEN_RISE0_SHIFT + 1, false);
+}
 
-       th = readl(data->base + EXYNOS7_THD_TEMP_FALL7_6 + reg_off);
-       th &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off));
-       th |= temp_to_code(data, temp - hyst) << (16 * bit_off);
-       writel(th, data->base + EXYNOS7_THD_TEMP_FALL7_6 + reg_off);
+static void exynos7_tmu_set_crit_temp(struct exynos_tmu_data *data, u8 temp)
+{
+       /*
+        * Like Exynos 4210, Exynos 7 does not seem to support critical temperature
+        * handling in hardware. Again, we still set a separate interrupt for it.
+        */
+       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 void exynos7_tmu_initialize(struct platform_device *pdev)
@@ -547,87 +585,44 @@ static void exynos7_tmu_initialize(struct platform_device *pdev)
 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;
-       struct thermal_trip trip;
-       unsigned int con, interrupt_en = 0, i;
+       unsigned int con;
 
        con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL));
 
-       if (on) {
-               for (i = 0; i < data->ntrip; i++) {
-                       if (thermal_zone_get_trip(tz, i, &trip))
-                               continue;
-
-                       interrupt_en |=
-                               BIT(EXYNOS_TMU_INTEN_RISE0_SHIFT + i * 4);
-               }
-
-               if (data->soc != SOC_ARCH_EXYNOS4210)
-                       interrupt_en |=
-                               interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT;
-
+       if (on)
                con |= BIT(EXYNOS_TMU_CORE_EN_SHIFT);
-       } else {
+       else
                con &= ~BIT(EXYNOS_TMU_CORE_EN_SHIFT);
-       }
 
-       writel(interrupt_en, data->base + EXYNOS_TMU_REG_INTEN);
        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;
-       struct thermal_trip trip;
-       unsigned int con, interrupt_en = 0, pd_det_en, i;
+       unsigned int con, pd_det_en;
 
        con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL));
 
-       if (on) {
-               for (i = 0; i < data->ntrip; i++) {
-                       if (thermal_zone_get_trip(tz, i, &trip))
-                               continue;
-
-                       interrupt_en |=
-                               BIT(EXYNOS7_TMU_INTEN_RISE0_SHIFT + i);
-               }
-
-               interrupt_en |=
-                       interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT;
-
+       if (on)
                con |= BIT(EXYNOS_TMU_CORE_EN_SHIFT);
-       else
+       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 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;
-       struct thermal_trip trip;
-       unsigned int con, interrupt_en = 0, i;
+       unsigned int con;
 
        con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL));
 
        if (on) {
-               for (i = 0; i < data->ntrip; i++) {
-                       if (thermal_zone_get_trip(tz, i, &trip))
-                               continue;
-
-                       interrupt_en |=
-                               BIT(EXYNOS7_TMU_INTEN_RISE0_SHIFT + i);
-               }
-
-               interrupt_en |=
-                       interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT;
-
                con |= BIT(EXYNOS_TMU_CORE_EN_SHIFT);
                con |= BIT(EXYNOS7_PD_DET_EN_SHIFT);
        } else {
@@ -635,7 +630,6 @@ static void exynos7_tmu_control(struct platform_device *pdev, bool on)
                con &= ~BIT(EXYNOS7_PD_DET_EN_SHIFT);
        }
 
-       writel(interrupt_en, data->base + EXYNOS7_TMU_REG_INTEN);
        writel(con, data->base + EXYNOS_TMU_REG_CONTROL);
 }
 
@@ -873,13 +867,15 @@ static int exynos_map_dt_data(struct platform_device *pdev)
 
        switch (data->soc) {
        case SOC_ARCH_EXYNOS4210:
-               data->tmu_set_trip_temp = exynos4210_tmu_set_trip_temp;
-               data->tmu_set_trip_hyst = exynos4210_tmu_set_trip_hyst;
+               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;
@@ -892,14 +888,16 @@ 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_trip_temp = exynos4412_tmu_set_trip_temp;
-               data->tmu_set_trip_hyst = exynos4412_tmu_set_trip_hyst;
+               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;
@@ -911,14 +909,16 @@ static int exynos_map_dt_data(struct platform_device *pdev)
                data->max_efuse_value = 100;
                break;
        case SOC_ARCH_EXYNOS5433:
-               data->tmu_set_trip_temp = exynos5433_tmu_set_trip_temp;
-               data->tmu_set_trip_hyst = exynos5433_tmu_set_trip_hyst;
+               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;
                data->gain = 8;
                if (res.start == EXYNOS5433_G3D_BASE)
                        data->reference_voltage = 23;
@@ -929,14 +929,16 @@ static int exynos_map_dt_data(struct platform_device *pdev)
                data->max_efuse_value = 150;
                break;
        case SOC_ARCH_EXYNOS7:
-               data->tmu_set_trip_temp = exynos7_tmu_set_trip_temp;
-               data->tmu_set_trip_hyst = exynos7_tmu_set_trip_hyst;
+               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;
@@ -972,9 +974,32 @@ static int exynos_map_dt_data(struct platform_device *pdev)
        return 0;
 }
 
+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)