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

Commit 66a271bc authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "drivers: qti-virtual-sensor: Add virtual sensors for QTI chipsets"

parents 484042a1 b874e742
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()
+10 −0
Original line number Diff line number Diff line
@@ -30,3 +30,13 @@ config QTI_THERMAL_LIMITS_DCVS
	  each cluster can be used to perform quick thermal mitigations by
	  tracking temperatures of the CPUs and taking thermal action in the
	  hardware without s/w intervention.

config QTI_VIRTUAL_SENSOR
	bool "QTI Virtual Sensor driver"
	depends on THERMAL_OF
	help
	  This driver has the information about the virtual sensors used by
	  QTI chipset's and registers the virtual sensors to a thermal zone.
	  The virtual sensor information includes the underlying thermal
	  sensors to query for temperature and the aggregation logic to
	  determine the virtual sensor temperature.
+1 −0
Original line number Diff line number Diff line
@@ -2,3 +2,4 @@ obj-$(CONFIG_QCOM_TSENS) += qcom_tsens.o
qcom_tsens-y			+= tsens.o tsens-common.o tsens-8916.o tsens-8974.o tsens-8960.o tsens-8996.o
obj-$(CONFIG_MSM_BCL_PERIPHERAL_CTL) += bcl_peripheral.o
obj-$(CONFIG_QTI_THERMAL_LIMITS_DCVS) += msm_lmh_dcvs.o
obj-$(CONFIG_QTI_VIRTUAL_SENSOR) += qti_virtual_sensor.o
+70 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2017, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */


#include <linux/thermal.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/err.h>
#include <linux/platform_device.h>

#include "qti_virtual_sensor.h"

static const struct virtual_sensor_data qti_virtual_sensors[] = {
	{
		.virt_zone_name = "gpu-virt-max-step",
		.num_sensors = 2,
		.sensor_names = {"gpu0-usr",
				"gpu1-usr"},
		.logic = VIRT_MAXIMUM,
	},
	{
		.virt_zone_name = "silver-virt-max-usr",
		.num_sensors = 4,
		.sensor_names = {"cpu0-silver-usr",
				"cpu1-silver-usr",
				"cpu2-silver-usr",
				"cpu3-silver-usr"},
		.logic = VIRT_MAXIMUM,
	},
	{
		.virt_zone_name = "gold-virt-max-usr",
		.num_sensors = 4,
		.sensor_names = {"cpu0-gold-usr",
				"cpu1-gold-usr",
				"cpu2-gold-usr",
				"cpu3-gold-usr"},
		.logic = VIRT_MAXIMUM,
	},
};

int qti_virtual_sensor_register(struct device *dev)
{
	int sens_ct = 0;
	static int idx;
	struct thermal_zone_device *tz;

	sens_ct = ARRAY_SIZE(qti_virtual_sensors);
	for (; idx < sens_ct; idx++) {
		tz = devm_thermal_of_virtual_sensor_register(dev,
				&qti_virtual_sensors[idx]);
		if (IS_ERR(tz))
			dev_dbg(dev, "sensor:%d register error:%ld\n",
					idx, PTR_ERR(tz));
		else
			dev_dbg(dev, "sensor:%d registered\n", idx);
	}

	return 0;
}
EXPORT_SYMBOL(qti_virtual_sensor_register);
+29 −0
Original line number Diff line number Diff line
/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#ifndef __QTI_VIRT_SENS_H__
#define __QTI_VIRT_SENS_H__

#ifdef CONFIG_QTI_VIRTUAL_SENSOR

int qti_virtual_sensor_register(struct device *dev);

#else

static inline int qti_virtual_sensor_register(struct device *dev)
{
	return -ENODEV;
}

#endif /* CONFIG_QTI_VIRTUAL_SENSOR */

#endif /* __QTI_VIRT_SENS_H__ */
Loading