On Fri, 2013-04-26 at 16:07 +0530, Amit Daniel Kachhap wrote: > 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@xxxxxxxxxxx> > Signed-off-by: Amit Daniel Kachhap <amit.daniel@xxxxxxxxxxx> > --- > drivers/thermal/samsung/Kconfig | 26 +- > drivers/thermal/samsung/Makefile | 4 +- > drivers/thermal/samsung/exynos_thermal.c | 421 +---------------------- > drivers/thermal/samsung/exynos_thermal_common.c | 389 +++++++++++++++++++++ > drivers/thermal/samsung/exynos_thermal_common.h | 71 ++++ > 5 files changed, 491 insertions(+), 420 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 2d3d9dc..1e3ba31 100644 > --- a/drivers/thermal/samsung/Kconfig > +++ b/drivers/thermal/samsung/Kconfig > @@ -1,9 +1,21 @@ > -config EXYNOS_THERMAL > - tristate "Temperature sensor on Samsung EXYNOS" > +config EXYNOS_COMMON EXYNOS_COMMON is misleading here, better use EXYNOS_THERMAL_COMMON? > + bool "Common thermal support for EXYNOS SOC's" > depends on (ARCH_EXYNOS4 || ARCH_EXYNOS5) > - depends on CPU_THERMAL > help > - If you say yes here you get support for TMU (Thermal Management > - Unit) on SAMSUNG EXYNOS series of SoC. This helps in registering > - the exynos thermal driver with the core thermal layer and cpu > - cooling API's. > + If you say yes here you get support for EXYNOS TMU > + (Thermal Management Unit) common registration/unregistration > + functions to the core thermal layer and also to use the generic > + cpu cooling API's. > + > +if EXYNOS_COMMON > + > +config EXYNOS_SOC_THERMAL > + tristate "Temperature sensor on Samsung EXYNOS series SOC" > + depends on (CPU_EXYNOS4210 || SOC_EXYNOS4212 || SOC_EXYNOS4412 || SOC_EXYNOS5250) > + help > + If you say yes here you can enable TMU (Thermal Management Unit) on > + SAMSUNG EXYNOS 4210, 4412, 4414 and 5250 series of SoC. This option > + initialises the TMU controller and registers/unregisters with exynos > + common thermal layer. > + > +endif > diff --git a/drivers/thermal/samsung/Makefile b/drivers/thermal/samsung/Makefile > index 1fe6d93..fcda5b4 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_SOC_THERMAL) += exynos_soc_thermal.o > +exynos_soc_thermal-y := exynos_thermal.o > +exynos_soc_thermal-$(CONFIG_EXYNOS_COMMON) += exynos_thermal_common.o the Makefile suggests that exynos_soc_thermal driver could be built without CONFIG_EXYNOS_COMMON. But the Kconfig file shows that exynos_soc_thermal depends on CONFIG_EXYNOS_COMMON. If exynos_thermal_common.c contains the code to interact with the generic thermal layer and you do not want a soc driver without thermal support, just remove CONFIG_EXYNOS_COMMON and always build it into exynos_soc_thermal.o. what do you think? thanks, rui > diff --git a/drivers/thermal/samsung/exynos_thermal.c b/drivers/thermal/samsung/exynos_thermal.c > index d20ce9e..4c85945 100644 > --- a/drivers/thermal/samsung/exynos_thermal.c > +++ b/drivers/thermal/samsung/exynos_thermal.c > @@ -21,23 +21,19 @@ > * > */ > > -#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/interrupt.h> > +#include <linux/kernel.h> > +#include <linux/kobject.h> > +#include <linux/module.h> > #include <linux/mutex.h> > -#include <linux/platform_data/exynos_thermal.h> > -#include <linux/thermal.h> > -#include <linux/cpufreq.h> > -#include <linux/cpu_cooling.h> > #include <linux/of.h> > +#include <linux/platform_device.h> > +#include <linux/platform_data/exynos_thermal.h> > +#include <linux/slab.h> > +#include <linux/workqueue.h> > +#include "exynos_thermal_common.h" > > /* Exynos generic registers */ > #define EXYNOS_TMU_REG_TRIMINFO 0x0 > @@ -88,16 +84,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 +92,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 +104,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..9a57606 > --- /dev/null > +++ b/drivers/thermal/samsung/exynos_thermal_common.c > @@ -0,0 +1,389 @@ > +/* > + * exynos_thermal_common.c - Samsung EXYNOS common thermal file > + * > + * Copyright (C) 2013 Samsung Electronics > + * Amit Daniel Kachhap <amit.daniel@xxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA > + * > + */ > + > +#include <linux/cpufreq.h> > +#include <linux/cpu_cooling.h> > +#include <linux/err.h> > +#include <linux/io.h> > +#include <linux/kernel.h> > +#include <linux/kobject.h> > +#include <linux/mutex.h> > +#include <linux/platform_data/exynos_thermal.h> > +#include <linux/slab.h> > +#include <linux/thermal.h> > +#include "exynos_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..165401f > --- /dev/null > +++ b/drivers/thermal/samsung/exynos_thermal_common.h > @@ -0,0 +1,71 @@ > +/* > + * exynos_thermal_common.h - Samsung EXYNOS common header file > + * > + * Copyright (C) 2013 Samsung Electronics > + * Amit Daniel Kachhap <amit.daniel@xxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA > + * > + */ > + > +#ifndef _LINUX_EXYNOS_THERMAL_COMMON_H > +#define _LINUX_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; > + 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; > +}; > + > +/*Functions used exynos based thermal sensor driver*/ > +void exynos_unregister_thermal(void); > +int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf); > +void exynos_report_trigger(void); > +#endif /* _LINUX_EXYNOS_THERMAL_COMMON_H */ -- To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html