[PATCH v4] usb: pci-quirks: always assert xHCI OS ownership
From: Rishabh Jain
Date: Tue Sep 01 2026 - 21:24:25 EST
The xHCI ownership protocol requires the OS driver to assert the HC OS
Owned semaphore before using the host controller, then wait for HC BIOS
Owned to clear if firmware owns it.
quirk_usb_handoff_xhci() currently asserts OS Owned only when BIOS Owned
is already set. If firmware leaves BIOS Owned clear, Linux uses the xHC
while both ownership semaphores remain clear.
On an AMD PROM21 xHCI controller (1022:43fc), this caused every S3
resume to terminate Controller Restore State with USBSTS 0x401. Linux
then reset the host controller, both root hubs and the USB Bluetooth
adapter.
The controller entered resume ready and halted with USBSTS 0x1.
Endpoint state, 100 ms save/restore delays, scratchpads, the DCBAA,
device contexts and command, event and transfer rings were verified not
to cause the restore error.
Across four S3 cycles, asserting only HC OS Owned changed USBLEGSUP from
0x00000801 to 0x01000801 and eliminated the restore failure. Testing
included the unmodified 7.1.8-ogc1.1.fc44.x86_64 distribution kernel
using a test module that set the HC OS Owned semaphore. Clearing
USBLEGCTLSTS was independently verified to be unnecessary.
Always assert OS Owned for controllers using the standard xHCI handoff,
and leave the existing TI/Renesas forced handoff unchanged. Keep OS
Owned asserted after the standard handoff to prevent firmware from
reclaiming the controller during subsequent suspends.
Fixes: 66d4eadd8d06 ("USB: xhci: BIOS handoff and HW initialization.")
Cc: stable@xxxxxxxxxxxxxxx
Assisted-by: LLM
Signed-off-by: Rishabh Jain <rishabh.jain1198@xxxxxxxxx>
---
Thanks for the review.
By "stock-kernel test", I meant that the register change was tested on
the unmodified 7.1.8-ogc1.1.fc44.x86_64 distribution kernel using a test
module that set the HC OS Owned semaphore.
Sorry, I missed the AI assistant guidelines. Fixed now.
The TI/Renesas forced-handoff path is restored unchanged. This revision
only changes the standard handoff path. I left the byte-access change
out of this patch.
Changes in v4:
- Preserve the existing TI/Renesas forced-handoff path.
- Limit the ownership change to the standard handoff path.
drivers/usb/host/pci-quirks.c | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c
index 0404489c2f6a..fa9b5db4d115 100644
--- a/drivers/usb/host/pci-quirks.c
+++ b/drivers/usb/host/pci-quirks.c
@@ -1193,22 +1193,25 @@ static void quirk_usb_handoff_xhci(struct pci_dev *pdev)
&& pdev->device == 0x0014)) {
val = (val | XHCI_HC_OS_OWNED) & ~XHCI_HC_BIOS_OWNED;
writel(val, base + ext_cap_offset);
- }
-
- /* If the BIOS owns the HC, signal that the OS wants it, and wait */
- if (val & XHCI_HC_BIOS_OWNED) {
- writel(val | XHCI_HC_OS_OWNED, base + ext_cap_offset);
+ } else {
+ /*
+ * Perform the standard handoff and leave OS ownership set to
+ * keep the BIOS at bay during subsequent suspends.
+ */
+ val |= XHCI_HC_OS_OWNED;
+ writel(val, base + ext_cap_offset);
/* Wait for 1 second with 10 microsecond polling interval */
timeout = handshake(base + ext_cap_offset, XHCI_HC_BIOS_OWNED,
- 0, 1000000, 10);
+ 0, 1000000, 10);
/* Assume a buggy BIOS and take HC ownership anyway */
if (timeout) {
dev_warn(&pdev->dev,
"xHCI BIOS handoff failed (BIOS bug ?) %08x\n",
val);
- writel(val & ~XHCI_HC_BIOS_OWNED, base + ext_cap_offset);
+ writel(val & ~XHCI_HC_BIOS_OWNED,
+ base + ext_cap_offset);
}
}
--
2.50.1 (Apple Git-155)