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.