| From 340a3504fd39dad753ba908fb6f894ee81fc3ae2 Mon Sep 17 00:00:00 2001 |
| From: Sarah Sharp <sarah.a.sharp@linux.intel.com> |
| Date: Mon, 13 Feb 2012 14:42:11 -0800 |
| Subject: xhci: Fix encoding for HS bulk/control NAK rate. |
| |
| From: Sarah Sharp <sarah.a.sharp@linux.intel.com> |
| |
| commit 340a3504fd39dad753ba908fb6f894ee81fc3ae2 upstream. |
| |
| The xHCI 0.96 spec says that HS bulk and control endpoint NAK rate must |
| be encoded as an exponent of two number of microframes. The endpoint |
| descriptor has the NAK rate encoded in number of microframes. We were |
| just copying the value from the endpoint descriptor into the endpoint |
| context interval field, which was not correct. This lead to the VIA |
| host rejecting the add of a bulk OUT endpoint from any USB 2.0 mass |
| storage device. |
| |
| The fix is to use the correct encoding. Refactor the code to convert |
| number of frames to an exponential number of microframes, and make sure |
| we convert the number of microframes in HS bulk and control endpoints to |
| an exponent. |
| |
| This should be back ported to kernels as old as 2.6.31, that contain the |
| commit dfa49c4ad120a784ef1ff0717168aa79f55a483a "USB: xhci - fix math |
| in xhci_get_endpoint_interval" |
| |
| Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> |
| Tested-by: Felipe Contreras <felipe.contreras@gmail.com> |
| Suggested-by: Andiry Xu <andiry.xu@amd.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/usb/host/xhci-mem.c | 32 ++++++++++++++++++++++++-------- |
| 1 file changed, 24 insertions(+), 8 deletions(-) |
| |
| --- a/drivers/usb/host/xhci-mem.c |
| +++ b/drivers/usb/host/xhci-mem.c |
| @@ -472,26 +472,42 @@ static unsigned int xhci_parse_exponent_ |
| } |
| |
| /* |
| - * Convert bInterval expressed in frames (in 1-255 range) to exponent of |
| + * Convert bInterval expressed in microframes (in 1-255 range) to exponent of |
| * microframes, rounded down to nearest power of 2. |
| */ |
| -static unsigned int xhci_parse_frame_interval(struct usb_device *udev, |
| - struct usb_host_endpoint *ep) |
| +static unsigned int xhci_microframes_to_exponent(struct usb_device *udev, |
| + struct usb_host_endpoint *ep, unsigned int desc_interval, |
| + unsigned int min_exponent, unsigned int max_exponent) |
| { |
| unsigned int interval; |
| |
| - interval = fls(8 * ep->desc.bInterval) - 1; |
| - interval = clamp_val(interval, 3, 10); |
| - if ((1 << interval) != 8 * ep->desc.bInterval) |
| + interval = fls(desc_interval) - 1; |
| + interval = clamp_val(interval, min_exponent, max_exponent); |
| + if ((1 << interval) != desc_interval) |
| dev_warn(&udev->dev, |
| "ep %#x - rounding interval to %d microframes, ep desc says %d microframes\n", |
| ep->desc.bEndpointAddress, |
| 1 << interval, |
| - 8 * ep->desc.bInterval); |
| + desc_interval); |
| |
| return interval; |
| } |
| |
| +static unsigned int xhci_parse_microframe_interval(struct usb_device *udev, |
| + struct usb_host_endpoint *ep) |
| +{ |
| + return xhci_microframes_to_exponent(udev, ep, |
| + ep->desc.bInterval, 0, 15); |
| +} |
| + |
| + |
| +static unsigned int xhci_parse_frame_interval(struct usb_device *udev, |
| + struct usb_host_endpoint *ep) |
| +{ |
| + return xhci_microframes_to_exponent(udev, ep, |
| + ep->desc.bInterval * 8, 3, 10); |
| +} |
| + |
| /* Return the polling or NAK interval. |
| * |
| * The polling interval is expressed in "microframes". If xHCI's Interval field |
| @@ -510,7 +526,7 @@ static inline unsigned int xhci_get_endp |
| /* Max NAK rate */ |
| if (usb_endpoint_xfer_control(&ep->desc) || |
| usb_endpoint_xfer_bulk(&ep->desc)) { |
| - interval = ep->desc.bInterval; |
| + interval = xhci_parse_microframe_interval(udev, ep); |
| break; |
| } |
| /* Fall through - SS and HS isoc/int have same decoding */ |