diff options
| -rw-r--r-- | drivers/usb/host/ehci-dbg.c | 4 | ||||
| -rw-r--r-- | drivers/usb/host/ehci-hcd.c | 73 | ||||
| -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, 70 insertions, 39 deletions
diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 34b7a31cd85b..23b95b2bfe15 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c | |||
| @@ -754,9 +754,7 @@ show_registers (struct class_device *class_dev, char *buf) | |||
| 754 | } | 754 | } |
| 755 | 755 | ||
| 756 | if (ehci->reclaim) { | 756 | if (ehci->reclaim) { |
| 757 | temp = scnprintf (next, size, "reclaim qh %p%s\n", | 757 | temp = scnprintf (next, size, "reclaim qh %p\n", ehci->reclaim); |
| 758 | ehci->reclaim, | ||
| 759 | ehci->reclaim_ready ? " ready" : ""); | ||
| 760 | size -= temp; | 758 | size -= temp; |
| 761 | next += temp; | 759 | next += temp; |
| 762 | } | 760 | } |
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 4e1a8c308893..5ac918591131 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c | |||
| @@ -111,7 +111,7 @@ static const char hcd_name [] = "ehci_hcd"; | |||
| 111 | #define EHCI_TUNE_MULT_TT 1 | 111 | #define EHCI_TUNE_MULT_TT 1 |
| 112 | #define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */ | 112 | #define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */ |
| 113 | 113 | ||
| 114 | #define EHCI_IAA_JIFFIES (HZ/100) /* arbitrary; ~10 msec */ | 114 | #define EHCI_IAA_MSECS 10 /* arbitrary */ |
| 115 | #define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */ | 115 | #define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */ |
| 116 | #define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */ | 116 | #define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */ |
| 117 | #define EHCI_SHRINK_JIFFIES (HZ/200) /* async qh unlink delay */ | 117 | #define EHCI_SHRINK_JIFFIES (HZ/200) /* async qh unlink delay */ |
| @@ -254,6 +254,7 @@ static void ehci_quiesce (struct ehci_hcd *ehci) | |||
| 254 | 254 | ||
| 255 | /*-------------------------------------------------------------------------*/ | 255 | /*-------------------------------------------------------------------------*/ |
| 256 | 256 | ||
| 257 | static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs); | ||
| 257 | static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs); | 258 | static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs); |
| 258 | 259 | ||
| 259 | #include "ehci-hub.c" | 260 | #include "ehci-hub.c" |
| @@ -263,28 +264,39 @@ static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs); | |||
| 263 | 264 | ||
| 264 | /*-------------------------------------------------------------------------*/ | 265 | /*-------------------------------------------------------------------------*/ |
| 265 | 266 | ||
| 266 | static void ehci_watchdog (unsigned long param) | 267 | static void ehci_iaa_watchdog (unsigned long param) |
| 267 | { | 268 | { |
| 268 | struct ehci_hcd *ehci = (struct ehci_hcd *) param; | 269 | struct ehci_hcd *ehci = (struct ehci_hcd *) param; |
| 269 | unsigned long flags; | 270 | unsigned long flags; |
| 271 | u32 status; | ||
| 270 | 272 | ||
| 271 | spin_lock_irqsave (&ehci->lock, flags); | 273 | spin_lock_irqsave (&ehci->lock, flags); |
| 274 | WARN_ON(!ehci->reclaim); | ||
| 272 | 275 | ||
| 273 | /* lost IAA irqs wedge things badly; seen with a vt8235 */ | 276 | /* lost IAA irqs wedge things badly; seen first with a vt8235 */ |
| 274 | if (ehci->reclaim) { | 277 | if (ehci->reclaim) { |
| 275 | u32 status = readl (&ehci->regs->status); | 278 | status = readl (&ehci->regs->status); |
| 276 | |||
| 277 | if (status & STS_IAA) { | 279 | if (status & STS_IAA) { |
| 278 | ehci_vdbg (ehci, "lost IAA\n"); | 280 | ehci_vdbg (ehci, "lost IAA\n"); |
| 279 | COUNT (ehci->stats.lost_iaa); | 281 | COUNT (ehci->stats.lost_iaa); |
| 280 | writel (STS_IAA, &ehci->regs->status); | 282 | writel (STS_IAA, &ehci->regs->status); |
| 281 | ehci->reclaim_ready = 1; | 283 | end_unlink_async (ehci, NULL); |
| 282 | } | 284 | } |
| 283 | } | 285 | } |
| 284 | 286 | ||
| 285 | /* stop async processing after it's idled a bit */ | 287 | spin_unlock_irqrestore (&ehci->lock, flags); |
| 288 | } | ||
| 289 | |||
| 290 | static void ehci_watchdog (unsigned long param) | ||
| 291 | { | ||
| 292 | struct ehci_hcd *ehci = (struct ehci_hcd *) param; | ||
| 293 | unsigned long flags; | ||
| 294 | |||
| 295 | spin_lock_irqsave (&ehci->lock, flags); | ||
| 296 | |||
| 297 | /* stop async processing after it's idled a bit */ | ||
| 286 | if (test_bit (TIMER_ASYNC_OFF, &ehci->actions)) | 298 | if (test_bit (TIMER_ASYNC_OFF, &ehci->actions)) |
| 287 | start_unlink_async (ehci, ehci->async); | 299 | start_unlink_async (ehci, ehci->async); |
| 288 | 300 | ||
| 289 | /* ehci could run by timer, without IRQs ... */ | 301 | /* ehci could run by timer, without IRQs ... */ |
| 290 | ehci_work (ehci, NULL); | 302 | ehci_work (ehci, NULL); |
| @@ -333,8 +345,6 @@ static void ehci_port_power (struct ehci_hcd *ehci, int is_on) | |||
| 333 | static void ehci_work (struct ehci_hcd *ehci, struct pt_regs *regs) | 345 | static void ehci_work (struct ehci_hcd *ehci, struct pt_regs *regs) |
| 334 | { | 346 | { |
| 335 | timer_action_done (ehci, TIMER_IO_WATCHDOG); | 347 | timer_action_done (ehci, TIMER_IO_WATCHDOG); |
| 336 | if (ehci->reclaim_ready) | ||
| 337 | end_unlink_async (ehci, regs); | ||
| 338 | 348 | ||
| 339 | /* another CPU may drop ehci->lock during a schedule scan while | 349 | /* another CPU may drop ehci->lock during a schedule scan while |
| 340 | * it reports urb completions. this flag guards against bogus | 350 | * it reports urb completions. this flag guards against bogus |
| @@ -369,6 +379,7 @@ static void ehci_stop (struct usb_hcd *hcd) | |||
| 369 | 379 | ||
| 370 | /* no more interrupts ... */ | 380 | /* no more interrupts ... */ |
| 371 | del_timer_sync (&ehci->watchdog); | 381 | del_timer_sync (&ehci->watchdog); |
| 382 | del_timer_sync (&ehci->iaa_watchdog); | ||
| 372 | 383 | ||
| 373 | spin_lock_irq(&ehci->lock); | 384 | spin_lock_irq(&ehci->lock); |
| 374 | if (HC_IS_RUNNING (hcd->state)) | 385 | if (HC_IS_RUNNING (hcd->state)) |
| @@ -415,6 +426,10 @@ static int ehci_init(struct usb_hcd *hcd) | |||
| 415 | ehci->watchdog.function = ehci_watchdog; | 426 | ehci->watchdog.function = ehci_watchdog; |
| 416 | ehci->watchdog.data = (unsigned long) ehci; | 427 | ehci->watchdog.data = (unsigned long) ehci; |
| 417 | 428 | ||
| 429 | init_timer(&ehci->iaa_watchdog); | ||
| 430 | ehci->iaa_watchdog.function = ehci_iaa_watchdog; | ||
| 431 | ehci->iaa_watchdog.data = (unsigned long) ehci; | ||
| 432 | |||
| 418 | /* | 433 | /* |
| 419 | * hw default: 1K periodic list heads, one per frame. | 434 | * hw default: 1K periodic list heads, one per frame. |
| 420 | * periodic_size can shrink by USBCMD update if hcc_params allows. | 435 | * periodic_size can shrink by USBCMD update if hcc_params allows. |
| @@ -431,7 +446,6 @@ static int ehci_init(struct usb_hcd *hcd) | |||
| 431 | ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params); | 446 | ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params); |
| 432 | 447 | ||
| 433 | ehci->reclaim = NULL; | 448 | ehci->reclaim = NULL; |
| 434 | ehci->reclaim_ready = 0; | ||
| 435 | ehci->next_uframe = -1; | 449 | ehci->next_uframe = -1; |
| 436 | 450 | ||
| 437 | /* | 451 | /* |
| @@ -605,7 +619,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs) | |||
| 605 | /* complete the unlinking of some qh [4.15.2.3] */ | 619 | /* complete the unlinking of some qh [4.15.2.3] */ |
| 606 | if (status & STS_IAA) { | 620 | if (status & STS_IAA) { |
| 607 | COUNT (ehci->stats.reclaim); | 621 | COUNT (ehci->stats.reclaim); |
| 608 | ehci->reclaim_ready = 1; | 622 | end_unlink_async (ehci, regs); |
| 609 | bh = 1; | 623 | bh = 1; |
| 610 | } | 624 | } |
| 611 | 625 | ||
| @@ -709,10 +723,14 @@ static int ehci_urb_enqueue ( | |||
| 709 | 723 | ||
| 710 | static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) | 724 | static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) |
| 711 | { | 725 | { |
| 712 | /* if we need to use IAA and it's busy, defer */ | 726 | // BUG_ON(qh->qh_state != QH_STATE_LINKED); |
| 713 | if (qh->qh_state == QH_STATE_LINKED | 727 | |
| 714 | && ehci->reclaim | 728 | /* failfast */ |
| 715 | && HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) { | 729 | if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) |
| 730 | end_unlink_async (ehci, NULL); | ||
| 731 | |||
| 732 | /* defer till later if busy */ | ||
| 733 | else if (ehci->reclaim) { | ||
| 716 | struct ehci_qh *last; | 734 | struct ehci_qh *last; |
| 717 | 735 | ||
| 718 | for (last = ehci->reclaim; | 736 | for (last = ehci->reclaim; |
| @@ -722,12 +740,8 @@ static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) | |||
| 722 | qh->qh_state = QH_STATE_UNLINK_WAIT; | 740 | qh->qh_state = QH_STATE_UNLINK_WAIT; |
| 723 | last->reclaim = qh; | 741 | last->reclaim = qh; |
| 724 | 742 | ||
| 725 | /* bypass IAA if the hc can't care */ | 743 | /* start IAA cycle */ |
| 726 | } else if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state) && ehci->reclaim) | 744 | } else |
| 727 | end_unlink_async (ehci, NULL); | ||
| 728 | |||
| 729 | /* something else might have unlinked the qh by now */ | ||
| 730 | if (qh->qh_state == QH_STATE_LINKED) | ||
| 731 | start_unlink_async (ehci, qh); | 745 | start_unlink_async (ehci, qh); |
| 732 | } | 746 | } |
| 733 | 747 | ||
| @@ -749,7 +763,19 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) | |||
| 749 | qh = (struct ehci_qh *) urb->hcpriv; | 763 | qh = (struct ehci_qh *) urb->hcpriv; |
| 750 | if (!qh) | 764 | if (!qh) |
| 751 | break; | 765 | break; |
| 752 | unlink_async (ehci, qh); | 766 | switch (qh->qh_state) { |
| 767 | case QH_STATE_LINKED: | ||
| 768 | case QH_STATE_COMPLETING: | ||
| 769 | unlink_async (ehci, qh); | ||
| 770 | break; | ||
| 771 | case QH_STATE_UNLINK: | ||
| 772 | case QH_STATE_UNLINK_WAIT: | ||
| 773 | /* already started */ | ||
| 774 | break; | ||
| 775 | case QH_STATE_IDLE: | ||
| 776 | WARN_ON(1); | ||
| 777 | break; | ||
| 778 | } | ||
| 753 | break; | 779 | break; |
| 754 | 780 | ||
| 755 | case PIPE_INTERRUPT: | 781 | case PIPE_INTERRUPT: |
| @@ -841,6 +867,7 @@ rescan: | |||
| 841 | unlink_async (ehci, qh); | 867 | unlink_async (ehci, qh); |
| 842 | /* FALL THROUGH */ | 868 | /* FALL THROUGH */ |
| 843 | case QH_STATE_UNLINK: /* wait for hw to finish? */ | 869 | case QH_STATE_UNLINK: /* wait for hw to finish? */ |
| 870 | case QH_STATE_UNLINK_WAIT: | ||
| 844 | idle_timeout: | 871 | idle_timeout: |
| 845 | spin_unlock_irqrestore (&ehci->lock, flags); | 872 | spin_unlock_irqrestore (&ehci->lock, flags); |
| 846 | schedule_timeout_uninterruptible(1); | 873 | schedule_timeout_uninterruptible(1); |
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index a5eeb9cd6ab2..b2ee13c58517 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c | |||
| @@ -48,7 +48,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) | |||
| 48 | } | 48 | } |
| 49 | ehci->command = readl (&ehci->regs->command); | 49 | ehci->command = readl (&ehci->regs->command); |
| 50 | if (ehci->reclaim) | 50 | if (ehci->reclaim) |
| 51 | ehci->reclaim_ready = 1; | 51 | end_unlink_async (ehci, NULL); |
| 52 | ehci_work(ehci, NULL); | 52 | ehci_work(ehci, NULL); |
| 53 | 53 | ||
| 54 | /* suspend any active/unsuspended ports, maybe allow wakeup */ | 54 | /* suspend any active/unsuspended ports, maybe allow wakeup */ |
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index e6a3bcddd55b..08d0472d4f57 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c | |||
| @@ -303,7 +303,7 @@ restart: | |||
| 303 | /* emptying the schedule aborts any urbs */ | 303 | /* emptying the schedule aborts any urbs */ |
| 304 | spin_lock_irq(&ehci->lock); | 304 | spin_lock_irq(&ehci->lock); |
| 305 | if (ehci->reclaim) | 305 | if (ehci->reclaim) |
| 306 | ehci->reclaim_ready = 1; | 306 | end_unlink_async (ehci, NULL); |
| 307 | ehci_work(ehci, NULL); | 307 | ehci_work(ehci, NULL); |
| 308 | spin_unlock_irq(&ehci->lock); | 308 | spin_unlock_irq(&ehci->lock); |
| 309 | 309 | ||
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index c0da40bbfa35..7fc25b6bd7d2 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c | |||
| @@ -967,7 +967,7 @@ static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs) | |||
| 967 | struct ehci_qh *qh = ehci->reclaim; | 967 | struct ehci_qh *qh = ehci->reclaim; |
| 968 | struct ehci_qh *next; | 968 | struct ehci_qh *next; |
| 969 | 969 | ||
| 970 | timer_action_done (ehci, TIMER_IAA_WATCHDOG); | 970 | iaa_watchdog_done (ehci); |
| 971 | 971 | ||
| 972 | // qh->hw_next = cpu_to_le32 (qh->qh_dma); | 972 | // qh->hw_next = cpu_to_le32 (qh->qh_dma); |
| 973 | qh->qh_state = QH_STATE_IDLE; | 973 | qh->qh_state = QH_STATE_IDLE; |
| @@ -977,7 +977,6 @@ static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs) | |||
| 977 | /* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */ | 977 | /* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */ |
| 978 | next = qh->reclaim; | 978 | next = qh->reclaim; |
| 979 | ehci->reclaim = next; | 979 | ehci->reclaim = next; |
| 980 | ehci->reclaim_ready = 0; | ||
| 981 | qh->reclaim = NULL; | 980 | qh->reclaim = NULL; |
| 982 | 981 | ||
| 983 | qh_completions (ehci, qh, regs); | 982 | qh_completions (ehci, qh, regs); |
| @@ -1052,11 +1051,10 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) | |||
| 1052 | return; | 1051 | return; |
| 1053 | } | 1052 | } |
| 1054 | 1053 | ||
| 1055 | ehci->reclaim_ready = 0; | ||
| 1056 | cmd |= CMD_IAAD; | 1054 | cmd |= CMD_IAAD; |
| 1057 | writel (cmd, &ehci->regs->command); | 1055 | writel (cmd, &ehci->regs->command); |
| 1058 | (void) readl (&ehci->regs->command); | 1056 | (void) readl (&ehci->regs->command); |
| 1059 | timer_action (ehci, TIMER_IAA_WATCHDOG); | 1057 | iaa_watchdog_start (ehci); |
| 1060 | } | 1058 | } |
| 1061 | 1059 | ||
| 1062 | /*-------------------------------------------------------------------------*/ | 1060 | /*-------------------------------------------------------------------------*/ |
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index bbc3082a73d7..6aac39f50e07 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h | |||
| @@ -58,7 +58,6 @@ struct ehci_hcd { /* one per controller */ | |||
| 58 | /* async schedule support */ | 58 | /* async schedule support */ |
| 59 | struct ehci_qh *async; | 59 | struct ehci_qh *async; |
| 60 | struct ehci_qh *reclaim; | 60 | struct ehci_qh *reclaim; |
| 61 | unsigned reclaim_ready : 1; | ||
| 62 | unsigned scanning : 1; | 61 | unsigned scanning : 1; |
| 63 | 62 | ||
| 64 | /* periodic schedule support */ | 63 | /* periodic schedule support */ |
| @@ -81,6 +80,7 @@ struct ehci_hcd { /* one per controller */ | |||
| 81 | struct dma_pool *itd_pool; /* itd per iso urb */ | 80 | struct dma_pool *itd_pool; /* itd per iso urb */ |
| 82 | struct dma_pool *sitd_pool; /* sitd per split iso urb */ | 81 | struct dma_pool *sitd_pool; /* sitd per split iso urb */ |
| 83 | 82 | ||
| 83 | struct timer_list iaa_watchdog; | ||
| 84 | struct timer_list watchdog; | 84 | struct timer_list watchdog; |
| 85 | unsigned long actions; | 85 | unsigned long actions; |
| 86 | unsigned stamp; | 86 | unsigned stamp; |
| @@ -114,9 +114,21 @@ static inline struct usb_hcd *ehci_to_hcd (struct ehci_hcd *ehci) | |||
| 114 | } | 114 | } |
| 115 | 115 | ||
| 116 | 116 | ||
| 117 | static inline void | ||
| 118 | iaa_watchdog_start (struct ehci_hcd *ehci) | ||
| 119 | { | ||
| 120 | WARN_ON(timer_pending(&ehci->iaa_watchdog)); | ||
| 121 | mod_timer (&ehci->iaa_watchdog, | ||
| 122 | jiffies + msecs_to_jiffies(EHCI_IAA_MSECS)); | ||
| 123 | } | ||
| 124 | |||
| 125 | static inline void iaa_watchdog_done (struct ehci_hcd *ehci) | ||
| 126 | { | ||
| 127 | del_timer (&ehci->iaa_watchdog); | ||
| 128 | } | ||
| 129 | |||
| 117 | enum ehci_timer_action { | 130 | enum ehci_timer_action { |
| 118 | TIMER_IO_WATCHDOG, | 131 | TIMER_IO_WATCHDOG, |
| 119 | TIMER_IAA_WATCHDOG, | ||
| 120 | TIMER_ASYNC_SHRINK, | 132 | TIMER_ASYNC_SHRINK, |
| 121 | TIMER_ASYNC_OFF, | 133 | TIMER_ASYNC_OFF, |
| 122 | }; | 134 | }; |
| @@ -134,9 +146,6 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action) | |||
| 134 | unsigned long t; | 146 | unsigned long t; |
| 135 | 147 | ||
| 136 | switch (action) { | 148 | switch (action) { |
| 137 | case TIMER_IAA_WATCHDOG: | ||
| 138 | t = EHCI_IAA_JIFFIES; | ||
| 139 | break; | ||
| 140 | case TIMER_IO_WATCHDOG: | 149 | case TIMER_IO_WATCHDOG: |
| 141 | t = EHCI_IO_JIFFIES; | 150 | t = EHCI_IO_JIFFIES; |
| 142 | break; | 151 | break; |
| @@ -153,8 +162,7 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action) | |||
| 153 | // async queue SHRINK often precedes IAA. while it's ready | 162 | // async queue SHRINK often precedes IAA. while it's ready |
| 154 | // to go OFF neither can matter, and afterwards the IO | 163 | // to go OFF neither can matter, and afterwards the IO |
| 155 | // watchdog stops unless there's still periodic traffic. | 164 | // watchdog stops unless there's still periodic traffic. |
| 156 | if (action != TIMER_IAA_WATCHDOG | 165 | if (time_before_eq(t, ehci->watchdog.expires) |
| 157 | && t > ehci->watchdog.expires | ||
| 158 | && timer_pending (&ehci->watchdog)) | 166 | && timer_pending (&ehci->watchdog)) |
| 159 | return; | 167 | return; |
| 160 | mod_timer (&ehci->watchdog, t); | 168 | mod_timer (&ehci->watchdog, t); |
