Commit 5e467f6ebab151b2f0166e17348e5b85ae3c87fa

Authored by Andiry Xu
Committed by Sarah Sharp
1 parent a7114230f6

usbcore: warm reset USB3 port in SS.Inactive state

Some USB3.0 devices go to SS.Inactive state when hot plug to USB3 ports.
Warm reset the port to transition it to U0 state.

This patch fixes the issue that Kingston USB3.0 flash drive can not be
recognized when hot plug to USB3 port.

Signed-off-by: Andiry Xu <andiry.xu@amd.com>
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>

Showing 1 changed file with 46 additions and 0 deletions Side-by-side Diff

drivers/usb/core/hub.c
... ... @@ -2151,6 +2151,42 @@
2151 2151 return status;
2152 2152 }
2153 2153  
  2154 +/* Warm reset a USB3 protocol port */
  2155 +static int hub_port_warm_reset(struct usb_hub *hub, int port)
  2156 +{
  2157 + int ret;
  2158 + u16 portstatus, portchange;
  2159 +
  2160 + if (!hub_is_superspeed(hub->hdev)) {
  2161 + dev_err(hub->intfdev, "only USB3 hub support warm reset\n");
  2162 + return -EINVAL;
  2163 + }
  2164 +
  2165 + /* Warm reset the port */
  2166 + ret = set_port_feature(hub->hdev,
  2167 + port, USB_PORT_FEAT_BH_PORT_RESET);
  2168 + if (ret) {
  2169 + dev_err(hub->intfdev, "cannot warm reset port %d\n", port);
  2170 + return ret;
  2171 + }
  2172 +
  2173 + msleep(20);
  2174 + ret = hub_port_status(hub, port, &portstatus, &portchange);
  2175 +
  2176 + if (portchange & USB_PORT_STAT_C_RESET)
  2177 + clear_port_feature(hub->hdev, port, USB_PORT_FEAT_C_RESET);
  2178 +
  2179 + if (portchange & USB_PORT_STAT_C_BH_RESET)
  2180 + clear_port_feature(hub->hdev, port,
  2181 + USB_PORT_FEAT_C_BH_PORT_RESET);
  2182 +
  2183 + if (portchange & USB_PORT_STAT_C_LINK_STATE)
  2184 + clear_port_feature(hub->hdev, port,
  2185 + USB_PORT_FEAT_C_PORT_LINK_STATE);
  2186 +
  2187 + return ret;
  2188 +}
  2189 +
2154 2190 /* Check if a port is power on */
2155 2191 static int port_is_power_on(struct usb_hub *hub, unsigned portstatus)
2156 2192 {
... ... @@ -3517,6 +3553,16 @@
3517 3553 i);
3518 3554 clear_port_feature(hub->hdev, i,
3519 3555 USB_PORT_FEAT_C_PORT_CONFIG_ERROR);
  3556 + }
  3557 +
  3558 + /* Warm reset a USB3 protocol port if it's in
  3559 + * SS.Inactive state.
  3560 + */
  3561 + if (hub_is_superspeed(hub->hdev) &&
  3562 + (portstatus & USB_PORT_STAT_LINK_STATE)
  3563 + == USB_SS_PORT_LS_SS_INACTIVE) {
  3564 + dev_dbg(hub_dev, "warm reset port %d\n", i);
  3565 + hub_port_warm_reset(hub, i);
3520 3566 }
3521 3567  
3522 3568 if (connect_change)