Certain models have a so-called "always on USB charging" feature, which enables USB charging even when the computer is turned off or suspended, and which may be controlled/queried using the SALS/HALS ACPI methods. Expose this functionality via a new device attribute (usb_charging). Tested on: Lenovo YOGA 520-14IKB 80X8 Signed-off-by: Barnabás Pőcze <pobrn@xxxxxxxxxxxxxx> diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index a43574e65f61..5f7883e59f21 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -58,18 +58,22 @@ enum { }; enum { - HALS_KBD_BL_SUPPORT_BIT = 4, - HALS_KBD_BL_STATE_BIT = 5, - HALS_FNLOCK_SUPPORT_BIT = 9, - HALS_FNLOCK_STATE_BIT = 10, - HALS_HOTKEYS_PRIMARY_BIT = 11, + HALS_KBD_BL_SUPPORT_BIT = 4, + HALS_KBD_BL_STATE_BIT = 5, + HALS_USB_CHARGING_SUPPORT_BIT = 6, + HALS_USB_CHARGING_STATE_BIT = 7, + HALS_FNLOCK_SUPPORT_BIT = 9, + HALS_FNLOCK_STATE_BIT = 10, + HALS_HOTKEYS_PRIMARY_BIT = 11, }; enum { - SALS_KBD_BL_ON = 0x8, - SALS_KBD_BL_OFF = 0x9, - SALS_FNLOCK_ON = 0xe, - SALS_FNLOCK_OFF = 0xf, + SALS_KBD_BL_ON = 0x8, + SALS_KBD_BL_OFF = 0x9, + SALS_USB_CHARGING_ON = 0xa, + SALS_USB_CHARGING_OFF = 0xb, + SALS_FNLOCK_ON = 0xe, + SALS_FNLOCK_OFF = 0xf, }; enum { @@ -118,6 +122,7 @@ struct ideapad_private { bool fan_mode : 1, conservation_mode : 1, fn_lock : 1, + usb_charging : 1, hw_rfkill_switch : 1; } features; struct { @@ -581,12 +586,50 @@ static ssize_t fn_lock_store(struct device *dev, static DEVICE_ATTR_RW(fn_lock); +static ssize_t usb_charging_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ideapad_private *priv = dev_get_drvdata(dev); + unsigned long hals; + int err; + + err = eval_hals(priv->adev->handle, &hals); + if (err) + return err; + + return sysfs_emit(buf, "%u\n", test_bit(HALS_USB_CHARGING_STATE_BIT, &hals)); +} + +static ssize_t usb_charging_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; + + err = eval_sals(priv->adev->handle, + state ? SALS_USB_CHARGING_ON : SALS_USB_CHARGING_OFF); + if (err) + return err; + + return count; +} + +static DEVICE_ATTR_RW(usb_charging); + static struct attribute *ideapad_attributes[] = { &dev_attr_camera_power.attr, &dev_attr_fan_mode.attr, &dev_attr_touchpad.attr, &dev_attr_conservation_mode.attr, &dev_attr_fn_lock.attr, + &dev_attr_usb_charging.attr, NULL }; @@ -608,6 +651,8 @@ static umode_t ideapad_is_visible(struct kobject *kobj, supported = priv->features.conservation_mode; else if (attr == &dev_attr_fn_lock.attr) supported = priv->features.fn_lock; + else if (attr == &dev_attr_usb_charging.attr) + supported = priv->features.usb_charging; return supported ? attr->mode : 0; } @@ -1175,9 +1220,13 @@ static void ideapad_check_features(struct ideapad_private *priv) priv->features.conservation_mode = true; if (acpi_has_method(handle, "HALS") && acpi_has_method(handle, "SALS")) { - if (!eval_hals(handle, &val)) + if (!eval_hals(handle, &val)) { if (test_bit(HALS_FNLOCK_SUPPORT_BIT, &val)) priv->features.fn_lock = true; + + if (test_bit(HALS_USB_CHARGING_SUPPORT_BIT, &val)) + priv->features.usb_charging = true; + } } } -- 2.29.2