On Wed, 11 Apr 2012, Chen Peter-B29397 wrote: > > A better approach might be to have ehci_bus_resume check the port > > statuses before turning on CMD_RUN, and re-suspend any enabled port > > that isn't already suspended. Then when the controller starts sending > > SOF packets, the host shouldn't think the device had disconnected. > > Would that solve your problem? What about this patch? Alan Stern drivers/usb/host/ehci-hub.c | 63 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 4 deletions(-) Index: usb-3.4/drivers/usb/host/ehci-hub.c =================================================================== --- usb-3.4.orig/drivers/usb/host/ehci-hub.c +++ usb-3.4/drivers/usb/host/ehci-hub.c @@ -329,6 +329,59 @@ static int ehci_bus_suspend (struct usb_ } +static void ehci_resuspend_ports(struct ehci_hcd *ehci) +{ + u32 temp; + int i; + unsigned long resuspend_needed = 0; + + /* + * If a remote wakeup request was received, the controller will + * already have begun resume signalling on that port. Some buggy + * controllers (e.g., Synopsys) automatically turn off the resume + * signal after 20 ms, leaving the port active and using high-speed + * signalling. But since CMD_RUN has been turned off, the device + * will not have received any SOF packets, so it will have gone + * back into suspend, using full-speed signalling. As a result, + * when the controller starts sending SOF packets again, it will + * think the device has disconnected. + * + * To avoid this problem, check to see if any enabled ports are + * resumed or resuming, and force them back into suspend before + * turning on CMD_RUN. + */ + i = HCS_N_PORTS(ehci->hcs_params); + while (i--) { + temp = ehci_readl(ehci, &ehci->regs->port_status[i]); + temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); + if ((temp & PORT_PE) && (!(temp & PORT_SUSPEND) || + (temp & PORT_RESUME))) { + __set_bit(i, &resuspend_needed); + temp &= ~PORT_RESUME; + ehci_writel(ehci, temp, &ehci->regs->port_status[i]); + } + } + + /* Give the port time to leave suspend, then resuspend it */ + if (resuspend_needed) { + spin_unlock_irq(&ehci->lock); + msleep(3); + spin_lock_irq(&ehci->lock); + + i = HCS_N_PORTS(ehci->hcs_params); + while (i--) { + if (test_bit(i, &resuspend_needed)) { + temp = ehci_readl(ehci, + &ehci->regs->port_status[i]); + temp &= ~PORT_RWC_BITS; + temp |= PORT_SUSPEND; + ehci_writel(ehci, temp, + &ehci->regs->port_status[i]); + } + } + } +} + /* caller has locked the root hub, and should reset/reinit on error */ static int ehci_bus_resume (struct usb_hcd *hcd) { @@ -373,10 +426,6 @@ static int ehci_bus_resume (struct usb_h ehci_writel(ehci, ehci->periodic_dma, &ehci->regs->frame_list); ehci_writel(ehci, (u32) ehci->async->qh_dma, &ehci->regs->async_next); - /* restore CMD_RUN, framelist size, and irq threshold */ - ehci_writel(ehci, ehci->command, &ehci->regs->command); - ehci->rh_state = EHCI_RH_RUNNING; - /* Some controller/firmware combinations need a delay during which * they set up the port statuses. See Bugzilla #8190. */ spin_unlock_irq(&ehci->lock); @@ -402,6 +451,12 @@ static int ehci_bus_resume (struct usb_h spin_lock_irq(&ehci->lock); } + ehci_resuspend_ports(ehci); + + /* restore CMD_RUN, framelist size, and irq threshold */ + ehci_writel(ehci, ehci->command, &ehci->regs->command); + ehci->rh_state = EHCI_RH_RUNNING; + /* manually resume the ports we suspended during bus_suspend() */ i = HCS_N_PORTS (ehci->hcs_params); while (i--) { -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html