From nobody Wed Dec 17 12:25:56 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A7BE7248868; Thu, 19 Jun 2025 07:42:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750318977; cv=none; b=b1hbZueK0BW6DteIijaTwQNEHhD77YAkf3rxq7/hNHmR/qTqW1L+Gcd84yMivp1+rwrJWmp2QqCrzRQWNJEUeYpj051l4vXeuswqtwJ+h/A/c8KMToTA9CaSLSDJc26lNGAtaGnw395dPtzDT/BxZeNn7Gvp7V+NHO8w65p7Rag= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750318977; c=relaxed/simple; bh=RikCrKLnCHgGPRI5B8T21KzfkjhIAbjsuwYMRU52dRA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=WwA2Ed+Ctex/PMAVySb+0HwTjQiCoCV47JGdYxRGcQnP2zJmYes+HP7aW4HpdnWnkD1LOaWGyI81OuxoMM0/I8f+lKyRWbkT67/54OS1sfkgJSK6HFVDabPggjtl5AI+B8HJH0c7VhsbViWKrq4RD0SrJeLorsN3SAJhrrVbKbs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=FMNFZ1nU; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="FMNFZ1nU" Received: by smtp.kernel.org (Postfix) with ESMTPS id 5505DC4CEF0; Thu, 19 Jun 2025 07:42:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1750318977; bh=RikCrKLnCHgGPRI5B8T21KzfkjhIAbjsuwYMRU52dRA=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=FMNFZ1nUawzVMsnxuAV8ZvSLCdcu6V/8dqwWDMNXcZ8307//KT+H9Jh96rABj7OKA Ysd5TiqF4xmP2sxZdMC38/ZG5x4lqrjStLb+h+3svSfJXCtcuWcLu0awC6v7EfI2JD m0oUHZL0zskinZOrW1roH4IBkcB85Dn0EPHgu93HsJan7fVMlC4DfHACHaJdGgZSq0 MYy/+96SYmgKOuTlxa1dXL7pJeykeLxw0CHYUPC4WUOjjc1fTBBi/oiq1CpnV3ekH7 ydwx5Jslr00WDLHFDWgz9OAX08/1YgTzY7RiKomz76eWThraxI6O81aPnZCRt8sXtf RT7MpY5upztrw== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 46D00C761AE; Thu, 19 Jun 2025 07:42:57 +0000 (UTC) From: Sung-Chi Li via B4 Relay Date: Thu, 19 Jun 2025 15:42:56 +0800 Subject: [PATCH v4 3/3] hwmon: (cros_ec) register fans into thermal framework cooling devices Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20250619-cros_ec_fan-v4-3-ca11548ce449@chromium.org> References: <20250619-cros_ec_fan-v4-0-ca11548ce449@chromium.org> In-Reply-To: <20250619-cros_ec_fan-v4-0-ca11548ce449@chromium.org> To: Benson Leung , Guenter Roeck , =?utf-8?q?Thomas_Wei=C3=9Fschuh?= , Jean Delvare , Guenter Roeck , Jonathan Corbet , =?utf-8?q?Thomas_Wei=C3=9Fschuh?= Cc: chrome-platform@lists.linux.dev, linux-kernel@vger.kernel.org, linux-hwmon@vger.kernel.org, linux-doc@vger.kernel.org, Sung-Chi Li , Sung-Chi Li X-Mailer: b4 0.14.2 X-Developer-Signature: v=1; a=ed25519-sha256; t=1750318976; l=5521; i=lschyi@chromium.org; s=20250429; h=from:subject:message-id; bh=dGrNQjFZmAqqQuLX31fi6rvA+UA6e5udimOyMjEIouo=; b=0dwadgCMsVUj2zNX3LAZTyaW6PXKOphWxSH6/DPNS5uCEwqeWx63ZWVIgvgYoHCvDsHjphEi1 L4uaCbewRHCA9AiGKf2p1+FNA5FaKMC2pN+rrjROQ6BHBtWACsiXCfq X-Developer-Key: i=lschyi@chromium.org; a=ed25519; pk=9gCZPRJmYyHDt6VN9FV2UreFcUr73JFrwYvmsltW9Y8= X-Endpoint-Received: by B4 Relay for lschyi@chromium.org/20250429 with auth_id=392 X-Original-From: Sung-Chi Li Reply-To: lschyi@chromium.org From: Sung-Chi Li 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 Acked-by: Thomas Wei=C3=9Fschuh --- Documentation/hwmon/cros_ec_hwmon.rst | 2 + drivers/hwmon/cros_ec_hwmon.c | 85 +++++++++++++++++++++++++++++++= ++++ 2 files changed, 87 insertions(+) diff --git a/Documentation/hwmon/cros_ec_hwmon.rst b/Documentation/hwmon/cr= os_ec_hwmon.rst index 355557a08c9a54b4c177bafde3743e7dc02218be..6db812708325f7abb6d319af331= 2b4079e6923c6 100644 --- a/Documentation/hwmon/cros_ec_hwmon.rst +++ b/Documentation/hwmon/cros_ec_hwmon.rst @@ -27,3 +27,5 @@ Fan and temperature readings are supported. PWM fan contr= ol is also supported if the EC also supports setting fan PWM values and fan mode. Note that EC will switch fan control mode back to auto when suspended. This driver will rest= ore the fan state to what they were before suspended when resumed. +If a fan is controllable, this driver will register that fan as a cooling = device +in the thermal framework as well. diff --git a/drivers/hwmon/cros_ec_hwmon.c b/drivers/hwmon/cros_ec_hwmon.c index c0c4ffa6f85419edf5dacd730192e7f1c28e2e5c..572b194e51fb1a05fa5151abb08= 26d1c8ba2948d 100644 --- a/drivers/hwmon/cros_ec_hwmon.c +++ b/drivers/hwmon/cros_ec_hwmon.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include =20 @@ -31,6 +32,11 @@ struct cros_ec_hwmon_priv { u8 manual_fan_pwm[EC_FAN_SPEED_ENTRIES]; }; =20 +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; @@ -306,6 +312,42 @@ static const struct hwmon_channel_info * const cros_ec= _hwmon_info[] =3D { NULL }; =20 +static int cros_ec_hwmon_cooling_get_max_state(struct thermal_cooling_devi= ce *cdev, + unsigned long *val) +{ + *val =3D 255; + return 0; +} + +static int cros_ec_hwmon_cooling_get_cur_state(struct thermal_cooling_devi= ce *cdev, + unsigned long *val) +{ + const struct cros_ec_hwmon_cooling_priv *priv =3D cdev->devdata; + u8 read_val; + int ret; + + ret =3D cros_ec_hwmon_read_pwm_value(priv->hwmon_priv->cros_ec, priv->ind= ex, &read_val); + if (ret) + return ret; + + *val =3D read_val; + return ret; +} + +static int cros_ec_hwmon_cooling_set_cur_state(struct thermal_cooling_devi= ce *cdev, + unsigned long val) +{ + const struct cros_ec_hwmon_cooling_priv *priv =3D cdev->devdata; + + return cros_ec_hwmon_write_pwm_input(priv->hwmon_priv->cros_ec, priv->ind= ex, val); +} + +static const struct thermal_cooling_device_ops cros_ec_thermal_cooling_ops= =3D { + .get_max_state =3D cros_ec_hwmon_cooling_get_max_state, + .get_cur_state =3D cros_ec_hwmon_cooling_get_cur_state, + .set_cur_state =3D cros_ec_hwmon_cooling_set_cur_state, +}; + static const struct hwmon_ops cros_ec_hwmon_ops =3D { .read =3D cros_ec_hwmon_read, .read_string =3D cros_ec_hwmon_read_string, @@ -381,6 +423,48 @@ static bool cros_ec_hwmon_probe_fan_control_supported(= struct cros_ec_device *cro is_cros_ec_cmd_available(cros_ec, EC_CMD_THERMAL_AUTO_FAN_CTRL, CR= OS_EC_HWMON_THERMAL_AUTO_FAN_CTRL_CMD_VERSION); } =20 +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; + char *type; + size_t i; + + if (!IS_ENABLED(CONFIG_THERMAL)) + return; + + if (!priv->fan_control_supported) + return; + + for (i =3D 0; i < EC_FAN_SPEED_ENTRIES; i++) { + if (!(priv->usable_fans & BIT(i))) + continue; + + cpriv =3D devm_kzalloc(dev, sizeof(*cpriv), GFP_KERNEL); + if (!cpriv) { + dev_warn(dev, "no memory for registering fan %zu as a cooling device\n"= , i); + continue; + } + + type =3D 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 =3D priv; + cpriv->index =3D i; + cdev =3D 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 =3D &pdev->dev; @@ -408,6 +492,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 =3D cros_ec_hwmon_probe_fan_control_supported= (priv->cros_ec); + cros_ec_hwmon_register_fan_cooling_devices(dev, priv); =20 hwmon_dev =3D devm_hwmon_device_register_with_info(dev, "cros_ec", priv, &cros_ec_hwmon_chip_info, NULL); --=20 2.50.0.rc2.701.gf1e915cc24-goog