The dwc3 driver was synced from Linux v6.16. In Linux the DMA api keeps
the trb and the buffers in sync with the controller. In u-boot the ep0
trb and buffers come from dma_alloc_coherent, which is just cached
memory, not coherent. Nothing syncs them around the controller dma, so
we must flush and invalidate by hand.

U-boot did this before, the resync dropped it. Without cache maintenance
the controller reads a stale trb and a stale buffer, and reads back stale
status. The device answers GET_DESCRIPTOR with garbage, so the host fails
to enumerate with -71 (EPROTO).

Flush the trb and the buffer before the controller reads them. Invalidate
the setup packet, the status write back and the OUT data buffer after the
controller writes them.

Reported-by: Anshul Dalal <[email protected]>
Signed-off-by: Anders Roxell <[email protected]>
---
 drivers/usb/dwc3/ep0.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index e81404e11405..f82c0f7e6dec 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -55,6 +55,9 @@ static void dwc3_ep0_prepare_one_trb(struct dwc3_ep *dep,
        else
                trb->ctrl |= (DWC3_TRB_CTRL_IOC
                                | DWC3_TRB_CTRL_LST);
+
+       dwc3_flush_cache((uintptr_t)buf_dma, len);
+       dwc3_flush_cache((uintptr_t)trb, sizeof(*trb));
 }
 
 static int dwc3_ep0_start_trans(struct dwc3_ep *dep)
@@ -812,6 +815,8 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
        int ret = -EINVAL;
        u32 len;
 
+       dwc3_invalidate_cache((uintptr_t)ctrl, sizeof(*ctrl));
+
        if (!dwc->gadget_driver || !dwc->softconnect || !dwc->connected)
                goto out;
 
@@ -857,6 +862,8 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
        dwc->ep0_next_event = DWC3_EP0_NRDY_STATUS;
        trb = dwc->ep0_trb;
 
+       dwc3_invalidate_cache((uintptr_t)trb, sizeof(*trb) * 2);
+
        r = next_request(&ep0->pending_list);
        if (!r)
                return;
@@ -874,6 +881,9 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
 
        length = trb->size & DWC3_TRB_SIZE_MASK;
        transferred = ur->length - length;
+
+       if (ur->buf && !r->direction)
+               dwc3_invalidate_cache((uintptr_t)ur->dma, ur->length);
        ur->actual += transferred;
 
        if ((IS_ALIGNED(ur->length, ep0->endpoint.maxpacket) &&
-- 
2.53.0

Reply via email to