Skip to content

Commit

Permalink
usb: cdns3: fix iso transfer error when mult is not zero
Browse files Browse the repository at this point in the history
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:  <[email protected]>
Fixes: 7733f6c ("usb: cdns3: Add Cadence USB3 DRD Driver")
Signed-off-by: Frank Li <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Greg Kroah-Hartman <[email protected]>
  • Loading branch information
nxpfrankli authored and gregkh committed Jan 4, 2024
1 parent 1b8be5e commit 92f02ef
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 23 deletions.
59 changes: 36 additions & 23 deletions drivers/usb/cdns3/cdns3-gadget.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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) {
Expand All @@ -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;
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down
3 changes: 3 additions & 0 deletions drivers/usb/cdns3/cdns3-gadget.h
Original file line number Diff line number Diff line change
Expand Up @@ -1168,6 +1168,9 @@ struct cdns3_endpoint {
u8 dir;
u8 num;
u8 type;
u8 mult;
u8 bMaxBurst;
u16 wMaxPacketSize;
int interval;

int free_trbs;
Expand Down

0 comments on commit 92f02ef

Please sign in to comment.