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] */
|
/* PCI errors [4.15.2.4] */
|
||||||
if (unlikely ((status & STS_FATAL) != 0)) {
|
if (unlikely ((status & STS_FATAL) != 0)) {
|
||||||
ehci_err(ehci, "fatal error\n");
|
ehci_err(ehci, "fatal error\n");
|
||||||
ehci->rh_state = EHCI_RH_STOPPING;
|
|
||||||
dbg_cmd(ehci, "fatal", cmd);
|
dbg_cmd(ehci, "fatal", cmd);
|
||||||
dbg_status(ehci, "fatal", status);
|
dbg_status(ehci, "fatal", status);
|
||||||
ehci_halt(ehci);
|
|
||||||
dead:
|
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);
|
usb_hc_died(hcd);
|
||||||
/* generic layer kills/unlinks all urbs, then
|
|
||||||
* uses ehci_stop to clean up the rest
|
/* Don't let the controller do anything more */
|
||||||
*/
|
ehci->rh_state = EHCI_RH_STOPPING;
|
||||||
bh = 1;
|
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)
|
if (bh)
|
||||||
|
|
|
@ -69,6 +69,7 @@ static void ehci_clear_command_bit(struct ehci_hcd *ehci, u32 bit)
|
||||||
static unsigned event_delays_ns[] = {
|
static unsigned event_delays_ns[] = {
|
||||||
1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_ASS */
|
1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_ASS */
|
||||||
1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_PSS */
|
1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_PSS */
|
||||||
|
1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_DEAD */
|
||||||
1125 * NSEC_PER_USEC, /* EHCI_HRTIMER_UNLINK_INTR */
|
1125 * NSEC_PER_USEC, /* EHCI_HRTIMER_UNLINK_INTR */
|
||||||
10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */
|
10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */
|
||||||
15 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_ASYNC */
|
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 */
|
/* Handle unlinked interrupt QHs once they are gone from the hardware */
|
||||||
static void ehci_handle_intr_unlinks(struct ehci_hcd *ehci)
|
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 *) = {
|
static void (*event_handlers[])(struct ehci_hcd *) = {
|
||||||
ehci_poll_ASS, /* EHCI_HRTIMER_POLL_ASS */
|
ehci_poll_ASS, /* EHCI_HRTIMER_POLL_ASS */
|
||||||
ehci_poll_PSS, /* EHCI_HRTIMER_POLL_PSS */
|
ehci_poll_PSS, /* EHCI_HRTIMER_POLL_PSS */
|
||||||
|
ehci_handle_controller_death, /* EHCI_HRTIMER_POLL_DEAD */
|
||||||
ehci_handle_intr_unlinks, /* EHCI_HRTIMER_UNLINK_INTR */
|
ehci_handle_intr_unlinks, /* EHCI_HRTIMER_UNLINK_INTR */
|
||||||
ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */
|
ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */
|
||||||
ehci_disable_ASE, /* EHCI_HRTIMER_DISABLE_ASYNC */
|
ehci_disable_ASE, /* EHCI_HRTIMER_DISABLE_ASYNC */
|
||||||
|
|
|
@ -81,6 +81,7 @@ enum ehci_rh_state {
|
||||||
enum ehci_hrtimer_event {
|
enum ehci_hrtimer_event {
|
||||||
EHCI_HRTIMER_POLL_ASS, /* Poll for async schedule off */
|
EHCI_HRTIMER_POLL_ASS, /* Poll for async schedule off */
|
||||||
EHCI_HRTIMER_POLL_PSS, /* Poll for periodic 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_UNLINK_INTR, /* Wait for interrupt QH unlink */
|
||||||
EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */
|
EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */
|
||||||
EHCI_HRTIMER_DISABLE_ASYNC, /* Wait to disable async 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 PSS_poll_count;
|
||||||
int ASS_poll_count;
|
int ASS_poll_count;
|
||||||
|
int died_poll_count;
|
||||||
|
|
||||||
/* glue to PCI and HCD framework */
|
/* glue to PCI and HCD framework */
|
||||||
struct ehci_caps __iomem *caps;
|
struct ehci_caps __iomem *caps;
|
||||||
|
|
Loading…
Reference in New Issue