Message ID | 1371451599-31035-5-git-send-email-amit.daniel@samsung.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Amit, On 17-06-2013 02:46, 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@samsung.com> > Acked-by: Jonghwa Lee <jonghwa3.lee@samsung.com> > Signed-off-by: Amit Daniel Kachhap <amit.daniel@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 | 384 +++++++++++++++++++++ > drivers/thermal/samsung/exynos_thermal_common.h | 83 +++++ > 5 files changed, 490 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. Should this one depend on CPU_THERMAL? Is it mandatory? > 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 03e4bbc..5293849 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..92e50bc > --- /dev/null > +++ b/drivers/thermal/samsung/exynos_thermal_common.c > @@ -0,0 +1,384 @@ > +/* > + * 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/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 */ >
On 17-06-2013 02:46, 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@samsung.com> > Acked-by: Jonghwa Lee <jonghwa3.lee@samsung.com> > Signed-off-by: Amit Daniel Kachhap <amit.daniel@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 | 384 +++++++++++++++++++++ > drivers/thermal/samsung/exynos_thermal_common.h | 83 +++++ > 5 files changed, 490 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 This is not really on this patch, but I think this one needs to depend on CONFIG_OF too. > 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 03e4bbc..5293849 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..92e50bc > --- /dev/null > +++ b/drivers/thermal/samsung/exynos_thermal_common.c > @@ -0,0 +1,384 @@ > +/* > + * 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/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 */ >
On Thu, Jun 20, 2013 at 12:15 AM, Eduardo Valentin <eduardo.valentin@ti.com> wrote: > Amit, > > On 17-06-2013 02:46, 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@samsung.com> >> Acked-by: Jonghwa Lee <jonghwa3.lee@samsung.com> >> Signed-off-by: Amit Daniel Kachhap <amit.daniel@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 | 384 +++++++++++++++++++++ >> drivers/thermal/samsung/exynos_thermal_common.h | 83 +++++ >> 5 files changed, 490 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. > Should this one depend on CPU_THERMAL? Is it mandatory? Hi Eduardo, No it is not mandatory. If CPU_THERMAL is not present then cooling device is not created and only temp/trip_points etc sysfs are created. Thanks, Amit Daniel > >> 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 03e4bbc..5293849 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..92e50bc >> --- /dev/null >> +++ b/drivers/thermal/samsung/exynos_thermal_common.c >> @@ -0,0 +1,384 @@ >> +/* >> + * 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/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 */ >> > > > -- > You have got to be excited about what you are doing. (L. Lamport) > > Eduardo Valentin > -- To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Thu, Jun 20, 2013 at 12:31 AM, Eduardo Valentin <eduardo.valentin@ti.com> wrote: > On 17-06-2013 02:46, 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@samsung.com> >> Acked-by: Jonghwa Lee <jonghwa3.lee@samsung.com> >> Signed-off-by: Amit Daniel Kachhap <amit.daniel@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 | 384 +++++++++++++++++++++ >> drivers/thermal/samsung/exynos_thermal_common.h | 83 +++++ >> 5 files changed, 490 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 > > This is not really on this patch, but I think this one needs to depend > on CONFIG_OF too. In the driver CONFIG_OF is used for DT api's so this is not needed here. > >> 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 03e4bbc..5293849 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..92e50bc >> --- /dev/null >> +++ b/drivers/thermal/samsung/exynos_thermal_common.c >> @@ -0,0 +1,384 @@ >> +/* >> + * 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/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 */ >> > > > -- > You have got to be excited about what you are doing. (L. Lamport) > > Eduardo Valentin > -- To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On 17-06-2013 02:46, 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@samsung.com> > Acked-by: Jonghwa Lee <jonghwa3.lee@samsung.com> > Signed-off-by: Amit Daniel Kachhap <amit.daniel@samsung.com> Acked-by: Eduardo Valentin <eduardo.valentin@ti.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 | 384 +++++++++++++++++++++ > drivers/thermal/samsung/exynos_thermal_common.h | 83 +++++ > 5 files changed, 490 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 03e4bbc..5293849 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..92e50bc > --- /dev/null > +++ b/drivers/thermal/samsung/exynos_thermal_common.c > @@ -0,0 +1,384 @@ > +/* > + * 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/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 */ >
On 20-06-2013 21:50, amit daniel kachhap wrote: > On Thu, Jun 20, 2013 at 12:15 AM, Eduardo Valentin > <eduardo.valentin@ti.com> wrote: >> Amit, >> >> On 17-06-2013 02:46, 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@samsung.com> >>> Acked-by: Jonghwa Lee <jonghwa3.lee@samsung.com> >>> Signed-off-by: Amit Daniel Kachhap <amit.daniel@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 | 384 +++++++++++++++++++++ >>> drivers/thermal/samsung/exynos_thermal_common.h | 83 +++++ >>> 5 files changed, 490 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. >> Should this one depend on CPU_THERMAL? Is it mandatory? > Hi Eduardo, > > No it is not mandatory. If CPU_THERMAL is not present then cooling > device is not created and only temp/trip_points etc sysfs are created. OK. > > Thanks, > Amit Daniel >> >>> 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 03e4bbc..5293849 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..92e50bc >>> --- /dev/null >>> +++ b/drivers/thermal/samsung/exynos_thermal_common.c >>> @@ -0,0 +1,384 @@ >>> +/* >>> + * 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/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 */ >>> >> >> >> -- >> You have got to be excited about what you are doing. (L. Lamport) >> >> Eduardo Valentin >> > >
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 03e4bbc..5293849 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..92e50bc --- /dev/null +++ b/drivers/thermal/samsung/exynos_thermal_common.c @@ -0,0 +1,384 @@ +/* + * 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/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 */