2013-03-26 11:34:22

by amit daniel kachhap

[permalink] [raw]
Subject: [PATCH 0/9] thermal: exynos: Add thermal driver for exynos5440

This patchset adds TMU(Thermal management Unit) driver support for
exynos5440 platform. There are 3 instances of the TMU controllers so
necessary cleanup is done to handle multiple thermal zone.

Patch 1 [thermal: exynos: Adapt to temperature emulation core thermal]
is a re-post of the earlier posted patch,
https://patchwork.kernel.org/patch/2123131/.

All these patches are based on thermal maintainers git tree,
git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux.git next.

Amit Daniel Kachhap (9):
thermal: exynos: Adapt to temperature emulation core thermal
framework
thermal: exynos: Add support for instance based register/unregister
thermal: exynos: Moving into samsung directory for easy maintenance.
thermal: exynos: Bifurcate exynos thermal common and tmu controller
code
thermal: exynos: Make the zone handling dependent on trip count
thermal: exynos: small cleanups to prepare for adding exynos5440
driver
thermal: exynos: Add support for exynos5440 TMU sensor.
thermal: exynos: Parse the platform data from the device tree.
ARM: dts: Add device tree node for exynos5440 TMU controller

.../bindings/thermal/exynos5440-thermal.txt | 93 ++
Documentation/thermal/exynos_thermal_emulation | 8 +-
arch/arm/boot/dts/exynos5440.dtsi | 43 +
drivers/thermal/Kconfig | 22 +-
drivers/thermal/Makefile | 2 +-
drivers/thermal/exynos_thermal.c | 1112 --------------------
drivers/thermal/samsung/Kconfig | 32 +
drivers/thermal/samsung/Makefile | 6 +
drivers/thermal/samsung/exynos4210_thermal.c | 658 ++++++++++++
drivers/thermal/samsung/exynos5440_thermal.c | 712 +++++++++++++
drivers/thermal/samsung/exynos_common.c | 515 +++++++++
drivers/thermal/samsung/exynos_common.h | 74 ++
include/linux/platform_data/exynos_thermal.h | 37 +-
13 files changed, 2163 insertions(+), 1151 deletions(-)
create mode 100644 Documentation/devicetree/bindings/thermal/exynos5440-thermal.txt
delete mode 100644 drivers/thermal/exynos_thermal.c
create mode 100644 drivers/thermal/samsung/Kconfig
create mode 100644 drivers/thermal/samsung/Makefile
create mode 100644 drivers/thermal/samsung/exynos4210_thermal.c
create mode 100644 drivers/thermal/samsung/exynos5440_thermal.c
create mode 100644 drivers/thermal/samsung/exynos_common.c
create mode 100644 drivers/thermal/samsung/exynos_common.h


2013-03-26 11:34:30

by amit daniel kachhap

[permalink] [raw]
Subject: [PATCH 1/9] thermal: exynos: Adapt to temperature emulation core thermal framework

This removes the driver specific sysfs support of the temperature
emulation and uses the newly added core thermal framework for thermal
emulation. An exynos platform specific handler is added to support this.

In this patch, the exynos senor(tmu) related code and exynos framework
related (thermal zone, cooling devices) code are intentionally kept separate.
So an emulated function pointer is passed from sensor to framework. This is
beneficial in adding more sensor support using the same framework code
which is an ongoing work. The goal is to finally split them totally. Even
the existing read_temperature also follows the same execution method.

Acked-by: Kukjin Kim <[email protected]>
Signed-off-by: Amit Daniel Kachhap <[email protected]>
---
Documentation/thermal/exynos_thermal_emulation | 8 +-
drivers/thermal/Kconfig | 9 --
drivers/thermal/exynos_thermal.c | 158 ++++++++++--------------
3 files changed, 67 insertions(+), 108 deletions(-)

diff --git a/Documentation/thermal/exynos_thermal_emulation b/Documentation/thermal/exynos_thermal_emulation
index b73bbfb..36a3e79 100644
--- a/Documentation/thermal/exynos_thermal_emulation
+++ b/Documentation/thermal/exynos_thermal_emulation
@@ -13,11 +13,11 @@ Thermal emulation mode supports software debug for TMU's operation. User can set
manually with software code and TMU will read current temperature from user value not from
sensor's value.

-Enabling CONFIG_EXYNOS_THERMAL_EMUL option will make this support in available.
-When it's enabled, sysfs node will be created under
-/sys/bus/platform/devices/'exynos device name'/ with name of 'emulation'.
+Enabling CONFIG_THERMAL_EMULATION option will make this support available.
+When it's enabled, sysfs node will be created as
+/sys/devices/virtual/thermal/thermal_zone'zone id'/emul_temp.

-The sysfs node, 'emulation', will contain value 0 for the initial state. When you input any
+The sysfs node, 'emul_node', will contain value 0 for the initial state. When you input any
temperature you want to update to sysfs node, it automatically enable emulation mode and
current temperature will be changed into it.
(Exynos also supports user changable delay time which would be used to delay of
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index a764f16..da4c19e 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -117,15 +117,6 @@ config EXYNOS_THERMAL
If you say yes here you get support for TMU (Thermal Management
Unit) on SAMSUNG EXYNOS series of SoC.

-config EXYNOS_THERMAL_EMUL
- bool "EXYNOS TMU emulation mode support"
- depends on EXYNOS_THERMAL
- help
- Exynos 4412 and 4414 and 5 series has emulation mode on TMU.
- Enable this option will be make sysfs node in exynos thermal platform
- device directory to support emulation mode. With emulation mode sysfs
- node, you can manually input temperature to TMU for simulation purpose.
-
config DOVE_THERMAL
tristate "Temperature sensor on Marvell Dove SoCs"
depends on ARCH_DOVE
diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c
index 46568c0..1cd7837 100644
--- a/drivers/thermal/exynos_thermal.c
+++ b/drivers/thermal/exynos_thermal.c
@@ -100,13 +100,13 @@
#define IDLE_INTERVAL 10000
#define MCELSIUS 1000

-#ifdef CONFIG_EXYNOS_THERMAL_EMUL
+#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_EXYNOS_THERMAL_EMUL */
+#endif /* CONFIG_THERMAL_EMULATION */

/* CPU Zone information */
#define PANIC_ZONE 4
@@ -145,6 +145,7 @@ struct thermal_cooling_conf {
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 *private_data;
@@ -369,6 +370,23 @@ static int exynos_get_temp(struct thermal_zone_device *thermal,
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;
+
+ if (!th_zone->sensor_conf) {
+ pr_info("Temperature sensor not initialised\n");
+ return -EINVAL;
+ }
+ data = th_zone->sensor_conf->private_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)
@@ -392,6 +410,7 @@ 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,
@@ -714,6 +733,47 @@ static int exynos_tmu_read(struct exynos_tmu_data *data)
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 void exynos_tmu_work(struct work_struct *work)
{
struct exynos_tmu_data *data = container_of(work,
@@ -747,6 +807,7 @@ static irqreturn_t exynos_tmu_irq(int irq, void *id)
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,
};

#if defined(CONFIG_CPU_EXYNOS4210)
@@ -853,93 +914,6 @@ static inline struct exynos_tmu_platform_data *exynos_get_driver_data(
platform_get_device_id(pdev)->driver_data;
}

-#ifdef CONFIG_EXYNOS_THERMAL_EMUL
-static ssize_t exynos_tmu_emulation_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct platform_device *pdev = container_of(dev,
- struct platform_device, dev);
- struct exynos_tmu_data *data = platform_get_drvdata(pdev);
- unsigned int reg;
- u8 temp_code;
- int temp = 0;
-
- if (data->soc == SOC_ARCH_EXYNOS4210)
- goto out;
-
- mutex_lock(&data->lock);
- clk_enable(data->clk);
- reg = readl(data->base + EXYNOS_EMUL_CON);
- clk_disable(data->clk);
- mutex_unlock(&data->lock);
-
- if (reg & EXYNOS_EMUL_ENABLE) {
- reg >>= EXYNOS_EMUL_DATA_SHIFT;
- temp_code = reg & EXYNOS_EMUL_DATA_MASK;
- temp = code_to_temp(data, temp_code);
- }
-out:
- return sprintf(buf, "%d\n", temp * MCELSIUS);
-}
-
-static ssize_t exynos_tmu_emulation_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct platform_device *pdev = container_of(dev,
- struct platform_device, dev);
- struct exynos_tmu_data *data = platform_get_drvdata(pdev);
- unsigned int reg;
- int temp;
-
- if (data->soc == SOC_ARCH_EXYNOS4210)
- goto out;
-
- if (!sscanf(buf, "%d\n", &temp) || temp < 0)
- return -EINVAL;
-
- mutex_lock(&data->lock);
- clk_enable(data->clk);
-
- reg = readl(data->base + EXYNOS_EMUL_CON);
-
- if (temp) {
- /* Both CELSIUS and MCELSIUS type are available for input */
- if (temp > MCELSIUS)
- temp /= MCELSIUS;
-
- reg = (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT) |
- (temp_to_code(data, (temp / MCELSIUS))
- << 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);
-
-out:
- return count;
-}
-
-static DEVICE_ATTR(emulation, 0644, exynos_tmu_emulation_show,
- exynos_tmu_emulation_store);
-static int create_emulation_sysfs(struct device *dev)
-{
- return device_create_file(dev, &dev_attr_emulation);
-}
-static void remove_emulation_sysfs(struct device *dev)
-{
- device_remove_file(dev, &dev_attr_emulation);
-}
-#else
-static inline int create_emulation_sysfs(struct device *dev) { return 0; }
-static inline void remove_emulation_sysfs(struct device *dev) {}
-#endif
-
static int exynos_tmu_probe(struct platform_device *pdev)
{
struct exynos_tmu_data *data;
@@ -1039,10 +1013,6 @@ static int exynos_tmu_probe(struct platform_device *pdev)
goto err_clk;
}

- ret = create_emulation_sysfs(&pdev->dev);
- if (ret)
- dev_err(&pdev->dev, "Failed to create emulation mode sysfs node\n");
-
return 0;
err_clk:
platform_set_drvdata(pdev, NULL);
@@ -1054,8 +1024,6 @@ static int exynos_tmu_remove(struct platform_device *pdev)
{
struct exynos_tmu_data *data = platform_get_drvdata(pdev);

- remove_emulation_sysfs(&pdev->dev);
-
exynos_tmu_control(pdev, false);

exynos_unregister_thermal();
--
1.7.1

2013-03-26 11:34:37

by amit daniel kachhap

[permalink] [raw]
Subject: [PATCH 3/9] thermal: exynos: Moving into samsung directory for easy maintenance.

This movement of files is done for easy maintenance and adding more
new sensor's support for exynos platform easily . This will also help in
bifurcating exynos common and sensor related parts.

Signed-off-by: Amit Daniel Kachhap <[email protected]>
---
drivers/thermal/Kconfig | 13 +-
drivers/thermal/Makefile | 2 +-
drivers/thermal/exynos_thermal.c | 1093 ------------------------------
drivers/thermal/samsung/Kconfig | 11 +
drivers/thermal/samsung/Makefile | 5 +
drivers/thermal/samsung/exynos_thermal.c | 1093 ++++++++++++++++++++++++++++++
6 files changed, 1115 insertions(+), 1102 deletions(-)
delete mode 100644 drivers/thermal/exynos_thermal.c
create mode 100644 drivers/thermal/samsung/Kconfig
create mode 100644 drivers/thermal/samsung/Makefile
create mode 100644 drivers/thermal/samsung/exynos_thermal.c

diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index da4c19e..9b2372a 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -109,14 +109,6 @@ config KIRKWOOD_THERMAL
Support for the Kirkwood thermal sensor driver into the Linux thermal
framework. Only kirkwood 88F6282 and 88F6283 have this sensor.

-config EXYNOS_THERMAL
- tristate "Temperature sensor on Samsung EXYNOS"
- depends on (ARCH_EXYNOS4 || ARCH_EXYNOS5)
- depends on CPU_THERMAL
- help
- If you say yes here you get support for TMU (Thermal Management
- Unit) on SAMSUNG EXYNOS series of SoC.
-
config DOVE_THERMAL
tristate "Temperature sensor on Marvell Dove SoCs"
depends on ARCH_DOVE
@@ -156,4 +148,9 @@ config INTEL_POWERCLAMP
enforce idle time which results in more package C-state residency. The
user interface is exposed via generic thermal framework.

+menu "Exynos thermal drivers"
+depends on PLAT_SAMSUNG
+source "drivers/thermal/samsung/Kconfig"
+endmenu
+
endif
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index d3a2b38..584862c 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -16,7 +16,7 @@ obj-$(CONFIG_CPU_THERMAL) += cpu_cooling.o
obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o
obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o
obj-$(CONFIG_KIRKWOOD_THERMAL) += kirkwood_thermal.o
-obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o
+obj-y += samsung/
obj-$(CONFIG_DOVE_THERMAL) += dove_thermal.o
obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o
obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o
diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c
deleted file mode 100644
index dc9b91b..0000000
--- a/drivers/thermal/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 <[email protected]>
- * Amit Daniel Kachhap <[email protected]>
- *
- * 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 <linux/module.h>
-#include <linux/err.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/clk.h>
-#include <linux/workqueue.h>
-#include <linux/sysfs.h>
-#include <linux/kobject.h>
-#include <linux/io.h>
-#include <linux/mutex.h>
-#include <linux/platform_data/exynos_thermal.h>
-#include <linux/thermal.h>
-#include <linux/cpufreq.h>
-#include <linux/cpu_cooling.h>
-#include <linux/of.h>
-
-#include <plat/cpu.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
-
-/* 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]);
- }
-
- kfree(th_zone);
- pr_info("Exynos: Kernel Thermal management unregistered\n");
-}
-
-/*
- * 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 <[email protected]>");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:exynos-tmu");
diff --git a/drivers/thermal/samsung/Kconfig b/drivers/thermal/samsung/Kconfig
new file mode 100644
index 0000000..5737b85
--- /dev/null
+++ b/drivers/thermal/samsung/Kconfig
@@ -0,0 +1,11 @@
+
+config EXYNOS_THERMAL
+ tristate "Temperature sensor on Samsung EXYNOS"
+ depends on (ARCH_EXYNOS4 || ARCH_EXYNOS5)
+ 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.
+
diff --git a/drivers/thermal/samsung/Makefile b/drivers/thermal/samsung/Makefile
new file mode 100644
index 0000000..fa55df5
--- /dev/null
+++ b/drivers/thermal/samsung/Makefile
@@ -0,0 +1,5 @@
+#
+# Samsung thermal specific Makefile
+#
+obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o
+
diff --git a/drivers/thermal/samsung/exynos_thermal.c b/drivers/thermal/samsung/exynos_thermal.c
new file mode 100644
index 0000000..dc9b91b
--- /dev/null
+++ b/drivers/thermal/samsung/exynos_thermal.c
@@ -0,0 +1,1093 @@
+/*
+ * exynos_thermal.c - Samsung EXYNOS TMU (Thermal Management Unit)
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * Donggeun Kim <[email protected]>
+ * Amit Daniel Kachhap <[email protected]>
+ *
+ * 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 <linux/module.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/workqueue.h>
+#include <linux/sysfs.h>
+#include <linux/kobject.h>
+#include <linux/io.h>
+#include <linux/mutex.h>
+#include <linux/platform_data/exynos_thermal.h>
+#include <linux/thermal.h>
+#include <linux/cpufreq.h>
+#include <linux/cpu_cooling.h>
+#include <linux/of.h>
+
+#include <plat/cpu.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
+
+/* 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]);
+ }
+
+ kfree(th_zone);
+ pr_info("Exynos: Kernel Thermal management unregistered\n");
+}
+
+/*
+ * 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 <[email protected]>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:exynos-tmu");
--
1.7.1

2013-03-26 11:34:47

by amit daniel kachhap

[permalink] [raw]
Subject: [PATCH 5/9] thermal: exynos: Make the zone handling dependent on trip count

This code changes the zone handling to use the trip count passed
by the TMU driver. This also helps in adding more zone support.

Signed-off-by: Amit Daniel Kachhap <[email protected]>
---
drivers/thermal/samsung/exynos_common.c | 56 +++++++++++++------------
drivers/thermal/samsung/exynos_common.h | 13 +++---
include/linux/platform_data/exynos_thermal.h | 7 ++-
3 files changed, 40 insertions(+), 36 deletions(-)

diff --git a/drivers/thermal/samsung/exynos_common.c b/drivers/thermal/samsung/exynos_common.c
index 649d67c..0c0098d 100644
--- a/drivers/thermal/samsung/exynos_common.c
+++ b/drivers/thermal/samsung/exynos_common.c
@@ -84,17 +84,16 @@ static int exynos_set_mode(struct thermal_zone_device *thermal,
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:
+ struct exynos_thermal_zone *th_zone = thermal->devdata;
+ int max_trip = th_zone->sensor_conf->trip_data.trip_count;
+
+ if (trip < 0 || trip >= max_trip)
return -EINVAL;
- }
+ else if (trip == (max_trip - 1))
+ *type = THERMAL_TRIP_CRITICAL;
+ else
+ *type = THERMAL_TRIP_ACTIVE;
+
return 0;
}

@@ -103,8 +102,9 @@ static int exynos_get_trip_temp(struct thermal_zone_device *thermal, int trip,
unsigned long *temp)
{
struct exynos_thermal_zone *th_zone = thermal->devdata;
+ int max_trip = th_zone->sensor_conf->trip_data.trip_count;

- if (trip < GET_TRIP(MONITOR_ZONE) || trip > GET_TRIP(PANIC_ZONE))
+ if (trip < 0 || trip >= max_trip)
return -EINVAL;

*temp = th_zone->sensor_conf->trip_data.trip_val[trip];
@@ -118,10 +118,10 @@ static int exynos_get_trip_temp(struct thermal_zone_device *thermal, int trip,
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;
+ struct exynos_thermal_zone *th_zone = thermal->devdata;
+ int max_trip = th_zone->sensor_conf->trip_data.trip_count;
+ /* Get the temp of highest trip*/
+ return exynos_get_trip_temp(thermal, max_trip - 1, temp);
}

int exynos_get_frequency_level(unsigned int cpu, unsigned int freq)
@@ -157,7 +157,7 @@ static int exynos_bind(struct thermal_zone_device *thermal,
tab_size = data->cooling_data.freq_clip_count;

if (tab_ptr == NULL || tab_size == 0)
- return -EINVAL;
+ return 0;

/* find the cooling device registered*/
for (i = 0; i < th_zone->cool_dev_size; i++)
@@ -206,7 +206,7 @@ static int exynos_unbind(struct thermal_zone_device *thermal,
tab_size = data->cooling_data.freq_clip_count;

if (tab_size == 0)
- return -EINVAL;
+ return 0;

/* find the cooling device registered*/
for (i = 0; i < th_zone->cool_dev_size; i++)
@@ -366,19 +366,21 @@ int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf)
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;
+ if (sensor_conf->cooling_data.freq_clip_count > 0) {
+ 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->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);
+ sensor_conf->trip_data.trip_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");
diff --git a/drivers/thermal/samsung/exynos_common.h b/drivers/thermal/samsung/exynos_common.h
index b8d289e..453e09a 100644
--- a/drivers/thermal/samsung/exynos_common.h
+++ b/drivers/thermal/samsung/exynos_common.h
@@ -27,23 +27,22 @@
#define SENSOR_NAME_LEN 16
#define MAX_TRIP_COUNT 8
#define MAX_COOLING_DEVICE 4
-#define MAX_THRESHOLD_LEVS 4
+#define MAX_THRESHOLD_LEVS 5

#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 PANIC_ZONE 5
+#define ALARM_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;
diff --git a/include/linux/platform_data/exynos_thermal.h b/include/linux/platform_data/exynos_thermal.h
index da7e627..893b758 100644
--- a/include/linux/platform_data/exynos_thermal.h
+++ b/include/linux/platform_data/exynos_thermal.h
@@ -23,6 +23,8 @@
#define _LINUX_EXYNOS_THERMAL_H
#include <linux/cpu_cooling.h>

+#define MAX_TRIP 5
+
enum calibration_type {
TYPE_ONE_POINT_TRIMMING,
TYPE_TWO_POINT_TRIMMING,
@@ -100,11 +102,12 @@ struct freq_clip_table {
struct exynos_tmu_platform_data {
u8 threshold;
u8 threshold_falling;
- u8 trigger_levels[4];
+ u8 trigger_levels[MAX_TRIP];
bool trigger_level0_en;
bool trigger_level1_en;
bool trigger_level2_en;
bool trigger_level3_en;
+ bool trigger_level4_en;

u8 gain;
u8 reference_voltage;
@@ -113,7 +116,7 @@ struct exynos_tmu_platform_data {

enum calibration_type cal_type;
enum soc_type type;
- struct freq_clip_table freq_tab[4];
+ struct freq_clip_table freq_tab[MAX_TRIP];
unsigned int freq_tab_count;
};
#endif /* _LINUX_EXYNOS_THERMAL_H */
--
1.7.1

2013-03-26 11:34:57

by amit daniel kachhap

[permalink] [raw]
Subject: [PATCH 7/9] thermal: exynos: Add support for exynos5440 TMU sensor.

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 <[email protected]>
---
drivers/thermal/samsung/Kconfig | 9 +
drivers/thermal/samsung/Makefile | 1 +
drivers/thermal/samsung/exynos5440_thermal.c | 713 ++++++++++++++++++++++++++
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 <[email protected]>
+ *
+ * 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 <linux/clk.h>
+#include <linux/cpufreq.h>
+#include <linux/cpu_cooling.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/kobject.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/thermal.h>
+#include <linux/workqueue.h>
+#include <linux/platform_data/exynos_thermal.h>
+
+#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);
+
+ 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);
+
+ 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
+ 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<[email protected]>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:exynos5440-tmu");
--
1.7.1

2013-03-26 11:35:05

by amit daniel kachhap

[permalink] [raw]
Subject: [PATCH 8/9] thermal: exynos: Parse the platform data from the device tree.

This patch adds code to parse the DT based platform data like threshold temp,
sensor configuration parameters like gain, reference voltages, calibration
modes etc.

Signed-off-by: Amit Daniel Kachhap <[email protected]>
---
.../bindings/thermal/exynos5440-thermal.txt | 93 ++++++++++++++++++++
drivers/thermal/samsung/exynos5440_thermal.c | 5 +-
drivers/thermal/samsung/exynos_common.c | 92 +++++++++++++++++++
drivers/thermal/samsung/exynos_common.h | 2 +
4 files changed, 189 insertions(+), 3 deletions(-)
create mode 100644 Documentation/devicetree/bindings/thermal/exynos5440-thermal.txt

diff --git a/Documentation/devicetree/bindings/thermal/exynos5440-thermal.txt b/Documentation/devicetree/bindings/thermal/exynos5440-thermal.txt
new file mode 100644
index 0000000..1ad2dee
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/exynos5440-thermal.txt
@@ -0,0 +1,93 @@
+
+Exynos5440 TMU driver
+-------------------
+
+Exynos5440 SoC Thermal Sensor driver for CPU temperature measurement and
+performing cooling actions.
+
+Required properties:
+- interrupts: Interrupt to know the threshold change interrupts.
+- clocks: phandle of the clock from common clock binding. More description can
+ be found in Documentation/devicetree/bindings/clock/clock-bindings.txt.
+- clock-name: clock name as used in the clock fetch.
+- tmu-ctrl-data: phandle of the TMU controller configuration data. Its member
+ description is shown below,
+- gain: A factor describing the gain of amplifier. The value varies between
+ 0-15.
+- reference-voltage: A factor describing the reference volt to amplifier. The
+ value varies between 0-31.
+- noise-cancel-mode: This value selects thermal tripping mode. The possible
+ values can be,
+ 0: Use current temp.
+ 4: Use current temp and past 4 temp values.
+ 5: Use current temp and past 8 temp values.
+ 6: Use current temp and past 12 temp values.
+ 7: Use current temp and past 16 temp values.
+-cal-type: This value selects temperature calibration mode. The possible values
+ can be,
+ 0: No calibration.
+ 1: 1 point trimming method at 25 C.
+ 2: 1 point trimming method at 85 C.
+ 3: 2 point trimming method.
+-cal-mode: This value selects hw/sw mode calibration. The possible values can be,
+ 0: Software calibration.
+ 1: Hardware calibration.
+
+Optional properties:
+-efuse-value: This value should be used when the controller does not contain
+ valid fused temperature data needed for calibration. This is a 16 bit
+ value.
+-threshold-falling: This value defines the falling threshold when it is added to
+ trip threshold. If this value is not supplied then rising and falling
+ threshold are same.
+
+-trip@: This node is added to define the trip properties. The trip members are
+ shown below,
+-trip: This field defines the trip ID. Exynos5440 has 5 trip ID.
+-trigger-temp: Temperature at which threshold trigger is fired. Its unit is
+ celsius.
+-type: This denotes the type of trigger. The possible values are,
+ 0: active trip type
+ 1: critical
+ 2: hw system trip
+-frequency-max: cpu frequency when this trip is reached.
+
+Example:
+--------
+ tmu_ctrl_info: tmu-ctrl-info {
+ gain = <8>;
+ reference-voltage = <16>;
+ noise-cancel-mode = <4>;
+ cal-type = <0>;
+ cal-mode = <0>;
+ efuse-value = <0xabcd>;
+ threshold-falling = <5>;
+
+ trip@0{
+ trip = <0>;
+ trigger-temp = <80>;
+ type = <0>;
+ frequency-max = <1200000>;
+ };
+
+ trip@3{
+ trip = <3>;
+ trigger-temp = <110>;
+ type = <1>;
+ };
+
+ trip@4{
+ trip = <4>;
+ trigger-temp = <120>;
+ type = <2>;
+ };
+ };
+
+ tmuctrl_0: tmuctrl@160118 {
+ compatible = "samsung,exynos5440-tmu";
+ reg = <0x160118 0x300>;
+ interrupts = <0 58 0>;
+ clocks = <&clock 8>;
+ clock-names = "tmu_apbif";
+ tmu-ctrl-data = <&tmu_ctrl_info>;
+ };
diff --git a/drivers/thermal/samsung/exynos5440_thermal.c b/drivers/thermal/samsung/exynos5440_thermal.c
index a3c75d3..c140e8c 100644
--- a/drivers/thermal/samsung/exynos5440_thermal.c
+++ b/drivers/thermal/samsung/exynos5440_thermal.c
@@ -564,9 +564,8 @@ static int exynos_tmu_probe(struct platform_device *pdev)
return -ENOMEM;
}

- pdata = (struct exynos_tmu_platform_data *)
- platform_get_device_id(pdev)->driver_data;
- if (!pdata) {
+ pdata = exynos_parse_tmu_dt_data(pdev);
+ if (!pdata || IS_ERR(pdata)) {
dev_err(&pdev->dev, "No platform init data supplied.\n");
return -ENODEV;
}
diff --git a/drivers/thermal/samsung/exynos_common.c b/drivers/thermal/samsung/exynos_common.c
index 0c0098d..345284c 100644
--- a/drivers/thermal/samsung/exynos_common.c
+++ b/drivers/thermal/samsung/exynos_common.c
@@ -27,7 +27,9 @@
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/mutex.h>
+#include <linux/of.h>
#include <linux/platform_data/exynos_thermal.h>
+#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/thermal.h>
#include "exynos_common.h"
@@ -421,3 +423,93 @@ void exynos_unregister_thermal(struct thermal_sensor_conf *sensor_conf)
kfree(th_zone);
pr_info("Exynos: Kernel Thermal[%d] management unregistered\n", id);
}
+
+struct exynos_tmu_platform_data *
+ exynos_parse_tmu_dt_data(struct platform_device *pdev)
+{
+ int ret;
+ struct device_node *np, *cn;
+ struct exynos_tmu_platform_data *pdata;
+ unsigned int val, trip;
+
+ if (!pdev->dev.of_node)
+ return ERR_PTR(-ENODEV);
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ np = of_parse_phandle(pdev->dev.of_node, "tmu-ctrl-data", 0);
+ if (!np) {
+ ret = -ENODEV;
+ goto err_parse_dt;
+ }
+
+ /* Parse compulsory parameters */
+ ret = of_property_read_u32(np, "gain", &val);
+ if (ret)
+ goto err_parse_dt;
+ pdata->gain = val;
+
+ ret = of_property_read_u32(np, "reference-voltage", &val);
+ if (ret)
+ goto err_parse_dt;
+ pdata->reference_voltage = val;
+
+ ret = of_property_read_u32(np, "noise-cancel-mode", &val);
+ if (ret)
+ goto err_parse_dt;
+ pdata->noise_cancel_mode = val;
+
+ ret = of_property_read_u32(np, "cal-type", &val);
+ if (ret)
+ goto err_parse_dt;
+ pdata->cal_type = val;
+
+ ret = of_property_read_u32(np, "cal-mode", &val);
+ if (ret)
+ goto err_parse_dt;
+ pdata->cal_mode = val;
+
+ /* Parse optional parameters */
+ ret = of_property_read_u32(np, "efuse-value", &val);
+ if (!ret)
+ pdata->efuse_value = val;
+
+ ret = of_property_read_u32(np, "threshold-falling", &val);
+ if (!ret)
+ pdata->threshold_falling = val;
+
+ for (cn = NULL ; (cn = of_get_next_child(np, cn));) {
+ /* Parse compulsory child parameters */
+ ret = of_property_read_u32(cn, "trip", &trip);
+ if (ret || trip > MAX_TRIP)
+ goto err_parse_dt;
+
+ ret = of_property_read_u32(cn, "trigger-temp", &val);
+ if (ret || !val)
+ goto err_parse_dt;
+ pdata->trigger_levels[trip] = val;
+
+ ret = of_property_read_u32(cn, "type", &val);
+ if (ret || val > HW_TRIP)
+ goto err_parse_dt;
+ pdata->trigger_type[trip] = val;
+
+ /* Parse optional child parameters */
+ ret = of_property_read_u32(cn, "frequency-max", &val);
+ if (!ret && val) {
+ pdata->freq_tab[pdata->freq_tab_count].freq_clip_max
+ = val;
+ pdata->freq_tab[pdata->freq_tab_count].temp_level
+ = pdata->trigger_levels[trip];
+ pdata->freq_tab_count++;
+ }
+ pdata->trigger_enable[trip] = true;
+ }
+ return pdata;
+
+err_parse_dt:
+ dev_err(&pdev->dev, "Parsing TMU device tree data error.\n");
+ return ERR_PTR(ret);
+}
diff --git a/drivers/thermal/samsung/exynos_common.h b/drivers/thermal/samsung/exynos_common.h
index 453e09a..5ca640b 100644
--- a/drivers/thermal/samsung/exynos_common.h
+++ b/drivers/thermal/samsung/exynos_common.h
@@ -69,4 +69,6 @@ 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);
+struct exynos_tmu_platform_data *
+ exynos_parse_tmu_dt_data(struct platform_device *pdev);
#endif /* _LINUX_EXYNOS_COMMON_H */
--
1.7.1

2013-03-26 11:35:17

by amit daniel kachhap

[permalink] [raw]
Subject: [PATCH 9/9] ARM: dts: Add device tree node for exynos5440 TMU controller

This patch adds device node for TMU controller. There are 3
instances of the controllers so 3 nodes are created.

Signed-off-by: Amit Daniel Kachhap <[email protected]>
---
arch/arm/boot/dts/exynos5440.dtsi | 43 +++++++++++++++++++++++++++++++++++++
1 files changed, 43 insertions(+), 0 deletions(-)

diff --git a/arch/arm/boot/dts/exynos5440.dtsi b/arch/arm/boot/dts/exynos5440.dtsi
index 5f3562a..b74ce9f 100644
--- a/arch/arm/boot/dts/exynos5440.dtsi
+++ b/arch/arm/boot/dts/exynos5440.dtsi
@@ -16,6 +16,12 @@

interrupt-parent = <&gic>;

+ aliases {
+ tmuctrl0 = &tmuctrl_0;
+ tmuctrl1 = &tmuctrl_1;
+ tmuctrl2 = &tmuctrl_2;
+ };
+
gic:interrupt-controller@2E0000 {
compatible = "arm,cortex-a15-gic";
#interrupt-cells = <3>;
@@ -156,4 +162,41 @@
reg = <0x130000 0x1000>;
interrupts = <0 17 0>, <0 16 0>;
};
+
+ tmu_ctrl_info: tmu-ctrl-info {
+ gain = <5>;
+ reference-voltage = <16>;
+ noise-cancel-mode = <4>;
+ cal-type = <2>;
+ cal-mode = <0>;
+ efuse-value = <0x5b2c>;
+ threshold-falling = <5>;
+ };
+
+ tmuctrl_0: tmuctrl@160118 {
+ compatible = "samsung,exynos5440-tmu";
+ reg = <0x160118 0x300>;
+ interrupts = <0 58 0>;
+ clocks = <&clock 8>;
+ clock-names = "tmu_apbif";
+ tmu-ctrl-data = <&tmu_ctrl_info>;
+ };
+
+ tmuctrl_1: tmuctrl@16011C {
+ compatible = "samsung,exynos5440-tmu";
+ reg = <0x16011C 0x300>;
+ interrupts = <0 58 0>;
+ clocks = <&clock 8>;
+ clock-names = "tmu_apbif";
+ tmu-ctrl-data = <&tmu_ctrl_info>;
+ };
+
+ tmuctrl_2: tmuctrl@160120 {
+ compatible = "samsung,exynos5440-tmu";
+ reg = <0x160120 0x300>;
+ interrupts = <0 58 0>;
+ clocks = <&clock 8>;
+ clock-names = "tmu_apbif";
+ tmu-ctrl-data = <&tmu_ctrl_info>;
+ };
};
--
1.7.1

2013-03-26 11:34:44

by amit daniel kachhap

[permalink] [raw]
Subject: [PATCH 4/9] thermal: exynos: Bifurcate exynos thermal common and tmu controller code

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 <[email protected]>
---
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

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 <[email protected]>
+ * Amit Daniel Kachhap <[email protected]>
+ * Amit Daniel Kachhap <[email protected]>
+ *
+ * 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 <linux/clk.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/kobject.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/exynos_thermal.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#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 <[email protected]>");
+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 <[email protected]>
+ *
+ * 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 <linux/cpufreq.h>
+#include <linux/cpu_cooling.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/kobject.h>
+#include <linux/mutex.h>
+#include <linux/platform_data/exynos_thermal.h>
+#include <linux/slab.h>
+#include <linux/thermal.h>
+#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 <[email protected]>
+ *
+ * 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 <[email protected]>
- * Amit Daniel Kachhap <[email protected]>
- *
- * 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 <linux/module.h>
-#include <linux/err.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/clk.h>
-#include <linux/workqueue.h>
-#include <linux/sysfs.h>
-#include <linux/kobject.h>
-#include <linux/io.h>
-#include <linux/mutex.h>
-#include <linux/platform_data/exynos_thermal.h>
-#include <linux/thermal.h>
-#include <linux/cpufreq.h>
-#include <linux/cpu_cooling.h>
-#include <linux/of.h>
-
-#include <plat/cpu.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
-
-/* 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]);
- }
-
- kfree(th_zone);
- pr_info("Exynos: Kernel Thermal management unregistered\n");
-}
-
-/*
- * 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 <[email protected]>");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:exynos-tmu");
--
1.7.1

2013-03-26 11:36:40

by amit daniel kachhap

[permalink] [raw]
Subject: [PATCH 2/9] thermal: exynos: Add support for instance based register/unregister

This code modifies the thermal driver to have multiple thermal zone
support by replacing the global thermal zone varibale with device data
member of thermal_zone_device.

Signed-off-by: Amit Daniel Kachhap <[email protected]>
---
drivers/thermal/exynos_thermal.c | 65 ++++++++++++++++++++++---------------
1 files changed, 39 insertions(+), 26 deletions(-)

diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c
index 1cd7837..dc9b91b 100644
--- a/drivers/thermal/exynos_thermal.c
+++ b/drivers/thermal/exynos_thermal.c
@@ -148,7 +148,8 @@ struct thermal_sensor_conf {
int (*write_emul_temp)(void *drv_data, unsigned long temp);
struct thermal_trip_point_conf trip_data;
struct thermal_cooling_conf cooling_data;
- void *private_data;
+ void *driver_data;
+ void *pzone_data;
};

struct exynos_thermal_zone {
@@ -161,14 +162,14 @@ struct exynos_thermal_zone {
bool bind;
};

-static struct exynos_thermal_zone *th_zone;
-static void exynos_unregister_thermal(void);
+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;
@@ -178,25 +179,26 @@ static int exynos_get_mode(struct thermal_zone_device *thermal,
static int exynos_set_mode(struct thermal_zone_device *thermal,
enum thermal_device_mode mode)
{
- if (!th_zone->therm_dev) {
+ struct exynos_thermal_zone *th_zone = thermal->devdata;
+ if (!th_zone) {
pr_notice("thermal zone not registered\n");
return 0;
}

- mutex_lock(&th_zone->therm_dev->lock);
+ mutex_lock(&thermal->lock);

if (mode == THERMAL_DEVICE_ENABLED &&
!th_zone->sensor_conf->trip_data.trigger_falling)
- th_zone->therm_dev->polling_delay = IDLE_INTERVAL;
+ thermal->polling_delay = IDLE_INTERVAL;
else
- th_zone->therm_dev->polling_delay = 0;
+ thermal->polling_delay = 0;

- mutex_unlock(&th_zone->therm_dev->lock);
+ mutex_unlock(&thermal->lock);

th_zone->mode = mode;
- thermal_zone_device_update(th_zone->therm_dev);
+ thermal_zone_device_update(thermal);
pr_info("thermal polling set for duration=%d msec\n",
- th_zone->therm_dev->polling_delay);
+ thermal->polling_delay);
return 0;
}

@@ -223,6 +225,8 @@ static int exynos_get_trip_type(struct thermal_zone_device *thermal, int trip,
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;

@@ -269,6 +273,7 @@ static int exynos_bind(struct thermal_zone_device *thermal,
{
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;
@@ -315,6 +320,7 @@ 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)
@@ -357,13 +363,14 @@ static int exynos_unbind(struct thermal_zone_device *thermal,
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->private_data;
+ data = th_zone->sensor_conf->driver_data;
*temp = th_zone->sensor_conf->read_temperature(data);
/* convert the temperature into millicelsius */
*temp = *temp * MCELSIUS;
@@ -376,12 +383,13 @@ static int exynos_set_emul_temp(struct thermal_zone_device *thermal,
{
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->private_data;
+ 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;
@@ -391,7 +399,7 @@ static int exynos_set_emul_temp(struct thermal_zone_device *thermal,
static int exynos_get_trend(struct thermal_zone_device *thermal,
int trip, enum thermal_trend *trend)
{
- int ret;
+ int ret = 0;
unsigned long trip_temp;

ret = exynos_get_trip_temp(thermal, trip, &trip_temp);
@@ -403,7 +411,7 @@ static int exynos_get_trend(struct thermal_zone_device *thermal,
else
*trend = THERMAL_TREND_DROP_FULL;

- return 0;
+ return ret;
}
/* Operation callback functions for thermal zone */
static struct thermal_zone_device_ops const exynos_dev_ops = {
@@ -423,11 +431,12 @@ static struct thermal_zone_device_ops const exynos_dev_ops = {
* This function may be called from interrupt based temperature sensor
* when threshold is changed.
*/
-static void exynos_report_trigger(void)
+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;
@@ -468,6 +477,7 @@ 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");
@@ -489,7 +499,7 @@ static int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf)
th_zone->cool_dev_size++;

th_zone->therm_dev = thermal_zone_device_register(sensor_conf->name,
- EXYNOS_ZONE_COUNT, 0, NULL, &exynos_dev_ops, NULL, 0,
+ EXYNOS_ZONE_COUNT, 0, th_zone, &exynos_dev_ops, NULL, 0,
sensor_conf->trip_data.trigger_falling ?
0 : IDLE_INTERVAL);

@@ -499,20 +509,22 @@ static int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf)
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();
+ exynos_unregister_thermal(sensor_conf);
return ret;
}

/* Un-Register with the in-kernel thermal management */
-static void exynos_unregister_thermal(void)
+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;
@@ -774,12 +786,18 @@ 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_report_trigger(&exynos_sensor_conf);
mutex_lock(&data->lock);
clk_enable(data->clk);
if (data->soc == SOC_ARCH_EXYNOS)
@@ -804,11 +822,6 @@ static irqreturn_t exynos_tmu_irq(int irq, void *id)

return IRQ_HANDLED;
}
-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,
-};

#if defined(CONFIG_CPU_EXYNOS4210)
static struct exynos_tmu_platform_data const exynos4210_default_tmu_data = {
@@ -987,7 +1000,7 @@ static int exynos_tmu_probe(struct platform_device *pdev)
exynos_tmu_control(pdev, true);

/* Register the sensor with thermal management interface */
- (&exynos_sensor_conf)->private_data = data;
+ (&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;
@@ -1026,7 +1039,7 @@ static int exynos_tmu_remove(struct platform_device *pdev)

exynos_tmu_control(pdev, false);

- exynos_unregister_thermal();
+ exynos_unregister_thermal(&exynos_sensor_conf);

clk_put(data->clk);

--
1.7.1

2013-03-26 11:42:28

by amit daniel kachhap

[permalink] [raw]
Subject: [PATCH 6/9] thermal: exynos: small cleanups to prepare for adding exynos5440 driver

Add calib mode, trigger types and trigger_enable array. This is needed
for adding exynos5440 TMU driver.

Signed-off-by: Amit Daniel Kachhap <[email protected]>
---
drivers/thermal/samsung/exynos4210_thermal.c | 30 ++++++++++++------------
include/linux/platform_data/exynos_thermal.h | 32 +++++++++++++-------------
2 files changed, 31 insertions(+), 31 deletions(-)

diff --git a/drivers/thermal/samsung/exynos4210_thermal.c b/drivers/thermal/samsung/exynos4210_thermal.c
index 09ea8c8..58d16ac 100644
--- a/drivers/thermal/samsung/exynos4210_thermal.c
+++ b/drivers/thermal/samsung/exynos4210_thermal.c
@@ -276,10 +276,10 @@ static void exynos_tmu_control(struct platform_device *pdev, bool on)

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;
+ interrupt_en = pdata->trigger_enable[3] << 12 |
+ pdata->trigger_enable[2] << 8 |
+ pdata->trigger_enable[1] << 4 |
+ pdata->trigger_enable[0];
if (pdata->threshold_falling)
interrupt_en |= interrupt_en << 16;
} else {
@@ -394,10 +394,10 @@ static struct exynos_tmu_platform_data const exynos4210_default_tmu_data = {
.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,
+ .trigger_enable[0] = 1,
+ .trigger_enable[1] = 1,
+ .trigger_enable[2] = 1,
+ .trigger_enable[3] = 0,
.gain = 15,
.reference_voltage = 7,
.cal_type = TYPE_ONE_POINT_TRIMMING,
@@ -423,10 +423,10 @@ static struct exynos_tmu_platform_data const exynos_default_tmu_data = {
.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,
+ .trigger_enable[0] = 1,
+ .trigger_enable[1] = 1,
+ .trigger_enable[2] = 1,
+ .trigger_enable[3] = 0,
.gain = 8,
.reference_voltage = 16,
.noise_cancel_mode = 4,
@@ -566,9 +566,9 @@ static int exynos_tmu_probe(struct platform_device *pdev)

/* 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;
+ exynos_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 < exynos_sensor_conf.trip_data.trip_count; i++)
exynos_sensor_conf.trip_data.trip_val[i] =
diff --git a/include/linux/platform_data/exynos_thermal.h b/include/linux/platform_data/exynos_thermal.h
index 893b758..1090f48 100644
--- a/include/linux/platform_data/exynos_thermal.h
+++ b/include/linux/platform_data/exynos_thermal.h
@@ -31,6 +31,17 @@ enum calibration_type {
TYPE_NONE,
};

+enum calibration_mode {
+ SW_MODE,
+ HW_MODE,
+};
+
+enum trigger_type {
+ ACTIVE,
+ CRITICAL,
+ HW_TRIP,
+};
+
enum soc_type {
SOC_ARCH_EXYNOS4210 = 1,
SOC_ARCH_EXYNOS,
@@ -71,18 +82,9 @@ struct freq_clip_table {
* 3: temperature for trigger_level3 interrupt
* condition for trigger_level3 interrupt:
* current temperature > threshold + trigger_levels[3]
- * @trigger_level0_en:
+ * @trigger_enable[]: array to denote which trigger levels are enabled.
* 1 = enable trigger_level0 interrupt,
* 0 = disable trigger_level0 interrupt
- * @trigger_level1_en:
- * 1 = enable trigger_level1 interrupt,
- * 0 = disable trigger_level1 interrupt
- * @trigger_level2_en:
- * 1 = enable trigger_level2 interrupt,
- * 0 = disable trigger_level2 interrupt
- * @trigger_level3_en:
- * 1 = enable trigger_level3 interrupt,
- * 0 = disable trigger_level3 interrupt
* @gain: gain of amplifier in the positive-TC generator block
* 0 <= gain <= 15
* @reference_voltage: reference voltage of amplifier
@@ -93,6 +95,7 @@ struct freq_clip_table {
* @type: determines the type of SOC
* @efuse_value: platform defined fuse value
* @cal_type: calibration type for temperature
+ * @cal_mode: calibration mode for temperature
* @freq_clip_table: Table representing frequency reduction percentage.
* @freq_tab_count: Count of the above table as frequency reduction may
* applicable to only some of the trigger levels.
@@ -103,18 +106,15 @@ struct exynos_tmu_platform_data {
u8 threshold;
u8 threshold_falling;
u8 trigger_levels[MAX_TRIP];
- bool trigger_level0_en;
- bool trigger_level1_en;
- bool trigger_level2_en;
- bool trigger_level3_en;
- bool trigger_level4_en;
-
+ enum trigger_type trigger_type[MAX_TRIP];
+ bool trigger_enable[MAX_TRIP];
u8 gain;
u8 reference_voltage;
u8 noise_cancel_mode;
u32 efuse_value;

enum calibration_type cal_type;
+ enum calibration_mode cal_mode;
enum soc_type type;
struct freq_clip_table freq_tab[MAX_TRIP];
unsigned int freq_tab_count;
--
1.7.1

2013-04-02 10:26:52

by Kukjin Kim

[permalink] [raw]
Subject: RE: [PATCH 0/9] thermal: exynos: Add thermal driver for exynos5440

Amit Daniel Kachhap wrote:
>
> This patchset adds TMU(Thermal management Unit) driver support for
> exynos5440 platform. There are 3 instances of the TMU controllers so
> necessary cleanup is done to handle multiple thermal zone.
>
> Patch 1 [thermal: exynos: Adapt to temperature emulation core thermal]
> is a re-post of the earlier posted patch,
> https://patchwork.kernel.org/patch/2123131/.
>

Looks OK to me on whole series, there is my ack only on 1st patch though.

So if you want to add my ack, feel free:

Acked-by: Kukjin Kim <[email protected]>

Thanks.

- Kukjin

> All these patches are based on thermal maintainers git tree,
> git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux.git next.
>
> Amit Daniel Kachhap (9):
> thermal: exynos: Adapt to temperature emulation core thermal
> framework
> thermal: exynos: Add support for instance based register/unregister
> thermal: exynos: Moving into samsung directory for easy maintenance.
> thermal: exynos: Bifurcate exynos thermal common and tmu controller
> code
> thermal: exynos: Make the zone handling dependent on trip count
> thermal: exynos: small cleanups to prepare for adding exynos5440
> driver
> thermal: exynos: Add support for exynos5440 TMU sensor.
> thermal: exynos: Parse the platform data from the device tree.
> ARM: dts: Add device tree node for exynos5440 TMU controller
>
> .../bindings/thermal/exynos5440-thermal.txt | 93 ++
> Documentation/thermal/exynos_thermal_emulation | 8 +-
> arch/arm/boot/dts/exynos5440.dtsi | 43 +
> drivers/thermal/Kconfig | 22 +-
> drivers/thermal/Makefile | 2 +-
> drivers/thermal/exynos_thermal.c | 1112
--------------------
> drivers/thermal/samsung/Kconfig | 32 +
> drivers/thermal/samsung/Makefile | 6 +
> drivers/thermal/samsung/exynos4210_thermal.c | 658 ++++++++++++
> drivers/thermal/samsung/exynos5440_thermal.c | 712 +++++++++++++
> drivers/thermal/samsung/exynos_common.c | 515 +++++++++
> drivers/thermal/samsung/exynos_common.h | 74 ++
> include/linux/platform_data/exynos_thermal.h | 37 +-
> 13 files changed, 2163 insertions(+), 1151 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/thermal/exynos5440-
> thermal.txt
> delete mode 100644 drivers/thermal/exynos_thermal.c
> create mode 100644 drivers/thermal/samsung/Kconfig
> create mode 100644 drivers/thermal/samsung/Makefile
> create mode 100644 drivers/thermal/samsung/exynos4210_thermal.c
> create mode 100644 drivers/thermal/samsung/exynos5440_thermal.c
> create mode 100644 drivers/thermal/samsung/exynos_common.c
> create mode 100644 drivers/thermal/samsung/exynos_common.h

2013-04-08 12:25:07

by Kukjin Kim

[permalink] [raw]
Subject: RE: [PATCH 9/9] ARM: dts: Add device tree node for exynos5440 TMU controller

Amit Daniel Kachhap wrote:
>
> This patch adds device node for TMU controller. There are 3
> instances of the controllers so 3 nodes are created.
>
> Signed-off-by: Amit Daniel Kachhap <[email protected]>
> ---
> arch/arm/boot/dts/exynos5440.dtsi | 43
> +++++++++++++++++++++++++++++++++++++
> 1 files changed, 43 insertions(+), 0 deletions(-)
>
> diff --git a/arch/arm/boot/dts/exynos5440.dtsi
> b/arch/arm/boot/dts/exynos5440.dtsi
> index 5f3562a..b74ce9f 100644
> --- a/arch/arm/boot/dts/exynos5440.dtsi
> +++ b/arch/arm/boot/dts/exynos5440.dtsi
> @@ -16,6 +16,12 @@
>
> interrupt-parent = <&gic>;
>
> + aliases {
> + tmuctrl0 = &tmuctrl_0;
> + tmuctrl1 = &tmuctrl_1;
> + tmuctrl2 = &tmuctrl_2;
> + };
> +
> gic:interrupt-controller@2E0000 {
> compatible = "arm,cortex-a15-gic";
> #interrupt-cells = <3>;
> @@ -156,4 +162,41 @@
> reg = <0x130000 0x1000>;
> interrupts = <0 17 0>, <0 16 0>;
> };
> +
> + tmu_ctrl_info: tmu-ctrl-info {
> + gain = <5>;
> + reference-voltage = <16>;
> + noise-cancel-mode = <4>;
> + cal-type = <2>;
> + cal-mode = <0>;
> + efuse-value = <0x5b2c>;
> + threshold-falling = <5>;
> + };
> +
> + tmuctrl_0: tmuctrl@160118 {
> + compatible = "samsung,exynos5440-tmu";
> + reg = <0x160118 0x300>;
> + interrupts = <0 58 0>;
> + clocks = <&clock 8>;

Should be:
+ clocks = <&clock 21>;

> + clock-names = "tmu_apbif";
> + tmu-ctrl-data = <&tmu_ctrl_info>;
> + };
> +
> + tmuctrl_1: tmuctrl@16011C {
> + compatible = "samsung,exynos5440-tmu";
> + reg = <0x16011C 0x300>;
> + interrupts = <0 58 0>;
> + clocks = <&clock 8>;

+ clocks = <&clock 21>;

> + clock-names = "tmu_apbif";
> + tmu-ctrl-data = <&tmu_ctrl_info>;
> + };
> +
> + tmuctrl_2: tmuctrl@160120 {
> + compatible = "samsung,exynos5440-tmu";
> + reg = <0x160120 0x300>;
> + interrupts = <0 58 0>;
> + clocks = <&clock 8>;

+ clocks = <&clock 21>;

> + clock-names = "tmu_apbif";
> + tmu-ctrl-data = <&tmu_ctrl_info>;
> + };
> };
> --
> 1.7.1

- Kukjin

2013-04-09 05:24:20

by amit daniel kachhap

[permalink] [raw]
Subject: Re: [PATCH 0/9] thermal: exynos: Add thermal driver for exynos5440

Hi Rui,

On Tue, Apr 2, 2013 at 3:56 PM, Kukjin Kim <[email protected]> wrote:
> Amit Daniel Kachhap wrote:
>>
>> This patchset adds TMU(Thermal management Unit) driver support for
>> exynos5440 platform. There are 3 instances of the TMU controllers so
>> necessary cleanup is done to handle multiple thermal zone.
>>
>> Patch 1 [thermal: exynos: Adapt to temperature emulation core thermal]
>> is a re-post of the earlier posted patch,
>> https://patchwork.kernel.org/patch/2123131/.
>>
>
> Looks OK to me on whole series, there is my ack only on 1st patch though.
>
> So if you want to add my ack, feel free:
>
> Acked-by: Kukjin Kim <[email protected]>
>
> Thanks.
>
> - Kukjin

Any comment on this patchset? I know this patch series involves quite
lot of changes but these are important for this merge window. Also
they are now acked by Samsung platform maintainer.

Regards,
Amit Daniel
>
>> All these patches are based on thermal maintainers git tree,
>> git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux.git next.
>>
>> Amit Daniel Kachhap (9):
>> thermal: exynos: Adapt to temperature emulation core thermal
>> framework
>> thermal: exynos: Add support for instance based register/unregister
>> thermal: exynos: Moving into samsung directory for easy maintenance.
>> thermal: exynos: Bifurcate exynos thermal common and tmu controller
>> code
>> thermal: exynos: Make the zone handling dependent on trip count
>> thermal: exynos: small cleanups to prepare for adding exynos5440
>> driver
>> thermal: exynos: Add support for exynos5440 TMU sensor.
>> thermal: exynos: Parse the platform data from the device tree.
>> ARM: dts: Add device tree node for exynos5440 TMU controller
>>
>> .../bindings/thermal/exynos5440-thermal.txt | 93 ++
>> Documentation/thermal/exynos_thermal_emulation | 8 +-
>> arch/arm/boot/dts/exynos5440.dtsi | 43 +
>> drivers/thermal/Kconfig | 22 +-
>> drivers/thermal/Makefile | 2 +-
>> drivers/thermal/exynos_thermal.c | 1112
> --------------------
>> drivers/thermal/samsung/Kconfig | 32 +
>> drivers/thermal/samsung/Makefile | 6 +
>> drivers/thermal/samsung/exynos4210_thermal.c | 658 ++++++++++++
>> drivers/thermal/samsung/exynos5440_thermal.c | 712 +++++++++++++
>> drivers/thermal/samsung/exynos_common.c | 515 +++++++++
>> drivers/thermal/samsung/exynos_common.h | 74 ++
>> include/linux/platform_data/exynos_thermal.h | 37 +-
>> 13 files changed, 2163 insertions(+), 1151 deletions(-)
>> create mode 100644 Documentation/devicetree/bindings/thermal/exynos5440-
>> thermal.txt
>> delete mode 100644 drivers/thermal/exynos_thermal.c
>> create mode 100644 drivers/thermal/samsung/Kconfig
>> create mode 100644 drivers/thermal/samsung/Makefile
>> create mode 100644 drivers/thermal/samsung/exynos4210_thermal.c
>> create mode 100644 drivers/thermal/samsung/exynos5440_thermal.c
>> create mode 100644 drivers/thermal/samsung/exynos_common.c
>> create mode 100644 drivers/thermal/samsung/exynos_common.h
>

2013-04-11 19:34:09

by Eduardo Valentin

[permalink] [raw]
Subject: Re: [1/9] thermal: exynos: Adapt to temperature emulation core thermal framework

Hello Amit,

Couple of comments inline.

On 26-03-2013 07:33, Amit Daniel Kachhap wrote:
> This removes the driver specific sysfs support of the temperature
> emulation and uses the newly added core thermal framework for thermal
> emulation. An exynos platform specific handler is added to support this.
>
> In this patch, the exynos senor(tmu) related code and exynos framework
> related (thermal zone, cooling devices) code are intentionally kept separate.
> So an emulated function pointer is passed from sensor to framework. This is
> beneficial in adding more sensor support using the same framework code
> which is an ongoing work. The goal is to finally split them totally. Even
> the existing read_temperature also follows the same execution method.
>
> Acked-by: Kukjin Kim <[email protected]>
> Signed-off-by: Amit Daniel Kachhap <[email protected]>
>
> ---
> Documentation/thermal/exynos_thermal_emulation | 8 +-
> drivers/thermal/Kconfig | 9 --
> drivers/thermal/exynos_thermal.c | 158 ++++++++++--------------
> 3 files changed, 67 insertions(+), 108 deletions(-)
>
> diff --git a/Documentation/thermal/exynos_thermal_emulation b/Documentation/thermal/exynos_thermal_emulation
> index b73bbfb..36a3e79 100644
> --- a/Documentation/thermal/exynos_thermal_emulation
> +++ b/Documentation/thermal/exynos_thermal_emulation
> @@ -13,11 +13,11 @@ Thermal emulation mode supports software debug for TMU's operation. User can set
> manually with software code and TMU will read current temperature from user value not from
> sensor's value.
>
> -Enabling CONFIG_EXYNOS_THERMAL_EMUL option will make this support in available.
> -When it's enabled, sysfs node will be created under
> -/sys/bus/platform/devices/'exynos device name'/ with name of 'emulation'.
> +Enabling CONFIG_THERMAL_EMULATION option will make this support available.
> +When it's enabled, sysfs node will be created as
> +/sys/devices/virtual/thermal/thermal_zone'zone id'/emul_temp.
>
> -The sysfs node, 'emulation', will contain value 0 for the initial state. When you input any
> +The sysfs node, 'emul_node', will contain value 0 for the initial state. When you input any
> temperature you want to update to sysfs node, it automatically enable emulation mode and
> current temperature will be changed into it.
> (Exynos also supports user changable delay time which would be used to delay of

not part of this patch but:
s/changable/changeable

> diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
> index a764f16..da4c19e 100644
> --- a/drivers/thermal/Kconfig
> +++ b/drivers/thermal/Kconfig
> @@ -117,15 +117,6 @@ config EXYNOS_THERMAL
> If you say yes here you get support for TMU (Thermal Management
> Unit) on SAMSUNG EXYNOS series of SoC.
>
> -config EXYNOS_THERMAL_EMUL
> - bool "EXYNOS TMU emulation mode support"
> - depends on EXYNOS_THERMAL
> - help
> - Exynos 4412 and 4414 and 5 series has emulation mode on TMU.
> - Enable this option will be make sysfs node in exynos thermal platform
> - device directory to support emulation mode. With emulation mode sysfs
> - node, you can manually input temperature to TMU for simulation purpose.
> -
> config DOVE_THERMAL
> tristate "Temperature sensor on Marvell Dove SoCs"
> depends on ARCH_DOVE
> diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c
> index 46568c0..1cd7837 100644
> --- a/drivers/thermal/exynos_thermal.c
> +++ b/drivers/thermal/exynos_thermal.c
> @@ -100,13 +100,13 @@
> #define IDLE_INTERVAL 10000
> #define MCELSIUS 1000
>
> -#ifdef CONFIG_EXYNOS_THERMAL_EMUL
> +#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_EXYNOS_THERMAL_EMUL */
> +#endif /* CONFIG_THERMAL_EMULATION */
>

As the above is only used in one single function, I suggest moving it to
same ifdef where the function belongs below. It reduces your ifdefery
and also makes your code cleaner.


> /* CPU Zone information */
> #define PANIC_ZONE 4
> @@ -145,6 +145,7 @@ struct thermal_cooling_conf {
> struct thermal_sensor_conf {
> char name[SENSOR_NAME_LEN];
> int (*read_temperature)(void *data);
> + int (*write_emul_temp)(void *drv_data, unsigned long temp);

I dont get why you need a private callback to do this, assuming you
have only one sensor_conf for exynos.

> struct thermal_trip_point_conf trip_data;
> struct thermal_cooling_conf cooling_data;
> void *private_data;
> @@ -369,6 +370,23 @@ static int exynos_get_temp(struct thermal_zone_device *thermal,
> 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;

In case you still want to keep your private callback, Id say -ENOTSUPP
is better for the case you dont have the callback.

> +
> + if (!th_zone->sensor_conf) {
> + pr_info("Temperature sensor not initialised\n");
> + return -EINVAL;
> + }
> + data = th_zone->sensor_conf->private_data;
> + if (th_zone->sensor_conf->write_emul_temp)
> + ret = th_zone->sensor_conf->write_emul_temp(data, temp);

nip: a blank line.

> + return ret;
> +}
> +
> /* Get the temperature trend */
> static int exynos_get_trend(struct thermal_zone_device *thermal,
> int trip, enum thermal_trend *trend)
> @@ -392,6 +410,7 @@ 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,
> @@ -714,6 +733,47 @@ static int exynos_tmu_read(struct exynos_tmu_data *data)
> return temp;
> }
>
> +#ifdef CONFIG_THERMAL_EMULATION
>

I think your code looks cleaner if you move the defines at the beginning
of your patch to this point.

+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;
> +

Can you resolve this by not defining this callback for the
SOC_ARCH_EXYNOS4210 thermal zone ops?


> + 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);

nip: blank line.

> + return 0;
> +out:
> + return ret;
> +}
> +#else
> +static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp)
unnecessary space |

> + { return -EINVAL; }

I believe if you do a static inline function return 0, the compiler
should translate it into a nope.

> +#endif/*CONFIG_THERMAL_EMULATION*/
> +
> static void exynos_tmu_work(struct work_struct *work)
> {
> struct exynos_tmu_data *data = container_of(work,
> @@ -747,6 +807,7 @@ static irqreturn_t exynos_tmu_irq(int irq, void *id)
> 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,
> };
>
> #if defined(CONFIG_CPU_EXYNOS4210)
> @@ -853,93 +914,6 @@ static inline struct exynos_tmu_platform_data *exynos_get_driver_data(
> platform_get_device_id(pdev)->driver_data;
> }
>
> -#ifdef CONFIG_EXYNOS_THERMAL_EMUL
> -static ssize_t exynos_tmu_emulation_show(struct device *dev,
> - struct device_attribute *attr,
> - char *buf)
> -{
> - struct platform_device *pdev = container_of(dev,
> - struct platform_device, dev);
> - struct exynos_tmu_data *data = platform_get_drvdata(pdev);
> - unsigned int reg;
> - u8 temp_code;
> - int temp = 0;
> -
> - if (data->soc == SOC_ARCH_EXYNOS4210)
> - goto out;
> -
> - mutex_lock(&data->lock);
> - clk_enable(data->clk);
> - reg = readl(data->base + EXYNOS_EMUL_CON);
> - clk_disable(data->clk);
> - mutex_unlock(&data->lock);
> -
> - if (reg & EXYNOS_EMUL_ENABLE) {
> - reg >>= EXYNOS_EMUL_DATA_SHIFT;
> - temp_code = reg & EXYNOS_EMUL_DATA_MASK;
> - temp = code_to_temp(data, temp_code);
> - }
> -out:
> - return sprintf(buf, "%d\n", temp * MCELSIUS);
> -}
> -
> -static ssize_t exynos_tmu_emulation_store(struct device *dev,
> - struct device_attribute *attr,
> - const char *buf, size_t count)
> -{
> - struct platform_device *pdev = container_of(dev,
> - struct platform_device, dev);
> - struct exynos_tmu_data *data = platform_get_drvdata(pdev);
> - unsigned int reg;
> - int temp;
> -
> - if (data->soc == SOC_ARCH_EXYNOS4210)
> - goto out;
> -
> - if (!sscanf(buf, "%d\n", &temp) || temp < 0)
> - return -EINVAL;
> -
> - mutex_lock(&data->lock);
> - clk_enable(data->clk);
> -
> - reg = readl(data->base + EXYNOS_EMUL_CON);
> -
> - if (temp) {
> - /* Both CELSIUS and MCELSIUS type are available for input */
> - if (temp > MCELSIUS)
> - temp /= MCELSIUS;
> -
> - reg = (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT) |
> - (temp_to_code(data, (temp / MCELSIUS))
> - << 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);
> -
> -out:
> - return count;
> -}
> -
> -static DEVICE_ATTR(emulation, 0644, exynos_tmu_emulation_show,
> - exynos_tmu_emulation_store);
> -static int create_emulation_sysfs(struct device *dev)
> -{
> - return device_create_file(dev, &dev_attr_emulation);
> -}
> -static void remove_emulation_sysfs(struct device *dev)
> -{
> - device_remove_file(dev, &dev_attr_emulation);
> -}
> -#else
> -static inline int create_emulation_sysfs(struct device *dev) { return 0; }
> -static inline void remove_emulation_sysfs(struct device *dev) {}
> -#endif
> -
> static int exynos_tmu_probe(struct platform_device *pdev)
> {
> struct exynos_tmu_data *data;
> @@ -1039,10 +1013,6 @@ static int exynos_tmu_probe(struct platform_device *pdev)
> goto err_clk;
> }
>
> - ret = create_emulation_sysfs(&pdev->dev);
> - if (ret)
> - dev_err(&pdev->dev, "Failed to create emulation mode sysfs node\n");
> -
> return 0;
> err_clk:
> platform_set_drvdata(pdev, NULL);
> @@ -1054,8 +1024,6 @@ static int exynos_tmu_remove(struct platform_device *pdev)
> {
> struct exynos_tmu_data *data = platform_get_drvdata(pdev);
>
> - remove_emulation_sysfs(&pdev->dev);
> -
> exynos_tmu_control(pdev, false);
>
> exynos_unregister_thermal();
>

2013-04-11 20:09:40

by Eduardo Valentin

[permalink] [raw]
Subject: Re: [2/9] thermal: exynos: Add support for instance based register/unregister

Hey Amit,

On 26-03-2013 07:33, Amit Daniel Kachhap wrote:
> This code modifies the thermal driver to have multiple thermal zone
> support by replacing the global thermal zone varibale with device data

s/varibale/variable

> member of thermal_zone_device.
>
> Signed-off-by: Amit Daniel Kachhap <[email protected]>

I understand the idea of your patch but I also see that you do at least
to major changes. One is to set the thermal_device.devdata. The second
is to split your internal reference private_data into driver_data and
pzone_data. Is it possible to split this patch into two, one per
modification? So it is easier to review your changes?

>
> ---
> drivers/thermal/exynos_thermal.c | 65 ++++++++++++++++++++++---------------
> 1 files changed, 39 insertions(+), 26 deletions(-)
>
> diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c
> index 1cd7837..dc9b91b 100644
> --- a/drivers/thermal/exynos_thermal.c
> +++ b/drivers/thermal/exynos_thermal.c
> @@ -148,7 +148,8 @@ struct thermal_sensor_conf {
> int (*write_emul_temp)(void *drv_data, unsigned long temp);
> struct thermal_trip_point_conf trip_data;
> struct thermal_cooling_conf cooling_data;
> - void *private_data;
> + void *driver_data;
> + void *pzone_data;
> };
>
> struct exynos_thermal_zone {
> @@ -161,14 +162,14 @@ struct exynos_thermal_zone {
> bool bind;
> };
>
> -static struct exynos_thermal_zone *th_zone;
> -static void exynos_unregister_thermal(void);
> +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;
> @@ -178,25 +179,26 @@ static int exynos_get_mode(struct thermal_zone_device *thermal,
> static int exynos_set_mode(struct thermal_zone_device *thermal,
> enum thermal_device_mode mode)
> {
> - if (!th_zone->therm_dev) {
> + struct exynos_thermal_zone *th_zone = thermal->devdata;
> + if (!th_zone) {
> pr_notice("thermal zone not registered\n");
> return 0;
> }
>
> - mutex_lock(&th_zone->therm_dev->lock);
> + mutex_lock(&thermal->lock);
>
> if (mode == THERMAL_DEVICE_ENABLED &&
> !th_zone->sensor_conf->trip_data.trigger_falling)
> - th_zone->therm_dev->polling_delay = IDLE_INTERVAL;
> + thermal->polling_delay = IDLE_INTERVAL;
> else
> - th_zone->therm_dev->polling_delay = 0;
> + thermal->polling_delay = 0;
>
> - mutex_unlock(&th_zone->therm_dev->lock);
> + mutex_unlock(&thermal->lock);
>
> th_zone->mode = mode;
> - thermal_zone_device_update(th_zone->therm_dev);
> + thermal_zone_device_update(thermal);
> pr_info("thermal polling set for duration=%d msec\n",
> - th_zone->therm_dev->polling_delay);
> + thermal->polling_delay);
> return 0;
> }
>
> @@ -223,6 +225,8 @@ static int exynos_get_trip_type(struct thermal_zone_device *thermal, int trip,
> 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;
>
> @@ -269,6 +273,7 @@ static int exynos_bind(struct thermal_zone_device *thermal,
> {
> 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;
> @@ -315,6 +320,7 @@ 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)
> @@ -357,13 +363,14 @@ static int exynos_unbind(struct thermal_zone_device *thermal,
> 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->private_data;
> + data = th_zone->sensor_conf->driver_data;
> *temp = th_zone->sensor_conf->read_temperature(data);
> /* convert the temperature into millicelsius */
> *temp = *temp * MCELSIUS;
> @@ -376,12 +383,13 @@ static int exynos_set_emul_temp(struct thermal_zone_device *thermal,
> {
> 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->private_data;
> + 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;
> @@ -391,7 +399,7 @@ static int exynos_set_emul_temp(struct thermal_zone_device *thermal,
> static int exynos_get_trend(struct thermal_zone_device *thermal,
> int trip, enum thermal_trend *trend)
> {
> - int ret;
> + int ret = 0;
> unsigned long trip_temp;
>
> ret = exynos_get_trip_temp(thermal, trip, &trip_temp);
> @@ -403,7 +411,7 @@ static int exynos_get_trend(struct thermal_zone_device *thermal,
> else
> *trend = THERMAL_TREND_DROP_FULL;
>
> - return 0;
> + return ret;
> }
> /* Operation callback functions for thermal zone */
> static struct thermal_zone_device_ops const exynos_dev_ops = {
> @@ -423,11 +431,12 @@ static struct thermal_zone_device_ops const exynos_dev_ops = {
> * This function may be called from interrupt based temperature sensor
> * when threshold is changed.
> */
> -static void exynos_report_trigger(void)
> +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;
> @@ -468,6 +477,7 @@ 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");
> @@ -489,7 +499,7 @@ static int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf)
> th_zone->cool_dev_size++;
>
> th_zone->therm_dev = thermal_zone_device_register(sensor_conf->name,
> - EXYNOS_ZONE_COUNT, 0, NULL, &exynos_dev_ops, NULL, 0,
> + EXYNOS_ZONE_COUNT, 0, th_zone, &exynos_dev_ops, NULL, 0,
> sensor_conf->trip_data.trigger_falling ?
> 0 : IDLE_INTERVAL);
>
> @@ -499,20 +509,22 @@ static int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf)
> 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();
> + exynos_unregister_thermal(sensor_conf);
> return ret;
> }
>
> /* Un-Register with the in-kernel thermal management */
> -static void exynos_unregister_thermal(void)
> +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;
> @@ -774,12 +786,18 @@ 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,
> +};
> +

Assuming you have only one exynos_sensor_conf and this is the parameter
for each call to exynos_register_thermal, if you plan to add support to
different pzone_data, then the above needs to be allocated. Or there is
no point in having this.

> 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_report_trigger(&exynos_sensor_conf);
> mutex_lock(&data->lock);
> clk_enable(data->clk);
> if (data->soc == SOC_ARCH_EXYNOS)
> @@ -804,11 +822,6 @@ static irqreturn_t exynos_tmu_irq(int irq, void *id)
>
> return IRQ_HANDLED;
> }
> -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,
> -};
>
> #if defined(CONFIG_CPU_EXYNOS4210)
> static struct exynos_tmu_platform_data const exynos4210_default_tmu_data = {
> @@ -987,7 +1000,7 @@ static int exynos_tmu_probe(struct platform_device *pdev)
> exynos_tmu_control(pdev, true);
>
> /* Register the sensor with thermal management interface */
> - (&exynos_sensor_conf)->private_data = data;
> + (&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;
> @@ -1026,7 +1039,7 @@ static int exynos_tmu_remove(struct platform_device *pdev)
>
> exynos_tmu_control(pdev, false);
>
> - exynos_unregister_thermal();
> + exynos_unregister_thermal(&exynos_sensor_conf);
>
> clk_put(data->clk);
>
>

2013-04-11 20:31:16

by Eduardo Valentin

[permalink] [raw]
Subject: Re: [4/9] thermal: exynos: Bifurcate exynos thermal common and tmu controller code

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 <[email protected]>
>
> ---
> 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?

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.

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.

> 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 <[email protected]>
> + * Amit Daniel Kachhap <[email protected]>
> + * Amit Daniel Kachhap <[email protected]>
> + *
> + * 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 <linux/clk.h>
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/kernel.h>
> +#include <linux/kobject.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/platform_data/exynos_thermal.h>
> +#include <linux/slab.h>
> +#include <linux/workqueue.h>
> +#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 <[email protected]>");
> +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 <[email protected]>
> + *
> + * 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 <linux/cpufreq.h>
> +#include <linux/cpu_cooling.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/kobject.h>
> +#include <linux/mutex.h>
> +#include <linux/platform_data/exynos_thermal.h>
> +#include <linux/slab.h>
> +#include <linux/thermal.h>
> +#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 <[email protected]>
> + *
> + * 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 <[email protected]>
> - * Amit Daniel Kachhap <[email protected]>
> - *
> - * 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 <linux/module.h>
> -#include <linux/err.h>
> -#include <linux/kernel.h>
> -#include <linux/slab.h>
> -#include <linux/platform_device.h>
> -#include <linux/interrupt.h>
> -#include <linux/clk.h>
> -#include <linux/workqueue.h>
> -#include <linux/sysfs.h>
> -#include <linux/kobject.h>
> -#include <linux/io.h>
> -#include <linux/mutex.h>
> -#include <linux/platform_data/exynos_thermal.h>
> -#include <linux/thermal.h>
> -#include <linux/cpufreq.h>
> -#include <linux/cpu_cooling.h>
> -#include <linux/of.h>
> -
> -#include <plat/cpu.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
> -
> -/* 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]);
> - }
> -
> - kfree(th_zone);
> - pr_info("Exynos: Kernel Thermal management unregistered\n");
> -}
> -
> -/*
> - * 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 <[email protected]>");
> -MODULE_LICENSE("GPL");
> -MODULE_ALIAS("platform:exynos-tmu");
>

2013-04-11 20:41:43

by Eduardo Valentin

[permalink] [raw]
Subject: Re: [3/9] thermal: exynos: Moving into samsung directory for easy maintenance.

On 26-03-2013 07:33, Amit Daniel Kachhap wrote:
> This movement of files is done for easy maintenance and adding more
> new sensor's support for exynos platform easily . This will also help in
> bifurcating exynos common and sensor related parts.
>
> Signed-off-by: Amit Daniel Kachhap <[email protected]>
>
> ---
> drivers/thermal/Kconfig | 13 +-
> drivers/thermal/Makefile | 2 +-
> drivers/thermal/exynos_thermal.c | 1093 ------------------------------
> drivers/thermal/samsung/Kconfig | 11 +
> drivers/thermal/samsung/Makefile | 5 +
> drivers/thermal/samsung/exynos_thermal.c | 1093 ++++++++++++++++++++++++++++++
> 6 files changed, 1115 insertions(+), 1102 deletions(-)
> delete mode 100644 drivers/thermal/exynos_thermal.c
> create mode 100644 drivers/thermal/samsung/Kconfig
> create mode 100644 drivers/thermal/samsung/Makefile
> create mode 100644 drivers/thermal/samsung/exynos_thermal.c
>


nip: use git send-email --find-renames when moving / renaming files. It
is easier to track the changes you had to do while moving.

> diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
> index da4c19e..9b2372a 100644
> --- a/drivers/thermal/Kconfig
> +++ b/drivers/thermal/Kconfig
> @@ -109,14 +109,6 @@ config KIRKWOOD_THERMAL
> Support for the Kirkwood thermal sensor driver into the Linux thermal
> framework. Only kirkwood 88F6282 and 88F6283 have this sensor.
>
> -config EXYNOS_THERMAL
> - tristate "Temperature sensor on Samsung EXYNOS"
> - depends on (ARCH_EXYNOS4 || ARCH_EXYNOS5)
> - depends on CPU_THERMAL
> - help
> - If you say yes here you get support for TMU (Thermal Management
> - Unit) on SAMSUNG EXYNOS series of SoC.
> -
> config DOVE_THERMAL
> tristate "Temperature sensor on Marvell Dove SoCs"
> depends on ARCH_DOVE
> @@ -156,4 +148,9 @@ config INTEL_POWERCLAMP
> enforce idle time which results in more package C-state residency. The
> user interface is exposed via generic thermal framework.
>
> +menu "Exynos thermal drivers"
> +depends on PLAT_SAMSUNG
> +source "drivers/thermal/samsung/Kconfig"
> +endmenu
> +
> endif
> diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
> index d3a2b38..584862c 100644
> --- a/drivers/thermal/Makefile
> +++ b/drivers/thermal/Makefile
> @@ -16,7 +16,7 @@ obj-$(CONFIG_CPU_THERMAL) += cpu_cooling.o
> obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o
> obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o
> obj-$(CONFIG_KIRKWOOD_THERMAL) += kirkwood_thermal.o
> -obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o
> +obj-y += samsung/
> obj-$(CONFIG_DOVE_THERMAL) += dove_thermal.o
> obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o
> obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o
> diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c
> deleted file mode 100644
> index dc9b91b..0000000
> --- a/drivers/thermal/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 <[email protected]>
> - * Amit Daniel Kachhap <[email protected]>
> - *
> - * 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 <linux/module.h>
> -#include <linux/err.h>
> -#include <linux/kernel.h>
> -#include <linux/slab.h>
> -#include <linux/platform_device.h>
> -#include <linux/interrupt.h>
> -#include <linux/clk.h>
> -#include <linux/workqueue.h>
> -#include <linux/sysfs.h>
> -#include <linux/kobject.h>
> -#include <linux/io.h>
> -#include <linux/mutex.h>
> -#include <linux/platform_data/exynos_thermal.h>
> -#include <linux/thermal.h>
> -#include <linux/cpufreq.h>
> -#include <linux/cpu_cooling.h>
> -#include <linux/of.h>
> -
> -#include <plat/cpu.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
> -
> -/* 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]);
> - }
> -
> - kfree(th_zone);
> - pr_info("Exynos: Kernel Thermal management unregistered\n");
> -}
> -
> -/*
> - * 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 <[email protected]>");
> -MODULE_LICENSE("GPL");
> -MODULE_ALIAS("platform:exynos-tmu");
> diff --git a/drivers/thermal/samsung/Kconfig b/drivers/thermal/samsung/Kconfig
> new file mode 100644
> index 0000000..5737b85
> --- /dev/null
> +++ b/drivers/thermal/samsung/Kconfig
> @@ -0,0 +1,11 @@
> +
> +config EXYNOS_THERMAL
> + tristate "Temperature sensor on Samsung EXYNOS"
> + depends on (ARCH_EXYNOS4 || ARCH_EXYNOS5)
> + 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.
> +

No need to have this ending blank line

> diff --git a/drivers/thermal/samsung/Makefile b/drivers/thermal/samsung/Makefile
> new file mode 100644
> index 0000000..fa55df5
> --- /dev/null
> +++ b/drivers/thermal/samsung/Makefile
> @@ -0,0 +1,5 @@
> +#
> +# Samsung thermal specific Makefile
> +#
> +obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o
> +

No need to have an ending blank line

> diff --git a/drivers/thermal/samsung/exynos_thermal.c b/drivers/thermal/samsung/exynos_thermal.c
> new file mode 100644
> index 0000000..dc9b91b
> --- /dev/null
> +++ b/drivers/thermal/samsung/exynos_thermal.c
> @@ -0,0 +1,1093 @@
> +/*
> + * exynos_thermal.c - Samsung EXYNOS TMU (Thermal Management Unit)
> + *
> + * Copyright (C) 2011 Samsung Electronics
> + * Donggeun Kim <[email protected]>
> + * Amit Daniel Kachhap <[email protected]>
> + *
> + * 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 <linux/module.h>
> +#include <linux/err.h>
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/platform_device.h>
> +#include <linux/interrupt.h>
> +#include <linux/clk.h>
> +#include <linux/workqueue.h>
> +#include <linux/sysfs.h>
> +#include <linux/kobject.h>
> +#include <linux/io.h>
> +#include <linux/mutex.h>
> +#include <linux/platform_data/exynos_thermal.h>
> +#include <linux/thermal.h>
> +#include <linux/cpufreq.h>
> +#include <linux/cpu_cooling.h>
> +#include <linux/of.h>
> +
> +#include <plat/cpu.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
> +
> +/* 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]);
> + }
> +
> + kfree(th_zone);
> + pr_info("Exynos: Kernel Thermal management unregistered\n");
> +}
> +
> +/*
> + * 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 <[email protected]>");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:exynos-tmu");
>

2013-04-11 20:43:27

by Eduardo Valentin

[permalink] [raw]
Subject: Re: [4/9] thermal: exynos: Bifurcate exynos thermal common and tmu controller code

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 <[email protected]>
>
> ---
> 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
>
> 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

Are you sure you want separated modules?

If yes you have to review this patch and export the symbols from common
to exynos4210. Saying that because it generates a compilation error:
ERROR: "exynos_report_trigger"
[drivers/thermal/samsung/exynos4210_thermal.ko] undefined!
ERROR: "exynos_register_thermal"
[drivers/thermal/samsung/exynos4210_thermal.ko] undefined!
ERROR: "exynos_unregister_thermal"
[drivers/thermal/samsung/exynos4210_thermal.ko] undefined!

If you want separate modules, you have to EXPORT_SYMBOL those.

You can also do what I have done for TI SoC thermal (check linux-next
drivers/staging/ti-soc-thermal/Makefile)
> 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 <[email protected]>
> + * Amit Daniel Kachhap <[email protected]>
> + * Amit Daniel Kachhap <[email protected]>
> + *
> + * 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 <linux/clk.h>
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/kernel.h>
> +#include <linux/kobject.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/platform_data/exynos_thermal.h>
> +#include <linux/slab.h>
> +#include <linux/workqueue.h>
> +#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 <[email protected]>");
> +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 <[email protected]>
> + *
> + * 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 <linux/cpufreq.h>
> +#include <linux/cpu_cooling.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/kobject.h>
> +#include <linux/mutex.h>
> +#include <linux/platform_data/exynos_thermal.h>
> +#include <linux/slab.h>
> +#include <linux/thermal.h>
> +#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 <[email protected]>
> + *
> + * 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 <[email protected]>
> - * Amit Daniel Kachhap <[email protected]>
> - *
> - * 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 <linux/module.h>
> -#include <linux/err.h>
> -#include <linux/kernel.h>
> -#include <linux/slab.h>
> -#include <linux/platform_device.h>
> -#include <linux/interrupt.h>
> -#include <linux/clk.h>
> -#include <linux/workqueue.h>
> -#include <linux/sysfs.h>
> -#include <linux/kobject.h>
> -#include <linux/io.h>
> -#include <linux/mutex.h>
> -#include <linux/platform_data/exynos_thermal.h>
> -#include <linux/thermal.h>
> -#include <linux/cpufreq.h>
> -#include <linux/cpu_cooling.h>
> -#include <linux/of.h>
> -
> -#include <plat/cpu.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
> -
> -/* 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]);
> - }
> -
> - kfree(th_zone);
> - pr_info("Exynos: Kernel Thermal management unregistered\n");
> -}
> -
> -/*
> - * 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 <[email protected]>");
> -MODULE_LICENSE("GPL");
> -MODULE_ALIAS("platform:exynos-tmu");
>

2013-04-11 20:48:27

by Eduardo Valentin

[permalink] [raw]
Subject: Re: [5/9] thermal: exynos: Make the zone handling dependent on trip count

On 26-03-2013 07:33, Amit Daniel Kachhap wrote:
> This code changes the zone handling to use the trip count passed
> by the TMU driver. This also helps in adding more zone support.
>
> Signed-off-by: Amit Daniel Kachhap <[email protected]>
>
> ---
> drivers/thermal/samsung/exynos_common.c | 56 +++++++++++++------------
> drivers/thermal/samsung/exynos_common.h | 13 +++---
> include/linux/platform_data/exynos_thermal.h | 7 ++-
> 3 files changed, 40 insertions(+), 36 deletions(-)
>
> diff --git a/drivers/thermal/samsung/exynos_common.c b/drivers/thermal/samsung/exynos_common.c
> index 649d67c..0c0098d 100644
> --- a/drivers/thermal/samsung/exynos_common.c
> +++ b/drivers/thermal/samsung/exynos_common.c
> @@ -84,17 +84,16 @@ static int exynos_set_mode(struct thermal_zone_device *thermal,
> 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:
> + struct exynos_thermal_zone *th_zone = thermal->devdata;
> + int max_trip = th_zone->sensor_conf->trip_data.trip_count;
> +
> + if (trip < 0 || trip >= max_trip)
> return -EINVAL;
> - }
> + else if (trip == (max_trip - 1))
> + *type = THERMAL_TRIP_CRITICAL;
> + else
> + *type = THERMAL_TRIP_ACTIVE;
> +
> return 0;
> }
>
> @@ -103,8 +102,9 @@ static int exynos_get_trip_temp(struct thermal_zone_device *thermal, int trip,
> unsigned long *temp)
> {
> struct exynos_thermal_zone *th_zone = thermal->devdata;
> + int max_trip = th_zone->sensor_conf->trip_data.trip_count;
>
> - if (trip < GET_TRIP(MONITOR_ZONE) || trip > GET_TRIP(PANIC_ZONE))
> + if (trip < 0 || trip >= max_trip)
> return -EINVAL;
>
> *temp = th_zone->sensor_conf->trip_data.trip_val[trip];
> @@ -118,10 +118,10 @@ static int exynos_get_trip_temp(struct thermal_zone_device *thermal, int trip,
> 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;
> + struct exynos_thermal_zone *th_zone = thermal->devdata;
> + int max_trip = th_zone->sensor_conf->trip_data.trip_count;
> + /* Get the temp of highest trip*/
> + return exynos_get_trip_temp(thermal, max_trip - 1, temp);
> }
>
> int exynos_get_frequency_level(unsigned int cpu, unsigned int freq)
> @@ -157,7 +157,7 @@ static int exynos_bind(struct thermal_zone_device *thermal,
> tab_size = data->cooling_data.freq_clip_count;
>
> if (tab_ptr == NULL || tab_size == 0)
> - return -EINVAL;
> + return 0;
>
> /* find the cooling device registered*/
> for (i = 0; i < th_zone->cool_dev_size; i++)
> @@ -206,7 +206,7 @@ static int exynos_unbind(struct thermal_zone_device *thermal,
> tab_size = data->cooling_data.freq_clip_count;
>
> if (tab_size == 0)
> - return -EINVAL;
> + return 0;
>
> /* find the cooling device registered*/
> for (i = 0; i < th_zone->cool_dev_size; i++)

The above two chunks are confusing. I dont understand how they are now
supposed to be valid settings.

> @@ -366,19 +366,21 @@ int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf)
> 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;
> + if (sensor_conf->cooling_data.freq_clip_count > 0) {
> + 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->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);
> + sensor_conf->trip_data.trip_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");
> diff --git a/drivers/thermal/samsung/exynos_common.h b/drivers/thermal/samsung/exynos_common.h
> index b8d289e..453e09a 100644
> --- a/drivers/thermal/samsung/exynos_common.h
> +++ b/drivers/thermal/samsung/exynos_common.h
> @@ -27,23 +27,22 @@
> #define SENSOR_NAME_LEN 16
> #define MAX_TRIP_COUNT 8
> #define MAX_COOLING_DEVICE 4
> -#define MAX_THRESHOLD_LEVS 4
> +#define MAX_THRESHOLD_LEVS 5
>
> #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 PANIC_ZONE 5
> +#define ALARM_ZONE 4
> +#define WARN_ZONE 3
> +#define MONITOR_ZONE 2
> +#define SAFE_ZONE 1
>


Updating this does not seam to be part of the intent of this patch. You
probably want to send a separate patch for this.

> #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;
> diff --git a/include/linux/platform_data/exynos_thermal.h b/include/linux/platform_data/exynos_thermal.h
> index da7e627..893b758 100644
> --- a/include/linux/platform_data/exynos_thermal.h
> +++ b/include/linux/platform_data/exynos_thermal.h
> @@ -23,6 +23,8 @@
> #define _LINUX_EXYNOS_THERMAL_H
> #include <linux/cpu_cooling.h>
>
> +#define MAX_TRIP 5
> +


Should MAX_TRIP/THRESHOLD_LEVEL/PANIC_ZONE be somewhat same

> enum calibration_type {
> TYPE_ONE_POINT_TRIMMING,
> TYPE_TWO_POINT_TRIMMING,
> @@ -100,11 +102,12 @@ struct freq_clip_table {
> struct exynos_tmu_platform_data {
> u8 threshold;
> u8 threshold_falling;
> - u8 trigger_levels[4];
> + u8 trigger_levels[MAX_TRIP];
> bool trigger_level0_en;
> bool trigger_level1_en;
> bool trigger_level2_en;
> bool trigger_level3_en;
> + bool trigger_level4_en;
>
> u8 gain;
> u8 reference_voltage;
> @@ -113,7 +116,7 @@ struct exynos_tmu_platform_data {
>
> enum calibration_type cal_type;
> enum soc_type type;
> - struct freq_clip_table freq_tab[4];
> + struct freq_clip_table freq_tab[MAX_TRIP];
> unsigned int freq_tab_count;
> };
> #endif /* _LINUX_EXYNOS_THERMAL_H */
>

2013-04-11 20:54:54

by Eduardo Valentin

[permalink] [raw]
Subject: Re: [6/9] thermal: exynos: small cleanups to prepare for adding exynos5440 driver

Amit,

On 26-03-2013 07:33, Amit Daniel Kachhap wrote:
> Add calib mode, trigger types and trigger_enable array. This is needed
> for adding exynos5440 TMU driver.
>

I dont think the above are small cleanups. I d rather split this patch
into three, one per change and describe them properly.

> Signed-off-by: Amit Daniel Kachhap <[email protected]>
>
> ---
> drivers/thermal/samsung/exynos4210_thermal.c | 30 ++++++++++++------------
> include/linux/platform_data/exynos_thermal.h | 32 +++++++++++++-------------
> 2 files changed, 31 insertions(+), 31 deletions(-)
>
> diff --git a/drivers/thermal/samsung/exynos4210_thermal.c b/drivers/thermal/samsung/exynos4210_thermal.c
> index 09ea8c8..58d16ac 100644
> --- a/drivers/thermal/samsung/exynos4210_thermal.c
> +++ b/drivers/thermal/samsung/exynos4210_thermal.c
> @@ -276,10 +276,10 @@ static void exynos_tmu_control(struct platform_device *pdev, bool on)
>
> 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;
> + interrupt_en = pdata->trigger_enable[3] << 12 |
> + pdata->trigger_enable[2] << 8 |
> + pdata->trigger_enable[1] << 4 |
> + pdata->trigger_enable[0];
> if (pdata->threshold_falling)
> interrupt_en |= interrupt_en << 16;
> } else {
> @@ -394,10 +394,10 @@ static struct exynos_tmu_platform_data const exynos4210_default_tmu_data = {
> .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,
> + .trigger_enable[0] = 1,
> + .trigger_enable[1] = 1,
> + .trigger_enable[2] = 1,
> + .trigger_enable[3] = 0,
> .gain = 15,
> .reference_voltage = 7,
> .cal_type = TYPE_ONE_POINT_TRIMMING,
> @@ -423,10 +423,10 @@ static struct exynos_tmu_platform_data const exynos_default_tmu_data = {
> .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,
> + .trigger_enable[0] = 1,
> + .trigger_enable[1] = 1,
> + .trigger_enable[2] = 1,
> + .trigger_enable[3] = 0,
> .gain = 8,
> .reference_voltage = 16,
> .noise_cancel_mode = 4,
> @@ -566,9 +566,9 @@ static int exynos_tmu_probe(struct platform_device *pdev)
>
> /* 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;
> + exynos_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 < exynos_sensor_conf.trip_data.trip_count; i++)
> exynos_sensor_conf.trip_data.trip_val[i] =
> diff --git a/include/linux/platform_data/exynos_thermal.h b/include/linux/platform_data/exynos_thermal.h
> index 893b758..1090f48 100644
> --- a/include/linux/platform_data/exynos_thermal.h
> +++ b/include/linux/platform_data/exynos_thermal.h
> @@ -31,6 +31,17 @@ enum calibration_type {
> TYPE_NONE,
> };
>
> +enum calibration_mode {
> + SW_MODE,
> + HW_MODE,
> +};
> +
> +enum trigger_type {
> + ACTIVE,
> + CRITICAL,
> + HW_TRIP,
> +};
> +
> enum soc_type {
> SOC_ARCH_EXYNOS4210 = 1,
> SOC_ARCH_EXYNOS,
> @@ -71,18 +82,9 @@ struct freq_clip_table {
> * 3: temperature for trigger_level3 interrupt
> * condition for trigger_level3 interrupt:
> * current temperature > threshold + trigger_levels[3]
> - * @trigger_level0_en:
> + * @trigger_enable[]: array to denote which trigger levels are enabled.
> * 1 = enable trigger_level0 interrupt,
> * 0 = disable trigger_level0 interrupt
> - * @trigger_level1_en:
> - * 1 = enable trigger_level1 interrupt,
> - * 0 = disable trigger_level1 interrupt
> - * @trigger_level2_en:
> - * 1 = enable trigger_level2 interrupt,
> - * 0 = disable trigger_level2 interrupt
> - * @trigger_level3_en:
> - * 1 = enable trigger_level3 interrupt,
> - * 0 = disable trigger_level3 interrupt
> * @gain: gain of amplifier in the positive-TC generator block
> * 0 <= gain <= 15
> * @reference_voltage: reference voltage of amplifier
> @@ -93,6 +95,7 @@ struct freq_clip_table {
> * @type: determines the type of SOC
> * @efuse_value: platform defined fuse value
> * @cal_type: calibration type for temperature
> + * @cal_mode: calibration mode for temperature

How about trigger_type?
> * @freq_clip_table: Table representing frequency reduction percentage.
> * @freq_tab_count: Count of the above table as frequency reduction may
> * applicable to only some of the trigger levels.
> @@ -103,18 +106,15 @@ struct exynos_tmu_platform_data {
> u8 threshold;
> u8 threshold_falling;
> u8 trigger_levels[MAX_TRIP];
> - bool trigger_level0_en;
> - bool trigger_level1_en;
> - bool trigger_level2_en;
> - bool trigger_level3_en;
> - bool trigger_level4_en;
> -
> + enum trigger_type trigger_type[MAX_TRIP];
> + bool trigger_enable[MAX_TRIP];
> u8 gain;
> u8 reference_voltage;
> u8 noise_cancel_mode;
> u32 efuse_value;
>
> enum calibration_type cal_type;
> + enum calibration_mode cal_mode;
> enum soc_type type;
> struct freq_clip_table freq_tab[MAX_TRIP];
> unsigned int freq_tab_count;
>

2013-04-11 21:04:38

by Eduardo Valentin

[permalink] [raw]
Subject: Re: [7/9] thermal: exynos: Add support for exynos5440 TMU sensor.


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 <[email protected]>
>
> ---
> 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!


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)

> 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 <[email protected]>
> + *
> + * 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 <linux/clk.h>
> +#include <linux/cpufreq.h>
> +#include <linux/cpu_cooling.h>
> +#include <linux/err.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/kobject.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/thermal.h>
> +#include <linux/workqueue.h>
> +#include <linux/platform_data/exynos_thermal.h>
> +
> +#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<[email protected]>");
> +MODULE_LICENSE("GPL");
GPL v2?

> +MODULE_ALIAS("platform:exynos5440-tmu");
>

2013-04-11 21:13:47

by Eduardo Valentin

[permalink] [raw]
Subject: Re: [8/9] thermal: exynos: Parse the platform data from the device tree.

Amit,

Copying Grant for the DT part discussion.

On 26-03-2013 07:34, Amit Daniel Kachhap wrote:
> This patch adds code to parse the DT based platform data like threshold temp,
> sensor configuration parameters like gain, reference voltages, calibration
> modes etc.
>
> Signed-off-by: Amit Daniel Kachhap <[email protected]>
>
> ---
> .../bindings/thermal/exynos5440-thermal.txt | 93 ++++++++++++++++++++
> drivers/thermal/samsung/exynos5440_thermal.c | 5 +-
> drivers/thermal/samsung/exynos_common.c | 92 +++++++++++++++++++
> drivers/thermal/samsung/exynos_common.h | 2 +
> 4 files changed, 189 insertions(+), 3 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/thermal/exynos5440-thermal.txt
>
> diff --git a/Documentation/devicetree/bindings/thermal/exynos5440-thermal.txt b/Documentation/devicetree/bindings/thermal/exynos5440-thermal.txt
> new file mode 100644
> index 0000000..1ad2dee
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/thermal/exynos5440-thermal.txt
> @@ -0,0 +1,93 @@
> +
> +Exynos5440 TMU driver
> +-------------------
> +
> +Exynos5440 SoC Thermal Sensor driver for CPU temperature measurement and
> +performing cooling actions.
> +
> +Required properties:
> +- interrupts: Interrupt to know the threshold change interrupts.
> +- clocks: phandle of the clock from common clock binding. More description can
> + be found in Documentation/devicetree/bindings/clock/clock-bindings.txt.
> +- clock-name: clock name as used in the clock fetch.
> +- tmu-ctrl-data: phandle of the TMU controller configuration data. Its member
> + description is shown below,
> +- gain: A factor describing the gain of amplifier. The value varies between
> + 0-15.
> +- reference-voltage: A factor describing the reference volt to amplifier. The
> + value varies between 0-31.
> +- noise-cancel-mode: This value selects thermal tripping mode. The possible
> + values can be,
> + 0: Use current temp.
> + 4: Use current temp and past 4 temp values.
> + 5: Use current temp and past 8 temp values.
> + 6: Use current temp and past 12 temp values.
> + 7: Use current temp and past 16 temp values.
> +-cal-type: This value selects temperature calibration mode. The possible values
> + can be,
> + 0: No calibration.
> + 1: 1 point trimming method at 25 C.
> + 2: 1 point trimming method at 85 C.
> + 3: 2 point trimming method.
> +-cal-mode: This value selects hw/sw mode calibration. The possible values can be,
> + 0: Software calibration.
> + 1: Hardware calibration.
> +
> +Optional properties:
> +-efuse-value: This value should be used when the controller does not contain
> + valid fused temperature data needed for calibration. This is a 16 bit
> + value.
> +-threshold-falling: This value defines the falling threshold when it is added to
> + trip threshold. If this value is not supplied then rising and falling
> + threshold are same.
> +
> +-trip@: This node is added to define the trip properties. The trip members are
> + shown below,
> +-trip: This field defines the trip ID. Exynos5440 has 5 trip ID.
> +-trigger-temp: Temperature at which threshold trigger is fired. Its unit is
> + celsius.
> +-type: This denotes the type of trigger. The possible values are,
> + 0: active trip type
> + 1: critical
> + 2: hw system trip
> +-frequency-max: cpu frequency when this trip is reached.
> +
> +Example:
> +--------
> + tmu_ctrl_info: tmu-ctrl-info {
Shouldnt this be named something like samsung,tmu_ctrl_info?

> + gain = <8>;
> + reference-voltage = <16>;
> + noise-cancel-mode = <4>;
> + cal-type = <0>;
> + cal-mode = <0>;
> + efuse-value = <0xabcd>;
> + threshold-falling = <5>;
> +

ditto for the prefix of the above,

> + trip@0{
> + trip = <0>;
> + trigger-temp = <80>;
> + type = <0>;
> + frequency-max = <1200000>;
> + };
> +
> + trip@3{
> + trip = <3>;
> + trigger-temp = <110>;
> + type = <1>;
> + };
> +
> + trip@4{
> + trip = <4>;
> + trigger-temp = <120>;
> + type = <2>;
> + };
> + };
> +
> + tmuctrl_0: tmuctrl@160118 {
> + compatible = "samsung,exynos5440-tmu";
> + reg = <0x160118 0x300>;
> + interrupts = <0 58 0>;
> + clocks = <&clock 8>;
> + clock-names = "tmu_apbif";
> + tmu-ctrl-data = <&tmu_ctrl_info>;

ditto.

Grant,

In fact I believe we must talk about how to standardize the thermal
description under DT.

> + };
> diff --git a/drivers/thermal/samsung/exynos5440_thermal.c b/drivers/thermal/samsung/exynos5440_thermal.c
> index a3c75d3..c140e8c 100644
> --- a/drivers/thermal/samsung/exynos5440_thermal.c
> +++ b/drivers/thermal/samsung/exynos5440_thermal.c
> @@ -564,9 +564,8 @@ static int exynos_tmu_probe(struct platform_device *pdev)
> return -ENOMEM;
> }
>
> - pdata = (struct exynos_tmu_platform_data *)
> - platform_get_device_id(pdev)->driver_data;
> - if (!pdata) {
> + pdata = exynos_parse_tmu_dt_data(pdev);
> + if (!pdata || IS_ERR(pdata)) {
> dev_err(&pdev->dev, "No platform init data supplied.\n");
> return -ENODEV;
> }
> diff --git a/drivers/thermal/samsung/exynos_common.c b/drivers/thermal/samsung/exynos_common.c
> index 0c0098d..345284c 100644
> --- a/drivers/thermal/samsung/exynos_common.c
> +++ b/drivers/thermal/samsung/exynos_common.c
> @@ -27,7 +27,9 @@
> #include <linux/kernel.h>
> #include <linux/kobject.h>
> #include <linux/mutex.h>
> +#include <linux/of.h>
> #include <linux/platform_data/exynos_thermal.h>
> +#include <linux/platform_device.h>
> #include <linux/slab.h>
> #include <linux/thermal.h>
> #include "exynos_common.h"
> @@ -421,3 +423,93 @@ void exynos_unregister_thermal(struct thermal_sensor_conf *sensor_conf)
> kfree(th_zone);
> pr_info("Exynos: Kernel Thermal[%d] management unregistered\n", id);
> }
> +
> +struct exynos_tmu_platform_data *
> + exynos_parse_tmu_dt_data(struct platform_device *pdev)
> +{
> + int ret;
> + struct device_node *np, *cn;
> + struct exynos_tmu_platform_data *pdata;
> + unsigned int val, trip;
> +
> + if (!pdev->dev.of_node)
> + return ERR_PTR(-ENODEV);
> +
> + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
> + if (!pdata)
> + return ERR_PTR(-ENOMEM);
> +
> + np = of_parse_phandle(pdev->dev.of_node, "tmu-ctrl-data", 0);
> + if (!np) {
> + ret = -ENODEV;
> + goto err_parse_dt;
> + }
> +
> + /* Parse compulsory parameters */
> + ret = of_property_read_u32(np, "gain", &val);
> + if (ret)
> + goto err_parse_dt;
> + pdata->gain = val;
> +
> + ret = of_property_read_u32(np, "reference-voltage", &val);
> + if (ret)
> + goto err_parse_dt;
> + pdata->reference_voltage = val;
> +
> + ret = of_property_read_u32(np, "noise-cancel-mode", &val);
> + if (ret)
> + goto err_parse_dt;
> + pdata->noise_cancel_mode = val;
> +
> + ret = of_property_read_u32(np, "cal-type", &val);
> + if (ret)
> + goto err_parse_dt;
> + pdata->cal_type = val;
> +
> + ret = of_property_read_u32(np, "cal-mode", &val);
> + if (ret)
> + goto err_parse_dt;
> + pdata->cal_mode = val;
> +
> + /* Parse optional parameters */
> + ret = of_property_read_u32(np, "efuse-value", &val);
> + if (!ret)
> + pdata->efuse_value = val;
> +
> + ret = of_property_read_u32(np, "threshold-falling", &val);
> + if (!ret)
> + pdata->threshold_falling = val;
> +
> + for (cn = NULL ; (cn = of_get_next_child(np, cn));) {
> + /* Parse compulsory child parameters */
> + ret = of_property_read_u32(cn, "trip", &trip);
> + if (ret || trip > MAX_TRIP)
> + goto err_parse_dt;
> +
> + ret = of_property_read_u32(cn, "trigger-temp", &val);
> + if (ret || !val)
> + goto err_parse_dt;
> + pdata->trigger_levels[trip] = val;
> +
> + ret = of_property_read_u32(cn, "type", &val);
> + if (ret || val > HW_TRIP)
> + goto err_parse_dt;
> + pdata->trigger_type[trip] = val;
> +
> + /* Parse optional child parameters */
> + ret = of_property_read_u32(cn, "frequency-max", &val);
> + if (!ret && val) {
> + pdata->freq_tab[pdata->freq_tab_count].freq_clip_max
> + = val;
> + pdata->freq_tab[pdata->freq_tab_count].temp_level
> + = pdata->trigger_levels[trip];
> + pdata->freq_tab_count++;
> + }
> + pdata->trigger_enable[trip] = true;
> + }
> + return pdata;
> +
> +err_parse_dt:
> + dev_err(&pdev->dev, "Parsing TMU device tree data error.\n");
> + return ERR_PTR(ret);
> +}
> diff --git a/drivers/thermal/samsung/exynos_common.h b/drivers/thermal/samsung/exynos_common.h
> index 453e09a..5ca640b 100644
> --- a/drivers/thermal/samsung/exynos_common.h
> +++ b/drivers/thermal/samsung/exynos_common.h
> @@ -69,4 +69,6 @@ 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);
> +struct exynos_tmu_platform_data *
> + exynos_parse_tmu_dt_data(struct platform_device *pdev);

This patch again generates a linking error while compiling as module
ERROR: "exynos_parse_tmu_dt_data"
[drivers/thermal/samsung/exynos5440_thermal.ko] undefined!


> #endif /* _LINUX_EXYNOS_COMMON_H */
>

2013-04-11 21:16:10

by Eduardo Valentin

[permalink] [raw]
Subject: Re: [9/9] ARM: dts: Add device tree node for exynos5440 TMU controller

Amit,

Copying Grant for DT.

On 26-03-2013 07:34, Amit Daniel Kachhap wrote:
> This patch adds device node for TMU controller. There are 3
> instances of the controllers so 3 nodes are created.
>
> Signed-off-by: Amit Daniel Kachhap <[email protected]>
>
> ---
> arch/arm/boot/dts/exynos5440.dtsi | 43 +++++++++++++++++++++++++++++++++++++
> 1 files changed, 43 insertions(+), 0 deletions(-)
>
> diff --git a/arch/arm/boot/dts/exynos5440.dtsi b/arch/arm/boot/dts/exynos5440.dtsi
> index 5f3562a..b74ce9f 100644
> --- a/arch/arm/boot/dts/exynos5440.dtsi
> +++ b/arch/arm/boot/dts/exynos5440.dtsi
> @@ -16,6 +16,12 @@
>
> interrupt-parent = <&gic>;
>
> + aliases {
> + tmuctrl0 = &tmuctrl_0;
> + tmuctrl1 = &tmuctrl_1;
> + tmuctrl2 = &tmuctrl_2;
> + };
> +
> gic:interrupt-controller@2E0000 {
> compatible = "arm,cortex-a15-gic";
> #interrupt-cells = <3>;
> @@ -156,4 +162,41 @@
> reg = <0x130000 0x1000>;
> interrupts = <0 17 0>, <0 16 0>;
> };
> +
> + tmu_ctrl_info: tmu-ctrl-info {
> + gain = <5>;
> + reference-voltage = <16>;
> + noise-cancel-mode = <4>;
> + cal-type = <2>;
> + cal-mode = <0>;
> + efuse-value = <0x5b2c>;
> + threshold-falling = <5>;
> + };
> +
> + tmuctrl_0: tmuctrl@160118 {
> + compatible = "samsung,exynos5440-tmu";
> + reg = <0x160118 0x300>;
> + interrupts = <0 58 0>;
> + clocks = <&clock 8>;
> + clock-names = "tmu_apbif";
> + tmu-ctrl-data = <&tmu_ctrl_info>;
> + };
> +
> + tmuctrl_1: tmuctrl@16011C {
> + compatible = "samsung,exynos5440-tmu";
> + reg = <0x16011C 0x300>;
> + interrupts = <0 58 0>;
> + clocks = <&clock 8>;
> + clock-names = "tmu_apbif";
> + tmu-ctrl-data = <&tmu_ctrl_info>;
> + };
> +
> + tmuctrl_2: tmuctrl@160120 {
> + compatible = "samsung,exynos5440-tmu";
> + reg = <0x160120 0x300>;
> + interrupts = <0 58 0>;
> + clocks = <&clock 8>;
> + clock-names = "tmu_apbif";
> + tmu-ctrl-data = <&tmu_ctrl_info>;
> + };

Grant, how the above thermal data must be handled in DT? Is it fine to
create private phandles like that?

> };
>
Please consider the comments on patch 8.

2013-04-12 10:57:11

by Amit Kachhap

[permalink] [raw]
Subject: Re: [1/9] thermal: exynos: Adapt to temperature emulation core thermal framework

Hi Eduardo,

Thanks for your review comments.

On Fri, Apr 12, 2013 at 1:03 AM, Eduardo Valentin
<[email protected]> wrote:
> Hello Amit,
>
> Couple of comments inline.
>
>
> On 26-03-2013 07:33, Amit Daniel Kachhap wrote:
>>
>> This removes the driver specific sysfs support of the temperature
>> emulation and uses the newly added core thermal framework for thermal
>> emulation. An exynos platform specific handler is added to support this.
>>
>> In this patch, the exynos senor(tmu) related code and exynos framework
>> related (thermal zone, cooling devices) code are intentionally kept
>> separate.
>> So an emulated function pointer is passed from sensor to framework. This
>> is
>> beneficial in adding more sensor support using the same framework code
>> which is an ongoing work. The goal is to finally split them totally. Even
>> the existing read_temperature also follows the same execution method.
>>
>> Acked-by: Kukjin Kim <[email protected]>
>> Signed-off-by: Amit Daniel Kachhap <[email protected]>
>>
>> ---
>> Documentation/thermal/exynos_thermal_emulation | 8 +-
>> drivers/thermal/Kconfig | 9 --
>> drivers/thermal/exynos_thermal.c | 158
>> ++++++++++--------------
>> 3 files changed, 67 insertions(+), 108 deletions(-)
>>
>> diff --git a/Documentation/thermal/exynos_thermal_emulation
>> b/Documentation/thermal/exynos_thermal_emulation
>> index b73bbfb..36a3e79 100644
>> --- a/Documentation/thermal/exynos_thermal_emulation
>> +++ b/Documentation/thermal/exynos_thermal_emulation
>> @@ -13,11 +13,11 @@ Thermal emulation mode supports software debug for
>> TMU's operation. User can set
>> manually with software code and TMU will read current temperature from
>> user value not from
>> sensor's value.
>>
>> -Enabling CONFIG_EXYNOS_THERMAL_EMUL option will make this support in
>> available.
>> -When it's enabled, sysfs node will be created under
>> -/sys/bus/platform/devices/'exynos device name'/ with name of 'emulation'.
>> +Enabling CONFIG_THERMAL_EMULATION option will make this support
>> available.
>> +When it's enabled, sysfs node will be created as
>> +/sys/devices/virtual/thermal/thermal_zone'zone id'/emul_temp.
>>
>> -The sysfs node, 'emulation', will contain value 0 for the initial state.
>> When you input any
>> +The sysfs node, 'emul_node', will contain value 0 for the initial state.
>> When you input any
>> temperature you want to update to sysfs node, it automatically enable
>> emulation mode and
>> current temperature will be changed into it.
>> (Exynos also supports user changable delay time which would be used to
>> delay of
>
>
> not part of this patch but:
> s/changable/changeable
Ok
>
>
>> diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
>> index a764f16..da4c19e 100644
>> --- a/drivers/thermal/Kconfig
>> +++ b/drivers/thermal/Kconfig
>> @@ -117,15 +117,6 @@ config EXYNOS_THERMAL
>> If you say yes here you get support for TMU (Thermal Management
>> Unit) on SAMSUNG EXYNOS series of SoC.
>>
>> -config EXYNOS_THERMAL_EMUL
>> - bool "EXYNOS TMU emulation mode support"
>> - depends on EXYNOS_THERMAL
>> - help
>> - Exynos 4412 and 4414 and 5 series has emulation mode on TMU.
>> - Enable this option will be make sysfs node in exynos thermal
>> platform
>> - device directory to support emulation mode. With emulation mode
>> sysfs
>> - node, you can manually input temperature to TMU for simulation
>> purpose.
>> -
>> config DOVE_THERMAL
>> tristate "Temperature sensor on Marvell Dove SoCs"
>> depends on ARCH_DOVE
>> diff --git a/drivers/thermal/exynos_thermal.c
>> b/drivers/thermal/exynos_thermal.c
>> index 46568c0..1cd7837 100644
>> --- a/drivers/thermal/exynos_thermal.c
>> +++ b/drivers/thermal/exynos_thermal.c
>> @@ -100,13 +100,13 @@
>> #define IDLE_INTERVAL 10000
>> #define MCELSIUS 1000
>>
>> -#ifdef CONFIG_EXYNOS_THERMAL_EMUL
>> +#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_EXYNOS_THERMAL_EMUL */
>> +#endif /* CONFIG_THERMAL_EMULATION */
>>
>
> As the above is only used in one single function, I suggest moving it to
> same ifdef where the function belongs below. It reduces your ifdefery and
> also makes your code cleaner.
Right makes sense.
>
>
>
>> /* CPU Zone information */
>> #define PANIC_ZONE 4
>> @@ -145,6 +145,7 @@ struct thermal_cooling_conf {
>> struct thermal_sensor_conf {
>> char name[SENSOR_NAME_LEN];
>> int (*read_temperature)(void *data);
>> + int (*write_emul_temp)(void *drv_data, unsigned long temp);
>
>
> I dont get why you need a private callback to do this, assuming you have
> only one sensor_conf for exynos.
Actually It is done in this way to keep sensor related stuffs and
common thermal zone related parts. With this it is easy to split this
file in later patches.
>
>
>> struct thermal_trip_point_conf trip_data;
>> struct thermal_cooling_conf cooling_data;
>> void *private_data;
>> @@ -369,6 +370,23 @@ static int exynos_get_temp(struct thermal_zone_device
>> *thermal,
>> 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;
>
>
> In case you still want to keep your private callback, Id say -ENOTSUPP is
> better for the case you dont have the callback.
Ok
>
>
>> +
>> + if (!th_zone->sensor_conf) {
>> + pr_info("Temperature sensor not initialised\n");
>> + return -EINVAL;
>> + }
>> + data = th_zone->sensor_conf->private_data;
>> + if (th_zone->sensor_conf->write_emul_temp)
>> + ret = th_zone->sensor_conf->write_emul_temp(data, temp);
>
>
> nip: a blank line.
>
>
>> + return ret;
>> +}
>> +
>> /* Get the temperature trend */
>> static int exynos_get_trend(struct thermal_zone_device *thermal,
>> int trip, enum thermal_trend *trend)
>> @@ -392,6 +410,7 @@ 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,
>> @@ -714,6 +733,47 @@ static int exynos_tmu_read(struct exynos_tmu_data
>> *data)
>> return temp;
>> }
>>
>> +#ifdef CONFIG_THERMAL_EMULATION
>>
>
> I think your code looks cleaner if you move the defines at the beginning of
> your patch to this point.
>
Ok
>
> +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;
>> +
>
>
> Can you resolve this by not defining this callback for the
> SOC_ARCH_EXYNOS4210 thermal zone ops?
ok.
>
>
>
>> + 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);
>
>
> nip: blank line.
>
>
>> + return 0;
>> +out:
>> + return ret;
>> +}
>> +#else
>> +static int exynos_tmu_set_emulation(void *drv_data, unsigned long
>> temp)
>
> unnecessary space |
>
>> + { return -EINVAL; }
>
>
> I believe if you do a static inline function return 0, the compiler should
> translate it into a nope.
ok will check this.
>
>
>> +#endif/*CONFIG_THERMAL_EMULATION*/
>> +
>> static void exynos_tmu_work(struct work_struct *work)
>> {
>> struct exynos_tmu_data *data = container_of(work,
>> @@ -747,6 +807,7 @@ static irqreturn_t exynos_tmu_irq(int irq, void *id)
>> 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,
>> };
>>
>> #if defined(CONFIG_CPU_EXYNOS4210)
>> @@ -853,93 +914,6 @@ static inline struct exynos_tmu_platform_data
>> *exynos_get_driver_data(
>> platform_get_device_id(pdev)->driver_data;
>> }
>>
>> -#ifdef CONFIG_EXYNOS_THERMAL_EMUL
>> -static ssize_t exynos_tmu_emulation_show(struct device *dev,
>> - struct device_attribute *attr,
>> - char *buf)
>> -{
>> - struct platform_device *pdev = container_of(dev,
>> - struct platform_device, dev);
>> - struct exynos_tmu_data *data = platform_get_drvdata(pdev);
>> - unsigned int reg;
>> - u8 temp_code;
>> - int temp = 0;
>> -
>> - if (data->soc == SOC_ARCH_EXYNOS4210)
>> - goto out;
>> -
>> - mutex_lock(&data->lock);
>> - clk_enable(data->clk);
>> - reg = readl(data->base + EXYNOS_EMUL_CON);
>> - clk_disable(data->clk);
>> - mutex_unlock(&data->lock);
>> -
>> - if (reg & EXYNOS_EMUL_ENABLE) {
>> - reg >>= EXYNOS_EMUL_DATA_SHIFT;
>> - temp_code = reg & EXYNOS_EMUL_DATA_MASK;
>> - temp = code_to_temp(data, temp_code);
>> - }
>> -out:
>> - return sprintf(buf, "%d\n", temp * MCELSIUS);
>> -}
>> -
>> -static ssize_t exynos_tmu_emulation_store(struct device *dev,
>> - struct device_attribute *attr,
>> - const char *buf, size_t count)
>> -{
>> - struct platform_device *pdev = container_of(dev,
>> - struct platform_device, dev);
>> - struct exynos_tmu_data *data = platform_get_drvdata(pdev);
>> - unsigned int reg;
>> - int temp;
>> -
>> - if (data->soc == SOC_ARCH_EXYNOS4210)
>> - goto out;
>> -
>> - if (!sscanf(buf, "%d\n", &temp) || temp < 0)
>> - return -EINVAL;
>> -
>> - mutex_lock(&data->lock);
>> - clk_enable(data->clk);
>> -
>> - reg = readl(data->base + EXYNOS_EMUL_CON);
>> -
>> - if (temp) {
>> - /* Both CELSIUS and MCELSIUS type are available for input
>> */
>> - if (temp > MCELSIUS)
>> - temp /= MCELSIUS;
>> -
>> - reg = (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT) |
>> - (temp_to_code(data, (temp / MCELSIUS))
>> - << 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);
>> -
>> -out:
>> - return count;
>> -}
>> -
>> -static DEVICE_ATTR(emulation, 0644, exynos_tmu_emulation_show,
>> - exynos_tmu_emulation_store);
>> -static int create_emulation_sysfs(struct device *dev)
>> -{
>> - return device_create_file(dev, &dev_attr_emulation);
>> -}
>> -static void remove_emulation_sysfs(struct device *dev)
>> -{
>> - device_remove_file(dev, &dev_attr_emulation);
>> -}
>> -#else
>> -static inline int create_emulation_sysfs(struct device *dev) { return 0;
>> }
>> -static inline void remove_emulation_sysfs(struct device *dev) {}
>> -#endif
>> -
>> static int exynos_tmu_probe(struct platform_device *pdev)
>> {
>> struct exynos_tmu_data *data;
>> @@ -1039,10 +1013,6 @@ static int exynos_tmu_probe(struct platform_device
>> *pdev)
>> goto err_clk;
>> }
>>
>> - ret = create_emulation_sysfs(&pdev->dev);
>> - if (ret)
>> - dev_err(&pdev->dev, "Failed to create emulation mode sysfs
>> node\n");
>> -
>> return 0;
>> err_clk:
>> platform_set_drvdata(pdev, NULL);
>> @@ -1054,8 +1024,6 @@ static int exynos_tmu_remove(struct platform_device
>> *pdev)
>> {
>> struct exynos_tmu_data *data = platform_get_drvdata(pdev);
>>
>> - remove_emulation_sysfs(&pdev->dev);
>> -
>> exynos_tmu_control(pdev, false);
>>
>> exynos_unregister_thermal();
>>
>

2013-04-12 11:03:51

by Amit Kachhap

[permalink] [raw]
Subject: Re: [2/9] thermal: exynos: Add support for instance based register/unregister

Hi Eduardo,

On Fri, Apr 12, 2013 at 1:39 AM, Eduardo Valentin
<[email protected]> wrote:
> Hey Amit,
>
>
> On 26-03-2013 07:33, Amit Daniel Kachhap wrote:
>>
>> This code modifies the thermal driver to have multiple thermal zone
>> support by replacing the global thermal zone varibale with device data
>
>
> s/varibale/variable
ok type mistake :)
>
>
>> member of thermal_zone_device.
>>
>> Signed-off-by: Amit Daniel Kachhap <[email protected]>
>
>
> I understand the idea of your patch but I also see that you do at least to
> major changes. One is to set the thermal_device.devdata. The second is to
> split your internal reference private_data into driver_data and pzone_data.
> Is it possible to split this patch into two, one per modification? So it is
> easier to review your changes?
yes agreed..the kind of patch restructuring suggested by you makes sense.
>
>
>>
>> ---
>> drivers/thermal/exynos_thermal.c | 65
>> ++++++++++++++++++++++---------------
>> 1 files changed, 39 insertions(+), 26 deletions(-)
>>
>> diff --git a/drivers/thermal/exynos_thermal.c
>> b/drivers/thermal/exynos_thermal.c
>> index 1cd7837..dc9b91b 100644
>> --- a/drivers/thermal/exynos_thermal.c
>> +++ b/drivers/thermal/exynos_thermal.c
>> @@ -148,7 +148,8 @@ struct thermal_sensor_conf {
>> int (*write_emul_temp)(void *drv_data, unsigned long temp);
>> struct thermal_trip_point_conf trip_data;
>> struct thermal_cooling_conf cooling_data;
>> - void *private_data;
>> + void *driver_data;
>> + void *pzone_data;
>> };
>>
>> struct exynos_thermal_zone {
>> @@ -161,14 +162,14 @@ struct exynos_thermal_zone {
>> bool bind;
>> };
>>
>> -static struct exynos_thermal_zone *th_zone;
>> -static void exynos_unregister_thermal(void);
>> +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;
>> @@ -178,25 +179,26 @@ static int exynos_get_mode(struct
>> thermal_zone_device *thermal,
>> static int exynos_set_mode(struct thermal_zone_device *thermal,
>> enum thermal_device_mode mode)
>> {
>> - if (!th_zone->therm_dev) {
>> + struct exynos_thermal_zone *th_zone = thermal->devdata;
>> + if (!th_zone) {
>> pr_notice("thermal zone not registered\n");
>> return 0;
>> }
>>
>> - mutex_lock(&th_zone->therm_dev->lock);
>> + mutex_lock(&thermal->lock);
>>
>> if (mode == THERMAL_DEVICE_ENABLED &&
>> !th_zone->sensor_conf->trip_data.trigger_falling)
>> - th_zone->therm_dev->polling_delay = IDLE_INTERVAL;
>> + thermal->polling_delay = IDLE_INTERVAL;
>> else
>> - th_zone->therm_dev->polling_delay = 0;
>> + thermal->polling_delay = 0;
>>
>> - mutex_unlock(&th_zone->therm_dev->lock);
>> + mutex_unlock(&thermal->lock);
>>
>> th_zone->mode = mode;
>> - thermal_zone_device_update(th_zone->therm_dev);
>> + thermal_zone_device_update(thermal);
>> pr_info("thermal polling set for duration=%d msec\n",
>> - th_zone->therm_dev->polling_delay);
>> + thermal->polling_delay);
>> return 0;
>> }
>>
>> @@ -223,6 +225,8 @@ static int exynos_get_trip_type(struct
>> thermal_zone_device *thermal, int trip,
>> 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;
>>
>> @@ -269,6 +273,7 @@ static int exynos_bind(struct thermal_zone_device
>> *thermal,
>> {
>> 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;
>> @@ -315,6 +320,7 @@ 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)
>> @@ -357,13 +363,14 @@ static int exynos_unbind(struct thermal_zone_device
>> *thermal,
>> 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->private_data;
>> + data = th_zone->sensor_conf->driver_data;
>> *temp = th_zone->sensor_conf->read_temperature(data);
>> /* convert the temperature into millicelsius */
>> *temp = *temp * MCELSIUS;
>> @@ -376,12 +383,13 @@ static int exynos_set_emul_temp(struct
>> thermal_zone_device *thermal,
>> {
>> 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->private_data;
>> + 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;
>> @@ -391,7 +399,7 @@ static int exynos_set_emul_temp(struct
>> thermal_zone_device *thermal,
>> static int exynos_get_trend(struct thermal_zone_device *thermal,
>> int trip, enum thermal_trend *trend)
>> {
>> - int ret;
>> + int ret = 0;
>> unsigned long trip_temp;
>>
>> ret = exynos_get_trip_temp(thermal, trip, &trip_temp);
>> @@ -403,7 +411,7 @@ static int exynos_get_trend(struct thermal_zone_device
>> *thermal,
>> else
>> *trend = THERMAL_TREND_DROP_FULL;
>>
>> - return 0;
>> + return ret;
>> }
>> /* Operation callback functions for thermal zone */
>> static struct thermal_zone_device_ops const exynos_dev_ops = {
>> @@ -423,11 +431,12 @@ static struct thermal_zone_device_ops const
>> exynos_dev_ops = {
>> * This function may be called from interrupt based temperature sensor
>> * when threshold is changed.
>> */
>> -static void exynos_report_trigger(void)
>> +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;
>> @@ -468,6 +477,7 @@ 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");
>> @@ -489,7 +499,7 @@ static int exynos_register_thermal(struct
>> thermal_sensor_conf *sensor_conf)
>> th_zone->cool_dev_size++;
>>
>> th_zone->therm_dev =
>> thermal_zone_device_register(sensor_conf->name,
>> - EXYNOS_ZONE_COUNT, 0, NULL, &exynos_dev_ops, NULL,
>> 0,
>> + EXYNOS_ZONE_COUNT, 0, th_zone, &exynos_dev_ops,
>> NULL, 0,
>> sensor_conf->trip_data.trigger_falling ?
>> 0 : IDLE_INTERVAL);
>>
>> @@ -499,20 +509,22 @@ static int exynos_register_thermal(struct
>> thermal_sensor_conf *sensor_conf)
>> 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();
>> + exynos_unregister_thermal(sensor_conf);
>> return ret;
>> }
>>
>> /* Un-Register with the in-kernel thermal management */
>> -static void exynos_unregister_thermal(void)
>> +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;
>> @@ -774,12 +786,18 @@ 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,
>> +};
>> +
>
>
> Assuming you have only one exynos_sensor_conf and this is the parameter for
> each call to exynos_register_thermal, if you plan to add support to
> different pzone_data, then the above needs to be allocated. Or there is no
> point in having this.
Yes creating this structure by allocation is a good idea. Actually In
my case I used a different file for another type of sensor so left it
like this. Anyway your suggestion is worth implementing.
>
>
>> 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_report_trigger(&exynos_sensor_conf);
>> mutex_lock(&data->lock);
>> clk_enable(data->clk);
>> if (data->soc == SOC_ARCH_EXYNOS)
>> @@ -804,11 +822,6 @@ static irqreturn_t exynos_tmu_irq(int irq, void *id)
>>
>> return IRQ_HANDLED;
>> }
>> -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,
>> -};
>>
>> #if defined(CONFIG_CPU_EXYNOS4210)
>> static struct exynos_tmu_platform_data const exynos4210_default_tmu_data
>> = {
>> @@ -987,7 +1000,7 @@ static int exynos_tmu_probe(struct platform_device
>> *pdev)
>> exynos_tmu_control(pdev, true);
>>
>> /* Register the sensor with thermal management interface */
>> - (&exynos_sensor_conf)->private_data = data;
>> + (&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;
>> @@ -1026,7 +1039,7 @@ static int exynos_tmu_remove(struct platform_device
>> *pdev)
>>
>> exynos_tmu_control(pdev, false);
>>
>> - exynos_unregister_thermal();
>> + exynos_unregister_thermal(&exynos_sensor_conf);
>>
>> clk_put(data->clk);
>>
>>
>

2013-04-12 11:06:59

by amit daniel kachhap

[permalink] [raw]
Subject: Re: [4/9] thermal: exynos: Bifurcate exynos thermal common and tmu controller code

Hi Eduardo,

On Fri, Apr 12, 2013 at 2:00 AM, Eduardo Valentin
<[email protected]> 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 <[email protected]>
>>
>> ---
>> 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 <[email protected]>
>> + * Amit Daniel Kachhap <[email protected]>
>> + * Amit Daniel Kachhap <[email protected]>
>> + *
>> + * 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 <linux/clk.h>
>> +#include <linux/io.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/kernel.h>
>> +#include <linux/kobject.h>
>> +#include <linux/module.h>
>> +#include <linux/mutex.h>
>> +#include <linux/of.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/platform_data/exynos_thermal.h>
>> +#include <linux/slab.h>
>> +#include <linux/workqueue.h>
>> +#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 <[email protected]>");
>> +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 <[email protected]>
>> + *
>> + * 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 <linux/cpufreq.h>
>> +#include <linux/cpu_cooling.h>
>> +#include <linux/err.h>
>> +#include <linux/io.h>
>> +#include <linux/kernel.h>
>> +#include <linux/kobject.h>
>> +#include <linux/mutex.h>
>> +#include <linux/platform_data/exynos_thermal.h>
>> +#include <linux/slab.h>
>> +#include <linux/thermal.h>
>> +#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 <[email protected]>
>> + *
>> + * 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 <[email protected]>
>> - * Amit Daniel Kachhap <[email protected]>
>> - *
>> - * 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 <linux/module.h>
>> -#include <linux/err.h>
>> -#include <linux/kernel.h>
>> -#include <linux/slab.h>
>> -#include <linux/platform_device.h>
>> -#include <linux/interrupt.h>
>> -#include <linux/clk.h>
>> -#include <linux/workqueue.h>
>> -#include <linux/sysfs.h>
>> -#include <linux/kobject.h>
>> -#include <linux/io.h>
>> -#include <linux/mutex.h>
>> -#include <linux/platform_data/exynos_thermal.h>
>> -#include <linux/thermal.h>
>> -#include <linux/cpufreq.h>
>> -#include <linux/cpu_cooling.h>
>> -#include <linux/of.h>
>> -
>> -#include <plat/cpu.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
>> -
>> -/* 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]);

2013-04-12 11:09:17

by amit daniel kachhap

[permalink] [raw]
Subject: Re: [4/9] thermal: exynos: Bifurcate exynos thermal common and tmu controller code

On Fri, Apr 12, 2013 at 2:12 AM, Eduardo Valentin
<[email protected]> 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 <[email protected]>
>>
>> ---
>> 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
>>
>> 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
>
>
> Are you sure you want separated modules?
>
> If yes you have to review this patch and export the symbols from common to
> exynos4210. Saying that because it generates a compilation error:
> ERROR: "exynos_report_trigger"
> [drivers/thermal/samsung/exynos4210_thermal.ko] undefined!
> ERROR: "exynos_register_thermal"
> [drivers/thermal/samsung/exynos4210_thermal.ko] undefined!
> ERROR: "exynos_unregister_thermal"
> [drivers/thermal/samsung/exynos4210_thermal.ko] undefined!
>
> If you want separate modules, you have to EXPORT_SYMBOL those.
Ok. looks like I missed this test.
>
> You can also do what I have done for TI SoC thermal (check linux-next
> drivers/staging/ti-soc-thermal/Makefile)
ok sure.

Thanks,
Amit D
>>
>> 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 <[email protected]>
>> + * Amit Daniel Kachhap <[email protected]>
>> + * Amit Daniel Kachhap <[email protected]>
>> + *
>> + * 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 <linux/clk.h>
>> +#include <linux/io.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/kernel.h>
>> +#include <linux/kobject.h>
>> +#include <linux/module.h>
>> +#include <linux/mutex.h>
>> +#include <linux/of.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/platform_data/exynos_thermal.h>
>> +#include <linux/slab.h>
>> +#include <linux/workqueue.h>
>> +#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 <[email protected]>");
>> +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 <[email protected]>
>> + *
>> + * 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 <linux/cpufreq.h>
>> +#include <linux/cpu_cooling.h>
>> +#include <linux/err.h>
>> +#include <linux/io.h>
>> +#include <linux/kernel.h>
>> +#include <linux/kobject.h>
>> +#include <linux/mutex.h>
>> +#include <linux/platform_data/exynos_thermal.h>
>> +#include <linux/slab.h>
>> +#include <linux/thermal.h>
>> +#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 <[email protected]>
>> + *
>> + * 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 <[email protected]>
>> - * Amit Daniel Kachhap <[email protected]>
>> - *
>> - * 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 <linux/module.h>
>> -#include <linux/err.h>
>> -#include <linux/kernel.h>
>> -#include <linux/slab.h>
>> -#include <linux/platform_device.h>
>> -#include <linux/interrupt.h>
>> -#include <linux/clk.h>
>> -#include <linux/workqueue.h>
>> -#include <linux/sysfs.h>
>> -#include <linux/kobject.h>
>> -#include <linux/io.h>
>> -#include <linux/mutex.h>
>> -#include <linux/platform_data/exynos_thermal.h>
>> -#include <linux/thermal.h>
>> -#include <linux/cpufreq.h>
>> -#include <linux/cpu_cooling.h>
>> -#include <linux/of.h>
>> -
>> -#include <plat/cpu.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
>> -
>> -/* 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);

2013-04-12 11:16:12

by amit daniel kachhap

[permalink] [raw]
Subject: Re: [5/9] thermal: exynos: Make the zone handling dependent on trip count

Hi Eduardo,

On Fri, Apr 12, 2013 at 2:18 AM, Eduardo Valentin
<[email protected]> wrote:
> On 26-03-2013 07:33, Amit Daniel Kachhap wrote:
>>
>> This code changes the zone handling to use the trip count passed
>> by the TMU driver. This also helps in adding more zone support.
>>
>> Signed-off-by: Amit Daniel Kachhap <[email protected]>
>>
>> ---
>> drivers/thermal/samsung/exynos_common.c | 56
>> +++++++++++++------------
>> drivers/thermal/samsung/exynos_common.h | 13 +++---
>> include/linux/platform_data/exynos_thermal.h | 7 ++-
>> 3 files changed, 40 insertions(+), 36 deletions(-)
>>
>> diff --git a/drivers/thermal/samsung/exynos_common.c
>> b/drivers/thermal/samsung/exynos_common.c
>> index 649d67c..0c0098d 100644
>> --- a/drivers/thermal/samsung/exynos_common.c
>> +++ b/drivers/thermal/samsung/exynos_common.c
>> @@ -84,17 +84,16 @@ static int exynos_set_mode(struct thermal_zone_device
>> *thermal,
>> 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:
>> + struct exynos_thermal_zone *th_zone = thermal->devdata;
>> + int max_trip = th_zone->sensor_conf->trip_data.trip_count;
>> +
>> + if (trip < 0 || trip >= max_trip)
>> return -EINVAL;
>> - }
>> + else if (trip == (max_trip - 1))
>> + *type = THERMAL_TRIP_CRITICAL;
>> + else
>> + *type = THERMAL_TRIP_ACTIVE;
>> +
>> return 0;
>> }
>>
>> @@ -103,8 +102,9 @@ static int exynos_get_trip_temp(struct
>> thermal_zone_device *thermal, int trip,
>> unsigned long *temp)
>> {
>> struct exynos_thermal_zone *th_zone = thermal->devdata;
>> + int max_trip = th_zone->sensor_conf->trip_data.trip_count;
>>
>> - if (trip < GET_TRIP(MONITOR_ZONE) || trip > GET_TRIP(PANIC_ZONE))
>> + if (trip < 0 || trip >= max_trip)
>> return -EINVAL;
>>
>> *temp = th_zone->sensor_conf->trip_data.trip_val[trip];
>> @@ -118,10 +118,10 @@ static int exynos_get_trip_temp(struct
>> thermal_zone_device *thermal, int trip,
>> 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;
>> + struct exynos_thermal_zone *th_zone = thermal->devdata;
>> + int max_trip = th_zone->sensor_conf->trip_data.trip_count;
>> + /* Get the temp of highest trip*/
>> + return exynos_get_trip_temp(thermal, max_trip - 1, temp);
>> }
>>
>> int exynos_get_frequency_level(unsigned int cpu, unsigned int freq)
>> @@ -157,7 +157,7 @@ static int exynos_bind(struct thermal_zone_device
>> *thermal,
>> tab_size = data->cooling_data.freq_clip_count;
>>
>> if (tab_ptr == NULL || tab_size == 0)
>> - return -EINVAL;
>> + return 0;
>>
>> /* find the cooling device registered*/
>> for (i = 0; i < th_zone->cool_dev_size; i++)
>> @@ -206,7 +206,7 @@ static int exynos_unbind(struct thermal_zone_device
>> *thermal,
>> tab_size = data->cooling_data.freq_clip_count;
>>
>> if (tab_size == 0)
>> - return -EINVAL;
>> + return 0;
>>
>> /* find the cooling device registered*/
>> for (i = 0; i < th_zone->cool_dev_size; i++)
>
>
> The above two chunks are confusing. I dont understand how they are now
> supposed to be valid settings.
Well it is made valid when there is no cooling table registered and
hence no cooling.
>
>
>> @@ -366,19 +366,21 @@ int exynos_register_thermal(struct
>> thermal_sensor_conf *sensor_conf)
>> 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;
>> + if (sensor_conf->cooling_data.freq_clip_count > 0) {
>> + 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->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);
>> + sensor_conf->trip_data.trip_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");
>> diff --git a/drivers/thermal/samsung/exynos_common.h
>> b/drivers/thermal/samsung/exynos_common.h
>> index b8d289e..453e09a 100644
>> --- a/drivers/thermal/samsung/exynos_common.h
>> +++ b/drivers/thermal/samsung/exynos_common.h
>> @@ -27,23 +27,22 @@
>> #define SENSOR_NAME_LEN 16
>> #define MAX_TRIP_COUNT 8
>> #define MAX_COOLING_DEVICE 4
>> -#define MAX_THRESHOLD_LEVS 4
>> +#define MAX_THRESHOLD_LEVS 5
>>
>> #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 PANIC_ZONE 5
>> +#define ALARM_ZONE 4
>> +#define WARN_ZONE 3
>> +#define MONITOR_ZONE 2
>> +#define SAFE_ZONE 1
>>
>
>
> Updating this does not seam to be part of the intent of this patch. You
> probably want to send a separate patch for this.
yes right.
>
>
>> #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;
>> diff --git a/include/linux/platform_data/exynos_thermal.h
>> b/include/linux/platform_data/exynos_thermal.h
>> index da7e627..893b758 100644
>> --- a/include/linux/platform_data/exynos_thermal.h
>> +++ b/include/linux/platform_data/exynos_thermal.h
>> @@ -23,6 +23,8 @@
>> #define _LINUX_EXYNOS_THERMAL_H
>> #include <linux/cpu_cooling.h>
>>
>> +#define MAX_TRIP 5
>> +
>
>
>
> Should MAX_TRIP/THRESHOLD_LEVEL/PANIC_ZONE be somewhat same
Yes they can be removed.
>
>
>> enum calibration_type {
>> TYPE_ONE_POINT_TRIMMING,
>> TYPE_TWO_POINT_TRIMMING,
>> @@ -100,11 +102,12 @@ struct freq_clip_table {
>> struct exynos_tmu_platform_data {
>> u8 threshold;
>> u8 threshold_falling;
>> - u8 trigger_levels[4];
>> + u8 trigger_levels[MAX_TRIP];
>> bool trigger_level0_en;
>> bool trigger_level1_en;
>> bool trigger_level2_en;
>> bool trigger_level3_en;
>> + bool trigger_level4_en;
>>
>> u8 gain;
>> u8 reference_voltage;
>> @@ -113,7 +116,7 @@ struct exynos_tmu_platform_data {
>>
>> enum calibration_type cal_type;
>> enum soc_type type;
>> - struct freq_clip_table freq_tab[4];
>> + struct freq_clip_table freq_tab[MAX_TRIP];
>> unsigned int freq_tab_count;
>> };
>> #endif /* _LINUX_EXYNOS_THERMAL_H */
>>
>

2013-04-12 11:18:48

by amit daniel kachhap

[permalink] [raw]
Subject: Re: [6/9] thermal: exynos: small cleanups to prepare for adding exynos5440 driver

Hi Eduardo,


On Fri, Apr 12, 2013 at 2:24 AM, Eduardo Valentin
<[email protected]> wrote:
> Amit,
>
>
> On 26-03-2013 07:33, Amit Daniel Kachhap wrote:
>>
>> Add calib mode, trigger types and trigger_enable array. This is needed
>> for adding exynos5440 TMU driver.
>>
>
> I dont think the above are small cleanups. I d rather split this patch into
> three, one per change and describe them properly.
Yes Right.
>
>
>> Signed-off-by: Amit Daniel Kachhap <[email protected]>
>>
>> ---
>> drivers/thermal/samsung/exynos4210_thermal.c | 30
>> ++++++++++++------------
>> include/linux/platform_data/exynos_thermal.h | 32
>> +++++++++++++-------------
>> 2 files changed, 31 insertions(+), 31 deletions(-)
>>
>> diff --git a/drivers/thermal/samsung/exynos4210_thermal.c
>> b/drivers/thermal/samsung/exynos4210_thermal.c
>> index 09ea8c8..58d16ac 100644
>> --- a/drivers/thermal/samsung/exynos4210_thermal.c
>> +++ b/drivers/thermal/samsung/exynos4210_thermal.c
>> @@ -276,10 +276,10 @@ static void exynos_tmu_control(struct
>> platform_device *pdev, bool on)
>>
>> 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;
>> + interrupt_en = pdata->trigger_enable[3] << 12 |
>> + pdata->trigger_enable[2] << 8 |
>> + pdata->trigger_enable[1] << 4 |
>> + pdata->trigger_enable[0];
>> if (pdata->threshold_falling)
>> interrupt_en |= interrupt_en << 16;
>> } else {
>> @@ -394,10 +394,10 @@ static struct exynos_tmu_platform_data const
>> exynos4210_default_tmu_data = {
>> .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,
>> + .trigger_enable[0] = 1,
>> + .trigger_enable[1] = 1,
>> + .trigger_enable[2] = 1,
>> + .trigger_enable[3] = 0,
>> .gain = 15,
>> .reference_voltage = 7,
>> .cal_type = TYPE_ONE_POINT_TRIMMING,
>> @@ -423,10 +423,10 @@ static struct exynos_tmu_platform_data const
>> exynos_default_tmu_data = {
>> .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,
>> + .trigger_enable[0] = 1,
>> + .trigger_enable[1] = 1,
>> + .trigger_enable[2] = 1,
>> + .trigger_enable[3] = 0,
>> .gain = 8,
>> .reference_voltage = 16,
>> .noise_cancel_mode = 4,
>> @@ -566,9 +566,9 @@ static int exynos_tmu_probe(struct platform_device
>> *pdev)
>>
>> /* 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;
>> + exynos_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 < exynos_sensor_conf.trip_data.trip_count; i++)
>> exynos_sensor_conf.trip_data.trip_val[i] =
>> diff --git a/include/linux/platform_data/exynos_thermal.h
>> b/include/linux/platform_data/exynos_thermal.h
>> index 893b758..1090f48 100644
>> --- a/include/linux/platform_data/exynos_thermal.h
>> +++ b/include/linux/platform_data/exynos_thermal.h
>> @@ -31,6 +31,17 @@ enum calibration_type {
>> TYPE_NONE,
>> };
>>
>> +enum calibration_mode {
>> + SW_MODE,
>> + HW_MODE,
>> +};
>> +
>> +enum trigger_type {
>> + ACTIVE,
>> + CRITICAL,
>> + HW_TRIP,
>> +};
>> +
>> enum soc_type {
>> SOC_ARCH_EXYNOS4210 = 1,
>> SOC_ARCH_EXYNOS,
>> @@ -71,18 +82,9 @@ struct freq_clip_table {
>> * 3: temperature for trigger_level3 interrupt
>> * condition for trigger_level3 interrupt:
>> * current temperature > threshold + trigger_levels[3]
>> - * @trigger_level0_en:
>> + * @trigger_enable[]: array to denote which trigger levels are enabled.
>> * 1 = enable trigger_level0 interrupt,
>> * 0 = disable trigger_level0 interrupt
>> - * @trigger_level1_en:
>> - * 1 = enable trigger_level1 interrupt,
>> - * 0 = disable trigger_level1 interrupt
>> - * @trigger_level2_en:
>> - * 1 = enable trigger_level2 interrupt,
>> - * 0 = disable trigger_level2 interrupt
>> - * @trigger_level3_en:
>> - * 1 = enable trigger_level3 interrupt,
>> - * 0 = disable trigger_level3 interrupt
>> * @gain: gain of amplifier in the positive-TC generator block
>> * 0 <= gain <= 15
>> * @reference_voltage: reference voltage of amplifier
>> @@ -93,6 +95,7 @@ struct freq_clip_table {
>> * @type: determines the type of SOC
>> * @efuse_value: platform defined fuse value
>> * @cal_type: calibration type for temperature
>> + * @cal_mode: calibration mode for temperature
>
>
> How about trigger_type?
will add. Thanks for the detailed review.
>
>> * @freq_clip_table: Table representing frequency reduction percentage.
>> * @freq_tab_count: Count of the above table as frequency reduction may
>> * applicable to only some of the trigger levels.
>> @@ -103,18 +106,15 @@ struct exynos_tmu_platform_data {
>> u8 threshold;
>> u8 threshold_falling;
>> u8 trigger_levels[MAX_TRIP];
>> - bool trigger_level0_en;
>> - bool trigger_level1_en;
>> - bool trigger_level2_en;
>> - bool trigger_level3_en;
>> - bool trigger_level4_en;
>> -
>> + enum trigger_type trigger_type[MAX_TRIP];
>> + bool trigger_enable[MAX_TRIP];
>> u8 gain;
>> u8 reference_voltage;
>> u8 noise_cancel_mode;
>> u32 efuse_value;
>>
>> enum calibration_type cal_type;
>> + enum calibration_mode cal_mode;
>> enum soc_type type;
>> struct freq_clip_table freq_tab[MAX_TRIP];
>> unsigned int freq_tab_count;
>>
>

2013-04-12 11:32:25

by amit daniel kachhap

[permalink] [raw]
Subject: Re: [7/9] thermal: exynos: Add support for exynos5440 TMU sensor.

Hi Eduardo,

On Fri, Apr 12, 2013 at 2:34 AM, Eduardo Valentin
<[email protected]> 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 <[email protected]>
>>
>> ---
>> 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 <[email protected]>
>> + *
>> + * 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 <linux/clk.h>
>> +#include <linux/cpufreq.h>
>> +#include <linux/cpu_cooling.h>
>> +#include <linux/err.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/io.h>
>> +#include <linux/kernel.h>
>> +#include <linux/kobject.h>
>> +#include <linux/module.h>
>> +#include <linux/mutex.h>
>> +#include <linux/of.h>
>> +#include <linux/of_address.h>
>> +#include <linux/of_irq.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/slab.h>
>> +#include <linux/thermal.h>
>> +#include <linux/workqueue.h>
>> +#include <linux/platform_data/exynos_thermal.h>
>> +
>> +#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<[email protected]>");
>> +MODULE_LICENSE("GPL");
>
> GPL v2?
>
>> +MODULE_ALIAS("platform:exynos5440-tmu");
>>
>

2013-04-12 12:42:24

by Eduardo Valentin

[permalink] [raw]
Subject: Re: [4/9] thermal: exynos: Bifurcate exynos thermal common and tmu controller code

On 12-04-2013 07:09, amit daniel kachhap wrote:
> On Fri, Apr 12, 2013 at 2:12 AM, Eduardo Valentin
> <[email protected]> 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 <[email protected]>
>>>
>>> ---
>>> 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
>>>
>>> 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
>>
>>
>> Are you sure you want separated modules?
>>
>> If yes you have to review this patch and export the symbols from common to
>> exynos4210. Saying that because it generates a compilation error:
>> ERROR: "exynos_report_trigger"
>> [drivers/thermal/samsung/exynos4210_thermal.ko] undefined!
>> ERROR: "exynos_register_thermal"
>> [drivers/thermal/samsung/exynos4210_thermal.ko] undefined!
>> ERROR: "exynos_unregister_thermal"
>> [drivers/thermal/samsung/exynos4210_thermal.ko] undefined!
>>
>> If you want separate modules, you have to EXPORT_SYMBOL those.
> Ok. looks like I missed this test.
>>
>> You can also do what I have done for TI SoC thermal (check linux-next
>> drivers/staging/ti-soc-thermal/Makefile)
> ok sure.

That was obviously a suggestion. In my case I chose to have 1 single
module (ti-soc-thermal.ko) that will contain the thermal support for all
TI SoC chips. If you want to follow that design then, take that file as
an example. If not, then you need to fix the above export errors.

>
> Thanks,
> Amit D
>>>
>>> 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 <[email protected]>
>>> + * Amit Daniel Kachhap <[email protected]>
>>> + * Amit Daniel Kachhap <[email protected]>
>>> + *
>>> + * 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 <linux/clk.h>
>>> +#include <linux/io.h>
>>> +#include <linux/interrupt.h>
>>> +#include <linux/kernel.h>
>>> +#include <linux/kobject.h>
>>> +#include <linux/module.h>
>>> +#include <linux/mutex.h>
>>> +#include <linux/of.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/platform_data/exynos_thermal.h>
>>> +#include <linux/slab.h>
>>> +#include <linux/workqueue.h>
>>> +#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 <[email protected]>");
>>> +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 <[email protected]>
>>> + *
>>> + * 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 <linux/cpufreq.h>
>>> +#include <linux/cpu_cooling.h>
>>> +#include <linux/err.h>
>>> +#include <linux/io.h>
>>> +#include <linux/kernel.h>
>>> +#include <linux/kobject.h>
>>> +#include <linux/mutex.h>
>>> +#include <linux/platform_data/exynos_thermal.h>
>>> +#include <linux/slab.h>
>>> +#include <linux/thermal.h>
>>> +#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 <[email protected]>
>>> + *
>>> + * 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 <[email protected]>
>>> - * Amit Daniel Kachhap <[email protected]>
>>> - *
>>> - * 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 <linux/module.h>
>>> -#include <linux/err.h>
>>> -#include <linux/kernel.h>
>>> -#include <linux/slab.h>
>>> -#include <linux/platform_device.h>
>>> -#include <linux/interrupt.h>
>>> -#include <linux/clk.h>
>>> -#include <linux/workqueue.h>
>>> -#include <linux/sysfs.h>
>>> -#include <linux/kobject.h>
>>> -#include <linux/io.h>
>>> -#include <linux/mutex.h>
>>> -#include <linux/platform_data/exynos_thermal.h>
>>> -#include <linux/thermal.h>
>>> -#include <linux/cpufreq.h>
>>> -#include <linux/cpu_cooling.h>
>>> -#include <linux/of.h>
>>> -
>>> -#include <plat/cpu.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
>>> -
>>> -/* 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);
>
>

2013-04-12 12:45:13

by Eduardo Valentin

[permalink] [raw]
Subject: Re: [5/9] thermal: exynos: Make the zone handling dependent on trip count

On 12-04-2013 07:16, amit daniel kachhap wrote:
> Hi Eduardo,
>
> On Fri, Apr 12, 2013 at 2:18 AM, Eduardo Valentin
> <[email protected]> wrote:
>> On 26-03-2013 07:33, Amit Daniel Kachhap wrote:
>>>
>>> This code changes the zone handling to use the trip count passed
>>> by the TMU driver. This also helps in adding more zone support.
>>>
>>> Signed-off-by: Amit Daniel Kachhap <[email protected]>
>>>
>>> ---
>>> drivers/thermal/samsung/exynos_common.c | 56
>>> +++++++++++++------------
>>> drivers/thermal/samsung/exynos_common.h | 13 +++---
>>> include/linux/platform_data/exynos_thermal.h | 7 ++-
>>> 3 files changed, 40 insertions(+), 36 deletions(-)
>>>
>>> diff --git a/drivers/thermal/samsung/exynos_common.c
>>> b/drivers/thermal/samsung/exynos_common.c
>>> index 649d67c..0c0098d 100644
>>> --- a/drivers/thermal/samsung/exynos_common.c
>>> +++ b/drivers/thermal/samsung/exynos_common.c
>>> @@ -84,17 +84,16 @@ static int exynos_set_mode(struct thermal_zone_device
>>> *thermal,
>>> 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:
>>> + struct exynos_thermal_zone *th_zone = thermal->devdata;
>>> + int max_trip = th_zone->sensor_conf->trip_data.trip_count;
>>> +
>>> + if (trip < 0 || trip >= max_trip)
>>> return -EINVAL;
>>> - }
>>> + else if (trip == (max_trip - 1))
>>> + *type = THERMAL_TRIP_CRITICAL;
>>> + else
>>> + *type = THERMAL_TRIP_ACTIVE;
>>> +
>>> return 0;
>>> }
>>>
>>> @@ -103,8 +102,9 @@ static int exynos_get_trip_temp(struct
>>> thermal_zone_device *thermal, int trip,
>>> unsigned long *temp)
>>> {
>>> struct exynos_thermal_zone *th_zone = thermal->devdata;
>>> + int max_trip = th_zone->sensor_conf->trip_data.trip_count;
>>>
>>> - if (trip < GET_TRIP(MONITOR_ZONE) || trip > GET_TRIP(PANIC_ZONE))
>>> + if (trip < 0 || trip >= max_trip)
>>> return -EINVAL;
>>>
>>> *temp = th_zone->sensor_conf->trip_data.trip_val[trip];
>>> @@ -118,10 +118,10 @@ static int exynos_get_trip_temp(struct
>>> thermal_zone_device *thermal, int trip,
>>> 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;
>>> + struct exynos_thermal_zone *th_zone = thermal->devdata;
>>> + int max_trip = th_zone->sensor_conf->trip_data.trip_count;
>>> + /* Get the temp of highest trip*/
>>> + return exynos_get_trip_temp(thermal, max_trip - 1, temp);
>>> }
>>>
>>> int exynos_get_frequency_level(unsigned int cpu, unsigned int freq)
>>> @@ -157,7 +157,7 @@ static int exynos_bind(struct thermal_zone_device
>>> *thermal,
>>> tab_size = data->cooling_data.freq_clip_count;
>>>
>>> if (tab_ptr == NULL || tab_size == 0)
>>> - return -EINVAL;
>>> + return 0;
>>>
>>> /* find the cooling device registered*/
>>> for (i = 0; i < th_zone->cool_dev_size; i++)
>>> @@ -206,7 +206,7 @@ static int exynos_unbind(struct thermal_zone_device
>>> *thermal,
>>> tab_size = data->cooling_data.freq_clip_count;
>>>
>>> if (tab_size == 0)
>>> - return -EINVAL;
>>> + return 0;
>>>
>>> /* find the cooling device registered*/
>>> for (i = 0; i < th_zone->cool_dev_size; i++)
>>
>>
>> The above two chunks are confusing. I dont understand how they are now
>> supposed to be valid settings.
> Well it is made valid when there is no cooling table registered and
> hence no cooling.

I suppose that is the case when you have sensors covering domains
different of your CPU domain, where you cool off using cpufreq (table),
right?

I suggest you adding a comment explaining why this is now valid..


>>
>>
>>> @@ -366,19 +366,21 @@ int exynos_register_thermal(struct
>>> thermal_sensor_conf *sensor_conf)
>>> 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;
>>> + if (sensor_conf->cooling_data.freq_clip_count > 0) {
>>> + 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->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);
>>> + sensor_conf->trip_data.trip_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");
>>> diff --git a/drivers/thermal/samsung/exynos_common.h
>>> b/drivers/thermal/samsung/exynos_common.h
>>> index b8d289e..453e09a 100644
>>> --- a/drivers/thermal/samsung/exynos_common.h
>>> +++ b/drivers/thermal/samsung/exynos_common.h
>>> @@ -27,23 +27,22 @@
>>> #define SENSOR_NAME_LEN 16
>>> #define MAX_TRIP_COUNT 8
>>> #define MAX_COOLING_DEVICE 4
>>> -#define MAX_THRESHOLD_LEVS 4
>>> +#define MAX_THRESHOLD_LEVS 5
>>>
>>> #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 PANIC_ZONE 5
>>> +#define ALARM_ZONE 4
>>> +#define WARN_ZONE 3
>>> +#define MONITOR_ZONE 2
>>> +#define SAFE_ZONE 1
>>>
>>
>>
>> Updating this does not seam to be part of the intent of this patch. You
>> probably want to send a separate patch for this.
> yes right.
>>
>>
>>> #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;
>>> diff --git a/include/linux/platform_data/exynos_thermal.h
>>> b/include/linux/platform_data/exynos_thermal.h
>>> index da7e627..893b758 100644
>>> --- a/include/linux/platform_data/exynos_thermal.h
>>> +++ b/include/linux/platform_data/exynos_thermal.h
>>> @@ -23,6 +23,8 @@
>>> #define _LINUX_EXYNOS_THERMAL_H
>>> #include <linux/cpu_cooling.h>
>>>
>>> +#define MAX_TRIP 5
>>> +
>>
>>
>>
>> Should MAX_TRIP/THRESHOLD_LEVEL/PANIC_ZONE be somewhat same
> Yes they can be removed.
>>
>>
>>> enum calibration_type {
>>> TYPE_ONE_POINT_TRIMMING,
>>> TYPE_TWO_POINT_TRIMMING,
>>> @@ -100,11 +102,12 @@ struct freq_clip_table {
>>> struct exynos_tmu_platform_data {
>>> u8 threshold;
>>> u8 threshold_falling;
>>> - u8 trigger_levels[4];
>>> + u8 trigger_levels[MAX_TRIP];
>>> bool trigger_level0_en;
>>> bool trigger_level1_en;
>>> bool trigger_level2_en;
>>> bool trigger_level3_en;
>>> + bool trigger_level4_en;
>>>
>>> u8 gain;
>>> u8 reference_voltage;
>>> @@ -113,7 +116,7 @@ struct exynos_tmu_platform_data {
>>>
>>> enum calibration_type cal_type;
>>> enum soc_type type;
>>> - struct freq_clip_table freq_tab[4];
>>> + struct freq_clip_table freq_tab[MAX_TRIP];
>>> unsigned int freq_tab_count;
>>> };
>>> #endif /* _LINUX_EXYNOS_THERMAL_H */
>>>
>>
>
>