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

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


The following commit(s) were added to refs/heads/master by this push:
     new 3c3afd04bdc espressif/esp_pm.c: restore GPIO config after using it as 
a wake source
3c3afd04bdc is described below

commit 3c3afd04bdcb044acafc45c20f2c71863daafb44
Author: Felipe Moura <[email protected]>
AuthorDate: Sat Sep 19 17:56:15 2026 -0300

    espressif/esp_pm.c: restore GPIO config after using it as a wake source
    
    Arming a pin as a light-sleep wake source destroyed whatever it was
    configured as, permanently.
    
    esp_pm_gpio_wakeup_prepare() has to reconfigure each masked pin to plain
    INPUT and hand it to gpio_wakeup_enable(), because the wakeup path only
    supports level triggering.  It then never put anything back.  A pin that
    was also a normal peripheral interrupt -- a sensor's data-ready line, say
    -- came out of the first light sleep with its trigger mode gone and never
    interrupted again.  Nothing failed loudly; the device just went silent.
    
    Fixed generically rather than per-board:
    
      - esp_configgpio() now remembers the last attr applied to each pin, and
        a new esp_getconfiggpio() hands it back.  This is what lets the PM
        code restore a pin without having to know what the pin is for.
    
      - esp_pm_gpio_wakeup_prepare() saves each masked pin's attr before
        overwriting it, and a new esp_pm_gpio_wakeup_restore() puts it back
        as soon as esp_pm_light_sleep_start() returns.
    
    Tied to the physical sleep/wake cycle deliberately, not to PM state
    transitions.  An earlier attempt used a board-level pm_register()/notify()
    callback and never fired at all, because the board sits in PM_STANDBY
    without transitioning back to PM_NORMAL -- there is no state change to
    hang the restore on.  The return from esp_pm_light_sleep_start() is the
    one event that always happens exactly once per sleep.
    
    Assisted-by: Claude:claude-opus-5
    Signed-off-by: Felipe Moura <[email protected]>
---
 arch/xtensa/src/common/espressif/esp_gpio.c | 32 +++++++++++++++++++++++
 arch/xtensa/src/common/espressif/esp_gpio.h | 19 ++++++++++++++
 arch/xtensa/src/common/espressif/esp_pm.c   | 39 +++++++++++++++++++++++++++++
 3 files changed, 90 insertions(+)

diff --git a/arch/xtensa/src/common/espressif/esp_gpio.c 
b/arch/xtensa/src/common/espressif/esp_gpio.c
index d2593ca0cdd..2c08597e819 100644
--- a/arch/xtensa/src/common/espressif/esp_gpio.c
+++ b/arch/xtensa/src/common/espressif/esp_gpio.c
@@ -72,6 +72,13 @@ static gpio_hal_context_t g_gpio_hal =
   .dev = GPIO_HAL_GET_HW(GPIO_PORT_0)
 };
 
+/* Mirrors the attr last applied to each pin via esp_configgpio(), so a
+ * caller that has to temporarily reconfigure a pin (e.g. PM wake-source
+ * setup) can read it back and restore it afterward.
+ */
+
+static gpio_pinattr_t g_gpio_lastattr[SOC_GPIO_PIN_COUNT];
+
 /****************************************************************************
  * Private Functions
  ****************************************************************************/
@@ -138,6 +145,8 @@ int esp_configgpio(int pin, gpio_pinattr_t attr)
 {
   DEBUGASSERT(pin >= 0 && pin < SOC_GPIO_PIN_COUNT);
 
+  g_gpio_lastattr[pin] = attr;
+
   /* Handle input pins */
 
   if ((attr & INPUT) != 0)
@@ -191,6 +200,7 @@ int esp_configgpio(int pin, gpio_pinattr_t attr)
   if ((attr & DRIVE_MASK) != 0)
     {
       uint32_t val = ((attr & DRIVE_MASK) >> DRIVE_SHIFT) - 1;
+
       gpio_hal_set_drive_capability(&g_gpio_hal, pin, val);
     }
   else
@@ -202,6 +212,7 @@ int esp_configgpio(int pin, gpio_pinattr_t attr)
   if ((attr & FUNCTION_MASK) != 0)
     {
       uint32_t val = ((attr & FUNCTION_MASK) >> FUNCTION_SHIFT) - 1;
+
       gpio_hal_func_sel(&g_gpio_hal, pin, val);
     }
   else
@@ -212,6 +223,27 @@ int esp_configgpio(int pin, gpio_pinattr_t attr)
   return OK;
 }
 
+/****************************************************************************
+ * Name: esp_getconfiggpio
+ *
+ * Description:
+ *   Return the attr last passed to esp_configgpio() for this pin.
+ *
+ * Input Parameters:
+ *   pin - GPIO pin to query.
+ *
+ * Returned Value:
+ *   The attr last passed to esp_configgpio() for this pin.
+ *
+ ****************************************************************************/
+
+gpio_pinattr_t esp_getconfiggpio(int pin)
+{
+  DEBUGASSERT(pin >= 0 && pin < SOC_GPIO_PIN_COUNT);
+
+  return g_gpio_lastattr[pin];
+}
+
 /****************************************************************************
  * Name: esp_gpio_matrix_in
  *
diff --git a/arch/xtensa/src/common/espressif/esp_gpio.h 
b/arch/xtensa/src/common/espressif/esp_gpio.h
index a046eb72583..52b3eb21783 100644
--- a/arch/xtensa/src/common/espressif/esp_gpio.h
+++ b/arch/xtensa/src/common/espressif/esp_gpio.h
@@ -157,6 +157,25 @@ extern "C"
 
 int esp_configgpio(int pin, gpio_pinattr_t attr);
 
+/****************************************************************************
+ * Name: esp_getconfiggpio
+ *
+ * Description:
+ *   Return the attr last passed to esp_configgpio() for this pin, so a
+ *   caller that has to temporarily reconfigure a pin (e.g. PM wake-source
+ *   setup) can restore it afterward without needing its own knowledge of
+ *   what the pin is normally used for.
+ *
+ * Input Parameters:
+ *   pin - GPIO pin to query.
+ *
+ * Returned Value:
+ *   The attr last passed to esp_configgpio() for this pin.
+ *
+ ****************************************************************************/
+
+gpio_pinattr_t esp_getconfiggpio(int pin);
+
 /****************************************************************************
  * Name: esp_gpio_matrix_in
  *
diff --git a/arch/xtensa/src/common/espressif/esp_pm.c 
b/arch/xtensa/src/common/espressif/esp_pm.c
index 7001e23c71a..f35ee2dfae9 100644
--- a/arch/xtensa/src/common/espressif/esp_pm.c
+++ b/arch/xtensa/src/common/espressif/esp_pm.c
@@ -446,6 +446,8 @@ static uint64_t IRAM_ATTR esp_pm_get_gpio_mask(void)
  *
  ****************************************************************************/
 
+static gpio_pinattr_t g_gpio_wakeup_saved[CONFIG_SOC_GPIO_PIN_COUNT];
+
 static void IRAM_ATTR esp_pm_gpio_wakeup_prepare(void)
 {
   uint64_t mask_value = esp_pm_get_gpio_mask();
@@ -461,6 +463,14 @@ static void IRAM_ATTR esp_pm_gpio_wakeup_prepare(void)
       pin_mask = BIT(i);
       if ((mask_value & pin_mask) != 0)
         {
+          /* This pin may also be a normal edge-triggered peripheral IRQ
+           * (e.g. a sensor's data-ready line) -- gpio_wakeup_enable()
+           * below only supports level triggering, so remember whatever
+           * esp_configgpio() had last set here and put it back in
+           * esp_pm_gpio_wakeup_restore() once this sleep is over.
+           */
+
+          g_gpio_wakeup_saved[i] = esp_getconfiggpio(i);
           esp_configgpio(i, INPUT);
           gpio_wakeup_enable(i, level_mode);
         }
@@ -468,6 +478,31 @@ static void IRAM_ATTR esp_pm_gpio_wakeup_prepare(void)
 
   esp_sleep_enable_gpio_wakeup();
 }
+
+/****************************************************************************
+ * Name: esp_pm_gpio_wakeup_restore
+ *
+ * Description:
+ *   Undo esp_pm_gpio_wakeup_prepare(): restore each wake-source gpio to
+ *   whatever esp_configgpio() had it set to before this sleep.
+ *
+ ****************************************************************************/
+
+static void IRAM_ATTR esp_pm_gpio_wakeup_restore(void)
+{
+  uint64_t mask_value = esp_pm_get_gpio_mask();
+  int pin_mask = 0;
+
+  for (int i = 0; i < CONFIG_SOC_GPIO_PIN_COUNT; i++)
+    {
+      pin_mask = BIT(i);
+      if ((mask_value & pin_mask) != 0)
+        {
+          gpio_wakeup_disable(i);
+          esp_configgpio(i, g_gpio_wakeup_saved[i]);
+        }
+    }
+}
 #endif /* CONFIG_PM_GPIO_WAKEUP */
 
 #ifdef CONFIG_PM_UART_WAKEUP
@@ -777,6 +812,10 @@ void esp_pmstandby(uint64_t time_in_us)
 
   esp_pm_light_sleep_start(&rtc_diff_us);
 
+#ifdef CONFIG_PM_GPIO_WAKEUP
+  esp_pm_gpio_wakeup_restore();
+#endif
+
   /* Only step the clock where the systimer actually stalls during sleep
    * (SOC_SLEEP_SYSTIMER_STALL_WORKAROUND); elsewhere it keeps counting
    * through light sleep, so stepping it here would double-count the time.

Reply via email to