summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorArmin Wolf <W_Armin@gmx.de>2026-05-30 19:08:10 +0200
committerIlpo Järvinen <ilpo.jarvinen@linux.intel.com>2026-07-06 11:27:06 +0300
commite02c8d7a197d8a94e30fdcdee2a626d8a9ae1549 (patch)
tree07e00ebdda6355adaf0acb87b78e7c72783d5851 /drivers
parentc393505bab5b1fa25aa702c04a25b3bae0a570ee (diff)
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>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/platform/x86/uniwill/uniwill-acpi.c100
1 files changed, 100 insertions, 0 deletions
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;