]> git.sur5r.net Git - u-boot/commitdiff
usb: xhci: Honor endpoint's interval
authorBin Meng <bmeng.cn@gmail.com>
Mon, 18 Sep 2017 13:40:47 +0000 (06:40 -0700)
committerMarek Vasut <marex@denx.de>
Sun, 1 Oct 2017 14:32:54 +0000 (16:32 +0200)
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@gmail.com>
drivers/usb/host/xhci.c
drivers/usb/host/xhci.h
include/linux/usb/ch9.h

index ec82fa664e100a10ae14d04077821506a95281d8..8aed4283edb02fa50523d2b6527a9b38fb8723b5 100644 (file)
@@ -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);
index 3377450fcad903480ffcff44c3f187baa6f53bf7..ba5f650144f5d94ccf7a3ab7b02398bc3125a3b9 100644 (file)
@@ -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 */
index 0ad4782a36e820731e5dd4436ba5c598c92da50d..264c9712a33a68996de6fd2bb445b278e9f68a81 100644 (file)
@@ -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)
 {