diff options
Diffstat (limited to 'drivers/usb/host')
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 2 | ||||
-rw-r--r-- | drivers/usb/host/ehci-mem.c | 1 | ||||
-rw-r--r-- | drivers/usb/host/ehci-q.c | 3 | ||||
-rw-r--r-- | drivers/usb/host/ehci-sched.c | 66 | ||||
-rw-r--r-- | drivers/usb/host/ehci.h | 6 |
5 files changed, 63 insertions, 15 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 4725d15d096f..e551bb38852b 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c | |||
@@ -485,6 +485,7 @@ static int ehci_init(struct usb_hcd *hcd) | |||
485 | * periodic_size can shrink by USBCMD update if hcc_params allows. | 485 | * periodic_size can shrink by USBCMD update if hcc_params allows. |
486 | */ | 486 | */ |
487 | ehci->periodic_size = DEFAULT_I_TDPS; | 487 | ehci->periodic_size = DEFAULT_I_TDPS; |
488 | INIT_LIST_HEAD(&ehci->cached_itd_list); | ||
488 | if ((retval = ehci_mem_init(ehci, GFP_KERNEL)) < 0) | 489 | if ((retval = ehci_mem_init(ehci, GFP_KERNEL)) < 0) |
489 | return retval; | 490 | return retval; |
490 | 491 | ||
@@ -497,6 +498,7 @@ static int ehci_init(struct usb_hcd *hcd) | |||
497 | 498 | ||
498 | ehci->reclaim = NULL; | 499 | ehci->reclaim = NULL; |
499 | ehci->next_uframe = -1; | 500 | ehci->next_uframe = -1; |
501 | ehci->clock_frame = -1; | ||
500 | 502 | ||
501 | /* | 503 | /* |
502 | * dedicate a qh for the async ring head, since we couldn't unlink | 504 | * dedicate a qh for the async ring head, since we couldn't unlink |
diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c index 0431397836f6..10d52919abbb 100644 --- a/drivers/usb/host/ehci-mem.c +++ b/drivers/usb/host/ehci-mem.c | |||
@@ -128,6 +128,7 @@ static inline void qh_put (struct ehci_qh *qh) | |||
128 | 128 | ||
129 | static void ehci_mem_cleanup (struct ehci_hcd *ehci) | 129 | static void ehci_mem_cleanup (struct ehci_hcd *ehci) |
130 | { | 130 | { |
131 | free_cached_itd_list(ehci); | ||
131 | if (ehci->async) | 132 | if (ehci->async) |
132 | qh_put (ehci->async); | 133 | qh_put (ehci->async); |
133 | ehci->async = NULL; | 134 | ehci->async = NULL; |
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 3712b925b315..ecc9b66c03cd 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c | |||
@@ -1095,7 +1095,8 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) | |||
1095 | prev->qh_next = qh->qh_next; | 1095 | prev->qh_next = qh->qh_next; |
1096 | wmb (); | 1096 | wmb (); |
1097 | 1097 | ||
1098 | if (unlikely (ehci_to_hcd(ehci)->state == HC_STATE_HALT)) { | 1098 | /* If the controller isn't running, we don't have to wait for it */ |
1099 | if (unlikely(!HC_IS_RUNNING(ehci_to_hcd(ehci)->state))) { | ||
1099 | /* if (unlikely (qh->reclaim != 0)) | 1100 | /* if (unlikely (qh->reclaim != 0)) |
1100 | * this will recurse, probably not much | 1101 | * this will recurse, probably not much |
1101 | */ | 1102 | */ |
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index a081ee65bde6..1d0b49e3f192 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c | |||
@@ -1004,7 +1004,8 @@ iso_stream_put(struct ehci_hcd *ehci, struct ehci_iso_stream *stream) | |||
1004 | 1004 | ||
1005 | is_in = (stream->bEndpointAddress & USB_DIR_IN) ? 0x10 : 0; | 1005 | is_in = (stream->bEndpointAddress & USB_DIR_IN) ? 0x10 : 0; |
1006 | stream->bEndpointAddress &= 0x0f; | 1006 | stream->bEndpointAddress &= 0x0f; |
1007 | stream->ep->hcpriv = NULL; | 1007 | if (stream->ep) |
1008 | stream->ep->hcpriv = NULL; | ||
1008 | 1009 | ||
1009 | if (stream->rescheduled) { | 1010 | if (stream->rescheduled) { |
1010 | ehci_info (ehci, "ep%d%s-iso rescheduled " | 1011 | ehci_info (ehci, "ep%d%s-iso rescheduled " |
@@ -1535,7 +1536,7 @@ itd_link_urb ( | |||
1535 | struct ehci_itd, itd_list); | 1536 | struct ehci_itd, itd_list); |
1536 | list_move_tail (&itd->itd_list, &stream->td_list); | 1537 | list_move_tail (&itd->itd_list, &stream->td_list); |
1537 | itd->stream = iso_stream_get (stream); | 1538 | itd->stream = iso_stream_get (stream); |
1538 | itd->urb = usb_get_urb (urb); | 1539 | itd->urb = urb; |
1539 | itd_init (ehci, stream, itd); | 1540 | itd_init (ehci, stream, itd); |
1540 | } | 1541 | } |
1541 | 1542 | ||
@@ -1644,7 +1645,7 @@ itd_complete ( | |||
1644 | (void) disable_periodic(ehci); | 1645 | (void) disable_periodic(ehci); |
1645 | ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--; | 1646 | ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--; |
1646 | 1647 | ||
1647 | if (unlikely (list_empty (&stream->td_list))) { | 1648 | if (unlikely(list_is_singular(&stream->td_list))) { |
1648 | ehci_to_hcd(ehci)->self.bandwidth_allocated | 1649 | ehci_to_hcd(ehci)->self.bandwidth_allocated |
1649 | -= stream->bandwidth; | 1650 | -= stream->bandwidth; |
1650 | ehci_vdbg (ehci, | 1651 | ehci_vdbg (ehci, |
@@ -1653,14 +1654,27 @@ itd_complete ( | |||
1653 | (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); | 1654 | (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); |
1654 | } | 1655 | } |
1655 | iso_stream_put (ehci, stream); | 1656 | iso_stream_put (ehci, stream); |
1656 | /* OK to recycle this ITD now that its completion callback ran. */ | 1657 | |
1657 | done: | 1658 | done: |
1658 | usb_put_urb(urb); | ||
1659 | itd->urb = NULL; | 1659 | itd->urb = NULL; |
1660 | itd->stream = NULL; | 1660 | if (ehci->clock_frame != itd->frame || itd->index[7] != -1) { |
1661 | list_move(&itd->itd_list, &stream->free_list); | 1661 | /* OK to recycle this ITD now. */ |
1662 | iso_stream_put(ehci, stream); | 1662 | itd->stream = NULL; |
1663 | 1663 | list_move(&itd->itd_list, &stream->free_list); | |
1664 | iso_stream_put(ehci, stream); | ||
1665 | } else { | ||
1666 | /* HW might remember this ITD, so we can't recycle it yet. | ||
1667 | * Move it to a safe place until a new frame starts. | ||
1668 | */ | ||
1669 | list_move(&itd->itd_list, &ehci->cached_itd_list); | ||
1670 | if (stream->refcount == 2) { | ||
1671 | /* If iso_stream_put() were called here, stream | ||
1672 | * would be freed. Instead, just prevent reuse. | ||
1673 | */ | ||
1674 | stream->ep->hcpriv = NULL; | ||
1675 | stream->ep = NULL; | ||
1676 | } | ||
1677 | } | ||
1664 | return retval; | 1678 | return retval; |
1665 | } | 1679 | } |
1666 | 1680 | ||
@@ -1934,7 +1948,7 @@ sitd_link_urb ( | |||
1934 | struct ehci_sitd, sitd_list); | 1948 | struct ehci_sitd, sitd_list); |
1935 | list_move_tail (&sitd->sitd_list, &stream->td_list); | 1949 | list_move_tail (&sitd->sitd_list, &stream->td_list); |
1936 | sitd->stream = iso_stream_get (stream); | 1950 | sitd->stream = iso_stream_get (stream); |
1937 | sitd->urb = usb_get_urb (urb); | 1951 | sitd->urb = urb; |
1938 | 1952 | ||
1939 | sitd_patch(ehci, stream, sitd, sched, packet); | 1953 | sitd_patch(ehci, stream, sitd, sched, packet); |
1940 | sitd_link (ehci, (next_uframe >> 3) % ehci->periodic_size, | 1954 | sitd_link (ehci, (next_uframe >> 3) % ehci->periodic_size, |
@@ -2019,7 +2033,7 @@ sitd_complete ( | |||
2019 | (void) disable_periodic(ehci); | 2033 | (void) disable_periodic(ehci); |
2020 | ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--; | 2034 | ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--; |
2021 | 2035 | ||
2022 | if (list_empty (&stream->td_list)) { | 2036 | if (list_is_singular(&stream->td_list)) { |
2023 | ehci_to_hcd(ehci)->self.bandwidth_allocated | 2037 | ehci_to_hcd(ehci)->self.bandwidth_allocated |
2024 | -= stream->bandwidth; | 2038 | -= stream->bandwidth; |
2025 | ehci_vdbg (ehci, | 2039 | ehci_vdbg (ehci, |
@@ -2030,7 +2044,6 @@ sitd_complete ( | |||
2030 | iso_stream_put (ehci, stream); | 2044 | iso_stream_put (ehci, stream); |
2031 | /* OK to recycle this SITD now that its completion callback ran. */ | 2045 | /* OK to recycle this SITD now that its completion callback ran. */ |
2032 | done: | 2046 | done: |
2033 | usb_put_urb(urb); | ||
2034 | sitd->urb = NULL; | 2047 | sitd->urb = NULL; |
2035 | sitd->stream = NULL; | 2048 | sitd->stream = NULL; |
2036 | list_move(&sitd->sitd_list, &stream->free_list); | 2049 | list_move(&sitd->sitd_list, &stream->free_list); |
@@ -2101,6 +2114,20 @@ done: | |||
2101 | 2114 | ||
2102 | /*-------------------------------------------------------------------------*/ | 2115 | /*-------------------------------------------------------------------------*/ |
2103 | 2116 | ||
2117 | static void free_cached_itd_list(struct ehci_hcd *ehci) | ||
2118 | { | ||
2119 | struct ehci_itd *itd, *n; | ||
2120 | |||
2121 | list_for_each_entry_safe(itd, n, &ehci->cached_itd_list, itd_list) { | ||
2122 | struct ehci_iso_stream *stream = itd->stream; | ||
2123 | itd->stream = NULL; | ||
2124 | list_move(&itd->itd_list, &stream->free_list); | ||
2125 | iso_stream_put(ehci, stream); | ||
2126 | } | ||
2127 | } | ||
2128 | |||
2129 | /*-------------------------------------------------------------------------*/ | ||
2130 | |||
2104 | static void | 2131 | static void |
2105 | scan_periodic (struct ehci_hcd *ehci) | 2132 | scan_periodic (struct ehci_hcd *ehci) |
2106 | { | 2133 | { |
@@ -2115,10 +2142,17 @@ scan_periodic (struct ehci_hcd *ehci) | |||
2115 | * Touches as few pages as possible: cache-friendly. | 2142 | * Touches as few pages as possible: cache-friendly. |
2116 | */ | 2143 | */ |
2117 | now_uframe = ehci->next_uframe; | 2144 | now_uframe = ehci->next_uframe; |
2118 | if (HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) | 2145 | if (HC_IS_RUNNING(ehci_to_hcd(ehci)->state)) { |
2119 | clock = ehci_readl(ehci, &ehci->regs->frame_index); | 2146 | clock = ehci_readl(ehci, &ehci->regs->frame_index); |
2120 | else | 2147 | clock_frame = (clock >> 3) % ehci->periodic_size; |
2148 | } else { | ||
2121 | clock = now_uframe + mod - 1; | 2149 | clock = now_uframe + mod - 1; |
2150 | clock_frame = -1; | ||
2151 | } | ||
2152 | if (ehci->clock_frame != clock_frame) { | ||
2153 | free_cached_itd_list(ehci); | ||
2154 | ehci->clock_frame = clock_frame; | ||
2155 | } | ||
2122 | clock %= mod; | 2156 | clock %= mod; |
2123 | clock_frame = clock >> 3; | 2157 | clock_frame = clock >> 3; |
2124 | 2158 | ||
@@ -2277,6 +2311,10 @@ restart: | |||
2277 | /* rescan the rest of this frame, then ... */ | 2311 | /* rescan the rest of this frame, then ... */ |
2278 | clock = now; | 2312 | clock = now; |
2279 | clock_frame = clock >> 3; | 2313 | clock_frame = clock >> 3; |
2314 | if (ehci->clock_frame != clock_frame) { | ||
2315 | free_cached_itd_list(ehci); | ||
2316 | ehci->clock_frame = clock_frame; | ||
2317 | } | ||
2280 | } else { | 2318 | } else { |
2281 | now_uframe++; | 2319 | now_uframe++; |
2282 | now_uframe %= mod; | 2320 | now_uframe %= mod; |
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index fb7054ccf4fc..262b00c9b334 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h | |||
@@ -87,6 +87,10 @@ struct ehci_hcd { /* one per controller */ | |||
87 | int next_uframe; /* scan periodic, start here */ | 87 | int next_uframe; /* scan periodic, start here */ |
88 | unsigned periodic_sched; /* periodic activity count */ | 88 | unsigned periodic_sched; /* periodic activity count */ |
89 | 89 | ||
90 | /* list of itds completed while clock_frame was still active */ | ||
91 | struct list_head cached_itd_list; | ||
92 | unsigned clock_frame; | ||
93 | |||
90 | /* per root hub port */ | 94 | /* per root hub port */ |
91 | unsigned long reset_done [EHCI_MAX_ROOT_PORTS]; | 95 | unsigned long reset_done [EHCI_MAX_ROOT_PORTS]; |
92 | 96 | ||
@@ -220,6 +224,8 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action) | |||
220 | } | 224 | } |
221 | } | 225 | } |
222 | 226 | ||
227 | static void free_cached_itd_list(struct ehci_hcd *ehci); | ||
228 | |||
223 | /*-------------------------------------------------------------------------*/ | 229 | /*-------------------------------------------------------------------------*/ |
224 | 230 | ||
225 | #include <linux/usb/ehci_def.h> | 231 | #include <linux/usb/ehci_def.h> |