diff options
Diffstat (limited to 'drivers/usb/host/uhci-hcd.c')
-rw-r--r-- | drivers/usb/host/uhci-hcd.c | 74 |
1 files changed, 58 insertions, 16 deletions
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index d3e0d8aa3980..3a7bfe7a8874 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c | |||
@@ -234,7 +234,7 @@ static int resume_detect_interrupts_are_broken(struct uhci_hcd *uhci) | |||
234 | return 0; | 234 | return 0; |
235 | } | 235 | } |
236 | 236 | ||
237 | static int remote_wakeup_is_broken(struct uhci_hcd *uhci) | 237 | static int global_suspend_mode_is_broken(struct uhci_hcd *uhci) |
238 | { | 238 | { |
239 | int port; | 239 | int port; |
240 | const char *sys_info; | 240 | const char *sys_info; |
@@ -261,27 +261,60 @@ __releases(uhci->lock) | |||
261 | __acquires(uhci->lock) | 261 | __acquires(uhci->lock) |
262 | { | 262 | { |
263 | int auto_stop; | 263 | int auto_stop; |
264 | int int_enable, egsm_enable; | 264 | int int_enable, egsm_enable, wakeup_enable; |
265 | struct usb_device *rhdev = uhci_to_hcd(uhci)->self.root_hub; | 265 | struct usb_device *rhdev = uhci_to_hcd(uhci)->self.root_hub; |
266 | 266 | ||
267 | auto_stop = (new_state == UHCI_RH_AUTO_STOPPED); | 267 | auto_stop = (new_state == UHCI_RH_AUTO_STOPPED); |
268 | dev_dbg(&rhdev->dev, "%s%s\n", __func__, | 268 | dev_dbg(&rhdev->dev, "%s%s\n", __func__, |
269 | (auto_stop ? " (auto-stop)" : "")); | 269 | (auto_stop ? " (auto-stop)" : "")); |
270 | 270 | ||
271 | /* Enable resume-detect interrupts if they work. | 271 | /* Start off by assuming Resume-Detect interrupts and EGSM work |
272 | * Then enter Global Suspend mode if _it_ works, still configured. | 272 | * and that remote wakeups should be enabled. |
273 | */ | 273 | */ |
274 | egsm_enable = USBCMD_EGSM; | 274 | egsm_enable = USBCMD_EGSM; |
275 | uhci->working_RD = 1; | 275 | uhci->RD_enable = 1; |
276 | int_enable = USBINTR_RESUME; | 276 | int_enable = USBINTR_RESUME; |
277 | if (remote_wakeup_is_broken(uhci)) | 277 | wakeup_enable = 1; |
278 | egsm_enable = 0; | 278 | |
279 | if (resume_detect_interrupts_are_broken(uhci) || !egsm_enable || | 279 | /* In auto-stop mode wakeups must always be detected, but |
280 | * Resume-Detect interrupts may be prohibited. (In the absence | ||
281 | * of CONFIG_PM, they are always disallowed.) | ||
282 | */ | ||
283 | if (auto_stop) { | ||
284 | if (!device_may_wakeup(&rhdev->dev)) | ||
285 | int_enable = 0; | ||
286 | |||
287 | /* In bus-suspend mode wakeups may be disabled, but if they are | ||
288 | * allowed then so are Resume-Detect interrupts. | ||
289 | */ | ||
290 | } else { | ||
280 | #ifdef CONFIG_PM | 291 | #ifdef CONFIG_PM |
281 | (!auto_stop && !rhdev->do_remote_wakeup) || | 292 | if (!rhdev->do_remote_wakeup) |
293 | wakeup_enable = 0; | ||
282 | #endif | 294 | #endif |
283 | (auto_stop && !device_may_wakeup(&rhdev->dev))) | 295 | } |
284 | uhci->working_RD = int_enable = 0; | 296 | |
297 | /* EGSM causes the root hub to echo a 'K' signal (resume) out any | ||
298 | * port which requests a remote wakeup. According to the USB spec, | ||
299 | * every hub is supposed to do this. But if we are ignoring | ||
300 | * remote-wakeup requests anyway then there's no point to it. | ||
301 | * We also shouldn't enable EGSM if it's broken. | ||
302 | */ | ||
303 | if (!wakeup_enable || global_suspend_mode_is_broken(uhci)) | ||
304 | egsm_enable = 0; | ||
305 | |||
306 | /* If we're ignoring wakeup events then there's no reason to | ||
307 | * enable Resume-Detect interrupts. We also shouldn't enable | ||
308 | * them if they are broken or disallowed. | ||
309 | * | ||
310 | * This logic may lead us to enabling RD but not EGSM. The UHCI | ||
311 | * spec foolishly says that RD works only when EGSM is on, but | ||
312 | * there's no harm in enabling it anyway -- perhaps some chips | ||
313 | * will implement it! | ||
314 | */ | ||
315 | if (!wakeup_enable || resume_detect_interrupts_are_broken(uhci) || | ||
316 | !int_enable) | ||
317 | uhci->RD_enable = int_enable = 0; | ||
285 | 318 | ||
286 | outw(int_enable, uhci->io_addr + USBINTR); | 319 | outw(int_enable, uhci->io_addr + USBINTR); |
287 | outw(egsm_enable | USBCMD_CF, uhci->io_addr + USBCMD); | 320 | outw(egsm_enable | USBCMD_CF, uhci->io_addr + USBCMD); |
@@ -308,7 +341,11 @@ __acquires(uhci->lock) | |||
308 | 341 | ||
309 | uhci->rh_state = new_state; | 342 | uhci->rh_state = new_state; |
310 | uhci->is_stopped = UHCI_IS_STOPPED; | 343 | uhci->is_stopped = UHCI_IS_STOPPED; |
311 | uhci_to_hcd(uhci)->poll_rh = !int_enable; | 344 | |
345 | /* If interrupts don't work and remote wakeup is enabled then | ||
346 | * the suspended root hub needs to be polled. | ||
347 | */ | ||
348 | uhci_to_hcd(uhci)->poll_rh = (!int_enable && wakeup_enable); | ||
312 | 349 | ||
313 | uhci_scan_schedule(uhci); | 350 | uhci_scan_schedule(uhci); |
314 | uhci_fsbr_off(uhci); | 351 | uhci_fsbr_off(uhci); |
@@ -344,9 +381,12 @@ __acquires(uhci->lock) | |||
344 | * for 20 ms. | 381 | * for 20 ms. |
345 | */ | 382 | */ |
346 | if (uhci->rh_state == UHCI_RH_SUSPENDED) { | 383 | if (uhci->rh_state == UHCI_RH_SUSPENDED) { |
384 | unsigned egsm; | ||
385 | |||
386 | /* Keep EGSM on if it was set before */ | ||
387 | egsm = inw(uhci->io_addr + USBCMD) & USBCMD_EGSM; | ||
347 | uhci->rh_state = UHCI_RH_RESUMING; | 388 | uhci->rh_state = UHCI_RH_RESUMING; |
348 | outw(USBCMD_FGR | USBCMD_EGSM | USBCMD_CF, | 389 | outw(USBCMD_FGR | USBCMD_CF | egsm, uhci->io_addr + USBCMD); |
349 | uhci->io_addr + USBCMD); | ||
350 | spin_unlock_irq(&uhci->lock); | 390 | spin_unlock_irq(&uhci->lock); |
351 | msleep(20); | 391 | msleep(20); |
352 | spin_lock_irq(&uhci->lock); | 392 | spin_lock_irq(&uhci->lock); |
@@ -801,8 +841,10 @@ static int uhci_pci_resume(struct usb_hcd *hcd) | |||
801 | 841 | ||
802 | spin_unlock_irq(&uhci->lock); | 842 | spin_unlock_irq(&uhci->lock); |
803 | 843 | ||
804 | if (!uhci->working_RD) { | 844 | /* If interrupts don't work and remote wakeup is enabled then |
805 | /* Suspended root hub needs to be polled */ | 845 | * the suspended root hub needs to be polled. |
846 | */ | ||
847 | if (!uhci->RD_enable && hcd->self.root_hub->do_remote_wakeup) { | ||
806 | hcd->poll_rh = 1; | 848 | hcd->poll_rh = 1; |
807 | usb_hcd_poll_rh_status(hcd); | 849 | usb_hcd_poll_rh_status(hcd); |
808 | } | 850 | } |