[PATCH v2 1/2] usb: core: check ACPI port power during registration

From: Sean Rhodes

Date: Thu Sep 17 2026 - 18:37:38 EST


usb_hub_create_port_device() checks whether a port is power manageable
before hdev->maxchild is set. usb_acpi_power_manageable() looks up the
hub through usb_hub_to_struct_hub(), which rejects hubs while maxchild is
zero.

The port device has already been registered and its ACPI companion bound
at this point. Check that companion directly.

Fixes: 8020c41b39f5 ("usb: core: allow ACPI-managed hard-wired ports to power off")

Signed-off-by: Sean Rhodes <sean@starlabs.systems>
---
drivers/usb/core/port.c | 7 +++----
drivers/usb/core/usb-acpi.c | 7 +++++++
drivers/usb/core/usb.h | 6 ++++++
3 files changed, 16 insertions(+), 4 deletions(-)

diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index b4452b665f59..59c3ab31f030 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -21,8 +21,7 @@ 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,
+static bool usb_port_allow_power_off(struct usb_hub *hub,
struct usb_port *port_dev)
{
if (hub_is_port_power_switchable(hub))
@@ -32,7 +31,7 @@ static bool usb_port_allow_power_off(struct usb_device *hdev,
return false;

return port_dev->connect_type == USB_PORT_CONNECT_TYPE_HARD_WIRED &&
- usb_acpi_power_manageable(hdev, port_dev->portnum - 1);
+ usb_acpi_port_power_manageable(port_dev);
}

static ssize_t early_stop_show(struct device *dev,
@@ -825,7 +824,7 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)
* Keep hidden the ability to enable port-poweroff if neither the
* USB hub nor platform firmware can manage downstream port power.
*/
- if (!usb_port_allow_power_off(hdev, hub, port_dev))
+ if (!usb_port_allow_power_off(hub, port_dev))
return 0;

/* Attempt to let userspace take over the policy. */
diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c
index 489dbdc96f94..5163ef363eef 100644
--- a/drivers/usb/core/usb-acpi.c
+++ b/drivers/usb/core/usb-acpi.c
@@ -37,6 +37,13 @@ bool usb_acpi_power_manageable(struct usb_device *hdev, int index)
}
EXPORT_SYMBOL_GPL(usb_acpi_power_manageable);

+bool usb_acpi_port_power_manageable(struct usb_port *port_dev)
+{
+ struct acpi_device *adev = ACPI_COMPANION(&port_dev->dev);
+
+ return adev && acpi_device_power_manageable(adev);
+}
+
#define UUID_USB_CONTROLLER_DSM "ce2ee385-00e6-48cb-9f05-2edb927c4899"
#define USB_DSM_DISABLE_U1_U2_FOR_PORT 5

diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index a9b37aeb515b..356d36ccbdd3 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -8,6 +8,7 @@

struct usb_hub_descriptor;
struct usb_dev_state;
+struct usb_port;

/* Functions local to drivers/usb/core/ */

@@ -211,7 +212,12 @@ extern int usb_acpi_register(void);
extern void usb_acpi_unregister(void);
extern acpi_handle usb_get_hub_port_acpi_handle(struct usb_device *hdev,
int port1);
+bool usb_acpi_port_power_manageable(struct usb_port *port_dev);
#else
static inline int usb_acpi_register(void) { return 0; };
static inline void usb_acpi_unregister(void) { };
+static inline bool usb_acpi_port_power_manageable(struct usb_port *port_dev)
+{
+ return false;
+}
#endif