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