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

Commit 2f2b7464 authored by Ram Chandrasekar's avatar Ram Chandrasekar
Browse files

drivers: thermal: Add virtual thermal zone



SoCs can have multiple thermal sensors monitoring different hot spots of
a same subsystem. In those cases both the sensor temperatures should be
considered for monitoring and mitigation.

In order to support such cases, add support for virtual thermal zone
interface. These virtual thermal zone will use the dependent sensors
thermal zone to get the temperature and aggregate the different sensors
temperatures. The sensors to be used and the way temperatures are
aggregated has to be defined during the virtual zone registration.
Since this is virtual thermal zone, this sensor requires
the thermal core to poll to monitor the trip temperatures.

The current interface provides three aggregation mechanism. It can
either find the,
1. maximum of all sensor readings
2. minimum of all sensor readings
3. weighted aggregation of sensor readings

Change-Id: I7f38a3612ff56de9e7a56a9a337da87823b65bbc
Signed-off-by: default avatarRam Chandrasekar <rkumbako@codeaurora.org>
parent 5915565f
Loading
Loading
Loading
Loading
+199 −0
Original line number Diff line number Diff line
@@ -107,11 +107,78 @@ struct __thermal_zone {
	struct __sensor_param *senps;
};

/**
 * 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
 * @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];
	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   ***/

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

	sens->last_reading = *val = temp;

	return 0;
}

static int of_thermal_get_temp(struct thermal_zone_device *tz,
			       int *temp)
{
@@ -516,6 +583,10 @@ static struct thermal_zone_device_ops of_thermal_ops = {
	.unbind = of_thermal_unbind,
};

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

/***   sensor API   ***/

static struct thermal_zone_device *
@@ -726,6 +797,134 @@ static int devm_thermal_zone_of_sensor_match(struct device *dev, void *res,
	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_err(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->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);

	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
 *				thermal_zone_of_sensor_register()
+47 −0
Original line number Diff line number Diff line
@@ -43,6 +43,9 @@
/* Default weight of a bound cooling device */
#define THERMAL_WEIGHT_DEFAULT 0

/* Max sensors that can be used for a single virtual thermalzone */
#define THERMAL_MAX_VIRT_SENSORS 5

/* use value, which < 0K, to indicate an invalid/uninitialized temperature */
#define THERMAL_TEMP_INVALID	-274000

@@ -405,6 +408,39 @@ struct thermal_trip {
	enum thermal_trip_type type;
};

/* Different aggregation logic supported for virtual sensors */
enum aggregation_logic {
	VIRT_WEIGHTED_AVG,
	VIRT_MAXIMUM,
	VIRT_MINIMUM,
	VIRT_AGGREGATION_NR,
};

/*
 * struct virtual_sensor_data - Data structure used to provide
 *			      information about the virtual zone.
 * @virt_zone_name - Virtual thermal zone name
 * @num_sensors - Number of sensors this virtual zone uses to compute
 *		  temperature
 * @sensor_names - Array of sensor names
 * @logic - Temperature aggregation logic to be used
 * @coefficients - Coefficients to be used for weighted average logic
 * @coefficient_ct - number of coefficients provided as input
 * @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_data {
	int                    num_sensors;
	char                   virt_zone_name[THERMAL_NAME_LENGTH];
	char                   *sensor_names[THERMAL_MAX_VIRT_SENSORS];
	enum aggregation_logic logic;
	int                    coefficients[THERMAL_MAX_VIRT_SENSORS];
	int                    coefficient_ct;
	int                    avg_offset;
	int                    avg_denominator;
};

/* Function declarations */
#ifdef CONFIG_THERMAL_OF
struct thermal_zone_device *
@@ -417,6 +453,9 @@ struct thermal_zone_device *devm_thermal_zone_of_sensor_register(
		const struct thermal_zone_of_device_ops *ops);
void devm_thermal_zone_of_sensor_unregister(struct device *dev,
					    struct thermal_zone_device *tz);
struct thermal_zone_device *devm_thermal_of_virtual_sensor_register(
		struct device *dev,
		const struct virtual_sensor_data *sensor_data);
#else
static inline struct thermal_zone_device *
thermal_zone_of_sensor_register(struct device *dev, int id, void *data,
@@ -444,6 +483,14 @@ void devm_thermal_zone_of_sensor_unregister(struct device *dev,
{
}

static inline
struct thermal_zone_device *devm_thermal_of_virtual_sensor_register(
		struct device *dev,
		const struct virtual_sensor_data *sensor_data)
{
	return ERR_PTR(-ENODEV);
}

#endif

#if IS_ENABLED(CONFIG_THERMAL)