USB: EHCI: use hrtimer for controller death
This patch (as1578) adds an hrtimer event to handle the death of an EHCI controller. When a controller dies, it doesn't necessarily stop running right away. The new event polls at 1-ms intervals to see when all activity has safely stopped. This replaces a busy-wait polling loop in the current code. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
df2022553d
commit
bf6387bcd1
|
@ -888,20 +888,20 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
|
|||
/* PCI errors [4.15.2.4] */
|
||||
if (unlikely ((status & STS_FATAL) != 0)) {
|
||||
ehci_err(ehci, "fatal error\n");
|
||||
ehci->rh_state = EHCI_RH_STOPPING;
|
||||
dbg_cmd(ehci, "fatal", cmd);
|
||||
dbg_status(ehci, "fatal", status);
|
||||
ehci_halt(ehci);
|
||||
dead:
|
||||
ehci->enabled_hrtimer_events = 0;
|
||||
hrtimer_try_to_cancel(&ehci->hrtimer);
|
||||
ehci_reset(ehci);
|
||||
ehci_writel(ehci, 0, &ehci->regs->configured_flag);
|
||||
usb_hc_died(hcd);
|
||||
/* generic layer kills/unlinks all urbs, then
|
||||
* uses ehci_stop to clean up the rest
|
||||
*/
|
||||
bh = 1;
|
||||
|
||||
/* Don't let the controller do anything more */
|
||||
ehci->rh_state = EHCI_RH_STOPPING;
|
||||
ehci->command &= ~(CMD_RUN | CMD_ASE | CMD_PSE);
|
||||
ehci_writel(ehci, ehci->command, &ehci->regs->command);
|
||||
ehci_writel(ehci, 0, &ehci->regs->intr_enable);
|
||||
ehci_handle_controller_death(ehci);
|
||||
|
||||
/* Handle completions when the controller stops */
|
||||
bh = 0;
|
||||
}
|
||||
|
||||
if (bh)
|
||||
|
|
|
@ -69,6 +69,7 @@ static void ehci_clear_command_bit(struct ehci_hcd *ehci, u32 bit)
|
|||
static unsigned event_delays_ns[] = {
|
||||
1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_ASS */
|
||||
1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_PSS */
|
||||
1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_DEAD */
|
||||
1125 * NSEC_PER_USEC, /* EHCI_HRTIMER_UNLINK_INTR */
|
||||
10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */
|
||||
15 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_ASYNC */
|
||||
|
@ -193,6 +194,30 @@ static void ehci_disable_PSE(struct ehci_hcd *ehci)
|
|||
}
|
||||
|
||||
|
||||
/* Poll the STS_HALT status bit; see when a dead controller stops */
|
||||
static void ehci_handle_controller_death(struct ehci_hcd *ehci)
|
||||
{
|
||||
if (!(ehci_readl(ehci, &ehci->regs->status) & STS_HALT)) {
|
||||
|
||||
/* Give up after a few milliseconds */
|
||||
if (ehci->died_poll_count++ < 5) {
|
||||
/* Try again later */
|
||||
ehci_enable_event(ehci, EHCI_HRTIMER_POLL_DEAD, true);
|
||||
return;
|
||||
}
|
||||
ehci_warn(ehci, "Waited too long for the controller to stop, giving up\n");
|
||||
}
|
||||
|
||||
/* Clean up the mess */
|
||||
ehci->rh_state = EHCI_RH_HALTED;
|
||||
ehci_writel(ehci, 0, &ehci->regs->configured_flag);
|
||||
ehci_writel(ehci, 0, &ehci->regs->intr_enable);
|
||||
ehci_work(ehci);
|
||||
|
||||
/* Not in process context, so don't try to reset the controller */
|
||||
}
|
||||
|
||||
|
||||
/* Handle unlinked interrupt QHs once they are gone from the hardware */
|
||||
static void ehci_handle_intr_unlinks(struct ehci_hcd *ehci)
|
||||
{
|
||||
|
@ -233,6 +258,7 @@ static void ehci_handle_intr_unlinks(struct ehci_hcd *ehci)
|
|||
static void (*event_handlers[])(struct ehci_hcd *) = {
|
||||
ehci_poll_ASS, /* EHCI_HRTIMER_POLL_ASS */
|
||||
ehci_poll_PSS, /* EHCI_HRTIMER_POLL_PSS */
|
||||
ehci_handle_controller_death, /* EHCI_HRTIMER_POLL_DEAD */
|
||||
ehci_handle_intr_unlinks, /* EHCI_HRTIMER_UNLINK_INTR */
|
||||
ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */
|
||||
ehci_disable_ASE, /* EHCI_HRTIMER_DISABLE_ASYNC */
|
||||
|
|
|
@ -81,6 +81,7 @@ enum ehci_rh_state {
|
|||
enum ehci_hrtimer_event {
|
||||
EHCI_HRTIMER_POLL_ASS, /* Poll for async schedule off */
|
||||
EHCI_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */
|
||||
EHCI_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */
|
||||
EHCI_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */
|
||||
EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */
|
||||
EHCI_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */
|
||||
|
@ -97,6 +98,7 @@ struct ehci_hcd { /* one per controller */
|
|||
|
||||
int PSS_poll_count;
|
||||
int ASS_poll_count;
|
||||
int died_poll_count;
|
||||
|
||||
/* glue to PCI and HCD framework */
|
||||
struct ehci_caps __iomem *caps;
|
||||
|
|
Loading…
Reference in New Issue