Message ID | 20220911161718.24284-1-p.jungkamp@gmx.net (mailing list archive) |
---|---|
State | Changes Requested, archived |
Headers | show |
Series | Add IdeaPad quick_charge attribute to sysfs | expand |
Hi 2022. szeptember 11., vasárnap 18:17 keltezéssel, Philipp Jungkamp írta: > 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 There is already an entry for this on the kernel bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=216176 I have two concerns: one, it adds a new driver specific attribute for a somewhat generic functionality; two, Lenovo Vantage does not only check this single bit before allowing this mode to be enabled (as far as I can recall). Regards, Barnabás Pőcze > > 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 >
Hi, On 9/11/22 19:08, Barnabás Pőcze wrote: > Hi > > > 2022. szeptember 11., vasárnap 18:17 keltezéssel, Philipp Jungkamp írta: >> 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 > > There is already an entry for this on the kernel bugzilla: > https://bugzilla.kernel.org/show_bug.cgi?id=216176 > > I have two concerns: one, it adds a new driver specific attribute for a somewhat > generic functionality; Right this really needs a standardized API using the power_supply sysfs class, see: https://bugzilla.kernel.org/show_bug.cgi?id=216176#c5 Please write an API proposal for this and submit it as discussed in the linked comment, Regards, Hans > two, Lenovo Vantage does not only check this single bit > before allowing this mode to be enabled (as far as I can recall). > > > Regards, > Barnabás Pőcze > > >> >> 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 >> >
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;