[PATCH] usb: core: allow ACPI-managed hard-wired ports to power off
From: Sean Rhodes
Date: Sun Mar 15 2026 - 18:34:45 EST
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>
---
drivers/usb/core/port.c | 20 +++++++++++++++++---
1 file changed, 17 insertions(+), 3 deletions(-)
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index f54198171b6a..6445d05a33be 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. */
--
2.51.0