usbcore: warm reset USB3 port in SS.Inactive state
[deliverable/linux.git] / drivers / usb / core / hub.c
index 93035d862c63138af6c1551d23492857e322abf6..79a58c3a2e2a3a254b48a9f2fb7ac78c5c3bd27e 100644 (file)
@@ -2151,6 +2151,42 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
        return status;
 }
 
+/* Warm reset a USB3 protocol port */
+static int hub_port_warm_reset(struct usb_hub *hub, int port)
+{
+       int ret;
+       u16 portstatus, portchange;
+
+       if (!hub_is_superspeed(hub->hdev)) {
+               dev_err(hub->intfdev, "only USB3 hub support warm reset\n");
+               return -EINVAL;
+       }
+
+       /* Warm reset the port */
+       ret = set_port_feature(hub->hdev,
+                               port, USB_PORT_FEAT_BH_PORT_RESET);
+       if (ret) {
+               dev_err(hub->intfdev, "cannot warm reset port %d\n", port);
+               return ret;
+       }
+
+       msleep(20);
+       ret = hub_port_status(hub, port, &portstatus, &portchange);
+
+       if (portchange & USB_PORT_STAT_C_RESET)
+               clear_port_feature(hub->hdev, port, USB_PORT_FEAT_C_RESET);
+
+       if (portchange & USB_PORT_STAT_C_BH_RESET)
+               clear_port_feature(hub->hdev, port,
+                                       USB_PORT_FEAT_C_BH_PORT_RESET);
+
+       if (portchange & USB_PORT_STAT_C_LINK_STATE)
+               clear_port_feature(hub->hdev, port,
+                                       USB_PORT_FEAT_C_PORT_LINK_STATE);
+
+       return ret;
+}
+
 /* Check if a port is power on */
 static int port_is_power_on(struct usb_hub *hub, unsigned portstatus)
 {
@@ -3519,6 +3555,16 @@ static void hub_events(void)
                                                USB_PORT_FEAT_C_PORT_CONFIG_ERROR);
                        }
 
+                       /* Warm reset a USB3 protocol port if it's in
+                        * SS.Inactive state.
+                        */
+                       if (hub_is_superspeed(hub->hdev) &&
+                               (portstatus & USB_PORT_STAT_LINK_STATE)
+                                       == USB_SS_PORT_LS_SS_INACTIVE) {
+                               dev_dbg(hub_dev, "warm reset port %d\n", i);
+                               hub_port_warm_reset(hub, i);
+                       }
+
                        if (connect_change)
                                hub_port_connect_change(hub, i,
                                                portstatus, portchange);
This page took 0.024259 seconds and 5 git commands to generate.