public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH v6 00/13] platform-x86: lenovo-wmi: Add fixes and enhancement
@ 2026-03-31 18:11 Derek J. Clark
  2026-03-31 18:11 ` [PATCH v6 01/13] platform/x86: lenovo-wmi-other: Move LWMI_FAN_DIV Derek J. Clark
                   ` (13 more replies)
  0 siblings, 14 replies; 33+ messages in thread
From: Derek J. Clark @ 2026-03-31 18:11 UTC (permalink / raw)
  To: Ilpo Järvinen, Hans de Goede
  Cc: Mark Pearson, Armin Wolf, Jonathan Corbet, Rong Zhang, Kurt Borja,
	Derek J . Clark, platform-driver-x86, linux-kernel

This series adds many much needed features and fixes to the lenovo-wmi
drivers.

Patch 1 moves LWMI_FAN_DIV to be next to the rest of the fan attribute
defines in preparation for adding additional attrbiute macros. This is
so the attribute macros can all be in the same place in the file.

Patch 2 cleans up tunable_attr_01 by removing an unused pointer and
correctly assigning the members as u8 isntead of u32.

Patch 3 fixes a bug when sending 32 bit arguments via WMI where the
second value in the args struct was uninitialized.

Patch 4 moves all gamezone enums from the gamezone header into the
helpers header in preparation for the rest of the series.

Patch 5 adds a function to make assigning attribute ID's for capdata
cleaner and easier.

Patch 6 addresses bugs where devices that don't support exposed
attributes would still create the attribute, and also attempts to
identify the correct capdata and set/get methods since some legacy
interfaces don't use the custom mode in the method or capdata ID.

Patch 7 adds the remaining CPU attributes that weren't previously
exposed.

Patch 8 adds GPU attributes.

Patch 9 renames a name constant in preparation for patch 6.

Patch 10 adds battery charge-type limiting when supported only by WMI, or
when a module parameter to skip compatibility checks is set. The
MODULE_PARM_DESC macro creates one check and two warnings in checkpatch.
I reviewed other examples from the kernel and I am following the same
convention, so I left it as is.

Patch 11 fixes a bug where the 'gamezone' and the 'other' drivers were
incorrectly coupled in the Kconfig, leading to side effects under
certain kernel configurations.

Patch 12 adds a debugfs directory.

Patch 13 adds a debugfs file for dumping capdata.

Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
---
v6:
  - Incorporate Rong Zhang's debugfs and decoupling patches into the
    series.
  - Add a patch to clean up too many cross-references to wmi-gamezone.h
  - Make lwmi_attr_id a static inline in wmi-capdata.h
  - Added a patch  to fix a bug where ares.arg1 is uninitialized when it
    is sent to the firmware.
  - Add supported checks before adding battery extenstion, and ensure
    both the new checks and the is_writable checks are not casting u32
    to i32.
  - Misc formating changes.
v5: https://lore.kernel.org/platform-driver-x86/20260324221032.1333636-1-derekjohn.clark@gmail.com/
  - Remove cv/cd_mode_id references that occured before patch 4.
  - Move lwmi_attr_id to capdata.c with a namespace export.
  - Fix mixing include.
  - Make lwmi_attr_is_supported return bool.
  - Use switch instead of if for setting/getting charge type state.
  - Various formatting fixes.
v4: https://lore.kernel.org/platform-driver-x86/20260312031032.3467565-1-derekjohn.clark@gmail.com/
  - Use loop instead of back gotos for identifying the working attribute
    ID.
  - Use function instead of macro to assign attribute_id, preserving
    types.
  - Removed unused defines and enum values.
  - Rename charging defines to clarify thier purpose.
  - Fixed various formatting issues from v3.
  - Added module param to skip ACPI check when loading the driver for
    the power supply extension.
  - Don't abort adding power supply extension if the ACPI handle from
    ideapad is not present.
  - Don't worry about symmetric cleanup when cleaning up attributes in
    an error state.
  - Reword Patch 8 commit message to be more concise.
  - Fix wording in Patch 7 to match the changes.
v3: https://lore.kernel.org/platform-driver-x86/20260224043200.2680384-1-derekjohn.clark@gmail.com/
  - Re-add HWMON name const and just rename LWMI_OM_FW_ATTR_BASE_PATH
  - Fix linker warnings by moving acpi/battery include to the end of the
    list.
  - Remove CPU/GPU OC features. These attributes are BOOL type and will
    need a new constructor that I'll add later.
v2: https://lore.kernel.org/platform-driver-x86/20260215061339.2842486-1-derekjohn.clark@gmail.com/
  - Fix gpu_mode misisng from attributes list.
  - Fix prototypes for power suppy patch.
  - Reorganize CPU and GPU attributes alphabetically.
  - Break out the patch consolidating the driver name cost.
  - Move some of the refactoring of attribute_id back to into patch 1
    where it belongs.
  - Fix some additional typos in function prototypes.
v1: https://lore.kernel.org/platform-driver-x86/20260213081243.794288-1-derekjohn.clark@gmail.com/


Derek J. Clark (10):
  platform/x86: lenovo-wmi-other: Move LWMI_FAN_DIV
  platform/x86: lenovo-wmi-other: Fix tunable_attr_01 struct members
  platform/x86: lenovo-wmi-other: Zero initialize WMI arguments
  platform/x86: lenovo-wmi-helpers: Move gamezone enums to wmi-helpers
  platform/x86: lenovo-wmi-other: Add lwmi_attr_id() function
  platform/x86: lenovo-wmi-other: Limit adding attributes to supported
    devices
  platform/x86: lenovo-wmi-other: Add missing CPU tunable attributes
  platform/x86: lenovo-wmi-other: Add GPU tunable attributes
  platform/x86: lenovo-wmi-other: Rename LWMI_OM_FW_ATTR_BASE_PATH
  platform/x86: lenovo-wmi-other: Add WMI battery charge limiting

Rong Zhang (3):
  platform/x86: lenovo: Decouple lenovo-wmi-gamezone and
    lenovo-wmi-other
  platform/x86: lenovo-wmi-helpers: Add helper for creating per-device
    debugfs dir
  platform/x86: lenovo-wmi-capdata: Add debugfs file for dumping capdata

 .../wmi/devices/lenovo-wmi-other.rst          |  19 +
 drivers/platform/x86/lenovo/Kconfig           |   3 +-
 drivers/platform/x86/lenovo/wmi-capdata.c     | 128 +++-
 drivers/platform/x86/lenovo/wmi-capdata.h     |  31 +-
 drivers/platform/x86/lenovo/wmi-events.c      |   2 +-
 drivers/platform/x86/lenovo/wmi-gamezone.c    |   5 +-
 drivers/platform/x86/lenovo/wmi-gamezone.h    |  20 -
 drivers/platform/x86/lenovo/wmi-helpers.c     | 136 ++++
 drivers/platform/x86/lenovo/wmi-helpers.h     |  23 +
 drivers/platform/x86/lenovo/wmi-other.c       | 689 ++++++++++++++----
 drivers/platform/x86/lenovo/wmi-other.h       |  16 -
 11 files changed, 891 insertions(+), 181 deletions(-)
 delete mode 100644 drivers/platform/x86/lenovo/wmi-gamezone.h
 delete mode 100644 drivers/platform/x86/lenovo/wmi-other.h

-- 
2.53.0


^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH v6 01/13] platform/x86: lenovo-wmi-other: Move LWMI_FAN_DIV
  2026-03-31 18:11 [PATCH v6 00/13] platform-x86: lenovo-wmi: Add fixes and enhancement Derek J. Clark
@ 2026-03-31 18:11 ` Derek J. Clark
  2026-03-31 18:11 ` [PATCH v6 02/13] platform/x86: lenovo-wmi-other: Fix tunable_attr_01 struct members Derek J. Clark
                   ` (12 subsequent siblings)
  13 siblings, 0 replies; 33+ messages in thread
From: Derek J. Clark @ 2026-03-31 18:11 UTC (permalink / raw)
  To: Ilpo Järvinen, Hans de Goede
  Cc: Mark Pearson, Armin Wolf, Jonathan Corbet, Rong Zhang, Kurt Borja,
	Derek J . Clark, platform-driver-x86, linux-kernel

Later patches in this series will add additional attribute ID macros.
Keep the fan defines together and permit attribute ID macros to stay
together as well by moving LWMI_FAN_DIV.

Reviewed-by: Rong Zhang <i@rong.moe>
Tested-by: Rong Zhang <i@rong.moe>
Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
---
 drivers/platform/x86/lenovo/wmi-other.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
index 6040f45aa2b0..caf360b76fc5 100644
--- a/drivers/platform/x86/lenovo/wmi-other.c
+++ b/drivers/platform/x86/lenovo/wmi-other.c
@@ -71,13 +71,13 @@
 #define LWMI_FAN_NR 4
 #define LWMI_FAN_ID(x) ((x) + LWMI_FAN_ID_BASE)
 
+#define LWMI_FAN_DIV 100
+
 #define LWMI_ATTR_ID_FAN_RPM(x)						\
 	(FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, LWMI_DEVICE_ID_FAN) |	\
 	 FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, LWMI_FEATURE_ID_FAN_RPM) |	\
 	 FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, LWMI_FAN_ID(x)))
 
-#define LWMI_FAN_DIV 100
-
 #define LWMI_OM_FW_ATTR_BASE_PATH "lenovo-wmi-other"
 #define LWMI_OM_HWMON_NAME "lenovo_wmi_other"
 
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v6 02/13] platform/x86: lenovo-wmi-other: Fix tunable_attr_01 struct members
  2026-03-31 18:11 [PATCH v6 00/13] platform-x86: lenovo-wmi: Add fixes and enhancement Derek J. Clark
  2026-03-31 18:11 ` [PATCH v6 01/13] platform/x86: lenovo-wmi-other: Move LWMI_FAN_DIV Derek J. Clark
@ 2026-03-31 18:11 ` Derek J. Clark
  2026-03-31 18:11 ` [PATCH v6 03/13] platform/x86: lenovo-wmi-other: Zero initialize WMI arguments Derek J. Clark
                   ` (11 subsequent siblings)
  13 siblings, 0 replies; 33+ messages in thread
From: Derek J. Clark @ 2026-03-31 18:11 UTC (permalink / raw)
  To: Ilpo Järvinen, Hans de Goede
  Cc: Mark Pearson, Armin Wolf, Jonathan Corbet, Rong Zhang, Kurt Borja,
	Derek J . Clark, platform-driver-x86, linux-kernel

In struct tunable_attr_01 the capdata pointer is unused and the size of
the id members is u32 when it should be u8. Fix these prior to adding
additional members.

Reviewed-by: Rong Zhang <i@rong.moe>
Tested-by: Rong Zhang <i@rong.moe>
Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
---
 drivers/platform/x86/lenovo/wmi-other.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
index caf360b76fc5..c1728c7c2957 100644
--- a/drivers/platform/x86/lenovo/wmi-other.c
+++ b/drivers/platform/x86/lenovo/wmi-other.c
@@ -546,11 +546,10 @@ static void lwmi_om_fan_info_collect_cd_fan(struct device *dev, struct cd_list *
 /* ======== fw_attributes (component: lenovo-wmi-capdata 01) ======== */
 
 struct tunable_attr_01 {
-	struct capdata01 *capdata;
 	struct device *dev;
-	u32 feature_id;
-	u32 device_id;
-	u32 type_id;
+	u8 feature_id;
+	u8 device_id;
+	u8 type_id;
 };
 
 static struct tunable_attr_01 ppt_pl1_spl = {
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v6 03/13] platform/x86: lenovo-wmi-other: Zero initialize WMI arguments
  2026-03-31 18:11 [PATCH v6 00/13] platform-x86: lenovo-wmi: Add fixes and enhancement Derek J. Clark
  2026-03-31 18:11 ` [PATCH v6 01/13] platform/x86: lenovo-wmi-other: Move LWMI_FAN_DIV Derek J. Clark
  2026-03-31 18:11 ` [PATCH v6 02/13] platform/x86: lenovo-wmi-other: Fix tunable_attr_01 struct members Derek J. Clark
@ 2026-03-31 18:11 ` Derek J. Clark
  2026-04-01 18:26   ` Rong Zhang
  2026-03-31 18:11 ` [PATCH v6 04/13] platform/x86: lenovo-wmi-helpers: Move gamezone enums to wmi-helpers Derek J. Clark
                   ` (10 subsequent siblings)
  13 siblings, 1 reply; 33+ messages in thread
From: Derek J. Clark @ 2026-03-31 18:11 UTC (permalink / raw)
  To: Ilpo Järvinen, Hans de Goede
  Cc: Mark Pearson, Armin Wolf, Jonathan Corbet, Rong Zhang, Kurt Borja,
	Derek J . Clark, platform-driver-x86, linux-kernel

Adds explicit initialization of wmi_method_args_32 declarations with
zero values to prevent uninitialized data from being sent to the WMI
device when passed.

Suggested-by: Rong Zhang <i@rong.moe>
Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
---
 drivers/platform/x86/lenovo/wmi-other.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
index c1728c7c2957..c3614bc4f90c 100644
--- a/drivers/platform/x86/lenovo/wmi-other.c
+++ b/drivers/platform/x86/lenovo/wmi-other.c
@@ -166,7 +166,7 @@ MODULE_PARM_DESC(relax_fan_constraint,
  */
 static int lwmi_om_fan_get_set(struct lwmi_om_priv *priv, int channel, u32 *val, bool set)
 {
-	struct wmi_method_args_32 args;
+	struct wmi_method_args_32 args = { 0x0, 0x0 };
 	u32 method_id, retval;
 	int err;
 
@@ -772,7 +772,7 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
 					struct tunable_attr_01 *tunable_attr)
 {
 	struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev);
-	struct wmi_method_args_32 args;
+	struct wmi_method_args_32 args = { 0x0, 0x0 };
 	struct capdata01 capdata;
 	enum thermal_mode mode;
 	u32 attribute_id;
@@ -835,7 +835,7 @@ static ssize_t attr_current_value_show(struct kobject *kobj,
 				       struct tunable_attr_01 *tunable_attr)
 {
 	struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev);
-	struct wmi_method_args_32 args;
+	struct wmi_method_args_32 args = { 0x0, 0x0 };
 	enum thermal_mode mode;
 	u32 attribute_id;
 	int retval;
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v6 04/13] platform/x86: lenovo-wmi-helpers: Move gamezone enums to wmi-helpers
  2026-03-31 18:11 [PATCH v6 00/13] platform-x86: lenovo-wmi: Add fixes and enhancement Derek J. Clark
                   ` (2 preceding siblings ...)
  2026-03-31 18:11 ` [PATCH v6 03/13] platform/x86: lenovo-wmi-other: Zero initialize WMI arguments Derek J. Clark
@ 2026-03-31 18:11 ` Derek J. Clark
  2026-04-01 18:26   ` Rong Zhang
  2026-03-31 18:12 ` [PATCH v6 05/13] platform/x86: lenovo-wmi-other: Add lwmi_attr_id() function Derek J. Clark
                   ` (9 subsequent siblings)
  13 siblings, 1 reply; 33+ messages in thread
From: Derek J. Clark @ 2026-03-31 18:11 UTC (permalink / raw)
  To: Ilpo Järvinen, Hans de Goede
  Cc: Mark Pearson, Armin Wolf, Jonathan Corbet, Rong Zhang, Kurt Borja,
	Derek J . Clark, platform-driver-x86, linux-kernel

In the next patch, the thermal mode enum will be accessed across three
separate drivers (wmi-capdata, wmi-gamezone and wmi-other). An
additional patch in the series will also add a function protoype that
needs to reference this enum in wmi-helpers.h. To avoid having all these
drivers import each others headers, and to avoid declaring an opaque enum
to hande the second case, move the thermal mode enum to helpers where it
can be safely accessed by everything that needs it from a single import.

While at it, since the gamezone_events_type enum is the only remaining
item in the header, move that as well to permit removing the gamezone
header entirely.

Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
---
 drivers/platform/x86/lenovo/wmi-events.c   |  2 +-
 drivers/platform/x86/lenovo/wmi-gamezone.c |  1 -
 drivers/platform/x86/lenovo/wmi-gamezone.h | 20 --------------------
 drivers/platform/x86/lenovo/wmi-helpers.h  | 13 +++++++++++++
 drivers/platform/x86/lenovo/wmi-other.c    |  1 -
 5 files changed, 14 insertions(+), 23 deletions(-)
 delete mode 100644 drivers/platform/x86/lenovo/wmi-gamezone.h

diff --git a/drivers/platform/x86/lenovo/wmi-events.c b/drivers/platform/x86/lenovo/wmi-events.c
index 0994cd7dd504..9e9f2e82e04d 100644
--- a/drivers/platform/x86/lenovo/wmi-events.c
+++ b/drivers/platform/x86/lenovo/wmi-events.c
@@ -17,7 +17,7 @@
 #include <linux/wmi.h>
 
 #include "wmi-events.h"
-#include "wmi-gamezone.h"
+#include "wmi-helpers.h"
 
 #define THERMAL_MODE_EVENT_GUID "D320289E-8FEA-41E0-86F9-911D83151B5F"
 
diff --git a/drivers/platform/x86/lenovo/wmi-gamezone.c b/drivers/platform/x86/lenovo/wmi-gamezone.c
index 381836d29a96..602a48de1b4e 100644
--- a/drivers/platform/x86/lenovo/wmi-gamezone.c
+++ b/drivers/platform/x86/lenovo/wmi-gamezone.c
@@ -21,7 +21,6 @@
 #include <linux/wmi.h>
 
 #include "wmi-events.h"
-#include "wmi-gamezone.h"
 #include "wmi-helpers.h"
 #include "wmi-other.h"
 
diff --git a/drivers/platform/x86/lenovo/wmi-gamezone.h b/drivers/platform/x86/lenovo/wmi-gamezone.h
deleted file mode 100644
index 6b163a5eeb95..000000000000
--- a/drivers/platform/x86/lenovo/wmi-gamezone.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-/* Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com> */
-
-#ifndef _LENOVO_WMI_GAMEZONE_H_
-#define _LENOVO_WMI_GAMEZONE_H_
-
-enum gamezone_events_type {
-	LWMI_GZ_GET_THERMAL_MODE = 1,
-};
-
-enum thermal_mode {
-	LWMI_GZ_THERMAL_MODE_QUIET =	   0x01,
-	LWMI_GZ_THERMAL_MODE_BALANCED =	   0x02,
-	LWMI_GZ_THERMAL_MODE_PERFORMANCE = 0x03,
-	LWMI_GZ_THERMAL_MODE_EXTREME =	   0xE0, /* Ver 6+ */
-	LWMI_GZ_THERMAL_MODE_CUSTOM =	   0xFF,
-};
-
-#endif /* !_LENOVO_WMI_GAMEZONE_H_ */
diff --git a/drivers/platform/x86/lenovo/wmi-helpers.h b/drivers/platform/x86/lenovo/wmi-helpers.h
index 20fd21749803..3364d8e152ca 100644
--- a/drivers/platform/x86/lenovo/wmi-helpers.h
+++ b/drivers/platform/x86/lenovo/wmi-helpers.h
@@ -14,6 +14,19 @@ struct wmi_method_args_32 {
 	u32 arg1;
 };
 
+enum lwmi_event_type {
+	LWMI_GZ_GET_THERMAL_MODE = 0x01,
+};
+
+enum thermal_mode {
+	LWMI_GZ_THERMAL_MODE_NONE =	   0x00,
+	LWMI_GZ_THERMAL_MODE_QUIET =	   0x01,
+	LWMI_GZ_THERMAL_MODE_BALANCED =	   0x02,
+	LWMI_GZ_THERMAL_MODE_PERFORMANCE = 0x03,
+	LWMI_GZ_THERMAL_MODE_EXTREME =	   0xE0, /* Ver 6+ */
+	LWMI_GZ_THERMAL_MODE_CUSTOM =	   0xFF,
+};
+
 int lwmi_dev_evaluate_int(struct wmi_device *wdev, u8 instance, u32 method_id,
 			  unsigned char *buf, size_t size, u32 *retval);
 
diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
index c3614bc4f90c..b24bf98c19ad 100644
--- a/drivers/platform/x86/lenovo/wmi-other.c
+++ b/drivers/platform/x86/lenovo/wmi-other.c
@@ -47,7 +47,6 @@
 
 #include "wmi-capdata.h"
 #include "wmi-events.h"
-#include "wmi-gamezone.h"
 #include "wmi-helpers.h"
 #include "wmi-other.h"
 #include "../firmware_attributes_class.h"
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v6 05/13] platform/x86: lenovo-wmi-other: Add lwmi_attr_id() function
  2026-03-31 18:11 [PATCH v6 00/13] platform-x86: lenovo-wmi: Add fixes and enhancement Derek J. Clark
                   ` (3 preceding siblings ...)
  2026-03-31 18:11 ` [PATCH v6 04/13] platform/x86: lenovo-wmi-helpers: Move gamezone enums to wmi-helpers Derek J. Clark
@ 2026-03-31 18:12 ` Derek J. Clark
  2026-04-01 18:29   ` Rong Zhang
  2026-03-31 18:12 ` [PATCH v6 06/13] platform/x86: lenovo-wmi-other: Limit adding attributes to supported devices Derek J. Clark
                   ` (8 subsequent siblings)
  13 siblings, 1 reply; 33+ messages in thread
From: Derek J. Clark @ 2026-03-31 18:12 UTC (permalink / raw)
  To: Ilpo Järvinen, Hans de Goede
  Cc: Mark Pearson, Armin Wolf, Jonathan Corbet, Rong Zhang, Kurt Borja,
	Derek J . Clark, platform-driver-x86, linux-kernel

Adds lwmi_attr_id() function. In the same vein as LWMI_ATTR_ID_FAN_RPM(),
but as a generic, to de-duplicate attribute_id assignment biolerplate.

Reviewed-by: Rong Zhang <i@rong.moe>
Tested-by: Rong Zhang <i@rong.moe>
Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
---
v6:
  - Move lwmi_attr_id to wmi-capdata.h as static inline.
v5:
  - Move references to cv/cd_mode_id to patch 4/8.
  - Move lwmi_attr_id to wmi-capdata.c and export with namespace.
v4:
  - Switch from macro to static inline to preserve types.
---
 drivers/platform/x86/lenovo/wmi-capdata.c |  8 ++---
 drivers/platform/x86/lenovo/wmi-capdata.h | 20 ++++++++++++
 drivers/platform/x86/lenovo/wmi-other.c   | 39 ++++++-----------------
 3 files changed, 34 insertions(+), 33 deletions(-)

diff --git a/drivers/platform/x86/lenovo/wmi-capdata.c b/drivers/platform/x86/lenovo/wmi-capdata.c
index ee1fb02d8e31..169665be4dcf 100644
--- a/drivers/platform/x86/lenovo/wmi-capdata.c
+++ b/drivers/platform/x86/lenovo/wmi-capdata.c
@@ -27,7 +27,6 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/acpi.h>
-#include <linux/bitfield.h>
 #include <linux/bug.h>
 #include <linux/cleanup.h>
 #include <linux/component.h>
@@ -48,6 +47,7 @@
 #include <linux/wmi.h>
 
 #include "wmi-capdata.h"
+#include "wmi-helpers.h"
 
 #define LENOVO_CAPABILITY_DATA_00_GUID "362A3AFE-3D96-4665-8530-96DAD5BB300E"
 #define LENOVO_CAPABILITY_DATA_01_GUID "7A8F5407-CB67-4D6E-B547-39B3BE018154"
@@ -58,9 +58,9 @@
 
 #define LWMI_FEATURE_ID_FAN_TEST 0x05
 
-#define LWMI_ATTR_ID_FAN_TEST							\
-	(FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, LWMI_DEVICE_ID_FAN) |		\
-	 FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, LWMI_FEATURE_ID_FAN_TEST))
+#define LWMI_ATTR_ID_FAN_TEST                                      \
+	lwmi_attr_id(LWMI_DEVICE_ID_FAN, LWMI_FEATURE_ID_FAN_TEST, \
+		     LWMI_GZ_THERMAL_MODE_NONE, LWMI_TYPE_ID_NONE)
 
 enum lwmi_cd_type {
 	LENOVO_CAPABILITY_DATA_00,
diff --git a/drivers/platform/x86/lenovo/wmi-capdata.h b/drivers/platform/x86/lenovo/wmi-capdata.h
index 8c1df3efcc55..1388eaf4ab4a 100644
--- a/drivers/platform/x86/lenovo/wmi-capdata.h
+++ b/drivers/platform/x86/lenovo/wmi-capdata.h
@@ -6,6 +6,7 @@
 #define _LENOVO_WMI_CAPDATA_H_
 
 #include <linux/bits.h>
+#include <linux/bitfield.h>
 #include <linux/types.h>
 
 #define LWMI_SUPP_VALID		BIT(0)
@@ -19,6 +20,8 @@
 
 #define LWMI_DEVICE_ID_FAN	0x04
 
+#define LWMI_TYPE_ID_NONE 0x00
+
 struct component_match;
 struct device;
 struct cd_list;
@@ -57,6 +60,23 @@ struct lwmi_cd_binder {
 	cd_list_cb_t cd_fan_list_cb;
 };
 
+/**
+ * lwmi_attr_id() - Formats a capability data attribute ID
+ * @dev_id: The u8 corresponding to the device ID.
+ * @feat_id: The u8 corresponding to the feature ID on the device.
+ * @mode_id: The u8 corresponding to the wmi-gamezone mode for set/get.
+ * @type_id: The u8 corresponding to the sub-device.
+ *
+ * Return: u32.
+ */
+static inline u32 lwmi_attr_id(u8 dev_id, u8 feat_id, u8 mode_id, u8 type_id)
+{
+	return (FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, dev_id)   |
+		FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, feat_id) |
+		FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode_id) |
+		FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, type_id));
+}
+
 void lwmi_cd_match_add_all(struct device *master, struct component_match **matchptr);
 int lwmi_cd00_get_data(struct cd_list *list, u32 attribute_id, struct capdata00 *output);
 int lwmi_cd01_get_data(struct cd_list *list, u32 attribute_id, struct capdata01 *output);
diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
index b24bf98c19ad..d2838ae5934f 100644
--- a/drivers/platform/x86/lenovo/wmi-other.c
+++ b/drivers/platform/x86/lenovo/wmi-other.c
@@ -27,7 +27,6 @@
  */
 
 #include <linux/acpi.h>
-#include <linux/bitfield.h>
 #include <linux/cleanup.h>
 #include <linux/component.h>
 #include <linux/container_of.h>
@@ -61,8 +60,6 @@
 
 #define LWMI_FEATURE_ID_FAN_RPM 0x03
 
-#define LWMI_TYPE_ID_NONE 0x00
-
 #define LWMI_FEATURE_VALUE_GET 17
 #define LWMI_FEATURE_VALUE_SET 18
 
@@ -72,10 +69,9 @@
 
 #define LWMI_FAN_DIV 100
 
-#define LWMI_ATTR_ID_FAN_RPM(x)						\
-	(FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, LWMI_DEVICE_ID_FAN) |	\
-	 FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, LWMI_FEATURE_ID_FAN_RPM) |	\
-	 FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, LWMI_FAN_ID(x)))
+#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))
 
 #define LWMI_OM_FW_ATTR_BASE_PATH "lenovo-wmi-other"
 #define LWMI_OM_HWMON_NAME "lenovo_wmi_other"
@@ -714,12 +710,8 @@ static ssize_t attr_capdata01_show(struct kobject *kobj,
 	u32 attribute_id;
 	int value, ret;
 
-	attribute_id =
-		FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
-		FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
-		FIELD_PREP(LWMI_ATTR_MODE_ID_MASK,
-			   LWMI_GZ_THERMAL_MODE_CUSTOM) |
-		FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
+	attribute_id = lwmi_attr_id(tunable_attr->device_id, tunable_attr->feature_id,
+				    LWMI_GZ_THERMAL_MODE_CUSTOM, tunable_attr->type_id);
 
 	ret = lwmi_cd01_get_data(priv->cd01_list, attribute_id, &capdata);
 	if (ret)
@@ -774,7 +766,6 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
 	struct wmi_method_args_32 args = { 0x0, 0x0 };
 	struct capdata01 capdata;
 	enum thermal_mode mode;
-	u32 attribute_id;
 	u32 value;
 	int ret;
 
@@ -785,13 +776,10 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
 	if (mode != LWMI_GZ_THERMAL_MODE_CUSTOM)
 		return -EBUSY;
 
-	attribute_id =
-		FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
-		FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
-		FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) |
-		FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
+	args.arg0 = lwmi_attr_id(tunable_attr->device_id, tunable_attr->feature_id,
+				 mode, tunable_attr->type_id);
 
-	ret = lwmi_cd01_get_data(priv->cd01_list, attribute_id, &capdata);
+	ret = lwmi_cd01_get_data(priv->cd01_list, args.arg0, &capdata);
 	if (ret)
 		return ret;
 
@@ -802,7 +790,6 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
 	if (value < capdata.min_value || value > capdata.max_value)
 		return -EINVAL;
 
-	args.arg0 = attribute_id;
 	args.arg1 = value;
 
 	ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_SET,
@@ -836,7 +823,6 @@ static ssize_t attr_current_value_show(struct kobject *kobj,
 	struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev);
 	struct wmi_method_args_32 args = { 0x0, 0x0 };
 	enum thermal_mode mode;
-	u32 attribute_id;
 	int retval;
 	int ret;
 
@@ -844,13 +830,8 @@ static ssize_t attr_current_value_show(struct kobject *kobj,
 	if (ret)
 		return ret;
 
-	attribute_id =
-		FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
-		FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
-		FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) |
-		FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
-
-	args.arg0 = attribute_id;
+	args.arg0 = lwmi_attr_id(tunable_attr->device_id, tunable_attr->feature_id,
+				 mode, tunable_attr->type_id);
 
 	ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_GET,
 				    (unsigned char *)&args, sizeof(args),
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v6 06/13] platform/x86: lenovo-wmi-other: Limit adding attributes to supported devices
  2026-03-31 18:11 [PATCH v6 00/13] platform-x86: lenovo-wmi: Add fixes and enhancement Derek J. Clark
                   ` (4 preceding siblings ...)
  2026-03-31 18:12 ` [PATCH v6 05/13] platform/x86: lenovo-wmi-other: Add lwmi_attr_id() function Derek J. Clark
@ 2026-03-31 18:12 ` Derek J. Clark
  2026-03-31 22:28   ` Kurt Borja
  2026-03-31 18:12 ` [PATCH v6 07/13] platform/x86: lenovo-wmi-other: Add missing CPU tunable attributes Derek J. Clark
                   ` (7 subsequent siblings)
  13 siblings, 1 reply; 33+ messages in thread
From: Derek J. Clark @ 2026-03-31 18:12 UTC (permalink / raw)
  To: Ilpo Järvinen, Hans de Goede
  Cc: Mark Pearson, Armin Wolf, Jonathan Corbet, Rong Zhang, Kurt Borja,
	Derek J . Clark, platform-driver-x86, linux-kernel

Adds lwmi_is_attr_01_supported, and only creates the attribute subfolder
if the attribute is supported by the hardware. Due to some poorly
implemented BIOS this is a multi-step sequence of events. This is
because:
- Some BIOS support getting the capability data from custom mode (0xff),
  while others only support it in no-mode (0x00).
- Some BIOS support get/set for the current value from custom mode (0xff),
  while others only support it in no-mode (0x00).
- Some BIOS report capability data for a method that is not fully
  implemented.
- Some BIOS have methods fully implemented, but no complimentary
  capability data.

To ensure we only expose fully implemented methods with corresponding
capability data, we check each outcome before reporting that an
attribute can be supported.

Checking for lwmi_is_attr_01_supported during remove is not done to
ensure that we don't attempt to call cd01 or send WMI events if one of
the interfaces being removed was the cause of the driver unloading.

Reviewed-by: Rong Zhang <i@rong.moe>
Tested-by: Rong Zhang <i@rong.moe>
Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
Reported-by: Kurt Borja <kuurtb@gmail.com>
Closes: https://lore.kernel.org/platform-driver-x86/DG60P3SHXR8H.3NSEHMZ6J7XRC@gmail.com/
Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
---
v6:
  - Zero initialize args in lwmi_is_attr_01_supported.
  - Fix formatting.
v5:
  - Move cv/cd_mode_id refrences from path 3/4.
  - Add missing import for ARRAY_SIZE.
  - Make lwmi_is_attr_01_supported return bool instead of u32.
  - Various formatting fixes.
v4:
  - Use for loop instead of backtrace gotos for checking if an attribute
    is supported.
  - Add include for dev_printk.
  - Wrap dev_dbg in lwmi_is_attr_01_supported earlier.
  - Don't use symmetric cleanup of attributes in error states.
---
 drivers/platform/x86/lenovo/wmi-other.c | 87 ++++++++++++++++++++++++-
 1 file changed, 84 insertions(+), 3 deletions(-)

diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
index d2838ae5934f..f689d19d711a 100644
--- a/drivers/platform/x86/lenovo/wmi-other.c
+++ b/drivers/platform/x86/lenovo/wmi-other.c
@@ -27,10 +27,12 @@
  */
 
 #include <linux/acpi.h>
+#include <linux/array_size.h>
 #include <linux/cleanup.h>
 #include <linux/component.h>
 #include <linux/container_of.h>
 #include <linux/device.h>
+#include <linux/dev_printk.h>
 #include <linux/export.h>
 #include <linux/gfp_types.h>
 #include <linux/hwmon.h>
@@ -545,6 +547,8 @@ struct tunable_attr_01 {
 	u8 feature_id;
 	u8 device_id;
 	u8 type_id;
+	u8 cd_mode_id; /* mode arg for searching capdata */
+	u8 cv_mode_id; /* mode arg for set/get current_value */
 };
 
 static struct tunable_attr_01 ppt_pl1_spl = {
@@ -777,7 +781,7 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
 		return -EBUSY;
 
 	args.arg0 = lwmi_attr_id(tunable_attr->device_id, tunable_attr->feature_id,
-				 mode, tunable_attr->type_id);
+				 tunable_attr->cd_mode_id, tunable_attr->type_id);
 
 	ret = lwmi_cd01_get_data(priv->cd01_list, args.arg0, &capdata);
 	if (ret)
@@ -830,6 +834,10 @@ static ssize_t attr_current_value_show(struct kobject *kobj,
 	if (ret)
 		return ret;
 
+	/* If "no-mode" is the supported mode, ensure we never send current mode */
+	if (tunable_attr->cv_mode_id == LWMI_GZ_THERMAL_MODE_NONE)
+		mode = tunable_attr->cv_mode_id;
+
 	args.arg0 = lwmi_attr_id(tunable_attr->device_id, tunable_attr->feature_id,
 				 mode, tunable_attr->type_id);
 
@@ -842,6 +850,77 @@ static ssize_t attr_current_value_show(struct kobject *kobj,
 	return sysfs_emit(buf, "%d\n", retval);
 }
 
+/**
+ * lwmi_attr_01_is_supported() - Determine if the given attribute is supported.
+ * @tunable_attr: The attribute to verify.
+ *
+ * First check if the attribute has a corresponding capdata01 table in the cd01
+ * module under the "custom" mode (0xff). If that is not present then check if
+ * there is a corresponding "no-mode" (0x00) entry. If either of those passes,
+ * check capdata->supported for values > 0. If capdata is available, attempt to
+ * determine the set/get mode for the current value property using a similar
+ * pattern. If the value returned by either custom or no-mode is 0, or we get
+ * an error, we assume that mode is not supported. If any of the above checks
+ * fail then the attribute is not fully supported.
+ *
+ * The probed cd_mode_id/cv_mode_id are stored on the tunable_attr for later
+ * reference.
+ *
+ * Return: bool.
+ */
+static bool lwmi_attr_01_is_supported(struct tunable_attr_01 *tunable_attr)
+{
+	u8 modes[2] = { LWMI_GZ_THERMAL_MODE_CUSTOM, LWMI_GZ_THERMAL_MODE_NONE };
+	struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev);
+	struct wmi_method_args_32 args = { 0x0, 0x0 };
+	bool cd_mode_found = false;
+	bool cv_mode_found = false;
+	struct capdata01 capdata;
+	int retval, ret, i;
+
+	/* Determine tunable_attr->cd_mode_id*/
+	for (i = 0; i < ARRAY_SIZE(modes); i++) {
+		args.arg0 = lwmi_attr_id(tunable_attr->device_id, tunable_attr->feature_id,
+					 modes[i], tunable_attr->type_id);
+
+		ret = lwmi_cd01_get_data(priv->cd01_list, args.arg0, &capdata);
+		if (ret || !capdata.supported)
+			continue;
+		tunable_attr->cd_mode_id = modes[i];
+		cd_mode_found = true;
+		break;
+	}
+
+	if (!cd_mode_found)
+		return cd_mode_found;
+
+	/* Determine tunable_attr->cv_mode_id, returns 1 if supported*/
+	for (i = 0; i < ARRAY_SIZE(modes); i++) {
+		args.arg0 = lwmi_attr_id(tunable_attr->device_id, tunable_attr->feature_id,
+					 modes[i], tunable_attr->type_id);
+
+		ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_GET,
+					    (unsigned char *)&args, sizeof(args),
+					    &retval);
+		if (ret || !retval)
+			continue;
+		tunable_attr->cv_mode_id = modes[i];
+		cv_mode_found = true;
+		break;
+	}
+
+	if (!cv_mode_found)
+		return cv_mode_found;
+
+	dev_dbg(tunable_attr->dev,
+		"cd_mode_id: %#010x, cv_mode_id: %#010x, attribute support level: %#010x\n",
+		lwmi_attr_id(tunable_attr->device_id, tunable_attr->feature_id,
+			     tunable_attr->cd_mode_id, tunable_attr->type_id),
+		args.arg0, capdata.supported);
+
+	return capdata.supported > 0 ? true : false;
+}
+
 /* Lenovo WMI Other Mode Attribute macros */
 #define __LWMI_ATTR_RO(_func, _name)                                  \
 	{                                                             \
@@ -965,12 +1044,14 @@ static int lwmi_om_fw_attr_add(struct lwmi_om_priv *priv)
 	}
 
 	for (i = 0; i < ARRAY_SIZE(cd01_attr_groups) - 1; i++) {
+		cd01_attr_groups[i].tunable_attr->dev = &priv->wdev->dev;
+		if (!lwmi_attr_01_is_supported(cd01_attr_groups[i].tunable_attr))
+			continue;
+
 		err = sysfs_create_group(&priv->fw_attr_kset->kobj,
 					 cd01_attr_groups[i].attr_group);
 		if (err)
 			goto err_remove_groups;
-
-		cd01_attr_groups[i].tunable_attr->dev = &priv->wdev->dev;
 	}
 	return 0;
 
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v6 07/13] platform/x86: lenovo-wmi-other: Add missing CPU tunable attributes
  2026-03-31 18:11 [PATCH v6 00/13] platform-x86: lenovo-wmi: Add fixes and enhancement Derek J. Clark
                   ` (5 preceding siblings ...)
  2026-03-31 18:12 ` [PATCH v6 06/13] platform/x86: lenovo-wmi-other: Limit adding attributes to supported devices Derek J. Clark
@ 2026-03-31 18:12 ` Derek J. Clark
  2026-03-31 22:29   ` Kurt Borja
  2026-04-01 18:30   ` Rong Zhang
  2026-03-31 18:12 ` [PATCH v6 08/13] platform/x86: lenovo-wmi-other: Add GPU " Derek J. Clark
                   ` (6 subsequent siblings)
  13 siblings, 2 replies; 33+ messages in thread
From: Derek J. Clark @ 2026-03-31 18:12 UTC (permalink / raw)
  To: Ilpo Järvinen, Hans de Goede
  Cc: Mark Pearson, Armin Wolf, Jonathan Corbet, Rong Zhang, Kurt Borja,
	Derek J . Clark, platform-driver-x86, linux-kernel

Use an enum for all device ID's and CPU attribute feature ID's,
add missing CPU attributes.

Reviewed-by: Rong Zhang <i@rong.moe>
Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
---
v4:
  - Align type ID defines.
  - Align CPU feature enum values.
  - remove cpu_oc_stat from Documentation.
v3:
  - Remove cpu_oc_stat.
---
 .../wmi/devices/lenovo-wmi-other.rst          |   9 ++
 drivers/platform/x86/lenovo/wmi-capdata.h     |   5 +-
 drivers/platform/x86/lenovo/wmi-other.c       | 101 +++++++++++++++++-
 3 files changed, 109 insertions(+), 6 deletions(-)

diff --git a/Documentation/wmi/devices/lenovo-wmi-other.rst b/Documentation/wmi/devices/lenovo-wmi-other.rst
index 01d471156738..82c17361e749 100644
--- a/Documentation/wmi/devices/lenovo-wmi-other.rst
+++ b/Documentation/wmi/devices/lenovo-wmi-other.rst
@@ -68,9 +68,18 @@ Each attribute has the following properties:
  - type
 
 The following firmware-attributes are implemented:
+ - cpu_temp: CPU Thermal Load Limit
+ - ppt_cpu_cl: CPU Cross Loading Power Limit
+ - ppt_pl1_apu_spl: Platform Profile Tracking APU Sustained Power Limit
  - ppt_pl1_spl: Platform Profile Tracking Sustained Power Limit
+ - ppt_pl1_spl_cl: Platform Profile Tracking Cross Loading Sustained Power Limit
+ - ppt_pl1_tau: Exceed Duration for Platform Profile Tracking Sustained Power Limit
  - ppt_pl2_sppt: Platform Profile Tracking Slow Package Power Tracking
+ - ppt_pl2_sppt_cl: Platform Profile Tracking Cross Loading Slow Package Tracking
  - ppt_pl3_fppt: Platform Profile Tracking Fast Package Power Tracking
+ - ppt_pl3_fppt_cl: Platform Profile Tracking Cross Loading Fast Package Power Tracking
+ - ppt_pl4_ipl: Platform Profile Trakcing Instantaneous Power Limit
+ - ppt_pl4_ipl_cl: Platform Profile Tracking Cross Loading Instantaneous Power Limit
 
 LENOVO_FAN_TEST_DATA
 -------------------------
diff --git a/drivers/platform/x86/lenovo/wmi-capdata.h b/drivers/platform/x86/lenovo/wmi-capdata.h
index 1388eaf4ab4a..f2d45cd7a188 100644
--- a/drivers/platform/x86/lenovo/wmi-capdata.h
+++ b/drivers/platform/x86/lenovo/wmi-capdata.h
@@ -18,7 +18,10 @@
 #define LWMI_ATTR_MODE_ID_MASK	GENMASK(15, 8)
 #define LWMI_ATTR_TYPE_ID_MASK	GENMASK(7, 0)
 
-#define LWMI_DEVICE_ID_FAN	0x04
+enum lwmi_device_id {
+	LWMI_DEVICE_ID_CPU = 0x01,
+	LWMI_DEVICE_ID_FAN = 0x04,
+};
 
 #define LWMI_TYPE_ID_NONE 0x00
 
diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
index f689d19d711a..508186c7410d 100644
--- a/drivers/platform/x86/lenovo/wmi-other.c
+++ b/drivers/platform/x86/lenovo/wmi-other.c
@@ -54,14 +54,21 @@
 
 #define LENOVO_OTHER_MODE_GUID "DC2A8805-3A8C-41BA-A6F7-092E0089CD3B"
 
-#define LWMI_DEVICE_ID_CPU 0x01
-
-#define LWMI_FEATURE_ID_CPU_SPPT 0x01
-#define LWMI_FEATURE_ID_CPU_SPL 0x02
-#define LWMI_FEATURE_ID_CPU_FPPT 0x03
+enum lwmi_feature_id_cpu {
+	LWMI_FEATURE_ID_CPU_SPPT =	0x01,
+	LWMI_FEATURE_ID_CPU_SPL =	0x02,
+	LWMI_FEATURE_ID_CPU_FPPT =	0x03,
+	LWMI_FEATURE_ID_CPU_TEMP =	0x04,
+	LWMI_FEATURE_ID_CPU_APU =	0x05,
+	LWMI_FEATURE_ID_CPU_CL =	0x06,
+	LWMI_FEATURE_ID_CPU_TAU =	0x07,
+	LWMI_FEATURE_ID_CPU_IPL =	0x09,
+};
 
 #define LWMI_FEATURE_ID_FAN_RPM 0x03
 
+#define LWMI_TYPE_ID_CROSSLOAD	0x01
+
 #define LWMI_FEATURE_VALUE_GET 17
 #define LWMI_FEATURE_VALUE_SET 18
 
@@ -557,18 +564,72 @@ static struct tunable_attr_01 ppt_pl1_spl = {
 	.type_id = LWMI_TYPE_ID_NONE,
 };
 
+static struct tunable_attr_01 ppt_pl1_spl_cl = {
+	.device_id = LWMI_DEVICE_ID_CPU,
+	.feature_id = LWMI_FEATURE_ID_CPU_SPL,
+	.type_id = LWMI_TYPE_ID_CROSSLOAD,
+};
+
 static struct tunable_attr_01 ppt_pl2_sppt = {
 	.device_id = LWMI_DEVICE_ID_CPU,
 	.feature_id = LWMI_FEATURE_ID_CPU_SPPT,
 	.type_id = LWMI_TYPE_ID_NONE,
 };
 
+static struct tunable_attr_01 ppt_pl2_sppt_cl = {
+	.device_id = LWMI_DEVICE_ID_CPU,
+	.feature_id = LWMI_FEATURE_ID_CPU_SPPT,
+	.type_id = LWMI_TYPE_ID_CROSSLOAD,
+};
+
 static struct tunable_attr_01 ppt_pl3_fppt = {
 	.device_id = LWMI_DEVICE_ID_CPU,
 	.feature_id = LWMI_FEATURE_ID_CPU_FPPT,
 	.type_id = LWMI_TYPE_ID_NONE,
 };
 
+static struct tunable_attr_01 ppt_pl3_fppt_cl = {
+	.device_id = LWMI_DEVICE_ID_CPU,
+	.feature_id = LWMI_FEATURE_ID_CPU_FPPT,
+	.type_id = LWMI_TYPE_ID_CROSSLOAD,
+};
+
+static struct tunable_attr_01 cpu_temp = {
+	.device_id = LWMI_DEVICE_ID_CPU,
+	.feature_id = LWMI_FEATURE_ID_CPU_TEMP,
+	.type_id = LWMI_TYPE_ID_NONE,
+};
+
+static struct tunable_attr_01 ppt_pl1_apu_spl = {
+	.device_id = LWMI_DEVICE_ID_CPU,
+	.feature_id = LWMI_FEATURE_ID_CPU_APU,
+	.type_id = LWMI_TYPE_ID_NONE,
+};
+
+static struct tunable_attr_01 ppt_cpu_cl = {
+	.device_id = LWMI_DEVICE_ID_CPU,
+	.feature_id = LWMI_FEATURE_ID_CPU_CL,
+	.type_id = LWMI_TYPE_ID_NONE,
+};
+
+static struct tunable_attr_01 ppt_pl1_tau = {
+	.device_id = LWMI_DEVICE_ID_CPU,
+	.feature_id = LWMI_FEATURE_ID_CPU_TAU,
+	.type_id = LWMI_TYPE_ID_NONE,
+};
+
+static struct tunable_attr_01 ppt_pl4_ipl = {
+	.device_id = LWMI_DEVICE_ID_CPU,
+	.feature_id = LWMI_FEATURE_ID_CPU_IPL,
+	.type_id = LWMI_TYPE_ID_NONE,
+};
+
+static struct tunable_attr_01 ppt_pl4_ipl_cl = {
+	.device_id = LWMI_DEVICE_ID_CPU,
+	.feature_id = LWMI_FEATURE_ID_CPU_IPL,
+	.type_id = LWMI_TYPE_ID_CROSSLOAD,
+};
+
 struct capdata01_attr_group {
 	const struct attribute_group *attr_group;
 	struct tunable_attr_01 *tunable_attr;
@@ -794,6 +855,8 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
 	if (value < capdata.min_value || value > capdata.max_value)
 		return -EINVAL;
 
+	args.arg0 = lwmi_attr_id(tunable_attr->device_id, tunable_attr->feature_id,
+				 tunable_attr->cv_mode_id, tunable_attr->type_id);
 	args.arg1 = value;
 
 	ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_SET,
@@ -998,17 +1061,45 @@ static bool lwmi_attr_01_is_supported(struct tunable_attr_01 *tunable_attr)
 		.name = _fsname, .attrs = _attrname##_attrs               \
 	}
 
+LWMI_ATTR_GROUP_TUNABLE_CAP01(cpu_temp, "cpu_temp",
+			      "Set the CPU thermal load limit");
+LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_cpu_cl, "ppt_cpu_cl",
+			      "Set the CPU cross loading power limit");
+LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl1_apu_spl, "ppt_pl1_apu_spl",
+			      "Set the APU sustained power limit");
 LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl1_spl, "ppt_pl1_spl",
 			      "Set the CPU sustained power limit");
+LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl1_spl_cl, "ppt_pl1_spl_cl",
+			      "Set the CPU cross loading sustained power limit");
 LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl2_sppt, "ppt_pl2_sppt",
 			      "Set the CPU slow package power tracking limit");
+LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl2_sppt_cl, "ppt_pl2_sppt_cl",
+			      "Set the CPU cross loading slow package power tracking limit");
 LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl3_fppt, "ppt_pl3_fppt",
 			      "Set the CPU fast package power tracking limit");
+LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl3_fppt_cl, "ppt_pl3_fppt_cl",
+			      "Set the CPU cross loading fast package power tracking limit");
+LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl1_tau, "ppt_pl1_tau",
+			      "Set the CPU sustained power limit exceed duration");
+LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl4_ipl, "ppt_pl4_ipl",
+			      "Set the CPU instantaneous power limit");
+LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl4_ipl_cl, "ppt_pl4_ipl_cl",
+			      "Set the CPU cross loading instantaneous power limit");
+
 
 static struct capdata01_attr_group cd01_attr_groups[] = {
+	{ &cpu_temp_attr_group, &cpu_temp },
+	{ &ppt_cpu_cl_attr_group, &ppt_cpu_cl },
+	{ &ppt_pl1_apu_spl_attr_group, &ppt_pl1_apu_spl },
 	{ &ppt_pl1_spl_attr_group, &ppt_pl1_spl },
+	{ &ppt_pl1_spl_cl_attr_group, &ppt_pl1_spl_cl },
+	{ &ppt_pl1_tau_attr_group, &ppt_pl1_tau },
 	{ &ppt_pl2_sppt_attr_group, &ppt_pl2_sppt },
+	{ &ppt_pl2_sppt_cl_attr_group, &ppt_pl2_sppt_cl },
 	{ &ppt_pl3_fppt_attr_group, &ppt_pl3_fppt },
+	{ &ppt_pl3_fppt_cl_attr_group, &ppt_pl3_fppt_cl },
+	{ &ppt_pl4_ipl_attr_group, &ppt_pl4_ipl },
+	{ &ppt_pl4_ipl_cl_attr_group, &ppt_pl4_ipl_cl },
 	{},
 };
 
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v6 08/13] platform/x86: lenovo-wmi-other: Add GPU tunable attributes
  2026-03-31 18:11 [PATCH v6 00/13] platform-x86: lenovo-wmi: Add fixes and enhancement Derek J. Clark
                   ` (6 preceding siblings ...)
  2026-03-31 18:12 ` [PATCH v6 07/13] platform/x86: lenovo-wmi-other: Add missing CPU tunable attributes Derek J. Clark
@ 2026-03-31 18:12 ` Derek J. Clark
  2026-03-31 22:29   ` Kurt Borja
  2026-04-01 18:30   ` Rong Zhang
  2026-03-31 18:12 ` [PATCH v6 09/13] platform/x86: lenovo-wmi-other: Rename LWMI_OM_FW_ATTR_BASE_PATH Derek J. Clark
                   ` (5 subsequent siblings)
  13 siblings, 2 replies; 33+ messages in thread
From: Derek J. Clark @ 2026-03-31 18:12 UTC (permalink / raw)
  To: Ilpo Järvinen, Hans de Goede
  Cc: Mark Pearson, Armin Wolf, Jonathan Corbet, Rong Zhang, Kurt Borja,
	Derek J . Clark, platform-driver-x86, linux-kernel

Use an enum for all GPU attribute feature ID's and add GPU attributes.

Reviewed-by: Rong Zhang <i@rong.moe>
Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
---
v4:
  - Align CPU feature enum values.
  - Remove gpu_oc_stat from Documentation.
v3:
  - Remove gpu_oc_stat.
---
 .../wmi/devices/lenovo-wmi-other.rst          |  10 ++
 drivers/platform/x86/lenovo/wmi-capdata.h     |   1 +
 drivers/platform/x86/lenovo/wmi-other.c       | 105 ++++++++++++++++++
 3 files changed, 116 insertions(+)

diff --git a/Documentation/wmi/devices/lenovo-wmi-other.rst b/Documentation/wmi/devices/lenovo-wmi-other.rst
index 82c17361e749..bfbea59637bd 100644
--- a/Documentation/wmi/devices/lenovo-wmi-other.rst
+++ b/Documentation/wmi/devices/lenovo-wmi-other.rst
@@ -69,6 +69,16 @@ Each attribute has the following properties:
 
 The following firmware-attributes are implemented:
  - cpu_temp: CPU Thermal Load Limit
+ - dgpu_boost_clk: Dedicated GPU Boost Clock
+ - dgpu_enable: Dedicated GPU Enabled Status
+ - gpu_didvid: GPU Device Identifier and Vendor Identifier
+ - gpu_mode: GPU Mode by Power Limit
+ - gpu_nv_ac_offset: Nvidia GPU AC Total Processing Power Baseline Offset
+ - gpu_nv_bpl: Nvidia GPU Base Power Limit
+ - gpu_nv_cpu_boost: Nvidia GPU to CPU Dynamic Boost Limit
+ - gpu_nv_ctgp: Nvidia GPU Configurable Total Graphics Power
+ - gpu_nv_ppab: Nvidia GPU Power Performance Aware Boost Limit
+ - gpu_temp: GPU Thermal Load Limit
  - ppt_cpu_cl: CPU Cross Loading Power Limit
  - ppt_pl1_apu_spl: Platform Profile Tracking APU Sustained Power Limit
  - ppt_pl1_spl: Platform Profile Tracking Sustained Power Limit
diff --git a/drivers/platform/x86/lenovo/wmi-capdata.h b/drivers/platform/x86/lenovo/wmi-capdata.h
index f2d45cd7a188..891b12ca1db6 100644
--- a/drivers/platform/x86/lenovo/wmi-capdata.h
+++ b/drivers/platform/x86/lenovo/wmi-capdata.h
@@ -20,6 +20,7 @@
 
 enum lwmi_device_id {
 	LWMI_DEVICE_ID_CPU = 0x01,
+	LWMI_DEVICE_ID_GPU = 0x02,
 	LWMI_DEVICE_ID_FAN = 0x04,
 };
 
diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
index 508186c7410d..2792671ac1de 100644
--- a/drivers/platform/x86/lenovo/wmi-other.c
+++ b/drivers/platform/x86/lenovo/wmi-other.c
@@ -65,6 +65,19 @@ enum lwmi_feature_id_cpu {
 	LWMI_FEATURE_ID_CPU_IPL =	0x09,
 };
 
+enum lwmi_feature_id_gpu {
+	LWMI_FEATURE_ID_GPU_NV_PPAB =		0x01,
+	LWMI_FEATURE_ID_GPU_NV_CTGP =		0x02,
+	LWMI_FEATURE_ID_GPU_TEMP =		0x03,
+	LWMI_FEATURE_ID_GPU_AC_OFFSET =		0x04,
+	LWMI_FEATURE_ID_DGPU_BOOST_CLK =	0x06,
+	LWMI_FEATURE_ID_DGPU_EN =		0x07,
+	LWMI_FEATURE_ID_GPU_MODE =		0x08,
+	LWMI_FEATURE_ID_DGPU_DIDVID =		0x09,
+	LWMI_FEATURE_ID_GPU_NV_BPL =		0x0a,
+	LWMI_FEATURE_ID_GPU_NV_CPU_BOOST =	0x0b,
+};
+
 #define LWMI_FEATURE_ID_FAN_RPM 0x03
 
 #define LWMI_TYPE_ID_CROSSLOAD	0x01
@@ -630,6 +643,66 @@ static struct tunable_attr_01 ppt_pl4_ipl_cl = {
 	.type_id = LWMI_TYPE_ID_CROSSLOAD,
 };
 
+static struct tunable_attr_01 gpu_nv_ppab = {
+	.device_id = LWMI_DEVICE_ID_GPU,
+	.feature_id = LWMI_FEATURE_ID_GPU_NV_PPAB,
+	.type_id = LWMI_TYPE_ID_NONE,
+};
+
+static struct tunable_attr_01 gpu_nv_ctgp = {
+	.device_id = LWMI_DEVICE_ID_GPU,
+	.feature_id = LWMI_FEATURE_ID_GPU_NV_CTGP,
+	.type_id = LWMI_TYPE_ID_NONE,
+};
+
+static struct tunable_attr_01 gpu_temp = {
+	.device_id = LWMI_DEVICE_ID_GPU,
+	.feature_id = LWMI_FEATURE_ID_GPU_TEMP,
+	.type_id = LWMI_TYPE_ID_NONE,
+};
+
+static struct tunable_attr_01 gpu_nv_ac_offset = {
+	.device_id = LWMI_DEVICE_ID_GPU,
+	.feature_id = LWMI_FEATURE_ID_GPU_AC_OFFSET,
+	.type_id = LWMI_TYPE_ID_NONE,
+};
+
+static struct tunable_attr_01 dgpu_boost_clk = {
+	.device_id = LWMI_DEVICE_ID_GPU,
+	.feature_id = LWMI_FEATURE_ID_DGPU_BOOST_CLK,
+	.type_id = LWMI_TYPE_ID_NONE,
+};
+
+static struct tunable_attr_01 dgpu_enable = {
+	.device_id = LWMI_DEVICE_ID_GPU,
+	.feature_id = LWMI_FEATURE_ID_DGPU_EN,
+	.type_id = LWMI_TYPE_ID_NONE,
+};
+
+static struct tunable_attr_01 gpu_mode = {
+	.device_id = LWMI_DEVICE_ID_GPU,
+	.feature_id = LWMI_FEATURE_ID_GPU_MODE,
+	.type_id = LWMI_TYPE_ID_NONE,
+};
+
+static struct tunable_attr_01 dgpu_didvid = {
+	.device_id = LWMI_DEVICE_ID_GPU,
+	.feature_id = LWMI_FEATURE_ID_DGPU_DIDVID,
+	.type_id = LWMI_TYPE_ID_NONE,
+};
+
+static struct tunable_attr_01 gpu_nv_bpl = {
+	.device_id = LWMI_DEVICE_ID_GPU,
+	.feature_id = LWMI_FEATURE_ID_GPU_NV_BPL,
+	.type_id = LWMI_TYPE_ID_NONE,
+};
+
+static struct tunable_attr_01 gpu_nv_cpu_boost = {
+	.device_id = LWMI_DEVICE_ID_GPU,
+	.feature_id = LWMI_FEATURE_ID_GPU_NV_CPU_BOOST,
+	.type_id = LWMI_TYPE_ID_NONE,
+};
+
 struct capdata01_attr_group {
 	const struct attribute_group *attr_group;
 	struct tunable_attr_01 *tunable_attr;
@@ -1061,6 +1134,7 @@ static bool lwmi_attr_01_is_supported(struct tunable_attr_01 *tunable_attr)
 		.name = _fsname, .attrs = _attrname##_attrs               \
 	}
 
+/* CPU tunable attributes */
 LWMI_ATTR_GROUP_TUNABLE_CAP01(cpu_temp, "cpu_temp",
 			      "Set the CPU thermal load limit");
 LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_cpu_cl, "ppt_cpu_cl",
@@ -1086,9 +1160,40 @@ LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl4_ipl, "ppt_pl4_ipl",
 LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl4_ipl_cl, "ppt_pl4_ipl_cl",
 			      "Set the CPU cross loading instantaneous power limit");
 
+/* GPU tunable attributes */
+LWMI_ATTR_GROUP_TUNABLE_CAP01(dgpu_boost_clk, "gpu_boost_clk",
+			      "Set the dedicated GPU boost clock");
+LWMI_ATTR_GROUP_TUNABLE_CAP01(dgpu_didvid, "gpu_didvid",
+			      "Get the GPU device identifier and vendor identifier");
+LWMI_ATTR_GROUP_TUNABLE_CAP01(dgpu_enable, "dgpu_enable",
+			      "Set the dedicated Nvidia GPU enabled status");
+LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_mode, "gpu_mode",
+			      "Set the GPU mode by power limit");
+LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_nv_ac_offset, "gpu_nv_ac_offset",
+			      "Set the Nvidia GPU AC total processing power baseline offset");
+LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_nv_bpl, "gpu_nv_bpl",
+			      "Set the Nvidia GPU base power limit");
+LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_nv_cpu_boost, "gpu_nv_cpu_boost",
+			      "Set the Nvidia GPU to CPU dynamic boost limit");
+LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_nv_ctgp, "gpu_nv_ctgp",
+			      "Set the GPU configurable total graphics power");
+LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_nv_ppab, "gpu_nv_ppab",
+			      "Set the Nvidia GPU power performance aware boost limit");
+LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_temp, "gpu_temp",
+			      "Set the GPU thermal load limit");
 
 static struct capdata01_attr_group cd01_attr_groups[] = {
 	{ &cpu_temp_attr_group, &cpu_temp },
+	{ &dgpu_boost_clk_attr_group, &dgpu_boost_clk },
+	{ &dgpu_didvid_attr_group, &dgpu_didvid },
+	{ &dgpu_enable_attr_group, &dgpu_enable },
+	{ &gpu_mode_attr_group, &gpu_mode },
+	{ &gpu_nv_ac_offset_attr_group, &gpu_nv_ac_offset },
+	{ &gpu_nv_bpl_attr_group, &gpu_nv_bpl },
+	{ &gpu_nv_cpu_boost_attr_group, &gpu_nv_cpu_boost },
+	{ &gpu_nv_ctgp_attr_group, &gpu_nv_ctgp },
+	{ &gpu_nv_ppab_attr_group, &gpu_nv_ppab },
+	{ &gpu_temp_attr_group, &gpu_temp },
 	{ &ppt_cpu_cl_attr_group, &ppt_cpu_cl },
 	{ &ppt_pl1_apu_spl_attr_group, &ppt_pl1_apu_spl },
 	{ &ppt_pl1_spl_attr_group, &ppt_pl1_spl },
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v6 09/13] platform/x86: lenovo-wmi-other: Rename LWMI_OM_FW_ATTR_BASE_PATH
  2026-03-31 18:11 [PATCH v6 00/13] platform-x86: lenovo-wmi: Add fixes and enhancement Derek J. Clark
                   ` (7 preceding siblings ...)
  2026-03-31 18:12 ` [PATCH v6 08/13] platform/x86: lenovo-wmi-other: Add GPU " Derek J. Clark
@ 2026-03-31 18:12 ` Derek J. Clark
  2026-03-31 18:12 ` [PATCH v6 10/13] platform/x86: lenovo-wmi-other: Add WMI battery charge limiting Derek J. Clark
                   ` (4 subsequent siblings)
  13 siblings, 0 replies; 33+ messages in thread
From: Derek J. Clark @ 2026-03-31 18:12 UTC (permalink / raw)
  To: Ilpo Järvinen, Hans de Goede
  Cc: Mark Pearson, Armin Wolf, Jonathan Corbet, Rong Zhang, Kurt Borja,
	Derek J . Clark, platform-driver-x86, linux-kernel

In the next patch a power supply extension is added which requires
a name attribute. Instead of creating another const macro with the
same information, rename LWMI_OM_FW_ATTR_BASE_PATH to
LWMI_OM_SYSFS_NAME.

Reviewed-by: Rong Zhang <i@rong.moe>
Tested-by: Rong Zhang <i@rong.moe>
Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
---
 drivers/platform/x86/lenovo/wmi-other.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
index 2792671ac1de..0366acf04ae5 100644
--- a/drivers/platform/x86/lenovo/wmi-other.c
+++ b/drivers/platform/x86/lenovo/wmi-other.c
@@ -95,7 +95,7 @@ enum lwmi_feature_id_gpu {
 	lwmi_attr_id(LWMI_DEVICE_ID_FAN, LWMI_FEATURE_ID_FAN_RPM, \
 		     LWMI_GZ_THERMAL_MODE_NONE, LWMI_FAN_ID(x))
 
-#define LWMI_OM_FW_ATTR_BASE_PATH "lenovo-wmi-other"
+#define LWMI_OM_SYSFS_NAME "lenovo-wmi-other"
 #define LWMI_OM_HWMON_NAME "lenovo_wmi_other"
 
 static BLOCKING_NOTIFIER_HEAD(om_chain_head);
@@ -1225,8 +1225,7 @@ static int lwmi_om_fw_attr_add(struct lwmi_om_priv *priv)
 
 	priv->fw_attr_dev = device_create(&firmware_attributes_class, NULL,
 					  MKDEV(0, 0), NULL, "%s-%u",
-					  LWMI_OM_FW_ATTR_BASE_PATH,
-					  priv->ida_id);
+					  LWMI_OM_SYSFS_NAME, priv->ida_id);
 	if (IS_ERR(priv->fw_attr_dev)) {
 		err = PTR_ERR(priv->fw_attr_dev);
 		goto err_free_ida;
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v6 10/13] platform/x86: lenovo-wmi-other: Add WMI battery charge limiting
  2026-03-31 18:11 [PATCH v6 00/13] platform-x86: lenovo-wmi: Add fixes and enhancement Derek J. Clark
                   ` (8 preceding siblings ...)
  2026-03-31 18:12 ` [PATCH v6 09/13] platform/x86: lenovo-wmi-other: Rename LWMI_OM_FW_ATTR_BASE_PATH Derek J. Clark
@ 2026-03-31 18:12 ` Derek J. Clark
  2026-04-01 18:32   ` Rong Zhang
  2026-03-31 18:12 ` [PATCH v6 11/13] platform/x86: lenovo: Decouple lenovo-wmi-gamezone and lenovo-wmi-other Derek J. Clark
                   ` (3 subsequent siblings)
  13 siblings, 1 reply; 33+ messages in thread
From: Derek J. Clark @ 2026-03-31 18:12 UTC (permalink / raw)
  To: Ilpo Järvinen, Hans de Goede
  Cc: Mark Pearson, Armin Wolf, Jonathan Corbet, Rong Zhang, Kurt Borja,
	Derek J . Clark, platform-driver-x86, linux-kernel

Add charge-type power supply extension for devices that support WMI based
charge enable/disable.

Lenovo Legion devices that implement WMI function and capdata ID
0x03010001 in their BIOS are able to enable or disable charging at 80%
through the lenovo-wmi-other interface. Add a charge_type power supply
extension to expose this capability to the sysfs.

The ideapad_laptop driver can also provide the charge_type attribute. To
avoid conflicts between the drivers, get the acpi_handle and do the same
check that ideapad_laptop does when it enables the feature. If the
feature is supported in ideapad_laptop, abort adding the extension from
lenovo-wmi-other. The ACPI method is more reliable when both are
present, from my testing, so we can prefer that implementation and do
not need to worry about de-conflicting from inside that driver. A new
module parameter, force_load_psy_ext, is provided to bypass this ACPI
check, if desired.

Reviewed-by: Rong Zhang <i@rong.moe>
Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
---
v6:
  - Check feature flags to determine if the extension should be loaded
    and if it is writable.
  - Zero initialize wmi_method_args_32.
  - Fix formatting.
v5:
  - Use switch statement instead of if for battery charge state set/get.
  - use force_load_psy_ext to skip all ACPI interactions.
  - Various formatting fixes.
v4:
  - Remove unused defines.
  - Disambiguate charging defines by renaming them to be more consistent
    with the kernel modes they represent.
  - Add module parameter to ignore ACPI checks.
  - Don't fail if the ACPI handle isn't found, skip the ACPI check
    instead.
---
 drivers/platform/x86/lenovo/Kconfig       |   1 +
 drivers/platform/x86/lenovo/wmi-capdata.h |   1 +
 drivers/platform/x86/lenovo/wmi-other.c   | 274 +++++++++++++++++++++-
 3 files changed, 275 insertions(+), 1 deletion(-)

diff --git a/drivers/platform/x86/lenovo/Kconfig b/drivers/platform/x86/lenovo/Kconfig
index f885127b007f..75a8b144b0da 100644
--- a/drivers/platform/x86/lenovo/Kconfig
+++ b/drivers/platform/x86/lenovo/Kconfig
@@ -263,6 +263,7 @@ config LENOVO_WMI_GAMEZONE
 config LENOVO_WMI_TUNING
 	tristate "Lenovo Other Mode WMI Driver"
 	depends on ACPI_WMI
+	depends on ACPI_BATTERY
 	select HWMON
 	select FW_ATTR_CLASS
 	select LENOVO_WMI_CAPDATA
diff --git a/drivers/platform/x86/lenovo/wmi-capdata.h b/drivers/platform/x86/lenovo/wmi-capdata.h
index 891b12ca1db6..e0a30f2c0c87 100644
--- a/drivers/platform/x86/lenovo/wmi-capdata.h
+++ b/drivers/platform/x86/lenovo/wmi-capdata.h
@@ -21,6 +21,7 @@
 enum lwmi_device_id {
 	LWMI_DEVICE_ID_CPU = 0x01,
 	LWMI_DEVICE_ID_GPU = 0x02,
+	LWMI_DEVICE_ID_PSU = 0x03,
 	LWMI_DEVICE_ID_FAN = 0x04,
 };
 
diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
index 0366acf04ae5..e0633c42972c 100644
--- a/drivers/platform/x86/lenovo/wmi-other.c
+++ b/drivers/platform/x86/lenovo/wmi-other.c
@@ -43,9 +43,12 @@
 #include <linux/module.h>
 #include <linux/notifier.h>
 #include <linux/platform_profile.h>
+#include <linux/power_supply.h>
 #include <linux/types.h>
 #include <linux/wmi.h>
 
+#include <acpi/battery.h>
+
 #include "wmi-capdata.h"
 #include "wmi-events.h"
 #include "wmi-helpers.h"
@@ -78,9 +81,11 @@ enum lwmi_feature_id_gpu {
 	LWMI_FEATURE_ID_GPU_NV_CPU_BOOST =	0x0b,
 };
 
-#define LWMI_FEATURE_ID_FAN_RPM 0x03
+#define LWMI_FEATURE_ID_FAN_RPM		0x03
+#define LWMI_FEATURE_ID_PSU_CHARGE_TYPE	0x01
 
 #define LWMI_TYPE_ID_CROSSLOAD	0x01
+#define LWMI_TYPE_ID_PSU_AC	0x01
 
 #define LWMI_FEATURE_VALUE_GET 17
 #define LWMI_FEATURE_VALUE_SET 18
@@ -91,10 +96,17 @@ enum lwmi_feature_id_gpu {
 
 #define LWMI_FAN_DIV 100
 
+#define LWMI_CHARGE_TYPE_STANDARD	0x00
+#define LWMI_CHARGE_TYPE_LONGLIFE	0x01
+
 #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))
 
+#define LWMI_ATTR_ID_PSU(feat, type)			\
+	lwmi_attr_id(LWMI_DEVICE_ID_PSU, feat,		\
+		     LWMI_GZ_THERMAL_MODE_NONE, type)
+
 #define LWMI_OM_SYSFS_NAME "lenovo-wmi-other"
 #define LWMI_OM_HWMON_NAME "lenovo_wmi_other"
 
@@ -136,6 +148,8 @@ struct lwmi_om_priv {
 		bool capdata00_collected : 1;
 		bool capdata_fan_collected : 1;
 	} fan_flags;
+
+	struct acpi_battery_hook battery_hook;
 };
 
 /*
@@ -560,6 +574,263 @@ static void lwmi_om_fan_info_collect_cd_fan(struct device *dev, struct cd_list *
 	lwmi_om_hwmon_add(priv);
 }
 
+/* ======== Power Supply Extension (component: lenovo-wmi-capdata 00) ======== */
+
+/**
+ * lwmi_psy_ext_get_prop() - Get a power_supply_ext property
+ * @ps: The battery that was extended
+ * @ext: The extension
+ * @ext_data: Pointer the lwmi_om_priv drvdata
+ * @prop: The property to read
+ * @val: The value to return
+ *
+ * Writes the given value to the power_supply_ext property
+ *
+ * Return: 0 on success, or an error
+ */
+static int lwmi_psy_ext_get_prop(struct power_supply *ps,
+				 const struct power_supply_ext *ext,
+				 void *ext_data,
+				 enum power_supply_property prop,
+				 union power_supply_propval *val)
+{
+	struct wmi_method_args_32 args = { 0x0, 0x0 };
+	struct lwmi_om_priv *priv = ext_data;
+	u32 retval;
+	int ret;
+
+	args.arg0 = LWMI_ATTR_ID_PSU(LWMI_FEATURE_ID_PSU_CHARGE_TYPE, LWMI_TYPE_ID_PSU_AC);
+
+	ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_GET,
+				    (unsigned char *)&args, sizeof(args),
+				    &retval);
+	if (ret)
+		return ret;
+
+	dev_dbg(&priv->wdev->dev, "Got return value %#x for property %#x\n", retval, prop);
+
+	switch (retval) {
+	case LWMI_CHARGE_TYPE_LONGLIFE:
+		val->intval = POWER_SUPPLY_CHARGE_TYPE_LONGLIFE;
+		break;
+	case LWMI_CHARGE_TYPE_STANDARD:
+		val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
+		break;
+	default:
+		dev_err(&priv->wdev->dev, "Got invalid charge value: %#x\n", retval);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * lwmi_psy_ext_set_prop() - Set a power_supply_ext property
+ * @ps: The battery that was extended
+ * @ext: The extension
+ * @ext_data: Pointer the lwmi_om_priv drvdata
+ * @prop: The property to write
+ * @val: The value to write
+ *
+ * Writes the given value to the power_supply_ext property
+ *
+ * Return: 0 on success, or an error
+ */
+static int lwmi_psy_ext_set_prop(struct power_supply *ps,
+				 const struct power_supply_ext *ext,
+				 void *ext_data,
+				 enum power_supply_property prop,
+				 const union power_supply_propval *val)
+{
+	struct wmi_method_args_32 args = { 0x0, 0x0 };
+	struct lwmi_om_priv *priv = ext_data;
+
+	args.arg0 = LWMI_ATTR_ID_PSU(LWMI_FEATURE_ID_PSU_CHARGE_TYPE, LWMI_TYPE_ID_PSU_AC);
+	switch (val->intval) {
+	case POWER_SUPPLY_CHARGE_TYPE_LONGLIFE:
+		args.arg1 = LWMI_CHARGE_TYPE_LONGLIFE;
+		break;
+	case POWER_SUPPLY_CHARGE_TYPE_STANDARD:
+		args.arg1 = LWMI_CHARGE_TYPE_STANDARD;
+		break;
+	default:
+		dev_err(&priv->wdev->dev, "Got invalid charge value: %#x\n", val->intval);
+		return -EINVAL;
+	}
+
+	dev_dbg(&priv->wdev->dev, "Attempting to set %#010x for property %#x to %#x\n",
+		args.arg0, prop, args.arg1);
+
+	return lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_SET,
+				     (unsigned char *)&args, sizeof(args), NULL);
+}
+
+/**
+ * lwmi_psy_prop_is_supported() - Determine if the property is supported
+ * @priv: Pointer the lwmi_om_priv drvdata
+ *
+ * Checks capdata 00 to determine if the property is supported.
+ *
+ * Return: true if readable, or false
+ */
+static bool lwmi_psy_prop_is_supported(struct lwmi_om_priv *priv)
+{
+	u32 attribute_id = LWMI_ATTR_ID_PSU(LWMI_FEATURE_ID_PSU_CHARGE_TYPE, LWMI_TYPE_ID_PSU_AC);
+	struct capdata00 capdata;
+	int ret;
+
+	ret = lwmi_cd00_get_data(priv->cd00_list, attribute_id, &capdata);
+	if (ret)
+		return false;
+
+	dev_dbg(&priv->wdev->dev, "Battery charge mode (%#010x) support level: %#x\n",
+		attribute_id, capdata.supported);
+
+	return ((capdata.supported & LWMI_SUPP_VALID) && (capdata.supported & LWMI_SUPP_GET));
+}
+
+/**
+ * lwmi_psy_prop_is_writeable() - Determine if the property is writeable
+ * @ps: The battery that was extended
+ * @ext: The extension
+ * @ext_data: Pointer the lwmi_om_priv drvdata
+ * @prop: The property to check
+ *
+ * Checks capdata 00 to determine if the property is writable.
+ *
+ * Return: true if writable, or false
+ */
+static int lwmi_psy_prop_is_writeable(struct power_supply *ps,
+				      const struct power_supply_ext *ext,
+				      void *ext_data,
+				      enum power_supply_property prop)
+{
+	u32 attribute_id = LWMI_ATTR_ID_PSU(LWMI_FEATURE_ID_PSU_CHARGE_TYPE, LWMI_TYPE_ID_PSU_AC);
+	struct lwmi_om_priv *priv = ext_data;
+	struct capdata00 capdata;
+	int ret;
+
+	ret = lwmi_cd00_get_data(priv->cd00_list, attribute_id, &capdata);
+	if (ret)
+		return false;
+
+	return !!(capdata.supported & LWMI_SUPP_SET);
+}
+
+static const enum power_supply_property lwmi_psy_ext_props[] = {
+	POWER_SUPPLY_PROP_CHARGE_TYPES,
+};
+
+static const struct power_supply_ext lwmi_psy_ext = {
+	.name			= LWMI_OM_SYSFS_NAME,
+	.properties		= lwmi_psy_ext_props,
+	.num_properties		= ARRAY_SIZE(lwmi_psy_ext_props),
+	.charge_types		= (BIT(POWER_SUPPLY_CHARGE_TYPE_STANDARD) |
+				   BIT(POWER_SUPPLY_CHARGE_TYPE_LONGLIFE)),
+	.get_property		= lwmi_psy_ext_get_prop,
+	.set_property		= lwmi_psy_ext_set_prop,
+	.property_is_writeable	= lwmi_psy_prop_is_writeable,
+};
+
+/**
+ * lwmi_add_battery() - Connect the power_supply_ext
+ * @battery: The battery to extend
+ * @hook: The driver hook used to extend the battery
+ *
+ * Return: 0 on success, or an error.
+ */
+static int lwmi_add_battery(struct power_supply *battery, struct acpi_battery_hook *hook)
+{
+	struct lwmi_om_priv *priv = container_of(hook, struct lwmi_om_priv, battery_hook);
+
+	return power_supply_register_extension(battery, &lwmi_psy_ext, &priv->wdev->dev, priv);
+}
+
+/**
+ * lwmi_remove_battery() - Disconnect the power_supply_ext
+ * @battery: The battery that was extended
+ * @hook: The driver hook used to extend the battery
+ *
+ * Return: 0 on success, or an error.
+ */
+static int lwmi_remove_battery(struct power_supply *battery, struct acpi_battery_hook *hook)
+{
+	power_supply_unregister_extension(battery, &lwmi_psy_ext);
+	return 0;
+}
+
+/**
+ * lwmi_acpi_match() - Attempts to return the ideapad acpi handle
+ * @handle: The ACPI handle that manages battery charging
+ * @lvl: Unused
+ * @context: Void pointer to the acpi_handle object to return
+ * @retval: Unused
+ *
+ * Checks if the ideapad_laptop driver is going to manage charge_type first,
+ * then if not, hooks the battery to our WMI methods.
+ *
+ * Return: AE_CTRL_TERMINATE if found, AE_OK if not found.
+ */
+static acpi_status lwmi_acpi_match(acpi_handle handle, u32 lvl,
+				   void *context, void **retval)
+{
+	acpi_handle *ahand = context;
+
+	if (!handle)
+		return AE_OK;
+
+	*ahand = handle;
+
+	return AE_CTRL_TERMINATE;
+}
+
+static bool force_load_psy_ext;
+module_param(force_load_psy_ext, bool, 0444);
+MODULE_PARM_DESC(force_load_psy_ext,
+	"This option will skip checking if the ideapad_laptop driver will conflict "
+	"with adding an extension to set the battery charge type. It is recommended "
+	"to blacklist the ideapad driver before using this option.");
+
+/**
+ * lwmi_om_psy_ext_init() - Hooks power supply extension to device battery
+ * @priv: Driver private data
+ *
+ * Checks if the ideapad_laptop driver is going to manage charge_type first,
+ * then if not, hooks the battery to our WMI methods.
+ */
+static void lwmi_om_psy_ext_init(struct lwmi_om_priv *priv)
+{
+	static const char * const ideapad_hid = "VPC2004";
+	acpi_handle handle = NULL;
+	int ret;
+
+	/* Deconflict ideapad_laptop driver */
+	if (force_load_psy_ext)
+		goto load_psy_ext;
+
+	if (!lwmi_psy_prop_is_supported(priv))
+		return;
+
+	ret = acpi_get_devices(ideapad_hid, lwmi_acpi_match, &handle, NULL);
+	if (ret)
+		return;
+
+	if (handle && acpi_has_method(handle, "GBMD") && acpi_has_method(handle, "SBMC")) {
+		dev_dbg(&priv->wdev->dev, "ideapad_laptop driver manages battery for device\n");
+		return;
+	}
+
+load_psy_ext:
+	/* Add battery hooks */
+	priv->battery_hook.add_battery = lwmi_add_battery;
+	priv->battery_hook.remove_battery = lwmi_remove_battery;
+	priv->battery_hook.name = "Lenovo WMI Other Battery Extension";
+
+	ret = devm_battery_hook_register(&priv->wdev->dev, &priv->battery_hook);
+	if (ret)
+		dev_err(&priv->wdev->dev, "Error during battery hook: %i\n", ret);
+}
+
 /* ======== fw_attributes (component: lenovo-wmi-capdata 01) ======== */
 
 struct tunable_attr_01 {
@@ -1317,6 +1588,7 @@ static int lwmi_om_master_bind(struct device *dev)
 		return -ENODEV;
 
 	lwmi_om_fan_info_collect_cd00(priv);
+	lwmi_om_psy_ext_init(priv);
 
 	return lwmi_om_fw_attr_add(priv);
 }
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v6 11/13] platform/x86: lenovo: Decouple lenovo-wmi-gamezone and lenovo-wmi-other
  2026-03-31 18:11 [PATCH v6 00/13] platform-x86: lenovo-wmi: Add fixes and enhancement Derek J. Clark
                   ` (9 preceding siblings ...)
  2026-03-31 18:12 ` [PATCH v6 10/13] platform/x86: lenovo-wmi-other: Add WMI battery charge limiting Derek J. Clark
@ 2026-03-31 18:12 ` Derek J. Clark
  2026-04-01 18:34   ` Rong Zhang
  2026-03-31 18:12 ` [PATCH v6 12/13] platform/x86: lenovo-wmi-helpers: Add helper for creating per-device debugfs dir Derek J. Clark
                   ` (2 subsequent siblings)
  13 siblings, 1 reply; 33+ messages in thread
From: Derek J. Clark @ 2026-03-31 18:12 UTC (permalink / raw)
  To: Ilpo Järvinen, Hans de Goede
  Cc: Mark Pearson, Armin Wolf, Jonathan Corbet, Rong Zhang, Kurt Borja,
	Derek J . Clark, platform-driver-x86, linux-kernel, stable,
	kernel test robot

From: Rong Zhang <i@rong.moe>

Currently, lenovo-wmi-gamezone depends on lenovo-wmi-other as the former
imports symbols from the latter. The imported symbols are just used to
register a notifier block. However, there is no runtime dependency
between both drivers, and either of them can run without the other,
which is the major purpose of using the notifier framework.

Such a link-time dependency is non-optimal. A previous attempt to "fix"
it made LENOVO_WMI_GAMEZONE select LENOVO_WMI_TUNING, which was
fundamentally broken and resulted in undefined Kconfig behavior, as
`select' cannot be used on a symbol with potentially unmet dependencies.

Decouple both drivers by moving the thermal mode notifier chain to
lenovo-wmi-helpers. Methods for notifier block (un)registration are
exported for lenovo-wmi-gamezone, while a method for querying the
current thermal mode are exported for lenovo-wmi-other.

This turns the dependency graph from

            +------------ lenovo-wmi-gamezone
            |                     |
            v                     |
    lenovo-wmi-helpers            |
            ^                     |
            |                     V
            +------------ lenovo-wmi-other

into

            +------------ lenovo-wmi-gamezone
            |
            v
    lenovo-wmi-helpers
            ^
            |
            +------------ lenovo-wmi-other

To make it clear, the name of the notifier chain is also renamed from
`om_chain_head' to `tm_chain_head', indicating that it's used to query
the current thermal mode.

No functional change intended.

Fixes: 6e38b9fcbfa3 ("platform/x86: lenovo: gamezone needs "other mode"")
Cc: stable@vger.kernel.org
Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202603252259.gHvJDyh3-lkp@intel.com/
Closes: https://lore.kernel.org/oe-kbuild-all/202603260302.X0NjQOda-lkp@intel.com/
Signed-off-by: Rong Zhang <i@rong.moe>
Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
---
 drivers/platform/x86/lenovo/Kconfig        |   1 -
 drivers/platform/x86/lenovo/wmi-gamezone.c |   4 +-
 drivers/platform/x86/lenovo/wmi-helpers.c  | 101 ++++++++++++++++++++
 drivers/platform/x86/lenovo/wmi-helpers.h  |   8 ++
 drivers/platform/x86/lenovo/wmi-other.c    | 104 +--------------------
 drivers/platform/x86/lenovo/wmi-other.h    |  16 ----
 6 files changed, 112 insertions(+), 122 deletions(-)
 delete mode 100644 drivers/platform/x86/lenovo/wmi-other.h

diff --git a/drivers/platform/x86/lenovo/Kconfig b/drivers/platform/x86/lenovo/Kconfig
index 75a8b144b0da..b9a5d18caa1e 100644
--- a/drivers/platform/x86/lenovo/Kconfig
+++ b/drivers/platform/x86/lenovo/Kconfig
@@ -252,7 +252,6 @@ config LENOVO_WMI_GAMEZONE
 	select ACPI_PLATFORM_PROFILE
 	select LENOVO_WMI_EVENTS
 	select LENOVO_WMI_HELPERS
-	select LENOVO_WMI_TUNING
 	help
 	  Say Y here if you have a WMI aware Lenovo Legion device and would like to use the
 	  platform-profile firmware interface to manage power usage.
diff --git a/drivers/platform/x86/lenovo/wmi-gamezone.c b/drivers/platform/x86/lenovo/wmi-gamezone.c
index 602a48de1b4e..a614af8f08e8 100644
--- a/drivers/platform/x86/lenovo/wmi-gamezone.c
+++ b/drivers/platform/x86/lenovo/wmi-gamezone.c
@@ -22,7 +22,6 @@
 
 #include "wmi-events.h"
 #include "wmi-helpers.h"
-#include "wmi-other.h"
 
 #define LENOVO_GAMEZONE_GUID "887B54E3-DDDC-4B2C-8B88-68A26A8835D0"
 
@@ -384,7 +383,7 @@ static int lwmi_gz_probe(struct wmi_device *wdev, const void *context)
 		return ret;
 
 	priv->mode_nb.notifier_call = lwmi_gz_mode_call;
-	return devm_lwmi_om_register_notifier(&wdev->dev, &priv->mode_nb);
+	return devm_lwmi_tm_register_notifier(&wdev->dev, &priv->mode_nb);
 }
 
 static const struct wmi_device_id lwmi_gz_id_table[] = {
@@ -406,7 +405,6 @@ module_wmi_driver(lwmi_gz_driver);
 
 MODULE_IMPORT_NS("LENOVO_WMI_EVENTS");
 MODULE_IMPORT_NS("LENOVO_WMI_HELPERS");
-MODULE_IMPORT_NS("LENOVO_WMI_OTHER");
 MODULE_DEVICE_TABLE(wmi, lwmi_gz_id_table);
 MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@gmail.com>");
 MODULE_DESCRIPTION("Lenovo GameZone WMI Driver");
diff --git a/drivers/platform/x86/lenovo/wmi-helpers.c b/drivers/platform/x86/lenovo/wmi-helpers.c
index 7379defac500..e1cf869224d2 100644
--- a/drivers/platform/x86/lenovo/wmi-helpers.c
+++ b/drivers/platform/x86/lenovo/wmi-helpers.c
@@ -21,11 +21,15 @@
 #include <linux/errno.h>
 #include <linux/export.h>
 #include <linux/module.h>
+#include <linux/notifier.h>
 #include <linux/unaligned.h>
 #include <linux/wmi.h>
 
 #include "wmi-helpers.h"
 
+/* Thermal mode notifier chain. */
+static BLOCKING_NOTIFIER_HEAD(tm_chain_head);
+
 /**
  * lwmi_dev_evaluate_int() - Helper function for calling WMI methods that
  * return an integer.
@@ -84,6 +88,103 @@ int lwmi_dev_evaluate_int(struct wmi_device *wdev, u8 instance, u32 method_id,
 };
 EXPORT_SYMBOL_NS_GPL(lwmi_dev_evaluate_int, "LENOVO_WMI_HELPERS");
 
+/**
+ * lwmi_tm_register_notifier() - Add a notifier to the blocking notifier chain
+ * @nb: The notifier_block struct to register
+ *
+ * Call blocking_notifier_chain_register to register the notifier block to the
+ * thermal mode notifier chain.
+ *
+ * Return: 0 on success, %-EEXIST on error.
+ */
+int lwmi_tm_register_notifier(struct notifier_block *nb)
+{
+	return blocking_notifier_chain_register(&tm_chain_head, nb);
+}
+EXPORT_SYMBOL_NS_GPL(lwmi_tm_register_notifier, "LENOVO_WMI_HELPERS");
+
+/**
+ * lwmi_tm_unregister_notifier() - Remove a notifier from the blocking notifier
+ * chain.
+ * @nb: The notifier_block struct to register
+ *
+ * Call blocking_notifier_chain_unregister to unregister the notifier block from the
+ * thermal mode notifier chain.
+ *
+ * Return: 0 on success, %-ENOENT on error.
+ */
+int lwmi_tm_unregister_notifier(struct notifier_block *nb)
+{
+	return blocking_notifier_chain_unregister(&tm_chain_head, nb);
+}
+EXPORT_SYMBOL_NS_GPL(lwmi_tm_unregister_notifier, "LENOVO_WMI_HELPERS");
+
+/**
+ * devm_lwmi_tm_unregister_notifier() - Remove a notifier from the blocking
+ * notifier chain.
+ * @data: Void pointer to the notifier_block struct to register.
+ *
+ * Call lwmi_tm_unregister_notifier to unregister the notifier block from the
+ * thermal mode notifier chain.
+ *
+ * Return: 0 on success, %-ENOENT on error.
+ */
+static void devm_lwmi_tm_unregister_notifier(void *data)
+{
+	struct notifier_block *nb = data;
+
+	lwmi_tm_unregister_notifier(nb);
+}
+
+/**
+ * devm_lwmi_tm_register_notifier() - Add a notifier to the blocking notifier
+ * chain.
+ * @dev: The parent device of the notifier_block struct.
+ * @nb: The notifier_block struct to register
+ *
+ * Call lwmi_tm_register_notifier to register the notifier block to the
+ * thermal mode notifier chain. Then add devm_lwmi_tm_unregister_notifier
+ * as a device managed action to automatically unregister the notifier block
+ * upon parent device removal.
+ *
+ * Return: 0 on success, or an error code.
+ */
+int devm_lwmi_tm_register_notifier(struct device *dev,
+				   struct notifier_block *nb)
+{
+	int ret;
+
+	ret = lwmi_tm_register_notifier(nb);
+	if (ret < 0)
+		return ret;
+
+	return devm_add_action_or_reset(dev, devm_lwmi_tm_unregister_notifier,
+					nb);
+}
+EXPORT_SYMBOL_NS_GPL(devm_lwmi_tm_register_notifier, "LENOVO_WMI_HELPERS");
+
+/**
+ * lwmi_tm_notifier_call() - Call functions for the notifier call chain.
+ * @mode: Pointer to a thermal mode enum to retrieve the data from.
+ *
+ * Call blocking_notifier_call_chain to retrieve the thermal mode from the
+ * lenovo-wmi-gamezone driver.
+ *
+ * Return: 0 on success, or an error code.
+ */
+int lwmi_tm_notifier_call(enum thermal_mode *mode)
+{
+	int ret;
+
+	ret = blocking_notifier_call_chain(&tm_chain_head,
+					   LWMI_GZ_GET_THERMAL_MODE, &mode);
+	if ((ret & ~NOTIFY_STOP_MASK) != NOTIFY_OK)
+		return -EINVAL;
+
+	return 0;
+}
+EXPORT_SYMBOL_NS_GPL(lwmi_tm_notifier_call, "LENOVO_WMI_HELPERS");
+
 MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@gmail.com>");
 MODULE_DESCRIPTION("Lenovo WMI Helpers Driver");
 MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/lenovo/wmi-helpers.h b/drivers/platform/x86/lenovo/wmi-helpers.h
index 3364d8e152ca..ed7db3ebba6c 100644
--- a/drivers/platform/x86/lenovo/wmi-helpers.h
+++ b/drivers/platform/x86/lenovo/wmi-helpers.h
@@ -7,6 +7,8 @@
 
 #include <linux/types.h>
 
+struct device;
+struct notifier_block;
 struct wmi_device;
 
 struct wmi_method_args_32 {
@@ -30,4 +32,10 @@ enum thermal_mode {
 int lwmi_dev_evaluate_int(struct wmi_device *wdev, u8 instance, u32 method_id,
 			  unsigned char *buf, size_t size, u32 *retval);
 
+int lwmi_tm_register_notifier(struct notifier_block *nb);
+int lwmi_tm_unregister_notifier(struct notifier_block *nb);
+int devm_lwmi_tm_register_notifier(struct device *dev,
+				   struct notifier_block *nb);
+int lwmi_tm_notifier_call(enum thermal_mode *mode);
+
 #endif /* !_LENOVO_WMI_HELPERS_H_ */
diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
index e0633c42972c..d871ee02dfcb 100644
--- a/drivers/platform/x86/lenovo/wmi-other.c
+++ b/drivers/platform/x86/lenovo/wmi-other.c
@@ -41,7 +41,6 @@
 #include <linux/kobject.h>
 #include <linux/limits.h>
 #include <linux/module.h>
-#include <linux/notifier.h>
 #include <linux/platform_profile.h>
 #include <linux/power_supply.h>
 #include <linux/types.h>
@@ -52,7 +51,6 @@
 #include "wmi-capdata.h"
 #include "wmi-events.h"
 #include "wmi-helpers.h"
-#include "wmi-other.h"
 #include "../firmware_attributes_class.h"
 
 #define LENOVO_OTHER_MODE_GUID "DC2A8805-3A8C-41BA-A6F7-092E0089CD3B"
@@ -110,7 +108,6 @@ enum lwmi_feature_id_gpu {
 #define LWMI_OM_SYSFS_NAME "lenovo-wmi-other"
 #define LWMI_OM_HWMON_NAME "lenovo_wmi_other"
 
-static BLOCKING_NOTIFIER_HEAD(om_chain_head);
 static DEFINE_IDA(lwmi_om_ida);
 
 enum attribute_property {
@@ -138,7 +135,6 @@ struct lwmi_om_priv {
 	struct device *hwmon_dev;
 	struct device *fw_attr_dev;
 	struct kset *fw_attr_kset;
-	struct notifier_block nb;
 	struct wmi_device *wdev;
 	int ida_id;
 
@@ -979,102 +975,6 @@ struct capdata01_attr_group {
 	struct tunable_attr_01 *tunable_attr;
 };
 
-/**
- * lwmi_om_register_notifier() - Add a notifier to the blocking notifier chain
- * @nb: The notifier_block struct to register
- *
- * Call blocking_notifier_chain_register to register the notifier block to the
- * lenovo-wmi-other driver notifier chain.
- *
- * Return: 0 on success, %-EEXIST on error.
- */
-int lwmi_om_register_notifier(struct notifier_block *nb)
-{
-	return blocking_notifier_chain_register(&om_chain_head, nb);
-}
-EXPORT_SYMBOL_NS_GPL(lwmi_om_register_notifier, "LENOVO_WMI_OTHER");
-
-/**
- * lwmi_om_unregister_notifier() - Remove a notifier from the blocking notifier
- * chain.
- * @nb: The notifier_block struct to register
- *
- * Call blocking_notifier_chain_unregister to unregister the notifier block from the
- * lenovo-wmi-other driver notifier chain.
- *
- * Return: 0 on success, %-ENOENT on error.
- */
-int lwmi_om_unregister_notifier(struct notifier_block *nb)
-{
-	return blocking_notifier_chain_unregister(&om_chain_head, nb);
-}
-EXPORT_SYMBOL_NS_GPL(lwmi_om_unregister_notifier, "LENOVO_WMI_OTHER");
-
-/**
- * devm_lwmi_om_unregister_notifier() - Remove a notifier from the blocking
- * notifier chain.
- * @data: Void pointer to the notifier_block struct to register.
- *
- * Call lwmi_om_unregister_notifier to unregister the notifier block from the
- * lenovo-wmi-other driver notifier chain.
- *
- * Return: 0 on success, %-ENOENT on error.
- */
-static void devm_lwmi_om_unregister_notifier(void *data)
-{
-	struct notifier_block *nb = data;
-
-	lwmi_om_unregister_notifier(nb);
-}
-
-/**
- * devm_lwmi_om_register_notifier() - Add a notifier to the blocking notifier
- * chain.
- * @dev: The parent device of the notifier_block struct.
- * @nb: The notifier_block struct to register
- *
- * Call lwmi_om_register_notifier to register the notifier block to the
- * lenovo-wmi-other driver notifier chain. Then add devm_lwmi_om_unregister_notifier
- * as a device managed action to automatically unregister the notifier block
- * upon parent device removal.
- *
- * Return: 0 on success, or an error code.
- */
-int devm_lwmi_om_register_notifier(struct device *dev,
-				   struct notifier_block *nb)
-{
-	int ret;
-
-	ret = lwmi_om_register_notifier(nb);
-	if (ret < 0)
-		return ret;
-
-	return devm_add_action_or_reset(dev, devm_lwmi_om_unregister_notifier,
-					nb);
-}
-EXPORT_SYMBOL_NS_GPL(devm_lwmi_om_register_notifier, "LENOVO_WMI_OTHER");
-
-/**
- * lwmi_om_notifier_call() - Call functions for the notifier call chain.
- * @mode: Pointer to a thermal mode enum to retrieve the data from.
- *
- * Call blocking_notifier_call_chain to retrieve the thermal mode from the
- * lenovo-wmi-gamezone driver.
- *
- * Return: 0 on success, or an error code.
- */
-static int lwmi_om_notifier_call(enum thermal_mode *mode)
-{
-	int ret;
-
-	ret = blocking_notifier_call_chain(&om_chain_head,
-					   LWMI_GZ_GET_THERMAL_MODE, &mode);
-	if ((ret & ~NOTIFY_STOP_MASK) != NOTIFY_OK)
-		return -EINVAL;
-
-	return 0;
-}
-
 /* Attribute Methods */
 
 /**
@@ -1178,7 +1078,7 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
 	u32 value;
 	int ret;
 
-	ret = lwmi_om_notifier_call(&mode);
+	ret = lwmi_tm_notifier_call(&mode);
 	if (ret)
 		return ret;
 
@@ -1237,7 +1137,7 @@ static ssize_t attr_current_value_show(struct kobject *kobj,
 	int retval;
 	int ret;
 
-	ret = lwmi_om_notifier_call(&mode);
+	ret = lwmi_tm_notifier_call(&mode);
 	if (ret)
 		return ret;
 
diff --git a/drivers/platform/x86/lenovo/wmi-other.h b/drivers/platform/x86/lenovo/wmi-other.h
deleted file mode 100644
index 8ebf5602bb99..000000000000
--- a/drivers/platform/x86/lenovo/wmi-other.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-/* Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com> */
-
-#ifndef _LENOVO_WMI_OTHER_H_
-#define _LENOVO_WMI_OTHER_H_
-
-struct device;
-struct notifier_block;
-
-int lwmi_om_register_notifier(struct notifier_block *nb);
-int lwmi_om_unregister_notifier(struct notifier_block *nb);
-int devm_lwmi_om_register_notifier(struct device *dev,
-				   struct notifier_block *nb);
-
-#endif /* !_LENOVO_WMI_OTHER_H_ */
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v6 12/13] platform/x86: lenovo-wmi-helpers: Add helper for creating per-device debugfs dir
  2026-03-31 18:11 [PATCH v6 00/13] platform-x86: lenovo-wmi: Add fixes and enhancement Derek J. Clark
                   ` (10 preceding siblings ...)
  2026-03-31 18:12 ` [PATCH v6 11/13] platform/x86: lenovo: Decouple lenovo-wmi-gamezone and lenovo-wmi-other Derek J. Clark
@ 2026-03-31 18:12 ` Derek J. Clark
  2026-04-01 18:35   ` Rong Zhang
  2026-03-31 18:12 ` [PATCH v6 13/13] platform/x86: lenovo-wmi-capdata: Add debugfs file for dumping capdata Derek J. Clark
  2026-04-01 18:44 ` [PATCH v6 00/13] platform-x86: lenovo-wmi: Add fixes and enhancement Rong Zhang
  13 siblings, 1 reply; 33+ messages in thread
From: Derek J. Clark @ 2026-03-31 18:12 UTC (permalink / raw)
  To: Ilpo Järvinen, Hans de Goede
  Cc: Mark Pearson, Armin Wolf, Jonathan Corbet, Rong Zhang, Kurt Borja,
	Derek J . Clark, platform-driver-x86, linux-kernel

From: Rong Zhang <i@rong.moe>

We are about to add debugfs support for lenovo-wmi-capdata. Let's setup
a debugfs directory called "lenovo_wmi" for tidiness, so that any
lenovo-wmi-* device can put its subdirectory under the directory.
Subdirectories will be named after the corresponding WMI devices.

Signed-off-by: Rong Zhang <i@rong.moe>
Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
---
 drivers/platform/x86/lenovo/wmi-helpers.c | 34 +++++++++++++++++++++++
 drivers/platform/x86/lenovo/wmi-helpers.h |  2 ++
 2 files changed, 36 insertions(+)

diff --git a/drivers/platform/x86/lenovo/wmi-helpers.c b/drivers/platform/x86/lenovo/wmi-helpers.c
index e1cf869224d2..52a0c6a5a4bf 100644
--- a/drivers/platform/x86/lenovo/wmi-helpers.c
+++ b/drivers/platform/x86/lenovo/wmi-helpers.c
@@ -17,6 +17,8 @@
  */
 
 #include <linux/acpi.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
 #include <linux/cleanup.h>
 #include <linux/errno.h>
 #include <linux/export.h>
@@ -185,6 +187,38 @@ int lwmi_tm_notifier_call(enum thermal_mode *mode)
 }
 EXPORT_SYMBOL_NS_GPL(lwmi_tm_notifier_call, "LENOVO_WMI_HELPERS");
 
+static struct dentry *lwmi_debugfs_dir;
+
+/**
+ * lwmi_debugfs_create_dir() - Helper function for creating a debugfs directory
+ * for a device.
+ * @wdev: Pointer to the WMI device to be called.
+ *
+ * Caller must remove the directory with debugfs_remove_recursive() on device
+ * removal.
+ *
+ * Return: Pointer to the created directory.
+ */
+struct dentry *lwmi_debugfs_create_dir(struct wmi_device *wdev)
+{
+	return debugfs_create_dir(dev_name(&wdev->dev), lwmi_debugfs_dir);
+}
+EXPORT_SYMBOL_NS_GPL(lwmi_debugfs_create_dir, "LENOVO_WMI_HELPERS");
+
+static int __init lwmi_helpers_init(void)
+{
+	lwmi_debugfs_dir = debugfs_create_dir("lenovo_wmi", NULL);
+
+	return 0;
+}
+module_init(lwmi_helpers_init)
+
+static void __exit lwmi_helpers_exit(void)
+{
+	debugfs_remove_recursive(lwmi_debugfs_dir);
+}
+module_exit(lwmi_helpers_exit)
+
 MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@gmail.com>");
 MODULE_DESCRIPTION("Lenovo WMI Helpers Driver");
 MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/lenovo/wmi-helpers.h b/drivers/platform/x86/lenovo/wmi-helpers.h
index ed7db3ebba6c..039fe61003ce 100644
--- a/drivers/platform/x86/lenovo/wmi-helpers.h
+++ b/drivers/platform/x86/lenovo/wmi-helpers.h
@@ -16,6 +16,8 @@ struct wmi_method_args_32 {
 	u32 arg1;
 };
 
+struct dentry *lwmi_debugfs_create_dir(struct wmi_device *wdev);
+
 enum lwmi_event_type {
 	LWMI_GZ_GET_THERMAL_MODE = 0x01,
 };
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH v6 13/13] platform/x86: lenovo-wmi-capdata: Add debugfs file for dumping capdata
  2026-03-31 18:11 [PATCH v6 00/13] platform-x86: lenovo-wmi: Add fixes and enhancement Derek J. Clark
                   ` (11 preceding siblings ...)
  2026-03-31 18:12 ` [PATCH v6 12/13] platform/x86: lenovo-wmi-helpers: Add helper for creating per-device debugfs dir Derek J. Clark
@ 2026-03-31 18:12 ` Derek J. Clark
  2026-04-01 18:44 ` [PATCH v6 00/13] platform-x86: lenovo-wmi: Add fixes and enhancement Rong Zhang
  13 siblings, 0 replies; 33+ messages in thread
From: Derek J. Clark @ 2026-03-31 18:12 UTC (permalink / raw)
  To: Ilpo Järvinen, Hans de Goede
  Cc: Mark Pearson, Armin Wolf, Jonathan Corbet, Rong Zhang, Kurt Borja,
	Derek J . Clark, platform-driver-x86, linux-kernel

From: Rong Zhang <i@rong.moe>

The Lenovo GameZone/Other interfaces have some delicate divergences
among different devices. When making a bug report or adding support for
new devices/interfaces, capdata is the most important information to
cross-check with.

Add a debugfs file (lenovo_wmi/<device_name>/capdata), so that users can
dump capdata and include it in their reports.

Since `struct capdata01' is just an extension to `struct capdata00',
also converts the former to include the latter anonymously
(-fms-extensions, since v6.19). In this manner type casting won't be
confusing.

Signed-off-by: Rong Zhang <i@rong.moe>
Tested-by: Kurt Borja <kuurtb@gmail.com>
Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
---
 drivers/platform/x86/lenovo/Kconfig       |   1 +
 drivers/platform/x86/lenovo/wmi-capdata.c | 120 ++++++++++++++++++++++
 drivers/platform/x86/lenovo/wmi-capdata.h |   4 +-
 3 files changed, 122 insertions(+), 3 deletions(-)

diff --git a/drivers/platform/x86/lenovo/Kconfig b/drivers/platform/x86/lenovo/Kconfig
index b9a5d18caa1e..4443f40ef8aa 100644
--- a/drivers/platform/x86/lenovo/Kconfig
+++ b/drivers/platform/x86/lenovo/Kconfig
@@ -236,6 +236,7 @@ config YT2_1380
 config LENOVO_WMI_CAPDATA
 	tristate
 	depends on ACPI_WMI
+	depends on LENOVO_WMI_HELPERS
 
 config LENOVO_WMI_EVENTS
 	tristate
diff --git a/drivers/platform/x86/lenovo/wmi-capdata.c b/drivers/platform/x86/lenovo/wmi-capdata.c
index 169665be4dcf..b5e2c16d638f 100644
--- a/drivers/platform/x86/lenovo/wmi-capdata.c
+++ b/drivers/platform/x86/lenovo/wmi-capdata.c
@@ -31,6 +31,7 @@
 #include <linux/cleanup.h>
 #include <linux/component.h>
 #include <linux/container_of.h>
+#include <linux/debugfs.h>
 #include <linux/device.h>
 #include <linux/dev_printk.h>
 #include <linux/err.h>
@@ -42,6 +43,7 @@
 #include <linux/mutex_types.h>
 #include <linux/notifier.h>
 #include <linux/overflow.h>
+#include <linux/seq_file.h>
 #include <linux/stddef.h>
 #include <linux/types.h>
 #include <linux/wmi.h>
@@ -88,6 +90,7 @@ struct lwmi_cd_priv {
 	struct notifier_block acpi_nb; /* ACPI events */
 	struct wmi_device *wdev;
 	struct cd_list *list;
+	struct dentry *debugfs_dir;
 
 	/*
 	 * A capdata device may be a component master of another capdata device.
@@ -118,6 +121,8 @@ struct cd_list {
 
 static struct wmi_driver lwmi_cd_driver;
 
+/* ======== Device components ======== */
+
 /**
  * lwmi_cd_match() - Match rule for the master driver.
  * @dev: Pointer to the capability data parent device.
@@ -471,6 +476,116 @@ EXPORT_SYMBOL_NS_GPL(lwmi_cd01_get_data, "LENOVO_WMI_CAPDATA");
 DEF_LWMI_CDXX_GET_DATA(cd_fan, LENOVO_FAN_TEST_DATA, struct capdata_fan);
 EXPORT_SYMBOL_NS_GPL(lwmi_cd_fan_get_data, "LENOVO_WMI_CAPDATA");
 
+/* ======== debugfs ======== */
+
+/**
+ * lwmi_cd00_show() - Dump capdata00
+ * @s: Pointer to seq_file where the capdata00 is dumped.
+ * @cd00: Pointer to a capdata00 struct to be dumped.
+ */
+static void lwmi_cd00_show(struct seq_file *s, struct capdata00 *cd00)
+{
+	u8 dev = FIELD_GET(LWMI_ATTR_DEV_ID_MASK, cd00->id);
+	u8 feat = FIELD_GET(LWMI_ATTR_FEAT_ID_MASK, cd00->id);
+	u8 mode = FIELD_GET(LWMI_ATTR_MODE_ID_MASK, cd00->id);
+	u8 type = FIELD_GET(LWMI_ATTR_TYPE_ID_MASK, cd00->id);
+	bool extra = cd00->supported & ~(LWMI_SUPP_GET | LWMI_SUPP_SET | LWMI_SUPP_VALID);
+	bool get = cd00->supported & LWMI_SUPP_GET;
+	bool set = cd00->supported & LWMI_SUPP_SET;
+	bool valid = cd00->supported & LWMI_SUPP_VALID;
+
+	seq_printf(s, "  id:             0x%08x [dev: %2u, feat: %2u, mode: %2u, type: %2u]\n",
+		   cd00->id, dev, feat, mode, type);
+
+	seq_printf(s, "  supported:      0x%08x [%c%c%c%c]\n", cd00->supported,
+		   extra ? '+' : ' ',
+		   get   ? 'R' : ' ',
+		   set   ? 'W' : ' ',
+		   valid ? 'V' : ' ');
+
+	seq_printf(s, "  default_value:  %u\n", cd00->default_value);
+}
+
+/**
+ * lwmi_cd01_show() - Dump capdata01
+ * @s: Pointer to seq_file where the capdata01 is dumped.
+ * @cd01: Pointer to a capdata01 struct to be dumped.
+ */
+static void lwmi_cd01_show(struct seq_file *s, struct capdata01 *cd01)
+{
+	/* capdata01 is an extension to capdata00. */
+	lwmi_cd00_show(s, (struct capdata00 *)cd01);
+
+	seq_printf(s, "  step:           %u\n", cd01->step);
+	seq_printf(s, "  min_value:      %u\n", cd01->min_value);
+	seq_printf(s, "  max_value:      %u\n", cd01->max_value);
+}
+
+/**
+ * lwmi_cd_fan_show() - Dump capdata_fan
+ * @s: Pointer to seq_file where the capdata_fan is dumped.
+ * @cd_fan: Pointer to a capdata_fan struct to be dumped.
+ */
+static void lwmi_cd_fan_show(struct seq_file *s, struct capdata_fan *cd_fan)
+{
+	seq_printf(s, "  id:             %u\n", cd_fan->id);
+	seq_printf(s, "  min_rpm:        %u\n", cd_fan->min_rpm);
+	seq_printf(s, "  max_rpm:        %u\n", cd_fan->max_rpm);
+}
+
+/**
+ * lwmi_cd_debugfs_show() - Dump capability data to debugfs
+ * @s: Pointer to seq_file where the capability data is dumped.
+ * @data: unused.
+ *
+ * Return: 0
+ */
+static int lwmi_cd_debugfs_show(struct seq_file *s, void *data)
+{
+	struct lwmi_cd_priv *priv = s->private;
+	u8 idx;
+
+	guard(mutex)(&priv->list->list_mutex);
+
+	/* lwmi_cd_alloc() ensured priv->list->type must be a valid type. */
+	for (idx = 0; idx < priv->list->count; idx++) {
+		seq_printf(s, "%s[%u]:\n", lwmi_cd_table[priv->list->type].name, idx);
+
+		if (priv->list->type == LENOVO_CAPABILITY_DATA_00)
+			lwmi_cd00_show(s, &priv->list->cd00[idx]);
+		else if (priv->list->type == LENOVO_CAPABILITY_DATA_01)
+			lwmi_cd01_show(s, &priv->list->cd01[idx]);
+		else if (priv->list->type == LENOVO_FAN_TEST_DATA)
+			lwmi_cd_fan_show(s, &priv->list->cd_fan[idx]);
+	}
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(lwmi_cd_debugfs);
+
+/**
+ * lwmi_cd_debugfs_add() - Create debugfs directory and files for a device
+ * @priv: lenovo-wmi-capdata driver data.
+ */
+static void lwmi_cd_debugfs_add(struct lwmi_cd_priv *priv)
+{
+	priv->debugfs_dir = lwmi_debugfs_create_dir(priv->wdev);
+
+	debugfs_create_file("capdata", 0444, priv->debugfs_dir, priv, &lwmi_cd_debugfs_fops);
+}
+
+/**
+ * lwmi_cd_debugfs_remove() - Remove debugfs directory for a device
+ * @priv: lenovo-wmi-capdata driver data.
+ */
+static void lwmi_cd_debugfs_remove(struct lwmi_cd_priv *priv)
+{
+	debugfs_remove_recursive(priv->debugfs_dir);
+	priv->debugfs_dir = NULL;
+}
+
+/* ======== WMI interface ======== */
+
 /**
  * lwmi_cd_cache() - Cache all WMI data block information
  * @priv: lenovo-wmi-capdata driver data.
@@ -773,6 +888,8 @@ static int lwmi_cd_probe(struct wmi_device *wdev, const void *context)
 		dev_err(&wdev->dev, "failed to register %s: %d\n",
 			info->name, ret);
 	} else {
+		lwmi_cd_debugfs_add(priv);
+
 		dev_dbg(&wdev->dev, "registered %s with %u items\n",
 			info->name, priv->list->count);
 	}
@@ -783,6 +900,8 @@ static void lwmi_cd_remove(struct wmi_device *wdev)
 {
 	struct lwmi_cd_priv *priv = dev_get_drvdata(&wdev->dev);
 
+	lwmi_cd_debugfs_remove(priv);
+
 	switch (priv->list->type) {
 	case LENOVO_CAPABILITY_DATA_00:
 		lwmi_cd_sub_master_del(priv);
@@ -822,6 +941,7 @@ static struct wmi_driver lwmi_cd_driver = {
 
 module_wmi_driver(lwmi_cd_driver);
 
+MODULE_IMPORT_NS("LENOVO_WMI_HELPERS");
 MODULE_DEVICE_TABLE(wmi, lwmi_cd_id_table);
 MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@gmail.com>");
 MODULE_AUTHOR("Rong Zhang <i@rong.moe>");
diff --git a/drivers/platform/x86/lenovo/wmi-capdata.h b/drivers/platform/x86/lenovo/wmi-capdata.h
index e0a30f2c0c87..0df3ae534ed5 100644
--- a/drivers/platform/x86/lenovo/wmi-capdata.h
+++ b/drivers/platform/x86/lenovo/wmi-capdata.h
@@ -38,9 +38,7 @@ struct capdata00 {
 };
 
 struct capdata01 {
-	u32 id;
-	u32 supported;
-	u32 default_value;
+	struct capdata00;
 	u32 step;
 	u32 min_value;
 	u32 max_value;
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 06/13] platform/x86: lenovo-wmi-other: Limit adding attributes to supported devices
  2026-03-31 18:12 ` [PATCH v6 06/13] platform/x86: lenovo-wmi-other: Limit adding attributes to supported devices Derek J. Clark
@ 2026-03-31 22:28   ` Kurt Borja
  0 siblings, 0 replies; 33+ messages in thread
From: Kurt Borja @ 2026-03-31 22:28 UTC (permalink / raw)
  To: Derek J. Clark, Ilpo Järvinen, Hans de Goede
  Cc: Mark Pearson, Armin Wolf, Jonathan Corbet, Rong Zhang, Kurt Borja,
	platform-driver-x86, linux-kernel

On Tue Mar 31, 2026 at 1:12 PM -05, Derek J. Clark wrote:
> Adds lwmi_is_attr_01_supported, and only creates the attribute subfolder
> if the attribute is supported by the hardware. Due to some poorly
> implemented BIOS this is a multi-step sequence of events. This is
> because:
> - Some BIOS support getting the capability data from custom mode (0xff),
>   while others only support it in no-mode (0x00).
> - Some BIOS support get/set for the current value from custom mode (0xff),
>   while others only support it in no-mode (0x00).
> - Some BIOS report capability data for a method that is not fully
>   implemented.
> - Some BIOS have methods fully implemented, but no complimentary
>   capability data.
>
> To ensure we only expose fully implemented methods with corresponding
> capability data, we check each outcome before reporting that an
> attribute can be supported.
>
> Checking for lwmi_is_attr_01_supported during remove is not done to
> ensure that we don't attempt to call cd01 or send WMI events if one of
> the interfaces being removed was the cause of the driver unloading.
>
> Reviewed-by: Rong Zhang <i@rong.moe>
> Tested-by: Rong Zhang <i@rong.moe>
> Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
> Reported-by: Kurt Borja <kuurtb@gmail.com>
> Closes: https://lore.kernel.org/platform-driver-x86/DG60P3SHXR8H.3NSEHMZ6J7XRC@gmail.com/
> Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>

Thanks for this!

Tested-by: Kurt Borja <kuurtb@gmail.com>

> ---
> v6:
>   - Zero initialize args in lwmi_is_attr_01_supported.
>   - Fix formatting.
> v5:
>   - Move cv/cd_mode_id refrences from path 3/4.
>   - Add missing import for ARRAY_SIZE.
>   - Make lwmi_is_attr_01_supported return bool instead of u32.
>   - Various formatting fixes.
> v4:
>   - Use for loop instead of backtrace gotos for checking if an attribute
>     is supported.
>   - Add include for dev_printk.
>   - Wrap dev_dbg in lwmi_is_attr_01_supported earlier.
>   - Don't use symmetric cleanup of attributes in error states.
> ---
>  drivers/platform/x86/lenovo/wmi-other.c | 87 ++++++++++++++++++++++++-
>  1 file changed, 84 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
> index d2838ae5934f..f689d19d711a 100644
> --- a/drivers/platform/x86/lenovo/wmi-other.c
> +++ b/drivers/platform/x86/lenovo/wmi-other.c
> @@ -27,10 +27,12 @@
>   */
>  
>  #include <linux/acpi.h>
> +#include <linux/array_size.h>
>  #include <linux/cleanup.h>
>  #include <linux/component.h>
>  #include <linux/container_of.h>
>  #include <linux/device.h>
> +#include <linux/dev_printk.h>
>  #include <linux/export.h>
>  #include <linux/gfp_types.h>
>  #include <linux/hwmon.h>
> @@ -545,6 +547,8 @@ struct tunable_attr_01 {
>  	u8 feature_id;
>  	u8 device_id;
>  	u8 type_id;
> +	u8 cd_mode_id; /* mode arg for searching capdata */
> +	u8 cv_mode_id; /* mode arg for set/get current_value */
>  };
>  
>  static struct tunable_attr_01 ppt_pl1_spl = {
> @@ -777,7 +781,7 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
>  		return -EBUSY;
>  
>  	args.arg0 = lwmi_attr_id(tunable_attr->device_id, tunable_attr->feature_id,
> -				 mode, tunable_attr->type_id);
> +				 tunable_attr->cd_mode_id, tunable_attr->type_id);
>  
>  	ret = lwmi_cd01_get_data(priv->cd01_list, args.arg0, &capdata);
>  	if (ret)
> @@ -830,6 +834,10 @@ static ssize_t attr_current_value_show(struct kobject *kobj,
>  	if (ret)
>  		return ret;
>  
> +	/* If "no-mode" is the supported mode, ensure we never send current mode */
> +	if (tunable_attr->cv_mode_id == LWMI_GZ_THERMAL_MODE_NONE)
> +		mode = tunable_attr->cv_mode_id;
> +
>  	args.arg0 = lwmi_attr_id(tunable_attr->device_id, tunable_attr->feature_id,
>  				 mode, tunable_attr->type_id);
>  
> @@ -842,6 +850,77 @@ static ssize_t attr_current_value_show(struct kobject *kobj,
>  	return sysfs_emit(buf, "%d\n", retval);
>  }
>  
> +/**
> + * lwmi_attr_01_is_supported() - Determine if the given attribute is supported.
> + * @tunable_attr: The attribute to verify.
> + *
> + * First check if the attribute has a corresponding capdata01 table in the cd01
> + * module under the "custom" mode (0xff). If that is not present then check if
> + * there is a corresponding "no-mode" (0x00) entry. If either of those passes,
> + * check capdata->supported for values > 0. If capdata is available, attempt to
> + * determine the set/get mode for the current value property using a similar
> + * pattern. If the value returned by either custom or no-mode is 0, or we get
> + * an error, we assume that mode is not supported. If any of the above checks
> + * fail then the attribute is not fully supported.
> + *
> + * The probed cd_mode_id/cv_mode_id are stored on the tunable_attr for later
> + * reference.
> + *
> + * Return: bool.
> + */
> +static bool lwmi_attr_01_is_supported(struct tunable_attr_01 *tunable_attr)
> +{
> +	u8 modes[2] = { LWMI_GZ_THERMAL_MODE_CUSTOM, LWMI_GZ_THERMAL_MODE_NONE };
> +	struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev);
> +	struct wmi_method_args_32 args = { 0x0, 0x0 };
> +	bool cd_mode_found = false;
> +	bool cv_mode_found = false;
> +	struct capdata01 capdata;
> +	int retval, ret, i;
> +
> +	/* Determine tunable_attr->cd_mode_id*/
> +	for (i = 0; i < ARRAY_SIZE(modes); i++) {
> +		args.arg0 = lwmi_attr_id(tunable_attr->device_id, tunable_attr->feature_id,
> +					 modes[i], tunable_attr->type_id);
> +
> +		ret = lwmi_cd01_get_data(priv->cd01_list, args.arg0, &capdata);
> +		if (ret || !capdata.supported)
> +			continue;
> +		tunable_attr->cd_mode_id = modes[i];
> +		cd_mode_found = true;
> +		break;
> +	}
> +
> +	if (!cd_mode_found)
> +		return cd_mode_found;
> +
> +	/* Determine tunable_attr->cv_mode_id, returns 1 if supported*/
> +	for (i = 0; i < ARRAY_SIZE(modes); i++) {
> +		args.arg0 = lwmi_attr_id(tunable_attr->device_id, tunable_attr->feature_id,
> +					 modes[i], tunable_attr->type_id);
> +
> +		ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_GET,
> +					    (unsigned char *)&args, sizeof(args),
> +					    &retval);
> +		if (ret || !retval)
> +			continue;
> +		tunable_attr->cv_mode_id = modes[i];
> +		cv_mode_found = true;
> +		break;
> +	}
> +
> +	if (!cv_mode_found)
> +		return cv_mode_found;
> +
> +	dev_dbg(tunable_attr->dev,
> +		"cd_mode_id: %#010x, cv_mode_id: %#010x, attribute support level: %#010x\n",
> +		lwmi_attr_id(tunable_attr->device_id, tunable_attr->feature_id,
> +			     tunable_attr->cd_mode_id, tunable_attr->type_id),
> +		args.arg0, capdata.supported);
> +
> +	return capdata.supported > 0 ? true : false;
> +}
> +
>  /* Lenovo WMI Other Mode Attribute macros */
>  #define __LWMI_ATTR_RO(_func, _name)                                  \
>  	{                                                             \
> @@ -965,12 +1044,14 @@ static int lwmi_om_fw_attr_add(struct lwmi_om_priv *priv)
>  	}
>  
>  	for (i = 0; i < ARRAY_SIZE(cd01_attr_groups) - 1; i++) {
> +		cd01_attr_groups[i].tunable_attr->dev = &priv->wdev->dev;
> +		if (!lwmi_attr_01_is_supported(cd01_attr_groups[i].tunable_attr))
> +			continue;
> +
>  		err = sysfs_create_group(&priv->fw_attr_kset->kobj,
>  					 cd01_attr_groups[i].attr_group);
>  		if (err)
>  			goto err_remove_groups;
> -
> -		cd01_attr_groups[i].tunable_attr->dev = &priv->wdev->dev;
>  	}
>  	return 0;
>  


-- 
Thanks,
 ~ Kurt


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 07/13] platform/x86: lenovo-wmi-other: Add missing CPU tunable attributes
  2026-03-31 18:12 ` [PATCH v6 07/13] platform/x86: lenovo-wmi-other: Add missing CPU tunable attributes Derek J. Clark
@ 2026-03-31 22:29   ` Kurt Borja
  2026-04-01 18:30   ` Rong Zhang
  1 sibling, 0 replies; 33+ messages in thread
From: Kurt Borja @ 2026-03-31 22:29 UTC (permalink / raw)
  To: Derek J. Clark, Ilpo Järvinen, Hans de Goede
  Cc: Mark Pearson, Armin Wolf, Jonathan Corbet, Rong Zhang, Kurt Borja,
	platform-driver-x86, linux-kernel

On Tue Mar 31, 2026 at 1:12 PM -05, Derek J. Clark wrote:
> Use an enum for all device ID's and CPU attribute feature ID's,
> add missing CPU attributes.
>
> Reviewed-by: Rong Zhang <i@rong.moe>
> Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
> Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>

Tested-by: Kurt Borja <kuurtb@gmail.com>

> ---
> v4:
>   - Align type ID defines.
>   - Align CPU feature enum values.
>   - remove cpu_oc_stat from Documentation.
> v3:
>   - Remove cpu_oc_stat.
> ---
>  .../wmi/devices/lenovo-wmi-other.rst          |   9 ++
>  drivers/platform/x86/lenovo/wmi-capdata.h     |   5 +-
>  drivers/platform/x86/lenovo/wmi-other.c       | 101 +++++++++++++++++-
>  3 files changed, 109 insertions(+), 6 deletions(-)
>
> diff --git a/Documentation/wmi/devices/lenovo-wmi-other.rst b/Documentation/wmi/devices/lenovo-wmi-other.rst
> index 01d471156738..82c17361e749 100644
> --- a/Documentation/wmi/devices/lenovo-wmi-other.rst
> +++ b/Documentation/wmi/devices/lenovo-wmi-other.rst
> @@ -68,9 +68,18 @@ Each attribute has the following properties:
>   - type
>  
>  The following firmware-attributes are implemented:
> + - cpu_temp: CPU Thermal Load Limit
> + - ppt_cpu_cl: CPU Cross Loading Power Limit
> + - ppt_pl1_apu_spl: Platform Profile Tracking APU Sustained Power Limit
>   - ppt_pl1_spl: Platform Profile Tracking Sustained Power Limit
> + - ppt_pl1_spl_cl: Platform Profile Tracking Cross Loading Sustained Power Limit
> + - ppt_pl1_tau: Exceed Duration for Platform Profile Tracking Sustained Power Limit
>   - ppt_pl2_sppt: Platform Profile Tracking Slow Package Power Tracking
> + - ppt_pl2_sppt_cl: Platform Profile Tracking Cross Loading Slow Package Tracking
>   - ppt_pl3_fppt: Platform Profile Tracking Fast Package Power Tracking
> + - ppt_pl3_fppt_cl: Platform Profile Tracking Cross Loading Fast Package Power Tracking
> + - ppt_pl4_ipl: Platform Profile Trakcing Instantaneous Power Limit
> + - ppt_pl4_ipl_cl: Platform Profile Tracking Cross Loading Instantaneous Power Limit
>  
>  LENOVO_FAN_TEST_DATA
>  -------------------------
> diff --git a/drivers/platform/x86/lenovo/wmi-capdata.h b/drivers/platform/x86/lenovo/wmi-capdata.h
> index 1388eaf4ab4a..f2d45cd7a188 100644
> --- a/drivers/platform/x86/lenovo/wmi-capdata.h
> +++ b/drivers/platform/x86/lenovo/wmi-capdata.h
> @@ -18,7 +18,10 @@
>  #define LWMI_ATTR_MODE_ID_MASK	GENMASK(15, 8)
>  #define LWMI_ATTR_TYPE_ID_MASK	GENMASK(7, 0)
>  
> -#define LWMI_DEVICE_ID_FAN	0x04
> +enum lwmi_device_id {
> +	LWMI_DEVICE_ID_CPU = 0x01,
> +	LWMI_DEVICE_ID_FAN = 0x04,
> +};
>  
>  #define LWMI_TYPE_ID_NONE 0x00
>  
> diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
> index f689d19d711a..508186c7410d 100644
> --- a/drivers/platform/x86/lenovo/wmi-other.c
> +++ b/drivers/platform/x86/lenovo/wmi-other.c
> @@ -54,14 +54,21 @@
>  
>  #define LENOVO_OTHER_MODE_GUID "DC2A8805-3A8C-41BA-A6F7-092E0089CD3B"
>  
> -#define LWMI_DEVICE_ID_CPU 0x01
> -
> -#define LWMI_FEATURE_ID_CPU_SPPT 0x01
> -#define LWMI_FEATURE_ID_CPU_SPL 0x02
> -#define LWMI_FEATURE_ID_CPU_FPPT 0x03
> +enum lwmi_feature_id_cpu {
> +	LWMI_FEATURE_ID_CPU_SPPT =	0x01,
> +	LWMI_FEATURE_ID_CPU_SPL =	0x02,
> +	LWMI_FEATURE_ID_CPU_FPPT =	0x03,
> +	LWMI_FEATURE_ID_CPU_TEMP =	0x04,
> +	LWMI_FEATURE_ID_CPU_APU =	0x05,
> +	LWMI_FEATURE_ID_CPU_CL =	0x06,
> +	LWMI_FEATURE_ID_CPU_TAU =	0x07,
> +	LWMI_FEATURE_ID_CPU_IPL =	0x09,
> +};
>  
>  #define LWMI_FEATURE_ID_FAN_RPM 0x03
>  
> +#define LWMI_TYPE_ID_CROSSLOAD	0x01
> +
>  #define LWMI_FEATURE_VALUE_GET 17
>  #define LWMI_FEATURE_VALUE_SET 18
>  
> @@ -557,18 +564,72 @@ static struct tunable_attr_01 ppt_pl1_spl = {
>  	.type_id = LWMI_TYPE_ID_NONE,
>  };
>  
> +static struct tunable_attr_01 ppt_pl1_spl_cl = {
> +	.device_id = LWMI_DEVICE_ID_CPU,
> +	.feature_id = LWMI_FEATURE_ID_CPU_SPL,
> +	.type_id = LWMI_TYPE_ID_CROSSLOAD,
> +};
> +
>  static struct tunable_attr_01 ppt_pl2_sppt = {
>  	.device_id = LWMI_DEVICE_ID_CPU,
>  	.feature_id = LWMI_FEATURE_ID_CPU_SPPT,
>  	.type_id = LWMI_TYPE_ID_NONE,
>  };
>  
> +static struct tunable_attr_01 ppt_pl2_sppt_cl = {
> +	.device_id = LWMI_DEVICE_ID_CPU,
> +	.feature_id = LWMI_FEATURE_ID_CPU_SPPT,
> +	.type_id = LWMI_TYPE_ID_CROSSLOAD,
> +};
> +
>  static struct tunable_attr_01 ppt_pl3_fppt = {
>  	.device_id = LWMI_DEVICE_ID_CPU,
>  	.feature_id = LWMI_FEATURE_ID_CPU_FPPT,
>  	.type_id = LWMI_TYPE_ID_NONE,
>  };
>  
> +static struct tunable_attr_01 ppt_pl3_fppt_cl = {
> +	.device_id = LWMI_DEVICE_ID_CPU,
> +	.feature_id = LWMI_FEATURE_ID_CPU_FPPT,
> +	.type_id = LWMI_TYPE_ID_CROSSLOAD,
> +};
> +
> +static struct tunable_attr_01 cpu_temp = {
> +	.device_id = LWMI_DEVICE_ID_CPU,
> +	.feature_id = LWMI_FEATURE_ID_CPU_TEMP,
> +	.type_id = LWMI_TYPE_ID_NONE,
> +};
> +
> +static struct tunable_attr_01 ppt_pl1_apu_spl = {
> +	.device_id = LWMI_DEVICE_ID_CPU,
> +	.feature_id = LWMI_FEATURE_ID_CPU_APU,
> +	.type_id = LWMI_TYPE_ID_NONE,
> +};
> +
> +static struct tunable_attr_01 ppt_cpu_cl = {
> +	.device_id = LWMI_DEVICE_ID_CPU,
> +	.feature_id = LWMI_FEATURE_ID_CPU_CL,
> +	.type_id = LWMI_TYPE_ID_NONE,
> +};
> +
> +static struct tunable_attr_01 ppt_pl1_tau = {
> +	.device_id = LWMI_DEVICE_ID_CPU,
> +	.feature_id = LWMI_FEATURE_ID_CPU_TAU,
> +	.type_id = LWMI_TYPE_ID_NONE,
> +};
> +
> +static struct tunable_attr_01 ppt_pl4_ipl = {
> +	.device_id = LWMI_DEVICE_ID_CPU,
> +	.feature_id = LWMI_FEATURE_ID_CPU_IPL,
> +	.type_id = LWMI_TYPE_ID_NONE,
> +};
> +
> +static struct tunable_attr_01 ppt_pl4_ipl_cl = {
> +	.device_id = LWMI_DEVICE_ID_CPU,
> +	.feature_id = LWMI_FEATURE_ID_CPU_IPL,
> +	.type_id = LWMI_TYPE_ID_CROSSLOAD,
> +};
> +
>  struct capdata01_attr_group {
>  	const struct attribute_group *attr_group;
>  	struct tunable_attr_01 *tunable_attr;
> @@ -794,6 +855,8 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
>  	if (value < capdata.min_value || value > capdata.max_value)
>  		return -EINVAL;
>  
> +	args.arg0 = lwmi_attr_id(tunable_attr->device_id, tunable_attr->feature_id,
> +				 tunable_attr->cv_mode_id, tunable_attr->type_id);
>  	args.arg1 = value;
>  
>  	ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_SET,
> @@ -998,17 +1061,45 @@ static bool lwmi_attr_01_is_supported(struct tunable_attr_01 *tunable_attr)
>  		.name = _fsname, .attrs = _attrname##_attrs               \
>  	}
>  
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(cpu_temp, "cpu_temp",
> +			      "Set the CPU thermal load limit");
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_cpu_cl, "ppt_cpu_cl",
> +			      "Set the CPU cross loading power limit");
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl1_apu_spl, "ppt_pl1_apu_spl",
> +			      "Set the APU sustained power limit");
>  LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl1_spl, "ppt_pl1_spl",
>  			      "Set the CPU sustained power limit");
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl1_spl_cl, "ppt_pl1_spl_cl",
> +			      "Set the CPU cross loading sustained power limit");
>  LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl2_sppt, "ppt_pl2_sppt",
>  			      "Set the CPU slow package power tracking limit");
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl2_sppt_cl, "ppt_pl2_sppt_cl",
> +			      "Set the CPU cross loading slow package power tracking limit");
>  LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl3_fppt, "ppt_pl3_fppt",
>  			      "Set the CPU fast package power tracking limit");
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl3_fppt_cl, "ppt_pl3_fppt_cl",
> +			      "Set the CPU cross loading fast package power tracking limit");
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl1_tau, "ppt_pl1_tau",
> +			      "Set the CPU sustained power limit exceed duration");
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl4_ipl, "ppt_pl4_ipl",
> +			      "Set the CPU instantaneous power limit");
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl4_ipl_cl, "ppt_pl4_ipl_cl",
> +			      "Set the CPU cross loading instantaneous power limit");
> +
>  
>  static struct capdata01_attr_group cd01_attr_groups[] = {
> +	{ &cpu_temp_attr_group, &cpu_temp },
> +	{ &ppt_cpu_cl_attr_group, &ppt_cpu_cl },
> +	{ &ppt_pl1_apu_spl_attr_group, &ppt_pl1_apu_spl },
>  	{ &ppt_pl1_spl_attr_group, &ppt_pl1_spl },
> +	{ &ppt_pl1_spl_cl_attr_group, &ppt_pl1_spl_cl },
> +	{ &ppt_pl1_tau_attr_group, &ppt_pl1_tau },
>  	{ &ppt_pl2_sppt_attr_group, &ppt_pl2_sppt },
> +	{ &ppt_pl2_sppt_cl_attr_group, &ppt_pl2_sppt_cl },
>  	{ &ppt_pl3_fppt_attr_group, &ppt_pl3_fppt },
> +	{ &ppt_pl3_fppt_cl_attr_group, &ppt_pl3_fppt_cl },
> +	{ &ppt_pl4_ipl_attr_group, &ppt_pl4_ipl },
> +	{ &ppt_pl4_ipl_cl_attr_group, &ppt_pl4_ipl_cl },
>  	{},
>  };
>  


-- 
Thanks,
 ~ Kurt


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 08/13] platform/x86: lenovo-wmi-other: Add GPU tunable attributes
  2026-03-31 18:12 ` [PATCH v6 08/13] platform/x86: lenovo-wmi-other: Add GPU " Derek J. Clark
@ 2026-03-31 22:29   ` Kurt Borja
  2026-04-01 18:30   ` Rong Zhang
  1 sibling, 0 replies; 33+ messages in thread
From: Kurt Borja @ 2026-03-31 22:29 UTC (permalink / raw)
  To: Derek J. Clark, Ilpo Järvinen, Hans de Goede
  Cc: Mark Pearson, Armin Wolf, Jonathan Corbet, Rong Zhang, Kurt Borja,
	platform-driver-x86, linux-kernel

On Tue Mar 31, 2026 at 1:12 PM -05, Derek J. Clark wrote:
> Use an enum for all GPU attribute feature ID's and add GPU attributes.
>
> Reviewed-by: Rong Zhang <i@rong.moe>
> Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
> Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>

Tested-by: Kurt Borja <kuurtb@gmail.com>

> ---
> v4:
>   - Align CPU feature enum values.
>   - Remove gpu_oc_stat from Documentation.
> v3:
>   - Remove gpu_oc_stat.
> ---
>  .../wmi/devices/lenovo-wmi-other.rst          |  10 ++
>  drivers/platform/x86/lenovo/wmi-capdata.h     |   1 +
>  drivers/platform/x86/lenovo/wmi-other.c       | 105 ++++++++++++++++++
>  3 files changed, 116 insertions(+)
>
> diff --git a/Documentation/wmi/devices/lenovo-wmi-other.rst b/Documentation/wmi/devices/lenovo-wmi-other.rst
> index 82c17361e749..bfbea59637bd 100644
> --- a/Documentation/wmi/devices/lenovo-wmi-other.rst
> +++ b/Documentation/wmi/devices/lenovo-wmi-other.rst
> @@ -69,6 +69,16 @@ Each attribute has the following properties:
>  
>  The following firmware-attributes are implemented:
>   - cpu_temp: CPU Thermal Load Limit
> + - dgpu_boost_clk: Dedicated GPU Boost Clock
> + - dgpu_enable: Dedicated GPU Enabled Status
> + - gpu_didvid: GPU Device Identifier and Vendor Identifier
> + - gpu_mode: GPU Mode by Power Limit
> + - gpu_nv_ac_offset: Nvidia GPU AC Total Processing Power Baseline Offset
> + - gpu_nv_bpl: Nvidia GPU Base Power Limit
> + - gpu_nv_cpu_boost: Nvidia GPU to CPU Dynamic Boost Limit
> + - gpu_nv_ctgp: Nvidia GPU Configurable Total Graphics Power
> + - gpu_nv_ppab: Nvidia GPU Power Performance Aware Boost Limit
> + - gpu_temp: GPU Thermal Load Limit
>   - ppt_cpu_cl: CPU Cross Loading Power Limit
>   - ppt_pl1_apu_spl: Platform Profile Tracking APU Sustained Power Limit
>   - ppt_pl1_spl: Platform Profile Tracking Sustained Power Limit
> diff --git a/drivers/platform/x86/lenovo/wmi-capdata.h b/drivers/platform/x86/lenovo/wmi-capdata.h
> index f2d45cd7a188..891b12ca1db6 100644
> --- a/drivers/platform/x86/lenovo/wmi-capdata.h
> +++ b/drivers/platform/x86/lenovo/wmi-capdata.h
> @@ -20,6 +20,7 @@
>  
>  enum lwmi_device_id {
>  	LWMI_DEVICE_ID_CPU = 0x01,
> +	LWMI_DEVICE_ID_GPU = 0x02,
>  	LWMI_DEVICE_ID_FAN = 0x04,
>  };
>  
> diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
> index 508186c7410d..2792671ac1de 100644
> --- a/drivers/platform/x86/lenovo/wmi-other.c
> +++ b/drivers/platform/x86/lenovo/wmi-other.c
> @@ -65,6 +65,19 @@ enum lwmi_feature_id_cpu {
>  	LWMI_FEATURE_ID_CPU_IPL =	0x09,
>  };
>  
> +enum lwmi_feature_id_gpu {
> +	LWMI_FEATURE_ID_GPU_NV_PPAB =		0x01,
> +	LWMI_FEATURE_ID_GPU_NV_CTGP =		0x02,
> +	LWMI_FEATURE_ID_GPU_TEMP =		0x03,
> +	LWMI_FEATURE_ID_GPU_AC_OFFSET =		0x04,
> +	LWMI_FEATURE_ID_DGPU_BOOST_CLK =	0x06,
> +	LWMI_FEATURE_ID_DGPU_EN =		0x07,
> +	LWMI_FEATURE_ID_GPU_MODE =		0x08,
> +	LWMI_FEATURE_ID_DGPU_DIDVID =		0x09,
> +	LWMI_FEATURE_ID_GPU_NV_BPL =		0x0a,
> +	LWMI_FEATURE_ID_GPU_NV_CPU_BOOST =	0x0b,
> +};
> +
>  #define LWMI_FEATURE_ID_FAN_RPM 0x03
>  
>  #define LWMI_TYPE_ID_CROSSLOAD	0x01
> @@ -630,6 +643,66 @@ static struct tunable_attr_01 ppt_pl4_ipl_cl = {
>  	.type_id = LWMI_TYPE_ID_CROSSLOAD,
>  };
>  
> +static struct tunable_attr_01 gpu_nv_ppab = {
> +	.device_id = LWMI_DEVICE_ID_GPU,
> +	.feature_id = LWMI_FEATURE_ID_GPU_NV_PPAB,
> +	.type_id = LWMI_TYPE_ID_NONE,
> +};
> +
> +static struct tunable_attr_01 gpu_nv_ctgp = {
> +	.device_id = LWMI_DEVICE_ID_GPU,
> +	.feature_id = LWMI_FEATURE_ID_GPU_NV_CTGP,
> +	.type_id = LWMI_TYPE_ID_NONE,
> +};
> +
> +static struct tunable_attr_01 gpu_temp = {
> +	.device_id = LWMI_DEVICE_ID_GPU,
> +	.feature_id = LWMI_FEATURE_ID_GPU_TEMP,
> +	.type_id = LWMI_TYPE_ID_NONE,
> +};
> +
> +static struct tunable_attr_01 gpu_nv_ac_offset = {
> +	.device_id = LWMI_DEVICE_ID_GPU,
> +	.feature_id = LWMI_FEATURE_ID_GPU_AC_OFFSET,
> +	.type_id = LWMI_TYPE_ID_NONE,
> +};
> +
> +static struct tunable_attr_01 dgpu_boost_clk = {
> +	.device_id = LWMI_DEVICE_ID_GPU,
> +	.feature_id = LWMI_FEATURE_ID_DGPU_BOOST_CLK,
> +	.type_id = LWMI_TYPE_ID_NONE,
> +};
> +
> +static struct tunable_attr_01 dgpu_enable = {
> +	.device_id = LWMI_DEVICE_ID_GPU,
> +	.feature_id = LWMI_FEATURE_ID_DGPU_EN,
> +	.type_id = LWMI_TYPE_ID_NONE,
> +};
> +
> +static struct tunable_attr_01 gpu_mode = {
> +	.device_id = LWMI_DEVICE_ID_GPU,
> +	.feature_id = LWMI_FEATURE_ID_GPU_MODE,
> +	.type_id = LWMI_TYPE_ID_NONE,
> +};
> +
> +static struct tunable_attr_01 dgpu_didvid = {
> +	.device_id = LWMI_DEVICE_ID_GPU,
> +	.feature_id = LWMI_FEATURE_ID_DGPU_DIDVID,
> +	.type_id = LWMI_TYPE_ID_NONE,
> +};
> +
> +static struct tunable_attr_01 gpu_nv_bpl = {
> +	.device_id = LWMI_DEVICE_ID_GPU,
> +	.feature_id = LWMI_FEATURE_ID_GPU_NV_BPL,
> +	.type_id = LWMI_TYPE_ID_NONE,
> +};
> +
> +static struct tunable_attr_01 gpu_nv_cpu_boost = {
> +	.device_id = LWMI_DEVICE_ID_GPU,
> +	.feature_id = LWMI_FEATURE_ID_GPU_NV_CPU_BOOST,
> +	.type_id = LWMI_TYPE_ID_NONE,
> +};
> +
>  struct capdata01_attr_group {
>  	const struct attribute_group *attr_group;
>  	struct tunable_attr_01 *tunable_attr;
> @@ -1061,6 +1134,7 @@ static bool lwmi_attr_01_is_supported(struct tunable_attr_01 *tunable_attr)
>  		.name = _fsname, .attrs = _attrname##_attrs               \
>  	}
>  
> +/* CPU tunable attributes */
>  LWMI_ATTR_GROUP_TUNABLE_CAP01(cpu_temp, "cpu_temp",
>  			      "Set the CPU thermal load limit");
>  LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_cpu_cl, "ppt_cpu_cl",
> @@ -1086,9 +1160,40 @@ LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl4_ipl, "ppt_pl4_ipl",
>  LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl4_ipl_cl, "ppt_pl4_ipl_cl",
>  			      "Set the CPU cross loading instantaneous power limit");
>  
> +/* GPU tunable attributes */
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(dgpu_boost_clk, "gpu_boost_clk",
> +			      "Set the dedicated GPU boost clock");
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(dgpu_didvid, "gpu_didvid",
> +			      "Get the GPU device identifier and vendor identifier");
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(dgpu_enable, "dgpu_enable",
> +			      "Set the dedicated Nvidia GPU enabled status");
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_mode, "gpu_mode",
> +			      "Set the GPU mode by power limit");
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_nv_ac_offset, "gpu_nv_ac_offset",
> +			      "Set the Nvidia GPU AC total processing power baseline offset");
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_nv_bpl, "gpu_nv_bpl",
> +			      "Set the Nvidia GPU base power limit");
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_nv_cpu_boost, "gpu_nv_cpu_boost",
> +			      "Set the Nvidia GPU to CPU dynamic boost limit");
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_nv_ctgp, "gpu_nv_ctgp",
> +			      "Set the GPU configurable total graphics power");
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_nv_ppab, "gpu_nv_ppab",
> +			      "Set the Nvidia GPU power performance aware boost limit");
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_temp, "gpu_temp",
> +			      "Set the GPU thermal load limit");
>  
>  static struct capdata01_attr_group cd01_attr_groups[] = {
>  	{ &cpu_temp_attr_group, &cpu_temp },
> +	{ &dgpu_boost_clk_attr_group, &dgpu_boost_clk },
> +	{ &dgpu_didvid_attr_group, &dgpu_didvid },
> +	{ &dgpu_enable_attr_group, &dgpu_enable },
> +	{ &gpu_mode_attr_group, &gpu_mode },
> +	{ &gpu_nv_ac_offset_attr_group, &gpu_nv_ac_offset },
> +	{ &gpu_nv_bpl_attr_group, &gpu_nv_bpl },
> +	{ &gpu_nv_cpu_boost_attr_group, &gpu_nv_cpu_boost },
> +	{ &gpu_nv_ctgp_attr_group, &gpu_nv_ctgp },
> +	{ &gpu_nv_ppab_attr_group, &gpu_nv_ppab },
> +	{ &gpu_temp_attr_group, &gpu_temp },
>  	{ &ppt_cpu_cl_attr_group, &ppt_cpu_cl },
>  	{ &ppt_pl1_apu_spl_attr_group, &ppt_pl1_apu_spl },
>  	{ &ppt_pl1_spl_attr_group, &ppt_pl1_spl },


-- 
Thanks,
 ~ Kurt


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 03/13] platform/x86: lenovo-wmi-other: Zero initialize WMI arguments
  2026-03-31 18:11 ` [PATCH v6 03/13] platform/x86: lenovo-wmi-other: Zero initialize WMI arguments Derek J. Clark
@ 2026-04-01 18:26   ` Rong Zhang
  0 siblings, 0 replies; 33+ messages in thread
From: Rong Zhang @ 2026-04-01 18:26 UTC (permalink / raw)
  To: Derek J. Clark, Ilpo Järvinen, Hans de Goede
  Cc: Mark Pearson, Armin Wolf, Jonathan Corbet, Kurt Borja,
	platform-driver-x86, linux-kernel

Hi Derek,

On Tue, 2026-03-31 at 18:11 +0000, Derek J. Clark wrote:
> Adds explicit initialization of wmi_method_args_32 declarations with
> zero values to prevent uninitialized data from being sent to the WMI
> device when passed.
> 
> Suggested-by: Rong Zhang <i@rong.moe>
> Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>

Add a Fixes: tag if applicable?

If you decide to add it, fix patches should be the very first patches in
the series so that they can be backported painlessly. See also my reply
to the cover letter.

> ---
>  drivers/platform/x86/lenovo/wmi-other.c | 6 +++---
>  1 file changed, 3 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
> index c1728c7c2957..c3614bc4f90c 100644
> --- a/drivers/platform/x86/lenovo/wmi-other.c
> +++ b/drivers/platform/x86/lenovo/wmi-other.c
> @@ -166,7 +166,7 @@ MODULE_PARM_DESC(relax_fan_constraint,
>   */
>  static int lwmi_om_fan_get_set(struct lwmi_om_priv *priv, int channel, u32 *val, bool set)
>  {
> -	struct wmi_method_args_32 args;
> +	struct wmi_method_args_32 args = { 0x0, 0x0 };

`= {}' is enough and more readable. Ditto for following ones and those
in other patches.

Also, sashiko.dev reported that lwmi_gz_profile_set() needs zero-
initialization too. Could you fix it in this patch too?

https://sashiko.dev/#/patchset/20260331181208.421552-1-derekjohn.clark%40gmail.com

With them fixed:

Reviewed-by: Rong Zhang <i@rong.moe>
Tested-by: Rong Zhang <i@rong.moe>

Thanks,
Rong

>  	u32 method_id, retval;
>  	int err;
>  
> @@ -772,7 +772,7 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
>  					struct tunable_attr_01 *tunable_attr)
>  {
>  	struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev);
> -	struct wmi_method_args_32 args;
> +	struct wmi_method_args_32 args = { 0x0, 0x0 };
>  	struct capdata01 capdata;
>  	enum thermal_mode mode;
>  	u32 attribute_id;
> @@ -835,7 +835,7 @@ static ssize_t attr_current_value_show(struct kobject *kobj,
>  				       struct tunable_attr_01 *tunable_attr)
>  {
>  	struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev);
> -	struct wmi_method_args_32 args;
> +	struct wmi_method_args_32 args = { 0x0, 0x0 };
>  	enum thermal_mode mode;
>  	u32 attribute_id;
>  	int retval;

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 04/13] platform/x86: lenovo-wmi-helpers: Move gamezone enums to wmi-helpers
  2026-03-31 18:11 ` [PATCH v6 04/13] platform/x86: lenovo-wmi-helpers: Move gamezone enums to wmi-helpers Derek J. Clark
@ 2026-04-01 18:26   ` Rong Zhang
  0 siblings, 0 replies; 33+ messages in thread
From: Rong Zhang @ 2026-04-01 18:26 UTC (permalink / raw)
  To: Derek J. Clark, Ilpo Järvinen, Hans de Goede
  Cc: Mark Pearson, Armin Wolf, Jonathan Corbet, Kurt Borja,
	platform-driver-x86, linux-kernel

Hi Derek,

On Tue, 2026-03-31 at 18:11 +0000, Derek J. Clark wrote:
> In the next patch, the thermal mode enum will be accessed across three
> separate drivers (wmi-capdata, wmi-gamezone and wmi-other). An
> additional patch in the series will also add a function protoype that
> needs to reference this enum in wmi-helpers.h. To avoid having all these
> drivers import each others headers, and to avoid declaring an opaque enum
> to hande the second case, move the thermal mode enum to helpers where it
> can be safely accessed by everything that needs it from a single import.
> 
> While at it, since the gamezone_events_type enum is the only remaining
> item in the header, move that as well to permit removing the gamezone
> header entirely.
> 
> Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>

Reviewed-by: Rong Zhang <i@rong.moe>
Tested-by: Rong Zhang <i@rong.moe>

Thanks,
Rong

> ---
>  drivers/platform/x86/lenovo/wmi-events.c   |  2 +-
>  drivers/platform/x86/lenovo/wmi-gamezone.c |  1 -
>  drivers/platform/x86/lenovo/wmi-gamezone.h | 20 --------------------
>  drivers/platform/x86/lenovo/wmi-helpers.h  | 13 +++++++++++++
>  drivers/platform/x86/lenovo/wmi-other.c    |  1 -
>  5 files changed, 14 insertions(+), 23 deletions(-)
>  delete mode 100644 drivers/platform/x86/lenovo/wmi-gamezone.h
> 
> diff --git a/drivers/platform/x86/lenovo/wmi-events.c b/drivers/platform/x86/lenovo/wmi-events.c
> index 0994cd7dd504..9e9f2e82e04d 100644
> --- a/drivers/platform/x86/lenovo/wmi-events.c
> +++ b/drivers/platform/x86/lenovo/wmi-events.c
> @@ -17,7 +17,7 @@
>  #include <linux/wmi.h>
>  
>  #include "wmi-events.h"
> -#include "wmi-gamezone.h"
> +#include "wmi-helpers.h"
>  
>  #define THERMAL_MODE_EVENT_GUID "D320289E-8FEA-41E0-86F9-911D83151B5F"
>  
> diff --git a/drivers/platform/x86/lenovo/wmi-gamezone.c b/drivers/platform/x86/lenovo/wmi-gamezone.c
> index 381836d29a96..602a48de1b4e 100644
> --- a/drivers/platform/x86/lenovo/wmi-gamezone.c
> +++ b/drivers/platform/x86/lenovo/wmi-gamezone.c
> @@ -21,7 +21,6 @@
>  #include <linux/wmi.h>
>  
>  #include "wmi-events.h"
> -#include "wmi-gamezone.h"
>  #include "wmi-helpers.h"
>  #include "wmi-other.h"
>  
> diff --git a/drivers/platform/x86/lenovo/wmi-gamezone.h b/drivers/platform/x86/lenovo/wmi-gamezone.h
> deleted file mode 100644
> index 6b163a5eeb95..000000000000
> --- a/drivers/platform/x86/lenovo/wmi-gamezone.h
> +++ /dev/null
> @@ -1,20 +0,0 @@
> -/* SPDX-License-Identifier: GPL-2.0-or-later */
> -
> -/* Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com> */
> -
> -#ifndef _LENOVO_WMI_GAMEZONE_H_
> -#define _LENOVO_WMI_GAMEZONE_H_
> -
> -enum gamezone_events_type {
> -	LWMI_GZ_GET_THERMAL_MODE = 1,
> -};
> -
> -enum thermal_mode {
> -	LWMI_GZ_THERMAL_MODE_QUIET =	   0x01,
> -	LWMI_GZ_THERMAL_MODE_BALANCED =	   0x02,
> -	LWMI_GZ_THERMAL_MODE_PERFORMANCE = 0x03,
> -	LWMI_GZ_THERMAL_MODE_EXTREME =	   0xE0, /* Ver 6+ */
> -	LWMI_GZ_THERMAL_MODE_CUSTOM =	   0xFF,
> -};
> -
> -#endif /* !_LENOVO_WMI_GAMEZONE_H_ */
> diff --git a/drivers/platform/x86/lenovo/wmi-helpers.h b/drivers/platform/x86/lenovo/wmi-helpers.h
> index 20fd21749803..3364d8e152ca 100644
> --- a/drivers/platform/x86/lenovo/wmi-helpers.h
> +++ b/drivers/platform/x86/lenovo/wmi-helpers.h
> @@ -14,6 +14,19 @@ struct wmi_method_args_32 {
>  	u32 arg1;
>  };
>  
> +enum lwmi_event_type {
> +	LWMI_GZ_GET_THERMAL_MODE = 0x01,
> +};
> +
> +enum thermal_mode {
> +	LWMI_GZ_THERMAL_MODE_NONE =	   0x00,
> +	LWMI_GZ_THERMAL_MODE_QUIET =	   0x01,
> +	LWMI_GZ_THERMAL_MODE_BALANCED =	   0x02,
> +	LWMI_GZ_THERMAL_MODE_PERFORMANCE = 0x03,
> +	LWMI_GZ_THERMAL_MODE_EXTREME =	   0xE0, /* Ver 6+ */
> +	LWMI_GZ_THERMAL_MODE_CUSTOM =	   0xFF,
> +};
> +
>  int lwmi_dev_evaluate_int(struct wmi_device *wdev, u8 instance, u32 method_id,
>  			  unsigned char *buf, size_t size, u32 *retval);
>  
> diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
> index c3614bc4f90c..b24bf98c19ad 100644
> --- a/drivers/platform/x86/lenovo/wmi-other.c
> +++ b/drivers/platform/x86/lenovo/wmi-other.c
> @@ -47,7 +47,6 @@
>  
>  #include "wmi-capdata.h"
>  #include "wmi-events.h"
> -#include "wmi-gamezone.h"
>  #include "wmi-helpers.h"
>  #include "wmi-other.h"
>  #include "../firmware_attributes_class.h"

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 05/13] platform/x86: lenovo-wmi-other: Add lwmi_attr_id() function
  2026-03-31 18:12 ` [PATCH v6 05/13] platform/x86: lenovo-wmi-other: Add lwmi_attr_id() function Derek J. Clark
@ 2026-04-01 18:29   ` Rong Zhang
  2026-04-01 19:24     ` Derek J. Clark
  0 siblings, 1 reply; 33+ messages in thread
From: Rong Zhang @ 2026-04-01 18:29 UTC (permalink / raw)
  To: Derek J. Clark, Ilpo Järvinen, Hans de Goede
  Cc: Mark Pearson, Armin Wolf, Jonathan Corbet, Kurt Borja,
	platform-driver-x86, linux-kernel

Hi Derek,

On Tue, 2026-03-31 at 18:12 +0000, Derek J. Clark wrote:
> Adds lwmi_attr_id() function. In the same vein as LWMI_ATTR_ID_FAN_RPM(),
> but as a generic, to de-duplicate attribute_id assignment biolerplate.
> 
> Reviewed-by: Rong Zhang <i@rong.moe>
> Tested-by: Rong Zhang <i@rong.moe>
> Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
> Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
> ---
> v6:
>   - Move lwmi_attr_id to wmi-capdata.h as static inline.
> v5:
>   - Move references to cv/cd_mode_id to patch 4/8.
>   - Move lwmi_attr_id to wmi-capdata.c and export with namespace.
> v4:
>   - Switch from macro to static inline to preserve types.
> ---
>  drivers/platform/x86/lenovo/wmi-capdata.c |  8 ++---
>  drivers/platform/x86/lenovo/wmi-capdata.h | 20 ++++++++++++
>  drivers/platform/x86/lenovo/wmi-other.c   | 39 ++++++-----------------
>  3 files changed, 34 insertions(+), 33 deletions(-)
> 
> diff --git a/drivers/platform/x86/lenovo/wmi-capdata.c b/drivers/platform/x86/lenovo/wmi-capdata.c
> index ee1fb02d8e31..169665be4dcf 100644
> --- a/drivers/platform/x86/lenovo/wmi-capdata.c
> +++ b/drivers/platform/x86/lenovo/wmi-capdata.c
> @@ -27,7 +27,6 @@
>  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>  
>  #include <linux/acpi.h>
> -#include <linux/bitfield.h>
>  #include <linux/bug.h>
>  #include <linux/cleanup.h>
>  #include <linux/component.h>
> @@ -48,6 +47,7 @@
>  #include <linux/wmi.h>
>  
>  #include "wmi-capdata.h"
> +#include "wmi-helpers.h"

Bad rebase? IIUC it is unneeded until patch 13.

Thanks,
Rong

>  
>  #define LENOVO_CAPABILITY_DATA_00_GUID "362A3AFE-3D96-4665-8530-96DAD5BB300E"
>  #define LENOVO_CAPABILITY_DATA_01_GUID "7A8F5407-CB67-4D6E-B547-39B3BE018154"
> @@ -58,9 +58,9 @@
>  
>  #define LWMI_FEATURE_ID_FAN_TEST 0x05
>  
> -#define LWMI_ATTR_ID_FAN_TEST							\
> -	(FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, LWMI_DEVICE_ID_FAN) |		\
> -	 FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, LWMI_FEATURE_ID_FAN_TEST))
> +#define LWMI_ATTR_ID_FAN_TEST                                      \
> +	lwmi_attr_id(LWMI_DEVICE_ID_FAN, LWMI_FEATURE_ID_FAN_TEST, \
> +		     LWMI_GZ_THERMAL_MODE_NONE, LWMI_TYPE_ID_NONE)
>  
>  enum lwmi_cd_type {
>  	LENOVO_CAPABILITY_DATA_00,
> diff --git a/drivers/platform/x86/lenovo/wmi-capdata.h b/drivers/platform/x86/lenovo/wmi-capdata.h
> index 8c1df3efcc55..1388eaf4ab4a 100644
> --- a/drivers/platform/x86/lenovo/wmi-capdata.h
> +++ b/drivers/platform/x86/lenovo/wmi-capdata.h
> @@ -6,6 +6,7 @@
>  #define _LENOVO_WMI_CAPDATA_H_
>  
>  #include <linux/bits.h>
> +#include <linux/bitfield.h>
>  #include <linux/types.h>
>  
>  #define LWMI_SUPP_VALID		BIT(0)
> @@ -19,6 +20,8 @@
>  
>  #define LWMI_DEVICE_ID_FAN	0x04
>  
> +#define LWMI_TYPE_ID_NONE 0x00
> +
>  struct component_match;
>  struct device;
>  struct cd_list;
> @@ -57,6 +60,23 @@ struct lwmi_cd_binder {
>  	cd_list_cb_t cd_fan_list_cb;
>  };
>  
> +/**
> + * lwmi_attr_id() - Formats a capability data attribute ID
> + * @dev_id: The u8 corresponding to the device ID.
> + * @feat_id: The u8 corresponding to the feature ID on the device.
> + * @mode_id: The u8 corresponding to the wmi-gamezone mode for set/get.
> + * @type_id: The u8 corresponding to the sub-device.
> + *
> + * Return: u32.
> + */
> +static inline u32 lwmi_attr_id(u8 dev_id, u8 feat_id, u8 mode_id, u8 type_id)
> +{
> +	return (FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, dev_id)   |
> +		FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, feat_id) |
> +		FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode_id) |
> +		FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, type_id));
> +}
> +
>  void lwmi_cd_match_add_all(struct device *master, struct component_match **matchptr);
>  int lwmi_cd00_get_data(struct cd_list *list, u32 attribute_id, struct capdata00 *output);
>  int lwmi_cd01_get_data(struct cd_list *list, u32 attribute_id, struct capdata01 *output);
> diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
> index b24bf98c19ad..d2838ae5934f 100644
> --- a/drivers/platform/x86/lenovo/wmi-other.c
> +++ b/drivers/platform/x86/lenovo/wmi-other.c
> @@ -27,7 +27,6 @@
>   */
>  
>  #include <linux/acpi.h>
> -#include <linux/bitfield.h>
>  #include <linux/cleanup.h>
>  #include <linux/component.h>
>  #include <linux/container_of.h>
> @@ -61,8 +60,6 @@
>  
>  #define LWMI_FEATURE_ID_FAN_RPM 0x03
>  
> -#define LWMI_TYPE_ID_NONE 0x00
> -
>  #define LWMI_FEATURE_VALUE_GET 17
>  #define LWMI_FEATURE_VALUE_SET 18
>  
> @@ -72,10 +69,9 @@
>  
>  #define LWMI_FAN_DIV 100
>  
> -#define LWMI_ATTR_ID_FAN_RPM(x)						\
> -	(FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, LWMI_DEVICE_ID_FAN) |	\
> -	 FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, LWMI_FEATURE_ID_FAN_RPM) |	\
> -	 FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, LWMI_FAN_ID(x)))
> +#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))
>  
>  #define LWMI_OM_FW_ATTR_BASE_PATH "lenovo-wmi-other"
>  #define LWMI_OM_HWMON_NAME "lenovo_wmi_other"
> @@ -714,12 +710,8 @@ static ssize_t attr_capdata01_show(struct kobject *kobj,
>  	u32 attribute_id;
>  	int value, ret;
>  
> -	attribute_id =
> -		FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
> -		FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
> -		FIELD_PREP(LWMI_ATTR_MODE_ID_MASK,
> -			   LWMI_GZ_THERMAL_MODE_CUSTOM) |
> -		FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
> +	attribute_id = lwmi_attr_id(tunable_attr->device_id, tunable_attr->feature_id,
> +				    LWMI_GZ_THERMAL_MODE_CUSTOM, tunable_attr->type_id);
>  
>  	ret = lwmi_cd01_get_data(priv->cd01_list, attribute_id, &capdata);
>  	if (ret)
> @@ -774,7 +766,6 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
>  	struct wmi_method_args_32 args = { 0x0, 0x0 };
>  	struct capdata01 capdata;
>  	enum thermal_mode mode;
> -	u32 attribute_id;
>  	u32 value;
>  	int ret;
>  
> @@ -785,13 +776,10 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
>  	if (mode != LWMI_GZ_THERMAL_MODE_CUSTOM)
>  		return -EBUSY;
>  
> -	attribute_id =
> -		FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
> -		FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
> -		FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) |
> -		FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
> +	args.arg0 = lwmi_attr_id(tunable_attr->device_id, tunable_attr->feature_id,
> +				 mode, tunable_attr->type_id);
>  
> -	ret = lwmi_cd01_get_data(priv->cd01_list, attribute_id, &capdata);
> +	ret = lwmi_cd01_get_data(priv->cd01_list, args.arg0, &capdata);
>  	if (ret)
>  		return ret;
>  
> @@ -802,7 +790,6 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
>  	if (value < capdata.min_value || value > capdata.max_value)
>  		return -EINVAL;
>  
> -	args.arg0 = attribute_id;
>  	args.arg1 = value;
>  
>  	ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_SET,
> @@ -836,7 +823,6 @@ static ssize_t attr_current_value_show(struct kobject *kobj,
>  	struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev);
>  	struct wmi_method_args_32 args = { 0x0, 0x0 };
>  	enum thermal_mode mode;
> -	u32 attribute_id;
>  	int retval;
>  	int ret;
>  
> @@ -844,13 +830,8 @@ static ssize_t attr_current_value_show(struct kobject *kobj,
>  	if (ret)
>  		return ret;
>  
> -	attribute_id =
> -		FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
> -		FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
> -		FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) |
> -		FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
> -
> -	args.arg0 = attribute_id;
> +	args.arg0 = lwmi_attr_id(tunable_attr->device_id, tunable_attr->feature_id,
> +				 mode, tunable_attr->type_id);
>  
>  	ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_GET,
>  				    (unsigned char *)&args, sizeof(args),

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 07/13] platform/x86: lenovo-wmi-other: Add missing CPU tunable attributes
  2026-03-31 18:12 ` [PATCH v6 07/13] platform/x86: lenovo-wmi-other: Add missing CPU tunable attributes Derek J. Clark
  2026-03-31 22:29   ` Kurt Borja
@ 2026-04-01 18:30   ` Rong Zhang
  1 sibling, 0 replies; 33+ messages in thread
From: Rong Zhang @ 2026-04-01 18:30 UTC (permalink / raw)
  To: Derek J. Clark, Ilpo Järvinen, Hans de Goede
  Cc: Mark Pearson, Armin Wolf, Jonathan Corbet, Kurt Borja,
	platform-driver-x86, linux-kernel

Hi Derek,

On Tue, 2026-03-31 at 18:12 +0000, Derek J. Clark wrote:
> Use an enum for all device ID's and CPU attribute feature ID's,
> add missing CPU attributes.
> 
> Reviewed-by: Rong Zhang <i@rong.moe>
> Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
> Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
> ---
> v4:
>   - Align type ID defines.
>   - Align CPU feature enum values.
>   - remove cpu_oc_stat from Documentation.
> v3:
>   - Remove cpu_oc_stat.
> ---
>  .../wmi/devices/lenovo-wmi-other.rst          |   9 ++
>  drivers/platform/x86/lenovo/wmi-capdata.h     |   5 +-
>  drivers/platform/x86/lenovo/wmi-other.c       | 101 +++++++++++++++++-
>  3 files changed, 109 insertions(+), 6 deletions(-)
> 
> diff --git a/Documentation/wmi/devices/lenovo-wmi-other.rst b/Documentation/wmi/devices/lenovo-wmi-other.rst
> index 01d471156738..82c17361e749 100644
> --- a/Documentation/wmi/devices/lenovo-wmi-other.rst
> +++ b/Documentation/wmi/devices/lenovo-wmi-other.rst
> @@ -68,9 +68,18 @@ Each attribute has the following properties:
>   - type
>  
>  The following firmware-attributes are implemented:
> + - cpu_temp: CPU Thermal Load Limit
> + - ppt_cpu_cl: CPU Cross Loading Power Limit
> + - ppt_pl1_apu_spl: Platform Profile Tracking APU Sustained Power Limit
>   - ppt_pl1_spl: Platform Profile Tracking Sustained Power Limit
> + - ppt_pl1_spl_cl: Platform Profile Tracking Cross Loading Sustained Power Limit
> + - ppt_pl1_tau: Exceed Duration for Platform Profile Tracking Sustained Power Limit
>   - ppt_pl2_sppt: Platform Profile Tracking Slow Package Power Tracking
> + - ppt_pl2_sppt_cl: Platform Profile Tracking Cross Loading Slow Package Tracking
>   - ppt_pl3_fppt: Platform Profile Tracking Fast Package Power Tracking
> + - ppt_pl3_fppt_cl: Platform Profile Tracking Cross Loading Fast Package Power Tracking
> + - ppt_pl4_ipl: Platform Profile Trakcing Instantaneous Power Limit

Sashiko.dev suggests:

s/Trakcing/Tracking/g

https://sashiko.dev/#/patchset/20260331181208.421552-1-derekjohn.clark%40gmail.com

> + - ppt_pl4_ipl_cl: Platform Profile Tracking Cross Loading Instantaneous Power Limit
>  
>  LENOVO_FAN_TEST_DATA
>  -------------------------
> diff --git a/drivers/platform/x86/lenovo/wmi-capdata.h b/drivers/platform/x86/lenovo/wmi-capdata.h
> index 1388eaf4ab4a..f2d45cd7a188 100644
> --- a/drivers/platform/x86/lenovo/wmi-capdata.h
> +++ b/drivers/platform/x86/lenovo/wmi-capdata.h
> @@ -18,7 +18,10 @@
>  #define LWMI_ATTR_MODE_ID_MASK	GENMASK(15, 8)
>  #define LWMI_ATTR_TYPE_ID_MASK	GENMASK(7, 0)
>  
> -#define LWMI_DEVICE_ID_FAN	0x04
> +enum lwmi_device_id {
> +	LWMI_DEVICE_ID_CPU = 0x01,
> +	LWMI_DEVICE_ID_FAN = 0x04,
> +};
>  
>  #define LWMI_TYPE_ID_NONE 0x00
>  
> diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
> index f689d19d711a..508186c7410d 100644
> --- a/drivers/platform/x86/lenovo/wmi-other.c
> +++ b/drivers/platform/x86/lenovo/wmi-other.c
> @@ -54,14 +54,21 @@
>  
>  #define LENOVO_OTHER_MODE_GUID "DC2A8805-3A8C-41BA-A6F7-092E0089CD3B"
>  
> -#define LWMI_DEVICE_ID_CPU 0x01
> -
> -#define LWMI_FEATURE_ID_CPU_SPPT 0x01
> -#define LWMI_FEATURE_ID_CPU_SPL 0x02
> -#define LWMI_FEATURE_ID_CPU_FPPT 0x03
> +enum lwmi_feature_id_cpu {
> +	LWMI_FEATURE_ID_CPU_SPPT =	0x01,
> +	LWMI_FEATURE_ID_CPU_SPL =	0x02,
> +	LWMI_FEATURE_ID_CPU_FPPT =	0x03,
> +	LWMI_FEATURE_ID_CPU_TEMP =	0x04,
> +	LWMI_FEATURE_ID_CPU_APU =	0x05,
> +	LWMI_FEATURE_ID_CPU_CL =	0x06,
> +	LWMI_FEATURE_ID_CPU_TAU =	0x07,
> +	LWMI_FEATURE_ID_CPU_IPL =	0x09,
> +};
>  
>  #define LWMI_FEATURE_ID_FAN_RPM 0x03
>  
> +#define LWMI_TYPE_ID_CROSSLOAD	0x01
> +
>  #define LWMI_FEATURE_VALUE_GET 17
>  #define LWMI_FEATURE_VALUE_SET 18
>  
> @@ -557,18 +564,72 @@ static struct tunable_attr_01 ppt_pl1_spl = {
>  	.type_id = LWMI_TYPE_ID_NONE,
>  };
>  
> +static struct tunable_attr_01 ppt_pl1_spl_cl = {
> +	.device_id = LWMI_DEVICE_ID_CPU,
> +	.feature_id = LWMI_FEATURE_ID_CPU_SPL,
> +	.type_id = LWMI_TYPE_ID_CROSSLOAD,
> +};
> +
>  static struct tunable_attr_01 ppt_pl2_sppt = {
>  	.device_id = LWMI_DEVICE_ID_CPU,
>  	.feature_id = LWMI_FEATURE_ID_CPU_SPPT,
>  	.type_id = LWMI_TYPE_ID_NONE,
>  };
>  
> +static struct tunable_attr_01 ppt_pl2_sppt_cl = {
> +	.device_id = LWMI_DEVICE_ID_CPU,
> +	.feature_id = LWMI_FEATURE_ID_CPU_SPPT,
> +	.type_id = LWMI_TYPE_ID_CROSSLOAD,
> +};
> +
>  static struct tunable_attr_01 ppt_pl3_fppt = {
>  	.device_id = LWMI_DEVICE_ID_CPU,
>  	.feature_id = LWMI_FEATURE_ID_CPU_FPPT,
>  	.type_id = LWMI_TYPE_ID_NONE,
>  };
>  
> +static struct tunable_attr_01 ppt_pl3_fppt_cl = {
> +	.device_id = LWMI_DEVICE_ID_CPU,
> +	.feature_id = LWMI_FEATURE_ID_CPU_FPPT,
> +	.type_id = LWMI_TYPE_ID_CROSSLOAD,
> +};
> +
> +static struct tunable_attr_01 cpu_temp = {
> +	.device_id = LWMI_DEVICE_ID_CPU,
> +	.feature_id = LWMI_FEATURE_ID_CPU_TEMP,
> +	.type_id = LWMI_TYPE_ID_NONE,
> +};
> +
> +static struct tunable_attr_01 ppt_pl1_apu_spl = {
> +	.device_id = LWMI_DEVICE_ID_CPU,
> +	.feature_id = LWMI_FEATURE_ID_CPU_APU,
> +	.type_id = LWMI_TYPE_ID_NONE,
> +};
> +
> +static struct tunable_attr_01 ppt_cpu_cl = {
> +	.device_id = LWMI_DEVICE_ID_CPU,
> +	.feature_id = LWMI_FEATURE_ID_CPU_CL,
> +	.type_id = LWMI_TYPE_ID_NONE,
> +};
> +
> +static struct tunable_attr_01 ppt_pl1_tau = {
> +	.device_id = LWMI_DEVICE_ID_CPU,
> +	.feature_id = LWMI_FEATURE_ID_CPU_TAU,
> +	.type_id = LWMI_TYPE_ID_NONE,
> +};
> +
> +static struct tunable_attr_01 ppt_pl4_ipl = {
> +	.device_id = LWMI_DEVICE_ID_CPU,
> +	.feature_id = LWMI_FEATURE_ID_CPU_IPL,
> +	.type_id = LWMI_TYPE_ID_NONE,
> +};
> +
> +static struct tunable_attr_01 ppt_pl4_ipl_cl = {
> +	.device_id = LWMI_DEVICE_ID_CPU,
> +	.feature_id = LWMI_FEATURE_ID_CPU_IPL,
> +	.type_id = LWMI_TYPE_ID_CROSSLOAD,
> +};
> +
>  struct capdata01_attr_group {
>  	const struct attribute_group *attr_group;
>  	struct tunable_attr_01 *tunable_attr;
> @@ -794,6 +855,8 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
>  	if (value < capdata.min_value || value > capdata.max_value)
>  		return -EINVAL;
>  
> +	args.arg0 = lwmi_attr_id(tunable_attr->device_id, tunable_attr->feature_id,
> +				 tunable_attr->cv_mode_id, tunable_attr->type_id);

This should have been added to patch 6. Bad rebase?

(found by sashiko.dev)

Thanks,
Rong

>  	args.arg1 = value;
>  
>  	ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_SET,
> @@ -998,17 +1061,45 @@ static bool lwmi_attr_01_is_supported(struct tunable_attr_01 *tunable_attr)
>  		.name = _fsname, .attrs = _attrname##_attrs               \
>  	}
>  
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(cpu_temp, "cpu_temp",
> +			      "Set the CPU thermal load limit");
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_cpu_cl, "ppt_cpu_cl",
> +			      "Set the CPU cross loading power limit");
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl1_apu_spl, "ppt_pl1_apu_spl",
> +			      "Set the APU sustained power limit");
>  LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl1_spl, "ppt_pl1_spl",
>  			      "Set the CPU sustained power limit");
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl1_spl_cl, "ppt_pl1_spl_cl",
> +			      "Set the CPU cross loading sustained power limit");
>  LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl2_sppt, "ppt_pl2_sppt",
>  			      "Set the CPU slow package power tracking limit");
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl2_sppt_cl, "ppt_pl2_sppt_cl",
> +			      "Set the CPU cross loading slow package power tracking limit");
>  LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl3_fppt, "ppt_pl3_fppt",
>  			      "Set the CPU fast package power tracking limit");
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl3_fppt_cl, "ppt_pl3_fppt_cl",
> +			      "Set the CPU cross loading fast package power tracking limit");
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl1_tau, "ppt_pl1_tau",
> +			      "Set the CPU sustained power limit exceed duration");
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl4_ipl, "ppt_pl4_ipl",
> +			      "Set the CPU instantaneous power limit");
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl4_ipl_cl, "ppt_pl4_ipl_cl",
> +			      "Set the CPU cross loading instantaneous power limit");
> +
>  
>  static struct capdata01_attr_group cd01_attr_groups[] = {
> +	{ &cpu_temp_attr_group, &cpu_temp },
> +	{ &ppt_cpu_cl_attr_group, &ppt_cpu_cl },
> +	{ &ppt_pl1_apu_spl_attr_group, &ppt_pl1_apu_spl },
>  	{ &ppt_pl1_spl_attr_group, &ppt_pl1_spl },
> +	{ &ppt_pl1_spl_cl_attr_group, &ppt_pl1_spl_cl },
> +	{ &ppt_pl1_tau_attr_group, &ppt_pl1_tau },
>  	{ &ppt_pl2_sppt_attr_group, &ppt_pl2_sppt },
> +	{ &ppt_pl2_sppt_cl_attr_group, &ppt_pl2_sppt_cl },
>  	{ &ppt_pl3_fppt_attr_group, &ppt_pl3_fppt },
> +	{ &ppt_pl3_fppt_cl_attr_group, &ppt_pl3_fppt_cl },
> +	{ &ppt_pl4_ipl_attr_group, &ppt_pl4_ipl },
> +	{ &ppt_pl4_ipl_cl_attr_group, &ppt_pl4_ipl_cl },
>  	{},
>  };
>  

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 08/13] platform/x86: lenovo-wmi-other: Add GPU tunable attributes
  2026-03-31 18:12 ` [PATCH v6 08/13] platform/x86: lenovo-wmi-other: Add GPU " Derek J. Clark
  2026-03-31 22:29   ` Kurt Borja
@ 2026-04-01 18:30   ` Rong Zhang
  1 sibling, 0 replies; 33+ messages in thread
From: Rong Zhang @ 2026-04-01 18:30 UTC (permalink / raw)
  To: Derek J. Clark, Ilpo Järvinen, Hans de Goede
  Cc: Mark Pearson, Armin Wolf, Jonathan Corbet, Kurt Borja,
	platform-driver-x86, linux-kernel

Hi Derek,

On Tue, 2026-03-31 at 18:12 +0000, Derek J. Clark wrote:
> Use an enum for all GPU attribute feature ID's and add GPU attributes.
> 
> Reviewed-by: Rong Zhang <i@rong.moe>
> Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
> Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
> ---
> v4:
>   - Align CPU feature enum values.
>   - Remove gpu_oc_stat from Documentation.
> v3:
>   - Remove gpu_oc_stat.
> ---
>  .../wmi/devices/lenovo-wmi-other.rst          |  10 ++
>  drivers/platform/x86/lenovo/wmi-capdata.h     |   1 +
>  drivers/platform/x86/lenovo/wmi-other.c       | 105 ++++++++++++++++++
>  3 files changed, 116 insertions(+)
> 
> diff --git a/Documentation/wmi/devices/lenovo-wmi-other.rst b/Documentation/wmi/devices/lenovo-wmi-other.rst
> index 82c17361e749..bfbea59637bd 100644
> --- a/Documentation/wmi/devices/lenovo-wmi-other.rst
> +++ b/Documentation/wmi/devices/lenovo-wmi-other.rst
> @@ -69,6 +69,16 @@ Each attribute has the following properties:
>  
>  The following firmware-attributes are implemented:
>   - cpu_temp: CPU Thermal Load Limit
> + - dgpu_boost_clk: Dedicated GPU Boost Clock
> + - dgpu_enable: Dedicated GPU Enabled Status
> + - gpu_didvid: GPU Device Identifier and Vendor Identifier
> + - gpu_mode: GPU Mode by Power Limit
> + - gpu_nv_ac_offset: Nvidia GPU AC Total Processing Power Baseline Offset
> + - gpu_nv_bpl: Nvidia GPU Base Power Limit
> + - gpu_nv_cpu_boost: Nvidia GPU to CPU Dynamic Boost Limit
> + - gpu_nv_ctgp: Nvidia GPU Configurable Total Graphics Power
> + - gpu_nv_ppab: Nvidia GPU Power Performance Aware Boost Limit
> + - gpu_temp: GPU Thermal Load Limit
>   - ppt_cpu_cl: CPU Cross Loading Power Limit
>   - ppt_pl1_apu_spl: Platform Profile Tracking APU Sustained Power Limit
>   - ppt_pl1_spl: Platform Profile Tracking Sustained Power Limit
> diff --git a/drivers/platform/x86/lenovo/wmi-capdata.h b/drivers/platform/x86/lenovo/wmi-capdata.h
> index f2d45cd7a188..891b12ca1db6 100644
> --- a/drivers/platform/x86/lenovo/wmi-capdata.h
> +++ b/drivers/platform/x86/lenovo/wmi-capdata.h
> @@ -20,6 +20,7 @@
>  
>  enum lwmi_device_id {
>  	LWMI_DEVICE_ID_CPU = 0x01,
> +	LWMI_DEVICE_ID_GPU = 0x02,
>  	LWMI_DEVICE_ID_FAN = 0x04,
>  };
>  
> diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
> index 508186c7410d..2792671ac1de 100644
> --- a/drivers/platform/x86/lenovo/wmi-other.c
> +++ b/drivers/platform/x86/lenovo/wmi-other.c
> @@ -65,6 +65,19 @@ enum lwmi_feature_id_cpu {
>  	LWMI_FEATURE_ID_CPU_IPL =	0x09,
>  };
>  
> +enum lwmi_feature_id_gpu {
> +	LWMI_FEATURE_ID_GPU_NV_PPAB =		0x01,
> +	LWMI_FEATURE_ID_GPU_NV_CTGP =		0x02,
> +	LWMI_FEATURE_ID_GPU_TEMP =		0x03,
> +	LWMI_FEATURE_ID_GPU_AC_OFFSET =		0x04,
> +	LWMI_FEATURE_ID_DGPU_BOOST_CLK =	0x06,
> +	LWMI_FEATURE_ID_DGPU_EN =		0x07,
> +	LWMI_FEATURE_ID_GPU_MODE =		0x08,
> +	LWMI_FEATURE_ID_DGPU_DIDVID =		0x09,
> +	LWMI_FEATURE_ID_GPU_NV_BPL =		0x0a,
> +	LWMI_FEATURE_ID_GPU_NV_CPU_BOOST =	0x0b,
> +};
> +
>  #define LWMI_FEATURE_ID_FAN_RPM 0x03
>  
>  #define LWMI_TYPE_ID_CROSSLOAD	0x01
> @@ -630,6 +643,66 @@ static struct tunable_attr_01 ppt_pl4_ipl_cl = {
>  	.type_id = LWMI_TYPE_ID_CROSSLOAD,
>  };
>  
> +static struct tunable_attr_01 gpu_nv_ppab = {
> +	.device_id = LWMI_DEVICE_ID_GPU,
> +	.feature_id = LWMI_FEATURE_ID_GPU_NV_PPAB,
> +	.type_id = LWMI_TYPE_ID_NONE,
> +};
> +
> +static struct tunable_attr_01 gpu_nv_ctgp = {
> +	.device_id = LWMI_DEVICE_ID_GPU,
> +	.feature_id = LWMI_FEATURE_ID_GPU_NV_CTGP,
> +	.type_id = LWMI_TYPE_ID_NONE,
> +};
> +
> +static struct tunable_attr_01 gpu_temp = {
> +	.device_id = LWMI_DEVICE_ID_GPU,
> +	.feature_id = LWMI_FEATURE_ID_GPU_TEMP,
> +	.type_id = LWMI_TYPE_ID_NONE,
> +};
> +
> +static struct tunable_attr_01 gpu_nv_ac_offset = {
> +	.device_id = LWMI_DEVICE_ID_GPU,
> +	.feature_id = LWMI_FEATURE_ID_GPU_AC_OFFSET,
> +	.type_id = LWMI_TYPE_ID_NONE,
> +};
> +
> +static struct tunable_attr_01 dgpu_boost_clk = {
> +	.device_id = LWMI_DEVICE_ID_GPU,
> +	.feature_id = LWMI_FEATURE_ID_DGPU_BOOST_CLK,
> +	.type_id = LWMI_TYPE_ID_NONE,
> +};
> +
> +static struct tunable_attr_01 dgpu_enable = {
> +	.device_id = LWMI_DEVICE_ID_GPU,
> +	.feature_id = LWMI_FEATURE_ID_DGPU_EN,
> +	.type_id = LWMI_TYPE_ID_NONE,
> +};
> +
> +static struct tunable_attr_01 gpu_mode = {
> +	.device_id = LWMI_DEVICE_ID_GPU,
> +	.feature_id = LWMI_FEATURE_ID_GPU_MODE,
> +	.type_id = LWMI_TYPE_ID_NONE,
> +};
> +
> +static struct tunable_attr_01 dgpu_didvid = {
> +	.device_id = LWMI_DEVICE_ID_GPU,
> +	.feature_id = LWMI_FEATURE_ID_DGPU_DIDVID,
> +	.type_id = LWMI_TYPE_ID_NONE,
> +};
> +
> +static struct tunable_attr_01 gpu_nv_bpl = {
> +	.device_id = LWMI_DEVICE_ID_GPU,
> +	.feature_id = LWMI_FEATURE_ID_GPU_NV_BPL,
> +	.type_id = LWMI_TYPE_ID_NONE,
> +};
> +
> +static struct tunable_attr_01 gpu_nv_cpu_boost = {
> +	.device_id = LWMI_DEVICE_ID_GPU,
> +	.feature_id = LWMI_FEATURE_ID_GPU_NV_CPU_BOOST,
> +	.type_id = LWMI_TYPE_ID_NONE,
> +};
> +
>  struct capdata01_attr_group {
>  	const struct attribute_group *attr_group;
>  	struct tunable_attr_01 *tunable_attr;
> @@ -1061,6 +1134,7 @@ static bool lwmi_attr_01_is_supported(struct tunable_attr_01 *tunable_attr)
>  		.name = _fsname, .attrs = _attrname##_attrs               \
>  	}
>  
> +/* CPU tunable attributes */
>  LWMI_ATTR_GROUP_TUNABLE_CAP01(cpu_temp, "cpu_temp",
>  			      "Set the CPU thermal load limit");
>  LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_cpu_cl, "ppt_cpu_cl",
> @@ -1086,9 +1160,40 @@ LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl4_ipl, "ppt_pl4_ipl",
>  LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl4_ipl_cl, "ppt_pl4_ipl_cl",
>  			      "Set the CPU cross loading instantaneous power limit");
>  
> +/* GPU tunable attributes */
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(dgpu_boost_clk, "gpu_boost_clk",

Typo here?

s/"gpu_boost_clk"/"dgpu_boost_clk"/

(found by sashiko.dev)

https://sashiko.dev/#/patchset/20260331181208.421552-1-derekjohn.clark%40gmail.com

Thanks,
Rong

> +			      "Set the dedicated GPU boost clock");
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(dgpu_didvid, "gpu_didvid",
> +			      "Get the GPU device identifier and vendor identifier");
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(dgpu_enable, "dgpu_enable",
> +			      "Set the dedicated Nvidia GPU enabled status");
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_mode, "gpu_mode",
> +			      "Set the GPU mode by power limit");
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_nv_ac_offset, "gpu_nv_ac_offset",
> +			      "Set the Nvidia GPU AC total processing power baseline offset");
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_nv_bpl, "gpu_nv_bpl",
> +			      "Set the Nvidia GPU base power limit");
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_nv_cpu_boost, "gpu_nv_cpu_boost",
> +			      "Set the Nvidia GPU to CPU dynamic boost limit");
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_nv_ctgp, "gpu_nv_ctgp",
> +			      "Set the GPU configurable total graphics power");
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_nv_ppab, "gpu_nv_ppab",
> +			      "Set the Nvidia GPU power performance aware boost limit");
> +LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_temp, "gpu_temp",
> +			      "Set the GPU thermal load limit");
>  
>  static struct capdata01_attr_group cd01_attr_groups[] = {
>  	{ &cpu_temp_attr_group, &cpu_temp },
> +	{ &dgpu_boost_clk_attr_group, &dgpu_boost_clk },
> +	{ &dgpu_didvid_attr_group, &dgpu_didvid },
> +	{ &dgpu_enable_attr_group, &dgpu_enable },
> +	{ &gpu_mode_attr_group, &gpu_mode },
> +	{ &gpu_nv_ac_offset_attr_group, &gpu_nv_ac_offset },
> +	{ &gpu_nv_bpl_attr_group, &gpu_nv_bpl },
> +	{ &gpu_nv_cpu_boost_attr_group, &gpu_nv_cpu_boost },
> +	{ &gpu_nv_ctgp_attr_group, &gpu_nv_ctgp },
> +	{ &gpu_nv_ppab_attr_group, &gpu_nv_ppab },
> +	{ &gpu_temp_attr_group, &gpu_temp },
>  	{ &ppt_cpu_cl_attr_group, &ppt_cpu_cl },
>  	{ &ppt_pl1_apu_spl_attr_group, &ppt_pl1_apu_spl },
>  	{ &ppt_pl1_spl_attr_group, &ppt_pl1_spl },

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 10/13] platform/x86: lenovo-wmi-other: Add WMI battery charge limiting
  2026-03-31 18:12 ` [PATCH v6 10/13] platform/x86: lenovo-wmi-other: Add WMI battery charge limiting Derek J. Clark
@ 2026-04-01 18:32   ` Rong Zhang
  0 siblings, 0 replies; 33+ messages in thread
From: Rong Zhang @ 2026-04-01 18:32 UTC (permalink / raw)
  To: Derek J. Clark, Ilpo Järvinen, Hans de Goede
  Cc: Mark Pearson, Armin Wolf, Jonathan Corbet, Kurt Borja,
	platform-driver-x86, linux-kernel

Hi Derek,

On Tue, 2026-03-31 at 18:12 +0000, Derek J. Clark wrote:
> Add charge-type power supply extension for devices that support WMI based
> charge enable/disable.
> 
> Lenovo Legion devices that implement WMI function and capdata ID
> 0x03010001 in their BIOS are able to enable or disable charging at 80%
> through the lenovo-wmi-other interface. Add a charge_type power supply
> extension to expose this capability to the sysfs.
> 
> The ideapad_laptop driver can also provide the charge_type attribute. To
> avoid conflicts between the drivers, get the acpi_handle and do the same
> check that ideapad_laptop does when it enables the feature. If the
> feature is supported in ideapad_laptop, abort adding the extension from
> lenovo-wmi-other. The ACPI method is more reliable when both are
> present, from my testing, so we can prefer that implementation and do
> not need to worry about de-conflicting from inside that driver. A new
> module parameter, force_load_psy_ext, is provided to bypass this ACPI
> check, if desired.
> 
> Reviewed-by: Rong Zhang <i@rong.moe>
> Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
> Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
> ---
> v6:
>   - Check feature flags to determine if the extension should be loaded
>     and if it is writable.
>   - Zero initialize wmi_method_args_32.
>   - Fix formatting.
> v5:
>   - Use switch statement instead of if for battery charge state set/get.
>   - use force_load_psy_ext to skip all ACPI interactions.
>   - Various formatting fixes.
> v4:
>   - Remove unused defines.
>   - Disambiguate charging defines by renaming them to be more consistent
>     with the kernel modes they represent.
>   - Add module parameter to ignore ACPI checks.
>   - Don't fail if the ACPI handle isn't found, skip the ACPI check
>     instead.
> ---
>  drivers/platform/x86/lenovo/Kconfig       |   1 +
>  drivers/platform/x86/lenovo/wmi-capdata.h |   1 +
>  drivers/platform/x86/lenovo/wmi-other.c   | 274 +++++++++++++++++++++-
>  3 files changed, 275 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/platform/x86/lenovo/Kconfig b/drivers/platform/x86/lenovo/Kconfig
> index f885127b007f..75a8b144b0da 100644
> --- a/drivers/platform/x86/lenovo/Kconfig
> +++ b/drivers/platform/x86/lenovo/Kconfig
> @@ -263,6 +263,7 @@ config LENOVO_WMI_GAMEZONE
>  config LENOVO_WMI_TUNING
>  	tristate "Lenovo Other Mode WMI Driver"
>  	depends on ACPI_WMI
> +	depends on ACPI_BATTERY
>  	select HWMON
>  	select FW_ATTR_CLASS
>  	select LENOVO_WMI_CAPDATA
> diff --git a/drivers/platform/x86/lenovo/wmi-capdata.h b/drivers/platform/x86/lenovo/wmi-capdata.h
> index 891b12ca1db6..e0a30f2c0c87 100644
> --- a/drivers/platform/x86/lenovo/wmi-capdata.h
> +++ b/drivers/platform/x86/lenovo/wmi-capdata.h
> @@ -21,6 +21,7 @@
>  enum lwmi_device_id {
>  	LWMI_DEVICE_ID_CPU = 0x01,
>  	LWMI_DEVICE_ID_GPU = 0x02,
> +	LWMI_DEVICE_ID_PSU = 0x03,
>  	LWMI_DEVICE_ID_FAN = 0x04,
>  };
>  
> diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
> index 0366acf04ae5..e0633c42972c 100644
> --- a/drivers/platform/x86/lenovo/wmi-other.c
> +++ b/drivers/platform/x86/lenovo/wmi-other.c
> @@ -43,9 +43,12 @@
>  #include <linux/module.h>
>  #include <linux/notifier.h>
>  #include <linux/platform_profile.h>
> +#include <linux/power_supply.h>
>  #include <linux/types.h>
>  #include <linux/wmi.h>
>  
> +#include <acpi/battery.h>
> +
>  #include "wmi-capdata.h"
>  #include "wmi-events.h"
>  #include "wmi-helpers.h"
> @@ -78,9 +81,11 @@ enum lwmi_feature_id_gpu {
>  	LWMI_FEATURE_ID_GPU_NV_CPU_BOOST =	0x0b,
>  };
>  
> -#define LWMI_FEATURE_ID_FAN_RPM 0x03
> +#define LWMI_FEATURE_ID_FAN_RPM		0x03
> +#define LWMI_FEATURE_ID_PSU_CHARGE_TYPE	0x01
>  
>  #define LWMI_TYPE_ID_CROSSLOAD	0x01
> +#define LWMI_TYPE_ID_PSU_AC	0x01
>  
>  #define LWMI_FEATURE_VALUE_GET 17
>  #define LWMI_FEATURE_VALUE_SET 18
> @@ -91,10 +96,17 @@ enum lwmi_feature_id_gpu {
>  
>  #define LWMI_FAN_DIV 100
>  
> +#define LWMI_CHARGE_TYPE_STANDARD	0x00
> +#define LWMI_CHARGE_TYPE_LONGLIFE	0x01
> +
>  #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))
>  
> +#define LWMI_ATTR_ID_PSU(feat, type)			\
> +	lwmi_attr_id(LWMI_DEVICE_ID_PSU, feat,		\
> +		     LWMI_GZ_THERMAL_MODE_NONE, type)
> +
>  #define LWMI_OM_SYSFS_NAME "lenovo-wmi-other"
>  #define LWMI_OM_HWMON_NAME "lenovo_wmi_other"
>  
> @@ -136,6 +148,8 @@ struct lwmi_om_priv {
>  		bool capdata00_collected : 1;
>  		bool capdata_fan_collected : 1;
>  	} fan_flags;
> +
> +	struct acpi_battery_hook battery_hook;
>  };
>  
>  /*
> @@ -560,6 +574,263 @@ static void lwmi_om_fan_info_collect_cd_fan(struct device *dev, struct cd_list *
>  	lwmi_om_hwmon_add(priv);
>  }
>  
> +/* ======== Power Supply Extension (component: lenovo-wmi-capdata 00) ======== */
> +
> +/**
> + * lwmi_psy_ext_get_prop() - Get a power_supply_ext property
> + * @ps: The battery that was extended
> + * @ext: The extension
> + * @ext_data: Pointer the lwmi_om_priv drvdata
> + * @prop: The property to read
> + * @val: The value to return
> + *
> + * Writes the given value to the power_supply_ext property
> + *
> + * Return: 0 on success, or an error
> + */
> +static int lwmi_psy_ext_get_prop(struct power_supply *ps,
> +				 const struct power_supply_ext *ext,
> +				 void *ext_data,
> +				 enum power_supply_property prop,
> +				 union power_supply_propval *val)
> +{
> +	struct wmi_method_args_32 args = { 0x0, 0x0 };
> +	struct lwmi_om_priv *priv = ext_data;
> +	u32 retval;
> +	int ret;
> +
> +	args.arg0 = LWMI_ATTR_ID_PSU(LWMI_FEATURE_ID_PSU_CHARGE_TYPE, LWMI_TYPE_ID_PSU_AC);
> +
> +	ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_GET,
> +				    (unsigned char *)&args, sizeof(args),
> +				    &retval);
> +	if (ret)
> +		return ret;
> +
> +	dev_dbg(&priv->wdev->dev, "Got return value %#x for property %#x\n", retval, prop);
> +
> +	switch (retval) {
> +	case LWMI_CHARGE_TYPE_LONGLIFE:
> +		val->intval = POWER_SUPPLY_CHARGE_TYPE_LONGLIFE;
> +		break;
> +	case LWMI_CHARGE_TYPE_STANDARD:
> +		val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
> +		break;
> +	default:
> +		dev_err(&priv->wdev->dev, "Got invalid charge value: %#x\n", retval);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * lwmi_psy_ext_set_prop() - Set a power_supply_ext property
> + * @ps: The battery that was extended
> + * @ext: The extension
> + * @ext_data: Pointer the lwmi_om_priv drvdata
> + * @prop: The property to write
> + * @val: The value to write
> + *
> + * Writes the given value to the power_supply_ext property
> + *
> + * Return: 0 on success, or an error
> + */
> +static int lwmi_psy_ext_set_prop(struct power_supply *ps,
> +				 const struct power_supply_ext *ext,
> +				 void *ext_data,
> +				 enum power_supply_property prop,
> +				 const union power_supply_propval *val)
> +{
> +	struct wmi_method_args_32 args = { 0x0, 0x0 };
> +	struct lwmi_om_priv *priv = ext_data;
> +
> +	args.arg0 = LWMI_ATTR_ID_PSU(LWMI_FEATURE_ID_PSU_CHARGE_TYPE, LWMI_TYPE_ID_PSU_AC);
> +	switch (val->intval) {
> +	case POWER_SUPPLY_CHARGE_TYPE_LONGLIFE:
> +		args.arg1 = LWMI_CHARGE_TYPE_LONGLIFE;
> +		break;
> +	case POWER_SUPPLY_CHARGE_TYPE_STANDARD:
> +		args.arg1 = LWMI_CHARGE_TYPE_STANDARD;
> +		break;
> +	default:
> +		dev_err(&priv->wdev->dev, "Got invalid charge value: %#x\n", val->intval);
> +		return -EINVAL;
> +	}
> +
> +	dev_dbg(&priv->wdev->dev, "Attempting to set %#010x for property %#x to %#x\n",
> +		args.arg0, prop, args.arg1);
> +
> +	return lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_SET,
> +				     (unsigned char *)&args, sizeof(args), NULL);
> +}
> +
> +/**
> + * lwmi_psy_prop_is_supported() - Determine if the property is supported
> + * @priv: Pointer the lwmi_om_priv drvdata
> + *
> + * Checks capdata 00 to determine if the property is supported.
> + *
> + * Return: true if readable, or false
> + */
> +static bool lwmi_psy_prop_is_supported(struct lwmi_om_priv *priv)
> +{
> +	u32 attribute_id = LWMI_ATTR_ID_PSU(LWMI_FEATURE_ID_PSU_CHARGE_TYPE, LWMI_TYPE_ID_PSU_AC);
> +	struct capdata00 capdata;
> +	int ret;
> +
> +	ret = lwmi_cd00_get_data(priv->cd00_list, attribute_id, &capdata);
> +	if (ret)
> +		return false;
> +
> +	dev_dbg(&priv->wdev->dev, "Battery charge mode (%#010x) support level: %#x\n",
> +		attribute_id, capdata.supported);
> +
> +	return ((capdata.supported & LWMI_SUPP_VALID) && (capdata.supported & LWMI_SUPP_GET));
> +}
> +
> +/**
> + * lwmi_psy_prop_is_writeable() - Determine if the property is writeable
> + * @ps: The battery that was extended
> + * @ext: The extension
> + * @ext_data: Pointer the lwmi_om_priv drvdata
> + * @prop: The property to check
> + *
> + * Checks capdata 00 to determine if the property is writable.
> + *
> + * Return: true if writable, or false
> + */
> +static int lwmi_psy_prop_is_writeable(struct power_supply *ps,
> +				      const struct power_supply_ext *ext,
> +				      void *ext_data,
> +				      enum power_supply_property prop)
> +{
> +	u32 attribute_id = LWMI_ATTR_ID_PSU(LWMI_FEATURE_ID_PSU_CHARGE_TYPE, LWMI_TYPE_ID_PSU_AC);
> +	struct lwmi_om_priv *priv = ext_data;
> +	struct capdata00 capdata;
> +	int ret;
> +
> +	ret = lwmi_cd00_get_data(priv->cd00_list, attribute_id, &capdata);
> +	if (ret)
> +		return false;
> +
> +	return !!(capdata.supported & LWMI_SUPP_SET);
> +}
> +
> +static const enum power_supply_property lwmi_psy_ext_props[] = {
> +	POWER_SUPPLY_PROP_CHARGE_TYPES,
> +};
> +
> +static const struct power_supply_ext lwmi_psy_ext = {
> +	.name			= LWMI_OM_SYSFS_NAME,
> +	.properties		= lwmi_psy_ext_props,
> +	.num_properties		= ARRAY_SIZE(lwmi_psy_ext_props),
> +	.charge_types		= (BIT(POWER_SUPPLY_CHARGE_TYPE_STANDARD) |
> +				   BIT(POWER_SUPPLY_CHARGE_TYPE_LONGLIFE)),
> +	.get_property		= lwmi_psy_ext_get_prop,
> +	.set_property		= lwmi_psy_ext_set_prop,
> +	.property_is_writeable	= lwmi_psy_prop_is_writeable,
> +};
> +
> +/**
> + * lwmi_add_battery() - Connect the power_supply_ext
> + * @battery: The battery to extend
> + * @hook: The driver hook used to extend the battery
> + *
> + * Return: 0 on success, or an error.
> + */
> +static int lwmi_add_battery(struct power_supply *battery, struct acpi_battery_hook *hook)
> +{
> +	struct lwmi_om_priv *priv = container_of(hook, struct lwmi_om_priv, battery_hook);
> +
> +	return power_supply_register_extension(battery, &lwmi_psy_ext, &priv->wdev->dev, priv);
> +}
> +
> +/**
> + * lwmi_remove_battery() - Disconnect the power_supply_ext
> + * @battery: The battery that was extended
> + * @hook: The driver hook used to extend the battery
> + *
> + * Return: 0 on success, or an error.
> + */
> +static int lwmi_remove_battery(struct power_supply *battery, struct acpi_battery_hook *hook)
> +{
> +	power_supply_unregister_extension(battery, &lwmi_psy_ext);
> +	return 0;
> +}
> +
> +/**
> + * lwmi_acpi_match() - Attempts to return the ideapad acpi handle
> + * @handle: The ACPI handle that manages battery charging
> + * @lvl: Unused
> + * @context: Void pointer to the acpi_handle object to return
> + * @retval: Unused
> + *
> + * Checks if the ideapad_laptop driver is going to manage charge_type first,
> + * then if not, hooks the battery to our WMI methods.
> + *
> + * Return: AE_CTRL_TERMINATE if found, AE_OK if not found.
> + */
> +static acpi_status lwmi_acpi_match(acpi_handle handle, u32 lvl,
> +				   void *context, void **retval)
> +{
> +	acpi_handle *ahand = context;
> +
> +	if (!handle)
> +		return AE_OK;
> +
> +	*ahand = handle;
> +
> +	return AE_CTRL_TERMINATE;
> +}
> +
> +static bool force_load_psy_ext;
> +module_param(force_load_psy_ext, bool, 0444);
> +MODULE_PARM_DESC(force_load_psy_ext,
> +	"This option will skip checking if the ideapad_laptop driver will conflict "
> +	"with adding an extension to set the battery charge type. It is recommended "
> +	"to blacklist the ideapad driver before using this option.");
> +
> +/**
> + * lwmi_om_psy_ext_init() - Hooks power supply extension to device battery
> + * @priv: Driver private data
> + *
> + * Checks if the ideapad_laptop driver is going to manage charge_type first,
> + * then if not, hooks the battery to our WMI methods.
> + */
> +static void lwmi_om_psy_ext_init(struct lwmi_om_priv *priv)
> +{
> +	static const char * const ideapad_hid = "VPC2004";
> +	acpi_handle handle = NULL;
> +	int ret;
> +
> +	/* Deconflict ideapad_laptop driver */
> +	if (force_load_psy_ext)
> +		goto load_psy_ext;
> +
> +	if (!lwmi_psy_prop_is_supported(priv))
> +		return;
> +
> +	ret = acpi_get_devices(ideapad_hid, lwmi_acpi_match, &handle, NULL);
> +	if (ret)
> +		return;
> +
> +	if (handle && acpi_has_method(handle, "GBMD") && acpi_has_method(handle, "SBMC")) {
> +		dev_dbg(&priv->wdev->dev, "ideapad_laptop driver manages battery for device\n");
> +		return;
> +	}
> +
> +load_psy_ext:
> +	/* Add battery hooks */
> +	priv->battery_hook.add_battery = lwmi_add_battery;
> +	priv->battery_hook.remove_battery = lwmi_remove_battery;
> +	priv->battery_hook.name = "Lenovo WMI Other Battery Extension";
> +
> +	ret = devm_battery_hook_register(&priv->wdev->dev, &priv->battery_hook);

Using devm here ties power_supply_ext's lifecycle to the wmi-other
device instead of the aggregate device, resulting in power_supply_ext
being registered multiple times if components are rebound.

Use plain battery_hook_register() here and emit a call to
battery_hook_unregister() in the unbind callback like the hwmon device.
The registration status should also be tracked by adding a flag to
lwmi_om_priv (or NULLifying some contents of priv->battery_hook if the
registration is skipped or if it's unregistered).

Thanks,
Rong

> +	if (ret)
> +		dev_err(&priv->wdev->dev, "Error during battery hook: %i\n", ret);
> +}
> +
>  /* ======== fw_attributes (component: lenovo-wmi-capdata 01) ======== */
>  
>  struct tunable_attr_01 {
> @@ -1317,6 +1588,7 @@ static int lwmi_om_master_bind(struct device *dev)
>  		return -ENODEV;
>  
>  	lwmi_om_fan_info_collect_cd00(priv);
> +	lwmi_om_psy_ext_init(priv);
>  
>  	return lwmi_om_fw_attr_add(priv);
>  }

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 11/13] platform/x86: lenovo: Decouple lenovo-wmi-gamezone and lenovo-wmi-other
  2026-03-31 18:12 ` [PATCH v6 11/13] platform/x86: lenovo: Decouple lenovo-wmi-gamezone and lenovo-wmi-other Derek J. Clark
@ 2026-04-01 18:34   ` Rong Zhang
  0 siblings, 0 replies; 33+ messages in thread
From: Rong Zhang @ 2026-04-01 18:34 UTC (permalink / raw)
  To: Derek J. Clark, Ilpo Järvinen, Hans de Goede
  Cc: Mark Pearson, Armin Wolf, Jonathan Corbet, Kurt Borja,
	platform-driver-x86, linux-kernel, stable, kernel test robot

Hi Derek,

On Tue, 2026-03-31 at 18:12 +0000, Derek J. Clark wrote:
> From: Rong Zhang <i@rong.moe>
> 
> Currently, lenovo-wmi-gamezone depends on lenovo-wmi-other as the former
> imports symbols from the latter. The imported symbols are just used to
> register a notifier block. However, there is no runtime dependency
> between both drivers, and either of them can run without the other,
> which is the major purpose of using the notifier framework.
> 
> Such a link-time dependency is non-optimal. A previous attempt to "fix"
> it made LENOVO_WMI_GAMEZONE select LENOVO_WMI_TUNING, which was
> fundamentally broken and resulted in undefined Kconfig behavior, as
> `select' cannot be used on a symbol with potentially unmet dependencies.
> 
> Decouple both drivers by moving the thermal mode notifier chain to
> lenovo-wmi-helpers. Methods for notifier block (un)registration are
> exported for lenovo-wmi-gamezone, while a method for querying the
> current thermal mode are exported for lenovo-wmi-other.
> 
> This turns the dependency graph from
> 
>             +------------ lenovo-wmi-gamezone
>             |                     |
>             v                     |
>     lenovo-wmi-helpers            |
>             ^                     |
>             |                     V
>             +------------ lenovo-wmi-other
> 
> into
> 
>             +------------ lenovo-wmi-gamezone
>             |
>             v
>     lenovo-wmi-helpers
>             ^
>             |
>             +------------ lenovo-wmi-other
> 
> To make it clear, the name of the notifier chain is also renamed from
> `om_chain_head' to `tm_chain_head', indicating that it's used to query
> the current thermal mode.
> 
> No functional change intended.
> 
> Fixes: 6e38b9fcbfa3 ("platform/x86: lenovo: gamezone needs "other mode"")

I tagged it as a fix patch as stable kernel may also run into a broken
randconfig where CONFIG_LENOVO_WMI_TUNING=m/y is selected by
CONFIG_LENOVO_WMI_GAMEZONE=m/y but CONFIG_LENOVO_WMI_CAPDATA=n is still
selected by the randomizer, which is almost identical to [1].

Fix patches should be the very first patches in the series so that
backporting is less painful. See also my reply to the cover letter.

> Cc: stable@vger.kernel.org
> Reported-by: kernel test robot <lkp@intel.com>
> Closes: https://lore.kernel.org/oe-kbuild-all/202603252259.gHvJDyh3-lkp@intel.com/

^ [1]

> Closes: https://lore.kernel.org/oe-kbuild-all/202603260302.X0NjQOda-lkp@intel.com/
> Signed-off-by: Rong Zhang <i@rong.moe>
> Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
> ---
>  drivers/platform/x86/lenovo/Kconfig        |   1 -
>  drivers/platform/x86/lenovo/wmi-gamezone.c |   4 +-
>  drivers/platform/x86/lenovo/wmi-helpers.c  | 101 ++++++++++++++++++++
>  drivers/platform/x86/lenovo/wmi-helpers.h  |   8 ++
>  drivers/platform/x86/lenovo/wmi-other.c    | 104 +--------------------
>  drivers/platform/x86/lenovo/wmi-other.h    |  16 ----
>  6 files changed, 112 insertions(+), 122 deletions(-)
>  delete mode 100644 drivers/platform/x86/lenovo/wmi-other.h
> 
> diff --git a/drivers/platform/x86/lenovo/Kconfig b/drivers/platform/x86/lenovo/Kconfig
> index 75a8b144b0da..b9a5d18caa1e 100644
> --- a/drivers/platform/x86/lenovo/Kconfig
> +++ b/drivers/platform/x86/lenovo/Kconfig
> @@ -252,7 +252,6 @@ config LENOVO_WMI_GAMEZONE
>  	select ACPI_PLATFORM_PROFILE
>  	select LENOVO_WMI_EVENTS
>  	select LENOVO_WMI_HELPERS
> -	select LENOVO_WMI_TUNING
>  	help
>  	  Say Y here if you have a WMI aware Lenovo Legion device and would like to use the
>  	  platform-profile firmware interface to manage power usage.
> diff --git a/drivers/platform/x86/lenovo/wmi-gamezone.c b/drivers/platform/x86/lenovo/wmi-gamezone.c
> index 602a48de1b4e..a614af8f08e8 100644
> --- a/drivers/platform/x86/lenovo/wmi-gamezone.c
> +++ b/drivers/platform/x86/lenovo/wmi-gamezone.c
> @@ -22,7 +22,6 @@
>  
>  #include "wmi-events.h"
>  #include "wmi-helpers.h"
> -#include "wmi-other.h"
>  
>  #define LENOVO_GAMEZONE_GUID "887B54E3-DDDC-4B2C-8B88-68A26A8835D0"
>  
> @@ -384,7 +383,7 @@ static int lwmi_gz_probe(struct wmi_device *wdev, const void *context)
>  		return ret;
>  
>  	priv->mode_nb.notifier_call = lwmi_gz_mode_call;
> -	return devm_lwmi_om_register_notifier(&wdev->dev, &priv->mode_nb);
> +	return devm_lwmi_tm_register_notifier(&wdev->dev, &priv->mode_nb);
>  }
>  
>  static const struct wmi_device_id lwmi_gz_id_table[] = {
> @@ -406,7 +405,6 @@ module_wmi_driver(lwmi_gz_driver);
>  
>  MODULE_IMPORT_NS("LENOVO_WMI_EVENTS");
>  MODULE_IMPORT_NS("LENOVO_WMI_HELPERS");
> -MODULE_IMPORT_NS("LENOVO_WMI_OTHER");
>  MODULE_DEVICE_TABLE(wmi, lwmi_gz_id_table);
>  MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@gmail.com>");
>  MODULE_DESCRIPTION("Lenovo GameZone WMI Driver");
> diff --git a/drivers/platform/x86/lenovo/wmi-helpers.c b/drivers/platform/x86/lenovo/wmi-helpers.c
> index 7379defac500..e1cf869224d2 100644
> --- a/drivers/platform/x86/lenovo/wmi-helpers.c
> +++ b/drivers/platform/x86/lenovo/wmi-helpers.c
> @@ -21,11 +21,15 @@
>  #include <linux/errno.h>
>  #include <linux/export.h>
>  #include <linux/module.h>
> +#include <linux/notifier.h>
>  #include <linux/unaligned.h>
>  #include <linux/wmi.h>
>  
>  #include "wmi-helpers.h"
>  
> +/* Thermal mode notifier chain. */
> +static BLOCKING_NOTIFIER_HEAD(tm_chain_head);
> +
>  /**
>   * lwmi_dev_evaluate_int() - Helper function for calling WMI methods that
>   * return an integer.
> @@ -84,6 +88,103 @@ int lwmi_dev_evaluate_int(struct wmi_device *wdev, u8 instance, u32 method_id,
>  };
>  EXPORT_SYMBOL_NS_GPL(lwmi_dev_evaluate_int, "LENOVO_WMI_HELPERS");
>  
> +/**
> + * lwmi_tm_register_notifier() - Add a notifier to the blocking notifier chain
> + * @nb: The notifier_block struct to register
> + *
> + * Call blocking_notifier_chain_register to register the notifier block to the
> + * thermal mode notifier chain.
> + *
> + * Return: 0 on success, %-EEXIST on error.
> + */
> +int lwmi_tm_register_notifier(struct notifier_block *nb)
> +{
> +	return blocking_notifier_chain_register(&tm_chain_head, nb);
> +}
> +EXPORT_SYMBOL_NS_GPL(lwmi_tm_register_notifier, "LENOVO_WMI_HELPERS");
> +
> +/**
> + * lwmi_tm_unregister_notifier() - Remove a notifier from the blocking notifier
> + * chain.
> + * @nb: The notifier_block struct to register

There is a typo. s/register/unregister/

(found by sashiko.dev)

https://sashiko.dev/#/patchset/20260331181208.421552-1-derekjohn.clark%40gmail.com

> + *
> + * Call blocking_notifier_chain_unregister to unregister the notifier block from the
> + * thermal mode notifier chain.
> + *
> + * Return: 0 on success, %-ENOENT on error.
> + */
> +int lwmi_tm_unregister_notifier(struct notifier_block *nb)
> +{
> +	return blocking_notifier_chain_unregister(&tm_chain_head, nb);
> +}
> +EXPORT_SYMBOL_NS_GPL(lwmi_tm_unregister_notifier, "LENOVO_WMI_HELPERS");
> +
> +/**
> + * devm_lwmi_tm_unregister_notifier() - Remove a notifier from the blocking
> + * notifier chain.
> + * @data: Void pointer to the notifier_block struct to register.

Ditto (found by sashiko.dev).

> + *
> + * Call lwmi_tm_unregister_notifier to unregister the notifier block from the
> + * thermal mode notifier chain.
> + *
> + * Return: 0 on success, %-ENOENT on error.

Remove it as it's a void function (found by sashiko.dev).

These typos have existed since its first appearance. I didn't catch them
when I was moving them.

Let's fix them as we are anyway touching them.

Thanks,
Rong

> + */
> +static void devm_lwmi_tm_unregister_notifier(void *data)
> +{
> +	struct notifier_block *nb = data;
> +
> +	lwmi_tm_unregister_notifier(nb);
> +}
> +
> +/**
> + * devm_lwmi_tm_register_notifier() - Add a notifier to the blocking notifier
> + * chain.
> + * @dev: The parent device of the notifier_block struct.
> + * @nb: The notifier_block struct to register
> + *
> + * Call lwmi_tm_register_notifier to register the notifier block to the
> + * thermal mode notifier chain. Then add devm_lwmi_tm_unregister_notifier
> + * as a device managed action to automatically unregister the notifier block
> + * upon parent device removal.
> + *
> + * Return: 0 on success, or an error code.
> + */
> +int devm_lwmi_tm_register_notifier(struct device *dev,
> +				   struct notifier_block *nb)
> +{
> +	int ret;
> +
> +	ret = lwmi_tm_register_notifier(nb);
> +	if (ret < 0)
> +		return ret;
> +
> +	return devm_add_action_or_reset(dev, devm_lwmi_tm_unregister_notifier,
> +					nb);
> +}
> +EXPORT_SYMBOL_NS_GPL(devm_lwmi_tm_register_notifier, "LENOVO_WMI_HELPERS");
> +
> +/**
> + * lwmi_tm_notifier_call() - Call functions for the notifier call chain.
> + * @mode: Pointer to a thermal mode enum to retrieve the data from.
> + *
> + * Call blocking_notifier_call_chain to retrieve the thermal mode from the
> + * lenovo-wmi-gamezone driver.
> + *
> + * Return: 0 on success, or an error code.
> + */
> +int lwmi_tm_notifier_call(enum thermal_mode *mode)
> +{
> +	int ret;
> +
> +	ret = blocking_notifier_call_chain(&tm_chain_head,
> +					   LWMI_GZ_GET_THERMAL_MODE, &mode);
> +	if ((ret & ~NOTIFY_STOP_MASK) != NOTIFY_OK)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_NS_GPL(lwmi_tm_notifier_call, "LENOVO_WMI_HELPERS");
> +
>  MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@gmail.com>");
>  MODULE_DESCRIPTION("Lenovo WMI Helpers Driver");
>  MODULE_LICENSE("GPL");
> diff --git a/drivers/platform/x86/lenovo/wmi-helpers.h b/drivers/platform/x86/lenovo/wmi-helpers.h
> index 3364d8e152ca..ed7db3ebba6c 100644
> --- a/drivers/platform/x86/lenovo/wmi-helpers.h
> +++ b/drivers/platform/x86/lenovo/wmi-helpers.h
> @@ -7,6 +7,8 @@
>  
>  #include <linux/types.h>
>  
> +struct device;
> +struct notifier_block;
>  struct wmi_device;
>  
>  struct wmi_method_args_32 {
> @@ -30,4 +32,10 @@ enum thermal_mode {
>  int lwmi_dev_evaluate_int(struct wmi_device *wdev, u8 instance, u32 method_id,
>  			  unsigned char *buf, size_t size, u32 *retval);
>  
> +int lwmi_tm_register_notifier(struct notifier_block *nb);
> +int lwmi_tm_unregister_notifier(struct notifier_block *nb);
> +int devm_lwmi_tm_register_notifier(struct device *dev,
> +				   struct notifier_block *nb);
> +int lwmi_tm_notifier_call(enum thermal_mode *mode);
> +
>  #endif /* !_LENOVO_WMI_HELPERS_H_ */
> diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
> index e0633c42972c..d871ee02dfcb 100644
> --- a/drivers/platform/x86/lenovo/wmi-other.c
> +++ b/drivers/platform/x86/lenovo/wmi-other.c
> @@ -41,7 +41,6 @@
>  #include <linux/kobject.h>
>  #include <linux/limits.h>
>  #include <linux/module.h>
> -#include <linux/notifier.h>
>  #include <linux/platform_profile.h>
>  #include <linux/power_supply.h>
>  #include <linux/types.h>
> @@ -52,7 +51,6 @@
>  #include "wmi-capdata.h"
>  #include "wmi-events.h"
>  #include "wmi-helpers.h"
> -#include "wmi-other.h"
>  #include "../firmware_attributes_class.h"
>  
>  #define LENOVO_OTHER_MODE_GUID "DC2A8805-3A8C-41BA-A6F7-092E0089CD3B"
> @@ -110,7 +108,6 @@ enum lwmi_feature_id_gpu {
>  #define LWMI_OM_SYSFS_NAME "lenovo-wmi-other"
>  #define LWMI_OM_HWMON_NAME "lenovo_wmi_other"
>  
> -static BLOCKING_NOTIFIER_HEAD(om_chain_head);
>  static DEFINE_IDA(lwmi_om_ida);
>  
>  enum attribute_property {
> @@ -138,7 +135,6 @@ struct lwmi_om_priv {
>  	struct device *hwmon_dev;
>  	struct device *fw_attr_dev;
>  	struct kset *fw_attr_kset;
> -	struct notifier_block nb;
>  	struct wmi_device *wdev;
>  	int ida_id;
>  
> @@ -979,102 +975,6 @@ struct capdata01_attr_group {
>  	struct tunable_attr_01 *tunable_attr;
>  };
>  
> -/**
> - * lwmi_om_register_notifier() - Add a notifier to the blocking notifier chain
> - * @nb: The notifier_block struct to register
> - *
> - * Call blocking_notifier_chain_register to register the notifier block to the
> - * lenovo-wmi-other driver notifier chain.
> - *
> - * Return: 0 on success, %-EEXIST on error.
> - */
> -int lwmi_om_register_notifier(struct notifier_block *nb)
> -{
> -	return blocking_notifier_chain_register(&om_chain_head, nb);
> -}
> -EXPORT_SYMBOL_NS_GPL(lwmi_om_register_notifier, "LENOVO_WMI_OTHER");
> -
> -/**
> - * lwmi_om_unregister_notifier() - Remove a notifier from the blocking notifier
> - * chain.
> - * @nb: The notifier_block struct to register
> - *
> - * Call blocking_notifier_chain_unregister to unregister the notifier block from the
> - * lenovo-wmi-other driver notifier chain.
> - *
> - * Return: 0 on success, %-ENOENT on error.
> - */
> -int lwmi_om_unregister_notifier(struct notifier_block *nb)
> -{
> -	return blocking_notifier_chain_unregister(&om_chain_head, nb);
> -}
> -EXPORT_SYMBOL_NS_GPL(lwmi_om_unregister_notifier, "LENOVO_WMI_OTHER");
> -
> -/**
> - * devm_lwmi_om_unregister_notifier() - Remove a notifier from the blocking
> - * notifier chain.
> - * @data: Void pointer to the notifier_block struct to register.
> - *
> - * Call lwmi_om_unregister_notifier to unregister the notifier block from the
> - * lenovo-wmi-other driver notifier chain.
> - *
> - * Return: 0 on success, %-ENOENT on error.
> - */
> -static void devm_lwmi_om_unregister_notifier(void *data)
> -{
> -	struct notifier_block *nb = data;
> -
> -	lwmi_om_unregister_notifier(nb);
> -}
> -
> -/**
> - * devm_lwmi_om_register_notifier() - Add a notifier to the blocking notifier
> - * chain.
> - * @dev: The parent device of the notifier_block struct.
> - * @nb: The notifier_block struct to register
> - *
> - * Call lwmi_om_register_notifier to register the notifier block to the
> - * lenovo-wmi-other driver notifier chain. Then add devm_lwmi_om_unregister_notifier
> - * as a device managed action to automatically unregister the notifier block
> - * upon parent device removal.
> - *
> - * Return: 0 on success, or an error code.
> - */
> -int devm_lwmi_om_register_notifier(struct device *dev,
> -				   struct notifier_block *nb)
> -{
> -	int ret;
> -
> -	ret = lwmi_om_register_notifier(nb);
> -	if (ret < 0)
> -		return ret;
> -
> -	return devm_add_action_or_reset(dev, devm_lwmi_om_unregister_notifier,
> -					nb);
> -}
> -EXPORT_SYMBOL_NS_GPL(devm_lwmi_om_register_notifier, "LENOVO_WMI_OTHER");
> -
> -/**
> - * lwmi_om_notifier_call() - Call functions for the notifier call chain.
> - * @mode: Pointer to a thermal mode enum to retrieve the data from.
> - *
> - * Call blocking_notifier_call_chain to retrieve the thermal mode from the
> - * lenovo-wmi-gamezone driver.
> - *
> - * Return: 0 on success, or an error code.
> - */
> -static int lwmi_om_notifier_call(enum thermal_mode *mode)
> -{
> -	int ret;
> -
> -	ret = blocking_notifier_call_chain(&om_chain_head,
> -					   LWMI_GZ_GET_THERMAL_MODE, &mode);
> -	if ((ret & ~NOTIFY_STOP_MASK) != NOTIFY_OK)
> -		return -EINVAL;
> -
> -	return 0;
> -}
> -
>  /* Attribute Methods */
>  
>  /**
> @@ -1178,7 +1078,7 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
>  	u32 value;
>  	int ret;
>  
> -	ret = lwmi_om_notifier_call(&mode);
> +	ret = lwmi_tm_notifier_call(&mode);
>  	if (ret)
>  		return ret;
>  
> @@ -1237,7 +1137,7 @@ static ssize_t attr_current_value_show(struct kobject *kobj,
>  	int retval;
>  	int ret;
>  
> -	ret = lwmi_om_notifier_call(&mode);
> +	ret = lwmi_tm_notifier_call(&mode);
>  	if (ret)
>  		return ret;
>  
> diff --git a/drivers/platform/x86/lenovo/wmi-other.h b/drivers/platform/x86/lenovo/wmi-other.h
> deleted file mode 100644
> index 8ebf5602bb99..000000000000
> --- a/drivers/platform/x86/lenovo/wmi-other.h
> +++ /dev/null
> @@ -1,16 +0,0 @@
> -/* SPDX-License-Identifier: GPL-2.0-or-later */
> -
> -/* Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com> */
> -
> -#ifndef _LENOVO_WMI_OTHER_H_
> -#define _LENOVO_WMI_OTHER_H_
> -
> -struct device;
> -struct notifier_block;
> -
> -int lwmi_om_register_notifier(struct notifier_block *nb);
> -int lwmi_om_unregister_notifier(struct notifier_block *nb);
> -int devm_lwmi_om_register_notifier(struct device *dev,
> -				   struct notifier_block *nb);
> -
> -#endif /* !_LENOVO_WMI_OTHER_H_ */

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 12/13] platform/x86: lenovo-wmi-helpers: Add helper for creating per-device debugfs dir
  2026-03-31 18:12 ` [PATCH v6 12/13] platform/x86: lenovo-wmi-helpers: Add helper for creating per-device debugfs dir Derek J. Clark
@ 2026-04-01 18:35   ` Rong Zhang
  0 siblings, 0 replies; 33+ messages in thread
From: Rong Zhang @ 2026-04-01 18:35 UTC (permalink / raw)
  To: Derek J. Clark, Ilpo Järvinen, Hans de Goede
  Cc: Mark Pearson, Armin Wolf, Jonathan Corbet, Kurt Borja,
	platform-driver-x86, linux-kernel

Hi Derek,

On Tue, 2026-03-31 at 18:12 +0000, Derek J. Clark wrote:
> From: Rong Zhang <i@rong.moe>
> 
> We are about to add debugfs support for lenovo-wmi-capdata. Let's setup
> a debugfs directory called "lenovo_wmi" for tidiness, so that any
> lenovo-wmi-* device can put its subdirectory under the directory.
> Subdirectories will be named after the corresponding WMI devices.
> 
> Signed-off-by: Rong Zhang <i@rong.moe>
> Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
> ---
>  drivers/platform/x86/lenovo/wmi-helpers.c | 34 +++++++++++++++++++++++
>  drivers/platform/x86/lenovo/wmi-helpers.h |  2 ++
>  2 files changed, 36 insertions(+)
> 
> diff --git a/drivers/platform/x86/lenovo/wmi-helpers.c b/drivers/platform/x86/lenovo/wmi-helpers.c
> index e1cf869224d2..52a0c6a5a4bf 100644
> --- a/drivers/platform/x86/lenovo/wmi-helpers.c
> +++ b/drivers/platform/x86/lenovo/wmi-helpers.c
> @@ -17,6 +17,8 @@
>   */
>  
>  #include <linux/acpi.h>
> +#include <linux/debugfs.h>
> +#include <linux/device.h>
>  #include <linux/cleanup.h>
>  #include <linux/errno.h>
>  #include <linux/export.h>
> @@ -185,6 +187,38 @@ int lwmi_tm_notifier_call(enum thermal_mode *mode)
>  }
>  EXPORT_SYMBOL_NS_GPL(lwmi_tm_notifier_call, "LENOVO_WMI_HELPERS");
>  
> +static struct dentry *lwmi_debugfs_dir;
> +
> +/**
> + * lwmi_debugfs_create_dir() - Helper function for creating a debugfs directory
> + * for a device.
> + * @wdev: Pointer to the WMI device to be called.
> + *
> + * Caller must remove the directory with debugfs_remove_recursive() on device
> + * removal.
> + *
> + * Return: Pointer to the created directory.
> + */
> +struct dentry *lwmi_debugfs_create_dir(struct wmi_device *wdev)
> +{
> +	return debugfs_create_dir(dev_name(&wdev->dev), lwmi_debugfs_dir);
> +}
> +EXPORT_SYMBOL_NS_GPL(lwmi_debugfs_create_dir, "LENOVO_WMI_HELPERS");
> +
> +static int __init lwmi_helpers_init(void)
> +{
> +	lwmi_debugfs_dir = debugfs_create_dir("lenovo_wmi", NULL);
> +
> +	return 0;
> +}
> +module_init(lwmi_helpers_init)

I missed it before. This should be `subsys_initcall(lwmi_helpers_init)'
so that it can be initialized first if both wmi-helpers and wmi-capdata
are built into the kernel statically (i.e., =y).

Found by sashiko.dev.

https://sashiko.dev/#/patchset/20260331181208.421552-1-derekjohn.clark%40gmail.com

module_exit(lwmi_helpers_exit) has no effect when built into the kernel
so that's fine.

Thanks,
Rong

> +
> +static void __exit lwmi_helpers_exit(void)
> +{
> +	debugfs_remove_recursive(lwmi_debugfs_dir);
> +}
> +module_exit(lwmi_helpers_exit)
> +
>  MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@gmail.com>");
>  MODULE_DESCRIPTION("Lenovo WMI Helpers Driver");
>  MODULE_LICENSE("GPL");
> diff --git a/drivers/platform/x86/lenovo/wmi-helpers.h b/drivers/platform/x86/lenovo/wmi-helpers.h
> index ed7db3ebba6c..039fe61003ce 100644
> --- a/drivers/platform/x86/lenovo/wmi-helpers.h
> +++ b/drivers/platform/x86/lenovo/wmi-helpers.h
> @@ -16,6 +16,8 @@ struct wmi_method_args_32 {
>  	u32 arg1;
>  };
>  
> +struct dentry *lwmi_debugfs_create_dir(struct wmi_device *wdev);
> +
>  enum lwmi_event_type {
>  	LWMI_GZ_GET_THERMAL_MODE = 0x01,
>  };

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 00/13] platform-x86: lenovo-wmi: Add fixes and enhancement
  2026-03-31 18:11 [PATCH v6 00/13] platform-x86: lenovo-wmi: Add fixes and enhancement Derek J. Clark
                   ` (12 preceding siblings ...)
  2026-03-31 18:12 ` [PATCH v6 13/13] platform/x86: lenovo-wmi-capdata: Add debugfs file for dumping capdata Derek J. Clark
@ 2026-04-01 18:44 ` Rong Zhang
  2026-04-01 19:00   ` [PATCH 1/3] platform/x86: lenovo-wmi-helpers: Fix memory leak in lwmi_dev_evaluate_int() Rong Zhang
                     ` (3 more replies)
  13 siblings, 4 replies; 33+ messages in thread
From: Rong Zhang @ 2026-04-01 18:44 UTC (permalink / raw)
  To: Derek J. Clark, Ilpo Järvinen, Hans de Goede
  Cc: Mark Pearson, Armin Wolf, Jonathan Corbet, Kurt Borja,
	platform-driver-x86, linux-kernel

Hi Derek,

On Tue, 2026-03-31 at 18:11 +0000, Derek J. Clark wrote:
> This series adds many much needed features and fixes to the lenovo-wmi
> drivers.
> 
> Patch 1 moves LWMI_FAN_DIV to be next to the rest of the fan attribute
> defines in preparation for adding additional attrbiute macros. This is
> so the attribute macros can all be in the same place in the file.
> 
> Patch 2 cleans up tunable_attr_01 by removing an unused pointer and
> correctly assigning the members as u8 isntead of u32.
> 
> Patch 3 fixes a bug when sending 32 bit arguments via WMI where the
> second value in the args struct was uninitialized.
> 
> Patch 4 moves all gamezone enums from the gamezone header into the
> helpers header in preparation for the rest of the series.
> 
> Patch 5 adds a function to make assigning attribute ID's for capdata
> cleaner and easier.
> 
> Patch 6 addresses bugs where devices that don't support exposed
> attributes would still create the attribute, and also attempts to
> identify the correct capdata and set/get methods since some legacy
> interfaces don't use the custom mode in the method or capdata ID.
> 
> Patch 7 adds the remaining CPU attributes that weren't previously
> exposed.
> 
> Patch 8 adds GPU attributes.
> 
> Patch 9 renames a name constant in preparation for patch 6.
> 
> Patch 10 adds battery charge-type limiting when supported only by WMI, or
> when a module parameter to skip compatibility checks is set. The
> MODULE_PARM_DESC macro creates one check and two warnings in checkpatch.
> I reviewed other examples from the kernel and I am following the same
> convention, so I left it as is.
> 
> Patch 11 fixes a bug where the 'gamezone' and the 'other' drivers were
> incorrectly coupled in the Kconfig, leading to side effects under
> certain kernel configurations.
> 
> Patch 12 adds a debugfs directory.
> 
> Patch 13 adds a debugfs file for dumping capdata.
> 
> Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>

The series LGTM except for some tiny issues. See my replies to the
corresponding patches.

Sashiko.dev reports some potential issues in the series as well as some
existing bugs.

https://sashiko.dev/#/patchset/20260331181208.421552-1-derekjohn.clark%40gmail.com

I've mentioned some in my reply to the corresponding patches. Besides, I
am going to talk about existing bugs here:


- tunable_attr_01 is a statically shared structure, but .dev is
overwritten each time when the master is bound, which violates
`.no_singleton = true'

AFAIK no device in wild has multiple wmi-other instances so it should be
safe for the time being.

Fixing it will need numerous fundamental refactorings to the attributes
and conflict with patch 6 where .cd_mode_id and .cv_mode_id is added to
tunable_attr_01.

I don't really want to make this series too large and miss this cycle.
Hence, I am going to fix it in the next cycle with some extra
refactorings to get rid of attribute-specific show/store callbacks.

For this series, I'd consider violating `.no_singleton = true' a very
minor concern. It should be fine to keep it as is. If we really care
about it, we could set it to false temporarily.


- Rebinding wmi-other leaks IDA

- Failure in lwmi_om_fw_attr_add() followed by lwmi_other_remove()
double frees the same IDA

- Calling lwmi_dev_evaluate_int() with `retval == NULL' leaks memory

- Unbalanced component bind and unbind in the error path of
lwmi_om_master_bind()

Legit concerns. I will reply with a series fixing them. Could you
incorporate it into your series?

Fix patches should be the very first patches in the series so that
backporting is less painful. I'd suggest rearranging the series like:

patches in my fix series
patch 3
patch 4
patch 11
the rest patches

Thanks,
Rong

> ---
> v6:
>   - Incorporate Rong Zhang's debugfs and decoupling patches into the
>     series.
>   - Add a patch to clean up too many cross-references to wmi-gamezone.h
>   - Make lwmi_attr_id a static inline in wmi-capdata.h
>   - Added a patch  to fix a bug where ares.arg1 is uninitialized when it
>     is sent to the firmware.
>   - Add supported checks before adding battery extenstion, and ensure
>     both the new checks and the is_writable checks are not casting u32
>     to i32.
>   - Misc formating changes.
> v5: https://lore.kernel.org/platform-driver-x86/20260324221032.1333636-1-derekjohn.clark@gmail.com/
>   - Remove cv/cd_mode_id references that occured before patch 4.
>   - Move lwmi_attr_id to capdata.c with a namespace export.
>   - Fix mixing include.
>   - Make lwmi_attr_is_supported return bool.
>   - Use switch instead of if for setting/getting charge type state.
>   - Various formatting fixes.
> v4: https://lore.kernel.org/platform-driver-x86/20260312031032.3467565-1-derekjohn.clark@gmail.com/
>   - Use loop instead of back gotos for identifying the working attribute
>     ID.
>   - Use function instead of macro to assign attribute_id, preserving
>     types.
>   - Removed unused defines and enum values.
>   - Rename charging defines to clarify thier purpose.
>   - Fixed various formatting issues from v3.
>   - Added module param to skip ACPI check when loading the driver for
>     the power supply extension.
>   - Don't abort adding power supply extension if the ACPI handle from
>     ideapad is not present.
>   - Don't worry about symmetric cleanup when cleaning up attributes in
>     an error state.
>   - Reword Patch 8 commit message to be more concise.
>   - Fix wording in Patch 7 to match the changes.
> v3: https://lore.kernel.org/platform-driver-x86/20260224043200.2680384-1-derekjohn.clark@gmail.com/
>   - Re-add HWMON name const and just rename LWMI_OM_FW_ATTR_BASE_PATH
>   - Fix linker warnings by moving acpi/battery include to the end of the
>     list.
>   - Remove CPU/GPU OC features. These attributes are BOOL type and will
>     need a new constructor that I'll add later.
> v2: https://lore.kernel.org/platform-driver-x86/20260215061339.2842486-1-derekjohn.clark@gmail.com/
>   - Fix gpu_mode misisng from attributes list.
>   - Fix prototypes for power suppy patch.
>   - Reorganize CPU and GPU attributes alphabetically.
>   - Break out the patch consolidating the driver name cost.
>   - Move some of the refactoring of attribute_id back to into patch 1
>     where it belongs.
>   - Fix some additional typos in function prototypes.
> v1: https://lore.kernel.org/platform-driver-x86/20260213081243.794288-1-derekjohn.clark@gmail.com/
> 
> 
> Derek J. Clark (10):
>   platform/x86: lenovo-wmi-other: Move LWMI_FAN_DIV
>   platform/x86: lenovo-wmi-other: Fix tunable_attr_01 struct members
>   platform/x86: lenovo-wmi-other: Zero initialize WMI arguments
>   platform/x86: lenovo-wmi-helpers: Move gamezone enums to wmi-helpers
>   platform/x86: lenovo-wmi-other: Add lwmi_attr_id() function
>   platform/x86: lenovo-wmi-other: Limit adding attributes to supported
>     devices
>   platform/x86: lenovo-wmi-other: Add missing CPU tunable attributes
>   platform/x86: lenovo-wmi-other: Add GPU tunable attributes
>   platform/x86: lenovo-wmi-other: Rename LWMI_OM_FW_ATTR_BASE_PATH
>   platform/x86: lenovo-wmi-other: Add WMI battery charge limiting
> 
> Rong Zhang (3):
>   platform/x86: lenovo: Decouple lenovo-wmi-gamezone and
>     lenovo-wmi-other
>   platform/x86: lenovo-wmi-helpers: Add helper for creating per-device
>     debugfs dir
>   platform/x86: lenovo-wmi-capdata: Add debugfs file for dumping capdata
> 
>  .../wmi/devices/lenovo-wmi-other.rst          |  19 +
>  drivers/platform/x86/lenovo/Kconfig           |   3 +-
>  drivers/platform/x86/lenovo/wmi-capdata.c     | 128 +++-
>  drivers/platform/x86/lenovo/wmi-capdata.h     |  31 +-
>  drivers/platform/x86/lenovo/wmi-events.c      |   2 +-
>  drivers/platform/x86/lenovo/wmi-gamezone.c    |   5 +-
>  drivers/platform/x86/lenovo/wmi-gamezone.h    |  20 -
>  drivers/platform/x86/lenovo/wmi-helpers.c     | 136 ++++
>  drivers/platform/x86/lenovo/wmi-helpers.h     |  23 +
>  drivers/platform/x86/lenovo/wmi-other.c       | 689 ++++++++++++++----
>  drivers/platform/x86/lenovo/wmi-other.h       |  16 -
>  11 files changed, 891 insertions(+), 181 deletions(-)
>  delete mode 100644 drivers/platform/x86/lenovo/wmi-gamezone.h
>  delete mode 100644 drivers/platform/x86/lenovo/wmi-other.h

^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 1/3] platform/x86: lenovo-wmi-helpers: Fix memory leak in lwmi_dev_evaluate_int()
  2026-04-01 18:44 ` [PATCH v6 00/13] platform-x86: lenovo-wmi: Add fixes and enhancement Rong Zhang
@ 2026-04-01 19:00   ` Rong Zhang
  2026-04-02 10:06     ` Ilpo Järvinen
  2026-04-01 19:00   ` [PATCH 2/3] platform/x86: lenovo-wmi-other: Balance IDA id allocation and free Rong Zhang
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 33+ messages in thread
From: Rong Zhang @ 2026-04-01 19:00 UTC (permalink / raw)
  To: Derek J . Clark, Ilpo Järvinen, Hans de Goede
  Cc: Rong Zhang, Mark Pearson, Armin Wolf, Jonathan Corbet, Kurt Borja,
	platform-driver-x86, linux-kernel, stable

lwmi_dev_evaluate_int() leaks output.pointer when retval == NULL (found
by sashiko.dev [1]).

Fix it by moving `ret_obj = output.pointer' outside of the `if (retval)'
block so that it is always freed by the __free cleanup callback.

No functional change intended.

Fixes: e521d16e76cd ("platform/x86: Add lenovo-wmi-helpers")
Cc: stable@vger.kernel.org
Link: https://sashiko.dev/#/patchset/20260331181208.421552-1-derekjohn.clark%40gmail.com [1]
Signed-off-by: Rong Zhang <i@rong.moe>
---
 drivers/platform/x86/lenovo/wmi-helpers.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/platform/x86/lenovo/wmi-helpers.c b/drivers/platform/x86/lenovo/wmi-helpers.c
index 7379defac500..80021f59d1ef 100644
--- a/drivers/platform/x86/lenovo/wmi-helpers.c
+++ b/drivers/platform/x86/lenovo/wmi-helpers.c
@@ -55,8 +55,9 @@ int lwmi_dev_evaluate_int(struct wmi_device *wdev, u8 instance, u32 method_id,
 	if (ACPI_FAILURE(status))
 		return -EIO;
 
+	ret_obj = output.pointer;
+
 	if (retval) {
-		ret_obj = output.pointer;
 		if (!ret_obj)
 			return -ENODATA;
 

base-commit: 9147566d801602c9e7fc7f85e989735735bf38ba
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH 2/3] platform/x86: lenovo-wmi-other: Balance IDA id allocation and free
  2026-04-01 18:44 ` [PATCH v6 00/13] platform-x86: lenovo-wmi: Add fixes and enhancement Rong Zhang
  2026-04-01 19:00   ` [PATCH 1/3] platform/x86: lenovo-wmi-helpers: Fix memory leak in lwmi_dev_evaluate_int() Rong Zhang
@ 2026-04-01 19:00   ` Rong Zhang
  2026-04-01 19:00   ` [PATCH 3/3] platform/x86: lenovo-wmi-other: Balance component bind and unbind Rong Zhang
  2026-04-01 19:37   ` [PATCH v6 00/13] platform-x86: lenovo-wmi: Add fixes and enhancement Derek J. Clark
  3 siblings, 0 replies; 33+ messages in thread
From: Rong Zhang @ 2026-04-01 19:00 UTC (permalink / raw)
  To: Derek J . Clark, Ilpo Järvinen, Hans de Goede
  Cc: Rong Zhang, Mark Pearson, Armin Wolf, Jonathan Corbet, Kurt Borja,
	platform-driver-x86, linux-kernel, stable

Currently, the IDA id is only freed on wmi-other device removal or
failure to create firmware-attributes device, kset, or attributes. It
leaks IDA ids if the wmi-other device is bound multiple times, as the
unbind callback never frees the previously allocated IDA id.
Additionally, if the wmi-other device has failed to create a
firmware-attributes device before it gets removed, the wmi-device
removal callback double frees the same IDA id.

These bugs were found by sashiko.dev [1].

Fix them by moving ida_free() into lwmi_om_fw_attr_remove() so it is
balanced with ida_alloc() in lwmi_om_fw_attr_add(). With them fixed,
properly set and utilize the validity of priv->ida_id to balance
firmware-attributes registration and removal, without relying on
propagating the registration error to the component framework, which is
more reliable and aligns with the hwmon device registration and removal
sequences.

No functional change intended.

Fixes: edc4b183b794 ("platform/x86: Add Lenovo Other Mode WMI Driver")
Cc: stable@vger.kernel.org
Link: https://sashiko.dev/#/patchset/20260331181208.421552-1-derekjohn.clark%40gmail.com [1]
Signed-off-by: Rong Zhang <i@rong.moe>
---
 drivers/platform/x86/lenovo/wmi-other.c | 34 +++++++++++++++----------
 1 file changed, 20 insertions(+), 14 deletions(-)

diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
index 6040f45aa2b0..b47418df099f 100644
--- a/drivers/platform/x86/lenovo/wmi-other.c
+++ b/drivers/platform/x86/lenovo/wmi-other.c
@@ -957,17 +957,17 @@ static struct capdata01_attr_group cd01_attr_groups[] = {
 /**
  * lwmi_om_fw_attr_add() - Register all firmware_attributes_class members
  * @priv: The Other Mode driver data.
- *
- * Return: Either 0, or an error code.
  */
-static int lwmi_om_fw_attr_add(struct lwmi_om_priv *priv)
+static void lwmi_om_fw_attr_add(struct lwmi_om_priv *priv)
 {
 	unsigned int i;
 	int err;
 
 	priv->ida_id = ida_alloc(&lwmi_om_ida, GFP_KERNEL);
-	if (priv->ida_id < 0)
-		return priv->ida_id;
+	if (priv->ida_id < 0) {
+		err = priv->ida_id;
+		goto err;
+	}
 
 	priv->fw_attr_dev = device_create(&firmware_attributes_class, NULL,
 					  MKDEV(0, 0), NULL, "%s-%u",
@@ -993,7 +993,7 @@ static int lwmi_om_fw_attr_add(struct lwmi_om_priv *priv)
 
 		cd01_attr_groups[i].tunable_attr->dev = &priv->wdev->dev;
 	}
-	return 0;
+	return;
 
 err_remove_groups:
 	while (i--)
@@ -1007,7 +1007,12 @@ static int lwmi_om_fw_attr_add(struct lwmi_om_priv *priv)
 
 err_free_ida:
 	ida_free(&lwmi_om_ida, priv->ida_id);
-	return err;
+
+err:
+	priv->ida_id = -EIDRM;
+
+	dev_warn(&priv->wdev->dev,
+		 "failed to register firmware-attributes device: %d\n", err);
 }
 
 /**
@@ -1016,12 +1021,17 @@ static int lwmi_om_fw_attr_add(struct lwmi_om_priv *priv)
  */
 static void lwmi_om_fw_attr_remove(struct lwmi_om_priv *priv)
 {
+	if (priv->ida_id < 0)
+		return;
+
 	for (unsigned int i = 0; i < ARRAY_SIZE(cd01_attr_groups) - 1; i++)
 		sysfs_remove_group(&priv->fw_attr_kset->kobj,
 				   cd01_attr_groups[i].attr_group);
 
 	kset_unregister(priv->fw_attr_kset);
 	device_unregister(priv->fw_attr_dev);
+	ida_free(&lwmi_om_ida, priv->ida_id);
+	priv->ida_id = -EIDRM;
 }
 
 /* ======== Self (master: lenovo-wmi-other) ======== */
@@ -1063,7 +1073,9 @@ static int lwmi_om_master_bind(struct device *dev)
 
 	lwmi_om_fan_info_collect_cd00(priv);
 
-	return lwmi_om_fw_attr_add(priv);
+	lwmi_om_fw_attr_add(priv);
+
+	return 0;
 }
 
 /**
@@ -1115,13 +1127,7 @@ static int lwmi_other_probe(struct wmi_device *wdev, const void *context)
 
 static void lwmi_other_remove(struct wmi_device *wdev)
 {
-	struct lwmi_om_priv *priv = dev_get_drvdata(&wdev->dev);
-
 	component_master_del(&wdev->dev, &lwmi_om_master_ops);
-
-	/* No IDA to free if the driver is never bound to its components. */
-	if (priv->ida_id >= 0)
-		ida_free(&lwmi_om_ida, priv->ida_id);
 }
 
 static const struct wmi_device_id lwmi_other_id_table[] = {
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH 3/3] platform/x86: lenovo-wmi-other: Balance component bind and unbind
  2026-04-01 18:44 ` [PATCH v6 00/13] platform-x86: lenovo-wmi: Add fixes and enhancement Rong Zhang
  2026-04-01 19:00   ` [PATCH 1/3] platform/x86: lenovo-wmi-helpers: Fix memory leak in lwmi_dev_evaluate_int() Rong Zhang
  2026-04-01 19:00   ` [PATCH 2/3] platform/x86: lenovo-wmi-other: Balance IDA id allocation and free Rong Zhang
@ 2026-04-01 19:00   ` Rong Zhang
  2026-04-01 19:37   ` [PATCH v6 00/13] platform-x86: lenovo-wmi: Add fixes and enhancement Derek J. Clark
  3 siblings, 0 replies; 33+ messages in thread
From: Rong Zhang @ 2026-04-01 19:00 UTC (permalink / raw)
  To: Derek J . Clark, Ilpo Järvinen, Hans de Goede
  Cc: Rong Zhang, Mark Pearson, Armin Wolf, Jonathan Corbet, Kurt Borja,
	platform-driver-x86, linux-kernel, stable

When lwmi_om_master_bind() fails, the master device's components are
left bound, with the aggregate device destroyed due to the failure
(found by sashiko.dev [1]).

Balance calls to component_bind_all() and component_unbind_all() when an
error is propagated to the component framework.

No functional change intended.

Fixes: edc4b183b794 ("platform/x86: Add Lenovo Other Mode WMI Driver")
Cc: stable@vger.kernel.org
Link: https://sashiko.dev/#/patchset/20260331181208.421552-1-derekjohn.clark%40gmail.com [1]
Signed-off-by: Rong Zhang <i@rong.moe>
---
 drivers/platform/x86/lenovo/wmi-other.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
index b47418df099f..4b47b5886e33 100644
--- a/drivers/platform/x86/lenovo/wmi-other.c
+++ b/drivers/platform/x86/lenovo/wmi-other.c
@@ -1068,8 +1068,11 @@ static int lwmi_om_master_bind(struct device *dev)
 
 	priv->cd00_list = binder.cd00_list;
 	priv->cd01_list = binder.cd01_list;
-	if (!priv->cd00_list || !priv->cd01_list)
+	if (!priv->cd00_list || !priv->cd01_list) {
+		component_unbind_all(dev, NULL);
+
 		return -ENODEV;
+	}
 
 	lwmi_om_fan_info_collect_cd00(priv);
 
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 05/13] platform/x86: lenovo-wmi-other: Add lwmi_attr_id() function
  2026-04-01 18:29   ` Rong Zhang
@ 2026-04-01 19:24     ` Derek J. Clark
  0 siblings, 0 replies; 33+ messages in thread
From: Derek J. Clark @ 2026-04-01 19:24 UTC (permalink / raw)
  To: Rong Zhang, Ilpo Järvinen, Hans de Goede
  Cc: Mark Pearson, Armin Wolf, Jonathan Corbet, Kurt Borja,
	platform-driver-x86, linux-kernel

On April 1, 2026 11:29:25 AM PDT, Rong Zhang <i@rong.moe> wrote:
>Hi Derek,
>
>On Tue, 2026-03-31 at 18:12 +0000, Derek J. Clark wrote:
>> Adds lwmi_attr_id() function. In the same vein as LWMI_ATTR_ID_FAN_RPM(),
>> but as a generic, to de-duplicate attribute_id assignment biolerplate.
>> 
>> Reviewed-by: Rong Zhang <i@rong.moe>
>> Tested-by: Rong Zhang <i@rong.moe>
>> Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
>> Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
>> ---
>> v6:
>>   - Move lwmi_attr_id to wmi-capdata.h as static inline.
>> v5:
>>   - Move references to cv/cd_mode_id to patch 4/8.
>>   - Move lwmi_attr_id to wmi-capdata.c and export with namespace.
>> v4:
>>   - Switch from macro to static inline to preserve types.
>> ---
>>  drivers/platform/x86/lenovo/wmi-capdata.c |  8 ++---
>>  drivers/platform/x86/lenovo/wmi-capdata.h | 20 ++++++++++++
>>  drivers/platform/x86/lenovo/wmi-other.c   | 39 ++++++-----------------
>>  3 files changed, 34 insertions(+), 33 deletions(-)
>> 
>> diff --git a/drivers/platform/x86/lenovo/wmi-capdata.c b/drivers/platform/x86/lenovo/wmi-capdata.c
>> index ee1fb02d8e31..169665be4dcf 100644
>> --- a/drivers/platform/x86/lenovo/wmi-capdata.c
>> +++ b/drivers/platform/x86/lenovo/wmi-capdata.c
>> @@ -27,7 +27,6 @@
>>  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>>  
>>  #include <linux/acpi.h>
>> -#include <linux/bitfield.h>
>>  #include <linux/bug.h>
>>  #include <linux/cleanup.h>
>>  #include <linux/component.h>
>> @@ -48,6 +47,7 @@
>>  #include <linux/wmi.h>
>>  
>>  #include "wmi-capdata.h"
>> +#include "wmi-helpers.h"
>
>Bad rebase? IIUC it is unneeded until patch 13.
>
Hi Rong.

No, it is needed.

>Thanks,
>Rong
>
>>  
>>  #define LENOVO_CAPABILITY_DATA_00_GUID "362A3AFE-3D96-4665-8530-96DAD5BB300E"
>>  #define LENOVO_CAPABILITY_DATA_01_GUID "7A8F5407-CB67-4D6E-B547-39B3BE018154"
>> @@ -58,9 +58,9 @@
>>  
>>  #define LWMI_FEATURE_ID_FAN_TEST 0x05
>>  
>> -#define LWMI_ATTR_ID_FAN_TEST							\
>> -	(FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, LWMI_DEVICE_ID_FAN) |		\
>> -	 FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, LWMI_FEATURE_ID_FAN_TEST))
>> +#define LWMI_ATTR_ID_FAN_TEST                                      \
>> +	lwmi_attr_id(LWMI_DEVICE_ID_FAN, LWMI_FEATURE_ID_FAN_TEST, \
>> +		     LWMI_GZ_THERMAL_MODE_NONE, LWMI_TYPE_ID_NONE)
>>  

Thermal mode none is referenced here, and now lives in helpers.h

- Derek

>>  enum lwmi_cd_type {
>>  	LENOVO_CAPABILITY_DATA_00,
>> diff --git a/drivers/platform/x86/lenovo/wmi-capdata.h b/drivers/platform/x86/lenovo/wmi-capdata.h
>> index 8c1df3efcc55..1388eaf4ab4a 100644
>> --- a/drivers/platform/x86/lenovo/wmi-capdata.h
>> +++ b/drivers/platform/x86/lenovo/wmi-capdata.h
>> @@ -6,6 +6,7 @@
>>  #define _LENOVO_WMI_CAPDATA_H_
>>  
>>  #include <linux/bits.h>
>> +#include <linux/bitfield.h>
>>  #include <linux/types.h>
>>  
>>  #define LWMI_SUPP_VALID		BIT(0)
>> @@ -19,6 +20,8 @@
>>  
>>  #define LWMI_DEVICE_ID_FAN	0x04
>>  
>> +#define LWMI_TYPE_ID_NONE 0x00
>> +
>>  struct component_match;
>>  struct device;
>>  struct cd_list;
>> @@ -57,6 +60,23 @@ struct lwmi_cd_binder {
>>  	cd_list_cb_t cd_fan_list_cb;
>>  };
>>  
>> +/**
>> + * lwmi_attr_id() - Formats a capability data attribute ID
>> + * @dev_id: The u8 corresponding to the device ID.
>> + * @feat_id: The u8 corresponding to the feature ID on the device.
>> + * @mode_id: The u8 corresponding to the wmi-gamezone mode for set/get.
>> + * @type_id: The u8 corresponding to the sub-device.
>> + *
>> + * Return: u32.
>> + */
>> +static inline u32 lwmi_attr_id(u8 dev_id, u8 feat_id, u8 mode_id, u8 type_id)
>> +{
>> +	return (FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, dev_id)   |
>> +		FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, feat_id) |
>> +		FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode_id) |
>> +		FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, type_id));
>> +}
>> +
>>  void lwmi_cd_match_add_all(struct device *master, struct component_match **matchptr);
>>  int lwmi_cd00_get_data(struct cd_list *list, u32 attribute_id, struct capdata00 *output);
>>  int lwmi_cd01_get_data(struct cd_list *list, u32 attribute_id, struct capdata01 *output);
>> diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
>> index b24bf98c19ad..d2838ae5934f 100644
>> --- a/drivers/platform/x86/lenovo/wmi-other.c
>> +++ b/drivers/platform/x86/lenovo/wmi-other.c
>> @@ -27,7 +27,6 @@
>>   */
>>  
>>  #include <linux/acpi.h>
>> -#include <linux/bitfield.h>
>>  #include <linux/cleanup.h>
>>  #include <linux/component.h>
>>  #include <linux/container_of.h>
>> @@ -61,8 +60,6 @@
>>  
>>  #define LWMI_FEATURE_ID_FAN_RPM 0x03
>>  
>> -#define LWMI_TYPE_ID_NONE 0x00
>> -
>>  #define LWMI_FEATURE_VALUE_GET 17
>>  #define LWMI_FEATURE_VALUE_SET 18
>>  
>> @@ -72,10 +69,9 @@
>>  
>>  #define LWMI_FAN_DIV 100
>>  
>> -#define LWMI_ATTR_ID_FAN_RPM(x)						\
>> -	(FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, LWMI_DEVICE_ID_FAN) |	\
>> -	 FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, LWMI_FEATURE_ID_FAN_RPM) |	\
>> -	 FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, LWMI_FAN_ID(x)))
>> +#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))
>>  
>>  #define LWMI_OM_FW_ATTR_BASE_PATH "lenovo-wmi-other"
>>  #define LWMI_OM_HWMON_NAME "lenovo_wmi_other"
>> @@ -714,12 +710,8 @@ static ssize_t attr_capdata01_show(struct kobject *kobj,
>>  	u32 attribute_id;
>>  	int value, ret;
>>  
>> -	attribute_id =
>> -		FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
>> -		FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
>> -		FIELD_PREP(LWMI_ATTR_MODE_ID_MASK,
>> -			   LWMI_GZ_THERMAL_MODE_CUSTOM) |
>> -		FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
>> +	attribute_id = lwmi_attr_id(tunable_attr->device_id, tunable_attr->feature_id,
>> +				    LWMI_GZ_THERMAL_MODE_CUSTOM, tunable_attr->type_id);
>>  
>>  	ret = lwmi_cd01_get_data(priv->cd01_list, attribute_id, &capdata);
>>  	if (ret)
>> @@ -774,7 +766,6 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
>>  	struct wmi_method_args_32 args = { 0x0, 0x0 };
>>  	struct capdata01 capdata;
>>  	enum thermal_mode mode;
>> -	u32 attribute_id;
>>  	u32 value;
>>  	int ret;
>>  
>> @@ -785,13 +776,10 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
>>  	if (mode != LWMI_GZ_THERMAL_MODE_CUSTOM)
>>  		return -EBUSY;
>>  
>> -	attribute_id =
>> -		FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
>> -		FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
>> -		FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) |
>> -		FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
>> +	args.arg0 = lwmi_attr_id(tunable_attr->device_id, tunable_attr->feature_id,
>> +				 mode, tunable_attr->type_id);
>>  
>> -	ret = lwmi_cd01_get_data(priv->cd01_list, attribute_id, &capdata);
>> +	ret = lwmi_cd01_get_data(priv->cd01_list, args.arg0, &capdata);
>>  	if (ret)
>>  		return ret;
>>  
>> @@ -802,7 +790,6 @@ static ssize_t attr_current_value_store(struct kobject *kobj,
>>  	if (value < capdata.min_value || value > capdata.max_value)
>>  		return -EINVAL;
>>  
>> -	args.arg0 = attribute_id;
>>  	args.arg1 = value;
>>  
>>  	ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_SET,
>> @@ -836,7 +823,6 @@ static ssize_t attr_current_value_show(struct kobject *kobj,
>>  	struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev);
>>  	struct wmi_method_args_32 args = { 0x0, 0x0 };
>>  	enum thermal_mode mode;
>> -	u32 attribute_id;
>>  	int retval;
>>  	int ret;
>>  
>> @@ -844,13 +830,8 @@ static ssize_t attr_current_value_show(struct kobject *kobj,
>>  	if (ret)
>>  		return ret;
>>  
>> -	attribute_id =
>> -		FIELD_PREP(LWMI_ATTR_DEV_ID_MASK, tunable_attr->device_id) |
>> -		FIELD_PREP(LWMI_ATTR_FEAT_ID_MASK, tunable_attr->feature_id) |
>> -		FIELD_PREP(LWMI_ATTR_MODE_ID_MASK, mode) |
>> -		FIELD_PREP(LWMI_ATTR_TYPE_ID_MASK, tunable_attr->type_id);
>> -
>> -	args.arg0 = attribute_id;
>> +	args.arg0 = lwmi_attr_id(tunable_attr->device_id, tunable_attr->feature_id,
>> +				 mode, tunable_attr->type_id);
>>  
>>  	ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_GET,
>>  				    (unsigned char *)&args, sizeof(args),


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 00/13] platform-x86: lenovo-wmi: Add fixes and enhancement
  2026-04-01 18:44 ` [PATCH v6 00/13] platform-x86: lenovo-wmi: Add fixes and enhancement Rong Zhang
                     ` (2 preceding siblings ...)
  2026-04-01 19:00   ` [PATCH 3/3] platform/x86: lenovo-wmi-other: Balance component bind and unbind Rong Zhang
@ 2026-04-01 19:37   ` Derek J. Clark
  2026-04-01 20:12     ` Rong Zhang
  3 siblings, 1 reply; 33+ messages in thread
From: Derek J. Clark @ 2026-04-01 19:37 UTC (permalink / raw)
  To: Rong Zhang, Ilpo Järvinen, Hans de Goede
  Cc: Mark Pearson, Armin Wolf, Jonathan Corbet, Kurt Borja,
	platform-driver-x86, linux-kernel

On April 1, 2026 11:44:51 AM PDT, Rong Zhang <i@rong.moe> wrote:
>Hi Derek,
>
>On Tue, 2026-03-31 at 18:11 +0000, Derek J. Clark wrote:
>> This series adds many much needed features and fixes to the lenovo-wmi
>> drivers.
>> 
>> Patch 1 moves LWMI_FAN_DIV to be next to the rest of the fan attribute
>> defines in preparation for adding additional attrbiute macros. This is
>> so the attribute macros can all be in the same place in the file.
>> 
>> Patch 2 cleans up tunable_attr_01 by removing an unused pointer and
>> correctly assigning the members as u8 isntead of u32.
>> 
>> Patch 3 fixes a bug when sending 32 bit arguments via WMI where the
>> second value in the args struct was uninitialized.
>> 
>> Patch 4 moves all gamezone enums from the gamezone header into the
>> helpers header in preparation for the rest of the series.
>> 
>> Patch 5 adds a function to make assigning attribute ID's for capdata
>> cleaner and easier.
>> 
>> Patch 6 addresses bugs where devices that don't support exposed
>> attributes would still create the attribute, and also attempts to
>> identify the correct capdata and set/get methods since some legacy
>> interfaces don't use the custom mode in the method or capdata ID.
>> 
>> Patch 7 adds the remaining CPU attributes that weren't previously
>> exposed.
>> 
>> Patch 8 adds GPU attributes.
>> 
>> Patch 9 renames a name constant in preparation for patch 6.
>> 
>> Patch 10 adds battery charge-type limiting when supported only by WMI, or
>> when a module parameter to skip compatibility checks is set. The
>> MODULE_PARM_DESC macro creates one check and two warnings in checkpatch.
>> I reviewed other examples from the kernel and I am following the same
>> convention, so I left it as is.
>> 
>> Patch 11 fixes a bug where the 'gamezone' and the 'other' drivers were
>> incorrectly coupled in the Kconfig, leading to side effects under
>> certain kernel configurations.
>> 
>> Patch 12 adds a debugfs directory.
>> 
>> Patch 13 adds a debugfs file for dumping capdata.
>> 
>> Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
>
>The series LGTM except for some tiny issues. See my replies to the
>corresponding patches.
>
>Sashiko.dev reports some potential issues in the series as well as some
>existing bugs.
>
>https://sashiko.dev/#/patchset/20260331181208.421552-1-derekjohn.clark%40gmail.com
>
>I've mentioned some in my reply to the corresponding patches. Besides, I
>am going to talk about existing bugs here:
>
>
>- tunable_attr_01 is a statically shared structure, but .dev is
>overwritten each time when the master is bound, which violates
>`.no_singleton = true'
>
>AFAIK no device in wild has multiple wmi-other instances so it should be
>safe for the time being.
>
>Fixing it will need numerous fundamental refactorings to the attributes
>and conflict with patch 6 where .cd_mode_id and .cv_mode_id is added to
>tunable_attr_01.
>
>I don't really want to make this series too large and miss this cycle.
>Hence, I am going to fix it in the next cycle with some extra
>refactorings to get rid of attribute-specific show/store callbacks.

Might be good to collaborate on this. I wanted to add some callbacks & macros for BOOL type attributes and rename the current ATTR macro/callbacks to specify U8 in the names. 

I also wanted to add fan curve settings with pwm_auto attributes for devices that support it soon, but that might be a big lift with such a large refactor going on. I personally prefer to alternate between bug fixing and feature adding per series/cycle if I can, this one just became a mix of both.

>For this series, I'd consider violating `.no_singleton = true' a very
>minor concern. It should be fine to keep it as is. If we really care
>about it, we could set it to false temporarily.
>

I'll leave it for now unless Ilpo or Armin specifically want it changed. 

>- Rebinding wmi-other leaks IDA
>
>- Failure in lwmi_om_fw_attr_add() followed by lwmi_other_remove()
>double frees the same IDA
>
>- Calling lwmi_dev_evaluate_int() with `retval == NULL' leaks memory
>
>- Unbalanced component bind and unbind in the error path of
>lwmi_om_master_bind()
>
>Legit concerns. I will reply with a series fixing them. Could you
>incorporate it into your series?
>
>Fix patches should be the very first patches in the series so that
>backporting is less painful. I'd suggest rearranging the series like:
>
>patches in my fix series
>patch 3
>patch 4
>patch 11
>the rest patches
>

Sounds good. I'll try to get those changes incorporated and sent out today. This series is getting out of hand as it is (from 5 to 16 patches now) so putting it to bed is high on my priority list.

Thanks, 

Derek
>Thanks,
>Rong
>
>> ---
>> v6:
>>   - Incorporate Rong Zhang's debugfs and decoupling patches into the
>>     series.
>>   - Add a patch to clean up too many cross-references to wmi-gamezone.h
>>   - Make lwmi_attr_id a static inline in wmi-capdata.h
>>   - Added a patch  to fix a bug where ares.arg1 is uninitialized when it
>>     is sent to the firmware.
>>   - Add supported checks before adding battery extenstion, and ensure
>>     both the new checks and the is_writable checks are not casting u32
>>     to i32.
>>   - Misc formating changes.
>> v5: https://lore.kernel.org/platform-driver-x86/20260324221032.1333636-1-derekjohn.clark@gmail.com/
>>   - Remove cv/cd_mode_id references that occured before patch 4.
>>   - Move lwmi_attr_id to capdata.c with a namespace export.
>>   - Fix mixing include.
>>   - Make lwmi_attr_is_supported return bool.
>>   - Use switch instead of if for setting/getting charge type state.
>>   - Various formatting fixes.
>> v4: https://lore.kernel.org/platform-driver-x86/20260312031032.3467565-1-derekjohn.clark@gmail.com/
>>   - Use loop instead of back gotos for identifying the working attribute
>>     ID.
>>   - Use function instead of macro to assign attribute_id, preserving
>>     types.
>>   - Removed unused defines and enum values.
>>   - Rename charging defines to clarify thier purpose.
>>   - Fixed various formatting issues from v3.
>>   - Added module param to skip ACPI check when loading the driver for
>>     the power supply extension.
>>   - Don't abort adding power supply extension if the ACPI handle from
>>     ideapad is not present.
>>   - Don't worry about symmetric cleanup when cleaning up attributes in
>>     an error state.
>>   - Reword Patch 8 commit message to be more concise.
>>   - Fix wording in Patch 7 to match the changes.
>> v3: https://lore.kernel.org/platform-driver-x86/20260224043200.2680384-1-derekjohn.clark@gmail.com/
>>   - Re-add HWMON name const and just rename LWMI_OM_FW_ATTR_BASE_PATH
>>   - Fix linker warnings by moving acpi/battery include to the end of the
>>     list.
>>   - Remove CPU/GPU OC features. These attributes are BOOL type and will
>>     need a new constructor that I'll add later.
>> v2: https://lore.kernel.org/platform-driver-x86/20260215061339.2842486-1-derekjohn.clark@gmail.com/
>>   - Fix gpu_mode misisng from attributes list.
>>   - Fix prototypes for power suppy patch.
>>   - Reorganize CPU and GPU attributes alphabetically.
>>   - Break out the patch consolidating the driver name cost.
>>   - Move some of the refactoring of attribute_id back to into patch 1
>>     where it belongs.
>>   - Fix some additional typos in function prototypes.
>> v1: https://lore.kernel.org/platform-driver-x86/20260213081243.794288-1-derekjohn.clark@gmail.com/
>> 
>> 
>> Derek J. Clark (10):
>>   platform/x86: lenovo-wmi-other: Move LWMI_FAN_DIV
>>   platform/x86: lenovo-wmi-other: Fix tunable_attr_01 struct members
>>   platform/x86: lenovo-wmi-other: Zero initialize WMI arguments
>>   platform/x86: lenovo-wmi-helpers: Move gamezone enums to wmi-helpers
>>   platform/x86: lenovo-wmi-other: Add lwmi_attr_id() function
>>   platform/x86: lenovo-wmi-other: Limit adding attributes to supported
>>     devices
>>   platform/x86: lenovo-wmi-other: Add missing CPU tunable attributes
>>   platform/x86: lenovo-wmi-other: Add GPU tunable attributes
>>   platform/x86: lenovo-wmi-other: Rename LWMI_OM_FW_ATTR_BASE_PATH
>>   platform/x86: lenovo-wmi-other: Add WMI battery charge limiting
>> 
>> Rong Zhang (3):
>>   platform/x86: lenovo: Decouple lenovo-wmi-gamezone and
>>     lenovo-wmi-other
>>   platform/x86: lenovo-wmi-helpers: Add helper for creating per-device
>>     debugfs dir
>>   platform/x86: lenovo-wmi-capdata: Add debugfs file for dumping capdata
>> 
>>  .../wmi/devices/lenovo-wmi-other.rst          |  19 +
>>  drivers/platform/x86/lenovo/Kconfig           |   3 +-
>>  drivers/platform/x86/lenovo/wmi-capdata.c     | 128 +++-
>>  drivers/platform/x86/lenovo/wmi-capdata.h     |  31 +-
>>  drivers/platform/x86/lenovo/wmi-events.c      |   2 +-
>>  drivers/platform/x86/lenovo/wmi-gamezone.c    |   5 +-
>>  drivers/platform/x86/lenovo/wmi-gamezone.h    |  20 -
>>  drivers/platform/x86/lenovo/wmi-helpers.c     | 136 ++++
>>  drivers/platform/x86/lenovo/wmi-helpers.h     |  23 +
>>  drivers/platform/x86/lenovo/wmi-other.c       | 689 ++++++++++++++----
>>  drivers/platform/x86/lenovo/wmi-other.h       |  16 -
>>  11 files changed, 891 insertions(+), 181 deletions(-)
>>  delete mode 100644 drivers/platform/x86/lenovo/wmi-gamezone.h
>>  delete mode 100644 drivers/platform/x86/lenovo/wmi-other.h


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH v6 00/13] platform-x86: lenovo-wmi: Add fixes and enhancement
  2026-04-01 19:37   ` [PATCH v6 00/13] platform-x86: lenovo-wmi: Add fixes and enhancement Derek J. Clark
@ 2026-04-01 20:12     ` Rong Zhang
  0 siblings, 0 replies; 33+ messages in thread
From: Rong Zhang @ 2026-04-01 20:12 UTC (permalink / raw)
  To: Derek J. Clark, Ilpo Järvinen, Hans de Goede
  Cc: Mark Pearson, Armin Wolf, Jonathan Corbet, Kurt Borja,
	platform-driver-x86, linux-kernel

Hi Derek,

On Wed, 2026-04-01 at 12:37 -0700, Derek J. Clark wrote:
> On April 1, 2026 11:44:51 AM PDT, Rong Zhang <i@rong.moe> wrote:
> > Hi Derek,
> > 
> > On Tue, 2026-03-31 at 18:11 +0000, Derek J. Clark wrote:
> > > This series adds many much needed features and fixes to the lenovo-wmi
> > > drivers.
> > > 
> > > Patch 1 moves LWMI_FAN_DIV to be next to the rest of the fan attribute
> > > defines in preparation for adding additional attrbiute macros. This is
> > > so the attribute macros can all be in the same place in the file.
> > > 
> > > Patch 2 cleans up tunable_attr_01 by removing an unused pointer and
> > > correctly assigning the members as u8 isntead of u32.
> > > 
> > > Patch 3 fixes a bug when sending 32 bit arguments via WMI where the
> > > second value in the args struct was uninitialized.
> > > 
> > > Patch 4 moves all gamezone enums from the gamezone header into the
> > > helpers header in preparation for the rest of the series.
> > > 
> > > Patch 5 adds a function to make assigning attribute ID's for capdata
> > > cleaner and easier.
> > > 
> > > Patch 6 addresses bugs where devices that don't support exposed
> > > attributes would still create the attribute, and also attempts to
> > > identify the correct capdata and set/get methods since some legacy
> > > interfaces don't use the custom mode in the method or capdata ID.
> > > 
> > > Patch 7 adds the remaining CPU attributes that weren't previously
> > > exposed.
> > > 
> > > Patch 8 adds GPU attributes.
> > > 
> > > Patch 9 renames a name constant in preparation for patch 6.
> > > 
> > > Patch 10 adds battery charge-type limiting when supported only by WMI, or
> > > when a module parameter to skip compatibility checks is set. The
> > > MODULE_PARM_DESC macro creates one check and two warnings in checkpatch.
> > > I reviewed other examples from the kernel and I am following the same
> > > convention, so I left it as is.
> > > 
> > > Patch 11 fixes a bug where the 'gamezone' and the 'other' drivers were
> > > incorrectly coupled in the Kconfig, leading to side effects under
> > > certain kernel configurations.
> > > 
> > > Patch 12 adds a debugfs directory.
> > > 
> > > Patch 13 adds a debugfs file for dumping capdata.
> > > 
> > > Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
> > 
> > The series LGTM except for some tiny issues. See my replies to the
> > corresponding patches.
> > 
> > Sashiko.dev reports some potential issues in the series as well as some
> > existing bugs.
> > 
> > https://sashiko.dev/#/patchset/20260331181208.421552-1-derekjohn.clark%40gmail.com
> > 
> > I've mentioned some in my reply to the corresponding patches. Besides, I
> > am going to talk about existing bugs here:
> > 
> > 
> > - tunable_attr_01 is a statically shared structure, but .dev is
> > overwritten each time when the master is bound, which violates
> > `.no_singleton = true'
> > 
> > AFAIK no device in wild has multiple wmi-other instances so it should be
> > safe for the time being.
> > 
> > Fixing it will need numerous fundamental refactorings to the attributes
> > and conflict with patch 6 where .cd_mode_id and .cv_mode_id is added to
> > tunable_attr_01.
> > 
> > I don't really want to make this series too large and miss this cycle.
> > Hence, I am going to fix it in the next cycle with some extra
> > refactorings to get rid of attribute-specific show/store callbacks.
> 
> Might be good to collaborate on this. I wanted to add some callbacks & macros for BOOL type attributes and rename the current ATTR macro/callbacks to specify U8 in the names. 

Thanks. I will get in touch with you off-list when I finishes the PoC.
Probably in this week or next if I am not going to be very busy :)

> 
> I also wanted to add fan curve settings with pwm_auto attributes for devices that support it soon, but that might be a big lift with such a large refactor going on. I personally prefer to alternate between bug fixing and feature adding per series/cycle if I can, this one just became a mix of both.

I understand your difficulties, as that's mostly my preference too. But
on the other hand, bug reports can't be ignored if it's legit and
reproducible.

I tried my best to make my fix patches tiny and easy to review. I hope
this is helpful.

> 
> > For this series, I'd consider violating `.no_singleton = true' a very
> > minor concern. It should be fine to keep it as is. If we really care
> > about it, we could set it to false temporarily.
> > 
> 
> I'll leave it for now unless Ilpo or Armin specifically want it changed. 
> 
> > - Rebinding wmi-other leaks IDA
> > 
> > - Failure in lwmi_om_fw_attr_add() followed by lwmi_other_remove()
> > double frees the same IDA
> > 
> > - Calling lwmi_dev_evaluate_int() with `retval == NULL' leaks memory
> > 
> > - Unbalanced component bind and unbind in the error path of
> > lwmi_om_master_bind()
> > 
> > Legit concerns. I will reply with a series fixing them. Could you
> > incorporate it into your series?
> > 
> > Fix patches should be the very first patches in the series so that
> > backporting is less painful. I'd suggest rearranging the series like:
> > 
> > patches in my fix series
> > patch 3
> > patch 4
> > patch 11
> > the rest patches
> > 
> 
> Sounds good. I'll try to get those changes incorporated and sent out today. This series is getting out of hand as it is (from 5 to 16 patches now) so putting it to bed is high on my priority list.

I also hope the dust settles soon. Thanks a lot for your great efforts
and patience -- they are priceless.

Thanks,
Rong

> 
> Thanks, 
> 
> Derek
> > Thanks,
> > Rong
> > 
> > > ---
> > > v6:
> > >   - Incorporate Rong Zhang's debugfs and decoupling patches into the
> > >     series.
> > >   - Add a patch to clean up too many cross-references to wmi-gamezone.h
> > >   - Make lwmi_attr_id a static inline in wmi-capdata.h
> > >   - Added a patch  to fix a bug where ares.arg1 is uninitialized when it
> > >     is sent to the firmware.
> > >   - Add supported checks before adding battery extenstion, and ensure
> > >     both the new checks and the is_writable checks are not casting u32
> > >     to i32.
> > >   - Misc formating changes.
> > > v5: https://lore.kernel.org/platform-driver-x86/20260324221032.1333636-1-derekjohn.clark@gmail.com/
> > >   - Remove cv/cd_mode_id references that occured before patch 4.
> > >   - Move lwmi_attr_id to capdata.c with a namespace export.
> > >   - Fix mixing include.
> > >   - Make lwmi_attr_is_supported return bool.
> > >   - Use switch instead of if for setting/getting charge type state.
> > >   - Various formatting fixes.
> > > v4: https://lore.kernel.org/platform-driver-x86/20260312031032.3467565-1-derekjohn.clark@gmail.com/
> > >   - Use loop instead of back gotos for identifying the working attribute
> > >     ID.
> > >   - Use function instead of macro to assign attribute_id, preserving
> > >     types.
> > >   - Removed unused defines and enum values.
> > >   - Rename charging defines to clarify thier purpose.
> > >   - Fixed various formatting issues from v3.
> > >   - Added module param to skip ACPI check when loading the driver for
> > >     the power supply extension.
> > >   - Don't abort adding power supply extension if the ACPI handle from
> > >     ideapad is not present.
> > >   - Don't worry about symmetric cleanup when cleaning up attributes in
> > >     an error state.
> > >   - Reword Patch 8 commit message to be more concise.
> > >   - Fix wording in Patch 7 to match the changes.
> > > v3: https://lore.kernel.org/platform-driver-x86/20260224043200.2680384-1-derekjohn.clark@gmail.com/
> > >   - Re-add HWMON name const and just rename LWMI_OM_FW_ATTR_BASE_PATH
> > >   - Fix linker warnings by moving acpi/battery include to the end of the
> > >     list.
> > >   - Remove CPU/GPU OC features. These attributes are BOOL type and will
> > >     need a new constructor that I'll add later.
> > > v2: https://lore.kernel.org/platform-driver-x86/20260215061339.2842486-1-derekjohn.clark@gmail.com/
> > >   - Fix gpu_mode misisng from attributes list.
> > >   - Fix prototypes for power suppy patch.
> > >   - Reorganize CPU and GPU attributes alphabetically.
> > >   - Break out the patch consolidating the driver name cost.
> > >   - Move some of the refactoring of attribute_id back to into patch 1
> > >     where it belongs.
> > >   - Fix some additional typos in function prototypes.
> > > v1: https://lore.kernel.org/platform-driver-x86/20260213081243.794288-1-derekjohn.clark@gmail.com/
> > > 
> > > 
> > > Derek J. Clark (10):
> > >   platform/x86: lenovo-wmi-other: Move LWMI_FAN_DIV
> > >   platform/x86: lenovo-wmi-other: Fix tunable_attr_01 struct members
> > >   platform/x86: lenovo-wmi-other: Zero initialize WMI arguments
> > >   platform/x86: lenovo-wmi-helpers: Move gamezone enums to wmi-helpers
> > >   platform/x86: lenovo-wmi-other: Add lwmi_attr_id() function
> > >   platform/x86: lenovo-wmi-other: Limit adding attributes to supported
> > >     devices
> > >   platform/x86: lenovo-wmi-other: Add missing CPU tunable attributes
> > >   platform/x86: lenovo-wmi-other: Add GPU tunable attributes
> > >   platform/x86: lenovo-wmi-other: Rename LWMI_OM_FW_ATTR_BASE_PATH
> > >   platform/x86: lenovo-wmi-other: Add WMI battery charge limiting
> > > 
> > > Rong Zhang (3):
> > >   platform/x86: lenovo: Decouple lenovo-wmi-gamezone and
> > >     lenovo-wmi-other
> > >   platform/x86: lenovo-wmi-helpers: Add helper for creating per-device
> > >     debugfs dir
> > >   platform/x86: lenovo-wmi-capdata: Add debugfs file for dumping capdata
> > > 
> > >  .../wmi/devices/lenovo-wmi-other.rst          |  19 +
> > >  drivers/platform/x86/lenovo/Kconfig           |   3 +-
> > >  drivers/platform/x86/lenovo/wmi-capdata.c     | 128 +++-
> > >  drivers/platform/x86/lenovo/wmi-capdata.h     |  31 +-
> > >  drivers/platform/x86/lenovo/wmi-events.c      |   2 +-
> > >  drivers/platform/x86/lenovo/wmi-gamezone.c    |   5 +-
> > >  drivers/platform/x86/lenovo/wmi-gamezone.h    |  20 -
> > >  drivers/platform/x86/lenovo/wmi-helpers.c     | 136 ++++
> > >  drivers/platform/x86/lenovo/wmi-helpers.h     |  23 +
> > >  drivers/platform/x86/lenovo/wmi-other.c       | 689 ++++++++++++++----
> > >  drivers/platform/x86/lenovo/wmi-other.h       |  16 -
> > >  11 files changed, 891 insertions(+), 181 deletions(-)
> > >  delete mode 100644 drivers/platform/x86/lenovo/wmi-gamezone.h
> > >  delete mode 100644 drivers/platform/x86/lenovo/wmi-other.h

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [PATCH 1/3] platform/x86: lenovo-wmi-helpers: Fix memory leak in lwmi_dev_evaluate_int()
  2026-04-01 19:00   ` [PATCH 1/3] platform/x86: lenovo-wmi-helpers: Fix memory leak in lwmi_dev_evaluate_int() Rong Zhang
@ 2026-04-02 10:06     ` Ilpo Järvinen
  0 siblings, 0 replies; 33+ messages in thread
From: Ilpo Järvinen @ 2026-04-02 10:06 UTC (permalink / raw)
  To: Rong Zhang
  Cc: Derek J . Clark, Hans de Goede, Mark Pearson, Armin Wolf,
	Jonathan Corbet, Kurt Borja, platform-driver-x86, LKML, stable

On Thu, 2 Apr 2026, Rong Zhang wrote:

> lwmi_dev_evaluate_int() leaks output.pointer when retval == NULL (found
> by sashiko.dev [1]).
> 
> Fix it by moving `ret_obj = output.pointer' outside of the `if (retval)'
> block so that it is always freed by the __free cleanup callback.
> 
> No functional change intended.
> 
> Fixes: e521d16e76cd ("platform/x86: Add lenovo-wmi-helpers")
> Cc: stable@vger.kernel.org
> Link: https://sashiko.dev/#/patchset/20260331181208.421552-1-derekjohn.clark%40gmail.com [1]
> Signed-off-by: Rong Zhang <i@rong.moe>
> ---
>  drivers/platform/x86/lenovo/wmi-helpers.c | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/platform/x86/lenovo/wmi-helpers.c b/drivers/platform/x86/lenovo/wmi-helpers.c
> index 7379defac500..80021f59d1ef 100644
> --- a/drivers/platform/x86/lenovo/wmi-helpers.c
> +++ b/drivers/platform/x86/lenovo/wmi-helpers.c
> @@ -55,8 +55,9 @@ int lwmi_dev_evaluate_int(struct wmi_device *wdev, u8 instance, u32 method_id,
>  	if (ACPI_FAILURE(status))
>  		return -EIO;
>  
> +	ret_obj = output.pointer;

To follow the best practice, could you please also place the variable 
declaration here as well so there's no separate ret_obj = NULL line.

> +
>  	if (retval) {
> -		ret_obj = output.pointer;
>  		if (!ret_obj)
>  			return -ENODATA;

-- 
 i.


^ permalink raw reply	[flat|nested] 33+ messages in thread

end of thread, other threads:[~2026-04-02 10:06 UTC | newest]

Thread overview: 33+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-31 18:11 [PATCH v6 00/13] platform-x86: lenovo-wmi: Add fixes and enhancement Derek J. Clark
2026-03-31 18:11 ` [PATCH v6 01/13] platform/x86: lenovo-wmi-other: Move LWMI_FAN_DIV Derek J. Clark
2026-03-31 18:11 ` [PATCH v6 02/13] platform/x86: lenovo-wmi-other: Fix tunable_attr_01 struct members Derek J. Clark
2026-03-31 18:11 ` [PATCH v6 03/13] platform/x86: lenovo-wmi-other: Zero initialize WMI arguments Derek J. Clark
2026-04-01 18:26   ` Rong Zhang
2026-03-31 18:11 ` [PATCH v6 04/13] platform/x86: lenovo-wmi-helpers: Move gamezone enums to wmi-helpers Derek J. Clark
2026-04-01 18:26   ` Rong Zhang
2026-03-31 18:12 ` [PATCH v6 05/13] platform/x86: lenovo-wmi-other: Add lwmi_attr_id() function Derek J. Clark
2026-04-01 18:29   ` Rong Zhang
2026-04-01 19:24     ` Derek J. Clark
2026-03-31 18:12 ` [PATCH v6 06/13] platform/x86: lenovo-wmi-other: Limit adding attributes to supported devices Derek J. Clark
2026-03-31 22:28   ` Kurt Borja
2026-03-31 18:12 ` [PATCH v6 07/13] platform/x86: lenovo-wmi-other: Add missing CPU tunable attributes Derek J. Clark
2026-03-31 22:29   ` Kurt Borja
2026-04-01 18:30   ` Rong Zhang
2026-03-31 18:12 ` [PATCH v6 08/13] platform/x86: lenovo-wmi-other: Add GPU " Derek J. Clark
2026-03-31 22:29   ` Kurt Borja
2026-04-01 18:30   ` Rong Zhang
2026-03-31 18:12 ` [PATCH v6 09/13] platform/x86: lenovo-wmi-other: Rename LWMI_OM_FW_ATTR_BASE_PATH Derek J. Clark
2026-03-31 18:12 ` [PATCH v6 10/13] platform/x86: lenovo-wmi-other: Add WMI battery charge limiting Derek J. Clark
2026-04-01 18:32   ` Rong Zhang
2026-03-31 18:12 ` [PATCH v6 11/13] platform/x86: lenovo: Decouple lenovo-wmi-gamezone and lenovo-wmi-other Derek J. Clark
2026-04-01 18:34   ` Rong Zhang
2026-03-31 18:12 ` [PATCH v6 12/13] platform/x86: lenovo-wmi-helpers: Add helper for creating per-device debugfs dir Derek J. Clark
2026-04-01 18:35   ` Rong Zhang
2026-03-31 18:12 ` [PATCH v6 13/13] platform/x86: lenovo-wmi-capdata: Add debugfs file for dumping capdata Derek J. Clark
2026-04-01 18:44 ` [PATCH v6 00/13] platform-x86: lenovo-wmi: Add fixes and enhancement Rong Zhang
2026-04-01 19:00   ` [PATCH 1/3] platform/x86: lenovo-wmi-helpers: Fix memory leak in lwmi_dev_evaluate_int() Rong Zhang
2026-04-02 10:06     ` Ilpo Järvinen
2026-04-01 19:00   ` [PATCH 2/3] platform/x86: lenovo-wmi-other: Balance IDA id allocation and free Rong Zhang
2026-04-01 19:00   ` [PATCH 3/3] platform/x86: lenovo-wmi-other: Balance component bind and unbind Rong Zhang
2026-04-01 19:37   ` [PATCH v6 00/13] platform-x86: lenovo-wmi: Add fixes and enhancement Derek J. Clark
2026-04-01 20:12     ` Rong Zhang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox