diff mbox

[RFC,1/4] ARM: Exynos4: Add thermal sensor driver for samsung exynos4 platform.

Message ID 1319629012-21205-1-git-send-email-amit.kachhap@linaro.org
State New
Headers show

Commit Message

Amit Daniel Kachhap Oct. 26, 2011, 11:36 a.m. UTC
This patch adds support for thermal sensor driver. It supports 3 trigger
level. The first 2 level are warn and monitor temperature level. The
third critical trigger level resets the system. The threshold
change information is sent to the thermal interface layer.

Signed-off-by: SangWook Ju <sw.ju@samsung.com>
Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>
---
 arch/arm/mach-exynos4/include/mach/exynos4-tmu.h   |   75 ++++
 drivers/staging/Kconfig                            |    2 +
 drivers/staging/Makefile                           |    1 +
 drivers/staging/thermal_exynos4/sensor/Kconfig     |   14 +
 drivers/staging/thermal_exynos4/sensor/Makefile    |    4 +
 .../thermal_exynos4/sensor/exynos4210_tmu.c        |  465 ++++++++++++++++++++
 6 files changed, 561 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-exynos4/include/mach/exynos4-tmu.h
 create mode 100644 drivers/staging/thermal_exynos4/sensor/Kconfig
 create mode 100644 drivers/staging/thermal_exynos4/sensor/Makefile
 create mode 100644 drivers/staging/thermal_exynos4/sensor/exynos4210_tmu.c

Comments

Kyungmin Park Oct. 27, 2011, 8:08 a.m. UTC | #1
Hi,

exynos4 tmu is already merged 3.2-rc
you can find it at below message and latest git kernel
http://www.spinics.net/lists/lm-sensors/msg33872.html

Thank you,
Kyungmin Park

On Wed, Oct 26, 2011 at 8:36 PM, Amit Daniel Kachhap
<amit.kachhap@linaro.org> wrote:
> This patch adds support for thermal sensor driver. It supports 3 trigger
> level. The first 2 level are warn and monitor temperature level. The
> third critical trigger level resets the system. The threshold
> change information is sent to the thermal interface layer.
>
> Signed-off-by: SangWook Ju <sw.ju@samsung.com>
> Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>
> ---
>  arch/arm/mach-exynos4/include/mach/exynos4-tmu.h   |   75 ++++
>  drivers/staging/Kconfig                            |    2 +
>  drivers/staging/Makefile                           |    1 +
>  drivers/staging/thermal_exynos4/sensor/Kconfig     |   14 +
>  drivers/staging/thermal_exynos4/sensor/Makefile    |    4 +
>  .../thermal_exynos4/sensor/exynos4210_tmu.c        |  465 ++++++++++++++++++++
>  6 files changed, 561 insertions(+), 0 deletions(-)
>  create mode 100644 arch/arm/mach-exynos4/include/mach/exynos4-tmu.h
>  create mode 100644 drivers/staging/thermal_exynos4/sensor/Kconfig
>  create mode 100644 drivers/staging/thermal_exynos4/sensor/Makefile
>  create mode 100644 drivers/staging/thermal_exynos4/sensor/exynos4210_tmu.c
>
> diff --git a/arch/arm/mach-exynos4/include/mach/exynos4-tmu.h b/arch/arm/mach-exynos4/include/mach/exynos4-tmu.h
> new file mode 100644
> index 0000000..dc7bf37
> --- /dev/null
> +++ b/arch/arm/mach-exynos4/include/mach/exynos4-tmu.h
> @@ -0,0 +1,75 @@
> +/**
> + * exynos4-tmu.h - definitions of exynos4 specific thermal interface
> + *
> + *   Copyright (C) 2011 Samsung Electronics Co. ltd.
> +
> + * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +#ifndef _EXYNOS4_TMU_H
> +#define _EXYNOS4_TMU_H
> +
> +/**
> + * tmu_data - tmu driver private structure
> + * @t1: colling stop temperature.
> + * @dc_temp: Temperature base.
> + * @init_threshold: Initial threshold temperature.
> + * @mode: Compensation mode.
> + * @comp_threshold: compensated threshold temperature.
> + *
> + */
> +struct tmu_data {
> +       char    te1;
> +       char    te2;
> +       unsigned int    init_threshold;
> +       int             mode;
> +       int             cooling;
> +       unsigned int    comp_threshold;
> +       int             tmu_flag;
> +};
> +
> +/**
> + * tmu_platform_device - tmu platform specific structure
> + * @id: Device id.
> + * @tmu_base: Memory register base.
> + * @dev: TMU device.
> + * @data: TMU driver private data structure.
> + *
> + */
> +struct tmu_platform_device {
> +       int                     id;
> +       struct platform_device *pdev;
> +       void __iomem            *tmu_base;
> +       struct device           *dev;
> +       struct tmu_data         data;
> +       struct thermal_dev      *therm_fw;
> +};
> +
> +#ifdef CONFIG_PM
> +struct exynos4_tmu_register {
> +       unsigned int regval_thresh_temp;
> +       unsigned int regval_trig_lev0;
> +       unsigned int regval_trig_lev1;
> +       unsigned int regval_trig_lev2;
> +       unsigned int regval_trig_lev3;
> +       unsigned int regval_tmu_con0;
> +       unsigned int regval_int_en;
> +};
> +#endif
> +
> +extern void exynos4_tmu_set_platdata(struct tmu_data *pd);
> +extern struct tmu_platform_device *exynos4_tmu_get_platdata(void);
> +extern int exynos4_tmu_get_irqno(int num);
> +#endif /* _EXYNOS4_TMU_H */
> diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
> index 2582e18..4376fed 100644
> --- a/drivers/staging/Kconfig
> +++ b/drivers/staging/Kconfig
> @@ -148,4 +148,6 @@ source "drivers/staging/mei/Kconfig"
>
>  source "drivers/staging/nvec/Kconfig"
>
> +source "drivers/staging/thermal_exynos4/Kconfig"
> +
>  endif # STAGING
> diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
> index 50d112f..24a2943 100644
> --- a/drivers/staging/Makefile
> +++ b/drivers/staging/Makefile
> @@ -65,3 +65,4 @@ obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4)  += ste_rmi4/
>  obj-$(CONFIG_DRM_PSB)          += gma500/
>  obj-$(CONFIG_INTEL_MEI)                += mei/
>  obj-$(CONFIG_MFD_NVEC)         += nvec/
> +obj-$(CONFIG_SAMSUNG_THERMAL_INTERFACE) += thermal_exynos4/
> diff --git a/drivers/staging/thermal_exynos4/sensor/Kconfig b/drivers/staging/thermal_exynos4/sensor/Kconfig
> new file mode 100644
> index 0000000..dcdd04b
> --- /dev/null
> +++ b/drivers/staging/thermal_exynos4/sensor/Kconfig
> @@ -0,0 +1,14 @@
> +#
> +# Thermal Temp sensor configuration
> +#
> +
> +config SENSORS_EXYNOS4_TMU
> +       tristate "Samsung thermal sensor for exynos platform"
> +       depends on ARCH_EXYNOS4
> +       default n
> +       help
> +         Say Y here if you want to enable thermal sensor for samsung
> +         exynos platform.
> +
> +         This driver can also be built as a module.  If so, the module
> +         will be called exynos4210-tmu.
> diff --git a/drivers/staging/thermal_exynos4/sensor/Makefile b/drivers/staging/thermal_exynos4/sensor/Makefile
> new file mode 100644
> index 0000000..4cdb818
> --- /dev/null
> +++ b/drivers/staging/thermal_exynos4/sensor/Makefile
> @@ -0,0 +1,4 @@
> +#
> +# Makefile for sensor chip drivers.
> +#
> +obj-$(CONFIG_SENSORS_EXYNOS4_TMU)+= exynos4210_tmu.o
> diff --git a/drivers/staging/thermal_exynos4/sensor/exynos4210_tmu.c b/drivers/staging/thermal_exynos4/sensor/exynos4210_tmu.c
> new file mode 100644
> index 0000000..b125379
> --- /dev/null
> +++ b/drivers/staging/thermal_exynos4/sensor/exynos4210_tmu.c
> @@ -0,0 +1,465 @@
> +/**
> + * exynos4210_tmu.c - exynos4 thermal sensor driver
> + *
> + * Copyright (C) 2011 Samsung Electronics Co. ltd.
> + *    Author: SangWook Ju <sw.ju@samsung.com>
> + *    Author: Amit Daniel Kachhap <amit.kachhap@linaro.org>
> +
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> +
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> +
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/fs.h>
> +#include <linux/string.h>
> +#include <linux/types.h>
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/delay.h>
> +#include <linux/platform_device.h>
> +#include <linux/interrupt.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/irq.h>
> +#include <linux/sysfs.h>
> +#include <linux/slab.h>
> +#include <asm/irq.h>
> +#include <mach/regs-tmu.h>
> +#include <mach/exynos4-tmu.h>
> +#include <mach/thermal_interface.h>
> +
> +#define INIT_THRESHOLD 80
> +/*Monitor temp = INIT_THRESHOLD + TRIGGER_LEV0 = 85*/
> +#define        TRIGGER_LEV0 5
> +/*Warning temp = INIT_THRESHOLD + TRIGGER_LEV1 = 100*/
> +#define        TRIGGER_LEV1 20
> +/*Panic temp = INIT_THRESHOLD + TRIGGER_LEV2 = 110*/
> +#define        TRIGGER_LEV2 30
> +#define        TRIGGER_LEV3 0xFF
> +
> +#define EFUSE_MIN_VALUE 60
> +#define EFUSE_AVG_VALUE 80
> +#define EFUSE_MAX_VALUE 100
> +
> +#define VREF_SLOPE             0x07000F02
> +#define TMU_EN                 0x1
> +#define TMU_DC_VALUE           25
> +
> +enum tmu_status_t {
> +       TMU_STATUS_NORMAL = 0,
> +       TMU_STATUS_MONITOR,
> +       TMU_STATUS_WARNING,
> +       TMU_STATUS_PANIC,
> +};
> +
> +static struct resource *exynos4_tmu_mem;
> +static struct workqueue_struct  *tmu_wq;
> +static struct delayed_work tmu_work;
> +
> +#ifdef CONFIG_PM
> +static struct exynos4_tmu_register tmu_register_save;
> +#endif
> +
> +static int irq_tmu = NO_IRQ;
> +static struct tmu_platform_device *tmu_dev_t;
> +
> +/**
> + * exynos4_tmu_readb - read a byte value from the given register.
> + * @tmu_dev: The tmu device structure.
> + * @reg: Register.
> + *
> + * Read a byte value from the given register.
> + */
> +static unsigned int exynos4_tmu_readb(struct tmu_platform_device *tmu_dev,
> +                                               unsigned int reg)
> +{
> +       return __raw_readb(tmu_dev->tmu_base + reg);
> +}
> +
> +/**
> + * exynos4_tmu_writeb - write a byte value to the given register.
> + * @tmu_dev: The tmu device structure.
> + * @reg: Register.
> + * @val: Value to write.
> + *
> + * write a byte value to the given register.
> + */
> +static void exynos4_tmu_writeb(struct tmu_platform_device *tmu_dev,
> +                                       unsigned int reg, unsigned int val)
> +{
> +       __raw_writeb(val, tmu_dev->tmu_base + reg);
> +}
> +
> +/**
> + * exynos4_tmu_readl - Read a long value from the given register.
> + * @tmu_dev: The tmu device structure.
> + * @reg: Register.
> + *
> + * Read a long value from the given register.
> + */
> +static unsigned int exynos4_tmu_readl(struct tmu_platform_device *tmu_dev,
> +                                               unsigned int reg)
> +{
> +       return __raw_readl(tmu_dev->tmu_base + reg);
> +}
> +
> +/**
> + * exynos4_tmu_writel - write a long value to the given register.
> + * @tmu_dev: The tmu device structure.
> + * @reg: Register.
> + * @val: Value to write.
> + *
> + * write a long value to the given register.
> + */
> +static void exynos4_tmu_writel(struct tmu_platform_device *tmu_dev,
> +                                       unsigned int reg, unsigned int val)
> +{
> +       __raw_writel(val, tmu_dev->tmu_base + reg);
> +}
> +
> +
> +static int exynos4_tmu_current_temp(struct tmu_platform_device *tmu_dev)
> +{
> +       int temp;
> +       temp = exynos4_tmu_readl(tmu_dev, CURRENT_TEMP)
> +                       - tmu_dev->data.te1 + TMU_DC_VALUE;
> +       return temp;
> +}
> +
> +/**
> + * tmu_monitor - Timer handler for monitoring interrupt.
> + * @work: temp work.
> + *
> + * Timer handler for monitor/warning/panic interrupts.
> +*/
> +
> +static void tmu_monitor(struct work_struct *work)
> +{
> +       exynos4_report_trigger();
> +       enable_irq(irq_tmu);
> +}
> +
> +int exynos4_tmu_read_temp(int *temp)
> +{
> +       *temp = 0;
> +       if (tmu_dev_t)
> +               *temp = exynos4_tmu_readl(tmu_dev_t, CURRENT_TEMP)
> +                       - tmu_dev_t->data.te1 + TMU_DC_VALUE;
> +       if (*temp > 0)
> +               return 0;
> +       else
> +               return -EINVAL;
> +}
> +
> +/*
> + * exynos4_tmu_initialize - Initializes the TMU driver.
> + * @pdev: The platform device structure.
> + *
> + * Initialises the different registers of the temperature sensor.
> + */
> +static int exynos4_tmu_initialize(struct platform_device *pdev)
> +{
> +       struct tmu_platform_device *tmu_dev = platform_get_drvdata(pdev);
> +       unsigned int en;
> +       unsigned int te_temp;
> +       unsigned int t_new;
> +
> +       en = (exynos4_tmu_readb(tmu_dev, TMU_STATUS) & 0x1);
> +       if (!en) {
> +               dev_err(&pdev->dev, "failed to start tmu drvier\n");
> +               return -ENOENT;
> +       }
> +
> +       /* Temperature compensation */
> +       te_temp = exynos4_tmu_readl(tmu_dev, TRIMINFO);
> +       tmu_dev->data.te1 = te_temp  & TRIM_TEMP_MASK;
> +       tmu_dev->data.te2 = ((te_temp >> 8) & TRIM_TEMP_MASK);
> +
> +       if ((EFUSE_MIN_VALUE > tmu_dev->data.te1) ||
> +                       (tmu_dev->data.te1 > EFUSE_MAX_VALUE) ||
> +                       (tmu_dev->data.te2 != 0))
> +               tmu_dev->data.te1 = EFUSE_AVG_VALUE;
> +
> +       t_new =  INIT_THRESHOLD + tmu_dev->data.te1 - TMU_DC_VALUE;
> +
> +       exynos4_tmu_writel(tmu_dev, THRESHOLD_TEMP, t_new);
> +
> +       /* Need to initialize regsiter setting after getting parameter info */
> +       /* [28:23] vref [11:8] slope - Tunning parameter */
> +       exynos4_tmu_writel(tmu_dev, TMU_CON0, VREF_SLOPE);
> +       exynos4_tmu_writeb(tmu_dev, TRG_LEV0, TRIGGER_LEV0);
> +       exynos4_tmu_writeb(tmu_dev, TRG_LEV1, TRIGGER_LEV1);
> +       exynos4_tmu_writeb(tmu_dev, TRG_LEV2, TRIGGER_LEV2);
> +       exynos4_tmu_writeb(tmu_dev, TRG_LEV3, TRIGGER_LEV3);
> +       pr_info("TMU is initialized !!\n");
> +       pr_info("Threshold temp: %dc Monitor temp: %dc\n",
> +                       INIT_THRESHOLD,
> +                       INIT_THRESHOLD + exynos4_tmu_readb(tmu_dev, TRG_LEV0));
> +       pr_info("Warning temp: %dc Panic temp: %dc\n",
> +                       INIT_THRESHOLD + exynos4_tmu_readb(tmu_dev, TRG_LEV1),
> +                       INIT_THRESHOLD + exynos4_tmu_readb(tmu_dev, TRG_LEV2));
> +       pr_info("Cooling temp: %dc\n",
> +                       tmu_dev->data.cooling);
> +}
> +
> +/**
> + * exynos4_tmu_start - Triggers the TMU driver.
> + * @pdev: The platform device structure.
> + *
> + * Triggers the temperature sensor.
> + */
> +static void exynos4_tmu_start(struct platform_device *pdev)
> +{
> +       struct tmu_platform_device *tmu_dev = platform_get_drvdata(pdev);
> +       unsigned int con, cur_temp;
> +       exynos4_tmu_writel(tmu_dev, INTCLEAR,
> +                       INTCLEAR0|INTCLEAR1|INTCLEAR2|INTCLEAR3);
> +
> +       /* TMU core enable */
> +       con = exynos4_tmu_readl(tmu_dev, TMU_CON0);
> +       con |= TMU_EN;
> +       exynos4_tmu_writel(tmu_dev, TMU_CON0, con);
> +
> +       /*LEV0 LEV1 LEV2 interrupt enable */
> +       exynos4_tmu_writel(tmu_dev, INTEN, (INTEN0|INTEN1|INTEN2));
> +       usleep_range(200, 1000);
> +       cur_temp = exynos4_tmu_current_temp(tmu_dev);
> +       pr_info("Current Temperature : %dc\n\n", cur_temp);
> +}
> +/**
> + * exynos4_tmu_irq - IRQ handler for tmu interrupt.
> + * @irq: IRQ line.
> + * @id:        void data pointer for the tmu device.
> + *
> + * IRQ handler for  interrupt.
> +*/
> +static irqreturn_t exynos4_tmu_irq(int irq, void *id)
> +{
> +       struct tmu_platform_device *tmu_dev = (struct tmu_platform_device *)id;
> +       unsigned int status;
> +
> +       disable_irq_nosync(irq);
> +
> +       status = __raw_readl(tmu_dev->tmu_base + INTSTAT);
> +
> +       if (status & INTSTAT0) {
> +               pr_info("Monitor interrupt occured!!!!\n");
> +               __raw_writel(INTCLEAR0, tmu_dev->tmu_base + INTCLEAR);
> +               tmu_dev->data.tmu_flag = TMU_STATUS_MONITOR;
> +               queue_delayed_work_on(0, tmu_wq, &tmu_work,
> +                                                usecs_to_jiffies(10 * 1000));
> +       } else if (status & INTSTAT1) {
> +               pr_info("Warning interrupt occured!!!!\n");
> +               __raw_writel(INTCLEAR1, tmu_dev->tmu_base + INTCLEAR);
> +               tmu_dev->data.tmu_flag = TMU_STATUS_WARNING;
> +               queue_delayed_work_on(0, tmu_wq, &tmu_work,
> +                                                usecs_to_jiffies(10 * 1000));
> +       } else if (status & INTSTAT2) {
> +               pr_info("Panic interrupt occured!!!!\n");
> +               __raw_writel(INTCLEAR2, tmu_dev->tmu_base + INTCLEAR);
> +               tmu_dev->data.tmu_flag = TMU_STATUS_PANIC;
> +               queue_delayed_work_on(0, tmu_wq, &tmu_work,
> +                                                usecs_to_jiffies(10 * 1000));
> +       } else {
> +               pr_err("%s: TMU interrupt error\n", __func__);
> +               return -ENODEV;
> +       }
> +
> +       return IRQ_HANDLED;
> +}
> +
> +/**
> + * exynos4_tmu_probe - device probe entry.
> + * @pdev: The device being probed.
> + */
> +static int __devinit exynos4_tmu_probe(struct platform_device *pdev)
> +{
> +       struct tmu_platform_device *tmu_dev = platform_get_drvdata(pdev);
> +       struct resource *res;
> +       int ret;
> +
> +       pr_debug("%s: probe=%p\n", __func__, pdev);
> +       irq_tmu = platform_get_irq(pdev, 0);
> +       if (irq_tmu < 0) {
> +               dev_err(&pdev->dev, "no irq for thermal monitor\n");
> +               return -ENOENT;
> +       }
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       if (res == NULL) {
> +               dev_err(&pdev->dev, "failed to get memory region resource\n");
> +               return -ENOENT;
> +       }
> +
> +       exynos4_tmu_mem = request_mem_region(res->start,
> +                                       res->end-res->start+1,
> +                                       pdev->name);
> +       if (exynos4_tmu_mem == NULL) {
> +               dev_err(&pdev->dev, "failed to reserve memory region\n");
> +               ret = -ENOENT;
> +               goto err_nores;
> +       }
> +
> +       tmu_dev->tmu_base = ioremap(res->start, (res->end - res->start) + 1);
> +       if (tmu_dev->tmu_base == NULL) {
> +               dev_err(&pdev->dev, "failed ioremap()\n");
> +               ret = -EINVAL;
> +               goto err_nomap;
> +       }
> +       tmu_dev->pdev = pdev;
> +       tmu_dev->dev = &pdev->dev;
> +
> +       ret = request_irq(irq_tmu, exynos4_tmu_irq,
> +                       IRQF_DISABLED,  "exynos4-tmu ", tmu_dev);
> +       if (ret) {
> +               dev_err(&pdev->dev, "IRQ%d error %d\n", irq_tmu, ret);
> +               goto err_irq;
> +       }
> +
> +       tmu_dev_t = tmu_dev;
> +
> +       exynos4_tmu_initialize(pdev);
> +       if (ret)
> +               goto err_nores;
> +
> +       exynos4_tmu_start(pdev);
> +
> +       exynos4_read_sensor = exynos4_tmu_read_temp;
> +
> +       return 0;
> +err_irq:
> +       iounmap(tmu_dev->tmu_base);
> +err_nomap:
> +       release_resource(exynos4_tmu_mem);
> +err_nores:
> +       return ret;
> +}
> +
> +/**
> + * exynos4_tmu_remove - device remove entry.
> + * @pdev: The device being removed.
> + */
> +static int __devinit exynos4_tmu_remove(struct platform_device *pdev)
> +{
> +       struct tmu_platform_device *tmu_dev = platform_get_drvdata(pdev);
> +       free_irq(irq_tmu, tmu_dev);
> +       iounmap(tmu_dev->tmu_base);
> +       release_resource(exynos4_tmu_mem);
> +       return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +/**
> + * exynos4_tmu_suspend - device suspend function.
> + * @pdev: The device being suspended.
> + * @state: suspend event message.
> + */
> +static int exynos4_tmu_suspend(struct platform_device *pdev, pm_message_t state)
> +{
> +       struct tmu_platform_device *tmu_dev = platform_get_drvdata(pdev);
> +
> +       tmu_register_save.regval_thresh_temp =
> +                       exynos4_tmu_readb(tmu_dev, THRESHOLD_TEMP);
> +       tmu_register_save.regval_trig_lev0 =
> +                       exynos4_tmu_readb(tmu_dev, TRG_LEV0);
> +       tmu_register_save.regval_trig_lev1 =
> +                       exynos4_tmu_readb(tmu_dev, TRG_LEV1);
> +       tmu_register_save.regval_trig_lev2 =
> +                       exynos4_tmu_readb(tmu_dev, TRG_LEV2);
> +       tmu_register_save.regval_trig_lev3 =
> +                       exynos4_tmu_readb(tmu_dev, TRG_LEV3);
> +       tmu_register_save.regval_tmu_con0 =
> +                       exynos4_tmu_readl(tmu_dev, TMU_CON0);
> +       tmu_register_save.regval_int_en =
> +                       exynos4_tmu_readl(tmu_dev, INTEN);
> +       return 0;
> +}
> +
> +/**
> + * exynos4_tmu_resume - device resume function.
> + * @pdev: The device being suspended.
> + */
> +static int exynos4_tmu_resume(struct platform_device *pdev)
> +{
> +       struct tmu_platform_device *tmu_dev = platform_get_drvdata(pdev);
> +
> +       exynos4_tmu_writeb(tmu_dev, THRESHOLD_TEMP,
> +                               tmu_register_save.regval_thresh_temp);
> +       exynos4_tmu_writeb(tmu_dev, TRG_LEV0,
> +                               tmu_register_save.regval_trig_lev0);
> +       exynos4_tmu_writeb(tmu_dev, TRG_LEV1,
> +                               tmu_register_save.regval_trig_lev1);
> +       exynos4_tmu_writeb(tmu_dev, TRG_LEV2,
> +                               tmu_register_save.regval_trig_lev2);
> +       exynos4_tmu_writeb(tmu_dev, TRG_LEV3,
> +                               tmu_register_save.regval_trig_lev3);
> +       exynos4_tmu_writeb(tmu_dev, TMU_CON0,
> +                               tmu_register_save.regval_tmu_con0);
> +       exynos4_tmu_writel(tmu_dev, INTEN,
> +                               tmu_register_save.regval_int_en);
> +       exynos4_tmu_writel(tmu_dev, INTCLEAR,
> +                               INTCLEAR0|INTCLEAR1|INTCLEAR2|INTCLEAR3);
> +       return 0;
> +}
> +
> +#else
> +#define exynos4_tmu_suspend    NULL
> +#define exynos4_tmu_resume     NULL
> +#endif
> +
> +static struct platform_driver exynos4_tmu_driver = {
> +       .probe          = exynos4_tmu_probe,
> +       .remove         = exynos4_tmu_remove,
> +       .suspend        = exynos4_tmu_suspend,
> +       .resume         = exynos4_tmu_resume,
> +       .driver         = {
> +                       .name   = "exynos4-tmu",
> +                       .owner  = THIS_MODULE,
> +                       },
> +};
> +
> +static int __init exynos4_tmu_driver_init(void)
> +{
> +       int ret = 0;
> +       pr_info("EXYNOS4 TMU driver is loaded.\n");
> +       tmu_wq = create_workqueue("tmu");
> +       if (!tmu_wq) {
> +               pr_err(" creation of tmu failed\n");
> +               ret = -EFAULT;
> +               goto out;
> +       }
> +       INIT_DELAYED_WORK_DEFERRABLE(&tmu_work, tmu_monitor);
> +       ret = platform_driver_register(&exynos4_tmu_driver);
> +       if (ret) {
> +               pr_err("registeration of tmu_driver failed\n");
> +               destroy_workqueue(tmu_wq);
> +               goto out;
> +       }
> +out:
> +       return ret;
> +}
> +
> +static void __exit exynos4_tmu_driver_exit(void)
> +{
> +       destroy_workqueue(tmu_wq);
> +       platform_driver_unregister(&exynos4_tmu_driver);
> +}
> +
> +module_init(exynos4_tmu_driver_init);
> +module_exit(exynos4_tmu_driver_exit);
> +
> +MODULE_AUTHOR("Samsung Electronics");
> +MODULE_DESCRIPTION("EXYNOS4 temperature sensor driver");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:exynos4-tmu");
> --
> 1.7.1
>
>
> _______________________________________________
> linaro-dev mailing list
> linaro-dev@lists.linaro.org
> http://lists.linaro.org/mailman/listinfo/linaro-dev
>
Amit Daniel Kachhap Oct. 31, 2011, 9:56 p.m. UTC | #2
On 27 October 2011 04:08, Kyungmin Park <kmpark@infradead.org> wrote:
> Hi,
>
> exynos4 tmu is already merged 3.2-rc
> you can find it at below message and latest git kernel
> http://www.spinics.net/lists/lm-sensors/msg33872.html
>
> Thank you,
> Kyungmin Park

ok I will rebase my work on top of sensor
Thanks for pointing this out.

>
> On Wed, Oct 26, 2011 at 8:36 PM, Amit Daniel Kachhap
> <amit.kachhap@linaro.org> wrote:
>> This patch adds support for thermal sensor driver. It supports 3 trigger
>> level. The first 2 level are warn and monitor temperature level. The
>> third critical trigger level resets the system. The threshold
>> change information is sent to the thermal interface layer.
>>
>> Signed-off-by: SangWook Ju <sw.ju@samsung.com>
>> Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>
>> ---
>>  arch/arm/mach-exynos4/include/mach/exynos4-tmu.h   |   75 ++++
>>  drivers/staging/Kconfig                            |    2 +
>>  drivers/staging/Makefile                           |    1 +
>>  drivers/staging/thermal_exynos4/sensor/Kconfig     |   14 +
>>  drivers/staging/thermal_exynos4/sensor/Makefile    |    4 +
>>  .../thermal_exynos4/sensor/exynos4210_tmu.c        |  465 ++++++++++++++++++++
>>  6 files changed, 561 insertions(+), 0 deletions(-)
>>  create mode 100644 arch/arm/mach-exynos4/include/mach/exynos4-tmu.h
>>  create mode 100644 drivers/staging/thermal_exynos4/sensor/Kconfig
>>  create mode 100644 drivers/staging/thermal_exynos4/sensor/Makefile
>>  create mode 100644 drivers/staging/thermal_exynos4/sensor/exynos4210_tmu.c
>>
>> diff --git a/arch/arm/mach-exynos4/include/mach/exynos4-tmu.h b/arch/arm/mach-exynos4/include/mach/exynos4-tmu.h
>> new file mode 100644
>> index 0000000..dc7bf37
>> --- /dev/null
>> +++ b/arch/arm/mach-exynos4/include/mach/exynos4-tmu.h
>> @@ -0,0 +1,75 @@
>> +/**
>> + * exynos4-tmu.h - definitions of exynos4 specific thermal interface
>> + *
>> + *   Copyright (C) 2011 Samsung Electronics Co. ltd.
>> +
>> + * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
>> + */
>> +
>> +#ifndef _EXYNOS4_TMU_H
>> +#define _EXYNOS4_TMU_H
>> +
>> +/**
>> + * tmu_data - tmu driver private structure
>> + * @t1: colling stop temperature.
>> + * @dc_temp: Temperature base.
>> + * @init_threshold: Initial threshold temperature.
>> + * @mode: Compensation mode.
>> + * @comp_threshold: compensated threshold temperature.
>> + *
>> + */
>> +struct tmu_data {
>> +       char    te1;
>> +       char    te2;
>> +       unsigned int    init_threshold;
>> +       int             mode;
>> +       int             cooling;
>> +       unsigned int    comp_threshold;
>> +       int             tmu_flag;
>> +};
>> +
>> +/**
>> + * tmu_platform_device - tmu platform specific structure
>> + * @id: Device id.
>> + * @tmu_base: Memory register base.
>> + * @dev: TMU device.
>> + * @data: TMU driver private data structure.
>> + *
>> + */
>> +struct tmu_platform_device {
>> +       int                     id;
>> +       struct platform_device *pdev;
>> +       void __iomem            *tmu_base;
>> +       struct device           *dev;
>> +       struct tmu_data         data;
>> +       struct thermal_dev      *therm_fw;
>> +};
>> +
>> +#ifdef CONFIG_PM
>> +struct exynos4_tmu_register {
>> +       unsigned int regval_thresh_temp;
>> +       unsigned int regval_trig_lev0;
>> +       unsigned int regval_trig_lev1;
>> +       unsigned int regval_trig_lev2;
>> +       unsigned int regval_trig_lev3;
>> +       unsigned int regval_tmu_con0;
>> +       unsigned int regval_int_en;
>> +};
>> +#endif
>> +
>> +extern void exynos4_tmu_set_platdata(struct tmu_data *pd);
>> +extern struct tmu_platform_device *exynos4_tmu_get_platdata(void);
>> +extern int exynos4_tmu_get_irqno(int num);
>> +#endif /* _EXYNOS4_TMU_H */
>> diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
>> index 2582e18..4376fed 100644
>> --- a/drivers/staging/Kconfig
>> +++ b/drivers/staging/Kconfig
>> @@ -148,4 +148,6 @@ source "drivers/staging/mei/Kconfig"
>>
>>  source "drivers/staging/nvec/Kconfig"
>>
>> +source "drivers/staging/thermal_exynos4/Kconfig"
>> +
>>  endif # STAGING
>> diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
>> index 50d112f..24a2943 100644
>> --- a/drivers/staging/Makefile
>> +++ b/drivers/staging/Makefile
>> @@ -65,3 +65,4 @@ obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4)  += ste_rmi4/
>>  obj-$(CONFIG_DRM_PSB)          += gma500/
>>  obj-$(CONFIG_INTEL_MEI)                += mei/
>>  obj-$(CONFIG_MFD_NVEC)         += nvec/
>> +obj-$(CONFIG_SAMSUNG_THERMAL_INTERFACE) += thermal_exynos4/
>> diff --git a/drivers/staging/thermal_exynos4/sensor/Kconfig b/drivers/staging/thermal_exynos4/sensor/Kconfig
>> new file mode 100644
>> index 0000000..dcdd04b
>> --- /dev/null
>> +++ b/drivers/staging/thermal_exynos4/sensor/Kconfig
>> @@ -0,0 +1,14 @@
>> +#
>> +# Thermal Temp sensor configuration
>> +#
>> +
>> +config SENSORS_EXYNOS4_TMU
>> +       tristate "Samsung thermal sensor for exynos platform"
>> +       depends on ARCH_EXYNOS4
>> +       default n
>> +       help
>> +         Say Y here if you want to enable thermal sensor for samsung
>> +         exynos platform.
>> +
>> +         This driver can also be built as a module.  If so, the module
>> +         will be called exynos4210-tmu.
>> diff --git a/drivers/staging/thermal_exynos4/sensor/Makefile b/drivers/staging/thermal_exynos4/sensor/Makefile
>> new file mode 100644
>> index 0000000..4cdb818
>> --- /dev/null
>> +++ b/drivers/staging/thermal_exynos4/sensor/Makefile
>> @@ -0,0 +1,4 @@
>> +#
>> +# Makefile for sensor chip drivers.
>> +#
>> +obj-$(CONFIG_SENSORS_EXYNOS4_TMU)+= exynos4210_tmu.o
>> diff --git a/drivers/staging/thermal_exynos4/sensor/exynos4210_tmu.c b/drivers/staging/thermal_exynos4/sensor/exynos4210_tmu.c
>> new file mode 100644
>> index 0000000..b125379
>> --- /dev/null
>> +++ b/drivers/staging/thermal_exynos4/sensor/exynos4210_tmu.c
>> @@ -0,0 +1,465 @@
>> +/**
>> + * exynos4210_tmu.c - exynos4 thermal sensor driver
>> + *
>> + * Copyright (C) 2011 Samsung Electronics Co. ltd.
>> + *    Author: SangWook Ju <sw.ju@samsung.com>
>> + *    Author: Amit Daniel Kachhap <amit.kachhap@linaro.org>
>> +
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> +
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> +
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/fs.h>
>> +#include <linux/string.h>
>> +#include <linux/types.h>
>> +#include <linux/kernel.h>
>> +#include <linux/init.h>
>> +#include <linux/delay.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/err.h>
>> +#include <linux/io.h>
>> +#include <linux/irq.h>
>> +#include <linux/sysfs.h>
>> +#include <linux/slab.h>
>> +#include <asm/irq.h>
>> +#include <mach/regs-tmu.h>
>> +#include <mach/exynos4-tmu.h>
>> +#include <mach/thermal_interface.h>
>> +
>> +#define INIT_THRESHOLD 80
>> +/*Monitor temp = INIT_THRESHOLD + TRIGGER_LEV0 = 85*/
>> +#define        TRIGGER_LEV0 5
>> +/*Warning temp = INIT_THRESHOLD + TRIGGER_LEV1 = 100*/
>> +#define        TRIGGER_LEV1 20
>> +/*Panic temp = INIT_THRESHOLD + TRIGGER_LEV2 = 110*/
>> +#define        TRIGGER_LEV2 30
>> +#define        TRIGGER_LEV3 0xFF
>> +
>> +#define EFUSE_MIN_VALUE 60
>> +#define EFUSE_AVG_VALUE 80
>> +#define EFUSE_MAX_VALUE 100
>> +
>> +#define VREF_SLOPE             0x07000F02
>> +#define TMU_EN                 0x1
>> +#define TMU_DC_VALUE           25
>> +
>> +enum tmu_status_t {
>> +       TMU_STATUS_NORMAL = 0,
>> +       TMU_STATUS_MONITOR,
>> +       TMU_STATUS_WARNING,
>> +       TMU_STATUS_PANIC,
>> +};
>> +
>> +static struct resource *exynos4_tmu_mem;
>> +static struct workqueue_struct  *tmu_wq;
>> +static struct delayed_work tmu_work;
>> +
>> +#ifdef CONFIG_PM
>> +static struct exynos4_tmu_register tmu_register_save;
>> +#endif
>> +
>> +static int irq_tmu = NO_IRQ;
>> +static struct tmu_platform_device *tmu_dev_t;
>> +
>> +/**
>> + * exynos4_tmu_readb - read a byte value from the given register.
>> + * @tmu_dev: The tmu device structure.
>> + * @reg: Register.
>> + *
>> + * Read a byte value from the given register.
>> + */
>> +static unsigned int exynos4_tmu_readb(struct tmu_platform_device *tmu_dev,
>> +                                               unsigned int reg)
>> +{
>> +       return __raw_readb(tmu_dev->tmu_base + reg);
>> +}
>> +
>> +/**
>> + * exynos4_tmu_writeb - write a byte value to the given register.
>> + * @tmu_dev: The tmu device structure.
>> + * @reg: Register.
>> + * @val: Value to write.
>> + *
>> + * write a byte value to the given register.
>> + */
>> +static void exynos4_tmu_writeb(struct tmu_platform_device *tmu_dev,
>> +                                       unsigned int reg, unsigned int val)
>> +{
>> +       __raw_writeb(val, tmu_dev->tmu_base + reg);
>> +}
>> +
>> +/**
>> + * exynos4_tmu_readl - Read a long value from the given register.
>> + * @tmu_dev: The tmu device structure.
>> + * @reg: Register.
>> + *
>> + * Read a long value from the given register.
>> + */
>> +static unsigned int exynos4_tmu_readl(struct tmu_platform_device *tmu_dev,
>> +                                               unsigned int reg)
>> +{
>> +       return __raw_readl(tmu_dev->tmu_base + reg);
>> +}
>> +
>> +/**
>> + * exynos4_tmu_writel - write a long value to the given register.
>> + * @tmu_dev: The tmu device structure.
>> + * @reg: Register.
>> + * @val: Value to write.
>> + *
>> + * write a long value to the given register.
>> + */
>> +static void exynos4_tmu_writel(struct tmu_platform_device *tmu_dev,
>> +                                       unsigned int reg, unsigned int val)
>> +{
>> +       __raw_writel(val, tmu_dev->tmu_base + reg);
>> +}
>> +
>> +
>> +static int exynos4_tmu_current_temp(struct tmu_platform_device *tmu_dev)
>> +{
>> +       int temp;
>> +       temp = exynos4_tmu_readl(tmu_dev, CURRENT_TEMP)
>> +                       - tmu_dev->data.te1 + TMU_DC_VALUE;
>> +       return temp;
>> +}
>> +
>> +/**
>> + * tmu_monitor - Timer handler for monitoring interrupt.
>> + * @work: temp work.
>> + *
>> + * Timer handler for monitor/warning/panic interrupts.
>> +*/
>> +
>> +static void tmu_monitor(struct work_struct *work)
>> +{
>> +       exynos4_report_trigger();
>> +       enable_irq(irq_tmu);
>> +}
>> +
>> +int exynos4_tmu_read_temp(int *temp)
>> +{
>> +       *temp = 0;
>> +       if (tmu_dev_t)
>> +               *temp = exynos4_tmu_readl(tmu_dev_t, CURRENT_TEMP)
>> +                       - tmu_dev_t->data.te1 + TMU_DC_VALUE;
>> +       if (*temp > 0)
>> +               return 0;
>> +       else
>> +               return -EINVAL;
>> +}
>> +
>> +/*
>> + * exynos4_tmu_initialize - Initializes the TMU driver.
>> + * @pdev: The platform device structure.
>> + *
>> + * Initialises the different registers of the temperature sensor.
>> + */
>> +static int exynos4_tmu_initialize(struct platform_device *pdev)
>> +{
>> +       struct tmu_platform_device *tmu_dev = platform_get_drvdata(pdev);
>> +       unsigned int en;
>> +       unsigned int te_temp;
>> +       unsigned int t_new;
>> +
>> +       en = (exynos4_tmu_readb(tmu_dev, TMU_STATUS) & 0x1);
>> +       if (!en) {
>> +               dev_err(&pdev->dev, "failed to start tmu drvier\n");
>> +               return -ENOENT;
>> +       }
>> +
>> +       /* Temperature compensation */
>> +       te_temp = exynos4_tmu_readl(tmu_dev, TRIMINFO);
>> +       tmu_dev->data.te1 = te_temp  & TRIM_TEMP_MASK;
>> +       tmu_dev->data.te2 = ((te_temp >> 8) & TRIM_TEMP_MASK);
>> +
>> +       if ((EFUSE_MIN_VALUE > tmu_dev->data.te1) ||
>> +                       (tmu_dev->data.te1 > EFUSE_MAX_VALUE) ||
>> +                       (tmu_dev->data.te2 != 0))
>> +               tmu_dev->data.te1 = EFUSE_AVG_VALUE;
>> +
>> +       t_new =  INIT_THRESHOLD + tmu_dev->data.te1 - TMU_DC_VALUE;
>> +
>> +       exynos4_tmu_writel(tmu_dev, THRESHOLD_TEMP, t_new);
>> +
>> +       /* Need to initialize regsiter setting after getting parameter info */
>> +       /* [28:23] vref [11:8] slope - Tunning parameter */
>> +       exynos4_tmu_writel(tmu_dev, TMU_CON0, VREF_SLOPE);
>> +       exynos4_tmu_writeb(tmu_dev, TRG_LEV0, TRIGGER_LEV0);
>> +       exynos4_tmu_writeb(tmu_dev, TRG_LEV1, TRIGGER_LEV1);
>> +       exynos4_tmu_writeb(tmu_dev, TRG_LEV2, TRIGGER_LEV2);
>> +       exynos4_tmu_writeb(tmu_dev, TRG_LEV3, TRIGGER_LEV3);
>> +       pr_info("TMU is initialized !!\n");
>> +       pr_info("Threshold temp: %dc Monitor temp: %dc\n",
>> +                       INIT_THRESHOLD,
>> +                       INIT_THRESHOLD + exynos4_tmu_readb(tmu_dev, TRG_LEV0));
>> +       pr_info("Warning temp: %dc Panic temp: %dc\n",
>> +                       INIT_THRESHOLD + exynos4_tmu_readb(tmu_dev, TRG_LEV1),
>> +                       INIT_THRESHOLD + exynos4_tmu_readb(tmu_dev, TRG_LEV2));
>> +       pr_info("Cooling temp: %dc\n",
>> +                       tmu_dev->data.cooling);
>> +}
>> +
>> +/**
>> + * exynos4_tmu_start - Triggers the TMU driver.
>> + * @pdev: The platform device structure.
>> + *
>> + * Triggers the temperature sensor.
>> + */
>> +static void exynos4_tmu_start(struct platform_device *pdev)
>> +{
>> +       struct tmu_platform_device *tmu_dev = platform_get_drvdata(pdev);
>> +       unsigned int con, cur_temp;
>> +       exynos4_tmu_writel(tmu_dev, INTCLEAR,
>> +                       INTCLEAR0|INTCLEAR1|INTCLEAR2|INTCLEAR3);
>> +
>> +       /* TMU core enable */
>> +       con = exynos4_tmu_readl(tmu_dev, TMU_CON0);
>> +       con |= TMU_EN;
>> +       exynos4_tmu_writel(tmu_dev, TMU_CON0, con);
>> +
>> +       /*LEV0 LEV1 LEV2 interrupt enable */
>> +       exynos4_tmu_writel(tmu_dev, INTEN, (INTEN0|INTEN1|INTEN2));
>> +       usleep_range(200, 1000);
>> +       cur_temp = exynos4_tmu_current_temp(tmu_dev);
>> +       pr_info("Current Temperature : %dc\n\n", cur_temp);
>> +}
>> +/**
>> + * exynos4_tmu_irq - IRQ handler for tmu interrupt.
>> + * @irq: IRQ line.
>> + * @id:        void data pointer for the tmu device.
>> + *
>> + * IRQ handler for  interrupt.
>> +*/
>> +static irqreturn_t exynos4_tmu_irq(int irq, void *id)
>> +{
>> +       struct tmu_platform_device *tmu_dev = (struct tmu_platform_device *)id;
>> +       unsigned int status;
>> +
>> +       disable_irq_nosync(irq);
>> +
>> +       status = __raw_readl(tmu_dev->tmu_base + INTSTAT);
>> +
>> +       if (status & INTSTAT0) {
>> +               pr_info("Monitor interrupt occured!!!!\n");
>> +               __raw_writel(INTCLEAR0, tmu_dev->tmu_base + INTCLEAR);
>> +               tmu_dev->data.tmu_flag = TMU_STATUS_MONITOR;
>> +               queue_delayed_work_on(0, tmu_wq, &tmu_work,
>> +                                                usecs_to_jiffies(10 * 1000));
>> +       } else if (status & INTSTAT1) {
>> +               pr_info("Warning interrupt occured!!!!\n");
>> +               __raw_writel(INTCLEAR1, tmu_dev->tmu_base + INTCLEAR);
>> +               tmu_dev->data.tmu_flag = TMU_STATUS_WARNING;
>> +               queue_delayed_work_on(0, tmu_wq, &tmu_work,
>> +                                                usecs_to_jiffies(10 * 1000));
>> +       } else if (status & INTSTAT2) {
>> +               pr_info("Panic interrupt occured!!!!\n");
>> +               __raw_writel(INTCLEAR2, tmu_dev->tmu_base + INTCLEAR);
>> +               tmu_dev->data.tmu_flag = TMU_STATUS_PANIC;
>> +               queue_delayed_work_on(0, tmu_wq, &tmu_work,
>> +                                                usecs_to_jiffies(10 * 1000));
>> +       } else {
>> +               pr_err("%s: TMU interrupt error\n", __func__);
>> +               return -ENODEV;
>> +       }
>> +
>> +       return IRQ_HANDLED;
>> +}
>> +
>> +/**
>> + * exynos4_tmu_probe - device probe entry.
>> + * @pdev: The device being probed.
>> + */
>> +static int __devinit exynos4_tmu_probe(struct platform_device *pdev)
>> +{
>> +       struct tmu_platform_device *tmu_dev = platform_get_drvdata(pdev);
>> +       struct resource *res;
>> +       int ret;
>> +
>> +       pr_debug("%s: probe=%p\n", __func__, pdev);
>> +       irq_tmu = platform_get_irq(pdev, 0);
>> +       if (irq_tmu < 0) {
>> +               dev_err(&pdev->dev, "no irq for thermal monitor\n");
>> +               return -ENOENT;
>> +       }
>> +
>> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +       if (res == NULL) {
>> +               dev_err(&pdev->dev, "failed to get memory region resource\n");
>> +               return -ENOENT;
>> +       }
>> +
>> +       exynos4_tmu_mem = request_mem_region(res->start,
>> +                                       res->end-res->start+1,
>> +                                       pdev->name);
>> +       if (exynos4_tmu_mem == NULL) {
>> +               dev_err(&pdev->dev, "failed to reserve memory region\n");
>> +               ret = -ENOENT;
>> +               goto err_nores;
>> +       }
>> +
>> +       tmu_dev->tmu_base = ioremap(res->start, (res->end - res->start) + 1);
>> +       if (tmu_dev->tmu_base == NULL) {
>> +               dev_err(&pdev->dev, "failed ioremap()\n");
>> +               ret = -EINVAL;
>> +               goto err_nomap;
>> +       }
>> +       tmu_dev->pdev = pdev;
>> +       tmu_dev->dev = &pdev->dev;
>> +
>> +       ret = request_irq(irq_tmu, exynos4_tmu_irq,
>> +                       IRQF_DISABLED,  "exynos4-tmu ", tmu_dev);
>> +       if (ret) {
>> +               dev_err(&pdev->dev, "IRQ%d error %d\n", irq_tmu, ret);
>> +               goto err_irq;
>> +       }
>> +
>> +       tmu_dev_t = tmu_dev;
>> +
>> +       exynos4_tmu_initialize(pdev);
>> +       if (ret)
>> +               goto err_nores;
>> +
>> +       exynos4_tmu_start(pdev);
>> +
>> +       exynos4_read_sensor = exynos4_tmu_read_temp;
>> +
>> +       return 0;
>> +err_irq:
>> +       iounmap(tmu_dev->tmu_base);
>> +err_nomap:
>> +       release_resource(exynos4_tmu_mem);
>> +err_nores:
>> +       return ret;
>> +}
>> +
>> +/**
>> + * exynos4_tmu_remove - device remove entry.
>> + * @pdev: The device being removed.
>> + */
>> +static int __devinit exynos4_tmu_remove(struct platform_device *pdev)
>> +{
>> +       struct tmu_platform_device *tmu_dev = platform_get_drvdata(pdev);
>> +       free_irq(irq_tmu, tmu_dev);
>> +       iounmap(tmu_dev->tmu_base);
>> +       release_resource(exynos4_tmu_mem);
>> +       return 0;
>> +}
>> +
>> +#ifdef CONFIG_PM
>> +/**
>> + * exynos4_tmu_suspend - device suspend function.
>> + * @pdev: The device being suspended.
>> + * @state: suspend event message.
>> + */
>> +static int exynos4_tmu_suspend(struct platform_device *pdev, pm_message_t state)
>> +{
>> +       struct tmu_platform_device *tmu_dev = platform_get_drvdata(pdev);
>> +
>> +       tmu_register_save.regval_thresh_temp =
>> +                       exynos4_tmu_readb(tmu_dev, THRESHOLD_TEMP);
>> +       tmu_register_save.regval_trig_lev0 =
>> +                       exynos4_tmu_readb(tmu_dev, TRG_LEV0);
>> +       tmu_register_save.regval_trig_lev1 =
>> +                       exynos4_tmu_readb(tmu_dev, TRG_LEV1);
>> +       tmu_register_save.regval_trig_lev2 =
>> +                       exynos4_tmu_readb(tmu_dev, TRG_LEV2);
>> +       tmu_register_save.regval_trig_lev3 =
>> +                       exynos4_tmu_readb(tmu_dev, TRG_LEV3);
>> +       tmu_register_save.regval_tmu_con0 =
>> +                       exynos4_tmu_readl(tmu_dev, TMU_CON0);
>> +       tmu_register_save.regval_int_en =
>> +                       exynos4_tmu_readl(tmu_dev, INTEN);
>> +       return 0;
>> +}
>> +
>> +/**
>> + * exynos4_tmu_resume - device resume function.
>> + * @pdev: The device being suspended.
>> + */
>> +static int exynos4_tmu_resume(struct platform_device *pdev)
>> +{
>> +       struct tmu_platform_device *tmu_dev = platform_get_drvdata(pdev);
>> +
>> +       exynos4_tmu_writeb(tmu_dev, THRESHOLD_TEMP,
>> +                               tmu_register_save.regval_thresh_temp);
>> +       exynos4_tmu_writeb(tmu_dev, TRG_LEV0,
>> +                               tmu_register_save.regval_trig_lev0);
>> +       exynos4_tmu_writeb(tmu_dev, TRG_LEV1,
>> +                               tmu_register_save.regval_trig_lev1);
>> +       exynos4_tmu_writeb(tmu_dev, TRG_LEV2,
>> +                               tmu_register_save.regval_trig_lev2);
>> +       exynos4_tmu_writeb(tmu_dev, TRG_LEV3,
>> +                               tmu_register_save.regval_trig_lev3);
>> +       exynos4_tmu_writeb(tmu_dev, TMU_CON0,
>> +                               tmu_register_save.regval_tmu_con0);
>> +       exynos4_tmu_writel(tmu_dev, INTEN,
>> +                               tmu_register_save.regval_int_en);
>> +       exynos4_tmu_writel(tmu_dev, INTCLEAR,
>> +                               INTCLEAR0|INTCLEAR1|INTCLEAR2|INTCLEAR3);
>> +       return 0;
>> +}
>> +
>> +#else
>> +#define exynos4_tmu_suspend    NULL
>> +#define exynos4_tmu_resume     NULL
>> +#endif
>> +
>> +static struct platform_driver exynos4_tmu_driver = {
>> +       .probe          = exynos4_tmu_probe,
>> +       .remove         = exynos4_tmu_remove,
>> +       .suspend        = exynos4_tmu_suspend,
>> +       .resume         = exynos4_tmu_resume,
>> +       .driver         = {
>> +                       .name   = "exynos4-tmu",
>> +                       .owner  = THIS_MODULE,
>> +                       },
>> +};
>> +
>> +static int __init exynos4_tmu_driver_init(void)
>> +{
>> +       int ret = 0;
>> +       pr_info("EXYNOS4 TMU driver is loaded.\n");
>> +       tmu_wq = create_workqueue("tmu");
>> +       if (!tmu_wq) {
>> +               pr_err(" creation of tmu failed\n");
>> +               ret = -EFAULT;
>> +               goto out;
>> +       }
>> +       INIT_DELAYED_WORK_DEFERRABLE(&tmu_work, tmu_monitor);
>> +       ret = platform_driver_register(&exynos4_tmu_driver);
>> +       if (ret) {
>> +               pr_err("registeration of tmu_driver failed\n");
>> +               destroy_workqueue(tmu_wq);
>> +               goto out;
>> +       }
>> +out:
>> +       return ret;
>> +}
>> +
>> +static void __exit exynos4_tmu_driver_exit(void)
>> +{
>> +       destroy_workqueue(tmu_wq);
>> +       platform_driver_unregister(&exynos4_tmu_driver);
>> +}
>> +
>> +module_init(exynos4_tmu_driver_init);
>> +module_exit(exynos4_tmu_driver_exit);
>> +
>> +MODULE_AUTHOR("Samsung Electronics");
>> +MODULE_DESCRIPTION("EXYNOS4 temperature sensor driver");
>> +MODULE_LICENSE("GPL");
>> +MODULE_ALIAS("platform:exynos4-tmu");
>> --
>> 1.7.1
>>
>>
>> _______________________________________________
>> linaro-dev mailing list
>> linaro-dev@lists.linaro.org
>> http://lists.linaro.org/mailman/listinfo/linaro-dev
>>
>
diff mbox

Patch

diff --git a/arch/arm/mach-exynos4/include/mach/exynos4-tmu.h b/arch/arm/mach-exynos4/include/mach/exynos4-tmu.h
new file mode 100644
index 0000000..dc7bf37
--- /dev/null
+++ b/arch/arm/mach-exynos4/include/mach/exynos4-tmu.h
@@ -0,0 +1,75 @@ 
+/**
+ * exynos4-tmu.h - definitions of exynos4 specific thermal interface
+ *
+ *   Copyright (C) 2011 Samsung Electronics Co. ltd.
+
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _EXYNOS4_TMU_H
+#define _EXYNOS4_TMU_H
+
+/**
+ * tmu_data - tmu driver private structure
+ * @t1: colling stop temperature.
+ * @dc_temp: Temperature base.
+ * @init_threshold: Initial threshold temperature.
+ * @mode: Compensation mode.
+ * @comp_threshold: compensated threshold temperature.
+ *
+ */
+struct tmu_data {
+	char	te1;
+	char	te2;
+	unsigned int	init_threshold;
+	int		mode;
+	int		cooling;
+	unsigned int	comp_threshold;
+	int		tmu_flag;
+};
+
+/**
+ * tmu_platform_device - tmu platform specific structure
+ * @id: Device id.
+ * @tmu_base: Memory register base.
+ * @dev: TMU device.
+ * @data: TMU driver private data structure.
+ *
+ */
+struct tmu_platform_device {
+	int			id;
+	struct platform_device *pdev;
+	void __iomem		*tmu_base;
+	struct device		*dev;
+	struct tmu_data		data;
+	struct thermal_dev	*therm_fw;
+};
+
+#ifdef CONFIG_PM
+struct exynos4_tmu_register {
+	unsigned int regval_thresh_temp;
+	unsigned int regval_trig_lev0;
+	unsigned int regval_trig_lev1;
+	unsigned int regval_trig_lev2;
+	unsigned int regval_trig_lev3;
+	unsigned int regval_tmu_con0;
+	unsigned int regval_int_en;
+};
+#endif
+
+extern void exynos4_tmu_set_platdata(struct tmu_data *pd);
+extern struct tmu_platform_device *exynos4_tmu_get_platdata(void);
+extern int exynos4_tmu_get_irqno(int num);
+#endif /* _EXYNOS4_TMU_H */
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 2582e18..4376fed 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -148,4 +148,6 @@  source "drivers/staging/mei/Kconfig"
 
 source "drivers/staging/nvec/Kconfig"
 
+source "drivers/staging/thermal_exynos4/Kconfig"
+
 endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index 50d112f..24a2943 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -65,3 +65,4 @@  obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4)	+= ste_rmi4/
 obj-$(CONFIG_DRM_PSB)		+= gma500/
 obj-$(CONFIG_INTEL_MEI)		+= mei/
 obj-$(CONFIG_MFD_NVEC)		+= nvec/
+obj-$(CONFIG_SAMSUNG_THERMAL_INTERFACE) += thermal_exynos4/
diff --git a/drivers/staging/thermal_exynos4/sensor/Kconfig b/drivers/staging/thermal_exynos4/sensor/Kconfig
new file mode 100644
index 0000000..dcdd04b
--- /dev/null
+++ b/drivers/staging/thermal_exynos4/sensor/Kconfig
@@ -0,0 +1,14 @@ 
+#
+# Thermal Temp sensor configuration
+#
+
+config SENSORS_EXYNOS4_TMU
+	tristate "Samsung thermal sensor for exynos platform"
+	depends on ARCH_EXYNOS4
+	default n
+	help
+	  Say Y here if you want to enable thermal sensor for samsung
+	  exynos platform.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called exynos4210-tmu.
diff --git a/drivers/staging/thermal_exynos4/sensor/Makefile b/drivers/staging/thermal_exynos4/sensor/Makefile
new file mode 100644
index 0000000..4cdb818
--- /dev/null
+++ b/drivers/staging/thermal_exynos4/sensor/Makefile
@@ -0,0 +1,4 @@ 
+#
+# Makefile for sensor chip drivers.
+#
+obj-$(CONFIG_SENSORS_EXYNOS4_TMU)+= exynos4210_tmu.o
diff --git a/drivers/staging/thermal_exynos4/sensor/exynos4210_tmu.c b/drivers/staging/thermal_exynos4/sensor/exynos4210_tmu.c
new file mode 100644
index 0000000..b125379
--- /dev/null
+++ b/drivers/staging/thermal_exynos4/sensor/exynos4210_tmu.c
@@ -0,0 +1,465 @@ 
+/**
+ * exynos4210_tmu.c - exynos4 thermal sensor driver
+ *
+ * Copyright (C) 2011 Samsung Electronics Co. ltd.
+ *    Author: SangWook Ju <sw.ju@samsung.com>
+ *    Author: Amit Daniel Kachhap <amit.kachhap@linaro.org>
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/sysfs.h>
+#include <linux/slab.h>
+#include <asm/irq.h>
+#include <mach/regs-tmu.h>
+#include <mach/exynos4-tmu.h>
+#include <mach/thermal_interface.h>
+
+#define INIT_THRESHOLD	80
+/*Monitor temp = INIT_THRESHOLD + TRIGGER_LEV0 = 85*/
+#define	TRIGGER_LEV0 5
+/*Warning temp = INIT_THRESHOLD + TRIGGER_LEV1 = 100*/
+#define	TRIGGER_LEV1 20
+/*Panic temp = INIT_THRESHOLD + TRIGGER_LEV2 = 110*/
+#define	TRIGGER_LEV2 30
+#define	TRIGGER_LEV3 0xFF
+
+#define EFUSE_MIN_VALUE 60
+#define EFUSE_AVG_VALUE 80
+#define EFUSE_MAX_VALUE 100
+
+#define VREF_SLOPE		0x07000F02
+#define TMU_EN			0x1
+#define TMU_DC_VALUE		25
+
+enum tmu_status_t {
+	TMU_STATUS_NORMAL = 0,
+	TMU_STATUS_MONITOR,
+	TMU_STATUS_WARNING,
+	TMU_STATUS_PANIC,
+};
+
+static struct resource *exynos4_tmu_mem;
+static struct workqueue_struct  *tmu_wq;
+static struct delayed_work tmu_work;
+
+#ifdef CONFIG_PM
+static struct exynos4_tmu_register tmu_register_save;
+#endif
+
+static int irq_tmu = NO_IRQ;
+static struct tmu_platform_device *tmu_dev_t;
+
+/**
+ * exynos4_tmu_readb - read a byte value from the given register.
+ * @tmu_dev: The tmu device structure.
+ * @reg: Register.
+ *
+ * Read a byte value from the given register.
+ */
+static unsigned int exynos4_tmu_readb(struct tmu_platform_device *tmu_dev,
+						unsigned int reg)
+{
+	return __raw_readb(tmu_dev->tmu_base + reg);
+}
+
+/**
+ * exynos4_tmu_writeb - write a byte value to the given register.
+ * @tmu_dev: The tmu device structure.
+ * @reg: Register.
+ * @val: Value to write.
+ *
+ * write a byte value to the given register.
+ */
+static void exynos4_tmu_writeb(struct tmu_platform_device *tmu_dev,
+					unsigned int reg, unsigned int val)
+{
+	__raw_writeb(val, tmu_dev->tmu_base + reg);
+}
+
+/**
+ * exynos4_tmu_readl - Read a long value from the given register.
+ * @tmu_dev: The tmu device structure.
+ * @reg: Register.
+ *
+ * Read a long value from the given register.
+ */
+static unsigned int exynos4_tmu_readl(struct tmu_platform_device *tmu_dev,
+						unsigned int reg)
+{
+	return __raw_readl(tmu_dev->tmu_base + reg);
+}
+
+/**
+ * exynos4_tmu_writel - write a long value to the given register.
+ * @tmu_dev: The tmu device structure.
+ * @reg: Register.
+ * @val: Value to write.
+ *
+ * write a long value to the given register.
+ */
+static void exynos4_tmu_writel(struct tmu_platform_device *tmu_dev,
+					unsigned int reg, unsigned int val)
+{
+	__raw_writel(val, tmu_dev->tmu_base + reg);
+}
+
+
+static int exynos4_tmu_current_temp(struct tmu_platform_device *tmu_dev)
+{
+	int temp;
+	temp = exynos4_tmu_readl(tmu_dev, CURRENT_TEMP)
+			- tmu_dev->data.te1 + TMU_DC_VALUE;
+	return temp;
+}
+
+/**
+ * tmu_monitor - Timer handler for monitoring interrupt.
+ * @work: temp work.
+ *
+ * Timer handler for monitor/warning/panic interrupts.
+*/
+
+static void tmu_monitor(struct work_struct *work)
+{
+	exynos4_report_trigger();
+	enable_irq(irq_tmu);
+}
+
+int exynos4_tmu_read_temp(int *temp)
+{
+	*temp = 0;
+	if (tmu_dev_t)
+		*temp = exynos4_tmu_readl(tmu_dev_t, CURRENT_TEMP)
+			- tmu_dev_t->data.te1 + TMU_DC_VALUE;
+	if (*temp > 0)
+		return 0;
+	else
+		return -EINVAL;
+}
+
+/*
+ * exynos4_tmu_initialize - Initializes the TMU driver.
+ * @pdev: The platform device structure.
+ *
+ * Initialises the different registers of the temperature sensor.
+ */
+static int exynos4_tmu_initialize(struct platform_device *pdev)
+{
+	struct tmu_platform_device *tmu_dev = platform_get_drvdata(pdev);
+	unsigned int en;
+	unsigned int te_temp;
+	unsigned int t_new;
+
+	en = (exynos4_tmu_readb(tmu_dev, TMU_STATUS) & 0x1);
+	if (!en) {
+		dev_err(&pdev->dev, "failed to start tmu drvier\n");
+		return -ENOENT;
+	}
+
+	/* Temperature compensation */
+	te_temp = exynos4_tmu_readl(tmu_dev, TRIMINFO);
+	tmu_dev->data.te1 = te_temp  & TRIM_TEMP_MASK;
+	tmu_dev->data.te2 = ((te_temp >> 8) & TRIM_TEMP_MASK);
+
+	if ((EFUSE_MIN_VALUE > tmu_dev->data.te1) ||
+			(tmu_dev->data.te1 > EFUSE_MAX_VALUE) ||
+			(tmu_dev->data.te2 != 0))
+		tmu_dev->data.te1 = EFUSE_AVG_VALUE;
+
+	t_new =  INIT_THRESHOLD + tmu_dev->data.te1 - TMU_DC_VALUE;
+
+	exynos4_tmu_writel(tmu_dev, THRESHOLD_TEMP, t_new);
+
+	/* Need to initialize regsiter setting after getting parameter info */
+	/* [28:23] vref [11:8] slope - Tunning parameter */
+	exynos4_tmu_writel(tmu_dev, TMU_CON0, VREF_SLOPE);
+	exynos4_tmu_writeb(tmu_dev, TRG_LEV0, TRIGGER_LEV0);
+	exynos4_tmu_writeb(tmu_dev, TRG_LEV1, TRIGGER_LEV1);
+	exynos4_tmu_writeb(tmu_dev, TRG_LEV2, TRIGGER_LEV2);
+	exynos4_tmu_writeb(tmu_dev, TRG_LEV3, TRIGGER_LEV3);
+	pr_info("TMU is initialized !!\n");
+	pr_info("Threshold temp: %dc Monitor temp: %dc\n",
+			INIT_THRESHOLD,
+			INIT_THRESHOLD + exynos4_tmu_readb(tmu_dev, TRG_LEV0));
+	pr_info("Warning temp: %dc Panic temp: %dc\n",
+			INIT_THRESHOLD + exynos4_tmu_readb(tmu_dev, TRG_LEV1),
+			INIT_THRESHOLD + exynos4_tmu_readb(tmu_dev, TRG_LEV2));
+	pr_info("Cooling temp: %dc\n",
+			tmu_dev->data.cooling);
+}
+
+/**
+ * exynos4_tmu_start - Triggers the TMU driver.
+ * @pdev: The platform device structure.
+ *
+ * Triggers the temperature sensor.
+ */
+static void exynos4_tmu_start(struct platform_device *pdev)
+{
+	struct tmu_platform_device *tmu_dev = platform_get_drvdata(pdev);
+	unsigned int con, cur_temp;
+	exynos4_tmu_writel(tmu_dev, INTCLEAR,
+			INTCLEAR0|INTCLEAR1|INTCLEAR2|INTCLEAR3);
+
+	/* TMU core enable */
+	con = exynos4_tmu_readl(tmu_dev, TMU_CON0);
+	con |= TMU_EN;
+	exynos4_tmu_writel(tmu_dev, TMU_CON0, con);
+
+	/*LEV0 LEV1 LEV2 interrupt enable */
+	exynos4_tmu_writel(tmu_dev, INTEN, (INTEN0|INTEN1|INTEN2));
+	usleep_range(200, 1000);
+	cur_temp = exynos4_tmu_current_temp(tmu_dev);
+	pr_info("Current Temperature : %dc\n\n", cur_temp);
+}
+/**
+ * exynos4_tmu_irq - IRQ handler for tmu interrupt.
+ * @irq: IRQ line.
+ * @id:	void data pointer for the tmu device.
+ *
+ * IRQ handler for  interrupt.
+*/
+static irqreturn_t exynos4_tmu_irq(int irq, void *id)
+{
+	struct tmu_platform_device *tmu_dev = (struct tmu_platform_device *)id;
+	unsigned int status;
+
+	disable_irq_nosync(irq);
+
+	status = __raw_readl(tmu_dev->tmu_base + INTSTAT);
+
+	if (status & INTSTAT0) {
+		pr_info("Monitor interrupt occured!!!!\n");
+		__raw_writel(INTCLEAR0, tmu_dev->tmu_base + INTCLEAR);
+		tmu_dev->data.tmu_flag = TMU_STATUS_MONITOR;
+		queue_delayed_work_on(0, tmu_wq, &tmu_work,
+						 usecs_to_jiffies(10 * 1000));
+	} else if (status & INTSTAT1) {
+		pr_info("Warning interrupt occured!!!!\n");
+		__raw_writel(INTCLEAR1, tmu_dev->tmu_base + INTCLEAR);
+		tmu_dev->data.tmu_flag = TMU_STATUS_WARNING;
+		queue_delayed_work_on(0, tmu_wq, &tmu_work,
+						 usecs_to_jiffies(10 * 1000));
+	} else if (status & INTSTAT2) {
+		pr_info("Panic interrupt occured!!!!\n");
+		__raw_writel(INTCLEAR2, tmu_dev->tmu_base + INTCLEAR);
+		tmu_dev->data.tmu_flag = TMU_STATUS_PANIC;
+		queue_delayed_work_on(0, tmu_wq, &tmu_work,
+						 usecs_to_jiffies(10 * 1000));
+	} else {
+		pr_err("%s: TMU interrupt error\n", __func__);
+		return -ENODEV;
+	}
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * exynos4_tmu_probe - device probe entry.
+ * @pdev: The device being probed.
+ */
+static int __devinit exynos4_tmu_probe(struct platform_device *pdev)
+{
+	struct tmu_platform_device *tmu_dev = platform_get_drvdata(pdev);
+	struct resource *res;
+	int ret;
+
+	pr_debug("%s: probe=%p\n", __func__, pdev);
+	irq_tmu = platform_get_irq(pdev, 0);
+	if (irq_tmu < 0) {
+		dev_err(&pdev->dev, "no irq for thermal monitor\n");
+		return -ENOENT;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "failed to get memory region resource\n");
+		return -ENOENT;
+	}
+
+	exynos4_tmu_mem = request_mem_region(res->start,
+					res->end-res->start+1,
+					pdev->name);
+	if (exynos4_tmu_mem == NULL) {
+		dev_err(&pdev->dev, "failed to reserve memory region\n");
+		ret = -ENOENT;
+		goto err_nores;
+	}
+
+	tmu_dev->tmu_base = ioremap(res->start, (res->end - res->start) + 1);
+	if (tmu_dev->tmu_base == NULL) {
+		dev_err(&pdev->dev, "failed ioremap()\n");
+		ret = -EINVAL;
+		goto err_nomap;
+	}
+	tmu_dev->pdev = pdev;
+	tmu_dev->dev = &pdev->dev;
+
+	ret = request_irq(irq_tmu, exynos4_tmu_irq,
+			IRQF_DISABLED,  "exynos4-tmu ", tmu_dev);
+	if (ret) {
+		dev_err(&pdev->dev, "IRQ%d error %d\n", irq_tmu, ret);
+		goto err_irq;
+	}
+
+	tmu_dev_t = tmu_dev;
+
+	exynos4_tmu_initialize(pdev);
+	if (ret)
+		goto err_nores;
+
+	exynos4_tmu_start(pdev);
+
+	exynos4_read_sensor = exynos4_tmu_read_temp;
+
+	return 0;
+err_irq:
+	iounmap(tmu_dev->tmu_base);
+err_nomap:
+	release_resource(exynos4_tmu_mem);
+err_nores:
+	return ret;
+}
+
+/**
+ * exynos4_tmu_remove - device remove entry.
+ * @pdev: The device being removed.
+ */
+static int __devinit exynos4_tmu_remove(struct platform_device *pdev)
+{
+	struct tmu_platform_device *tmu_dev = platform_get_drvdata(pdev);
+	free_irq(irq_tmu, tmu_dev);
+	iounmap(tmu_dev->tmu_base);
+	release_resource(exynos4_tmu_mem);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+/**
+ * exynos4_tmu_suspend - device suspend function.
+ * @pdev: The device being suspended.
+ * @state: suspend event message.
+ */
+static int exynos4_tmu_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct tmu_platform_device *tmu_dev = platform_get_drvdata(pdev);
+
+	tmu_register_save.regval_thresh_temp =
+			exynos4_tmu_readb(tmu_dev, THRESHOLD_TEMP);
+	tmu_register_save.regval_trig_lev0 =
+			exynos4_tmu_readb(tmu_dev, TRG_LEV0);
+	tmu_register_save.regval_trig_lev1 =
+			exynos4_tmu_readb(tmu_dev, TRG_LEV1);
+	tmu_register_save.regval_trig_lev2 =
+			exynos4_tmu_readb(tmu_dev, TRG_LEV2);
+	tmu_register_save.regval_trig_lev3 =
+			exynos4_tmu_readb(tmu_dev, TRG_LEV3);
+	tmu_register_save.regval_tmu_con0 =
+			exynos4_tmu_readl(tmu_dev, TMU_CON0);
+	tmu_register_save.regval_int_en =
+			exynos4_tmu_readl(tmu_dev, INTEN);
+	return 0;
+}
+
+/**
+ * exynos4_tmu_resume - device resume function.
+ * @pdev: The device being suspended.
+ */
+static int exynos4_tmu_resume(struct platform_device *pdev)
+{
+	struct tmu_platform_device *tmu_dev = platform_get_drvdata(pdev);
+
+	exynos4_tmu_writeb(tmu_dev, THRESHOLD_TEMP,
+				tmu_register_save.regval_thresh_temp);
+	exynos4_tmu_writeb(tmu_dev, TRG_LEV0,
+				tmu_register_save.regval_trig_lev0);
+	exynos4_tmu_writeb(tmu_dev, TRG_LEV1,
+				tmu_register_save.regval_trig_lev1);
+	exynos4_tmu_writeb(tmu_dev, TRG_LEV2,
+				tmu_register_save.regval_trig_lev2);
+	exynos4_tmu_writeb(tmu_dev, TRG_LEV3,
+				tmu_register_save.regval_trig_lev3);
+	exynos4_tmu_writeb(tmu_dev, TMU_CON0,
+				tmu_register_save.regval_tmu_con0);
+	exynos4_tmu_writel(tmu_dev, INTEN,
+				tmu_register_save.regval_int_en);
+	exynos4_tmu_writel(tmu_dev, INTCLEAR,
+				INTCLEAR0|INTCLEAR1|INTCLEAR2|INTCLEAR3);
+	return 0;
+}
+
+#else
+#define exynos4_tmu_suspend	NULL
+#define exynos4_tmu_resume	NULL
+#endif
+
+static struct platform_driver exynos4_tmu_driver = {
+	.probe		= exynos4_tmu_probe,
+	.remove		= exynos4_tmu_remove,
+	.suspend	= exynos4_tmu_suspend,
+	.resume		= exynos4_tmu_resume,
+	.driver		= {
+			.name	= "exynos4-tmu",
+			.owner	= THIS_MODULE,
+			},
+};
+
+static int __init exynos4_tmu_driver_init(void)
+{
+	int ret = 0;
+	pr_info("EXYNOS4 TMU driver is loaded.\n");
+	tmu_wq = create_workqueue("tmu");
+	if (!tmu_wq) {
+		pr_err(" creation of tmu failed\n");
+		ret = -EFAULT;
+		goto out;
+	}
+	INIT_DELAYED_WORK_DEFERRABLE(&tmu_work, tmu_monitor);
+	ret = platform_driver_register(&exynos4_tmu_driver);
+	if (ret) {
+		pr_err("registeration of tmu_driver failed\n");
+		destroy_workqueue(tmu_wq);
+		goto out;
+	}
+out:
+	return ret;
+}
+
+static void __exit exynos4_tmu_driver_exit(void)
+{
+	destroy_workqueue(tmu_wq);
+	platform_driver_unregister(&exynos4_tmu_driver);
+}
+
+module_init(exynos4_tmu_driver_init);
+module_exit(exynos4_tmu_driver_exit);
+
+MODULE_AUTHOR("Samsung Electronics");
+MODULE_DESCRIPTION("EXYNOS4 temperature sensor driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:exynos4-tmu");