Loading drivers/thermal/of-thermal.c +199 −0 Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -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 * Loading Loading @@ -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() Loading drivers/thermal/qcom/Kconfig +10 −0 Original line number Diff line number Diff line Loading @@ -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. drivers/thermal/qcom/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -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 drivers/thermal/qcom/qti_virtual_sensor.c 0 → 100644 +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); drivers/thermal/qcom/qti_virtual_sensor.h 0 → 100644 +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
drivers/thermal/of-thermal.c +199 −0 Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -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 * Loading Loading @@ -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() Loading
drivers/thermal/qcom/Kconfig +10 −0 Original line number Diff line number Diff line Loading @@ -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.
drivers/thermal/qcom/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -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
drivers/thermal/qcom/qti_virtual_sensor.c 0 → 100644 +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);
drivers/thermal/qcom/qti_virtual_sensor.h 0 → 100644 +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__ */