RE: Remote wakeup timing

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux