Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bf6387bc authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman
Browse files

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: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent df202255
Loading
Loading
Loading
Loading
+10 −10
Original line number Diff line number Diff line
@@ -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)
+26 −0
Original line number Diff line number Diff line
@@ -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 */
+2 −0
Original line number Diff line number Diff line
@@ -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;