From a6a5c8aff2c32d78f7b7b21363a84eea309d963f Mon Sep 17 00:00:00 2001 From: dechao_gong Date: Thu, 30 Jul 2026 18:09:28 +0800 Subject: [PATCH] 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 Assisted-by: Claude --- 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); } }