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: 7+ 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-22 18:55 ` [RFC PATCH 1/3] platform/x86: lenovo-wmi-other: Add Legion Go Full Speed control 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:47 ` [RFC PATCH 3/3] platform/x86: lenovo: Add Legion Go Fan Method curve driver Aditya Dash
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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox