diff options
| author | Alan Stern <stern@rowland.harvard.edu> | 2007-12-11 16:05:30 -0500 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2008-02-01 17:34:55 -0500 |
| commit | 07d29b63ef6b39963ab37818653284d861cf55af (patch) | |
| tree | 10460d9d13ad7284b644181af66ecdbf416cc5ba | |
| parent | 2e2eb83ffd1aeb92bf8793eea892b5bc05a993ea (diff) | |
USB: EHCI: add separate IAA watchdog timer
This patch (as1028) was mostly written by David Brownell; I made only
a few changes (extra log info and a small bug fix -- which might
account for why David's version had to be reverted). It adds a new
watchdog timer to the ehci-hcd driver to be used exclusively for
detecting lost or missing IAA notifications.
Previously a shared timer had been used, which may have led to some
problems as reported by Christian Hoffmann.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Cc: David Brownell <david-b@pacbell.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
| -rw-r--r-- | drivers/usb/host/ehci-dbg.c | 4 | ||||
| -rw-r--r-- | drivers/usb/host/ehci-hcd.c | 77 | ||||
| -rw-r--r-- | drivers/usb/host/ehci-hub.c | 2 | ||||
| -rw-r--r-- | drivers/usb/host/ehci-pci.c | 2 | ||||
| -rw-r--r-- | drivers/usb/host/ehci-q.c | 6 | ||||
| -rw-r--r-- | drivers/usb/host/ehci.h | 22 |
6 files changed, 75 insertions, 38 deletions
diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 39673f8194d9..64ebfc5548a3 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c | |||
| @@ -786,9 +786,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf) | |||
| 786 | } | 786 | } |
| 787 | 787 | ||
| 788 | if (ehci->reclaim) { | 788 | if (ehci->reclaim) { |
| 789 | temp = scnprintf (next, size, "reclaim qh %p%s\n", | 789 | temp = scnprintf(next, size, "reclaim qh %p\n", ehci->reclaim); |
| 790 | ehci->reclaim, | ||
| 791 | ehci->reclaim_ready ? " ready" : ""); | ||
| 792 | size -= temp; | 790 | size -= temp; |
| 793 | next += temp; | 791 | next += temp; |
| 794 | } | 792 | } |
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 264182654f4c..5917c6b7ce33 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c | |||
| @@ -110,7 +110,7 @@ static const char hcd_name [] = "ehci_hcd"; | |||
| 110 | #define EHCI_TUNE_MULT_TT 1 | 110 | #define EHCI_TUNE_MULT_TT 1 |
| 111 | #define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */ | 111 | #define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */ |
| 112 | 112 | ||
| 113 | #define EHCI_IAA_JIFFIES (HZ/100) /* arbitrary; ~10 msec */ | 113 | #define EHCI_IAA_MSECS 10 /* arbitrary */ |
| 114 | #define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */ | 114 | #define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */ |
| 115 | #define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */ | 115 | #define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */ |
| 116 | #define EHCI_SHRINK_JIFFIES (HZ/200) /* async qh unlink delay */ | 116 | #define EHCI_SHRINK_JIFFIES (HZ/200) /* async qh unlink delay */ |
| @@ -267,6 +267,7 @@ static void ehci_quiesce (struct ehci_hcd *ehci) | |||
| 267 | 267 | ||
| 268 | /*-------------------------------------------------------------------------*/ | 268 | /*-------------------------------------------------------------------------*/ |
| 269 | 269 | ||
| 270 | static void end_unlink_async(struct ehci_hcd *ehci); | ||
| 270 | static void ehci_work(struct ehci_hcd *ehci); | 271 | static void ehci_work(struct ehci_hcd *ehci); |
| 271 | 272 | ||
| 272 | #include "ehci-hub.c" | 273 | #include "ehci-hub.c" |
| @@ -276,25 +277,41 @@ static void ehci_work(struct ehci_hcd *ehci); | |||
| 276 | 277 | ||
| 277 | /*-------------------------------------------------------------------------*/ | 278 | /*-------------------------------------------------------------------------*/ |
| 278 | 279 | ||
| 279 | static void ehci_watchdog (unsigned long param) | 280 | static void ehci_iaa_watchdog(unsigned long param) |
| 280 | { | 281 | { |
| 281 | struct ehci_hcd *ehci = (struct ehci_hcd *) param; | 282 | struct ehci_hcd *ehci = (struct ehci_hcd *) param; |
| 282 | unsigned long flags; | 283 | unsigned long flags; |
| 284 | u32 status, cmd; | ||
| 283 | 285 | ||
| 284 | spin_lock_irqsave (&ehci->lock, flags); | 286 | spin_lock_irqsave (&ehci->lock, flags); |
| 287 | WARN_ON(!ehci->reclaim); | ||
| 285 | 288 | ||
| 286 | /* lost IAA irqs wedge things badly; seen with a vt8235 */ | 289 | status = ehci_readl(ehci, &ehci->regs->status); |
| 290 | cmd = ehci_readl(ehci, &ehci->regs->command); | ||
| 291 | ehci_dbg(ehci, "IAA watchdog: status %x cmd %x\n", status, cmd); | ||
| 292 | |||
| 293 | /* lost IAA irqs wedge things badly; seen first with a vt8235 */ | ||
| 287 | if (ehci->reclaim) { | 294 | if (ehci->reclaim) { |
| 288 | u32 status = ehci_readl(ehci, &ehci->regs->status); | ||
| 289 | if (status & STS_IAA) { | 295 | if (status & STS_IAA) { |
| 290 | ehci_vdbg (ehci, "lost IAA\n"); | 296 | ehci_vdbg (ehci, "lost IAA\n"); |
| 291 | COUNT (ehci->stats.lost_iaa); | 297 | COUNT (ehci->stats.lost_iaa); |
| 292 | ehci_writel(ehci, STS_IAA, &ehci->regs->status); | 298 | ehci_writel(ehci, STS_IAA, &ehci->regs->status); |
| 293 | ehci->reclaim_ready = 1; | ||
| 294 | } | 299 | } |
| 300 | ehci_writel(ehci, cmd & ~CMD_IAAD, &ehci->regs->command); | ||
| 301 | end_unlink_async(ehci); | ||
| 295 | } | 302 | } |
| 296 | 303 | ||
| 297 | /* stop async processing after it's idled a bit */ | 304 | spin_unlock_irqrestore(&ehci->lock, flags); |
| 305 | } | ||
| 306 | |||
| 307 | static void ehci_watchdog(unsigned long param) | ||
| 308 | { | ||
| 309 | struct ehci_hcd *ehci = (struct ehci_hcd *) param; | ||
| 310 | unsigned long flags; | ||
| 311 | |||
| 312 | spin_lock_irqsave(&ehci->lock, flags); | ||
| 313 | |||
| 314 | /* stop async processing after it's idled a bit */ | ||
| 298 | if (test_bit (TIMER_ASYNC_OFF, &ehci->actions)) | 315 | if (test_bit (TIMER_ASYNC_OFF, &ehci->actions)) |
| 299 | start_unlink_async (ehci, ehci->async); | 316 | start_unlink_async (ehci, ehci->async); |
| 300 | 317 | ||
| @@ -364,8 +381,6 @@ static void ehci_port_power (struct ehci_hcd *ehci, int is_on) | |||
| 364 | static void ehci_work (struct ehci_hcd *ehci) | 381 | static void ehci_work (struct ehci_hcd *ehci) |
| 365 | { | 382 | { |
| 366 | timer_action_done (ehci, TIMER_IO_WATCHDOG); | 383 | timer_action_done (ehci, TIMER_IO_WATCHDOG); |
| 367 | if (ehci->reclaim_ready) | ||
| 368 | end_unlink_async (ehci); | ||
| 369 | 384 | ||
| 370 | /* another CPU may drop ehci->lock during a schedule scan while | 385 | /* another CPU may drop ehci->lock during a schedule scan while |
| 371 | * it reports urb completions. this flag guards against bogus | 386 | * it reports urb completions. this flag guards against bogus |
| @@ -400,6 +415,7 @@ static void ehci_stop (struct usb_hcd *hcd) | |||
| 400 | 415 | ||
| 401 | /* no more interrupts ... */ | 416 | /* no more interrupts ... */ |
| 402 | del_timer_sync (&ehci->watchdog); | 417 | del_timer_sync (&ehci->watchdog); |
| 418 | del_timer_sync(&ehci->iaa_watchdog); | ||
| 403 | 419 | ||
| 404 | spin_lock_irq(&ehci->lock); | 420 | spin_lock_irq(&ehci->lock); |
| 405 | if (HC_IS_RUNNING (hcd->state)) | 421 | if (HC_IS_RUNNING (hcd->state)) |
| @@ -448,6 +464,10 @@ static int ehci_init(struct usb_hcd *hcd) | |||
| 448 | ehci->watchdog.function = ehci_watchdog; | 464 | ehci->watchdog.function = ehci_watchdog; |
| 449 | ehci->watchdog.data = (unsigned long) ehci; | 465 | ehci->watchdog.data = (unsigned long) ehci; |
| 450 | 466 | ||
| 467 | init_timer(&ehci->iaa_watchdog); | ||
| 468 | ehci->iaa_watchdog.function = ehci_iaa_watchdog; | ||
| 469 | ehci->iaa_watchdog.data = (unsigned long) ehci; | ||
| 470 | |||
| 451 | /* | 471 | /* |
| 452 | * hw default: 1K periodic list heads, one per frame. | 472 | * hw default: 1K periodic list heads, one per frame. |
| 453 | * periodic_size can shrink by USBCMD update if hcc_params allows. | 473 | * periodic_size can shrink by USBCMD update if hcc_params allows. |
| @@ -464,7 +484,6 @@ static int ehci_init(struct usb_hcd *hcd) | |||
| 464 | ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params); | 484 | ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params); |
| 465 | 485 | ||
| 466 | ehci->reclaim = NULL; | 486 | ehci->reclaim = NULL; |
| 467 | ehci->reclaim_ready = 0; | ||
| 468 | ehci->next_uframe = -1; | 487 | ehci->next_uframe = -1; |
| 469 | 488 | ||
| 470 | /* | 489 | /* |
| @@ -655,8 +674,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) | |||
| 655 | /* complete the unlinking of some qh [4.15.2.3] */ | 674 | /* complete the unlinking of some qh [4.15.2.3] */ |
| 656 | if (status & STS_IAA) { | 675 | if (status & STS_IAA) { |
| 657 | COUNT (ehci->stats.reclaim); | 676 | COUNT (ehci->stats.reclaim); |
| 658 | ehci->reclaim_ready = 1; | 677 | end_unlink_async(ehci); |
| 659 | bh = 1; | ||
| 660 | } | 678 | } |
| 661 | 679 | ||
| 662 | /* remote wakeup [4.3.1] */ | 680 | /* remote wakeup [4.3.1] */ |
| @@ -762,10 +780,16 @@ static int ehci_urb_enqueue ( | |||
| 762 | 780 | ||
| 763 | static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) | 781 | static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) |
| 764 | { | 782 | { |
| 765 | /* if we need to use IAA and it's busy, defer */ | 783 | /* failfast */ |
| 766 | if (qh->qh_state == QH_STATE_LINKED | 784 | if (!HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) |
| 767 | && ehci->reclaim | 785 | end_unlink_async(ehci); |
| 768 | && HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) { | 786 | |
| 787 | /* if it's not linked then there's nothing to do */ | ||
| 788 | if (qh->qh_state != QH_STATE_LINKED) | ||
| 789 | ; | ||
| 790 | |||
| 791 | /* defer till later if busy */ | ||
| 792 | else if (ehci->reclaim) { | ||
| 769 | struct ehci_qh *last; | 793 | struct ehci_qh *last; |
| 770 | 794 | ||
| 771 | for (last = ehci->reclaim; | 795 | for (last = ehci->reclaim; |
| @@ -775,12 +799,8 @@ static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) | |||
| 775 | qh->qh_state = QH_STATE_UNLINK_WAIT; | 799 | qh->qh_state = QH_STATE_UNLINK_WAIT; |
| 776 | last->reclaim = qh; | 800 | last->reclaim = qh; |
| 777 | 801 | ||
| 778 | /* bypass IAA if the hc can't care */ | 802 | /* start IAA cycle */ |
| 779 | } else if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state) && ehci->reclaim) | 803 | } else |
| 780 | end_unlink_async (ehci); | ||
| 781 | |||
| 782 | /* something else might have unlinked the qh by now */ | ||
| 783 | if (qh->qh_state == QH_STATE_LINKED) | ||
| 784 | start_unlink_async (ehci, qh); | 804 | start_unlink_async (ehci, qh); |
| 785 | } | 805 | } |
| 786 | 806 | ||
| @@ -807,7 +827,19 @@ static int ehci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) | |||
| 807 | qh = (struct ehci_qh *) urb->hcpriv; | 827 | qh = (struct ehci_qh *) urb->hcpriv; |
| 808 | if (!qh) | 828 | if (!qh) |
| 809 | break; | 829 | break; |
| 810 | unlink_async (ehci, qh); | 830 | switch (qh->qh_state) { |
| 831 | case QH_STATE_LINKED: | ||
| 832 | case QH_STATE_COMPLETING: | ||
| 833 | unlink_async(ehci, qh); | ||
| 834 | break; | ||
| 835 | case QH_STATE_UNLINK: | ||
| 836 | case QH_STATE_UNLINK_WAIT: | ||
| 837 | /* already started */ | ||
| 838 | break; | ||
| 839 | case QH_STATE_IDLE: | ||
| 840 | WARN_ON(1); | ||
| 841 | break; | ||
| 842 | } | ||
| 811 | break; | 843 | break; |
| 812 | 844 | ||
| 813 | case PIPE_INTERRUPT: | 845 | case PIPE_INTERRUPT: |
| @@ -899,6 +931,7 @@ rescan: | |||
| 899 | unlink_async (ehci, qh); | 931 | unlink_async (ehci, qh); |
| 900 | /* FALL THROUGH */ | 932 | /* FALL THROUGH */ |
| 901 | case QH_STATE_UNLINK: /* wait for hw to finish? */ | 933 | case QH_STATE_UNLINK: /* wait for hw to finish? */ |
| 934 | case QH_STATE_UNLINK_WAIT: | ||
| 902 | idle_timeout: | 935 | idle_timeout: |
| 903 | spin_unlock_irqrestore (&ehci->lock, flags); | 936 | spin_unlock_irqrestore (&ehci->lock, flags); |
| 904 | schedule_timeout_uninterruptible(1); | 937 | schedule_timeout_uninterruptible(1); |
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index a165e0a0961c..a249d03a5024 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c | |||
| @@ -134,7 +134,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) | |||
| 134 | } | 134 | } |
| 135 | ehci->command = ehci_readl(ehci, &ehci->regs->command); | 135 | ehci->command = ehci_readl(ehci, &ehci->regs->command); |
| 136 | if (ehci->reclaim) | 136 | if (ehci->reclaim) |
| 137 | ehci->reclaim_ready = 1; | 137 | end_unlink_async(ehci); |
| 138 | ehci_work(ehci); | 138 | ehci_work(ehci); |
| 139 | 139 | ||
| 140 | /* Unlike other USB host controller types, EHCI doesn't have | 140 | /* Unlike other USB host controller types, EHCI doesn't have |
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 45e040000280..3ba01664f821 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c | |||
| @@ -305,7 +305,7 @@ static int ehci_pci_resume(struct usb_hcd *hcd) | |||
| 305 | /* emptying the schedule aborts any urbs */ | 305 | /* emptying the schedule aborts any urbs */ |
| 306 | spin_lock_irq(&ehci->lock); | 306 | spin_lock_irq(&ehci->lock); |
| 307 | if (ehci->reclaim) | 307 | if (ehci->reclaim) |
| 308 | ehci->reclaim_ready = 1; | 308 | end_unlink_async(ehci); |
| 309 | ehci_work(ehci); | 309 | ehci_work(ehci); |
| 310 | spin_unlock_irq(&ehci->lock); | 310 | spin_unlock_irq(&ehci->lock); |
| 311 | 311 | ||
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index b10f39c047e9..853e5e6396a5 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c | |||
| @@ -973,7 +973,7 @@ static void end_unlink_async (struct ehci_hcd *ehci) | |||
| 973 | struct ehci_qh *qh = ehci->reclaim; | 973 | struct ehci_qh *qh = ehci->reclaim; |
| 974 | struct ehci_qh *next; | 974 | struct ehci_qh *next; |
| 975 | 975 | ||
| 976 | timer_action_done (ehci, TIMER_IAA_WATCHDOG); | 976 | iaa_watchdog_done(ehci); |
| 977 | 977 | ||
| 978 | // qh->hw_next = cpu_to_hc32(qh->qh_dma); | 978 | // qh->hw_next = cpu_to_hc32(qh->qh_dma); |
| 979 | qh->qh_state = QH_STATE_IDLE; | 979 | qh->qh_state = QH_STATE_IDLE; |
| @@ -983,7 +983,6 @@ static void end_unlink_async (struct ehci_hcd *ehci) | |||
| 983 | /* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */ | 983 | /* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */ |
| 984 | next = qh->reclaim; | 984 | next = qh->reclaim; |
| 985 | ehci->reclaim = next; | 985 | ehci->reclaim = next; |
| 986 | ehci->reclaim_ready = 0; | ||
| 987 | qh->reclaim = NULL; | 986 | qh->reclaim = NULL; |
| 988 | 987 | ||
| 989 | qh_completions (ehci, qh); | 988 | qh_completions (ehci, qh); |
| @@ -1059,11 +1058,10 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) | |||
| 1059 | return; | 1058 | return; |
| 1060 | } | 1059 | } |
| 1061 | 1060 | ||
| 1062 | ehci->reclaim_ready = 0; | ||
| 1063 | cmd |= CMD_IAAD; | 1061 | cmd |= CMD_IAAD; |
| 1064 | ehci_writel(ehci, cmd, &ehci->regs->command); | 1062 | ehci_writel(ehci, cmd, &ehci->regs->command); |
| 1065 | (void)ehci_readl(ehci, &ehci->regs->command); | 1063 | (void)ehci_readl(ehci, &ehci->regs->command); |
| 1066 | timer_action (ehci, TIMER_IAA_WATCHDOG); | 1064 | iaa_watchdog_start(ehci); |
| 1067 | } | 1065 | } |
| 1068 | 1066 | ||
| 1069 | /*-------------------------------------------------------------------------*/ | 1067 | /*-------------------------------------------------------------------------*/ |
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 10e71417c352..eeda4c88ebae 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h | |||
| @@ -74,7 +74,6 @@ struct ehci_hcd { /* one per controller */ | |||
| 74 | /* async schedule support */ | 74 | /* async schedule support */ |
| 75 | struct ehci_qh *async; | 75 | struct ehci_qh *async; |
| 76 | struct ehci_qh *reclaim; | 76 | struct ehci_qh *reclaim; |
| 77 | unsigned reclaim_ready : 1; | ||
| 78 | unsigned scanning : 1; | 77 | unsigned scanning : 1; |
| 79 | 78 | ||
| 80 | /* periodic schedule support */ | 79 | /* periodic schedule support */ |
| @@ -105,6 +104,7 @@ struct ehci_hcd { /* one per controller */ | |||
| 105 | struct dma_pool *itd_pool; /* itd per iso urb */ | 104 | struct dma_pool *itd_pool; /* itd per iso urb */ |
| 106 | struct dma_pool *sitd_pool; /* sitd per split iso urb */ | 105 | struct dma_pool *sitd_pool; /* sitd per split iso urb */ |
| 107 | 106 | ||
| 107 | struct timer_list iaa_watchdog; | ||
| 108 | struct timer_list watchdog; | 108 | struct timer_list watchdog; |
| 109 | unsigned long actions; | 109 | unsigned long actions; |
| 110 | unsigned stamp; | 110 | unsigned stamp; |
| @@ -148,9 +148,21 @@ static inline struct usb_hcd *ehci_to_hcd (struct ehci_hcd *ehci) | |||
| 148 | } | 148 | } |
| 149 | 149 | ||
| 150 | 150 | ||
| 151 | static inline void | ||
| 152 | iaa_watchdog_start(struct ehci_hcd *ehci) | ||
| 153 | { | ||
| 154 | WARN_ON(timer_pending(&ehci->iaa_watchdog)); | ||
| 155 | mod_timer(&ehci->iaa_watchdog, | ||
| 156 | jiffies + msecs_to_jiffies(EHCI_IAA_MSECS)); | ||
| 157 | } | ||
| 158 | |||
| 159 | static inline void iaa_watchdog_done(struct ehci_hcd *ehci) | ||
| 160 | { | ||
| 161 | del_timer(&ehci->iaa_watchdog); | ||
| 162 | } | ||
| 163 | |||
| 151 | enum ehci_timer_action { | 164 | enum ehci_timer_action { |
| 152 | TIMER_IO_WATCHDOG, | 165 | TIMER_IO_WATCHDOG, |
| 153 | TIMER_IAA_WATCHDOG, | ||
| 154 | TIMER_ASYNC_SHRINK, | 166 | TIMER_ASYNC_SHRINK, |
| 155 | TIMER_ASYNC_OFF, | 167 | TIMER_ASYNC_OFF, |
| 156 | }; | 168 | }; |
| @@ -168,9 +180,6 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action) | |||
| 168 | unsigned long t; | 180 | unsigned long t; |
| 169 | 181 | ||
| 170 | switch (action) { | 182 | switch (action) { |
| 171 | case TIMER_IAA_WATCHDOG: | ||
| 172 | t = EHCI_IAA_JIFFIES; | ||
| 173 | break; | ||
| 174 | case TIMER_IO_WATCHDOG: | 183 | case TIMER_IO_WATCHDOG: |
| 175 | t = EHCI_IO_JIFFIES; | 184 | t = EHCI_IO_JIFFIES; |
| 176 | break; | 185 | break; |
| @@ -187,8 +196,7 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action) | |||
| 187 | // async queue SHRINK often precedes IAA. while it's ready | 196 | // async queue SHRINK often precedes IAA. while it's ready |
| 188 | // to go OFF neither can matter, and afterwards the IO | 197 | // to go OFF neither can matter, and afterwards the IO |
| 189 | // watchdog stops unless there's still periodic traffic. | 198 | // watchdog stops unless there's still periodic traffic. |
| 190 | if (action != TIMER_IAA_WATCHDOG | 199 | if (time_before_eq(t, ehci->watchdog.expires) |
| 191 | && t > ehci->watchdog.expires | ||
| 192 | && timer_pending (&ehci->watchdog)) | 200 | && timer_pending (&ehci->watchdog)) |
| 193 | return; | 201 | return; |
| 194 | mod_timer (&ehci->watchdog, t); | 202 | mod_timer (&ehci->watchdog, t); |
