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

EHCI: keep track of ports being resumed and indicate in hub_status_data

This patch (as1537) adds a bit-array to ehci-hcd for keeping track of
which ports are undergoing a resume transition.  If any of the bits
are set when ehci_hub_status_data() is called, the routine will return
a nonzero value even if no ports have any status changes pending.
This will allow usbcore to handle races between root-hub suspend and
port wakeup.
Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
CC: Sarah Sharp <sarah.a.sharp@linux.intel.com>
CC: Chen Peter-B29397 <B29397@freescale.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 879d38e6
...@@ -347,6 +347,8 @@ static int ehci_reset (struct ehci_hcd *ehci) ...@@ -347,6 +347,8 @@ static int ehci_reset (struct ehci_hcd *ehci)
if (ehci->debug) if (ehci->debug)
dbgp_external_startup(); dbgp_external_startup();
ehci->port_c_suspend = ehci->suspended_ports =
ehci->resuming_ports = 0;
return retval; return retval;
} }
...@@ -939,6 +941,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) ...@@ -939,6 +941,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
* like usb_port_resume() does. * like usb_port_resume() does.
*/ */
ehci->reset_done[i] = jiffies + msecs_to_jiffies(25); ehci->reset_done[i] = jiffies + msecs_to_jiffies(25);
set_bit(i, &ehci->resuming_ports);
ehci_dbg (ehci, "port %d remote wakeup\n", i + 1); ehci_dbg (ehci, "port %d remote wakeup\n", i + 1);
mod_timer(&hcd->rh_timer, ehci->reset_done[i]); mod_timer(&hcd->rh_timer, ehci->reset_done[i]);
} }
......
...@@ -223,17 +223,12 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) ...@@ -223,17 +223,12 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
* remote wakeup, we must fail the suspend. * remote wakeup, we must fail the suspend.
*/ */
if (hcd->self.root_hub->do_remote_wakeup) { if (hcd->self.root_hub->do_remote_wakeup) {
port = HCS_N_PORTS(ehci->hcs_params); if (ehci->resuming_ports) {
while (port--) {
if (ehci->reset_done[port] != 0) {
spin_unlock_irq(&ehci->lock); spin_unlock_irq(&ehci->lock);
ehci_dbg(ehci, "suspend failed because " ehci_dbg(ehci, "suspend failed because a port is resuming\n");
"port %d is resuming\n",
port + 1);
return -EBUSY; return -EBUSY;
} }
} }
}
/* stop schedules, clean any completed work */ /* stop schedules, clean any completed work */
if (ehci->rh_state == EHCI_RH_RUNNING) if (ehci->rh_state == EHCI_RH_RUNNING)
...@@ -554,16 +549,12 @@ static int ...@@ -554,16 +549,12 @@ static int
ehci_hub_status_data (struct usb_hcd *hcd, char *buf) ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
{ {
struct ehci_hcd *ehci = hcd_to_ehci (hcd); struct ehci_hcd *ehci = hcd_to_ehci (hcd);
u32 temp, status = 0; u32 temp, status;
u32 mask; u32 mask;
int ports, i, retval = 1; int ports, i, retval = 1;
unsigned long flags; unsigned long flags;
u32 ppcd = 0; u32 ppcd = 0;
/* if !USB_SUSPEND, root hub timers won't get shut down ... */
if (ehci->rh_state != EHCI_RH_RUNNING)
return 0;
/* init status to no-changes */ /* init status to no-changes */
buf [0] = 0; buf [0] = 0;
ports = HCS_N_PORTS (ehci->hcs_params); ports = HCS_N_PORTS (ehci->hcs_params);
...@@ -572,6 +563,11 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) ...@@ -572,6 +563,11 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
retval++; retval++;
} }
/* Inform the core about resumes-in-progress by returning
* a non-zero value even if there are no status changes.
*/
status = ehci->resuming_ports;
/* Some boards (mostly VIA?) report bogus overcurrent indications, /* Some boards (mostly VIA?) report bogus overcurrent indications,
* causing massive log spam unless we completely ignore them. It * causing massive log spam unless we completely ignore them. It
* may be relevant that VIA VT8235 controllers, where PORT_POWER is * may be relevant that VIA VT8235 controllers, where PORT_POWER is
...@@ -846,6 +842,7 @@ static int ehci_hub_control ( ...@@ -846,6 +842,7 @@ static int ehci_hub_control (
ehci_writel(ehci, ehci_writel(ehci,
temp & ~(PORT_RWC_BITS | PORT_RESUME), temp & ~(PORT_RWC_BITS | PORT_RESUME),
status_reg); status_reg);
clear_bit(wIndex, &ehci->resuming_ports);
retval = handshake(ehci, status_reg, retval = handshake(ehci, status_reg,
PORT_RESUME, 0, 2000 /* 2msec */); PORT_RESUME, 0, 2000 /* 2msec */);
if (retval != 0) { if (retval != 0) {
...@@ -864,6 +861,7 @@ static int ehci_hub_control ( ...@@ -864,6 +861,7 @@ static int ehci_hub_control (
ehci->reset_done[wIndex])) { 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;
clear_bit(wIndex, &ehci->resuming_ports);
/* force reset to complete */ /* force reset to complete */
ehci_writel(ehci, temp & ~(PORT_RWC_BITS | PORT_RESET), ehci_writel(ehci, temp & ~(PORT_RWC_BITS | PORT_RESET),
...@@ -884,8 +882,10 @@ static int ehci_hub_control ( ...@@ -884,8 +882,10 @@ static int ehci_hub_control (
ehci_readl(ehci, status_reg)); ehci_readl(ehci, status_reg));
} }
if (!(temp & (PORT_RESUME|PORT_RESET))) if (!(temp & (PORT_RESUME|PORT_RESET))) {
ehci->reset_done[wIndex] = 0; 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) &&
...@@ -920,6 +920,7 @@ static int ehci_hub_control ( ...@@ -920,6 +920,7 @@ static int ehci_hub_control (
status |= USB_PORT_STAT_SUSPEND; status |= USB_PORT_STAT_SUSPEND;
} else if (test_bit(wIndex, &ehci->suspended_ports)) { } else if (test_bit(wIndex, &ehci->suspended_ports)) {
clear_bit(wIndex, &ehci->suspended_ports); clear_bit(wIndex, &ehci->suspended_ports);
clear_bit(wIndex, &ehci->resuming_ports);
ehci->reset_done[wIndex] = 0; ehci->reset_done[wIndex] = 0;
if (temp & PORT_PE) if (temp & PORT_PE)
set_bit(wIndex, &ehci->port_c_suspend); set_bit(wIndex, &ehci->port_c_suspend);
......
...@@ -224,6 +224,7 @@ static int tegra_ehci_hub_control( ...@@ -224,6 +224,7 @@ static int tegra_ehci_hub_control(
temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
/* start resume signalling */ /* start resume signalling */
ehci_writel(ehci, temp | PORT_RESUME, status_reg); ehci_writel(ehci, temp | PORT_RESUME, status_reg);
set_bit(wIndex-1, &ehci->resuming_ports);
spin_unlock_irqrestore(&ehci->lock, flags); spin_unlock_irqrestore(&ehci->lock, flags);
msleep(20); msleep(20);
...@@ -236,6 +237,7 @@ static int tegra_ehci_hub_control( ...@@ -236,6 +237,7 @@ static int tegra_ehci_hub_control(
pr_err("%s: timeout waiting for SUSPEND\n", __func__); pr_err("%s: timeout waiting for SUSPEND\n", __func__);
ehci->reset_done[wIndex-1] = 0; ehci->reset_done[wIndex-1] = 0;
clear_bit(wIndex-1, &ehci->resuming_ports);
tegra->port_resuming = 1; tegra->port_resuming = 1;
goto done; goto done;
......
...@@ -117,6 +117,8 @@ struct ehci_hcd { /* one per controller */ ...@@ -117,6 +117,8 @@ struct ehci_hcd { /* one per controller */
the change-suspend feature turned on */ the change-suspend feature turned on */
unsigned long suspended_ports; /* which ports are unsigned long suspended_ports; /* which ports are
suspended */ suspended */
unsigned long resuming_ports; /* which ports have
started to resume */
/* per-HC memory pools (could be per-bus, but ...) */ /* per-HC memory pools (could be per-bus, but ...) */
struct dma_pool *qh_pool; /* qh per active urb */ struct dma_pool *qh_pool; /* qh per active urb */
......
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