aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/ohci-hub.c
diff options
context:
space:
mode:
authorDavid Brownell <david-b@pacbell.net>2006-08-04 14:31:55 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2006-09-27 14:58:48 -0400
commitd413984ae936fad46678403b38d79c595e5aaafe (patch)
treef494a450d7289d941e3ca2b2e33bfacc29cac024 /drivers/usb/host/ohci-hub.c
parent06afff00bcab0e384afbef70194fd3469532abdf (diff)
USB: OHCI avoids root hub timer polling
This teaches OHCI to use the root hub status change (RHSC) IRQ, bypassing root hub timers most of the time and switching over to the "new" root hub polling scheme. It's complicated by the fact that implementations of OHCI trigger and ack that IRQ differently (the spec is vague there). Avoiding root hub timers helps mechanisms like "dynamic tick" leave the CPU in lowpower modes for longer intervals. Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
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);