thermal: exynos: Added cold event.
authorHyeonseong Gil <hs.gil@samsung.com>
Mon, 9 May 2016 06:22:25 +0000 (15:22 +0900)
committerChungwoo Park <cww.park@samsung.com>
Mon, 21 May 2018 08:09:15 +0000 (17:09 +0900)
Resolved migration conflicts from kernel 4.9 to 4.14.

Change-Id: Ie3397c0359dc0b220ac03f3c552cc1852415f0c2
Signed-off-by: Hyeonseong Gil <hs.gil@samsung.com>
drivers/thermal/cpu_cooling.c
drivers/thermal/samsung/exynos_tmu.c
include/linux/thermal.h

index 815b6f10d56aea74179ab766615397614df104c0..dc35593a0d5bc78b142e8f2b952082d8e61060b0 100644 (file)
@@ -34,6 +34,8 @@
 
 #include <trace/events/thermal.h>
 
+#include <soc/samsung/tmu.h>
+
 /*
  * Cooling state <-> CPUFreq frequency
  *
@@ -61,6 +63,10 @@ struct freq_table {
        u32 power;
 };
 
+static BLOCKING_NOTIFIER_HEAD(cpu_notifier);
+
+static enum tmu_noti_state_t cpu_tstate = TMU_NORMAL;
+
 /**
  * struct time_in_idle - Idle time stats
  * @time: previous reading of the absolute time that this cpu was idle
@@ -593,6 +599,30 @@ static int cpufreq_power2state(struct thermal_cooling_device *cdev,
        return 0;
 }
 
+static int cpufreq_set_cur_temp(struct thermal_cooling_device *cdev,
+                               bool suspended, int temp)
+{
+       enum tmu_noti_state_t tstate;
+       unsigned int on;
+
+       if (suspended || temp < EXYNOS_COLD_TEMP) {
+               tstate = TMU_COLD;
+               on = 1;
+       } else {
+               tstate = TMU_NORMAL;
+               on = 0;
+       }
+
+       if (cpu_tstate == tstate)
+               return 0;
+
+       cpu_tstate = tstate;
+
+       blocking_notifier_call_chain(&cpu_notifier, TMU_COLD, &on);
+
+       return 0;
+}
+
 /* Bind cpufreq callbacks to thermal cooling device ops */
 
 static struct thermal_cooling_device_ops cpufreq_cooling_ops = {
@@ -615,6 +645,11 @@ static struct notifier_block thermal_cpufreq_notifier_block = {
        .notifier_call = cpufreq_thermal_notifier,
 };
 
+int exynos_tmu_add_notifier(struct notifier_block *n)
+{
+       return blocking_notifier_chain_register(&cpu_notifier, n);
+}
+
 static unsigned int find_next_max(struct cpufreq_frequency_table *table,
                                  unsigned int prev_max)
 {
@@ -703,6 +738,9 @@ __cpufreq_cooling_register(struct device_node *np,
        }
        cpufreq_cdev->id = ret;
 
+       if (cpufreq_cdev->id == 0)
+               cpufreq_cooling_ops.set_cur_temp = cpufreq_set_cur_temp;
+
        snprintf(dev_name, sizeof(dev_name), "thermal-cpufreq-%d",
                 cpufreq_cdev->id);
 
index 4da916c80e55bfd123ef5f501e137e7c0b81a617..03988c90ceddf755dbedbe5c605c8e63d90a9627 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/of_irq.h>
 #include <linux/platform_device.h>
 #include <linux/cpufreq.h>
+#include <linux/suspend.h>
 
 #include "exynos_tmu.h"
 #include "../thermal_core.h"
 #define EXYNOS_TMU_REG_EMUL_CON                        (0x160)
 
 #define MCELSIUS       1000
+
+static bool suspended;
+static DEFINE_MUTEX (thermal_suspend_lock);
+
+/* list of multiple instance for each thermal sensor */
+static LIST_HEAD(dtm_dev_list);
 /**
  * struct exynos_tmu_data : A structure to hold the private data of the TMU
        driver
@@ -128,6 +135,7 @@ struct exynos_tmu_data {
        struct thermal_zone_device *tzd;
        unsigned int ntrip;
        struct thermal_cooling_device *cool_dev;
+       struct list_head node;
 
        int (*tmu_initialize)(struct platform_device *pdev);
        void (*tmu_control)(struct platform_device *pdev, bool on);
@@ -402,6 +410,7 @@ static void exynos8890_tmu_control(struct platform_device *pdev, bool on)
 static int exynos_get_temp(void *p, int *temp)
 {
        struct exynos_tmu_data *data = p;
+       struct thermal_cooling_device *cdev;
 
        if (!data || !data->tmu_read)
                return -EINVAL;
@@ -412,6 +421,18 @@ static int exynos_get_temp(void *p, int *temp)
 
        mutex_unlock(&data->lock);
 
+       cdev = data->cool_dev;
+
+       if (!cdev)
+               return 0;
+
+       mutex_lock(&thermal_suspend_lock);
+
+       if (cdev->ops->set_cur_temp)
+               cdev->ops->set_cur_temp(cdev, suspended, *temp / 1000);
+
+       mutex_unlock(&thermal_suspend_lock);
+
        return 0;
 }
 
@@ -525,6 +546,38 @@ static irqreturn_t exynos_tmu_irq(int irq, void *id)
        return IRQ_HANDLED;
 }
 
+static int exynos_pm_notifier(struct notifier_block *notifier,
+                       unsigned long event, void *v)
+{
+       struct exynos_tmu_data *devnode;
+       struct thermal_cooling_device *cdev;
+
+       switch (event) {
+       case PM_SUSPEND_PREPARE:
+               mutex_lock(&thermal_suspend_lock);
+               suspended = true;
+               list_for_each_entry(devnode, &dtm_dev_list, node) {
+                       cdev = devnode->cool_dev;
+
+                       if (cdev && cdev->ops->set_cur_temp)
+                               cdev->ops->set_cur_temp(cdev, suspended, 0);
+               }
+               mutex_unlock(&thermal_suspend_lock);
+               break;
+       case PM_POST_SUSPEND:
+               mutex_lock(&thermal_suspend_lock);
+               suspended = false;
+               mutex_unlock(&thermal_suspend_lock);
+               break;
+       }
+
+       return NOTIFY_OK;
+}
+
+static struct notifier_block exynos_tmu_pm_notifier = {
+       .notifier_call = exynos_pm_notifier,
+};
+
 static const struct of_device_id exynos_tmu_match[] = {
        { .compatible = "samsung,exynos8890-tmu", },
        { /* sentinel */ },
@@ -725,6 +778,13 @@ static int exynos_tmu_probe(struct platform_device *pdev)
 
        exynos_tmu_control(pdev, true);
 
+       mutex_lock(&data->lock);
+       list_add_tail(&data->node, &dtm_dev_list);
+       mutex_unlock(&data->lock);
+
+       if (list_is_singular(&dtm_dev_list))
+               register_pm_notifier(&exynos_tmu_pm_notifier);
+
        if (!IS_ERR(data->tzd))
                data->tzd->ops->set_mode(data->tzd, THERMAL_DEVICE_ENABLED);
 
@@ -740,10 +800,22 @@ static int exynos_tmu_remove(struct platform_device *pdev)
 {
        struct exynos_tmu_data *data = platform_get_drvdata(pdev);
        struct thermal_zone_device *tzd = data->tzd;
+       struct exynos_tmu_data *devnode;
+
+       if (list_is_singular(&dtm_dev_list))
+               unregister_pm_notifier(&exynos_tmu_pm_notifier);
 
        thermal_zone_of_sensor_unregister(&pdev->dev, tzd);
        exynos_tmu_control(pdev, false);
 
+       mutex_lock(&data->lock);
+       list_for_each_entry(devnode, &dtm_dev_list, node) {
+               if (devnode->id == data->id) {
+                       list_del(&devnode->node);
+               }
+       }
+       mutex_unlock(&data->lock);
+
        return 0;
 }
 
index fd5b959c753c5340f3eaa58da5b752fea2908e84..905af30d494431c71fdb8895259b07e41bfc2dfa 100644 (file)
@@ -140,6 +140,7 @@ struct thermal_cooling_device_ops {
                           struct thermal_zone_device *, unsigned long, u32 *);
        int (*power2state)(struct thermal_cooling_device *,
                           struct thermal_zone_device *, u32, unsigned long *);
+       int (*set_cur_temp) (struct thermal_cooling_device *, bool, int);
 };
 
 struct thermal_cooling_device {