Skip to content

Commit f073d10

Browse files
pawellcdnsgregkh
authored andcommitted
usb: cdns3: remove fetched trb from cache before dequeuing
commit 1301c7b upstream. After doorbell DMA fetches the TRB. If during dequeuing request driver changes NORMAL TRB to LINK TRB but doesn't delete it from controller cache then controller will handle cached TRB and packet can be lost. The example scenario for this issue looks like: 1. queue request - set doorbell 2. dequeue request 3. send OUT data packet from host 4. Device will accept this packet which is unexpected 5. queue new request - set doorbell 6. Device lost the expected packet. By setting DFLUSH controller clears DRDY bit and stop DMA transfer. Fixes: 7733f6c ("usb: cdns3: Add Cadence USB3 DRD Driver") cc: <[email protected]> Signed-off-by: Pawel Laszczak <[email protected]> Acked-by: Peter Chen <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 73f4bde commit f073d10

File tree

1 file changed

+12
-0
lines changed

1 file changed

+12
-0
lines changed

drivers/usb/cdns3/cdns3-gadget.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2610,6 +2610,7 @@ int cdns3_gadget_ep_dequeue(struct usb_ep *ep,
26102610
u8 req_on_hw_ring = 0;
26112611
unsigned long flags;
26122612
int ret = 0;
2613+
int val;
26132614

26142615
if (!ep || !request || !ep->desc)
26152616
return -EINVAL;
@@ -2645,6 +2646,13 @@ int cdns3_gadget_ep_dequeue(struct usb_ep *ep,
26452646

26462647
/* Update ring only if removed request is on pending_req_list list */
26472648
if (req_on_hw_ring && link_trb) {
2649+
/* Stop DMA */
2650+
writel(EP_CMD_DFLUSH, &priv_dev->regs->ep_cmd);
2651+
2652+
/* wait for DFLUSH cleared */
2653+
readl_poll_timeout_atomic(&priv_dev->regs->ep_cmd, val,
2654+
!(val & EP_CMD_DFLUSH), 1, 1000);
2655+
26482656
link_trb->buffer = cpu_to_le32(TRB_BUFFER(priv_ep->trb_pool_dma +
26492657
((priv_req->end_trb + 1) * TRB_SIZE)));
26502658
link_trb->control = cpu_to_le32((le32_to_cpu(link_trb->control) & TRB_CYCLE) |
@@ -2656,6 +2664,10 @@ int cdns3_gadget_ep_dequeue(struct usb_ep *ep,
26562664

26572665
cdns3_gadget_giveback(priv_ep, priv_req, -ECONNRESET);
26582666

2667+
req = cdns3_next_request(&priv_ep->pending_req_list);
2668+
if (req)
2669+
cdns3_rearm_transfer(priv_ep, 1);
2670+
26592671
not_found:
26602672
spin_unlock_irqrestore(&priv_dev->lock, flags);
26612673
return ret;

0 commit comments

Comments
 (0)