Commit a448e4dc25303fe551e4dafe16c8c7c34f1b9d82

Authored by Alan Stern
Committed by Greg Kroah-Hartman
1 parent 879d38e6bc

EHCI: keep track of ports being resumed and indicate in hub_status_data

This patch (as1537) adds a bit-array to ehci-hcd for keeping track of
which ports are undergoing a resume transition.  If any of the bits
are set when ehci_hub_status_data() is called, the routine will return
a nonzero value even if no ports have any status changes pending.
This will allow usbcore to handle races between root-hub suspend and
port wakeup.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
CC: Sarah Sharp <sarah.a.sharp@linux.intel.com>
CC: Chen Peter-B29397 <B29397@freescale.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

Showing 4 changed files with 23 additions and 15 deletions Side-by-side Diff

drivers/usb/host/ehci-hcd.c
... ... @@ -347,6 +347,8 @@
347 347 if (ehci->debug)
348 348 dbgp_external_startup();
349 349  
  350 + ehci->port_c_suspend = ehci->suspended_ports =
  351 + ehci->resuming_ports = 0;
350 352 return retval;
351 353 }
352 354  
... ... @@ -939,6 +941,7 @@
939 941 * like usb_port_resume() does.
940 942 */
941 943 ehci->reset_done[i] = jiffies + msecs_to_jiffies(25);
  944 + set_bit(i, &ehci->resuming_ports);
942 945 ehci_dbg (ehci, "port %d remote wakeup\n", i + 1);
943 946 mod_timer(&hcd->rh_timer, ehci->reset_done[i]);
944 947 }
drivers/usb/host/ehci-hub.c
... ... @@ -223,15 +223,10 @@
223 223 * remote wakeup, we must fail the suspend.
224 224 */
225 225 if (hcd->self.root_hub->do_remote_wakeup) {
226   - port = HCS_N_PORTS(ehci->hcs_params);
227   - while (port--) {
228   - if (ehci->reset_done[port] != 0) {
229   - spin_unlock_irq(&ehci->lock);
230   - ehci_dbg(ehci, "suspend failed because "
231   - "port %d is resuming\n",
232   - port + 1);
233   - return -EBUSY;
234   - }
  226 + if (ehci->resuming_ports) {
  227 + spin_unlock_irq(&ehci->lock);
  228 + ehci_dbg(ehci, "suspend failed because a port is resuming\n");
  229 + return -EBUSY;
235 230 }
236 231 }
237 232  
238 233  
... ... @@ -554,16 +549,12 @@
554 549 ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
555 550 {
556 551 struct ehci_hcd *ehci = hcd_to_ehci (hcd);
557   - u32 temp, status = 0;
  552 + u32 temp, status;
558 553 u32 mask;
559 554 int ports, i, retval = 1;
560 555 unsigned long flags;
561 556 u32 ppcd = 0;
562 557  
563   - /* if !USB_SUSPEND, root hub timers won't get shut down ... */
564   - if (ehci->rh_state != EHCI_RH_RUNNING)
565   - return 0;
566   -
567 558 /* init status to no-changes */
568 559 buf [0] = 0;
569 560 ports = HCS_N_PORTS (ehci->hcs_params);
... ... @@ -572,6 +563,11 @@
572 563 retval++;
573 564 }
574 565  
  566 + /* Inform the core about resumes-in-progress by returning
  567 + * a non-zero value even if there are no status changes.
  568 + */
  569 + status = ehci->resuming_ports;
  570 +
575 571 /* Some boards (mostly VIA?) report bogus overcurrent indications,
576 572 * causing massive log spam unless we completely ignore them. It
577 573 * may be relevant that VIA VT8235 controllers, where PORT_POWER is
... ... @@ -846,6 +842,7 @@
846 842 ehci_writel(ehci,
847 843 temp & ~(PORT_RWC_BITS | PORT_RESUME),
848 844 status_reg);
  845 + clear_bit(wIndex, &ehci->resuming_ports);
849 846 retval = handshake(ehci, status_reg,
850 847 PORT_RESUME, 0, 2000 /* 2msec */);
851 848 if (retval != 0) {
... ... @@ -864,6 +861,7 @@
864 861 ehci->reset_done[wIndex])) {
865 862 status |= USB_PORT_STAT_C_RESET << 16;
866 863 ehci->reset_done [wIndex] = 0;
  864 + clear_bit(wIndex, &ehci->resuming_ports);
867 865  
868 866 /* force reset to complete */
869 867 ehci_writel(ehci, temp & ~(PORT_RWC_BITS | PORT_RESET),
870 868  
... ... @@ -884,8 +882,10 @@
884 882 ehci_readl(ehci, status_reg));
885 883 }
886 884  
887   - if (!(temp & (PORT_RESUME|PORT_RESET)))
  885 + if (!(temp & (PORT_RESUME|PORT_RESET))) {
888 886 ehci->reset_done[wIndex] = 0;
  887 + clear_bit(wIndex, &ehci->resuming_ports);
  888 + }
889 889  
890 890 /* transfer dedicated ports to the companion hc */
891 891 if ((temp & PORT_CONNECT) &&
... ... @@ -920,6 +920,7 @@
920 920 status |= USB_PORT_STAT_SUSPEND;
921 921 } else if (test_bit(wIndex, &ehci->suspended_ports)) {
922 922 clear_bit(wIndex, &ehci->suspended_ports);
  923 + clear_bit(wIndex, &ehci->resuming_ports);
923 924 ehci->reset_done[wIndex] = 0;
924 925 if (temp & PORT_PE)
925 926 set_bit(wIndex, &ehci->port_c_suspend);
drivers/usb/host/ehci-tegra.c
... ... @@ -224,6 +224,7 @@
224 224 temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
225 225 /* start resume signalling */
226 226 ehci_writel(ehci, temp | PORT_RESUME, status_reg);
  227 + set_bit(wIndex-1, &ehci->resuming_ports);
227 228  
228 229 spin_unlock_irqrestore(&ehci->lock, flags);
229 230 msleep(20);
... ... @@ -236,6 +237,7 @@
236 237 pr_err("%s: timeout waiting for SUSPEND\n", __func__);
237 238  
238 239 ehci->reset_done[wIndex-1] = 0;
  240 + clear_bit(wIndex-1, &ehci->resuming_ports);
239 241  
240 242 tegra->port_resuming = 1;
241 243 goto done;
drivers/usb/host/ehci.h
... ... @@ -117,6 +117,8 @@
117 117 the change-suspend feature turned on */
118 118 unsigned long suspended_ports; /* which ports are
119 119 suspended */
  120 + unsigned long resuming_ports; /* which ports have
  121 + started to resume */
120 122  
121 123 /* per-HC memory pools (could be per-bus, but ...) */
122 124 struct dma_pool *qh_pool; /* qh per active urb */