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

USB: ohci: port reset paranoia timeout

This limits how long the OHCI port reset loop waits for the hardware
to do its job, if the controller either (a) dies, or (b) can't finish
the reset. Such limits are always a good idea.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

David Brownell and committed by
Greg Kroah-Hartman
e01e7fe3 9776afc8

+21 -3
+21 -3
drivers/usb/host/ohci-hub.c
··· 564 564 u32 temp; 565 565 u16 now = ohci_readl(ohci, &ohci->regs->fmnumber); 566 566 u16 reset_done = now + PORT_RESET_MSEC; 567 + int limit_1 = DIV_ROUND_UP(PORT_RESET_MSEC, PORT_RESET_HW_MSEC); 567 568 568 569 /* build a "continuous enough" reset signal, with up to 569 570 * 3msec gap between pulses. scheduler HZ==100 must work; 570 571 * this might need to be deadline-scheduled. 571 572 */ 572 573 do { 574 + int limit_2; 575 + 573 576 /* spin until any current reset finishes */ 574 - for (;;) { 577 + limit_2 = PORT_RESET_HW_MSEC * 2; 578 + while (--limit_2 >= 0) { 575 579 temp = ohci_readl (ohci, portstat); 576 580 /* handle e.g. CardBus eject */ 577 581 if (temp == ~(u32)0) ··· 583 579 if (!(temp & RH_PS_PRS)) 584 580 break; 585 581 udelay (500); 582 + } 583 + 584 + /* timeout (a hardware error) has been observed when 585 + * EHCI sets CF while this driver is resetting a port; 586 + * presumably other disconnect paths might do it too. 587 + */ 588 + if (limit_2 < 0) { 589 + ohci_dbg(ohci, 590 + "port[%d] reset timeout, stat %08x\n", 591 + port, temp); 592 + break; 586 593 } 587 594 588 595 if (!(temp & RH_PS_CCS)) ··· 605 590 ohci_writel (ohci, RH_PS_PRS, portstat); 606 591 msleep(PORT_RESET_HW_MSEC); 607 592 now = ohci_readl(ohci, &ohci->regs->fmnumber); 608 - } while (tick_before(now, reset_done)); 609 - /* caller synchronizes using PRSC */ 593 + } while (tick_before(now, reset_done) && --limit_1 >= 0); 594 + 595 + /* caller synchronizes using PRSC ... and handles PRS 596 + * still being set when this returns. 597 + */ 610 598 611 599 return 0; 612 600 }