USB: EHCI, OHCI: handover changes

This patch (as887) changes the way ehci-hcd and ohci-hcd handle a loss
of VBUS power during suspend.  In order for the USB-persist facility
to work correctly, it is necessary for low- and full-speed devices
attached to a high-speed port to be handed back to the companion
controller during resume processing.

This entails three changes: adding code to ehci-hcd to perform the
handover, removing code from ohci-hcd to turn off ports during
root-hub reinit, and adding code to ohci-hcd to turn on ports during
PCI controller resume.  (Other bus glue resume methods for platforms
supporting high-speed controllers would need a similar change, if any
existed.)

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
Alan Stern
2007-05-04 11:52:40 -04:00
committed by Greg Kroah-Hartman
parent 0458d5b4c9
commit 383975d765
6 changed files with 145 additions and 28 deletions

View File

@@ -28,6 +28,87 @@
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_USB_PERSIST
static int ehci_hub_control(
struct usb_hcd *hcd,
u16 typeReq,
u16 wValue,
u16 wIndex,
char *buf,
u16 wLength
);
/* After a power loss, ports that were owned by the companion must be
* reset so that the companion can still own them.
*/
static void ehci_handover_companion_ports(struct ehci_hcd *ehci)
{
u32 __iomem *reg;
u32 status;
int port;
__le32 buf;
struct usb_hcd *hcd = ehci_to_hcd(ehci);
if (!ehci->owned_ports)
return;
/* Give the connections some time to appear */
msleep(20);
port = HCS_N_PORTS(ehci->hcs_params);
while (port--) {
if (test_bit(port, &ehci->owned_ports)) {
reg = &ehci->regs->port_status[port];
status = ehci_readl(ehci, reg);
/* Port already owned by companion? */
if (status & PORT_OWNER)
clear_bit(port, &ehci->owned_ports);
else
ehci_hub_control(hcd, SetPortFeature,
USB_PORT_FEAT_RESET, port + 1,
NULL, 0);
}
}
if (!ehci->owned_ports)
return;
msleep(90); /* Wait for resets to complete */
port = HCS_N_PORTS(ehci->hcs_params);
while (port--) {
if (test_bit(port, &ehci->owned_ports)) {
ehci_hub_control(hcd, GetPortStatus,
0, port + 1,
(char *) &buf, sizeof(buf));
/* The companion should now own the port,
* but if something went wrong the port must not
* remain enabled.
*/
reg = &ehci->regs->port_status[port];
status = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
if (status & PORT_OWNER)
ehci_writel(ehci, status | PORT_CSC, reg);
else {
ehci_dbg(ehci, "failed handover port %d: %x\n",
port + 1, status);
ehci_writel(ehci, status & ~PORT_PE, reg);
}
}
}
ehci->owned_ports = 0;
}
#else /* CONFIG_USB_PERSIST */
static inline void ehci_handover_companion_ports(struct ehci_hcd *ehci)
{ }
#endif
#ifdef CONFIG_PM
static int ehci_bus_suspend (struct usb_hcd *hcd)
@@ -60,14 +141,16 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
* then manually resume them in the bus_resume() routine.
*/
ehci->bus_suspended = 0;
ehci->owned_ports = 0;
while (port--) {
u32 __iomem *reg = &ehci->regs->port_status [port];
u32 t1 = ehci_readl(ehci, reg) & ~PORT_RWC_BITS;
u32 t2 = t1;
/* keep track of which ports we suspend */
if ((t1 & PORT_PE) && !(t1 & PORT_OWNER) &&
!(t1 & PORT_SUSPEND)) {
if (t1 & PORT_OWNER)
set_bit(port, &ehci->owned_ports);
else if ((t1 & PORT_PE) && !(t1 & PORT_SUSPEND)) {
t2 |= PORT_SUSPEND;
set_bit(port, &ehci->bus_suspended);
}
@@ -108,6 +191,7 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
u32 temp;
u32 power_okay;
int i;
if (time_before (jiffies, ehci->next_statechange))
@@ -120,8 +204,9 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
* the last user of the controller, not reset/pm hardware keeping
* state we gave to it.
*/
temp = ehci_readl(ehci, &ehci->regs->intr_enable);
ehci_dbg(ehci, "resume root hub%s\n", temp ? "" : " after power loss");
power_okay = ehci_readl(ehci, &ehci->regs->intr_enable);
ehci_dbg(ehci, "resume root hub%s\n",
power_okay ? "" : " after power loss");
/* at least some APM implementations will try to deliver
* IRQs right away, so delay them until we're ready.
@@ -184,6 +269,9 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable);
spin_unlock_irq (&ehci->lock);
if (!power_okay)
ehci_handover_companion_ports(ehci);
return 0;
}
@@ -448,7 +536,8 @@ static int ehci_hub_control (
) {
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
int ports = HCS_N_PORTS (ehci->hcs_params);
u32 __iomem *status_reg = &ehci->regs->port_status[wIndex - 1];
u32 __iomem *status_reg = &ehci->regs->port_status[
(wIndex & 0xff) - 1];
u32 temp, status;
unsigned long flags;
int retval = 0;