diff options
-rw-r--r-- | drivers/usb/core/hcd-pci.c | 12 | ||||
-rw-r--r-- | drivers/usb/core/hcd.c | 5 | ||||
-rw-r--r-- | include/linux/usb/hcd.h | 2 |
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 | } |
2000 | EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub); | 2003 | EXPORT_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? */ |