[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