Fix issues about watchdog timers on ESP32, ESP32-S2 and ESP32-S3#18968
Merged
Conversation
With the BOARD_LATE_INITIALIZE flow, board_late_initialize() runs on a kernel thread and sets the MWDT0 prescaler. However, after it returns, the init task (NSH) is spawned and lib_cxx_initialize() invokes constructor functions, including enable_timer_group0_for_calibration() from esp-hal-3rdparty which calls timg_ll_reset_register(0), resetting all Timer Group 0 registers (including the WDT prescaler) back to their defaults. Re-apply the prescaler, timeout, and feed the WDT counter in wdt_lh_start() just before enabling the timer. This ensures correct WDT configuration regardless of any intermediate register resets by external code. Signed-off-by: Tiago Medicci Serrano <tiago.medicci@espressif.com>
Same issue as ESP32-S3: with BOARD_LATE_INITIALIZE, the constructor function enable_timer_group0_for_calibration() resets Timer Group 0 registers after board_late_initialize() has configured the MWDT0 prescaler, causing the watchdog to fire at an incorrect rate. Re-apply the prescaler, timeout, and feed the WDT counter in wdt_lh_start() just before enabling the timer. Signed-off-by: Tiago Medicci Serrano <tiago.medicci@espressif.com>
Same issue as ESP32-S3/S2: with BOARD_LATE_INITIALIZE, the constructor function enable_timer_group0_for_calibration() resets Timer Group 0 registers after board_late_initialize() has configured the MWDT0 prescaler, causing the watchdog to fire at an incorrect rate. Re-apply the prescaler, timeout, and feed the WDT counter in esp32_wdt_start() just before enabling the timer. Signed-off-by: Tiago Medicci Serrano <tiago.medicci@espressif.com>
Under the BOARD_LATE_INITIALIZE boot flow, rtc_clk_xtal_freq_get() may return 0 (SOC_XTAL_FREQ_AUTO) because the XTAL frequency has not yet been stored to the RTC register when the WDT driver is initialized. This causes rtc_clk_cal() to divide by zero internally (EXCCAUSE=0006) when computing the RTC slow clock period. Fix by explicitly calling rtc_clk_xtal_freq_update() with the board's configured crystal frequency if rtc_clk_xtal_freq_get() returns 0. Also add a guard in esp32_wdt_settimeout() to return -EIO if ESP32_RWDT_CLK() still returns 0 cycles/ms, preventing a subsequent divide-by-zero when computing the maximum timeout. Signed-off-by: Tiago Medicci Serrano <tiago.medicci@espressif.com>
The RWDT register offsets were incorrectly set to ESP32-S3 values instead of ESP32 values. This was introduced when the code was refactored from using the local NuttX header hardware/esp32_rtccntl.h (which had the correct offsets) to using the HAL library headers, and the offsets were moved inline into esp32_wdt.c with wrong values. Correct the offsets to match the actual ESP32 register layout from soc/rtc_cntl_reg.h (RTC_CNTL_WDTCONFIG0_REG at 0x8c, INT_ENA at 0x3c, etc). Without this fix, all RWDT operations (enable, configure timeout, enable interrupt, acknowledge interrupt, feed, write-protect) were targeting wrong memory addresses, rendering the RWDT completely non-functional. Signed-off-by: Tiago Medicci Serrano <tiago.medicci@espressif.com>
fdcavalcanti
approved these changes
May 26, 2026
xiaoxiang781216
approved these changes
May 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Our internal CI spotted some bugs related to the watchdog timers. The general functionality was working as expected, but minor issues have appeared:
xtensa/esp32: Fix RWDT register offsets.
The RWDT register offsets were incorrectly set to ESP32-S3 values instead of ESP32 values. This was introduced when the code was
refactored from using the local NuttX header hardware/esp32_rtccntl.h which had the correct offsets) to using the HAL library headers, and
the offsets were moved inline into esp32_wdt.c with wrong values. Correct the offsets to match the actual ESP32 register layout from
soc/rtc_cntl_reg.h (RTC_CNTL_WDTCONFIG0_REG at 0x8c, INT_ENA at 0x3c, etc). Without this fix, all RWDT operations (enable, configure
timeout, enable interrupt, acknowledge interrupt, feed, write-protect) were targeting wrong memory addresses, rendering the RWDT completely
non-functional.
xtensa/esp32: Fix divide-by-zero in RTC WDT clock calibration.
not yet been stored to the RTC register when the WDT driver is initialized. This causes rtc_clk_cal() to divide by zero internally (EXCCAUSE=0006) when computing the RTC slow clock period. Fix by explicitly calling rtc_clk_xtal_freq_update() with the board's configured crystal frequency if rtc_clk_xtal_freq_get() returns 0. Also add a guard in esp32_wdt_settimeout() to return -EIO if ESP32_RWDT_CLK() still returns 0 cycles/ms, preventing a subsequent divide-by-zero when computing the maximum timeout.
xtensa/esp32: Re-apply WDT prescaler and timeout at start.
xtensa/esp32s2: Re-apply WDT prescaler and timeout at start.
xtensa/esp32s3: Re-apply WDT prescaler and timeout at start.
returns, the init task (NSH) is spawned and lib_cxx_initialize() invokes constructor functions, including enable_timer_group0_for_calibration() from esp-hal-3rdparty which calls timg_ll_reset_register(0), resetting all Timer Group 0 registers (including the WDT prescaler) back to their defaults. Re-apply the prescaler, timeout, and feed the WDT counter in wdt_lh_start() just before enabling the timer. This ensures correct WDT configuration regardless of any intermediate register resets by external code.
Impact
Impact on user: Yes. It prevented setting a watchdog handler on watchdog timer 0. Now fixed.
Impact on build: No.
Impact on hardware: Yes. Xtensa-based Espressif's SoCs. Now fixed.
Impact on documentation: No.
Impact on security: No.
Impact on compatibility: No.
Testing
Just build the
watchdogdefconfig for such devices. For ESP32-S3, for instance:Building
Running
Run
wdog -i /dev/watchdog0to ensure watchdog is triggering properly.Results