Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 26 additions & 16 deletions esp-radio/src/common_adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,10 @@ pub(crate) fn enable_wifi_power_domain() {
.dig_pwc()
.modify(|_, w| w.wifi_force_pd().clear_bit());

// Give the domain time to power up before touching it, mirroring
// ESP-IDF's `esp_wifi_bt_power_domain_on`.
esp_rom_sys::rom::ets_delay_us(10);

#[cfg(not(esp32))]
cfg_select! {
soc_has_apb_ctrl => {
Expand Down Expand Up @@ -405,26 +409,32 @@ pub(crate) fn enable_wifi_power_domain() {
});
}
// ESP32-C2 has no separate modem power domain (RTC_CNTL lacks
// wifi_force_pd/iso), but a system reset still leaves the shared modem
// subsystems in their previous state.
esp32c2 => {
regs!(APB_CTRL).wifi_rst_en().modify(|_, w| {
w.wifibb_rst().set_bit();
w.fe_rst().set_bit();
w.mac_rst().set_bit();
w.ble_rpa_rst().set_bit()
});
regs!(APB_CTRL).wifi_rst_en().modify(|_, w| {
w.wifibb_rst().clear_bit();
w.fe_rst().clear_bit();
w.mac_rst().clear_bit();
w.ble_rpa_rst().clear_bit()
});
}
// wifi_force_pd/iso) — nothing to power up here. ESP-IDF's
// `esp_wifi_bt_power_domain_on` is a no-op on this chip for the same
// reason; in particular it does not reset the shared modem, whose
// state the Wi-Fi driver retains across a deinit/init cycle.
_ => {}
}
}

/// Power down the Wi-Fi power domain, mirroring `enable_wifi_power_domain` and
/// ESP-IDF's `esp_wifi_bt_power_domain_off`.
pub(crate) fn disable_wifi_power_domain() {
#[cfg(not(any(soc_has_pmu, esp32c2)))]
{
let rtc_cntl = regs!(RTC_CNTL);

// Isolate before powering down.
rtc_cntl
.dig_iso()
.modify(|_, w| w.wifi_force_iso().set_bit());

rtc_cntl
.dig_pwc()
.modify(|_, w| w.wifi_force_pd().set_bit());
}
}

/// **************************************************************************
/// Name: esp_queue_create
///
Expand Down
7 changes: 6 additions & 1 deletion esp-radio/src/ieee802154/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ pub struct Ieee802154<'a> {
transmit_buffer: [u8; FRAME_SIZE],
_phy_clock_guard: PhyClockGuard<'a>,
_phy_init_guard: PhyInitGuard<'a>,
// Fields drop in declaration order: this guard must stay last so the PHY
// is torn down (which still needs the modem clocks) before the clocks are
// gated off.
_radio_clock_guard: RadioClockGuard,
}

impl<'a> Ieee802154<'a> {
Expand All @@ -123,12 +127,13 @@ impl<'a> Ieee802154<'a> {
/// things will break.
#[instability::unstable]
pub fn new(radio: IEEE802154<'a>) -> Self {
let (_phy_clock_guard, _phy_init_guard) = esp_ieee802154_enable(radio);
let (_phy_clock_guard, _phy_init_guard, _radio_clock_guard) = esp_ieee802154_enable(radio);
Self {
_align: 0,
transmit_buffer: [0u8; FRAME_SIZE],
_phy_clock_guard,
_phy_init_guard,
_radio_clock_guard,
}
}

Expand Down
22 changes: 19 additions & 3 deletions esp-radio/src/ieee802154/raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use super::{
pib::*,
};
use crate::{
radio_clocks::{clocks_ll::enable_ieee802154, init_radio_clocks},
radio_clocks::{clocks_ll::enable_ieee802154, deinit_radio_clocks, init_radio_clocks},
sys::include::{
ieee802154_coex_event_t,
ieee802154_coex_event_t_IEEE802154_IDLE,
Expand Down Expand Up @@ -104,9 +104,25 @@ pub struct RawReceived {
pub channel: u8,
}

/// Gates off the 802.15.4 modem clocks and undoes the radio clock
/// initialization (`deinit_radio_clocks`) when dropped.
///
/// Must be dropped only after the PHY guards: PHY teardown still requires the
/// modem clocks.
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(crate) struct RadioClockGuard;

impl Drop for RadioClockGuard {
fn drop(&mut self) {
enable_ieee802154(false);
deinit_radio_clocks();
}
}

pub(crate) fn esp_ieee802154_enable(
radio: IEEE802154<'_>,
) -> (PhyClockGuard<'_>, PhyInitGuard<'_>) {
) -> (PhyClockGuard<'_>, PhyInitGuard<'_>, RadioClockGuard) {
init_radio_clocks();
let phy_clock_guard = esp_phy::enable_phy_clock();
enable_ieee802154(true);
Expand All @@ -117,7 +133,7 @@ pub(crate) fn esp_ieee802154_enable(
ieee802154_mac_init(radio);

info!("date={:x}", mac_date());
(phy_clock_guard, phy_init_guard)
(phy_clock_guard, phy_init_guard, RadioClockGuard)
}

fn esp_btbb_enable() {
Expand Down
28 changes: 27 additions & 1 deletion esp-radio/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,10 +323,15 @@ pub(crate) fn init() {
);
}

// Ungate the modem clocks first: `enable_wifi_power_domain` pulses the
// modem reset, which is ineffective while the clocks are gated — and
// esp-phy's clock guard has gated them again by the time we re-init.
// (ESP-IDF never gates these clocks, so its power-up reset always lands.)
radio_clocks::init_radio_clocks();

crate::common_adapter::enable_wifi_power_domain();

wifi_set_log_verbose();
radio_clocks::init_radio_clocks();

#[cfg(feature = "coex")]
match crate::wifi::coex_initialize() {
Expand All @@ -350,6 +355,27 @@ pub(crate) fn deinit() {
#[cfg(feature = "ble")]
ble::shutdown_ble_isr();

// Gate the BT clocks (the Wi-Fi driver gates its own clocks during
// `wifi_deinit`), power down the modem power domain, and gate the
// remaining modem clocks, mirroring ESP-IDF's fixed-mask clock control
// (`periph_ll_wifi_module_disable_clk_set_rst` and friends). This must
// only run once all radios are off: PHY teardown still needs the modem
// clocks.
#[cfg(feature = "ble")]
crate::radio_clocks::clocks_ll::enable_bt(false);
crate::common_adapter::disable_wifi_power_domain();
crate::radio_clocks::deinit_radio_clocks();

// After the modem power domain has been powered down, the PHY driver's
// internal init flag must be reset, otherwise the next `phy_wakeup_init`
// assumes retained PHY registers that the power-down wiped (mirrors
// ESP-IDF's `esp_phy_modem_deinit`, "Fix the issue caused by the power
// domain off. This issue is only on ESP32C3.").
#[cfg(esp32c3)]
unsafe {
crate::sys::include::phy_init_flag()
};

esp_hal::if_unstable_hal! {
// Allow using `ADC2` again
#[cfg(esp32)]
Expand Down
7 changes: 7 additions & 0 deletions esp-radio/src/radio_clocks/clocks_ll/esp32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,13 @@ pub(crate) fn init_clocks() {
.write(|w| unsafe { w.bits(u32::MAX) });
}

pub(crate) fn deinit_clocks() {
// Nothing to do: the Wi-Fi clock bits (`DPORT_WIFI_CLK_WIFI_EN_M`) are
// cleared by `enable_wifi(false)` when the Wi-Fi driver deinitializes,
// and `disable_wifi_power_domain` powers the modem power domain off.
// ESP-IDF has no global clock-register restore on deinit either.
}

pub(crate) fn ble_rtc_clk_init() {
// nothing for this target
}
Expand Down
9 changes: 9 additions & 0 deletions esp-radio/src/radio_clocks/clocks_ll/esp32c2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@ pub(crate) fn init_clocks() {
.modify(|r, w| unsafe { w.bits(r.bits() & !WIFI_BT_SDIO_CLK | SYSTEM_WIFI_CLK_EN) });
}

pub(crate) fn deinit_clocks() {
// Nothing to do: when the last `PhyClockGuard` drops, esp-phy gates the
// shared modem clocks (`SYSTEM_WIFI_CLK_WIFI_BT_COMMON_M`) — the same
// state ESP-IDF leaves behind via `wifi_bt_common_module_disable`, and
// its `periph_ll_wifi_module_disable_clk_set_rst` is a no-op on ESP32-C2.
// Gating anything beyond that here breaks Wi-Fi re-initialization on
// ESP32-C2.
}

pub(crate) fn ble_rtc_clk_init() {
regs!(MODEM_CLKRST).modem_lp_timer_conf().modify(|_, w| {
w.lp_timer_sel_xtal32k().clear_bit();
Expand Down
18 changes: 18 additions & 0 deletions esp-radio/src/radio_clocks/clocks_ll/esp32c3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,24 @@ pub(crate) fn init_clocks() {
.modify(|r, w| unsafe { w.bits(r.bits() & !WIFI_BT_SDIO_CLK | SYSTEM_WIFI_CLK_EN) });
}

pub(crate) fn deinit_clocks() {
// No `wifi_clk_en` restore: ESP-IDF's Wi-Fi disable mask
// (`SYSTEM_WIFI_CLK_WIFI_EN_M`) is 0 on this chip — its
// `periph_ll_wifi_module_disable_clk_set_rst` clears nothing.

// Power the BT domain back down, re-asserting the state cleared by
// `init_clocks` and mirroring ESP-IDF's `esp_bt_power_domain_off` (the
// Wi-Fi domain is powered down by `disable_wifi_power_domain`, which
// `deinit` calls first). Isolate before powering down.
regs!(RTC_CNTL)
.dig_iso()
.modify(|_, w| w.bt_force_iso().set_bit());

regs!(RTC_CNTL)
.dig_pwc()
.modify(|_, w| w.bt_force_pd().set_bit());
}

pub(crate) fn ble_rtc_clk_init() {
// nothing for this target
}
Expand Down
4 changes: 4 additions & 0 deletions esp-radio/src/radio_clocks/clocks_ll/esp32c5.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ pub(crate) fn init_clocks() {
// done in esp-hal
}

pub(crate) fn deinit_clocks() {
// nothing to do, `init_clocks` is a no-op
}

pub(crate) fn ble_rtc_clk_init() {
// nothing for this target (yet)
}
Expand Down
6 changes: 5 additions & 1 deletion esp-radio/src/radio_clocks/clocks_ll/esp32c6.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ pub(crate) fn enable_ieee802154(en: bool) {

regs!(MODEM_LPCON)
.clk_conf()
.modify(|_, w| w.clk_coex_en().set_bit());
.modify(|_, w| w.clk_coex_en().bit(en));
}

pub(crate) fn enable_bt(en: bool) {
Expand Down Expand Up @@ -85,6 +85,10 @@ pub(crate) fn init_clocks() {
// done in esp-hal
}

pub(crate) fn deinit_clocks() {
// nothing to do, `init_clocks` is a no-op
}

pub(crate) fn ble_rtc_clk_init() {
// nothing for this target (yet)
}
Expand Down
4 changes: 4 additions & 0 deletions esp-radio/src/radio_clocks/clocks_ll/esp32c61.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ pub(crate) fn init_clocks() {
// done in esp-hal
}

pub(crate) fn deinit_clocks() {
// nothing to do, `init_clocks` is a no-op
}

pub(crate) fn ble_rtc_clk_init() {
// nothing for this target (yet)
}
Expand Down
39 changes: 29 additions & 10 deletions esp-radio/src/radio_clocks/clocks_ll/esp32h2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,17 @@ pub(crate) fn enable_ieee802154(en: bool) {
}

pub(crate) fn init_clocks() {
regs!(PMU)
.hp_sleep_icg_modem()
let pmu = regs!(PMU);

pmu.hp_sleep_icg_modem()
.modify(|_, w| unsafe { w.hp_sleep_dig_icg_modem_code().bits(0) });
regs!(PMU)
.hp_modem_icg_modem()
pmu.hp_modem_icg_modem()
.modify(|_, w| unsafe { w.hp_modem_dig_icg_modem_code().bits(1) });
regs!(PMU)
.hp_active_icg_modem()
pmu.hp_active_icg_modem()
.modify(|_, w| unsafe { w.hp_active_dig_icg_modem_code().bits(2) });
regs!(PMU)
.imm_modem_icg()
pmu.imm_modem_icg()
.write(|w| w.update_dig_icg_modem_en().set_bit());
regs!(PMU)
.imm_sleep_sysclk()
pmu.imm_sleep_sysclk()
.write(|w| w.update_dig_icg_switch().set_bit());

regs!(MODEM_LPCON).clk_conf().modify(|_, w| {
Expand All @@ -61,6 +58,28 @@ pub(crate) fn init_clocks() {
});
}

pub(crate) fn deinit_clocks() {
let pmu = regs!(PMU);

// Restore ESP-IDF's `pmu_init` defaults for the modem clock-gating codes
// (`PMU_HP_*_CLOCK_CONFIG_DEFAULT` in `pmu_param.c`): IDF configures them
// once at startup and does not touch them on radio deinit.
pmu.hp_sleep_icg_modem()
.modify(|_, w| unsafe { w.hp_sleep_dig_icg_modem_code().bits(2) });
pmu.hp_modem_icg_modem()
.modify(|_, w| unsafe { w.hp_modem_dig_icg_modem_code().bits(0) });
pmu.hp_active_icg_modem()
.modify(|_, w| unsafe { w.hp_active_dig_icg_modem_code().bits(0) });
pmu.imm_modem_icg()
.write(|w| w.update_dig_icg_modem_en().set_bit());

regs!(MODEM_LPCON).clk_conf().modify(|_, w| {
w.clk_i2c_mst_en().clear_bit();
w.clk_coex_en().clear_bit();
w.clk_fe_mem_en().clear_bit()
});
}

pub(crate) fn ble_rtc_clk_init() {
// nothing for this target (yet)
}
Expand Down
9 changes: 9 additions & 0 deletions esp-radio/src/radio_clocks/clocks_ll/esp32s2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,12 @@ pub(crate) fn init_clocks() {
.wifi_clk_en()
.modify(|r, w| unsafe { w.bits(r.bits() & !WIFI_BT_SDIO_CLK | DPORT_WIFI_CLK_WIFI_EN) });
}

pub(crate) fn deinit_clocks() {
// Nothing to do: `enable_wifi(false)` (called by the Wi-Fi driver on
// deinit) clears `DPORT_WIFI_CLK_WIFI_EN_M`. That mask (0x7cf) is smaller
// than the value `init_clocks` writes (0x3807cf), so the upper bits stay
// set — ESP-IDF's `periph_ll_wifi_module_disable_clk_set_rst` leaves them
// set as well, and they only feed the modem, which
// `disable_wifi_power_domain` powers off.
}
7 changes: 7 additions & 0 deletions esp-radio/src/radio_clocks/clocks_ll/esp32s3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ pub(crate) fn init_clocks() {
.modify(|r, w| unsafe { w.bits(r.bits() & !WIFI_BT_SDIO_CLK | SYSTEM_WIFI_CLK_EN) });
}

pub(crate) fn deinit_clocks() {
// Nothing to do: ESP-IDF's Wi-Fi disable mask
// (`SYSTEM_WIFI_CLK_WIFI_EN_M`) is 0 on this chip — its
// `periph_ll_wifi_module_disable_clk_set_rst` clears nothing — and
// `disable_wifi_power_domain` powers the modem power domain off.
}

pub(crate) fn ble_rtc_clk_init() {
// nothing for this target
}
Expand Down
7 changes: 7 additions & 0 deletions esp-radio/src/radio_clocks/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,10 @@ pub(crate) mod clocks_ll;
pub(crate) fn init_radio_clocks() {
clocks_ll::init_clocks();
}

/// Undo the clock initialization done by [`init_radio_clocks`], gating the
/// modem clocks again (mirroring ESP-IDF's per-module clock disable).
#[inline]
pub(crate) fn deinit_radio_clocks() {
clocks_ll::deinit_clocks();
}
1 change: 1 addition & 0 deletions qa-test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ esp-radio = { path = "../esp-radio", features = [
"unstable",
], optional = true }
esp-storage = { path = "../esp-storage", optional = true }
ieee802154 = "0.6.1"
lis3dh-async = "0.9.3"
ssd1306 = "0.10.0"
static_cell = "2.1.1"
Expand Down
Loading
Loading