diff options
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 1 | ||||
-rw-r--r-- | drivers/usb/host/ehci-hub.c | 1 | ||||
-rw-r--r-- | drivers/usb/host/ehci-mem.c | 1 | ||||
-rw-r--r-- | drivers/usb/host/ehci-sched.c | 47 | ||||
-rw-r--r-- | drivers/usb/host/ehci-timer.c | 34 | ||||
-rw-r--r-- | drivers/usb/host/ehci.h | 3 |
6 files changed, 84 insertions, 3 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 387cedf2b698..7ad9ef8aedee 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c | |||
@@ -487,6 +487,7 @@ static int ehci_init(struct usb_hcd *hcd) | |||
487 | ehci->periodic_size = DEFAULT_I_TDPS; | 487 | ehci->periodic_size = DEFAULT_I_TDPS; |
488 | INIT_LIST_HEAD(&ehci->async_unlink); | 488 | INIT_LIST_HEAD(&ehci->async_unlink); |
489 | INIT_LIST_HEAD(&ehci->async_idle); | 489 | INIT_LIST_HEAD(&ehci->async_idle); |
490 | INIT_LIST_HEAD(&ehci->intr_unlink_wait); | ||
490 | INIT_LIST_HEAD(&ehci->intr_unlink); | 491 | INIT_LIST_HEAD(&ehci->intr_unlink); |
491 | INIT_LIST_HEAD(&ehci->intr_qh_list); | 492 | INIT_LIST_HEAD(&ehci->intr_qh_list); |
492 | INIT_LIST_HEAD(&ehci->cached_itd_list); | 493 | INIT_LIST_HEAD(&ehci->cached_itd_list); |
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 6e69ee1a3371..269a2e8ad0ab 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c | |||
@@ -345,6 +345,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) | |||
345 | 345 | ||
346 | end_unlink_async(ehci); | 346 | end_unlink_async(ehci); |
347 | unlink_empty_async_suspended(ehci); | 347 | unlink_empty_async_suspended(ehci); |
348 | ehci_handle_start_intr_unlinks(ehci); | ||
348 | ehci_handle_intr_unlinks(ehci); | 349 | ehci_handle_intr_unlinks(ehci); |
349 | end_free_itds(ehci); | 350 | end_free_itds(ehci); |
350 | 351 | ||
diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c index ef2c3a1eca4b..52a77734a225 100644 --- a/drivers/usb/host/ehci-mem.c +++ b/drivers/usb/host/ehci-mem.c | |||
@@ -93,6 +93,7 @@ static struct ehci_qh *ehci_qh_alloc (struct ehci_hcd *ehci, gfp_t flags) | |||
93 | qh->qh_dma = dma; | 93 | qh->qh_dma = dma; |
94 | // INIT_LIST_HEAD (&qh->qh_list); | 94 | // INIT_LIST_HEAD (&qh->qh_list); |
95 | INIT_LIST_HEAD (&qh->qtd_list); | 95 | INIT_LIST_HEAD (&qh->qtd_list); |
96 | INIT_LIST_HEAD(&qh->unlink_node); | ||
96 | 97 | ||
97 | /* dummy td enables safe urb queuing */ | 98 | /* dummy td enables safe urb queuing */ |
98 | qh->dummy = ehci_qtd_alloc (ehci, flags); | 99 | qh->dummy = ehci_qtd_alloc (ehci, flags); |
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index f80d0330d548..94388738a6f7 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c | |||
@@ -601,12 +601,29 @@ static void qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh) | |||
601 | list_del(&qh->intr_node); | 601 | list_del(&qh->intr_node); |
602 | } | 602 | } |
603 | 603 | ||
604 | static void cancel_unlink_wait_intr(struct ehci_hcd *ehci, struct ehci_qh *qh) | ||
605 | { | ||
606 | if (qh->qh_state != QH_STATE_LINKED || | ||
607 | list_empty(&qh->unlink_node)) | ||
608 | return; | ||
609 | |||
610 | list_del_init(&qh->unlink_node); | ||
611 | |||
612 | /* | ||
613 | * TODO: disable the event of EHCI_HRTIMER_START_UNLINK_INTR for | ||
614 | * avoiding unnecessary CPU wakeup | ||
615 | */ | ||
616 | } | ||
617 | |||
604 | static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh) | 618 | static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh) |
605 | { | 619 | { |
606 | /* If the QH isn't linked then there's nothing we can do. */ | 620 | /* If the QH isn't linked then there's nothing we can do. */ |
607 | if (qh->qh_state != QH_STATE_LINKED) | 621 | if (qh->qh_state != QH_STATE_LINKED) |
608 | return; | 622 | return; |
609 | 623 | ||
624 | /* if the qh is waiting for unlink, cancel it now */ | ||
625 | cancel_unlink_wait_intr(ehci, qh); | ||
626 | |||
610 | qh_unlink_periodic (ehci, qh); | 627 | qh_unlink_periodic (ehci, qh); |
611 | 628 | ||
612 | /* Make sure the unlinks are visible before starting the timer */ | 629 | /* Make sure the unlinks are visible before starting the timer */ |
@@ -632,6 +649,27 @@ static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh) | |||
632 | } | 649 | } |
633 | } | 650 | } |
634 | 651 | ||
652 | /* | ||
653 | * It is common only one intr URB is scheduled on one qh, and | ||
654 | * given complete() is run in tasklet context, introduce a bit | ||
655 | * delay to avoid unlink qh too early. | ||
656 | */ | ||
657 | static void start_unlink_intr_wait(struct ehci_hcd *ehci, | ||
658 | struct ehci_qh *qh) | ||
659 | { | ||
660 | qh->unlink_cycle = ehci->intr_unlink_wait_cycle; | ||
661 | |||
662 | /* New entries go at the end of the intr_unlink_wait list */ | ||
663 | list_add_tail(&qh->unlink_node, &ehci->intr_unlink_wait); | ||
664 | |||
665 | if (ehci->rh_state < EHCI_RH_RUNNING) | ||
666 | ehci_handle_start_intr_unlinks(ehci); | ||
667 | else if (ehci->intr_unlink_wait.next == &qh->unlink_node) { | ||
668 | ehci_enable_event(ehci, EHCI_HRTIMER_START_UNLINK_INTR, true); | ||
669 | ++ehci->intr_unlink_wait_cycle; | ||
670 | } | ||
671 | } | ||
672 | |||
635 | static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh) | 673 | static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh) |
636 | { | 674 | { |
637 | struct ehci_qh_hw *hw = qh->hw; | 675 | struct ehci_qh_hw *hw = qh->hw; |
@@ -889,6 +927,9 @@ static int intr_submit ( | |||
889 | if (qh->qh_state == QH_STATE_IDLE) { | 927 | if (qh->qh_state == QH_STATE_IDLE) { |
890 | qh_refresh(ehci, qh); | 928 | qh_refresh(ehci, qh); |
891 | qh_link_periodic(ehci, qh); | 929 | qh_link_periodic(ehci, qh); |
930 | } else { | ||
931 | /* cancel unlink wait for the qh */ | ||
932 | cancel_unlink_wait_intr(ehci, qh); | ||
892 | } | 933 | } |
893 | 934 | ||
894 | /* ... update usbfs periodic stats */ | 935 | /* ... update usbfs periodic stats */ |
@@ -924,9 +965,11 @@ static void scan_intr(struct ehci_hcd *ehci) | |||
924 | * in qh_unlink_periodic(). | 965 | * in qh_unlink_periodic(). |
925 | */ | 966 | */ |
926 | temp = qh_completions(ehci, qh); | 967 | temp = qh_completions(ehci, qh); |
927 | if (unlikely(temp || (list_empty(&qh->qtd_list) && | 968 | if (unlikely(temp)) |
928 | qh->qh_state == QH_STATE_LINKED))) | ||
929 | start_unlink_intr(ehci, qh); | 969 | start_unlink_intr(ehci, qh); |
970 | else if (unlikely(list_empty(&qh->qtd_list) && | ||
971 | qh->qh_state == QH_STATE_LINKED)) | ||
972 | start_unlink_intr_wait(ehci, qh); | ||
930 | } | 973 | } |
931 | } | 974 | } |
932 | } | 975 | } |
diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c index 11e5b32f73e9..424ac5d83714 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 | 5 * NSEC_PER_MSEC, /* EHCI_HRTIMER_START_UNLINK_INTR */ | ||
75 | 6 * NSEC_PER_MSEC, /* EHCI_HRTIMER_ASYNC_UNLINKS */ | 76 | 6 * NSEC_PER_MSEC, /* EHCI_HRTIMER_ASYNC_UNLINKS */ |
76 | 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_IAA_WATCHDOG */ | 77 | 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_IAA_WATCHDOG */ |
77 | 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */ | 78 | 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */ |
@@ -215,6 +216,36 @@ static void ehci_handle_controller_death(struct ehci_hcd *ehci) | |||
215 | /* Not in process context, so don't try to reset the controller */ | 216 | /* Not in process context, so don't try to reset the controller */ |
216 | } | 217 | } |
217 | 218 | ||
219 | /* start to unlink interrupt QHs */ | ||
220 | static void ehci_handle_start_intr_unlinks(struct ehci_hcd *ehci) | ||
221 | { | ||
222 | bool stopped = (ehci->rh_state < EHCI_RH_RUNNING); | ||
223 | |||
224 | /* | ||
225 | * Process all the QHs on the intr_unlink list that were added | ||
226 | * before the current unlink cycle began. The list is in | ||
227 | * temporal order, so stop when we reach the first entry in the | ||
228 | * current cycle. But if the root hub isn't running then | ||
229 | * process all the QHs on the list. | ||
230 | */ | ||
231 | while (!list_empty(&ehci->intr_unlink_wait)) { | ||
232 | struct ehci_qh *qh; | ||
233 | |||
234 | qh = list_first_entry(&ehci->intr_unlink_wait, | ||
235 | struct ehci_qh, unlink_node); | ||
236 | if (!stopped && (qh->unlink_cycle == | ||
237 | ehci->intr_unlink_wait_cycle)) | ||
238 | break; | ||
239 | list_del_init(&qh->unlink_node); | ||
240 | start_unlink_intr(ehci, qh); | ||
241 | } | ||
242 | |||
243 | /* Handle remaining entries later */ | ||
244 | if (!list_empty(&ehci->intr_unlink_wait)) { | ||
245 | ehci_enable_event(ehci, EHCI_HRTIMER_START_UNLINK_INTR, true); | ||
246 | ++ehci->intr_unlink_wait_cycle; | ||
247 | } | ||
248 | } | ||
218 | 249 | ||
219 | /* Handle unlinked interrupt QHs once they are gone from the hardware */ | 250 | /* Handle unlinked interrupt QHs once they are gone from the hardware */ |
220 | static void ehci_handle_intr_unlinks(struct ehci_hcd *ehci) | 251 | static void ehci_handle_intr_unlinks(struct ehci_hcd *ehci) |
@@ -236,7 +267,7 @@ static void ehci_handle_intr_unlinks(struct ehci_hcd *ehci) | |||
236 | unlink_node); | 267 | unlink_node); |
237 | if (!stopped && qh->unlink_cycle == ehci->intr_unlink_cycle) | 268 | if (!stopped && qh->unlink_cycle == ehci->intr_unlink_cycle) |
238 | break; | 269 | break; |
239 | list_del(&qh->unlink_node); | 270 | list_del_init(&qh->unlink_node); |
240 | end_unlink_intr(ehci, qh); | 271 | end_unlink_intr(ehci, qh); |
241 | } | 272 | } |
242 | 273 | ||
@@ -363,6 +394,7 @@ static void (*event_handlers[])(struct ehci_hcd *) = { | |||
363 | ehci_handle_controller_death, /* EHCI_HRTIMER_POLL_DEAD */ | 394 | ehci_handle_controller_death, /* EHCI_HRTIMER_POLL_DEAD */ |
364 | ehci_handle_intr_unlinks, /* EHCI_HRTIMER_UNLINK_INTR */ | 395 | ehci_handle_intr_unlinks, /* EHCI_HRTIMER_UNLINK_INTR */ |
365 | end_free_itds, /* EHCI_HRTIMER_FREE_ITDS */ | 396 | end_free_itds, /* EHCI_HRTIMER_FREE_ITDS */ |
397 | ehci_handle_start_intr_unlinks, /* EHCI_HRTIMER_START_UNLINK_INTR */ | ||
366 | unlink_empty_async, /* EHCI_HRTIMER_ASYNC_UNLINKS */ | 398 | unlink_empty_async, /* EHCI_HRTIMER_ASYNC_UNLINKS */ |
367 | ehci_iaa_watchdog, /* EHCI_HRTIMER_IAA_WATCHDOG */ | 399 | ehci_iaa_watchdog, /* EHCI_HRTIMER_IAA_WATCHDOG */ |
368 | ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */ | 400 | ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */ |
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 64f9a08e959c..947752015d5d 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h | |||
@@ -88,6 +88,7 @@ enum ehci_hrtimer_event { | |||
88 | EHCI_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */ | 88 | EHCI_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */ |
89 | EHCI_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */ | 89 | EHCI_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */ |
90 | EHCI_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */ | 90 | EHCI_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */ |
91 | EHCI_HRTIMER_START_UNLINK_INTR, /* Unlink empty interrupt QHs */ | ||
91 | EHCI_HRTIMER_ASYNC_UNLINKS, /* Unlink empty async QHs */ | 92 | EHCI_HRTIMER_ASYNC_UNLINKS, /* Unlink empty async QHs */ |
92 | EHCI_HRTIMER_IAA_WATCHDOG, /* Handle lost IAA interrupts */ | 93 | EHCI_HRTIMER_IAA_WATCHDOG, /* Handle lost IAA interrupts */ |
93 | EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ | 94 | EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ |
@@ -143,7 +144,9 @@ struct ehci_hcd { /* one per controller */ | |||
143 | unsigned i_thresh; /* uframes HC might cache */ | 144 | unsigned i_thresh; /* uframes HC might cache */ |
144 | 145 | ||
145 | union ehci_shadow *pshadow; /* mirror hw periodic table */ | 146 | union ehci_shadow *pshadow; /* mirror hw periodic table */ |
147 | struct list_head intr_unlink_wait; | ||
146 | struct list_head intr_unlink; | 148 | struct list_head intr_unlink; |
149 | unsigned intr_unlink_wait_cycle; | ||
147 | unsigned intr_unlink_cycle; | 150 | unsigned intr_unlink_cycle; |
148 | unsigned now_frame; /* frame from HC hardware */ | 151 | unsigned now_frame; /* frame from HC hardware */ |
149 | unsigned last_iso_frame; /* last frame scanned for iso */ | 152 | unsigned last_iso_frame; /* last frame scanned for iso */ |