LKML Archive on lore.kernel.org
help / color / mirror / Atom feed
* [PATCH 0/2] Add a generic virtual thermal sensor
@ 2021-09-06 19:04 Alexandre Bailon
2021-09-06 19:04 ` [PATCH 1/2] dt-bindings: Add bindings for the " Alexandre Bailon
2021-09-06 19:04 ` [PATCH 2/2] thermal: add a virtual sensor to aggregate temperatures Alexandre Bailon
0 siblings, 2 replies; 7+ messages in thread
From: Alexandre Bailon @ 2021-09-06 19:04 UTC (permalink / raw)
To: rui.zhang, daniel.lezcano, amitk
Cc: linux-pm, linux-kernel, ben.tseng, khilman, gpain, Alexandre Bailon
This series add a virtual thermal sensor.
It could be used to get a temperature using some thermal sensors.
Currently, the supported operations are max, min and avg.
The virtual sensor could be easily extended to support others operations.
Note:
Currently, thermal drivers must explicitly register their sensors to make them
available to the virtual sensor.
This doesn't seem a good solution to me and I think it would be preferable to
update the framework to register the list of each available sensors.
Alexandre Bailon (2):
dt-bindings: Add bindings for the virtual thermal sensor
thermal: add a virtual sensor to aggregate temperatures
.../thermal/virtual,thermal-sensor.yaml | 67 +++
drivers/thermal/Kconfig | 8 +
drivers/thermal/Makefile | 1 +
drivers/thermal/virtual-sensor.h | 51 +++
drivers/thermal/virtual_sensor.c | 400 ++++++++++++++++++
include/dt-bindings/thermal/virtual-sensor.h | 15 +
6 files changed, 542 insertions(+)
create mode 100644 Documentation/devicetree/bindings/thermal/virtual,thermal-sensor.yaml
create mode 100644 drivers/thermal/virtual-sensor.h
create mode 100644 drivers/thermal/virtual_sensor.c
create mode 100644 include/dt-bindings/thermal/virtual-sensor.h
--
2.31.1
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 1/2] dt-bindings: Add bindings for the virtual thermal sensor
2021-09-06 19:04 [PATCH 0/2] Add a generic virtual thermal sensor Alexandre Bailon
@ 2021-09-06 19:04 ` Alexandre Bailon
2021-10-07 15:56 ` Rafael J. Wysocki
2021-09-06 19:04 ` [PATCH 2/2] thermal: add a virtual sensor to aggregate temperatures Alexandre Bailon
1 sibling, 1 reply; 7+ messages in thread
From: Alexandre Bailon @ 2021-09-06 19:04 UTC (permalink / raw)
To: rui.zhang, daniel.lezcano, amitk
Cc: linux-pm, linux-kernel, ben.tseng, khilman, gpain, Alexandre Bailon
This adds the device tree bidings for the virtual thermal sensor.
The virtual sensor could be used to a temperature computed from
many thermal sensors.
Signed-off-by: Alexandre Bailon <abailon@baylibre.com>
---
.../thermal/virtual,thermal-sensor.yaml | 67 +++++++++++++++++++
include/dt-bindings/thermal/virtual-sensor.h | 15 +++++
2 files changed, 82 insertions(+)
create mode 100644 Documentation/devicetree/bindings/thermal/virtual,thermal-sensor.yaml
create mode 100644 include/dt-bindings/thermal/virtual-sensor.h
diff --git a/Documentation/devicetree/bindings/thermal/virtual,thermal-sensor.yaml b/Documentation/devicetree/bindings/thermal/virtual,thermal-sensor.yaml
new file mode 100644
index 0000000000000..848b5912c79f1
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/virtual,thermal-sensor.yaml
@@ -0,0 +1,67 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright 2021 BayLibre
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/thermal/thermal-sensor.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Virtual thermal sensor binding
+
+description: |
+ The virtual thermal sensor devices provide temperature sensing capabilities
+ based on hardware thermal sensors. Basically, this could be used to get the
+ maximum, minimum or average temperature of the hardware thermal sensors.
+properties:
+ "#thermal-sensor-cells":
+ description:
+ Used to uniquely identify a thermal sensor instance within an IC. Will be
+ 0 on sensor nodes with only a single sensor and at least 1 on nodes
+ containing several internal sensors.
+ enum: [0, 1]
+
+ type:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ Used to select the operations to perform on the sensors to get the virtual
+ sensor temperature.
+ enum:
+ - VIRTUAL_SENSOR_MIN
+ - VIRTUAL_SENSOR_MAX
+ - VIRTUAL_SENSOR_AVG
+
+ thermal-sensors:
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ description:
+ The thermal sensor phandle and sensor specifier used to monitor this
+ thermal zone.
+
+required:
+ - "#thermal-sensor-cells"
+ - type
+ - thermal-sensors
+
+additionalProperties: true
+
+examples:
+ - |
+ #include <dt-bindings/thermal/thermal.h>
+ #include <dt-bindings/thermal/virtual-sensor.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/clock/mt8192-clk.h>
+
+ lvts: lvts@1100b000 {
+ compatible = "mediatek,mt6873-lvts";
+ reg = <0x1100b000 0x1000>;
+ clocks = <&infracfg CLK_INFRA_THERM>;
+ clock-names = "lvts_clk";
+ #thermal-sensor-cells = <0>;
+ interrupts = <GIC_SPI 147 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+ soc_max_sensor: soc_max_sensor {
+ compatible = "virtual,thermal-sensor";
+ #thermal-sensor-cells = <1>;
+ type = <VIRTUAL_SENSOR_MAX>;
+ thermal-sensors = <&lvts 0>, <&lvts 1>;
+ };
+...
diff --git a/include/dt-bindings/thermal/virtual-sensor.h b/include/dt-bindings/thermal/virtual-sensor.h
new file mode 100644
index 0000000000000..b3e4032f6f62b
--- /dev/null
+++ b/include/dt-bindings/thermal/virtual-sensor.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * This header provides constants for virtual thermal sensor bindings.
+ *
+ * Copyright (C) 2021 BayLibre
+ */
+
+#ifndef _DT_BINDINGS_THERMAL_VIRTUAL_SENSOR_H
+#define _DT_BINDINGS_THERMAL_VIRTUAL_SENSOR_H
+
+#define VIRTUAL_SENSOR_MIN 0
+#define VIRTUAL_SENSOR_MAX 1
+#define VIRTUAL_SENSOR_AVG 2
+
+#endif /* _DT_BINDINGS_THERMAL_VIRTUAL_SENSOR_H */
--
2.31.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 2/2] thermal: add a virtual sensor to aggregate temperatures
2021-09-06 19:04 [PATCH 0/2] Add a generic virtual thermal sensor Alexandre Bailon
2021-09-06 19:04 ` [PATCH 1/2] dt-bindings: Add bindings for the " Alexandre Bailon
@ 2021-09-06 19:04 ` Alexandre Bailon
2021-09-07 5:14 ` kernel test robot
2021-09-09 22:29 ` Matthias Kaehlcke
1 sibling, 2 replies; 7+ messages in thread
From: Alexandre Bailon @ 2021-09-06 19:04 UTC (permalink / raw)
To: rui.zhang, daniel.lezcano, amitk
Cc: linux-pm, linux-kernel, ben.tseng, khilman, gpain, Alexandre Bailon
This adds a virtual thermal sensor that reads temperature from
hardware sensor and return an aggregated temperature.
Currently, this only return the max temperature.
Signed-off-by: Alexandre Bailon <abailon@baylibre.com>
---
drivers/thermal/Kconfig | 8 +
drivers/thermal/Makefile | 1 +
drivers/thermal/virtual-sensor.h | 51 ++++
drivers/thermal/virtual_sensor.c | 400 +++++++++++++++++++++++++++++++
4 files changed, 460 insertions(+)
create mode 100644 drivers/thermal/virtual-sensor.h
create mode 100644 drivers/thermal/virtual_sensor.c
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 7a4ba50ba97d0..23dc903da2fc5 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -228,6 +228,14 @@ config THERMAL_MMIO
register or shared memory, is a potential candidate to work with this
driver.
+config VIRTUAL_THERMAL
+ tristate "Generic virtual thermal sensor driver"
+ depends on THERMAL_OF || COMPILE_TEST
+ help
+ This option enables the generic thermal sensor aggregator.
+ This driver creates a thermal sensor that reads the hardware sensors
+ and aggregate the temperature.
+
config HISI_THERMAL
tristate "Hisilicon thermal driver"
depends on ARCH_HISI || COMPILE_TEST
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 9729a2b089919..76dfa1d61bfc5 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -60,3 +60,4 @@ obj-$(CONFIG_UNIPHIER_THERMAL) += uniphier_thermal.o
obj-$(CONFIG_AMLOGIC_THERMAL) += amlogic_thermal.o
obj-$(CONFIG_SPRD_THERMAL) += sprd_thermal.o
obj-$(CONFIG_KHADAS_MCU_FAN_THERMAL) += khadas_mcu_fan.o
+obj-$(CONFIG_VIRTUAL_THERMAL) += virtual_sensor.o
diff --git a/drivers/thermal/virtual-sensor.h b/drivers/thermal/virtual-sensor.h
new file mode 100644
index 0000000000000..e024d434856c7
--- /dev/null
+++ b/drivers/thermal/virtual-sensor.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021 BayLibre
+ */
+
+#ifndef __THERMAL_VIRTUAL_SENSOR_H__
+#define __THERMAL_VIRTUAL_SENSOR_H__
+
+struct virtual_sensor;
+struct virtual_sensor_data;
+
+#ifdef CONFIG_VIRTUAL_THERMAL
+struct virtual_sensor_data *
+thermal_virtual_sensor_register(struct device *dev, int sensor_id, void *data,
+ const struct thermal_zone_of_device_ops *ops);
+void thermal_virtual_sensor_unregister(struct device *dev,
+ struct virtual_sensor_data *sensor_data);
+struct virtual_sensor_data *
+devm_thermal_virtual_sensor_register(struct device *dev, int sensor_id, void *data,
+ const struct thermal_zone_of_device_ops *ops);
+
+void devm_thermal_virtual_sensor_unregister(struct device *dev,
+ struct virtual_sensor *sensor);
+#else
+static inline struct virtual_sensor_data *
+thermal_virtual_sensor_register(struct device *dev, int sensor_id, void *data,
+ const struct thermal_zone_of_device_ops *ops)
+{
+ return ERR_PTR(-ENODEV);
+}
+
+void thermal_virtual_sensor_unregister(struct device *dev,
+ struct virtual_sensor_data *sensor_data)
+{
+}
+
+static inline struct virtual_sensor_data *
+devm_thermal_virtual_sensor_register(struct device *dev, int sensor_id, void *data,
+ const struct thermal_zone_of_device_ops *ops)
+{
+ return ERR_PTR(-ENODEV);
+}
+
+static inline
+void devm_thermal_virtual_sensor_unregister(struct device *dev,
+ struct virtual_sensor *sensor)
+{
+}
+#endif
+
+#endif /* __THERMAL_VIRTUAL_SENSOR_H__ */
diff --git a/drivers/thermal/virtual_sensor.c b/drivers/thermal/virtual_sensor.c
new file mode 100644
index 0000000000000..e5bb0ef9adb39
--- /dev/null
+++ b/drivers/thermal/virtual_sensor.c
@@ -0,0 +1,400 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 BayLibre
+ */
+
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <linux/thermal.h>
+#include <linux/types.h>
+#include <linux/string.h>
+
+#include <dt-bindings/thermal/virtual-sensor.h>
+
+#include "virtual-sensor.h"
+
+struct virtual_sensor_data {
+ struct list_head node;
+
+ /* sensor interface */
+ int id;
+ void *sensor_data;
+ const struct thermal_zone_of_device_ops *ops;
+};
+
+struct virtual_sensor {
+ int count;
+ struct virtual_sensor_data *sensors;
+ struct thermal_zone_device *tzd;
+
+ struct list_head node;
+};
+
+static LIST_HEAD(thermal_sensors);
+static LIST_HEAD(virtual_sensors);
+
+static int virtual_sensor_get_temp_max(void *data, int *temperature)
+{
+ struct virtual_sensor *sensor = data;
+ int max_temp = INT_MIN;
+ int temp;
+ int i;
+
+ for (i = 0; i < sensor->count; i++) {
+ struct virtual_sensor_data *hw_sensor;
+
+ hw_sensor = &sensor->sensors[i];
+ if (!hw_sensor->ops)
+ return -ENODEV;
+
+ hw_sensor->ops->get_temp(hw_sensor->sensor_data, &temp);
+ max_temp = max(max_temp, temp);
+ }
+
+ *temperature = max_temp;
+
+ return 0;
+}
+
+static const struct thermal_zone_of_device_ops virtual_sensor_max_ops = {
+ .get_temp = virtual_sensor_get_temp_max,
+};
+
+static int virtual_sensor_get_temp_min(void *data, int *temperature)
+{
+ struct virtual_sensor *sensor = data;
+ int min_temp = INT_MAX;
+ int temp;
+ int i;
+
+ for (i = 0; i < sensor->count; i++) {
+ struct virtual_sensor_data *hw_sensor;
+
+ hw_sensor = &sensor->sensors[i];
+ if (!hw_sensor->ops)
+ return -ENODEV;
+
+ hw_sensor->ops->get_temp(hw_sensor->sensor_data, &temp);
+ min_temp = min(min_temp, temp);
+ }
+
+ *temperature = min_temp;
+
+ return 0;
+}
+
+static const struct thermal_zone_of_device_ops virtual_sensor_min_ops = {
+ .get_temp = virtual_sensor_get_temp_min,
+};
+
+static int do_avg(int val1, int val2)
+{
+ return ((val1) / 2) + ((val2) / 2) + (((val1) % 2 + (val2) % 2) / 2);
+}
+
+static int virtual_sensor_get_temp_avg(void *data, int *temperature)
+{
+ struct virtual_sensor *sensor = data;
+ int avg_temp = 0;
+ int temp;
+ int i;
+
+ for (i = 0; i < sensor->count; i++) {
+ struct virtual_sensor_data *hw_sensor;
+
+ hw_sensor = &sensor->sensors[i];
+ if (!hw_sensor->ops)
+ return -ENODEV;
+
+ hw_sensor->ops->get_temp(hw_sensor->sensor_data, &temp);
+ avg_temp = do_avg(avg_temp, temp);
+ }
+
+ *temperature = avg_temp;
+
+ return 0;
+}
+
+static const struct thermal_zone_of_device_ops virtual_sensor_avg_ops = {
+ .get_temp = virtual_sensor_get_temp_avg,
+};
+
+static int register_virtual_sensor(struct virtual_sensor *sensor,
+ struct of_phandle_args args,
+ int index)
+{
+ struct virtual_sensor_data *sensor_data;
+ int id;
+
+ list_for_each_entry(sensor_data, &thermal_sensors, node) {
+ id = args.args_count ? args.args[0] : 0;
+ if (sensor_data->id == id) {
+ memcpy(&sensor->sensors[index], sensor_data,
+ sizeof(*sensor_data));
+ return 0;
+ }
+ }
+
+ return -ENODEV;
+}
+
+static int virtual_sensor_probe(struct platform_device *pdev)
+{
+ const struct thermal_zone_of_device_ops *ops;
+ struct virtual_sensor *sensor;
+ struct device *dev = &pdev->dev;
+ struct of_phandle_args args;
+ u32 type;
+ int ret;
+ int i;
+
+ sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
+ if (!sensor)
+ return -ENOMEM;
+
+ sensor->count = of_count_phandle_with_args(dev->of_node,
+ "thermal-sensors",
+ "#thermal-sensor-cells");
+ if (sensor->count <= 0)
+ return -EINVAL;
+
+ sensor->sensors = devm_kmalloc_array(dev, sensor->count,
+ sizeof(*sensor->sensors),
+ GFP_KERNEL);
+ if (!sensor->sensors)
+ return -ENOMEM;
+
+ for (i = 0; i < sensor->count; i++) {
+ ret = of_parse_phandle_with_args(dev->of_node,
+ "thermal-sensors",
+ "#thermal-sensor-cells",
+ i,
+ &args);
+ if (ret)
+ return ret;
+
+ ret = register_virtual_sensor(sensor, args, i);
+ if (ret)
+ return ret;
+ }
+
+ ret = of_property_read_u32(dev->of_node, "type", &type);
+ if (ret)
+ return ret;
+
+ switch (type) {
+ case VIRTUAL_SENSOR_MAX:
+ ops = &virtual_sensor_max_ops;
+ break;
+ case VIRTUAL_SENSOR_MIN:
+ ops = &virtual_sensor_min_ops;
+ break;
+ case VIRTUAL_SENSOR_AVG:
+ ops = &virtual_sensor_avg_ops;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor->tzd = devm_thermal_zone_of_sensor_register(dev, 0, sensor, ops);
+ if (IS_ERR(sensor->tzd))
+ return PTR_ERR(sensor->tzd);
+
+ platform_set_drvdata(pdev, sensor);
+ list_add(&sensor->node, &virtual_sensors);
+
+ return 0;
+}
+
+static int virtual_sensor_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct virtual_sensor *sensor;
+
+ sensor = platform_get_drvdata(pdev);
+ list_del(&sensor->node);
+
+ devm_thermal_zone_of_sensor_unregister(dev, sensor->tzd);
+ devm_kfree(dev, sensor->sensors);
+ devm_kfree(dev, sensor);
+
+ return 0;
+}
+
+static const struct of_device_id virtual_sensor_of_match[] = {
+ {
+ .compatible = "virtual,thermal-sensor",
+ },
+ {
+ },
+};
+MODULE_DEVICE_TABLE(of, thermal_aggr_of_match);
+
+static struct platform_driver virtual_sensor = {
+ .probe = virtual_sensor_probe,
+ .remove = virtual_sensor_remove,
+ .driver = {
+ .name = "virtual-sensor",
+ .of_match_table = virtual_sensor_of_match,
+ },
+};
+
+/**
+ * thermal_virtual_sensor_register - registers a sensor that could by a virtual
+ * sensor
+ * @dev: a valid struct device pointer of a sensor device. Must contain
+ * a valid .of_node, for the sensor node.
+ * @sensor_id: a sensor identifier, in case the sensor IP has more
+ * than one sensors
+ * @data: a private pointer (owned by the caller) that will be passed
+ * back, when a temperature reading is needed.
+ * @ops: struct thermal_zone_of_device_ops *. Must contain at least .get_temp.
+ *
+ * This function will register a thermal sensor to make it available for later
+ * usage by a virtual sensor.
+ *
+ * The thermal zone temperature is provided by the @get_temp function
+ * pointer. When called, it will have the private pointer @data back.
+ *
+ * Return: On success returns a valid struct thermal_zone_device,
+ * otherwise, it returns a corresponding ERR_PTR(). Caller must
+ * check the return value with help of IS_ERR() helper.
+ */
+struct virtual_sensor_data *thermal_virtual_sensor_register(
+ struct device *dev, int sensor_id, void *data,
+ const struct thermal_zone_of_device_ops *ops)
+{
+ struct virtual_sensor_data *sensor_data;
+
+ sensor_data = devm_kzalloc(dev, sizeof(*sensor_data), GFP_KERNEL);
+ if (!sensor_data)
+ return ERR_PTR(-ENOMEM);
+
+ sensor_data->id = sensor_id;
+ sensor_data->sensor_data = data;
+ sensor_data->ops = ops;
+
+ list_add(&sensor_data->node, &thermal_sensors);
+
+ return sensor_data;
+}
+EXPORT_SYMBOL_GPL(thermal_virtual_sensor_register);
+
+/**
+ * thermal_virtual_sensor_unregister - unregisters a sensor
+ * @dev: a valid struct device pointer of a sensor device.
+ * @sensor_data: a pointer to struct virtual_sensor_data to unregister.
+ *
+ * This function removes the sensor from the list of available thermal sensors.
+ * If the sensor is in use, then the next call to .get_temp will return -ENODEV.
+ */
+void thermal_virtual_sensor_unregister(struct device *dev,
+ struct virtual_sensor_data *sensor_data)
+{
+ struct virtual_sensor_data *temp;
+ struct virtual_sensor *sensor;
+ int i;
+
+ list_del(&sensor_data->node);
+
+ list_for_each_entry(sensor, &virtual_sensors, node) {
+ for (i = 0; i < sensor->count; i++) {
+ temp = &sensor->sensors[i];
+ if (temp->id == sensor_data->id &&
+ temp->sensor_data == sensor_data->sensor_data) {
+ temp->ops = NULL;
+ }
+ }
+ }
+ devm_kfree(dev, sensor_data);
+}
+EXPORT_SYMBOL_GPL(thermal_virtual_sensor_unregister);
+
+static void devm_thermal_virtual_sensor_release(struct device *dev, void *res)
+{
+ thermal_virtual_sensor_unregister(dev,
+ *(struct virtual_sensor_data **)res);
+}
+
+static int devm_thermal_virtual_sensor_match(struct device *dev, void *res,
+ void *data)
+{
+ struct virtual_sensor_data **r = res;
+
+ if (WARN_ON(!r || !*r))
+ return 0;
+
+ return *r == data;
+}
+
+
+/**
+ * devm_thermal_virtual_sensor_register - Resource managed version of
+ * thermal_virtual_sensor_register()
+ * @dev: a valid struct device pointer of a sensor device. Must contain
+ * a valid .of_node, for the sensor node.
+ * @sensor_id: a sensor identifier, in case the sensor IP has more
+ * than one sensors
+ * @data: a private pointer (owned by the caller) that will be passed
+ * back, when a temperature reading is needed.
+ * @ops: struct thermal_zone_of_device_ops *. Must contain at least .get_temp.
+ *
+ * Refer thermal_zone_of_sensor_register() for more details.
+ *
+ * Return: On success returns a valid struct virtual_sensor_data,
+ * otherwise, it returns a corresponding ERR_PTR(). Caller must
+ * check the return value with help of IS_ERR() helper.
+ * Registered virtual_sensor_data device will automatically be
+ * released when device is unbounded.
+ */
+struct virtual_sensor_data *devm_thermal_virtual_sensor_register(
+ struct device *dev, int sensor_id,
+ void *data, const struct thermal_zone_of_device_ops *ops)
+{
+ struct virtual_sensor_data **ptr, *sensor_data;
+
+ ptr = devres_alloc(devm_thermal_virtual_sensor_release, sizeof(*ptr),
+ GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ sensor_data = thermal_virtual_sensor_register(dev, sensor_id, data, ops);
+ if (IS_ERR(sensor_data)) {
+ devres_free(ptr);
+ return sensor_data;
+ }
+
+ *ptr = sensor_data;
+ devres_add(dev, ptr);
+
+ return sensor_data;
+}
+EXPORT_SYMBOL_GPL(devm_thermal_virtual_sensor_register);
+
+/**
+ * devm_thermal_virtual_sensor_unregister - Resource managed version of
+ * thermal_virtual_sensor_unregister().
+ * @dev: Device for which resource was allocated.
+ * @sensor: a pointer to struct thermal_zone_device where the sensor is registered.
+ *
+ * This function removes the sensor from the list of sensors registered with
+ * devm_thermal_virtual_sensor_register() API.
+ * Normally this function will not need to be called and the resource
+ * management code will ensure that the resource is freed.
+ */
+void devm_thermal_virtual_sensor_unregister(struct device *dev,
+ struct virtual_sensor *sensor)
+{
+ WARN_ON(devres_release(dev, devm_thermal_virtual_sensor_release,
+ devm_thermal_virtual_sensor_match, sensor));
+}
+EXPORT_SYMBOL_GPL(devm_thermal_virtual_sensor_unregister);
+
+module_platform_driver(virtual_sensor);
+MODULE_AUTHOR("Alexandre Bailon <abailon@baylibre.com>");
+MODULE_DESCRIPTION("Virtual thermal sensor");
+MODULE_LICENSE("GPL v2");
--
2.31.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH 2/2] thermal: add a virtual sensor to aggregate temperatures
2021-09-06 19:04 ` [PATCH 2/2] thermal: add a virtual sensor to aggregate temperatures Alexandre Bailon
@ 2021-09-07 5:14 ` kernel test robot
2021-09-09 22:29 ` Matthias Kaehlcke
1 sibling, 0 replies; 7+ messages in thread
From: kernel test robot @ 2021-09-07 5:14 UTC (permalink / raw)
To: Alexandre Bailon, rui.zhang, daniel.lezcano, amitk
Cc: kbuild-all, linux-pm, linux-kernel, ben.tseng, khilman, gpain,
Alexandre Bailon
[-- Attachment #1: Type: text/plain, Size: 12231 bytes --]
Hi Alexandre,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on robh/for-next]
[also build test ERROR on linus/master v5.14 next-20210906]
[cannot apply to thermal/next soc-thermal/next]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Alexandre-Bailon/Add-a-generic-virtual-thermal-sensor/20210907-030547
base: https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next
config: riscv-allmodconfig (attached as .config)
compiler: riscv64-linux-gcc (GCC) 11.2.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/0day-ci/linux/commit/2e3e80b0ee6c69039ada990aaf0380e8c6c024c0
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Alexandre-Bailon/Add-a-generic-virtual-thermal-sensor/20210907-030547
git checkout 2e3e80b0ee6c69039ada990aaf0380e8c6c024c0
# save the attached .config to linux build tree
mkdir build_dir
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross O=build_dir ARCH=riscv SHELL=/bin/bash drivers/
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>
All errors (new ones prefixed by >>):
In file included from drivers/thermal/virtual_sensor.c:18:
drivers/thermal/virtual-sensor.h:32:6: warning: no previous prototype for 'thermal_virtual_sensor_unregister' [-Wmissing-prototypes]
32 | void thermal_virtual_sensor_unregister(struct device *dev,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from drivers/thermal/virtual_sensor.c:8:
>> drivers/thermal/virtual_sensor.c:235:25: error: 'thermal_aggr_of_match' undeclared here (not in a function)
235 | MODULE_DEVICE_TABLE(of, thermal_aggr_of_match);
| ^~~~~~~~~~~~~~~~~~~~~
include/linux/module.h:244:15: note: in definition of macro 'MODULE_DEVICE_TABLE'
244 | extern typeof(name) __mod_##type##__##name##_device_table \
| ^~~~
>> drivers/thermal/virtual_sensor.c:267:29: error: redefinition of 'thermal_virtual_sensor_register'
267 | struct virtual_sensor_data *thermal_virtual_sensor_register(
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from drivers/thermal/virtual_sensor.c:18:
drivers/thermal/virtual-sensor.h:26:1: note: previous definition of 'thermal_virtual_sensor_register' with type 'struct virtual_sensor_data *(struct device *, int, void *, const struct thermal_zone_of_device_ops *)'
26 | thermal_virtual_sensor_register(struct device *dev, int sensor_id, void *data,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/thermal/virtual_sensor.c:295:6: error: redefinition of 'thermal_virtual_sensor_unregister'
295 | void thermal_virtual_sensor_unregister(struct device *dev,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from drivers/thermal/virtual_sensor.c:18:
drivers/thermal/virtual-sensor.h:32:6: note: previous definition of 'thermal_virtual_sensor_unregister' with type 'void(struct device *, struct virtual_sensor_data *)'
32 | void thermal_virtual_sensor_unregister(struct device *dev,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/thermal/virtual_sensor.c:354:29: error: redefinition of 'devm_thermal_virtual_sensor_register'
354 | struct virtual_sensor_data *devm_thermal_virtual_sensor_register(
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from drivers/thermal/virtual_sensor.c:18:
drivers/thermal/virtual-sensor.h:38:1: note: previous definition of 'devm_thermal_virtual_sensor_register' with type 'struct virtual_sensor_data *(struct device *, int, void *, const struct thermal_zone_of_device_ops *)'
38 | devm_thermal_virtual_sensor_register(struct device *dev, int sensor_id, void *data,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/thermal/virtual_sensor.c:389:6: error: redefinition of 'devm_thermal_virtual_sensor_unregister'
389 | void devm_thermal_virtual_sensor_unregister(struct device *dev,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from drivers/thermal/virtual_sensor.c:18:
drivers/thermal/virtual-sensor.h:45:6: note: previous definition of 'devm_thermal_virtual_sensor_unregister' with type 'void(struct device *, struct virtual_sensor *)'
45 | void devm_thermal_virtual_sensor_unregister(struct device *dev,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from drivers/thermal/virtual_sensor.c:8:
>> include/linux/module.h:244:21: error: '__mod_of__thermal_aggr_of_match_device_table' aliased to undefined symbol 'thermal_aggr_of_match'
244 | extern typeof(name) __mod_##type##__##name##_device_table \
| ^~~~~~
drivers/thermal/virtual_sensor.c:235:1: note: in expansion of macro 'MODULE_DEVICE_TABLE'
235 | MODULE_DEVICE_TABLE(of, thermal_aggr_of_match);
| ^~~~~~~~~~~~~~~~~~~
vim +/thermal_aggr_of_match +235 drivers/thermal/virtual_sensor.c
227
228 static const struct of_device_id virtual_sensor_of_match[] = {
229 {
230 .compatible = "virtual,thermal-sensor",
231 },
232 {
233 },
234 };
> 235 MODULE_DEVICE_TABLE(of, thermal_aggr_of_match);
236
237 static struct platform_driver virtual_sensor = {
238 .probe = virtual_sensor_probe,
239 .remove = virtual_sensor_remove,
240 .driver = {
241 .name = "virtual-sensor",
242 .of_match_table = virtual_sensor_of_match,
243 },
244 };
245
246 /**
247 * thermal_virtual_sensor_register - registers a sensor that could by a virtual
248 * sensor
249 * @dev: a valid struct device pointer of a sensor device. Must contain
250 * a valid .of_node, for the sensor node.
251 * @sensor_id: a sensor identifier, in case the sensor IP has more
252 * than one sensors
253 * @data: a private pointer (owned by the caller) that will be passed
254 * back, when a temperature reading is needed.
255 * @ops: struct thermal_zone_of_device_ops *. Must contain at least .get_temp.
256 *
257 * This function will register a thermal sensor to make it available for later
258 * usage by a virtual sensor.
259 *
260 * The thermal zone temperature is provided by the @get_temp function
261 * pointer. When called, it will have the private pointer @data back.
262 *
263 * Return: On success returns a valid struct thermal_zone_device,
264 * otherwise, it returns a corresponding ERR_PTR(). Caller must
265 * check the return value with help of IS_ERR() helper.
266 */
> 267 struct virtual_sensor_data *thermal_virtual_sensor_register(
268 struct device *dev, int sensor_id, void *data,
269 const struct thermal_zone_of_device_ops *ops)
270 {
271 struct virtual_sensor_data *sensor_data;
272
273 sensor_data = devm_kzalloc(dev, sizeof(*sensor_data), GFP_KERNEL);
274 if (!sensor_data)
275 return ERR_PTR(-ENOMEM);
276
277 sensor_data->id = sensor_id;
278 sensor_data->sensor_data = data;
279 sensor_data->ops = ops;
280
281 list_add(&sensor_data->node, &thermal_sensors);
282
283 return sensor_data;
284 }
285 EXPORT_SYMBOL_GPL(thermal_virtual_sensor_register);
286
287 /**
288 * thermal_virtual_sensor_unregister - unregisters a sensor
289 * @dev: a valid struct device pointer of a sensor device.
290 * @sensor_data: a pointer to struct virtual_sensor_data to unregister.
291 *
292 * This function removes the sensor from the list of available thermal sensors.
293 * If the sensor is in use, then the next call to .get_temp will return -ENODEV.
294 */
> 295 void thermal_virtual_sensor_unregister(struct device *dev,
296 struct virtual_sensor_data *sensor_data)
297 {
298 struct virtual_sensor_data *temp;
299 struct virtual_sensor *sensor;
300 int i;
301
302 list_del(&sensor_data->node);
303
304 list_for_each_entry(sensor, &virtual_sensors, node) {
305 for (i = 0; i < sensor->count; i++) {
306 temp = &sensor->sensors[i];
307 if (temp->id == sensor_data->id &&
308 temp->sensor_data == sensor_data->sensor_data) {
309 temp->ops = NULL;
310 }
311 }
312 }
313 devm_kfree(dev, sensor_data);
314 }
315 EXPORT_SYMBOL_GPL(thermal_virtual_sensor_unregister);
316
317 static void devm_thermal_virtual_sensor_release(struct device *dev, void *res)
318 {
319 thermal_virtual_sensor_unregister(dev,
320 *(struct virtual_sensor_data **)res);
321 }
322
323 static int devm_thermal_virtual_sensor_match(struct device *dev, void *res,
324 void *data)
325 {
326 struct virtual_sensor_data **r = res;
327
328 if (WARN_ON(!r || !*r))
329 return 0;
330
331 return *r == data;
332 }
333
334
335 /**
336 * devm_thermal_virtual_sensor_register - Resource managed version of
337 * thermal_virtual_sensor_register()
338 * @dev: a valid struct device pointer of a sensor device. Must contain
339 * a valid .of_node, for the sensor node.
340 * @sensor_id: a sensor identifier, in case the sensor IP has more
341 * than one sensors
342 * @data: a private pointer (owned by the caller) that will be passed
343 * back, when a temperature reading is needed.
344 * @ops: struct thermal_zone_of_device_ops *. Must contain at least .get_temp.
345 *
346 * Refer thermal_zone_of_sensor_register() for more details.
347 *
348 * Return: On success returns a valid struct virtual_sensor_data,
349 * otherwise, it returns a corresponding ERR_PTR(). Caller must
350 * check the return value with help of IS_ERR() helper.
351 * Registered virtual_sensor_data device will automatically be
352 * released when device is unbounded.
353 */
> 354 struct virtual_sensor_data *devm_thermal_virtual_sensor_register(
355 struct device *dev, int sensor_id,
356 void *data, const struct thermal_zone_of_device_ops *ops)
357 {
358 struct virtual_sensor_data **ptr, *sensor_data;
359
360 ptr = devres_alloc(devm_thermal_virtual_sensor_release, sizeof(*ptr),
361 GFP_KERNEL);
362 if (!ptr)
363 return ERR_PTR(-ENOMEM);
364
365 sensor_data = thermal_virtual_sensor_register(dev, sensor_id, data, ops);
366 if (IS_ERR(sensor_data)) {
367 devres_free(ptr);
368 return sensor_data;
369 }
370
371 *ptr = sensor_data;
372 devres_add(dev, ptr);
373
374 return sensor_data;
375 }
376 EXPORT_SYMBOL_GPL(devm_thermal_virtual_sensor_register);
377
378 /**
379 * devm_thermal_virtual_sensor_unregister - Resource managed version of
380 * thermal_virtual_sensor_unregister().
381 * @dev: Device for which resource was allocated.
382 * @sensor: a pointer to struct thermal_zone_device where the sensor is registered.
383 *
384 * This function removes the sensor from the list of sensors registered with
385 * devm_thermal_virtual_sensor_register() API.
386 * Normally this function will not need to be called and the resource
387 * management code will ensure that the resource is freed.
388 */
> 389 void devm_thermal_virtual_sensor_unregister(struct device *dev,
390 struct virtual_sensor *sensor)
391 {
392 WARN_ON(devres_release(dev, devm_thermal_virtual_sensor_release,
393 devm_thermal_virtual_sensor_match, sensor));
394 }
395 EXPORT_SYMBOL_GPL(devm_thermal_virtual_sensor_unregister);
396
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 70094 bytes --]
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 2/2] thermal: add a virtual sensor to aggregate temperatures
2021-09-06 19:04 ` [PATCH 2/2] thermal: add a virtual sensor to aggregate temperatures Alexandre Bailon
2021-09-07 5:14 ` kernel test robot
@ 2021-09-09 22:29 ` Matthias Kaehlcke
2021-09-15 13:32 ` Alexandre Bailon
1 sibling, 1 reply; 7+ messages in thread
From: Matthias Kaehlcke @ 2021-09-09 22:29 UTC (permalink / raw)
To: Alexandre Bailon
Cc: rui.zhang, daniel.lezcano, amitk, linux-pm, linux-kernel,
ben.tseng, khilman, gpain
On Mon, Sep 06, 2021 at 09:04:54PM +0200, Alexandre Bailon wrote:
> This adds a virtual thermal sensor that reads temperature from
> hardware sensor and return an aggregated temperature.
s/hardware sensor/hardware sensors/
> Currently, this only return the max temperature.
it seems this is outdated.
> Signed-off-by: Alexandre Bailon <abailon@baylibre.com>
> ---
> drivers/thermal/Kconfig | 8 +
> drivers/thermal/Makefile | 1 +
> drivers/thermal/virtual-sensor.h | 51 ++++
> drivers/thermal/virtual_sensor.c | 400 +++++++++++++++++++++++++++++++
> 4 files changed, 460 insertions(+)
> create mode 100644 drivers/thermal/virtual-sensor.h
> create mode 100644 drivers/thermal/virtual_sensor.c
>
> diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
> index 7a4ba50ba97d0..23dc903da2fc5 100644
> --- a/drivers/thermal/Kconfig
> +++ b/drivers/thermal/Kconfig
> @@ -228,6 +228,14 @@ config THERMAL_MMIO
> register or shared memory, is a potential candidate to work with this
> driver.
>
> +config VIRTUAL_THERMAL
> + tristate "Generic virtual thermal sensor driver"
Not sure if 'Generic' adds much value here.
> + depends on THERMAL_OF || COMPILE_TEST
> + help
> + This option enables the generic thermal sensor aggregator.
> + This driver creates a thermal sensor that reads the hardware sensors
> + and aggregate the temperature.
> +
> config HISI_THERMAL
> tristate "Hisilicon thermal driver"
> depends on ARCH_HISI || COMPILE_TEST
> diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
> index 9729a2b089919..76dfa1d61bfc5 100644
> --- a/drivers/thermal/Makefile
> +++ b/drivers/thermal/Makefile
> @@ -60,3 +60,4 @@ obj-$(CONFIG_UNIPHIER_THERMAL) += uniphier_thermal.o
> obj-$(CONFIG_AMLOGIC_THERMAL) += amlogic_thermal.o
> obj-$(CONFIG_SPRD_THERMAL) += sprd_thermal.o
> obj-$(CONFIG_KHADAS_MCU_FAN_THERMAL) += khadas_mcu_fan.o
> +obj-$(CONFIG_VIRTUAL_THERMAL) += virtual_sensor.o
> diff --git a/drivers/thermal/virtual-sensor.h b/drivers/thermal/virtual-sensor.h
> new file mode 100644
> index 0000000000000..e024d434856c7
> --- /dev/null
> +++ b/drivers/thermal/virtual-sensor.h
> @@ -0,0 +1,51 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2021 BayLibre
> + */
> +
> +#ifndef __THERMAL_VIRTUAL_SENSOR_H__
> +#define __THERMAL_VIRTUAL_SENSOR_H__
> +
> +struct virtual_sensor;
> +struct virtual_sensor_data;
Other types of virtual sensors could be added in the future, you might
want to name these virtual_thermal_sensor(_data). Then again, these
structs are of internal use only, so it's probably not super important.
> +
> +#ifdef CONFIG_VIRTUAL_THERMAL
> +struct virtual_sensor_data *
> +thermal_virtual_sensor_register(struct device *dev, int sensor_id, void *data,
> + const struct thermal_zone_of_device_ops *ops);
> +void thermal_virtual_sensor_unregister(struct device *dev,
> + struct virtual_sensor_data *sensor_data);
> +struct virtual_sensor_data *
> +devm_thermal_virtual_sensor_register(struct device *dev, int sensor_id, void *data,
> + const struct thermal_zone_of_device_ops *ops);
> +
> +void devm_thermal_virtual_sensor_unregister(struct device *dev,
> + struct virtual_sensor *sensor);
> +#else
> +static inline struct virtual_sensor_data *
> +thermal_virtual_sensor_register(struct device *dev, int sensor_id, void *data,
> + const struct thermal_zone_of_device_ops *ops)
> +{
> + return ERR_PTR(-ENODEV);
> +}
> +
> +void thermal_virtual_sensor_unregister(struct device *dev,
> + struct virtual_sensor_data *sensor_data)
> +{
> +}
> +
> +static inline struct virtual_sensor_data *
> +devm_thermal_virtual_sensor_register(struct device *dev, int sensor_id, void *data,
> + const struct thermal_zone_of_device_ops *ops)
> +{
> + return ERR_PTR(-ENODEV);
> +}
> +
> +static inline
> +void devm_thermal_virtual_sensor_unregister(struct device *dev,
> + struct virtual_sensor *sensor)
> +{
> +}
> +#endif
> +
> +#endif /* __THERMAL_VIRTUAL_SENSOR_H__ */
> diff --git a/drivers/thermal/virtual_sensor.c b/drivers/thermal/virtual_sensor.c
> new file mode 100644
> index 0000000000000..e5bb0ef9adb39
> --- /dev/null
> +++ b/drivers/thermal/virtual_sensor.c
> @@ -0,0 +1,400 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2021 BayLibre
> + */
> +
> +#include <linux/err.h>
> +#include <linux/export.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/of_platform.h>
> +#include <linux/slab.h>
> +#include <linux/thermal.h>
> +#include <linux/types.h>
> +#include <linux/string.h>
> +
> +#include <dt-bindings/thermal/virtual-sensor.h>
> +
> +#include "virtual-sensor.h"
> +
> +struct virtual_sensor_data {
This struct (typically) corresponds to an actual sensor, maybe name it
'thermal_sensor_data' instead?
> + struct list_head node;
> +
> + /* sensor interface */
> + int id;
> + void *sensor_data;
> + const struct thermal_zone_of_device_ops *ops;
> +};
> +
> +struct virtual_sensor {
> + int count;
> + struct virtual_sensor_data *sensors;
> + struct thermal_zone_device *tzd;
> +
> + struct list_head node;
> +};
> +
> +static LIST_HEAD(thermal_sensors);
> +static LIST_HEAD(virtual_sensors);
> +
> +static int virtual_sensor_get_temp_max(void *data, int *temperature)
> +{
> + struct virtual_sensor *sensor = data;
> + int max_temp = INT_MIN;
> + int temp;
> + int i;
> +
> + for (i = 0; i < sensor->count; i++) {
> + struct virtual_sensor_data *hw_sensor;
> +
> + hw_sensor = &sensor->sensors[i];
> + if (!hw_sensor->ops)
> + return -ENODEV;
> +
> + hw_sensor->ops->get_temp(hw_sensor->sensor_data, &temp);
> + max_temp = max(max_temp, temp);
> + }
> +
> + *temperature = max_temp;
> +
> + return 0;
> +}
> +
> +static const struct thermal_zone_of_device_ops virtual_sensor_max_ops = {
> + .get_temp = virtual_sensor_get_temp_max,
> +};
> +
> +static int virtual_sensor_get_temp_min(void *data, int *temperature)
> +{
> + struct virtual_sensor *sensor = data;
> + int min_temp = INT_MAX;
> + int temp;
> + int i;
> +
> + for (i = 0; i < sensor->count; i++) {
> + struct virtual_sensor_data *hw_sensor;
> +
> + hw_sensor = &sensor->sensors[i];
> + if (!hw_sensor->ops)
> + return -ENODEV;
> +
> + hw_sensor->ops->get_temp(hw_sensor->sensor_data, &temp);
> + min_temp = min(min_temp, temp);
> + }
> +
> + *temperature = min_temp;
> +
> + return 0;
> +}
> +
> +static const struct thermal_zone_of_device_ops virtual_sensor_min_ops = {
> + .get_temp = virtual_sensor_get_temp_min,
> +};
> +
> +static int do_avg(int val1, int val2)
> +{
> + return ((val1) / 2) + ((val2) / 2) + (((val1) % 2 + (val2) % 2) / 2);
> +}
> +
> +static int virtual_sensor_get_temp_avg(void *data, int *temperature)
> +{
> + struct virtual_sensor *sensor = data;
> + int avg_temp = 0;
> + int temp;
> + int i;
> +
> + for (i = 0; i < sensor->count; i++) {
> + struct virtual_sensor_data *hw_sensor;
> +
> + hw_sensor = &sensor->sensors[i];
> + if (!hw_sensor->ops)
> + return -ENODEV;
> +
> + hw_sensor->ops->get_temp(hw_sensor->sensor_data, &temp);
> + avg_temp = do_avg(avg_temp, temp);
> + }
> +
> + *temperature = avg_temp;
> +
> + return 0;
> +}
_get_temp_min(), _get_temp_max() and _get_temp_avg() have the same
structure, the only differences is the aggregator function and the
initialization value. If you wanted to save a few lines of code you
could have a meta-function that receives the initialization value and
a pointer of the aggregator function.
The functions are relatively short though, so I wouldn't claim that
it would be a huge improvement, one could also argue that the code is
easier to follow as is.
> +
> +static const struct thermal_zone_of_device_ops virtual_sensor_avg_ops = {
> + .get_temp = virtual_sensor_get_temp_avg,
> +};
> +
> +static int register_virtual_sensor(struct virtual_sensor *sensor,
> + struct of_phandle_args args,
> + int index)
Does this really register a virtual sensor? IIUC the registered sensor is
(typically) an actual sensor, which is used by a virtual sensor.
Shouldn't it be something like 'register_thermal_sensor' or
'virtual_sensor_add_sensor'?
> +{
> + struct virtual_sensor_data *sensor_data;
> + int id;
> +
> + list_for_each_entry(sensor_data, &thermal_sensors, node) {
> + id = args.args_count ? args.args[0] : 0;
> + if (sensor_data->id == id) {
> + memcpy(&sensor->sensors[index], sensor_data,
> + sizeof(*sensor_data));
> + return 0;
> + }
> + }
> +
> + return -ENODEV;
> +}
> +
> +static int virtual_sensor_probe(struct platform_device *pdev)
> +{
> + const struct thermal_zone_of_device_ops *ops;
> + struct virtual_sensor *sensor;
> + struct device *dev = &pdev->dev;
> + struct of_phandle_args args;
> + u32 type;
> + int ret;
> + int i;
> +
> + sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
> + if (!sensor)
> + return -ENOMEM;
> +
> + sensor->count = of_count_phandle_with_args(dev->of_node,
> + "thermal-sensors",
> + "#thermal-sensor-cells");
> + if (sensor->count <= 0)
> + return -EINVAL;
> +
> + sensor->sensors = devm_kmalloc_array(dev, sensor->count,
> + sizeof(*sensor->sensors),
> + GFP_KERNEL);
> + if (!sensor->sensors)
> + return -ENOMEM;
> +
> + for (i = 0; i < sensor->count; i++) {
> + ret = of_parse_phandle_with_args(dev->of_node,
> + "thermal-sensors",
> + "#thermal-sensor-cells",
> + i,
> + &args);
> + if (ret)
> + return ret;
> +
> + ret = register_virtual_sensor(sensor, args, i);
> + if (ret)
> + return ret;
> + }
> +
> + ret = of_property_read_u32(dev->of_node, "type", &type);
More a question for the binding, butthis should probably be something
more specific than 'type', like 'aggregation-function'.
> + if (ret)
> + return ret;
> +
> + switch (type) {
> + case VIRTUAL_SENSOR_MAX:
> + ops = &virtual_sensor_max_ops;
> + break;
> + case VIRTUAL_SENSOR_MIN:
> + ops = &virtual_sensor_min_ops;
> + break;
> + case VIRTUAL_SENSOR_AVG:
> + ops = &virtual_sensor_avg_ops;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + sensor->tzd = devm_thermal_zone_of_sensor_register(dev, 0, sensor, ops);
> + if (IS_ERR(sensor->tzd))
> + return PTR_ERR(sensor->tzd);
> +
> + platform_set_drvdata(pdev, sensor);
> + list_add(&sensor->node, &virtual_sensors);
If you also added the sensor to 'thermal_sensors' you could support virtual
sensors using virtual sensors, though it's not clear how useful that would be
in practice and it could raise issues with the initialization order.
> +
> + return 0;
> +}
> +
> +static int virtual_sensor_remove(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct virtual_sensor *sensor;
> +
> + sensor = platform_get_drvdata(pdev);
> + list_del(&sensor->node);
> +
> + devm_thermal_zone_of_sensor_unregister(dev, sensor->tzd);
> + devm_kfree(dev, sensor->sensors);
> + devm_kfree(dev, sensor);
Are the above 3 statements really needed, shouldn't devm_* handle that
automagically?
> +
> + return 0;
> +}
> +
> +static const struct of_device_id virtual_sensor_of_match[] = {
> + {
> + .compatible = "virtual,thermal-sensor",
> + },
> + {
> + },
> +};
> +MODULE_DEVICE_TABLE(of, thermal_aggr_of_match);
> +
> +static struct platform_driver virtual_sensor = {
> + .probe = virtual_sensor_probe,
> + .remove = virtual_sensor_remove,
> + .driver = {
> + .name = "virtual-sensor",
I suggest to change it to 'virtual-thermal-sensor' since there might be
other types of virtual sensors.
> + .of_match_table = virtual_sensor_of_match,
> + },
> +};
> +
> +/**
> + * thermal_virtual_sensor_register - registers a sensor that could by a virtual
s/by/be/
> + * sensor
> + * @dev: a valid struct device pointer of a sensor device. Must contain
> + * a valid .of_node, for the sensor node.
> + * @sensor_id: a sensor identifier, in case the sensor IP has more
> + * than one sensors
s/sensors/sensor/
> + * @data: a private pointer (owned by the caller) that will be passed
> + * back, when a temperature reading is needed.
> + * @ops: struct thermal_zone_of_device_ops *. Must contain at least .get_temp.
> + *
> + * This function will register a thermal sensor to make it available for later
> + * usage by a virtual sensor.
> + *
> + * The thermal zone temperature is provided by the @get_temp function
> + * pointer. When called, it will have the private pointer @data back.
> + *
> + * Return: On success returns a valid struct thermal_zone_device,
> + * otherwise, it returns a corresponding ERR_PTR(). Caller must
> + * check the return value with help of IS_ERR() helper.
> + */
> +struct virtual_sensor_data *thermal_virtual_sensor_register(
> + struct device *dev, int sensor_id, void *data,
> + const struct thermal_zone_of_device_ops *ops)
> +{
> + struct virtual_sensor_data *sensor_data;
> +
> + sensor_data = devm_kzalloc(dev, sizeof(*sensor_data), GFP_KERNEL);
> + if (!sensor_data)
> + return ERR_PTR(-ENOMEM);
> +
> + sensor_data->id = sensor_id;
> + sensor_data->sensor_data = data;
> + sensor_data->ops = ops;
> +
> + list_add(&sensor_data->node, &thermal_sensors);
> +
> + return sensor_data;
> +}
> +EXPORT_SYMBOL_GPL(thermal_virtual_sensor_register);
> +
> +/**
> + * thermal_virtual_sensor_unregister - unregisters a sensor
> + * @dev: a valid struct device pointer of a sensor device.
> + * @sensor_data: a pointer to struct virtual_sensor_data to unregister.
> + *
> + * This function removes the sensor from the list of available thermal sensors.
> + * If the sensor is in use, then the next call to .get_temp will return -ENODEV.
> + */
> +void thermal_virtual_sensor_unregister(struct device *dev,
> + struct virtual_sensor_data *sensor_data)
> +{
> + struct virtual_sensor_data *temp;
'temp' might not be the best name in this context, since it's associated with
temperature. Maybe name it 'sd'?
> + struct virtual_sensor *sensor;
> + int i;
> +
> + list_del(&sensor_data->node);
> +
> + list_for_each_entry(sensor, &virtual_sensors, node) {
> + for (i = 0; i < sensor->count; i++) {
> + temp = &sensor->sensors[i];
> + if (temp->id == sensor_data->id &&
> + temp->sensor_data == sensor_data->sensor_data) {
> + temp->ops = NULL;
> + }
> + }
> + }
> + devm_kfree(dev, sensor_data);
Does it actually make sense to allocate the memory with devm_kzalloc() if
it is explicitly freed here?
> +}
> +EXPORT_SYMBOL_GPL(thermal_virtual_sensor_unregister);
> +
> +static void devm_thermal_virtual_sensor_release(struct device *dev, void *res)
> +{
> + thermal_virtual_sensor_unregister(dev,
> + *(struct virtual_sensor_data **)res);
> +}
> +
> +static int devm_thermal_virtual_sensor_match(struct device *dev, void *res,
> + void *data)
> +{
> + struct virtual_sensor_data **r = res;
> +
> + if (WARN_ON(!r || !*r))
> + return 0;
> +
> + return *r == data;
> +}
> +
> +
delete one of the empty lines
> +/**
> + * devm_thermal_virtual_sensor_register - Resource managed version of
> + * thermal_virtual_sensor_register()
> + * @dev: a valid struct device pointer of a sensor device. Must contain
> + * a valid .of_node, for the sensor node.
> + * @sensor_id: a sensor identifier, in case the sensor IP has more
> + * than one sensors
s/sensors/sensor/
> + * @data: a private pointer (owned by the caller) that will be passed
> + * back, when a temperature reading is needed.
> + * @ops: struct thermal_zone_of_device_ops *. Must contain at least .get_temp.
> + *
> + * Refer thermal_zone_of_sensor_register() for more details.
> + *
> + * Return: On success returns a valid struct virtual_sensor_data,
> + * otherwise, it returns a corresponding ERR_PTR(). Caller must
> + * check the return value with help of IS_ERR() helper.
> + * Registered virtual_sensor_data device will automatically be
> + * released when device is unbounded.
s/unbounded/unbound/
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 2/2] thermal: add a virtual sensor to aggregate temperatures
2021-09-09 22:29 ` Matthias Kaehlcke
@ 2021-09-15 13:32 ` Alexandre Bailon
0 siblings, 0 replies; 7+ messages in thread
From: Alexandre Bailon @ 2021-09-15 13:32 UTC (permalink / raw)
To: Matthias Kaehlcke
Cc: rui.zhang, daniel.lezcano, amitk, linux-pm, linux-kernel,
ben.tseng, khilman, gpain
Hi Matthias,
On 9/10/21 12:29 AM, Matthias Kaehlcke wrote:
> On Mon, Sep 06, 2021 at 09:04:54PM +0200, Alexandre Bailon wrote:
>> This adds a virtual thermal sensor that reads temperature from
>> hardware sensor and return an aggregated temperature.
> s/hardware sensor/hardware sensors/
>
>> Currently, this only return the max temperature.
> it seems this is outdated.
>
>> Signed-off-by: Alexandre Bailon <abailon@baylibre.com>
>> ---
>> drivers/thermal/Kconfig | 8 +
>> drivers/thermal/Makefile | 1 +
>> drivers/thermal/virtual-sensor.h | 51 ++++
>> drivers/thermal/virtual_sensor.c | 400 +++++++++++++++++++++++++++++++
>> 4 files changed, 460 insertions(+)
>> create mode 100644 drivers/thermal/virtual-sensor.h
>> create mode 100644 drivers/thermal/virtual_sensor.c
>>
>> diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
>> index 7a4ba50ba97d0..23dc903da2fc5 100644
>> --- a/drivers/thermal/Kconfig
>> +++ b/drivers/thermal/Kconfig
>> @@ -228,6 +228,14 @@ config THERMAL_MMIO
>> register or shared memory, is a potential candidate to work with this
>> driver.
>>
>> +config VIRTUAL_THERMAL
>> + tristate "Generic virtual thermal sensor driver"
> Not sure if 'Generic' adds much value here.
>
>> + depends on THERMAL_OF || COMPILE_TEST
>> + help
>> + This option enables the generic thermal sensor aggregator.
>> + This driver creates a thermal sensor that reads the hardware sensors
>> + and aggregate the temperature.
>> +
>> config HISI_THERMAL
>> tristate "Hisilicon thermal driver"
>> depends on ARCH_HISI || COMPILE_TEST
>> diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
>> index 9729a2b089919..76dfa1d61bfc5 100644
>> --- a/drivers/thermal/Makefile
>> +++ b/drivers/thermal/Makefile
>> @@ -60,3 +60,4 @@ obj-$(CONFIG_UNIPHIER_THERMAL) += uniphier_thermal.o
>> obj-$(CONFIG_AMLOGIC_THERMAL) += amlogic_thermal.o
>> obj-$(CONFIG_SPRD_THERMAL) += sprd_thermal.o
>> obj-$(CONFIG_KHADAS_MCU_FAN_THERMAL) += khadas_mcu_fan.o
>> +obj-$(CONFIG_VIRTUAL_THERMAL) += virtual_sensor.o
>> diff --git a/drivers/thermal/virtual-sensor.h b/drivers/thermal/virtual-sensor.h
>> new file mode 100644
>> index 0000000000000..e024d434856c7
>> --- /dev/null
>> +++ b/drivers/thermal/virtual-sensor.h
>> @@ -0,0 +1,51 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (c) 2021 BayLibre
>> + */
>> +
>> +#ifndef __THERMAL_VIRTUAL_SENSOR_H__
>> +#define __THERMAL_VIRTUAL_SENSOR_H__
>> +
>> +struct virtual_sensor;
>> +struct virtual_sensor_data;
> Other types of virtual sensors could be added in the future, you might
> want to name these virtual_thermal_sensor(_data). Then again, these
> structs are of internal use only, so it's probably not super important.
You are right. This might become confusing at some point.
I will rename them.
>
>> +
>> +#ifdef CONFIG_VIRTUAL_THERMAL
>> +struct virtual_sensor_data *
>> +thermal_virtual_sensor_register(struct device *dev, int sensor_id, void *data,
>> + const struct thermal_zone_of_device_ops *ops);
>> +void thermal_virtual_sensor_unregister(struct device *dev,
>> + struct virtual_sensor_data *sensor_data);
>> +struct virtual_sensor_data *
>> +devm_thermal_virtual_sensor_register(struct device *dev, int sensor_id, void *data,
>> + const struct thermal_zone_of_device_ops *ops);
>> +
>> +void devm_thermal_virtual_sensor_unregister(struct device *dev,
>> + struct virtual_sensor *sensor);
>> +#else
>> +static inline struct virtual_sensor_data *
>> +thermal_virtual_sensor_register(struct device *dev, int sensor_id, void *data,
>> + const struct thermal_zone_of_device_ops *ops)
>> +{
>> + return ERR_PTR(-ENODEV);
>> +}
>> +
>> +void thermal_virtual_sensor_unregister(struct device *dev,
>> + struct virtual_sensor_data *sensor_data)
>> +{
>> +}
>> +
>> +static inline struct virtual_sensor_data *
>> +devm_thermal_virtual_sensor_register(struct device *dev, int sensor_id, void *data,
>> + const struct thermal_zone_of_device_ops *ops)
>> +{
>> + return ERR_PTR(-ENODEV);
>> +}
>> +
>> +static inline
>> +void devm_thermal_virtual_sensor_unregister(struct device *dev,
>> + struct virtual_sensor *sensor)
>> +{
>> +}
>> +#endif
>> +
>> +#endif /* __THERMAL_VIRTUAL_SENSOR_H__ */
>> diff --git a/drivers/thermal/virtual_sensor.c b/drivers/thermal/virtual_sensor.c
>> new file mode 100644
>> index 0000000000000..e5bb0ef9adb39
>> --- /dev/null
>> +++ b/drivers/thermal/virtual_sensor.c
>> @@ -0,0 +1,400 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (c) 2021 BayLibre
>> + */
>> +
>> +#include <linux/err.h>
>> +#include <linux/export.h>
>> +#include <linux/module.h>
>> +#include <linux/of_device.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/slab.h>
>> +#include <linux/thermal.h>
>> +#include <linux/types.h>
>> +#include <linux/string.h>
>> +
>> +#include <dt-bindings/thermal/virtual-sensor.h>
>> +
>> +#include "virtual-sensor.h"
>> +
>> +struct virtual_sensor_data {
> This struct (typically) corresponds to an actual sensor, maybe name it
> 'thermal_sensor_data' instead?
>
>> + struct list_head node;
>> +
>> + /* sensor interface */
>> + int id;
>> + void *sensor_data;
>> + const struct thermal_zone_of_device_ops *ops;
>> +};
>> +
>> +struct virtual_sensor {
>> + int count;
>> + struct virtual_sensor_data *sensors;
>> + struct thermal_zone_device *tzd;
>> +
>> + struct list_head node;
>> +};
>> +
>> +static LIST_HEAD(thermal_sensors);
>> +static LIST_HEAD(virtual_sensors);
>> +
>> +static int virtual_sensor_get_temp_max(void *data, int *temperature)
>> +{
>> + struct virtual_sensor *sensor = data;
>> + int max_temp = INT_MIN;
>> + int temp;
>> + int i;
>> +
>> + for (i = 0; i < sensor->count; i++) {
>> + struct virtual_sensor_data *hw_sensor;
>> +
>> + hw_sensor = &sensor->sensors[i];
>> + if (!hw_sensor->ops)
>> + return -ENODEV;
>> +
>> + hw_sensor->ops->get_temp(hw_sensor->sensor_data, &temp);
>> + max_temp = max(max_temp, temp);
>> + }
>> +
>> + *temperature = max_temp;
>> +
>> + return 0;
>> +}
>> +
>> +static const struct thermal_zone_of_device_ops virtual_sensor_max_ops = {
>> + .get_temp = virtual_sensor_get_temp_max,
>> +};
>> +
>> +static int virtual_sensor_get_temp_min(void *data, int *temperature)
>> +{
>> + struct virtual_sensor *sensor = data;
>> + int min_temp = INT_MAX;
>> + int temp;
>> + int i;
>> +
>> + for (i = 0; i < sensor->count; i++) {
>> + struct virtual_sensor_data *hw_sensor;
>> +
>> + hw_sensor = &sensor->sensors[i];
>> + if (!hw_sensor->ops)
>> + return -ENODEV;
>> +
>> + hw_sensor->ops->get_temp(hw_sensor->sensor_data, &temp);
>> + min_temp = min(min_temp, temp);
>> + }
>> +
>> + *temperature = min_temp;
>> +
>> + return 0;
>> +}
>> +
>> +static const struct thermal_zone_of_device_ops virtual_sensor_min_ops = {
>> + .get_temp = virtual_sensor_get_temp_min,
>> +};
>> +
>> +static int do_avg(int val1, int val2)
>> +{
>> + return ((val1) / 2) + ((val2) / 2) + (((val1) % 2 + (val2) % 2) / 2);
>> +}
>> +
>> +static int virtual_sensor_get_temp_avg(void *data, int *temperature)
>> +{
>> + struct virtual_sensor *sensor = data;
>> + int avg_temp = 0;
>> + int temp;
>> + int i;
>> +
>> + for (i = 0; i < sensor->count; i++) {
>> + struct virtual_sensor_data *hw_sensor;
>> +
>> + hw_sensor = &sensor->sensors[i];
>> + if (!hw_sensor->ops)
>> + return -ENODEV;
>> +
>> + hw_sensor->ops->get_temp(hw_sensor->sensor_data, &temp);
>> + avg_temp = do_avg(avg_temp, temp);
>> + }
>> +
>> + *temperature = avg_temp;
>> +
>> + return 0;
>> +}
> _get_temp_min(), _get_temp_max() and _get_temp_avg() have the same
> structure, the only differences is the aggregator function and the
> initialization value. If you wanted to save a few lines of code you
> could have a meta-function that receives the initialization value and
> a pointer of the aggregator function.
>
> The functions are relatively short though, so I wouldn't claim that
> it would be a huge improvement, one could also argue that the code is
> easier to follow as is.
My intent was to make possible adding more complex functions.
I will factorize the code, and if later there are some use cases,
we will made the update to support more complex functions.
>
>> +
>> +static const struct thermal_zone_of_device_ops virtual_sensor_avg_ops = {
>> + .get_temp = virtual_sensor_get_temp_avg,
>> +};
>> +
>> +static int register_virtual_sensor(struct virtual_sensor *sensor,
>> + struct of_phandle_args args,
>> + int index)
> Does this really register a virtual sensor? IIUC the registered sensor is
> (typically) an actual sensor, which is used by a virtual sensor.
>
> Shouldn't it be something like 'register_thermal_sensor' or
> 'virtual_sensor_add_sensor'?
You are right, virtual_sensor_add_sensor sounds a lot better.
>
>> +{
>> + struct virtual_sensor_data *sensor_data;
>> + int id;
>> +
>> + list_for_each_entry(sensor_data, &thermal_sensors, node) {
>> + id = args.args_count ? args.args[0] : 0;
>> + if (sensor_data->id == id) {
>> + memcpy(&sensor->sensors[index], sensor_data,
>> + sizeof(*sensor_data));
>> + return 0;
>> + }
>> + }
>> +
>> + return -ENODEV;
>> +}
>> +
>> +static int virtual_sensor_probe(struct platform_device *pdev)
>> +{
>> + const struct thermal_zone_of_device_ops *ops;
>> + struct virtual_sensor *sensor;
>> + struct device *dev = &pdev->dev;
>> + struct of_phandle_args args;
>> + u32 type;
>> + int ret;
>> + int i;
>> +
>> + sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
>> + if (!sensor)
>> + return -ENOMEM;
>> +
>> + sensor->count = of_count_phandle_with_args(dev->of_node,
>> + "thermal-sensors",
>> + "#thermal-sensor-cells");
>> + if (sensor->count <= 0)
>> + return -EINVAL;
>> +
>> + sensor->sensors = devm_kmalloc_array(dev, sensor->count,
>> + sizeof(*sensor->sensors),
>> + GFP_KERNEL);
>> + if (!sensor->sensors)
>> + return -ENOMEM;
>> +
>> + for (i = 0; i < sensor->count; i++) {
>> + ret = of_parse_phandle_with_args(dev->of_node,
>> + "thermal-sensors",
>> + "#thermal-sensor-cells",
>> + i,
>> + &args);
>> + if (ret)
>> + return ret;
>> +
>> + ret = register_virtual_sensor(sensor, args, i);
>> + if (ret)
>> + return ret;
>> + }
>> +
>> + ret = of_property_read_u32(dev->of_node, "type", &type);
> More a question for the binding, butthis should probably be something
> more specific than 'type', like 'aggregation-function'.
>
>> + if (ret)
>> + return ret;
>> +
>> + switch (type) {
>> + case VIRTUAL_SENSOR_MAX:
>> + ops = &virtual_sensor_max_ops;
>> + break;
>> + case VIRTUAL_SENSOR_MIN:
>> + ops = &virtual_sensor_min_ops;
>> + break;
>> + case VIRTUAL_SENSOR_AVG:
>> + ops = &virtual_sensor_avg_ops;
>> + break;
>> + default:
>> + return -EINVAL;
>> + }
>> +
>> + sensor->tzd = devm_thermal_zone_of_sensor_register(dev, 0, sensor, ops);
>> + if (IS_ERR(sensor->tzd))
>> + return PTR_ERR(sensor->tzd);
>> +
>> + platform_set_drvdata(pdev, sensor);
>> + list_add(&sensor->node, &virtual_sensors);
> If you also added the sensor to 'thermal_sensors' you could support virtual
> sensors using virtual sensors, though it's not clear how useful that would be
> in practice and it could raise issues with the initialization order.
Unless if we found some use cases, I think we should not do that.
As you mentioned, this would add some complexity to the initialization
to handle it.
>
>> +
>> + return 0;
>> +}
>> +
>> +static int virtual_sensor_remove(struct platform_device *pdev)
>> +{
>> + struct device *dev = &pdev->dev;
>> + struct virtual_sensor *sensor;
>> +
>> + sensor = platform_get_drvdata(pdev);
>> + list_del(&sensor->node);
>> +
>> + devm_thermal_zone_of_sensor_unregister(dev, sensor->tzd);
>> + devm_kfree(dev, sensor->sensors);
>> + devm_kfree(dev, sensor);
> Are the above 3 statements really needed, shouldn't devm_* handle that
> automagically?
No, I will remove them.
>
>> +
>> + return 0;
>> +}
>> +
>> +static const struct of_device_id virtual_sensor_of_match[] = {
>> + {
>> + .compatible = "virtual,thermal-sensor",
>> + },
>> + {
>> + },
>> +};
>> +MODULE_DEVICE_TABLE(of, thermal_aggr_of_match);
>> +
>> +static struct platform_driver virtual_sensor = {
>> + .probe = virtual_sensor_probe,
>> + .remove = virtual_sensor_remove,
>> + .driver = {
>> + .name = "virtual-sensor",
> I suggest to change it to 'virtual-thermal-sensor' since there might be
> other types of virtual sensors.
>
>> + .of_match_table = virtual_sensor_of_match,
>> + },
>> +};
>> +
>> +/**
>> + * thermal_virtual_sensor_register - registers a sensor that could by a virtual
> s/by/be/
>
>> + * sensor
>> + * @dev: a valid struct device pointer of a sensor device. Must contain
>> + * a valid .of_node, for the sensor node.
>> + * @sensor_id: a sensor identifier, in case the sensor IP has more
>> + * than one sensors
> s/sensors/sensor/
>
>> + * @data: a private pointer (owned by the caller) that will be passed
>> + * back, when a temperature reading is needed.
>> + * @ops: struct thermal_zone_of_device_ops *. Must contain at least .get_temp.
>> + *
>> + * This function will register a thermal sensor to make it available for later
>> + * usage by a virtual sensor.
>> + *
>> + * The thermal zone temperature is provided by the @get_temp function
>> + * pointer. When called, it will have the private pointer @data back.
>> + *
>> + * Return: On success returns a valid struct thermal_zone_device,
>> + * otherwise, it returns a corresponding ERR_PTR(). Caller must
>> + * check the return value with help of IS_ERR() helper.
>> + */
>> +struct virtual_sensor_data *thermal_virtual_sensor_register(
>> + struct device *dev, int sensor_id, void *data,
>> + const struct thermal_zone_of_device_ops *ops)
>> +{
>> + struct virtual_sensor_data *sensor_data;
>> +
>> + sensor_data = devm_kzalloc(dev, sizeof(*sensor_data), GFP_KERNEL);
>> + if (!sensor_data)
>> + return ERR_PTR(-ENOMEM);
>> +
>> + sensor_data->id = sensor_id;
>> + sensor_data->sensor_data = data;
>> + sensor_data->ops = ops;
>> +
>> + list_add(&sensor_data->node, &thermal_sensors);
>> +
>> + return sensor_data;
>> +}
>> +EXPORT_SYMBOL_GPL(thermal_virtual_sensor_register);
>> +
>> +/**
>> + * thermal_virtual_sensor_unregister - unregisters a sensor
>> + * @dev: a valid struct device pointer of a sensor device.
>> + * @sensor_data: a pointer to struct virtual_sensor_data to unregister.
>> + *
>> + * This function removes the sensor from the list of available thermal sensors.
>> + * If the sensor is in use, then the next call to .get_temp will return -ENODEV.
>> + */
>> +void thermal_virtual_sensor_unregister(struct device *dev,
>> + struct virtual_sensor_data *sensor_data)
>> +{
>> + struct virtual_sensor_data *temp;
> 'temp' might not be the best name in this context, since it's associated with
> temperature. Maybe name it 'sd'?
>
>> + struct virtual_sensor *sensor;
>> + int i;
>> +
>> + list_del(&sensor_data->node);
>> +
>> + list_for_each_entry(sensor, &virtual_sensors, node) {
>> + for (i = 0; i < sensor->count; i++) {
>> + temp = &sensor->sensors[i];
>> + if (temp->id == sensor_data->id &&
>> + temp->sensor_data == sensor_data->sensor_data) {
>> + temp->ops = NULL;
>> + }
>> + }
>> + }
>> + devm_kfree(dev, sensor_data);
> Does it actually make sense to allocate the memory with devm_kzalloc() if
> it is explicitly freed here?
No, I should not call devm_kfree here.
I will remove it.
Thanks,
Alexandre
>
>> +}
>> +EXPORT_SYMBOL_GPL(thermal_virtual_sensor_unregister);
>> +
>> +static void devm_thermal_virtual_sensor_release(struct device *dev, void *res)
>> +{
>> + thermal_virtual_sensor_unregister(dev,
>> + *(struct virtual_sensor_data **)res);
>> +}
>> +
>> +static int devm_thermal_virtual_sensor_match(struct device *dev, void *res,
>> + void *data)
>> +{
>> + struct virtual_sensor_data **r = res;
>> +
>> + if (WARN_ON(!r || !*r))
>> + return 0;
>> +
>> + return *r == data;
>> +}
>> +
>> +
> delete one of the empty lines
>
>> +/**
>> + * devm_thermal_virtual_sensor_register - Resource managed version of
>> + * thermal_virtual_sensor_register()
>> + * @dev: a valid struct device pointer of a sensor device. Must contain
>> + * a valid .of_node, for the sensor node.
>> + * @sensor_id: a sensor identifier, in case the sensor IP has more
>> + * than one sensors
> s/sensors/sensor/
>
>> + * @data: a private pointer (owned by the caller) that will be passed
>> + * back, when a temperature reading is needed.
>> + * @ops: struct thermal_zone_of_device_ops *. Must contain at least .get_temp.
>> + *
>> + * Refer thermal_zone_of_sensor_register() for more details.
>> + *
>> + * Return: On success returns a valid struct virtual_sensor_data,
>> + * otherwise, it returns a corresponding ERR_PTR(). Caller must
>> + * check the return value with help of IS_ERR() helper.
>> + * Registered virtual_sensor_data device will automatically be
>> + * released when device is unbounded.
> s/unbounded/unbound/
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 1/2] dt-bindings: Add bindings for the virtual thermal sensor
2021-09-06 19:04 ` [PATCH 1/2] dt-bindings: Add bindings for the " Alexandre Bailon
@ 2021-10-07 15:56 ` Rafael J. Wysocki
0 siblings, 0 replies; 7+ messages in thread
From: Rafael J. Wysocki @ 2021-10-07 15:56 UTC (permalink / raw)
To: Alexandre Bailon
Cc: Zhang, Rui, Daniel Lezcano, Amit Kucheria, Linux PM,
Linux Kernel Mailing List, ben.tseng, Kevin Hilman, gpain
On Mon, Sep 6, 2021 at 9:05 PM Alexandre Bailon <abailon@baylibre.com> wrote:
>
> This adds the device tree bidings for the virtual thermal sensor.
I'm not sure what "the virtual thermal sensor" is.
I'm guessing that you mean "DT bindings for the DT-based virtual
sensor driver introduced by a subsequent patch" or something like
this.
I also guess that the purpose is to allow the platform designer to
tell the kernel that some sensors need to be aggregated in order to
get useful information from them and how to aggregate them. Otherwise
it would be hard to say why the aggregation needed to take place in
the kernel.
Moreover, the aggregation functions supported by this series are
somewhat simple and I'm not sure if they are really sufficient in
practice.
> The virtual sensor could be used to a temperature computed from
> many thermal sensors.
>
> Signed-off-by: Alexandre Bailon <abailon@baylibre.com>
Admittedly, I'm not a DT bindings expert, so if I say something
blatantly silly below, sorry about that.
> ---
> .../thermal/virtual,thermal-sensor.yaml | 67 +++++++++++++++++++
> include/dt-bindings/thermal/virtual-sensor.h | 15 +++++
> 2 files changed, 82 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/thermal/virtual,thermal-sensor.yaml
> create mode 100644 include/dt-bindings/thermal/virtual-sensor.h
>
> diff --git a/Documentation/devicetree/bindings/thermal/virtual,thermal-sensor.yaml b/Documentation/devicetree/bindings/thermal/virtual,thermal-sensor.yaml
> new file mode 100644
> index 0000000000000..848b5912c79f1
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/thermal/virtual,thermal-sensor.yaml
> @@ -0,0 +1,67 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +# Copyright 2021 BayLibre
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/thermal/thermal-sensor.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Virtual thermal sensor binding
> +
> +description: |
> + The virtual thermal sensor devices provide temperature sensing capabilities
> + based on hardware thermal sensors. Basically, this could be used to get the
> + maximum, minimum or average temperature of the hardware thermal sensors.
> +properties:
> + "#thermal-sensor-cells":
It isn't clear to me why this is needed. If the "thermal-sensors"
property is required anyway, I'm not sure why it's still necessary to
have another one to find out whether there is just one sensor or more
of them.
> + description:
> + Used to uniquely identify a thermal sensor instance within an IC. Will be
> + 0 on sensor nodes with only a single sensor and at least 1 on nodes
> + containing several internal sensors.
> + enum: [0, 1]
> +
> + type:
> + $ref: /schemas/types.yaml#/definitions/uint32
> + description:
> + Used to select the operations to perform on the sensors to get the virtual
> + sensor temperature.
> + enum:
> + - VIRTUAL_SENSOR_MIN
> + - VIRTUAL_SENSOR_MAX
> + - VIRTUAL_SENSOR_AVG
> +
> + thermal-sensors:
> + $ref: /schemas/types.yaml#/definitions/phandle-array
> + description:
> + The thermal sensor phandle and sensor specifier used to monitor this
> + thermal zone.
> +
> +required:
> + - "#thermal-sensor-cells"
> + - type
> + - thermal-sensors
> +
> +additionalProperties: true
> +
> +examples:
> + - |
> + #include <dt-bindings/thermal/thermal.h>
> + #include <dt-bindings/thermal/virtual-sensor.h>
> + #include <dt-bindings/interrupt-controller/arm-gic.h>
> + #include <dt-bindings/clock/mt8192-clk.h>
> +
> + lvts: lvts@1100b000 {
> + compatible = "mediatek,mt6873-lvts";
> + reg = <0x1100b000 0x1000>;
> + clocks = <&infracfg CLK_INFRA_THERM>;
> + clock-names = "lvts_clk";
> + #thermal-sensor-cells = <0>;
> + interrupts = <GIC_SPI 147 IRQ_TYPE_LEVEL_HIGH>;
> + };
> +
> + soc_max_sensor: soc_max_sensor {
> + compatible = "virtual,thermal-sensor";
Where/how is the above defined?
> + #thermal-sensor-cells = <1>;
> + type = <VIRTUAL_SENSOR_MAX>;
> + thermal-sensors = <&lvts 0>, <&lvts 1>;
> + };
> +...
> diff --git a/include/dt-bindings/thermal/virtual-sensor.h b/include/dt-bindings/thermal/virtual-sensor.h
> new file mode 100644
> index 0000000000000..b3e4032f6f62b
> --- /dev/null
> +++ b/include/dt-bindings/thermal/virtual-sensor.h
> @@ -0,0 +1,15 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * This header provides constants for virtual thermal sensor bindings.
> + *
> + * Copyright (C) 2021 BayLibre
> + */
> +
> +#ifndef _DT_BINDINGS_THERMAL_VIRTUAL_SENSOR_H
> +#define _DT_BINDINGS_THERMAL_VIRTUAL_SENSOR_H
> +
It would be better to use an enum type here.
> +#define VIRTUAL_SENSOR_MIN 0
> +#define VIRTUAL_SENSOR_MAX 1
> +#define VIRTUAL_SENSOR_AVG 2
Also note that the _MIN and _MAX symbols may be confused as limits, so
it may be better to call them _MIN_VAL and _MAX_VAL, respectively.
> +
> +#endif /* _DT_BINDINGS_THERMAL_VIRTUAL_SENSOR_H */
> --
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2021-10-07 15:56 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-09-06 19:04 [PATCH 0/2] Add a generic virtual thermal sensor Alexandre Bailon
2021-09-06 19:04 ` [PATCH 1/2] dt-bindings: Add bindings for the " Alexandre Bailon
2021-10-07 15:56 ` Rafael J. Wysocki
2021-09-06 19:04 ` [PATCH 2/2] thermal: add a virtual sensor to aggregate temperatures Alexandre Bailon
2021-09-07 5:14 ` kernel test robot
2021-09-09 22:29 ` Matthias Kaehlcke
2021-09-15 13:32 ` Alexandre Bailon
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).