From 9e81bac6dba2ffb1c82b05d519efa4c590b6022d Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Fri, 7 Nov 2014 21:24:39 -0400 Subject: [PATCH 01/14] thermal: of: improve of-thermal sensor registration API Different drivers request API extensions in of-thermal. For this reason, additional callbacks are required to fit the new drivers needs. The current API implementation expects the registering sensor driver to provide a get_temp and get_trend callbacks as function parameters. As the amount of callbacks is growing, this patch changes the existing implementation to use a .ops field to hold all the of thermal callbacks to sensor drivers. This patch also changes the existing of-thermal users to fit the new API design. No functional change is introduced in this patch. Cc: Alexandre Courbot Cc: devicetree@vger.kernel.org Cc: Grant Likely Cc: Guenter Roeck Cc: Jean Delvare Cc: linux-kernel@vger.kernel.org Cc: linux-pm@vger.kernel.org Cc: linux-tegra@vger.kernel.org Cc: lm-sensors@lm-sensors.org Cc: Rob Herring Cc: Stephen Warren Cc: Thierry Reding Cc: Zhang Rui Acked-by: Guenter Roeck Tested-by: Mikko Perttunen Reviewed-by: Mikko Perttunen Reviewed-by: Alexandre Courbot Reviewed-by: Lukasz Majewski Signed-off-by: Eduardo Valentin --- drivers/hwmon/lm75.c | 9 +++-- drivers/hwmon/ntc_thermistor.c | 6 ++- drivers/hwmon/tmp102.c | 6 ++- drivers/thermal/of-thermal.c | 39 ++++++++----------- .../ti-soc-thermal/ti-thermal-common.c | 8 +++- include/linux/thermal.h | 24 +++++++++--- 6 files changed, 57 insertions(+), 35 deletions(-) diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index d16dbb33a53179..e7c8bf9093ead2 100644 --- a/drivers/hwmon/lm75.c +++ b/drivers/hwmon/lm75.c @@ -176,6 +176,10 @@ static struct attribute *lm75_attrs[] = { }; ATTRIBUTE_GROUPS(lm75); +static const struct thermal_zone_of_device_ops lm75_of_thermal_ops = { + .get_temp = lm75_read_temp, +}; + /*-----------------------------------------------------------------------*/ /* device probe and removal */ @@ -291,10 +295,9 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id) if (IS_ERR(data->hwmon_dev)) return PTR_ERR(data->hwmon_dev); - data->tz = thermal_zone_of_sensor_register(data->hwmon_dev, - 0, + data->tz = thermal_zone_of_sensor_register(data->hwmon_dev, 0, data->hwmon_dev, - lm75_read_temp, NULL); + &lm75_of_thermal_ops); if (IS_ERR(data->tz)) data->tz = NULL; diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c index 4ff89b2482e48a..bca8521c8a9bda 100644 --- a/drivers/hwmon/ntc_thermistor.c +++ b/drivers/hwmon/ntc_thermistor.c @@ -486,6 +486,10 @@ static const struct attribute_group ntc_attr_group = { .attrs = ntc_attributes, }; +static const struct thermal_zone_of_device_ops ntc_of_thermal_ops = { + .get_temp = ntc_read_temp, +}; + static int ntc_thermistor_probe(struct platform_device *pdev) { const struct of_device_id *of_id = @@ -579,7 +583,7 @@ static int ntc_thermistor_probe(struct platform_device *pdev) pdev_id->name); data->tz = thermal_zone_of_sensor_register(data->dev, 0, data->dev, - ntc_read_temp, NULL); + &ntc_of_thermal_ops); if (IS_ERR(data->tz)) { dev_dbg(&pdev->dev, "Failed to register to thermal fw.\n"); data->tz = NULL; diff --git a/drivers/hwmon/tmp102.c b/drivers/hwmon/tmp102.c index 51719956cc03f5..ba9f478f64ee68 100644 --- a/drivers/hwmon/tmp102.c +++ b/drivers/hwmon/tmp102.c @@ -158,6 +158,10 @@ ATTRIBUTE_GROUPS(tmp102); #define TMP102_CONFIG (TMP102_CONF_TM | TMP102_CONF_EM | TMP102_CONF_CR1) #define TMP102_CONFIG_RD_ONLY (TMP102_CONF_R0 | TMP102_CONF_R1 | TMP102_CONF_AL) +static const struct thermal_zone_of_device_ops tmp102_of_thermal_ops = { + .get_temp = tmp102_read_temp, +}; + static int tmp102_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -215,7 +219,7 @@ static int tmp102_probe(struct i2c_client *client, } tmp102->hwmon_dev = hwmon_dev; tmp102->tz = thermal_zone_of_sensor_register(hwmon_dev, 0, hwmon_dev, - tmp102_read_temp, NULL); + &tmp102_of_thermal_ops); if (IS_ERR(tmp102->tz)) tmp102->tz = NULL; diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c index 62143ba3100182..b7982f0a6eaf42 100644 --- a/drivers/thermal/of-thermal.c +++ b/drivers/thermal/of-thermal.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "thermal_core.h" @@ -77,8 +78,7 @@ struct __thermal_bind_params { * @num_tbps: number of thermal bind params * @tbps: an array of thermal bind params (0..num_tbps - 1) * @sensor_data: sensor private data used while reading temperature and trend - * @get_temp: sensor callback to read temperature - * @get_trend: sensor callback to read temperature trend + * @ops: set of callbacks to handle the thermal zone based on DT */ struct __thermal_zone { @@ -96,8 +96,7 @@ struct __thermal_zone { /* sensor interface */ void *sensor_data; - int (*get_temp)(void *, long *); - int (*get_trend)(void *, long *); + const struct thermal_zone_of_device_ops *ops; }; /*** DT thermal zone device callbacks ***/ @@ -107,10 +106,10 @@ static int of_thermal_get_temp(struct thermal_zone_device *tz, { struct __thermal_zone *data = tz->devdata; - if (!data->get_temp) + if (!data->ops->get_temp) return -EINVAL; - return data->get_temp(data->sensor_data, temp); + return data->ops->get_temp(data->sensor_data, temp); } static int of_thermal_get_trend(struct thermal_zone_device *tz, int trip, @@ -120,10 +119,10 @@ static int of_thermal_get_trend(struct thermal_zone_device *tz, int trip, long dev_trend; int r; - if (!data->get_trend) + if (!data->ops->get_trend) return -EINVAL; - r = data->get_trend(data->sensor_data, &dev_trend); + r = data->ops->get_trend(data->sensor_data, &dev_trend); if (r) return r; @@ -324,8 +323,7 @@ static struct thermal_zone_device_ops of_thermal_ops = { static struct thermal_zone_device * thermal_zone_of_add_sensor(struct device_node *zone, struct device_node *sensor, void *data, - int (*get_temp)(void *, long *), - int (*get_trend)(void *, long *)) + const struct thermal_zone_of_device_ops *ops) { struct thermal_zone_device *tzd; struct __thermal_zone *tz; @@ -336,9 +334,11 @@ thermal_zone_of_add_sensor(struct device_node *zone, tz = tzd->devdata; + if (!ops) + return ERR_PTR(-EINVAL); + mutex_lock(&tzd->lock); - tz->get_temp = get_temp; - tz->get_trend = get_trend; + tz->ops = ops; tz->sensor_data = data; tzd->ops->get_temp = of_thermal_get_temp; @@ -356,8 +356,7 @@ thermal_zone_of_add_sensor(struct device_node *zone, * than one sensors * @data: a private pointer (owned by the caller) that will be passed * back, when a temperature reading is needed. - * @get_temp: a pointer to a function that reads the sensor temperature. - * @get_trend: a pointer to a function that reads the sensor temperature trend. + * @ops: struct thermal_zone_of_device_ops *. Must contain at least .get_temp. * * This function will search the list of thermal zones described in device * tree and look for the zone that refer to the sensor device pointed by @@ -382,9 +381,8 @@ thermal_zone_of_add_sensor(struct device_node *zone, * check the return value with help of IS_ERR() helper. */ struct thermal_zone_device * -thermal_zone_of_sensor_register(struct device *dev, int sensor_id, - void *data, int (*get_temp)(void *, long *), - int (*get_trend)(void *, long *)) +thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data, + const struct thermal_zone_of_device_ops *ops) { struct device_node *np, *child, *sensor_np; struct thermal_zone_device *tzd = ERR_PTR(-ENODEV); @@ -426,9 +424,7 @@ thermal_zone_of_sensor_register(struct device *dev, int sensor_id, if (sensor_specs.np == sensor_np && id == sensor_id) { tzd = thermal_zone_of_add_sensor(child, sensor_np, - data, - get_temp, - get_trend); + data, ops); of_node_put(sensor_specs.np); of_node_put(child); goto exit; @@ -476,8 +472,7 @@ void thermal_zone_of_sensor_unregister(struct device *dev, tzd->ops->get_temp = NULL; tzd->ops->get_trend = NULL; - tz->get_temp = NULL; - tz->get_trend = NULL; + tz->ops = NULL; tz->sensor_data = NULL; mutex_unlock(&tzd->lock); } diff --git a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c index 9eec26dc044890..5fd03865e396e3 100644 --- a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c +++ b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c @@ -286,6 +286,11 @@ static int ti_thermal_get_crit_temp(struct thermal_zone_device *thermal, return ti_thermal_get_trip_temp(thermal, OMAP_TRIP_NUMBER - 1, temp); } +static const struct thermal_zone_of_device_ops ti_of_thermal_ops = { + .get_temp = __ti_thermal_get_temp, + .get_trend = __ti_thermal_get_trend, +}; + static struct thermal_zone_device_ops ti_thermal_ops = { .get_temp = ti_thermal_get_temp, .get_trend = ti_thermal_get_trend, @@ -333,8 +338,7 @@ int ti_thermal_expose_sensor(struct ti_bandgap *bgp, int id, /* in case this is specified by DT */ data->ti_thermal = thermal_zone_of_sensor_register(bgp->dev, id, - data, __ti_thermal_get_temp, - __ti_thermal_get_trend); + data, &ti_of_thermal_ops); if (IS_ERR(data->ti_thermal)) { /* Create thermal zone */ data->ti_thermal = thermal_zone_device_register(domain, diff --git a/include/linux/thermal.h b/include/linux/thermal.h index ef90838b36a072..5bc28a70014e88 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -289,19 +289,31 @@ struct thermal_genl_event { enum events event; }; +/** + * struct thermal_zone_of_device_ops - scallbacks for handling DT based zones + * + * Mandatory: + * @get_temp: a pointer to a function that reads the sensor temperature. + * + * Optional: + * @get_trend: a pointer to a function that reads the sensor temperature trend. + */ +struct thermal_zone_of_device_ops { + int (*get_temp)(void *, long *); + int (*get_trend)(void *, long *); +}; + /* Function declarations */ #ifdef CONFIG_THERMAL_OF struct thermal_zone_device * -thermal_zone_of_sensor_register(struct device *dev, int id, - void *data, int (*get_temp)(void *, long *), - int (*get_trend)(void *, long *)); +thermal_zone_of_sensor_register(struct device *dev, int id, void *data, + const struct thermal_zone_of_device_ops *ops); void thermal_zone_of_sensor_unregister(struct device *dev, struct thermal_zone_device *tz); #else static inline struct thermal_zone_device * -thermal_zone_of_sensor_register(struct device *dev, int id, - void *data, int (*get_temp)(void *, long *), - int (*get_trend)(void *, long *)) +thermal_zone_of_sensor_register(struct device *dev, int id, void *data, + const struct thermal_zone_of_device_ops *ops) { return NULL; } From a561e79609b4f59910c94f1174685ef5fa7665d6 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Mon, 8 Dec 2014 18:04:17 +0100 Subject: [PATCH 02/14] thermal: of: Extend of-thermal.c to provide number of trip points This patch extends the of-thermal.c to provide information about number of available trip points. Signed-off-by: Lukasz Majewski Signed-off-by: Eduardo Valentin --- drivers/thermal/of-thermal.c | 21 +++++++++++++++++++++ drivers/thermal/thermal_core.h | 5 +++++ 2 files changed, 26 insertions(+) diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c index b7982f0a6eaf42..7facd23b1d9ba9 100644 --- a/drivers/thermal/of-thermal.c +++ b/drivers/thermal/of-thermal.c @@ -112,6 +112,27 @@ static int of_thermal_get_temp(struct thermal_zone_device *tz, return data->ops->get_temp(data->sensor_data, temp); } +/** + * of_thermal_get_ntrips - function to export number of available trip + * points. + * @tz: pointer to a thermal zone + * + * This function is a globally visible wrapper to get number of trip points + * stored in the local struct __thermal_zone + * + * Return: number of available trip points, -ENODEV when data not available + */ +int of_thermal_get_ntrips(struct thermal_zone_device *tz) +{ + struct __thermal_zone *data = tz->devdata; + + if (!data || IS_ERR(data)) + return -ENODEV; + + return data->ntrips; +} +EXPORT_SYMBOL_GPL(of_thermal_get_ntrips); + static int of_thermal_get_trend(struct thermal_zone_device *tz, int trip, enum thermal_trend *trend) { diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index d15d243de27a7f..1cc5041b7a26de 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -89,9 +89,14 @@ static inline void thermal_gov_user_space_unregister(void) {} #ifdef CONFIG_THERMAL_OF int of_parse_thermal_zones(void); void of_thermal_destroy_zones(void); +int of_thermal_get_ntrips(struct thermal_zone_device *); #else static inline int of_parse_thermal_zones(void) { return 0; } static inline void of_thermal_destroy_zones(void) { } +static inline int of_thermal_get_ntrips(struct thermal_zone_device *tz) +{ + return 0; +} #endif #endif /* __THERMAL_CORE_H__ */ From 1455d9684eb4eb71218dd5e9bf125c50b2ab2817 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Mon, 8 Dec 2014 18:04:18 +0100 Subject: [PATCH 03/14] thermal: of: Extend of-thermal.c to provide check if trip point is valid This patch extends the of-thermal.c to provide check if trip point is valid. Signed-off-by: Lukasz Majewski Signed-off-by: Eduardo Valentin --- drivers/thermal/of-thermal.c | 21 +++++++++++++++++++++ drivers/thermal/thermal_core.h | 6 ++++++ 2 files changed, 27 insertions(+) diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c index 7facd23b1d9ba9..87b9cfe28eb813 100644 --- a/drivers/thermal/of-thermal.c +++ b/drivers/thermal/of-thermal.c @@ -133,6 +133,27 @@ int of_thermal_get_ntrips(struct thermal_zone_device *tz) } EXPORT_SYMBOL_GPL(of_thermal_get_ntrips); +/** + * of_thermal_is_trip_valid - function to check if trip point is valid + * + * @tz: pointer to a thermal zone + * @trip: trip point to evaluate + * + * This function is responsible for checking if passed trip point is valid + * + * Return: true if trip point is valid, false otherwise + */ +bool of_thermal_is_trip_valid(struct thermal_zone_device *tz, int trip) +{ + struct __thermal_zone *data = tz->devdata; + + if (!data || trip >= data->ntrips || trip < 0) + return false; + + return true; +} +EXPORT_SYMBOL_GPL(of_thermal_is_trip_valid); + static int of_thermal_get_trend(struct thermal_zone_device *tz, int trip, enum thermal_trend *trend) { diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 1cc5041b7a26de..58a0dfa4470569 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -90,6 +90,7 @@ static inline void thermal_gov_user_space_unregister(void) {} int of_parse_thermal_zones(void); void of_thermal_destroy_zones(void); int of_thermal_get_ntrips(struct thermal_zone_device *); +bool of_thermal_is_trip_valid(struct thermal_zone_device *, int); #else static inline int of_parse_thermal_zones(void) { return 0; } static inline void of_thermal_destroy_zones(void) { } @@ -97,6 +98,11 @@ static inline int of_thermal_get_ntrips(struct thermal_zone_device *tz) { return 0; } +static inline bool of_thermal_is_trip_valid(struct thermal_zone_device *tz, + int trip) +{ + return 0; +} #endif #endif /* __THERMAL_CORE_H__ */ From 5902549398c091c84b2b9137a6711b34b37cc353 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Mon, 8 Dec 2014 18:04:19 +0100 Subject: [PATCH 04/14] thermal: of: Rename struct __thermal_trip to struct thermal_trip This patch changes name of struct __thermal_trip to thermal_trip and moves declaration of the latter to ./include/linux/thermal.h for better visibility. Signed-off-by: Lukasz Majewski Signed-off-by: Eduardo Valentin --- drivers/thermal/of-thermal.c | 21 +++------------------ include/linux/thermal.h | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c index 87b9cfe28eb813..d3ac117e2ddd68 100644 --- a/drivers/thermal/of-thermal.c +++ b/drivers/thermal/of-thermal.c @@ -36,21 +36,6 @@ /*** Private data structures to represent thermal device tree data ***/ -/** - * struct __thermal_trip - representation of a point in temperature domain - * @np: pointer to struct device_node that this trip point was created from - * @temperature: temperature value in miliCelsius - * @hysteresis: relative hysteresis in miliCelsius - * @type: trip point type - */ - -struct __thermal_trip { - struct device_node *np; - unsigned long int temperature; - unsigned long int hysteresis; - enum thermal_trip_type type; -}; - /** * struct __thermal_bind_param - a match between trip and cooling device * @cooling_device: a pointer to identify the referred cooling device @@ -88,7 +73,7 @@ struct __thermal_zone { /* trip data */ int ntrips; - struct __thermal_trip *trips; + struct thermal_trip *trips; /* cooling binding data */ int num_tbps; @@ -538,7 +523,7 @@ EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_unregister); */ static int thermal_of_populate_bind_params(struct device_node *np, struct __thermal_bind_params *__tbp, - struct __thermal_trip *trips, + struct thermal_trip *trips, int ntrips) { struct of_phandle_args cooling_spec; @@ -641,7 +626,7 @@ static int thermal_of_get_trip_type(struct device_node *np, * Return: 0 on success, proper error code otherwise */ static int thermal_of_populate_trip(struct device_node *np, - struct __thermal_trip *trip) + struct thermal_trip *trip) { int prop; int ret; diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 5bc28a70014e88..b8d91efa854e22 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -303,6 +303,21 @@ struct thermal_zone_of_device_ops { int (*get_trend)(void *, long *); }; +/** + * struct thermal_trip - representation of a point in temperature domain + * @np: pointer to struct device_node that this trip point was created from + * @temperature: temperature value in miliCelsius + * @hysteresis: relative hysteresis in miliCelsius + * @type: trip point type + */ + +struct thermal_trip { + struct device_node *np; + unsigned long int temperature; + unsigned long int hysteresis; + enum thermal_trip_type type; +}; + /* Function declarations */ #ifdef CONFIG_THERMAL_OF struct thermal_zone_device * From 19ca4914eba42b96728972a6d72f144293c92beb Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Mon, 8 Dec 2014 18:04:20 +0100 Subject: [PATCH 05/14] thermal: of: Extend of-thermal to export table of trip points This patch extends the of-thermal.c to export trip points for a given thermal zone. Thermal drivers should use of_thermal_get_trip_points() method to get pointer to table of thermal trip points. Signed-off-by: Lukasz Majewski Signed-off-by: Eduardo Valentin --- drivers/thermal/of-thermal.c | 22 ++++++++++++++++++++++ drivers/thermal/thermal_core.h | 7 +++++++ 2 files changed, 29 insertions(+) diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c index d3ac117e2ddd68..e062bf59ab6c05 100644 --- a/drivers/thermal/of-thermal.c +++ b/drivers/thermal/of-thermal.c @@ -139,6 +139,28 @@ bool of_thermal_is_trip_valid(struct thermal_zone_device *tz, int trip) } EXPORT_SYMBOL_GPL(of_thermal_is_trip_valid); +/** + * of_thermal_get_trip_points - function to get access to a globally exported + * trip points + * + * @tz: pointer to a thermal zone + * + * This function provides a pointer to trip points table + * + * Return: pointer to trip points table, NULL otherwise + */ +const struct thermal_trip * const +of_thermal_get_trip_points(struct thermal_zone_device *tz) +{ + struct __thermal_zone *data = tz->devdata; + + if (!data) + return NULL; + + return data->trips; +} +EXPORT_SYMBOL_GPL(of_thermal_get_trip_points); + static int of_thermal_get_trend(struct thermal_zone_device *tz, int trip, enum thermal_trend *trend) { diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 58a0dfa4470569..9083e75206236c 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -91,6 +91,8 @@ int of_parse_thermal_zones(void); void of_thermal_destroy_zones(void); int of_thermal_get_ntrips(struct thermal_zone_device *); bool of_thermal_is_trip_valid(struct thermal_zone_device *, int); +const struct thermal_trip * const +of_thermal_get_trip_points(struct thermal_zone_device *); #else static inline int of_parse_thermal_zones(void) { return 0; } static inline void of_thermal_destroy_zones(void) { } @@ -103,6 +105,11 @@ static inline bool of_thermal_is_trip_valid(struct thermal_zone_device *tz, { return 0; } +static inline const struct thermal_trip * const +of_thermal_get_trip_points(struct thermal_zone_device *tz) +{ + return NULL; +} #endif #endif /* __THERMAL_CORE_H__ */ From a483361b9322254af898a5bb8172ba0ca5e3c435 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Mon, 8 Dec 2014 18:04:21 +0100 Subject: [PATCH 06/14] thermal: of: Extend current of-thermal.c code to allow setting emulated temp Before this change it was only possible to set get_temp() and get_trend() methods to be used in the common code handling passing parameters via device tree to "cpu-thermal" CPU thermal zone device. Now it is possible to also set emulated value of temperature for debug purposes. Signed-off-by: Lukasz Majewski Acked-by: Eduardo Valentin Signed-off-by: Eduardo Valentin --- drivers/thermal/of-thermal.c | 24 ++++++++++++++++++++++++ include/linux/thermal.h | 3 +++ 2 files changed, 27 insertions(+) diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c index e062bf59ab6c05..e145b66df444e6 100644 --- a/drivers/thermal/of-thermal.c +++ b/drivers/thermal/of-thermal.c @@ -161,6 +161,28 @@ of_thermal_get_trip_points(struct thermal_zone_device *tz) } EXPORT_SYMBOL_GPL(of_thermal_get_trip_points); +/** + * of_thermal_set_emul_temp - function to set emulated temperature + * + * @tz: pointer to a thermal zone + * @temp: temperature to set + * + * This function gives the ability to set emulated value of temperature, + * which is handy for debugging + * + * Return: zero on success, error code otherwise + */ +static int of_thermal_set_emul_temp(struct thermal_zone_device *tz, + unsigned long temp) +{ + struct __thermal_zone *data = tz->devdata; + + if (!data->ops || !data->ops->set_emul_temp) + return -EINVAL; + + return data->ops->set_emul_temp(data->sensor_data, temp); +} + static int of_thermal_get_trend(struct thermal_zone_device *tz, int trip, enum thermal_trend *trend) { @@ -392,6 +414,7 @@ thermal_zone_of_add_sensor(struct device_node *zone, tzd->ops->get_temp = of_thermal_get_temp; tzd->ops->get_trend = of_thermal_get_trend; + tzd->ops->set_emul_temp = of_thermal_set_emul_temp; mutex_unlock(&tzd->lock); return tzd; @@ -520,6 +543,7 @@ void thermal_zone_of_sensor_unregister(struct device *dev, mutex_lock(&tzd->lock); tzd->ops->get_temp = NULL; tzd->ops->get_trend = NULL; + tzd->ops->set_emul_temp = NULL; tz->ops = NULL; tz->sensor_data = NULL; diff --git a/include/linux/thermal.h b/include/linux/thermal.h index b8d91efa854e22..99be7fc79c3bd4 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -297,10 +297,13 @@ struct thermal_genl_event { * * Optional: * @get_trend: a pointer to a function that reads the sensor temperature trend. + * @set_emul_temp: a pointer to a function that sets sensor emulated + * temperature. */ struct thermal_zone_of_device_ops { int (*get_temp)(void *, long *); int (*get_trend)(void *, long *); + int (*set_emul_temp)(void *, unsigned long); }; /** From bdaee57dd748ec854c5c62e9473da6c1fe51022f Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sat, 3 Jan 2015 22:56:56 +0100 Subject: [PATCH 07/14] thermal: of: Remove bogus type qualifier for of_thermal_get_trip_points() With gcc 4.1.2, 4.2, and 4.2.4 (4.4 and later are OK): drivers/thermal/thermal_core.h:110: warning: type qualifiers ignored on function return type Signed-off-by: Geert Uytterhoeven Fixes: ce8be7785922de0e ("thermal: of: Extend of-thermal to export table of trip points") Signed-off-by: Eduardo Valentin --- drivers/thermal/of-thermal.c | 2 +- drivers/thermal/thermal_core.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c index e145b66df444e6..d717f3dab6f141 100644 --- a/drivers/thermal/of-thermal.c +++ b/drivers/thermal/of-thermal.c @@ -149,7 +149,7 @@ EXPORT_SYMBOL_GPL(of_thermal_is_trip_valid); * * Return: pointer to trip points table, NULL otherwise */ -const struct thermal_trip * const +const struct thermal_trip * of_thermal_get_trip_points(struct thermal_zone_device *tz) { struct __thermal_zone *data = tz->devdata; diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 9083e75206236c..0531c752fbbb66 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -91,7 +91,7 @@ int of_parse_thermal_zones(void); void of_thermal_destroy_zones(void); int of_thermal_get_ntrips(struct thermal_zone_device *); bool of_thermal_is_trip_valid(struct thermal_zone_device *, int); -const struct thermal_trip * const +const struct thermal_trip * of_thermal_get_trip_points(struct thermal_zone_device *); #else static inline int of_parse_thermal_zones(void) { return 0; } @@ -105,7 +105,7 @@ static inline bool of_thermal_is_trip_valid(struct thermal_zone_device *tz, { return 0; } -static inline const struct thermal_trip * const +static inline const struct thermal_trip * of_thermal_get_trip_points(struct thermal_zone_device *tz) { return NULL; From f83b12e6963455368d1ef7b98c149608927e8c4e Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Mon, 19 Jan 2015 12:44:04 +0100 Subject: [PATCH 08/14] thermal: of: Enable thermal_zoneX when sensor is correctly added Up till now the thermal_zone mode was by default "disabled". With this patch the default behavior was changed to "enable". One can read the mode at: /sys/class/thermal/thermal_zone0/mode Tested-by: Javi Merino Reported-by: Abhilash Kesavan Signed-off-by: Lukasz Majewski Signed-off-by: Eduardo Valentin --- drivers/thermal/of-thermal.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c index d717f3dab6f141..668fb1bdea9eff 100644 --- a/drivers/thermal/of-thermal.c +++ b/drivers/thermal/of-thermal.c @@ -497,6 +497,9 @@ thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data, if (sensor_specs.np == sensor_np && id == sensor_id) { tzd = thermal_zone_of_add_sensor(child, sensor_np, data, ops); + if (!IS_ERR(tzd)) + tzd->ops->set_mode(tzd, THERMAL_DEVICE_ENABLED); + of_node_put(sensor_specs.np); of_node_put(child); goto exit; From b412c95bf86630c12c6ca208b567ea9b8cc94d4c Mon Sep 17 00:00:00 2001 From: Punit Agrawal Date: Tue, 9 Dec 2014 12:22:01 +0000 Subject: [PATCH 09/14] thermal: Fix cdev registration with THERMAL_NO_LIMIT on 64bit The size of unsigned long varies between 32 and 64 bit systems while the size of phandle arguments is always 32 bits per parameter. On 64-bit systems, cooling devices registered via of-thermal apis fail to bind when the min/max cooling state is specified as THERMAL_NO_LIMIT (-1UL) as there is a mis-match between the value read from the device tree (32bit) and the pre-processor define (64bit). As we're unlikely to need cooling states larger than 32 bits, and for consistency with the size of phandle arguments, explicitly limit THERMAL_NO_LIMIT to 32 bits. Reported-by: Hyungwoo Yang Acked-by: Zhang Rui Signed-off-by: Punit Agrawal Signed-off-by: Eduardo Valentin --- include/dt-bindings/thermal/thermal.h | 2 +- include/linux/thermal.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/dt-bindings/thermal/thermal.h b/include/dt-bindings/thermal/thermal.h index 59822a9958581d..b5e6b0069ac770 100644 --- a/include/dt-bindings/thermal/thermal.h +++ b/include/dt-bindings/thermal/thermal.h @@ -11,7 +11,7 @@ #define _DT_BINDINGS_THERMAL_THERMAL_H /* On cooling devices upper and lower limits */ -#define THERMAL_NO_LIMIT (-1UL) +#define THERMAL_NO_LIMIT (~0) #endif diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 99be7fc79c3bd4..2de3d9e14ccc29 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -38,7 +38,7 @@ #define THERMAL_CSTATE_INVALID -1UL /* No upper/lower limit requirement */ -#define THERMAL_NO_LIMIT THERMAL_CSTATE_INVALID +#define THERMAL_NO_LIMIT ((u32)~0) /* Unit conversion macros */ #define KELVIN_TO_CELSIUS(t) (long)(((long)t-2732 >= 0) ? \ From 23a588594f96c4156f382d9c5d82548965981937 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Thu, 26 Feb 2015 22:47:34 +0800 Subject: [PATCH 10/14] clk: hi6220: register clk for thermal sensor Signed-off-by: Leo Yan --- drivers/clk/hisilicon/clk-hi6220.c | 1 + include/dt-bindings/clock/hi6220-clock.h | 23 ++++++++++++----------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/clk/hisilicon/clk-hi6220.c b/drivers/clk/hisilicon/clk-hi6220.c index 37a0fe5b79901e..b052aef86cc378 100644 --- a/drivers/clk/hisilicon/clk-hi6220.c +++ b/drivers/clk/hisilicon/clk-hi6220.c @@ -132,6 +132,7 @@ static struct hisi_gate_clock hi6220_separated_gate_clks_sys[] __initdata = { { HI6220_UART3_PCLK, "uart3_pclk", "uart3_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 7, 0, }, { HI6220_UART4_PCLK, "uart4_pclk", "uart4_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 8, 0, }, { HI6220_SPI_CLK, "spi_clk", "clk_150m", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 9, 0, }, + { HI6220_TSENSOR_CLK, "tsensor_clk", "clk_bus", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 12, 0, }, { HI6220_MMU_CLK, "mmu_clk", "ddrc_axi1", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x240, 11, 0, }, { HI6220_HIFI_SEL, "hifi_sel", "hifi_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 0, 0, }, { HI6220_MMC0_SYSPLL, "mmc0_syspll", "syspll", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 1, 0, }, diff --git a/include/dt-bindings/clock/hi6220-clock.h b/include/dt-bindings/clock/hi6220-clock.h index b8dbf566c3157e..563c9b9936ad71 100644 --- a/include/dt-bindings/clock/hi6220-clock.h +++ b/include/dt-bindings/clock/hi6220-clock.h @@ -81,17 +81,18 @@ #define HI6220_UART3_PCLK 19 #define HI6220_UART4_PCLK 20 #define HI6220_SPI_CLK 21 -#define HI6220_MMU_CLK 22 -#define HI6220_HIFI_SEL 23 -#define HI6220_MMC0_SYSPLL 24 -#define HI6220_MMC1_SYSPLL 25 -#define HI6220_MMC2_SYSPLL 26 -#define HI6220_MMC0_SEL 27 -#define HI6220_MMC1_SEL 28 -#define HI6220_BBPPLL_SEL 29 -#define HI6220_MEDIA_PLL_SRC 30 -#define HI6220_MMC2_SEL 31 -#define HI6220_CS_ATB_SYSPLL 32 +#define HI6220_TSENSOR_CLK 22 +#define HI6220_MMU_CLK 23 +#define HI6220_HIFI_SEL 24 +#define HI6220_MMC0_SYSPLL 25 +#define HI6220_MMC1_SYSPLL 26 +#define HI6220_MMC2_SYSPLL 27 +#define HI6220_MMC0_SEL 28 +#define HI6220_MMC1_SEL 29 +#define HI6220_BBPPLL_SEL 30 +#define HI6220_MEDIA_PLL_SRC 31 +#define HI6220_MMC2_SEL 32 +#define HI6220_CS_ATB_SYSPLL 33 /* mux clocks */ #define HI6220_MMC0_SRC 35 From 9c36734dcb094e22d8831f3e1253b3968fecd775 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Thu, 26 Feb 2015 22:59:22 +0800 Subject: [PATCH 11/14] cpufreq: hisi-acpu: use cluster0 clock when coupled If use coupled clock, then second clock pointer has not been initialized. So need always use cluster0's clock in this case. Signed-off-by: Leo Yan --- drivers/cpufreq/hisi-acpu-cpufreq.c | 46 +++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/drivers/cpufreq/hisi-acpu-cpufreq.c b/drivers/cpufreq/hisi-acpu-cpufreq.c index 2ad5013c19c149..d8a7c653648c4a 100644 --- a/drivers/cpufreq/hisi-acpu-cpufreq.c +++ b/drivers/cpufreq/hisi-acpu-cpufreq.c @@ -17,6 +17,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include #include #include @@ -41,10 +42,16 @@ static atomic_t cluster_usage[MAX_CLUSTERS] = { static struct clk *clk[MAX_CLUSTERS]; static struct mutex cluster_lock[MAX_CLUSTERS]; +static struct thermal_cooling_device *cdev; + +static inline int cpu_to_cluster(int cpu) +{ + return coupled_clusters ? 0 : topology_physical_package_id(cpu); +} static unsigned int hisi_acpu_cpufreq_get_rate(unsigned int cpu) { - int cluster = topology_physical_package_id(cpu); + int cluster = cpu_to_cluster(cpu); unsigned int freq; mutex_lock(&cluster_lock[cluster]); @@ -63,7 +70,7 @@ static int hisi_acpu_cpufreq_set_target(struct cpufreq_policy *policy, unsigned int freqs_new; int ret = 0; - cluster = topology_physical_package_id(cpu); + cluster = cpu_to_cluster(cpu); freqs_new = freq_table[cluster][index].frequency; pr_debug("%s: cluster %d freq_new %d\n", __func__, cluster, freqs_new); @@ -80,7 +87,7 @@ static int hisi_acpu_cpufreq_set_target(struct cpufreq_policy *policy, static void put_cluster_clk_and_freq_table(struct device *cpu_dev) { - u32 cluster = topology_physical_package_id(cpu_dev->id); + u32 cluster = cpu_to_cluster(cpu_dev->id); if (atomic_dec_return(&cluster_usage[cluster])) return; @@ -100,7 +107,7 @@ static int get_cluster_clk_and_freq_table(struct device *cpu_dev) char name[6]; int ret; - cluster = topology_physical_package_id(cpu_dev->id); + cluster = cpu_to_cluster(cpu_dev->id); if (atomic_inc_return(&cluster_usage[cluster]) != 1) return 0; @@ -158,7 +165,7 @@ static int get_cluster_clk_and_freq_table(struct device *cpu_dev) /* Per-CPU initialization */ static int hisi_acpu_cpufreq_init(struct cpufreq_policy *policy) { - u32 cur_cluster = topology_physical_package_id(policy->cpu); + u32 cur_cluster = cpu_to_cluster(policy->cpu); struct device *cpu_dev; int ret; @@ -243,7 +250,7 @@ MODULE_DEVICE_TABLE(of, hisi_acpu_cpufreq_match); static int hisi_acpu_cpufreq_probe(struct platform_device *pdev) { int ret = 0, i; - struct device_node *np; + struct device_node *np, *cpus; np = pdev->dev.of_node; if (!np) { @@ -264,12 +271,39 @@ static int hisi_acpu_cpufreq_probe(struct platform_device *pdev) dev_err(&pdev->dev, "%s: failed to register cpufreq driver\n", __func__); + cpus = of_find_node_by_path("/cpus"); + if (!cpus) { + dev_err(&pdev->dev, "failed to find cpus node\n"); + return 0; + } + + np = of_get_next_child(cpus, NULL); + if (!np) { + dev_err(&pdev->dev, "failed to find cpus child node\n"); + of_node_put(cpus); + return 0; + } + + if (of_find_property(np, "#cooling-cells", NULL)) { + cdev = of_cpufreq_cooling_register(np, cpu_present_mask); + if (IS_ERR(cdev)) { + dev_err(&pdev->dev, "running cpufreq without cooling device: %ld\n", + PTR_ERR(cdev)); + cdev = NULL; + } + } + of_node_put(np); + of_node_put(cpus); + + return 0; + out: return ret; } static int hisi_acpu_cpufreq_remove(struct platform_device *pdev) { + cpufreq_cooling_unregister(cdev); cpufreq_unregister_driver(&hisi_acpu_cpufreq_driver); return 0; } From 3d9d845423807b7eb84c5ccd4826e6d0122d2da6 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Mon, 2 Mar 2015 14:14:41 +0800 Subject: [PATCH 12/14] thermal: hisilicon: support thermal sensor This patch adds the support for hisilicon thermal sensor, within hisilicon SoC, the thermal module has 4 sensors: 1. sensor 0: local sensor; 2. sensor 1: remote sensor for ACPU cluster 1; 3. sensor 2: remote sensor for ACPU cluster 2; 4. sensor 3: remote sensor for GPU. So this driver will register these sensors for thermal framework; and use device tree to bind cooling device. Signed-off-by: Leo Yan --- drivers/thermal/Kconfig | 10 + drivers/thermal/Makefile | 1 + drivers/thermal/hisi_thermal.c | 543 +++++++++++++++++++++++++++++++++ 3 files changed, 554 insertions(+) create mode 100644 drivers/thermal/hisi_thermal.c diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index f554d25b439971..87a1361a7c46ab 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -124,6 +124,16 @@ config THERMAL_EMULATION because userland can easily disable the thermal policy by simply flooding this sysfs node with low temperature values. +config HISI_THERMAL + tristate "Hisilicon thermal driver" + depends on ARCH_HISI + depends on CPU_THERMAL + depends on OF + help + Enable this to plug hisilicon's thermal sensor driver into the Linux + thermal framework. cpufreq is used as the cooling device to throttle + CPUs when the passive trip is crossed. + config IMX_THERMAL tristate "Temperature sensor driver for Freescale i.MX SoCs" depends on CPU_THERMAL diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 39c4fe87da2f83..0291353fe3b6ba 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -26,6 +26,7 @@ obj-y += samsung/ obj-$(CONFIG_DOVE_THERMAL) += dove_thermal.o obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o +obj-$(CONFIG_HISI_THERMAL) += hisi_thermal.o obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o obj-$(CONFIG_DB8500_CPUFREQ_COOLING) += db8500_cpufreq_cooling.o obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o diff --git a/drivers/thermal/hisi_thermal.c b/drivers/thermal/hisi_thermal.c new file mode 100644 index 00000000000000..04f133053b2ea8 --- /dev/null +++ b/drivers/thermal/hisi_thermal.c @@ -0,0 +1,543 @@ +/* + * Hisilicon thermal sensor driver + * + * Copyright (c) 2014-2015 Hisilicon Limited. + * Copyright (c) 2014-2015 Linaro Limited. + * + * Xinwei Kong + * Leo Yan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#define TEMP0_LAG (0x0) +#define TEMP0_TH (0x4) +#define TEMP0_RST_TH (0x8) +#define TEMP0_CFG (0xC) +#define TEMP0_EN (0x10) +#define TEMP0_INT_EN (0x14) +#define TEMP0_INT_CLR (0x18) +#define TEMP0_RST_MSK (0x1C) +#define TEMP0_RAW_INT (0x20) +#define TEMP0_MSK_INT (0x24) +#define TEMP0_VALUE (0x28) + +#define HISI_TEMP_BASE (-60) +#define HISI_TEMP_PASSIVE (85000) + +#define HISI_MAX_SENSORS 4 + +struct hisi_thermal_sensor { + struct hisi_thermal_data *thermal; + struct thermal_zone_device *tzd; + + uint32_t id; + uint32_t lag; + uint32_t thres_temp, reset_temp; +}; + +struct hisi_thermal_data { + struct platform_device *pdev; + struct clk *clk; + + int irq, irq_bind_sensor; + bool irq_enabled; + + unsigned int sensors_num; + struct hisi_thermal_sensor sensors[HISI_MAX_SENSORS]; + + void __iomem *regs; +}; + +static DEFINE_SPINLOCK(thermal_lock); + +/* in millicelsius */ +static inline int _step_to_temp(int step) +{ + /* + * Every step equals (1 * 200) / 255 celsius, and finally + * need convert to millicelsius. + */ + return (HISI_TEMP_BASE + (step * 200 / 255)) * 1000; +} + +static inline int _temp_to_step(int temp) +{ + return ((temp / 1000 - HISI_TEMP_BASE) * 255 / 200); +} + +static long hisi_thermal_get_sensor_temp(struct hisi_thermal_data *data, + struct hisi_thermal_sensor *sensor) +{ + unsigned long flags; + int val; + + spin_lock_irqsave(&thermal_lock, flags); + + /* disable module firstly */ + writel(0x0, data->regs + TEMP0_EN); + + /* select sensor id */ + writel((sensor->id << 12), data->regs + TEMP0_CFG); + + /* enable module */ + writel(0x1, data->regs + TEMP0_EN); + + mdelay(5); + + val = readl(data->regs + TEMP0_VALUE); + val = _step_to_temp(val); + + /* adjust for negative value */ + val = (val < 0) ? 0 : val; + + spin_unlock_irqrestore(&thermal_lock, flags); + + return val; +} + +static void hisi_thermal_bind_irq(struct hisi_thermal_data *data) +{ + struct hisi_thermal_sensor *sensor; + unsigned long flags; + + sensor = &data->sensors[data->irq_bind_sensor]; + + spin_lock_irqsave(&thermal_lock, flags); + + /* disable module firstly */ + writel(0x0, data->regs + TEMP0_EN); + + /* select sensor id */ + writel((sensor->id << 12), data->regs + TEMP0_CFG); + + /* enable for interrupt */ + writel(_temp_to_step(sensor->thres_temp) | + _temp_to_step(sensor->thres_temp) << 8 | + _temp_to_step(sensor->thres_temp) << 16 | + _temp_to_step(sensor->thres_temp) << 24, + data->regs + TEMP0_TH); + + writel(_temp_to_step(sensor->reset_temp), data->regs + TEMP0_RST_TH); + + /* enable module */ + writel(0x1, data->regs + TEMP0_EN); + + mdelay(5); + + spin_unlock_irqrestore(&thermal_lock, flags); + return; +} + +static void hisi_thermal_enable_sensor(struct hisi_thermal_data *data) +{ + struct hisi_thermal_sensor *sensor; + unsigned long flags; + + sensor = &data->sensors[data->irq_bind_sensor]; + + spin_lock_irqsave(&thermal_lock, flags); + + /* disable module firstly */ + writel(0x0, data->regs + TEMP0_RST_MSK); + writel(0x0, data->regs + TEMP0_EN); + + /* select sensor id */ + writel((sensor->id << 12), data->regs + TEMP0_CFG); + + /* enable for interrupt */ + writel(_temp_to_step(sensor->thres_temp) | + _temp_to_step(sensor->thres_temp) << 8 | + _temp_to_step(sensor->thres_temp) << 16 | + _temp_to_step(sensor->thres_temp) << 24, + data->regs + TEMP0_TH); + + writel(_temp_to_step(sensor->reset_temp), data->regs + TEMP0_RST_TH); + + writel(0x0, data->regs + TEMP0_MSK_INT); + writel(0x1, data->regs + TEMP0_INT_EN); + + /* enable module */ + writel(0x1, data->regs + TEMP0_RST_MSK); + writel(0x1, data->regs + TEMP0_EN); + + mdelay(5); + + spin_unlock_irqrestore(&thermal_lock, flags); + return; +} + +static void hisi_thermal_disable_sensor(struct hisi_thermal_data *data) +{ + unsigned long flags; + + spin_lock_irqsave(&thermal_lock, flags); + + /* disable sensor module */ + writel(0x0, data->regs + TEMP0_INT_EN); + writel(0x0, data->regs + TEMP0_RST_MSK); + writel(0x0, data->regs + TEMP0_EN); + + spin_unlock_irqrestore(&thermal_lock, flags); + return; +} + +static int hisi_thermal_get_temp(void *_sensor, long *temp) +{ + struct hisi_thermal_sensor *sensor = _sensor; + struct hisi_thermal_data *data = sensor->thermal; + + *temp = hisi_thermal_get_sensor_temp(data, sensor); + + dev_dbg(&data->pdev->dev, "id=%d, irq=%d, temp=%ld, thres=%d\n", + sensor->id, data->irq_enabled, *temp, sensor->thres_temp); + + /* + * Bind irq to sensor for two cases: + * Reenable alarm IRQ if temperature below threshold; + * if irq has been enabled, always set it; + */ + if (!data->irq_enabled && *temp < sensor->thres_temp) { + data->irq_enabled = true; + hisi_thermal_bind_irq(data); + } else if (data->irq_enabled) + hisi_thermal_bind_irq(data); + + return 0; +} + +static struct thermal_zone_of_device_ops hisi_of_thermal_ops = { + .get_temp = hisi_thermal_get_temp, +}; + +static irqreturn_t hisi_thermal_alarm_irq(int irq, void *dev) +{ + struct hisi_thermal_data *data = dev; + unsigned long flags; + + spin_lock_irqsave(&thermal_lock, flags); + + /* set maximum threshold so irq will not be triggered */ + writel(0xFFFFFFFF, data->regs + TEMP0_TH); + + /* disable module */ + while (readl(data->regs + TEMP0_RAW_INT) & 0x1) + writel(0x1, data->regs + TEMP0_INT_CLR); + + spin_unlock_irqrestore(&thermal_lock, flags); + data->irq_enabled = false; + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t hisi_thermal_alarm_irq_thread(int irq, void *dev) +{ + struct hisi_thermal_data *data = dev; + struct hisi_thermal_sensor *sensor; + int i; + + sensor = &data->sensors[data->irq_bind_sensor]; + + dev_crit(&data->pdev->dev, "THERMAL ALARM: T > %d\n", + sensor->thres_temp / 1000); + + for (i = 0; i < data->sensors_num; i++) + thermal_zone_device_update(data->sensors[i].tzd); + + return IRQ_HANDLED; +} + +static int hisi_thermal_init_sensor(struct platform_device *pdev, + struct device_node *np, + struct hisi_thermal_data *data, + int index) +{ + struct hisi_thermal_sensor *sensor; + int ret; + + sensor = &data->sensors[index]; + + ret = of_property_read_u32(np, "hisilicon,tsensor-id", + &sensor->id); + if (ret) { + dev_err(&pdev->dev, "failed to get id %d: %d\n", index, ret); + return ret; + } + + ret = of_property_read_u32(np, "hisilicon,tsensor-lag-value", + &sensor->lag); + if (ret) { + dev_err(&pdev->dev, "failed to get lag %d: %d\n", index, ret); + return ret; + } + + ret = of_property_read_u32(np, "hisilicon,tsensor-thres-temp", + &sensor->thres_temp); + if (ret) { + dev_err(&pdev->dev, "failed to get thres value %d: %d\n", + index, ret); + return ret; + } + + ret = of_property_read_u32(np, "hisilicon,tsensor-reset-temp", + &sensor->reset_temp); + if (ret) { + dev_err(&pdev->dev, "failed to get reset value %d: %d\n", + index, ret); + return ret; + } + + if (of_property_read_bool(np, "hisilicon,tsensor-bind-irq")) { + + if (data->irq_bind_sensor != -1) + dev_warn(&pdev->dev, "irq has bound to index %d\n", + data->irq_bind_sensor); + + /* bind irq to this sensor */ + data->irq_bind_sensor = index; + } + + sensor->thermal = data; + sensor->tzd = thermal_zone_of_sensor_register(&pdev->dev, sensor->id, + sensor, &hisi_of_thermal_ops); + if (IS_ERR(sensor->tzd)) { + ret = PTR_ERR(sensor->tzd); + dev_err(&pdev->dev, "failed to register sensor id %d: %d\n", + sensor->id, ret); + return ret; + } + + return 0; +} + +static int hisi_thermal_get_sensor_data(struct platform_device *pdev) +{ + struct hisi_thermal_data *data = platform_get_drvdata(pdev); + struct device_node *np = pdev->dev.of_node; + struct device_node *child_np; + int ret, i, index; + + data->irq_bind_sensor = -1; + + /* sensor initialization */ + index = 0; + for_each_child_of_node(np, child_np) { + + if (index >= HISI_MAX_SENSORS) { + dev_err(&pdev->dev, "unsupported sensor number\n"); + ret = -EINVAL; + goto err_init_sensors; + } + + ret = hisi_thermal_init_sensor(pdev, child_np, data, index); + if (ret) + goto err_init_sensors; + + index++; + } + + if (data->irq_bind_sensor == -1) { + dev_err(&pdev->dev, "don't bind irq for sensor\n"); + ret = -EINVAL; + goto err_init_sensors; + } + + data->sensors_num = index; + return 0; + +err_init_sensors: + for (i = 0; i < index; i++) + thermal_zone_of_sensor_unregister(&pdev->dev, + data->sensors[i].tzd); + return ret; +} + +static const struct of_device_id of_hisi_thermal_match[] = { + { .compatible = "hisilicon,tsensor" }, + { /* end */ } +}; +MODULE_DEVICE_TABLE(of, of_hisi_thermal_match); + +static void hisi_thermal_toggle_sensor(struct hisi_thermal_sensor *sensor, + bool on) +{ + struct thermal_zone_device *tzd = sensor->tzd; + + tzd->ops->set_mode(tzd, + on ? THERMAL_DEVICE_ENABLED : THERMAL_DEVICE_DISABLED); +} + +static int hisi_thermal_probe(struct platform_device *pdev) +{ + struct hisi_thermal_data *data; + struct resource *res; + int i; + int ret; + + if (!cpufreq_get_current_driver()) { + dev_dbg(&pdev->dev, "no cpufreq driver!"); + return -EPROBE_DEFER; + } + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->pdev = pdev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + data->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(data->regs)) { + dev_err(&pdev->dev, "failed to get io address\n"); + return PTR_ERR(data->regs); + } + + data->irq = platform_get_irq(pdev, 0); + if (data->irq < 0) + return data->irq; + + ret = devm_request_threaded_irq(&pdev->dev, data->irq, + hisi_thermal_alarm_irq, hisi_thermal_alarm_irq_thread, + 0, "hisi_thermal", data); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request alarm irq: %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, data); + + data->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(data->clk)) { + ret = PTR_ERR(data->clk); + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, + "failed to get thermal clk: %d\n", ret); + return ret; + } + + /* enable clock for thermal */ + ret = clk_prepare_enable(data->clk); + if (ret) { + dev_err(&pdev->dev, "failed to enable thermal clk: %d\n", ret); + return ret; + } + + ret = hisi_thermal_get_sensor_data(pdev); + if (ret) { + dev_err(&pdev->dev, "failed to get sensor data\n"); + goto err_get_sensor_data; + return ret; + } + + hisi_thermal_enable_sensor(data); + data->irq_enabled = true; + + for (i = 0; i < data->sensors_num; i++) + hisi_thermal_toggle_sensor(&data->sensors[i], true); + + return 0; + +err_get_sensor_data: + clk_disable_unprepare(data->clk); + return ret; +} + +static int hisi_thermal_remove(struct platform_device *pdev) +{ + struct hisi_thermal_data *data = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < data->sensors_num; i++) { + struct hisi_thermal_sensor *sensor = &data->sensors[i]; + + hisi_thermal_toggle_sensor(sensor, false); + thermal_zone_of_sensor_unregister(&pdev->dev, sensor->tzd); + } + + hisi_thermal_disable_sensor(data); + clk_disable_unprepare(data->clk); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int hisi_thermal_suspend(struct device *dev) +{ + struct hisi_thermal_data *data = dev_get_drvdata(dev); + + hisi_thermal_disable_sensor(data); + data->irq_enabled = false; + + clk_disable_unprepare(data->clk); + + return 0; +} + +static int hisi_thermal_resume(struct device *dev) +{ + struct hisi_thermal_data *data = dev_get_drvdata(dev); + + clk_prepare_enable(data->clk); + + data->irq_enabled = true; + hisi_thermal_enable_sensor(data); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(hisi_thermal_pm_ops, + hisi_thermal_suspend, hisi_thermal_resume); + +static struct platform_driver hisi_thermal_driver = { + .driver = { + .name = "hisi_thermal", + .owner = THIS_MODULE, + .pm = &hisi_thermal_pm_ops, + .of_match_table = of_hisi_thermal_match, + }, + .probe = hisi_thermal_probe, + .remove = hisi_thermal_remove, +}; + +static int __init hisi_thermal_init(void) +{ + return platform_driver_register(&hisi_thermal_driver); +} + +static void __exit hisi_thermal_exit(void) +{ + platform_driver_unregister(&hisi_thermal_driver); +} + +late_initcall(hisi_thermal_init); +module_exit(hisi_thermal_exit); + +MODULE_AUTHOR("Xinwei Kong "); +MODULE_AUTHOR("Leo Yan "); +MODULE_DESCRIPTION("Hisilicon thermal driver"); +MODULE_LICENSE("GPL v2"); From c77f5f4407d8ca829ceaf0d136794b84c8c3dc51 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Mon, 2 Mar 2015 14:16:10 +0800 Subject: [PATCH 13/14] dts: hi6220: add nodes for thermal There have two parts for thermal sensor: 1. The first part is related with thermal sensor, every sensor need to pass the sensor id, the threshold and reset temperature; so th sensor will be registered as sensor devices; 2. The second part is related with thermal zones, in this part it will define the thermal zones and which sensor device should be bound to. it also need specify the polling interval for every thermal zone. U can check the thermal status from user space: /sys/class/thermal/. Signed-off-by: Leo Yan --- arch/arm64/boot/dts/hi6220.dtsi | 180 +++++++++++++++++++++++++------- 1 file changed, 144 insertions(+), 36 deletions(-) diff --git a/arch/arm64/boot/dts/hi6220.dtsi b/arch/arm64/boot/dts/hi6220.dtsi index 0849ca2710d100..9cad6be8ee2609 100644 --- a/arch/arm64/boot/dts/hi6220.dtsi +++ b/arch/arm64/boot/dts/hi6220.dtsi @@ -5,6 +5,7 @@ */ #include +#include / { cpus { @@ -20,12 +21,13 @@ clock-latency = <0>; operating-points = < /* kHz */ - 208000 0 - 432000 0 - 729000 0 - 960000 0 1200000 0 + 960000 0 + 729000 0 + 432000 0 + 208000 0 >; + #cooling-cells = <2>; /* min followed by max */ }; cpu1: cpu@1 { compatible = "arm,cortex-a53", "arm,armv8"; @@ -1114,42 +1116,148 @@ interrupts = <0 77 0x4>; }; - thermal { - compatible = "hisilicon,hisi-tsensor-driver"; - reg = <0x0 0xf7030000 0x0 0x1000>; - tsensor-enable = <1>; - tsensor-num = <3>; - acpu-freq-limit-num = <3>; - acpu-freq-limit-table = <729000 432000 208000>; /* ~KHz */ - - acpu_c1: temp@a1 { - compatible = "hisilicon,hisi-tsensor0"; - tsensor-type = <0x1>; - tsensor-sel = <1>; - tsensor-reset-value = <100>; - tsensor-thres-value = <70>; - tsensor-alarm-count = <4>; - tsensor-recover-count = <10>; + + tsensor: tsensor@0,f7030700 { + compatible = "hisilicon,tsensor"; + reg = <0x0 0xf7030700 0x0 0x1000>; + interrupts = <0 7 0x4>; + clocks = <&clock_sys HI6220_TSENSOR_CLK>; + clock-names = "thermal_clk"; + #thermal-sensor-cells = <1>; + + local_sensor { + hisilicon,tsensor-id = <0>; + hisilicon,tsensor-lag-value = <10>; + hisilicon,tsensor-thres-temp = <80000>; + hisilicon,tsensor-reset-temp = <100000>; + hisilicon,tsensor-bind-irq; + }; + + acpu1_sensor { + hisilicon,tsensor-id = <1>; + hisilicon,tsensor-lag-value = <10>; + hisilicon,tsensor-thres-temp = <80000>; + hisilicon,tsensor-reset-temp = <100000>; + }; + + acpu0_sensor { + hisilicon,tsensor-id = <2>; + hisilicon,tsensor-lag-value = <10>; + hisilicon,tsensor-thres-temp = <80000>; + hisilicon,tsensor-reset-temp = <100000>; }; - acpu_c0: temp@a2 { - compatible = "hisilicon,hisi-tsensor1"; - tsensor-type = <0x0>; - tsensor-sel = <2>; - tsensor-reset-value = <100>; - tsensor-thres-value = <70>; - tsensor-alarm-count = <4>; - tsensor-recover-count = <10>; + gpu_sensor { + hisilicon,tsensor-id = <3>; + hisilicon,tsensor-lag-value = <10>; + hisilicon,tsensor-thres-temp = <80000>; + hisilicon,tsensor-reset-temp = <100000>; }; + }; + + thermal-zones { + local: local { + polling-delay-passive = <1000>; /* milliseconds */ + polling-delay = <5000>; /* milliseconds */ + + /* sensor ID */ + thermal-sensors = <&tsensor 0>; + + trips { + local_alert: local_alert { + temperature = <70000>; /* millicelsius */ + hysteresis = <2000>; /* millicelsius */ + type = "passive"; + }; + local_crit: local_crit { + temperature = <90000>; /* millicelsius */ + hysteresis = <2000>; /* millicelsius */ + type = "critical"; + }; + }; + + cooling-maps { + /* There are currently no cooling maps because there are no cooling devices */ + }; + }; + + cluster1: cluster1 { + polling-delay-passive = <1000>; /* milliseconds */ + polling-delay = <5000>; /* milliseconds */ + + /* sensor ID */ + thermal-sensors = <&tsensor 1>; - gpu: temp@a3 { - compatible = "hisilicon,hisi-tsensor2"; - tsensor-type = <0x2>; - tsensor-sel = <3>; - tsensor-reset-value = <100>; - tsensor-thres-value = <70>; - tsensor-alarm-count = <4>; - tsensor-recover-count = <10>; + trips { + cluster1_alert: cluster1_alert { + temperature = <70000>; /* millicelsius */ + hysteresis = <2000>; /* millicelsius */ + type = "passive"; + }; + cluster1_crit: cluster1_crit { + temperature = <90000>; /* millicelsius */ + hysteresis = <2000>; /* millicelsius */ + type = "critical"; + }; + }; + + cooling-maps { + /* There are currently no cooling maps because there are no cooling devices */ + }; + }; + + cluster0: cluster0 { + polling-delay-passive = <1000>; /* milliseconds */ + polling-delay = <5000>; /* milliseconds */ + + /* sensor ID */ + thermal-sensors = <&tsensor 2>; + + trips { + cluster0_alert: cluster0_alert { + temperature = <70000>; /* millicelsius */ + hysteresis = <2000>; /* millicelsius */ + type = "passive"; + }; + cluster0_crit: cluster0_crit { + temperature = <90000>; /* millicelsius */ + hysteresis = <2000>; /* millicelsius */ + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&cluster0_alert>; + cooling-device = + <&cpu0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + gpu: gpu { + polling-delay-passive = <1000>; /* milliseconds */ + polling-delay = <5000>; /* milliseconds */ + + /* sensor ID */ + thermal-sensors = <&tsensor 3>; + + trips { + gpu_alert: gpu_alert { + temperature = <70000>; /* millicelsius */ + hysteresis = <2000>; /* millicelsius */ + type = "passive"; + }; + gpu_crit: gpu_crit { + temperature = <90000>; /* millicelsius */ + hysteresis = <2000>; /* millicelsius */ + type = "critical"; + }; + }; + + cooling-maps { + /* There are currently no cooling maps because there are no cooling devices */ + }; }; }; }; From 89b4aa4b54b5a23bf0adb5b79a32446544b65a5b Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Mon, 2 Mar 2015 14:28:50 +0800 Subject: [PATCH 14/14] ARM64: defconfig: add support for thermal Enable thermal configuration so that can use thermal framework and its governor for constraint for cpu frequency. Signed-off-by: Leo Yan --- arch/arm64/configs/defconfig | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 646c1764586607..300405d5761821 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -249,7 +249,14 @@ CONFIG_SPI=y CONFIG_SPI_PL022=y CONFIG_GPIO_SYSFS=y CONFIG_GPIO_XGENE=y -CONFIG_SENSORS_HI6220=y +CONFIG_THERMAL=y +CONFIG_THERMAL_HWMON=y +CONFIG_THERMAL_OF=y +CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y +CONFIG_THERMAL_GOV_STEP_WISE=y +CONFIG_THERMAL_GOV_USER_SPACE=y +CONFIG_CPU_THERMAL=y +CONFIG_HISI_THERMAL=y CONFIG_REGULATOR=y CONFIG_REGULATOR_FIXED_VOLTAGE=y CONFIG_REGULATOR_HI6220=y