[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <CADGdYn7SGW+HC4JU1SN=Am8FSjFUm_GZTJJoKf6kV3imduxozg@mail.gmail.com>
Date: Fri, 12 Apr 2013 16:39:13 +0530
From: amit daniel kachhap <amit.daniel@...sung.com>
To: Eduardo Valentin <eduardo.valentin@...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,
Kukjin Kim <kgene.kim@...sung.com>
Subject: Re: [4/9] thermal: exynos: Bifurcate exynos thermal common and tmu
controller code
On Fri, Apr 12, 2013 at 2:12 AM, Eduardo Valentin
<eduardo.valentin@...com> wrote:
> 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
>>
>> 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
>
>
> Are you sure you want separated modules?
>
> If yes you have to review this patch and export the symbols from common to
> exynos4210. Saying that because it generates a compilation error:
> ERROR: "exynos_report_trigger"
> [drivers/thermal/samsung/exynos4210_thermal.ko] undefined!
> ERROR: "exynos_register_thermal"
> [drivers/thermal/samsung/exynos4210_thermal.ko] undefined!
> ERROR: "exynos_unregister_thermal"
> [drivers/thermal/samsung/exynos4210_thermal.ko] undefined!
>
> If you want separate modules, you have to EXPORT_SYMBOL those.
Ok. looks like I missed this test.
>
> You can also do what I have done for TI SoC thermal (check linux-next
> drivers/staging/ti-soc-thermal/Makefile)
ok sure.
Thanks,
Amit D
>>
>> 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);
--
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