diff options
Diffstat (limited to 'drivers/usb/host/ehci-hcd.c')
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 73 |
1 files changed, 50 insertions, 23 deletions
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); |