Re: [PATCH V2 02/20] thermal: exynos: Bifurcate exynos thermal common and tmu controller code

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, 2013-04-26 at 16:07 +0530, Amit Daniel Kachhap wrote:
> This code bifurcates exynos thermal implementation into common and sensor
> specific parts. The common thermal code interacts with core thermal layer and
> core cpufreq cooling parts and is independent of SOC specific driver. This
> change is needed to cleanly add support for new TMU sensors.
> 
> Acked-by: Kukjin Kim <kgene.kim@xxxxxxxxxxx>
> Signed-off-by: Amit Daniel Kachhap <amit.daniel@xxxxxxxxxxx>
> ---
>  drivers/thermal/samsung/Kconfig                 |   26 +-
>  drivers/thermal/samsung/Makefile                |    4 +-
>  drivers/thermal/samsung/exynos_thermal.c        |  421 +----------------------
>  drivers/thermal/samsung/exynos_thermal_common.c |  389 +++++++++++++++++++++
>  drivers/thermal/samsung/exynos_thermal_common.h |   71 ++++
>  5 files changed, 491 insertions(+), 420 deletions(-)
>  create mode 100644 drivers/thermal/samsung/exynos_thermal_common.c
>  create mode 100644 drivers/thermal/samsung/exynos_thermal_common.h
> 
> diff --git a/drivers/thermal/samsung/Kconfig b/drivers/thermal/samsung/Kconfig
> index 2d3d9dc..1e3ba31 100644
> --- a/drivers/thermal/samsung/Kconfig
> +++ b/drivers/thermal/samsung/Kconfig
> @@ -1,9 +1,21 @@
> -config EXYNOS_THERMAL
> -	tristate "Temperature sensor on Samsung EXYNOS"
> +config EXYNOS_COMMON

EXYNOS_COMMON is misleading here, better use EXYNOS_THERMAL_COMMON?

> +	bool "Common thermal support for EXYNOS SOC's"
>  	depends on (ARCH_EXYNOS4 || ARCH_EXYNOS5)
> -	depends on CPU_THERMAL
>  	help
> -	  If you say yes here you get support for TMU (Thermal Management
> -	  Unit) on SAMSUNG EXYNOS series of SoC. This helps in registering
> -	  the exynos thermal driver with the core thermal layer and cpu
> -	  cooling API's.
> +	  If you say yes here you get support for EXYNOS TMU
> +	  (Thermal Management Unit) common registration/unregistration
> +	  functions to the core thermal layer and also to use the generic
> +	  cpu cooling API's.
> +
> +if EXYNOS_COMMON
> +
> +config EXYNOS_SOC_THERMAL
> +	tristate "Temperature sensor on Samsung EXYNOS series SOC"
> +	depends on (CPU_EXYNOS4210 || SOC_EXYNOS4212 || SOC_EXYNOS4412 || SOC_EXYNOS5250)
> + 	help
> +	  If you say yes here you can enable TMU (Thermal Management Unit) on
> +	  SAMSUNG EXYNOS 4210, 4412, 4414 and 5250 series of SoC. This option
> +	  initialises the TMU controller and registers/unregisters with exynos
> +	  common thermal layer.
> +
> +endif
> diff --git a/drivers/thermal/samsung/Makefile b/drivers/thermal/samsung/Makefile
> index 1fe6d93..fcda5b4 100644
> --- a/drivers/thermal/samsung/Makefile
> +++ b/drivers/thermal/samsung/Makefile
> @@ -1,4 +1,6 @@
>  #
>  # Samsung thermal specific Makefile
>  #
> -obj-$(CONFIG_EXYNOS_THERMAL)	+= exynos_thermal.o
> +obj-$(CONFIG_EXYNOS_SOC_THERMAL)		+= exynos_soc_thermal.o
> +exynos_soc_thermal-y				:= exynos_thermal.o
> +exynos_soc_thermal-$(CONFIG_EXYNOS_COMMON)	+= exynos_thermal_common.o

the Makefile suggests that exynos_soc_thermal driver could be built
without CONFIG_EXYNOS_COMMON.
But the Kconfig file shows that exynos_soc_thermal depends on
CONFIG_EXYNOS_COMMON.
If exynos_thermal_common.c contains the code to interact with the
generic thermal layer and you do not want a soc driver without thermal
support, just remove CONFIG_EXYNOS_COMMON and always build it into
exynos_soc_thermal.o.
what do you think?
 
thanks,
rui
> diff --git a/drivers/thermal/samsung/exynos_thermal.c b/drivers/thermal/samsung/exynos_thermal.c
> index d20ce9e..4c85945 100644
> --- a/drivers/thermal/samsung/exynos_thermal.c
> +++ b/drivers/thermal/samsung/exynos_thermal.c
> @@ -21,23 +21,19 @@
>   *
>   */
>  
> -#include <linux/module.h>
> -#include <linux/err.h>
> -#include <linux/kernel.h>
> -#include <linux/slab.h>
> -#include <linux/platform_device.h>
> -#include <linux/interrupt.h>
>  #include <linux/clk.h>
> -#include <linux/workqueue.h>
> -#include <linux/sysfs.h>
> -#include <linux/kobject.h>
>  #include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/kernel.h>
> +#include <linux/kobject.h>
> +#include <linux/module.h>
>  #include <linux/mutex.h>
> -#include <linux/platform_data/exynos_thermal.h>
> -#include <linux/thermal.h>
> -#include <linux/cpufreq.h>
> -#include <linux/cpu_cooling.h>
>  #include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/platform_data/exynos_thermal.h>
> +#include <linux/slab.h>
> +#include <linux/workqueue.h>
> +#include "exynos_thermal_common.h"
>  
>  /* Exynos generic registers */
>  #define EXYNOS_TMU_REG_TRIMINFO		0x0
> @@ -88,16 +84,6 @@
>  #define EFUSE_MIN_VALUE 40
>  #define EFUSE_MAX_VALUE 100
>  
> -/* In-kernel thermal framework related macros & definations */
> -#define SENSOR_NAME_LEN	16
> -#define MAX_TRIP_COUNT	8
> -#define MAX_COOLING_DEVICE 4
> -#define MAX_THRESHOLD_LEVS 4
> -
> -#define ACTIVE_INTERVAL 500
> -#define IDLE_INTERVAL 10000
> -#define MCELSIUS	1000
> -
>  #ifdef CONFIG_THERMAL_EMULATION
>  #define EXYNOS_EMUL_TIME	0x57F0
>  #define EXYNOS_EMUL_TIME_SHIFT	16
> @@ -106,17 +92,6 @@
>  #define EXYNOS_EMUL_ENABLE	0x1
>  #endif /* CONFIG_THERMAL_EMULATION */
>  
> -/* CPU Zone information */
> -#define PANIC_ZONE      4
> -#define WARN_ZONE       3
> -#define MONITOR_ZONE    2
> -#define SAFE_ZONE       1
> -
> -#define GET_ZONE(trip) (trip + 2)
> -#define GET_TRIP(zone) (zone - 2)
> -
> -#define EXYNOS_ZONE_COUNT	3
> -
>  struct exynos_tmu_data {
>  	struct exynos_tmu_platform_data *pdata;
>  	struct resource *mem;
> @@ -129,384 +104,6 @@ struct exynos_tmu_data {
>  	u8 temp_error1, temp_error2;
>  };
>  
> -struct	thermal_trip_point_conf {
> -	int trip_val[MAX_TRIP_COUNT];
> -	int trip_count;
> -	u8 trigger_falling;
> -};
> -
> -struct	thermal_cooling_conf {
> -	struct freq_clip_table freq_data[MAX_TRIP_COUNT];
> -	int freq_clip_count;
> -};
> -
> -struct thermal_sensor_conf {
> -	char name[SENSOR_NAME_LEN];
> -	int (*read_temperature)(void *data);
> -	int (*write_emul_temp)(void *drv_data, unsigned long temp);
> -	struct thermal_trip_point_conf trip_data;
> -	struct thermal_cooling_conf cooling_data;
> -	void *private_data;
> -};
> -
> -struct exynos_thermal_zone {
> -	enum thermal_device_mode mode;
> -	struct thermal_zone_device *therm_dev;
> -	struct thermal_cooling_device *cool_dev[MAX_COOLING_DEVICE];
> -	unsigned int cool_dev_size;
> -	struct platform_device *exynos4_dev;
> -	struct thermal_sensor_conf *sensor_conf;
> -	bool bind;
> -};
> -
> -static struct exynos_thermal_zone *th_zone;
> -static void exynos_unregister_thermal(void);
> -static int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf);
> -
> -/* Get mode callback functions for thermal zone */
> -static int exynos_get_mode(struct thermal_zone_device *thermal,
> -			enum thermal_device_mode *mode)
> -{
> -	if (th_zone)
> -		*mode = th_zone->mode;
> -	return 0;
> -}
> -
> -/* Set mode callback functions for thermal zone */
> -static int exynos_set_mode(struct thermal_zone_device *thermal,
> -			enum thermal_device_mode mode)
> -{
> -	if (!th_zone->therm_dev) {
> -		pr_notice("thermal zone not registered\n");
> -		return 0;
> -	}
> -
> -	mutex_lock(&th_zone->therm_dev->lock);
> -
> -	if (mode == THERMAL_DEVICE_ENABLED &&
> -		!th_zone->sensor_conf->trip_data.trigger_falling)
> -		th_zone->therm_dev->polling_delay = IDLE_INTERVAL;
> -	else
> -		th_zone->therm_dev->polling_delay = 0;
> -
> -	mutex_unlock(&th_zone->therm_dev->lock);
> -
> -	th_zone->mode = mode;
> -	thermal_zone_device_update(th_zone->therm_dev);
> -	pr_info("thermal polling set for duration=%d msec\n",
> -				th_zone->therm_dev->polling_delay);
> -	return 0;
> -}
> -
> -
> -/* Get trip type callback functions for thermal zone */
> -static int exynos_get_trip_type(struct thermal_zone_device *thermal, int trip,
> -				 enum thermal_trip_type *type)
> -{
> -	switch (GET_ZONE(trip)) {
> -	case MONITOR_ZONE:
> -	case WARN_ZONE:
> -		*type = THERMAL_TRIP_ACTIVE;
> -		break;
> -	case PANIC_ZONE:
> -		*type = THERMAL_TRIP_CRITICAL;
> -		break;
> -	default:
> -		return -EINVAL;
> -	}
> -	return 0;
> -}
> -
> -/* Get trip temperature callback functions for thermal zone */
> -static int exynos_get_trip_temp(struct thermal_zone_device *thermal, int trip,
> -				unsigned long *temp)
> -{
> -	if (trip < GET_TRIP(MONITOR_ZONE) || trip > GET_TRIP(PANIC_ZONE))
> -		return -EINVAL;
> -
> -	*temp = th_zone->sensor_conf->trip_data.trip_val[trip];
> -	/* convert the temperature into millicelsius */
> -	*temp = *temp * MCELSIUS;
> -
> -	return 0;
> -}
> -
> -/* Get critical temperature callback functions for thermal zone */
> -static int exynos_get_crit_temp(struct thermal_zone_device *thermal,
> -				unsigned long *temp)
> -{
> -	int ret;
> -	/* Panic zone */
> -	ret = exynos_get_trip_temp(thermal, GET_TRIP(PANIC_ZONE), temp);
> -	return ret;
> -}
> -
> -/* Bind callback functions for thermal zone */
> -static int exynos_bind(struct thermal_zone_device *thermal,
> -			struct thermal_cooling_device *cdev)
> -{
> -	int ret = 0, i, tab_size, level;
> -	struct freq_clip_table *tab_ptr, *clip_data;
> -	struct thermal_sensor_conf *data = th_zone->sensor_conf;
> -
> -	tab_ptr = (struct freq_clip_table *)data->cooling_data.freq_data;
> -	tab_size = data->cooling_data.freq_clip_count;
> -
> -	if (tab_ptr == NULL || tab_size == 0)
> -		return -EINVAL;
> -
> -	/* find the cooling device registered*/
> -	for (i = 0; i < th_zone->cool_dev_size; i++)
> -		if (cdev == th_zone->cool_dev[i])
> -			break;
> -
> -	/* No matching cooling device */
> -	if (i == th_zone->cool_dev_size)
> -		return 0;
> -
> -	/* Bind the thermal zone to the cpufreq cooling device */
> -	for (i = 0; i < tab_size; i++) {
> -		clip_data = (struct freq_clip_table *)&(tab_ptr[i]);
> -		level = cpufreq_cooling_get_level(0, clip_data->freq_clip_max);
> -		if (level == THERMAL_CSTATE_INVALID)
> -			return 0;
> -		switch (GET_ZONE(i)) {
> -		case MONITOR_ZONE:
> -		case WARN_ZONE:
> -			if (thermal_zone_bind_cooling_device(thermal, i, cdev,
> -								level, 0)) {
> -				pr_err("error binding cdev inst %d\n", i);
> -				ret = -EINVAL;
> -			}
> -			th_zone->bind = true;
> -			break;
> -		default:
> -			ret = -EINVAL;
> -		}
> -	}
> -
> -	return ret;
> -}
> -
> -/* Unbind callback functions for thermal zone */
> -static int exynos_unbind(struct thermal_zone_device *thermal,
> -			struct thermal_cooling_device *cdev)
> -{
> -	int ret = 0, i, tab_size;
> -	struct thermal_sensor_conf *data = th_zone->sensor_conf;
> -
> -	if (th_zone->bind == false)
> -		return 0;
> -
> -	tab_size = data->cooling_data.freq_clip_count;
> -
> -	if (tab_size == 0)
> -		return -EINVAL;
> -
> -	/* find the cooling device registered*/
> -	for (i = 0; i < th_zone->cool_dev_size; i++)
> -		if (cdev == th_zone->cool_dev[i])
> -			break;
> -
> -	/* No matching cooling device */
> -	if (i == th_zone->cool_dev_size)
> -		return 0;
> -
> -	/* Bind the thermal zone to the cpufreq cooling device */
> -	for (i = 0; i < tab_size; i++) {
> -		switch (GET_ZONE(i)) {
> -		case MONITOR_ZONE:
> -		case WARN_ZONE:
> -			if (thermal_zone_unbind_cooling_device(thermal, i,
> -								cdev)) {
> -				pr_err("error unbinding cdev inst=%d\n", i);
> -				ret = -EINVAL;
> -			}
> -			th_zone->bind = false;
> -			break;
> -		default:
> -			ret = -EINVAL;
> -		}
> -	}
> -	return ret;
> -}
> -
> -/* Get temperature callback functions for thermal zone */
> -static int exynos_get_temp(struct thermal_zone_device *thermal,
> -			unsigned long *temp)
> -{
> -	void *data;
> -
> -	if (!th_zone->sensor_conf) {
> -		pr_info("Temperature sensor not initialised\n");
> -		return -EINVAL;
> -	}
> -	data = th_zone->sensor_conf->private_data;
> -	*temp = th_zone->sensor_conf->read_temperature(data);
> -	/* convert the temperature into millicelsius */
> -	*temp = *temp * MCELSIUS;
> -	return 0;
> -}
> -
> -/* Get temperature callback functions for thermal zone */
> -static int exynos_set_emul_temp(struct thermal_zone_device *thermal,
> -						unsigned long temp)
> -{
> -	void *data;
> -	int ret = -EINVAL;
> -
> -	if (!th_zone->sensor_conf) {
> -		pr_info("Temperature sensor not initialised\n");
> -		return -EINVAL;
> -	}
> -	data = th_zone->sensor_conf->private_data;
> -	if (th_zone->sensor_conf->write_emul_temp)
> -		ret = th_zone->sensor_conf->write_emul_temp(data, temp);
> -	return ret;
> -}
> -
> -/* Get the temperature trend */
> -static int exynos_get_trend(struct thermal_zone_device *thermal,
> -			int trip, enum thermal_trend *trend)
> -{
> -	int ret;
> -	unsigned long trip_temp;
> -
> -	ret = exynos_get_trip_temp(thermal, trip, &trip_temp);
> -	if (ret < 0)
> -		return ret;
> -
> -	if (thermal->temperature >= trip_temp)
> -		*trend = THERMAL_TREND_RAISE_FULL;
> -	else
> -		*trend = THERMAL_TREND_DROP_FULL;
> -
> -	return 0;
> -}
> -/* Operation callback functions for thermal zone */
> -static struct thermal_zone_device_ops const exynos_dev_ops = {
> -	.bind = exynos_bind,
> -	.unbind = exynos_unbind,
> -	.get_temp = exynos_get_temp,
> -	.set_emul_temp = exynos_set_emul_temp,
> -	.get_trend = exynos_get_trend,
> -	.get_mode = exynos_get_mode,
> -	.set_mode = exynos_set_mode,
> -	.get_trip_type = exynos_get_trip_type,
> -	.get_trip_temp = exynos_get_trip_temp,
> -	.get_crit_temp = exynos_get_crit_temp,
> -};
> -
> -/*
> - * This function may be called from interrupt based temperature sensor
> - * when threshold is changed.
> - */
> -static void exynos_report_trigger(void)
> -{
> -	unsigned int i;
> -	char data[10];
> -	char *envp[] = { data, NULL };
> -
> -	if (!th_zone || !th_zone->therm_dev)
> -		return;
> -	if (th_zone->bind == false) {
> -		for (i = 0; i < th_zone->cool_dev_size; i++) {
> -			if (!th_zone->cool_dev[i])
> -				continue;
> -			exynos_bind(th_zone->therm_dev,
> -					th_zone->cool_dev[i]);
> -		}
> -	}
> -
> -	thermal_zone_device_update(th_zone->therm_dev);
> -
> -	mutex_lock(&th_zone->therm_dev->lock);
> -	/* Find the level for which trip happened */
> -	for (i = 0; i < th_zone->sensor_conf->trip_data.trip_count; i++) {
> -		if (th_zone->therm_dev->last_temperature <
> -			th_zone->sensor_conf->trip_data.trip_val[i] * MCELSIUS)
> -			break;
> -	}
> -
> -	if (th_zone->mode == THERMAL_DEVICE_ENABLED &&
> -		!th_zone->sensor_conf->trip_data.trigger_falling) {
> -		if (i > 0)
> -			th_zone->therm_dev->polling_delay = ACTIVE_INTERVAL;
> -		else
> -			th_zone->therm_dev->polling_delay = IDLE_INTERVAL;
> -	}
> -
> -	snprintf(data, sizeof(data), "%u", i);
> -	kobject_uevent_env(&th_zone->therm_dev->device.kobj, KOBJ_CHANGE, envp);
> -	mutex_unlock(&th_zone->therm_dev->lock);
> -}
> -
> -/* Register with the in-kernel thermal management */
> -static int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf)
> -{
> -	int ret;
> -	struct cpumask mask_val;
> -
> -	if (!sensor_conf || !sensor_conf->read_temperature) {
> -		pr_err("Temperature sensor not initialised\n");
> -		return -EINVAL;
> -	}
> -
> -	th_zone = kzalloc(sizeof(struct exynos_thermal_zone), GFP_KERNEL);
> -	if (!th_zone)
> -		return -ENOMEM;
> -
> -	th_zone->sensor_conf = sensor_conf;
> -	cpumask_set_cpu(0, &mask_val);
> -	th_zone->cool_dev[0] = cpufreq_cooling_register(&mask_val);
> -	if (IS_ERR(th_zone->cool_dev[0])) {
> -		pr_err("Failed to register cpufreq cooling device\n");
> -		ret = -EINVAL;
> -		goto err_unregister;
> -	}
> -	th_zone->cool_dev_size++;
> -
> -	th_zone->therm_dev = thermal_zone_device_register(sensor_conf->name,
> -			EXYNOS_ZONE_COUNT, 0, NULL, &exynos_dev_ops, NULL, 0,
> -			sensor_conf->trip_data.trigger_falling ?
> -			0 : IDLE_INTERVAL);
> -
> -	if (IS_ERR(th_zone->therm_dev)) {
> -		pr_err("Failed to register thermal zone device\n");
> -		ret = PTR_ERR(th_zone->therm_dev);
> -		goto err_unregister;
> -	}
> -	th_zone->mode = THERMAL_DEVICE_ENABLED;
> -
> -	pr_info("Exynos: Kernel Thermal management registered\n");
> -
> -	return 0;
> -
> -err_unregister:
> -	exynos_unregister_thermal();
> -	return ret;
> -}
> -
> -/* Un-Register with the in-kernel thermal management */
> -static void exynos_unregister_thermal(void)
> -{
> -	int i;
> -
> -	if (!th_zone)
> -		return;
> -
> -	if (th_zone->therm_dev)
> -		thermal_zone_device_unregister(th_zone->therm_dev);
> -
> -	for (i = 0; i < th_zone->cool_dev_size; i++) {
> -		if (th_zone->cool_dev[i])
> -			cpufreq_cooling_unregister(th_zone->cool_dev[i]);
> -	}
> -
> -	kfree(th_zone);
> -	pr_info("Exynos: Kernel Thermal management unregistered\n");
> -}
> -
>  /*
>   * TMU treats temperature as a mapped temperature code.
>   * The temperature is converted differently depending on the calibration type.
> diff --git a/drivers/thermal/samsung/exynos_thermal_common.c b/drivers/thermal/samsung/exynos_thermal_common.c
> new file mode 100644
> index 0000000..9a57606
> --- /dev/null
> +++ b/drivers/thermal/samsung/exynos_thermal_common.c
> @@ -0,0 +1,389 @@
> +/*
> + * exynos_thermal_common.c - Samsung EXYNOS common thermal file
> + *
> + *  Copyright (C) 2013 Samsung Electronics
> + *  Amit Daniel Kachhap <amit.daniel@xxxxxxxxxxx>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + *
> + */
> +
> +#include <linux/cpufreq.h>
> +#include <linux/cpu_cooling.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/kobject.h>
> +#include <linux/mutex.h>
> +#include <linux/platform_data/exynos_thermal.h>
> +#include <linux/slab.h>
> +#include <linux/thermal.h>
> +#include "exynos_thermal_common.h"
> +
> +struct exynos_thermal_zone {
> +	enum thermal_device_mode mode;
> +	struct thermal_zone_device *therm_dev;
> +	struct thermal_cooling_device *cool_dev[MAX_COOLING_DEVICE];
> +	unsigned int cool_dev_size;
> +	struct platform_device *exynos4_dev;
> +	struct thermal_sensor_conf *sensor_conf;
> +	bool bind;
> +};
> +
> +static struct exynos_thermal_zone *th_zone;
> +
> +/* Get mode callback functions for thermal zone */
> +static int exynos_get_mode(struct thermal_zone_device *thermal,
> +			enum thermal_device_mode *mode)
> +{
> +	if (th_zone)
> +		*mode = th_zone->mode;
> +	return 0;
> +}
> +
> +/* Set mode callback functions for thermal zone */
> +static int exynos_set_mode(struct thermal_zone_device *thermal,
> +			enum thermal_device_mode mode)
> +{
> +	if (!th_zone->therm_dev) {
> +		pr_notice("thermal zone not registered\n");
> +		return 0;
> +	}
> +
> +	mutex_lock(&th_zone->therm_dev->lock);
> +
> +	if (mode == THERMAL_DEVICE_ENABLED &&
> +		!th_zone->sensor_conf->trip_data.trigger_falling)
> +		th_zone->therm_dev->polling_delay = IDLE_INTERVAL;
> +	else
> +		th_zone->therm_dev->polling_delay = 0;
> +
> +	mutex_unlock(&th_zone->therm_dev->lock);
> +
> +	th_zone->mode = mode;
> +	thermal_zone_device_update(th_zone->therm_dev);
> +	pr_info("thermal polling set for duration=%d msec\n",
> +				th_zone->therm_dev->polling_delay);
> +	return 0;
> +}
> +
> +
> +/* Get trip type callback functions for thermal zone */
> +static int exynos_get_trip_type(struct thermal_zone_device *thermal, int trip,
> +				 enum thermal_trip_type *type)
> +{
> +	switch (GET_ZONE(trip)) {
> +	case MONITOR_ZONE:
> +	case WARN_ZONE:
> +		*type = THERMAL_TRIP_ACTIVE;
> +		break;
> +	case PANIC_ZONE:
> +		*type = THERMAL_TRIP_CRITICAL;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	return 0;
> +}
> +
> +/* Get trip temperature callback functions for thermal zone */
> +static int exynos_get_trip_temp(struct thermal_zone_device *thermal, int trip,
> +				unsigned long *temp)
> +{
> +	if (trip < GET_TRIP(MONITOR_ZONE) || trip > GET_TRIP(PANIC_ZONE))
> +		return -EINVAL;
> +
> +	*temp = th_zone->sensor_conf->trip_data.trip_val[trip];
> +	/* convert the temperature into millicelsius */
> +	*temp = *temp * MCELSIUS;
> +
> +	return 0;
> +}
> +
> +/* Get critical temperature callback functions for thermal zone */
> +static int exynos_get_crit_temp(struct thermal_zone_device *thermal,
> +				unsigned long *temp)
> +{
> +	int ret;
> +	/* Panic zone */
> +	ret = exynos_get_trip_temp(thermal, GET_TRIP(PANIC_ZONE), temp);
> +	return ret;
> +}
> +
> +/* Bind callback functions for thermal zone */
> +static int exynos_bind(struct thermal_zone_device *thermal,
> +			struct thermal_cooling_device *cdev)
> +{
> +	int ret = 0, i, tab_size, level;
> +	struct freq_clip_table *tab_ptr, *clip_data;
> +	struct thermal_sensor_conf *data = th_zone->sensor_conf;
> +
> +	tab_ptr = (struct freq_clip_table *)data->cooling_data.freq_data;
> +	tab_size = data->cooling_data.freq_clip_count;
> +
> +	if (tab_ptr == NULL || tab_size == 0)
> +		return -EINVAL;
> +
> +	/* find the cooling device registered*/
> +	for (i = 0; i < th_zone->cool_dev_size; i++)
> +		if (cdev == th_zone->cool_dev[i])
> +			break;
> +
> +	/* No matching cooling device */
> +	if (i == th_zone->cool_dev_size)
> +		return 0;
> +
> +	/* Bind the thermal zone to the cpufreq cooling device */
> +	for (i = 0; i < tab_size; i++) {
> +		clip_data = (struct freq_clip_table *)&(tab_ptr[i]);
> +		level = cpufreq_cooling_get_level(0, clip_data->freq_clip_max);
> +		if (level == THERMAL_CSTATE_INVALID)
> +			return 0;
> +		switch (GET_ZONE(i)) {
> +		case MONITOR_ZONE:
> +		case WARN_ZONE:
> +			if (thermal_zone_bind_cooling_device(thermal, i, cdev,
> +								level, 0)) {
> +				pr_err("error binding cdev inst %d\n", i);
> +				ret = -EINVAL;
> +			}
> +			th_zone->bind = true;
> +			break;
> +		default:
> +			ret = -EINVAL;
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +/* Unbind callback functions for thermal zone */
> +static int exynos_unbind(struct thermal_zone_device *thermal,
> +			struct thermal_cooling_device *cdev)
> +{
> +	int ret = 0, i, tab_size;
> +	struct thermal_sensor_conf *data = th_zone->sensor_conf;
> +
> +	if (th_zone->bind == false)
> +		return 0;
> +
> +	tab_size = data->cooling_data.freq_clip_count;
> +
> +	if (tab_size == 0)
> +		return -EINVAL;
> +
> +	/* find the cooling device registered*/
> +	for (i = 0; i < th_zone->cool_dev_size; i++)
> +		if (cdev == th_zone->cool_dev[i])
> +			break;
> +
> +	/* No matching cooling device */
> +	if (i == th_zone->cool_dev_size)
> +		return 0;
> +
> +	/* Bind the thermal zone to the cpufreq cooling device */
> +	for (i = 0; i < tab_size; i++) {
> +		switch (GET_ZONE(i)) {
> +		case MONITOR_ZONE:
> +		case WARN_ZONE:
> +			if (thermal_zone_unbind_cooling_device(thermal, i,
> +								cdev)) {
> +				pr_err("error unbinding cdev inst=%d\n", i);
> +				ret = -EINVAL;
> +			}
> +			th_zone->bind = false;
> +			break;
> +		default:
> +			ret = -EINVAL;
> +		}
> +	}
> +	return ret;
> +}
> +
> +/* Get temperature callback functions for thermal zone */
> +static int exynos_get_temp(struct thermal_zone_device *thermal,
> +			unsigned long *temp)
> +{
> +	void *data;
> +
> +	if (!th_zone->sensor_conf) {
> +		pr_info("Temperature sensor not initialised\n");
> +		return -EINVAL;
> +	}
> +	data = th_zone->sensor_conf->private_data;
> +	*temp = th_zone->sensor_conf->read_temperature(data);
> +	/* convert the temperature into millicelsius */
> +	*temp = *temp * MCELSIUS;
> +	return 0;
> +}
> +
> +/* Get temperature callback functions for thermal zone */
> +static int exynos_set_emul_temp(struct thermal_zone_device *thermal,
> +						unsigned long temp)
> +{
> +	void *data;
> +	int ret = -EINVAL;
> +
> +	if (!th_zone->sensor_conf) {
> +		pr_info("Temperature sensor not initialised\n");
> +		return -EINVAL;
> +	}
> +	data = th_zone->sensor_conf->private_data;
> +	if (th_zone->sensor_conf->write_emul_temp)
> +		ret = th_zone->sensor_conf->write_emul_temp(data, temp);
> +	return ret;
> +}
> +
> +/* Get the temperature trend */
> +static int exynos_get_trend(struct thermal_zone_device *thermal,
> +			int trip, enum thermal_trend *trend)
> +{
> +	int ret;
> +	unsigned long trip_temp;
> +
> +	ret = exynos_get_trip_temp(thermal, trip, &trip_temp);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (thermal->temperature >= trip_temp)
> +		*trend = THERMAL_TREND_RAISE_FULL;
> +	else
> +		*trend = THERMAL_TREND_DROP_FULL;
> +
> +	return 0;
> +}
> +/* Operation callback functions for thermal zone */
> +static struct thermal_zone_device_ops const exynos_dev_ops = {
> +	.bind = exynos_bind,
> +	.unbind = exynos_unbind,
> +	.get_temp = exynos_get_temp,
> +	.set_emul_temp = exynos_set_emul_temp,
> +	.get_trend = exynos_get_trend,
> +	.get_mode = exynos_get_mode,
> +	.set_mode = exynos_set_mode,
> +	.get_trip_type = exynos_get_trip_type,
> +	.get_trip_temp = exynos_get_trip_temp,
> +	.get_crit_temp = exynos_get_crit_temp,
> +};
> +
> +/*
> + * This function may be called from interrupt based temperature sensor
> + * when threshold is changed.
> + */
> +void exynos_report_trigger(void)
> +{
> +	unsigned int i;
> +	char data[10];
> +	char *envp[] = { data, NULL };
> +
> +	if (!th_zone || !th_zone->therm_dev)
> +		return;
> +	if (th_zone->bind == false) {
> +		for (i = 0; i < th_zone->cool_dev_size; i++) {
> +			if (!th_zone->cool_dev[i])
> +				continue;
> +			exynos_bind(th_zone->therm_dev,
> +					th_zone->cool_dev[i]);
> +		}
> +	}
> +
> +	thermal_zone_device_update(th_zone->therm_dev);
> +
> +	mutex_lock(&th_zone->therm_dev->lock);
> +	/* Find the level for which trip happened */
> +	for (i = 0; i < th_zone->sensor_conf->trip_data.trip_count; i++) {
> +		if (th_zone->therm_dev->last_temperature <
> +			th_zone->sensor_conf->trip_data.trip_val[i] * MCELSIUS)
> +			break;
> +	}
> +
> +	if (th_zone->mode == THERMAL_DEVICE_ENABLED &&
> +		!th_zone->sensor_conf->trip_data.trigger_falling) {
> +		if (i > 0)
> +			th_zone->therm_dev->polling_delay = ACTIVE_INTERVAL;
> +		else
> +			th_zone->therm_dev->polling_delay = IDLE_INTERVAL;
> +	}
> +
> +	snprintf(data, sizeof(data), "%u", i);
> +	kobject_uevent_env(&th_zone->therm_dev->device.kobj, KOBJ_CHANGE, envp);
> +	mutex_unlock(&th_zone->therm_dev->lock);
> +}
> +
> +/* Register with the in-kernel thermal management */
> +int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf)
> +{
> +	int ret;
> +	struct cpumask mask_val;
> +
> +	if (!sensor_conf || !sensor_conf->read_temperature) {
> +		pr_err("Temperature sensor not initialised\n");
> +		return -EINVAL;
> +	}
> +
> +	th_zone = kzalloc(sizeof(struct exynos_thermal_zone), GFP_KERNEL);
> +	if (!th_zone)
> +		return -ENOMEM;
> +
> +	th_zone->sensor_conf = sensor_conf;
> +	cpumask_set_cpu(0, &mask_val);
> +	th_zone->cool_dev[0] = cpufreq_cooling_register(&mask_val);
> +	if (IS_ERR(th_zone->cool_dev[0])) {
> +		pr_err("Failed to register cpufreq cooling device\n");
> +		ret = -EINVAL;
> +		goto err_unregister;
> +	}
> +	th_zone->cool_dev_size++;
> +
> +	th_zone->therm_dev = thermal_zone_device_register(sensor_conf->name,
> +			EXYNOS_ZONE_COUNT, 0, NULL, &exynos_dev_ops, NULL, 0,
> +			sensor_conf->trip_data.trigger_falling ?
> +			0 : IDLE_INTERVAL);
> +
> +	if (IS_ERR(th_zone->therm_dev)) {
> +		pr_err("Failed to register thermal zone device\n");
> +		ret = PTR_ERR(th_zone->therm_dev);
> +		goto err_unregister;
> +	}
> +	th_zone->mode = THERMAL_DEVICE_ENABLED;
> +
> +	pr_info("Exynos: Kernel Thermal management registered\n");
> +
> +	return 0;
> +
> +err_unregister:
> +	exynos_unregister_thermal();
> +	return ret;
> +}
> +
> +/* Un-Register with the in-kernel thermal management */
> +void exynos_unregister_thermal(void)
> +{
> +	int i;
> +
> +	if (!th_zone)
> +		return;
> +
> +	if (th_zone->therm_dev)
> +		thermal_zone_device_unregister(th_zone->therm_dev);
> +
> +	for (i = 0; i < th_zone->cool_dev_size; i++) {
> +		if (th_zone->cool_dev[i])
> +			cpufreq_cooling_unregister(th_zone->cool_dev[i]);
> +	}
> +
> +	kfree(th_zone);
> +	pr_info("Exynos: Kernel Thermal management unregistered\n");
> +}
> diff --git a/drivers/thermal/samsung/exynos_thermal_common.h b/drivers/thermal/samsung/exynos_thermal_common.h
> new file mode 100644
> index 0000000..165401f
> --- /dev/null
> +++ b/drivers/thermal/samsung/exynos_thermal_common.h
> @@ -0,0 +1,71 @@
> +/*
> + * exynos_thermal_common.h - Samsung EXYNOS common header file
> + *
> + *  Copyright (C) 2013 Samsung Electronics
> + *  Amit Daniel Kachhap <amit.daniel@xxxxxxxxxxx>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + *
> + */
> +
> +#ifndef _LINUX_EXYNOS_THERMAL_COMMON_H
> +#define _LINUX_EXYNOS_THERMAL_COMMON_H
> +
> +/* In-kernel thermal framework related macros & definations */
> +#define SENSOR_NAME_LEN	16
> +#define MAX_TRIP_COUNT	8
> +#define MAX_COOLING_DEVICE 4
> +#define MAX_THRESHOLD_LEVS 4
> +
> +#define ACTIVE_INTERVAL 500
> +#define IDLE_INTERVAL 10000
> +#define MCELSIUS	1000
> +
> +/* CPU Zone information */
> +#define PANIC_ZONE      4
> +#define WARN_ZONE       3
> +#define MONITOR_ZONE    2
> +#define SAFE_ZONE       1
> +
> +#define GET_ZONE(trip) (trip + 2)
> +#define GET_TRIP(zone) (zone - 2)
> +
> +#define EXYNOS_ZONE_COUNT	3
> +
> +struct	thermal_trip_point_conf {
> +	int trip_val[MAX_TRIP_COUNT];
> +	int trip_count;
> +	u8 trigger_falling;
> +};
> +
> +struct	thermal_cooling_conf {
> +	struct freq_clip_table freq_data[MAX_TRIP_COUNT];
> +	int freq_clip_count;
> +};
> +
> +struct thermal_sensor_conf {
> +	char name[SENSOR_NAME_LEN];
> +	int (*read_temperature)(void *data);
> +	int (*write_emul_temp)(void *drv_data, unsigned long temp);
> +	struct thermal_trip_point_conf trip_data;
> +	struct thermal_cooling_conf cooling_data;
> +	void *private_data;
> +};
> +
> +/*Functions used exynos based thermal sensor driver*/
> +void exynos_unregister_thermal(void);
> +int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf);
> +void exynos_report_trigger(void);
> +#endif /* _LINUX_EXYNOS_THERMAL_COMMON_H */


--
To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux SoC Development]     [Linux Rockchip Development]     [Linux USB Development]     [Video for Linux]     [Linux Audio Users]     [Linux SCSI]     [Yosemite News]

  Powered by Linux