From 23064088d6aea049746755e9851760620921a80b Mon Sep 17 00:00:00 2001 From: Durgadoss R Date: Tue, 18 Sep 2012 11:04:52 +0530 Subject: [PATCH 01/36] Thermal: Refactor thermal.h file This patch rearranges the code in thermal.h file, in the following order, so that it is easy to read/maintain. 1. All #defines 2. All enums 3. All fops structures 4. All device structures 5. All function declarations Signed-off-by: Durgadoss R Signed-off-by: Zhang Rui --- include/linux/thermal.h | 78 +++++++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 35 deletions(-) diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 91b34812cd8..8611e3eba60 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -29,6 +29,23 @@ #include #include +#define THERMAL_TRIPS_NONE -1 +#define THERMAL_MAX_TRIPS 12 +#define THERMAL_NAME_LENGTH 20 + +/* No upper/lower limit requirement */ +#define THERMAL_NO_LIMIT -1UL + +/* Unit conversion macros */ +#define KELVIN_TO_CELSIUS(t) (long)(((long)t-2732 >= 0) ? \ + ((long)t-2732+5)/10 : ((long)t-2732-5)/10) +#define CELSIUS_TO_KELVIN(t) ((t)*10+2732) + +/* Adding event notification support elements */ +#define THERMAL_GENL_FAMILY_NAME "thermal_event" +#define THERMAL_GENL_VERSION 0x01 +#define THERMAL_GENL_MCAST_GROUP_NAME "thermal_mc_group" + struct thermal_zone_device; struct thermal_cooling_device; @@ -50,6 +67,30 @@ enum thermal_trend { THERMAL_TREND_DROPPING, /* temperature is dropping */ }; +/* Events supported by Thermal Netlink */ +enum events { + THERMAL_AUX0, + THERMAL_AUX1, + THERMAL_CRITICAL, + THERMAL_DEV_FAULT, +}; + +/* attributes of thermal_genl_family */ +enum { + THERMAL_GENL_ATTR_UNSPEC, + THERMAL_GENL_ATTR_EVENT, + __THERMAL_GENL_ATTR_MAX, +}; +#define THERMAL_GENL_ATTR_MAX (__THERMAL_GENL_ATTR_MAX - 1) + +/* commands supported by the thermal_genl_family */ +enum { + THERMAL_GENL_CMD_UNSPEC, + THERMAL_GENL_CMD_EVENT, + __THERMAL_GENL_CMD_MAX, +}; +#define THERMAL_GENL_CMD_MAX (__THERMAL_GENL_CMD_MAX - 1) + struct thermal_zone_device_ops { int (*bind) (struct thermal_zone_device *, struct thermal_cooling_device *); @@ -83,11 +124,6 @@ struct thermal_cooling_device_ops { int (*set_cur_state) (struct thermal_cooling_device *, unsigned long); }; -#define THERMAL_NO_LIMIT -1UL /* no upper/lower limit requirement */ - -#define THERMAL_TRIPS_NONE -1 -#define THERMAL_MAX_TRIPS 12 -#define THERMAL_NAME_LENGTH 20 struct thermal_cooling_device { int id; char type[THERMAL_NAME_LENGTH]; @@ -100,10 +136,6 @@ struct thermal_cooling_device { struct list_head node; }; -#define KELVIN_TO_CELSIUS(t) (long)(((long)t-2732 >= 0) ? \ - ((long)t-2732+5)/10 : ((long)t-2732-5)/10) -#define CELSIUS_TO_KELVIN(t) ((t)*10+2732) - struct thermal_attr { struct device_attribute attr; char name[THERMAL_NAME_LENGTH]; @@ -131,38 +163,13 @@ struct thermal_zone_device { struct list_head node; struct delayed_work poll_queue; }; -/* Adding event notification support elements */ -#define THERMAL_GENL_FAMILY_NAME "thermal_event" -#define THERMAL_GENL_VERSION 0x01 -#define THERMAL_GENL_MCAST_GROUP_NAME "thermal_mc_group" - -enum events { - THERMAL_AUX0, - THERMAL_AUX1, - THERMAL_CRITICAL, - THERMAL_DEV_FAULT, -}; struct thermal_genl_event { u32 orig; enum events event; }; -/* attributes of thermal_genl_family */ -enum { - THERMAL_GENL_ATTR_UNSPEC, - THERMAL_GENL_ATTR_EVENT, - __THERMAL_GENL_ATTR_MAX, -}; -#define THERMAL_GENL_ATTR_MAX (__THERMAL_GENL_ATTR_MAX - 1) - -/* commands supported by the thermal_genl_family */ -enum { - THERMAL_GENL_CMD_UNSPEC, - THERMAL_GENL_CMD_EVENT, - __THERMAL_GENL_CMD_MAX, -}; -#define THERMAL_GENL_CMD_MAX (__THERMAL_GENL_CMD_MAX - 1) +/* Function declarations */ struct thermal_zone_device *thermal_zone_device_register(const char *, int, int, void *, const struct thermal_zone_device_ops *, int, int); void thermal_zone_device_unregister(struct thermal_zone_device *); @@ -173,6 +180,7 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *, int, int thermal_zone_unbind_cooling_device(struct thermal_zone_device *, int, struct thermal_cooling_device *); void thermal_zone_device_update(struct thermal_zone_device *); + struct thermal_cooling_device *thermal_cooling_device_register(char *, void *, const struct thermal_cooling_device_ops *); void thermal_cooling_device_unregister(struct thermal_cooling_device *); From 71350db43b4c5c4da59b729c805f00ff6675b99d Mon Sep 17 00:00:00 2001 From: Durgadoss R Date: Tue, 18 Sep 2012 11:04:53 +0530 Subject: [PATCH 02/36] Thermal: Move thermal_instance to thermal_core.h This patch creates a thermal_core.h file which can contain all defines used by the core thermal framework files. For now, move the thermal_instance structure to thermal_core.h This structure is used by files under drivers/thermal/. Signed-off-by: Durgadoss R Signed-off-by: Zhang Rui --- drivers/thermal/thermal_core.h | 53 ++++++++++++++++++++++++++++++++++ drivers/thermal/thermal_sys.c | 23 ++------------- 2 files changed, 55 insertions(+), 21 deletions(-) create mode 100644 drivers/thermal/thermal_core.h diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h new file mode 100644 index 00000000000..0d3205a1811 --- /dev/null +++ b/drivers/thermal/thermal_core.h @@ -0,0 +1,53 @@ +/* + * thermal_core.h + * + * Copyright (C) 2012 Intel Corp + * Author: Durgadoss R + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * 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; version 2 of the License. + * + * 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 __THERMAL_CORE_H__ +#define __THERMAL_CORE_H__ + +#include +#include + +/* Initial state of a cooling device during binding */ +#define THERMAL_NO_TARGET -1UL + +/* + * This structure is used to describe the behavior of + * a certain cooling device on a certain trip point + * in a certain thermal zone + */ +struct thermal_instance { + int id; + char name[THERMAL_NAME_LENGTH]; + struct thermal_zone_device *tz; + struct thermal_cooling_device *cdev; + int trip; + unsigned long upper; /* Highest cooling state for this trip point */ + unsigned long lower; /* Lowest cooling state for this trip point */ + unsigned long target; /* expected cooling state */ + char attr_name[THERMAL_NAME_LENGTH]; + struct device_attribute attr; + struct list_head tz_node; /* node in tz->thermal_instances */ + struct list_head cdev_node; /* node in cdev->thermal_instances */ +}; + +#endif /* __THERMAL_CORE_H__ */ diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index 9ee42ca4d28..bbc834625f7 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -37,31 +37,12 @@ #include #include +#include "thermal_core.h" + MODULE_AUTHOR("Zhang Rui"); MODULE_DESCRIPTION("Generic thermal management sysfs support"); MODULE_LICENSE("GPL"); -#define THERMAL_NO_TARGET -1UL -/* - * This structure is used to describe the behavior of - * a certain cooling device on a certain trip point - * in a certain thermal zone - */ -struct thermal_instance { - int id; - char name[THERMAL_NAME_LENGTH]; - struct thermal_zone_device *tz; - struct thermal_cooling_device *cdev; - int trip; - unsigned long upper; /* Highest cooling state for this trip point */ - unsigned long lower; /* Lowest cooling state for this trip point */ - unsigned long target; /* expected cooling state */ - char attr_name[THERMAL_NAME_LENGTH]; - struct device_attribute attr; - struct list_head tz_node; /* node in tz->thermal_instances */ - struct list_head cdev_node; /* node in cdev->thermal_instances */ -}; - static DEFINE_IDR(thermal_tz_idr); static DEFINE_IDR(thermal_cdev_idr); static DEFINE_MUTEX(thermal_idr_lock); From 9b4298a088907811a4fe5a5d7cd2454da60708c5 Mon Sep 17 00:00:00 2001 From: Durgadoss R Date: Tue, 18 Sep 2012 11:04:54 +0530 Subject: [PATCH 03/36] Thermal: Add get trend, get instance API's to thermal_sys This patch adds the following API's to thermal_sys.c, that can be used by other Thermal drivers. * get_tz_trend: obtain the trend of the given thermal zone * get_thermal_instance: obtain the instance corresponding to the given tz, cdev and the trip point. Signed-off-by: Durgadoss R Signed-off-by: Zhang Rui --- drivers/thermal/thermal_sys.c | 40 +++++++++++++++++++++++++++++++++++ include/linux/thermal.h | 4 ++++ 2 files changed, 44 insertions(+) diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index bbc834625f7..1f98c560a88 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -82,6 +82,46 @@ static void release_idr(struct idr *idr, struct mutex *lock, int id) mutex_unlock(lock); } +int get_tz_trend(struct thermal_zone_device *tz, int trip) +{ + enum thermal_trend trend; + + if (!tz->ops->get_trend || tz->ops->get_trend(tz, trip, &trend)) { + if (tz->temperature > tz->last_temperature) + trend = THERMAL_TREND_RAISING; + else if (tz->temperature < tz->last_temperature) + trend = THERMAL_TREND_DROPPING; + else + trend = THERMAL_TREND_STABLE; + } + + return trend; +} +EXPORT_SYMBOL(get_tz_trend); + +struct thermal_instance *get_thermal_instance(struct thermal_zone_device *tz, + struct thermal_cooling_device *cdev, int trip) +{ + struct thermal_instance *pos = NULL; + struct thermal_instance *target_instance = NULL; + + mutex_lock(&tz->lock); + mutex_lock(&cdev->lock); + + list_for_each_entry(pos, &tz->thermal_instances, tz_node) { + if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) { + target_instance = pos; + break; + } + } + + mutex_unlock(&cdev->lock); + mutex_unlock(&tz->lock); + + return target_instance; +} +EXPORT_SYMBOL(get_thermal_instance); + /* sys I/F for thermal zone */ #define to_thermal_zone(_dev) \ diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 8611e3eba60..32af124d23c 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -185,6 +185,10 @@ struct thermal_cooling_device *thermal_cooling_device_register(char *, void *, const struct thermal_cooling_device_ops *); void thermal_cooling_device_unregister(struct thermal_cooling_device *); +int get_tz_trend(struct thermal_zone_device *, int); +struct thermal_instance *get_thermal_instance(struct thermal_zone_device *, + struct thermal_cooling_device *, int); + #ifdef CONFIG_NET extern int thermal_generate_netlink_event(u32 orig, enum events event); #else From ef87394791206019e4e4e04cb746865f2dc115ed Mon Sep 17 00:00:00 2001 From: Durgadoss R Date: Tue, 18 Sep 2012 11:04:55 +0530 Subject: [PATCH 04/36] Thermal: Add platform level information to thermal.h This patch adds platform level information to thermal.h by introducing two structures to hold: * bind parameters for a thermal zone, * zone level platform parameters Signed-off-by: Durgadoss R Signed-off-by: Zhang Rui --- include/linux/thermal.h | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 32af124d23c..4caa32e400b 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -157,6 +157,7 @@ struct thermal_zone_device { int passive; unsigned int forced_passive; const struct thermal_zone_device_ops *ops; + const struct thermal_zone_params *tzp; struct list_head thermal_instances; struct idr idr; struct mutex lock; /* protect thermal_instances list */ @@ -164,6 +165,34 @@ struct thermal_zone_device { struct delayed_work poll_queue; }; +/* Structure that holds binding parameters for a zone */ +struct thermal_bind_params { + struct thermal_cooling_device *cdev; + + /* + * This is a measure of 'how effectively these devices can + * cool 'this' thermal zone. The shall be determined by platform + * characterization. This is on a 'percentage' scale. + * See Documentation/thermal/sysfs-api.txt for more information. + */ + int weight; + + /* + * This is a bit mask that gives the binding relation between this + * thermal zone and cdev, for a particular trip point. + * See Documentation/thermal/sysfs-api.txt for more information. + */ + int trip_mask; + int (*match) (struct thermal_zone_device *tz, + struct thermal_cooling_device *cdev); +}; + +/* Structure to define Thermal Zone parameters */ +struct thermal_zone_params { + int num_tbps; /* Number of tbp entries */ + struct thermal_bind_params *tbp; +}; + struct thermal_genl_event { u32 orig; enum events event; From 50125a9b27dd09e9afdc1b8712ba0b3859886c68 Mon Sep 17 00:00:00 2001 From: Durgadoss R Date: Tue, 18 Sep 2012 11:04:56 +0530 Subject: [PATCH 05/36] Thermal: Pass zone parameters as argument to tzd_register This patch adds the thermal zone parameter as an argument to the tzd_register() function call; and updates other drivers using this function. Signed-off-by: Durgadoss R Signed-off-by: Zhang Rui --- drivers/acpi/thermal.c | 6 +++--- drivers/platform/x86/acerhdf.c | 2 +- drivers/platform/x86/intel_mid_thermal.c | 2 +- drivers/power/power_supply_core.c | 2 +- drivers/staging/omap-thermal/omap-thermal-common.c | 2 +- drivers/thermal/exynos_thermal.c | 2 +- drivers/thermal/rcar_thermal.c | 2 +- drivers/thermal/spear_thermal.c | 2 +- drivers/thermal/thermal_sys.c | 3 +++ include/linux/thermal.h | 3 ++- 10 files changed, 15 insertions(+), 11 deletions(-) diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 804204d4199..1794468223c 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -900,14 +900,14 @@ static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz) if (tz->trips.passive.flags.valid) tz->thermal_zone = thermal_zone_device_register("acpitz", trips, 0, tz, - &acpi_thermal_zone_ops, + &acpi_thermal_zone_ops, NULL, tz->trips.passive.tsp*100, tz->polling_frequency*100); else tz->thermal_zone = thermal_zone_device_register("acpitz", trips, 0, tz, - &acpi_thermal_zone_ops, 0, - tz->polling_frequency*100); + &acpi_thermal_zone_ops, NULL, + 0, tz->polling_frequency*100); if (IS_ERR(tz->thermal_zone)) return -ENODEV; diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c index 84c56881ba8..c2e3e63d2c1 100644 --- a/drivers/platform/x86/acerhdf.c +++ b/drivers/platform/x86/acerhdf.c @@ -662,7 +662,7 @@ static int acerhdf_register_thermal(void) return -EINVAL; thz_dev = thermal_zone_device_register("acerhdf", 1, 0, NULL, - &acerhdf_dev_ops, 0, + &acerhdf_dev_ops, NULL, 0, (kernelmode) ? interval*1000 : 0); if (IS_ERR(thz_dev)) return -EINVAL; diff --git a/drivers/platform/x86/intel_mid_thermal.c b/drivers/platform/x86/intel_mid_thermal.c index c8097616dd6..93de09019d1 100644 --- a/drivers/platform/x86/intel_mid_thermal.c +++ b/drivers/platform/x86/intel_mid_thermal.c @@ -502,7 +502,7 @@ static int mid_thermal_probe(struct platform_device *pdev) goto err; } pinfo->tzd[i] = thermal_zone_device_register(name[i], - 0, 0, td_info, &tzd_ops, 0, 0); + 0, 0, td_info, &tzd_ops, NULL, 0, 0); if (IS_ERR(pinfo->tzd[i])) { kfree(td_info); ret = PTR_ERR(pinfo->tzd[i]); diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index 2436f135001..f77a41272e5 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -201,7 +201,7 @@ static int psy_register_thermal(struct power_supply *psy) for (i = 0; i < psy->num_properties; i++) { if (psy->properties[i] == POWER_SUPPLY_PROP_TEMP) { psy->tzd = thermal_zone_device_register(psy->name, 0, 0, - psy, &psy_tzd_ops, 0, 0); + psy, &psy_tzd_ops, NULL, 0, 0); if (IS_ERR(psy->tzd)) return PTR_ERR(psy->tzd); break; diff --git a/drivers/staging/omap-thermal/omap-thermal-common.c b/drivers/staging/omap-thermal/omap-thermal-common.c index 5c0c203b887..788f64f2f46 100644 --- a/drivers/staging/omap-thermal/omap-thermal-common.c +++ b/drivers/staging/omap-thermal/omap-thermal-common.c @@ -270,7 +270,7 @@ int omap_thermal_expose_sensor(struct omap_bandgap *bg_ptr, int id, /* Create thermal zone */ data->omap_thermal = thermal_zone_device_register(domain, OMAP_TRIP_NUMBER, 0, data, &omap_thermal_ops, - FAST_TEMP_MONITORING_RATE, + NULL, FAST_TEMP_MONITORING_RATE, FAST_TEMP_MONITORING_RATE); if (IS_ERR_OR_NULL(data->omap_thermal)) { dev_err(bg_ptr->dev, "thermal zone device is NULL\n"); diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c index 6dd29e4ce36..7772d160376 100644 --- a/drivers/thermal/exynos_thermal.c +++ b/drivers/thermal/exynos_thermal.c @@ -451,7 +451,7 @@ static int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf) th_zone->cool_dev_size++; th_zone->therm_dev = thermal_zone_device_register(sensor_conf->name, - EXYNOS_ZONE_COUNT, 0, NULL, &exynos_dev_ops, 0, + EXYNOS_ZONE_COUNT, 0, NULL, &exynos_dev_ops, NULL, 0, IDLE_INTERVAL); if (IS_ERR(th_zone->therm_dev)) { diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index f7a1b574a30..762f6373d50 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -211,7 +211,7 @@ static int rcar_thermal_probe(struct platform_device *pdev) } zone = thermal_zone_device_register("rcar_thermal", 0, 0, priv, - &rcar_thermal_zone_ops, 0, 0); + &rcar_thermal_zone_ops, NULL, 0, 0); if (IS_ERR(zone)) { dev_err(&pdev->dev, "thermal zone device is NULL\n"); ret = PTR_ERR(zone); diff --git a/drivers/thermal/spear_thermal.c b/drivers/thermal/spear_thermal.c index 9bc969261d0..6b2d8b21aae 100644 --- a/drivers/thermal/spear_thermal.c +++ b/drivers/thermal/spear_thermal.c @@ -147,7 +147,7 @@ static int spear_thermal_probe(struct platform_device *pdev) writel_relaxed(stdev->flags, stdev->thermal_base); spear_thermal = thermal_zone_device_register("spear_thermal", 0, 0, - stdev, &ops, 0, 0); + stdev, &ops, NULL, 0, 0); if (IS_ERR(spear_thermal)) { dev_err(&pdev->dev, "thermal zone device is NULL\n"); ret = PTR_ERR(spear_thermal); diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index 1f98c560a88..dca3bfc0e70 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -1341,6 +1341,7 @@ static void remove_trip_attrs(struct thermal_zone_device *tz) * @mask: a bit string indicating the writeablility of trip points * @devdata: private device data * @ops: standard thermal zone device callbacks + * @tzp: thermal zone platform parameters * @passive_delay: number of milliseconds to wait between polls when * performing passive cooling * @polling_delay: number of milliseconds to wait between polls when checking @@ -1353,6 +1354,7 @@ static void remove_trip_attrs(struct thermal_zone_device *tz) struct thermal_zone_device *thermal_zone_device_register(const char *type, int trips, int mask, void *devdata, const struct thermal_zone_device_ops *ops, + const struct thermal_zone_params *tzp, int passive_delay, int polling_delay) { struct thermal_zone_device *tz; @@ -1386,6 +1388,7 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, strcpy(tz->type, type ? : ""); tz->ops = ops; + tz->tzp = tzp; tz->device.class = &thermal_class; tz->devdata = devdata; tz->trips = trips; diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 4caa32e400b..58cb1c036a0 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -200,7 +200,8 @@ struct thermal_genl_event { /* Function declarations */ struct thermal_zone_device *thermal_zone_device_register(const char *, int, int, - void *, const struct thermal_zone_device_ops *, int, int); + void *, const struct thermal_zone_device_ops *, + const struct thermal_zone_params *, int, int); void thermal_zone_device_unregister(struct thermal_zone_device *); int thermal_zone_bind_cooling_device(struct thermal_zone_device *, int, From a4a15485fbba44d162d626141b9a7404e790f570 Mon Sep 17 00:00:00 2001 From: Durgadoss R Date: Tue, 18 Sep 2012 11:04:57 +0530 Subject: [PATCH 06/36] Thermal: Add thermal governor registration APIs This patch creates a structure to hold platform thermal governor information, and provides APIs for individual thermal governors to register/unregister with the Thermal framework. Signed-off-by: Durgadoss R Signed-off-by: Zhang Rui --- drivers/thermal/thermal_sys.c | 90 +++++++++++++++++++++++++++++++++++ include/linux/thermal.h | 16 +++++++ 2 files changed, 106 insertions(+) diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index dca3bfc0e70..0afd84cb127 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -49,7 +49,86 @@ static DEFINE_MUTEX(thermal_idr_lock); static LIST_HEAD(thermal_tz_list); static LIST_HEAD(thermal_cdev_list); +static LIST_HEAD(thermal_governor_list); + static DEFINE_MUTEX(thermal_list_lock); +static DEFINE_MUTEX(thermal_governor_lock); + +static struct thermal_governor *__find_governor(const char *name) +{ + struct thermal_governor *pos; + + list_for_each_entry(pos, &thermal_governor_list, governor_list) + if (!strnicmp(name, pos->name, THERMAL_NAME_LENGTH)) + return pos; + + return NULL; +} + +int thermal_register_governor(struct thermal_governor *governor) +{ + int err; + const char *name; + struct thermal_zone_device *pos; + + if (!governor) + return -EINVAL; + + mutex_lock(&thermal_governor_lock); + + err = -EBUSY; + if (__find_governor(governor->name) == NULL) { + err = 0; + list_add(&governor->governor_list, &thermal_governor_list); + } + + mutex_lock(&thermal_list_lock); + + list_for_each_entry(pos, &thermal_tz_list, node) { + if (pos->governor) + continue; + if (pos->tzp) + name = pos->tzp->governor_name; + else + name = DEFAULT_THERMAL_GOVERNOR; + if (!strnicmp(name, governor->name, THERMAL_NAME_LENGTH)) + pos->governor = governor; + } + + mutex_unlock(&thermal_list_lock); + mutex_unlock(&thermal_governor_lock); + + return err; +} +EXPORT_SYMBOL_GPL(thermal_register_governor); + +void thermal_unregister_governor(struct thermal_governor *governor) +{ + struct thermal_zone_device *pos; + + if (!governor) + return; + + mutex_lock(&thermal_governor_lock); + + if (__find_governor(governor->name) == NULL) + goto exit; + + mutex_lock(&thermal_list_lock); + + list_for_each_entry(pos, &thermal_tz_list, node) { + if (!strnicmp(pos->governor->name, governor->name, + THERMAL_NAME_LENGTH)) + pos->governor = NULL; + } + + mutex_unlock(&thermal_list_lock); + list_del(&governor->governor_list); +exit: + mutex_unlock(&thermal_governor_lock); + return; +} +EXPORT_SYMBOL_GPL(thermal_unregister_governor); static int get_idr(struct idr *idr, struct mutex *lock, int *id) { @@ -1437,6 +1516,16 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, if (result) goto unregister; + /* Update 'this' zone's governor information */ + mutex_lock(&thermal_governor_lock); + + if (tz->tzp) + tz->governor = __find_governor(tz->tzp->governor_name); + else + tz->governor = __find_governor(DEFAULT_THERMAL_GOVERNOR); + + mutex_unlock(&thermal_governor_lock); + result = thermal_add_hwmon_sysfs(tz); if (result) goto unregister; @@ -1500,6 +1589,7 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) if (tz->ops->get_mode) device_remove_file(&tz->device, &dev_attr_mode); remove_trip_attrs(tz); + tz->governor = NULL; thermal_remove_hwmon_sysfs(tz); release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id); diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 58cb1c036a0..6182bd5f750 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -46,6 +46,9 @@ #define THERMAL_GENL_VERSION 0x01 #define THERMAL_GENL_MCAST_GROUP_NAME "thermal_mc_group" +/* Default Thermal Governor: Does Linear Throttling */ +#define DEFAULT_THERMAL_GOVERNOR "step_wise" + struct thermal_zone_device; struct thermal_cooling_device; @@ -158,6 +161,7 @@ struct thermal_zone_device { unsigned int forced_passive; const struct thermal_zone_device_ops *ops; const struct thermal_zone_params *tzp; + struct thermal_governor *governor; struct list_head thermal_instances; struct idr idr; struct mutex lock; /* protect thermal_instances list */ @@ -165,6 +169,14 @@ struct thermal_zone_device { struct delayed_work poll_queue; }; +/* Structure that holds thermal governor information */ +struct thermal_governor { + char name[THERMAL_NAME_LENGTH]; + int (*throttle)(struct thermal_zone_device *tz, int trip); + struct list_head governor_list; + struct module *owner; +}; + /* Structure that holds binding parameters for a zone */ struct thermal_bind_params { struct thermal_cooling_device *cdev; @@ -189,6 +201,7 @@ struct thermal_bind_params { /* Structure to define Thermal Zone parameters */ struct thermal_zone_params { + char governor_name[THERMAL_NAME_LENGTH]; int num_tbps; /* Number of tbp entries */ struct thermal_bind_params *tbp; }; @@ -219,6 +232,9 @@ int get_tz_trend(struct thermal_zone_device *, int); struct thermal_instance *get_thermal_instance(struct thermal_zone_device *, struct thermal_cooling_device *, int); +int thermal_register_governor(struct thermal_governor *); +void thermal_unregister_governor(struct thermal_governor *); + #ifdef CONFIG_NET extern int thermal_generate_netlink_event(u32 orig, enum events event); #else From 5a2c090b3327353a78232182f2c7fcd69a46eff0 Mon Sep 17 00:00:00 2001 From: Durgadoss R Date: Tue, 18 Sep 2012 11:04:58 +0530 Subject: [PATCH 07/36] Thermal: Add a policy sysfs attribute This patch adds a policy sysfs attribute to a thermal zone. This attribute denotes the throttling governor used for the zone. This is a RW attribute. Signed-off-by: Durgadoss R Signed-off-by: Zhang Rui --- drivers/thermal/thermal_sys.c | 42 ++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index 0afd84cb127..a6933dad3ba 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -454,10 +454,41 @@ passive_show(struct device *dev, struct device_attribute *attr, return sprintf(buf, "%d\n", tz->forced_passive); } +static ssize_t +policy_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret = -EINVAL; + struct thermal_zone_device *tz = to_thermal_zone(dev); + struct thermal_governor *gov; + + mutex_lock(&thermal_governor_lock); + + gov = __find_governor(buf); + if (!gov) + goto exit; + + tz->governor = gov; + ret = count; + +exit: + mutex_unlock(&thermal_governor_lock); + return ret; +} + +static ssize_t +policy_show(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct thermal_zone_device *tz = to_thermal_zone(dev); + + return sprintf(buf, "%s\n", tz->governor->name); +} + static DEVICE_ATTR(type, 0444, type_show, NULL); static DEVICE_ATTR(temp, 0444, temp_show, NULL); static DEVICE_ATTR(mode, 0644, mode_show, mode_store); static DEVICE_ATTR(passive, S_IRUGO | S_IWUSR, passive_show, passive_store); +static DEVICE_ATTR(policy, S_IRUGO | S_IWUSR, policy_show, policy_store); /* sys I/F for cooling device */ #define to_cooling_device(_dev) \ @@ -1509,10 +1540,14 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, passive = 1; } - if (!passive) - result = device_create_file(&tz->device, - &dev_attr_passive); + if (!passive) { + result = device_create_file(&tz->device, &dev_attr_passive); + if (result) + goto unregister; + } + /* Create policy attribute */ + result = device_create_file(&tz->device, &dev_attr_policy); if (result) goto unregister; @@ -1588,6 +1623,7 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) device_remove_file(&tz->device, &dev_attr_temp); if (tz->ops->get_mode) device_remove_file(&tz->device, &dev_attr_mode); + device_remove_file(&tz->device, &dev_attr_policy); remove_trip_attrs(tz); tz->governor = NULL; From 7e8ee1e9d7561fe640ae8df6c28f10a6a3239592 Mon Sep 17 00:00:00 2001 From: Durgadoss R Date: Tue, 18 Sep 2012 11:04:59 +0530 Subject: [PATCH 08/36] Thermal: Update binding logic based on platform data This patch updates the binding logic in thermal_sys.c It uses the platform layer data to bind a thermal zone to a cdev for a particular trip point. * If we do not have platform data and do not have .bind defined, do not bind. * If we do not have platform data but .bind is defined, then use tz->ops->bind. * If we have platform data, use it to create binding. The same logic sequence is followed for unbind also. Signed-off-by: Durgadoss R Signed-off-by: Zhang Rui --- drivers/thermal/thermal_sys.c | 183 +++++++++++++++++++++++++++++----- 1 file changed, 157 insertions(+), 26 deletions(-) diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index a6933dad3ba..a32803224e3 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -201,6 +201,111 @@ struct thermal_instance *get_thermal_instance(struct thermal_zone_device *tz, } EXPORT_SYMBOL(get_thermal_instance); +static void print_bind_err_msg(struct thermal_zone_device *tz, + struct thermal_cooling_device *cdev, int ret) +{ + dev_err(&tz->device, "binding zone %s with cdev %s failed:%d\n", + tz->type, cdev->type, ret); +} + +static void __bind(struct thermal_zone_device *tz, int mask, + struct thermal_cooling_device *cdev) +{ + int i, ret; + + for (i = 0; i < tz->trips; i++) { + if (mask & (1 << i)) { + ret = thermal_zone_bind_cooling_device(tz, i, cdev, + THERMAL_NO_LIMIT, THERMAL_NO_LIMIT); + if (ret) + print_bind_err_msg(tz, cdev, ret); + } + } +} + +static void __unbind(struct thermal_zone_device *tz, int mask, + struct thermal_cooling_device *cdev) +{ + int i; + + for (i = 0; i < tz->trips; i++) + if (mask & (1 << i)) + thermal_zone_unbind_cooling_device(tz, i, cdev); +} + +static void bind_cdev(struct thermal_cooling_device *cdev) +{ + int i, ret; + const struct thermal_zone_params *tzp; + struct thermal_zone_device *pos = NULL; + + mutex_lock(&thermal_list_lock); + + list_for_each_entry(pos, &thermal_tz_list, node) { + if (!pos->tzp && !pos->ops->bind) + continue; + + if (!pos->tzp && pos->ops->bind) { + ret = pos->ops->bind(pos, cdev); + if (ret) + print_bind_err_msg(pos, cdev, ret); + } + + tzp = pos->tzp; + if (!tzp->tbp) + return; + + for (i = 0; i < tzp->num_tbps; i++) { + if (tzp->tbp[i].cdev || !tzp->tbp[i].match) + continue; + if (tzp->tbp[i].match(pos, cdev)) + continue; + tzp->tbp[i].cdev = cdev; + __bind(pos, tzp->tbp[i].trip_mask, cdev); + } + } + + mutex_unlock(&thermal_list_lock); +} + +static void bind_tz(struct thermal_zone_device *tz) +{ + int i, ret; + struct thermal_cooling_device *pos = NULL; + const struct thermal_zone_params *tzp = tz->tzp; + + if (!tzp && !tz->ops->bind) + return; + + mutex_lock(&thermal_list_lock); + + /* If there is no platform data, try to use ops->bind */ + if (!tzp && tz->ops->bind) { + list_for_each_entry(pos, &thermal_cdev_list, node) { + ret = tz->ops->bind(tz, pos); + if (ret) + print_bind_err_msg(tz, pos, ret); + } + goto exit; + } + + if (!tzp->tbp) + goto exit; + + list_for_each_entry(pos, &thermal_cdev_list, node) { + for (i = 0; i < tzp->num_tbps; i++) { + if (tzp->tbp[i].cdev || !tzp->tbp[i].match) + continue; + if (tzp->tbp[i].match(tz, pos)) + continue; + tzp->tbp[i].cdev = pos; + __bind(tz, tzp->tbp[i].trip_mask, pos); + } + } +exit: + mutex_unlock(&thermal_list_lock); +} + /* sys I/F for thermal zone */ #define to_thermal_zone(_dev) \ @@ -1026,7 +1131,6 @@ thermal_cooling_device_register(char *type, void *devdata, const struct thermal_cooling_device_ops *ops) { struct thermal_cooling_device *cdev; - struct thermal_zone_device *pos; int result; if (type && strlen(type) >= THERMAL_NAME_LENGTH) @@ -1076,20 +1180,15 @@ thermal_cooling_device_register(char *type, void *devdata, if (result) goto unregister; + /* Add 'this' new cdev to the global cdev list */ mutex_lock(&thermal_list_lock); list_add(&cdev->node, &thermal_cdev_list); - list_for_each_entry(pos, &thermal_tz_list, node) { - if (!pos->ops->bind) - continue; - result = pos->ops->bind(pos, cdev); - if (result) - break; - - } mutex_unlock(&thermal_list_lock); - if (!result) - return cdev; + /* Update binding information for 'this' new cdev */ + bind_cdev(cdev); + + return cdev; unregister: release_idr(&thermal_cdev_idr, &thermal_idr_lock, cdev->id); @@ -1105,10 +1204,10 @@ EXPORT_SYMBOL(thermal_cooling_device_register); * thermal_cooling_device_unregister() must be called when the device is no * longer needed. */ -void thermal_cooling_device_unregister(struct - thermal_cooling_device - *cdev) +void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev) { + int i; + const struct thermal_zone_params *tzp; struct thermal_zone_device *tz; struct thermal_cooling_device *pos = NULL; @@ -1125,12 +1224,28 @@ void thermal_cooling_device_unregister(struct return; } list_del(&cdev->node); + + /* Unbind all thermal zones associated with 'this' cdev */ list_for_each_entry(tz, &thermal_tz_list, node) { - if (!tz->ops->unbind) + if (tz->ops->unbind) { + tz->ops->unbind(tz, cdev); continue; - tz->ops->unbind(tz, cdev); + } + + if (!tz->tzp || !tz->tzp->tbp) + continue; + + tzp = tz->tzp; + for (i = 0; i < tzp->num_tbps; i++) { + if (tzp->tbp[i].cdev == cdev) { + __unbind(tz, tzp->tbp[i].trip_mask, cdev); + tzp->tbp[i].cdev = NULL; + } + } } + mutex_unlock(&thermal_list_lock); + if (cdev->type[0]) device_remove_file(&cdev->device, &dev_attr_cdev_type); device_remove_file(&cdev->device, &dev_attr_max_state); @@ -1468,7 +1583,6 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, int passive_delay, int polling_delay) { struct thermal_zone_device *tz; - struct thermal_cooling_device *pos; enum thermal_trip_type trip_type; int result; int count; @@ -1567,14 +1681,11 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, mutex_lock(&thermal_list_lock); list_add_tail(&tz->node, &thermal_tz_list); - if (ops->bind) - list_for_each_entry(pos, &thermal_cdev_list, node) { - result = ops->bind(tz, pos); - if (result) - break; - } mutex_unlock(&thermal_list_lock); + /* Bind cooling devices for this zone */ + bind_tz(tz); + INIT_DELAYED_WORK(&(tz->poll_queue), thermal_zone_device_check); thermal_zone_device_update(tz); @@ -1595,12 +1706,16 @@ EXPORT_SYMBOL(thermal_zone_device_register); */ void thermal_zone_device_unregister(struct thermal_zone_device *tz) { + int i; + const struct thermal_zone_params *tzp; struct thermal_cooling_device *cdev; struct thermal_zone_device *pos = NULL; if (!tz) return; + tzp = tz->tzp; + mutex_lock(&thermal_list_lock); list_for_each_entry(pos, &thermal_tz_list, node) if (pos == tz) @@ -1611,9 +1726,25 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) return; } list_del(&tz->node); - if (tz->ops->unbind) - list_for_each_entry(cdev, &thermal_cdev_list, node) - tz->ops->unbind(tz, cdev); + + /* Unbind all cdevs associated with 'this' thermal zone */ + list_for_each_entry(cdev, &thermal_cdev_list, node) { + if (tz->ops->unbind) { + tz->ops->unbind(tz, cdev); + continue; + } + + if (!tzp || !tzp->tbp) + break; + + for (i = 0; i < tzp->num_tbps; i++) { + if (tzp->tbp[i].cdev == cdev) { + __unbind(tz, tzp->tbp[i].trip_mask, cdev); + tzp->tbp[i].cdev = NULL; + } + } + } + mutex_unlock(&thermal_list_lock); thermal_zone_device_set_polling(tz, 0); From dc76548269ca2c44e5ddc29db5c36188bc4f2234 Mon Sep 17 00:00:00 2001 From: Durgadoss R Date: Tue, 18 Sep 2012 11:05:00 +0530 Subject: [PATCH 09/36] Thermal: Make thermal_cdev_update as a global function This patch makes the thermal_cdev_update function as a global one, so that other files can use it. This function serves as a single arbitrator to set the state of a cooling device. Signed-off-by: Durgadoss R Signed-off-by: Zhang Rui --- drivers/thermal/thermal_sys.c | 5 +++-- include/linux/thermal.h | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index a32803224e3..9d21f82986e 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -1257,7 +1257,7 @@ void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev) } EXPORT_SYMBOL(thermal_cooling_device_unregister); -static void thermal_cdev_do_update(struct thermal_cooling_device *cdev) +void thermal_cdev_update(struct thermal_cooling_device *cdev) { struct thermal_instance *instance; unsigned long target = 0; @@ -1278,13 +1278,14 @@ static void thermal_cdev_do_update(struct thermal_cooling_device *cdev) cdev->ops->set_cur_state(cdev, target); cdev->updated = true; } +EXPORT_SYMBOL(thermal_cdev_update); static void thermal_zone_do_update(struct thermal_zone_device *tz) { struct thermal_instance *instance; list_for_each_entry(instance, &tz->thermal_instances, tz_node) - thermal_cdev_do_update(instance->cdev); + thermal_cdev_update(instance->cdev); } /* diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 6182bd5f750..2bd91584181 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -231,6 +231,7 @@ void thermal_cooling_device_unregister(struct thermal_cooling_device *); int get_tz_trend(struct thermal_zone_device *, int); struct thermal_instance *get_thermal_instance(struct thermal_zone_device *, struct thermal_cooling_device *, int); +void thermal_cdev_update(struct thermal_cooling_device *); int thermal_register_governor(struct thermal_governor *); void thermal_unregister_governor(struct thermal_governor *); From 4ccc5743ae70530f758c7d7ec9a0ca58878e584b Mon Sep 17 00:00:00 2001 From: Durgadoss R Date: Tue, 18 Sep 2012 11:05:01 +0530 Subject: [PATCH 10/36] Thermal: Introduce fair_share thermal governor This patch introduces a simple 'weight' based governor named fair_share governor. Whenever the thermal framework gets notified of the trip point violation, this governor (if configured), throttles the cooling devices associated with a thermal zone. This mapping between a thermal zone and a cooling device and the effectiveness of cooling are provided in the platform layer. Signed-off-by: Durgadoss R Signed-off-by: Zhang Rui --- drivers/thermal/Kconfig | 6 ++ drivers/thermal/Makefile | 1 + drivers/thermal/fair_share.c | 133 +++++++++++++++++++++++++++++++++++ 3 files changed, 140 insertions(+) create mode 100644 drivers/thermal/fair_share.c diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index e1cb6bd75f6..84b0f0382e5 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -55,3 +55,9 @@ config EXYNOS_THERMAL help If you say yes here you get support for TMU (Thermal Managment Unit) on SAMSUNG EXYNOS series of SoC. + +config FAIR_SHARE + bool "Fair-share thermal governor" + depends on THERMAL + help + Enable this to manage platform thermals using fair-share governor. diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 885550dc64b..30540dde408 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_CPU_THERMAL) += cpu_cooling.o obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o +obj-$(CONFIG_FAIR_SHARE) += fair_share.o diff --git a/drivers/thermal/fair_share.c b/drivers/thermal/fair_share.c new file mode 100644 index 00000000000..5d3a7a3e6a7 --- /dev/null +++ b/drivers/thermal/fair_share.c @@ -0,0 +1,133 @@ +/* + * fair_share.c - A simple weight based Thermal governor + * + * Copyright (C) 2012 Intel Corp + * Copyright (C) 2012 Durgadoss R + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include + +#include "thermal_core.h" + +/** + * get_trip_level: - obtains the current trip level for a zone + * @tz: thermal zone device + */ +static int get_trip_level(struct thermal_zone_device *tz) +{ + int count = 0; + unsigned long trip_temp; + + if (tz->trips == 0 || !tz->ops->get_trip_temp) + return 0; + + for (count = 0; count < tz->trips; count++) { + tz->ops->get_trip_temp(tz, count, &trip_temp); + if (tz->temperature < trip_temp) + break; + } + return count; +} + +static long get_target_state(struct thermal_zone_device *tz, + struct thermal_cooling_device *cdev, int weight, int level) +{ + unsigned long max_state; + + cdev->ops->get_max_state(cdev, &max_state); + + return (long)(weight * level * max_state) / (100 * tz->trips); +} + +/** + * fair_share_throttle - throttles devices asscciated with the given zone + * @tz - thermal_zone_device + * + * Throttling Logic: This uses three parameters to calculate the new + * throttle state of the cooling devices associated with the given zone. + * + * Parameters used for Throttling: + * P1. max_state: Maximum throttle state exposed by the cooling device. + * P2. weight[i]/100: + * How 'effective' the 'i'th device is, in cooling the given zone. + * P3. cur_trip_level/max_no_of_trips: + * This describes the extent to which the devices should be throttled. + * We do not want to throttle too much when we trip a lower temperature, + * whereas the throttling is at full swing if we trip critical levels. + * (Heavily assumes the trip points are in ascending order) + * new_state of cooling device = P3 * P2 * P1 + */ +int fair_share_throttle(struct thermal_zone_device *tz, int trip) +{ + const struct thermal_zone_params *tzp; + struct thermal_cooling_device *cdev; + struct thermal_instance *instance; + int i; + int cur_trip_level = get_trip_level(tz); + + if (!tz->tzp || !tz->tzp->tbp) + return -EINVAL; + + tzp = tz->tzp; + + for (i = 0; i < tzp->num_tbps; i++) { + if (!tzp->tbp[i].cdev) + continue; + + cdev = tzp->tbp[i].cdev; + instance = get_thermal_instance(tz, cdev, trip); + if (!instance) + continue; + + instance->target = get_target_state(tz, cdev, + tzp->tbp[i].weight, cur_trip_level); + + instance->cdev->updated = false; + thermal_cdev_update(cdev); + } + return 0; +} + +struct thermal_governor thermal_gov_fair_share = { + .name = "fair_share", + .throttle = fair_share_throttle, + .owner = THIS_MODULE, +}; + +static int __init thermal_gov_fair_share_init(void) +{ + return thermal_register_governor(&thermal_gov_fair_share); +} + +static void __exit thermal_gov_fair_share_exit(void) +{ + thermal_unregister_governor(&thermal_gov_fair_share); +} + +/* This should load after thermal framework */ +fs_initcall(thermal_gov_fair_share_init); +module_exit(thermal_gov_fair_share_exit); + +MODULE_AUTHOR("Durgadoss R"); +MODULE_DESCRIPTION("A simple weight based thermal throttling governor"); +MODULE_LICENSE("GPL"); From e151a202a084f9f4310d1aa4398325c56ca95fda Mon Sep 17 00:00:00 2001 From: Durgadoss R Date: Fri, 21 Sep 2012 12:06:04 +0530 Subject: [PATCH 11/36] Thermal: Introduce a step_wise thermal governor This patch adds a simple step_wise governor to the generic thermal layer. This algorithm throttles the cooling devices in a linear fashion. If the 'trend' is heating, it throttles by one step. And if the thermal trend is cooling it de-throttles by one step. This actually moves the throttling logic from thermal_sys.c and puts inside step_wise.c, without any change. Signed-off-by: Durgadoss R Signed-off-by: Zhang Rui --- drivers/thermal/Kconfig | 6 ++ drivers/thermal/Makefile | 1 + drivers/thermal/step_wise.c | 194 ++++++++++++++++++++++++++++++++++++ 3 files changed, 201 insertions(+) create mode 100644 drivers/thermal/step_wise.c diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 84b0f0382e5..7e1b6de6856 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -61,3 +61,9 @@ config FAIR_SHARE depends on THERMAL help Enable this to manage platform thermals using fair-share governor. + +config STEP_WISE + bool "Step_wise thermal governor" + depends on THERMAL + help + Enable this to manage platform thermals using a simple linear diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 30540dde408..4e53c00fe89 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o obj-$(CONFIG_FAIR_SHARE) += fair_share.o +obj-$(CONFIG_STEP_WISE) += step_wise.o diff --git a/drivers/thermal/step_wise.c b/drivers/thermal/step_wise.c new file mode 100644 index 00000000000..3f9cfcf66cc --- /dev/null +++ b/drivers/thermal/step_wise.c @@ -0,0 +1,194 @@ +/* + * step_wise.c - A step-by-step Thermal throttling governor + * + * Copyright (C) 2012 Intel Corp + * Copyright (C) 2012 Durgadoss R + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include + +#include "thermal_core.h" + +/* + * If the temperature is higher than a trip point, + * a. if the trend is THERMAL_TREND_RAISING, use higher cooling + * state for this trip point + * b. if the trend is THERMAL_TREND_DROPPING, use lower cooling + * state for this trip point + */ +static unsigned long get_target_state(struct thermal_instance *instance, + enum thermal_trend trend) +{ + struct thermal_cooling_device *cdev = instance->cdev; + unsigned long cur_state; + + cdev->ops->get_cur_state(cdev, &cur_state); + + if (trend == THERMAL_TREND_RAISING) { + cur_state = cur_state < instance->upper ? + (cur_state + 1) : instance->upper; + } else if (trend == THERMAL_TREND_DROPPING) { + cur_state = cur_state > instance->lower ? + (cur_state - 1) : instance->lower; + } + + return cur_state; +} + +static void update_passive_instance(struct thermal_zone_device *tz, + enum thermal_trip_type type, int value) +{ + /* + * If value is +1, activate a passive instance. + * If value is -1, deactivate a passive instance. + */ + if (type == THERMAL_TRIP_PASSIVE || type == THERMAL_TRIPS_NONE) + tz->passive += value; +} + +static void update_instance_for_throttle(struct thermal_zone_device *tz, + int trip, enum thermal_trip_type trip_type, + enum thermal_trend trend) +{ + struct thermal_instance *instance; + + list_for_each_entry(instance, &tz->thermal_instances, tz_node) { + if (instance->trip != trip) + continue; + + instance->target = get_target_state(instance, trend); + + /* Activate a passive thermal instance */ + if (instance->target == THERMAL_NO_TARGET) + update_passive_instance(tz, trip_type, 1); + + instance->cdev->updated = false; /* cdev needs update */ + } +} + +static void update_instance_for_dethrottle(struct thermal_zone_device *tz, + int trip, enum thermal_trip_type trip_type) +{ + struct thermal_instance *instance; + struct thermal_cooling_device *cdev; + unsigned long cur_state; + + list_for_each_entry(instance, &tz->thermal_instances, tz_node) { + if (instance->trip != trip || + instance->target == THERMAL_NO_TARGET) + continue; + + cdev = instance->cdev; + cdev->ops->get_cur_state(cdev, &cur_state); + + instance->target = cur_state > instance->lower ? + (cur_state - 1) : THERMAL_NO_TARGET; + + /* Deactivate a passive thermal instance */ + if (instance->target == THERMAL_NO_TARGET) + update_passive_instance(tz, trip_type, -1); + + cdev->updated = false; /* cdev needs update */ + } +} + +static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip) +{ + long trip_temp; + enum thermal_trip_type trip_type; + enum thermal_trend trend; + + if (trip == THERMAL_TRIPS_NONE) { + trip_temp = tz->forced_passive; + trip_type = THERMAL_TRIPS_NONE; + } else { + tz->ops->get_trip_temp(tz, trip, &trip_temp); + tz->ops->get_trip_type(tz, trip, &trip_type); + } + + trend = get_tz_trend(tz, trip); + + mutex_lock(&tz->lock); + + if (tz->temperature >= trip_temp) + update_instance_for_throttle(tz, trip, trip_type, trend); + else + update_instance_for_dethrottle(tz, trip, trip_type); + + mutex_unlock(&tz->lock); +} + +/** + * step_wise_throttle - throttles devices asscciated with the given zone + * @tz - thermal_zone_device + * @trip - the trip point + * @trip_type - type of the trip point + * + * Throttling Logic: This uses the trend of the thermal zone to throttle. + * If the thermal zone is 'heating up' this throttles all the cooling + * devices associated with the zone and its particular trip point, by one + * step. If the zone is 'cooling down' it brings back the performance of + * the devices by one step. + */ +int step_wise_throttle(struct thermal_zone_device *tz, int trip) +{ + struct thermal_instance *instance; + + thermal_zone_trip_update(tz, trip); + + if (tz->forced_passive) + thermal_zone_trip_update(tz, THERMAL_TRIPS_NONE); + + mutex_lock(&tz->lock); + + list_for_each_entry(instance, &tz->thermal_instances, tz_node) + thermal_cdev_update(instance->cdev); + + mutex_unlock(&tz->lock); + + return 0; +} + +struct thermal_governor thermal_gov_step_wise = { + .name = DEFAULT_THERMAL_GOVERNOR, + .throttle = step_wise_throttle, + .owner = THIS_MODULE, +}; + +static int __init thermal_gov_step_wise_init(void) +{ + return thermal_register_governor(&thermal_gov_step_wise); +} + +static void __exit thermal_gov_step_wise_exit(void) +{ + thermal_unregister_governor(&thermal_gov_step_wise); +} + +/* This should load after thermal framework */ +fs_initcall(thermal_gov_step_wise_init); +module_exit(thermal_gov_step_wise_exit); + +MODULE_AUTHOR("Durgadoss R"); +MODULE_DESCRIPTION("A step-by-step thermal throttling governor"); +MODULE_LICENSE("GPL"); From 1cc807a234cb988d69ba18f6a3a1c68d71e54bed Mon Sep 17 00:00:00 2001 From: Durgadoss R Date: Tue, 18 Sep 2012 11:05:03 +0530 Subject: [PATCH 12/36] Thermal: Add a thermal notifier for user space This patch registers a governor which will let the user land manage the platform thermals. Whenever a trip happens, this governor just notifies the user space using kobj_uevent(). Signed-off-by: Durgadoss R Signed-off-by: Zhang Rui --- drivers/thermal/Kconfig | 6 ++++ drivers/thermal/Makefile | 1 + drivers/thermal/user_space.c | 68 ++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 drivers/thermal/user_space.c diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 7e1b6de6856..f679eea2add 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -67,3 +67,9 @@ config STEP_WISE depends on THERMAL help Enable this to manage platform thermals using a simple linear + +config USER_SPACE + bool "User_space thermal governor" + depends on THERMAL + help + Enable this to let the user space manage the platform thermals. diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 4e53c00fe89..e5f79747fa9 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o obj-$(CONFIG_FAIR_SHARE) += fair_share.o obj-$(CONFIG_STEP_WISE) += step_wise.o +obj-$(CONFIG_USER_SPACE) += user_space.o diff --git a/drivers/thermal/user_space.c b/drivers/thermal/user_space.c new file mode 100644 index 00000000000..fb246b90286 --- /dev/null +++ b/drivers/thermal/user_space.c @@ -0,0 +1,68 @@ +/* + * user_space.c - A simple user space Thermal events notifier + * + * Copyright (C) 2012 Intel Corp + * Copyright (C) 2012 Durgadoss R + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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; version 2 of the License. + * + * 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. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include + +#include "thermal_core.h" + +/** + * notify_user_space - Notifies user space about thermal events + * @tz - thermal_zone_device + * + * This function notifies the user space through UEvents. + */ +int notify_user_space(struct thermal_zone_device *tz, int trip) +{ + mutex_lock(&tz->lock); + kobject_uevent(&tz->device.kobj, KOBJ_CHANGE); + mutex_unlock(&tz->lock); + return 0; +} + +struct thermal_governor thermal_gov_user_space = { + .name = "user_space", + .throttle = notify_user_space, + .owner = THIS_MODULE, +}; + +static int __init thermal_gov_user_space_init(void) +{ + return thermal_register_governor(&thermal_gov_user_space); +} + +static void __exit thermal_gov_user_space_exit(void) +{ + thermal_unregister_governor(&thermal_gov_user_space); +} + +/* This should load after thermal framework */ +fs_initcall(thermal_gov_user_space_init); +module_exit(thermal_gov_user_space_exit); + +MODULE_AUTHOR("Durgadoss R"); +MODULE_DESCRIPTION("A user space Thermal notifier"); +MODULE_LICENSE("GPL"); From 0c01ebbfd3caf1dc132e0d93c8e7e9f742839d94 Mon Sep 17 00:00:00 2001 From: Durgadoss R Date: Tue, 18 Sep 2012 11:05:04 +0530 Subject: [PATCH 13/36] Thermal: Remove throttling logic out of thermal_sys.c This patch removes the throttling logic out of thermal_sys.c; also refactors the code into smaller functions so that are easy to read/maintain. * Seperates the handling of critical and non-critical trips * Re-arranges the set_polling and device_check methods, so that all related functions are arranged in one place. * Removes the 'do_update' and 'trip_update' method, as part of moving the throttling logic out of thermal_sys.c Signed-off-by: Durgadoss R Signed-off-by: Zhang Rui --- drivers/thermal/thermal_sys.c | 307 ++++++++++++---------------------- 1 file changed, 109 insertions(+), 198 deletions(-) diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index 9d21f82986e..64f3e973531 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -306,6 +306,115 @@ exit: mutex_unlock(&thermal_list_lock); } +static void thermal_zone_device_set_polling(struct thermal_zone_device *tz, + int delay) +{ + if (delay > 1000) + mod_delayed_work(system_freezable_wq, &tz->poll_queue, + round_jiffies(msecs_to_jiffies(delay))); + else if (delay) + mod_delayed_work(system_freezable_wq, &tz->poll_queue, + msecs_to_jiffies(delay)); + else + cancel_delayed_work(&tz->poll_queue); +} + +static void monitor_thermal_zone(struct thermal_zone_device *tz) +{ + mutex_lock(&tz->lock); + + if (tz->passive) + thermal_zone_device_set_polling(tz, tz->passive_delay); + else if (tz->polling_delay) + thermal_zone_device_set_polling(tz, tz->polling_delay); + else + thermal_zone_device_set_polling(tz, 0); + + mutex_unlock(&tz->lock); +} + +static void handle_non_critical_trips(struct thermal_zone_device *tz, + int trip, enum thermal_trip_type trip_type) +{ + tz->governor->throttle(tz, trip); +} + +static void handle_critical_trips(struct thermal_zone_device *tz, + int trip, enum thermal_trip_type trip_type) +{ + long trip_temp; + + tz->ops->get_trip_temp(tz, trip, &trip_temp); + + /* If we have not crossed the trip_temp, we do not care. */ + if (tz->temperature < trip_temp) + return; + + if (tz->ops->notify) + tz->ops->notify(tz, trip, trip_type); + + if (trip_type == THERMAL_TRIP_CRITICAL) { + pr_emerg("Critical temperature reached(%d C),shutting down\n", + tz->temperature / 1000); + orderly_poweroff(true); + } +} + +static void handle_thermal_trip(struct thermal_zone_device *tz, int trip) +{ + enum thermal_trip_type type; + + tz->ops->get_trip_type(tz, trip, &type); + + if (type == THERMAL_TRIP_CRITICAL || type == THERMAL_TRIP_HOT) + handle_critical_trips(tz, trip, type); + else + handle_non_critical_trips(tz, trip, type); + /* + * Alright, we handled this trip successfully. + * So, start monitoring again. + */ + monitor_thermal_zone(tz); +} + +static void update_temperature(struct thermal_zone_device *tz) +{ + long temp; + int ret; + + mutex_lock(&tz->lock); + + ret = tz->ops->get_temp(tz, &temp); + if (ret) { + pr_warn("failed to read out thermal zone %d\n", tz->id); + return; + } + + tz->last_temperature = tz->temperature; + tz->temperature = temp; + + mutex_unlock(&tz->lock); +} + +void thermal_zone_device_update(struct thermal_zone_device *tz) +{ + int count; + + update_temperature(tz); + + for (count = 0; count < tz->trips; count++) + handle_thermal_trip(tz, count); +} +EXPORT_SYMBOL(thermal_zone_device_update); + +static void thermal_zone_device_check(struct work_struct *work) +{ + struct thermal_zone_device *tz = container_of(work, struct + thermal_zone_device, + poll_queue.work); + thermal_zone_device_update(tz); +} + /* sys I/F for thermal zone */ #define to_thermal_zone(_dev) \ @@ -936,27 +1045,6 @@ thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz) } #endif -static void thermal_zone_device_set_polling(struct thermal_zone_device *tz, - int delay) -{ - if (delay > 1000) - mod_delayed_work(system_freezable_wq, &tz->poll_queue, - round_jiffies(msecs_to_jiffies(delay))); - else if (delay) - mod_delayed_work(system_freezable_wq, &tz->poll_queue, - msecs_to_jiffies(delay)); - else - cancel_delayed_work(&tz->poll_queue); -} - -static void thermal_zone_device_check(struct work_struct *work) -{ - struct thermal_zone_device *tz = container_of(work, struct - thermal_zone_device, - poll_queue.work); - thermal_zone_device_update(tz); -} - /** * thermal_zone_bind_cooling_device - bind a cooling device to a thermal zone * @tz: thermal zone device @@ -1280,183 +1368,6 @@ void thermal_cdev_update(struct thermal_cooling_device *cdev) } EXPORT_SYMBOL(thermal_cdev_update); -static void thermal_zone_do_update(struct thermal_zone_device *tz) -{ - struct thermal_instance *instance; - - list_for_each_entry(instance, &tz->thermal_instances, tz_node) - thermal_cdev_update(instance->cdev); -} - -/* - * Cooling algorithm for both active and passive cooling - * - * 1. if the temperature is higher than a trip point, - * a. if the trend is THERMAL_TREND_RAISING, use higher cooling - * state for this trip point - * b. if the trend is THERMAL_TREND_DROPPING, use lower cooling - * state for this trip point - * - * 2. if the temperature is lower than a trip point, use lower - * cooling state for this trip point - * - * Note that this behaves the same as the previous passive cooling - * algorithm. - */ - -static void thermal_zone_trip_update(struct thermal_zone_device *tz, - int trip, long temp) -{ - struct thermal_instance *instance; - struct thermal_cooling_device *cdev = NULL; - unsigned long cur_state, max_state; - long trip_temp; - enum thermal_trip_type trip_type; - enum thermal_trend trend; - - if (trip == THERMAL_TRIPS_NONE) { - trip_temp = tz->forced_passive; - trip_type = THERMAL_TRIPS_NONE; - } else { - tz->ops->get_trip_temp(tz, trip, &trip_temp); - tz->ops->get_trip_type(tz, trip, &trip_type); - } - - if (!tz->ops->get_trend || tz->ops->get_trend(tz, trip, &trend)) { - /* - * compare the current temperature and previous temperature - * to get the thermal trend, if no special requirement - */ - if (tz->temperature > tz->last_temperature) - trend = THERMAL_TREND_RAISING; - else if (tz->temperature < tz->last_temperature) - trend = THERMAL_TREND_DROPPING; - else - trend = THERMAL_TREND_STABLE; - } - - if (temp >= trip_temp) { - list_for_each_entry(instance, &tz->thermal_instances, tz_node) { - if (instance->trip != trip) - continue; - - cdev = instance->cdev; - - cdev->ops->get_cur_state(cdev, &cur_state); - cdev->ops->get_max_state(cdev, &max_state); - - if (trend == THERMAL_TREND_RAISING) { - cur_state = cur_state < instance->upper ? - (cur_state + 1) : instance->upper; - } else if (trend == THERMAL_TREND_DROPPING) { - cur_state = cur_state > instance->lower ? - (cur_state - 1) : instance->lower; - } - - /* activate a passive thermal instance */ - if ((trip_type == THERMAL_TRIP_PASSIVE || - trip_type == THERMAL_TRIPS_NONE) && - instance->target == THERMAL_NO_TARGET) - tz->passive++; - - instance->target = cur_state; - cdev->updated = false; /* cooling device needs update */ - } - } else { /* below trip */ - list_for_each_entry(instance, &tz->thermal_instances, tz_node) { - if (instance->trip != trip) - continue; - - /* Do not use the inactive thermal instance */ - if (instance->target == THERMAL_NO_TARGET) - continue; - cdev = instance->cdev; - cdev->ops->get_cur_state(cdev, &cur_state); - - cur_state = cur_state > instance->lower ? - (cur_state - 1) : THERMAL_NO_TARGET; - - /* deactivate a passive thermal instance */ - if ((trip_type == THERMAL_TRIP_PASSIVE || - trip_type == THERMAL_TRIPS_NONE) && - cur_state == THERMAL_NO_TARGET) - tz->passive--; - instance->target = cur_state; - cdev->updated = false; /* cooling device needs update */ - } - } - - return; -} -/** - * thermal_zone_device_update - force an update of a thermal zone's state - * @ttz: the thermal zone to update - */ - -void thermal_zone_device_update(struct thermal_zone_device *tz) -{ - int count, ret = 0; - long temp, trip_temp; - enum thermal_trip_type trip_type; - - mutex_lock(&tz->lock); - - if (tz->ops->get_temp(tz, &temp)) { - /* get_temp failed - retry it later */ - pr_warn("failed to read out thermal zone %d\n", tz->id); - goto leave; - } - - tz->last_temperature = tz->temperature; - tz->temperature = temp; - - for (count = 0; count < tz->trips; count++) { - tz->ops->get_trip_type(tz, count, &trip_type); - tz->ops->get_trip_temp(tz, count, &trip_temp); - - switch (trip_type) { - case THERMAL_TRIP_CRITICAL: - if (temp >= trip_temp) { - if (tz->ops->notify) - ret = tz->ops->notify(tz, count, - trip_type); - if (!ret) { - pr_emerg("Critical temperature reached (%ld C), shutting down\n", - temp/1000); - orderly_poweroff(true); - } - } - break; - case THERMAL_TRIP_HOT: - if (temp >= trip_temp) - if (tz->ops->notify) - tz->ops->notify(tz, count, trip_type); - break; - case THERMAL_TRIP_ACTIVE: - thermal_zone_trip_update(tz, count, temp); - break; - case THERMAL_TRIP_PASSIVE: - if (temp >= trip_temp || tz->passive) - thermal_zone_trip_update(tz, count, temp); - break; - } - } - - if (tz->forced_passive) - thermal_zone_trip_update(tz, THERMAL_TRIPS_NONE, temp); - thermal_zone_do_update(tz); - -leave: - if (tz->passive) - thermal_zone_device_set_polling(tz, tz->passive_delay); - else if (tz->polling_delay) - thermal_zone_device_set_polling(tz, tz->polling_delay); - else - thermal_zone_device_set_polling(tz, 0); - mutex_unlock(&tz->lock); -} -EXPORT_SYMBOL(thermal_zone_device_update); - /** * create_trip_attrs - create attributes for trip points * @tz: the thermal zone device From f2b4caafd4aa64c9438b0a94af78b127228e6905 Mon Sep 17 00:00:00 2001 From: Durgadoss R Date: Tue, 18 Sep 2012 11:05:05 +0530 Subject: [PATCH 14/36] Thermal: Add a notification API This patch adds a notification API which the sensor drivers' can use to notify the framework. The framework then takes care of the throttling according to the configured policy. Signed-off-by: Durgadoss R Signed-off-by: Zhang Rui --- drivers/thermal/thermal_sys.c | 18 ++++++++++++++++++ include/linux/thermal.h | 1 + 2 files changed, 19 insertions(+) diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index 64f3e973531..a69f24c4414 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -1368,6 +1368,24 @@ void thermal_cdev_update(struct thermal_cooling_device *cdev) } EXPORT_SYMBOL(thermal_cdev_update); +/** + * notify_thermal_framework - Sensor drivers use this API to notify framework + * @tz: thermal zone device + * @trip: indicates which trip point has been crossed + * + * This function handles the trip events from sensor drivers. It starts + * throttling the cooling devices according to the policy configured. + * For CRITICAL and HOT trip points, this notifies the respective drivers, + * and does actual throttling for other trip points i.e ACTIVE and PASSIVE. + * The throttling policy is based on the configured platform data; if no + * platform data is provided, this uses the step_wise throttling policy. + */ +void notify_thermal_framework(struct thermal_zone_device *tz, int trip) +{ + handle_thermal_trip(tz, trip); +} +EXPORT_SYMBOL(notify_thermal_framework); + /** * create_trip_attrs - create attributes for trip points * @tz: the thermal zone device diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 2bd91584181..807f2146fe3 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -232,6 +232,7 @@ int get_tz_trend(struct thermal_zone_device *, int); struct thermal_instance *get_thermal_instance(struct thermal_zone_device *, struct thermal_cooling_device *, int); void thermal_cdev_update(struct thermal_cooling_device *); +void notify_thermal_framework(struct thermal_zone_device *, int); int thermal_register_governor(struct thermal_governor *); void thermal_unregister_governor(struct thermal_governor *); From 6ea083b18d39b35730e59587128cfd6f68bd30bc Mon Sep 17 00:00:00 2001 From: Durgadoss R Date: Tue, 18 Sep 2012 11:05:06 +0530 Subject: [PATCH 15/36] Thermal: Add documentation for platform layer data This patch adds documentation for thermal_bind_params and thermal_zone_params structures. Also, adds details on EXPORT_SYMBOL APIs. Signed-off-by: Durgadoss R Signed-off-by: Zhang Rui --- Documentation/thermal/sysfs-api.txt | 64 +++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/Documentation/thermal/sysfs-api.txt b/Documentation/thermal/sysfs-api.txt index ca1a1a34970..88c02334e35 100644 --- a/Documentation/thermal/sysfs-api.txt +++ b/Documentation/thermal/sysfs-api.txt @@ -112,6 +112,29 @@ temperature) and throttle appropriate devices. trip: indicates which trip point the cooling devices is associated with in this thermal zone. +1.4 Thermal Zone Parameters +1.4.1 struct thermal_bind_params + This structure defines the following parameters that are used to bind + a zone with a cooling device for a particular trip point. + .cdev: The cooling device pointer + .weight: The 'influence' of a particular cooling device on this zone. + This is on a percentage scale. The sum of all these weights + (for a particular zone) cannot exceed 100. + .trip_mask:This is a bit mask that gives the binding relation between + this thermal zone and cdev, for a particular trip point. + If nth bit is set, then the cdev and thermal zone are bound + for trip point n. + .match: This call back returns success(0) if the 'tz and cdev' need to + be bound, as per platform data. +1.4.2 struct thermal_zone_params + This structure defines the platform level parameters for a thermal zone. + This data, for each thermal zone should come from the platform layer. + This is an optional feature where some platforms can choose not to + provide this data. + .governor_name: Name of the thermal governor used for this zone + .num_tbps: Number of thermal_bind_params entries for this zone + .tbp: thermal_bind_params entries + 2. sysfs attributes structure RO read only value @@ -126,6 +149,7 @@ Thermal zone device sys I/F, created once it's registered: |---type: Type of the thermal zone |---temp: Current temperature |---mode: Working mode of the thermal zone + |---policy: Thermal governor used for this zone |---trip_point_[0-*]_temp: Trip point temperature |---trip_point_[0-*]_type: Trip point type |---trip_point_[0-*]_hyst: Hysteresis value for this trip point @@ -187,6 +211,10 @@ mode charge of the thermal management. RW, Optional +policy + One of the various thermal governors used for a particular zone. + RW, Required + trip_point_[0-*]_temp The temperature above which trip point will be fired. Unit: millidegree Celsius @@ -264,6 +292,7 @@ method, the sys I/F structure will be built like this: |---type: acpitz |---temp: 37000 |---mode: enabled + |---policy: step_wise |---trip_point_0_temp: 100000 |---trip_point_0_type: critical |---trip_point_1_temp: 80000 @@ -305,3 +334,38 @@ to a thermal_zone_device when it registers itself with the framework. The event will be one of:{THERMAL_AUX0, THERMAL_AUX1, THERMAL_CRITICAL, THERMAL_DEV_FAULT}. Notification can be sent when the current temperature crosses any of the configured thresholds. + +5. Export Symbol APIs: + +5.1: get_tz_trend: +This function returns the trend of a thermal zone, i.e the rate of change +of temperature of the thermal zone. Ideally, the thermal sensor drivers +are supposed to implement the callback. If they don't, the thermal +framework calculated the trend by comparing the previous and the current +temperature values. + +5.2:get_thermal_instance: +This function returns the thermal_instance corresponding to a given +{thermal_zone, cooling_device, trip_point} combination. Returns NULL +if such an instance does not exist. + +5.3:notify_thermal_framework: +This function handles the trip events from sensor drivers. It starts +throttling the cooling devices according to the policy configured. +For CRITICAL and HOT trip points, this notifies the respective drivers, +and does actual throttling for other trip points i.e ACTIVE and PASSIVE. +The throttling policy is based on the configured platform data; if no +platform data is provided, this uses the step_wise throttling policy. + +5.4:thermal_cdev_update: +This function serves as an arbitrator to set the state of a cooling +device. It sets the cooling device to the deepest cooling state if +possible. + +5.5:thermal_register_governor: +This function lets the various thermal governors to register themselves +with the Thermal framework. At run time, depending on a zone's platform +data, a particular governor is used for throttling. + +5.6:thermal_unregister_governor: +This function unregisters a governor from the thermal framework. From a56757af8e7d7d8bfb6317c25b6a8809abfceb9a Mon Sep 17 00:00:00 2001 From: Durgadoss R Date: Fri, 21 Sep 2012 14:32:24 +0530 Subject: [PATCH 16/36] Thermal: Provide option to choose default thermal governor This patch provides option to choose the default thermal governor. If no option is provided, the step_wise governor is selected by default. Signed-off-by: Durgadoss R Signed-off-by: Zhang Rui --- drivers/thermal/Kconfig | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index f679eea2add..266c15eb89e 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -73,3 +73,34 @@ config USER_SPACE depends on THERMAL help Enable this to let the user space manage the platform thermals. + +choice + prompt "Default Thermal governor" + depends on THERMAL + default THERMAL_DEFAULT_GOV_STEP_WISE + help + This option sets which thermal governor shall be loaded at + startup. If in doubt, select 'step_wise'. + +config THERMAL_DEFAULT_GOV_STEP_WISE + bool "step_wise" + select STEP_WISE + help + Use the step_wise governor as default. This throttles the + devices one step at a time. + +config THERMAL_DEFAULT_GOV_FAIR_SHARE + bool "fair_share" + select FAIR_SHARE + help + Use the fair_share governor as default. This throttles the + devices based on their 'contribution' to a zone. The + contribution should be provided through platform data. + +config THERMAL_DEFAULT_GOV_USER_SPACE + bool "user_space" + select USER_SPACE + help + Select this if you want to let the user space manage the + platform thermals. +endchoice From 791700cdfc2453eb927ff7875d183d9808d89bfb Mon Sep 17 00:00:00 2001 From: Hugh Dickins Date: Thu, 27 Sep 2012 16:48:28 -0700 Subject: [PATCH 17/36] Thermal: Fix oops and unlocking in thermal_sys.c This patch fixes the following mutex and NULL pointer problems in thermal_sys.c: * mutex_unlock fix in update_temperature function * mutex_unlock fix in bind_cdev function * Correct early return to continue in bind_cdev function * NULL check fix in bind_cdev function * NULL check fix in bind_tz function Reported-by: Dan Carpenter Reported-by: Sedat Dilek Reported-by: Hugh Dickins Signed-off-by: Durgadoss R Signed-off-by: Sedat Dilek Signed-off-by: Hugh Dickins Signed-off-by: Zhang Rui --- drivers/thermal/thermal_sys.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index a69f24c4414..8f0f37bb282 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -252,8 +252,8 @@ static void bind_cdev(struct thermal_cooling_device *cdev) } tzp = pos->tzp; - if (!tzp->tbp) - return; + if (!tzp || !tzp->tbp) + continue; for (i = 0; i < tzp->num_tbps; i++) { if (tzp->tbp[i].cdev || !tzp->tbp[i].match) @@ -289,7 +289,7 @@ static void bind_tz(struct thermal_zone_device *tz) goto exit; } - if (!tzp->tbp) + if (!tzp || !tzp->tbp) goto exit; list_for_each_entry(pos, &thermal_cdev_list, node) { @@ -387,12 +387,13 @@ static void update_temperature(struct thermal_zone_device *tz) ret = tz->ops->get_temp(tz, &temp); if (ret) { pr_warn("failed to read out thermal zone %d\n", tz->id); - return; + goto exit; } tz->last_temperature = tz->temperature; tz->temperature = temp; +exit: mutex_unlock(&tz->lock); } From b88a497701f84a0edc6631a79d21087e049bbcb1 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 27 Sep 2012 16:28:12 +0530 Subject: [PATCH 18/36] thermal: step_wise: Add missing static storage class specifiers Fixes the following sparse warnings: drivers/thermal/step_wise.c:153:5: warning: symbol 'step_wise_throttle' was not declared. Should it be static? drivers/thermal/step_wise.c:172:25: warning: symbol 'thermal_gov_step_wise' was not declared. Should it be static? Signed-off-by: Sachin Kamat Acked-by: Durgadoss R Signed-off-by: Zhang Rui --- drivers/thermal/step_wise.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/thermal/step_wise.c b/drivers/thermal/step_wise.c index 3f9cfcf66cc..1242cffed8b 100644 --- a/drivers/thermal/step_wise.c +++ b/drivers/thermal/step_wise.c @@ -150,7 +150,7 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip) * step. If the zone is 'cooling down' it brings back the performance of * the devices by one step. */ -int step_wise_throttle(struct thermal_zone_device *tz, int trip) +static int step_wise_throttle(struct thermal_zone_device *tz, int trip) { struct thermal_instance *instance; @@ -169,7 +169,7 @@ int step_wise_throttle(struct thermal_zone_device *tz, int trip) return 0; } -struct thermal_governor thermal_gov_step_wise = { +static struct thermal_governor thermal_gov_step_wise = { .name = DEFAULT_THERMAL_GOVERNOR, .throttle = step_wise_throttle, .owner = THIS_MODULE, From 621769334b6a9be702e4f9e3c56a77da1a112377 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 27 Sep 2012 16:57:53 +0530 Subject: [PATCH 19/36] thermal: fair_share: Add missing static storage class specifiers Fixes the following sparse warnings: drivers/thermal/fair_share.c:80:5: warning: symbol 'fair_share_throttle' was not declared. Should it be static? drivers/thermal/fair_share.c:111:25: warning: symbol 'thermal_gov_fair_share' was not declared. Should it be static? Signed-off-by: Sachin Kamat Acked-by: Durgadoss R Signed-off-by: Zhang Rui --- drivers/thermal/fair_share.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/thermal/fair_share.c b/drivers/thermal/fair_share.c index 5d3a7a3e6a7..792479f2b64 100644 --- a/drivers/thermal/fair_share.c +++ b/drivers/thermal/fair_share.c @@ -77,7 +77,7 @@ static long get_target_state(struct thermal_zone_device *tz, * (Heavily assumes the trip points are in ascending order) * new_state of cooling device = P3 * P2 * P1 */ -int fair_share_throttle(struct thermal_zone_device *tz, int trip) +static int fair_share_throttle(struct thermal_zone_device *tz, int trip) { const struct thermal_zone_params *tzp; struct thermal_cooling_device *cdev; @@ -108,7 +108,7 @@ int fair_share_throttle(struct thermal_zone_device *tz, int trip) return 0; } -struct thermal_governor thermal_gov_fair_share = { +static struct thermal_governor thermal_gov_fair_share = { .name = "fair_share", .throttle = fair_share_throttle, .owner = THIS_MODULE, From e15cb144939c84947b4fbcf191cda59d71cd0b34 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 27 Sep 2012 16:57:54 +0530 Subject: [PATCH 20/36] thermal: user_space: Add missing static storage class specifiers Fixes the following sparse warnings: drivers/thermal/user_space.c:38:5: warning: symbol 'notify_user_space' was not declared. Should it be static? drivers/thermal/user_space.c:46:25: warning: symbol 'thermal_gov_user_space' was not declared. Should it be static? Signed-off-by: Sachin Kamat Acked-by: Durgadoss R Signed-off-by: Zhang Rui --- drivers/thermal/user_space.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/thermal/user_space.c b/drivers/thermal/user_space.c index fb246b90286..6bbb380b6d1 100644 --- a/drivers/thermal/user_space.c +++ b/drivers/thermal/user_space.c @@ -35,7 +35,7 @@ * * This function notifies the user space through UEvents. */ -int notify_user_space(struct thermal_zone_device *tz, int trip) +static int notify_user_space(struct thermal_zone_device *tz, int trip) { mutex_lock(&tz->lock); kobject_uevent(&tz->device.kobj, KOBJ_CHANGE); @@ -43,7 +43,7 @@ int notify_user_space(struct thermal_zone_device *tz, int trip) return 0; } -struct thermal_governor thermal_gov_user_space = { +static struct thermal_governor thermal_gov_user_space = { .name = "user_space", .throttle = notify_user_space, .owner = THIS_MODULE, From 4e8e2f644e11a7d65381967a916ae7cac76a735b Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 2 Oct 2012 23:51:09 -0700 Subject: [PATCH 21/36] thermal: rcar_thermal: remove explicitly used devm_kfree/iounap() devm_kfree and devm_iounmap should not have to be explicitly used Signed-off-by: Kuninori Morimoto Signed-off-by: Zhang Rui --- drivers/thermal/rcar_thermal.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 762f6373d50..81dce23828f 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -185,7 +185,6 @@ static int rcar_thermal_probe(struct platform_device *pdev) struct thermal_zone_device *zone; struct rcar_thermal_priv *priv; struct resource *res; - int ret; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { @@ -206,16 +205,14 @@ static int rcar_thermal_probe(struct platform_device *pdev) res->start, resource_size(res)); if (!priv->base) { dev_err(&pdev->dev, "Unable to ioremap thermal register\n"); - ret = -ENOMEM; - goto error_free_priv; + return -ENOMEM; } zone = thermal_zone_device_register("rcar_thermal", 0, 0, priv, &rcar_thermal_zone_ops, NULL, 0, 0); if (IS_ERR(zone)) { dev_err(&pdev->dev, "thermal zone device is NULL\n"); - ret = PTR_ERR(zone); - goto error_iounmap; + return PTR_ERR(zone); } platform_set_drvdata(pdev, zone); @@ -223,26 +220,15 @@ static int rcar_thermal_probe(struct platform_device *pdev) dev_info(&pdev->dev, "proved\n"); return 0; - -error_iounmap: - devm_iounmap(&pdev->dev, priv->base); -error_free_priv: - devm_kfree(&pdev->dev, priv); - - return ret; } static int rcar_thermal_remove(struct platform_device *pdev) { struct thermal_zone_device *zone = platform_get_drvdata(pdev); - struct rcar_thermal_priv *priv = zone->devdata; thermal_zone_device_unregister(zone); platform_set_drvdata(pdev, NULL); - devm_iounmap(&pdev->dev, priv->base); - devm_kfree(&pdev->dev, priv); - return 0; } From 6b6519df84afcda294c487bccb83a8caecb48acd Mon Sep 17 00:00:00 2001 From: "hongbo.zhang" Date: Tue, 30 Oct 2012 17:48:57 +0100 Subject: [PATCH 22/36] Thermal: add indent for code alignment. The curly bracket should be aligned with corresponding if else statements. Signed-off-by: hongbo.zhang Reviewed-by: Viresh Kumar Signed-off-by: Zhang Rui --- drivers/thermal/cpu_cooling.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index cc1c930a90e..b6b4c2a3f5b 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -369,7 +369,7 @@ struct thermal_cooling_device *cpufreq_cooling_register( if (min != policy.cpuinfo.min_freq || max != policy.cpuinfo.max_freq) return ERR_PTR(-EINVAL); -} + } } cpufreq_dev = kzalloc(sizeof(struct cpufreq_cooling_device), GFP_KERNEL); From 9c51b05a7852183ba9654ca850bee97d38e948d5 Mon Sep 17 00:00:00 2001 From: "hongbo.zhang" Date: Tue, 30 Oct 2012 17:48:58 +0100 Subject: [PATCH 23/36] Thermal: fix bug of counting cpu frequencies. In the while loop for counting cpu frequencies, if table[i].frequency equals CPUFREQ_ENTRY_INVALID, index i won't be increased, so this leads to an endless loop, what's more the index i cannot be referred as cpu frequencies number if there is CPUFREQ_ENTRY_INVALID case. Signed-off-by: hongbo.zhang Reviewed-by: Viresh Kumar Reviewed-by: Amit Daniel Kachhap Signed-off-by: Zhang Rui --- drivers/thermal/cpu_cooling.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index b6b4c2a3f5b..bfd62b7f32a 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -245,6 +245,7 @@ static int cpufreq_get_max_state(struct thermal_cooling_device *cdev, struct cpumask *maskPtr; unsigned int cpu; struct cpufreq_frequency_table *table; + unsigned long count = 0; mutex_lock(&cooling_cpufreq_lock); list_for_each_entry(cpufreq_device, &cooling_cpufreq_list, node) { @@ -263,13 +264,14 @@ static int cpufreq_get_max_state(struct thermal_cooling_device *cdev, goto return_get_max_state; } - while (table[i].frequency != CPUFREQ_TABLE_END) { + for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) { if (table[i].frequency == CPUFREQ_ENTRY_INVALID) continue; - i++; + count++; } - if (i > 0) { - *state = --i; + + if (count > 0) { + *state = --count; ret = 0; } From 160b7d8048b87cb594e1a22b5345b468b6c2c40e Mon Sep 17 00:00:00 2001 From: "hongbo.zhang" Date: Tue, 30 Oct 2012 17:48:59 +0100 Subject: [PATCH 24/36] Thermal: Remove the cooling_cpufreq_list. Problem of using this list is that the cpufreq_get_max_state callback will be called when register cooling device by thermal_cooling_device_register, but this list isn't ready at this moment. What's more, there is no need to maintain such a list, we can get cpufreq_cooling_device instance by the private thermal_cooling_device.devdata. Signed-off-by: hongbo.zhang Reviewed-by: Francesco Lavra Reviewed-by: Amit Daniel Kachhap Signed-off-by: Zhang Rui --- drivers/thermal/cpu_cooling.c | 91 ++++++++--------------------------- 1 file changed, 19 insertions(+), 72 deletions(-) diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index bfd62b7f32a..392d57d8590 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -58,8 +58,9 @@ struct cpufreq_cooling_device { }; static LIST_HEAD(cooling_cpufreq_list); static DEFINE_IDR(cpufreq_idr); +static DEFINE_MUTEX(cooling_cpufreq_lock); -static struct mutex cooling_cpufreq_lock; +static unsigned int cpufreq_dev_count; /* notify_table passes value to the CPUFREQ_ADJUST callback function. */ #define NOTIFY_INVALID NULL @@ -240,28 +241,18 @@ static int cpufreq_thermal_notifier(struct notifier_block *nb, static int cpufreq_get_max_state(struct thermal_cooling_device *cdev, unsigned long *state) { - int ret = -EINVAL, i = 0; - struct cpufreq_cooling_device *cpufreq_device; - struct cpumask *maskPtr; + struct cpufreq_cooling_device *cpufreq_device = cdev->devdata; + struct cpumask *maskPtr = &cpufreq_device->allowed_cpus; unsigned int cpu; struct cpufreq_frequency_table *table; unsigned long count = 0; + int i = 0; - mutex_lock(&cooling_cpufreq_lock); - list_for_each_entry(cpufreq_device, &cooling_cpufreq_list, node) { - if (cpufreq_device && cpufreq_device->cool_dev == cdev) - break; - } - if (cpufreq_device == NULL) - goto return_get_max_state; - - maskPtr = &cpufreq_device->allowed_cpus; cpu = cpumask_any(maskPtr); table = cpufreq_frequency_get_table(cpu); if (!table) { *state = 0; - ret = 0; - goto return_get_max_state; + return 0; } for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) { @@ -272,12 +263,10 @@ static int cpufreq_get_max_state(struct thermal_cooling_device *cdev, if (count > 0) { *state = --count; - ret = 0; + return 0; } -return_get_max_state: - mutex_unlock(&cooling_cpufreq_lock); - return ret; + return -EINVAL; } /** @@ -288,20 +277,10 @@ return_get_max_state: static int cpufreq_get_cur_state(struct thermal_cooling_device *cdev, unsigned long *state) { - int ret = -EINVAL; - struct cpufreq_cooling_device *cpufreq_device; + struct cpufreq_cooling_device *cpufreq_device = cdev->devdata; - mutex_lock(&cooling_cpufreq_lock); - list_for_each_entry(cpufreq_device, &cooling_cpufreq_list, node) { - if (cpufreq_device && cpufreq_device->cool_dev == cdev) { - *state = cpufreq_device->cpufreq_state; - ret = 0; - break; - } - } - mutex_unlock(&cooling_cpufreq_lock); - - return ret; + *state = cpufreq_device->cpufreq_state; + return 0; } /** @@ -312,22 +291,9 @@ static int cpufreq_get_cur_state(struct thermal_cooling_device *cdev, static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) { - int ret = -EINVAL; - struct cpufreq_cooling_device *cpufreq_device; + struct cpufreq_cooling_device *cpufreq_device = cdev->devdata; - mutex_lock(&cooling_cpufreq_lock); - list_for_each_entry(cpufreq_device, &cooling_cpufreq_list, node) { - if (cpufreq_device && cpufreq_device->cool_dev == cdev) { - ret = 0; - break; - } - } - if (!ret) - ret = cpufreq_apply_cooling(cpufreq_device, state); - - mutex_unlock(&cooling_cpufreq_lock); - - return ret; + return cpufreq_apply_cooling(cpufreq_device, state); } /* Bind cpufreq callbacks to thermal cooling device ops */ @@ -351,14 +317,11 @@ struct thermal_cooling_device *cpufreq_cooling_register( { struct thermal_cooling_device *cool_dev; struct cpufreq_cooling_device *cpufreq_dev = NULL; - unsigned int cpufreq_dev_count = 0, min = 0, max = 0; + unsigned int min = 0, max = 0; char dev_name[THERMAL_NAME_LENGTH]; int ret = 0, i; struct cpufreq_policy policy; - list_for_each_entry(cpufreq_dev, &cooling_cpufreq_list, node) - cpufreq_dev_count++; - /*Verify that all the clip cpus have same freq_min, freq_max limit*/ for_each_cpu(i, clip_cpus) { /*continue if cpufreq policy not found and not return error*/ @@ -380,9 +343,6 @@ struct thermal_cooling_device *cpufreq_cooling_register( cpumask_copy(&cpufreq_dev->allowed_cpus, clip_cpus); - if (cpufreq_dev_count == 0) - mutex_init(&cooling_cpufreq_lock); - ret = get_idr(&cpufreq_idr, &cpufreq_dev->id); if (ret) { kfree(cpufreq_dev); @@ -401,12 +361,12 @@ struct thermal_cooling_device *cpufreq_cooling_register( cpufreq_dev->cool_dev = cool_dev; cpufreq_dev->cpufreq_state = 0; mutex_lock(&cooling_cpufreq_lock); - list_add_tail(&cpufreq_dev->node, &cooling_cpufreq_list); /* Register the notifier for first cpufreq cooling device */ if (cpufreq_dev_count == 0) cpufreq_register_notifier(&thermal_cpufreq_notifier_block, CPUFREQ_POLICY_NOTIFIER); + cpufreq_dev_count++; mutex_unlock(&cooling_cpufreq_lock); return cool_dev; @@ -419,33 +379,20 @@ EXPORT_SYMBOL(cpufreq_cooling_register); */ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) { - struct cpufreq_cooling_device *cpufreq_dev = NULL; - unsigned int cpufreq_dev_count = 0; + struct cpufreq_cooling_device *cpufreq_dev = cdev->devdata; mutex_lock(&cooling_cpufreq_lock); - list_for_each_entry(cpufreq_dev, &cooling_cpufreq_list, node) { - if (cpufreq_dev && cpufreq_dev->cool_dev == cdev) - break; - cpufreq_dev_count++; - } - - if (!cpufreq_dev || cpufreq_dev->cool_dev != cdev) { - mutex_unlock(&cooling_cpufreq_lock); - return; - } - - list_del(&cpufreq_dev->node); + cpufreq_dev_count--; /* Unregister the notifier for the last cpufreq cooling device */ - if (cpufreq_dev_count == 1) { + if (cpufreq_dev_count == 0) { cpufreq_unregister_notifier(&thermal_cpufreq_notifier_block, CPUFREQ_POLICY_NOTIFIER); } mutex_unlock(&cooling_cpufreq_lock); + thermal_cooling_device_unregister(cpufreq_dev->cool_dev); release_idr(&cpufreq_idr, cpufreq_dev->id); - if (cpufreq_dev_count == 1) - mutex_destroy(&cooling_cpufreq_lock); kfree(cpufreq_dev); } EXPORT_SYMBOL(cpufreq_cooling_unregister); From a0f846c23cf8e52da054abb52294d54e4b1986f9 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 15 Nov 2012 12:19:44 +0530 Subject: [PATCH 25/36] thermal: cpu_cooling: Make 'notify_device' static Silences the following sparse warning: drivers/thermal/cpu_cooling.c:67:31: warning: symbol 'notify_device' was not declared. Should it be static? Signed-off-by: Sachin Kamat Signed-off-by: Zhang Rui --- drivers/thermal/cpu_cooling.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index 392d57d8590..6f94c2cac1f 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -64,7 +64,7 @@ static unsigned int cpufreq_dev_count; /* notify_table passes value to the CPUFREQ_ADJUST callback function. */ #define NOTIFY_INVALID NULL -struct cpufreq_cooling_device *notify_device; +static struct cpufreq_cooling_device *notify_device; /** * get_idr - function to get a unique id. From 72e198978223f2020f7f59a6e2520f2b7d005e72 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Thu, 15 Nov 2012 09:16:20 +0800 Subject: [PATCH 26/36] Refactor drivers/thermal/Kconfig Signed-off-by: Zhang Rui --- drivers/thermal/Kconfig | 115 ++++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 58 deletions(-) diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 266c15eb89e..937a23da66e 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -13,70 +13,15 @@ menuconfig THERMAL All platforms with ACPI thermal support can use this driver. If you want this support, you should say Y or M here. +if THERMAL + config THERMAL_HWMON bool - depends on THERMAL depends on HWMON=y || HWMON=THERMAL default y -config CPU_THERMAL - bool "generic cpu cooling support" - depends on THERMAL && CPU_FREQ - select CPU_FREQ_TABLE - help - This implements the generic cpu cooling mechanism through frequency - reduction, cpu hotplug and any other ways of reducing temperature. An - ACPI version of this already exists(drivers/acpi/processor_thermal.c). - This will be useful for platforms using the generic thermal interface - and not the ACPI interface. - If you want this support, you should say Y here. - -config SPEAR_THERMAL - bool "SPEAr thermal sensor driver" - depends on THERMAL - depends on PLAT_SPEAR - depends on OF - help - Enable this to plug the SPEAr thermal sensor driver into the Linux - thermal framework - -config RCAR_THERMAL - tristate "Renesas R-Car thermal driver" - depends on THERMAL - depends on ARCH_SHMOBILE - help - Enable this to plug the R-Car thermal sensor driver into the Linux - thermal framework - -config EXYNOS_THERMAL - tristate "Temperature sensor on Samsung EXYNOS" - depends on (ARCH_EXYNOS4 || ARCH_EXYNOS5) && THERMAL - select CPU_FREQ_TABLE - help - If you say yes here you get support for TMU (Thermal Managment - Unit) on SAMSUNG EXYNOS series of SoC. - -config FAIR_SHARE - bool "Fair-share thermal governor" - depends on THERMAL - help - Enable this to manage platform thermals using fair-share governor. - -config STEP_WISE - bool "Step_wise thermal governor" - depends on THERMAL - help - Enable this to manage platform thermals using a simple linear - -config USER_SPACE - bool "User_space thermal governor" - depends on THERMAL - help - Enable this to let the user space manage the platform thermals. - choice prompt "Default Thermal governor" - depends on THERMAL default THERMAL_DEFAULT_GOV_STEP_WISE help This option sets which thermal governor shall be loaded at @@ -102,5 +47,59 @@ config THERMAL_DEFAULT_GOV_USER_SPACE select USER_SPACE help Select this if you want to let the user space manage the - platform thermals. + lpatform thermals. + endchoice + +config FAIR_SHARE + bool "Fair-share thermal governor" + help + Enable this to manage platform thermals using fair-share governor. + +config STEP_WISE + bool "Step_wise thermal governor" + help + Enable this to manage platform thermals using a simple linear + +config USER_SPACE + bool "User_space thermal governor" + help + Enable this to let the user space manage the platform thermals. + +config CPU_THERMAL + bool "generic cpu cooling support" + depends on CPU_FREQ + select CPU_FREQ_TABLE + help + This implements the generic cpu cooling mechanism through frequency + reduction, cpu hotplug and any other ways of reducing temperature. An + ACPI version of this already exists(drivers/acpi/processor_thermal.c). + This will be useful for platforms using the generic thermal interface + and not the ACPI interface. + If you want this support, you should say Y here. + +config SPEAR_THERMAL + bool "SPEAr thermal sensor driver" + depends on PLAT_SPEAR + depends on OF + help + Enable this to plug the SPEAr thermal sensor driver into the Linux + thermal framework + +config RCAR_THERMAL + tristate "Renesas R-Car thermal driver" + depends on ARCH_SHMOBILE + help + Enable this to plug the R-Car thermal sensor driver into the Linux + thermal framework + +config EXYNOS_THERMAL + tristate "Temperature sensor on Samsung EXYNOS" + depends on (ARCH_EXYNOS4 || ARCH_EXYNOS5) + select CPU_FREQ_TABLE + help + If you say yes here you get support for TMU (Thermal Managment + Unit) on SAMSUNG EXYNOS series of SoC. + + +endif From ec54c74c8fb33a4317ea899f0d4dd49fb5f854ae Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Thu, 15 Nov 2012 14:51:05 +0800 Subject: [PATCH 27/36] Exynos: Add missing dependency CPU_FREQ_TABLE depends on CPU_FREQ. Selecting CPU_FREQ_TABLE without checking for dependencies gives the following compilation warnings: warning: (ARCH_TEGRA_2x_SOC && ARCH_TEGRA_3x_SOC && UX500_SOC_DB8500 && CPU_THERMAL && EXYNOS_THERMAL) selects CPU_FREQ_TABLE which has unmet direct dependencies (ARCH_HAS_CPUFREQ && CPU_FREQ) Based-on-patch-by: Sachin Kamat Signed-off-by: Zhang Rui --- drivers/thermal/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 937a23da66e..99b6587ab8b 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -96,7 +96,7 @@ config RCAR_THERMAL config EXYNOS_THERMAL tristate "Temperature sensor on Samsung EXYNOS" depends on (ARCH_EXYNOS4 || ARCH_EXYNOS5) - select CPU_FREQ_TABLE + depends on CPU_THERMAL help If you say yes here you get support for TMU (Thermal Managment Unit) on SAMSUNG EXYNOS series of SoC. From 445110e9d05e693c5e511717a010969175878754 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Thu, 15 Nov 2012 16:07:46 +0800 Subject: [PATCH 28/36] drivers/thermal/Makefile refactor Signed-off-by: Zhang Rui --- drivers/thermal/Makefile | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index e5f79747fa9..0b6b048f236 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -3,10 +3,16 @@ # obj-$(CONFIG_THERMAL) += thermal_sys.o -obj-$(CONFIG_CPU_THERMAL) += cpu_cooling.o -obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o + +# governors +obj-$(CONFIG_FAIR_SHARE) += fair_share.o +obj-$(CONFIG_STEP_WISE) += step_wise.o +obj-$(CONFIG_USER_SPACE) += user_space.o + +# cpufreq cooling +obj-$(CONFIG_CPU_THERMAL) += cpu_cooling.o + +# platform thermal drivers +obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o -obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o -obj-$(CONFIG_FAIR_SHARE) += fair_share.o -obj-$(CONFIG_STEP_WISE) += step_wise.o -obj-$(CONFIG_USER_SPACE) += user_space.o +obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o From aa1acb0451bb27add173d9641d0b74c58889e693 Mon Sep 17 00:00:00 2001 From: "hongbo.zhang" Date: Thu, 15 Nov 2012 18:56:42 +0800 Subject: [PATCH 29/36] Thermal: Add ST-Ericsson DB8500 thermal driver. This driver is based on the thermal management framework in thermal_sys.c. A thermal zone device is created with the trip points to which cooling devices can be bound, the current cooling device is cpufreq, e.g. CPU frequency is clipped down to cool the CPU, and other cooling devices can be added and bound to the trip points dynamically. The platform specific PRCMU interrupts are used to active thermal update when trip points are reached. Signed-off-by: hongbo.zhang Reviewed-by: Viresh Kumar Reviewed-by: Francesco Lavra Signed-off-by: Zhang Rui --- .../bindings/thermal/db8500-thermal.txt | 44 ++ drivers/thermal/Kconfig | 20 + drivers/thermal/Makefile | 2 + drivers/thermal/db8500_cpufreq_cooling.c | 108 ++++ drivers/thermal/db8500_thermal.c | 531 ++++++++++++++++++ include/linux/platform_data/db8500_thermal.h | 38 ++ 6 files changed, 743 insertions(+) create mode 100644 Documentation/devicetree/bindings/thermal/db8500-thermal.txt create mode 100644 drivers/thermal/db8500_cpufreq_cooling.c create mode 100644 drivers/thermal/db8500_thermal.c create mode 100644 include/linux/platform_data/db8500_thermal.h diff --git a/Documentation/devicetree/bindings/thermal/db8500-thermal.txt b/Documentation/devicetree/bindings/thermal/db8500-thermal.txt new file mode 100644 index 00000000000..2e1c06fad81 --- /dev/null +++ b/Documentation/devicetree/bindings/thermal/db8500-thermal.txt @@ -0,0 +1,44 @@ +* ST-Ericsson DB8500 Thermal + +** Thermal node properties: + +- compatible : "stericsson,db8500-thermal"; +- reg : address range of the thermal sensor registers; +- interrupts : interrupts generated from PRCMU; +- interrupt-names : "IRQ_HOTMON_LOW" and "IRQ_HOTMON_HIGH"; +- num-trips : number of total trip points, this is required, set it 0 if none, + if greater than 0, the following properties must be defined; +- tripN-temp : temperature of trip point N, should be in ascending order; +- tripN-type : type of trip point N, should be one of "active" "passive" "hot" + "critical"; +- tripN-cdev-num : number of the cooling devices which can be bound to trip + point N, this is required if trip point N is defined, set it 0 if none, + otherwise the following cooling device names must be defined; +- tripN-cdev-nameM : name of the No. M cooling device of trip point N; + +Usually the num-trips and tripN-*** are separated in board related dts files. + +Example: +thermal@801573c0 { + compatible = "stericsson,db8500-thermal"; + reg = <0x801573c0 0x40>; + interrupts = <21 0x4>, <22 0x4>; + interrupt-names = "IRQ_HOTMON_LOW", "IRQ_HOTMON_HIGH"; + + num-trips = <3>; + + trip0-temp = <75000>; + trip0-type = "active"; + trip0-cdev-num = <1>; + trip0-cdev-name0 = "thermal-cpufreq-0"; + + trip1-temp = <80000>; + trip1-type = "active"; + trip1-cdev-num = <2>; + trip1-cdev-name0 = "thermal-cpufreq-0"; + trip1-cdev-name1 = "thermal-fan"; + + trip2-temp = <85000>; + trip2-type = "critical"; + trip2-cdev-num = <0>; +} diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 99b6587ab8b..d96da075c9f 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -101,5 +101,25 @@ config EXYNOS_THERMAL If you say yes here you get support for TMU (Thermal Managment Unit) on SAMSUNG EXYNOS series of SoC. +config DB8500_THERMAL + bool "DB8500 thermal management" + depends on ARCH_U8500 + default y + help + Adds DB8500 thermal management implementation according to the thermal + management framework. A thermal zone with several trip points will be + created. Cooling devices can be bound to the trip points to cool this + thermal zone if trip points reached. + +config DB8500_CPUFREQ_COOLING + tristate "DB8500 cpufreq cooling" + depends on ARCH_U8500 + depends on CPU_THERMAL + default y + help + Adds DB8500 cpufreq cooling devices, and these cooling devices can be + bound to thermal zone trip points. When a trip point reached, the + bound cpufreq cooling device turns active to set CPU frequency low to + cool down the CPU. endif diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 0b6b048f236..d8da683245f 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -16,3 +16,5 @@ obj-$(CONFIG_CPU_THERMAL) += cpu_cooling.o obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o +obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o +obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o diff --git a/drivers/thermal/db8500_cpufreq_cooling.c b/drivers/thermal/db8500_cpufreq_cooling.c new file mode 100644 index 00000000000..4cf8e72af90 --- /dev/null +++ b/drivers/thermal/db8500_cpufreq_cooling.c @@ -0,0 +1,108 @@ +/* + * db8500_cpufreq_cooling.c - DB8500 cpufreq works as cooling device. + * + * Copyright (C) 2012 ST-Ericsson + * Copyright (C) 2012 Linaro Ltd. + * + * Author: Hongbo Zhang + * + * 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. + */ + +#include +#include +#include +#include +#include +#include + +static int db8500_cpufreq_cooling_probe(struct platform_device *pdev) +{ + struct thermal_cooling_device *cdev; + struct cpumask mask_val; + + /* make sure cpufreq driver has been initialized */ + if (!cpufreq_frequency_get_table(0)) + return -EPROBE_DEFER; + + cpumask_set_cpu(0, &mask_val); + cdev = cpufreq_cooling_register(&mask_val); + + if (IS_ERR_OR_NULL(cdev)) { + dev_err(&pdev->dev, "Failed to register cooling device\n"); + return PTR_ERR(cdev); + } + + platform_set_drvdata(pdev, cdev); + + dev_info(&pdev->dev, "Cooling device registered: %s\n", cdev->type); + + return 0; +} + +static int db8500_cpufreq_cooling_remove(struct platform_device *pdev) +{ + struct thermal_cooling_device *cdev = platform_get_drvdata(pdev); + + cpufreq_cooling_unregister(cdev); + + return 0; +} + +static int db8500_cpufreq_cooling_suspend(struct platform_device *pdev, + pm_message_t state) +{ + return -ENOSYS; +} + +static int db8500_cpufreq_cooling_resume(struct platform_device *pdev) +{ + return -ENOSYS; +} + +#ifdef CONFIG_OF +static const struct of_device_id db8500_cpufreq_cooling_match[] = { + { .compatible = "stericsson,db8500-cpufreq-cooling" }, + {}, +}; +#else +#define db8500_cpufreq_cooling_match NULL +#endif + +static struct platform_driver db8500_cpufreq_cooling_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "db8500-cpufreq-cooling", + .of_match_table = db8500_cpufreq_cooling_match, + }, + .probe = db8500_cpufreq_cooling_probe, + .suspend = db8500_cpufreq_cooling_suspend, + .resume = db8500_cpufreq_cooling_resume, + .remove = db8500_cpufreq_cooling_remove, +}; + +static int __init db8500_cpufreq_cooling_init(void) +{ + return platform_driver_register(&db8500_cpufreq_cooling_driver); +} + +static void __exit db8500_cpufreq_cooling_exit(void) +{ + platform_driver_unregister(&db8500_cpufreq_cooling_driver); +} + +/* Should be later than db8500_cpufreq_register */ +late_initcall(db8500_cpufreq_cooling_init); +module_exit(db8500_cpufreq_cooling_exit); + +MODULE_AUTHOR("Hongbo Zhang "); +MODULE_DESCRIPTION("DB8500 cpufreq cooling driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/thermal/db8500_thermal.c b/drivers/thermal/db8500_thermal.c new file mode 100644 index 00000000000..ec71ade3e31 --- /dev/null +++ b/drivers/thermal/db8500_thermal.c @@ -0,0 +1,531 @@ +/* + * db8500_thermal.c - DB8500 Thermal Management Implementation + * + * Copyright (C) 2012 ST-Ericsson + * Copyright (C) 2012 Linaro Ltd. + * + * Author: Hongbo Zhang + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PRCMU_DEFAULT_MEASURE_TIME 0xFFF +#define PRCMU_DEFAULT_LOW_TEMP 0 + +struct db8500_thermal_zone { + struct thermal_zone_device *therm_dev; + struct mutex th_lock; + struct work_struct therm_work; + struct db8500_thsens_platform_data *trip_tab; + enum thermal_device_mode mode; + enum thermal_trend trend; + unsigned long cur_temp_pseudo; + unsigned int cur_index; +}; + +/* Local function to check if thermal zone matches cooling devices */ +static int db8500_thermal_match_cdev(struct thermal_cooling_device *cdev, + struct db8500_trip_point *trip_point) +{ + int i; + + if (!strlen(cdev->type)) + return -EINVAL; + + for (i = 0; i < COOLING_DEV_MAX; i++) { + if (!strcmp(trip_point->cdev_name[i], cdev->type)) + return 0; + } + + return -ENODEV; +} + +/* Callback to bind cooling device to thermal zone */ +static int db8500_cdev_bind(struct thermal_zone_device *thermal, + struct thermal_cooling_device *cdev) +{ + struct db8500_thermal_zone *pzone = thermal->devdata; + struct db8500_thsens_platform_data *ptrips = pzone->trip_tab; + unsigned long max_state, upper, lower; + int i, ret = -EINVAL; + + cdev->ops->get_max_state(cdev, &max_state); + + for (i = 0; i < ptrips->num_trips; i++) { + if (db8500_thermal_match_cdev(cdev, &ptrips->trip_points[i])) + continue; + + upper = lower = i > max_state ? max_state : i; + + ret = thermal_zone_bind_cooling_device(thermal, i, cdev, + upper, lower); + + dev_info(&cdev->device, "%s bind to %d: %d-%s\n", cdev->type, + i, ret, ret ? "fail" : "succeed"); + } + + return ret; +} + +/* Callback to unbind cooling device from thermal zone */ +static int db8500_cdev_unbind(struct thermal_zone_device *thermal, + struct thermal_cooling_device *cdev) +{ + struct db8500_thermal_zone *pzone = thermal->devdata; + struct db8500_thsens_platform_data *ptrips = pzone->trip_tab; + int i, ret = -EINVAL; + + for (i = 0; i < ptrips->num_trips; i++) { + if (db8500_thermal_match_cdev(cdev, &ptrips->trip_points[i])) + continue; + + ret = thermal_zone_unbind_cooling_device(thermal, i, cdev); + + dev_info(&cdev->device, "%s unbind from %d: %s\n", cdev->type, + i, ret ? "fail" : "succeed"); + } + + return ret; +} + +/* Callback to get current temperature */ +static int db8500_sys_get_temp(struct thermal_zone_device *thermal, + unsigned long *temp) +{ + struct db8500_thermal_zone *pzone = thermal->devdata; + + /* + * TODO: There is no PRCMU interface to get temperature data currently, + * so a pseudo temperature is returned , it works for thermal framework + * and this will be fixed when the PRCMU interface is available. + */ + *temp = pzone->cur_temp_pseudo; + + return 0; +} + +/* Callback to get temperature changing trend */ +static int db8500_sys_get_trend(struct thermal_zone_device *thermal, + int trip, enum thermal_trend *trend) +{ + struct db8500_thermal_zone *pzone = thermal->devdata; + + *trend = pzone->trend; + + return 0; +} + +/* Callback to get thermal zone mode */ +static int db8500_sys_get_mode(struct thermal_zone_device *thermal, + enum thermal_device_mode *mode) +{ + struct db8500_thermal_zone *pzone = thermal->devdata; + + mutex_lock(&pzone->th_lock); + *mode = pzone->mode; + mutex_unlock(&pzone->th_lock); + + return 0; +} + +/* Callback to set thermal zone mode */ +static int db8500_sys_set_mode(struct thermal_zone_device *thermal, + enum thermal_device_mode mode) +{ + struct db8500_thermal_zone *pzone = thermal->devdata; + + mutex_lock(&pzone->th_lock); + + pzone->mode = mode; + if (mode == THERMAL_DEVICE_ENABLED) + schedule_work(&pzone->therm_work); + + mutex_unlock(&pzone->th_lock); + + return 0; +} + +/* Callback to get trip point type */ +static int db8500_sys_get_trip_type(struct thermal_zone_device *thermal, + int trip, enum thermal_trip_type *type) +{ + struct db8500_thermal_zone *pzone = thermal->devdata; + struct db8500_thsens_platform_data *ptrips = pzone->trip_tab; + + if (trip >= ptrips->num_trips) + return -EINVAL; + + *type = ptrips->trip_points[trip].type; + + return 0; +} + +/* Callback to get trip point temperature */ +static int db8500_sys_get_trip_temp(struct thermal_zone_device *thermal, + int trip, unsigned long *temp) +{ + struct db8500_thermal_zone *pzone = thermal->devdata; + struct db8500_thsens_platform_data *ptrips = pzone->trip_tab; + + if (trip >= ptrips->num_trips) + return -EINVAL; + + *temp = ptrips->trip_points[trip].temp; + + return 0; +} + +/* Callback to get critical trip point temperature */ +static int db8500_sys_get_crit_temp(struct thermal_zone_device *thermal, + unsigned long *temp) +{ + struct db8500_thermal_zone *pzone = thermal->devdata; + struct db8500_thsens_platform_data *ptrips = pzone->trip_tab; + int i; + + for (i = ptrips->num_trips - 1; i > 0; i--) { + if (ptrips->trip_points[i].type == THERMAL_TRIP_CRITICAL) { + *temp = ptrips->trip_points[i].temp; + return 0; + } + } + + return -EINVAL; +} + +static struct thermal_zone_device_ops thdev_ops = { + .bind = db8500_cdev_bind, + .unbind = db8500_cdev_unbind, + .get_temp = db8500_sys_get_temp, + .get_trend = db8500_sys_get_trend, + .get_mode = db8500_sys_get_mode, + .set_mode = db8500_sys_set_mode, + .get_trip_type = db8500_sys_get_trip_type, + .get_trip_temp = db8500_sys_get_trip_temp, + .get_crit_temp = db8500_sys_get_crit_temp, +}; + +static void db8500_thermal_update_config(struct db8500_thermal_zone *pzone, + unsigned int idx, enum thermal_trend trend, + unsigned long next_low, unsigned long next_high) +{ + prcmu_stop_temp_sense(); + + pzone->cur_index = idx; + pzone->cur_temp_pseudo = (next_low + next_high)/2; + pzone->trend = trend; + + prcmu_config_hotmon((u8)(next_low/1000), (u8)(next_high/1000)); + prcmu_start_temp_sense(PRCMU_DEFAULT_MEASURE_TIME); +} + +static irqreturn_t prcmu_low_irq_handler(int irq, void *irq_data) +{ + struct db8500_thermal_zone *pzone = irq_data; + struct db8500_thsens_platform_data *ptrips = pzone->trip_tab; + unsigned int idx = pzone->cur_index; + unsigned long next_low, next_high; + + if (unlikely(idx == 0)) + /* Meaningless for thermal management, ignoring it */ + return IRQ_HANDLED; + + if (idx == 1) { + next_high = ptrips->trip_points[0].temp; + next_low = PRCMU_DEFAULT_LOW_TEMP; + } else { + next_high = ptrips->trip_points[idx-1].temp; + next_low = ptrips->trip_points[idx-2].temp; + } + idx -= 1; + + db8500_thermal_update_config(pzone, idx, THERMAL_TREND_DROPPING, + next_low, next_high); + + dev_dbg(&pzone->therm_dev->device, + "PRCMU set max %ld, min %ld\n", next_high, next_low); + + schedule_work(&pzone->therm_work); + + return IRQ_HANDLED; +} + +static irqreturn_t prcmu_high_irq_handler(int irq, void *irq_data) +{ + struct db8500_thermal_zone *pzone = irq_data; + struct db8500_thsens_platform_data *ptrips = pzone->trip_tab; + unsigned int idx = pzone->cur_index; + unsigned long next_low, next_high; + + if (idx < ptrips->num_trips - 1) { + next_high = ptrips->trip_points[idx+1].temp; + next_low = ptrips->trip_points[idx].temp; + idx += 1; + + db8500_thermal_update_config(pzone, idx, THERMAL_TREND_RAISING, + next_low, next_high); + + dev_dbg(&pzone->therm_dev->device, + "PRCMU set max %ld, min %ld\n", next_high, next_low); + } else if (idx == ptrips->num_trips - 1) + pzone->cur_temp_pseudo = ptrips->trip_points[idx].temp + 1; + + schedule_work(&pzone->therm_work); + + return IRQ_HANDLED; +} + +static void db8500_thermal_work(struct work_struct *work) +{ + enum thermal_device_mode cur_mode; + struct db8500_thermal_zone *pzone; + + pzone = container_of(work, struct db8500_thermal_zone, therm_work); + + mutex_lock(&pzone->th_lock); + cur_mode = pzone->mode; + mutex_unlock(&pzone->th_lock); + + if (cur_mode == THERMAL_DEVICE_DISABLED) + return; + + thermal_zone_device_update(pzone->therm_dev); + dev_dbg(&pzone->therm_dev->device, "thermal work finished.\n"); +} + +#ifdef CONFIG_OF +static struct db8500_thsens_platform_data* + db8500_thermal_parse_dt(struct platform_device *pdev) +{ + struct db8500_thsens_platform_data *ptrips; + struct device_node *np = pdev->dev.of_node; + char prop_name[32]; + const char *tmp_str; + u32 tmp_data; + int i, j; + + ptrips = devm_kzalloc(&pdev->dev, sizeof(*ptrips), GFP_KERNEL); + if (!ptrips) + return NULL; + + if (of_property_read_u32(np, "num-trips", &tmp_data)) + goto err_parse_dt; + + if (tmp_data > THERMAL_MAX_TRIPS) + goto err_parse_dt; + + ptrips->num_trips = tmp_data; + + for (i = 0; i < ptrips->num_trips; i++) { + sprintf(prop_name, "trip%d-temp", i); + if (of_property_read_u32(np, prop_name, &tmp_data)) + goto err_parse_dt; + + ptrips->trip_points[i].temp = tmp_data; + + sprintf(prop_name, "trip%d-type", i); + if (of_property_read_string(np, prop_name, &tmp_str)) + goto err_parse_dt; + + if (!strcmp(tmp_str, "active")) + ptrips->trip_points[i].type = THERMAL_TRIP_ACTIVE; + else if (!strcmp(tmp_str, "passive")) + ptrips->trip_points[i].type = THERMAL_TRIP_PASSIVE; + else if (!strcmp(tmp_str, "hot")) + ptrips->trip_points[i].type = THERMAL_TRIP_HOT; + else if (!strcmp(tmp_str, "critical")) + ptrips->trip_points[i].type = THERMAL_TRIP_CRITICAL; + else + goto err_parse_dt; + + sprintf(prop_name, "trip%d-cdev-num", i); + if (of_property_read_u32(np, prop_name, &tmp_data)) + goto err_parse_dt; + + if (tmp_data > COOLING_DEV_MAX) + goto err_parse_dt; + + for (j = 0; j < tmp_data; j++) { + sprintf(prop_name, "trip%d-cdev-name%d", i, j); + if (of_property_read_string(np, prop_name, &tmp_str)) + goto err_parse_dt; + + if (strlen(tmp_str) >= THERMAL_NAME_LENGTH) + goto err_parse_dt; + + strcpy(ptrips->trip_points[i].cdev_name[j], tmp_str); + } + } + return ptrips; + +err_parse_dt: + dev_err(&pdev->dev, "Parsing device tree data error.\n"); + return NULL; +} +#else +static inline struct db8500_thsens_platform_data* + db8500_thermal_parse_dt(struct platform_device *pdev) +{ + return NULL; +} +#endif + +static int db8500_thermal_probe(struct platform_device *pdev) +{ + struct db8500_thermal_zone *pzone = NULL; + struct db8500_thsens_platform_data *ptrips = NULL; + struct device_node *np = pdev->dev.of_node; + int low_irq, high_irq, ret = 0; + unsigned long dft_low, dft_high; + + if (np) + ptrips = db8500_thermal_parse_dt(pdev); + else + ptrips = dev_get_platdata(&pdev->dev); + + if (!ptrips) + return -EINVAL; + + pzone = devm_kzalloc(&pdev->dev, sizeof(*pzone), GFP_KERNEL); + if (!pzone) + return -ENOMEM; + + mutex_init(&pzone->th_lock); + mutex_lock(&pzone->th_lock); + + pzone->mode = THERMAL_DEVICE_DISABLED; + pzone->trip_tab = ptrips; + + INIT_WORK(&pzone->therm_work, db8500_thermal_work); + + low_irq = platform_get_irq_byname(pdev, "IRQ_HOTMON_LOW"); + if (low_irq < 0) { + dev_err(&pdev->dev, "Get IRQ_HOTMON_LOW failed.\n"); + return low_irq; + } + + ret = devm_request_threaded_irq(&pdev->dev, low_irq, NULL, + prcmu_low_irq_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT, + "dbx500_temp_low", pzone); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to allocate temp low irq.\n"); + return ret; + } + + high_irq = platform_get_irq_byname(pdev, "IRQ_HOTMON_HIGH"); + if (high_irq < 0) { + dev_err(&pdev->dev, "Get IRQ_HOTMON_HIGH failed.\n"); + return high_irq; + } + + ret = devm_request_threaded_irq(&pdev->dev, high_irq, NULL, + prcmu_high_irq_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT, + "dbx500_temp_high", pzone); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to allocate temp high irq.\n"); + return ret; + } + + pzone->therm_dev = thermal_zone_device_register("db8500_thermal_zone", + ptrips->num_trips, 0, pzone, &thdev_ops, NULL, 0, 0); + + if (IS_ERR_OR_NULL(pzone->therm_dev)) { + dev_err(&pdev->dev, "Register thermal zone device failed.\n"); + return PTR_ERR(pzone->therm_dev); + } + dev_info(&pdev->dev, "Thermal zone device registered.\n"); + + dft_low = PRCMU_DEFAULT_LOW_TEMP; + dft_high = ptrips->trip_points[0].temp; + + db8500_thermal_update_config(pzone, 0, THERMAL_TREND_STABLE, + dft_low, dft_high); + + platform_set_drvdata(pdev, pzone); + pzone->mode = THERMAL_DEVICE_ENABLED; + mutex_unlock(&pzone->th_lock); + + return 0; +} + +static int db8500_thermal_remove(struct platform_device *pdev) +{ + struct db8500_thermal_zone *pzone = platform_get_drvdata(pdev); + + thermal_zone_device_unregister(pzone->therm_dev); + cancel_work_sync(&pzone->therm_work); + mutex_destroy(&pzone->th_lock); + + return 0; +} + +static int db8500_thermal_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct db8500_thermal_zone *pzone = platform_get_drvdata(pdev); + + flush_work(&pzone->therm_work); + prcmu_stop_temp_sense(); + + return 0; +} + +static int db8500_thermal_resume(struct platform_device *pdev) +{ + struct db8500_thermal_zone *pzone = platform_get_drvdata(pdev); + struct db8500_thsens_platform_data *ptrips = pzone->trip_tab; + unsigned long dft_low, dft_high; + + dft_low = PRCMU_DEFAULT_LOW_TEMP; + dft_high = ptrips->trip_points[0].temp; + + db8500_thermal_update_config(pzone, 0, THERMAL_TREND_STABLE, + dft_low, dft_high); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id db8500_thermal_match[] = { + { .compatible = "stericsson,db8500-thermal" }, + {}, +}; +#else +#define db8500_thermal_match NULL +#endif + +static struct platform_driver db8500_thermal_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "db8500-thermal", + .of_match_table = db8500_thermal_match, + }, + .probe = db8500_thermal_probe, + .suspend = db8500_thermal_suspend, + .resume = db8500_thermal_resume, + .remove = db8500_thermal_remove, +}; + +module_platform_driver(db8500_thermal_driver); + +MODULE_AUTHOR("Hongbo Zhang "); +MODULE_DESCRIPTION("DB8500 thermal driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/platform_data/db8500_thermal.h b/include/linux/platform_data/db8500_thermal.h new file mode 100644 index 00000000000..3bf60902e90 --- /dev/null +++ b/include/linux/platform_data/db8500_thermal.h @@ -0,0 +1,38 @@ +/* + * db8500_thermal.h - DB8500 Thermal Management Implementation + * + * Copyright (C) 2012 ST-Ericsson + * Copyright (C) 2012 Linaro Ltd. + * + * Author: Hongbo Zhang + * + * 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. + */ + +#ifndef _DB8500_THERMAL_H_ +#define _DB8500_THERMAL_H_ + +#include + +#define COOLING_DEV_MAX 8 + +struct db8500_trip_point { + unsigned long temp; + enum thermal_trip_type type; + char cdev_name[COOLING_DEV_MAX][THERMAL_NAME_LENGTH]; +}; + +struct db8500_thsens_platform_data { + struct db8500_trip_point trip_points[THERMAL_MAX_TRIPS]; + int num_trips; +}; + +#endif /* _DB8500_THERMAL_H_ */ From dc1956b5f84de7d453ec4d9fe68385fffd689686 Mon Sep 17 00:00:00 2001 From: "hongbo.zhang" Date: Thu, 15 Nov 2012 18:56:43 +0800 Subject: [PATCH 30/36] Thermal: Add ST-Ericsson DB8500 thermal properties and platform data. This patch adds device tree properties for ST-Ericsson DB8500 thermal driver, also adds the platform data to support the old fashion. Signed-off-by: hongbo.zhang Reviewed-by: Viresh Kumar Acked-by: Linus Walleij Signed-off-by: Zhang Rui --- arch/arm/boot/dts/dbx5x0.dtsi | 14 +++++++ arch/arm/boot/dts/snowball.dts | 31 +++++++++++++++ arch/arm/configs/u8500_defconfig | 2 + arch/arm/mach-ux500/board-mop500.c | 64 ++++++++++++++++++++++++++++++ 4 files changed, 111 insertions(+) diff --git a/arch/arm/boot/dts/dbx5x0.dtsi b/arch/arm/boot/dts/dbx5x0.dtsi index 4b0e0ca08f4..731086b2fca 100644 --- a/arch/arm/boot/dts/dbx5x0.dtsi +++ b/arch/arm/boot/dts/dbx5x0.dtsi @@ -203,6 +203,14 @@ reg = <0x80157450 0xC>; }; + thermal@801573c0 { + compatible = "stericsson,db8500-thermal"; + reg = <0x801573c0 0x40>; + interrupts = <21 0x4>, <22 0x4>; + interrupt-names = "IRQ_HOTMON_LOW", "IRQ_HOTMON_HIGH"; + status = "disabled"; + }; + db8500-prcmu-regulators { compatible = "stericsson,db8500-prcmu-regulator"; @@ -660,5 +668,11 @@ ranges = <0 0x50000000 0x4000000>; status = "disabled"; }; + + cpufreq-cooling { + compatible = "stericsson,db8500-cpufreq-cooling"; + status = "disabled"; + }; + }; }; diff --git a/arch/arm/boot/dts/snowball.dts b/arch/arm/boot/dts/snowball.dts index 702c0baa600..c6f85f0bc53 100644 --- a/arch/arm/boot/dts/snowball.dts +++ b/arch/arm/boot/dts/snowball.dts @@ -99,6 +99,33 @@ status = "okay"; }; + prcmu@80157000 { + thermal@801573c0 { + num-trips = <4>; + + trip0-temp = <70000>; + trip0-type = "active"; + trip0-cdev-num = <1>; + trip0-cdev-name0 = "thermal-cpufreq-0"; + + trip1-temp = <75000>; + trip1-type = "active"; + trip1-cdev-num = <1>; + trip1-cdev-name0 = "thermal-cpufreq-0"; + + trip2-temp = <80000>; + trip2-type = "active"; + trip2-cdev-num = <1>; + trip2-cdev-name0 = "thermal-cpufreq-0"; + + trip3-temp = <85000>; + trip3-type = "critical"; + trip3-cdev-num = <0>; + + status = "okay"; + }; + }; + external-bus@50000000 { status = "okay"; @@ -183,5 +210,9 @@ reg = <0x33>; }; }; + + cpufreq-cooling { + status = "okay"; + }; }; }; diff --git a/arch/arm/configs/u8500_defconfig b/arch/arm/configs/u8500_defconfig index da6845493ca..250625d5223 100644 --- a/arch/arm/configs/u8500_defconfig +++ b/arch/arm/configs/u8500_defconfig @@ -69,6 +69,8 @@ CONFIG_GPIO_TC3589X=y CONFIG_POWER_SUPPLY=y CONFIG_AB8500_BM=y CONFIG_AB8500_BATTERY_THERM_ON_BATCTRL=y +CONFIG_THERMAL=y +CONFIG_CPU_THERMAL=y CONFIG_MFD_STMPE=y CONFIG_MFD_TC3589X=y CONFIG_AB5500_CORE=y diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c index 416d436111f..b03216b1549 100644 --- a/arch/arm/mach-ux500/board-mop500.c +++ b/arch/arm/mach-ux500/board-mop500.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -228,6 +229,67 @@ static struct ab8500_platform_data ab8500_platdata = { .codec = &ab8500_codec_pdata, }; +/* + * Thermal Sensor + */ + +static struct resource db8500_thsens_resources[] = { + { + .name = "IRQ_HOTMON_LOW", + .start = IRQ_PRCMU_HOTMON_LOW, + .end = IRQ_PRCMU_HOTMON_LOW, + .flags = IORESOURCE_IRQ, + }, + { + .name = "IRQ_HOTMON_HIGH", + .start = IRQ_PRCMU_HOTMON_HIGH, + .end = IRQ_PRCMU_HOTMON_HIGH, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct db8500_thsens_platform_data db8500_thsens_data = { + .trip_points[0] = { + .temp = 70000, + .type = THERMAL_TRIP_ACTIVE, + .cdev_name = { + [0] = "thermal-cpufreq-0", + }, + }, + .trip_points[1] = { + .temp = 75000, + .type = THERMAL_TRIP_ACTIVE, + .cdev_name = { + [0] = "thermal-cpufreq-0", + }, + }, + .trip_points[2] = { + .temp = 80000, + .type = THERMAL_TRIP_ACTIVE, + .cdev_name = { + [0] = "thermal-cpufreq-0", + }, + }, + .trip_points[3] = { + .temp = 85000, + .type = THERMAL_TRIP_CRITICAL, + }, + .num_trips = 4, +}; + +static struct platform_device u8500_thsens_device = { + .name = "db8500-thermal", + .resource = db8500_thsens_resources, + .num_resources = ARRAY_SIZE(db8500_thsens_resources), + .dev = { + .platform_data = &db8500_thsens_data, + }, +}; + +static struct platform_device u8500_cpufreq_cooling_device = { + .name = "db8500-cpufreq-cooling", +}; + /* * TPS61052 */ @@ -583,6 +645,8 @@ static struct platform_device *snowball_platform_devs[] __initdata = { &snowball_key_dev, &snowball_sbnet_dev, &snowball_gpio_en_3v3_regulator_dev, + &u8500_thsens_device, + &u8500_cpufreq_cooling_device, }; static void __init mop500_init_machine(void) From 3778ff5c709899a29cefe4b3077a926d0e1e9785 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Mon, 12 Nov 2012 15:58:50 +0000 Subject: [PATCH 31/36] thermal: cpu cooling: use const parameter while registering There are predefined cpu_masks that are const data structures. This patch changes the cpu cooling register function so that those const cpu_masks can be used, without compilation warnings. include/linux/cpumask.h * The following particular system cpumasks and operations manage * possible, present, active and online cpus. * * cpu_possible_mask- has bit 'cpu' set iff cpu is populatable * cpu_present_mask - has bit 'cpu' set iff cpu is populated * cpu_online_mask - has bit 'cpu' set iff cpu available to scheduler * cpu_active_mask - has bit 'cpu' set iff cpu available to migration * Signed-off-by: Eduardo Valentin Signed-off-by: Zhang Rui --- drivers/thermal/cpu_cooling.c | 2 +- include/linux/cpu_cooling.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index 6f94c2cac1f..836828e29a8 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -313,7 +313,7 @@ static struct notifier_block thermal_cpufreq_notifier_block = { * @clip_cpus: cpumask of cpus where the frequency constraints will happen. */ struct thermal_cooling_device *cpufreq_cooling_register( - struct cpumask *clip_cpus) + const struct cpumask *clip_cpus) { struct thermal_cooling_device *cool_dev; struct cpufreq_cooling_device *cpufreq_dev = NULL; diff --git a/include/linux/cpu_cooling.h b/include/linux/cpu_cooling.h index 851530128e6..b30cc79c7a8 100644 --- a/include/linux/cpu_cooling.h +++ b/include/linux/cpu_cooling.h @@ -35,7 +35,7 @@ * @clip_cpus: cpumask of cpus where the frequency constraints will happen */ struct thermal_cooling_device *cpufreq_cooling_register( - struct cpumask *clip_cpus); + const struct cpumask *clip_cpus); /** * cpufreq_cooling_unregister - function to remove cpufreq cooling device. @@ -44,7 +44,7 @@ struct thermal_cooling_device *cpufreq_cooling_register( void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev); #else /* !CONFIG_CPU_THERMAL */ static inline struct thermal_cooling_device *cpufreq_cooling_register( - struct cpumask *clip_cpus) + const struct cpumask *clip_cpus) { return NULL; } From 4ba115b1e1ab6b717e08e0f4e766a1d16853d217 Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Wed, 14 Nov 2012 15:23:30 +0000 Subject: [PATCH 32/36] thermal: cpu cooling: allow module builds As thermal drivers can be built as modules and also the thermal framework itself, building cpu cooling only as built-in can cause linking errors. For instance: * Generic Thermal sysfs driver * Generic Thermal sysfs driver (THERMAL) [M/n/y/?] m generic cpu cooling support (CPU_THERMAL) [N/y/?] (NEW) y with the following drive: CONFIG_OMAP_BANDGAP=m generates: ERROR: "cpufreq_cooling_unregister" [drivers/staging/omap-thermal/omap-thermal.ko] undefined! ERROR: "cpufreq_cooling_register" [drivers/staging/omap-thermal/omap-thermal.ko] undefined! This patch changes cpu cooling driver to allow it to be built as module. Reported-by: Enric Balletbo i Serra Signed-off-by: Eduardo Valentin Reviewed-by: Durgadoss R Signed-off-by: Zhang Rui --- drivers/thermal/Kconfig | 2 +- include/linux/cpu_cooling.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index d96da075c9f..8636fae1f7e 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -67,7 +67,7 @@ config USER_SPACE Enable this to let the user space manage the platform thermals. config CPU_THERMAL - bool "generic cpu cooling support" + tristate "generic cpu cooling support" depends on CPU_FREQ select CPU_FREQ_TABLE help diff --git a/include/linux/cpu_cooling.h b/include/linux/cpu_cooling.h index b30cc79c7a8..40b4ef54cc7 100644 --- a/include/linux/cpu_cooling.h +++ b/include/linux/cpu_cooling.h @@ -29,7 +29,7 @@ #define CPUFREQ_COOLING_START 0 #define CPUFREQ_COOLING_STOP 1 -#ifdef CONFIG_CPU_THERMAL +#if defined(CONFIG_CPU_THERMAL) || defined(CONFIG_CPU_THERMAL_MODULE) /** * cpufreq_cooling_register - function to create cpufreq cooling device. * @clip_cpus: cpumask of cpus where the frequency constraints will happen From c499703e64bef8f2e8ffe9ea7ee63a0a926b5530 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 26 Nov 2012 02:32:06 +0000 Subject: [PATCH 33/36] thermal: rcar: fixup the unit of temperature The unit of temperature is Milli-Celsius. Signed-off-by: Kuninori Morimoto Signed-off-by: Zhang Rui --- drivers/thermal/rcar_thermal.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 81dce23828f..f2678ff0ed2 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -43,6 +43,8 @@ struct rcar_thermal_priv { u32 comp; }; +#define MCELSIUS(temp) ((temp) * 1000) + /* * basic functions */ @@ -169,7 +171,7 @@ static int rcar_thermal_get_temp(struct thermal_zone_device *zone, } } - *temp = tmp; + *temp = MCELSIUS(tmp); return 0; } From d12250ef8dc1bbc811d505776be053e358ba82f5 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 26 Nov 2012 02:32:20 +0000 Subject: [PATCH 34/36] thermal: rcar: add rcar_zone_to_priv() macro This patch adds rcar_zone_to_priv() which is a helper macro for gettign private data. Signed-off-by: Kuninori Morimoto Signed-off-by: Zhang Rui --- drivers/thermal/rcar_thermal.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index f2678ff0ed2..90db951725d 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -44,6 +44,7 @@ struct rcar_thermal_priv { }; #define MCELSIUS(temp) ((temp) * 1000) +#define rcar_zone_to_priv(zone) (zone->devdata) /* * basic functions @@ -98,7 +99,7 @@ static void rcar_thermal_bset(struct rcar_thermal_priv *priv, u32 reg, static int rcar_thermal_get_temp(struct thermal_zone_device *zone, unsigned long *temp) { - struct rcar_thermal_priv *priv = zone->devdata; + struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); int val, min, max, tmp; tmp = -200; /* default */ From d567c686aebc7140f73318ee250236bda39c04eb Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Wed, 12 Dec 2012 15:23:54 +0800 Subject: [PATCH 35/36] Thermal: fix a NULL pointer dereference when generic thermal layer is built as a module [ 12.761956] BUG: unable to handle kernel NULL pointer dereference at 0000000000000018 [ 12.762016] IP: [] handle_thermal_trip+0x47/0x130 [thermal_sys] [ 12.762060] PGD 1fec74067 PUD 1fee5b067 PMD 0 [ 12.762127] Oops: 0000 [#1] SMP [ 12.762177] Modules linked in: hid_generic crc32c_intel usbhid hid firewire_ohci(+) e1000e(+) firewire_core crc_itu_t xhci_hcd(+) thermal(+) fan thermal_sys hwmon [ 12.762423] CPU 1 [ 12.762443] Pid: 187, comm: modprobe Tainted: G A 3.7.0-thermal-module+ #25 /DH77DF [ 12.762496] RIP: 0010:[] [] handle_thermal_trip+0x47/0x130 [thermal_sys] [ 12.762682] RSP: 0018:ffff8801fe7ddc18 EFLAGS: 00010282 [ 12.762704] RAX: 0000000000000000 RBX: ffff8801ff3e9c00 RCX: ffff8801fdc39800 [ 12.762728] RDX: ffff8801fe7ddc24 RSI: 0000000000000001 RDI: ffff8801ff3e9c00 [ 12.762764] RBP: ffff8801fe7ddc48 R08: 0000000004000000 R09: ffffffffa001f568 [ 12.762797] R10: ffffffff81363083 R11: 0000000000000001 R12: 0000000000000001 [ 12.762832] R13: 0000000000000000 R14: 0000000000000001 R15: ffff8801fde73e68 [ 12.762866] FS: 00007f5548516700(0000) GS:ffff88021f240000(0000) knlGS:0000000000000000 [ 12.762912] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 12.762946] CR2: 0000000000000018 CR3: 00000001fefe2000 CR4: 00000000001407e0 [ 12.762979] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 12.763014] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 [ 12.763048] Process modprobe (pid: 187, threadinfo ffff8801fe7dc000, task ffff8801fe5bdb40) [ 12.763095] Stack: [ 12.763122] 0000000000019640 00000000fdc39800 ffff8801fe7ddc48 ffff8801ff3e9c00 [ 12.763225] 0000000000000002 0000000000000000 ffff8801fe7ddc78 ffffffffa00053e7 [ 12.763338] ffff8801ff3e9c00 0000000000006c98 ffffffffa0007480 ffff8801ff3e9c00 [ 12.763440] Call Trace: [ 12.763470] [] thermal_zone_device_update+0x77/0xa0 [thermal_sys] [ 12.763515] [] thermal_zone_device_register+0x788/0xa88 [thermal_sys] [ 12.763562] [] acpi_thermal_add+0x360/0x4c8 [thermal] [ 12.763598] [] acpi_device_probe+0x50/0x190 [ 12.763632] [] ? sysfs_create_link+0x13/0x20 [ 12.763666] [] driver_probe_device+0x7b/0x240 [ 12.763699] [] __driver_attach+0xab/0xb0 [ 12.763732] [] ? driver_probe_device+0x240/0x240 [ 12.763766] [] bus_for_each_dev+0x56/0x90 [ 12.763799] [] driver_attach+0x1e/0x20 [ 12.763831] [] bus_add_driver+0x190/0x290 [ 12.763864] [] ? 0xffffffffa0021fff [ 12.763896] [] driver_register+0x7a/0x160 [ 12.763928] [] ? 0xffffffffa0021fff [ 12.763960] [] acpi_bus_register_driver+0x43/0x45 [ 12.763995] [] acpi_thermal_init+0x3a/0x42 [thermal] [ 12.764029] [] do_one_initcall+0x3f/0x170 [ 12.764063] [] sys_init_module+0x8f/0x200 [ 12.764097] [] system_call_fastpath+0x16/0x1b [ 12.764129] Code: 48 8b 87 c8 02 00 00 41 89 f4 48 8d 55 dc ff 50 28 44 8b 6d dc 41 8d 45 fe 83 f8 01 76 5e 48 8b 83 d8 02 00 00 44 89 e6 48 89 df 50 18 4c 8d a3 10 03 00 00 4c 89 e7 e8 87 f1 5e e1 8b 83 bc [ 12.765164] RIP [] handle_thermal_trip+0x47/0x130 [thermal_sys] [ 12.765223] RSP [ 12.765252] CR2: 0000000000000018 [ 12.765284] ---[ end trace 7723294cdfb00d2a ]--- This is because thermal_zone_device_update() is invoked before any thermal governors being registered. Signed-off-by: Zhang Rui --- drivers/thermal/thermal_sys.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index 8f0f37bb282..8c8ce806180 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -336,7 +336,8 @@ static void monitor_thermal_zone(struct thermal_zone_device *tz) static void handle_non_critical_trips(struct thermal_zone_device *tz, int trip, enum thermal_trip_type trip_type) { - tz->governor->throttle(tz, trip); + if (tz->governor) + tz->governor->throttle(tz, trip); } static void handle_critical_trips(struct thermal_zone_device *tz, From 1f53ef17d3ed6c34868cc8e7aa7c1d351c2fdc95 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Wed, 12 Dec 2012 15:31:37 +0800 Subject: [PATCH 36/36] Thermal: Fix DEFAULT_THERMAL_GOVERNOR Fix DEFAULT_THERMAL_GOVERNOR to be consistant with the default governor selected in kernel config file. Signed-off-by: Zhang Rui --- drivers/thermal/step_wise.c | 2 +- include/linux/thermal.h | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/thermal/step_wise.c b/drivers/thermal/step_wise.c index 1242cffed8b..0cd5e9fbab1 100644 --- a/drivers/thermal/step_wise.c +++ b/drivers/thermal/step_wise.c @@ -170,7 +170,7 @@ static int step_wise_throttle(struct thermal_zone_device *tz, int trip) } static struct thermal_governor thermal_gov_step_wise = { - .name = DEFAULT_THERMAL_GOVERNOR, + .name = "step_wise", .throttle = step_wise_throttle, .owner = THIS_MODULE, }; diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 807f2146fe3..fe82022478e 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -46,8 +46,14 @@ #define THERMAL_GENL_VERSION 0x01 #define THERMAL_GENL_MCAST_GROUP_NAME "thermal_mc_group" -/* Default Thermal Governor: Does Linear Throttling */ -#define DEFAULT_THERMAL_GOVERNOR "step_wise" +/* Default Thermal Governor */ +#if defined(CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE) +#define DEFAULT_THERMAL_GOVERNOR "step_wise" +#elif defined(CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE) +#define DEFAULT_THERMAL_GOVERNOR "fair_share" +#elif defined(CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE) +#define DEFAULT_THERMAL_GOVERNOR "user_space" +#endif struct thermal_zone_device; struct thermal_cooling_device;