Commit 6b49dea4 authored by Barnabás Pőcze's avatar Barnabás Pőcze Committed by Hans de Goede
Browse files

platform/x86: ideapad-laptop: add "always on USB charging" control support



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: default avatarBarnabás Pőcze <pobrn@protonmail.com>
Link: https://lore.kernel.org/r/20210203215403.290792-28-pobrn@protonmail.com


Reviewed-by: default avatarHans de Goede <hdegoede@redhat.com>
Signed-off-by: default avatarHans de Goede <hdegoede@redhat.com>
parent 503325f8
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -41,3 +41,12 @@ Description:

		  # echo "0" >	\
		  /sys/bus/pci/devices/0000:00:1f.0/PNP0C09:00/VPC2004:00/fn_lock

What:		/sys/bus/platform/devices/VPC2004:*/usb_charging
Date:		Feb 2021
KernelVersion:	5.12
Contact:	platform-driver-x86@vger.kernel.org
Description:
		Controls whether the "always on USB charging" feature is
		enabled or not. This feature enables charging USB devices
		even if the computer is not turned on.
+56 −9
Original line number Diff line number Diff line
@@ -64,6 +64,8 @@ enum {
enum {
	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,
@@ -72,6 +74,8 @@ enum {
enum {
	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,
};
@@ -134,6 +138,7 @@ struct ideapad_private {
		bool hw_rfkill_switch     : 1;
		bool kbd_bl               : 1;
		bool touchpad_ctrl_via_ec : 1;
		bool usb_charging         : 1;
	} features;
	struct {
		bool initialized;
@@ -592,12 +597,49 @@ static ssize_t touchpad_store(struct device *dev,

static DEVICE_ATTR_RW(touchpad);

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, "%d\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 = exec_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_conservation_mode.attr,
	&dev_attr_fan_mode.attr,
	&dev_attr_fn_lock.attr,
	&dev_attr_touchpad.attr,
	&dev_attr_usb_charging.attr,
	NULL
};

@@ -620,6 +662,8 @@ static umode_t ideapad_is_visible(struct kobject *kobj,
	else if (attr == &dev_attr_touchpad.attr)
		supported = priv->features.touchpad_ctrl_via_ec &&
			    test_bit(CFG_CAP_TOUCHPAD_BIT, &priv->cfg);
	else if (attr == &dev_attr_usb_charging.attr)
		supported = priv->features.usb_charging;

	return supported ? attr->mode : 0;
}
@@ -1459,6 +1503,9 @@ static void ideapad_check_features(struct ideapad_private *priv)

			if (test_bit(HALS_KBD_BL_SUPPORT_BIT, &val))
				priv->features.kbd_bl = true;

			if (test_bit(HALS_USB_CHARGING_SUPPORT_BIT, &val))
				priv->features.usb_charging = true;
		}
	}
}