[PATCH v5 8/8] platform/x86: lenovo-wmi-other: Add WMI battery charge limiting

Derek J. Clark posted 8 patches 1 week, 2 days ago
There is a newer version of this series
[PATCH v5 8/8] platform/x86: lenovo-wmi-other: Add WMI battery charge limiting
Posted by Derek J. Clark 1 week, 2 days ago
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: Mark Pearson <mpearson-lenovo@squebb.ca>
Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
---
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   | 250 +++++++++++++++++++++-
 3 files changed, 251 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 b026ee30c828..1939401c6c14 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_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 11c16857ef97..cef51cded7be 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-gamezone.h"
@@ -79,9 +82,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
@@ -92,10 +97,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"
 
@@ -137,6 +149,8 @@ struct lwmi_om_priv {
 		bool capdata00_collected : 1;
 		bool capdata_fan_collected : 1;
 	} fan_flags;
+
+	struct acpi_battery_hook battery_hook;
 };
 
 /*
@@ -561,6 +575,239 @@ 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 lwmi_om_priv *priv = ext_data;
+	struct wmi_method_args_32 args;
+	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 lwmi_om_priv *priv = ext_data;
+	struct wmi_method_args_32 args;
+
+	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 %#10x 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_writeable() - Determine if the property is supported
+ * @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 supported.
+ *
+ * Return: Support level, 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)
+{
+	struct lwmi_om_priv *priv = ext_data;
+	struct capdata00 capdata;
+	u32 attribute_id = LWMI_ATTR_ID_PSU(LWMI_FEATURE_ID_PSU_CHARGE_TYPE, LWMI_TYPE_ID_PSU_AC);
+	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 (%#10x) support level: %#x\n",
+		attribute_id, capdata.supported);
+
+	return capdata.supported;
+}
+
+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_ps_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_ps_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;
+
+	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 {
@@ -1318,6 +1565,7 @@ static int lwmi_om_master_bind(struct device *dev)
 		return -ENODEV;
 
 	lwmi_om_fan_info_collect_cd00(priv);
+	lwmi_om_ps_ext_init(priv);
 
 	return lwmi_om_fw_attr_add(priv);
 }
-- 
2.53.0
Re: [PATCH v5 8/8] platform/x86: lenovo-wmi-other: Add WMI battery charge limiting
Posted by kernel test robot 1 week, 1 day ago
Hi Derek,

kernel test robot noticed the following build errors:

[auto build test ERROR on linus/master]
[also build test ERROR on v7.0-rc5 next-20260324]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Derek-J-Clark/platform-x86-lenovo-wmi-other-Move-LWMI_FAN_DIV/20260325-070851
base:   linus/master
patch link:    https://lore.kernel.org/r/20260324221032.1333636-9-derekjohn.clark%40gmail.com
patch subject: [PATCH v5 8/8] platform/x86: lenovo-wmi-other: Add WMI battery charge limiting
config: x86_64-randconfig-005-20260325 (https://download.01.org/0day-ci/archive/20260326/202603260302.X0NjQOda-lkp@intel.com/config)
compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260326/202603260302.X0NjQOda-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202603260302.X0NjQOda-lkp@intel.com/

All errors (new ones prefixed by >>):

   ld: vmlinux.o: in function `lwmi_om_fan_get_set':
>> drivers/platform/x86/lenovo/wmi-other.c:206:(.text+0x1b74542): undefined reference to `lwmi_attr_id'
   ld: vmlinux.o: in function `lwmi_attr_01_is_supported':
   drivers/platform/x86/lenovo/wmi-other.c:1267:(.text+0x1b74fdf): undefined reference to `lwmi_attr_id'
>> ld: drivers/platform/x86/lenovo/wmi-other.c:1270:(.text+0x1b75013): undefined reference to `lwmi_cd01_get_data'
>> ld: drivers/platform/x86/lenovo/wmi-other.c:1283:(.text+0x1b7515b): undefined reference to `lwmi_attr_id'
   ld: drivers/platform/x86/lenovo/wmi-other.c:1299:(.text+0x1b752f4): undefined reference to `lwmi_attr_id'
   ld: vmlinux.o: in function `lwmi_om_fan_info_collect_cd_fan':
>> drivers/platform/x86/lenovo/wmi-other.c:565:(.text+0x1b75ca3): undefined reference to `lwmi_cd_fan_get_data'
   ld: vmlinux.o: in function `lwmi_psy_prop_is_writeable':
   drivers/platform/x86/lenovo/wmi-other.c:687:(.text+0x1b75e2f): undefined reference to `lwmi_attr_id'
>> ld: drivers/platform/x86/lenovo/wmi-other.c:690:(.text+0x1b75e5a): undefined reference to `lwmi_cd00_get_data'
   ld: vmlinux.o: in function `lwmi_psy_ext_set_prop':
   drivers/platform/x86/lenovo/wmi-other.c:649:(.text+0x1b75f9b): undefined reference to `lwmi_attr_id'
   ld: vmlinux.o: in function `lwmi_psy_ext_get_prop':
   drivers/platform/x86/lenovo/wmi-other.c:603:(.text+0x1b761aa): undefined reference to `lwmi_attr_id'
   ld: vmlinux.o: in function `lwmi_other_probe':
>> drivers/platform/x86/lenovo/wmi-other.c:1612:(.text+0x1b764eb): undefined reference to `lwmi_cd_match_add_all'
   ld: vmlinux.o: in function `lwmi_om_fan_info_collect_cd00':
   drivers/platform/x86/lenovo/wmi-other.c:540:(.text+0x1b7680f): undefined reference to `lwmi_attr_id'
   ld: drivers/platform/x86/lenovo/wmi-other.c:540:(.text+0x1b76838): undefined reference to `lwmi_cd00_get_data'
   ld: vmlinux.o: in function `lwmi_om_ps_ext_init':
>> drivers/platform/x86/lenovo/wmi-other.c:806:(.text+0x1b76a57): undefined reference to `devm_battery_hook_register'
   ld: vmlinux.o: in function `lwmi_om_fw_attr_add':
>> drivers/platform/x86/lenovo/wmi-other.c:1474:(.text+0x1b76afa): undefined reference to `firmware_attributes_class'
   ld: vmlinux.o: in function `attr_current_value_show':
   drivers/platform/x86/lenovo/wmi-other.c:1225:(.text+0x1b77015): undefined reference to `lwmi_attr_id'
   ld: vmlinux.o: in function `attr_current_value_store':
   drivers/platform/x86/lenovo/wmi-other.c:1165:(.text+0x1b776e2): undefined reference to `lwmi_attr_id'
   ld: drivers/platform/x86/lenovo/wmi-other.c:1168:(.text+0x1b7771c): undefined reference to `lwmi_cd01_get_data'
   ld: drivers/platform/x86/lenovo/wmi-other.c:1179:(.text+0x1b7781e): undefined reference to `lwmi_attr_id'
   ld: vmlinux.o: in function `attr_capdata01_show':
   drivers/platform/x86/lenovo/wmi-other.c:1099:(.text+0x1b77e38): undefined reference to `lwmi_attr_id'
   ld: drivers/platform/x86/lenovo/wmi-other.c:1102:(.text+0x1b77e67): undefined reference to `lwmi_cd01_get_data'

Kconfig warnings: (for reference only)
   WARNING: unmet direct dependencies detected for LENOVO_WMI_TUNING
   Depends on [m]: X86_PLATFORM_DEVICES [=y] && ACPI_WMI [=y] && ACPI_BATTERY [=m]
   Selected by [y]:
   - LENOVO_WMI_GAMEZONE [=y] && X86_PLATFORM_DEVICES [=y] && ACPI_WMI [=y] && DMI [=y]


vim +206 drivers/platform/x86/lenovo/wmi-other.c

51ed34282f63fa Rong Zhang 2026-01-21  186  
51ed34282f63fa Rong Zhang 2026-01-21  187  /**
51ed34282f63fa Rong Zhang 2026-01-21  188   * lwmi_om_fan_get_set() - Get or set fan RPM value of specified fan
51ed34282f63fa Rong Zhang 2026-01-21  189   * @priv: Driver private data structure
51ed34282f63fa Rong Zhang 2026-01-21  190   * @channel: Fan channel index (0-based)
51ed34282f63fa Rong Zhang 2026-01-21  191   * @val: Pointer to value (input for set, output for get)
51ed34282f63fa Rong Zhang 2026-01-21  192   * @set: True to set value, false to get value
51ed34282f63fa Rong Zhang 2026-01-21  193   *
51ed34282f63fa Rong Zhang 2026-01-21  194   * Communicates with WMI interface to either retrieve current fan RPM
51ed34282f63fa Rong Zhang 2026-01-21  195   * or set target fan RPM.
51ed34282f63fa Rong Zhang 2026-01-21  196   *
51ed34282f63fa Rong Zhang 2026-01-21  197   * Return: 0 on success, or an error code.
51ed34282f63fa Rong Zhang 2026-01-21  198   */
51ed34282f63fa Rong Zhang 2026-01-21  199  static int lwmi_om_fan_get_set(struct lwmi_om_priv *priv, int channel, u32 *val, bool set)
51ed34282f63fa Rong Zhang 2026-01-21  200  {
51ed34282f63fa Rong Zhang 2026-01-21  201  	struct wmi_method_args_32 args;
51ed34282f63fa Rong Zhang 2026-01-21  202  	u32 method_id, retval;
51ed34282f63fa Rong Zhang 2026-01-21  203  	int err;
51ed34282f63fa Rong Zhang 2026-01-21  204  
51ed34282f63fa Rong Zhang 2026-01-21  205  	method_id = set ? LWMI_FEATURE_VALUE_SET : LWMI_FEATURE_VALUE_GET;
51ed34282f63fa Rong Zhang 2026-01-21 @206  	args.arg0 = LWMI_ATTR_ID_FAN_RPM(channel);
51ed34282f63fa Rong Zhang 2026-01-21  207  	args.arg1 = set ? *val : 0;
51ed34282f63fa Rong Zhang 2026-01-21  208  
51ed34282f63fa Rong Zhang 2026-01-21  209  	err = lwmi_dev_evaluate_int(priv->wdev, 0x0, method_id,
51ed34282f63fa Rong Zhang 2026-01-21  210  				    (unsigned char *)&args, sizeof(args), &retval);
51ed34282f63fa Rong Zhang 2026-01-21  211  	if (err)
51ed34282f63fa Rong Zhang 2026-01-21  212  		return err;
51ed34282f63fa Rong Zhang 2026-01-21  213  
51ed34282f63fa Rong Zhang 2026-01-21  214  	if (!set) {
51ed34282f63fa Rong Zhang 2026-01-21  215  		*val = retval;
51ed34282f63fa Rong Zhang 2026-01-21  216  		return 0;
51ed34282f63fa Rong Zhang 2026-01-21  217  	}
51ed34282f63fa Rong Zhang 2026-01-21  218  
51ed34282f63fa Rong Zhang 2026-01-21  219  	/*
51ed34282f63fa Rong Zhang 2026-01-21  220  	 * It seems that 0 means "no error" and 1 means "done". Apparently
51ed34282f63fa Rong Zhang 2026-01-21  221  	 * different firmware teams have different thoughts on indicating
51ed34282f63fa Rong Zhang 2026-01-21  222  	 * success, so we accepts both.
51ed34282f63fa Rong Zhang 2026-01-21  223  	 */
51ed34282f63fa Rong Zhang 2026-01-21  224  	return (retval == 0 || retval == 1) ? 0 : -EIO;
51ed34282f63fa Rong Zhang 2026-01-21  225  }
51ed34282f63fa Rong Zhang 2026-01-21  226  

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Re: [PATCH v5 8/8] platform/x86: lenovo-wmi-other: Add WMI battery charge limiting
Posted by Derek John Clark 1 week, 1 day ago
On Wed, Mar 25, 2026 at 12:43 PM kernel test robot <lkp@intel.com> wrote:
>
> Hi Derek,
>
> kernel test robot noticed the following build errors:
>
> [auto build test ERROR on linus/master]
> [also build test ERROR on v7.0-rc5 next-20260324]
> [If your patch is applied to the wrong git tree, kindly drop us a note.
> And when submitting patch, we suggest to use '--base' as documented in
> https://git-scm.com/docs/git-format-patch#_base_tree_information]
>
> url:    https://github.com/intel-lab-lkp/linux/commits/Derek-J-Clark/platform-x86-lenovo-wmi-other-Move-LWMI_FAN_DIV/20260325-070851
> base:   linus/master
> patch link:    https://lore.kernel.org/r/20260324221032.1333636-9-derekjohn.clark%40gmail.com
> patch subject: [PATCH v5 8/8] platform/x86: lenovo-wmi-other: Add WMI battery charge limiting
> config: x86_64-randconfig-005-20260325 (https://download.01.org/0day-ci/archive/20260326/202603260302.X0NjQOda-lkp@intel.com/config)
> compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
> reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260326/202603260302.X0NjQOda-lkp@intel.com/reproduce)
>
> If you fix the issue in a separate patch/commit (i.e. not just a new version of
> the same patch/commit), kindly add following tags
> | Reported-by: kernel test robot <lkp@intel.com>
> | Closes: https://lore.kernel.org/oe-kbuild-all/202603260302.X0NjQOda-lkp@intel.com/
>
> All errors (new ones prefixed by >>):
>
>    ld: vmlinux.o: in function `lwmi_om_fan_get_set':
> >> drivers/platform/x86/lenovo/wmi-other.c:206:(.text+0x1b74542): undefined reference to `lwmi_attr_id'
>    ld: vmlinux.o: in function `lwmi_attr_01_is_supported':
>    drivers/platform/x86/lenovo/wmi-other.c:1267:(.text+0x1b74fdf): undefined reference to `lwmi_attr_id'
> >> ld: drivers/platform/x86/lenovo/wmi-other.c:1270:(.text+0x1b75013): undefined reference to `lwmi_cd01_get_data'
> >> ld: drivers/platform/x86/lenovo/wmi-other.c:1283:(.text+0x1b7515b): undefined reference to `lwmi_attr_id'
>    ld: drivers/platform/x86/lenovo/wmi-other.c:1299:(.text+0x1b752f4): undefined reference to `lwmi_attr_id'
>    ld: vmlinux.o: in function `lwmi_om_fan_info_collect_cd_fan':
> >> drivers/platform/x86/lenovo/wmi-other.c:565:(.text+0x1b75ca3): undefined reference to `lwmi_cd_fan_get_data'
>    ld: vmlinux.o: in function `lwmi_psy_prop_is_writeable':
>    drivers/platform/x86/lenovo/wmi-other.c:687:(.text+0x1b75e2f): undefined reference to `lwmi_attr_id'
> >> ld: drivers/platform/x86/lenovo/wmi-other.c:690:(.text+0x1b75e5a): undefined reference to `lwmi_cd00_get_data'
>    ld: vmlinux.o: in function `lwmi_psy_ext_set_prop':
>    drivers/platform/x86/lenovo/wmi-other.c:649:(.text+0x1b75f9b): undefined reference to `lwmi_attr_id'
>    ld: vmlinux.o: in function `lwmi_psy_ext_get_prop':
>    drivers/platform/x86/lenovo/wmi-other.c:603:(.text+0x1b761aa): undefined reference to `lwmi_attr_id'
>    ld: vmlinux.o: in function `lwmi_other_probe':
> >> drivers/platform/x86/lenovo/wmi-other.c:1612:(.text+0x1b764eb): undefined reference to `lwmi_cd_match_add_all'
>    ld: vmlinux.o: in function `lwmi_om_fan_info_collect_cd00':
>    drivers/platform/x86/lenovo/wmi-other.c:540:(.text+0x1b7680f): undefined reference to `lwmi_attr_id'
>    ld: drivers/platform/x86/lenovo/wmi-other.c:540:(.text+0x1b76838): undefined reference to `lwmi_cd00_get_data'
>    ld: vmlinux.o: in function `lwmi_om_ps_ext_init':
> >> drivers/platform/x86/lenovo/wmi-other.c:806:(.text+0x1b76a57): undefined reference to `devm_battery_hook_register'
>    ld: vmlinux.o: in function `lwmi_om_fw_attr_add':
> >> drivers/platform/x86/lenovo/wmi-other.c:1474:(.text+0x1b76afa): undefined reference to `firmware_attributes_class'
>    ld: vmlinux.o: in function `attr_current_value_show':
>    drivers/platform/x86/lenovo/wmi-other.c:1225:(.text+0x1b77015): undefined reference to `lwmi_attr_id'
>    ld: vmlinux.o: in function `attr_current_value_store':
>    drivers/platform/x86/lenovo/wmi-other.c:1165:(.text+0x1b776e2): undefined reference to `lwmi_attr_id'
>    ld: drivers/platform/x86/lenovo/wmi-other.c:1168:(.text+0x1b7771c): undefined reference to `lwmi_cd01_get_data'
>    ld: drivers/platform/x86/lenovo/wmi-other.c:1179:(.text+0x1b7781e): undefined reference to `lwmi_attr_id'
>    ld: vmlinux.o: in function `attr_capdata01_show':
>    drivers/platform/x86/lenovo/wmi-other.c:1099:(.text+0x1b77e38): undefined reference to `lwmi_attr_id'
>    ld: drivers/platform/x86/lenovo/wmi-other.c:1102:(.text+0x1b77e67): undefined reference to `lwmi_cd01_get_data'
>
> Kconfig warnings: (for reference only)
>    WARNING: unmet direct dependencies detected for LENOVO_WMI_TUNING
>    Depends on [m]: X86_PLATFORM_DEVICES [=y] && ACPI_WMI [=y] && ACPI_BATTERY [=m]
>    Selected by [y]:
>    - LENOVO_WMI_GAMEZONE [=y] && X86_PLATFORM_DEVICES [=y] && ACPI_WMI [=y] && DMI [=y]
>
>
> vim +206 drivers/platform/x86/lenovo/wmi-other.c
>
> 51ed34282f63fa Rong Zhang 2026-01-21  186
> 51ed34282f63fa Rong Zhang 2026-01-21  187  /**
> 51ed34282f63fa Rong Zhang 2026-01-21  188   * lwmi_om_fan_get_set() - Get or set fan RPM value of specified fan
> 51ed34282f63fa Rong Zhang 2026-01-21  189   * @priv: Driver private data structure
> 51ed34282f63fa Rong Zhang 2026-01-21  190   * @channel: Fan channel index (0-based)
> 51ed34282f63fa Rong Zhang 2026-01-21  191   * @val: Pointer to value (input for set, output for get)
> 51ed34282f63fa Rong Zhang 2026-01-21  192   * @set: True to set value, false to get value
> 51ed34282f63fa Rong Zhang 2026-01-21  193   *
> 51ed34282f63fa Rong Zhang 2026-01-21  194   * Communicates with WMI interface to either retrieve current fan RPM
> 51ed34282f63fa Rong Zhang 2026-01-21  195   * or set target fan RPM.
> 51ed34282f63fa Rong Zhang 2026-01-21  196   *
> 51ed34282f63fa Rong Zhang 2026-01-21  197   * Return: 0 on success, or an error code.
> 51ed34282f63fa Rong Zhang 2026-01-21  198   */
> 51ed34282f63fa Rong Zhang 2026-01-21  199  static int lwmi_om_fan_get_set(struct lwmi_om_priv *priv, int channel, u32 *val, bool set)
> 51ed34282f63fa Rong Zhang 2026-01-21  200  {
> 51ed34282f63fa Rong Zhang 2026-01-21  201       struct wmi_method_args_32 args;
> 51ed34282f63fa Rong Zhang 2026-01-21  202       u32 method_id, retval;
> 51ed34282f63fa Rong Zhang 2026-01-21  203       int err;
> 51ed34282f63fa Rong Zhang 2026-01-21  204
> 51ed34282f63fa Rong Zhang 2026-01-21  205       method_id = set ? LWMI_FEATURE_VALUE_SET : LWMI_FEATURE_VALUE_GET;
> 51ed34282f63fa Rong Zhang 2026-01-21 @206       args.arg0 = LWMI_ATTR_ID_FAN_RPM(channel);
> 51ed34282f63fa Rong Zhang 2026-01-21  207       args.arg1 = set ? *val : 0;
> 51ed34282f63fa Rong Zhang 2026-01-21  208
> 51ed34282f63fa Rong Zhang 2026-01-21  209       err = lwmi_dev_evaluate_int(priv->wdev, 0x0, method_id,
> 51ed34282f63fa Rong Zhang 2026-01-21  210                                   (unsigned char *)&args, sizeof(args), &retval);
> 51ed34282f63fa Rong Zhang 2026-01-21  211       if (err)
> 51ed34282f63fa Rong Zhang 2026-01-21  212               return err;
> 51ed34282f63fa Rong Zhang 2026-01-21  213
> 51ed34282f63fa Rong Zhang 2026-01-21  214       if (!set) {
> 51ed34282f63fa Rong Zhang 2026-01-21  215               *val = retval;
> 51ed34282f63fa Rong Zhang 2026-01-21  216               return 0;
> 51ed34282f63fa Rong Zhang 2026-01-21  217       }
> 51ed34282f63fa Rong Zhang 2026-01-21  218
> 51ed34282f63fa Rong Zhang 2026-01-21  219       /*
> 51ed34282f63fa Rong Zhang 2026-01-21  220        * It seems that 0 means "no error" and 1 means "done". Apparently
> 51ed34282f63fa Rong Zhang 2026-01-21  221        * different firmware teams have different thoughts on indicating
> 51ed34282f63fa Rong Zhang 2026-01-21  222        * success, so we accepts both.
> 51ed34282f63fa Rong Zhang 2026-01-21  223        */
> 51ed34282f63fa Rong Zhang 2026-01-21  224       return (retval == 0 || retval == 1) ? 0 : -EIO;
> 51ed34282f63fa Rong Zhang 2026-01-21  225  }
> 51ed34282f63fa Rong Zhang 2026-01-21  226
>
> --
> 0-DAY CI Kernel Test Service
> https://github.com/intel/lkp-tests/wiki

Can someone explain to me why this is happening? I cannot reproduce
these locally with W=1 and/or C=1. I'd prefer to resolve this before
submitting a v6.

Thanks,
Derek
Re: [PATCH v5 8/8] platform/x86: lenovo-wmi-other: Add WMI battery charge limiting
Posted by Rong Zhang 1 week ago
Hi Derek,

On Wed, 2026-03-25 at 19:12 -0700, Derek John Clark wrote:
> On Wed, Mar 25, 2026 at 12:43 PM kernel test robot <lkp@intel.com> wrote:
> > 
> > Hi Derek,
> > 
> > kernel test robot noticed the following build errors:
> > 
> > [auto build test ERROR on linus/master]
> > [also build test ERROR on v7.0-rc5 next-20260324]
> > [If your patch is applied to the wrong git tree, kindly drop us a note.
> > And when submitting patch, we suggest to use '--base' as documented in
> > https://git-scm.com/docs/git-format-patch#_base_tree_information]
> > 
> > url:    https://github.com/intel-lab-lkp/linux/commits/Derek-J-Clark/platform-x86-lenovo-wmi-other-Move-LWMI_FAN_DIV/20260325-070851
> > base:   linus/master
> > patch link:    https://lore.kernel.org/r/20260324221032.1333636-9-derekjohn.clark%40gmail.com
> > patch subject: [PATCH v5 8/8] platform/x86: lenovo-wmi-other: Add WMI battery charge limiting
> > config: x86_64-randconfig-005-20260325 (https://download.01.org/0day-ci/archive/20260326/202603260302.X0NjQOda-lkp@intel.com/config)

   CONFIG_DMI=y
   
   CONFIG_HWMON=y
   
   CONFIG_FW_ATTR_CLASS=m
   
   CONFIG_ACPI_BATTERY=m
   
   CONFIG_ACPI_PLATFORM_PROFILE=y
   
   CONFIG_ACPI_WMI=y
   
   CONFIG_LENOVO_WMI_CAPDATA=m
   CONFIG_LENOVO_WMI_EVENTS=y
   CONFIG_LENOVO_WMI_HELPERS=y
   CONFIG_LENOVO_WMI_GAMEZONE=y
   CONFIG_LENOVO_WMI_TUNING=y

This isn't a legit config, as CONFIG_LENOVO_WMI_TUNING=y should have
ensured CONFIG_LENOVO_WMI_CAPDATA=y, CONFIG_ACPI_BATTERY=y, and
CONFIG_FW_ATTR_CLASS=y.

> > compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
> > reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260326/202603260302.X0NjQOda-lkp@intel.com/reproduce)
> > 
> > If you fix the issue in a separate patch/commit (i.e. not just a new version of
> > the same patch/commit), kindly add following tags
> > > Reported-by: kernel test robot <lkp@intel.com>
> > > Closes: https://lore.kernel.org/oe-kbuild-all/202603260302.X0NjQOda-lkp@intel.com/
> > 
> > All errors (new ones prefixed by >>):
> > 
> >    ld: vmlinux.o: in function `lwmi_om_fan_get_set':
> > > > drivers/platform/x86/lenovo/wmi-other.c:206:(.text+0x1b74542): undefined reference to `lwmi_attr_id'
> >    ld: vmlinux.o: in function `lwmi_attr_01_is_supported':
> >    drivers/platform/x86/lenovo/wmi-other.c:1267:(.text+0x1b74fdf): undefined reference to `lwmi_attr_id'
> > > > ld: drivers/platform/x86/lenovo/wmi-other.c:1270:(.text+0x1b75013): undefined reference to `lwmi_cd01_get_data'
> > > > ld: drivers/platform/x86/lenovo/wmi-other.c:1283:(.text+0x1b7515b): undefined reference to `lwmi_attr_id'
> >    ld: drivers/platform/x86/lenovo/wmi-other.c:1299:(.text+0x1b752f4): undefined reference to `lwmi_attr_id'
> >    ld: vmlinux.o: in function `lwmi_om_fan_info_collect_cd_fan':
> > > > drivers/platform/x86/lenovo/wmi-other.c:565:(.text+0x1b75ca3): undefined reference to `lwmi_cd_fan_get_data'
> >    ld: vmlinux.o: in function `lwmi_psy_prop_is_writeable':
> >    drivers/platform/x86/lenovo/wmi-other.c:687:(.text+0x1b75e2f): undefined reference to `lwmi_attr_id'
> > > > ld: drivers/platform/x86/lenovo/wmi-other.c:690:(.text+0x1b75e5a): undefined reference to `lwmi_cd00_get_data'
> >    ld: vmlinux.o: in function `lwmi_psy_ext_set_prop':
> >    drivers/platform/x86/lenovo/wmi-other.c:649:(.text+0x1b75f9b): undefined reference to `lwmi_attr_id'
> >    ld: vmlinux.o: in function `lwmi_psy_ext_get_prop':
> >    drivers/platform/x86/lenovo/wmi-other.c:603:(.text+0x1b761aa): undefined reference to `lwmi_attr_id'
> >    ld: vmlinux.o: in function `lwmi_other_probe':
> > > > drivers/platform/x86/lenovo/wmi-other.c:1612:(.text+0x1b764eb): undefined reference to `lwmi_cd_match_add_all'
> >    ld: vmlinux.o: in function `lwmi_om_fan_info_collect_cd00':
> >    drivers/platform/x86/lenovo/wmi-other.c:540:(.text+0x1b7680f): undefined reference to `lwmi_attr_id'
> >    ld: drivers/platform/x86/lenovo/wmi-other.c:540:(.text+0x1b76838): undefined reference to `lwmi_cd00_get_data'
> >    ld: vmlinux.o: in function `lwmi_om_ps_ext_init':
> > > > drivers/platform/x86/lenovo/wmi-other.c:806:(.text+0x1b76a57): undefined reference to `devm_battery_hook_register'
> >    ld: vmlinux.o: in function `lwmi_om_fw_attr_add':
> > > > drivers/platform/x86/lenovo/wmi-other.c:1474:(.text+0x1b76afa): undefined reference to `firmware_attributes_class'
> >    ld: vmlinux.o: in function `attr_current_value_show':
> >    drivers/platform/x86/lenovo/wmi-other.c:1225:(.text+0x1b77015): undefined reference to `lwmi_attr_id'
> >    ld: vmlinux.o: in function `attr_current_value_store':
> >    drivers/platform/x86/lenovo/wmi-other.c:1165:(.text+0x1b776e2): undefined reference to `lwmi_attr_id'
> >    ld: drivers/platform/x86/lenovo/wmi-other.c:1168:(.text+0x1b7771c): undefined reference to `lwmi_cd01_get_data'
> >    ld: drivers/platform/x86/lenovo/wmi-other.c:1179:(.text+0x1b7781e): undefined reference to `lwmi_attr_id'
> >    ld: vmlinux.o: in function `attr_capdata01_show':
> >    drivers/platform/x86/lenovo/wmi-other.c:1099:(.text+0x1b77e38): undefined reference to `lwmi_attr_id'
> >    ld: drivers/platform/x86/lenovo/wmi-other.c:1102:(.text+0x1b77e67): undefined reference to `lwmi_cd01_get_data'
> > 
> > Kconfig warnings: (for reference only)
> >    WARNING: unmet direct dependencies detected for LENOVO_WMI_TUNING

When randconfig selects symbols, some are selected by the randomizer,
while some are selected by Kconfig `select SYMBOL'. In this lkp test,
CONFIG_LENOVO_WMI_GAMEZONE=y was selected by the randomizer, while
CONFIG_LENOVO_WMI_TUNING=y was selected by `select LENOVO_WMI_TUNING' in
`config LENOVO_WMI_GAMEZONE'.

Documentation/kbuild/kconfig-language.rst says "select will force a
symbol to a value without visiting the dependencies", resulting in unmet
dependencies in this test and
https://lore.kernel.org/r/202603252259.gHvJDyh3-lkp@intel.com

> >    Depends on [m]: X86_PLATFORM_DEVICES [=y] && ACPI_WMI [=y] && ACPI_BATTERY [=m]
> >    Selected by [y]:
> >    - LENOVO_WMI_GAMEZONE [=y] && X86_PLATFORM_DEVICES [=y] && ACPI_WMI [=y] && DMI [=y]
> > 
> > 
> > vim +206 drivers/platform/x86/lenovo/wmi-other.c
> > 
> > 51ed34282f63fa Rong Zhang 2026-01-21  186
> > 51ed34282f63fa Rong Zhang 2026-01-21  187  /**
> > 51ed34282f63fa Rong Zhang 2026-01-21  188   * lwmi_om_fan_get_set() - Get or set fan RPM value of specified fan
> > 51ed34282f63fa Rong Zhang 2026-01-21  189   * @priv: Driver private data structure
> > 51ed34282f63fa Rong Zhang 2026-01-21  190   * @channel: Fan channel index (0-based)
> > 51ed34282f63fa Rong Zhang 2026-01-21  191   * @val: Pointer to value (input for set, output for get)
> > 51ed34282f63fa Rong Zhang 2026-01-21  192   * @set: True to set value, false to get value
> > 51ed34282f63fa Rong Zhang 2026-01-21  193   *
> > 51ed34282f63fa Rong Zhang 2026-01-21  194   * Communicates with WMI interface to either retrieve current fan RPM
> > 51ed34282f63fa Rong Zhang 2026-01-21  195   * or set target fan RPM.
> > 51ed34282f63fa Rong Zhang 2026-01-21  196   *
> > 51ed34282f63fa Rong Zhang 2026-01-21  197   * Return: 0 on success, or an error code.
> > 51ed34282f63fa Rong Zhang 2026-01-21  198   */
> > 51ed34282f63fa Rong Zhang 2026-01-21  199  static int lwmi_om_fan_get_set(struct lwmi_om_priv *priv, int channel, u32 *val, bool set)
> > 51ed34282f63fa Rong Zhang 2026-01-21  200  {
> > 51ed34282f63fa Rong Zhang 2026-01-21  201       struct wmi_method_args_32 args;
> > 51ed34282f63fa Rong Zhang 2026-01-21  202       u32 method_id, retval;
> > 51ed34282f63fa Rong Zhang 2026-01-21  203       int err;
> > 51ed34282f63fa Rong Zhang 2026-01-21  204
> > 51ed34282f63fa Rong Zhang 2026-01-21  205       method_id = set ? LWMI_FEATURE_VALUE_SET : LWMI_FEATURE_VALUE_GET;
> > 51ed34282f63fa Rong Zhang 2026-01-21 @206       args.arg0 = LWMI_ATTR_ID_FAN_RPM(channel);
> > 51ed34282f63fa Rong Zhang 2026-01-21  207       args.arg1 = set ? *val : 0;
> > 51ed34282f63fa Rong Zhang 2026-01-21  208
> > 51ed34282f63fa Rong Zhang 2026-01-21  209       err = lwmi_dev_evaluate_int(priv->wdev, 0x0, method_id,
> > 51ed34282f63fa Rong Zhang 2026-01-21  210                                   (unsigned char *)&args, sizeof(args), &retval);
> > 51ed34282f63fa Rong Zhang 2026-01-21  211       if (err)
> > 51ed34282f63fa Rong Zhang 2026-01-21  212               return err;
> > 51ed34282f63fa Rong Zhang 2026-01-21  213
> > 51ed34282f63fa Rong Zhang 2026-01-21  214       if (!set) {
> > 51ed34282f63fa Rong Zhang 2026-01-21  215               *val = retval;
> > 51ed34282f63fa Rong Zhang 2026-01-21  216               return 0;
> > 51ed34282f63fa Rong Zhang 2026-01-21  217       }
> > 51ed34282f63fa Rong Zhang 2026-01-21  218
> > 51ed34282f63fa Rong Zhang 2026-01-21  219       /*
> > 51ed34282f63fa Rong Zhang 2026-01-21  220        * It seems that 0 means "no error" and 1 means "done". Apparently
> > 51ed34282f63fa Rong Zhang 2026-01-21  221        * different firmware teams have different thoughts on indicating
> > 51ed34282f63fa Rong Zhang 2026-01-21  222        * success, so we accepts both.
> > 51ed34282f63fa Rong Zhang 2026-01-21  223        */
> > 51ed34282f63fa Rong Zhang 2026-01-21  224       return (retval == 0 || retval == 1) ? 0 : -EIO;
> > 51ed34282f63fa Rong Zhang 2026-01-21  225  }
> > 51ed34282f63fa Rong Zhang 2026-01-21  226
> > 
> > --
> > 0-DAY CI Kernel Test Service
> > https://github.com/intel/lkp-tests/wiki
> 
> Can someone explain to me why this is happening? I cannot reproduce
> these locally with W=1 and/or C=1. I'd prefer to resolve this before
> submitting a v6.

The issue exists since commit 6e38b9fcbfa3 ('platform/x86: lenovo:
gamezone needs "other mode"'). It might be a coincidence that lkp didn't
running into a similar issue before. IMO `select LENOVO_WMI_TUNING'
shouldn't be used in the first place since LENOVO_WMI_TUNING has
numerous dependencies.

To fix this, we need to decouple lenovo-wmi-gamezone and lenovo-wmi-
other by moving the notifier chain into lenovo-wmi-helpers. I will reply
with a patch soon. Fell free to integrate it into your series.

Note that I couldn't completely test my patch since the lack of gamezone
support on my device.

BTW, the fix patch has conflicts with my debugfs series since both touch
lenovo-wmi-helpers. If you decide to integrate the fix patch into your
series, could you integrate the debugfs series into yours too? The
conflicts are trivial and contextual. You can resolve them yourself if
you don't mind it ;-)

Thanks,
Rong

> 
> Thanks,
> Derek
Re: [PATCH v5 8/8] platform/x86: lenovo-wmi-other: Add WMI battery charge limiting
Posted by Derek J. Clark 1 week ago
On March 26, 2026 9:12:41 AM PDT, Rong Zhang <i@rong.moe> wrote:
>Hi Derek,
>
>On Wed, 2026-03-25 at 19:12 -0700, Derek John Clark wrote:
>> On Wed, Mar 25, 2026 at 12:43 PM kernel test robot <lkp@intel.com> wrote:
>> > 
>> > Hi Derek,
>> > 
>> > kernel test robot noticed the following build errors:
>> > 
>> > [auto build test ERROR on linus/master]
>> > [also build test ERROR on v7.0-rc5 next-20260324]
>> > [If your patch is applied to the wrong git tree, kindly drop us a note.
>> > And when submitting patch, we suggest to use '--base' as documented in
>> > https://git-scm.com/docs/git-format-patch#_base_tree_information]
>> > 
>> > url:    https://github.com/intel-lab-lkp/linux/commits/Derek-J-Clark/platform-x86-lenovo-wmi-other-Move-LWMI_FAN_DIV/20260325-070851
>> > base:   linus/master
>> > patch link:    https://lore.kernel.org/r/20260324221032.1333636-9-derekjohn.clark%40gmail.com
>> > patch subject: [PATCH v5 8/8] platform/x86: lenovo-wmi-other: Add WMI battery charge limiting
>> > config: x86_64-randconfig-005-20260325 (https://download.01.org/0day-ci/archive/20260326/202603260302.X0NjQOda-lkp@intel.com/config)
>
>   CONFIG_DMI=y
>   
>   CONFIG_HWMON=y
>   
>   CONFIG_FW_ATTR_CLASS=m
>   
>   CONFIG_ACPI_BATTERY=m
>   
>   CONFIG_ACPI_PLATFORM_PROFILE=y
>   
>   CONFIG_ACPI_WMI=y
>   
>   CONFIG_LENOVO_WMI_CAPDATA=m
>   CONFIG_LENOVO_WMI_EVENTS=y
>   CONFIG_LENOVO_WMI_HELPERS=y
>   CONFIG_LENOVO_WMI_GAMEZONE=y
>   CONFIG_LENOVO_WMI_TUNING=y
>
>This isn't a legit config, as CONFIG_LENOVO_WMI_TUNING=y should have
>ensured CONFIG_LENOVO_WMI_CAPDATA=y, CONFIG_ACPI_BATTERY=y, and
>CONFIG_FW_ATTR_CLASS=y.
>
>> > compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
>> > reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260326/202603260302.X0NjQOda-lkp@intel.com/reproduce)
>> > 
>> > If you fix the issue in a separate patch/commit (i.e. not just a new version of
>> > the same patch/commit), kindly add following tags
>> > > Reported-by: kernel test robot <lkp@intel.com>
>> > > Closes: https://lore.kernel.org/oe-kbuild-all/202603260302.X0NjQOda-lkp@intel.com/
>> > 
>> > All errors (new ones prefixed by >>):
>> > 
>> >    ld: vmlinux.o: in function `lwmi_om_fan_get_set':
>> > > > drivers/platform/x86/lenovo/wmi-other.c:206:(.text+0x1b74542): undefined reference to `lwmi_attr_id'
>> >    ld: vmlinux.o: in function `lwmi_attr_01_is_supported':
>> >    drivers/platform/x86/lenovo/wmi-other.c:1267:(.text+0x1b74fdf): undefined reference to `lwmi_attr_id'
>> > > > ld: drivers/platform/x86/lenovo/wmi-other.c:1270:(.text+0x1b75013): undefined reference to `lwmi_cd01_get_data'
>> > > > ld: drivers/platform/x86/lenovo/wmi-other.c:1283:(.text+0x1b7515b): undefined reference to `lwmi_attr_id'
>> >    ld: drivers/platform/x86/lenovo/wmi-other.c:1299:(.text+0x1b752f4): undefined reference to `lwmi_attr_id'
>> >    ld: vmlinux.o: in function `lwmi_om_fan_info_collect_cd_fan':
>> > > > drivers/platform/x86/lenovo/wmi-other.c:565:(.text+0x1b75ca3): undefined reference to `lwmi_cd_fan_get_data'
>> >    ld: vmlinux.o: in function `lwmi_psy_prop_is_writeable':
>> >    drivers/platform/x86/lenovo/wmi-other.c:687:(.text+0x1b75e2f): undefined reference to `lwmi_attr_id'
>> > > > ld: drivers/platform/x86/lenovo/wmi-other.c:690:(.text+0x1b75e5a): undefined reference to `lwmi_cd00_get_data'
>> >    ld: vmlinux.o: in function `lwmi_psy_ext_set_prop':
>> >    drivers/platform/x86/lenovo/wmi-other.c:649:(.text+0x1b75f9b): undefined reference to `lwmi_attr_id'
>> >    ld: vmlinux.o: in function `lwmi_psy_ext_get_prop':
>> >    drivers/platform/x86/lenovo/wmi-other.c:603:(.text+0x1b761aa): undefined reference to `lwmi_attr_id'
>> >    ld: vmlinux.o: in function `lwmi_other_probe':
>> > > > drivers/platform/x86/lenovo/wmi-other.c:1612:(.text+0x1b764eb): undefined reference to `lwmi_cd_match_add_all'
>> >    ld: vmlinux.o: in function `lwmi_om_fan_info_collect_cd00':
>> >    drivers/platform/x86/lenovo/wmi-other.c:540:(.text+0x1b7680f): undefined reference to `lwmi_attr_id'
>> >    ld: drivers/platform/x86/lenovo/wmi-other.c:540:(.text+0x1b76838): undefined reference to `lwmi_cd00_get_data'
>> >    ld: vmlinux.o: in function `lwmi_om_ps_ext_init':
>> > > > drivers/platform/x86/lenovo/wmi-other.c:806:(.text+0x1b76a57): undefined reference to `devm_battery_hook_register'
>> >    ld: vmlinux.o: in function `lwmi_om_fw_attr_add':
>> > > > drivers/platform/x86/lenovo/wmi-other.c:1474:(.text+0x1b76afa): undefined reference to `firmware_attributes_class'
>> >    ld: vmlinux.o: in function `attr_current_value_show':
>> >    drivers/platform/x86/lenovo/wmi-other.c:1225:(.text+0x1b77015): undefined reference to `lwmi_attr_id'
>> >    ld: vmlinux.o: in function `attr_current_value_store':
>> >    drivers/platform/x86/lenovo/wmi-other.c:1165:(.text+0x1b776e2): undefined reference to `lwmi_attr_id'
>> >    ld: drivers/platform/x86/lenovo/wmi-other.c:1168:(.text+0x1b7771c): undefined reference to `lwmi_cd01_get_data'
>> >    ld: drivers/platform/x86/lenovo/wmi-other.c:1179:(.text+0x1b7781e): undefined reference to `lwmi_attr_id'
>> >    ld: vmlinux.o: in function `attr_capdata01_show':
>> >    drivers/platform/x86/lenovo/wmi-other.c:1099:(.text+0x1b77e38): undefined reference to `lwmi_attr_id'
>> >    ld: drivers/platform/x86/lenovo/wmi-other.c:1102:(.text+0x1b77e67): undefined reference to `lwmi_cd01_get_data'
>> > 
>> > Kconfig warnings: (for reference only)
>> >    WARNING: unmet direct dependencies detected for LENOVO_WMI_TUNING
>
>When randconfig selects symbols, some are selected by the randomizer,
>while some are selected by Kconfig `select SYMBOL'. In this lkp test,
>CONFIG_LENOVO_WMI_GAMEZONE=y was selected by the randomizer, while
>CONFIG_LENOVO_WMI_TUNING=y was selected by `select LENOVO_WMI_TUNING' in
>`config LENOVO_WMI_GAMEZONE'.
>
>Documentation/kbuild/kconfig-language.rst says "select will force a
>symbol to a value without visiting the dependencies", resulting in unmet
>dependencies in this test and
>https://lore.kernel.org/r/202603252259.gHvJDyh3-lkp@intel.com
>
>> >    Depends on [m]: X86_PLATFORM_DEVICES [=y] && ACPI_WMI [=y] && ACPI_BATTERY [=m]
>> >    Selected by [y]:
>> >    - LENOVO_WMI_GAMEZONE [=y] && X86_PLATFORM_DEVICES [=y] && ACPI_WMI [=y] && DMI [=y]
>> > 
>> > 
>> > vim +206 drivers/platform/x86/lenovo/wmi-other.c
>> > 
>> > 51ed34282f63fa Rong Zhang 2026-01-21  186
>> > 51ed34282f63fa Rong Zhang 2026-01-21  187  /**
>> > 51ed34282f63fa Rong Zhang 2026-01-21  188   * lwmi_om_fan_get_set() - Get or set fan RPM value of specified fan
>> > 51ed34282f63fa Rong Zhang 2026-01-21  189   * @priv: Driver private data structure
>> > 51ed34282f63fa Rong Zhang 2026-01-21  190   * @channel: Fan channel index (0-based)
>> > 51ed34282f63fa Rong Zhang 2026-01-21  191   * @val: Pointer to value (input for set, output for get)
>> > 51ed34282f63fa Rong Zhang 2026-01-21  192   * @set: True to set value, false to get value
>> > 51ed34282f63fa Rong Zhang 2026-01-21  193   *
>> > 51ed34282f63fa Rong Zhang 2026-01-21  194   * Communicates with WMI interface to either retrieve current fan RPM
>> > 51ed34282f63fa Rong Zhang 2026-01-21  195   * or set target fan RPM.
>> > 51ed34282f63fa Rong Zhang 2026-01-21  196   *
>> > 51ed34282f63fa Rong Zhang 2026-01-21  197   * Return: 0 on success, or an error code.
>> > 51ed34282f63fa Rong Zhang 2026-01-21  198   */
>> > 51ed34282f63fa Rong Zhang 2026-01-21  199  static int lwmi_om_fan_get_set(struct lwmi_om_priv *priv, int channel, u32 *val, bool set)
>> > 51ed34282f63fa Rong Zhang 2026-01-21  200  {
>> > 51ed34282f63fa Rong Zhang 2026-01-21  201       struct wmi_method_args_32 args;
>> > 51ed34282f63fa Rong Zhang 2026-01-21  202       u32 method_id, retval;
>> > 51ed34282f63fa Rong Zhang 2026-01-21  203       int err;
>> > 51ed34282f63fa Rong Zhang 2026-01-21  204
>> > 51ed34282f63fa Rong Zhang 2026-01-21  205       method_id = set ? LWMI_FEATURE_VALUE_SET : LWMI_FEATURE_VALUE_GET;
>> > 51ed34282f63fa Rong Zhang 2026-01-21 @206       args.arg0 = LWMI_ATTR_ID_FAN_RPM(channel);
>> > 51ed34282f63fa Rong Zhang 2026-01-21  207       args.arg1 = set ? *val : 0;
>> > 51ed34282f63fa Rong Zhang 2026-01-21  208
>> > 51ed34282f63fa Rong Zhang 2026-01-21  209       err = lwmi_dev_evaluate_int(priv->wdev, 0x0, method_id,
>> > 51ed34282f63fa Rong Zhang 2026-01-21  210                                   (unsigned char *)&args, sizeof(args), &retval);
>> > 51ed34282f63fa Rong Zhang 2026-01-21  211       if (err)
>> > 51ed34282f63fa Rong Zhang 2026-01-21  212               return err;
>> > 51ed34282f63fa Rong Zhang 2026-01-21  213
>> > 51ed34282f63fa Rong Zhang 2026-01-21  214       if (!set) {
>> > 51ed34282f63fa Rong Zhang 2026-01-21  215               *val = retval;
>> > 51ed34282f63fa Rong Zhang 2026-01-21  216               return 0;
>> > 51ed34282f63fa Rong Zhang 2026-01-21  217       }
>> > 51ed34282f63fa Rong Zhang 2026-01-21  218
>> > 51ed34282f63fa Rong Zhang 2026-01-21  219       /*
>> > 51ed34282f63fa Rong Zhang 2026-01-21  220        * It seems that 0 means "no error" and 1 means "done". Apparently
>> > 51ed34282f63fa Rong Zhang 2026-01-21  221        * different firmware teams have different thoughts on indicating
>> > 51ed34282f63fa Rong Zhang 2026-01-21  222        * success, so we accepts both.
>> > 51ed34282f63fa Rong Zhang 2026-01-21  223        */
>> > 51ed34282f63fa Rong Zhang 2026-01-21  224       return (retval == 0 || retval == 1) ? 0 : -EIO;
>> > 51ed34282f63fa Rong Zhang 2026-01-21  225  }
>> > 51ed34282f63fa Rong Zhang 2026-01-21  226
>> > 
>> > --
>> > 0-DAY CI Kernel Test Service
>> > https://github.com/intel/lkp-tests/wiki
>> 
>> Can someone explain to me why this is happening? I cannot reproduce
>> these locally with W=1 and/or C=1. I'd prefer to resolve this before
>> submitting a v6.
>
>The issue exists since commit 6e38b9fcbfa3 ('platform/x86: lenovo:
>gamezone needs "other mode"'). It might be a coincidence that lkp didn't
>running into a similar issue before. IMO `select LENOVO_WMI_TUNING'
>shouldn't be used in the first place since LENOVO_WMI_TUNING has
>numerous dependencies.
>
>To fix this, we need to decouple lenovo-wmi-gamezone and lenovo-wmi-
>other by moving the notifier chain into lenovo-wmi-helpers. I will reply
>with a patch soon. Fell free to integrate it into your series.
>
>Note that I couldn't completely test my patch since the lack of gamezone
>support on my device.
>
>BTW, the fix patch has conflicts with my debugfs series since both touch
>lenovo-wmi-helpers. If you decide to integrate the fix patch into your
>series, could you integrate the debugfs series into yours too? The
>conflicts are trivial and contextual. You can resolve them yourself if
>you don't mind it ;-)
>
>Thanks,
>Rong
>

Absolutely, that's a good plan. If you want, send the new patch to me directly. Also, please leave a note on that other series in case Ilpo doesn't see this first.

And thanks for the clear explanation. Hopefully it all works like we expect.

Cheers,
Derek

>> 
>> Thanks,
>> Derek
[PATCH] platform/x86: lenovo: Decouple lenovo-wmi-gamezone and lenovo-wmi-other
Posted by Rong Zhang 1 week ago
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>
---
 drivers/platform/x86/lenovo/Kconfig        |   1 -
 drivers/platform/x86/lenovo/wmi-gamezone.c |   4 +-
 drivers/platform/x86/lenovo/wmi-helpers.c  | 102 ++++++++++++++++++++
 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, 113 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 f885127b007f..09b1b055d2e0 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 c7fe7e3c9f17..92020225db27 100644
--- a/drivers/platform/x86/lenovo/wmi-gamezone.c
+++ b/drivers/platform/x86/lenovo/wmi-gamezone.c
@@ -23,7 +23,6 @@
 #include "wmi-events.h"
 #include "wmi-gamezone.h"
 #include "wmi-helpers.h"
-#include "wmi-other.h"
 
 #define LENOVO_GAMEZONE_GUID "887B54E3-DDDC-4B2C-8B88-68A26A8835D0"
 
@@ -383,7 +382,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[] = {
@@ -405,7 +404,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..5a88bccb5037 100644
--- a/drivers/platform/x86/lenovo/wmi-helpers.c
+++ b/drivers/platform/x86/lenovo/wmi-helpers.c
@@ -21,11 +21,16 @@
 #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-gamezone.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 +89,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 20fd21749803..651a039228ed 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 {
@@ -17,4 +19,10 @@ struct wmi_method_args_32 {
 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 6040f45aa2b0..b394cc8168a6 100644
--- a/drivers/platform/x86/lenovo/wmi-other.c
+++ b/drivers/platform/x86/lenovo/wmi-other.c
@@ -40,7 +40,6 @@
 #include <linux/kobject.h>
 #include <linux/limits.h>
 #include <linux/module.h>
-#include <linux/notifier.h>
 #include <linux/platform_profile.h>
 #include <linux/types.h>
 #include <linux/wmi.h>
@@ -49,7 +48,6 @@
 #include "wmi-events.h"
 #include "wmi-gamezone.h"
 #include "wmi-helpers.h"
-#include "wmi-other.h"
 #include "../firmware_attributes_class.h"
 
 #define LENOVO_OTHER_MODE_GUID "DC2A8805-3A8C-41BA-A6F7-092E0089CD3B"
@@ -81,7 +79,6 @@
 #define LWMI_OM_FW_ATTR_BASE_PATH "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 {
@@ -109,7 +106,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;
 
@@ -576,102 +572,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 */
 
 /**
@@ -780,7 +680,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;
 
@@ -842,7 +742,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_ */

base-commit: 0138af2472dfdef0d56fc4697416eaa0ff2589bd
-- 
2.53.0
Re: [PATCH] platform/x86: lenovo: Decouple lenovo-wmi-gamezone and lenovo-wmi-other
Posted by Ilpo Järvinen 6 days, 15 hours ago
On Fri, 27 Mar 2026, Rong Zhang wrote:

> 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>
> ---
>  drivers/platform/x86/lenovo/Kconfig        |   1 -
>  drivers/platform/x86/lenovo/wmi-gamezone.c |   4 +-
>  drivers/platform/x86/lenovo/wmi-helpers.c  | 102 ++++++++++++++++++++
>  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, 113 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 f885127b007f..09b1b055d2e0 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 c7fe7e3c9f17..92020225db27 100644
> --- a/drivers/platform/x86/lenovo/wmi-gamezone.c
> +++ b/drivers/platform/x86/lenovo/wmi-gamezone.c
> @@ -23,7 +23,6 @@
>  #include "wmi-events.h"
>  #include "wmi-gamezone.h"
>  #include "wmi-helpers.h"
> -#include "wmi-other.h"
>  
>  #define LENOVO_GAMEZONE_GUID "887B54E3-DDDC-4B2C-8B88-68A26A8835D0"
>  
> @@ -383,7 +382,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[] = {
> @@ -405,7 +404,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..5a88bccb5037 100644
> --- a/drivers/platform/x86/lenovo/wmi-helpers.c
> +++ b/drivers/platform/x86/lenovo/wmi-helpers.c
> @@ -21,11 +21,16 @@
>  #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-gamezone.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 +89,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 20fd21749803..651a039228ed 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 {
> @@ -17,4 +19,10 @@ struct wmi_method_args_32 {
>  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);

This enum is not introduced earlier within this header?

-- 
 i.

> +
>  #endif /* !_LENOVO_WMI_HELPERS_H_ */
> diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c
> index 6040f45aa2b0..b394cc8168a6 100644
> --- a/drivers/platform/x86/lenovo/wmi-other.c
> +++ b/drivers/platform/x86/lenovo/wmi-other.c
> @@ -40,7 +40,6 @@
>  #include <linux/kobject.h>
>  #include <linux/limits.h>
>  #include <linux/module.h>
> -#include <linux/notifier.h>
>  #include <linux/platform_profile.h>
>  #include <linux/types.h>
>  #include <linux/wmi.h>
> @@ -49,7 +48,6 @@
>  #include "wmi-events.h"
>  #include "wmi-gamezone.h"
>  #include "wmi-helpers.h"
> -#include "wmi-other.h"
>  #include "../firmware_attributes_class.h"
>  
>  #define LENOVO_OTHER_MODE_GUID "DC2A8805-3A8C-41BA-A6F7-092E0089CD3B"
> @@ -81,7 +79,6 @@
>  #define LWMI_OM_FW_ATTR_BASE_PATH "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 {
> @@ -109,7 +106,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;
>  
> @@ -576,102 +572,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 */
>  
>  /**
> @@ -780,7 +680,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;
>  
> @@ -842,7 +742,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_ */
> 
> base-commit: 0138af2472dfdef0d56fc4697416eaa0ff2589bd
>
Re: [PATCH] platform/x86: lenovo: Decouple lenovo-wmi-gamezone and lenovo-wmi-other
Posted by Rong Zhang 6 days, 11 hours ago
Hi Ilpo,

On Fri, 2026-03-27 at 13:25 +0200, Ilpo Järvinen wrote:
> On Fri, 27 Mar 2026, Rong Zhang wrote:
> 
> > 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>
> > ---
> >  drivers/platform/x86/lenovo/Kconfig        |   1 -
> >  drivers/platform/x86/lenovo/wmi-gamezone.c |   4 +-
> >  drivers/platform/x86/lenovo/wmi-helpers.c  | 102 ++++++++++++++++++++
> >  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, 113 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 f885127b007f..09b1b055d2e0 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 c7fe7e3c9f17..92020225db27 100644
> > --- a/drivers/platform/x86/lenovo/wmi-gamezone.c
> > +++ b/drivers/platform/x86/lenovo/wmi-gamezone.c
> > @@ -23,7 +23,6 @@
> >  #include "wmi-events.h"
> >  #include "wmi-gamezone.h"
> >  #include "wmi-helpers.h"
> > -#include "wmi-other.h"
> >  
> >  #define LENOVO_GAMEZONE_GUID "887B54E3-DDDC-4B2C-8B88-68A26A8835D0"
> >  
> > @@ -383,7 +382,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[] = {
> > @@ -405,7 +404,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..5a88bccb5037 100644
> > --- a/drivers/platform/x86/lenovo/wmi-helpers.c
> > +++ b/drivers/platform/x86/lenovo/wmi-helpers.c
> > @@ -21,11 +21,16 @@
> >  #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-gamezone.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 +89,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 20fd21749803..651a039228ed 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 {
> > @@ -17,4 +19,10 @@ struct wmi_method_args_32 {
> >  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);
> 
> This enum is not introduced earlier within this header?

Hmm, no. Declaring a opaque enum earlier should be enough to fix it, as
wmi-gamezone.h shouldn't be included here. Derek, what do you think?

Thanks,
Rong
Re: [PATCH] platform/x86: lenovo: Decouple lenovo-wmi-gamezone and lenovo-wmi-other
Posted by Derek John Clark 3 days, 6 hours ago
On Fri, Mar 27, 2026 at 8:21 AM Rong Zhang <i@rong.moe> wrote:
>
> Hi Ilpo,
>
> On Fri, 2026-03-27 at 13:25 +0200, Ilpo Järvinen wrote:
> > On Fri, 27 Mar 2026, Rong Zhang wrote:
> >
> > > 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>
> > > ---
> > >  drivers/platform/x86/lenovo/Kconfig        |   1 -
> > >  drivers/platform/x86/lenovo/wmi-gamezone.c |   4 +-
> > >  drivers/platform/x86/lenovo/wmi-helpers.c  | 102 ++++++++++++++++++++
> > >  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, 113 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 f885127b007f..09b1b055d2e0 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 c7fe7e3c9f17..92020225db27 100644
> > > --- a/drivers/platform/x86/lenovo/wmi-gamezone.c
> > > +++ b/drivers/platform/x86/lenovo/wmi-gamezone.c
> > > @@ -23,7 +23,6 @@
> > >  #include "wmi-events.h"
> > >  #include "wmi-gamezone.h"
> > >  #include "wmi-helpers.h"
> > > -#include "wmi-other.h"
> > >
> > >  #define LENOVO_GAMEZONE_GUID "887B54E3-DDDC-4B2C-8B88-68A26A8835D0"
> > >
> > > @@ -383,7 +382,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[] = {
> > > @@ -405,7 +404,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..5a88bccb5037 100644
> > > --- a/drivers/platform/x86/lenovo/wmi-helpers.c
> > > +++ b/drivers/platform/x86/lenovo/wmi-helpers.c
> > > @@ -21,11 +21,16 @@
> > >  #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-gamezone.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 +89,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 20fd21749803..651a039228ed 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 {
> > > @@ -17,4 +19,10 @@ struct wmi_method_args_32 {
> > >  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);
> >
> > This enum is not introduced earlier within this header?
>
> Hmm, no. Declaring a opaque enum earlier should be enough to fix it, as
> wmi-gamezone.h shouldn't be included here. Derek, what do you think?

I think it makes more sense at this point to move everything from
wmi_gamezone.h into wmi_helpers.h. This prevents wmi_capdata.c from
needing to import wmi_gamezone.h, since the thermal mode enum will be
moved (now used by capdata, gamezone, and other). Then the only thing
in the gamezone header would be the gamezone_events_type enum, which
is only used by gamezone and helpers anyway, so that can safely move
and we can delete the entire file. To make that enum name more generic
I'll rename it in the move from gamezone_events_type to
lwmi_event_type (the enum isn't directly referenced anywhere anyway).

If that works for everyone I'll add one more patch to do the move &
cleanup before  "platform/x86: lenovo-wmi-other: Add lwmi_attr_id()
function" where I'm adding the .._NONE thermal mode. That will keep
the purpose of each patch clean and avoid me needing to modify your
patches too much.

Thanks,
Derek.

> Thanks,
> Rong
Re: [PATCH] platform/x86: lenovo: Decouple lenovo-wmi-gamezone and lenovo-wmi-other
Posted by Rong Zhang 2 days, 10 hours ago
Hi Derek,

On Mon, 2026-03-30 at 13:04 -0700, Derek John Clark wrote:
> On Fri, Mar 27, 2026 at 8:21 AM Rong Zhang <i@rong.moe> wrote:
> > 
> > Hi Ilpo,
> > 
> > On Fri, 2026-03-27 at 13:25 +0200, Ilpo Järvinen wrote:
> > > On Fri, 27 Mar 2026, Rong Zhang wrote:
> > > 
> > > > 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>
> > > > ---
> > > >  drivers/platform/x86/lenovo/Kconfig        |   1 -
> > > >  drivers/platform/x86/lenovo/wmi-gamezone.c |   4 +-
> > > >  drivers/platform/x86/lenovo/wmi-helpers.c  | 102 ++++++++++++++++++++
> > > >  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, 113 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 f885127b007f..09b1b055d2e0 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 c7fe7e3c9f17..92020225db27 100644
> > > > --- a/drivers/platform/x86/lenovo/wmi-gamezone.c
> > > > +++ b/drivers/platform/x86/lenovo/wmi-gamezone.c
> > > > @@ -23,7 +23,6 @@
> > > >  #include "wmi-events.h"
> > > >  #include "wmi-gamezone.h"
> > > >  #include "wmi-helpers.h"
> > > > -#include "wmi-other.h"
> > > > 
> > > >  #define LENOVO_GAMEZONE_GUID "887B54E3-DDDC-4B2C-8B88-68A26A8835D0"
> > > > 
> > > > @@ -383,7 +382,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[] = {
> > > > @@ -405,7 +404,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..5a88bccb5037 100644
> > > > --- a/drivers/platform/x86/lenovo/wmi-helpers.c
> > > > +++ b/drivers/platform/x86/lenovo/wmi-helpers.c
> > > > @@ -21,11 +21,16 @@
> > > >  #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-gamezone.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 +89,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 20fd21749803..651a039228ed 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 {
> > > > @@ -17,4 +19,10 @@ struct wmi_method_args_32 {
> > > >  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);
> > > 
> > > This enum is not introduced earlier within this header?
> > 
> > Hmm, no. Declaring a opaque enum earlier should be enough to fix it, as
> > wmi-gamezone.h shouldn't be included here. Derek, what do you think?
> 
> I think it makes more sense at this point to move everything from
> wmi_gamezone.h into wmi_helpers.h. This prevents wmi_capdata.c from
> needing to import wmi_gamezone.h, since the thermal mode enum will be
> moved (now used by capdata, gamezone, and other). Then the only thing
> in the gamezone header would be the gamezone_events_type enum, which
> is only used by gamezone and helpers anyway, so that can safely move
> and we can delete the entire file. To make that enum name more generic
> I'll rename it in the move from gamezone_events_type to
> lwmi_event_type (the enum isn't directly referenced anywhere anyway).
> 
> If that works for everyone I'll add one more patch to do the move &
> cleanup before  "platform/x86: lenovo-wmi-other: Add lwmi_attr_id()
> function" where I'm adding the .._NONE thermal mode. That will keep
> the purpose of each patch clean and avoid me needing to modify your
> patches too much.

That sounds good to me.

Moving everything from wmi_gamezone.h into wmi_helpers.h also seems more
semantically correct from my perspective, since the former was just used
to glue things together, which instead matches the purpose of the
latter.

Thanks,
Rong

> 
> Thanks,
> Derek.
> 
> > Thanks,
> > Rong
Re: [PATCH v5 8/8] platform/x86: lenovo-wmi-other: Add WMI battery charge limiting
Posted by Rong Zhang 1 week, 1 day ago
Hi Derek,

On Tue, 2026-03-24 at 22:10 +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: Mark Pearson <mpearson-lenovo@squebb.ca>
> Signed-off-by: Derek J. Clark <derekjohn.clark@gmail.com>
> ---
> 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   | 250 +++++++++++++++++++++-
>  3 files changed, 251 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 b026ee30c828..1939401c6c14 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_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 11c16857ef97..cef51cded7be 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-gamezone.h"
> @@ -79,9 +82,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
> @@ -92,10 +97,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"
>  
> @@ -137,6 +149,8 @@ struct lwmi_om_priv {
>  		bool capdata00_collected : 1;
>  		bool capdata_fan_collected : 1;
>  	} fan_flags;
> +
> +	struct acpi_battery_hook battery_hook;
>  };
>  
>  /*
> @@ -561,6 +575,239 @@ 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 lwmi_om_priv *priv = ext_data;
> +	struct wmi_method_args_32 args;

Zero-initialize `args'. See my previous reply.

> +	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);

Both should be `%#x'.

> +
> +	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 lwmi_om_priv *priv = ext_data;
> +	struct wmi_method_args_32 args;
> +
> +	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 %#10x for property %#x to %#x\n",
> +		args.arg0, prop, args.arg1);

%#010x

> +
> +	return lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_SET,
> +				     (unsigned char *)&args, sizeof(args), NULL);
> +}
> +
> +/**
> + * lwmi_psy_prop_is_writeable() - Determine if the property is supported
> + * @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 supported.
> + *
> + * Return: Support level, 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)
> +{
> +	struct lwmi_om_priv *priv = ext_data;
> +	struct capdata00 capdata;
> +	u32 attribute_id = LWMI_ATTR_ID_PSU(LWMI_FEATURE_ID_PSU_CHARGE_TYPE, LWMI_TYPE_ID_PSU_AC);
> +	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 (%#10x) support level: %#x\n",
> +		attribute_id, capdata.supported);

%#010x

> +
> +	return capdata.supported;

This casts u32 into a *signed* int. I'd suggest:

        return !!(capdata.supported & LWMI_SUPP_SET);

LWMI_SUPP_VALID and LWMI_SUPP_GET should also be checked before
registering the power supply extension. See below.

> +}
> +
> +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_ps_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_ps_ext_init(struct lwmi_om_priv *priv)

Nitpick: rename it to lwmi_om_psy_ext_init (note the "y") to make naming
consistent.

> +{
> +	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;

Query capdata00 and check against (capdata.supported & LWMI_SUPP_VALID)
&& (capdata.supported & LWMI_SUPP_GET) before continuing. If it's
unsupported or unreadable, no need to register a bogus power supply
extension. force_load_psy_ext should be able to override this too.

> +
> +	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");

Drop the period at the end of the string.

Thanks,
Rong

> +		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 {
> @@ -1318,6 +1565,7 @@ static int lwmi_om_master_bind(struct device *dev)
>  		return -ENODEV;
>  
>  	lwmi_om_fan_info_collect_cd00(priv);
> +	lwmi_om_ps_ext_init(priv);
>  
>  	return lwmi_om_fw_attr_add(priv);
>  }
Re: [PATCH v5 8/8] platform/x86: lenovo-wmi-other: Add WMI battery charge limiting
Posted by kernel test robot 1 week, 1 day ago
Hi Derek,

kernel test robot noticed the following build errors:

[auto build test ERROR on linus/master]
[also build test ERROR on v7.0-rc5 next-20260324]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Derek-J-Clark/platform-x86-lenovo-wmi-other-Move-LWMI_FAN_DIV/20260325-070851
base:   linus/master
patch link:    https://lore.kernel.org/r/20260324221032.1333636-9-derekjohn.clark%40gmail.com
patch subject: [PATCH v5 8/8] platform/x86: lenovo-wmi-other: Add WMI battery charge limiting
config: i386-buildonly-randconfig-006-20260325 (https://download.01.org/0day-ci/archive/20260325/202603252259.gHvJDyh3-lkp@intel.com/config)
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260325/202603252259.gHvJDyh3-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202603252259.gHvJDyh3-lkp@intel.com/

All errors (new ones prefixed by >>, old ones prefixed by <<):

>> ERROR: modpost: "lwmi_cd_match_add_all" [drivers/platform/x86/lenovo/lenovo-wmi-other.ko] undefined!
>> ERROR: modpost: "lwmi_attr_id" [drivers/platform/x86/lenovo/lenovo-wmi-other.ko] undefined!
>> ERROR: modpost: "lwmi_cd00_get_data" [drivers/platform/x86/lenovo/lenovo-wmi-other.ko] undefined!
ERROR: modpost: "devm_battery_hook_register" [drivers/platform/x86/lenovo/lenovo-wmi-other.ko] undefined!
>> ERROR: modpost: "firmware_attributes_class" [drivers/platform/x86/lenovo/lenovo-wmi-other.ko] undefined!
>> ERROR: modpost: "lwmi_cd01_get_data" [drivers/platform/x86/lenovo/lenovo-wmi-other.ko] undefined!
>> ERROR: modpost: "lwmi_cd_fan_get_data" [drivers/platform/x86/lenovo/lenovo-wmi-other.ko] undefined!

Kconfig warnings: (for reference only)
   WARNING: unmet direct dependencies detected for LENOVO_WMI_TUNING
   Depends on [n]: X86_PLATFORM_DEVICES [=y] && ACPI_WMI [=y] && ACPI_BATTERY [=n]
   Selected by [m]:
   - LENOVO_WMI_GAMEZONE [=m] && X86_PLATFORM_DEVICES [=y] && ACPI_WMI [=y] && DMI [=y]

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki