diff mbox series

[RFC,2/2] thermal: sprd: Add virtual thermal driver

Message ID 1606466204-31657-1-git-send-email-gao.yunxiao6@gmail.com
State New
Headers show
Series None | expand

Commit Message

gao yunxiao Nov. 27, 2020, 8:36 a.m. UTC
From: "jeson.gao" <jeson.gao@unisoc.com>

IPA need a temperature of the whole CPU zone for thermal-cpufreq-0
and thermal-cpufreq-1 cooling device but the real sensor is placed
near per-core on sprd platform,so adding this driver to register a
virtual sensor,it will polling the temperature of per-core and find
the highest temperature as the current temperature of the whole cpu
zone for IPA using.

Signed-off-by: jeson.gao <jeson.gao@unisoc.com>
---
 drivers/thermal/Kconfig                |   9 ++
 drivers/thermal/Makefile               |   1 +
 drivers/thermal/sprd_virtual_thermal.c | 213 +++++++++++++++++++++++++
 3 files changed, 223 insertions(+)
 create mode 100644 drivers/thermal/sprd_virtual_thermal.c
diff mbox series

Patch

diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 7edc8dc6bbab..b3d392846f69 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -500,6 +500,15 @@  config SPRD_THERMAL
 	  Support for the Spreadtrum thermal sensor driver in the Linux thermal
 	  framework.
 
+config SPRD_VIRTUAL_THERMAL
+	tristate "sprd virtual thermal"
+	depends on ARCH_SPRD || COMPILE_TEST
+	depends on SPRD_THERMAL
+	help
+	  Say Y here to support virtual thermal driver
+	  We can use it to find the highest temperature from 8 per core sensor
+	  as the current temperature of the whole cpu zone.
+
 config KHADAS_MCU_FAN_THERMAL
 	tristate "Khadas MCU controller FAN cooling support"
 	depends on OF || COMPILE_TEST
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index b64dd50a6629..f4aecfff3703 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -61,4 +61,5 @@  obj-$(CONFIG_ZX2967_THERMAL)	+= zx2967_thermal.o
 obj-$(CONFIG_UNIPHIER_THERMAL)	+= uniphier_thermal.o
 obj-$(CONFIG_AMLOGIC_THERMAL)     += amlogic_thermal.o
 obj-$(CONFIG_SPRD_THERMAL)	+= sprd_thermal.o
+obj-$(CONFIG_SPRD_VIRTUAL_THERMAL)	+= sprd_virtual_thermal.o
 obj-$(CONFIG_KHADAS_MCU_FAN_THERMAL)	+= khadas_mcu_fan.o
diff --git a/drivers/thermal/sprd_virtual_thermal.c b/drivers/thermal/sprd_virtual_thermal.c
new file mode 100644
index 000000000000..db4d6eca2356
--- /dev/null
+++ b/drivers/thermal/sprd_virtual_thermal.c
@@ -0,0 +1,213 @@ 
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2020 Unisoc Inc.
+
+#include <linux/cpu_cooling.h>
+#include <linux/cpufreq.h>
+#include <linux/cpumask.h>
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/thermal.h>
+#include <linux/of.h>
+#include <linux/kernel.h>
+
+struct virtual_thm {
+	int id;
+	struct device *dev;
+	struct thermal_zone_device *thm_dev;
+};
+
+struct real_tz_list {
+	int temp;
+	struct thermal_zone_device *tz_dev;
+};
+
+struct virtual_thm_data {
+	int nr_thm;
+	struct real_tz_list *tz_list;
+	struct virtual_thm *vir_thm;
+};
+
+static int virtual_thm_get_temp(void *devdata, int *temp)
+{
+	int i, ret = 0;
+	int max_temp = 0;
+	struct thermal_zone_device *tz = NULL;
+	struct real_tz_list *tz_list = NULL;
+	struct virtual_thm_data *thm_data = devdata;
+	struct device *dev;
+
+	if (!thm_data || !temp)
+		return -EINVAL;
+
+	dev = thm_data->vir_thm->dev;
+	for (i = 0; i < thm_data->nr_thm; i++) {
+		tz_list = &thm_data->tz_list[i];
+		tz = tz_list->tz_dev;
+		if (!tz || IS_ERR(tz) || !tz->ops->get_temp)
+			return -EINVAL;
+		ret = tz->ops->get_temp(tz, &tz_list->temp);
+		if (ret) {
+			dev_err(dev, "fail to get temp\n");
+			return ret;
+		}
+		max_temp = max(max_temp, tz_list->temp);
+	}
+	*temp = max_temp;
+
+	return ret;
+}
+
+static void virtual_thm_unregister(struct platform_device *pdev)
+{
+	struct virtual_thm_data *data = platform_get_drvdata(pdev);
+	struct virtual_thm *vir_thm = data->vir_thm;
+
+	devm_thermal_zone_of_sensor_unregister(&pdev->dev, vir_thm->thm_dev);
+}
+
+static const struct thermal_zone_of_device_ops virtual_thm_ops = {
+	.get_temp = virtual_thm_get_temp,
+};
+
+static int virtual_thm_register(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct virtual_thm_data *data = platform_get_drvdata(pdev);
+	struct virtual_thm *vir_thm = data->vir_thm;
+
+	vir_thm->thm_dev = devm_thermal_zone_of_sensor_register(dev,
+							   vir_thm->id, data,
+							   &virtual_thm_ops);
+	if (IS_ERR_OR_NULL(vir_thm->thm_dev))
+		return -ENODEV;
+	thermal_zone_device_update(vir_thm->thm_dev, THERMAL_EVENT_UNSPECIFIED);
+
+	return 0;
+}
+
+static int get_thm_zone_counts(struct device *dev)
+{
+	int count;
+	struct device_node *np = dev->of_node;
+
+	if (!np) {
+		dev_err(dev, "device node not found\n");
+		return -EINVAL;
+	}
+
+	count = of_property_count_strings(np, "thmzone-names");
+	if (count < 0) {
+		dev_err(dev, "thmzone-names not found\n");
+		return count;
+	}
+
+	return count;
+}
+
+static int get_thm_zone_device(struct platform_device *pdev)
+{
+	int i, ret = 0;
+	const char *name;
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct real_tz_list *tz_list;
+	struct virtual_thm_data *data = platform_get_drvdata(pdev);
+
+	for (i = 0; i < data->nr_thm; i++) {
+		ret = of_property_read_string_index(np, "thmzone-names",
+						    i, &name);
+		if (ret) {
+			dev_err(dev, "fail to get thmzone-names\n");
+			return ret;
+		}
+		tz_list = &data->tz_list[i];
+		tz_list->tz_dev = thermal_zone_get_zone_by_name(name);
+		if (IS_ERR(tz_list->tz_dev)) {
+			dev_err(dev, "failed to get thermal zone by name\n");
+			return -EINVAL;
+		}
+	}
+
+	return ret;
+}
+
+static int virtual_thm_probe(struct platform_device *pdev)
+{
+	int count = 0, ret = 0, id;
+	struct virtual_thm_data *data;
+	struct device *dev = &pdev->dev;
+	struct device_node *np = pdev->dev.of_node;
+
+	if (!np) {
+		dev_err(dev, "device node not found\n");
+		return -EINVAL;
+	}
+	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	count = get_thm_zone_counts(dev);
+	if (count < 0) {
+		dev_err(dev, "failed to get thmzone count\n");
+		return -EINVAL;
+	}
+	data->nr_thm = count;
+	data->tz_list = devm_kzalloc(dev, sizeof(*data->tz_list) * data->nr_thm,
+				     GFP_KERNEL);
+	if (!data->tz_list)
+		return -ENOMEM;
+
+	data->vir_thm = devm_kzalloc(dev, sizeof(*data->vir_thm), GFP_KERNEL);
+	if (!data->vir_thm)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, data);
+	ret = get_thm_zone_device(pdev);
+	if (ret) {
+		dev_err(dev, "failed to get thmzone device\n");
+		return -EINVAL;
+	}
+	id = of_alias_get_id(np, "thm-sensor");
+	if (id < 0) {
+		dev_err(dev, "failed to get id\n");
+		return -ENODEV;
+	}
+	data->vir_thm->id = id;
+	data->vir_thm->dev = dev;
+	ret = virtual_thm_register(pdev);
+	if (ret < 0) {
+		dev_err(dev, "failed to register virtual thermal\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int virtual_thm_remove(struct platform_device *pdev)
+{
+	virtual_thm_unregister(pdev);
+	return 0;
+}
+
+static const struct of_device_id virtual_thermal_of_match[] = {
+	{ .compatible = "sprd,virtual-thermal" },
+	{},
+};
+
+static struct platform_driver virtual_thermal_driver = {
+	.probe = virtual_thm_probe,
+	.remove = virtual_thm_remove,
+	.driver = {
+		   .owner = THIS_MODULE,
+		   .name = "virtual_thermal",
+		   .of_match_table = virtual_thermal_of_match,
+	},
+};
+
+module_platform_driver(virtual_thermal_driver);
+
+MODULE_AUTHOR("Jeson Gao <jeson.gao@unisoc.com>");
+MODULE_DESCRIPTION("Unisoc virtual thermal driver");
+MODULE_LICENSE("GPL v2");