aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/ohci-hub.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/host/ohci-hub.c')
-rw-r--r--drivers/usb/host/ohci-hub.c70
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() */
40static 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
126done: 137done:
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
361done: 391done:
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);