diff options
Diffstat (limited to 'drivers/usb/host/ohci-hub.c')
| -rw-r--r-- | drivers/usb/host/ohci-hub.c | 70 |
1 files changed, 46 insertions, 24 deletions
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index 5b0a23fd798b..0b899339cac8 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c | |||
| @@ -36,6 +36,14 @@ | |||
| 36 | 36 | ||
| 37 | /*-------------------------------------------------------------------------*/ | 37 | /*-------------------------------------------------------------------------*/ |
| 38 | 38 | ||
| 39 | /* hcd->hub_irq_enable() */ | ||
| 40 | static void ohci_rhsc_enable (struct usb_hcd *hcd) | ||
| 41 | { | ||
| 42 | struct ohci_hcd *ohci = hcd_to_ohci (hcd); | ||
| 43 | |||
| 44 | ohci_writel (ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable); | ||
| 45 | } | ||
| 46 | |||
| 39 | #ifdef CONFIG_PM | 47 | #ifdef CONFIG_PM |
| 40 | 48 | ||
| 41 | #define OHCI_SCHED_ENABLES \ | 49 | #define OHCI_SCHED_ENABLES \ |
| @@ -123,10 +131,10 @@ static int ohci_bus_suspend (struct usb_hcd *hcd) | |||
| 123 | /* no resumes until devices finish suspending */ | 131 | /* no resumes until devices finish suspending */ |
| 124 | ohci->next_statechange = jiffies + msecs_to_jiffies (5); | 132 | ohci->next_statechange = jiffies + msecs_to_jiffies (5); |
| 125 | 133 | ||
| 134 | /* no timer polling */ | ||
| 135 | hcd->poll_rh = 0; | ||
| 136 | |||
| 126 | done: | 137 | done: |
| 127 | /* external suspend vs self autosuspend ... same effect */ | ||
| 128 | if (status == 0) | ||
| 129 | usb_hcd_suspend_root_hub(hcd); | ||
| 130 | spin_unlock_irqrestore (&ohci->lock, flags); | 138 | spin_unlock_irqrestore (&ohci->lock, flags); |
| 131 | return status; | 139 | return status; |
| 132 | } | 140 | } |
| @@ -256,8 +264,8 @@ static int ohci_bus_resume (struct usb_hcd *hcd) | |||
| 256 | /* TRSMRCY */ | 264 | /* TRSMRCY */ |
| 257 | msleep (10); | 265 | msleep (10); |
| 258 | 266 | ||
| 259 | /* keep it alive for ~5x suspend + resume costs */ | 267 | /* keep it alive for more than ~5x suspend + resume costs */ |
| 260 | ohci->next_statechange = jiffies + msecs_to_jiffies (250); | 268 | ohci->next_statechange = jiffies + STATECHANGE_DELAY; |
| 261 | 269 | ||
| 262 | /* maybe turn schedules back on */ | 270 | /* maybe turn schedules back on */ |
| 263 | enables = 0; | 271 | enables = 0; |
| @@ -302,9 +310,10 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) | |||
| 302 | { | 310 | { |
| 303 | struct ohci_hcd *ohci = hcd_to_ohci (hcd); | 311 | struct ohci_hcd *ohci = hcd_to_ohci (hcd); |
| 304 | int i, changed = 0, length = 1; | 312 | int i, changed = 0, length = 1; |
| 305 | int can_suspend = device_may_wakeup(&hcd->self.root_hub->dev); | 313 | int can_suspend; |
| 306 | unsigned long flags; | 314 | unsigned long flags; |
| 307 | 315 | ||
| 316 | can_suspend = device_may_wakeup(&hcd->self.root_hub->dev); | ||
| 308 | spin_lock_irqsave (&ohci->lock, flags); | 317 | spin_lock_irqsave (&ohci->lock, flags); |
| 309 | 318 | ||
| 310 | /* handle autosuspended root: finish resuming before | 319 | /* handle autosuspended root: finish resuming before |
| @@ -339,6 +348,10 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) | |||
| 339 | for (i = 0; i < ohci->num_ports; i++) { | 348 | for (i = 0; i < ohci->num_ports; i++) { |
| 340 | u32 status = roothub_portstatus (ohci, i); | 349 | u32 status = roothub_portstatus (ohci, i); |
| 341 | 350 | ||
| 351 | /* can't autosuspend with active ports */ | ||
| 352 | if ((status & RH_PS_PES) && !(status & RH_PS_PSS)) | ||
| 353 | can_suspend = 0; | ||
| 354 | |||
| 342 | if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC | 355 | if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC |
| 343 | | RH_PS_OCIC | RH_PS_PRSC)) { | 356 | | RH_PS_OCIC | RH_PS_PRSC)) { |
| 344 | changed = 1; | 357 | changed = 1; |
| @@ -348,32 +361,41 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) | |||
| 348 | buf [1] |= 1 << (i - 7); | 361 | buf [1] |= 1 << (i - 7); |
| 349 | continue; | 362 | continue; |
| 350 | } | 363 | } |
| 364 | } | ||
| 351 | 365 | ||
| 352 | /* can suspend if no ports are enabled; or if all all | 366 | /* after root hub changes, stop polling after debouncing |
| 353 | * enabled ports are suspended AND remote wakeup is on. | 367 | * for a while and maybe kicking in autosuspend |
| 354 | */ | 368 | */ |
| 355 | if (!(status & RH_PS_CCS)) | 369 | if (changed) { |
| 356 | continue; | 370 | ohci->next_statechange = jiffies + STATECHANGE_DELAY; |
| 357 | if ((status & RH_PS_PSS) && can_suspend) | ||
| 358 | continue; | ||
| 359 | can_suspend = 0; | 371 | can_suspend = 0; |
| 372 | } else if (time_before (jiffies, ohci->next_statechange)) { | ||
| 373 | can_suspend = 0; | ||
| 374 | } else { | ||
| 375 | #ifdef CONFIG_PM | ||
| 376 | can_suspend = can_suspend | ||
| 377 | && !ohci->ed_rm_list | ||
| 378 | && ((OHCI_CTRL_HCFS | OHCI_SCHED_ENABLES) | ||
| 379 | & ohci->hc_control) | ||
| 380 | == OHCI_USB_OPER; | ||
| 381 | #endif | ||
| 382 | if (hcd->uses_new_polling) { | ||
| 383 | hcd->poll_rh = 0; | ||
| 384 | /* use INTR_RHSC iff INTR_RD won't apply */ | ||
| 385 | if (!can_suspend) | ||
| 386 | ohci_writel (ohci, OHCI_INTR_RHSC, | ||
| 387 | &ohci->regs->intrenable); | ||
| 388 | } | ||
| 360 | } | 389 | } |
| 390 | |||
| 361 | done: | 391 | done: |
| 362 | spin_unlock_irqrestore (&ohci->lock, flags); | 392 | spin_unlock_irqrestore (&ohci->lock, flags); |
| 363 | 393 | ||
| 364 | #ifdef CONFIG_PM | 394 | #ifdef CONFIG_PM |
| 365 | /* save power by suspending idle root hubs; | 395 | /* save power by autosuspending idle root hubs; |
| 366 | * INTR_RD wakes us when there's work | 396 | * INTR_RD wakes us when there's work |
| 367 | */ | 397 | */ |
| 368 | if (can_suspend | 398 | if (can_suspend && usb_trylock_device (hcd->self.root_hub) == 0) { |
| 369 | && !changed | ||
| 370 | && !ohci->ed_rm_list | ||
| 371 | && ((OHCI_CTRL_HCFS | OHCI_SCHED_ENABLES) | ||
| 372 | & ohci->hc_control) | ||
| 373 | == OHCI_USB_OPER | ||
| 374 | && time_after (jiffies, ohci->next_statechange) | ||
| 375 | && usb_trylock_device (hcd->self.root_hub) == 0 | ||
| 376 | ) { | ||
| 377 | ohci_vdbg (ohci, "autosuspend\n"); | 399 | ohci_vdbg (ohci, "autosuspend\n"); |
| 378 | (void) ohci_bus_suspend (hcd); | 400 | (void) ohci_bus_suspend (hcd); |
| 379 | usb_unlock_device (hcd->self.root_hub); | 401 | usb_unlock_device (hcd->self.root_hub); |
