aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/core
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2014-05-29 15:58:46 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-07-09 18:43:12 -0400
commit3cd12f91514da6893954de479dc60b16d3b381f4 (patch)
tree3209b05551050ac2e1da7f79ca96bc49298f9ac9 /drivers/usb/core
parent51df62ff74b371866c1006dee887a8e42838c1f2 (diff)
usb: force warm reset to break link re-connect livelock
Resuming a powered down port sometimes results in the port state being stuck in the training sequence. hub 3-0:1.0: debounce: port 1: total 2000ms stable 0ms status 0x2e0 port1: can't get reconnection after setting port power on, status -110 hub 3-0:1.0: port 1 status 0000.02e0 after resume, -19 usb 3-1: can't resume, status -19 hub 3-0:1.0: logical disconnect on port 1 In the case above we wait for the port re-connect timeout of 2 seconds and observe that the port status is USB_SS_PORT_LS_POLLING (although it is likely toggling between this state and USB_SS_PORT_LS_RX_DETECT). This is indicative of a case where the device is failing to progress the link training state machine. It is resolved by issuing a warm reset to get the hub and device link state machines back in sync. hub 3-0:1.0: debounce: port 1: total 2000ms stable 0ms status 0x2e0 usb usb3: port1 usb_port_runtime_resume requires warm reset hub 3-0:1.0: port 1 not warm reset yet, waiting 50ms usb 3-1: reset SuperSpeed USB device number 2 using xhci_hcd After a reconnect timeout when we expect the device to be present, force a warm reset of the device. Note that we can not simply look at the link status to determine if a warm reset is required as any of the training states USB_SS_PORT_LS_POLLING, USB_SS_PORT_LS_RX_DETECT, or USB_SS_PORT_LS_COMP_MOD are valid states that do not indicate the need for warm reset by themselves. Cc: Alan Stern <stern@rowland.harvard.edu> Cc: Kukjin Kim <kgene.kim@samsung.com> Cc: Vincent Palatin <vpalatin@chromium.org> Cc: Lan Tianyu <tianyu.lan@intel.com> Cc: Ksenia Ragiadakou <burzalodowa@gmail.com> Cc: Vivek Gautam <gautam.vivek@samsung.com> Cc: Douglas Anderson <dianders@chromium.org> Cc: Felipe Balbi <balbi@ti.com> Cc: Sunil Joshi <joshi@samsung.com> Cc: Hans de Goede <hdegoede@redhat.com> Acked-by: Julius Werner <jwerner@chromium.org> Signed-off-by: Dan Williams <dan.j.williams@intel.com> Acked-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/core')
-rw-r--r--drivers/usb/core/hub.c36
-rw-r--r--drivers/usb/core/hub.h2
-rw-r--r--drivers/usb/core/port.c21
3 files changed, 39 insertions, 20 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index b90c6287bf47..88f1db27e0af 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -2587,13 +2587,20 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
2587/* Is a USB 3.0 port in the Inactive or Compliance Mode state? 2587/* Is a USB 3.0 port in the Inactive or Compliance Mode state?
2588 * Port worm reset is required to recover 2588 * Port worm reset is required to recover
2589 */ 2589 */
2590static bool hub_port_warm_reset_required(struct usb_hub *hub, u16 portstatus) 2590static bool hub_port_warm_reset_required(struct usb_hub *hub, int port1,
2591 u16 portstatus)
2591{ 2592{
2592 return hub_is_superspeed(hub->hdev) && 2593 u16 link_state;
2593 (((portstatus & USB_PORT_STAT_LINK_STATE) == 2594
2594 USB_SS_PORT_LS_SS_INACTIVE) || 2595 if (!hub_is_superspeed(hub->hdev))
2595 ((portstatus & USB_PORT_STAT_LINK_STATE) == 2596 return false;
2596 USB_SS_PORT_LS_COMP_MOD)) ; 2597
2598 if (test_bit(port1, hub->warm_reset_bits))
2599 return true;
2600
2601 link_state = portstatus & USB_PORT_STAT_LINK_STATE;
2602 return link_state == USB_SS_PORT_LS_SS_INACTIVE
2603 || link_state == USB_SS_PORT_LS_COMP_MOD;
2597} 2604}
2598 2605
2599static int hub_port_wait_reset(struct usb_hub *hub, int port1, 2606static int hub_port_wait_reset(struct usb_hub *hub, int port1,
@@ -2630,7 +2637,7 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
2630 if ((portstatus & USB_PORT_STAT_RESET)) 2637 if ((portstatus & USB_PORT_STAT_RESET))
2631 return -EBUSY; 2638 return -EBUSY;
2632 2639
2633 if (hub_port_warm_reset_required(hub, portstatus)) 2640 if (hub_port_warm_reset_required(hub, port1, portstatus))
2634 return -ENOTCONN; 2641 return -ENOTCONN;
2635 2642
2636 /* Device went away? */ 2643 /* Device went away? */
@@ -2730,9 +2737,10 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
2730 if (status < 0) 2737 if (status < 0)
2731 goto done; 2738 goto done;
2732 2739
2733 if (hub_port_warm_reset_required(hub, portstatus)) 2740 if (hub_port_warm_reset_required(hub, port1, portstatus))
2734 warm = true; 2741 warm = true;
2735 } 2742 }
2743 clear_bit(port1, hub->warm_reset_bits);
2736 2744
2737 /* Reset the port */ 2745 /* Reset the port */
2738 for (i = 0; i < PORT_RESET_TRIES; i++) { 2746 for (i = 0; i < PORT_RESET_TRIES; i++) {
@@ -2769,7 +2777,8 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
2769 &portstatus, &portchange) < 0) 2777 &portstatus, &portchange) < 0)
2770 goto done; 2778 goto done;
2771 2779
2772 if (!hub_port_warm_reset_required(hub, portstatus)) 2780 if (!hub_port_warm_reset_required(hub, port1,
2781 portstatus))
2773 goto done; 2782 goto done;
2774 2783
2775 /* 2784 /*
@@ -2856,8 +2865,13 @@ static int check_port_resume_type(struct usb_device *udev,
2856{ 2865{
2857 struct usb_port *port_dev = hub->ports[port1 - 1]; 2866 struct usb_port *port_dev = hub->ports[port1 - 1];
2858 2867
2868 /* Is a warm reset needed to recover the connection? */
2869 if (status == 0 && udev->reset_resume
2870 && hub_port_warm_reset_required(hub, port1, portstatus)) {
2871 /* pass */;
2872 }
2859 /* Is the device still present? */ 2873 /* Is the device still present? */
2860 if (status || port_is_suspended(hub, portstatus) || 2874 else if (status || port_is_suspended(hub, portstatus) ||
2861 !port_is_power_on(hub, portstatus) || 2875 !port_is_power_on(hub, portstatus) ||
2862 !(portstatus & USB_PORT_STAT_CONNECTION)) { 2876 !(portstatus & USB_PORT_STAT_CONNECTION)) {
2863 if (status >= 0) 2877 if (status >= 0)
@@ -4872,7 +4886,7 @@ static void port_event(struct usb_hub *hub, int port1)
4872 * Warm reset a USB3 protocol port if it's in 4886 * Warm reset a USB3 protocol port if it's in
4873 * SS.Inactive state. 4887 * SS.Inactive state.
4874 */ 4888 */
4875 if (hub_port_warm_reset_required(hub, portstatus)) { 4889 if (hub_port_warm_reset_required(hub, port1, portstatus)) {
4876 dev_dbg(&port_dev->dev, "do warm reset\n"); 4890 dev_dbg(&port_dev->dev, "do warm reset\n");
4877 if (!udev || !(portstatus & USB_PORT_STAT_CONNECTION) 4891 if (!udev || !(portstatus & USB_PORT_STAT_CONNECTION)
4878 || udev->state == USB_STATE_NOTATTACHED) { 4892 || udev->state == USB_STATE_NOTATTACHED) {
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index 326308e53961..c77d8778af4b 100644
--- a/drivers/usb/core/hub.h
+++ b/drivers/usb/core/hub.h
@@ -52,6 +52,8 @@ struct usb_hub {
52 unsigned long power_bits[1]; /* ports that are powered */ 52 unsigned long power_bits[1]; /* ports that are powered */
53 unsigned long child_usage_bits[1]; /* ports powered on for 53 unsigned long child_usage_bits[1]; /* ports powered on for
54 children */ 54 children */
55 unsigned long warm_reset_bits[1]; /* ports requesting warm
56 reset recovery */
55#if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */ 57#if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */
56#error event_bits[] is too short! 58#error event_bits[] is too short!
57#endif 59#endif
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index fe1b6d0967e3..cd3f9dc24a06 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -103,16 +103,19 @@ static int usb_port_runtime_resume(struct device *dev)
103 msleep(hub_power_on_good_delay(hub)); 103 msleep(hub_power_on_good_delay(hub));
104 if (udev && !retval) { 104 if (udev && !retval) {
105 /* 105 /*
106 * Attempt to wait for usb hub port to be reconnected in order 106 * Our preference is to simply wait for the port to reconnect,
107 * to make the resume procedure successful. The device may have 107 * as that is the lowest latency method to restart the port.
108 * disconnected while the port was powered off, so ignore the 108 * However, there are cases where toggling port power results in
109 * return status. 109 * the host port and the device port getting out of sync causing
110 * a link training live lock. Upon timeout, flag the port as
111 * needing warm reset recovery (to be performed later by
112 * usb_port_resume() as requested via usb_wakeup_notification())
110 */ 113 */
111 retval = hub_port_debounce_be_connected(hub, port1); 114 if (hub_port_debounce_be_connected(hub, port1) < 0) {
112 if (retval < 0) 115 dev_dbg(&port_dev->dev, "reconnect timeout\n");
113 dev_dbg(&port_dev->dev, "can't get reconnection after setting port power on, status %d\n", 116 if (hub_is_superspeed(hdev))
114 retval); 117 set_bit(port1, hub->warm_reset_bits);
115 retval = 0; 118 }
116 119
117 /* Force the child awake to revalidate after the power loss. */ 120 /* Force the child awake to revalidate after the power loss. */
118 if (!test_and_set_bit(port1, hub->child_usage_bits)) { 121 if (!test_and_set_bit(port1, hub->child_usage_bits)) {