diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2012-04-03 15:24:30 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-04-09 18:43:21 -0400 |
commit | a448e4dc25303fe551e4dafe16c8c7c34f1b9d82 (patch) | |
tree | f5ae9b654defe5170fd5be0146888f57fde54751 /drivers/usb/host/ehci-hub.c | |
parent | 879d38e6bc36d73b0ac40ec9b0d839fda9fa8b1a (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/usb/host/ehci-hub.c')
-rw-r--r-- | drivers/usb/host/ehci-hub.c | 31 |
1 files changed, 16 insertions, 15 deletions
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 | |||
554 | ehci_hub_status_data (struct usb_hcd *hcd, char *buf) | 549 | ehci_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); |