]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
hwmon: (cros_ec) Add support for temperature thresholds
authorThomas Weißschuh <linux@weissschuh.net>
Sun, 18 Jan 2026 09:45:58 +0000 (10:45 +0100)
committerGuenter Roeck <linux@roeck-us.net>
Sat, 7 Feb 2026 17:32:20 +0000 (09:32 -0800)
Implement reading temperature thresholds through
EC_CMD_THERMAL_GET_THRESHOLD/EC_CMD_THERMAL_SET_THRESHOLD.

Thresholds are mapped as follows between the EC and hwmon:

hwmon_temp_max       - EC_TEMP_THRESH_WARN
hwmon_temp_crit      - EC_TEMP_THRESH_HIGH
hwmon_temp_emergency - EC_TEMP_THRESH_HALT

Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
Reviewed-by: Tzung-Bi Shih <tzungbi@kernel.org>
Link: https://lore.kernel.org/r/20260118-cros_ec-hwmon-pwm-v2-4-77eb1709b031@weissschuh.net
[groeck: Rearrange code to no longer use unreachable() since that causes
 a hiccup with some versions of gcc and objtool]
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Documentation/hwmon/cros_ec_hwmon.rst
drivers/hwmon/cros_ec_hwmon.c

index ebc8da48fa8a2aa3a8e1449f82f5c199bd88e6dd..9ccab721e7c221b0bf2ba064e5177322bef03712 100644 (file)
@@ -35,6 +35,9 @@ Fan target speed
 Temperature readings
     Always supported.
 
+Temperature thresholds
+    If supported by the EC.
+
 PWM fan control
     If the EC also supports setting fan PWM values and fan mode.
 
index f5be293fdaa6637b5790670fc39ab9f9a3688c8b..6cf5ab0f4b738027972657ec5c80cb487eb28b64 100644 (file)
@@ -28,6 +28,7 @@ struct cros_ec_hwmon_priv {
        const char *temp_sensor_names[EC_TEMP_SENSOR_ENTRIES + EC_TEMP_SENSOR_B_ENTRIES];
        u8 usable_fans;
        bool fan_control_supported;
+       bool temp_threshold_supported;
        u8 manual_fans; /* bits to indicate whether the fan is set to manual */
        u8 manual_fan_pwm[EC_FAN_SPEED_ENTRIES];
 };
@@ -116,6 +117,23 @@ static int cros_ec_hwmon_read_temp(struct cros_ec_device *cros_ec, u8 index, u8
        return 0;
 }
 
+static int cros_ec_hwmon_read_temp_threshold(struct cros_ec_device *cros_ec, u8 index,
+                                            enum ec_temp_thresholds threshold, u32 *temp)
+{
+       struct ec_params_thermal_get_threshold_v1 req = {};
+       struct ec_thermal_config resp;
+       int ret;
+
+       req.sensor_num = index;
+       ret = cros_ec_cmd(cros_ec, 1, EC_CMD_THERMAL_GET_THRESHOLD,
+                         &req, sizeof(req), &resp, sizeof(resp));
+       if (ret < 0)
+               return ret;
+
+       *temp = resp.temp_host[threshold];
+       return 0;
+}
+
 static bool cros_ec_hwmon_is_error_fan(u16 speed)
 {
        return speed == EC_FAN_SPEED_NOT_PRESENT || speed == EC_FAN_SPEED_STALLED;
@@ -134,12 +152,29 @@ static long cros_ec_hwmon_temp_to_millicelsius(u8 temp)
        return kelvin_to_millicelsius((((long)temp) + EC_TEMP_SENSOR_OFFSET));
 }
 
+static bool cros_ec_hwmon_attr_is_temp_threshold(u32 attr)
+{
+       return attr == hwmon_temp_max ||
+              attr == hwmon_temp_crit ||
+              attr == hwmon_temp_emergency;
+}
+
+static enum ec_temp_thresholds cros_ec_hwmon_attr_to_thres(u32 attr)
+{
+       if (attr == hwmon_temp_max)
+               return EC_TEMP_THRESH_WARN;
+       else if (attr == hwmon_temp_crit)
+               return EC_TEMP_THRESH_HIGH;
+       return EC_TEMP_THRESH_HALT;     /* attr == hwmon_temp_emergency */
+}
+
 static int cros_ec_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
                              u32 attr, int channel, long *val)
 {
        struct cros_ec_hwmon_priv *priv = dev_get_drvdata(dev);
        int ret = -EOPNOTSUPP;
        u8 control_method;
+       u32 threshold;
        u8 pwm_value;
        u16 speed;
        u8 temp;
@@ -187,6 +222,13 @@ static int cros_ec_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
                        ret = cros_ec_hwmon_read_temp(priv->cros_ec, channel, &temp);
                        if (ret == 0)
                                *val = cros_ec_hwmon_is_error_temp(temp);
+
+               } else if (cros_ec_hwmon_attr_is_temp_threshold(attr)) {
+                       ret = cros_ec_hwmon_read_temp_threshold(priv->cros_ec, channel,
+                                                               cros_ec_hwmon_attr_to_thres(attr),
+                                                               &threshold);
+                       if (ret == 0)
+                               *val = kelvin_to_millicelsius(threshold);
                }
        }
 
@@ -291,8 +333,14 @@ static umode_t cros_ec_hwmon_is_visible(const void *data, enum hwmon_sensor_type
                if (priv->fan_control_supported && priv->usable_fans & BIT(channel))
                        return 0644;
        } else if (type == hwmon_temp) {
-               if (priv->temp_sensor_names[channel])
-                       return 0444;
+               if (priv->temp_sensor_names[channel]) {
+                       if (cros_ec_hwmon_attr_is_temp_threshold(attr)) {
+                               if (priv->temp_threshold_supported)
+                                       return 0444;
+                       } else {
+                               return 0444;
+                       }
+               }
        }
 
        return 0;
@@ -310,7 +358,8 @@ static const struct hwmon_channel_info * const cros_ec_hwmon_info[] = {
                           HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
                           HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
                           HWMON_PWM_INPUT | HWMON_PWM_ENABLE),
-#define CROS_EC_HWMON_TEMP_PARAMS (HWMON_T_INPUT | HWMON_T_FAULT | HWMON_T_LABEL)
+#define CROS_EC_HWMON_TEMP_PARAMS (HWMON_T_INPUT | HWMON_T_FAULT | HWMON_T_LABEL | \
+                                  HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_EMERGENCY)
        HWMON_CHANNEL_INFO(temp,
                           CROS_EC_HWMON_TEMP_PARAMS,
                           CROS_EC_HWMON_TEMP_PARAMS,
@@ -520,6 +569,8 @@ static int cros_ec_hwmon_probe(struct platform_device *pdev)
        cros_ec_hwmon_probe_temp_sensors(dev, priv, thermal_version);
        cros_ec_hwmon_probe_fans(priv);
        priv->fan_control_supported = cros_ec_hwmon_probe_fan_control_supported(priv->cros_ec);
+       priv->temp_threshold_supported = is_cros_ec_cmd_available(priv->cros_ec,
+                                                                 EC_CMD_THERMAL_GET_THRESHOLD, 1);
        cros_ec_hwmon_register_fan_cooling_devices(dev, priv);
 
        hwmon_dev = devm_hwmon_device_register_with_info(dev, "cros_ec", priv,