Add a generic virtual thermal sensor
Subsystems (2)
cover Add a generic virtual thermal sensor
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
Signature · 1 line
2.31.1
PATCH 1/2 dt-bindings: Add bindings for the virtual thermal sensor
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.
--- .../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
Documentation/devicetree/bindings/thermal/virtual,thermal-sensor.yaml new file +67 -0
@@ -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>; + }; +...
include/dt-bindings/thermal/virtual-sensor.h new file +15 -0
@@ -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 */
Signature · 1 line
2.31.1
Rafael J. Wysocki
maintainerRe: [PATCH 1/2] dt-bindings: Add bindings for the virtual thermal sensor
Quoted 2 lines · Alexandre Bailon
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.
Quoted 4 lines
The virtual sensor could be used to a temperature computed from many thermal sensors.
Signed-off-by Alexandre Bailon <[email protected]>
Admittedly, I'm not a DT bindings expert, so if I say something blatantly silly below, sorry about that.
Quoted 18 lines
--- .../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.hDocumentation/devicetree/bindings/thermal/virtual,thermal-sensor.yaml new file +5
-0@@ -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#
Documentation/devicetree/bindings/thermal/virtual,thermal-sensor.yaml line 15
Quoted 10 lines
Documentation/devicetree/bindings/thermal/virtual,thermal-sensor.yaml new file +10
-0+$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.
Quoted 37 lines
Documentation/devicetree/bindings/thermal/virtual,thermal-sensor.yaml new file +37
-0+ 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 {
Documentation/devicetree/bindings/thermal/virtual,thermal-sensor.yaml line 62
Quoted 10 lines
Documentation/devicetree/bindings/thermal/virtual,thermal-sensor.yaml new file +10
-0+ 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?
Quoted 11 lines
Documentation/devicetree/bindings/thermal/virtual,thermal-sensor.yaml new file +5
-0+ #thermal-sensor-cells = <1>; + type = <VIRTUAL_SENSOR_MAX>; + thermal-sensors = <&lvts 0>, <&lvts 1>; + }; +...include/dt-bindings/thermal/virtual-sensor.h new file +0
-0
include/dt-bindings/thermal/virtual-sensor.h line 10
Quoted 10 lines
include/dt-bindings/thermal/virtual-sensor.h new file +10
-0@@ -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.
include/dt-bindings/thermal/virtual-sensor.h line 13
Quoted 3 lines
include/dt-bindings/thermal/virtual-sensor.h new file +3
-0+#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.
Quoted 3 lines
include/dt-bindings/thermal/virtual-sensor.h new file +2
-1+ +#endif /* _DT_BINDINGS_THERMAL_VIRTUAL_SENSOR_H */ --
PATCH 2/2 thermal: add a virtual sensor to aggregate temperatures
In reply to Alexandre BailonThis adds a virtual thermal sensor that reads temperature from hardware sensor and return an aggregated temperature. Currently, this only return the max temperature.
--- 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
drivers/thermal/Kconfig +8 -0
@@ -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
drivers/thermal/Makefile +1 -0
@@ -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
drivers/thermal/virtual-sensor.h new file +51 -0
51 lines not on this page · raw message
drivers/thermal/virtual_sensor.c new file +400 -0
400 lines not on this page · raw message
Signature · 1 line
2.31.1
kernel test robot
build botRe: [PATCH 2/2] thermal: add a virtual sensor to aggregate temperatures
Thank you for the patch! Yet something to improve:
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
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(a)lists.01.org
Re: [PATCH 2/2] thermal: add a virtual sensor to aggregate temperatures
In reply to Alexandre BailonQuoted 2 lines · Alexandre Bailon
This adds a virtual thermal sensor that reads temperature from hardware sensor and return an aggregated temperature.
s/hardware sensor/hardware sensors/
Quoted 1 line
Currently, this only return the max temperature.
it seems this is outdated.
Quoted 15 lines
Signed-off-by Alexandre Bailon <[email protected]>--- 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.cdrivers/thermal/Kconfig +0
-0
drivers/thermal/Kconfig config THERMAL_MMIO · line 232
Quoted 5 lines
drivers/thermal/Kconfig +2
-0@@ -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.
Quoted 24 lines
drivers/thermal/Kconfig +6
-0+ 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_TESTdrivers/thermal/Makefile +1
-0@@ -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.odrivers/thermal/virtual-sensor.h new file +0
-0
drivers/thermal/virtual-sensor.h line 10
Quoted 10 lines
drivers/thermal/virtual-sensor.h new file +10
-0@@ -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.
Quoted 57 lines
drivers/thermal/virtual-sensor.h new file +41
-0+ +#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__ */ drivers/thermal/virtual_sensor.c new file +10
-0@@ -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>
drivers/thermal/virtual_sensor.c line 20
Quoted 10 lines
drivers/thermal/virtual_sensor.c new file +10
-0+#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?
Quoted 90 lines
drivers/thermal/virtual_sensor.c new file +90
-0+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)
drivers/thermal/virtual_sensor.c line 120
Quoted 10 lines
drivers/thermal/virtual_sensor.c new file +10
-0+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.
drivers/thermal/virtual_sensor.c line 128
Quoted 8 lines
drivers/thermal/virtual_sensor.c new file +8
-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)
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'?
Quoted 47 lines
drivers/thermal/virtual_sensor.c new file +47
-0+{ +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,
drivers/thermal/virtual_sensor.c line 185
Quoted 10 lines
drivers/thermal/virtual_sensor.c new file +10
-0+ &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'.
Quoted 13 lines
drivers/thermal/virtual_sensor.c new file +13
-0+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 ;
drivers/thermal/virtual_sensor.c line 208
Quoted 10 lines
drivers/thermal/virtual_sensor.c new file +10
-0+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.
Quoted 5 lines
drivers/thermal/virtual_sensor.c new file +5
-0+ +return 0 ; +} + +static int virtual_sensor_remove (struct platform_device *pdev)
drivers/thermal/virtual_sensor.c line 223
Quoted 10 lines
drivers/thermal/virtual_sensor.c new file +10
-0+{ +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?
Quoted 8 lines
drivers/thermal/virtual_sensor.c new file +8
-0+ +return 0 ; +} + +static const struct of_device_id virtual_sensor_of_match[] = { + { + .compatible ="virtual,thermal-sensor" , + },
drivers/thermal/virtual_sensor.c line 241
Quoted 10 lines
drivers/thermal/virtual_sensor.c new file +10
-0+ { + }, +}; +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.
drivers/thermal/virtual_sensor.c line 247
Quoted 6 lines
drivers/thermal/virtual_sensor.c new file +6
-0+ .of_match_table = virtual_sensor_of_match, + }, +}; + +/** +* thermal_virtual_sensor_register - registers a sensor that could by a virtual
s/by/be/
drivers/thermal/virtual_sensor.c line 252
Quoted 5 lines
drivers/thermal/virtual_sensor.c new file +5
-0+* 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/
Quoted 36 lines
drivers/thermal/virtual_sensor.c new file +36
-0+* @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
drivers/thermal/virtual_sensor.c line 298
Quoted 10 lines
drivers/thermal/virtual_sensor.c new file +10
-0+* @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'?
Quoted 5 lines
drivers/thermal/virtual_sensor.c new file +5
-0+struct virtual_sensor *sensor; +int i; + +list_del (&sensor_data->node); +
drivers/thermal/virtual_sensor.c line 313
Quoted 10 lines
drivers/thermal/virtual_sensor.c new file +10
-0+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?
Quoted 11 lines
drivers/thermal/virtual_sensor.c new file +11
-0+} +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)
drivers/thermal/virtual_sensor.c line 334
Quoted 10 lines
drivers/thermal/virtual_sensor.c new file +10
-0+{ +struct virtual_sensor_data **r = res; + +if (WARN_ON (!r || !*r)) +return 0 ; + +return *r == data; +} + +
delete one of the empty lines
drivers/thermal/virtual_sensor.c line 341
Quoted 7 lines
drivers/thermal/virtual_sensor.c new file +7
-0+/** +* 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/
drivers/thermal/virtual_sensor.c line 352
Quoted 11 lines
drivers/thermal/virtual_sensor.c new file +11
-0+* @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/
Re: [PATCH 2/2] thermal: add a virtual sensor to aggregate temperatures
Hi Matthias,
Quoted 59 lines · Matthias Kaehlcke
Quoted 2 lines · Alexandre Bailon
This adds a virtual thermal sensor that reads temperature from hardware sensor and return an aggregated temperature.
s/hardware sensor/hardware sensors/
Quoted 1 line
Currently, this only return the max temperature.
it seems this is outdated.
Quoted 15 lines
Signed-off-by Alexandre Bailon <[email protected]>--- 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.hcreate mode 100644drivers/thermal/virtual_sensor.cdrivers/thermal/Kconfig +0
-0drivers/thermal/Kconfig config THERMAL_MMIO · line 232
Quoted 5 lines
drivers/thermal/Kconfig +2
-0@@ -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.
Quoted 29 lines
drivers/thermal/Kconfig +6
-0+ 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_TESTdrivers/thermal/Makefile +1
-0@@ -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.odrivers/thermal/virtual-sensor.h new file +5
-0@@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* +* Copyright (c) 2021 BayLibre +*/ +
Quoted 8 lines
drivers/thermal/virtual-sensor.h line 10
Quoted 5 lines
drivers/thermal/virtual-sensor.h new file +5
-0+#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.
Quoted 177 lines
Quoted 58 lines
drivers/thermal/virtual-sensor.h new file +41
-0+ +#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__ */ drivers/thermal/virtual_sensor.c new file +10
-0@@ -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> drivers/thermal/virtual_sensor.c line 20
Quoted 10 lines
drivers/thermal/virtual_sensor.c new file +10
-0+#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?Quoted 90 lines
drivers/thermal/virtual_sensor.c new file +90
-0+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)drivers/thermal/virtual_sensor.c line 120
Quoted 10 lines
drivers/thermal/virtual_sensor.c new file +10
-0+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.
Quoted 3 lines
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.
Quoted 12 lines
drivers/thermal/virtual_sensor.c line 128
Quoted 9 lines
drivers/thermal/virtual_sensor.c new file +8
-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)Does this really register a virtual sensor? IIUC the registered sensor is (typically) an actual sensor, which is used by a virtual sensor.
Quoted 2 lines
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.
Quoted 79 lines
Quoted 48 lines
drivers/thermal/virtual_sensor.c new file +47
-0+{ +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,drivers/thermal/virtual_sensor.c line 185
Quoted 10 lines
drivers/thermal/virtual_sensor.c new file +10
-0+ &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'.
Quoted 18 lines
drivers/thermal/virtual_sensor.c new file +18
-0+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);
Quoted 8 lines
drivers/thermal/virtual_sensor.c line 208
Quoted 5 lines
drivers/thermal/virtual_sensor.c new file +5
-0+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.
Quoted 10 lines
Quoted 10 lines
drivers/thermal/virtual_sensor.c new file +9
-0+ +return 0 ; +} + +static int virtual_sensor_remove (struct platform_device *pdev) +{ +struct device *dev = &pdev->dev; +struct virtual_sensor *sensor; +
Quoted 8 lines
drivers/thermal/virtual_sensor.c line 223
Quoted 6 lines
drivers/thermal/virtual_sensor.c new file +6
-0+ 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.
Quoted 95 lines
Quoted 9 lines
drivers/thermal/virtual_sensor.c new file +8
-0+ +return 0 ; +} + +static const struct of_device_id virtual_sensor_of_match[] = { + { + .compatible ="virtual,thermal-sensor" , + },drivers/thermal/virtual_sensor.c line 241
Quoted 10 lines
drivers/thermal/virtual_sensor.c new file +10
-0+ { + }, +}; +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.
drivers/thermal/virtual_sensor.c line 247
Quoted 6 lines
drivers/thermal/virtual_sensor.c new file +6
-0+ .of_match_table = virtual_sensor_of_match, + }, +}; + +/** +* thermal_virtual_sensor_register - registers a sensor that could by a virtual s/by/be/
drivers/thermal/virtual_sensor.c line 252
Quoted 5 lines
drivers/thermal/virtual_sensor.c new file +5
-0+* 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/
Quoted 36 lines
drivers/thermal/virtual_sensor.c new file +36
-0+* @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 drivers/thermal/virtual_sensor.c line 298
Quoted 10 lines
drivers/thermal/virtual_sensor.c new file +10
-0+* @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'?
Quoted 9 lines
drivers/thermal/virtual_sensor.c new file +9
-0+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 &&
Quoted 8 lines
drivers/thermal/virtual_sensor.c line 313
Quoted 6 lines
drivers/thermal/virtual_sensor.c new file +6
-0+ 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
Quoted 45 lines
Quoted 12 lines
drivers/thermal/virtual_sensor.c new file +11
-0+} +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)drivers/thermal/virtual_sensor.c line 334
Quoted 10 lines
drivers/thermal/virtual_sensor.c new file +10
-0+{ +struct virtual_sensor_data **r = res; + +if (WARN_ON (!r || !*r)) +return 0 ; + +return *r == data; +} + +delete one of the empty lines
drivers/thermal/virtual_sensor.c line 341
Quoted 7 lines
drivers/thermal/virtual_sensor.c new file +7
-0+/** +* 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/
drivers/thermal/virtual_sensor.c line 352
Quoted 11 lines
drivers/thermal/virtual_sensor.c new file +11
-0+* @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/