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