diff mbox series

[1/5] thermal: of: properly parse coefficients with multiple thermal-sensors entries

Message ID 20241112052211.3087348-2-nico@fluxnic.net (mailing list archive)
State New
Headers show
Series thermal: multi-sensor aggregation support | expand

Commit Message

Nicolas Pitre Nov. 12, 2024, 5:19 a.m. UTC
From: Nicolas Pitre <npitre@baylibre.com>

The thermal zone DT binding description says:

      coefficients:
        $ref: /schemas/types.yaml#/definitions/uint32-array
        description:
          An array of integers containing the coefficients of a linear
          equation that binds all the sensors listed in this thermal zone.

          The linear equation used is as follows,
            z = c0 * x0 + c1 * x1 + ... + c(n-1) * x(n-1) + cn
          where c0, c1, .., cn are the coefficients.

          Coefficients default to 1 in case this property is not
          specified. The coefficients are ordered and are matched with
          sensors by means of the sensor ID. Additional coefficients are
          interpreted as constant offset.

And the code says:

        /*
         * For now, the thermal framework supports only one sensor per
         * thermal zone. Thus, we are considering only the first two
         * values as slope and offset.
         */

Furthermore, only bcm2711_thermal, bcm2835_thermal and ti-soc-thermal
use these values.

It is not clear how the equation in the bindings documentation should be
interpreted especially in the context of multiple sensors and sensor
aggregation. Assuming x0..xn are temperature values, coefficients have
to be fractional values otherwise the sum won't correspond to a
temperature anymore. So this patch interprets those coefficients as
per-sensor weight for determining the aggregated temperature value
instead. Also, in that context, constant offsets make no sense so they're
always set to 0. Because those weights are integer values, they must all be
provided otherwise they all default to 1.

To preserve backward compatibility, the current behavior is preserved
when "thermal-sensors" contains only one entry. The alternative
interpretation is applied only when "thermal-sensors" holds multiple
entries (which never happened so far).

Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
---
 drivers/thermal/thermal_of.c | 63 +++++++++++++++++++++++++++---------
 1 file changed, 47 insertions(+), 16 deletions(-)
diff mbox series

Patch

diff --git a/drivers/thermal/thermal_of.c b/drivers/thermal/thermal_of.c
index 07e0989716..b8ddd41247 100644
--- a/drivers/thermal/thermal_of.c
+++ b/drivers/thermal/thermal_of.c
@@ -136,7 +136,8 @@  static struct thermal_trip *thermal_of_trips_init(struct device_node *np, int *n
 	return ERR_PTR(ret);
 }
 
-static struct device_node *of_thermal_zone_find(struct device_node *sensor, int id)
+static struct device_node *of_thermal_zone_find(struct device_node *sensor,
+						int id, int *index)
 {
 	struct device_node *np, *tz;
 	struct of_phandle_args sensor_specs;
@@ -179,6 +180,8 @@  static struct device_node *of_thermal_zone_find(struct device_node *sensor, int
 			if ((sensor == sensor_specs.np) && id == (sensor_specs.args_count ?
 								  sensor_specs.args[0] : 0)) {
 				pr_debug("sensor %pOFn id=%d belongs to %pOFn\n", sensor, id, child);
+				/* return index only if multiple entries exist */
+				*index = (count > 1) ? i : -1;
 				tz = no_free_ptr(child);
 				goto out;
 			}
@@ -213,11 +216,10 @@  static int thermal_of_monitor_init(struct device_node *np, int *delay, int *pdel
 	return 0;
 }
 
-static void thermal_of_parameters_init(struct device_node *np,
+static void thermal_of_parameters_init(struct device_node *np, int index,
 				       struct thermal_zone_params *tzp)
 {
-	int coef[2];
-	int ncoef = ARRAY_SIZE(coef);
+	int ncoef, count;
 	int prop, ret;
 
 	tzp->no_hwmon = true;
@@ -226,18 +228,46 @@  static void thermal_of_parameters_init(struct device_node *np,
 		tzp->sustainable_power = prop;
 
 	/*
-	 * For now, the thermal framework supports only one sensor per
-	 * thermal zone. Thus, we are considering only the first two
-	 * values as slope and offset.
+	 * If only one sensor is specified in "thermal-sensors" (index == -1)
+	 * then only the first two "coefficients" values are considered, and
+	 * used as slope and offset (legacy interpretation).
+	 *
+	 * If /thermal-sensors" contains more than one sensor then index
+	 * contains a positive value indicating the "coefficients" value of
+	 * interest. The listed sensors are meant to be aggregated and the
+	 * provided coefficients represent the relative weight among those
+	 * sensors. The slope field is used for that purpose while the offset
+	 * is always 0.
 	 */
-	ret = of_property_read_u32_array(np, "coefficients", coef, ncoef);
-	if (ret) {
-		coef[0] = 1;
-		coef[1] = 0;
+	tzp->slope = 1;
+	tzp->offset = 0;
+	if (index == -1) {
+		int coef[2];
+
+		ncoef = ARRAY_SIZE(coef);
+		ret = of_property_read_u32_array(np, "coefficients", coef, ncoef);
+		if (!ret) {
+			tzp->slope = coef[0];
+			tzp->offset = coef[1];
+		}
+	} else {
+		ncoef = of_property_count_u32_elems(np, "coefficients");
+		if (ncoef > 0) {
+			count = of_count_phandle_with_args(np, "thermal-sensors",
+							   "#thermal-sensor-cells");
+			if (ncoef != count) {
+				pr_err("%pOFn: sensors and coefficients mismatch\n", np);
+			} else {
+				int *coef = kmalloc(sizeof(*coef) * (index + 1),
+						    GFP_KERNEL);
+				if (coef &&
+				    of_property_read_u32_array(np, "coefficients",
+							       coef, (index + 1)) == 0)
+					tzp->slope = coef[index];
+				kfree(coef);
+			}
+		}
 	}
-
-	tzp->slope = coef[0];
-	tzp->offset = coef[1];
 }
 
 static struct device_node *thermal_of_zone_get_by_name(struct thermal_zone_device *tz)
@@ -386,9 +416,10 @@  static struct thermal_zone_device *thermal_of_zone_register(struct device_node *
 	const char *action;
 	int delay, pdelay;
 	int ntrips;
+	int index;
 	int ret;
 
-	np = of_thermal_zone_find(sensor, id);
+	np = of_thermal_zone_find(sensor, id, &index);
 	if (IS_ERR(np)) {
 		if (PTR_ERR(np) != -ENODEV)
 			pr_err("Failed to find thermal zone for %pOFn id=%d\n", sensor, id);
@@ -411,7 +442,7 @@  static struct thermal_zone_device *thermal_of_zone_register(struct device_node *
 		goto out_kfree_trips;
 	}
 
-	thermal_of_parameters_init(np, &tzp);
+	thermal_of_parameters_init(np, index, &tzp);
 
 	of_ops.should_bind = thermal_of_should_bind;