TMU urgently sends active-high signal (thermal trip) to PMU, and thermal
tripping by hardware logic. Thermal tripping means that PMU cuts off the
whole power of SoC by controlling external voltage regulator.

Acked-by: Kukjin Kim <kgene....@samsung.com>
Signed-off-by: Jonghwan Choi <jhbird.c...@samsung.com>
Signed-off-by: Amit Daniel Kachhap <amit.dan...@samsung.com>
---
 drivers/thermal/samsung/exynos_tmu.c      |   45 +++++++++++++++++++++++++---
 drivers/thermal/samsung/exynos_tmu_data.c |    2 +
 drivers/thermal/samsung/exynos_tmu_data.h |    2 +
 3 files changed, 44 insertions(+), 5 deletions(-)

diff --git a/drivers/thermal/samsung/exynos_tmu.c 
b/drivers/thermal/samsung/exynos_tmu.c
index 6fd776f..33f494e 100644
--- a/drivers/thermal/samsung/exynos_tmu.c
+++ b/drivers/thermal/samsung/exynos_tmu.c
@@ -117,7 +117,7 @@ static int exynos_tmu_initialize(struct platform_device 
*pdev)
        struct exynos_tmu_data *data = platform_get_drvdata(pdev);
        struct exynos_tmu_platform_data *pdata = data->pdata;
        const struct exynos_tmu_registers *reg = pdata->registers;
-       unsigned int status, trim_info;
+       unsigned int status, trim_info = 0, con;
        unsigned int rising_threshold = 0, falling_threshold = 0;
        int ret = 0, threshold_code, i, trigger_levs = 0;
 
@@ -144,10 +144,26 @@ static int exynos_tmu_initialize(struct platform_device 
*pdev)
                        (data->temp_error2 != 0))
                data->temp_error1 = pdata->efuse_value;
 
-       /* Count trigger levels to be enabled */
-       for (i = 0; i < MAX_THRESHOLD_LEVS; i++)
-               if (pdata->trigger_levels[i])
+       if (pdata->max_trigger_level > MAX_THRESHOLD_LEVS) {
+               dev_err(&pdev->dev, "Invalid max trigger level\n");
+               goto out;
+       }
+
+       for (i = 0; i < pdata->max_trigger_level; i++) {
+               if (!pdata->trigger_levels[i])
+                       continue;
+
+               if ((pdata->trigger_type[i] == HW_TRIP) &&
+               (!pdata->trigger_levels[pdata->max_trigger_level - 1])) {
+                       dev_err(&pdev->dev, "Invalid hw trigger level\n");
+                       ret = -EINVAL;
+                       goto out;
+               }
+
+               /* Count trigger levels except the HW trip*/
+               if (!(pdata->trigger_type[i] == HW_TRIP))
                        trigger_levs++;
+       }
 
        if (data->soc == SOC_ARCH_EXYNOS4210) {
                /* Write temperature code for threshold */
@@ -165,7 +181,8 @@ static int exynos_tmu_initialize(struct platform_device 
*pdev)
                writel(reg->inten_rise_mask, data->base + reg->tmu_intclear);
        } else if (data->soc == SOC_ARCH_EXYNOS) {
                /* Write temperature code for rising and falling threshold */
-               for (i = 0; i < trigger_levs; i++) {
+               for (i = 0;
+               i < trigger_levs && i < EXYNOS_MAX_TRIGGER_PER_REG; i++) {
                        threshold_code = temp_to_code(data,
                                                pdata->trigger_levels[i]);
                        if (threshold_code < 0) {
@@ -191,6 +208,24 @@ static int exynos_tmu_initialize(struct platform_device 
*pdev)
                writel((reg->inten_rise_mask << reg->inten_rise_shift) |
                        (reg->inten_fall_mask << reg->inten_fall_shift),
                                data->base + reg->tmu_intclear);
+
+               /* if last threshold limit is also present */
+               i = pdata->max_trigger_level - 1;
+               if (pdata->trigger_levels[i] &&
+                               (pdata->trigger_type[i] == HW_TRIP)) {
+                       threshold_code = temp_to_code(data,
+                                               pdata->trigger_levels[i]);
+                       if (threshold_code < 0) {
+                               ret = threshold_code;
+                               goto out;
+                       }
+                       rising_threshold |= threshold_code << 8 * i;
+                       writel(rising_threshold,
+                               data->base + reg->threshold_th0);
+                       con = readl(data->base + reg->tmu_ctrl);
+                       con |= (1 << reg->therm_trip_en_shift);
+                       writel(con, data->base + reg->tmu_ctrl);
+               }
        }
 out:
        clk_disable(data->clk);
diff --git a/drivers/thermal/samsung/exynos_tmu_data.c 
b/drivers/thermal/samsung/exynos_tmu_data.c
index 589a519..e7cb1cc 100644
--- a/drivers/thermal/samsung/exynos_tmu_data.c
+++ b/drivers/thermal/samsung/exynos_tmu_data.c
@@ -123,6 +123,7 @@ struct exynos_tmu_platform_data const 
exynos5250_default_tmu_data = {
        .trigger_levels[0] = 85,
        .trigger_levels[1] = 103,
        .trigger_levels[2] = 110,
+       .trigger_levels[3] = 120,
        .trigger_enable[0] = 1,
        .trigger_enable[1] = 1,
        .trigger_enable[2] = 1,
@@ -130,6 +131,7 @@ struct exynos_tmu_platform_data const 
exynos5250_default_tmu_data = {
        .trigger_type[0] = THROTTLE_ACTIVE,
        .trigger_type[1] = THROTTLE_ACTIVE,
        .trigger_type[2] = SW_TRIP,
+       .trigger_type[3] = HW_TRIP,
        .max_trigger_level = 4,
        .gain = 8,
        .reference_voltage = 16,
diff --git a/drivers/thermal/samsung/exynos_tmu_data.h 
b/drivers/thermal/samsung/exynos_tmu_data.h
index 0e2244f..4acf070 100644
--- a/drivers/thermal/samsung/exynos_tmu_data.h
+++ b/drivers/thermal/samsung/exynos_tmu_data.h
@@ -91,6 +91,8 @@
 #define EXYNOS_EMUL_DATA_MASK  0xFF
 #define EXYNOS_EMUL_ENABLE     0x1
 
+#define EXYNOS_MAX_TRIGGER_PER_REG     4
+
 #if defined(CONFIG_CPU_EXYNOS4210)
 extern struct exynos_tmu_platform_data const exynos4210_default_tmu_data;
 #define EXYNOS4210_TMU_DRV_DATA (&exynos4210_default_tmu_data)
-- 
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to