This RFC version of the patch set is intended to share the current work about providing a thermal solution using Linux thermal infrastructure. The closest driver which has the same features and not using acpi layer is drivers/platform/x86/acerhdf.c.
For the ARM world there is no clarity for placing such files so currently I have placed the temperature sensor driver and a binding layer for cooling device, thermal zone and core thermal interfaces inside staging directory. Feel free to comment about the implementation, the directory structure and the shortcomings.
Amit Daniel Kachhap (4): ARM: Exynos4: Add thermal sensor driver for samsung exynos4 platform. ARM: Exynos4: Add thermal sensor driver platform device support ARM: Exynos4: Enable thermal sensor driver for origen board ARM: Exynos4: Add thermal interface support for linux thermal layer
arch/arm/mach-exynos4/Kconfig | 5 + arch/arm/mach-exynos4/Makefile | 3 +- arch/arm/mach-exynos4/dev-tmu.c | 71 +++ arch/arm/mach-exynos4/include/mach/exynos4-tmu.h | 75 ++++ arch/arm/mach-exynos4/include/mach/irqs.h | 3 + arch/arm/mach-exynos4/include/mach/map.h | 1 + arch/arm/mach-exynos4/include/mach/regs-tmu.h | 58 +++ .../mach-exynos4/include/mach/thermal_interface.h | 26 ++ arch/arm/mach-exynos4/mach-origen.c | 10 + arch/arm/plat-samsung/include/plat/devs.h | 2 + drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/thermal_exynos4/Kconfig | 12 + drivers/staging/thermal_exynos4/Makefile | 5 + drivers/staging/thermal_exynos4/sensor/Kconfig | 14 + drivers/staging/thermal_exynos4/sensor/Makefile | 4 + .../thermal_exynos4/sensor/exynos4210_tmu.c | 465 ++++++++++++++++++++ .../staging/thermal_exynos4/thermal_interface.c | 382 ++++++++++++++++ 18 files changed, 1138 insertions(+), 1 deletions(-) create mode 100644 arch/arm/mach-exynos4/dev-tmu.c create mode 100644 arch/arm/mach-exynos4/include/mach/exynos4-tmu.h create mode 100644 arch/arm/mach-exynos4/include/mach/regs-tmu.h create mode 100644 arch/arm/mach-exynos4/include/mach/thermal_interface.h create mode 100644 drivers/staging/thermal_exynos4/Kconfig create mode 100644 drivers/staging/thermal_exynos4/Makefile 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 create mode 100644 drivers/staging/thermal_exynos4/thermal_interface.c
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");
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
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
This patch adds necessary source definations needed for TMU driver and the platform device support.
Signed-off-by: SangWook Ju sw.ju@samsung.com Signed-off-by: Amit Daniel Kachhap amit.kachhap@linaro.org --- arch/arm/mach-exynos4/Makefile | 3 +- arch/arm/mach-exynos4/dev-tmu.c | 71 +++++++++++++++++++++++++ arch/arm/mach-exynos4/include/mach/irqs.h | 3 + arch/arm/mach-exynos4/include/mach/map.h | 1 + arch/arm/mach-exynos4/include/mach/regs-tmu.h | 58 ++++++++++++++++++++ arch/arm/plat-samsung/include/plat/devs.h | 2 + 6 files changed, 137 insertions(+), 1 deletions(-) create mode 100644 arch/arm/mach-exynos4/dev-tmu.c create mode 100644 arch/arm/mach-exynos4/include/mach/regs-tmu.h
diff --git a/arch/arm/mach-exynos4/Makefile b/arch/arm/mach-exynos4/Makefile index cbc9767..0a97fad 100644 --- a/arch/arm/mach-exynos4/Makefile +++ b/arch/arm/mach-exynos4/Makefile @@ -38,7 +38,8 @@ obj-y += dev-audio.o obj-$(CONFIG_EXYNOS4_DEV_AHCI) += dev-ahci.o obj-$(CONFIG_EXYNOS4_DEV_PD) += dev-pd.o obj-$(CONFIG_EXYNOS4_DEV_SYSMMU) += dev-sysmmu.o -obj-$(CONFIG_EXYNOS4_DEV_DWMCI) += dev-dwmci.o +obj-$(CONFIG_EXYNOS4_DEV_DWMCI) += dev-dwmci.o +obj-$(CONFIG_EXYNOS4_THERMAL) += dev-tmu.o
obj-$(CONFIG_EXYNOS4_SETUP_FIMC) += setup-fimc.o obj-$(CONFIG_EXYNOS4_SETUP_FIMD0) += setup-fimd0.o diff --git a/arch/arm/mach-exynos4/dev-tmu.c b/arch/arm/mach-exynos4/dev-tmu.c new file mode 100644 index 0000000..3aa7729 --- /dev/null +++ b/arch/arm/mach-exynos4/dev-tmu.c @@ -0,0 +1,71 @@ +/* linux/arch/arm/plat-exynos4/dev-tmu.c + * + * Copyright 2011 by SAMSUNG + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <asm/irq.h> + +#include <mach/irqs.h> +#include <mach/map.h> +#include <plat/devs.h> +#include <mach/exynos4-tmu.h> + +static struct resource exynos4_tmu_resource[] = { + [0] = { + .start = EXYNOS4_PA_TMU, + .end = EXYNOS4_PA_TMU + 0xFFFF - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_TMU_TRIG0, + .end = IRQ_TMU_TRIG0, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device exynos4_device_tmu = { + .name = "exynos4-tmu", + .id = -1, + .num_resources = ARRAY_SIZE(exynos4_tmu_resource), + .resource = exynos4_tmu_resource, +}; +EXPORT_SYMBOL(exynos4_device_tmu); + +static struct tmu_data default_tmu_data __initdata = { + .te1 = 0, /* cooling stop temp */ + .te2 = 0, /* cooling stop temp */ + .cooling = 84, /*Cooling temp*/ + .mode = 0, /* 0: 1-point compensation, 1: 2-point compensation */ +}; + +int exynos4_tmu_get_irqno(int num) +{ + return platform_get_irq(&exynos4_device_tmu, num); +} + +struct tmu_platform_device *exynos4_tmu_get_platdata(void) +{ + return platform_get_drvdata(&exynos4_device_tmu); +} + +void __init exynos4_tmu_set_platdata(struct tmu_data *pd) +{ + struct tmu_platform_device *npd; + npd = kmalloc(sizeof(struct tmu_platform_device), GFP_KERNEL); + if (!npd) + printk(KERN_ERR "%s: no memory for platform data\n", __func__); + if (!pd) + memcpy(&npd->data, &default_tmu_data, sizeof(struct tmu_data)); + else + memcpy(&npd->data, pd, sizeof(struct tmu_data)); + + platform_set_drvdata(&exynos4_device_tmu, npd); +} diff --git a/arch/arm/mach-exynos4/include/mach/irqs.h b/arch/arm/mach-exynos4/include/mach/irqs.h index f8952f8..c51dc97 100644 --- a/arch/arm/mach-exynos4/include/mach/irqs.h +++ b/arch/arm/mach-exynos4/include/mach/irqs.h @@ -119,6 +119,9 @@ #define COMBINER_GROUP(x) ((x) * MAX_IRQ_IN_COMBINER + IRQ_SPI(128)) #define COMBINER_IRQ(x, y) (COMBINER_GROUP(x) + y)
+#define IRQ_TMU_TRIG0 COMBINER_IRQ(2, 4) +#define IRQ_TMU_TRIG1 COMBINER_IRQ(3, 4) + #define IRQ_SYSMMU_MDMA0_0 COMBINER_IRQ(4, 0) #define IRQ_SYSMMU_SSS_0 COMBINER_IRQ(4, 1) #define IRQ_SYSMMU_FIMC0_0 COMBINER_IRQ(4, 2) diff --git a/arch/arm/mach-exynos4/include/mach/map.h b/arch/arm/mach-exynos4/include/mach/map.h index d32296d..75ac921 100644 --- a/arch/arm/mach-exynos4/include/mach/map.h +++ b/arch/arm/mach-exynos4/include/mach/map.h @@ -66,6 +66,7 @@ #define EXYNOS4_PA_COREPERI 0x10500000 #define EXYNOS4_PA_TWD 0x10500600 #define EXYNOS4_PA_L2CC 0x10502000 +#define EXYNOS4_PA_TMU 0x100C0000
#define EXYNOS4_PA_MDMA 0x10810000 #define EXYNOS4_PA_PDMA0 0x12680000 diff --git a/arch/arm/mach-exynos4/include/mach/regs-tmu.h b/arch/arm/mach-exynos4/include/mach/regs-tmu.h new file mode 100644 index 0000000..64ddc06 --- /dev/null +++ b/arch/arm/mach-exynos4/include/mach/regs-tmu.h @@ -0,0 +1,58 @@ +/* linux/arch/arm/mach-exynos4/include/mach/regs-tmu.h + +* Copyright (c) 2011 Samsung Electronics Co., Ltd. +* http://www.samsung.com/ +* +* EXYNOS4 - Clock register definitions +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +*/ + +#ifndef __ASM_ARCH_REGS_THERMAL_H +#define __ASM_ARCH_REGS_THERMAL_H __FILE__ + +/*Register definations*/ +#define TRIMINFO (0x0) +#define TRIMINFO_CONFIG (0x10) +#define TRIMINFO_CONTROL (0x14) +#define TMU_CON0 (0x20) +#define TMU_CON1 (0x24) +#define TMU_STATUS (0x28) +#define SAMPLING_INTERNAL (0x2C) +#define CNT_VALUE0 (0x30) +#define CNT_VALUE1 (0x34) +#define CURRENT_TEMP (0x40) +#define THRESHOLD_TEMP (0x44) +#define TRG_LEV0 (0x50) +#define TRG_LEV1 (0x54) +#define TRG_LEV2 (0x58) +#define TRG_LEV3 (0x5C) +#define PAST_TMEP0 (0x60) +#define PAST_TMEP1 (0x64) +#define PAST_TMEP2 (0x68) +#define PAST_TMEP3 (0x6C) +#define INTEN (0x70) +#define INTSTAT (0x74) +#define INTCLEAR (0x78) + +/*Register control bits*/ +#define INTEN0 (1) +#define INTEN1 (1<<4) +#define INTEN2 (1<<8) +#define INTEN3 (1<<12) + +#define TRIM_TEMP_MASK (0xFF) + +#define INTCLEAR0 (1) +#define INTCLEAR1 (1<<4) +#define INTCLEAR2 (1<<8) +#define INTCLEAR3 (1<<12) + +#define INTSTAT0 (1) +#define INTSTAT1 (1<<4) +#define INTSTAT2 (1<<8) +#define INTSTAT3 (1<<12) + +#endif diff --git a/arch/arm/plat-samsung/include/plat/devs.h b/arch/arm/plat-samsung/include/plat/devs.h index 24ebb1e..e7f46c6 100644 --- a/arch/arm/plat-samsung/include/plat/devs.h +++ b/arch/arm/plat-samsung/include/plat/devs.h @@ -149,6 +149,8 @@ extern struct platform_device s5p_device_ehci;
extern struct platform_device exynos4_device_sysmmu;
+extern struct platform_device exynos4_device_tmu; + /* s3c2440 specific devices */
#ifdef CONFIG_CPU_S3C2440
Signed-off-by: Amit Daniel Kachhap amit.kachhap@linaro.org --- arch/arm/mach-exynos4/Kconfig | 5 +++++ arch/arm/mach-exynos4/mach-origen.c | 10 ++++++++++ 2 files changed, 15 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-exynos4/Kconfig b/arch/arm/mach-exynos4/Kconfig index cf467b5..786fdb7 100644 --- a/arch/arm/mach-exynos4/Kconfig +++ b/arch/arm/mach-exynos4/Kconfig @@ -108,6 +108,11 @@ config EXYNOS4_SETUP_USB_PHY help Common setup code for USB PHY controller
+config EXYNOS4_THERMAL + bool + depends on SENSORS_EXYNOS4_TMU + default y + # machine support
menu "EXYNOS4 Machines" diff --git a/arch/arm/mach-exynos4/mach-origen.c b/arch/arm/mach-exynos4/mach-origen.c index ed59f86..089b0e3 100644 --- a/arch/arm/mach-exynos4/mach-origen.c +++ b/arch/arm/mach-exynos4/mach-origen.c @@ -24,6 +24,10 @@ #include <plat/devs.h> #include <plat/sdhci.h> #include <plat/iic.h> +#if defined(CONFIG_EXYNOS4_THERMAL) +#include <mach/exynos4-tmu.h> +#include <mach/regs-tmu.h> +#endif
#include <mach/map.h>
@@ -83,6 +87,9 @@ static struct platform_device *origen_devices[] __initdata = { &s3c_device_hsmmc2, &s3c_device_rtc, &s3c_device_wdt, +#ifdef CONFIG_EXYNOS4_THERMAL + &exynos4_device_tmu, +#endif };
static void __init origen_map_io(void) @@ -95,6 +102,9 @@ static void __init origen_map_io(void) static void __init origen_machine_init(void) { s3c_sdhci2_set_platdata(&origen_hsmmc2_pdata); +#ifdef CONFIG_EXYNOS4_THERMAL + exynos4_tmu_set_platdata(NULL); +#endif platform_add_devices(origen_devices, ARRAY_SIZE(origen_devices)); }
Please split the two patches. 1. Add TMU Kconfig 2. board support
On Wed, Oct 26, 2011 at 8:36 PM, Amit Daniel Kachhap amit.kachhap@linaro.org wrote:
Signed-off-by: Amit Daniel Kachhap amit.kachhap@linaro.org
arch/arm/mach-exynos4/Kconfig | 5 +++++ arch/arm/mach-exynos4/mach-origen.c | 10 ++++++++++ 2 files changed, 15 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-exynos4/Kconfig b/arch/arm/mach-exynos4/Kconfig index cf467b5..786fdb7 100644 --- a/arch/arm/mach-exynos4/Kconfig +++ b/arch/arm/mach-exynos4/Kconfig @@ -108,6 +108,11 @@ config EXYNOS4_SETUP_USB_PHY help Common setup code for USB PHY controller
+config EXYNOS4_THERMAL
- bool
- depends on SENSORS_EXYNOS4_TMU
- default y
# machine support
menu "EXYNOS4 Machines" diff --git a/arch/arm/mach-exynos4/mach-origen.c b/arch/arm/mach-exynos4/mach-origen.c index ed59f86..089b0e3 100644 --- a/arch/arm/mach-exynos4/mach-origen.c +++ b/arch/arm/mach-exynos4/mach-origen.c @@ -24,6 +24,10 @@ #include <plat/devs.h> #include <plat/sdhci.h> #include <plat/iic.h> +#if defined(CONFIG_EXYNOS4_THERMAL)
Do you this ifdef?
+#include <mach/exynos4-tmu.h> +#include <mach/regs-tmu.h> +#endif
#include <mach/map.h>
@@ -83,6 +87,9 @@ static struct platform_device *origen_devices[] __initdata = { &s3c_device_hsmmc2, &s3c_device_rtc, &s3c_device_wdt, +#ifdef CONFIG_EXYNOS4_THERMAL
If you select the this config at origen kconfig. it's not needed.
- &exynos4_device_tmu,
+#endif };
static void __init origen_map_io(void) @@ -95,6 +102,9 @@ static void __init origen_map_io(void) static void __init origen_machine_init(void) { s3c_sdhci2_set_platdata(&origen_hsmmc2_pdata); +#ifdef CONFIG_EXYNOS4_THERMAL
- exynos4_tmu_set_platdata(NULL);
+#endif
you can use it exynos4_tmu_set_platdata by adding proper ifdef at header file instead of C file
platform_add_devices(origen_devices, ARRAY_SIZE(origen_devices)); }
-- 1.7.1
linaro-dev mailing list linaro-dev@lists.linaro.org http://lists.linaro.org/mailman/listinfo/linaro-dev
This codes uses the generic linux thermal layer and creates a bridge between temperature sensors, linux thermal framework and cooling devices for samsung exynos platform. This layer recieves or monitor the temperature from the sensor and informs the generic thermal layer. It also provides the handlers for the cooling devices.
Signed-off-by: Amit Daniel Kachhap amit.kachhap@linaro.org --- .../mach-exynos4/include/mach/thermal_interface.h | 26 ++ drivers/staging/thermal_exynos4/Kconfig | 12 + drivers/staging/thermal_exynos4/Makefile | 5 + .../staging/thermal_exynos4/thermal_interface.c | 382 ++++++++++++++++++++ 4 files changed, 425 insertions(+), 0 deletions(-) create mode 100644 arch/arm/mach-exynos4/include/mach/thermal_interface.h create mode 100644 drivers/staging/thermal_exynos4/Kconfig create mode 100644 drivers/staging/thermal_exynos4/Makefile create mode 100644 drivers/staging/thermal_exynos4/thermal_interface.c
diff --git a/arch/arm/mach-exynos4/include/mach/thermal_interface.h b/arch/arm/mach-exynos4/include/mach/thermal_interface.h new file mode 100644 index 0000000..b228033 --- /dev/null +++ b/arch/arm/mach-exynos4/include/mach/thermal_interface.h @@ -0,0 +1,26 @@ +/* linux/drivers/staging/thermal_exynos4/thermal_interface.h + * + * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#ifndef THERMAL_INTERFACE_H +#define THERMAL_INTERFACE_H +/* CPU Zone information */ +#define PANIC_ZONE 4 +#define WARN_ZONE 3 +#define MONITOR_ZONE 2 +#define SAFE_ZONE 1 +#define NO_ACTION 0 + +#define EXYNOS_PANIC_TEMP 110 +#define EXYNOS_WARN_TEMP 100 +#define EXYNOS_MONITOR_TEMP 85 + +extern void exynos4_report_trigger(void); +extern int (*exynos4_read_sensor)(int *temp); +#endif diff --git a/drivers/staging/thermal_exynos4/Kconfig b/drivers/staging/thermal_exynos4/Kconfig new file mode 100644 index 0000000..85c6f5d --- /dev/null +++ b/drivers/staging/thermal_exynos4/Kconfig @@ -0,0 +1,12 @@ +# +# Generic Thermal Framework drivers configuration +# + +menuconfig SAMSUNG_THERMAL_INTERFACE + bool "Samsung Thermal support" + help + This is a samsung thermal interface which will be used as + a link between sensors and cooling devices with linux thermal + framework. + +source "drivers/staging/thermal_exynos4/sensor/Kconfig" diff --git a/drivers/staging/thermal_exynos4/Makefile b/drivers/staging/thermal_exynos4/Makefile new file mode 100644 index 0000000..daf393c --- /dev/null +++ b/drivers/staging/thermal_exynos4/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for sensor chip drivers. +# +obj-$(CONFIG_SAMSUNG_THERMAL_INTERFACE) += thermal_interface.o \ + sensor/ diff --git a/drivers/staging/thermal_exynos4/thermal_interface.c b/drivers/staging/thermal_exynos4/thermal_interface.c new file mode 100644 index 0000000..da15f13 --- /dev/null +++ b/drivers/staging/thermal_exynos4/thermal_interface.c @@ -0,0 +1,382 @@ +/* linux/drivers/staging/thermal_exynos4/thermal_interface.c + * + * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#define pr_fmt(fmt) "exynos4: " fmt + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/thermal.h> +#include <linux/platform_device.h> +#include <linux/cpufreq.h> +#include <linux/err.h> +#include <mach/thermal_interface.h> + +#define INITIAL_COOLING_LEVEL 0 +#define MAX_COOLING_LEVEL 2 + +static unsigned int idle_interval = 50; +static unsigned int active_interval = 1; +static unsigned int verbose; +static int exynos4_thermal_level; +static struct thermal_zone_device *therm_dev; +static struct thermal_cooling_device *cool_dev; +static struct platform_device *exynos4_dev; +int (*exynos4_read_sensor)(int *); + +static int exynos4_get_mode(struct thermal_zone_device *thermal, + enum thermal_device_mode *mode) +{ + if (!exynos4_read_sensor) { + pr_info("Temperature sensor not initialised\n"); + *mode = THERMAL_DEVICE_DISABLED; + } else + *mode = THERMAL_DEVICE_ENABLED; + return 0; +} + +/* + * set operation mode; + * enabled: the thermal layer of the kernel takes care about + * the temperature. + * disabled: temperature sensor is not enabled. + */ +static int exynos4_set_mode(struct thermal_zone_device *thermal, + enum thermal_device_mode mode) +{ + if (!therm_dev) { + pr_notice("thermal zone not registered\n"); + return 0; + } + if (mode == THERMAL_DEVICE_ENABLED) + therm_dev->polling_delay = active_interval*1000; + else + therm_dev->polling_delay = idle_interval*1000; + + thermal_zone_device_update(therm_dev); + pr_info("thermal polling set for duration=%d sec\n", + therm_dev->polling_delay/1000); + return 0; +} + +/*This may be called from interrupt based temperature sensor*/ +void exynos4_report_trigger(void) +{ + therm_dev->polling_delay = active_interval*1000; + thermal_zone_device_update(therm_dev); +} + +static int exynos4_get_trip_type(struct thermal_zone_device *thermal, int trip, + enum thermal_trip_type *type) +{ + if (trip == 0 || trip == 1) + *type = THERMAL_TRIP_ACTIVE; + else if (trip == 2) + *type = THERMAL_TRIP_CRITICAL; + else + return -EINVAL; + + return 0; +} + +static int exynos4_get_trip_temp(struct thermal_zone_device *thermal, int trip, + unsigned long *temp) +{ + if (trip == 0) + *temp = EXYNOS_MONITOR_TEMP; + else if (trip == 1) + *temp = EXYNOS_WARN_TEMP; + else if (trip == 2) + *temp = EXYNOS_PANIC_TEMP; + else + return -EINVAL; + + return 0; +} + +static int exynos4_get_crit_temp(struct thermal_zone_device *thermal, + unsigned long *temperature) +{ + *temperature = EXYNOS_PANIC_TEMP; + return 0; +} + +static int exynos4_bind(struct thermal_zone_device *thermal, + struct thermal_cooling_device *cdev) +{ + /* if the cooling device is the one from exynos4 bind it */ + if (cdev != cool_dev) + return 0; + if (thermal_zone_bind_cooling_device(thermal, 0, cdev)) { + pr_err("error binding cooling dev\n"); + return -EINVAL; + } + + return 0; +} + +static int exynos4_unbind(struct thermal_zone_device *thermal, + struct thermal_cooling_device *cdev) +{ + if (cdev != cool_dev) + return 0; + if (thermal_zone_unbind_cooling_device(thermal, 0, cdev)) { + pr_err("error unbinding cooling dev\n"); + return -EINVAL; + } + return 0; +} + +static int exynos4_get_temp(struct thermal_zone_device *thermal, + unsigned long *t) +{ + int temp, err = 0; + if (!exynos4_read_sensor) { + pr_info("Temperature sensor not initialised\n"); + return -EINVAL; + } + + err = exynos4_read_sensor(&temp); + if (err) + return err; + + if (verbose) + pr_notice("temp %d\n", temp); + + *t = temp; + return 0; +} + +/* bind callback functions to thermalzone */ +static struct thermal_zone_device_ops exynos4_dev_ops = { + .bind = exynos4_bind, + .unbind = exynos4_unbind, + .get_temp = exynos4_get_temp, + .get_mode = exynos4_get_mode, + .set_mode = exynos4_set_mode, + .get_trip_type = exynos4_get_trip_type, + .get_trip_temp = exynos4_get_trip_temp, + .get_crit_temp = exynos4_get_crit_temp, +}; + +/*Below codes defines functions to be used for cpufreq as cooling device*/ +static bool is_cpufreq_valid(void) +{ + struct cpufreq_policy policy; + if (!cpufreq_get_policy(&policy, 0)) + return true; + return false; +} + +static int exynos4_cpufreq_apply_cooling(int cooling_level) +{ + if (verbose) + pr_info("%s: %d cooling_level=%d\n", __func__, __LINE__, + cooling_level); + if (!is_cpufreq_valid()) + return 0; + exynos4_thermal_level = cooling_level; + cpufreq_update_policy(0); + if (cooling_level == INITIAL_COOLING_LEVEL) + therm_dev->polling_delay = idle_interval*1000; + return 0; +} + +static int exynos4_thermal_cpufreq_notifier(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct cpufreq_policy *policy = data; + unsigned long max_freq = 0; + if (event != CPUFREQ_ADJUST) + return 0; + + max_freq = + (policy->cpuinfo.max_freq * (100 - (exynos4_thermal_level*48))) / 100; + cpufreq_verify_within_limits(policy, 0, max_freq); + + return 0; +} + +static struct notifier_block exynos4_thermal_cpufreq_notifier_block = { + .notifier_call = exynos4_thermal_cpufreq_notifier, +}; + +/* + * cooling device callback functions + */ +static int exynos4_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + *state = MAX_COOLING_LEVEL; + return 0; +} + +static int exynos4_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + *state = exynos4_thermal_level; + return 0; +} + +static int exynos4_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long state) +{ + int zone, err = 0; + unsigned long cur_temp; + + err = exynos4_get_temp(therm_dev, &cur_temp); + if (err) { + pr_err("error reading temperature\n"); + return -EINVAL; + } + + if (cur_temp < EXYNOS_MONITOR_TEMP) + zone = SAFE_ZONE; + else if (cur_temp >= EXYNOS_MONITOR_TEMP && cur_temp < EXYNOS_WARN_TEMP) + zone = MONITOR_ZONE; + else if (cur_temp >= EXYNOS_WARN_TEMP) + zone = WARN_ZONE; + + if ((zone == SAFE_ZONE) && (state == INITIAL_COOLING_LEVEL)) + exynos4_cpufreq_apply_cooling(INITIAL_COOLING_LEVEL); + else if ((zone == MONITOR_ZONE) && (state > INITIAL_COOLING_LEVEL)) + exynos4_cpufreq_apply_cooling(INITIAL_COOLING_LEVEL + 1); + else if ((zone == WARN_ZONE) && (state > INITIAL_COOLING_LEVEL)) + exynos4_cpufreq_apply_cooling(INITIAL_COOLING_LEVEL + 2); + + return 0; +} + +/* bind cpufreq callbacks to cpufreq cooling device */ +static struct thermal_cooling_device_ops exynos4_cooling_ops = { + .get_max_state = exynos4_get_max_state, + .get_cur_state = exynos4_get_cur_state, + .set_cur_state = exynos4_set_cur_state, +}; + +static int __devinit exynos4_probe(struct platform_device *device) +{ + return 0; +} + +static int exynos4_remove(struct platform_device *device) +{ + return 0; +} + +static struct platform_driver exynos4_driver = { + .driver = { + .name = "exynos4", + .owner = THIS_MODULE, + }, + .probe = exynos4_probe, + .remove = exynos4_remove, +}; + +static int exynos4_register_platform(void) +{ + int err = 0; + + err = platform_driver_register(&exynos4_driver); + if (err) + return err; + + exynos4_dev = platform_device_alloc("exynos4", -1); + if (!exynos4_dev) { + err = -ENOMEM; + goto err_device_alloc; + } + err = platform_device_add(exynos4_dev); + if (err) + goto err_device_add; + + return 0; + +err_device_add: + platform_device_put(exynos4_dev); +err_device_alloc: + platform_driver_unregister(&exynos4_driver); + return err; +} + +static void exynos4_unregister_platform(void) +{ + platform_device_unregister(exynos4_dev); + platform_driver_unregister(&exynos4_driver); +} + +static int exynos4_register_thermal(void) +{ + cool_dev = thermal_cooling_device_register("exynos4-cpufreq", NULL, + &exynos4_cooling_ops); + + if (IS_ERR(cool_dev)) + return -EINVAL; + + therm_dev = thermal_zone_device_register("exynos4-therm", 3, NULL, + &exynos4_dev_ops, 0, 0, 0, + 1000); + if (IS_ERR(therm_dev)) + return -EINVAL; + + exynos4_set_mode(therm_dev, THERMAL_DEVICE_DISABLED); + + cpufreq_register_notifier(&exynos4_thermal_cpufreq_notifier_block, + CPUFREQ_POLICY_NOTIFIER); + + return 0; +} + +static void exynos4_unregister_thermal(void) +{ + if (cool_dev) { + thermal_cooling_device_unregister(cool_dev); + cool_dev = NULL; + } + + if (therm_dev) { + thermal_zone_device_unregister(therm_dev); + therm_dev = NULL; + } +} + +static int __init exynos4_init(void) +{ + int err = 0; + + err = exynos4_register_platform(); + if (err) + goto out_err; + + err = exynos4_register_thermal(); + if (err) + goto err_unreg; + + return 0; + +err_unreg: + exynos4_unregister_thermal(); + exynos4_unregister_platform(); + +out_err: + return err; +} + +static void __exit exynos4_exit(void) +{ + exynos4_unregister_thermal(); + exynos4_unregister_platform(); +} + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Amit Daniel amit.kachhap@linaro.org"); +MODULE_DESCRIPTION("Exynos4 thermal monitor driver"); + +module_init(exynos4_init); +module_exit(exynos4_exit);