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

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

commit a6a5c8aff2c32d78f7b7b21363a84eea309d963f
Author: dechao_gong <[email protected]>
AuthorDate: Thu Jul 30 18:09:28 2026 +0800

    arch/arm/common/ameba: fix level-triggered GPIO interrupt storm
    
    The shared port ISR clears interrupt status with GPIO_INTStatusClearEdge(),
    which only clears edge latches.  For a level-triggered pin the status stays
    asserted as long as the level is active, so the ISR re-enters forever once
    the level fires -- the system hangs before the application can even service
    the event.
    
    Remember at configure time whether a pin is level-triggered, and in the ISR
    mask such a pin (GPIO_INTConfig DISABLE) before invoking the callback.  The
    application re-enables it via go_enable() after servicing.  Edge pins are
    left unmasked, so their behaviour is unchanged.
    
    This fixes a hang reproducible on RTL8721Dx and RTL8721F when a
    level-high/level-low interrupt pin is used.
    
    Signed-off-by: dechao_gong <[email protected]>
    Assisted-by: Claude <[email protected]>
---
 arch/arm/src/common/ameba/ameba_gpio.c | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/arch/arm/src/common/ameba/ameba_gpio.c 
b/arch/arm/src/common/ameba/ameba_gpio.c
index 21e06ea5a1f..c5842738760 100644
--- a/arch/arm/src/common/ameba/ameba_gpio.c
+++ b/arch/arm/src/common/ameba/ameba_gpio.c
@@ -133,6 +133,7 @@ struct ameba_gpio_dev_s
 {
   struct gpio_dev_s gpio;      /* Lower-half GPIO device (must be first) */
   uint8_t pin;                 /* SDK pin encoding (port[7:5] | num[4:0]) */
+  bool leveltrig;              /* Level-triggered interrupt (not edge) */
   pin_interrupt_t callback;    /* Interrupt callback (interrupt pins only) */
 };
 
@@ -307,6 +308,16 @@ static void ameba_gpio_configure(struct ameba_gpio_dev_s 
*priv,
         break;
     }
 
+  /* Remember whether this is a level-triggered interrupt.  The port ISR
+   * masks such pins after dispatch: GPIO_INTStatusClearEdge() clears only
+   * edge latches, so a still-active level would otherwise re-enter the ISR
+   * forever (an interrupt storm).  Edge pins are left unmasked so a single
+   * attach/enable keeps receiving edges as before.
+   */
+
+  priv->leveltrig = (init.mode == AMEBA_GPIO_MODE_INT &&
+                     init.ittrigger == AMEBA_GPIO_IT_LEVEL);
+
   GPIO_Init(&init);
 
   /* GPIO_Init() leaves the interrupt source enabled, and setting up the
@@ -365,6 +376,18 @@ static int ameba_gpio_interrupt(int irq, void *context, 
void *arg)
 
       if (priv != NULL && priv->callback != NULL)
         {
+          /* A level-triggered pin keeps its interrupt status asserted for
+           * as long as the level is active; GPIO_INTStatusClearEdge() above
+           * cannot clear it, so mask the pin now to stop it re-entering the
+           * ISR forever.  The application re-enables it (go_enable) once it
+           * has serviced the event.  Edge pins are left unmasked.
+           */
+
+          if (priv->leveltrig)
+            {
+              GPIO_INTConfig(priv->pin, AMEBA_DISABLE);
+            }
+
           priv->callback(&priv->gpio, num);
         }
     }

Reply via email to