Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8a12149c authored by Ram Chandrasekar's avatar Ram Chandrasekar Committed by Manaf Meethalavalappu Pallikunhi
Browse files

drivers: thermal: snapshot of thermal core from msm-4.14



Add a snapshot of thermal core framework changes from msm-4.14.
This is snapshot as of 'commit <0a56f56a528f> ("drivers: thermal:
virtual-sensor: Add new virtual sensors for SDMMAGPIE")'.

Change-Id: Ib0c7a15fadc095fe97c1d7efb4ea7527384c2782
Signed-off-by: default avatarRam Chandrasekar <rkumbako@codeaurora.org>
parent aabf400f
Loading
Loading
Loading
Loading
+10 −0
Original line number Original line Diff line number Diff line
@@ -142,6 +142,16 @@ config THERMAL_GOV_USER_SPACE
	help
	help
	  Enable this to let the user space manage the platform thermals.
	  Enable this to let the user space manage the platform thermals.


config THERMAL_GOV_LOW_LIMITS
	bool "Low limits mitigation governor"
	help
	  Enable this to manage platform limits using low limits
	  governor.

	  Enable this governor to monitor and trigger floor mitigation.
	  This governor will monitor the limits going below a
	  trip threshold to trigger a floor mitigation.

config THERMAL_GOV_POWER_ALLOCATOR
config THERMAL_GOV_POWER_ALLOCATOR
	bool "Power allocator thermal governor"
	bool "Power allocator thermal governor"
	help
	help
+2 −1
Original line number Original line Diff line number Diff line
@@ -16,6 +16,7 @@ thermal_sys-$(CONFIG_THERMAL_GOV_FAIR_SHARE) += fair_share.o
thermal_sys-$(CONFIG_THERMAL_GOV_BANG_BANG)	+= gov_bang_bang.o
thermal_sys-$(CONFIG_THERMAL_GOV_BANG_BANG)	+= gov_bang_bang.o
thermal_sys-$(CONFIG_THERMAL_GOV_STEP_WISE)	+= step_wise.o
thermal_sys-$(CONFIG_THERMAL_GOV_STEP_WISE)	+= step_wise.o
thermal_sys-$(CONFIG_THERMAL_GOV_USER_SPACE)	+= user_space.o
thermal_sys-$(CONFIG_THERMAL_GOV_USER_SPACE)	+= user_space.o
thermal_sys-$(CONFIG_THERMAL_GOV_LOW_LIMITS) += gov_low_limits.o
thermal_sys-$(CONFIG_THERMAL_GOV_POWER_ALLOCATOR)	+= power_allocator.o
thermal_sys-$(CONFIG_THERMAL_GOV_POWER_ALLOCATOR)	+= power_allocator.o


# cpufreq cooling
# cpufreq cooling
@@ -54,7 +55,7 @@ obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal/
obj-$(CONFIG_INTEL_BXT_PMIC_THERMAL) += intel_bxt_pmic_thermal.o
obj-$(CONFIG_INTEL_BXT_PMIC_THERMAL) += intel_bxt_pmic_thermal.o
obj-$(CONFIG_INTEL_PCH_THERMAL)	+= intel_pch_thermal.o
obj-$(CONFIG_INTEL_PCH_THERMAL)	+= intel_pch_thermal.o
obj-$(CONFIG_ST_THERMAL)	+= st/
obj-$(CONFIG_ST_THERMAL)	+= st/
obj-$(CONFIG_QCOM_TSENS)	+= qcom/
obj-$(CONFIG_ARCH_QCOM)		+= qcom/
obj-y				+= tegra/
obj-y				+= tegra/
obj-$(CONFIG_HISI_THERMAL)     += hisi_thermal.o
obj-$(CONFIG_HISI_THERMAL)     += hisi_thermal.o
obj-$(CONFIG_MTK_THERMAL)	+= mtk_thermal.o
obj-$(CONFIG_MTK_THERMAL)	+= mtk_thermal.o
+130 −0
Original line number Original line Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2012 Intel Corp
 * Copyright (C) 2012 Durgadoss R <durgadoss.r@intel.com>
 * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
 */

#include <linux/thermal.h>
#include <trace/events/thermal.h>

#include "thermal_core.h"

static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
{
	int trip_temp, trip_hyst;
	enum thermal_trip_type trip_type;
	struct thermal_instance *instance;
	bool throttle;
	int old_target;

	tz->ops->get_trip_temp(tz, trip, &trip_temp);
	tz->ops->get_trip_type(tz, trip, &trip_type);
	if (tz->ops->get_trip_hyst) {
		tz->ops->get_trip_hyst(tz, trip, &trip_hyst);
		trip_hyst = trip_temp + trip_hyst;
	} else {
		trip_hyst = trip_temp;
	}

	mutex_lock(&tz->lock);

	list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
		if (instance->trip != trip)
			continue;

		if ((tz->temperature <= trip_temp) ||
			(instance->target != THERMAL_NO_TARGET
				&& tz->temperature < trip_hyst))
			throttle = true;
		else
			throttle = false;

		dev_dbg(&tz->device,
			"Trip%d[type=%d,temp=%d,hyst=%d],throttle=%d\n",
			trip, trip_type, trip_temp, trip_hyst, throttle);

		old_target = instance->target;
		instance->target = (throttle) ? instance->upper
					: THERMAL_NO_TARGET;
		dev_dbg(&instance->cdev->device, "old_target=%d, target=%d\n",
					old_target, (int)instance->target);

		if (instance->initialized && old_target == instance->target)
			continue;

		if (!instance->initialized) {
			if (instance->target != THERMAL_NO_TARGET) {
				trace_thermal_zone_trip(tz, trip, trip_type,
							true);
				tz->passive += 1;
			}
		} else {
			if (old_target == THERMAL_NO_TARGET &&
				instance->target != THERMAL_NO_TARGET) {
				trace_thermal_zone_trip(tz, trip, trip_type,
							true);
				tz->passive += 1;
			} else if (old_target != THERMAL_NO_TARGET &&
				instance->target == THERMAL_NO_TARGET) {
				trace_thermal_zone_trip(tz, trip, trip_type,
							false);
				tz->passive -= 1;
			}
		}

		instance->initialized = true;
		instance->cdev->updated = false; /* cdev needs update */
	}

	mutex_unlock(&tz->lock);
}

/**
 * low_limits_throttle - throttles devices associated with the given zone
 * @tz - thermal_zone_device
 * @trip - the trip point
 *
 * Throttling Logic: If the sensor reading goes below a trip point, the
 * pre-defined mitigation will be applied for the cooling device.
 * If the sensor reading goes above the trip hysteresis, the
 * mitigation will be removed.
 */
static int low_limits_throttle(struct thermal_zone_device *tz, int trip)
{
	struct thermal_instance *instance;

	thermal_zone_trip_update(tz, trip);

	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;
}

static struct thermal_governor thermal_gov_low_limits_floor = {
	.name		= "low_limits_floor",
	.throttle	= low_limits_throttle,
	.min_state_throttle = 1,
};

static struct thermal_governor thermal_gov_low_limits_cap = {
	.name		= "low_limits_cap",
	.throttle	= low_limits_throttle,
};

int thermal_gov_low_limits_register(void)
{
	thermal_register_governor(&thermal_gov_low_limits_cap);
	return thermal_register_governor(&thermal_gov_low_limits_floor);
}

void thermal_gov_low_limits_unregister(void)
{
	thermal_unregister_governor(&thermal_gov_low_limits_cap);
	thermal_unregister_governor(&thermal_gov_low_limits_floor);
}
+434 −42
Original line number Original line Diff line number Diff line
@@ -13,6 +13,10 @@
#include <linux/err.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/export.h>
#include <linux/string.h>
#include <linux/string.h>
#include <linux/list.h>

#define CREATE_TRACE_POINTS
#include <trace/events/thermal_virtual.h>


#include "thermal_core.h"
#include "thermal_core.h"


@@ -35,6 +39,23 @@ struct __thermal_bind_params {
	unsigned long max;
	unsigned long max;
};
};


/**
 * struct __sensor_param - Holds individual sensor data
 * @sensor_data: sensor driver private data passed as input argument
 * @ops: sensor driver ops
 * @trip_high: last trip high value programmed in the sensor driver
 * @trip_low: last trip low value programmed in the sensor driver
 * @lock: mutex lock acquired before updating the trip temperatures
 * @first_tz: list head pointing the first thermal zone
 */
struct __sensor_param {
	void *sensor_data;
	const struct thermal_zone_of_device_ops *ops;
	int trip_high, trip_low;
	struct mutex lock;
	struct list_head first_tz;
};

/**
/**
 * struct __thermal_zone - internal representation of a thermal zone
 * struct __thermal_zone - internal representation of a thermal zone
 * @mode: current thermal zone device mode (enabled/disabled)
 * @mode: current thermal zone device mode (enabled/disabled)
@@ -42,12 +63,14 @@ struct __thermal_bind_params {
 * @polling_delay: zone polling interval
 * @polling_delay: zone polling interval
 * @slope: slope of the temperature adjustment curve
 * @slope: slope of the temperature adjustment curve
 * @offset: offset of the temperature adjustment curve
 * @offset: offset of the temperature adjustment curve
 * @default_disable: Keep the thermal zone disabled by default
 * @tzd: thermal zone device pointer for this sensor
 * @ntrips: number of trip points
 * @ntrips: number of trip points
 * @trips: an array of trip points (0..ntrips - 1)
 * @trips: an array of trip points (0..ntrips - 1)
 * @num_tbps: number of thermal bind params
 * @num_tbps: number of thermal bind params
 * @tbps: an array of thermal bind params (0..num_tbps - 1)
 * @tbps: an array of thermal bind params (0..num_tbps - 1)
 * @sensor_data: sensor private data used while reading temperature and trend
 * @list: sibling thermal zone pointer
 * @ops: set of callbacks to handle the thermal zone based on DT
 * @senps: sensor related parameters
 */
 */


struct __thermal_zone {
struct __thermal_zone {
@@ -56,6 +79,8 @@ struct __thermal_zone {
	int polling_delay;
	int polling_delay;
	int slope;
	int slope;
	int offset;
	int offset;
	struct thermal_zone_device *tzd;
	bool default_disable;


	/* trip data */
	/* trip data */
	int ntrips;
	int ntrips;
@@ -65,35 +90,124 @@ struct __thermal_zone {
	int num_tbps;
	int num_tbps;
	struct __thermal_bind_params *tbps;
	struct __thermal_bind_params *tbps;


	struct list_head list;
	/* sensor interface */
	/* sensor interface */
	void *sensor_data;
	struct __sensor_param *senps;
	const struct thermal_zone_of_device_ops *ops;
};
};


/**
 * struct virtual_sensor - internal representation of a virtual thermal zone
 * @num_sensors - number of sensors this virtual sensor will reference to
 *		  estimate temperature
 * @tz - Array of thermal zones of the sensors this virtual sensor will use
 *	 to estimate temperature
 * @virt_tz - Virtual thermal zone pointer
 * @logic - aggregation logic to be used to estimate the temperature
 * @last_reading - last estimated temperature
 * @coefficients - array of coefficients to be used for weighted aggregation
 *		       logic
 * @avg_offset - offset value to be used for the weighted aggregation logic
 * @avg_denominator - denominator value to be used for the weighted aggregation
 *			logic
 */
struct virtual_sensor {
	int                        num_sensors;
	struct thermal_zone_device *tz[THERMAL_MAX_VIRT_SENSORS];
	struct thermal_zone_device *virt_tz;
	enum aggregation_logic     logic;
	int                        last_reading;
	int                        coefficients[THERMAL_MAX_VIRT_SENSORS];
	int                        avg_offset;
	int                        avg_denominator;
};

static int of_thermal_aggregate_trip_types(struct thermal_zone_device *tz,
		unsigned int trip_type_mask, int *low, int *high);

/***   DT thermal zone device callbacks   ***/
/***   DT thermal zone device callbacks   ***/


static int virt_sensor_read_temp(void *data, int *val)
{
	struct virtual_sensor *sens = data;
	int idx, temp = 0, ret = 0;

	for (idx = 0; idx < sens->num_sensors; idx++) {
		int sens_temp = 0;

		ret = thermal_zone_get_temp(sens->tz[idx], &sens_temp);
		if (ret) {
			pr_err("virt zone: sensor[%s] read error:%d\n",
				sens->tz[idx]->type, ret);
			return ret;
		}
		switch (sens->logic) {
		case VIRT_WEIGHTED_AVG:
			temp += sens_temp * sens->coefficients[idx];
			if (idx == (sens->num_sensors - 1))
				temp = (temp + sens->avg_offset)
					/ sens->avg_denominator;
			break;
		case VIRT_MAXIMUM:
			if (idx == 0)
				temp = INT_MIN;
			if (sens_temp > temp)
				temp = sens_temp;
			break;
		case VIRT_MINIMUM:
			if (idx == 0)
				temp = INT_MAX;
			if (sens_temp < temp)
				temp = sens_temp;
			break;
		default:
			break;
		}
		trace_virtual_temperature(sens->virt_tz, sens->tz[idx],
					sens_temp, temp);
	}

	sens->last_reading = *val = temp;

	return 0;
}

static int of_thermal_get_temp(struct thermal_zone_device *tz,
static int of_thermal_get_temp(struct thermal_zone_device *tz,
			       int *temp)
			       int *temp)
{
{
	struct __thermal_zone *data = tz->devdata;
	struct __thermal_zone *data = tz->devdata;


	if (!data->ops->get_temp)
	if (!data->senps || !data->senps->ops->get_temp)
		return -EINVAL;
		return -EINVAL;
	if (data->mode == THERMAL_DEVICE_DISABLED) {
		*temp = tz->tzp->tracks_low ?
				THERMAL_TEMP_INVALID_LOW :
				THERMAL_TEMP_INVALID;
		return 0;
	}


	return data->ops->get_temp(data->sensor_data, temp);
	return data->senps->ops->get_temp(data->senps->sensor_data, temp);
}
}


static int of_thermal_set_trips(struct thermal_zone_device *tz,
static int of_thermal_set_trips(struct thermal_zone_device *tz,
				int low, int high)
				int inp_low, int inp_high)
{
{
	struct __thermal_zone *data = tz->devdata;
	struct __thermal_zone *data = tz->devdata;
	int high = INT_MAX, low = INT_MIN, ret = 0;


	if (!data->ops || !data->ops->set_trips)
	if (!data->senps || !data->senps->ops->set_trips)
		return -EINVAL;
		return -EINVAL;


	return data->ops->set_trips(data->sensor_data, low, high);
	mutex_lock(&data->senps->lock);
}
	of_thermal_aggregate_trip_types(tz, GENMASK(THERMAL_TRIP_CRITICAL, 0),
					&low, &high);
	data->senps->trip_low = low;
	data->senps->trip_high = high;
	ret = data->senps->ops->set_trips(data->senps->sensor_data,
					  low, high);


	mutex_unlock(&data->senps->lock);
	return ret;
}
/**
/**
 * of_thermal_get_ntrips - function to export number of available trip
 * of_thermal_get_ntrips - function to export number of available trip
 *			   points.
 *			   points.
@@ -174,7 +288,10 @@ static int of_thermal_set_emul_temp(struct thermal_zone_device *tz,
{
{
	struct __thermal_zone *data = tz->devdata;
	struct __thermal_zone *data = tz->devdata;


	return data->ops->set_emul_temp(data->sensor_data, temp);
	if (!data->senps || !data->senps->ops->set_emul_temp)
		return -EINVAL;

	return data->senps->ops->set_emul_temp(data->senps->sensor_data, temp);
}
}


static int of_thermal_get_trend(struct thermal_zone_device *tz, int trip,
static int of_thermal_get_trend(struct thermal_zone_device *tz, int trip,
@@ -182,10 +299,11 @@ static int of_thermal_get_trend(struct thermal_zone_device *tz, int trip,
{
{
	struct __thermal_zone *data = tz->devdata;
	struct __thermal_zone *data = tz->devdata;


	if (!data->ops->get_trend)
	if (!data->senps || !data->senps->ops->get_trend)
		return -EINVAL;
		return -EINVAL;


	return data->ops->get_trend(data->sensor_data, trip, trend);
	return data->senps->ops->get_trend(data->senps->sensor_data,
					   trip, trend);
}
}


static int of_thermal_bind(struct thermal_zone_device *thermal,
static int of_thermal_bind(struct thermal_zone_device *thermal,
@@ -297,7 +415,16 @@ static int of_thermal_get_trip_temp(struct thermal_zone_device *tz, int trip,
	if (trip >= data->ntrips || trip < 0)
	if (trip >= data->ntrips || trip < 0)
		return -EDOM;
		return -EDOM;


	if (data->senps && data->senps->ops->get_trip_temp) {
		int ret;

		ret = data->senps->ops->get_trip_temp(data->senps->sensor_data,
						      trip, temp);
		if (ret)
			return ret;
	} else {
		*temp = data->trips[trip].temperature;
		*temp = data->trips[trip].temperature;
	}


	return 0;
	return 0;
}
}
@@ -310,10 +437,11 @@ static int of_thermal_set_trip_temp(struct thermal_zone_device *tz, int trip,
	if (trip >= data->ntrips || trip < 0)
	if (trip >= data->ntrips || trip < 0)
		return -EDOM;
		return -EDOM;


	if (data->ops->set_trip_temp) {
	if (data->senps && data->senps->ops->set_trip_temp) {
		int ret;
		int ret;


		ret = data->ops->set_trip_temp(data->sensor_data, trip, temp);
		ret = data->senps->ops->set_trip_temp(data->senps->sensor_data,
						      trip, temp);
		if (ret)
		if (ret)
			return ret;
			return ret;
	}
	}
@@ -366,6 +494,93 @@ static int of_thermal_get_crit_temp(struct thermal_zone_device *tz,
	return -EINVAL;
	return -EINVAL;
}
}


static int of_thermal_aggregate_trip_types(struct thermal_zone_device *tz,
		unsigned int trip_type_mask, int *low, int *high)
{
	int min = INT_MIN;
	int max = INT_MAX;
	int tt, th, trip;
	int temp = tz->temperature;
	struct thermal_zone_device *zone = NULL;
	struct __thermal_zone *data = tz->devdata;
	struct list_head *head;
	enum thermal_trip_type type = 0;

	head = &data->senps->first_tz;
	list_for_each_entry(data, head, list) {
		zone = data->tzd;
		if (data->mode == THERMAL_DEVICE_DISABLED)
			continue;
		for (trip = 0; trip < data->ntrips; trip++) {
			of_thermal_get_trip_type(zone, trip, &type);
			if (!(BIT(type) & trip_type_mask))
				continue;

			if (!zone->tzp->tracks_low) {
				tt = data->trips[trip].temperature;
				if (tt > temp && tt < max)
					max = tt;
				th = tt - data->trips[trip].hysteresis;
				if (th < temp && th > min)
					min = th;
			} else {
				tt = data->trips[trip].temperature;
				if (tt < temp && tt > min)
					min = tt;
				th = tt + data->trips[trip].hysteresis;
				if (th > temp && th < max)
					max = th;
			}
		}
	}

	*high = max;
	*low = min;

	return 0;
}

/*
 * of_thermal_aggregate_trip - aggregate trip temperatures across sibling
 *				thermal zones.
 * @tz: pointer to the primary thermal zone.
 * @type: the thermal trip type to be aggregated upon
 * @low: the low trip threshold which the most lesser than the @temp
 * @high: the high trip threshold which is the least greater than the @temp
 */
int of_thermal_aggregate_trip(struct thermal_zone_device *tz,
				enum thermal_trip_type type,
				int *low, int *high)
{
	if (type <= THERMAL_TRIP_CRITICAL)
		return of_thermal_aggregate_trip_types(tz, BIT(type), low,
						       high);

	return -EINVAL;
}
EXPORT_SYMBOL(of_thermal_aggregate_trip);

/*
 * of_thermal_handle_trip - Handle thermal trip from sensors
 *
 * @tz: pointer to the primary thermal zone.
 */
void of_thermal_handle_trip(struct thermal_zone_device *tz)
{
	struct thermal_zone_device *zone;
	struct __thermal_zone *data = tz->devdata;
	struct list_head *head;

	head = &data->senps->first_tz;
	list_for_each_entry(data, head, list) {
		zone = data->tzd;
		if (data->mode == THERMAL_DEVICE_DISABLED)
			continue;
		thermal_zone_device_update(zone, THERMAL_EVENT_UNSPECIFIED);
	}
}
EXPORT_SYMBOL(of_thermal_handle_trip);

static struct thermal_zone_device_ops of_thermal_ops = {
static struct thermal_zone_device_ops of_thermal_ops = {
	.get_mode = of_thermal_get_mode,
	.get_mode = of_thermal_get_mode,
	.set_mode = of_thermal_set_mode,
	.set_mode = of_thermal_set_mode,
@@ -381,12 +596,16 @@ static struct thermal_zone_device_ops of_thermal_ops = {
	.unbind = of_thermal_unbind,
	.unbind = of_thermal_unbind,
};
};


static struct thermal_zone_of_device_ops of_virt_ops = {
	.get_temp = virt_sensor_read_temp,
};

/***   sensor API   ***/
/***   sensor API   ***/


static struct thermal_zone_device *
static struct thermal_zone_device *
thermal_zone_of_add_sensor(struct device_node *zone,
thermal_zone_of_add_sensor(struct device_node *zone,
			   struct device_node *sensor, void *data,
			   struct device_node *sensor,
			   const struct thermal_zone_of_device_ops *ops)
			   struct __sensor_param *sens_param)
{
{
	struct thermal_zone_device *tzd;
	struct thermal_zone_device *tzd;
	struct __thermal_zone *tz;
	struct __thermal_zone *tz;
@@ -397,12 +616,11 @@ thermal_zone_of_add_sensor(struct device_node *zone,


	tz = tzd->devdata;
	tz = tzd->devdata;


	if (!ops)
	if (!sens_param->ops)
		return ERR_PTR(-EINVAL);
		return ERR_PTR(-EINVAL);


	mutex_lock(&tzd->lock);
	mutex_lock(&tzd->lock);
	tz->ops = ops;
	tz->senps = sens_param;
	tz->sensor_data = data;


	tzd->ops->get_temp = of_thermal_get_temp;
	tzd->ops->get_temp = of_thermal_get_temp;
	tzd->ops->get_trend = of_thermal_get_trend;
	tzd->ops->get_trend = of_thermal_get_trend;
@@ -411,12 +629,13 @@ thermal_zone_of_add_sensor(struct device_node *zone,
	 * The thermal zone core will calculate the window if they have set the
	 * The thermal zone core will calculate the window if they have set the
	 * optional set_trips pointer.
	 * optional set_trips pointer.
	 */
	 */
	if (ops->set_trips)
	if (sens_param->ops->set_trips)
		tzd->ops->set_trips = of_thermal_set_trips;
		tzd->ops->set_trips = of_thermal_set_trips;


	if (ops->set_emul_temp)
	if (sens_param->ops->set_emul_temp)
		tzd->ops->set_emul_temp = of_thermal_set_emul_temp;
		tzd->ops->set_emul_temp = of_thermal_set_emul_temp;


	list_add_tail(&tz->list, &sens_param->first_tz);
	mutex_unlock(&tzd->lock);
	mutex_unlock(&tzd->lock);


	return tzd;
	return tzd;
@@ -451,7 +670,9 @@ thermal_zone_of_add_sensor(struct device_node *zone,
 * that refer to it.
 * that refer to it.
 *
 *
 * Return: On success returns a valid struct thermal_zone_device,
 * Return: On success returns a valid struct thermal_zone_device,
 * otherwise, it returns a corresponding ERR_PTR(). Caller must
 * otherwise, it returns a corresponding ERR_PTR(). Incase there are multiple
 * thermal zones referencing the same sensor, the return value will be
 * thermal_zone_device pointer of the first thermal zone. Caller must
 * check the return value with help of IS_ERR() helper.
 * check the return value with help of IS_ERR() helper.
 */
 */
struct thermal_zone_device *
struct thermal_zone_device *
@@ -460,6 +681,8 @@ thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data,
{
{
	struct device_node *np, *child, *sensor_np;
	struct device_node *np, *child, *sensor_np;
	struct thermal_zone_device *tzd = ERR_PTR(-ENODEV);
	struct thermal_zone_device *tzd = ERR_PTR(-ENODEV);
	struct thermal_zone_device *first_tzd = NULL;
	struct __sensor_param *sens_param = NULL;


	np = of_find_node_by_name(NULL, "thermal-zones");
	np = of_find_node_by_name(NULL, "thermal-zones");
	if (!np)
	if (!np)
@@ -470,11 +693,23 @@ thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data,
		return ERR_PTR(-EINVAL);
		return ERR_PTR(-EINVAL);
	}
	}


	sens_param = kzalloc(sizeof(*sens_param), GFP_KERNEL);
	if (!sens_param) {
		of_node_put(np);
		return ERR_PTR(-ENOMEM);
	}
	sens_param->sensor_data = data;
	sens_param->ops = ops;
	INIT_LIST_HEAD(&sens_param->first_tz);
	sens_param->trip_high = INT_MAX;
	sens_param->trip_low = INT_MIN;
	mutex_init(&sens_param->lock);
	sensor_np = of_node_get(dev->of_node);
	sensor_np = of_node_get(dev->of_node);


	for_each_available_child_of_node(np, child) {
	for_each_available_child_of_node(np, child) {
		struct of_phandle_args sensor_specs;
		struct of_phandle_args sensor_specs;
		int ret, id;
		int ret, id;
		struct __thermal_zone *tz;


		/* For now, thermal framework supports only 1 sensor per zone */
		/* For now, thermal framework supports only 1 sensor per zone */
		ret = of_parse_phandle_with_args(child, "thermal-sensors",
		ret = of_parse_phandle_with_args(child, "thermal-sensors",
@@ -494,21 +729,26 @@ thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data,


		if (sensor_specs.np == sensor_np && id == sensor_id) {
		if (sensor_specs.np == sensor_np && id == sensor_id) {
			tzd = thermal_zone_of_add_sensor(child, sensor_np,
			tzd = thermal_zone_of_add_sensor(child, sensor_np,
							 data, ops);
							 sens_param);
			if (!IS_ERR(tzd))
			if (!IS_ERR(tzd)) {
				tzd->ops->set_mode(tzd, THERMAL_DEVICE_ENABLED);
				if (!first_tzd)

					first_tzd = tzd;
			of_node_put(sensor_specs.np);
				tz = tzd->devdata;
			of_node_put(child);
				if (!tz->default_disable)
			goto exit;
					tzd->ops->set_mode(tzd,
						THERMAL_DEVICE_ENABLED);
			}
		}
		}
		of_node_put(sensor_specs.np);
		of_node_put(sensor_specs.np);
	}
	}
exit:
	of_node_put(sensor_np);
	of_node_put(sensor_np);
	of_node_put(np);
	of_node_put(np);


	return tzd;
	if (!first_tzd) {
		first_tzd = ERR_PTR(-ENODEV);
		kfree(sens_param);
	}
	return first_tzd;
}
}
EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_register);
EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_register);


@@ -530,7 +770,9 @@ EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_register);
void thermal_zone_of_sensor_unregister(struct device *dev,
void thermal_zone_of_sensor_unregister(struct device *dev,
				       struct thermal_zone_device *tzd)
				       struct thermal_zone_device *tzd)
{
{
	struct __thermal_zone *tz;
	struct __thermal_zone *tz, *next;
	struct thermal_zone_device *pos;
	struct list_head *head;


	if (!dev || !tzd || !tzd->devdata)
	if (!dev || !tzd || !tzd->devdata)
		return;
		return;
@@ -541,14 +783,20 @@ void thermal_zone_of_sensor_unregister(struct device *dev,
	if (!tz)
	if (!tz)
		return;
		return;


	mutex_lock(&tzd->lock);
	head = &tz->senps->first_tz;
	tzd->ops->get_temp = NULL;
	list_for_each_entry_safe(tz, next, head, list) {
	tzd->ops->get_trend = NULL;
		pos = tz->tzd;
	tzd->ops->set_emul_temp = NULL;
		mutex_lock(&pos->lock);
		pos->ops->get_temp = NULL;
		pos->ops->get_trend = NULL;
		pos->ops->set_emul_temp = NULL;


	tz->ops = NULL;
		list_del(&tz->list);
	tz->sensor_data = NULL;
		if (list_empty(&tz->senps->first_tz))
	mutex_unlock(&tzd->lock);
			kfree(tz->senps);
		tz->senps = NULL;
		mutex_unlock(&pos->lock);
	}
}
}
EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_unregister);
EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_unregister);


@@ -569,6 +817,136 @@ static int devm_thermal_zone_of_sensor_match(struct device *dev, void *res,
	return *r == data;
	return *r == data;
}
}


/**
 * devm_thermal_of_virtual_sensor_register - Register a virtual sensor.
 *	Three types of virtual sensors are supported.
 *	1. Weighted aggregation type:
 *		Virtual sensor of this type calculates the weighted aggregation
 *		of sensor temperatures using the below formula,
 *		temp = (sensor_1_temp * coeff_1 + ... + sensor_n_temp * coeff_n)
 *			+ avg_offset / avg_denominator
 *		So the sensor drivers has to specify n+2 coefficients.
 *	2. Maximum type:
 *		Virtual sensors of this type will report the maximum of all
 *		sensor temperatures.
 *	3. Minimum type:
 *		Virtual sensors of this type will report the minimum of all
 *		sensor temperatures.
 *
 * @input arguments:
 * @dev: Virtual sensor driver device pointer.
 * @sensor_data: Virtual sensor data supported for the device.
 *
 * @return: Returns a virtual thermal zone pointer. Returns error if thermal
 * zone is not created. Returns -EAGAIN, if the sensor that is required for
 * this virtual sensor temperature estimation is not registered yet. The
 * sensor driver can try again later.
 */
struct thermal_zone_device *devm_thermal_of_virtual_sensor_register(
		struct device *dev,
		const struct virtual_sensor_data *sensor_data)
{
	int sens_idx = 0;
	struct virtual_sensor *sens;
	struct __thermal_zone *tz;
	struct thermal_zone_device **ptr;
	struct thermal_zone_device *tzd;
	struct __sensor_param *sens_param = NULL;
	enum thermal_device_mode mode;

	if (!dev || !sensor_data)
		return ERR_PTR(-EINVAL);

	tzd = thermal_zone_get_zone_by_name(
				sensor_data->virt_zone_name);
	if (IS_ERR(tzd)) {
		dev_dbg(dev, "sens:%s not available err: %ld\n",
				sensor_data->virt_zone_name,
				PTR_ERR(tzd));
		return tzd;
	}

	mutex_lock(&tzd->lock);
	/*
	 * Check if the virtual zone is registered and enabled.
	 * If so return the registered thermal zone.
	 */
	tzd->ops->get_mode(tzd, &mode);
	mutex_unlock(&tzd->lock);
	if (mode == THERMAL_DEVICE_ENABLED)
		return tzd;

	sens = devm_kzalloc(dev, sizeof(*sens), GFP_KERNEL);
	if (!sens)
		return ERR_PTR(-ENOMEM);

	sens->virt_tz = tzd;
	sens->logic = sensor_data->logic;
	sens->num_sensors = sensor_data->num_sensors;
	if (sens->logic == VIRT_WEIGHTED_AVG) {
		int coeff_ct = sensor_data->coefficient_ct;

		/*
		 * For weighted aggregation, sensor drivers has to specify
		 * n+2 coefficients.
		 */
		if (coeff_ct != sens->num_sensors) {
			dev_err(dev, "sens:%s Invalid coefficient\n",
					sensor_data->virt_zone_name);
			return ERR_PTR(-EINVAL);
		}
		memcpy(sens->coefficients, sensor_data->coefficients,
			       coeff_ct * sizeof(*sens->coefficients));
		sens->avg_offset = sensor_data->avg_offset;
		sens->avg_denominator = sensor_data->avg_denominator;
	}

	for (sens_idx = 0; sens_idx < sens->num_sensors; sens_idx++) {
		sens->tz[sens_idx] = thermal_zone_get_zone_by_name(
					sensor_data->sensor_names[sens_idx]);
		if (IS_ERR(sens->tz[sens_idx])) {
			dev_err(dev, "sens:%s sensor[%s] fetch err:%ld\n",
				     sensor_data->virt_zone_name,
				     sensor_data->sensor_names[sens_idx],
				     PTR_ERR(sens->tz[sens_idx]));
			break;
		}
	}
	if (sens->num_sensors != sens_idx)
		return ERR_PTR(-EAGAIN);

	sens_param = kzalloc(sizeof(*sens_param), GFP_KERNEL);
	if (!sens_param)
		return ERR_PTR(-ENOMEM);
	sens_param->sensor_data = sens;
	sens_param->ops = &of_virt_ops;
	INIT_LIST_HEAD(&sens_param->first_tz);
	sens_param->trip_high = INT_MAX;
	sens_param->trip_low = INT_MIN;
	mutex_init(&sens_param->lock);

	mutex_lock(&tzd->lock);
	tz = tzd->devdata;
	tz->senps = sens_param;
	tzd->ops->get_temp = of_thermal_get_temp;
	list_add_tail(&tz->list, &sens_param->first_tz);
	mutex_unlock(&tzd->lock);

	ptr = devres_alloc(devm_thermal_zone_of_sensor_release, sizeof(*ptr),
			   GFP_KERNEL);
	if (!ptr)
		return ERR_PTR(-ENOMEM);

	*ptr = tzd;
	devres_add(dev, ptr);

	if (!tz->default_disable)
		tzd->ops->set_mode(tzd, THERMAL_DEVICE_ENABLED);

	return tzd;
}
EXPORT_SYMBOL(devm_thermal_of_virtual_sensor_register);

/**
/**
 * devm_thermal_zone_of_sensor_register - Resource managed version of
 * devm_thermal_zone_of_sensor_register - Resource managed version of
 *				thermal_zone_of_sensor_register()
 *				thermal_zone_of_sensor_register()
@@ -817,6 +1195,7 @@ __init *thermal_of_build_thermal_zone(struct device_node *np)
	if (!tz)
	if (!tz)
		return ERR_PTR(-ENOMEM);
		return ERR_PTR(-ENOMEM);


	INIT_LIST_HEAD(&tz->list);
	ret = of_property_read_u32(np, "polling-delay-passive", &prop);
	ret = of_property_read_u32(np, "polling-delay-passive", &prop);
	if (ret < 0) {
	if (ret < 0) {
		pr_err("missing polling-delay-passive property\n");
		pr_err("missing polling-delay-passive property\n");
@@ -831,6 +1210,8 @@ __init *thermal_of_build_thermal_zone(struct device_node *np)
	}
	}
	tz->polling_delay = prop;
	tz->polling_delay = prop;


	tz->default_disable = of_property_read_bool(np,
					"disable-thermal-zone");
	/*
	/*
	 * REVIST: for now, the thermal framework supports only
	 * REVIST: for now, the thermal framework supports only
	 * one sensor per thermal zone. Thus, we are considering
	 * one sensor per thermal zone. Thus, we are considering
@@ -960,6 +1341,7 @@ int __init of_parse_thermal_zones(void)
		struct thermal_zone_params *tzp;
		struct thermal_zone_params *tzp;
		int i, mask = 0;
		int i, mask = 0;
		u32 prop;
		u32 prop;
		const char *governor_name;


		tz = thermal_of_build_thermal_zone(child);
		tz = thermal_of_build_thermal_zone(child);
		if (IS_ERR(tz)) {
		if (IS_ERR(tz)) {
@@ -982,6 +1364,11 @@ int __init of_parse_thermal_zones(void)
		/* No hwmon because there might be hwmon drivers registering */
		/* No hwmon because there might be hwmon drivers registering */
		tzp->no_hwmon = true;
		tzp->no_hwmon = true;


		if (!of_property_read_string(child, "thermal-governor",
						&governor_name))
			strlcpy(tzp->governor_name, governor_name,
					THERMAL_NAME_LENGTH);

		if (!of_property_read_u32(child, "sustainable-power", &prop))
		if (!of_property_read_u32(child, "sustainable-power", &prop))
			tzp->sustainable_power = prop;
			tzp->sustainable_power = prop;


@@ -992,6 +1379,9 @@ int __init of_parse_thermal_zones(void)
		tzp->slope = tz->slope;
		tzp->slope = tz->slope;
		tzp->offset = tz->offset;
		tzp->offset = tz->offset;


		if (of_property_read_bool(child, "tracks-low"))
			tzp->tracks_low = true;

		zone = thermal_zone_device_register(child->name, tz->ntrips,
		zone = thermal_zone_device_register(child->name, tz->ntrips,
						    mask, tz,
						    mask, tz,
						    ops, tzp,
						    ops, tzp,
@@ -1004,7 +1394,9 @@ int __init of_parse_thermal_zones(void)
			kfree(ops);
			kfree(ops);
			of_thermal_free_zone(tz);
			of_thermal_free_zone(tz);
			/* attempting to build remaining zones still */
			/* attempting to build remaining zones still */
			continue;
		}
		}
		tz->tzd = zone;
	}
	}
	of_node_put(np);
	of_node_put(np);


Loading