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:
committed by
Greg Kroah-Hartman
parent
0458d5b4c9
commit
383975d765
@@ -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;
|
||||
|
Reference in New Issue
Block a user