diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2012-07-11 11:22:44 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-07-16 19:56:46 -0400 |
commit | 9d9387475af261949f61a5ec465e1f762d7be08a (patch) | |
tree | 7aa6688962bde78fba655b791612e60cc1b50adf /drivers/usb/host/ehci-timer.c | |
parent | 8c5bf7be56f1a8aecc1f802f132d53f556a9bc45 (diff) |
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>
Diffstat (limited to 'drivers/usb/host/ehci-timer.c')
-rw-r--r-- | drivers/usb/host/ehci-timer.c | 45 |
1 files changed, 45 insertions, 0 deletions
diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c index 8feb60ff4228..aab042064226 100644 --- a/drivers/usb/host/ehci-timer.c +++ b/drivers/usb/host/ehci-timer.c | |||
@@ -72,6 +72,7 @@ static unsigned event_delays_ns[] = { | |||
72 | 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_DEAD */ | 72 | 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_DEAD */ |
73 | 1125 * NSEC_PER_USEC, /* EHCI_HRTIMER_UNLINK_INTR */ | 73 | 1125 * NSEC_PER_USEC, /* EHCI_HRTIMER_UNLINK_INTR */ |
74 | 2 * NSEC_PER_MSEC, /* EHCI_HRTIMER_FREE_ITDS */ | 74 | 2 * NSEC_PER_MSEC, /* EHCI_HRTIMER_FREE_ITDS */ |
75 | 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_IAA_WATCHDOG */ | ||
75 | 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */ | 76 | 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */ |
76 | 15 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_ASYNC */ | 77 | 15 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_ASYNC */ |
77 | }; | 78 | }; |
@@ -291,6 +292,49 @@ static void end_free_itds(struct ehci_hcd *ehci) | |||
291 | } | 292 | } |
292 | 293 | ||
293 | 294 | ||
295 | /* Handle lost (or very late) IAA interrupts */ | ||
296 | static void ehci_iaa_watchdog(struct ehci_hcd *ehci) | ||
297 | { | ||
298 | if (ehci->rh_state != EHCI_RH_RUNNING) | ||
299 | return; | ||
300 | |||
301 | /* | ||
302 | * Lost IAA irqs wedge things badly; seen first with a vt8235. | ||
303 | * So we need this watchdog, but must protect it against both | ||
304 | * (a) SMP races against real IAA firing and retriggering, and | ||
305 | * (b) clean HC shutdown, when IAA watchdog was pending. | ||
306 | */ | ||
307 | if (ehci->async_unlink) { | ||
308 | u32 cmd, status; | ||
309 | |||
310 | /* If we get here, IAA is *REALLY* late. It's barely | ||
311 | * conceivable that the system is so busy that CMD_IAAD | ||
312 | * is still legitimately set, so let's be sure it's | ||
313 | * clear before we read STS_IAA. (The HC should clear | ||
314 | * CMD_IAAD when it sets STS_IAA.) | ||
315 | */ | ||
316 | cmd = ehci_readl(ehci, &ehci->regs->command); | ||
317 | |||
318 | /* | ||
319 | * If IAA is set here it either legitimately triggered | ||
320 | * after the watchdog timer expired (_way_ late, so we'll | ||
321 | * still count it as lost) ... or a silicon erratum: | ||
322 | * - VIA seems to set IAA without triggering the IRQ; | ||
323 | * - IAAD potentially cleared without setting IAA. | ||
324 | */ | ||
325 | status = ehci_readl(ehci, &ehci->regs->status); | ||
326 | if ((status & STS_IAA) || !(cmd & CMD_IAAD)) { | ||
327 | COUNT(ehci->stats.lost_iaa); | ||
328 | ehci_writel(ehci, STS_IAA, &ehci->regs->status); | ||
329 | } | ||
330 | |||
331 | ehci_vdbg(ehci, "IAA watchdog: status %x cmd %x\n", | ||
332 | status, cmd); | ||
333 | end_unlink_async(ehci); | ||
334 | } | ||
335 | } | ||
336 | |||
337 | |||
294 | /* | 338 | /* |
295 | * Handler functions for the hrtimer event types. | 339 | * Handler functions for the hrtimer event types. |
296 | * Keep this array in the same order as the event types indexed by | 340 | * Keep this array in the same order as the event types indexed by |
@@ -302,6 +346,7 @@ static void (*event_handlers[])(struct ehci_hcd *) = { | |||
302 | ehci_handle_controller_death, /* EHCI_HRTIMER_POLL_DEAD */ | 346 | ehci_handle_controller_death, /* EHCI_HRTIMER_POLL_DEAD */ |
303 | ehci_handle_intr_unlinks, /* EHCI_HRTIMER_UNLINK_INTR */ | 347 | ehci_handle_intr_unlinks, /* EHCI_HRTIMER_UNLINK_INTR */ |
304 | end_free_itds, /* EHCI_HRTIMER_FREE_ITDS */ | 348 | end_free_itds, /* EHCI_HRTIMER_FREE_ITDS */ |
349 | ehci_iaa_watchdog, /* EHCI_HRTIMER_IAA_WATCHDOG */ | ||
305 | ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */ | 350 | ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */ |
306 | ehci_disable_ASE, /* EHCI_HRTIMER_DISABLE_ASYNC */ | 351 | ehci_disable_ASE, /* EHCI_HRTIMER_DISABLE_ASYNC */ |
307 | }; | 352 | }; |