This is an automated email from the ASF dual-hosted git repository.

xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit de440f910a21f3acf288902400697364553f2216
Author: Justin Hammond <[email protected]>
AuthorDate: Sun Aug 16 17:10:44 2026 +0800

    drivers/usbhost: Silence the xHCI interrupter until its worker has run.
    
    The handler read the status, queued the work that would answer it, and
    returned with the source still asserted.  On a level triggered line the
    interrupt controller sees the condition still true and raises it again at
    once, so the work that would have cleared it never runs.
    
    Mask the interrupter in the handler and let the worker unmask when it is
    done.  The unmask clears the pending flag in the same write, because a
    message is sent on that flag's clear to set transition and events that
    arrived while the interrupter was masked have already set it.
    
    Clearing opens its own window, so the worker drains the ring again after
    unmasking and repeats while a drain finds anything; xhci_events_poll()
    returns how many events it handled for that purpose.  A drain that finds
    nothing is the only state in which no event can have been lost.
    
    Assisted-by: Claude:claude-opus-5
    Signed-off-by: Justin Hammond <[email protected]>
---
 drivers/usbhost/usbhost_xhci.c | 39 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 38 insertions(+), 1 deletion(-)

diff --git a/drivers/usbhost/usbhost_xhci.c b/drivers/usbhost/usbhost_xhci.c
index 423090e840d..ed417a92a52 100644
--- a/drivers/usbhost/usbhost_xhci.c
+++ b/drivers/usbhost/usbhost_xhci.c
@@ -2804,6 +2804,7 @@ static int xhci_events_poll(FAR struct usbhost_xhci_s 
*priv)
   uintptr_t              addr;
   uint8_t                type;
   uint32_t               d2;
+  int                    count = 0;
 
   /* Invalidate event ring */
 
@@ -2875,6 +2876,7 @@ static int xhci_events_poll(FAR struct usbhost_xhci_s 
*priv)
 
       /* Next event */
 
+      count++;
       priv->evnt.i++;
 
       /* Handle ring wrap */
@@ -2891,7 +2893,7 @@ static int xhci_events_poll(FAR struct usbhost_xhci_s 
*priv)
   addr |= XHCI_ERDP_EHB;
   xhci_runt_putreg_8b(priv, XHCI_ERDP(0), addr);
 
-  return OK;
+  return count;
 }
 
 /****************************************************************************
@@ -2956,6 +2958,29 @@ static void xhci_interrupt_work(FAR void *arg)
   /* Clear pending bits */
 
   priv->pending = 0;
+
+  /* Let interrupts back in, which the handler masked on its way out, and
+   * clear the pending flag in the same write.
+   *
+   * A message signalled interrupt is sent on the flag's clear to set
+   * transition; a wire stays asserted while it is set.  Events that
+   * arrived while this interrupter was masked have already set the flag,
+   * so enabling without clearing leaves a message with nothing to
+   * transition on, and transfers have no timeout.
+   *
+   * Clearing opens its own window: an event delivered between the ring
+   * going empty and this write is discarded.  So drain again, and repeat
+   * if that drain found anything.  A drain that finds nothing is the only
+   * state in which no event can have been lost.
+   */
+
+  do
+    {
+      iman = xhci_runt_getreg(priv, XHCI_IMAN(0));
+      xhci_runt_putreg(priv, XHCI_IMAN(0),
+                       iman | XHCI_IMAN_IE | XHCI_IMAN_IP);
+    }
+  while (xhci_events_poll(priv) > 0);
 }
 
 /****************************************************************************
@@ -2969,11 +2994,23 @@ static void xhci_interrupt_work(FAR void *arg)
 static int xhci_interrupt(int irq, FAR void *context, FAR void *arg)
 {
   FAR struct usbhost_xhci_s *priv = arg;
+  uint32_t                   iman;
 
   /* Get pending interrupts */
 
   priv->pending = xhci_oper_getreg(priv, XHCI_USBSTS);
 
+  /* Silence the interrupter before returning.
+   *
+   * Nothing here clears the condition that raised the interrupt; the work
+   * runs later on a work queue.  On a level triggered line the source is
+   * still asserted on return, so the interrupt re-raises immediately and
+   * the worker never runs.  The worker clears the status and unmasks.
+   */
+
+  iman = xhci_runt_getreg(priv, XHCI_IMAN(0));
+  xhci_runt_putreg(priv, XHCI_IMAN(0), iman & ~XHCI_IMAN_IE);
+
   /* Handle interrupts in worker */
 
   if (work_available(&priv->work))

Reply via email to