[PATCH v3 2/6] usb: ehci: add port reset hooks

From: Linus Walleij

Date: Thu Sep 03 2026 - 17:26:33 EST


Some EHCI implementations need controller-specific sequencing around
port reset.

Add optional hooks for reset preparation and completion. Controllers
without the hooks retain the existing behavior.

Gate the callbacks behind the hidden USB_EHCI_PORT_RESET_HOOKS option
so controllers using standard reset sequencing incur no extra state or
runtime checks.

Suggested-by: Alan Stern <stern@xxxxxxxxxxxxxxxxxxx>
Suggested-by: Daniel Palmer <daniel@xxxxxxxxx>
Assisted-by: LLM
Signed-off-by: Linus Walleij <linusw@xxxxxxxxxx>
---
drivers/usb/host/Kconfig | 4 ++++
drivers/usb/host/ehci-hub.c | 16 ++++++++++++++++
drivers/usb/host/ehci.h | 6 ++++++
3 files changed, 26 insertions(+)

diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index af4fc14b8783..a0c66191d03c 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -215,6 +215,10 @@ config USB_EHCI_DEVIANT_PORT_STATUS_REG
bool
# Used for hosts with a deviant port status register location

+config USB_EHCI_PORT_RESET_HOOKS
+ bool
+ # Used for hosts with controller-specific port reset sequencing
+
config XPS_USB_HCD_XILINX
bool "Use Xilinx usb host EHCI controller core"
depends on (PPC32 || MICROBLAZE)
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index 77d0df41f45f..5aa629a92542 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -963,6 +963,13 @@ int ehci_hub_control(
/* see what we found out */
temp = check_reset_complete (ehci, wIndex, status_reg,
ehci_readl(ehci, status_reg));
+#ifdef CONFIG_USB_EHCI_PORT_RESET_HOOKS
+ if (ehci->post_port_reset) {
+ retval = ehci->post_port_reset(ehci, wIndex);
+ if (retval)
+ goto error_exit;
+ }
+#endif
}

/* transfer dedicated ports to the companion hc */
@@ -1121,7 +1128,16 @@ int ehci_hub_control(
*/
if (ehci_has_fsl_hs_errata(ehci))
temp |= (1 << PORTSC_FSL_PFSC);
+
+#ifdef CONFIG_USB_EHCI_PORT_RESET_HOOKS
+ if (ehci->pre_port_reset) {
+ retval = ehci->pre_port_reset(ehci, wIndex);
+ if (retval)
+ goto error_exit;
+ }
+#endif
}
+
ehci_writel(ehci, temp, status_reg);
break;

diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index a65fba092e76..d7e4cd6dc0a3 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -126,6 +126,12 @@ struct ehci_hcd { /* one per controller */
#ifdef CONFIG_USB_EHCI_DEVIANT_PORT_STATUS_REG
u32 __iomem *port_status;
#endif
+#ifdef CONFIG_USB_EHCI_PORT_RESET_HOOKS
+ int (*pre_port_reset)(struct ehci_hcd *ehci,
+ unsigned int port);
+ int (*post_port_reset)(struct ehci_hcd *ehci,
+ unsigned int port);
+#endif

__u32 hcs_params; /* cached register copy */
spinlock_t lock;

--
2.55.0