diff options
Diffstat (limited to 'drivers/usb/host/xhci-hub.c')
-rw-r--r-- | drivers/usb/host/xhci-hub.c | 47 |
1 files changed, 42 insertions, 5 deletions
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 0230965fb78c..f980c239eded 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c | |||
@@ -733,8 +733,30 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, | |||
733 | if ((raw_port_status & PORT_RESET) || | 733 | if ((raw_port_status & PORT_RESET) || |
734 | !(raw_port_status & PORT_PE)) | 734 | !(raw_port_status & PORT_PE)) |
735 | return 0xffffffff; | 735 | return 0xffffffff; |
736 | if (time_after_eq(jiffies, | 736 | /* did port event handler already start resume timing? */ |
737 | bus_state->resume_done[wIndex])) { | 737 | if (!bus_state->resume_done[wIndex]) { |
738 | /* If not, maybe we are in a host initated resume? */ | ||
739 | if (test_bit(wIndex, &bus_state->resuming_ports)) { | ||
740 | /* Host initated resume doesn't time the resume | ||
741 | * signalling using resume_done[]. | ||
742 | * It manually sets RESUME state, sleeps 20ms | ||
743 | * and sets U0 state. This should probably be | ||
744 | * changed, but not right now. | ||
745 | */ | ||
746 | } else { | ||
747 | /* port resume was discovered now and here, | ||
748 | * start resume timing | ||
749 | */ | ||
750 | unsigned long timeout = jiffies + | ||
751 | msecs_to_jiffies(USB_RESUME_TIMEOUT); | ||
752 | |||
753 | set_bit(wIndex, &bus_state->resuming_ports); | ||
754 | bus_state->resume_done[wIndex] = timeout; | ||
755 | mod_timer(&hcd->rh_timer, timeout); | ||
756 | } | ||
757 | /* Has resume been signalled for USB_RESUME_TIME yet? */ | ||
758 | } else if (time_after_eq(jiffies, | ||
759 | bus_state->resume_done[wIndex])) { | ||
738 | int time_left; | 760 | int time_left; |
739 | 761 | ||
740 | xhci_dbg(xhci, "Resume USB2 port %d\n", | 762 | xhci_dbg(xhci, "Resume USB2 port %d\n", |
@@ -775,13 +797,26 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, | |||
775 | } else { | 797 | } else { |
776 | /* | 798 | /* |
777 | * The resume has been signaling for less than | 799 | * The resume has been signaling for less than |
778 | * 20ms. Report the port status as SUSPEND, | 800 | * USB_RESUME_TIME. Report the port status as SUSPEND, |
779 | * let the usbcore check port status again | 801 | * let the usbcore check port status again and clear |
780 | * and clear resume signaling later. | 802 | * resume signaling later. |
781 | */ | 803 | */ |
782 | status |= USB_PORT_STAT_SUSPEND; | 804 | status |= USB_PORT_STAT_SUSPEND; |
783 | } | 805 | } |
784 | } | 806 | } |
807 | /* | ||
808 | * Clear stale usb2 resume signalling variables in case port changed | ||
809 | * state during resume signalling. For example on error | ||
810 | */ | ||
811 | if ((bus_state->resume_done[wIndex] || | ||
812 | test_bit(wIndex, &bus_state->resuming_ports)) && | ||
813 | (raw_port_status & PORT_PLS_MASK) != XDEV_U3 && | ||
814 | (raw_port_status & PORT_PLS_MASK) != XDEV_RESUME) { | ||
815 | bus_state->resume_done[wIndex] = 0; | ||
816 | clear_bit(wIndex, &bus_state->resuming_ports); | ||
817 | } | ||
818 | |||
819 | |||
785 | if ((raw_port_status & PORT_PLS_MASK) == XDEV_U0 && | 820 | if ((raw_port_status & PORT_PLS_MASK) == XDEV_U0 && |
786 | (raw_port_status & PORT_POWER)) { | 821 | (raw_port_status & PORT_POWER)) { |
787 | if (bus_state->suspended_ports & (1 << wIndex)) { | 822 | if (bus_state->suspended_ports & (1 << wIndex)) { |
@@ -1115,6 +1150,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, | |||
1115 | if ((temp & PORT_PE) == 0) | 1150 | if ((temp & PORT_PE) == 0) |
1116 | goto error; | 1151 | goto error; |
1117 | 1152 | ||
1153 | set_bit(wIndex, &bus_state->resuming_ports); | ||
1118 | xhci_set_link_state(xhci, port_array, wIndex, | 1154 | xhci_set_link_state(xhci, port_array, wIndex, |
1119 | XDEV_RESUME); | 1155 | XDEV_RESUME); |
1120 | spin_unlock_irqrestore(&xhci->lock, flags); | 1156 | spin_unlock_irqrestore(&xhci->lock, flags); |
@@ -1122,6 +1158,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, | |||
1122 | spin_lock_irqsave(&xhci->lock, flags); | 1158 | spin_lock_irqsave(&xhci->lock, flags); |
1123 | xhci_set_link_state(xhci, port_array, wIndex, | 1159 | xhci_set_link_state(xhci, port_array, wIndex, |
1124 | XDEV_U0); | 1160 | XDEV_U0); |
1161 | clear_bit(wIndex, &bus_state->resuming_ports); | ||
1125 | } | 1162 | } |
1126 | bus_state->port_c_suspend |= 1 << wIndex; | 1163 | bus_state->port_c_suspend |= 1 << wIndex; |
1127 | 1164 | ||