[2/2] thermal: of: Match function for of-thermal
diff mbox

Message ID 1421171482-16101-3-git-send-email-kapileshwar.singh@arm.com
State Changes Requested
Delegated to: Eduardo Valentin
Headers show

Commit Message

Kapileshwar Singh Jan. 13, 2015, 5:51 p.m. UTC
From: KP Singh <kapileshwar.singh@arm.com>

This patch introduces the following changes:

* Creation of a parsed_bind_params data structure to
  uniquely identify the bind parameters per coolig
  device and optimize the match callback

* Adding a match call-back to of-thermal to replace
  the specific bind operation

* In the previous implementation the thermal_zone_params
  and thermal_bind_params are not populated and the weight
  parameter which is read from the device tree
  (as contribution) does not get propagated to the governor

Signed-off-by: Kapileshwar Singh <kapileshwar.singh@arm.com>
---
 drivers/thermal/of-thermal.c |  340 ++++++++++++++++++++++++++++++++++--------
 1 file changed, 278 insertions(+), 62 deletions(-)

Comments

Eduardo Valentin Jan. 20, 2015, 1:57 p.m. UTC | #1
Same here, enconding is not helping.

On Tue, Jan 13, 2015 at 05:51:22PM +0000, Kapileshwar Singh wrote:
> From: KP Singh <kapileshwar.singh@arm.com>
> 
> This patch introduces the following changes:
> 
> * Creation of a parsed_bind_params data structure to
>   uniquely identify the bind parameters per coolig

s/coolig/cooling

>   device and optimize the match callback
> 
> * Adding a match call-back to of-thermal to replace
>   the specific bind operation
> 
> * In the previous implementation the thermal_zone_params
>   and thermal_bind_params are not populated and the weight
>   parameter which is read from the device tree
>   (as contribution) does not get propagated to the governor

Is it possible to break this patch into smaller parts?


> 
> Signed-off-by: Kapileshwar Singh <kapileshwar.singh@arm.com>
> ---
>  drivers/thermal/of-thermal.c |  340 ++++++++++++++++++++++++++++++++++--------
>  1 file changed, 278 insertions(+), 62 deletions(-)
> 
> diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c
> index e145b66df444..82f7e4b48845 100644
> --- a/drivers/thermal/of-thermal.c
> +++ b/drivers/thermal/of-thermal.c
> @@ -31,6 +31,7 @@
>  #include <linux/export.h>
>  #include <linux/string.h>
>  #include <linux/thermal.h>
> +#include <linux/bitops.h>
>  
>  #include "thermal_core.h"
>  
> @@ -54,6 +55,26 @@ struct __thermal_bind_params {
>  };
>  
>  /**
> + * struct parsed_bind_params - parsed bind parameters from device tree
> + * @cdev_node: a pointer to identify the device tree node of the cdev
> + * @trip_mask: a mask of all the trips the cdev is to be bound to
> + * @weight: the percentage (0 to 100) of cooling conrtibution
> + * @binding_limits: the max and min limits for each trip point
> + *
> + * The struct __thermal_bind_params is a raw representation of the
> + * data read from the device tree. This is then parsed into this
> + * struct such that there is only on param per cooling device

s/on/one

> + * and can be correlated efficiently with thermal_bind_params.
> + */
> +
> +struct parsed_bind_params {
> +	struct device_node *cdev_node;
> +	unsigned long trip_mask;
> +	unsigned int weight;
> +	unsigned long *binding_limits;
> +};
> +
> +/**
>   * struct __thermal_zone - internal representation of a thermal zone
>   * @mode: current thermal zone device mode (enabled/disabled)
>   * @passive_delay: polling interval while passive cooling is activated
> @@ -78,6 +99,8 @@ struct __thermal_zone {
>  	/* cooling binding data */
>  	int num_tbps;
>  	struct __thermal_bind_params *tbps;
> +	struct parsed_bind_params *pbs;
> +	int num_parsed_tbps;
>  
>  	/* sensor interface */
>  	void *sensor_data;
> @@ -208,60 +231,6 @@ static int of_thermal_get_trend(struct thermal_zone_device *tz, int trip,
>  	return 0;
>  }
>  
> -static int of_thermal_bind(struct thermal_zone_device *thermal,
> -			   struct thermal_cooling_device *cdev)
> -{
> -	struct __thermal_zone *data = thermal->devdata;
> -	int i;
> -
> -	if (!data || IS_ERR(data))
> -		return -ENODEV;
> -
> -	/* find where to bind */
> -	for (i = 0; i < data->num_tbps; i++) {
> -		struct __thermal_bind_params *tbp = data->tbps + i;
> -
> -		if (tbp->cooling_device == cdev->np) {
> -			int ret;
> -
> -			ret = thermal_zone_bind_cooling_device(thermal,
> -						tbp->trip_id, cdev,
> -						tbp->max,
> -						tbp->min);
> -			if (ret)
> -				return ret;
> -		}
> -	}
> -
> -	return 0;
> -}
> -
> -static int of_thermal_unbind(struct thermal_zone_device *thermal,
> -			     struct thermal_cooling_device *cdev)
> -{
> -	struct __thermal_zone *data = thermal->devdata;
> -	int i;
> -
> -	if (!data || IS_ERR(data))
> -		return -ENODEV;
> -
> -	/* find where to unbind */
> -	for (i = 0; i < data->num_tbps; i++) {
> -		struct __thermal_bind_params *tbp = data->tbps + i;
> -
> -		if (tbp->cooling_device == cdev->np) {
> -			int ret;
> -
> -			ret = thermal_zone_unbind_cooling_device(thermal,
> -						tbp->trip_id, cdev);
> -			if (ret)
> -				return ret;
> -		}
> -	}
> -
> -	return 0;
> -}
> -
>  static int of_thermal_get_mode(struct thermal_zone_device *tz,
>  			       enum thermal_device_mode *mode)
>  {
> @@ -384,9 +353,6 @@ static struct thermal_zone_device_ops of_thermal_ops = {
>  	.get_trip_hyst = of_thermal_get_trip_hyst,
>  	.set_trip_hyst = of_thermal_set_trip_hyst,
>  	.get_crit_temp = of_thermal_get_crit_temp,
> -
> -	.bind = of_thermal_bind,
> -	.unbind = of_thermal_unbind,
>  };
>  
>  /***   sensor API   ***/
> @@ -621,6 +587,237 @@ static int thermal_of_populate_bind_params(struct device_node *np,
>  	return ret;
>  }
>  
> +int of_thermal_match_bind_param(struct thermal_zone_device *tz,
> +				struct thermal_cooling_device *cdev,
> +				int index)
> +{
> +	struct __thermal_zone *__tz;
> +	struct parsed_bind_params *pbs;
> +	struct thermal_bind_params *tbp;
> +	int i;
> +
> +	if (!tz->devdata)
> +		return 1;
> +
> +	__tz = (struct __thermal_zone *)tz->devdata;
> +
> +	if (!__tz->pbs || !__tz->num_parsed_tbps)
> +		return 1;
> +
> +	pbs = __tz->pbs;
> +	tbp = tz->tzp->tbp;
> +
> +	for (i = 0; i < __tz->num_parsed_tbps; i++) {
> +		if (pbs[i].cdev_node == cdev->np) {
> +			if (tbp[index].trip_mask != pbs[i].trip_mask)
> +				return 1;
> +			if (tbp[index].binding_limits != pbs[i].binding_limits)
> +				return 1;
> +			if (tbp[index].weight != pbs[i].weight)
> +				return 1;
> +			return 0;
> +		}
> +	}
> +	return 1;
> +}
> +
> +/**
> + * get_unique_mask - return a mask indicating repeated cdevs
> + * @__tbp: __thermal_bind_params internal data structre
> + * @num_tbps: total number of cdev<->trip bindings
> + *
> + * A cooling device may be bound to more than one
> + * thermal trips. These multiple bindings are populated as
> + * separate entries in the @__tbp params internal data structure
> + * from the device tree. The unique mask identifies the location
> + * of re-ocurring cooling devices which is further used to
> + * populate the thermal_bind_params external data structre.
> + *
> + * Return: the calculated bitmask (long)
> +	   (set bit means a non-unique cdev at that index)
> + */
> +static unsigned long get_unique_mask(struct __thermal_bind_params *__tbp,
> +			       unsigned int num_tbps)
> +{
> +	unsigned long unique_mask = 0;
> +	int i, j;
> +	/*
> +	 * A bit set in the mask means that the cooling device
> +	 * at that position is not unique
> +	 */
> +	for (i = 0; i < num_tbps; i++)
> +		for (j = i + 1; j < num_tbps; j++)
> +			if (!test_bit(j, &unique_mask) &&
> +			   (__tbp[i].cooling_device == __tbp[j].cooling_device))
> +				set_bit(j, &unique_mask);
> +
> +	return  unique_mask;
> +}
> +
> +static void fill_parsed_params(struct parsed_bind_params *pbs,
> +				struct __thermal_bind_params *__tbp,
> +				int unique)
> +{
> +	pbs->binding_limits[2 * __tbp->trip_id] = __tbp->min;
> +	pbs->binding_limits[2 * __tbp->trip_id + 1] = __tbp->max;
> +
> +	if (unique) {
> +		pbs->weight = __tbp->usage;
> +		pbs->trip_mask = (1  << __tbp->trip_id);
> +		pbs->cdev_node = __tbp->cooling_device;
> +	} else {
> +		if (pbs->weight != __tbp->usage)
> +			pr_err("Multiple weights (%u, %u) sepcified for cdev %s",
> +			       pbs->weight, __tbp->usage, pbs->cdev_node->name);
> +		pbs->trip_mask |= (1 << __tbp->trip_id);
> +	}
> +}
> +
> +/**
> + * of_thermal_parse_bind_params - parse the populated bind params
> + * @__tz: __thermal_zone private device data for of-thermal
> + *
> + * This function creates a reflection of the thermal_bind_params
> + * data structure and condenses the cooling-map populated from the
> + * device tree. This structure is then used when the match
> + * callback of_thermal_match_bind_param is invoked.
> + *
> + * Return: parsed_bind_parameters structure
> + */
> +static struct parsed_bind_params*
> +of_thermal_parse_bind_params(struct __thermal_zone *__tz)
> +{
> +	struct parsed_bind_params *pbs;
> +	struct __thermal_bind_params *__tbp = __tz->tbps;
> +	unsigned long unique_mask;
> +	unsigned long *limits;
> +	unsigned int num_parsed;
> +	int i, j, err;
> +	int bind_count = 0;
> +
> +	unique_mask = get_unique_mask(__tbp, __tz->num_tbps);
> +	num_parsed = __tz->num_tbps - hweight_long(unique_mask);
> +	__tz->num_parsed_tbps = num_parsed;
> +
> +	pbs = kcalloc(num_parsed, sizeof(*pbs), GFP_KERNEL);
> +	if (!pbs) {
> +		err = -ENOMEM;
> +		goto err_exit;
> +	}
> +
> +	/* We have a number of trips in tz */
> +	for (i = 0; i < __tz->num_tbps; i++) {
> +
> +		/*
> +		 * We have two cases here :
> +		 * First occurence of the cooling device
> +		 * In this case we need to allocate a new binding_limits array
> +		 * and assign the limits ( __tbp[i].min and __tbp[i].max )
> +		 * and set the bit in the trip mask
> +		 *
> +		 * Repeated occurence of the cooling device:
> +		 * In this case we need to find the previously allocated
> +		 * binding_param and update the binding_limits and trip_mask.
> +		 */
> +
> +		int unique = !test_bit(i, &unique_mask);
> +
> +		if (unique) {
> +			pbs[bind_count].weight = __tbp[i].usage;
> +			limits = kcalloc(2 * __tz->ntrips, sizeof(*limits),
> +					 GFP_KERNEL);
> +			if (!limits) {
> +				err = -ENOMEM;
> +				goto free_bp;
> +			}
> +			pbs[bind_count].binding_limits = limits;
> +			fill_parsed_params(&pbs[bind_count], &__tbp[i],
> +					     unique);
> +			bind_count++;
> +		} else {
> +			struct device_node *curr;
> +			struct device_node *prev;
> +
> +			for (j = 0; j < bind_count; j++) {
> +				curr = __tbp[i].cooling_device;
> +				prev = pbs[j].cdev_node;
> +				if (curr == prev) {
> +					fill_parsed_params(&pbs[j],
> +							   &__tbp[i],
> +							   unique);
> +					break;
> +				}
> +			}
> +		}
> +
> +	}
> +
> +	return pbs;
> +
> +free_bp:
> +	for (i = 0; i < num_parsed; i++)
> +		kfree(pbs[i].binding_limits);
> +	kfree(pbs);
> +
> +err_exit:
> +	return ERR_PTR(err);
> +
> +}
> +
> +/**
> + * of_thermal_populate_tzp - populate the thermal zone params
> + * @__tz: __thermal_zone private device data for of-thermal
> + *
> + * This function populates the thermal_zone_params and also the
> + * tzp->tbp based on the parsed_bind_params (__tz->pbs)
> + *
> + * Return: struct thermal_zone params
> + */
> +static struct thermal_zone_params*
> +of_thermal_populate_tzp(struct __thermal_zone *__tz)
> +{
> +
> +	struct thermal_zone_params *tzp;
> +	struct parsed_bind_params *pbs = __tz->pbs;
> +	struct thermal_bind_params *tbp;
> +	int err;
> +	int i;
> +
> +	tzp = kzalloc(sizeof(*tzp), GFP_KERNEL);
> +	if (!tzp)
> +		return ERR_PTR(-ENOMEM);
> +
> +	if (!pbs || !__tz->num_parsed_tbps) {
> +		err = -ENODEV;
> +		goto free_tzp;
> +	}
> +
> +	tbp = kcalloc(__tz->num_parsed_tbps, sizeof(*tbp), GFP_KERNEL);
> +	if (!tbp) {
> +		err = -ENOMEM;
> +		goto free_tzp;
> +	 }
> +
> +	for (i = 0; i < __tz->num_parsed_tbps; i++) {
> +		tbp[i].weight = pbs[i].weight;
> +		tbp[i].binding_limits = pbs[i].binding_limits;
> +		tbp[i].trip_mask = pbs[i].trip_mask;
> +		tbp[i].match = of_thermal_match_bind_param;
> +	}
> +
> +	tzp->tbp = tbp;
> +	tzp->num_tbps = __tz->num_parsed_tbps;
> +
> +	/* No hwmon because there might be hwmon drivers registering */
> +	tzp->no_hwmon = true;
> +
> +	return tzp;
> +
> +free_tzp:
> +	kfree(tzp);
> +	return ERR_PTR(err);
> +}
> +
>  /**
>   * It maps 'enum thermal_trip_type' found in include/linux/thermal.h
>   * into the device tree binding of 'trip', property type.
> @@ -800,6 +997,12 @@ thermal_of_build_thermal_zone(struct device_node *np)
>  			goto free_tbps;
>  	}
>  
> +	tz->pbs = of_thermal_parse_bind_params(tz);
> +	if (IS_ERR(tz->pbs)) {
> +		ret = -ENOMEM;
> +		goto free_tbps;
> +	}
> +
>  finish:
>  	of_node_put(child);
>  	tz->mode = THERMAL_DEVICE_DISABLED;
> @@ -829,6 +1032,8 @@ static inline void of_thermal_free_zone(struct __thermal_zone *tz)
>  	for (i = 0; i < tz->num_tbps; i++)
>  		of_node_put(tz->tbps[i].cooling_device);
>  	kfree(tz->tbps);
> +	/* Free the parsed_bind_params */
> +	kfree(tz->pbs);
>  	for (i = 0; i < tz->ntrips; i++)
>  		of_node_put(tz->trips[i].np);
>  	kfree(tz->trips);
> @@ -879,15 +1084,14 @@ int __init of_parse_thermal_zones(void)
>  		if (!ops)
>  			goto exit_free;
>  
> -		tzp = kzalloc(sizeof(*tzp), GFP_KERNEL);
> -		if (!tzp) {
> +
> +		tzp = of_thermal_populate_tzp(tz);
> +
> +		if (IS_ERR(tzp)) {
>  			kfree(ops);
>  			goto exit_free;
>  		}
>  
> -		/* No hwmon because there might be hwmon drivers registering */
> -		tzp->no_hwmon = true;
> -
>  		zone = thermal_zone_device_register(child->name, tz->ntrips,
>  						    0, tz,
>  						    ops, tzp,
> @@ -936,6 +1140,7 @@ void of_thermal_destroy_zones(void)
>  
>  	for_each_child_of_node(np, child) {
>  		struct thermal_zone_device *zone;
> +		int i;
>  
>  		/* Check whether child is enabled or not */
>  		if (!of_device_is_available(child))
> @@ -946,6 +1151,17 @@ void of_thermal_destroy_zones(void)
>  			continue;
>  
>  		thermal_zone_device_unregister(zone);
> +		/*
> +		 * free the binding_limits
> +		 * free the thermal_bind_params
> +		 */
> +		if (zone->tzp && zone->tzp->tbp) {
> +			const struct thermal_zone_params *tzp = zone->tzp;
> +
> +			for (i = 0; i < tzp->num_tbps; i++)
> +				kfree(tzp->tbp[i].binding_limits);
> +			kfree(tzp->tbp);
> +		}
>  		kfree(zone->tzp);
>  		kfree(zone->ops);
>  		of_thermal_free_zone(zone->devdata);
> -- 
> 1.7.9.5
> 
>

Patch
diff mbox

diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c
index e145b66df444..82f7e4b48845 100644
--- a/drivers/thermal/of-thermal.c
+++ b/drivers/thermal/of-thermal.c
@@ -31,6 +31,7 @@ 
 #include <linux/export.h>
 #include <linux/string.h>
 #include <linux/thermal.h>
+#include <linux/bitops.h>
 
 #include "thermal_core.h"
 
@@ -54,6 +55,26 @@  struct __thermal_bind_params {
 };
 
 /**
+ * struct parsed_bind_params - parsed bind parameters from device tree
+ * @cdev_node: a pointer to identify the device tree node of the cdev
+ * @trip_mask: a mask of all the trips the cdev is to be bound to
+ * @weight: the percentage (0 to 100) of cooling conrtibution
+ * @binding_limits: the max and min limits for each trip point
+ *
+ * The struct __thermal_bind_params is a raw representation of the
+ * data read from the device tree. This is then parsed into this
+ * struct such that there is only on param per cooling device
+ * and can be correlated efficiently with thermal_bind_params.
+ */
+
+struct parsed_bind_params {
+	struct device_node *cdev_node;
+	unsigned long trip_mask;
+	unsigned int weight;
+	unsigned long *binding_limits;
+};
+
+/**
  * struct __thermal_zone - internal representation of a thermal zone
  * @mode: current thermal zone device mode (enabled/disabled)
  * @passive_delay: polling interval while passive cooling is activated
@@ -78,6 +99,8 @@  struct __thermal_zone {
 	/* cooling binding data */
 	int num_tbps;
 	struct __thermal_bind_params *tbps;
+	struct parsed_bind_params *pbs;
+	int num_parsed_tbps;
 
 	/* sensor interface */
 	void *sensor_data;
@@ -208,60 +231,6 @@  static int of_thermal_get_trend(struct thermal_zone_device *tz, int trip,
 	return 0;
 }
 
-static int of_thermal_bind(struct thermal_zone_device *thermal,
-			   struct thermal_cooling_device *cdev)
-{
-	struct __thermal_zone *data = thermal->devdata;
-	int i;
-
-	if (!data || IS_ERR(data))
-		return -ENODEV;
-
-	/* find where to bind */
-	for (i = 0; i < data->num_tbps; i++) {
-		struct __thermal_bind_params *tbp = data->tbps + i;
-
-		if (tbp->cooling_device == cdev->np) {
-			int ret;
-
-			ret = thermal_zone_bind_cooling_device(thermal,
-						tbp->trip_id, cdev,
-						tbp->max,
-						tbp->min);
-			if (ret)
-				return ret;
-		}
-	}
-
-	return 0;
-}
-
-static int of_thermal_unbind(struct thermal_zone_device *thermal,
-			     struct thermal_cooling_device *cdev)
-{
-	struct __thermal_zone *data = thermal->devdata;
-	int i;
-
-	if (!data || IS_ERR(data))
-		return -ENODEV;
-
-	/* find where to unbind */
-	for (i = 0; i < data->num_tbps; i++) {
-		struct __thermal_bind_params *tbp = data->tbps + i;
-
-		if (tbp->cooling_device == cdev->np) {
-			int ret;
-
-			ret = thermal_zone_unbind_cooling_device(thermal,
-						tbp->trip_id, cdev);
-			if (ret)
-				return ret;
-		}
-	}
-
-	return 0;
-}
-
 static int of_thermal_get_mode(struct thermal_zone_device *tz,
 			       enum thermal_device_mode *mode)
 {
@@ -384,9 +353,6 @@  static struct thermal_zone_device_ops of_thermal_ops = {
 	.get_trip_hyst = of_thermal_get_trip_hyst,
 	.set_trip_hyst = of_thermal_set_trip_hyst,
 	.get_crit_temp = of_thermal_get_crit_temp,
-
-	.bind = of_thermal_bind,
-	.unbind = of_thermal_unbind,
 };
 
 /***   sensor API   ***/
@@ -621,6 +587,237 @@  static int thermal_of_populate_bind_params(struct device_node *np,
 	return ret;
 }
 
+int of_thermal_match_bind_param(struct thermal_zone_device *tz,
+				struct thermal_cooling_device *cdev,
+				int index)
+{
+	struct __thermal_zone *__tz;
+	struct parsed_bind_params *pbs;
+	struct thermal_bind_params *tbp;
+	int i;
+
+	if (!tz->devdata)
+		return 1;
+
+	__tz = (struct __thermal_zone *)tz->devdata;
+
+	if (!__tz->pbs || !__tz->num_parsed_tbps)
+		return 1;
+
+	pbs = __tz->pbs;
+	tbp = tz->tzp->tbp;
+
+	for (i = 0; i < __tz->num_parsed_tbps; i++) {
+		if (pbs[i].cdev_node == cdev->np) {
+			if (tbp[index].trip_mask != pbs[i].trip_mask)
+				return 1;
+			if (tbp[index].binding_limits != pbs[i].binding_limits)
+				return 1;
+			if (tbp[index].weight != pbs[i].weight)
+				return 1;
+			return 0;
+		}
+	}
+	return 1;
+}
+
+/**
+ * get_unique_mask - return a mask indicating repeated cdevs
+ * @__tbp: __thermal_bind_params internal data structre
+ * @num_tbps: total number of cdev<->trip bindings
+ *
+ * A cooling device may be bound to more than one
+ * thermal trips. These multiple bindings are populated as
+ * separate entries in the @__tbp params internal data structure
+ * from the device tree. The unique mask identifies the location
+ * of re-ocurring cooling devices which is further used to
+ * populate the thermal_bind_params external data structre.
+ *
+ * Return: the calculated bitmask (long)
+	   (set bit means a non-unique cdev at that index)
+ */
+static unsigned long get_unique_mask(struct __thermal_bind_params *__tbp,
+			       unsigned int num_tbps)
+{
+	unsigned long unique_mask = 0;
+	int i, j;
+	/*
+	 * A bit set in the mask means that the cooling device
+	 * at that position is not unique
+	 */
+	for (i = 0; i < num_tbps; i++)
+		for (j = i + 1; j < num_tbps; j++)
+			if (!test_bit(j, &unique_mask) &&
+			   (__tbp[i].cooling_device == __tbp[j].cooling_device))
+				set_bit(j, &unique_mask);
+
+	return  unique_mask;
+}
+
+static void fill_parsed_params(struct parsed_bind_params *pbs,
+				struct __thermal_bind_params *__tbp,
+				int unique)
+{
+	pbs->binding_limits[2 * __tbp->trip_id] = __tbp->min;
+	pbs->binding_limits[2 * __tbp->trip_id + 1] = __tbp->max;
+
+	if (unique) {
+		pbs->weight = __tbp->usage;
+		pbs->trip_mask = (1  << __tbp->trip_id);
+		pbs->cdev_node = __tbp->cooling_device;
+	} else {
+		if (pbs->weight != __tbp->usage)
+			pr_err("Multiple weights (%u, %u) sepcified for cdev %s",
+			       pbs->weight, __tbp->usage, pbs->cdev_node->name);
+		pbs->trip_mask |= (1 << __tbp->trip_id);
+	}
+}
+
+/**
+ * of_thermal_parse_bind_params - parse the populated bind params
+ * @__tz: __thermal_zone private device data for of-thermal
+ *
+ * This function creates a reflection of the thermal_bind_params
+ * data structure and condenses the cooling-map populated from the
+ * device tree. This structure is then used when the match
+ * callback of_thermal_match_bind_param is invoked.
+ *
+ * Return: parsed_bind_parameters structure
+ */
+static struct parsed_bind_params*
+of_thermal_parse_bind_params(struct __thermal_zone *__tz)
+{
+	struct parsed_bind_params *pbs;
+	struct __thermal_bind_params *__tbp = __tz->tbps;
+	unsigned long unique_mask;
+	unsigned long *limits;
+	unsigned int num_parsed;
+	int i, j, err;
+	int bind_count = 0;
+
+	unique_mask = get_unique_mask(__tbp, __tz->num_tbps);
+	num_parsed = __tz->num_tbps - hweight_long(unique_mask);
+	__tz->num_parsed_tbps = num_parsed;
+
+	pbs = kcalloc(num_parsed, sizeof(*pbs), GFP_KERNEL);
+	if (!pbs) {
+		err = -ENOMEM;
+		goto err_exit;
+	}
+
+	/* We have a number of trips in tz */
+	for (i = 0; i < __tz->num_tbps; i++) {
+
+		/*
+		 * We have two cases here :
+		 * First occurence of the cooling device
+		 * In this case we need to allocate a new binding_limits array
+		 * and assign the limits ( __tbp[i].min and __tbp[i].max )
+		 * and set the bit in the trip mask
+		 *
+		 * Repeated occurence of the cooling device:
+		 * In this case we need to find the previously allocated
+		 * binding_param and update the binding_limits and trip_mask.
+		 */
+
+		int unique = !test_bit(i, &unique_mask);
+
+		if (unique) {
+			pbs[bind_count].weight = __tbp[i].usage;
+			limits = kcalloc(2 * __tz->ntrips, sizeof(*limits),
+					 GFP_KERNEL);
+			if (!limits) {
+				err = -ENOMEM;
+				goto free_bp;
+			}
+			pbs[bind_count].binding_limits = limits;
+			fill_parsed_params(&pbs[bind_count], &__tbp[i],
+					     unique);
+			bind_count++;
+		} else {
+			struct device_node *curr;
+			struct device_node *prev;
+
+			for (j = 0; j < bind_count; j++) {
+				curr = __tbp[i].cooling_device;
+				prev = pbs[j].cdev_node;
+				if (curr == prev) {
+					fill_parsed_params(&pbs[j],
+							   &__tbp[i],
+							   unique);
+					break;
+				}
+			}
+		}
+
+	}
+
+	return pbs;
+
+free_bp:
+	for (i = 0; i < num_parsed; i++)
+		kfree(pbs[i].binding_limits);
+	kfree(pbs);
+
+err_exit:
+	return ERR_PTR(err);
+
+}
+
+/**
+ * of_thermal_populate_tzp - populate the thermal zone params
+ * @__tz: __thermal_zone private device data for of-thermal
+ *
+ * This function populates the thermal_zone_params and also the
+ * tzp->tbp based on the parsed_bind_params (__tz->pbs)
+ *
+ * Return: struct thermal_zone params
+ */
+static struct thermal_zone_params*
+of_thermal_populate_tzp(struct __thermal_zone *__tz)
+{
+
+	struct thermal_zone_params *tzp;
+	struct parsed_bind_params *pbs = __tz->pbs;
+	struct thermal_bind_params *tbp;
+	int err;
+	int i;
+
+	tzp = kzalloc(sizeof(*tzp), GFP_KERNEL);
+	if (!tzp)
+		return ERR_PTR(-ENOMEM);
+
+	if (!pbs || !__tz->num_parsed_tbps) {
+		err = -ENODEV;
+		goto free_tzp;
+	}
+
+	tbp = kcalloc(__tz->num_parsed_tbps, sizeof(*tbp), GFP_KERNEL);
+	if (!tbp) {
+		err = -ENOMEM;
+		goto free_tzp;
+	 }
+
+	for (i = 0; i < __tz->num_parsed_tbps; i++) {
+		tbp[i].weight = pbs[i].weight;
+		tbp[i].binding_limits = pbs[i].binding_limits;
+		tbp[i].trip_mask = pbs[i].trip_mask;
+		tbp[i].match = of_thermal_match_bind_param;
+	}
+
+	tzp->tbp = tbp;
+	tzp->num_tbps = __tz->num_parsed_tbps;
+
+	/* No hwmon because there might be hwmon drivers registering */
+	tzp->no_hwmon = true;
+
+	return tzp;
+
+free_tzp:
+	kfree(tzp);
+	return ERR_PTR(err);
+}
+
 /**
  * It maps 'enum thermal_trip_type' found in include/linux/thermal.h
  * into the device tree binding of 'trip', property type.
@@ -800,6 +997,12 @@  thermal_of_build_thermal_zone(struct device_node *np)
 			goto free_tbps;
 	}
 
+	tz->pbs = of_thermal_parse_bind_params(tz);
+	if (IS_ERR(tz->pbs)) {
+		ret = -ENOMEM;
+		goto free_tbps;
+	}
+
 finish:
 	of_node_put(child);
 	tz->mode = THERMAL_DEVICE_DISABLED;
@@ -829,6 +1032,8 @@  static inline void of_thermal_free_zone(struct __thermal_zone *tz)
 	for (i = 0; i < tz->num_tbps; i++)
 		of_node_put(tz->tbps[i].cooling_device);
 	kfree(tz->tbps);
+	/* Free the parsed_bind_params */
+	kfree(tz->pbs);
 	for (i = 0; i < tz->ntrips; i++)
 		of_node_put(tz->trips[i].np);
 	kfree(tz->trips);
@@ -879,15 +1084,14 @@  int __init of_parse_thermal_zones(void)
 		if (!ops)
 			goto exit_free;
 
-		tzp = kzalloc(sizeof(*tzp), GFP_KERNEL);
-		if (!tzp) {
+
+		tzp = of_thermal_populate_tzp(tz);
+
+		if (IS_ERR(tzp)) {
 			kfree(ops);
 			goto exit_free;
 		}
 
-		/* No hwmon because there might be hwmon drivers registering */
-		tzp->no_hwmon = true;
-
 		zone = thermal_zone_device_register(child->name, tz->ntrips,
 						    0, tz,
 						    ops, tzp,
@@ -936,6 +1140,7 @@  void of_thermal_destroy_zones(void)
 
 	for_each_child_of_node(np, child) {
 		struct thermal_zone_device *zone;
+		int i;
 
 		/* Check whether child is enabled or not */
 		if (!of_device_is_available(child))
@@ -946,6 +1151,17 @@  void of_thermal_destroy_zones(void)
 			continue;
 
 		thermal_zone_device_unregister(zone);
+		/*
+		 * free the binding_limits
+		 * free the thermal_bind_params
+		 */
+		if (zone->tzp && zone->tzp->tbp) {
+			const struct thermal_zone_params *tzp = zone->tzp;
+
+			for (i = 0; i < tzp->num_tbps; i++)
+				kfree(tzp->tbp[i].binding_limits);
+			kfree(tzp->tbp);
+		}
 		kfree(zone->tzp);
 		kfree(zone->ops);
 		of_thermal_free_zone(zone->devdata);