All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/4] thermal: exynos: Add kernel thermal support for exynos platform
@ 2012-03-03 11:06 ` Amit Daniel Kachhap
  0 siblings, 0 replies; 43+ messages in thread
From: Amit Daniel Kachhap @ 2012-03-03 11:06 UTC (permalink / raw)
  To: linux-pm, linux-samsung-soc
  Cc: linaro-dev, patches, linux-kernel, lm-sensors, linux-acpi

Changes since RFC:
*Moved the Temperature sensor driver from driver/hwmon/ to driver/mfd
 as discussed with Guenter Roeck <guenter.roeck@ericsson.com> and 
 Donggeun Kim <dg77.kim@samsung.com> (https://lkml.org/lkml/2012/1/5/7)
*Some changes according to the changes in common cpu cooling APIs

All the patchset based on Kernel version 3.3-rc5 and uses the cpufreq
cooling registration APIs implemented in earlier patchset 
https://lkml.org/lkml/2012/2/22/123

The code added in this patchset adds a thermal interface layer for samsung
exynos platforms. This layer is registered from the temperature sensor driver 
and recieves/monitor the temperature from the sensor and informs the
generic thermal layer to take the necessary cooling action. Currently, this
layer can be used to create only one thermal zone and hence only one
temperature sensor can register. The future goal is to make this handle
multiple thermal zones.

Some modifications are done in the temperature sensor driver to export the
information needed for the thermal interface to register with the core linux
thermal framework and with the cpu frequency based cooling devices.

A simple data/control flow diagrams to illustrate this,

Core Linux thermal <------->  Exynos thermal  <-------- Temperature Sensor
	  |                             |
	 \|/                            |
  Cpufreq cooling device <-----


Amit Daniel Kachhap (4):
  thermal: exynos: Add thermal interface support for linux thermal
    layer
  hwmon: exynos4: Move thermal sensor driver to driver/mfd directory
  thermal: exynos4: Register the tmu sensor with the thermal interface
    layer
  ARM: exynos4: Add thermal sensor driver platform device support

 Documentation/hwmon/exynos4_tmu           |   81 -----
 Documentation/mfd/exynos4_tmu             |   52 +++
 arch/arm/mach-exynos/Kconfig              |   11 +
 arch/arm/mach-exynos/Makefile             |    1 +
 arch/arm/mach-exynos/clock.c              |    4 +
 arch/arm/mach-exynos/dev-tmu.c            |   64 ++++
 arch/arm/mach-exynos/include/mach/irqs.h  |    2 +
 arch/arm/mach-exynos/include/mach/map.h   |    1 +
 arch/arm/mach-exynos/mach-origen.c        |    1 +
 arch/arm/plat-samsung/include/plat/devs.h |    1 +
 drivers/hwmon/Kconfig                     |   10 -
 drivers/hwmon/Makefile                    |    1 -
 drivers/hwmon/exynos4_tmu.c               |  514 -----------------------------
 drivers/mfd/Kconfig                       |   10 +
 drivers/mfd/Makefile                      |    1 +
 drivers/mfd/exynos4_tmu.c                 |  443 +++++++++++++++++++++++++
 drivers/thermal/Kconfig                   |    8 +
 drivers/thermal/Makefile                  |    1 +
 drivers/thermal/exynos_thermal.c          |  272 +++++++++++++++
 include/linux/exynos_thermal.h            |   72 ++++
 include/linux/platform_data/exynos4_tmu.h |    7 +
 21 files changed, 951 insertions(+), 606 deletions(-)
 delete mode 100644 Documentation/hwmon/exynos4_tmu
 create mode 100644 Documentation/mfd/exynos4_tmu
 create mode 100644 arch/arm/mach-exynos/dev-tmu.c
 delete mode 100644 drivers/hwmon/exynos4_tmu.c
 create mode 100644 drivers/mfd/exynos4_tmu.c
 create mode 100644 drivers/thermal/exynos_thermal.c
 create mode 100644 include/linux/exynos_thermal.h

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [PATCH 0/4] thermal: exynos: Add kernel thermal support for exynos platform
@ 2012-03-03 11:06 ` Amit Daniel Kachhap
  0 siblings, 0 replies; 43+ messages in thread
From: Amit Daniel Kachhap @ 2012-03-03 11:06 UTC (permalink / raw)
  To: linux-pm, linux-samsung-soc
  Cc: linux-kernel, mjg59, linux-acpi, lenb, linaro-dev, lm-sensors,
	amit.kachhap, eduardo.valentin, durgadoss.r, patches

Changes since RFC:
*Moved the Temperature sensor driver from driver/hwmon/ to driver/mfd
 as discussed with Guenter Roeck <guenter.roeck@ericsson.com> and 
 Donggeun Kim <dg77.kim@samsung.com> (https://lkml.org/lkml/2012/1/5/7)
*Some changes according to the changes in common cpu cooling APIs

All the patchset based on Kernel version 3.3-rc5 and uses the cpufreq
cooling registration APIs implemented in earlier patchset 
https://lkml.org/lkml/2012/2/22/123

The code added in this patchset adds a thermal interface layer for samsung
exynos platforms. This layer is registered from the temperature sensor driver 
and recieves/monitor the temperature from the sensor and informs the
generic thermal layer to take the necessary cooling action. Currently, this
layer can be used to create only one thermal zone and hence only one
temperature sensor can register. The future goal is to make this handle
multiple thermal zones.

Some modifications are done in the temperature sensor driver to export the
information needed for the thermal interface to register with the core linux
thermal framework and with the cpu frequency based cooling devices.

A simple data/control flow diagrams to illustrate this,

Core Linux thermal <------->  Exynos thermal  <-------- Temperature Sensor
	  |                             |
	 \|/                            |
  Cpufreq cooling device <-----


Amit Daniel Kachhap (4):
  thermal: exynos: Add thermal interface support for linux thermal
    layer
  hwmon: exynos4: Move thermal sensor driver to driver/mfd directory
  thermal: exynos4: Register the tmu sensor with the thermal interface
    layer
  ARM: exynos4: Add thermal sensor driver platform device support

 Documentation/hwmon/exynos4_tmu           |   81 -----
 Documentation/mfd/exynos4_tmu             |   52 +++
 arch/arm/mach-exynos/Kconfig              |   11 +
 arch/arm/mach-exynos/Makefile             |    1 +
 arch/arm/mach-exynos/clock.c              |    4 +
 arch/arm/mach-exynos/dev-tmu.c            |   64 ++++
 arch/arm/mach-exynos/include/mach/irqs.h  |    2 +
 arch/arm/mach-exynos/include/mach/map.h   |    1 +
 arch/arm/mach-exynos/mach-origen.c        |    1 +
 arch/arm/plat-samsung/include/plat/devs.h |    1 +
 drivers/hwmon/Kconfig                     |   10 -
 drivers/hwmon/Makefile                    |    1 -
 drivers/hwmon/exynos4_tmu.c               |  514 -----------------------------
 drivers/mfd/Kconfig                       |   10 +
 drivers/mfd/Makefile                      |    1 +
 drivers/mfd/exynos4_tmu.c                 |  443 +++++++++++++++++++++++++
 drivers/thermal/Kconfig                   |    8 +
 drivers/thermal/Makefile                  |    1 +
 drivers/thermal/exynos_thermal.c          |  272 +++++++++++++++
 include/linux/exynos_thermal.h            |   72 ++++
 include/linux/platform_data/exynos4_tmu.h |    7 +
 21 files changed, 951 insertions(+), 606 deletions(-)
 delete mode 100644 Documentation/hwmon/exynos4_tmu
 create mode 100644 Documentation/mfd/exynos4_tmu
 create mode 100644 arch/arm/mach-exynos/dev-tmu.c
 delete mode 100644 drivers/hwmon/exynos4_tmu.c
 create mode 100644 drivers/mfd/exynos4_tmu.c
 create mode 100644 drivers/thermal/exynos_thermal.c
 create mode 100644 include/linux/exynos_thermal.h


^ permalink raw reply	[flat|nested] 43+ messages in thread

* [PATCH 1/4] thermal: exynos: Add thermal interface support for linux thermal layer
  2012-03-03 11:06 ` Amit Daniel Kachhap
  (?)
@ 2012-03-03 11:06   ` Amit Daniel Kachhap
  -1 siblings, 0 replies; 43+ messages in thread
From: Amit Daniel Kachhap @ 2012-03-03 11:06 UTC (permalink / raw)
  To: linux-pm, linux-samsung-soc
  Cc: linaro-dev, patches, linux-kernel, lm-sensors, linux-acpi

This codes uses the generic linux thermal layer and creates a bridge
between temperature sensors, linux thermal framework and cooling devices
for samsung exynos platform. This layer recieves or monitor the
temperature from the sensor and informs the generic thermal layer to take
the necessary cooling action.

Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>
---
 drivers/thermal/Kconfig          |    8 +
 drivers/thermal/Makefile         |    1 +
 drivers/thermal/exynos_thermal.c |  272 ++++++++++++++++++++++++++++++++++++++
 include/linux/exynos_thermal.h   |   72 ++++++++++
 4 files changed, 353 insertions(+), 0 deletions(-)
 create mode 100644 drivers/thermal/exynos_thermal.c
 create mode 100644 include/linux/exynos_thermal.h

diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 298c1cd..4e8df56 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -29,3 +29,11 @@ config CPU_THERMAL
 	  This will be useful for platforms using the generic thermal interface
 	  and not the ACPI interface.
 	  If you want this support, you should say Y or M here.
+
+config SAMSUNG_THERMAL_INTERFACE
+	bool "Samsung Thermal interface support"
+	depends on THERMAL && CPU_THERMAL
+	help
+	  This is a samsung thermal interface which will be used as
+	  a link between sensors and cooling devices with linux thermal
+	  framework.
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 655cbc4..c67b6b2 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -4,3 +4,4 @@
 
 obj-$(CONFIG_THERMAL)		+= thermal_sys.o
 obj-$(CONFIG_CPU_THERMAL)	+= cpu_cooling.o
+obj-$(CONFIG_SAMSUNG_THERMAL_INTERFACE)	+= exynos_thermal.o
diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c
new file mode 100644
index 0000000..878d45c
--- /dev/null
+++ b/drivers/thermal/exynos_thermal.c
@@ -0,0 +1,272 @@
+/* linux/drivers/thermal/exynos_thermal.c
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/thermal.h>
+#include <linux/platform_device.h>
+#include <linux/cpufreq.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/cpu_cooling.h>
+#include <linux/exynos_thermal.h>
+
+#define MAX_COOLING_DEVICE 4
+struct exynos4_thermal_zone {
+	unsigned int idle_interval;
+	unsigned int active_interval;
+	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;
+};
+
+static struct exynos4_thermal_zone *th_zone;
+
+static int exynos4_get_mode(struct thermal_zone_device *thermal,
+			    enum thermal_device_mode *mode)
+{
+	if (th_zone->sensor_conf) {
+		pr_info("Temperature sensor not initialised\n");
+		*mode = THERMAL_DEVICE_DISABLED;
+	} else
+		*mode = THERMAL_DEVICE_ENABLED;
+	return 0;
+}
+
+static int exynos4_set_mode(struct thermal_zone_device *thermal,
+			    enum thermal_device_mode mode)
+{
+	if (!th_zone->therm_dev) {
+		pr_notice("thermal zone not registered\n");
+		return 0;
+	}
+	if (mode == THERMAL_DEVICE_ENABLED)
+		th_zone->therm_dev->polling_delay =
+				th_zone->active_interval*1000;
+	else
+		th_zone->therm_dev->polling_delay =
+				th_zone->idle_interval*1000;
+
+	thermal_zone_device_update(th_zone->therm_dev);
+	pr_info("thermal polling set for duration=%d sec\n",
+				th_zone->therm_dev->polling_delay/1000);
+	return 0;
+}
+
+/*This may be called from interrupt based temperature sensor*/
+void exynos4_report_trigger(void)
+{
+	unsigned int monitor_temp;
+
+	if (!th_zone || !th_zone->therm_dev)
+		return;
+
+	monitor_temp = th_zone->sensor_conf->trip_data.trip_val[0];
+
+	thermal_zone_device_update(th_zone->therm_dev);
+
+	mutex_lock(&th_zone->therm_dev->lock);
+	if (th_zone->therm_dev->last_temperature > monitor_temp)
+		th_zone->therm_dev->polling_delay =
+					th_zone->active_interval*1000;
+	else
+		th_zone->therm_dev->polling_delay =
+					th_zone->idle_interval*1000;
+
+	kobject_uevent(&th_zone->therm_dev->device.kobj, KOBJ_CHANGE);
+	mutex_unlock(&th_zone->therm_dev->lock);
+}
+
+static int exynos4_get_trip_type(struct thermal_zone_device *thermal, int trip,
+				 enum thermal_trip_type *type)
+{
+	if (trip == 0 || trip == 1)
+		*type = THERMAL_TRIP_STATE_ACTIVE;
+	else if (trip == 2)
+		*type = THERMAL_TRIP_CRITICAL;
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+static int exynos4_get_trip_temp(struct thermal_zone_device *thermal, int trip,
+				 unsigned long *temp)
+{
+	/*Monitor zone*/
+	if (trip == 0)
+		*temp = th_zone->sensor_conf->trip_data.trip_val[0];
+	/*Warn zone*/
+	else if (trip == 1)
+		*temp = th_zone->sensor_conf->trip_data.trip_val[1];
+	/*Panic zone*/
+	else if (trip == 2)
+		*temp = th_zone->sensor_conf->trip_data.trip_val[2];
+	else
+		return -EINVAL;
+	/*convert the temperature into millicelsius*/
+	*temp = *temp * 1000;
+
+	return 0;
+}
+
+static int exynos4_get_crit_temp(struct thermal_zone_device *thermal,
+				 unsigned long *temp)
+{
+	/*Panic zone*/
+	*temp = th_zone->sensor_conf->trip_data.trip_val[2];
+	/*convert the temperature into millicelsius*/
+	*temp = *temp * 1000;
+	return 0;
+}
+
+static int exynos4_bind(struct thermal_zone_device *thermal,
+			struct thermal_cooling_device *cdev)
+{
+	/* if the cooling device is the one from exynos4 bind it */
+	if (cdev != th_zone->cool_dev[0])
+		return 0;
+
+	if (thermal_zone_bind_cooling_device(thermal, 0, cdev)) {
+		pr_err("error binding cooling dev\n");
+		return -EINVAL;
+	}
+	if (thermal_zone_bind_cooling_device(thermal, 1, cdev)) {
+		pr_err("error binding cooling dev\n");
+		return -EINVAL;
+	}
+
+	return 0;
+
+}
+
+static int exynos4_unbind(struct thermal_zone_device *thermal,
+			  struct thermal_cooling_device *cdev)
+{
+	if (cdev != th_zone->cool_dev[0])
+		return 0;
+
+	if (thermal_zone_unbind_cooling_device(thermal, 0, cdev)) {
+		pr_err("error unbinding cooling dev\n");
+		return -EINVAL;
+	}
+	if (thermal_zone_unbind_cooling_device(thermal, 1, cdev)) {
+		pr_err("error unbinding cooling dev\n");
+		return -EINVAL;
+	}
+	return 0;
+
+}
+
+static int exynos4_get_temp(struct thermal_zone_device *thermal,
+			       unsigned long *temp)
+{
+	void *data;
+
+	if (!th_zone->sensor_conf) {
+		pr_info("Temperature sensor not initialised\n");
+		return -EINVAL;
+	}
+	data = th_zone->sensor_conf->private_data;
+	*temp = th_zone->sensor_conf->read_temperature(data);
+	/*convert the temperature into millicelsius*/
+	*temp = *temp * 1000;
+	return 0;
+}
+
+/* bind callback functions to thermalzone */
+static struct thermal_zone_device_ops exynos4_dev_ops = {
+	.bind = exynos4_bind,
+	.unbind = exynos4_unbind,
+	.get_temp = exynos4_get_temp,
+	.get_mode = exynos4_get_mode,
+	.set_mode = exynos4_set_mode,
+	.get_trip_type = exynos4_get_trip_type,
+	.get_trip_temp = exynos4_get_trip_temp,
+	.get_crit_temp = exynos4_get_crit_temp,
+};
+
+int exynos4_register_thermal(struct thermal_sensor_conf *sensor_conf)
+{
+	int ret, count, tab_size;
+	struct freq_pctg_table *tab_ptr;
+
+	if (!sensor_conf || !sensor_conf->read_temperature) {
+		pr_err("Temperature sensor not initialised\n");
+		return -EINVAL;
+	}
+
+	th_zone = kzalloc(sizeof(struct exynos4_thermal_zone), GFP_KERNEL);
+	if (!th_zone) {
+		ret = -ENOMEM;
+		goto err_unregister;
+	}
+
+	th_zone->sensor_conf = sensor_conf;
+
+	tab_ptr = (struct freq_pctg_table *)sensor_conf->cooling_data.freq_data;
+	tab_size = sensor_conf->cooling_data.freq_pctg_count;
+
+	/*Register the cpufreq cooling device*/
+	th_zone->cool_dev_size = 1;
+	count = 0;
+	th_zone->cool_dev[count] = cpufreq_cooling_register(
+			(struct freq_pctg_table *)&(tab_ptr[count]),
+			tab_size, cpumask_of(0));
+
+	if (IS_ERR(th_zone->cool_dev[count])) {
+		pr_err("Failed to register cpufreq cooling device\n");
+		ret = -EINVAL;
+		th_zone->cool_dev_size = 0;
+		goto err_unregister;
+	}
+
+	th_zone->therm_dev = thermal_zone_device_register(sensor_conf->name,
+				3, NULL, &exynos4_dev_ops, 0, 0, 0, 1000);
+	if (IS_ERR(th_zone->therm_dev)) {
+		pr_err("Failed to register thermal zone device\n");
+		ret = -EINVAL;
+		goto err_unregister;
+	}
+
+	th_zone->active_interval = 1;
+	th_zone->idle_interval = 10;
+
+	exynos4_set_mode(th_zone->therm_dev, THERMAL_DEVICE_DISABLED);
+
+	pr_info("Exynos: Kernel Thermal management registered\n");
+
+	return 0;
+
+err_unregister:
+	exynos4_unregister_thermal();
+	return ret;
+}
+EXPORT_SYMBOL(exynos4_register_thermal);
+
+void exynos4_unregister_thermal(void)
+{
+	unsigned int i;
+
+	for (i = 0; i < th_zone->cool_dev_size; i++) {
+		if (th_zone && th_zone->cool_dev[i])
+			cpufreq_cooling_unregister(th_zone->cool_dev[i]);
+	}
+
+	if (th_zone && th_zone->therm_dev)
+		thermal_zone_device_unregister(th_zone->therm_dev);
+
+	kfree(th_zone);
+
+	pr_info("Exynos: Kernel Thermal management unregistered\n");
+}
+EXPORT_SYMBOL(exynos4_unregister_thermal);
diff --git a/include/linux/exynos_thermal.h b/include/linux/exynos_thermal.h
new file mode 100644
index 0000000..186e409
--- /dev/null
+++ b/include/linux/exynos_thermal.h
@@ -0,0 +1,72 @@
+/* linux/include/linux/exynos_thermal.h
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef THERMAL_INTERFACE_H
+#define THERMAL_INTERFACE_H
+/* CPU Zone information */
+
+#define SENSOR_NAME_LEN	16
+#define MAX_TRIP_COUNT	8
+
+#define PANIC_ZONE      4
+#define WARN_ZONE       3
+#define MONITOR_ZONE    2
+#define SAFE_ZONE       1
+#define NO_ACTION       0
+
+
+struct	thermal_trip_point_conf {
+	int trip_val[MAX_TRIP_COUNT];
+	int trip_count;
+};
+
+struct	thermal_cooling_conf {
+	struct freq_pctg_table freq_data[MAX_TRIP_COUNT];
+	int freq_pctg_count;
+};
+
+/**
+ * struct exynos4_tmu_platform_data
+ * @name: name of the temperature sensor
+ * @read_temperature: A function pointer to read temperature info
+ * @private_data: Temperature sensor private data
+ * @sensor_data: Sensor specific information like trigger temperature, level
+ */
+struct thermal_sensor_conf {
+	char	name[SENSOR_NAME_LEN];
+	int	(*read_temperature)(void *data);
+	struct	thermal_trip_point_conf trip_data;
+	struct	thermal_cooling_conf cooling_data;
+	void	*private_data;
+};
+
+/**
+ * exynos4_register_thermal: Register to the exynos thermal interface.
+ * @sensor_conf:   Structure containing temperature sensor information
+ *
+ * returns zero on success, else negative errno.
+ */
+int exynos4_register_thermal(struct thermal_sensor_conf *sensor_conf);
+
+/**
+ * exynos4_unregister_thermal: Un-register from the exynos thermal interface.
+ *
+ * return not applicable.
+ */
+void exynos4_unregister_thermal(void);
+
+/**
+ * exynos4_report_trigger: Report any trigger level crossed in the
+ *	temperature sensor. This may be useful to take any cooling action.
+ *
+ * return not applicable.
+ */
+extern void exynos4_report_trigger(void);
+#endif
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH 1/4] thermal: exynos: Add thermal interface support for linux thermal layer
@ 2012-03-03 11:06   ` Amit Daniel Kachhap
  0 siblings, 0 replies; 43+ messages in thread
From: Amit Daniel Kachhap @ 2012-03-03 11:06 UTC (permalink / raw)
  To: linux-pm, linux-samsung-soc
  Cc: linux-kernel, mjg59, linux-acpi, lenb, linaro-dev, lm-sensors,
	amit.kachhap, eduardo.valentin, durgadoss.r, patches

This codes uses the generic linux thermal layer and creates a bridge
between temperature sensors, linux thermal framework and cooling devices
for samsung exynos platform. This layer recieves or monitor the
temperature from the sensor and informs the generic thermal layer to take
the necessary cooling action.

Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>
---
 drivers/thermal/Kconfig          |    8 +
 drivers/thermal/Makefile         |    1 +
 drivers/thermal/exynos_thermal.c |  272 ++++++++++++++++++++++++++++++++++++++
 include/linux/exynos_thermal.h   |   72 ++++++++++
 4 files changed, 353 insertions(+), 0 deletions(-)
 create mode 100644 drivers/thermal/exynos_thermal.c
 create mode 100644 include/linux/exynos_thermal.h

diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 298c1cd..4e8df56 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -29,3 +29,11 @@ config CPU_THERMAL
 	  This will be useful for platforms using the generic thermal interface
 	  and not the ACPI interface.
 	  If you want this support, you should say Y or M here.
+
+config SAMSUNG_THERMAL_INTERFACE
+	bool "Samsung Thermal interface support"
+	depends on THERMAL && CPU_THERMAL
+	help
+	  This is a samsung thermal interface which will be used as
+	  a link between sensors and cooling devices with linux thermal
+	  framework.
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 655cbc4..c67b6b2 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -4,3 +4,4 @@
 
 obj-$(CONFIG_THERMAL)		+= thermal_sys.o
 obj-$(CONFIG_CPU_THERMAL)	+= cpu_cooling.o
+obj-$(CONFIG_SAMSUNG_THERMAL_INTERFACE)	+= exynos_thermal.o
diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c
new file mode 100644
index 0000000..878d45c
--- /dev/null
+++ b/drivers/thermal/exynos_thermal.c
@@ -0,0 +1,272 @@
+/* linux/drivers/thermal/exynos_thermal.c
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/thermal.h>
+#include <linux/platform_device.h>
+#include <linux/cpufreq.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/cpu_cooling.h>
+#include <linux/exynos_thermal.h>
+
+#define MAX_COOLING_DEVICE 4
+struct exynos4_thermal_zone {
+	unsigned int idle_interval;
+	unsigned int active_interval;
+	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;
+};
+
+static struct exynos4_thermal_zone *th_zone;
+
+static int exynos4_get_mode(struct thermal_zone_device *thermal,
+			    enum thermal_device_mode *mode)
+{
+	if (th_zone->sensor_conf) {
+		pr_info("Temperature sensor not initialised\n");
+		*mode = THERMAL_DEVICE_DISABLED;
+	} else
+		*mode = THERMAL_DEVICE_ENABLED;
+	return 0;
+}
+
+static int exynos4_set_mode(struct thermal_zone_device *thermal,
+			    enum thermal_device_mode mode)
+{
+	if (!th_zone->therm_dev) {
+		pr_notice("thermal zone not registered\n");
+		return 0;
+	}
+	if (mode == THERMAL_DEVICE_ENABLED)
+		th_zone->therm_dev->polling_delay =
+				th_zone->active_interval*1000;
+	else
+		th_zone->therm_dev->polling_delay =
+				th_zone->idle_interval*1000;
+
+	thermal_zone_device_update(th_zone->therm_dev);
+	pr_info("thermal polling set for duration=%d sec\n",
+				th_zone->therm_dev->polling_delay/1000);
+	return 0;
+}
+
+/*This may be called from interrupt based temperature sensor*/
+void exynos4_report_trigger(void)
+{
+	unsigned int monitor_temp;
+
+	if (!th_zone || !th_zone->therm_dev)
+		return;
+
+	monitor_temp = th_zone->sensor_conf->trip_data.trip_val[0];
+
+	thermal_zone_device_update(th_zone->therm_dev);
+
+	mutex_lock(&th_zone->therm_dev->lock);
+	if (th_zone->therm_dev->last_temperature > monitor_temp)
+		th_zone->therm_dev->polling_delay =
+					th_zone->active_interval*1000;
+	else
+		th_zone->therm_dev->polling_delay =
+					th_zone->idle_interval*1000;
+
+	kobject_uevent(&th_zone->therm_dev->device.kobj, KOBJ_CHANGE);
+	mutex_unlock(&th_zone->therm_dev->lock);
+}
+
+static int exynos4_get_trip_type(struct thermal_zone_device *thermal, int trip,
+				 enum thermal_trip_type *type)
+{
+	if (trip == 0 || trip == 1)
+		*type = THERMAL_TRIP_STATE_ACTIVE;
+	else if (trip == 2)
+		*type = THERMAL_TRIP_CRITICAL;
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+static int exynos4_get_trip_temp(struct thermal_zone_device *thermal, int trip,
+				 unsigned long *temp)
+{
+	/*Monitor zone*/
+	if (trip == 0)
+		*temp = th_zone->sensor_conf->trip_data.trip_val[0];
+	/*Warn zone*/
+	else if (trip == 1)
+		*temp = th_zone->sensor_conf->trip_data.trip_val[1];
+	/*Panic zone*/
+	else if (trip == 2)
+		*temp = th_zone->sensor_conf->trip_data.trip_val[2];
+	else
+		return -EINVAL;
+	/*convert the temperature into millicelsius*/
+	*temp = *temp * 1000;
+
+	return 0;
+}
+
+static int exynos4_get_crit_temp(struct thermal_zone_device *thermal,
+				 unsigned long *temp)
+{
+	/*Panic zone*/
+	*temp = th_zone->sensor_conf->trip_data.trip_val[2];
+	/*convert the temperature into millicelsius*/
+	*temp = *temp * 1000;
+	return 0;
+}
+
+static int exynos4_bind(struct thermal_zone_device *thermal,
+			struct thermal_cooling_device *cdev)
+{
+	/* if the cooling device is the one from exynos4 bind it */
+	if (cdev != th_zone->cool_dev[0])
+		return 0;
+
+	if (thermal_zone_bind_cooling_device(thermal, 0, cdev)) {
+		pr_err("error binding cooling dev\n");
+		return -EINVAL;
+	}
+	if (thermal_zone_bind_cooling_device(thermal, 1, cdev)) {
+		pr_err("error binding cooling dev\n");
+		return -EINVAL;
+	}
+
+	return 0;
+
+}
+
+static int exynos4_unbind(struct thermal_zone_device *thermal,
+			  struct thermal_cooling_device *cdev)
+{
+	if (cdev != th_zone->cool_dev[0])
+		return 0;
+
+	if (thermal_zone_unbind_cooling_device(thermal, 0, cdev)) {
+		pr_err("error unbinding cooling dev\n");
+		return -EINVAL;
+	}
+	if (thermal_zone_unbind_cooling_device(thermal, 1, cdev)) {
+		pr_err("error unbinding cooling dev\n");
+		return -EINVAL;
+	}
+	return 0;
+
+}
+
+static int exynos4_get_temp(struct thermal_zone_device *thermal,
+			       unsigned long *temp)
+{
+	void *data;
+
+	if (!th_zone->sensor_conf) {
+		pr_info("Temperature sensor not initialised\n");
+		return -EINVAL;
+	}
+	data = th_zone->sensor_conf->private_data;
+	*temp = th_zone->sensor_conf->read_temperature(data);
+	/*convert the temperature into millicelsius*/
+	*temp = *temp * 1000;
+	return 0;
+}
+
+/* bind callback functions to thermalzone */
+static struct thermal_zone_device_ops exynos4_dev_ops = {
+	.bind = exynos4_bind,
+	.unbind = exynos4_unbind,
+	.get_temp = exynos4_get_temp,
+	.get_mode = exynos4_get_mode,
+	.set_mode = exynos4_set_mode,
+	.get_trip_type = exynos4_get_trip_type,
+	.get_trip_temp = exynos4_get_trip_temp,
+	.get_crit_temp = exynos4_get_crit_temp,
+};
+
+int exynos4_register_thermal(struct thermal_sensor_conf *sensor_conf)
+{
+	int ret, count, tab_size;
+	struct freq_pctg_table *tab_ptr;
+
+	if (!sensor_conf || !sensor_conf->read_temperature) {
+		pr_err("Temperature sensor not initialised\n");
+		return -EINVAL;
+	}
+
+	th_zone = kzalloc(sizeof(struct exynos4_thermal_zone), GFP_KERNEL);
+	if (!th_zone) {
+		ret = -ENOMEM;
+		goto err_unregister;
+	}
+
+	th_zone->sensor_conf = sensor_conf;
+
+	tab_ptr = (struct freq_pctg_table *)sensor_conf->cooling_data.freq_data;
+	tab_size = sensor_conf->cooling_data.freq_pctg_count;
+
+	/*Register the cpufreq cooling device*/
+	th_zone->cool_dev_size = 1;
+	count = 0;
+	th_zone->cool_dev[count] = cpufreq_cooling_register(
+			(struct freq_pctg_table *)&(tab_ptr[count]),
+			tab_size, cpumask_of(0));
+
+	if (IS_ERR(th_zone->cool_dev[count])) {
+		pr_err("Failed to register cpufreq cooling device\n");
+		ret = -EINVAL;
+		th_zone->cool_dev_size = 0;
+		goto err_unregister;
+	}
+
+	th_zone->therm_dev = thermal_zone_device_register(sensor_conf->name,
+				3, NULL, &exynos4_dev_ops, 0, 0, 0, 1000);
+	if (IS_ERR(th_zone->therm_dev)) {
+		pr_err("Failed to register thermal zone device\n");
+		ret = -EINVAL;
+		goto err_unregister;
+	}
+
+	th_zone->active_interval = 1;
+	th_zone->idle_interval = 10;
+
+	exynos4_set_mode(th_zone->therm_dev, THERMAL_DEVICE_DISABLED);
+
+	pr_info("Exynos: Kernel Thermal management registered\n");
+
+	return 0;
+
+err_unregister:
+	exynos4_unregister_thermal();
+	return ret;
+}
+EXPORT_SYMBOL(exynos4_register_thermal);
+
+void exynos4_unregister_thermal(void)
+{
+	unsigned int i;
+
+	for (i = 0; i < th_zone->cool_dev_size; i++) {
+		if (th_zone && th_zone->cool_dev[i])
+			cpufreq_cooling_unregister(th_zone->cool_dev[i]);
+	}
+
+	if (th_zone && th_zone->therm_dev)
+		thermal_zone_device_unregister(th_zone->therm_dev);
+
+	kfree(th_zone);
+
+	pr_info("Exynos: Kernel Thermal management unregistered\n");
+}
+EXPORT_SYMBOL(exynos4_unregister_thermal);
diff --git a/include/linux/exynos_thermal.h b/include/linux/exynos_thermal.h
new file mode 100644
index 0000000..186e409
--- /dev/null
+++ b/include/linux/exynos_thermal.h
@@ -0,0 +1,72 @@
+/* linux/include/linux/exynos_thermal.h
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef THERMAL_INTERFACE_H
+#define THERMAL_INTERFACE_H
+/* CPU Zone information */
+
+#define SENSOR_NAME_LEN	16
+#define MAX_TRIP_COUNT	8
+
+#define PANIC_ZONE      4
+#define WARN_ZONE       3
+#define MONITOR_ZONE    2
+#define SAFE_ZONE       1
+#define NO_ACTION       0
+
+
+struct	thermal_trip_point_conf {
+	int trip_val[MAX_TRIP_COUNT];
+	int trip_count;
+};
+
+struct	thermal_cooling_conf {
+	struct freq_pctg_table freq_data[MAX_TRIP_COUNT];
+	int freq_pctg_count;
+};
+
+/**
+ * struct exynos4_tmu_platform_data
+ * @name: name of the temperature sensor
+ * @read_temperature: A function pointer to read temperature info
+ * @private_data: Temperature sensor private data
+ * @sensor_data: Sensor specific information like trigger temperature, level
+ */
+struct thermal_sensor_conf {
+	char	name[SENSOR_NAME_LEN];
+	int	(*read_temperature)(void *data);
+	struct	thermal_trip_point_conf trip_data;
+	struct	thermal_cooling_conf cooling_data;
+	void	*private_data;
+};
+
+/**
+ * exynos4_register_thermal: Register to the exynos thermal interface.
+ * @sensor_conf:   Structure containing temperature sensor information
+ *
+ * returns zero on success, else negative errno.
+ */
+int exynos4_register_thermal(struct thermal_sensor_conf *sensor_conf);
+
+/**
+ * exynos4_unregister_thermal: Un-register from the exynos thermal interface.
+ *
+ * return not applicable.
+ */
+void exynos4_unregister_thermal(void);
+
+/**
+ * exynos4_report_trigger: Report any trigger level crossed in the
+ *	temperature sensor. This may be useful to take any cooling action.
+ *
+ * return not applicable.
+ */
+extern void exynos4_report_trigger(void);
+#endif
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH 2/4] hwmon: exynos4: Move thermal sensor driver to driver/mfd directory
  2012-03-03 11:06 ` Amit Daniel Kachhap
  (?)
@ 2012-03-03 11:06   ` Amit Daniel Kachhap
  -1 siblings, 0 replies; 43+ messages in thread
From: Amit Daniel Kachhap @ 2012-03-03 11:06 UTC (permalink / raw)
  To: linux-pm, linux-samsung-soc
  Cc: linaro-dev, patches, linux-kernel, lm-sensors, linux-acpi

This movement is needed because the hwmon entries and corresponding
sysfs interface is a duplicate of utilities already provided by
driver/thermal/thermal_sys.c. The goal is to place it in mfd folder
and add necessary calls to get the temperature information.

Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>
Signed-off-by: Donggeun Kim <dg77.kim@samsung.com>
---
 Documentation/hwmon/exynos4_tmu |   81 ------
 Documentation/mfd/exynos4_tmu   |   81 ++++++
 drivers/hwmon/Kconfig           |   10 -
 drivers/hwmon/Makefile          |    1 -
 drivers/hwmon/exynos4_tmu.c     |  514 ---------------------------------------
 drivers/mfd/Kconfig             |   10 +
 drivers/mfd/Makefile            |    1 +
 drivers/mfd/exynos4_tmu.c       |  514 +++++++++++++++++++++++++++++++++++++++
 8 files changed, 606 insertions(+), 606 deletions(-)
 delete mode 100644 Documentation/hwmon/exynos4_tmu
 create mode 100644 Documentation/mfd/exynos4_tmu
 delete mode 100644 drivers/hwmon/exynos4_tmu.c
 create mode 100644 drivers/mfd/exynos4_tmu.c

diff --git a/Documentation/hwmon/exynos4_tmu b/Documentation/hwmon/exynos4_tmu
deleted file mode 100644
index c3c6b41..0000000
--- a/Documentation/hwmon/exynos4_tmu
+++ /dev/null
@@ -1,81 +0,0 @@
-Kernel driver exynos4_tmu
-=================
-
-Supported chips:
-* ARM SAMSUNG EXYNOS4 series of SoC
-  Prefix: 'exynos4-tmu'
-  Datasheet: Not publicly available
-
-Authors: Donggeun Kim <dg77.kim@samsung.com>
-
-Description
------------
-
-This driver allows to read temperature inside SAMSUNG EXYNOS4 series of SoC.
-
-The chip only exposes the measured 8-bit temperature code value
-through a register.
-Temperature can be taken from the temperature code.
-There are three equations converting from temperature to temperature code.
-
-The three equations are:
-  1. Two point trimming
-	Tc = (T - 25) * (TI2 - TI1) / (85 - 25) + TI1
-
-  2. One point trimming
-	Tc = T + TI1 - 25
-
-  3. No trimming
-	Tc = T + 50
-
-  Tc: Temperature code, T: Temperature,
-  TI1: Trimming info for 25 degree Celsius (stored at TRIMINFO register)
-       Temperature code measured at 25 degree Celsius which is unchanged
-  TI2: Trimming info for 85 degree Celsius (stored at TRIMINFO register)
-       Temperature code measured at 85 degree Celsius which is unchanged
-
-TMU(Thermal Management Unit) in EXYNOS4 generates interrupt
-when temperature exceeds pre-defined levels.
-The maximum number of configurable threshold is four.
-The threshold levels are defined as follows:
-  Level_0: current temperature > trigger_level_0 + threshold
-  Level_1: current temperature > trigger_level_1 + threshold
-  Level_2: current temperature > trigger_level_2 + threshold
-  Level_3: current temperature > trigger_level_3 + threshold
-
-  The threshold and each trigger_level are set
-  through the corresponding registers.
-
-When an interrupt occurs, this driver notify user space of
-one of four threshold levels for the interrupt
-through kobject_uevent_env and sysfs_notify functions.
-Although an interrupt condition for level_0 can be set,
-it is not notified to user space through sysfs_notify function.
-
-Sysfs Interface
----------------
-name		name of the temperature sensor
-		RO
-
-temp1_input	temperature
-		RO
-
-temp1_max	temperature for level_1 interrupt
-		RO
-
-temp1_crit	temperature for level_2 interrupt
-		RO
-
-temp1_emergency	temperature for level_3 interrupt
-		RO
-
-temp1_max_alarm	alarm for level_1 interrupt
-		RO
-
-temp1_crit_alarm
-		alarm for level_2 interrupt
-		RO
-
-temp1_emergency_alarm
-		alarm for level_3 interrupt
-		RO
diff --git a/Documentation/mfd/exynos4_tmu b/Documentation/mfd/exynos4_tmu
new file mode 100644
index 0000000..c3c6b41
--- /dev/null
+++ b/Documentation/mfd/exynos4_tmu
@@ -0,0 +1,81 @@
+Kernel driver exynos4_tmu
+=================
+
+Supported chips:
+* ARM SAMSUNG EXYNOS4 series of SoC
+  Prefix: 'exynos4-tmu'
+  Datasheet: Not publicly available
+
+Authors: Donggeun Kim <dg77.kim@samsung.com>
+
+Description
+-----------
+
+This driver allows to read temperature inside SAMSUNG EXYNOS4 series of SoC.
+
+The chip only exposes the measured 8-bit temperature code value
+through a register.
+Temperature can be taken from the temperature code.
+There are three equations converting from temperature to temperature code.
+
+The three equations are:
+  1. Two point trimming
+	Tc = (T - 25) * (TI2 - TI1) / (85 - 25) + TI1
+
+  2. One point trimming
+	Tc = T + TI1 - 25
+
+  3. No trimming
+	Tc = T + 50
+
+  Tc: Temperature code, T: Temperature,
+  TI1: Trimming info for 25 degree Celsius (stored at TRIMINFO register)
+       Temperature code measured at 25 degree Celsius which is unchanged
+  TI2: Trimming info for 85 degree Celsius (stored at TRIMINFO register)
+       Temperature code measured at 85 degree Celsius which is unchanged
+
+TMU(Thermal Management Unit) in EXYNOS4 generates interrupt
+when temperature exceeds pre-defined levels.
+The maximum number of configurable threshold is four.
+The threshold levels are defined as follows:
+  Level_0: current temperature > trigger_level_0 + threshold
+  Level_1: current temperature > trigger_level_1 + threshold
+  Level_2: current temperature > trigger_level_2 + threshold
+  Level_3: current temperature > trigger_level_3 + threshold
+
+  The threshold and each trigger_level are set
+  through the corresponding registers.
+
+When an interrupt occurs, this driver notify user space of
+one of four threshold levels for the interrupt
+through kobject_uevent_env and sysfs_notify functions.
+Although an interrupt condition for level_0 can be set,
+it is not notified to user space through sysfs_notify function.
+
+Sysfs Interface
+---------------
+name		name of the temperature sensor
+		RO
+
+temp1_input	temperature
+		RO
+
+temp1_max	temperature for level_1 interrupt
+		RO
+
+temp1_crit	temperature for level_2 interrupt
+		RO
+
+temp1_emergency	temperature for level_3 interrupt
+		RO
+
+temp1_max_alarm	alarm for level_1 interrupt
+		RO
+
+temp1_crit_alarm
+		alarm for level_2 interrupt
+		RO
+
+temp1_emergency_alarm
+		alarm for level_3 interrupt
+		RO
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 0226040..5c4a0fb 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -313,16 +313,6 @@ config SENSORS_DS1621
 	  This driver can also be built as a module.  If so, the module
 	  will be called ds1621.
 
-config SENSORS_EXYNOS4_TMU
-	tristate "Temperature sensor on Samsung EXYNOS4"
-	depends on ARCH_EXYNOS4
-	help
-	  If you say yes here you get support for TMU (Thermal Managment
-	  Unit) on SAMSUNG EXYNOS4 series of SoC.
-
-	  This driver can also be built as a module. If so, the module
-	  will be called exynos4-tmu.
-
 config SENSORS_I5K_AMB
 	tristate "FB-DIMM AMB temperature sensor on Intel 5000 series chipsets"
 	depends on PCI && EXPERIMENTAL
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 8251ce8..228a4b7 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -48,7 +48,6 @@ obj-$(CONFIG_SENSORS_DS1621)	+= ds1621.o
 obj-$(CONFIG_SENSORS_EMC1403)	+= emc1403.o
 obj-$(CONFIG_SENSORS_EMC2103)	+= emc2103.o
 obj-$(CONFIG_SENSORS_EMC6W201)	+= emc6w201.o
-obj-$(CONFIG_SENSORS_EXYNOS4_TMU)	+= exynos4_tmu.o
 obj-$(CONFIG_SENSORS_F71805F)	+= f71805f.o
 obj-$(CONFIG_SENSORS_F71882FG)	+= f71882fg.o
 obj-$(CONFIG_SENSORS_F75375S)	+= f75375s.o
diff --git a/drivers/hwmon/exynos4_tmu.c b/drivers/hwmon/exynos4_tmu.c
deleted file mode 100644
index f2359a0..0000000
--- a/drivers/hwmon/exynos4_tmu.c
+++ /dev/null
@@ -1,514 +0,0 @@
-/*
- * exynos4_tmu.c - Samsung EXYNOS4 TMU (Thermal Management Unit)
- *
- *  Copyright (C) 2011 Samsung Electronics
- *  Donggeun Kim <dg77.kim@samsung.com>
- *
- * 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/hwmon.h>
-#include <linux/hwmon-sysfs.h>
-
-#include <linux/platform_data/exynos4_tmu.h>
-
-#define EXYNOS4_TMU_REG_TRIMINFO	0x0
-#define EXYNOS4_TMU_REG_CONTROL		0x20
-#define EXYNOS4_TMU_REG_STATUS		0x28
-#define EXYNOS4_TMU_REG_CURRENT_TEMP	0x40
-#define EXYNOS4_TMU_REG_THRESHOLD_TEMP	0x44
-#define EXYNOS4_TMU_REG_TRIG_LEVEL0	0x50
-#define EXYNOS4_TMU_REG_TRIG_LEVEL1	0x54
-#define EXYNOS4_TMU_REG_TRIG_LEVEL2	0x58
-#define EXYNOS4_TMU_REG_TRIG_LEVEL3	0x5C
-#define EXYNOS4_TMU_REG_PAST_TEMP0	0x60
-#define EXYNOS4_TMU_REG_PAST_TEMP1	0x64
-#define EXYNOS4_TMU_REG_PAST_TEMP2	0x68
-#define EXYNOS4_TMU_REG_PAST_TEMP3	0x6C
-#define EXYNOS4_TMU_REG_INTEN		0x70
-#define EXYNOS4_TMU_REG_INTSTAT		0x74
-#define EXYNOS4_TMU_REG_INTCLEAR	0x78
-
-#define EXYNOS4_TMU_GAIN_SHIFT		8
-#define EXYNOS4_TMU_REF_VOLTAGE_SHIFT	24
-
-#define EXYNOS4_TMU_TRIM_TEMP_MASK	0xff
-#define EXYNOS4_TMU_CORE_ON	3
-#define EXYNOS4_TMU_CORE_OFF	2
-#define EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET	50
-#define EXYNOS4_TMU_TRIG_LEVEL0_MASK	0x1
-#define EXYNOS4_TMU_TRIG_LEVEL1_MASK	0x10
-#define EXYNOS4_TMU_TRIG_LEVEL2_MASK	0x100
-#define EXYNOS4_TMU_TRIG_LEVEL3_MASK	0x1000
-#define EXYNOS4_TMU_INTCLEAR_VAL	0x1111
-
-struct exynos4_tmu_data {
-	struct exynos4_tmu_platform_data *pdata;
-	struct device *hwmon_dev;
-	struct resource *mem;
-	void __iomem *base;
-	int irq;
-	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 exynos4_tmu_data *data, u8 temp)
-{
-	struct exynos4_tmu_platform_data *pdata = data->pdata;
-	int temp_code;
-
-	/* 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 + EXYNOS4_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 exynos4_tmu_data *data, u8 temp_code)
-{
-	struct exynos4_tmu_platform_data *pdata = data->pdata;
-	int temp;
-
-	/* 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 - EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET;
-		break;
-	}
-out:
-	return temp;
-}
-
-static int exynos4_tmu_initialize(struct platform_device *pdev)
-{
-	struct exynos4_tmu_data *data = platform_get_drvdata(pdev);
-	struct exynos4_tmu_platform_data *pdata = data->pdata;
-	unsigned int status, trim_info;
-	int ret = 0, threshold_code;
-
-	mutex_lock(&data->lock);
-	clk_enable(data->clk);
-
-	status = readb(data->base + EXYNOS4_TMU_REG_STATUS);
-	if (!status) {
-		ret = -EBUSY;
-		goto out;
-	}
-
-	/* Save trimming info in order to perform calibration */
-	trim_info = readl(data->base + EXYNOS4_TMU_REG_TRIMINFO);
-	data->temp_error1 = trim_info & EXYNOS4_TMU_TRIM_TEMP_MASK;
-	data->temp_error2 = ((trim_info >> 8) & EXYNOS4_TMU_TRIM_TEMP_MASK);
-
-	/* 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 + EXYNOS4_TMU_REG_THRESHOLD_TEMP);
-
-	writeb(pdata->trigger_levels[0],
-		data->base + EXYNOS4_TMU_REG_TRIG_LEVEL0);
-	writeb(pdata->trigger_levels[1],
-		data->base + EXYNOS4_TMU_REG_TRIG_LEVEL1);
-	writeb(pdata->trigger_levels[2],
-		data->base + EXYNOS4_TMU_REG_TRIG_LEVEL2);
-	writeb(pdata->trigger_levels[3],
-		data->base + EXYNOS4_TMU_REG_TRIG_LEVEL3);
-
-	writel(EXYNOS4_TMU_INTCLEAR_VAL,
-		data->base + EXYNOS4_TMU_REG_INTCLEAR);
-out:
-	clk_disable(data->clk);
-	mutex_unlock(&data->lock);
-
-	return ret;
-}
-
-static void exynos4_tmu_control(struct platform_device *pdev, bool on)
-{
-	struct exynos4_tmu_data *data = platform_get_drvdata(pdev);
-	struct exynos4_tmu_platform_data *pdata = data->pdata;
-	unsigned int con, interrupt_en;
-
-	mutex_lock(&data->lock);
-	clk_enable(data->clk);
-
-	con = pdata->reference_voltage << EXYNOS4_TMU_REF_VOLTAGE_SHIFT |
-		pdata->gain << EXYNOS4_TMU_GAIN_SHIFT;
-	if (on) {
-		con |= EXYNOS4_TMU_CORE_ON;
-		interrupt_en = pdata->trigger_level3_en << 12 |
-			pdata->trigger_level2_en << 8 |
-			pdata->trigger_level1_en << 4 |
-			pdata->trigger_level0_en;
-	} else {
-		con |= EXYNOS4_TMU_CORE_OFF;
-		interrupt_en = 0; /* Disable all interrupts */
-	}
-	writel(interrupt_en, data->base + EXYNOS4_TMU_REG_INTEN);
-	writel(con, data->base + EXYNOS4_TMU_REG_CONTROL);
-
-	clk_disable(data->clk);
-	mutex_unlock(&data->lock);
-}
-
-static int exynos4_tmu_read(struct exynos4_tmu_data *data)
-{
-	u8 temp_code;
-	int temp;
-
-	mutex_lock(&data->lock);
-	clk_enable(data->clk);
-
-	temp_code = readb(data->base + EXYNOS4_TMU_REG_CURRENT_TEMP);
-	temp = code_to_temp(data, temp_code);
-
-	clk_disable(data->clk);
-	mutex_unlock(&data->lock);
-
-	return temp;
-}
-
-static void exynos4_tmu_work(struct work_struct *work)
-{
-	struct exynos4_tmu_data *data = container_of(work,
-			struct exynos4_tmu_data, irq_work);
-
-	mutex_lock(&data->lock);
-	clk_enable(data->clk);
-
-	writel(EXYNOS4_TMU_INTCLEAR_VAL, data->base + EXYNOS4_TMU_REG_INTCLEAR);
-
-	kobject_uevent(&data->hwmon_dev->kobj, KOBJ_CHANGE);
-
-	enable_irq(data->irq);
-
-	clk_disable(data->clk);
-	mutex_unlock(&data->lock);
-}
-
-static irqreturn_t exynos4_tmu_irq(int irq, void *id)
-{
-	struct exynos4_tmu_data *data = id;
-
-	disable_irq_nosync(irq);
-	schedule_work(&data->irq_work);
-
-	return IRQ_HANDLED;
-}
-
-static ssize_t exynos4_tmu_show_name(struct device *dev,
-		struct device_attribute *attr, char *buf)
-{
-	return sprintf(buf, "exynos4-tmu\n");
-}
-
-static ssize_t exynos4_tmu_show_temp(struct device *dev,
-		struct device_attribute *attr, char *buf)
-{
-	struct exynos4_tmu_data *data = dev_get_drvdata(dev);
-	int ret;
-
-	ret = exynos4_tmu_read(data);
-	if (ret < 0)
-		return ret;
-
-	/* convert from degree Celsius to millidegree Celsius */
-	return sprintf(buf, "%d\n", ret * 1000);
-}
-
-static ssize_t exynos4_tmu_show_alarm(struct device *dev,
-		struct device_attribute *devattr, char *buf)
-{
-	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-	struct exynos4_tmu_data *data = dev_get_drvdata(dev);
-	struct exynos4_tmu_platform_data *pdata = data->pdata;
-	int temp;
-	unsigned int trigger_level;
-
-	temp = exynos4_tmu_read(data);
-	if (temp < 0)
-		return temp;
-
-	trigger_level = pdata->threshold + pdata->trigger_levels[attr->index];
-
-	return sprintf(buf, "%d\n", !!(temp > trigger_level));
-}
-
-static ssize_t exynos4_tmu_show_level(struct device *dev,
-		struct device_attribute *devattr, char *buf)
-{
-	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-	struct exynos4_tmu_data *data = dev_get_drvdata(dev);
-	struct exynos4_tmu_platform_data *pdata = data->pdata;
-	unsigned int temp = pdata->threshold +
-			pdata->trigger_levels[attr->index];
-
-	return sprintf(buf, "%u\n", temp * 1000);
-}
-
-static DEVICE_ATTR(name, S_IRUGO, exynos4_tmu_show_name, NULL);
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, exynos4_tmu_show_temp, NULL, 0);
-
-static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO,
-		exynos4_tmu_show_alarm, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO,
-		exynos4_tmu_show_alarm, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp1_emergency_alarm, S_IRUGO,
-		exynos4_tmu_show_alarm, NULL, 3);
-
-static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, exynos4_tmu_show_level, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, exynos4_tmu_show_level, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp1_emergency, S_IRUGO,
-		exynos4_tmu_show_level, NULL, 3);
-
-static struct attribute *exynos4_tmu_attributes[] = {
-	&dev_attr_name.attr,
-	&sensor_dev_attr_temp1_input.dev_attr.attr,
-	&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
-	&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
-	&sensor_dev_attr_temp1_emergency_alarm.dev_attr.attr,
-	&sensor_dev_attr_temp1_max.dev_attr.attr,
-	&sensor_dev_attr_temp1_crit.dev_attr.attr,
-	&sensor_dev_attr_temp1_emergency.dev_attr.attr,
-	NULL,
-};
-
-static const struct attribute_group exynos4_tmu_attr_group = {
-	.attrs = exynos4_tmu_attributes,
-};
-
-static int __devinit exynos4_tmu_probe(struct platform_device *pdev)
-{
-	struct exynos4_tmu_data *data;
-	struct exynos4_tmu_platform_data *pdata = pdev->dev.platform_data;
-	int ret;
-
-	if (!pdata) {
-		dev_err(&pdev->dev, "No platform init data supplied.\n");
-		return -ENODEV;
-	}
-
-	data = kzalloc(sizeof(struct exynos4_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) {
-		ret = data->irq;
-		dev_err(&pdev->dev, "Failed to get platform irq\n");
-		goto err_free;
-	}
-
-	INIT_WORK(&data->irq_work, exynos4_tmu_work);
-
-	data->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!data->mem) {
-		ret = -ENOENT;
-		dev_err(&pdev->dev, "Failed to get platform resource\n");
-		goto err_free;
-	}
-
-	data->mem = request_mem_region(data->mem->start,
-			resource_size(data->mem), pdev->name);
-	if (!data->mem) {
-		ret = -ENODEV;
-		dev_err(&pdev->dev, "Failed to request memory region\n");
-		goto err_free;
-	}
-
-	data->base = ioremap(data->mem->start, resource_size(data->mem));
-	if (!data->base) {
-		ret = -ENODEV;
-		dev_err(&pdev->dev, "Failed to ioremap memory\n");
-		goto err_mem_region;
-	}
-
-	ret = request_irq(data->irq, exynos4_tmu_irq,
-		IRQF_TRIGGER_RISING,
-		"exynos4-tmu", data);
-	if (ret) {
-		dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq);
-		goto err_io_remap;
-	}
-
-	data->clk = clk_get(NULL, "tmu_apbif");
-	if (IS_ERR(data->clk)) {
-		ret = PTR_ERR(data->clk);
-		dev_err(&pdev->dev, "Failed to get clock\n");
-		goto err_irq;
-	}
-
-	data->pdata = pdata;
-	platform_set_drvdata(pdev, data);
-	mutex_init(&data->lock);
-
-	ret = exynos4_tmu_initialize(pdev);
-	if (ret) {
-		dev_err(&pdev->dev, "Failed to initialize TMU\n");
-		goto err_clk;
-	}
-
-	ret = sysfs_create_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
-	if (ret) {
-		dev_err(&pdev->dev, "Failed to create sysfs group\n");
-		goto err_clk;
-	}
-
-	data->hwmon_dev = hwmon_device_register(&pdev->dev);
-	if (IS_ERR(data->hwmon_dev)) {
-		ret = PTR_ERR(data->hwmon_dev);
-		dev_err(&pdev->dev, "Failed to register hwmon device\n");
-		goto err_create_group;
-	}
-
-	exynos4_tmu_control(pdev, true);
-
-	return 0;
-
-err_create_group:
-	sysfs_remove_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
-err_clk:
-	platform_set_drvdata(pdev, NULL);
-	clk_put(data->clk);
-err_irq:
-	free_irq(data->irq, data);
-err_io_remap:
-	iounmap(data->base);
-err_mem_region:
-	release_mem_region(data->mem->start, resource_size(data->mem));
-err_free:
-	kfree(data);
-
-	return ret;
-}
-
-static int __devexit exynos4_tmu_remove(struct platform_device *pdev)
-{
-	struct exynos4_tmu_data *data = platform_get_drvdata(pdev);
-
-	exynos4_tmu_control(pdev, false);
-
-	hwmon_device_unregister(data->hwmon_dev);
-	sysfs_remove_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
-
-	clk_put(data->clk);
-
-	free_irq(data->irq, data);
-
-	iounmap(data->base);
-	release_mem_region(data->mem->start, resource_size(data->mem));
-
-	platform_set_drvdata(pdev, NULL);
-
-	kfree(data);
-
-	return 0;
-}
-
-#ifdef CONFIG_PM
-static int exynos4_tmu_suspend(struct platform_device *pdev, pm_message_t state)
-{
-	exynos4_tmu_control(pdev, false);
-
-	return 0;
-}
-
-static int exynos4_tmu_resume(struct platform_device *pdev)
-{
-	exynos4_tmu_initialize(pdev);
-	exynos4_tmu_control(pdev, true);
-
-	return 0;
-}
-#else
-#define exynos4_tmu_suspend NULL
-#define exynos4_tmu_resume NULL
-#endif
-
-static struct platform_driver exynos4_tmu_driver = {
-	.driver = {
-		.name   = "exynos4-tmu",
-		.owner  = THIS_MODULE,
-	},
-	.probe = exynos4_tmu_probe,
-	.remove	= __devexit_p(exynos4_tmu_remove),
-	.suspend = exynos4_tmu_suspend,
-	.resume = exynos4_tmu_resume,
-};
-
-module_platform_driver(exynos4_tmu_driver);
-
-MODULE_DESCRIPTION("EXYNOS4 TMU Driver");
-MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:exynos4-tmu");
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index f147395..e29113e 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -830,6 +830,16 @@ config MFD_INTEL_MSIC
 	  Passage) chip. This chip embeds audio, battery, GPIO, etc.
 	  devices used in Intel Medfield platforms.
 
+config SENSORS_EXYNOS4_TMU
+	tristate "Temperature sensor on Samsung EXYNOS4"
+	depends on ARCH_EXYNOS4
+	help
+	  If you say yes here you get support for TMU (Thermal Managment
+	  Unit) on SAMSUNG EXYNOS4 series of SoC.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called exynos4-tmu.
+
 endmenu
 endif
 
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index b953bab..766bf86 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -112,3 +112,4 @@ obj-$(CONFIG_TPS65911_COMPARATOR)	+= tps65911-comparator.o
 obj-$(CONFIG_MFD_AAT2870_CORE)	+= aat2870-core.o
 obj-$(CONFIG_MFD_INTEL_MSIC)	+= intel_msic.o
 obj-$(CONFIG_MFD_S5M_CORE)	+= s5m-core.o s5m-irq.o
+obj-$(CONFIG_SENSORS_EXYNOS4_TMU)	+= exynos4_tmu.o
diff --git a/drivers/mfd/exynos4_tmu.c b/drivers/mfd/exynos4_tmu.c
new file mode 100644
index 0000000..f2359a0
--- /dev/null
+++ b/drivers/mfd/exynos4_tmu.c
@@ -0,0 +1,514 @@
+/*
+ * exynos4_tmu.c - Samsung EXYNOS4 TMU (Thermal Management Unit)
+ *
+ *  Copyright (C) 2011 Samsung Electronics
+ *  Donggeun Kim <dg77.kim@samsung.com>
+ *
+ * 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/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+
+#include <linux/platform_data/exynos4_tmu.h>
+
+#define EXYNOS4_TMU_REG_TRIMINFO	0x0
+#define EXYNOS4_TMU_REG_CONTROL		0x20
+#define EXYNOS4_TMU_REG_STATUS		0x28
+#define EXYNOS4_TMU_REG_CURRENT_TEMP	0x40
+#define EXYNOS4_TMU_REG_THRESHOLD_TEMP	0x44
+#define EXYNOS4_TMU_REG_TRIG_LEVEL0	0x50
+#define EXYNOS4_TMU_REG_TRIG_LEVEL1	0x54
+#define EXYNOS4_TMU_REG_TRIG_LEVEL2	0x58
+#define EXYNOS4_TMU_REG_TRIG_LEVEL3	0x5C
+#define EXYNOS4_TMU_REG_PAST_TEMP0	0x60
+#define EXYNOS4_TMU_REG_PAST_TEMP1	0x64
+#define EXYNOS4_TMU_REG_PAST_TEMP2	0x68
+#define EXYNOS4_TMU_REG_PAST_TEMP3	0x6C
+#define EXYNOS4_TMU_REG_INTEN		0x70
+#define EXYNOS4_TMU_REG_INTSTAT		0x74
+#define EXYNOS4_TMU_REG_INTCLEAR	0x78
+
+#define EXYNOS4_TMU_GAIN_SHIFT		8
+#define EXYNOS4_TMU_REF_VOLTAGE_SHIFT	24
+
+#define EXYNOS4_TMU_TRIM_TEMP_MASK	0xff
+#define EXYNOS4_TMU_CORE_ON	3
+#define EXYNOS4_TMU_CORE_OFF	2
+#define EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET	50
+#define EXYNOS4_TMU_TRIG_LEVEL0_MASK	0x1
+#define EXYNOS4_TMU_TRIG_LEVEL1_MASK	0x10
+#define EXYNOS4_TMU_TRIG_LEVEL2_MASK	0x100
+#define EXYNOS4_TMU_TRIG_LEVEL3_MASK	0x1000
+#define EXYNOS4_TMU_INTCLEAR_VAL	0x1111
+
+struct exynos4_tmu_data {
+	struct exynos4_tmu_platform_data *pdata;
+	struct device *hwmon_dev;
+	struct resource *mem;
+	void __iomem *base;
+	int irq;
+	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 exynos4_tmu_data *data, u8 temp)
+{
+	struct exynos4_tmu_platform_data *pdata = data->pdata;
+	int temp_code;
+
+	/* 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 + EXYNOS4_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 exynos4_tmu_data *data, u8 temp_code)
+{
+	struct exynos4_tmu_platform_data *pdata = data->pdata;
+	int temp;
+
+	/* 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 - EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET;
+		break;
+	}
+out:
+	return temp;
+}
+
+static int exynos4_tmu_initialize(struct platform_device *pdev)
+{
+	struct exynos4_tmu_data *data = platform_get_drvdata(pdev);
+	struct exynos4_tmu_platform_data *pdata = data->pdata;
+	unsigned int status, trim_info;
+	int ret = 0, threshold_code;
+
+	mutex_lock(&data->lock);
+	clk_enable(data->clk);
+
+	status = readb(data->base + EXYNOS4_TMU_REG_STATUS);
+	if (!status) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	/* Save trimming info in order to perform calibration */
+	trim_info = readl(data->base + EXYNOS4_TMU_REG_TRIMINFO);
+	data->temp_error1 = trim_info & EXYNOS4_TMU_TRIM_TEMP_MASK;
+	data->temp_error2 = ((trim_info >> 8) & EXYNOS4_TMU_TRIM_TEMP_MASK);
+
+	/* 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 + EXYNOS4_TMU_REG_THRESHOLD_TEMP);
+
+	writeb(pdata->trigger_levels[0],
+		data->base + EXYNOS4_TMU_REG_TRIG_LEVEL0);
+	writeb(pdata->trigger_levels[1],
+		data->base + EXYNOS4_TMU_REG_TRIG_LEVEL1);
+	writeb(pdata->trigger_levels[2],
+		data->base + EXYNOS4_TMU_REG_TRIG_LEVEL2);
+	writeb(pdata->trigger_levels[3],
+		data->base + EXYNOS4_TMU_REG_TRIG_LEVEL3);
+
+	writel(EXYNOS4_TMU_INTCLEAR_VAL,
+		data->base + EXYNOS4_TMU_REG_INTCLEAR);
+out:
+	clk_disable(data->clk);
+	mutex_unlock(&data->lock);
+
+	return ret;
+}
+
+static void exynos4_tmu_control(struct platform_device *pdev, bool on)
+{
+	struct exynos4_tmu_data *data = platform_get_drvdata(pdev);
+	struct exynos4_tmu_platform_data *pdata = data->pdata;
+	unsigned int con, interrupt_en;
+
+	mutex_lock(&data->lock);
+	clk_enable(data->clk);
+
+	con = pdata->reference_voltage << EXYNOS4_TMU_REF_VOLTAGE_SHIFT |
+		pdata->gain << EXYNOS4_TMU_GAIN_SHIFT;
+	if (on) {
+		con |= EXYNOS4_TMU_CORE_ON;
+		interrupt_en = pdata->trigger_level3_en << 12 |
+			pdata->trigger_level2_en << 8 |
+			pdata->trigger_level1_en << 4 |
+			pdata->trigger_level0_en;
+	} else {
+		con |= EXYNOS4_TMU_CORE_OFF;
+		interrupt_en = 0; /* Disable all interrupts */
+	}
+	writel(interrupt_en, data->base + EXYNOS4_TMU_REG_INTEN);
+	writel(con, data->base + EXYNOS4_TMU_REG_CONTROL);
+
+	clk_disable(data->clk);
+	mutex_unlock(&data->lock);
+}
+
+static int exynos4_tmu_read(struct exynos4_tmu_data *data)
+{
+	u8 temp_code;
+	int temp;
+
+	mutex_lock(&data->lock);
+	clk_enable(data->clk);
+
+	temp_code = readb(data->base + EXYNOS4_TMU_REG_CURRENT_TEMP);
+	temp = code_to_temp(data, temp_code);
+
+	clk_disable(data->clk);
+	mutex_unlock(&data->lock);
+
+	return temp;
+}
+
+static void exynos4_tmu_work(struct work_struct *work)
+{
+	struct exynos4_tmu_data *data = container_of(work,
+			struct exynos4_tmu_data, irq_work);
+
+	mutex_lock(&data->lock);
+	clk_enable(data->clk);
+
+	writel(EXYNOS4_TMU_INTCLEAR_VAL, data->base + EXYNOS4_TMU_REG_INTCLEAR);
+
+	kobject_uevent(&data->hwmon_dev->kobj, KOBJ_CHANGE);
+
+	enable_irq(data->irq);
+
+	clk_disable(data->clk);
+	mutex_unlock(&data->lock);
+}
+
+static irqreturn_t exynos4_tmu_irq(int irq, void *id)
+{
+	struct exynos4_tmu_data *data = id;
+
+	disable_irq_nosync(irq);
+	schedule_work(&data->irq_work);
+
+	return IRQ_HANDLED;
+}
+
+static ssize_t exynos4_tmu_show_name(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "exynos4-tmu\n");
+}
+
+static ssize_t exynos4_tmu_show_temp(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct exynos4_tmu_data *data = dev_get_drvdata(dev);
+	int ret;
+
+	ret = exynos4_tmu_read(data);
+	if (ret < 0)
+		return ret;
+
+	/* convert from degree Celsius to millidegree Celsius */
+	return sprintf(buf, "%d\n", ret * 1000);
+}
+
+static ssize_t exynos4_tmu_show_alarm(struct device *dev,
+		struct device_attribute *devattr, char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	struct exynos4_tmu_data *data = dev_get_drvdata(dev);
+	struct exynos4_tmu_platform_data *pdata = data->pdata;
+	int temp;
+	unsigned int trigger_level;
+
+	temp = exynos4_tmu_read(data);
+	if (temp < 0)
+		return temp;
+
+	trigger_level = pdata->threshold + pdata->trigger_levels[attr->index];
+
+	return sprintf(buf, "%d\n", !!(temp > trigger_level));
+}
+
+static ssize_t exynos4_tmu_show_level(struct device *dev,
+		struct device_attribute *devattr, char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	struct exynos4_tmu_data *data = dev_get_drvdata(dev);
+	struct exynos4_tmu_platform_data *pdata = data->pdata;
+	unsigned int temp = pdata->threshold +
+			pdata->trigger_levels[attr->index];
+
+	return sprintf(buf, "%u\n", temp * 1000);
+}
+
+static DEVICE_ATTR(name, S_IRUGO, exynos4_tmu_show_name, NULL);
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, exynos4_tmu_show_temp, NULL, 0);
+
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO,
+		exynos4_tmu_show_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO,
+		exynos4_tmu_show_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp1_emergency_alarm, S_IRUGO,
+		exynos4_tmu_show_alarm, NULL, 3);
+
+static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, exynos4_tmu_show_level, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, exynos4_tmu_show_level, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp1_emergency, S_IRUGO,
+		exynos4_tmu_show_level, NULL, 3);
+
+static struct attribute *exynos4_tmu_attributes[] = {
+	&dev_attr_name.attr,
+	&sensor_dev_attr_temp1_input.dev_attr.attr,
+	&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+	&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
+	&sensor_dev_attr_temp1_emergency_alarm.dev_attr.attr,
+	&sensor_dev_attr_temp1_max.dev_attr.attr,
+	&sensor_dev_attr_temp1_crit.dev_attr.attr,
+	&sensor_dev_attr_temp1_emergency.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group exynos4_tmu_attr_group = {
+	.attrs = exynos4_tmu_attributes,
+};
+
+static int __devinit exynos4_tmu_probe(struct platform_device *pdev)
+{
+	struct exynos4_tmu_data *data;
+	struct exynos4_tmu_platform_data *pdata = pdev->dev.platform_data;
+	int ret;
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "No platform init data supplied.\n");
+		return -ENODEV;
+	}
+
+	data = kzalloc(sizeof(struct exynos4_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) {
+		ret = data->irq;
+		dev_err(&pdev->dev, "Failed to get platform irq\n");
+		goto err_free;
+	}
+
+	INIT_WORK(&data->irq_work, exynos4_tmu_work);
+
+	data->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!data->mem) {
+		ret = -ENOENT;
+		dev_err(&pdev->dev, "Failed to get platform resource\n");
+		goto err_free;
+	}
+
+	data->mem = request_mem_region(data->mem->start,
+			resource_size(data->mem), pdev->name);
+	if (!data->mem) {
+		ret = -ENODEV;
+		dev_err(&pdev->dev, "Failed to request memory region\n");
+		goto err_free;
+	}
+
+	data->base = ioremap(data->mem->start, resource_size(data->mem));
+	if (!data->base) {
+		ret = -ENODEV;
+		dev_err(&pdev->dev, "Failed to ioremap memory\n");
+		goto err_mem_region;
+	}
+
+	ret = request_irq(data->irq, exynos4_tmu_irq,
+		IRQF_TRIGGER_RISING,
+		"exynos4-tmu", data);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq);
+		goto err_io_remap;
+	}
+
+	data->clk = clk_get(NULL, "tmu_apbif");
+	if (IS_ERR(data->clk)) {
+		ret = PTR_ERR(data->clk);
+		dev_err(&pdev->dev, "Failed to get clock\n");
+		goto err_irq;
+	}
+
+	data->pdata = pdata;
+	platform_set_drvdata(pdev, data);
+	mutex_init(&data->lock);
+
+	ret = exynos4_tmu_initialize(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to initialize TMU\n");
+		goto err_clk;
+	}
+
+	ret = sysfs_create_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to create sysfs group\n");
+		goto err_clk;
+	}
+
+	data->hwmon_dev = hwmon_device_register(&pdev->dev);
+	if (IS_ERR(data->hwmon_dev)) {
+		ret = PTR_ERR(data->hwmon_dev);
+		dev_err(&pdev->dev, "Failed to register hwmon device\n");
+		goto err_create_group;
+	}
+
+	exynos4_tmu_control(pdev, true);
+
+	return 0;
+
+err_create_group:
+	sysfs_remove_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
+err_clk:
+	platform_set_drvdata(pdev, NULL);
+	clk_put(data->clk);
+err_irq:
+	free_irq(data->irq, data);
+err_io_remap:
+	iounmap(data->base);
+err_mem_region:
+	release_mem_region(data->mem->start, resource_size(data->mem));
+err_free:
+	kfree(data);
+
+	return ret;
+}
+
+static int __devexit exynos4_tmu_remove(struct platform_device *pdev)
+{
+	struct exynos4_tmu_data *data = platform_get_drvdata(pdev);
+
+	exynos4_tmu_control(pdev, false);
+
+	hwmon_device_unregister(data->hwmon_dev);
+	sysfs_remove_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
+
+	clk_put(data->clk);
+
+	free_irq(data->irq, data);
+
+	iounmap(data->base);
+	release_mem_region(data->mem->start, resource_size(data->mem));
+
+	platform_set_drvdata(pdev, NULL);
+
+	kfree(data);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int exynos4_tmu_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	exynos4_tmu_control(pdev, false);
+
+	return 0;
+}
+
+static int exynos4_tmu_resume(struct platform_device *pdev)
+{
+	exynos4_tmu_initialize(pdev);
+	exynos4_tmu_control(pdev, true);
+
+	return 0;
+}
+#else
+#define exynos4_tmu_suspend NULL
+#define exynos4_tmu_resume NULL
+#endif
+
+static struct platform_driver exynos4_tmu_driver = {
+	.driver = {
+		.name   = "exynos4-tmu",
+		.owner  = THIS_MODULE,
+	},
+	.probe = exynos4_tmu_probe,
+	.remove	= __devexit_p(exynos4_tmu_remove),
+	.suspend = exynos4_tmu_suspend,
+	.resume = exynos4_tmu_resume,
+};
+
+module_platform_driver(exynos4_tmu_driver);
+
+MODULE_DESCRIPTION("EXYNOS4 TMU Driver");
+MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:exynos4-tmu");
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH 2/4] hwmon: exynos4: Move thermal sensor driver to driver/mfd directory
@ 2012-03-03 11:06   ` Amit Daniel Kachhap
  0 siblings, 0 replies; 43+ messages in thread
From: Amit Daniel Kachhap @ 2012-03-03 11:06 UTC (permalink / raw)
  To: linux-pm, linux-samsung-soc
  Cc: linux-kernel, mjg59, linux-acpi, lenb, linaro-dev, lm-sensors,
	amit.kachhap, eduardo.valentin, durgadoss.r, patches

This movement is needed because the hwmon entries and corresponding
sysfs interface is a duplicate of utilities already provided by
driver/thermal/thermal_sys.c. The goal is to place it in mfd folder
and add necessary calls to get the temperature information.

Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>
Signed-off-by: Donggeun Kim <dg77.kim@samsung.com>
---
 Documentation/hwmon/exynos4_tmu |   81 ------
 Documentation/mfd/exynos4_tmu   |   81 ++++++
 drivers/hwmon/Kconfig           |   10 -
 drivers/hwmon/Makefile          |    1 -
 drivers/hwmon/exynos4_tmu.c     |  514 ---------------------------------------
 drivers/mfd/Kconfig             |   10 +
 drivers/mfd/Makefile            |    1 +
 drivers/mfd/exynos4_tmu.c       |  514 +++++++++++++++++++++++++++++++++++++++
 8 files changed, 606 insertions(+), 606 deletions(-)
 delete mode 100644 Documentation/hwmon/exynos4_tmu
 create mode 100644 Documentation/mfd/exynos4_tmu
 delete mode 100644 drivers/hwmon/exynos4_tmu.c
 create mode 100644 drivers/mfd/exynos4_tmu.c

diff --git a/Documentation/hwmon/exynos4_tmu b/Documentation/hwmon/exynos4_tmu
deleted file mode 100644
index c3c6b41..0000000
--- a/Documentation/hwmon/exynos4_tmu
+++ /dev/null
@@ -1,81 +0,0 @@
-Kernel driver exynos4_tmu
-=================
-
-Supported chips:
-* ARM SAMSUNG EXYNOS4 series of SoC
-  Prefix: 'exynos4-tmu'
-  Datasheet: Not publicly available
-
-Authors: Donggeun Kim <dg77.kim@samsung.com>
-
-Description
------------
-
-This driver allows to read temperature inside SAMSUNG EXYNOS4 series of SoC.
-
-The chip only exposes the measured 8-bit temperature code value
-through a register.
-Temperature can be taken from the temperature code.
-There are three equations converting from temperature to temperature code.
-
-The three equations are:
-  1. Two point trimming
-	Tc = (T - 25) * (TI2 - TI1) / (85 - 25) + TI1
-
-  2. One point trimming
-	Tc = T + TI1 - 25
-
-  3. No trimming
-	Tc = T + 50
-
-  Tc: Temperature code, T: Temperature,
-  TI1: Trimming info for 25 degree Celsius (stored at TRIMINFO register)
-       Temperature code measured at 25 degree Celsius which is unchanged
-  TI2: Trimming info for 85 degree Celsius (stored at TRIMINFO register)
-       Temperature code measured at 85 degree Celsius which is unchanged
-
-TMU(Thermal Management Unit) in EXYNOS4 generates interrupt
-when temperature exceeds pre-defined levels.
-The maximum number of configurable threshold is four.
-The threshold levels are defined as follows:
-  Level_0: current temperature > trigger_level_0 + threshold
-  Level_1: current temperature > trigger_level_1 + threshold
-  Level_2: current temperature > trigger_level_2 + threshold
-  Level_3: current temperature > trigger_level_3 + threshold
-
-  The threshold and each trigger_level are set
-  through the corresponding registers.
-
-When an interrupt occurs, this driver notify user space of
-one of four threshold levels for the interrupt
-through kobject_uevent_env and sysfs_notify functions.
-Although an interrupt condition for level_0 can be set,
-it is not notified to user space through sysfs_notify function.
-
-Sysfs Interface
----------------
-name		name of the temperature sensor
-		RO
-
-temp1_input	temperature
-		RO
-
-temp1_max	temperature for level_1 interrupt
-		RO
-
-temp1_crit	temperature for level_2 interrupt
-		RO
-
-temp1_emergency	temperature for level_3 interrupt
-		RO
-
-temp1_max_alarm	alarm for level_1 interrupt
-		RO
-
-temp1_crit_alarm
-		alarm for level_2 interrupt
-		RO
-
-temp1_emergency_alarm
-		alarm for level_3 interrupt
-		RO
diff --git a/Documentation/mfd/exynos4_tmu b/Documentation/mfd/exynos4_tmu
new file mode 100644
index 0000000..c3c6b41
--- /dev/null
+++ b/Documentation/mfd/exynos4_tmu
@@ -0,0 +1,81 @@
+Kernel driver exynos4_tmu
+=================
+
+Supported chips:
+* ARM SAMSUNG EXYNOS4 series of SoC
+  Prefix: 'exynos4-tmu'
+  Datasheet: Not publicly available
+
+Authors: Donggeun Kim <dg77.kim@samsung.com>
+
+Description
+-----------
+
+This driver allows to read temperature inside SAMSUNG EXYNOS4 series of SoC.
+
+The chip only exposes the measured 8-bit temperature code value
+through a register.
+Temperature can be taken from the temperature code.
+There are three equations converting from temperature to temperature code.
+
+The three equations are:
+  1. Two point trimming
+	Tc = (T - 25) * (TI2 - TI1) / (85 - 25) + TI1
+
+  2. One point trimming
+	Tc = T + TI1 - 25
+
+  3. No trimming
+	Tc = T + 50
+
+  Tc: Temperature code, T: Temperature,
+  TI1: Trimming info for 25 degree Celsius (stored at TRIMINFO register)
+       Temperature code measured at 25 degree Celsius which is unchanged
+  TI2: Trimming info for 85 degree Celsius (stored at TRIMINFO register)
+       Temperature code measured at 85 degree Celsius which is unchanged
+
+TMU(Thermal Management Unit) in EXYNOS4 generates interrupt
+when temperature exceeds pre-defined levels.
+The maximum number of configurable threshold is four.
+The threshold levels are defined as follows:
+  Level_0: current temperature > trigger_level_0 + threshold
+  Level_1: current temperature > trigger_level_1 + threshold
+  Level_2: current temperature > trigger_level_2 + threshold
+  Level_3: current temperature > trigger_level_3 + threshold
+
+  The threshold and each trigger_level are set
+  through the corresponding registers.
+
+When an interrupt occurs, this driver notify user space of
+one of four threshold levels for the interrupt
+through kobject_uevent_env and sysfs_notify functions.
+Although an interrupt condition for level_0 can be set,
+it is not notified to user space through sysfs_notify function.
+
+Sysfs Interface
+---------------
+name		name of the temperature sensor
+		RO
+
+temp1_input	temperature
+		RO
+
+temp1_max	temperature for level_1 interrupt
+		RO
+
+temp1_crit	temperature for level_2 interrupt
+		RO
+
+temp1_emergency	temperature for level_3 interrupt
+		RO
+
+temp1_max_alarm	alarm for level_1 interrupt
+		RO
+
+temp1_crit_alarm
+		alarm for level_2 interrupt
+		RO
+
+temp1_emergency_alarm
+		alarm for level_3 interrupt
+		RO
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 0226040..5c4a0fb 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -313,16 +313,6 @@ config SENSORS_DS1621
 	  This driver can also be built as a module.  If so, the module
 	  will be called ds1621.
 
-config SENSORS_EXYNOS4_TMU
-	tristate "Temperature sensor on Samsung EXYNOS4"
-	depends on ARCH_EXYNOS4
-	help
-	  If you say yes here you get support for TMU (Thermal Managment
-	  Unit) on SAMSUNG EXYNOS4 series of SoC.
-
-	  This driver can also be built as a module. If so, the module
-	  will be called exynos4-tmu.
-
 config SENSORS_I5K_AMB
 	tristate "FB-DIMM AMB temperature sensor on Intel 5000 series chipsets"
 	depends on PCI && EXPERIMENTAL
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 8251ce8..228a4b7 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -48,7 +48,6 @@ obj-$(CONFIG_SENSORS_DS1621)	+= ds1621.o
 obj-$(CONFIG_SENSORS_EMC1403)	+= emc1403.o
 obj-$(CONFIG_SENSORS_EMC2103)	+= emc2103.o
 obj-$(CONFIG_SENSORS_EMC6W201)	+= emc6w201.o
-obj-$(CONFIG_SENSORS_EXYNOS4_TMU)	+= exynos4_tmu.o
 obj-$(CONFIG_SENSORS_F71805F)	+= f71805f.o
 obj-$(CONFIG_SENSORS_F71882FG)	+= f71882fg.o
 obj-$(CONFIG_SENSORS_F75375S)	+= f75375s.o
diff --git a/drivers/hwmon/exynos4_tmu.c b/drivers/hwmon/exynos4_tmu.c
deleted file mode 100644
index f2359a0..0000000
--- a/drivers/hwmon/exynos4_tmu.c
+++ /dev/null
@@ -1,514 +0,0 @@
-/*
- * exynos4_tmu.c - Samsung EXYNOS4 TMU (Thermal Management Unit)
- *
- *  Copyright (C) 2011 Samsung Electronics
- *  Donggeun Kim <dg77.kim@samsung.com>
- *
- * 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/hwmon.h>
-#include <linux/hwmon-sysfs.h>
-
-#include <linux/platform_data/exynos4_tmu.h>
-
-#define EXYNOS4_TMU_REG_TRIMINFO	0x0
-#define EXYNOS4_TMU_REG_CONTROL		0x20
-#define EXYNOS4_TMU_REG_STATUS		0x28
-#define EXYNOS4_TMU_REG_CURRENT_TEMP	0x40
-#define EXYNOS4_TMU_REG_THRESHOLD_TEMP	0x44
-#define EXYNOS4_TMU_REG_TRIG_LEVEL0	0x50
-#define EXYNOS4_TMU_REG_TRIG_LEVEL1	0x54
-#define EXYNOS4_TMU_REG_TRIG_LEVEL2	0x58
-#define EXYNOS4_TMU_REG_TRIG_LEVEL3	0x5C
-#define EXYNOS4_TMU_REG_PAST_TEMP0	0x60
-#define EXYNOS4_TMU_REG_PAST_TEMP1	0x64
-#define EXYNOS4_TMU_REG_PAST_TEMP2	0x68
-#define EXYNOS4_TMU_REG_PAST_TEMP3	0x6C
-#define EXYNOS4_TMU_REG_INTEN		0x70
-#define EXYNOS4_TMU_REG_INTSTAT		0x74
-#define EXYNOS4_TMU_REG_INTCLEAR	0x78
-
-#define EXYNOS4_TMU_GAIN_SHIFT		8
-#define EXYNOS4_TMU_REF_VOLTAGE_SHIFT	24
-
-#define EXYNOS4_TMU_TRIM_TEMP_MASK	0xff
-#define EXYNOS4_TMU_CORE_ON	3
-#define EXYNOS4_TMU_CORE_OFF	2
-#define EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET	50
-#define EXYNOS4_TMU_TRIG_LEVEL0_MASK	0x1
-#define EXYNOS4_TMU_TRIG_LEVEL1_MASK	0x10
-#define EXYNOS4_TMU_TRIG_LEVEL2_MASK	0x100
-#define EXYNOS4_TMU_TRIG_LEVEL3_MASK	0x1000
-#define EXYNOS4_TMU_INTCLEAR_VAL	0x1111
-
-struct exynos4_tmu_data {
-	struct exynos4_tmu_platform_data *pdata;
-	struct device *hwmon_dev;
-	struct resource *mem;
-	void __iomem *base;
-	int irq;
-	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 exynos4_tmu_data *data, u8 temp)
-{
-	struct exynos4_tmu_platform_data *pdata = data->pdata;
-	int temp_code;
-
-	/* 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 + EXYNOS4_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 exynos4_tmu_data *data, u8 temp_code)
-{
-	struct exynos4_tmu_platform_data *pdata = data->pdata;
-	int temp;
-
-	/* 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 - EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET;
-		break;
-	}
-out:
-	return temp;
-}
-
-static int exynos4_tmu_initialize(struct platform_device *pdev)
-{
-	struct exynos4_tmu_data *data = platform_get_drvdata(pdev);
-	struct exynos4_tmu_platform_data *pdata = data->pdata;
-	unsigned int status, trim_info;
-	int ret = 0, threshold_code;
-
-	mutex_lock(&data->lock);
-	clk_enable(data->clk);
-
-	status = readb(data->base + EXYNOS4_TMU_REG_STATUS);
-	if (!status) {
-		ret = -EBUSY;
-		goto out;
-	}
-
-	/* Save trimming info in order to perform calibration */
-	trim_info = readl(data->base + EXYNOS4_TMU_REG_TRIMINFO);
-	data->temp_error1 = trim_info & EXYNOS4_TMU_TRIM_TEMP_MASK;
-	data->temp_error2 = ((trim_info >> 8) & EXYNOS4_TMU_TRIM_TEMP_MASK);
-
-	/* 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 + EXYNOS4_TMU_REG_THRESHOLD_TEMP);
-
-	writeb(pdata->trigger_levels[0],
-		data->base + EXYNOS4_TMU_REG_TRIG_LEVEL0);
-	writeb(pdata->trigger_levels[1],
-		data->base + EXYNOS4_TMU_REG_TRIG_LEVEL1);
-	writeb(pdata->trigger_levels[2],
-		data->base + EXYNOS4_TMU_REG_TRIG_LEVEL2);
-	writeb(pdata->trigger_levels[3],
-		data->base + EXYNOS4_TMU_REG_TRIG_LEVEL3);
-
-	writel(EXYNOS4_TMU_INTCLEAR_VAL,
-		data->base + EXYNOS4_TMU_REG_INTCLEAR);
-out:
-	clk_disable(data->clk);
-	mutex_unlock(&data->lock);
-
-	return ret;
-}
-
-static void exynos4_tmu_control(struct platform_device *pdev, bool on)
-{
-	struct exynos4_tmu_data *data = platform_get_drvdata(pdev);
-	struct exynos4_tmu_platform_data *pdata = data->pdata;
-	unsigned int con, interrupt_en;
-
-	mutex_lock(&data->lock);
-	clk_enable(data->clk);
-
-	con = pdata->reference_voltage << EXYNOS4_TMU_REF_VOLTAGE_SHIFT |
-		pdata->gain << EXYNOS4_TMU_GAIN_SHIFT;
-	if (on) {
-		con |= EXYNOS4_TMU_CORE_ON;
-		interrupt_en = pdata->trigger_level3_en << 12 |
-			pdata->trigger_level2_en << 8 |
-			pdata->trigger_level1_en << 4 |
-			pdata->trigger_level0_en;
-	} else {
-		con |= EXYNOS4_TMU_CORE_OFF;
-		interrupt_en = 0; /* Disable all interrupts */
-	}
-	writel(interrupt_en, data->base + EXYNOS4_TMU_REG_INTEN);
-	writel(con, data->base + EXYNOS4_TMU_REG_CONTROL);
-
-	clk_disable(data->clk);
-	mutex_unlock(&data->lock);
-}
-
-static int exynos4_tmu_read(struct exynos4_tmu_data *data)
-{
-	u8 temp_code;
-	int temp;
-
-	mutex_lock(&data->lock);
-	clk_enable(data->clk);
-
-	temp_code = readb(data->base + EXYNOS4_TMU_REG_CURRENT_TEMP);
-	temp = code_to_temp(data, temp_code);
-
-	clk_disable(data->clk);
-	mutex_unlock(&data->lock);
-
-	return temp;
-}
-
-static void exynos4_tmu_work(struct work_struct *work)
-{
-	struct exynos4_tmu_data *data = container_of(work,
-			struct exynos4_tmu_data, irq_work);
-
-	mutex_lock(&data->lock);
-	clk_enable(data->clk);
-
-	writel(EXYNOS4_TMU_INTCLEAR_VAL, data->base + EXYNOS4_TMU_REG_INTCLEAR);
-
-	kobject_uevent(&data->hwmon_dev->kobj, KOBJ_CHANGE);
-
-	enable_irq(data->irq);
-
-	clk_disable(data->clk);
-	mutex_unlock(&data->lock);
-}
-
-static irqreturn_t exynos4_tmu_irq(int irq, void *id)
-{
-	struct exynos4_tmu_data *data = id;
-
-	disable_irq_nosync(irq);
-	schedule_work(&data->irq_work);
-
-	return IRQ_HANDLED;
-}
-
-static ssize_t exynos4_tmu_show_name(struct device *dev,
-		struct device_attribute *attr, char *buf)
-{
-	return sprintf(buf, "exynos4-tmu\n");
-}
-
-static ssize_t exynos4_tmu_show_temp(struct device *dev,
-		struct device_attribute *attr, char *buf)
-{
-	struct exynos4_tmu_data *data = dev_get_drvdata(dev);
-	int ret;
-
-	ret = exynos4_tmu_read(data);
-	if (ret < 0)
-		return ret;
-
-	/* convert from degree Celsius to millidegree Celsius */
-	return sprintf(buf, "%d\n", ret * 1000);
-}
-
-static ssize_t exynos4_tmu_show_alarm(struct device *dev,
-		struct device_attribute *devattr, char *buf)
-{
-	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-	struct exynos4_tmu_data *data = dev_get_drvdata(dev);
-	struct exynos4_tmu_platform_data *pdata = data->pdata;
-	int temp;
-	unsigned int trigger_level;
-
-	temp = exynos4_tmu_read(data);
-	if (temp < 0)
-		return temp;
-
-	trigger_level = pdata->threshold + pdata->trigger_levels[attr->index];
-
-	return sprintf(buf, "%d\n", !!(temp > trigger_level));
-}
-
-static ssize_t exynos4_tmu_show_level(struct device *dev,
-		struct device_attribute *devattr, char *buf)
-{
-	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-	struct exynos4_tmu_data *data = dev_get_drvdata(dev);
-	struct exynos4_tmu_platform_data *pdata = data->pdata;
-	unsigned int temp = pdata->threshold +
-			pdata->trigger_levels[attr->index];
-
-	return sprintf(buf, "%u\n", temp * 1000);
-}
-
-static DEVICE_ATTR(name, S_IRUGO, exynos4_tmu_show_name, NULL);
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, exynos4_tmu_show_temp, NULL, 0);
-
-static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO,
-		exynos4_tmu_show_alarm, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO,
-		exynos4_tmu_show_alarm, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp1_emergency_alarm, S_IRUGO,
-		exynos4_tmu_show_alarm, NULL, 3);
-
-static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, exynos4_tmu_show_level, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, exynos4_tmu_show_level, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp1_emergency, S_IRUGO,
-		exynos4_tmu_show_level, NULL, 3);
-
-static struct attribute *exynos4_tmu_attributes[] = {
-	&dev_attr_name.attr,
-	&sensor_dev_attr_temp1_input.dev_attr.attr,
-	&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
-	&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
-	&sensor_dev_attr_temp1_emergency_alarm.dev_attr.attr,
-	&sensor_dev_attr_temp1_max.dev_attr.attr,
-	&sensor_dev_attr_temp1_crit.dev_attr.attr,
-	&sensor_dev_attr_temp1_emergency.dev_attr.attr,
-	NULL,
-};
-
-static const struct attribute_group exynos4_tmu_attr_group = {
-	.attrs = exynos4_tmu_attributes,
-};
-
-static int __devinit exynos4_tmu_probe(struct platform_device *pdev)
-{
-	struct exynos4_tmu_data *data;
-	struct exynos4_tmu_platform_data *pdata = pdev->dev.platform_data;
-	int ret;
-
-	if (!pdata) {
-		dev_err(&pdev->dev, "No platform init data supplied.\n");
-		return -ENODEV;
-	}
-
-	data = kzalloc(sizeof(struct exynos4_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) {
-		ret = data->irq;
-		dev_err(&pdev->dev, "Failed to get platform irq\n");
-		goto err_free;
-	}
-
-	INIT_WORK(&data->irq_work, exynos4_tmu_work);
-
-	data->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!data->mem) {
-		ret = -ENOENT;
-		dev_err(&pdev->dev, "Failed to get platform resource\n");
-		goto err_free;
-	}
-
-	data->mem = request_mem_region(data->mem->start,
-			resource_size(data->mem), pdev->name);
-	if (!data->mem) {
-		ret = -ENODEV;
-		dev_err(&pdev->dev, "Failed to request memory region\n");
-		goto err_free;
-	}
-
-	data->base = ioremap(data->mem->start, resource_size(data->mem));
-	if (!data->base) {
-		ret = -ENODEV;
-		dev_err(&pdev->dev, "Failed to ioremap memory\n");
-		goto err_mem_region;
-	}
-
-	ret = request_irq(data->irq, exynos4_tmu_irq,
-		IRQF_TRIGGER_RISING,
-		"exynos4-tmu", data);
-	if (ret) {
-		dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq);
-		goto err_io_remap;
-	}
-
-	data->clk = clk_get(NULL, "tmu_apbif");
-	if (IS_ERR(data->clk)) {
-		ret = PTR_ERR(data->clk);
-		dev_err(&pdev->dev, "Failed to get clock\n");
-		goto err_irq;
-	}
-
-	data->pdata = pdata;
-	platform_set_drvdata(pdev, data);
-	mutex_init(&data->lock);
-
-	ret = exynos4_tmu_initialize(pdev);
-	if (ret) {
-		dev_err(&pdev->dev, "Failed to initialize TMU\n");
-		goto err_clk;
-	}
-
-	ret = sysfs_create_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
-	if (ret) {
-		dev_err(&pdev->dev, "Failed to create sysfs group\n");
-		goto err_clk;
-	}
-
-	data->hwmon_dev = hwmon_device_register(&pdev->dev);
-	if (IS_ERR(data->hwmon_dev)) {
-		ret = PTR_ERR(data->hwmon_dev);
-		dev_err(&pdev->dev, "Failed to register hwmon device\n");
-		goto err_create_group;
-	}
-
-	exynos4_tmu_control(pdev, true);
-
-	return 0;
-
-err_create_group:
-	sysfs_remove_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
-err_clk:
-	platform_set_drvdata(pdev, NULL);
-	clk_put(data->clk);
-err_irq:
-	free_irq(data->irq, data);
-err_io_remap:
-	iounmap(data->base);
-err_mem_region:
-	release_mem_region(data->mem->start, resource_size(data->mem));
-err_free:
-	kfree(data);
-
-	return ret;
-}
-
-static int __devexit exynos4_tmu_remove(struct platform_device *pdev)
-{
-	struct exynos4_tmu_data *data = platform_get_drvdata(pdev);
-
-	exynos4_tmu_control(pdev, false);
-
-	hwmon_device_unregister(data->hwmon_dev);
-	sysfs_remove_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
-
-	clk_put(data->clk);
-
-	free_irq(data->irq, data);
-
-	iounmap(data->base);
-	release_mem_region(data->mem->start, resource_size(data->mem));
-
-	platform_set_drvdata(pdev, NULL);
-
-	kfree(data);
-
-	return 0;
-}
-
-#ifdef CONFIG_PM
-static int exynos4_tmu_suspend(struct platform_device *pdev, pm_message_t state)
-{
-	exynos4_tmu_control(pdev, false);
-
-	return 0;
-}
-
-static int exynos4_tmu_resume(struct platform_device *pdev)
-{
-	exynos4_tmu_initialize(pdev);
-	exynos4_tmu_control(pdev, true);
-
-	return 0;
-}
-#else
-#define exynos4_tmu_suspend NULL
-#define exynos4_tmu_resume NULL
-#endif
-
-static struct platform_driver exynos4_tmu_driver = {
-	.driver = {
-		.name   = "exynos4-tmu",
-		.owner  = THIS_MODULE,
-	},
-	.probe = exynos4_tmu_probe,
-	.remove	= __devexit_p(exynos4_tmu_remove),
-	.suspend = exynos4_tmu_suspend,
-	.resume = exynos4_tmu_resume,
-};
-
-module_platform_driver(exynos4_tmu_driver);
-
-MODULE_DESCRIPTION("EXYNOS4 TMU Driver");
-MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:exynos4-tmu");
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index f147395..e29113e 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -830,6 +830,16 @@ config MFD_INTEL_MSIC
 	  Passage) chip. This chip embeds audio, battery, GPIO, etc.
 	  devices used in Intel Medfield platforms.
 
+config SENSORS_EXYNOS4_TMU
+	tristate "Temperature sensor on Samsung EXYNOS4"
+	depends on ARCH_EXYNOS4
+	help
+	  If you say yes here you get support for TMU (Thermal Managment
+	  Unit) on SAMSUNG EXYNOS4 series of SoC.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called exynos4-tmu.
+
 endmenu
 endif
 
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index b953bab..766bf86 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -112,3 +112,4 @@ obj-$(CONFIG_TPS65911_COMPARATOR)	+= tps65911-comparator.o
 obj-$(CONFIG_MFD_AAT2870_CORE)	+= aat2870-core.o
 obj-$(CONFIG_MFD_INTEL_MSIC)	+= intel_msic.o
 obj-$(CONFIG_MFD_S5M_CORE)	+= s5m-core.o s5m-irq.o
+obj-$(CONFIG_SENSORS_EXYNOS4_TMU)	+= exynos4_tmu.o
diff --git a/drivers/mfd/exynos4_tmu.c b/drivers/mfd/exynos4_tmu.c
new file mode 100644
index 0000000..f2359a0
--- /dev/null
+++ b/drivers/mfd/exynos4_tmu.c
@@ -0,0 +1,514 @@
+/*
+ * exynos4_tmu.c - Samsung EXYNOS4 TMU (Thermal Management Unit)
+ *
+ *  Copyright (C) 2011 Samsung Electronics
+ *  Donggeun Kim <dg77.kim@samsung.com>
+ *
+ * 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/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+
+#include <linux/platform_data/exynos4_tmu.h>
+
+#define EXYNOS4_TMU_REG_TRIMINFO	0x0
+#define EXYNOS4_TMU_REG_CONTROL		0x20
+#define EXYNOS4_TMU_REG_STATUS		0x28
+#define EXYNOS4_TMU_REG_CURRENT_TEMP	0x40
+#define EXYNOS4_TMU_REG_THRESHOLD_TEMP	0x44
+#define EXYNOS4_TMU_REG_TRIG_LEVEL0	0x50
+#define EXYNOS4_TMU_REG_TRIG_LEVEL1	0x54
+#define EXYNOS4_TMU_REG_TRIG_LEVEL2	0x58
+#define EXYNOS4_TMU_REG_TRIG_LEVEL3	0x5C
+#define EXYNOS4_TMU_REG_PAST_TEMP0	0x60
+#define EXYNOS4_TMU_REG_PAST_TEMP1	0x64
+#define EXYNOS4_TMU_REG_PAST_TEMP2	0x68
+#define EXYNOS4_TMU_REG_PAST_TEMP3	0x6C
+#define EXYNOS4_TMU_REG_INTEN		0x70
+#define EXYNOS4_TMU_REG_INTSTAT		0x74
+#define EXYNOS4_TMU_REG_INTCLEAR	0x78
+
+#define EXYNOS4_TMU_GAIN_SHIFT		8
+#define EXYNOS4_TMU_REF_VOLTAGE_SHIFT	24
+
+#define EXYNOS4_TMU_TRIM_TEMP_MASK	0xff
+#define EXYNOS4_TMU_CORE_ON	3
+#define EXYNOS4_TMU_CORE_OFF	2
+#define EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET	50
+#define EXYNOS4_TMU_TRIG_LEVEL0_MASK	0x1
+#define EXYNOS4_TMU_TRIG_LEVEL1_MASK	0x10
+#define EXYNOS4_TMU_TRIG_LEVEL2_MASK	0x100
+#define EXYNOS4_TMU_TRIG_LEVEL3_MASK	0x1000
+#define EXYNOS4_TMU_INTCLEAR_VAL	0x1111
+
+struct exynos4_tmu_data {
+	struct exynos4_tmu_platform_data *pdata;
+	struct device *hwmon_dev;
+	struct resource *mem;
+	void __iomem *base;
+	int irq;
+	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 exynos4_tmu_data *data, u8 temp)
+{
+	struct exynos4_tmu_platform_data *pdata = data->pdata;
+	int temp_code;
+
+	/* 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 + EXYNOS4_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 exynos4_tmu_data *data, u8 temp_code)
+{
+	struct exynos4_tmu_platform_data *pdata = data->pdata;
+	int temp;
+
+	/* 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 - EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET;
+		break;
+	}
+out:
+	return temp;
+}
+
+static int exynos4_tmu_initialize(struct platform_device *pdev)
+{
+	struct exynos4_tmu_data *data = platform_get_drvdata(pdev);
+	struct exynos4_tmu_platform_data *pdata = data->pdata;
+	unsigned int status, trim_info;
+	int ret = 0, threshold_code;
+
+	mutex_lock(&data->lock);
+	clk_enable(data->clk);
+
+	status = readb(data->base + EXYNOS4_TMU_REG_STATUS);
+	if (!status) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	/* Save trimming info in order to perform calibration */
+	trim_info = readl(data->base + EXYNOS4_TMU_REG_TRIMINFO);
+	data->temp_error1 = trim_info & EXYNOS4_TMU_TRIM_TEMP_MASK;
+	data->temp_error2 = ((trim_info >> 8) & EXYNOS4_TMU_TRIM_TEMP_MASK);
+
+	/* 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 + EXYNOS4_TMU_REG_THRESHOLD_TEMP);
+
+	writeb(pdata->trigger_levels[0],
+		data->base + EXYNOS4_TMU_REG_TRIG_LEVEL0);
+	writeb(pdata->trigger_levels[1],
+		data->base + EXYNOS4_TMU_REG_TRIG_LEVEL1);
+	writeb(pdata->trigger_levels[2],
+		data->base + EXYNOS4_TMU_REG_TRIG_LEVEL2);
+	writeb(pdata->trigger_levels[3],
+		data->base + EXYNOS4_TMU_REG_TRIG_LEVEL3);
+
+	writel(EXYNOS4_TMU_INTCLEAR_VAL,
+		data->base + EXYNOS4_TMU_REG_INTCLEAR);
+out:
+	clk_disable(data->clk);
+	mutex_unlock(&data->lock);
+
+	return ret;
+}
+
+static void exynos4_tmu_control(struct platform_device *pdev, bool on)
+{
+	struct exynos4_tmu_data *data = platform_get_drvdata(pdev);
+	struct exynos4_tmu_platform_data *pdata = data->pdata;
+	unsigned int con, interrupt_en;
+
+	mutex_lock(&data->lock);
+	clk_enable(data->clk);
+
+	con = pdata->reference_voltage << EXYNOS4_TMU_REF_VOLTAGE_SHIFT |
+		pdata->gain << EXYNOS4_TMU_GAIN_SHIFT;
+	if (on) {
+		con |= EXYNOS4_TMU_CORE_ON;
+		interrupt_en = pdata->trigger_level3_en << 12 |
+			pdata->trigger_level2_en << 8 |
+			pdata->trigger_level1_en << 4 |
+			pdata->trigger_level0_en;
+	} else {
+		con |= EXYNOS4_TMU_CORE_OFF;
+		interrupt_en = 0; /* Disable all interrupts */
+	}
+	writel(interrupt_en, data->base + EXYNOS4_TMU_REG_INTEN);
+	writel(con, data->base + EXYNOS4_TMU_REG_CONTROL);
+
+	clk_disable(data->clk);
+	mutex_unlock(&data->lock);
+}
+
+static int exynos4_tmu_read(struct exynos4_tmu_data *data)
+{
+	u8 temp_code;
+	int temp;
+
+	mutex_lock(&data->lock);
+	clk_enable(data->clk);
+
+	temp_code = readb(data->base + EXYNOS4_TMU_REG_CURRENT_TEMP);
+	temp = code_to_temp(data, temp_code);
+
+	clk_disable(data->clk);
+	mutex_unlock(&data->lock);
+
+	return temp;
+}
+
+static void exynos4_tmu_work(struct work_struct *work)
+{
+	struct exynos4_tmu_data *data = container_of(work,
+			struct exynos4_tmu_data, irq_work);
+
+	mutex_lock(&data->lock);
+	clk_enable(data->clk);
+
+	writel(EXYNOS4_TMU_INTCLEAR_VAL, data->base + EXYNOS4_TMU_REG_INTCLEAR);
+
+	kobject_uevent(&data->hwmon_dev->kobj, KOBJ_CHANGE);
+
+	enable_irq(data->irq);
+
+	clk_disable(data->clk);
+	mutex_unlock(&data->lock);
+}
+
+static irqreturn_t exynos4_tmu_irq(int irq, void *id)
+{
+	struct exynos4_tmu_data *data = id;
+
+	disable_irq_nosync(irq);
+	schedule_work(&data->irq_work);
+
+	return IRQ_HANDLED;
+}
+
+static ssize_t exynos4_tmu_show_name(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "exynos4-tmu\n");
+}
+
+static ssize_t exynos4_tmu_show_temp(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct exynos4_tmu_data *data = dev_get_drvdata(dev);
+	int ret;
+
+	ret = exynos4_tmu_read(data);
+	if (ret < 0)
+		return ret;
+
+	/* convert from degree Celsius to millidegree Celsius */
+	return sprintf(buf, "%d\n", ret * 1000);
+}
+
+static ssize_t exynos4_tmu_show_alarm(struct device *dev,
+		struct device_attribute *devattr, char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	struct exynos4_tmu_data *data = dev_get_drvdata(dev);
+	struct exynos4_tmu_platform_data *pdata = data->pdata;
+	int temp;
+	unsigned int trigger_level;
+
+	temp = exynos4_tmu_read(data);
+	if (temp < 0)
+		return temp;
+
+	trigger_level = pdata->threshold + pdata->trigger_levels[attr->index];
+
+	return sprintf(buf, "%d\n", !!(temp > trigger_level));
+}
+
+static ssize_t exynos4_tmu_show_level(struct device *dev,
+		struct device_attribute *devattr, char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	struct exynos4_tmu_data *data = dev_get_drvdata(dev);
+	struct exynos4_tmu_platform_data *pdata = data->pdata;
+	unsigned int temp = pdata->threshold +
+			pdata->trigger_levels[attr->index];
+
+	return sprintf(buf, "%u\n", temp * 1000);
+}
+
+static DEVICE_ATTR(name, S_IRUGO, exynos4_tmu_show_name, NULL);
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, exynos4_tmu_show_temp, NULL, 0);
+
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO,
+		exynos4_tmu_show_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO,
+		exynos4_tmu_show_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp1_emergency_alarm, S_IRUGO,
+		exynos4_tmu_show_alarm, NULL, 3);
+
+static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, exynos4_tmu_show_level, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, exynos4_tmu_show_level, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp1_emergency, S_IRUGO,
+		exynos4_tmu_show_level, NULL, 3);
+
+static struct attribute *exynos4_tmu_attributes[] = {
+	&dev_attr_name.attr,
+	&sensor_dev_attr_temp1_input.dev_attr.attr,
+	&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+	&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
+	&sensor_dev_attr_temp1_emergency_alarm.dev_attr.attr,
+	&sensor_dev_attr_temp1_max.dev_attr.attr,
+	&sensor_dev_attr_temp1_crit.dev_attr.attr,
+	&sensor_dev_attr_temp1_emergency.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group exynos4_tmu_attr_group = {
+	.attrs = exynos4_tmu_attributes,
+};
+
+static int __devinit exynos4_tmu_probe(struct platform_device *pdev)
+{
+	struct exynos4_tmu_data *data;
+	struct exynos4_tmu_platform_data *pdata = pdev->dev.platform_data;
+	int ret;
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "No platform init data supplied.\n");
+		return -ENODEV;
+	}
+
+	data = kzalloc(sizeof(struct exynos4_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) {
+		ret = data->irq;
+		dev_err(&pdev->dev, "Failed to get platform irq\n");
+		goto err_free;
+	}
+
+	INIT_WORK(&data->irq_work, exynos4_tmu_work);
+
+	data->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!data->mem) {
+		ret = -ENOENT;
+		dev_err(&pdev->dev, "Failed to get platform resource\n");
+		goto err_free;
+	}
+
+	data->mem = request_mem_region(data->mem->start,
+			resource_size(data->mem), pdev->name);
+	if (!data->mem) {
+		ret = -ENODEV;
+		dev_err(&pdev->dev, "Failed to request memory region\n");
+		goto err_free;
+	}
+
+	data->base = ioremap(data->mem->start, resource_size(data->mem));
+	if (!data->base) {
+		ret = -ENODEV;
+		dev_err(&pdev->dev, "Failed to ioremap memory\n");
+		goto err_mem_region;
+	}
+
+	ret = request_irq(data->irq, exynos4_tmu_irq,
+		IRQF_TRIGGER_RISING,
+		"exynos4-tmu", data);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq);
+		goto err_io_remap;
+	}
+
+	data->clk = clk_get(NULL, "tmu_apbif");
+	if (IS_ERR(data->clk)) {
+		ret = PTR_ERR(data->clk);
+		dev_err(&pdev->dev, "Failed to get clock\n");
+		goto err_irq;
+	}
+
+	data->pdata = pdata;
+	platform_set_drvdata(pdev, data);
+	mutex_init(&data->lock);
+
+	ret = exynos4_tmu_initialize(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to initialize TMU\n");
+		goto err_clk;
+	}
+
+	ret = sysfs_create_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to create sysfs group\n");
+		goto err_clk;
+	}
+
+	data->hwmon_dev = hwmon_device_register(&pdev->dev);
+	if (IS_ERR(data->hwmon_dev)) {
+		ret = PTR_ERR(data->hwmon_dev);
+		dev_err(&pdev->dev, "Failed to register hwmon device\n");
+		goto err_create_group;
+	}
+
+	exynos4_tmu_control(pdev, true);
+
+	return 0;
+
+err_create_group:
+	sysfs_remove_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
+err_clk:
+	platform_set_drvdata(pdev, NULL);
+	clk_put(data->clk);
+err_irq:
+	free_irq(data->irq, data);
+err_io_remap:
+	iounmap(data->base);
+err_mem_region:
+	release_mem_region(data->mem->start, resource_size(data->mem));
+err_free:
+	kfree(data);
+
+	return ret;
+}
+
+static int __devexit exynos4_tmu_remove(struct platform_device *pdev)
+{
+	struct exynos4_tmu_data *data = platform_get_drvdata(pdev);
+
+	exynos4_tmu_control(pdev, false);
+
+	hwmon_device_unregister(data->hwmon_dev);
+	sysfs_remove_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
+
+	clk_put(data->clk);
+
+	free_irq(data->irq, data);
+
+	iounmap(data->base);
+	release_mem_region(data->mem->start, resource_size(data->mem));
+
+	platform_set_drvdata(pdev, NULL);
+
+	kfree(data);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int exynos4_tmu_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	exynos4_tmu_control(pdev, false);
+
+	return 0;
+}
+
+static int exynos4_tmu_resume(struct platform_device *pdev)
+{
+	exynos4_tmu_initialize(pdev);
+	exynos4_tmu_control(pdev, true);
+
+	return 0;
+}
+#else
+#define exynos4_tmu_suspend NULL
+#define exynos4_tmu_resume NULL
+#endif
+
+static struct platform_driver exynos4_tmu_driver = {
+	.driver = {
+		.name   = "exynos4-tmu",
+		.owner  = THIS_MODULE,
+	},
+	.probe = exynos4_tmu_probe,
+	.remove	= __devexit_p(exynos4_tmu_remove),
+	.suspend = exynos4_tmu_suspend,
+	.resume = exynos4_tmu_resume,
+};
+
+module_platform_driver(exynos4_tmu_driver);
+
+MODULE_DESCRIPTION("EXYNOS4 TMU Driver");
+MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:exynos4-tmu");
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH 3/4] thermal: exynos4: Register the tmu sensor with the thermal interface layer
  2012-03-03 11:06 ` Amit Daniel Kachhap
  (?)
@ 2012-03-03 11:06   ` Amit Daniel Kachhap
  -1 siblings, 0 replies; 43+ messages in thread
From: Amit Daniel Kachhap @ 2012-03-03 11:06 UTC (permalink / raw)
  To: linux-pm, linux-samsung-soc
  Cc: linaro-dev, patches, linux-kernel, lm-sensors, linux-acpi

Export and register information from the tmu temperature sensor to the samsung
exynos kernel thermal framework where different cooling devices and thermal
zone are binded. The exported information is based according to the data
structure thermal_sensor_conf present in exynos_thermal.h. HWMON sysfs
functions are removed as all of them are present in generic linux thermal layer.

Also the platform data structure is modified to pass frequency cooling
in percentages for each thermal level.

Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>
---
 Documentation/mfd/exynos4_tmu             |   35 +-------
 drivers/mfd/exynos4_tmu.c                 |  139 +++++++----------------------
 include/linux/platform_data/exynos4_tmu.h |    7 ++
 3 files changed, 44 insertions(+), 137 deletions(-)

diff --git a/Documentation/mfd/exynos4_tmu b/Documentation/mfd/exynos4_tmu
index c3c6b41..2b46f67 100644
--- a/Documentation/mfd/exynos4_tmu
+++ b/Documentation/mfd/exynos4_tmu
@@ -46,36 +46,7 @@ The threshold levels are defined as follows:
   The threshold and each trigger_level are set
   through the corresponding registers.
 
-When an interrupt occurs, this driver notify user space of
-one of four threshold levels for the interrupt
-through kobject_uevent_env and sysfs_notify functions.
+When an interrupt occurs, this driver notify kernel thermal framework
+with the function exynos4_report_trigger.
 Although an interrupt condition for level_0 can be set,
-it is not notified to user space through sysfs_notify function.
-
-Sysfs Interface
----------------
-name		name of the temperature sensor
-		RO
-
-temp1_input	temperature
-		RO
-
-temp1_max	temperature for level_1 interrupt
-		RO
-
-temp1_crit	temperature for level_2 interrupt
-		RO
-
-temp1_emergency	temperature for level_3 interrupt
-		RO
-
-temp1_max_alarm	alarm for level_1 interrupt
-		RO
-
-temp1_crit_alarm
-		alarm for level_2 interrupt
-		RO
-
-temp1_emergency_alarm
-		alarm for level_3 interrupt
-		RO
+it can be used to synchronize the cooling action.
diff --git a/drivers/mfd/exynos4_tmu.c b/drivers/mfd/exynos4_tmu.c
index f2359a0..e343923 100644
--- a/drivers/mfd/exynos4_tmu.c
+++ b/drivers/mfd/exynos4_tmu.c
@@ -33,10 +33,10 @@
 #include <linux/io.h>
 #include <linux/mutex.h>
 
-#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
-
 #include <linux/platform_data/exynos4_tmu.h>
+#ifdef CONFIG_SAMSUNG_THERMAL_INTERFACE
+#include <linux/exynos_thermal.h>
+#endif
 
 #define EXYNOS4_TMU_REG_TRIMINFO	0x0
 #define EXYNOS4_TMU_REG_CONTROL		0x20
@@ -70,7 +70,6 @@
 
 struct exynos4_tmu_data {
 	struct exynos4_tmu_platform_data *pdata;
-	struct device *hwmon_dev;
 	struct resource *mem;
 	void __iomem *base;
 	int irq;
@@ -246,12 +245,12 @@ static void exynos4_tmu_work(struct work_struct *work)
 
 	writel(EXYNOS4_TMU_INTCLEAR_VAL, data->base + EXYNOS4_TMU_REG_INTCLEAR);
 
-	kobject_uevent(&data->hwmon_dev->kobj, KOBJ_CHANGE);
-
-	enable_irq(data->irq);
-
 	clk_disable(data->clk);
 	mutex_unlock(&data->lock);
+#ifdef CONFIG_SAMSUNG_THERMAL_INTERFACE
+	exynos4_report_trigger();
+#endif
+	enable_irq(data->irq);
 }
 
 static irqreturn_t exynos4_tmu_irq(int irq, void *id)
@@ -264,92 +263,19 @@ static irqreturn_t exynos4_tmu_irq(int irq, void *id)
 	return IRQ_HANDLED;
 }
 
-static ssize_t exynos4_tmu_show_name(struct device *dev,
-		struct device_attribute *attr, char *buf)
-{
-	return sprintf(buf, "exynos4-tmu\n");
-}
-
-static ssize_t exynos4_tmu_show_temp(struct device *dev,
-		struct device_attribute *attr, char *buf)
-{
-	struct exynos4_tmu_data *data = dev_get_drvdata(dev);
-	int ret;
-
-	ret = exynos4_tmu_read(data);
-	if (ret < 0)
-		return ret;
-
-	/* convert from degree Celsius to millidegree Celsius */
-	return sprintf(buf, "%d\n", ret * 1000);
-}
-
-static ssize_t exynos4_tmu_show_alarm(struct device *dev,
-		struct device_attribute *devattr, char *buf)
-{
-	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-	struct exynos4_tmu_data *data = dev_get_drvdata(dev);
-	struct exynos4_tmu_platform_data *pdata = data->pdata;
-	int temp;
-	unsigned int trigger_level;
-
-	temp = exynos4_tmu_read(data);
-	if (temp < 0)
-		return temp;
-
-	trigger_level = pdata->threshold + pdata->trigger_levels[attr->index];
-
-	return sprintf(buf, "%d\n", !!(temp > trigger_level));
-}
-
-static ssize_t exynos4_tmu_show_level(struct device *dev,
-		struct device_attribute *devattr, char *buf)
-{
-	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-	struct exynos4_tmu_data *data = dev_get_drvdata(dev);
-	struct exynos4_tmu_platform_data *pdata = data->pdata;
-	unsigned int temp = pdata->threshold +
-			pdata->trigger_levels[attr->index];
-
-	return sprintf(buf, "%u\n", temp * 1000);
-}
-
-static DEVICE_ATTR(name, S_IRUGO, exynos4_tmu_show_name, NULL);
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, exynos4_tmu_show_temp, NULL, 0);
-
-static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO,
-		exynos4_tmu_show_alarm, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO,
-		exynos4_tmu_show_alarm, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp1_emergency_alarm, S_IRUGO,
-		exynos4_tmu_show_alarm, NULL, 3);
-
-static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, exynos4_tmu_show_level, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, exynos4_tmu_show_level, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp1_emergency, S_IRUGO,
-		exynos4_tmu_show_level, NULL, 3);
-
-static struct attribute *exynos4_tmu_attributes[] = {
-	&dev_attr_name.attr,
-	&sensor_dev_attr_temp1_input.dev_attr.attr,
-	&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
-	&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
-	&sensor_dev_attr_temp1_emergency_alarm.dev_attr.attr,
-	&sensor_dev_attr_temp1_max.dev_attr.attr,
-	&sensor_dev_attr_temp1_crit.dev_attr.attr,
-	&sensor_dev_attr_temp1_emergency.dev_attr.attr,
-	NULL,
-};
-
-static const struct attribute_group exynos4_tmu_attr_group = {
-	.attrs = exynos4_tmu_attributes,
+#ifdef CONFIG_SAMSUNG_THERMAL_INTERFACE
+static struct thermal_sensor_conf exynos4_sensor_conf = {
+	.name			= "exynos4-therm",
+	.read_temperature	= (int (*)(void *))exynos4_tmu_read,
 };
+#endif
+/*CONFIG_SAMSUNG_THERMAL_INTERFACE*/
 
 static int __devinit exynos4_tmu_probe(struct platform_device *pdev)
 {
 	struct exynos4_tmu_data *data;
 	struct exynos4_tmu_platform_data *pdata = pdev->dev.platform_data;
-	int ret;
+	int ret, i;
 
 	if (!pdata) {
 		dev_err(&pdev->dev, "No platform init data supplied.\n");
@@ -418,25 +344,27 @@ static int __devinit exynos4_tmu_probe(struct platform_device *pdev)
 		goto err_clk;
 	}
 
-	ret = sysfs_create_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
+	exynos4_tmu_control(pdev, true);
+#ifdef CONFIG_SAMSUNG_THERMAL_INTERFACE
+	(&exynos4_sensor_conf)->private_data = data;
+	exynos4_sensor_conf.trip_data.trip_count = 3;
+	for (i = 0; i < exynos4_sensor_conf.trip_data.trip_count; i++)
+		exynos4_sensor_conf.trip_data.trip_val[i] =
+			pdata->threshold + pdata->trigger_levels[i + 1];
+
+	exynos4_sensor_conf.cooling_data.freq_pctg_count =
+						pdata->freq_tab_count;
+	for (i = 0; i < pdata->freq_tab_count; i++)
+		exynos4_sensor_conf.cooling_data.freq_data[i].freq_clip_pctg =
+					pdata->freq_tab[i].freq_clip_pctg;
+
+	ret = exynos4_register_thermal(&exynos4_sensor_conf);
 	if (ret) {
-		dev_err(&pdev->dev, "Failed to create sysfs group\n");
+		dev_err(&pdev->dev, "Failed to register thermal interface\n");
 		goto err_clk;
 	}
-
-	data->hwmon_dev = hwmon_device_register(&pdev->dev);
-	if (IS_ERR(data->hwmon_dev)) {
-		ret = PTR_ERR(data->hwmon_dev);
-		dev_err(&pdev->dev, "Failed to register hwmon device\n");
-		goto err_create_group;
-	}
-
-	exynos4_tmu_control(pdev, true);
-
+#endif
 	return 0;
-
-err_create_group:
-	sysfs_remove_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
 err_clk:
 	platform_set_drvdata(pdev, NULL);
 	clk_put(data->clk);
@@ -458,8 +386,9 @@ static int __devexit exynos4_tmu_remove(struct platform_device *pdev)
 
 	exynos4_tmu_control(pdev, false);
 
-	hwmon_device_unregister(data->hwmon_dev);
-	sysfs_remove_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
+#ifdef CONFIG_SAMSUNG_THERMAL_INTERFACE
+	exynos4_unregister_thermal();
+#endif
 
 	clk_put(data->clk);
 
diff --git a/include/linux/platform_data/exynos4_tmu.h b/include/linux/platform_data/exynos4_tmu.h
index 39e038c..642c508 100644
--- a/include/linux/platform_data/exynos4_tmu.h
+++ b/include/linux/platform_data/exynos4_tmu.h
@@ -21,6 +21,7 @@
 
 #ifndef _LINUX_EXYNOS4_TMU_H
 #define _LINUX_EXYNOS4_TMU_H
+#include <linux/cpu_cooling.h>
 
 enum calibration_type {
 	TYPE_ONE_POINT_TRIMMING,
@@ -64,6 +65,9 @@ enum calibration_type {
  *	in the positive-TC generator block
  *	0 <= reference_voltage <= 31
  * @cal_type: calibration type for temperature
+ * @freq_pctg_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.
  *
  * This structure is required for configuration of exynos4_tmu driver.
  */
@@ -79,5 +83,8 @@ struct exynos4_tmu_platform_data {
 	u8 reference_voltage;
 
 	enum calibration_type cal_type;
+
+	struct freq_pctg_table freq_tab[4];
+	unsigned int freq_tab_count;
 };
 #endif /* _LINUX_EXYNOS4_TMU_H */
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH 3/4] thermal: exynos4: Register the tmu sensor with the thermal interface layer
@ 2012-03-03 11:06   ` Amit Daniel Kachhap
  0 siblings, 0 replies; 43+ messages in thread
From: Amit Daniel Kachhap @ 2012-03-03 11:06 UTC (permalink / raw)
  To: linux-pm, linux-samsung-soc
  Cc: linux-kernel, mjg59, linux-acpi, lenb, linaro-dev, lm-sensors,
	amit.kachhap, eduardo.valentin, durgadoss.r, patches

Export and register information from the tmu temperature sensor to the samsung
exynos kernel thermal framework where different cooling devices and thermal
zone are binded. The exported information is based according to the data
structure thermal_sensor_conf present in exynos_thermal.h. HWMON sysfs
functions are removed as all of them are present in generic linux thermal layer.

Also the platform data structure is modified to pass frequency cooling
in percentages for each thermal level.

Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>
---
 Documentation/mfd/exynos4_tmu             |   35 +-------
 drivers/mfd/exynos4_tmu.c                 |  139 +++++++----------------------
 include/linux/platform_data/exynos4_tmu.h |    7 ++
 3 files changed, 44 insertions(+), 137 deletions(-)

diff --git a/Documentation/mfd/exynos4_tmu b/Documentation/mfd/exynos4_tmu
index c3c6b41..2b46f67 100644
--- a/Documentation/mfd/exynos4_tmu
+++ b/Documentation/mfd/exynos4_tmu
@@ -46,36 +46,7 @@ The threshold levels are defined as follows:
   The threshold and each trigger_level are set
   through the corresponding registers.
 
-When an interrupt occurs, this driver notify user space of
-one of four threshold levels for the interrupt
-through kobject_uevent_env and sysfs_notify functions.
+When an interrupt occurs, this driver notify kernel thermal framework
+with the function exynos4_report_trigger.
 Although an interrupt condition for level_0 can be set,
-it is not notified to user space through sysfs_notify function.
-
-Sysfs Interface
----------------
-name		name of the temperature sensor
-		RO
-
-temp1_input	temperature
-		RO
-
-temp1_max	temperature for level_1 interrupt
-		RO
-
-temp1_crit	temperature for level_2 interrupt
-		RO
-
-temp1_emergency	temperature for level_3 interrupt
-		RO
-
-temp1_max_alarm	alarm for level_1 interrupt
-		RO
-
-temp1_crit_alarm
-		alarm for level_2 interrupt
-		RO
-
-temp1_emergency_alarm
-		alarm for level_3 interrupt
-		RO
+it can be used to synchronize the cooling action.
diff --git a/drivers/mfd/exynos4_tmu.c b/drivers/mfd/exynos4_tmu.c
index f2359a0..e343923 100644
--- a/drivers/mfd/exynos4_tmu.c
+++ b/drivers/mfd/exynos4_tmu.c
@@ -33,10 +33,10 @@
 #include <linux/io.h>
 #include <linux/mutex.h>
 
-#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
-
 #include <linux/platform_data/exynos4_tmu.h>
+#ifdef CONFIG_SAMSUNG_THERMAL_INTERFACE
+#include <linux/exynos_thermal.h>
+#endif
 
 #define EXYNOS4_TMU_REG_TRIMINFO	0x0
 #define EXYNOS4_TMU_REG_CONTROL		0x20
@@ -70,7 +70,6 @@
 
 struct exynos4_tmu_data {
 	struct exynos4_tmu_platform_data *pdata;
-	struct device *hwmon_dev;
 	struct resource *mem;
 	void __iomem *base;
 	int irq;
@@ -246,12 +245,12 @@ static void exynos4_tmu_work(struct work_struct *work)
 
 	writel(EXYNOS4_TMU_INTCLEAR_VAL, data->base + EXYNOS4_TMU_REG_INTCLEAR);
 
-	kobject_uevent(&data->hwmon_dev->kobj, KOBJ_CHANGE);
-
-	enable_irq(data->irq);
-
 	clk_disable(data->clk);
 	mutex_unlock(&data->lock);
+#ifdef CONFIG_SAMSUNG_THERMAL_INTERFACE
+	exynos4_report_trigger();
+#endif
+	enable_irq(data->irq);
 }
 
 static irqreturn_t exynos4_tmu_irq(int irq, void *id)
@@ -264,92 +263,19 @@ static irqreturn_t exynos4_tmu_irq(int irq, void *id)
 	return IRQ_HANDLED;
 }
 
-static ssize_t exynos4_tmu_show_name(struct device *dev,
-		struct device_attribute *attr, char *buf)
-{
-	return sprintf(buf, "exynos4-tmu\n");
-}
-
-static ssize_t exynos4_tmu_show_temp(struct device *dev,
-		struct device_attribute *attr, char *buf)
-{
-	struct exynos4_tmu_data *data = dev_get_drvdata(dev);
-	int ret;
-
-	ret = exynos4_tmu_read(data);
-	if (ret < 0)
-		return ret;
-
-	/* convert from degree Celsius to millidegree Celsius */
-	return sprintf(buf, "%d\n", ret * 1000);
-}
-
-static ssize_t exynos4_tmu_show_alarm(struct device *dev,
-		struct device_attribute *devattr, char *buf)
-{
-	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-	struct exynos4_tmu_data *data = dev_get_drvdata(dev);
-	struct exynos4_tmu_platform_data *pdata = data->pdata;
-	int temp;
-	unsigned int trigger_level;
-
-	temp = exynos4_tmu_read(data);
-	if (temp < 0)
-		return temp;
-
-	trigger_level = pdata->threshold + pdata->trigger_levels[attr->index];
-
-	return sprintf(buf, "%d\n", !!(temp > trigger_level));
-}
-
-static ssize_t exynos4_tmu_show_level(struct device *dev,
-		struct device_attribute *devattr, char *buf)
-{
-	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-	struct exynos4_tmu_data *data = dev_get_drvdata(dev);
-	struct exynos4_tmu_platform_data *pdata = data->pdata;
-	unsigned int temp = pdata->threshold +
-			pdata->trigger_levels[attr->index];
-
-	return sprintf(buf, "%u\n", temp * 1000);
-}
-
-static DEVICE_ATTR(name, S_IRUGO, exynos4_tmu_show_name, NULL);
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, exynos4_tmu_show_temp, NULL, 0);
-
-static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO,
-		exynos4_tmu_show_alarm, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO,
-		exynos4_tmu_show_alarm, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp1_emergency_alarm, S_IRUGO,
-		exynos4_tmu_show_alarm, NULL, 3);
-
-static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, exynos4_tmu_show_level, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, exynos4_tmu_show_level, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp1_emergency, S_IRUGO,
-		exynos4_tmu_show_level, NULL, 3);
-
-static struct attribute *exynos4_tmu_attributes[] = {
-	&dev_attr_name.attr,
-	&sensor_dev_attr_temp1_input.dev_attr.attr,
-	&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
-	&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
-	&sensor_dev_attr_temp1_emergency_alarm.dev_attr.attr,
-	&sensor_dev_attr_temp1_max.dev_attr.attr,
-	&sensor_dev_attr_temp1_crit.dev_attr.attr,
-	&sensor_dev_attr_temp1_emergency.dev_attr.attr,
-	NULL,
-};
-
-static const struct attribute_group exynos4_tmu_attr_group = {
-	.attrs = exynos4_tmu_attributes,
+#ifdef CONFIG_SAMSUNG_THERMAL_INTERFACE
+static struct thermal_sensor_conf exynos4_sensor_conf = {
+	.name			= "exynos4-therm",
+	.read_temperature	= (int (*)(void *))exynos4_tmu_read,
 };
+#endif
+/*CONFIG_SAMSUNG_THERMAL_INTERFACE*/
 
 static int __devinit exynos4_tmu_probe(struct platform_device *pdev)
 {
 	struct exynos4_tmu_data *data;
 	struct exynos4_tmu_platform_data *pdata = pdev->dev.platform_data;
-	int ret;
+	int ret, i;
 
 	if (!pdata) {
 		dev_err(&pdev->dev, "No platform init data supplied.\n");
@@ -418,25 +344,27 @@ static int __devinit exynos4_tmu_probe(struct platform_device *pdev)
 		goto err_clk;
 	}
 
-	ret = sysfs_create_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
+	exynos4_tmu_control(pdev, true);
+#ifdef CONFIG_SAMSUNG_THERMAL_INTERFACE
+	(&exynos4_sensor_conf)->private_data = data;
+	exynos4_sensor_conf.trip_data.trip_count = 3;
+	for (i = 0; i < exynos4_sensor_conf.trip_data.trip_count; i++)
+		exynos4_sensor_conf.trip_data.trip_val[i] =
+			pdata->threshold + pdata->trigger_levels[i + 1];
+
+	exynos4_sensor_conf.cooling_data.freq_pctg_count =
+						pdata->freq_tab_count;
+	for (i = 0; i < pdata->freq_tab_count; i++)
+		exynos4_sensor_conf.cooling_data.freq_data[i].freq_clip_pctg =
+					pdata->freq_tab[i].freq_clip_pctg;
+
+	ret = exynos4_register_thermal(&exynos4_sensor_conf);
 	if (ret) {
-		dev_err(&pdev->dev, "Failed to create sysfs group\n");
+		dev_err(&pdev->dev, "Failed to register thermal interface\n");
 		goto err_clk;
 	}
-
-	data->hwmon_dev = hwmon_device_register(&pdev->dev);
-	if (IS_ERR(data->hwmon_dev)) {
-		ret = PTR_ERR(data->hwmon_dev);
-		dev_err(&pdev->dev, "Failed to register hwmon device\n");
-		goto err_create_group;
-	}
-
-	exynos4_tmu_control(pdev, true);
-
+#endif
 	return 0;
-
-err_create_group:
-	sysfs_remove_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
 err_clk:
 	platform_set_drvdata(pdev, NULL);
 	clk_put(data->clk);
@@ -458,8 +386,9 @@ static int __devexit exynos4_tmu_remove(struct platform_device *pdev)
 
 	exynos4_tmu_control(pdev, false);
 
-	hwmon_device_unregister(data->hwmon_dev);
-	sysfs_remove_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
+#ifdef CONFIG_SAMSUNG_THERMAL_INTERFACE
+	exynos4_unregister_thermal();
+#endif
 
 	clk_put(data->clk);
 
diff --git a/include/linux/platform_data/exynos4_tmu.h b/include/linux/platform_data/exynos4_tmu.h
index 39e038c..642c508 100644
--- a/include/linux/platform_data/exynos4_tmu.h
+++ b/include/linux/platform_data/exynos4_tmu.h
@@ -21,6 +21,7 @@
 
 #ifndef _LINUX_EXYNOS4_TMU_H
 #define _LINUX_EXYNOS4_TMU_H
+#include <linux/cpu_cooling.h>
 
 enum calibration_type {
 	TYPE_ONE_POINT_TRIMMING,
@@ -64,6 +65,9 @@ enum calibration_type {
  *	in the positive-TC generator block
  *	0 <= reference_voltage <= 31
  * @cal_type: calibration type for temperature
+ * @freq_pctg_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.
  *
  * This structure is required for configuration of exynos4_tmu driver.
  */
@@ -79,5 +83,8 @@ struct exynos4_tmu_platform_data {
 	u8 reference_voltage;
 
 	enum calibration_type cal_type;
+
+	struct freq_pctg_table freq_tab[4];
+	unsigned int freq_tab_count;
 };
 #endif /* _LINUX_EXYNOS4_TMU_H */
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH 4/4] ARM: exynos4: Add thermal sensor driver platform device support
  2012-03-03 11:06 ` Amit Daniel Kachhap
  (?)
@ 2012-03-03 11:06   ` Amit Daniel Kachhap
  -1 siblings, 0 replies; 43+ messages in thread
From: Amit Daniel Kachhap @ 2012-03-03 11:06 UTC (permalink / raw)
  To: linux-pm, linux-samsung-soc
  Cc: linaro-dev, patches, linux-kernel, lm-sensors, linux-acpi

This patch adds necessary source definations needed for TMU driver and
the platform device support.

Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>
---
 arch/arm/mach-exynos/Kconfig              |   11 +++++
 arch/arm/mach-exynos/Makefile             |    1 +
 arch/arm/mach-exynos/clock.c              |    4 ++
 arch/arm/mach-exynos/dev-tmu.c            |   64 +++++++++++++++++++++++++++++
 arch/arm/mach-exynos/include/mach/irqs.h  |    2 +
 arch/arm/mach-exynos/include/mach/map.h   |    1 +
 arch/arm/mach-exynos/mach-origen.c        |    1 +
 arch/arm/plat-samsung/include/plat/devs.h |    1 +
 8 files changed, 85 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-exynos/dev-tmu.c

diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig
index 5d602f6..03968a6 100644
--- a/arch/arm/mach-exynos/Kconfig
+++ b/arch/arm/mach-exynos/Kconfig
@@ -160,6 +160,16 @@ config EXYNOS4_SETUP_SPI
 	help
 	  Common setup code for SPI GPIO configurations.
 
+config EXYNOS4_DEV_TMU
+	bool "Exynos4 tmu device support"
+	default n
+	depends on ARCH_EXYNOS4
+	---help---
+	  Compile in platform device definitions for TMU. This macro also
+	  enables compilation hwmon base TMU driver and also allows compilation
+	  of the platform device files. The platform data in this case is trip
+	  temperature and some tmu h/w configurations related parameter.
+
 # machine support
 
 if ARCH_EXYNOS4
@@ -199,6 +209,7 @@ config MACH_SMDKV310
 	select SAMSUNG_DEV_PWM
 	select EXYNOS4_DEV_USB_OHCI
 	select EXYNOS4_DEV_SYSMMU
+	select EXYNOS4_DEV_TMU
 	select EXYNOS4_SETUP_FIMD0
 	select EXYNOS4_SETUP_I2C1
 	select EXYNOS4_SETUP_KEYPAD
diff --git a/arch/arm/mach-exynos/Makefile b/arch/arm/mach-exynos/Makefile
index 5fc202c..9b62e69 100644
--- a/arch/arm/mach-exynos/Makefile
+++ b/arch/arm/mach-exynos/Makefile
@@ -50,6 +50,7 @@ obj-$(CONFIG_EXYNOS4_DEV_SYSMMU)	+= dev-sysmmu.o
 obj-$(CONFIG_EXYNOS4_DEV_DWMCI)		+= dev-dwmci.o
 obj-$(CONFIG_EXYNOS4_DEV_DMA)		+= dma.o
 obj-$(CONFIG_EXYNOS4_DEV_USB_OHCI)	+= dev-ohci.o
+obj-$(CONFIG_EXYNOS4_DEV_TMU)		+= dev-tmu.o
 
 obj-$(CONFIG_ARCH_EXYNOS4)		+= setup-i2c0.o
 obj-$(CONFIG_EXYNOS4_SETUP_FIMC)	+= setup-fimc.o
diff --git a/arch/arm/mach-exynos/clock.c b/arch/arm/mach-exynos/clock.c
index 187287a..3b15397 100644
--- a/arch/arm/mach-exynos/clock.c
+++ b/arch/arm/mach-exynos/clock.c
@@ -560,6 +560,10 @@ static struct clk init_clocks_off[] = {
 		.enable		= exynos4_clk_ip_peril_ctrl,
 		.ctrlbit	= (1 << 15),
 	}, {
+		.name		= "tmu_apbif",
+		.enable		= exynos4_clk_ip_perir_ctrl,
+		.ctrlbit	= (1 << 17),
+	}, {
 		.name		= "keypad",
 		.enable		= exynos4_clk_ip_perir_ctrl,
 		.ctrlbit	= (1 << 16),
diff --git a/arch/arm/mach-exynos/dev-tmu.c b/arch/arm/mach-exynos/dev-tmu.c
new file mode 100644
index 0000000..317b321
--- /dev/null
+++ b/arch/arm/mach-exynos/dev-tmu.c
@@ -0,0 +1,64 @@
+/* linux/arch/arm/mach-exynos4/dev-tmu.c
+ *
+ * Copyright 2011 by SAMSUNG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/platform_data/exynos4_tmu.h>
+#include <asm/irq.h>
+
+#include <mach/irqs.h>
+#include <mach/map.h>
+#include <plat/devs.h>
+
+static struct resource exynos4_tmu_resource[] = {
+	[0] = {
+		.start	= EXYNOS4_PA_TMU,
+		.end	= EXYNOS4_PA_TMU + 0xFFFF - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= IRQ_TMU_TRIG0,
+		.end	= IRQ_TMU_TRIG0,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct exynos4_tmu_platform_data default_tmu_data = {
+	.threshold = 80,
+	.trigger_levels[0] = 2,
+	.trigger_levels[1] = 5,
+	.trigger_levels[2] = 20,
+	.trigger_levels[3] = 30,
+	.trigger_level0_en = 1,
+	.trigger_level1_en = 1,
+	.trigger_level2_en = 1,
+	.trigger_level3_en = 1,
+	.gain = 15,
+	.reference_voltage = 7,
+	.cal_type = TYPE_ONE_POINT_TRIMMING,
+	.freq_tab[0] = {
+		.freq_clip_pctg = 30,
+		},
+	.freq_tab[1] = {
+		.freq_clip_pctg = 99,
+		},
+	.freq_tab_count = 2,
+};
+
+struct platform_device exynos4_device_tmu = {
+	.name		= "exynos4-tmu",
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(exynos4_tmu_resource),
+	.resource	= exynos4_tmu_resource,
+	.dev	= {
+		.platform_data	= &default_tmu_data,
+	},
+};
diff --git a/arch/arm/mach-exynos/include/mach/irqs.h b/arch/arm/mach-exynos/include/mach/irqs.h
index f77bce0..f98d2e4 100644
--- a/arch/arm/mach-exynos/include/mach/irqs.h
+++ b/arch/arm/mach-exynos/include/mach/irqs.h
@@ -128,6 +128,8 @@
 #define COMBINER_GROUP(x)	((x) * MAX_IRQ_IN_COMBINER + IRQ_SPI(128))
 #define COMBINER_IRQ(x, y)	(COMBINER_GROUP(x) + y)
 
+#define IRQ_TMU_TRIG0		COMBINER_IRQ(2, 4)
+#define IRQ_TMU_TRIG1		COMBINER_IRQ(3, 4)
 #define IRQ_SYSMMU_MDMA0_0	COMBINER_IRQ(4, 0)
 #define IRQ_SYSMMU_SSS_0	COMBINER_IRQ(4, 1)
 #define IRQ_SYSMMU_FIMC0_0	COMBINER_IRQ(4, 2)
diff --git a/arch/arm/mach-exynos/include/mach/map.h b/arch/arm/mach-exynos/include/mach/map.h
index c754a22..bc11f1f 100644
--- a/arch/arm/mach-exynos/include/mach/map.h
+++ b/arch/arm/mach-exynos/include/mach/map.h
@@ -66,6 +66,7 @@
 #define EXYNOS4_PA_COREPERI		0x10500000
 #define EXYNOS4_PA_TWD			0x10500600
 #define EXYNOS4_PA_L2CC			0x10502000
+#define EXYNOS4_PA_TMU			0x100C0000
 
 #define EXYNOS4_PA_MDMA			0x10810000
 #define EXYNOS4_PA_PDMA0		0x12680000
diff --git a/arch/arm/mach-exynos/mach-origen.c b/arch/arm/mach-exynos/mach-origen.c
index 0679b8a..5d56e53 100644
--- a/arch/arm/mach-exynos/mach-origen.c
+++ b/arch/arm/mach-exynos/mach-origen.c
@@ -630,6 +630,7 @@ static struct platform_device *origen_devices[] __initdata = {
 	&exynos4_device_pd[PD_MFC],
 	&origen_device_gpiokeys,
 	&origen_lcd_hv070wsa,
+	&exynos4_device_tmu,
 };
 
 /* LCD Backlight data */
diff --git a/arch/arm/plat-samsung/include/plat/devs.h b/arch/arm/plat-samsung/include/plat/devs.h
index 4214ea0..0960405 100644
--- a/arch/arm/plat-samsung/include/plat/devs.h
+++ b/arch/arm/plat-samsung/include/plat/devs.h
@@ -130,6 +130,7 @@ extern struct platform_device exynos4_device_pcm2;
 extern struct platform_device exynos4_device_pd[];
 extern struct platform_device exynos4_device_spdif;
 extern struct platform_device exynos4_device_sysmmu;
+extern struct platform_device exynos4_device_tmu;
 
 extern struct platform_device samsung_asoc_dma;
 extern struct platform_device samsung_asoc_idma;
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [PATCH 4/4] ARM: exynos4: Add thermal sensor driver platform device support
@ 2012-03-03 11:06   ` Amit Daniel Kachhap
  0 siblings, 0 replies; 43+ messages in thread
From: Amit Daniel Kachhap @ 2012-03-03 11:06 UTC (permalink / raw)
  To: linux-pm, linux-samsung-soc
  Cc: linux-kernel, mjg59, linux-acpi, lenb, linaro-dev, lm-sensors,
	amit.kachhap, eduardo.valentin, durgadoss.r, patches

This patch adds necessary source definations needed for TMU driver and
the platform device support.

Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>
---
 arch/arm/mach-exynos/Kconfig              |   11 +++++
 arch/arm/mach-exynos/Makefile             |    1 +
 arch/arm/mach-exynos/clock.c              |    4 ++
 arch/arm/mach-exynos/dev-tmu.c            |   64 +++++++++++++++++++++++++++++
 arch/arm/mach-exynos/include/mach/irqs.h  |    2 +
 arch/arm/mach-exynos/include/mach/map.h   |    1 +
 arch/arm/mach-exynos/mach-origen.c        |    1 +
 arch/arm/plat-samsung/include/plat/devs.h |    1 +
 8 files changed, 85 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-exynos/dev-tmu.c

diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig
index 5d602f6..03968a6 100644
--- a/arch/arm/mach-exynos/Kconfig
+++ b/arch/arm/mach-exynos/Kconfig
@@ -160,6 +160,16 @@ config EXYNOS4_SETUP_SPI
 	help
 	  Common setup code for SPI GPIO configurations.
 
+config EXYNOS4_DEV_TMU
+	bool "Exynos4 tmu device support"
+	default n
+	depends on ARCH_EXYNOS4
+	---help---
+	  Compile in platform device definitions for TMU. This macro also
+	  enables compilation hwmon base TMU driver and also allows compilation
+	  of the platform device files. The platform data in this case is trip
+	  temperature and some tmu h/w configurations related parameter.
+
 # machine support
 
 if ARCH_EXYNOS4
@@ -199,6 +209,7 @@ config MACH_SMDKV310
 	select SAMSUNG_DEV_PWM
 	select EXYNOS4_DEV_USB_OHCI
 	select EXYNOS4_DEV_SYSMMU
+	select EXYNOS4_DEV_TMU
 	select EXYNOS4_SETUP_FIMD0
 	select EXYNOS4_SETUP_I2C1
 	select EXYNOS4_SETUP_KEYPAD
diff --git a/arch/arm/mach-exynos/Makefile b/arch/arm/mach-exynos/Makefile
index 5fc202c..9b62e69 100644
--- a/arch/arm/mach-exynos/Makefile
+++ b/arch/arm/mach-exynos/Makefile
@@ -50,6 +50,7 @@ obj-$(CONFIG_EXYNOS4_DEV_SYSMMU)	+= dev-sysmmu.o
 obj-$(CONFIG_EXYNOS4_DEV_DWMCI)		+= dev-dwmci.o
 obj-$(CONFIG_EXYNOS4_DEV_DMA)		+= dma.o
 obj-$(CONFIG_EXYNOS4_DEV_USB_OHCI)	+= dev-ohci.o
+obj-$(CONFIG_EXYNOS4_DEV_TMU)		+= dev-tmu.o
 
 obj-$(CONFIG_ARCH_EXYNOS4)		+= setup-i2c0.o
 obj-$(CONFIG_EXYNOS4_SETUP_FIMC)	+= setup-fimc.o
diff --git a/arch/arm/mach-exynos/clock.c b/arch/arm/mach-exynos/clock.c
index 187287a..3b15397 100644
--- a/arch/arm/mach-exynos/clock.c
+++ b/arch/arm/mach-exynos/clock.c
@@ -560,6 +560,10 @@ static struct clk init_clocks_off[] = {
 		.enable		= exynos4_clk_ip_peril_ctrl,
 		.ctrlbit	= (1 << 15),
 	}, {
+		.name		= "tmu_apbif",
+		.enable		= exynos4_clk_ip_perir_ctrl,
+		.ctrlbit	= (1 << 17),
+	}, {
 		.name		= "keypad",
 		.enable		= exynos4_clk_ip_perir_ctrl,
 		.ctrlbit	= (1 << 16),
diff --git a/arch/arm/mach-exynos/dev-tmu.c b/arch/arm/mach-exynos/dev-tmu.c
new file mode 100644
index 0000000..317b321
--- /dev/null
+++ b/arch/arm/mach-exynos/dev-tmu.c
@@ -0,0 +1,64 @@
+/* linux/arch/arm/mach-exynos4/dev-tmu.c
+ *
+ * Copyright 2011 by SAMSUNG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/platform_data/exynos4_tmu.h>
+#include <asm/irq.h>
+
+#include <mach/irqs.h>
+#include <mach/map.h>
+#include <plat/devs.h>
+
+static struct resource exynos4_tmu_resource[] = {
+	[0] = {
+		.start	= EXYNOS4_PA_TMU,
+		.end	= EXYNOS4_PA_TMU + 0xFFFF - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= IRQ_TMU_TRIG0,
+		.end	= IRQ_TMU_TRIG0,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct exynos4_tmu_platform_data default_tmu_data = {
+	.threshold = 80,
+	.trigger_levels[0] = 2,
+	.trigger_levels[1] = 5,
+	.trigger_levels[2] = 20,
+	.trigger_levels[3] = 30,
+	.trigger_level0_en = 1,
+	.trigger_level1_en = 1,
+	.trigger_level2_en = 1,
+	.trigger_level3_en = 1,
+	.gain = 15,
+	.reference_voltage = 7,
+	.cal_type = TYPE_ONE_POINT_TRIMMING,
+	.freq_tab[0] = {
+		.freq_clip_pctg = 30,
+		},
+	.freq_tab[1] = {
+		.freq_clip_pctg = 99,
+		},
+	.freq_tab_count = 2,
+};
+
+struct platform_device exynos4_device_tmu = {
+	.name		= "exynos4-tmu",
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(exynos4_tmu_resource),
+	.resource	= exynos4_tmu_resource,
+	.dev	= {
+		.platform_data	= &default_tmu_data,
+	},
+};
diff --git a/arch/arm/mach-exynos/include/mach/irqs.h b/arch/arm/mach-exynos/include/mach/irqs.h
index f77bce0..f98d2e4 100644
--- a/arch/arm/mach-exynos/include/mach/irqs.h
+++ b/arch/arm/mach-exynos/include/mach/irqs.h
@@ -128,6 +128,8 @@
 #define COMBINER_GROUP(x)	((x) * MAX_IRQ_IN_COMBINER + IRQ_SPI(128))
 #define COMBINER_IRQ(x, y)	(COMBINER_GROUP(x) + y)
 
+#define IRQ_TMU_TRIG0		COMBINER_IRQ(2, 4)
+#define IRQ_TMU_TRIG1		COMBINER_IRQ(3, 4)
 #define IRQ_SYSMMU_MDMA0_0	COMBINER_IRQ(4, 0)
 #define IRQ_SYSMMU_SSS_0	COMBINER_IRQ(4, 1)
 #define IRQ_SYSMMU_FIMC0_0	COMBINER_IRQ(4, 2)
diff --git a/arch/arm/mach-exynos/include/mach/map.h b/arch/arm/mach-exynos/include/mach/map.h
index c754a22..bc11f1f 100644
--- a/arch/arm/mach-exynos/include/mach/map.h
+++ b/arch/arm/mach-exynos/include/mach/map.h
@@ -66,6 +66,7 @@
 #define EXYNOS4_PA_COREPERI		0x10500000
 #define EXYNOS4_PA_TWD			0x10500600
 #define EXYNOS4_PA_L2CC			0x10502000
+#define EXYNOS4_PA_TMU			0x100C0000
 
 #define EXYNOS4_PA_MDMA			0x10810000
 #define EXYNOS4_PA_PDMA0		0x12680000
diff --git a/arch/arm/mach-exynos/mach-origen.c b/arch/arm/mach-exynos/mach-origen.c
index 0679b8a..5d56e53 100644
--- a/arch/arm/mach-exynos/mach-origen.c
+++ b/arch/arm/mach-exynos/mach-origen.c
@@ -630,6 +630,7 @@ static struct platform_device *origen_devices[] __initdata = {
 	&exynos4_device_pd[PD_MFC],
 	&origen_device_gpiokeys,
 	&origen_lcd_hv070wsa,
+	&exynos4_device_tmu,
 };
 
 /* LCD Backlight data */
diff --git a/arch/arm/plat-samsung/include/plat/devs.h b/arch/arm/plat-samsung/include/plat/devs.h
index 4214ea0..0960405 100644
--- a/arch/arm/plat-samsung/include/plat/devs.h
+++ b/arch/arm/plat-samsung/include/plat/devs.h
@@ -130,6 +130,7 @@ extern struct platform_device exynos4_device_pcm2;
 extern struct platform_device exynos4_device_pd[];
 extern struct platform_device exynos4_device_spdif;
 extern struct platform_device exynos4_device_sysmmu;
+extern struct platform_device exynos4_device_tmu;
 
 extern struct platform_device samsung_asoc_dma;
 extern struct platform_device samsung_asoc_idma;
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [lm-sensors] [PATCH 0/4] thermal: exynos: Add kernel thermal support for exynos platform
@ 2012-03-03 11:06 ` Amit Daniel Kachhap
  0 siblings, 0 replies; 43+ messages in thread
From: Amit Daniel Kachhap @ 2012-03-03 11:18 UTC (permalink / raw)
  To: linux-pm, linux-samsung-soc
  Cc: linaro-dev, patches, linux-kernel, lm-sensors, linux-acpi

Changes since RFC:
*Moved the Temperature sensor driver from driver/hwmon/ to driver/mfd
 as discussed with Guenter Roeck <guenter.roeck@ericsson.com> and 
 Donggeun Kim <dg77.kim@samsung.com> (https://lkml.org/lkml/2012/1/5/7)
*Some changes according to the changes in common cpu cooling APIs

All the patchset based on Kernel version 3.3-rc5 and uses the cpufreq
cooling registration APIs implemented in earlier patchset 
https://lkml.org/lkml/2012/2/22/123

The code added in this patchset adds a thermal interface layer for samsung
exynos platforms. This layer is registered from the temperature sensor driver 
and recieves/monitor the temperature from the sensor and informs the
generic thermal layer to take the necessary cooling action. Currently, this
layer can be used to create only one thermal zone and hence only one
temperature sensor can register. The future goal is to make this handle
multiple thermal zones.

Some modifications are done in the temperature sensor driver to export the
information needed for the thermal interface to register with the core linux
thermal framework and with the cpu frequency based cooling devices.

A simple data/control flow diagrams to illustrate this,

Core Linux thermal <------->  Exynos thermal  <-------- Temperature Sensor
	  |                             |
	 \|/                            |
  Cpufreq cooling device <-----


Amit Daniel Kachhap (4):
  thermal: exynos: Add thermal interface support for linux thermal
    layer
  hwmon: exynos4: Move thermal sensor driver to driver/mfd directory
  thermal: exynos4: Register the tmu sensor with the thermal interface
    layer
  ARM: exynos4: Add thermal sensor driver platform device support

 Documentation/hwmon/exynos4_tmu           |   81 -----
 Documentation/mfd/exynos4_tmu             |   52 +++
 arch/arm/mach-exynos/Kconfig              |   11 +
 arch/arm/mach-exynos/Makefile             |    1 +
 arch/arm/mach-exynos/clock.c              |    4 +
 arch/arm/mach-exynos/dev-tmu.c            |   64 ++++
 arch/arm/mach-exynos/include/mach/irqs.h  |    2 +
 arch/arm/mach-exynos/include/mach/map.h   |    1 +
 arch/arm/mach-exynos/mach-origen.c        |    1 +
 arch/arm/plat-samsung/include/plat/devs.h |    1 +
 drivers/hwmon/Kconfig                     |   10 -
 drivers/hwmon/Makefile                    |    1 -
 drivers/hwmon/exynos4_tmu.c               |  514 -----------------------------
 drivers/mfd/Kconfig                       |   10 +
 drivers/mfd/Makefile                      |    1 +
 drivers/mfd/exynos4_tmu.c                 |  443 +++++++++++++++++++++++++
 drivers/thermal/Kconfig                   |    8 +
 drivers/thermal/Makefile                  |    1 +
 drivers/thermal/exynos_thermal.c          |  272 +++++++++++++++
 include/linux/exynos_thermal.h            |   72 ++++
 include/linux/platform_data/exynos4_tmu.h |    7 +
 21 files changed, 951 insertions(+), 606 deletions(-)
 delete mode 100644 Documentation/hwmon/exynos4_tmu
 create mode 100644 Documentation/mfd/exynos4_tmu
 create mode 100644 arch/arm/mach-exynos/dev-tmu.c
 delete mode 100644 drivers/hwmon/exynos4_tmu.c
 create mode 100644 drivers/mfd/exynos4_tmu.c
 create mode 100644 drivers/thermal/exynos_thermal.c
 create mode 100644 include/linux/exynos_thermal.h


_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 43+ messages in thread

* [lm-sensors] [PATCH 1/4] thermal: exynos: Add thermal interface support for linux thermal layer
@ 2012-03-03 11:06   ` Amit Daniel Kachhap
  0 siblings, 0 replies; 43+ messages in thread
From: Amit Daniel Kachhap @ 2012-03-03 11:18 UTC (permalink / raw)
  To: linux-pm, linux-samsung-soc
  Cc: linaro-dev, patches, linux-kernel, lm-sensors, linux-acpi

This codes uses the generic linux thermal layer and creates a bridge
between temperature sensors, linux thermal framework and cooling devices
for samsung exynos platform. This layer recieves or monitor the
temperature from the sensor and informs the generic thermal layer to take
the necessary cooling action.

Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>
---
 drivers/thermal/Kconfig          |    8 +
 drivers/thermal/Makefile         |    1 +
 drivers/thermal/exynos_thermal.c |  272 ++++++++++++++++++++++++++++++++++++++
 include/linux/exynos_thermal.h   |   72 ++++++++++
 4 files changed, 353 insertions(+), 0 deletions(-)
 create mode 100644 drivers/thermal/exynos_thermal.c
 create mode 100644 include/linux/exynos_thermal.h

diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 298c1cd..4e8df56 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -29,3 +29,11 @@ config CPU_THERMAL
 	  This will be useful for platforms using the generic thermal interface
 	  and not the ACPI interface.
 	  If you want this support, you should say Y or M here.
+
+config SAMSUNG_THERMAL_INTERFACE
+	bool "Samsung Thermal interface support"
+	depends on THERMAL && CPU_THERMAL
+	help
+	  This is a samsung thermal interface which will be used as
+	  a link between sensors and cooling devices with linux thermal
+	  framework.
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 655cbc4..c67b6b2 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -4,3 +4,4 @@
 
 obj-$(CONFIG_THERMAL)		+= thermal_sys.o
 obj-$(CONFIG_CPU_THERMAL)	+= cpu_cooling.o
+obj-$(CONFIG_SAMSUNG_THERMAL_INTERFACE)	+= exynos_thermal.o
diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c
new file mode 100644
index 0000000..878d45c
--- /dev/null
+++ b/drivers/thermal/exynos_thermal.c
@@ -0,0 +1,272 @@
+/* linux/drivers/thermal/exynos_thermal.c
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/thermal.h>
+#include <linux/platform_device.h>
+#include <linux/cpufreq.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/cpu_cooling.h>
+#include <linux/exynos_thermal.h>
+
+#define MAX_COOLING_DEVICE 4
+struct exynos4_thermal_zone {
+	unsigned int idle_interval;
+	unsigned int active_interval;
+	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;
+};
+
+static struct exynos4_thermal_zone *th_zone;
+
+static int exynos4_get_mode(struct thermal_zone_device *thermal,
+			    enum thermal_device_mode *mode)
+{
+	if (th_zone->sensor_conf) {
+		pr_info("Temperature sensor not initialised\n");
+		*mode = THERMAL_DEVICE_DISABLED;
+	} else
+		*mode = THERMAL_DEVICE_ENABLED;
+	return 0;
+}
+
+static int exynos4_set_mode(struct thermal_zone_device *thermal,
+			    enum thermal_device_mode mode)
+{
+	if (!th_zone->therm_dev) {
+		pr_notice("thermal zone not registered\n");
+		return 0;
+	}
+	if (mode = THERMAL_DEVICE_ENABLED)
+		th_zone->therm_dev->polling_delay +				th_zone->active_interval*1000;
+	else
+		th_zone->therm_dev->polling_delay +				th_zone->idle_interval*1000;
+
+	thermal_zone_device_update(th_zone->therm_dev);
+	pr_info("thermal polling set for duration=%d sec\n",
+				th_zone->therm_dev->polling_delay/1000);
+	return 0;
+}
+
+/*This may be called from interrupt based temperature sensor*/
+void exynos4_report_trigger(void)
+{
+	unsigned int monitor_temp;
+
+	if (!th_zone || !th_zone->therm_dev)
+		return;
+
+	monitor_temp = th_zone->sensor_conf->trip_data.trip_val[0];
+
+	thermal_zone_device_update(th_zone->therm_dev);
+
+	mutex_lock(&th_zone->therm_dev->lock);
+	if (th_zone->therm_dev->last_temperature > monitor_temp)
+		th_zone->therm_dev->polling_delay +					th_zone->active_interval*1000;
+	else
+		th_zone->therm_dev->polling_delay +					th_zone->idle_interval*1000;
+
+	kobject_uevent(&th_zone->therm_dev->device.kobj, KOBJ_CHANGE);
+	mutex_unlock(&th_zone->therm_dev->lock);
+}
+
+static int exynos4_get_trip_type(struct thermal_zone_device *thermal, int trip,
+				 enum thermal_trip_type *type)
+{
+	if (trip = 0 || trip = 1)
+		*type = THERMAL_TRIP_STATE_ACTIVE;
+	else if (trip = 2)
+		*type = THERMAL_TRIP_CRITICAL;
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+static int exynos4_get_trip_temp(struct thermal_zone_device *thermal, int trip,
+				 unsigned long *temp)
+{
+	/*Monitor zone*/
+	if (trip = 0)
+		*temp = th_zone->sensor_conf->trip_data.trip_val[0];
+	/*Warn zone*/
+	else if (trip = 1)
+		*temp = th_zone->sensor_conf->trip_data.trip_val[1];
+	/*Panic zone*/
+	else if (trip = 2)
+		*temp = th_zone->sensor_conf->trip_data.trip_val[2];
+	else
+		return -EINVAL;
+	/*convert the temperature into millicelsius*/
+	*temp = *temp * 1000;
+
+	return 0;
+}
+
+static int exynos4_get_crit_temp(struct thermal_zone_device *thermal,
+				 unsigned long *temp)
+{
+	/*Panic zone*/
+	*temp = th_zone->sensor_conf->trip_data.trip_val[2];
+	/*convert the temperature into millicelsius*/
+	*temp = *temp * 1000;
+	return 0;
+}
+
+static int exynos4_bind(struct thermal_zone_device *thermal,
+			struct thermal_cooling_device *cdev)
+{
+	/* if the cooling device is the one from exynos4 bind it */
+	if (cdev != th_zone->cool_dev[0])
+		return 0;
+
+	if (thermal_zone_bind_cooling_device(thermal, 0, cdev)) {
+		pr_err("error binding cooling dev\n");
+		return -EINVAL;
+	}
+	if (thermal_zone_bind_cooling_device(thermal, 1, cdev)) {
+		pr_err("error binding cooling dev\n");
+		return -EINVAL;
+	}
+
+	return 0;
+
+}
+
+static int exynos4_unbind(struct thermal_zone_device *thermal,
+			  struct thermal_cooling_device *cdev)
+{
+	if (cdev != th_zone->cool_dev[0])
+		return 0;
+
+	if (thermal_zone_unbind_cooling_device(thermal, 0, cdev)) {
+		pr_err("error unbinding cooling dev\n");
+		return -EINVAL;
+	}
+	if (thermal_zone_unbind_cooling_device(thermal, 1, cdev)) {
+		pr_err("error unbinding cooling dev\n");
+		return -EINVAL;
+	}
+	return 0;
+
+}
+
+static int exynos4_get_temp(struct thermal_zone_device *thermal,
+			       unsigned long *temp)
+{
+	void *data;
+
+	if (!th_zone->sensor_conf) {
+		pr_info("Temperature sensor not initialised\n");
+		return -EINVAL;
+	}
+	data = th_zone->sensor_conf->private_data;
+	*temp = th_zone->sensor_conf->read_temperature(data);
+	/*convert the temperature into millicelsius*/
+	*temp = *temp * 1000;
+	return 0;
+}
+
+/* bind callback functions to thermalzone */
+static struct thermal_zone_device_ops exynos4_dev_ops = {
+	.bind = exynos4_bind,
+	.unbind = exynos4_unbind,
+	.get_temp = exynos4_get_temp,
+	.get_mode = exynos4_get_mode,
+	.set_mode = exynos4_set_mode,
+	.get_trip_type = exynos4_get_trip_type,
+	.get_trip_temp = exynos4_get_trip_temp,
+	.get_crit_temp = exynos4_get_crit_temp,
+};
+
+int exynos4_register_thermal(struct thermal_sensor_conf *sensor_conf)
+{
+	int ret, count, tab_size;
+	struct freq_pctg_table *tab_ptr;
+
+	if (!sensor_conf || !sensor_conf->read_temperature) {
+		pr_err("Temperature sensor not initialised\n");
+		return -EINVAL;
+	}
+
+	th_zone = kzalloc(sizeof(struct exynos4_thermal_zone), GFP_KERNEL);
+	if (!th_zone) {
+		ret = -ENOMEM;
+		goto err_unregister;
+	}
+
+	th_zone->sensor_conf = sensor_conf;
+
+	tab_ptr = (struct freq_pctg_table *)sensor_conf->cooling_data.freq_data;
+	tab_size = sensor_conf->cooling_data.freq_pctg_count;
+
+	/*Register the cpufreq cooling device*/
+	th_zone->cool_dev_size = 1;
+	count = 0;
+	th_zone->cool_dev[count] = cpufreq_cooling_register(
+			(struct freq_pctg_table *)&(tab_ptr[count]),
+			tab_size, cpumask_of(0));
+
+	if (IS_ERR(th_zone->cool_dev[count])) {
+		pr_err("Failed to register cpufreq cooling device\n");
+		ret = -EINVAL;
+		th_zone->cool_dev_size = 0;
+		goto err_unregister;
+	}
+
+	th_zone->therm_dev = thermal_zone_device_register(sensor_conf->name,
+				3, NULL, &exynos4_dev_ops, 0, 0, 0, 1000);
+	if (IS_ERR(th_zone->therm_dev)) {
+		pr_err("Failed to register thermal zone device\n");
+		ret = -EINVAL;
+		goto err_unregister;
+	}
+
+	th_zone->active_interval = 1;
+	th_zone->idle_interval = 10;
+
+	exynos4_set_mode(th_zone->therm_dev, THERMAL_DEVICE_DISABLED);
+
+	pr_info("Exynos: Kernel Thermal management registered\n");
+
+	return 0;
+
+err_unregister:
+	exynos4_unregister_thermal();
+	return ret;
+}
+EXPORT_SYMBOL(exynos4_register_thermal);
+
+void exynos4_unregister_thermal(void)
+{
+	unsigned int i;
+
+	for (i = 0; i < th_zone->cool_dev_size; i++) {
+		if (th_zone && th_zone->cool_dev[i])
+			cpufreq_cooling_unregister(th_zone->cool_dev[i]);
+	}
+
+	if (th_zone && th_zone->therm_dev)
+		thermal_zone_device_unregister(th_zone->therm_dev);
+
+	kfree(th_zone);
+
+	pr_info("Exynos: Kernel Thermal management unregistered\n");
+}
+EXPORT_SYMBOL(exynos4_unregister_thermal);
diff --git a/include/linux/exynos_thermal.h b/include/linux/exynos_thermal.h
new file mode 100644
index 0000000..186e409
--- /dev/null
+++ b/include/linux/exynos_thermal.h
@@ -0,0 +1,72 @@
+/* linux/include/linux/exynos_thermal.h
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef THERMAL_INTERFACE_H
+#define THERMAL_INTERFACE_H
+/* CPU Zone information */
+
+#define SENSOR_NAME_LEN	16
+#define MAX_TRIP_COUNT	8
+
+#define PANIC_ZONE      4
+#define WARN_ZONE       3
+#define MONITOR_ZONE    2
+#define SAFE_ZONE       1
+#define NO_ACTION       0
+
+
+struct	thermal_trip_point_conf {
+	int trip_val[MAX_TRIP_COUNT];
+	int trip_count;
+};
+
+struct	thermal_cooling_conf {
+	struct freq_pctg_table freq_data[MAX_TRIP_COUNT];
+	int freq_pctg_count;
+};
+
+/**
+ * struct exynos4_tmu_platform_data
+ * @name: name of the temperature sensor
+ * @read_temperature: A function pointer to read temperature info
+ * @private_data: Temperature sensor private data
+ * @sensor_data: Sensor specific information like trigger temperature, level
+ */
+struct thermal_sensor_conf {
+	char	name[SENSOR_NAME_LEN];
+	int	(*read_temperature)(void *data);
+	struct	thermal_trip_point_conf trip_data;
+	struct	thermal_cooling_conf cooling_data;
+	void	*private_data;
+};
+
+/**
+ * exynos4_register_thermal: Register to the exynos thermal interface.
+ * @sensor_conf:   Structure containing temperature sensor information
+ *
+ * returns zero on success, else negative errno.
+ */
+int exynos4_register_thermal(struct thermal_sensor_conf *sensor_conf);
+
+/**
+ * exynos4_unregister_thermal: Un-register from the exynos thermal interface.
+ *
+ * return not applicable.
+ */
+void exynos4_unregister_thermal(void);
+
+/**
+ * exynos4_report_trigger: Report any trigger level crossed in the
+ *	temperature sensor. This may be useful to take any cooling action.
+ *
+ * return not applicable.
+ */
+extern void exynos4_report_trigger(void);
+#endif
-- 
1.7.1


_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [lm-sensors] [PATCH 2/4] hwmon: exynos4: Move thermal sensor driver to driver/mfd directory
@ 2012-03-03 11:06   ` Amit Daniel Kachhap
  0 siblings, 0 replies; 43+ messages in thread
From: Amit Daniel Kachhap @ 2012-03-03 11:18 UTC (permalink / raw)
  To: linux-pm, linux-samsung-soc
  Cc: linaro-dev, patches, linux-kernel, lm-sensors, linux-acpi

This movement is needed because the hwmon entries and corresponding
sysfs interface is a duplicate of utilities already provided by
driver/thermal/thermal_sys.c. The goal is to place it in mfd folder
and add necessary calls to get the temperature information.

Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>
Signed-off-by: Donggeun Kim <dg77.kim@samsung.com>
---
 Documentation/hwmon/exynos4_tmu |   81 ------
 Documentation/mfd/exynos4_tmu   |   81 ++++++
 drivers/hwmon/Kconfig           |   10 -
 drivers/hwmon/Makefile          |    1 -
 drivers/hwmon/exynos4_tmu.c     |  514 ---------------------------------------
 drivers/mfd/Kconfig             |   10 +
 drivers/mfd/Makefile            |    1 +
 drivers/mfd/exynos4_tmu.c       |  514 +++++++++++++++++++++++++++++++++++++++
 8 files changed, 606 insertions(+), 606 deletions(-)
 delete mode 100644 Documentation/hwmon/exynos4_tmu
 create mode 100644 Documentation/mfd/exynos4_tmu
 delete mode 100644 drivers/hwmon/exynos4_tmu.c
 create mode 100644 drivers/mfd/exynos4_tmu.c

diff --git a/Documentation/hwmon/exynos4_tmu b/Documentation/hwmon/exynos4_tmu
deleted file mode 100644
index c3c6b41..0000000
--- a/Documentation/hwmon/exynos4_tmu
+++ /dev/null
@@ -1,81 +0,0 @@
-Kernel driver exynos4_tmu
-========-
-Supported chips:
-* ARM SAMSUNG EXYNOS4 series of SoC
-  Prefix: 'exynos4-tmu'
-  Datasheet: Not publicly available
-
-Authors: Donggeun Kim <dg77.kim@samsung.com>
-
-Description
------------
-
-This driver allows to read temperature inside SAMSUNG EXYNOS4 series of SoC.
-
-The chip only exposes the measured 8-bit temperature code value
-through a register.
-Temperature can be taken from the temperature code.
-There are three equations converting from temperature to temperature code.
-
-The three equations are:
-  1. Two point trimming
-	Tc = (T - 25) * (TI2 - TI1) / (85 - 25) + TI1
-
-  2. One point trimming
-	Tc = T + TI1 - 25
-
-  3. No trimming
-	Tc = T + 50
-
-  Tc: Temperature code, T: Temperature,
-  TI1: Trimming info for 25 degree Celsius (stored at TRIMINFO register)
-       Temperature code measured at 25 degree Celsius which is unchanged
-  TI2: Trimming info for 85 degree Celsius (stored at TRIMINFO register)
-       Temperature code measured at 85 degree Celsius which is unchanged
-
-TMU(Thermal Management Unit) in EXYNOS4 generates interrupt
-when temperature exceeds pre-defined levels.
-The maximum number of configurable threshold is four.
-The threshold levels are defined as follows:
-  Level_0: current temperature > trigger_level_0 + threshold
-  Level_1: current temperature > trigger_level_1 + threshold
-  Level_2: current temperature > trigger_level_2 + threshold
-  Level_3: current temperature > trigger_level_3 + threshold
-
-  The threshold and each trigger_level are set
-  through the corresponding registers.
-
-When an interrupt occurs, this driver notify user space of
-one of four threshold levels for the interrupt
-through kobject_uevent_env and sysfs_notify functions.
-Although an interrupt condition for level_0 can be set,
-it is not notified to user space through sysfs_notify function.
-
-Sysfs Interface
----------------
-name		name of the temperature sensor
-		RO
-
-temp1_input	temperature
-		RO
-
-temp1_max	temperature for level_1 interrupt
-		RO
-
-temp1_crit	temperature for level_2 interrupt
-		RO
-
-temp1_emergency	temperature for level_3 interrupt
-		RO
-
-temp1_max_alarm	alarm for level_1 interrupt
-		RO
-
-temp1_crit_alarm
-		alarm for level_2 interrupt
-		RO
-
-temp1_emergency_alarm
-		alarm for level_3 interrupt
-		RO
diff --git a/Documentation/mfd/exynos4_tmu b/Documentation/mfd/exynos4_tmu
new file mode 100644
index 0000000..c3c6b41
--- /dev/null
+++ b/Documentation/mfd/exynos4_tmu
@@ -0,0 +1,81 @@
+Kernel driver exynos4_tmu
+========+
+Supported chips:
+* ARM SAMSUNG EXYNOS4 series of SoC
+  Prefix: 'exynos4-tmu'
+  Datasheet: Not publicly available
+
+Authors: Donggeun Kim <dg77.kim@samsung.com>
+
+Description
+-----------
+
+This driver allows to read temperature inside SAMSUNG EXYNOS4 series of SoC.
+
+The chip only exposes the measured 8-bit temperature code value
+through a register.
+Temperature can be taken from the temperature code.
+There are three equations converting from temperature to temperature code.
+
+The three equations are:
+  1. Two point trimming
+	Tc = (T - 25) * (TI2 - TI1) / (85 - 25) + TI1
+
+  2. One point trimming
+	Tc = T + TI1 - 25
+
+  3. No trimming
+	Tc = T + 50
+
+  Tc: Temperature code, T: Temperature,
+  TI1: Trimming info for 25 degree Celsius (stored at TRIMINFO register)
+       Temperature code measured at 25 degree Celsius which is unchanged
+  TI2: Trimming info for 85 degree Celsius (stored at TRIMINFO register)
+       Temperature code measured at 85 degree Celsius which is unchanged
+
+TMU(Thermal Management Unit) in EXYNOS4 generates interrupt
+when temperature exceeds pre-defined levels.
+The maximum number of configurable threshold is four.
+The threshold levels are defined as follows:
+  Level_0: current temperature > trigger_level_0 + threshold
+  Level_1: current temperature > trigger_level_1 + threshold
+  Level_2: current temperature > trigger_level_2 + threshold
+  Level_3: current temperature > trigger_level_3 + threshold
+
+  The threshold and each trigger_level are set
+  through the corresponding registers.
+
+When an interrupt occurs, this driver notify user space of
+one of four threshold levels for the interrupt
+through kobject_uevent_env and sysfs_notify functions.
+Although an interrupt condition for level_0 can be set,
+it is not notified to user space through sysfs_notify function.
+
+Sysfs Interface
+---------------
+name		name of the temperature sensor
+		RO
+
+temp1_input	temperature
+		RO
+
+temp1_max	temperature for level_1 interrupt
+		RO
+
+temp1_crit	temperature for level_2 interrupt
+		RO
+
+temp1_emergency	temperature for level_3 interrupt
+		RO
+
+temp1_max_alarm	alarm for level_1 interrupt
+		RO
+
+temp1_crit_alarm
+		alarm for level_2 interrupt
+		RO
+
+temp1_emergency_alarm
+		alarm for level_3 interrupt
+		RO
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 0226040..5c4a0fb 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -313,16 +313,6 @@ config SENSORS_DS1621
 	  This driver can also be built as a module.  If so, the module
 	  will be called ds1621.
 
-config SENSORS_EXYNOS4_TMU
-	tristate "Temperature sensor on Samsung EXYNOS4"
-	depends on ARCH_EXYNOS4
-	help
-	  If you say yes here you get support for TMU (Thermal Managment
-	  Unit) on SAMSUNG EXYNOS4 series of SoC.
-
-	  This driver can also be built as a module. If so, the module
-	  will be called exynos4-tmu.
-
 config SENSORS_I5K_AMB
 	tristate "FB-DIMM AMB temperature sensor on Intel 5000 series chipsets"
 	depends on PCI && EXPERIMENTAL
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 8251ce8..228a4b7 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -48,7 +48,6 @@ obj-$(CONFIG_SENSORS_DS1621)	+= ds1621.o
 obj-$(CONFIG_SENSORS_EMC1403)	+= emc1403.o
 obj-$(CONFIG_SENSORS_EMC2103)	+= emc2103.o
 obj-$(CONFIG_SENSORS_EMC6W201)	+= emc6w201.o
-obj-$(CONFIG_SENSORS_EXYNOS4_TMU)	+= exynos4_tmu.o
 obj-$(CONFIG_SENSORS_F71805F)	+= f71805f.o
 obj-$(CONFIG_SENSORS_F71882FG)	+= f71882fg.o
 obj-$(CONFIG_SENSORS_F75375S)	+= f75375s.o
diff --git a/drivers/hwmon/exynos4_tmu.c b/drivers/hwmon/exynos4_tmu.c
deleted file mode 100644
index f2359a0..0000000
--- a/drivers/hwmon/exynos4_tmu.c
+++ /dev/null
@@ -1,514 +0,0 @@
-/*
- * exynos4_tmu.c - Samsung EXYNOS4 TMU (Thermal Management Unit)
- *
- *  Copyright (C) 2011 Samsung Electronics
- *  Donggeun Kim <dg77.kim@samsung.com>
- *
- * 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/hwmon.h>
-#include <linux/hwmon-sysfs.h>
-
-#include <linux/platform_data/exynos4_tmu.h>
-
-#define EXYNOS4_TMU_REG_TRIMINFO	0x0
-#define EXYNOS4_TMU_REG_CONTROL		0x20
-#define EXYNOS4_TMU_REG_STATUS		0x28
-#define EXYNOS4_TMU_REG_CURRENT_TEMP	0x40
-#define EXYNOS4_TMU_REG_THRESHOLD_TEMP	0x44
-#define EXYNOS4_TMU_REG_TRIG_LEVEL0	0x50
-#define EXYNOS4_TMU_REG_TRIG_LEVEL1	0x54
-#define EXYNOS4_TMU_REG_TRIG_LEVEL2	0x58
-#define EXYNOS4_TMU_REG_TRIG_LEVEL3	0x5C
-#define EXYNOS4_TMU_REG_PAST_TEMP0	0x60
-#define EXYNOS4_TMU_REG_PAST_TEMP1	0x64
-#define EXYNOS4_TMU_REG_PAST_TEMP2	0x68
-#define EXYNOS4_TMU_REG_PAST_TEMP3	0x6C
-#define EXYNOS4_TMU_REG_INTEN		0x70
-#define EXYNOS4_TMU_REG_INTSTAT		0x74
-#define EXYNOS4_TMU_REG_INTCLEAR	0x78
-
-#define EXYNOS4_TMU_GAIN_SHIFT		8
-#define EXYNOS4_TMU_REF_VOLTAGE_SHIFT	24
-
-#define EXYNOS4_TMU_TRIM_TEMP_MASK	0xff
-#define EXYNOS4_TMU_CORE_ON	3
-#define EXYNOS4_TMU_CORE_OFF	2
-#define EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET	50
-#define EXYNOS4_TMU_TRIG_LEVEL0_MASK	0x1
-#define EXYNOS4_TMU_TRIG_LEVEL1_MASK	0x10
-#define EXYNOS4_TMU_TRIG_LEVEL2_MASK	0x100
-#define EXYNOS4_TMU_TRIG_LEVEL3_MASK	0x1000
-#define EXYNOS4_TMU_INTCLEAR_VAL	0x1111
-
-struct exynos4_tmu_data {
-	struct exynos4_tmu_platform_data *pdata;
-	struct device *hwmon_dev;
-	struct resource *mem;
-	void __iomem *base;
-	int irq;
-	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 exynos4_tmu_data *data, u8 temp)
-{
-	struct exynos4_tmu_platform_data *pdata = data->pdata;
-	int temp_code;
-
-	/* 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 + EXYNOS4_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 exynos4_tmu_data *data, u8 temp_code)
-{
-	struct exynos4_tmu_platform_data *pdata = data->pdata;
-	int temp;
-
-	/* 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 - EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET;
-		break;
-	}
-out:
-	return temp;
-}
-
-static int exynos4_tmu_initialize(struct platform_device *pdev)
-{
-	struct exynos4_tmu_data *data = platform_get_drvdata(pdev);
-	struct exynos4_tmu_platform_data *pdata = data->pdata;
-	unsigned int status, trim_info;
-	int ret = 0, threshold_code;
-
-	mutex_lock(&data->lock);
-	clk_enable(data->clk);
-
-	status = readb(data->base + EXYNOS4_TMU_REG_STATUS);
-	if (!status) {
-		ret = -EBUSY;
-		goto out;
-	}
-
-	/* Save trimming info in order to perform calibration */
-	trim_info = readl(data->base + EXYNOS4_TMU_REG_TRIMINFO);
-	data->temp_error1 = trim_info & EXYNOS4_TMU_TRIM_TEMP_MASK;
-	data->temp_error2 = ((trim_info >> 8) & EXYNOS4_TMU_TRIM_TEMP_MASK);
-
-	/* 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 + EXYNOS4_TMU_REG_THRESHOLD_TEMP);
-
-	writeb(pdata->trigger_levels[0],
-		data->base + EXYNOS4_TMU_REG_TRIG_LEVEL0);
-	writeb(pdata->trigger_levels[1],
-		data->base + EXYNOS4_TMU_REG_TRIG_LEVEL1);
-	writeb(pdata->trigger_levels[2],
-		data->base + EXYNOS4_TMU_REG_TRIG_LEVEL2);
-	writeb(pdata->trigger_levels[3],
-		data->base + EXYNOS4_TMU_REG_TRIG_LEVEL3);
-
-	writel(EXYNOS4_TMU_INTCLEAR_VAL,
-		data->base + EXYNOS4_TMU_REG_INTCLEAR);
-out:
-	clk_disable(data->clk);
-	mutex_unlock(&data->lock);
-
-	return ret;
-}
-
-static void exynos4_tmu_control(struct platform_device *pdev, bool on)
-{
-	struct exynos4_tmu_data *data = platform_get_drvdata(pdev);
-	struct exynos4_tmu_platform_data *pdata = data->pdata;
-	unsigned int con, interrupt_en;
-
-	mutex_lock(&data->lock);
-	clk_enable(data->clk);
-
-	con = pdata->reference_voltage << EXYNOS4_TMU_REF_VOLTAGE_SHIFT |
-		pdata->gain << EXYNOS4_TMU_GAIN_SHIFT;
-	if (on) {
-		con |= EXYNOS4_TMU_CORE_ON;
-		interrupt_en = pdata->trigger_level3_en << 12 |
-			pdata->trigger_level2_en << 8 |
-			pdata->trigger_level1_en << 4 |
-			pdata->trigger_level0_en;
-	} else {
-		con |= EXYNOS4_TMU_CORE_OFF;
-		interrupt_en = 0; /* Disable all interrupts */
-	}
-	writel(interrupt_en, data->base + EXYNOS4_TMU_REG_INTEN);
-	writel(con, data->base + EXYNOS4_TMU_REG_CONTROL);
-
-	clk_disable(data->clk);
-	mutex_unlock(&data->lock);
-}
-
-static int exynos4_tmu_read(struct exynos4_tmu_data *data)
-{
-	u8 temp_code;
-	int temp;
-
-	mutex_lock(&data->lock);
-	clk_enable(data->clk);
-
-	temp_code = readb(data->base + EXYNOS4_TMU_REG_CURRENT_TEMP);
-	temp = code_to_temp(data, temp_code);
-
-	clk_disable(data->clk);
-	mutex_unlock(&data->lock);
-
-	return temp;
-}
-
-static void exynos4_tmu_work(struct work_struct *work)
-{
-	struct exynos4_tmu_data *data = container_of(work,
-			struct exynos4_tmu_data, irq_work);
-
-	mutex_lock(&data->lock);
-	clk_enable(data->clk);
-
-	writel(EXYNOS4_TMU_INTCLEAR_VAL, data->base + EXYNOS4_TMU_REG_INTCLEAR);
-
-	kobject_uevent(&data->hwmon_dev->kobj, KOBJ_CHANGE);
-
-	enable_irq(data->irq);
-
-	clk_disable(data->clk);
-	mutex_unlock(&data->lock);
-}
-
-static irqreturn_t exynos4_tmu_irq(int irq, void *id)
-{
-	struct exynos4_tmu_data *data = id;
-
-	disable_irq_nosync(irq);
-	schedule_work(&data->irq_work);
-
-	return IRQ_HANDLED;
-}
-
-static ssize_t exynos4_tmu_show_name(struct device *dev,
-		struct device_attribute *attr, char *buf)
-{
-	return sprintf(buf, "exynos4-tmu\n");
-}
-
-static ssize_t exynos4_tmu_show_temp(struct device *dev,
-		struct device_attribute *attr, char *buf)
-{
-	struct exynos4_tmu_data *data = dev_get_drvdata(dev);
-	int ret;
-
-	ret = exynos4_tmu_read(data);
-	if (ret < 0)
-		return ret;
-
-	/* convert from degree Celsius to millidegree Celsius */
-	return sprintf(buf, "%d\n", ret * 1000);
-}
-
-static ssize_t exynos4_tmu_show_alarm(struct device *dev,
-		struct device_attribute *devattr, char *buf)
-{
-	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-	struct exynos4_tmu_data *data = dev_get_drvdata(dev);
-	struct exynos4_tmu_platform_data *pdata = data->pdata;
-	int temp;
-	unsigned int trigger_level;
-
-	temp = exynos4_tmu_read(data);
-	if (temp < 0)
-		return temp;
-
-	trigger_level = pdata->threshold + pdata->trigger_levels[attr->index];
-
-	return sprintf(buf, "%d\n", !!(temp > trigger_level));
-}
-
-static ssize_t exynos4_tmu_show_level(struct device *dev,
-		struct device_attribute *devattr, char *buf)
-{
-	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-	struct exynos4_tmu_data *data = dev_get_drvdata(dev);
-	struct exynos4_tmu_platform_data *pdata = data->pdata;
-	unsigned int temp = pdata->threshold +
-			pdata->trigger_levels[attr->index];
-
-	return sprintf(buf, "%u\n", temp * 1000);
-}
-
-static DEVICE_ATTR(name, S_IRUGO, exynos4_tmu_show_name, NULL);
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, exynos4_tmu_show_temp, NULL, 0);
-
-static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO,
-		exynos4_tmu_show_alarm, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO,
-		exynos4_tmu_show_alarm, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp1_emergency_alarm, S_IRUGO,
-		exynos4_tmu_show_alarm, NULL, 3);
-
-static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, exynos4_tmu_show_level, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, exynos4_tmu_show_level, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp1_emergency, S_IRUGO,
-		exynos4_tmu_show_level, NULL, 3);
-
-static struct attribute *exynos4_tmu_attributes[] = {
-	&dev_attr_name.attr,
-	&sensor_dev_attr_temp1_input.dev_attr.attr,
-	&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
-	&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
-	&sensor_dev_attr_temp1_emergency_alarm.dev_attr.attr,
-	&sensor_dev_attr_temp1_max.dev_attr.attr,
-	&sensor_dev_attr_temp1_crit.dev_attr.attr,
-	&sensor_dev_attr_temp1_emergency.dev_attr.attr,
-	NULL,
-};
-
-static const struct attribute_group exynos4_tmu_attr_group = {
-	.attrs = exynos4_tmu_attributes,
-};
-
-static int __devinit exynos4_tmu_probe(struct platform_device *pdev)
-{
-	struct exynos4_tmu_data *data;
-	struct exynos4_tmu_platform_data *pdata = pdev->dev.platform_data;
-	int ret;
-
-	if (!pdata) {
-		dev_err(&pdev->dev, "No platform init data supplied.\n");
-		return -ENODEV;
-	}
-
-	data = kzalloc(sizeof(struct exynos4_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) {
-		ret = data->irq;
-		dev_err(&pdev->dev, "Failed to get platform irq\n");
-		goto err_free;
-	}
-
-	INIT_WORK(&data->irq_work, exynos4_tmu_work);
-
-	data->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!data->mem) {
-		ret = -ENOENT;
-		dev_err(&pdev->dev, "Failed to get platform resource\n");
-		goto err_free;
-	}
-
-	data->mem = request_mem_region(data->mem->start,
-			resource_size(data->mem), pdev->name);
-	if (!data->mem) {
-		ret = -ENODEV;
-		dev_err(&pdev->dev, "Failed to request memory region\n");
-		goto err_free;
-	}
-
-	data->base = ioremap(data->mem->start, resource_size(data->mem));
-	if (!data->base) {
-		ret = -ENODEV;
-		dev_err(&pdev->dev, "Failed to ioremap memory\n");
-		goto err_mem_region;
-	}
-
-	ret = request_irq(data->irq, exynos4_tmu_irq,
-		IRQF_TRIGGER_RISING,
-		"exynos4-tmu", data);
-	if (ret) {
-		dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq);
-		goto err_io_remap;
-	}
-
-	data->clk = clk_get(NULL, "tmu_apbif");
-	if (IS_ERR(data->clk)) {
-		ret = PTR_ERR(data->clk);
-		dev_err(&pdev->dev, "Failed to get clock\n");
-		goto err_irq;
-	}
-
-	data->pdata = pdata;
-	platform_set_drvdata(pdev, data);
-	mutex_init(&data->lock);
-
-	ret = exynos4_tmu_initialize(pdev);
-	if (ret) {
-		dev_err(&pdev->dev, "Failed to initialize TMU\n");
-		goto err_clk;
-	}
-
-	ret = sysfs_create_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
-	if (ret) {
-		dev_err(&pdev->dev, "Failed to create sysfs group\n");
-		goto err_clk;
-	}
-
-	data->hwmon_dev = hwmon_device_register(&pdev->dev);
-	if (IS_ERR(data->hwmon_dev)) {
-		ret = PTR_ERR(data->hwmon_dev);
-		dev_err(&pdev->dev, "Failed to register hwmon device\n");
-		goto err_create_group;
-	}
-
-	exynos4_tmu_control(pdev, true);
-
-	return 0;
-
-err_create_group:
-	sysfs_remove_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
-err_clk:
-	platform_set_drvdata(pdev, NULL);
-	clk_put(data->clk);
-err_irq:
-	free_irq(data->irq, data);
-err_io_remap:
-	iounmap(data->base);
-err_mem_region:
-	release_mem_region(data->mem->start, resource_size(data->mem));
-err_free:
-	kfree(data);
-
-	return ret;
-}
-
-static int __devexit exynos4_tmu_remove(struct platform_device *pdev)
-{
-	struct exynos4_tmu_data *data = platform_get_drvdata(pdev);
-
-	exynos4_tmu_control(pdev, false);
-
-	hwmon_device_unregister(data->hwmon_dev);
-	sysfs_remove_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
-
-	clk_put(data->clk);
-
-	free_irq(data->irq, data);
-
-	iounmap(data->base);
-	release_mem_region(data->mem->start, resource_size(data->mem));
-
-	platform_set_drvdata(pdev, NULL);
-
-	kfree(data);
-
-	return 0;
-}
-
-#ifdef CONFIG_PM
-static int exynos4_tmu_suspend(struct platform_device *pdev, pm_message_t state)
-{
-	exynos4_tmu_control(pdev, false);
-
-	return 0;
-}
-
-static int exynos4_tmu_resume(struct platform_device *pdev)
-{
-	exynos4_tmu_initialize(pdev);
-	exynos4_tmu_control(pdev, true);
-
-	return 0;
-}
-#else
-#define exynos4_tmu_suspend NULL
-#define exynos4_tmu_resume NULL
-#endif
-
-static struct platform_driver exynos4_tmu_driver = {
-	.driver = {
-		.name   = "exynos4-tmu",
-		.owner  = THIS_MODULE,
-	},
-	.probe = exynos4_tmu_probe,
-	.remove	= __devexit_p(exynos4_tmu_remove),
-	.suspend = exynos4_tmu_suspend,
-	.resume = exynos4_tmu_resume,
-};
-
-module_platform_driver(exynos4_tmu_driver);
-
-MODULE_DESCRIPTION("EXYNOS4 TMU Driver");
-MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:exynos4-tmu");
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index f147395..e29113e 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -830,6 +830,16 @@ config MFD_INTEL_MSIC
 	  Passage) chip. This chip embeds audio, battery, GPIO, etc.
 	  devices used in Intel Medfield platforms.
 
+config SENSORS_EXYNOS4_TMU
+	tristate "Temperature sensor on Samsung EXYNOS4"
+	depends on ARCH_EXYNOS4
+	help
+	  If you say yes here you get support for TMU (Thermal Managment
+	  Unit) on SAMSUNG EXYNOS4 series of SoC.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called exynos4-tmu.
+
 endmenu
 endif
 
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index b953bab..766bf86 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -112,3 +112,4 @@ obj-$(CONFIG_TPS65911_COMPARATOR)	+= tps65911-comparator.o
 obj-$(CONFIG_MFD_AAT2870_CORE)	+= aat2870-core.o
 obj-$(CONFIG_MFD_INTEL_MSIC)	+= intel_msic.o
 obj-$(CONFIG_MFD_S5M_CORE)	+= s5m-core.o s5m-irq.o
+obj-$(CONFIG_SENSORS_EXYNOS4_TMU)	+= exynos4_tmu.o
diff --git a/drivers/mfd/exynos4_tmu.c b/drivers/mfd/exynos4_tmu.c
new file mode 100644
index 0000000..f2359a0
--- /dev/null
+++ b/drivers/mfd/exynos4_tmu.c
@@ -0,0 +1,514 @@
+/*
+ * exynos4_tmu.c - Samsung EXYNOS4 TMU (Thermal Management Unit)
+ *
+ *  Copyright (C) 2011 Samsung Electronics
+ *  Donggeun Kim <dg77.kim@samsung.com>
+ *
+ * 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/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+
+#include <linux/platform_data/exynos4_tmu.h>
+
+#define EXYNOS4_TMU_REG_TRIMINFO	0x0
+#define EXYNOS4_TMU_REG_CONTROL		0x20
+#define EXYNOS4_TMU_REG_STATUS		0x28
+#define EXYNOS4_TMU_REG_CURRENT_TEMP	0x40
+#define EXYNOS4_TMU_REG_THRESHOLD_TEMP	0x44
+#define EXYNOS4_TMU_REG_TRIG_LEVEL0	0x50
+#define EXYNOS4_TMU_REG_TRIG_LEVEL1	0x54
+#define EXYNOS4_TMU_REG_TRIG_LEVEL2	0x58
+#define EXYNOS4_TMU_REG_TRIG_LEVEL3	0x5C
+#define EXYNOS4_TMU_REG_PAST_TEMP0	0x60
+#define EXYNOS4_TMU_REG_PAST_TEMP1	0x64
+#define EXYNOS4_TMU_REG_PAST_TEMP2	0x68
+#define EXYNOS4_TMU_REG_PAST_TEMP3	0x6C
+#define EXYNOS4_TMU_REG_INTEN		0x70
+#define EXYNOS4_TMU_REG_INTSTAT		0x74
+#define EXYNOS4_TMU_REG_INTCLEAR	0x78
+
+#define EXYNOS4_TMU_GAIN_SHIFT		8
+#define EXYNOS4_TMU_REF_VOLTAGE_SHIFT	24
+
+#define EXYNOS4_TMU_TRIM_TEMP_MASK	0xff
+#define EXYNOS4_TMU_CORE_ON	3
+#define EXYNOS4_TMU_CORE_OFF	2
+#define EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET	50
+#define EXYNOS4_TMU_TRIG_LEVEL0_MASK	0x1
+#define EXYNOS4_TMU_TRIG_LEVEL1_MASK	0x10
+#define EXYNOS4_TMU_TRIG_LEVEL2_MASK	0x100
+#define EXYNOS4_TMU_TRIG_LEVEL3_MASK	0x1000
+#define EXYNOS4_TMU_INTCLEAR_VAL	0x1111
+
+struct exynos4_tmu_data {
+	struct exynos4_tmu_platform_data *pdata;
+	struct device *hwmon_dev;
+	struct resource *mem;
+	void __iomem *base;
+	int irq;
+	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 exynos4_tmu_data *data, u8 temp)
+{
+	struct exynos4_tmu_platform_data *pdata = data->pdata;
+	int temp_code;
+
+	/* 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 + EXYNOS4_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 exynos4_tmu_data *data, u8 temp_code)
+{
+	struct exynos4_tmu_platform_data *pdata = data->pdata;
+	int temp;
+
+	/* 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 - EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET;
+		break;
+	}
+out:
+	return temp;
+}
+
+static int exynos4_tmu_initialize(struct platform_device *pdev)
+{
+	struct exynos4_tmu_data *data = platform_get_drvdata(pdev);
+	struct exynos4_tmu_platform_data *pdata = data->pdata;
+	unsigned int status, trim_info;
+	int ret = 0, threshold_code;
+
+	mutex_lock(&data->lock);
+	clk_enable(data->clk);
+
+	status = readb(data->base + EXYNOS4_TMU_REG_STATUS);
+	if (!status) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	/* Save trimming info in order to perform calibration */
+	trim_info = readl(data->base + EXYNOS4_TMU_REG_TRIMINFO);
+	data->temp_error1 = trim_info & EXYNOS4_TMU_TRIM_TEMP_MASK;
+	data->temp_error2 = ((trim_info >> 8) & EXYNOS4_TMU_TRIM_TEMP_MASK);
+
+	/* 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 + EXYNOS4_TMU_REG_THRESHOLD_TEMP);
+
+	writeb(pdata->trigger_levels[0],
+		data->base + EXYNOS4_TMU_REG_TRIG_LEVEL0);
+	writeb(pdata->trigger_levels[1],
+		data->base + EXYNOS4_TMU_REG_TRIG_LEVEL1);
+	writeb(pdata->trigger_levels[2],
+		data->base + EXYNOS4_TMU_REG_TRIG_LEVEL2);
+	writeb(pdata->trigger_levels[3],
+		data->base + EXYNOS4_TMU_REG_TRIG_LEVEL3);
+
+	writel(EXYNOS4_TMU_INTCLEAR_VAL,
+		data->base + EXYNOS4_TMU_REG_INTCLEAR);
+out:
+	clk_disable(data->clk);
+	mutex_unlock(&data->lock);
+
+	return ret;
+}
+
+static void exynos4_tmu_control(struct platform_device *pdev, bool on)
+{
+	struct exynos4_tmu_data *data = platform_get_drvdata(pdev);
+	struct exynos4_tmu_platform_data *pdata = data->pdata;
+	unsigned int con, interrupt_en;
+
+	mutex_lock(&data->lock);
+	clk_enable(data->clk);
+
+	con = pdata->reference_voltage << EXYNOS4_TMU_REF_VOLTAGE_SHIFT |
+		pdata->gain << EXYNOS4_TMU_GAIN_SHIFT;
+	if (on) {
+		con |= EXYNOS4_TMU_CORE_ON;
+		interrupt_en = pdata->trigger_level3_en << 12 |
+			pdata->trigger_level2_en << 8 |
+			pdata->trigger_level1_en << 4 |
+			pdata->trigger_level0_en;
+	} else {
+		con |= EXYNOS4_TMU_CORE_OFF;
+		interrupt_en = 0; /* Disable all interrupts */
+	}
+	writel(interrupt_en, data->base + EXYNOS4_TMU_REG_INTEN);
+	writel(con, data->base + EXYNOS4_TMU_REG_CONTROL);
+
+	clk_disable(data->clk);
+	mutex_unlock(&data->lock);
+}
+
+static int exynos4_tmu_read(struct exynos4_tmu_data *data)
+{
+	u8 temp_code;
+	int temp;
+
+	mutex_lock(&data->lock);
+	clk_enable(data->clk);
+
+	temp_code = readb(data->base + EXYNOS4_TMU_REG_CURRENT_TEMP);
+	temp = code_to_temp(data, temp_code);
+
+	clk_disable(data->clk);
+	mutex_unlock(&data->lock);
+
+	return temp;
+}
+
+static void exynos4_tmu_work(struct work_struct *work)
+{
+	struct exynos4_tmu_data *data = container_of(work,
+			struct exynos4_tmu_data, irq_work);
+
+	mutex_lock(&data->lock);
+	clk_enable(data->clk);
+
+	writel(EXYNOS4_TMU_INTCLEAR_VAL, data->base + EXYNOS4_TMU_REG_INTCLEAR);
+
+	kobject_uevent(&data->hwmon_dev->kobj, KOBJ_CHANGE);
+
+	enable_irq(data->irq);
+
+	clk_disable(data->clk);
+	mutex_unlock(&data->lock);
+}
+
+static irqreturn_t exynos4_tmu_irq(int irq, void *id)
+{
+	struct exynos4_tmu_data *data = id;
+
+	disable_irq_nosync(irq);
+	schedule_work(&data->irq_work);
+
+	return IRQ_HANDLED;
+}
+
+static ssize_t exynos4_tmu_show_name(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "exynos4-tmu\n");
+}
+
+static ssize_t exynos4_tmu_show_temp(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct exynos4_tmu_data *data = dev_get_drvdata(dev);
+	int ret;
+
+	ret = exynos4_tmu_read(data);
+	if (ret < 0)
+		return ret;
+
+	/* convert from degree Celsius to millidegree Celsius */
+	return sprintf(buf, "%d\n", ret * 1000);
+}
+
+static ssize_t exynos4_tmu_show_alarm(struct device *dev,
+		struct device_attribute *devattr, char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	struct exynos4_tmu_data *data = dev_get_drvdata(dev);
+	struct exynos4_tmu_platform_data *pdata = data->pdata;
+	int temp;
+	unsigned int trigger_level;
+
+	temp = exynos4_tmu_read(data);
+	if (temp < 0)
+		return temp;
+
+	trigger_level = pdata->threshold + pdata->trigger_levels[attr->index];
+
+	return sprintf(buf, "%d\n", !!(temp > trigger_level));
+}
+
+static ssize_t exynos4_tmu_show_level(struct device *dev,
+		struct device_attribute *devattr, char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	struct exynos4_tmu_data *data = dev_get_drvdata(dev);
+	struct exynos4_tmu_platform_data *pdata = data->pdata;
+	unsigned int temp = pdata->threshold +
+			pdata->trigger_levels[attr->index];
+
+	return sprintf(buf, "%u\n", temp * 1000);
+}
+
+static DEVICE_ATTR(name, S_IRUGO, exynos4_tmu_show_name, NULL);
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, exynos4_tmu_show_temp, NULL, 0);
+
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO,
+		exynos4_tmu_show_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO,
+		exynos4_tmu_show_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp1_emergency_alarm, S_IRUGO,
+		exynos4_tmu_show_alarm, NULL, 3);
+
+static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, exynos4_tmu_show_level, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, exynos4_tmu_show_level, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp1_emergency, S_IRUGO,
+		exynos4_tmu_show_level, NULL, 3);
+
+static struct attribute *exynos4_tmu_attributes[] = {
+	&dev_attr_name.attr,
+	&sensor_dev_attr_temp1_input.dev_attr.attr,
+	&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+	&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
+	&sensor_dev_attr_temp1_emergency_alarm.dev_attr.attr,
+	&sensor_dev_attr_temp1_max.dev_attr.attr,
+	&sensor_dev_attr_temp1_crit.dev_attr.attr,
+	&sensor_dev_attr_temp1_emergency.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group exynos4_tmu_attr_group = {
+	.attrs = exynos4_tmu_attributes,
+};
+
+static int __devinit exynos4_tmu_probe(struct platform_device *pdev)
+{
+	struct exynos4_tmu_data *data;
+	struct exynos4_tmu_platform_data *pdata = pdev->dev.platform_data;
+	int ret;
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "No platform init data supplied.\n");
+		return -ENODEV;
+	}
+
+	data = kzalloc(sizeof(struct exynos4_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) {
+		ret = data->irq;
+		dev_err(&pdev->dev, "Failed to get platform irq\n");
+		goto err_free;
+	}
+
+	INIT_WORK(&data->irq_work, exynos4_tmu_work);
+
+	data->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!data->mem) {
+		ret = -ENOENT;
+		dev_err(&pdev->dev, "Failed to get platform resource\n");
+		goto err_free;
+	}
+
+	data->mem = request_mem_region(data->mem->start,
+			resource_size(data->mem), pdev->name);
+	if (!data->mem) {
+		ret = -ENODEV;
+		dev_err(&pdev->dev, "Failed to request memory region\n");
+		goto err_free;
+	}
+
+	data->base = ioremap(data->mem->start, resource_size(data->mem));
+	if (!data->base) {
+		ret = -ENODEV;
+		dev_err(&pdev->dev, "Failed to ioremap memory\n");
+		goto err_mem_region;
+	}
+
+	ret = request_irq(data->irq, exynos4_tmu_irq,
+		IRQF_TRIGGER_RISING,
+		"exynos4-tmu", data);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq);
+		goto err_io_remap;
+	}
+
+	data->clk = clk_get(NULL, "tmu_apbif");
+	if (IS_ERR(data->clk)) {
+		ret = PTR_ERR(data->clk);
+		dev_err(&pdev->dev, "Failed to get clock\n");
+		goto err_irq;
+	}
+
+	data->pdata = pdata;
+	platform_set_drvdata(pdev, data);
+	mutex_init(&data->lock);
+
+	ret = exynos4_tmu_initialize(pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to initialize TMU\n");
+		goto err_clk;
+	}
+
+	ret = sysfs_create_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to create sysfs group\n");
+		goto err_clk;
+	}
+
+	data->hwmon_dev = hwmon_device_register(&pdev->dev);
+	if (IS_ERR(data->hwmon_dev)) {
+		ret = PTR_ERR(data->hwmon_dev);
+		dev_err(&pdev->dev, "Failed to register hwmon device\n");
+		goto err_create_group;
+	}
+
+	exynos4_tmu_control(pdev, true);
+
+	return 0;
+
+err_create_group:
+	sysfs_remove_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
+err_clk:
+	platform_set_drvdata(pdev, NULL);
+	clk_put(data->clk);
+err_irq:
+	free_irq(data->irq, data);
+err_io_remap:
+	iounmap(data->base);
+err_mem_region:
+	release_mem_region(data->mem->start, resource_size(data->mem));
+err_free:
+	kfree(data);
+
+	return ret;
+}
+
+static int __devexit exynos4_tmu_remove(struct platform_device *pdev)
+{
+	struct exynos4_tmu_data *data = platform_get_drvdata(pdev);
+
+	exynos4_tmu_control(pdev, false);
+
+	hwmon_device_unregister(data->hwmon_dev);
+	sysfs_remove_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
+
+	clk_put(data->clk);
+
+	free_irq(data->irq, data);
+
+	iounmap(data->base);
+	release_mem_region(data->mem->start, resource_size(data->mem));
+
+	platform_set_drvdata(pdev, NULL);
+
+	kfree(data);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int exynos4_tmu_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	exynos4_tmu_control(pdev, false);
+
+	return 0;
+}
+
+static int exynos4_tmu_resume(struct platform_device *pdev)
+{
+	exynos4_tmu_initialize(pdev);
+	exynos4_tmu_control(pdev, true);
+
+	return 0;
+}
+#else
+#define exynos4_tmu_suspend NULL
+#define exynos4_tmu_resume NULL
+#endif
+
+static struct platform_driver exynos4_tmu_driver = {
+	.driver = {
+		.name   = "exynos4-tmu",
+		.owner  = THIS_MODULE,
+	},
+	.probe = exynos4_tmu_probe,
+	.remove	= __devexit_p(exynos4_tmu_remove),
+	.suspend = exynos4_tmu_suspend,
+	.resume = exynos4_tmu_resume,
+};
+
+module_platform_driver(exynos4_tmu_driver);
+
+MODULE_DESCRIPTION("EXYNOS4 TMU Driver");
+MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:exynos4-tmu");
-- 
1.7.1


_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [lm-sensors] [PATCH 3/4] thermal: exynos4: Register the tmu sensor with the thermal interface layer
@ 2012-03-03 11:06   ` Amit Daniel Kachhap
  0 siblings, 0 replies; 43+ messages in thread
From: Amit Daniel Kachhap @ 2012-03-03 11:18 UTC (permalink / raw)
  To: linux-pm, linux-samsung-soc
  Cc: linaro-dev, patches, linux-kernel, lm-sensors, linux-acpi

Export and register information from the tmu temperature sensor to the samsung
exynos kernel thermal framework where different cooling devices and thermal
zone are binded. The exported information is based according to the data
structure thermal_sensor_conf present in exynos_thermal.h. HWMON sysfs
functions are removed as all of them are present in generic linux thermal layer.

Also the platform data structure is modified to pass frequency cooling
in percentages for each thermal level.

Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>
---
 Documentation/mfd/exynos4_tmu             |   35 +-------
 drivers/mfd/exynos4_tmu.c                 |  139 +++++++----------------------
 include/linux/platform_data/exynos4_tmu.h |    7 ++
 3 files changed, 44 insertions(+), 137 deletions(-)

diff --git a/Documentation/mfd/exynos4_tmu b/Documentation/mfd/exynos4_tmu
index c3c6b41..2b46f67 100644
--- a/Documentation/mfd/exynos4_tmu
+++ b/Documentation/mfd/exynos4_tmu
@@ -46,36 +46,7 @@ The threshold levels are defined as follows:
   The threshold and each trigger_level are set
   through the corresponding registers.
 
-When an interrupt occurs, this driver notify user space of
-one of four threshold levels for the interrupt
-through kobject_uevent_env and sysfs_notify functions.
+When an interrupt occurs, this driver notify kernel thermal framework
+with the function exynos4_report_trigger.
 Although an interrupt condition for level_0 can be set,
-it is not notified to user space through sysfs_notify function.
-
-Sysfs Interface
----------------
-name		name of the temperature sensor
-		RO
-
-temp1_input	temperature
-		RO
-
-temp1_max	temperature for level_1 interrupt
-		RO
-
-temp1_crit	temperature for level_2 interrupt
-		RO
-
-temp1_emergency	temperature for level_3 interrupt
-		RO
-
-temp1_max_alarm	alarm for level_1 interrupt
-		RO
-
-temp1_crit_alarm
-		alarm for level_2 interrupt
-		RO
-
-temp1_emergency_alarm
-		alarm for level_3 interrupt
-		RO
+it can be used to synchronize the cooling action.
diff --git a/drivers/mfd/exynos4_tmu.c b/drivers/mfd/exynos4_tmu.c
index f2359a0..e343923 100644
--- a/drivers/mfd/exynos4_tmu.c
+++ b/drivers/mfd/exynos4_tmu.c
@@ -33,10 +33,10 @@
 #include <linux/io.h>
 #include <linux/mutex.h>
 
-#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
-
 #include <linux/platform_data/exynos4_tmu.h>
+#ifdef CONFIG_SAMSUNG_THERMAL_INTERFACE
+#include <linux/exynos_thermal.h>
+#endif
 
 #define EXYNOS4_TMU_REG_TRIMINFO	0x0
 #define EXYNOS4_TMU_REG_CONTROL		0x20
@@ -70,7 +70,6 @@
 
 struct exynos4_tmu_data {
 	struct exynos4_tmu_platform_data *pdata;
-	struct device *hwmon_dev;
 	struct resource *mem;
 	void __iomem *base;
 	int irq;
@@ -246,12 +245,12 @@ static void exynos4_tmu_work(struct work_struct *work)
 
 	writel(EXYNOS4_TMU_INTCLEAR_VAL, data->base + EXYNOS4_TMU_REG_INTCLEAR);
 
-	kobject_uevent(&data->hwmon_dev->kobj, KOBJ_CHANGE);
-
-	enable_irq(data->irq);
-
 	clk_disable(data->clk);
 	mutex_unlock(&data->lock);
+#ifdef CONFIG_SAMSUNG_THERMAL_INTERFACE
+	exynos4_report_trigger();
+#endif
+	enable_irq(data->irq);
 }
 
 static irqreturn_t exynos4_tmu_irq(int irq, void *id)
@@ -264,92 +263,19 @@ static irqreturn_t exynos4_tmu_irq(int irq, void *id)
 	return IRQ_HANDLED;
 }
 
-static ssize_t exynos4_tmu_show_name(struct device *dev,
-		struct device_attribute *attr, char *buf)
-{
-	return sprintf(buf, "exynos4-tmu\n");
-}
-
-static ssize_t exynos4_tmu_show_temp(struct device *dev,
-		struct device_attribute *attr, char *buf)
-{
-	struct exynos4_tmu_data *data = dev_get_drvdata(dev);
-	int ret;
-
-	ret = exynos4_tmu_read(data);
-	if (ret < 0)
-		return ret;
-
-	/* convert from degree Celsius to millidegree Celsius */
-	return sprintf(buf, "%d\n", ret * 1000);
-}
-
-static ssize_t exynos4_tmu_show_alarm(struct device *dev,
-		struct device_attribute *devattr, char *buf)
-{
-	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-	struct exynos4_tmu_data *data = dev_get_drvdata(dev);
-	struct exynos4_tmu_platform_data *pdata = data->pdata;
-	int temp;
-	unsigned int trigger_level;
-
-	temp = exynos4_tmu_read(data);
-	if (temp < 0)
-		return temp;
-
-	trigger_level = pdata->threshold + pdata->trigger_levels[attr->index];
-
-	return sprintf(buf, "%d\n", !!(temp > trigger_level));
-}
-
-static ssize_t exynos4_tmu_show_level(struct device *dev,
-		struct device_attribute *devattr, char *buf)
-{
-	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-	struct exynos4_tmu_data *data = dev_get_drvdata(dev);
-	struct exynos4_tmu_platform_data *pdata = data->pdata;
-	unsigned int temp = pdata->threshold +
-			pdata->trigger_levels[attr->index];
-
-	return sprintf(buf, "%u\n", temp * 1000);
-}
-
-static DEVICE_ATTR(name, S_IRUGO, exynos4_tmu_show_name, NULL);
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, exynos4_tmu_show_temp, NULL, 0);
-
-static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO,
-		exynos4_tmu_show_alarm, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO,
-		exynos4_tmu_show_alarm, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp1_emergency_alarm, S_IRUGO,
-		exynos4_tmu_show_alarm, NULL, 3);
-
-static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, exynos4_tmu_show_level, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, exynos4_tmu_show_level, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp1_emergency, S_IRUGO,
-		exynos4_tmu_show_level, NULL, 3);
-
-static struct attribute *exynos4_tmu_attributes[] = {
-	&dev_attr_name.attr,
-	&sensor_dev_attr_temp1_input.dev_attr.attr,
-	&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
-	&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
-	&sensor_dev_attr_temp1_emergency_alarm.dev_attr.attr,
-	&sensor_dev_attr_temp1_max.dev_attr.attr,
-	&sensor_dev_attr_temp1_crit.dev_attr.attr,
-	&sensor_dev_attr_temp1_emergency.dev_attr.attr,
-	NULL,
-};
-
-static const struct attribute_group exynos4_tmu_attr_group = {
-	.attrs = exynos4_tmu_attributes,
+#ifdef CONFIG_SAMSUNG_THERMAL_INTERFACE
+static struct thermal_sensor_conf exynos4_sensor_conf = {
+	.name			= "exynos4-therm",
+	.read_temperature	= (int (*)(void *))exynos4_tmu_read,
 };
+#endif
+/*CONFIG_SAMSUNG_THERMAL_INTERFACE*/
 
 static int __devinit exynos4_tmu_probe(struct platform_device *pdev)
 {
 	struct exynos4_tmu_data *data;
 	struct exynos4_tmu_platform_data *pdata = pdev->dev.platform_data;
-	int ret;
+	int ret, i;
 
 	if (!pdata) {
 		dev_err(&pdev->dev, "No platform init data supplied.\n");
@@ -418,25 +344,27 @@ static int __devinit exynos4_tmu_probe(struct platform_device *pdev)
 		goto err_clk;
 	}
 
-	ret = sysfs_create_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
+	exynos4_tmu_control(pdev, true);
+#ifdef CONFIG_SAMSUNG_THERMAL_INTERFACE
+	(&exynos4_sensor_conf)->private_data = data;
+	exynos4_sensor_conf.trip_data.trip_count = 3;
+	for (i = 0; i < exynos4_sensor_conf.trip_data.trip_count; i++)
+		exynos4_sensor_conf.trip_data.trip_val[i] +			pdata->threshold + pdata->trigger_levels[i + 1];
+
+	exynos4_sensor_conf.cooling_data.freq_pctg_count +						pdata->freq_tab_count;
+	for (i = 0; i < pdata->freq_tab_count; i++)
+		exynos4_sensor_conf.cooling_data.freq_data[i].freq_clip_pctg +					pdata->freq_tab[i].freq_clip_pctg;
+
+	ret = exynos4_register_thermal(&exynos4_sensor_conf);
 	if (ret) {
-		dev_err(&pdev->dev, "Failed to create sysfs group\n");
+		dev_err(&pdev->dev, "Failed to register thermal interface\n");
 		goto err_clk;
 	}
-
-	data->hwmon_dev = hwmon_device_register(&pdev->dev);
-	if (IS_ERR(data->hwmon_dev)) {
-		ret = PTR_ERR(data->hwmon_dev);
-		dev_err(&pdev->dev, "Failed to register hwmon device\n");
-		goto err_create_group;
-	}
-
-	exynos4_tmu_control(pdev, true);
-
+#endif
 	return 0;
-
-err_create_group:
-	sysfs_remove_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
 err_clk:
 	platform_set_drvdata(pdev, NULL);
 	clk_put(data->clk);
@@ -458,8 +386,9 @@ static int __devexit exynos4_tmu_remove(struct platform_device *pdev)
 
 	exynos4_tmu_control(pdev, false);
 
-	hwmon_device_unregister(data->hwmon_dev);
-	sysfs_remove_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
+#ifdef CONFIG_SAMSUNG_THERMAL_INTERFACE
+	exynos4_unregister_thermal();
+#endif
 
 	clk_put(data->clk);
 
diff --git a/include/linux/platform_data/exynos4_tmu.h b/include/linux/platform_data/exynos4_tmu.h
index 39e038c..642c508 100644
--- a/include/linux/platform_data/exynos4_tmu.h
+++ b/include/linux/platform_data/exynos4_tmu.h
@@ -21,6 +21,7 @@
 
 #ifndef _LINUX_EXYNOS4_TMU_H
 #define _LINUX_EXYNOS4_TMU_H
+#include <linux/cpu_cooling.h>
 
 enum calibration_type {
 	TYPE_ONE_POINT_TRIMMING,
@@ -64,6 +65,9 @@ enum calibration_type {
  *	in the positive-TC generator block
  *	0 <= reference_voltage <= 31
  * @cal_type: calibration type for temperature
+ * @freq_pctg_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.
  *
  * This structure is required for configuration of exynos4_tmu driver.
  */
@@ -79,5 +83,8 @@ struct exynos4_tmu_platform_data {
 	u8 reference_voltage;
 
 	enum calibration_type cal_type;
+
+	struct freq_pctg_table freq_tab[4];
+	unsigned int freq_tab_count;
 };
 #endif /* _LINUX_EXYNOS4_TMU_H */
-- 
1.7.1


_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* [lm-sensors] [PATCH 4/4] ARM: exynos4: Add thermal sensor driver platform device support
@ 2012-03-03 11:06   ` Amit Daniel Kachhap
  0 siblings, 0 replies; 43+ messages in thread
From: Amit Daniel Kachhap @ 2012-03-03 11:18 UTC (permalink / raw)
  To: linux-pm, linux-samsung-soc
  Cc: linaro-dev, patches, linux-kernel, lm-sensors, linux-acpi

This patch adds necessary source definations needed for TMU driver and
the platform device support.

Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>
---
 arch/arm/mach-exynos/Kconfig              |   11 +++++
 arch/arm/mach-exynos/Makefile             |    1 +
 arch/arm/mach-exynos/clock.c              |    4 ++
 arch/arm/mach-exynos/dev-tmu.c            |   64 +++++++++++++++++++++++++++++
 arch/arm/mach-exynos/include/mach/irqs.h  |    2 +
 arch/arm/mach-exynos/include/mach/map.h   |    1 +
 arch/arm/mach-exynos/mach-origen.c        |    1 +
 arch/arm/plat-samsung/include/plat/devs.h |    1 +
 8 files changed, 85 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-exynos/dev-tmu.c

diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig
index 5d602f6..03968a6 100644
--- a/arch/arm/mach-exynos/Kconfig
+++ b/arch/arm/mach-exynos/Kconfig
@@ -160,6 +160,16 @@ config EXYNOS4_SETUP_SPI
 	help
 	  Common setup code for SPI GPIO configurations.
 
+config EXYNOS4_DEV_TMU
+	bool "Exynos4 tmu device support"
+	default n
+	depends on ARCH_EXYNOS4
+	---help---
+	  Compile in platform device definitions for TMU. This macro also
+	  enables compilation hwmon base TMU driver and also allows compilation
+	  of the platform device files. The platform data in this case is trip
+	  temperature and some tmu h/w configurations related parameter.
+
 # machine support
 
 if ARCH_EXYNOS4
@@ -199,6 +209,7 @@ config MACH_SMDKV310
 	select SAMSUNG_DEV_PWM
 	select EXYNOS4_DEV_USB_OHCI
 	select EXYNOS4_DEV_SYSMMU
+	select EXYNOS4_DEV_TMU
 	select EXYNOS4_SETUP_FIMD0
 	select EXYNOS4_SETUP_I2C1
 	select EXYNOS4_SETUP_KEYPAD
diff --git a/arch/arm/mach-exynos/Makefile b/arch/arm/mach-exynos/Makefile
index 5fc202c..9b62e69 100644
--- a/arch/arm/mach-exynos/Makefile
+++ b/arch/arm/mach-exynos/Makefile
@@ -50,6 +50,7 @@ obj-$(CONFIG_EXYNOS4_DEV_SYSMMU)	+= dev-sysmmu.o
 obj-$(CONFIG_EXYNOS4_DEV_DWMCI)		+= dev-dwmci.o
 obj-$(CONFIG_EXYNOS4_DEV_DMA)		+= dma.o
 obj-$(CONFIG_EXYNOS4_DEV_USB_OHCI)	+= dev-ohci.o
+obj-$(CONFIG_EXYNOS4_DEV_TMU)		+= dev-tmu.o
 
 obj-$(CONFIG_ARCH_EXYNOS4)		+= setup-i2c0.o
 obj-$(CONFIG_EXYNOS4_SETUP_FIMC)	+= setup-fimc.o
diff --git a/arch/arm/mach-exynos/clock.c b/arch/arm/mach-exynos/clock.c
index 187287a..3b15397 100644
--- a/arch/arm/mach-exynos/clock.c
+++ b/arch/arm/mach-exynos/clock.c
@@ -560,6 +560,10 @@ static struct clk init_clocks_off[] = {
 		.enable		= exynos4_clk_ip_peril_ctrl,
 		.ctrlbit	= (1 << 15),
 	}, {
+		.name		= "tmu_apbif",
+		.enable		= exynos4_clk_ip_perir_ctrl,
+		.ctrlbit	= (1 << 17),
+	}, {
 		.name		= "keypad",
 		.enable		= exynos4_clk_ip_perir_ctrl,
 		.ctrlbit	= (1 << 16),
diff --git a/arch/arm/mach-exynos/dev-tmu.c b/arch/arm/mach-exynos/dev-tmu.c
new file mode 100644
index 0000000..317b321
--- /dev/null
+++ b/arch/arm/mach-exynos/dev-tmu.c
@@ -0,0 +1,64 @@
+/* linux/arch/arm/mach-exynos4/dev-tmu.c
+ *
+ * Copyright 2011 by SAMSUNG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/platform_data/exynos4_tmu.h>
+#include <asm/irq.h>
+
+#include <mach/irqs.h>
+#include <mach/map.h>
+#include <plat/devs.h>
+
+static struct resource exynos4_tmu_resource[] = {
+	[0] = {
+		.start	= EXYNOS4_PA_TMU,
+		.end	= EXYNOS4_PA_TMU + 0xFFFF - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= IRQ_TMU_TRIG0,
+		.end	= IRQ_TMU_TRIG0,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct exynos4_tmu_platform_data default_tmu_data = {
+	.threshold = 80,
+	.trigger_levels[0] = 2,
+	.trigger_levels[1] = 5,
+	.trigger_levels[2] = 20,
+	.trigger_levels[3] = 30,
+	.trigger_level0_en = 1,
+	.trigger_level1_en = 1,
+	.trigger_level2_en = 1,
+	.trigger_level3_en = 1,
+	.gain = 15,
+	.reference_voltage = 7,
+	.cal_type = TYPE_ONE_POINT_TRIMMING,
+	.freq_tab[0] = {
+		.freq_clip_pctg = 30,
+		},
+	.freq_tab[1] = {
+		.freq_clip_pctg = 99,
+		},
+	.freq_tab_count = 2,
+};
+
+struct platform_device exynos4_device_tmu = {
+	.name		= "exynos4-tmu",
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(exynos4_tmu_resource),
+	.resource	= exynos4_tmu_resource,
+	.dev	= {
+		.platform_data	= &default_tmu_data,
+	},
+};
diff --git a/arch/arm/mach-exynos/include/mach/irqs.h b/arch/arm/mach-exynos/include/mach/irqs.h
index f77bce0..f98d2e4 100644
--- a/arch/arm/mach-exynos/include/mach/irqs.h
+++ b/arch/arm/mach-exynos/include/mach/irqs.h
@@ -128,6 +128,8 @@
 #define COMBINER_GROUP(x)	((x) * MAX_IRQ_IN_COMBINER + IRQ_SPI(128))
 #define COMBINER_IRQ(x, y)	(COMBINER_GROUP(x) + y)
 
+#define IRQ_TMU_TRIG0		COMBINER_IRQ(2, 4)
+#define IRQ_TMU_TRIG1		COMBINER_IRQ(3, 4)
 #define IRQ_SYSMMU_MDMA0_0	COMBINER_IRQ(4, 0)
 #define IRQ_SYSMMU_SSS_0	COMBINER_IRQ(4, 1)
 #define IRQ_SYSMMU_FIMC0_0	COMBINER_IRQ(4, 2)
diff --git a/arch/arm/mach-exynos/include/mach/map.h b/arch/arm/mach-exynos/include/mach/map.h
index c754a22..bc11f1f 100644
--- a/arch/arm/mach-exynos/include/mach/map.h
+++ b/arch/arm/mach-exynos/include/mach/map.h
@@ -66,6 +66,7 @@
 #define EXYNOS4_PA_COREPERI		0x10500000
 #define EXYNOS4_PA_TWD			0x10500600
 #define EXYNOS4_PA_L2CC			0x10502000
+#define EXYNOS4_PA_TMU			0x100C0000
 
 #define EXYNOS4_PA_MDMA			0x10810000
 #define EXYNOS4_PA_PDMA0		0x12680000
diff --git a/arch/arm/mach-exynos/mach-origen.c b/arch/arm/mach-exynos/mach-origen.c
index 0679b8a..5d56e53 100644
--- a/arch/arm/mach-exynos/mach-origen.c
+++ b/arch/arm/mach-exynos/mach-origen.c
@@ -630,6 +630,7 @@ static struct platform_device *origen_devices[] __initdata = {
 	&exynos4_device_pd[PD_MFC],
 	&origen_device_gpiokeys,
 	&origen_lcd_hv070wsa,
+	&exynos4_device_tmu,
 };
 
 /* LCD Backlight data */
diff --git a/arch/arm/plat-samsung/include/plat/devs.h b/arch/arm/plat-samsung/include/plat/devs.h
index 4214ea0..0960405 100644
--- a/arch/arm/plat-samsung/include/plat/devs.h
+++ b/arch/arm/plat-samsung/include/plat/devs.h
@@ -130,6 +130,7 @@ extern struct platform_device exynos4_device_pcm2;
 extern struct platform_device exynos4_device_pd[];
 extern struct platform_device exynos4_device_spdif;
 extern struct platform_device exynos4_device_sysmmu;
+extern struct platform_device exynos4_device_tmu;
 
 extern struct platform_device samsung_asoc_dma;
 extern struct platform_device samsung_asoc_idma;
-- 
1.7.1


_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply related	[flat|nested] 43+ messages in thread

* Re: [PATCH 2/4] hwmon: exynos4: Move thermal sensor driver to driver/mfd directory
  2012-03-03 11:06   ` Amit Daniel Kachhap
@ 2012-03-03 12:21     ` Sylwester Nawrocki
  -1 siblings, 0 replies; 43+ messages in thread
From: Sylwester Nawrocki @ 2012-03-03 12:21 UTC (permalink / raw)
  To: Amit Daniel Kachhap
  Cc: linux-pm, linux-samsung-soc, linux-kernel, mjg59, linux-acpi,
	lenb, linaro-dev, lm-sensors, eduardo.valentin, durgadoss.r,
	patches

On 03/03/2012 12:06 PM, Amit Daniel Kachhap wrote:
> This movement is needed because the hwmon entries and corresponding
> sysfs interface is a duplicate of utilities already provided by
> driver/thermal/thermal_sys.c. The goal is to place it in mfd folder
> and add necessary calls to get the temperature information.
>
> Signed-off-by: Amit Daniel Kachhap<amit.kachhap@linaro.org>
> Signed-off-by: Donggeun Kim<dg77.kim@samsung.com>
> ---
>   Documentation/hwmon/exynos4_tmu |   81 ------
>   Documentation/mfd/exynos4_tmu   |   81 ++++++
>   drivers/hwmon/Kconfig           |   10 -
>   drivers/hwmon/Makefile          |    1 -
>   drivers/hwmon/exynos4_tmu.c     |  514 ---------------------------------------
>   drivers/mfd/Kconfig             |   10 +
>   drivers/mfd/Makefile            |    1 +
>   drivers/mfd/exynos4_tmu.c       |  514 +++++++++++++++++++++++++++++++++++++++
>   8 files changed, 606 insertions(+), 606 deletions(-)
>   delete mode 100644 Documentation/hwmon/exynos4_tmu
>   create mode 100644 Documentation/mfd/exynos4_tmu
>   delete mode 100644 drivers/hwmon/exynos4_tmu.c
>   create mode 100644 drivers/mfd/exynos4_tmu.c

Please consider adding -M option to git format-patch next time, which
would make the patch smaller and would let to see clearly what's moved
and what has changed.

--
Thanks,
Sylwester

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [lm-sensors] [PATCH 2/4] hwmon: exynos4: Move thermal sensor driver to driver/mfd directory
@ 2012-03-03 12:21     ` Sylwester Nawrocki
  0 siblings, 0 replies; 43+ messages in thread
From: Sylwester Nawrocki @ 2012-03-03 12:21 UTC (permalink / raw)
  To: Amit Daniel Kachhap
  Cc: linux-pm, linux-samsung-soc, linux-kernel, mjg59, linux-acpi,
	lenb, linaro-dev, lm-sensors, eduardo.valentin, durgadoss.r,
	patches

On 03/03/2012 12:06 PM, Amit Daniel Kachhap wrote:
> This movement is needed because the hwmon entries and corresponding
> sysfs interface is a duplicate of utilities already provided by
> driver/thermal/thermal_sys.c. The goal is to place it in mfd folder
> and add necessary calls to get the temperature information.
>
> Signed-off-by: Amit Daniel Kachhap<amit.kachhap@linaro.org>
> Signed-off-by: Donggeun Kim<dg77.kim@samsung.com>
> ---
>   Documentation/hwmon/exynos4_tmu |   81 ------
>   Documentation/mfd/exynos4_tmu   |   81 ++++++
>   drivers/hwmon/Kconfig           |   10 -
>   drivers/hwmon/Makefile          |    1 -
>   drivers/hwmon/exynos4_tmu.c     |  514 ---------------------------------------
>   drivers/mfd/Kconfig             |   10 +
>   drivers/mfd/Makefile            |    1 +
>   drivers/mfd/exynos4_tmu.c       |  514 +++++++++++++++++++++++++++++++++++++++
>   8 files changed, 606 insertions(+), 606 deletions(-)
>   delete mode 100644 Documentation/hwmon/exynos4_tmu
>   create mode 100644 Documentation/mfd/exynos4_tmu
>   delete mode 100644 drivers/hwmon/exynos4_tmu.c
>   create mode 100644 drivers/mfd/exynos4_tmu.c

Please consider adding -M option to git format-patch next time, which
would make the patch smaller and would let to see clearly what's moved
and what has changed.

--
Thanks,
Sylwester

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [lm-sensors] [PATCH 2/4] hwmon: exynos4: Move thermal sensor driver to driver/mfd directory
  2012-03-03 11:06   ` Amit Daniel Kachhap
  (?)
@ 2012-03-03 14:52     ` Guenter Roeck
  -1 siblings, 0 replies; 43+ messages in thread
From: Guenter Roeck @ 2012-03-03 14:52 UTC (permalink / raw)
  To: Amit Daniel Kachhap
  Cc: linux-pm, linux-samsung-soc, linaro-dev, patches, linux-kernel,
	lm-sensors, eduardo.valentin, linux-acpi, lenb

On Sat, Mar 03, 2012 at 06:06:05AM -0500, Amit Daniel Kachhap wrote:
> This movement is needed because the hwmon entries and corresponding
> sysfs interface is a duplicate of utilities already provided by
> driver/thermal/thermal_sys.c. The goal is to place it in mfd folder
> and add necessary calls to get the temperature information.
> 
> Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>
> Signed-off-by: Donggeun Kim <dg77.kim@samsung.com>
> ---
>  Documentation/hwmon/exynos4_tmu |   81 ------
>  Documentation/mfd/exynos4_tmu   |   81 ++++++
>  drivers/hwmon/Kconfig           |   10 -
>  drivers/hwmon/Makefile          |    1 -
>  drivers/hwmon/exynos4_tmu.c     |  514 ---------------------------------------
>  drivers/mfd/Kconfig             |   10 +
>  drivers/mfd/Makefile            |    1 +
>  drivers/mfd/exynos4_tmu.c       |  514 +++++++++++++++++++++++++++++++++++++++
>  8 files changed, 606 insertions(+), 606 deletions(-)
>  delete mode 100644 Documentation/hwmon/exynos4_tmu
>  create mode 100644 Documentation/mfd/exynos4_tmu
>  delete mode 100644 drivers/hwmon/exynos4_tmu.c
>  create mode 100644 drivers/mfd/exynos4_tmu.c
> 

You are just moving the driver from hwmon to mfd. It is still a hwmon driver
and registers itself as hwmon driver. That does not make sense. If you want it
as mfd driver, it should not register itself as hwmon driver. It might have sub-devices
which are thermal and/or hwmon devices in the respective trees, or it might just be
a thermal driver (which then registers itself as hwmon device automatically).

Guenter

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [lm-sensors] [PATCH 2/4] hwmon: exynos4: Move thermal sensor driver to driver/mfd directory
@ 2012-03-03 14:52     ` Guenter Roeck
  0 siblings, 0 replies; 43+ messages in thread
From: Guenter Roeck @ 2012-03-03 14:52 UTC (permalink / raw)
  To: Amit Daniel Kachhap
  Cc: linux-pm, linux-samsung-soc, linaro-dev, patches, linux-kernel,
	lm-sensors, eduardo.valentin, linux-acpi, lenb

On Sat, Mar 03, 2012 at 06:06:05AM -0500, Amit Daniel Kachhap wrote:
> This movement is needed because the hwmon entries and corresponding
> sysfs interface is a duplicate of utilities already provided by
> driver/thermal/thermal_sys.c. The goal is to place it in mfd folder
> and add necessary calls to get the temperature information.
> 
> Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>
> Signed-off-by: Donggeun Kim <dg77.kim@samsung.com>
> ---
>  Documentation/hwmon/exynos4_tmu |   81 ------
>  Documentation/mfd/exynos4_tmu   |   81 ++++++
>  drivers/hwmon/Kconfig           |   10 -
>  drivers/hwmon/Makefile          |    1 -
>  drivers/hwmon/exynos4_tmu.c     |  514 ---------------------------------------
>  drivers/mfd/Kconfig             |   10 +
>  drivers/mfd/Makefile            |    1 +
>  drivers/mfd/exynos4_tmu.c       |  514 +++++++++++++++++++++++++++++++++++++++
>  8 files changed, 606 insertions(+), 606 deletions(-)
>  delete mode 100644 Documentation/hwmon/exynos4_tmu
>  create mode 100644 Documentation/mfd/exynos4_tmu
>  delete mode 100644 drivers/hwmon/exynos4_tmu.c
>  create mode 100644 drivers/mfd/exynos4_tmu.c
> 

You are just moving the driver from hwmon to mfd. It is still a hwmon driver
and registers itself as hwmon driver. That does not make sense. If you want it
as mfd driver, it should not register itself as hwmon driver. It might have sub-devices
which are thermal and/or hwmon devices in the respective trees, or it might just be
a thermal driver (which then registers itself as hwmon device automatically).

Guenter

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [lm-sensors] [PATCH 2/4] hwmon: exynos4: Move thermal sensor driver to driver/mfd directory
@ 2012-03-03 14:52     ` Guenter Roeck
  0 siblings, 0 replies; 43+ messages in thread
From: Guenter Roeck @ 2012-03-03 14:52 UTC (permalink / raw)
  To: Amit Daniel Kachhap
  Cc: linux-pm, linux-samsung-soc, linaro-dev, patches, linux-kernel,
	lm-sensors, eduardo.valentin, linux-acpi, lenb

On Sat, Mar 03, 2012 at 06:06:05AM -0500, Amit Daniel Kachhap wrote:
> This movement is needed because the hwmon entries and corresponding
> sysfs interface is a duplicate of utilities already provided by
> driver/thermal/thermal_sys.c. The goal is to place it in mfd folder
> and add necessary calls to get the temperature information.
> 
> Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>
> Signed-off-by: Donggeun Kim <dg77.kim@samsung.com>
> ---
>  Documentation/hwmon/exynos4_tmu |   81 ------
>  Documentation/mfd/exynos4_tmu   |   81 ++++++
>  drivers/hwmon/Kconfig           |   10 -
>  drivers/hwmon/Makefile          |    1 -
>  drivers/hwmon/exynos4_tmu.c     |  514 ---------------------------------------
>  drivers/mfd/Kconfig             |   10 +
>  drivers/mfd/Makefile            |    1 +
>  drivers/mfd/exynos4_tmu.c       |  514 +++++++++++++++++++++++++++++++++++++++
>  8 files changed, 606 insertions(+), 606 deletions(-)
>  delete mode 100644 Documentation/hwmon/exynos4_tmu
>  create mode 100644 Documentation/mfd/exynos4_tmu
>  delete mode 100644 drivers/hwmon/exynos4_tmu.c
>  create mode 100644 drivers/mfd/exynos4_tmu.c
> 

You are just moving the driver from hwmon to mfd. It is still a hwmon driver
and registers itself as hwmon driver. That does not make sense. If you want it
as mfd driver, it should not register itself as hwmon driver. It might have sub-devices
which are thermal and/or hwmon devices in the respective trees, or it might just be
a thermal driver (which then registers itself as hwmon device automatically).

Guenter

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [lm-sensors] [PATCH 3/4] thermal: exynos4: Register the tmu sensor with the thermal interface layer
  2012-03-03 11:06   ` Amit Daniel Kachhap
  (?)
@ 2012-03-03 14:55     ` Guenter Roeck
  -1 siblings, 0 replies; 43+ messages in thread
From: Guenter Roeck @ 2012-03-03 14:55 UTC (permalink / raw)
  To: Amit Daniel Kachhap
  Cc: linux-pm, linux-samsung-soc, linaro-dev, patches, linux-kernel,
	lm-sensors, eduardo.valentin, linux-acpi, lenb

On Sat, Mar 03, 2012 at 06:06:06AM -0500, Amit Daniel Kachhap wrote:
> Export and register information from the tmu temperature sensor to the samsung
> exynos kernel thermal framework where different cooling devices and thermal
> zone are binded. The exported information is based according to the data
> structure thermal_sensor_conf present in exynos_thermal.h. HWMON sysfs
> functions are removed as all of them are present in generic linux thermal layer.
> 
> Also the platform data structure is modified to pass frequency cooling
> in percentages for each thermal level.
> 
> Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>

Ah, that is what I meant. I don't think it makes sense to have this as separate patch.

Guenter

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [lm-sensors] [PATCH 3/4] thermal: exynos4: Register the tmu sensor with the thermal interface layer
@ 2012-03-03 14:55     ` Guenter Roeck
  0 siblings, 0 replies; 43+ messages in thread
From: Guenter Roeck @ 2012-03-03 14:55 UTC (permalink / raw)
  To: Amit Daniel Kachhap
  Cc: linux-pm, linux-samsung-soc, linaro-dev, patches, linux-kernel,
	lm-sensors, eduardo.valentin, linux-acpi, lenb

On Sat, Mar 03, 2012 at 06:06:06AM -0500, Amit Daniel Kachhap wrote:
> Export and register information from the tmu temperature sensor to the samsung
> exynos kernel thermal framework where different cooling devices and thermal
> zone are binded. The exported information is based according to the data
> structure thermal_sensor_conf present in exynos_thermal.h. HWMON sysfs
> functions are removed as all of them are present in generic linux thermal layer.
> 
> Also the platform data structure is modified to pass frequency cooling
> in percentages for each thermal level.
> 
> Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>

Ah, that is what I meant. I don't think it makes sense to have this as separate patch.

Guenter

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [lm-sensors] [PATCH 3/4] thermal: exynos4: Register the tmu sensor with the thermal interface la
@ 2012-03-03 14:55     ` Guenter Roeck
  0 siblings, 0 replies; 43+ messages in thread
From: Guenter Roeck @ 2012-03-03 14:55 UTC (permalink / raw)
  To: Amit Daniel Kachhap
  Cc: linux-pm, linux-samsung-soc, linaro-dev, patches, linux-kernel,
	lm-sensors, eduardo.valentin, linux-acpi, lenb

On Sat, Mar 03, 2012 at 06:06:06AM -0500, Amit Daniel Kachhap wrote:
> Export and register information from the tmu temperature sensor to the samsung
> exynos kernel thermal framework where different cooling devices and thermal
> zone are binded. The exported information is based according to the data
> structure thermal_sensor_conf present in exynos_thermal.h. HWMON sysfs
> functions are removed as all of them are present in generic linux thermal layer.
> 
> Also the platform data structure is modified to pass frequency cooling
> in percentages for each thermal level.
> 
> Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>

Ah, that is what I meant. I don't think it makes sense to have this as separate patch.

Guenter

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 2/4] hwmon: exynos4: Move thermal sensor driver to driver/mfd directory
  2012-03-03 11:06   ` Amit Daniel Kachhap
  (?)
@ 2012-03-03 16:44     ` Mark Brown
  -1 siblings, 0 replies; 43+ messages in thread
From: Mark Brown @ 2012-03-03 16:44 UTC (permalink / raw)
  To: Amit Daniel Kachhap
  Cc: linux-samsung-soc, linaro-dev, patches, linux-kernel, lm-sensors,
	linux-acpi, linux-pm

On Sat, Mar 03, 2012 at 04:36:05PM +0530, Amit Daniel Kachhap wrote:
> This movement is needed because the hwmon entries and corresponding
> sysfs interface is a duplicate of utilities already provided by
> driver/thermal/thermal_sys.c. The goal is to place it in mfd folder
> and add necessary calls to get the temperature information.

> --- a/Documentation/hwmon/exynos4_tmu
> +++ /dev/null

Moving this seems to be a failure, the device is exposing a hwmon
interface even if you've moved the code to mfd (though it doesn't
actually look like a multi-function device at all as far as I can see -
usually a MFD would have a bunch of unrelated functionality while this
has one function used by two subsystems).

If anything it looks like the ADC driver ought to be moved into IIO with
either generic or Exynos specific function drivers layered on top of it
in hwmon and thermal making use of the values that are read.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [linux-pm] [PATCH 2/4] hwmon: exynos4: Move thermal sensor driver to driver/mfd directory
@ 2012-03-03 16:44     ` Mark Brown
  0 siblings, 0 replies; 43+ messages in thread
From: Mark Brown @ 2012-03-03 16:44 UTC (permalink / raw)
  To: Amit Daniel Kachhap
  Cc: linux-pm, linux-samsung-soc, linaro-dev, patches, linux-kernel,
	lm-sensors, linux-acpi

On Sat, Mar 03, 2012 at 04:36:05PM +0530, Amit Daniel Kachhap wrote:
> This movement is needed because the hwmon entries and corresponding
> sysfs interface is a duplicate of utilities already provided by
> driver/thermal/thermal_sys.c. The goal is to place it in mfd folder
> and add necessary calls to get the temperature information.

> --- a/Documentation/hwmon/exynos4_tmu
> +++ /dev/null

Moving this seems to be a failure, the device is exposing a hwmon
interface even if you've moved the code to mfd (though it doesn't
actually look like a multi-function device at all as far as I can see -
usually a MFD would have a bunch of unrelated functionality while this
has one function used by two subsystems).

If anything it looks like the ADC driver ought to be moved into IIO with
either generic or Exynos specific function drivers layered on top of it
in hwmon and thermal making use of the values that are read.

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [lm-sensors] [linux-pm] [PATCH 2/4] hwmon: exynos4: Move thermal sensor driver to driver/mfd dir
@ 2012-03-03 16:44     ` Mark Brown
  0 siblings, 0 replies; 43+ messages in thread
From: Mark Brown @ 2012-03-03 16:44 UTC (permalink / raw)
  To: Amit Daniel Kachhap
  Cc: linux-samsung-soc, linaro-dev, patches, linux-kernel, lm-sensors,
	linux-acpi, linux-pm

On Sat, Mar 03, 2012 at 04:36:05PM +0530, Amit Daniel Kachhap wrote:
> This movement is needed because the hwmon entries and corresponding
> sysfs interface is a duplicate of utilities already provided by
> driver/thermal/thermal_sys.c. The goal is to place it in mfd folder
> and add necessary calls to get the temperature information.

> --- a/Documentation/hwmon/exynos4_tmu
> +++ /dev/null

Moving this seems to be a failure, the device is exposing a hwmon
interface even if you've moved the code to mfd (though it doesn't
actually look like a multi-function device at all as far as I can see -
usually a MFD would have a bunch of unrelated functionality while this
has one function used by two subsystems).

If anything it looks like the ADC driver ought to be moved into IIO with
either generic or Exynos specific function drivers layered on top of it
in hwmon and thermal making use of the values that are read.

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [lm-sensors] [linux-pm] [PATCH 2/4] hwmon: exynos4: Move thermal sensor driver to driver/mfd directory
  2012-03-03 16:44     ` [linux-pm] [PATCH 2/4] hwmon: exynos4: Move thermal sensor driver to driver/mfd directory Mark Brown
  (?)
@ 2012-03-03 18:04       ` Guenter Roeck
  -1 siblings, 0 replies; 43+ messages in thread
From: Guenter Roeck @ 2012-03-03 18:04 UTC (permalink / raw)
  To: Mark Brown
  Cc: Amit Daniel Kachhap, linux-samsung-soc, linaro-dev, patches,
	linux-kernel, lm-sensors, linux-acpi, linux-pm

On Sat, Mar 03, 2012 at 11:44:10AM -0500, Mark Brown wrote:
> On Sat, Mar 03, 2012 at 04:36:05PM +0530, Amit Daniel Kachhap wrote:
> > This movement is needed because the hwmon entries and corresponding
> > sysfs interface is a duplicate of utilities already provided by
> > driver/thermal/thermal_sys.c. The goal is to place it in mfd folder
> > and add necessary calls to get the temperature information.
> 
> > --- a/Documentation/hwmon/exynos4_tmu
> > +++ /dev/null
> 
> Moving this seems to be a failure, the device is exposing a hwmon
> interface even if you've moved the code to mfd (though it doesn't
> actually look like a multi-function device at all as far as I can see -
> usually a MFD would have a bunch of unrelated functionality while this
> has one function used by two subsystems).
> 
> If anything it looks like the ADC driver ought to be moved into IIO with
> either generic or Exynos specific function drivers layered on top of it
> in hwmon and thermal making use of the values that are read.
> 
I would agree. Or maybe move it all to thermal, since thermal devices register
the hwmon subsystem.

Guenter

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [lm-sensors] [linux-pm] [PATCH 2/4] hwmon: exynos4: Move thermal sensor driver to driver/mfd directory
@ 2012-03-03 18:04       ` Guenter Roeck
  0 siblings, 0 replies; 43+ messages in thread
From: Guenter Roeck @ 2012-03-03 18:04 UTC (permalink / raw)
  To: Mark Brown
  Cc: Amit Daniel Kachhap, linux-samsung-soc, linaro-dev, patches,
	linux-kernel, lm-sensors, linux-acpi, linux-pm

On Sat, Mar 03, 2012 at 11:44:10AM -0500, Mark Brown wrote:
> On Sat, Mar 03, 2012 at 04:36:05PM +0530, Amit Daniel Kachhap wrote:
> > This movement is needed because the hwmon entries and corresponding
> > sysfs interface is a duplicate of utilities already provided by
> > driver/thermal/thermal_sys.c. The goal is to place it in mfd folder
> > and add necessary calls to get the temperature information.
> 
> > --- a/Documentation/hwmon/exynos4_tmu
> > +++ /dev/null
> 
> Moving this seems to be a failure, the device is exposing a hwmon
> interface even if you've moved the code to mfd (though it doesn't
> actually look like a multi-function device at all as far as I can see -
> usually a MFD would have a bunch of unrelated functionality while this
> has one function used by two subsystems).
> 
> If anything it looks like the ADC driver ought to be moved into IIO with
> either generic or Exynos specific function drivers layered on top of it
> in hwmon and thermal making use of the values that are read.
> 
I would agree. Or maybe move it all to thermal, since thermal devices register
the hwmon subsystem.

Guenter

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [lm-sensors] [linux-pm] [PATCH 2/4] hwmon: exynos4: Move thermal sensor driver to driver/mfd dir
@ 2012-03-03 18:04       ` Guenter Roeck
  0 siblings, 0 replies; 43+ messages in thread
From: Guenter Roeck @ 2012-03-03 18:04 UTC (permalink / raw)
  To: Mark Brown
  Cc: Amit Daniel Kachhap, linux-samsung-soc, linaro-dev, patches,
	linux-kernel, lm-sensors, linux-acpi, linux-pm

On Sat, Mar 03, 2012 at 11:44:10AM -0500, Mark Brown wrote:
> On Sat, Mar 03, 2012 at 04:36:05PM +0530, Amit Daniel Kachhap wrote:
> > This movement is needed because the hwmon entries and corresponding
> > sysfs interface is a duplicate of utilities already provided by
> > driver/thermal/thermal_sys.c. The goal is to place it in mfd folder
> > and add necessary calls to get the temperature information.
> 
> > --- a/Documentation/hwmon/exynos4_tmu
> > +++ /dev/null
> 
> Moving this seems to be a failure, the device is exposing a hwmon
> interface even if you've moved the code to mfd (though it doesn't
> actually look like a multi-function device at all as far as I can see -
> usually a MFD would have a bunch of unrelated functionality while this
> has one function used by two subsystems).
> 
> If anything it looks like the ADC driver ought to be moved into IIO with
> either generic or Exynos specific function drivers layered on top of it
> in hwmon and thermal making use of the values that are read.
> 
I would agree. Or maybe move it all to thermal, since thermal devices register
the hwmon subsystem.

Guenter

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 2/4] hwmon: exynos4: Move thermal sensor driver to driver/mfd directory
  2012-03-03 12:21     ` [lm-sensors] " Sylwester Nawrocki
@ 2012-03-05  8:56       ` Amit Kachhap
  -1 siblings, 0 replies; 43+ messages in thread
From: Amit Kachhap @ 2012-03-05  8:44 UTC (permalink / raw)
  To: Sylwester Nawrocki
  Cc: linux-pm, linux-samsung-soc, linux-kernel, mjg59, linux-acpi,
	lenb, linaro-dev, lm-sensors, eduardo.valentin, durgadoss.r,
	patches

On 3 March 2012 17:51, Sylwester Nawrocki <snjw23@gmail.com> wrote:
> On 03/03/2012 12:06 PM, Amit Daniel Kachhap wrote:
>>
>> This movement is needed because the hwmon entries and corresponding
>> sysfs interface is a duplicate of utilities already provided by
>> driver/thermal/thermal_sys.c. The goal is to place it in mfd folder
>> and add necessary calls to get the temperature information.
>>
>> Signed-off-by: Amit Daniel Kachhap<amit.kachhap@linaro.org>
>> Signed-off-by: Donggeun Kim<dg77.kim@samsung.com>
>> ---
>>  Documentation/hwmon/exynos4_tmu |   81 ------
>>  Documentation/mfd/exynos4_tmu   |   81 ++++++
>>  drivers/hwmon/Kconfig           |   10 -
>>  drivers/hwmon/Makefile          |    1 -
>>  drivers/hwmon/exynos4_tmu.c     |  514
>> ---------------------------------------
>>  drivers/mfd/Kconfig             |   10 +
>>  drivers/mfd/Makefile            |    1 +
>>  drivers/mfd/exynos4_tmu.c       |  514
>> +++++++++++++++++++++++++++++++++++++++
>>  8 files changed, 606 insertions(+), 606 deletions(-)
>>  delete mode 100644 Documentation/hwmon/exynos4_tmu
>>  create mode 100644 Documentation/mfd/exynos4_tmu
>>  delete mode 100644 drivers/hwmon/exynos4_tmu.c
>>  create mode 100644 drivers/mfd/exynos4_tmu.c
>
>
> Please consider adding -M option to git format-patch next time, which
> would make the patch smaller and would let to see clearly what's moved
> and what has changed.

Sure , will keep it in mind.

Thanks

>
> --
> Thanks,
> Sylwester

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [lm-sensors] [PATCH 2/4] hwmon: exynos4: Move thermal sensor driver to driver/mfd directory
@ 2012-03-05  8:56       ` Amit Kachhap
  0 siblings, 0 replies; 43+ messages in thread
From: Amit Kachhap @ 2012-03-05  8:56 UTC (permalink / raw)
  To: Sylwester Nawrocki
  Cc: linux-pm, linux-samsung-soc, linux-kernel, mjg59, linux-acpi,
	lenb, linaro-dev, lm-sensors, eduardo.valentin, durgadoss.r,
	patches

On 3 March 2012 17:51, Sylwester Nawrocki <snjw23@gmail.com> wrote:
> On 03/03/2012 12:06 PM, Amit Daniel Kachhap wrote:
>>
>> This movement is needed because the hwmon entries and corresponding
>> sysfs interface is a duplicate of utilities already provided by
>> driver/thermal/thermal_sys.c. The goal is to place it in mfd folder
>> and add necessary calls to get the temperature information.
>>
>> Signed-off-by: Amit Daniel Kachhap<amit.kachhap@linaro.org>
>> Signed-off-by: Donggeun Kim<dg77.kim@samsung.com>
>> ---
>>  Documentation/hwmon/exynos4_tmu |   81 ------
>>  Documentation/mfd/exynos4_tmu   |   81 ++++++
>>  drivers/hwmon/Kconfig           |   10 -
>>  drivers/hwmon/Makefile          |    1 -
>>  drivers/hwmon/exynos4_tmu.c     |  514
>> ---------------------------------------
>>  drivers/mfd/Kconfig             |   10 +
>>  drivers/mfd/Makefile            |    1 +
>>  drivers/mfd/exynos4_tmu.c       |  514
>> +++++++++++++++++++++++++++++++++++++++
>>  8 files changed, 606 insertions(+), 606 deletions(-)
>>  delete mode 100644 Documentation/hwmon/exynos4_tmu
>>  create mode 100644 Documentation/mfd/exynos4_tmu
>>  delete mode 100644 drivers/hwmon/exynos4_tmu.c
>>  create mode 100644 drivers/mfd/exynos4_tmu.c
>
>
> Please consider adding -M option to git format-patch next time, which
> would make the patch smaller and would let to see clearly what's moved
> and what has changed.

Sure , will keep it in mind.

Thanks

>
> --
> Thanks,
> Sylwester

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [lm-sensors] [PATCH 2/4] hwmon: exynos4: Move thermal sensor driver to driver/mfd directory
  2012-03-03 18:04       ` [lm-sensors] [linux-pm] [PATCH 2/4] hwmon: exynos4: Move thermal sensor driver to driver/mfd directory Guenter Roeck
  (?)
@ 2012-03-05  9:11         ` Amit Kachhap
  -1 siblings, 0 replies; 43+ messages in thread
From: Amit Kachhap @ 2012-03-05  9:11 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: linux-samsung-soc, linaro-dev, patches, Mark Brown, linux-kernel,
	lm-sensors, linux-acpi, linux-pm

On 3 March 2012 23:34, Guenter Roeck <guenter.roeck@ericsson.com> wrote:
> On Sat, Mar 03, 2012 at 11:44:10AM -0500, Mark Brown wrote:
>> On Sat, Mar 03, 2012 at 04:36:05PM +0530, Amit Daniel Kachhap wrote:
>> > This movement is needed because the hwmon entries and corresponding
>> > sysfs interface is a duplicate of utilities already provided by
>> > driver/thermal/thermal_sys.c. The goal is to place it in mfd folder
>> > and add necessary calls to get the temperature information.
>>
>> > --- a/Documentation/hwmon/exynos4_tmu
>> > +++ /dev/null
>>
>> Moving this seems to be a failure, the device is exposing a hwmon
>> interface even if you've moved the code to mfd (though it doesn't
>> actually look like a multi-function device at all as far as I can see -
>> usually a MFD would have a bunch of unrelated functionality while this
>> has one function used by two subsystems).
>>
>> If anything it looks like the ADC driver ought to be moved into IIO with
>> either generic or Exynos specific function drivers layered on top of it
>> in hwmon and thermal making use of the values that are read.
>>
> I would agree. Or maybe move it all to thermal, since thermal devices register
> the hwmon subsystem.

Ok I agree with your suggestion of moving into thermal. Since I wanted
to separate exynos specific generic thermal implementation with the
H/W driver so that other versions of the sensor driver can easily hook
into the common part. Anyway this implementation seems possible.

Thanks for the comments
>
> Guenter

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [lm-sensors] [linux-pm] [PATCH 2/4] hwmon: exynos4: Move thermal sensor driver to driver/mfd directory
@ 2012-03-05  9:11         ` Amit Kachhap
  0 siblings, 0 replies; 43+ messages in thread
From: Amit Kachhap @ 2012-03-05  9:11 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Mark Brown, linux-samsung-soc, linaro-dev, patches, linux-kernel,
	lm-sensors, linux-acpi, linux-pm

On 3 March 2012 23:34, Guenter Roeck <guenter.roeck@ericsson.com> wrote:
> On Sat, Mar 03, 2012 at 11:44:10AM -0500, Mark Brown wrote:
>> On Sat, Mar 03, 2012 at 04:36:05PM +0530, Amit Daniel Kachhap wrote:
>> > This movement is needed because the hwmon entries and corresponding
>> > sysfs interface is a duplicate of utilities already provided by
>> > driver/thermal/thermal_sys.c. The goal is to place it in mfd folder
>> > and add necessary calls to get the temperature information.
>>
>> > --- a/Documentation/hwmon/exynos4_tmu
>> > +++ /dev/null
>>
>> Moving this seems to be a failure, the device is exposing a hwmon
>> interface even if you've moved the code to mfd (though it doesn't
>> actually look like a multi-function device at all as far as I can see -
>> usually a MFD would have a bunch of unrelated functionality while this
>> has one function used by two subsystems).
>>
>> If anything it looks like the ADC driver ought to be moved into IIO with
>> either generic or Exynos specific function drivers layered on top of it
>> in hwmon and thermal making use of the values that are read.
>>
> I would agree. Or maybe move it all to thermal, since thermal devices register
> the hwmon subsystem.

Ok I agree with your suggestion of moving into thermal. Since I wanted
to separate exynos specific generic thermal implementation with the
H/W driver so that other versions of the sensor driver can easily hook
into the common part. Anyway this implementation seems possible.

Thanks for the comments
>
> Guenter

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [lm-sensors] [linux-pm] [PATCH 2/4] hwmon: exynos4: Move thermal sensor driver to driver/mfd dir
@ 2012-03-05  9:11         ` Amit Kachhap
  0 siblings, 0 replies; 43+ messages in thread
From: Amit Kachhap @ 2012-03-05  9:23 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: linux-samsung-soc, linaro-dev, patches, Mark Brown, linux-kernel,
	lm-sensors, linux-acpi, linux-pm

On 3 March 2012 23:34, Guenter Roeck <guenter.roeck@ericsson.com> wrote:
> On Sat, Mar 03, 2012 at 11:44:10AM -0500, Mark Brown wrote:
>> On Sat, Mar 03, 2012 at 04:36:05PM +0530, Amit Daniel Kachhap wrote:
>> > This movement is needed because the hwmon entries and corresponding
>> > sysfs interface is a duplicate of utilities already provided by
>> > driver/thermal/thermal_sys.c. The goal is to place it in mfd folder
>> > and add necessary calls to get the temperature information.
>>
>> > --- a/Documentation/hwmon/exynos4_tmu
>> > +++ /dev/null
>>
>> Moving this seems to be a failure, the device is exposing a hwmon
>> interface even if you've moved the code to mfd (though it doesn't
>> actually look like a multi-function device at all as far as I can see -
>> usually a MFD would have a bunch of unrelated functionality while this
>> has one function used by two subsystems).
>>
>> If anything it looks like the ADC driver ought to be moved into IIO with
>> either generic or Exynos specific function drivers layered on top of it
>> in hwmon and thermal making use of the values that are read.
>>
> I would agree. Or maybe move it all to thermal, since thermal devices register
> the hwmon subsystem.

Ok I agree with your suggestion of moving into thermal. Since I wanted
to separate exynos specific generic thermal implementation with the
H/W driver so that other versions of the sensor driver can easily hook
into the common part. Anyway this implementation seems possible.

Thanks for the comments
>
> Guenter

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 43+ messages in thread

* RE: [PATCH 1/4] thermal: exynos: Add thermal interface support for linux thermal layer
  2012-03-03 11:06   ` Amit Daniel Kachhap
  (?)
@ 2012-03-12 10:51     ` R, Durgadoss
  -1 siblings, 0 replies; 43+ messages in thread
From: R, Durgadoss @ 2012-03-12 10:51 UTC (permalink / raw)
  To: Amit Daniel Kachhap, linux-pm, linux-samsung-soc
  Cc: linux-kernel, mjg59, linux-acpi, lenb, linaro-dev, lm-sensors,
	eduardo.valentin, patches

Hi Amit,

Thanks for keeping this up. And Sorry for late reply.

> -----Original Message-----
> From: amit kachhap [mailto:amitdanielk@gmail.com] On Behalf Of Amit Daniel
> Kachhap
> Sent: Saturday, March 03, 2012 4:36 PM
> To: linux-pm@lists.linux-foundation.org; linux-samsung-soc@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org; mjg59@srcf.ucam.org; linux-
> acpi@vger.kernel.org; lenb@kernel.org; linaro-dev@lists.linaro.org; lm-
> sensors@lm-sensors.org; amit.kachhap@linaro.org; eduardo.valentin@ti.com; R,
> Durgadoss; patches@linaro.org
> Subject: [PATCH 1/4] thermal: exynos: Add thermal interface support for linux
> thermal layer
> 
> This codes uses the generic linux thermal layer and creates a bridge
> between temperature sensors, linux thermal framework and cooling devices
> for samsung exynos platform. This layer recieves or monitor the
> temperature from the sensor and informs the generic thermal layer to take
> the necessary cooling action.
> 
> Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>
> ---
>  drivers/thermal/Kconfig          |    8 +
>  drivers/thermal/Makefile         |    1 +
>  drivers/thermal/exynos_thermal.c |  272 ++++++++++++++++++++++++++++++++++++++
>  include/linux/exynos_thermal.h   |   72 ++++++++++
>  4 files changed, 353 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/thermal/exynos_thermal.c
>  create mode 100644 include/linux/exynos_thermal.h
> 
> diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
> index 298c1cd..4e8df56 100644
> --- a/drivers/thermal/Kconfig
> +++ b/drivers/thermal/Kconfig
> @@ -29,3 +29,11 @@ config CPU_THERMAL
>  	  This will be useful for platforms using the generic thermal interface
>  	  and not the ACPI interface.
>  	  If you want this support, you should say Y or M here.
> +
> +config SAMSUNG_THERMAL_INTERFACE
> +	bool "Samsung Thermal interface support"
> +	depends on THERMAL && CPU_THERMAL
> +	help
> +	  This is a samsung thermal interface which will be used as
> +	  a link between sensors and cooling devices with linux thermal
> +	  framework.
> diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
> index 655cbc4..c67b6b2 100644
> --- a/drivers/thermal/Makefile
> +++ b/drivers/thermal/Makefile
> @@ -4,3 +4,4 @@
> 
>  obj-$(CONFIG_THERMAL)		+= thermal_sys.o
>  obj-$(CONFIG_CPU_THERMAL)	+= cpu_cooling.o
> +obj-$(CONFIG_SAMSUNG_THERMAL_INTERFACE)	+= exynos_thermal.o
> diff --git a/drivers/thermal/exynos_thermal.c
> b/drivers/thermal/exynos_thermal.c
> new file mode 100644
> index 0000000..878d45c
> --- /dev/null
> +++ b/drivers/thermal/exynos_thermal.c
> @@ -0,0 +1,272 @@
> +/* linux/drivers/thermal/exynos_thermal.c
> + *
> + * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
> + *		http://www.samsung.com
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> +*/
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/thermal.h>
> +#include <linux/platform_device.h>
> +#include <linux/cpufreq.h>
> +#include <linux/err.h>
> +#include <linux/slab.h>
> +#include <linux/cpu_cooling.h>
> +#include <linux/exynos_thermal.h>
> +
> +#define MAX_COOLING_DEVICE 4
> +struct exynos4_thermal_zone {
> +	unsigned int idle_interval;
> +	unsigned int active_interval;
> +	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;
> +};
> +
> +static struct exynos4_thermal_zone *th_zone;
> +
> +static int exynos4_get_mode(struct thermal_zone_device *thermal,
> +			    enum thermal_device_mode *mode)
> +{
> +	if (th_zone->sensor_conf) {
> +		pr_info("Temperature sensor not initialised\n");
> +		*mode = THERMAL_DEVICE_DISABLED;
> +	} else
> +		*mode = THERMAL_DEVICE_ENABLED;
> +	return 0;
> +}
> +
> +static int exynos4_set_mode(struct thermal_zone_device *thermal,
> +			    enum thermal_device_mode mode)
> +{
> +	if (!th_zone->therm_dev) {
> +		pr_notice("thermal zone not registered\n");
> +		return 0;
> +	}
> +	if (mode == THERMAL_DEVICE_ENABLED)
> +		th_zone->therm_dev->polling_delay =
> +				th_zone->active_interval*1000;
> +	else
> +		th_zone->therm_dev->polling_delay =
> +				th_zone->idle_interval*1000;

If it is 'DISABLED' mode, shouldn't the polling delay be just 0 ?

> +
> +	thermal_zone_device_update(th_zone->therm_dev);
> +	pr_info("thermal polling set for duration=%d sec\n",
> +				th_zone->therm_dev->polling_delay/1000);
> +	return 0;
> +}
> +
> +/*This may be called from interrupt based temperature sensor*/
> +void exynos4_report_trigger(void)
> +{
> +	unsigned int monitor_temp;
> +
> +	if (!th_zone || !th_zone->therm_dev)
> +		return;
> +
> +	monitor_temp = th_zone->sensor_conf->trip_data.trip_val[0];

Why are we checking only against the 0-th trip point ?
Why not for other trip_val[i] also ?

> +
> +	thermal_zone_device_update(th_zone->therm_dev);
> +
> +	mutex_lock(&th_zone->therm_dev->lock);
> +	if (th_zone->therm_dev->last_temperature > monitor_temp)
> +		th_zone->therm_dev->polling_delay =
> +					th_zone->active_interval*1000;
> +	else
> +		th_zone->therm_dev->polling_delay =
> +					th_zone->idle_interval*1000;
> +
> +	kobject_uevent(&th_zone->therm_dev->device.kobj, KOBJ_CHANGE);

Wouldn't it make more sense to pass the trip point id also as an 'env'
parameter ? This way, the user space can easily figure out which trip
point has been crossed.

> +	mutex_unlock(&th_zone->therm_dev->lock);
> +}
> +
> +static int exynos4_get_trip_type(struct thermal_zone_device *thermal, int
> trip,
> +				 enum thermal_trip_type *type)
> +{
> +	if (trip == 0 || trip == 1)
> +		*type = THERMAL_TRIP_STATE_ACTIVE;
> +	else if (trip == 2)
> +		*type = THERMAL_TRIP_CRITICAL;
> +	else
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static int exynos4_get_trip_temp(struct thermal_zone_device *thermal, int
> trip,
> +				 unsigned long *temp)
> +{
> +	/*Monitor zone*/
> +	if (trip == 0)
> +		*temp = th_zone->sensor_conf->trip_data.trip_val[0];
> +	/*Warn zone*/
> +	else if (trip == 1)
> +		*temp = th_zone->sensor_conf->trip_data.trip_val[1];
> +	/*Panic zone*/
> +	else if (trip == 2)
> +		*temp = th_zone->sensor_conf->trip_data.trip_val[2];
> +	else
> +		return -EINVAL;
> +	/*convert the temperature into millicelsius*/
> +	*temp = *temp * 1000;
> +
> +	return 0;
> +}

This could be:

if (trip < 0 || trip >2) return -EINVAL;
*temp = th_zone->sensor_conf->trip_data.trip_val[trip];
return 0;

> +
> +static int exynos4_get_crit_temp(struct thermal_zone_device *thermal,
> +				 unsigned long *temp)
> +{
> +	/*Panic zone*/
> +	*temp = th_zone->sensor_conf->trip_data.trip_val[2];
> +	/*convert the temperature into millicelsius*/
> +	*temp = *temp * 1000;
> +	return 0;
> +}

Why not make it, exynos4_get_trip_temp(thermal, 2, temp) ?

> +
> +static int exynos4_bind(struct thermal_zone_device *thermal,
> +			struct thermal_cooling_device *cdev)
> +{
> +	/* if the cooling device is the one from exynos4 bind it */
> +	if (cdev != th_zone->cool_dev[0])
> +		return 0;
> +
> +	if (thermal_zone_bind_cooling_device(thermal, 0, cdev)) {
> +		pr_err("error binding cooling dev\n");
> +		return -EINVAL;
> +	}
> +	if (thermal_zone_bind_cooling_device(thermal, 1, cdev)) {
> +		pr_err("error binding cooling dev\n");
> +		return -EINVAL;

If we fail here, do you want to remove the earlier 'binding' also ?

> +	}
> +
> +	return 0;
> +
> +}
> +
> +static int exynos4_unbind(struct thermal_zone_device *thermal,
> +			  struct thermal_cooling_device *cdev)
> +{
> +	if (cdev != th_zone->cool_dev[0])
> +		return 0;
> +
> +	if (thermal_zone_unbind_cooling_device(thermal, 0, cdev)) {
> +		pr_err("error unbinding cooling dev\n");
> +		return -EINVAL;

I think we should still go ahead and try to 'unbind' the other one.

> +	}
> +	if (thermal_zone_unbind_cooling_device(thermal, 1, cdev)) {
> +		pr_err("error unbinding cooling dev\n");
> +		return -EINVAL;
> +	}
> +	return 0;
> +
> +}
> +
> +static int exynos4_get_temp(struct thermal_zone_device *thermal,
> +			       unsigned long *temp)
> +{
> +	void *data;
> +
> +	if (!th_zone->sensor_conf) {
> +		pr_info("Temperature sensor not initialised\n");
> +		return -EINVAL;
> +	}
> +	data = th_zone->sensor_conf->private_data;
> +	*temp = th_zone->sensor_conf->read_temperature(data);
> +	/*convert the temperature into millicelsius*/
> +	*temp = *temp * 1000;
> +	return 0;
> +}
> +
> +/* bind callback functions to thermalzone */
> +static struct thermal_zone_device_ops exynos4_dev_ops = {
> +	.bind = exynos4_bind,
> +	.unbind = exynos4_unbind,
> +	.get_temp = exynos4_get_temp,
> +	.get_mode = exynos4_get_mode,
> +	.set_mode = exynos4_set_mode,
> +	.get_trip_type = exynos4_get_trip_type,
> +	.get_trip_temp = exynos4_get_trip_temp,
> +	.get_crit_temp = exynos4_get_crit_temp,
> +};
> +
> +int exynos4_register_thermal(struct thermal_sensor_conf *sensor_conf)
> +{
> +	int ret, count, tab_size;
> +	struct freq_pctg_table *tab_ptr;
> +
> +	if (!sensor_conf || !sensor_conf->read_temperature) {
> +		pr_err("Temperature sensor not initialised\n");
> +		return -EINVAL;
> +	}
> +
> +	th_zone = kzalloc(sizeof(struct exynos4_thermal_zone), GFP_KERNEL);
> +	if (!th_zone) {
> +		ret = -ENOMEM;
> +		goto err_unregister;
> +	}
> +
> +	th_zone->sensor_conf = sensor_conf;
> +
> +	tab_ptr = (struct freq_pctg_table *)sensor_conf->cooling_data.freq_data;
> +	tab_size = sensor_conf->cooling_data.freq_pctg_count;
> +
> +	/*Register the cpufreq cooling device*/
> +	th_zone->cool_dev_size = 1;
> +	count = 0;
> +	th_zone->cool_dev[count] = cpufreq_cooling_register(
> +			(struct freq_pctg_table *)&(tab_ptr[count]),
> +			tab_size, cpumask_of(0));
> +
> +	if (IS_ERR(th_zone->cool_dev[count])) {
> +		pr_err("Failed to register cpufreq cooling device\n");
> +		ret = -EINVAL;
> +		th_zone->cool_dev_size = 0;
> +		goto err_unregister;
> +	}
> +
> +	th_zone->therm_dev = thermal_zone_device_register(sensor_conf->name,
> +				3, NULL, &exynos4_dev_ops, 0, 0, 0, 1000);
> +	if (IS_ERR(th_zone->therm_dev)) {
> +		pr_err("Failed to register thermal zone device\n");
> +		ret = -EINVAL;
> +		goto err_unregister;
> +	}
> +
> +	th_zone->active_interval = 1;
> +	th_zone->idle_interval = 10;
> +
> +	exynos4_set_mode(th_zone->therm_dev, THERMAL_DEVICE_DISABLED);
> +
> +	pr_info("Exynos: Kernel Thermal management registered\n");
> +
> +	return 0;
> +
> +err_unregister:
> +	exynos4_unregister_thermal();
> +	return ret;
> +}
> +EXPORT_SYMBOL(exynos4_register_thermal);
> +
> +void exynos4_unregister_thermal(void)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < th_zone->cool_dev_size; i++) {
> +		if (th_zone && th_zone->cool_dev[i])
> +			cpufreq_cooling_unregister(th_zone->cool_dev[i]);
> +	}
> +
> +	if (th_zone && th_zone->therm_dev)
> +		thermal_zone_device_unregister(th_zone->therm_dev);
> +
> +	kfree(th_zone);
> +
> +	pr_info("Exynos: Kernel Thermal management unregistered\n");
> +}
> +EXPORT_SYMBOL(exynos4_unregister_thermal);
> diff --git a/include/linux/exynos_thermal.h b/include/linux/exynos_thermal.h
> new file mode 100644
> index 0000000..186e409
> --- /dev/null
> +++ b/include/linux/exynos_thermal.h
> @@ -0,0 +1,72 @@
> +/* linux/include/linux/exynos_thermal.h
> + *
> + * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
> + *		http://www.samsung.com
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> +*/
> +
> +#ifndef THERMAL_INTERFACE_H
> +#define THERMAL_INTERFACE_H
> +/* CPU Zone information */
> +
> +#define SENSOR_NAME_LEN	16
> +#define MAX_TRIP_COUNT	8
> +
> +#define PANIC_ZONE      4
> +#define WARN_ZONE       3
> +#define MONITOR_ZONE    2
> +#define SAFE_ZONE       1
> +#define NO_ACTION       0

I don't get why we need two separate SAFE and NO_ACTION
zones..To me, both should be the same.

> +
> +
> +struct	thermal_trip_point_conf {
> +	int trip_val[MAX_TRIP_COUNT];
> +	int trip_count;
> +};
> +
> +struct	thermal_cooling_conf {
> +	struct freq_pctg_table freq_data[MAX_TRIP_COUNT];
> +	int freq_pctg_count;
> +};
> +
> +/**
> + * struct exynos4_tmu_platform_data
> + * @name: name of the temperature sensor
> + * @read_temperature: A function pointer to read temperature info
> + * @private_data: Temperature sensor private data
> + * @sensor_data: Sensor specific information like trigger temperature, level
> + */
> +struct thermal_sensor_conf {
> +	char	name[SENSOR_NAME_LEN];
> +	int	(*read_temperature)(void *data);
> +	struct	thermal_trip_point_conf trip_data;
> +	struct	thermal_cooling_conf cooling_data;

Since both trip_count and freq_pctg_count are same at all
times (please correct me if I am wrong) I think it's better
to have a single 'count' variable inside this structure and move
trip_val and freq_data to this structure directly.

One General Concern:
Why do we even need this exynos_thermal layer between Thermal Framework
and the Thermal Sensor Drivers ? I think the thermal sensor driver (in
this case exynos_tmu.c) can directly register with the Thermal framework.
This means, we will soon have platformXXX_thermal.c files for each
platform, which is not really a good way to go IMHO.
I also understand that the framework does not have alarm attributes,
notification support etc..But We can add them if needed.

I have reviewed your two sets of patches independently. My only
request to you would be to post the next versions of both the patch
sets at the same time, so that it becomes easier to understand and test.

Thanks,
Durga

> +	void	*private_data;
> +};
> +
> +/**
> + * exynos4_register_thermal: Register to the exynos thermal interface.
> + * @sensor_conf:   Structure containing temperature sensor information
> + *
> + * returns zero on success, else negative errno.
> + */
> +int exynos4_register_thermal(struct thermal_sensor_conf *sensor_conf);
> +
> +/**
> + * exynos4_unregister_thermal: Un-register from the exynos thermal interface.
> + *
> + * return not applicable.
> + */
> +void exynos4_unregister_thermal(void);
> +
> +/**
> + * exynos4_report_trigger: Report any trigger level crossed in the
> + *	temperature sensor. This may be useful to take any cooling action.
> + *
> + * return not applicable.
> + */
> +extern void exynos4_report_trigger(void);
> +#endif
> --
> 1.7.1


^ permalink raw reply	[flat|nested] 43+ messages in thread

* RE: [PATCH 1/4] thermal: exynos: Add thermal interface support for linux thermal layer
@ 2012-03-12 10:51     ` R, Durgadoss
  0 siblings, 0 replies; 43+ messages in thread
From: R, Durgadoss @ 2012-03-12 10:51 UTC (permalink / raw)
  To: Amit Daniel Kachhap, linux-pm, linux-samsung-soc
  Cc: linux-kernel, mjg59, linux-acpi, lenb, linaro-dev, lm-sensors,
	eduardo.valentin, patches

Hi Amit,

Thanks for keeping this up. And Sorry for late reply.

> -----Original Message-----
> From: amit kachhap [mailto:amitdanielk@gmail.com] On Behalf Of Amit Daniel
> Kachhap
> Sent: Saturday, March 03, 2012 4:36 PM
> To: linux-pm@lists.linux-foundation.org; linux-samsung-soc@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org; mjg59@srcf.ucam.org; linux-
> acpi@vger.kernel.org; lenb@kernel.org; linaro-dev@lists.linaro.org; lm-
> sensors@lm-sensors.org; amit.kachhap@linaro.org; eduardo.valentin@ti.com; R,
> Durgadoss; patches@linaro.org
> Subject: [PATCH 1/4] thermal: exynos: Add thermal interface support for linux
> thermal layer
> 
> This codes uses the generic linux thermal layer and creates a bridge
> between temperature sensors, linux thermal framework and cooling devices
> for samsung exynos platform. This layer recieves or monitor the
> temperature from the sensor and informs the generic thermal layer to take
> the necessary cooling action.
> 
> Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>
> ---
>  drivers/thermal/Kconfig          |    8 +
>  drivers/thermal/Makefile         |    1 +
>  drivers/thermal/exynos_thermal.c |  272 ++++++++++++++++++++++++++++++++++++++
>  include/linux/exynos_thermal.h   |   72 ++++++++++
>  4 files changed, 353 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/thermal/exynos_thermal.c
>  create mode 100644 include/linux/exynos_thermal.h
> 
> diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
> index 298c1cd..4e8df56 100644
> --- a/drivers/thermal/Kconfig
> +++ b/drivers/thermal/Kconfig
> @@ -29,3 +29,11 @@ config CPU_THERMAL
>  	  This will be useful for platforms using the generic thermal interface
>  	  and not the ACPI interface.
>  	  If you want this support, you should say Y or M here.
> +
> +config SAMSUNG_THERMAL_INTERFACE
> +	bool "Samsung Thermal interface support"
> +	depends on THERMAL && CPU_THERMAL
> +	help
> +	  This is a samsung thermal interface which will be used as
> +	  a link between sensors and cooling devices with linux thermal
> +	  framework.
> diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
> index 655cbc4..c67b6b2 100644
> --- a/drivers/thermal/Makefile
> +++ b/drivers/thermal/Makefile
> @@ -4,3 +4,4 @@
> 
>  obj-$(CONFIG_THERMAL)		+= thermal_sys.o
>  obj-$(CONFIG_CPU_THERMAL)	+= cpu_cooling.o
> +obj-$(CONFIG_SAMSUNG_THERMAL_INTERFACE)	+= exynos_thermal.o
> diff --git a/drivers/thermal/exynos_thermal.c
> b/drivers/thermal/exynos_thermal.c
> new file mode 100644
> index 0000000..878d45c
> --- /dev/null
> +++ b/drivers/thermal/exynos_thermal.c
> @@ -0,0 +1,272 @@
> +/* linux/drivers/thermal/exynos_thermal.c
> + *
> + * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
> + *		http://www.samsung.com
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> +*/
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/thermal.h>
> +#include <linux/platform_device.h>
> +#include <linux/cpufreq.h>
> +#include <linux/err.h>
> +#include <linux/slab.h>
> +#include <linux/cpu_cooling.h>
> +#include <linux/exynos_thermal.h>
> +
> +#define MAX_COOLING_DEVICE 4
> +struct exynos4_thermal_zone {
> +	unsigned int idle_interval;
> +	unsigned int active_interval;
> +	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;
> +};
> +
> +static struct exynos4_thermal_zone *th_zone;
> +
> +static int exynos4_get_mode(struct thermal_zone_device *thermal,
> +			    enum thermal_device_mode *mode)
> +{
> +	if (th_zone->sensor_conf) {
> +		pr_info("Temperature sensor not initialised\n");
> +		*mode = THERMAL_DEVICE_DISABLED;
> +	} else
> +		*mode = THERMAL_DEVICE_ENABLED;
> +	return 0;
> +}
> +
> +static int exynos4_set_mode(struct thermal_zone_device *thermal,
> +			    enum thermal_device_mode mode)
> +{
> +	if (!th_zone->therm_dev) {
> +		pr_notice("thermal zone not registered\n");
> +		return 0;
> +	}
> +	if (mode == THERMAL_DEVICE_ENABLED)
> +		th_zone->therm_dev->polling_delay =
> +				th_zone->active_interval*1000;
> +	else
> +		th_zone->therm_dev->polling_delay =
> +				th_zone->idle_interval*1000;

If it is 'DISABLED' mode, shouldn't the polling delay be just 0 ?

> +
> +	thermal_zone_device_update(th_zone->therm_dev);
> +	pr_info("thermal polling set for duration=%d sec\n",
> +				th_zone->therm_dev->polling_delay/1000);
> +	return 0;
> +}
> +
> +/*This may be called from interrupt based temperature sensor*/
> +void exynos4_report_trigger(void)
> +{
> +	unsigned int monitor_temp;
> +
> +	if (!th_zone || !th_zone->therm_dev)
> +		return;
> +
> +	monitor_temp = th_zone->sensor_conf->trip_data.trip_val[0];

Why are we checking only against the 0-th trip point ?
Why not for other trip_val[i] also ?

> +
> +	thermal_zone_device_update(th_zone->therm_dev);
> +
> +	mutex_lock(&th_zone->therm_dev->lock);
> +	if (th_zone->therm_dev->last_temperature > monitor_temp)
> +		th_zone->therm_dev->polling_delay =
> +					th_zone->active_interval*1000;
> +	else
> +		th_zone->therm_dev->polling_delay =
> +					th_zone->idle_interval*1000;
> +
> +	kobject_uevent(&th_zone->therm_dev->device.kobj, KOBJ_CHANGE);

Wouldn't it make more sense to pass the trip point id also as an 'env'
parameter ? This way, the user space can easily figure out which trip
point has been crossed.

> +	mutex_unlock(&th_zone->therm_dev->lock);
> +}
> +
> +static int exynos4_get_trip_type(struct thermal_zone_device *thermal, int
> trip,
> +				 enum thermal_trip_type *type)
> +{
> +	if (trip == 0 || trip == 1)
> +		*type = THERMAL_TRIP_STATE_ACTIVE;
> +	else if (trip == 2)
> +		*type = THERMAL_TRIP_CRITICAL;
> +	else
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static int exynos4_get_trip_temp(struct thermal_zone_device *thermal, int
> trip,
> +				 unsigned long *temp)
> +{
> +	/*Monitor zone*/
> +	if (trip == 0)
> +		*temp = th_zone->sensor_conf->trip_data.trip_val[0];
> +	/*Warn zone*/
> +	else if (trip == 1)
> +		*temp = th_zone->sensor_conf->trip_data.trip_val[1];
> +	/*Panic zone*/
> +	else if (trip == 2)
> +		*temp = th_zone->sensor_conf->trip_data.trip_val[2];
> +	else
> +		return -EINVAL;
> +	/*convert the temperature into millicelsius*/
> +	*temp = *temp * 1000;
> +
> +	return 0;
> +}

This could be:

if (trip < 0 || trip >2) return -EINVAL;
*temp = th_zone->sensor_conf->trip_data.trip_val[trip];
return 0;

> +
> +static int exynos4_get_crit_temp(struct thermal_zone_device *thermal,
> +				 unsigned long *temp)
> +{
> +	/*Panic zone*/
> +	*temp = th_zone->sensor_conf->trip_data.trip_val[2];
> +	/*convert the temperature into millicelsius*/
> +	*temp = *temp * 1000;
> +	return 0;
> +}

Why not make it, exynos4_get_trip_temp(thermal, 2, temp) ?

> +
> +static int exynos4_bind(struct thermal_zone_device *thermal,
> +			struct thermal_cooling_device *cdev)
> +{
> +	/* if the cooling device is the one from exynos4 bind it */
> +	if (cdev != th_zone->cool_dev[0])
> +		return 0;
> +
> +	if (thermal_zone_bind_cooling_device(thermal, 0, cdev)) {
> +		pr_err("error binding cooling dev\n");
> +		return -EINVAL;
> +	}
> +	if (thermal_zone_bind_cooling_device(thermal, 1, cdev)) {
> +		pr_err("error binding cooling dev\n");
> +		return -EINVAL;

If we fail here, do you want to remove the earlier 'binding' also ?

> +	}
> +
> +	return 0;
> +
> +}
> +
> +static int exynos4_unbind(struct thermal_zone_device *thermal,
> +			  struct thermal_cooling_device *cdev)
> +{
> +	if (cdev != th_zone->cool_dev[0])
> +		return 0;
> +
> +	if (thermal_zone_unbind_cooling_device(thermal, 0, cdev)) {
> +		pr_err("error unbinding cooling dev\n");
> +		return -EINVAL;

I think we should still go ahead and try to 'unbind' the other one.

> +	}
> +	if (thermal_zone_unbind_cooling_device(thermal, 1, cdev)) {
> +		pr_err("error unbinding cooling dev\n");
> +		return -EINVAL;
> +	}
> +	return 0;
> +
> +}
> +
> +static int exynos4_get_temp(struct thermal_zone_device *thermal,
> +			       unsigned long *temp)
> +{
> +	void *data;
> +
> +	if (!th_zone->sensor_conf) {
> +		pr_info("Temperature sensor not initialised\n");
> +		return -EINVAL;
> +	}
> +	data = th_zone->sensor_conf->private_data;
> +	*temp = th_zone->sensor_conf->read_temperature(data);
> +	/*convert the temperature into millicelsius*/
> +	*temp = *temp * 1000;
> +	return 0;
> +}
> +
> +/* bind callback functions to thermalzone */
> +static struct thermal_zone_device_ops exynos4_dev_ops = {
> +	.bind = exynos4_bind,
> +	.unbind = exynos4_unbind,
> +	.get_temp = exynos4_get_temp,
> +	.get_mode = exynos4_get_mode,
> +	.set_mode = exynos4_set_mode,
> +	.get_trip_type = exynos4_get_trip_type,
> +	.get_trip_temp = exynos4_get_trip_temp,
> +	.get_crit_temp = exynos4_get_crit_temp,
> +};
> +
> +int exynos4_register_thermal(struct thermal_sensor_conf *sensor_conf)
> +{
> +	int ret, count, tab_size;
> +	struct freq_pctg_table *tab_ptr;
> +
> +	if (!sensor_conf || !sensor_conf->read_temperature) {
> +		pr_err("Temperature sensor not initialised\n");
> +		return -EINVAL;
> +	}
> +
> +	th_zone = kzalloc(sizeof(struct exynos4_thermal_zone), GFP_KERNEL);
> +	if (!th_zone) {
> +		ret = -ENOMEM;
> +		goto err_unregister;
> +	}
> +
> +	th_zone->sensor_conf = sensor_conf;
> +
> +	tab_ptr = (struct freq_pctg_table *)sensor_conf->cooling_data.freq_data;
> +	tab_size = sensor_conf->cooling_data.freq_pctg_count;
> +
> +	/*Register the cpufreq cooling device*/
> +	th_zone->cool_dev_size = 1;
> +	count = 0;
> +	th_zone->cool_dev[count] = cpufreq_cooling_register(
> +			(struct freq_pctg_table *)&(tab_ptr[count]),
> +			tab_size, cpumask_of(0));
> +
> +	if (IS_ERR(th_zone->cool_dev[count])) {
> +		pr_err("Failed to register cpufreq cooling device\n");
> +		ret = -EINVAL;
> +		th_zone->cool_dev_size = 0;
> +		goto err_unregister;
> +	}
> +
> +	th_zone->therm_dev = thermal_zone_device_register(sensor_conf->name,
> +				3, NULL, &exynos4_dev_ops, 0, 0, 0, 1000);
> +	if (IS_ERR(th_zone->therm_dev)) {
> +		pr_err("Failed to register thermal zone device\n");
> +		ret = -EINVAL;
> +		goto err_unregister;
> +	}
> +
> +	th_zone->active_interval = 1;
> +	th_zone->idle_interval = 10;
> +
> +	exynos4_set_mode(th_zone->therm_dev, THERMAL_DEVICE_DISABLED);
> +
> +	pr_info("Exynos: Kernel Thermal management registered\n");
> +
> +	return 0;
> +
> +err_unregister:
> +	exynos4_unregister_thermal();
> +	return ret;
> +}
> +EXPORT_SYMBOL(exynos4_register_thermal);
> +
> +void exynos4_unregister_thermal(void)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < th_zone->cool_dev_size; i++) {
> +		if (th_zone && th_zone->cool_dev[i])
> +			cpufreq_cooling_unregister(th_zone->cool_dev[i]);
> +	}
> +
> +	if (th_zone && th_zone->therm_dev)
> +		thermal_zone_device_unregister(th_zone->therm_dev);
> +
> +	kfree(th_zone);
> +
> +	pr_info("Exynos: Kernel Thermal management unregistered\n");
> +}
> +EXPORT_SYMBOL(exynos4_unregister_thermal);
> diff --git a/include/linux/exynos_thermal.h b/include/linux/exynos_thermal.h
> new file mode 100644
> index 0000000..186e409
> --- /dev/null
> +++ b/include/linux/exynos_thermal.h
> @@ -0,0 +1,72 @@
> +/* linux/include/linux/exynos_thermal.h
> + *
> + * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
> + *		http://www.samsung.com
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> +*/
> +
> +#ifndef THERMAL_INTERFACE_H
> +#define THERMAL_INTERFACE_H
> +/* CPU Zone information */
> +
> +#define SENSOR_NAME_LEN	16
> +#define MAX_TRIP_COUNT	8
> +
> +#define PANIC_ZONE      4
> +#define WARN_ZONE       3
> +#define MONITOR_ZONE    2
> +#define SAFE_ZONE       1
> +#define NO_ACTION       0

I don't get why we need two separate SAFE and NO_ACTION
zones..To me, both should be the same.

> +
> +
> +struct	thermal_trip_point_conf {
> +	int trip_val[MAX_TRIP_COUNT];
> +	int trip_count;
> +};
> +
> +struct	thermal_cooling_conf {
> +	struct freq_pctg_table freq_data[MAX_TRIP_COUNT];
> +	int freq_pctg_count;
> +};
> +
> +/**
> + * struct exynos4_tmu_platform_data
> + * @name: name of the temperature sensor
> + * @read_temperature: A function pointer to read temperature info
> + * @private_data: Temperature sensor private data
> + * @sensor_data: Sensor specific information like trigger temperature, level
> + */
> +struct thermal_sensor_conf {
> +	char	name[SENSOR_NAME_LEN];
> +	int	(*read_temperature)(void *data);
> +	struct	thermal_trip_point_conf trip_data;
> +	struct	thermal_cooling_conf cooling_data;

Since both trip_count and freq_pctg_count are same at all
times (please correct me if I am wrong) I think it's better
to have a single 'count' variable inside this structure and move
trip_val and freq_data to this structure directly.

One General Concern:
Why do we even need this exynos_thermal layer between Thermal Framework
and the Thermal Sensor Drivers ? I think the thermal sensor driver (in
this case exynos_tmu.c) can directly register with the Thermal framework.
This means, we will soon have platformXXX_thermal.c files for each
platform, which is not really a good way to go IMHO.
I also understand that the framework does not have alarm attributes,
notification support etc..But We can add them if needed.

I have reviewed your two sets of patches independently. My only
request to you would be to post the next versions of both the patch
sets at the same time, so that it becomes easier to understand and test.

Thanks,
Durga

> +	void	*private_data;
> +};
> +
> +/**
> + * exynos4_register_thermal: Register to the exynos thermal interface.
> + * @sensor_conf:   Structure containing temperature sensor information
> + *
> + * returns zero on success, else negative errno.
> + */
> +int exynos4_register_thermal(struct thermal_sensor_conf *sensor_conf);
> +
> +/**
> + * exynos4_unregister_thermal: Un-register from the exynos thermal interface.
> + *
> + * return not applicable.
> + */
> +void exynos4_unregister_thermal(void);
> +
> +/**
> + * exynos4_report_trigger: Report any trigger level crossed in the
> + *	temperature sensor. This may be useful to take any cooling action.
> + *
> + * return not applicable.
> + */
> +extern void exynos4_report_trigger(void);
> +#endif
> --
> 1.7.1


^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [lm-sensors] [PATCH 1/4] thermal: exynos: Add thermal interface support for linux thermal layer
@ 2012-03-12 10:51     ` R, Durgadoss
  0 siblings, 0 replies; 43+ messages in thread
From: R, Durgadoss @ 2012-03-12 10:51 UTC (permalink / raw)
  To: Amit Daniel Kachhap, linux-pm, linux-samsung-soc
  Cc: linux-kernel, mjg59, linux-acpi, lenb, linaro-dev, lm-sensors,
	eduardo.valentin, patches

Hi Amit,

Thanks for keeping this up. And Sorry for late reply.

> -----Original Message-----
> From: amit kachhap [mailto:amitdanielk@gmail.com] On Behalf Of Amit Daniel
> Kachhap
> Sent: Saturday, March 03, 2012 4:36 PM
> To: linux-pm@lists.linux-foundation.org; linux-samsung-soc@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org; mjg59@srcf.ucam.org; linux-
> acpi@vger.kernel.org; lenb@kernel.org; linaro-dev@lists.linaro.org; lm-
> sensors@lm-sensors.org; amit.kachhap@linaro.org; eduardo.valentin@ti.com; R,
> Durgadoss; patches@linaro.org
> Subject: [PATCH 1/4] thermal: exynos: Add thermal interface support for linux
> thermal layer
> 
> This codes uses the generic linux thermal layer and creates a bridge
> between temperature sensors, linux thermal framework and cooling devices
> for samsung exynos platform. This layer recieves or monitor the
> temperature from the sensor and informs the generic thermal layer to take
> the necessary cooling action.
> 
> Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>
> ---
>  drivers/thermal/Kconfig          |    8 +
>  drivers/thermal/Makefile         |    1 +
>  drivers/thermal/exynos_thermal.c |  272 ++++++++++++++++++++++++++++++++++++++
>  include/linux/exynos_thermal.h   |   72 ++++++++++
>  4 files changed, 353 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/thermal/exynos_thermal.c
>  create mode 100644 include/linux/exynos_thermal.h
> 
> diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
> index 298c1cd..4e8df56 100644
> --- a/drivers/thermal/Kconfig
> +++ b/drivers/thermal/Kconfig
> @@ -29,3 +29,11 @@ config CPU_THERMAL
>  	  This will be useful for platforms using the generic thermal interface
>  	  and not the ACPI interface.
>  	  If you want this support, you should say Y or M here.
> +
> +config SAMSUNG_THERMAL_INTERFACE
> +	bool "Samsung Thermal interface support"
> +	depends on THERMAL && CPU_THERMAL
> +	help
> +	  This is a samsung thermal interface which will be used as
> +	  a link between sensors and cooling devices with linux thermal
> +	  framework.
> diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
> index 655cbc4..c67b6b2 100644
> --- a/drivers/thermal/Makefile
> +++ b/drivers/thermal/Makefile
> @@ -4,3 +4,4 @@
> 
>  obj-$(CONFIG_THERMAL)		+= thermal_sys.o
>  obj-$(CONFIG_CPU_THERMAL)	+= cpu_cooling.o
> +obj-$(CONFIG_SAMSUNG_THERMAL_INTERFACE)	+= exynos_thermal.o
> diff --git a/drivers/thermal/exynos_thermal.c
> b/drivers/thermal/exynos_thermal.c
> new file mode 100644
> index 0000000..878d45c
> --- /dev/null
> +++ b/drivers/thermal/exynos_thermal.c
> @@ -0,0 +1,272 @@
> +/* linux/drivers/thermal/exynos_thermal.c
> + *
> + * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
> + *		http://www.samsung.com
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> +*/
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/thermal.h>
> +#include <linux/platform_device.h>
> +#include <linux/cpufreq.h>
> +#include <linux/err.h>
> +#include <linux/slab.h>
> +#include <linux/cpu_cooling.h>
> +#include <linux/exynos_thermal.h>
> +
> +#define MAX_COOLING_DEVICE 4
> +struct exynos4_thermal_zone {
> +	unsigned int idle_interval;
> +	unsigned int active_interval;
> +	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;
> +};
> +
> +static struct exynos4_thermal_zone *th_zone;
> +
> +static int exynos4_get_mode(struct thermal_zone_device *thermal,
> +			    enum thermal_device_mode *mode)
> +{
> +	if (th_zone->sensor_conf) {
> +		pr_info("Temperature sensor not initialised\n");
> +		*mode = THERMAL_DEVICE_DISABLED;
> +	} else
> +		*mode = THERMAL_DEVICE_ENABLED;
> +	return 0;
> +}
> +
> +static int exynos4_set_mode(struct thermal_zone_device *thermal,
> +			    enum thermal_device_mode mode)
> +{
> +	if (!th_zone->therm_dev) {
> +		pr_notice("thermal zone not registered\n");
> +		return 0;
> +	}
> +	if (mode = THERMAL_DEVICE_ENABLED)
> +		th_zone->therm_dev->polling_delay > +				th_zone->active_interval*1000;
> +	else
> +		th_zone->therm_dev->polling_delay > +				th_zone->idle_interval*1000;

If it is 'DISABLED' mode, shouldn't the polling delay be just 0 ?

> +
> +	thermal_zone_device_update(th_zone->therm_dev);
> +	pr_info("thermal polling set for duration=%d sec\n",
> +				th_zone->therm_dev->polling_delay/1000);
> +	return 0;
> +}
> +
> +/*This may be called from interrupt based temperature sensor*/
> +void exynos4_report_trigger(void)
> +{
> +	unsigned int monitor_temp;
> +
> +	if (!th_zone || !th_zone->therm_dev)
> +		return;
> +
> +	monitor_temp = th_zone->sensor_conf->trip_data.trip_val[0];

Why are we checking only against the 0-th trip point ?
Why not for other trip_val[i] also ?

> +
> +	thermal_zone_device_update(th_zone->therm_dev);
> +
> +	mutex_lock(&th_zone->therm_dev->lock);
> +	if (th_zone->therm_dev->last_temperature > monitor_temp)
> +		th_zone->therm_dev->polling_delay > +					th_zone->active_interval*1000;
> +	else
> +		th_zone->therm_dev->polling_delay > +					th_zone->idle_interval*1000;
> +
> +	kobject_uevent(&th_zone->therm_dev->device.kobj, KOBJ_CHANGE);

Wouldn't it make more sense to pass the trip point id also as an 'env'
parameter ? This way, the user space can easily figure out which trip
point has been crossed.

> +	mutex_unlock(&th_zone->therm_dev->lock);
> +}
> +
> +static int exynos4_get_trip_type(struct thermal_zone_device *thermal, int
> trip,
> +				 enum thermal_trip_type *type)
> +{
> +	if (trip = 0 || trip = 1)
> +		*type = THERMAL_TRIP_STATE_ACTIVE;
> +	else if (trip = 2)
> +		*type = THERMAL_TRIP_CRITICAL;
> +	else
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static int exynos4_get_trip_temp(struct thermal_zone_device *thermal, int
> trip,
> +				 unsigned long *temp)
> +{
> +	/*Monitor zone*/
> +	if (trip = 0)
> +		*temp = th_zone->sensor_conf->trip_data.trip_val[0];
> +	/*Warn zone*/
> +	else if (trip = 1)
> +		*temp = th_zone->sensor_conf->trip_data.trip_val[1];
> +	/*Panic zone*/
> +	else if (trip = 2)
> +		*temp = th_zone->sensor_conf->trip_data.trip_val[2];
> +	else
> +		return -EINVAL;
> +	/*convert the temperature into millicelsius*/
> +	*temp = *temp * 1000;
> +
> +	return 0;
> +}

This could be:

if (trip < 0 || trip >2) return -EINVAL;
*temp = th_zone->sensor_conf->trip_data.trip_val[trip];
return 0;

> +
> +static int exynos4_get_crit_temp(struct thermal_zone_device *thermal,
> +				 unsigned long *temp)
> +{
> +	/*Panic zone*/
> +	*temp = th_zone->sensor_conf->trip_data.trip_val[2];
> +	/*convert the temperature into millicelsius*/
> +	*temp = *temp * 1000;
> +	return 0;
> +}

Why not make it, exynos4_get_trip_temp(thermal, 2, temp) ?

> +
> +static int exynos4_bind(struct thermal_zone_device *thermal,
> +			struct thermal_cooling_device *cdev)
> +{
> +	/* if the cooling device is the one from exynos4 bind it */
> +	if (cdev != th_zone->cool_dev[0])
> +		return 0;
> +
> +	if (thermal_zone_bind_cooling_device(thermal, 0, cdev)) {
> +		pr_err("error binding cooling dev\n");
> +		return -EINVAL;
> +	}
> +	if (thermal_zone_bind_cooling_device(thermal, 1, cdev)) {
> +		pr_err("error binding cooling dev\n");
> +		return -EINVAL;

If we fail here, do you want to remove the earlier 'binding' also ?

> +	}
> +
> +	return 0;
> +
> +}
> +
> +static int exynos4_unbind(struct thermal_zone_device *thermal,
> +			  struct thermal_cooling_device *cdev)
> +{
> +	if (cdev != th_zone->cool_dev[0])
> +		return 0;
> +
> +	if (thermal_zone_unbind_cooling_device(thermal, 0, cdev)) {
> +		pr_err("error unbinding cooling dev\n");
> +		return -EINVAL;

I think we should still go ahead and try to 'unbind' the other one.

> +	}
> +	if (thermal_zone_unbind_cooling_device(thermal, 1, cdev)) {
> +		pr_err("error unbinding cooling dev\n");
> +		return -EINVAL;
> +	}
> +	return 0;
> +
> +}
> +
> +static int exynos4_get_temp(struct thermal_zone_device *thermal,
> +			       unsigned long *temp)
> +{
> +	void *data;
> +
> +	if (!th_zone->sensor_conf) {
> +		pr_info("Temperature sensor not initialised\n");
> +		return -EINVAL;
> +	}
> +	data = th_zone->sensor_conf->private_data;
> +	*temp = th_zone->sensor_conf->read_temperature(data);
> +	/*convert the temperature into millicelsius*/
> +	*temp = *temp * 1000;
> +	return 0;
> +}
> +
> +/* bind callback functions to thermalzone */
> +static struct thermal_zone_device_ops exynos4_dev_ops = {
> +	.bind = exynos4_bind,
> +	.unbind = exynos4_unbind,
> +	.get_temp = exynos4_get_temp,
> +	.get_mode = exynos4_get_mode,
> +	.set_mode = exynos4_set_mode,
> +	.get_trip_type = exynos4_get_trip_type,
> +	.get_trip_temp = exynos4_get_trip_temp,
> +	.get_crit_temp = exynos4_get_crit_temp,
> +};
> +
> +int exynos4_register_thermal(struct thermal_sensor_conf *sensor_conf)
> +{
> +	int ret, count, tab_size;
> +	struct freq_pctg_table *tab_ptr;
> +
> +	if (!sensor_conf || !sensor_conf->read_temperature) {
> +		pr_err("Temperature sensor not initialised\n");
> +		return -EINVAL;
> +	}
> +
> +	th_zone = kzalloc(sizeof(struct exynos4_thermal_zone), GFP_KERNEL);
> +	if (!th_zone) {
> +		ret = -ENOMEM;
> +		goto err_unregister;
> +	}
> +
> +	th_zone->sensor_conf = sensor_conf;
> +
> +	tab_ptr = (struct freq_pctg_table *)sensor_conf->cooling_data.freq_data;
> +	tab_size = sensor_conf->cooling_data.freq_pctg_count;
> +
> +	/*Register the cpufreq cooling device*/
> +	th_zone->cool_dev_size = 1;
> +	count = 0;
> +	th_zone->cool_dev[count] = cpufreq_cooling_register(
> +			(struct freq_pctg_table *)&(tab_ptr[count]),
> +			tab_size, cpumask_of(0));
> +
> +	if (IS_ERR(th_zone->cool_dev[count])) {
> +		pr_err("Failed to register cpufreq cooling device\n");
> +		ret = -EINVAL;
> +		th_zone->cool_dev_size = 0;
> +		goto err_unregister;
> +	}
> +
> +	th_zone->therm_dev = thermal_zone_device_register(sensor_conf->name,
> +				3, NULL, &exynos4_dev_ops, 0, 0, 0, 1000);
> +	if (IS_ERR(th_zone->therm_dev)) {
> +		pr_err("Failed to register thermal zone device\n");
> +		ret = -EINVAL;
> +		goto err_unregister;
> +	}
> +
> +	th_zone->active_interval = 1;
> +	th_zone->idle_interval = 10;
> +
> +	exynos4_set_mode(th_zone->therm_dev, THERMAL_DEVICE_DISABLED);
> +
> +	pr_info("Exynos: Kernel Thermal management registered\n");
> +
> +	return 0;
> +
> +err_unregister:
> +	exynos4_unregister_thermal();
> +	return ret;
> +}
> +EXPORT_SYMBOL(exynos4_register_thermal);
> +
> +void exynos4_unregister_thermal(void)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < th_zone->cool_dev_size; i++) {
> +		if (th_zone && th_zone->cool_dev[i])
> +			cpufreq_cooling_unregister(th_zone->cool_dev[i]);
> +	}
> +
> +	if (th_zone && th_zone->therm_dev)
> +		thermal_zone_device_unregister(th_zone->therm_dev);
> +
> +	kfree(th_zone);
> +
> +	pr_info("Exynos: Kernel Thermal management unregistered\n");
> +}
> +EXPORT_SYMBOL(exynos4_unregister_thermal);
> diff --git a/include/linux/exynos_thermal.h b/include/linux/exynos_thermal.h
> new file mode 100644
> index 0000000..186e409
> --- /dev/null
> +++ b/include/linux/exynos_thermal.h
> @@ -0,0 +1,72 @@
> +/* linux/include/linux/exynos_thermal.h
> + *
> + * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
> + *		http://www.samsung.com
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> +*/
> +
> +#ifndef THERMAL_INTERFACE_H
> +#define THERMAL_INTERFACE_H
> +/* CPU Zone information */
> +
> +#define SENSOR_NAME_LEN	16
> +#define MAX_TRIP_COUNT	8
> +
> +#define PANIC_ZONE      4
> +#define WARN_ZONE       3
> +#define MONITOR_ZONE    2
> +#define SAFE_ZONE       1
> +#define NO_ACTION       0

I don't get why we need two separate SAFE and NO_ACTION
zones..To me, both should be the same.

> +
> +
> +struct	thermal_trip_point_conf {
> +	int trip_val[MAX_TRIP_COUNT];
> +	int trip_count;
> +};
> +
> +struct	thermal_cooling_conf {
> +	struct freq_pctg_table freq_data[MAX_TRIP_COUNT];
> +	int freq_pctg_count;
> +};
> +
> +/**
> + * struct exynos4_tmu_platform_data
> + * @name: name of the temperature sensor
> + * @read_temperature: A function pointer to read temperature info
> + * @private_data: Temperature sensor private data
> + * @sensor_data: Sensor specific information like trigger temperature, level
> + */
> +struct thermal_sensor_conf {
> +	char	name[SENSOR_NAME_LEN];
> +	int	(*read_temperature)(void *data);
> +	struct	thermal_trip_point_conf trip_data;
> +	struct	thermal_cooling_conf cooling_data;

Since both trip_count and freq_pctg_count are same at all
times (please correct me if I am wrong) I think it's better
to have a single 'count' variable inside this structure and move
trip_val and freq_data to this structure directly.

One General Concern:
Why do we even need this exynos_thermal layer between Thermal Framework
and the Thermal Sensor Drivers ? I think the thermal sensor driver (in
this case exynos_tmu.c) can directly register with the Thermal framework.
This means, we will soon have platformXXX_thermal.c files for each
platform, which is not really a good way to go IMHO.
I also understand that the framework does not have alarm attributes,
notification support etc..But We can add them if needed.

I have reviewed your two sets of patches independently. My only
request to you would be to post the next versions of both the patch
sets at the same time, so that it becomes easier to understand and test.

Thanks,
Durga

> +	void	*private_data;
> +};
> +
> +/**
> + * exynos4_register_thermal: Register to the exynos thermal interface.
> + * @sensor_conf:   Structure containing temperature sensor information
> + *
> + * returns zero on success, else negative errno.
> + */
> +int exynos4_register_thermal(struct thermal_sensor_conf *sensor_conf);
> +
> +/**
> + * exynos4_unregister_thermal: Un-register from the exynos thermal interface.
> + *
> + * return not applicable.
> + */
> +void exynos4_unregister_thermal(void);
> +
> +/**
> + * exynos4_report_trigger: Report any trigger level crossed in the
> + *	temperature sensor. This may be useful to take any cooling action.
> + *
> + * return not applicable.
> + */
> +extern void exynos4_report_trigger(void);
> +#endif
> --
> 1.7.1


_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 1/4] thermal: exynos: Add thermal interface support for linux thermal layer
  2012-03-12 10:51     ` R, Durgadoss
  (?)
@ 2012-03-13  4:22       ` Amit Kachhap
  -1 siblings, 0 replies; 43+ messages in thread
From: Amit Kachhap @ 2012-03-13  4:22 UTC (permalink / raw)
  To: R, Durgadoss
  Cc: linux-pm, linux-samsung-soc, linux-kernel, mjg59, linux-acpi,
	lenb, linaro-dev, lm-sensors, eduardo.valentin, patches

Hi Durgadoss,

Thanks for the detailed review.

On 12 March 2012 16:21, R, Durgadoss <durgadoss.r@intel.com> wrote:
> Hi Amit,
>
> Thanks for keeping this up. And Sorry for late reply.
>
>> -----Original Message-----
>> From: amit kachhap [mailto:amitdanielk@gmail.com] On Behalf Of Amit Daniel
>> Kachhap
>> Sent: Saturday, March 03, 2012 4:36 PM
>> To: linux-pm@lists.linux-foundation.org; linux-samsung-soc@vger.kernel.org
>> Cc: linux-kernel@vger.kernel.org; mjg59@srcf.ucam.org; linux-
>> acpi@vger.kernel.org; lenb@kernel.org; linaro-dev@lists.linaro.org; lm-
>> sensors@lm-sensors.org; amit.kachhap@linaro.org; eduardo.valentin@ti.com; R,
>> Durgadoss; patches@linaro.org
>> Subject: [PATCH 1/4] thermal: exynos: Add thermal interface support for linux
>> thermal layer
>>
>> This codes uses the generic linux thermal layer and creates a bridge
>> between temperature sensors, linux thermal framework and cooling devices
>> for samsung exynos platform. This layer recieves or monitor the
>> temperature from the sensor and informs the generic thermal layer to take
>> the necessary cooling action.
>>
>> Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>
>> ---
>>  drivers/thermal/Kconfig          |    8 +
>>  drivers/thermal/Makefile         |    1 +
>>  drivers/thermal/exynos_thermal.c |  272 ++++++++++++++++++++++++++++++++++++++
>>  include/linux/exynos_thermal.h   |   72 ++++++++++
>>  4 files changed, 353 insertions(+), 0 deletions(-)
>>  create mode 100644 drivers/thermal/exynos_thermal.c
>>  create mode 100644 include/linux/exynos_thermal.h
>>
>> diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
>> index 298c1cd..4e8df56 100644
>> --- a/drivers/thermal/Kconfig
>> +++ b/drivers/thermal/Kconfig
>> @@ -29,3 +29,11 @@ config CPU_THERMAL
>>         This will be useful for platforms using the generic thermal interface
>>         and not the ACPI interface.
>>         If you want this support, you should say Y or M here.
>> +
>> +config SAMSUNG_THERMAL_INTERFACE
>> +     bool "Samsung Thermal interface support"
>> +     depends on THERMAL && CPU_THERMAL
>> +     help
>> +       This is a samsung thermal interface which will be used as
>> +       a link between sensors and cooling devices with linux thermal
>> +       framework.
>> diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
>> index 655cbc4..c67b6b2 100644
>> --- a/drivers/thermal/Makefile
>> +++ b/drivers/thermal/Makefile
>> @@ -4,3 +4,4 @@
>>
>>  obj-$(CONFIG_THERMAL)                += thermal_sys.o
>>  obj-$(CONFIG_CPU_THERMAL)    += cpu_cooling.o
>> +obj-$(CONFIG_SAMSUNG_THERMAL_INTERFACE)      += exynos_thermal.o
>> diff --git a/drivers/thermal/exynos_thermal.c
>> b/drivers/thermal/exynos_thermal.c
>> new file mode 100644
>> index 0000000..878d45c
>> --- /dev/null
>> +++ b/drivers/thermal/exynos_thermal.c
>> @@ -0,0 +1,272 @@
>> +/* linux/drivers/thermal/exynos_thermal.c
>> + *
>> + * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
>> + *           http://www.samsung.com
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> +*/
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/thermal.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/cpufreq.h>
>> +#include <linux/err.h>
>> +#include <linux/slab.h>
>> +#include <linux/cpu_cooling.h>
>> +#include <linux/exynos_thermal.h>
>> +
>> +#define MAX_COOLING_DEVICE 4
>> +struct exynos4_thermal_zone {
>> +     unsigned int idle_interval;
>> +     unsigned int active_interval;
>> +     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;
>> +};
>> +
>> +static struct exynos4_thermal_zone *th_zone;
>> +
>> +static int exynos4_get_mode(struct thermal_zone_device *thermal,
>> +                         enum thermal_device_mode *mode)
>> +{
>> +     if (th_zone->sensor_conf) {
>> +             pr_info("Temperature sensor not initialised\n");
>> +             *mode = THERMAL_DEVICE_DISABLED;
>> +     } else
>> +             *mode = THERMAL_DEVICE_ENABLED;
>> +     return 0;
>> +}
>> +
>> +static int exynos4_set_mode(struct thermal_zone_device *thermal,
>> +                         enum thermal_device_mode mode)
>> +{
>> +     if (!th_zone->therm_dev) {
>> +             pr_notice("thermal zone not registered\n");
>> +             return 0;
>> +     }
>> +     if (mode == THERMAL_DEVICE_ENABLED)
>> +             th_zone->therm_dev->polling_delay =
>> +                             th_zone->active_interval*1000;
>> +     else
>> +             th_zone->therm_dev->polling_delay =
>> +                             th_zone->idle_interval*1000;
>
> If it is 'DISABLED' mode, shouldn't the polling delay be just 0 ?
Yes Ideally this should be zero. But I wanted thermal monitoring to
always happen with some long interval in case of error scenarios.
Anyway I will check this again.
>
>> +
>> +     thermal_zone_device_update(th_zone->therm_dev);
>> +     pr_info("thermal polling set for duration=%d sec\n",
>> +                             th_zone->therm_dev->polling_delay/1000);
>> +     return 0;
>> +}
>> +
>> +/*This may be called from interrupt based temperature sensor*/
>> +void exynos4_report_trigger(void)
>> +{
>> +     unsigned int monitor_temp;
>> +
>> +     if (!th_zone || !th_zone->therm_dev)
>> +             return;
>> +
>> +     monitor_temp = th_zone->sensor_conf->trip_data.trip_val[0];
>
> Why are we checking only against the 0-th trip point ?
> Why not for other trip_val[i] also ?
Yes correct. Actually the trip temperatures are arranged in ascending order.
>
>> +
>> +     thermal_zone_device_update(th_zone->therm_dev);
>> +
>> +     mutex_lock(&th_zone->therm_dev->lock);
>> +     if (th_zone->therm_dev->last_temperature > monitor_temp)
>> +             th_zone->therm_dev->polling_delay =
>> +                                     th_zone->active_interval*1000;
>> +     else
>> +             th_zone->therm_dev->polling_delay =
>> +                                     th_zone->idle_interval*1000;
>> +
>> +     kobject_uevent(&th_zone->therm_dev->device.kobj, KOBJ_CHANGE);
>
> Wouldn't it make more sense to pass the trip point id also as an 'env'
> parameter ? This way, the user space can easily figure out which trip
> point has been crossed.
Its a good suggestion. I will check if some uevent property allows that.
>
>> +     mutex_unlock(&th_zone->therm_dev->lock);
>> +}
>> +
>> +static int exynos4_get_trip_type(struct thermal_zone_device *thermal, int
>> trip,
>> +                              enum thermal_trip_type *type)
>> +{
>> +     if (trip == 0 || trip == 1)
>> +             *type = THERMAL_TRIP_STATE_ACTIVE;
>> +     else if (trip == 2)
>> +             *type = THERMAL_TRIP_CRITICAL;
>> +     else
>> +             return -EINVAL;
>> +
>> +     return 0;
>> +}
>> +
>> +static int exynos4_get_trip_temp(struct thermal_zone_device *thermal, int
>> trip,
>> +                              unsigned long *temp)
>> +{
>> +     /*Monitor zone*/
>> +     if (trip == 0)
>> +             *temp = th_zone->sensor_conf->trip_data.trip_val[0];
>> +     /*Warn zone*/
>> +     else if (trip == 1)
>> +             *temp = th_zone->sensor_conf->trip_data.trip_val[1];
>> +     /*Panic zone*/
>> +     else if (trip == 2)
>> +             *temp = th_zone->sensor_conf->trip_data.trip_val[2];
>> +     else
>> +             return -EINVAL;
>> +     /*convert the temperature into millicelsius*/
>> +     *temp = *temp * 1000;
>> +
>> +     return 0;
>> +}
>
> This could be:
>
> if (trip < 0 || trip >2) return -EINVAL;
> *temp = th_zone->sensor_conf->trip_data.trip_val[trip];
> return 0;
Agreed. Will apply.
>
>> +
>> +static int exynos4_get_crit_temp(struct thermal_zone_device *thermal,
>> +                              unsigned long *temp)
>> +{
>> +     /*Panic zone*/
>> +     *temp = th_zone->sensor_conf->trip_data.trip_val[2];
>> +     /*convert the temperature into millicelsius*/
>> +     *temp = *temp * 1000;
>> +     return 0;
>> +}
>
> Why not make it, exynos4_get_trip_temp(thermal, 2, temp) ?
OK.
>
>> +
>> +static int exynos4_bind(struct thermal_zone_device *thermal,
>> +                     struct thermal_cooling_device *cdev)
>> +{
>> +     /* if the cooling device is the one from exynos4 bind it */
>> +     if (cdev != th_zone->cool_dev[0])
>> +             return 0;
>> +
>> +     if (thermal_zone_bind_cooling_device(thermal, 0, cdev)) {
>> +             pr_err("error binding cooling dev\n");
>> +             return -EINVAL;
>> +     }
>> +     if (thermal_zone_bind_cooling_device(thermal, 1, cdev)) {
>> +             pr_err("error binding cooling dev\n");
>> +             return -EINVAL;
>
> If we fail here, do you want to remove the earlier 'binding' also ?
Yes. Missed this error handling.
>
>> +     }
>> +
>> +     return 0;
>> +
>> +}
>> +
>> +static int exynos4_unbind(struct thermal_zone_device *thermal,
>> +                       struct thermal_cooling_device *cdev)
>> +{
>> +     if (cdev != th_zone->cool_dev[0])
>> +             return 0;
>> +
>> +     if (thermal_zone_unbind_cooling_device(thermal, 0, cdev)) {
>> +             pr_err("error unbinding cooling dev\n");
>> +             return -EINVAL;
>
> I think we should still go ahead and try to 'unbind' the other one.
Yes. Missed this error handling.
>
>> +     }
>> +     if (thermal_zone_unbind_cooling_device(thermal, 1, cdev)) {
>> +             pr_err("error unbinding cooling dev\n");
>> +             return -EINVAL;
>> +     }
>> +     return 0;
>> +
>> +}
>> +
>> +static int exynos4_get_temp(struct thermal_zone_device *thermal,
>> +                            unsigned long *temp)
>> +{
>> +     void *data;
>> +
>> +     if (!th_zone->sensor_conf) {
>> +             pr_info("Temperature sensor not initialised\n");
>> +             return -EINVAL;
>> +     }
>> +     data = th_zone->sensor_conf->private_data;
>> +     *temp = th_zone->sensor_conf->read_temperature(data);
>> +     /*convert the temperature into millicelsius*/
>> +     *temp = *temp * 1000;
>> +     return 0;
>> +}
>> +
>> +/* bind callback functions to thermalzone */
>> +static struct thermal_zone_device_ops exynos4_dev_ops = {
>> +     .bind = exynos4_bind,
>> +     .unbind = exynos4_unbind,
>> +     .get_temp = exynos4_get_temp,
>> +     .get_mode = exynos4_get_mode,
>> +     .set_mode = exynos4_set_mode,
>> +     .get_trip_type = exynos4_get_trip_type,
>> +     .get_trip_temp = exynos4_get_trip_temp,
>> +     .get_crit_temp = exynos4_get_crit_temp,
>> +};
>> +
>> +int exynos4_register_thermal(struct thermal_sensor_conf *sensor_conf)
>> +{
>> +     int ret, count, tab_size;
>> +     struct freq_pctg_table *tab_ptr;
>> +
>> +     if (!sensor_conf || !sensor_conf->read_temperature) {
>> +             pr_err("Temperature sensor not initialised\n");
>> +             return -EINVAL;
>> +     }
>> +
>> +     th_zone = kzalloc(sizeof(struct exynos4_thermal_zone), GFP_KERNEL);
>> +     if (!th_zone) {
>> +             ret = -ENOMEM;
>> +             goto err_unregister;
>> +     }
>> +
>> +     th_zone->sensor_conf = sensor_conf;
>> +
>> +     tab_ptr = (struct freq_pctg_table *)sensor_conf->cooling_data.freq_data;
>> +     tab_size = sensor_conf->cooling_data.freq_pctg_count;
>> +
>> +     /*Register the cpufreq cooling device*/
>> +     th_zone->cool_dev_size = 1;
>> +     count = 0;
>> +     th_zone->cool_dev[count] = cpufreq_cooling_register(
>> +                     (struct freq_pctg_table *)&(tab_ptr[count]),
>> +                     tab_size, cpumask_of(0));
>> +
>> +     if (IS_ERR(th_zone->cool_dev[count])) {
>> +             pr_err("Failed to register cpufreq cooling device\n");
>> +             ret = -EINVAL;
>> +             th_zone->cool_dev_size = 0;
>> +             goto err_unregister;
>> +     }
>> +
>> +     th_zone->therm_dev = thermal_zone_device_register(sensor_conf->name,
>> +                             3, NULL, &exynos4_dev_ops, 0, 0, 0, 1000);
>> +     if (IS_ERR(th_zone->therm_dev)) {
>> +             pr_err("Failed to register thermal zone device\n");
>> +             ret = -EINVAL;
>> +             goto err_unregister;
>> +     }
>> +
>> +     th_zone->active_interval = 1;
>> +     th_zone->idle_interval = 10;
>> +
>> +     exynos4_set_mode(th_zone->therm_dev, THERMAL_DEVICE_DISABLED);
>> +
>> +     pr_info("Exynos: Kernel Thermal management registered\n");
>> +
>> +     return 0;
>> +
>> +err_unregister:
>> +     exynos4_unregister_thermal();
>> +     return ret;
>> +}
>> +EXPORT_SYMBOL(exynos4_register_thermal);
>> +
>> +void exynos4_unregister_thermal(void)
>> +{
>> +     unsigned int i;
>> +
>> +     for (i = 0; i < th_zone->cool_dev_size; i++) {
>> +             if (th_zone && th_zone->cool_dev[i])
>> +                     cpufreq_cooling_unregister(th_zone->cool_dev[i]);
>> +     }
>> +
>> +     if (th_zone && th_zone->therm_dev)
>> +             thermal_zone_device_unregister(th_zone->therm_dev);
>> +
>> +     kfree(th_zone);
>> +
>> +     pr_info("Exynos: Kernel Thermal management unregistered\n");
>> +}
>> +EXPORT_SYMBOL(exynos4_unregister_thermal);
>> diff --git a/include/linux/exynos_thermal.h b/include/linux/exynos_thermal.h
>> new file mode 100644
>> index 0000000..186e409
>> --- /dev/null
>> +++ b/include/linux/exynos_thermal.h
>> @@ -0,0 +1,72 @@
>> +/* linux/include/linux/exynos_thermal.h
>> + *
>> + * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
>> + *           http://www.samsung.com
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> +*/
>> +
>> +#ifndef THERMAL_INTERFACE_H
>> +#define THERMAL_INTERFACE_H
>> +/* CPU Zone information */
>> +
>> +#define SENSOR_NAME_LEN      16
>> +#define MAX_TRIP_COUNT       8
>> +
>> +#define PANIC_ZONE      4
>> +#define WARN_ZONE       3
>> +#define MONITOR_ZONE    2
>> +#define SAFE_ZONE       1
>> +#define NO_ACTION       0
>
> I don't get why we need two separate SAFE and NO_ACTION
> zones..To me, both should be the same.
Yes not needed.
>
>> +
>> +
>> +struct       thermal_trip_point_conf {
>> +     int trip_val[MAX_TRIP_COUNT];
>> +     int trip_count;
>> +};
>> +
>> +struct       thermal_cooling_conf {
>> +     struct freq_pctg_table freq_data[MAX_TRIP_COUNT];
>> +     int freq_pctg_count;
>> +};
>> +
>> +/**
>> + * struct exynos4_tmu_platform_data
>> + * @name: name of the temperature sensor
>> + * @read_temperature: A function pointer to read temperature info
>> + * @private_data: Temperature sensor private data
>> + * @sensor_data: Sensor specific information like trigger temperature, level
>> + */
>> +struct thermal_sensor_conf {
>> +     char    name[SENSOR_NAME_LEN];
>> +     int     (*read_temperature)(void *data);
>> +     struct  thermal_trip_point_conf trip_data;
>> +     struct  thermal_cooling_conf cooling_data;
>
> Since both trip_count and freq_pctg_count are same at all
> times (please correct me if I am wrong) I think it's better
> to have a single 'count' variable inside this structure and move
> trip_val and freq_data to this structure directly.
No trip_count and pctg_count are different.
>
> One General Concern:
> Why do we even need this exynos_thermal layer between Thermal Framework
> and the Thermal Sensor Drivers ? I think the thermal sensor driver (in
> this case exynos_tmu.c) can directly register with the Thermal framework.
> This means, we will soon have platformXXX_thermal.c files for each
> platform, which is not really a good way to go IMHO.
> I also understand that the framework does not have alarm attributes,
> notification support etc..But We can add them if needed.
Yes even comments from Guenter Roeck and Mark Brown also point in the
same direction. Actually I wanted to separate the sensor h/w
implementation independent from thermal management algorithm so its
future modification may be clean. But looks like the agreement is to
merge them.
>
> I have reviewed your two sets of patches independently. My only
> request to you would be to post the next versions of both the patch
> sets at the same time, so that it becomes easier to understand and test.
Thanks even i also think this a good way.
>
> Thanks,
> Durga
>
>> +     void    *private_data;
>> +};
>> +
>> +/**
>> + * exynos4_register_thermal: Register to the exynos thermal interface.
>> + * @sensor_conf:   Structure containing temperature sensor information
>> + *
>> + * returns zero on success, else negative errno.
>> + */
>> +int exynos4_register_thermal(struct thermal_sensor_conf *sensor_conf);
>> +
>> +/**
>> + * exynos4_unregister_thermal: Un-register from the exynos thermal interface.
>> + *
>> + * return not applicable.
>> + */
>> +void exynos4_unregister_thermal(void);
>> +
>> +/**
>> + * exynos4_report_trigger: Report any trigger level crossed in the
>> + *   temperature sensor. This may be useful to take any cooling action.
>> + *
>> + * return not applicable.
>> + */
>> +extern void exynos4_report_trigger(void);
>> +#endif
>> --
>> 1.7.1
>
--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 1/4] thermal: exynos: Add thermal interface support for linux thermal layer
@ 2012-03-13  4:22       ` Amit Kachhap
  0 siblings, 0 replies; 43+ messages in thread
From: Amit Kachhap @ 2012-03-13  4:22 UTC (permalink / raw)
  To: R, Durgadoss
  Cc: linux-pm, linux-samsung-soc, linux-kernel, mjg59, linux-acpi,
	lenb, linaro-dev, lm-sensors, eduardo.valentin, patches

Hi Durgadoss,

Thanks for the detailed review.

On 12 March 2012 16:21, R, Durgadoss <durgadoss.r@intel.com> wrote:
> Hi Amit,
>
> Thanks for keeping this up. And Sorry for late reply.
>
>> -----Original Message-----
>> From: amit kachhap [mailto:amitdanielk@gmail.com] On Behalf Of Amit Daniel
>> Kachhap
>> Sent: Saturday, March 03, 2012 4:36 PM
>> To: linux-pm@lists.linux-foundation.org; linux-samsung-soc@vger.kernel.org
>> Cc: linux-kernel@vger.kernel.org; mjg59@srcf.ucam.org; linux-
>> acpi@vger.kernel.org; lenb@kernel.org; linaro-dev@lists.linaro.org; lm-
>> sensors@lm-sensors.org; amit.kachhap@linaro.org; eduardo.valentin@ti.com; R,
>> Durgadoss; patches@linaro.org
>> Subject: [PATCH 1/4] thermal: exynos: Add thermal interface support for linux
>> thermal layer
>>
>> This codes uses the generic linux thermal layer and creates a bridge
>> between temperature sensors, linux thermal framework and cooling devices
>> for samsung exynos platform. This layer recieves or monitor the
>> temperature from the sensor and informs the generic thermal layer to take
>> the necessary cooling action.
>>
>> Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>
>> ---
>>  drivers/thermal/Kconfig          |    8 +
>>  drivers/thermal/Makefile         |    1 +
>>  drivers/thermal/exynos_thermal.c |  272 ++++++++++++++++++++++++++++++++++++++
>>  include/linux/exynos_thermal.h   |   72 ++++++++++
>>  4 files changed, 353 insertions(+), 0 deletions(-)
>>  create mode 100644 drivers/thermal/exynos_thermal.c
>>  create mode 100644 include/linux/exynos_thermal.h
>>
>> diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
>> index 298c1cd..4e8df56 100644
>> --- a/drivers/thermal/Kconfig
>> +++ b/drivers/thermal/Kconfig
>> @@ -29,3 +29,11 @@ config CPU_THERMAL
>>         This will be useful for platforms using the generic thermal interface
>>         and not the ACPI interface.
>>         If you want this support, you should say Y or M here.
>> +
>> +config SAMSUNG_THERMAL_INTERFACE
>> +     bool "Samsung Thermal interface support"
>> +     depends on THERMAL && CPU_THERMAL
>> +     help
>> +       This is a samsung thermal interface which will be used as
>> +       a link between sensors and cooling devices with linux thermal
>> +       framework.
>> diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
>> index 655cbc4..c67b6b2 100644
>> --- a/drivers/thermal/Makefile
>> +++ b/drivers/thermal/Makefile
>> @@ -4,3 +4,4 @@
>>
>>  obj-$(CONFIG_THERMAL)                += thermal_sys.o
>>  obj-$(CONFIG_CPU_THERMAL)    += cpu_cooling.o
>> +obj-$(CONFIG_SAMSUNG_THERMAL_INTERFACE)      += exynos_thermal.o
>> diff --git a/drivers/thermal/exynos_thermal.c
>> b/drivers/thermal/exynos_thermal.c
>> new file mode 100644
>> index 0000000..878d45c
>> --- /dev/null
>> +++ b/drivers/thermal/exynos_thermal.c
>> @@ -0,0 +1,272 @@
>> +/* linux/drivers/thermal/exynos_thermal.c
>> + *
>> + * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
>> + *           http://www.samsung.com
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> +*/
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/thermal.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/cpufreq.h>
>> +#include <linux/err.h>
>> +#include <linux/slab.h>
>> +#include <linux/cpu_cooling.h>
>> +#include <linux/exynos_thermal.h>
>> +
>> +#define MAX_COOLING_DEVICE 4
>> +struct exynos4_thermal_zone {
>> +     unsigned int idle_interval;
>> +     unsigned int active_interval;
>> +     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;
>> +};
>> +
>> +static struct exynos4_thermal_zone *th_zone;
>> +
>> +static int exynos4_get_mode(struct thermal_zone_device *thermal,
>> +                         enum thermal_device_mode *mode)
>> +{
>> +     if (th_zone->sensor_conf) {
>> +             pr_info("Temperature sensor not initialised\n");
>> +             *mode = THERMAL_DEVICE_DISABLED;
>> +     } else
>> +             *mode = THERMAL_DEVICE_ENABLED;
>> +     return 0;
>> +}
>> +
>> +static int exynos4_set_mode(struct thermal_zone_device *thermal,
>> +                         enum thermal_device_mode mode)
>> +{
>> +     if (!th_zone->therm_dev) {
>> +             pr_notice("thermal zone not registered\n");
>> +             return 0;
>> +     }
>> +     if (mode == THERMAL_DEVICE_ENABLED)
>> +             th_zone->therm_dev->polling_delay =
>> +                             th_zone->active_interval*1000;
>> +     else
>> +             th_zone->therm_dev->polling_delay =
>> +                             th_zone->idle_interval*1000;
>
> If it is 'DISABLED' mode, shouldn't the polling delay be just 0 ?
Yes Ideally this should be zero. But I wanted thermal monitoring to
always happen with some long interval in case of error scenarios.
Anyway I will check this again.
>
>> +
>> +     thermal_zone_device_update(th_zone->therm_dev);
>> +     pr_info("thermal polling set for duration=%d sec\n",
>> +                             th_zone->therm_dev->polling_delay/1000);
>> +     return 0;
>> +}
>> +
>> +/*This may be called from interrupt based temperature sensor*/
>> +void exynos4_report_trigger(void)
>> +{
>> +     unsigned int monitor_temp;
>> +
>> +     if (!th_zone || !th_zone->therm_dev)
>> +             return;
>> +
>> +     monitor_temp = th_zone->sensor_conf->trip_data.trip_val[0];
>
> Why are we checking only against the 0-th trip point ?
> Why not for other trip_val[i] also ?
Yes correct. Actually the trip temperatures are arranged in ascending order.
>
>> +
>> +     thermal_zone_device_update(th_zone->therm_dev);
>> +
>> +     mutex_lock(&th_zone->therm_dev->lock);
>> +     if (th_zone->therm_dev->last_temperature > monitor_temp)
>> +             th_zone->therm_dev->polling_delay =
>> +                                     th_zone->active_interval*1000;
>> +     else
>> +             th_zone->therm_dev->polling_delay =
>> +                                     th_zone->idle_interval*1000;
>> +
>> +     kobject_uevent(&th_zone->therm_dev->device.kobj, KOBJ_CHANGE);
>
> Wouldn't it make more sense to pass the trip point id also as an 'env'
> parameter ? This way, the user space can easily figure out which trip
> point has been crossed.
Its a good suggestion. I will check if some uevent property allows that.
>
>> +     mutex_unlock(&th_zone->therm_dev->lock);
>> +}
>> +
>> +static int exynos4_get_trip_type(struct thermal_zone_device *thermal, int
>> trip,
>> +                              enum thermal_trip_type *type)
>> +{
>> +     if (trip == 0 || trip == 1)
>> +             *type = THERMAL_TRIP_STATE_ACTIVE;
>> +     else if (trip == 2)
>> +             *type = THERMAL_TRIP_CRITICAL;
>> +     else
>> +             return -EINVAL;
>> +
>> +     return 0;
>> +}
>> +
>> +static int exynos4_get_trip_temp(struct thermal_zone_device *thermal, int
>> trip,
>> +                              unsigned long *temp)
>> +{
>> +     /*Monitor zone*/
>> +     if (trip == 0)
>> +             *temp = th_zone->sensor_conf->trip_data.trip_val[0];
>> +     /*Warn zone*/
>> +     else if (trip == 1)
>> +             *temp = th_zone->sensor_conf->trip_data.trip_val[1];
>> +     /*Panic zone*/
>> +     else if (trip == 2)
>> +             *temp = th_zone->sensor_conf->trip_data.trip_val[2];
>> +     else
>> +             return -EINVAL;
>> +     /*convert the temperature into millicelsius*/
>> +     *temp = *temp * 1000;
>> +
>> +     return 0;
>> +}
>
> This could be:
>
> if (trip < 0 || trip >2) return -EINVAL;
> *temp = th_zone->sensor_conf->trip_data.trip_val[trip];
> return 0;
Agreed. Will apply.
>
>> +
>> +static int exynos4_get_crit_temp(struct thermal_zone_device *thermal,
>> +                              unsigned long *temp)
>> +{
>> +     /*Panic zone*/
>> +     *temp = th_zone->sensor_conf->trip_data.trip_val[2];
>> +     /*convert the temperature into millicelsius*/
>> +     *temp = *temp * 1000;
>> +     return 0;
>> +}
>
> Why not make it, exynos4_get_trip_temp(thermal, 2, temp) ?
OK.
>
>> +
>> +static int exynos4_bind(struct thermal_zone_device *thermal,
>> +                     struct thermal_cooling_device *cdev)
>> +{
>> +     /* if the cooling device is the one from exynos4 bind it */
>> +     if (cdev != th_zone->cool_dev[0])
>> +             return 0;
>> +
>> +     if (thermal_zone_bind_cooling_device(thermal, 0, cdev)) {
>> +             pr_err("error binding cooling dev\n");
>> +             return -EINVAL;
>> +     }
>> +     if (thermal_zone_bind_cooling_device(thermal, 1, cdev)) {
>> +             pr_err("error binding cooling dev\n");
>> +             return -EINVAL;
>
> If we fail here, do you want to remove the earlier 'binding' also ?
Yes. Missed this error handling.
>
>> +     }
>> +
>> +     return 0;
>> +
>> +}
>> +
>> +static int exynos4_unbind(struct thermal_zone_device *thermal,
>> +                       struct thermal_cooling_device *cdev)
>> +{
>> +     if (cdev != th_zone->cool_dev[0])
>> +             return 0;
>> +
>> +     if (thermal_zone_unbind_cooling_device(thermal, 0, cdev)) {
>> +             pr_err("error unbinding cooling dev\n");
>> +             return -EINVAL;
>
> I think we should still go ahead and try to 'unbind' the other one.
Yes. Missed this error handling.
>
>> +     }
>> +     if (thermal_zone_unbind_cooling_device(thermal, 1, cdev)) {
>> +             pr_err("error unbinding cooling dev\n");
>> +             return -EINVAL;
>> +     }
>> +     return 0;
>> +
>> +}
>> +
>> +static int exynos4_get_temp(struct thermal_zone_device *thermal,
>> +                            unsigned long *temp)
>> +{
>> +     void *data;
>> +
>> +     if (!th_zone->sensor_conf) {
>> +             pr_info("Temperature sensor not initialised\n");
>> +             return -EINVAL;
>> +     }
>> +     data = th_zone->sensor_conf->private_data;
>> +     *temp = th_zone->sensor_conf->read_temperature(data);
>> +     /*convert the temperature into millicelsius*/
>> +     *temp = *temp * 1000;
>> +     return 0;
>> +}
>> +
>> +/* bind callback functions to thermalzone */
>> +static struct thermal_zone_device_ops exynos4_dev_ops = {
>> +     .bind = exynos4_bind,
>> +     .unbind = exynos4_unbind,
>> +     .get_temp = exynos4_get_temp,
>> +     .get_mode = exynos4_get_mode,
>> +     .set_mode = exynos4_set_mode,
>> +     .get_trip_type = exynos4_get_trip_type,
>> +     .get_trip_temp = exynos4_get_trip_temp,
>> +     .get_crit_temp = exynos4_get_crit_temp,
>> +};
>> +
>> +int exynos4_register_thermal(struct thermal_sensor_conf *sensor_conf)
>> +{
>> +     int ret, count, tab_size;
>> +     struct freq_pctg_table *tab_ptr;
>> +
>> +     if (!sensor_conf || !sensor_conf->read_temperature) {
>> +             pr_err("Temperature sensor not initialised\n");
>> +             return -EINVAL;
>> +     }
>> +
>> +     th_zone = kzalloc(sizeof(struct exynos4_thermal_zone), GFP_KERNEL);
>> +     if (!th_zone) {
>> +             ret = -ENOMEM;
>> +             goto err_unregister;
>> +     }
>> +
>> +     th_zone->sensor_conf = sensor_conf;
>> +
>> +     tab_ptr = (struct freq_pctg_table *)sensor_conf->cooling_data.freq_data;
>> +     tab_size = sensor_conf->cooling_data.freq_pctg_count;
>> +
>> +     /*Register the cpufreq cooling device*/
>> +     th_zone->cool_dev_size = 1;
>> +     count = 0;
>> +     th_zone->cool_dev[count] = cpufreq_cooling_register(
>> +                     (struct freq_pctg_table *)&(tab_ptr[count]),
>> +                     tab_size, cpumask_of(0));
>> +
>> +     if (IS_ERR(th_zone->cool_dev[count])) {
>> +             pr_err("Failed to register cpufreq cooling device\n");
>> +             ret = -EINVAL;
>> +             th_zone->cool_dev_size = 0;
>> +             goto err_unregister;
>> +     }
>> +
>> +     th_zone->therm_dev = thermal_zone_device_register(sensor_conf->name,
>> +                             3, NULL, &exynos4_dev_ops, 0, 0, 0, 1000);
>> +     if (IS_ERR(th_zone->therm_dev)) {
>> +             pr_err("Failed to register thermal zone device\n");
>> +             ret = -EINVAL;
>> +             goto err_unregister;
>> +     }
>> +
>> +     th_zone->active_interval = 1;
>> +     th_zone->idle_interval = 10;
>> +
>> +     exynos4_set_mode(th_zone->therm_dev, THERMAL_DEVICE_DISABLED);
>> +
>> +     pr_info("Exynos: Kernel Thermal management registered\n");
>> +
>> +     return 0;
>> +
>> +err_unregister:
>> +     exynos4_unregister_thermal();
>> +     return ret;
>> +}
>> +EXPORT_SYMBOL(exynos4_register_thermal);
>> +
>> +void exynos4_unregister_thermal(void)
>> +{
>> +     unsigned int i;
>> +
>> +     for (i = 0; i < th_zone->cool_dev_size; i++) {
>> +             if (th_zone && th_zone->cool_dev[i])
>> +                     cpufreq_cooling_unregister(th_zone->cool_dev[i]);
>> +     }
>> +
>> +     if (th_zone && th_zone->therm_dev)
>> +             thermal_zone_device_unregister(th_zone->therm_dev);
>> +
>> +     kfree(th_zone);
>> +
>> +     pr_info("Exynos: Kernel Thermal management unregistered\n");
>> +}
>> +EXPORT_SYMBOL(exynos4_unregister_thermal);
>> diff --git a/include/linux/exynos_thermal.h b/include/linux/exynos_thermal.h
>> new file mode 100644
>> index 0000000..186e409
>> --- /dev/null
>> +++ b/include/linux/exynos_thermal.h
>> @@ -0,0 +1,72 @@
>> +/* linux/include/linux/exynos_thermal.h
>> + *
>> + * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
>> + *           http://www.samsung.com
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> +*/
>> +
>> +#ifndef THERMAL_INTERFACE_H
>> +#define THERMAL_INTERFACE_H
>> +/* CPU Zone information */
>> +
>> +#define SENSOR_NAME_LEN      16
>> +#define MAX_TRIP_COUNT       8
>> +
>> +#define PANIC_ZONE      4
>> +#define WARN_ZONE       3
>> +#define MONITOR_ZONE    2
>> +#define SAFE_ZONE       1
>> +#define NO_ACTION       0
>
> I don't get why we need two separate SAFE and NO_ACTION
> zones..To me, both should be the same.
Yes not needed.
>
>> +
>> +
>> +struct       thermal_trip_point_conf {
>> +     int trip_val[MAX_TRIP_COUNT];
>> +     int trip_count;
>> +};
>> +
>> +struct       thermal_cooling_conf {
>> +     struct freq_pctg_table freq_data[MAX_TRIP_COUNT];
>> +     int freq_pctg_count;
>> +};
>> +
>> +/**
>> + * struct exynos4_tmu_platform_data
>> + * @name: name of the temperature sensor
>> + * @read_temperature: A function pointer to read temperature info
>> + * @private_data: Temperature sensor private data
>> + * @sensor_data: Sensor specific information like trigger temperature, level
>> + */
>> +struct thermal_sensor_conf {
>> +     char    name[SENSOR_NAME_LEN];
>> +     int     (*read_temperature)(void *data);
>> +     struct  thermal_trip_point_conf trip_data;
>> +     struct  thermal_cooling_conf cooling_data;
>
> Since both trip_count and freq_pctg_count are same at all
> times (please correct me if I am wrong) I think it's better
> to have a single 'count' variable inside this structure and move
> trip_val and freq_data to this structure directly.
No trip_count and pctg_count are different.
>
> One General Concern:
> Why do we even need this exynos_thermal layer between Thermal Framework
> and the Thermal Sensor Drivers ? I think the thermal sensor driver (in
> this case exynos_tmu.c) can directly register with the Thermal framework.
> This means, we will soon have platformXXX_thermal.c files for each
> platform, which is not really a good way to go IMHO.
> I also understand that the framework does not have alarm attributes,
> notification support etc..But We can add them if needed.
Yes even comments from Guenter Roeck and Mark Brown also point in the
same direction. Actually I wanted to separate the sensor h/w
implementation independent from thermal management algorithm so its
future modification may be clean. But looks like the agreement is to
merge them.
>
> I have reviewed your two sets of patches independently. My only
> request to you would be to post the next versions of both the patch
> sets at the same time, so that it becomes easier to understand and test.
Thanks even i also think this a good way.
>
> Thanks,
> Durga
>
>> +     void    *private_data;
>> +};
>> +
>> +/**
>> + * exynos4_register_thermal: Register to the exynos thermal interface.
>> + * @sensor_conf:   Structure containing temperature sensor information
>> + *
>> + * returns zero on success, else negative errno.
>> + */
>> +int exynos4_register_thermal(struct thermal_sensor_conf *sensor_conf);
>> +
>> +/**
>> + * exynos4_unregister_thermal: Un-register from the exynos thermal interface.
>> + *
>> + * return not applicable.
>> + */
>> +void exynos4_unregister_thermal(void);
>> +
>> +/**
>> + * exynos4_report_trigger: Report any trigger level crossed in the
>> + *   temperature sensor. This may be useful to take any cooling action.
>> + *
>> + * return not applicable.
>> + */
>> +extern void exynos4_report_trigger(void);
>> +#endif
>> --
>> 1.7.1
>

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [lm-sensors] [PATCH 1/4] thermal: exynos: Add thermal interface support for linux thermal layer
@ 2012-03-13  4:22       ` Amit Kachhap
  0 siblings, 0 replies; 43+ messages in thread
From: Amit Kachhap @ 2012-03-13  4:34 UTC (permalink / raw)
  To: R, Durgadoss
  Cc: linux-pm, linux-samsung-soc, linux-kernel, mjg59, linux-acpi,
	lenb, linaro-dev, lm-sensors, eduardo.valentin, patches

Hi Durgadoss,

Thanks for the detailed review.

On 12 March 2012 16:21, R, Durgadoss <durgadoss.r@intel.com> wrote:
> Hi Amit,
>
> Thanks for keeping this up. And Sorry for late reply.
>
>> -----Original Message-----
>> From: amit kachhap [mailto:amitdanielk@gmail.com] On Behalf Of Amit Daniel
>> Kachhap
>> Sent: Saturday, March 03, 2012 4:36 PM
>> To: linux-pm@lists.linux-foundation.org; linux-samsung-soc@vger.kernel.org
>> Cc: linux-kernel@vger.kernel.org; mjg59@srcf.ucam.org; linux-
>> acpi@vger.kernel.org; lenb@kernel.org; linaro-dev@lists.linaro.org; lm-
>> sensors@lm-sensors.org; amit.kachhap@linaro.org; eduardo.valentin@ti.com; R,
>> Durgadoss; patches@linaro.org
>> Subject: [PATCH 1/4] thermal: exynos: Add thermal interface support for linux
>> thermal layer
>>
>> This codes uses the generic linux thermal layer and creates a bridge
>> between temperature sensors, linux thermal framework and cooling devices
>> for samsung exynos platform. This layer recieves or monitor the
>> temperature from the sensor and informs the generic thermal layer to take
>> the necessary cooling action.
>>
>> Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>
>> ---
>>  drivers/thermal/Kconfig          |    8 +
>>  drivers/thermal/Makefile         |    1 +
>>  drivers/thermal/exynos_thermal.c |  272 ++++++++++++++++++++++++++++++++++++++
>>  include/linux/exynos_thermal.h   |   72 ++++++++++
>>  4 files changed, 353 insertions(+), 0 deletions(-)
>>  create mode 100644 drivers/thermal/exynos_thermal.c
>>  create mode 100644 include/linux/exynos_thermal.h
>>
>> diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
>> index 298c1cd..4e8df56 100644
>> --- a/drivers/thermal/Kconfig
>> +++ b/drivers/thermal/Kconfig
>> @@ -29,3 +29,11 @@ config CPU_THERMAL
>>         This will be useful for platforms using the generic thermal interface
>>         and not the ACPI interface.
>>         If you want this support, you should say Y or M here.
>> +
>> +config SAMSUNG_THERMAL_INTERFACE
>> +     bool "Samsung Thermal interface support"
>> +     depends on THERMAL && CPU_THERMAL
>> +     help
>> +       This is a samsung thermal interface which will be used as
>> +       a link between sensors and cooling devices with linux thermal
>> +       framework.
>> diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
>> index 655cbc4..c67b6b2 100644
>> --- a/drivers/thermal/Makefile
>> +++ b/drivers/thermal/Makefile
>> @@ -4,3 +4,4 @@
>>
>>  obj-$(CONFIG_THERMAL)                += thermal_sys.o
>>  obj-$(CONFIG_CPU_THERMAL)    += cpu_cooling.o
>> +obj-$(CONFIG_SAMSUNG_THERMAL_INTERFACE)      += exynos_thermal.o
>> diff --git a/drivers/thermal/exynos_thermal.c
>> b/drivers/thermal/exynos_thermal.c
>> new file mode 100644
>> index 0000000..878d45c
>> --- /dev/null
>> +++ b/drivers/thermal/exynos_thermal.c
>> @@ -0,0 +1,272 @@
>> +/* linux/drivers/thermal/exynos_thermal.c
>> + *
>> + * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
>> + *           http://www.samsung.com
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> +*/
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/thermal.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/cpufreq.h>
>> +#include <linux/err.h>
>> +#include <linux/slab.h>
>> +#include <linux/cpu_cooling.h>
>> +#include <linux/exynos_thermal.h>
>> +
>> +#define MAX_COOLING_DEVICE 4
>> +struct exynos4_thermal_zone {
>> +     unsigned int idle_interval;
>> +     unsigned int active_interval;
>> +     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;
>> +};
>> +
>> +static struct exynos4_thermal_zone *th_zone;
>> +
>> +static int exynos4_get_mode(struct thermal_zone_device *thermal,
>> +                         enum thermal_device_mode *mode)
>> +{
>> +     if (th_zone->sensor_conf) {
>> +             pr_info("Temperature sensor not initialised\n");
>> +             *mode = THERMAL_DEVICE_DISABLED;
>> +     } else
>> +             *mode = THERMAL_DEVICE_ENABLED;
>> +     return 0;
>> +}
>> +
>> +static int exynos4_set_mode(struct thermal_zone_device *thermal,
>> +                         enum thermal_device_mode mode)
>> +{
>> +     if (!th_zone->therm_dev) {
>> +             pr_notice("thermal zone not registered\n");
>> +             return 0;
>> +     }
>> +     if (mode == THERMAL_DEVICE_ENABLED)
>> +             th_zone->therm_dev->polling_delay =
>> +                             th_zone->active_interval*1000;
>> +     else
>> +             th_zone->therm_dev->polling_delay =
>> +                             th_zone->idle_interval*1000;
>
> If it is 'DISABLED' mode, shouldn't the polling delay be just 0 ?
Yes Ideally this should be zero. But I wanted thermal monitoring to
always happen with some long interval in case of error scenarios.
Anyway I will check this again.
>
>> +
>> +     thermal_zone_device_update(th_zone->therm_dev);
>> +     pr_info("thermal polling set for duration=%d sec\n",
>> +                             th_zone->therm_dev->polling_delay/1000);
>> +     return 0;
>> +}
>> +
>> +/*This may be called from interrupt based temperature sensor*/
>> +void exynos4_report_trigger(void)
>> +{
>> +     unsigned int monitor_temp;
>> +
>> +     if (!th_zone || !th_zone->therm_dev)
>> +             return;
>> +
>> +     monitor_temp = th_zone->sensor_conf->trip_data.trip_val[0];
>
> Why are we checking only against the 0-th trip point ?
> Why not for other trip_val[i] also ?
Yes correct. Actually the trip temperatures are arranged in ascending order.
>
>> +
>> +     thermal_zone_device_update(th_zone->therm_dev);
>> +
>> +     mutex_lock(&th_zone->therm_dev->lock);
>> +     if (th_zone->therm_dev->last_temperature > monitor_temp)
>> +             th_zone->therm_dev->polling_delay =
>> +                                     th_zone->active_interval*1000;
>> +     else
>> +             th_zone->therm_dev->polling_delay =
>> +                                     th_zone->idle_interval*1000;
>> +
>> +     kobject_uevent(&th_zone->therm_dev->device.kobj, KOBJ_CHANGE);
>
> Wouldn't it make more sense to pass the trip point id also as an 'env'
> parameter ? This way, the user space can easily figure out which trip
> point has been crossed.
Its a good suggestion. I will check if some uevent property allows that.
>
>> +     mutex_unlock(&th_zone->therm_dev->lock);
>> +}
>> +
>> +static int exynos4_get_trip_type(struct thermal_zone_device *thermal, int
>> trip,
>> +                              enum thermal_trip_type *type)
>> +{
>> +     if (trip == 0 || trip == 1)
>> +             *type = THERMAL_TRIP_STATE_ACTIVE;
>> +     else if (trip == 2)
>> +             *type = THERMAL_TRIP_CRITICAL;
>> +     else
>> +             return -EINVAL;
>> +
>> +     return 0;
>> +}
>> +
>> +static int exynos4_get_trip_temp(struct thermal_zone_device *thermal, int
>> trip,
>> +                              unsigned long *temp)
>> +{
>> +     /*Monitor zone*/
>> +     if (trip == 0)
>> +             *temp = th_zone->sensor_conf->trip_data.trip_val[0];
>> +     /*Warn zone*/
>> +     else if (trip == 1)
>> +             *temp = th_zone->sensor_conf->trip_data.trip_val[1];
>> +     /*Panic zone*/
>> +     else if (trip == 2)
>> +             *temp = th_zone->sensor_conf->trip_data.trip_val[2];
>> +     else
>> +             return -EINVAL;
>> +     /*convert the temperature into millicelsius*/
>> +     *temp = *temp * 1000;
>> +
>> +     return 0;
>> +}
>
> This could be:
>
> if (trip < 0 || trip >2) return -EINVAL;
> *temp = th_zone->sensor_conf->trip_data.trip_val[trip];
> return 0;
Agreed. Will apply.
>
>> +
>> +static int exynos4_get_crit_temp(struct thermal_zone_device *thermal,
>> +                              unsigned long *temp)
>> +{
>> +     /*Panic zone*/
>> +     *temp = th_zone->sensor_conf->trip_data.trip_val[2];
>> +     /*convert the temperature into millicelsius*/
>> +     *temp = *temp * 1000;
>> +     return 0;
>> +}
>
> Why not make it, exynos4_get_trip_temp(thermal, 2, temp) ?
OK.
>
>> +
>> +static int exynos4_bind(struct thermal_zone_device *thermal,
>> +                     struct thermal_cooling_device *cdev)
>> +{
>> +     /* if the cooling device is the one from exynos4 bind it */
>> +     if (cdev != th_zone->cool_dev[0])
>> +             return 0;
>> +
>> +     if (thermal_zone_bind_cooling_device(thermal, 0, cdev)) {
>> +             pr_err("error binding cooling dev\n");
>> +             return -EINVAL;
>> +     }
>> +     if (thermal_zone_bind_cooling_device(thermal, 1, cdev)) {
>> +             pr_err("error binding cooling dev\n");
>> +             return -EINVAL;
>
> If we fail here, do you want to remove the earlier 'binding' also ?
Yes. Missed this error handling.
>
>> +     }
>> +
>> +     return 0;
>> +
>> +}
>> +
>> +static int exynos4_unbind(struct thermal_zone_device *thermal,
>> +                       struct thermal_cooling_device *cdev)
>> +{
>> +     if (cdev != th_zone->cool_dev[0])
>> +             return 0;
>> +
>> +     if (thermal_zone_unbind_cooling_device(thermal, 0, cdev)) {
>> +             pr_err("error unbinding cooling dev\n");
>> +             return -EINVAL;
>
> I think we should still go ahead and try to 'unbind' the other one.
Yes. Missed this error handling.
>
>> +     }
>> +     if (thermal_zone_unbind_cooling_device(thermal, 1, cdev)) {
>> +             pr_err("error unbinding cooling dev\n");
>> +             return -EINVAL;
>> +     }
>> +     return 0;
>> +
>> +}
>> +
>> +static int exynos4_get_temp(struct thermal_zone_device *thermal,
>> +                            unsigned long *temp)
>> +{
>> +     void *data;
>> +
>> +     if (!th_zone->sensor_conf) {
>> +             pr_info("Temperature sensor not initialised\n");
>> +             return -EINVAL;
>> +     }
>> +     data = th_zone->sensor_conf->private_data;
>> +     *temp = th_zone->sensor_conf->read_temperature(data);
>> +     /*convert the temperature into millicelsius*/
>> +     *temp = *temp * 1000;
>> +     return 0;
>> +}
>> +
>> +/* bind callback functions to thermalzone */
>> +static struct thermal_zone_device_ops exynos4_dev_ops = {
>> +     .bind = exynos4_bind,
>> +     .unbind = exynos4_unbind,
>> +     .get_temp = exynos4_get_temp,
>> +     .get_mode = exynos4_get_mode,
>> +     .set_mode = exynos4_set_mode,
>> +     .get_trip_type = exynos4_get_trip_type,
>> +     .get_trip_temp = exynos4_get_trip_temp,
>> +     .get_crit_temp = exynos4_get_crit_temp,
>> +};
>> +
>> +int exynos4_register_thermal(struct thermal_sensor_conf *sensor_conf)
>> +{
>> +     int ret, count, tab_size;
>> +     struct freq_pctg_table *tab_ptr;
>> +
>> +     if (!sensor_conf || !sensor_conf->read_temperature) {
>> +             pr_err("Temperature sensor not initialised\n");
>> +             return -EINVAL;
>> +     }
>> +
>> +     th_zone = kzalloc(sizeof(struct exynos4_thermal_zone), GFP_KERNEL);
>> +     if (!th_zone) {
>> +             ret = -ENOMEM;
>> +             goto err_unregister;
>> +     }
>> +
>> +     th_zone->sensor_conf = sensor_conf;
>> +
>> +     tab_ptr = (struct freq_pctg_table *)sensor_conf->cooling_data.freq_data;
>> +     tab_size = sensor_conf->cooling_data.freq_pctg_count;
>> +
>> +     /*Register the cpufreq cooling device*/
>> +     th_zone->cool_dev_size = 1;
>> +     count = 0;
>> +     th_zone->cool_dev[count] = cpufreq_cooling_register(
>> +                     (struct freq_pctg_table *)&(tab_ptr[count]),
>> +                     tab_size, cpumask_of(0));
>> +
>> +     if (IS_ERR(th_zone->cool_dev[count])) {
>> +             pr_err("Failed to register cpufreq cooling device\n");
>> +             ret = -EINVAL;
>> +             th_zone->cool_dev_size = 0;
>> +             goto err_unregister;
>> +     }
>> +
>> +     th_zone->therm_dev = thermal_zone_device_register(sensor_conf->name,
>> +                             3, NULL, &exynos4_dev_ops, 0, 0, 0, 1000);
>> +     if (IS_ERR(th_zone->therm_dev)) {
>> +             pr_err("Failed to register thermal zone device\n");
>> +             ret = -EINVAL;
>> +             goto err_unregister;
>> +     }
>> +
>> +     th_zone->active_interval = 1;
>> +     th_zone->idle_interval = 10;
>> +
>> +     exynos4_set_mode(th_zone->therm_dev, THERMAL_DEVICE_DISABLED);
>> +
>> +     pr_info("Exynos: Kernel Thermal management registered\n");
>> +
>> +     return 0;
>> +
>> +err_unregister:
>> +     exynos4_unregister_thermal();
>> +     return ret;
>> +}
>> +EXPORT_SYMBOL(exynos4_register_thermal);
>> +
>> +void exynos4_unregister_thermal(void)
>> +{
>> +     unsigned int i;
>> +
>> +     for (i = 0; i < th_zone->cool_dev_size; i++) {
>> +             if (th_zone && th_zone->cool_dev[i])
>> +                     cpufreq_cooling_unregister(th_zone->cool_dev[i]);
>> +     }
>> +
>> +     if (th_zone && th_zone->therm_dev)
>> +             thermal_zone_device_unregister(th_zone->therm_dev);
>> +
>> +     kfree(th_zone);
>> +
>> +     pr_info("Exynos: Kernel Thermal management unregistered\n");
>> +}
>> +EXPORT_SYMBOL(exynos4_unregister_thermal);
>> diff --git a/include/linux/exynos_thermal.h b/include/linux/exynos_thermal.h
>> new file mode 100644
>> index 0000000..186e409
>> --- /dev/null
>> +++ b/include/linux/exynos_thermal.h
>> @@ -0,0 +1,72 @@
>> +/* linux/include/linux/exynos_thermal.h
>> + *
>> + * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
>> + *           http://www.samsung.com
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> +*/
>> +
>> +#ifndef THERMAL_INTERFACE_H
>> +#define THERMAL_INTERFACE_H
>> +/* CPU Zone information */
>> +
>> +#define SENSOR_NAME_LEN      16
>> +#define MAX_TRIP_COUNT       8
>> +
>> +#define PANIC_ZONE      4
>> +#define WARN_ZONE       3
>> +#define MONITOR_ZONE    2
>> +#define SAFE_ZONE       1
>> +#define NO_ACTION       0
>
> I don't get why we need two separate SAFE and NO_ACTION
> zones..To me, both should be the same.
Yes not needed.
>
>> +
>> +
>> +struct       thermal_trip_point_conf {
>> +     int trip_val[MAX_TRIP_COUNT];
>> +     int trip_count;
>> +};
>> +
>> +struct       thermal_cooling_conf {
>> +     struct freq_pctg_table freq_data[MAX_TRIP_COUNT];
>> +     int freq_pctg_count;
>> +};
>> +
>> +/**
>> + * struct exynos4_tmu_platform_data
>> + * @name: name of the temperature sensor
>> + * @read_temperature: A function pointer to read temperature info
>> + * @private_data: Temperature sensor private data
>> + * @sensor_data: Sensor specific information like trigger temperature, level
>> + */
>> +struct thermal_sensor_conf {
>> +     char    name[SENSOR_NAME_LEN];
>> +     int     (*read_temperature)(void *data);
>> +     struct  thermal_trip_point_conf trip_data;
>> +     struct  thermal_cooling_conf cooling_data;
>
> Since both trip_count and freq_pctg_count are same at all
> times (please correct me if I am wrong) I think it's better
> to have a single 'count' variable inside this structure and move
> trip_val and freq_data to this structure directly.
No trip_count and pctg_count are different.
>
> One General Concern:
> Why do we even need this exynos_thermal layer between Thermal Framework
> and the Thermal Sensor Drivers ? I think the thermal sensor driver (in
> this case exynos_tmu.c) can directly register with the Thermal framework.
> This means, we will soon have platformXXX_thermal.c files for each
> platform, which is not really a good way to go IMHO.
> I also understand that the framework does not have alarm attributes,
> notification support etc..But We can add them if needed.
Yes even comments from Guenter Roeck and Mark Brown also point in the
same direction. Actually I wanted to separate the sensor h/w
implementation independent from thermal management algorithm so its
future modification may be clean. But looks like the agreement is to
merge them.
>
> I have reviewed your two sets of patches independently. My only
> request to you would be to post the next versions of both the patch
> sets at the same time, so that it becomes easier to understand and test.
Thanks even i also think this a good way.
>
> Thanks,
> Durga
>
>> +     void    *private_data;
>> +};
>> +
>> +/**
>> + * exynos4_register_thermal: Register to the exynos thermal interface.
>> + * @sensor_conf:   Structure containing temperature sensor information
>> + *
>> + * returns zero on success, else negative errno.
>> + */
>> +int exynos4_register_thermal(struct thermal_sensor_conf *sensor_conf);
>> +
>> +/**
>> + * exynos4_unregister_thermal: Un-register from the exynos thermal interface.
>> + *
>> + * return not applicable.
>> + */
>> +void exynos4_unregister_thermal(void);
>> +
>> +/**
>> + * exynos4_report_trigger: Report any trigger level crossed in the
>> + *   temperature sensor. This may be useful to take any cooling action.
>> + *
>> + * return not applicable.
>> + */
>> +extern void exynos4_report_trigger(void);
>> +#endif
>> --
>> 1.7.1
>

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [PATCH 1/4] thermal: exynos: Add thermal interface support for linux thermal layer
  2012-03-13  4:22       ` Amit Kachhap
  (?)
@ 2012-03-13  4:48         ` R, Durgadoss
  -1 siblings, 0 replies; 43+ messages in thread
From: R, Durgadoss @ 2012-03-13  4:48 UTC (permalink / raw)
  To: Amit Kachhap
  Cc: linux-samsung-soc, linaro-dev, patches, linux-kernel, lm-sensors,
	linux-acpi, linux-pm

Hi Amit,

[snip.]

> >> +
> >> +     kobject_uevent(&th_zone->therm_dev->device.kobj, KOBJ_CHANGE);
> >
> > Wouldn't it make more sense to pass the trip point id also as an 'env'
> > parameter ? This way, the user space can easily figure out which trip
> > point has been crossed.
> Its a good suggestion. I will check if some uevent property allows that.

The kobject_uevent_env(...) call allows that. The third argument takes a
char *envp[]. For example, We could pass "trip=0" to indicate the trip point.
I should have mentioned this in my previous reply itself..missed it..

Thanks,
Durga

^ permalink raw reply	[flat|nested] 43+ messages in thread

* RE: [PATCH 1/4] thermal: exynos: Add thermal interface support for linux thermal layer
@ 2012-03-13  4:48         ` R, Durgadoss
  0 siblings, 0 replies; 43+ messages in thread
From: R, Durgadoss @ 2012-03-13  4:48 UTC (permalink / raw)
  To: Amit Kachhap
  Cc: linux-pm, linux-samsung-soc, linux-kernel, mjg59, linux-acpi,
	lenb, linaro-dev, lm-sensors, eduardo.valentin, patches

Hi Amit,

[snip.]

> >> +
> >> +     kobject_uevent(&th_zone->therm_dev->device.kobj, KOBJ_CHANGE);
> >
> > Wouldn't it make more sense to pass the trip point id also as an 'env'
> > parameter ? This way, the user space can easily figure out which trip
> > point has been crossed.
> Its a good suggestion. I will check if some uevent property allows that.

The kobject_uevent_env(...) call allows that. The third argument takes a
char *envp[]. For example, We could pass "trip=0" to indicate the trip point.
I should have mentioned this in my previous reply itself..missed it..

Thanks,
Durga

^ permalink raw reply	[flat|nested] 43+ messages in thread

* Re: [lm-sensors] [PATCH 1/4] thermal: exynos: Add thermal interface support for linux thermal layer
@ 2012-03-13  4:48         ` R, Durgadoss
  0 siblings, 0 replies; 43+ messages in thread
From: R, Durgadoss @ 2012-03-13  4:48 UTC (permalink / raw)
  To: Amit Kachhap
  Cc: linux-samsung-soc, linaro-dev, patches, linux-kernel, lm-sensors,
	linux-acpi, linux-pm

Hi Amit,

[snip.]

> >> +
> >> +     kobject_uevent(&th_zone->therm_dev->device.kobj, KOBJ_CHANGE);
> >
> > Wouldn't it make more sense to pass the trip point id also as an 'env'
> > parameter ? This way, the user space can easily figure out which trip
> > point has been crossed.
> Its a good suggestion. I will check if some uevent property allows that.

The kobject_uevent_env(...) call allows that. The third argument takes a
char *envp[]. For example, We could pass "trip=0" to indicate the trip point.
I should have mentioned this in my previous reply itself..missed it..

Thanks,
Durga

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 43+ messages in thread

end of thread, other threads:[~2012-03-13  4:48 UTC | newest]

Thread overview: 43+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-03-03 11:06 [PATCH 0/4] thermal: exynos: Add kernel thermal support for exynos platform Amit Daniel Kachhap
2012-03-03 11:18 ` [lm-sensors] " Amit Daniel Kachhap
2012-03-03 11:06 ` Amit Daniel Kachhap
2012-03-03 11:06 ` [PATCH 1/4] thermal: exynos: Add thermal interface support for linux thermal layer Amit Daniel Kachhap
2012-03-03 11:18   ` [lm-sensors] " Amit Daniel Kachhap
2012-03-03 11:06   ` Amit Daniel Kachhap
2012-03-12 10:51   ` R, Durgadoss
2012-03-12 10:51     ` [lm-sensors] " R, Durgadoss
2012-03-12 10:51     ` R, Durgadoss
2012-03-13  4:22     ` Amit Kachhap
2012-03-13  4:34       ` [lm-sensors] " Amit Kachhap
2012-03-13  4:22       ` Amit Kachhap
2012-03-13  4:48       ` R, Durgadoss
2012-03-13  4:48         ` [lm-sensors] " R, Durgadoss
2012-03-13  4:48         ` R, Durgadoss
2012-03-03 11:06 ` [PATCH 2/4] hwmon: exynos4: Move thermal sensor driver to driver/mfd directory Amit Daniel Kachhap
2012-03-03 11:18   ` [lm-sensors] " Amit Daniel Kachhap
2012-03-03 11:06   ` Amit Daniel Kachhap
2012-03-03 12:21   ` Sylwester Nawrocki
2012-03-03 12:21     ` [lm-sensors] " Sylwester Nawrocki
2012-03-05  8:44     ` Amit Kachhap
2012-03-05  8:56       ` [lm-sensors] " Amit Kachhap
2012-03-03 14:52   ` Guenter Roeck
2012-03-03 14:52     ` Guenter Roeck
2012-03-03 14:52     ` Guenter Roeck
2012-03-03 16:44   ` Mark Brown
2012-03-03 16:44     ` [lm-sensors] [linux-pm] [PATCH 2/4] hwmon: exynos4: Move thermal sensor driver to driver/mfd dir Mark Brown
2012-03-03 16:44     ` [linux-pm] [PATCH 2/4] hwmon: exynos4: Move thermal sensor driver to driver/mfd directory Mark Brown
2012-03-03 18:04     ` [lm-sensors] " Guenter Roeck
2012-03-03 18:04       ` [lm-sensors] [linux-pm] [PATCH 2/4] hwmon: exynos4: Move thermal sensor driver to driver/mfd dir Guenter Roeck
2012-03-03 18:04       ` [lm-sensors] [linux-pm] [PATCH 2/4] hwmon: exynos4: Move thermal sensor driver to driver/mfd directory Guenter Roeck
2012-03-05  9:11       ` [lm-sensors] " Amit Kachhap
2012-03-05  9:23         ` [lm-sensors] [linux-pm] [PATCH 2/4] hwmon: exynos4: Move thermal sensor driver to driver/mfd dir Amit Kachhap
2012-03-05  9:11         ` [lm-sensors] [linux-pm] [PATCH 2/4] hwmon: exynos4: Move thermal sensor driver to driver/mfd directory Amit Kachhap
2012-03-03 11:06 ` [PATCH 3/4] thermal: exynos4: Register the tmu sensor with the thermal interface layer Amit Daniel Kachhap
2012-03-03 11:18   ` [lm-sensors] " Amit Daniel Kachhap
2012-03-03 11:06   ` Amit Daniel Kachhap
2012-03-03 14:55   ` [lm-sensors] " Guenter Roeck
2012-03-03 14:55     ` [lm-sensors] [PATCH 3/4] thermal: exynos4: Register the tmu sensor with the thermal interface la Guenter Roeck
2012-03-03 14:55     ` [lm-sensors] [PATCH 3/4] thermal: exynos4: Register the tmu sensor with the thermal interface layer Guenter Roeck
2012-03-03 11:06 ` [PATCH 4/4] ARM: exynos4: Add thermal sensor driver platform device support Amit Daniel Kachhap
2012-03-03 11:18   ` [lm-sensors] " Amit Daniel Kachhap
2012-03-03 11:06   ` Amit Daniel Kachhap

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.