usb: cdns3: fix iso transfer error when mult is not zero
authorFrank Li <Frank.Li@nxp.com>
Sun, 24 Dec 2023 15:38:14 +0000 (10:38 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 4 Jan 2024 15:01:44 +0000 (16:01 +0100)
ISO basic transfer is
ITP(SOF) Package_0 Package_1 ... Package_n

CDNS3 DMA start dma transfer from memmory to internal FIFO when get SOF,
controller will transfer data to usb bus from internal FIFO when get IN
token.

According USB spec defination:
Maximum number of packets = (bMaxBurst + 1) * (Mult + 1)

Internal memory should be the same as (bMaxBurst + 1) * (Mult + 1). DMA
don't fetch data advance when ISO transfer, so only reserve
(bMaxBurst + 1) * (Mult + 1) internal memory for ISO transfer.

Need save Mult and bMaxBurst information and set it into EP_CFG register,
otherwise only 1 package is sent by controller, other package will be
lost.

Cc: <stable@vger.kernel.org>
Fixes: 7733f6c32e36 ("usb: cdns3: Add Cadence USB3 DRD Driver")
Signed-off-by: Frank Li <Frank.Li@nxp.com>
Link: https://lore.kernel.org/r/20231224153816.1664687-3-Frank.Li@nxp.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/cdns3/cdns3-gadget.c
drivers/usb/cdns3/cdns3-gadget.h

index 22a31ffa69423ad7f13e51d92974a3b579d538dd..4c6893af22dde5bc2b373abe1895b0b0a14f1d7d 100644 (file)
@@ -2065,11 +2065,10 @@ int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable)
        bool is_iso_ep = (priv_ep->type == USB_ENDPOINT_XFER_ISOC);
        struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
        u32 bEndpointAddress = priv_ep->num | priv_ep->dir;
-       u32 max_packet_size = 0;
-       u8 maxburst = 0;
+       u32 max_packet_size = priv_ep->wMaxPacketSize;
+       u8 maxburst = priv_ep->bMaxBurst;
        u32 ep_cfg = 0;
        u8 buffering;
-       u8 mult = 0;
        int ret;
 
        buffering = priv_dev->ep_buf_size - 1;
@@ -2091,8 +2090,7 @@ int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable)
                break;
        default:
                ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_ISOC);
-               mult = priv_dev->ep_iso_burst - 1;
-               buffering = mult + 1;
+               buffering = (priv_ep->bMaxBurst + 1) * (priv_ep->mult + 1) - 1;
        }
 
        switch (priv_dev->gadget.speed) {
@@ -2103,17 +2101,8 @@ int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable)
                max_packet_size = is_iso_ep ? 1024 : 512;
                break;
        case USB_SPEED_SUPER:
-               /* It's limitation that driver assumes in driver. */
-               mult = 0;
-               max_packet_size = 1024;
-               if (priv_ep->type == USB_ENDPOINT_XFER_ISOC) {
-                       maxburst = priv_dev->ep_iso_burst - 1;
-                       buffering = (mult + 1) *
-                                   (maxburst + 1);
-
-                       if (priv_ep->interval > 1)
-                               buffering++;
-               } else {
+               if (priv_ep->type != USB_ENDPOINT_XFER_ISOC) {
+                       max_packet_size = 1024;
                        maxburst = priv_dev->ep_buf_size - 1;
                }
                break;
@@ -2142,7 +2131,6 @@ int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable)
        if (priv_dev->dev_ver < DEV_VER_V2)
                priv_ep->trb_burst_size = 16;
 
-       mult = min_t(u8, mult, EP_CFG_MULT_MAX);
        buffering = min_t(u8, buffering, EP_CFG_BUFFERING_MAX);
        maxburst = min_t(u8, maxburst, EP_CFG_MAXBURST_MAX);
 
@@ -2176,7 +2164,7 @@ int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable)
        }
 
        ep_cfg |= EP_CFG_MAXPKTSIZE(max_packet_size) |
-                 EP_CFG_MULT(mult) |
+                 EP_CFG_MULT(priv_ep->mult) |                  /* must match EP setting */
                  EP_CFG_BUFFERING(buffering) |
                  EP_CFG_MAXBURST(maxburst);
 
@@ -2266,6 +2254,13 @@ usb_ep *cdns3_gadget_match_ep(struct usb_gadget *gadget,
        priv_ep->type = usb_endpoint_type(desc);
        priv_ep->flags |= EP_CLAIMED;
        priv_ep->interval = desc->bInterval ? BIT(desc->bInterval - 1) : 0;
+       priv_ep->wMaxPacketSize =  usb_endpoint_maxp(desc);
+       priv_ep->mult = USB_EP_MAXP_MULT(priv_ep->wMaxPacketSize);
+       priv_ep->wMaxPacketSize &= USB_ENDPOINT_MAXP_MASK;
+       if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && comp_desc) {
+               priv_ep->mult =  USB_SS_MULT(comp_desc->bmAttributes) - 1;
+               priv_ep->bMaxBurst = comp_desc->bMaxBurst;
+       }
 
        spin_unlock_irqrestore(&priv_dev->lock, flags);
        return &priv_ep->endpoint;
@@ -3049,22 +3044,40 @@ static int cdns3_gadget_check_config(struct usb_gadget *gadget)
        struct cdns3_endpoint *priv_ep;
        struct usb_ep *ep;
        int n_in = 0;
+       int iso = 0;
+       int out = 1;
        int total;
+       int n;
 
        list_for_each_entry(ep, &gadget->ep_list, ep_list) {
                priv_ep = ep_to_cdns3_ep(ep);
-               if ((priv_ep->flags & EP_CLAIMED) && (ep->address & USB_DIR_IN))
-                       n_in++;
+               if (!(priv_ep->flags & EP_CLAIMED))
+                       continue;
+
+               n = (priv_ep->mult + 1) * (priv_ep->bMaxBurst + 1);
+               if (ep->address & USB_DIR_IN) {
+                       /*
+                        * ISO transfer: DMA start move data when get ISO, only transfer
+                        * data as min(TD size, iso). No benefit for allocate bigger
+                        * internal memory than 'iso'.
+                        */
+                       if (priv_ep->type == USB_ENDPOINT_XFER_ISOC)
+                               iso += n;
+                       else
+                               n_in++;
+               } else {
+                       if (priv_ep->type == USB_ENDPOINT_XFER_ISOC)
+                               out = max_t(int, out, n);
+               }
        }
 
        /* 2KB are reserved for EP0, 1KB for out*/
-       total = 2 + n_in + 1;
+       total = 2 + n_in + out + iso;
 
        if (total > priv_dev->onchip_buffers)
                return -ENOMEM;
 
-       priv_dev->ep_buf_size = priv_dev->ep_iso_burst =
-                       (priv_dev->onchip_buffers - 2) / (n_in + 1);
+       priv_dev->ep_buf_size = (priv_dev->onchip_buffers - 2 - iso) / (n_in + out);
 
        return 0;
 }
index fbe4a8e3aa89777dafc8f1832ce2c731efe1cf17..086a7bb8389752a1a70be922982fe11c864a3fb8 100644 (file)
@@ -1168,6 +1168,9 @@ struct cdns3_endpoint {
        u8                      dir;
        u8                      num;
        u8                      type;
+       u8                      mult;
+       u8                      bMaxBurst;
+       u16                     wMaxPacketSize;
        int                     interval;
 
        int                     free_trbs;