lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Thu, 11 Apr 2013 16:30:47 -0400
From:	Eduardo Valentin <eduardo.valentin@...com>
To:	Amit Daniel Kachhap <amit.daniel@...sung.com>
CC:	<linux-pm@...r.kernel.org>,
	Thomas Abraham <thomas.abraham@...aro.org>,
	Zhang Rui <rui.zhang@...el.com>,
	<linux-samsung-soc@...r.kernel.org>,
	<linux-kernel@...r.kernel.org>, <amit.kachhap@...il.com>,
	Kukjin Kim <kgene.kim@...sung.com>, <eduardo.valentin@...com>
Subject: Re: [4/9] thermal: exynos: Bifurcate exynos thermal common and tmu
 controller code

Amit,

On 26-03-2013 07:33, Amit Daniel Kachhap wrote:
> This code bifurcates exynos thermal implementation into common and sensor
> specific parts as it will simplify adding support for new temperature
> sensors. The file is named as exynos4210 because it was original SOC for
> which this driver was developed and then later SOC's(5250, 4412) were added
> into it. This change is needed to add different TMU sensor for future exynos5
> SOC.
>
> Signed-off-by: Amit Daniel Kachhap <amit.daniel@...sung.com>
>
> ---
> drivers/thermal/samsung/Kconfig              |   24 +-
>   drivers/thermal/samsung/Makefile             |    4 +-
>   drivers/thermal/samsung/exynos4210_thermal.c |  658 ++++++++++++++++
>   drivers/thermal/samsung/exynos_common.c      |  421 ++++++++++
>   drivers/thermal/samsung/exynos_common.h      |   73 ++
>   drivers/thermal/samsung/exynos_thermal.c     | 1093 --------------------------
>   6 files changed, 1172 insertions(+), 1101 deletions(-)
>   create mode 100644 drivers/thermal/samsung/exynos4210_thermal.c
>   create mode 100644 drivers/thermal/samsung/exynos_common.c
>   create mode 100644 drivers/thermal/samsung/exynos_common.h
>   delete mode 100644 drivers/thermal/samsung/exynos_thermal.c
>

Same comment as in patch 3. Can you please use --find-renames?

Besides, it would be interesting if you easier the review process by 
splitting this patch into 2 at least. One for creating the _common.* and 
another to rename the remaining to 4210_thermal.

BTW, This follow same design pattern as under 
drivers/stating/ti-soc-thermal/ :-). I had to do similar design by 
having common and specific split so that I could afford the support for 
IPs with several sensors. Like OMAP5.

> diff --git a/drivers/thermal/samsung/Kconfig b/drivers/thermal/samsung/Kconfig
> index 5737b85..cefe693 100644
> --- a/drivers/thermal/samsung/Kconfig
> +++ b/drivers/thermal/samsung/Kconfig
> @@ -1,11 +1,23 @@
>
> -config EXYNOS_THERMAL
> -	tristate "Temperature sensor on Samsung EXYNOS"
> +config EXYNOS_COMMON
> +	tristate "Common thermal support for EXYNOS SOC's"
>   	depends on (ARCH_EXYNOS4 || ARCH_EXYNOS5)
> +	help
> +	  If you say yes here you get support for EXYNOS TMU
> +	  (Thermal Management Unit) common registration/unregistration
> +	  functions to the core thermal layer and also to use the generic
> +	  cpu cooling API's.
> +
> +if EXYNOS_COMMON
> +
> +config EXYNOS4210_THERMAL
> +	tristate "Temperature sensor on Samsung EXYNOS series SOC"
> +	depends on (CPU_EXYNOS4210 || SOC_EXYNOS4212 || SOC_EXYNOS4412 || SOC_EXYNOS5250)
>   	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.
> +	  If you say yes here you can enable TMU (Thermal Management Unit) on
> +	  SAMSUNG EXYNOS 4210, 4412, 4414 and 5250 series of SoC. This option
> +	  initialises the TMU controller and registers/unregisters with exynos
> +	  common thermal layer.
>
> +endif
> diff --git a/drivers/thermal/samsung/Makefile b/drivers/thermal/samsung/Makefile
> index fa55df5..d51d0c2 100644
> --- a/drivers/thermal/samsung/Makefile
> +++ b/drivers/thermal/samsung/Makefile
> @@ -1,5 +1,5 @@
>   #
>   # Samsung thermal specific Makefile
>   #
> -obj-$(CONFIG_EXYNOS_THERMAL)	+= exynos_thermal.o
> -
> +obj-$(CONFIG_EXYNOS_COMMON)		+= exynos_common.o
> +obj-$(CONFIG_EXYNOS4210_THERMAL)	+= exynos4210_thermal.o
> diff --git a/drivers/thermal/samsung/exynos4210_thermal.c b/drivers/thermal/samsung/exynos4210_thermal.c
> new file mode 100644
> index 0000000..09ea8c8
> --- /dev/null
> +++ b/drivers/thermal/samsung/exynos4210_thermal.c
> @@ -0,0 +1,658 @@
> +/*
> + * exynos4210_thermal.c - Samsung EXYNOS 4210, 4412, 5250 TMU
> + * (Thermal Management Unit)
> + *
> + *  Copyright (C) 2011 Samsung Electronics
> + *  Donggeun Kim <dg77.kim@...sung.com>
> + *  Amit Daniel Kachhap <amit.kachhap@...aro.org>
> + *  Amit Daniel Kachhap <amit.daniel@...sung.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + *
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/kernel.h>
> +#include <linux/kobject.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/platform_data/exynos_thermal.h>
> +#include <linux/slab.h>
> +#include <linux/workqueue.h>
> +#include "exynos_common.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
> +
> +#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 */
> +
> +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;
> +};
> +
> +/*
> + * 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@...sung.com>");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:exynos-tmu");
> diff --git a/drivers/thermal/samsung/exynos_common.c b/drivers/thermal/samsung/exynos_common.c
> new file mode 100644
> index 0000000..649d67c
> --- /dev/null
> +++ b/drivers/thermal/samsung/exynos_common.c
> @@ -0,0 +1,421 @@
> +/*
> + * exynos_common.c - Samsung EXYNOS common thermal file
> + *
> + *  Copyright (C) 2013 Samsung Electronics
> + *  Amit Daniel Kachhap <amit.daniel@...sung.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + *
> + */
> +
> +#include <linux/cpufreq.h>
> +#include <linux/cpu_cooling.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/kobject.h>
> +#include <linux/mutex.h>
> +#include <linux/platform_data/exynos_thermal.h>
> +#include <linux/slab.h>
> +#include <linux/thermal.h>
> +#include "exynos_common.h"
> +
> +struct exynos_thermal_zone {
> +	enum thermal_device_mode mode;
> +	struct thermal_zone_device *therm_dev;
> +	struct thermal_cooling_device *cool_dev[MAX_COOLING_DEVICE];
> +	unsigned int cool_dev_size;
> +	struct platform_device *exynos4_dev;
> +	struct thermal_sensor_conf *sensor_conf;
> +	bool bind;
> +};
> +
> +/* 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;
> +}
> +
> +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.
> + */
> +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 */
> +int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf)
> +{
> +	int ret, id;
> +	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;
> +	id = th_zone->therm_dev->id;
> +
> +	pr_info("Exynos: Kernel Thermal[%d] management registered\n", id);
> +
> +	return 0;
> +
> +err_unregister:
> +	exynos_unregister_thermal(sensor_conf);
> +	return ret;
> +}
> +
> +/* Un-Register with the in-kernel thermal management */
> +void exynos_unregister_thermal(struct thermal_sensor_conf *sensor_conf)
> +{
> +	int i, id;
> +	struct exynos_thermal_zone *th_zone = sensor_conf->pzone_data;
> +
> +	if (!th_zone)
> +		return;
> +
> +	id = th_zone->therm_dev->id;
> +	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[%d] management unregistered\n", id);
> +}
> diff --git a/drivers/thermal/samsung/exynos_common.h b/drivers/thermal/samsung/exynos_common.h
> new file mode 100644
> index 0000000..b8d289e
> --- /dev/null
> +++ b/drivers/thermal/samsung/exynos_common.h
> @@ -0,0 +1,73 @@
> +/*
> + * exynos_common.h - Samsung EXYNOS common header file
> + *
> + *  Copyright (C) 2013 Samsung Electronics
> + *  Amit Daniel Kachhap <amit.daniel@...sung.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + *
> + */
> +
> +#ifndef _LINUX_EXYNOS_COMMON_H
> +#define _LINUX_EXYNOS_COMMON_H
> +
> +/* In-kernel thermal framework related macros & definations */
> +#define SENSOR_NAME_LEN	16
> +#define MAX_TRIP_COUNT	8
> +#define MAX_COOLING_DEVICE 4
> +#define MAX_THRESHOLD_LEVS 4
> +
> +#define ACTIVE_INTERVAL 500
> +#define IDLE_INTERVAL 10000
> +#define MCELSIUS	1000
> +
> +/* CPU Zone information */
> +#define PANIC_ZONE      4
> +#define WARN_ZONE       3
> +#define MONITOR_ZONE    2
> +#define SAFE_ZONE       1
> +
> +#define GET_ZONE(trip) (trip + 2)
> +#define GET_TRIP(zone) (zone - 2)
> +
> +#define EXYNOS_ZONE_COUNT	3
> +
> +struct	thermal_trip_point_conf {
> +	int trip_val[MAX_TRIP_COUNT];
> +	int trip_count;
> +	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;
> +};
> +
> +/*Functions used exynos based thermal sensor driver*/
> +void exynos_unregister_thermal(struct thermal_sensor_conf *sensor_conf);
> +int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf);
> +void exynos_report_trigger(struct thermal_sensor_conf *sensor_conf);
> +int exynos_get_frequency_level(unsigned int cpu, unsigned int freq);
> +#endif /* _LINUX_EXYNOS_COMMON_H */
> diff --git a/drivers/thermal/samsung/exynos_thermal.c b/drivers/thermal/samsung/exynos_thermal.c
> deleted file mode 100644
> index dc9b91b..0000000
> --- a/drivers/thermal/samsung/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@...sung.com>
> - *  Amit Daniel Kachhap <amit.kachhap@...aro.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@...sung.com>");
> -MODULE_LICENSE("GPL");
> -MODULE_ALIAS("platform:exynos-tmu");
>

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ