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