From: Aditya Dash <mradityadash@gmail.com>
To: "Derek J. Clark" <derekjohn.clark@gmail.com>,
"Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>,
"Armin Wolf" <W_Armin@gmx.de>, "Hans de Goede" <hansg@kernel.org>,
"Mark Pearson" <mpearson-lenovo@squebb.ca>
Cc: Guenter Roeck <linux@roeck-us.net>,
Jonathan Corbet <corbet@lwn.net>,
Shuah Khan <skhan@linuxfoundation.org>,
linux-doc@vger.kernel.org, linux-hwmon@vger.kernel.org,
linux-kernel@vger.kernel.org,
platform-driver-x86@vger.kernel.org
Subject: [RFC PATCH 1/3] platform/x86: lenovo-wmi-other: Add Legion Go Full Speed control
Date: Sat, 22 Aug 2026 03:17:25 +0530 [thread overview]
Message-ID: <20260821214728.87773-2-mradityadash@gmail.com> (raw)
In-Reply-To: <20260821214728.87773-1-mradityadash@gmail.com>
Selected Legion Go firmware exposes Full Speed as Other Mode feature
0x04020000. Capability Data does not describe this feature, so
lenovo-wmi-other currently ignores it.
Probe the feature during HWMON setup on those products. If the read
succeeds and returns a Boolean value, expose it as pwm1_enable. Value 0
selects Full Speed, and value 2 returns fan control to firmware automatic
mode. Reject all other values.
Assisted-by: Pi:gpt-5.6-sol
Signed-off-by: Aditya Dash <mradityadash@gmail.com>
---
.../wmi/devices/lenovo-wmi-other.rst | 7 ++
drivers/platform/x86/lenovo/wmi-other.c | 108 +++++++++++++++++-
2 files changed, 114 insertions(+), 1 deletion(-)
diff --git a/Documentation/wmi/devices/lenovo-wmi-other.rst b/Documentation/wmi/devices/lenovo-wmi-other.rst
index 011054d64eac..75f2deaaef16 100644
--- a/Documentation/wmi/devices/lenovo-wmi-other.rst
+++ b/Documentation/wmi/devices/lenovo-wmi-other.rst
@@ -49,6 +49,13 @@ The following HWMON attributes are implemented:
Due to the internal RPM divisor, the current/target RPMs are rounded down to
its nearest multiple. The divisor itself is not necessary to be a power of two.
+Legion Go fan controls
+----------------------
+
+On supported Legion Go models, Other Mode feature ``0x04020000`` controls
+Full Speed mode in firmware. The driver exposes it as ``pwm1_enable``. Value 0
+enables Full Speed, and value 2 returns fan control to firmware.
+
LENOVO_CAPABILITY_DATA_01
-------------------------
diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
index fbb32bf404f2..c180933e1d18 100644
--- a/drivers/platform/x86/lenovo/wmi-other.c
+++ b/drivers/platform/x86/lenovo/wmi-other.c
@@ -32,6 +32,7 @@
#include <linux/component.h>
#include <linux/container_of.h>
#include <linux/device.h>
+#include <linux/dmi.h>
#include <linux/export.h>
#include <linux/gfp_types.h>
#include <linux/hwmon.h>
@@ -83,6 +84,7 @@ enum lwmi_feature_id_psu {
LWMI_FEATURE_ID_PSU_CHARGE_BEHAVIOUR = 0x02,
};
+#define LWMI_FEATURE_ID_FAN_FULLSPEED 0x02
#define LWMI_FEATURE_ID_FAN_RPM 0x03
#define LWMI_TYPE_ID_CROSSLOAD 0x01
@@ -102,6 +104,10 @@ enum lwmi_feature_id_psu {
#define LWMI_CHARGE_TYPE_STANDARD 0x00
#define LWMI_CHARGE_TYPE_LONGLIFE 0x01
+#define LWMI_ATTR_ID_FAN_FULLSPEED \
+ lwmi_attr_id(LWMI_DEVICE_ID_FAN, LWMI_FEATURE_ID_FAN_FULLSPEED, \
+ LWMI_GZ_THERMAL_MODE_NONE, LWMI_TYPE_ID_NONE)
+
#define LWMI_ATTR_ID_FAN_RPM(x) \
lwmi_attr_id(LWMI_DEVICE_ID_FAN, LWMI_FEATURE_ID_FAN_RPM, \
LWMI_GZ_THERMAL_MODE_NONE, LWMI_FAN_ID(x))
@@ -115,6 +121,50 @@ enum lwmi_feature_id_psu {
static DEFINE_IDA(lwmi_om_ida);
+static const struct dmi_system_id lwmi_fan_dmi_table[] = {
+ {
+ .ident = "Lenovo Legion Go 8APU1",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Legion Go 8APU1"),
+ },
+ },
+ {
+ .ident = "Lenovo Legion Go S 8APU1",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Legion Go S 8APU1"),
+ },
+ },
+ {
+ .ident = "Lenovo Legion Go S 8ARP1",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Legion Go S 8ARP1"),
+ },
+ },
+ {
+ .ident = "Lenovo Legion Go 8ASP2",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Legion Go 8ASP2"),
+ },
+ },
+ {
+ .ident = "Lenovo Legion Go 8AHP2",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Legion Go 8AHP2"),
+ },
+ },
+ {}
+};
+
+static bool lwmi_fan_supported(void)
+{
+ return dmi_check_system(lwmi_fan_dmi_table);
+}
+
enum attribute_property {
DEFAULT_VAL,
MAX_VAL,
@@ -144,6 +194,7 @@ struct lwmi_om_priv {
int ida_id;
struct lwmi_fan_info fan_info[LWMI_FAN_NR];
+ bool fullspeed_supported;
struct {
bool capdata00_collected : 1;
@@ -237,6 +288,38 @@ static int lwmi_om_fan_get_set(struct lwmi_om_priv *priv, int channel, u32 *val,
return (retval == 0 || retval == 1) ? 0 : -EIO;
}
+static int lwmi_om_fullspeed_get(struct lwmi_om_priv *priv, long *enable)
+{
+ struct wmi_method_args_32 args = {
+ .arg0 = LWMI_ATTR_ID_FAN_FULLSPEED,
+ };
+ u32 value;
+ int ret;
+
+ ret = lwmi_dev_evaluate_int(priv->wdev, 0, LWMI_FEATURE_VALUE_GET,
+ (u8 *)&args, sizeof(args), &value);
+ if (ret)
+ return ret;
+
+ if (value > 1)
+ return -ERANGE;
+
+ *enable = value ? 0 : 2;
+ return 0;
+}
+
+static int lwmi_om_fullspeed_set(struct lwmi_om_priv *priv, bool fullspeed)
+{
+ struct wmi_method_args_32 args = {
+ .arg0 = LWMI_ATTR_ID_FAN_FULLSPEED,
+ .arg1 = fullspeed,
+ };
+
+ /* The WMI method has no return value. */
+ return lwmi_dev_evaluate_int(priv->wdev, 0, LWMI_FEATURE_VALUE_SET,
+ (u8 *)&args, sizeof(args), NULL);
+}
+
/**
* lwmi_om_hwmon_is_visible() - Determine visibility of HWMON attributes
* @drvdata: Driver private data
@@ -255,6 +338,10 @@ static umode_t lwmi_om_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_t
struct lwmi_om_priv *priv = (struct lwmi_om_priv *)drvdata;
bool visible = false;
+ if (type == hwmon_pwm && priv->fullspeed_supported && channel == 0 &&
+ attr == hwmon_pwm_enable)
+ return 0644;
+
if (type == hwmon_fan) {
if (!(priv->fan_info[channel].supported & LWMI_SUPP_VALID))
return 0;
@@ -311,6 +398,9 @@ static int lwmi_om_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
u32 retval = 0;
int err;
+ if (type == hwmon_pwm && attr == hwmon_pwm_enable && channel == 0)
+ return lwmi_om_fullspeed_get(priv, val);
+
if (type == hwmon_fan) {
switch (attr) {
/*
@@ -366,6 +456,17 @@ static int lwmi_om_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
u32 raw, min_rpm, max_rpm;
int err;
+ if (type == hwmon_pwm && attr == hwmon_pwm_enable && channel == 0) {
+ switch (val) {
+ case 0:
+ return lwmi_om_fullspeed_set(priv, true);
+ case 2:
+ return lwmi_om_fullspeed_set(priv, false);
+ default:
+ return -EINVAL;
+ }
+ }
+
if (type == hwmon_fan) {
switch (attr) {
case hwmon_fan_target:
@@ -420,6 +521,7 @@ static const struct hwmon_channel_info * const lwmi_om_hwmon_info[] = {
HWMON_F_MIN | HWMON_F_MAX,
HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_DIV |
HWMON_F_MIN | HWMON_F_MAX),
+ HWMON_CHANNEL_INFO(pwm, HWMON_PWM_ENABLE),
NULL
};
@@ -440,6 +542,7 @@ static const struct hwmon_chip_info lwmi_om_hwmon_chip_info = {
*/
static void lwmi_om_hwmon_add(struct lwmi_om_priv *priv)
{
+ long enable;
int i, valid;
if (WARN_ON(priv->hwmon_dev))
@@ -458,6 +561,9 @@ static void lwmi_om_hwmon_add(struct lwmi_om_priv *priv)
if (relax_fan_constraint)
dev_warn(&priv->wdev->dev, "fan RPM constraint relaxed. Use with caution\n");
+ priv->fullspeed_supported =
+ lwmi_fan_supported() && !lwmi_om_fullspeed_get(priv, &enable);
+
valid = 0;
for (i = 0; i < LWMI_FAN_NR; i++) {
if (!(priv->fan_info[i].supported & LWMI_SUPP_VALID))
@@ -474,7 +580,7 @@ static void lwmi_om_hwmon_add(struct lwmi_om_priv *priv)
}
}
- if (valid == 0) {
+ if (valid == 0 && !priv->fullspeed_supported) {
dev_warn(&priv->wdev->dev,
"fan reporting/tuning is unsupported on this device\n");
return;
--
2.55.0
next prev parent reply other threads:[~2026-08-21 21:47 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-21 21:47 [RFC PATCH 0/3] platform/x86: lenovo: Add Legion Go fan controls Aditya Dash
2026-08-21 21:47 ` Aditya Dash [this message]
2026-08-21 21:55 ` [RFC PATCH 1/3] platform/x86: lenovo-wmi-other: Add Legion Go Full Speed control sashiko-bot
2026-08-22 18:55 ` Antheas Kapenekakis
2026-08-21 21:47 ` [RFC PATCH 2/3] platform/x86: lenovo-wmi-other: Add Legion Go fan RPM fallback Aditya Dash
2026-08-21 21:55 ` sashiko-bot
2026-08-22 18:50 ` Antheas Kapenekakis
2026-08-21 21:47 ` [RFC PATCH 3/3] platform/x86: lenovo: Add Legion Go Fan Method curve driver Aditya Dash
2026-08-21 21:54 ` sashiko-bot
2026-08-22 18:47 ` Antheas Kapenekakis
2026-08-22 18:33 ` [RFC PATCH 0/3] platform/x86: lenovo: Add Legion Go fan controls Antheas Kapenekakis
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260821214728.87773-2-mradityadash@gmail.com \
--to=mradityadash@gmail.com \
--cc=W_Armin@gmx.de \
--cc=corbet@lwn.net \
--cc=derekjohn.clark@gmail.com \
--cc=hansg@kernel.org \
--cc=ilpo.jarvinen@linux.intel.com \
--cc=linux-doc@vger.kernel.org \
--cc=linux-hwmon@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux@roeck-us.net \
--cc=mpearson-lenovo@squebb.ca \
--cc=platform-driver-x86@vger.kernel.org \
--cc=skhan@linuxfoundation.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.