diff options
| author | Armin Wolf <W_Armin@gmx.de> | 2026-05-30 19:08:10 +0200 |
|---|---|---|
| committer | Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> | 2026-07-06 11:27:06 +0300 |
| commit | e02c8d7a197d8a94e30fdcdee2a626d8a9ae1549 (patch) | |
| tree | 07e00ebdda6355adaf0acb87b78e7c72783d5851 | |
| parent | c393505bab5b1fa25aa702c04a25b3bae0a570ee (diff) | |
| download | linux-next-e02c8d7a197d8a94e30fdcdee2a626d8a9ae1549.tar.gz linux-next-e02c8d7a197d8a94e30fdcdee2a626d8a9ae1549.zip | |
platform/x86: uniwill-laptop: Add support for USB powershare
Some devices support a "USB powershare" feature where the system will
continue to provide power via the USB ports when hibernating or
powered off.
Add support for this feaure.
Signed-off-by: Armin Wolf <W_Armin@gmx.de>
Link: https://patch.msgid.link/20260530170813.10166-5-W_Armin@gmx.de
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
| -rw-r--r-- | Documentation/ABI/testing/sysfs-driver-uniwill-laptop | 16 | ||||
| -rw-r--r-- | Documentation/admin-guide/laptops/uniwill-laptop.rst | 7 | ||||
| -rw-r--r-- | drivers/platform/x86/uniwill/uniwill-acpi.c | 100 |
3 files changed, 122 insertions, 1 deletions
diff --git a/Documentation/ABI/testing/sysfs-driver-uniwill-laptop b/Documentation/ABI/testing/sysfs-driver-uniwill-laptop index 57272f906184..943f92c6b561 100644 --- a/Documentation/ABI/testing/sysfs-driver-uniwill-laptop +++ b/Documentation/ABI/testing/sysfs-driver-uniwill-laptop @@ -86,6 +86,20 @@ Contact: Armin Wolf <W_Armin@gmx.de> Description: Allows userspace applications to configure if the device should boot automatically when being connected to a power source. Writing "1"/"0" into this file - enables/disables this functionality. + enables/disables this functionality. Enabling both AC auto boot and USB powershare + at the same time is not supported. Reading this file returns the current status of the AC auto boot functionality. + +What: /sys/bus/platform/devices/INOU0000:XX/usb_powershare_high +Date: March 2026 +KernelVersion: 7.1 +Contact: Armin Wolf <W_Armin@gmx.de> +Description: + Allows userspace applications to configure if the device should continue to provide + power via the USB ports when hibernating or powered off. Might also increase the + power budget available to USB ports on some devices. Writing "1"/"0" into this + file enables/disables this functionality. Enabling both USB powershare and AC auto + boot at the same time is not supported. + + Reading this file returns the current status of the USB powershare functionality. diff --git a/Documentation/admin-guide/laptops/uniwill-laptop.rst b/Documentation/admin-guide/laptops/uniwill-laptop.rst index b6213fb1d3e0..be50b45b82ef 100644 --- a/Documentation/admin-guide/laptops/uniwill-laptop.rst +++ b/Documentation/admin-guide/laptops/uniwill-laptop.rst @@ -105,6 +105,13 @@ The ``uniwill-laptop`` driver allows the user to configure if the system should boot when being connected to a power source, see Documentation/ABI/testing/sysfs-driver-uniwill-laptop for details. +USB Powershare +-------------- + +The ``uniwill-laptop`` driver allows the user to configure if the system should continue to +provide power via the USB ports when hibernating or powered off, see +Documentation/ABI/testing/sysfs-driver-uniwill-laptop for details. + References ========== diff --git a/drivers/platform/x86/uniwill/uniwill-acpi.c b/drivers/platform/x86/uniwill/uniwill-acpi.c index 897c6163de53..00140c0a67a0 100644 --- a/drivers/platform/x86/uniwill/uniwill-acpi.c +++ b/drivers/platform/x86/uniwill/uniwill-acpi.c @@ -368,6 +368,7 @@ #define UNIWILL_FEATURE_USB_C_POWER_PRIORITY BIT(11) #define UNIWILL_FEATURE_KEYBOARD_BACKLIGHT BIT(12) #define UNIWILL_FEATURE_AC_AUTO_BOOT BIT(13) +#define UNIWILL_FEATURE_USB_POWERSHARE BIT(14) enum usb_c_power_priority_options { USB_C_POWER_PRIORITY_CHARGING = 0, @@ -393,6 +394,7 @@ struct uniwill_data { bool last_fn_lock_state; bool last_super_key_enable_state; bool last_touchpad_toggle_enable_state; + bool last_usb_powershare_high_state; struct mutex super_key_lock; /* Protects the toggling of the super key lock state */ struct list_head batteries; struct mutex led_lock; /* Protects writes to the lightbar registers */ @@ -1181,6 +1183,70 @@ static ssize_t ac_auto_boot_show(struct device *dev, struct device_attribute *at static DEVICE_ATTR_RW(ac_auto_boot); +static int uniwill_write_usb_powershare_high(struct uniwill_data *data, bool status) +{ + unsigned int value; + + if (status) + value = TRIGGER_USB_CHARGING; + else + value = 0; + + /* + * Normaly this RMW-sequence could also trigger the super key toggle, + * but the EC seems to take care that those bits are always read as 0. + */ + return regmap_update_bits(data->regmap, EC_ADDR_TRIGGER, TRIGGER_USB_CHARGING, value); +} + +static ssize_t usb_powershare_high_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct uniwill_data *data = dev_get_drvdata(dev); + bool enable; + int ret; + + ret = kstrtobool(buf, &enable); + if (ret < 0) + return ret; + + ret = uniwill_write_usb_powershare_high(data, enable); + if (ret < 0) + return ret; + + return count; +} + +static int uniwill_read_usb_powershare_high(struct uniwill_data *data, bool *status) +{ + unsigned int value; + int ret; + + ret = regmap_read(data->regmap, EC_ADDR_TRIGGER, &value); + if (ret < 0) + return ret; + + *status = !!(value & TRIGGER_USB_CHARGING); + + return 0; +} + +static ssize_t usb_powershare_high_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct uniwill_data *data = dev_get_drvdata(dev); + bool status; + int ret; + + ret = uniwill_read_usb_powershare_high(data, &status); + if (ret < 0) + return ret; + + return sysfs_emit(buf, "%d\n", status); +} + +static DEVICE_ATTR_RW(usb_powershare_high); + static struct attribute *uniwill_attrs[] = { /* Keyboard-related */ &dev_attr_fn_lock.attr, @@ -1193,6 +1259,7 @@ static struct attribute *uniwill_attrs[] = { &dev_attr_ctgp_offset.attr, &dev_attr_usb_c_power_priority.attr, &dev_attr_ac_auto_boot.attr, + &dev_attr_usb_powershare_high.attr, NULL }; @@ -1237,6 +1304,11 @@ static umode_t uniwill_attr_is_visible(struct kobject *kobj, struct attribute *a return attr->mode; } + if (attr == &dev_attr_usb_powershare_high.attr) { + if (uniwill_device_supports(data, UNIWILL_FEATURE_USB_POWERSHARE)) + return attr->mode; + } + return 0; } @@ -2382,6 +2454,18 @@ static int uniwill_suspend_kbd_led(struct uniwill_data *data) return regmap_write(data->regmap, EC_ADDR_KBD_STATUS, regval); } +static int uniwill_suspend_usb_powershare(struct uniwill_data *data) +{ + if (!uniwill_device_supports(data, UNIWILL_FEATURE_USB_POWERSHARE)) + return 0; + + /* + * EC_ADDR_TRIGGER is marked as volatile, so we have to restore it + * ourselves. + */ + return uniwill_read_usb_powershare_high(data, &data->last_usb_powershare_high_state); +} + static int uniwill_suspend_nvidia_ctgp(struct uniwill_data *data) { if (!uniwill_device_supports(data, UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL)) @@ -2416,6 +2500,10 @@ static int uniwill_suspend(struct device *dev) if (ret < 0) return ret; + ret = uniwill_suspend_usb_powershare(data); + if (ret < 0) + return ret; + ret = uniwill_suspend_nvidia_ctgp(data); if (ret < 0) return ret; @@ -2479,6 +2567,14 @@ static int uniwill_resume_kbd_led(struct uniwill_data *data) return regmap_write_bits(data->regmap, EC_ADDR_TRIGGER, RGB_APPLY_COLOR, RGB_APPLY_COLOR); } +static int uniwill_resume_usb_powershare(struct uniwill_data *data) +{ + if (!uniwill_device_supports(data, UNIWILL_FEATURE_USB_POWERSHARE)) + return 0; + + return uniwill_write_usb_powershare_high(data, data->last_usb_powershare_high_state); +} + static int uniwill_resume_nvidia_ctgp(struct uniwill_data *data) { if (!uniwill_device_supports(data, UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL)) @@ -2527,6 +2623,10 @@ static int uniwill_resume(struct device *dev) if (ret < 0) return ret; + ret = uniwill_resume_usb_powershare(data); + if (ret < 0) + return ret; + ret = uniwill_resume_nvidia_ctgp(data); if (ret < 0) return ret; |
