aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2013-08-01 17:09:28 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-08-01 17:37:07 -0400
commit6753f4cf29046f4a2ae68c3a93bf6e6e6dce9fb7 (patch)
tree37d15d48d057c017b1b1daac4a4ad40339ddb7e4
parent3a20446f02bfb71d68ae9ec673268bb7823f878c (diff)
USB: EHCI: don't depend on hardware for tracking port resets and resumes
In theory, an EHCI controller can turn off the PORT_RESUME or PORT_RESET bits in a port status register all by itself (and some controllers actually do this). We shouldn't depend on these bits being set correctly. This patch rearranges the code in ehci-hcd that handles completion of port resets and resumes. We guarantee that ehci->reset_done[portnum] is nonzero if a reset or resume is in progress, and that the portnum bit is set in ehci->resuming_ports if the operation is a resume. (To help enforce this guarantee, the patch prevents suspended ports from being reset.) Therefore it's not necessary to look at the port status bits to learn what's going on. The patch looks bigger than it really is, because it changes the indentation level of a sizeable region of code. Most of what it actually does is interchange some tests. The only functional changes are testing reset_done and resuming_ports rather than PORT_RESUME and PORT_RESET, removing a now-unnecessary check for spontaneous resets of the PORT_RESUME and PORT_RESET bits, and preventing a suspended or resuming port from being reset. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/usb/host/ehci-hub.c60
1 files changed, 26 insertions, 34 deletions
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index 6d0e59306ed9..6e69ee1a3371 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -867,11 +867,11 @@ static int ehci_hub_control (
867 } 867 }
868 } 868 }
869 869
870 /* whoever resumes must GetPortStatus to complete it!! */ 870 /* no reset or resume pending */
871 if (temp & PORT_RESUME) { 871 if (!ehci->reset_done[wIndex]) {
872 872
873 /* Remote Wakeup received? */ 873 /* Remote Wakeup received? */
874 if (!ehci->reset_done[wIndex]) { 874 if (temp & PORT_RESUME) {
875 /* resume signaling for 20 msec */ 875 /* resume signaling for 20 msec */
876 ehci->reset_done[wIndex] = jiffies 876 ehci->reset_done[wIndex] = jiffies
877 + msecs_to_jiffies(20); 877 + msecs_to_jiffies(20);
@@ -882,35 +882,32 @@ static int ehci_hub_control (
882 ehci->reset_done[wIndex]); 882 ehci->reset_done[wIndex]);
883 } 883 }
884 884
885 /* resume completed? */ 885 /* reset or resume not yet complete */
886 else if (time_after_eq(jiffies, 886 } else if (!time_after_eq(jiffies, ehci->reset_done[wIndex])) {
887 ehci->reset_done[wIndex])) { 887 ; /* wait until it is complete */
888 clear_bit(wIndex, &ehci->suspended_ports); 888
889 set_bit(wIndex, &ehci->port_c_suspend); 889 /* resume completed */
890 ehci->reset_done[wIndex] = 0; 890 } else if (test_bit(wIndex, &ehci->resuming_ports)) {
891 usb_hcd_end_port_resume(&hcd->self, wIndex); 891 clear_bit(wIndex, &ehci->suspended_ports);
892 892 set_bit(wIndex, &ehci->port_c_suspend);
893 /* stop resume signaling */ 893 ehci->reset_done[wIndex] = 0;
894 temp &= ~(PORT_RWC_BITS | 894 usb_hcd_end_port_resume(&hcd->self, wIndex);
895 PORT_SUSPEND | PORT_RESUME); 895
896 ehci_writel(ehci, temp, status_reg); 896 /* stop resume signaling */
897 clear_bit(wIndex, &ehci->resuming_ports); 897 temp &= ~(PORT_RWC_BITS | PORT_SUSPEND | PORT_RESUME);
898 retval = ehci_handshake(ehci, status_reg, 898 ehci_writel(ehci, temp, status_reg);
899 PORT_RESUME, 0, 2000 /* 2msec */); 899 clear_bit(wIndex, &ehci->resuming_ports);
900 if (retval != 0) { 900 retval = ehci_handshake(ehci, status_reg,
901 ehci_err(ehci, 901 PORT_RESUME, 0, 2000 /* 2msec */);
902 "port %d resume error %d\n", 902 if (retval != 0) {
903 ehci_err(ehci, "port %d resume error %d\n",
903 wIndex + 1, retval); 904 wIndex + 1, retval);
904 goto error; 905 goto error;
905 }
906 temp = ehci_readl(ehci, status_reg);
907 } 906 }
908 } 907 temp = ehci_readl(ehci, status_reg);
909 908
910 /* whoever resets must GetPortStatus to complete it!! */ 909 /* whoever resets must GetPortStatus to complete it!! */
911 if ((temp & PORT_RESET) 910 } else {
912 && time_after_eq(jiffies,
913 ehci->reset_done[wIndex])) {
914 status |= USB_PORT_STAT_C_RESET << 16; 911 status |= USB_PORT_STAT_C_RESET << 16;
915 ehci->reset_done [wIndex] = 0; 912 ehci->reset_done [wIndex] = 0;
916 913
@@ -933,11 +930,6 @@ static int ehci_hub_control (
933 ehci_readl(ehci, status_reg)); 930 ehci_readl(ehci, status_reg));
934 } 931 }
935 932
936 if (!(temp & (PORT_RESUME|PORT_RESET))) {
937 ehci->reset_done[wIndex] = 0;
938 clear_bit(wIndex, &ehci->resuming_ports);
939 }
940
941 /* transfer dedicated ports to the companion hc */ 933 /* transfer dedicated ports to the companion hc */
942 if ((temp & PORT_CONNECT) && 934 if ((temp & PORT_CONNECT) &&
943 test_bit(wIndex, &ehci->companion_ports)) { 935 test_bit(wIndex, &ehci->companion_ports)) {
@@ -1058,7 +1050,7 @@ static int ehci_hub_control (
1058 status_reg); 1050 status_reg);
1059 break; 1051 break;
1060 case USB_PORT_FEAT_RESET: 1052 case USB_PORT_FEAT_RESET:
1061 if (temp & PORT_RESUME) 1053 if (temp & (PORT_SUSPEND|PORT_RESUME))
1062 goto error; 1054 goto error;
1063 /* line status bits may report this as low speed, 1055 /* line status bits may report this as low speed,
1064 * which can be fine if this root hub has a 1056 * which can be fine if this root hub has a