diff options
Diffstat (limited to 'patches.tizen/0954-thermal-exynos-Bifurcate-exynos-thermal-common-and-t.patch')
-rw-r--r-- | patches.tizen/0954-thermal-exynos-Bifurcate-exynos-thermal-common-and-t.patch | 1003 |
1 files changed, 1003 insertions, 0 deletions
diff --git a/patches.tizen/0954-thermal-exynos-Bifurcate-exynos-thermal-common-and-t.patch b/patches.tizen/0954-thermal-exynos-Bifurcate-exynos-thermal-common-and-t.patch new file mode 100644 index 00000000000..ad355e19dbb --- /dev/null +++ b/patches.tizen/0954-thermal-exynos-Bifurcate-exynos-thermal-common-and-t.patch @@ -0,0 +1,1003 @@ +From edfb76bc55440d9a466408f01c6978e79d25845a Mon Sep 17 00:00:00 2001 +From: Amit Daniel Kachhap <amit.daniel@samsung.com> +Date: Mon, 24 Jun 2013 16:20:25 +0530 +Subject: [PATCH 0954/1302] thermal: exynos: Bifurcate exynos thermal common + and tmu controller code + +This code bifurcates exynos thermal implementation into common and sensor +specific parts. The common thermal code interacts with core thermal layer and +core cpufreq cooling parts and is independent of SOC specific driver. This +change is needed to cleanly add support for new TMU sensors. + +Acked-by: Kukjin Kim <kgene.kim@samsung.com> +Acked-by: Jonghwa Lee <jonghwa3.lee@samsung.com> +Acked-by: Eduardo Valentin <eduardo.valentin@ti.com> +Signed-off-by: Amit Daniel Kachhap <amit.daniel@samsung.com> +Signed-off-by: Eduardo Valentin <eduardo.valentin@ti.com> +Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com> +--- + drivers/thermal/samsung/Kconfig | 19 +- + drivers/thermal/samsung/Makefile | 4 +- + drivers/thermal/samsung/exynos_thermal.c | 419 +----------------------- + drivers/thermal/samsung/exynos_thermal_common.c | 385 ++++++++++++++++++++++ + drivers/thermal/samsung/exynos_thermal_common.h | 83 +++++ + 5 files changed, 491 insertions(+), 419 deletions(-) + create mode 100644 drivers/thermal/samsung/exynos_thermal_common.c + create mode 100644 drivers/thermal/samsung/exynos_thermal_common.h + +diff --git a/drivers/thermal/samsung/Kconfig b/drivers/thermal/samsung/Kconfig +index 2cf31ad..f8100b1 100644 +--- a/drivers/thermal/samsung/Kconfig ++++ b/drivers/thermal/samsung/Kconfig +@@ -1,8 +1,17 @@ + config EXYNOS_THERMAL +- tristate "Temperature sensor on Samsung EXYNOS" ++ tristate "Exynos thermal management unit driver" + depends on ARCH_HAS_BANDGAP + help +- If you say yes here you get support for TMU (Thermal Management +- Unit) on SAMSUNG EXYNOS series of SoC. This helps in registering +- the exynos thermal driver with the core thermal layer and cpu +- cooling API's. ++ If you say yes here you get support for the TMU (Thermal Management ++ Unit) driver for SAMSUNG EXYNOS series of soc. This driver initialises ++ the TMU, reports temperature and handles cooling action if defined. ++ This driver uses the exynos core thermal API's. ++ ++config EXYNOS_THERMAL_CORE ++ bool "Core thermal framework support for EXYNOS SOC's" ++ depends on EXYNOS_THERMAL ++ help ++ If you say yes here you get support for EXYNOS TMU ++ (Thermal Management Unit) common registration/unregistration ++ functions to the core thermal layer and also to use the generic ++ cpu cooling API's. +diff --git a/drivers/thermal/samsung/Makefile b/drivers/thermal/samsung/Makefile +index 1fe6d93..6227d4f 100644 +--- a/drivers/thermal/samsung/Makefile ++++ b/drivers/thermal/samsung/Makefile +@@ -1,4 +1,6 @@ + # + # Samsung thermal specific Makefile + # +-obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o ++obj-$(CONFIG_EXYNOS_THERMAL) += exynos_soc_thermal.o ++exynos_soc_thermal-y := exynos_thermal.o ++exynos_soc_thermal-$(CONFIG_EXYNOS_THERMAL_CORE) += exynos_thermal_common.o +diff --git a/drivers/thermal/samsung/exynos_thermal.c b/drivers/thermal/samsung/exynos_thermal.c +index 9af4b93..f3500ab 100644 +--- a/drivers/thermal/samsung/exynos_thermal.c ++++ b/drivers/thermal/samsung/exynos_thermal.c +@@ -21,23 +21,15 @@ + * + */ + +-#include <linux/module.h> +-#include <linux/err.h> +-#include <linux/kernel.h> +-#include <linux/slab.h> +-#include <linux/platform_device.h> +-#include <linux/interrupt.h> + #include <linux/clk.h> +-#include <linux/workqueue.h> +-#include <linux/sysfs.h> +-#include <linux/kobject.h> + #include <linux/io.h> +-#include <linux/mutex.h> +-#include <linux/platform_data/exynos_thermal.h> +-#include <linux/thermal.h> +-#include <linux/cpufreq.h> +-#include <linux/cpu_cooling.h> ++#include <linux/interrupt.h> ++#include <linux/module.h> + #include <linux/of.h> ++#include <linux/platform_device.h> ++#include <linux/platform_data/exynos_thermal.h> ++ ++#include "exynos_thermal_common.h" + + /* Exynos generic registers */ + #define EXYNOS_TMU_REG_TRIMINFO 0x0 +@@ -88,16 +80,6 @@ + #define EFUSE_MIN_VALUE 40 + #define EFUSE_MAX_VALUE 100 + +-/* In-kernel thermal framework related macros & definations */ +-#define SENSOR_NAME_LEN 16 +-#define MAX_TRIP_COUNT 8 +-#define MAX_COOLING_DEVICE 4 +-#define MAX_THRESHOLD_LEVS 4 +- +-#define ACTIVE_INTERVAL 500 +-#define IDLE_INTERVAL 10000 +-#define MCELSIUS 1000 +- + #ifdef CONFIG_THERMAL_EMULATION + #define EXYNOS_EMUL_TIME 0x57F0 + #define EXYNOS_EMUL_TIME_SHIFT 16 +@@ -106,17 +88,6 @@ + #define EXYNOS_EMUL_ENABLE 0x1 + #endif /* CONFIG_THERMAL_EMULATION */ + +-/* CPU Zone information */ +-#define PANIC_ZONE 4 +-#define WARN_ZONE 3 +-#define MONITOR_ZONE 2 +-#define SAFE_ZONE 1 +- +-#define GET_ZONE(trip) (trip + 2) +-#define GET_TRIP(zone) (zone - 2) +- +-#define EXYNOS_ZONE_COUNT 3 +- + struct exynos_tmu_data { + struct exynos_tmu_platform_data *pdata; + struct resource *mem; +@@ -129,384 +100,6 @@ struct exynos_tmu_data { + u8 temp_error1, temp_error2; + }; + +-struct thermal_trip_point_conf { +- int trip_val[MAX_TRIP_COUNT]; +- int trip_count; +- u8 trigger_falling; +-}; +- +-struct thermal_cooling_conf { +- struct freq_clip_table freq_data[MAX_TRIP_COUNT]; +- int freq_clip_count; +-}; +- +-struct thermal_sensor_conf { +- char name[SENSOR_NAME_LEN]; +- int (*read_temperature)(void *data); +- int (*write_emul_temp)(void *drv_data, unsigned long temp); +- struct thermal_trip_point_conf trip_data; +- struct thermal_cooling_conf cooling_data; +- void *private_data; +-}; +- +-struct exynos_thermal_zone { +- enum thermal_device_mode mode; +- struct thermal_zone_device *therm_dev; +- struct thermal_cooling_device *cool_dev[MAX_COOLING_DEVICE]; +- unsigned int cool_dev_size; +- struct platform_device *exynos4_dev; +- struct thermal_sensor_conf *sensor_conf; +- bool bind; +-}; +- +-static struct exynos_thermal_zone *th_zone; +-static void exynos_unregister_thermal(void); +-static int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf); +- +-/* Get mode callback functions for thermal zone */ +-static int exynos_get_mode(struct thermal_zone_device *thermal, +- enum thermal_device_mode *mode) +-{ +- if (th_zone) +- *mode = th_zone->mode; +- return 0; +-} +- +-/* Set mode callback functions for thermal zone */ +-static int exynos_set_mode(struct thermal_zone_device *thermal, +- enum thermal_device_mode mode) +-{ +- if (!th_zone->therm_dev) { +- pr_notice("thermal zone not registered\n"); +- return 0; +- } +- +- mutex_lock(&th_zone->therm_dev->lock); +- +- if (mode == THERMAL_DEVICE_ENABLED && +- !th_zone->sensor_conf->trip_data.trigger_falling) +- th_zone->therm_dev->polling_delay = IDLE_INTERVAL; +- else +- th_zone->therm_dev->polling_delay = 0; +- +- mutex_unlock(&th_zone->therm_dev->lock); +- +- th_zone->mode = mode; +- thermal_zone_device_update(th_zone->therm_dev); +- pr_info("thermal polling set for duration=%d msec\n", +- th_zone->therm_dev->polling_delay); +- return 0; +-} +- +- +-/* Get trip type callback functions for thermal zone */ +-static int exynos_get_trip_type(struct thermal_zone_device *thermal, int trip, +- enum thermal_trip_type *type) +-{ +- switch (GET_ZONE(trip)) { +- case MONITOR_ZONE: +- case WARN_ZONE: +- *type = THERMAL_TRIP_ACTIVE; +- break; +- case PANIC_ZONE: +- *type = THERMAL_TRIP_CRITICAL; +- break; +- default: +- return -EINVAL; +- } +- return 0; +-} +- +-/* Get trip temperature callback functions for thermal zone */ +-static int exynos_get_trip_temp(struct thermal_zone_device *thermal, int trip, +- unsigned long *temp) +-{ +- if (trip < GET_TRIP(MONITOR_ZONE) || trip > GET_TRIP(PANIC_ZONE)) +- return -EINVAL; +- +- *temp = th_zone->sensor_conf->trip_data.trip_val[trip]; +- /* convert the temperature into millicelsius */ +- *temp = *temp * MCELSIUS; +- +- return 0; +-} +- +-/* Get critical temperature callback functions for thermal zone */ +-static int exynos_get_crit_temp(struct thermal_zone_device *thermal, +- unsigned long *temp) +-{ +- int ret; +- /* Panic zone */ +- ret = exynos_get_trip_temp(thermal, GET_TRIP(PANIC_ZONE), temp); +- return ret; +-} +- +-/* Bind callback functions for thermal zone */ +-static int exynos_bind(struct thermal_zone_device *thermal, +- struct thermal_cooling_device *cdev) +-{ +- int ret = 0, i, tab_size, level; +- struct freq_clip_table *tab_ptr, *clip_data; +- struct thermal_sensor_conf *data = th_zone->sensor_conf; +- +- tab_ptr = (struct freq_clip_table *)data->cooling_data.freq_data; +- tab_size = data->cooling_data.freq_clip_count; +- +- if (tab_ptr == NULL || tab_size == 0) +- return -EINVAL; +- +- /* find the cooling device registered*/ +- for (i = 0; i < th_zone->cool_dev_size; i++) +- if (cdev == th_zone->cool_dev[i]) +- break; +- +- /* No matching cooling device */ +- if (i == th_zone->cool_dev_size) +- return 0; +- +- /* Bind the thermal zone to the cpufreq cooling device */ +- for (i = 0; i < tab_size; i++) { +- clip_data = (struct freq_clip_table *)&(tab_ptr[i]); +- level = cpufreq_cooling_get_level(0, clip_data->freq_clip_max); +- if (level == THERMAL_CSTATE_INVALID) +- return 0; +- switch (GET_ZONE(i)) { +- case MONITOR_ZONE: +- case WARN_ZONE: +- if (thermal_zone_bind_cooling_device(thermal, i, cdev, +- level, 0)) { +- pr_err("error binding cdev inst %d\n", i); +- ret = -EINVAL; +- } +- th_zone->bind = true; +- break; +- default: +- ret = -EINVAL; +- } +- } +- +- return ret; +-} +- +-/* Unbind callback functions for thermal zone */ +-static int exynos_unbind(struct thermal_zone_device *thermal, +- struct thermal_cooling_device *cdev) +-{ +- int ret = 0, i, tab_size; +- struct thermal_sensor_conf *data = th_zone->sensor_conf; +- +- if (th_zone->bind == false) +- return 0; +- +- tab_size = data->cooling_data.freq_clip_count; +- +- if (tab_size == 0) +- return -EINVAL; +- +- /* find the cooling device registered*/ +- for (i = 0; i < th_zone->cool_dev_size; i++) +- if (cdev == th_zone->cool_dev[i]) +- break; +- +- /* No matching cooling device */ +- if (i == th_zone->cool_dev_size) +- return 0; +- +- /* Bind the thermal zone to the cpufreq cooling device */ +- for (i = 0; i < tab_size; i++) { +- switch (GET_ZONE(i)) { +- case MONITOR_ZONE: +- case WARN_ZONE: +- if (thermal_zone_unbind_cooling_device(thermal, i, +- cdev)) { +- pr_err("error unbinding cdev inst=%d\n", i); +- ret = -EINVAL; +- } +- th_zone->bind = false; +- break; +- default: +- ret = -EINVAL; +- } +- } +- return ret; +-} +- +-/* Get temperature callback functions for thermal zone */ +-static int exynos_get_temp(struct thermal_zone_device *thermal, +- unsigned long *temp) +-{ +- 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 * MCELSIUS; +- return 0; +-} +- +-/* Get temperature callback functions for thermal zone */ +-static int exynos_set_emul_temp(struct thermal_zone_device *thermal, +- unsigned long temp) +-{ +- void *data; +- int ret = -EINVAL; +- +- if (!th_zone->sensor_conf) { +- pr_info("Temperature sensor not initialised\n"); +- return -EINVAL; +- } +- data = th_zone->sensor_conf->private_data; +- if (th_zone->sensor_conf->write_emul_temp) +- ret = th_zone->sensor_conf->write_emul_temp(data, temp); +- return ret; +-} +- +-/* Get the temperature trend */ +-static int exynos_get_trend(struct thermal_zone_device *thermal, +- int trip, enum thermal_trend *trend) +-{ +- int ret; +- unsigned long trip_temp; +- +- ret = exynos_get_trip_temp(thermal, trip, &trip_temp); +- if (ret < 0) +- return ret; +- +- if (thermal->temperature >= trip_temp) +- *trend = THERMAL_TREND_RAISE_FULL; +- else +- *trend = THERMAL_TREND_DROP_FULL; +- +- return 0; +-} +-/* Operation callback functions for thermal zone */ +-static struct thermal_zone_device_ops const exynos_dev_ops = { +- .bind = exynos_bind, +- .unbind = exynos_unbind, +- .get_temp = exynos_get_temp, +- .set_emul_temp = exynos_set_emul_temp, +- .get_trend = exynos_get_trend, +- .get_mode = exynos_get_mode, +- .set_mode = exynos_set_mode, +- .get_trip_type = exynos_get_trip_type, +- .get_trip_temp = exynos_get_trip_temp, +- .get_crit_temp = exynos_get_crit_temp, +-}; +- +-/* +- * This function may be called from interrupt based temperature sensor +- * when threshold is changed. +- */ +-static void exynos_report_trigger(void) +-{ +- unsigned int i; +- char data[10]; +- char *envp[] = { data, NULL }; +- +- if (!th_zone || !th_zone->therm_dev) +- return; +- if (th_zone->bind == false) { +- for (i = 0; i < th_zone->cool_dev_size; i++) { +- if (!th_zone->cool_dev[i]) +- continue; +- exynos_bind(th_zone->therm_dev, +- th_zone->cool_dev[i]); +- } +- } +- +- thermal_zone_device_update(th_zone->therm_dev); +- +- mutex_lock(&th_zone->therm_dev->lock); +- /* Find the level for which trip happened */ +- for (i = 0; i < th_zone->sensor_conf->trip_data.trip_count; i++) { +- if (th_zone->therm_dev->last_temperature < +- th_zone->sensor_conf->trip_data.trip_val[i] * MCELSIUS) +- break; +- } +- +- if (th_zone->mode == THERMAL_DEVICE_ENABLED && +- !th_zone->sensor_conf->trip_data.trigger_falling) { +- if (i > 0) +- th_zone->therm_dev->polling_delay = ACTIVE_INTERVAL; +- else +- th_zone->therm_dev->polling_delay = IDLE_INTERVAL; +- } +- +- snprintf(data, sizeof(data), "%u", i); +- kobject_uevent_env(&th_zone->therm_dev->device.kobj, KOBJ_CHANGE, envp); +- mutex_unlock(&th_zone->therm_dev->lock); +-} +- +-/* Register with the in-kernel thermal management */ +-static int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf) +-{ +- int ret; +- struct cpumask mask_val; +- +- if (!sensor_conf || !sensor_conf->read_temperature) { +- pr_err("Temperature sensor not initialised\n"); +- return -EINVAL; +- } +- +- th_zone = kzalloc(sizeof(struct exynos_thermal_zone), GFP_KERNEL); +- if (!th_zone) +- return -ENOMEM; +- +- th_zone->sensor_conf = sensor_conf; +- cpumask_set_cpu(0, &mask_val); +- th_zone->cool_dev[0] = cpufreq_cooling_register(&mask_val); +- if (IS_ERR(th_zone->cool_dev[0])) { +- pr_err("Failed to register cpufreq cooling device\n"); +- ret = -EINVAL; +- goto err_unregister; +- } +- th_zone->cool_dev_size++; +- +- th_zone->therm_dev = thermal_zone_device_register(sensor_conf->name, +- EXYNOS_ZONE_COUNT, 0, NULL, &exynos_dev_ops, NULL, 0, +- sensor_conf->trip_data.trigger_falling ? +- 0 : IDLE_INTERVAL); +- +- if (IS_ERR(th_zone->therm_dev)) { +- pr_err("Failed to register thermal zone device\n"); +- ret = PTR_ERR(th_zone->therm_dev); +- goto err_unregister; +- } +- th_zone->mode = THERMAL_DEVICE_ENABLED; +- +- pr_info("Exynos: Kernel Thermal management registered\n"); +- +- return 0; +- +-err_unregister: +- exynos_unregister_thermal(); +- return ret; +-} +- +-/* Un-Register with the in-kernel thermal management */ +-static void exynos_unregister_thermal(void) +-{ +- int i; +- +- if (!th_zone) +- return; +- +- if (th_zone->therm_dev) +- thermal_zone_device_unregister(th_zone->therm_dev); +- +- for (i = 0; i < th_zone->cool_dev_size; i++) { +- if (th_zone->cool_dev[i]) +- cpufreq_cooling_unregister(th_zone->cool_dev[i]); +- } +- +- kfree(th_zone); +- pr_info("Exynos: Kernel Thermal management unregistered\n"); +-} +- + /* + * TMU treats temperature as a mapped temperature code. + * The temperature is converted differently depending on the calibration type. +diff --git a/drivers/thermal/samsung/exynos_thermal_common.c b/drivers/thermal/samsung/exynos_thermal_common.c +new file mode 100644 +index 0000000..f20f458 +--- /dev/null ++++ b/drivers/thermal/samsung/exynos_thermal_common.c +@@ -0,0 +1,385 @@ ++/* ++ * exynos_thermal_common.c - Samsung EXYNOS common thermal file ++ * ++ * Copyright (C) 2013 Samsung Electronics ++ * Amit Daniel Kachhap <amit.daniel@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/cpu_cooling.h> ++#include <linux/err.h> ++#include <linux/platform_data/exynos_thermal.h> ++#include <linux/slab.h> ++#include <linux/thermal.h> ++ ++#include "exynos_thermal_common.h" ++ ++struct exynos_thermal_zone { ++ enum thermal_device_mode mode; ++ struct thermal_zone_device *therm_dev; ++ struct thermal_cooling_device *cool_dev[MAX_COOLING_DEVICE]; ++ unsigned int cool_dev_size; ++ struct platform_device *exynos4_dev; ++ struct thermal_sensor_conf *sensor_conf; ++ bool bind; ++}; ++ ++static struct exynos_thermal_zone *th_zone; ++ ++/* Get mode callback functions for thermal zone */ ++static int exynos_get_mode(struct thermal_zone_device *thermal, ++ enum thermal_device_mode *mode) ++{ ++ if (th_zone) ++ *mode = th_zone->mode; ++ return 0; ++} ++ ++/* Set mode callback functions for thermal zone */ ++static int exynos_set_mode(struct thermal_zone_device *thermal, ++ enum thermal_device_mode mode) ++{ ++ if (!th_zone->therm_dev) { ++ pr_notice("thermal zone not registered\n"); ++ return 0; ++ } ++ ++ mutex_lock(&th_zone->therm_dev->lock); ++ ++ if (mode == THERMAL_DEVICE_ENABLED && ++ !th_zone->sensor_conf->trip_data.trigger_falling) ++ th_zone->therm_dev->polling_delay = IDLE_INTERVAL; ++ else ++ th_zone->therm_dev->polling_delay = 0; ++ ++ mutex_unlock(&th_zone->therm_dev->lock); ++ ++ th_zone->mode = mode; ++ thermal_zone_device_update(th_zone->therm_dev); ++ pr_info("thermal polling set for duration=%d msec\n", ++ th_zone->therm_dev->polling_delay); ++ return 0; ++} ++ ++ ++/* Get trip type callback functions for thermal zone */ ++static int exynos_get_trip_type(struct thermal_zone_device *thermal, int trip, ++ enum thermal_trip_type *type) ++{ ++ switch (GET_ZONE(trip)) { ++ case MONITOR_ZONE: ++ case WARN_ZONE: ++ *type = THERMAL_TRIP_ACTIVE; ++ break; ++ case PANIC_ZONE: ++ *type = THERMAL_TRIP_CRITICAL; ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++/* Get trip temperature callback functions for thermal zone */ ++static int exynos_get_trip_temp(struct thermal_zone_device *thermal, int trip, ++ unsigned long *temp) ++{ ++ if (trip < GET_TRIP(MONITOR_ZONE) || trip > GET_TRIP(PANIC_ZONE)) ++ return -EINVAL; ++ ++ *temp = th_zone->sensor_conf->trip_data.trip_val[trip]; ++ /* convert the temperature into millicelsius */ ++ *temp = *temp * MCELSIUS; ++ ++ return 0; ++} ++ ++/* Get critical temperature callback functions for thermal zone */ ++static int exynos_get_crit_temp(struct thermal_zone_device *thermal, ++ unsigned long *temp) ++{ ++ int ret; ++ /* Panic zone */ ++ ret = exynos_get_trip_temp(thermal, GET_TRIP(PANIC_ZONE), temp); ++ return ret; ++} ++ ++/* Bind callback functions for thermal zone */ ++static int exynos_bind(struct thermal_zone_device *thermal, ++ struct thermal_cooling_device *cdev) ++{ ++ int ret = 0, i, tab_size, level; ++ struct freq_clip_table *tab_ptr, *clip_data; ++ struct thermal_sensor_conf *data = th_zone->sensor_conf; ++ ++ tab_ptr = (struct freq_clip_table *)data->cooling_data.freq_data; ++ tab_size = data->cooling_data.freq_clip_count; ++ ++ if (tab_ptr == NULL || tab_size == 0) ++ return -EINVAL; ++ ++ /* find the cooling device registered*/ ++ for (i = 0; i < th_zone->cool_dev_size; i++) ++ if (cdev == th_zone->cool_dev[i]) ++ break; ++ ++ /* No matching cooling device */ ++ if (i == th_zone->cool_dev_size) ++ return 0; ++ ++ /* Bind the thermal zone to the cpufreq cooling device */ ++ for (i = 0; i < tab_size; i++) { ++ clip_data = (struct freq_clip_table *)&(tab_ptr[i]); ++ level = cpufreq_cooling_get_level(0, clip_data->freq_clip_max); ++ if (level == THERMAL_CSTATE_INVALID) ++ return 0; ++ switch (GET_ZONE(i)) { ++ case MONITOR_ZONE: ++ case WARN_ZONE: ++ if (thermal_zone_bind_cooling_device(thermal, i, cdev, ++ level, 0)) { ++ pr_err("error binding cdev inst %d\n", i); ++ ret = -EINVAL; ++ } ++ th_zone->bind = true; ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ } ++ ++ return ret; ++} ++ ++/* Unbind callback functions for thermal zone */ ++static int exynos_unbind(struct thermal_zone_device *thermal, ++ struct thermal_cooling_device *cdev) ++{ ++ int ret = 0, i, tab_size; ++ struct thermal_sensor_conf *data = th_zone->sensor_conf; ++ ++ if (th_zone->bind == false) ++ return 0; ++ ++ tab_size = data->cooling_data.freq_clip_count; ++ ++ if (tab_size == 0) ++ return -EINVAL; ++ ++ /* find the cooling device registered*/ ++ for (i = 0; i < th_zone->cool_dev_size; i++) ++ if (cdev == th_zone->cool_dev[i]) ++ break; ++ ++ /* No matching cooling device */ ++ if (i == th_zone->cool_dev_size) ++ return 0; ++ ++ /* Bind the thermal zone to the cpufreq cooling device */ ++ for (i = 0; i < tab_size; i++) { ++ switch (GET_ZONE(i)) { ++ case MONITOR_ZONE: ++ case WARN_ZONE: ++ if (thermal_zone_unbind_cooling_device(thermal, i, ++ cdev)) { ++ pr_err("error unbinding cdev inst=%d\n", i); ++ ret = -EINVAL; ++ } ++ th_zone->bind = false; ++ break; ++ default: ++ ret = -EINVAL; ++ } ++ } ++ return ret; ++} ++ ++/* Get temperature callback functions for thermal zone */ ++static int exynos_get_temp(struct thermal_zone_device *thermal, ++ unsigned long *temp) ++{ ++ 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 * MCELSIUS; ++ return 0; ++} ++ ++/* Get temperature callback functions for thermal zone */ ++static int exynos_set_emul_temp(struct thermal_zone_device *thermal, ++ unsigned long temp) ++{ ++ void *data; ++ int ret = -EINVAL; ++ ++ if (!th_zone->sensor_conf) { ++ pr_info("Temperature sensor not initialised\n"); ++ return -EINVAL; ++ } ++ data = th_zone->sensor_conf->private_data; ++ if (th_zone->sensor_conf->write_emul_temp) ++ ret = th_zone->sensor_conf->write_emul_temp(data, temp); ++ return ret; ++} ++ ++/* Get the temperature trend */ ++static int exynos_get_trend(struct thermal_zone_device *thermal, ++ int trip, enum thermal_trend *trend) ++{ ++ int ret; ++ unsigned long trip_temp; ++ ++ ret = exynos_get_trip_temp(thermal, trip, &trip_temp); ++ if (ret < 0) ++ return ret; ++ ++ if (thermal->temperature >= trip_temp) ++ *trend = THERMAL_TREND_RAISE_FULL; ++ else ++ *trend = THERMAL_TREND_DROP_FULL; ++ ++ return 0; ++} ++/* Operation callback functions for thermal zone */ ++static struct thermal_zone_device_ops const exynos_dev_ops = { ++ .bind = exynos_bind, ++ .unbind = exynos_unbind, ++ .get_temp = exynos_get_temp, ++ .set_emul_temp = exynos_set_emul_temp, ++ .get_trend = exynos_get_trend, ++ .get_mode = exynos_get_mode, ++ .set_mode = exynos_set_mode, ++ .get_trip_type = exynos_get_trip_type, ++ .get_trip_temp = exynos_get_trip_temp, ++ .get_crit_temp = exynos_get_crit_temp, ++}; ++ ++/* ++ * This function may be called from interrupt based temperature sensor ++ * when threshold is changed. ++ */ ++void exynos_report_trigger(void) ++{ ++ unsigned int i; ++ char data[10]; ++ char *envp[] = { data, NULL }; ++ ++ if (!th_zone || !th_zone->therm_dev) ++ return; ++ if (th_zone->bind == false) { ++ for (i = 0; i < th_zone->cool_dev_size; i++) { ++ if (!th_zone->cool_dev[i]) ++ continue; ++ exynos_bind(th_zone->therm_dev, ++ th_zone->cool_dev[i]); ++ } ++ } ++ ++ thermal_zone_device_update(th_zone->therm_dev); ++ ++ mutex_lock(&th_zone->therm_dev->lock); ++ /* Find the level for which trip happened */ ++ for (i = 0; i < th_zone->sensor_conf->trip_data.trip_count; i++) { ++ if (th_zone->therm_dev->last_temperature < ++ th_zone->sensor_conf->trip_data.trip_val[i] * MCELSIUS) ++ break; ++ } ++ ++ if (th_zone->mode == THERMAL_DEVICE_ENABLED && ++ !th_zone->sensor_conf->trip_data.trigger_falling) { ++ if (i > 0) ++ th_zone->therm_dev->polling_delay = ACTIVE_INTERVAL; ++ else ++ th_zone->therm_dev->polling_delay = IDLE_INTERVAL; ++ } ++ ++ snprintf(data, sizeof(data), "%u", i); ++ kobject_uevent_env(&th_zone->therm_dev->device.kobj, KOBJ_CHANGE, envp); ++ mutex_unlock(&th_zone->therm_dev->lock); ++} ++ ++/* Register with the in-kernel thermal management */ ++int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf) ++{ ++ int ret; ++ struct cpumask mask_val; ++ ++ if (!sensor_conf || !sensor_conf->read_temperature) { ++ pr_err("Temperature sensor not initialised\n"); ++ return -EINVAL; ++ } ++ ++ th_zone = kzalloc(sizeof(struct exynos_thermal_zone), GFP_KERNEL); ++ if (!th_zone) ++ return -ENOMEM; ++ ++ th_zone->sensor_conf = sensor_conf; ++ cpumask_set_cpu(0, &mask_val); ++ th_zone->cool_dev[0] = cpufreq_cooling_register(&mask_val); ++ if (IS_ERR(th_zone->cool_dev[0])) { ++ pr_err("Failed to register cpufreq cooling device\n"); ++ ret = -EINVAL; ++ goto err_unregister; ++ } ++ th_zone->cool_dev_size++; ++ ++ th_zone->therm_dev = thermal_zone_device_register(sensor_conf->name, ++ EXYNOS_ZONE_COUNT, 0, NULL, &exynos_dev_ops, NULL, 0, ++ sensor_conf->trip_data.trigger_falling ? ++ 0 : IDLE_INTERVAL); ++ ++ if (IS_ERR(th_zone->therm_dev)) { ++ pr_err("Failed to register thermal zone device\n"); ++ ret = PTR_ERR(th_zone->therm_dev); ++ goto err_unregister; ++ } ++ th_zone->mode = THERMAL_DEVICE_ENABLED; ++ ++ pr_info("Exynos: Kernel Thermal management registered\n"); ++ ++ return 0; ++ ++err_unregister: ++ exynos_unregister_thermal(); ++ return ret; ++} ++ ++/* Un-Register with the in-kernel thermal management */ ++void exynos_unregister_thermal(void) ++{ ++ int i; ++ ++ if (!th_zone) ++ return; ++ ++ if (th_zone->therm_dev) ++ thermal_zone_device_unregister(th_zone->therm_dev); ++ ++ for (i = 0; i < th_zone->cool_dev_size; i++) { ++ if (th_zone->cool_dev[i]) ++ cpufreq_cooling_unregister(th_zone->cool_dev[i]); ++ } ++ ++ kfree(th_zone); ++ pr_info("Exynos: Kernel Thermal management unregistered\n"); ++} +diff --git a/drivers/thermal/samsung/exynos_thermal_common.h b/drivers/thermal/samsung/exynos_thermal_common.h +new file mode 100644 +index 0000000..8df1848 +--- /dev/null ++++ b/drivers/thermal/samsung/exynos_thermal_common.h +@@ -0,0 +1,83 @@ ++/* ++ * exynos_thermal_common.h - Samsung EXYNOS common header file ++ * ++ * Copyright (C) 2013 Samsung Electronics ++ * Amit Daniel Kachhap <amit.daniel@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 ++ * ++ */ ++ ++#ifndef _EXYNOS_THERMAL_COMMON_H ++#define _EXYNOS_THERMAL_COMMON_H ++ ++/* In-kernel thermal framework related macros & definations */ ++#define SENSOR_NAME_LEN 16 ++#define MAX_TRIP_COUNT 8 ++#define MAX_COOLING_DEVICE 4 ++#define MAX_THRESHOLD_LEVS 4 ++ ++#define ACTIVE_INTERVAL 500 ++#define IDLE_INTERVAL 10000 ++#define MCELSIUS 1000 ++ ++/* CPU Zone information */ ++#define PANIC_ZONE 4 ++#define WARN_ZONE 3 ++#define MONITOR_ZONE 2 ++#define SAFE_ZONE 1 ++ ++#define GET_ZONE(trip) (trip + 2) ++#define GET_TRIP(zone) (zone - 2) ++ ++#define EXYNOS_ZONE_COUNT 3 ++ ++struct thermal_trip_point_conf { ++ int trip_val[MAX_TRIP_COUNT]; ++ int trip_count; ++ unsigned char trigger_falling; ++}; ++ ++struct thermal_cooling_conf { ++ struct freq_clip_table freq_data[MAX_TRIP_COUNT]; ++ int freq_clip_count; ++}; ++ ++struct thermal_sensor_conf { ++ char name[SENSOR_NAME_LEN]; ++ int (*read_temperature)(void *data); ++ int (*write_emul_temp)(void *drv_data, unsigned long temp); ++ struct thermal_trip_point_conf trip_data; ++ struct thermal_cooling_conf cooling_data; ++ void *private_data; ++}; ++ ++/*Functions used exynos based thermal sensor driver*/ ++#ifdef CONFIG_EXYNOS_THERMAL_CORE ++void exynos_unregister_thermal(void); ++int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf); ++void exynos_report_trigger(void); ++#else ++static inline void ++exynos_unregister_thermal(void) { return; } ++ ++static inline int ++exynos_register_thermal(struct thermal_sensor_conf *sensor_conf) { return 0; } ++ ++static inline void ++exynos_report_trigger(void) { return; } ++ ++#endif /* CONFIG_EXYNOS_THERMAL_CORE */ ++#endif /* _EXYNOS_THERMAL_COMMON_H */ +-- +1.8.3.2 + |