Re: [PATCH v3] usb: warm-reset ports on hub resume, if requested

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 18.01.2019 13:28, Jan-Marek Glogowski wrote:
Just adding some CC of commit signers from get_maintainer.pl

Am 16.01.19 um 18:07 schrieb Jan-Marek Glogowski:
On plug-in of my USB-C device, its USB_SS_PORT_LS_SS_INACTIVE
link state bit is set. Greping all the kernel for this bit shows
that the port status requests a warm-reset this way.

This just happens, if its the only device on that hub and the
hub resumes, so we don't call port_event, which would otherwise
warm-reset ports. The device works ok without this patch, if
there is already any other device connected to the hub.


I've heard about issues with bad port redrivers/retimers causing similar issues
at resume.

One reason why port_event() isn't called is that hub thread failed to report
any activity for those ports.

At bus resume we start polling the roothub, which calls
xhci_hub_status_data(). This function will check if there are any interesting
changes going on, and mark these ports.
If no activity then it asks usb core to stop roothub polling.

xhci_hub_status_data() does not check for USB_PORT_LS_SS_INACTIVE, or compliance mode,
or USB3 polling state, which all would need more attention.

Could make sense to add those.
Does the below code help your situation?

diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index e2eece6..70f9b26 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -1458,7 +1458,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
        struct xhci_hcd *xhci = hcd_to_xhci(hcd);
        int max_ports;
        struct xhci_bus_state *bus_state;
-       bool reset_change = false;
+       bool poll_roothub = false;
        struct xhci_hub *rhub;
        struct xhci_port **ports;
@@ -1491,16 +1491,23 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
                trace_xhci_hub_status_data(i, temp);
if ((temp & mask) != 0 ||
-                       (bus_state->port_c_suspend & 1 << i) ||
-                       (bus_state->resume_done[i] && time_after_eq(
+                   (bus_state->port_c_suspend & 1 << i) ||
+                   (temp & PORT_PLS_MASK) == XDEV_COMP_MODE ||
+                   (temp & PORT_PLS_MASK) == XDEV_INACTIVE ||
+                   (bus_state->resume_done[i] && time_after_eq(
                            jiffies, bus_state->resume_done[i]))) {
                        buf[(i + 1) / 8] |= 1 << (i + 1) % 8;
                        status = 1;
                }
-               if ((temp & PORT_RC))
-                       reset_change = true;
+               /*
+                * don't stop roothub polling if port is in polling state as it
+                * can end up in compliance mode without issuing any event
+                */
+               if ((temp & PORT_RC) ||
+                   (temp & PORT_PLS_MASK) == XDEV_POLLING)
+                       poll_roothub = true;
        }
-       if (!status && !reset_change) {
+       if (!status && !poll_roothub) {
                xhci_dbg(xhci, "%s: stopping port polling.\n", __func__);
                clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
        }

-Mathias



[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux