diff options
Diffstat (limited to 'drivers/usb/host/ohci-hub.c')
-rw-r--r-- | drivers/usb/host/ohci-hub.c | 67 |
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() */ | ||
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,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 | |||
126 | done: | 137 | done: |
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 | |||
361 | done: | 394 | done: |
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); |