diff --git a/esp-hal/README.md b/esp-hal/README.md index 945c019edbb..ed37e79ba9b 100644 --- a/esp-hal/README.md +++ b/esp-hal/README.md @@ -111,7 +111,7 @@ For help getting started with this HAL, please refer to [The Rust on ESP Book] a | Driver | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 | ESP32-S31 | | ------------- |:-----:|:--------:|:--------:|:--------:|:--------:|:---------:|:--------:|:--------:|:--------:|:--------:|:---------:| -| Bluetooth | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | | | ⚒️ | ❌ | +| Bluetooth | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | | | ⚒️ | ⚒️ | | WIFI | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | | | ⚒️ | ⚒️ | ⚒️ | | IEEE 802.15.4 | | | | ⚒️ | ⚒️ | | ⚒️ | | | | ⚒️ | | Ethernet | ⚒️ | | | | | | | ⚒️ | | | ⚒️ | diff --git a/esp-hal/src/soc/esp32/clocks.rs b/esp-hal/src/soc/esp32/clocks.rs index cd19537698c..ed2ec1cf47b 100644 --- a/esp-hal/src/soc/esp32/clocks.rs +++ b/esp-hal/src/soc/esp32/clocks.rs @@ -59,6 +59,8 @@ impl CpuClock { syscon_pre_div: None, cpu_clk: Some(CpuClkConfig::Pll), rtc_slow_clk: Some(xtal32k::default_rtc_slow_clk()), + // The divisor follows the detected crystal. `configure` fills it in. + ble_lp_clk: None, rtc_fast_clk: Some(RtcFastClkConfig::Rc), timg_calibration_clock: None, }; @@ -70,6 +72,8 @@ impl CpuClock { syscon_pre_div: None, cpu_clk: Some(CpuClkConfig::Pll), rtc_slow_clk: Some(xtal32k::default_rtc_slow_clk()), + // The divisor follows the detected crystal. `configure` fills it in. + ble_lp_clk: None, rtc_fast_clk: Some(RtcFastClkConfig::Rc), timg_calibration_clock: None, }; @@ -81,6 +85,8 @@ impl CpuClock { syscon_pre_div: None, cpu_clk: Some(CpuClkConfig::Pll), rtc_slow_clk: Some(xtal32k::default_rtc_slow_clk()), + // The divisor follows the detected crystal. `configure` fills it in. + ble_lp_clk: None, rtc_fast_clk: Some(RtcFastClkConfig::Rc), timg_calibration_clock: None, }; @@ -129,6 +135,15 @@ impl ClockConfig { self.xtal_clk = Some(xtal); } + if self.ble_lp_clk.is_none() { + // The controller runs the crystal at 500 kHz. + let xtal_hz = unwrap!(self.xtal_clk).value(); + self.ble_lp_clk = Some(BleLpClkConfig::new( + BleLpClkSclk::Xtal, + xtal_hz / 500_000 - 1, + )); + } + self.apply(clocks); } } @@ -772,3 +787,13 @@ impl RmtInstance { } } } + +fn enable_ble_lp_clk_impl(_clocks: &mut ClockTree, _en: bool) {} + +fn configure_ble_lp_clk_impl( + _clocks: &mut ClockTree, + _old_config: Option, + _new_config: BleLpClkConfig, +) { + // The BTDM controller programs this mux from the clock-tree selection. +} diff --git a/esp-hal/src/soc/esp32c2/clocks.rs b/esp-hal/src/soc/esp32c2/clocks.rs index b23e925d3c8..d9fbf276fb2 100644 --- a/esp-hal/src/soc/esp32c2/clocks.rs +++ b/esp-hal/src/soc/esp32c2/clocks.rs @@ -19,7 +19,7 @@ use esp_rom_sys::rom::{ets_delay_us, ets_update_cpu_frequency_rom}; use crate::{ clock::RtcClock, - peripherals::{I2C_ANA_MST, LPWR, SYSTEM, TIMG0}, + peripherals::{I2C_ANA_MST, LPWR, MODEM_CLKRST, SYSTEM, TIMG0}, rtc_cntl::Rtc, soc::regi2c, time::Rate, @@ -52,6 +52,8 @@ impl CpuClock { cpu_clk: Some(CpuClkConfig::Pll), rc_fast_clk_div_n: Some(RcFastClkDivNConfig::new(0)), rtc_slow_clk: Some(RtcSlowClkConfig::RcSlow), + // The divisor follows the detected crystal. `configure` fills it in. + ble_lp_clk: None, rtc_fast_clk: Some(RtcFastClkConfig::Rc), low_power_clk: Some(LowPowerClkConfig::RtcSlow), timg_calibration_clock: None, @@ -63,6 +65,8 @@ impl CpuClock { cpu_clk: Some(CpuClkConfig::Pll), rc_fast_clk_div_n: Some(RcFastClkDivNConfig::new(0)), rtc_slow_clk: Some(RtcSlowClkConfig::RcSlow), + // The divisor follows the detected crystal. `configure` fills it in. + ble_lp_clk: None, rtc_fast_clk: Some(RtcFastClkConfig::Rc), low_power_clk: Some(LowPowerClkConfig::RtcSlow), timg_calibration_clock: None, @@ -111,6 +115,21 @@ impl ClockConfig { self.xtal_clk = Some(xtal); } + if self.ble_lp_clk.is_none() { + let xtal_hz = unwrap!(self.xtal_clk).value(); + // 26 MHz crystals run the timer at 40 kHz. 40 MHz crystals run it at 32 kHz. + // BLE_LP_XTAL_CLK already divides the crystal by 5. + let target_hz = if xtal_hz == 26_000_000 { + 40_000 + } else { + 32_000 + }; + self.ble_lp_clk = Some(BleLpClkConfig::new( + BleLpClkSclk::Xtal, + xtal_hz / (5 * target_hz) - 1, + )); + } + self.apply(clocks); } } @@ -663,3 +682,32 @@ impl TimgInstance { }); } } + +fn enable_ble_lp_xtal_clk_impl(_clocks: &mut ClockTree, _en: bool) { + // The divide-by-5 crystal input has no clock gate. +} + +fn enable_ble_lp_clk_impl(_clocks: &mut ClockTree, _en: bool) {} + +fn configure_ble_lp_clk_impl( + _clocks: &mut ClockTree, + _old_config: Option, + new_config: BleLpClkConfig, +) { + let sclk = new_config.sclk(); + + MODEM_CLKRST::regs() + .modem_lp_timer_conf() + .modify(|_, w| unsafe { + w.lp_timer_sel_xtal32k().bit(sclk == BleLpClkSclk::OscSlow); + w.lp_timer_sel_xtal().bit(sclk == BleLpClkSclk::Xtal); + w.lp_timer_sel_8m().clear_bit(); + w.lp_timer_sel_rtc_slow().bit(sclk == BleLpClkSclk::RcSlow); + w.lp_timer_clk_div_num().bits(new_config.divisor() as u8) + }); + + MODEM_CLKRST::regs().etm_clk_conf().modify(|_, w| { + w.etm_clk_active().set_bit(); + w.etm_clk_sel().clear_bit() + }); +} diff --git a/esp-hal/src/soc/esp32c3/clocks.rs b/esp-hal/src/soc/esp32c3/clocks.rs index 0ccbb9c314a..48cf2eaefc0 100644 --- a/esp-hal/src/soc/esp32c3/clocks.rs +++ b/esp-hal/src/soc/esp32c3/clocks.rs @@ -52,6 +52,11 @@ impl CpuClock { cpu_clk: Some(CpuClkConfig::Pll), rc_fast_clk_div_n: Some(RcFastClkDivNConfig::new(0)), rtc_slow_clk: Some(RtcSlowClkConfig::RcSlow), + // 40 MHz crystal divided to 1 MHz. + ble_lp_clk: Some(BleLpClkConfig::new( + BleLpClkSclk::Xtal, + 40_000_000 / 1_000_000 - 1, + )), rtc_fast_clk: Some(RtcFastClkConfig::Rc), low_power_clk: Some(LowPowerClkConfig::RtcSlow), timg_calibration_clock: None, @@ -64,6 +69,11 @@ impl CpuClock { cpu_clk: Some(CpuClkConfig::Pll), rc_fast_clk_div_n: Some(RcFastClkDivNConfig::new(0)), rtc_slow_clk: Some(RtcSlowClkConfig::RcSlow), + // 40 MHz crystal divided to 1 MHz. + ble_lp_clk: Some(BleLpClkConfig::new( + BleLpClkSclk::Xtal, + 40_000_000 / 1_000_000 - 1, + )), rtc_fast_clk: Some(RtcFastClkConfig::Rc), low_power_clk: Some(LowPowerClkConfig::RtcSlow), timg_calibration_clock: None, @@ -678,3 +688,13 @@ impl TimgInstance { }); } } + +fn enable_ble_lp_clk_impl(_clocks: &mut ClockTree, _en: bool) {} + +fn configure_ble_lp_clk_impl( + _clocks: &mut ClockTree, + _old_config: Option, + _new_config: BleLpClkConfig, +) { + // The BTDM controller programs this mux from the clock-tree selection. +} diff --git a/esp-hal/src/soc/esp32c5/clocks.rs b/esp-hal/src/soc/esp32c5/clocks.rs index b04b0a98825..184aa9f1830 100644 --- a/esp-hal/src/soc/esp32c5/clocks.rs +++ b/esp-hal/src/soc/esp32c5/clocks.rs @@ -16,7 +16,7 @@ // TODO: This is a temporary place for this, should probably be moved into clocks_ll. use crate::{ - peripherals::{I2C_ANA_MST, LP_CLKRST, PCR, PMU}, + peripherals::{I2C_ANA_MST, LP_CLKRST, MODEM_LPCON, PCR, PMU}, soc::{regi2c, xtal32k}, }; @@ -51,6 +51,8 @@ impl CpuClock { apb_clk: Some(ApbClkConfig::new(0)), lp_fast_clk: Some(LpFastClkConfig::RcFast), lp_slow_clk: Some(xtal32k::default_lp_slow_clk()), + // The divisor follows the crystal frequency. `configure` fills it in. + ble_lp_clk: None, crypto_clk: Some(CryptoClkConfig::PllF480m), iomux_function_clock: Some(IomuxFunctionClockConfig::PllF80m), timg_calibration_clock: None, @@ -63,6 +65,8 @@ impl CpuClock { apb_clk: Some(ApbClkConfig::new(0)), lp_fast_clk: Some(LpFastClkConfig::RcFast), lp_slow_clk: Some(xtal32k::default_lp_slow_clk()), + // The divisor follows the crystal frequency. `configure` fills it in. + ble_lp_clk: None, crypto_clk: Some(CryptoClkConfig::PllF480m), iomux_function_clock: Some(IomuxFunctionClockConfig::PllF80m), timg_calibration_clock: None, @@ -75,6 +79,8 @@ impl CpuClock { apb_clk: Some(ApbClkConfig::new(0)), lp_fast_clk: Some(LpFastClkConfig::RcFast), lp_slow_clk: Some(xtal32k::default_lp_slow_clk()), + // The divisor follows the crystal frequency. `configure` fills it in. + ble_lp_clk: None, crypto_clk: Some(CryptoClkConfig::PllF480m), iomux_function_clock: Some(IomuxFunctionClockConfig::PllF80m), timg_calibration_clock: None, @@ -116,6 +122,15 @@ impl ClockConfig { Some(XtalClkConfig::_48) }; + if self.ble_lp_clk.is_none() { + // The controller runs the crystal at 100 kHz. + let xtal_hz = unwrap!(self.xtal_clk).value(); + self.ble_lp_clk = Some(BleLpClkConfig::new( + BleLpClkSclk::Xtal, + xtal_hz / 100_000 - 1, + )); + } + self.apply(clocks); } } @@ -236,6 +251,12 @@ fn enable_xtal32k_clk_impl(_clocks: &mut ClockTree, en: bool) { // OSC_SLOW_CLK +fn enable_rc32k_clk_impl(_clocks: &mut ClockTree, en: bool) { + PMU::regs() + .hp_sleep_lp_ck_power() + .modify(|_, w| w.hp_sleep_xpd_rc32k().bit(en)); +} + fn enable_osc_slow_clk_impl(_clocks: &mut ClockTree, en: bool) { LP_CLKRST::regs() .clk_to_hp() @@ -674,3 +695,47 @@ impl TimgInstance { }); } } + +fn enable_ble_lp_clk_impl(_clocks: &mut ClockTree, en: bool) { + MODEM_LPCON::regs() + .clk_conf() + .modify(|_, w| w.clk_lp_timer_en().bit(en)); +} + +fn configure_ble_lp_clk_impl( + _clocks: &mut ClockTree, + _old_config: Option, + new_config: BleLpClkConfig, +) { + let sclk = new_config.sclk(); + let sel_32k = ble_lp_clk_32k_sel(sclk); + + MODEM_LPCON::regs() + .test_conf() + .modify(|_, w| w.clk_en().set_bit()); + + MODEM_LPCON::regs().lp_timer_conf().modify(|_, w| unsafe { + w.clk_lp_timer_sel_osc_slow() + .bit(sclk == BleLpClkSclk::RcSlow); + w.clk_lp_timer_sel_osc_fast().clear_bit(); + w.clk_lp_timer_sel_xtal().bit(sclk == BleLpClkSclk::Xtal); + w.clk_lp_timer_sel_xtal32k().bit(sel_32k.is_some()); + w.clk_lp_timer_div_num().bits(new_config.divisor() as u16) + }); + + if let Some(sel) = sel_32k { + MODEM_LPCON::regs() + .modem_32k_clk_conf() + .modify(|_, w| unsafe { w.clk_modem_32k_sel().bits(sel) }); + } +} + +fn ble_lp_clk_32k_sel(sclk: BleLpClkSclk) -> Option { + match sclk { + #[cfg(use_xtal32k)] + BleLpClkSclk::Xtal32k => Some(0), + BleLpClkSclk::Rc32k => Some(1), + BleLpClkSclk::Ext32k => Some(2), + _ => None, + } +} diff --git a/esp-hal/src/soc/esp32c6/clocks.rs b/esp-hal/src/soc/esp32c6/clocks.rs index 7f6e1878d18..734e8b02d1b 100644 --- a/esp-hal/src/soc/esp32c6/clocks.rs +++ b/esp-hal/src/soc/esp32c6/clocks.rs @@ -17,7 +17,7 @@ // TODO: This is a temporary place for this, should probably be moved into clocks_ll. use crate::{ - peripherals::{I2C_ANA_MST, LP_CLKRST, PCR, PMU, TIMG0}, + peripherals::{I2C_ANA_MST, LP_CLKRST, MODEM_LPCON, PCR, PMU, TIMG0}, soc::{regi2c, xtal32k}, }; @@ -56,6 +56,9 @@ impl CpuClock { iomux_function_clock: Some(IomuxFunctionClockConfig::PllF80m), lp_fast_clk: Some(LpFastClkConfig::RcFastClk), lp_slow_clk: Some(xtal32k::default_lp_slow_clk()), + // The BLE low-power clock depends on the chip revision, `configure` fills it in. + ble_lp_xtal_clk: None, + ble_lp_clk: None, timg_calibration_clock: None, }; const PRESET_160: ClockConfig = ClockConfig { @@ -73,6 +76,9 @@ impl CpuClock { iomux_function_clock: Some(IomuxFunctionClockConfig::PllF80m), lp_fast_clk: Some(LpFastClkConfig::RcFastClk), lp_slow_clk: Some(xtal32k::default_lp_slow_clk()), + // The BLE low-power clock depends on the chip revision, `configure` fills it in. + ble_lp_xtal_clk: None, + ble_lp_clk: None, timg_calibration_clock: None, }; } @@ -106,6 +112,17 @@ impl ClockConfig { self.xtal_clk = Some(XtalClkConfig::_40); } + if self.ble_lp_xtal_clk.is_none() { + self.ble_lp_xtal_clk = Some(BleLpXtalClkConfig::new(ble_lp_xtal_divisor())); + } + if self.ble_lp_clk.is_none() { + let source = XTAL_HZ / ble_lp_xtal_divisor() as u32; + self.ble_lp_clk = Some(BleLpClkConfig::new( + BleLpClkSclk::Xtal, + source / BLE_LP_CLK_HZ - 1, + )); + } + // On ESP32C6, MSPI source clock's default HS divider leads to 120MHz, which is unusable // before calibration. Therefore, before switching SOC_ROOT_CLK to HS, we need to set // MSPI source clock HS divider to make it run at 80MHz after the switch. @@ -232,6 +249,12 @@ fn enable_xtal32k_clk_impl(_clocks: &mut ClockTree, en: bool) { // OSC_SLOW_CLK +fn enable_rc32k_clk_impl(_clocks: &mut ClockTree, en: bool) { + PMU::regs() + .hp_sleep_lp_ck_power() + .modify(|_, w| w.hp_sleep_xpd_rc32k().bit(en)); +} + fn enable_osc_slow_clk_impl(_clocks: &mut ClockTree, _en: bool) { // TODO: // gpio_ll_input_enable(&GPIO, SOC_EXT_OSC_SLOW_GPIO_NUM); @@ -749,3 +772,76 @@ impl TimgInstance { }); } } + +/// Crystal frequency of the ESP32-C6. +const XTAL_HZ: u32 = 40_000_000; + +/// Nominal frequency of the BLE low-power clock. +const BLE_LP_CLK_HZ: u32 = 100_000; + +/// Chip revisions after 0.0 supply the BLE timer from the Wi-Fi power domain, which divides +/// the crystal by 80. +fn ble_lp_xtal_divisor() -> BleLpXtalClkDivisor { + if crate::efuse::chip_revision().combined() == 0 { + BleLpXtalClkDivisor::_1 + } else { + BleLpXtalClkDivisor::_80 + } +} + +fn enable_ble_lp_xtal_clk_impl(_clocks: &mut ClockTree, _en: bool) { + // Revisions after 0.0 take this clock from the Wi-Fi power domain, whose gate esp-hal + // already enables. +} + +fn configure_ble_lp_xtal_clk_impl( + _clocks: &mut ClockTree, + _old_config: Option, + _new_config: BleLpXtalClkConfig, +) { + // The divisor is not programmable, it follows the chip revision. +} + +fn enable_ble_lp_clk_impl(_clocks: &mut ClockTree, en: bool) { + MODEM_LPCON::regs() + .clk_conf() + .modify(|_, w| w.clk_lp_timer_en().bit(en)); +} + +fn configure_ble_lp_clk_impl( + _clocks: &mut ClockTree, + _old_config: Option, + new_config: BleLpClkConfig, +) { + let sclk = new_config.sclk(); + let sel_32k = ble_lp_clk_32k_sel(sclk); + + MODEM_LPCON::regs() + .test_conf() + .modify(|_, w| w.clk_en().set_bit()); + + MODEM_LPCON::regs().lp_timer_conf().modify(|_, w| unsafe { + w.clk_lp_timer_sel_osc_slow() + .bit(sclk == BleLpClkSclk::RcSlow); + w.clk_lp_timer_sel_osc_fast().clear_bit(); + w.clk_lp_timer_sel_xtal().bit(sclk == BleLpClkSclk::Xtal); + w.clk_lp_timer_sel_xtal32k().bit(sel_32k.is_some()); + w.clk_lp_timer_div_num().bits(new_config.divisor() as u16) + }); + + if let Some(sel) = sel_32k { + MODEM_LPCON::regs() + .modem_32k_clk_conf() + .modify(|_, w| unsafe { w.clk_modem_32k_sel().bits(sel) }); + } +} + +fn ble_lp_clk_32k_sel(sclk: BleLpClkSclk) -> Option { + match sclk { + #[cfg(use_xtal32k)] + BleLpClkSclk::Xtal32k => Some(0), + BleLpClkSclk::Rc32k => Some(1), + BleLpClkSclk::Ext32k => Some(2), + _ => None, + } +} diff --git a/esp-hal/src/soc/esp32c61/clocks.rs b/esp-hal/src/soc/esp32c61/clocks.rs index 5b550e1f9e7..73172887b06 100644 --- a/esp-hal/src/soc/esp32c61/clocks.rs +++ b/esp-hal/src/soc/esp32c61/clocks.rs @@ -14,7 +14,7 @@ #![allow(missing_docs, reason = "Experimental")] use crate::{ - peripherals::{I2C_ANA_MST, LP_CLKRST, PCR, PMU}, + peripherals::{I2C_ANA_MST, LP_CLKRST, MODEM_LPCON, PCR, PMU}, soc::{regi2c, xtal32k}, }; @@ -46,6 +46,11 @@ impl CpuClock { apb_clk: Some(ApbClkConfig::new(0)), lp_fast_clk: Some(LpFastClkConfig::RcFast), lp_slow_clk: Some(xtal32k::default_lp_slow_clk()), + // 40 MHz crystal divided to 100 kHz. + ble_lp_clk: Some(BleLpClkConfig::new( + BleLpClkSclk::Xtal, + 40_000_000 / 100_000 - 1, + )), crypto_clk: Some(CryptoClkConfig::PllF480m), timg_calibration_clock: None, }; @@ -57,6 +62,11 @@ impl CpuClock { apb_clk: Some(ApbClkConfig::new(0)), lp_fast_clk: Some(LpFastClkConfig::RcFast), lp_slow_clk: Some(xtal32k::default_lp_slow_clk()), + // 40 MHz crystal divided to 100 kHz. + ble_lp_clk: Some(BleLpClkConfig::new( + BleLpClkSclk::Xtal, + 40_000_000 / 100_000 - 1, + )), crypto_clk: Some(CryptoClkConfig::PllF480m), timg_calibration_clock: None, }; @@ -190,6 +200,12 @@ fn enable_xtal32k_clk_impl(_clocks: &mut ClockTree, en: bool) { // OSC_SLOW_CLK +fn enable_rc32k_clk_impl(_clocks: &mut ClockTree, en: bool) { + PMU::regs() + .hp_sleep_lp_ck_power() + .modify(|_, w| w.hp_sleep_xpd_rc32k().bit(en)); +} + fn enable_osc_slow_clk_impl(_clocks: &mut ClockTree, en: bool) { LP_CLKRST::regs() .clk_to_hp() @@ -509,3 +525,47 @@ impl TimgInstance { }); } } + +fn enable_ble_lp_clk_impl(_clocks: &mut ClockTree, en: bool) { + MODEM_LPCON::regs() + .clk_conf() + .modify(|_, w| w.clk_lp_timer_en().bit(en)); +} + +fn configure_ble_lp_clk_impl( + _clocks: &mut ClockTree, + _old_config: Option, + new_config: BleLpClkConfig, +) { + let sclk = new_config.sclk(); + let sel_32k = ble_lp_clk_32k_sel(sclk); + + MODEM_LPCON::regs() + .test_conf() + .modify(|_, w| w.clk_en().set_bit()); + + MODEM_LPCON::regs().lp_timer_conf().modify(|_, w| unsafe { + w.clk_lp_timer_sel_osc_slow() + .bit(sclk == BleLpClkSclk::RcSlow); + w.clk_lp_timer_sel_osc_fast().clear_bit(); + w.clk_lp_timer_sel_xtal().bit(sclk == BleLpClkSclk::Xtal); + w.clk_lp_timer_sel_xtal32k().bit(sel_32k.is_some()); + w.clk_lp_timer_div_num().bits(new_config.divisor() as u16) + }); + + if let Some(sel) = sel_32k { + MODEM_LPCON::regs() + .modem_32k_clk_conf() + .modify(|_, w| unsafe { w.clk_modem_32k_sel().bits(sel) }); + } +} + +fn ble_lp_clk_32k_sel(sclk: BleLpClkSclk) -> Option { + match sclk { + #[cfg(use_xtal32k)] + BleLpClkSclk::Xtal32k => Some(0), + BleLpClkSclk::Rc32k => Some(1), + BleLpClkSclk::Ext32k => Some(2), + _ => None, + } +} diff --git a/esp-hal/src/soc/esp32h2/clocks.rs b/esp-hal/src/soc/esp32h2/clocks.rs index ed51b36a793..5346ea1baf2 100644 --- a/esp-hal/src/soc/esp32h2/clocks.rs +++ b/esp-hal/src/soc/esp32h2/clocks.rs @@ -46,6 +46,11 @@ impl CpuClock { iomux_function_clock: Some(IomuxFunctionClockConfig::PllF48m), lp_fast_clk: Some(LpFastClkConfig::RcFastClk), lp_slow_clk: Some(xtal32k::default_lp_slow_clk()), + // 32 MHz crystal divided to 100 kHz. + ble_lp_clk: Some(BleLpClkConfig::new( + BleLpClkSclk::Xtal, + 32_000_000 / 100_000 - 1, + )), timg_calibration_clock: None, }; } @@ -198,6 +203,12 @@ fn enable_xtal32k_clk_impl(_clocks: &mut ClockTree, en: bool) { // OSC_SLOW_CLK +fn enable_rc32k_clk_impl(_clocks: &mut ClockTree, en: bool) { + PMU::regs() + .hp_sleep_lp_ck_power() + .modify(|_, w| w.hp_sleep_xpd_rc32k().bit(en)); +} + fn enable_osc_slow_clk_impl(_clocks: &mut ClockTree, _en: bool) { unimplemented!() } @@ -601,3 +612,38 @@ impl TimgInstance { }); } } + +fn enable_ble_lp_clk_impl(_clocks: &mut ClockTree, _en: bool) { + // ESP32-H2 has no BLE RTC timer clock gate. +} + +fn configure_ble_lp_clk_impl( + _clocks: &mut ClockTree, + _old_config: Option, + new_config: BleLpClkConfig, +) { + let sclk = new_config.sclk(); + let sel_32k = ble_lp_clk_32k_sel(sclk); + + LP_CLKRST::regs().lpperi().modify(|_, w| unsafe { + w.lp_sel_osc_slow().bit(sclk == BleLpClkSclk::RcSlow); + w.lp_sel_osc_fast().clear_bit(); + w.lp_sel_xtal().bit(sclk == BleLpClkSclk::Xtal); + w.lp_sel_xtal32k().bit(sel_32k.is_some()); + w.lp_bletimer_div_num().bits(new_config.divisor() as u16); + if let Some(sel) = sel_32k { + w.lp_bletimer_32k_sel().bits(sel); + } + w + }); +} + +fn ble_lp_clk_32k_sel(sclk: BleLpClkSclk) -> Option { + match sclk { + #[cfg(use_xtal32k)] + BleLpClkSclk::Xtal32k => Some(0), + BleLpClkSclk::Rc32k => Some(1), + BleLpClkSclk::Ext32k => Some(2), + _ => None, + } +} diff --git a/esp-hal/src/soc/esp32s3/clocks.rs b/esp-hal/src/soc/esp32s3/clocks.rs index 9f5b3ccf212..3693eb0196c 100644 --- a/esp-hal/src/soc/esp32s3/clocks.rs +++ b/esp-hal/src/soc/esp32s3/clocks.rs @@ -55,6 +55,11 @@ impl CpuClock { cpu_clk: Some(CpuClkConfig::Pll), rc_fast_clk_div_n: Some(RcFastClkDivNConfig::new(0)), rtc_slow_clk: Some(xtal32k::default_rtc_slow_clk()), + // 40 MHz crystal divided to 1 MHz. + ble_lp_clk: Some(BleLpClkConfig::new( + BleLpClkSclk::Xtal, + 40_000_000 / 1_000_000 - 1, + )), rtc_fast_clk: Some(RtcFastClkConfig::Rc), low_power_clk: Some(LowPowerClkConfig::RtcSlow), timg_calibration_clock: None, @@ -67,6 +72,11 @@ impl CpuClock { cpu_clk: Some(CpuClkConfig::Pll), rc_fast_clk_div_n: Some(RcFastClkDivNConfig::new(0)), rtc_slow_clk: Some(xtal32k::default_rtc_slow_clk()), + // 40 MHz crystal divided to 1 MHz. + ble_lp_clk: Some(BleLpClkConfig::new( + BleLpClkSclk::Xtal, + 40_000_000 / 1_000_000 - 1, + )), rtc_fast_clk: Some(RtcFastClkConfig::Rc), low_power_clk: Some(LowPowerClkConfig::RtcSlow), timg_calibration_clock: None, @@ -79,6 +89,11 @@ impl CpuClock { cpu_clk: Some(CpuClkConfig::Pll), rc_fast_clk_div_n: Some(RcFastClkDivNConfig::new(0)), rtc_slow_clk: Some(xtal32k::default_rtc_slow_clk()), + // 40 MHz crystal divided to 1 MHz. + ble_lp_clk: Some(BleLpClkConfig::new( + BleLpClkSclk::Xtal, + 40_000_000 / 1_000_000 - 1, + )), rtc_fast_clk: Some(RtcFastClkConfig::Rc), low_power_clk: Some(LowPowerClkConfig::RtcSlow), timg_calibration_clock: None, @@ -917,3 +932,13 @@ impl TimgInstance { }); } } + +fn enable_ble_lp_clk_impl(_clocks: &mut ClockTree, _en: bool) {} + +fn configure_ble_lp_clk_impl( + _clocks: &mut ClockTree, + _old_config: Option, + _new_config: BleLpClkConfig, +) { + // The BTDM controller programs this mux from the clock-tree selection. +} diff --git a/esp-hal/src/soc/esp32s31/clocks.rs b/esp-hal/src/soc/esp32s31/clocks.rs index feed532d73c..aa8996c1f94 100644 --- a/esp-hal/src/soc/esp32s31/clocks.rs +++ b/esp-hal/src/soc/esp32s31/clocks.rs @@ -13,7 +13,7 @@ use core::sync::atomic::{AtomicBool, Ordering}; use esp_rom_sys::rom::ets_update_cpu_frequency_rom; use crate::{ - peripherals::{HP_ALIVE_SYS, HP_SYS, HP_SYS_CLKRST, LP_AON_CLK_RST, PMU}, + peripherals::{HP_ALIVE_SYS, HP_SYS, HP_SYS_CLKRST, LP_AON_CLK_RST, MODEM_LPCON, PMU}, soc::xtal32k, }; @@ -46,6 +46,11 @@ impl CpuClock { apb_clk: Some(ApbClkConfig::new(1)), // MAX ~320/6MHz lp_fast_clk: Some(LpFastClkConfig::RcFast), lp_slow_clk: Some(xtal32k::default_lp_slow_clk()), + // 40 MHz crystal divided to 100 kHz. + ble_lp_clk: Some(BleLpClkConfig::new( + BleLpClkSclk::Xtal, + 40_000_000 / 100_000 - 1, + )), crypto_clk: Some(CryptoClkConfig::PllF240m), iomux_function_clock: Some(IomuxFunctionClockConfig::new( IomuxFunctionClockSource::PllF80m, @@ -61,6 +66,11 @@ impl CpuClock { apb_clk: Some(ApbClkConfig::new(1)), // MAX ~320/6MHz lp_fast_clk: Some(LpFastClkConfig::RcFast), lp_slow_clk: Some(xtal32k::default_lp_slow_clk()), + // 40 MHz crystal divided to 100 kHz. + ble_lp_clk: Some(BleLpClkConfig::new( + BleLpClkSclk::Xtal, + 40_000_000 / 100_000 - 1, + )), crypto_clk: Some(CryptoClkConfig::PllF240m), iomux_function_clock: Some(IomuxFunctionClockConfig::new( IomuxFunctionClockSource::PllF80m, @@ -76,6 +86,11 @@ impl CpuClock { apb_clk: Some(ApbClkConfig::new(1)), // MAX ~320/6MHz lp_fast_clk: Some(LpFastClkConfig::RcFast), lp_slow_clk: Some(xtal32k::default_lp_slow_clk()), + // 40 MHz crystal divided to 100 kHz. + ble_lp_clk: Some(BleLpClkConfig::new( + BleLpClkSclk::Xtal, + 40_000_000 / 100_000 - 1, + )), crypto_clk: Some(CryptoClkConfig::PllF240m), iomux_function_clock: Some(IomuxFunctionClockConfig::new( IomuxFunctionClockSource::PllF80m, @@ -359,6 +374,19 @@ fn enable_rc_slow_clk_impl(_clocks: &mut ClockTree, en: bool) { .modify(|_, w| w.hp_sosc_150k_clk_en().bit(en)); } +fn enable_osc_slow_clk_impl(_clocks: &mut ClockTree, _en: bool) { + // External 32 kHz input. Pad setup is not part of this node. +} + +fn enable_rc32k_clk_impl(_clocks: &mut ClockTree, en: bool) { + PMU::regs() + .hp_sleep_lp_ck_power() + .modify(|_, w| w.hp_sleep_xpd_rc32k().bit(en)); + HP_ALIVE_SYS::regs() + .hp_clk_ctrl() + .modify(|_, w| w.hp_rc_32k_clk_en().bit(en)); +} + macro_rules! pll_gate { ($name:ident, $register:ident) => { fn $name(_clocks: &mut ClockTree, en: bool) { @@ -796,3 +824,55 @@ impl LcdCamInstance { }); } } + +fn enable_ble_lp_clk_impl(_clocks: &mut ClockTree, en: bool) { + MODEM_LPCON::regs() + .clk_conf() + .modify(|_, w| w.clk_lp_timer_en().bit(en)); +} + +fn configure_ble_lp_clk_impl( + _clocks: &mut ClockTree, + _old_config: Option, + new_config: BleLpClkConfig, +) { + let sclk = new_config.sclk(); + let sel_32k = ble_lp_clk_32k_sel(sclk); + + MODEM_LPCON::regs() + .test_conf() + .modify(|_, w| w.clk_en().set_bit()); + + MODEM_LPCON::regs().lp_timer_conf().modify(|_, w| unsafe { + w.clk_lp_timer_sel_osc_slow() + .bit(sclk == BleLpClkSclk::RcSlow); + w.clk_lp_timer_sel_osc_fast().clear_bit(); + w.clk_lp_timer_sel_xtal().bit(sclk == BleLpClkSclk::Xtal); + w.clk_lp_timer_sel_xtal32k().bit(sel_32k.is_some()); + w.clk_lp_timer_div_num().bits(new_config.divisor() as u16) + }); + + if let Some(sel) = sel_32k { + MODEM_LPCON::regs() + .modem_32k_clk_conf() + .modify(|_, w| unsafe { w.clk_modem_32k_sel().bits(sel) }); + } + + // IDF `modem_lpcon_ll_reset_ble_rtc_timer`. + MODEM_LPCON::regs() + .rst_conf() + .modify(|_, w| w.rst_lp_timer().set_bit()); + MODEM_LPCON::regs() + .rst_conf() + .modify(|_, w| w.rst_lp_timer().clear_bit()); +} + +fn ble_lp_clk_32k_sel(sclk: BleLpClkSclk) -> Option { + match sclk { + #[cfg(use_xtal32k)] + BleLpClkSclk::Xtal32k => Some(0), + BleLpClkSclk::Rc32k => Some(1), + BleLpClkSclk::Ext32k => Some(2), + _ => None, + } +} diff --git a/esp-metadata-generated/src/_build_script_utils.rs b/esp-metadata-generated/src/_build_script_utils.rs index 8fdcd654ce3..b0f5eda7b7e 100644 --- a/esp-metadata-generated/src/_build_script_utils.rs +++ b/esp-metadata-generated/src/_build_script_utils.rs @@ -465,6 +465,8 @@ impl Chip { "soc_has_clock_node_xtal_div_clk", "soc_has_clock_node_rtc_slow_clk", "soc_clock_node_rtc_slow_clk_is_configurable", + "soc_has_clock_node_ble_lp_clk", + "soc_clock_node_ble_lp_clk_is_configurable", "soc_has_clock_node_rtc_fast_clk", "soc_clock_node_rtc_fast_clk_is_configurable", "soc_has_clock_node_uart_mem_clk", @@ -753,6 +755,8 @@ impl Chip { "cargo:rustc-cfg=soc_has_clock_node_xtal_div_clk", "cargo:rustc-cfg=soc_has_clock_node_rtc_slow_clk", "cargo:rustc-cfg=soc_clock_node_rtc_slow_clk_is_configurable", + "cargo:rustc-cfg=soc_has_clock_node_ble_lp_clk", + "cargo:rustc-cfg=soc_clock_node_ble_lp_clk_is_configurable", "cargo:rustc-cfg=soc_has_clock_node_rtc_fast_clk", "cargo:rustc-cfg=soc_clock_node_rtc_fast_clk_is_configurable", "cargo:rustc-cfg=soc_has_clock_node_uart_mem_clk", @@ -1139,6 +1143,9 @@ impl Chip { "soc_has_clock_node_xtal_div_clk", "soc_has_clock_node_rtc_slow_clk", "soc_clock_node_rtc_slow_clk_is_configurable", + "soc_has_clock_node_ble_lp_xtal_clk", + "soc_has_clock_node_ble_lp_clk", + "soc_clock_node_ble_lp_clk_is_configurable", "soc_has_clock_node_rtc_fast_clk", "soc_clock_node_rtc_fast_clk_is_configurable", "soc_has_clock_node_low_power_clk", @@ -1352,6 +1359,9 @@ impl Chip { "cargo:rustc-cfg=soc_has_clock_node_xtal_div_clk", "cargo:rustc-cfg=soc_has_clock_node_rtc_slow_clk", "cargo:rustc-cfg=soc_clock_node_rtc_slow_clk_is_configurable", + "cargo:rustc-cfg=soc_has_clock_node_ble_lp_xtal_clk", + "cargo:rustc-cfg=soc_has_clock_node_ble_lp_clk", + "cargo:rustc-cfg=soc_clock_node_ble_lp_clk_is_configurable", "cargo:rustc-cfg=soc_has_clock_node_rtc_fast_clk", "cargo:rustc-cfg=soc_clock_node_rtc_fast_clk_is_configurable", "cargo:rustc-cfg=soc_has_clock_node_low_power_clk", @@ -1748,6 +1758,8 @@ impl Chip { "soc_has_clock_node_xtal_div_clk", "soc_has_clock_node_rtc_slow_clk", "soc_clock_node_rtc_slow_clk_is_configurable", + "soc_has_clock_node_ble_lp_clk", + "soc_clock_node_ble_lp_clk_is_configurable", "soc_has_clock_node_rtc_fast_clk", "soc_clock_node_rtc_fast_clk_is_configurable", "soc_has_clock_node_low_power_clk", @@ -2035,6 +2047,8 @@ impl Chip { "cargo:rustc-cfg=soc_has_clock_node_xtal_div_clk", "cargo:rustc-cfg=soc_has_clock_node_rtc_slow_clk", "cargo:rustc-cfg=soc_clock_node_rtc_slow_clk_is_configurable", + "cargo:rustc-cfg=soc_has_clock_node_ble_lp_clk", + "cargo:rustc-cfg=soc_clock_node_ble_lp_clk_is_configurable", "cargo:rustc-cfg=soc_has_clock_node_rtc_fast_clk", "cargo:rustc-cfg=soc_clock_node_rtc_fast_clk_is_configurable", "cargo:rustc-cfg=soc_has_clock_node_low_power_clk", @@ -2472,6 +2486,7 @@ impl Chip { "soc_has_clock_node_rc_fast_clk", "soc_has_clock_node_xtal32k_clk", "soc_has_clock_node_osc_slow_clk", + "soc_has_clock_node_rc32k_clk", "soc_has_clock_node_rc_slow_clk", "soc_has_clock_node_pll_f12m", "soc_has_clock_node_pll_f20m", @@ -2495,6 +2510,8 @@ impl Chip { "soc_clock_node_lp_fast_clk_is_configurable", "soc_has_clock_node_lp_slow_clk", "soc_clock_node_lp_slow_clk_is_configurable", + "soc_has_clock_node_ble_lp_clk", + "soc_clock_node_ble_lp_clk_is_configurable", "soc_has_clock_node_crypto_pll_div", "soc_has_clock_node_crypto_clk", "soc_clock_node_crypto_clk_is_configurable", @@ -2813,6 +2830,7 @@ impl Chip { "cargo:rustc-cfg=soc_has_clock_node_rc_fast_clk", "cargo:rustc-cfg=soc_has_clock_node_xtal32k_clk", "cargo:rustc-cfg=soc_has_clock_node_osc_slow_clk", + "cargo:rustc-cfg=soc_has_clock_node_rc32k_clk", "cargo:rustc-cfg=soc_has_clock_node_rc_slow_clk", "cargo:rustc-cfg=soc_has_clock_node_pll_f12m", "cargo:rustc-cfg=soc_has_clock_node_pll_f20m", @@ -2836,6 +2854,8 @@ impl Chip { "cargo:rustc-cfg=soc_clock_node_lp_fast_clk_is_configurable", "cargo:rustc-cfg=soc_has_clock_node_lp_slow_clk", "cargo:rustc-cfg=soc_clock_node_lp_slow_clk_is_configurable", + "cargo:rustc-cfg=soc_has_clock_node_ble_lp_clk", + "cargo:rustc-cfg=soc_clock_node_ble_lp_clk_is_configurable", "cargo:rustc-cfg=soc_has_clock_node_crypto_pll_div", "cargo:rustc-cfg=soc_has_clock_node_crypto_clk", "cargo:rustc-cfg=soc_clock_node_crypto_clk_is_configurable", @@ -3312,6 +3332,7 @@ impl Chip { "soc_has_clock_node_rc_fast_clk", "soc_has_clock_node_xtal32k_clk", "soc_has_clock_node_osc_slow_clk", + "soc_has_clock_node_rc32k_clk", "soc_has_clock_node_rc_slow_clk", "soc_has_clock_node_soc_root_clk", "soc_clock_node_soc_root_clk_is_configurable", @@ -3350,6 +3371,10 @@ impl Chip { "soc_clock_node_lp_fast_clk_is_configurable", "soc_has_clock_node_lp_slow_clk", "soc_clock_node_lp_slow_clk_is_configurable", + "soc_has_clock_node_ble_lp_xtal_clk", + "soc_clock_node_ble_lp_xtal_clk_is_configurable", + "soc_has_clock_node_ble_lp_clk", + "soc_clock_node_ble_lp_clk_is_configurable", "soc_has_clock_node_timg_calibration_clock", "soc_clock_node_timg_calibration_clock_is_configurable", "soc_has_clock_node_uart_function_clock", @@ -3673,6 +3698,7 @@ impl Chip { "cargo:rustc-cfg=soc_has_clock_node_rc_fast_clk", "cargo:rustc-cfg=soc_has_clock_node_xtal32k_clk", "cargo:rustc-cfg=soc_has_clock_node_osc_slow_clk", + "cargo:rustc-cfg=soc_has_clock_node_rc32k_clk", "cargo:rustc-cfg=soc_has_clock_node_rc_slow_clk", "cargo:rustc-cfg=soc_has_clock_node_soc_root_clk", "cargo:rustc-cfg=soc_clock_node_soc_root_clk_is_configurable", @@ -3711,6 +3737,10 @@ impl Chip { "cargo:rustc-cfg=soc_clock_node_lp_fast_clk_is_configurable", "cargo:rustc-cfg=soc_has_clock_node_lp_slow_clk", "cargo:rustc-cfg=soc_clock_node_lp_slow_clk_is_configurable", + "cargo:rustc-cfg=soc_has_clock_node_ble_lp_xtal_clk", + "cargo:rustc-cfg=soc_clock_node_ble_lp_xtal_clk_is_configurable", + "cargo:rustc-cfg=soc_has_clock_node_ble_lp_clk", + "cargo:rustc-cfg=soc_clock_node_ble_lp_clk_is_configurable", "cargo:rustc-cfg=soc_has_clock_node_timg_calibration_clock", "cargo:rustc-cfg=soc_clock_node_timg_calibration_clock_is_configurable", "cargo:rustc-cfg=soc_has_clock_node_uart_function_clock", @@ -4124,6 +4154,7 @@ impl Chip { "soc_has_clock_node_pll_clk", "soc_has_clock_node_xtal32k_clk", "soc_has_clock_node_osc_slow_clk", + "soc_has_clock_node_rc32k_clk", "soc_has_clock_node_rc_slow_clk", "soc_has_clock_node_pll_f20m", "soc_has_clock_node_pll_f40m", @@ -4145,6 +4176,8 @@ impl Chip { "soc_clock_node_lp_fast_clk_is_configurable", "soc_has_clock_node_lp_slow_clk", "soc_clock_node_lp_slow_clk_is_configurable", + "soc_has_clock_node_ble_lp_clk", + "soc_clock_node_ble_lp_clk_is_configurable", "soc_has_clock_node_crypto_pll_div", "soc_has_clock_node_crypto_clk", "soc_clock_node_crypto_clk_is_configurable", @@ -4394,6 +4427,7 @@ impl Chip { "cargo:rustc-cfg=soc_has_clock_node_pll_clk", "cargo:rustc-cfg=soc_has_clock_node_xtal32k_clk", "cargo:rustc-cfg=soc_has_clock_node_osc_slow_clk", + "cargo:rustc-cfg=soc_has_clock_node_rc32k_clk", "cargo:rustc-cfg=soc_has_clock_node_rc_slow_clk", "cargo:rustc-cfg=soc_has_clock_node_pll_f20m", "cargo:rustc-cfg=soc_has_clock_node_pll_f40m", @@ -4415,6 +4449,8 @@ impl Chip { "cargo:rustc-cfg=soc_clock_node_lp_fast_clk_is_configurable", "cargo:rustc-cfg=soc_has_clock_node_lp_slow_clk", "cargo:rustc-cfg=soc_clock_node_lp_slow_clk_is_configurable", + "cargo:rustc-cfg=soc_has_clock_node_ble_lp_clk", + "cargo:rustc-cfg=soc_clock_node_ble_lp_clk_is_configurable", "cargo:rustc-cfg=soc_has_clock_node_crypto_pll_div", "cargo:rustc-cfg=soc_has_clock_node_crypto_clk", "cargo:rustc-cfg=soc_clock_node_crypto_clk_is_configurable", @@ -4867,6 +4903,7 @@ impl Chip { "soc_clock_node_iomux_function_clock_is_configurable", "soc_has_clock_node_xtal32k_clk", "soc_has_clock_node_osc_slow_clk", + "soc_has_clock_node_rc32k_clk", "soc_has_clock_node_rc_slow_clk", "soc_has_clock_node_pll_lp_clk", "soc_has_clock_node_hp_root_clk", @@ -4882,6 +4919,8 @@ impl Chip { "soc_clock_node_lp_fast_clk_is_configurable", "soc_has_clock_node_lp_slow_clk", "soc_clock_node_lp_slow_clk_is_configurable", + "soc_has_clock_node_ble_lp_clk", + "soc_clock_node_ble_lp_clk_is_configurable", "soc_has_clock_node_crypto_clk", "soc_clock_node_crypto_clk_is_configurable", "soc_has_clock_node_timg_calibration_clock", @@ -5188,6 +5227,7 @@ impl Chip { "cargo:rustc-cfg=soc_clock_node_iomux_function_clock_is_configurable", "cargo:rustc-cfg=soc_has_clock_node_xtal32k_clk", "cargo:rustc-cfg=soc_has_clock_node_osc_slow_clk", + "cargo:rustc-cfg=soc_has_clock_node_rc32k_clk", "cargo:rustc-cfg=soc_has_clock_node_rc_slow_clk", "cargo:rustc-cfg=soc_has_clock_node_pll_lp_clk", "cargo:rustc-cfg=soc_has_clock_node_hp_root_clk", @@ -5203,6 +5243,8 @@ impl Chip { "cargo:rustc-cfg=soc_clock_node_lp_fast_clk_is_configurable", "cargo:rustc-cfg=soc_has_clock_node_lp_slow_clk", "cargo:rustc-cfg=soc_clock_node_lp_slow_clk_is_configurable", + "cargo:rustc-cfg=soc_has_clock_node_ble_lp_clk", + "cargo:rustc-cfg=soc_clock_node_ble_lp_clk_is_configurable", "cargo:rustc-cfg=soc_has_clock_node_crypto_clk", "cargo:rustc-cfg=soc_clock_node_crypto_clk_is_configurable", "cargo:rustc-cfg=soc_has_clock_node_timg_calibration_clock", @@ -7461,6 +7503,8 @@ impl Chip { "soc_has_clock_node_xtal_div_clk", "soc_has_clock_node_rtc_slow_clk", "soc_clock_node_rtc_slow_clk_is_configurable", + "soc_has_clock_node_ble_lp_clk", + "soc_clock_node_ble_lp_clk_is_configurable", "soc_has_clock_node_rtc_fast_clk", "soc_clock_node_rtc_fast_clk_is_configurable", "soc_has_clock_node_low_power_clk", @@ -7812,6 +7856,8 @@ impl Chip { "cargo:rustc-cfg=soc_has_clock_node_xtal_div_clk", "cargo:rustc-cfg=soc_has_clock_node_rtc_slow_clk", "cargo:rustc-cfg=soc_clock_node_rtc_slow_clk_is_configurable", + "cargo:rustc-cfg=soc_has_clock_node_ble_lp_clk", + "cargo:rustc-cfg=soc_clock_node_ble_lp_clk_is_configurable", "cargo:rustc-cfg=soc_has_clock_node_rtc_fast_clk", "cargo:rustc-cfg=soc_clock_node_rtc_fast_clk_is_configurable", "cargo:rustc-cfg=soc_has_clock_node_low_power_clk", @@ -8142,6 +8188,7 @@ impl Chip { "soc_has_usb_hs", "soc_has_adc1", "soc_has_adc2", + "soc_has_bt", "soc_has_flash", "soc_has_psram", "soc_has_gpio_dedicated", @@ -8166,6 +8213,7 @@ impl Chip { "sdmmc_driver_supported", "usb_otg_hs_driver_supported", "usb_serial_jtag_driver_supported", + "bt_driver_supported", "wifi_driver_supported", "ieee802154_driver_supported", "ethernet_driver_supported", @@ -8272,6 +8320,7 @@ impl Chip { "sdmmc_has_iomux", "sdmmc_psram_dma", "usb_otg_hs_fifo_depth_words=\"896\"", + "bt_controller=\"btdm2\"", "wifi_has_wifi6", "wifi_mac_version=\"3\"", "wifi_csi_supported", @@ -8375,6 +8424,8 @@ impl Chip { "soc_has_clock_node_mpll_clk", "soc_has_clock_node_rc_fast_clk", "soc_has_clock_node_xtal32k_clk", + "soc_has_clock_node_osc_slow_clk", + "soc_has_clock_node_rc32k_clk", "soc_has_clock_node_rc_slow_clk", "soc_has_clock_node_pll_f20m", "soc_has_clock_node_pll_f80m", @@ -8399,6 +8450,8 @@ impl Chip { "soc_clock_node_lp_fast_clk_is_configurable", "soc_has_clock_node_lp_slow_clk", "soc_clock_node_lp_slow_clk_is_configurable", + "soc_has_clock_node_ble_lp_clk", + "soc_clock_node_ble_lp_clk_is_configurable", "soc_has_clock_node_crypto_clk", "soc_clock_node_crypto_clk_is_configurable", "soc_has_clock_node_iomux_function_clock", @@ -8517,6 +8570,7 @@ impl Chip { "cargo:rustc-cfg=soc_has_usb_hs", "cargo:rustc-cfg=soc_has_adc1", "cargo:rustc-cfg=soc_has_adc2", + "cargo:rustc-cfg=soc_has_bt", "cargo:rustc-cfg=soc_has_flash", "cargo:rustc-cfg=soc_has_psram", "cargo:rustc-cfg=soc_has_gpio_dedicated", @@ -8541,6 +8595,7 @@ impl Chip { "cargo:rustc-cfg=sdmmc_driver_supported", "cargo:rustc-cfg=usb_otg_hs_driver_supported", "cargo:rustc-cfg=usb_serial_jtag_driver_supported", + "cargo:rustc-cfg=bt_driver_supported", "cargo:rustc-cfg=wifi_driver_supported", "cargo:rustc-cfg=ieee802154_driver_supported", "cargo:rustc-cfg=ethernet_driver_supported", @@ -8647,6 +8702,7 @@ impl Chip { "cargo:rustc-cfg=sdmmc_has_iomux", "cargo:rustc-cfg=sdmmc_psram_dma", "cargo:rustc-cfg=usb_otg_hs_fifo_depth_words=\"896\"", + "cargo:rustc-cfg=bt_controller=\"btdm2\"", "cargo:rustc-cfg=wifi_has_wifi6", "cargo:rustc-cfg=wifi_mac_version=\"3\"", "cargo:rustc-cfg=wifi_csi_supported", @@ -8750,6 +8806,8 @@ impl Chip { "cargo:rustc-cfg=soc_has_clock_node_mpll_clk", "cargo:rustc-cfg=soc_has_clock_node_rc_fast_clk", "cargo:rustc-cfg=soc_has_clock_node_xtal32k_clk", + "cargo:rustc-cfg=soc_has_clock_node_osc_slow_clk", + "cargo:rustc-cfg=soc_has_clock_node_rc32k_clk", "cargo:rustc-cfg=soc_has_clock_node_rc_slow_clk", "cargo:rustc-cfg=soc_has_clock_node_pll_f20m", "cargo:rustc-cfg=soc_has_clock_node_pll_f80m", @@ -8774,6 +8832,8 @@ impl Chip { "cargo:rustc-cfg=soc_clock_node_lp_fast_clk_is_configurable", "cargo:rustc-cfg=soc_has_clock_node_lp_slow_clk", "cargo:rustc-cfg=soc_clock_node_lp_slow_clk_is_configurable", + "cargo:rustc-cfg=soc_has_clock_node_ble_lp_clk", + "cargo:rustc-cfg=soc_clock_node_ble_lp_clk_is_configurable", "cargo:rustc-cfg=soc_has_clock_node_crypto_clk", "cargo:rustc-cfg=soc_clock_node_crypto_clk_is_configurable", "cargo:rustc-cfg=soc_has_clock_node_iomux_function_clock", @@ -9314,6 +9374,8 @@ impl Chip { "soc_has_clock_node_xtal_div_clk", "soc_has_clock_node_rtc_slow_clk", "soc_clock_node_rtc_slow_clk_is_configurable", + "soc_has_clock_node_ble_lp_clk", + "soc_clock_node_ble_lp_clk_is_configurable", "soc_has_clock_node_rtc_fast_clk", "soc_clock_node_rtc_fast_clk_is_configurable", "soc_has_clock_node_uart_mem_clk", @@ -9398,6 +9460,7 @@ impl Chip { "soc_clock_node_mspi_clk_is_configurable", "soc_has_clock_node_rc_fast_clk_div_n", "soc_clock_node_rc_fast_clk_div_n_is_configurable", + "soc_has_clock_node_ble_lp_xtal_clk", "soc_has_clock_node_low_power_clk", "soc_clock_node_low_power_clk_is_configurable", "soc_has_clock_node_timg_function_clock", @@ -9507,6 +9570,7 @@ impl Chip { "dma_can_access_psram", "soc_cpu_has_branch_predictor", "soc_cpu_csr_prv_mode_is_set", + "soc_has_clock_node_rc32k_clk", "soc_has_clock_node_pll_f12m", "soc_has_clock_node_pll_f20m", "soc_has_clock_node_pll_f40m", @@ -9561,6 +9625,7 @@ impl Chip { "soc_clock_node_mspi_fast_clk_is_configurable", "soc_has_clock_node_ledc_sclk", "soc_clock_node_ledc_sclk_is_configurable", + "soc_clock_node_ble_lp_xtal_clk_is_configurable", "soc_clock_node_mcpwm_function_clock_is_configurable", "esp32c61", "esp32h2", @@ -9624,7 +9689,6 @@ impl Chip { "soc_has_clock_node_spll_clk", "soc_has_clock_node_mpll_clk", "soc_clock_node_mpll_clk_is_configurable", - "soc_has_clock_node_rc32k_clk", "soc_has_clock_node_spll_d3_clock", "soc_has_clock_node_pll_f25m", "soc_has_clock_node_pll_f50m", @@ -9710,7 +9774,7 @@ impl Chip { values(\"1073047552\",\"1610703872\",\"1610638336\",\"1610642432\",\"1342842880\",\"\ 1061250048\",\"1610704896\",\"540366848\")", "rmt_channel_ram_size, values(\"64\",\"48\")", - "bt_controller, values(\"btdm\",\"npl\")", + "bt_controller, values(\"btdm\",\"npl\",\"btdm2\")", "wifi_mac_version, values(\"1\",\"3\",\"2\")", "ethernet_dma_burst_len, values(\"32\",\"16\")", "rgb_display_output_lines, values(\"24\",\"16\")", diff --git a/esp-metadata-generated/src/_generated_esp32.rs b/esp-metadata-generated/src/_generated_esp32.rs index d516a8b050b..8c7eecf2597 100644 --- a/esp-metadata-generated/src/_generated_esp32.rs +++ b/esp-metadata-generated/src/_generated_esp32.rs @@ -559,6 +559,13 @@ macro_rules! property { ::soc::clocks::RtcSlowClkConfig::RcSlow, crate ::soc::clocks::RtcSlowClkConfig::RcFast] }; + ("clock_tree.ble_lp_clk.sclk") => { + [crate ::soc::clocks::BleLpClkSclk::Xtal, crate + ::soc::clocks::BleLpClkSclk::RtcSlow] + }; + ("clock_tree.ble_lp_clk.divisor") => { + (0, 255) + }; ("clock_tree.rtc_fast_clk") => { [crate ::soc::clocks::RtcFastClkConfig::Xtal, crate ::soc::clocks::RtcFastClkConfig::Rc] @@ -1148,6 +1155,20 @@ macro_rules! for_each_sw_interrupt { /// todo!() /// } /// +/// // BLE_LP_CLK +/// +/// fn enable_ble_lp_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_ble_lp_clk_impl( +/// _clocks: &mut ClockTree, +/// _old_config: Option, +/// _new_config: BleLpClkConfig, +/// ) { +/// todo!() +/// } +/// /// // RTC_FAST_CLK /// /// fn enable_rtc_fast_clk_impl(_clocks: &mut ClockTree, _en: bool) { @@ -1431,7 +1452,7 @@ macro_rules! define_clock_tree_types { pub const fn new(divisor: CpuPllDivDivisor) -> Self { Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -1466,7 +1487,7 @@ macro_rules! define_clock_tree_types { ); Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -1516,7 +1537,7 @@ macro_rules! define_clock_tree_types { ); Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -1542,7 +1563,7 @@ macro_rules! define_clock_tree_types { ); Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -1568,7 +1589,7 @@ macro_rules! define_clock_tree_types { ); Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -1594,7 +1615,7 @@ macro_rules! define_clock_tree_types { ); Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -1623,6 +1644,45 @@ macro_rules! define_clock_tree_types { /// Selects `RC_FAST_DIV_CLK`. RcFast, } + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum BleLpClkSclk { + #[default] + /// Selects `XTAL_CLK`. + Xtal, + /// Selects `RTC_SLOW_CLK`. + RtcSlow, + } + /// Configures the `BLE_LP_CLK` clock node. + /// + /// The output is calculated as `OUTPUT = sclk / (divisor + 1)`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct BleLpClkConfig { + sclk: BleLpClkSclk, + divisor: u32, + } + impl BleLpClkConfig { + /// Creates a new configuration for the BLE_LP_CLK clock node. + /// + /// ## Panics + /// + /// Panics if the divisor value is outside the + /// valid range (0 ..= 255). + pub const fn new(sclk: BleLpClkSclk, divisor: u32) -> Self { + ::core::assert!( + divisor <= 255, + "`BLE_LP_CLK` divisor must be between 0 and 255 (inclusive)." + ); + Self { sclk, divisor } + } + pub fn sclk(self) -> BleLpClkSclk { + self.sclk + } + pub fn divisor(self) -> u32 { + self.divisor as u32 + } + } /// The list of clock signals that the `RTC_FAST_CLK` multiplexer can output. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -1672,7 +1732,7 @@ macro_rules! define_clock_tree_types { pub const fn new(sclk: I2cFunctionClockSclk) -> Self { Self { sclk } } - pub(crate) fn sclk(self) -> I2cFunctionClockSclk { + pub fn sclk(self) -> I2cFunctionClockSclk { self.sclk } } @@ -1729,16 +1789,16 @@ macro_rules! define_clock_tree_types { div_b, } } - pub(crate) fn sclk(self) -> I2sMclkSclk { + pub fn sclk(self) -> I2sMclkSclk { self.sclk } - pub(crate) fn div_num(self) -> u32 { + pub fn div_num(self) -> u32 { self.div_num as u32 } - pub(crate) fn div_a(self) -> u32 { + pub fn div_a(self) -> u32 { self.div_a as u32 } - pub(crate) fn div_b(self) -> u32 { + pub fn div_b(self) -> u32 { self.div_b as u32 } } @@ -1790,7 +1850,7 @@ macro_rules! define_clock_tree_types { pub const fn new(sclk: UartFunctionClockSclk) -> Self { Self { sclk } } - pub(crate) fn sclk(self) -> UartFunctionClockSclk { + pub fn sclk(self) -> UartFunctionClockSclk { self.sclk } } @@ -1829,10 +1889,10 @@ macro_rules! define_clock_tree_types { integral, } } - pub(crate) fn fractional(self) -> u32 { + pub fn fractional(self) -> u32 { self.fractional as u32 } - pub(crate) fn integral(self) -> u32 { + pub fn integral(self) -> u32 { self.integral as u32 } } @@ -1853,6 +1913,7 @@ macro_rules! define_clock_tree_types { ref_tick_pll: Option, cpu_clk: Option, rtc_slow_clk: Option, + ble_lp_clk: Option, rtc_fast_clk: Option, timg_calibration_clock: Option, i2c_function_clock: [Option; 2], @@ -1871,7 +1932,7 @@ macro_rules! define_clock_tree_types { xtal32k_clk_refcount: u32, rc_slow_clk_refcount: u32, rc_fast_div_clk_refcount: u32, - rtc_slow_clk_refcount: u32, + ble_lp_clk_refcount: u32, rtc_fast_clk_refcount: u32, uart_mem_clk_refcount: u32, timg_calibration_clock_refcount: u32, @@ -1950,6 +2011,10 @@ macro_rules! define_clock_tree_types { pub fn rtc_slow_clk(&self) -> Option { self.rtc_slow_clk } + /// Returns the current configuration of the BLE_LP_CLK clock tree node + pub fn ble_lp_clk(&self) -> Option { + self.ble_lp_clk + } /// Returns the current configuration of the RTC_FAST_CLK clock tree node pub fn rtc_fast_clk(&self) -> Option { self.rtc_fast_clk @@ -2020,6 +2085,7 @@ macro_rules! define_clock_tree_types { ref_tick_pll: None, cpu_clk: None, rtc_slow_clk: None, + ble_lp_clk: None, rtc_fast_clk: None, timg_calibration_clock: None, i2c_function_clock: [None; 2], @@ -2038,7 +2104,7 @@ macro_rules! define_clock_tree_types { xtal32k_clk_refcount: 0, rc_slow_clk_refcount: 0, rc_fast_div_clk_refcount: 0, - rtc_slow_clk_refcount: 0, + ble_lp_clk_refcount: 0, rtc_fast_clk_refcount: 0, uart_mem_clk_refcount: 0, timg_calibration_clock_refcount: 0, @@ -2070,6 +2136,8 @@ macro_rules! define_clock_tree_types { ::core::sync::atomic::AtomicU32::new(0); static RTC_SLOW_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = ::core::sync::atomic::AtomicU32::new(0); + static BLE_LP_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = + ::core::sync::atomic::AtomicU32::new(0); static RTC_FAST_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = ::core::sync::atomic::AtomicU32::new(0); static TIMG_CALIBRATION_CLOCK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = @@ -2883,24 +2951,20 @@ macro_rules! define_clock_tree_types { pub fn configure_rtc_slow_clk(clocks: &mut ClockTree, new_selector: RtcSlowClkConfig) { let old_selector = clocks.rtc_slow_clk.replace(new_selector); refresh_rtc_slow_clk_downstream(clocks); - if clocks.rtc_slow_clk_refcount > 0 { - match new_selector { + match new_selector { + #[cfg(use_xtal32k)] + RtcSlowClkConfig::Xtal32k => request_xtal32k_clk(clocks), + RtcSlowClkConfig::RcSlow => request_rc_slow_clk(clocks), + RtcSlowClkConfig::RcFast => request_rc_fast_div_clk(clocks), + } + configure_rtc_slow_clk_impl(clocks, old_selector, new_selector); + if let Some(old_selector) = old_selector { + match old_selector { #[cfg(use_xtal32k)] - RtcSlowClkConfig::Xtal32k => request_xtal32k_clk(clocks), - RtcSlowClkConfig::RcSlow => request_rc_slow_clk(clocks), - RtcSlowClkConfig::RcFast => request_rc_fast_div_clk(clocks), - } - configure_rtc_slow_clk_impl(clocks, old_selector, new_selector); - if let Some(old_selector) = old_selector { - match old_selector { - #[cfg(use_xtal32k)] - RtcSlowClkConfig::Xtal32k => release_xtal32k_clk(clocks), - RtcSlowClkConfig::RcSlow => release_rc_slow_clk(clocks), - RtcSlowClkConfig::RcFast => release_rc_fast_div_clk(clocks), - } + RtcSlowClkConfig::Xtal32k => release_xtal32k_clk(clocks), + RtcSlowClkConfig::RcSlow => release_rc_slow_clk(clocks), + RtcSlowClkConfig::RcFast => release_rc_fast_div_clk(clocks), } - } else { - configure_rtc_slow_clk_impl(clocks, old_selector, new_selector); } } pub fn rtc_slow_clk_config(clocks: &mut ClockTree) -> Option { @@ -2908,28 +2972,24 @@ macro_rules! define_clock_tree_types { } pub fn request_rtc_slow_clk(clocks: &mut ClockTree) { trace!("Requesting RTC_SLOW_CLK"); - if increment_reference_count(&mut clocks.rtc_slow_clk_refcount) { - trace!("Enabling RTC_SLOW_CLK"); - match unwrap!(clocks.rtc_slow_clk) { - #[cfg(use_xtal32k)] - RtcSlowClkConfig::Xtal32k => request_xtal32k_clk(clocks), - RtcSlowClkConfig::RcSlow => request_rc_slow_clk(clocks), - RtcSlowClkConfig::RcFast => request_rc_fast_div_clk(clocks), - } - enable_rtc_slow_clk_impl(clocks, true); + trace!("Enabling RTC_SLOW_CLK"); + match unwrap!(clocks.rtc_slow_clk) { + #[cfg(use_xtal32k)] + RtcSlowClkConfig::Xtal32k => request_xtal32k_clk(clocks), + RtcSlowClkConfig::RcSlow => request_rc_slow_clk(clocks), + RtcSlowClkConfig::RcFast => request_rc_fast_div_clk(clocks), } + enable_rtc_slow_clk_impl(clocks, true); } pub fn release_rtc_slow_clk(clocks: &mut ClockTree) { trace!("Releasing RTC_SLOW_CLK"); - if decrement_reference_count(&mut clocks.rtc_slow_clk_refcount) { - trace!("Disabling RTC_SLOW_CLK"); - enable_rtc_slow_clk_impl(clocks, false); - match unwrap!(clocks.rtc_slow_clk) { - #[cfg(use_xtal32k)] - RtcSlowClkConfig::Xtal32k => release_xtal32k_clk(clocks), - RtcSlowClkConfig::RcSlow => release_rc_slow_clk(clocks), - RtcSlowClkConfig::RcFast => release_rc_fast_div_clk(clocks), - } + trace!("Disabling RTC_SLOW_CLK"); + enable_rtc_slow_clk_impl(clocks, false); + match unwrap!(clocks.rtc_slow_clk) { + #[cfg(use_xtal32k)] + RtcSlowClkConfig::Xtal32k => release_xtal32k_clk(clocks), + RtcSlowClkConfig::RcSlow => release_rc_slow_clk(clocks), + RtcSlowClkConfig::RcFast => release_rc_fast_div_clk(clocks), } } #[allow(unused_variables)] @@ -2955,6 +3015,66 @@ macro_rules! define_clock_tree_types { RtcSlowClkConfig::RcFast => rc_fast_div_clk_frequency(), } } + pub fn configure_ble_lp_clk(clocks: &mut ClockTree, config: BleLpClkConfig) { + let old_config = clocks.ble_lp_clk.replace(config); + refresh_ble_lp_clk_downstream(clocks); + if clocks.ble_lp_clk_refcount > 0 { + match config.sclk { + BleLpClkSclk::Xtal => request_xtal_clk(clocks), + BleLpClkSclk::RtcSlow => request_rtc_slow_clk(clocks), + } + configure_ble_lp_clk_impl(clocks, old_config, config); + if let Some(old_config) = old_config { + match old_config.sclk { + BleLpClkSclk::Xtal => release_xtal_clk(clocks), + BleLpClkSclk::RtcSlow => release_rtc_slow_clk(clocks), + } + } + } else { + configure_ble_lp_clk_impl(clocks, old_config, config); + } + } + pub fn ble_lp_clk_config(clocks: &mut ClockTree) -> Option { + clocks.ble_lp_clk + } + pub fn request_ble_lp_clk(clocks: &mut ClockTree) { + trace!("Requesting BLE_LP_CLK"); + if increment_reference_count(&mut clocks.ble_lp_clk_refcount) { + trace!("Enabling BLE_LP_CLK"); + match unwrap!(clocks.ble_lp_clk).sclk { + BleLpClkSclk::Xtal => request_xtal_clk(clocks), + BleLpClkSclk::RtcSlow => request_rtc_slow_clk(clocks), + } + enable_ble_lp_clk_impl(clocks, true); + } + } + pub fn release_ble_lp_clk(clocks: &mut ClockTree) { + trace!("Releasing BLE_LP_CLK"); + if decrement_reference_count(&mut clocks.ble_lp_clk_refcount) { + trace!("Disabling BLE_LP_CLK"); + enable_ble_lp_clk_impl(clocks, false); + match unwrap!(clocks.ble_lp_clk).sclk { + BleLpClkSclk::Xtal => release_xtal_clk(clocks), + BleLpClkSclk::RtcSlow => release_rtc_slow_clk(clocks), + } + } + } + #[allow(unused_variables)] + pub fn ble_lp_clk_config_frequency(clocks: &mut ClockTree, config: BleLpClkConfig) -> u32 { + (match config.sclk { + BleLpClkSclk::Xtal => xtal_clk_frequency(), + BleLpClkSclk::RtcSlow => rtc_slow_clk_frequency(), + } / (config.divisor() + 1)) + } + pub fn ble_lp_clk_frequency() -> u32 { + BLE_LP_CLK_FREQ_CACHE.load(::core::sync::atomic::Ordering::Acquire) + } + pub fn ble_lp_clk_source_frequency(sclk: BleLpClkSclk) -> u32 { + match sclk { + BleLpClkSclk::Xtal => xtal_clk_frequency(), + BleLpClkSclk::RtcSlow => rtc_slow_clk_frequency(), + } + } pub fn configure_rtc_fast_clk(clocks: &mut ClockTree, new_selector: RtcFastClkConfig) { let old_selector = clocks.rtc_fast_clk.replace(new_selector); refresh_rtc_fast_clk_downstream(clocks); @@ -3571,6 +3691,8 @@ macro_rules! define_clock_tree_types { pub cpu_clk: Option, /// `RTC_SLOW_CLK` configuration. pub rtc_slow_clk: Option, + /// `BLE_LP_CLK` configuration. + pub ble_lp_clk: Option, /// `RTC_FAST_CLK` configuration. pub rtc_fast_clk: Option, /// `TIMG_CALIBRATION_CLOCK` configuration. @@ -3599,6 +3721,9 @@ macro_rules! define_clock_tree_types { if let Some(config) = self.rtc_slow_clk { configure_rtc_slow_clk(clocks, config); } + if let Some(config) = self.ble_lp_clk { + configure_ble_lp_clk(clocks, config); + } if let Some(config) = self.rtc_fast_clk { configure_rtc_fast_clk(clocks, config); } @@ -3626,6 +3751,7 @@ macro_rules! define_clock_tree_types { } refresh_pll_clk_downstream(clocks); refresh_syscon_pre_div_in_downstream(clocks); + refresh_ble_lp_clk_downstream(clocks); refresh_rtc_fast_clk_downstream(clocks); } fn refresh_pll_clk_downstream(clocks: &mut ClockTree) { @@ -3705,6 +3831,15 @@ macro_rules! define_clock_tree_types { ::core::sync::atomic::Ordering::Release, ); } + refresh_ble_lp_clk_downstream(clocks); + } + fn refresh_ble_lp_clk_downstream(clocks: &mut ClockTree) { + if let Some(config) = clocks.ble_lp_clk { + BLE_LP_CLK_FREQ_CACHE.store( + ble_lp_clk_config_frequency(clocks, config), + ::core::sync::atomic::Ordering::Release, + ); + } } fn refresh_rtc_fast_clk_downstream(clocks: &mut ClockTree) { if let Some(config) = clocks.rtc_fast_clk { diff --git a/esp-metadata-generated/src/_generated_esp32c2.rs b/esp-metadata-generated/src/_generated_esp32c2.rs index c1ff2494c94..8d06d183e21 100644 --- a/esp-metadata-generated/src/_generated_esp32c2.rs +++ b/esp-metadata-generated/src/_generated_esp32c2.rs @@ -422,6 +422,13 @@ macro_rules! property { ::soc::clocks::RtcSlowClkConfig::RcSlow, crate ::soc::clocks::RtcSlowClkConfig::RcFast] }; + ("clock_tree.ble_lp_clk.sclk") => { + [crate ::soc::clocks::BleLpClkSclk::Xtal, crate + ::soc::clocks::BleLpClkSclk::RcSlow, crate ::soc::clocks::BleLpClkSclk::OscSlow] + }; + ("clock_tree.ble_lp_clk.divisor") => { + (0, 255) + }; ("clock_tree.rtc_fast_clk") => { [crate ::soc::clocks::RtcFastClkConfig::Xtal, crate ::soc::clocks::RtcFastClkConfig::Rc] @@ -971,6 +978,26 @@ macro_rules! for_each_sw_interrupt { /// todo!() /// } /// +/// // BLE_LP_XTAL_CLK +/// +/// fn enable_ble_lp_xtal_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// // BLE_LP_CLK +/// +/// fn enable_ble_lp_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_ble_lp_clk_impl( +/// _clocks: &mut ClockTree, +/// _old_config: Option, +/// _new_config: BleLpClkConfig, +/// ) { +/// todo!() +/// } +/// /// // RTC_FAST_CLK /// /// fn enable_rtc_fast_clk_impl(_clocks: &mut ClockTree, _en: bool) { @@ -1191,7 +1218,7 @@ macro_rules! define_clock_tree_types { ); Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -1226,7 +1253,7 @@ macro_rules! define_clock_tree_types { pub const fn new(divisor: CpuPllDivDivisor) -> Self { Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -1290,7 +1317,7 @@ macro_rules! define_clock_tree_types { ); Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -1305,6 +1332,47 @@ macro_rules! define_clock_tree_types { /// Selects `RC_FAST_DIV_CLK`. RcFast, } + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum BleLpClkSclk { + #[default] + /// Selects `BLE_LP_XTAL_CLK`. + Xtal, + /// Selects `RC_SLOW_CLK`. + RcSlow, + /// Selects `OSC_SLOW_CLK`. + OscSlow, + } + /// Configures the `BLE_LP_CLK` clock node. + /// + /// The output is calculated as `OUTPUT = sclk / (divisor + 1)`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct BleLpClkConfig { + sclk: BleLpClkSclk, + divisor: u32, + } + impl BleLpClkConfig { + /// Creates a new configuration for the BLE_LP_CLK clock node. + /// + /// ## Panics + /// + /// Panics if the divisor value is outside the + /// valid range (0 ..= 255). + pub const fn new(sclk: BleLpClkSclk, divisor: u32) -> Self { + ::core::assert!( + divisor <= 255, + "`BLE_LP_CLK` divisor must be between 0 and 255 (inclusive)." + ); + Self { sclk, divisor } + } + pub fn sclk(self) -> BleLpClkSclk { + self.sclk + } + pub fn divisor(self) -> u32 { + self.divisor as u32 + } + } /// The list of clock signals that the `RTC_FAST_CLK` multiplexer can output. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -1378,10 +1446,10 @@ macro_rules! define_clock_tree_types { ); Self { sclk, div_num } } - pub(crate) fn sclk(self) -> I2cFunctionClockSclk { + pub fn sclk(self) -> I2cFunctionClockSclk { self.sclk } - pub(crate) fn div_num(self) -> u32 { + pub fn div_num(self) -> u32 { self.div_num as u32 } } @@ -1449,10 +1517,10 @@ macro_rules! define_clock_tree_types { ); Self { sclk, div_num } } - pub(crate) fn sclk(self) -> UartFunctionClockSclk { + pub fn sclk(self) -> UartFunctionClockSclk { self.sclk } - pub(crate) fn div_num(self) -> u32 { + pub fn div_num(self) -> u32 { self.div_num as u32 } } @@ -1490,10 +1558,10 @@ macro_rules! define_clock_tree_types { integral, } } - pub(crate) fn fractional(self) -> u32 { + pub fn fractional(self) -> u32 { self.fractional as u32 } - pub(crate) fn integral(self) -> u32 { + pub fn integral(self) -> u32 { self.integral as u32 } } @@ -1509,6 +1577,7 @@ macro_rules! define_clock_tree_types { cpu_clk: Option, rc_fast_clk_div_n: Option, rtc_slow_clk: Option, + ble_lp_clk: Option, rtc_fast_clk: Option, low_power_clk: Option, timg_calibration_clock: Option, @@ -1527,6 +1596,7 @@ macro_rules! define_clock_tree_types { mspi_clk_refcount: u32, pll_40m_refcount: u32, pll_60m_refcount: u32, + ble_lp_clk_refcount: u32, rtc_fast_clk_refcount: u32, low_power_clk_refcount: u32, uart_mem_clk_refcount: u32, @@ -1584,6 +1654,10 @@ macro_rules! define_clock_tree_types { pub fn rtc_slow_clk(&self) -> Option { self.rtc_slow_clk } + /// Returns the current configuration of the BLE_LP_CLK clock tree node + pub fn ble_lp_clk(&self) -> Option { + self.ble_lp_clk + } /// Returns the current configuration of the RTC_FAST_CLK clock tree node pub fn rtc_fast_clk(&self) -> Option { self.rtc_fast_clk @@ -1641,6 +1715,7 @@ macro_rules! define_clock_tree_types { cpu_clk: None, rc_fast_clk_div_n: None, rtc_slow_clk: None, + ble_lp_clk: None, rtc_fast_clk: None, low_power_clk: None, timg_calibration_clock: None, @@ -1659,6 +1734,7 @@ macro_rules! define_clock_tree_types { mspi_clk_refcount: 0, pll_40m_refcount: 0, pll_60m_refcount: 0, + ble_lp_clk_refcount: 0, rtc_fast_clk_refcount: 0, low_power_clk_refcount: 0, uart_mem_clk_refcount: 0, @@ -1685,6 +1761,8 @@ macro_rules! define_clock_tree_types { ::core::sync::atomic::AtomicU32::new(0); static RTC_SLOW_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = ::core::sync::atomic::AtomicU32::new(0); + static BLE_LP_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = + ::core::sync::atomic::AtomicU32::new(0); static RTC_FAST_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = ::core::sync::atomic::AtomicU32::new(0); static LOW_POWER_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = @@ -2365,6 +2443,90 @@ macro_rules! define_clock_tree_types { RtcSlowClkConfig::RcFast => rc_fast_div_clk_frequency(), } } + pub fn request_ble_lp_xtal_clk(clocks: &mut ClockTree) { + trace!("Requesting BLE_LP_XTAL_CLK"); + trace!("Enabling BLE_LP_XTAL_CLK"); + request_xtal_clk(clocks); + enable_ble_lp_xtal_clk_impl(clocks, true); + } + pub fn release_ble_lp_xtal_clk(clocks: &mut ClockTree) { + trace!("Releasing BLE_LP_XTAL_CLK"); + trace!("Disabling BLE_LP_XTAL_CLK"); + enable_ble_lp_xtal_clk_impl(clocks, false); + release_xtal_clk(clocks); + } + pub fn ble_lp_xtal_clk_frequency() -> u32 { + (xtal_clk_frequency() / 5) + } + pub fn ble_lp_xtal_clk_source_frequency() -> u32 { + xtal_clk_frequency() + } + pub fn configure_ble_lp_clk(clocks: &mut ClockTree, config: BleLpClkConfig) { + let old_config = clocks.ble_lp_clk.replace(config); + refresh_ble_lp_clk_downstream(clocks); + if clocks.ble_lp_clk_refcount > 0 { + match config.sclk { + BleLpClkSclk::Xtal => request_ble_lp_xtal_clk(clocks), + BleLpClkSclk::RcSlow => request_rc_slow_clk(clocks), + BleLpClkSclk::OscSlow => request_osc_slow_clk(clocks), + } + configure_ble_lp_clk_impl(clocks, old_config, config); + if let Some(old_config) = old_config { + match old_config.sclk { + BleLpClkSclk::Xtal => release_ble_lp_xtal_clk(clocks), + BleLpClkSclk::RcSlow => release_rc_slow_clk(clocks), + BleLpClkSclk::OscSlow => release_osc_slow_clk(clocks), + } + } + } else { + configure_ble_lp_clk_impl(clocks, old_config, config); + } + } + pub fn ble_lp_clk_config(clocks: &mut ClockTree) -> Option { + clocks.ble_lp_clk + } + pub fn request_ble_lp_clk(clocks: &mut ClockTree) { + trace!("Requesting BLE_LP_CLK"); + if increment_reference_count(&mut clocks.ble_lp_clk_refcount) { + trace!("Enabling BLE_LP_CLK"); + match unwrap!(clocks.ble_lp_clk).sclk { + BleLpClkSclk::Xtal => request_ble_lp_xtal_clk(clocks), + BleLpClkSclk::RcSlow => request_rc_slow_clk(clocks), + BleLpClkSclk::OscSlow => request_osc_slow_clk(clocks), + } + enable_ble_lp_clk_impl(clocks, true); + } + } + pub fn release_ble_lp_clk(clocks: &mut ClockTree) { + trace!("Releasing BLE_LP_CLK"); + if decrement_reference_count(&mut clocks.ble_lp_clk_refcount) { + trace!("Disabling BLE_LP_CLK"); + enable_ble_lp_clk_impl(clocks, false); + match unwrap!(clocks.ble_lp_clk).sclk { + BleLpClkSclk::Xtal => release_ble_lp_xtal_clk(clocks), + BleLpClkSclk::RcSlow => release_rc_slow_clk(clocks), + BleLpClkSclk::OscSlow => release_osc_slow_clk(clocks), + } + } + } + #[allow(unused_variables)] + pub fn ble_lp_clk_config_frequency(clocks: &mut ClockTree, config: BleLpClkConfig) -> u32 { + (match config.sclk { + BleLpClkSclk::Xtal => ble_lp_xtal_clk_frequency(), + BleLpClkSclk::RcSlow => rc_slow_clk_frequency(), + BleLpClkSclk::OscSlow => osc_slow_clk_frequency(), + } / (config.divisor() + 1)) + } + pub fn ble_lp_clk_frequency() -> u32 { + BLE_LP_CLK_FREQ_CACHE.load(::core::sync::atomic::Ordering::Acquire) + } + pub fn ble_lp_clk_source_frequency(sclk: BleLpClkSclk) -> u32 { + match sclk { + BleLpClkSclk::Xtal => ble_lp_xtal_clk_frequency(), + BleLpClkSclk::RcSlow => rc_slow_clk_frequency(), + BleLpClkSclk::OscSlow => osc_slow_clk_frequency(), + } + } pub fn configure_rtc_fast_clk(clocks: &mut ClockTree, new_selector: RtcFastClkConfig) { let old_selector = clocks.rtc_fast_clk.replace(new_selector); refresh_rtc_fast_clk_downstream(clocks); @@ -3078,6 +3240,8 @@ macro_rules! define_clock_tree_types { pub rc_fast_clk_div_n: Option, /// `RTC_SLOW_CLK` configuration. pub rtc_slow_clk: Option, + /// `BLE_LP_CLK` configuration. + pub ble_lp_clk: Option, /// `RTC_FAST_CLK` configuration. pub rtc_fast_clk: Option, /// `LOW_POWER_CLK` configuration. @@ -3105,6 +3269,9 @@ macro_rules! define_clock_tree_types { if let Some(config) = self.rtc_slow_clk { configure_rtc_slow_clk(clocks, config); } + if let Some(config) = self.ble_lp_clk { + configure_ble_lp_clk(clocks, config); + } if let Some(config) = self.rtc_fast_clk { configure_rtc_fast_clk(clocks, config); } @@ -3135,6 +3302,7 @@ macro_rules! define_clock_tree_types { } refresh_system_pre_div_in_downstream(clocks); refresh_cpu_pll_div_downstream(clocks); + refresh_ble_lp_clk_downstream(clocks); refresh_rtc_fast_clk_downstream(clocks); refresh_low_power_clk_downstream(clocks); for child_instance in [I2cInstance::I2c0] { @@ -3217,6 +3385,14 @@ macro_rules! define_clock_tree_types { } refresh_low_power_clk_downstream(clocks); } + fn refresh_ble_lp_clk_downstream(clocks: &mut ClockTree) { + if let Some(config) = clocks.ble_lp_clk { + BLE_LP_CLK_FREQ_CACHE.store( + ble_lp_clk_config_frequency(clocks, config), + ::core::sync::atomic::Ordering::Release, + ); + } + } fn refresh_rtc_fast_clk_downstream(clocks: &mut ClockTree) { if let Some(config) = clocks.rtc_fast_clk { RTC_FAST_CLK_FREQ_CACHE.store( diff --git a/esp-metadata-generated/src/_generated_esp32c3.rs b/esp-metadata-generated/src/_generated_esp32c3.rs index cbf5fc6f851..ef4a1467eee 100644 --- a/esp-metadata-generated/src/_generated_esp32c3.rs +++ b/esp-metadata-generated/src/_generated_esp32c3.rs @@ -514,6 +514,13 @@ macro_rules! property { ::soc::clocks::RtcSlowClkConfig::RcSlow, crate ::soc::clocks::RtcSlowClkConfig::RcFast] }; + ("clock_tree.ble_lp_clk.sclk") => { + [crate ::soc::clocks::BleLpClkSclk::Xtal, crate + ::soc::clocks::BleLpClkSclk::RtcSlow] + }; + ("clock_tree.ble_lp_clk.divisor") => { + (0, 255) + }; ("clock_tree.rtc_fast_clk") => { [crate ::soc::clocks::RtcFastClkConfig::Xtal, crate ::soc::clocks::RtcFastClkConfig::Rc] @@ -1358,6 +1365,20 @@ macro_rules! for_each_sw_interrupt { /// todo!() /// } /// +/// // BLE_LP_CLK +/// +/// fn enable_ble_lp_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_ble_lp_clk_impl( +/// _clocks: &mut ClockTree, +/// _old_config: Option, +/// _new_config: BleLpClkConfig, +/// ) { +/// todo!() +/// } +/// /// // RTC_FAST_CLK /// /// fn enable_rtc_fast_clk_impl(_clocks: &mut ClockTree, _en: bool) { @@ -1677,7 +1698,7 @@ macro_rules! define_clock_tree_types { ); Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -1749,7 +1770,7 @@ macro_rules! define_clock_tree_types { ); Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -1765,6 +1786,45 @@ macro_rules! define_clock_tree_types { /// Selects `RC_FAST_DIV_CLK`. RcFast, } + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum BleLpClkSclk { + #[default] + /// Selects `XTAL_CLK`. + Xtal, + /// Selects `RTC_SLOW_CLK`. + RtcSlow, + } + /// Configures the `BLE_LP_CLK` clock node. + /// + /// The output is calculated as `OUTPUT = sclk / (divisor + 1)`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct BleLpClkConfig { + sclk: BleLpClkSclk, + divisor: u32, + } + impl BleLpClkConfig { + /// Creates a new configuration for the BLE_LP_CLK clock node. + /// + /// ## Panics + /// + /// Panics if the divisor value is outside the + /// valid range (0 ..= 255). + pub const fn new(sclk: BleLpClkSclk, divisor: u32) -> Self { + ::core::assert!( + divisor <= 255, + "`BLE_LP_CLK` divisor must be between 0 and 255 (inclusive)." + ); + Self { sclk, divisor } + } + pub fn sclk(self) -> BleLpClkSclk { + self.sclk + } + pub fn divisor(self) -> u32 { + self.divisor as u32 + } + } /// The list of clock signals that the `RTC_FAST_CLK` multiplexer can output. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -1840,10 +1900,10 @@ macro_rules! define_clock_tree_types { ); Self { sclk, div_num } } - pub(crate) fn sclk(self) -> I2cFunctionClockSclk { + pub fn sclk(self) -> I2cFunctionClockSclk { self.sclk } - pub(crate) fn div_num(self) -> u32 { + pub fn div_num(self) -> u32 { self.div_num as u32 } } @@ -1900,16 +1960,16 @@ macro_rules! define_clock_tree_types { div_b, } } - pub(crate) fn sclk(self) -> I2sClkSclk { + pub fn sclk(self) -> I2sClkSclk { self.sclk } - pub(crate) fn div_num(self) -> u32 { + pub fn div_num(self) -> u32 { self.div_num as u32 } - pub(crate) fn div_a(self) -> u32 { + pub fn div_a(self) -> u32 { self.div_a as u32 } - pub(crate) fn div_b(self) -> u32 { + pub fn div_b(self) -> u32 { self.div_b as u32 } } @@ -1999,10 +2059,10 @@ macro_rules! define_clock_tree_types { ); Self { sclk, div_num } } - pub(crate) fn sclk(self) -> UartFunctionClockSclk { + pub fn sclk(self) -> UartFunctionClockSclk { self.sclk } - pub(crate) fn div_num(self) -> u32 { + pub fn div_num(self) -> u32 { self.div_num as u32 } } @@ -2040,10 +2100,10 @@ macro_rules! define_clock_tree_types { integral, } } - pub(crate) fn fractional(self) -> u32 { + pub fn fractional(self) -> u32 { self.fractional as u32 } - pub(crate) fn integral(self) -> u32 { + pub fn integral(self) -> u32 { self.integral as u32 } } @@ -2059,6 +2119,7 @@ macro_rules! define_clock_tree_types { cpu_clk: Option, rc_fast_clk_div_n: Option, rtc_slow_clk: Option, + ble_lp_clk: Option, rtc_fast_clk: Option, low_power_clk: Option, timg_calibration_clock: Option, @@ -2081,6 +2142,8 @@ macro_rules! define_clock_tree_types { crypto_clk_refcount: u32, pll_80m_refcount: u32, pll_160m_refcount: u32, + rtc_slow_clk_refcount: u32, + ble_lp_clk_refcount: u32, rtc_fast_clk_refcount: u32, low_power_clk_refcount: u32, uart_mem_clk_refcount: u32, @@ -2143,6 +2206,10 @@ macro_rules! define_clock_tree_types { pub fn rtc_slow_clk(&self) -> Option { self.rtc_slow_clk } + /// Returns the current configuration of the BLE_LP_CLK clock tree node + pub fn ble_lp_clk(&self) -> Option { + self.ble_lp_clk + } /// Returns the current configuration of the RTC_FAST_CLK clock tree node pub fn rtc_fast_clk(&self) -> Option { self.rtc_fast_clk @@ -2224,6 +2291,7 @@ macro_rules! define_clock_tree_types { cpu_clk: None, rc_fast_clk_div_n: None, rtc_slow_clk: None, + ble_lp_clk: None, rtc_fast_clk: None, low_power_clk: None, timg_calibration_clock: None, @@ -2246,6 +2314,8 @@ macro_rules! define_clock_tree_types { crypto_clk_refcount: 0, pll_80m_refcount: 0, pll_160m_refcount: 0, + rtc_slow_clk_refcount: 0, + ble_lp_clk_refcount: 0, rtc_fast_clk_refcount: 0, low_power_clk_refcount: 0, uart_mem_clk_refcount: 0, @@ -2279,6 +2349,8 @@ macro_rules! define_clock_tree_types { ::core::sync::atomic::AtomicU32::new(0); static RTC_SLOW_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = ::core::sync::atomic::AtomicU32::new(0); + static BLE_LP_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = + ::core::sync::atomic::AtomicU32::new(0); static RTC_FAST_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = ::core::sync::atomic::AtomicU32::new(0); static LOW_POWER_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = @@ -2823,20 +2895,24 @@ macro_rules! define_clock_tree_types { pub fn configure_rtc_slow_clk(clocks: &mut ClockTree, new_selector: RtcSlowClkConfig) { let old_selector = clocks.rtc_slow_clk.replace(new_selector); refresh_rtc_slow_clk_downstream(clocks); - match new_selector { - #[cfg(use_xtal32k)] - RtcSlowClkConfig::Xtal32k => request_xtal32k_clk(clocks), - RtcSlowClkConfig::RcSlow => request_rc_slow_clk(clocks), - RtcSlowClkConfig::RcFast => request_rc_fast_div_clk(clocks), - } - configure_rtc_slow_clk_impl(clocks, old_selector, new_selector); - if let Some(old_selector) = old_selector { - match old_selector { + if clocks.rtc_slow_clk_refcount > 0 { + match new_selector { #[cfg(use_xtal32k)] - RtcSlowClkConfig::Xtal32k => release_xtal32k_clk(clocks), - RtcSlowClkConfig::RcSlow => release_rc_slow_clk(clocks), - RtcSlowClkConfig::RcFast => release_rc_fast_div_clk(clocks), + RtcSlowClkConfig::Xtal32k => request_xtal32k_clk(clocks), + RtcSlowClkConfig::RcSlow => request_rc_slow_clk(clocks), + RtcSlowClkConfig::RcFast => request_rc_fast_div_clk(clocks), } + configure_rtc_slow_clk_impl(clocks, old_selector, new_selector); + if let Some(old_selector) = old_selector { + match old_selector { + #[cfg(use_xtal32k)] + RtcSlowClkConfig::Xtal32k => release_xtal32k_clk(clocks), + RtcSlowClkConfig::RcSlow => release_rc_slow_clk(clocks), + RtcSlowClkConfig::RcFast => release_rc_fast_div_clk(clocks), + } + } + } else { + configure_rtc_slow_clk_impl(clocks, old_selector, new_selector); } } pub fn rtc_slow_clk_config(clocks: &mut ClockTree) -> Option { @@ -2844,24 +2920,28 @@ macro_rules! define_clock_tree_types { } pub fn request_rtc_slow_clk(clocks: &mut ClockTree) { trace!("Requesting RTC_SLOW_CLK"); - trace!("Enabling RTC_SLOW_CLK"); - match unwrap!(clocks.rtc_slow_clk) { - #[cfg(use_xtal32k)] - RtcSlowClkConfig::Xtal32k => request_xtal32k_clk(clocks), - RtcSlowClkConfig::RcSlow => request_rc_slow_clk(clocks), - RtcSlowClkConfig::RcFast => request_rc_fast_div_clk(clocks), + if increment_reference_count(&mut clocks.rtc_slow_clk_refcount) { + trace!("Enabling RTC_SLOW_CLK"); + match unwrap!(clocks.rtc_slow_clk) { + #[cfg(use_xtal32k)] + RtcSlowClkConfig::Xtal32k => request_xtal32k_clk(clocks), + RtcSlowClkConfig::RcSlow => request_rc_slow_clk(clocks), + RtcSlowClkConfig::RcFast => request_rc_fast_div_clk(clocks), + } + enable_rtc_slow_clk_impl(clocks, true); } - enable_rtc_slow_clk_impl(clocks, true); } pub fn release_rtc_slow_clk(clocks: &mut ClockTree) { trace!("Releasing RTC_SLOW_CLK"); - trace!("Disabling RTC_SLOW_CLK"); - enable_rtc_slow_clk_impl(clocks, false); - match unwrap!(clocks.rtc_slow_clk) { - #[cfg(use_xtal32k)] - RtcSlowClkConfig::Xtal32k => release_xtal32k_clk(clocks), - RtcSlowClkConfig::RcSlow => release_rc_slow_clk(clocks), - RtcSlowClkConfig::RcFast => release_rc_fast_div_clk(clocks), + if decrement_reference_count(&mut clocks.rtc_slow_clk_refcount) { + trace!("Disabling RTC_SLOW_CLK"); + enable_rtc_slow_clk_impl(clocks, false); + match unwrap!(clocks.rtc_slow_clk) { + #[cfg(use_xtal32k)] + RtcSlowClkConfig::Xtal32k => release_xtal32k_clk(clocks), + RtcSlowClkConfig::RcSlow => release_rc_slow_clk(clocks), + RtcSlowClkConfig::RcFast => release_rc_fast_div_clk(clocks), + } } } #[allow(unused_variables)] @@ -2887,6 +2967,66 @@ macro_rules! define_clock_tree_types { RtcSlowClkConfig::RcFast => rc_fast_div_clk_frequency(), } } + pub fn configure_ble_lp_clk(clocks: &mut ClockTree, config: BleLpClkConfig) { + let old_config = clocks.ble_lp_clk.replace(config); + refresh_ble_lp_clk_downstream(clocks); + if clocks.ble_lp_clk_refcount > 0 { + match config.sclk { + BleLpClkSclk::Xtal => request_xtal_clk(clocks), + BleLpClkSclk::RtcSlow => request_rtc_slow_clk(clocks), + } + configure_ble_lp_clk_impl(clocks, old_config, config); + if let Some(old_config) = old_config { + match old_config.sclk { + BleLpClkSclk::Xtal => release_xtal_clk(clocks), + BleLpClkSclk::RtcSlow => release_rtc_slow_clk(clocks), + } + } + } else { + configure_ble_lp_clk_impl(clocks, old_config, config); + } + } + pub fn ble_lp_clk_config(clocks: &mut ClockTree) -> Option { + clocks.ble_lp_clk + } + pub fn request_ble_lp_clk(clocks: &mut ClockTree) { + trace!("Requesting BLE_LP_CLK"); + if increment_reference_count(&mut clocks.ble_lp_clk_refcount) { + trace!("Enabling BLE_LP_CLK"); + match unwrap!(clocks.ble_lp_clk).sclk { + BleLpClkSclk::Xtal => request_xtal_clk(clocks), + BleLpClkSclk::RtcSlow => request_rtc_slow_clk(clocks), + } + enable_ble_lp_clk_impl(clocks, true); + } + } + pub fn release_ble_lp_clk(clocks: &mut ClockTree) { + trace!("Releasing BLE_LP_CLK"); + if decrement_reference_count(&mut clocks.ble_lp_clk_refcount) { + trace!("Disabling BLE_LP_CLK"); + enable_ble_lp_clk_impl(clocks, false); + match unwrap!(clocks.ble_lp_clk).sclk { + BleLpClkSclk::Xtal => release_xtal_clk(clocks), + BleLpClkSclk::RtcSlow => release_rtc_slow_clk(clocks), + } + } + } + #[allow(unused_variables)] + pub fn ble_lp_clk_config_frequency(clocks: &mut ClockTree, config: BleLpClkConfig) -> u32 { + (match config.sclk { + BleLpClkSclk::Xtal => xtal_clk_frequency(), + BleLpClkSclk::RtcSlow => rtc_slow_clk_frequency(), + } / (config.divisor() + 1)) + } + pub fn ble_lp_clk_frequency() -> u32 { + BLE_LP_CLK_FREQ_CACHE.load(::core::sync::atomic::Ordering::Acquire) + } + pub fn ble_lp_clk_source_frequency(sclk: BleLpClkSclk) -> u32 { + match sclk { + BleLpClkSclk::Xtal => xtal_clk_frequency(), + BleLpClkSclk::RtcSlow => rtc_slow_clk_frequency(), + } + } pub fn configure_rtc_fast_clk(clocks: &mut ClockTree, new_selector: RtcFastClkConfig) { let old_selector = clocks.rtc_fast_clk.replace(new_selector); refresh_rtc_fast_clk_downstream(clocks); @@ -3904,6 +4044,8 @@ macro_rules! define_clock_tree_types { pub rc_fast_clk_div_n: Option, /// `RTC_SLOW_CLK` configuration. pub rtc_slow_clk: Option, + /// `BLE_LP_CLK` configuration. + pub ble_lp_clk: Option, /// `RTC_FAST_CLK` configuration. pub rtc_fast_clk: Option, /// `LOW_POWER_CLK` configuration. @@ -3934,6 +4076,9 @@ macro_rules! define_clock_tree_types { if let Some(config) = self.rtc_slow_clk { configure_rtc_slow_clk(clocks, config); } + if let Some(config) = self.ble_lp_clk { + configure_ble_lp_clk(clocks, config); + } if let Some(config) = self.rtc_fast_clk { configure_rtc_fast_clk(clocks, config); } @@ -3964,6 +4109,7 @@ macro_rules! define_clock_tree_types { } refresh_pll_clk_downstream(clocks); refresh_system_pre_div_in_downstream(clocks); + refresh_ble_lp_clk_downstream(clocks); refresh_rtc_fast_clk_downstream(clocks); refresh_low_power_clk_downstream(clocks); for child_instance in [I2cInstance::I2c0] { @@ -4056,8 +4202,17 @@ macro_rules! define_clock_tree_types { ::core::sync::atomic::Ordering::Release, ); } + refresh_ble_lp_clk_downstream(clocks); refresh_low_power_clk_downstream(clocks); } + fn refresh_ble_lp_clk_downstream(clocks: &mut ClockTree) { + if let Some(config) = clocks.ble_lp_clk { + BLE_LP_CLK_FREQ_CACHE.store( + ble_lp_clk_config_frequency(clocks, config), + ::core::sync::atomic::Ordering::Release, + ); + } + } fn refresh_rtc_fast_clk_downstream(clocks: &mut ClockTree) { if let Some(config) = clocks.rtc_fast_clk { RTC_FAST_CLK_FREQ_CACHE.store( diff --git a/esp-metadata-generated/src/_generated_esp32c5.rs b/esp-metadata-generated/src/_generated_esp32c5.rs index 74dc1cace3f..e791c188329 100644 --- a/esp-metadata-generated/src/_generated_esp32c5.rs +++ b/esp-metadata-generated/src/_generated_esp32c5.rs @@ -557,6 +557,15 @@ macro_rules! property { ::soc::clocks::LpSlowClkConfig::Xtal32k, crate ::soc::clocks::LpSlowClkConfig::OscSlow] }; + ("clock_tree.ble_lp_clk.sclk") => { + [crate ::soc::clocks::BleLpClkSclk::Xtal, crate + ::soc::clocks::BleLpClkSclk::RcSlow, #[cfg(use_xtal32k)] crate + ::soc::clocks::BleLpClkSclk::Xtal32k, crate ::soc::clocks::BleLpClkSclk::Rc32k, + crate ::soc::clocks::BleLpClkSclk::Ext32k] + }; + ("clock_tree.ble_lp_clk.divisor") => { + (0, 4095) + }; ("clock_tree.crypto_clk") => { [crate ::soc::clocks::CryptoClkConfig::Xtal, crate ::soc::clocks::CryptoClkConfig::Fosc, crate @@ -1366,6 +1375,12 @@ macro_rules! for_each_sw_interrupt { /// todo!() /// } /// +/// // RC32K_CLK +/// +/// fn enable_rc32k_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// /// // RC_SLOW_CLK /// /// fn enable_rc_slow_clk_impl(_clocks: &mut ClockTree, _en: bool) { @@ -1516,6 +1531,20 @@ macro_rules! for_each_sw_interrupt { /// todo!() /// } /// +/// // BLE_LP_CLK +/// +/// fn enable_ble_lp_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_ble_lp_clk_impl( +/// _clocks: &mut ClockTree, +/// _old_config: Option, +/// _new_config: BleLpClkConfig, +/// ) { +/// todo!() +/// } +/// /// // CRYPTO_PLL_DIV /// /// fn enable_crypto_pll_div_impl(_clocks: &mut ClockTree, _en: bool) { @@ -1851,7 +1880,7 @@ macro_rules! define_clock_tree_types { ); Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -1877,7 +1906,7 @@ macro_rules! define_clock_tree_types { ); Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -1903,7 +1932,7 @@ macro_rules! define_clock_tree_types { ); Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -1930,6 +1959,52 @@ macro_rules! define_clock_tree_types { /// Selects `OSC_SLOW_CLK`. OscSlow, } + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum BleLpClkSclk { + #[default] + /// Selects `XTAL_CLK`. + Xtal, + /// Selects `RC_SLOW_CLK`. + RcSlow, + #[cfg(use_xtal32k)] + /// Selects `XTAL32K_CLK`. + Xtal32k, + /// Selects `RC32K_CLK`. + Rc32k, + /// Selects `OSC_SLOW_CLK`. + Ext32k, + } + /// Configures the `BLE_LP_CLK` clock node. + /// + /// The output is calculated as `OUTPUT = sclk / (divisor + 1)`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct BleLpClkConfig { + sclk: BleLpClkSclk, + divisor: u32, + } + impl BleLpClkConfig { + /// Creates a new configuration for the BLE_LP_CLK clock node. + /// + /// ## Panics + /// + /// Panics if the divisor value is outside the + /// valid range (0 ..= 4095). + pub const fn new(sclk: BleLpClkSclk, divisor: u32) -> Self { + ::core::assert!( + divisor <= 4095, + "`BLE_LP_CLK` divisor must be between 0 and 4095 (inclusive)." + ); + Self { sclk, divisor } + } + pub fn sclk(self) -> BleLpClkSclk { + self.sclk + } + pub fn divisor(self) -> u32 { + self.divisor as u32 + } + } /// The list of clock signals that the `CRYPTO_CLK` multiplexer can output. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -1999,10 +2074,10 @@ macro_rules! define_clock_tree_types { ); Self { sclk, div_num } } - pub(crate) fn sclk(self) -> I2cFunctionClockSclk { + pub fn sclk(self) -> I2cFunctionClockSclk { self.sclk } - pub(crate) fn div_num(self) -> u32 { + pub fn div_num(self) -> u32 { self.div_num as u32 } } @@ -2061,16 +2136,16 @@ macro_rules! define_clock_tree_types { div_b, } } - pub(crate) fn sclk(self) -> I2sClkSclk { + pub fn sclk(self) -> I2sClkSclk { self.sclk } - pub(crate) fn div_num(self) -> u32 { + pub fn div_num(self) -> u32 { self.div_num as u32 } - pub(crate) fn div_a(self) -> u32 { + pub fn div_a(self) -> u32 { self.div_a as u32 } - pub(crate) fn div_b(self) -> u32 { + pub fn div_b(self) -> u32 { self.div_b as u32 } } @@ -2180,10 +2255,10 @@ macro_rules! define_clock_tree_types { ); Self { sclk, div_num } } - pub(crate) fn sclk(self) -> UartFunctionClockSclk { + pub fn sclk(self) -> UartFunctionClockSclk { self.sclk } - pub(crate) fn div_num(self) -> u32 { + pub fn div_num(self) -> u32 { self.div_num as u32 } } @@ -2221,10 +2296,10 @@ macro_rules! define_clock_tree_types { integral, } } - pub(crate) fn fractional(self) -> u32 { + pub fn fractional(self) -> u32 { self.fractional as u32 } - pub(crate) fn integral(self) -> u32 { + pub fn integral(self) -> u32 { self.integral as u32 } } @@ -2237,6 +2312,7 @@ macro_rules! define_clock_tree_types { apb_clk: Option, lp_fast_clk: Option, lp_slow_clk: Option, + ble_lp_clk: Option, crypto_clk: Option, iomux_function_clock: Option, timg_calibration_clock: Option, @@ -2271,6 +2347,7 @@ macro_rules! define_clock_tree_types { apb_clk_refcount: u32, lp_fast_clk_refcount: u32, lp_slow_clk_refcount: u32, + ble_lp_clk_refcount: u32, crypto_clk_refcount: u32, timg_calibration_clock_refcount: u32, sdm_function_clock_refcount: [u32; 1], @@ -2320,6 +2397,10 @@ macro_rules! define_clock_tree_types { pub fn lp_slow_clk(&self) -> Option { self.lp_slow_clk } + /// Returns the current configuration of the BLE_LP_CLK clock tree node + pub fn ble_lp_clk(&self) -> Option { + self.ble_lp_clk + } /// Returns the current configuration of the CRYPTO_CLK clock tree node pub fn crypto_clk(&self) -> Option { self.crypto_clk @@ -2406,6 +2487,7 @@ macro_rules! define_clock_tree_types { apb_clk: None, lp_fast_clk: None, lp_slow_clk: None, + ble_lp_clk: None, crypto_clk: None, iomux_function_clock: None, timg_calibration_clock: None, @@ -2440,6 +2522,7 @@ macro_rules! define_clock_tree_types { apb_clk_refcount: 0, lp_fast_clk_refcount: 0, lp_slow_clk_refcount: 0, + ble_lp_clk_refcount: 0, crypto_clk_refcount: 0, timg_calibration_clock_refcount: 0, sdm_function_clock_refcount: [0; 1], @@ -2470,6 +2553,8 @@ macro_rules! define_clock_tree_types { ::core::sync::atomic::AtomicU32::new(0); static LP_SLOW_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = ::core::sync::atomic::AtomicU32::new(0); + static BLE_LP_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = + ::core::sync::atomic::AtomicU32::new(0); static CRYPTO_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = ::core::sync::atomic::AtomicU32::new(0); static IOMUX_FUNCTION_CLOCK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = @@ -2593,6 +2678,19 @@ macro_rules! define_clock_tree_types { pub fn osc_slow_clk_frequency() -> u32 { 32768 } + pub fn request_rc32k_clk(clocks: &mut ClockTree) { + trace!("Requesting RC32K_CLK"); + trace!("Enabling RC32K_CLK"); + enable_rc32k_clk_impl(clocks, true); + } + pub fn release_rc32k_clk(clocks: &mut ClockTree) { + trace!("Releasing RC32K_CLK"); + trace!("Disabling RC32K_CLK"); + enable_rc32k_clk_impl(clocks, false); + } + pub fn rc32k_clk_frequency() -> u32 { + 32000 + } pub fn request_rc_slow_clk(clocks: &mut ClockTree) { trace!("Requesting RC_SLOW_CLK"); if increment_reference_count(&mut clocks.rc_slow_clk_refcount) { @@ -3139,6 +3237,90 @@ macro_rules! define_clock_tree_types { LpSlowClkConfig::OscSlow => osc_slow_clk_frequency(), } } + pub fn configure_ble_lp_clk(clocks: &mut ClockTree, config: BleLpClkConfig) { + let old_config = clocks.ble_lp_clk.replace(config); + refresh_ble_lp_clk_downstream(clocks); + if clocks.ble_lp_clk_refcount > 0 { + match config.sclk { + BleLpClkSclk::Xtal => request_xtal_clk(clocks), + BleLpClkSclk::RcSlow => request_rc_slow_clk(clocks), + #[cfg(use_xtal32k)] + BleLpClkSclk::Xtal32k => request_xtal32k_clk(clocks), + BleLpClkSclk::Rc32k => request_rc32k_clk(clocks), + BleLpClkSclk::Ext32k => request_osc_slow_clk(clocks), + } + configure_ble_lp_clk_impl(clocks, old_config, config); + if let Some(old_config) = old_config { + match old_config.sclk { + BleLpClkSclk::Xtal => release_xtal_clk(clocks), + BleLpClkSclk::RcSlow => release_rc_slow_clk(clocks), + #[cfg(use_xtal32k)] + BleLpClkSclk::Xtal32k => release_xtal32k_clk(clocks), + BleLpClkSclk::Rc32k => release_rc32k_clk(clocks), + BleLpClkSclk::Ext32k => release_osc_slow_clk(clocks), + } + } + } else { + configure_ble_lp_clk_impl(clocks, old_config, config); + } + } + pub fn ble_lp_clk_config(clocks: &mut ClockTree) -> Option { + clocks.ble_lp_clk + } + pub fn request_ble_lp_clk(clocks: &mut ClockTree) { + trace!("Requesting BLE_LP_CLK"); + if increment_reference_count(&mut clocks.ble_lp_clk_refcount) { + trace!("Enabling BLE_LP_CLK"); + match unwrap!(clocks.ble_lp_clk).sclk { + BleLpClkSclk::Xtal => request_xtal_clk(clocks), + BleLpClkSclk::RcSlow => request_rc_slow_clk(clocks), + #[cfg(use_xtal32k)] + BleLpClkSclk::Xtal32k => request_xtal32k_clk(clocks), + BleLpClkSclk::Rc32k => request_rc32k_clk(clocks), + BleLpClkSclk::Ext32k => request_osc_slow_clk(clocks), + } + enable_ble_lp_clk_impl(clocks, true); + } + } + pub fn release_ble_lp_clk(clocks: &mut ClockTree) { + trace!("Releasing BLE_LP_CLK"); + if decrement_reference_count(&mut clocks.ble_lp_clk_refcount) { + trace!("Disabling BLE_LP_CLK"); + enable_ble_lp_clk_impl(clocks, false); + match unwrap!(clocks.ble_lp_clk).sclk { + BleLpClkSclk::Xtal => release_xtal_clk(clocks), + BleLpClkSclk::RcSlow => release_rc_slow_clk(clocks), + #[cfg(use_xtal32k)] + BleLpClkSclk::Xtal32k => release_xtal32k_clk(clocks), + BleLpClkSclk::Rc32k => release_rc32k_clk(clocks), + BleLpClkSclk::Ext32k => release_osc_slow_clk(clocks), + } + } + } + #[allow(unused_variables)] + pub fn ble_lp_clk_config_frequency(clocks: &mut ClockTree, config: BleLpClkConfig) -> u32 { + (match config.sclk { + BleLpClkSclk::Xtal => xtal_clk_frequency(), + BleLpClkSclk::RcSlow => rc_slow_clk_frequency(), + #[cfg(use_xtal32k)] + BleLpClkSclk::Xtal32k => xtal32k_clk_frequency(), + BleLpClkSclk::Rc32k => rc32k_clk_frequency(), + BleLpClkSclk::Ext32k => osc_slow_clk_frequency(), + } / (config.divisor() + 1)) + } + pub fn ble_lp_clk_frequency() -> u32 { + BLE_LP_CLK_FREQ_CACHE.load(::core::sync::atomic::Ordering::Acquire) + } + pub fn ble_lp_clk_source_frequency(sclk: BleLpClkSclk) -> u32 { + match sclk { + BleLpClkSclk::Xtal => xtal_clk_frequency(), + BleLpClkSclk::RcSlow => rc_slow_clk_frequency(), + #[cfg(use_xtal32k)] + BleLpClkSclk::Xtal32k => xtal32k_clk_frequency(), + BleLpClkSclk::Rc32k => rc32k_clk_frequency(), + BleLpClkSclk::Ext32k => osc_slow_clk_frequency(), + } + } pub fn request_crypto_pll_div(clocks: &mut ClockTree) { trace!("Requesting CRYPTO_PLL_DIV"); trace!("Enabling CRYPTO_PLL_DIV"); @@ -4276,6 +4458,8 @@ macro_rules! define_clock_tree_types { pub lp_fast_clk: Option, /// `LP_SLOW_CLK` configuration. pub lp_slow_clk: Option, + /// `BLE_LP_CLK` configuration. + pub ble_lp_clk: Option, /// `CRYPTO_CLK` configuration. pub crypto_clk: Option, /// `IOMUX_FUNCTION_CLOCK` configuration. @@ -4306,6 +4490,9 @@ macro_rules! define_clock_tree_types { if let Some(config) = self.lp_slow_clk { configure_lp_slow_clk(clocks, config); } + if let Some(config) = self.ble_lp_clk { + configure_ble_lp_clk(clocks, config); + } if let Some(config) = self.crypto_clk { configure_crypto_clk(clocks, config); } @@ -4336,6 +4523,7 @@ macro_rules! define_clock_tree_types { } refresh_hp_root_clk_downstream(clocks); refresh_lp_fast_clk_downstream(clocks); + refresh_ble_lp_clk_downstream(clocks); refresh_crypto_clk_downstream(clocks); refresh_iomux_function_clock_downstream(clocks); for child_instance in [I2cInstance::I2c0] { @@ -4414,6 +4602,14 @@ macro_rules! define_clock_tree_types { ); } } + fn refresh_ble_lp_clk_downstream(clocks: &mut ClockTree) { + if let Some(config) = clocks.ble_lp_clk { + BLE_LP_CLK_FREQ_CACHE.store( + ble_lp_clk_config_frequency(clocks, config), + ::core::sync::atomic::Ordering::Release, + ); + } + } fn refresh_crypto_clk_downstream(clocks: &mut ClockTree) { if let Some(config) = clocks.crypto_clk { CRYPTO_CLK_FREQ_CACHE.store( diff --git a/esp-metadata-generated/src/_generated_esp32c6.rs b/esp-metadata-generated/src/_generated_esp32c6.rs index 6d58a1921e7..2447ac1c6e2 100644 --- a/esp-metadata-generated/src/_generated_esp32c6.rs +++ b/esp-metadata-generated/src/_generated_esp32c6.rs @@ -596,6 +596,18 @@ macro_rules! property { ::soc::clocks::LpSlowClkConfig::RcSlow, crate ::soc::clocks::LpSlowClkConfig::OscSlow] }; + ("clock_tree.ble_lp_xtal_clk.divisor") => { + [1, 80] + }; + ("clock_tree.ble_lp_clk.sclk") => { + [crate ::soc::clocks::BleLpClkSclk::Xtal, crate + ::soc::clocks::BleLpClkSclk::RcSlow, #[cfg(use_xtal32k)] crate + ::soc::clocks::BleLpClkSclk::Xtal32k, crate ::soc::clocks::BleLpClkSclk::Rc32k, + crate ::soc::clocks::BleLpClkSclk::Ext32k] + }; + ("clock_tree.ble_lp_clk.divisor") => { + (0, 4095) + }; ("clock_tree.timg_calibration_clock") => { [crate ::soc::clocks::TimgCalibrationClockConfig::RcSlowClk, crate ::soc::clocks::TimgCalibrationClockConfig::RcFastDivClk, #[cfg(use_xtal32k)] @@ -1354,6 +1366,12 @@ macro_rules! for_each_sw_interrupt { /// todo!() /// } /// +/// // RC32K_CLK +/// +/// fn enable_rc32k_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// /// // RC_SLOW_CLK /// /// fn enable_rc_slow_clk_impl(_clocks: &mut ClockTree, _en: bool) { @@ -1602,6 +1620,34 @@ macro_rules! for_each_sw_interrupt { /// todo!() /// } /// +/// // BLE_LP_XTAL_CLK +/// +/// fn enable_ble_lp_xtal_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_ble_lp_xtal_clk_impl( +/// _clocks: &mut ClockTree, +/// _old_config: Option, +/// _new_config: BleLpXtalClkConfig, +/// ) { +/// todo!() +/// } +/// +/// // BLE_LP_CLK +/// +/// fn enable_ble_lp_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_ble_lp_clk_impl( +/// _clocks: &mut ClockTree, +/// _old_config: Option, +/// _new_config: BleLpClkConfig, +/// ) { +/// todo!() +/// } +/// /// // TIMG_CALIBRATION_CLOCK /// /// fn enable_timg_calibration_clock_impl(_clocks: &mut ClockTree, _en: bool) { @@ -1921,7 +1967,7 @@ macro_rules! define_clock_tree_types { pub const fn new(divisor: HpRootClkDivisor) -> Self { Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -1997,7 +2043,7 @@ macro_rules! define_clock_tree_types { pub const fn new(divisor: CpuHsDivDivisor) -> Self { Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -2044,7 +2090,7 @@ macro_rules! define_clock_tree_types { pub const fn new(divisor: CpuLsDivDivisor) -> Self { Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -2082,7 +2128,7 @@ macro_rules! define_clock_tree_types { pub const fn new(divisor: AhbHsDivDivisor) -> Self { Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -2129,7 +2175,7 @@ macro_rules! define_clock_tree_types { pub const fn new(divisor: AhbLsDivDivisor) -> Self { Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -2167,7 +2213,7 @@ macro_rules! define_clock_tree_types { pub const fn new(divisor: ApbClkDivisor) -> Self { Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -2205,7 +2251,7 @@ macro_rules! define_clock_tree_types { pub const fn new(divisor: MspiFastHsClkDivisor) -> Self { Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -2243,7 +2289,7 @@ macro_rules! define_clock_tree_types { pub const fn new(divisor: MspiFastLsClkDivisor) -> Self { Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -2291,6 +2337,87 @@ macro_rules! define_clock_tree_types { /// Selects `OSC_SLOW_CLK`. OscSlow, } + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum BleLpXtalClkDivisor { + /// Selects `divisor = 1`. + _1 = 1, + /// Selects `divisor = 80`. + _80 = 80, + } + impl BleLpXtalClkDivisor { + /// Creates a new parameter value from a raw number. + pub const fn new(raw: u32) -> Self { + match raw { + 1 => Self::_1, + 80 => Self::_80, + _ => ::core::panic!("Invalid BLE_LP_XTAL_CLK divisor value"), + } + } + } + /// Configures the `BLE_LP_XTAL_CLK` clock node. + /// + /// The output is calculated as `OUTPUT = XTAL_CLK / divisor`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct BleLpXtalClkConfig { + divisor: BleLpXtalClkDivisor, + } + impl BleLpXtalClkConfig { + /// Creates a new configuration for the BLE_LP_XTAL_CLK clock node. + pub const fn new(divisor: BleLpXtalClkDivisor) -> Self { + Self { divisor } + } + pub fn divisor(self) -> u32 { + self.divisor as u32 + } + } + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum BleLpClkSclk { + #[default] + /// Selects `BLE_LP_XTAL_CLK`. + Xtal, + /// Selects `RC_SLOW_CLK`. + RcSlow, + #[cfg(use_xtal32k)] + /// Selects `XTAL32K_CLK`. + Xtal32k, + /// Selects `RC32K_CLK`. + Rc32k, + /// Selects `OSC_SLOW_CLK`. + Ext32k, + } + /// Configures the `BLE_LP_CLK` clock node. + /// + /// The output is calculated as `OUTPUT = sclk / (divisor + 1)`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct BleLpClkConfig { + sclk: BleLpClkSclk, + divisor: u32, + } + impl BleLpClkConfig { + /// Creates a new configuration for the BLE_LP_CLK clock node. + /// + /// ## Panics + /// + /// Panics if the divisor value is outside the + /// valid range (0 ..= 4095). + pub const fn new(sclk: BleLpClkSclk, divisor: u32) -> Self { + ::core::assert!( + divisor <= 4095, + "`BLE_LP_CLK` divisor must be between 0 and 4095 (inclusive)." + ); + Self { sclk, divisor } + } + pub fn sclk(self) -> BleLpClkSclk { + self.sclk + } + pub fn divisor(self) -> u32 { + self.divisor as u32 + } + } /// The list of clock signals that the `TIMG_CALIBRATION_CLOCK` multiplexer can output. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -2335,10 +2462,10 @@ macro_rules! define_clock_tree_types { ); Self { sclk, div_num } } - pub(crate) fn sclk(self) -> I2cFunctionClockSclk { + pub fn sclk(self) -> I2cFunctionClockSclk { self.sclk } - pub(crate) fn div_num(self) -> u32 { + pub fn div_num(self) -> u32 { self.div_num as u32 } } @@ -2397,16 +2524,16 @@ macro_rules! define_clock_tree_types { div_b, } } - pub(crate) fn sclk(self) -> I2sClkSclk { + pub fn sclk(self) -> I2sClkSclk { self.sclk } - pub(crate) fn div_num(self) -> u32 { + pub fn div_num(self) -> u32 { self.div_num as u32 } - pub(crate) fn div_a(self) -> u32 { + pub fn div_a(self) -> u32 { self.div_a as u32 } - pub(crate) fn div_b(self) -> u32 { + pub fn div_b(self) -> u32 { self.div_b as u32 } } @@ -2526,10 +2653,10 @@ macro_rules! define_clock_tree_types { ); Self { sclk, div_num } } - pub(crate) fn sclk(self) -> UartFunctionClockSclk { + pub fn sclk(self) -> UartFunctionClockSclk { self.sclk } - pub(crate) fn div_num(self) -> u32 { + pub fn div_num(self) -> u32 { self.div_num as u32 } } @@ -2567,10 +2694,10 @@ macro_rules! define_clock_tree_types { integral, } } - pub(crate) fn fractional(self) -> u32 { + pub fn fractional(self) -> u32 { self.fractional as u32 } - pub(crate) fn integral(self) -> u32 { + pub fn integral(self) -> u32 { self.integral as u32 } } @@ -2593,6 +2720,8 @@ macro_rules! define_clock_tree_types { ledc_sclk: Option, lp_fast_clk: Option, lp_slow_clk: Option, + ble_lp_xtal_clk: Option, + ble_lp_clk: Option, timg_calibration_clock: Option, i2c_function_clock: [Option; 1], i2s_tx_clk: [Option; 1], @@ -2611,6 +2740,8 @@ macro_rules! define_clock_tree_types { rc_fast_clk_refcount: u32, #[cfg(use_xtal32k)] xtal32k_clk_refcount: u32, + osc_slow_clk_refcount: u32, + rc_slow_clk_refcount: u32, hp_root_clk_refcount: u32, mspi_fast_clk_refcount: u32, apb_clk_refcount: u32, @@ -2620,6 +2751,7 @@ macro_rules! define_clock_tree_types { pll_f240m_refcount: u32, ledc_sclk_refcount: u32, lp_fast_clk_refcount: u32, + ble_lp_clk_refcount: u32, timg_calibration_clock_refcount: u32, sdm_function_clock_refcount: [u32; 1], i2c_function_clock_refcount: [u32; 1], @@ -2709,6 +2841,14 @@ macro_rules! define_clock_tree_types { pub fn lp_slow_clk(&self) -> Option { self.lp_slow_clk } + /// Returns the current configuration of the BLE_LP_XTAL_CLK clock tree node + pub fn ble_lp_xtal_clk(&self) -> Option { + self.ble_lp_xtal_clk + } + /// Returns the current configuration of the BLE_LP_CLK clock tree node + pub fn ble_lp_clk(&self) -> Option { + self.ble_lp_clk + } /// Returns the current configuration of the TIMG_CALIBRATION_CLOCK clock tree node pub fn timg_calibration_clock(&self) -> Option { self.timg_calibration_clock @@ -2801,6 +2941,8 @@ macro_rules! define_clock_tree_types { ledc_sclk: None, lp_fast_clk: None, lp_slow_clk: None, + ble_lp_xtal_clk: None, + ble_lp_clk: None, timg_calibration_clock: None, i2c_function_clock: [None; 1], i2s_tx_clk: [None; 1], @@ -2819,6 +2961,8 @@ macro_rules! define_clock_tree_types { rc_fast_clk_refcount: 0, #[cfg(use_xtal32k)] xtal32k_clk_refcount: 0, + osc_slow_clk_refcount: 0, + rc_slow_clk_refcount: 0, hp_root_clk_refcount: 0, mspi_fast_clk_refcount: 0, apb_clk_refcount: 0, @@ -2828,6 +2972,7 @@ macro_rules! define_clock_tree_types { pll_f240m_refcount: 0, ledc_sclk_refcount: 0, lp_fast_clk_refcount: 0, + ble_lp_clk_refcount: 0, timg_calibration_clock_refcount: 0, sdm_function_clock_refcount: [0; 1], i2c_function_clock_refcount: [0; 1], @@ -2856,6 +3001,10 @@ macro_rules! define_clock_tree_types { ::core::sync::atomic::AtomicU32::new(0); static LP_SLOW_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = ::core::sync::atomic::AtomicU32::new(0); + static BLE_LP_XTAL_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = + ::core::sync::atomic::AtomicU32::new(0); + static BLE_LP_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = + ::core::sync::atomic::AtomicU32::new(0); static TIMG_CALIBRATION_CLOCK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = ::core::sync::atomic::AtomicU32::new(0); static I2C_FUNCTION_CLOCK_FREQ_CACHE: [::core::sync::atomic::AtomicU32; 1] = @@ -2984,26 +3133,47 @@ macro_rules! define_clock_tree_types { } pub fn request_osc_slow_clk(clocks: &mut ClockTree) { trace!("Requesting OSC_SLOW_CLK"); - trace!("Enabling OSC_SLOW_CLK"); - enable_osc_slow_clk_impl(clocks, true); + if increment_reference_count(&mut clocks.osc_slow_clk_refcount) { + trace!("Enabling OSC_SLOW_CLK"); + enable_osc_slow_clk_impl(clocks, true); + } } pub fn release_osc_slow_clk(clocks: &mut ClockTree) { trace!("Releasing OSC_SLOW_CLK"); - trace!("Disabling OSC_SLOW_CLK"); - enable_osc_slow_clk_impl(clocks, false); + if decrement_reference_count(&mut clocks.osc_slow_clk_refcount) { + trace!("Disabling OSC_SLOW_CLK"); + enable_osc_slow_clk_impl(clocks, false); + } } pub fn osc_slow_clk_frequency() -> u32 { 32768 } + pub fn request_rc32k_clk(clocks: &mut ClockTree) { + trace!("Requesting RC32K_CLK"); + trace!("Enabling RC32K_CLK"); + enable_rc32k_clk_impl(clocks, true); + } + pub fn release_rc32k_clk(clocks: &mut ClockTree) { + trace!("Releasing RC32K_CLK"); + trace!("Disabling RC32K_CLK"); + enable_rc32k_clk_impl(clocks, false); + } + pub fn rc32k_clk_frequency() -> u32 { + 32000 + } pub fn request_rc_slow_clk(clocks: &mut ClockTree) { trace!("Requesting RC_SLOW_CLK"); - trace!("Enabling RC_SLOW_CLK"); - enable_rc_slow_clk_impl(clocks, true); + if increment_reference_count(&mut clocks.rc_slow_clk_refcount) { + trace!("Enabling RC_SLOW_CLK"); + enable_rc_slow_clk_impl(clocks, true); + } } pub fn release_rc_slow_clk(clocks: &mut ClockTree) { trace!("Releasing RC_SLOW_CLK"); - trace!("Disabling RC_SLOW_CLK"); - enable_rc_slow_clk_impl(clocks, false); + if decrement_reference_count(&mut clocks.rc_slow_clk_refcount) { + trace!("Disabling RC_SLOW_CLK"); + enable_rc_slow_clk_impl(clocks, false); + } } pub fn rc_slow_clk_frequency() -> u32 { 136000 @@ -3827,6 +3997,123 @@ macro_rules! define_clock_tree_types { LpSlowClkConfig::OscSlow => osc_slow_clk_frequency(), } } + pub fn configure_ble_lp_xtal_clk(clocks: &mut ClockTree, config: BleLpXtalClkConfig) { + let old_config = clocks.ble_lp_xtal_clk.replace(config); + refresh_ble_lp_xtal_clk_downstream(clocks); + configure_ble_lp_xtal_clk_impl(clocks, old_config, config); + } + pub fn ble_lp_xtal_clk_config(clocks: &mut ClockTree) -> Option { + clocks.ble_lp_xtal_clk + } + pub fn request_ble_lp_xtal_clk(clocks: &mut ClockTree) { + trace!("Requesting BLE_LP_XTAL_CLK"); + trace!("Enabling BLE_LP_XTAL_CLK"); + request_xtal_clk(clocks); + enable_ble_lp_xtal_clk_impl(clocks, true); + } + pub fn release_ble_lp_xtal_clk(clocks: &mut ClockTree) { + trace!("Releasing BLE_LP_XTAL_CLK"); + trace!("Disabling BLE_LP_XTAL_CLK"); + enable_ble_lp_xtal_clk_impl(clocks, false); + release_xtal_clk(clocks); + } + #[allow(unused_variables)] + pub fn ble_lp_xtal_clk_config_frequency( + clocks: &mut ClockTree, + config: BleLpXtalClkConfig, + ) -> u32 { + (xtal_clk_frequency() / config.divisor()) + } + pub fn ble_lp_xtal_clk_frequency() -> u32 { + BLE_LP_XTAL_CLK_FREQ_CACHE.load(::core::sync::atomic::Ordering::Acquire) + } + pub fn ble_lp_xtal_clk_source_frequency() -> u32 { + xtal_clk_frequency() + } + pub fn configure_ble_lp_clk(clocks: &mut ClockTree, config: BleLpClkConfig) { + let old_config = clocks.ble_lp_clk.replace(config); + refresh_ble_lp_clk_downstream(clocks); + if clocks.ble_lp_clk_refcount > 0 { + match config.sclk { + BleLpClkSclk::Xtal => request_ble_lp_xtal_clk(clocks), + BleLpClkSclk::RcSlow => request_rc_slow_clk(clocks), + #[cfg(use_xtal32k)] + BleLpClkSclk::Xtal32k => request_xtal32k_clk(clocks), + BleLpClkSclk::Rc32k => request_rc32k_clk(clocks), + BleLpClkSclk::Ext32k => request_osc_slow_clk(clocks), + } + configure_ble_lp_clk_impl(clocks, old_config, config); + if let Some(old_config) = old_config { + match old_config.sclk { + BleLpClkSclk::Xtal => release_ble_lp_xtal_clk(clocks), + BleLpClkSclk::RcSlow => release_rc_slow_clk(clocks), + #[cfg(use_xtal32k)] + BleLpClkSclk::Xtal32k => release_xtal32k_clk(clocks), + BleLpClkSclk::Rc32k => release_rc32k_clk(clocks), + BleLpClkSclk::Ext32k => release_osc_slow_clk(clocks), + } + } + } else { + configure_ble_lp_clk_impl(clocks, old_config, config); + } + } + pub fn ble_lp_clk_config(clocks: &mut ClockTree) -> Option { + clocks.ble_lp_clk + } + pub fn request_ble_lp_clk(clocks: &mut ClockTree) { + trace!("Requesting BLE_LP_CLK"); + if increment_reference_count(&mut clocks.ble_lp_clk_refcount) { + trace!("Enabling BLE_LP_CLK"); + match unwrap!(clocks.ble_lp_clk).sclk { + BleLpClkSclk::Xtal => request_ble_lp_xtal_clk(clocks), + BleLpClkSclk::RcSlow => request_rc_slow_clk(clocks), + #[cfg(use_xtal32k)] + BleLpClkSclk::Xtal32k => request_xtal32k_clk(clocks), + BleLpClkSclk::Rc32k => request_rc32k_clk(clocks), + BleLpClkSclk::Ext32k => request_osc_slow_clk(clocks), + } + enable_ble_lp_clk_impl(clocks, true); + } + } + pub fn release_ble_lp_clk(clocks: &mut ClockTree) { + trace!("Releasing BLE_LP_CLK"); + if decrement_reference_count(&mut clocks.ble_lp_clk_refcount) { + trace!("Disabling BLE_LP_CLK"); + enable_ble_lp_clk_impl(clocks, false); + match unwrap!(clocks.ble_lp_clk).sclk { + BleLpClkSclk::Xtal => release_ble_lp_xtal_clk(clocks), + BleLpClkSclk::RcSlow => release_rc_slow_clk(clocks), + #[cfg(use_xtal32k)] + BleLpClkSclk::Xtal32k => release_xtal32k_clk(clocks), + BleLpClkSclk::Rc32k => release_rc32k_clk(clocks), + BleLpClkSclk::Ext32k => release_osc_slow_clk(clocks), + } + } + } + #[allow(unused_variables)] + pub fn ble_lp_clk_config_frequency(clocks: &mut ClockTree, config: BleLpClkConfig) -> u32 { + (match config.sclk { + BleLpClkSclk::Xtal => ble_lp_xtal_clk_frequency(), + BleLpClkSclk::RcSlow => rc_slow_clk_frequency(), + #[cfg(use_xtal32k)] + BleLpClkSclk::Xtal32k => xtal32k_clk_frequency(), + BleLpClkSclk::Rc32k => rc32k_clk_frequency(), + BleLpClkSclk::Ext32k => osc_slow_clk_frequency(), + } / (config.divisor() + 1)) + } + pub fn ble_lp_clk_frequency() -> u32 { + BLE_LP_CLK_FREQ_CACHE.load(::core::sync::atomic::Ordering::Acquire) + } + pub fn ble_lp_clk_source_frequency(sclk: BleLpClkSclk) -> u32 { + match sclk { + BleLpClkSclk::Xtal => ble_lp_xtal_clk_frequency(), + BleLpClkSclk::RcSlow => rc_slow_clk_frequency(), + #[cfg(use_xtal32k)] + BleLpClkSclk::Xtal32k => xtal32k_clk_frequency(), + BleLpClkSclk::Rc32k => rc32k_clk_frequency(), + BleLpClkSclk::Ext32k => osc_slow_clk_frequency(), + } + } pub fn configure_timg_calibration_clock( clocks: &mut ClockTree, new_selector: TimgCalibrationClockConfig, @@ -4929,6 +5216,10 @@ macro_rules! define_clock_tree_types { pub lp_fast_clk: Option, /// `LP_SLOW_CLK` configuration. pub lp_slow_clk: Option, + /// `BLE_LP_XTAL_CLK` configuration. + pub ble_lp_xtal_clk: Option, + /// `BLE_LP_CLK` configuration. + pub ble_lp_clk: Option, /// `TIMG_CALIBRATION_CLOCK` configuration. pub timg_calibration_clock: Option, } @@ -4952,6 +5243,12 @@ macro_rules! define_clock_tree_types { if let Some(config) = self.lp_slow_clk { configure_lp_slow_clk(clocks, config); } + if let Some(config) = self.ble_lp_xtal_clk { + configure_ble_lp_xtal_clk(clocks, config); + } + if let Some(config) = self.ble_lp_clk { + configure_ble_lp_clk(clocks, config); + } if let Some(config) = self.timg_calibration_clock { configure_timg_calibration_clock(clocks, config); } @@ -4999,6 +5296,7 @@ macro_rules! define_clock_tree_types { refresh_iomux_function_clock_downstream(clocks); refresh_ledc_sclk_downstream(clocks); refresh_lp_fast_clk_downstream(clocks); + refresh_ble_lp_xtal_clk_downstream(clocks); for child_instance in [I2cInstance::I2c0] { refresh_i2c_function_clock_downstream(clocks, child_instance); } @@ -5069,6 +5367,23 @@ macro_rules! define_clock_tree_types { } refresh_timg_calibration_clock_downstream(clocks); } + fn refresh_ble_lp_xtal_clk_downstream(clocks: &mut ClockTree) { + if let Some(config) = clocks.ble_lp_xtal_clk { + BLE_LP_XTAL_CLK_FREQ_CACHE.store( + ble_lp_xtal_clk_config_frequency(clocks, config), + ::core::sync::atomic::Ordering::Release, + ); + } + refresh_ble_lp_clk_downstream(clocks); + } + fn refresh_ble_lp_clk_downstream(clocks: &mut ClockTree) { + if let Some(config) = clocks.ble_lp_clk { + BLE_LP_CLK_FREQ_CACHE.store( + ble_lp_clk_config_frequency(clocks, config), + ::core::sync::atomic::Ordering::Release, + ); + } + } fn refresh_timg_calibration_clock_downstream(clocks: &mut ClockTree) { if let Some(config) = clocks.timg_calibration_clock { TIMG_CALIBRATION_CLOCK_FREQ_CACHE.store( diff --git a/esp-metadata-generated/src/_generated_esp32c61.rs b/esp-metadata-generated/src/_generated_esp32c61.rs index f03ee91a38c..681dce2519c 100644 --- a/esp-metadata-generated/src/_generated_esp32c61.rs +++ b/esp-metadata-generated/src/_generated_esp32c61.rs @@ -472,6 +472,15 @@ macro_rules! property { ::soc::clocks::LpSlowClkConfig::Xtal32k, crate ::soc::clocks::LpSlowClkConfig::OscSlow] }; + ("clock_tree.ble_lp_clk.sclk") => { + [crate ::soc::clocks::BleLpClkSclk::Xtal, crate + ::soc::clocks::BleLpClkSclk::RcSlow, #[cfg(use_xtal32k)] crate + ::soc::clocks::BleLpClkSclk::Xtal32k, crate ::soc::clocks::BleLpClkSclk::Rc32k, + crate ::soc::clocks::BleLpClkSclk::Ext32k] + }; + ("clock_tree.ble_lp_clk.divisor") => { + (0, 4095) + }; ("clock_tree.crypto_clk") => { [crate ::soc::clocks::CryptoClkConfig::Xtal, crate ::soc::clocks::CryptoClkConfig::RcFast, crate @@ -920,6 +929,12 @@ macro_rules! for_each_sw_interrupt { /// todo!() /// } /// +/// // RC32K_CLK +/// +/// fn enable_rc32k_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// /// // RC_SLOW_CLK /// /// fn enable_rc_slow_clk_impl(_clocks: &mut ClockTree, _en: bool) { @@ -1058,6 +1073,20 @@ macro_rules! for_each_sw_interrupt { /// todo!() /// } /// +/// // BLE_LP_CLK +/// +/// fn enable_ble_lp_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_ble_lp_clk_impl( +/// _clocks: &mut ClockTree, +/// _old_config: Option, +/// _new_config: BleLpClkConfig, +/// ) { +/// todo!() +/// } +/// /// // CRYPTO_PLL_DIV /// /// fn enable_crypto_pll_div_impl(_clocks: &mut ClockTree, _en: bool) { @@ -1310,7 +1339,7 @@ macro_rules! define_clock_tree_types { ); Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -1336,7 +1365,7 @@ macro_rules! define_clock_tree_types { ); Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -1362,7 +1391,7 @@ macro_rules! define_clock_tree_types { ); Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -1389,6 +1418,52 @@ macro_rules! define_clock_tree_types { /// Selects `OSC_SLOW_CLK`. OscSlow, } + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum BleLpClkSclk { + #[default] + /// Selects `XTAL_CLK`. + Xtal, + /// Selects `RC_SLOW_CLK`. + RcSlow, + #[cfg(use_xtal32k)] + /// Selects `XTAL32K_CLK`. + Xtal32k, + /// Selects `RC32K_CLK`. + Rc32k, + /// Selects `OSC_SLOW_CLK`. + Ext32k, + } + /// Configures the `BLE_LP_CLK` clock node. + /// + /// The output is calculated as `OUTPUT = sclk / (divisor + 1)`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct BleLpClkConfig { + sclk: BleLpClkSclk, + divisor: u32, + } + impl BleLpClkConfig { + /// Creates a new configuration for the BLE_LP_CLK clock node. + /// + /// ## Panics + /// + /// Panics if the divisor value is outside the + /// valid range (0 ..= 4095). + pub const fn new(sclk: BleLpClkSclk, divisor: u32) -> Self { + ::core::assert!( + divisor <= 4095, + "`BLE_LP_CLK` divisor must be between 0 and 4095 (inclusive)." + ); + Self { sclk, divisor } + } + pub fn sclk(self) -> BleLpClkSclk { + self.sclk + } + pub fn divisor(self) -> u32 { + self.divisor as u32 + } + } /// The list of clock signals that the `CRYPTO_CLK` multiplexer can output. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -1446,10 +1521,10 @@ macro_rules! define_clock_tree_types { ); Self { sclk, div_num } } - pub(crate) fn sclk(self) -> I2cFunctionClockSclk { + pub fn sclk(self) -> I2cFunctionClockSclk { self.sclk } - pub(crate) fn div_num(self) -> u32 { + pub fn div_num(self) -> u32 { self.div_num as u32 } } @@ -1508,16 +1583,16 @@ macro_rules! define_clock_tree_types { div_b, } } - pub(crate) fn sclk(self) -> I2sClkSclk { + pub fn sclk(self) -> I2sClkSclk { self.sclk } - pub(crate) fn div_num(self) -> u32 { + pub fn div_num(self) -> u32 { self.div_num as u32 } - pub(crate) fn div_a(self) -> u32 { + pub fn div_a(self) -> u32 { self.div_a as u32 } - pub(crate) fn div_b(self) -> u32 { + pub fn div_b(self) -> u32 { self.div_b as u32 } } @@ -1601,10 +1676,10 @@ macro_rules! define_clock_tree_types { ); Self { sclk, div_num } } - pub(crate) fn sclk(self) -> UartFunctionClockSclk { + pub fn sclk(self) -> UartFunctionClockSclk { self.sclk } - pub(crate) fn div_num(self) -> u32 { + pub fn div_num(self) -> u32 { self.div_num as u32 } } @@ -1642,10 +1717,10 @@ macro_rules! define_clock_tree_types { integral, } } - pub(crate) fn fractional(self) -> u32 { + pub fn fractional(self) -> u32 { self.fractional as u32 } - pub(crate) fn integral(self) -> u32 { + pub fn integral(self) -> u32 { self.integral as u32 } } @@ -1658,6 +1733,7 @@ macro_rules! define_clock_tree_types { apb_clk: Option, lp_fast_clk: Option, lp_slow_clk: Option, + ble_lp_clk: Option, crypto_clk: Option, timg_calibration_clock: Option, i2c_function_clock: [Option; 1], @@ -1687,6 +1763,7 @@ macro_rules! define_clock_tree_types { apb_clk_refcount: u32, lp_fast_clk_refcount: u32, lp_slow_clk_refcount: u32, + ble_lp_clk_refcount: u32, crypto_clk_refcount: u32, timg_calibration_clock_refcount: u32, i2c_function_clock_refcount: [u32; 1], @@ -1732,6 +1809,10 @@ macro_rules! define_clock_tree_types { pub fn lp_slow_clk(&self) -> Option { self.lp_slow_clk } + /// Returns the current configuration of the BLE_LP_CLK clock tree node + pub fn ble_lp_clk(&self) -> Option { + self.ble_lp_clk + } /// Returns the current configuration of the CRYPTO_CLK clock tree node pub fn crypto_clk(&self) -> Option { self.crypto_clk @@ -1810,6 +1891,7 @@ macro_rules! define_clock_tree_types { apb_clk: None, lp_fast_clk: None, lp_slow_clk: None, + ble_lp_clk: None, crypto_clk: None, timg_calibration_clock: None, i2c_function_clock: [None; 1], @@ -1839,6 +1921,7 @@ macro_rules! define_clock_tree_types { apb_clk_refcount: 0, lp_fast_clk_refcount: 0, lp_slow_clk_refcount: 0, + ble_lp_clk_refcount: 0, crypto_clk_refcount: 0, timg_calibration_clock_refcount: 0, i2c_function_clock_refcount: [0; 1], @@ -1865,6 +1948,8 @@ macro_rules! define_clock_tree_types { ::core::sync::atomic::AtomicU32::new(0); static LP_SLOW_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = ::core::sync::atomic::AtomicU32::new(0); + static BLE_LP_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = + ::core::sync::atomic::AtomicU32::new(0); static CRYPTO_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = ::core::sync::atomic::AtomicU32::new(0); static TIMG_CALIBRATION_CLOCK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = @@ -1980,6 +2065,19 @@ macro_rules! define_clock_tree_types { pub fn osc_slow_clk_frequency() -> u32 { 32768 } + pub fn request_rc32k_clk(clocks: &mut ClockTree) { + trace!("Requesting RC32K_CLK"); + trace!("Enabling RC32K_CLK"); + enable_rc32k_clk_impl(clocks, true); + } + pub fn release_rc32k_clk(clocks: &mut ClockTree) { + trace!("Releasing RC32K_CLK"); + trace!("Disabling RC32K_CLK"); + enable_rc32k_clk_impl(clocks, false); + } + pub fn rc32k_clk_frequency() -> u32 { + 32000 + } pub fn request_rc_slow_clk(clocks: &mut ClockTree) { trace!("Requesting RC_SLOW_CLK"); if increment_reference_count(&mut clocks.rc_slow_clk_refcount) { @@ -2480,6 +2578,90 @@ macro_rules! define_clock_tree_types { LpSlowClkConfig::OscSlow => osc_slow_clk_frequency(), } } + pub fn configure_ble_lp_clk(clocks: &mut ClockTree, config: BleLpClkConfig) { + let old_config = clocks.ble_lp_clk.replace(config); + refresh_ble_lp_clk_downstream(clocks); + if clocks.ble_lp_clk_refcount > 0 { + match config.sclk { + BleLpClkSclk::Xtal => request_xtal_clk(clocks), + BleLpClkSclk::RcSlow => request_rc_slow_clk(clocks), + #[cfg(use_xtal32k)] + BleLpClkSclk::Xtal32k => request_xtal32k_clk(clocks), + BleLpClkSclk::Rc32k => request_rc32k_clk(clocks), + BleLpClkSclk::Ext32k => request_osc_slow_clk(clocks), + } + configure_ble_lp_clk_impl(clocks, old_config, config); + if let Some(old_config) = old_config { + match old_config.sclk { + BleLpClkSclk::Xtal => release_xtal_clk(clocks), + BleLpClkSclk::RcSlow => release_rc_slow_clk(clocks), + #[cfg(use_xtal32k)] + BleLpClkSclk::Xtal32k => release_xtal32k_clk(clocks), + BleLpClkSclk::Rc32k => release_rc32k_clk(clocks), + BleLpClkSclk::Ext32k => release_osc_slow_clk(clocks), + } + } + } else { + configure_ble_lp_clk_impl(clocks, old_config, config); + } + } + pub fn ble_lp_clk_config(clocks: &mut ClockTree) -> Option { + clocks.ble_lp_clk + } + pub fn request_ble_lp_clk(clocks: &mut ClockTree) { + trace!("Requesting BLE_LP_CLK"); + if increment_reference_count(&mut clocks.ble_lp_clk_refcount) { + trace!("Enabling BLE_LP_CLK"); + match unwrap!(clocks.ble_lp_clk).sclk { + BleLpClkSclk::Xtal => request_xtal_clk(clocks), + BleLpClkSclk::RcSlow => request_rc_slow_clk(clocks), + #[cfg(use_xtal32k)] + BleLpClkSclk::Xtal32k => request_xtal32k_clk(clocks), + BleLpClkSclk::Rc32k => request_rc32k_clk(clocks), + BleLpClkSclk::Ext32k => request_osc_slow_clk(clocks), + } + enable_ble_lp_clk_impl(clocks, true); + } + } + pub fn release_ble_lp_clk(clocks: &mut ClockTree) { + trace!("Releasing BLE_LP_CLK"); + if decrement_reference_count(&mut clocks.ble_lp_clk_refcount) { + trace!("Disabling BLE_LP_CLK"); + enable_ble_lp_clk_impl(clocks, false); + match unwrap!(clocks.ble_lp_clk).sclk { + BleLpClkSclk::Xtal => release_xtal_clk(clocks), + BleLpClkSclk::RcSlow => release_rc_slow_clk(clocks), + #[cfg(use_xtal32k)] + BleLpClkSclk::Xtal32k => release_xtal32k_clk(clocks), + BleLpClkSclk::Rc32k => release_rc32k_clk(clocks), + BleLpClkSclk::Ext32k => release_osc_slow_clk(clocks), + } + } + } + #[allow(unused_variables)] + pub fn ble_lp_clk_config_frequency(clocks: &mut ClockTree, config: BleLpClkConfig) -> u32 { + (match config.sclk { + BleLpClkSclk::Xtal => xtal_clk_frequency(), + BleLpClkSclk::RcSlow => rc_slow_clk_frequency(), + #[cfg(use_xtal32k)] + BleLpClkSclk::Xtal32k => xtal32k_clk_frequency(), + BleLpClkSclk::Rc32k => rc32k_clk_frequency(), + BleLpClkSclk::Ext32k => osc_slow_clk_frequency(), + } / (config.divisor() + 1)) + } + pub fn ble_lp_clk_frequency() -> u32 { + BLE_LP_CLK_FREQ_CACHE.load(::core::sync::atomic::Ordering::Acquire) + } + pub fn ble_lp_clk_source_frequency(sclk: BleLpClkSclk) -> u32 { + match sclk { + BleLpClkSclk::Xtal => xtal_clk_frequency(), + BleLpClkSclk::RcSlow => rc_slow_clk_frequency(), + #[cfg(use_xtal32k)] + BleLpClkSclk::Xtal32k => xtal32k_clk_frequency(), + BleLpClkSclk::Rc32k => rc32k_clk_frequency(), + BleLpClkSclk::Ext32k => osc_slow_clk_frequency(), + } + } pub fn request_crypto_pll_div(clocks: &mut ClockTree) { trace!("Requesting CRYPTO_PLL_DIV"); trace!("Enabling CRYPTO_PLL_DIV"); @@ -3327,6 +3509,8 @@ macro_rules! define_clock_tree_types { pub lp_fast_clk: Option, /// `LP_SLOW_CLK` configuration. pub lp_slow_clk: Option, + /// `BLE_LP_CLK` configuration. + pub ble_lp_clk: Option, /// `CRYPTO_CLK` configuration. pub crypto_clk: Option, /// `TIMG_CALIBRATION_CLOCK` configuration. @@ -3355,6 +3539,9 @@ macro_rules! define_clock_tree_types { if let Some(config) = self.lp_slow_clk { configure_lp_slow_clk(clocks, config); } + if let Some(config) = self.ble_lp_clk { + configure_ble_lp_clk(clocks, config); + } if let Some(config) = self.crypto_clk { configure_crypto_clk(clocks, config); } @@ -3382,6 +3569,7 @@ macro_rules! define_clock_tree_types { } refresh_hp_root_clk_downstream(clocks); refresh_lp_fast_clk_downstream(clocks); + refresh_ble_lp_clk_downstream(clocks); refresh_crypto_clk_downstream(clocks); for child_instance in [I2cInstance::I2c0] { refresh_i2c_function_clock_downstream(clocks, child_instance); @@ -3456,6 +3644,14 @@ macro_rules! define_clock_tree_types { ); } } + fn refresh_ble_lp_clk_downstream(clocks: &mut ClockTree) { + if let Some(config) = clocks.ble_lp_clk { + BLE_LP_CLK_FREQ_CACHE.store( + ble_lp_clk_config_frequency(clocks, config), + ::core::sync::atomic::Ordering::Release, + ); + } + } fn refresh_crypto_clk_downstream(clocks: &mut ClockTree) { if let Some(config) = clocks.crypto_clk { CRYPTO_CLK_FREQ_CACHE.store( diff --git a/esp-metadata-generated/src/_generated_esp32h2.rs b/esp-metadata-generated/src/_generated_esp32h2.rs index aa2bcabcf76..9e2d3ca6654 100644 --- a/esp-metadata-generated/src/_generated_esp32h2.rs +++ b/esp-metadata-generated/src/_generated_esp32h2.rs @@ -531,6 +531,15 @@ macro_rules! property { ::soc::clocks::LpSlowClkConfig::RcSlow, crate ::soc::clocks::LpSlowClkConfig::OscSlow] }; + ("clock_tree.ble_lp_clk.sclk") => { + [crate ::soc::clocks::BleLpClkSclk::Xtal, crate + ::soc::clocks::BleLpClkSclk::RcSlow, #[cfg(use_xtal32k)] crate + ::soc::clocks::BleLpClkSclk::Xtal32k, crate ::soc::clocks::BleLpClkSclk::Rc32k, + crate ::soc::clocks::BleLpClkSclk::Ext32k] + }; + ("clock_tree.ble_lp_clk.divisor") => { + (0, 4095) + }; ("clock_tree.crypto_clk") => { [crate ::soc::clocks::CryptoClkConfig::Xtal, crate ::soc::clocks::CryptoClkConfig::RcFast, crate @@ -1323,6 +1332,12 @@ macro_rules! for_each_sw_interrupt { /// todo!() /// } /// +/// // RC32K_CLK +/// +/// fn enable_rc32k_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// /// // RC_SLOW_CLK /// /// fn enable_rc_slow_clk_impl(_clocks: &mut ClockTree, _en: bool) { @@ -1418,6 +1433,20 @@ macro_rules! for_each_sw_interrupt { /// todo!() /// } /// +/// // BLE_LP_CLK +/// +/// fn enable_ble_lp_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_ble_lp_clk_impl( +/// _clocks: &mut ClockTree, +/// _old_config: Option, +/// _new_config: BleLpClkConfig, +/// ) { +/// todo!() +/// } +/// /// // CRYPTO_CLK /// /// fn enable_crypto_clk_impl(_clocks: &mut ClockTree, _en: bool) { @@ -1766,7 +1795,7 @@ macro_rules! define_clock_tree_types { ); Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -1792,7 +1821,7 @@ macro_rules! define_clock_tree_types { ); Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -1818,7 +1847,7 @@ macro_rules! define_clock_tree_types { ); Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -1846,6 +1875,52 @@ macro_rules! define_clock_tree_types { /// Selects `OSC_SLOW_CLK`. OscSlow, } + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum BleLpClkSclk { + #[default] + /// Selects `XTAL_CLK`. + Xtal, + /// Selects `RC_SLOW_CLK`. + RcSlow, + #[cfg(use_xtal32k)] + /// Selects `XTAL32K_CLK`. + Xtal32k, + /// Selects `RC32K_CLK`. + Rc32k, + /// Selects `OSC_SLOW_CLK`. + Ext32k, + } + /// Configures the `BLE_LP_CLK` clock node. + /// + /// The output is calculated as `OUTPUT = sclk / (divisor + 1)`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct BleLpClkConfig { + sclk: BleLpClkSclk, + divisor: u32, + } + impl BleLpClkConfig { + /// Creates a new configuration for the BLE_LP_CLK clock node. + /// + /// ## Panics + /// + /// Panics if the divisor value is outside the + /// valid range (0 ..= 4095). + pub const fn new(sclk: BleLpClkSclk, divisor: u32) -> Self { + ::core::assert!( + divisor <= 4095, + "`BLE_LP_CLK` divisor must be between 0 and 4095 (inclusive)." + ); + Self { sclk, divisor } + } + pub fn sclk(self) -> BleLpClkSclk { + self.sclk + } + pub fn divisor(self) -> u32 { + self.divisor as u32 + } + } /// The list of clock signals that the `CRYPTO_CLK` multiplexer can output. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -1903,10 +1978,10 @@ macro_rules! define_clock_tree_types { ); Self { sclk, div_num } } - pub(crate) fn sclk(self) -> I2cFunctionClockSclk { + pub fn sclk(self) -> I2cFunctionClockSclk { self.sclk } - pub(crate) fn div_num(self) -> u32 { + pub fn div_num(self) -> u32 { self.div_num as u32 } } @@ -1965,16 +2040,16 @@ macro_rules! define_clock_tree_types { div_b, } } - pub(crate) fn sclk(self) -> I2sClkSclk { + pub fn sclk(self) -> I2sClkSclk { self.sclk } - pub(crate) fn div_num(self) -> u32 { + pub fn div_num(self) -> u32 { self.div_num as u32 } - pub(crate) fn div_a(self) -> u32 { + pub fn div_a(self) -> u32 { self.div_a as u32 } - pub(crate) fn div_b(self) -> u32 { + pub fn div_b(self) -> u32 { self.div_b as u32 } } @@ -2092,10 +2167,10 @@ macro_rules! define_clock_tree_types { ); Self { sclk, div_num } } - pub(crate) fn sclk(self) -> UartFunctionClockSclk { + pub fn sclk(self) -> UartFunctionClockSclk { self.sclk } - pub(crate) fn div_num(self) -> u32 { + pub fn div_num(self) -> u32 { self.div_num as u32 } } @@ -2133,10 +2208,10 @@ macro_rules! define_clock_tree_types { integral, } } - pub(crate) fn fractional(self) -> u32 { + pub fn fractional(self) -> u32 { self.fractional as u32 } - pub(crate) fn integral(self) -> u32 { + pub fn integral(self) -> u32 { self.integral as u32 } } @@ -2150,6 +2225,7 @@ macro_rules! define_clock_tree_types { apb_clk: Option, lp_fast_clk: Option, lp_slow_clk: Option, + ble_lp_clk: Option, crypto_clk: Option, timg_calibration_clock: Option, i2c_function_clock: [Option; 2], @@ -2171,9 +2247,12 @@ macro_rules! define_clock_tree_types { rc_fast_clk_refcount: u32, #[cfg(use_xtal32k)] xtal32k_clk_refcount: u32, + osc_slow_clk_refcount: u32, + rc_slow_clk_refcount: u32, hp_root_clk_refcount: u32, apb_clk_refcount: u32, lp_fast_clk_refcount: u32, + ble_lp_clk_refcount: u32, crypto_clk_refcount: u32, timg_calibration_clock_refcount: u32, sdm_function_clock_refcount: [u32; 1], @@ -2228,6 +2307,10 @@ macro_rules! define_clock_tree_types { pub fn lp_slow_clk(&self) -> Option { self.lp_slow_clk } + /// Returns the current configuration of the BLE_LP_CLK clock tree node + pub fn ble_lp_clk(&self) -> Option { + self.ble_lp_clk + } /// Returns the current configuration of the CRYPTO_CLK clock tree node pub fn crypto_clk(&self) -> Option { self.crypto_clk @@ -2319,6 +2402,7 @@ macro_rules! define_clock_tree_types { apb_clk: None, lp_fast_clk: None, lp_slow_clk: None, + ble_lp_clk: None, crypto_clk: None, timg_calibration_clock: None, i2c_function_clock: [None; 2], @@ -2340,9 +2424,12 @@ macro_rules! define_clock_tree_types { rc_fast_clk_refcount: 0, #[cfg(use_xtal32k)] xtal32k_clk_refcount: 0, + osc_slow_clk_refcount: 0, + rc_slow_clk_refcount: 0, hp_root_clk_refcount: 0, apb_clk_refcount: 0, lp_fast_clk_refcount: 0, + ble_lp_clk_refcount: 0, crypto_clk_refcount: 0, timg_calibration_clock_refcount: 0, sdm_function_clock_refcount: [0; 1], @@ -2376,6 +2463,8 @@ macro_rules! define_clock_tree_types { ::core::sync::atomic::AtomicU32::new(0); static LP_SLOW_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = ::core::sync::atomic::AtomicU32::new(0); + static BLE_LP_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = + ::core::sync::atomic::AtomicU32::new(0); static CRYPTO_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = ::core::sync::atomic::AtomicU32::new(0); static TIMG_CALIBRATION_CLOCK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = @@ -2572,26 +2661,47 @@ macro_rules! define_clock_tree_types { } pub fn request_osc_slow_clk(clocks: &mut ClockTree) { trace!("Requesting OSC_SLOW_CLK"); - trace!("Enabling OSC_SLOW_CLK"); - enable_osc_slow_clk_impl(clocks, true); + if increment_reference_count(&mut clocks.osc_slow_clk_refcount) { + trace!("Enabling OSC_SLOW_CLK"); + enable_osc_slow_clk_impl(clocks, true); + } } pub fn release_osc_slow_clk(clocks: &mut ClockTree) { trace!("Releasing OSC_SLOW_CLK"); - trace!("Disabling OSC_SLOW_CLK"); - enable_osc_slow_clk_impl(clocks, false); + if decrement_reference_count(&mut clocks.osc_slow_clk_refcount) { + trace!("Disabling OSC_SLOW_CLK"); + enable_osc_slow_clk_impl(clocks, false); + } } pub fn osc_slow_clk_frequency() -> u32 { 32768 } + pub fn request_rc32k_clk(clocks: &mut ClockTree) { + trace!("Requesting RC32K_CLK"); + trace!("Enabling RC32K_CLK"); + enable_rc32k_clk_impl(clocks, true); + } + pub fn release_rc32k_clk(clocks: &mut ClockTree) { + trace!("Releasing RC32K_CLK"); + trace!("Disabling RC32K_CLK"); + enable_rc32k_clk_impl(clocks, false); + } + pub fn rc32k_clk_frequency() -> u32 { + 32000 + } pub fn request_rc_slow_clk(clocks: &mut ClockTree) { trace!("Requesting RC_SLOW_CLK"); - trace!("Enabling RC_SLOW_CLK"); - enable_rc_slow_clk_impl(clocks, true); + if increment_reference_count(&mut clocks.rc_slow_clk_refcount) { + trace!("Enabling RC_SLOW_CLK"); + enable_rc_slow_clk_impl(clocks, true); + } } pub fn release_rc_slow_clk(clocks: &mut ClockTree) { trace!("Releasing RC_SLOW_CLK"); - trace!("Disabling RC_SLOW_CLK"); - enable_rc_slow_clk_impl(clocks, false); + if decrement_reference_count(&mut clocks.rc_slow_clk_refcount) { + trace!("Disabling RC_SLOW_CLK"); + enable_rc_slow_clk_impl(clocks, false); + } } pub fn rc_slow_clk_frequency() -> u32 { 130000 @@ -2927,6 +3037,90 @@ macro_rules! define_clock_tree_types { LpSlowClkConfig::OscSlow => osc_slow_clk_frequency(), } } + pub fn configure_ble_lp_clk(clocks: &mut ClockTree, config: BleLpClkConfig) { + let old_config = clocks.ble_lp_clk.replace(config); + refresh_ble_lp_clk_downstream(clocks); + if clocks.ble_lp_clk_refcount > 0 { + match config.sclk { + BleLpClkSclk::Xtal => request_xtal_clk(clocks), + BleLpClkSclk::RcSlow => request_rc_slow_clk(clocks), + #[cfg(use_xtal32k)] + BleLpClkSclk::Xtal32k => request_xtal32k_clk(clocks), + BleLpClkSclk::Rc32k => request_rc32k_clk(clocks), + BleLpClkSclk::Ext32k => request_osc_slow_clk(clocks), + } + configure_ble_lp_clk_impl(clocks, old_config, config); + if let Some(old_config) = old_config { + match old_config.sclk { + BleLpClkSclk::Xtal => release_xtal_clk(clocks), + BleLpClkSclk::RcSlow => release_rc_slow_clk(clocks), + #[cfg(use_xtal32k)] + BleLpClkSclk::Xtal32k => release_xtal32k_clk(clocks), + BleLpClkSclk::Rc32k => release_rc32k_clk(clocks), + BleLpClkSclk::Ext32k => release_osc_slow_clk(clocks), + } + } + } else { + configure_ble_lp_clk_impl(clocks, old_config, config); + } + } + pub fn ble_lp_clk_config(clocks: &mut ClockTree) -> Option { + clocks.ble_lp_clk + } + pub fn request_ble_lp_clk(clocks: &mut ClockTree) { + trace!("Requesting BLE_LP_CLK"); + if increment_reference_count(&mut clocks.ble_lp_clk_refcount) { + trace!("Enabling BLE_LP_CLK"); + match unwrap!(clocks.ble_lp_clk).sclk { + BleLpClkSclk::Xtal => request_xtal_clk(clocks), + BleLpClkSclk::RcSlow => request_rc_slow_clk(clocks), + #[cfg(use_xtal32k)] + BleLpClkSclk::Xtal32k => request_xtal32k_clk(clocks), + BleLpClkSclk::Rc32k => request_rc32k_clk(clocks), + BleLpClkSclk::Ext32k => request_osc_slow_clk(clocks), + } + enable_ble_lp_clk_impl(clocks, true); + } + } + pub fn release_ble_lp_clk(clocks: &mut ClockTree) { + trace!("Releasing BLE_LP_CLK"); + if decrement_reference_count(&mut clocks.ble_lp_clk_refcount) { + trace!("Disabling BLE_LP_CLK"); + enable_ble_lp_clk_impl(clocks, false); + match unwrap!(clocks.ble_lp_clk).sclk { + BleLpClkSclk::Xtal => release_xtal_clk(clocks), + BleLpClkSclk::RcSlow => release_rc_slow_clk(clocks), + #[cfg(use_xtal32k)] + BleLpClkSclk::Xtal32k => release_xtal32k_clk(clocks), + BleLpClkSclk::Rc32k => release_rc32k_clk(clocks), + BleLpClkSclk::Ext32k => release_osc_slow_clk(clocks), + } + } + } + #[allow(unused_variables)] + pub fn ble_lp_clk_config_frequency(clocks: &mut ClockTree, config: BleLpClkConfig) -> u32 { + (match config.sclk { + BleLpClkSclk::Xtal => xtal_clk_frequency(), + BleLpClkSclk::RcSlow => rc_slow_clk_frequency(), + #[cfg(use_xtal32k)] + BleLpClkSclk::Xtal32k => xtal32k_clk_frequency(), + BleLpClkSclk::Rc32k => rc32k_clk_frequency(), + BleLpClkSclk::Ext32k => osc_slow_clk_frequency(), + } / (config.divisor() + 1)) + } + pub fn ble_lp_clk_frequency() -> u32 { + BLE_LP_CLK_FREQ_CACHE.load(::core::sync::atomic::Ordering::Acquire) + } + pub fn ble_lp_clk_source_frequency(sclk: BleLpClkSclk) -> u32 { + match sclk { + BleLpClkSclk::Xtal => xtal_clk_frequency(), + BleLpClkSclk::RcSlow => rc_slow_clk_frequency(), + #[cfg(use_xtal32k)] + BleLpClkSclk::Xtal32k => xtal32k_clk_frequency(), + BleLpClkSclk::Rc32k => rc32k_clk_frequency(), + BleLpClkSclk::Ext32k => osc_slow_clk_frequency(), + } + } pub fn configure_crypto_clk(clocks: &mut ClockTree, new_selector: CryptoClkConfig) { let old_selector = clocks.crypto_clk.replace(new_selector); refresh_crypto_clk_downstream(clocks); @@ -4085,6 +4279,8 @@ macro_rules! define_clock_tree_types { pub lp_fast_clk: Option, /// `LP_SLOW_CLK` configuration. pub lp_slow_clk: Option, + /// `BLE_LP_CLK` configuration. + pub ble_lp_clk: Option, /// `CRYPTO_CLK` configuration. pub crypto_clk: Option, /// `TIMG_CALIBRATION_CLOCK` configuration. @@ -4116,6 +4312,9 @@ macro_rules! define_clock_tree_types { if let Some(config) = self.lp_slow_clk { configure_lp_slow_clk(clocks, config); } + if let Some(config) = self.ble_lp_clk { + configure_ble_lp_clk(clocks, config); + } if let Some(config) = self.crypto_clk { configure_crypto_clk(clocks, config); } @@ -4144,6 +4343,7 @@ macro_rules! define_clock_tree_types { refresh_iomux_function_clock_downstream(clocks); refresh_hp_root_clk_downstream(clocks); refresh_lp_fast_clk_downstream(clocks); + refresh_ble_lp_clk_downstream(clocks); refresh_crypto_clk_downstream(clocks); for child_instance in [I2cInstance::I2c0, I2cInstance::I2c1] { refresh_i2c_function_clock_downstream(clocks, child_instance); @@ -4233,6 +4433,14 @@ macro_rules! define_clock_tree_types { } refresh_timg_calibration_clock_downstream(clocks); } + fn refresh_ble_lp_clk_downstream(clocks: &mut ClockTree) { + if let Some(config) = clocks.ble_lp_clk { + BLE_LP_CLK_FREQ_CACHE.store( + ble_lp_clk_config_frequency(clocks, config), + ::core::sync::atomic::Ordering::Release, + ); + } + } fn refresh_crypto_clk_downstream(clocks: &mut ClockTree) { if let Some(config) = clocks.crypto_clk { CRYPTO_CLK_FREQ_CACHE.store( diff --git a/esp-metadata-generated/src/_generated_esp32p4.rs b/esp-metadata-generated/src/_generated_esp32p4.rs index f1bc52a0c80..ff905ff1705 100644 --- a/esp-metadata-generated/src/_generated_esp32p4.rs +++ b/esp-metadata-generated/src/_generated_esp32p4.rs @@ -2184,10 +2184,10 @@ macro_rules! define_clock_tree_types { ); Self { source, div_num } } - pub(crate) fn source(self) -> IomuxFunctionClockSource { + pub fn source(self) -> IomuxFunctionClockSource { self.source } - pub(crate) fn div_num(self) -> u32 { + pub fn div_num(self) -> u32 { self.div_num as u32 } } @@ -2224,7 +2224,7 @@ macro_rules! define_clock_tree_types { ); Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -2250,7 +2250,7 @@ macro_rules! define_clock_tree_types { ); Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -2276,7 +2276,7 @@ macro_rules! define_clock_tree_types { ); Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -2302,7 +2302,7 @@ macro_rules! define_clock_tree_types { ); Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -2416,10 +2416,10 @@ macro_rules! define_clock_tree_types { ); Self { sclk, div_num } } - pub(crate) fn sclk(self) -> UartFunctionClockSclk { + pub fn sclk(self) -> UartFunctionClockSclk { self.sclk } - pub(crate) fn div_num(self) -> u32 { + pub fn div_num(self) -> u32 { self.div_num as u32 } } @@ -2457,10 +2457,10 @@ macro_rules! define_clock_tree_types { integral, } } - pub(crate) fn fractional(self) -> u32 { + pub fn fractional(self) -> u32 { self.fractional as u32 } - pub(crate) fn integral(self) -> u32 { + pub fn integral(self) -> u32 { self.integral as u32 } } @@ -2508,10 +2508,10 @@ macro_rules! define_clock_tree_types { ); Self { sclk, div_num } } - pub(crate) fn sclk(self) -> I2cFunctionClockSclk { + pub fn sclk(self) -> I2cFunctionClockSclk { self.sclk } - pub(crate) fn div_num(self) -> u32 { + pub fn div_num(self) -> u32 { self.div_num as u32 } } @@ -2568,16 +2568,16 @@ macro_rules! define_clock_tree_types { div_b, } } - pub(crate) fn sclk(self) -> I2sClkSclk { + pub fn sclk(self) -> I2sClkSclk { self.sclk } - pub(crate) fn div_num(self) -> u32 { + pub fn div_num(self) -> u32 { self.div_num as u32 } - pub(crate) fn div_a(self) -> u32 { + pub fn div_a(self) -> u32 { self.div_a as u32 } - pub(crate) fn div_b(self) -> u32 { + pub fn div_b(self) -> u32 { self.div_b as u32 } } @@ -2625,10 +2625,10 @@ macro_rules! define_clock_tree_types { ); Self { sclk, div_num } } - pub(crate) fn sclk(self) -> MipiDsiDpiClkSclk { + pub fn sclk(self) -> MipiDsiDpiClkSclk { self.sclk } - pub(crate) fn div_num(self) -> u32 { + pub fn div_num(self) -> u32 { self.div_num as u32 } } @@ -2668,10 +2668,10 @@ macro_rules! define_clock_tree_types { ); Self { sclk, div_num } } - pub(crate) fn sclk(self) -> MipiDsiPhyPllRefclkSclk { + pub fn sclk(self) -> MipiDsiPhyPllRefclkSclk { self.sclk } - pub(crate) fn div_num(self) -> u32 { + pub fn div_num(self) -> u32 { self.div_num as u32 } } diff --git a/esp-metadata-generated/src/_generated_esp32s2.rs b/esp-metadata-generated/src/_generated_esp32s2.rs index 9e7f70c2a5c..0a65318a27e 100644 --- a/esp-metadata-generated/src/_generated_esp32s2.rs +++ b/esp-metadata-generated/src/_generated_esp32s2.rs @@ -1602,7 +1602,7 @@ macro_rules! define_clock_tree_types { pub const fn new(divisor: CpuPllDivDivisor) -> Self { Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -1637,7 +1637,7 @@ macro_rules! define_clock_tree_types { ); Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -1689,7 +1689,7 @@ macro_rules! define_clock_tree_types { ); Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -1715,7 +1715,7 @@ macro_rules! define_clock_tree_types { ); Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -1795,7 +1795,7 @@ macro_rules! define_clock_tree_types { pub const fn new(sclk: I2cFunctionClockSclk) -> Self { Self { sclk } } - pub(crate) fn sclk(self) -> I2cFunctionClockSclk { + pub fn sclk(self) -> I2cFunctionClockSclk { self.sclk } } @@ -1852,16 +1852,16 @@ macro_rules! define_clock_tree_types { div_b, } } - pub(crate) fn sclk(self) -> I2sMclkSclk { + pub fn sclk(self) -> I2sMclkSclk { self.sclk } - pub(crate) fn div_num(self) -> u32 { + pub fn div_num(self) -> u32 { self.div_num as u32 } - pub(crate) fn div_a(self) -> u32 { + pub fn div_a(self) -> u32 { self.div_a as u32 } - pub(crate) fn div_b(self) -> u32 { + pub fn div_b(self) -> u32 { self.div_b as u32 } } @@ -1915,7 +1915,7 @@ macro_rules! define_clock_tree_types { pub const fn new(sclk: UartFunctionClockSclk) -> Self { Self { sclk } } - pub(crate) fn sclk(self) -> UartFunctionClockSclk { + pub fn sclk(self) -> UartFunctionClockSclk { self.sclk } } @@ -1954,10 +1954,10 @@ macro_rules! define_clock_tree_types { integral, } } - pub(crate) fn fractional(self) -> u32 { + pub fn fractional(self) -> u32 { self.fractional as u32 } - pub(crate) fn integral(self) -> u32 { + pub fn integral(self) -> u32 { self.integral as u32 } } diff --git a/esp-metadata-generated/src/_generated_esp32s3.rs b/esp-metadata-generated/src/_generated_esp32s3.rs index eb2d1ba618f..4ef12b2770a 100644 --- a/esp-metadata-generated/src/_generated_esp32s3.rs +++ b/esp-metadata-generated/src/_generated_esp32s3.rs @@ -561,6 +561,13 @@ macro_rules! property { ::soc::clocks::RtcSlowClkConfig::RcSlow, crate ::soc::clocks::RtcSlowClkConfig::RcFast] }; + ("clock_tree.ble_lp_clk.sclk") => { + [crate ::soc::clocks::BleLpClkSclk::Xtal, crate + ::soc::clocks::BleLpClkSclk::RtcSlow] + }; + ("clock_tree.ble_lp_clk.divisor") => { + (0, 255) + }; ("clock_tree.rtc_fast_clk") => { [crate ::soc::clocks::RtcFastClkConfig::Xtal, crate ::soc::clocks::RtcFastClkConfig::Rc] @@ -1549,6 +1556,20 @@ macro_rules! for_each_sw_interrupt { /// todo!() /// } /// +/// // BLE_LP_CLK +/// +/// fn enable_ble_lp_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_ble_lp_clk_impl( +/// _clocks: &mut ClockTree, +/// _old_config: Option, +/// _new_config: BleLpClkConfig, +/// ) { +/// todo!() +/// } +/// /// // RTC_FAST_CLK /// /// fn enable_rtc_fast_clk_impl(_clocks: &mut ClockTree, _en: bool) { @@ -1906,7 +1927,7 @@ macro_rules! define_clock_tree_types { ); Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -1981,7 +2002,7 @@ macro_rules! define_clock_tree_types { ); Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -1997,6 +2018,45 @@ macro_rules! define_clock_tree_types { /// Selects `RC_FAST_DIV_CLK`. RcFast, } + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum BleLpClkSclk { + #[default] + /// Selects `XTAL_CLK`. + Xtal, + /// Selects `RTC_SLOW_CLK`. + RtcSlow, + } + /// Configures the `BLE_LP_CLK` clock node. + /// + /// The output is calculated as `OUTPUT = sclk / (divisor + 1)`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct BleLpClkConfig { + sclk: BleLpClkSclk, + divisor: u32, + } + impl BleLpClkConfig { + /// Creates a new configuration for the BLE_LP_CLK clock node. + /// + /// ## Panics + /// + /// Panics if the divisor value is outside the + /// valid range (0 ..= 255). + pub const fn new(sclk: BleLpClkSclk, divisor: u32) -> Self { + ::core::assert!( + divisor <= 255, + "`BLE_LP_CLK` divisor must be between 0 and 255 (inclusive)." + ); + Self { sclk, divisor } + } + pub fn sclk(self) -> BleLpClkSclk { + self.sclk + } + pub fn divisor(self) -> u32 { + self.divisor as u32 + } + } /// The list of clock signals that the `RTC_FAST_CLK` multiplexer can output. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -2072,10 +2132,10 @@ macro_rules! define_clock_tree_types { ); Self { sclk, div_num } } - pub(crate) fn sclk(self) -> I2cFunctionClockSclk { + pub fn sclk(self) -> I2cFunctionClockSclk { self.sclk } - pub(crate) fn div_num(self) -> u32 { + pub fn div_num(self) -> u32 { self.div_num as u32 } } @@ -2134,16 +2194,16 @@ macro_rules! define_clock_tree_types { div_b, } } - pub(crate) fn sclk(self) -> I2sClkSclk { + pub fn sclk(self) -> I2sClkSclk { self.sclk } - pub(crate) fn div_num(self) -> u32 { + pub fn div_num(self) -> u32 { self.div_num as u32 } - pub(crate) fn div_a(self) -> u32 { + pub fn div_a(self) -> u32 { self.div_a as u32 } - pub(crate) fn div_b(self) -> u32 { + pub fn div_b(self) -> u32 { self.div_b as u32 } } @@ -2217,16 +2277,16 @@ macro_rules! define_clock_tree_types { div_b, } } - pub(crate) fn sclk(self) -> LcdCamLcdClockSclk { + pub fn sclk(self) -> LcdCamLcdClockSclk { self.sclk } - pub(crate) fn div_num(self) -> u32 { + pub fn div_num(self) -> u32 { self.div_num as u32 } - pub(crate) fn div_a(self) -> u32 { + pub fn div_a(self) -> u32 { self.div_a as u32 } - pub(crate) fn div_b(self) -> u32 { + pub fn div_b(self) -> u32 { self.div_b as u32 } } @@ -2290,16 +2350,16 @@ macro_rules! define_clock_tree_types { div_b, } } - pub(crate) fn sclk(self) -> LcdCamCamClockSclk { + pub fn sclk(self) -> LcdCamCamClockSclk { self.sclk } - pub(crate) fn div_num(self) -> u32 { + pub fn div_num(self) -> u32 { self.div_num as u32 } - pub(crate) fn div_a(self) -> u32 { + pub fn div_a(self) -> u32 { self.div_a as u32 } - pub(crate) fn div_b(self) -> u32 { + pub fn div_b(self) -> u32 { self.div_b as u32 } } @@ -2377,10 +2437,10 @@ macro_rules! define_clock_tree_types { ); Self { sclk, div_num } } - pub(crate) fn sclk(self) -> UartFunctionClockSclk { + pub fn sclk(self) -> UartFunctionClockSclk { self.sclk } - pub(crate) fn div_num(self) -> u32 { + pub fn div_num(self) -> u32 { self.div_num as u32 } } @@ -2418,10 +2478,10 @@ macro_rules! define_clock_tree_types { integral, } } - pub(crate) fn fractional(self) -> u32 { + pub fn fractional(self) -> u32 { self.fractional as u32 } - pub(crate) fn integral(self) -> u32 { + pub fn integral(self) -> u32 { self.integral as u32 } } @@ -2437,6 +2497,7 @@ macro_rules! define_clock_tree_types { cpu_clk: Option, rc_fast_clk_div_n: Option, rtc_slow_clk: Option, + ble_lp_clk: Option, rtc_fast_clk: Option, low_power_clk: Option, timg_calibration_clock: Option, @@ -2461,6 +2522,8 @@ macro_rules! define_clock_tree_types { crypto_pwm_clk_refcount: u32, pll_d2_refcount: u32, pll_160m_refcount: u32, + rtc_slow_clk_refcount: u32, + ble_lp_clk_refcount: u32, rtc_fast_clk_refcount: u32, low_power_clk_refcount: u32, uart_mem_clk_refcount: u32, @@ -2525,6 +2588,10 @@ macro_rules! define_clock_tree_types { pub fn rtc_slow_clk(&self) -> Option { self.rtc_slow_clk } + /// Returns the current configuration of the BLE_LP_CLK clock tree node + pub fn ble_lp_clk(&self) -> Option { + self.ble_lp_clk + } /// Returns the current configuration of the RTC_FAST_CLK clock tree node pub fn rtc_fast_clk(&self) -> Option { self.rtc_fast_clk @@ -2634,6 +2701,7 @@ macro_rules! define_clock_tree_types { cpu_clk: None, rc_fast_clk_div_n: None, rtc_slow_clk: None, + ble_lp_clk: None, rtc_fast_clk: None, low_power_clk: None, timg_calibration_clock: None, @@ -2658,6 +2726,8 @@ macro_rules! define_clock_tree_types { crypto_pwm_clk_refcount: 0, pll_d2_refcount: 0, pll_160m_refcount: 0, + rtc_slow_clk_refcount: 0, + ble_lp_clk_refcount: 0, rtc_fast_clk_refcount: 0, low_power_clk_refcount: 0, uart_mem_clk_refcount: 0, @@ -2693,6 +2763,8 @@ macro_rules! define_clock_tree_types { ::core::sync::atomic::AtomicU32::new(0); static RTC_SLOW_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = ::core::sync::atomic::AtomicU32::new(0); + static BLE_LP_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = + ::core::sync::atomic::AtomicU32::new(0); static RTC_FAST_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = ::core::sync::atomic::AtomicU32::new(0); static LOW_POWER_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = @@ -3272,20 +3344,24 @@ macro_rules! define_clock_tree_types { pub fn configure_rtc_slow_clk(clocks: &mut ClockTree, new_selector: RtcSlowClkConfig) { let old_selector = clocks.rtc_slow_clk.replace(new_selector); refresh_rtc_slow_clk_downstream(clocks); - match new_selector { - #[cfg(use_xtal32k)] - RtcSlowClkConfig::Xtal32k => request_xtal32k_clk(clocks), - RtcSlowClkConfig::RcSlow => request_rc_slow_clk(clocks), - RtcSlowClkConfig::RcFast => request_rc_fast_div_clk(clocks), - } - configure_rtc_slow_clk_impl(clocks, old_selector, new_selector); - if let Some(old_selector) = old_selector { - match old_selector { + if clocks.rtc_slow_clk_refcount > 0 { + match new_selector { #[cfg(use_xtal32k)] - RtcSlowClkConfig::Xtal32k => release_xtal32k_clk(clocks), - RtcSlowClkConfig::RcSlow => release_rc_slow_clk(clocks), - RtcSlowClkConfig::RcFast => release_rc_fast_div_clk(clocks), + RtcSlowClkConfig::Xtal32k => request_xtal32k_clk(clocks), + RtcSlowClkConfig::RcSlow => request_rc_slow_clk(clocks), + RtcSlowClkConfig::RcFast => request_rc_fast_div_clk(clocks), } + configure_rtc_slow_clk_impl(clocks, old_selector, new_selector); + if let Some(old_selector) = old_selector { + match old_selector { + #[cfg(use_xtal32k)] + RtcSlowClkConfig::Xtal32k => release_xtal32k_clk(clocks), + RtcSlowClkConfig::RcSlow => release_rc_slow_clk(clocks), + RtcSlowClkConfig::RcFast => release_rc_fast_div_clk(clocks), + } + } + } else { + configure_rtc_slow_clk_impl(clocks, old_selector, new_selector); } } pub fn rtc_slow_clk_config(clocks: &mut ClockTree) -> Option { @@ -3293,24 +3369,28 @@ macro_rules! define_clock_tree_types { } pub fn request_rtc_slow_clk(clocks: &mut ClockTree) { trace!("Requesting RTC_SLOW_CLK"); - trace!("Enabling RTC_SLOW_CLK"); - match unwrap!(clocks.rtc_slow_clk) { - #[cfg(use_xtal32k)] - RtcSlowClkConfig::Xtal32k => request_xtal32k_clk(clocks), - RtcSlowClkConfig::RcSlow => request_rc_slow_clk(clocks), - RtcSlowClkConfig::RcFast => request_rc_fast_div_clk(clocks), + if increment_reference_count(&mut clocks.rtc_slow_clk_refcount) { + trace!("Enabling RTC_SLOW_CLK"); + match unwrap!(clocks.rtc_slow_clk) { + #[cfg(use_xtal32k)] + RtcSlowClkConfig::Xtal32k => request_xtal32k_clk(clocks), + RtcSlowClkConfig::RcSlow => request_rc_slow_clk(clocks), + RtcSlowClkConfig::RcFast => request_rc_fast_div_clk(clocks), + } + enable_rtc_slow_clk_impl(clocks, true); } - enable_rtc_slow_clk_impl(clocks, true); } pub fn release_rtc_slow_clk(clocks: &mut ClockTree) { trace!("Releasing RTC_SLOW_CLK"); - trace!("Disabling RTC_SLOW_CLK"); - enable_rtc_slow_clk_impl(clocks, false); - match unwrap!(clocks.rtc_slow_clk) { - #[cfg(use_xtal32k)] - RtcSlowClkConfig::Xtal32k => release_xtal32k_clk(clocks), - RtcSlowClkConfig::RcSlow => release_rc_slow_clk(clocks), - RtcSlowClkConfig::RcFast => release_rc_fast_div_clk(clocks), + if decrement_reference_count(&mut clocks.rtc_slow_clk_refcount) { + trace!("Disabling RTC_SLOW_CLK"); + enable_rtc_slow_clk_impl(clocks, false); + match unwrap!(clocks.rtc_slow_clk) { + #[cfg(use_xtal32k)] + RtcSlowClkConfig::Xtal32k => release_xtal32k_clk(clocks), + RtcSlowClkConfig::RcSlow => release_rc_slow_clk(clocks), + RtcSlowClkConfig::RcFast => release_rc_fast_div_clk(clocks), + } } } #[allow(unused_variables)] @@ -3336,6 +3416,66 @@ macro_rules! define_clock_tree_types { RtcSlowClkConfig::RcFast => rc_fast_div_clk_frequency(), } } + pub fn configure_ble_lp_clk(clocks: &mut ClockTree, config: BleLpClkConfig) { + let old_config = clocks.ble_lp_clk.replace(config); + refresh_ble_lp_clk_downstream(clocks); + if clocks.ble_lp_clk_refcount > 0 { + match config.sclk { + BleLpClkSclk::Xtal => request_xtal_clk(clocks), + BleLpClkSclk::RtcSlow => request_rtc_slow_clk(clocks), + } + configure_ble_lp_clk_impl(clocks, old_config, config); + if let Some(old_config) = old_config { + match old_config.sclk { + BleLpClkSclk::Xtal => release_xtal_clk(clocks), + BleLpClkSclk::RtcSlow => release_rtc_slow_clk(clocks), + } + } + } else { + configure_ble_lp_clk_impl(clocks, old_config, config); + } + } + pub fn ble_lp_clk_config(clocks: &mut ClockTree) -> Option { + clocks.ble_lp_clk + } + pub fn request_ble_lp_clk(clocks: &mut ClockTree) { + trace!("Requesting BLE_LP_CLK"); + if increment_reference_count(&mut clocks.ble_lp_clk_refcount) { + trace!("Enabling BLE_LP_CLK"); + match unwrap!(clocks.ble_lp_clk).sclk { + BleLpClkSclk::Xtal => request_xtal_clk(clocks), + BleLpClkSclk::RtcSlow => request_rtc_slow_clk(clocks), + } + enable_ble_lp_clk_impl(clocks, true); + } + } + pub fn release_ble_lp_clk(clocks: &mut ClockTree) { + trace!("Releasing BLE_LP_CLK"); + if decrement_reference_count(&mut clocks.ble_lp_clk_refcount) { + trace!("Disabling BLE_LP_CLK"); + enable_ble_lp_clk_impl(clocks, false); + match unwrap!(clocks.ble_lp_clk).sclk { + BleLpClkSclk::Xtal => release_xtal_clk(clocks), + BleLpClkSclk::RtcSlow => release_rtc_slow_clk(clocks), + } + } + } + #[allow(unused_variables)] + pub fn ble_lp_clk_config_frequency(clocks: &mut ClockTree, config: BleLpClkConfig) -> u32 { + (match config.sclk { + BleLpClkSclk::Xtal => xtal_clk_frequency(), + BleLpClkSclk::RtcSlow => rtc_slow_clk_frequency(), + } / (config.divisor() + 1)) + } + pub fn ble_lp_clk_frequency() -> u32 { + BLE_LP_CLK_FREQ_CACHE.load(::core::sync::atomic::Ordering::Acquire) + } + pub fn ble_lp_clk_source_frequency(sclk: BleLpClkSclk) -> u32 { + match sclk { + BleLpClkSclk::Xtal => xtal_clk_frequency(), + BleLpClkSclk::RtcSlow => rtc_slow_clk_frequency(), + } + } pub fn configure_rtc_fast_clk(clocks: &mut ClockTree, new_selector: RtcFastClkConfig) { let old_selector = clocks.rtc_fast_clk.replace(new_selector); refresh_rtc_fast_clk_downstream(clocks); @@ -4485,6 +4625,8 @@ macro_rules! define_clock_tree_types { pub rc_fast_clk_div_n: Option, /// `RTC_SLOW_CLK` configuration. pub rtc_slow_clk: Option, + /// `BLE_LP_CLK` configuration. + pub ble_lp_clk: Option, /// `RTC_FAST_CLK` configuration. pub rtc_fast_clk: Option, /// `LOW_POWER_CLK` configuration. @@ -4515,6 +4657,9 @@ macro_rules! define_clock_tree_types { if let Some(config) = self.rtc_slow_clk { configure_rtc_slow_clk(clocks, config); } + if let Some(config) = self.ble_lp_clk { + configure_ble_lp_clk(clocks, config); + } if let Some(config) = self.rtc_fast_clk { configure_rtc_fast_clk(clocks, config); } @@ -4545,6 +4690,7 @@ macro_rules! define_clock_tree_types { } refresh_pll_clk_downstream(clocks); refresh_system_pre_div_in_downstream(clocks); + refresh_ble_lp_clk_downstream(clocks); refresh_rtc_fast_clk_downstream(clocks); refresh_low_power_clk_downstream(clocks); for child_instance in [I2cInstance::I2c0, I2cInstance::I2c1] { @@ -4653,8 +4799,17 @@ macro_rules! define_clock_tree_types { ::core::sync::atomic::Ordering::Release, ); } + refresh_ble_lp_clk_downstream(clocks); refresh_low_power_clk_downstream(clocks); } + fn refresh_ble_lp_clk_downstream(clocks: &mut ClockTree) { + if let Some(config) = clocks.ble_lp_clk { + BLE_LP_CLK_FREQ_CACHE.store( + ble_lp_clk_config_frequency(clocks, config), + ::core::sync::atomic::Ordering::Release, + ); + } + } fn refresh_rtc_fast_clk_downstream(clocks: &mut ClockTree) { if let Some(config) = clocks.rtc_fast_clk { RTC_FAST_CLK_FREQ_CACHE.store( diff --git a/esp-metadata-generated/src/_generated_esp32s31.rs b/esp-metadata-generated/src/_generated_esp32s31.rs index 422cfa27577..5e7f005733d 100644 --- a/esp-metadata-generated/src/_generated_esp32s31.rs +++ b/esp-metadata-generated/src/_generated_esp32s31.rs @@ -331,6 +331,9 @@ macro_rules! property { ("usb_otg_hs.fifo_depth_words", str) => { stringify!(896) }; + ("bt.controller") => { + "btdm2" + }; ("wifi.has_wifi6") => { true }; @@ -585,6 +588,15 @@ macro_rules! property { [crate ::soc::clocks::LpSlowClkConfig::RcSlow, #[cfg(use_xtal32k)] crate ::soc::clocks::LpSlowClkConfig::Xtal32k] }; + ("clock_tree.ble_lp_clk.sclk") => { + [crate ::soc::clocks::BleLpClkSclk::Xtal, crate + ::soc::clocks::BleLpClkSclk::RcSlow, #[cfg(use_xtal32k)] crate + ::soc::clocks::BleLpClkSclk::Xtal32k, crate ::soc::clocks::BleLpClkSclk::Rc32k, + crate ::soc::clocks::BleLpClkSclk::Ext32k] + }; + ("clock_tree.ble_lp_clk.divisor") => { + (0, 4095) + }; ("clock_tree.crypto_clk") => { [crate ::soc::clocks::CryptoClkConfig::Xtal, crate ::soc::clocks::CryptoClkConfig::RcFast, crate @@ -1603,6 +1615,18 @@ macro_rules! for_each_sw_interrupt { /// todo!() /// } /// +/// // OSC_SLOW_CLK +/// +/// fn enable_osc_slow_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// // RC32K_CLK +/// +/// fn enable_rc32k_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// /// // RC_SLOW_CLK /// /// fn enable_rc_slow_clk_impl(_clocks: &mut ClockTree, _en: bool) { @@ -1757,6 +1781,20 @@ macro_rules! for_each_sw_interrupt { /// todo!() /// } /// +/// // BLE_LP_CLK +/// +/// fn enable_ble_lp_clk_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_ble_lp_clk_impl( +/// _clocks: &mut ClockTree, +/// _old_config: Option, +/// _new_config: BleLpClkConfig, +/// ) { +/// todo!() +/// } +/// /// // CRYPTO_CLK /// /// fn enable_crypto_clk_impl(_clocks: &mut ClockTree, _en: bool) { @@ -2101,7 +2139,7 @@ macro_rules! define_clock_tree_types { ); Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -2127,7 +2165,7 @@ macro_rules! define_clock_tree_types { ); Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -2153,7 +2191,7 @@ macro_rules! define_clock_tree_types { ); Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -2179,7 +2217,7 @@ macro_rules! define_clock_tree_types { ); Self { divisor } } - pub(crate) fn divisor(self) -> u32 { + pub fn divisor(self) -> u32 { self.divisor as u32 } } @@ -2202,6 +2240,52 @@ macro_rules! define_clock_tree_types { /// Selects `XTAL32K_CLK`. Xtal32k, } + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum BleLpClkSclk { + #[default] + /// Selects `XTAL_CLK`. + Xtal, + /// Selects `RC_SLOW_CLK`. + RcSlow, + #[cfg(use_xtal32k)] + /// Selects `XTAL32K_CLK`. + Xtal32k, + /// Selects `RC32K_CLK`. + Rc32k, + /// Selects `OSC_SLOW_CLK`. + Ext32k, + } + /// Configures the `BLE_LP_CLK` clock node. + /// + /// The output is calculated as `OUTPUT = sclk / (divisor + 1)`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct BleLpClkConfig { + sclk: BleLpClkSclk, + divisor: u32, + } + impl BleLpClkConfig { + /// Creates a new configuration for the BLE_LP_CLK clock node. + /// + /// ## Panics + /// + /// Panics if the divisor value is outside the + /// valid range (0 ..= 4095). + pub const fn new(sclk: BleLpClkSclk, divisor: u32) -> Self { + ::core::assert!( + divisor <= 4095, + "`BLE_LP_CLK` divisor must be between 0 and 4095 (inclusive)." + ); + Self { sclk, divisor } + } + pub fn sclk(self) -> BleLpClkSclk { + self.sclk + } + pub fn divisor(self) -> u32 { + self.divisor as u32 + } + } /// The list of clock signals that the `CRYPTO_CLK` multiplexer can output. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -2245,10 +2329,10 @@ macro_rules! define_clock_tree_types { ); Self { source, div_num } } - pub(crate) fn source(self) -> IomuxFunctionClockSource { + pub fn source(self) -> IomuxFunctionClockSource { self.source } - pub(crate) fn div_num(self) -> u32 { + pub fn div_num(self) -> u32 { self.div_num as u32 } } @@ -2296,10 +2380,10 @@ macro_rules! define_clock_tree_types { ); Self { sclk, div_num } } - pub(crate) fn sclk(self) -> I2cFunctionClockSclk { + pub fn sclk(self) -> I2cFunctionClockSclk { self.sclk } - pub(crate) fn div_num(self) -> u32 { + pub fn div_num(self) -> u32 { self.div_num as u32 } } @@ -2354,16 +2438,16 @@ macro_rules! define_clock_tree_types { div_b, } } - pub(crate) fn sclk(self) -> I2sClkSclk { + pub fn sclk(self) -> I2sClkSclk { self.sclk } - pub(crate) fn div_num(self) -> u32 { + pub fn div_num(self) -> u32 { self.div_num as u32 } - pub(crate) fn div_a(self) -> u32 { + pub fn div_a(self) -> u32 { self.div_a as u32 } - pub(crate) fn div_b(self) -> u32 { + pub fn div_b(self) -> u32 { self.div_b as u32 } } @@ -2435,16 +2519,16 @@ macro_rules! define_clock_tree_types { div_b, } } - pub(crate) fn sclk(self) -> LcdCamLcdClockSclk { + pub fn sclk(self) -> LcdCamLcdClockSclk { self.sclk } - pub(crate) fn div_num(self) -> u32 { + pub fn div_num(self) -> u32 { self.div_num as u32 } - pub(crate) fn div_a(self) -> u32 { + pub fn div_a(self) -> u32 { self.div_a as u32 } - pub(crate) fn div_b(self) -> u32 { + pub fn div_b(self) -> u32 { self.div_b as u32 } } @@ -2506,16 +2590,16 @@ macro_rules! define_clock_tree_types { div_b, } } - pub(crate) fn sclk(self) -> LcdCamCamClockSclk { + pub fn sclk(self) -> LcdCamCamClockSclk { self.sclk } - pub(crate) fn div_num(self) -> u32 { + pub fn div_num(self) -> u32 { self.div_num as u32 } - pub(crate) fn div_a(self) -> u32 { + pub fn div_a(self) -> u32 { self.div_a as u32 } - pub(crate) fn div_b(self) -> u32 { + pub fn div_b(self) -> u32 { self.div_b as u32 } } @@ -2601,10 +2685,10 @@ macro_rules! define_clock_tree_types { ); Self { sclk, div_num } } - pub(crate) fn sclk(self) -> UartFunctionClockSclk { + pub fn sclk(self) -> UartFunctionClockSclk { self.sclk } - pub(crate) fn div_num(self) -> u32 { + pub fn div_num(self) -> u32 { self.div_num as u32 } } @@ -2642,10 +2726,10 @@ macro_rules! define_clock_tree_types { integral, } } - pub(crate) fn fractional(self) -> u32 { + pub fn fractional(self) -> u32 { self.fractional as u32 } - pub(crate) fn integral(self) -> u32 { + pub fn integral(self) -> u32 { self.integral as u32 } } @@ -2670,6 +2754,7 @@ macro_rules! define_clock_tree_types { apb_clk: Option, lp_fast_clk: Option, lp_slow_clk: Option, + ble_lp_clk: Option, crypto_clk: Option, iomux_function_clock: Option, timg_calibration_clock: Option, @@ -2706,6 +2791,7 @@ macro_rules! define_clock_tree_types { mem_clk_refcount: u32, lp_fast_clk_refcount: u32, lp_slow_clk_refcount: u32, + ble_lp_clk_refcount: u32, crypto_clk_refcount: u32, timg_calibration_clock_refcount: u32, sdm_function_clock_refcount: [u32; 1], @@ -2757,6 +2843,10 @@ macro_rules! define_clock_tree_types { pub fn lp_slow_clk(&self) -> Option { self.lp_slow_clk } + /// Returns the current configuration of the BLE_LP_CLK clock tree node + pub fn ble_lp_clk(&self) -> Option { + self.ble_lp_clk + } /// Returns the current configuration of the CRYPTO_CLK clock tree node pub fn crypto_clk(&self) -> Option { self.crypto_clk @@ -2883,6 +2973,7 @@ macro_rules! define_clock_tree_types { apb_clk: None, lp_fast_clk: None, lp_slow_clk: None, + ble_lp_clk: None, crypto_clk: None, iomux_function_clock: None, timg_calibration_clock: None, @@ -2919,6 +3010,7 @@ macro_rules! define_clock_tree_types { mem_clk_refcount: 0, lp_fast_clk_refcount: 0, lp_slow_clk_refcount: 0, + ble_lp_clk_refcount: 0, crypto_clk_refcount: 0, timg_calibration_clock_refcount: 0, sdm_function_clock_refcount: [0; 1], @@ -2951,6 +3043,8 @@ macro_rules! define_clock_tree_types { ::core::sync::atomic::AtomicU32::new(0); static LP_SLOW_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = ::core::sync::atomic::AtomicU32::new(0); + static BLE_LP_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = + ::core::sync::atomic::AtomicU32::new(0); static CRYPTO_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = ::core::sync::atomic::AtomicU32::new(0); static IOMUX_FUNCTION_CLOCK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = @@ -3091,6 +3185,32 @@ macro_rules! define_clock_tree_types { pub fn xtal32k_clk_frequency() -> u32 { 32768 } + pub fn request_osc_slow_clk(clocks: &mut ClockTree) { + trace!("Requesting OSC_SLOW_CLK"); + trace!("Enabling OSC_SLOW_CLK"); + enable_osc_slow_clk_impl(clocks, true); + } + pub fn release_osc_slow_clk(clocks: &mut ClockTree) { + trace!("Releasing OSC_SLOW_CLK"); + trace!("Disabling OSC_SLOW_CLK"); + enable_osc_slow_clk_impl(clocks, false); + } + pub fn osc_slow_clk_frequency() -> u32 { + 32768 + } + pub fn request_rc32k_clk(clocks: &mut ClockTree) { + trace!("Requesting RC32K_CLK"); + trace!("Enabling RC32K_CLK"); + enable_rc32k_clk_impl(clocks, true); + } + pub fn release_rc32k_clk(clocks: &mut ClockTree) { + trace!("Releasing RC32K_CLK"); + trace!("Disabling RC32K_CLK"); + enable_rc32k_clk_impl(clocks, false); + } + pub fn rc32k_clk_frequency() -> u32 { + 32000 + } pub fn request_rc_slow_clk(clocks: &mut ClockTree) { trace!("Requesting RC_SLOW_CLK"); if increment_reference_count(&mut clocks.rc_slow_clk_refcount) { @@ -3623,6 +3743,90 @@ macro_rules! define_clock_tree_types { LpSlowClkConfig::Xtal32k => xtal32k_clk_frequency(), } } + pub fn configure_ble_lp_clk(clocks: &mut ClockTree, config: BleLpClkConfig) { + let old_config = clocks.ble_lp_clk.replace(config); + refresh_ble_lp_clk_downstream(clocks); + if clocks.ble_lp_clk_refcount > 0 { + match config.sclk { + BleLpClkSclk::Xtal => request_xtal_clk(clocks), + BleLpClkSclk::RcSlow => request_rc_slow_clk(clocks), + #[cfg(use_xtal32k)] + BleLpClkSclk::Xtal32k => request_xtal32k_clk(clocks), + BleLpClkSclk::Rc32k => request_rc32k_clk(clocks), + BleLpClkSclk::Ext32k => request_osc_slow_clk(clocks), + } + configure_ble_lp_clk_impl(clocks, old_config, config); + if let Some(old_config) = old_config { + match old_config.sclk { + BleLpClkSclk::Xtal => release_xtal_clk(clocks), + BleLpClkSclk::RcSlow => release_rc_slow_clk(clocks), + #[cfg(use_xtal32k)] + BleLpClkSclk::Xtal32k => release_xtal32k_clk(clocks), + BleLpClkSclk::Rc32k => release_rc32k_clk(clocks), + BleLpClkSclk::Ext32k => release_osc_slow_clk(clocks), + } + } + } else { + configure_ble_lp_clk_impl(clocks, old_config, config); + } + } + pub fn ble_lp_clk_config(clocks: &mut ClockTree) -> Option { + clocks.ble_lp_clk + } + pub fn request_ble_lp_clk(clocks: &mut ClockTree) { + trace!("Requesting BLE_LP_CLK"); + if increment_reference_count(&mut clocks.ble_lp_clk_refcount) { + trace!("Enabling BLE_LP_CLK"); + match unwrap!(clocks.ble_lp_clk).sclk { + BleLpClkSclk::Xtal => request_xtal_clk(clocks), + BleLpClkSclk::RcSlow => request_rc_slow_clk(clocks), + #[cfg(use_xtal32k)] + BleLpClkSclk::Xtal32k => request_xtal32k_clk(clocks), + BleLpClkSclk::Rc32k => request_rc32k_clk(clocks), + BleLpClkSclk::Ext32k => request_osc_slow_clk(clocks), + } + enable_ble_lp_clk_impl(clocks, true); + } + } + pub fn release_ble_lp_clk(clocks: &mut ClockTree) { + trace!("Releasing BLE_LP_CLK"); + if decrement_reference_count(&mut clocks.ble_lp_clk_refcount) { + trace!("Disabling BLE_LP_CLK"); + enable_ble_lp_clk_impl(clocks, false); + match unwrap!(clocks.ble_lp_clk).sclk { + BleLpClkSclk::Xtal => release_xtal_clk(clocks), + BleLpClkSclk::RcSlow => release_rc_slow_clk(clocks), + #[cfg(use_xtal32k)] + BleLpClkSclk::Xtal32k => release_xtal32k_clk(clocks), + BleLpClkSclk::Rc32k => release_rc32k_clk(clocks), + BleLpClkSclk::Ext32k => release_osc_slow_clk(clocks), + } + } + } + #[allow(unused_variables)] + pub fn ble_lp_clk_config_frequency(clocks: &mut ClockTree, config: BleLpClkConfig) -> u32 { + (match config.sclk { + BleLpClkSclk::Xtal => xtal_clk_frequency(), + BleLpClkSclk::RcSlow => rc_slow_clk_frequency(), + #[cfg(use_xtal32k)] + BleLpClkSclk::Xtal32k => xtal32k_clk_frequency(), + BleLpClkSclk::Rc32k => rc32k_clk_frequency(), + BleLpClkSclk::Ext32k => osc_slow_clk_frequency(), + } / (config.divisor() + 1)) + } + pub fn ble_lp_clk_frequency() -> u32 { + BLE_LP_CLK_FREQ_CACHE.load(::core::sync::atomic::Ordering::Acquire) + } + pub fn ble_lp_clk_source_frequency(sclk: BleLpClkSclk) -> u32 { + match sclk { + BleLpClkSclk::Xtal => xtal_clk_frequency(), + BleLpClkSclk::RcSlow => rc_slow_clk_frequency(), + #[cfg(use_xtal32k)] + BleLpClkSclk::Xtal32k => xtal32k_clk_frequency(), + BleLpClkSclk::Rc32k => rc32k_clk_frequency(), + BleLpClkSclk::Ext32k => osc_slow_clk_frequency(), + } + } pub fn configure_crypto_clk(clocks: &mut ClockTree, new_selector: CryptoClkConfig) { let old_selector = clocks.crypto_clk.replace(new_selector); refresh_crypto_clk_downstream(clocks); @@ -4781,6 +4985,8 @@ macro_rules! define_clock_tree_types { pub lp_fast_clk: Option, /// `LP_SLOW_CLK` configuration. pub lp_slow_clk: Option, + /// `BLE_LP_CLK` configuration. + pub ble_lp_clk: Option, /// `CRYPTO_CLK` configuration. pub crypto_clk: Option, /// `IOMUX_FUNCTION_CLOCK` configuration. @@ -4811,6 +5017,9 @@ macro_rules! define_clock_tree_types { if let Some(config) = self.lp_slow_clk { configure_lp_slow_clk(clocks, config); } + if let Some(config) = self.ble_lp_clk { + configure_ble_lp_clk(clocks, config); + } if let Some(config) = self.crypto_clk { configure_crypto_clk(clocks, config); } @@ -4892,6 +5101,14 @@ macro_rules! define_clock_tree_types { ); } } + fn refresh_ble_lp_clk_downstream(clocks: &mut ClockTree) { + if let Some(config) = clocks.ble_lp_clk { + BLE_LP_CLK_FREQ_CACHE.store( + ble_lp_clk_config_frequency(clocks, config), + ::core::sync::atomic::Ordering::Release, + ); + } + } fn refresh_crypto_clk_downstream(clocks: &mut ClockTree) { if let Some(config) = clocks.crypto_clk { CRYPTO_CLK_FREQ_CACHE.store( @@ -6148,6 +6365,11 @@ macro_rules! for_each_peripheral { "ADC1 peripheral singleton"] ADC1 <= virtual() (unstable))); _for_each_inner_peripheral!((@ peri_type #[doc = "ADC2 peripheral singleton"] ADC2 <= virtual() (unstable))); _for_each_inner_peripheral!((@ peri_type #[doc = + "BT peripheral singleton"] BT <= virtual(MODEM_BT_BB : { bind_bb_interrupt, + enable_bb_interrupt, disable_bb_interrupt }, MODEM_LP_TIMER : { + bind_lp_timer_interrupt, enable_lp_timer_interrupt, disable_lp_timer_interrupt }, + MODEM_BT_MAC : { bind_mac_interrupt, enable_mac_interrupt, disable_mac_interrupt + }) (unstable))); _for_each_inner_peripheral!((@ peri_type #[doc = "FLASH peripheral singleton"] FLASH <= virtual() (unstable))); _for_each_inner_peripheral!((@ peri_type #[doc = "PSRAM peripheral singleton"] PSRAM <= virtual() (unstable))); _for_each_inner_peripheral!((@ peri_type #[doc = @@ -6286,6 +6508,7 @@ macro_rules! for_each_peripheral { _for_each_inner_peripheral!((USB_HS(unstable))); _for_each_inner_peripheral!((ADC1(unstable))); _for_each_inner_peripheral!((ADC2(unstable))); + _for_each_inner_peripheral!((BT(unstable))); _for_each_inner_peripheral!((FLASH(unstable))); _for_each_inner_peripheral!((PSRAM(unstable))); _for_each_inner_peripheral!((GPIO_DEDICATED(unstable))); @@ -6632,9 +6855,14 @@ macro_rules! for_each_peripheral { bind_peri_interrupt, enable_peri_interrupt, disable_peri_interrupt }) (unstable)), (@ peri_type #[doc = "ADC1 peripheral singleton"] ADC1 <= virtual() (unstable)), (@ peri_type #[doc = "ADC2 peripheral singleton"] ADC2 <= virtual() - (unstable)), (@ peri_type #[doc = "FLASH peripheral singleton"] FLASH <= - virtual() (unstable)), (@ peri_type #[doc = "PSRAM peripheral singleton"] PSRAM - <= virtual() (unstable)), (@ peri_type #[doc = + (unstable)), (@ peri_type #[doc = "BT peripheral singleton"] BT <= + virtual(MODEM_BT_BB : { bind_bb_interrupt, enable_bb_interrupt, + disable_bb_interrupt }, MODEM_LP_TIMER : { bind_lp_timer_interrupt, + enable_lp_timer_interrupt, disable_lp_timer_interrupt }, MODEM_BT_MAC : { + bind_mac_interrupt, enable_mac_interrupt, disable_mac_interrupt }) (unstable)), + (@ peri_type #[doc = "FLASH peripheral singleton"] FLASH <= virtual() + (unstable)), (@ peri_type #[doc = "PSRAM peripheral singleton"] PSRAM <= + virtual() (unstable)), (@ peri_type #[doc = "GPIO_DEDICATED peripheral singleton"] GPIO_DEDICATED <= virtual() (unstable)), (@ peri_type #[doc = "WIFI peripheral singleton"] WIFI <= virtual(MODEM_WIFI_BB : { bind_bb_interrupt, enable_bb_interrupt, disable_bb_interrupt }, MODEM_WIFI_MAC @@ -6683,15 +6911,15 @@ macro_rules! for_each_peripheral { (DMA(unstable)), (SHA(unstable)), (SYSTEM(unstable)), (SYSTIMER(unstable)), (SDHOST(unstable)), (TEE(unstable)), (TIMG0(unstable)), (TIMG1(unstable)), (UART0), (UART1), (UART2), (UART3), (UHCI0(unstable)), (USB_DEVICE(unstable)), - (USB_HS(unstable)), (ADC1(unstable)), (ADC2(unstable)), (FLASH(unstable)), - (PSRAM(unstable)), (GPIO_DEDICATED(unstable)), (WIFI), (CPU_CTRL(unstable)), - (IPC(unstable)), (FROM_CPU_INTR0(unstable)), (FROM_CPU_INTR1(unstable)), - (FROM_CPU_INTR2(unstable)), (FROM_CPU_INTR3(unstable)))); - _for_each_inner_peripheral!((dma_eligible(UHCI0, Uhci0, 0, AhbGdmaChannel), - (LCD_CAM, LcdCam, 0, AxiGdmaChannel), (I2S0, I2s0, 1, AhbGdmaChannel), (SPI2, - Spi2, 1, AxiGdmaChannel), (SPI3, Spi3, 2, AxiGdmaChannel), (AES, Aes, 4, - AxiGdmaChannel), (I2S1, I2s1, 5, AhbGdmaChannel), (SHA, Sha, 5, - AxiGdmaChannel))); + (USB_HS(unstable)), (ADC1(unstable)), (ADC2(unstable)), (BT(unstable)), + (FLASH(unstable)), (PSRAM(unstable)), (GPIO_DEDICATED(unstable)), (WIFI), + (CPU_CTRL(unstable)), (IPC(unstable)), (FROM_CPU_INTR0(unstable)), + (FROM_CPU_INTR1(unstable)), (FROM_CPU_INTR2(unstable)), + (FROM_CPU_INTR3(unstable)))); _for_each_inner_peripheral!((dma_eligible(UHCI0, + Uhci0, 0, AhbGdmaChannel), (LCD_CAM, LcdCam, 0, AxiGdmaChannel), (I2S0, I2s0, 1, + AhbGdmaChannel), (SPI2, Spi2, 1, AxiGdmaChannel), (SPI3, Spi3, 2, + AxiGdmaChannel), (AES, Aes, 4, AxiGdmaChannel), (I2S1, I2s1, 5, AhbGdmaChannel), + (SHA, Sha, 5, AxiGdmaChannel))); }; } /// This macro can be used to generate code for each `GPIOn` instance. diff --git a/esp-metadata/devices/esp32/clocks.toml b/esp-metadata/devices/esp32/clocks.toml index 28884f0ed58..65561c188b7 100644 --- a/esp-metadata/devices/esp32/clocks.toml +++ b/esp-metadata/devices/esp32/clocks.toml @@ -114,6 +114,12 @@ system_clocks = { clock_tree = [ { name = "RC_SLOW", outputs = "RC_SLOW_CLK" }, { name = "RC_FAST", outputs = "RC_FAST_DIV_CLK" }, ] }, + # BLE modem-sleep clock. The blob selects divided XTAL (1 MHz) or RTC_SLOW_CLK. + # The BTDM blob programs this divider. The stored value is the field the blob writes. + { name = "BLE_LP_CLK", type = "generic", output = "sclk / (divisor + 1)", params = { sclk = [ + { name = "XTAL", outputs = "XTAL_CLK", default = true }, + { name = "RTC_SLOW", outputs = "RTC_SLOW_CLK" }, + ], divisor = "0 ..= 255" } }, { name = "RTC_FAST_CLK", type = "mux", variants = [ { name = "XTAL", outputs = "XTAL_DIV_CLK" }, { name = "RC", outputs = "RC_FAST_CLK" }, diff --git a/esp-metadata/devices/esp32c2/clocks.toml b/esp-metadata/devices/esp32c2/clocks.toml index 16555fc154d..05fe22913b5 100644 --- a/esp-metadata/devices/esp32c2/clocks.toml +++ b/esp-metadata/devices/esp32c2/clocks.toml @@ -52,6 +52,14 @@ system_clocks = { clock_tree = [ { name = "RC_SLOW", outputs = "RC_SLOW_CLK" }, { name = "RC_FAST", outputs = "RC_FAST_DIV_CLK" }, ] }, + # BLE RTC timer. Divided main XTAL, RC slow, or the external slow oscillator. + # The crystal input of the BLE timer is divided by 5 in hardware (MODEM_CLKRST). + { name = "BLE_LP_XTAL_CLK", type = "generic", output = "XTAL_CLK / 5" }, + { name = "BLE_LP_CLK", type = "generic", output = "sclk / (divisor + 1)", params = { sclk = [ + { name = "XTAL", outputs = "BLE_LP_XTAL_CLK", default = true }, + { name = "RC_SLOW", outputs = "RC_SLOW_CLK" }, + { name = "OSC_SLOW", outputs = "OSC_SLOW_CLK" }, + ], divisor = "0 ..= 255" } }, { name = "RTC_FAST_CLK", type = "mux", variants = [ # RTC_CNTL_FAST_CLK_RTC_SEL { name = "XTAL", outputs = "XTAL_DIV_CLK" }, { name = "RC", outputs = "RC_FAST_CLK_DIV_N" }, diff --git a/esp-metadata/devices/esp32c3/clocks.toml b/esp-metadata/devices/esp32c3/clocks.toml index 74b19de3eed..0a2bdf911c7 100644 --- a/esp-metadata/devices/esp32c3/clocks.toml +++ b/esp-metadata/devices/esp32c3/clocks.toml @@ -47,6 +47,12 @@ system_clocks = { clock_tree = [ { name = "RC_SLOW", outputs = "RC_SLOW_CLK" }, { name = "RC_FAST", outputs = "RC_FAST_DIV_CLK" }, ] }, + # BLE modem-sleep clock. The blob selects divided XTAL (1 MHz) or RTC_SLOW_CLK. + # The BTDM blob programs this divider. The stored value is the field written as `divisor + 1`. + { name = "BLE_LP_CLK", type = "generic", output = "sclk / (divisor + 1)", params = { sclk = [ + { name = "XTAL", outputs = "XTAL_CLK", default = true }, + { name = "RTC_SLOW", outputs = "RTC_SLOW_CLK" }, + ], divisor = "0 ..= 255" } }, { name = "RTC_FAST_CLK", type = "mux", variants = [ { name = "XTAL", outputs = "XTAL_DIV_CLK" }, { name = "RC", outputs = "RC_FAST_CLK_DIV_N" }, diff --git a/esp-metadata/devices/esp32c5/clocks.toml b/esp-metadata/devices/esp32c5/clocks.toml index 7fbdb036f7d..d45502f5d29 100644 --- a/esp-metadata/devices/esp32c5/clocks.toml +++ b/esp-metadata/devices/esp32c5/clocks.toml @@ -7,6 +7,7 @@ system_clocks = { clock_tree = [ # Low-speed clocks { name = "XTAL32K_CLK", type = "source", output = "32768", cfg = "use_xtal32k" }, { name = "OSC_SLOW_CLK", type = "source", output = "32768" }, + { name = "RC32K_CLK", type = "source", output = "32000" }, { name = "RC_SLOW_CLK", type = "source", output = "130_000" }, # { name = "EXT32K_CLK", type = "source", output = "32768" }, # currently unimplemented @@ -50,6 +51,15 @@ system_clocks = { clock_tree = [ #{ name = "EXT32K", outputs = "EXT32K_CLK" }, { name = "OSC_SLOW", outputs = "OSC_SLOW_CLK" }, ] }, + # BLE RTC timer (MODEM_LPCON). Independent of LP_SLOW_CLK. + # BLE RTC timer (MODEM_LPCON). Independent of LP_SLOW_CLK. + { name = "BLE_LP_CLK", type = "generic", output = "sclk / (divisor + 1)", params = { sclk = [ + { name = "XTAL", outputs = "XTAL_CLK", default = true }, + { name = "RC_SLOW", outputs = "RC_SLOW_CLK" }, + { name = "XTAL32K", outputs = "XTAL32K_CLK", cfg = "use_xtal32k" }, + { name = "RC32K", outputs = "RC32K_CLK" }, + { name = "EXT32K", outputs = "OSC_SLOW_CLK" }, + ], divisor = "0 ..= 0xFFF" } }, # Global peripheral-related clocks { name = "CRYPTO_PLL_DIV", type = "generic", output = "PLL_CLK / 3" }, diff --git a/esp-metadata/devices/esp32c6/clocks.toml b/esp-metadata/devices/esp32c6/clocks.toml index 27c51179b4a..7058e5d0d5b 100644 --- a/esp-metadata/devices/esp32c6/clocks.toml +++ b/esp-metadata/devices/esp32c6/clocks.toml @@ -7,6 +7,7 @@ system_clocks = { clock_tree = [ # Low-speed clocks { name = "XTAL32K_CLK", type = "source", output = "32768", cfg = "use_xtal32k" }, { name = "OSC_SLOW_CLK", type = "source", output = "32768" }, + { name = "RC32K_CLK", type = "source", output = "32000" }, { name = "RC_SLOW_CLK", type = "source", output = "136_000" }, { name = "SOC_ROOT_CLK", type = "mux", variants = [ @@ -67,6 +68,17 @@ system_clocks = { clock_tree = [ { name = "RC_SLOW", outputs = "RC_SLOW_CLK" }, { name = "OSC_SLOW", outputs = "OSC_SLOW_CLK" }, ] }, + # BLE RTC timer (MODEM_LPCON). Independent of LP_SLOW_CLK. + # Chip revisions after 0.0 supply the crystal input from the Wi-Fi power domain, which + # divides the crystal by 80. The divisor is not programmable, it follows the revision. + { name = "BLE_LP_XTAL_CLK", type = "generic", params = { divisor = "1, 80" }, output = "XTAL_CLK / divisor" }, + { name = "BLE_LP_CLK", type = "generic", output = "sclk / (divisor + 1)", params = { sclk = [ + { name = "XTAL", outputs = "BLE_LP_XTAL_CLK", default = true }, + { name = "RC_SLOW", outputs = "RC_SLOW_CLK" }, + { name = "XTAL32K", outputs = "XTAL32K_CLK", cfg = "use_xtal32k" }, + { name = "RC32K", outputs = "RC32K_CLK" }, + { name = "EXT32K", outputs = "OSC_SLOW_CLK" }, + ], divisor = "0 ..= 0xFFF" } }, # LP_DYN_SLOW_CLK is LP_SLOW_CLK # LP_DYN_FAST_CLK is automatically managed based on power mode. Not sure how to model it, some LP peripherals need it. diff --git a/esp-metadata/devices/esp32c61/clocks.toml b/esp-metadata/devices/esp32c61/clocks.toml index b329f4991b7..c6479ee162c 100644 --- a/esp-metadata/devices/esp32c61/clocks.toml +++ b/esp-metadata/devices/esp32c61/clocks.toml @@ -7,6 +7,7 @@ system_clocks = { clock_tree = [ # Low-speed clocks { name = "XTAL32K_CLK", type = "source", output = "32768", cfg = "use_xtal32k" }, { name = "OSC_SLOW_CLK", type = "source", output = "32768" }, + { name = "RC32K_CLK", type = "source", output = "32000" }, { name = "RC_SLOW_CLK", type = "source", output = "136_000" }, # PLL divider taps @@ -43,6 +44,15 @@ system_clocks = { clock_tree = [ { name = "XTAL32K", outputs = "XTAL32K_CLK", cfg = "use_xtal32k" }, { name = "OSC_SLOW", outputs = "OSC_SLOW_CLK" }, ] }, + # BLE RTC timer (MODEM_LPCON). Independent of LP_SLOW_CLK. + # BLE RTC timer (MODEM_LPCON). Independent of LP_SLOW_CLK. + { name = "BLE_LP_CLK", type = "generic", output = "sclk / (divisor + 1)", params = { sclk = [ + { name = "XTAL", outputs = "XTAL_CLK", default = true }, + { name = "RC_SLOW", outputs = "RC_SLOW_CLK" }, + { name = "XTAL32K", outputs = "XTAL32K_CLK", cfg = "use_xtal32k" }, + { name = "RC32K", outputs = "RC32K_CLK" }, + { name = "EXT32K", outputs = "OSC_SLOW_CLK" }, + ], divisor = "0 ..= 0xFFF" } }, # Global peripheral-related clocks { name = "CRYPTO_PLL_DIV", type = "generic", output = "PLL_CLK / 3" }, diff --git a/esp-metadata/devices/esp32h2/clocks.toml b/esp-metadata/devices/esp32h2/clocks.toml index e4144a4360b..cdd1f6335fb 100644 --- a/esp-metadata/devices/esp32h2/clocks.toml +++ b/esp-metadata/devices/esp32h2/clocks.toml @@ -14,6 +14,7 @@ system_clocks = { clock_tree = [ # Low-speed clocks { name = "XTAL32K_CLK", type = "source", output = "32768", cfg = "use_xtal32k" }, { name = "OSC_SLOW_CLK", type = "source", output = "32768" }, + { name = "RC32K_CLK", type = "source", output = "32000" }, { name = "RC_SLOW_CLK", type = "source", output = "130_000" }, { name = "PLL_LP_CLK", type = "derived", from = "XTAL32K_CLK", output = "8_000_000", cfg = "use_xtal32k" }, @@ -42,6 +43,15 @@ system_clocks = { clock_tree = [ { name = "RC_SLOW", outputs = "RC_SLOW_CLK" }, { name = "OSC_SLOW", outputs = "OSC_SLOW_CLK" }, ] }, + # BLE RTC timer (LP_CLKRST.lpperi). Independent of LP_SLOW_CLK. + # BLE RTC timer (LP_CLKRST.lpperi). Independent of LP_SLOW_CLK. + { name = "BLE_LP_CLK", type = "generic", output = "sclk / (divisor + 1)", params = { sclk = [ + { name = "XTAL", outputs = "XTAL_CLK", default = true }, + { name = "RC_SLOW", outputs = "RC_SLOW_CLK" }, + { name = "XTAL32K", outputs = "XTAL32K_CLK", cfg = "use_xtal32k" }, + { name = "RC32K", outputs = "RC32K_CLK" }, + { name = "EXT32K", outputs = "OSC_SLOW_CLK" }, + ], divisor = "0 ..= 0xFFF" } }, # LP_DYN_SLOW_CLK is LP_SLOW_CLK # LP_DYN_FAST_CLK is automatically managed based on power mode. Not sure how to model it, some LP peripherals need it. diff --git a/esp-metadata/devices/esp32s3/clocks.toml b/esp-metadata/devices/esp32s3/clocks.toml index f330dc79d11..0b4990e32e1 100644 --- a/esp-metadata/devices/esp32s3/clocks.toml +++ b/esp-metadata/devices/esp32s3/clocks.toml @@ -48,6 +48,12 @@ system_clocks = { clock_tree = [ { name = "RC_SLOW", outputs = "RC_SLOW_CLK" }, { name = "RC_FAST", outputs = "RC_FAST_DIV_CLK" }, ] }, + # BLE modem-sleep clock. The blob selects divided XTAL (1 MHz) or RTC_SLOW_CLK. + # The BTDM blob programs this divider. The stored value is the field written as `divisor + 1`. + { name = "BLE_LP_CLK", type = "generic", output = "sclk / (divisor + 1)", params = { sclk = [ + { name = "XTAL", outputs = "XTAL_CLK", default = true }, + { name = "RTC_SLOW", outputs = "RTC_SLOW_CLK" }, + ], divisor = "0 ..= 255" } }, { name = "RTC_FAST_CLK", type = "mux", variants = [ { name = "XTAL", outputs = "XTAL_DIV_CLK" }, { name = "RC", outputs = "RC_FAST_CLK_DIV_N" }, diff --git a/esp-metadata/devices/esp32s31/clocks.toml b/esp-metadata/devices/esp32s31/clocks.toml index c70dbae103f..db694adfbdf 100644 --- a/esp-metadata/devices/esp32s31/clocks.toml +++ b/esp-metadata/devices/esp32s31/clocks.toml @@ -8,6 +8,8 @@ system_clocks = { clock_tree = [ # Low-speed clocks { name = "XTAL32K_CLK", type = "source", output = "32768", cfg = "use_xtal32k" }, + { name = "OSC_SLOW_CLK", type = "source", output = "32768" }, + { name = "RC32K_CLK", type = "source", output = "32000" }, { name = "RC_SLOW_CLK", type = "source", output = "136_000" }, # BBPLL divider taps (480MHz / N) @@ -51,6 +53,15 @@ system_clocks = { clock_tree = [ { name = "RC_SLOW", outputs = "RC_SLOW_CLK" }, { name = "XTAL32K", outputs = "XTAL32K_CLK", cfg = "use_xtal32k" }, ] }, + # BLE RTC timer (MODEM_LPCON). Independent of LP_SLOW_CLK. + # BLE RTC timer (MODEM_LPCON). Independent of LP_SLOW_CLK. + { name = "BLE_LP_CLK", type = "generic", output = "sclk / (divisor + 1)", params = { sclk = [ + { name = "XTAL", outputs = "XTAL_CLK", default = true }, + { name = "RC_SLOW", outputs = "RC_SLOW_CLK" }, + { name = "XTAL32K", outputs = "XTAL32K_CLK", cfg = "use_xtal32k" }, + { name = "RC32K", outputs = "RC32K_CLK" }, + { name = "EXT32K", outputs = "OSC_SLOW_CLK" }, + ], divisor = "0 ..= 0xFFF" } }, # Global peripheral-related clocks # HP_SYS_CLKRST.crypto_ctrl0.crypto_clk_src_sel diff --git a/esp-metadata/devices/esp32s31/soc.toml b/esp-metadata/devices/esp32s31/soc.toml index 156c5b3b8d3..bb7a9e977f8 100644 --- a/esp-metadata/devices/esp32s31/soc.toml +++ b/esp-metadata/devices/esp32s31/soc.toml @@ -122,6 +122,7 @@ peripherals = [ { name = "ADC1", virtual = true }, { name = "ADC2", virtual = true }, + { name = "BT", virtual = true, interrupts = { mac = "MODEM_BT_MAC", bb = "MODEM_BT_BB", lp_timer = "MODEM_LP_TIMER" } }, { name = "FLASH", virtual = true }, { name = "PSRAM", virtual = true, clock_group = "PSRAM" }, { name = "GPIO_DEDICATED", virtual = true }, @@ -560,8 +561,9 @@ has_wifi6 = true csi_supported = true [device.bt] -support_status = "not_supported" -controller = "npl" # FIXME: placeholder +support_status = "partial" +# IDF `porting_btdm` (nested config + `esp_*` VHCI), not the ESP32/C3/S3 OSI table +controller = "btdm2" [device.phy] support_status = "partial" diff --git a/esp-metadata/src/cfg/soc/clock_tree/generic.rs b/esp-metadata/src/cfg/soc/clock_tree/generic.rs index 9e115b10b4e..748a9318be0 100644 --- a/esp-metadata/src/cfg/soc/clock_tree/generic.rs +++ b/esp-metadata/src/cfg/soc/clock_tree/generic.rs @@ -732,14 +732,14 @@ impl ClockTreeNodeType for Generic { match param { NodeParameter::Value(_) => { quote! { - pub(crate) fn #param_name(self) -> u32 { + pub fn #param_name(self) -> u32 { self.#param_name as u32 } } } NodeParameter::Source(_) => { quote! { - pub(crate) fn #param_name(self) -> #param_ty { + pub fn #param_name(self) -> #param_ty { self.#param_name } } diff --git a/esp-radio-rtos-driver/src/queue.rs b/esp-radio-rtos-driver/src/queue.rs index 830a40925b1..3ba0e2e83b2 100644 --- a/esp-radio-rtos-driver/src/queue.rs +++ b/esp-radio-rtos-driver/src/queue.rs @@ -54,6 +54,11 @@ unsafe extern "Rust" { item: *const u8, higher_prio_task_waken: Option<&mut bool>, ) -> bool; + fn esp_rtos_queue_try_send_to_front_from_isr( + queue: QueuePtr, + item: *const u8, + higher_prio_task_waken: Option<&mut bool>, + ) -> bool; fn esp_rtos_queue_receive(queue: QueuePtr, item: *mut u8, timeout_us: Option) -> bool; fn esp_rtos_queue_receive_with_deadline( queue: QueuePtr, @@ -222,6 +227,23 @@ pub trait QueueImplementation { higher_prio_task_waken: Option<&mut bool>, ) -> bool; + /// Attempts to enqueues an item at the front of the queue. + /// + /// If the queue is full, this function will immediately return `false`. + /// + /// The `higher_prio_task_waken` parameter is an optional mutable reference to a boolean flag. + /// If the flag is `Some`, the implementation may set it to `true` to request a context switch. + /// + /// # Safety + /// + /// The caller must ensure that `item` can be dereferenced and points to an allocation of + /// a size equal to the queue's item size. + unsafe fn try_send_to_front_from_isr( + queue: QueuePtr, + item: *const u8, + higher_prio_task_waken: Option<&mut bool>, + ) -> bool; + /// Dequeues an item from the queue. /// /// If the queue is empty, this function will block for the given timeout. If timeout is None, @@ -370,6 +392,22 @@ macro_rules! register_queue_implementation { } } + #[unsafe(no_mangle)] + #[inline] + fn esp_rtos_queue_try_send_to_front_from_isr( + queue: $crate::queue::QueuePtr, + item: *const u8, + higher_prio_task_waken: Option<&mut bool>, + ) -> bool { + unsafe { + <$t as $crate::queue::QueueImplementation>::try_send_to_front_from_isr( + queue, + item, + higher_prio_task_waken, + ) + } + } + #[unsafe(no_mangle)] #[inline] fn esp_rtos_queue_receive( @@ -568,6 +606,28 @@ impl QueueHandle { } } + /// Attempts to enqueues an item at the front of the queue. + /// + /// If the queue is full, this function will immediately return `false`. + /// + /// If a higher priority task is woken up by this operation, the `higher_prio_task_waken` flag + /// is set to `true`. + /// + /// # Safety + /// + /// The caller must ensure that `item` can be dereferenced and points to an allocation of + /// a size equal to the queue's item size. + #[inline] + pub unsafe fn try_send_to_front_from_isr( + &self, + item: *const u8, + higher_priority_task_waken: Option<&mut bool>, + ) -> bool { + unsafe { + esp_rtos_queue_try_send_to_front_from_isr(self.0, item, higher_priority_task_waken) + } + } + /// Dequeues an item from the queue. /// /// If the queue is empty, this function will block for the given timeout. If timeout is None, @@ -871,6 +931,27 @@ mod implementation { } } + unsafe fn try_send_to_front_from_isr( + queue: QueuePtr, + item: *const u8, + mut higher_prio_task_waken: Option<&mut bool>, + ) -> bool { + let queue = unsafe { CompatQueue::from_ptr(queue) }; + + if queue + .semaphore_empty + .try_take_from_isr(higher_prio_task_waken.as_deref_mut()) + { + queue.with(|inner| inner.send_to_front(item)); + queue + .semaphore_full + .try_give_from_isr(higher_prio_task_waken); + true + } else { + false + } + } + unsafe fn receive(queue: QueuePtr, item: *mut u8, timeout_us: Option) -> bool { let deadline_instant = timeout_us.map(|timeout| now() + timeout as u64); unsafe { Self::receive_with_deadline(queue, item, deadline_instant) } diff --git a/esp-radio/src/ble/btdm/mod.rs b/esp-radio/src/ble/btdm/mod.rs index 12b0fb05c2c..a8e42ba31dc 100644 --- a/esp-radio/src/ble/btdm/mod.rs +++ b/esp-radio/src/ble/btdm/mod.rs @@ -1,8 +1,5 @@ use alloc::boxed::Box; -use core::{ - ptr::{NonNull, addr_of_mut}, - task::Poll, -}; +use core::{ptr::NonNull, task::Poll}; use esp_phy::PhyInitGuard; use portable_atomic::{AtomicBool, AtomicU32, Ordering}; @@ -12,7 +9,6 @@ use super::{Config, ReceivedPacket}; use crate::sys::include; use crate::{ asynch::AtomicWaker, - ble::{HCI_OUT_COLLECTOR, HciOutCollector}, compat::common::str_from_c, hal::ram, sys::{c_types::*, include::*}, @@ -209,46 +205,131 @@ unsafe extern "C" fn rand() -> i32 { unsafe { crate::common_adapter::random() as i32 } } +const LP_CYCLE_US_FRAC: u32 = 19; +static LP_CYCLE_US: AtomicU32 = AtomicU32::new(1 << LP_CYCLE_US_FRAC); + +unsafe extern "C" { + fn btdm_lpclk_select_src(sel: u32) -> bool; + fn btdm_lpclk_set_div(div: u32) -> bool; + fn btdm_sleep_clock_sync() -> u8; + fn btdm_controller_enable_sleep(enable: bool); + fn btdm_wakeup_request(); + fn btdm_in_wakeup_requesting_set(set: bool); + fn btdm_power_state_active() -> bool; + + #[cfg(feature = "coex")] + fn coex_update_lpclk_interval(); +} + +fn program_btdm_lpclk() { + let params = super::lp_clk::btdm(); + LP_CYCLE_US.store(params.lpcycle_us, Ordering::Relaxed); + unsafe { + let selected = btdm_lpclk_select_src(params.select); + let divided = btdm_lpclk_set_div(params.divider); + assert!(selected && divided, "btdm_lpclk_select_src/set_div failed"); + #[cfg(feature = "coex")] + coex_update_lpclk_interval(); + } +} + #[ram] -unsafe extern "C" fn btdm_lpcycles_2_hus(_cycles: u32, _error_corr: u32) -> u32 { - todo!(); +unsafe extern "C" fn btdm_lpcycles_2_hus(cycles: u32, error_corr: u32) -> u32 { + let lpcycle_us = LP_CYCLE_US.load(Ordering::Relaxed).max(1); + cfg_select! { + esp32 => { + let _ = error_corr; + let us = u64::from(lpcycle_us) * u64::from(cycles); + ((us + (1 << (LP_CYCLE_US_FRAC - 1))) >> LP_CYCLE_US_FRAC) as u32 + } + _ => { + let error_corr = error_corr as *mut u32; + let mut local = if error_corr.is_null() { + 0 + } else { + unsafe { u64::from(*error_corr) } + }; + let mut res = u64::from(lpcycle_us) * u64::from(cycles) * 2; + local += res; + res = local >> LP_CYCLE_US_FRAC; + local -= res << LP_CYCLE_US_FRAC; + if !error_corr.is_null() { + unsafe { *error_corr = local as u32 }; + } + res as u32 + } + } } #[ram] unsafe extern "C" fn btdm_hus_2_lpcycles(us: u32) -> u32 { - const RTC_CLK_CAL_FRACT: u32 = 19; - let g_btdm_lpcycle_us_frac = RTC_CLK_CAL_FRACT; - let g_btdm_lpcycle_us = 2 << (g_btdm_lpcycle_us_frac); - - // Converts a duration in half us into a number of low power clock cycles. - let cycles: u64 = ((us as u64) << g_btdm_lpcycle_us_frac) / (g_btdm_lpcycle_us as u64); - trace!("btdm_hus_2_lpcycles {} {}", us, cycles); - + let lpcycle_us = u64::from(LP_CYCLE_US.load(Ordering::Relaxed).max(1)); + let mut cycles = (u64::from(us) << LP_CYCLE_US_FRAC) / lpcycle_us; + cfg_select! { + esp32 => {} + _ => { + cycles >>= 1; + } + } cycles as u32 } -unsafe extern "C" fn btdm_sleep_check_duration(_slot_cnt: i32) -> i32 { - todo!(); -} +#[ram] +unsafe extern "C" fn btdm_sleep_check_duration(slot_cnt: i32) -> i32 { + if !super::modem_sleep_enabled() { + return 0; + } -unsafe extern "C" fn btdm_sleep_enter_phase1(_lpcycles: i32) { - todo!(); + let (min_sleep, wake_delay) = cfg_select! { + esp32 => (12, 4), + _ => (24, 8), + }; + let slot_cnt = slot_cnt as *mut i32; + let slots = unsafe { *slot_cnt }; + if slots < min_sleep { + return 0; + } + unsafe { *slot_cnt = slots - wake_delay }; + 1 } +unsafe extern "C" fn btdm_sleep_enter_phase1(_lpcycles: i32) {} + unsafe extern "C" fn btdm_sleep_enter_phase2() { - todo!(); + if super::modem_sleep_enabled() { + super::modem_phy_release(); + } } -unsafe extern "C" fn btdm_sleep_exit_phase1() { - todo!(); +unsafe extern "C" fn btdm_sleep_exit_phase1() {} + +unsafe extern "C" fn btdm_sleep_exit_phase2() {} + +unsafe extern "C" fn btdm_sleep_exit_phase3() { + if !super::modem_sleep_enabled() { + return; + } + super::modem_phy_acquire(); + unsafe { while btdm_sleep_clock_sync() != 0 {} } } -unsafe extern "C" fn btdm_sleep_exit_phase2() { - todo!(); +fn wake_controller_for_hci() { + if !super::modem_sleep_enabled() { + return; + } + unsafe { + btdm_in_wakeup_requesting_set(true); + if !btdm_power_state_active() { + btdm_wakeup_request(); + } + } } -unsafe extern "C" fn btdm_sleep_exit_phase3() { - todo!(); +fn end_controller_hci_wake() { + if !super::modem_sleep_enabled() { + return; + } + unsafe { btdm_in_wakeup_requesting_set(false) }; } #[ram] @@ -257,9 +338,12 @@ unsafe extern "C" fn read_efuse_mac(mac: *const ()) -> i32 { } pub(crate) fn ble_init(config: &Config) -> PhyInitGuard<'static> { + super::set_modem_sleep(config.modem_sleep()); + super::lp_clk::request(); + program_btdm_lpclk(); + let phy_init_guard; unsafe { - (*addr_of_mut!(HCI_OUT_COLLECTOR)).write(HciOutCollector::new()); // turn on logging #[allow(static_mut_refs)] #[cfg(feature = "print-logs-from-driver")] @@ -326,6 +410,10 @@ pub(crate) fn ble_init(config: &Config) -> PhyInitGuard<'static> { btdm_controller_enable(esp_bt_mode_t_ESP_BT_MODE_BLE); + if config.modem_sleep() { + btdm_controller_enable_sleep(true); + } + API_vhci_host_register_callback(&VHCI_HOST_CALLBACK); } @@ -335,6 +423,9 @@ pub(crate) fn ble_init(config: &Config) -> PhyInitGuard<'static> { } pub(crate) fn ble_deinit() { + super::modem_phy_acquire(); + super::set_modem_sleep(false); + esp_hal::rng::TrngSource::decrease_entropy_source_counter(unsafe { esp_hal::Internal::conjure() }); @@ -342,24 +433,22 @@ pub(crate) fn ble_deinit() { unsafe { btdm_controller_deinit(); } + + super::lp_clk::release(); // Disabling the PHY happens automatically, when the BLEController gets dropped. } -/// Sends HCI data to the BLE controller. -/// -/// Returns the number of bytes taken from `data`. At most one packet is sent per call, so the -/// caller must offer the remaining bytes again. -pub(crate) fn send_hci(data: &[u8]) -> usize { +pub(crate) fn send(data: &[u8]) { // make sure the packet buffer doesn't get touched until sent while PACKET_IN_FLIGHT.load(Ordering::Acquire) {} while unsafe { !API_vhci_host_check_send_available() } { trace!("can_send is false"); } - super::collect_and_send(data, send_packet) + send_packet(data) } -pub(crate) async fn send_hci_async(data: &[u8]) -> usize { +pub(crate) async fn send_async(data: &[u8]) { // make sure the packet buffer doesn't get touched until sent core::future::poll_fn(|cx| { PACKET_SENT_WAKER.register(cx.waker()); @@ -373,13 +462,15 @@ pub(crate) async fn send_hci_async(data: &[u8]) -> usize { }) .await; - super::collect_and_send(data, send_packet) + send_packet(data) } fn send_packet(packet: &[u8]) { unsafe { PACKET_IN_FLIGHT.store(true, Ordering::Relaxed); + wake_controller_for_hci(); + #[cfg(all(esp32, feature = "coex"))] chip_specific::async_wakeup_request(chip_specific::BTDM_ASYNC_WAKEUP_REQ_HCI); @@ -387,6 +478,8 @@ fn send_packet(packet: &[u8]) { #[cfg(all(esp32, feature = "coex"))] chip_specific::async_wakeup_request_end(chip_specific::BTDM_ASYNC_WAKEUP_REQ_HCI); + + end_controller_hci_wake(); } trace!("sent vhci host packet"); diff --git a/esp-radio/src/ble/btdm/os_adapter_esp32.rs b/esp-radio/src/ble/btdm/os_adapter_esp32.rs index e6c574cd91d..9197e689b83 100644 --- a/esp-radio/src/ble/btdm/os_adapter_esp32.rs +++ b/esp-radio/src/ble/btdm/os_adapter_esp32.rs @@ -282,6 +282,12 @@ static BTDM_DRAM_AVAILABLE_REGION: [btdm_dram_available_region_t; 7] = [ #[derive(BuilderLite, Clone, Copy, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Config { + /// Enables controller modem sleep. + /// + /// The low-power clock source comes from the clock tree (`BLE_LP_CLK`). + /// Set this before the controller starts. The default is off. + modem_sleep: bool, + /// The priority of the RTOS task. task_priority: u8, @@ -330,6 +336,7 @@ pub struct Config { impl Default for Config { fn default() -> Self { Self { + modem_sleep: false, task_priority: crate::preempt::max_task_priority() .saturating_sub(2) .min(255) as u8, @@ -467,9 +474,16 @@ pub(crate) fn disable_sleep_mode() { } const BTDM_MODEM_SLEEP_MODE_NONE: u8 = 0; + const BTDM_MODEM_SLEEP_MODE_ORIG: u8 = 1; + + let mode = if crate::ble::modem_sleep_enabled() { + BTDM_MODEM_SLEEP_MODE_ORIG + } else { + BTDM_MODEM_SLEEP_MODE_NONE + }; unsafe { - btdm_controller_set_sleep_mode(BTDM_MODEM_SLEEP_MODE_NONE); + btdm_controller_set_sleep_mode(mode); } } diff --git a/esp-radio/src/ble/btdm/os_adapter_esp32c3_s3.rs b/esp-radio/src/ble/btdm/os_adapter_esp32c3_s3.rs index 8ddc94208d8..1d4f8700f20 100644 --- a/esp-radio/src/ble/btdm/os_adapter_esp32c3_s3.rs +++ b/esp-radio/src/ble/btdm/os_adapter_esp32c3_s3.rs @@ -364,6 +364,12 @@ pub enum CcaMode { #[derive(BuilderLite, Clone, Copy, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Config { + /// Enables controller modem sleep. + /// + /// The low-power clock source comes from the clock tree (`BLE_LP_CLK`). + /// Set this before the controller starts. The default is off. + modem_sleep: bool, + /// The priority of the RTOS task. task_priority: u8, @@ -466,6 +472,7 @@ pub struct Config { impl Default for Config { fn default() -> Self { Self { + modem_sleep: false, task_priority: crate::preempt::max_task_priority() .saturating_sub(2) .min(255) as u8, @@ -531,7 +538,7 @@ pub(crate) fn create_ble_config(config: &Config) -> esp_bt_controller_config_t { bluetooth_mode: esp_bt_mode_t_ESP_BT_MODE_BLE as _, ble_max_act: config.max_connections, - sleep_mode: 0, + sleep_mode: u8::from(config.modem_sleep), sleep_clock: 0, ble_st_acl_tx_buf_nb: 0, ble_hw_cca_check: 0, diff --git a/esp-radio/src/ble/btdm2/mod.rs b/esp-radio/src/ble/btdm2/mod.rs new file mode 100644 index 00000000000..45670866b64 --- /dev/null +++ b/esp-radio/src/ble/btdm2/mod.rs @@ -0,0 +1,1589 @@ +//! IDF `porting_btdm` controller (nested BLE/BR-EDR/BTDM config + `esp_*` VHCI). +//! +//! Distinct from the original BTDM OSI / `API_vhci_host_*` path used on +//! ESP32/C3/S3 (`bt_controller = "btdm"`). +//! +//! The second half of this file is the `wr_btdm_osal_*` OSAL the blob calls +//! back into. Objects are heap-allocated (IDF's `BTDM_MEMPOOL_ALLOC` path is +//! not used) and, as with IDF's `BTDM_OSAL_USE_ESP_TIMER`, one tick is one +//! millisecond. + +use core::{ + mem::{size_of, transmute}, + ptr::NonNull, + sync::atomic::{AtomicU8, AtomicU16, AtomicU32, Ordering}, +}; + +use esp_phy::PhyInitGuard; +use portable_atomic::AtomicBool; + +use super::{Config, ReceivedPacket, in_isr}; +use crate::{ + compat::{ + self, + common::str_from_c, + mutex, + queue::{ + queue_create, + queue_delete, + queue_messages_waiting, + queue_receive, + queue_remove, + queue_send_to_back, + queue_send_to_front, + queue_try_receive_from_isr, + queue_try_send_to_back_from_isr, + queue_try_send_to_front_from_isr, + }, + semaphore, + }, + hal::{ram, system::Cpu}, + sys::{ + c_types::{c_char, c_void}, + include::*, + }, + time::{blob_ticks_to_micros, blob_ticks_to_millis, millis_to_blob_ticks}, +}; + +#[cfg_attr(esp32s31, path = "os_adapter_esp32s31.rs")] +pub(crate) mod chip_specific; + +const EXT_FUNC_VERSION: u32 = 0x20250819; +const EXT_FUNC_MAGIC: u32 = 0xA5A5A5A5; +const ACL_DATA_MBUF_LEADINGSPACE: usize = 4; + +static CONTROLLER_STATUS: AtomicU32 = + AtomicU32::new(esp_bt_controller_status_t_ESP_BT_CONTROLLER_STATUS_IDLE); +/// IDF `s_bt_active`. While set, `e_btdm_lp_modem_clock_set(false)` is a +/// no-op so the blob cannot gate MAC/BB clocks between HCI commands. +static BT_ACTIVE: AtomicBool = AtomicBool::new(false); + +/// Out-parameter of `r_ble_ll_get_npl_element_info`: how many OSAL elements of +/// each kind the controller needs. IDF pre-allocates a pool per kind; we +/// allocate on demand, so only `evt_count` is of any use to us. +#[derive(Default)] +#[repr(C)] +struct OsalElemNum { + evt_count: u16, + evtq_count: u16, + co_count: u16, + sem_count: u16, + mutex_count: u16, +} + +#[repr(C)] +struct ExtFuncsT { + ext_version: u32, + malloc: Option *mut c_void>, + free: Option, + osi_assert: Option, + os_random: Option u32>, + ecc_gen_key_pair: Option i32>, + ecc_gen_dh_key: Option i32>, + esp_reset_rpa_moudle: Option, + ecc_aes_cmac: Option i32>, + magic: u32, +} + +static EXT_FUNCS: ExtFuncsT = ExtFuncsT { + ext_version: EXT_FUNC_VERSION, + malloc: Some(wr_btdm_osal_malloc), + free: Some(wr_btdm_osal_free), + osi_assert: Some(osi_assert), + os_random: Some(os_random), + ecc_gen_key_pair: Some(ecc_gen_key_pair), + ecc_gen_dh_key: Some(ecc_gen_dh_key), + esp_reset_rpa_moudle: Some(reset_rpa), + ecc_aes_cmac: Some(ecc_aes_cmac), + magic: EXT_FUNC_MAGIC, +}; + +/// Chained memory buffer used by the NimBLE-style controller. +#[repr(C)] +struct OsMbuf { + om_data: *const u8, + om_flags: u8, + om_pkthdr_len: u8, + om_len: u16, + om_omp: *const c_void, + next: *const OsMbuf, +} + +unsafe extern "C" { + fn r_btdm_task_init(cfg: *mut esp_btdm_controller_config_t) -> i32; + fn r_btdm_task_deinit(); + fn r_btdm_task_enable() -> i32; + fn r_btdm_task_disable(); + fn r_btdm_hal_rtc_freq_set(freq: u64); + fn r_esp_ble_change_rtc_freq(freq: u32); + + fn esp_register_ext_funcs(funcs: *const ExtFuncsT) -> i32; + fn esp_unregister_ext_funcs(); + fn esp_ble_register_bb_funcs() -> i32; + fn esp_ble_unregister_bb_funcs() -> i32; + fn r_ble_controller_init(cfg: *mut esp_bt_controller_config_t__bindgen_ty_1) -> i32; + fn r_ble_controller_deinit() -> i32; + fn r_esp_ble_msys_init( + msys_size1: u16, + msys_size2: u16, + msys_cnt1: u16, + msys_cnt2: u16, + from_heap: u8, + ) -> i32; + fn r_esp_ble_msys_deinit(); + fn r_esp_ble_ll_set_public_addr(addr: *const u8); + fn r_ble_ll_get_npl_element_info( + cfg: *mut esp_bt_controller_config_t__bindgen_ty_1, + elem: *mut OsalElemNum, + ) -> i32; + fn r_base_stack_initEnv() -> i32; + fn r_base_stack_deinitEnv() -> i32; + fn r_base_stack_enable() -> i32; + fn r_base_stack_disable(); + fn r_conn_stack_initEnv() -> i32; + fn r_conn_stack_deinitEnv(); + fn r_conn_stack_enable() -> i32; + fn r_conn_stack_disable(); + fn bt_bb_v2_init_cmplx(value: u8); + fn coex_pti_v2(); + fn bt_bb_get_tx_pwr_table(length: *mut u8) -> *const i8; + + fn r_ble_hci_trans_cfg_hs( + evt: Option i32>, + evt_arg: *const c_void, + acl_cb: Option i32>, + acl_arg: *const c_void, + ); + fn r_ble_hci_trans_buf_free(buf: *const u8); + fn r_ble_hci_trans_hs_acl_tx(om: *mut OsMbuf) -> i32; + fn r_os_msys_get_pkthdr(dsize: u16, user_hdr_len: u16) -> *mut OsMbuf; + fn r_os_mbuf_append(om: *mut OsMbuf, src: *const u8, len: u16) -> i32; + fn r_os_mbuf_free_chain(om: *mut OsMbuf) -> i32; + + fn r_btdm_hci_trans_buf_alloc(typ: u8, conn_handle: u16) -> *mut HciDriverPacket; + fn r_btdm_hci_trans_buf_free(pkt: *mut HciDriverPacket); + fn r_btdm_hci_trans_rx(pkt: *mut HciDriverPacket) -> i32; + fn r_btdm_hci_trans_register_tx(tx_func: *const c_void, async_: bool) -> i32; + + // IDF `UC_BT_CTRL_CONN_FC_ENABLE` is hardcoded on. The BLE stack + // calls these accessors after a connection; without `env_init` the + // env pointer stays NULL and `r_sym_bt_E5PSb5NmRdfYVGFUOLdR` load- + // faults at offset 1. + fn r_btdm_hci_fc_env_init() -> i32; + fn r_btdm_hci_fc_env_deinit(); + fn r_btdm_hci_fc_enable() -> i32; + fn r_btdm_hci_fc_disable(); +} + +/// IDF `hci_driver_packet_t` (`STAILQ_ENTRY` is one next pointer). +#[repr(C)] +struct HciDriverPacket { + next: *mut HciDriverPacket, + data: *mut u8, + length: u16, + flags: u8, + type_: u8, +} + +const HCI_DRIVER_TYPE_CMD: u8 = 0x01; + +unsafe extern "C" fn osi_assert(ln: u32, fn_name: *const c_void, param1: u32, param2: u32) { + unsafe { + let name = crate::compat::common::str_from_c(fn_name.cast()); + panic!("BLE assert {}:{} 0x{:x} 0x{:x}", name, ln, param1, param2); + } +} + +unsafe extern "C" fn os_random() -> u32 { + unsafe { crate::common_adapter::random() as u32 } +} + +unsafe extern "C" fn reset_rpa() { + crate::radio_clocks::clocks_ll::reset_rpa(); +} + +unsafe extern "C" fn ecc_gen_key_pair(_pub: *mut u8, _priv: *mut u8) -> i32 { + -1 +} + +unsafe extern "C" fn ecc_gen_dh_key( + _x: *const u8, + _y: *const u8, + _priv: *const u8, + _out: *mut u8, +) -> i32 { + -1 +} + +unsafe extern "C" fn ecc_aes_cmac( + _key: *const u8, + _input: *const u8, + _len: usize, + _out: *mut u8, +) -> i32 { + -1 +} + +fn controller_status() -> u32 { + CONTROLLER_STATUS.load(Ordering::Relaxed) +} + +fn set_controller_status(status: u32) { + CONTROLLER_STATUS.store(status, Ordering::Relaxed); +} + +/// Number of entries to size the event queue for, following IDF's +/// `esp_bt_controller_init` seed of `3 + 100` plus the `+10` workaround in +/// `ble_osal_elem_calc`. +fn eventq_depth(cfg: *mut esp_bt_controller_config_t) -> u16 { + let mut elem = OsalElemNum::default(); + // On failure `elem` stays zeroed, leaving us with IDF's seed. + unsafe { r_ble_ll_get_npl_element_info(&raw mut (*cfg).ble, &mut elem) }; + elem.evt_count.saturating_add(113) +} + +fn ble_stack_init(cfg: *mut esp_bt_controller_config_t) -> i32 { + unsafe { + let res = esp_register_ext_funcs(&raw const EXT_FUNCS); + if res != 0 { + warn!("esp_register_ext_funcs failed {}", res); + return res; + } + + // C6 IDF sets this to `s_bt_lpclk_freq` for MAIN_XTAL (100 kHz). + // porting_btdm's S31 stub still hardcodes 32000; the HW divider we + // program is 100 kHz. Advertising enable busy-waits on cputime. + (*cfg).ble.rtc_freq = lpclk_hz(); + + let res = esp_ble_register_bb_funcs(); + if res != 0 { + warn!("esp_ble_register_bb_funcs failed {}", res); + return res; + } + + let res = r_ble_controller_init(&raw mut (*cfg).ble); + if res != 0 { + warn!("r_ble_controller_init failed {}", res); + return res; + } + + r_esp_ble_change_rtc_freq(lpclk_hz() as u32); + + let res = r_esp_ble_msys_init( + CONFIG_BT_LE_MSYS_1_BLOCK_SIZE as u16, + CONFIG_BT_LE_MSYS_2_BLOCK_SIZE as u16, + CONFIG_BT_LE_MSYS_1_BLOCK_COUNT as u16, + CONFIG_BT_LE_MSYS_2_BLOCK_COUNT as u16, + CONFIG_BT_LE_MSYS_BUF_FROM_HEAP as u8, + ); + if res != 0 { + warn!("r_esp_ble_msys_init failed {}", res); + return res; + } + + let mut mac = [0u8; 6]; + crate::common_adapter::read_mac(mac.as_mut_ptr(), 2); + mac.reverse(); + r_esp_ble_ll_set_public_addr(mac.as_ptr()); + + let res = r_base_stack_initEnv(); + if res != 0 { + warn!("r_base_stack_initEnv failed {}", res); + return res; + } + + let res = r_conn_stack_initEnv(); + if res != 0 { + warn!("r_conn_stack_initEnv failed {}", res); + return res; + } + } + 0 +} + +fn ble_stack_deinit() { + unsafe { + r_conn_stack_deinitEnv(); + let _ = r_base_stack_deinitEnv(); + r_esp_ble_msys_deinit(); + let _ = r_ble_controller_deinit(); + let _ = esp_ble_unregister_bb_funcs(); + esp_unregister_ext_funcs(); + } +} + +fn ble_stack_enable() -> i32 { + unsafe { + let res = r_base_stack_enable(); + if res != 0 { + return res; + } + // `r_conn_stack_enable` in this blob is a 2-byte `c.jr ra` that + // never writes a0, so the "return value" is leftover register + // garbage (we saw 8). Connection setup is done in initEnv. + let _ = r_conn_stack_enable(); + 0 + } +} + +fn ble_stack_disable() { + unsafe { + r_conn_stack_disable(); + r_base_stack_disable(); + } +} + +fn bt_controller_deinit() { + BT_ACTIVE.store(false, Ordering::Relaxed); + set_controller_status(esp_bt_controller_status_t_ESP_BT_CONTROLLER_STATUS_IDLE); + unsafe { + // `r_ble_controller_init` registers the two coex callbacks, but + // `r_ble_controller_deinit` frees their environment without + // unregistering them. The next `coex_enable` / `coex_disable` from + // Wi-Fi would run them against a NULL environment. + #[cfg(feature = "coex")] + { + coex_register_ble_cb(0, core::ptr::null_mut()); + coex_register_ble_cb(1, core::ptr::null_mut()); + } + + r_ble_hci_trans_cfg_hs(None, core::ptr::null(), None, core::ptr::null()); + ble_stack_deinit(); + r_btdm_hci_fc_env_deinit(); + r_btdm_task_deinit(); + crate::radio_clocks::clocks_ll::enable_bt(false); + } +} + +fn esp_bt_controller_init(cfg: *mut esp_bt_controller_config_t) -> esp_err_t { + if cfg.is_null() { + return ESP_ERR_INVALID_ARG as _; + } + if controller_status() != esp_bt_controller_status_t_ESP_BT_CONTROLLER_STATUS_IDLE { + return ESP_ERR_INVALID_STATE as _; + } + + unsafe { + if (*cfg).btdm.bluetooth_mode == esp_bt_mode_t_ESP_BT_MODE_IDLE as u8 { + return ESP_ERR_INVALID_ARG as _; + } + + EVENTQ_DEPTH.store(eventq_depth(cfg), Ordering::Relaxed); + + crate::radio_clocks::clocks_ll::enable_bt(true); + + let res = r_btdm_task_init(&raw mut (*cfg).btdm); + if res != 0 { + warn!("r_btdm_task_init failed {}", res); + bt_controller_deinit(); + return ESP_FAIL as _; + } + + btdm_lp_init(); + + // IDF `esp_bt_controller_init`: after `r_btdm_task_init` / + // `btdm_lp_init`, before `ble_stack_init`. + let res = r_btdm_hci_fc_env_init(); + if res != 0 { + warn!("r_btdm_hci_fc_env_init failed {}", res); + bt_controller_deinit(); + return ESP_FAIL as _; + } + + bt_bb_v2_init_cmplx(1); + coex_pti_v2(); + // Same ROM pointer 802.15.4 TX needs on S31. A null/stale + // `coex_pti_tab_ptr` leaves the BB waiting for a PTI grant. + { + unsafe extern "C" { + static mut coex_pti_tab_ptr: u32; + static coex_pti_tab: u8; + } + (&raw mut coex_pti_tab_ptr).write_volatile(&raw const coex_pti_tab as u32); + } + + let res = ble_stack_init(cfg); + if res != 0 { + bt_controller_deinit(); + return ESP_FAIL as _; + } + + // IDF `hci_transport_init` registers this during init so + // `r_ble_hci_trans_ll_evt_tx` has a host callback before enable. + r_ble_hci_trans_cfg_hs( + Some(ble_hs_hci_rx_evt), + core::ptr::null(), + Some(ble_hs_rx_data), + core::ptr::null(), + ); + // IDF also registers the BTDM-common TX path so dual-mode / + // controller-originated packets reach the host. The blob returns + // the previous callback pointer (often a ROM address), not an + // error code. + let _prev = r_btdm_hci_trans_register_tx(btdm_hci_controller_tx as *const c_void, false); + } + + set_controller_status(esp_bt_controller_status_t_ESP_BT_CONTROLLER_STATUS_INITED); + 0 +} + +fn esp_bt_controller_deinit() -> esp_err_t { + if controller_status() != esp_bt_controller_status_t_ESP_BT_CONTROLLER_STATUS_INITED { + return ESP_ERR_INVALID_STATE as _; + } + bt_controller_deinit(); + 0 +} + +fn esp_bt_controller_enable(_mode: esp_bt_mode_t) -> esp_err_t { + if controller_status() != esp_bt_controller_status_t_ESP_BT_CONTROLLER_STATUS_INITED { + return ESP_ERR_INVALID_STATE as _; + } + + // IDF `btdm_lp_reset(true)` sets `s_bt_active`, then `esp_phy_enable` + // + `esp_btbb_enable`, before `r_btdm_task_enable`. PHY is already + // held by `ble_init`; re-init BB here so TX (advertising) is armed. + BT_ACTIVE.store(true, Ordering::Relaxed); + + unsafe { + bt_bb_v2_init_cmplx(1); + + let res = ble_stack_enable(); + if res != 0 { + warn!("ble_stack_enable failed {}", res); + BT_ACTIVE.store(false, Ordering::Relaxed); + let _ = esp_bt_controller_disable(); + return ESP_FAIL as _; + } + + let res = r_btdm_hci_fc_enable(); + if res != 0 { + warn!("r_btdm_hci_fc_enable failed {}", res); + BT_ACTIVE.store(false, Ordering::Relaxed); + let _ = esp_bt_controller_disable(); + return ESP_FAIL as _; + } + + let res = r_btdm_task_enable(); + if res != 0 { + warn!("r_btdm_task_enable failed {}", res); + BT_ACTIVE.store(false, Ordering::Relaxed); + let _ = esp_bt_controller_disable(); + return ESP_FAIL as _; + } + } + + set_controller_status(esp_bt_controller_status_t_ESP_BT_CONTROLLER_STATUS_ENABLED); + 0 +} + +fn esp_bt_controller_disable() -> esp_err_t { + if controller_status() != esp_bt_controller_status_t_ESP_BT_CONTROLLER_STATUS_ENABLED { + return ESP_ERR_INVALID_STATE as _; + } + + unsafe { + r_ble_hci_trans_cfg_hs(None, core::ptr::null(), None, core::ptr::null()); + r_btdm_task_disable(); + r_btdm_hci_fc_disable(); + ble_stack_disable(); + } + BT_ACTIVE.store(false, Ordering::Relaxed); + set_controller_status(esp_bt_controller_status_t_ESP_BT_CONTROLLER_STATUS_INITED); + 0 +} + +#[unsafe(no_mangle)] +extern "C" fn e_btdm_lp_modem_clock_set(enable: bool) { + trace!("e_btdm_lp_modem_clock_set {:?}", enable); + // crate::radio_clocks::clocks_ll::enable_bt_clocks(enable); +} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_assert(file: *const c_void, line: i32, p0: i32, p1: i32, _ty: u8) { + unsafe { + let name = crate::compat::common::str_from_c(file.cast()); + panic!("BLE assert {}:{} 0x{:x} 0x{:x}", name, line, p0, p1); + } +} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_log_set_flags(_p0: u8) {} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_log_reset_flags(_p0: u8) {} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_log_internal_x0(_p0: u32) {} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_log_internal_x1(_p0: u32, _p1: u32) {} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_log_internal_x2(_p0: u32, _p1: u32, _p2: u32) {} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_log_internal_x3(_p0: u32, _p1: u32, _p2: u32, _p3: u32) {} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_log_internal_hex(_p0: u32, _p1: u32, _p2: u32) {} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_log_raw_export(_p0: u16, _p1: u32, _p2: u32) {} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_external_bb_get_tx_pwr_table(length: *mut u8, _modem_cfg: u8) -> *const i8 { + unsafe { bt_bb_get_tx_pwr_table(length) } +} + +// The controller's view of coexistence, mirroring IDF `btdm_coex.c`. Without +// the `coex` feature these keep the values IDF returns with +// `CONFIG_SW_COEXIST_ENABLE` off. + +// Declared `extern` in IDF `btdm_coex.c` rather than in a coex header, so +// esp-wifi-sys does not generate bindings for them. +#[cfg(feature = "coex")] +unsafe extern "C" { + fn coex_register_ble_cb(ty: u8, func: *mut c_void) -> i32; + fn coex_ble_idle_time_inform(start_offset: u32, duration: u32); +} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_coex_schm_status_bit_set(_ty: u32, _status: u32) { + #[cfg(feature = "coex")] + unsafe { + coex_schm_status_bit_set(_ty, _status) + }; +} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_coex_schm_status_bit_clear(_ty: u32, _status: u32) { + #[cfg(feature = "coex")] + unsafe { + coex_schm_status_bit_clear(_ty, _status) + }; +} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_coex_schm_register_btdm_callback(_cb: *mut c_void) -> i32 { + cfg_select! { + feature = "coex" => unsafe { + coex_schm_register_callback(coex_schm_callback_type_t_COEX_SCHM_CALLBACK_TYPE_BT, _cb) + }, + _ => 0, + } +} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_coex_schm_interval_get() -> u32 { + cfg_select! { + feature = "coex" => unsafe { coex_schm_interval_get() }, + _ => 0, + } +} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_coex_schm_curr_period_get() -> u8 { + cfg_select! { + // Before a scheme is running the scheduler has no period to report. + // Clamp to the one-period value IDF uses with coex off rather than + // handing the controller a zero it may divide by. + feature = "coex" => unsafe { coex_schm_curr_period_get().max(1) }, + _ => 1, + } +} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_coex_schm_curr_phase_get() -> *mut c_void { + cfg_select! { + feature = "coex" => unsafe { coex_schm_curr_phase_get() }, + _ => core::ptr::null_mut(), + } +} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_coex_register_wifi_channel_change_callback( + _cb: coex_wifi_channel_change_cb_t, +) -> i32 { + cfg_select! { + feature = "coex" => unsafe { coex_register_wifi_channel_change_callback(_cb) }, + _ => -1, + } +} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_coex_wifi_channel_get(_primary: *mut u8, _secondary: *mut u8) -> i32 { + cfg_select! { + feature = "coex" => unsafe { coex_wifi_channel_get(_primary, _secondary) }, + _ => -1, + } +} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_coex_version_get(_major: *mut u32, _minor: *mut u32, _path: *mut u32) -> i32 { + // IDF reports success here while leaving the out-parameters untouched: its + // `coex_version_get_value` call is `#if 0`-ed out pending a coexist + // submodule update. Query it for real rather than reporting success over + // whatever the controller left on its stack. + cfg_select! { + feature = "coex" => unsafe { + let mut version = coex_version_t { + major: 0, + minor: 0, + patch: 0, + }; + let res = coex_version_get_value(&mut version); + if res == 0 { + _major.write(version.major.into()); + _minor.write(version.minor.into()); + _path.write(version.patch.into()); + } + res + }, + _ => -1, + } +} + +// The ISO hooks stay stubbed: IDF gates them on `CONFIG_BT_LE_ISO_SUPPORT`, +// which esp-radio does not offer. + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_coex_iso_start(_info: *mut c_void) -> i32 { + 0 +} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_coex_iso_stop(_handle: u16) -> i32 { + 0 +} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_coex_iso_protect_frame_thres_get() -> u16 { + 0 +} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_coex_iso_start_int_handle(_handle: u16, _duration: u32) {} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_coex_iso_end_int_handle(_handle: u16, _duration: u32) {} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_coex_ble_idle_time_inform(_start_offset: u32, _duration: u32) { + cfg_select! { + feature = "coex" => unsafe { coex_ble_idle_time_inform(_start_offset, _duration) }, + _ => {} + } +} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_coex_register_ble_cb(_ty: u32, _func: *mut c_void) -> i32 { + cfg_select! { + feature = "coex" => unsafe { coex_register_ble_cb(_ty as u8, _func) }, + _ => 0, + } +} + +/// Hands a complete HCI packet, indicator byte first, to the host reader. +fn queue_rx_packet(header: &[u8], payload: &[u8]) { + let mut data = alloc::vec::Vec::with_capacity(header.len() + payload.len()); + data.extend_from_slice(header); + data.extend_from_slice(payload); + + super::dump_packet_info(&data); + super::BT_STATE.with(|state| { + state.rx_queue.push_back(ReceivedPacket { + data: data.into_boxed_slice(), + }); + }); + crate::ble::controller::hci_read_data_available(); +} + +extern "C" fn btdm_hci_controller_tx(pkt: *mut HciDriverPacket) -> i32 { + let Some(pkt) = (unsafe { pkt.as_mut() }) else { + return -1; + }; + queue_rx_packet(&[pkt.type_], unsafe { + core::slice::from_raw_parts(pkt.data, pkt.length as usize) + }); + unsafe { r_btdm_hci_trans_buf_free(core::ptr::from_mut(pkt)) }; + 0 +} + +unsafe extern "C" fn ble_hs_hci_rx_evt(cmd: *const u8, _arg: *const c_void) -> i32 { + let event = unsafe { *cmd }; + let len = unsafe { *cmd.add(1) }; + queue_rx_packet(&[0x04, event, len], unsafe { + core::slice::from_raw_parts(cmd.add(2), len as usize) + }); + unsafe { r_ble_hci_trans_buf_free(cmd) }; + 0 +} + +unsafe extern "C" fn ble_hs_rx_data(om: *const OsMbuf, _arg: *const c_void) -> i32 { + queue_rx_packet(&[0x02], unsafe { + core::slice::from_raw_parts((*om).om_data, (*om).om_len as usize) + }); + unsafe { r_os_mbuf_free_chain(om.cast_mut()) }; + 0 +} + +fn send_hci_packet(packet: &[u8]) { + const DATA_TYPE_COMMAND: u8 = 1; + const DATA_TYPE_ACL: u8 = 2; + + super::dump_packet_info(packet); + + unsafe { + if packet[0] == DATA_TYPE_COMMAND { + // IDF VHCI uses the BTDM-common packet path, not NimBLE + // `r_ble_hci_trans_hs_cmd_tx`. + let pkt = r_btdm_hci_trans_buf_alloc(HCI_DRIVER_TYPE_CMD, 0); + if pkt.is_null() { + warn!("r_btdm_hci_trans_buf_alloc failed"); + return; + } + (*pkt).type_ = HCI_DRIVER_TYPE_CMD; + (*pkt).length = (packet.len() - 1) as u16; + core::ptr::copy_nonoverlapping(packet.as_ptr().add(1), (*pkt).data, packet.len() - 1); + let res = r_btdm_hci_trans_rx(pkt); + if res != 0 { + warn!("r_btdm_hci_trans_rx returned {}", res); + } + } else if packet[0] == DATA_TYPE_ACL { + let om = r_os_msys_get_pkthdr(packet.len() as u16, ACL_DATA_MBUF_LEADINGSPACE as u16); + if om.is_null() { + warn!("r_os_msys_get_pkthdr failed"); + return; + } + let res = r_os_mbuf_append(om, packet.as_ptr().add(1), (packet.len() - 1) as u16); + if res != 0 { + panic!("r_os_mbuf_append returned {}", res); + } + *((*om).om_data as *mut u8).add(1) = 0; + let res = r_ble_hci_trans_hs_acl_tx(om); + if res != 0 { + panic!("r_ble_hci_trans_hs_acl_tx returned {}", res); + } + } + } +} + +fn lpclk_hz() -> u64 { + u64::from(super::lp_clk::frequency_hz()) +} + +#[ram] +unsafe extern "C" fn btdm_lp_sleep_cb(_enable_tick: u32, _arg: *mut c_void) { + // The callbacks are registered during controller init. IDF guards them with `s_bt_active` so + // that the PHY stays up until the controller is enabled. + if !BT_ACTIVE.load(Ordering::Relaxed) { + return; + } + super::modem_phy_release(); +} + +#[ram] +unsafe extern "C" fn btdm_lp_wake_up_cb(_arg: *mut c_void) { + super::modem_phy_acquire(); +} + +/// IDF `btdm_lp_init`. Runs after `r_btdm_task_init`, before `ble_stack_init`. +/// +/// `r_btdm_task_init` initializes the hardware environment that holds the low-power clock +/// frequency, so the frequency has to be set after it. +unsafe fn btdm_lp_init() { + unsafe extern "C" { + fn r_btdm_sleep_set_sleep_cb( + sleep_cb: unsafe extern "C" fn(u32, *mut c_void), + wakeup_cb: unsafe extern "C" fn(*mut c_void), + sleep_arg: *mut c_void, + wakeup_arg: *mut c_void, + us_to_enabled: u32, + ); + } + + // IDF `BTDM_RTC_DELAY_US_MODEM_SLEEP`: how long the controller needs to enable the PHY. + const RTC_DELAY_US_MODEM_SLEEP: u32 = 1500; + + unsafe { + if super::modem_sleep_enabled() { + r_btdm_sleep_set_sleep_cb( + btdm_lp_sleep_cb, + btdm_lp_wake_up_cb, + core::ptr::null_mut(), + core::ptr::null_mut(), + RTC_DELAY_US_MODEM_SLEEP, + ); + } + + r_btdm_hal_rtc_freq_set(lpclk_hz()); + } +} + +pub(crate) fn ble_init(config: &Config) -> PhyInitGuard<'static> { + super::set_modem_sleep(config.modem_sleep()); + super::lp_clk::request(); + chip_specific::btdm_controller_mem_init(); + + let mut cfg = chip_specific::create_ble_config(config); + + #[cfg(feature = "coex")] + unsafe { + let res = crate::wifi::coex_init(); + assert!(res == 0, "coex_init failed"); + } + + chip_specific::bt_periph_module_enable(); + chip_specific::disable_sleep_mode(); + + let phy_init_guard = esp_phy::enable_phy(); + + let res = esp_bt_controller_init(&mut cfg); + assert!(res == 0, "esp_bt_controller_init returned {}", res); + + #[cfg(feature = "coex")] + unsafe { + crate::sys::include::coex_enable(); + } + + let res = esp_bt_controller_enable(esp_bt_mode_t_ESP_BT_MODE_BLE); + assert!(res == 0, "esp_bt_controller_enable returned {}", res); + + #[cfg(rng_trng_supported)] + unsafe { + esp_hal::rng::TrngSource::increase_entropy_source_counter() + }; + + phy_init_guard +} + +pub(crate) fn ble_deinit() { + super::modem_phy_acquire(); + super::set_modem_sleep(false); + + #[cfg(rng_trng_supported)] + esp_hal::rng::TrngSource::decrease_entropy_source_counter(unsafe { + esp_hal::Internal::conjure() + }); + + let _ = esp_bt_controller_disable(); + let _ = esp_bt_controller_deinit(); + + super::lp_clk::release(); +} + +pub(crate) fn send(data: &[u8]) { + send_hci_packet(data) +} + +pub(crate) async fn send_async(data: &[u8]) { + send_hci_packet(data) +} + +// ------------------------------------------------------------------------- +// OSAL (wr_btdm_osal_*) +// +// Event queues are local. CompatQueue's blocking receive sleeps inside +// NonReentrantMutex (IRQs off); on S31 the SWI yield is lost and the next +// schedule_wakeup panics because the task is already Sleeping. +// ------------------------------------------------------------------------- + +const BTDM_OSAL_OK: i32 = 0; +const BTDM_OSAL_INVALID_PARM: i32 = 3; +const BTDM_OSAL_TIMEOUT: i32 = 6; + +static EVENTQ_DEPTH: AtomicU16 = AtomicU16::new(16); +static CRITICAL_NEST: AtomicU8 = AtomicU8::new(0); +static CRITICAL_TOKEN: AtomicU32 = AtomicU32::new(0); + +#[repr(C)] +#[derive(Default, Clone, Copy)] +struct BtdmOsalPtr { + pub ptr: *mut c_void, +} + +type EventFn = unsafe extern "C" fn(*mut BtdmOsalPtr); + +struct Event { + queued: bool, + fn_ptr: Option, + arg: *mut c_void, +} + +struct Callout { + timer: ets_timer, + evq: *mut BtdmOsalPtr, + ev: BtdmOsalPtr, + expiry_ms: u32, +} + +#[inline(always)] +fn event_inner(ev: *mut BtdmOsalPtr) -> &'static mut Event { + let ev = unwrap!(unsafe { ev.as_ref() }, "event is null"); + unwrap!( + unsafe { ev.ptr.cast::().as_mut() }, + "event inner is null" + ) +} + +fn alloc() -> *mut T { + unsafe { crate::compat::malloc::calloc(1, size_of::()).cast() } +} + +fn free_ptr(ptr: *mut c_void) { + unsafe { crate::compat::malloc::free(ptr.cast()) } +} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_osal_eventq_init(evq: *mut BtdmOsalPtr) { + trace!("wr_btdm_osal_eventq_init {:?}", evq); + let evq = unwrap!(unsafe { evq.as_mut() }, "eventq is null"); + if !evq.ptr.is_null() { + return; + } + + evq.ptr = queue_create( + EVENTQ_DEPTH.load(Ordering::Relaxed) as i32, + size_of::<*mut BtdmOsalPtr>() as i32, + ); +} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_osal_eventq_deinit(evq: *mut BtdmOsalPtr) { + trace!("wr_btdm_osal_eventq_deinit {:?}", evq); + let evq = unwrap!(unsafe { evq.as_mut() }, "eventq is null"); + queue_delete(evq.ptr) +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn wr_btdm_osal_eventq_get(evq: *mut BtdmOsalPtr, tmo: u32) -> *mut BtdmOsalPtr { + trace!("wr_btdm_osal_eventq_get {:?} {}", evq, tmo); + let evq = unwrap!(unsafe { evq.as_mut() }, "eventq is null"); + + let mut ev = core::ptr::null_mut::(); + let received = if in_isr() { + if tmo != 0 { + return core::ptr::null_mut(); + } + queue_try_receive_from_isr(evq.ptr, (&raw mut ev).cast(), core::ptr::null_mut()) + } else { + queue_receive(evq.ptr, (&raw mut ev).cast(), blob_ticks_to_micros(tmo)) + }; + + if received != 0 + && let Some(inner) = unsafe { (*ev).ptr.cast::().as_mut() } + { + inner.queued = false; + } + + ev +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn wr_btdm_osal_eventq_put(evq: *mut BtdmOsalPtr, ev: *mut BtdmOsalPtr) { + trace!("wr_btdm_osal_eventq_put {:?} {:?}", evq, ev); + let evq = unwrap!(unsafe { evq.as_mut() }, "eventq is null"); + + let ret = if in_isr() { + queue_try_send_to_back_from_isr(evq.ptr, (&raw const ev).cast(), core::ptr::null_mut()) + } else { + queue_send_to_back(evq.ptr, (&raw const ev).cast(), OSI_FUNCS_TIME_BLOCKING) + }; + + assert_ne!(ret, 0); +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn wr_btdm_osal_eventq_put_to_front(evq: *mut BtdmOsalPtr, ev: *mut BtdmOsalPtr) { + trace!("wr_btdm_osal_eventq_put_to_front {:?} {:?}", evq, ev); + let evq = unwrap!(unsafe { evq.as_mut() }, "eventq is null"); + + let ret = if in_isr() { + queue_try_send_to_front_from_isr(evq.ptr, (&raw const ev).cast(), core::ptr::null_mut()) + } else { + queue_send_to_front(evq.ptr, (&raw const ev).cast(), OSI_FUNCS_TIME_BLOCKING) + }; + + assert_ne!(ret, 0); +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn wr_btdm_osal_eventq_remove(evq: *mut BtdmOsalPtr, ev: *mut BtdmOsalPtr) { + trace!("wr_btdm_osal_eventq_remove {:?} {:?}", evq, ev); + let evq = unwrap!(unsafe { evq.as_mut() }, "eventq is null"); + let inner = event_inner(ev); + + if inner.queued { + queue_remove(evq.ptr, (&raw const ev).cast()); + inner.queued = false; + } +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn wr_btdm_osal_eventq_is_empty(evq: *mut BtdmOsalPtr) -> bool { + let evq = unwrap!(unsafe { evq.as_mut() }, "eventq is null"); + queue_messages_waiting(evq.ptr) == 0 +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn wr_btdm_osal_event_run(ev: *mut BtdmOsalPtr) { + trace!("wr_btdm_osal_event_run {:?}", ev); + if let Some(func) = event_inner(ev).fn_ptr { + trace!("calling event handler {:#x}", func as usize); + unsafe { func(ev) }; + } +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn wr_btdm_osal_event_init( + ev: *mut BtdmOsalPtr, + func: Option, + arg: *mut c_void, +) { + let func_p = func.map(|f| f as usize).unwrap_or(0); + trace!("creating event with handler {:#x}", func_p); + let ev = unwrap!(unsafe { ev.as_mut() }, "event is null"); + if ev.ptr.is_null() { + ev.ptr = alloc::().cast(); + } + let inner = unwrap!( + unsafe { ev.ptr.cast::().as_mut() }, + "event alloc failed" + ); + *inner = Event { + queued: false, + fn_ptr: func, + arg, + }; +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn wr_btdm_osal_event_deinit(ev: *mut BtdmOsalPtr) { + trace!("wr_btdm_osal_event_deinit {:?}", ev); + let ev = unwrap!(unsafe { ev.as_mut() }, "event is null"); + if !ev.ptr.is_null() { + free_ptr(ev.ptr); + ev.ptr = core::ptr::null_mut(); + } +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn wr_btdm_osal_event_reset(ev: *mut BtdmOsalPtr) { + trace!("wr_btdm_osal_event_reset {:?}", ev); + let ev = unwrap!(unsafe { ev.as_ref() }, "event is null"); + if let Some(inner) = unsafe { ev.ptr.cast::().as_mut() } { + inner.queued = false; + } +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn wr_btdm_osal_event_is_queued(ev: *mut BtdmOsalPtr) -> bool { + trace!("wr_btdm_osal_event_is_queued {:?}", ev); + event_inner(ev).queued +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn wr_btdm_osal_event_get_arg(ev: *mut BtdmOsalPtr) -> *mut c_void { + trace!("wr_btdm_osal_event_get_arg {:?}", ev); + event_inner(ev).arg +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn wr_btdm_osal_event_set_arg(ev: *mut BtdmOsalPtr, arg: *mut c_void) { + trace!("wr_btdm_osal_event_set_arg {:?} {:?}", ev, arg); + event_inner(ev).arg = arg; +} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_osal_mutex_init(mu: *mut BtdmOsalPtr) -> i32 { + trace!("wr_btdm_osal_mutex_init {:?}", mu); + let mu = unwrap!(unsafe { mu.as_mut() }, "mutex is null"); + if mu.ptr.is_null() { + mu.ptr = mutex::mutex_create(true); + } + if mu.ptr.is_null() { + BTDM_OSAL_INVALID_PARM + } else { + BTDM_OSAL_OK + } +} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_osal_mutex_deinit(mu: *mut BtdmOsalPtr) -> i32 { + trace!("wr_btdm_osal_mutex_deinit {:?}", mu); + let mu = unwrap!(unsafe { mu.as_mut() }, "mutex is null"); + if mu.ptr.is_null() { + return BTDM_OSAL_INVALID_PARM; + } + mutex::mutex_delete(mu.ptr); + mu.ptr = core::ptr::null_mut(); + BTDM_OSAL_OK +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn wr_btdm_osal_mutex_pend(mu: *mut BtdmOsalPtr, timeout: u32) -> i32 { + trace!("wr_btdm_osal_mutex_pend {:?} {}", mu, timeout); + let mu = unwrap!(unsafe { mu.as_ref() }, "mutex is null"); + if mu.ptr.is_null() || in_isr() { + return BTDM_OSAL_INVALID_PARM; + } + + if mutex::mutex_lock_with_timeout(mu.ptr, blob_ticks_to_micros(timeout)) != 0 { + BTDM_OSAL_OK + } else { + BTDM_OSAL_TIMEOUT + } +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn wr_btdm_osal_mutex_release(mu: *mut BtdmOsalPtr) -> i32 { + trace!("wr_btdm_osal_mutex_release {:?}", mu); + let mu = unwrap!(unsafe { mu.as_ref() }, "mutex is null"); + if mu.ptr.is_null() || in_isr() { + return BTDM_OSAL_INVALID_PARM; + } + if mutex::mutex_unlock(mu.ptr) != 0 { + BTDM_OSAL_OK + } else { + BTDM_OSAL_TIMEOUT + } +} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_osal_sem_init(sem: *mut BtdmOsalPtr, tokens: u16) -> i32 { + trace!("wr_btdm_osal_sem_init {:?} {:?}", sem, tokens); + let sem = unwrap!(unsafe { sem.as_mut() }, "sem is null"); + if sem.ptr.is_null() { + sem.ptr = semaphore::sem_create(128, tokens as u32); + } + if sem.ptr.is_null() { + BTDM_OSAL_INVALID_PARM + } else { + BTDM_OSAL_OK + } +} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_osal_sem_deinit(sem: *mut BtdmOsalPtr) -> i32 { + trace!("wr_btdm_osal_sem_deinit {:?}", sem); + let sem = unwrap!(unsafe { sem.as_mut() }, "sem is null"); + if sem.ptr.is_null() { + return BTDM_OSAL_INVALID_PARM; + } + semaphore::sem_delete(sem.ptr); + sem.ptr = core::ptr::null_mut(); + BTDM_OSAL_OK +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn wr_btdm_osal_sem_pend(sem: *mut BtdmOsalPtr, timeout: u32) -> i32 { + trace!("wr_btdm_osal_sem_pend {:?} {}", sem, timeout); + let sem = unwrap!(unsafe { sem.as_ref() }, "sem is null"); + if sem.ptr.is_null() { + return BTDM_OSAL_INVALID_PARM; + } + + let taken = if in_isr() { + if timeout != 0 { + return BTDM_OSAL_INVALID_PARM; + } + + semaphore::sem_try_take_from_isr(sem.ptr, core::ptr::null_mut()) + } else { + semaphore::sem_take(sem.ptr, blob_ticks_to_micros(timeout)) + }; + if taken != 0 { + BTDM_OSAL_OK + } else { + BTDM_OSAL_TIMEOUT + } +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn wr_btdm_osal_sem_release(sem: *mut BtdmOsalPtr) -> i32 { + trace!("wr_btdm_osal_sem_release {:?}", sem); + let sem = unwrap!(unsafe { sem.as_ref() }, "sem is null"); + if sem.ptr.is_null() { + return BTDM_OSAL_INVALID_PARM; + } + let given = if in_isr() { + semaphore::sem_try_give_from_isr(sem.ptr, core::ptr::null_mut()) + } else { + semaphore::sem_give(sem.ptr) + }; + if given != 0 { + BTDM_OSAL_OK + } else { + BTDM_OSAL_TIMEOUT + } +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn wr_btdm_osal_sem_get_count(sem: *mut BtdmOsalPtr) -> u16 { + trace!("wr_btdm_osal_sem_get_count {:?}", sem); + let Some(sem) = (unsafe { sem.as_ref() }) else { + return 0; + }; + if sem.ptr.is_null() { + return 0; + } + semaphore::sem_count(sem.ptr) as u16 +} + +#[inline(always)] +fn callout_inner(co: *mut BtdmOsalPtr) -> &'static mut Callout { + let co = unwrap!(unsafe { co.as_ref() }, "callout is null"); + unwrap!( + unsafe { co.ptr.cast::().as_mut() }, + "callout inner is null" + ) +} + +unsafe extern "C" fn callout_timer_cb(arg: *mut c_void) { + trace!("callout_timer_cb {:#x}", arg as usize); + let callout = arg.cast::(); + let callout = unwrap!(unsafe { callout.as_mut() }, "callout is null"); + if !callout.evq.is_null() { + wr_btdm_osal_eventq_put(callout.evq, &raw mut callout.ev); + } else { + wr_btdm_osal_event_run(&raw mut callout.ev); + } +} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_osal_callout_init( + co: *mut BtdmOsalPtr, + evq: *mut BtdmOsalPtr, + ev_cb: Option, + ev_arg: *mut c_void, +) -> i32 { + trace!( + "wr_btdm_osal_callout_init co={:#x} evq={:#x}", + co as usize, evq as usize + ); + let co = unwrap!(unsafe { co.as_mut() }, "callout is null"); + if co.ptr.is_null() { + let callout = alloc::(); + if callout.is_null() { + return -1; + } + unsafe { + (*callout).ev.ptr = core::ptr::null_mut(); + compat::timer_compat::compat_timer_setfn( + &raw mut (*callout).timer, + callout_timer_cb, + callout.cast(), + ); + } + co.ptr = callout.cast(); + } + + let callout = co.ptr.cast::(); + unsafe { + (*callout).evq = evq; + wr_btdm_osal_event_init(&raw mut (*callout).ev, ev_cb, ev_arg); + } + + 0 +} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_osal_callout_deinit(co: *mut BtdmOsalPtr) { + trace!("wr_btdm_osal_callout_deinit co={:#x}", co as usize); + let co = unwrap!(unsafe { co.as_mut() }, "callout is null"); + if co.ptr.is_null() { + return; + } + let callout = co.ptr.cast::(); + let timer = unsafe { &raw mut (*callout).timer }; + unsafe { + compat::timer_compat::compat_timer_disarm(timer); + compat::timer_compat::compat_timer_done(timer); + wr_btdm_osal_event_deinit(&raw mut (*callout).ev); + free_ptr(callout.cast()); + } + co.ptr = core::ptr::null_mut(); +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn wr_btdm_osal_callout_reset(co: *mut BtdmOsalPtr, ticks: u32) -> i32 { + trace!( + "wr_btdm_osal_callout_reset co={:#x} ticks={}", + co as usize, ticks + ); + let callout = callout_inner(co); + compat::timer_compat::compat_timer_disarm(&raw mut callout.timer); + if !callout.evq.is_null() { + wr_btdm_osal_eventq_remove(callout.evq, &raw mut callout.ev); + } + let millis = blob_ticks_to_millis(ticks.max(1)); + callout.expiry_ms = wr_btdm_osal_time_get().wrapping_add(millis); + compat::timer_compat::compat_timer_arm(&raw mut callout.timer, millis, false); + BTDM_OSAL_OK +} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_osal_callout_mem_reset(co: *mut BtdmOsalPtr) { + trace!("wr_btdm_osal_callout_mem_reset co={:#x}", co as usize); + let callout = callout_inner(co); + wr_btdm_osal_event_reset(&raw mut callout.ev); +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn wr_btdm_osal_callout_stop(co: *mut BtdmOsalPtr) { + trace!("wr_btdm_osal_callout_stop co={:#x}", co as usize); + let Some(co) = (unsafe { co.as_ref() }) else { + return; + }; + let Some(callout) = (unsafe { co.ptr.cast::().as_mut() }) else { + return; + }; + compat::timer_compat::compat_timer_disarm(&raw mut callout.timer); + if !callout.evq.is_null() { + wr_btdm_osal_eventq_remove(callout.evq, &raw mut callout.ev); + } +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn wr_btdm_osal_callout_is_active(co: *mut BtdmOsalPtr) -> bool { + trace!("wr_btdm_osal_callout_is_active co={:#x}", co as usize); + let callout = callout_inner(co); + compat::timer_compat::compat_timer_is_active(&raw mut callout.timer) +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn wr_btdm_osal_callout_get_ticks(co: *mut BtdmOsalPtr) -> u32 { + trace!("wr_btdm_osal_callout_get_ticks co={:#x}", co as usize); + let callout = callout_inner(co); + callout.expiry_ms +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn wr_btdm_osal_callout_remaining_ticks(co: *mut BtdmOsalPtr, now: u32) -> u32 { + trace!( + "wr_btdm_osal_callout_remaining_ticks co={:#x} now={}", + co as usize, now + ); + let callout = callout_inner(co); + if !compat::timer_compat::compat_timer_is_active(&raw mut callout.timer) { + return 0; + } + callout.expiry_ms.saturating_sub(now) +} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_osal_callout_set_arg(co: *mut BtdmOsalPtr, arg: *mut c_void) { + trace!( + "wr_btdm_osal_callout_set_arg co={:#x} arg={:#x}", + co as usize, arg as usize + ); + let callout = callout_inner(co); + wr_btdm_osal_event_set_arg(&raw mut callout.ev, arg); +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn wr_btdm_osal_time_get() -> u32 { + crate::hal::time::Instant::now() + .duration_since_epoch() + .as_millis() as u32 +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn wr_btdm_osal_time_ms_to_ticks(ms: u32, out_ticks: *mut u32) -> i32 { + unsafe { *out_ticks = millis_to_blob_ticks(ms) }; + BTDM_OSAL_OK +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn wr_btdm_osal_time_ticks_to_ms(ticks: u32, out_ms: *mut u32) -> i32 { + unsafe { *out_ms = blob_ticks_to_millis(ticks) }; + BTDM_OSAL_OK +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn wr_btdm_osal_time_ms_to_ticks32(ms: u32) -> u32 { + millis_to_blob_ticks(ms) +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn wr_btdm_osal_time_ticks_to_ms32(ticks: u32) -> u32 { + blob_ticks_to_millis(ticks) +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn wr_btdm_osal_get_time_forever() -> u32 { + OSI_FUNCS_TIME_BLOCKING +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn wr_btdm_osal_hw_enter_critical() -> u32 { + // We count calls because in at least one place, the blob doesn't pass the returned number back + // to `exit_critical`. + let last = CRITICAL_NEST.fetch_add(1, Ordering::Release); + if last == 0 { + let token = unsafe { crate::ble::ESP_RADIO_LOCK.acquire().inner() }; + CRITICAL_TOKEN.store(token, Ordering::Relaxed); + } + 0 +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn wr_btdm_osal_hw_exit_critical(_ctx: u32) { + if CRITICAL_NEST.fetch_sub(1, Ordering::Release) == 1 { + let last = CRITICAL_TOKEN.load(Ordering::Relaxed); + + unsafe { + crate::ble::ESP_RADIO_LOCK.release(esp_sync::RestoreState::new(last)); + } + } +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn wr_btdm_osal_hw_is_in_critical() -> u8 { + CRITICAL_NEST.load(Ordering::Acquire) +} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_osal_task_create( + fn_ptr: *mut c_void, + name: *const c_char, + stack_size: u32, + arg: *mut c_void, + priority: u32, + task_handle: *mut *mut c_void, + core_id: u32, +) -> i32 { + let name_str = unsafe { str_from_c(name) }; + unsafe { + let task_func = transmute::<*mut c_void, extern "C" fn(*mut c_void)>(fn_ptr); + // Sleeping waits (usleep) let embassy main run even at the blob + // priority. Ready yield-loops at prio 29 starve main. + let task = crate::preempt::task_create( + name_str, + task_func, + arg, + priority.min(crate::preempt::max_task_priority()), + if core_id < Cpu::COUNT as u32 { + Some(core_id) + } else { + None + }, + stack_size as usize, + ); + if !task_handle.is_null() { + *task_handle = task.as_ptr().cast(); + } + } + BTDM_OSAL_OK +} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_osal_task_delete(task_handle: *mut c_void) { + unsafe { + crate::preempt::schedule_task_deletion(NonNull::new(task_handle.cast::<()>())); + } +} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_osal_intr_alloc( + src: i32, + _flags: i32, + fn_ptr: Option, + arg: *mut c_void, + _handle: *mut *mut c_void, +) -> i32 { + let Some(func) = fn_ptr else { + return BTDM_OSAL_INVALID_PARM; + }; + unsafe { chip_specific::osal_intr_alloc(src as u32, func, arg) } +} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_osal_intr_free(_handle: *mut c_void) -> i32 { + 0 +} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_osal_malloc(size: u32, _flags: u32) -> *mut c_void { + unsafe { crate::compat::malloc::malloc(size as usize).cast() } +} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_osal_free(ptr: *mut c_void) { + if !ptr.is_null() { + unsafe { crate::compat::malloc::free(ptr.cast()) }; + } +} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_osal_mmgmt_block_malloc(size: u32) -> *mut c_void { + let raw = wr_btdm_osal_malloc(size.saturating_add(4), 0) as *mut u32; + if raw.is_null() { + return core::ptr::null_mut(); + } + unsafe { + *raw = (5 << 29) | ((size + 4) >> 2); + raw.add(1).cast() + } +} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_osal_mmgmt_block_free(ptr: *mut c_void) { + if ptr.is_null() { + return; + } + wr_btdm_osal_free(unsafe { (ptr as *mut u32).sub(1) }.cast()); +} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_osal_mmgmt_block_copy(dst: *mut c_void, src: *const c_void, size: u16) { + unsafe extern "C" { + fn r_ble_lll_mmgmt_block_copy(dst: *mut c_void, src: *mut c_void, size: u16); + } + unsafe { r_ble_lll_mmgmt_block_copy(dst, src.cast_mut(), size) }; +} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_osal_read_efuse_mac(mac: *mut u8) -> i32 { + unsafe { crate::common_adapter::read_mac(mac, 2) } +} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_osal_srand(_seed: u32) {} + +#[unsafe(no_mangle)] +extern "C" fn wr_btdm_osal_rand() -> i32 { + unsafe { crate::common_adapter::random() as i32 } +} diff --git a/esp-radio/src/ble/btdm2/os_adapter_esp32s31.rs b/esp-radio/src/ble/btdm2/os_adapter_esp32s31.rs new file mode 100644 index 00000000000..d37cde50917 --- /dev/null +++ b/esp-radio/src/ble/btdm2/os_adapter_esp32s31.rs @@ -0,0 +1,334 @@ +use procmacros::BuilderLite; + +use super::*; +use crate::{ + ble::InvalidConfigError, + hal::{ + interrupt::{self, Priority}, + peripherals::Interrupt, + ram, + system::Cpu, + }, + interrupt_dispatch::Handler, + sys::{c_types::c_void, include::esp_bt_controller_config_t}, +}; + +static ISR_INTERRUPT_MAC: Handler = Handler::new(); +static ISR_INTERRUPT_MAC_INT1: Handler = Handler::new(); +static ISR_INTERRUPT_BB: Handler = Handler::new(); +static ISR_INTERRUPT_BB_NMI: Handler = Handler::new(); +static ISR_INTERRUPT_LP_TIMER: Handler = Handler::new(); +static ISR_INTERRUPT_BLE_TIMER: Handler = Handler::new(); +static ISR_INTERRUPT_BLE_SEC: Handler = Handler::new(); + +/// The interrupts the controller may ask for, each with the slot holding its +/// handler and the trampoline that dispatches from that slot. +const BLE_INTERRUPTS: &[(Interrupt, &Handler)] = &[ + (Interrupt::MODEM_BT_MAC, &ISR_INTERRUPT_MAC), + (Interrupt::MODEM_BT_MAC_INT1, &ISR_INTERRUPT_MAC_INT1), + (Interrupt::MODEM_BT_BB, &ISR_INTERRUPT_BB), + (Interrupt::MODEM_BT_BB_NMI, &ISR_INTERRUPT_BB_NMI), + (Interrupt::MODEM_LP_TIMER, &ISR_INTERRUPT_LP_TIMER), + (Interrupt::MODEM_BLE_TIMER, &ISR_INTERRUPT_BLE_TIMER), + (Interrupt::MODEM_BLE_SEC, &ISR_INTERRUPT_BLE_SEC), +]; + +/// Antenna Selection +#[derive(Default, Clone, Copy, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Antenna { + /// Use Antenna 0 + #[default] + Antenna0 = 0, + /// Use Antenna 1 + Antenna1 = 1, +} + +/// Transmission Power Level +#[derive(Default, Clone, Copy, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum TxPower { + /// -15 dBm + N15, + /// -12 dBm + N12, + /// -9 dBm + N9, + /// -6 dBm + N6, + /// -3 dBm + N3, + /// 0 dBm + N0, + /// 3 dBm + P3, + /// 6 dBm + P6, + /// 9 dBm + #[default] + P9, + /// 12 dBm + P12, + /// 15 dBm + P15, + /// 18 dBm + P18, + /// 20 dBm + P20, +} + +impl TxPower { + fn dbm(self) -> i8 { + match self { + Self::N15 => -15, + Self::N12 => -12, + Self::N9 => -9, + Self::N6 => -6, + Self::N3 => -3, + Self::N0 => 0, + Self::P3 => 3, + Self::P6 => 6, + Self::P9 => 9, + Self::P12 => 12, + Self::P15 => 15, + Self::P18 => 18, + Self::P20 => 20, + } + } +} + +/// Bluetooth controller configuration. +#[derive(BuilderLite, Clone, Copy, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Config { + /// Enables controller modem sleep. + /// + /// The low-power clock source comes from the clock tree (`BLE_LP_CLK`). + /// Set this before the controller starts. The default is off. + modem_sleep: bool, + + /// The priority of the RTOS task. + task_priority: u8, + /// The stack size of the RTOS task. + task_stack_size: u16, + /// The CPU core on which the BLE controller task should run. + task_cpu: Cpu, + /// The maximum number of simultaneous connections. + max_connections: u8, + /// Enable QA test mode. + qa_test_mode: bool, + /// Default TX antenna. + default_tx_antenna: Antenna, + /// Default RX antenna. + default_rx_antenna: Antenna, + /// Default TX power. + default_tx_power: TxPower, + /// Coexistence: limit on MAX Tx/Rx time for coded-PHY connection. + limit_time_for_coded_phy_connection: bool, +} + +impl Default for Config { + fn default() -> Self { + Self { + modem_sleep: false, + task_priority: crate::preempt::max_task_priority() + .saturating_sub(2) + .min(255) as u8, + task_stack_size: CONFIG_BT_CTRL_TASK_STACK_SIZE as u16, + task_cpu: Cpu::ProCpu, + max_connections: DEFAULT_BT_LE_MAX_CONNECTIONS as u8, + qa_test_mode: false, + default_tx_antenna: Antenna::default(), + default_rx_antenna: Antenna::default(), + default_tx_power: TxPower::default(), + limit_time_for_coded_phy_connection: false, + } + } +} + +impl Config { + pub(crate) fn validate(&self) -> Result<(), InvalidConfigError> { + crate::ble::validate_range!( + self, + task_priority, + 0, + crate::preempt::max_task_priority().min(255) as u8 + ); + crate::ble::validate_range!(self, max_connections, 1, 10); + Ok(()) + } +} + +pub(crate) fn create_ble_config(config: &Config) -> esp_bt_controller_config_t { + // BLE-only defaults from IDF `BT_CONTROLLER_INIT_CONFIG_DEFAULT`. + let ble = esp_bt_controller_config_t__bindgen_ty_1 { + config_version: BLE_CONFIG_VERSION, + ble_ll_resolv_list_size: CONFIG_BT_LE_LL_RESOLV_LIST_SIZE as u16, + ble_hci_evt_hi_buf_count: DEFAULT_BT_LE_HCI_EVT_HI_BUF_COUNT as u16, + ble_hci_evt_lo_buf_count: DEFAULT_BT_LE_HCI_EVT_LO_BUF_COUNT as u16, + ble_ll_sync_list_cnt: DEFAULT_BT_LE_MAX_PERIODIC_ADVERTISER_LIST as u8, + ble_ll_sync_cnt: DEFAULT_BT_LE_MAX_PERIODIC_SYNCS as u8, + ble_ll_rsp_dup_list_count: CONFIG_BT_LE_LL_DUP_SCAN_LIST_COUNT as u16, + ble_ll_adv_dup_list_count: CONFIG_BT_LE_LL_DUP_SCAN_LIST_COUNT as u16, + ble_ll_tx_pwr_dbm: config.default_tx_power.dbm() as u8, + rtc_freq: super::super::lp_clk::frequency_hz() as u64, + ble_ll_sca: CONFIG_BT_LE_LL_SCA as _, + ble_ll_scan_phy_number: BLE_LL_SCAN_PHY_NUMBER_N as u8, + ble_ll_conn_def_auth_pyld_tmo: BLE_LL_CONN_DEF_AUTH_PYLD_TMO_N as u16, + ble_ll_jitter_usecs: BLE_LL_JITTER_USECS_N as u8, + ble_ll_sched_max_adv_pdu_usecs: BLE_LL_SCHED_MAX_ADV_PDU_USECS_N as u16, + ble_ll_sched_direct_adv_max_usecs: BLE_LL_SCHED_DIRECT_ADV_MAX_USECS_N as u16, + ble_ll_sched_adv_max_usecs: BLE_LL_SCHED_ADV_MAX_USECS_N as u16, + ble_scan_rsp_data_max_len: DEFAULT_BT_LE_SCAN_RSP_DATA_MAX_LEN_N as u16, + ble_ll_cfg_num_hci_cmd_pkts: BLE_LL_CFG_NUM_HCI_CMD_PKTS_N as u8, + ble_ll_ctrl_proc_timeout_ms: BLE_LL_CTRL_PROC_TIMEOUT_MS_N, + nimble_max_connections: config.max_connections as u16, + ble_whitelist_size: DEFAULT_BT_NIMBLE_WHITELIST_SIZE as u8, + ble_acl_buf_size: DEFAULT_BT_LE_ACL_BUF_SIZE as u16, + ble_acl_buf_count: DEFAULT_BT_LE_ACL_BUF_COUNT as u16, + ble_hci_evt_buf_size: DEFAULT_BT_LE_HCI_EVT_BUF_SIZE as u16, + ble_multi_adv_instances: DEFAULT_BT_LE_MAX_EXT_ADV_INSTANCES as u16, + ble_ext_adv_max_size: DEFAULT_BT_LE_EXT_ADV_MAX_SIZE as u16, + controller_task_stack_size: config.task_stack_size, + controller_task_prio: config.task_priority, + controller_run_cpu: config.task_cpu as u8, + enable_qa_test: config.qa_test_mode as u8, + enable_bqb_test: 0, + enable_tx_cca: DEFAULT_BT_LE_TX_CCA_ENABLED as u8, + cca_rssi_thresh: (256 - DEFAULT_BT_LE_CCA_RSSI_THRESH) as u8, + sleep_en: u8::from(config.modem_sleep), + coex_phy_coded_tx_rx_time_limit: if cfg!(feature = "coex") { + config.limit_time_for_coded_phy_connection as u8 + } else { + DEFAULT_BT_LE_COEX_PHY_CODED_TX_RX_TLIM_EFF as u8 + }, + dis_scan_backoff: 0, + ble_scan_classify_filter_enable: 1, + cca_drop_mode: 0, + cca_low_tx_pwr: 0, + main_xtal_freq: CONFIG_XTAL_FREQ as u8, + ignore_wl_for_direct_adv: 0, + enable_pcl: 0, + csa2_select: 1, + enable_csr: 0, + backoff_rssi: -100, + iso_enabled: DEFAULT_BT_LE_ISO_ENABLED != 0, + iso_bqb_test: false, + iso_fra_unseg: DEFAULT_BT_LE_ISO_FRA_UNSEG != 0, + iso_nsfc_en: DEFAULT_BT_LE_ISO_NSFC_EN != 0, + iso_nsfc_num: DEFAULT_BT_LE_ISO_NSFC_NUM as u8, + iso_buf_count: DEFAULT_BT_LE_ISO_BUF_COUNT as u8, + iso_buf_size: DEFAULT_BT_LE_ISO_BUF_SIZE as u16, + iso_big_count: DEFAULT_BT_LE_ISO_BIG as u8, + iso_bis_count: DEFAULT_BT_LE_ISO_BIS as u16, + iso_bis_per_big: DEFAULT_BT_LE_ISO_BIS_PER_BIG as u8, + iso_cig_count: DEFAULT_BT_LE_ISO_CIG as u8, + iso_cis_count: DEFAULT_BT_LE_ISO_CIS as u16, + iso_cis_per_cig: DEFAULT_BT_LE_ISO_CIS_PER_CIG as u8, + config_magic: BLE_CONFIG_MAGIC, + }; + + let btdm = esp_btdm_controller_config_t { + version: BTDM_CONFIG_VERSION, + task_stack_size: config.task_stack_size, + task_prio: config.task_priority, + task_run_cpu: config.task_cpu as u8, + hci_cmd_num: CONFIG_BT_CTRL_HCI_CMD_NUM as u8, + hci_conn_num: config.max_connections, + sleep_en: u8::from(config.modem_sleep), + version_num: 0, + bluetooth_mode: esp_bt_mode_t_ESP_BT_MODE_BLE as u8, + magic: BTDM_CONFIG_MAGIC_VALUE, + }; + + esp_bt_controller_config_t { + ble, + bredr: unsafe { core::mem::zeroed() }, + btdm, + } +} + +pub(crate) fn btdm_controller_mem_init() {} + +pub(crate) fn bt_periph_module_enable() { + crate::radio_clocks::clocks_ll::enable_bt(true); +} + +pub(crate) fn disable_sleep_mode() {} + +pub(super) unsafe fn osal_intr_alloc( + source: u32, + func: unsafe extern "C" fn(*mut c_void), + arg: *mut c_void, +) -> i32 { + trace!( + "btdm osal_intr_alloc source={} fn={:?} arg={:?}", + source, func, arg + ); + + // The blob passes IDF interrupt source numbers, which are the PAC's + // `Interrupt` discriminants. + let entry = u8::try_from(source) + .ok() + .and_then(|source| Interrupt::try_from(source).ok()) + .and_then(|source| BLE_INTERRUPTS.iter().find(|(int, _)| *int == source)); + + let Some(&(int, slot)) = entry else { + panic!("Unsupported BLE interrupt source {}", source); + }; + + slot.set(func as *const c_void, arg); + + interrupt::enable(int, Priority::Priority3); + 0 +} + +pub(crate) fn shutdown_ble_isr() { + for &(int, _) in BLE_INTERRUPTS { + for core in Cpu::all() { + interrupt::disable(core, int); + } + } +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn MODEM_BT_MAC() { + ISR_INTERRUPT_MAC.dispatch(); +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn MODEM_BT_BB() { + ISR_INTERRUPT_BB.dispatch(); +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn MODEM_BT_BB_NMI() { + ISR_INTERRUPT_BB_NMI.dispatch(); +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn MODEM_LP_TIMER() { + ISR_INTERRUPT_LP_TIMER.dispatch(); +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn MODEM_BLE_TIMER() { + ISR_INTERRUPT_BLE_TIMER.dispatch(); +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn MODEM_BLE_SEC() { + ISR_INTERRUPT_BLE_SEC.dispatch(); +} + +#[unsafe(no_mangle)] +#[ram] +extern "C" fn MODEM_BT_MAC_INT1() { + ISR_INTERRUPT_MAC_INT1.dispatch(); +} diff --git a/esp-radio/src/ble/controller/mod.rs b/esp-radio/src/ble/controller/mod.rs index 4faddcc5eb3..b4a38e9bcb6 100644 --- a/esp-radio/src/ble/controller/mod.rs +++ b/esp-radio/src/ble/controller/mod.rs @@ -11,20 +11,21 @@ use bt_hci::{ }; use bt_hci_transport::{PacketKind, PacketToController, PacketToHost}; use docsplay::Display; +use embassy_sync::mutex::Mutex; use esp_phy::PhyInitGuard; +use esp_sync::RawMutex; use crate::{ RadioRefGuard, asynch::AtomicWaker, ble::{ Config, + HciOutCollector, InvalidConfigError, have_hci_packet, have_hci_read_data, read_hci, read_next, - send_hci, - send_hci_async, take_next, }, }; @@ -52,6 +53,9 @@ pub struct BleConnector<'d> { _phy_init_guard: PhyInitGuard<'d>, _device: crate::hal::peripherals::BT<'d>, _guard: RadioRefGuard, + /// The `Transport` implementations write through a shared reference, so they need the mutex to + /// get at the writer. + hci_writer: Mutex, } impl Drop for BleConnector<'_> { @@ -75,6 +79,7 @@ impl<'d> BleConnector<'d> { _phy_init_guard: crate::ble::ble_init(&config), _device: device, _guard, + hci_writer: Mutex::new(HciOutCollector::new()), }) } @@ -117,7 +122,7 @@ impl<'d> BleConnector<'d> { /// Returns the number of bytes written, which is at most one packet. #[instability::unstable] pub fn write(&mut self, buf: &[u8]) -> Result { - Ok(send_hci(buf)) + Ok(self.hci_writer.get_mut().write(buf)) } /// Write to HCI asynchronously. @@ -125,7 +130,7 @@ impl<'d> BleConnector<'d> { /// Returns the number of bytes written, which is at most one packet. #[instability::unstable] pub async fn write_async(&mut self, buf: &[u8]) -> Result { - Ok(send_hci_async(buf).await) + Ok(self.hci_writer.get_mut().write_async(buf).await) } } @@ -280,27 +285,6 @@ impl core::future::Future for HciPacketReadyEventFuture { } } -/// The HCI output of the BLE controller. -/// -/// The transport implementations use this zero-sized writer to serialize packets directly into the -/// controller. -struct HciWriter; - -impl embedded_io_07::ErrorType for HciWriter { - type Error = BleConnectorError; -} - -impl embedded_io_async_07::Write for HciWriter { - async fn write(&mut self, buf: &[u8]) -> Result { - Ok(send_hci_async(buf).await) - } - - async fn flush(&mut self) -> Result<(), Self::Error> { - // nothing to do - Ok(()) - } -} - impl From> for BleConnectorError { fn from(_e: bt_hci_transport::ReadHciError) -> Self { BleConnectorError::Unknown @@ -343,8 +327,9 @@ impl bt_hci::transport::Transport for BleConnector<'_> { /// Write a complete HCI packet from the tx buffer async fn write(&self, val: &T) -> Result<(), Self::Error> { + let mut writer = self.hci_writer.lock().await; bt_hci::transport::WithIndicator::new(val) - .write_hci_async(HciWriter) + .write_hci_async(&mut *writer) .await } } @@ -363,8 +348,9 @@ impl bt_hci_transport::Transport for BleConnector<'_> { /// Write a complete HCI packet from the tx buffer async fn write(&self, tx: &P) -> Result<(), Self::Error> { + let mut writer = self.hci_writer.lock().await; bt_hci_transport::WithIndicator::new(tx) - .write_hci_async(HciWriter) + .write_hci_async(&mut *writer) .await } } diff --git a/esp-radio/src/ble/lp_clk.rs b/esp-radio/src/ble/lp_clk.rs new file mode 100644 index 00000000000..18051112da6 --- /dev/null +++ b/esp-radio/src/ble/lp_clk.rs @@ -0,0 +1,98 @@ +//! BLE low-power clock, read from the clock tree. +//! +//! The tree reports the divided output frequency. An RC source has no calibrated rate, so selecting +//! one panics here. + +use esp_hal::clock::ll::{self, BleLpClkConfig, BleLpClkSclk}; + +/// BTDM blob parameters for the selected low-power clock. +#[cfg(bt_controller = "btdm")] +pub(crate) struct BtdmLpClk { + /// Value for `btdm_lpclk_select_src`. Crystal is 0. RTC slow is 2. + pub select: u32, + /// Value for `btdm_lpclk_set_div`. + pub divider: u32, + /// Microseconds per low-power cycle, in Q19 format. + pub lpcycle_us: u32, +} + +fn selected() -> BleLpClkConfig { + ll::ClockTree::with(|clocks| clocks.ble_lp_clk()).expect("BLE_LP_CLK is not configured") +} + +/// Turns the BLE low-power clock on for as long as the controller runs. +/// +/// The controller wakes from sleep on this clock, so it must keep running even while the +/// controller is asleep. +pub(crate) fn request() { + ll::ClockTree::with(ll::request_ble_lp_clk); +} + +/// Releases the request taken by [`request`]. +pub(crate) fn release() { + ll::ClockTree::with(ll::release_ble_lp_clk); +} + +fn reject_rc(sclk: BleLpClkSclk) { + let rc = match sclk { + #[cfg(not(any(esp32, esp32c3, esp32s3)))] + BleLpClkSclk::RcSlow => true, + #[cfg(not(any(esp32, esp32c2, esp32c3, esp32s3)))] + BleLpClkSclk::Rc32k => true, + #[cfg(any(esp32, esp32c3, esp32s3))] + BleLpClkSclk::RtcSlow => { + matches!( + ll::ClockTree::with(|clocks| clocks.rtc_slow_clk()), + Some(ll::RtcSlowClkConfig::RcSlow) | Some(ll::RtcSlowClkConfig::RcFast) + ) + } + _ => false, + }; + if rc { + panic!("RC clocks cannot be selected as the BLE low-power clock"); + } +} + +/// Returns the divided BLE low-power clock frequency, in hertz. +/// +/// # Panics +/// +/// Panics when `BLE_LP_CLK` is not configured, or when its source is an RC oscillator. +pub(crate) fn frequency_hz() -> u32 { + let config = selected(); + reject_rc(config.sclk()); + ll::ble_lp_clk_frequency() +} + +/// Returns the BTDM blob clock-select parameters for the configured source. +#[cfg(bt_controller = "btdm")] +pub(crate) fn btdm() -> BtdmLpClk { + let config = selected(); + reject_rc(config.sclk()); + + let hz = ll::ble_lp_clk_frequency().max(1); + let lpcycle_us = ((1_000_000u64 << 19) / u64::from(hz)) as u32; + + // The C3 and S3 blob takes the divide ratio. The ESP32 blob takes the divider field, which is + // one less than that ratio. RTC slow passes the field through on every chip. + let divider = match config.sclk() { + BleLpClkSclk::Xtal => { + cfg_select! { + esp32 => config.divisor(), + _ => config.divisor() + 1, + } + } + BleLpClkSclk::RtcSlow => config.divisor(), + }; + + let select = match config.sclk() { + BleLpClkSclk::Xtal => 0, + BleLpClkSclk::RtcSlow => 2, + }; + + BtdmLpClk { + select, + divider, + lpcycle_us, + } +} diff --git a/esp-radio/src/ble/mod.rs b/esp-radio/src/ble/mod.rs index 6f27baa643b..a999696fec8 100644 --- a/esp-radio/src/ble/mod.rs +++ b/esp-radio/src/ble/mod.rs @@ -1,14 +1,16 @@ //! Bluetooth Low Energy HCI interface +mod lp_clk; + #[cfg_attr(bt_controller = "btdm", path = "btdm/mod.rs")] #[cfg_attr(bt_controller = "npl", path = "npl/mod.rs")] +#[cfg_attr(bt_controller = "btdm2", path = "btdm2/mod.rs")] pub(crate) mod porting; use alloc::{boxed::Box, collections::vec_deque::VecDeque}; -use core::mem::MaybeUninit; use docsplay::Display; use esp_sync::NonReentrantMutex; -pub(crate) use porting::{ble_deinit, ble_init, send_hci, send_hci_async}; +pub(crate) use porting::{ble_deinit, ble_init}; /// An error that is returned when the configuration is invalid. #[derive(Display, Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -23,14 +25,55 @@ pub use porting::chip_specific::*; pub(crate) static ESP_RADIO_LOCK: esp_sync::RawMutex = esp_sync::RawMutex::new(); +/// Returns whether the caller runs in an interrupt handler. +/// +/// The controller calls some of the OS glue from its interrupt handlers. Those calls must not +/// block, so they use the try-variant of the operation. +#[cfg(any(bt_controller = "npl", bt_controller = "btdm2"))] +pub(crate) fn in_isr() -> bool { + !crate::hal::interrupt::RunLevel::current().is_thread() +} + +static MODEM_SLEEP: portable_atomic::AtomicBool = portable_atomic::AtomicBool::new(false); +static MODEM_PHY_OFF: portable_atomic::AtomicBool = portable_atomic::AtomicBool::new(false); + +pub(crate) fn set_modem_sleep(enabled: bool) { + MODEM_SLEEP.store(enabled, portable_atomic::Ordering::Relaxed); +} + +#[allow(dead_code, reason = "The ESP32 BTDM adapter reads this flag")] +pub(crate) fn modem_sleep_enabled() -> bool { + MODEM_SLEEP.load(portable_atomic::Ordering::Relaxed) +} + +/// Drops the extra PHY reference taken around controller sleep. +/// +/// The controller's [`esp_phy::PhyInitGuard`] stays alive. This pairs with +/// [`modem_phy_acquire`]. +pub(crate) fn modem_phy_release() { + if !MODEM_PHY_OFF.swap(true, portable_atomic::Ordering::SeqCst) { + esp_phy::disable_phy(); + } +} + +/// Restores the PHY reference if sleep left it off. +pub(crate) fn modem_phy_acquire() { + if MODEM_PHY_OFF.swap(false, portable_atomic::Ordering::SeqCst) { + core::mem::forget(esp_phy::enable_phy()); + } +} + unstable_module! { pub mod controller; } +// btdm2 registers its own `wr_btdm_osal_malloc` / `wr_btdm_osal_free` wrappers. +#[cfg(not(bt_controller = "btdm2"))] pub(crate) unsafe extern "C" fn malloc(size: u32) -> *mut crate::sys::c_types::c_void { unsafe { crate::compat::malloc::malloc(size as usize).cast() } } +#[cfg(not(bt_controller = "btdm2"))] pub(crate) unsafe extern "C" fn free(ptr: *mut crate::sys::c_types::c_void) { unsafe { crate::compat::malloc::free(ptr.cast()) } } @@ -47,8 +90,6 @@ static BT_STATE: NonReentrantMutex = NonReentrantMutex::new(BleState { partial_read: None, }); -static mut HCI_OUT_COLLECTOR: MaybeUninit = MaybeUninit::uninit(); - #[derive(PartialEq, Debug)] enum HciOutType { Unknown, @@ -64,7 +105,7 @@ const MAX_HCI_PACKET_LEN: usize = 259; /// The byte-stream write APIs put no constraint on where the caller splits a packet, and one /// write can hold several packets. The collector takes only the bytes that the packet in progress /// still needs, so that it never runs past a packet boundary. -struct HciOutCollector { +pub(crate) struct HciOutCollector { data: [u8; MAX_HCI_PACKET_LEN], index: usize, ready: bool, @@ -72,7 +113,7 @@ struct HciOutCollector { } impl HciOutCollector { - fn new() -> HciOutCollector { + pub(crate) const fn new() -> HciOutCollector { HciOutCollector { data: [0u8; MAX_HCI_PACKET_LEN], index: 0, @@ -173,24 +214,43 @@ impl HciOutCollector { fn packet(&self) -> &[u8] { &self.data[0..self.index] } -} -/// Collects bytes of the host's stream, and passes the packet to `send` once it is complete. -/// -/// This behaves like a byte-stream write: it handles at most one packet, and returns the number -/// of bytes it took from `data`. Bytes that belong to the next packet stay in `data`, so the -/// caller offers the rest in a later call. A non-empty `data` always yields a non-zero count. -pub(crate) fn collect_and_send(data: &[u8], send: impl FnOnce(&[u8])) -> usize { - let hci_out = unsafe { (*core::ptr::addr_of_mut!(HCI_OUT_COLLECTOR)).assume_init_mut() }; + pub(crate) fn write(&mut self, buf: &[u8]) -> usize { + let taken = self.push(buf); + + if self.is_ready() { + porting::send(self.packet()); + self.reset(); + } + + taken + } + + pub(crate) async fn write_async(&mut self, buf: &[u8]) -> usize { + let taken = self.push(buf); - let taken = hci_out.push(data); + if self.is_ready() { + porting::send_async(self.packet()).await; + self.reset(); + } + + taken + } +} + +impl embedded_io_07::ErrorType for HciOutCollector { + type Error = controller::BleConnectorError; +} - if hci_out.is_ready() { - send(hci_out.packet()); - hci_out.reset(); +impl embedded_io_async_07::Write for HciOutCollector { + async fn write(&mut self, buf: &[u8]) -> Result { + Ok(self.write_async(buf).await) } - taken + async fn flush(&mut self) -> Result<(), Self::Error> { + // nothing to do + Ok(()) + } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/esp-radio/src/ble/npl/mod.rs b/esp-radio/src/ble/npl/mod.rs index 85535c6b43d..f4a4e9bf5b3 100644 --- a/esp-radio/src/ble/npl/mod.rs +++ b/esp-radio/src/ble/npl/mod.rs @@ -1,14 +1,10 @@ use alloc::vec::Vec; -use core::{ - mem::transmute, - ptr::{NonNull, addr_of_mut}, -}; +use core::{mem::transmute, ptr::NonNull}; use esp_phy::PhyInitGuard; -use super::{Config, ReceivedPacket}; +use super::{Config, ReceivedPacket, in_isr}; use crate::{ - ble::{HCI_OUT_COLLECTOR, HciOutCollector}, compat::{self, OSI_FUNCS_TIME_BLOCKING, common::str_from_c, queue}, hal::time::Instant, sys::{c_types::*, include::*}, @@ -526,6 +522,8 @@ pub(crate) struct npl_funcs_t { p_ble_npl_hw_exit_critical: Option, p_ble_npl_get_time_forever: Option u32>, p_ble_npl_hw_is_in_critical: Option u8>, + p_ble_npl_eventq_put_to_front: + Option, } static G_NPL_FUNCS: npl_funcs_t = npl_funcs_t { @@ -573,6 +571,7 @@ static G_NPL_FUNCS: npl_funcs_t = npl_funcs_t { p_ble_npl_hw_exit_critical: Some(ble_npl_hw_exit_critical), p_ble_npl_get_time_forever: Some(ble_npl_get_time_forever), p_ble_npl_hw_is_in_critical: Some(ble_npl_hw_is_in_critical), + p_ble_npl_eventq_put_to_front: Some(ble_npl_eventq_put_to_front), }; #[repr(C)] @@ -940,6 +939,19 @@ unsafe extern "C" fn ble_npl_eventq_remove( unsafe extern "C" fn ble_npl_eventq_put(queue: *mut ble_npl_eventq, event: *const ble_npl_event) { trace!("ble_npl_eventq_put {:?} {:?}", queue, event); + unsafe { eventq_insert(queue, event, false) } +} + +unsafe extern "C" fn ble_npl_eventq_put_to_front( + queue: *mut ble_npl_eventq, + event: *const ble_npl_event, +) { + trace!("ble_npl_eventq_put_to_front {:?} {:?}", queue, event); + + unsafe { eventq_insert(queue, event, true) } +} + +unsafe fn eventq_insert(queue: *mut ble_npl_eventq, event: *const ble_npl_event, front: bool) { let evt = unsafe { (*event).dummy } as *mut Event; assert!(!evt.is_null()); @@ -953,13 +965,29 @@ unsafe extern "C" fn ble_npl_eventq_put(queue: *mut ble_npl_eventq, event: *cons } let wrapper = unwrap!(unsafe { queue.as_mut() }, "queue wrapper is null"); - + let handle = wrapper.dummy as _; // Store the pointer to the ble_npl_event in the queue - this is what we'll need to dequeue. - queue::queue_send_to_back( - wrapper.dummy as _, - (&raw const event).cast(), - OSI_FUNCS_TIME_BLOCKING, - ); + let item = (&raw const event).cast(); + + let sent = if in_isr() { + if front { + queue::queue_try_send_to_front_from_isr(handle, item, core::ptr::null_mut()) + } else { + queue::queue_try_send_to_back_from_isr(handle, item, core::ptr::null_mut()) + } + } else if front { + queue::queue_send_to_front(handle, item, OSI_FUNCS_TIME_BLOCKING) + } else { + queue::queue_send_to_back(handle, item, OSI_FUNCS_TIME_BLOCKING) + }; + + if sent == 0 { + // The queue is full. Mark the event unqueued so that the controller can post it again. + trace!("Event queue is full, event dropped"); + unsafe { + (*evt).queued = false; + } + } } unsafe extern "C" fn ble_npl_eventq_get( @@ -970,13 +998,18 @@ unsafe extern "C" fn ble_npl_eventq_get( let mut evt = core::ptr::null_mut::(); let wrapper = unwrap!(unsafe { queue.as_mut() }, "queue wrapper is null"); + let item = (&raw mut evt).cast(); + + let received = if in_isr() { + if timeout != 0 { + return core::ptr::null(); + } + queue::queue_try_receive_from_isr(wrapper.dummy as _, item, core::ptr::null_mut()) + } else { + queue::queue_receive(wrapper.dummy as _, item, blob_ticks_to_micros(timeout)) + }; - if queue::queue_receive( - wrapper.dummy as _, - (&raw mut evt).cast(), - blob_ticks_to_micros(timeout), - ) == 1 - { + if received != 0 { trace!("got {:x}", evt as usize); unsafe { let evt = (*evt).dummy as *mut Event; @@ -1081,11 +1114,68 @@ pub(crate) struct BleNplCountInfoT { mutex_count: u16, } +#[crate::hal::ram] +unsafe extern "C" fn controller_sleep_cb(_enable_tick: u32, _arg: *mut c_void) { + super::modem_phy_release(); +} + +#[crate::hal::ram] +unsafe extern "C" fn controller_wakeup_cb(_arg: *mut c_void) { + super::modem_phy_acquire(); +} + +fn register_modem_sleep() { + unsafe extern "C" { + #[cfg(not(esp32c2))] + fn r_ble_lll_sleep_set_sleep_cb( + sleep_cb: unsafe extern "C" fn(u32, *mut c_void), + wakeup_cb: unsafe extern "C" fn(*mut c_void), + sleep_arg: *mut c_void, + wakeup_arg: *mut c_void, + us_to_enabled: u32, + ); + #[cfg(esp32c2)] + fn r_ble_lll_rfmgmt_set_sleep_cb( + sleep_cb: unsafe extern "C" fn(u32, *mut c_void), + wakeup_cb: unsafe extern "C" fn(*mut c_void), + sleep_arg: *mut c_void, + wakeup_arg: *mut c_void, + us_to_enabled: u32, + ); + } + + // ESP-IDF modem-sleep PHY enable delay. C2 adds `BLE_RTC_DELAY_US` (1800) to 500. + let delay_us = cfg_select! { + esp32c2 => 2_300, + esp32h2 => 1_500, + _ => 500, + }; + + unsafe { + cfg_select! { + esp32c2 => r_ble_lll_rfmgmt_set_sleep_cb( + controller_sleep_cb, + controller_wakeup_cb, + core::ptr::null_mut(), + core::ptr::null_mut(), + delay_us, + ), + _ => r_ble_lll_sleep_set_sleep_cb( + controller_sleep_cb, + controller_wakeup_cb, + core::ptr::null_mut(), + core::ptr::null_mut(), + delay_us, + ), + } + } +} + pub(crate) fn ble_init(config: &Config) -> PhyInitGuard<'static> { + super::set_modem_sleep(config.modem_sleep()); + let phy_init_guard; unsafe { - (*addr_of_mut!(HCI_OUT_COLLECTOR)).write(HciOutCollector::new()); - // turn on logging #[allow(static_mut_refs)] #[cfg(all(feature = "print-logs-from-driver", esp32c2))] @@ -1100,6 +1190,8 @@ pub(crate) fn ble_init(config: &Config) -> PhyInitGuard<'static> { self::chip_specific::ble_rtc_clk_init(); + super::lp_clk::request(); + let cfg = chip_specific::create_ble_config(config); let res = esp_register_ext_funcs(&G_OSI_FUNCS as *const ExtFuncsT); @@ -1163,6 +1255,10 @@ pub(crate) fn ble_init(config: &Config) -> PhyInitGuard<'static> { let res = ble_controller_init(&cfg); assert!(res == 0, "ble_controller_init returned {}", res); + if config.modem_sleep() { + register_modem_sleep(); + } + #[cfg(feature = "coex")] crate::sys::include::coex_enable(); @@ -1203,6 +1299,9 @@ pub(crate) fn ble_init(config: &Config) -> PhyInitGuard<'static> { } pub(crate) fn ble_deinit() { + super::modem_phy_acquire(); + super::set_modem_sleep(false); + #[cfg(rng_trng_supported)] esp_hal::rng::TrngSource::decrease_entropy_source_counter(unsafe { esp_hal::Internal::conjure() @@ -1226,6 +1325,8 @@ pub(crate) fn ble_deinit() { esp_unregister_npl_funcs(); esp_unregister_ext_funcs(); } + + super::lp_clk::release(); } unsafe extern "C" fn ble_hs_hci_rx_evt(cmd: *const u8, arg: *const c_void) -> i32 { @@ -1289,20 +1390,12 @@ unsafe extern "C" fn ble_hs_rx_data(om: *const OsMbuf, arg: *const c_void) -> i3 0 } -/// Sends HCI data to the Bluetooth controller. -/// -/// Returns the number of bytes taken from `data`. At most one packet is sent per call, so the -/// caller must offer the remaining bytes again. -pub(crate) fn send_hci(data: &[u8]) -> usize { - super::collect_and_send(data, send_packet) +pub(crate) fn send(data: &[u8]) { + send_packet(data) } -/// Sends HCI data to the Bluetooth controller. -/// -/// Returns the number of bytes taken from `data`. At most one packet is sent per call, so the -/// caller must offer the remaining bytes again. -pub(crate) async fn send_hci_async(data: &[u8]) -> usize { - super::collect_and_send(data, send_packet) +pub(crate) async fn send_async(data: &[u8]) { + send_packet(data) } fn send_packet(packet: &[u8]) { @@ -1311,7 +1404,7 @@ fn send_packet(packet: &[u8]) { super::dump_packet_info(packet); - super::BT_STATE.with(|_state| unsafe { + unsafe { if packet[0] == DATA_TYPE_COMMAND { let cmd = r_ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_CMD); core::ptr::copy_nonoverlapping( @@ -1343,5 +1436,5 @@ fn send_packet(packet: &[u8]) { } else { warn!("Unknown packet kind {} dropped", packet[0]); } - }); + } } diff --git a/esp-radio/src/ble/npl/os_adapter_esp32c2.rs b/esp-radio/src/ble/npl/os_adapter_esp32c2.rs index 8ba02648573..3c430e29a6d 100644 --- a/esp-radio/src/ble/npl/os_adapter_esp32c2.rs +++ b/esp-radio/src/ble/npl/os_adapter_esp32c2.rs @@ -99,6 +99,12 @@ impl TxPower { #[derive(BuilderLite, Clone, Copy, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Config { + /// Enables controller modem sleep. + /// + /// The low-power clock source comes from the clock tree (`BLE_LP_CLK`). + /// Set this before the controller starts. The default is off. + modem_sleep: bool, + /// The priority of the RTOS task. task_priority: u8, @@ -212,6 +218,7 @@ pub struct Config { impl Default for Config { fn default() -> Self { Self { + modem_sleep: false, task_priority: crate::preempt::max_task_priority() .saturating_sub(2) .min(255) as u8, @@ -270,7 +277,7 @@ impl Config { pub(crate) fn create_ble_config(config: &Config) -> esp_bt_controller_config_t { let main_xtal_freq = esp_hal::clock::xtal_clock().as_mhz() as u8; - let rtc_freq = if main_xtal_freq == 26 { 40000 } else { 32000 }; + let rtc_freq = u64::from(super::super::lp_clk::frequency_hz()); // keep them aligned with BT_CONTROLLER_INIT_CONFIG_DEFAULT in ESP-IDF // ideally _some_ of these values should be configurable @@ -320,7 +327,7 @@ pub(crate) fn create_ble_config(config: &Config) -> esp_bt_controller_config_t { ble_hci_uart_uart_parity: 0, enable_tx_cca: config.cca as u8, cca_rssi_thresh: (256 - config.cca_threshold as u32) as u8, - sleep_en: 0, // CONFIG_BT_LE_SLEEP_ENABLE unset + sleep_en: u8::from(config.modem_sleep), coex_phy_coded_tx_rx_time_limit: CONFIG_BT_LE_COEX_PHY_CODED_TX_RX_TLIM_EFF as _, dis_scan_backoff: config.dis_scan_backoff as u8, ble_scan_classify_filter_enable: 0, diff --git a/esp-radio/src/ble/npl/os_adapter_esp32c5.rs b/esp-radio/src/ble/npl/os_adapter_esp32c5.rs index 7712f4f8c59..c151a13a085 100644 --- a/esp-radio/src/ble/npl/os_adapter_esp32c5.rs +++ b/esp-radio/src/ble/npl/os_adapter_esp32c5.rs @@ -87,6 +87,12 @@ impl TxPower { #[derive(BuilderLite, Clone, Copy, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Config { + /// Enables controller modem sleep. + /// + /// The low-power clock source comes from the clock tree (`BLE_LP_CLK`). + /// Set this before the controller starts. The default is off. + modem_sleep: bool, + /// The priority of the RTOS task. task_priority: u8, @@ -206,6 +212,7 @@ pub struct Config { impl Default for Config { fn default() -> Self { Self { + modem_sleep: false, task_priority: crate::preempt::max_task_priority() .saturating_sub(2) .min(255) as u8, @@ -276,7 +283,7 @@ pub(crate) fn create_ble_config(config: &Config) -> esp_bt_controller_config_t { ble_ll_rsp_dup_list_count: config.ll_rsp_dup_list_count, ble_ll_adv_dup_list_count: config.ll_adv_dup_list_count, ble_ll_tx_pwr_dbm: config.default_tx_power.dbm() as u8, - rtc_freq: 32768, + rtc_freq: u64::from(super::super::lp_clk::frequency_hz()), ble_ll_sca: 60, ble_ll_scan_phy_number: 2, ble_ll_conn_def_auth_pyld_tmo: 3000, @@ -301,7 +308,7 @@ pub(crate) fn create_ble_config(config: &Config) -> esp_bt_controller_config_t { enable_bqb_test: config.bqb_test as u8, enable_tx_cca: config.cca as u8, cca_rssi_thresh: (256 - config.cca_threshold as u32) as u8, - sleep_en: 0, + sleep_en: u8::from(config.modem_sleep), coex_phy_coded_tx_rx_time_limit: 0, dis_scan_backoff: config.dis_scan_backoff as u8, ble_scan_classify_filter_enable: 1, diff --git a/esp-radio/src/ble/npl/os_adapter_esp32c6.rs b/esp-radio/src/ble/npl/os_adapter_esp32c6.rs index bce06dd81e6..219fc7a6fd8 100644 --- a/esp-radio/src/ble/npl/os_adapter_esp32c6.rs +++ b/esp-radio/src/ble/npl/os_adapter_esp32c6.rs @@ -87,6 +87,12 @@ impl TxPower { #[derive(BuilderLite, Clone, Copy, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Config { + /// Enables controller modem sleep. + /// + /// The low-power clock source comes from the clock tree (`BLE_LP_CLK`). + /// Set this before the controller starts. The default is off. + modem_sleep: bool, + /// The priority of the RTOS task. task_priority: u8, @@ -206,6 +212,7 @@ pub struct Config { impl Default for Config { fn default() -> Self { Self { + modem_sleep: false, task_priority: crate::preempt::max_task_priority() .saturating_sub(2) .min(255) as u8, @@ -276,7 +283,7 @@ pub(crate) fn create_ble_config(config: &Config) -> esp_bt_controller_config_t { ble_ll_rsp_dup_list_count: config.ll_rsp_dup_list_count, ble_ll_adv_dup_list_count: config.ll_adv_dup_list_count, ble_ll_tx_pwr_dbm: config.default_tx_power.dbm() as u8, - rtc_freq: 32000, + rtc_freq: u64::from(super::super::lp_clk::frequency_hz()), ble_ll_sca: 60, ble_ll_scan_phy_number: 2, ble_ll_conn_def_auth_pyld_tmo: 3000, @@ -301,7 +308,7 @@ pub(crate) fn create_ble_config(config: &Config) -> esp_bt_controller_config_t { enable_bqb_test: config.bqb_test as u8, enable_tx_cca: config.cca as u8, cca_rssi_thresh: (256 - config.cca_threshold as u32) as u8, - sleep_en: 0, + sleep_en: u8::from(config.modem_sleep), coex_phy_coded_tx_rx_time_limit: 0, dis_scan_backoff: config.dis_scan_backoff as u8, ble_scan_classify_filter_enable: 1, diff --git a/esp-radio/src/ble/npl/os_adapter_esp32c61.rs b/esp-radio/src/ble/npl/os_adapter_esp32c61.rs index 30d04f05193..d37665eb9c8 100644 --- a/esp-radio/src/ble/npl/os_adapter_esp32c61.rs +++ b/esp-radio/src/ble/npl/os_adapter_esp32c61.rs @@ -87,6 +87,12 @@ impl TxPower { #[derive(BuilderLite, Clone, Copy, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Config { + /// Enables controller modem sleep. + /// + /// The low-power clock source comes from the clock tree (`BLE_LP_CLK`). + /// Set this before the controller starts. The default is off. + modem_sleep: bool, + /// The priority of the RTOS task. task_priority: u8, @@ -206,6 +212,7 @@ pub struct Config { impl Default for Config { fn default() -> Self { Self { + modem_sleep: false, task_priority: crate::preempt::max_task_priority() .saturating_sub(2) .min(255) as u8, @@ -276,7 +283,7 @@ pub(crate) fn create_ble_config(config: &Config) -> esp_bt_controller_config_t { ble_ll_rsp_dup_list_count: config.ll_rsp_dup_list_count, ble_ll_adv_dup_list_count: config.ll_adv_dup_list_count, ble_ll_tx_pwr_dbm: config.default_tx_power.dbm() as u8, - rtc_freq: 32768, + rtc_freq: u64::from(super::super::lp_clk::frequency_hz()), ble_ll_sca: 60, ble_ll_scan_phy_number: 2, ble_ll_conn_def_auth_pyld_tmo: 3000, @@ -301,7 +308,7 @@ pub(crate) fn create_ble_config(config: &Config) -> esp_bt_controller_config_t { enable_bqb_test: config.bqb_test as u8, enable_tx_cca: config.cca as u8, cca_rssi_thresh: (256 - config.cca_threshold as u32) as u8, - sleep_en: 0, + sleep_en: u8::from(config.modem_sleep), coex_phy_coded_tx_rx_time_limit: 0, dis_scan_backoff: config.dis_scan_backoff as u8, ble_scan_classify_filter_enable: 1, diff --git a/esp-radio/src/ble/npl/os_adapter_esp32h2.rs b/esp-radio/src/ble/npl/os_adapter_esp32h2.rs index ee17f25be42..e3b20d2209c 100644 --- a/esp-radio/src/ble/npl/os_adapter_esp32h2.rs +++ b/esp-radio/src/ble/npl/os_adapter_esp32h2.rs @@ -99,6 +99,12 @@ impl TxPower { #[derive(BuilderLite, Clone, Copy, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Config { + /// Enables controller modem sleep. + /// + /// The low-power clock source comes from the clock tree (`BLE_LP_CLK`). + /// Set this before the controller starts. The default is off. + modem_sleep: bool, + /// The priority of the RTOS task. task_priority: u8, @@ -218,6 +224,7 @@ pub struct Config { impl Default for Config { fn default() -> Self { Self { + modem_sleep: false, task_priority: crate::preempt::max_task_priority() .saturating_sub(2) .min(255) as u8, @@ -286,7 +293,7 @@ pub(crate) fn create_ble_config(config: &Config) -> esp_bt_controller_config_t { ble_ll_rsp_dup_list_count: config.ll_rsp_dup_list_count, ble_ll_adv_dup_list_count: config.ll_adv_dup_list_count, ble_ll_tx_pwr_dbm: config.default_tx_power.dbm() as u8, - rtc_freq: 32000, + rtc_freq: u64::from(super::super::lp_clk::frequency_hz()), ble_ll_sca: CONFIG_BT_LE_LL_SCA as _, ble_ll_scan_phy_number: if CONFIG_BT_LE_LL_CFG_FEAT_LE_CODED_PHY != 0 { 2 @@ -315,7 +322,7 @@ pub(crate) fn create_ble_config(config: &Config) -> esp_bt_controller_config_t { enable_bqb_test: config.bqb_test as u8, enable_tx_cca: config.cca as u8, cca_rssi_thresh: (256 - config.cca_threshold as u32) as u8, - sleep_en: 0, + sleep_en: u8::from(config.modem_sleep), coex_phy_coded_tx_rx_time_limit: CONFIG_BT_LE_COEX_PHY_CODED_TX_RX_TLIM_EFF as _, dis_scan_backoff: config.dis_scan_backoff as u8, ble_scan_classify_filter_enable: 1, diff --git a/esp-radio/src/common_adapter.rs b/esp-radio/src/common_adapter.rs index eb488100b40..507270bdfdb 100644 --- a/esp-radio/src/common_adapter.rs +++ b/esp-radio/src/common_adapter.rs @@ -11,7 +11,10 @@ use crate::{ }; #[ram] -#[cfg(any(feature = "coex", all(feature = "ble", bt_controller = "btdm")))] +#[cfg(any( + feature = "coex", + all(feature = "ble", any(bt_controller = "btdm", bt_controller = "btdm2")) +))] pub(crate) unsafe extern "C" fn is_in_isr() -> i32 { !hal::interrupt::RunLevel::current().is_thread() as i32 } diff --git a/esp-radio/src/compat/mod.rs b/esp-radio/src/compat/mod.rs index ba62106f373..a6510165892 100644 --- a/esp-radio/src/compat/mod.rs +++ b/esp-radio/src/compat/mod.rs @@ -11,7 +11,10 @@ pub mod mutex; pub mod queue; pub mod semaphore; -#[cfg(any(feature = "wifi", all(feature = "ble", bt_controller = "npl")))] +#[cfg(any( + feature = "wifi", + all(feature = "ble", any(bt_controller = "npl", bt_controller = "btdm2")) +))] pub mod timer_compat; pub(crate) const OSI_FUNCS_TIME_BLOCKING: u32 = u32::MAX; diff --git a/esp-radio/src/compat/mutex.rs b/esp-radio/src/compat/mutex.rs index 7dd00671083..e57ba6ad6b2 100644 --- a/esp-radio/src/compat/mutex.rs +++ b/esp-radio/src/compat/mutex.rs @@ -1,6 +1,7 @@ use esp_radio_rtos_driver::semaphore::SemaphoreKind; use crate::{ + compat::OSI_FUNCS_TIME_BLOCKING, preempt::semaphore::{SemaphoreHandle, SemaphorePtr}, sys::c_types::c_void, }; @@ -28,11 +29,29 @@ pub(crate) fn mutex_delete(mutex: *mut c_void) { } pub(crate) fn mutex_lock(mutex: *mut c_void) -> i32 { + mutex_lock_with_timeout(mutex, OSI_FUNCS_TIME_BLOCKING) +} + +pub(crate) fn mutex_lock_with_timeout(mutex: *mut c_void, tick: u32) -> i32 { + let ptr = unwrap!(SemaphorePtr::new(mutex.cast()), "mutex is null"); + + let handle = unsafe { SemaphoreHandle::ref_from_ptr(&ptr) }; + // Assuming `tick` is in microseconds + let timeout = if tick == OSI_FUNCS_TIME_BLOCKING { + None + } else { + Some(tick) + }; + + handle.take(timeout) as i32 +} + +pub(crate) fn mutex_try_lock(mutex: *mut c_void) -> bool { let ptr = unwrap!(SemaphorePtr::new(mutex.cast()), "mutex is null"); let handle = unsafe { SemaphoreHandle::ref_from_ptr(&ptr) }; - handle.take(None) as i32 + handle.try_take() } pub(crate) fn mutex_unlock(mutex: *mut c_void) -> i32 { diff --git a/esp-radio/src/compat/queue.rs b/esp-radio/src/compat/queue.rs index c00ab2ed82b..31d7b602474 100644 --- a/esp-radio/src/compat/queue.rs +++ b/esp-radio/src/compat/queue.rs @@ -17,7 +17,7 @@ pub(crate) fn queue_create(queue_len: c_int, item_size: c_int) -> *mut c_void { } pub(crate) fn queue_delete(queue: *mut c_void) { - trace!("delete_queue {:?}", queue); + trace!("queue_delete {:?}", queue); let ptr = unwrap!(QueuePtr::new(queue.cast()), "queue is null"); @@ -27,7 +27,7 @@ pub(crate) fn queue_delete(queue: *mut c_void) { pub(crate) fn queue_send_to_back(queue: *mut c_void, item: *const c_void, tick: u32) -> i32 { trace!( - "queue_send queue {:?} item {:x} tick {}", + "queue_send_to_back queue {:?} item {:#x} tick {}", queue, item as usize, tick ); @@ -50,7 +50,7 @@ pub(crate) fn queue_try_send_to_back_from_isr( higher_priority_task_waken: *mut bool, ) -> i32 { trace!( - "queue_try_send_to_back_from_isr queue {:?} item {:x}", + "queue_try_send_to_back_from_isr queue {:?} item {:#x}", queue, item as usize ); @@ -63,10 +63,29 @@ pub(crate) fn queue_try_send_to_back_from_isr( } } +pub(crate) fn queue_try_send_to_front_from_isr( + queue: *mut c_void, + item: *const c_void, + higher_priority_task_waken: *mut bool, +) -> i32 { + trace!( + "queue_try_send_to_front_from_isr queue {:?} item {:#x}", + queue, item as usize + ); + + let ptr = unwrap!(QueuePtr::new(queue.cast()), "queue is null"); + + let handle = unsafe { QueueHandle::ref_from_ptr(&ptr) }; + + unsafe { + handle.try_send_to_front_from_isr(item.cast(), higher_priority_task_waken.as_mut()) as i32 + } +} + pub(crate) fn queue_send_to_front(queue: *mut c_void, item: *const c_void, tick: u32) -> i32 { trace!( - "queue_send_to_front {:?} item {:?} tick {}", - queue, item, tick + "queue_send_to_front {:?} item {:#x} tick {}", + queue, item as usize, tick ); let ptr = unwrap!(QueuePtr::new(queue.cast()), "queue is null"); @@ -82,7 +101,10 @@ pub(crate) fn queue_send_to_front(queue: *mut c_void, item: *const c_void, tick: } pub(crate) fn queue_receive(queue: *mut c_void, item: *mut c_void, tick: u32) -> i32 { - trace!("queue_recv {:?} item {:?} tick {}", queue, item, tick); + trace!( + "queue_receive {:?} item {:#x} tick {}", + queue, item as usize, tick + ); let ptr = unwrap!(QueuePtr::new(queue.cast()), "queue is null"); @@ -102,7 +124,10 @@ pub(crate) fn queue_try_receive_from_isr( item: *mut c_void, higher_priority_task_waken: *mut bool, ) -> i32 { - trace!("queue_try_recv_from_isr {:?} item {:?}", queue, item); + trace!( + "queue_try_receive_from_isr {:?} item {:#x}", + queue, item as usize + ); let ptr = unwrap!(QueuePtr::new(queue.cast()), "queue is null"); @@ -112,7 +137,7 @@ pub(crate) fn queue_try_receive_from_isr( } pub(crate) fn queue_remove(queue: *mut c_void, item: *const c_void) { - trace!("queue_remove queue {:?} item {:x}", queue, item as usize); + trace!("queue_remove queue {:?} item {:#x}", queue, item as usize); let ptr = unwrap!(QueuePtr::new(queue.cast()), "queue is null"); @@ -122,7 +147,7 @@ pub(crate) fn queue_remove(queue: *mut c_void, item: *const c_void) { } pub(crate) fn queue_messages_waiting(queue: *mut c_void) -> u32 { - trace!("queue_msg_waiting {:?}", queue); + trace!("queue_messages_waiting {:?}", queue); let ptr = unwrap!(QueuePtr::new(queue.cast()), "queue is null"); diff --git a/esp-radio/src/compat/semaphore.rs b/esp-radio/src/compat/semaphore.rs index 948f33bd7e3..a16861de6aa 100644 --- a/esp-radio/src/compat/semaphore.rs +++ b/esp-radio/src/compat/semaphore.rs @@ -70,3 +70,9 @@ pub(crate) fn sem_give_from_isr(semphr: *mut c_void, _higher_prio_task_waken: *m handle.give() as i32 } + +pub(crate) fn sem_count(semphr: *mut c_void) -> u32 { + let ptr = unwrap!(SemaphorePtr::new(semphr.cast()), "semphr is null"); + let handle = unsafe { SemaphoreHandle::ref_from_ptr(&ptr) }; + handle.current_count() +} diff --git a/esp-radio/src/interrupt_dispatch.rs b/esp-radio/src/interrupt_dispatch.rs index f8eaf6214e6..e298aaaaffa 100644 --- a/esp-radio/src/interrupt_dispatch.rs +++ b/esp-radio/src/interrupt_dispatch.rs @@ -28,7 +28,10 @@ impl Handler { core::mem::transmute::<*const c_void, unsafe extern "C" fn(*mut c_void)>(f) }; let arg = self.arg.load(Ordering::Relaxed); - trace!("calling {:x} with {:x}", f as usize, arg as usize); + trace!( + "calling interrupt handler {:x} with {:x}", + f as usize, arg as usize + ); unsafe { func(arg) }; trace!("{:x} done", f as usize); } diff --git a/esp-radio/src/radio_clocks/clocks_ll/esp32c2.rs b/esp-radio/src/radio_clocks/clocks_ll/esp32c2.rs index aa0f76f6ae9..3f30beb90de 100644 --- a/esp-radio/src/radio_clocks/clocks_ll/esp32c2.rs +++ b/esp-radio/src/radio_clocks/clocks_ll/esp32c2.rs @@ -41,29 +41,7 @@ pub(crate) fn deinit_clocks() { } pub(crate) fn ble_rtc_clk_init() { - regs!(MODEM_CLKRST).modem_lp_timer_conf().modify(|_, w| { - w.lp_timer_sel_xtal32k().clear_bit(); - w.lp_timer_sel_xtal().set_bit(); - w.lp_timer_sel_8m().clear_bit(); - w.lp_timer_sel_rtc_slow().clear_bit() - }); - - let xtal_frequency = esp_hal::clock::xtal_clock(); - let bt_lpclk_target = match xtal_frequency.as_mhz() { - 26 => 40000, - // 40MHz - _ => 32000, - }; - let divider = xtal_frequency.as_hz() / (5 * bt_lpclk_target) - 1; - - regs!(MODEM_CLKRST) - .modem_lp_timer_conf() - .modify(|_, w| unsafe { w.lp_timer_clk_div_num().bits(divider as u8) }); - - regs!(MODEM_CLKRST).etm_clk_conf().modify(|_, w| { - w.etm_clk_active().set_bit(); - w.etm_clk_sel().clear_bit() - }); + // `BLE_LP_CLK` is programmed by the clock tree. } pub(crate) fn reset_rpa() { diff --git a/esp-radio/src/radio_clocks/clocks_ll/esp32s31.rs b/esp-radio/src/radio_clocks/clocks_ll/esp32s31.rs index 501eb4c6aeb..0d9759422ed 100644 --- a/esp-radio/src/radio_clocks/clocks_ll/esp32s31.rs +++ b/esp-radio/src/radio_clocks/clocks_ll/esp32s31.rs @@ -1,5 +1,3 @@ -use core::sync::atomic::{AtomicBool, Ordering}; - pub(crate) fn enable_wifi(en: bool) { regs!(MODEM_SYSCON) .clk_conf1() @@ -86,46 +84,8 @@ pub(crate) fn deinit_clocks() { // nothing to do, `init_clocks` is a no-op } -/// IDF `btdm_lp` default: `CONFIG_BT_CTRL_LP_CLK_SRC_MAIN_XTAL` at 100 kHz. -pub(crate) const BT_LPCLK_HZ: u64 = 100_000; - -/// Select the BLE RTC / modem LP timer clock. -/// -/// IDF `btdm_lp_timer_clk_init` + `modem_clock_select_lp_clock_source(PERIPH_BT_MODULE)`. -/// Without a source and `clk_lp_timer_en`, the controller accepts HCI scan -/// enable but never schedules radio windows. pub(crate) fn ble_rtc_clk_init() { - static INITED: AtomicBool = AtomicBool::new(false); - - if INITED.swap(true, Ordering::SeqCst) { - return; - } - let xtal_hz = esp_hal::clock::xtal_clock().as_hz(); - let divider = (xtal_hz / BT_LPCLK_HZ as u32).saturating_sub(1) as u16; - - regs!(MODEM_LPCON) - .test_conf() - .modify(|_, w| w.clk_en().set_bit()); - - regs!(MODEM_LPCON).lp_timer_conf().modify(|_, w| { - w.clk_lp_timer_sel_osc_slow().clear_bit(); - w.clk_lp_timer_sel_osc_fast().clear_bit(); - w.clk_lp_timer_sel_xtal32k().clear_bit(); - w.clk_lp_timer_sel_xtal().set_bit(); - unsafe { w.clk_lp_timer_div_num().bits(divider) } - }); - - // IDF `modem_lpcon_ll_reset_ble_rtc_timer`. - regs!(MODEM_LPCON) - .rst_conf() - .modify(|_, w| w.rst_lp_timer().set_bit()); - regs!(MODEM_LPCON) - .rst_conf() - .modify(|_, w| w.rst_lp_timer().clear_bit()); - - regs!(MODEM_LPCON) - .clk_conf() - .modify(|_, w| w.clk_lp_timer_en().set_bit()); + // `BLE_LP_CLK` is programmed by the clock tree. } pub(crate) fn reset_rpa() { diff --git a/esp-radio/src/time.rs b/esp-radio/src/time.rs index 78183fc6548..a9fa85ceb77 100644 --- a/esp-radio/src/time.rs +++ b/esp-radio/src/time.rs @@ -4,18 +4,22 @@ #![allow(unused)] +#[inline(always)] pub(crate) const fn blob_ticks_to_micros(ticks: u32) -> u32 { ticks.saturating_mul(1_000) } +#[inline(always)] pub(crate) const fn micros_to_blob_ticks(micros: u32) -> u32 { micros / 1_000 } +#[inline(always)] pub(crate) const fn blob_ticks_to_millis(ticks: u32) -> u32 { ticks } +#[inline(always)] pub(crate) const fn millis_to_blob_ticks(millis: u32) -> u32 { millis } diff --git a/esp-radio/src/wifi/os_adapter/esp32s31.rs b/esp-radio/src/wifi/os_adapter/esp32s31.rs index 9194532d5f9..bb3bc74c288 100644 --- a/esp-radio/src/wifi/os_adapter/esp32s31.rs +++ b/esp-radio/src/wifi/os_adapter/esp32s31.rs @@ -1,26 +1,43 @@ use crate::{ - hal::{interrupt::Priority, peripherals::WIFI}, + hal::{interrupt::Priority, peripherals::WIFI, ram}, interrupt_dispatch::Handler, sys::c_types::{c_int, c_void}, }; static ISR_INTERRUPT_1: Handler = Handler::new(); -pub(crate) fn chip_ints_on(_mask: u32) { - // CLIC: Enable Wi-Fi interrupt / mask is a no-op — already enabled. +pub(crate) fn chip_ints_on(mask: u32) { + // set_intr called with intr_num = 1 -> mask = 1 << 1 == 2 + trace!("chip_ints_on - mask {:#x}", mask); + if mask & 2 == 2 { + unsafe { + WIFI::steal().enable_mac_interrupt(Priority::Priority1); + WIFI::steal().enable_pwr_interrupt(Priority::Priority1); + } + } } -pub(crate) fn chip_ints_off(_mask: u32) { - // leaving the interrupt enabled seems to not cause issues +pub(crate) fn chip_ints_off(mask: u32) { + trace!("chip_ints_off - mask {:#x}", mask); + if mask & 2 == 2 { + unsafe { + WIFI::steal().disable_mac_interrupt_on_all_cores(); + WIFI::steal().disable_pwr_interrupt_on_all_cores(); + } + } } pub(crate) unsafe extern "C" fn set_intr( - _cpu_no: i32, - _intr_source: u32, - _intr_num: u32, - _intr_prio: i32, + cpu_no: i32, + intr_source: u32, + intr_num: u32, + intr_prio: i32, ) { - // Interrupts are configured in `set_isr`. + trace!( + "set_intr - core {} interrupt {} num {:?} prio {:?}", + cpu_no, intr_source, intr_num, intr_prio + ); + // These are expected to be direct-bound, but we don't do that for now. } pub(crate) unsafe extern "C" fn regdma_link_set_write_wait_content_dummy( @@ -39,24 +56,19 @@ pub unsafe extern "C" fn set_isr(n: i32, f: *mut c_void, arg: *mut c_void) { trace!("set_isr - interrupt {} function {:?} arg {:?}", n, f, arg); match n { - 0 | 1 => ISR_INTERRUPT_1.set(f, arg), + 1 => ISR_INTERRUPT_1.set(f, arg), _ => panic!("set_isr - unsupported interrupt number {}", n), } - - unsafe { - WIFI::steal().enable_mac_interrupt(Priority::Priority1); - WIFI::steal().enable_pwr_interrupt(Priority::Priority1); - } } #[unsafe(no_mangle)] -#[crate::hal::ram] +#[ram] extern "C" fn MODEM_WIFI_MAC() { ISR_INTERRUPT_1.dispatch(); } #[unsafe(no_mangle)] -#[crate::hal::ram] +#[ram] extern "C" fn MODEM_WIFI_PWR() { ISR_INTERRUPT_1.dispatch(); } diff --git a/esp-radio/src/wifi/os_adapter/mod.rs b/esp-radio/src/wifi/os_adapter/mod.rs index d07f670c1d6..a86b6fb96dd 100644 --- a/esp-radio/src/wifi/os_adapter/mod.rs +++ b/esp-radio/src/wifi/os_adapter/mod.rs @@ -847,9 +847,9 @@ pub unsafe extern "C" fn phy_enable() { #[allow(clippy::unnecessary_cast)] pub unsafe extern "C" fn phy_update_country_info(country: *const c_char) -> c_int { unsafe { - // not implemented in original code + // FIXME trace!("phy_update_country_info {}", str_from_c(country.cast())); - -1 + 0 } } diff --git a/esp-rtos/src/lib.rs b/esp-rtos/src/lib.rs index 6264c8c07e3..667b8d2fa7e 100644 --- a/esp-rtos/src/lib.rs +++ b/esp-rtos/src/lib.rs @@ -356,6 +356,7 @@ fn init_tracing() { } } +#[track_caller] fn assert_thread_mode(function: &str) { assert!( esp_hal::interrupt::RunLevel::current().is_thread(), diff --git a/esp-sync/src/raw.rs b/esp-sync/src/raw.rs index f6c6cae484c..7292577162a 100644 --- a/esp-sync/src/raw.rs +++ b/esp-sync/src/raw.rs @@ -35,7 +35,7 @@ impl RawLock for SingleCoreInterruptLock { #[inline] unsafe fn enter(&self) -> RestoreState { cfg_select! { - esp32p4 => { + any(esp32p4, esp32s31) => { // TODO: any with zcmp // ESP32-P4 (v3.2/ECO7 etc.) Zcmp hardware bug workaround (IDF-14279 / DIG-661): // Clearing mstatus.mie alone does not fully mask CLIC interrupts -- an @@ -96,7 +96,7 @@ impl RawLock for SingleCoreInterruptLock { let token = token.inner(); cfg_select! { - esp32p4 => { + any(esp32p4, esp32s31) => { if (token & 0b1000) != 0 { unsafe { riscv::interrupt::enable(); diff --git a/examples/ble/bas_peripheral/Cargo.toml b/examples/ble/bas_peripheral/Cargo.toml index 040716fa7bc..e7566b3b3fe 100644 --- a/examples/ble/bas_peripheral/Cargo.toml +++ b/examples/ble/bas_peripheral/Cargo.toml @@ -80,6 +80,14 @@ esp32c61 = [ "esp-rtos/esp32c61", "esp-radio/esp32c61", ] +esp32s31 = [ + "esp-alloc/esp32s31", + "esp-backtrace/esp32s31", + "esp-bootloader-esp-idf/esp32s31", + "esp-hal/esp32s31", + "esp-rtos/esp32s31", + "esp-radio/esp32s31", +] esp32h2 = [ "esp-alloc/esp32h2", "esp-backtrace/esp32h2", diff --git a/examples/ble/low-power-bas-peripheral/.cargo/config.toml b/examples/ble/low-power-bas-peripheral/.cargo/config.toml new file mode 100644 index 00000000000..44dacc4278b --- /dev/null +++ b/examples/ble/low-power-bas-peripheral/.cargo/config.toml @@ -0,0 +1,24 @@ +[target.'cfg(target_arch = "riscv32")'] +runner = "espflash flash --monitor" +rustflags = [ + "-C", "link-arg=-Tlinkall.x", + "-C", "force-frame-pointers", +] + +[target.'cfg(target_arch = "xtensa")'] +runner = "espflash flash --monitor" +rustflags = [ + # GNU LD + "-C", "link-arg=-Wl,-Tlinkall.x", + "-C", "link-arg=-nostartfiles", + + # LLD + # "-C", "link-arg=-Tlinkall.x", + # "-C", "linker=rust-lld", +] + +[env] +ESP_LOG = "info" + +[unstable] +build-std = ["core", "alloc"] diff --git a/examples/ble/low-power-bas-peripheral/Cargo.toml b/examples/ble/low-power-bas-peripheral/Cargo.toml new file mode 100644 index 00000000000..8f4113e76c2 --- /dev/null +++ b/examples/ble/low-power-bas-peripheral/Cargo.toml @@ -0,0 +1,116 @@ +[package] +name = "low-power-bas-peripheral" +version = "0.0.0" +edition = "2024" +publish = false + +[dependencies] +embassy-executor = "0.10.0" +embassy-futures = "0.1" +embassy-sync = "0.8" +embassy-time = "0.5.0" +esp-alloc = { path = "../../../esp-alloc" } +esp-backtrace = { path = "../../../esp-backtrace", features = [ + "panic-handler", + "println", +] } +esp-bootloader-esp-idf = { path = "../../../esp-bootloader-esp-idf" } +esp-hal = { path = "../../../esp-hal", features = ["log-04", "unstable"] } +esp-println = { path = "../../../esp-println", features = ["log-04"] } +esp-rtos = { path = "../../../esp-rtos", features = ["esp-radio", "embassy", "log-04"] } +esp-radio = { path = "../../../esp-radio", features = [ + "ble", + "log-04", + "unstable", +] } +log = "0.4.27" +trouble-host = { version = "0.7.0", features = [ + "default-packet-pool-mtu-255", + "derive", + "scan", +] } +static_cell = "2.1.1" + +[features] +esp32 = [ + "esp-alloc/esp32", + "esp-backtrace/esp32", + "esp-bootloader-esp-idf/esp32", + "esp-hal/esp32", + "esp-rtos/esp32", + "esp-radio/esp32", +] +esp32c2 = [ + "esp-alloc/esp32c2", + "esp-backtrace/esp32c2", + "esp-bootloader-esp-idf/esp32c2", + "esp-hal/esp32c2", + "esp-rtos/esp32c2", + "esp-radio/esp32c2", +] +esp32c3 = [ + "esp-alloc/esp32c3", + "esp-backtrace/esp32c3", + "esp-bootloader-esp-idf/esp32c3", + "esp-hal/esp32c3", + "esp-rtos/esp32c3", + "esp-radio/esp32c3", +] +esp32c5 = [ + "esp-alloc/esp32c5", + "esp-backtrace/esp32c5", + "esp-bootloader-esp-idf/esp32c5", + "esp-hal/esp32c5", + "esp-rtos/esp32c5", + "esp-radio/esp32c5", +] +esp32c6 = [ + "esp-alloc/esp32c6", + "esp-backtrace/esp32c6", + "esp-bootloader-esp-idf/esp32c6", + "esp-hal/esp32c6", + "esp-rtos/esp32c6", + "esp-radio/esp32c6", +] +esp32c61 = [ + "esp-alloc/esp32c61", + "esp-backtrace/esp32c61", + "esp-bootloader-esp-idf/esp32c61", + "esp-hal/esp32c61", + "esp-rtos/esp32c61", + "esp-radio/esp32c61", +] +esp32s31 = [ + "esp-alloc/esp32s31", + "esp-backtrace/esp32s31", + "esp-bootloader-esp-idf/esp32s31", + "esp-hal/esp32s31", + "esp-rtos/esp32s31", + "esp-radio/esp32s31", +] +esp32h2 = [ + "esp-alloc/esp32h2", + "esp-backtrace/esp32h2", + "esp-bootloader-esp-idf/esp32h2", + "esp-hal/esp32h2", + "esp-rtos/esp32h2", + "esp-radio/esp32h2", +] +esp32s3 = [ + "esp-alloc/esp32s3", + "esp-backtrace/esp32s3", + "esp-bootloader-esp-idf/esp32s3", + "esp-hal/esp32s3", + "esp-rtos/esp32s3", + "esp-radio/esp32s3", +] + +[profile.release] +debug = true +debug-assertions = true +lto = "fat" +codegen-units = 1 + +[package.metadata.cargo-machete] +# static_cell is used indirectly in #[gatt_service] macro. +ignored = ["embassy-sync", "static_cell"] diff --git a/examples/ble/low-power-bas-peripheral/src/main.rs b/examples/ble/low-power-bas-peripheral/src/main.rs new file mode 100644 index 00000000000..033a33ebe00 --- /dev/null +++ b/examples/ble/low-power-bas-peripheral/src/main.rs @@ -0,0 +1,236 @@ +//! Battery service peripheral with BLE modem sleep enabled. +//! +//! The controller uses the default divided-crystal `BLE_LP_CLK`. The radio +//! wake lock still prevents SoC light sleep. + +//% CHIP_FILTER: bt_driver_supported + +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_futures::{join::join, select::select}; +use embassy_time::Timer; +use esp_alloc as _; +use esp_backtrace as _; +use esp_hal::{clock::CpuClock, timer::timg::TimerGroup}; +use esp_radio::ble::controller::BleConnector; +use log::{info, warn}; +use trouble_host::prelude::*; + +esp_bootloader_esp_idf::esp_app_desc!(); + +#[esp_hal::main] +async fn main(_s: Spawner) { + esp_println::logger::init_logger_from_env(); + let peripherals = esp_hal::init(esp_hal::Config::default().with_cpu_clock(CpuClock::max())); + esp_alloc::heap_allocator!(size: 72 * 1024); + let timg0 = TimerGroup::new(peripherals.TIMG0); + esp_rtos::start(timg0.timer0); + + let bluetooth = peripherals.BT; + let connector = BleConnector::new( + bluetooth, + esp_radio::ble::Config::default().with_modem_sleep(true), + ) + .unwrap(); + let controller: ExternalController<_, 1> = ExternalController::new(connector); + + ble_bas_peripheral_run(controller).await; +} + +/// Max number of connections +const CONNECTIONS_MAX: usize = 1; +/// Max number of L2CAP channels. +const L2CAP_CHANNELS_MAX: usize = 2; // Signal + att + +// GATT Server definition +#[gatt_server] +struct Server { + battery_service: BatteryService, +} + +/// Battery service +#[gatt_service(uuid = service::BATTERY)] +struct BatteryService { + /// Battery Level + #[descriptor(uuid = descriptors::VALID_RANGE, read, value = [0, 100])] + #[descriptor(uuid = descriptors::MEASUREMENT_DESCRIPTION, name = "hello", read, value = "Battery Level", type = &'static str)] + #[characteristic(uuid = characteristic::BATTERY_LEVEL, read, notify, value = 10)] + level: u8, + #[characteristic(uuid = "408813df-5dd4-1f87-ec11-cdb001100000", write, read, notify)] + status: bool, +} + +/// Run the BLE stack. +pub async fn ble_bas_peripheral_run(controller: C) +where + C: Controller, +{ + // Using a fixed "random" address can be useful for testing. In real scenarios, one would + // use e.g. the MAC 6 byte array as the address (how to get that varies by the platform). + let address: Address = Address::random([0xff, 0x8f, 0x1a, 0x05, 0xe4, 0xff]); + info!("Our address = {:?}", address); + + let mut resources: HostResources<_, DefaultPacketPool, CONNECTIONS_MAX, L2CAP_CHANNELS_MAX> = + HostResources::new(); + let stack = trouble_host::new(controller, &mut resources) + .set_random_address(address) + .build(); + let runner = stack.runner(); + let mut peripheral = stack.peripheral(); + + info!("Starting advertising and GATT service"); + let server = Server::new_with_config(GapConfig::Peripheral(PeripheralConfig { + name: "TrouBLE", + appearance: &appearance::power_device::GENERIC_POWER_DEVICE, + })) + .unwrap(); + + let _ = join(ble_task(runner), async { + loop { + match advertise("Trouble Example", &mut peripheral, &server).await { + Ok(conn) => { + // set up tasks when the connection is established to a central, so they don't + // run when no one is connected. + let a = gatt_events_task(&server, &conn); + let b = custom_task(&server, &conn, &stack); + // run until any task ends (usually because the connection has been closed), + // then return to advertising state. + select(a, b).await; + } + Err(e) => { + panic!("[adv] error: {:?}", e); + } + } + } + }) + .await; +} + +/// This is a background task that is required to run forever alongside any other BLE tasks. +/// +/// ## Alternative +/// +/// If you didn't require this to be generic for your application, you could statically spawn this +/// with i.e. +/// +/// ```rust,ignore +/// +/// #[embassy_executor::task] +/// async fn ble_task(mut runner: Runner<'static, SoftdeviceController<'static>>) { +/// runner.run().await; +/// } +/// +/// spawner.spawn(ble_task(runner).unwrap()); +/// ``` +async fn ble_task(mut runner: Runner<'_, C, P>) { + loop { + if let Err(e) = runner.run().await { + panic!("[ble_task] error: {:?}", e); + } + } +} + +/// Stream Events until the connection closes. +/// +/// This function will handle the GATT events and process them. +/// This is how we interact with read and write requests. +async fn gatt_events_task( + server: &Server<'_>, + conn: &GattConnection<'_, '_, P>, +) -> Result<(), Error> { + let level = server.battery_service.level; + let reason = loop { + match conn.next().await { + GattConnectionEvent::Disconnected { reason } => break reason, + GattConnectionEvent::Gatt { event } => { + match &event { + GattEvent::Read(event) => { + if event.handle() == level.handle { + let value = server.get(&level); + info!("[gatt] Read Event to Level Characteristic: {:?}", value); + } + } + GattEvent::Write(event) => { + event.with_data(|offset, data| { + info!( + "[gatt] Write Event to Level Characteristic at {}: {:?}", + offset, data + ) + }); + } + _ => {} + }; + // This step is also performed at drop(), but writing it explicitly is necessary + // in order to ensure reply is sent. + match event.accept() { + Ok(reply) => reply.send().await, + Err(e) => warn!("[gatt] error sending response: {:?}", e), + }; + } + _ => {} // ignore other Gatt Connection Events + } + }; + info!("[gatt] disconnected: {:?}", reason); + Ok(()) +} + +/// Create an advertiser to use to connect to a BLE Central, and wait for it to connect. +async fn advertise<'values, 'server, C: Controller>( + name: &'values str, + peripheral: &mut Peripheral<'values, C, DefaultPacketPool>, + server: &'server Server<'values>, +) -> Result, BleHostError> { + let mut advertiser_data = [0; 31]; + let len = AdStructure::encode_slice( + &[ + AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED), + AdStructure::IncompleteServiceUuids16(&[[0x0f, 0x18]]), + AdStructure::CompleteLocalName(name.as_bytes()), + ], + &mut advertiser_data[..], + )?; + let advertiser = peripheral + .advertise( + &Default::default(), + Advertisement::ConnectableScannableUndirected { + adv_data: &advertiser_data[..len], + scan_data: &[], + }, + ) + .await?; + info!("[adv] advertising"); + let conn = advertiser.accept().await?.with_attribute_server(server)?; + info!("[adv] connection established"); + Ok(conn) +} + +/// Example task to use the BLE notifier interface. +/// This task will notify the connected central of a counter value every 2 seconds. +/// It will also read the RSSI value every 2 seconds. +/// and will stop when the connection is closed by the central or an error occurs. +async fn custom_task( + server: &Server<'_>, + conn: &GattConnection<'_, '_, P>, + stack: &Stack<'_, C, P>, +) { + let mut tick: u8 = 0; + let level = server.battery_service.level; + loop { + tick = tick.wrapping_add(1); + info!("[custom_task] notifying connection of tick {}", tick); + if level.notify(conn, &tick, true).await.is_err() { + info!("[custom_task] error notifying connection"); + break; + }; + // read RSSI (Received Signal Strength Indicator) of the connection. + if let Ok(rssi) = conn.raw().rssi(stack).await { + info!("[custom_task] RSSI: {:?}", rssi); + } else { + info!("[custom_task] error getting RSSI"); + break; + }; + Timer::after_secs(2).await; + } +} diff --git a/examples/ble/scanner/Cargo.toml b/examples/ble/scanner/Cargo.toml index 94e36a619d0..ee699c8c2e2 100644 --- a/examples/ble/scanner/Cargo.toml +++ b/examples/ble/scanner/Cargo.toml @@ -80,6 +80,14 @@ esp32c61 = [ "esp-rtos/esp32c61", "esp-radio/esp32c61", ] +esp32s31 = [ + "esp-alloc/esp32s31", + "esp-backtrace/esp32s31", + "esp-bootloader-esp-idf/esp32s31", + "esp-hal/esp32s31", + "esp-rtos/esp32s31", + "esp-radio/esp32s31", +] esp32h2 = [ "esp-alloc/esp32h2", "esp-backtrace/esp32h2", diff --git a/examples/wifi/embassy_coex/Cargo.toml b/examples/wifi/embassy_coex/Cargo.toml index 1553264accc..db3df5e6f49 100644 --- a/examples/wifi/embassy_coex/Cargo.toml +++ b/examples/wifi/embassy_coex/Cargo.toml @@ -88,6 +88,14 @@ esp32c61 = [ "esp-rtos/esp32c61", "esp-radio/esp32c61", ] +esp32s31 = [ + "esp-alloc/esp32s31", + "esp-backtrace/esp32s31", + "esp-bootloader-esp-idf/esp32s31", + "esp-hal/esp32s31", + "esp-rtos/esp32s31", + "esp-radio/esp32s31", +] esp32s3 = [ "esp-alloc/esp32s3", "esp-backtrace/esp32s3", diff --git a/hil-test/src/bin/radio_basic.rs b/hil-test/src/bin/radio_basic.rs index 1972f47a866..d25c52e4470 100644 --- a/hil-test/src/bin/radio_basic.rs +++ b/hil-test/src/bin/radio_basic.rs @@ -1,8 +1,8 @@ //% CHIP_FILTER(no_wifi): bt_driver_supported && !wifi_driver_supported //% CHIP_FILTER(no_ble): wifi_driver_supported && !bt_driver_supported //% CHIP_FILTER(no_radio): !wifi_driver_supported && !bt_driver_supported -//% CHIP_FILTER(has_wifi_ble): wifi_driver_supported && bt_driver_supported //% CHIP_FILTER(stable_wifi): wifi_driver_supported +//% CHIP_FILTER(has_wifi_ble): wifi_driver_supported && bt_driver_supported //% FEATURES: unstable esp-alloc embassy //% FEATURES(no_radio): rtos-radio-driver @@ -58,8 +58,7 @@ mod ble_controller; #[cfg(feature = "esp-radio")] mod wifi_controller; -// TODO why does such a basic thing fail on S31? -#[cfg(all(soc_has_wifi, not(esp32s31)))] +#[cfg(soc_has_wifi)] #[path = "radio_basic/esp_now_config.rs"] #[cfg(feature = "esp-radio-unstable")] mod esp_now_config;