[U-Boot] [PATCH 11/14] usb: xhci: Honor endpoint's interval

Stefan Roese sr at denx.de
Fri Sep 22 05:06:42 UTC 2017


On 18.09.2017 15:40, Bin Meng wrote:
> USB endpoint reports the period between consecutive requests to send
> or receive data as bInverval in its endpoint descriptor. So far this
> is ignored by xHCI driver and the 'Interval' field in xHC's endpoint
> context is always programmed to zero which means 1ms for low speed
> or full speed , or 125us for high speed or super speed. We should
> honor the interval by getting it from endpoint descriptor.
> 
> Signed-off-by: Bin Meng <bmeng.cn at gmail.com>
> ---
> 
>   drivers/usb/host/xhci.c | 195 ++++++++++++++++++++++++++++++++++++++++++++++++
>   drivers/usb/host/xhci.h |   5 +-
>   include/linux/usb/ch9.h |  20 +++++
>   3 files changed, 218 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
> index ec82fa6..8aed428 100644
> --- a/drivers/usb/host/xhci.c
> +++ b/drivers/usb/host/xhci.c
> @@ -257,6 +257,172 @@ static unsigned int xhci_get_ep_index(struct usb_endpoint_descriptor *desc)
>   	return index;
>   }
>   
> +/*
> + * Convert bInterval expressed in microframes (in 1-255 range) to exponent of
> + * microframes, rounded down to nearest power of 2.
> + */
> +static unsigned int xhci_microframes_to_exponent(unsigned int desc_interval,
> +						 unsigned int min_exponent,
> +						 unsigned int max_exponent)
> +{
> +	unsigned int interval;
> +
> +	interval = fls(desc_interval) - 1;
> +	interval = clamp_val(interval, min_exponent, max_exponent);
> +	if ((1 << interval) != desc_interval)
> +		debug("rounding interval to %d microframes, "\
> +		      "ep desc says %d microframes\n",
> +		      1 << interval, desc_interval);
> +
> +	return interval;
> +}
> +
> +static unsigned int xhci_parse_microframe_interval(struct usb_device *udev,
> +	struct usb_endpoint_descriptor *endpt_desc)
> +{
> +	if (endpt_desc->bInterval == 0)
> +		return 0;
> +
> +	return xhci_microframes_to_exponent(endpt_desc->bInterval, 0, 15);
> +}
> +
> +static unsigned int xhci_parse_frame_interval(struct usb_device *udev,
> +	struct usb_endpoint_descriptor *endpt_desc)
> +{
> +	return xhci_microframes_to_exponent(endpt_desc->bInterval * 8, 3, 10);
> +}
> +
> +/*
> + * Convert interval expressed as 2^(bInterval - 1) == interval into
> + * straight exponent value 2^n == interval.
> + */
> +static unsigned int xhci_parse_exponent_interval(struct usb_device *udev,
> +	struct usb_endpoint_descriptor *endpt_desc)
> +{
> +	unsigned int interval;
> +
> +	interval = clamp_val(endpt_desc->bInterval, 1, 16) - 1;
> +	if (interval != endpt_desc->bInterval - 1)
> +		debug("ep %#x - rounding interval to %d %sframes\n",
> +		      endpt_desc->bEndpointAddress, 1 << interval,
> +		      udev->speed == USB_SPEED_FULL ? "" : "micro");
> +
> +	if (udev->speed == USB_SPEED_FULL) {
> +		/*
> +		 * Full speed isoc endpoints specify interval in frames,
> +		 * not microframes. We are using microframes everywhere,
> +		 * so adjust accordingly.
> +		 */
> +		interval += 3;	/* 1 frame = 2^3 uframes */
> +	}
> +
> +	return interval;
> +}
> +
> +/*
> + * Return the polling or NAK interval.
> + *
> + * The polling interval is expressed in "microframes". If xHCI's Interval field
> + * is set to N, it will service the endpoint every 2^(Interval)*125us.
> + *
> + * The NAK interval is one NAK per 1 to 255 microframes, or no NAKs if interval
> + * is set to 0.
> + */
> +static unsigned int xhci_get_endpoint_interval(struct usb_device *udev,
> +	struct usb_endpoint_descriptor *endpt_desc)
> +{
> +	unsigned int interval = 0;
> +
> +	switch (udev->speed) {
> +	case USB_SPEED_HIGH:
> +		/* Max NAK rate */
> +		if (usb_endpoint_xfer_control(endpt_desc) ||
> +		    usb_endpoint_xfer_bulk(endpt_desc)) {
> +			interval = xhci_parse_microframe_interval(udev,
> +								  endpt_desc);
> +			break;
> +		}
> +		/* Fall through - SS and HS isoc/int have same decoding */
> +
> +	case USB_SPEED_SUPER:
> +		if (usb_endpoint_xfer_int(endpt_desc) ||
> +		    usb_endpoint_xfer_isoc(endpt_desc)) {
> +			interval = xhci_parse_exponent_interval(udev,
> +								endpt_desc);
> +		}
> +		break;
> +
> +	case USB_SPEED_FULL:
> +		if (usb_endpoint_xfer_isoc(endpt_desc)) {
> +			interval = xhci_parse_exponent_interval(udev,
> +								endpt_desc);
> +			break;
> +		}
> +		/*
> +		 * Fall through for interrupt endpoint interval decoding
> +		 * since it uses the same rules as low speed interrupt
> +		 * endpoints.
> +		 */
> +
> +	case USB_SPEED_LOW:
> +		if (usb_endpoint_xfer_int(endpt_desc) ||
> +		    usb_endpoint_xfer_isoc(endpt_desc)) {
> +			interval = xhci_parse_frame_interval(udev, endpt_desc);
> +		}
> +		break;
> +
> +	default:
> +		BUG();
> +	}
> +
> +	return interval;
> +}
> +
> +/*
> + * The "Mult" field in the endpoint context is only set for SuperSpeed isoc eps.
> + * High speed endpoint descriptors can define "the number of additional
> + * transaction opportunities per microframe", but that goes in the Max Burst
> + * endpoint context field.
> + */
> +static u32 xhci_get_endpoint_mult(struct usb_device *udev,
> +	struct usb_endpoint_descriptor *endpt_desc,
> +	struct usb_ss_ep_comp_descriptor *ss_ep_comp_desc)
> +{
> +	if (udev->speed < USB_SPEED_SUPER ||
> +	    !usb_endpoint_xfer_isoc(endpt_desc))
> +		return 0;
> +
> +	return ss_ep_comp_desc->bmAttributes;
> +}
> +
> +/*
> + * Return the maximum endpoint service interval time (ESIT) payload.
> + * Basically, this is the maxpacket size, multiplied by the burst size
> + * and mult size.
> + */
> +static u32 xhci_get_max_esit_payload(struct usb_device *udev,
> +	struct usb_endpoint_descriptor *endpt_desc,
> +	struct usb_ss_ep_comp_descriptor *ss_ep_comp_desc)
> +{
> +	int max_burst;
> +	int max_packet;
> +
> +	/* Only applies for interrupt or isochronous endpoints */
> +	if (usb_endpoint_xfer_control(endpt_desc) ||
> +	    usb_endpoint_xfer_bulk(endpt_desc))
> +		return 0;
> +
> +	/* SuperSpeed Isoc ep with less than 48k per esit */
> +	if (udev->speed >= USB_SPEED_SUPER)
> +		return le16_to_cpu(ss_ep_comp_desc->wBytesPerInterval);
> +
> +	max_packet = usb_endpoint_maxp(endpt_desc);
> +	max_burst = usb_endpoint_maxp_mult(endpt_desc);
> +
> +	/* A 0 in max burst means 1 transfer per ESIT */
> +	return max_packet * max_burst;
> +}
> +
>   /**
>    * Issue a configure endpoint command or evaluate context command
>    * and wait for it to finish.
> @@ -324,6 +490,10 @@ static int xhci_set_configuration(struct usb_device *udev)
>   	int slot_id = udev->slot_id;
>   	struct xhci_virt_device *virt_dev = ctrl->devs[slot_id];
>   	struct usb_interface *ifdesc;
> +	u32 max_esit_payload;
> +	unsigned int interval;
> +	unsigned int mult;
> +	unsigned int avg_trb_len;
>   
>   	out_ctx = virt_dev->out_ctx;
>   	in_ctx = virt_dev->in_ctx;
> @@ -357,10 +527,26 @@ static int xhci_set_configuration(struct usb_device *udev)
>   	/* filling up ep contexts */
>   	for (cur_ep = 0; cur_ep < num_of_ep; cur_ep++) {
>   		struct usb_endpoint_descriptor *endpt_desc = NULL;
> +		struct usb_ss_ep_comp_descriptor *ss_ep_comp_desc = NULL;
>   
>   		endpt_desc = &ifdesc->ep_desc[cur_ep];
> +		ss_ep_comp_desc = &ifdesc->ss_ep_comp_desc[cur_ep];
>   		trb_64 = 0;
>   
> +		/*
> +		 * Get values to fill the endpoint context, mostly from ep
> +		 * descriptor. The average TRB buffer lengt for bulk endpoints
> +		 * is unclear as we have no clue on scatter gather list entry
> +		 * size. For Isoc and Int, set it to max available.
> +		 * See xHCI 1.1 spec 4.14.1.1 for details.
> +		 */
> +		max_esit_payload = xhci_get_max_esit_payload(udev, endpt_desc,
> +							     ss_ep_comp_desc);
> +		interval = xhci_get_endpoint_interval(udev, endpt_desc);
> +		mult = xhci_get_endpoint_mult(udev, endpt_desc,
> +					      ss_ep_comp_desc);
> +		avg_trb_len = max_esit_payload;
> +
>   		ep_index = xhci_get_ep_index(endpt_desc);
>   		ep_ctx[ep_index] = xhci_get_ep_ctx(ctrl, in_ctx, ep_index);
>   
> @@ -372,6 +558,11 @@ static int xhci_set_configuration(struct usb_device *udev)
>   		/*NOTE: ep_desc[0] actually represents EP1 and so on */
>   		dir = (((endpt_desc->bEndpointAddress) & (0x80)) >> 7);
>   		ep_type = (((endpt_desc->bmAttributes) & (0x3)) | (dir << 2));
> +
> +		ep_ctx[ep_index]->ep_info =
> +			cpu_to_le32(EP_MAX_ESIT_PAYLOAD_HI(max_esit_payload) |
> +			EP_INTERVAL(interval) | EP_MULT(mult));
> +
>   		ep_ctx[ep_index]->ep_info2 =
>   			cpu_to_le32(ep_type << EP_TYPE_SHIFT);
>   		ep_ctx[ep_index]->ep_info2 |=
> @@ -386,6 +577,10 @@ static int xhci_set_configuration(struct usb_device *udev)
>   				virt_dev->eps[ep_index].ring->enqueue;
>   		ep_ctx[ep_index]->deq = cpu_to_le64(trb_64 |
>   				virt_dev->eps[ep_index].ring->cycle_state);
> +
> +		ep_ctx[ep_index]->tx_info =
> +			cpu_to_le32(EP_MAX_ESIT_PAYLOAD_LO(max_esit_payload) |
> +			EP_AVG_TRB_LENGTH(avg_trb_len));
>   	}
>   
>   	return xhci_configure_endpoints(udev, false);
> diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
> index a497d9d..6deefbf 100644
> --- a/drivers/usb/host/xhci.h
> +++ b/drivers/usb/host/xhci.h
> @@ -663,8 +663,9 @@ struct xhci_ep_ctx {
>   #define GET_MAX_PACKET(p)	((p) & 0x7ff)
>   
>   /* tx_info bitmasks */
> -#define AVG_TRB_LENGTH_FOR_EP(p)	((p) & 0xffff)
> -#define MAX_ESIT_PAYLOAD_FOR_EP(p)	(((p) & 0xffff) << 16)
> +#define EP_AVG_TRB_LENGTH(p)		((p) & 0xffff)
> +#define EP_MAX_ESIT_PAYLOAD_LO(p)	(((p) & 0xffff) << 16)
> +#define EP_MAX_ESIT_PAYLOAD_HI(p)	((((p) >> 16) & 0xff) << 24)
>   #define CTX_TO_MAX_ESIT_PAYLOAD(p)	(((p) >> 16) & 0xffff)
>   
>   /* deq bitmasks */
> diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h
> index 0ad4782..264c971 100644
> --- a/include/linux/usb/ch9.h
> +++ b/include/linux/usb/ch9.h
> @@ -418,6 +418,12 @@ struct __packed usb_class_report_descriptor {
>   #define USB_ENDPOINT_XFER_INT		3
>   #define USB_ENDPOINT_MAX_ADJUSTABLE	0x80
>   
> +#define USB_ENDPOINT_MAXP_MASK		0x07ff
> +#define USB_EP_MAXP_MULT_SHIFT		11
> +#define USB_EP_MAXP_MULT_MASK		(3 << USB_EP_MAXP_MULT_SHIFT)
> +#define USB_EP_MAXP_MULT(m)		\
> +	(((m) & USB_EP_MAXP_MULT_MASK) >> USB_EP_MAXP_MULT_SHIFT)
> +
>   /* The USB 3.0 spec redefines bits 5:4 of bmAttributes as interrupt ep type. */
>   #define USB_ENDPOINT_INTRTYPE		0x30
>   #define USB_ENDPOINT_INTR_PERIODIC	(0 << 4)
> @@ -625,6 +631,20 @@ static inline int usb_endpoint_maxp(const struct usb_endpoint_descriptor *epd)
>   	return __le16_to_cpu(get_unaligned(&epd->wMaxPacketSize));
>   }
>   
> +/**
> + * usb_endpoint_maxp_mult - get endpoint's transactional opportunities
> + * @epd: endpoint to be checked
> + *
> + * Return @epd's wMaxPacketSize[12:11] + 1
> + */
> +static inline int
> +usb_endpoint_maxp_mult(const struct usb_endpoint_descriptor *epd)
> +{
> +	int maxp = __le16_to_cpu(epd->wMaxPacketSize);
> +
> +	return USB_EP_MAXP_MULT(maxp) + 1;
> +}
> +
>   static inline int usb_endpoint_interrupt_type(
>   		const struct usb_endpoint_descriptor *epd)
>   {
> 

Reviewed-by: Stefan Roese <sr at denx.de>
Tested-by: Stefan Roese <sr at denx.de>

Thanks,
Stefan


More information about the U-Boot mailing list