[PATCH 5/5] hwmon: (cros_ec) Add support for temperature thresholds

From: Thomas Weißschuh
Date: Sat Jun 08 2024 - 04:14:04 EST


Implement reading and writing 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@xxxxxxxxxxxxxx>
---
Documentation/hwmon/cros_ec_hwmon.rst | 1 +
drivers/hwmon/cros_ec_hwmon.c | 82 +++++++++++++++++++++++++++++++++--
2 files changed, 80 insertions(+), 3 deletions(-)

diff --git a/Documentation/hwmon/cros_ec_hwmon.rst b/Documentation/hwmon/cros_ec_hwmon.rst
index 3cc345425aac..24ff261ae232 100644
--- a/Documentation/hwmon/cros_ec_hwmon.rst
+++ b/Documentation/hwmon/cros_ec_hwmon.rst
@@ -29,3 +29,4 @@ Supported features:
- Target fan speed (for fan 1 only)
- PWM-based fan control
- Current temperature
+ - Temperature thresholds
diff --git a/drivers/hwmon/cros_ec_hwmon.c b/drivers/hwmon/cros_ec_hwmon.c
index 5cddf78cfe0e..009b94af6df4 100644
--- a/drivers/hwmon/cros_ec_hwmon.c
+++ b/drivers/hwmon/cros_ec_hwmon.c
@@ -29,6 +29,7 @@ struct cros_ec_hwmon_priv {
const char *temp_sensor_names[EC_TEMP_SENSOR_ENTRIES + EC_TEMP_SENSOR_B_ENTRIES];
u8 usable_fans;
bool has_fan_pwm;
+ bool has_temp_threshold;
u8 fan_pwm[EC_FAN_SPEED_ENTRIES];
enum cros_ec_hwmon_fan_mode fan_mode[EC_FAN_SPEED_ENTRIES];
};
@@ -97,6 +98,42 @@ 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 int cros_ec_hwmon_write_temp_threshold(struct cros_ec_device *cros_ec, u8 index,
+ enum ec_temp_thresholds threshold, u32 temp)
+{
+ struct ec_params_thermal_get_threshold_v1 get_req = {};
+ struct ec_params_thermal_set_threshold_v1 set_req = {};
+ int ret;
+
+ get_req.sensor_num = index;
+ ret = cros_ec_cmd(cros_ec, 1, EC_CMD_THERMAL_GET_THRESHOLD,
+ &get_req, sizeof(get_req), &set_req.cfg, sizeof(set_req.cfg));
+ if (ret < 0)
+ return ret;
+
+ set_req.sensor_num = index;
+ set_req.cfg.temp_host[threshold] = temp;
+ return cros_ec_cmd(cros_ec, 1, EC_CMD_THERMAL_SET_THRESHOLD,
+ &set_req, sizeof(set_req), NULL, 0);
+}
+
static bool cros_ec_hwmon_is_error_fan(u16 speed)
{
return speed == EC_FAN_SPEED_NOT_PRESENT || speed == EC_FAN_SPEED_STALLED;
@@ -115,11 +152,24 @@ static long cros_ec_hwmon_temp_to_millicelsius(u8 temp)
return kelvin_to_millicelsius((((long)temp) + EC_TEMP_SENSOR_OFFSET));
}

+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;
+ else if (attr == hwmon_temp_emergency)
+ return EC_TEMP_THRESH_HALT;
+ else
+ return 0;
+}
+
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;
+ u32 threshold;
u16 speed;
u8 temp;

@@ -166,6 +216,14 @@ 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 (attr == hwmon_temp_max || attr == hwmon_temp_crit ||
+ attr == hwmon_temp_emergency) {
+ 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);
}
}

@@ -235,6 +293,10 @@ static int cros_ec_hwmon_write(struct device *dev, enum hwmon_sensor_types type,

else if (attr == hwmon_pwm_enable)
ret = cros_ec_hwmon_write_pwm_enable(priv, channel, val);
+ } else if (type == hwmon_temp) {
+ ret = cros_ec_hwmon_write_temp_threshold(priv->cros_ec, channel,
+ cros_ec_hwmon_attr_to_thres(attr),
+ millicelsius_to_kelvin(val));
}

return ret;
@@ -259,8 +321,16 @@ static umode_t cros_ec_hwmon_is_visible(const void *data, enum hwmon_sensor_type
return 0644;

} else if (type == hwmon_temp) {
- if (priv->temp_sensor_names[channel])
- return 0444;
+ if (priv->temp_sensor_names[channel]) {
+ if (attr == hwmon_temp_max ||
+ attr == hwmon_temp_crit ||
+ attr == hwmon_temp_emergency) {
+ if (priv->has_temp_threshold)
+ return 0644;
+ } else {
+ return 0444;
+ }
+ }
}

return 0;
@@ -278,7 +348,8 @@ static const struct hwmon_channel_info * const cros_ec_hwmon_info[] = {
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,
@@ -325,9 +396,14 @@ static void cros_ec_hwmon_probe_temp_sensors(struct device *dev, struct cros_ec_
struct ec_params_temp_sensor_get_info req = {};
struct ec_response_temp_sensor_get_info resp;
size_t candidates, i, sensor_name_size;
+ u32 threshold;
int ret;
u8 temp;

+ ret = cros_ec_hwmon_read_temp_threshold(priv->cros_ec, 0, EC_TEMP_THRESH_HIGH, &threshold);
+ if (ret == 0)
+ priv->has_temp_threshold = 1;
+
if (thermal_version < 2)
candidates = EC_TEMP_SENSOR_ENTRIES;
else

--
2.45.2