Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753856Ab3DLLcZ (ORCPT ); Fri, 12 Apr 2013 07:32:25 -0400 Received: from mail-ia0-f180.google.com ([209.85.210.180]:44674 "EHLO mail-ia0-f180.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752629Ab3DLLcW (ORCPT ); Fri, 12 Apr 2013 07:32:22 -0400 MIME-Version: 1.0 In-Reply-To: <51672548.3010106@ti.com> References: <1364297642-2746-8-git-send-email-amit.daniel@samsung.com> <51672548.3010106@ti.com> Date: Fri, 12 Apr 2013 17:02:22 +0530 X-Google-Sender-Auth: m_xHdGzI354MXGzdhlW9anuXj4U Message-ID: Subject: Re: [7/9] thermal: exynos: Add support for exynos5440 TMU sensor. 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: 30876 Lines: 854 Hi Eduardo, On Fri, Apr 12, 2013 at 2:34 AM, Eduardo Valentin wrote: > > Amit, > > > On 26-03-2013 07:34, Amit Daniel Kachhap wrote: >> >> This sensor registers 3 instance of the tmu controller with the thermal >> zone >> and hence reports 3 temperature output. This driver supports upto five >> trip >> points. For critical threshold the driver uses the core driver thermal >> framework for shutdown and for non-critical threshold it invokes the hw >> based >> frequency clipping limits. Because of such differences with the existing >> 4210 >> tmu controller, exynos5440 tmu driver is added in a new file. >> >> Signed-off-by: Amit Daniel Kachhap >> >> --- >> drivers/thermal/samsung/Kconfig | 9 + >> drivers/thermal/samsung/Makefile | 1 + >> drivers/thermal/samsung/exynos5440_thermal.c | 713 >> ++++++++++++++++++++++++++ > > > This driver does not compile as module: > ERROR: "exynos_report_trigger" > [drivers/thermal/samsung/exynos5440_thermal.ko] undefined! > ERROR: "exynos_get_frequency_level" > [drivers/thermal/samsung/exynos5440_thermal.ko] undefined! > ERROR: "exynos_unregister_thermal" > [drivers/thermal/samsung/exynos5440_thermal.ko] undefined! Ok will fix it. > > > Besides, this driver is pretty similar to 4210 driver. Are you you cannot > isolate the difference into config data? Again, check the driver design for > TI SoC thermal (drivers/staging/ti-soc-thermal/ on linux-next) Yes I started with a 4210 file and srarted modifying it but then lot of conditional code started getting inserted and it become very complex. So I splitted this file itself. Will check your implementation. Thanks, Amit Daniel > > >> 3 files changed, 723 insertions(+), 0 deletions(-) >> create mode 100644 drivers/thermal/samsung/exynos5440_thermal.c >> >> diff --git a/drivers/thermal/samsung/Kconfig >> b/drivers/thermal/samsung/Kconfig >> index cefe693..0c7b4eb 100644 >> --- a/drivers/thermal/samsung/Kconfig >> +++ b/drivers/thermal/samsung/Kconfig >> @@ -20,4 +20,13 @@ config EXYNOS4210_THERMAL >> initialises the TMU controller and registers/unregisters with >> exynos >> common thermal layer. >> >> +config EXYNOS5440_THERMAL >> + tristate "Temperature sensor on Samsung EXYNOS 5440 SOC" >> + depends on SOC_EXYNOS5440 >> + help >> + If you say yes here you can enable TMU (Thermal Management Unit) >> + support on SAMSUNG EXYNOS 5440 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 d51d0c2..53230cf 100644 >> --- a/drivers/thermal/samsung/Makefile >> +++ b/drivers/thermal/samsung/Makefile >> @@ -3,3 +3,4 @@ >> # >> obj-$(CONFIG_EXYNOS_COMMON) += exynos_common.o >> obj-$(CONFIG_EXYNOS4210_THERMAL) += exynos4210_thermal.o >> +obj-$(CONFIG_EXYNOS5440_THERMAL) += exynos5440_thermal.o >> diff --git a/drivers/thermal/samsung/exynos5440_thermal.c >> b/drivers/thermal/samsung/exynos5440_thermal.c >> new file mode 100644 >> index 0000000..a3c75d3 >> --- /dev/null >> +++ b/drivers/thermal/samsung/exynos5440_thermal.c >> @@ -0,0 +1,713 @@ >> +/* >> + * exynos5440_thermal.c - Samsung EXYNOS 5440 TMU >> + * (Thermal Management Unit) >> + * >> + * 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 >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> + >> +#include "exynos_common.h" >> + >> + >> +/* Exynos5440 specific registers */ >> +#define TMU_S0_7_TRIM 0x0118 >> +#define TMU_S0_7_CTRL 0x0138 >> +#define TMU_S0_7_DEBUG 0x0158 >> +#define TMU_S0_7_STATUS 0x0178 >> +#define TMU_S0_7_COUNTER0 0x0198 >> +#define TMU_S0_7_COUNTER1 0x01b8 >> +#define TMU_S0_7_COUNTER2 0x01d8 >> +#define TMU_S0_7_COUNTER3 0x01f8 >> +#define TMU_S0_7_TEMP 0x0208 >> +#define TMU_S0_7_TH0 0x0228 >> +#define TMU_S0_7_TH1 0x0248 >> +#define TMU_S0_7_TH2 0x0268 >> +#define TMU_S0_7_PTEMP0 0x0288 >> +#define TMU_S0_7_PTEMP1 0x02a8 >> +#define TMU_S0_7_PTEMP2 0x02c8 >> +#define TMU_S0_7_PTEMP3 0x02e8 >> +#define TMU_S0_7_EVTEN 0x0308 >> +#define TMU_S0_7_IRQEN 0x0328 >> +#define TMU_S0_7_IRQ 0x0348 >> +#define TMU_IRQ_STATUS 0x0368 >> +#define TMU_PMIN 0x036c >> +#define TMU_TEMP 0x0370 >> +#define TMU_MISC 0x0374 >> + >> +/* Exynos5440 specific mask and shifts */ >> +#define TMU_TEMP_MASK 0xff >> + >> +#define TMU_TRIM_DATA_25C_SHIFT 0x0 >> +#define TMU_TRIM_DATA_85C_SHIFT 0x8 >> + >> +#define TMU_BUF_VREF_SEL_MASK 0x1f >> +#define TMU_BUF_VREF_SEL_SHIFT 24 >> +#define TMU_THERM_TRIP_MODE_MASK 0x7 >> +#define TMU_THERM_TRIP_MODE_SHIFT 13 >> +#define TMU_THERM_TRIP_EN_SHIFT 12 >> +#define TMU_BUF_SLOPE_SEL_MASK 0Xf >> +#define TMU_BUF_SLOPE_SEL_SHIFT 8 >> +#define TMU_THERM_IRQ_MODE_SHIFT 7 >> +#define TMU_CALIB_MODE_MASK 0x3 >> +#define TMU_CALIB_MODE_SHIFT 4 >> +#define TMU_FILTER_MODE_MASK 0x7 >> +#define TMU_FILTER_MODE_SHIFT 1 >> +#define TMU_SENSOR_EN_SHIFT 0 >> +#define TMU_SENSOR_ENABLE 0x1 >> + >> +#define TMU_EMU_EN_SHIFT 0 >> +#define TMU_TEMP_EMU_SHIFT 8 >> +#define TMU_EMUL_ENABLE 1 >> + >> +#define TMU_STATUS_IDLE_SHIFT 0 >> + >> +#define TMU_TIME_MASK 0xffff >> +#define TMU_TIME_OF_SHIFT 16 >> +#define TMU_TIME_ON_SHIFT 0 >> + >> +#define TMU_CURRENT_TEMP_SHIFT 0 >> +#define TMU_FILTERED_TEMP_SHIFT 8 >> +#define TMU_RAW_TEMP_SHIFT 16 >> +#define TMU_TEMP_SEQNUM 24 >> + >> +#define TMU_THRES_RISE0_SHIFT 0 >> +#define TMU_THRES_RISE1_SHIFT 8 >> +#define TMU_THRES_RISE2_SHIFT 16 >> +#define TMU_THRES_RISE3_SHIFT 24 >> + >> +#define TMU_THRES_FALL0_SHIFT 0 >> +#define TMU_THRES_FALL1_SHIFT 8 >> +#define TMU_THRES_FALL2_SHIFT 16 >> +#define TMU_THRES_FALL3_SHIFT 24 >> + >> +#define TMU_THRES_RISE4_SHIFT 24 >> + >> +#define TMU_RISE_EVTEN_MASK 0xf >> +#define TMU_RISE_EVTEN_SHIFT 0 >> +#define TMU_FALL_EVTEN_MASK 0xf >> +#define TMU_FALL_EVTEN_SHIFT 4 >> + >> +#define TMU_RISE_IRQEN_MASK 0xf >> +#define TMU_RISE_IRQEN_SHIFT 0 >> +#define TMU_FALL_IRQEN_MASK 0xf >> +#define TMU_FALL_IRQEN_SHIFT 4 >> +#define TMU_CLEAR_RISE_INT TMU_RISE_IRQEN_MASK >> +#define TMU_CLEAR_FALL_INT (TMU_FALL_IRQEN_MASK << 4) >> + >> +#define TMU_PMIN_MASK 0x7 >> +#define TMU_PMIN0_SHIFT 0 >> +#define TMU_PMIN1_SHIFT 4 >> +#define TMU_PMIN2_SHIFT 8 >> +#define TMU_PMIN3_SHIFT 12 >> +#define TMU_PMIN_SHIFT(x) (4 * x) >> +#define TMU_TPMIN_SHIFT 16 >> + >> +#define TMU_TEMP_MAX_SHIFT 0 >> +#define TMU_MAX_RISE_LEVEL 4 >> +#define TMU_MAX_FALL_LEVEL 4 >> +#define TMU_MAX_SENSOR 8 >> + >> +#define TMU_DEF_CODE_TO_TEMP_OFFSET 20 >> + >> +struct exynos_tmu_data { >> + int irq; >> + int id; >> + unsigned int shift; >> + enum soc_type soc; >> + void __iomem *base; >> + struct clk *clk; >> + struct work_struct irq_work; >> + u8 temp_error1, temp_error2; >> + struct mutex lock; >> + struct thermal_sensor_conf *reg_conf; >> + struct exynos_tmu_platform_data *pdata; >> +}; >> + >> +struct exynos_tmu_common { >> + int level[TMU_MAX_SENSOR]; >> + int sensor_count; >> +}; >> +static struct exynos_tmu_common tmu_common; >> +/* >> + * 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 (pdata->cal_mode == HW_MODE) >> + return temp; >> + >> + switch (pdata->cal_type) { >> + case TYPE_TWO_POINT_TRIMMING: >> + temp_code = (temp - 25) * >> + (data->temp_error2 - data->temp_error1) / >> + (70 - 25) + data->temp_error1; >> + break; >> + case TYPE_ONE_POINT_TRIMMING: >> + temp_code = temp + data->temp_error1 - 25; >> + break; >> + default: >> + temp_code = temp + TMU_DEF_CODE_TO_TEMP_OFFSET; >> + break; >> + } >> + >> + 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 (pdata->cal_mode == HW_MODE) >> + return temp_code; >> + >> + switch (pdata->cal_type) { >> + case TYPE_TWO_POINT_TRIMMING: >> + temp = (temp_code - data->temp_error1) * (70 - 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 - TMU_DEF_CODE_TO_TEMP_OFFSET; >> + break; >> + } >> + >> + 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, con, trim_info; >> + unsigned int rising_threshold = 0, falling_threshold = 0; >> + int ret = 0, threshold_code, i, trigger_levs = 0; >> + >> + status = readl(data->base + data->shift + TMU_S0_7_STATUS); >> + status &= 0x1; >> + if (!status) >> + dev_err(&pdev->dev, "Sensor Initial status is busy\n"); >> + >> + if (pdata->cal_mode == HW_MODE) >> + goto skip_calib_data; >> + >> + /* Save trimming info in order to perform calibration */ >> + trim_info = readl(data->base + data->shift + TMU_S0_7_TRIM); >> + data->temp_error1 = trim_info & TMU_TEMP_MASK; >> + data->temp_error2 = ((trim_info >> 8) & TMU_TEMP_MASK); >> + if (!data->temp_error1) >> + data->temp_error1 = pdata->efuse_value & TMU_TEMP_MASK; >> + if (!data->temp_error2) >> + data->temp_error2 = (pdata->efuse_value >> 8) & >> TMU_TEMP_MASK; >> + >> +skip_calib_data: >> + /* Count trigger levels to be enabled */ >> + for (i = 0; i < MAX_THRESHOLD_LEVS; i++) >> + if (pdata->trigger_levels[i]) >> + trigger_levs++; >> + >> + /* Write temperature code for rising and falling threshold */ >> + for (i = 0; (i < trigger_levs && i < TMU_MAX_RISE_LEVEL); i++) { >> + threshold_code = temp_to_code(data, >> + pdata->trigger_levels[i]); >> + if (threshold_code < 0) { >> + ret = threshold_code; >> + dev_err(&pdev->dev, "Invalid threshold=%d >> level=%d\n", >> + threshold_code, >> i); >> + 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 + data->shift + TMU_S0_7_TH0); >> + writel(falling_threshold, >> + data->base + data->shift + TMU_S0_7_TH1); >> + >> + /* if 5th threshold limit is also present */ >> + if (i == TMU_MAX_RISE_LEVEL) { >> + threshold_code = temp_to_code(data, >> + pdata->trigger_levels[i]); >> + if (threshold_code < 0) { >> + ret = threshold_code; >> + dev_err(&pdev->dev, "Invalid threshold=%d >> level=%d\n", >> + threshold_code, >> i); >> + goto out; >> + } >> + rising_threshold = threshold_code << >> TMU_THRES_RISE4_SHIFT; >> + writel(rising_threshold, >> + data->base + data->shift + TMU_S0_7_TH2); >> + con = readl(data->base + data->shift + TMU_S0_7_CTRL); >> + con |= (1 << TMU_THERM_TRIP_EN_SHIFT); >> + writel(con, data->base + data->shift + TMU_S0_7_CTRL); >> + } >> + >> + writel(TMU_CLEAR_RISE_INT | TMU_CLEAR_FALL_INT, >> + data->base + data->shift + TMU_S0_7_IRQ); >> + >> + /* clear all PMIN */ >> + writel(0, data->base + TMU_PMIN); >> +out: >> + 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); >> + con = readl(data->base + data->shift + TMU_S0_7_CTRL); >> + con &= ~(TMU_BUF_VREF_SEL_MASK << TMU_BUF_VREF_SEL_SHIFT | >> + TMU_THERM_TRIP_MODE_MASK << TMU_THERM_TRIP_MODE_SHIFT | >> + TMU_BUF_SLOPE_SEL_MASK << TMU_BUF_SLOPE_SEL_SHIFT | >> + TMU_CALIB_MODE_MASK << TMU_CALIB_MODE_SHIFT | >> + TMU_FILTER_MODE_MASK << TMU_FILTER_MODE_SHIFT | >> + TMU_SENSOR_ENABLE << TMU_SENSOR_EN_SHIFT); >> + >> + con |= pdata->reference_voltage << TMU_BUF_VREF_SEL_SHIFT | >> + pdata->gain << TMU_BUF_SLOPE_SEL_SHIFT; >> + >> + if (pdata->cal_mode == HW_MODE) >> + con |= pdata->cal_type << TMU_CALIB_MODE_SHIFT; >> + >> + con |= pdata->noise_cancel_mode << TMU_THERM_TRIP_MODE_SHIFT; >> + >> + if (on) { >> + con |= TMU_SENSOR_ENABLE; >> + interrupt_en = >> + pdata->trigger_enable[3] << 3 | >> + pdata->trigger_enable[2] << 2 | >> + pdata->trigger_enable[1] << 1 | >> + pdata->trigger_enable[0] << 0; >> + if (pdata->threshold_falling) >> + interrupt_en |= interrupt_en << >> TMU_FALL_IRQEN_SHIFT; >> + } else { >> + interrupt_en = 0; /* Disable all interrupts */ >> + } >> + writel(interrupt_en, data->base + data->shift + TMU_S0_7_IRQEN); >> + writel(interrupt_en, data->base + data->shift + TMU_S0_7_EVTEN); >> + writel(con, data->base + data->shift + TMU_S0_7_CTRL); >> + >> + mutex_unlock(&data->lock); >> +} >> + >> +static int exynos_tmu_read(struct exynos_tmu_data *data) >> +{ >> + u8 temp_code; >> + int temp; >> + >> + mutex_lock(&data->lock); > > > Dont you need to enable clocks? > > >> + >> + temp_code = readl(data->base + data->shift + TMU_S0_7_TEMP); >> + temp_code >>= TMU_CURRENT_TEMP_SHIFT; >> + temp_code &= TMU_TEMP_MASK; >> + temp = code_to_temp(data, temp_code); >> + >> + mutex_unlock(&data->lock); >> + >> + return temp; >> +} >> + >> +#ifdef CONFIG_THERMAL_EMULATION >> +static int exynos_tmu_set_emulation(struct exynos_tmu_data *data, >> + unsigned long temp) >> +{ >> + unsigned int reg; >> + >> + if (temp && temp < MCELSIUS) >> + goto out; >> + >> + mutex_lock(&data->lock); >> + reg = readl(data->base + data->shift + TMU_S0_7_DEBUG); >> + >> + if (temp) { >> + temp /= MCELSIUS; >> + reg &= ~(TMU_TEMP_MASK << TMU_TEMP_EMU_SHIFT); >> + reg |= (temp_to_code(data, temp) << TMU_TEMP_EMU_SHIFT) | >> + TMU_EMUL_ENABLE; >> + } else { >> + reg &= ~TMU_EMUL_ENABLE; >> + } >> + >> + writel(reg, data->base + data->shift + TMU_S0_7_DEBUG); >> + mutex_unlock(&data->lock); >> + return 0; >> +out: >> + return -EINVAL; >> +} >> +#endif >> + >> +static void exynos_tmu_set_cooling(struct exynos_tmu_data *data, int >> level, >> + unsigned int cur_temp) >> +{ >> + struct exynos_tmu_platform_data *pdata = data->pdata; >> + bool check_rise, change; >> + unsigned int thres_temp, freq = 0, val; >> + int i, index, max_level = 0; >> + >> + /* Get the max level across all sensors except this */ >> + for (i = 0; i < tmu_common.sensor_count; i++) { >> + if (i == data->id) >> + continue; >> + if (tmu_common.level[i] > max_level) >> + max_level = tmu_common.level[i]; >> + } >> + change = false; >> + if (level < TMU_MAX_RISE_LEVEL) { >> + thres_temp = readl(data->base + data->shift + >> TMU_S0_7_TH0); >> + thres_temp = (thres_temp >> (level * 8) & TMU_TEMP_MASK); >> + check_rise = true; >> + tmu_common.level[data->id] = level + 1; >> + if (tmu_common.level[data->id] > max_level) >> + change = true; >> + } else { >> + level -= TMU_MAX_RISE_LEVEL; >> + thres_temp = readl(data->base + data->shift + >> TMU_S0_7_TH1); >> + thres_temp = (thres_temp >> (level * 8) & TMU_TEMP_MASK); >> + check_rise = false; >> + tmu_common.level[data->id] = level; >> + if (tmu_common.level[data->id] >= max_level) >> + change = true; >> + } >> + >> + if (change == false) >> + return; >> + >> + thres_temp = code_to_temp(data, thres_temp); >> + if (!check_rise) >> + thres_temp += pdata->threshold_falling; >> + >> + change = false; >> + /* find this threshold temp in the patform table cooling data */ >> + for (i = 0; i < pdata->freq_tab_count; i++) { >> + if (thres_temp != pdata->freq_tab[i].temp_level) >> + continue; >> + >> + if (check_rise && cur_temp >= thres_temp) { >> + freq = pdata->freq_tab[i].freq_clip_max; >> + change = true; >> + } >> + if (!check_rise && >> + (cur_temp <= (thres_temp - pdata->threshold_falling))) { >> + change = true; >> + freq = 0; >> + } >> + } >> + >> + /* critical threshold temp */ >> + if (thres_temp == pdata->trigger_levels[TMU_MAX_RISE_LEVEL - 1]) >> + exynos_report_trigger(data->reg_conf); >> + >> + if (change == false) >> + return; >> + >> + index = 0; >> + >> + if (freq) { >> + index = exynos_get_frequency_level(0, freq); >> + if (index < 0) >> + return; >> + } >> + >> + val = readl(data->base + TMU_PMIN); >> + val &= (~(TMU_PMIN_MASK << TMU_PMIN_SHIFT(level))); >> + val |= (index << TMU_PMIN_SHIFT(level)); >> + writel(val, data->base + TMU_PMIN); >> +} >> + >> +static void exynos_tmu_work(struct work_struct *work) >> +{ >> + struct exynos_tmu_data *data = container_of(work, >> + struct exynos_tmu_data, irq_work); >> + int i, cur_temp; >> + unsigned int val_type, val_irq; >> + >> + if (!data) >> + goto out; >> + >> + val_type = readl(data->base + TMU_IRQ_STATUS); >> + >> + /* Find which sensor generated this interrupt */ >> + if (!((val_type >> data->id) & 0x1)) >> + goto out; >> + >> + cur_temp = exynos_tmu_read(data); >> + val_irq = readl(data->base + data->shift + TMU_S0_7_IRQ); >> + for (i = 0; i < (TMU_MAX_RISE_LEVEL + TMU_MAX_FALL_LEVEL); i++) { >> + if (!((val_irq >> i) & 0x1)) >> + continue; >> + exynos_tmu_set_cooling(data, i, cur_temp); >> + } >> + /* clear the interrupts */ >> + writel(val_irq, data->base + data->shift + TMU_S0_7_IRQ); >> +out: >> + 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; >> +} >> + >> +static const struct of_device_id exynos_tmu_match[] = { >> + { >> + .compatible = "samsung,exynos5440-tmu", >> + }, >> + {}, >> +}; >> +MODULE_DEVICE_TABLE(of, exynos_tmu_match); >> + >> +int exynos_map_dt_data(struct platform_device *pdev) >> +{ >> + struct exynos_tmu_data *data = platform_get_drvdata(pdev); >> + struct resource res; >> + >> + if (!data) >> + return -ENODEV; >> + >> + data->id = of_alias_get_id(pdev->dev.of_node, "tmuctrl"); >> + if (data->id < 0) >> + data->id = 0; >> + >> + data->shift = data->id * 4; >> + >> + data->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); >> + if (data->irq <= 0) { >> + dev_err(&pdev->dev, "failed to get IRQ\n"); >> + return -ENODEV; >> + } >> + >> + if (of_address_to_resource(pdev->dev.of_node, 0, &res)) { >> + dev_err(&pdev->dev, "failed to get Resource\n"); >> + return -ENODEV; >> + } >> + >> + /* clear the last 16 bytes */ >> + res.start &= (~(0xFFFF)); >> + data->base = devm_ioremap(&pdev->dev, res.start, >> resource_size(&res)); >> + if (!data->base) { >> + dev_err(&pdev->dev, "Failed to ioremap memory\n"); >> + return -ENOMEM; >> + } >> + return 0; >> +} >> + >> +static int exynos_tmu_probe(struct platform_device *pdev) >> +{ >> + struct exynos_tmu_data *data; >> + struct exynos_tmu_platform_data *pdata; >> + struct thermal_sensor_conf *sensor_conf; >> + int ret, i; >> + >> + 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; >> + } >> + >> + pdata = (struct exynos_tmu_platform_data *) >> + platform_get_device_id(pdev)->driver_data; >> + if (!pdata) { >> + dev_err(&pdev->dev, "No platform init data supplied.\n"); >> + return -ENODEV; >> + } >> + >> + data->pdata = pdata; >> + platform_set_drvdata(pdev, data); >> + >> + ret = exynos_map_dt_data(pdev); >> + if (ret) >> + goto unset_data; >> + >> + INIT_WORK(&data->irq_work, exynos_tmu_work); >> + >> + ret = devm_request_irq(&pdev->dev, data->irq, exynos_tmu_irq, >> + IRQF_TRIGGER_RISING|IRQF_SHARED, dev_name(&pdev->dev), >> data); >> + if (ret) { >> + dev_err(&pdev->dev, "Failed to request irq: %d\n", >> data->irq); >> + goto unset_data; >> + } >> + >> + data->clk = of_clk_get(pdev->dev.of_node, 0); >> + if (IS_ERR(data->clk)) { >> + dev_err(&pdev->dev, "Failed to get tmu clock\n"); >> + ret = PTR_ERR(data->clk); >> + goto unset_data; >> + } >> + clk_enable(data->clk); >> + > > > hmmm ok, you want it to be always running, right? > > >> + 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); >> + >> + /* Allocate a structure to register with the exynos core thermal >> */ >> + sensor_conf = devm_kzalloc(&pdev->dev, >> + sizeof(struct thermal_sensor_conf), >> GFP_KERNEL); >> + if (!sensor_conf) { >> + dev_err(&pdev->dev, "Failed to allocate registration >> struct\n"); >> + ret = -ENOMEM; >> + goto err_clk; >> + } >> + data->reg_conf = sensor_conf; >> + sprintf(sensor_conf->name, "therm_zone%d", data->id); >> + sensor_conf->read_temperature = (int (*)(void *))exynos_tmu_read; >> +#ifdef CONFIG_THERMAL_EMULATION >> + sensor_conf->write_emul_temp = >> + (int (*)(void *, unsigned long))exynos_tmu_set_emulation; >> +#endif > > > Do you really need this ifdef here? Cant you do same as you have done for > 4210? > > >> + sensor_conf->driver_data = data; >> + sensor_conf->trip_data.trip_count = pdata->trigger_enable[0] + >> + pdata->trigger_enable[1] + >> pdata->trigger_enable[2] + >> + pdata->trigger_enable[3]; >> + >> + for (i = 0; i < sensor_conf->trip_data.trip_count; i++) >> + sensor_conf->trip_data.trip_val[i] = >> pdata->trigger_levels[i]; >> + >> + sensor_conf->trip_data.trigger_falling = pdata->threshold_falling; >> + >> + /* Register the sensor with thermal management interface */ >> + ret = exynos_register_thermal(sensor_conf); >> + if (ret) { >> + dev_err(&pdev->dev, "Failed to register thermal >> interface\n"); >> + goto err_clk; >> + } >> + tmu_common.sensor_count++; >> + return 0; >> +err_clk: >> + clk_disable(data->clk); >> + clk_put(data->clk); >> +unset_data: >> + platform_set_drvdata(pdev, NULL); >> + return ret; >> +} >> + >> +static int exynos_tmu_remove(struct platform_device *pdev) >> +{ >> + struct exynos_tmu_data *data = platform_get_drvdata(pdev); >> + struct thermal_sensor_conf *sensor_conf = data->reg_conf; >> + >> + exynos_tmu_control(pdev, false); >> + clk_disable(data->clk); >> + >> + exynos_unregister_thermal(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) >> +{ >> + struct platform_device *pdev = to_platform_device(dev); >> + struct exynos_tmu_data *data = platform_get_drvdata(pdev); >> + >> + exynos_tmu_control(pdev, false); >> + clk_disable(data->clk); >> + >> + return 0; >> +} >> + >> +static int exynos_tmu_resume(struct device *dev) >> +{ >> + struct platform_device *pdev = to_platform_device(dev); >> + struct exynos_tmu_data *data = platform_get_drvdata(pdev); >> + >> + clk_enable(data->clk); >> + 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 = "exynos5440-tmu", >> + .owner = THIS_MODULE, >> + .pm = EXYNOS_TMU_PM, >> + .of_match_table = exynos_tmu_match, >> + }, >> + .probe = exynos_tmu_probe, >> + .remove = exynos_tmu_remove, >> +}; >> + >> +module_platform_driver(exynos_tmu_driver); >> + >> +MODULE_DESCRIPTION("EXYNOS5440 TMU Driver"); >> +MODULE_AUTHOR("Amit Daniel"); >> +MODULE_LICENSE("GPL"); > > GPL v2? > >> +MODULE_ALIAS("platform:exynos5440-tmu"); >> > -- 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/