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....@samsung.com>
> Acked-by: Jonghwa Lee <jonghwa3....@samsung.com>
> Signed-off-by: Amit Daniel Kachhap <amit.dan...@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.dan...@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.dan...@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

Attachment: signature.asc
Description: OpenPGP digital signature

Reply via email to