hwmon: (cros_ec) Add support for temperature thresholds

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>
This commit is contained in:
Thomas Weißschuh 2026-01-18 10:45:58 +01:00 committed by Guenter Roeck
parent 11c5802d28
commit afa7c56ec4
2 changed files with 57 additions and 3 deletions

View 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.

View 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,