diff mbox

[3/9] thermal: exynos: Moving into samsung directory for easy maintenance.

Message ID 1364297642-2746-4-git-send-email-amit.daniel@samsung.com (mailing list archive)
State Changes Requested
Delegated to: Zhang Rui
Headers show

Commit Message

Amit Kachhap March 26, 2013, 11:33 a.m. UTC
This movement of files is done for easy maintenance and adding more
new sensor's support for exynos platform easily . This will also help in
bifurcating exynos common and sensor related parts.

Signed-off-by: Amit Daniel Kachhap <amit.daniel@samsung.com>
---
 drivers/thermal/Kconfig                  |   13 +-
 drivers/thermal/Makefile                 |    2 +-
 drivers/thermal/exynos_thermal.c         | 1093 ------------------------------
 drivers/thermal/samsung/Kconfig          |   11 +
 drivers/thermal/samsung/Makefile         |    5 +
 drivers/thermal/samsung/exynos_thermal.c | 1093 ++++++++++++++++++++++++++++++
 6 files changed, 1115 insertions(+), 1102 deletions(-)
 delete mode 100644 drivers/thermal/exynos_thermal.c
 create mode 100644 drivers/thermal/samsung/Kconfig
 create mode 100644 drivers/thermal/samsung/Makefile
 create mode 100644 drivers/thermal/samsung/exynos_thermal.c

Comments

Eduardo Valentin April 11, 2013, 8:25 p.m. UTC | #1
On 26-03-2013 07:33, Amit Daniel Kachhap wrote:
> This movement of files is done for easy maintenance and adding more
> new sensor's support for exynos platform easily . This will also help in
> bifurcating exynos common and sensor related parts.
>
> Signed-off-by: Amit Daniel Kachhap <amit.daniel@samsung.com>
>
> ---
> drivers/thermal/Kconfig                  |   13 +-
>   drivers/thermal/Makefile                 |    2 +-
>   drivers/thermal/exynos_thermal.c         | 1093 ------------------------------
>   drivers/thermal/samsung/Kconfig          |   11 +
>   drivers/thermal/samsung/Makefile         |    5 +
>   drivers/thermal/samsung/exynos_thermal.c | 1093 ++++++++++++++++++++++++++++++
>   6 files changed, 1115 insertions(+), 1102 deletions(-)
>   delete mode 100644 drivers/thermal/exynos_thermal.c
>   create mode 100644 drivers/thermal/samsung/Kconfig
>   create mode 100644 drivers/thermal/samsung/Makefile
>   create mode 100644 drivers/thermal/samsung/exynos_thermal.c
>


nip: use git send-email --find-renames when moving / renaming files. It 
is easier to track  the changes you had to do while moving.

> diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
> index da4c19e..9b2372a 100644
> --- a/drivers/thermal/Kconfig
> +++ b/drivers/thermal/Kconfig
> @@ -109,14 +109,6 @@ config KIRKWOOD_THERMAL
>   	  Support for the Kirkwood thermal sensor driver into the Linux thermal
>   	  framework. Only kirkwood 88F6282 and 88F6283 have this sensor.
>
> -config EXYNOS_THERMAL
> -	tristate "Temperature sensor on Samsung EXYNOS"
> -	depends on (ARCH_EXYNOS4 || ARCH_EXYNOS5)
> -	depends on CPU_THERMAL
> -	help
> -	  If you say yes here you get support for TMU (Thermal Management
> -	  Unit) on SAMSUNG EXYNOS series of SoC.
> -
>   config DOVE_THERMAL
>   	tristate "Temperature sensor on Marvell Dove SoCs"
>   	depends on ARCH_DOVE
> @@ -156,4 +148,9 @@ config INTEL_POWERCLAMP
>   	  enforce idle time which results in more package C-state residency. The
>   	  user interface is exposed via generic thermal framework.
>
> +menu "Exynos thermal drivers"
> +depends on PLAT_SAMSUNG
> +source "drivers/thermal/samsung/Kconfig"
> +endmenu
> +
>   endif
> diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
> index d3a2b38..584862c 100644
> --- a/drivers/thermal/Makefile
> +++ b/drivers/thermal/Makefile
> @@ -16,7 +16,7 @@ obj-$(CONFIG_CPU_THERMAL)	+= cpu_cooling.o
>   obj-$(CONFIG_SPEAR_THERMAL)	+= spear_thermal.o
>   obj-$(CONFIG_RCAR_THERMAL)	+= rcar_thermal.o
>   obj-$(CONFIG_KIRKWOOD_THERMAL)  += kirkwood_thermal.o
> -obj-$(CONFIG_EXYNOS_THERMAL)	+= exynos_thermal.o
> +obj-y				+= samsung/
>   obj-$(CONFIG_DOVE_THERMAL)  	+= dove_thermal.o
>   obj-$(CONFIG_DB8500_THERMAL)	+= db8500_thermal.o
>   obj-$(CONFIG_DB8500_CPUFREQ_COOLING)	+= db8500_cpufreq_cooling.o
> diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c
> deleted file mode 100644
> index dc9b91b..0000000
> --- a/drivers/thermal/exynos_thermal.c
> +++ /dev/null
> @@ -1,1093 +0,0 @@
> -/*
> - * exynos_thermal.c - Samsung EXYNOS TMU (Thermal Management Unit)
> - *
> - *  Copyright (C) 2011 Samsung Electronics
> - *  Donggeun Kim <dg77.kim@samsung.com>
> - *  Amit Daniel Kachhap <amit.kachhap@linaro.org>
> - *
> - * 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/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/of.h>
> -
> -#include <plat/cpu.h>
> -
> -/* Exynos generic registers */
> -#define EXYNOS_TMU_REG_TRIMINFO		0x0
> -#define EXYNOS_TMU_REG_CONTROL		0x20
> -#define EXYNOS_TMU_REG_STATUS		0x28
> -#define EXYNOS_TMU_REG_CURRENT_TEMP	0x40
> -#define EXYNOS_TMU_REG_INTEN		0x70
> -#define EXYNOS_TMU_REG_INTSTAT		0x74
> -#define EXYNOS_TMU_REG_INTCLEAR		0x78
> -
> -#define EXYNOS_TMU_TRIM_TEMP_MASK	0xff
> -#define EXYNOS_TMU_GAIN_SHIFT		8
> -#define EXYNOS_TMU_REF_VOLTAGE_SHIFT	24
> -#define EXYNOS_TMU_CORE_ON		3
> -#define EXYNOS_TMU_CORE_OFF		2
> -#define EXYNOS_TMU_DEF_CODE_TO_TEMP_OFFSET	50
> -
> -/* Exynos4210 specific registers */
> -#define EXYNOS4210_TMU_REG_THRESHOLD_TEMP	0x44
> -#define EXYNOS4210_TMU_REG_TRIG_LEVEL0	0x50
> -#define EXYNOS4210_TMU_REG_TRIG_LEVEL1	0x54
> -#define EXYNOS4210_TMU_REG_TRIG_LEVEL2	0x58
> -#define EXYNOS4210_TMU_REG_TRIG_LEVEL3	0x5C
> -#define EXYNOS4210_TMU_REG_PAST_TEMP0	0x60
> -#define EXYNOS4210_TMU_REG_PAST_TEMP1	0x64
> -#define EXYNOS4210_TMU_REG_PAST_TEMP2	0x68
> -#define EXYNOS4210_TMU_REG_PAST_TEMP3	0x6C
> -
> -#define EXYNOS4210_TMU_TRIG_LEVEL0_MASK	0x1
> -#define EXYNOS4210_TMU_TRIG_LEVEL1_MASK	0x10
> -#define EXYNOS4210_TMU_TRIG_LEVEL2_MASK	0x100
> -#define EXYNOS4210_TMU_TRIG_LEVEL3_MASK	0x1000
> -#define EXYNOS4210_TMU_INTCLEAR_VAL	0x1111
> -
> -/* Exynos5250 and Exynos4412 specific registers */
> -#define EXYNOS_TMU_TRIMINFO_CON	0x14
> -#define EXYNOS_THD_TEMP_RISE		0x50
> -#define EXYNOS_THD_TEMP_FALL		0x54
> -#define EXYNOS_EMUL_CON		0x80
> -
> -#define EXYNOS_TRIMINFO_RELOAD		0x1
> -#define EXYNOS_TMU_CLEAR_RISE_INT	0x111
> -#define EXYNOS_TMU_CLEAR_FALL_INT	(0x111 << 12)
> -#define EXYNOS_MUX_ADDR_VALUE		6
> -#define EXYNOS_MUX_ADDR_SHIFT		20
> -#define EXYNOS_TMU_TRIP_MODE_SHIFT	13
> -
> -#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
> -#define EXYNOS_EMUL_DATA_SHIFT	8
> -#define EXYNOS_EMUL_DATA_MASK	0xFF
> -#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;
> -	void __iomem *base;
> -	int irq;
> -	enum soc_type soc;
> -	struct work_struct irq_work;
> -	struct mutex lock;
> -	struct clk *clk;
> -	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 *driver_data;
> -	void *pzone_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 void exynos_unregister_thermal(struct thermal_sensor_conf *sensor_conf);
> -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)
> -{
> -	struct exynos_thermal_zone *th_zone = thermal->devdata;
> -	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)
> -{
> -	struct exynos_thermal_zone *th_zone = thermal->devdata;
> -	if (!th_zone) {
> -		pr_notice("thermal zone not registered\n");
> -		return 0;
> -	}
> -
> -	mutex_lock(&thermal->lock);
> -
> -	if (mode == THERMAL_DEVICE_ENABLED &&
> -		!th_zone->sensor_conf->trip_data.trigger_falling)
> -		thermal->polling_delay = IDLE_INTERVAL;
> -	else
> -		thermal->polling_delay = 0;
> -
> -	mutex_unlock(&thermal->lock);
> -
> -	th_zone->mode = mode;
> -	thermal_zone_device_update(thermal);
> -	pr_info("thermal polling set for duration=%d msec\n",
> -				thermal->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)
> -{
> -	struct exynos_thermal_zone *th_zone = thermal->devdata;
> -
> -	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;
> -}
> -
> -static int exynos_get_frequency_level(unsigned int cpu, unsigned int freq)
> -{
> -	int i = 0, ret = -EINVAL;
> -	struct cpufreq_frequency_table *table = NULL;
> -#ifdef CONFIG_CPU_FREQ
> -	table = cpufreq_frequency_get_table(cpu);
> -#endif
> -	if (!table)
> -		return ret;
> -
> -	while (table[i].frequency != CPUFREQ_TABLE_END) {
> -		if (table[i].frequency == CPUFREQ_ENTRY_INVALID)
> -			continue;
> -		if (table[i].frequency == freq)
> -			return i;
> -		i++;
> -	}
> -	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 exynos_thermal_zone *th_zone = thermal->devdata;
> -	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 = exynos_get_frequency_level(0, clip_data->freq_clip_max);
> -		if (level < 0)
> -			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 exynos_thermal_zone *th_zone = thermal->devdata;
> -	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)
> -{
> -	struct exynos_thermal_zone *th_zone = thermal->devdata;
> -	void *data;
> -
> -	if (!th_zone->sensor_conf) {
> -		pr_info("Temperature sensor not initialised\n");
> -		return -EINVAL;
> -	}
> -	data = th_zone->sensor_conf->driver_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;
> -	struct exynos_thermal_zone *th_zone = thermal->devdata;
> -
> -	if (!th_zone->sensor_conf) {
> -		pr_info("Temperature sensor not initialised\n");
> -		return -EINVAL;
> -	}
> -	data = th_zone->sensor_conf->driver_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 = 0;
> -	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 ret;
> -}
> -/* 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(struct thermal_sensor_conf *conf)
> -{
> -	unsigned int i;
> -	char data[10];
> -	char *envp[] = { data, NULL };
> -	struct exynos_thermal_zone *th_zone = conf->pzone_data;
> -
> -	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;
> -	struct exynos_thermal_zone *th_zone;
> -
> -	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, th_zone, &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;
> -	sensor_conf->pzone_data = th_zone;
> -
> -	pr_info("Exynos: Kernel Thermal management registered\n");
> -
> -	return 0;
> -
> -err_unregister:
> -	exynos_unregister_thermal(sensor_conf);
> -	return ret;
> -}
> -
> -/* Un-Register with the in-kernel thermal management */
> -static void exynos_unregister_thermal(struct thermal_sensor_conf *sensor_conf)
> -{
> -	int i;
> -	struct exynos_thermal_zone *th_zone = sensor_conf->pzone_data;
> -
> -	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.
> - */
> -static int temp_to_code(struct exynos_tmu_data *data, u8 temp)
> -{
> -	struct exynos_tmu_platform_data *pdata = data->pdata;
> -	int temp_code;
> -
> -	if (data->soc == SOC_ARCH_EXYNOS4210)
> -		/* temp should range between 25 and 125 */
> -		if (temp < 25 || temp > 125) {
> -			temp_code = -EINVAL;
> -			goto out;
> -		}
> -
> -	switch (pdata->cal_type) {
> -	case TYPE_TWO_POINT_TRIMMING:
> -		temp_code = (temp - 25) *
> -		    (data->temp_error2 - data->temp_error1) /
> -		    (85 - 25) + data->temp_error1;
> -		break;
> -	case TYPE_ONE_POINT_TRIMMING:
> -		temp_code = temp + data->temp_error1 - 25;
> -		break;
> -	default:
> -		temp_code = temp + EXYNOS_TMU_DEF_CODE_TO_TEMP_OFFSET;
> -		break;
> -	}
> -out:
> -	return temp_code;
> -}
> -
> -/*
> - * Calculate a temperature value from a temperature code.
> - * The unit of the temperature is degree Celsius.
> - */
> -static int code_to_temp(struct exynos_tmu_data *data, u8 temp_code)
> -{
> -	struct exynos_tmu_platform_data *pdata = data->pdata;
> -	int temp;
> -
> -	if (data->soc == SOC_ARCH_EXYNOS4210)
> -		/* temp_code should range between 75 and 175 */
> -		if (temp_code < 75 || temp_code > 175) {
> -			temp = -ENODATA;
> -			goto out;
> -		}
> -
> -	switch (pdata->cal_type) {
> -	case TYPE_TWO_POINT_TRIMMING:
> -		temp = (temp_code - data->temp_error1) * (85 - 25) /
> -		    (data->temp_error2 - data->temp_error1) + 25;
> -		break;
> -	case TYPE_ONE_POINT_TRIMMING:
> -		temp = temp_code - data->temp_error1 + 25;
> -		break;
> -	default:
> -		temp = temp_code - EXYNOS_TMU_DEF_CODE_TO_TEMP_OFFSET;
> -		break;
> -	}
> -out:
> -	return temp;
> -}
> -
> -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;
> -	unsigned int status, trim_info;
> -	unsigned int rising_threshold = 0, falling_threshold = 0;
> -	int ret = 0, threshold_code, i, trigger_levs = 0;
> -
> -	mutex_lock(&data->lock);
> -	clk_enable(data->clk);
> -
> -	status = readb(data->base + EXYNOS_TMU_REG_STATUS);
> -	if (!status) {
> -		ret = -EBUSY;
> -		goto out;
> -	}
> -
> -	if (data->soc == SOC_ARCH_EXYNOS) {
> -		__raw_writel(EXYNOS_TRIMINFO_RELOAD,
> -				data->base + EXYNOS_TMU_TRIMINFO_CON);
> -	}
> -	/* Save trimming info in order to perform calibration */
> -	trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO);
> -	data->temp_error1 = trim_info & EXYNOS_TMU_TRIM_TEMP_MASK;
> -	data->temp_error2 = ((trim_info >> 8) & EXYNOS_TMU_TRIM_TEMP_MASK);
> -
> -	if ((EFUSE_MIN_VALUE > data->temp_error1) ||
> -			(data->temp_error1 > EFUSE_MAX_VALUE) ||
> -			(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])
> -			trigger_levs++;
> -
> -	if (data->soc == SOC_ARCH_EXYNOS4210) {
> -		/* Write temperature code for threshold */
> -		threshold_code = temp_to_code(data, pdata->threshold);
> -		if (threshold_code < 0) {
> -			ret = threshold_code;
> -			goto out;
> -		}
> -		writeb(threshold_code,
> -			data->base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP);
> -		for (i = 0; i < trigger_levs; i++)
> -			writeb(pdata->trigger_levels[i],
> -			data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL0 + i * 4);
> -
> -		writel(EXYNOS4210_TMU_INTCLEAR_VAL,
> -			data->base + EXYNOS_TMU_REG_INTCLEAR);
> -	} else if (data->soc == SOC_ARCH_EXYNOS) {
> -		/* Write temperature code for rising and falling threshold */
> -		for (i = 0; i < trigger_levs; i++) {
> -			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;
> -			if (pdata->threshold_falling) {
> -				threshold_code = temp_to_code(data,
> -						pdata->trigger_levels[i] -
> -						pdata->threshold_falling);
> -				if (threshold_code > 0)
> -					falling_threshold |=
> -						threshold_code << 8 * i;
> -			}
> -		}
> -
> -		writel(rising_threshold,
> -				data->base + EXYNOS_THD_TEMP_RISE);
> -		writel(falling_threshold,
> -				data->base + EXYNOS_THD_TEMP_FALL);
> -
> -		writel(EXYNOS_TMU_CLEAR_RISE_INT | EXYNOS_TMU_CLEAR_FALL_INT,
> -				data->base + EXYNOS_TMU_REG_INTCLEAR);
> -	}
> -out:
> -	clk_disable(data->clk);
> -	mutex_unlock(&data->lock);
> -
> -	return ret;
> -}
> -
> -static void exynos_tmu_control(struct platform_device *pdev, bool on)
> -{
> -	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
> -	struct exynos_tmu_platform_data *pdata = data->pdata;
> -	unsigned int con, interrupt_en;
> -
> -	mutex_lock(&data->lock);
> -	clk_enable(data->clk);
> -
> -	con = pdata->reference_voltage << EXYNOS_TMU_REF_VOLTAGE_SHIFT |
> -		pdata->gain << EXYNOS_TMU_GAIN_SHIFT;
> -
> -	if (data->soc == SOC_ARCH_EXYNOS) {
> -		con |= pdata->noise_cancel_mode << EXYNOS_TMU_TRIP_MODE_SHIFT;
> -		con |= (EXYNOS_MUX_ADDR_VALUE << EXYNOS_MUX_ADDR_SHIFT);
> -	}
> -
> -	if (on) {
> -		con |= EXYNOS_TMU_CORE_ON;
> -		interrupt_en = pdata->trigger_level3_en << 12 |
> -			pdata->trigger_level2_en << 8 |
> -			pdata->trigger_level1_en << 4 |
> -			pdata->trigger_level0_en;
> -		if (pdata->threshold_falling)
> -			interrupt_en |= interrupt_en << 16;
> -	} else {
> -		con |= EXYNOS_TMU_CORE_OFF;
> -		interrupt_en = 0; /* Disable all interrupts */
> -	}
> -	writel(interrupt_en, data->base + EXYNOS_TMU_REG_INTEN);
> -	writel(con, data->base + EXYNOS_TMU_REG_CONTROL);
> -
> -	clk_disable(data->clk);
> -	mutex_unlock(&data->lock);
> -}
> -
> -static int exynos_tmu_read(struct exynos_tmu_data *data)
> -{
> -	u8 temp_code;
> -	int temp;
> -
> -	mutex_lock(&data->lock);
> -	clk_enable(data->clk);
> -
> -	temp_code = readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP);
> -	temp = code_to_temp(data, temp_code);
> -
> -	clk_disable(data->clk);
> -	mutex_unlock(&data->lock);
> -
> -	return temp;
> -}
> -
> -#ifdef CONFIG_THERMAL_EMULATION
> -static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp)
> -{
> -	struct exynos_tmu_data *data = drv_data;
> -	unsigned int reg;
> -	int ret = -EINVAL;
> -
> -	if (data->soc == SOC_ARCH_EXYNOS4210)
> -		goto out;
> -
> -	if (temp && temp < MCELSIUS)
> -		goto out;
> -
> -	mutex_lock(&data->lock);
> -	clk_enable(data->clk);
> -
> -	reg = readl(data->base + EXYNOS_EMUL_CON);
> -
> -	if (temp) {
> -		temp /= MCELSIUS;
> -
> -		reg = (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT) |
> -			(temp_to_code(data, temp)
> -			 << EXYNOS_EMUL_DATA_SHIFT) | EXYNOS_EMUL_ENABLE;
> -	} else {
> -		reg &= ~EXYNOS_EMUL_ENABLE;
> -	}
> -
> -	writel(reg, data->base + EXYNOS_EMUL_CON);
> -
> -	clk_disable(data->clk);
> -	mutex_unlock(&data->lock);
> -	return 0;
> -out:
> -	return ret;
> -}
> -#else
> -static int exynos_tmu_set_emulation(void *drv_data,	unsigned long temp)
> -	{ return -EINVAL; }
> -#endif/*CONFIG_THERMAL_EMULATION*/
> -
> -static struct thermal_sensor_conf exynos_sensor_conf = {
> -	.name			= "exynos-therm",
> -	.read_temperature	= (int (*)(void *))exynos_tmu_read,
> -	.write_emul_temp	= exynos_tmu_set_emulation,
> -};
> -
> -static void exynos_tmu_work(struct work_struct *work)
> -{
> -	struct exynos_tmu_data *data = container_of(work,
> -			struct exynos_tmu_data, irq_work);
> -
> -	exynos_report_trigger(&exynos_sensor_conf);
> -	mutex_lock(&data->lock);
> -	clk_enable(data->clk);
> -	if (data->soc == SOC_ARCH_EXYNOS)
> -		writel(EXYNOS_TMU_CLEAR_RISE_INT |
> -				EXYNOS_TMU_CLEAR_FALL_INT,
> -				data->base + EXYNOS_TMU_REG_INTCLEAR);
> -	else
> -		writel(EXYNOS4210_TMU_INTCLEAR_VAL,
> -				data->base + EXYNOS_TMU_REG_INTCLEAR);
> -	clk_disable(data->clk);
> -	mutex_unlock(&data->lock);
> -
> -	enable_irq(data->irq);
> -}
> -
> -static irqreturn_t exynos_tmu_irq(int irq, void *id)
> -{
> -	struct exynos_tmu_data *data = id;
> -
> -	disable_irq_nosync(irq);
> -	schedule_work(&data->irq_work);
> -
> -	return IRQ_HANDLED;
> -}
> -
> -#if defined(CONFIG_CPU_EXYNOS4210)
> -static struct exynos_tmu_platform_data const exynos4210_default_tmu_data = {
> -	.threshold = 80,
> -	.trigger_levels[0] = 5,
> -	.trigger_levels[1] = 20,
> -	.trigger_levels[2] = 30,
> -	.trigger_level0_en = 1,
> -	.trigger_level1_en = 1,
> -	.trigger_level2_en = 1,
> -	.trigger_level3_en = 0,
> -	.gain = 15,
> -	.reference_voltage = 7,
> -	.cal_type = TYPE_ONE_POINT_TRIMMING,
> -	.freq_tab[0] = {
> -		.freq_clip_max = 800 * 1000,
> -		.temp_level = 85,
> -	},
> -	.freq_tab[1] = {
> -		.freq_clip_max = 200 * 1000,
> -		.temp_level = 100,
> -	},
> -	.freq_tab_count = 2,
> -	.type = SOC_ARCH_EXYNOS4210,
> -};
> -#define EXYNOS4210_TMU_DRV_DATA (&exynos4210_default_tmu_data)
> -#else
> -#define EXYNOS4210_TMU_DRV_DATA (NULL)
> -#endif
> -
> -#if defined(CONFIG_SOC_EXYNOS5250) || defined(CONFIG_SOC_EXYNOS4412)
> -static struct exynos_tmu_platform_data const exynos_default_tmu_data = {
> -	.threshold_falling = 10,
> -	.trigger_levels[0] = 85,
> -	.trigger_levels[1] = 103,
> -	.trigger_levels[2] = 110,
> -	.trigger_level0_en = 1,
> -	.trigger_level1_en = 1,
> -	.trigger_level2_en = 1,
> -	.trigger_level3_en = 0,
> -	.gain = 8,
> -	.reference_voltage = 16,
> -	.noise_cancel_mode = 4,
> -	.cal_type = TYPE_ONE_POINT_TRIMMING,
> -	.efuse_value = 55,
> -	.freq_tab[0] = {
> -		.freq_clip_max = 800 * 1000,
> -		.temp_level = 85,
> -	},
> -	.freq_tab[1] = {
> -		.freq_clip_max = 200 * 1000,
> -		.temp_level = 103,
> -	},
> -	.freq_tab_count = 2,
> -	.type = SOC_ARCH_EXYNOS,
> -};
> -#define EXYNOS_TMU_DRV_DATA (&exynos_default_tmu_data)
> -#else
> -#define EXYNOS_TMU_DRV_DATA (NULL)
> -#endif
> -
> -#ifdef CONFIG_OF
> -static const struct of_device_id exynos_tmu_match[] = {
> -	{
> -		.compatible = "samsung,exynos4210-tmu",
> -		.data = (void *)EXYNOS4210_TMU_DRV_DATA,
> -	},
> -	{
> -		.compatible = "samsung,exynos5250-tmu",
> -		.data = (void *)EXYNOS_TMU_DRV_DATA,
> -	},
> -	{},
> -};
> -MODULE_DEVICE_TABLE(of, exynos_tmu_match);
> -#endif
> -
> -static struct platform_device_id exynos_tmu_driver_ids[] = {
> -	{
> -		.name		= "exynos4210-tmu",
> -		.driver_data    = (kernel_ulong_t)EXYNOS4210_TMU_DRV_DATA,
> -	},
> -	{
> -		.name		= "exynos5250-tmu",
> -		.driver_data    = (kernel_ulong_t)EXYNOS_TMU_DRV_DATA,
> -	},
> -	{ },
> -};
> -MODULE_DEVICE_TABLE(platform, exynos_tmu_driver_ids);
> -
> -static inline struct  exynos_tmu_platform_data *exynos_get_driver_data(
> -			struct platform_device *pdev)
> -{
> -#ifdef CONFIG_OF
> -	if (pdev->dev.of_node) {
> -		const struct of_device_id *match;
> -		match = of_match_node(exynos_tmu_match, pdev->dev.of_node);
> -		if (!match)
> -			return NULL;
> -		return (struct exynos_tmu_platform_data *) match->data;
> -	}
> -#endif
> -	return (struct exynos_tmu_platform_data *)
> -			platform_get_device_id(pdev)->driver_data;
> -}
> -
> -static int exynos_tmu_probe(struct platform_device *pdev)
> -{
> -	struct exynos_tmu_data *data;
> -	struct exynos_tmu_platform_data *pdata = pdev->dev.platform_data;
> -	int ret, i;
> -
> -	if (!pdata)
> -		pdata = exynos_get_driver_data(pdev);
> -
> -	if (!pdata) {
> -		dev_err(&pdev->dev, "No platform init data supplied.\n");
> -		return -ENODEV;
> -	}
> -	data = devm_kzalloc(&pdev->dev, sizeof(struct exynos_tmu_data),
> -					GFP_KERNEL);
> -	if (!data) {
> -		dev_err(&pdev->dev, "Failed to allocate driver structure\n");
> -		return -ENOMEM;
> -	}
> -
> -	data->irq = platform_get_irq(pdev, 0);
> -	if (data->irq < 0) {
> -		dev_err(&pdev->dev, "Failed to get platform irq\n");
> -		return data->irq;
> -	}
> -
> -	INIT_WORK(&data->irq_work, exynos_tmu_work);
> -
> -	data->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> -	if (!data->mem) {
> -		dev_err(&pdev->dev, "Failed to get platform resource\n");
> -		return -ENOENT;
> -	}
> -
> -	data->base = devm_ioremap_resource(&pdev->dev, data->mem);
> -	if (IS_ERR(data->base))
> -		return PTR_ERR(data->base);
> -
> -	ret = devm_request_irq(&pdev->dev, data->irq, exynos_tmu_irq,
> -		IRQF_TRIGGER_RISING, "exynos-tmu", data);
> -	if (ret) {
> -		dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq);
> -		return ret;
> -	}
> -
> -	data->clk = clk_get(NULL, "tmu_apbif");
> -	if (IS_ERR(data->clk)) {
> -		dev_err(&pdev->dev, "Failed to get clock\n");
> -		return  PTR_ERR(data->clk);
> -	}
> -
> -	if (pdata->type == SOC_ARCH_EXYNOS ||
> -				pdata->type == SOC_ARCH_EXYNOS4210)
> -		data->soc = pdata->type;
> -	else {
> -		ret = -EINVAL;
> -		dev_err(&pdev->dev, "Platform not supported\n");
> -		goto err_clk;
> -	}
> -
> -	data->pdata = pdata;
> -	platform_set_drvdata(pdev, data);
> -	mutex_init(&data->lock);
> -
> -	ret = exynos_tmu_initialize(pdev);
> -	if (ret) {
> -		dev_err(&pdev->dev, "Failed to initialize TMU\n");
> -		goto err_clk;
> -	}
> -
> -	exynos_tmu_control(pdev, true);
> -
> -	/* Register the sensor with thermal management interface */
> -	(&exynos_sensor_conf)->driver_data = data;
> -	exynos_sensor_conf.trip_data.trip_count = pdata->trigger_level0_en +
> -			pdata->trigger_level1_en + pdata->trigger_level2_en +
> -			pdata->trigger_level3_en;
> -
> -	for (i = 0; i < exynos_sensor_conf.trip_data.trip_count; i++)
> -		exynos_sensor_conf.trip_data.trip_val[i] =
> -			pdata->threshold + pdata->trigger_levels[i];
> -
> -	exynos_sensor_conf.trip_data.trigger_falling = pdata->threshold_falling;
> -
> -	exynos_sensor_conf.cooling_data.freq_clip_count =
> -						pdata->freq_tab_count;
> -	for (i = 0; i < pdata->freq_tab_count; i++) {
> -		exynos_sensor_conf.cooling_data.freq_data[i].freq_clip_max =
> -					pdata->freq_tab[i].freq_clip_max;
> -		exynos_sensor_conf.cooling_data.freq_data[i].temp_level =
> -					pdata->freq_tab[i].temp_level;
> -	}
> -
> -	ret = exynos_register_thermal(&exynos_sensor_conf);
> -	if (ret) {
> -		dev_err(&pdev->dev, "Failed to register thermal interface\n");
> -		goto err_clk;
> -	}
> -
> -	return 0;
> -err_clk:
> -	platform_set_drvdata(pdev, NULL);
> -	clk_put(data->clk);
> -	return ret;
> -}
> -
> -static int exynos_tmu_remove(struct platform_device *pdev)
> -{
> -	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
> -
> -	exynos_tmu_control(pdev, false);
> -
> -	exynos_unregister_thermal(&exynos_sensor_conf);
> -
> -	clk_put(data->clk);
> -
> -	platform_set_drvdata(pdev, NULL);
> -
> -	return 0;
> -}
> -
> -#ifdef CONFIG_PM_SLEEP
> -static int exynos_tmu_suspend(struct device *dev)
> -{
> -	exynos_tmu_control(to_platform_device(dev), false);
> -
> -	return 0;
> -}
> -
> -static int exynos_tmu_resume(struct device *dev)
> -{
> -	struct platform_device *pdev = to_platform_device(dev);
> -
> -	exynos_tmu_initialize(pdev);
> -	exynos_tmu_control(pdev, true);
> -
> -	return 0;
> -}
> -
> -static SIMPLE_DEV_PM_OPS(exynos_tmu_pm,
> -			 exynos_tmu_suspend, exynos_tmu_resume);
> -#define EXYNOS_TMU_PM	(&exynos_tmu_pm)
> -#else
> -#define EXYNOS_TMU_PM	NULL
> -#endif
> -
> -static struct platform_driver exynos_tmu_driver = {
> -	.driver = {
> -		.name   = "exynos-tmu",
> -		.owner  = THIS_MODULE,
> -		.pm     = EXYNOS_TMU_PM,
> -		.of_match_table = of_match_ptr(exynos_tmu_match),
> -	},
> -	.probe = exynos_tmu_probe,
> -	.remove	= exynos_tmu_remove,
> -	.id_table = exynos_tmu_driver_ids,
> -};
> -
> -module_platform_driver(exynos_tmu_driver);
> -
> -MODULE_DESCRIPTION("EXYNOS TMU Driver");
> -MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
> -MODULE_LICENSE("GPL");
> -MODULE_ALIAS("platform:exynos-tmu");
> diff --git a/drivers/thermal/samsung/Kconfig b/drivers/thermal/samsung/Kconfig
> new file mode 100644
> index 0000000..5737b85
> --- /dev/null
> +++ b/drivers/thermal/samsung/Kconfig
> @@ -0,0 +1,11 @@
> +
> +config EXYNOS_THERMAL
> +	tristate "Temperature sensor on Samsung EXYNOS"
> +	depends on (ARCH_EXYNOS4 || ARCH_EXYNOS5)
> +	depends on CPU_THERMAL
> +	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.
> +

No need to have this ending blank line

> diff --git a/drivers/thermal/samsung/Makefile b/drivers/thermal/samsung/Makefile
> new file mode 100644
> index 0000000..fa55df5
> --- /dev/null
> +++ b/drivers/thermal/samsung/Makefile
> @@ -0,0 +1,5 @@
> +#
> +# Samsung thermal specific Makefile
> +#
> +obj-$(CONFIG_EXYNOS_THERMAL)	+= exynos_thermal.o
> +

No need to have an ending blank line

> diff --git a/drivers/thermal/samsung/exynos_thermal.c b/drivers/thermal/samsung/exynos_thermal.c
> new file mode 100644
> index 0000000..dc9b91b
> --- /dev/null
> +++ b/drivers/thermal/samsung/exynos_thermal.c
> @@ -0,0 +1,1093 @@
> +/*
> + * exynos_thermal.c - Samsung EXYNOS TMU (Thermal Management Unit)
> + *
> + *  Copyright (C) 2011 Samsung Electronics
> + *  Donggeun Kim <dg77.kim@samsung.com>
> + *  Amit Daniel Kachhap <amit.kachhap@linaro.org>
> + *
> + * 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/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/of.h>
> +
> +#include <plat/cpu.h>
> +
> +/* Exynos generic registers */
> +#define EXYNOS_TMU_REG_TRIMINFO		0x0
> +#define EXYNOS_TMU_REG_CONTROL		0x20
> +#define EXYNOS_TMU_REG_STATUS		0x28
> +#define EXYNOS_TMU_REG_CURRENT_TEMP	0x40
> +#define EXYNOS_TMU_REG_INTEN		0x70
> +#define EXYNOS_TMU_REG_INTSTAT		0x74
> +#define EXYNOS_TMU_REG_INTCLEAR		0x78
> +
> +#define EXYNOS_TMU_TRIM_TEMP_MASK	0xff
> +#define EXYNOS_TMU_GAIN_SHIFT		8
> +#define EXYNOS_TMU_REF_VOLTAGE_SHIFT	24
> +#define EXYNOS_TMU_CORE_ON		3
> +#define EXYNOS_TMU_CORE_OFF		2
> +#define EXYNOS_TMU_DEF_CODE_TO_TEMP_OFFSET	50
> +
> +/* Exynos4210 specific registers */
> +#define EXYNOS4210_TMU_REG_THRESHOLD_TEMP	0x44
> +#define EXYNOS4210_TMU_REG_TRIG_LEVEL0	0x50
> +#define EXYNOS4210_TMU_REG_TRIG_LEVEL1	0x54
> +#define EXYNOS4210_TMU_REG_TRIG_LEVEL2	0x58
> +#define EXYNOS4210_TMU_REG_TRIG_LEVEL3	0x5C
> +#define EXYNOS4210_TMU_REG_PAST_TEMP0	0x60
> +#define EXYNOS4210_TMU_REG_PAST_TEMP1	0x64
> +#define EXYNOS4210_TMU_REG_PAST_TEMP2	0x68
> +#define EXYNOS4210_TMU_REG_PAST_TEMP3	0x6C
> +
> +#define EXYNOS4210_TMU_TRIG_LEVEL0_MASK	0x1
> +#define EXYNOS4210_TMU_TRIG_LEVEL1_MASK	0x10
> +#define EXYNOS4210_TMU_TRIG_LEVEL2_MASK	0x100
> +#define EXYNOS4210_TMU_TRIG_LEVEL3_MASK	0x1000
> +#define EXYNOS4210_TMU_INTCLEAR_VAL	0x1111
> +
> +/* Exynos5250 and Exynos4412 specific registers */
> +#define EXYNOS_TMU_TRIMINFO_CON	0x14
> +#define EXYNOS_THD_TEMP_RISE		0x50
> +#define EXYNOS_THD_TEMP_FALL		0x54
> +#define EXYNOS_EMUL_CON		0x80
> +
> +#define EXYNOS_TRIMINFO_RELOAD		0x1
> +#define EXYNOS_TMU_CLEAR_RISE_INT	0x111
> +#define EXYNOS_TMU_CLEAR_FALL_INT	(0x111 << 12)
> +#define EXYNOS_MUX_ADDR_VALUE		6
> +#define EXYNOS_MUX_ADDR_SHIFT		20
> +#define EXYNOS_TMU_TRIP_MODE_SHIFT	13
> +
> +#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
> +#define EXYNOS_EMUL_DATA_SHIFT	8
> +#define EXYNOS_EMUL_DATA_MASK	0xFF
> +#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;
> +	void __iomem *base;
> +	int irq;
> +	enum soc_type soc;
> +	struct work_struct irq_work;
> +	struct mutex lock;
> +	struct clk *clk;
> +	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 *driver_data;
> +	void *pzone_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 void exynos_unregister_thermal(struct thermal_sensor_conf *sensor_conf);
> +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)
> +{
> +	struct exynos_thermal_zone *th_zone = thermal->devdata;
> +	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)
> +{
> +	struct exynos_thermal_zone *th_zone = thermal->devdata;
> +	if (!th_zone) {
> +		pr_notice("thermal zone not registered\n");
> +		return 0;
> +	}
> +
> +	mutex_lock(&thermal->lock);
> +
> +	if (mode == THERMAL_DEVICE_ENABLED &&
> +		!th_zone->sensor_conf->trip_data.trigger_falling)
> +		thermal->polling_delay = IDLE_INTERVAL;
> +	else
> +		thermal->polling_delay = 0;
> +
> +	mutex_unlock(&thermal->lock);
> +
> +	th_zone->mode = mode;
> +	thermal_zone_device_update(thermal);
> +	pr_info("thermal polling set for duration=%d msec\n",
> +				thermal->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)
> +{
> +	struct exynos_thermal_zone *th_zone = thermal->devdata;
> +
> +	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;
> +}
> +
> +static int exynos_get_frequency_level(unsigned int cpu, unsigned int freq)
> +{
> +	int i = 0, ret = -EINVAL;
> +	struct cpufreq_frequency_table *table = NULL;
> +#ifdef CONFIG_CPU_FREQ
> +	table = cpufreq_frequency_get_table(cpu);
> +#endif
> +	if (!table)
> +		return ret;
> +
> +	while (table[i].frequency != CPUFREQ_TABLE_END) {
> +		if (table[i].frequency == CPUFREQ_ENTRY_INVALID)
> +			continue;
> +		if (table[i].frequency == freq)
> +			return i;
> +		i++;
> +	}
> +	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 exynos_thermal_zone *th_zone = thermal->devdata;
> +	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 = exynos_get_frequency_level(0, clip_data->freq_clip_max);
> +		if (level < 0)
> +			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 exynos_thermal_zone *th_zone = thermal->devdata;
> +	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)
> +{
> +	struct exynos_thermal_zone *th_zone = thermal->devdata;
> +	void *data;
> +
> +	if (!th_zone->sensor_conf) {
> +		pr_info("Temperature sensor not initialised\n");
> +		return -EINVAL;
> +	}
> +	data = th_zone->sensor_conf->driver_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;
> +	struct exynos_thermal_zone *th_zone = thermal->devdata;
> +
> +	if (!th_zone->sensor_conf) {
> +		pr_info("Temperature sensor not initialised\n");
> +		return -EINVAL;
> +	}
> +	data = th_zone->sensor_conf->driver_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 = 0;
> +	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 ret;
> +}
> +/* 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(struct thermal_sensor_conf *conf)
> +{
> +	unsigned int i;
> +	char data[10];
> +	char *envp[] = { data, NULL };
> +	struct exynos_thermal_zone *th_zone = conf->pzone_data;
> +
> +	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;
> +	struct exynos_thermal_zone *th_zone;
> +
> +	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, th_zone, &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;
> +	sensor_conf->pzone_data = th_zone;
> +
> +	pr_info("Exynos: Kernel Thermal management registered\n");
> +
> +	return 0;
> +
> +err_unregister:
> +	exynos_unregister_thermal(sensor_conf);
> +	return ret;
> +}
> +
> +/* Un-Register with the in-kernel thermal management */
> +static void exynos_unregister_thermal(struct thermal_sensor_conf *sensor_conf)
> +{
> +	int i;
> +	struct exynos_thermal_zone *th_zone = sensor_conf->pzone_data;
> +
> +	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.
> + */
> +static int temp_to_code(struct exynos_tmu_data *data, u8 temp)
> +{
> +	struct exynos_tmu_platform_data *pdata = data->pdata;
> +	int temp_code;
> +
> +	if (data->soc == SOC_ARCH_EXYNOS4210)
> +		/* temp should range between 25 and 125 */
> +		if (temp < 25 || temp > 125) {
> +			temp_code = -EINVAL;
> +			goto out;
> +		}
> +
> +	switch (pdata->cal_type) {
> +	case TYPE_TWO_POINT_TRIMMING:
> +		temp_code = (temp - 25) *
> +		    (data->temp_error2 - data->temp_error1) /
> +		    (85 - 25) + data->temp_error1;
> +		break;
> +	case TYPE_ONE_POINT_TRIMMING:
> +		temp_code = temp + data->temp_error1 - 25;
> +		break;
> +	default:
> +		temp_code = temp + EXYNOS_TMU_DEF_CODE_TO_TEMP_OFFSET;
> +		break;
> +	}
> +out:
> +	return temp_code;
> +}
> +
> +/*
> + * Calculate a temperature value from a temperature code.
> + * The unit of the temperature is degree Celsius.
> + */
> +static int code_to_temp(struct exynos_tmu_data *data, u8 temp_code)
> +{
> +	struct exynos_tmu_platform_data *pdata = data->pdata;
> +	int temp;
> +
> +	if (data->soc == SOC_ARCH_EXYNOS4210)
> +		/* temp_code should range between 75 and 175 */
> +		if (temp_code < 75 || temp_code > 175) {
> +			temp = -ENODATA;
> +			goto out;
> +		}
> +
> +	switch (pdata->cal_type) {
> +	case TYPE_TWO_POINT_TRIMMING:
> +		temp = (temp_code - data->temp_error1) * (85 - 25) /
> +		    (data->temp_error2 - data->temp_error1) + 25;
> +		break;
> +	case TYPE_ONE_POINT_TRIMMING:
> +		temp = temp_code - data->temp_error1 + 25;
> +		break;
> +	default:
> +		temp = temp_code - EXYNOS_TMU_DEF_CODE_TO_TEMP_OFFSET;
> +		break;
> +	}
> +out:
> +	return temp;
> +}
> +
> +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;
> +	unsigned int status, trim_info;
> +	unsigned int rising_threshold = 0, falling_threshold = 0;
> +	int ret = 0, threshold_code, i, trigger_levs = 0;
> +
> +	mutex_lock(&data->lock);
> +	clk_enable(data->clk);
> +
> +	status = readb(data->base + EXYNOS_TMU_REG_STATUS);
> +	if (!status) {
> +		ret = -EBUSY;
> +		goto out;
> +	}
> +
> +	if (data->soc == SOC_ARCH_EXYNOS) {
> +		__raw_writel(EXYNOS_TRIMINFO_RELOAD,
> +				data->base + EXYNOS_TMU_TRIMINFO_CON);
> +	}
> +	/* Save trimming info in order to perform calibration */
> +	trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO);
> +	data->temp_error1 = trim_info & EXYNOS_TMU_TRIM_TEMP_MASK;
> +	data->temp_error2 = ((trim_info >> 8) & EXYNOS_TMU_TRIM_TEMP_MASK);
> +
> +	if ((EFUSE_MIN_VALUE > data->temp_error1) ||
> +			(data->temp_error1 > EFUSE_MAX_VALUE) ||
> +			(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])
> +			trigger_levs++;
> +
> +	if (data->soc == SOC_ARCH_EXYNOS4210) {
> +		/* Write temperature code for threshold */
> +		threshold_code = temp_to_code(data, pdata->threshold);
> +		if (threshold_code < 0) {
> +			ret = threshold_code;
> +			goto out;
> +		}
> +		writeb(threshold_code,
> +			data->base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP);
> +		for (i = 0; i < trigger_levs; i++)
> +			writeb(pdata->trigger_levels[i],
> +			data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL0 + i * 4);
> +
> +		writel(EXYNOS4210_TMU_INTCLEAR_VAL,
> +			data->base + EXYNOS_TMU_REG_INTCLEAR);
> +	} else if (data->soc == SOC_ARCH_EXYNOS) {
> +		/* Write temperature code for rising and falling threshold */
> +		for (i = 0; i < trigger_levs; i++) {
> +			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;
> +			if (pdata->threshold_falling) {
> +				threshold_code = temp_to_code(data,
> +						pdata->trigger_levels[i] -
> +						pdata->threshold_falling);
> +				if (threshold_code > 0)
> +					falling_threshold |=
> +						threshold_code << 8 * i;
> +			}
> +		}
> +
> +		writel(rising_threshold,
> +				data->base + EXYNOS_THD_TEMP_RISE);
> +		writel(falling_threshold,
> +				data->base + EXYNOS_THD_TEMP_FALL);
> +
> +		writel(EXYNOS_TMU_CLEAR_RISE_INT | EXYNOS_TMU_CLEAR_FALL_INT,
> +				data->base + EXYNOS_TMU_REG_INTCLEAR);
> +	}
> +out:
> +	clk_disable(data->clk);
> +	mutex_unlock(&data->lock);
> +
> +	return ret;
> +}
> +
> +static void exynos_tmu_control(struct platform_device *pdev, bool on)
> +{
> +	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
> +	struct exynos_tmu_platform_data *pdata = data->pdata;
> +	unsigned int con, interrupt_en;
> +
> +	mutex_lock(&data->lock);
> +	clk_enable(data->clk);
> +
> +	con = pdata->reference_voltage << EXYNOS_TMU_REF_VOLTAGE_SHIFT |
> +		pdata->gain << EXYNOS_TMU_GAIN_SHIFT;
> +
> +	if (data->soc == SOC_ARCH_EXYNOS) {
> +		con |= pdata->noise_cancel_mode << EXYNOS_TMU_TRIP_MODE_SHIFT;
> +		con |= (EXYNOS_MUX_ADDR_VALUE << EXYNOS_MUX_ADDR_SHIFT);
> +	}
> +
> +	if (on) {
> +		con |= EXYNOS_TMU_CORE_ON;
> +		interrupt_en = pdata->trigger_level3_en << 12 |
> +			pdata->trigger_level2_en << 8 |
> +			pdata->trigger_level1_en << 4 |
> +			pdata->trigger_level0_en;
> +		if (pdata->threshold_falling)
> +			interrupt_en |= interrupt_en << 16;
> +	} else {
> +		con |= EXYNOS_TMU_CORE_OFF;
> +		interrupt_en = 0; /* Disable all interrupts */
> +	}
> +	writel(interrupt_en, data->base + EXYNOS_TMU_REG_INTEN);
> +	writel(con, data->base + EXYNOS_TMU_REG_CONTROL);
> +
> +	clk_disable(data->clk);
> +	mutex_unlock(&data->lock);
> +}
> +
> +static int exynos_tmu_read(struct exynos_tmu_data *data)
> +{
> +	u8 temp_code;
> +	int temp;
> +
> +	mutex_lock(&data->lock);
> +	clk_enable(data->clk);
> +
> +	temp_code = readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP);
> +	temp = code_to_temp(data, temp_code);
> +
> +	clk_disable(data->clk);
> +	mutex_unlock(&data->lock);
> +
> +	return temp;
> +}
> +
> +#ifdef CONFIG_THERMAL_EMULATION
> +static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp)
> +{
> +	struct exynos_tmu_data *data = drv_data;
> +	unsigned int reg;
> +	int ret = -EINVAL;
> +
> +	if (data->soc == SOC_ARCH_EXYNOS4210)
> +		goto out;
> +
> +	if (temp && temp < MCELSIUS)
> +		goto out;
> +
> +	mutex_lock(&data->lock);
> +	clk_enable(data->clk);
> +
> +	reg = readl(data->base + EXYNOS_EMUL_CON);
> +
> +	if (temp) {
> +		temp /= MCELSIUS;
> +
> +		reg = (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT) |
> +			(temp_to_code(data, temp)
> +			 << EXYNOS_EMUL_DATA_SHIFT) | EXYNOS_EMUL_ENABLE;
> +	} else {
> +		reg &= ~EXYNOS_EMUL_ENABLE;
> +	}
> +
> +	writel(reg, data->base + EXYNOS_EMUL_CON);
> +
> +	clk_disable(data->clk);
> +	mutex_unlock(&data->lock);
> +	return 0;
> +out:
> +	return ret;
> +}
> +#else
> +static int exynos_tmu_set_emulation(void *drv_data,	unsigned long temp)
> +	{ return -EINVAL; }
> +#endif/*CONFIG_THERMAL_EMULATION*/
> +
> +static struct thermal_sensor_conf exynos_sensor_conf = {
> +	.name			= "exynos-therm",
> +	.read_temperature	= (int (*)(void *))exynos_tmu_read,
> +	.write_emul_temp	= exynos_tmu_set_emulation,
> +};
> +
> +static void exynos_tmu_work(struct work_struct *work)
> +{
> +	struct exynos_tmu_data *data = container_of(work,
> +			struct exynos_tmu_data, irq_work);
> +
> +	exynos_report_trigger(&exynos_sensor_conf);
> +	mutex_lock(&data->lock);
> +	clk_enable(data->clk);
> +	if (data->soc == SOC_ARCH_EXYNOS)
> +		writel(EXYNOS_TMU_CLEAR_RISE_INT |
> +				EXYNOS_TMU_CLEAR_FALL_INT,
> +				data->base + EXYNOS_TMU_REG_INTCLEAR);
> +	else
> +		writel(EXYNOS4210_TMU_INTCLEAR_VAL,
> +				data->base + EXYNOS_TMU_REG_INTCLEAR);
> +	clk_disable(data->clk);
> +	mutex_unlock(&data->lock);
> +
> +	enable_irq(data->irq);
> +}
> +
> +static irqreturn_t exynos_tmu_irq(int irq, void *id)
> +{
> +	struct exynos_tmu_data *data = id;
> +
> +	disable_irq_nosync(irq);
> +	schedule_work(&data->irq_work);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +#if defined(CONFIG_CPU_EXYNOS4210)
> +static struct exynos_tmu_platform_data const exynos4210_default_tmu_data = {
> +	.threshold = 80,
> +	.trigger_levels[0] = 5,
> +	.trigger_levels[1] = 20,
> +	.trigger_levels[2] = 30,
> +	.trigger_level0_en = 1,
> +	.trigger_level1_en = 1,
> +	.trigger_level2_en = 1,
> +	.trigger_level3_en = 0,
> +	.gain = 15,
> +	.reference_voltage = 7,
> +	.cal_type = TYPE_ONE_POINT_TRIMMING,
> +	.freq_tab[0] = {
> +		.freq_clip_max = 800 * 1000,
> +		.temp_level = 85,
> +	},
> +	.freq_tab[1] = {
> +		.freq_clip_max = 200 * 1000,
> +		.temp_level = 100,
> +	},
> +	.freq_tab_count = 2,
> +	.type = SOC_ARCH_EXYNOS4210,
> +};
> +#define EXYNOS4210_TMU_DRV_DATA (&exynos4210_default_tmu_data)
> +#else
> +#define EXYNOS4210_TMU_DRV_DATA (NULL)
> +#endif
> +
> +#if defined(CONFIG_SOC_EXYNOS5250) || defined(CONFIG_SOC_EXYNOS4412)
> +static struct exynos_tmu_platform_data const exynos_default_tmu_data = {
> +	.threshold_falling = 10,
> +	.trigger_levels[0] = 85,
> +	.trigger_levels[1] = 103,
> +	.trigger_levels[2] = 110,
> +	.trigger_level0_en = 1,
> +	.trigger_level1_en = 1,
> +	.trigger_level2_en = 1,
> +	.trigger_level3_en = 0,
> +	.gain = 8,
> +	.reference_voltage = 16,
> +	.noise_cancel_mode = 4,
> +	.cal_type = TYPE_ONE_POINT_TRIMMING,
> +	.efuse_value = 55,
> +	.freq_tab[0] = {
> +		.freq_clip_max = 800 * 1000,
> +		.temp_level = 85,
> +	},
> +	.freq_tab[1] = {
> +		.freq_clip_max = 200 * 1000,
> +		.temp_level = 103,
> +	},
> +	.freq_tab_count = 2,
> +	.type = SOC_ARCH_EXYNOS,
> +};
> +#define EXYNOS_TMU_DRV_DATA (&exynos_default_tmu_data)
> +#else
> +#define EXYNOS_TMU_DRV_DATA (NULL)
> +#endif
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id exynos_tmu_match[] = {
> +	{
> +		.compatible = "samsung,exynos4210-tmu",
> +		.data = (void *)EXYNOS4210_TMU_DRV_DATA,
> +	},
> +	{
> +		.compatible = "samsung,exynos5250-tmu",
> +		.data = (void *)EXYNOS_TMU_DRV_DATA,
> +	},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, exynos_tmu_match);
> +#endif
> +
> +static struct platform_device_id exynos_tmu_driver_ids[] = {
> +	{
> +		.name		= "exynos4210-tmu",
> +		.driver_data    = (kernel_ulong_t)EXYNOS4210_TMU_DRV_DATA,
> +	},
> +	{
> +		.name		= "exynos5250-tmu",
> +		.driver_data    = (kernel_ulong_t)EXYNOS_TMU_DRV_DATA,
> +	},
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(platform, exynos_tmu_driver_ids);
> +
> +static inline struct  exynos_tmu_platform_data *exynos_get_driver_data(
> +			struct platform_device *pdev)
> +{
> +#ifdef CONFIG_OF
> +	if (pdev->dev.of_node) {
> +		const struct of_device_id *match;
> +		match = of_match_node(exynos_tmu_match, pdev->dev.of_node);
> +		if (!match)
> +			return NULL;
> +		return (struct exynos_tmu_platform_data *) match->data;
> +	}
> +#endif
> +	return (struct exynos_tmu_platform_data *)
> +			platform_get_device_id(pdev)->driver_data;
> +}
> +
> +static int exynos_tmu_probe(struct platform_device *pdev)
> +{
> +	struct exynos_tmu_data *data;
> +	struct exynos_tmu_platform_data *pdata = pdev->dev.platform_data;
> +	int ret, i;
> +
> +	if (!pdata)
> +		pdata = exynos_get_driver_data(pdev);
> +
> +	if (!pdata) {
> +		dev_err(&pdev->dev, "No platform init data supplied.\n");
> +		return -ENODEV;
> +	}
> +	data = devm_kzalloc(&pdev->dev, sizeof(struct exynos_tmu_data),
> +					GFP_KERNEL);
> +	if (!data) {
> +		dev_err(&pdev->dev, "Failed to allocate driver structure\n");
> +		return -ENOMEM;
> +	}
> +
> +	data->irq = platform_get_irq(pdev, 0);
> +	if (data->irq < 0) {
> +		dev_err(&pdev->dev, "Failed to get platform irq\n");
> +		return data->irq;
> +	}
> +
> +	INIT_WORK(&data->irq_work, exynos_tmu_work);
> +
> +	data->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!data->mem) {
> +		dev_err(&pdev->dev, "Failed to get platform resource\n");
> +		return -ENOENT;
> +	}
> +
> +	data->base = devm_ioremap_resource(&pdev->dev, data->mem);
> +	if (IS_ERR(data->base))
> +		return PTR_ERR(data->base);
> +
> +	ret = devm_request_irq(&pdev->dev, data->irq, exynos_tmu_irq,
> +		IRQF_TRIGGER_RISING, "exynos-tmu", data);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq);
> +		return ret;
> +	}
> +
> +	data->clk = clk_get(NULL, "tmu_apbif");
> +	if (IS_ERR(data->clk)) {
> +		dev_err(&pdev->dev, "Failed to get clock\n");
> +		return  PTR_ERR(data->clk);
> +	}
> +
> +	if (pdata->type == SOC_ARCH_EXYNOS ||
> +				pdata->type == SOC_ARCH_EXYNOS4210)
> +		data->soc = pdata->type;
> +	else {
> +		ret = -EINVAL;
> +		dev_err(&pdev->dev, "Platform not supported\n");
> +		goto err_clk;
> +	}
> +
> +	data->pdata = pdata;
> +	platform_set_drvdata(pdev, data);
> +	mutex_init(&data->lock);
> +
> +	ret = exynos_tmu_initialize(pdev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Failed to initialize TMU\n");
> +		goto err_clk;
> +	}
> +
> +	exynos_tmu_control(pdev, true);
> +
> +	/* Register the sensor with thermal management interface */
> +	(&exynos_sensor_conf)->driver_data = data;
> +	exynos_sensor_conf.trip_data.trip_count = pdata->trigger_level0_en +
> +			pdata->trigger_level1_en + pdata->trigger_level2_en +
> +			pdata->trigger_level3_en;
> +
> +	for (i = 0; i < exynos_sensor_conf.trip_data.trip_count; i++)
> +		exynos_sensor_conf.trip_data.trip_val[i] =
> +			pdata->threshold + pdata->trigger_levels[i];
> +
> +	exynos_sensor_conf.trip_data.trigger_falling = pdata->threshold_falling;
> +
> +	exynos_sensor_conf.cooling_data.freq_clip_count =
> +						pdata->freq_tab_count;
> +	for (i = 0; i < pdata->freq_tab_count; i++) {
> +		exynos_sensor_conf.cooling_data.freq_data[i].freq_clip_max =
> +					pdata->freq_tab[i].freq_clip_max;
> +		exynos_sensor_conf.cooling_data.freq_data[i].temp_level =
> +					pdata->freq_tab[i].temp_level;
> +	}
> +
> +	ret = exynos_register_thermal(&exynos_sensor_conf);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Failed to register thermal interface\n");
> +		goto err_clk;
> +	}
> +
> +	return 0;
> +err_clk:
> +	platform_set_drvdata(pdev, NULL);
> +	clk_put(data->clk);
> +	return ret;
> +}
> +
> +static int exynos_tmu_remove(struct platform_device *pdev)
> +{
> +	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
> +
> +	exynos_tmu_control(pdev, false);
> +
> +	exynos_unregister_thermal(&exynos_sensor_conf);
> +
> +	clk_put(data->clk);
> +
> +	platform_set_drvdata(pdev, NULL);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int exynos_tmu_suspend(struct device *dev)
> +{
> +	exynos_tmu_control(to_platform_device(dev), false);
> +
> +	return 0;
> +}
> +
> +static int exynos_tmu_resume(struct device *dev)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +
> +	exynos_tmu_initialize(pdev);
> +	exynos_tmu_control(pdev, true);
> +
> +	return 0;
> +}
> +
> +static SIMPLE_DEV_PM_OPS(exynos_tmu_pm,
> +			 exynos_tmu_suspend, exynos_tmu_resume);
> +#define EXYNOS_TMU_PM	(&exynos_tmu_pm)
> +#else
> +#define EXYNOS_TMU_PM	NULL
> +#endif
> +
> +static struct platform_driver exynos_tmu_driver = {
> +	.driver = {
> +		.name   = "exynos-tmu",
> +		.owner  = THIS_MODULE,
> +		.pm     = EXYNOS_TMU_PM,
> +		.of_match_table = of_match_ptr(exynos_tmu_match),
> +	},
> +	.probe = exynos_tmu_probe,
> +	.remove	= exynos_tmu_remove,
> +	.id_table = exynos_tmu_driver_ids,
> +};
> +
> +module_platform_driver(exynos_tmu_driver);
> +
> +MODULE_DESCRIPTION("EXYNOS TMU Driver");
> +MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:exynos-tmu");
>

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

Patch

diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index da4c19e..9b2372a 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -109,14 +109,6 @@  config KIRKWOOD_THERMAL
 	  Support for the Kirkwood thermal sensor driver into the Linux thermal
 	  framework. Only kirkwood 88F6282 and 88F6283 have this sensor.
 
-config EXYNOS_THERMAL
-	tristate "Temperature sensor on Samsung EXYNOS"
-	depends on (ARCH_EXYNOS4 || ARCH_EXYNOS5)
-	depends on CPU_THERMAL
-	help
-	  If you say yes here you get support for TMU (Thermal Management
-	  Unit) on SAMSUNG EXYNOS series of SoC.
-
 config DOVE_THERMAL
 	tristate "Temperature sensor on Marvell Dove SoCs"
 	depends on ARCH_DOVE
@@ -156,4 +148,9 @@  config INTEL_POWERCLAMP
 	  enforce idle time which results in more package C-state residency. The
 	  user interface is exposed via generic thermal framework.
 
+menu "Exynos thermal drivers"
+depends on PLAT_SAMSUNG
+source "drivers/thermal/samsung/Kconfig"
+endmenu
+
 endif
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index d3a2b38..584862c 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -16,7 +16,7 @@  obj-$(CONFIG_CPU_THERMAL)	+= cpu_cooling.o
 obj-$(CONFIG_SPEAR_THERMAL)	+= spear_thermal.o
 obj-$(CONFIG_RCAR_THERMAL)	+= rcar_thermal.o
 obj-$(CONFIG_KIRKWOOD_THERMAL)  += kirkwood_thermal.o
-obj-$(CONFIG_EXYNOS_THERMAL)	+= exynos_thermal.o
+obj-y				+= samsung/
 obj-$(CONFIG_DOVE_THERMAL)  	+= dove_thermal.o
 obj-$(CONFIG_DB8500_THERMAL)	+= db8500_thermal.o
 obj-$(CONFIG_DB8500_CPUFREQ_COOLING)	+= db8500_cpufreq_cooling.o
diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c
deleted file mode 100644
index dc9b91b..0000000
--- a/drivers/thermal/exynos_thermal.c
+++ /dev/null
@@ -1,1093 +0,0 @@ 
-/*
- * exynos_thermal.c - Samsung EXYNOS TMU (Thermal Management Unit)
- *
- *  Copyright (C) 2011 Samsung Electronics
- *  Donggeun Kim <dg77.kim@samsung.com>
- *  Amit Daniel Kachhap <amit.kachhap@linaro.org>
- *
- * 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/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/of.h>
-
-#include <plat/cpu.h>
-
-/* Exynos generic registers */
-#define EXYNOS_TMU_REG_TRIMINFO		0x0
-#define EXYNOS_TMU_REG_CONTROL		0x20
-#define EXYNOS_TMU_REG_STATUS		0x28
-#define EXYNOS_TMU_REG_CURRENT_TEMP	0x40
-#define EXYNOS_TMU_REG_INTEN		0x70
-#define EXYNOS_TMU_REG_INTSTAT		0x74
-#define EXYNOS_TMU_REG_INTCLEAR		0x78
-
-#define EXYNOS_TMU_TRIM_TEMP_MASK	0xff
-#define EXYNOS_TMU_GAIN_SHIFT		8
-#define EXYNOS_TMU_REF_VOLTAGE_SHIFT	24
-#define EXYNOS_TMU_CORE_ON		3
-#define EXYNOS_TMU_CORE_OFF		2
-#define EXYNOS_TMU_DEF_CODE_TO_TEMP_OFFSET	50
-
-/* Exynos4210 specific registers */
-#define EXYNOS4210_TMU_REG_THRESHOLD_TEMP	0x44
-#define EXYNOS4210_TMU_REG_TRIG_LEVEL0	0x50
-#define EXYNOS4210_TMU_REG_TRIG_LEVEL1	0x54
-#define EXYNOS4210_TMU_REG_TRIG_LEVEL2	0x58
-#define EXYNOS4210_TMU_REG_TRIG_LEVEL3	0x5C
-#define EXYNOS4210_TMU_REG_PAST_TEMP0	0x60
-#define EXYNOS4210_TMU_REG_PAST_TEMP1	0x64
-#define EXYNOS4210_TMU_REG_PAST_TEMP2	0x68
-#define EXYNOS4210_TMU_REG_PAST_TEMP3	0x6C
-
-#define EXYNOS4210_TMU_TRIG_LEVEL0_MASK	0x1
-#define EXYNOS4210_TMU_TRIG_LEVEL1_MASK	0x10
-#define EXYNOS4210_TMU_TRIG_LEVEL2_MASK	0x100
-#define EXYNOS4210_TMU_TRIG_LEVEL3_MASK	0x1000
-#define EXYNOS4210_TMU_INTCLEAR_VAL	0x1111
-
-/* Exynos5250 and Exynos4412 specific registers */
-#define EXYNOS_TMU_TRIMINFO_CON	0x14
-#define EXYNOS_THD_TEMP_RISE		0x50
-#define EXYNOS_THD_TEMP_FALL		0x54
-#define EXYNOS_EMUL_CON		0x80
-
-#define EXYNOS_TRIMINFO_RELOAD		0x1
-#define EXYNOS_TMU_CLEAR_RISE_INT	0x111
-#define EXYNOS_TMU_CLEAR_FALL_INT	(0x111 << 12)
-#define EXYNOS_MUX_ADDR_VALUE		6
-#define EXYNOS_MUX_ADDR_SHIFT		20
-#define EXYNOS_TMU_TRIP_MODE_SHIFT	13
-
-#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
-#define EXYNOS_EMUL_DATA_SHIFT	8
-#define EXYNOS_EMUL_DATA_MASK	0xFF
-#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;
-	void __iomem *base;
-	int irq;
-	enum soc_type soc;
-	struct work_struct irq_work;
-	struct mutex lock;
-	struct clk *clk;
-	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 *driver_data;
-	void *pzone_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 void exynos_unregister_thermal(struct thermal_sensor_conf *sensor_conf);
-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)
-{
-	struct exynos_thermal_zone *th_zone = thermal->devdata;
-	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)
-{
-	struct exynos_thermal_zone *th_zone = thermal->devdata;
-	if (!th_zone) {
-		pr_notice("thermal zone not registered\n");
-		return 0;
-	}
-
-	mutex_lock(&thermal->lock);
-
-	if (mode == THERMAL_DEVICE_ENABLED &&
-		!th_zone->sensor_conf->trip_data.trigger_falling)
-		thermal->polling_delay = IDLE_INTERVAL;
-	else
-		thermal->polling_delay = 0;
-
-	mutex_unlock(&thermal->lock);
-
-	th_zone->mode = mode;
-	thermal_zone_device_update(thermal);
-	pr_info("thermal polling set for duration=%d msec\n",
-				thermal->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)
-{
-	struct exynos_thermal_zone *th_zone = thermal->devdata;
-
-	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;
-}
-
-static int exynos_get_frequency_level(unsigned int cpu, unsigned int freq)
-{
-	int i = 0, ret = -EINVAL;
-	struct cpufreq_frequency_table *table = NULL;
-#ifdef CONFIG_CPU_FREQ
-	table = cpufreq_frequency_get_table(cpu);
-#endif
-	if (!table)
-		return ret;
-
-	while (table[i].frequency != CPUFREQ_TABLE_END) {
-		if (table[i].frequency == CPUFREQ_ENTRY_INVALID)
-			continue;
-		if (table[i].frequency == freq)
-			return i;
-		i++;
-	}
-	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 exynos_thermal_zone *th_zone = thermal->devdata;
-	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 = exynos_get_frequency_level(0, clip_data->freq_clip_max);
-		if (level < 0)
-			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 exynos_thermal_zone *th_zone = thermal->devdata;
-	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)
-{
-	struct exynos_thermal_zone *th_zone = thermal->devdata;
-	void *data;
-
-	if (!th_zone->sensor_conf) {
-		pr_info("Temperature sensor not initialised\n");
-		return -EINVAL;
-	}
-	data = th_zone->sensor_conf->driver_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;
-	struct exynos_thermal_zone *th_zone = thermal->devdata;
-
-	if (!th_zone->sensor_conf) {
-		pr_info("Temperature sensor not initialised\n");
-		return -EINVAL;
-	}
-	data = th_zone->sensor_conf->driver_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 = 0;
-	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 ret;
-}
-/* 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(struct thermal_sensor_conf *conf)
-{
-	unsigned int i;
-	char data[10];
-	char *envp[] = { data, NULL };
-	struct exynos_thermal_zone *th_zone = conf->pzone_data;
-
-	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;
-	struct exynos_thermal_zone *th_zone;
-
-	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, th_zone, &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;
-	sensor_conf->pzone_data = th_zone;
-
-	pr_info("Exynos: Kernel Thermal management registered\n");
-
-	return 0;
-
-err_unregister:
-	exynos_unregister_thermal(sensor_conf);
-	return ret;
-}
-
-/* Un-Register with the in-kernel thermal management */
-static void exynos_unregister_thermal(struct thermal_sensor_conf *sensor_conf)
-{
-	int i;
-	struct exynos_thermal_zone *th_zone = sensor_conf->pzone_data;
-
-	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.
- */
-static int temp_to_code(struct exynos_tmu_data *data, u8 temp)
-{
-	struct exynos_tmu_platform_data *pdata = data->pdata;
-	int temp_code;
-
-	if (data->soc == SOC_ARCH_EXYNOS4210)
-		/* temp should range between 25 and 125 */
-		if (temp < 25 || temp > 125) {
-			temp_code = -EINVAL;
-			goto out;
-		}
-
-	switch (pdata->cal_type) {
-	case TYPE_TWO_POINT_TRIMMING:
-		temp_code = (temp - 25) *
-		    (data->temp_error2 - data->temp_error1) /
-		    (85 - 25) + data->temp_error1;
-		break;
-	case TYPE_ONE_POINT_TRIMMING:
-		temp_code = temp + data->temp_error1 - 25;
-		break;
-	default:
-		temp_code = temp + EXYNOS_TMU_DEF_CODE_TO_TEMP_OFFSET;
-		break;
-	}
-out:
-	return temp_code;
-}
-
-/*
- * Calculate a temperature value from a temperature code.
- * The unit of the temperature is degree Celsius.
- */
-static int code_to_temp(struct exynos_tmu_data *data, u8 temp_code)
-{
-	struct exynos_tmu_platform_data *pdata = data->pdata;
-	int temp;
-
-	if (data->soc == SOC_ARCH_EXYNOS4210)
-		/* temp_code should range between 75 and 175 */
-		if (temp_code < 75 || temp_code > 175) {
-			temp = -ENODATA;
-			goto out;
-		}
-
-	switch (pdata->cal_type) {
-	case TYPE_TWO_POINT_TRIMMING:
-		temp = (temp_code - data->temp_error1) * (85 - 25) /
-		    (data->temp_error2 - data->temp_error1) + 25;
-		break;
-	case TYPE_ONE_POINT_TRIMMING:
-		temp = temp_code - data->temp_error1 + 25;
-		break;
-	default:
-		temp = temp_code - EXYNOS_TMU_DEF_CODE_TO_TEMP_OFFSET;
-		break;
-	}
-out:
-	return temp;
-}
-
-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;
-	unsigned int status, trim_info;
-	unsigned int rising_threshold = 0, falling_threshold = 0;
-	int ret = 0, threshold_code, i, trigger_levs = 0;
-
-	mutex_lock(&data->lock);
-	clk_enable(data->clk);
-
-	status = readb(data->base + EXYNOS_TMU_REG_STATUS);
-	if (!status) {
-		ret = -EBUSY;
-		goto out;
-	}
-
-	if (data->soc == SOC_ARCH_EXYNOS) {
-		__raw_writel(EXYNOS_TRIMINFO_RELOAD,
-				data->base + EXYNOS_TMU_TRIMINFO_CON);
-	}
-	/* Save trimming info in order to perform calibration */
-	trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO);
-	data->temp_error1 = trim_info & EXYNOS_TMU_TRIM_TEMP_MASK;
-	data->temp_error2 = ((trim_info >> 8) & EXYNOS_TMU_TRIM_TEMP_MASK);
-
-	if ((EFUSE_MIN_VALUE > data->temp_error1) ||
-			(data->temp_error1 > EFUSE_MAX_VALUE) ||
-			(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])
-			trigger_levs++;
-
-	if (data->soc == SOC_ARCH_EXYNOS4210) {
-		/* Write temperature code for threshold */
-		threshold_code = temp_to_code(data, pdata->threshold);
-		if (threshold_code < 0) {
-			ret = threshold_code;
-			goto out;
-		}
-		writeb(threshold_code,
-			data->base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP);
-		for (i = 0; i < trigger_levs; i++)
-			writeb(pdata->trigger_levels[i],
-			data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL0 + i * 4);
-
-		writel(EXYNOS4210_TMU_INTCLEAR_VAL,
-			data->base + EXYNOS_TMU_REG_INTCLEAR);
-	} else if (data->soc == SOC_ARCH_EXYNOS) {
-		/* Write temperature code for rising and falling threshold */
-		for (i = 0; i < trigger_levs; i++) {
-			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;
-			if (pdata->threshold_falling) {
-				threshold_code = temp_to_code(data,
-						pdata->trigger_levels[i] -
-						pdata->threshold_falling);
-				if (threshold_code > 0)
-					falling_threshold |=
-						threshold_code << 8 * i;
-			}
-		}
-
-		writel(rising_threshold,
-				data->base + EXYNOS_THD_TEMP_RISE);
-		writel(falling_threshold,
-				data->base + EXYNOS_THD_TEMP_FALL);
-
-		writel(EXYNOS_TMU_CLEAR_RISE_INT | EXYNOS_TMU_CLEAR_FALL_INT,
-				data->base + EXYNOS_TMU_REG_INTCLEAR);
-	}
-out:
-	clk_disable(data->clk);
-	mutex_unlock(&data->lock);
-
-	return ret;
-}
-
-static void exynos_tmu_control(struct platform_device *pdev, bool on)
-{
-	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
-	struct exynos_tmu_platform_data *pdata = data->pdata;
-	unsigned int con, interrupt_en;
-
-	mutex_lock(&data->lock);
-	clk_enable(data->clk);
-
-	con = pdata->reference_voltage << EXYNOS_TMU_REF_VOLTAGE_SHIFT |
-		pdata->gain << EXYNOS_TMU_GAIN_SHIFT;
-
-	if (data->soc == SOC_ARCH_EXYNOS) {
-		con |= pdata->noise_cancel_mode << EXYNOS_TMU_TRIP_MODE_SHIFT;
-		con |= (EXYNOS_MUX_ADDR_VALUE << EXYNOS_MUX_ADDR_SHIFT);
-	}
-
-	if (on) {
-		con |= EXYNOS_TMU_CORE_ON;
-		interrupt_en = pdata->trigger_level3_en << 12 |
-			pdata->trigger_level2_en << 8 |
-			pdata->trigger_level1_en << 4 |
-			pdata->trigger_level0_en;
-		if (pdata->threshold_falling)
-			interrupt_en |= interrupt_en << 16;
-	} else {
-		con |= EXYNOS_TMU_CORE_OFF;
-		interrupt_en = 0; /* Disable all interrupts */
-	}
-	writel(interrupt_en, data->base + EXYNOS_TMU_REG_INTEN);
-	writel(con, data->base + EXYNOS_TMU_REG_CONTROL);
-
-	clk_disable(data->clk);
-	mutex_unlock(&data->lock);
-}
-
-static int exynos_tmu_read(struct exynos_tmu_data *data)
-{
-	u8 temp_code;
-	int temp;
-
-	mutex_lock(&data->lock);
-	clk_enable(data->clk);
-
-	temp_code = readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP);
-	temp = code_to_temp(data, temp_code);
-
-	clk_disable(data->clk);
-	mutex_unlock(&data->lock);
-
-	return temp;
-}
-
-#ifdef CONFIG_THERMAL_EMULATION
-static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp)
-{
-	struct exynos_tmu_data *data = drv_data;
-	unsigned int reg;
-	int ret = -EINVAL;
-
-	if (data->soc == SOC_ARCH_EXYNOS4210)
-		goto out;
-
-	if (temp && temp < MCELSIUS)
-		goto out;
-
-	mutex_lock(&data->lock);
-	clk_enable(data->clk);
-
-	reg = readl(data->base + EXYNOS_EMUL_CON);
-
-	if (temp) {
-		temp /= MCELSIUS;
-
-		reg = (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT) |
-			(temp_to_code(data, temp)
-			 << EXYNOS_EMUL_DATA_SHIFT) | EXYNOS_EMUL_ENABLE;
-	} else {
-		reg &= ~EXYNOS_EMUL_ENABLE;
-	}
-
-	writel(reg, data->base + EXYNOS_EMUL_CON);
-
-	clk_disable(data->clk);
-	mutex_unlock(&data->lock);
-	return 0;
-out:
-	return ret;
-}
-#else
-static int exynos_tmu_set_emulation(void *drv_data,	unsigned long temp)
-	{ return -EINVAL; }
-#endif/*CONFIG_THERMAL_EMULATION*/
-
-static struct thermal_sensor_conf exynos_sensor_conf = {
-	.name			= "exynos-therm",
-	.read_temperature	= (int (*)(void *))exynos_tmu_read,
-	.write_emul_temp	= exynos_tmu_set_emulation,
-};
-
-static void exynos_tmu_work(struct work_struct *work)
-{
-	struct exynos_tmu_data *data = container_of(work,
-			struct exynos_tmu_data, irq_work);
-
-	exynos_report_trigger(&exynos_sensor_conf);
-	mutex_lock(&data->lock);
-	clk_enable(data->clk);
-	if (data->soc == SOC_ARCH_EXYNOS)
-		writel(EXYNOS_TMU_CLEAR_RISE_INT |
-				EXYNOS_TMU_CLEAR_FALL_INT,
-				data->base + EXYNOS_TMU_REG_INTCLEAR);
-	else
-		writel(EXYNOS4210_TMU_INTCLEAR_VAL,
-				data->base + EXYNOS_TMU_REG_INTCLEAR);
-	clk_disable(data->clk);
-	mutex_unlock(&data->lock);
-
-	enable_irq(data->irq);
-}
-
-static irqreturn_t exynos_tmu_irq(int irq, void *id)
-{
-	struct exynos_tmu_data *data = id;
-
-	disable_irq_nosync(irq);
-	schedule_work(&data->irq_work);
-
-	return IRQ_HANDLED;
-}
-
-#if defined(CONFIG_CPU_EXYNOS4210)
-static struct exynos_tmu_platform_data const exynos4210_default_tmu_data = {
-	.threshold = 80,
-	.trigger_levels[0] = 5,
-	.trigger_levels[1] = 20,
-	.trigger_levels[2] = 30,
-	.trigger_level0_en = 1,
-	.trigger_level1_en = 1,
-	.trigger_level2_en = 1,
-	.trigger_level3_en = 0,
-	.gain = 15,
-	.reference_voltage = 7,
-	.cal_type = TYPE_ONE_POINT_TRIMMING,
-	.freq_tab[0] = {
-		.freq_clip_max = 800 * 1000,
-		.temp_level = 85,
-	},
-	.freq_tab[1] = {
-		.freq_clip_max = 200 * 1000,
-		.temp_level = 100,
-	},
-	.freq_tab_count = 2,
-	.type = SOC_ARCH_EXYNOS4210,
-};
-#define EXYNOS4210_TMU_DRV_DATA (&exynos4210_default_tmu_data)
-#else
-#define EXYNOS4210_TMU_DRV_DATA (NULL)
-#endif
-
-#if defined(CONFIG_SOC_EXYNOS5250) || defined(CONFIG_SOC_EXYNOS4412)
-static struct exynos_tmu_platform_data const exynos_default_tmu_data = {
-	.threshold_falling = 10,
-	.trigger_levels[0] = 85,
-	.trigger_levels[1] = 103,
-	.trigger_levels[2] = 110,
-	.trigger_level0_en = 1,
-	.trigger_level1_en = 1,
-	.trigger_level2_en = 1,
-	.trigger_level3_en = 0,
-	.gain = 8,
-	.reference_voltage = 16,
-	.noise_cancel_mode = 4,
-	.cal_type = TYPE_ONE_POINT_TRIMMING,
-	.efuse_value = 55,
-	.freq_tab[0] = {
-		.freq_clip_max = 800 * 1000,
-		.temp_level = 85,
-	},
-	.freq_tab[1] = {
-		.freq_clip_max = 200 * 1000,
-		.temp_level = 103,
-	},
-	.freq_tab_count = 2,
-	.type = SOC_ARCH_EXYNOS,
-};
-#define EXYNOS_TMU_DRV_DATA (&exynos_default_tmu_data)
-#else
-#define EXYNOS_TMU_DRV_DATA (NULL)
-#endif
-
-#ifdef CONFIG_OF
-static const struct of_device_id exynos_tmu_match[] = {
-	{
-		.compatible = "samsung,exynos4210-tmu",
-		.data = (void *)EXYNOS4210_TMU_DRV_DATA,
-	},
-	{
-		.compatible = "samsung,exynos5250-tmu",
-		.data = (void *)EXYNOS_TMU_DRV_DATA,
-	},
-	{},
-};
-MODULE_DEVICE_TABLE(of, exynos_tmu_match);
-#endif
-
-static struct platform_device_id exynos_tmu_driver_ids[] = {
-	{
-		.name		= "exynos4210-tmu",
-		.driver_data    = (kernel_ulong_t)EXYNOS4210_TMU_DRV_DATA,
-	},
-	{
-		.name		= "exynos5250-tmu",
-		.driver_data    = (kernel_ulong_t)EXYNOS_TMU_DRV_DATA,
-	},
-	{ },
-};
-MODULE_DEVICE_TABLE(platform, exynos_tmu_driver_ids);
-
-static inline struct  exynos_tmu_platform_data *exynos_get_driver_data(
-			struct platform_device *pdev)
-{
-#ifdef CONFIG_OF
-	if (pdev->dev.of_node) {
-		const struct of_device_id *match;
-		match = of_match_node(exynos_tmu_match, pdev->dev.of_node);
-		if (!match)
-			return NULL;
-		return (struct exynos_tmu_platform_data *) match->data;
-	}
-#endif
-	return (struct exynos_tmu_platform_data *)
-			platform_get_device_id(pdev)->driver_data;
-}
-
-static int exynos_tmu_probe(struct platform_device *pdev)
-{
-	struct exynos_tmu_data *data;
-	struct exynos_tmu_platform_data *pdata = pdev->dev.platform_data;
-	int ret, i;
-
-	if (!pdata)
-		pdata = exynos_get_driver_data(pdev);
-
-	if (!pdata) {
-		dev_err(&pdev->dev, "No platform init data supplied.\n");
-		return -ENODEV;
-	}
-	data = devm_kzalloc(&pdev->dev, sizeof(struct exynos_tmu_data),
-					GFP_KERNEL);
-	if (!data) {
-		dev_err(&pdev->dev, "Failed to allocate driver structure\n");
-		return -ENOMEM;
-	}
-
-	data->irq = platform_get_irq(pdev, 0);
-	if (data->irq < 0) {
-		dev_err(&pdev->dev, "Failed to get platform irq\n");
-		return data->irq;
-	}
-
-	INIT_WORK(&data->irq_work, exynos_tmu_work);
-
-	data->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!data->mem) {
-		dev_err(&pdev->dev, "Failed to get platform resource\n");
-		return -ENOENT;
-	}
-
-	data->base = devm_ioremap_resource(&pdev->dev, data->mem);
-	if (IS_ERR(data->base))
-		return PTR_ERR(data->base);
-
-	ret = devm_request_irq(&pdev->dev, data->irq, exynos_tmu_irq,
-		IRQF_TRIGGER_RISING, "exynos-tmu", data);
-	if (ret) {
-		dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq);
-		return ret;
-	}
-
-	data->clk = clk_get(NULL, "tmu_apbif");
-	if (IS_ERR(data->clk)) {
-		dev_err(&pdev->dev, "Failed to get clock\n");
-		return  PTR_ERR(data->clk);
-	}
-
-	if (pdata->type == SOC_ARCH_EXYNOS ||
-				pdata->type == SOC_ARCH_EXYNOS4210)
-		data->soc = pdata->type;
-	else {
-		ret = -EINVAL;
-		dev_err(&pdev->dev, "Platform not supported\n");
-		goto err_clk;
-	}
-
-	data->pdata = pdata;
-	platform_set_drvdata(pdev, data);
-	mutex_init(&data->lock);
-
-	ret = exynos_tmu_initialize(pdev);
-	if (ret) {
-		dev_err(&pdev->dev, "Failed to initialize TMU\n");
-		goto err_clk;
-	}
-
-	exynos_tmu_control(pdev, true);
-
-	/* Register the sensor with thermal management interface */
-	(&exynos_sensor_conf)->driver_data = data;
-	exynos_sensor_conf.trip_data.trip_count = pdata->trigger_level0_en +
-			pdata->trigger_level1_en + pdata->trigger_level2_en +
-			pdata->trigger_level3_en;
-
-	for (i = 0; i < exynos_sensor_conf.trip_data.trip_count; i++)
-		exynos_sensor_conf.trip_data.trip_val[i] =
-			pdata->threshold + pdata->trigger_levels[i];
-
-	exynos_sensor_conf.trip_data.trigger_falling = pdata->threshold_falling;
-
-	exynos_sensor_conf.cooling_data.freq_clip_count =
-						pdata->freq_tab_count;
-	for (i = 0; i < pdata->freq_tab_count; i++) {
-		exynos_sensor_conf.cooling_data.freq_data[i].freq_clip_max =
-					pdata->freq_tab[i].freq_clip_max;
-		exynos_sensor_conf.cooling_data.freq_data[i].temp_level =
-					pdata->freq_tab[i].temp_level;
-	}
-
-	ret = exynos_register_thermal(&exynos_sensor_conf);
-	if (ret) {
-		dev_err(&pdev->dev, "Failed to register thermal interface\n");
-		goto err_clk;
-	}
-
-	return 0;
-err_clk:
-	platform_set_drvdata(pdev, NULL);
-	clk_put(data->clk);
-	return ret;
-}
-
-static int exynos_tmu_remove(struct platform_device *pdev)
-{
-	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
-
-	exynos_tmu_control(pdev, false);
-
-	exynos_unregister_thermal(&exynos_sensor_conf);
-
-	clk_put(data->clk);
-
-	platform_set_drvdata(pdev, NULL);
-
-	return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int exynos_tmu_suspend(struct device *dev)
-{
-	exynos_tmu_control(to_platform_device(dev), false);
-
-	return 0;
-}
-
-static int exynos_tmu_resume(struct device *dev)
-{
-	struct platform_device *pdev = to_platform_device(dev);
-
-	exynos_tmu_initialize(pdev);
-	exynos_tmu_control(pdev, true);
-
-	return 0;
-}
-
-static SIMPLE_DEV_PM_OPS(exynos_tmu_pm,
-			 exynos_tmu_suspend, exynos_tmu_resume);
-#define EXYNOS_TMU_PM	(&exynos_tmu_pm)
-#else
-#define EXYNOS_TMU_PM	NULL
-#endif
-
-static struct platform_driver exynos_tmu_driver = {
-	.driver = {
-		.name   = "exynos-tmu",
-		.owner  = THIS_MODULE,
-		.pm     = EXYNOS_TMU_PM,
-		.of_match_table = of_match_ptr(exynos_tmu_match),
-	},
-	.probe = exynos_tmu_probe,
-	.remove	= exynos_tmu_remove,
-	.id_table = exynos_tmu_driver_ids,
-};
-
-module_platform_driver(exynos_tmu_driver);
-
-MODULE_DESCRIPTION("EXYNOS TMU Driver");
-MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:exynos-tmu");
diff --git a/drivers/thermal/samsung/Kconfig b/drivers/thermal/samsung/Kconfig
new file mode 100644
index 0000000..5737b85
--- /dev/null
+++ b/drivers/thermal/samsung/Kconfig
@@ -0,0 +1,11 @@ 
+
+config EXYNOS_THERMAL
+	tristate "Temperature sensor on Samsung EXYNOS"
+	depends on (ARCH_EXYNOS4 || ARCH_EXYNOS5)
+	depends on CPU_THERMAL
+	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.
+
diff --git a/drivers/thermal/samsung/Makefile b/drivers/thermal/samsung/Makefile
new file mode 100644
index 0000000..fa55df5
--- /dev/null
+++ b/drivers/thermal/samsung/Makefile
@@ -0,0 +1,5 @@ 
+#
+# Samsung thermal specific Makefile
+#
+obj-$(CONFIG_EXYNOS_THERMAL)	+= exynos_thermal.o
+
diff --git a/drivers/thermal/samsung/exynos_thermal.c b/drivers/thermal/samsung/exynos_thermal.c
new file mode 100644
index 0000000..dc9b91b
--- /dev/null
+++ b/drivers/thermal/samsung/exynos_thermal.c
@@ -0,0 +1,1093 @@ 
+/*
+ * exynos_thermal.c - Samsung EXYNOS TMU (Thermal Management Unit)
+ *
+ *  Copyright (C) 2011 Samsung Electronics
+ *  Donggeun Kim <dg77.kim@samsung.com>
+ *  Amit Daniel Kachhap <amit.kachhap@linaro.org>
+ *
+ * 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/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/of.h>
+
+#include <plat/cpu.h>
+
+/* Exynos generic registers */
+#define EXYNOS_TMU_REG_TRIMINFO		0x0
+#define EXYNOS_TMU_REG_CONTROL		0x20
+#define EXYNOS_TMU_REG_STATUS		0x28
+#define EXYNOS_TMU_REG_CURRENT_TEMP	0x40
+#define EXYNOS_TMU_REG_INTEN		0x70
+#define EXYNOS_TMU_REG_INTSTAT		0x74
+#define EXYNOS_TMU_REG_INTCLEAR		0x78
+
+#define EXYNOS_TMU_TRIM_TEMP_MASK	0xff
+#define EXYNOS_TMU_GAIN_SHIFT		8
+#define EXYNOS_TMU_REF_VOLTAGE_SHIFT	24
+#define EXYNOS_TMU_CORE_ON		3
+#define EXYNOS_TMU_CORE_OFF		2
+#define EXYNOS_TMU_DEF_CODE_TO_TEMP_OFFSET	50
+
+/* Exynos4210 specific registers */
+#define EXYNOS4210_TMU_REG_THRESHOLD_TEMP	0x44
+#define EXYNOS4210_TMU_REG_TRIG_LEVEL0	0x50
+#define EXYNOS4210_TMU_REG_TRIG_LEVEL1	0x54
+#define EXYNOS4210_TMU_REG_TRIG_LEVEL2	0x58
+#define EXYNOS4210_TMU_REG_TRIG_LEVEL3	0x5C
+#define EXYNOS4210_TMU_REG_PAST_TEMP0	0x60
+#define EXYNOS4210_TMU_REG_PAST_TEMP1	0x64
+#define EXYNOS4210_TMU_REG_PAST_TEMP2	0x68
+#define EXYNOS4210_TMU_REG_PAST_TEMP3	0x6C
+
+#define EXYNOS4210_TMU_TRIG_LEVEL0_MASK	0x1
+#define EXYNOS4210_TMU_TRIG_LEVEL1_MASK	0x10
+#define EXYNOS4210_TMU_TRIG_LEVEL2_MASK	0x100
+#define EXYNOS4210_TMU_TRIG_LEVEL3_MASK	0x1000
+#define EXYNOS4210_TMU_INTCLEAR_VAL	0x1111
+
+/* Exynos5250 and Exynos4412 specific registers */
+#define EXYNOS_TMU_TRIMINFO_CON	0x14
+#define EXYNOS_THD_TEMP_RISE		0x50
+#define EXYNOS_THD_TEMP_FALL		0x54
+#define EXYNOS_EMUL_CON		0x80
+
+#define EXYNOS_TRIMINFO_RELOAD		0x1
+#define EXYNOS_TMU_CLEAR_RISE_INT	0x111
+#define EXYNOS_TMU_CLEAR_FALL_INT	(0x111 << 12)
+#define EXYNOS_MUX_ADDR_VALUE		6
+#define EXYNOS_MUX_ADDR_SHIFT		20
+#define EXYNOS_TMU_TRIP_MODE_SHIFT	13
+
+#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
+#define EXYNOS_EMUL_DATA_SHIFT	8
+#define EXYNOS_EMUL_DATA_MASK	0xFF
+#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;
+	void __iomem *base;
+	int irq;
+	enum soc_type soc;
+	struct work_struct irq_work;
+	struct mutex lock;
+	struct clk *clk;
+	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 *driver_data;
+	void *pzone_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 void exynos_unregister_thermal(struct thermal_sensor_conf *sensor_conf);
+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)
+{
+	struct exynos_thermal_zone *th_zone = thermal->devdata;
+	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)
+{
+	struct exynos_thermal_zone *th_zone = thermal->devdata;
+	if (!th_zone) {
+		pr_notice("thermal zone not registered\n");
+		return 0;
+	}
+
+	mutex_lock(&thermal->lock);
+
+	if (mode == THERMAL_DEVICE_ENABLED &&
+		!th_zone->sensor_conf->trip_data.trigger_falling)
+		thermal->polling_delay = IDLE_INTERVAL;
+	else
+		thermal->polling_delay = 0;
+
+	mutex_unlock(&thermal->lock);
+
+	th_zone->mode = mode;
+	thermal_zone_device_update(thermal);
+	pr_info("thermal polling set for duration=%d msec\n",
+				thermal->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)
+{
+	struct exynos_thermal_zone *th_zone = thermal->devdata;
+
+	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;
+}
+
+static int exynos_get_frequency_level(unsigned int cpu, unsigned int freq)
+{
+	int i = 0, ret = -EINVAL;
+	struct cpufreq_frequency_table *table = NULL;
+#ifdef CONFIG_CPU_FREQ
+	table = cpufreq_frequency_get_table(cpu);
+#endif
+	if (!table)
+		return ret;
+
+	while (table[i].frequency != CPUFREQ_TABLE_END) {
+		if (table[i].frequency == CPUFREQ_ENTRY_INVALID)
+			continue;
+		if (table[i].frequency == freq)
+			return i;
+		i++;
+	}
+	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 exynos_thermal_zone *th_zone = thermal->devdata;
+	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 = exynos_get_frequency_level(0, clip_data->freq_clip_max);
+		if (level < 0)
+			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 exynos_thermal_zone *th_zone = thermal->devdata;
+	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)
+{
+	struct exynos_thermal_zone *th_zone = thermal->devdata;
+	void *data;
+
+	if (!th_zone->sensor_conf) {
+		pr_info("Temperature sensor not initialised\n");
+		return -EINVAL;
+	}
+	data = th_zone->sensor_conf->driver_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;
+	struct exynos_thermal_zone *th_zone = thermal->devdata;
+
+	if (!th_zone->sensor_conf) {
+		pr_info("Temperature sensor not initialised\n");
+		return -EINVAL;
+	}
+	data = th_zone->sensor_conf->driver_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 = 0;
+	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 ret;
+}
+/* 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(struct thermal_sensor_conf *conf)
+{
+	unsigned int i;
+	char data[10];
+	char *envp[] = { data, NULL };
+	struct exynos_thermal_zone *th_zone = conf->pzone_data;
+
+	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;
+	struct exynos_thermal_zone *th_zone;
+
+	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, th_zone, &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;
+	sensor_conf->pzone_data = th_zone;
+
+	pr_info("Exynos: Kernel Thermal management registered\n");
+
+	return 0;
+
+err_unregister:
+	exynos_unregister_thermal(sensor_conf);
+	return ret;
+}
+
+/* Un-Register with the in-kernel thermal management */
+static void exynos_unregister_thermal(struct thermal_sensor_conf *sensor_conf)
+{
+	int i;
+	struct exynos_thermal_zone *th_zone = sensor_conf->pzone_data;
+
+	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.
+ */
+static int temp_to_code(struct exynos_tmu_data *data, u8 temp)
+{
+	struct exynos_tmu_platform_data *pdata = data->pdata;
+	int temp_code;
+
+	if (data->soc == SOC_ARCH_EXYNOS4210)
+		/* temp should range between 25 and 125 */
+		if (temp < 25 || temp > 125) {
+			temp_code = -EINVAL;
+			goto out;
+		}
+
+	switch (pdata->cal_type) {
+	case TYPE_TWO_POINT_TRIMMING:
+		temp_code = (temp - 25) *
+		    (data->temp_error2 - data->temp_error1) /
+		    (85 - 25) + data->temp_error1;
+		break;
+	case TYPE_ONE_POINT_TRIMMING:
+		temp_code = temp + data->temp_error1 - 25;
+		break;
+	default:
+		temp_code = temp + EXYNOS_TMU_DEF_CODE_TO_TEMP_OFFSET;
+		break;
+	}
+out:
+	return temp_code;
+}
+
+/*
+ * Calculate a temperature value from a temperature code.
+ * The unit of the temperature is degree Celsius.
+ */
+static int code_to_temp(struct exynos_tmu_data *data, u8 temp_code)
+{
+	struct exynos_tmu_platform_data *pdata = data->pdata;
+	int temp;
+
+	if (data->soc == SOC_ARCH_EXYNOS4210)
+		/* temp_code should range between 75 and 175 */
+		if (temp_code < 75 || temp_code > 175) {
+			temp = -ENODATA;
+			goto out;
+		}
+
+	switch (pdata->cal_type) {
+	case TYPE_TWO_POINT_TRIMMING:
+		temp = (temp_code - data->temp_error1) * (85 - 25) /
+		    (data->temp_error2 - data->temp_error1) + 25;
+		break;
+	case TYPE_ONE_POINT_TRIMMING:
+		temp = temp_code - data->temp_error1 + 25;
+		break;
+	default:
+		temp = temp_code - EXYNOS_TMU_DEF_CODE_TO_TEMP_OFFSET;
+		break;
+	}
+out:
+	return temp;
+}
+
+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;
+	unsigned int status, trim_info;
+	unsigned int rising_threshold = 0, falling_threshold = 0;
+	int ret = 0, threshold_code, i, trigger_levs = 0;
+
+	mutex_lock(&data->lock);
+	clk_enable(data->clk);
+
+	status = readb(data->base + EXYNOS_TMU_REG_STATUS);
+	if (!status) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	if (data->soc == SOC_ARCH_EXYNOS) {
+		__raw_writel(EXYNOS_TRIMINFO_RELOAD,
+				data->base + EXYNOS_TMU_TRIMINFO_CON);
+	}
+	/* Save trimming info in order to perform calibration */
+	trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO);
+	data->temp_error1 = trim_info & EXYNOS_TMU_TRIM_TEMP_MASK;
+	data->temp_error2 = ((trim_info >> 8) & EXYNOS_TMU_TRIM_TEMP_MASK);
+
+	if ((EFUSE_MIN_VALUE > data->temp_error1) ||
+			(data->temp_error1 > EFUSE_MAX_VALUE) ||
+			(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])
+			trigger_levs++;
+
+	if (data->soc == SOC_ARCH_EXYNOS4210) {
+		/* Write temperature code for threshold */
+		threshold_code = temp_to_code(data, pdata->threshold);
+		if (threshold_code < 0) {
+			ret = threshold_code;
+			goto out;
+		}
+		writeb(threshold_code,
+			data->base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP);
+		for (i = 0; i < trigger_levs; i++)
+			writeb(pdata->trigger_levels[i],
+			data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL0 + i * 4);
+
+		writel(EXYNOS4210_TMU_INTCLEAR_VAL,
+			data->base + EXYNOS_TMU_REG_INTCLEAR);
+	} else if (data->soc == SOC_ARCH_EXYNOS) {
+		/* Write temperature code for rising and falling threshold */
+		for (i = 0; i < trigger_levs; i++) {
+			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;
+			if (pdata->threshold_falling) {
+				threshold_code = temp_to_code(data,
+						pdata->trigger_levels[i] -
+						pdata->threshold_falling);
+				if (threshold_code > 0)
+					falling_threshold |=
+						threshold_code << 8 * i;
+			}
+		}
+
+		writel(rising_threshold,
+				data->base + EXYNOS_THD_TEMP_RISE);
+		writel(falling_threshold,
+				data->base + EXYNOS_THD_TEMP_FALL);
+
+		writel(EXYNOS_TMU_CLEAR_RISE_INT | EXYNOS_TMU_CLEAR_FALL_INT,
+				data->base + EXYNOS_TMU_REG_INTCLEAR);
+	}
+out:
+	clk_disable(data->clk);
+	mutex_unlock(&data->lock);
+
+	return ret;
+}
+
+static void exynos_tmu_control(struct platform_device *pdev, bool on)
+{
+	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
+	struct exynos_tmu_platform_data *pdata = data->pdata;
+	unsigned int con, interrupt_en;
+
+	mutex_lock(&data->lock);
+	clk_enable(data->clk);
+
+	con = pdata->reference_voltage << EXYNOS_TMU_REF_VOLTAGE_SHIFT |
+		pdata->gain << EXYNOS_TMU_GAIN_SHIFT;
+
+	if (data->soc == SOC_ARCH_EXYNOS) {
+		con |= pdata->noise_cancel_mode << EXYNOS_TMU_TRIP_MODE_SHIFT;
+		con |= (EXYNOS_MUX_ADDR_VALUE << EXYNOS_MUX_ADDR_SHIFT);
+	}
+
+	if (on) {
+		con |= EXYNOS_TMU_CORE_ON;
+		interrupt_en = pdata->trigger_level3_en << 12 |
+			pdata->trigger_level2_en << 8 |
+			pdata->trigger_level1_en << 4 |
+			pdata->trigger_level0_en;
+		if (pdata->threshold_falling)
+			interrupt_en |= interrupt_en << 16;
+	} else {
+		con |= EXYNOS_TMU_CORE_OFF;
+		interrupt_en = 0; /* Disable all interrupts */
+	}
+	writel(interrupt_en, data->base + EXYNOS_TMU_REG_INTEN);
+	writel(con, data->base + EXYNOS_TMU_REG_CONTROL);
+
+	clk_disable(data->clk);
+	mutex_unlock(&data->lock);
+}
+
+static int exynos_tmu_read(struct exynos_tmu_data *data)
+{
+	u8 temp_code;
+	int temp;
+
+	mutex_lock(&data->lock);
+	clk_enable(data->clk);
+
+	temp_code = readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP);
+	temp = code_to_temp(data, temp_code);
+
+	clk_disable(data->clk);
+	mutex_unlock(&data->lock);
+
+	return temp;
+}
+
+#ifdef CONFIG_THERMAL_EMULATION
+static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp)
+{
+	struct exynos_tmu_data *data = drv_data;
+	unsigned int reg;
+	int ret = -EINVAL;
+
+	if (data->soc == SOC_ARCH_EXYNOS4210)
+		goto out;
+
+	if (temp && temp < MCELSIUS)
+		goto out;
+
+	mutex_lock(&data->lock);
+	clk_enable(data->clk);
+
+	reg = readl(data->base + EXYNOS_EMUL_CON);
+
+	if (temp) {
+		temp /= MCELSIUS;
+
+		reg = (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT) |
+			(temp_to_code(data, temp)
+			 << EXYNOS_EMUL_DATA_SHIFT) | EXYNOS_EMUL_ENABLE;
+	} else {
+		reg &= ~EXYNOS_EMUL_ENABLE;
+	}
+
+	writel(reg, data->base + EXYNOS_EMUL_CON);
+
+	clk_disable(data->clk);
+	mutex_unlock(&data->lock);
+	return 0;
+out:
+	return ret;
+}
+#else
+static int exynos_tmu_set_emulation(void *drv_data,	unsigned long temp)
+	{ return -EINVAL; }
+#endif/*CONFIG_THERMAL_EMULATION*/
+
+static struct thermal_sensor_conf exynos_sensor_conf = {
+	.name			= "exynos-therm",
+	.read_temperature	= (int (*)(void *))exynos_tmu_read,
+	.write_emul_temp	= exynos_tmu_set_emulation,
+};
+
+static void exynos_tmu_work(struct work_struct *work)
+{
+	struct exynos_tmu_data *data = container_of(work,
+			struct exynos_tmu_data, irq_work);
+
+	exynos_report_trigger(&exynos_sensor_conf);
+	mutex_lock(&data->lock);
+	clk_enable(data->clk);
+	if (data->soc == SOC_ARCH_EXYNOS)
+		writel(EXYNOS_TMU_CLEAR_RISE_INT |
+				EXYNOS_TMU_CLEAR_FALL_INT,
+				data->base + EXYNOS_TMU_REG_INTCLEAR);
+	else
+		writel(EXYNOS4210_TMU_INTCLEAR_VAL,
+				data->base + EXYNOS_TMU_REG_INTCLEAR);
+	clk_disable(data->clk);
+	mutex_unlock(&data->lock);
+
+	enable_irq(data->irq);
+}
+
+static irqreturn_t exynos_tmu_irq(int irq, void *id)
+{
+	struct exynos_tmu_data *data = id;
+
+	disable_irq_nosync(irq);
+	schedule_work(&data->irq_work);
+
+	return IRQ_HANDLED;
+}
+
+#if defined(CONFIG_CPU_EXYNOS4210)
+static struct exynos_tmu_platform_data const exynos4210_default_tmu_data = {
+	.threshold = 80,
+	.trigger_levels[0] = 5,
+	.trigger_levels[1] = 20,
+	.trigger_levels[2] = 30,
+	.trigger_level0_en = 1,
+	.trigger_level1_en = 1,
+	.trigger_level2_en = 1,
+	.trigger_level3_en = 0,
+	.gain = 15,
+	.reference_voltage = 7,
+	.cal_type = TYPE_ONE_POINT_TRIMMING,
+	.freq_tab[0] = {
+		.freq_clip_max = 800 * 1000,
+		.temp_level = 85,
+	},
+	.freq_tab[1] = {
+		.freq_clip_max = 200 * 1000,
+		.temp_level = 100,
+	},
+	.freq_tab_count = 2,
+	.type = SOC_ARCH_EXYNOS4210,
+};
+#define EXYNOS4210_TMU_DRV_DATA (&exynos4210_default_tmu_data)
+#else
+#define EXYNOS4210_TMU_DRV_DATA (NULL)
+#endif
+
+#if defined(CONFIG_SOC_EXYNOS5250) || defined(CONFIG_SOC_EXYNOS4412)
+static struct exynos_tmu_platform_data const exynos_default_tmu_data = {
+	.threshold_falling = 10,
+	.trigger_levels[0] = 85,
+	.trigger_levels[1] = 103,
+	.trigger_levels[2] = 110,
+	.trigger_level0_en = 1,
+	.trigger_level1_en = 1,
+	.trigger_level2_en = 1,
+	.trigger_level3_en = 0,
+	.gain = 8,
+	.reference_voltage = 16,
+	.noise_cancel_mode = 4,
+	.cal_type = TYPE_ONE_POINT_TRIMMING,
+	.efuse_value = 55,
+	.freq_tab[0] = {
+		.freq_clip_max = 800 * 1000,
+		.temp_level = 85,
+	},
+	.freq_tab[1] = {
+		.freq_clip_max = 200 * 1000,
+		.temp_level = 103,
+	},
+	.freq_tab_count = 2,
+	.type = SOC_ARCH_EXYNOS,
+};
+#define EXYNOS_TMU_DRV_DATA (&exynos_default_tmu_data)
+#else
+#define EXYNOS_TMU_DRV_DATA (NULL)
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id exynos_tmu_match[] = {
+	{
+		.compatible = "samsung,exynos4210-tmu",
+		.data = (void *)EXYNOS4210_TMU_DRV_DATA,
+	},
+	{
+		.compatible = "samsung,exynos5250-tmu",
+		.data = (void *)EXYNOS_TMU_DRV_DATA,
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, exynos_tmu_match);
+#endif
+
+static struct platform_device_id exynos_tmu_driver_ids[] = {
+	{
+		.name		= "exynos4210-tmu",
+		.driver_data    = (kernel_ulong_t)EXYNOS4210_TMU_DRV_DATA,
+	},
+	{
+		.name		= "exynos5250-tmu",
+		.driver_data    = (kernel_ulong_t)EXYNOS_TMU_DRV_DATA,
+	},
+	{ },
+};
+MODULE_DEVICE_TABLE(platform, exynos_tmu_driver_ids);
+
+static inline struct  exynos_tmu_platform_data *exynos_get_driver_data(
+			struct platform_device *pdev)
+{
+#ifdef CONFIG_OF
+	if (pdev->dev.of_node) {
+		const struct of_device_id *match;
+		match = of_match_node(exynos_tmu_match, pdev->dev.of_node);
+		if (!match)
+			return NULL;
+		return (struct exynos_tmu_platform_data *) match->data;
+	}
+#endif
+	return (struct exynos_tmu_platform_data *)
+			platform_get_device_id(pdev)->driver_data;
+}
+
+static int exynos_tmu_probe(struct platform_device *pdev)
+{
+	struct exynos_tmu_data *data;
+	struct exynos_tmu_platform_data *pdata = pdev->dev.platform_data;
+	int ret, i;
+
+	if (!pdata)
+		pdata = exynos_get_driver_data(pdev);
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "No platform init data supplied.\n");
+		return -ENODEV;
+	}
+	data = devm_kzalloc(&pdev->dev, sizeof(struct exynos_tmu_data),
+					GFP_KERNEL);
+	if (!data) {
+		dev_err(&pdev->dev, "Failed to allocate driver structure\n");
+		return -ENOMEM;
+	}
+
+	data->irq = platform_get_irq(pdev, 0);
+	if (data->irq < 0) {
+		dev_err(&pdev->dev, "Failed to get platform irq\n");
+		return data->irq;
+	}
+
+	INIT_WORK(&data->irq_work, exynos_tmu_work);
+
+	data->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!data->mem) {
+		dev_err(&pdev->dev, "Failed to get platform resource\n");
+		return -ENOENT;
+	}
+
+	data->base = devm_ioremap_resource(&pdev->dev, data->mem);
+	if (IS_ERR(data->base))
+		return PTR_ERR(data->base);
+
+	ret = devm_request_irq(&pdev->dev, data->irq, exynos_tmu_irq,
+		IRQF_TRIGGER_RISING, "exynos-tmu", data);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq);
+		return ret;
+	}
+
+	data->clk = clk_get(NULL, "tmu_apbif");
+	if (IS_ERR(data->clk)) {
+		dev_err(&pdev->dev, "Failed to get clock\n");
+		return  PTR_ERR(data->clk);
+	}
+
+	if (pdata->type == SOC_ARCH_EXYNOS ||
+				pdata->type == SOC_ARCH_EXYNOS4210)
+		data->soc = pdata->type;
+	else {
+		ret = -EINVAL;
+		dev_err(&pdev->dev, "Platform not supported\n");
+		goto err_clk;
+	}
+
+	data->pdata = pdata;
+	platform_set_drvdata(pdev, data);
+	mutex_init(&data->lock);
+
+	ret = exynos_tmu_initialize(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to initialize TMU\n");
+		goto err_clk;
+	}
+
+	exynos_tmu_control(pdev, true);
+
+	/* Register the sensor with thermal management interface */
+	(&exynos_sensor_conf)->driver_data = data;
+	exynos_sensor_conf.trip_data.trip_count = pdata->trigger_level0_en +
+			pdata->trigger_level1_en + pdata->trigger_level2_en +
+			pdata->trigger_level3_en;
+
+	for (i = 0; i < exynos_sensor_conf.trip_data.trip_count; i++)
+		exynos_sensor_conf.trip_data.trip_val[i] =
+			pdata->threshold + pdata->trigger_levels[i];
+
+	exynos_sensor_conf.trip_data.trigger_falling = pdata->threshold_falling;
+
+	exynos_sensor_conf.cooling_data.freq_clip_count =
+						pdata->freq_tab_count;
+	for (i = 0; i < pdata->freq_tab_count; i++) {
+		exynos_sensor_conf.cooling_data.freq_data[i].freq_clip_max =
+					pdata->freq_tab[i].freq_clip_max;
+		exynos_sensor_conf.cooling_data.freq_data[i].temp_level =
+					pdata->freq_tab[i].temp_level;
+	}
+
+	ret = exynos_register_thermal(&exynos_sensor_conf);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to register thermal interface\n");
+		goto err_clk;
+	}
+
+	return 0;
+err_clk:
+	platform_set_drvdata(pdev, NULL);
+	clk_put(data->clk);
+	return ret;
+}
+
+static int exynos_tmu_remove(struct platform_device *pdev)
+{
+	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
+
+	exynos_tmu_control(pdev, false);
+
+	exynos_unregister_thermal(&exynos_sensor_conf);
+
+	clk_put(data->clk);
+
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int exynos_tmu_suspend(struct device *dev)
+{
+	exynos_tmu_control(to_platform_device(dev), false);
+
+	return 0;
+}
+
+static int exynos_tmu_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+
+	exynos_tmu_initialize(pdev);
+	exynos_tmu_control(pdev, true);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(exynos_tmu_pm,
+			 exynos_tmu_suspend, exynos_tmu_resume);
+#define EXYNOS_TMU_PM	(&exynos_tmu_pm)
+#else
+#define EXYNOS_TMU_PM	NULL
+#endif
+
+static struct platform_driver exynos_tmu_driver = {
+	.driver = {
+		.name   = "exynos-tmu",
+		.owner  = THIS_MODULE,
+		.pm     = EXYNOS_TMU_PM,
+		.of_match_table = of_match_ptr(exynos_tmu_match),
+	},
+	.probe = exynos_tmu_probe,
+	.remove	= exynos_tmu_remove,
+	.id_table = exynos_tmu_driver_ids,
+};
+
+module_platform_driver(exynos_tmu_driver);
+
+MODULE_DESCRIPTION("EXYNOS TMU Driver");
+MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:exynos-tmu");