#include <trace/events/thermal.h>
+#include <soc/samsung/tmu.h>
+
/*
* Cooling state <-> CPUFreq frequency
*
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
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 = {
.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)
{
}
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);
#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
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);
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;
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;
}
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 */ },
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);
{
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;
}