mirror of
https://github.com/FEX-Emu/linux.git
synced 2024-12-27 03:47:43 +00:00
USB: EHCI: add delay during suspend to prevent erroneous wakeups
High-speed USB connections revert back to full-speed signalling when the device goes into suspend. This takes several milliseconds, and during that time it's not possible to tell reliably whether the device has been disconnected. On some platforms, the Wake-On-Disconnect circuitry gets confused during this intermediate state. It generates a false wakeup signal, which can prevent the controller from going to sleep. To avoid this problem, this patch adds a 5-ms delay to the ehci_bus_suspend() routine if any ports have to switch over to full-speed signalling. (Actually, the delay was already present for devices using a particular kind of PHY power management; the patch merely causes the delay to be used more widely.) Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Reviewed-by: Peter Chen <Peter.Chen@freescale.com> CC: <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
6d0abeca32
commit
3e8d6d85ad
@ -238,6 +238,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
|
|||||||
int port;
|
int port;
|
||||||
int mask;
|
int mask;
|
||||||
int changed;
|
int changed;
|
||||||
|
bool fs_idle_delay;
|
||||||
|
|
||||||
ehci_dbg(ehci, "suspend root hub\n");
|
ehci_dbg(ehci, "suspend root hub\n");
|
||||||
|
|
||||||
@ -272,6 +273,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
|
|||||||
ehci->bus_suspended = 0;
|
ehci->bus_suspended = 0;
|
||||||
ehci->owned_ports = 0;
|
ehci->owned_ports = 0;
|
||||||
changed = 0;
|
changed = 0;
|
||||||
|
fs_idle_delay = false;
|
||||||
port = HCS_N_PORTS(ehci->hcs_params);
|
port = HCS_N_PORTS(ehci->hcs_params);
|
||||||
while (port--) {
|
while (port--) {
|
||||||
u32 __iomem *reg = &ehci->regs->port_status [port];
|
u32 __iomem *reg = &ehci->regs->port_status [port];
|
||||||
@ -300,16 +302,32 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (t1 != t2) {
|
if (t1 != t2) {
|
||||||
|
/*
|
||||||
|
* On some controllers, Wake-On-Disconnect will
|
||||||
|
* generate false wakeup signals until the bus
|
||||||
|
* switches over to full-speed idle. For their
|
||||||
|
* sake, add a delay if we need one.
|
||||||
|
*/
|
||||||
|
if ((t2 & PORT_WKDISC_E) &&
|
||||||
|
ehci_port_speed(ehci, t2) ==
|
||||||
|
USB_PORT_STAT_HIGH_SPEED)
|
||||||
|
fs_idle_delay = true;
|
||||||
ehci_writel(ehci, t2, reg);
|
ehci_writel(ehci, t2, reg);
|
||||||
changed = 1;
|
changed = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
spin_unlock_irq(&ehci->lock);
|
||||||
|
|
||||||
|
if ((changed && ehci->has_tdi_phy_lpm) || fs_idle_delay) {
|
||||||
|
/*
|
||||||
|
* Wait for HCD to enter low-power mode or for the bus
|
||||||
|
* to switch to full-speed idle.
|
||||||
|
*/
|
||||||
|
usleep_range(5000, 5500);
|
||||||
|
}
|
||||||
|
|
||||||
if (changed && ehci->has_tdi_phy_lpm) {
|
if (changed && ehci->has_tdi_phy_lpm) {
|
||||||
spin_unlock_irq(&ehci->lock);
|
|
||||||
msleep(5); /* 5 ms for HCD to enter low-power mode */
|
|
||||||
spin_lock_irq(&ehci->lock);
|
spin_lock_irq(&ehci->lock);
|
||||||
|
|
||||||
port = HCS_N_PORTS(ehci->hcs_params);
|
port = HCS_N_PORTS(ehci->hcs_params);
|
||||||
while (port--) {
|
while (port--) {
|
||||||
u32 __iomem *hostpc_reg = &ehci->regs->hostpc[port];
|
u32 __iomem *hostpc_reg = &ehci->regs->hostpc[port];
|
||||||
@ -322,8 +340,8 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
|
|||||||
port, (t3 & HOSTPC_PHCD) ?
|
port, (t3 & HOSTPC_PHCD) ?
|
||||||
"succeeded" : "failed");
|
"succeeded" : "failed");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
spin_unlock_irq(&ehci->lock);
|
spin_unlock_irq(&ehci->lock);
|
||||||
|
}
|
||||||
|
|
||||||
/* Apparently some devices need a >= 1-uframe delay here */
|
/* Apparently some devices need a >= 1-uframe delay here */
|
||||||
if (ehci->bus_suspended)
|
if (ehci->bus_suspended)
|
||||||
|
Loading…
Reference in New Issue
Block a user