Skip to content

Commit 1301c7b

Browse files
pawellcdnsgregkh
authored andcommitted
usb: cdns3: remove fetched trb from cache before dequeuing
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 b1f02c6 commit 1301c7b

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
@@ -2614,6 +2614,7 @@ int cdns3_gadget_ep_dequeue(struct usb_ep *ep,
26142614
u8 req_on_hw_ring = 0;
26152615
unsigned long flags;
26162616
int ret = 0;
2617+
int val;
26172618

26182619
if (!ep || !request || !ep->desc)
26192620
return -EINVAL;
@@ -2649,6 +2650,13 @@ int cdns3_gadget_ep_dequeue(struct usb_ep *ep,
26492650

26502651
/* Update ring only if removed request is on pending_req_list list */
26512652
if (req_on_hw_ring && link_trb) {
2653+
/* Stop DMA */
2654+
writel(EP_CMD_DFLUSH, &priv_dev->regs->ep_cmd);
2655+
2656+
/* wait for DFLUSH cleared */
2657+
readl_poll_timeout_atomic(&priv_dev->regs->ep_cmd, val,
2658+
!(val & EP_CMD_DFLUSH), 1, 1000);
2659+
26522660
link_trb->buffer = cpu_to_le32(TRB_BUFFER(priv_ep->trb_pool_dma +
26532661
((priv_req->end_trb + 1) * TRB_SIZE)));
26542662
link_trb->control = cpu_to_le32((le32_to_cpu(link_trb->control) & TRB_CYCLE) |
@@ -2660,6 +2668,10 @@ int cdns3_gadget_ep_dequeue(struct usb_ep *ep,
26602668

26612669
cdns3_gadget_giveback(priv_ep, priv_req, -ECONNRESET);
26622670

2671+
req = cdns3_next_request(&priv_ep->pending_req_list);
2672+
if (req)
2673+
cdns3_rearm_transfer(priv_ep, 1);
2674+
26632675
not_found:
26642676
spin_unlock_irqrestore(&priv_dev->lock, flags);
26652677
return ret;

0 commit comments

Comments
 (0)