Re: [PATCH V6 04/30] thermal: exynos: Bifurcate exynos thermal commonand tmu controller code

From: Eduardo Valentin
Date: Fri Jun 21 2013 - 10:30:39 EST


On 20-06-2013 21:50, amit daniel kachhap wrote:
> On Thu, Jun 20, 2013 at 12:15 AM, Eduardo Valentin
> <eduardo.valentin@xxxxxx> 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@xxxxxxxxxxx>
>>> Acked-by: Jonghwa Lee <jonghwa3.lee@xxxxxxxxxxx>
>>> Signed-off-by: Amit Daniel Kachhap <amit.daniel@xxxxxxxxxxx>
>>> ---
>>> 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@xxxxxxxxxxx>
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify
>>> + * it under the terms of the GNU General Public License as published by
>>> + * the Free Software Foundation; either version 2 of the License, or
>>> + * (at your option) any later version.
>>> + *
>>> + * This program is distributed in the hope that it will be useful,
>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>>> + * GNU General Public License for more details.
>>> + *
>>> + * You should have received a copy of the GNU General Public License
>>> + * along with this program; if not, write to the Free Software
>>> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
>>> + *
>>> + */
>>> +
>>> +#include <linux/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@xxxxxxxxxxx>
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify
>>> + * it under the terms of the GNU General Public License as published by
>>> + * the Free Software Foundation; either version 2 of the License, or
>>> + * (at your option) any later version.
>>> + *
>>> + * This program is distributed in the hope that it will be useful,
>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>>> + * GNU General Public License for more details.
>>> + *
>>> + * You should have received a copy of the GNU General Public License
>>> + * along with this program; if not, write to the Free Software
>>> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
>>> + *
>>> + */
>>> +
>>> +#ifndef _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
>>
>
>


--
You have got to be excited about what you are doing. (L. Lamport)

Eduardo Valentin

Attachment: signature.asc
Description: OpenPGP digital signature