Message ID | 1319629012-21205-1-git-send-email-amit.kachhap@linaro.org |
---|---|
State | New |
Headers | show |
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 >> >
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");