aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/core/hcd-pci.c12
-rw-r--r--drivers/usb/core/hcd.c5
-rw-r--r--include/linux/usb/hcd.h2
3 files changed, 18 insertions, 1 deletions
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index e387e394f876..352577baa53d 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -388,8 +388,20 @@ static int hcd_pci_suspend(struct device *dev)
388 if (hcd->driver->pci_suspend) { 388 if (hcd->driver->pci_suspend) {
389 bool do_wakeup = device_may_wakeup(dev); 389 bool do_wakeup = device_may_wakeup(dev);
390 390
391 /* Optimization: Don't suspend if a root-hub wakeup is
392 * pending and it would cause the HCD to wake up anyway.
393 */
394 if (do_wakeup && HCD_WAKEUP_PENDING(hcd))
395 return -EBUSY;
391 retval = hcd->driver->pci_suspend(hcd, do_wakeup); 396 retval = hcd->driver->pci_suspend(hcd, do_wakeup);
392 suspend_report_result(hcd->driver->pci_suspend, retval); 397 suspend_report_result(hcd->driver->pci_suspend, retval);
398
399 /* Check again in case wakeup raced with pci_suspend */
400 if (retval == 0 && do_wakeup && HCD_WAKEUP_PENDING(hcd)) {
401 if (hcd->driver->pci_resume)
402 hcd->driver->pci_resume(hcd, false);
403 retval = -EBUSY;
404 }
393 if (retval) 405 if (retval)
394 return retval; 406 return retval;
395 } 407 }
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index f2fe7c8e991d..0358c05e6e8a 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -1940,6 +1940,7 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg)
1940 1940
1941 dev_dbg(&rhdev->dev, "usb %s%s\n", 1941 dev_dbg(&rhdev->dev, "usb %s%s\n",
1942 (msg.event & PM_EVENT_AUTO ? "auto-" : ""), "resume"); 1942 (msg.event & PM_EVENT_AUTO ? "auto-" : ""), "resume");
1943 clear_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags);
1943 if (!hcd->driver->bus_resume) 1944 if (!hcd->driver->bus_resume)
1944 return -ENOENT; 1945 return -ENOENT;
1945 if (hcd->state == HC_STATE_RUNNING) 1946 if (hcd->state == HC_STATE_RUNNING)
@@ -1993,8 +1994,10 @@ void usb_hcd_resume_root_hub (struct usb_hcd *hcd)
1993 unsigned long flags; 1994 unsigned long flags;
1994 1995
1995 spin_lock_irqsave (&hcd_root_hub_lock, flags); 1996 spin_lock_irqsave (&hcd_root_hub_lock, flags);
1996 if (hcd->rh_registered) 1997 if (hcd->rh_registered) {
1998 set_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags);
1997 queue_work(pm_wq, &hcd->wakeup_work); 1999 queue_work(pm_wq, &hcd->wakeup_work);
2000 }
1998 spin_unlock_irqrestore (&hcd_root_hub_lock, flags); 2001 spin_unlock_irqrestore (&hcd_root_hub_lock, flags);
1999} 2002}
2000EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub); 2003EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub);
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index ae10020b4023..3b571f1ffbb3 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -98,6 +98,7 @@ struct usb_hcd {
98#define HCD_FLAG_SAW_IRQ 1 98#define HCD_FLAG_SAW_IRQ 1
99#define HCD_FLAG_POLL_RH 2 /* poll for rh status? */ 99#define HCD_FLAG_POLL_RH 2 /* poll for rh status? */
100#define HCD_FLAG_POLL_PENDING 3 /* status has changed? */ 100#define HCD_FLAG_POLL_PENDING 3 /* status has changed? */
101#define HCD_FLAG_WAKEUP_PENDING 4 /* root hub is resuming? */
101 102
102 /* The flags can be tested using these macros; they are likely to 103 /* The flags can be tested using these macros; they are likely to
103 * be slightly faster than test_bit(). 104 * be slightly faster than test_bit().
@@ -106,6 +107,7 @@ struct usb_hcd {
106#define HCD_SAW_IRQ(hcd) ((hcd)->flags & (1U << HCD_FLAG_SAW_IRQ)) 107#define HCD_SAW_IRQ(hcd) ((hcd)->flags & (1U << HCD_FLAG_SAW_IRQ))
107#define HCD_POLL_RH(hcd) ((hcd)->flags & (1U << HCD_FLAG_POLL_RH)) 108#define HCD_POLL_RH(hcd) ((hcd)->flags & (1U << HCD_FLAG_POLL_RH))
108#define HCD_POLL_PENDING(hcd) ((hcd)->flags & (1U << HCD_FLAG_POLL_PENDING)) 109#define HCD_POLL_PENDING(hcd) ((hcd)->flags & (1U << HCD_FLAG_POLL_PENDING))
110#define HCD_WAKEUP_PENDING(hcd) ((hcd)->flags & (1U << HCD_FLAG_WAKEUP_PENDING))
109 111
110 /* Flags that get set only during HCD registration or removal. */ 112 /* Flags that get set only during HCD registration or removal. */
111 unsigned rh_registered:1;/* is root hub registered? */ 113 unsigned rh_registered:1;/* is root hub registered? */