mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-03-22 15:36:55 +08:00
platform/x86: ideapad-laptop: Add charge_types:Fast (Rapid Charge)
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> Tested-By: Jelle van der Waa <jelle@vdwaa.nl> Link: https://patch.msgid.link/20251105182832.104946-5-i@rong.moe Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
This commit is contained in:
committed by
Ilpo Järvinen
parent
5c54ece047
commit
90430ea98f
@@ -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;
|
||||
/* 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 = &ideapad_battery_ext_v1;
|
||||
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";
|
||||
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;
|
||||
err = devm_battery_hook_register(&priv->platform_device->dev,
|
||||
&priv->battery_hook);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (acpi_has_method(handle, "DYTC"))
|
||||
|
||||
Reference in New Issue
Block a user