* [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