summaryrefslogtreecommitdiff
path: root/drivers/hwmon
diff options
context:
space:
mode:
authorSung-Chi Li <lschyi@chromium.org>2025-09-11 06:56:36 +0000
committerGuenter Roeck <linux@roeck-us.net>2025-09-25 08:08:14 -0700
commit5798b62867b47b6ace287d31172ce748ad70d869 (patch)
treeebc1cb6eb9fc53ef65d4c6b24101a5939204c565 /drivers/hwmon
parentfb8e659309f72e54ed011c6bfe98597b9236805d (diff)
downloadlinux-5798b62867b47b6ace287d31172ce748ad70d869.tar.gz
linux-5798b62867b47b6ace287d31172ce748ad70d869.tar.bz2
linux-5798b62867b47b6ace287d31172ce748ad70d869.zip
hwmon: (cros_ec) register fans into thermal framework cooling devices
Register fans connected under EC as thermal cooling devices as well, so these fans can then work with the thermal framework. During the driver probing phase, we will also try to register each fan as a thermal cooling device based on previous probe result (whether the there are fans connected on that channel, and whether EC supports fan control). The basic get max state, get current state, and set current state methods are then implemented as well. Signed-off-by: Sung-Chi Li <lschyi@chromium.org> Acked-by: Thomas Weißschuh <linux@weissschuh.net> Link: https://lore.kernel.org/r/20250911-cros_ec_fan-v6-3-a1446cc098af@google.com Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Diffstat (limited to 'drivers/hwmon')
-rw-r--r--drivers/hwmon/cros_ec_hwmon.c83
1 files changed, 83 insertions, 0 deletions
diff --git a/drivers/hwmon/cros_ec_hwmon.c b/drivers/hwmon/cros_ec_hwmon.c
index 9eddc554ddef..48331703f2f5 100644
--- a/drivers/hwmon/cros_ec_hwmon.c
+++ b/drivers/hwmon/cros_ec_hwmon.c
@@ -13,6 +13,7 @@
#include <linux/platform_device.h>
#include <linux/platform_data/cros_ec_commands.h>
#include <linux/platform_data/cros_ec_proto.h>
+#include <linux/thermal.h>
#include <linux/types.h>
#include <linux/units.h>
@@ -31,6 +32,11 @@ struct cros_ec_hwmon_priv {
u8 manual_fan_pwm[EC_FAN_SPEED_ENTRIES];
};
+struct cros_ec_hwmon_cooling_priv {
+ struct cros_ec_hwmon_priv *hwmon_priv;
+ u8 index;
+};
+
static int cros_ec_hwmon_read_fan_speed(struct cros_ec_device *cros_ec, u8 index, u16 *speed)
{
int ret;
@@ -308,6 +314,42 @@ static const struct hwmon_channel_info * const cros_ec_hwmon_info[] = {
NULL
};
+static int cros_ec_hwmon_cooling_get_max_state(struct thermal_cooling_device *cdev,
+ unsigned long *val)
+{
+ *val = 255;
+ return 0;
+}
+
+static int cros_ec_hwmon_cooling_get_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long *val)
+{
+ const struct cros_ec_hwmon_cooling_priv *priv = cdev->devdata;
+ u8 read_val;
+ int ret;
+
+ ret = cros_ec_hwmon_read_pwm_value(priv->hwmon_priv->cros_ec, priv->index, &read_val);
+ if (ret)
+ return ret;
+
+ *val = read_val;
+ return 0;
+}
+
+static int cros_ec_hwmon_cooling_set_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long val)
+{
+ const struct cros_ec_hwmon_cooling_priv *priv = cdev->devdata;
+
+ return cros_ec_hwmon_write_pwm_input(priv->hwmon_priv->cros_ec, priv->index, val);
+}
+
+static const struct thermal_cooling_device_ops cros_ec_thermal_cooling_ops = {
+ .get_max_state = cros_ec_hwmon_cooling_get_max_state,
+ .get_cur_state = cros_ec_hwmon_cooling_get_cur_state,
+ .set_cur_state = cros_ec_hwmon_cooling_set_cur_state,
+};
+
static const struct hwmon_ops cros_ec_hwmon_ops = {
.read = cros_ec_hwmon_read,
.read_string = cros_ec_hwmon_read_string,
@@ -386,6 +428,46 @@ static bool cros_ec_hwmon_probe_fan_control_supported(struct cros_ec_device *cro
CROS_EC_HWMON_THERMAL_AUTO_FAN_CTRL_CMD_VERSION);
}
+static void cros_ec_hwmon_register_fan_cooling_devices(struct device *dev,
+ struct cros_ec_hwmon_priv *priv)
+{
+ struct cros_ec_hwmon_cooling_priv *cpriv;
+ struct thermal_cooling_device *cdev;
+ const char *type;
+ size_t i;
+
+ if (!IS_ENABLED(CONFIG_THERMAL))
+ return;
+
+ if (!priv->fan_control_supported)
+ return;
+
+ for (i = 0; i < EC_FAN_SPEED_ENTRIES; i++) {
+ if (!(priv->usable_fans & BIT(i)))
+ continue;
+
+ cpriv = devm_kzalloc(dev, sizeof(*cpriv), GFP_KERNEL);
+ if (!cpriv)
+ continue;
+
+ type = devm_kasprintf(dev, GFP_KERNEL, "%s-fan%zu", dev_name(dev), i);
+ if (!type) {
+ dev_warn(dev, "no memory to compose cooling device type for fan %zu\n", i);
+ continue;
+ }
+
+ cpriv->hwmon_priv = priv;
+ cpriv->index = i;
+ cdev = devm_thermal_of_cooling_device_register(dev, NULL, type, cpriv,
+ &cros_ec_thermal_cooling_ops);
+ if (IS_ERR(cdev)) {
+ dev_warn(dev, "failed to register fan %zu as a cooling device: %pe\n", i,
+ cdev);
+ continue;
+ }
+ }
+}
+
static int cros_ec_hwmon_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -413,6 +495,7 @@ 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);
+ cros_ec_hwmon_register_fan_cooling_devices(dev, priv);
hwmon_dev = devm_hwmon_device_register_with_info(dev, "cros_ec", priv,
&cros_ec_hwmon_chip_info, NULL);