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

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

USB: EHCI: use hrtimer for the IAA watchdog



This patch (as1581) replaces the iaa_watchdog kernel timer used by
ehci-hcd with an hrtimer event, in keeping with the general conversion
to high-res timers.

Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 8c5bf7be
Loading
Loading
Loading
Loading
+14 −52
Original line number Diff line number Diff line
@@ -93,7 +93,6 @@ static const char hcd_name [] = "ehci_hcd";
 */
#define	EHCI_TUNE_FLS		1	/* (medium) 512-frame schedule */

#define EHCI_IAA_MSECS		10		/* arbitrary */
#define EHCI_IO_JIFFIES		(HZ/10)		/* io watchdog > irq_thresh */
#define EHCI_SHRINK_JIFFIES	(DIV_ROUND_UP(HZ, 200) + 1)
						/* 5-ms async qh unlink delay */
@@ -322,51 +321,6 @@ static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh);

/*-------------------------------------------------------------------------*/

static void ehci_iaa_watchdog(unsigned long param)
{
	struct ehci_hcd		*ehci = (struct ehci_hcd *) param;
	unsigned long		flags;

	spin_lock_irqsave (&ehci->lock, flags);

	/* Lost IAA irqs wedge things badly; seen first with a vt8235.
	 * So we need this watchdog, but must protect it against both
	 * (a) SMP races against real IAA firing and retriggering, and
	 * (b) clean HC shutdown, when IAA watchdog was pending.
	 */
	if (ehci->async_unlink
			&& !timer_pending(&ehci->iaa_watchdog)
			&& ehci->rh_state == EHCI_RH_RUNNING) {
		u32 cmd, status;

		/* If we get here, IAA is *REALLY* late.  It's barely
		 * conceivable that the system is so busy that CMD_IAAD
		 * is still legitimately set, so let's be sure it's
		 * clear before we read STS_IAA.  (The HC should clear
		 * CMD_IAAD when it sets STS_IAA.)
		 */
		cmd = ehci_readl(ehci, &ehci->regs->command);

		/* If IAA is set here it either legitimately triggered
		 * before we cleared IAAD above (but _way_ late, so we'll
		 * still count it as lost) ... or a silicon erratum:
		 * - VIA seems to set IAA without triggering the IRQ;
		 * - IAAD potentially cleared without setting IAA.
		 */
		status = ehci_readl(ehci, &ehci->regs->status);
		if ((status & STS_IAA) || !(cmd & CMD_IAAD)) {
			COUNT (ehci->stats.lost_iaa);
			ehci_writel(ehci, STS_IAA, &ehci->regs->status);
		}

		ehci_vdbg(ehci, "IAA watchdog: status %x cmd %x\n",
				status, cmd);
		end_unlink_async(ehci);
	}

	spin_unlock_irqrestore(&ehci->lock, flags);
}

static void ehci_watchdog(unsigned long param)
{
	struct ehci_hcd		*ehci = (struct ehci_hcd *) param;
@@ -418,7 +372,6 @@ static void ehci_shutdown(struct usb_hcd *hcd)
	struct ehci_hcd	*ehci = hcd_to_ehci(hcd);

	del_timer_sync(&ehci->watchdog);
	del_timer_sync(&ehci->iaa_watchdog);

	spin_lock_irq(&ehci->lock);
	ehci->rh_state = EHCI_RH_STOPPING;
@@ -491,7 +444,6 @@ static void ehci_stop (struct usb_hcd *hcd)

	/* no more interrupts ... */
	del_timer_sync (&ehci->watchdog);
	del_timer_sync(&ehci->iaa_watchdog);

	spin_lock_irq(&ehci->lock);
	ehci->enabled_hrtimer_events = 0;
@@ -547,10 +499,6 @@ static int ehci_init(struct usb_hcd *hcd)
	ehci->watchdog.function = ehci_watchdog;
	ehci->watchdog.data = (unsigned long) ehci;

	init_timer(&ehci->iaa_watchdog);
	ehci->iaa_watchdog.function = ehci_iaa_watchdog;
	ehci->iaa_watchdog.data = (unsigned long) ehci;

	hrtimer_init(&ehci->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
	ehci->hrtimer.function = ehci_hrtimer_func;
	ehci->next_hrtimer_event = EHCI_HRTIMER_NO_EVENT;
@@ -830,6 +778,20 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)

	/* complete the unlinking of some qh [4.15.2.3] */
	if (status & STS_IAA) {

		/* Turn off the IAA watchdog */
		ehci->enabled_hrtimer_events &= ~BIT(EHCI_HRTIMER_IAA_WATCHDOG);

		/*
		 * Mild optimization: Allow another IAAD to reset the
		 * hrtimer, if one occurs before the next expiration.
		 * In theory we could always cancel the hrtimer, but
		 * tests show that about half the time it will be reset
		 * for some other event anyway.
		 */
		if (ehci->next_hrtimer_event == EHCI_HRTIMER_IAA_WATCHDOG)
			++ehci->next_hrtimer_event;

		/* guard against (alleged) silicon errata */
		if (cmd & CMD_IAAD)
			ehci_dbg(ehci, "IAA with IAAD still set?\n");
+0 −1
Original line number Diff line number Diff line
@@ -209,7 +209,6 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
	if (time_before (jiffies, ehci->next_statechange))
		msleep(5);
	del_timer_sync(&ehci->watchdog);
	del_timer_sync(&ehci->iaa_watchdog);

	spin_lock_irq (&ehci->lock);

+1 −3
Original line number Diff line number Diff line
@@ -1173,8 +1173,6 @@ static void end_unlink_async (struct ehci_hcd *ehci)
	struct ehci_qh		*qh = ehci->async_unlink;
	struct ehci_qh		*next;

	iaa_watchdog_done(ehci);

	// qh->hw_next = cpu_to_hc32(qh->qh_dma);
	qh->qh_state = QH_STATE_IDLE;
	qh->qh_next.qh = NULL;
@@ -1243,7 +1241,7 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)

	ehci_writel(ehci, ehci->command | CMD_IAAD, &ehci->regs->command);
	(void)ehci_readl(ehci, &ehci->regs->command);
	iaa_watchdog_start(ehci);
	ehci_enable_event(ehci, EHCI_HRTIMER_IAA_WATCHDOG, true);
}

/*-------------------------------------------------------------------------*/
+45 −0
Original line number Diff line number Diff line
@@ -72,6 +72,7 @@ static unsigned event_delays_ns[] = {
	1 * NSEC_PER_MSEC,	/* EHCI_HRTIMER_POLL_DEAD */
	1125 * NSEC_PER_USEC,	/* EHCI_HRTIMER_UNLINK_INTR */
	2 * NSEC_PER_MSEC,	/* EHCI_HRTIMER_FREE_ITDS */
	10 * NSEC_PER_MSEC,	/* EHCI_HRTIMER_IAA_WATCHDOG */
	10 * NSEC_PER_MSEC,	/* EHCI_HRTIMER_DISABLE_PERIODIC */
	15 * NSEC_PER_MSEC,	/* EHCI_HRTIMER_DISABLE_ASYNC */
};
@@ -291,6 +292,49 @@ static void end_free_itds(struct ehci_hcd *ehci)
}


/* Handle lost (or very late) IAA interrupts */
static void ehci_iaa_watchdog(struct ehci_hcd *ehci)
{
	if (ehci->rh_state != EHCI_RH_RUNNING)
		return;

	/*
	 * Lost IAA irqs wedge things badly; seen first with a vt8235.
	 * So we need this watchdog, but must protect it against both
	 * (a) SMP races against real IAA firing and retriggering, and
	 * (b) clean HC shutdown, when IAA watchdog was pending.
	 */
	if (ehci->async_unlink) {
		u32 cmd, status;

		/* If we get here, IAA is *REALLY* late.  It's barely
		 * conceivable that the system is so busy that CMD_IAAD
		 * is still legitimately set, so let's be sure it's
		 * clear before we read STS_IAA.  (The HC should clear
		 * CMD_IAAD when it sets STS_IAA.)
		 */
		cmd = ehci_readl(ehci, &ehci->regs->command);

		/*
		 * If IAA is set here it either legitimately triggered
		 * after the watchdog timer expired (_way_ late, so we'll
		 * still count it as lost) ... or a silicon erratum:
		 * - VIA seems to set IAA without triggering the IRQ;
		 * - IAAD potentially cleared without setting IAA.
		 */
		status = ehci_readl(ehci, &ehci->regs->status);
		if ((status & STS_IAA) || !(cmd & CMD_IAAD)) {
			COUNT(ehci->stats.lost_iaa);
			ehci_writel(ehci, STS_IAA, &ehci->regs->status);
		}

		ehci_vdbg(ehci, "IAA watchdog: status %x cmd %x\n",
				status, cmd);
		end_unlink_async(ehci);
	}
}


/*
 * Handler functions for the hrtimer event types.
 * Keep this array in the same order as the event types indexed by
@@ -302,6 +346,7 @@ static void (*event_handlers[])(struct ehci_hcd *) = {
	ehci_handle_controller_death,	/* EHCI_HRTIMER_POLL_DEAD */
	ehci_handle_intr_unlinks,	/* EHCI_HRTIMER_UNLINK_INTR */
	end_free_itds,			/* EHCI_HRTIMER_FREE_ITDS */
	ehci_iaa_watchdog,		/* EHCI_HRTIMER_IAA_WATCHDOG */
	ehci_disable_PSE,		/* EHCI_HRTIMER_DISABLE_PERIODIC */
	ehci_disable_ASE,		/* EHCI_HRTIMER_DISABLE_ASYNC */
};
+1 −15
Original line number Diff line number Diff line
@@ -84,6 +84,7 @@ enum ehci_hrtimer_event {
	EHCI_HRTIMER_POLL_DEAD,		/* Wait for dead controller to stop */
	EHCI_HRTIMER_UNLINK_INTR,	/* Wait for interrupt QH unlink */
	EHCI_HRTIMER_FREE_ITDS,		/* Wait for unused iTDs and siTDs */
	EHCI_HRTIMER_IAA_WATCHDOG,	/* Handle lost IAA interrupts */
	EHCI_HRTIMER_DISABLE_PERIODIC,	/* Wait to disable periodic sched */
	EHCI_HRTIMER_DISABLE_ASYNC,	/* Wait to disable async sched */
	EHCI_HRTIMER_NUM_EVENTS		/* Must come last */
@@ -168,7 +169,6 @@ struct ehci_hcd { /* one per controller */
	struct dma_pool		*itd_pool;	/* itd per iso urb */
	struct dma_pool		*sitd_pool;	/* sitd per split iso urb */

	struct timer_list	iaa_watchdog;
	struct timer_list	watchdog;
	unsigned long		actions;
	unsigned		periodic_stamp;
@@ -228,20 +228,6 @@ static inline struct usb_hcd *ehci_to_hcd (struct ehci_hcd *ehci)
	return container_of ((void *) ehci, struct usb_hcd, hcd_priv);
}


static inline void
iaa_watchdog_start(struct ehci_hcd *ehci)
{
	WARN_ON(timer_pending(&ehci->iaa_watchdog));
	mod_timer(&ehci->iaa_watchdog,
			jiffies + msecs_to_jiffies(EHCI_IAA_MSECS));
}

static inline void iaa_watchdog_done(struct ehci_hcd *ehci)
{
	del_timer(&ehci->iaa_watchdog);
}

enum ehci_timer_action {
	TIMER_IO_WATCHDOG,
	TIMER_ASYNC_SHRINK,