summaryrefslogtreecommitdiff
path: root/drivers/usb/host/uhci-hcd.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/host/uhci-hcd.c')
-rw-r--r--drivers/usb/host/uhci-hcd.c66
1 files changed, 34 insertions, 32 deletions
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index fba99b120588..c8ae199cfbb8 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -294,50 +294,50 @@ __acquires(uhci->lock)
* and that remote wakeups should be enabled.
*/
egsm_enable = USBCMD_EGSM;
- uhci->RD_enable = 1;
int_enable = USBINTR_RESUME;
wakeup_enable = 1;
- /* In auto-stop mode wakeups must always be detected, but
- * Resume-Detect interrupts may be prohibited. (In the absence
- * of CONFIG_PM, they are always disallowed.)
+ /*
+ * In auto-stop mode, we must be able to detect new connections.
+ * The user can force us to poll by disabling remote wakeup;
+ * otherwise we will use the EGSM/RD mechanism.
*/
if (auto_stop) {
if (!device_may_wakeup(&rhdev->dev))
- int_enable = 0;
+ egsm_enable = int_enable = 0;
+ }
- /* In bus-suspend mode wakeups may be disabled, but if they are
- * allowed then so are Resume-Detect interrupts.
- */
- } else {
#ifdef CONFIG_PM
+ /*
+ * In bus-suspend mode, we use the wakeup setting specified
+ * for the root hub.
+ */
+ else {
if (!rhdev->do_remote_wakeup)
wakeup_enable = 0;
-#endif
}
+#endif
- /* EGSM causes the root hub to echo a 'K' signal (resume) out any
- * port which requests a remote wakeup. According to the USB spec,
- * every hub is supposed to do this. But if we are ignoring
- * remote-wakeup requests anyway then there's no point to it.
- * We also shouldn't enable EGSM if it's broken.
- */
- if (!wakeup_enable || global_suspend_mode_is_broken(uhci))
- egsm_enable = 0;
-
- /* If we're ignoring wakeup events then there's no reason to
- * enable Resume-Detect interrupts. We also shouldn't enable
- * them if they are broken or disallowed.
+ /*
+ * UHCI doesn't distinguish between wakeup requests from downstream
+ * devices and local connect/disconnect events. There's no way to
+ * enable one without the other; both are controlled by EGSM. Thus
+ * if wakeups are disallowed then EGSM must be turned off -- in which
+ * case remote wakeup requests from downstream during system sleep
+ * will be lost.
+ *
+ * In addition, if EGSM is broken then we can't use it. Likewise,
+ * if Resume-Detect interrupts are broken then we can't use them.
*
- * This logic may lead us to enabling RD but not EGSM. The UHCI
- * spec foolishly says that RD works only when EGSM is on, but
- * there's no harm in enabling it anyway -- perhaps some chips
- * will implement it!
+ * Finally, neither EGSM nor RD is useful by itself. Without EGSM,
+ * the RD status bit will never get set. Without RD, the controller
+ * won't generate interrupts to tell the system about wakeup events.
*/
- if (!wakeup_enable || resume_detect_interrupts_are_broken(uhci) ||
- !int_enable)
- uhci->RD_enable = int_enable = 0;
+ if (!wakeup_enable || global_suspend_mode_is_broken(uhci) ||
+ resume_detect_interrupts_are_broken(uhci))
+ egsm_enable = int_enable = 0;
+ uhci->RD_enable = !!int_enable;
uhci_writew(uhci, int_enable, USBINTR);
uhci_writew(uhci, egsm_enable | USBCMD_CF, USBCMD);
mb();
@@ -364,10 +364,12 @@ __acquires(uhci->lock)
uhci->rh_state = new_state;
uhci->is_stopped = UHCI_IS_STOPPED;
- /* If interrupts don't work and remote wakeup is enabled then
- * the suspended root hub needs to be polled.
+ /*
+ * If remote wakeup is enabled but either EGSM or RD interrupts
+ * doesn't work, then we won't get an interrupt when a wakeup event
+ * occurs. Thus the suspended root hub needs to be polled.
*/
- if (!int_enable && wakeup_enable)
+ if (wakeup_enable && (!int_enable || !egsm_enable))
set_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags);
else
clear_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags);