diff options
| -rw-r--r-- | drivers/usb/host/uhci-hcd.c | 74 | ||||
| -rw-r--r-- | drivers/usb/host/uhci-hcd.h | 5 |
2 files changed, 61 insertions, 18 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 | } |
diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index 340d6ed3e6e9..7d01c5677f92 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h | |||
| @@ -400,8 +400,9 @@ struct uhci_hcd { | |||
| 400 | unsigned int scan_in_progress:1; /* Schedule scan is running */ | 400 | unsigned int scan_in_progress:1; /* Schedule scan is running */ |
| 401 | unsigned int need_rescan:1; /* Redo the schedule scan */ | 401 | unsigned int need_rescan:1; /* Redo the schedule scan */ |
| 402 | unsigned int dead:1; /* Controller has died */ | 402 | unsigned int dead:1; /* Controller has died */ |
| 403 | unsigned int working_RD:1; /* Suspended root hub doesn't | 403 | unsigned int RD_enable:1; /* Suspended root hub with |
| 404 | need to be polled */ | 404 | Resume-Detect interrupts |
| 405 | enabled */ | ||
| 405 | unsigned int is_initialized:1; /* Data structure is usable */ | 406 | unsigned int is_initialized:1; /* Data structure is usable */ |
| 406 | unsigned int fsbr_is_on:1; /* FSBR is turned on */ | 407 | unsigned int fsbr_is_on:1; /* FSBR is turned on */ |
| 407 | unsigned int fsbr_is_wanted:1; /* Does any URB want FSBR? */ | 408 | unsigned int fsbr_is_wanted:1; /* Does any URB want FSBR? */ |
