Re: [PATCH v2] xhci: Set port link to RxDetect if port is not enabled after resume

From: Mathias Nyman
Date: Mon Apr 27 2020 - 12:47:00 EST


On 27.4.2020 12.05, Kai-Heng Feng wrote:
>
>
>> On Apr 23, 2020, at 19:25, Mathias Nyman <mathias.nyman@xxxxxxxxxxxxxxx> wrote:
>>
>> Was this roothub port forcefully suspended xhci_bus_suspend()?
>> i.e. was a bit set in bus_state->bus_suspended for this port?
>
> No, it's a USB3 device so it was set to U3 via USB_PORT_FEAT_LINK_STATE.

Logs show port was first forced by xhci_bus_suspend() to U3 ("port 0 not
suspended" message)
and only later set to U3 by USB_PORT_FEAT_LINK_STATE.
Seems line wrong order or race.

while suspended we get a port event about a connect status change,
showing port link state is in disabled.
Cherry picked messages:

[ 1330.021450] xhci_hcd 0000:3f:00.0: port 0 not suspended
[ 1330.036822] xhci_hcd 0000:3f:00.0: Set port 4-1 link state, portsc: 0x1203, write 0x11261
[ 1331.020736] xhci_hcd 0000:3f:00.0: Port change event, 4-1, id 1, portsc: 0x20280
[ 1331.020738] xhci_hcd 0000:3f:00.0: resume root hub
[ 1331.020741] xhci_hcd 0000:3f:00.0: handle_port_status: starting port polling.

If we force the port link state to U3 in xhci_bus_suspend() maybe it would make
sense to try and recover it in xhci_bus_resume(). But only for that forced
port.

None of the previous suspend/resume cycles in the logs went smooth either.
Each time device 4-1 was forced to U3 bus xhci_bus_suspend(), and at resume the
link was stuck in polling until timeout, after which it went to compliance mode,
and had to be warm reset to recover.

We could add the code to recover USB3 ports from disabled state in case we
forced them to U3, but the rootcause of theus suspend/resume issues should
be found as well

Limiting your code to USB3 devices that xhi_bus_suspend forced to U3 would look
something like this:

diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 9eca1fe81061..0f16dd936ab8 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -1789,6 +1789,15 @@ int xhci_bus_resume(struct usb_hcd *hcd)
case XDEV_RESUME:
/* resume already initiated */
break;
+ case XDEV_DISABLED:
+ if (hcd->speed >= HCD_USB3) {
+ portsc = xhci_port_state_to_neutral(
+ portsc);
+ portsc &= ~PORT_PLS_MASK;
+ portsc |= PORT_LINK_STROBE |
+ XDEV_RXDETECT;
+ }
+ /* fall through for both USB3 abd USB2 */
default:
/* not in a resumeable state, ignore it */
clear_bit(port_index,

-Mathias