On 2026-08-06 18:47, Anshul Dalal wrote:
> On Thu, 16 Jul 2026 15:41:30 +0200, Anders Roxell <[email protected]> 
> wrote:
> > [...]
> > This is compile-tested only.
> > 
> > The series needs the dwc2 endpoint fixes from Mattijs [3]. They are
> > already on the list and should go in first.
> > 
> > The branch is also available at [4].
> 
> Hi Andres,

Hi Anshul,

Thanks for testing on real hardware.

> 
> I was unable to get DFU boot to work on TI devices with this patch series. I
> tested on AM62p EVM where I don't see any error logs on the console but the
> DFU device never shows up on the host side (verified with dfu-util and lsusb).
> 
> I'm not that familiar with dwc3 but the issue seems to be that dwc3_interrupt
> doesn't report any interrupts.


The event path is broken in three places. The main one: in the kernel
dwc3_check_event_buf() copies the event ring into evt->cache and
dwc3_process_event_buf() reads from evt->cache. We took the reader but not
the copy, so it walks a buffer nothing fills. Before the resync we read
evt->buf directly, so it worked. I think this is what you are hitting, but
you have the board.

We also never write the count back to GEVNTCOUNT, and dwc3_thread_interrupt()
gets the controller where it wants the event buffer.

Fix below, it follows the kernel dwc3_invalidate_cache() is ours, it was in
io.h before the resync and got lost.

--- a/drivers/usb/dwc3/io.h
+++ b/drivers/usb/dwc3/io.h
@@ -48,4 +48,12 @@ static inline void dwc3_flush_cache(uintptr_t addr, int 
length)
        flush_dcache_range((unsigned long)start_addr, (unsigned long)end_addr);
 }

+static inline void dwc3_invalidate_cache(uintptr_t addr, int length)
+{
+       uintptr_t start_addr = (uintptr_t)addr & ~(CACHELINE_SIZE - 1);
+       uintptr_t end_addr = ALIGN((uintptr_t)addr + length, CACHELINE_SIZE);
+
+       invalidate_dcache_range((unsigned long)start_addr, (unsigned 
long)end_addr);
+}
+
 #endif /* __DRIVERS_USB_DWC3_IO_H */

--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -4201,23 +4201,46 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3 
*dwc)
 {
        struct dwc3_event_buffer *evt;
+       u32 amount;
        u32 count;
-       u32 reg;

        evt = dwc->ev_buf;

+       if (evt->flags & DWC3_EVENT_PENDING)
+               return IRQ_HANDLED;
+
        count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0));
        count &= DWC3_GEVNTCOUNT_MASK;
        if (!count)
                return IRQ_NONE;

+       if (count > evt->length) {
+               dev_err(dwc->dev, "invalid count(%u) > evt->length(%u)\n",
+                       count, evt->length);
+               return IRQ_NONE;
+       }
+
        evt->count = count;
        evt->flags |= DWC3_EVENT_PENDING;

        /* Mask interrupt */
-       reg = dwc3_readl(dwc->regs, DWC3_GEVNTSIZ(0));
-       reg |= DWC3_GEVNTSIZ_INTMASK;
-       dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), reg);
+       dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0),
+                   DWC3_GEVNTSIZ_INTMASK | DWC3_GEVNTSIZ_SIZE(evt->length));
+
+       /* The controller filled the buffer over DMA, drop our stale lines */
+       dwc3_invalidate_cache((uintptr_t)evt->buf, evt->length);
+
+       amount = min(count, evt->length - evt->lpos);
+       memcpy(evt->cache + evt->lpos, evt->buf + evt->lpos, amount);
+
+       if (amount < count)
+               memcpy(evt->cache, evt->buf, count - amount);
+
+       dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), count);

        return IRQ_WAKE_THREAD;
 }

And in dwc3_gadget_uboot_handle_interrupt(), same file:

-               struct dwc3_event_buffer *evt;
-               dwc3_thread_interrupt(0, dwc);
+               struct dwc3_event_buffer *evt = dwc->ev_buf;
+               dwc3_thread_interrupt(0, evt);

evt was used there without being set as well.

All of this is in v6 that I plan to send out. I have no AM62p and it is
only compile tested, so please try it on your board so we know if it
solves your issue before I send out v6.

Cheers,
Anders

Reply via email to