summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAmit Daniel Kachhap <amit.kachhap@linaro.org>2011-12-12 10:44:36 +0530
committerAmit Daniel Kachhap <amit.kachhap@linaro.org>2011-12-14 21:31:27 +0530
commitd9f151cda68a86d176b0d6773c019eeae1d58301 (patch)
tree4e6f3e9556db8558b37c63801c1429b618210712
parenta3cb3d54f43aa6fdae32be9b0e12b66a0ccedfc3 (diff)
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. Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>
-rw-r--r--drivers/thermal/Kconfig8
-rw-r--r--drivers/thermal/Makefile1
-rw-r--r--drivers/thermal/exynos_thermal.c334
-rw-r--r--include/linux/exynos_thermal.h32
4 files changed, 375 insertions, 0 deletions
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 298c1cdcd38..938953fd1c2 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.
+
+menuconfig SAMSUNG_THERMAL_INTERFACE
+ bool "Samsung Thermal 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 655cbc42529..c67b6b222be 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 00000000000..ff189b145ad
--- /dev/null
+++ b/drivers/thermal/exynos_thermal.c
@@ -0,0 +1,334 @@
+/* linux/drivers/staging/thermal_exynos4/thermal_interface.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/platform_data/exynos4_tmu.h>
+#include <linux/exynos_thermal.h>
+
+static unsigned int verbose;
+
+struct exynos4_thermal_zone {
+ unsigned int idle_interval;
+ unsigned int active_interval;
+ struct thermal_zone_device *therm_dev;
+ struct thermal_cooling_device *cool_dev;
+ struct platform_device *exynos4_dev;
+ struct exynos4_tmu_platform_data *sensor_data;
+};
+static struct thermal_sensor_info *exynos4_sensor_info;
+
+static struct exynos4_thermal_zone *th_zone;
+
+static int exynos4_get_mode(struct thermal_zone_device *thermal,
+ enum thermal_device_mode *mode)
+{
+ if (exynos4_sensor_info) {
+ pr_info("Temperature sensor not initialised\n");
+ *mode = THERMAL_DEVICE_DISABLED;
+ } else
+ *mode = THERMAL_DEVICE_ENABLED;
+ return 0;
+}
+
+/*
+ * set operation mode;
+ * enabled: the thermal layer of the kernel takes care about
+ * the temperature.
+ * disabled: temperature sensor is not enabled.
+ */
+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 th_temp = th_zone->sensor_data->threshold;
+ unsigned int monitor_temp = th_temp +
+ th_zone->sensor_data->trigger_levels[1];
+
+ thermal_zone_device_update(th_zone->therm_dev);
+
+ 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;
+}
+
+static int exynos4_get_trip_type(struct thermal_zone_device *thermal, int trip,
+ enum thermal_trip_type *type)
+{
+ if (verbose)
+ pr_info("%s, trip no=%d\n", __func__, trip);
+ 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)
+{
+ unsigned int th_temp = th_zone->sensor_data->threshold;
+
+ /*Monitor zone*/
+ if (trip == 0)
+ *temp = th_temp + th_zone->sensor_data->trigger_levels[1];
+ /*Warn zone*/
+ else if (trip == 1)
+ *temp = th_temp + th_zone->sensor_data->trigger_levels[2];
+ /*Panic zone*/
+ else if (trip == 2)
+ *temp = th_temp + th_zone->sensor_data->trigger_levels[3];
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int exynos4_get_crit_temp(struct thermal_zone_device *thermal,
+ unsigned long *temp)
+{
+ unsigned int th_temp = th_zone->sensor_data->threshold;
+ /*Panic zone*/
+ *temp = th_temp + th_zone->sensor_data->trigger_levels[3];
+ 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)
+ 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)
+ return 0;
+ if (thermal_zone_unbind_cooling_device(thermal, 0, cdev)) {
+ pr_err("error unbinding cooling dev\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int exynos4_get_temp(struct thermal_zone_device *thermal,
+ unsigned long *t)
+{
+ int temp = 0;
+ void *data;
+
+ if (!exynos4_sensor_info) {
+ pr_info("Temperature sensor not initialised\n");
+ return -EINVAL;
+ }
+ data = exynos4_sensor_info->private_data;
+ temp = exynos4_sensor_info->read_temperature(data);
+
+ if (verbose)
+ pr_notice("temp %d\n", temp);
+
+ *t = temp;
+ 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_temp_sensor(struct thermal_sensor_info *sensor_info)
+{
+ exynos4_sensor_info = sensor_info;
+ return 0;
+}
+
+
+static int __devinit exynos4_probe(struct platform_device *device)
+{
+ return 0;
+}
+
+static int exynos4_remove(struct platform_device *device)
+{
+ return 0;
+}
+
+static struct platform_driver exynos4_driver = {
+ .driver = {
+ .name = "exynos4",
+ .owner = THIS_MODULE,
+ },
+ .probe = exynos4_probe,
+ .remove = exynos4_remove,
+};
+
+static int exynos4_register_platform(void)
+{
+ int err = 0;
+
+ th_zone = kzalloc(sizeof(struct exynos4_thermal_zone), GFP_KERNEL);
+ if (!th_zone)
+ return -ENOMEM;
+
+ err = platform_driver_register(&exynos4_driver);
+ if (err)
+ return err;
+
+ th_zone->exynos4_dev = platform_device_alloc("exynos4", -1);
+ if (!th_zone->exynos4_dev) {
+ err = -ENOMEM;
+ goto err_device_alloc;
+ }
+ err = platform_device_add(th_zone->exynos4_dev);
+ if (err)
+ goto err_device_add;
+
+ return 0;
+
+err_device_add:
+ platform_device_put(th_zone->exynos4_dev);
+err_device_alloc:
+ platform_driver_unregister(&exynos4_driver);
+ return err;
+}
+
+static void exynos4_unregister_platform(void)
+{
+ platform_device_unregister(th_zone->exynos4_dev);
+ platform_driver_unregister(&exynos4_driver);
+ kfree(th_zone);
+}
+
+static int exynos4_register_thermal(void)
+{
+ if (!exynos4_sensor_info) {
+ pr_info("Temperature sensor not initialised\n");
+ return -EINVAL;
+ }
+
+ th_zone->sensor_data = exynos4_sensor_info->sensor_data;
+ if (!th_zone->sensor_data) {
+ pr_info("Temperature sensor data not initialised\n");
+ return -EINVAL;
+ }
+
+ th_zone->cool_dev = cpufreq_cooling_register(
+ (struct freq_pctg_table *)th_zone->sensor_data->freq_tab,
+ th_zone->sensor_data->level_count);
+
+ if (IS_ERR(th_zone->cool_dev))
+ return -EINVAL;
+
+ th_zone->therm_dev = thermal_zone_device_register("exynos4-therm",
+ 3, NULL, &exynos4_dev_ops, 0, 0, 0, 1000);
+ if (IS_ERR(th_zone->therm_dev))
+ return -EINVAL;
+
+ th_zone->active_interval = 1;
+ th_zone->idle_interval = 10;
+ exynos4_set_mode(th_zone->therm_dev, THERMAL_DEVICE_DISABLED);
+
+ return 0;
+}
+
+static void exynos4_unregister_thermal(void)
+{
+ if (th_zone->cool_dev)
+ cpufreq_cooling_unregister();
+
+ if (th_zone->therm_dev)
+ thermal_zone_device_unregister(th_zone->therm_dev);
+}
+
+static int __init exynos4_thermal_init(void)
+{
+ int err = 0;
+
+ err = exynos4_register_platform();
+ if (err)
+ goto out_err;
+
+ err = exynos4_register_thermal();
+ if (err)
+ goto err_unreg;
+
+ return 0;
+
+err_unreg:
+ exynos4_unregister_thermal();
+ exynos4_unregister_platform();
+
+out_err:
+ return err;
+}
+
+static void __exit exynos4_thermal_exit(void)
+{
+ exynos4_unregister_thermal();
+ exynos4_unregister_platform();
+}
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Amit Daniel <amit.kachhap@linaro.org>");
+MODULE_DESCRIPTION("samsung Exynos4 thermal monitor driver");
+
+module_init(exynos4_thermal_init);
+module_exit(exynos4_thermal_exit);
diff --git a/include/linux/exynos_thermal.h b/include/linux/exynos_thermal.h
new file mode 100644
index 00000000000..15f1a17453d
--- /dev/null
+++ b/include/linux/exynos_thermal.h
@@ -0,0 +1,32 @@
+/* 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 PANIC_ZONE 4
+#define WARN_ZONE 3
+#define MONITOR_ZONE 2
+#define SAFE_ZONE 1
+#define NO_ACTION 0
+
+struct thermal_sensor_info {
+ char name[SENSOR_NAME_LEN];
+ int (*read_temperature)(void *data);
+ void *private_data;
+ void *sensor_data;
+};
+
+extern int exynos4_register_temp_sensor(struct thermal_sensor_info *sensor);
+extern void exynos4_report_trigger(void);
+#endif