From mboxrd@z Thu Jan 1 00:00:00 1970 From: Stefan Roese Date: Fri, 22 Sep 2017 07:06:42 +0200 Subject: [U-Boot] [PATCH 11/14] usb: xhci: Honor endpoint's interval In-Reply-To: <1505742050-5697-12-git-send-email-bmeng.cn@gmail.com> References: <1505742050-5697-1-git-send-email-bmeng.cn@gmail.com> <1505742050-5697-12-git-send-email-bmeng.cn@gmail.com> Message-ID: <5eaaecae-e597-d760-1f1f-0c5d890db940@denx.de> List-Id: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: u-boot@lists.denx.de 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 > --- > > 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 Tested-by: Stefan Roese Thanks, Stefan