mirror of https://gitee.com/openkylin/linux.git
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: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
8c5bf7be56
commit
9d9387475a
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
|
|
@ -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 */
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue