Commit 6753f4cf authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

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: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 3a20446f
...@@ -867,11 +867,11 @@ static int ehci_hub_control ( ...@@ -867,11 +867,11 @@ static int ehci_hub_control (
} }
} }
/* whoever resumes must GetPortStatus to complete it!! */ /* no reset or resume pending */
if (temp & PORT_RESUME) { if (!ehci->reset_done[wIndex]) {
/* Remote Wakeup received? */ /* Remote Wakeup received? */
if (!ehci->reset_done[wIndex]) { if (temp & PORT_RESUME) {
/* resume signaling for 20 msec */ /* resume signaling for 20 msec */
ehci->reset_done[wIndex] = jiffies ehci->reset_done[wIndex] = jiffies
+ msecs_to_jiffies(20); + msecs_to_jiffies(20);
...@@ -882,35 +882,32 @@ static int ehci_hub_control ( ...@@ -882,35 +882,32 @@ static int ehci_hub_control (
ehci->reset_done[wIndex]); ehci->reset_done[wIndex]);
} }
/* resume completed? */ /* reset or resume not yet complete */
else if (time_after_eq(jiffies, } else if (!time_after_eq(jiffies, ehci->reset_done[wIndex])) {
ehci->reset_done[wIndex])) { ; /* wait until it is complete */
clear_bit(wIndex, &ehci->suspended_ports);
set_bit(wIndex, &ehci->port_c_suspend); /* resume completed */
ehci->reset_done[wIndex] = 0; } else if (test_bit(wIndex, &ehci->resuming_ports)) {
usb_hcd_end_port_resume(&hcd->self, wIndex); clear_bit(wIndex, &ehci->suspended_ports);
set_bit(wIndex, &ehci->port_c_suspend);
/* stop resume signaling */ ehci->reset_done[wIndex] = 0;
temp &= ~(PORT_RWC_BITS | usb_hcd_end_port_resume(&hcd->self, wIndex);
PORT_SUSPEND | PORT_RESUME);
ehci_writel(ehci, temp, status_reg); /* stop resume signaling */
clear_bit(wIndex, &ehci->resuming_ports); temp &= ~(PORT_RWC_BITS | PORT_SUSPEND | PORT_RESUME);
retval = ehci_handshake(ehci, status_reg, ehci_writel(ehci, temp, status_reg);
PORT_RESUME, 0, 2000 /* 2msec */); clear_bit(wIndex, &ehci->resuming_ports);
if (retval != 0) { retval = ehci_handshake(ehci, status_reg,
ehci_err(ehci, PORT_RESUME, 0, 2000 /* 2msec */);
"port %d resume error %d\n", if (retval != 0) {
ehci_err(ehci, "port %d resume error %d\n",
wIndex + 1, retval); wIndex + 1, retval);
goto error; goto error;
}
temp = ehci_readl(ehci, status_reg);
} }
} temp = ehci_readl(ehci, status_reg);
/* whoever resets must GetPortStatus to complete it!! */ /* whoever resets must GetPortStatus to complete it!! */
if ((temp & PORT_RESET) } else {
&& time_after_eq(jiffies,
ehci->reset_done[wIndex])) {
status |= USB_PORT_STAT_C_RESET << 16; status |= USB_PORT_STAT_C_RESET << 16;
ehci->reset_done [wIndex] = 0; ehci->reset_done [wIndex] = 0;
...@@ -933,11 +930,6 @@ static int ehci_hub_control ( ...@@ -933,11 +930,6 @@ static int ehci_hub_control (
ehci_readl(ehci, status_reg)); ehci_readl(ehci, status_reg));
} }
if (!(temp & (PORT_RESUME|PORT_RESET))) {
ehci->reset_done[wIndex] = 0;
clear_bit(wIndex, &ehci->resuming_ports);
}
/* transfer dedicated ports to the companion hc */ /* transfer dedicated ports to the companion hc */
if ((temp & PORT_CONNECT) && if ((temp & PORT_CONNECT) &&
test_bit(wIndex, &ehci->companion_ports)) { test_bit(wIndex, &ehci->companion_ports)) {
...@@ -1058,7 +1050,7 @@ static int ehci_hub_control ( ...@@ -1058,7 +1050,7 @@ static int ehci_hub_control (
status_reg); status_reg);
break; break;
case USB_PORT_FEAT_RESET: case USB_PORT_FEAT_RESET:
if (temp & PORT_RESUME) if (temp & (PORT_SUSPEND|PORT_RESUME))
goto error; goto error;
/* line status bits may report this as low speed, /* line status bits may report this as low speed,
* which can be fine if this root hub has a * which can be fine if this root hub has a
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment