* [PATCH v2 0/4] platform/x86: ideapad-laptop: Add charge_types:Fast (Rapid Charge)
@ 2025-11-05 18:28 Rong Zhang
2025-11-05 18:28 ` [PATCH v2 1/4] platform/x86: ideapad-laptop: Use str_on_off() helper Rong Zhang
` (4 more replies)
0 siblings, 5 replies; 7+ messages in thread
From: Rong Zhang @ 2025-11-05 18:28 UTC (permalink / raw)
To: Ike Panhc, Mark Pearson, Derek J. Clark, Hans de Goede,
Ilpo Järvinen
Cc: Rong Zhang, Jelle van der Waa, platform-driver-x86, linux-kernel
The GBMD/SBMC interface on recent devices supports Rapid Charge mode
(charge_types: Fast) in addition to Conservation Mode (charge_types:
Long_Life).
Query the GBMD interface on probe to determine if a device supports
Rapid Charge. If so, expose these two modes while carefully maintaining
their mutually exclusive state, which aligns with the behavior of
manufacturer utilities on Windows.
Tested on ThinkBook 14 G7+ ASP.
Changes in v2:
- Only expose Rapid Charge on devices that support it (thanks Jelle van
der Waa)
- Support multiple power_supply_ext definitions to implement the change
above
- Use str_on_off() helper (thanks Ilpo Järvinen)
- Link to v1: https://lore.kernel.org/r/20251020192443.33088-1-i@rong.moe/
Rong Zhang (4):
platform/x86: ideapad-laptop: Use str_on_off() helper
platform/x86: ideapad-laptop: Protect GBMD/SBMC calls with mutex
platform/x86: ideapad-laptop: Support multiple power_supply_ext
definitions
platform/x86: ideapad-laptop: Add charge_types:Fast (Rapid Charge)
drivers/platform/x86/lenovo/ideapad-laptop.c | 218 ++++++++++++++-----
1 file changed, 162 insertions(+), 56 deletions(-)
base-commit: 1c353dc8d962de652bc7ad2ba2e63f553331391c
--
2.51.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 1/4] platform/x86: ideapad-laptop: Use str_on_off() helper
2025-11-05 18:28 [PATCH v2 0/4] platform/x86: ideapad-laptop: Add charge_types:Fast (Rapid Charge) Rong Zhang
@ 2025-11-05 18:28 ` Rong Zhang
2025-11-05 18:28 ` [PATCH v2 2/4] platform/x86: ideapad-laptop: Protect GBMD/SBMC calls with mutex Rong Zhang
` (3 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Rong Zhang @ 2025-11-05 18:28 UTC (permalink / raw)
To: Ike Panhc, Mark Pearson, Derek J. Clark, Hans de Goede,
Ilpo Järvinen
Cc: Rong Zhang, Jelle van der Waa, platform-driver-x86, linux-kernel,
kernel test robot, Julia Lawall
Use the str_on_off() helper instead of open-coding the same operation.
This improves code readability.
No functional change intended.
Reported-by: kernel test robot <lkp@intel.com>
Reported-by: Julia Lawall <julia.lawall@inria.fr>
Closes: https://lore.kernel.org/r/202510311551.xjWbHTrm-lkp@intel.com/
Suggested-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Link: https://lore.kernel.org/r/2bae2ea7-2ef9-0cfa-0c2c-39a7043b2aa5@linux.intel.com/
Signed-off-by: Rong Zhang <i@rong.moe>
---
Changes in v2:
- Include a seperated patch into this series (thanks Ilpo Järvinen)
- Fix missing include (ditto)
- Reword commit message
- Link to previous patch: https://lore.kernel.org/r/20251031184435.44819-1-i@rong.moe/
---
drivers/platform/x86/lenovo/ideapad-laptop.c | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/drivers/platform/x86/lenovo/ideapad-laptop.c b/drivers/platform/x86/lenovo/ideapad-laptop.c
index fcebfbaf0460..b61ed6993990 100644
--- a/drivers/platform/x86/lenovo/ideapad-laptop.c
+++ b/drivers/platform/x86/lenovo/ideapad-laptop.c
@@ -31,6 +31,7 @@
#include <linux/power_supply.h>
#include <linux/rfkill.h>
#include <linux/seq_file.h>
+#include <linux/string_choices.h>
#include <linux/sysfs.h>
#include <linux/types.h>
#include <linux/wmi.h>
@@ -462,25 +463,25 @@ static int debugfs_status_show(struct seq_file *s, void *data)
if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL, &value))
seq_printf(s, "Backlight now: %lu\n", value);
if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &value))
- seq_printf(s, "BL power value: %s (%lu)\n", value ? "on" : "off", value);
+ seq_printf(s, "BL power value: %s (%lu)\n", str_on_off(value), value);
seq_puts(s, "=====================\n");
if (!read_ec_data(priv->adev->handle, VPCCMD_R_RF, &value))
- seq_printf(s, "Radio status: %s (%lu)\n", value ? "on" : "off", value);
+ seq_printf(s, "Radio status: %s (%lu)\n", str_on_off(value), value);
if (!read_ec_data(priv->adev->handle, VPCCMD_R_WIFI, &value))
- seq_printf(s, "Wifi status: %s (%lu)\n", value ? "on" : "off", value);
+ seq_printf(s, "Wifi status: %s (%lu)\n", str_on_off(value), value);
if (!read_ec_data(priv->adev->handle, VPCCMD_R_BT, &value))
- seq_printf(s, "BT status: %s (%lu)\n", value ? "on" : "off", value);
+ seq_printf(s, "BT status: %s (%lu)\n", str_on_off(value), value);
if (!read_ec_data(priv->adev->handle, VPCCMD_R_3G, &value))
- seq_printf(s, "3G status: %s (%lu)\n", value ? "on" : "off", value);
+ seq_printf(s, "3G status: %s (%lu)\n", str_on_off(value), value);
seq_puts(s, "=====================\n");
if (!read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value))
- seq_printf(s, "Touchpad status: %s (%lu)\n", value ? "on" : "off", value);
+ seq_printf(s, "Touchpad status: %s (%lu)\n", str_on_off(value), value);
if (!read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &value))
- seq_printf(s, "Camera status: %s (%lu)\n", value ? "on" : "off", value);
+ seq_printf(s, "Camera status: %s (%lu)\n", str_on_off(value), value);
seq_puts(s, "=====================\n");
--
2.51.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 2/4] platform/x86: ideapad-laptop: Protect GBMD/SBMC calls with mutex
2025-11-05 18:28 [PATCH v2 0/4] platform/x86: ideapad-laptop: Add charge_types:Fast (Rapid Charge) Rong Zhang
2025-11-05 18:28 ` [PATCH v2 1/4] platform/x86: ideapad-laptop: Use str_on_off() helper Rong Zhang
@ 2025-11-05 18:28 ` Rong Zhang
2025-11-05 18:28 ` [PATCH v2 3/4] platform/x86: ideapad-laptop: Support multiple power_supply_ext definitions Rong Zhang
` (2 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Rong Zhang @ 2025-11-05 18:28 UTC (permalink / raw)
To: Ike Panhc, Mark Pearson, Derek J. Clark, Hans de Goede,
Ilpo Järvinen
Cc: Rong Zhang, Jelle van der Waa, platform-driver-x86, linux-kernel
The upcoming changes for Rapid Charge support require two consecutive
SBMC calls to switch charge_types. Hence, a mutex is required.
No functional change intended.
Signed-off-by: Rong Zhang <i@rong.moe>
Acked-by: Ike Panhc <ikepanhc@gmail.com>
---
Changes in v2:
- Rebase on top of the newly added [PATCH v2 1/4]
- Reword commit message
---
drivers/platform/x86/lenovo/ideapad-laptop.c | 91 ++++++++++++--------
1 file changed, 56 insertions(+), 35 deletions(-)
diff --git a/drivers/platform/x86/lenovo/ideapad-laptop.c b/drivers/platform/x86/lenovo/ideapad-laptop.c
index b61ed6993990..141de6335740 100644
--- a/drivers/platform/x86/lenovo/ideapad-laptop.c
+++ b/drivers/platform/x86/lenovo/ideapad-laptop.c
@@ -159,6 +159,7 @@ struct ideapad_rfk_priv {
struct ideapad_private {
struct acpi_device *adev;
struct mutex vpc_mutex; /* protects the VPC calls */
+ struct mutex gbmd_sbmc_mutex; /* protects GBMD/SBMC calls */
struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM];
struct ideapad_rfk_priv rfk_priv[IDEAPAD_RFKILL_DEV_NUM];
struct platform_device *platform_device;
@@ -456,37 +457,40 @@ static int debugfs_status_show(struct seq_file *s, void *data)
struct ideapad_private *priv = s->private;
unsigned long value;
- guard(mutex)(&priv->vpc_mutex);
-
- if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_MAX, &value))
- seq_printf(s, "Backlight max: %lu\n", value);
- if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL, &value))
- seq_printf(s, "Backlight now: %lu\n", value);
- if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &value))
- seq_printf(s, "BL power value: %s (%lu)\n", str_on_off(value), value);
-
- seq_puts(s, "=====================\n");
-
- if (!read_ec_data(priv->adev->handle, VPCCMD_R_RF, &value))
- seq_printf(s, "Radio status: %s (%lu)\n", str_on_off(value), value);
- if (!read_ec_data(priv->adev->handle, VPCCMD_R_WIFI, &value))
- seq_printf(s, "Wifi status: %s (%lu)\n", str_on_off(value), value);
- if (!read_ec_data(priv->adev->handle, VPCCMD_R_BT, &value))
- seq_printf(s, "BT status: %s (%lu)\n", str_on_off(value), value);
- if (!read_ec_data(priv->adev->handle, VPCCMD_R_3G, &value))
- seq_printf(s, "3G status: %s (%lu)\n", str_on_off(value), value);
+ scoped_guard(mutex, &priv->vpc_mutex) {
+ if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_MAX, &value))
+ seq_printf(s, "Backlight max: %lu\n", value);
+ if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL, &value))
+ seq_printf(s, "Backlight now: %lu\n", value);
+ if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &value))
+ seq_printf(s, "BL power value: %s (%lu)\n", str_on_off(value), value);
+
+ seq_puts(s, "=====================\n");
+
+ if (!read_ec_data(priv->adev->handle, VPCCMD_R_RF, &value))
+ seq_printf(s, "Radio status: %s (%lu)\n", str_on_off(value), value);
+ if (!read_ec_data(priv->adev->handle, VPCCMD_R_WIFI, &value))
+ seq_printf(s, "Wifi status: %s (%lu)\n", str_on_off(value), value);
+ if (!read_ec_data(priv->adev->handle, VPCCMD_R_BT, &value))
+ seq_printf(s, "BT status: %s (%lu)\n", str_on_off(value), value);
+ if (!read_ec_data(priv->adev->handle, VPCCMD_R_3G, &value))
+ seq_printf(s, "3G status: %s (%lu)\n", str_on_off(value), value);
+
+ seq_puts(s, "=====================\n");
+
+ if (!read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value))
+ seq_printf(s, "Touchpad status: %s (%lu)\n", str_on_off(value), value);
+ if (!read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &value))
+ seq_printf(s, "Camera status: %s (%lu)\n", str_on_off(value), value);
+ }
seq_puts(s, "=====================\n");
- if (!read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value))
- seq_printf(s, "Touchpad status: %s (%lu)\n", str_on_off(value), value);
- if (!read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &value))
- seq_printf(s, "Camera status: %s (%lu)\n", str_on_off(value), value);
-
- seq_puts(s, "=====================\n");
+ scoped_guard(mutex, &priv->gbmd_sbmc_mutex) {
+ if (!eval_gbmd(priv->adev->handle, &value))
+ seq_printf(s, "GBMD: %#010lx\n", value);
+ }
- if (!eval_gbmd(priv->adev->handle, &value))
- seq_printf(s, "GBMD: %#010lx\n", value);
if (!eval_hals(priv->adev->handle, &value))
seq_printf(s, "HALS: %#010lx\n", value);
@@ -623,9 +627,11 @@ static ssize_t conservation_mode_show(struct device *dev,
show_conservation_mode_deprecation_warning(dev);
- err = eval_gbmd(priv->adev->handle, &result);
- if (err)
- return err;
+ scoped_guard(mutex, &priv->gbmd_sbmc_mutex) {
+ err = eval_gbmd(priv->adev->handle, &result);
+ if (err)
+ return err;
+ }
return sysfs_emit(buf, "%d\n", !!test_bit(GBMD_CONSERVATION_STATE_BIT, &result));
}
@@ -644,6 +650,8 @@ static ssize_t conservation_mode_store(struct device *dev,
if (err)
return err;
+ guard(mutex)(&priv->gbmd_sbmc_mutex);
+
err = exec_sbmc(priv->adev->handle, state ? SBMC_CONSERVATION_ON : SBMC_CONSERVATION_OFF);
if (err)
return err;
@@ -2008,15 +2016,22 @@ static int ideapad_psy_ext_set_prop(struct power_supply *psy,
const union power_supply_propval *val)
{
struct ideapad_private *priv = ext_data;
+ unsigned long op;
switch (val->intval) {
case POWER_SUPPLY_CHARGE_TYPE_LONGLIFE:
- return exec_sbmc(priv->adev->handle, SBMC_CONSERVATION_ON);
+ op = SBMC_CONSERVATION_ON;
+ break;
case POWER_SUPPLY_CHARGE_TYPE_STANDARD:
- return exec_sbmc(priv->adev->handle, SBMC_CONSERVATION_OFF);
+ op = SBMC_CONSERVATION_OFF;
+ break;
default:
return -EINVAL;
}
+
+ guard(mutex)(&priv->gbmd_sbmc_mutex);
+
+ return exec_sbmc(priv->adev->handle, op);
}
static int ideapad_psy_ext_get_prop(struct power_supply *psy,
@@ -2029,9 +2044,11 @@ static int ideapad_psy_ext_get_prop(struct power_supply *psy,
unsigned long result;
int err;
- err = eval_gbmd(priv->adev->handle, &result);
- if (err)
- return err;
+ scoped_guard(mutex, &priv->gbmd_sbmc_mutex) {
+ err = eval_gbmd(priv->adev->handle, &result);
+ if (err)
+ return err;
+ }
if (test_bit(GBMD_CONSERVATION_STATE_BIT, &result))
val->intval = POWER_SUPPLY_CHARGE_TYPE_LONGLIFE;
@@ -2293,6 +2310,10 @@ static int ideapad_acpi_add(struct platform_device *pdev)
if (err)
return err;
+ err = devm_mutex_init(&pdev->dev, &priv->gbmd_sbmc_mutex);
+ if (err)
+ return err;
+
err = ideapad_check_features(priv);
if (err)
return err;
--
2.51.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 3/4] platform/x86: ideapad-laptop: Support multiple power_supply_ext definitions
2025-11-05 18:28 [PATCH v2 0/4] platform/x86: ideapad-laptop: Add charge_types:Fast (Rapid Charge) Rong Zhang
2025-11-05 18:28 ` [PATCH v2 1/4] platform/x86: ideapad-laptop: Use str_on_off() helper Rong Zhang
2025-11-05 18:28 ` [PATCH v2 2/4] platform/x86: ideapad-laptop: Protect GBMD/SBMC calls with mutex Rong Zhang
@ 2025-11-05 18:28 ` Rong Zhang
2025-11-05 18:28 ` [PATCH v2 4/4] platform/x86: ideapad-laptop: Add charge_types:Fast (Rapid Charge) Rong Zhang
2025-11-10 12:00 ` [PATCH v2 0/4] " Ilpo Järvinen
4 siblings, 0 replies; 7+ messages in thread
From: Rong Zhang @ 2025-11-05 18:28 UTC (permalink / raw)
To: Ike Panhc, Mark Pearson, Derek J. Clark, Hans de Goede,
Ilpo Järvinen
Cc: Rong Zhang, Jelle van der Waa, platform-driver-x86, linux-kernel
Some recent devices supports more charge_types. To properly support
these device without breaking the existing ones, we need to define
multiple power_supply_ext for different GBMD/SBMC interface revisions.
No functional change intended.
Signed-off-by: Rong Zhang <i@rong.moe>
---
Changes in v2:
- New patch in the series.
---
drivers/platform/x86/lenovo/ideapad-laptop.c | 35 +++++++++++++-------
1 file changed, 23 insertions(+), 12 deletions(-)
diff --git a/drivers/platform/x86/lenovo/ideapad-laptop.c b/drivers/platform/x86/lenovo/ideapad-laptop.c
index 141de6335740..af89063108be 100644
--- a/drivers/platform/x86/lenovo/ideapad-laptop.c
+++ b/drivers/platform/x86/lenovo/ideapad-laptop.c
@@ -168,6 +168,7 @@ struct ideapad_private {
struct ideapad_dytc_priv *dytc;
struct dentry *debug;
struct acpi_battery_hook battery_hook;
+ const struct power_supply_ext *battery_ext;
unsigned long cfg;
unsigned long r_touchpad_val;
struct {
@@ -2070,29 +2071,36 @@ static const enum power_supply_property ideapad_power_supply_props[] = {
POWER_SUPPLY_PROP_CHARGE_TYPES,
};
-static const struct power_supply_ext ideapad_battery_ext = {
- .name = "ideapad_laptop",
- .properties = ideapad_power_supply_props,
- .num_properties = ARRAY_SIZE(ideapad_power_supply_props),
- .charge_types = (BIT(POWER_SUPPLY_CHARGE_TYPE_STANDARD) |
- BIT(POWER_SUPPLY_CHARGE_TYPE_LONGLIFE)),
- .get_property = ideapad_psy_ext_get_prop,
- .set_property = ideapad_psy_ext_set_prop,
- .property_is_writeable = ideapad_psy_prop_is_writeable,
-};
+#define DEFINE_IDEAPAD_POWER_SUPPLY_EXTENSION(_name, _charge_types) \
+ static const struct power_supply_ext _name = { \
+ .name = "ideapad_laptop", \
+ .properties = ideapad_power_supply_props, \
+ .num_properties = ARRAY_SIZE(ideapad_power_supply_props), \
+ .charge_types = _charge_types, \
+ .get_property = ideapad_psy_ext_get_prop, \
+ .set_property = ideapad_psy_ext_set_prop, \
+ .property_is_writeable = ideapad_psy_prop_is_writeable, \
+ }
+
+DEFINE_IDEAPAD_POWER_SUPPLY_EXTENSION(ideapad_battery_ext_v1,
+ (BIT(POWER_SUPPLY_CHARGE_TYPE_STANDARD) |
+ BIT(POWER_SUPPLY_CHARGE_TYPE_LONGLIFE))
+);
static int ideapad_battery_add(struct power_supply *battery, struct acpi_battery_hook *hook)
{
struct ideapad_private *priv = container_of(hook, struct ideapad_private, battery_hook);
- return power_supply_register_extension(battery, &ideapad_battery_ext,
+ return power_supply_register_extension(battery, priv->battery_ext,
&priv->platform_device->dev, priv);
}
static int ideapad_battery_remove(struct power_supply *battery,
struct acpi_battery_hook *hook)
{
- power_supply_unregister_extension(battery, &ideapad_battery_ext);
+ struct ideapad_private *priv = container_of(hook, struct ideapad_private, battery_hook);
+
+ power_supply_unregister_extension(battery, priv->battery_ext);
return 0;
}
@@ -2118,6 +2126,9 @@ static int ideapad_check_features(struct ideapad_private *priv)
if (acpi_has_method(handle, "GBMD") && acpi_has_method(handle, "SBMC")) {
priv->features.conservation_mode = true;
+
+ priv->battery_ext = &ideapad_battery_ext_v1;
+
priv->battery_hook.add_battery = ideapad_battery_add;
priv->battery_hook.remove_battery = ideapad_battery_remove;
priv->battery_hook.name = "Ideapad Battery Extension";
--
2.51.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 4/4] platform/x86: ideapad-laptop: Add charge_types:Fast (Rapid Charge)
2025-11-05 18:28 [PATCH v2 0/4] platform/x86: ideapad-laptop: Add charge_types:Fast (Rapid Charge) Rong Zhang
` (2 preceding siblings ...)
2025-11-05 18:28 ` [PATCH v2 3/4] platform/x86: ideapad-laptop: Support multiple power_supply_ext definitions Rong Zhang
@ 2025-11-05 18:28 ` Rong Zhang
2025-11-06 12:59 ` Jelle van der Waa
2025-11-10 12:00 ` [PATCH v2 0/4] " Ilpo Järvinen
4 siblings, 1 reply; 7+ messages in thread
From: Rong Zhang @ 2025-11-05 18:28 UTC (permalink / raw)
To: Ike Panhc, Mark Pearson, Derek J. Clark, Hans de Goede,
Ilpo Järvinen
Cc: Rong Zhang, Jelle van der Waa, platform-driver-x86, linux-kernel
The GBMD/SBMC interface on recent devices supports Rapid Charge mode
(charge_types: Fast) in addition to Conservation Mode (charge_types:
Long_Life).
Query the GBMD interface on probe to determine if a device supports
Rapid Charge. If so, expose these two modes while carefully maintaining
their mutually exclusive state, which aligns with the behavior of
manufacturer utilities on Windows.
Signed-off-by: Rong Zhang <i@rong.moe>
Acked-by: Ike Panhc <ikepanhc@gmail.com>
Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
---
Changes in v2:
- Only expose Rapid Charge on devices that support it (thanks Jelle van
der Waa)
- Reword commit message
---
drivers/platform/x86/lenovo/ideapad-laptop.c | 105 ++++++++++++++++---
1 file changed, 89 insertions(+), 16 deletions(-)
diff --git a/drivers/platform/x86/lenovo/ideapad-laptop.c b/drivers/platform/x86/lenovo/ideapad-laptop.c
index af89063108be..5171a077f62c 100644
--- a/drivers/platform/x86/lenovo/ideapad-laptop.c
+++ b/drivers/platform/x86/lenovo/ideapad-laptop.c
@@ -63,13 +63,27 @@ enum {
CFG_OSD_CAM_BIT = 31,
};
+/*
+ * There are two charge modes supported by the GBMD/SBMC interface:
+ * - "Rapid Charge": increase power to speed up charging
+ * - "Conservation Mode": stop charging at 60-80% (depends on model)
+ *
+ * The interface doesn't prohibit enabling both modes at the same time.
+ * However, doing so is essentially meaningless, and the manufacturer utilities
+ * on Windows always make them mutually exclusive.
+ */
+
enum {
+ GBMD_RAPID_CHARGE_STATE_BIT = 2,
GBMD_CONSERVATION_STATE_BIT = 5,
+ GBMD_RAPID_CHARGE_SUPPORTED_BIT = 17,
};
enum {
SBMC_CONSERVATION_ON = 3,
SBMC_CONSERVATION_OFF = 5,
+ SBMC_RAPID_CHARGE_ON = 7,
+ SBMC_RAPID_CHARGE_OFF = 8,
};
enum {
@@ -172,6 +186,7 @@ struct ideapad_private {
unsigned long cfg;
unsigned long r_touchpad_val;
struct {
+ bool rapid_charge : 1;
bool conservation_mode : 1;
bool dytc : 1;
bool fan_mode : 1;
@@ -634,6 +649,10 @@ static ssize_t conservation_mode_show(struct device *dev,
return err;
}
+ /*
+ * For backward compatibility, ignore Rapid Charge while reporting the
+ * state of Conservation Mode.
+ */
return sysfs_emit(buf, "%d\n", !!test_bit(GBMD_CONSERVATION_STATE_BIT, &result));
}
@@ -653,6 +672,16 @@ static ssize_t conservation_mode_store(struct device *dev,
guard(mutex)(&priv->gbmd_sbmc_mutex);
+ /*
+ * Prevent mutually exclusive modes from being set at the same time,
+ * but do not disable Rapid Charge while disabling Conservation Mode.
+ */
+ if (priv->features.rapid_charge && state) {
+ err = exec_sbmc(priv->adev->handle, SBMC_RAPID_CHARGE_OFF);
+ if (err)
+ return err;
+ }
+
err = exec_sbmc(priv->adev->handle, state ? SBMC_CONSERVATION_ON : SBMC_CONSERVATION_OFF);
if (err)
return err;
@@ -2017,14 +2046,24 @@ static int ideapad_psy_ext_set_prop(struct power_supply *psy,
const union power_supply_propval *val)
{
struct ideapad_private *priv = ext_data;
- unsigned long op;
+ unsigned long op1, op2;
+ int err;
switch (val->intval) {
+ case POWER_SUPPLY_CHARGE_TYPE_FAST:
+ if (WARN_ON(!priv->features.rapid_charge))
+ return -EINVAL;
+
+ op1 = SBMC_CONSERVATION_OFF;
+ op2 = SBMC_RAPID_CHARGE_ON;
+ break;
case POWER_SUPPLY_CHARGE_TYPE_LONGLIFE:
- op = SBMC_CONSERVATION_ON;
+ op1 = SBMC_RAPID_CHARGE_OFF;
+ op2 = SBMC_CONSERVATION_ON;
break;
case POWER_SUPPLY_CHARGE_TYPE_STANDARD:
- op = SBMC_CONSERVATION_OFF;
+ op1 = SBMC_RAPID_CHARGE_OFF;
+ op2 = SBMC_CONSERVATION_OFF;
break;
default:
return -EINVAL;
@@ -2032,7 +2071,14 @@ static int ideapad_psy_ext_set_prop(struct power_supply *psy,
guard(mutex)(&priv->gbmd_sbmc_mutex);
- return exec_sbmc(priv->adev->handle, op);
+ /* If !rapid_charge, op1 must be SBMC_RAPID_CHARGE_OFF. Skip it. */
+ if (priv->features.rapid_charge) {
+ err = exec_sbmc(priv->adev->handle, op1);
+ if (err)
+ return err;
+ }
+
+ return exec_sbmc(priv->adev->handle, op2);
}
static int ideapad_psy_ext_get_prop(struct power_supply *psy,
@@ -2042,6 +2088,7 @@ static int ideapad_psy_ext_get_prop(struct power_supply *psy,
union power_supply_propval *val)
{
struct ideapad_private *priv = ext_data;
+ bool is_rapid_charge, is_conservation;
unsigned long result;
int err;
@@ -2051,7 +2098,19 @@ static int ideapad_psy_ext_get_prop(struct power_supply *psy,
return err;
}
- if (test_bit(GBMD_CONSERVATION_STATE_BIT, &result))
+ is_rapid_charge = (priv->features.rapid_charge &&
+ test_bit(GBMD_RAPID_CHARGE_STATE_BIT, &result));
+ is_conservation = test_bit(GBMD_CONSERVATION_STATE_BIT, &result);
+
+ if (unlikely(is_rapid_charge && is_conservation)) {
+ dev_err(&priv->platform_device->dev,
+ "unexpected charge_types: both [Fast] and [Long_Life] are enabled\n");
+ return -EINVAL;
+ }
+
+ if (is_rapid_charge)
+ val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
+ else if (is_conservation)
val->intval = POWER_SUPPLY_CHARGE_TYPE_LONGLIFE;
else
val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
@@ -2087,6 +2146,12 @@ DEFINE_IDEAPAD_POWER_SUPPLY_EXTENSION(ideapad_battery_ext_v1,
BIT(POWER_SUPPLY_CHARGE_TYPE_LONGLIFE))
);
+DEFINE_IDEAPAD_POWER_SUPPLY_EXTENSION(ideapad_battery_ext_v2,
+ (BIT(POWER_SUPPLY_CHARGE_TYPE_STANDARD) |
+ BIT(POWER_SUPPLY_CHARGE_TYPE_FAST) |
+ BIT(POWER_SUPPLY_CHARGE_TYPE_LONGLIFE))
+);
+
static int ideapad_battery_add(struct power_supply *battery, struct acpi_battery_hook *hook)
{
struct ideapad_private *priv = container_of(hook, struct ideapad_private, battery_hook);
@@ -2125,17 +2190,25 @@ static int ideapad_check_features(struct ideapad_private *priv)
priv->features.fan_mode = true;
if (acpi_has_method(handle, "GBMD") && acpi_has_method(handle, "SBMC")) {
- priv->features.conservation_mode = true;
-
- priv->battery_ext = &ideapad_battery_ext_v1;
-
- priv->battery_hook.add_battery = ideapad_battery_add;
- priv->battery_hook.remove_battery = ideapad_battery_remove;
- priv->battery_hook.name = "Ideapad Battery Extension";
-
- err = devm_battery_hook_register(&priv->platform_device->dev, &priv->battery_hook);
- if (err)
- return err;
+ /* Not acquiring gbmd_sbmc_mutex as race condition is impossible on init */
+ if (!eval_gbmd(handle, &val)) {
+ priv->features.conservation_mode = true;
+ priv->features.rapid_charge = test_bit(GBMD_RAPID_CHARGE_SUPPORTED_BIT,
+ &val);
+
+ priv->battery_ext = priv->features.rapid_charge
+ ? &ideapad_battery_ext_v2
+ : &ideapad_battery_ext_v1;
+
+ priv->battery_hook.add_battery = ideapad_battery_add;
+ priv->battery_hook.remove_battery = ideapad_battery_remove;
+ priv->battery_hook.name = "Ideapad Battery Extension";
+
+ err = devm_battery_hook_register(&priv->platform_device->dev,
+ &priv->battery_hook);
+ if (err)
+ return err;
+ }
}
if (acpi_has_method(handle, "DYTC"))
--
2.51.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2 4/4] platform/x86: ideapad-laptop: Add charge_types:Fast (Rapid Charge)
2025-11-05 18:28 ` [PATCH v2 4/4] platform/x86: ideapad-laptop: Add charge_types:Fast (Rapid Charge) Rong Zhang
@ 2025-11-06 12:59 ` Jelle van der Waa
0 siblings, 0 replies; 7+ messages in thread
From: Jelle van der Waa @ 2025-11-06 12:59 UTC (permalink / raw)
To: Rong Zhang, Ike Panhc, Mark Pearson, Derek J. Clark,
Hans de Goede, Ilpo Järvinen
Cc: platform-driver-x86, linux-kernel
On 05/11/2025 19:28, Rong Zhang wrote:
> The GBMD/SBMC interface on recent devices supports Rapid Charge mode
> (charge_types: Fast) in addition to Conservation Mode (charge_types:
> Long_Life).
>
> Query the GBMD interface on probe to determine if a device supports
> Rapid Charge. If so, expose these two modes while carefully maintaining
> their mutually exclusive state, which aligns with the behavior of
> manufacturer utilities on Windows.
>
> Signed-off-by: Rong Zhang <i@rong.moe>
> Acked-by: Ike Panhc <ikepanhc@gmail.com>
> Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
> ---
> Changes in v2:
> - Only expose Rapid Charge on devices that support it (thanks Jelle van
> der Waa)
> - Reword commit message
> ---
<snip>
Tested on my U330p and it doesn't expose the "Fast" charge type as expected.
You can add Tested-By: Jelle van der Waa <jelle@vdwaa.nl>
Thanks,
Jelle van der Waa
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2 0/4] platform/x86: ideapad-laptop: Add charge_types:Fast (Rapid Charge)
2025-11-05 18:28 [PATCH v2 0/4] platform/x86: ideapad-laptop: Add charge_types:Fast (Rapid Charge) Rong Zhang
` (3 preceding siblings ...)
2025-11-05 18:28 ` [PATCH v2 4/4] platform/x86: ideapad-laptop: Add charge_types:Fast (Rapid Charge) Rong Zhang
@ 2025-11-10 12:00 ` Ilpo Järvinen
4 siblings, 0 replies; 7+ messages in thread
From: Ilpo Järvinen @ 2025-11-10 12:00 UTC (permalink / raw)
To: Ike Panhc, Mark Pearson, Derek J. Clark, Hans de Goede, Rong Zhang
Cc: Jelle van der Waa, platform-driver-x86, linux-kernel
On Thu, 06 Nov 2025 02:28:23 +0800, Rong Zhang wrote:
> The GBMD/SBMC interface on recent devices supports Rapid Charge mode
> (charge_types: Fast) in addition to Conservation Mode (charge_types:
> Long_Life).
>
> Query the GBMD interface on probe to determine if a device supports
> Rapid Charge. If so, expose these two modes while carefully maintaining
> their mutually exclusive state, which aligns with the behavior of
> manufacturer utilities on Windows.
>
> [...]
Thank you for your contribution, it has been applied to my local
review-ilpo-next branch. Note it will show up in the public
platform-drivers-x86/review-ilpo-next branch only once I've pushed my
local branch there, which might take a while.
The list of commits applied:
[1/4] platform/x86: ideapad-laptop: Use str_on_off() helper
commit: 06c129cac01be45e458d37534d1a6ba753cf6f13
[2/4] platform/x86: ideapad-laptop: Protect GBMD/SBMC calls with mutex
commit: 85901a0d8591ecd756078e57be077f12a0ee78be
[3/4] platform/x86: ideapad-laptop: Support multiple power_supply_ext definitions
commit: 5c54ece0476638f7c5fc655c655e721286a26bf1
[4/4] platform/x86: ideapad-laptop: Add charge_types:Fast (Rapid Charge)
commit: 90430ea98f5585270bc185cee548154d239af8d7
--
i.
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2025-11-10 12:00 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-11-05 18:28 [PATCH v2 0/4] platform/x86: ideapad-laptop: Add charge_types:Fast (Rapid Charge) Rong Zhang
2025-11-05 18:28 ` [PATCH v2 1/4] platform/x86: ideapad-laptop: Use str_on_off() helper Rong Zhang
2025-11-05 18:28 ` [PATCH v2 2/4] platform/x86: ideapad-laptop: Protect GBMD/SBMC calls with mutex Rong Zhang
2025-11-05 18:28 ` [PATCH v2 3/4] platform/x86: ideapad-laptop: Support multiple power_supply_ext definitions Rong Zhang
2025-11-05 18:28 ` [PATCH v2 4/4] platform/x86: ideapad-laptop: Add charge_types:Fast (Rapid Charge) Rong Zhang
2025-11-06 12:59 ` Jelle van der Waa
2025-11-10 12:00 ` [PATCH v2 0/4] " Ilpo Järvinen
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®