The new type of sensor allows to calculate temperature of thermal zone container. It relays on real thermal zones temperature and provides basic weighted avg algorithm.
Signed-off-by: Lukasz Luba l.luba@partner.samsung.com --- drivers/thermal/Makefile | 2 + drivers/thermal/virt_tsens.c | 126 +++++++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 drivers/thermal/virt_tsens.c
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 610344eb3e03..c0d29120379a 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -27,6 +27,8 @@ thermal_sys-$(CONFIG_CLOCK_THERMAL) += clock_cooling.o # devfreq cooling thermal_sys-$(CONFIG_DEVFREQ_THERMAL) += devfreq_cooling.o
+obj-y += virt_tsens.o + # platform thermal drivers obj-y += broadcom/ obj-$(CONFIG_QCOM_SPMI_TEMP_ALARM) += qcom-spmi-temp-alarm.o diff --git a/drivers/thermal/virt_tsens.c b/drivers/thermal/virt_tsens.c new file mode 100644 index 000000000000..59dffeacecbf --- /dev/null +++ b/drivers/thermal/virt_tsens.c @@ -0,0 +1,126 @@ +/*license */ + +#define pr_fmt(fmt) "VTSENS: " fmt + +#include <linux/device.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/thermal.h> +#include <linux/types.h> + + + +struct vtsens { + struct thermal_zone_device *tzd; + struct mutex lock; +}; + +static int vtsens_get_temp(void *p, int *temp) +{ + struct vtsens *vts = p; + struct thermal_zone_device *tzd = vts->tzd; + struct thermal_zone_device *tz; + int ret = 0; + int subtz_temp; + int avg_temp = 0; + int subzones_capacitance = 0; + + /* We are virtual temp sensor so must relay on real sensors + * which are present in subzones. */ + if (!tzd || !tzd->container) + return -EINVAL; + + + if (list_empty(&tzd->subzones)) + return -EINVAL; + + list_for_each_entry(tz, &tzd->subzones, subzones) { + /* Avoid checking the first entry and prevent from + aggregating containers (circular dependencies, etc.) */ + if (tz->container) + continue; + ret = thermal_zone_get_temp(tz, &subtz_temp); + if (ret) { + return ret; + } + avg_temp += tz->capacitance * subtz_temp; + subzones_capacitance += tz->capacitance; + /* pr_info("%s, subtz_temp = %d\n", tzd->type, subtz_temp); */ + } + + if (subzones_capacitance) + avg_temp /= subzones_capacitance; + else + ret = -EINVAL; + + *temp = avg_temp; + + return ret; +} + +static const struct thermal_zone_of_device_ops vtsens_ops = { + .get_temp = vtsens_get_temp, +}; + +static int vtsens_probe(struct platform_device *pdev) +{ + struct vtsens *vts; + struct device *dev = &pdev->dev; + int ret; + + if (!dev || !dev->of_node) + return -EINVAL; + + vts = devm_kzalloc(&pdev->dev, sizeof(struct vtsens), + GFP_KERNEL); + if (!vts) + return -ENOMEM; + + platform_set_drvdata(pdev, vts); + mutex_init(&vts->lock); + + vts->tzd = thermal_zone_of_sensor_register(&pdev->dev, 0, vts, + &vtsens_ops); + + if (IS_ERR(vts->tzd)) { + ret = PTR_ERR(vts->tzd); + dev_err(&pdev->dev, "Failed during registration of sensor %d\n", + ret); + return ret; + } + + return 0; +} + +static int vtsens_remove(struct platform_device *pdev) +{ + + return 0; +} + +static const struct of_device_id of_virt_tsens_match[] = { + { .compatible = "thermal,virt-tsens", }, + { /* end */ } +}; +MODULE_DEVICE_TABLE(of, of_virt_tsens_match); + + +static struct platform_driver vtsens_thermal = { + .driver = { + .name = "vtsens_thermal", + .of_match_table = of_virt_tsens_match, + }, + .probe = vtsens_probe, + .remove = vtsens_remove, +}; +module_platform_driver(vtsens_thermal); + +MODULE_AUTHOR("Samsung, Inc."); +MODULE_DESCRIPTION("Virtual temperature sensor driver for agregated thermal zones"); +MODULE_LICENSE("GPL v2");