More recent IdeaPads allow USB-C quick-charging to be controlled via ACPI. This seems to be mutually exclusive with the ACPI conservation mode. Expose a readable and writable 'quick_charge' sysfs attribute next when support is indicated in ACPI. --- I deduced the indicator bits from their names in the DSDT (QCHO and QCHX). I don't have an IdeaPad except mine on hand and can't check whether these are indeed the intended uses or their behaviour on other IdeaPads. I can confirm that a change in the quick_charge toggle is visible in Lenovo Vantage when dual booting into Windows 11. Greetings, Philipp Jungkamp drivers/platform/x86/ideapad-laptop.c | 64 ++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index abd0c81d62c4..dea35779264a 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -54,12 +54,16 @@ enum { }; enum { - GBMD_CONSERVATION_STATE_BIT = 5, + GBMD_QUICK_CHARGE_STATE_BIT = 2, + GBMD_CONSERVATION_STATE_BIT = 5, + GBMD_QUICK_CHARGE_SUPPORT_BIT = 17, }; enum { SBMC_CONSERVATION_ON = 3, SBMC_CONSERVATION_OFF = 5, + SBMC_QUICK_CHARGE_ON = 7, + SBMC_QUICK_CHARGE_OFF = 8, }; enum { @@ -140,6 +144,7 @@ struct ideapad_private { bool kbd_bl : 1; bool touchpad_ctrl_via_ec : 1; bool usb_charging : 1; + bool quick_charge : 1; } features; struct { bool initialized; @@ -482,6 +487,12 @@ static ssize_t conservation_mode_store(struct device *dev, if (err) return err; + if (priv->features.quick_charge && state) { + err = exec_sbmc(priv->adev->handle, SBMC_QUICK_CHARGE_OFF); + if (err) + return err; + } + err = exec_sbmc(priv->adev->handle, state ? SBMC_CONSERVATION_ON : SBMC_CONSERVATION_OFF); if (err) return err; @@ -491,6 +502,48 @@ static ssize_t conservation_mode_store(struct device *dev, static DEVICE_ATTR_RW(conservation_mode); +static ssize_t quick_charge_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ideapad_private *priv = dev_get_drvdata(dev); + unsigned long result; + int err; + + err = eval_gbmd(priv->adev->handle, &result); + if (err) + return err; + + return sysfs_emit(buf, "%d\n", !!test_bit(GBMD_QUICK_CHARGE_STATE_BIT, &result)); +} + +static ssize_t quick_charge_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ideapad_private *priv = dev_get_drvdata(dev); + bool state; + int err; + + err = kstrtobool(buf, &state); + if (err) + return err; + + if (priv->features.conservation_mode && state) { + err = exec_sbmc(priv->adev->handle, SBMC_CONSERVATION_OFF); + if (err) + return err; + } + + err = exec_sbmc(priv->adev->handle, state ? SBMC_QUICK_CHARGE_ON : SBMC_QUICK_CHARGE_OFF); + if (err) + return err; + + return count; +} + +static DEVICE_ATTR_RW(quick_charge); + static ssize_t fan_mode_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -641,6 +694,7 @@ static DEVICE_ATTR_RW(usb_charging); static struct attribute *ideapad_attributes[] = { &dev_attr_camera_power.attr, &dev_attr_conservation_mode.attr, + &dev_attr_quick_charge.attr, &dev_attr_fan_mode.attr, &dev_attr_fn_lock.attr, &dev_attr_touchpad.attr, @@ -660,6 +714,8 @@ static umode_t ideapad_is_visible(struct kobject *kobj, supported = test_bit(CFG_CAP_CAM_BIT, &priv->cfg); else if (attr == &dev_attr_conservation_mode.attr) supported = priv->features.conservation_mode; + else if (attr == &dev_attr_quick_charge.attr) + supported = priv->features.quick_charge; else if (attr == &dev_attr_fan_mode.attr) supported = priv->features.fan_mode; else if (attr == &dev_attr_fn_lock.attr) @@ -1546,9 +1602,13 @@ static void ideapad_check_features(struct ideapad_private *priv) if (!read_ec_data(handle, VPCCMD_R_FAN, &val)) priv->features.fan_mode = true; - if (acpi_has_method(handle, "GBMD") && acpi_has_method(handle, "SBMC")) + if (acpi_has_method(handle, "GBMD") && acpi_has_method(handle, "SBMC")) { priv->features.conservation_mode = true; + if (!eval_gbmd(handle,&val) && test_bit(GBMD_QUICK_CHARGE_SUPPORT_BIT, &val)) + priv->features.quick_charge = true; + } + if (acpi_has_method(handle, "DYTC")) priv->features.dytc = true; -- 2.37.3