aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2012-04-03 15:24:30 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-04-09 18:43:21 -0400
commita448e4dc25303fe551e4dafe16c8c7c34f1b9d82 (patch)
treef5ae9b654defe5170fd5be0146888f57fde54751 /drivers
parent879d38e6bc36d73b0ac40ec9b0d839fda9fa8b1a (diff)
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>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/host/ehci-hcd.c3
-rw-r--r--drivers/usb/host/ehci-hub.c31
-rw-r--r--drivers/usb/host/ehci-tegra.c2
-rw-r--r--drivers/usb/host/ehci.h2
4 files changed, 23 insertions, 15 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 057cdda7a489..806cc95317aa 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -347,6 +347,8 @@ static int ehci_reset (struct ehci_hcd *ehci)
347 if (ehci->debug) 347 if (ehci->debug)
348 dbgp_external_startup(); 348 dbgp_external_startup();
349 349
350 ehci->port_c_suspend = ehci->suspended_ports =
351 ehci->resuming_ports = 0;
350 return retval; 352 return retval;
351} 353}
352 354
@@ -939,6 +941,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
939 * like usb_port_resume() does. 941 * like usb_port_resume() does.
940 */ 942 */
941 ehci->reset_done[i] = jiffies + msecs_to_jiffies(25); 943 ehci->reset_done[i] = jiffies + msecs_to_jiffies(25);
944 set_bit(i, &ehci->resuming_ports);
942 ehci_dbg (ehci, "port %d remote wakeup\n", i + 1); 945 ehci_dbg (ehci, "port %d remote wakeup\n", i + 1);
943 mod_timer(&hcd->rh_timer, ehci->reset_done[i]); 946 mod_timer(&hcd->rh_timer, ehci->reset_done[i]);
944 } 947 }
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index 256fbd42e48c..38fe07623152 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -223,15 +223,10 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
223 * remote wakeup, we must fail the suspend. 223 * remote wakeup, we must fail the suspend.
224 */ 224 */
225 if (hcd->self.root_hub->do_remote_wakeup) { 225 if (hcd->self.root_hub->do_remote_wakeup) {
226 port = HCS_N_PORTS(ehci->hcs_params); 226 if (ehci->resuming_ports) {
227 while (port--) { 227 spin_unlock_irq(&ehci->lock);
228 if (ehci->reset_done[port] != 0) { 228 ehci_dbg(ehci, "suspend failed because a port is resuming\n");
229 spin_unlock_irq(&ehci->lock); 229 return -EBUSY;
230 ehci_dbg(ehci, "suspend failed because "
231 "port %d is resuming\n",
232 port + 1);
233 return -EBUSY;
234 }
235 } 230 }
236 } 231 }
237 232
@@ -554,16 +549,12 @@ static int
554ehci_hub_status_data (struct usb_hcd *hcd, char *buf) 549ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
555{ 550{
556 struct ehci_hcd *ehci = hcd_to_ehci (hcd); 551 struct ehci_hcd *ehci = hcd_to_ehci (hcd);
557 u32 temp, status = 0; 552 u32 temp, status;
558 u32 mask; 553 u32 mask;
559 int ports, i, retval = 1; 554 int ports, i, retval = 1;
560 unsigned long flags; 555 unsigned long flags;
561 u32 ppcd = 0; 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 /* init status to no-changes */ 558 /* init status to no-changes */
568 buf [0] = 0; 559 buf [0] = 0;
569 ports = HCS_N_PORTS (ehci->hcs_params); 560 ports = HCS_N_PORTS (ehci->hcs_params);
@@ -572,6 +563,11 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
572 retval++; 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 /* Some boards (mostly VIA?) report bogus overcurrent indications, 571 /* Some boards (mostly VIA?) report bogus overcurrent indications,
576 * causing massive log spam unless we completely ignore them. It 572 * causing massive log spam unless we completely ignore them. It
577 * may be relevant that VIA VT8235 controllers, where PORT_POWER is 573 * may be relevant that VIA VT8235 controllers, where PORT_POWER is
@@ -846,6 +842,7 @@ static int ehci_hub_control (
846 ehci_writel(ehci, 842 ehci_writel(ehci,
847 temp & ~(PORT_RWC_BITS | PORT_RESUME), 843 temp & ~(PORT_RWC_BITS | PORT_RESUME),
848 status_reg); 844 status_reg);
845 clear_bit(wIndex, &ehci->resuming_ports);
849 retval = handshake(ehci, status_reg, 846 retval = handshake(ehci, status_reg,
850 PORT_RESUME, 0, 2000 /* 2msec */); 847 PORT_RESUME, 0, 2000 /* 2msec */);
851 if (retval != 0) { 848 if (retval != 0) {
@@ -864,6 +861,7 @@ static int ehci_hub_control (
864 ehci->reset_done[wIndex])) { 861 ehci->reset_done[wIndex])) {
865 status |= USB_PORT_STAT_C_RESET << 16; 862 status |= USB_PORT_STAT_C_RESET << 16;
866 ehci->reset_done [wIndex] = 0; 863 ehci->reset_done [wIndex] = 0;
864 clear_bit(wIndex, &ehci->resuming_ports);
867 865
868 /* force reset to complete */ 866 /* force reset to complete */
869 ehci_writel(ehci, temp & ~(PORT_RWC_BITS | PORT_RESET), 867 ehci_writel(ehci, temp & ~(PORT_RWC_BITS | PORT_RESET),
@@ -884,8 +882,10 @@ static int ehci_hub_control (
884 ehci_readl(ehci, status_reg)); 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 ehci->reset_done[wIndex] = 0; 886 ehci->reset_done[wIndex] = 0;
887 clear_bit(wIndex, &ehci->resuming_ports);
888 }
889 889
890 /* transfer dedicated ports to the companion hc */ 890 /* transfer dedicated ports to the companion hc */
891 if ((temp & PORT_CONNECT) && 891 if ((temp & PORT_CONNECT) &&
@@ -920,6 +920,7 @@ static int ehci_hub_control (
920 status |= USB_PORT_STAT_SUSPEND; 920 status |= USB_PORT_STAT_SUSPEND;
921 } else if (test_bit(wIndex, &ehci->suspended_ports)) { 921 } else if (test_bit(wIndex, &ehci->suspended_ports)) {
922 clear_bit(wIndex, &ehci->suspended_ports); 922 clear_bit(wIndex, &ehci->suspended_ports);
923 clear_bit(wIndex, &ehci->resuming_ports);
923 ehci->reset_done[wIndex] = 0; 924 ehci->reset_done[wIndex] = 0;
924 if (temp & PORT_PE) 925 if (temp & PORT_PE)
925 set_bit(wIndex, &ehci->port_c_suspend); 926 set_bit(wIndex, &ehci->port_c_suspend);
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c
index 3de48a2d7955..73544bd440bd 100644
--- a/drivers/usb/host/ehci-tegra.c
+++ b/drivers/usb/host/ehci-tegra.c
@@ -224,6 +224,7 @@ static int tegra_ehci_hub_control(
224 temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); 224 temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
225 /* start resume signalling */ 225 /* start resume signalling */
226 ehci_writel(ehci, temp | PORT_RESUME, status_reg); 226 ehci_writel(ehci, temp | PORT_RESUME, status_reg);
227 set_bit(wIndex-1, &ehci->resuming_ports);
227 228
228 spin_unlock_irqrestore(&ehci->lock, flags); 229 spin_unlock_irqrestore(&ehci->lock, flags);
229 msleep(20); 230 msleep(20);
@@ -236,6 +237,7 @@ static int tegra_ehci_hub_control(
236 pr_err("%s: timeout waiting for SUSPEND\n", __func__); 237 pr_err("%s: timeout waiting for SUSPEND\n", __func__);
237 238
238 ehci->reset_done[wIndex-1] = 0; 239 ehci->reset_done[wIndex-1] = 0;
240 clear_bit(wIndex-1, &ehci->resuming_ports);
239 241
240 tegra->port_resuming = 1; 242 tegra->port_resuming = 1;
241 goto done; 243 goto done;
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 8f9acbc96fde..2694ed6558d2 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -117,6 +117,8 @@ struct ehci_hcd { /* one per controller */
117 the change-suspend feature turned on */ 117 the change-suspend feature turned on */
118 unsigned long suspended_ports; /* which ports are 118 unsigned long suspended_ports; /* which ports are
119 suspended */ 119 suspended */
120 unsigned long resuming_ports; /* which ports have
121 started to resume */
120 122
121 /* per-HC memory pools (could be per-bus, but ...) */ 123 /* per-HC memory pools (could be per-bus, but ...) */
122 struct dma_pool *qh_pool; /* qh per active urb */ 124 struct dma_pool *qh_pool; /* qh per active urb */