aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/host/ehci-hcd.c1
-rw-r--r--drivers/usb/host/ehci-hub.c1
-rw-r--r--drivers/usb/host/ehci-mem.c1
-rw-r--r--drivers/usb/host/ehci-sched.c47
-rw-r--r--drivers/usb/host/ehci-timer.c34
-rw-r--r--drivers/usb/host/ehci.h3
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
604static 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
604static void start_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh) 618static 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 */
657static 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
635static void end_unlink_intr(struct ehci_hcd *ehci, struct ehci_qh *qh) 673static 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 */
220static 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 */
220static void ehci_handle_intr_unlinks(struct ehci_hcd *ehci) 251static 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 */