ricardgb opened a new pull request, #19786:
URL: https://github.com/apache/nuttx/pull/19786
## Summary
The Espressif Wi-Fi stack cannot work unless the `esp_timer` subsystem has
been
initialized, but nothing in the Wi-Fi code does that — the initialization is
left to each board's bringup, which has to call `esp_hr_timer_init()` before
`board_wlan_init()`. Every in-tree ESP32/ESP32-S2/ESP32-S3 board with a Wi-Fi
defconfig happens to make that call, so in-tree configurations work today;
any
board that does not (a new port, or a board file written by following another
subsystem's example) dies on the first RF enable, with a failure mode that
gives
no diagnostic whatsoever.
This moves the initialization into the common Wi-Fi init path, where the
requirement actually originates, so the Wi-Fi stack no longer depends on
board
code getting an undocumented ordering right.
## The dependency
The requirement is invisible from the Wi-Fi sources. Bring-up runs:
```
board_wlan_init() -> esp_wlan_sta_initialize() -> esp_wlan_initialize()
-> esp_wifi_initialize() -> esp_wifi_api_adapter_init()
```
and then, the first time the radio is powered up:
```
esp_phy_enable_wrapper()
(arch/{xtensa,risc-v}/src/<chip>/..._wifi_adapter.c)
-> esp_phy_enable() (esp-hal-3rdparty,
components/esp_phy/src/phy_init.c)
-> phy_track_pll_init() (esp-hal-3rdparty,
components/esp_phy/src/phy_common.c)
-> ESP_ERROR_CHECK(esp_timer_create(...))
-> ESP_ERROR_CHECK(esp_timer_start_periodic(...))
```
`esp_timer_create()`/`esp_timer_start_periodic()` return
`ESP_ERR_INVALID_STATE` while `esp_timer` is uninitialized. The HAL would
normally initialize itself from its `esp_timer_init_os()` startup hook, but
that
hook is compiled out on NuttX (`#ifndef __NuttX__` in
`components/esp_timer/src/esp_timer.c`), so the timer task and the timer ISR
are
only ever created from NuttX's `esp_hr_timer_init()` -> `esp_timer_init()`.
`phy_track_pll_init()` is reached on every target except the original ESP32
(`#if !CONFIG_IDF_TARGET_ESP32 && !CONFIG_ESP_PHY_DISABLE_PLL_TRACK`), and
PLL
tracking is enabled in the NuttX `sdkconfig.h` for esp32s2/s3/c3/c6/h2.
## The change
* `esp_wifi_api_adapter_init()` (`arch/xtensa/src/common/espressif/`) now
calls `esp_hr_timer_init()` before
`esp_wifi_init()`, with a comment recording the `phy_track_pll_init()` ->
`esp_timer` chain, since that constraint is not obvious from the call site.
`esp_hr_timer_init()` is idempotent (it early-returns once the subsystem is
up), so the boards that already initialize it during bringup are
unaffected —
the second call returns `OK` immediately.
* `ESPRESSIF_WIRELESS` now `select`s `ESPRESSIF_HR_TIMER` explicitly.
Today it inherits it only indirectly, through the deprecated
`ESP32_RT_TIMER` / `ESP32S2_RT_TIMER` / `ESP32S3_RT_TIMER` symbols whose
sole
remaining job is to select `ESPRESSIF_HR_TIMER`. Since the timer adapter
(`esp_timer_adapter.c`) is only compiled when `ESPRESSIF_HR_TIMER=y`, the
dependency should be stated where the radio is enabled rather than routed
through compatibility symbols. The RISC-V `ESPRESSIF_WIRELESS` already has
this `select`.
No behavioral change for any in-tree defconfig: `ESPRESSIF_HR_TIMER` is
already
`y` in every wireless configuration, and every in-tree Wi-Fi board already
calls
`esp_hr_timer_init()` from bringup, so on those the added call is a no-op
early return.
## How it was diagnosed
Found while bringing up an out-of-tree ESP32-S3 board whose bringup did not
have
the call. The failure has 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 — which is why it went unexplained for a month.
It was tracked down with ROM-level `ets_printf()` breadcrumbs along the whole
init path, plus a high-priority thread that busy-waits on `ets_delay_us()`
instead of sleeping:
* the breadcrumb trail ends inside `phy_track_pll_init()` and never reaches
the
print immediately after it;
* the busy-wait thread keeps printing while every `sleep()`-based thread
stops
waking — i.e. the tick is gone, the CPU is not.
Initializing the timer ahead of Wi-Fi init turns the same image into a
working
one: the board associates to an AP, obtains a DHCP lease and serves telnet.
## Testing
* Silicon: validated on ESP32-S3 (240 MHz, no PSRAM, 16 MiB flash) — Wi-Fi
associates, DHCP lease obtained, telnet session served. Without the
initialization the same image locks up as described above.
* Build: `esp32s3-devkit:wifi` (xtensa-esp32s3-elf) builds clean, no new
warnings, links and images successfully.
* `tools/checkpatch.sh -g HEAD`: all checks pass.
* Scope is deliberately Xtensa-only. 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. An earlier draft of
this
patch mirrored the change there too; it was dropped rather than posted
untested, since there is no RISC-V toolchain on this machine. Happy to add
it
if maintainers would prefer the common-source trees kept symmetric.
## Disclosure
This root-cause analysis, patch, and hardware validation were performed by
an AI
agent (Claude Code, operated and directed by the submitter), and the result
was
reviewed by the submitter before posting.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]