Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

OHCI: Allow broken controllers to auto-stop

This patch (as1134) attempts to improve the way we handle OHCI
controllers with broken Root Hub Status Change interrupt support. In
these controllers the RHSC interrupt bit essentially never turns off,
making RHSC interrupts useless -- they have to remain permanently
disabled.

Such controllers should still be allowed to turn off their root hubs
when no devices are attached. Polling for new connections can
continue while the root hub is suspended. The patch implements this
feature. (It won't have much effect unless CONFIG_PM is enabled and
CONFIG_USB_SUSPEND is disabled, but since the overhead is very small
we may as well do it.)

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Cc: stable <stable@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

Alan Stern and committed by
Greg Kroah-Hartman
4a511bc3 8bfa2472

+32 -28
+32 -28
drivers/usb/host/ohci-hub.c
··· 362 362 int any_connected) 363 363 { 364 364 int poll_rh = 1; 365 - int rhsc; 365 + int rhsc_status, rhsc_enable; 366 366 367 - rhsc = ohci_readl(ohci, &ohci->regs->intrenable) & OHCI_INTR_RHSC; 367 + /* Some broken controllers never turn off RHCS in the interrupt 368 + * status register. For their sake we won't re-enable RHSC 369 + * interrupts if the interrupt bit is already active. 370 + */ 371 + rhsc_status = ohci_readl(ohci, &ohci->regs->intrstatus) & 372 + OHCI_INTR_RHSC; 373 + rhsc_enable = ohci_readl(ohci, &ohci->regs->intrenable) & 374 + OHCI_INTR_RHSC; 375 + 368 376 switch (ohci->hc_control & OHCI_CTRL_HCFS) { 369 - 370 377 case OHCI_USB_OPER: 371 - /* If no status changes are pending, enable status-change 372 - * interrupts. 373 - */ 374 - if (!rhsc && !changed) { 375 - rhsc = OHCI_INTR_RHSC; 376 - ohci_writel(ohci, rhsc, &ohci->regs->intrenable); 378 + /* If no status changes are pending, enable RHSC interrupts. */ 379 + if (!rhsc_enable && !rhsc_status && !changed) { 380 + rhsc_enable = OHCI_INTR_RHSC; 381 + ohci_writel(ohci, rhsc_enable, &ohci->regs->intrenable); 377 382 } 378 383 379 384 /* Keep on polling until we know a device is connected ··· 388 383 if (any_connected || 389 384 !device_may_wakeup(&ohci_to_hcd(ohci) 390 385 ->self.root_hub->dev)) { 391 - if (rhsc) 386 + if (rhsc_enable) 392 387 poll_rh = 0; 393 388 } else { 394 389 ohci->autostop = 1; ··· 401 396 ohci->autostop = 0; 402 397 ohci->next_statechange = jiffies + 403 398 STATECHANGE_DELAY; 404 - } else if (rhsc && time_after_eq(jiffies, 399 + } else if (time_after_eq(jiffies, 405 400 ohci->next_statechange) 406 401 && !ohci->ed_rm_list 407 402 && !(ohci->hc_control & 408 403 OHCI_SCHED_ENABLES)) { 409 404 ohci_rh_suspend(ohci, 1); 410 - poll_rh = 0; 405 + if (rhsc_enable) 406 + poll_rh = 0; 411 407 } 412 408 } 413 409 break; 414 410 415 - /* if there is a port change, autostart or ask to be resumed */ 416 411 case OHCI_USB_SUSPEND: 417 412 case OHCI_USB_RESUME: 413 + /* if there is a port change, autostart or ask to be resumed */ 418 414 if (changed) { 419 415 if (ohci->autostop) 420 416 ohci_rh_resume(ohci); 421 417 else 422 418 usb_hcd_resume_root_hub(ohci_to_hcd(ohci)); 423 419 } else { 424 - if (!rhsc && (ohci->autostop || 420 + if (!rhsc_enable && !rhsc_status && (ohci->autostop || 425 421 ohci_to_hcd(ohci)->self.root_hub-> 426 - do_remote_wakeup)) 427 - ohci_writel(ohci, OHCI_INTR_RHSC, 422 + do_remote_wakeup)) { 423 + rhsc_enable = OHCI_INTR_RHSC; 424 + ohci_writel(ohci, rhsc_enable, 428 425 &ohci->regs->intrenable); 429 - 430 - /* everything is idle, no need for polling */ 431 - poll_rh = 0; 426 + } 427 + if (rhsc_enable) 428 + poll_rh = 0; 432 429 } 433 430 break; 434 431 } ··· 450 443 static int ohci_root_hub_state_changes(struct ohci_hcd *ohci, int changed, 451 444 int any_connected) 452 445 { 446 + int rhsc_status; 447 + 453 448 /* If RHSC is enabled, don't poll */ 454 449 if (ohci_readl(ohci, &ohci->regs->intrenable) & OHCI_INTR_RHSC) 455 450 return 0; 456 451 457 - /* If no status changes are pending, enable status-change interrupts */ 458 - if (!changed) { 452 + /* If no status changes are pending, enable RHSC interrupts */ 453 + rhsc_status = ohci_readl(ohci, &ohci->regs->intrstatus) & 454 + OHCI_INTR_RHSC; 455 + if (!changed && !rhsc_status) { 459 456 ohci_writel(ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable); 460 457 return 0; 461 458 } ··· 502 491 buf [1] = 0; 503 492 length++; 504 493 } 505 - 506 - /* Some broken controllers never turn off RHCS in the interrupt 507 - * status register. For their sake we won't re-enable RHSC 508 - * interrupts if the flag is already set. 509 - */ 510 - if (ohci_readl(ohci, &ohci->regs->intrstatus) & OHCI_INTR_RHSC) 511 - changed = 1; 512 494 513 495 /* look at each port */ 514 496 for (i = 0; i < ohci->num_ports; i++) {