arch/xtensa/espressif: initialize the HR Timer from the Wi-Fi init path

The Espressif Wi-Fi stack cannot work unless the esp_timer subsystem has
been initialized, but nothing in the Wi-Fi code does that: it is left to
each board's bringup to call esp_hr_timer_init() first.  Any board that
does not happen to make that call dies on the first RF enable.

The dependency is not visible from the Wi-Fi sources.  The path is:

  board_wlan_init() -> esp_wlan_sta_initialize() -> esp_wlan_initialize()
    -> esp_wifi_initialize() -> esp_wifi_api_adapter_init()

and later, when the radio is first powered up:

  esp_phy_enable_wrapper() -> esp_phy_enable() (esp-hal-3rdparty,
  components/esp_phy/src/phy_init.c) -> phy_track_pll_init()
  (components/esp_phy/src/phy_common.c)

phy_track_pll_init() calls esp_timer_create() and
esp_timer_start_periodic() wrapped in ESP_ERROR_CHECK().  Both return
ESP_ERR_INVALID_STATE while esp_timer is uninitialized, because the HAL's
own esp_timer_init_os() startup hook is compiled out on NuttX
(#ifndef __NuttX__ in components/esp_timer/src/esp_timer.c), so the timer
task and the timer ISR only ever get created from NuttX's
esp_hr_timer_init() -> esp_timer_init().

Initialize the HR Timer at the top of esp_wifi_api_adapter_init(), where
the requirement actually originates.  esp_hr_timer_init() is idempotent
(it early-returns once the subsystem is up), so boards that already call
it during bringup are unaffected.  Also make ESPRESSIF_WIRELESS select
ESPRESSIF_HR_TIMER explicitly instead of inheriting it through the
deprecated ESP32{,S2,S3}_RT_TIMER symbols, so the timer adapter is
guaranteed to be built whenever the radio is.

This is deliberately limited to Xtensa.  The RISC-V common-espressif tree
has the same unenforced dependency, but nothing is broken there today: its
ESPRESSIF_WIRELESS already selects both ESPRESSIF_HR_TIMER and RTC_DRIVER,
and esp_rtc.c initializes the timer.  The mirror change can follow from
someone able to test it on RISC-V hardware.

This was diagnosed on an out-of-tree ESP32-S3 board whose bringup lacked
the call.  The failure gives no panic output at all and looks exactly like
a CPU lockup: the system tick stops, the console dies mid-line and USB
stays enumerated but unresponsive.  It was tracked down with ROM-level
ets_printf() breadcrumbs along the init path plus a high-priority thread
that busy-waits on ets_delay_us(): the breadcrumb trail ends inside
phy_track_pll_init() and never reaches the print after it, and the
busy-wait thread keeps printing while every sleep()-based thread stops
waking, showing the tick is gone.  Initializing the timer ahead of Wi-Fi
init makes the same image associate to an AP, obtain a DHCP lease and
serve telnet.  Validated on ESP32-S3 silicon (240 MHz, no PSRAM, 16 MiB
flash).

esp32s3-devkit:wifi builds clean with the change.

Signed-off-by: Ricard Rosson <ricard@groundbits.com>
Assisted-by: Claude Opus 5 (Claude Code)
This commit is contained in:
Ricard Rosson
2026-08-11 10:43:39 -03:00
committed by Filipe do Ó Cavalcanti
parent 23ea1503a1
commit 3d30272ce7
2 changed files with 20 additions and 0 deletions
+1
View File
@@ -2045,6 +2045,7 @@ config ESPRESSIF_WIRELESS
select ESP32_RNG if ARCH_CHIP_ESP32
select ESP32_RT_TIMER if ARCH_CHIP_ESP32
select ESP32_TIMER0 if ARCH_CHIP_ESP32
select ESPRESSIF_HR_TIMER
---help---
Enable Wireless support
@@ -34,6 +34,7 @@
#include "esp_wifi.h"
#include "esp_private/wifi.h"
#include "esp_hr_timer.h"
#include "esp_wifi_utils.h"
#include "esp_wifi_api.h"
@@ -124,6 +125,24 @@ int esp_wifi_api_adapter_init(void)
int ret;
wifi_init_config_t wifi_cfg = WIFI_INIT_CONFIG_DEFAULT();
/* Make sure the HR Timer (and, with it, the underlying esp_timer
* subsystem) is running before the radio is brought up. The first RF
* enable reaches phy_track_pll_init(), which calls esp_timer_create() and
* esp_timer_start_periodic() inside an ESP_ERROR_CHECK(). Those calls
* only work once esp_timer_init() has created the timer task and
* installed the timer ISR, and that only happens from
* esp_hr_timer_init(). Doing it here keeps the Wi-Fi stack independent
* of whether a particular board's bringup code happens to have
* initialized the HR Timer. The call is idempotent.
*/
ret = esp_hr_timer_init();
if (ret < 0)
{
wlerr("Failed to initialize HR Timer error=%d\n", ret);
return ret;
}
esp_wifi_lock(true);
esp_evt_work_init();