Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753797Ab3DLLG7 (ORCPT ); Fri, 12 Apr 2013 07:06:59 -0400 Received: from mail-ia0-f181.google.com ([209.85.210.181]:46191 "EHLO mail-ia0-f181.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752934Ab3DLLG4 (ORCPT ); Fri, 12 Apr 2013 07:06:56 -0400 MIME-Version: 1.0 In-Reply-To: <51671D77.1020504@ti.com> References: <1364297642-2746-5-git-send-email-amit.daniel@samsung.com> <51671D77.1020504@ti.com> Date: Fri, 12 Apr 2013 16:36:55 +0530 X-Google-Sender-Auth: VjcXxP0SNJdT5qotZV4xaG1e2WA Message-ID: Subject: Re: [4/9] thermal: exynos: Bifurcate exynos thermal common and tmu controller code From: amit daniel kachhap To: Eduardo Valentin Cc: linux-pm@vger.kernel.org, Thomas Abraham , Zhang Rui , linux-samsung-soc@vger.kernel.org, linux-kernel@vger.kernel.org, Kukjin Kim Content-Type: text/plain; charset=ISO-8859-1 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 68033 Lines: 1870 Hi Eduardo, On Fri, Apr 12, 2013 at 2:00 AM, Eduardo Valentin wrote: > Amit, > > On 26-03-2013 07:33, Amit Daniel Kachhap wrote: >> >> This code bifurcates exynos thermal implementation into common and sensor >> specific parts as it will simplify adding support for new temperature >> sensors. The file is named as exynos4210 because it was original SOC for >> which this driver was developed and then later SOC's(5250, 4412) were >> added >> into it. This change is needed to add different TMU sensor for future >> exynos5 >> SOC. >> >> Signed-off-by: Amit Daniel Kachhap >> >> --- >> drivers/thermal/samsung/Kconfig | 24 +- >> drivers/thermal/samsung/Makefile | 4 +- >> drivers/thermal/samsung/exynos4210_thermal.c | 658 ++++++++++++++++ >> drivers/thermal/samsung/exynos_common.c | 421 ++++++++++ >> drivers/thermal/samsung/exynos_common.h | 73 ++ >> drivers/thermal/samsung/exynos_thermal.c | 1093 >> -------------------------- >> 6 files changed, 1172 insertions(+), 1101 deletions(-) >> create mode 100644 drivers/thermal/samsung/exynos4210_thermal.c >> create mode 100644 drivers/thermal/samsung/exynos_common.c >> create mode 100644 drivers/thermal/samsung/exynos_common.h >> delete mode 100644 drivers/thermal/samsung/exynos_thermal.c >> > > Same comment as in patch 3. Can you please use --find-renames? Ok > > Besides, it would be interesting if you easier the review process by > splitting this patch into 2 at least. One for creating the _common.* and > another to rename the remaining to 4210_thermal. Ok > > BTW, This follow same design pattern as under > drivers/stating/ti-soc-thermal/ :-). I had to do similar design by having > common and specific split so that I could afford the support for IPs with > several sensors. Like OMAP5. Ok sure. > >> diff --git a/drivers/thermal/samsung/Kconfig >> b/drivers/thermal/samsung/Kconfig >> index 5737b85..cefe693 100644 >> --- a/drivers/thermal/samsung/Kconfig >> +++ b/drivers/thermal/samsung/Kconfig >> @@ -1,11 +1,23 @@ >> >> -config EXYNOS_THERMAL >> - tristate "Temperature sensor on Samsung EXYNOS" >> +config EXYNOS_COMMON >> + tristate "Common thermal support for EXYNOS SOC's" >> depends on (ARCH_EXYNOS4 || ARCH_EXYNOS5) >> + help >> + If you say yes here you get support for EXYNOS TMU >> + (Thermal Management Unit) common registration/unregistration >> + functions to the core thermal layer and also to use the generic >> + cpu cooling API's. >> + >> +if EXYNOS_COMMON >> + >> +config EXYNOS4210_THERMAL >> + tristate "Temperature sensor on Samsung EXYNOS series SOC" >> + depends on (CPU_EXYNOS4210 || SOC_EXYNOS4212 || SOC_EXYNOS4412 || >> SOC_EXYNOS5250) >> depends on CPU_THERMAL >> help >> - If you say yes here you get support for TMU (Thermal Management >> - Unit) on SAMSUNG EXYNOS series of SoC. This helps in registering >> - the exynos thermal driver with the core thermal layer and cpu >> - cooling API's. >> + If you say yes here you can enable TMU (Thermal Management Unit) >> on >> + SAMSUNG EXYNOS 4210, 4412, 4414 and 5250 series of SoC. This >> option >> + initialises the TMU controller and registers/unregisters with >> exynos >> + common thermal layer. >> >> +endif >> diff --git a/drivers/thermal/samsung/Makefile >> b/drivers/thermal/samsung/Makefile >> index fa55df5..d51d0c2 100644 >> --- a/drivers/thermal/samsung/Makefile >> +++ b/drivers/thermal/samsung/Makefile >> @@ -1,5 +1,5 @@ >> # >> # Samsung thermal specific Makefile >> # >> -obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o >> - >> +obj-$(CONFIG_EXYNOS_COMMON) += exynos_common.o >> +obj-$(CONFIG_EXYNOS4210_THERMAL) += exynos4210_thermal.o >> diff --git a/drivers/thermal/samsung/exynos4210_thermal.c >> b/drivers/thermal/samsung/exynos4210_thermal.c >> new file mode 100644 >> index 0000000..09ea8c8 >> --- /dev/null >> +++ b/drivers/thermal/samsung/exynos4210_thermal.c >> @@ -0,0 +1,658 @@ >> +/* >> + * exynos4210_thermal.c - Samsung EXYNOS 4210, 4412, 5250 TMU >> + * (Thermal Management Unit) >> + * >> + * Copyright (C) 2011 Samsung Electronics >> + * Donggeun Kim >> + * Amit Daniel Kachhap >> + * Amit Daniel Kachhap >> + * >> + * This program is free software; you can redistribute it and/or modify >> + * it under the terms of the GNU General Public License as published by >> + * the Free Software Foundation; either version 2 of the License, or >> + * (at your option) any later version. >> + * >> + * This program is distributed in the hope that it will be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >> + * GNU General Public License for more details. >> + * >> + * You should have received a copy of the GNU General Public License >> + * along with this program; if not, write to the Free Software >> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 >> USA >> + * >> + */ >> + >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include "exynos_common.h" >> + >> +/* Exynos generic registers */ >> +#define EXYNOS_TMU_REG_TRIMINFO 0x0 >> +#define EXYNOS_TMU_REG_CONTROL 0x20 >> +#define EXYNOS_TMU_REG_STATUS 0x28 >> +#define EXYNOS_TMU_REG_CURRENT_TEMP 0x40 >> +#define EXYNOS_TMU_REG_INTEN 0x70 >> +#define EXYNOS_TMU_REG_INTSTAT 0x74 >> +#define EXYNOS_TMU_REG_INTCLEAR 0x78 >> + >> +#define EXYNOS_TMU_TRIM_TEMP_MASK 0xff >> +#define EXYNOS_TMU_GAIN_SHIFT 8 >> +#define EXYNOS_TMU_REF_VOLTAGE_SHIFT 24 >> +#define EXYNOS_TMU_CORE_ON 3 >> +#define EXYNOS_TMU_CORE_OFF 2 >> +#define EXYNOS_TMU_DEF_CODE_TO_TEMP_OFFSET 50 >> + >> +/* Exynos4210 specific registers */ >> +#define EXYNOS4210_TMU_REG_THRESHOLD_TEMP 0x44 >> +#define EXYNOS4210_TMU_REG_TRIG_LEVEL0 0x50 >> +#define EXYNOS4210_TMU_REG_TRIG_LEVEL1 0x54 >> +#define EXYNOS4210_TMU_REG_TRIG_LEVEL2 0x58 >> +#define EXYNOS4210_TMU_REG_TRIG_LEVEL3 0x5C >> +#define EXYNOS4210_TMU_REG_PAST_TEMP0 0x60 >> +#define EXYNOS4210_TMU_REG_PAST_TEMP1 0x64 >> +#define EXYNOS4210_TMU_REG_PAST_TEMP2 0x68 >> +#define EXYNOS4210_TMU_REG_PAST_TEMP3 0x6C >> + >> +#define EXYNOS4210_TMU_TRIG_LEVEL0_MASK 0x1 >> +#define EXYNOS4210_TMU_TRIG_LEVEL1_MASK 0x10 >> +#define EXYNOS4210_TMU_TRIG_LEVEL2_MASK 0x100 >> +#define EXYNOS4210_TMU_TRIG_LEVEL3_MASK 0x1000 >> +#define EXYNOS4210_TMU_INTCLEAR_VAL 0x1111 >> + >> +/* Exynos5250 and Exynos4412 specific registers */ >> +#define EXYNOS_TMU_TRIMINFO_CON 0x14 >> +#define EXYNOS_THD_TEMP_RISE 0x50 >> +#define EXYNOS_THD_TEMP_FALL 0x54 >> +#define EXYNOS_EMUL_CON 0x80 >> + >> +#define EXYNOS_TRIMINFO_RELOAD 0x1 >> +#define EXYNOS_TMU_CLEAR_RISE_INT 0x111 >> +#define EXYNOS_TMU_CLEAR_FALL_INT (0x111 << 12) >> +#define EXYNOS_MUX_ADDR_VALUE 6 >> +#define EXYNOS_MUX_ADDR_SHIFT 20 >> +#define EXYNOS_TMU_TRIP_MODE_SHIFT 13 >> + >> +#define EFUSE_MIN_VALUE 40 >> +#define EFUSE_MAX_VALUE 100 >> + >> +#ifdef CONFIG_THERMAL_EMULATION >> +#define EXYNOS_EMUL_TIME 0x57F0 >> +#define EXYNOS_EMUL_TIME_SHIFT 16 >> +#define EXYNOS_EMUL_DATA_SHIFT 8 >> +#define EXYNOS_EMUL_DATA_MASK 0xFF >> +#define EXYNOS_EMUL_ENABLE 0x1 >> +#endif /* CONFIG_THERMAL_EMULATION */ >> + >> +struct exynos_tmu_data { >> + struct exynos_tmu_platform_data *pdata; >> + struct resource *mem; >> + void __iomem *base; >> + int irq; >> + enum soc_type soc; >> + struct work_struct irq_work; >> + struct mutex lock; >> + struct clk *clk; >> + u8 temp_error1, temp_error2; >> +}; >> + >> +/* >> + * TMU treats temperature as a mapped temperature code. >> + * The temperature is converted differently depending on the calibration >> type. >> + */ >> +static int temp_to_code(struct exynos_tmu_data *data, u8 temp) >> +{ >> + struct exynos_tmu_platform_data *pdata = data->pdata; >> + int temp_code; >> + >> + if (data->soc == SOC_ARCH_EXYNOS4210) >> + /* temp should range between 25 and 125 */ >> + if (temp < 25 || temp > 125) { >> + temp_code = -EINVAL; >> + goto out; >> + } >> + >> + switch (pdata->cal_type) { >> + case TYPE_TWO_POINT_TRIMMING: >> + temp_code = (temp - 25) * >> + (data->temp_error2 - data->temp_error1) / >> + (85 - 25) + data->temp_error1; >> + break; >> + case TYPE_ONE_POINT_TRIMMING: >> + temp_code = temp + data->temp_error1 - 25; >> + break; >> + default: >> + temp_code = temp + EXYNOS_TMU_DEF_CODE_TO_TEMP_OFFSET; >> + break; >> + } >> +out: >> + return temp_code; >> +} >> + >> +/* >> + * Calculate a temperature value from a temperature code. >> + * The unit of the temperature is degree Celsius. >> + */ >> +static int code_to_temp(struct exynos_tmu_data *data, u8 temp_code) >> +{ >> + struct exynos_tmu_platform_data *pdata = data->pdata; >> + int temp; >> + >> + if (data->soc == SOC_ARCH_EXYNOS4210) >> + /* temp_code should range between 75 and 175 */ >> + if (temp_code < 75 || temp_code > 175) { >> + temp = -ENODATA; >> + goto out; >> + } >> + >> + switch (pdata->cal_type) { >> + case TYPE_TWO_POINT_TRIMMING: >> + temp = (temp_code - data->temp_error1) * (85 - 25) / >> + (data->temp_error2 - data->temp_error1) + 25; >> + break; >> + case TYPE_ONE_POINT_TRIMMING: >> + temp = temp_code - data->temp_error1 + 25; >> + break; >> + default: >> + temp = temp_code - EXYNOS_TMU_DEF_CODE_TO_TEMP_OFFSET; >> + break; >> + } >> +out: >> + return temp; >> +} >> + >> +static int exynos_tmu_initialize(struct platform_device *pdev) >> +{ >> + struct exynos_tmu_data *data = platform_get_drvdata(pdev); >> + struct exynos_tmu_platform_data *pdata = data->pdata; >> + unsigned int status, trim_info; >> + unsigned int rising_threshold = 0, falling_threshold = 0; >> + int ret = 0, threshold_code, i, trigger_levs = 0; >> + >> + mutex_lock(&data->lock); >> + clk_enable(data->clk); >> + >> + status = readb(data->base + EXYNOS_TMU_REG_STATUS); >> + if (!status) { >> + ret = -EBUSY; >> + goto out; >> + } >> + >> + if (data->soc == SOC_ARCH_EXYNOS) { >> + __raw_writel(EXYNOS_TRIMINFO_RELOAD, >> + data->base + EXYNOS_TMU_TRIMINFO_CON); >> + } >> + /* Save trimming info in order to perform calibration */ >> + trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); >> + data->temp_error1 = trim_info & EXYNOS_TMU_TRIM_TEMP_MASK; >> + data->temp_error2 = ((trim_info >> 8) & >> EXYNOS_TMU_TRIM_TEMP_MASK); >> + >> + if ((EFUSE_MIN_VALUE > data->temp_error1) || >> + (data->temp_error1 > EFUSE_MAX_VALUE) || >> + (data->temp_error2 != 0)) >> + data->temp_error1 = pdata->efuse_value; >> + >> + /* Count trigger levels to be enabled */ >> + for (i = 0; i < MAX_THRESHOLD_LEVS; i++) >> + if (pdata->trigger_levels[i]) >> + trigger_levs++; >> + >> + if (data->soc == SOC_ARCH_EXYNOS4210) { >> + /* Write temperature code for threshold */ >> + threshold_code = temp_to_code(data, pdata->threshold); >> + if (threshold_code < 0) { >> + ret = threshold_code; >> + goto out; >> + } >> + writeb(threshold_code, >> + data->base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP); >> + for (i = 0; i < trigger_levs; i++) >> + writeb(pdata->trigger_levels[i], >> + data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL0 + i * >> 4); >> + >> + writel(EXYNOS4210_TMU_INTCLEAR_VAL, >> + data->base + EXYNOS_TMU_REG_INTCLEAR); >> + } else if (data->soc == SOC_ARCH_EXYNOS) { >> + /* Write temperature code for rising and falling threshold >> */ >> + for (i = 0; i < trigger_levs; i++) { >> + threshold_code = temp_to_code(data, >> + pdata->trigger_levels[i]); >> + if (threshold_code < 0) { >> + ret = threshold_code; >> + goto out; >> + } >> + rising_threshold |= threshold_code << 8 * i; >> + if (pdata->threshold_falling) { >> + threshold_code = temp_to_code(data, >> + pdata->trigger_levels[i] - >> + pdata->threshold_falling); >> + if (threshold_code > 0) >> + falling_threshold |= >> + threshold_code << 8 * i; >> + } >> + } >> + >> + writel(rising_threshold, >> + data->base + EXYNOS_THD_TEMP_RISE); >> + writel(falling_threshold, >> + data->base + EXYNOS_THD_TEMP_FALL); >> + >> + writel(EXYNOS_TMU_CLEAR_RISE_INT | >> EXYNOS_TMU_CLEAR_FALL_INT, >> + data->base + EXYNOS_TMU_REG_INTCLEAR); >> + } >> +out: >> + clk_disable(data->clk); >> + mutex_unlock(&data->lock); >> + >> + return ret; >> +} >> + >> +static void exynos_tmu_control(struct platform_device *pdev, bool on) >> +{ >> + struct exynos_tmu_data *data = platform_get_drvdata(pdev); >> + struct exynos_tmu_platform_data *pdata = data->pdata; >> + unsigned int con, interrupt_en; >> + >> + mutex_lock(&data->lock); >> + clk_enable(data->clk); >> + >> + con = pdata->reference_voltage << EXYNOS_TMU_REF_VOLTAGE_SHIFT | >> + pdata->gain << EXYNOS_TMU_GAIN_SHIFT; >> + >> + if (data->soc == SOC_ARCH_EXYNOS) { >> + con |= pdata->noise_cancel_mode << >> EXYNOS_TMU_TRIP_MODE_SHIFT; >> + con |= (EXYNOS_MUX_ADDR_VALUE << EXYNOS_MUX_ADDR_SHIFT); >> + } >> + >> + if (on) { >> + con |= EXYNOS_TMU_CORE_ON; >> + interrupt_en = pdata->trigger_level3_en << 12 | >> + pdata->trigger_level2_en << 8 | >> + pdata->trigger_level1_en << 4 | >> + pdata->trigger_level0_en; >> + if (pdata->threshold_falling) >> + interrupt_en |= interrupt_en << 16; >> + } else { >> + con |= EXYNOS_TMU_CORE_OFF; >> + interrupt_en = 0; /* Disable all interrupts */ >> + } >> + writel(interrupt_en, data->base + EXYNOS_TMU_REG_INTEN); >> + writel(con, data->base + EXYNOS_TMU_REG_CONTROL); >> + >> + clk_disable(data->clk); >> + mutex_unlock(&data->lock); >> +} >> + >> +static int exynos_tmu_read(struct exynos_tmu_data *data) >> +{ >> + u8 temp_code; >> + int temp; >> + >> + mutex_lock(&data->lock); >> + clk_enable(data->clk); >> + >> + temp_code = readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP); >> + temp = code_to_temp(data, temp_code); >> + >> + clk_disable(data->clk); >> + mutex_unlock(&data->lock); >> + >> + return temp; >> +} >> + >> +#ifdef CONFIG_THERMAL_EMULATION >> +static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp) >> +{ >> + struct exynos_tmu_data *data = drv_data; >> + unsigned int reg; >> + int ret = -EINVAL; >> + >> + if (data->soc == SOC_ARCH_EXYNOS4210) >> + goto out; >> + >> + if (temp && temp < MCELSIUS) >> + goto out; >> + >> + mutex_lock(&data->lock); >> + clk_enable(data->clk); >> + >> + reg = readl(data->base + EXYNOS_EMUL_CON); >> + >> + if (temp) { >> + temp /= MCELSIUS; >> + >> + reg = (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT) | >> + (temp_to_code(data, temp) >> + << EXYNOS_EMUL_DATA_SHIFT) | EXYNOS_EMUL_ENABLE; >> + } else { >> + reg &= ~EXYNOS_EMUL_ENABLE; >> + } >> + >> + writel(reg, data->base + EXYNOS_EMUL_CON); >> + >> + clk_disable(data->clk); >> + mutex_unlock(&data->lock); >> + return 0; >> +out: >> + return ret; >> +} >> +#else >> +static int exynos_tmu_set_emulation(void *drv_data, unsigned long >> temp) >> + { return -EINVAL; } >> +#endif/*CONFIG_THERMAL_EMULATION*/ >> + >> +static struct thermal_sensor_conf exynos_sensor_conf = { >> + .name = "exynos-therm", >> + .read_temperature = (int (*)(void *))exynos_tmu_read, >> + .write_emul_temp = exynos_tmu_set_emulation, >> +}; >> + >> +static void exynos_tmu_work(struct work_struct *work) >> +{ >> + struct exynos_tmu_data *data = container_of(work, >> + struct exynos_tmu_data, irq_work); >> + >> + exynos_report_trigger(&exynos_sensor_conf); >> + mutex_lock(&data->lock); >> + clk_enable(data->clk); >> + if (data->soc == SOC_ARCH_EXYNOS) >> + writel(EXYNOS_TMU_CLEAR_RISE_INT | >> + EXYNOS_TMU_CLEAR_FALL_INT, >> + data->base + EXYNOS_TMU_REG_INTCLEAR); >> + else >> + writel(EXYNOS4210_TMU_INTCLEAR_VAL, >> + data->base + EXYNOS_TMU_REG_INTCLEAR); >> + clk_disable(data->clk); >> + mutex_unlock(&data->lock); >> + >> + enable_irq(data->irq); >> +} >> + >> +static irqreturn_t exynos_tmu_irq(int irq, void *id) >> +{ >> + struct exynos_tmu_data *data = id; >> + >> + disable_irq_nosync(irq); >> + schedule_work(&data->irq_work); >> + >> + return IRQ_HANDLED; >> +} >> + >> +#if defined(CONFIG_CPU_EXYNOS4210) >> +static struct exynos_tmu_platform_data const exynos4210_default_tmu_data >> = { >> + .threshold = 80, >> + .trigger_levels[0] = 5, >> + .trigger_levels[1] = 20, >> + .trigger_levels[2] = 30, >> + .trigger_level0_en = 1, >> + .trigger_level1_en = 1, >> + .trigger_level2_en = 1, >> + .trigger_level3_en = 0, >> + .gain = 15, >> + .reference_voltage = 7, >> + .cal_type = TYPE_ONE_POINT_TRIMMING, >> + .freq_tab[0] = { >> + .freq_clip_max = 800 * 1000, >> + .temp_level = 85, >> + }, >> + .freq_tab[1] = { >> + .freq_clip_max = 200 * 1000, >> + .temp_level = 100, >> + }, >> + .freq_tab_count = 2, >> + .type = SOC_ARCH_EXYNOS4210, >> +}; >> +#define EXYNOS4210_TMU_DRV_DATA (&exynos4210_default_tmu_data) >> +#else >> +#define EXYNOS4210_TMU_DRV_DATA (NULL) >> +#endif >> + >> +#if defined(CONFIG_SOC_EXYNOS5250) || defined(CONFIG_SOC_EXYNOS4412) >> +static struct exynos_tmu_platform_data const exynos_default_tmu_data = { >> + .threshold_falling = 10, >> + .trigger_levels[0] = 85, >> + .trigger_levels[1] = 103, >> + .trigger_levels[2] = 110, >> + .trigger_level0_en = 1, >> + .trigger_level1_en = 1, >> + .trigger_level2_en = 1, >> + .trigger_level3_en = 0, >> + .gain = 8, >> + .reference_voltage = 16, >> + .noise_cancel_mode = 4, >> + .cal_type = TYPE_ONE_POINT_TRIMMING, >> + .efuse_value = 55, >> + .freq_tab[0] = { >> + .freq_clip_max = 800 * 1000, >> + .temp_level = 85, >> + }, >> + .freq_tab[1] = { >> + .freq_clip_max = 200 * 1000, >> + .temp_level = 103, >> + }, >> + .freq_tab_count = 2, >> + .type = SOC_ARCH_EXYNOS, >> +}; >> +#define EXYNOS_TMU_DRV_DATA (&exynos_default_tmu_data) >> +#else >> +#define EXYNOS_TMU_DRV_DATA (NULL) >> +#endif >> + >> +#ifdef CONFIG_OF >> +static const struct of_device_id exynos_tmu_match[] = { >> + { >> + .compatible = "samsung,exynos4210-tmu", >> + .data = (void *)EXYNOS4210_TMU_DRV_DATA, >> + }, >> + { >> + .compatible = "samsung,exynos5250-tmu", >> + .data = (void *)EXYNOS_TMU_DRV_DATA, >> + }, >> + {}, >> +}; >> +MODULE_DEVICE_TABLE(of, exynos_tmu_match); >> +#endif >> + >> +static struct platform_device_id exynos_tmu_driver_ids[] = { >> + { >> + .name = "exynos4210-tmu", >> + .driver_data = (kernel_ulong_t)EXYNOS4210_TMU_DRV_DATA, >> + }, >> + { >> + .name = "exynos5250-tmu", >> + .driver_data = (kernel_ulong_t)EXYNOS_TMU_DRV_DATA, >> + }, >> + { }, >> +}; >> +MODULE_DEVICE_TABLE(platform, exynos_tmu_driver_ids); >> + >> +static inline struct exynos_tmu_platform_data *exynos_get_driver_data( >> + struct platform_device *pdev) >> +{ >> +#ifdef CONFIG_OF >> + if (pdev->dev.of_node) { >> + const struct of_device_id *match; >> + match = of_match_node(exynos_tmu_match, >> pdev->dev.of_node); >> + if (!match) >> + return NULL; >> + return (struct exynos_tmu_platform_data *) match->data; >> + } >> +#endif >> + return (struct exynos_tmu_platform_data *) >> + platform_get_device_id(pdev)->driver_data; >> +} >> + >> +static int exynos_tmu_probe(struct platform_device *pdev) >> +{ >> + struct exynos_tmu_data *data; >> + struct exynos_tmu_platform_data *pdata = pdev->dev.platform_data; >> + int ret, i; >> + >> + if (!pdata) >> + pdata = exynos_get_driver_data(pdev); >> + >> + if (!pdata) { >> + dev_err(&pdev->dev, "No platform init data supplied.\n"); >> + return -ENODEV; >> + } >> + data = devm_kzalloc(&pdev->dev, sizeof(struct exynos_tmu_data), >> + GFP_KERNEL); >> + if (!data) { >> + dev_err(&pdev->dev, "Failed to allocate driver >> structure\n"); >> + return -ENOMEM; >> + } >> + >> + data->irq = platform_get_irq(pdev, 0); >> + if (data->irq < 0) { >> + dev_err(&pdev->dev, "Failed to get platform irq\n"); >> + return data->irq; >> + } >> + >> + INIT_WORK(&data->irq_work, exynos_tmu_work); >> + >> + data->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> + if (!data->mem) { >> + dev_err(&pdev->dev, "Failed to get platform resource\n"); >> + return -ENOENT; >> + } >> + >> + data->base = devm_ioremap_resource(&pdev->dev, data->mem); >> + if (IS_ERR(data->base)) >> + return PTR_ERR(data->base); >> + >> + ret = devm_request_irq(&pdev->dev, data->irq, exynos_tmu_irq, >> + IRQF_TRIGGER_RISING, "exynos-tmu", data); >> + if (ret) { >> + dev_err(&pdev->dev, "Failed to request irq: %d\n", >> data->irq); >> + return ret; >> + } >> + >> + data->clk = clk_get(NULL, "tmu_apbif"); >> + if (IS_ERR(data->clk)) { >> + dev_err(&pdev->dev, "Failed to get clock\n"); >> + return PTR_ERR(data->clk); >> + } >> + >> + if (pdata->type == SOC_ARCH_EXYNOS || >> + pdata->type == SOC_ARCH_EXYNOS4210) >> + data->soc = pdata->type; >> + else { >> + ret = -EINVAL; >> + dev_err(&pdev->dev, "Platform not supported\n"); >> + goto err_clk; >> + } >> + >> + data->pdata = pdata; >> + platform_set_drvdata(pdev, data); >> + mutex_init(&data->lock); >> + >> + ret = exynos_tmu_initialize(pdev); >> + if (ret) { >> + dev_err(&pdev->dev, "Failed to initialize TMU\n"); >> + goto err_clk; >> + } >> + >> + exynos_tmu_control(pdev, true); >> + >> + /* Register the sensor with thermal management interface */ >> + (&exynos_sensor_conf)->driver_data = data; >> + exynos_sensor_conf.trip_data.trip_count = pdata->trigger_level0_en >> + >> + pdata->trigger_level1_en + >> pdata->trigger_level2_en + >> + pdata->trigger_level3_en; >> + >> + for (i = 0; i < exynos_sensor_conf.trip_data.trip_count; i++) >> + exynos_sensor_conf.trip_data.trip_val[i] = >> + pdata->threshold + pdata->trigger_levels[i]; >> + >> + exynos_sensor_conf.trip_data.trigger_falling = >> pdata->threshold_falling; >> + >> + exynos_sensor_conf.cooling_data.freq_clip_count = >> + pdata->freq_tab_count; >> + for (i = 0; i < pdata->freq_tab_count; i++) { >> + exynos_sensor_conf.cooling_data.freq_data[i].freq_clip_max >> = >> + pdata->freq_tab[i].freq_clip_max; >> + exynos_sensor_conf.cooling_data.freq_data[i].temp_level = >> + pdata->freq_tab[i].temp_level; >> + } >> + >> + ret = exynos_register_thermal(&exynos_sensor_conf); >> + if (ret) { >> + dev_err(&pdev->dev, "Failed to register thermal >> interface\n"); >> + goto err_clk; >> + } >> + >> + return 0; >> +err_clk: >> + platform_set_drvdata(pdev, NULL); >> + clk_put(data->clk); >> + return ret; >> +} >> + >> +static int exynos_tmu_remove(struct platform_device *pdev) >> +{ >> + struct exynos_tmu_data *data = platform_get_drvdata(pdev); >> + >> + exynos_tmu_control(pdev, false); >> + >> + exynos_unregister_thermal(&exynos_sensor_conf); >> + >> + clk_put(data->clk); >> + >> + platform_set_drvdata(pdev, NULL); >> + >> + return 0; >> +} >> + >> +#ifdef CONFIG_PM_SLEEP >> +static int exynos_tmu_suspend(struct device *dev) >> +{ >> + exynos_tmu_control(to_platform_device(dev), false); >> + >> + return 0; >> +} >> + >> +static int exynos_tmu_resume(struct device *dev) >> +{ >> + struct platform_device *pdev = to_platform_device(dev); >> + >> + exynos_tmu_initialize(pdev); >> + exynos_tmu_control(pdev, true); >> + >> + return 0; >> +} >> + >> +static SIMPLE_DEV_PM_OPS(exynos_tmu_pm, >> + exynos_tmu_suspend, exynos_tmu_resume); >> +#define EXYNOS_TMU_PM (&exynos_tmu_pm) >> +#else >> +#define EXYNOS_TMU_PM NULL >> +#endif >> + >> +static struct platform_driver exynos_tmu_driver = { >> + .driver = { >> + .name = "exynos-tmu", >> + .owner = THIS_MODULE, >> + .pm = EXYNOS_TMU_PM, >> + .of_match_table = of_match_ptr(exynos_tmu_match), >> + }, >> + .probe = exynos_tmu_probe, >> + .remove = exynos_tmu_remove, >> + .id_table = exynos_tmu_driver_ids, >> +}; >> + >> +module_platform_driver(exynos_tmu_driver); >> + >> +MODULE_DESCRIPTION("EXYNOS TMU Driver"); >> +MODULE_AUTHOR("Donggeun Kim "); >> +MODULE_LICENSE("GPL"); >> +MODULE_ALIAS("platform:exynos-tmu"); >> diff --git a/drivers/thermal/samsung/exynos_common.c >> b/drivers/thermal/samsung/exynos_common.c >> new file mode 100644 >> index 0000000..649d67c >> --- /dev/null >> +++ b/drivers/thermal/samsung/exynos_common.c >> @@ -0,0 +1,421 @@ >> +/* >> + * exynos_common.c - Samsung EXYNOS common thermal file >> + * >> + * Copyright (C) 2013 Samsung Electronics >> + * Amit Daniel Kachhap >> + * >> + * This program is free software; you can redistribute it and/or modify >> + * it under the terms of the GNU General Public License as published by >> + * the Free Software Foundation; either version 2 of the License, or >> + * (at your option) any later version. >> + * >> + * This program is distributed in the hope that it will be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >> + * GNU General Public License for more details. >> + * >> + * You should have received a copy of the GNU General Public License >> + * along with this program; if not, write to the Free Software >> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 >> USA >> + * >> + */ >> + >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include "exynos_common.h" >> + >> +struct exynos_thermal_zone { >> + enum thermal_device_mode mode; >> + struct thermal_zone_device *therm_dev; >> + struct thermal_cooling_device *cool_dev[MAX_COOLING_DEVICE]; >> + unsigned int cool_dev_size; >> + struct platform_device *exynos4_dev; >> + struct thermal_sensor_conf *sensor_conf; >> + bool bind; >> +}; >> + >> +/* Get mode callback functions for thermal zone */ >> +static int exynos_get_mode(struct thermal_zone_device *thermal, >> + enum thermal_device_mode *mode) >> +{ >> + struct exynos_thermal_zone *th_zone = thermal->devdata; >> + if (th_zone) >> + *mode = th_zone->mode; >> + return 0; >> +} >> + >> +/* Set mode callback functions for thermal zone */ >> +static int exynos_set_mode(struct thermal_zone_device *thermal, >> + enum thermal_device_mode mode) >> +{ >> + struct exynos_thermal_zone *th_zone = thermal->devdata; >> + if (!th_zone) { >> + pr_notice("thermal zone not registered\n"); >> + return 0; >> + } >> + >> + mutex_lock(&thermal->lock); >> + >> + if (mode == THERMAL_DEVICE_ENABLED && >> + !th_zone->sensor_conf->trip_data.trigger_falling) >> + thermal->polling_delay = IDLE_INTERVAL; >> + else >> + thermal->polling_delay = 0; >> + >> + mutex_unlock(&thermal->lock); >> + >> + th_zone->mode = mode; >> + thermal_zone_device_update(thermal); >> + pr_info("thermal polling set for duration=%d msec\n", >> + thermal->polling_delay); >> + return 0; >> +} >> + >> + >> +/* Get trip type callback functions for thermal zone */ >> +static int exynos_get_trip_type(struct thermal_zone_device *thermal, int >> trip, >> + enum thermal_trip_type *type) >> +{ >> + switch (GET_ZONE(trip)) { >> + case MONITOR_ZONE: >> + case WARN_ZONE: >> + *type = THERMAL_TRIP_ACTIVE; >> + break; >> + case PANIC_ZONE: >> + *type = THERMAL_TRIP_CRITICAL; >> + break; >> + default: >> + return -EINVAL; >> + } >> + return 0; >> +} >> + >> +/* Get trip temperature callback functions for thermal zone */ >> +static int exynos_get_trip_temp(struct thermal_zone_device *thermal, int >> trip, >> + unsigned long *temp) >> +{ >> + struct exynos_thermal_zone *th_zone = thermal->devdata; >> + >> + if (trip < GET_TRIP(MONITOR_ZONE) || trip > GET_TRIP(PANIC_ZONE)) >> + return -EINVAL; >> + >> + *temp = th_zone->sensor_conf->trip_data.trip_val[trip]; >> + /* convert the temperature into millicelsius */ >> + *temp = *temp * MCELSIUS; >> + >> + return 0; >> +} >> + >> +/* Get critical temperature callback functions for thermal zone */ >> +static int exynos_get_crit_temp(struct thermal_zone_device *thermal, >> + unsigned long *temp) >> +{ >> + int ret; >> + /* Panic zone */ >> + ret = exynos_get_trip_temp(thermal, GET_TRIP(PANIC_ZONE), temp); >> + return ret; >> +} >> + >> +int exynos_get_frequency_level(unsigned int cpu, unsigned int freq) >> +{ >> + int i = 0, ret = -EINVAL; >> + struct cpufreq_frequency_table *table = NULL; >> +#ifdef CONFIG_CPU_FREQ >> + table = cpufreq_frequency_get_table(cpu); >> +#endif >> + if (!table) >> + return ret; >> + >> + while (table[i].frequency != CPUFREQ_TABLE_END) { >> + if (table[i].frequency == CPUFREQ_ENTRY_INVALID) >> + continue; >> + if (table[i].frequency == freq) >> + return i; >> + i++; >> + } >> + return ret; >> +} >> + >> +/* Bind callback functions for thermal zone */ >> +static int exynos_bind(struct thermal_zone_device *thermal, >> + struct thermal_cooling_device *cdev) >> +{ >> + int ret = 0, i, tab_size, level; >> + struct freq_clip_table *tab_ptr, *clip_data; >> + struct exynos_thermal_zone *th_zone = thermal->devdata; >> + struct thermal_sensor_conf *data = th_zone->sensor_conf; >> + >> + tab_ptr = (struct freq_clip_table *)data->cooling_data.freq_data; >> + tab_size = data->cooling_data.freq_clip_count; >> + >> + if (tab_ptr == NULL || tab_size == 0) >> + return -EINVAL; >> + >> + /* find the cooling device registered*/ >> + for (i = 0; i < th_zone->cool_dev_size; i++) >> + if (cdev == th_zone->cool_dev[i]) >> + break; >> + >> + /* No matching cooling device */ >> + if (i == th_zone->cool_dev_size) >> + return 0; >> + >> + /* Bind the thermal zone to the cpufreq cooling device */ >> + for (i = 0; i < tab_size; i++) { >> + clip_data = (struct freq_clip_table *)&(tab_ptr[i]); >> + level = exynos_get_frequency_level(0, >> clip_data->freq_clip_max); >> + if (level < 0) >> + return 0; >> + switch (GET_ZONE(i)) { >> + case MONITOR_ZONE: >> + case WARN_ZONE: >> + if (thermal_zone_bind_cooling_device(thermal, i, >> cdev, >> + level, 0)) >> { >> + pr_err("error binding cdev inst %d\n", i); >> + ret = -EINVAL; >> + } >> + th_zone->bind = true; >> + break; >> + default: >> + ret = -EINVAL; >> + } >> + } >> + >> + return ret; >> +} >> + >> +/* Unbind callback functions for thermal zone */ >> +static int exynos_unbind(struct thermal_zone_device *thermal, >> + struct thermal_cooling_device *cdev) >> +{ >> + int ret = 0, i, tab_size; >> + struct exynos_thermal_zone *th_zone = thermal->devdata; >> + struct thermal_sensor_conf *data = th_zone->sensor_conf; >> + >> + if (th_zone->bind == false) >> + return 0; >> + >> + tab_size = data->cooling_data.freq_clip_count; >> + >> + if (tab_size == 0) >> + return -EINVAL; >> + >> + /* find the cooling device registered*/ >> + for (i = 0; i < th_zone->cool_dev_size; i++) >> + if (cdev == th_zone->cool_dev[i]) >> + break; >> + >> + /* No matching cooling device */ >> + if (i == th_zone->cool_dev_size) >> + return 0; >> + >> + /* Bind the thermal zone to the cpufreq cooling device */ >> + for (i = 0; i < tab_size; i++) { >> + switch (GET_ZONE(i)) { >> + case MONITOR_ZONE: >> + case WARN_ZONE: >> + if (thermal_zone_unbind_cooling_device(thermal, i, >> + cdev)) { >> + pr_err("error unbinding cdev inst=%d\n", >> i); >> + ret = -EINVAL; >> + } >> + th_zone->bind = false; >> + break; >> + default: >> + ret = -EINVAL; >> + } >> + } >> + return ret; >> +} >> + >> +/* Get temperature callback functions for thermal zone */ >> +static int exynos_get_temp(struct thermal_zone_device *thermal, >> + unsigned long *temp) >> +{ >> + struct exynos_thermal_zone *th_zone = thermal->devdata; >> + void *data; >> + >> + if (!th_zone->sensor_conf) { >> + pr_info("Temperature sensor not initialised\n"); >> + return -EINVAL; >> + } >> + data = th_zone->sensor_conf->driver_data; >> + *temp = th_zone->sensor_conf->read_temperature(data); >> + /* convert the temperature into millicelsius */ >> + *temp = *temp * MCELSIUS; >> + return 0; >> +} >> + >> +/* Get temperature callback functions for thermal zone */ >> +static int exynos_set_emul_temp(struct thermal_zone_device *thermal, >> + unsigned long temp) >> +{ >> + void *data; >> + int ret = -EINVAL; >> + struct exynos_thermal_zone *th_zone = thermal->devdata; >> + >> + if (!th_zone->sensor_conf) { >> + pr_info("Temperature sensor not initialised\n"); >> + return -EINVAL; >> + } >> + data = th_zone->sensor_conf->driver_data; >> + if (th_zone->sensor_conf->write_emul_temp) >> + ret = th_zone->sensor_conf->write_emul_temp(data, temp); >> + return ret; >> +} >> + >> +/* Get the temperature trend */ >> +static int exynos_get_trend(struct thermal_zone_device *thermal, >> + int trip, enum thermal_trend *trend) >> +{ >> + int ret = 0; >> + unsigned long trip_temp; >> + >> + ret = exynos_get_trip_temp(thermal, trip, &trip_temp); >> + if (ret < 0) >> + return ret; >> + >> + if (thermal->temperature >= trip_temp) >> + *trend = THERMAL_TREND_RAISE_FULL; >> + else >> + *trend = THERMAL_TREND_DROP_FULL; >> + >> + return ret; >> +} >> +/* Operation callback functions for thermal zone */ >> +static struct thermal_zone_device_ops const exynos_dev_ops = { >> + .bind = exynos_bind, >> + .unbind = exynos_unbind, >> + .get_temp = exynos_get_temp, >> + .set_emul_temp = exynos_set_emul_temp, >> + .get_trend = exynos_get_trend, >> + .get_mode = exynos_get_mode, >> + .set_mode = exynos_set_mode, >> + .get_trip_type = exynos_get_trip_type, >> + .get_trip_temp = exynos_get_trip_temp, >> + .get_crit_temp = exynos_get_crit_temp, >> +}; >> + >> +/* >> + * This function may be called from interrupt based temperature sensor >> + * when threshold is changed. >> + */ >> +void exynos_report_trigger(struct thermal_sensor_conf *conf) >> +{ >> + unsigned int i; >> + char data[10]; >> + char *envp[] = { data, NULL }; >> + struct exynos_thermal_zone *th_zone = conf->pzone_data; >> + >> + if (!th_zone || !th_zone->therm_dev) >> + return; >> + if (th_zone->bind == false) { >> + for (i = 0; i < th_zone->cool_dev_size; i++) { >> + if (!th_zone->cool_dev[i]) >> + continue; >> + exynos_bind(th_zone->therm_dev, >> + th_zone->cool_dev[i]); >> + } >> + } >> + >> + thermal_zone_device_update(th_zone->therm_dev); >> + >> + mutex_lock(&th_zone->therm_dev->lock); >> + /* Find the level for which trip happened */ >> + for (i = 0; i < th_zone->sensor_conf->trip_data.trip_count; i++) { >> + if (th_zone->therm_dev->last_temperature < >> + th_zone->sensor_conf->trip_data.trip_val[i] * >> MCELSIUS) >> + break; >> + } >> + >> + if (th_zone->mode == THERMAL_DEVICE_ENABLED && >> + !th_zone->sensor_conf->trip_data.trigger_falling) { >> + if (i > 0) >> + th_zone->therm_dev->polling_delay = >> ACTIVE_INTERVAL; >> + else >> + th_zone->therm_dev->polling_delay = IDLE_INTERVAL; >> + } >> + >> + snprintf(data, sizeof(data), "%u", i); >> + kobject_uevent_env(&th_zone->therm_dev->device.kobj, KOBJ_CHANGE, >> envp); >> + mutex_unlock(&th_zone->therm_dev->lock); >> +} >> + >> +/* Register with the in-kernel thermal management */ >> +int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf) >> +{ >> + int ret, id; >> + struct cpumask mask_val; >> + struct exynos_thermal_zone *th_zone; >> + >> + if (!sensor_conf || !sensor_conf->read_temperature) { >> + pr_err("Temperature sensor not initialised\n"); >> + return -EINVAL; >> + } >> + >> + th_zone = kzalloc(sizeof(struct exynos_thermal_zone), GFP_KERNEL); >> + if (!th_zone) >> + return -ENOMEM; >> + >> + th_zone->sensor_conf = sensor_conf; >> + cpumask_set_cpu(0, &mask_val); >> + th_zone->cool_dev[0] = cpufreq_cooling_register(&mask_val); >> + if (IS_ERR(th_zone->cool_dev[0])) { >> + pr_err("Failed to register cpufreq cooling device\n"); >> + ret = -EINVAL; >> + goto err_unregister; >> + } >> + th_zone->cool_dev_size++; >> + >> + th_zone->therm_dev = >> thermal_zone_device_register(sensor_conf->name, >> + EXYNOS_ZONE_COUNT, 0, th_zone, &exynos_dev_ops, >> NULL, 0, >> + sensor_conf->trip_data.trigger_falling ? >> + 0 : IDLE_INTERVAL); >> + >> + if (IS_ERR(th_zone->therm_dev)) { >> + pr_err("Failed to register thermal zone device\n"); >> + ret = PTR_ERR(th_zone->therm_dev); >> + goto err_unregister; >> + } >> + th_zone->mode = THERMAL_DEVICE_ENABLED; >> + sensor_conf->pzone_data = th_zone; >> + id = th_zone->therm_dev->id; >> + >> + pr_info("Exynos: Kernel Thermal[%d] management registered\n", id); >> + >> + return 0; >> + >> +err_unregister: >> + exynos_unregister_thermal(sensor_conf); >> + return ret; >> +} >> + >> +/* Un-Register with the in-kernel thermal management */ >> +void exynos_unregister_thermal(struct thermal_sensor_conf *sensor_conf) >> +{ >> + int i, id; >> + struct exynos_thermal_zone *th_zone = sensor_conf->pzone_data; >> + >> + if (!th_zone) >> + return; >> + >> + id = th_zone->therm_dev->id; >> + if (th_zone->therm_dev) >> + thermal_zone_device_unregister(th_zone->therm_dev); >> + >> + for (i = 0; i < th_zone->cool_dev_size; i++) { >> + if (th_zone->cool_dev[i]) >> + cpufreq_cooling_unregister(th_zone->cool_dev[i]); >> + } >> + >> + kfree(th_zone); >> + pr_info("Exynos: Kernel Thermal[%d] management unregistered\n", >> id); >> +} >> diff --git a/drivers/thermal/samsung/exynos_common.h >> b/drivers/thermal/samsung/exynos_common.h >> new file mode 100644 >> index 0000000..b8d289e >> --- /dev/null >> +++ b/drivers/thermal/samsung/exynos_common.h >> @@ -0,0 +1,73 @@ >> +/* >> + * exynos_common.h - Samsung EXYNOS common header file >> + * >> + * Copyright (C) 2013 Samsung Electronics >> + * Amit Daniel Kachhap >> + * >> + * This program is free software; you can redistribute it and/or modify >> + * it under the terms of the GNU General Public License as published by >> + * the Free Software Foundation; either version 2 of the License, or >> + * (at your option) any later version. >> + * >> + * This program is distributed in the hope that it will be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >> + * GNU General Public License for more details. >> + * >> + * You should have received a copy of the GNU General Public License >> + * along with this program; if not, write to the Free Software >> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 >> USA >> + * >> + */ >> + >> +#ifndef _LINUX_EXYNOS_COMMON_H >> +#define _LINUX_EXYNOS_COMMON_H >> + >> +/* In-kernel thermal framework related macros & definations */ >> +#define SENSOR_NAME_LEN 16 >> +#define MAX_TRIP_COUNT 8 >> +#define MAX_COOLING_DEVICE 4 >> +#define MAX_THRESHOLD_LEVS 4 >> + >> +#define ACTIVE_INTERVAL 500 >> +#define IDLE_INTERVAL 10000 >> +#define MCELSIUS 1000 >> + >> +/* CPU Zone information */ >> +#define PANIC_ZONE 4 >> +#define WARN_ZONE 3 >> +#define MONITOR_ZONE 2 >> +#define SAFE_ZONE 1 >> + >> +#define GET_ZONE(trip) (trip + 2) >> +#define GET_TRIP(zone) (zone - 2) >> + >> +#define EXYNOS_ZONE_COUNT 3 >> + >> +struct thermal_trip_point_conf { >> + int trip_val[MAX_TRIP_COUNT]; >> + int trip_count; >> + u8 trigger_falling; >> +}; >> + >> +struct thermal_cooling_conf { >> + struct freq_clip_table freq_data[MAX_TRIP_COUNT]; >> + int freq_clip_count; >> +}; >> + >> +struct thermal_sensor_conf { >> + char name[SENSOR_NAME_LEN]; >> + int (*read_temperature)(void *data); >> + int (*write_emul_temp)(void *drv_data, unsigned long temp); >> + struct thermal_trip_point_conf trip_data; >> + struct thermal_cooling_conf cooling_data; >> + void *driver_data; >> + void *pzone_data; >> +}; >> + >> +/*Functions used exynos based thermal sensor driver*/ >> +void exynos_unregister_thermal(struct thermal_sensor_conf *sensor_conf); >> +int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf); >> +void exynos_report_trigger(struct thermal_sensor_conf *sensor_conf); >> +int exynos_get_frequency_level(unsigned int cpu, unsigned int freq); >> +#endif /* _LINUX_EXYNOS_COMMON_H */ >> diff --git a/drivers/thermal/samsung/exynos_thermal.c >> b/drivers/thermal/samsung/exynos_thermal.c >> deleted file mode 100644 >> index dc9b91b..0000000 >> --- a/drivers/thermal/samsung/exynos_thermal.c >> +++ /dev/null >> @@ -1,1093 +0,0 @@ >> -/* >> - * exynos_thermal.c - Samsung EXYNOS TMU (Thermal Management Unit) >> - * >> - * Copyright (C) 2011 Samsung Electronics >> - * Donggeun Kim >> - * Amit Daniel Kachhap >> - * >> - * This program is free software; you can redistribute it and/or modify >> - * it under the terms of the GNU General Public License as published by >> - * the Free Software Foundation; either version 2 of the License, or >> - * (at your option) any later version. >> - * >> - * This program is distributed in the hope that it will be useful, >> - * but WITHOUT ANY WARRANTY; without even the implied warranty of >> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >> - * GNU General Public License for more details. >> - * >> - * You should have received a copy of the GNU General Public License >> - * along with this program; if not, write to the Free Software >> - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 >> USA >> - * >> - */ >> - >> -#include >> -#include >> -#include >> -#include >> -#include >> -#include >> -#include >> -#include >> -#include >> -#include >> -#include >> -#include >> -#include >> -#include >> -#include >> -#include >> -#include >> - >> -#include >> - >> -/* Exynos generic registers */ >> -#define EXYNOS_TMU_REG_TRIMINFO 0x0 >> -#define EXYNOS_TMU_REG_CONTROL 0x20 >> -#define EXYNOS_TMU_REG_STATUS 0x28 >> -#define EXYNOS_TMU_REG_CURRENT_TEMP 0x40 >> -#define EXYNOS_TMU_REG_INTEN 0x70 >> -#define EXYNOS_TMU_REG_INTSTAT 0x74 >> -#define EXYNOS_TMU_REG_INTCLEAR 0x78 >> - >> -#define EXYNOS_TMU_TRIM_TEMP_MASK 0xff >> -#define EXYNOS_TMU_GAIN_SHIFT 8 >> -#define EXYNOS_TMU_REF_VOLTAGE_SHIFT 24 >> -#define EXYNOS_TMU_CORE_ON 3 >> -#define EXYNOS_TMU_CORE_OFF 2 >> -#define EXYNOS_TMU_DEF_CODE_TO_TEMP_OFFSET 50 >> - >> -/* Exynos4210 specific registers */ >> -#define EXYNOS4210_TMU_REG_THRESHOLD_TEMP 0x44 >> -#define EXYNOS4210_TMU_REG_TRIG_LEVEL0 0x50 >> -#define EXYNOS4210_TMU_REG_TRIG_LEVEL1 0x54 >> -#define EXYNOS4210_TMU_REG_TRIG_LEVEL2 0x58 >> -#define EXYNOS4210_TMU_REG_TRIG_LEVEL3 0x5C >> -#define EXYNOS4210_TMU_REG_PAST_TEMP0 0x60 >> -#define EXYNOS4210_TMU_REG_PAST_TEMP1 0x64 >> -#define EXYNOS4210_TMU_REG_PAST_TEMP2 0x68 >> -#define EXYNOS4210_TMU_REG_PAST_TEMP3 0x6C >> - >> -#define EXYNOS4210_TMU_TRIG_LEVEL0_MASK 0x1 >> -#define EXYNOS4210_TMU_TRIG_LEVEL1_MASK 0x10 >> -#define EXYNOS4210_TMU_TRIG_LEVEL2_MASK 0x100 >> -#define EXYNOS4210_TMU_TRIG_LEVEL3_MASK 0x1000 >> -#define EXYNOS4210_TMU_INTCLEAR_VAL 0x1111 >> - >> -/* Exynos5250 and Exynos4412 specific registers */ >> -#define EXYNOS_TMU_TRIMINFO_CON 0x14 >> -#define EXYNOS_THD_TEMP_RISE 0x50 >> -#define EXYNOS_THD_TEMP_FALL 0x54 >> -#define EXYNOS_EMUL_CON 0x80 >> - >> -#define EXYNOS_TRIMINFO_RELOAD 0x1 >> -#define EXYNOS_TMU_CLEAR_RISE_INT 0x111 >> -#define EXYNOS_TMU_CLEAR_FALL_INT (0x111 << 12) >> -#define EXYNOS_MUX_ADDR_VALUE 6 >> -#define EXYNOS_MUX_ADDR_SHIFT 20 >> -#define EXYNOS_TMU_TRIP_MODE_SHIFT 13 >> - >> -#define EFUSE_MIN_VALUE 40 >> -#define EFUSE_MAX_VALUE 100 >> - >> -/* In-kernel thermal framework related macros & definations */ >> -#define SENSOR_NAME_LEN 16 >> -#define MAX_TRIP_COUNT 8 >> -#define MAX_COOLING_DEVICE 4 >> -#define MAX_THRESHOLD_LEVS 4 >> - >> -#define ACTIVE_INTERVAL 500 >> -#define IDLE_INTERVAL 10000 >> -#define MCELSIUS 1000 >> - >> -#ifdef CONFIG_THERMAL_EMULATION >> -#define EXYNOS_EMUL_TIME 0x57F0 >> -#define EXYNOS_EMUL_TIME_SHIFT 16 >> -#define EXYNOS_EMUL_DATA_SHIFT 8 >> -#define EXYNOS_EMUL_DATA_MASK 0xFF >> -#define EXYNOS_EMUL_ENABLE 0x1 >> -#endif /* CONFIG_THERMAL_EMULATION */ >> - >> -/* CPU Zone information */ >> -#define PANIC_ZONE 4 >> -#define WARN_ZONE 3 >> -#define MONITOR_ZONE 2 >> -#define SAFE_ZONE 1 >> - >> -#define GET_ZONE(trip) (trip + 2) >> -#define GET_TRIP(zone) (zone - 2) >> - >> -#define EXYNOS_ZONE_COUNT 3 >> - >> -struct exynos_tmu_data { >> - struct exynos_tmu_platform_data *pdata; >> - struct resource *mem; >> - void __iomem *base; >> - int irq; >> - enum soc_type soc; >> - struct work_struct irq_work; >> - struct mutex lock; >> - struct clk *clk; >> - u8 temp_error1, temp_error2; >> -}; >> - >> -struct thermal_trip_point_conf { >> - int trip_val[MAX_TRIP_COUNT]; >> - int trip_count; >> - u8 trigger_falling; >> -}; >> - >> -struct thermal_cooling_conf { >> - struct freq_clip_table freq_data[MAX_TRIP_COUNT]; >> - int freq_clip_count; >> -}; >> - >> -struct thermal_sensor_conf { >> - char name[SENSOR_NAME_LEN]; >> - int (*read_temperature)(void *data); >> - int (*write_emul_temp)(void *drv_data, unsigned long temp); >> - struct thermal_trip_point_conf trip_data; >> - struct thermal_cooling_conf cooling_data; >> - void *driver_data; >> - void *pzone_data; >> -}; >> - >> -struct exynos_thermal_zone { >> - enum thermal_device_mode mode; >> - struct thermal_zone_device *therm_dev; >> - struct thermal_cooling_device *cool_dev[MAX_COOLING_DEVICE]; >> - unsigned int cool_dev_size; >> - struct platform_device *exynos4_dev; >> - struct thermal_sensor_conf *sensor_conf; >> - bool bind; >> -}; >> - >> -static void exynos_unregister_thermal(struct thermal_sensor_conf >> *sensor_conf); >> -static int exynos_register_thermal(struct thermal_sensor_conf >> *sensor_conf); >> - >> -/* Get mode callback functions for thermal zone */ >> -static int exynos_get_mode(struct thermal_zone_device *thermal, >> - enum thermal_device_mode *mode) >> -{ >> - struct exynos_thermal_zone *th_zone = thermal->devdata; >> - if (th_zone) >> - *mode = th_zone->mode; >> - return 0; >> -} >> - >> -/* Set mode callback functions for thermal zone */ >> -static int exynos_set_mode(struct thermal_zone_device *thermal, >> - enum thermal_device_mode mode) >> -{ >> - struct exynos_thermal_zone *th_zone = thermal->devdata; >> - if (!th_zone) { >> - pr_notice("thermal zone not registered\n"); >> - return 0; >> - } >> - >> - mutex_lock(&thermal->lock); >> - >> - if (mode == THERMAL_DEVICE_ENABLED && >> - !th_zone->sensor_conf->trip_data.trigger_falling) >> - thermal->polling_delay = IDLE_INTERVAL; >> - else >> - thermal->polling_delay = 0; >> - >> - mutex_unlock(&thermal->lock); >> - >> - th_zone->mode = mode; >> - thermal_zone_device_update(thermal); >> - pr_info("thermal polling set for duration=%d msec\n", >> - thermal->polling_delay); >> - return 0; >> -} >> - >> - >> -/* Get trip type callback functions for thermal zone */ >> -static int exynos_get_trip_type(struct thermal_zone_device *thermal, int >> trip, >> - enum thermal_trip_type *type) >> -{ >> - switch (GET_ZONE(trip)) { >> - case MONITOR_ZONE: >> - case WARN_ZONE: >> - *type = THERMAL_TRIP_ACTIVE; >> - break; >> - case PANIC_ZONE: >> - *type = THERMAL_TRIP_CRITICAL; >> - break; >> - default: >> - return -EINVAL; >> - } >> - return 0; >> -} >> - >> -/* Get trip temperature callback functions for thermal zone */ >> -static int exynos_get_trip_temp(struct thermal_zone_device *thermal, int >> trip, >> - unsigned long *temp) >> -{ >> - struct exynos_thermal_zone *th_zone = thermal->devdata; >> - >> - if (trip < GET_TRIP(MONITOR_ZONE) || trip > GET_TRIP(PANIC_ZONE)) >> - return -EINVAL; >> - >> - *temp = th_zone->sensor_conf->trip_data.trip_val[trip]; >> - /* convert the temperature into millicelsius */ >> - *temp = *temp * MCELSIUS; >> - >> - return 0; >> -} >> - >> -/* Get critical temperature callback functions for thermal zone */ >> -static int exynos_get_crit_temp(struct thermal_zone_device *thermal, >> - unsigned long *temp) >> -{ >> - int ret; >> - /* Panic zone */ >> - ret = exynos_get_trip_temp(thermal, GET_TRIP(PANIC_ZONE), temp); >> - return ret; >> -} >> - >> -static int exynos_get_frequency_level(unsigned int cpu, unsigned int >> freq) >> -{ >> - int i = 0, ret = -EINVAL; >> - struct cpufreq_frequency_table *table = NULL; >> -#ifdef CONFIG_CPU_FREQ >> - table = cpufreq_frequency_get_table(cpu); >> -#endif >> - if (!table) >> - return ret; >> - >> - while (table[i].frequency != CPUFREQ_TABLE_END) { >> - if (table[i].frequency == CPUFREQ_ENTRY_INVALID) >> - continue; >> - if (table[i].frequency == freq) >> - return i; >> - i++; >> - } >> - return ret; >> -} >> - >> -/* Bind callback functions for thermal zone */ >> -static int exynos_bind(struct thermal_zone_device *thermal, >> - struct thermal_cooling_device *cdev) >> -{ >> - int ret = 0, i, tab_size, level; >> - struct freq_clip_table *tab_ptr, *clip_data; >> - struct exynos_thermal_zone *th_zone = thermal->devdata; >> - struct thermal_sensor_conf *data = th_zone->sensor_conf; >> - >> - tab_ptr = (struct freq_clip_table *)data->cooling_data.freq_data; >> - tab_size = data->cooling_data.freq_clip_count; >> - >> - if (tab_ptr == NULL || tab_size == 0) >> - return -EINVAL; >> - >> - /* find the cooling device registered*/ >> - for (i = 0; i < th_zone->cool_dev_size; i++) >> - if (cdev == th_zone->cool_dev[i]) >> - break; >> - >> - /* No matching cooling device */ >> - if (i == th_zone->cool_dev_size) >> - return 0; >> - >> - /* Bind the thermal zone to the cpufreq cooling device */ >> - for (i = 0; i < tab_size; i++) { >> - clip_data = (struct freq_clip_table *)&(tab_ptr[i]); >> - level = exynos_get_frequency_level(0, >> clip_data->freq_clip_max); >> - if (level < 0) >> - return 0; >> - switch (GET_ZONE(i)) { >> - case MONITOR_ZONE: >> - case WARN_ZONE: >> - if (thermal_zone_bind_cooling_device(thermal, i, >> cdev, >> - level, 0)) >> { >> - pr_err("error binding cdev inst %d\n", i); >> - ret = -EINVAL; >> - } >> - th_zone->bind = true; >> - break; >> - default: >> - ret = -EINVAL; >> - } >> - } >> - >> - return ret; >> -} >> - >> -/* Unbind callback functions for thermal zone */ >> -static int exynos_unbind(struct thermal_zone_device *thermal, >> - struct thermal_cooling_device *cdev) >> -{ >> - int ret = 0, i, tab_size; >> - struct exynos_thermal_zone *th_zone = thermal->devdata; >> - struct thermal_sensor_conf *data = th_zone->sensor_conf; >> - >> - if (th_zone->bind == false) >> - return 0; >> - >> - tab_size = data->cooling_data.freq_clip_count; >> - >> - if (tab_size == 0) >> - return -EINVAL; >> - >> - /* find the cooling device registered*/ >> - for (i = 0; i < th_zone->cool_dev_size; i++) >> - if (cdev == th_zone->cool_dev[i]) >> - break; >> - >> - /* No matching cooling device */ >> - if (i == th_zone->cool_dev_size) >> - return 0; >> - >> - /* Bind the thermal zone to the cpufreq cooling device */ >> - for (i = 0; i < tab_size; i++) { >> - switch (GET_ZONE(i)) { >> - case MONITOR_ZONE: >> - case WARN_ZONE: >> - if (thermal_zone_unbind_cooling_device(thermal, i, >> - cdev)) { >> - pr_err("error unbinding cdev inst=%d\n", >> i); >> - ret = -EINVAL; >> - } >> - th_zone->bind = false; >> - break; >> - default: >> - ret = -EINVAL; >> - } >> - } >> - return ret; >> -} >> - >> -/* Get temperature callback functions for thermal zone */ >> -static int exynos_get_temp(struct thermal_zone_device *thermal, >> - unsigned long *temp) >> -{ >> - struct exynos_thermal_zone *th_zone = thermal->devdata; >> - void *data; >> - >> - if (!th_zone->sensor_conf) { >> - pr_info("Temperature sensor not initialised\n"); >> - return -EINVAL; >> - } >> - data = th_zone->sensor_conf->driver_data; >> - *temp = th_zone->sensor_conf->read_temperature(data); >> - /* convert the temperature into millicelsius */ >> - *temp = *temp * MCELSIUS; >> - return 0; >> -} >> - >> -/* Get temperature callback functions for thermal zone */ >> -static int exynos_set_emul_temp(struct thermal_zone_device *thermal, >> - unsigned long temp) >> -{ >> - void *data; >> - int ret = -EINVAL; >> - struct exynos_thermal_zone *th_zone = thermal->devdata; >> - >> - if (!th_zone->sensor_conf) { >> - pr_info("Temperature sensor not initialised\n"); >> - return -EINVAL; >> - } >> - data = th_zone->sensor_conf->driver_data; >> - if (th_zone->sensor_conf->write_emul_temp) >> - ret = th_zone->sensor_conf->write_emul_temp(data, temp); >> - return ret; >> -} >> - >> -/* Get the temperature trend */ >> -static int exynos_get_trend(struct thermal_zone_device *thermal, >> - int trip, enum thermal_trend *trend) >> -{ >> - int ret = 0; >> - unsigned long trip_temp; >> - >> - ret = exynos_get_trip_temp(thermal, trip, &trip_temp); >> - if (ret < 0) >> - return ret; >> - >> - if (thermal->temperature >= trip_temp) >> - *trend = THERMAL_TREND_RAISE_FULL; >> - else >> - *trend = THERMAL_TREND_DROP_FULL; >> - >> - return ret; >> -} >> -/* Operation callback functions for thermal zone */ >> -static struct thermal_zone_device_ops const exynos_dev_ops = { >> - .bind = exynos_bind, >> - .unbind = exynos_unbind, >> - .get_temp = exynos_get_temp, >> - .set_emul_temp = exynos_set_emul_temp, >> - .get_trend = exynos_get_trend, >> - .get_mode = exynos_get_mode, >> - .set_mode = exynos_set_mode, >> - .get_trip_type = exynos_get_trip_type, >> - .get_trip_temp = exynos_get_trip_temp, >> - .get_crit_temp = exynos_get_crit_temp, >> -}; >> - >> -/* >> - * This function may be called from interrupt based temperature sensor >> - * when threshold is changed. >> - */ >> -static void exynos_report_trigger(struct thermal_sensor_conf *conf) >> -{ >> - unsigned int i; >> - char data[10]; >> - char *envp[] = { data, NULL }; >> - struct exynos_thermal_zone *th_zone = conf->pzone_data; >> - >> - if (!th_zone || !th_zone->therm_dev) >> - return; >> - if (th_zone->bind == false) { >> - for (i = 0; i < th_zone->cool_dev_size; i++) { >> - if (!th_zone->cool_dev[i]) >> - continue; >> - exynos_bind(th_zone->therm_dev, >> - th_zone->cool_dev[i]); >> - } >> - } >> - >> - thermal_zone_device_update(th_zone->therm_dev); >> - >> - mutex_lock(&th_zone->therm_dev->lock); >> - /* Find the level for which trip happened */ >> - for (i = 0; i < th_zone->sensor_conf->trip_data.trip_count; i++) { >> - if (th_zone->therm_dev->last_temperature < >> - th_zone->sensor_conf->trip_data.trip_val[i] * >> MCELSIUS) >> - break; >> - } >> - >> - if (th_zone->mode == THERMAL_DEVICE_ENABLED && >> - !th_zone->sensor_conf->trip_data.trigger_falling) { >> - if (i > 0) >> - th_zone->therm_dev->polling_delay = >> ACTIVE_INTERVAL; >> - else >> - th_zone->therm_dev->polling_delay = IDLE_INTERVAL; >> - } >> - >> - snprintf(data, sizeof(data), "%u", i); >> - kobject_uevent_env(&th_zone->therm_dev->device.kobj, KOBJ_CHANGE, >> envp); >> - mutex_unlock(&th_zone->therm_dev->lock); >> -} >> - >> -/* Register with the in-kernel thermal management */ >> -static int exynos_register_thermal(struct thermal_sensor_conf >> *sensor_conf) >> -{ >> - int ret; >> - struct cpumask mask_val; >> - struct exynos_thermal_zone *th_zone; >> - >> - if (!sensor_conf || !sensor_conf->read_temperature) { >> - pr_err("Temperature sensor not initialised\n"); >> - return -EINVAL; >> - } >> - >> - th_zone = kzalloc(sizeof(struct exynos_thermal_zone), GFP_KERNEL); >> - if (!th_zone) >> - return -ENOMEM; >> - >> - th_zone->sensor_conf = sensor_conf; >> - cpumask_set_cpu(0, &mask_val); >> - th_zone->cool_dev[0] = cpufreq_cooling_register(&mask_val); >> - if (IS_ERR(th_zone->cool_dev[0])) { >> - pr_err("Failed to register cpufreq cooling device\n"); >> - ret = -EINVAL; >> - goto err_unregister; >> - } >> - th_zone->cool_dev_size++; >> - >> - th_zone->therm_dev = >> thermal_zone_device_register(sensor_conf->name, >> - EXYNOS_ZONE_COUNT, 0, th_zone, &exynos_dev_ops, >> NULL, 0, >> - sensor_conf->trip_data.trigger_falling ? >> - 0 : IDLE_INTERVAL); >> - >> - if (IS_ERR(th_zone->therm_dev)) { >> - pr_err("Failed to register thermal zone device\n"); >> - ret = PTR_ERR(th_zone->therm_dev); >> - goto err_unregister; >> - } >> - th_zone->mode = THERMAL_DEVICE_ENABLED; >> - sensor_conf->pzone_data = th_zone; >> - >> - pr_info("Exynos: Kernel Thermal management registered\n"); >> - >> - return 0; >> - >> -err_unregister: >> - exynos_unregister_thermal(sensor_conf); >> - return ret; >> -} >> - >> -/* Un-Register with the in-kernel thermal management */ >> -static void exynos_unregister_thermal(struct thermal_sensor_conf >> *sensor_conf) >> -{ >> - int i; >> - struct exynos_thermal_zone *th_zone = sensor_conf->pzone_data; >> - >> - if (!th_zone) >> - return; >> - >> - if (th_zone->therm_dev) >> - thermal_zone_device_unregister(th_zone->therm_dev); >> - >> - for (i = 0; i < th_zone->cool_dev_size; i++) { >> - if (th_zone->cool_dev[i]) >> - cpufreq_cooling_unregister(th_zone->cool_dev[i]); -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/