summaryrefslogtreecommitdiff
path: root/patches.tizen/0954-thermal-exynos-Bifurcate-exynos-thermal-common-and-t.patch
diff options
context:
space:
mode:
Diffstat (limited to 'patches.tizen/0954-thermal-exynos-Bifurcate-exynos-thermal-common-and-t.patch')
-rw-r--r--patches.tizen/0954-thermal-exynos-Bifurcate-exynos-thermal-common-and-t.patch1003
1 files changed, 1003 insertions, 0 deletions
diff --git a/patches.tizen/0954-thermal-exynos-Bifurcate-exynos-thermal-common-and-t.patch b/patches.tizen/0954-thermal-exynos-Bifurcate-exynos-thermal-common-and-t.patch
new file mode 100644
index 00000000000..ad355e19dbb
--- /dev/null
+++ b/patches.tizen/0954-thermal-exynos-Bifurcate-exynos-thermal-common-and-t.patch
@@ -0,0 +1,1003 @@
+From edfb76bc55440d9a466408f01c6978e79d25845a Mon Sep 17 00:00:00 2001
+From: Amit Daniel Kachhap <amit.daniel@samsung.com>
+Date: Mon, 24 Jun 2013 16:20:25 +0530
+Subject: [PATCH 0954/1302] thermal: exynos: Bifurcate exynos thermal common
+ and tmu controller code
+
+This code bifurcates exynos thermal implementation into common and sensor
+specific parts. The common thermal code interacts with core thermal layer and
+core cpufreq cooling parts and is independent of SOC specific driver. This
+change is needed to cleanly add support for new TMU sensors.
+
+Acked-by: Kukjin Kim <kgene.kim@samsung.com>
+Acked-by: Jonghwa Lee <jonghwa3.lee@samsung.com>
+Acked-by: Eduardo Valentin <eduardo.valentin@ti.com>
+Signed-off-by: Amit Daniel Kachhap <amit.daniel@samsung.com>
+Signed-off-by: Eduardo Valentin <eduardo.valentin@ti.com>
+Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
+---
+ drivers/thermal/samsung/Kconfig | 19 +-
+ drivers/thermal/samsung/Makefile | 4 +-
+ drivers/thermal/samsung/exynos_thermal.c | 419 +-----------------------
+ drivers/thermal/samsung/exynos_thermal_common.c | 385 ++++++++++++++++++++++
+ drivers/thermal/samsung/exynos_thermal_common.h | 83 +++++
+ 5 files changed, 491 insertions(+), 419 deletions(-)
+ create mode 100644 drivers/thermal/samsung/exynos_thermal_common.c
+ create mode 100644 drivers/thermal/samsung/exynos_thermal_common.h
+
+diff --git a/drivers/thermal/samsung/Kconfig b/drivers/thermal/samsung/Kconfig
+index 2cf31ad..f8100b1 100644
+--- a/drivers/thermal/samsung/Kconfig
++++ b/drivers/thermal/samsung/Kconfig
+@@ -1,8 +1,17 @@
+ config EXYNOS_THERMAL
+- tristate "Temperature sensor on Samsung EXYNOS"
++ tristate "Exynos thermal management unit driver"
+ depends on ARCH_HAS_BANDGAP
+ help
+- If you say yes here you get support for TMU (Thermal Management
+- Unit) on SAMSUNG EXYNOS series of SoC. This helps in registering
+- the exynos thermal driver with the core thermal layer and cpu
+- cooling API's.
++ If you say yes here you get support for the TMU (Thermal Management
++ Unit) driver for SAMSUNG EXYNOS series of soc. This driver initialises
++ the TMU, reports temperature and handles cooling action if defined.
++ This driver uses the exynos core thermal API's.
++
++config EXYNOS_THERMAL_CORE
++ bool "Core thermal framework support for EXYNOS SOC's"
++ depends on EXYNOS_THERMAL
++ help
++ If you say yes here you get support for EXYNOS TMU
++ (Thermal Management Unit) common registration/unregistration
++ functions to the core thermal layer and also to use the generic
++ cpu cooling API's.
+diff --git a/drivers/thermal/samsung/Makefile b/drivers/thermal/samsung/Makefile
+index 1fe6d93..6227d4f 100644
+--- a/drivers/thermal/samsung/Makefile
++++ b/drivers/thermal/samsung/Makefile
+@@ -1,4 +1,6 @@
+ #
+ # Samsung thermal specific Makefile
+ #
+-obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o
++obj-$(CONFIG_EXYNOS_THERMAL) += exynos_soc_thermal.o
++exynos_soc_thermal-y := exynos_thermal.o
++exynos_soc_thermal-$(CONFIG_EXYNOS_THERMAL_CORE) += exynos_thermal_common.o
+diff --git a/drivers/thermal/samsung/exynos_thermal.c b/drivers/thermal/samsung/exynos_thermal.c
+index 9af4b93..f3500ab 100644
+--- a/drivers/thermal/samsung/exynos_thermal.c
++++ b/drivers/thermal/samsung/exynos_thermal.c
+@@ -21,23 +21,15 @@
+ *
+ */
+
+-#include <linux/module.h>
+-#include <linux/err.h>
+-#include <linux/kernel.h>
+-#include <linux/slab.h>
+-#include <linux/platform_device.h>
+-#include <linux/interrupt.h>
+ #include <linux/clk.h>
+-#include <linux/workqueue.h>
+-#include <linux/sysfs.h>
+-#include <linux/kobject.h>
+ #include <linux/io.h>
+-#include <linux/mutex.h>
+-#include <linux/platform_data/exynos_thermal.h>
+-#include <linux/thermal.h>
+-#include <linux/cpufreq.h>
+-#include <linux/cpu_cooling.h>
++#include <linux/interrupt.h>
++#include <linux/module.h>
+ #include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/platform_data/exynos_thermal.h>
++
++#include "exynos_thermal_common.h"
+
+ /* Exynos generic registers */
+ #define EXYNOS_TMU_REG_TRIMINFO 0x0
+@@ -88,16 +80,6 @@
+ #define EFUSE_MIN_VALUE 40
+ #define EFUSE_MAX_VALUE 100
+
+-/* In-kernel thermal framework related macros & definations */
+-#define SENSOR_NAME_LEN 16
+-#define MAX_TRIP_COUNT 8
+-#define MAX_COOLING_DEVICE 4
+-#define MAX_THRESHOLD_LEVS 4
+-
+-#define ACTIVE_INTERVAL 500
+-#define IDLE_INTERVAL 10000
+-#define MCELSIUS 1000
+-
+ #ifdef CONFIG_THERMAL_EMULATION
+ #define EXYNOS_EMUL_TIME 0x57F0
+ #define EXYNOS_EMUL_TIME_SHIFT 16
+@@ -106,17 +88,6 @@
+ #define EXYNOS_EMUL_ENABLE 0x1
+ #endif /* CONFIG_THERMAL_EMULATION */
+
+-/* CPU Zone information */
+-#define PANIC_ZONE 4
+-#define WARN_ZONE 3
+-#define MONITOR_ZONE 2
+-#define SAFE_ZONE 1
+-
+-#define GET_ZONE(trip) (trip + 2)
+-#define GET_TRIP(zone) (zone - 2)
+-
+-#define EXYNOS_ZONE_COUNT 3
+-
+ struct exynos_tmu_data {
+ struct exynos_tmu_platform_data *pdata;
+ struct resource *mem;
+@@ -129,384 +100,6 @@ struct exynos_tmu_data {
+ u8 temp_error1, temp_error2;
+ };
+
+-struct thermal_trip_point_conf {
+- int trip_val[MAX_TRIP_COUNT];
+- int trip_count;
+- u8 trigger_falling;
+-};
+-
+-struct thermal_cooling_conf {
+- struct freq_clip_table freq_data[MAX_TRIP_COUNT];
+- int freq_clip_count;
+-};
+-
+-struct thermal_sensor_conf {
+- char name[SENSOR_NAME_LEN];
+- int (*read_temperature)(void *data);
+- int (*write_emul_temp)(void *drv_data, unsigned long temp);
+- struct thermal_trip_point_conf trip_data;
+- struct thermal_cooling_conf cooling_data;
+- void *private_data;
+-};
+-
+-struct exynos_thermal_zone {
+- enum thermal_device_mode mode;
+- struct thermal_zone_device *therm_dev;
+- struct thermal_cooling_device *cool_dev[MAX_COOLING_DEVICE];
+- unsigned int cool_dev_size;
+- struct platform_device *exynos4_dev;
+- struct thermal_sensor_conf *sensor_conf;
+- bool bind;
+-};
+-
+-static struct exynos_thermal_zone *th_zone;
+-static void exynos_unregister_thermal(void);
+-static int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf);
+-
+-/* Get mode callback functions for thermal zone */
+-static int exynos_get_mode(struct thermal_zone_device *thermal,
+- enum thermal_device_mode *mode)
+-{
+- if (th_zone)
+- *mode = th_zone->mode;
+- return 0;
+-}
+-
+-/* Set mode callback functions for thermal zone */
+-static int exynos_set_mode(struct thermal_zone_device *thermal,
+- enum thermal_device_mode mode)
+-{
+- if (!th_zone->therm_dev) {
+- pr_notice("thermal zone not registered\n");
+- return 0;
+- }
+-
+- mutex_lock(&th_zone->therm_dev->lock);
+-
+- if (mode == THERMAL_DEVICE_ENABLED &&
+- !th_zone->sensor_conf->trip_data.trigger_falling)
+- th_zone->therm_dev->polling_delay = IDLE_INTERVAL;
+- else
+- th_zone->therm_dev->polling_delay = 0;
+-
+- mutex_unlock(&th_zone->therm_dev->lock);
+-
+- th_zone->mode = mode;
+- thermal_zone_device_update(th_zone->therm_dev);
+- pr_info("thermal polling set for duration=%d msec\n",
+- th_zone->therm_dev->polling_delay);
+- return 0;
+-}
+-
+-
+-/* Get trip type callback functions for thermal zone */
+-static int exynos_get_trip_type(struct thermal_zone_device *thermal, int trip,
+- enum thermal_trip_type *type)
+-{
+- switch (GET_ZONE(trip)) {
+- case MONITOR_ZONE:
+- case WARN_ZONE:
+- *type = THERMAL_TRIP_ACTIVE;
+- break;
+- case PANIC_ZONE:
+- *type = THERMAL_TRIP_CRITICAL;
+- break;
+- default:
+- return -EINVAL;
+- }
+- return 0;
+-}
+-
+-/* Get trip temperature callback functions for thermal zone */
+-static int exynos_get_trip_temp(struct thermal_zone_device *thermal, int trip,
+- unsigned long *temp)
+-{
+- if (trip < GET_TRIP(MONITOR_ZONE) || trip > GET_TRIP(PANIC_ZONE))
+- return -EINVAL;
+-
+- *temp = th_zone->sensor_conf->trip_data.trip_val[trip];
+- /* convert the temperature into millicelsius */
+- *temp = *temp * MCELSIUS;
+-
+- return 0;
+-}
+-
+-/* Get critical temperature callback functions for thermal zone */
+-static int exynos_get_crit_temp(struct thermal_zone_device *thermal,
+- unsigned long *temp)
+-{
+- int ret;
+- /* Panic zone */
+- ret = exynos_get_trip_temp(thermal, GET_TRIP(PANIC_ZONE), temp);
+- return ret;
+-}
+-
+-/* Bind callback functions for thermal zone */
+-static int exynos_bind(struct thermal_zone_device *thermal,
+- struct thermal_cooling_device *cdev)
+-{
+- int ret = 0, i, tab_size, level;
+- struct freq_clip_table *tab_ptr, *clip_data;
+- struct thermal_sensor_conf *data = th_zone->sensor_conf;
+-
+- tab_ptr = (struct freq_clip_table *)data->cooling_data.freq_data;
+- tab_size = data->cooling_data.freq_clip_count;
+-
+- if (tab_ptr == NULL || tab_size == 0)
+- return -EINVAL;
+-
+- /* find the cooling device registered*/
+- for (i = 0; i < th_zone->cool_dev_size; i++)
+- if (cdev == th_zone->cool_dev[i])
+- break;
+-
+- /* No matching cooling device */
+- if (i == th_zone->cool_dev_size)
+- return 0;
+-
+- /* Bind the thermal zone to the cpufreq cooling device */
+- for (i = 0; i < tab_size; i++) {
+- clip_data = (struct freq_clip_table *)&(tab_ptr[i]);
+- level = cpufreq_cooling_get_level(0, clip_data->freq_clip_max);
+- if (level == THERMAL_CSTATE_INVALID)
+- return 0;
+- switch (GET_ZONE(i)) {
+- case MONITOR_ZONE:
+- case WARN_ZONE:
+- if (thermal_zone_bind_cooling_device(thermal, i, cdev,
+- level, 0)) {
+- pr_err("error binding cdev inst %d\n", i);
+- ret = -EINVAL;
+- }
+- th_zone->bind = true;
+- break;
+- default:
+- ret = -EINVAL;
+- }
+- }
+-
+- return ret;
+-}
+-
+-/* Unbind callback functions for thermal zone */
+-static int exynos_unbind(struct thermal_zone_device *thermal,
+- struct thermal_cooling_device *cdev)
+-{
+- int ret = 0, i, tab_size;
+- struct thermal_sensor_conf *data = th_zone->sensor_conf;
+-
+- if (th_zone->bind == false)
+- return 0;
+-
+- tab_size = data->cooling_data.freq_clip_count;
+-
+- if (tab_size == 0)
+- return -EINVAL;
+-
+- /* find the cooling device registered*/
+- for (i = 0; i < th_zone->cool_dev_size; i++)
+- if (cdev == th_zone->cool_dev[i])
+- break;
+-
+- /* No matching cooling device */
+- if (i == th_zone->cool_dev_size)
+- return 0;
+-
+- /* Bind the thermal zone to the cpufreq cooling device */
+- for (i = 0; i < tab_size; i++) {
+- switch (GET_ZONE(i)) {
+- case MONITOR_ZONE:
+- case WARN_ZONE:
+- if (thermal_zone_unbind_cooling_device(thermal, i,
+- cdev)) {
+- pr_err("error unbinding cdev inst=%d\n", i);
+- ret = -EINVAL;
+- }
+- th_zone->bind = false;
+- break;
+- default:
+- ret = -EINVAL;
+- }
+- }
+- return ret;
+-}
+-
+-/* Get temperature callback functions for thermal zone */
+-static int exynos_get_temp(struct thermal_zone_device *thermal,
+- unsigned long *temp)
+-{
+- void *data;
+-
+- if (!th_zone->sensor_conf) {
+- pr_info("Temperature sensor not initialised\n");
+- return -EINVAL;
+- }
+- data = th_zone->sensor_conf->private_data;
+- *temp = th_zone->sensor_conf->read_temperature(data);
+- /* convert the temperature into millicelsius */
+- *temp = *temp * MCELSIUS;
+- return 0;
+-}
+-
+-/* Get temperature callback functions for thermal zone */
+-static int exynos_set_emul_temp(struct thermal_zone_device *thermal,
+- unsigned long temp)
+-{
+- void *data;
+- int ret = -EINVAL;
+-
+- if (!th_zone->sensor_conf) {
+- pr_info("Temperature sensor not initialised\n");
+- return -EINVAL;
+- }
+- data = th_zone->sensor_conf->private_data;
+- if (th_zone->sensor_conf->write_emul_temp)
+- ret = th_zone->sensor_conf->write_emul_temp(data, temp);
+- return ret;
+-}
+-
+-/* Get the temperature trend */
+-static int exynos_get_trend(struct thermal_zone_device *thermal,
+- int trip, enum thermal_trend *trend)
+-{
+- int ret;
+- unsigned long trip_temp;
+-
+- ret = exynos_get_trip_temp(thermal, trip, &trip_temp);
+- if (ret < 0)
+- return ret;
+-
+- if (thermal->temperature >= trip_temp)
+- *trend = THERMAL_TREND_RAISE_FULL;
+- else
+- *trend = THERMAL_TREND_DROP_FULL;
+-
+- return 0;
+-}
+-/* Operation callback functions for thermal zone */
+-static struct thermal_zone_device_ops const exynos_dev_ops = {
+- .bind = exynos_bind,
+- .unbind = exynos_unbind,
+- .get_temp = exynos_get_temp,
+- .set_emul_temp = exynos_set_emul_temp,
+- .get_trend = exynos_get_trend,
+- .get_mode = exynos_get_mode,
+- .set_mode = exynos_set_mode,
+- .get_trip_type = exynos_get_trip_type,
+- .get_trip_temp = exynos_get_trip_temp,
+- .get_crit_temp = exynos_get_crit_temp,
+-};
+-
+-/*
+- * This function may be called from interrupt based temperature sensor
+- * when threshold is changed.
+- */
+-static void exynos_report_trigger(void)
+-{
+- unsigned int i;
+- char data[10];
+- char *envp[] = { data, NULL };
+-
+- if (!th_zone || !th_zone->therm_dev)
+- return;
+- if (th_zone->bind == false) {
+- for (i = 0; i < th_zone->cool_dev_size; i++) {
+- if (!th_zone->cool_dev[i])
+- continue;
+- exynos_bind(th_zone->therm_dev,
+- th_zone->cool_dev[i]);
+- }
+- }
+-
+- thermal_zone_device_update(th_zone->therm_dev);
+-
+- mutex_lock(&th_zone->therm_dev->lock);
+- /* Find the level for which trip happened */
+- for (i = 0; i < th_zone->sensor_conf->trip_data.trip_count; i++) {
+- if (th_zone->therm_dev->last_temperature <
+- th_zone->sensor_conf->trip_data.trip_val[i] * MCELSIUS)
+- break;
+- }
+-
+- if (th_zone->mode == THERMAL_DEVICE_ENABLED &&
+- !th_zone->sensor_conf->trip_data.trigger_falling) {
+- if (i > 0)
+- th_zone->therm_dev->polling_delay = ACTIVE_INTERVAL;
+- else
+- th_zone->therm_dev->polling_delay = IDLE_INTERVAL;
+- }
+-
+- snprintf(data, sizeof(data), "%u", i);
+- kobject_uevent_env(&th_zone->therm_dev->device.kobj, KOBJ_CHANGE, envp);
+- mutex_unlock(&th_zone->therm_dev->lock);
+-}
+-
+-/* Register with the in-kernel thermal management */
+-static int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf)
+-{
+- int ret;
+- struct cpumask mask_val;
+-
+- if (!sensor_conf || !sensor_conf->read_temperature) {
+- pr_err("Temperature sensor not initialised\n");
+- return -EINVAL;
+- }
+-
+- th_zone = kzalloc(sizeof(struct exynos_thermal_zone), GFP_KERNEL);
+- if (!th_zone)
+- return -ENOMEM;
+-
+- th_zone->sensor_conf = sensor_conf;
+- cpumask_set_cpu(0, &mask_val);
+- th_zone->cool_dev[0] = cpufreq_cooling_register(&mask_val);
+- if (IS_ERR(th_zone->cool_dev[0])) {
+- pr_err("Failed to register cpufreq cooling device\n");
+- ret = -EINVAL;
+- goto err_unregister;
+- }
+- th_zone->cool_dev_size++;
+-
+- th_zone->therm_dev = thermal_zone_device_register(sensor_conf->name,
+- EXYNOS_ZONE_COUNT, 0, NULL, &exynos_dev_ops, NULL, 0,
+- sensor_conf->trip_data.trigger_falling ?
+- 0 : IDLE_INTERVAL);
+-
+- if (IS_ERR(th_zone->therm_dev)) {
+- pr_err("Failed to register thermal zone device\n");
+- ret = PTR_ERR(th_zone->therm_dev);
+- goto err_unregister;
+- }
+- th_zone->mode = THERMAL_DEVICE_ENABLED;
+-
+- pr_info("Exynos: Kernel Thermal management registered\n");
+-
+- return 0;
+-
+-err_unregister:
+- exynos_unregister_thermal();
+- return ret;
+-}
+-
+-/* Un-Register with the in-kernel thermal management */
+-static void exynos_unregister_thermal(void)
+-{
+- int i;
+-
+- if (!th_zone)
+- return;
+-
+- if (th_zone->therm_dev)
+- thermal_zone_device_unregister(th_zone->therm_dev);
+-
+- for (i = 0; i < th_zone->cool_dev_size; i++) {
+- if (th_zone->cool_dev[i])
+- cpufreq_cooling_unregister(th_zone->cool_dev[i]);
+- }
+-
+- kfree(th_zone);
+- pr_info("Exynos: Kernel Thermal management unregistered\n");
+-}
+-
+ /*
+ * TMU treats temperature as a mapped temperature code.
+ * The temperature is converted differently depending on the calibration type.
+diff --git a/drivers/thermal/samsung/exynos_thermal_common.c b/drivers/thermal/samsung/exynos_thermal_common.c
+new file mode 100644
+index 0000000..f20f458
+--- /dev/null
++++ b/drivers/thermal/samsung/exynos_thermal_common.c
+@@ -0,0 +1,385 @@
++/*
++ * exynos_thermal_common.c - Samsung EXYNOS common thermal file
++ *
++ * Copyright (C) 2013 Samsung Electronics
++ * Amit Daniel Kachhap <amit.daniel@samsung.com>
++ *
++ * 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/cpu_cooling.h>
++#include <linux/err.h>
++#include <linux/platform_data/exynos_thermal.h>
++#include <linux/slab.h>
++#include <linux/thermal.h>
++
++#include "exynos_thermal_common.h"
++
++struct exynos_thermal_zone {
++ enum thermal_device_mode mode;
++ struct thermal_zone_device *therm_dev;
++ struct thermal_cooling_device *cool_dev[MAX_COOLING_DEVICE];
++ unsigned int cool_dev_size;
++ struct platform_device *exynos4_dev;
++ struct thermal_sensor_conf *sensor_conf;
++ bool bind;
++};
++
++static struct exynos_thermal_zone *th_zone;
++
++/* Get mode callback functions for thermal zone */
++static int exynos_get_mode(struct thermal_zone_device *thermal,
++ enum thermal_device_mode *mode)
++{
++ if (th_zone)
++ *mode = th_zone->mode;
++ return 0;
++}
++
++/* Set mode callback functions for thermal zone */
++static int exynos_set_mode(struct thermal_zone_device *thermal,
++ enum thermal_device_mode mode)
++{
++ if (!th_zone->therm_dev) {
++ pr_notice("thermal zone not registered\n");
++ return 0;
++ }
++
++ mutex_lock(&th_zone->therm_dev->lock);
++
++ if (mode == THERMAL_DEVICE_ENABLED &&
++ !th_zone->sensor_conf->trip_data.trigger_falling)
++ th_zone->therm_dev->polling_delay = IDLE_INTERVAL;
++ else
++ th_zone->therm_dev->polling_delay = 0;
++
++ mutex_unlock(&th_zone->therm_dev->lock);
++
++ th_zone->mode = mode;
++ thermal_zone_device_update(th_zone->therm_dev);
++ pr_info("thermal polling set for duration=%d msec\n",
++ th_zone->therm_dev->polling_delay);
++ return 0;
++}
++
++
++/* Get trip type callback functions for thermal zone */
++static int exynos_get_trip_type(struct thermal_zone_device *thermal, int trip,
++ enum thermal_trip_type *type)
++{
++ switch (GET_ZONE(trip)) {
++ case MONITOR_ZONE:
++ case WARN_ZONE:
++ *type = THERMAL_TRIP_ACTIVE;
++ break;
++ case PANIC_ZONE:
++ *type = THERMAL_TRIP_CRITICAL;
++ break;
++ default:
++ return -EINVAL;
++ }
++ return 0;
++}
++
++/* Get trip temperature callback functions for thermal zone */
++static int exynos_get_trip_temp(struct thermal_zone_device *thermal, int trip,
++ unsigned long *temp)
++{
++ if (trip < GET_TRIP(MONITOR_ZONE) || trip > GET_TRIP(PANIC_ZONE))
++ return -EINVAL;
++
++ *temp = th_zone->sensor_conf->trip_data.trip_val[trip];
++ /* convert the temperature into millicelsius */
++ *temp = *temp * MCELSIUS;
++
++ return 0;
++}
++
++/* Get critical temperature callback functions for thermal zone */
++static int exynos_get_crit_temp(struct thermal_zone_device *thermal,
++ unsigned long *temp)
++{
++ int ret;
++ /* Panic zone */
++ ret = exynos_get_trip_temp(thermal, GET_TRIP(PANIC_ZONE), temp);
++ return ret;
++}
++
++/* Bind callback functions for thermal zone */
++static int exynos_bind(struct thermal_zone_device *thermal,
++ struct thermal_cooling_device *cdev)
++{
++ int ret = 0, i, tab_size, level;
++ struct freq_clip_table *tab_ptr, *clip_data;
++ struct thermal_sensor_conf *data = th_zone->sensor_conf;
++
++ tab_ptr = (struct freq_clip_table *)data->cooling_data.freq_data;
++ tab_size = data->cooling_data.freq_clip_count;
++
++ if (tab_ptr == NULL || tab_size == 0)
++ return -EINVAL;
++
++ /* find the cooling device registered*/
++ for (i = 0; i < th_zone->cool_dev_size; i++)
++ if (cdev == th_zone->cool_dev[i])
++ break;
++
++ /* No matching cooling device */
++ if (i == th_zone->cool_dev_size)
++ return 0;
++
++ /* Bind the thermal zone to the cpufreq cooling device */
++ for (i = 0; i < tab_size; i++) {
++ clip_data = (struct freq_clip_table *)&(tab_ptr[i]);
++ level = cpufreq_cooling_get_level(0, clip_data->freq_clip_max);
++ if (level == THERMAL_CSTATE_INVALID)
++ return 0;
++ switch (GET_ZONE(i)) {
++ case MONITOR_ZONE:
++ case WARN_ZONE:
++ if (thermal_zone_bind_cooling_device(thermal, i, cdev,
++ level, 0)) {
++ pr_err("error binding cdev inst %d\n", i);
++ ret = -EINVAL;
++ }
++ th_zone->bind = true;
++ break;
++ default:
++ ret = -EINVAL;
++ }
++ }
++
++ return ret;
++}
++
++/* Unbind callback functions for thermal zone */
++static int exynos_unbind(struct thermal_zone_device *thermal,
++ struct thermal_cooling_device *cdev)
++{
++ int ret = 0, i, tab_size;
++ struct thermal_sensor_conf *data = th_zone->sensor_conf;
++
++ if (th_zone->bind == false)
++ return 0;
++
++ tab_size = data->cooling_data.freq_clip_count;
++
++ if (tab_size == 0)
++ return -EINVAL;
++
++ /* find the cooling device registered*/
++ for (i = 0; i < th_zone->cool_dev_size; i++)
++ if (cdev == th_zone->cool_dev[i])
++ break;
++
++ /* No matching cooling device */
++ if (i == th_zone->cool_dev_size)
++ return 0;
++
++ /* Bind the thermal zone to the cpufreq cooling device */
++ for (i = 0; i < tab_size; i++) {
++ switch (GET_ZONE(i)) {
++ case MONITOR_ZONE:
++ case WARN_ZONE:
++ if (thermal_zone_unbind_cooling_device(thermal, i,
++ cdev)) {
++ pr_err("error unbinding cdev inst=%d\n", i);
++ ret = -EINVAL;
++ }
++ th_zone->bind = false;
++ break;
++ default:
++ ret = -EINVAL;
++ }
++ }
++ return ret;
++}
++
++/* Get temperature callback functions for thermal zone */
++static int exynos_get_temp(struct thermal_zone_device *thermal,
++ unsigned long *temp)
++{
++ void *data;
++
++ if (!th_zone->sensor_conf) {
++ pr_info("Temperature sensor not initialised\n");
++ return -EINVAL;
++ }
++ data = th_zone->sensor_conf->private_data;
++ *temp = th_zone->sensor_conf->read_temperature(data);
++ /* convert the temperature into millicelsius */
++ *temp = *temp * MCELSIUS;
++ return 0;
++}
++
++/* Get temperature callback functions for thermal zone */
++static int exynos_set_emul_temp(struct thermal_zone_device *thermal,
++ unsigned long temp)
++{
++ void *data;
++ int ret = -EINVAL;
++
++ if (!th_zone->sensor_conf) {
++ pr_info("Temperature sensor not initialised\n");
++ return -EINVAL;
++ }
++ data = th_zone->sensor_conf->private_data;
++ if (th_zone->sensor_conf->write_emul_temp)
++ ret = th_zone->sensor_conf->write_emul_temp(data, temp);
++ return ret;
++}
++
++/* Get the temperature trend */
++static int exynos_get_trend(struct thermal_zone_device *thermal,
++ int trip, enum thermal_trend *trend)
++{
++ int ret;
++ unsigned long trip_temp;
++
++ ret = exynos_get_trip_temp(thermal, trip, &trip_temp);
++ if (ret < 0)
++ return ret;
++
++ if (thermal->temperature >= trip_temp)
++ *trend = THERMAL_TREND_RAISE_FULL;
++ else
++ *trend = THERMAL_TREND_DROP_FULL;
++
++ return 0;
++}
++/* Operation callback functions for thermal zone */
++static struct thermal_zone_device_ops const exynos_dev_ops = {
++ .bind = exynos_bind,
++ .unbind = exynos_unbind,
++ .get_temp = exynos_get_temp,
++ .set_emul_temp = exynos_set_emul_temp,
++ .get_trend = exynos_get_trend,
++ .get_mode = exynos_get_mode,
++ .set_mode = exynos_set_mode,
++ .get_trip_type = exynos_get_trip_type,
++ .get_trip_temp = exynos_get_trip_temp,
++ .get_crit_temp = exynos_get_crit_temp,
++};
++
++/*
++ * This function may be called from interrupt based temperature sensor
++ * when threshold is changed.
++ */
++void exynos_report_trigger(void)
++{
++ unsigned int i;
++ char data[10];
++ char *envp[] = { data, NULL };
++
++ if (!th_zone || !th_zone->therm_dev)
++ return;
++ if (th_zone->bind == false) {
++ for (i = 0; i < th_zone->cool_dev_size; i++) {
++ if (!th_zone->cool_dev[i])
++ continue;
++ exynos_bind(th_zone->therm_dev,
++ th_zone->cool_dev[i]);
++ }
++ }
++
++ thermal_zone_device_update(th_zone->therm_dev);
++
++ mutex_lock(&th_zone->therm_dev->lock);
++ /* Find the level for which trip happened */
++ for (i = 0; i < th_zone->sensor_conf->trip_data.trip_count; i++) {
++ if (th_zone->therm_dev->last_temperature <
++ th_zone->sensor_conf->trip_data.trip_val[i] * MCELSIUS)
++ break;
++ }
++
++ if (th_zone->mode == THERMAL_DEVICE_ENABLED &&
++ !th_zone->sensor_conf->trip_data.trigger_falling) {
++ if (i > 0)
++ th_zone->therm_dev->polling_delay = ACTIVE_INTERVAL;
++ else
++ th_zone->therm_dev->polling_delay = IDLE_INTERVAL;
++ }
++
++ snprintf(data, sizeof(data), "%u", i);
++ kobject_uevent_env(&th_zone->therm_dev->device.kobj, KOBJ_CHANGE, envp);
++ mutex_unlock(&th_zone->therm_dev->lock);
++}
++
++/* Register with the in-kernel thermal management */
++int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf)
++{
++ int ret;
++ struct cpumask mask_val;
++
++ if (!sensor_conf || !sensor_conf->read_temperature) {
++ pr_err("Temperature sensor not initialised\n");
++ return -EINVAL;
++ }
++
++ th_zone = kzalloc(sizeof(struct exynos_thermal_zone), GFP_KERNEL);
++ if (!th_zone)
++ return -ENOMEM;
++
++ th_zone->sensor_conf = sensor_conf;
++ cpumask_set_cpu(0, &mask_val);
++ th_zone->cool_dev[0] = cpufreq_cooling_register(&mask_val);
++ if (IS_ERR(th_zone->cool_dev[0])) {
++ pr_err("Failed to register cpufreq cooling device\n");
++ ret = -EINVAL;
++ goto err_unregister;
++ }
++ th_zone->cool_dev_size++;
++
++ th_zone->therm_dev = thermal_zone_device_register(sensor_conf->name,
++ EXYNOS_ZONE_COUNT, 0, NULL, &exynos_dev_ops, NULL, 0,
++ sensor_conf->trip_data.trigger_falling ?
++ 0 : IDLE_INTERVAL);
++
++ if (IS_ERR(th_zone->therm_dev)) {
++ pr_err("Failed to register thermal zone device\n");
++ ret = PTR_ERR(th_zone->therm_dev);
++ goto err_unregister;
++ }
++ th_zone->mode = THERMAL_DEVICE_ENABLED;
++
++ pr_info("Exynos: Kernel Thermal management registered\n");
++
++ return 0;
++
++err_unregister:
++ exynos_unregister_thermal();
++ return ret;
++}
++
++/* Un-Register with the in-kernel thermal management */
++void exynos_unregister_thermal(void)
++{
++ int i;
++
++ if (!th_zone)
++ return;
++
++ if (th_zone->therm_dev)
++ thermal_zone_device_unregister(th_zone->therm_dev);
++
++ for (i = 0; i < th_zone->cool_dev_size; i++) {
++ if (th_zone->cool_dev[i])
++ cpufreq_cooling_unregister(th_zone->cool_dev[i]);
++ }
++
++ kfree(th_zone);
++ pr_info("Exynos: Kernel Thermal management unregistered\n");
++}
+diff --git a/drivers/thermal/samsung/exynos_thermal_common.h b/drivers/thermal/samsung/exynos_thermal_common.h
+new file mode 100644
+index 0000000..8df1848
+--- /dev/null
++++ b/drivers/thermal/samsung/exynos_thermal_common.h
+@@ -0,0 +1,83 @@
++/*
++ * exynos_thermal_common.h - Samsung EXYNOS common header file
++ *
++ * Copyright (C) 2013 Samsung Electronics
++ * Amit Daniel Kachhap <amit.daniel@samsung.com>
++ *
++ * 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
++ *
++ */
++
++#ifndef _EXYNOS_THERMAL_COMMON_H
++#define _EXYNOS_THERMAL_COMMON_H
++
++/* In-kernel thermal framework related macros & definations */
++#define SENSOR_NAME_LEN 16
++#define MAX_TRIP_COUNT 8
++#define MAX_COOLING_DEVICE 4
++#define MAX_THRESHOLD_LEVS 4
++
++#define ACTIVE_INTERVAL 500
++#define IDLE_INTERVAL 10000
++#define MCELSIUS 1000
++
++/* CPU Zone information */
++#define PANIC_ZONE 4
++#define WARN_ZONE 3
++#define MONITOR_ZONE 2
++#define SAFE_ZONE 1
++
++#define GET_ZONE(trip) (trip + 2)
++#define GET_TRIP(zone) (zone - 2)
++
++#define EXYNOS_ZONE_COUNT 3
++
++struct thermal_trip_point_conf {
++ int trip_val[MAX_TRIP_COUNT];
++ int trip_count;
++ unsigned char trigger_falling;
++};
++
++struct thermal_cooling_conf {
++ struct freq_clip_table freq_data[MAX_TRIP_COUNT];
++ int freq_clip_count;
++};
++
++struct thermal_sensor_conf {
++ char name[SENSOR_NAME_LEN];
++ int (*read_temperature)(void *data);
++ int (*write_emul_temp)(void *drv_data, unsigned long temp);
++ struct thermal_trip_point_conf trip_data;
++ struct thermal_cooling_conf cooling_data;
++ void *private_data;
++};
++
++/*Functions used exynos based thermal sensor driver*/
++#ifdef CONFIG_EXYNOS_THERMAL_CORE
++void exynos_unregister_thermal(void);
++int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf);
++void exynos_report_trigger(void);
++#else
++static inline void
++exynos_unregister_thermal(void) { return; }
++
++static inline int
++exynos_register_thermal(struct thermal_sensor_conf *sensor_conf) { return 0; }
++
++static inline void
++exynos_report_trigger(void) { return; }
++
++#endif /* CONFIG_EXYNOS_THERMAL_CORE */
++#endif /* _EXYNOS_THERMAL_COMMON_H */
+--
+1.8.3.2
+