summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Rhodes <sean@starlabs.systems>2026-03-16 01:34:33 +0300
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-03-18 18:03:55 +0300
commit8020c41b39f514941d93f3322b598afdce487064 (patch)
tree1bad2cfbaeaa54d443479bf240d90a823b820adf
parent1f50332c60c2f63118b6c8b41a61d0e43d707743 (diff)
downloadlinux-8020c41b39f514941d93f3322b598afdce487064.tar.xz
usb: core: allow ACPI-managed hard-wired ports to power off
USB core only relaxes the default PM_QOS_FLAG_NO_POWER_OFF policy when an upstream hub reports switchable port power. That misses internal ports whose power is managed by platform firmware instead of the USB hub descriptor. Allow the port-poweroff policy to be exposed for hard-wired ports with an ACPI-managed power resource. The existing runtime PM path still requires the child usage count to drop and remote wakeup to be clear before it will power the port down. This lets internal devices such as CNVi Bluetooth use the existing USB ACPI runtime power path even when the root hub reports no USB-standard port power switching. Signed-off-by: Sean Rhodes <sean@starlabs.systems> Link: https://patch.msgid.link/20260315223433.23452-1-sean@starlabs.systems Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/usb/core/port.c20
1 files changed, 17 insertions, 3 deletions
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index 44e38f922bc5..b6cdea7e9521 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -21,6 +21,20 @@ static int usb_port_block_power_off;
static const struct attribute_group *port_dev_group[];
+static bool usb_port_allow_power_off(struct usb_device *hdev,
+ struct usb_hub *hub,
+ struct usb_port *port_dev)
+{
+ if (hub_is_port_power_switchable(hub))
+ return true;
+
+ if (!IS_ENABLED(CONFIG_ACPI))
+ return false;
+
+ return port_dev->connect_type == USB_PORT_CONNECT_TYPE_HARD_WIRED &&
+ usb_acpi_power_manageable(hdev, port_dev->portnum - 1);
+}
+
static ssize_t early_stop_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -805,10 +819,10 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)
device_enable_async_suspend(&port_dev->dev);
/*
- * Keep hidden the ability to enable port-poweroff if the hub
- * does not support power switching.
+ * Keep hidden the ability to enable port-poweroff if neither the
+ * USB hub nor platform firmware can manage downstream port power.
*/
- if (!hub_is_port_power_switchable(hub))
+ if (!usb_port_allow_power_off(hdev, hub, port_dev))
return 0;
/* Attempt to let userspace take over the policy. */