From f4fc6f87111a71035b8089d1c8636ed256c2258e Mon Sep 17 00:00:00 2001 From: tzyt Date: Tue, 26 May 2026 21:03:47 -0500 Subject: [PATCH 01/26] initial commit for SDM driver --- esp-hal/README.md | 3 +- esp-hal/src/lib.rs | 2 + .../src/_build_script_utils.rs | 67 +++++++++++++++ .../src/_generated_esp32.rs | 34 ++++++++ .../src/_generated_esp32c3.rs | 35 ++++++++ .../src/_generated_esp32c5.rs | 35 ++++++++ .../src/_generated_esp32c6.rs | 35 ++++++++ .../src/_generated_esp32h2.rs | 35 ++++++++ .../src/_generated_esp32p4.rs | 83 ++++++++++++++----- .../src/_generated_esp32s2.rs | 39 +++++++++ .../src/_generated_esp32s3.rs | 39 +++++++++ esp-metadata/devices/esp32.toml | 6 +- esp-metadata/devices/esp32c3.toml | 6 +- esp-metadata/devices/esp32c5.toml | 6 +- esp-metadata/devices/esp32c6.toml | 6 +- esp-metadata/devices/esp32h2.toml | 6 +- esp-metadata/devices/esp32p4.toml | 7 +- esp-metadata/devices/esp32s2.toml | 6 +- esp-metadata/devices/esp32s3.toml | 6 +- esp-metadata/src/cfg.rs | 12 ++- 20 files changed, 437 insertions(+), 31 deletions(-) diff --git a/esp-hal/README.md b/esp-hal/README.md index ee66f4763bd..6eec34ae65a 100644 --- a/esp-hal/README.md +++ b/esp-hal/README.md @@ -101,7 +101,7 @@ For help getting started with this HAL, please refer to [The Rust on ESP Book] a | SDIO host | ❌ | | | | | | | ❌ | | ❌ | | SDIO slave | ❌ | | | [❌][5169] [^1] | ❌ | [❌][5417] [^1] | | ❌ | | | | SHA | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ❌ | ⚒️ | ⚒️ | -| SDM | [❌][2370] [^1] | | [❌][2370] [^1] | [❌][2370] [^1] | [❌][2370] [^1] | | [❌][2370] [^1] | [❌][2370] [^1] | [❌][2370] [^1] | [❌][2370] [^1] | +| SDM | ✔️ | | ✔️ | ✔️ | ✔️ | | ✔️ | ✔️ | ✔️ | ✔️ | | Light/deep sleep | ⚒️ | ⚒️ | ⚒️ | [❌][5165] [^1] | ⚒️ | [❌][5424] [^1] | ⚒️ | ❌ | ⚒️ | ⚒️ | | SPI master | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ⚒️ | ✔️ | ✔️ | | SPI slave | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ❌ | ⚒️ | ⚒️ | @@ -128,7 +128,6 @@ For help getting started with this HAL, please refer to [The Rust on ESP Book] a [884]: https://github.com/esp-rs/esp-hal/issues/884 [1905]: https://github.com/esp-rs/esp-hal/issues/1905 [1909]: https://github.com/esp-rs/esp-hal/issues/1909 -[2370]: https://github.com/esp-rs/esp-hal/issues/2370 [5153]: https://github.com/esp-rs/esp-hal/issues/5153 [5154]: https://github.com/esp-rs/esp-hal/issues/5154 [5155]: https://github.com/esp-rs/esp-hal/issues/5155 diff --git a/esp-hal/src/lib.rs b/esp-hal/src/lib.rs index 56fb7b7d939..79867386157 100644 --- a/esp-hal/src/lib.rs +++ b/esp-hal/src/lib.rs @@ -389,6 +389,8 @@ unstable_driver! { pub mod rsa; #[cfg(sha_driver_supported)] pub mod sha; + #[cfg(sdm_driver_supported)] + pub mod sdm; #[cfg(touch)] pub mod touch; #[cfg(soc_has_trace0)] diff --git a/esp-metadata-generated/src/_build_script_utils.rs b/esp-metadata-generated/src/_build_script_utils.rs index 061a8d76342..4b20df73400 100644 --- a/esp-metadata-generated/src/_build_script_utils.rs +++ b/esp-metadata-generated/src/_build_script_utils.rs @@ -294,6 +294,7 @@ impl Chip { "rsa_driver_supported", "lp_timer_driver_supported", "sha_driver_supported", + "sdm_driver_supported", "sleep_driver_supported", "soc_driver_supported", "spi_master_driver_supported", @@ -357,6 +358,8 @@ impl Chip { "rsa_version=\"1\"", "rsa_size_increment=\"512\"", "rsa_memory_size_bytes=\"512\"", + "sdm_clock_sources_apb", + "sdm_default_clock_source=\"apb\"", "sleep_light_sleep", "sleep_deep_sleep", "soc_multi_core_enabled", @@ -505,6 +508,7 @@ impl Chip { "cargo:rustc-cfg=rsa_driver_supported", "cargo:rustc-cfg=lp_timer_driver_supported", "cargo:rustc-cfg=sha_driver_supported", + "cargo:rustc-cfg=sdm_driver_supported", "cargo:rustc-cfg=sleep_driver_supported", "cargo:rustc-cfg=soc_driver_supported", "cargo:rustc-cfg=spi_master_driver_supported", @@ -568,6 +572,8 @@ impl Chip { "cargo:rustc-cfg=rsa_version=\"1\"", "cargo:rustc-cfg=rsa_size_increment=\"512\"", "cargo:rustc-cfg=rsa_memory_size_bytes=\"512\"", + "cargo:rustc-cfg=sdm_clock_sources_apb", + "cargo:rustc-cfg=sdm_default_clock_source=\"apb\"", "cargo:rustc-cfg=sleep_light_sleep", "cargo:rustc-cfg=sleep_deep_sleep", "cargo:rustc-cfg=soc_multi_core_enabled", @@ -1297,6 +1303,7 @@ impl Chip { "rsa_driver_supported", "lp_timer_driver_supported", "sha_driver_supported", + "sdm_driver_supported", "sleep_driver_supported", "soc_driver_supported", "spi_master_driver_supported", @@ -1379,6 +1386,8 @@ impl Chip { "rsa_size_increment=\"32\"", "rsa_memory_size_bytes=\"384\"", "sha_dma", + "sdm_clock_sources_apb", + "sdm_default_clock_source=\"apb\"", "sleep_light_sleep", "sleep_deep_sleep", "soc_cpu_has_csr_pc", @@ -1512,6 +1521,7 @@ impl Chip { "cargo:rustc-cfg=rsa_driver_supported", "cargo:rustc-cfg=lp_timer_driver_supported", "cargo:rustc-cfg=sha_driver_supported", + "cargo:rustc-cfg=sdm_driver_supported", "cargo:rustc-cfg=sleep_driver_supported", "cargo:rustc-cfg=soc_driver_supported", "cargo:rustc-cfg=spi_master_driver_supported", @@ -1594,6 +1604,8 @@ impl Chip { "cargo:rustc-cfg=rsa_size_increment=\"32\"", "cargo:rustc-cfg=rsa_memory_size_bytes=\"384\"", "cargo:rustc-cfg=sha_dma", + "cargo:rustc-cfg=sdm_clock_sources_apb", + "cargo:rustc-cfg=sdm_default_clock_source=\"apb\"", "cargo:rustc-cfg=sleep_light_sleep", "cargo:rustc-cfg=sleep_deep_sleep", "cargo:rustc-cfg=soc_cpu_has_csr_pc", @@ -1868,6 +1880,7 @@ impl Chip { "rsa_driver_supported", "lp_timer_driver_supported", "sha_driver_supported", + "sdm_driver_supported", "soc_driver_supported", "spi_master_driver_supported", "spi_slave_driver_supported", @@ -1961,6 +1974,9 @@ impl Chip { "rsa_size_increment=\"32\"", "rsa_memory_size_bytes=\"384\"", "sha_dma", + "sdm_clock_sources_xtal", + "sdm_clock_sources_pll_f80m", + "sdm_default_clock_source=\"pll_f80m\"", "soc_cpu_has_branch_predictor", "soc_cpu_csr_prv_mode=\"2064\"", "soc_cpu_csr_prv_mode_is_set", @@ -2131,6 +2147,7 @@ impl Chip { "cargo:rustc-cfg=rsa_driver_supported", "cargo:rustc-cfg=lp_timer_driver_supported", "cargo:rustc-cfg=sha_driver_supported", + "cargo:rustc-cfg=sdm_driver_supported", "cargo:rustc-cfg=soc_driver_supported", "cargo:rustc-cfg=spi_master_driver_supported", "cargo:rustc-cfg=spi_slave_driver_supported", @@ -2224,6 +2241,9 @@ impl Chip { "cargo:rustc-cfg=rsa_size_increment=\"32\"", "cargo:rustc-cfg=rsa_memory_size_bytes=\"384\"", "cargo:rustc-cfg=sha_dma", + "cargo:rustc-cfg=sdm_clock_sources_xtal", + "cargo:rustc-cfg=sdm_clock_sources_pll_f80m", + "cargo:rustc-cfg=sdm_default_clock_source=\"pll_f80m\"", "cargo:rustc-cfg=soc_cpu_has_branch_predictor", "cargo:rustc-cfg=soc_cpu_csr_prv_mode=\"2064\"", "cargo:rustc-cfg=soc_cpu_csr_prv_mode_is_set", @@ -2550,6 +2570,7 @@ impl Chip { "rsa_driver_supported", "lp_timer_driver_supported", "sha_driver_supported", + "sdm_driver_supported", "sleep_driver_supported", "soc_driver_supported", "spi_master_driver_supported", @@ -2642,6 +2663,9 @@ impl Chip { "rsa_size_increment=\"32\"", "rsa_memory_size_bytes=\"384\"", "sha_dma", + "sdm_clock_sources_xtal", + "sdm_clock_sources_pll_f80m", + "sdm_default_clock_source=\"pll_f80m\"", "sleep_light_sleep", "sleep_deep_sleep", "soc_cpu_has_csr_pc", @@ -2835,6 +2859,7 @@ impl Chip { "cargo:rustc-cfg=rsa_driver_supported", "cargo:rustc-cfg=lp_timer_driver_supported", "cargo:rustc-cfg=sha_driver_supported", + "cargo:rustc-cfg=sdm_driver_supported", "cargo:rustc-cfg=sleep_driver_supported", "cargo:rustc-cfg=soc_driver_supported", "cargo:rustc-cfg=spi_master_driver_supported", @@ -2927,6 +2952,9 @@ impl Chip { "cargo:rustc-cfg=rsa_size_increment=\"32\"", "cargo:rustc-cfg=rsa_memory_size_bytes=\"384\"", "cargo:rustc-cfg=sha_dma", + "cargo:rustc-cfg=sdm_clock_sources_xtal", + "cargo:rustc-cfg=sdm_clock_sources_pll_f80m", + "cargo:rustc-cfg=sdm_default_clock_source=\"pll_f80m\"", "cargo:rustc-cfg=sleep_light_sleep", "cargo:rustc-cfg=sleep_deep_sleep", "cargo:rustc-cfg=soc_cpu_has_csr_pc", @@ -3799,6 +3827,7 @@ impl Chip { "rsa_driver_supported", "lp_timer_driver_supported", "sha_driver_supported", + "sdm_driver_supported", "sleep_driver_supported", "soc_driver_supported", "spi_master_driver_supported", @@ -3891,6 +3920,9 @@ impl Chip { "rsa_size_increment=\"32\"", "rsa_memory_size_bytes=\"384\"", "sha_dma", + "sdm_clock_sources_xtal", + "sdm_clock_sources_pll_f48m", + "sdm_default_clock_source=\"pll_f48m\"", "sleep_light_sleep", "sleep_deep_sleep", "soc_cpu_has_csr_pc", @@ -4051,6 +4083,7 @@ impl Chip { "cargo:rustc-cfg=rsa_driver_supported", "cargo:rustc-cfg=lp_timer_driver_supported", "cargo:rustc-cfg=sha_driver_supported", + "cargo:rustc-cfg=sdm_driver_supported", "cargo:rustc-cfg=sleep_driver_supported", "cargo:rustc-cfg=soc_driver_supported", "cargo:rustc-cfg=spi_master_driver_supported", @@ -4143,6 +4176,9 @@ impl Chip { "cargo:rustc-cfg=rsa_size_increment=\"32\"", "cargo:rustc-cfg=rsa_memory_size_bytes=\"384\"", "cargo:rustc-cfg=sha_dma", + "cargo:rustc-cfg=sdm_clock_sources_xtal", + "cargo:rustc-cfg=sdm_clock_sources_pll_f48m", + "cargo:rustc-cfg=sdm_default_clock_source=\"pll_f48m\"", "cargo:rustc-cfg=sleep_light_sleep", "cargo:rustc-cfg=sleep_deep_sleep", "cargo:rustc-cfg=soc_cpu_has_csr_pc", @@ -4304,6 +4340,7 @@ impl Chip { "multi_core", "soc_has_efuse", "soc_has_gpio", + "soc_has_gpio_sd", "soc_has_system", "soc_has_hp_sys", "soc_has_hp_sys_clkrst", @@ -4372,6 +4409,7 @@ impl Chip { "interrupts_driver_supported", "psram_driver_supported", "rng_driver_supported", + "sdm_driver_supported", "soc_driver_supported", "spi_master_driver_supported", "systimer_driver_supported", @@ -4432,6 +4470,9 @@ impl Chip { "rsa_size_increment=\"32\"", "rsa_memory_size_bytes=\"384\"", "sha_dma", + "sdm_clock_sources_xtal", + "sdm_clock_sources_pll_f80m", + "sdm_default_clock_source=\"pll_f80m\"", "soc_cpu_has_branch_predictor", "soc_multi_core_enabled", "soc_rc_fast_clk_default=\"20000000\"", @@ -4481,6 +4522,7 @@ impl Chip { "cargo:rustc-cfg=multi_core", "cargo:rustc-cfg=soc_has_efuse", "cargo:rustc-cfg=soc_has_gpio", + "cargo:rustc-cfg=soc_has_gpio_sd", "cargo:rustc-cfg=soc_has_system", "cargo:rustc-cfg=soc_has_hp_sys", "cargo:rustc-cfg=soc_has_hp_sys_clkrst", @@ -4549,6 +4591,7 @@ impl Chip { "cargo:rustc-cfg=interrupts_driver_supported", "cargo:rustc-cfg=psram_driver_supported", "cargo:rustc-cfg=rng_driver_supported", + "cargo:rustc-cfg=sdm_driver_supported", "cargo:rustc-cfg=soc_driver_supported", "cargo:rustc-cfg=spi_master_driver_supported", "cargo:rustc-cfg=systimer_driver_supported", @@ -4609,6 +4652,9 @@ impl Chip { "cargo:rustc-cfg=rsa_size_increment=\"32\"", "cargo:rustc-cfg=rsa_memory_size_bytes=\"384\"", "cargo:rustc-cfg=sha_dma", + "cargo:rustc-cfg=sdm_clock_sources_xtal", + "cargo:rustc-cfg=sdm_clock_sources_pll_f80m", + "cargo:rustc-cfg=sdm_default_clock_source=\"pll_f80m\"", "cargo:rustc-cfg=soc_cpu_has_branch_predictor", "cargo:rustc-cfg=soc_multi_core_enabled", "cargo:rustc-cfg=soc_rc_fast_clk_default=\"20000000\"", @@ -4989,6 +5035,7 @@ impl Chip { "rsa_driver_supported", "lp_timer_driver_supported", "sha_driver_supported", + "sdm_driver_supported", "sleep_driver_supported", "soc_driver_supported", "spi_master_driver_supported", @@ -5071,6 +5118,8 @@ impl Chip { "rsa_size_increment=\"32\"", "rsa_memory_size_bytes=\"512\"", "sha_dma", + "sdm_clock_sources_apb", + "sdm_default_clock_source=\"apb\"", "sleep_light_sleep", "sleep_deep_sleep", "soc_rc_fast_clk_default=\"8500000\"", @@ -5215,6 +5264,7 @@ impl Chip { "cargo:rustc-cfg=rsa_driver_supported", "cargo:rustc-cfg=lp_timer_driver_supported", "cargo:rustc-cfg=sha_driver_supported", + "cargo:rustc-cfg=sdm_driver_supported", "cargo:rustc-cfg=sleep_driver_supported", "cargo:rustc-cfg=soc_driver_supported", "cargo:rustc-cfg=spi_master_driver_supported", @@ -5297,6 +5347,8 @@ impl Chip { "cargo:rustc-cfg=rsa_size_increment=\"32\"", "cargo:rustc-cfg=rsa_memory_size_bytes=\"512\"", "cargo:rustc-cfg=sha_dma", + "cargo:rustc-cfg=sdm_clock_sources_apb", + "cargo:rustc-cfg=sdm_default_clock_source=\"apb\"", "cargo:rustc-cfg=sleep_light_sleep", "cargo:rustc-cfg=sleep_deep_sleep", "cargo:rustc-cfg=soc_rc_fast_clk_default=\"8500000\"", @@ -5651,6 +5703,7 @@ impl Chip { "rsa_driver_supported", "lp_timer_driver_supported", "sha_driver_supported", + "sdm_driver_supported", "sleep_driver_supported", "soc_driver_supported", "spi_master_driver_supported", @@ -5747,6 +5800,8 @@ impl Chip { "rsa_size_increment=\"32\"", "rsa_memory_size_bytes=\"512\"", "sha_dma", + "sdm_clock_sources_apb", + "sdm_default_clock_source=\"apb\"", "sleep_light_sleep", "sleep_deep_sleep", "soc_multi_core_enabled", @@ -5910,6 +5965,7 @@ impl Chip { "cargo:rustc-cfg=rsa_driver_supported", "cargo:rustc-cfg=lp_timer_driver_supported", "cargo:rustc-cfg=sha_driver_supported", + "cargo:rustc-cfg=sdm_driver_supported", "cargo:rustc-cfg=sleep_driver_supported", "cargo:rustc-cfg=soc_driver_supported", "cargo:rustc-cfg=spi_master_driver_supported", @@ -6006,6 +6062,8 @@ impl Chip { "cargo:rustc-cfg=rsa_size_increment=\"32\"", "cargo:rustc-cfg=rsa_memory_size_bytes=\"512\"", "cargo:rustc-cfg=sha_dma", + "cargo:rustc-cfg=sdm_clock_sources_apb", + "cargo:rustc-cfg=sdm_default_clock_source=\"apb\"", "cargo:rustc-cfg=sleep_light_sleep", "cargo:rustc-cfg=sleep_deep_sleep", "cargo:rustc-cfg=soc_multi_core_enabled", @@ -6413,6 +6471,7 @@ pub fn emit_check_cfg_directives() { println!("cargo:rustc-check-cfg=cfg(rsa_driver_supported)"); println!("cargo:rustc-check-cfg=cfg(lp_timer_driver_supported)"); println!("cargo:rustc-check-cfg=cfg(sha_driver_supported)"); + println!("cargo:rustc-check-cfg=cfg(sdm_driver_supported)"); println!("cargo:rustc-check-cfg=cfg(sleep_driver_supported)"); println!("cargo:rustc-check-cfg=cfg(soc_driver_supported)"); println!("cargo:rustc-check-cfg=cfg(spi_master_driver_supported)"); @@ -6450,6 +6509,7 @@ pub fn emit_check_cfg_directives() { println!("cargo:rustc-check-cfg=cfg(phy_combo_module)"); println!("cargo:rustc-check-cfg=cfg(rmt_has_per_channel_clock)"); println!("cargo:rustc-check-cfg=cfg(rng_trng_supported)"); + println!("cargo:rustc-check-cfg=cfg(sdm_clock_sources_apb)"); println!("cargo:rustc-check-cfg=cfg(sleep_light_sleep)"); println!("cargo:rustc-check-cfg=cfg(sleep_deep_sleep)"); println!("cargo:rustc-check-cfg=cfg(soc_multi_core_enabled)"); @@ -6640,6 +6700,8 @@ pub fn emit_check_cfg_directives() { println!("cargo:rustc-check-cfg=cfg(i2c_master_has_reliable_fsm_reset)"); println!("cargo:rustc-check-cfg=cfg(i2s_clock_configured_by_pcr)"); println!("cargo:rustc-check-cfg=cfg(rmt_has_tx_loop_auto_stop)"); + println!("cargo:rustc-check-cfg=cfg(sdm_clock_sources_xtal)"); + println!("cargo:rustc-check-cfg=cfg(sdm_clock_sources_pll_f80m)"); println!("cargo:rustc-check-cfg=cfg(soc_cpu_has_branch_predictor)"); println!("cargo:rustc-check-cfg=cfg(soc_cpu_csr_prv_mode_is_set)"); println!("cargo:rustc-check-cfg=cfg(soc_has_clock_node_pll_f12m)"); @@ -6693,6 +6755,7 @@ pub fn emit_check_cfg_directives() { println!("cargo:rustc-check-cfg=cfg(soc_has_mem2mem10)"); println!("cargo:rustc-check-cfg=cfg(soc_has_mem2mem11)"); println!("cargo:rustc-check-cfg=cfg(esp32h2)"); + println!("cargo:rustc-check-cfg=cfg(sdm_clock_sources_pll_f48m)"); println!("cargo:rustc-check-cfg=cfg(soc_has_clock_node_pll_f96m_clk)"); println!("cargo:rustc-check-cfg=cfg(soc_has_clock_node_pll_f64m_clk)"); println!("cargo:rustc-check-cfg=cfg(soc_has_clock_node_pll_f48m_clk)"); @@ -6805,6 +6868,10 @@ pub fn emit_check_cfg_directives() { println!("cargo:rustc-check-cfg=cfg(rsa_version, values(\"1\",\"3\",\"2\"))"); println!("cargo:rustc-check-cfg=cfg(rsa_size_increment, values(\"512\",\"32\"))"); println!("cargo:rustc-check-cfg=cfg(rsa_memory_size_bytes, values(\"512\",\"384\"))"); + println!( + "cargo:rustc-check-cfg=cfg(sdm_default_clock_source, \ + values(\"apb\",\"pll_f80m\",\"pll_f48m\"))" + ); println!( "cargo:rustc-check-cfg=cfg(soc_rc_fast_clk_default, \ values(\"8500000\",\"17500000\",\"20000000\"))" diff --git a/esp-metadata-generated/src/_generated_esp32.rs b/esp-metadata-generated/src/_generated_esp32.rs index 821b93f6c20..3e94fa0ffe6 100644 --- a/esp-metadata-generated/src/_generated_esp32.rs +++ b/esp-metadata-generated/src/_generated_esp32.rs @@ -301,6 +301,15 @@ macro_rules! property { ("sha.dma") => { false }; + ("sdm.channel_count") => { + 8 + }; + ("sdm.channel_count", str) => { + stringify!(8) + }; + ("sdm.default_clock_source") => { + "apb" + }; ("sleep.light_sleep") => { true }; @@ -552,6 +561,22 @@ macro_rules! for_each_sha_algorithm { }; } #[macro_export] +#[cfg_attr(docsrs, doc(cfg(feature = "_device-selected")))] +macro_rules! for_each_sdm_channel { + ($($pattern:tt => $code:tt;)*) => { + macro_rules! _for_each_inner_sdm_channel { $(($pattern) => $code;)* ($other : tt) + => {} } _for_each_inner_sdm_channel!((0, GPIO_SD0)); + _for_each_inner_sdm_channel!((1, GPIO_SD1)); _for_each_inner_sdm_channel!((2, + GPIO_SD2)); _for_each_inner_sdm_channel!((3, GPIO_SD3)); + _for_each_inner_sdm_channel!((4, GPIO_SD4)); _for_each_inner_sdm_channel!((5, + GPIO_SD5)); _for_each_inner_sdm_channel!((6, GPIO_SD6)); + _for_each_inner_sdm_channel!((7, GPIO_SD7)); + _for_each_inner_sdm_channel!((channels(0, GPIO_SD0), (1, GPIO_SD1), (2, + GPIO_SD2), (3, GPIO_SD3), (4, GPIO_SD4), (5, GPIO_SD5), (6, GPIO_SD6), (7, + GPIO_SD7))); + }; +} +#[macro_export] /// ESP-HAL must provide implementation for the following functions: /// ```rust, no_run /// // XTAL_CLK @@ -3085,6 +3110,8 @@ macro_rules! implement_peripheral_clocks { Aes, /// EMAC peripheral clock signal Emac, + /// GPIO_SD peripheral clock signal + GpioSd, /// I2C_EXT0 peripheral clock signal I2cExt0, /// I2C_EXT1 peripheral clock signal @@ -3138,6 +3165,7 @@ macro_rules! implement_peripheral_clocks { const ALL: &[Self] = &[ Self::Aes, Self::Emac, + Self::GpioSd, Self::I2cExt0, Self::I2cExt1, Self::I2s0, @@ -3175,6 +3203,9 @@ macro_rules! implement_peripheral_clocks { .wifi_clk_en() .modify(|_, w| w.emac_clk_en().bit(enable)); } + Peripheral::GpioSd => { + let _ = enable; + } Peripheral::I2cExt0 => { crate::peripherals::SYSTEM::regs() .perip_clk_en() @@ -3304,6 +3335,9 @@ macro_rules! implement_peripheral_clocks { .wifi_rst_en() .modify(|_, w| w.emac_rst().bit(reset)); } + Peripheral::GpioSd => { + let _ = reset; + } Peripheral::I2cExt0 => { crate::peripherals::SYSTEM::regs() .perip_rst_en() diff --git a/esp-metadata-generated/src/_generated_esp32c3.rs b/esp-metadata-generated/src/_generated_esp32c3.rs index 7a0abffaad8..8ec1bc2ac10 100644 --- a/esp-metadata-generated/src/_generated_esp32c3.rs +++ b/esp-metadata-generated/src/_generated_esp32c3.rs @@ -319,6 +319,15 @@ macro_rules! property { ("sha.dma") => { true }; + ("sdm.channel_count") => { + 4 + }; + ("sdm.channel_count", str) => { + stringify!(4) + }; + ("sdm.default_clock_source") => { + "apb" + }; ("sleep.light_sleep") => { true }; @@ -846,6 +855,18 @@ macro_rules! for_each_sha_algorithm { }; } #[macro_export] +#[cfg_attr(docsrs, doc(cfg(feature = "_device-selected")))] +macro_rules! for_each_sdm_channel { + ($($pattern:tt => $code:tt;)*) => { + macro_rules! _for_each_inner_sdm_channel { $(($pattern) => $code;)* ($other : tt) + => {} } _for_each_inner_sdm_channel!((0, GPIO_SD0)); + _for_each_inner_sdm_channel!((1, GPIO_SD1)); _for_each_inner_sdm_channel!((2, + GPIO_SD2)); _for_each_inner_sdm_channel!((3, GPIO_SD3)); + _for_each_inner_sdm_channel!((channels(0, GPIO_SD0), (1, GPIO_SD1), (2, + GPIO_SD2), (3, GPIO_SD3))); + }; +} +#[macro_export] /// ESP-HAL must provide implementation for the following functions: /// ```rust, no_run /// // XTAL_CLK @@ -3077,6 +3098,8 @@ macro_rules! implement_peripheral_clocks { Dma, /// DS peripheral clock signal Ds, + /// GPIO_SD peripheral clock signal + GpioSd, /// HMAC peripheral clock signal Hmac, /// I2C_EXT0 peripheral clock signal @@ -3128,6 +3151,7 @@ macro_rules! implement_peripheral_clocks { Self::ApbSarAdc, Self::Dma, Self::Ds, + Self::GpioSd, Self::Hmac, Self::I2cExt0, Self::I2s0, @@ -3170,6 +3194,14 @@ macro_rules! implement_peripheral_clocks { .perip_clk_en1() .modify(|_, w| w.crypto_ds_clk_en().bit(enable)); } + Peripheral::GpioSd => { + crate::peripherals::GPIO_SD::regs() + .clock_gate() + .modify(|_, w| w.clk_en().bit(enable)); + crate::peripherals::GPIO_SD::regs() + .sigmadelta_misc() + .modify(|_, w| w.function_clk_en().bit(enable)); + } Peripheral::Hmac => { crate::peripherals::SYSTEM::regs() .perip_clk_en1() @@ -3284,6 +3316,9 @@ macro_rules! implement_peripheral_clocks { .perip_rst_en1() .modify(|_, w| w.crypto_ds_rst().bit(reset)); } + Peripheral::GpioSd => { + let _ = reset; + } Peripheral::Hmac => { crate::peripherals::SYSTEM::regs() .perip_rst_en1() diff --git a/esp-metadata-generated/src/_generated_esp32c5.rs b/esp-metadata-generated/src/_generated_esp32c5.rs index dd96b298df0..4ddf4075894 100644 --- a/esp-metadata-generated/src/_generated_esp32c5.rs +++ b/esp-metadata-generated/src/_generated_esp32c5.rs @@ -355,6 +355,15 @@ macro_rules! property { ("sha.dma") => { true }; + ("sdm.channel_count") => { + 4 + }; + ("sdm.channel_count", str) => { + stringify!(4) + }; + ("sdm.default_clock_source") => { + "pll_f80m" + }; ("sleep.light_sleep") => { false }; @@ -981,6 +990,18 @@ macro_rules! for_each_sha_algorithm { }; } #[macro_export] +#[cfg_attr(docsrs, doc(cfg(feature = "_device-selected")))] +macro_rules! for_each_sdm_channel { + ($($pattern:tt => $code:tt;)*) => { + macro_rules! _for_each_inner_sdm_channel { $(($pattern) => $code;)* ($other : tt) + => {} } _for_each_inner_sdm_channel!((0, GPIO_SD0)); + _for_each_inner_sdm_channel!((1, GPIO_SD1)); _for_each_inner_sdm_channel!((2, + GPIO_SD2)); _for_each_inner_sdm_channel!((3, GPIO_SD3)); + _for_each_inner_sdm_channel!((channels(0, GPIO_SD0), (1, GPIO_SD1), (2, + GPIO_SD2), (3, GPIO_SD3))); + }; +} +#[macro_export] /// ESP-HAL must provide implementation for the following functions: /// ```rust, no_run /// // XTAL_CLK @@ -3250,6 +3271,8 @@ macro_rules! implement_peripheral_clocks { Dma, /// ECC peripheral clock signal Ecc, + /// GPIO_SD peripheral clock signal + GpioSd, /// I2C_EXT0 peripheral clock signal I2cExt0, /// I2S0 peripheral clock signal @@ -3295,6 +3318,7 @@ macro_rules! implement_peripheral_clocks { Self::ApbSarAdc, Self::Dma, Self::Ecc, + Self::GpioSd, Self::I2cExt0, Self::I2s0, Self::ParlIo, @@ -3334,6 +3358,14 @@ macro_rules! implement_peripheral_clocks { .ecc_conf() .modify(|_, w| w.ecc_clk_en().bit(enable)); } + Peripheral::GpioSd => { + crate::peripherals::GPIO_SD::regs() + .clock_gate() + .modify(|_, w| w.clk_en().bit(enable)); + crate::peripherals::GPIO_SD::regs() + .sigmadelta_misc() + .modify(|_, w| w.sigmadelta_clk_en().bit(enable)); + } Peripheral::I2cExt0 => { crate::peripherals::SYSTEM::regs() .i2c0_conf() @@ -3437,6 +3469,9 @@ macro_rules! implement_peripheral_clocks { .ecc_conf() .modify(|_, w| w.ecc_rst_en().bit(reset)); } + Peripheral::GpioSd => { + let _ = reset; + } Peripheral::I2cExt0 => { crate::peripherals::SYSTEM::regs() .i2c0_conf() diff --git a/esp-metadata-generated/src/_generated_esp32c6.rs b/esp-metadata-generated/src/_generated_esp32c6.rs index 47deb8a42fc..b1369617b4c 100644 --- a/esp-metadata-generated/src/_generated_esp32c6.rs +++ b/esp-metadata-generated/src/_generated_esp32c6.rs @@ -346,6 +346,15 @@ macro_rules! property { ("sha.dma") => { true }; + ("sdm.channel_count") => { + 4 + }; + ("sdm.channel_count", str) => { + stringify!(4) + }; + ("sdm.default_clock_source") => { + "pll_f80m" + }; ("sleep.light_sleep") => { true }; @@ -918,6 +927,18 @@ macro_rules! for_each_sha_algorithm { }; } #[macro_export] +#[cfg_attr(docsrs, doc(cfg(feature = "_device-selected")))] +macro_rules! for_each_sdm_channel { + ($($pattern:tt => $code:tt;)*) => { + macro_rules! _for_each_inner_sdm_channel { $(($pattern) => $code;)* ($other : tt) + => {} } _for_each_inner_sdm_channel!((0, GPIO_SD0)); + _for_each_inner_sdm_channel!((1, GPIO_SD1)); _for_each_inner_sdm_channel!((2, + GPIO_SD2)); _for_each_inner_sdm_channel!((3, GPIO_SD3)); + _for_each_inner_sdm_channel!((channels(0, GPIO_SD0), (1, GPIO_SD1), (2, + GPIO_SD2), (3, GPIO_SD3))); + }; +} +#[macro_export] /// ESP-HAL must provide implementation for the following functions: /// ```rust, no_run /// // XTAL_CLK @@ -3952,6 +3973,8 @@ macro_rules! implement_peripheral_clocks { Ecc, /// ETM peripheral clock signal Etm, + /// GPIO_SD peripheral clock signal + GpioSd, /// HMAC peripheral clock signal Hmac, /// I2C_EXT0 peripheral clock signal @@ -4015,6 +4038,7 @@ macro_rules! implement_peripheral_clocks { Self::Ds, Self::Ecc, Self::Etm, + Self::GpioSd, Self::Hmac, Self::I2cExt0, Self::I2s0, @@ -4072,6 +4096,14 @@ macro_rules! implement_peripheral_clocks { .etm_conf() .modify(|_, w| w.etm_clk_en().bit(enable)); } + Peripheral::GpioSd => { + crate::peripherals::GPIO_SD::regs() + .clock_gate() + .modify(|_, w| w.clk_en().bit(enable)); + crate::peripherals::GPIO_SD::regs() + .sigmadelta_misc() + .modify(|_, w| w.function_clk_en().bit(enable)); + } Peripheral::Hmac => { crate::peripherals::SYSTEM::regs() .hmac_conf() @@ -4231,6 +4263,9 @@ macro_rules! implement_peripheral_clocks { .etm_conf() .modify(|_, w| w.etm_rst_en().bit(reset)); } + Peripheral::GpioSd => { + let _ = reset; + } Peripheral::Hmac => { crate::peripherals::SYSTEM::regs() .hmac_conf() diff --git a/esp-metadata-generated/src/_generated_esp32h2.rs b/esp-metadata-generated/src/_generated_esp32h2.rs index 0f74b18f8d3..d1d4b75ffcb 100644 --- a/esp-metadata-generated/src/_generated_esp32h2.rs +++ b/esp-metadata-generated/src/_generated_esp32h2.rs @@ -334,6 +334,15 @@ macro_rules! property { ("sha.dma") => { true }; + ("sdm.channel_count") => { + 4 + }; + ("sdm.channel_count", str) => { + stringify!(4) + }; + ("sdm.default_clock_source") => { + "pll_f48m" + }; ("sleep.light_sleep") => { true }; @@ -898,6 +907,18 @@ macro_rules! for_each_sha_algorithm { }; } #[macro_export] +#[cfg_attr(docsrs, doc(cfg(feature = "_device-selected")))] +macro_rules! for_each_sdm_channel { + ($($pattern:tt => $code:tt;)*) => { + macro_rules! _for_each_inner_sdm_channel { $(($pattern) => $code;)* ($other : tt) + => {} } _for_each_inner_sdm_channel!((0, GPIO_SD0)); + _for_each_inner_sdm_channel!((1, GPIO_SD1)); _for_each_inner_sdm_channel!((2, + GPIO_SD2)); _for_each_inner_sdm_channel!((3, GPIO_SD3)); + _for_each_inner_sdm_channel!((channels(0, GPIO_SD0), (1, GPIO_SD1), (2, + GPIO_SD2), (3, GPIO_SD3))); + }; +} +#[macro_export] /// ESP-HAL must provide implementation for the following functions: /// ```rust, no_run /// // XTAL_CLK @@ -2959,6 +2980,8 @@ macro_rules! implement_peripheral_clocks { Ecdsa, /// ETM peripheral clock signal Etm, + /// GPIO_SD peripheral clock signal + GpioSd, /// HMAC peripheral clock signal Hmac, /// I2C_EXT0 peripheral clock signal @@ -3016,6 +3039,7 @@ macro_rules! implement_peripheral_clocks { Self::Ecc, Self::Ecdsa, Self::Etm, + Self::GpioSd, Self::Hmac, Self::I2cExt0, Self::I2cExt1, @@ -3077,6 +3101,14 @@ macro_rules! implement_peripheral_clocks { .etm_conf() .modify(|_, w| w.etm_clk_en().bit(enable)); } + Peripheral::GpioSd => { + crate::peripherals::GPIO_SD::regs() + .clock_gate() + .modify(|_, w| w.clk_en().bit(enable)); + crate::peripherals::GPIO_SD::regs() + .sigmadelta_misc() + .modify(|_, w| w.function_clk_en().bit(enable)); + } Peripheral::Hmac => { crate::peripherals::SYSTEM::regs() .hmac_conf() @@ -3233,6 +3265,9 @@ macro_rules! implement_peripheral_clocks { .etm_conf() .modify(|_, w| w.etm_rst_en().bit(reset)); } + Peripheral::GpioSd => { + let _ = reset; + } Peripheral::Hmac => { crate::peripherals::SYSTEM::regs() .hmac_conf() diff --git a/esp-metadata-generated/src/_generated_esp32p4.rs b/esp-metadata-generated/src/_generated_esp32p4.rs index 0e7c23b004b..1980c22d5d0 100644 --- a/esp-metadata-generated/src/_generated_esp32p4.rs +++ b/esp-metadata-generated/src/_generated_esp32p4.rs @@ -274,6 +274,15 @@ macro_rules! property { ("sha.dma") => { true }; + ("sdm.channel_count") => { + 8 + }; + ("sdm.channel_count", str) => { + stringify!(8) + }; + ("sdm.default_clock_source") => { + "pll_f80m" + }; ("sleep.light_sleep") => { false }; @@ -830,6 +839,22 @@ macro_rules! for_each_sha_algorithm { }; } #[macro_export] +#[cfg_attr(docsrs, doc(cfg(feature = "_device-selected")))] +macro_rules! for_each_sdm_channel { + ($($pattern:tt => $code:tt;)*) => { + macro_rules! _for_each_inner_sdm_channel { $(($pattern) => $code;)* ($other : tt) + => {} } _for_each_inner_sdm_channel!((0, GPIO_SD0)); + _for_each_inner_sdm_channel!((1, GPIO_SD1)); _for_each_inner_sdm_channel!((2, + GPIO_SD2)); _for_each_inner_sdm_channel!((3, GPIO_SD3)); + _for_each_inner_sdm_channel!((4, GPIO_SD4)); _for_each_inner_sdm_channel!((5, + GPIO_SD5)); _for_each_inner_sdm_channel!((6, GPIO_SD6)); + _for_each_inner_sdm_channel!((7, GPIO_SD7)); + _for_each_inner_sdm_channel!((channels(0, GPIO_SD0), (1, GPIO_SD1), (2, + GPIO_SD2), (3, GPIO_SD3), (4, GPIO_SD4), (5, GPIO_SD5), (6, GPIO_SD6), (7, + GPIO_SD7))); + }; +} +#[macro_export] /// ESP-HAL must provide implementation for the following functions: /// ```rust, no_run /// // XTAL_CLK @@ -2689,6 +2714,8 @@ macro_rules! implement_peripheral_clocks { Emac, /// GDMA peripheral clock signal Gdma, + /// GPIO_SD peripheral clock signal + GpioSd, /// HMAC peripheral clock signal Hmac, /// I2C0 peripheral clock signal @@ -2778,6 +2805,7 @@ macro_rules! implement_peripheral_clocks { Self::Ecdsa, Self::Emac, Self::Gdma, + Self::GpioSd, Self::Hmac, Self::I2c0, Self::I2c1, @@ -2866,6 +2894,14 @@ macro_rules! implement_peripheral_clocks { .soc_clk_ctrl1() .modify(|_, w| w.gdma_sys_clk_en().bit(enable)); } + Peripheral::GpioSd => { + crate::peripherals::GPIO_SD::regs() + .clock_gate() + .modify(|_, w| w.clk_en().bit(enable)); + crate::peripherals::GPIO_SD::regs() + .sigmadelta_misc() + .modify(|_, w| w.function_clk_en().bit(enable)); + } Peripheral::Hmac => { crate::peripherals::HP_SYS_CLKRST::regs() .soc_clk_ctrl1() @@ -3114,6 +3150,9 @@ macro_rules! implement_peripheral_clocks { .hp_rst_en0() .modify(|_, w| w.rst_en_gdma().bit(reset)); } + Peripheral::GpioSd => { + let _ = reset; + } Peripheral::Hmac => { crate::peripherals::HP_SYS_CLKRST::regs() .hp_rst_en2() @@ -3616,10 +3655,11 @@ macro_rules! for_each_peripheral { _for_each_inner_peripheral!((@ peri_type #[doc = "EFUSE peripheral singleton"] EFUSE <= EFUSE() (unstable))); _for_each_inner_peripheral!((@ peri_type #[doc = "GPIO peripheral singleton"] GPIO <= GPIO() (unstable))); - _for_each_inner_peripheral!((@ peri_type #[doc = "SYSTEM peripheral singleton"] - SYSTEM <= HP_SYS() (unstable))); _for_each_inner_peripheral!((@ peri_type #[doc = - "HP_SYS peripheral singleton"] HP_SYS <= HP_SYS() (unstable))); - _for_each_inner_peripheral!((@ peri_type #[doc = + _for_each_inner_peripheral!((@ peri_type #[doc = "GPIO_SD peripheral singleton"] + GPIO_SD <= GPIO_SD() (unstable))); _for_each_inner_peripheral!((@ peri_type #[doc + = "SYSTEM peripheral singleton"] SYSTEM <= HP_SYS() (unstable))); + _for_each_inner_peripheral!((@ peri_type #[doc = "HP_SYS peripheral singleton"] + HP_SYS <= HP_SYS() (unstable))); _for_each_inner_peripheral!((@ peri_type #[doc = "HP_SYS_CLKRST peripheral singleton"] HP_SYS_CLKRST <= HP_SYS_CLKRST() (unstable))); _for_each_inner_peripheral!((@ peri_type #[doc = "RNG peripheral singleton"] RNG <= LP_SYS() (unstable))); @@ -3758,6 +3798,7 @@ macro_rules! for_each_peripheral { _for_each_inner_peripheral!((GPIO51)); _for_each_inner_peripheral!((GPIO52)); _for_each_inner_peripheral!((GPIO53)); _for_each_inner_peripheral!((GPIO54)); _for_each_inner_peripheral!((GPIO(unstable))); + _for_each_inner_peripheral!((GPIO_SD(unstable))); _for_each_inner_peripheral!((SYSTEM(unstable))); _for_each_inner_peripheral!((HP_SYS(unstable))); _for_each_inner_peripheral!((HP_SYS_CLKRST(unstable))); @@ -3934,19 +3975,21 @@ macro_rules! for_each_peripheral { "GPIO54 peripheral singleton"] GPIO54 <= virtual()), (@ peri_type #[doc = "EFUSE peripheral singleton"] EFUSE <= EFUSE() (unstable)), (@ peri_type #[doc = "GPIO peripheral singleton"] GPIO <= GPIO() (unstable)), (@ peri_type #[doc = - "SYSTEM peripheral singleton"] SYSTEM <= HP_SYS() (unstable)), (@ peri_type #[doc - = "HP_SYS peripheral singleton"] HP_SYS <= HP_SYS() (unstable)), (@ peri_type - #[doc = "HP_SYS_CLKRST peripheral singleton"] HP_SYS_CLKRST <= HP_SYS_CLKRST() - (unstable)), (@ peri_type #[doc = "RNG peripheral singleton"] RNG <= LP_SYS() - (unstable)), (@ peri_type #[doc = "INTERRUPT_CORE0 peripheral singleton"] - INTERRUPT_CORE0 <= INTERRUPT_CORE0() (unstable)), (@ peri_type #[doc = - "INTERRUPT_CORE1 peripheral singleton"] INTERRUPT_CORE1 <= INTERRUPT_CORE1() - (unstable)), (@ peri_type #[doc = "LP_I2C_ANA_MST peripheral singleton"] - LP_I2C_ANA_MST <= LP_I2C_ANA_MST() (unstable)), (@ peri_type #[doc = - "CLIC peripheral singleton"] CLIC <= CLIC() (unstable)), (@ peri_type #[doc = - "IO_MUX peripheral singleton"] IO_MUX <= IO_MUX() (unstable)), (@ peri_type #[doc - = "LP_AON peripheral singleton"] LP_AON <= LP_SYS() (unstable)), (@ peri_type - #[doc = "LP_AON_CLKRST peripheral singleton"] LP_AON_CLKRST <= LP_AON_CLKRST() + "GPIO_SD peripheral singleton"] GPIO_SD <= GPIO_SD() (unstable)), (@ peri_type + #[doc = "SYSTEM peripheral singleton"] SYSTEM <= HP_SYS() (unstable)), (@ + peri_type #[doc = "HP_SYS peripheral singleton"] HP_SYS <= HP_SYS() (unstable)), + (@ peri_type #[doc = "HP_SYS_CLKRST peripheral singleton"] HP_SYS_CLKRST <= + HP_SYS_CLKRST() (unstable)), (@ peri_type #[doc = "RNG peripheral singleton"] RNG + <= LP_SYS() (unstable)), (@ peri_type #[doc = + "INTERRUPT_CORE0 peripheral singleton"] INTERRUPT_CORE0 <= INTERRUPT_CORE0() + (unstable)), (@ peri_type #[doc = "INTERRUPT_CORE1 peripheral singleton"] + INTERRUPT_CORE1 <= INTERRUPT_CORE1() (unstable)), (@ peri_type #[doc = + "LP_I2C_ANA_MST peripheral singleton"] LP_I2C_ANA_MST <= LP_I2C_ANA_MST() + (unstable)), (@ peri_type #[doc = "CLIC peripheral singleton"] CLIC <= CLIC() + (unstable)), (@ peri_type #[doc = "IO_MUX peripheral singleton"] IO_MUX <= + IO_MUX() (unstable)), (@ peri_type #[doc = "LP_AON peripheral singleton"] LP_AON + <= LP_SYS() (unstable)), (@ peri_type #[doc = + "LP_AON_CLKRST peripheral singleton"] LP_AON_CLKRST <= LP_AON_CLKRST() (unstable)), (@ peri_type #[doc = "LP_SYS peripheral singleton"] LP_SYS <= LP_SYS() (unstable)), (@ peri_type #[doc = "LP_WDT peripheral singleton"] LP_WDT <= LP_WDT() (unstable)), (@ peri_type #[doc = "LPWR peripheral singleton"] LPWR @@ -4025,9 +4068,9 @@ macro_rules! for_each_peripheral { (GPIO27), (GPIO28), (GPIO29), (GPIO30), (GPIO31), (GPIO32), (GPIO33), (GPIO34), (GPIO35), (GPIO36), (GPIO37), (GPIO38), (GPIO39), (GPIO40), (GPIO41), (GPIO42), (GPIO43), (GPIO44), (GPIO45), (GPIO46), (GPIO47), (GPIO48), (GPIO49), (GPIO50), - (GPIO51), (GPIO52), (GPIO53), (GPIO54), (GPIO(unstable)), (SYSTEM(unstable)), - (HP_SYS(unstable)), (HP_SYS_CLKRST(unstable)), (RNG(unstable)), - (INTERRUPT_CORE0(unstable)), (INTERRUPT_CORE1(unstable)), + (GPIO51), (GPIO52), (GPIO53), (GPIO54), (GPIO(unstable)), (GPIO_SD(unstable)), + (SYSTEM(unstable)), (HP_SYS(unstable)), (HP_SYS_CLKRST(unstable)), + (RNG(unstable)), (INTERRUPT_CORE0(unstable)), (INTERRUPT_CORE1(unstable)), (LP_I2C_ANA_MST(unstable)), (CLIC(unstable)), (IO_MUX(unstable)), (LP_AON(unstable)), (LP_AON_CLKRST(unstable)), (LP_SYS(unstable)), (LP_WDT(unstable)), (LPWR(unstable)), (PMU(unstable)), (SYSTIMER(unstable)), diff --git a/esp-metadata-generated/src/_generated_esp32s2.rs b/esp-metadata-generated/src/_generated_esp32s2.rs index f1958452fdf..9ef2c62edf8 100644 --- a/esp-metadata-generated/src/_generated_esp32s2.rs +++ b/esp-metadata-generated/src/_generated_esp32s2.rs @@ -304,6 +304,15 @@ macro_rules! property { ("sha.dma") => { true }; + ("sdm.channel_count") => { + 8 + }; + ("sdm.channel_count", str) => { + stringify!(8) + }; + ("sdm.default_clock_source") => { + "apb" + }; ("sleep.light_sleep") => { true }; @@ -777,6 +786,22 @@ macro_rules! for_each_sha_algorithm { }; } #[macro_export] +#[cfg_attr(docsrs, doc(cfg(feature = "_device-selected")))] +macro_rules! for_each_sdm_channel { + ($($pattern:tt => $code:tt;)*) => { + macro_rules! _for_each_inner_sdm_channel { $(($pattern) => $code;)* ($other : tt) + => {} } _for_each_inner_sdm_channel!((0, GPIO_SD0)); + _for_each_inner_sdm_channel!((1, GPIO_SD1)); _for_each_inner_sdm_channel!((2, + GPIO_SD2)); _for_each_inner_sdm_channel!((3, GPIO_SD3)); + _for_each_inner_sdm_channel!((4, GPIO_SD4)); _for_each_inner_sdm_channel!((5, + GPIO_SD5)); _for_each_inner_sdm_channel!((6, GPIO_SD6)); + _for_each_inner_sdm_channel!((7, GPIO_SD7)); + _for_each_inner_sdm_channel!((channels(0, GPIO_SD0), (1, GPIO_SD1), (2, + GPIO_SD2), (3, GPIO_SD3), (4, GPIO_SD4), (5, GPIO_SD5), (6, GPIO_SD6), (7, + GPIO_SD7))); + }; +} +#[macro_export] /// ESP-HAL must provide implementation for the following functions: /// ```rust, no_run /// // XTAL_CLK @@ -3093,6 +3118,8 @@ macro_rules! implement_peripheral_clocks { DedicatedGpio, /// DS peripheral clock signal Ds, + /// GPIO_SD peripheral clock signal + GpioSd, /// HMAC peripheral clock signal Hmac, /// I2C_EXT0 peripheral clock signal @@ -3157,6 +3184,7 @@ macro_rules! implement_peripheral_clocks { Self::CryptoDma, Self::DedicatedGpio, Self::Ds, + Self::GpioSd, Self::Hmac, Self::I2cExt0, Self::I2cExt1, @@ -3217,6 +3245,14 @@ macro_rules! implement_peripheral_clocks { .perip_clk_en1() .modify(|_, w| w.crypto_ds_clk_en().bit(enable)); } + Peripheral::GpioSd => { + crate::peripherals::GPIO_SD::regs() + .clock_gate() + .modify(|_, w| w.clk_en().bit(enable)); + crate::peripherals::GPIO_SD::regs() + .sigmadelta_misc() + .modify(|_, w| w.function_clk_en().bit(enable)); + } Peripheral::Hmac => { crate::peripherals::SYSTEM::regs() .perip_clk_en1() @@ -3388,6 +3424,9 @@ macro_rules! implement_peripheral_clocks { .perip_rst_en1() .modify(|_, w| w.crypto_ds_rst().bit(reset)); } + Peripheral::GpioSd => { + let _ = reset; + } Peripheral::Hmac => { crate::peripherals::SYSTEM::regs() .perip_rst_en1() diff --git a/esp-metadata-generated/src/_generated_esp32s3.rs b/esp-metadata-generated/src/_generated_esp32s3.rs index c95c7cf5083..201ec6065ac 100644 --- a/esp-metadata-generated/src/_generated_esp32s3.rs +++ b/esp-metadata-generated/src/_generated_esp32s3.rs @@ -325,6 +325,15 @@ macro_rules! property { ("sha.dma") => { true }; + ("sdm.channel_count") => { + 8 + }; + ("sdm.channel_count", str) => { + stringify!(8) + }; + ("sdm.default_clock_source") => { + "apb" + }; ("sleep.light_sleep") => { true }; @@ -808,6 +817,22 @@ macro_rules! for_each_sha_algorithm { }; } #[macro_export] +#[cfg_attr(docsrs, doc(cfg(feature = "_device-selected")))] +macro_rules! for_each_sdm_channel { + ($($pattern:tt => $code:tt;)*) => { + macro_rules! _for_each_inner_sdm_channel { $(($pattern) => $code;)* ($other : tt) + => {} } _for_each_inner_sdm_channel!((0, GPIO_SD0)); + _for_each_inner_sdm_channel!((1, GPIO_SD1)); _for_each_inner_sdm_channel!((2, + GPIO_SD2)); _for_each_inner_sdm_channel!((3, GPIO_SD3)); + _for_each_inner_sdm_channel!((4, GPIO_SD4)); _for_each_inner_sdm_channel!((5, + GPIO_SD5)); _for_each_inner_sdm_channel!((6, GPIO_SD6)); + _for_each_inner_sdm_channel!((7, GPIO_SD7)); + _for_each_inner_sdm_channel!((channels(0, GPIO_SD0), (1, GPIO_SD1), (2, + GPIO_SD2), (3, GPIO_SD3), (4, GPIO_SD4), (5, GPIO_SD5), (6, GPIO_SD6), (7, + GPIO_SD7))); + }; +} +#[macro_export] /// ESP-HAL must provide implementation for the following functions: /// ```rust, no_run /// // XTAL_CLK @@ -3112,6 +3137,8 @@ macro_rules! implement_peripheral_clocks { Dma, /// DS peripheral clock signal Ds, + /// GPIO_SD peripheral clock signal + GpioSd, /// HMAC peripheral clock signal Hmac, /// I2C_EXT0 peripheral clock signal @@ -3184,6 +3211,7 @@ macro_rules! implement_peripheral_clocks { Self::DedicatedGpio, Self::Dma, Self::Ds, + Self::GpioSd, Self::Hmac, Self::I2cExt0, Self::I2cExt1, @@ -3241,6 +3269,14 @@ macro_rules! implement_peripheral_clocks { .perip_clk_en1() .modify(|_, w| w.crypto_ds_clk_en().bit(enable)); } + Peripheral::GpioSd => { + crate::peripherals::GPIO_SD::regs() + .clock_gate() + .modify(|_, w| w.clk_en().bit(enable)); + crate::peripherals::GPIO_SD::regs() + .sigmadelta_misc() + .modify(|_, w| w.function_clk_en().bit(enable)); + } Peripheral::Hmac => { crate::peripherals::SYSTEM::regs() .perip_clk_en1() @@ -3410,6 +3446,9 @@ macro_rules! implement_peripheral_clocks { .perip_rst_en1() .modify(|_, w| w.crypto_ds_rst().bit(reset)); } + Peripheral::GpioSd => { + let _ = reset; + } Peripheral::Hmac => { crate::peripherals::SYSTEM::regs() .perip_rst_en1() diff --git a/esp-metadata/devices/esp32.toml b/esp-metadata/devices/esp32.toml index 5cdb46883b1..b8f9a75f669 100644 --- a/esp-metadata/devices/esp32.toml +++ b/esp-metadata/devices/esp32.toml @@ -302,6 +302,7 @@ clocks = { system_clocks = { clock_tree = [ { name = "Ledc" }, { name = "Pcnt" }, { name = "Rmt" }, + { name = "GpioSd", template_params = { clk_en_template = "let _ = enable;", rst_template = "let _ = reset;" } }, { name = "Uhci0" }, { name = "I2cExt0" }, { name = "Spi2" }, @@ -829,7 +830,10 @@ support_status = "partial" algo = { sha1 = 0, sha256 = 0, sha384 = 0, sha512 = 0 } # fake mode bits, ESP32 has separate register sets [device.sdm] -support_status = { status = "not_supported", issue = 2370 } +support_status = "supported" +channel_count = 8 +clock_sources = ["apb"] +default_clock_source = "apb" [device.spi_master] version = 1 diff --git a/esp-metadata/devices/esp32c3.toml b/esp-metadata/devices/esp32c3.toml index 176418b1fce..342d41ffa49 100644 --- a/esp-metadata/devices/esp32c3.toml +++ b/esp-metadata/devices/esp32c3.toml @@ -228,6 +228,7 @@ clocks = { system_clocks = { clock_tree = [ { name = "Timg0", template_params = { peripheral = "timergroup" }, keep_enabled = true }, { name = "Ledc" }, { name = "Rmt" }, + { name = "GpioSd", template_params = { clk_en_template = "crate::peripherals::GPIO_SD::regs().clock_gate().modify(|_, w| w.clk_en().bit(enable)); crate::peripherals::GPIO_SD::regs().sigmadelta_misc().modify(|_, w| w.function_clk_en().bit(enable));", rst_template = "let _ = reset;" } }, { name = "Uhci0" }, { name = "I2cExt0" }, { name = "Spi2" }, @@ -484,7 +485,10 @@ dma = true algo = { sha1 = 0, sha224 = 1, sha256 = 2 } [device.sdm] -support_status = { status = "not_supported", issue = 2370 } +support_status = "supported" +channel_count = 4 +clock_sources = ["apb"] +default_clock_source = "apb" [device.spi_master] version = 3 diff --git a/esp-metadata/devices/esp32c5.toml b/esp-metadata/devices/esp32c5.toml index 4b1bcfae326..50bcad40198 100644 --- a/esp-metadata/devices/esp32c5.toml +++ b/esp-metadata/devices/esp32c5.toml @@ -268,6 +268,7 @@ clocks = { system_clocks = { clock_tree = [ { name = "I2s0", template_params = { peripheral = "i2s" } }, { name = "Pcnt" }, { name = "Rmt" }, + { name = "GpioSd", template_params = { clk_en_template = "crate::peripherals::GPIO_SD::regs().clock_gate().modify(|_, w| w.clk_en().bit(enable)); crate::peripherals::GPIO_SD::regs().sigmadelta_misc().modify(|_, w| w.sigmadelta_clk_en().bit(enable));", rst_template = "let _ = reset;" } }, { name = "Systimer", keep_enabled = true }, # can be clocked from XTAL_CLK or RC_FAST_CLK, the latter has no divider? { name = "ApbSarAdc", template_params = { peripheral = "saradc", clk_en_field = "saradc_reg_clk_en", rst_field = "saradc_reg_rst_en" }, keep_enabled = true }, { name = "ParlIo", template_params = { clk_en_field = "parl_clk_en", rst_field = "parl_rst_en" } }, @@ -655,7 +656,10 @@ support_status = { status = "not_supported", issue = 5164 } support_status = { status = "not_supported", issue = 884 } [device.sdm] -support_status = { status = "not_supported", issue = 2370 } +support_status = "supported" +channel_count = 4 +clock_sources = ["xtal", "pll_f80m"] +default_clock_source = "pll_f80m" [device.lp_uart] support_status = { status = "not_supported", issue = 5155 } diff --git a/esp-metadata/devices/esp32c6.toml b/esp-metadata/devices/esp32c6.toml index 91b71e73bb7..5ea54fc79d0 100644 --- a/esp-metadata/devices/esp32c6.toml +++ b/esp-metadata/devices/esp32c6.toml @@ -303,6 +303,7 @@ clocks = { system_clocks = { clock_tree = [ { name = "Hmac" }, #{ name = "Iomux" }, #{ name = "MemMonitor" }, + { name = "GpioSd", template_params = { clk_en_template = "crate::peripherals::GPIO_SD::regs().clock_gate().modify(|_, w| w.clk_en().bit(enable)); crate::peripherals::GPIO_SD::regs().sigmadelta_misc().modify(|_, w| w.function_clk_en().bit(enable));", rst_template = "let _ = reset;" } }, { name = "Trace0", template_params = { peripheral = "trace" } }, #{ name = "Assist" }, #{ name = "Cache" }, @@ -690,7 +691,10 @@ dma = true algo = { sha1 = 0, sha224 = 1, sha256 = 2 } [device.sdm] -support_status = { status = "not_supported", issue = 2370 } +support_status = "supported" +channel_count = 4 +clock_sources = ["xtal", "pll_f80m"] +default_clock_source = "pll_f80m" [device.spi_master] version = 3 diff --git a/esp-metadata/devices/esp32h2.toml b/esp-metadata/devices/esp32h2.toml index 5425a14f696..dd5c2b9151b 100644 --- a/esp-metadata/devices/esp32h2.toml +++ b/esp-metadata/devices/esp32h2.toml @@ -256,6 +256,7 @@ clocks = { system_clocks = { clock_tree = [ { name = "Ecdsa" }, #{ name = "Iomux" }, #{ name = "MemMonitor" }, + { name = "GpioSd", template_params = { clk_en_template = "crate::peripherals::GPIO_SD::regs().clock_gate().modify(|_, w| w.clk_en().bit(enable)); crate::peripherals::GPIO_SD::regs().sigmadelta_misc().modify(|_, w| w.function_clk_en().bit(enable));", rst_template = "let _ = reset;" } }, { name = "Trace0", template_params = { peripheral = "trace" } }, #{ name = "Assist" }, #{ name = "Cache" }, @@ -588,7 +589,10 @@ dma = true algo = { sha1 = 0, sha224 = 1, sha256 = 2 } [device.sdm] -support_status = { status = "not_supported", issue = 2370 } +support_status = "supported" +channel_count = 4 +clock_sources = ["xtal", "pll_f48m"] +default_clock_source = "pll_f48m" [device.ecdsa] support_status = { status = "not_supported", issue = 5444 } diff --git a/esp-metadata/devices/esp32p4.toml b/esp-metadata/devices/esp32p4.toml index fd7aecea33d..bb85650b382 100644 --- a/esp-metadata/devices/esp32p4.toml +++ b/esp-metadata/devices/esp32p4.toml @@ -196,6 +196,7 @@ clocks = { system_clocks = { clock_tree = [ # PWM/Counter/RMT -- LCD/CAM clock gate lives in SOC_CLK_CTRL3 (not modelled in current PAC), keep stub. { name = "Ledc", template_params = { default_rst_template = "{{rst_en1_template}}", rst_field = "rst_en_ledc" } }, { name = "Pcnt", template_params = { default_clk_en_template = "{{apb_clk_template}}", apb_field = "pcnt_apb_clk_en", default_rst_template = "{{rst_en1_template}}", rst_field = "rst_en_pcnt" } }, + { name = "GpioSd", template_params = { clk_en_template = "crate::peripherals::GPIO_SD::regs().clock_gate().modify(|_, w| w.clk_en().bit(enable)); crate::peripherals::GPIO_SD::regs().sigmadelta_misc().modify(|_, w| w.function_clk_en().bit(enable));", rst_template = "let _ = reset;" } }, { name = "Mcpwm0", template_params = { default_clk_en_template = "{{apb_clk_template}}", apb_field = "mcpwm0_apb_clk_en", default_rst_template = "{{rst_en1_template}}", rst_field = "rst_en_pwm0" } }, { name = "Mcpwm1", template_params = { default_clk_en_template = "{{apb_clk_template}}", apb_field = "mcpwm1_apb_clk_en", default_rst_template = "{{rst_en1_template}}", rst_field = "rst_en_pwm1" } }, # NOTE: `rmt_sys_clk_en` despite the name lives on SOC_CLK_CTRL2, not CTRL1. @@ -247,6 +248,7 @@ peripherals = [ # Minimal set for esp-hal compilation { name = "EFUSE", hidden = true }, { name = "GPIO" }, + { name = "GPIO_SD" }, { name = "SYSTEM", pac = "HP_SYS" }, { name = "HP_SYS" }, { name = "HP_SYS_CLKRST" }, @@ -454,7 +456,10 @@ support_status = "not_supported" # SDIO host [device.sd_slave] support_status = "not_supported" [device.sdm] -support_status = { status = "not_supported", issue = 2370 } +support_status = "supported" +channel_count = 8 +clock_sources = ["xtal", "pll_f80m"] +default_clock_source = "pll_f80m" [device.sleep] support_status = "not_supported" # PMU eco5 sleep -- placeholder TODO in esp-hal [device.spi_slave] diff --git a/esp-metadata/devices/esp32s2.toml b/esp-metadata/devices/esp32s2.toml index 041a16bfbe8..28c5fa1dd5b 100644 --- a/esp-metadata/devices/esp32s2.toml +++ b/esp-metadata/devices/esp32s2.toml @@ -247,6 +247,7 @@ clocks = { system_clocks = { clock_tree = [ { name = "Ledc" }, { name = "Pcnt" }, { name = "Rmt" }, + { name = "GpioSd", template_params = { clk_en_template = "crate::peripherals::GPIO_SD::regs().clock_gate().modify(|_, w| w.clk_en().bit(enable)); crate::peripherals::GPIO_SD::regs().sigmadelta_misc().modify(|_, w| w.function_clk_en().bit(enable));", rst_template = "let _ = reset;" } }, { name = "Uhci0" }, { name = "I2cExt0" }, { name = "Spi2" }, @@ -613,7 +614,10 @@ dma = true algo = { sha1 = 0, sha224 = 1, sha256 = 2, sha384 = 3, sha512 = 4, sha512_224 = 5, sha512_256 = 6, sha512_t = 7 } [device.sdm] -support_status = { status = "not_supported", issue = 2370 } +support_status = "supported" +channel_count = 8 +clock_sources = ["apb"] +default_clock_source = "apb" [device.spi_master] version = 2 diff --git a/esp-metadata/devices/esp32s3.toml b/esp-metadata/devices/esp32s3.toml index fde81fd91ec..eb7ad502634 100644 --- a/esp-metadata/devices/esp32s3.toml +++ b/esp-metadata/devices/esp32s3.toml @@ -262,6 +262,7 @@ clocks = { system_clocks = { clock_tree = [ { name = "Ledc" }, { name = "Pcnt" }, { name = "Rmt" }, + { name = "GpioSd", template_params = { clk_en_template = "crate::peripherals::GPIO_SD::regs().clock_gate().modify(|_, w| w.clk_en().bit(enable)); crate::peripherals::GPIO_SD::regs().sigmadelta_misc().modify(|_, w| w.function_clk_en().bit(enable));", rst_template = "let _ = reset;" } }, { name = "Uhci0" }, { name = "I2cExt0" }, { name = "Spi2" }, @@ -805,7 +806,10 @@ dma = true algo = { sha1 = 0, sha224 = 1, sha256 = 2, sha384 = 3, sha512 = 4, sha512_224 = 5, sha512_256 = 6, sha512_t = 7 } [device.sdm] -support_status = { status = "not_supported", issue = 2370 } +support_status = "supported" +channel_count = 8 +clock_sources = ["apb"] +default_clock_source = "apb" [device.spi_master] version = 3 diff --git a/esp-metadata/src/cfg.rs b/esp-metadata/src/cfg.rs index 5c63d0b5015..983ccc5183b 100644 --- a/esp-metadata/src/cfg.rs +++ b/esp-metadata/src/cfg.rs @@ -5,6 +5,7 @@ pub(crate) mod i2c_master; pub(crate) mod interrupt; pub(crate) mod rmt; pub(crate) mod rsa; +pub(crate) mod sdm; pub(crate) mod sha; pub(crate) mod soc; pub(crate) mod spi_master; @@ -18,6 +19,7 @@ pub(crate) use gpio::*; pub(crate) use i2c_master::*; pub(crate) use interrupt::*; pub(crate) use rmt::*; +pub(crate) use sdm::*; pub(crate) use sha::*; pub(crate) use soc::*; pub(crate) use spi_master::*; @@ -635,7 +637,15 @@ driver_configs![ SdmProperties { driver: sdm, name: "SDM", - properties: {} + properties: { + /// Number of sigma-delta channels. + channel_count: SdmChannels, + /// Supported source clocks for the shared SDM/IO_MUX clock. + #[serde(default)] + clock_sources: Vec, + /// Default source clock for the shared SDM/IO_MUX clock. + default_clock_source: String, + } }, SleepProperties { driver: sleep, From 50db7519090cc7ae7023dd3dca32e4a4a9bde51d Mon Sep 17 00:00:00 2001 From: tzyt Date: Tue, 26 May 2026 21:05:09 -0500 Subject: [PATCH 02/26] add new file for SDM --- esp-hal/src/sdm.rs | 580 +++++++++++++++++++++ esp-metadata/src/cfg/sdm.rs | 50 ++ examples/peripheral/sdm/.cargo/config.toml | 24 + examples/peripheral/sdm/Cargo.toml | 62 +++ examples/peripheral/sdm/src/main.rs | 46 ++ 5 files changed, 762 insertions(+) create mode 100644 esp-hal/src/sdm.rs create mode 100644 esp-metadata/src/cfg/sdm.rs create mode 100644 examples/peripheral/sdm/.cargo/config.toml create mode 100644 examples/peripheral/sdm/Cargo.toml create mode 100644 examples/peripheral/sdm/src/main.rs diff --git a/esp-hal/src/sdm.rs b/esp-hal/src/sdm.rs new file mode 100644 index 00000000000..e7c76da0a5b --- /dev/null +++ b/esp-hal/src/sdm.rs @@ -0,0 +1,580 @@ +//! Sigma-delta modulation peripheral. +//! +//! The sigma-delta modulator produces a pulse-density modulated output on a +//! GPIO matrix signal. Each channel can be configured with a carrier frequency +//! and pulse density, then routed to one output pin. + +use core::fmt; + +use esp_sync::NonReentrantMutex; + +use crate::{ + gpio::{ + OutputConfig, OutputSignal, PinGuard, + interconnect::{OutputSignal as GpioOutputSignal, PeripheralOutput}, + }, + peripherals::GPIO_SD, + system::{GenericPeripheralGuard, Peripheral}, + time::Rate, +}; + +static CLOCK_STATE: NonReentrantMutex = NonReentrantMutex::new(SdmClockState::new()); + +struct SdmClockState { + users: usize, + source: Option, +} + +impl SdmClockState { + const fn new() -> Self { + Self { + users: 0, + source: None, + } + } +} + +for_each_sdm_channel!( + (channels $(($ch:literal, $signal:ident)),*) => { + paste::paste! { + /// Sigma-delta peripheral. + /// + /// This type only owns the SDM peripheral token and exposes the hardware + /// channels. Moving individual channels out of this collection is supported. + #[derive(Debug)] + #[non_exhaustive] + pub struct Sdm<'d> { + _instance: GPIO_SD<'d>, + $( + #[doc = concat!("Channel ", stringify!($ch), ".")] + pub []: Channel<$ch>, + )* + } + + impl<'d> Sdm<'d> { + /// Creates a new sigma-delta peripheral driver. + pub fn new(instance: GPIO_SD<'d>) -> Self { + Self::new_with_config(instance, SdmConfig::default()) + } + + /// Creates a new sigma-delta peripheral driver with the given + /// peripheral-wide configuration. + /// + /// The SDM clock source is shared by all SDM channels through the IO_MUX + /// clock, so it is selected here instead of being configurable per channel. + pub fn new_with_config(instance: GPIO_SD<'d>, config: SdmConfig) -> Self { + Self { + _instance: instance, + $( + []: Channel::new(config.clock_source), + )* + } + } + } + } + + fn output_signal(channel: usize) -> OutputSignal { + match channel { + $( + $ch => OutputSignal::$signal, + )* + _ => unreachable!("SDM channel index out of range"), + } + } + }; +); + +/// Sigma-delta peripheral configuration. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub struct SdmConfig { + /// Clock source used by the shared SDM/IO_MUX clock. + pub clock_source: ClockSource, +} + +impl SdmConfig { + /// Selects the source clock used by all SDM channels. + pub const fn with_clock_source(mut self, clock_source: ClockSource) -> Self { + self.clock_source = clock_source; + self + } +} + +impl Default for SdmConfig { + fn default() -> Self { + Self { + clock_source: ClockSource::default(), + } + } +} + +/// Source clock for the shared SDM/IO_MUX clock. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum ClockSource { + /// APB clock. + #[cfg(any(esp32, esp32c3, esp32s2, esp32s3))] + Apb, + /// XTAL clock. + #[cfg(any(esp32c5, esp32c6, esp32h2, esp32p4))] + Xtal, + /// Fixed 80 MHz PLL clock. + #[cfg(any(esp32c5, esp32c6, esp32p4))] + PllF80m, + /// Fixed 48 MHz PLL clock. + #[cfg(esp32h2)] + PllF48m, +} + +impl Default for ClockSource { + fn default() -> Self { + cfg_if::cfg_if! { + if #[cfg(any(esp32, esp32c3, esp32s2, esp32s3))] { + Self::Apb + } else if #[cfg(any(esp32c5, esp32c6, esp32p4))] { + Self::PllF80m + } else if #[cfg(esp32h2)] { + Self::PllF48m + } + } + } +} + +impl ClockSource { + fn frequency(self) -> Rate { + match self { + #[cfg(any(esp32, esp32c3, esp32s2, esp32s3))] + Self::Apb => Rate::from_hz(crate::soc::clocks::apb_clk_frequency()), + #[cfg(any(esp32c5, esp32c6, esp32h2, esp32p4))] + Self::Xtal => crate::clock::xtal_clock(), + #[cfg(any(esp32c5, esp32c6, esp32p4))] + Self::PllF80m => Rate::from_mhz(80), + #[cfg(esp32h2)] + Self::PllF48m => Rate::from_mhz(48), + } + } +} + +/// Sigma-delta channel timing configuration. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Timing { + /// Derive the hardware prescaler from a requested output frequency. + Frequency(Rate), + /// Use a raw hardware prescaler. + /// + /// The hardware divider range is `1..=256`. + Prescaler(u16), +} + +impl Timing { + fn prescaler(self, clock_source: ClockSource) -> Result { + match self { + Self::Frequency(frequency) => prescaler_from_frequency(frequency, clock_source), + Self::Prescaler(prescaler) => { + check_prescaler(prescaler)?; + Ok(prescaler) + } + } + } +} + +/// Sigma-delta channel configuration. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct ChannelConfig { + /// Channel timing. + /// + /// Requested frequencies are converted to a raw hardware prescaler using + /// the SDM clock source selected in [`SdmConfig`]. + pub timing: Timing, + /// Pulse density. + /// + /// The value ranges from `-128` to `127`. + pub pulse_density: i8, +} + +impl ChannelConfig { + /// Sets the requested output frequency. + pub const fn with_frequency(mut self, frequency: Rate) -> Self { + self.timing = Timing::Frequency(frequency); + self + } + + /// Sets the raw hardware prescaler. + pub const fn with_prescaler(mut self, prescaler: u16) -> Self { + self.timing = Timing::Prescaler(prescaler); + self + } + + /// Sets the pulse density. + pub const fn with_pulse_density(mut self, density: i8) -> Self { + self.pulse_density = density; + self + } + + /// Sets the duty cycle. + /// + /// This is a convenience mapping where `0` maps to the minimum density and + /// `255` maps to the maximum density. + pub const fn with_duty(mut self, duty: u8) -> Self { + self.pulse_density = duty_to_density(duty); + self + } +} + +impl Default for ChannelConfig { + fn default() -> Self { + Self { + timing: Timing::Prescaler(1), + pulse_density: 0, + } + } +} + +/// Sigma-delta configuration or runtime error. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error { + /// The requested frequency cannot be represented by the hardware prescaler. + UnreachableTargetFrequency, + /// Another SDM channel is already using a different clock source. + ClockSourceConflict, + /// The raw prescaler is outside the hardware range. + PrescalerOutOfRange, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::UnreachableTargetFrequency => f.write_str("unreachable target frequency"), + Self::ClockSourceConflict => f.write_str("SDM clock source conflict"), + Self::PrescalerOutOfRange => f.write_str("prescaler out of range"), + } + } +} + +impl core::error::Error for Error {} + +/// A sigma-delta channel. +/// +/// Channels are exposed by [`Sdm`]. A channel enables the SDM peripheral clock +/// when it is connected. +#[derive(Debug)] +pub struct Channel { + // This is copied from the Sdm-level configuration so partially moved + // channels still use the shared clock source selected at construction time. + // It is intentionally not configurable per channel. + clock_source: ClockSource, + pin_guard: Option, + clock_guard: Option, +} + +impl Channel { + const fn new(clock_source: ClockSource) -> Self { + Self { + clock_source, + pin_guard: None, + clock_guard: None, + } + } + + /// Configures this channel and connects it to an output pin. + /// + /// Reconnecting a channel replaces the previous pin connection. + pub fn connect<'d>( + &mut self, + pin: impl PeripheralOutput<'d>, + config: ChannelConfig, + ) -> Result<(), Error> { + let prescaler = config.timing.prescaler(self.clock_source)?; + self.clock_guard = Some(SdmClockGuard::new(self.clock_source)?); + set_prescaler_raw(CHANNEL, prescaler); + set_pulse_density_raw(CHANNEL, config.pulse_density); + self.reconnect(pin); + Ok(()) + } + + /// Connects this channel to a new output pin without changing its channel + /// configuration. + /// + /// This only changes the GPIO matrix route and replaces the previous pin + /// connection. + pub fn reconnect<'d>(&mut self, pin: impl PeripheralOutput<'d>) { + self.pin_guard = None; + let pin: GpioOutputSignal<'d> = pin.into(); + pin.apply_output_config(&OutputConfig::default()); + pin.set_output_enable(true); + self.pin_guard = Some(pin.connect_with_guard(output_signal(CHANNEL))); + } + + /// Returns whether the channel is currently routed to an output pin. + pub fn is_connected(&self) -> bool { + self.pin_guard.is_some() + } + + /// Applies channel timing and pulse density configuration. + /// + /// This is equivalent to setting the prescaler/frequency and pulse density + /// separately. + /// + /// The channel must have been successfully connected first. + pub fn apply_config(&mut self, config: ChannelConfig) -> Result<(), Error> { + let prescaler = config.timing.prescaler(self.clock_source)?; + set_prescaler_raw(CHANNEL, prescaler); + set_pulse_density_raw(CHANNEL, config.pulse_density); + Ok(()) + } + + /// Sets raw pulse density. + /// + /// The value ranges from `-128` to `127`. + /// + /// The channel must have been successfully connected first. + pub fn set_pulse_density(&mut self, density: i8) { + set_pulse_density_raw(CHANNEL, density); + } + + /// Sets duty cycle. `0` maps to the minimum density and `255` maps to the + /// maximum density. + /// + /// The channel must have been successfully connected first. + pub fn set_duty(&mut self, duty: u8) { + self.set_pulse_density(duty_to_density(duty)) + } + + /// Sets raw prescaler. + /// + /// The hardware divider range is `1..=256`. + /// + /// The channel must have been successfully connected first. + pub fn set_prescaler(&mut self, prescaler: u16) -> Result<(), Error> { + check_prescaler(prescaler)?; + set_prescaler_raw(CHANNEL, prescaler); + Ok(()) + } + + /// Sets the output frequency. + /// + /// The channel must have been successfully connected first. + pub fn set_frequency(&mut self, frequency: Rate) -> Result<(), Error> { + let prescaler = prescaler_from_frequency(frequency, self.clock_source)?; + self.set_prescaler(prescaler) + } + + /// Reads the raw hardware prescaler. + /// + /// The returned value is in the hardware divider range `1..=256`. + /// + /// The channel must have been successfully connected first. + pub fn prescaler(&mut self) -> Result { + Ok(prescaler_raw(CHANNEL)) + } + + /// Reads the raw pulse density. + /// + /// The returned value is in the hardware range `-128..=127`. + /// + /// The channel must have been successfully connected first. + pub fn pulse_density(&mut self) -> Result { + Ok(pulse_density_raw(CHANNEL)) + } +} + +impl Drop for Channel { + fn drop(&mut self) { + self.pin_guard = None; + if self.clock_guard.is_some() { + set_pulse_density_raw(CHANNEL, 0); + } + self.clock_guard = None; + } +} + +#[derive(Debug)] +struct SdmClockGuard { + _peripheral: GenericPeripheralGuard<{ Peripheral::GpioSd as u8 }>, + clock_source: ClockSource, +} + +impl SdmClockGuard { + fn new(clock_source: ClockSource) -> Result { + CLOCK_STATE.with(|state| { + if state.users == usize::MAX { + panic!("SDM clock reference counter overflowed"); + } + + if state.users == 0 { + configure_clock_source(clock_source); + state.source = Some(clock_source); + } else if state.source != Some(clock_source) { + return Err(Error::ClockSourceConflict); + } + + state.users += 1; + Ok(()) + })?; + + Ok(Self { + _peripheral: GenericPeripheralGuard::new(), + clock_source, + }) + } +} + +impl Drop for SdmClockGuard { + fn drop(&mut self) { + CLOCK_STATE.with(|state| match state.users { + 0 => panic!("SDM clock reference counter underflowed"), + 1 => { + debug_assert_eq!(state.source, Some(self.clock_source)); + state.users = 0; + state.source = None; + } + _ => { + debug_assert_eq!(state.source, Some(self.clock_source)); + state.users -= 1; + } + }); + } +} + +fn prescaler_from_frequency(frequency: Rate, clock_source: ClockSource) -> Result { + let source_frequency = clock_source.frequency().as_hz(); + let requested_frequency = frequency.as_hz(); + + if requested_frequency == 0 || requested_frequency > source_frequency { + return Err(Error::UnreachableTargetFrequency); + } + + let prescaler = + (source_frequency as u64 + requested_frequency as u64 / 2) / requested_frequency as u64; + if prescaler == 0 || prescaler > 256 { + return Err(Error::UnreachableTargetFrequency); + } + + Ok(prescaler as u16) +} + +fn check_prescaler(prescaler: u16) -> Result<(), Error> { + if (1..=256).contains(&prescaler) { + Ok(()) + } else { + Err(Error::PrescalerOutOfRange) + } +} + +const fn duty_to_density(duty: u8) -> i8 { + (duty as i16 - 128) as i8 +} + +fn configure_clock_source(clock_source: ClockSource) { + // Newer chips expose this selector in different clock-control blocks: + // - C5/C6/H2 use PCR.iomux_clk_conf.iomux_func_clk_sel, but the selector + // encodings differ by chip. + // - P4 uses HP_SYS_CLKRST.peri_clk_ctrl26.iomux_clk_src_sel, a one-bit + // XTAL/PLL_F80M selector. + // - ESP32/C3/S2/S3 use APB for this driver path and do not need a source + // selector write here. + cfg_if::cfg_if! { + if #[cfg(esp32c5)] { + crate::peripherals::PCR::regs().iomux_clk_conf().modify(|_, w| unsafe { + w.iomux_func_clk_sel() + .bits(match clock_source { + ClockSource::Xtal => 0, + ClockSource::PllF80m => 2, + }) + .iomux_func_clk_en() + .set_bit() + }); + } else if #[cfg(esp32c6)] { + crate::peripherals::PCR::regs().iomux_clk_conf().modify(|_, w| unsafe { + w.iomux_func_clk_sel() + .bits(match clock_source { + ClockSource::PllF80m => 1, + ClockSource::Xtal => 3, + }) + .iomux_func_clk_en() + .set_bit() + }); + } else if #[cfg(esp32h2)] { + crate::peripherals::PCR::regs().iomux_clk_conf().modify(|_, w| unsafe { + w.iomux_func_clk_sel() + .bits(match clock_source { + ClockSource::Xtal => 0, + ClockSource::PllF48m => 2, + }) + .iomux_func_clk_en() + .set_bit() + }); + } else if #[cfg(esp32p4)] { + crate::peripherals::HP_SYS_CLKRST::regs() + .peri_clk_ctrl26() + .modify(|_, w| { + match clock_source { + ClockSource::Xtal => w.iomux_clk_src_sel().clear_bit(), + ClockSource::PllF80m => w.iomux_clk_src_sel().set_bit(), + } + .iomux_clk_en() + .set_bit() + }); + } else { + let _ = clock_source; + } + } +} + +fn set_pulse_density_raw(channel: usize, density: i8) { + // The register layout is the same conceptually, but ESP32-C5's PAC names + // the field `sd_in`; the other supported PACs name it `in`. + let sd = GPIO_SD::regs(); + + #[cfg(esp32c5)] + sd.sigmadelta(channel) + .modify(|_, w| unsafe { w.sd_in().bits(density as _) }); + + #[cfg(not(esp32c5))] + sd.sigmadelta(channel) + .modify(|_, w| unsafe { w.in_().bits(density as _) }); +} + +fn set_prescaler_raw(channel: usize, prescaler: u16) { + // Hardware stores prescaler - 1. ESP32-C5's PAC names the field + // `sd_prescale`; the other supported PACs name it `prescale`. + let bits = (prescaler - 1) as _; + let sd = GPIO_SD::regs(); + + #[cfg(esp32c5)] + sd.sigmadelta(channel) + .modify(|_, w| unsafe { w.sd_prescale().bits(bits) }); + + #[cfg(not(esp32c5))] + sd.sigmadelta(channel) + .modify(|_, w| unsafe { w.prescale().bits(bits) }); +} + +fn pulse_density_raw(channel: usize) -> i8 { + let sd = GPIO_SD::regs(); + + #[cfg(esp32c5)] + return sd.sigmadelta(channel).read().sd_in().bits() as i8; + + #[cfg(not(esp32c5))] + return sd.sigmadelta(channel).read().in_().bits() as i8; +} + +fn prescaler_raw(channel: usize) -> u16 { + let sd = GPIO_SD::regs(); + + #[cfg(esp32c5)] + let bits = sd.sigmadelta(channel).read().sd_prescale().bits(); + + #[cfg(not(esp32c5))] + let bits = sd.sigmadelta(channel).read().prescale().bits(); + + bits as u16 + 1 +} diff --git a/esp-metadata/src/cfg/sdm.rs b/esp-metadata/src/cfg/sdm.rs new file mode 100644 index 00000000000..fcde4d1ea50 --- /dev/null +++ b/esp-metadata/src/cfg/sdm.rs @@ -0,0 +1,50 @@ +use proc_macro2::TokenStream; +use quote::format_ident; + +use crate::{cfg::GenericProperty, generate_for_each_macro, number}; + +#[derive(Clone, Debug)] +pub struct SdmChannels { + count: u32, +} + +impl<'de> serde::Deserialize<'de> for SdmChannels { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + Ok(Self { + count: u32::deserialize(deserializer)?, + }) + } +} + +impl GenericProperty for SdmChannels { + fn macros(&self) -> Option { + let channels = (0..self.count) + .map(|channel| { + let channel = number(channel); + let signal = format_ident!("GPIO_SD{channel}"); + quote::quote! { #channel, #signal } + }) + .collect::>(); + + Some(generate_for_each_macro( + "sdm_channel", + &[("channels", &channels)], + )) + } + + fn property_macro_branches(&self) -> TokenStream { + let count = number(self.count); + + quote::quote! { + ("sdm.channel_count") => { + #count + }; + ("sdm.channel_count", str) => { + stringify!(#count) + }; + } + } +} diff --git a/examples/peripheral/sdm/.cargo/config.toml b/examples/peripheral/sdm/.cargo/config.toml new file mode 100644 index 00000000000..44dacc4278b --- /dev/null +++ b/examples/peripheral/sdm/.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/peripheral/sdm/Cargo.toml b/examples/peripheral/sdm/Cargo.toml new file mode 100644 index 00000000000..ff94441bf35 --- /dev/null +++ b/examples/peripheral/sdm/Cargo.toml @@ -0,0 +1,62 @@ +[package] +name = "sdm" +version = "0.0.0" +edition = "2024" +publish = false + +[dependencies] +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"] } + +[features] +esp32 = [ + "esp-backtrace/esp32", + "esp-bootloader-esp-idf/esp32", + "esp-hal/esp32", +] +esp32c3 = [ + "esp-backtrace/esp32c3", + "esp-bootloader-esp-idf/esp32c3", + "esp-hal/esp32c3", +] +esp32c5 = [ + "esp-backtrace/esp32c5", + "esp-bootloader-esp-idf/esp32c5", + "esp-hal/esp32c5", +] +esp32c6 = [ + "esp-backtrace/esp32c6", + "esp-bootloader-esp-idf/esp32c6", + "esp-hal/esp32c6", +] +esp32h2 = [ + "esp-backtrace/esp32h2", + "esp-bootloader-esp-idf/esp32h2", + "esp-hal/esp32h2", +] +esp32p4 = [ + "esp-backtrace/esp32p4", + "esp-bootloader-esp-idf/esp32p4", + "esp-hal/esp32p4", +] +esp32s2 = [ + "esp-backtrace/esp32s2", + "esp-bootloader-esp-idf/esp32s2", + "esp-hal/esp32s2", +] +esp32s3 = [ + "esp-backtrace/esp32s3", + "esp-bootloader-esp-idf/esp32s3", + "esp-hal/esp32s3", +] + +[profile.release] +debug = true +debug-assertions = true +lto = "fat" +codegen-units = 1 diff --git a/examples/peripheral/sdm/src/main.rs b/examples/peripheral/sdm/src/main.rs new file mode 100644 index 00000000000..2600942aa13 --- /dev/null +++ b/examples/peripheral/sdm/src/main.rs @@ -0,0 +1,46 @@ +//! This example shows how to generate a sigma-delta output signal. +//! +//! The following wiring is assumed: +//! - SDM output => GPIO2 + +#![no_std] +#![no_main] + +use esp_backtrace as _; +use esp_hal::{ + delay::Delay, + main, + sdm::{ChannelConfig, Sdm}, + time::Rate, +}; +use esp_println::print; + +esp_bootloader_esp_idf::esp_app_desc!(); + +#[main] +fn main() -> ! { + esp_println::logger::init_logger_from_env(); + let peripherals = esp_hal::init(esp_hal::Config::default()); + + let sdm = Sdm::new(peripherals.GPIO_SD); + let mut channel = sdm.channel0; + channel + .connect( + peripherals.GPIO2, + ChannelConfig::default() + .with_frequency(Rate::from_khz(500)) + .with_pulse_density(0), + ) + .unwrap(); + + let delay = Delay::new(); + let mut duty = 0; + + loop { + channel.set_duty(duty); + print!("SDM duty: {duty:3}\r"); + + duty = duty.wrapping_add(1); + delay.delay_millis(100); + } +} From 6d8338dbb06d99ac217198ee92bc70e034034376 Mon Sep 17 00:00:00 2001 From: tzyt Date: Wed, 27 May 2026 12:11:13 -0500 Subject: [PATCH 03/26] changing supported to partial in support matrix --- esp-hal/README.md | 2 +- esp-metadata/devices/esp32.toml | 2 +- esp-metadata/devices/esp32c3.toml | 2 +- esp-metadata/devices/esp32c5.toml | 2 +- esp-metadata/devices/esp32c6.toml | 2 +- esp-metadata/devices/esp32h2.toml | 2 +- esp-metadata/devices/esp32p4.toml | 2 +- esp-metadata/devices/esp32s2.toml | 2 +- esp-metadata/devices/esp32s3.toml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/esp-hal/README.md b/esp-hal/README.md index 6eec34ae65a..d6709fdc1e6 100644 --- a/esp-hal/README.md +++ b/esp-hal/README.md @@ -101,7 +101,7 @@ For help getting started with this HAL, please refer to [The Rust on ESP Book] a | SDIO host | ❌ | | | | | | | ❌ | | ❌ | | SDIO slave | ❌ | | | [❌][5169] [^1] | ❌ | [❌][5417] [^1] | | ❌ | | | | SHA | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ❌ | ⚒️ | ⚒️ | -| SDM | ✔️ | | ✔️ | ✔️ | ✔️ | | ✔️ | ✔️ | ✔️ | ✔️ | +| SDM | ⚒️ | | ⚒️ | ⚒️ | ⚒️ | | ⚒️ | ⚒️ | ⚒️ | ⚒️ | | Light/deep sleep | ⚒️ | ⚒️ | ⚒️ | [❌][5165] [^1] | ⚒️ | [❌][5424] [^1] | ⚒️ | ❌ | ⚒️ | ⚒️ | | SPI master | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ⚒️ | ✔️ | ✔️ | | SPI slave | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ⚒️ | ❌ | ⚒️ | ⚒️ | diff --git a/esp-metadata/devices/esp32.toml b/esp-metadata/devices/esp32.toml index c366c767cb6..79366364fb4 100644 --- a/esp-metadata/devices/esp32.toml +++ b/esp-metadata/devices/esp32.toml @@ -835,7 +835,7 @@ support_status = "partial" algo = { sha1 = 0, sha256 = 0, sha384 = 0, sha512 = 0 } # fake mode bits, ESP32 has separate register sets [device.sdm] -support_status = "supported" +support_status = "partial" channel_count = 8 clock_sources = ["apb"] default_clock_source = "apb" diff --git a/esp-metadata/devices/esp32c3.toml b/esp-metadata/devices/esp32c3.toml index 28548000a84..e046529d03c 100644 --- a/esp-metadata/devices/esp32c3.toml +++ b/esp-metadata/devices/esp32c3.toml @@ -488,7 +488,7 @@ dma = true algo = { sha1 = 0, sha224 = 1, sha256 = 2 } [device.sdm] -support_status = "supported" +support_status = "partial" channel_count = 4 clock_sources = ["apb"] default_clock_source = "apb" diff --git a/esp-metadata/devices/esp32c5.toml b/esp-metadata/devices/esp32c5.toml index e886525162d..87f490f2774 100644 --- a/esp-metadata/devices/esp32c5.toml +++ b/esp-metadata/devices/esp32c5.toml @@ -659,7 +659,7 @@ support_status = { status = "not_supported", issue = 5164 } support_status = { status = "not_supported", issue = 884 } [device.sdm] -support_status = "supported" +support_status = "partial" channel_count = 4 clock_sources = ["xtal", "pll_f80m"] default_clock_source = "pll_f80m" diff --git a/esp-metadata/devices/esp32c6.toml b/esp-metadata/devices/esp32c6.toml index d4b955bf0d2..df4400d8ac7 100644 --- a/esp-metadata/devices/esp32c6.toml +++ b/esp-metadata/devices/esp32c6.toml @@ -694,7 +694,7 @@ dma = true algo = { sha1 = 0, sha224 = 1, sha256 = 2 } [device.sdm] -support_status = "supported" +support_status = "partial" channel_count = 4 clock_sources = ["xtal", "pll_f80m"] default_clock_source = "pll_f80m" diff --git a/esp-metadata/devices/esp32h2.toml b/esp-metadata/devices/esp32h2.toml index 91b4a215b0b..36a526f704c 100644 --- a/esp-metadata/devices/esp32h2.toml +++ b/esp-metadata/devices/esp32h2.toml @@ -592,7 +592,7 @@ dma = true algo = { sha1 = 0, sha224 = 1, sha256 = 2 } [device.sdm] -support_status = "supported" +support_status = "partial" channel_count = 4 clock_sources = ["xtal", "pll_f48m"] default_clock_source = "pll_f48m" diff --git a/esp-metadata/devices/esp32p4.toml b/esp-metadata/devices/esp32p4.toml index 847f8d4acd7..4a8024cfa3e 100644 --- a/esp-metadata/devices/esp32p4.toml +++ b/esp-metadata/devices/esp32p4.toml @@ -462,7 +462,7 @@ support_status = "not_supported" # SDIO host [device.sd_slave] support_status = "not_supported" [device.sdm] -support_status = "supported" +support_status = "partial" channel_count = 8 clock_sources = ["xtal", "pll_f80m"] default_clock_source = "pll_f80m" diff --git a/esp-metadata/devices/esp32s2.toml b/esp-metadata/devices/esp32s2.toml index c93a7ab75cd..75c0df069eb 100644 --- a/esp-metadata/devices/esp32s2.toml +++ b/esp-metadata/devices/esp32s2.toml @@ -617,7 +617,7 @@ dma = true algo = { sha1 = 0, sha224 = 1, sha256 = 2, sha384 = 3, sha512 = 4, sha512_224 = 5, sha512_256 = 6, sha512_t = 7 } [device.sdm] -support_status = "supported" +support_status = "partial" channel_count = 8 clock_sources = ["apb"] default_clock_source = "apb" diff --git a/esp-metadata/devices/esp32s3.toml b/esp-metadata/devices/esp32s3.toml index 4ccbc9be630..178630ef2d0 100644 --- a/esp-metadata/devices/esp32s3.toml +++ b/esp-metadata/devices/esp32s3.toml @@ -809,7 +809,7 @@ dma = true algo = { sha1 = 0, sha224 = 1, sha256 = 2, sha384 = 3, sha512 = 4, sha512_224 = 5, sha512_256 = 6, sha512_t = 7 } [device.sdm] -support_status = "supported" +support_status = "partial" channel_count = 8 clock_sources = ["apb"] default_clock_source = "apb" From fa129640feeadfacbbf70aea667890fe327ddb2c Mon Sep 17 00:00:00 2001 From: tzyt Date: Wed, 27 May 2026 20:53:01 -0500 Subject: [PATCH 04/26] change SDM API to typestate style --- esp-hal/src/sdm.rs | 241 +++++++++++----------------- examples/peripheral/sdm/src/main.rs | 21 +-- 2 files changed, 102 insertions(+), 160 deletions(-) diff --git a/esp-hal/src/sdm.rs b/esp-hal/src/sdm.rs index e7c76da0a5b..c962082ca1c 100644 --- a/esp-hal/src/sdm.rs +++ b/esp-hal/src/sdm.rs @@ -40,14 +40,15 @@ for_each_sdm_channel!( /// Sigma-delta peripheral. /// /// This type only owns the SDM peripheral token and exposes the hardware - /// channels. Moving individual channels out of this collection is supported. + /// channel creators. Moving individual channel creators out of this + /// collection is supported. #[derive(Debug)] #[non_exhaustive] pub struct Sdm<'d> { _instance: GPIO_SD<'d>, $( - #[doc = concat!("Channel ", stringify!($ch), ".")] - pub []: Channel<$ch>, + #[doc = concat!("Channel ", stringify!($ch), " creator.")] + pub []: ChannelCreator<$ch>, )* } @@ -66,13 +67,17 @@ for_each_sdm_channel!( Self { _instance: instance, $( - []: Channel::new(config.clock_source), + []: ChannelCreator::new(config.clock_source), )* } } } } + }; +); +for_each_sdm_channel!( + (channels $(($ch:literal, $signal:ident)),*) => { fn output_signal(channel: usize) -> OutputSignal { match channel { $( @@ -157,84 +162,6 @@ impl ClockSource { } } -/// Sigma-delta channel timing configuration. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[non_exhaustive] -pub enum Timing { - /// Derive the hardware prescaler from a requested output frequency. - Frequency(Rate), - /// Use a raw hardware prescaler. - /// - /// The hardware divider range is `1..=256`. - Prescaler(u16), -} - -impl Timing { - fn prescaler(self, clock_source: ClockSource) -> Result { - match self { - Self::Frequency(frequency) => prescaler_from_frequency(frequency, clock_source), - Self::Prescaler(prescaler) => { - check_prescaler(prescaler)?; - Ok(prescaler) - } - } - } -} - -/// Sigma-delta channel configuration. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct ChannelConfig { - /// Channel timing. - /// - /// Requested frequencies are converted to a raw hardware prescaler using - /// the SDM clock source selected in [`SdmConfig`]. - pub timing: Timing, - /// Pulse density. - /// - /// The value ranges from `-128` to `127`. - pub pulse_density: i8, -} - -impl ChannelConfig { - /// Sets the requested output frequency. - pub const fn with_frequency(mut self, frequency: Rate) -> Self { - self.timing = Timing::Frequency(frequency); - self - } - - /// Sets the raw hardware prescaler. - pub const fn with_prescaler(mut self, prescaler: u16) -> Self { - self.timing = Timing::Prescaler(prescaler); - self - } - - /// Sets the pulse density. - pub const fn with_pulse_density(mut self, density: i8) -> Self { - self.pulse_density = density; - self - } - - /// Sets the duty cycle. - /// - /// This is a convenience mapping where `0` maps to the minimum density and - /// `255` maps to the maximum density. - pub const fn with_duty(mut self, duty: u8) -> Self { - self.pulse_density = duty_to_density(duty); - self - } -} - -impl Default for ChannelConfig { - fn default() -> Self { - Self { - timing: Timing::Prescaler(1), - pulse_density: 0, - } - } -} - /// Sigma-delta configuration or runtime error. #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -260,89 +187,107 @@ impl fmt::Display for Error { impl core::error::Error for Error {} -/// A sigma-delta channel. +/// Creates a connected sigma-delta channel. /// -/// Channels are exposed by [`Sdm`]. A channel enables the SDM peripheral clock -/// when it is connected. +/// Channel creators are exposed by [`Sdm`]. Calling [`connect`](Self::connect) +/// consumes the creator and returns an active [`Channel`], which prevents +/// accidentally connecting the same channel twice. #[derive(Debug)] -pub struct Channel { +pub struct ChannelCreator { // This is copied from the Sdm-level configuration so partially moved - // channels still use the shared clock source selected at construction time. + // channel creators still use the shared clock source selected at construction time. // It is intentionally not configurable per channel. clock_source: ClockSource, - pin_guard: Option, - clock_guard: Option, + prescaler: u16, + pulse_density: i8, } -impl Channel { +impl ChannelCreator { const fn new(clock_source: ClockSource) -> Self { Self { clock_source, - pin_guard: None, - clock_guard: None, + prescaler: 1, + pulse_density: 0, } } - /// Configures this channel and connects it to an output pin. - /// - /// Reconnecting a channel replaces the previous pin connection. - pub fn connect<'d>( - &mut self, - pin: impl PeripheralOutput<'d>, - config: ChannelConfig, - ) -> Result<(), Error> { - let prescaler = config.timing.prescaler(self.clock_source)?; - self.clock_guard = Some(SdmClockGuard::new(self.clock_source)?); - set_prescaler_raw(CHANNEL, prescaler); - set_pulse_density_raw(CHANNEL, config.pulse_density); - self.reconnect(pin); - Ok(()) + const fn with_raw_config(clock_source: ClockSource, prescaler: u16, pulse_density: i8) -> Self { + Self { + clock_source, + prescaler, + pulse_density, + } } - /// Connects this channel to a new output pin without changing its channel - /// configuration. + /// Sets the requested output frequency. /// - /// This only changes the GPIO matrix route and replaces the previous pin - /// connection. - pub fn reconnect<'d>(&mut self, pin: impl PeripheralOutput<'d>) { - self.pin_guard = None; - let pin: GpioOutputSignal<'d> = pin.into(); - pin.apply_output_config(&OutputConfig::default()); - pin.set_output_enable(true); - self.pin_guard = Some(pin.connect_with_guard(output_signal(CHANNEL))); + /// The frequency is converted immediately using the SDM clock source + /// selected in [`SdmConfig`]. + pub fn with_frequency(mut self, frequency: Rate) -> Result { + self.prescaler = prescaler_from_frequency(frequency, self.clock_source)?; + Ok(self) } - /// Returns whether the channel is currently routed to an output pin. - pub fn is_connected(&self) -> bool { - self.pin_guard.is_some() + /// Sets the raw hardware prescaler. + /// + /// The hardware divider range is `1..=256`. + pub fn with_prescaler(mut self, prescaler: u16) -> Result { + check_prescaler(prescaler)?; + self.prescaler = prescaler; + Ok(self) } - /// Applies channel timing and pulse density configuration. - /// - /// This is equivalent to setting the prescaler/frequency and pulse density - /// separately. + /// Sets the pulse density. /// - /// The channel must have been successfully connected first. - pub fn apply_config(&mut self, config: ChannelConfig) -> Result<(), Error> { - let prescaler = config.timing.prescaler(self.clock_source)?; - set_prescaler_raw(CHANNEL, prescaler); - set_pulse_density_raw(CHANNEL, config.pulse_density); - Ok(()) + /// The value ranges is `-128..=127` + pub const fn with_pulse_density(mut self, density: i8) -> Self { + self.pulse_density = density; + self + } + + /// Sets duty cycle. `0` maps to the minimum density and `255` maps to the + /// maximum density. + pub const fn with_duty(mut self, duty: u8) -> Self { + self.pulse_density = duty_to_density(duty); + self + } + + /// Configures this channel and connects it to an output pin. + pub fn connect<'d>(self, pin: impl PeripheralOutput<'d>) -> Result, Error> { + let clock_guard = SdmClockGuard::new(self.clock_source)?; + set_prescaler_raw(CHANNEL, self.prescaler); + set_pulse_density_raw(CHANNEL, self.pulse_density); + + Ok(Channel { + clock_source: self.clock_source, + _pin_guard: connect_pin(CHANNEL, pin), + _clock_guard: clock_guard, + }) } +} + +/// A connected sigma-delta channel. +/// +/// Dropping a channel disconnects its output pin and releases the SDM clock +/// guard. Use [`disconnect`](Self::disconnect) to explicitly release the +/// channel and recover its [`ChannelCreator`]. +#[derive(Debug)] +pub struct Channel { + clock_source: ClockSource, + _pin_guard: PinGuard, + _clock_guard: SdmClockGuard, +} +impl Channel { /// Sets raw pulse density. /// /// The value ranges from `-128` to `127`. - /// - /// The channel must have been successfully connected first. pub fn set_pulse_density(&mut self, density: i8) { set_pulse_density_raw(CHANNEL, density); } /// Sets duty cycle. `0` maps to the minimum density and `255` maps to the /// maximum density. - /// - /// The channel must have been successfully connected first. pub fn set_duty(&mut self, duty: u8) { self.set_pulse_density(duty_to_density(duty)) } @@ -350,8 +295,6 @@ impl Channel { /// Sets raw prescaler. /// /// The hardware divider range is `1..=256`. - /// - /// The channel must have been successfully connected first. pub fn set_prescaler(&mut self, prescaler: u16) -> Result<(), Error> { check_prescaler(prescaler)?; set_prescaler_raw(CHANNEL, prescaler); @@ -359,8 +302,6 @@ impl Channel { } /// Sets the output frequency. - /// - /// The channel must have been successfully connected first. pub fn set_frequency(&mut self, frequency: Rate) -> Result<(), Error> { let prescaler = prescaler_from_frequency(frequency, self.clock_source)?; self.set_prescaler(prescaler) @@ -369,8 +310,6 @@ impl Channel { /// Reads the raw hardware prescaler. /// /// The returned value is in the hardware divider range `1..=256`. - /// - /// The channel must have been successfully connected first. pub fn prescaler(&mut self) -> Result { Ok(prescaler_raw(CHANNEL)) } @@ -378,23 +317,33 @@ impl Channel { /// Reads the raw pulse density. /// /// The returned value is in the hardware range `-128..=127`. - /// - /// The channel must have been successfully connected first. pub fn pulse_density(&mut self) -> Result { Ok(pulse_density_raw(CHANNEL)) } + + /// Disconnects this channel and returns its channel creator. + pub fn disconnect(self) -> ChannelCreator { + let clock_source = self.clock_source; + let prescaler = prescaler_raw(CHANNEL); + let pulse_density = pulse_density_raw(CHANNEL); + drop(self); + ChannelCreator::with_raw_config(clock_source, prescaler, pulse_density) + } } impl Drop for Channel { fn drop(&mut self) { - self.pin_guard = None; - if self.clock_guard.is_some() { - set_pulse_density_raw(CHANNEL, 0); - } - self.clock_guard = None; + set_pulse_density_raw(CHANNEL, 0); } } +fn connect_pin<'d>(channel: usize, pin: impl PeripheralOutput<'d>) -> PinGuard { + let pin: GpioOutputSignal<'d> = pin.into(); + pin.apply_output_config(&OutputConfig::default()); + pin.set_output_enable(true); + pin.connect_with_guard(output_signal(channel)) +} + #[derive(Debug)] struct SdmClockGuard { _peripheral: GenericPeripheralGuard<{ Peripheral::GpioSd as u8 }>, diff --git a/examples/peripheral/sdm/src/main.rs b/examples/peripheral/sdm/src/main.rs index 2600942aa13..de6c0ff37e6 100644 --- a/examples/peripheral/sdm/src/main.rs +++ b/examples/peripheral/sdm/src/main.rs @@ -7,12 +7,7 @@ #![no_main] use esp_backtrace as _; -use esp_hal::{ - delay::Delay, - main, - sdm::{ChannelConfig, Sdm}, - time::Rate, -}; +use esp_hal::{delay::Delay, main, sdm::Sdm, time::Rate}; use esp_println::print; esp_bootloader_esp_idf::esp_app_desc!(); @@ -23,14 +18,12 @@ fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); let sdm = Sdm::new(peripherals.GPIO_SD); - let mut channel = sdm.channel0; - channel - .connect( - peripherals.GPIO2, - ChannelConfig::default() - .with_frequency(Rate::from_khz(500)) - .with_pulse_density(0), - ) + let mut channel = sdm + .channel0 + .with_frequency(Rate::from_khz(500)) + .unwrap() + .with_pulse_density(0) + .connect(peripherals.GPIO2) .unwrap(); let delay = Delay::new(); From 5b4f2723fdb85ccff1dcc4df9991622d9a504b59 Mon Sep 17 00:00:00 2001 From: ttzytt Date: Sun, 31 May 2026 11:45:48 -0700 Subject: [PATCH 05/26] Refine SDM channel configuration API, add apply_config back --- esp-hal/src/sdm.rs | 258 +++++++++++++++------------- examples/peripheral/sdm/src/main.rs | 9 +- 2 files changed, 143 insertions(+), 124 deletions(-) diff --git a/esp-hal/src/sdm.rs b/esp-hal/src/sdm.rs index c962082ca1c..525e22bfc0a 100644 --- a/esp-hal/src/sdm.rs +++ b/esp-hal/src/sdm.rs @@ -46,6 +46,7 @@ for_each_sdm_channel!( #[non_exhaustive] pub struct Sdm<'d> { _instance: GPIO_SD<'d>, + clock_source: ClockSource, $( #[doc = concat!("Channel ", stringify!($ch), " creator.")] pub []: ChannelCreator<$ch>, @@ -66,11 +67,18 @@ for_each_sdm_channel!( pub fn new_with_config(instance: GPIO_SD<'d>, config: SdmConfig) -> Self { Self { _instance: instance, + clock_source: config.clock_source, $( []: ChannelCreator::new(config.clock_source), )* } } + + /// Creates a channel configuration builder using this peripheral's + /// clock source. + pub const fn channel_config(&self) -> ChannelConfigBuilder { + ChannelConfigBuilder::new(self.clock_source) + } } } }; @@ -171,7 +179,7 @@ pub enum Error { UnreachableTargetFrequency, /// Another SDM channel is already using a different clock source. ClockSourceConflict, - /// The raw prescaler is outside the hardware range. + /// The prescaler is outside the supported range. PrescalerOutOfRange, } @@ -187,59 +195,32 @@ impl fmt::Display for Error { impl core::error::Error for Error {} -/// Creates a connected sigma-delta channel. +/// Sigma-delta channel configuration. /// -/// Channel creators are exposed by [`Sdm`]. Calling [`connect`](Self::connect) -/// consumes the creator and returns an active [`Channel`], which prevents -/// accidentally connecting the same channel twice. -#[derive(Debug)] -pub struct ChannelCreator { - // This is copied from the Sdm-level configuration so partially moved - // channel creators still use the shared clock source selected at construction time. - // It is intentionally not configurable per channel. - clock_source: ClockSource, - prescaler: u16, +/// The hardware stores the prescaler and pulse density in the same register, +/// so applying a complete channel configuration can update both fields with a +/// single register write. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub struct ChannelConfig { + raw_prescaler: u8, pulse_density: i8, } -impl ChannelCreator { - const fn new(clock_source: ClockSource) -> Self { - Self { - clock_source, - prescaler: 1, - pulse_density: 0, - } - } - - const fn with_raw_config(clock_source: ClockSource, prescaler: u16, pulse_density: i8) -> Self { - Self { - clock_source, - prescaler, - pulse_density, - } - } - - /// Sets the requested output frequency. - /// - /// The frequency is converted immediately using the SDM clock source - /// selected in [`SdmConfig`]. - pub fn with_frequency(mut self, frequency: Rate) -> Result { - self.prescaler = prescaler_from_frequency(frequency, self.clock_source)?; - Ok(self) - } - - /// Sets the raw hardware prescaler. +impl ChannelConfig { + /// Sets the hardware prescaler. /// /// The hardware divider range is `1..=256`. pub fn with_prescaler(mut self, prescaler: u16) -> Result { check_prescaler(prescaler)?; - self.prescaler = prescaler; + self.raw_prescaler = raw_prescaler(prescaler); Ok(self) } /// Sets the pulse density. /// - /// The value ranges is `-128..=127` + /// The value ranges is `-128..=127`. pub const fn with_pulse_density(mut self, density: i8) -> Self { self.pulse_density = density; self @@ -251,15 +232,92 @@ impl ChannelCreator { self.pulse_density = duty_to_density(duty); self } +} + +/// Builds a sigma-delta channel configuration. +/// +/// This builder carries the peripheral-wide clock source so frequencies can be +/// converted to raw prescaler values without storing clock selection in the +/// channel configuration itself. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub struct ChannelConfigBuilder { + clock_source: ClockSource, + config: ChannelConfig, +} + +impl ChannelConfigBuilder { + const fn new(clock_source: ClockSource) -> Self { + Self { + clock_source, + config: ChannelConfig { + raw_prescaler: 0, + pulse_density: 0, + }, + } + } + + /// Sets the requested output frequency. + pub fn with_frequency(mut self, frequency: Rate) -> Result { + self.config.raw_prescaler = + raw_prescaler(prescaler_from_frequency(frequency, self.clock_source)?); + Ok(self.config) + } + + /// Sets the hardware prescaler. + /// + /// The hardware divider range is `1..=256`. + pub fn with_prescaler(mut self, prescaler: u16) -> Result { + self.config = self.config.with_prescaler(prescaler)?; + Ok(self.config) + } + + /// Returns the default channel configuration. + pub const fn build(self) -> ChannelConfig { + self.config + } +} + +impl Default for ChannelConfig { + fn default() -> Self { + Self { + raw_prescaler: 0, + pulse_density: 0, + } + } +} + +/// Creates a connected sigma-delta channel. +/// +/// Channel creators are exposed by [`Sdm`]. Calling [`connect`](Self::connect) +/// consumes the creator and returns an active [`Channel`], which prevents +/// accidentally connecting the same channel twice. +#[derive(Debug)] +pub struct ChannelCreator { + // This is copied from the Sdm-level configuration so partially moved + // channel creators still use the shared clock source selected at construction time. + // It is intentionally not configurable per channel. + clock_source: ClockSource, +} + +impl ChannelCreator { + const fn new(clock_source: ClockSource) -> Self { + Self { clock_source } + } /// Configures this channel and connects it to an output pin. - pub fn connect<'d>(self, pin: impl PeripheralOutput<'d>) -> Result, Error> { + pub fn connect<'d>( + self, + pin: impl PeripheralOutput<'d>, + config: ChannelConfig, + ) -> Result, Error> { let clock_guard = SdmClockGuard::new(self.clock_source)?; - set_prescaler_raw(CHANNEL, self.prescaler); - set_pulse_density_raw(CHANNEL, self.pulse_density); + write_config_raw(CHANNEL, config); Ok(Channel { clock_source: self.clock_source, + config, _pin_guard: connect_pin(CHANNEL, pin), _clock_guard: clock_guard, }) @@ -274,16 +332,24 @@ impl ChannelCreator { #[derive(Debug)] pub struct Channel { clock_source: ClockSource, + config: ChannelConfig, _pin_guard: PinGuard, _clock_guard: SdmClockGuard, } impl Channel { + /// Applies a new channel configuration. + pub fn apply_config(&mut self, config: &ChannelConfig) { + write_config_raw(CHANNEL, *config); + self.config = *config; + } + /// Sets raw pulse density. /// /// The value ranges from `-128` to `127`. pub fn set_pulse_density(&mut self, density: i8) { - set_pulse_density_raw(CHANNEL, density); + let config = self.config.with_pulse_density(density); + self.apply_config(&config); } /// Sets duty cycle. `0` maps to the minimum density and `255` maps to the @@ -292,48 +358,25 @@ impl Channel { self.set_pulse_density(duty_to_density(duty)) } - /// Sets raw prescaler. - /// - /// The hardware divider range is `1..=256`. - pub fn set_prescaler(&mut self, prescaler: u16) -> Result<(), Error> { - check_prescaler(prescaler)?; - set_prescaler_raw(CHANNEL, prescaler); - Ok(()) - } - - /// Sets the output frequency. - pub fn set_frequency(&mut self, frequency: Rate) -> Result<(), Error> { - let prescaler = prescaler_from_frequency(frequency, self.clock_source)?; - self.set_prescaler(prescaler) - } - - /// Reads the raw hardware prescaler. + /// Reads the hardware prescaler. /// /// The returned value is in the hardware divider range `1..=256`. - pub fn prescaler(&mut self) -> Result { - Ok(prescaler_raw(CHANNEL)) + pub fn prescaler(&self) -> u16 { + self.config.raw_prescaler as u16 + 1 } /// Reads the raw pulse density. /// /// The returned value is in the hardware range `-128..=127`. - pub fn pulse_density(&mut self) -> Result { - Ok(pulse_density_raw(CHANNEL)) + pub fn pulse_density(&self) -> i8 { + self.config.pulse_density } /// Disconnects this channel and returns its channel creator. pub fn disconnect(self) -> ChannelCreator { let clock_source = self.clock_source; - let prescaler = prescaler_raw(CHANNEL); - let pulse_density = pulse_density_raw(CHANNEL); drop(self); - ChannelCreator::with_raw_config(clock_source, prescaler, pulse_density) - } -} - -impl Drop for Channel { - fn drop(&mut self) { - set_pulse_density_raw(CHANNEL, 0); + ChannelCreator::new(clock_source) } } @@ -417,8 +460,12 @@ fn check_prescaler(prescaler: u16) -> Result<(), Error> { } } +fn raw_prescaler(prescaler: u16) -> u8 { + (prescaler - 1) as u8 +} + const fn duty_to_density(duty: u8) -> i8 { - (duty as i16 - 128) as i8 + duty.wrapping_sub(128) as i8 } fn configure_clock_source(clock_source: ClockSource) { @@ -477,53 +524,26 @@ fn configure_clock_source(clock_source: ClockSource) { } } -fn set_pulse_density_raw(channel: usize, density: i8) { - // The register layout is the same conceptually, but ESP32-C5's PAC names - // the field `sd_in`; the other supported PACs name it `in`. +fn write_config_raw(channel: usize, config: ChannelConfig) { + // ESP32-C5's PAC names these fields `sd_in`/`sd_prescale`; the other + // supported PACs name them `in`/`prescale`. + let prescaler = config.raw_prescaler as _; + let density = config.pulse_density as _; let sd = GPIO_SD::regs(); #[cfg(esp32c5)] - sd.sigmadelta(channel) - .modify(|_, w| unsafe { w.sd_in().bits(density as _) }); + sd.sigmadelta(channel).write(|w| unsafe { + w.sd_in() + .bits(density) + .sd_prescale() + .bits(prescaler) + }); #[cfg(not(esp32c5))] - sd.sigmadelta(channel) - .modify(|_, w| unsafe { w.in_().bits(density as _) }); -} - -fn set_prescaler_raw(channel: usize, prescaler: u16) { - // Hardware stores prescaler - 1. ESP32-C5's PAC names the field - // `sd_prescale`; the other supported PACs name it `prescale`. - let bits = (prescaler - 1) as _; - let sd = GPIO_SD::regs(); - - #[cfg(esp32c5)] - sd.sigmadelta(channel) - .modify(|_, w| unsafe { w.sd_prescale().bits(bits) }); - - #[cfg(not(esp32c5))] - sd.sigmadelta(channel) - .modify(|_, w| unsafe { w.prescale().bits(bits) }); -} - -fn pulse_density_raw(channel: usize) -> i8 { - let sd = GPIO_SD::regs(); - - #[cfg(esp32c5)] - return sd.sigmadelta(channel).read().sd_in().bits() as i8; - - #[cfg(not(esp32c5))] - return sd.sigmadelta(channel).read().in_().bits() as i8; -} - -fn prescaler_raw(channel: usize) -> u16 { - let sd = GPIO_SD::regs(); - - #[cfg(esp32c5)] - let bits = sd.sigmadelta(channel).read().sd_prescale().bits(); - - #[cfg(not(esp32c5))] - let bits = sd.sigmadelta(channel).read().prescale().bits(); - - bits as u16 + 1 + sd.sigmadelta(channel).write(|w| unsafe { + w.in_() + .bits(density) + .prescale() + .bits(prescaler) + }); } diff --git a/examples/peripheral/sdm/src/main.rs b/examples/peripheral/sdm/src/main.rs index de6c0ff37e6..2368b319043 100644 --- a/examples/peripheral/sdm/src/main.rs +++ b/examples/peripheral/sdm/src/main.rs @@ -18,13 +18,12 @@ fn main() -> ! { let peripherals = esp_hal::init(esp_hal::Config::default()); let sdm = Sdm::new(peripherals.GPIO_SD); - let mut channel = sdm - .channel0 + let config = sdm + .channel_config() .with_frequency(Rate::from_khz(500)) .unwrap() - .with_pulse_density(0) - .connect(peripherals.GPIO2) - .unwrap(); + .with_pulse_density(0); + let mut channel = sdm.channel0.connect(peripherals.GPIO2, config).unwrap(); let delay = Delay::new(); let mut duty = 0; From 658e0b78f82b947a038588665c2394ff8c40d6ff Mon Sep 17 00:00:00 2001 From: ttzytt Date: Sun, 31 May 2026 22:21:09 -0700 Subject: [PATCH 06/26] remove new_with_config --- esp-hal/src/sdm.rs | 8 +------- examples/peripheral/sdm/src/main.rs | 9 +++++++-- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/esp-hal/src/sdm.rs b/esp-hal/src/sdm.rs index 525e22bfc0a..927a2397662 100644 --- a/esp-hal/src/sdm.rs +++ b/esp-hal/src/sdm.rs @@ -55,16 +55,10 @@ for_each_sdm_channel!( impl<'d> Sdm<'d> { /// Creates a new sigma-delta peripheral driver. - pub fn new(instance: GPIO_SD<'d>) -> Self { - Self::new_with_config(instance, SdmConfig::default()) - } - - /// Creates a new sigma-delta peripheral driver with the given - /// peripheral-wide configuration. /// /// The SDM clock source is shared by all SDM channels through the IO_MUX /// clock, so it is selected here instead of being configurable per channel. - pub fn new_with_config(instance: GPIO_SD<'d>, config: SdmConfig) -> Self { + pub fn new(instance: GPIO_SD<'d>, config: SdmConfig) -> Self { Self { _instance: instance, clock_source: config.clock_source, diff --git a/examples/peripheral/sdm/src/main.rs b/examples/peripheral/sdm/src/main.rs index 2368b319043..4faac02e6df 100644 --- a/examples/peripheral/sdm/src/main.rs +++ b/examples/peripheral/sdm/src/main.rs @@ -7,7 +7,12 @@ #![no_main] use esp_backtrace as _; -use esp_hal::{delay::Delay, main, sdm::Sdm, time::Rate}; +use esp_hal::{ + delay::Delay, + main, + sdm::{Sdm, SdmConfig}, + time::Rate, +}; use esp_println::print; esp_bootloader_esp_idf::esp_app_desc!(); @@ -17,7 +22,7 @@ fn main() -> ! { esp_println::logger::init_logger_from_env(); let peripherals = esp_hal::init(esp_hal::Config::default()); - let sdm = Sdm::new(peripherals.GPIO_SD); + let sdm = Sdm::new(peripherals.GPIO_SD, SdmConfig::default()); let config = sdm .channel_config() .with_frequency(Rate::from_khz(500)) From 7a317ef0ece823071456e14d02bde3ce05015f42 Mon Sep 17 00:00:00 2001 From: ttzytt Date: Tue, 2 Jun 2026 15:53:52 -0500 Subject: [PATCH 07/26] add HIL test for SDM --- hil-test/Cargo.toml | 5 + hil-test/src/bin/sdm.rs | 334 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 339 insertions(+) create mode 100644 hil-test/src/bin/sdm.rs diff --git a/hil-test/Cargo.toml b/hil-test/Cargo.toml index 74e8119de0b..8f00f3c14f6 100644 --- a/hil-test/Cargo.toml +++ b/hil-test/Cargo.toml @@ -66,6 +66,11 @@ name = "rmt" harness = false required-features = ["embassy", "esp-alloc"] +[[bin]] +name = "sdm" +harness = false +required-features = ["unstable"] + [[bin]] name = "spi_full_duplex" harness = false diff --git a/hil-test/src/bin/sdm.rs b/hil-test/src/bin/sdm.rs new file mode 100644 index 00000000000..608d8ab7dcb --- /dev/null +++ b/hil-test/src/bin/sdm.rs @@ -0,0 +1,334 @@ +//! SDM Test +//! +//! Uses the common test pins documented in `hil-test/README.md`: +//! - SDM output => first common test pin +//! - RMT RX => second common test pin + +//% CHIPS: esp32 esp32c3 esp32c5 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: unstable + +#![no_std] +#![no_main] + +use esp_hal::{ + Blocking, + delay::Delay, + gpio::{AnyPin, Level, Output, OutputConfig, interconnect::PeripheralInput}, + peripherals::{GPIO_SD, RMT}, + rmt::{CHANNEL_RAM_SIZE, Channel, PulseCode, Rmt, Rx, RxChannelConfig, RxChannelCreator}, + sdm::{Sdm, SdmConfig}, + time::Rate, +}; +use hil_test as _; + +cfg_if::cfg_if! { + if #[cfg(esp32h2)] { + const RMT_FREQUENCY: Rate = Rate::from_mhz(32); + // default clock source for h2 is 32Mhz + const RMT_DIVIDER: u8 = 32; + } else { + const RMT_FREQUENCY: Rate = Rate::from_mhz(80); + // default clock source for other models is 80Mhz + const RMT_DIVIDER: u8 = 80; + } + // make sure that all models uses 1 Mhz clock +} + +const SDM_FREQUENCY: Rate = Rate::from_khz(500); +const SDM_WARM_UP_US: u32 = 1_000; +const SAMPLE_TIME_US: u32 = 200; +const RMT_IDLE_THRESHOLD: u16 = 100; +const RX_LEN: usize = CHANNEL_RAM_SIZE; +const DEBUG_WAVE_MAX_CHARS: usize = 200; +const DUTY_MIN: u8 = 10; +const DUTY_MAX: u8 = 245; +// recommended density is [-90, 90] +// ref: https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/peripherals/sdm.html +const DUTY_STEP: u8 = 20; +const DUTY_RATIO_TOLERANCE_PER_MILLE: u32 = 20; + +cfg_if::cfg_if! { + if #[cfg(any(esp32, esp32s3))] { + macro_rules! rx_channel_creator { + ($rmt:expr) => { + $rmt.channel4 + }; + } + } else { + macro_rules! rx_channel_creator { + ($rmt:expr) => { + $rmt.channel2 + }; + } + } +} + +struct Context { + gpio_sd: GPIO_SD<'static>, + rmt: RMT<'static>, + sdm_pin: AnyPin<'static>, + rmt_pin: AnyPin<'static>, +} + +struct PulseDebug(PulseCode); + +impl core::fmt::Debug for PulseDebug { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let code = self.0; + let level1 = if code.level1() == Level::High { + 'H' + } else { + 'L' + }; + let level2 = if code.level2() == Level::High { + 'H' + } else { + 'L' + }; + + write!(f, "{level1}{}/{level2}{}", code.length1(), code.length2()) + } +} + +struct PulseCodesDebug<'a>(&'a [PulseCode]); + +impl core::fmt::Debug for PulseCodesDebug<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut list = f.debug_list(); + + for code in self.0 { + list.entry(&PulseDebug(*code)); + } + + list.finish() + } +} + +struct PulseWaveDebug<'a>(&'a [PulseCode]); + +impl core::fmt::Display for PulseWaveDebug<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut written = 0; + + for code in self.0 { + for (level, length) in [ + (code.level1(), code.length1()), + (code.level2(), code.length2()), + ] { + if length == 0 { + continue; + } + + let symbol = if level == Level::High { '^' } else { '_' }; + let width = usize::from(length); + + for _ in 0..width { + if written >= DEBUG_WAVE_MAX_CHARS { + return f.write_str("..."); + } + + written += 1; + write!(f, "{symbol}")?; + } + } + } + + Ok(()) + } +} + +struct Measurement { + ratio: u32, + count: usize, + pulses: [PulseCode; RX_LEN], +} + +impl Measurement { + fn new(data: &[PulseCode; RX_LEN], count: usize) -> Self { + Self { + ratio: high_ratio_per_mille(data, count), + count, + pulses: *data, + } + } + + fn valid_pulses(&self) -> &[PulseCode] { + &self.pulses[..self.count.min(RX_LEN)] + } +} + +fn configure_rx_channel<'a>( + rmt: Rmt<'a, Blocking>, + pin: impl PeripheralInput<'a>, +) -> Channel<'a, Blocking, Rx> { + rx_channel_creator!(rmt) + .configure_rx( + &RxChannelConfig::default() + .with_clk_divider(RMT_DIVIDER) + .with_idle_threshold(RMT_IDLE_THRESHOLD), + ) + .unwrap() + .with_pin(pin) +} + +fn high_ratio_per_mille(data: &[PulseCode], count: usize) -> u32 { + let mut high = 0_u32; + let mut low = 0_u32; + let mut remaining = SAMPLE_TIME_US; + + for code in data.iter().take(count) { + if code.length1() != 0 { + let length = u32::from(code.length1()).min(remaining); + + if code.level1() == Level::High { + high += length; + } else { + low += length; + } + + remaining -= length; + if remaining == 0 { + break; + } + } + + if code.length2() != 0 { + let length = u32::from(code.length2()).min(remaining); + + if code.level2() == Level::High { + high += length; + } else { + low += length; + } + + remaining -= length; + if remaining == 0 { + break; + } + } + } + + assert!(high + low > 0, "RMT did not capture any SDM pulse duration"); + high * 1_000 / (high + low) +} + +fn expected_ratio_per_mille(duty: u8) -> u32 { + (u32::from(duty) * 1_000 + 128) / 256 +} + +fn measure_high_ratio(ctx: &mut Context, duty: u8) -> Measurement { + let sdm = Sdm::new(ctx.gpio_sd.reborrow(), SdmConfig::default()); + let config = sdm + .channel_config() + .with_frequency(SDM_FREQUENCY) + .unwrap() + .with_duty(duty); + let channel = sdm + .channel0 + .connect(ctx.sdm_pin.reborrow(), config) + .unwrap(); + + Delay::new().delay_micros(SDM_WARM_UP_US); + + let rmt = Rmt::new(ctx.rmt.reborrow(), RMT_FREQUENCY).unwrap(); + let rx_channel = configure_rx_channel(rmt, ctx.rmt_pin.reborrow()); + let mut rx_data = [PulseCode::end_marker(); RX_LEN]; + let rx_transaction = rx_channel.receive(&mut rx_data).unwrap(); + + Delay::new().delay_micros(SAMPLE_TIME_US); + + let _creator = channel.disconnect(); + let mut output = Output::new(ctx.sdm_pin.reborrow(), Level::Low, OutputConfig::default()); + output.set_low(); + // send idle signal so that RMT will stop recording pulses + + let (count, _rx_channel) = rx_transaction.wait().unwrap(); + + Measurement::new(&rx_data, count) +} + +#[embedded_test::tests(default_timeout = 10)] +mod tests { + use hil_test::assert; + + use super::*; + + #[init] + fn init() -> Context { + let peripherals = esp_hal::init(esp_hal::Config::default()); + let (sdm_pin, rmt_pin) = hil_test::common_test_pins!(peripherals); + + Context { + gpio_sd: peripherals.GPIO_SD, + rmt: peripherals.RMT, + sdm_pin: AnyPin::from(sdm_pin), + rmt_pin: AnyPin::from(rmt_pin), + } + } + + #[test] + fn duties_have_expected_high_ratios(mut ctx: Context) { + let mut duty = DUTY_MIN; + + loop { + let measurement = measure_high_ratio(&mut ctx, duty); + let expected = expected_ratio_per_mille(duty); + let error = measurement.ratio.abs_diff(expected); + let pulses = measurement.valid_pulses(); + + assert!( + error <= DUTY_RATIO_TOLERANCE_PER_MILLE, + "expected duty {} high ratio to be {} +/- {} per mille, got {}; count={}; pulses={:?}; wave={}", + duty, + expected, + DUTY_RATIO_TOLERANCE_PER_MILLE, + measurement.ratio, + measurement.count, + PulseCodesDebug(pulses), + PulseWaveDebug(pulses), + ); + + if duty == DUTY_MAX { + break; + } + + duty = duty.saturating_add(DUTY_STEP).min(DUTY_MAX); + } + } + + #[test] + fn higher_duties_have_higher_high_ratios(mut ctx: Context) { + let mut previous_duty = DUTY_MIN; + let mut previous = measure_high_ratio(&mut ctx, previous_duty); + let mut duty = previous_duty.saturating_add(DUTY_STEP).min(DUTY_MAX); + + while duty != previous_duty { + let current = measure_high_ratio(&mut ctx, duty); + let previous_pulses = previous.valid_pulses(); + let current_pulses = current.valid_pulses(); + + assert!( + current.ratio > previous.ratio, + "expected duty {} high ratio ({}) to be higher than duty {} ({}); current_count={}; current_pulses={:?}; current_wave={}; previous_count={}; previous_pulses={:?}; previous_wave={}", + duty, + current.ratio, + previous_duty, + previous.ratio, + current.count, + PulseCodesDebug(current_pulses), + PulseWaveDebug(current_pulses), + previous.count, + PulseCodesDebug(previous_pulses), + PulseWaveDebug(previous_pulses), + ); + + if duty == DUTY_MAX { + break; + } + + previous_duty = duty; + previous = current; + duty = duty.saturating_add(DUTY_STEP).min(DUTY_MAX); + } + } +} From a209330b9384650aa65266dab3d3af57caecacb9 Mon Sep 17 00:00:00 2001 From: ttzytt Date: Tue, 2 Jun 2026 16:28:54 -0500 Subject: [PATCH 08/26] move the example from /example to module-level rustdoc; lint and format --- esp-hal/Cargo.toml | 2 +- esp-hal/src/sdm.rs | 96 +++++++++++++---------------- examples/peripheral/sdm/Cargo.toml | 62 ------------------- examples/peripheral/sdm/src/main.rs | 43 ------------- 4 files changed, 44 insertions(+), 159 deletions(-) delete mode 100644 examples/peripheral/sdm/Cargo.toml delete mode 100644 examples/peripheral/sdm/src/main.rs diff --git a/esp-hal/Cargo.toml b/esp-hal/Cargo.toml index 73fd5eb3e81..fe9dd659e67 100644 --- a/esp-hal/Cargo.toml +++ b/esp-hal/Cargo.toml @@ -347,4 +347,4 @@ requires-unstable = [] mixed_attributes_style = "allow" [lints.rust] -unexpected_cfgs = { level = "warn", check-cfg = ['cfg(host_os, values("windows"))'] } +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(host_os, values("windows"))'] } \ No newline at end of file diff --git a/esp-hal/src/sdm.rs b/esp-hal/src/sdm.rs index 927a2397662..4b2d24737eb 100644 --- a/esp-hal/src/sdm.rs +++ b/esp-hal/src/sdm.rs @@ -1,8 +1,34 @@ +#![cfg_attr(docsrs, procmacros::doc_replace)] + //! Sigma-delta modulation peripheral. //! //! The sigma-delta modulator produces a pulse-density modulated output on a //! GPIO matrix signal. Each channel can be configured with a carrier frequency //! and pulse density, then routed to one output pin. +//! +//! ## Examples +//! +//! Generate a sigma-delta output signal on a GPIO pin. +//! +//! ```rust, no_run +//! # {before_snippet} +//! use esp_hal::{ +//! sdm::{Sdm, SdmConfig}, +//! time::Rate, +//! }; +//! +//! let sdm = Sdm::new(peripherals.GPIO_SD, SdmConfig::default()); +//! let config = sdm +//! .channel_config() +//! .with_frequency(Rate::from_khz(500))? +//! .with_duty(128); +//! let mut channel = sdm.channel0.connect(peripherals.GPIO2, config)?; +//! +//! channel.set_duty(192); // duty ranges from 0 to 255 +//! channel.set_pulse_density(0); // pulse density ranges from -128 to 127 +//! // +//! # {after_snippet} +//! ``` use core::fmt; @@ -10,7 +36,9 @@ use esp_sync::NonReentrantMutex; use crate::{ gpio::{ - OutputConfig, OutputSignal, PinGuard, + OutputConfig, + OutputSignal, + PinGuard, interconnect::{OutputSignal as GpioOutputSignal, PeripheralOutput}, }, peripherals::GPIO_SD, @@ -92,7 +120,7 @@ for_each_sdm_channel!( ); /// Sigma-delta peripheral configuration. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub struct SdmConfig { @@ -108,47 +136,28 @@ impl SdmConfig { } } -impl Default for SdmConfig { - fn default() -> Self { - Self { - clock_source: ClockSource::default(), - } - } -} - /// Source clock for the shared SDM/IO_MUX clock. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum ClockSource { /// APB clock. #[cfg(any(esp32, esp32c3, esp32s2, esp32s3))] + #[default] Apb, /// XTAL clock. #[cfg(any(esp32c5, esp32c6, esp32h2, esp32p4))] Xtal, /// Fixed 80 MHz PLL clock. #[cfg(any(esp32c5, esp32c6, esp32p4))] + #[cfg_attr(any(esp32c5, esp32c6, esp32p4), default)] PllF80m, /// Fixed 48 MHz PLL clock. #[cfg(esp32h2)] + #[cfg_attr(esp32h2, default)] PllF48m, } -impl Default for ClockSource { - fn default() -> Self { - cfg_if::cfg_if! { - if #[cfg(any(esp32, esp32c3, esp32s2, esp32s3))] { - Self::Apb - } else if #[cfg(any(esp32c5, esp32c6, esp32p4))] { - Self::PllF80m - } else if #[cfg(esp32h2)] { - Self::PllF48m - } - } - } -} - impl ClockSource { fn frequency(self) -> Rate { match self { @@ -194,7 +203,7 @@ impl core::error::Error for Error {} /// The hardware stores the prescaler and pulse density in the same register, /// so applying a complete channel configuration can update both fields with a /// single register write. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub struct ChannelConfig { @@ -273,15 +282,6 @@ impl ChannelConfigBuilder { } } -impl Default for ChannelConfig { - fn default() -> Self { - Self { - raw_prescaler: 0, - pulse_density: 0, - } - } -} - /// Creates a connected sigma-delta channel. /// /// Channel creators are exposed by [`Sdm`]. Calling [`connect`](Self::connect) @@ -464,12 +464,10 @@ const fn duty_to_density(duty: u8) -> i8 { fn configure_clock_source(clock_source: ClockSource) { // Newer chips expose this selector in different clock-control blocks: - // - C5/C6/H2 use PCR.iomux_clk_conf.iomux_func_clk_sel, but the selector - // encodings differ by chip. - // - P4 uses HP_SYS_CLKRST.peri_clk_ctrl26.iomux_clk_src_sel, a one-bit - // XTAL/PLL_F80M selector. - // - ESP32/C3/S2/S3 use APB for this driver path and do not need a source - // selector write here. + // - C5/C6/H2 use PCR.iomux_clk_conf.iomux_func_clk_sel, but the selector encodings differ by + // chip. + // - P4 uses HP_SYS_CLKRST.peri_clk_ctrl26.iomux_clk_src_sel, a one-bit XTAL/PLL_F80M selector. + // - ESP32/C3/S2/S3 use APB for this driver path and do not need a source selector write here. cfg_if::cfg_if! { if #[cfg(esp32c5)] { crate::peripherals::PCR::regs().iomux_clk_conf().modify(|_, w| unsafe { @@ -526,18 +524,10 @@ fn write_config_raw(channel: usize, config: ChannelConfig) { let sd = GPIO_SD::regs(); #[cfg(esp32c5)] - sd.sigmadelta(channel).write(|w| unsafe { - w.sd_in() - .bits(density) - .sd_prescale() - .bits(prescaler) - }); + sd.sigmadelta(channel) + .write(|w| unsafe { w.sd_in().bits(density).sd_prescale().bits(prescaler) }); #[cfg(not(esp32c5))] - sd.sigmadelta(channel).write(|w| unsafe { - w.in_() - .bits(density) - .prescale() - .bits(prescaler) - }); + sd.sigmadelta(channel) + .write(|w| unsafe { w.in_().bits(density).prescale().bits(prescaler) }); } diff --git a/examples/peripheral/sdm/Cargo.toml b/examples/peripheral/sdm/Cargo.toml deleted file mode 100644 index ff94441bf35..00000000000 --- a/examples/peripheral/sdm/Cargo.toml +++ /dev/null @@ -1,62 +0,0 @@ -[package] -name = "sdm" -version = "0.0.0" -edition = "2024" -publish = false - -[dependencies] -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"] } - -[features] -esp32 = [ - "esp-backtrace/esp32", - "esp-bootloader-esp-idf/esp32", - "esp-hal/esp32", -] -esp32c3 = [ - "esp-backtrace/esp32c3", - "esp-bootloader-esp-idf/esp32c3", - "esp-hal/esp32c3", -] -esp32c5 = [ - "esp-backtrace/esp32c5", - "esp-bootloader-esp-idf/esp32c5", - "esp-hal/esp32c5", -] -esp32c6 = [ - "esp-backtrace/esp32c6", - "esp-bootloader-esp-idf/esp32c6", - "esp-hal/esp32c6", -] -esp32h2 = [ - "esp-backtrace/esp32h2", - "esp-bootloader-esp-idf/esp32h2", - "esp-hal/esp32h2", -] -esp32p4 = [ - "esp-backtrace/esp32p4", - "esp-bootloader-esp-idf/esp32p4", - "esp-hal/esp32p4", -] -esp32s2 = [ - "esp-backtrace/esp32s2", - "esp-bootloader-esp-idf/esp32s2", - "esp-hal/esp32s2", -] -esp32s3 = [ - "esp-backtrace/esp32s3", - "esp-bootloader-esp-idf/esp32s3", - "esp-hal/esp32s3", -] - -[profile.release] -debug = true -debug-assertions = true -lto = "fat" -codegen-units = 1 diff --git a/examples/peripheral/sdm/src/main.rs b/examples/peripheral/sdm/src/main.rs deleted file mode 100644 index 4faac02e6df..00000000000 --- a/examples/peripheral/sdm/src/main.rs +++ /dev/null @@ -1,43 +0,0 @@ -//! This example shows how to generate a sigma-delta output signal. -//! -//! The following wiring is assumed: -//! - SDM output => GPIO2 - -#![no_std] -#![no_main] - -use esp_backtrace as _; -use esp_hal::{ - delay::Delay, - main, - sdm::{Sdm, SdmConfig}, - time::Rate, -}; -use esp_println::print; - -esp_bootloader_esp_idf::esp_app_desc!(); - -#[main] -fn main() -> ! { - esp_println::logger::init_logger_from_env(); - let peripherals = esp_hal::init(esp_hal::Config::default()); - - let sdm = Sdm::new(peripherals.GPIO_SD, SdmConfig::default()); - let config = sdm - .channel_config() - .with_frequency(Rate::from_khz(500)) - .unwrap() - .with_pulse_density(0); - let mut channel = sdm.channel0.connect(peripherals.GPIO2, config).unwrap(); - - let delay = Delay::new(); - let mut duty = 0; - - loop { - channel.set_duty(duty); - print!("SDM duty: {duty:3}\r"); - - duty = duty.wrapping_add(1); - delay.delay_millis(100); - } -} From 53cca28f9f4ee218fb8de1c2b221853f94150074 Mon Sep 17 00:00:00 2001 From: ttzytt Date: Tue, 2 Jun 2026 16:44:36 -0500 Subject: [PATCH 09/26] stop cacheing config in SDM channels --- esp-hal/src/sdm.rs | 47 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/esp-hal/src/sdm.rs b/esp-hal/src/sdm.rs index 4b2d24737eb..020e013430a 100644 --- a/esp-hal/src/sdm.rs +++ b/esp-hal/src/sdm.rs @@ -311,7 +311,6 @@ impl ChannelCreator { Ok(Channel { clock_source: self.clock_source, - config, _pin_guard: connect_pin(CHANNEL, pin), _clock_guard: clock_guard, }) @@ -326,7 +325,6 @@ impl ChannelCreator { #[derive(Debug)] pub struct Channel { clock_source: ClockSource, - config: ChannelConfig, _pin_guard: PinGuard, _clock_guard: SdmClockGuard, } @@ -335,15 +333,13 @@ impl Channel { /// Applies a new channel configuration. pub fn apply_config(&mut self, config: &ChannelConfig) { write_config_raw(CHANNEL, *config); - self.config = *config; } /// Sets raw pulse density. /// /// The value ranges from `-128` to `127`. pub fn set_pulse_density(&mut self, density: i8) { - let config = self.config.with_pulse_density(density); - self.apply_config(&config); + modify_pulse_density_raw(CHANNEL, density); } /// Sets duty cycle. `0` maps to the minimum density and `255` maps to the @@ -356,14 +352,14 @@ impl Channel { /// /// The returned value is in the hardware divider range `1..=256`. pub fn prescaler(&self) -> u16 { - self.config.raw_prescaler as u16 + 1 + prescaler_raw(CHANNEL) + 1 } /// Reads the raw pulse density. /// /// The returned value is in the hardware range `-128..=127`. pub fn pulse_density(&self) -> i8 { - self.config.pulse_density + pulse_density_raw(CHANNEL) } /// Disconnects this channel and returns its channel creator. @@ -531,3 +527,40 @@ fn write_config_raw(channel: usize, config: ChannelConfig) { sd.sigmadelta(channel) .write(|w| unsafe { w.in_().bits(density).prescale().bits(prescaler) }); } + +fn modify_pulse_density_raw(channel: usize, density: i8) { + // ESP32-C5's PAC names this field `sd_in`; the other supported PACs name it `in`. + let sd = GPIO_SD::regs(); + + #[cfg(esp32c5)] + sd.sigmadelta(channel) + .modify(|_, w| unsafe { w.sd_in().bits(density as _) }); + + #[cfg(not(esp32c5))] + sd.sigmadelta(channel) + .modify(|_, w| unsafe { w.in_().bits(density as _) }); +} + +fn prescaler_raw(channel: usize) -> u16 { + let sd = GPIO_SD::regs(); + + #[cfg(esp32c5)] + let bits = sd.sigmadelta(channel).read().sd_prescale().bits(); + + #[cfg(not(esp32c5))] + let bits = sd.sigmadelta(channel).read().prescale().bits(); + + bits as u16 +} + +fn pulse_density_raw(channel: usize) -> i8 { + let sd = GPIO_SD::regs(); + + #[cfg(esp32c5)] + let bits = sd.sigmadelta(channel).read().sd_in().bits(); + + #[cfg(not(esp32c5))] + let bits = sd.sigmadelta(channel).read().in_().bits(); + + bits as i8 +} From f31640b6e3780a07c0a0071389d9592c75b6fb50 Mon Sep 17 00:00:00 2001 From: ttzytt Date: Tue, 2 Jun 2026 18:06:43 -0500 Subject: [PATCH 10/26] modify timeout for HIL --- hil-test/src/bin/sdm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hil-test/src/bin/sdm.rs b/hil-test/src/bin/sdm.rs index 608d8ab7dcb..4df127dcdcc 100644 --- a/hil-test/src/bin/sdm.rs +++ b/hil-test/src/bin/sdm.rs @@ -247,7 +247,7 @@ fn measure_high_ratio(ctx: &mut Context, duty: u8) -> Measurement { Measurement::new(&rx_data, count) } -#[embedded_test::tests(default_timeout = 10)] +#[embedded_test::tests(default_timeout = 3)] mod tests { use hil_test::assert; From 498d138010979dde203f1c44d8d84d0faeb1ea9a Mon Sep 17 00:00:00 2001 From: tzyt Date: Fri, 17 Jul 2026 16:50:38 -0500 Subject: [PATCH 11/26] remove unnecessary generic and add lifetime for sdm --- esp-hal/src/sdm.rs | 80 +++++++++++++++++++++-------------------- hil-test/src/bin/sdm.rs | 18 +++++----- 2 files changed, 51 insertions(+), 47 deletions(-) diff --git a/esp-hal/src/sdm.rs b/esp-hal/src/sdm.rs index 020e013430a..6acad887c31 100644 --- a/esp-hal/src/sdm.rs +++ b/esp-hal/src/sdm.rs @@ -17,7 +17,7 @@ //! time::Rate, //! }; //! -//! let sdm = Sdm::new(peripherals.GPIO_SD, SdmConfig::default()); +//! let mut sdm = Sdm::new(peripherals.GPIO_SD, SdmConfig::default()); //! let config = sdm //! .channel_config() //! .with_frequency(Rate::from_khz(500))? @@ -30,7 +30,7 @@ //! # {after_snippet} //! ``` -use core::fmt; +use core::{fmt, marker::PhantomData}; use esp_sync::NonReentrantMutex; @@ -77,7 +77,7 @@ for_each_sdm_channel!( clock_source: ClockSource, $( #[doc = concat!("Channel ", stringify!($ch), " creator.")] - pub []: ChannelCreator<$ch>, + pub []: ChannelCreator, )* } @@ -91,7 +91,7 @@ for_each_sdm_channel!( _instance: instance, clock_source: config.clock_source, $( - []: ChannelCreator::new(config.clock_source), + []: ChannelCreator::new($ch, config.clock_source), )* } } @@ -285,33 +285,38 @@ impl ChannelConfigBuilder { /// Creates a connected sigma-delta channel. /// /// Channel creators are exposed by [`Sdm`]. Calling [`connect`](Self::connect) -/// consumes the creator and returns an active [`Channel`], which prevents -/// accidentally connecting the same channel twice. +/// mutably borrows the creator for the lifetime of the active [`Channel`], +/// which prevents accidentally connecting the same channel twice. #[derive(Debug)] -pub struct ChannelCreator { +pub struct ChannelCreator { + channel: usize, // This is copied from the Sdm-level configuration so partially moved // channel creators still use the shared clock source selected at construction time. // It is intentionally not configurable per channel. clock_source: ClockSource, } -impl ChannelCreator { - const fn new(clock_source: ClockSource) -> Self { - Self { clock_source } +impl ChannelCreator { + const fn new(channel: usize, clock_source: ClockSource) -> Self { + Self { + channel, + clock_source, + } } /// Configures this channel and connects it to an output pin. - pub fn connect<'d>( - self, + pub fn connect<'a, 'd>( + &'a mut self, pin: impl PeripheralOutput<'d>, config: ChannelConfig, - ) -> Result, Error> { + ) -> Result, Error> { let clock_guard = SdmClockGuard::new(self.clock_source)?; - write_config_raw(CHANNEL, config); + write_config_raw(self.channel, config); Ok(Channel { - clock_source: self.clock_source, - _pin_guard: connect_pin(CHANNEL, pin), + channel: self.channel, + _creator: PhantomData, + _pin_guard: connect_pin(self.channel, pin), _clock_guard: clock_guard, }) } @@ -319,27 +324,27 @@ impl ChannelCreator { /// A connected sigma-delta channel. /// -/// Dropping a channel disconnects its output pin and releases the SDM clock -/// guard. Use [`disconnect`](Self::disconnect) to explicitly release the -/// channel and recover its [`ChannelCreator`]. +/// Dropping a channel disconnects its output pin, releases the SDM clock guard, +/// and makes its [`ChannelCreator`] available again. #[derive(Debug)] -pub struct Channel { - clock_source: ClockSource, +pub struct Channel<'a> { + channel: usize, + _creator: PhantomData<&'a mut ChannelCreator>, _pin_guard: PinGuard, _clock_guard: SdmClockGuard, } -impl Channel { +impl Channel<'_> { /// Applies a new channel configuration. pub fn apply_config(&mut self, config: &ChannelConfig) { - write_config_raw(CHANNEL, *config); + write_config_raw(self.channel, *config); } /// Sets raw pulse density. /// /// The value ranges from `-128` to `127`. pub fn set_pulse_density(&mut self, density: i8) { - modify_pulse_density_raw(CHANNEL, density); + modify_pulse_density_raw(self.channel, density); } /// Sets duty cycle. `0` maps to the minimum density and `255` maps to the @@ -352,21 +357,14 @@ impl Channel { /// /// The returned value is in the hardware divider range `1..=256`. pub fn prescaler(&self) -> u16 { - prescaler_raw(CHANNEL) + 1 + prescaler_raw(self.channel) + 1 } /// Reads the raw pulse density. /// /// The returned value is in the hardware range `-128..=127`. pub fn pulse_density(&self) -> i8 { - pulse_density_raw(CHANNEL) - } - - /// Disconnects this channel and returns its channel creator. - pub fn disconnect(self) -> ChannelCreator { - let clock_source = self.clock_source; - drop(self); - ChannelCreator::new(clock_source) + pulse_density_raw(self.channel) } } @@ -464,8 +462,8 @@ fn configure_clock_source(clock_source: ClockSource) { // chip. // - P4 uses HP_SYS_CLKRST.peri_clk_ctrl26.iomux_clk_src_sel, a one-bit XTAL/PLL_F80M selector. // - ESP32/C3/S2/S3 use APB for this driver path and do not need a source selector write here. - cfg_if::cfg_if! { - if #[cfg(esp32c5)] { + cfg_select! { + esp32c5 => { crate::peripherals::PCR::regs().iomux_clk_conf().modify(|_, w| unsafe { w.iomux_func_clk_sel() .bits(match clock_source { @@ -475,7 +473,8 @@ fn configure_clock_source(clock_source: ClockSource) { .iomux_func_clk_en() .set_bit() }); - } else if #[cfg(esp32c6)] { + } + esp32c6 => { crate::peripherals::PCR::regs().iomux_clk_conf().modify(|_, w| unsafe { w.iomux_func_clk_sel() .bits(match clock_source { @@ -485,7 +484,8 @@ fn configure_clock_source(clock_source: ClockSource) { .iomux_func_clk_en() .set_bit() }); - } else if #[cfg(esp32h2)] { + } + esp32h2 => { crate::peripherals::PCR::regs().iomux_clk_conf().modify(|_, w| unsafe { w.iomux_func_clk_sel() .bits(match clock_source { @@ -495,7 +495,8 @@ fn configure_clock_source(clock_source: ClockSource) { .iomux_func_clk_en() .set_bit() }); - } else if #[cfg(esp32p4)] { + } + esp32p4 => { crate::peripherals::HP_SYS_CLKRST::regs() .peri_clk_ctrl26() .modify(|_, w| { @@ -506,7 +507,8 @@ fn configure_clock_source(clock_source: ClockSource) { .iomux_clk_en() .set_bit() }); - } else { + } + _ => { let _ = clock_source; } } diff --git a/hil-test/src/bin/sdm.rs b/hil-test/src/bin/sdm.rs index 4df127dcdcc..2801203b17c 100644 --- a/hil-test/src/bin/sdm.rs +++ b/hil-test/src/bin/sdm.rs @@ -21,12 +21,13 @@ use esp_hal::{ }; use hil_test as _; -cfg_if::cfg_if! { - if #[cfg(esp32h2)] { +cfg_select! { + esp32h2 => { const RMT_FREQUENCY: Rate = Rate::from_mhz(32); // default clock source for h2 is 32Mhz const RMT_DIVIDER: u8 = 32; - } else { + } + _ => { const RMT_FREQUENCY: Rate = Rate::from_mhz(80); // default clock source for other models is 80Mhz const RMT_DIVIDER: u8 = 80; @@ -47,14 +48,15 @@ const DUTY_MAX: u8 = 245; const DUTY_STEP: u8 = 20; const DUTY_RATIO_TOLERANCE_PER_MILLE: u32 = 20; -cfg_if::cfg_if! { - if #[cfg(any(esp32, esp32s3))] { +cfg_select! { + any(esp32, esp32s3) => { macro_rules! rx_channel_creator { ($rmt:expr) => { $rmt.channel4 }; } - } else { + } + _ => { macro_rules! rx_channel_creator { ($rmt:expr) => { $rmt.channel2 @@ -217,7 +219,7 @@ fn expected_ratio_per_mille(duty: u8) -> u32 { } fn measure_high_ratio(ctx: &mut Context, duty: u8) -> Measurement { - let sdm = Sdm::new(ctx.gpio_sd.reborrow(), SdmConfig::default()); + let mut sdm = Sdm::new(ctx.gpio_sd.reborrow(), SdmConfig::default()); let config = sdm .channel_config() .with_frequency(SDM_FREQUENCY) @@ -237,7 +239,7 @@ fn measure_high_ratio(ctx: &mut Context, duty: u8) -> Measurement { Delay::new().delay_micros(SAMPLE_TIME_US); - let _creator = channel.disconnect(); + drop(channel); let mut output = Output::new(ctx.sdm_pin.reborrow(), Level::Low, OutputConfig::default()); output.set_low(); // send idle signal so that RMT will stop recording pulses From f33246d5775af34f60582754e70b10523103fb37 Mon Sep 17 00:00:00 2001 From: tzyt Date: Sat, 18 Jul 2026 02:49:41 -0500 Subject: [PATCH 12/26] swtich clock handling implementation to existing clock source --- esp-hal/src/sdm.rs | 221 ++++++------------ esp-hal/src/soc/esp32c5/clocks.rs | 25 ++ esp-hal/src/soc/esp32c6/clocks.rs | 24 ++ esp-hal/src/soc/esp32h2/clocks.rs | 22 ++ esp-hal/src/soc/esp32p4/clocks.rs | 22 ++ .../src/_build_script_utils.rs | 9 + .../src/_generated_esp32c5.rs | 124 ++++++++++ .../src/_generated_esp32c6.rs | 124 ++++++++++ .../src/_generated_esp32h2.rs | 116 +++++++++ .../src/_generated_esp32p4.rs | 116 +++++++++ esp-metadata/devices/esp32c5/clocks.toml | 5 + esp-metadata/devices/esp32c6/clocks.toml | 5 + esp-metadata/devices/esp32h2/clocks.toml | 5 + esp-metadata/devices/esp32p4/clocks.toml | 5 + hil-test/src/bin/sdm.rs | 5 +- 15 files changed, 670 insertions(+), 158 deletions(-) diff --git a/esp-hal/src/sdm.rs b/esp-hal/src/sdm.rs index 6acad887c31..235aaf99ccc 100644 --- a/esp-hal/src/sdm.rs +++ b/esp-hal/src/sdm.rs @@ -22,7 +22,7 @@ //! .channel_config() //! .with_frequency(Rate::from_khz(500))? //! .with_duty(128); -//! let mut channel = sdm.channel0.connect(peripherals.GPIO2, config)?; +//! let mut channel = sdm.channel0.connect(peripherals.GPIO2, config); //! //! channel.set_duty(192); // duty ranges from 0 to 255 //! channel.set_pulse_density(0); // pulse density ranges from -128 to 127 @@ -32,8 +32,8 @@ use core::{fmt, marker::PhantomData}; -use esp_sync::NonReentrantMutex; - +#[cfg(soc_has_clock_node_iomux_function_clock)] +use crate::soc::clocks::{self, ClockTree, IomuxFunctionClockConfig}; use crate::{ gpio::{ OutputConfig, @@ -46,22 +46,6 @@ use crate::{ time::Rate, }; -static CLOCK_STATE: NonReentrantMutex = NonReentrantMutex::new(SdmClockState::new()); - -struct SdmClockState { - users: usize, - source: Option, -} - -impl SdmClockState { - const fn new() -> Self { - Self { - users: 0, - source: None, - } - } -} - for_each_sdm_channel!( (channels $(($ch:literal, $signal:ident)),*) => { paste::paste! { @@ -74,7 +58,6 @@ for_each_sdm_channel!( #[non_exhaustive] pub struct Sdm<'d> { _instance: GPIO_SD<'d>, - clock_source: ClockSource, $( #[doc = concat!("Channel ", stringify!($ch), " creator.")] pub []: ChannelCreator, @@ -87,19 +70,27 @@ for_each_sdm_channel!( /// The SDM clock source is shared by all SDM channels through the IO_MUX /// clock, so it is selected here instead of being configurable per channel. pub fn new(instance: GPIO_SD<'d>, config: SdmConfig) -> Self { + #[cfg(soc_has_clock_node_iomux_function_clock)] + ClockTree::with(|clocks| { + clocks::configure_iomux_function_clock( + clocks, + config.clock_source.into(), + ); + }); + #[cfg(not(soc_has_clock_node_iomux_function_clock))] + let _ = config; + Self { _instance: instance, - clock_source: config.clock_source, $( - []: ChannelCreator::new($ch, config.clock_source), + []: ChannelCreator::new($ch), )* } } - /// Creates a channel configuration builder using this peripheral's - /// clock source. + /// Creates a channel configuration builder. pub const fn channel_config(&self) -> ChannelConfigBuilder { - ChannelConfigBuilder::new(self.clock_source) + ChannelConfigBuilder::new() } } } @@ -158,17 +149,25 @@ pub enum ClockSource { PllF48m, } -impl ClockSource { - fn frequency(self) -> Rate { - match self { - #[cfg(any(esp32, esp32c3, esp32s2, esp32s3))] - Self::Apb => Rate::from_hz(crate::soc::clocks::apb_clk_frequency()), - #[cfg(any(esp32c5, esp32c6, esp32h2, esp32p4))] - Self::Xtal => crate::clock::xtal_clock(), - #[cfg(any(esp32c5, esp32c6, esp32p4))] - Self::PllF80m => Rate::from_mhz(80), - #[cfg(esp32h2)] - Self::PllF48m => Rate::from_mhz(48), +#[cfg(any(esp32c5, esp32c6, esp32p4))] +// C5/C6 hardware also routes RC_FAST (called FOSC in some register descriptions) through the +// IO MUX. ESP-IDF does not expose it as an SDM clock source, so neither do we. It should be +// usable in principle, but has not been included in the supported SDM API. +impl From for IomuxFunctionClockConfig { + fn from(source: ClockSource) -> Self { + match source { + ClockSource::Xtal => Self::XtalClk, + ClockSource::PllF80m => Self::PllF80m, + } + } +} + +#[cfg(esp32h2)] +impl From for IomuxFunctionClockConfig { + fn from(source: ClockSource) -> Self { + match source { + ClockSource::Xtal => Self::XtalClk, + ClockSource::PllF48m => Self::PllF48m, } } } @@ -180,8 +179,6 @@ impl ClockSource { pub enum Error { /// The requested frequency cannot be represented by the hardware prescaler. UnreachableTargetFrequency, - /// Another SDM channel is already using a different clock source. - ClockSourceConflict, /// The prescaler is outside the supported range. PrescalerOutOfRange, } @@ -190,7 +187,6 @@ impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::UnreachableTargetFrequency => f.write_str("unreachable target frequency"), - Self::ClockSourceConflict => f.write_str("SDM clock source conflict"), Self::PrescalerOutOfRange => f.write_str("prescaler out of range"), } } @@ -238,22 +234,16 @@ impl ChannelConfig { } /// Builds a sigma-delta channel configuration. -/// -/// This builder carries the peripheral-wide clock source so frequencies can be -/// converted to raw prescaler values without storing clock selection in the -/// channel configuration itself. #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub struct ChannelConfigBuilder { - clock_source: ClockSource, config: ChannelConfig, } impl ChannelConfigBuilder { - const fn new(clock_source: ClockSource) -> Self { + const fn new() -> Self { Self { - clock_source, config: ChannelConfig { raw_prescaler: 0, pulse_density: 0, @@ -263,8 +253,7 @@ impl ChannelConfigBuilder { /// Sets the requested output frequency. pub fn with_frequency(mut self, frequency: Rate) -> Result { - self.config.raw_prescaler = - raw_prescaler(prescaler_from_frequency(frequency, self.clock_source)?); + self.config.raw_prescaler = raw_prescaler(prescaler_from_frequency(frequency)?); Ok(self.config) } @@ -290,18 +279,11 @@ impl ChannelConfigBuilder { #[derive(Debug)] pub struct ChannelCreator { channel: usize, - // This is copied from the Sdm-level configuration so partially moved - // channel creators still use the shared clock source selected at construction time. - // It is intentionally not configurable per channel. - clock_source: ClockSource, } impl ChannelCreator { - const fn new(channel: usize, clock_source: ClockSource) -> Self { - Self { - channel, - clock_source, - } + const fn new(channel: usize) -> Self { + Self { channel } } /// Configures this channel and connects it to an output pin. @@ -309,16 +291,16 @@ impl ChannelCreator { &'a mut self, pin: impl PeripheralOutput<'d>, config: ChannelConfig, - ) -> Result, Error> { - let clock_guard = SdmClockGuard::new(self.clock_source)?; + ) -> Channel<'a> { + let clock_guard = SdmClockGuard::new(); write_config_raw(self.channel, config); - Ok(Channel { + Channel { channel: self.channel, _creator: PhantomData, _pin_guard: connect_pin(self.channel, pin), _clock_guard: clock_guard, - }) + } } } @@ -378,53 +360,42 @@ fn connect_pin<'d>(channel: usize, pin: impl PeripheralOutput<'d>) -> PinGuard { #[derive(Debug)] struct SdmClockGuard { _peripheral: GenericPeripheralGuard<{ Peripheral::GpioSd as u8 }>, - clock_source: ClockSource, + _iomux: IomuxClockGuard, } impl SdmClockGuard { - fn new(clock_source: ClockSource) -> Result { - CLOCK_STATE.with(|state| { - if state.users == usize::MAX { - panic!("SDM clock reference counter overflowed"); - } - - if state.users == 0 { - configure_clock_source(clock_source); - state.source = Some(clock_source); - } else if state.source != Some(clock_source) { - return Err(Error::ClockSourceConflict); - } + fn new() -> Self { + let iomux = IomuxClockGuard::new(); + Self { + _peripheral: GenericPeripheralGuard::new(), + _iomux: iomux, + } + } +} - state.users += 1; - Ok(()) - })?; +#[derive(Debug)] +struct IomuxClockGuard; - Ok(Self { - _peripheral: GenericPeripheralGuard::new(), - clock_source, - }) +impl IomuxClockGuard { + fn new() -> Self { + #[cfg(soc_has_clock_node_iomux_function_clock)] + ClockTree::with(clocks::request_iomux_function_clock); + Self } } -impl Drop for SdmClockGuard { +impl Drop for IomuxClockGuard { fn drop(&mut self) { - CLOCK_STATE.with(|state| match state.users { - 0 => panic!("SDM clock reference counter underflowed"), - 1 => { - debug_assert_eq!(state.source, Some(self.clock_source)); - state.users = 0; - state.source = None; - } - _ => { - debug_assert_eq!(state.source, Some(self.clock_source)); - state.users -= 1; - } - }); + #[cfg(soc_has_clock_node_iomux_function_clock)] + ClockTree::with(clocks::release_iomux_function_clock); } } -fn prescaler_from_frequency(frequency: Rate, clock_source: ClockSource) -> Result { - let source_frequency = clock_source.frequency().as_hz(); +fn prescaler_from_frequency(frequency: Rate) -> Result { + #[cfg(soc_has_clock_node_iomux_function_clock)] + let source_frequency = clocks::iomux_function_clock_frequency(); + #[cfg(not(soc_has_clock_node_iomux_function_clock))] + let source_frequency = crate::soc::clocks::apb_clk_frequency(); let requested_frequency = frequency.as_hz(); if requested_frequency == 0 || requested_frequency > source_frequency { @@ -456,64 +427,6 @@ const fn duty_to_density(duty: u8) -> i8 { duty.wrapping_sub(128) as i8 } -fn configure_clock_source(clock_source: ClockSource) { - // Newer chips expose this selector in different clock-control blocks: - // - C5/C6/H2 use PCR.iomux_clk_conf.iomux_func_clk_sel, but the selector encodings differ by - // chip. - // - P4 uses HP_SYS_CLKRST.peri_clk_ctrl26.iomux_clk_src_sel, a one-bit XTAL/PLL_F80M selector. - // - ESP32/C3/S2/S3 use APB for this driver path and do not need a source selector write here. - cfg_select! { - esp32c5 => { - crate::peripherals::PCR::regs().iomux_clk_conf().modify(|_, w| unsafe { - w.iomux_func_clk_sel() - .bits(match clock_source { - ClockSource::Xtal => 0, - ClockSource::PllF80m => 2, - }) - .iomux_func_clk_en() - .set_bit() - }); - } - esp32c6 => { - crate::peripherals::PCR::regs().iomux_clk_conf().modify(|_, w| unsafe { - w.iomux_func_clk_sel() - .bits(match clock_source { - ClockSource::PllF80m => 1, - ClockSource::Xtal => 3, - }) - .iomux_func_clk_en() - .set_bit() - }); - } - esp32h2 => { - crate::peripherals::PCR::regs().iomux_clk_conf().modify(|_, w| unsafe { - w.iomux_func_clk_sel() - .bits(match clock_source { - ClockSource::Xtal => 0, - ClockSource::PllF48m => 2, - }) - .iomux_func_clk_en() - .set_bit() - }); - } - esp32p4 => { - crate::peripherals::HP_SYS_CLKRST::regs() - .peri_clk_ctrl26() - .modify(|_, w| { - match clock_source { - ClockSource::Xtal => w.iomux_clk_src_sel().clear_bit(), - ClockSource::PllF80m => w.iomux_clk_src_sel().set_bit(), - } - .iomux_clk_en() - .set_bit() - }); - } - _ => { - let _ = clock_source; - } - } -} - fn write_config_raw(channel: usize, config: ChannelConfig) { // ESP32-C5's PAC names these fields `sd_in`/`sd_prescale`; the other // supported PACs name them `in`/`prescale`. diff --git a/esp-hal/src/soc/esp32c5/clocks.rs b/esp-hal/src/soc/esp32c5/clocks.rs index f4f55c5d17c..10c3d545391 100644 --- a/esp-hal/src/soc/esp32c5/clocks.rs +++ b/esp-hal/src/soc/esp32c5/clocks.rs @@ -52,6 +52,7 @@ impl CpuClock { lp_fast_clk: Some(LpFastClkConfig::RcFast), lp_slow_clk: Some(LpSlowClkConfig::RcSlow), crypto_clk: Some(CryptoClkConfig::PllF480m), + iomux_function_clock: Some(IomuxFunctionClockConfig::PllF80m), timg_calibration_clock: None, }; const PRESET_160: ClockConfig = ClockConfig { @@ -63,6 +64,7 @@ impl CpuClock { lp_fast_clk: Some(LpFastClkConfig::RcFast), lp_slow_clk: Some(LpSlowClkConfig::RcSlow), crypto_clk: Some(CryptoClkConfig::PllF480m), + iomux_function_clock: Some(IomuxFunctionClockConfig::PllF80m), timg_calibration_clock: None, }; const PRESET_240: ClockConfig = ClockConfig { @@ -74,6 +76,7 @@ impl CpuClock { lp_fast_clk: Some(LpFastClkConfig::RcFast), lp_slow_clk: Some(LpSlowClkConfig::RcSlow), crypto_clk: Some(CryptoClkConfig::PllF480m), + iomux_function_clock: Some(IomuxFunctionClockConfig::PllF80m), timg_calibration_clock: None, }; } @@ -464,6 +467,28 @@ fn configure_crypto_clk_impl( }); } +// IOMUX_FUNCTION_CLOCK + +fn enable_iomux_function_clock_impl(_clocks: &mut ClockTree, en: bool) { + PCR::regs() + .iomux_clk_conf() + .modify(|_, w| w.iomux_func_clk_en().bit(en)); +} + +fn configure_iomux_function_clock_impl( + _clocks: &mut ClockTree, + _old_config: Option, + new_config: IomuxFunctionClockConfig, +) { + PCR::regs().iomux_clk_conf().modify(|_, w| unsafe { + w.iomux_func_clk_sel().bits(match new_config { + IomuxFunctionClockConfig::XtalClk => 0, + IomuxFunctionClockConfig::RcFastClk => 1, + IomuxFunctionClockConfig::PllF80m => 2, + }) + }); +} + // TIMG_CALIBRATION_CLOCK fn enable_timg_calibration_clock_impl(_clocks: &mut ClockTree, _en: bool) { diff --git a/esp-hal/src/soc/esp32c6/clocks.rs b/esp-hal/src/soc/esp32c6/clocks.rs index 1f33725324b..21b3d30ee8a 100644 --- a/esp-hal/src/soc/esp32c6/clocks.rs +++ b/esp-hal/src/soc/esp32c6/clocks.rs @@ -53,6 +53,7 @@ impl CpuClock { mspi_fast_ls_clk: None, // Unused when root clock is PLL apb_clk: Some(ApbClkConfig::new(ApbClkDivisor::_0)), ledc_sclk: Some(LedcSclkConfig::PllF80m), + iomux_function_clock: Some(IomuxFunctionClockConfig::PllF80m), lp_fast_clk: Some(LpFastClkConfig::RcFastClk), lp_slow_clk: Some(LpSlowClkConfig::RcSlow), timg_calibration_clock: None, @@ -69,6 +70,7 @@ impl CpuClock { mspi_fast_ls_clk: None, // Unused when root clock is PLL apb_clk: Some(ApbClkConfig::new(ApbClkDivisor::_0)), ledc_sclk: Some(LedcSclkConfig::PllF80m), + iomux_function_clock: Some(IomuxFunctionClockConfig::PllF80m), lp_fast_clk: Some(LpFastClkConfig::RcFastClk), lp_slow_clk: Some(LpSlowClkConfig::RcSlow), timg_calibration_clock: None, @@ -462,6 +464,28 @@ fn enable_pll_f240m_impl(_clocks: &mut ClockTree, _en: bool) { // Nothing to do. } +// IOMUX_FUNCTION_CLOCK + +fn enable_iomux_function_clock_impl(_clocks: &mut ClockTree, en: bool) { + PCR::regs() + .iomux_clk_conf() + .modify(|_, w| w.iomux_func_clk_en().bit(en)); +} + +fn configure_iomux_function_clock_impl( + _clocks: &mut ClockTree, + _old_config: Option, + new_config: IomuxFunctionClockConfig, +) { + PCR::regs().iomux_clk_conf().modify(|_, w| unsafe { + w.iomux_func_clk_sel().bits(match new_config { + IomuxFunctionClockConfig::PllF80m => 1, + IomuxFunctionClockConfig::RcFastClk => 2, + IomuxFunctionClockConfig::XtalClk => 3, + }) + }); +} + // LEDC_SCLK fn enable_ledc_sclk_impl(_clocks: &mut ClockTree, en: bool) { diff --git a/esp-hal/src/soc/esp32h2/clocks.rs b/esp-hal/src/soc/esp32h2/clocks.rs index f8f093d6a00..b77934f3d65 100644 --- a/esp-hal/src/soc/esp32h2/clocks.rs +++ b/esp-hal/src/soc/esp32h2/clocks.rs @@ -42,6 +42,7 @@ impl CpuClock { cpu_clk: Some(CpuClkConfig::new(0)), ahb_clk: Some(AhbClkConfig::new(0)), apb_clk: Some(ApbClkConfig::new(0)), + iomux_function_clock: Some(IomuxFunctionClockConfig::PllF48m), lp_fast_clk: Some(LpFastClkConfig::RcFastClk), lp_slow_clk: Some(LpSlowClkConfig::RcSlow), timg_calibration_clock: None, @@ -308,6 +309,27 @@ fn configure_apb_clk_impl( .modify(|_, w| unsafe { w.apb_div_num().bits(new_config.divisor() as u8) }); } +// IOMUX_FUNCTION_CLOCK + +fn enable_iomux_function_clock_impl(_clocks: &mut ClockTree, en: bool) { + PCR::regs() + .iomux_clk_conf() + .modify(|_, w| w.iomux_func_clk_en().bit(en)); +} + +fn configure_iomux_function_clock_impl( + _clocks: &mut ClockTree, + _old_config: Option, + new_config: IomuxFunctionClockConfig, +) { + PCR::regs().iomux_clk_conf().modify(|_, w| unsafe { + w.iomux_func_clk_sel().bits(match new_config { + IomuxFunctionClockConfig::XtalClk => 0, + IomuxFunctionClockConfig::PllF48m => 2, + }) + }); +} + // XTAL_D2_CLK fn enable_xtal_d2_clk_impl(_clocks: &mut ClockTree, _en: bool) { diff --git a/esp-hal/src/soc/esp32p4/clocks.rs b/esp-hal/src/soc/esp32p4/clocks.rs index 7fe99805afe..0a406783de0 100644 --- a/esp-hal/src/soc/esp32p4/clocks.rs +++ b/esp-hal/src/soc/esp32p4/clocks.rs @@ -57,6 +57,7 @@ impl CpuClock { mem_clk: Some(MemClkConfig::new(1)), // /2 = 200 MHz sys_clk: Some(SysClkConfig::new(0)), // /1 = 200 MHz apb_clk: Some(ApbClkConfig::new(1)), // /2 = 100 MHz + iomux_function_clock: Some(IomuxFunctionClockConfig::PllF80m), lp_fast_clk: Some(LpFastClkConfig::RcFast), lp_slow_clk: Some(LpSlowClkConfig::RcSlow), timg_calibration_clock: None, @@ -73,6 +74,7 @@ impl CpuClock { mem_clk: Some(MemClkConfig::new(0)), // /1 = 200 MHz sys_clk: Some(SysClkConfig::new(0)), // /1 = 200 MHz apb_clk: Some(ApbClkConfig::new(1)), // /2 = 100 MHz + iomux_function_clock: Some(IomuxFunctionClockConfig::PllF80m), lp_fast_clk: Some(LpFastClkConfig::RcFast), lp_slow_clk: Some(LpSlowClkConfig::RcSlow), timg_calibration_clock: None, @@ -87,6 +89,7 @@ impl CpuClock { mem_clk: Some(MemClkConfig::new(0)), // /1 = 100 MHz sys_clk: Some(SysClkConfig::new(0)), // /1 = 100 MHz apb_clk: Some(ApbClkConfig::new(0)), // /1 = 100 MHz + iomux_function_clock: Some(IomuxFunctionClockConfig::PllF80m), lp_fast_clk: Some(LpFastClkConfig::RcFast), lp_slow_clk: Some(LpSlowClkConfig::RcSlow), timg_calibration_clock: None, @@ -399,6 +402,25 @@ fn configure_apb_clk_impl( update_divider(); } +// IOMUX_FUNCTION_CLOCK + +fn enable_iomux_function_clock_impl(_clocks: &mut ClockTree, _en: bool) { + // The IO MUX gate is owned by the always-enabled IOMUX peripheral clock. +} + +fn configure_iomux_function_clock_impl( + _clocks: &mut ClockTree, + _old_config: Option, + new_config: IomuxFunctionClockConfig, +) { + HP_SYS_CLKRST::regs() + .peri_clk_ctrl26() + .modify(|_, w| match new_config { + IomuxFunctionClockConfig::XtalClk => w.iomux_clk_src_sel().clear_bit(), + IomuxFunctionClockConfig::PllF80m => w.iomux_clk_src_sel().set_bit(), + }); +} + // LP_FAST_CLK mux fn configure_lp_fast_clk_impl( _clocks: &mut ClockTree, diff --git a/esp-metadata-generated/src/_build_script_utils.rs b/esp-metadata-generated/src/_build_script_utils.rs index 8a681c28a24..03ddd6894a5 100644 --- a/esp-metadata-generated/src/_build_script_utils.rs +++ b/esp-metadata-generated/src/_build_script_utils.rs @@ -2106,6 +2106,7 @@ impl Chip { "soc_has_clock_node_lp_fast_clk", "soc_has_clock_node_lp_slow_clk", "soc_has_clock_node_crypto_clk", + "soc_has_clock_node_iomux_function_clock", "soc_has_clock_node_timg_calibration_clock", "soc_has_clock_node_uart_function_clock", "soc_has_clock_node_uart_baud_rate_generator", @@ -2389,6 +2390,7 @@ impl Chip { "cargo:rustc-cfg=soc_has_clock_node_lp_fast_clk", "cargo:rustc-cfg=soc_has_clock_node_lp_slow_clk", "cargo:rustc-cfg=soc_has_clock_node_crypto_clk", + "cargo:rustc-cfg=soc_has_clock_node_iomux_function_clock", "cargo:rustc-cfg=soc_has_clock_node_timg_calibration_clock", "cargo:rustc-cfg=soc_has_clock_node_uart_function_clock", "cargo:rustc-cfg=soc_has_clock_node_uart_baud_rate_generator", @@ -2822,6 +2824,7 @@ impl Chip { "soc_has_clock_node_pll_f80m", "soc_has_clock_node_pll_f160m", "soc_has_clock_node_pll_f240m", + "soc_has_clock_node_iomux_function_clock", "soc_has_clock_node_ledc_sclk", "soc_has_clock_node_xtal_d2_clk", "soc_has_clock_node_lp_fast_clk", @@ -3119,6 +3122,7 @@ impl Chip { "cargo:rustc-cfg=soc_has_clock_node_pll_f80m", "cargo:rustc-cfg=soc_has_clock_node_pll_f160m", "cargo:rustc-cfg=soc_has_clock_node_pll_f240m", + "cargo:rustc-cfg=soc_has_clock_node_iomux_function_clock", "cargo:rustc-cfg=soc_has_clock_node_ledc_sclk", "cargo:rustc-cfg=soc_has_clock_node_xtal_d2_clk", "cargo:rustc-cfg=soc_has_clock_node_lp_fast_clk", @@ -4104,6 +4108,7 @@ impl Chip { "soc_has_clock_node_pll_f64m_clk", "soc_has_clock_node_pll_f48m_clk", "soc_has_clock_node_rc_fast_clk", + "soc_has_clock_node_iomux_function_clock", "soc_has_clock_node_xtal32k_clk", "soc_has_clock_node_osc_slow_clk", "soc_has_clock_node_rc_slow_clk", @@ -4372,6 +4377,7 @@ impl Chip { "cargo:rustc-cfg=soc_has_clock_node_pll_f64m_clk", "cargo:rustc-cfg=soc_has_clock_node_pll_f48m_clk", "cargo:rustc-cfg=soc_has_clock_node_rc_fast_clk", + "cargo:rustc-cfg=soc_has_clock_node_iomux_function_clock", "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_rc_slow_clk", @@ -4745,6 +4751,7 @@ impl Chip { "soc_has_clock_node_pll_f120m", "soc_has_clock_node_pll_f160m", "soc_has_clock_node_pll_f240m", + "soc_has_clock_node_iomux_function_clock", "soc_has_clock_node_pll_f25m", "soc_has_clock_node_pll_f50m", "soc_has_clock_node_xtal_d2_clk", @@ -5007,6 +5014,7 @@ impl Chip { "cargo:rustc-cfg=soc_has_clock_node_pll_f120m", "cargo:rustc-cfg=soc_has_clock_node_pll_f160m", "cargo:rustc-cfg=soc_has_clock_node_pll_f240m", + "cargo:rustc-cfg=soc_has_clock_node_iomux_function_clock", "cargo:rustc-cfg=soc_has_clock_node_pll_f25m", "cargo:rustc-cfg=soc_has_clock_node_pll_f50m", "cargo:rustc-cfg=soc_has_clock_node_xtal_d2_clk", @@ -7534,6 +7542,7 @@ impl Chip { "soc_has_clock_node_xtal_d2_clk", "soc_has_clock_node_lp_fast_clk", "soc_has_clock_node_lp_slow_clk", + "soc_has_clock_node_iomux_function_clock", "soc_has_clock_node_parl_io_rx_clock", "soc_has_clock_node_parl_io_tx_clock", "spi_master_has_clk_pre_div", diff --git a/esp-metadata-generated/src/_generated_esp32c5.rs b/esp-metadata-generated/src/_generated_esp32c5.rs index e775516c0d4..f390a0316fa 100644 --- a/esp-metadata-generated/src/_generated_esp32c5.rs +++ b/esp-metadata-generated/src/_generated_esp32c5.rs @@ -1374,6 +1374,20 @@ macro_rules! for_each_wakeup_source { /// todo!() /// } /// +/// // IOMUX_FUNCTION_CLOCK +/// +/// fn enable_iomux_function_clock_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_iomux_function_clock_impl( +/// _clocks: &mut ClockTree, +/// _old_config: Option, +/// _new_config: IomuxFunctionClockConfig, +/// ) { +/// todo!() +/// } +/// /// // TIMG_CALIBRATION_CLOCK /// /// fn enable_timg_calibration_clock_impl(_clocks: &mut ClockTree, _en: bool) { @@ -1705,6 +1719,18 @@ macro_rules! define_clock_tree_types { /// Selects `PLL_CLK`. PllF480m, } + /// The list of clock signals that the `IOMUX_FUNCTION_CLOCK` multiplexer can output. + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum IomuxFunctionClockConfig { + /// Selects `XTAL_CLK`. + XtalClk, + /// Selects `RC_FAST_CLK`. + RcFastClk, + #[default] + /// Selects `PLL_F80M`. + PllF80m, + } /// 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))] @@ -1923,6 +1949,7 @@ macro_rules! define_clock_tree_types { lp_fast_clk: Option, lp_slow_clk: Option, crypto_clk: Option, + iomux_function_clock: Option, timg_calibration_clock: Option, i2c_function_clock: [Option; 1], parl_io_rx_clock: [Option; 1], @@ -1952,6 +1979,7 @@ macro_rules! define_clock_tree_types { lp_fast_clk_refcount: u32, lp_slow_clk_refcount: u32, crypto_clk_refcount: u32, + iomux_function_clock_refcount: u32, timg_calibration_clock_refcount: u32, i2c_function_clock_refcount: [u32; 1], parl_io_rx_clock_refcount: [u32; 1], @@ -2000,6 +2028,10 @@ macro_rules! define_clock_tree_types { pub fn crypto_clk(&self) -> Option { self.crypto_clk } + /// Returns the current configuration of the IOMUX_FUNCTION_CLOCK clock tree node + pub fn iomux_function_clock(&self) -> Option { + self.iomux_function_clock + } /// Returns the current configuration of the TIMG_CALIBRATION_CLOCK clock tree node pub fn timg_calibration_clock(&self) -> Option { self.timg_calibration_clock @@ -2067,6 +2099,7 @@ macro_rules! define_clock_tree_types { lp_fast_clk: None, lp_slow_clk: None, crypto_clk: None, + iomux_function_clock: None, timg_calibration_clock: None, i2c_function_clock: [None; 1], parl_io_rx_clock: [None; 1], @@ -2096,6 +2129,7 @@ macro_rules! define_clock_tree_types { lp_fast_clk_refcount: 0, lp_slow_clk_refcount: 0, crypto_clk_refcount: 0, + iomux_function_clock_refcount: 0, timg_calibration_clock_refcount: 0, i2c_function_clock_refcount: [0; 1], parl_io_rx_clock_refcount: [0; 1], @@ -2123,6 +2157,8 @@ macro_rules! define_clock_tree_types { ::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 = + ::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] = @@ -2839,6 +2875,80 @@ macro_rules! define_clock_tree_types { CryptoClkConfig::PllF480m => pll_clk_frequency(), } } + pub fn configure_iomux_function_clock( + clocks: &mut ClockTree, + new_selector: IomuxFunctionClockConfig, + ) { + let old_selector = clocks.iomux_function_clock.replace(new_selector); + refresh_iomux_function_clock_downstream(clocks); + if clocks.iomux_function_clock_refcount > 0 { + match new_selector { + IomuxFunctionClockConfig::XtalClk => request_xtal_clk(clocks), + IomuxFunctionClockConfig::RcFastClk => request_rc_fast_clk(clocks), + IomuxFunctionClockConfig::PllF80m => request_pll_f80m(clocks), + } + configure_iomux_function_clock_impl(clocks, old_selector, new_selector); + if let Some(old_selector) = old_selector { + match old_selector { + IomuxFunctionClockConfig::XtalClk => release_xtal_clk(clocks), + IomuxFunctionClockConfig::RcFastClk => release_rc_fast_clk(clocks), + IomuxFunctionClockConfig::PllF80m => release_pll_f80m(clocks), + } + } + } else { + configure_iomux_function_clock_impl(clocks, old_selector, new_selector); + } + } + pub fn iomux_function_clock_config( + clocks: &mut ClockTree, + ) -> Option { + clocks.iomux_function_clock + } + pub fn request_iomux_function_clock(clocks: &mut ClockTree) { + trace!("Requesting IOMUX_FUNCTION_CLOCK"); + if increment_reference_count(&mut clocks.iomux_function_clock_refcount) { + trace!("Enabling IOMUX_FUNCTION_CLOCK"); + match unwrap!(clocks.iomux_function_clock) { + IomuxFunctionClockConfig::XtalClk => request_xtal_clk(clocks), + IomuxFunctionClockConfig::RcFastClk => request_rc_fast_clk(clocks), + IomuxFunctionClockConfig::PllF80m => request_pll_f80m(clocks), + } + enable_iomux_function_clock_impl(clocks, true); + } + } + pub fn release_iomux_function_clock(clocks: &mut ClockTree) { + trace!("Releasing IOMUX_FUNCTION_CLOCK"); + if decrement_reference_count(&mut clocks.iomux_function_clock_refcount) { + trace!("Disabling IOMUX_FUNCTION_CLOCK"); + enable_iomux_function_clock_impl(clocks, false); + match unwrap!(clocks.iomux_function_clock) { + IomuxFunctionClockConfig::XtalClk => release_xtal_clk(clocks), + IomuxFunctionClockConfig::RcFastClk => release_rc_fast_clk(clocks), + IomuxFunctionClockConfig::PllF80m => release_pll_f80m(clocks), + } + } + } + #[allow(unused_variables)] + pub fn iomux_function_clock_config_frequency( + clocks: &mut ClockTree, + config: IomuxFunctionClockConfig, + ) -> u32 { + match config { + IomuxFunctionClockConfig::XtalClk => xtal_clk_frequency(), + IomuxFunctionClockConfig::RcFastClk => rc_fast_clk_frequency(), + IomuxFunctionClockConfig::PllF80m => pll_f80m_frequency(), + } + } + pub fn iomux_function_clock_frequency() -> u32 { + IOMUX_FUNCTION_CLOCK_FREQ_CACHE.load(::core::sync::atomic::Ordering::Acquire) + } + pub fn iomux_function_clock_source_frequency(source: IomuxFunctionClockConfig) -> u32 { + match source { + IomuxFunctionClockConfig::XtalClk => xtal_clk_frequency(), + IomuxFunctionClockConfig::RcFastClk => rc_fast_clk_frequency(), + IomuxFunctionClockConfig::PllF80m => pll_f80m_frequency(), + } + } pub fn configure_timg_calibration_clock( clocks: &mut ClockTree, new_selector: TimgCalibrationClockConfig, @@ -3616,6 +3726,8 @@ macro_rules! define_clock_tree_types { pub lp_slow_clk: Option, /// `CRYPTO_CLK` configuration. pub crypto_clk: Option, + /// `IOMUX_FUNCTION_CLOCK` configuration. + pub iomux_function_clock: Option, /// `TIMG_CALIBRATION_CLOCK` configuration. pub timg_calibration_clock: Option, } @@ -3645,6 +3757,9 @@ macro_rules! define_clock_tree_types { if let Some(config) = self.crypto_clk { configure_crypto_clk(clocks, config); } + if let Some(config) = self.iomux_function_clock { + configure_iomux_function_clock(clocks, config); + } if let Some(config) = self.timg_calibration_clock { configure_timg_calibration_clock(clocks, config); } @@ -3670,6 +3785,7 @@ macro_rules! define_clock_tree_types { refresh_hp_root_clk_downstream(clocks); refresh_lp_fast_clk_downstream(clocks); refresh_crypto_clk_downstream(clocks); + refresh_iomux_function_clock_downstream(clocks); for child_instance in [I2cInstance::I2c0] { refresh_i2c_function_clock_downstream(clocks, child_instance); } @@ -3750,6 +3866,14 @@ macro_rules! define_clock_tree_types { ); } } + fn refresh_iomux_function_clock_downstream(clocks: &mut ClockTree) { + if let Some(config) = clocks.iomux_function_clock { + IOMUX_FUNCTION_CLOCK_FREQ_CACHE.store( + iomux_function_clock_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_esp32c6.rs b/esp-metadata-generated/src/_generated_esp32c6.rs index b43d4ca4fdc..6b7d78feda2 100644 --- a/esp-metadata-generated/src/_generated_esp32c6.rs +++ b/esp-metadata-generated/src/_generated_esp32c6.rs @@ -1364,6 +1364,20 @@ macro_rules! for_each_wakeup_source { /// todo!() /// } /// +/// // IOMUX_FUNCTION_CLOCK +/// +/// fn enable_iomux_function_clock_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_iomux_function_clock_impl( +/// _clocks: &mut ClockTree, +/// _old_config: Option, +/// _new_config: IomuxFunctionClockConfig, +/// ) { +/// todo!() +/// } +/// /// // LEDC_SCLK /// /// fn enable_ledc_sclk_impl(_clocks: &mut ClockTree, _en: bool) { @@ -1994,6 +2008,18 @@ macro_rules! define_clock_tree_types { self.divisor as u32 } } + /// The list of clock signals that the `IOMUX_FUNCTION_CLOCK` multiplexer can output. + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum IomuxFunctionClockConfig { + #[default] + /// Selects `PLL_F80M`. + PllF80m, + /// Selects `RC_FAST_CLK`. + RcFastClk, + /// Selects `XTAL_CLK`. + XtalClk, + } /// The list of clock signals that the `LEDC_SCLK` multiplexer can output. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -2256,6 +2282,7 @@ macro_rules! define_clock_tree_types { apb_clk: Option, mspi_fast_hs_clk: Option, mspi_fast_ls_clk: Option, + iomux_function_clock: Option, ledc_sclk: Option, lp_fast_clk: Option, lp_slow_clk: Option, @@ -2279,6 +2306,7 @@ macro_rules! define_clock_tree_types { pll_f48m_refcount: u32, pll_f80m_refcount: u32, pll_f240m_refcount: u32, + iomux_function_clock_refcount: u32, ledc_sclk_refcount: u32, lp_fast_clk_refcount: u32, timg_calibration_clock_refcount: u32, @@ -2350,6 +2378,10 @@ macro_rules! define_clock_tree_types { pub fn mspi_fast_ls_clk(&self) -> Option { self.mspi_fast_ls_clk } + /// Returns the current configuration of the IOMUX_FUNCTION_CLOCK clock tree node + pub fn iomux_function_clock(&self) -> Option { + self.iomux_function_clock + } /// Returns the current configuration of the LEDC_SCLK clock tree node pub fn ledc_sclk(&self) -> Option { self.ledc_sclk @@ -2438,6 +2470,7 @@ macro_rules! define_clock_tree_types { apb_clk: None, mspi_fast_hs_clk: None, mspi_fast_ls_clk: None, + iomux_function_clock: None, ledc_sclk: None, lp_fast_clk: None, lp_slow_clk: None, @@ -2461,6 +2494,7 @@ macro_rules! define_clock_tree_types { pll_f48m_refcount: 0, pll_f80m_refcount: 0, pll_f240m_refcount: 0, + iomux_function_clock_refcount: 0, ledc_sclk_refcount: 0, lp_fast_clk_refcount: 0, timg_calibration_clock_refcount: 0, @@ -2479,6 +2513,8 @@ macro_rules! define_clock_tree_types { ::core::sync::atomic::AtomicU32::new(0); static SOC_ROOT_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = ::core::sync::atomic::AtomicU32::new(0); + static IOMUX_FUNCTION_CLOCK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = + ::core::sync::atomic::AtomicU32::new(0); static LEDC_SCLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = ::core::sync::atomic::AtomicU32::new(0); static LP_FAST_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = @@ -3181,6 +3217,80 @@ macro_rules! define_clock_tree_types { pub fn pll_f240m_source_frequency() -> u32 { pll_clk_frequency() } + pub fn configure_iomux_function_clock( + clocks: &mut ClockTree, + new_selector: IomuxFunctionClockConfig, + ) { + let old_selector = clocks.iomux_function_clock.replace(new_selector); + refresh_iomux_function_clock_downstream(clocks); + if clocks.iomux_function_clock_refcount > 0 { + match new_selector { + IomuxFunctionClockConfig::PllF80m => request_pll_f80m(clocks), + IomuxFunctionClockConfig::RcFastClk => request_rc_fast_clk(clocks), + IomuxFunctionClockConfig::XtalClk => request_xtal_clk(clocks), + } + configure_iomux_function_clock_impl(clocks, old_selector, new_selector); + if let Some(old_selector) = old_selector { + match old_selector { + IomuxFunctionClockConfig::PllF80m => release_pll_f80m(clocks), + IomuxFunctionClockConfig::RcFastClk => release_rc_fast_clk(clocks), + IomuxFunctionClockConfig::XtalClk => release_xtal_clk(clocks), + } + } + } else { + configure_iomux_function_clock_impl(clocks, old_selector, new_selector); + } + } + pub fn iomux_function_clock_config( + clocks: &mut ClockTree, + ) -> Option { + clocks.iomux_function_clock + } + pub fn request_iomux_function_clock(clocks: &mut ClockTree) { + trace!("Requesting IOMUX_FUNCTION_CLOCK"); + if increment_reference_count(&mut clocks.iomux_function_clock_refcount) { + trace!("Enabling IOMUX_FUNCTION_CLOCK"); + match unwrap!(clocks.iomux_function_clock) { + IomuxFunctionClockConfig::PllF80m => request_pll_f80m(clocks), + IomuxFunctionClockConfig::RcFastClk => request_rc_fast_clk(clocks), + IomuxFunctionClockConfig::XtalClk => request_xtal_clk(clocks), + } + enable_iomux_function_clock_impl(clocks, true); + } + } + pub fn release_iomux_function_clock(clocks: &mut ClockTree) { + trace!("Releasing IOMUX_FUNCTION_CLOCK"); + if decrement_reference_count(&mut clocks.iomux_function_clock_refcount) { + trace!("Disabling IOMUX_FUNCTION_CLOCK"); + enable_iomux_function_clock_impl(clocks, false); + match unwrap!(clocks.iomux_function_clock) { + IomuxFunctionClockConfig::PllF80m => release_pll_f80m(clocks), + IomuxFunctionClockConfig::RcFastClk => release_rc_fast_clk(clocks), + IomuxFunctionClockConfig::XtalClk => release_xtal_clk(clocks), + } + } + } + #[allow(unused_variables)] + pub fn iomux_function_clock_config_frequency( + clocks: &mut ClockTree, + config: IomuxFunctionClockConfig, + ) -> u32 { + match config { + IomuxFunctionClockConfig::PllF80m => pll_f80m_frequency(), + IomuxFunctionClockConfig::RcFastClk => rc_fast_clk_frequency(), + IomuxFunctionClockConfig::XtalClk => xtal_clk_frequency(), + } + } + pub fn iomux_function_clock_frequency() -> u32 { + IOMUX_FUNCTION_CLOCK_FREQ_CACHE.load(::core::sync::atomic::Ordering::Acquire) + } + pub fn iomux_function_clock_source_frequency(source: IomuxFunctionClockConfig) -> u32 { + match source { + IomuxFunctionClockConfig::PllF80m => pll_f80m_frequency(), + IomuxFunctionClockConfig::RcFastClk => rc_fast_clk_frequency(), + IomuxFunctionClockConfig::XtalClk => xtal_clk_frequency(), + } + } pub fn configure_ledc_sclk(clocks: &mut ClockTree, new_selector: LedcSclkConfig) { let old_selector = clocks.ledc_sclk.replace(new_selector); refresh_ledc_sclk_downstream(clocks); @@ -4253,6 +4363,8 @@ macro_rules! define_clock_tree_types { pub mspi_fast_hs_clk: Option, /// `MSPI_FAST_LS_CLK` configuration. pub mspi_fast_ls_clk: Option, + /// `IOMUX_FUNCTION_CLOCK` configuration. + pub iomux_function_clock: Option, /// `LEDC_SCLK` configuration. pub ledc_sclk: Option, /// `LP_FAST_CLK` configuration. @@ -4270,6 +4382,9 @@ macro_rules! define_clock_tree_types { if let Some(config) = self.soc_root_clk { configure_soc_root_clk(clocks, config); } + if let Some(config) = self.iomux_function_clock { + configure_iomux_function_clock(clocks, config); + } if let Some(config) = self.ledc_sclk { configure_ledc_sclk(clocks, config); } @@ -4323,6 +4438,7 @@ macro_rules! define_clock_tree_types { ); } refresh_soc_root_clk_downstream(clocks); + refresh_iomux_function_clock_downstream(clocks); refresh_ledc_sclk_downstream(clocks); refresh_lp_fast_clk_downstream(clocks); for child_instance in [I2cInstance::I2c0] { @@ -4358,6 +4474,14 @@ macro_rules! define_clock_tree_types { } refresh_hp_root_clk_downstream(clocks); } + fn refresh_iomux_function_clock_downstream(clocks: &mut ClockTree) { + if let Some(config) = clocks.iomux_function_clock { + IOMUX_FUNCTION_CLOCK_FREQ_CACHE.store( + iomux_function_clock_config_frequency(clocks, config), + ::core::sync::atomic::Ordering::Release, + ); + } + } fn refresh_ledc_sclk_downstream(clocks: &mut ClockTree) { if let Some(config) = clocks.ledc_sclk { LEDC_SCLK_FREQ_CACHE.store( diff --git a/esp-metadata-generated/src/_generated_esp32h2.rs b/esp-metadata-generated/src/_generated_esp32h2.rs index b3151f6eaec..62a27cd800a 100644 --- a/esp-metadata-generated/src/_generated_esp32h2.rs +++ b/esp-metadata-generated/src/_generated_esp32h2.rs @@ -1163,6 +1163,20 @@ macro_rules! for_each_wakeup_source { /// todo!() /// } /// +/// // IOMUX_FUNCTION_CLOCK +/// +/// fn enable_iomux_function_clock_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_iomux_function_clock_impl( +/// _clocks: &mut ClockTree, +/// _old_config: Option, +/// _new_config: IomuxFunctionClockConfig, +/// ) { +/// todo!() +/// } +/// /// // XTAL32K_CLK /// /// fn enable_xtal32k_clk_impl(_clocks: &mut ClockTree, _en: bool) { @@ -1495,6 +1509,16 @@ macro_rules! define_clock_tree_types { } } } + /// The list of clock signals that the `IOMUX_FUNCTION_CLOCK` multiplexer can output. + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum IomuxFunctionClockConfig { + /// Selects `XTAL_CLK`. + XtalClk, + #[default] + /// Selects `PLL_F48M_CLK`. + PllF48m, + } /// The list of clock signals that the `HP_ROOT_CLK` multiplexer can output. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -1825,6 +1849,7 @@ macro_rules! define_clock_tree_types { /// Represents the device's clock tree. pub struct ClockTree { xtal_clk: Option, + iomux_function_clock: Option, hp_root_clk: Option, cpu_clk: Option, ahb_clk: Option, @@ -1845,6 +1870,7 @@ macro_rules! define_clock_tree_types { pll_f96m_clk_refcount: u32, pll_f48m_clk_refcount: u32, rc_fast_clk_refcount: u32, + iomux_function_clock_refcount: u32, xtal32k_clk_refcount: u32, hp_root_clk_refcount: u32, apb_clk_refcount: u32, @@ -1870,6 +1896,10 @@ macro_rules! define_clock_tree_types { pub fn xtal_clk(&self) -> Option { self.xtal_clk } + /// Returns the current configuration of the IOMUX_FUNCTION_CLOCK clock tree node + pub fn iomux_function_clock(&self) -> Option { + self.iomux_function_clock + } /// Returns the current configuration of the HP_ROOT_CLK clock tree node pub fn hp_root_clk(&self) -> Option { self.hp_root_clk @@ -1962,6 +1992,7 @@ macro_rules! define_clock_tree_types { static CLOCK_TREE: ::esp_sync::NonReentrantMutex = ::esp_sync::NonReentrantMutex::new(ClockTree { xtal_clk: None, + iomux_function_clock: None, hp_root_clk: None, cpu_clk: None, ahb_clk: None, @@ -1982,6 +2013,7 @@ macro_rules! define_clock_tree_types { pll_f96m_clk_refcount: 0, pll_f48m_clk_refcount: 0, rc_fast_clk_refcount: 0, + iomux_function_clock_refcount: 0, xtal32k_clk_refcount: 0, hp_root_clk_refcount: 0, apb_clk_refcount: 0, @@ -2000,6 +2032,8 @@ macro_rules! define_clock_tree_types { }); static XTAL_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = ::core::sync::atomic::AtomicU32::new(0); + static IOMUX_FUNCTION_CLOCK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = + ::core::sync::atomic::AtomicU32::new(0); static HP_ROOT_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = ::core::sync::atomic::AtomicU32::new(0); static CPU_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = @@ -2130,6 +2164,74 @@ macro_rules! define_clock_tree_types { pub fn rc_fast_clk_frequency() -> u32 { 8000000 } + pub fn configure_iomux_function_clock( + clocks: &mut ClockTree, + new_selector: IomuxFunctionClockConfig, + ) { + let old_selector = clocks.iomux_function_clock.replace(new_selector); + refresh_iomux_function_clock_downstream(clocks); + if clocks.iomux_function_clock_refcount > 0 { + match new_selector { + IomuxFunctionClockConfig::XtalClk => request_xtal_clk(clocks), + IomuxFunctionClockConfig::PllF48m => request_pll_f48m_clk(clocks), + } + configure_iomux_function_clock_impl(clocks, old_selector, new_selector); + if let Some(old_selector) = old_selector { + match old_selector { + IomuxFunctionClockConfig::XtalClk => release_xtal_clk(clocks), + IomuxFunctionClockConfig::PllF48m => release_pll_f48m_clk(clocks), + } + } + } else { + configure_iomux_function_clock_impl(clocks, old_selector, new_selector); + } + } + pub fn iomux_function_clock_config( + clocks: &mut ClockTree, + ) -> Option { + clocks.iomux_function_clock + } + pub fn request_iomux_function_clock(clocks: &mut ClockTree) { + trace!("Requesting IOMUX_FUNCTION_CLOCK"); + if increment_reference_count(&mut clocks.iomux_function_clock_refcount) { + trace!("Enabling IOMUX_FUNCTION_CLOCK"); + match unwrap!(clocks.iomux_function_clock) { + IomuxFunctionClockConfig::XtalClk => request_xtal_clk(clocks), + IomuxFunctionClockConfig::PllF48m => request_pll_f48m_clk(clocks), + } + enable_iomux_function_clock_impl(clocks, true); + } + } + pub fn release_iomux_function_clock(clocks: &mut ClockTree) { + trace!("Releasing IOMUX_FUNCTION_CLOCK"); + if decrement_reference_count(&mut clocks.iomux_function_clock_refcount) { + trace!("Disabling IOMUX_FUNCTION_CLOCK"); + enable_iomux_function_clock_impl(clocks, false); + match unwrap!(clocks.iomux_function_clock) { + IomuxFunctionClockConfig::XtalClk => release_xtal_clk(clocks), + IomuxFunctionClockConfig::PllF48m => release_pll_f48m_clk(clocks), + } + } + } + #[allow(unused_variables)] + pub fn iomux_function_clock_config_frequency( + clocks: &mut ClockTree, + config: IomuxFunctionClockConfig, + ) -> u32 { + match config { + IomuxFunctionClockConfig::XtalClk => xtal_clk_frequency(), + IomuxFunctionClockConfig::PllF48m => pll_f48m_clk_frequency(), + } + } + pub fn iomux_function_clock_frequency() -> u32 { + IOMUX_FUNCTION_CLOCK_FREQ_CACHE.load(::core::sync::atomic::Ordering::Acquire) + } + pub fn iomux_function_clock_source_frequency(source: IomuxFunctionClockConfig) -> u32 { + match source { + IomuxFunctionClockConfig::XtalClk => xtal_clk_frequency(), + IomuxFunctionClockConfig::PllF48m => pll_f48m_clk_frequency(), + } + } pub fn request_xtal32k_clk(clocks: &mut ClockTree) { trace!("Requesting XTAL32K_CLK"); if increment_reference_count(&mut clocks.xtal32k_clk_refcount) { @@ -3330,6 +3432,8 @@ macro_rules! define_clock_tree_types { pub struct ClockConfig { /// `XTAL_CLK` configuration. pub xtal_clk: Option, + /// `IOMUX_FUNCTION_CLOCK` configuration. + pub iomux_function_clock: Option, /// `HP_ROOT_CLK` configuration. pub hp_root_clk: Option, /// `CPU_CLK` configuration. @@ -3350,6 +3454,9 @@ macro_rules! define_clock_tree_types { if let Some(config) = self.xtal_clk { configure_xtal_clk(clocks, config); } + if let Some(config) = self.iomux_function_clock { + configure_iomux_function_clock(clocks, config); + } if let Some(config) = self.hp_root_clk { configure_hp_root_clk(clocks, config); } @@ -3390,6 +3497,7 @@ macro_rules! define_clock_tree_types { ::core::sync::atomic::Ordering::Release, ); } + refresh_iomux_function_clock_downstream(clocks); refresh_hp_root_clk_downstream(clocks); refresh_lp_fast_clk_downstream(clocks); for child_instance in [I2cInstance::I2c0, I2cInstance::I2c1] { @@ -3416,6 +3524,14 @@ macro_rules! define_clock_tree_types { refresh_uart_function_clock_downstream(clocks, child_instance); } } + fn refresh_iomux_function_clock_downstream(clocks: &mut ClockTree) { + if let Some(config) = clocks.iomux_function_clock { + IOMUX_FUNCTION_CLOCK_FREQ_CACHE.store( + iomux_function_clock_config_frequency(clocks, config), + ::core::sync::atomic::Ordering::Release, + ); + } + } fn refresh_hp_root_clk_downstream(clocks: &mut ClockTree) { if let Some(config) = clocks.hp_root_clk { HP_ROOT_CLK_FREQ_CACHE.store( diff --git a/esp-metadata-generated/src/_generated_esp32p4.rs b/esp-metadata-generated/src/_generated_esp32p4.rs index 75b635964db..1bd8dedbfd9 100644 --- a/esp-metadata-generated/src/_generated_esp32p4.rs +++ b/esp-metadata-generated/src/_generated_esp32p4.rs @@ -1305,6 +1305,20 @@ macro_rules! for_each_wakeup_source { /// todo!() /// } /// +/// // IOMUX_FUNCTION_CLOCK +/// +/// fn enable_iomux_function_clock_impl(_clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_iomux_function_clock_impl( +/// _clocks: &mut ClockTree, +/// _old_config: Option, +/// _new_config: IomuxFunctionClockConfig, +/// ) { +/// todo!() +/// } +/// /// // PLL_F25M /// /// fn enable_pll_f25m_impl(_clocks: &mut ClockTree, _en: bool) { @@ -1664,6 +1678,16 @@ macro_rules! define_clock_tree_types { } } } + /// The list of clock signals that the `IOMUX_FUNCTION_CLOCK` multiplexer can output. + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum IomuxFunctionClockConfig { + /// Selects `XTAL_CLK`. + XtalClk, + #[default] + /// Selects `PLL_F80M`. + PllF80m, + } /// The list of clock signals that the `CPU_ROOT_CLK` multiplexer can output. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -2074,6 +2098,7 @@ macro_rules! define_clock_tree_types { cpll_clk: Option, spll_clk: Option, mpll_clk: Option, + iomux_function_clock: Option, cpu_root_clk: Option, cpu_clk: Option, mem_clk: Option, @@ -2099,6 +2124,7 @@ macro_rules! define_clock_tree_types { rc32k_clk_refcount: u32, pll_f80m_refcount: u32, pll_f120m_refcount: u32, + iomux_function_clock_refcount: u32, pll_f50m_refcount: u32, apb_clk_refcount: u32, lp_fast_clk_refcount: u32, @@ -2130,6 +2156,10 @@ macro_rules! define_clock_tree_types { pub fn mpll_clk(&self) -> Option { self.mpll_clk } + /// Returns the current configuration of the IOMUX_FUNCTION_CLOCK clock tree node + pub fn iomux_function_clock(&self) -> Option { + self.iomux_function_clock + } /// Returns the current configuration of the CPU_ROOT_CLK clock tree node pub fn cpu_root_clk(&self) -> Option { self.cpu_root_clk @@ -2252,6 +2282,7 @@ macro_rules! define_clock_tree_types { cpll_clk: None, spll_clk: None, mpll_clk: None, + iomux_function_clock: None, cpu_root_clk: None, cpu_clk: None, mem_clk: None, @@ -2277,6 +2308,7 @@ macro_rules! define_clock_tree_types { rc32k_clk_refcount: 0, pll_f80m_refcount: 0, pll_f120m_refcount: 0, + iomux_function_clock_refcount: 0, pll_f50m_refcount: 0, apb_clk_refcount: 0, lp_fast_clk_refcount: 0, @@ -2297,6 +2329,8 @@ macro_rules! define_clock_tree_types { ::core::sync::atomic::AtomicU32::new(0); static MPLL_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = ::core::sync::atomic::AtomicU32::new(0); + static IOMUX_FUNCTION_CLOCK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = + ::core::sync::atomic::AtomicU32::new(0); static CPU_ROOT_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = ::core::sync::atomic::AtomicU32::new(0); static CPU_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = @@ -2613,6 +2647,74 @@ macro_rules! define_clock_tree_types { pub fn pll_f240m_source_frequency() -> u32 { spll_clk_frequency() } + pub fn configure_iomux_function_clock( + clocks: &mut ClockTree, + new_selector: IomuxFunctionClockConfig, + ) { + let old_selector = clocks.iomux_function_clock.replace(new_selector); + refresh_iomux_function_clock_downstream(clocks); + if clocks.iomux_function_clock_refcount > 0 { + match new_selector { + IomuxFunctionClockConfig::XtalClk => request_xtal_clk(clocks), + IomuxFunctionClockConfig::PllF80m => request_pll_f80m(clocks), + } + configure_iomux_function_clock_impl(clocks, old_selector, new_selector); + if let Some(old_selector) = old_selector { + match old_selector { + IomuxFunctionClockConfig::XtalClk => release_xtal_clk(clocks), + IomuxFunctionClockConfig::PllF80m => release_pll_f80m(clocks), + } + } + } else { + configure_iomux_function_clock_impl(clocks, old_selector, new_selector); + } + } + pub fn iomux_function_clock_config( + clocks: &mut ClockTree, + ) -> Option { + clocks.iomux_function_clock + } + pub fn request_iomux_function_clock(clocks: &mut ClockTree) { + trace!("Requesting IOMUX_FUNCTION_CLOCK"); + if increment_reference_count(&mut clocks.iomux_function_clock_refcount) { + trace!("Enabling IOMUX_FUNCTION_CLOCK"); + match unwrap!(clocks.iomux_function_clock) { + IomuxFunctionClockConfig::XtalClk => request_xtal_clk(clocks), + IomuxFunctionClockConfig::PllF80m => request_pll_f80m(clocks), + } + enable_iomux_function_clock_impl(clocks, true); + } + } + pub fn release_iomux_function_clock(clocks: &mut ClockTree) { + trace!("Releasing IOMUX_FUNCTION_CLOCK"); + if decrement_reference_count(&mut clocks.iomux_function_clock_refcount) { + trace!("Disabling IOMUX_FUNCTION_CLOCK"); + enable_iomux_function_clock_impl(clocks, false); + match unwrap!(clocks.iomux_function_clock) { + IomuxFunctionClockConfig::XtalClk => release_xtal_clk(clocks), + IomuxFunctionClockConfig::PllF80m => release_pll_f80m(clocks), + } + } + } + #[allow(unused_variables)] + pub fn iomux_function_clock_config_frequency( + clocks: &mut ClockTree, + config: IomuxFunctionClockConfig, + ) -> u32 { + match config { + IomuxFunctionClockConfig::XtalClk => xtal_clk_frequency(), + IomuxFunctionClockConfig::PllF80m => pll_f80m_frequency(), + } + } + pub fn iomux_function_clock_frequency() -> u32 { + IOMUX_FUNCTION_CLOCK_FREQ_CACHE.load(::core::sync::atomic::Ordering::Acquire) + } + pub fn iomux_function_clock_source_frequency(source: IomuxFunctionClockConfig) -> u32 { + match source { + IomuxFunctionClockConfig::XtalClk => xtal_clk_frequency(), + IomuxFunctionClockConfig::PllF80m => pll_f80m_frequency(), + } + } pub fn request_pll_f25m(clocks: &mut ClockTree) { trace!("Requesting PLL_F25M"); trace!("Enabling PLL_F25M"); @@ -3775,6 +3877,8 @@ macro_rules! define_clock_tree_types { pub spll_clk: Option, /// `MPLL_CLK` configuration. pub mpll_clk: Option, + /// `IOMUX_FUNCTION_CLOCK` configuration. + pub iomux_function_clock: Option, /// `CPU_ROOT_CLK` configuration. pub cpu_root_clk: Option, /// `CPU_CLK` configuration. @@ -3803,6 +3907,9 @@ macro_rules! define_clock_tree_types { if let Some(config) = self.mpll_clk { configure_mpll_clk(clocks, config); } + if let Some(config) = self.iomux_function_clock { + configure_iomux_function_clock(clocks, config); + } if let Some(config) = self.cpu_root_clk { configure_cpu_root_clk(clocks, config); } @@ -3859,6 +3966,7 @@ macro_rules! define_clock_tree_types { ::core::sync::atomic::Ordering::Release, ); } + refresh_iomux_function_clock_downstream(clocks); refresh_timg_calibration_clock_downstream(clocks); for child_instance in [TimgInstance::Timg0, TimgInstance::Timg1] { refresh_timg_function_clock_downstream(clocks, child_instance); @@ -3895,6 +4003,14 @@ macro_rules! define_clock_tree_types { refresh_mipi_dsi_phy_cfg_clk_downstream(clocks, child_instance); } } + fn refresh_iomux_function_clock_downstream(clocks: &mut ClockTree) { + if let Some(config) = clocks.iomux_function_clock { + IOMUX_FUNCTION_CLOCK_FREQ_CACHE.store( + iomux_function_clock_config_frequency(clocks, config), + ::core::sync::atomic::Ordering::Release, + ); + } + } fn refresh_cpu_root_clk_downstream(clocks: &mut ClockTree) { if let Some(config) = clocks.cpu_root_clk { CPU_ROOT_CLK_FREQ_CACHE.store( diff --git a/esp-metadata/devices/esp32c5/clocks.toml b/esp-metadata/devices/esp32c5/clocks.toml index a844591abe3..717c2fe6e0d 100644 --- a/esp-metadata/devices/esp32c5/clocks.toml +++ b/esp-metadata/devices/esp32c5/clocks.toml @@ -57,6 +57,11 @@ system_clocks = { clock_tree = [ { name = "FOSC", outputs = "RC_FAST_CLK" }, { name = "PLL_F480M", outputs = "PLL_CLK" }, ] }, + { name = "IOMUX_FUNCTION_CLOCK", type = "mux", variants = [ + { name = "XTAL_CLK", outputs = "XTAL_CLK" }, + { name = "RC_FAST_CLK", outputs = "RC_FAST_CLK" }, + { name = "PLL_F80M", outputs = "PLL_F80M", default = true }, + ] }, { name = "TIMG_CALIBRATION_CLOCK", type = "mux", wake_locking = true, variants = [ # PCR_32K_SEL / clk_tg_xtal32k { name = "OSC_SLOW_CLK", outputs = "OSC_SLOW_CLK" }, { name = "RC_SLOW_CLK", outputs = "RC_SLOW_CLK" }, diff --git a/esp-metadata/devices/esp32c6/clocks.toml b/esp-metadata/devices/esp32c6/clocks.toml index 7e4fe239d11..1d00471cdcd 100644 --- a/esp-metadata/devices/esp32c6/clocks.toml +++ b/esp-metadata/devices/esp32c6/clocks.toml @@ -45,6 +45,11 @@ system_clocks = { clock_tree = [ { name = "PLL_F80M", type = "derived", from = "PLL_CLK", output = "80_000_000" }, { name = "PLL_F160M", type = "derived", from = "PLL_CLK", output = "160_000_000" }, { name = "PLL_F240M", type = "derived", from = "PLL_CLK", output = "240_000_000" }, + { name = "IOMUX_FUNCTION_CLOCK", type = "mux", variants = [ + { name = "PLL_F80M", outputs = "PLL_F80M", default = true }, + { name = "RC_FAST_CLK", outputs = "RC_FAST_CLK" }, + { name = "XTAL_CLK", outputs = "XTAL_CLK" }, + ] }, { name = "LEDC_SCLK", type = "mux", variants = [ { name = "PLL_F80M", outputs = "PLL_F80M" }, { name = "RC_FAST_CLK", outputs = "RC_FAST_CLK" }, diff --git a/esp-metadata/devices/esp32h2/clocks.toml b/esp-metadata/devices/esp32h2/clocks.toml index 332c30d4092..ec18b6576ea 100644 --- a/esp-metadata/devices/esp32h2/clocks.toml +++ b/esp-metadata/devices/esp32h2/clocks.toml @@ -5,6 +5,11 @@ system_clocks = { clock_tree = [ { name = "PLL_F64M_CLK", type = "generic", output = "PLL_F96M_CLK * 2 / 3" }, { name = "PLL_F48M_CLK", type = "generic", output = "PLL_F96M_CLK / 2" }, { name = "RC_FAST_CLK", type = "source", output = "8_000_000" }, + + { name = "IOMUX_FUNCTION_CLOCK", type = "mux", variants = [ + { name = "XTAL_CLK", outputs = "XTAL_CLK" }, + { name = "PLL_F48M", outputs = "PLL_F48M_CLK", default = true }, + ] }, # Low-speed clocks { name = "XTAL32K_CLK", type = "source", output = "32768" }, diff --git a/esp-metadata/devices/esp32p4/clocks.toml b/esp-metadata/devices/esp32p4/clocks.toml index 850cc46a65e..b14423fbc96 100644 --- a/esp-metadata/devices/esp32p4/clocks.toml +++ b/esp-metadata/devices/esp32p4/clocks.toml @@ -19,6 +19,11 @@ system_clocks = { clock_tree = [ { name = "PLL_F160M", type = "generic", output = "SPLL_CLK / 3" }, { name = "PLL_F240M", type = "generic", output = "SPLL_CLK / 2" }, + { name = "IOMUX_FUNCTION_CLOCK", type = "mux", variants = [ + { name = "XTAL_CLK", outputs = "XTAL_CLK" }, + { name = "PLL_F80M", outputs = "PLL_F80M", default = true }, + ] }, + # MPLL divider taps { name = "PLL_F25M", type = "generic", output = "MPLL_CLK / 20" }, { name = "PLL_F50M", type = "generic", output = "MPLL_CLK / 10" }, diff --git a/hil-test/src/bin/sdm.rs b/hil-test/src/bin/sdm.rs index 2801203b17c..ae069cb3fa4 100644 --- a/hil-test/src/bin/sdm.rs +++ b/hil-test/src/bin/sdm.rs @@ -225,10 +225,7 @@ fn measure_high_ratio(ctx: &mut Context, duty: u8) -> Measurement { .with_frequency(SDM_FREQUENCY) .unwrap() .with_duty(duty); - let channel = sdm - .channel0 - .connect(ctx.sdm_pin.reborrow(), config) - .unwrap(); + let channel = sdm.channel0.connect(ctx.sdm_pin.reborrow(), config); Delay::new().delay_micros(SDM_WARM_UP_US); From 53179722fde55eceada76a2b4b4a7583e9ba879a Mon Sep 17 00:00:00 2001 From: tzyt Date: Sat, 18 Jul 2026 14:35:37 -0500 Subject: [PATCH 13/26] use builderlite for appropriate fields --- esp-hal/src/sdm.rs | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/esp-hal/src/sdm.rs b/esp-hal/src/sdm.rs index 235aaf99ccc..37554a5d7b6 100644 --- a/esp-hal/src/sdm.rs +++ b/esp-hal/src/sdm.rs @@ -111,20 +111,12 @@ for_each_sdm_channel!( ); /// Sigma-delta peripheral configuration. -#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, procmacros::BuilderLite)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub struct SdmConfig { /// Clock source used by the shared SDM/IO_MUX clock. - pub clock_source: ClockSource, -} - -impl SdmConfig { - /// Selects the source clock used by all SDM channels. - pub const fn with_clock_source(mut self, clock_source: ClockSource) -> Self { - self.clock_source = clock_source; - self - } + clock_source: ClockSource, } /// Source clock for the shared SDM/IO_MUX clock. @@ -199,11 +191,15 @@ impl core::error::Error for Error {} /// The hardware stores the prescaler and pulse density in the same register, /// so applying a complete channel configuration can update both fields with a /// single register write. -#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, procmacros::BuilderLite)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub struct ChannelConfig { + /// Raw hardware prescaler value. + #[builder_lite(skip)] raw_prescaler: u8, + + /// Pulse density in the hardware range `-128..=127`. pulse_density: i8, } @@ -217,14 +213,6 @@ impl ChannelConfig { Ok(self) } - /// Sets the pulse density. - /// - /// The value ranges is `-128..=127`. - pub const fn with_pulse_density(mut self, density: i8) -> Self { - self.pulse_density = density; - self - } - /// Sets duty cycle. `0` maps to the minimum density and `255` maps to the /// maximum density. pub const fn with_duty(mut self, duty: u8) -> Self { From 259a74d81430159a14101e5ab986e078039a5ff9 Mon Sep 17 00:00:00 2001 From: tzyt Date: Sat, 18 Jul 2026 15:35:44 -0500 Subject: [PATCH 14/26] add comments and changelog --- esp-hal/CHANGELOG.md | 1 + esp-hal/src/sdm.rs | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 86272120800..6cf441b0f96 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `handle_gpio_interrupt` and `wake_pin()` for user-defined GPIO ISRs (#5531) - GPIO: `Input::wait_for_with_options()` allows waking from light sleep while waiting for event (#5551) - Add ESP32-S31 support (#5922) +- SDM Driver (#5610) ### Changed diff --git a/esp-hal/src/sdm.rs b/esp-hal/src/sdm.rs index 37554a5d7b6..e1af680e84e 100644 --- a/esp-hal/src/sdm.rs +++ b/esp-hal/src/sdm.rs @@ -26,7 +26,7 @@ //! //! channel.set_duty(192); // duty ranges from 0 to 255 //! channel.set_pulse_density(0); // pulse density ranges from -128 to 127 -//! // +//! //! # {after_snippet} //! ``` @@ -90,6 +90,10 @@ for_each_sdm_channel!( /// Creates a channel configuration builder. pub const fn channel_config(&self) -> ChannelConfigBuilder { + // this ensures that channels can be configured only after that SDM + // have been initialized with a selected clock source. Since + // `with_frequency` requires a clock source to calculate the prescaler, + // it is important that the SDM is initialized first. ChannelConfigBuilder::new() } } @@ -231,6 +235,7 @@ pub struct ChannelConfigBuilder { impl ChannelConfigBuilder { const fn new() -> Self { + // see `Sdm::channel_config()` for why this is not public Self { config: ChannelConfig { raw_prescaler: 0, From 4ea4aa52d7713a9ca7421e619e019b9a8950cf25 Mon Sep 17 00:00:00 2001 From: tzyt Date: Sat, 18 Jul 2026 15:43:58 -0500 Subject: [PATCH 15/26] Remove direct changelog edit --- esp-hal/CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 6cf441b0f96..86272120800 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -23,7 +23,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `handle_gpio_interrupt` and `wake_pin()` for user-defined GPIO ISRs (#5531) - GPIO: `Input::wait_for_with_options()` allows waking from light sleep while waiting for event (#5551) - Add ESP32-S31 support (#5922) -- SDM Driver (#5610) ### Changed From 6d5382369f79749a20dea19fbe1eef00d0b44fe6 Mon Sep 17 00:00:00 2001 From: tzyt Date: Thu, 30 Jul 2026 23:20:41 +0800 Subject: [PATCH 16/26] remove unused metadata --- .../src/_build_script_utils.rs | 45 ------------------- .../src/_generated_esp32.rs | 3 -- .../src/_generated_esp32c3.rs | 3 -- .../src/_generated_esp32c5.rs | 3 -- .../src/_generated_esp32c6.rs | 3 -- .../src/_generated_esp32h2.rs | 3 -- .../src/_generated_esp32p4.rs | 17 +++++-- .../src/_generated_esp32s2.rs | 3 -- .../src/_generated_esp32s3.rs | 3 -- esp-metadata/devices/esp32/soc.toml | 2 - esp-metadata/devices/esp32c3/soc.toml | 2 - esp-metadata/devices/esp32c5/soc.toml | 2 - esp-metadata/devices/esp32c6/soc.toml | 2 - esp-metadata/devices/esp32h2/soc.toml | 2 - esp-metadata/devices/esp32p4/soc.toml | 2 - esp-metadata/devices/esp32s2/soc.toml | 2 - esp-metadata/devices/esp32s3/soc.toml | 2 - esp-metadata/src/cfg.rs | 5 --- 18 files changed, 14 insertions(+), 90 deletions(-) diff --git a/esp-metadata-generated/src/_build_script_utils.rs b/esp-metadata-generated/src/_build_script_utils.rs index 8c02b4ac27a..76805d03d25 100644 --- a/esp-metadata-generated/src/_build_script_utils.rs +++ b/esp-metadata-generated/src/_build_script_utils.rs @@ -378,8 +378,6 @@ impl Chip { "rsa_size_increment=\"512\"", "rsa_memory_size_bytes=\"512\"", "sdmmc_has_iomux", - "sdm_clock_sources_apb", - "sdm_default_clock_source=\"apb\"", "sleep_light_sleep", "sleep_deep_sleep", "sleep_has_wakeup_source_ext0", @@ -616,8 +614,6 @@ impl Chip { "cargo:rustc-cfg=rsa_size_increment=\"512\"", "cargo:rustc-cfg=rsa_memory_size_bytes=\"512\"", "cargo:rustc-cfg=sdmmc_has_iomux", - "cargo:rustc-cfg=sdm_clock_sources_apb", - "cargo:rustc-cfg=sdm_default_clock_source=\"apb\"", "cargo:rustc-cfg=sleep_light_sleep", "cargo:rustc-cfg=sleep_deep_sleep", "cargo:rustc-cfg=sleep_has_wakeup_source_ext0", @@ -1494,8 +1490,6 @@ impl Chip { "rsa_version=\"3\"", "rsa_size_increment=\"32\"", "rsa_memory_size_bytes=\"384\"", - "sdm_clock_sources_apb", - "sdm_default_clock_source=\"apb\"", "sleep_light_sleep", "sleep_deep_sleep", "sleep_has_wakeup_source_gpio", @@ -1741,8 +1735,6 @@ impl Chip { "cargo:rustc-cfg=rsa_version=\"3\"", "cargo:rustc-cfg=rsa_size_increment=\"32\"", "cargo:rustc-cfg=rsa_memory_size_bytes=\"384\"", - "cargo:rustc-cfg=sdm_clock_sources_apb", - "cargo:rustc-cfg=sdm_default_clock_source=\"apb\"", "cargo:rustc-cfg=sleep_light_sleep", "cargo:rustc-cfg=sleep_deep_sleep", "cargo:rustc-cfg=sleep_has_wakeup_source_gpio", @@ -2132,9 +2124,6 @@ impl Chip { "rsa_version=\"3\"", "rsa_size_increment=\"32\"", "rsa_memory_size_bytes=\"384\"", - "sdm_clock_sources_xtal", - "sdm_clock_sources_pll_f80m", - "sdm_default_clock_source=\"pll_f80m\"", "sleep_light_sleep", "sleep_deep_sleep", "sleep_has_wakeup_source_ext1", @@ -2427,9 +2416,6 @@ impl Chip { "cargo:rustc-cfg=rsa_version=\"3\"", "cargo:rustc-cfg=rsa_size_increment=\"32\"", "cargo:rustc-cfg=rsa_memory_size_bytes=\"384\"", - "cargo:rustc-cfg=sdm_clock_sources_xtal", - "cargo:rustc-cfg=sdm_clock_sources_pll_f80m", - "cargo:rustc-cfg=sdm_default_clock_source=\"pll_f80m\"", "cargo:rustc-cfg=sleep_light_sleep", "cargo:rustc-cfg=sleep_deep_sleep", "cargo:rustc-cfg=sleep_has_wakeup_source_ext1", @@ -2873,9 +2859,6 @@ impl Chip { "rsa_version=\"3\"", "rsa_size_increment=\"32\"", "rsa_memory_size_bytes=\"384\"", - "sdm_clock_sources_xtal", - "sdm_clock_sources_pll_f80m", - "sdm_default_clock_source=\"pll_f80m\"", "sleep_light_sleep", "sleep_deep_sleep", "sleep_has_wakeup_source_ext1", @@ -3182,9 +3165,6 @@ impl Chip { "cargo:rustc-cfg=rsa_version=\"3\"", "cargo:rustc-cfg=rsa_size_increment=\"32\"", "cargo:rustc-cfg=rsa_memory_size_bytes=\"384\"", - "cargo:rustc-cfg=sdm_clock_sources_xtal", - "cargo:rustc-cfg=sdm_clock_sources_pll_f80m", - "cargo:rustc-cfg=sdm_default_clock_source=\"pll_f80m\"", "cargo:rustc-cfg=sleep_light_sleep", "cargo:rustc-cfg=sleep_deep_sleep", "cargo:rustc-cfg=sleep_has_wakeup_source_ext1", @@ -4218,9 +4198,6 @@ impl Chip { "rsa_version=\"3\"", "rsa_size_increment=\"32\"", "rsa_memory_size_bytes=\"384\"", - "sdm_clock_sources_xtal", - "sdm_clock_sources_pll_f48m", - "sdm_default_clock_source=\"pll_f48m\"", "sleep_light_sleep", "sleep_deep_sleep", "sleep_has_wakeup_source_ext1", @@ -4498,9 +4475,6 @@ impl Chip { "cargo:rustc-cfg=rsa_version=\"3\"", "cargo:rustc-cfg=rsa_size_increment=\"32\"", "cargo:rustc-cfg=rsa_memory_size_bytes=\"384\"", - "cargo:rustc-cfg=sdm_clock_sources_xtal", - "cargo:rustc-cfg=sdm_clock_sources_pll_f48m", - "cargo:rustc-cfg=sdm_default_clock_source=\"pll_f48m\"", "cargo:rustc-cfg=sleep_light_sleep", "cargo:rustc-cfg=sleep_deep_sleep", "cargo:rustc-cfg=sleep_has_wakeup_source_ext1", @@ -4874,9 +4848,6 @@ impl Chip { "sdmmc_has_iomux", "sdmmc_has_gpio_matrix", "sdmmc_psram_dma", - "sdm_clock_sources_xtal", - "sdm_clock_sources_pll_f80m", - "sdm_default_clock_source=\"pll_f80m\"", "sleep_light_sleep", "sleep_deep_sleep", "sleep_has_wakeup_source_sdio", @@ -5145,9 +5116,6 @@ impl Chip { "cargo:rustc-cfg=sdmmc_has_iomux", "cargo:rustc-cfg=sdmmc_has_gpio_matrix", "cargo:rustc-cfg=sdmmc_psram_dma", - "cargo:rustc-cfg=sdm_clock_sources_xtal", - "cargo:rustc-cfg=sdm_clock_sources_pll_f80m", - "cargo:rustc-cfg=sdm_default_clock_source=\"pll_f80m\"", "cargo:rustc-cfg=sleep_light_sleep", "cargo:rustc-cfg=sleep_deep_sleep", "cargo:rustc-cfg=sleep_has_wakeup_source_sdio", @@ -5639,8 +5607,6 @@ impl Chip { "rsa_version=\"2\"", "rsa_size_increment=\"32\"", "rsa_memory_size_bytes=\"512\"", - "sdm_clock_sources_apb", - "sdm_default_clock_source=\"apb\"", "sleep_light_sleep", "sleep_deep_sleep", "sleep_has_wakeup_source_ext0", @@ -5888,8 +5854,6 @@ impl Chip { "cargo:rustc-cfg=rsa_version=\"2\"", "cargo:rustc-cfg=rsa_size_increment=\"32\"", "cargo:rustc-cfg=rsa_memory_size_bytes=\"512\"", - "cargo:rustc-cfg=sdm_clock_sources_apb", - "cargo:rustc-cfg=sdm_default_clock_source=\"apb\"", "cargo:rustc-cfg=sleep_light_sleep", "cargo:rustc-cfg=sleep_deep_sleep", "cargo:rustc-cfg=sleep_has_wakeup_source_ext0", @@ -6374,8 +6338,6 @@ impl Chip { "sdmmc_delay_phase_num=\"4\"", "sdmmc_delay_phase_num_is_set", "sdmmc_has_gpio_matrix", - "sdm_clock_sources_apb", - "sdm_default_clock_source=\"apb\"", "sleep_light_sleep", "sleep_deep_sleep", "sleep_has_wakeup_source_ext0", @@ -6670,8 +6632,6 @@ impl Chip { "cargo:rustc-cfg=sdmmc_delay_phase_num=\"4\"", "cargo:rustc-cfg=sdmmc_delay_phase_num_is_set", "cargo:rustc-cfg=sdmmc_has_gpio_matrix", - "cargo:rustc-cfg=sdm_clock_sources_apb", - "cargo:rustc-cfg=sdm_default_clock_source=\"apb\"", "cargo:rustc-cfg=sleep_light_sleep", "cargo:rustc-cfg=sleep_deep_sleep", "cargo:rustc-cfg=sleep_has_wakeup_source_ext0", @@ -7579,7 +7539,6 @@ impl Chip { "rom_has_crc_be", "rom_has_md5_bsd", "sdmmc_has_iomux", - "sdm_clock_sources_apb", "sleep_light_sleep", "sleep_deep_sleep", "sleep_has_wakeup_source_ext0", @@ -7774,8 +7733,6 @@ impl Chip { "i2s_clock_configured_by_pcr", "rmt_has_tx_loop_auto_stop", "rng_is_lp_sys", - "sdm_clock_sources_xtal", - "sdm_clock_sources_pll_f80m", "sleep_has_wakeup_source_wifi_beacon", "sleep_has_wakeup_source_lp_core", "sleep_has_wakeup_source_usb", @@ -7828,7 +7785,6 @@ impl Chip { "wifi_has_wifi6", "esp32c61", "esp32h2", - "sdm_clock_sources_pll_f48m", "soc_has_clock_node_pll_f96m_clk", "soc_has_clock_node_pll_f64m_clk", "soc_has_clock_node_pll_f48m_clk", @@ -7956,7 +7912,6 @@ impl Chip { "rsa_version, values(\"1\",\"3\",\"2\")", "rsa_size_increment, values(\"512\",\"32\")", "rsa_memory_size_bytes, values(\"512\",\"384\")", - "sdm_default_clock_source, values(\"apb\",\"pll_f80m\",\"pll_f48m\")", "soc_cpu_mcause_mask, values(\"0\",\"31\",\"63\")", "spi_master_version, values(\"1\",\"3\",\"2\")", "spi_master_fifo_size, values(\"64\",\"72\")", diff --git a/esp-metadata-generated/src/_generated_esp32.rs b/esp-metadata-generated/src/_generated_esp32.rs index b6bc388486c..21acb9368dd 100644 --- a/esp-metadata-generated/src/_generated_esp32.rs +++ b/esp-metadata-generated/src/_generated_esp32.rs @@ -361,9 +361,6 @@ macro_rules! property { ("sdm.channel_count", str) => { stringify!(8) }; - ("sdm.default_clock_source") => { - "apb" - }; ("sleep.light_sleep") => { true }; diff --git a/esp-metadata-generated/src/_generated_esp32c3.rs b/esp-metadata-generated/src/_generated_esp32c3.rs index 3ca29dd9c85..eab4ce698af 100644 --- a/esp-metadata-generated/src/_generated_esp32c3.rs +++ b/esp-metadata-generated/src/_generated_esp32c3.rs @@ -364,9 +364,6 @@ macro_rules! property { ("sdm.channel_count", str) => { stringify!(4) }; - ("sdm.default_clock_source") => { - "apb" - }; ("sleep.light_sleep") => { true }; diff --git a/esp-metadata-generated/src/_generated_esp32c5.rs b/esp-metadata-generated/src/_generated_esp32c5.rs index 8068ab675c4..d3bf487dbb6 100644 --- a/esp-metadata-generated/src/_generated_esp32c5.rs +++ b/esp-metadata-generated/src/_generated_esp32c5.rs @@ -382,9 +382,6 @@ macro_rules! property { ("sdm.channel_count", str) => { stringify!(4) }; - ("sdm.default_clock_source") => { - "pll_f80m" - }; ("sleep.light_sleep") => { true }; diff --git a/esp-metadata-generated/src/_generated_esp32c6.rs b/esp-metadata-generated/src/_generated_esp32c6.rs index b5b8cb50c33..1b35498e69f 100644 --- a/esp-metadata-generated/src/_generated_esp32c6.rs +++ b/esp-metadata-generated/src/_generated_esp32c6.rs @@ -391,9 +391,6 @@ macro_rules! property { ("sdm.channel_count", str) => { stringify!(4) }; - ("sdm.default_clock_source") => { - "pll_f80m" - }; ("sleep.light_sleep") => { true }; diff --git a/esp-metadata-generated/src/_generated_esp32h2.rs b/esp-metadata-generated/src/_generated_esp32h2.rs index 6392b11a144..138d304fba8 100644 --- a/esp-metadata-generated/src/_generated_esp32h2.rs +++ b/esp-metadata-generated/src/_generated_esp32h2.rs @@ -379,9 +379,6 @@ macro_rules! property { ("sdm.channel_count", str) => { stringify!(4) }; - ("sdm.default_clock_source") => { - "pll_f48m" - }; ("sleep.light_sleep") => { true }; diff --git a/esp-metadata-generated/src/_generated_esp32p4.rs b/esp-metadata-generated/src/_generated_esp32p4.rs index 0644227f715..4c20be723c4 100644 --- a/esp-metadata-generated/src/_generated_esp32p4.rs +++ b/esp-metadata-generated/src/_generated_esp32p4.rs @@ -331,9 +331,6 @@ macro_rules! property { ("sdm.channel_count", str) => { stringify!(8) }; - ("sdm.default_clock_source") => { - "pll_f80m" - }; ("sleep.light_sleep") => { true }; @@ -4200,6 +4197,8 @@ macro_rules! implement_peripheral_clocks { Emac, /// GPIO_SD peripheral clock signal GpioSd, + /// GPIO_SD peripheral clock signal + GpioSd, /// HMAC peripheral clock signal Hmac, /// I2C0 peripheral clock signal @@ -4292,6 +4291,7 @@ macro_rules! implement_peripheral_clocks { Self::Ecdsa, Self::Emac, Self::GpioSd, + Self::GpioSd, Self::Hmac, Self::I2c0, Self::I2c1, @@ -4380,6 +4380,14 @@ macro_rules! implement_peripheral_clocks { .sigmadelta_misc() .modify(|_, w| w.function_clk_en().bit(enable)); } + Peripheral::GpioSd => { + crate::peripherals::GPIO_SD::regs() + .clock_gate() + .modify(|_, w| w.clk_en().bit(enable)); + crate::peripherals::GPIO_SD::regs() + .sigmadelta_misc() + .modify(|_, w| w.function_clk_en().bit(enable)); + } Peripheral::Hmac => { crate::peripherals::HP_SYS_CLKRST::regs() .soc_clk_ctrl1() @@ -4631,6 +4639,9 @@ macro_rules! implement_peripheral_clocks { Peripheral::GpioSd => { let _ = reset; } + Peripheral::GpioSd => { + let _ = reset; + } Peripheral::Hmac => { crate::peripherals::HP_SYS_CLKRST::regs() .hp_rst_en2() diff --git a/esp-metadata-generated/src/_generated_esp32s2.rs b/esp-metadata-generated/src/_generated_esp32s2.rs index b0ae7e23ea6..e84837e3b1c 100644 --- a/esp-metadata-generated/src/_generated_esp32s2.rs +++ b/esp-metadata-generated/src/_generated_esp32s2.rs @@ -355,9 +355,6 @@ macro_rules! property { ("sdm.channel_count", str) => { stringify!(8) }; - ("sdm.default_clock_source") => { - "apb" - }; ("sleep.light_sleep") => { true }; diff --git a/esp-metadata-generated/src/_generated_esp32s3.rs b/esp-metadata-generated/src/_generated_esp32s3.rs index 3e8e2552968..3750412a045 100644 --- a/esp-metadata-generated/src/_generated_esp32s3.rs +++ b/esp-metadata-generated/src/_generated_esp32s3.rs @@ -388,9 +388,6 @@ macro_rules! property { ("sdm.channel_count", str) => { stringify!(8) }; - ("sdm.default_clock_source") => { - "apb" - }; ("sleep.light_sleep") => { true }; diff --git a/esp-metadata/devices/esp32/soc.toml b/esp-metadata/devices/esp32/soc.toml index 2f22dd7a0c2..8fc2ce01dfc 100644 --- a/esp-metadata/devices/esp32/soc.toml +++ b/esp-metadata/devices/esp32/soc.toml @@ -199,8 +199,6 @@ algo = { sha1 = 0, sha256 = 0, sha384 = 0, sha512 = 0 } # fake mode bits, ESP32 [device.sdm] support_status = "partial" channel_count = 8 -clock_sources = ["apb"] -default_clock_source = "apb" [device.spi_master] version = 1 diff --git a/esp-metadata/devices/esp32c3/soc.toml b/esp-metadata/devices/esp32c3/soc.toml index a2b2466afd0..3b324e25988 100644 --- a/esp-metadata/devices/esp32c3/soc.toml +++ b/esp-metadata/devices/esp32c3/soc.toml @@ -202,8 +202,6 @@ algo = { sha1 = 0, sha224 = 1, sha256 = 2 } [device.sdm] support_status = "partial" channel_count = 4 -clock_sources = ["apb"] -default_clock_source = "apb" [device.spi_master] version = 3 diff --git a/esp-metadata/devices/esp32c5/soc.toml b/esp-metadata/devices/esp32c5/soc.toml index c42090adde5..32117defaa1 100644 --- a/esp-metadata/devices/esp32c5/soc.toml +++ b/esp-metadata/devices/esp32c5/soc.toml @@ -340,8 +340,6 @@ support_status = { status = "not_supported", issue = 884 } [device.sdm] support_status = "partial" channel_count = 4 -clock_sources = ["xtal", "pll_f80m"] -default_clock_source = "pll_f80m" [device.lp_uart] support_status = { status = "not_supported", issue = 5155 } diff --git a/esp-metadata/devices/esp32c6/soc.toml b/esp-metadata/devices/esp32c6/soc.toml index 8ee8cb0af9c..8a1b91dbd89 100644 --- a/esp-metadata/devices/esp32c6/soc.toml +++ b/esp-metadata/devices/esp32c6/soc.toml @@ -254,8 +254,6 @@ algo = { sha1 = 0, sha224 = 1, sha256 = 2 } [device.sdm] support_status = "partial" channel_count = 4 -clock_sources = ["xtal", "pll_f80m"] -default_clock_source = "pll_f80m" [device.spi_master] version = 3 diff --git a/esp-metadata/devices/esp32h2/soc.toml b/esp-metadata/devices/esp32h2/soc.toml index 8cab5a5cc86..d68fde3c90e 100644 --- a/esp-metadata/devices/esp32h2/soc.toml +++ b/esp-metadata/devices/esp32h2/soc.toml @@ -243,8 +243,6 @@ algo = { sha1 = 0, sha224 = 1, sha256 = 2 } [device.sdm] support_status = "partial" channel_count = 4 -clock_sources = ["xtal", "pll_f48m"] -default_clock_source = "pll_f48m" [device.ecdsa] support_status = { status = "not_supported", issue = 5444 } diff --git a/esp-metadata/devices/esp32p4/soc.toml b/esp-metadata/devices/esp32p4/soc.toml index bd7dfc6c0e5..c62b76df4fa 100644 --- a/esp-metadata/devices/esp32p4/soc.toml +++ b/esp-metadata/devices/esp32p4/soc.toml @@ -397,8 +397,6 @@ slot_config = [ [device.sdm] support_status = "partial" channel_count = 8 -clock_sources = ["xtal", "pll_f80m"] -default_clock_source = "pll_f80m" [device.sleep] support_status = "partial" diff --git a/esp-metadata/devices/esp32s2/soc.toml b/esp-metadata/devices/esp32s2/soc.toml index ebf4cc5d5da..ad60f856ce2 100644 --- a/esp-metadata/devices/esp32s2/soc.toml +++ b/esp-metadata/devices/esp32s2/soc.toml @@ -229,8 +229,6 @@ algo = { sha1 = 0, sha224 = 1, sha256 = 2, sha384 = 3, sha512 = 4, sha512_224 = [device.sdm] support_status = "partial" channel_count = 8 -clock_sources = ["apb"] -default_clock_source = "apb" [device.spi_master] version = 2 diff --git a/esp-metadata/devices/esp32s3/soc.toml b/esp-metadata/devices/esp32s3/soc.toml index f718c3a7932..befc525fa9c 100644 --- a/esp-metadata/devices/esp32s3/soc.toml +++ b/esp-metadata/devices/esp32s3/soc.toml @@ -228,8 +228,6 @@ algo = { sha1 = 0, sha224 = 1, sha256 = 2, sha384 = 3, sha512 = 4, sha512_224 = [device.sdm] support_status = "partial" channel_count = 8 -clock_sources = ["apb"] -default_clock_source = "apb" [device.spi_master] version = 3 diff --git a/esp-metadata/src/cfg.rs b/esp-metadata/src/cfg.rs index 1afa433c1a1..244cced0a18 100644 --- a/esp-metadata/src/cfg.rs +++ b/esp-metadata/src/cfg.rs @@ -703,11 +703,6 @@ driver_configs![ properties: { /// Number of sigma-delta channels. channel_count: SdmChannels, - /// Supported source clocks for the shared SDM/IO_MUX clock. - #[serde(default)] - clock_sources: Vec, - /// Default source clock for the shared SDM/IO_MUX clock. - default_clock_source: String, } }, SleepProperties { From 8cd299ed8cf712ceb895c76a12dd565d787c604f Mon Sep 17 00:00:00 2001 From: tzyt Date: Fri, 31 Jul 2026 15:38:47 +0800 Subject: [PATCH 17/26] correct clock tree implementation for sdm Move the GPIO_SD function-clock gate out of the peripheral clock template and model it as an SDM clock-tree node. Use APB as its parent on older chips and IOMUX_FUNCTION_CLOCK on newer chips, with SoC-specific enable implementations. --- esp-hal/src/sdm.rs | 31 ++-- esp-hal/src/soc/esp32/clocks.rs | 16 ++ esp-hal/src/soc/esp32c3/clocks.rs | 18 ++ esp-hal/src/soc/esp32c5/clocks.rs | 18 ++ esp-hal/src/soc/esp32c6/clocks.rs | 18 ++ esp-hal/src/soc/esp32h2/clocks.rs | 18 ++ esp-hal/src/soc/esp32p4/clocks.rs | 18 ++ esp-hal/src/soc/esp32s2/clocks.rs | 18 ++ esp-hal/src/soc/esp32s3/clocks.rs | 18 ++ .../src/_build_script_utils.rs | 17 ++ .../src/_generated_esp32.rs | 93 +++++++++++ .../src/_generated_esp32c3.rs | 96 ++++++++++- .../src/_generated_esp32c5.rs | 152 +++++++++++++---- .../src/_generated_esp32c6.rs | 152 +++++++++++++---- .../src/_generated_esp32h2.rs | 144 ++++++++++++---- .../src/_generated_esp32p4.rs | 158 +++++++++++++----- .../src/_generated_esp32s2.rs | 96 ++++++++++- .../src/_generated_esp32s3.rs | 96 ++++++++++- esp-metadata/devices/esp32/clocks.toml | 3 + esp-metadata/devices/esp32/soc.toml | 2 +- esp-metadata/devices/esp32c3/clocks.toml | 5 +- esp-metadata/devices/esp32c3/soc.toml | 2 +- esp-metadata/devices/esp32c5/clocks.toml | 5 +- esp-metadata/devices/esp32c5/soc.toml | 2 +- esp-metadata/devices/esp32c6/clocks.toml | 5 +- esp-metadata/devices/esp32c6/soc.toml | 2 +- esp-metadata/devices/esp32h2/clocks.toml | 5 +- esp-metadata/devices/esp32h2/soc.toml | 2 +- esp-metadata/devices/esp32p4/clocks.toml | 6 +- esp-metadata/devices/esp32p4/soc.toml | 2 +- esp-metadata/devices/esp32s2/clocks.toml | 5 +- esp-metadata/devices/esp32s2/soc.toml | 2 +- esp-metadata/devices/esp32s3/clocks.toml | 5 +- esp-metadata/devices/esp32s3/soc.toml | 2 +- 34 files changed, 1041 insertions(+), 191 deletions(-) diff --git a/esp-hal/src/sdm.rs b/esp-hal/src/sdm.rs index e1af680e84e..7382e9fea93 100644 --- a/esp-hal/src/sdm.rs +++ b/esp-hal/src/sdm.rs @@ -33,7 +33,7 @@ use core::{fmt, marker::PhantomData}; #[cfg(soc_has_clock_node_iomux_function_clock)] -use crate::soc::clocks::{self, ClockTree, IomuxFunctionClockConfig}; +use crate::soc::clocks::{self, IomuxFunctionClockConfig}; use crate::{ gpio::{ OutputConfig, @@ -42,6 +42,7 @@ use crate::{ interconnect::{OutputSignal as GpioOutputSignal, PeripheralOutput}, }, peripherals::GPIO_SD, + soc::clocks::{ClockTree, SdmInstance}, system::{GenericPeripheralGuard, Peripheral}, time::Rate, }; @@ -352,43 +353,41 @@ fn connect_pin<'d>(channel: usize, pin: impl PeripheralOutput<'d>) -> PinGuard { #[derive(Debug)] struct SdmClockGuard { + // Fields are dropped in declaration order. Release the function clock + // while the GPIO_SD register clock is still available. + _function_clock: SdmFunctionClockGuard, _peripheral: GenericPeripheralGuard<{ Peripheral::GpioSd as u8 }>, - _iomux: IomuxClockGuard, } impl SdmClockGuard { fn new() -> Self { - let iomux = IomuxClockGuard::new(); + let peripheral = GenericPeripheralGuard::new(); + let function_clock = SdmFunctionClockGuard::new(); Self { - _peripheral: GenericPeripheralGuard::new(), - _iomux: iomux, + _function_clock: function_clock, + _peripheral: peripheral, } } } #[derive(Debug)] -struct IomuxClockGuard; +struct SdmFunctionClockGuard; -impl IomuxClockGuard { +impl SdmFunctionClockGuard { fn new() -> Self { - #[cfg(soc_has_clock_node_iomux_function_clock)] - ClockTree::with(clocks::request_iomux_function_clock); + ClockTree::with(|clocks| SdmInstance::GpioSd.request_function_clock(clocks)); Self } } -impl Drop for IomuxClockGuard { +impl Drop for SdmFunctionClockGuard { fn drop(&mut self) { - #[cfg(soc_has_clock_node_iomux_function_clock)] - ClockTree::with(clocks::release_iomux_function_clock); + ClockTree::with(|clocks| SdmInstance::GpioSd.release_function_clock(clocks)); } } fn prescaler_from_frequency(frequency: Rate) -> Result { - #[cfg(soc_has_clock_node_iomux_function_clock)] - let source_frequency = clocks::iomux_function_clock_frequency(); - #[cfg(not(soc_has_clock_node_iomux_function_clock))] - let source_frequency = crate::soc::clocks::apb_clk_frequency(); + let source_frequency = SdmInstance::GpioSd.function_clock_frequency(); let requested_frequency = frequency.as_hz(); if requested_frequency == 0 || requested_frequency > source_frequency { diff --git a/esp-hal/src/soc/esp32/clocks.rs b/esp-hal/src/soc/esp32/clocks.rs index 83186a2e3e4..92b30362804 100644 --- a/esp-hal/src/soc/esp32/clocks.rs +++ b/esp-hal/src/soc/esp32/clocks.rs @@ -750,6 +750,22 @@ impl McpwmInstance { } } +impl SdmInstance { + // SDM_FUNCTION_CLOCK + + fn enable_function_clock_impl(self, _clocks: &mut ClockTree, _en: bool) { + // Nothing to do. + } + + fn configure_function_clock_impl( + self, + _clocks: &mut ClockTree, + _old_config: Option, + _new_config: SdmFunctionClockConfig, + ) { + // Nothing to do. + } +} impl RmtInstance { // RMT_SCLK diff --git a/esp-hal/src/soc/esp32c3/clocks.rs b/esp-hal/src/soc/esp32c3/clocks.rs index 5ebaad547e5..96e728700d1 100644 --- a/esp-hal/src/soc/esp32c3/clocks.rs +++ b/esp-hal/src/soc/esp32c3/clocks.rs @@ -580,6 +580,24 @@ fn configure_timg_calibration_clock_impl( }); } +impl SdmInstance { + // SDM_FUNCTION_CLOCK + + fn enable_function_clock_impl(self, _clocks: &mut ClockTree, en: bool) { + crate::peripherals::GPIO_SD::regs() + .sigmadelta_misc() + .modify(|_, w| w.function_clk_en().bit(en)); + } + + fn configure_function_clock_impl( + self, + _clocks: &mut ClockTree, + _old_config: Option, + _new_config: SdmFunctionClockConfig, + ) { + // Nothing to do. + } +} impl RmtInstance { // RMT_SCLK diff --git a/esp-hal/src/soc/esp32c5/clocks.rs b/esp-hal/src/soc/esp32c5/clocks.rs index 10c3d545391..498f38445f9 100644 --- a/esp-hal/src/soc/esp32c5/clocks.rs +++ b/esp-hal/src/soc/esp32c5/clocks.rs @@ -559,6 +559,24 @@ impl ParlIoInstance { } } +impl SdmInstance { + // SDM_FUNCTION_CLOCK + + fn enable_function_clock_impl(self, _clocks: &mut ClockTree, en: bool) { + crate::peripherals::GPIO_SD::regs() + .sigmadelta_misc() + .modify(|_, w| w.sigmadelta_clk_en().bit(en)); + } + + fn configure_function_clock_impl( + self, + _clocks: &mut ClockTree, + _old_config: Option, + _new_config: SdmFunctionClockConfig, + ) { + // Nothing to do. + } +} impl RmtInstance { // RMT_SCLK diff --git a/esp-hal/src/soc/esp32c6/clocks.rs b/esp-hal/src/soc/esp32c6/clocks.rs index 21b3d30ee8a..6f1b924dd96 100644 --- a/esp-hal/src/soc/esp32c6/clocks.rs +++ b/esp-hal/src/soc/esp32c6/clocks.rs @@ -647,6 +647,24 @@ impl ParlIoInstance { } } +impl SdmInstance { + // SDM_FUNCTION_CLOCK + + fn enable_function_clock_impl(self, _clocks: &mut ClockTree, en: bool) { + crate::peripherals::GPIO_SD::regs() + .sigmadelta_misc() + .modify(|_, w| w.function_clk_en().bit(en)); + } + + fn configure_function_clock_impl( + self, + _clocks: &mut ClockTree, + _old_config: Option, + _new_config: SdmFunctionClockConfig, + ) { + // Nothing to do. + } +} impl RmtInstance { // RMT_SCLK diff --git a/esp-hal/src/soc/esp32h2/clocks.rs b/esp-hal/src/soc/esp32h2/clocks.rs index b77934f3d65..1d7a1df73f1 100644 --- a/esp-hal/src/soc/esp32h2/clocks.rs +++ b/esp-hal/src/soc/esp32h2/clocks.rs @@ -470,6 +470,24 @@ impl ParlIoInstance { } } +impl SdmInstance { + // SDM_FUNCTION_CLOCK + + fn enable_function_clock_impl(self, _clocks: &mut ClockTree, en: bool) { + crate::peripherals::GPIO_SD::regs() + .sigmadelta_misc() + .modify(|_, w| w.function_clk_en().bit(en)); + } + + fn configure_function_clock_impl( + self, + _clocks: &mut ClockTree, + _old_config: Option, + _new_config: SdmFunctionClockConfig, + ) { + // Nothing to do. + } +} impl RmtInstance { // RMT_SCLK diff --git a/esp-hal/src/soc/esp32p4/clocks.rs b/esp-hal/src/soc/esp32p4/clocks.rs index 0a406783de0..b0b9202bbc6 100644 --- a/esp-hal/src/soc/esp32p4/clocks.rs +++ b/esp-hal/src/soc/esp32p4/clocks.rs @@ -457,6 +457,24 @@ fn configure_lp_slow_clk_impl( // Per-instance clock impl for TIMG +impl SdmInstance { + // SDM_FUNCTION_CLOCK + + fn enable_function_clock_impl(self, _clocks: &mut ClockTree, en: bool) { + crate::peripherals::GPIO_SD::regs() + .sigmadelta_misc() + .modify(|_, w| w.function_clk_en().bit(en)); + } + + fn configure_function_clock_impl( + self, + _clocks: &mut ClockTree, + _old_config: Option, + _new_config: SdmFunctionClockConfig, + ) { + // Nothing to do. + } +} impl TimgInstance { fn enable_function_clock_impl(self, _clocks: &mut ClockTree, _en: bool) { // TIMG function clock is managed by peripheral clock gates diff --git a/esp-hal/src/soc/esp32s2/clocks.rs b/esp-hal/src/soc/esp32s2/clocks.rs index 774f077c87e..83b4a13d29d 100644 --- a/esp-hal/src/soc/esp32s2/clocks.rs +++ b/esp-hal/src/soc/esp32s2/clocks.rs @@ -681,6 +681,24 @@ impl TimgInstance { } } +impl SdmInstance { + // SDM_FUNCTION_CLOCK + + fn enable_function_clock_impl(self, _clocks: &mut ClockTree, en: bool) { + crate::peripherals::GPIO_SD::regs() + .sigmadelta_misc() + .modify(|_, w| w.function_clk_en().bit(en)); + } + + fn configure_function_clock_impl( + self, + _clocks: &mut ClockTree, + _old_config: Option, + _new_config: SdmFunctionClockConfig, + ) { + // Nothing to do. + } +} impl RmtInstance { // RMT_SCLK diff --git a/esp-hal/src/soc/esp32s3/clocks.rs b/esp-hal/src/soc/esp32s3/clocks.rs index 0a0852a3915..31c7a4ecc16 100644 --- a/esp-hal/src/soc/esp32s3/clocks.rs +++ b/esp-hal/src/soc/esp32s3/clocks.rs @@ -788,6 +788,24 @@ impl McpwmInstance { } } +impl SdmInstance { + // SDM_FUNCTION_CLOCK + + fn enable_function_clock_impl(self, _clocks: &mut ClockTree, en: bool) { + crate::peripherals::GPIO_SD::regs() + .sigmadelta_misc() + .modify(|_, w| w.function_clk_en().bit(en)); + } + + fn configure_function_clock_impl( + self, + _clocks: &mut ClockTree, + _old_config: Option, + _new_config: SdmFunctionClockConfig, + ) { + // Nothing to do. + } +} impl RmtInstance { // RMT_SCLK diff --git a/esp-metadata-generated/src/_build_script_utils.rs b/esp-metadata-generated/src/_build_script_utils.rs index 76805d03d25..ccb3aa318c8 100644 --- a/esp-metadata-generated/src/_build_script_utils.rs +++ b/esp-metadata-generated/src/_build_script_utils.rs @@ -423,6 +423,7 @@ impl Chip { "soc_has_clock_node_uart_function_clock", "soc_has_clock_node_uart_mem_clock", "soc_has_clock_node_uart_baud_rate_generator", + "soc_has_clock_node_sdm_function_clock", "soc_has_clock_node_rmt_sclk", "soc_has_clock_node_spi_function_clock", "has_dram_region", @@ -659,6 +660,7 @@ impl Chip { "cargo:rustc-cfg=soc_has_clock_node_uart_function_clock", "cargo:rustc-cfg=soc_has_clock_node_uart_mem_clock", "cargo:rustc-cfg=soc_has_clock_node_uart_baud_rate_generator", + "cargo:rustc-cfg=soc_has_clock_node_sdm_function_clock", "cargo:rustc-cfg=soc_has_clock_node_rmt_sclk", "cargo:rustc-cfg=soc_has_clock_node_spi_function_clock", "cargo:rustc-cfg=has_dram_region", @@ -1526,6 +1528,7 @@ impl Chip { "soc_has_clock_node_timg_calibration_clock", "soc_has_clock_node_timg_function_clock", "soc_has_clock_node_timg_wdt_clock", + "soc_has_clock_node_sdm_function_clock", "soc_has_clock_node_rmt_sclk", "soc_has_clock_node_uart_function_clock", "soc_has_clock_node_uart_mem_clock", @@ -1771,6 +1774,7 @@ impl Chip { "cargo:rustc-cfg=soc_has_clock_node_timg_calibration_clock", "cargo:rustc-cfg=soc_has_clock_node_timg_function_clock", "cargo:rustc-cfg=soc_has_clock_node_timg_wdt_clock", + "cargo:rustc-cfg=soc_has_clock_node_sdm_function_clock", "cargo:rustc-cfg=soc_has_clock_node_rmt_sclk", "cargo:rustc-cfg=soc_has_clock_node_uart_function_clock", "cargo:rustc-cfg=soc_has_clock_node_uart_mem_clock", @@ -2170,6 +2174,7 @@ impl Chip { "soc_has_clock_node_uart_baud_rate_generator", "soc_has_clock_node_timg_function_clock", "soc_has_clock_node_timg_wdt_clock", + "soc_has_clock_node_sdm_function_clock", "soc_has_clock_node_rmt_sclk", "soc_has_clock_node_parl_io_rx_clock", "soc_has_clock_node_parl_io_tx_clock", @@ -2462,6 +2467,7 @@ impl Chip { "cargo:rustc-cfg=soc_has_clock_node_uart_baud_rate_generator", "cargo:rustc-cfg=soc_has_clock_node_timg_function_clock", "cargo:rustc-cfg=soc_has_clock_node_timg_wdt_clock", + "cargo:rustc-cfg=soc_has_clock_node_sdm_function_clock", "cargo:rustc-cfg=soc_has_clock_node_rmt_sclk", "cargo:rustc-cfg=soc_has_clock_node_parl_io_rx_clock", "cargo:rustc-cfg=soc_has_clock_node_parl_io_tx_clock", @@ -2906,6 +2912,7 @@ impl Chip { "soc_has_clock_node_timg_calibration_clock", "soc_has_clock_node_uart_function_clock", "soc_has_clock_node_uart_baud_rate_generator", + "soc_has_clock_node_sdm_function_clock", "soc_has_clock_node_rmt_sclk", "soc_has_clock_node_timg_function_clock", "soc_has_clock_node_timg_wdt_clock", @@ -3212,6 +3219,7 @@ impl Chip { "cargo:rustc-cfg=soc_has_clock_node_timg_calibration_clock", "cargo:rustc-cfg=soc_has_clock_node_uart_function_clock", "cargo:rustc-cfg=soc_has_clock_node_uart_baud_rate_generator", + "cargo:rustc-cfg=soc_has_clock_node_sdm_function_clock", "cargo:rustc-cfg=soc_has_clock_node_rmt_sclk", "cargo:rustc-cfg=soc_has_clock_node_timg_function_clock", "cargo:rustc-cfg=soc_has_clock_node_timg_wdt_clock", @@ -4235,6 +4243,7 @@ impl Chip { "soc_has_clock_node_timg_calibration_clock", "soc_has_clock_node_uart_function_clock", "soc_has_clock_node_uart_baud_rate_generator", + "soc_has_clock_node_sdm_function_clock", "soc_has_clock_node_rmt_sclk", "soc_has_clock_node_timg_function_clock", "soc_has_clock_node_timg_wdt_clock", @@ -4512,6 +4521,7 @@ impl Chip { "cargo:rustc-cfg=soc_has_clock_node_timg_calibration_clock", "cargo:rustc-cfg=soc_has_clock_node_uart_function_clock", "cargo:rustc-cfg=soc_has_clock_node_uart_baud_rate_generator", + "cargo:rustc-cfg=soc_has_clock_node_sdm_function_clock", "cargo:rustc-cfg=soc_has_clock_node_rmt_sclk", "cargo:rustc-cfg=soc_has_clock_node_timg_function_clock", "cargo:rustc-cfg=soc_has_clock_node_timg_wdt_clock", @@ -4890,6 +4900,7 @@ impl Chip { "soc_has_clock_node_timg_calibration_clock", "soc_has_clock_node_uart_function_clock", "soc_has_clock_node_uart_baud_rate_generator", + "soc_has_clock_node_sdm_function_clock", "soc_has_clock_node_timg_function_clock", "soc_has_clock_node_timg_wdt_clock", "soc_has_clock_node_i2c_function_clock", @@ -5158,6 +5169,7 @@ impl Chip { "cargo:rustc-cfg=soc_has_clock_node_timg_calibration_clock", "cargo:rustc-cfg=soc_has_clock_node_uart_function_clock", "cargo:rustc-cfg=soc_has_clock_node_uart_baud_rate_generator", + "cargo:rustc-cfg=soc_has_clock_node_sdm_function_clock", "cargo:rustc-cfg=soc_has_clock_node_timg_function_clock", "cargo:rustc-cfg=soc_has_clock_node_timg_wdt_clock", "cargo:rustc-cfg=soc_has_clock_node_i2c_function_clock", @@ -5648,6 +5660,7 @@ impl Chip { "soc_has_clock_node_uart_function_clock", "soc_has_clock_node_uart_baud_rate_generator", "soc_has_clock_node_uart_mem_clock", + "soc_has_clock_node_sdm_function_clock", "soc_has_clock_node_rmt_sclk", "soc_has_clock_node_i2c_function_clock", "soc_has_clock_node_spi_function_clock", @@ -5895,6 +5908,7 @@ impl Chip { "cargo:rustc-cfg=soc_has_clock_node_uart_function_clock", "cargo:rustc-cfg=soc_has_clock_node_uart_baud_rate_generator", "cargo:rustc-cfg=soc_has_clock_node_uart_mem_clock", + "cargo:rustc-cfg=soc_has_clock_node_sdm_function_clock", "cargo:rustc-cfg=soc_has_clock_node_rmt_sclk", "cargo:rustc-cfg=soc_has_clock_node_i2c_function_clock", "cargo:rustc-cfg=soc_has_clock_node_spi_function_clock", @@ -6378,6 +6392,7 @@ impl Chip { "soc_has_clock_node_timg_calibration_clock", "soc_has_clock_node_mcpwm_function_clock", "soc_has_clock_node_timg_function_clock", + "soc_has_clock_node_sdm_function_clock", "soc_has_clock_node_rmt_sclk", "soc_has_clock_node_uart_function_clock", "soc_has_clock_node_uart_baud_rate_generator", @@ -6672,6 +6687,7 @@ impl Chip { "cargo:rustc-cfg=soc_has_clock_node_timg_calibration_clock", "cargo:rustc-cfg=soc_has_clock_node_mcpwm_function_clock", "cargo:rustc-cfg=soc_has_clock_node_timg_function_clock", + "cargo:rustc-cfg=soc_has_clock_node_sdm_function_clock", "cargo:rustc-cfg=soc_has_clock_node_rmt_sclk", "cargo:rustc-cfg=soc_has_clock_node_uart_function_clock", "cargo:rustc-cfg=soc_has_clock_node_uart_baud_rate_generator", @@ -7583,6 +7599,7 @@ impl Chip { "soc_has_clock_node_uart_function_clock", "soc_has_clock_node_uart_mem_clock", "soc_has_clock_node_uart_baud_rate_generator", + "soc_has_clock_node_sdm_function_clock", "soc_has_clock_node_rmt_sclk", "soc_has_clock_node_spi_function_clock", "has_dram_region", diff --git a/esp-metadata-generated/src/_generated_esp32.rs b/esp-metadata-generated/src/_generated_esp32.rs index 21acb9368dd..3cb45937e9b 100644 --- a/esp-metadata-generated/src/_generated_esp32.rs +++ b/esp-metadata-generated/src/_generated_esp32.rs @@ -1047,6 +1047,22 @@ macro_rules! for_each_wakeup_source { /// todo!() /// } /// +/// impl SdmInstance { +/// // SDM_FUNCTION_CLOCK +/// +/// fn enable_function_clock_impl(self, _clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_function_clock_impl( +/// self, +/// _clocks: &mut ClockTree, +/// _old_config: Option, +/// _new_config: SdmFunctionClockConfig, +/// ) { +/// todo!() +/// } +/// } /// impl I2cInstance { /// // I2C_FUNCTION_CLOCK /// @@ -1160,6 +1176,11 @@ macro_rules! for_each_wakeup_source { /// ``` macro_rules! define_clock_tree_types { () => { + #[derive(Clone, Copy, PartialEq, Eq, Debug)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum SdmInstance { + GpioSd = 0, + } #[derive(Clone, Copy, PartialEq, Eq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum I2cInstance { @@ -1508,6 +1529,18 @@ macro_rules! define_clock_tree_types { /// Selects `XTAL32K_CLK`. Xtal32kClk, } + /// Configures the `GPIO_SD_FUNCTION_CLOCK` clock node. + /// + /// The output is calculated as `OUTPUT = APB_CLK`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct SdmFunctionClockConfig {} + impl SdmFunctionClockConfig { + /// Creates a new configuration for the FUNCTION_CLOCK clock node. + pub const fn new() -> Self { + Self {} + } + } #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum I2cFunctionClockSclk { @@ -1657,6 +1690,7 @@ macro_rules! define_clock_tree_types { rtc_slow_clk: Option, rtc_fast_clk: Option, timg_calibration_clock: Option, + sdm_function_clock: [Option; 1], i2c_function_clock: [Option; 2], mcpwm_function_clock: [Option; 2], rmt_sclk: [Option; 1], @@ -1676,6 +1710,7 @@ macro_rules! define_clock_tree_types { rtc_fast_clk_refcount: u32, uart_mem_clk_refcount: u32, timg_calibration_clock_refcount: u32, + sdm_function_clock_refcount: [u32; 1], i2c_function_clock_refcount: [u32; 2], mcpwm_function_clock_refcount: [u32; 2], rmt_sclk_refcount: [u32; 1], @@ -1757,6 +1792,10 @@ macro_rules! define_clock_tree_types { pub fn timg_calibration_clock(&self) -> Option { self.timg_calibration_clock } + /// Returns the current configuration of the GPIO_SD_FUNCTION_CLOCK clock tree node + pub fn gpio_sd_function_clock(&self) -> Option { + self.sdm_function_clock[SdmInstance::GpioSd as usize] + } /// Returns the current configuration of the I2C0_FUNCTION_CLOCK clock tree node pub fn i2c0_function_clock(&self) -> Option { self.i2c_function_clock[I2cInstance::I2c0 as usize] @@ -1841,6 +1880,7 @@ macro_rules! define_clock_tree_types { rtc_slow_clk: None, rtc_fast_clk: None, timg_calibration_clock: None, + sdm_function_clock: [None; 1], i2c_function_clock: [None; 2], mcpwm_function_clock: [None; 2], rmt_sclk: [None; 1], @@ -1860,6 +1900,7 @@ macro_rules! define_clock_tree_types { rtc_fast_clk_refcount: 0, uart_mem_clk_refcount: 0, timg_calibration_clock_refcount: 0, + sdm_function_clock_refcount: [0; 1], i2c_function_clock_refcount: [0; 2], mcpwm_function_clock_refcount: [0; 2], rmt_sclk_refcount: [0; 1], @@ -2897,6 +2938,54 @@ macro_rules! define_clock_tree_types { TimgCalibrationClockConfig::Xtal32kClk => xtal32k_clk_frequency(), } } + impl SdmInstance { + pub fn configure_function_clock( + self, + clocks: &mut ClockTree, + config: SdmFunctionClockConfig, + ) { + let old_config = clocks.sdm_function_clock[self as usize].replace(config); + refresh_sdm_function_clock_downstream(clocks, self); + self.configure_function_clock_impl(clocks, old_config, config); + } + pub fn function_clock_config( + self, + clocks: &mut ClockTree, + ) -> Option { + clocks.sdm_function_clock[self as usize] + } + pub fn request_function_clock(self, clocks: &mut ClockTree) { + trace!("Requesting {:?}::FUNCTION_CLOCK", self); + if increment_reference_count(&mut clocks.sdm_function_clock_refcount[self as usize]) + { + trace!("Enabling {:?}::FUNCTION_CLOCK", self); + request_apb_clk(clocks); + self.enable_function_clock_impl(clocks, true); + } + } + pub fn release_function_clock(self, clocks: &mut ClockTree) { + trace!("Releasing {:?}::FUNCTION_CLOCK", self); + if decrement_reference_count(&mut clocks.sdm_function_clock_refcount[self as usize]) + { + trace!("Disabling {:?}::FUNCTION_CLOCK", self); + self.enable_function_clock_impl(clocks, false); + release_apb_clk(clocks); + } + } + #[allow(unused_variables)] + pub fn function_clock_config_frequency( + clocks: &mut ClockTree, + config: SdmFunctionClockConfig, + ) -> u32 { + apb_clk_frequency() + } + pub fn function_clock_frequency(self) -> u32 { + apb_clk_frequency() + } + pub fn function_clock_source_frequency() -> u32 { + apb_clk_frequency() + } + } impl I2cInstance { pub fn configure_function_clock( self, @@ -3498,6 +3587,9 @@ macro_rules! define_clock_tree_types { refresh_ref_tick_fosc_downstream(clocks); refresh_ref_tick_apll_downstream(clocks); refresh_ref_tick_pll_downstream(clocks); + for child_instance in [SdmInstance::GpioSd] { + refresh_sdm_function_clock_downstream(clocks, child_instance); + } for child_instance in [I2cInstance::I2c0, I2cInstance::I2c1] { refresh_i2c_function_clock_downstream(clocks, child_instance); } @@ -3551,6 +3643,7 @@ macro_rules! define_clock_tree_types { } refresh_ref_tick_downstream(clocks); } + fn refresh_sdm_function_clock_downstream(clocks: &mut ClockTree, instance: SdmInstance) {} fn refresh_i2c_function_clock_downstream(clocks: &mut ClockTree, instance: I2cInstance) {} fn refresh_spi_function_clock_downstream(clocks: &mut ClockTree, instance: SpiInstance) {} fn refresh_ref_tick_downstream(clocks: &mut ClockTree) { diff --git a/esp-metadata-generated/src/_generated_esp32c3.rs b/esp-metadata-generated/src/_generated_esp32c3.rs index eab4ce698af..c42d1fb2e78 100644 --- a/esp-metadata-generated/src/_generated_esp32c3.rs +++ b/esp-metadata-generated/src/_generated_esp32c3.rs @@ -1283,6 +1283,22 @@ macro_rules! for_each_wakeup_source { /// todo!() /// } /// +/// impl SdmInstance { +/// // SDM_FUNCTION_CLOCK +/// +/// fn enable_function_clock_impl(self, _clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_function_clock_impl( +/// self, +/// _clocks: &mut ClockTree, +/// _old_config: Option, +/// _new_config: SdmFunctionClockConfig, +/// ) { +/// todo!() +/// } +/// } /// impl I2cInstance { /// // I2C_FUNCTION_CLOCK /// @@ -1411,6 +1427,11 @@ macro_rules! for_each_wakeup_source { /// ``` macro_rules! define_clock_tree_types { () => { + #[derive(Clone, Copy, PartialEq, Eq, Debug)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum SdmInstance { + GpioSd = 0, + } #[derive(Clone, Copy, PartialEq, Eq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum I2cInstance { @@ -1628,6 +1649,18 @@ macro_rules! define_clock_tree_types { /// Selects `XTAL32K_CLK`. Xtal32kClk, } + /// Configures the `GPIO_SD_FUNCTION_CLOCK` clock node. + /// + /// The output is calculated as `OUTPUT = APB_CLK`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct SdmFunctionClockConfig {} + impl SdmFunctionClockConfig { + /// Creates a new configuration for the FUNCTION_CLOCK clock node. + pub const fn new() -> Self { + Self {} + } + } #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum I2cFunctionClockSclk { @@ -1818,6 +1851,7 @@ macro_rules! define_clock_tree_types { rtc_fast_clk: Option, low_power_clk: Option, timg_calibration_clock: Option, + sdm_function_clock: [Option; 1], i2c_function_clock: [Option; 1], rmt_sclk: [Option; 1], spi_function_clock: [Option; 1], @@ -1837,6 +1871,7 @@ macro_rules! define_clock_tree_types { low_power_clk_refcount: u32, uart_mem_clk_refcount: u32, timg_calibration_clock_refcount: u32, + sdm_function_clock_refcount: [u32; 1], i2c_function_clock_refcount: [u32; 1], rmt_sclk_refcount: [u32; 1], spi_function_clock_refcount: [u32; 1], @@ -1903,6 +1938,10 @@ macro_rules! define_clock_tree_types { pub fn timg_calibration_clock(&self) -> Option { self.timg_calibration_clock } + /// Returns the current configuration of the GPIO_SD_FUNCTION_CLOCK clock tree node + pub fn gpio_sd_function_clock(&self) -> Option { + self.sdm_function_clock[SdmInstance::GpioSd as usize] + } /// Returns the current configuration of the I2C0_FUNCTION_CLOCK clock tree node pub fn i2c0_function_clock(&self) -> Option { self.i2c_function_clock[I2cInstance::I2c0 as usize] @@ -1971,6 +2010,7 @@ macro_rules! define_clock_tree_types { rtc_fast_clk: None, low_power_clk: None, timg_calibration_clock: None, + sdm_function_clock: [None; 1], i2c_function_clock: [None; 1], rmt_sclk: [None; 1], spi_function_clock: [None; 1], @@ -1990,6 +2030,7 @@ macro_rules! define_clock_tree_types { low_power_clk_refcount: 0, uart_mem_clk_refcount: 0, timg_calibration_clock_refcount: 0, + sdm_function_clock_refcount: [0; 1], i2c_function_clock_refcount: [0; 1], rmt_sclk_refcount: [0; 1], spi_function_clock_refcount: [0; 1], @@ -2842,6 +2883,54 @@ macro_rules! define_clock_tree_types { TimgCalibrationClockConfig::Xtal32kClk => xtal32k_clk_frequency(), } } + impl SdmInstance { + pub fn configure_function_clock( + self, + clocks: &mut ClockTree, + config: SdmFunctionClockConfig, + ) { + let old_config = clocks.sdm_function_clock[self as usize].replace(config); + refresh_sdm_function_clock_downstream(clocks, self); + self.configure_function_clock_impl(clocks, old_config, config); + } + pub fn function_clock_config( + self, + clocks: &mut ClockTree, + ) -> Option { + clocks.sdm_function_clock[self as usize] + } + pub fn request_function_clock(self, clocks: &mut ClockTree) { + trace!("Requesting {:?}::FUNCTION_CLOCK", self); + if increment_reference_count(&mut clocks.sdm_function_clock_refcount[self as usize]) + { + trace!("Enabling {:?}::FUNCTION_CLOCK", self); + request_apb_clk(clocks); + self.enable_function_clock_impl(clocks, true); + } + } + pub fn release_function_clock(self, clocks: &mut ClockTree) { + trace!("Releasing {:?}::FUNCTION_CLOCK", self); + if decrement_reference_count(&mut clocks.sdm_function_clock_refcount[self as usize]) + { + trace!("Disabling {:?}::FUNCTION_CLOCK", self); + self.enable_function_clock_impl(clocks, false); + release_apb_clk(clocks); + } + } + #[allow(unused_variables)] + pub fn function_clock_config_frequency( + clocks: &mut ClockTree, + config: SdmFunctionClockConfig, + ) -> u32 { + apb_clk_frequency() + } + pub fn function_clock_frequency(self) -> u32 { + apb_clk_frequency() + } + pub fn function_clock_source_frequency() -> u32 { + apb_clk_frequency() + } + } impl I2cInstance { pub fn configure_function_clock( self, @@ -3599,6 +3688,9 @@ macro_rules! define_clock_tree_types { ::core::sync::atomic::Ordering::Release, ); } + for child_instance in [SdmInstance::GpioSd] { + refresh_sdm_function_clock_downstream(clocks, child_instance); + } for child_instance in [RmtInstance::Rmt] { refresh_rmt_sclk_downstream(clocks, child_instance); } @@ -3618,6 +3710,7 @@ macro_rules! define_clock_tree_types { ); } } + fn refresh_sdm_function_clock_downstream(clocks: &mut ClockTree, instance: SdmInstance) {} fn refresh_rmt_sclk_downstream(clocks: &mut ClockTree, instance: RmtInstance) { if let Some(config) = clocks.rmt_sclk[instance as usize] { RMT_SCLK_FREQ_CACHE[instance as usize].store( @@ -3784,9 +3877,6 @@ macro_rules! implement_peripheral_clocks { crate::peripherals::GPIO_SD::regs() .clock_gate() .modify(|_, w| w.clk_en().bit(enable)); - crate::peripherals::GPIO_SD::regs() - .sigmadelta_misc() - .modify(|_, w| w.function_clk_en().bit(enable)); } Peripheral::Hmac => { crate::peripherals::SYSTEM::regs() diff --git a/esp-metadata-generated/src/_generated_esp32c5.rs b/esp-metadata-generated/src/_generated_esp32c5.rs index d3bf487dbb6..291b65da008 100644 --- a/esp-metadata-generated/src/_generated_esp32c5.rs +++ b/esp-metadata-generated/src/_generated_esp32c5.rs @@ -1405,6 +1405,22 @@ macro_rules! for_each_wakeup_source { /// todo!() /// } /// +/// impl SdmInstance { +/// // SDM_FUNCTION_CLOCK +/// +/// fn enable_function_clock_impl(self, _clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_function_clock_impl( +/// self, +/// _clocks: &mut ClockTree, +/// _old_config: Option, +/// _new_config: SdmFunctionClockConfig, +/// ) { +/// todo!() +/// } +/// } /// impl I2cInstance { /// // I2C_FUNCTION_CLOCK /// @@ -1549,6 +1565,11 @@ macro_rules! for_each_wakeup_source { /// ``` macro_rules! define_clock_tree_types { () => { + #[derive(Clone, Copy, PartialEq, Eq, Debug)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum SdmInstance { + GpioSd = 0, + } #[derive(Clone, Copy, PartialEq, Eq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum I2cInstance { @@ -1747,6 +1768,18 @@ macro_rules! define_clock_tree_types { /// Selects `XTAL32K_CLK`. Xtal32kClk, } + /// Configures the `GPIO_SD_FUNCTION_CLOCK` clock node. + /// + /// The output is calculated as `OUTPUT = IOMUX_FUNCTION_CLOCK`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct SdmFunctionClockConfig {} + impl SdmFunctionClockConfig { + /// Creates a new configuration for the FUNCTION_CLOCK clock node. + pub const fn new() -> Self { + Self {} + } + } #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum I2cFunctionClockSclk { @@ -1954,6 +1987,7 @@ macro_rules! define_clock_tree_types { crypto_clk: Option, iomux_function_clock: Option, timg_calibration_clock: Option, + sdm_function_clock: [Option; 1], i2c_function_clock: [Option; 1], parl_io_rx_clock: [Option; 1], parl_io_tx_clock: [Option; 1], @@ -1982,8 +2016,8 @@ macro_rules! define_clock_tree_types { lp_fast_clk_refcount: u32, lp_slow_clk_refcount: u32, crypto_clk_refcount: u32, - iomux_function_clock_refcount: u32, timg_calibration_clock_refcount: u32, + sdm_function_clock_refcount: [u32; 1], i2c_function_clock_refcount: [u32; 1], parl_io_rx_clock_refcount: [u32; 1], parl_io_tx_clock_refcount: [u32; 1], @@ -2039,6 +2073,10 @@ macro_rules! define_clock_tree_types { pub fn timg_calibration_clock(&self) -> Option { self.timg_calibration_clock } + /// Returns the current configuration of the GPIO_SD_FUNCTION_CLOCK clock tree node + pub fn gpio_sd_function_clock(&self) -> Option { + self.sdm_function_clock[SdmInstance::GpioSd as usize] + } /// Returns the current configuration of the I2C0_FUNCTION_CLOCK clock tree node pub fn i2c0_function_clock(&self) -> Option { self.i2c_function_clock[I2cInstance::I2c0 as usize] @@ -2104,6 +2142,7 @@ macro_rules! define_clock_tree_types { crypto_clk: None, iomux_function_clock: None, timg_calibration_clock: None, + sdm_function_clock: [None; 1], i2c_function_clock: [None; 1], parl_io_rx_clock: [None; 1], parl_io_tx_clock: [None; 1], @@ -2132,8 +2171,8 @@ macro_rules! define_clock_tree_types { lp_fast_clk_refcount: 0, lp_slow_clk_refcount: 0, crypto_clk_refcount: 0, - iomux_function_clock_refcount: 0, timg_calibration_clock_refcount: 0, + sdm_function_clock_refcount: [0; 1], i2c_function_clock_refcount: [0; 1], parl_io_rx_clock_refcount: [0; 1], parl_io_tx_clock_refcount: [0; 1], @@ -2884,22 +2923,18 @@ macro_rules! define_clock_tree_types { ) { let old_selector = clocks.iomux_function_clock.replace(new_selector); refresh_iomux_function_clock_downstream(clocks); - if clocks.iomux_function_clock_refcount > 0 { - match new_selector { - IomuxFunctionClockConfig::XtalClk => request_xtal_clk(clocks), - IomuxFunctionClockConfig::RcFastClk => request_rc_fast_clk(clocks), - IomuxFunctionClockConfig::PllF80m => request_pll_f80m(clocks), - } - configure_iomux_function_clock_impl(clocks, old_selector, new_selector); - if let Some(old_selector) = old_selector { - match old_selector { - IomuxFunctionClockConfig::XtalClk => release_xtal_clk(clocks), - IomuxFunctionClockConfig::RcFastClk => release_rc_fast_clk(clocks), - IomuxFunctionClockConfig::PllF80m => release_pll_f80m(clocks), - } + match new_selector { + IomuxFunctionClockConfig::XtalClk => request_xtal_clk(clocks), + IomuxFunctionClockConfig::RcFastClk => request_rc_fast_clk(clocks), + IomuxFunctionClockConfig::PllF80m => request_pll_f80m(clocks), + } + configure_iomux_function_clock_impl(clocks, old_selector, new_selector); + if let Some(old_selector) = old_selector { + match old_selector { + IomuxFunctionClockConfig::XtalClk => release_xtal_clk(clocks), + IomuxFunctionClockConfig::RcFastClk => release_rc_fast_clk(clocks), + IomuxFunctionClockConfig::PllF80m => release_pll_f80m(clocks), } - } else { - configure_iomux_function_clock_impl(clocks, old_selector, new_selector); } } pub fn iomux_function_clock_config( @@ -2909,26 +2944,22 @@ macro_rules! define_clock_tree_types { } pub fn request_iomux_function_clock(clocks: &mut ClockTree) { trace!("Requesting IOMUX_FUNCTION_CLOCK"); - if increment_reference_count(&mut clocks.iomux_function_clock_refcount) { - trace!("Enabling IOMUX_FUNCTION_CLOCK"); - match unwrap!(clocks.iomux_function_clock) { - IomuxFunctionClockConfig::XtalClk => request_xtal_clk(clocks), - IomuxFunctionClockConfig::RcFastClk => request_rc_fast_clk(clocks), - IomuxFunctionClockConfig::PllF80m => request_pll_f80m(clocks), - } - enable_iomux_function_clock_impl(clocks, true); + trace!("Enabling IOMUX_FUNCTION_CLOCK"); + match unwrap!(clocks.iomux_function_clock) { + IomuxFunctionClockConfig::XtalClk => request_xtal_clk(clocks), + IomuxFunctionClockConfig::RcFastClk => request_rc_fast_clk(clocks), + IomuxFunctionClockConfig::PllF80m => request_pll_f80m(clocks), } + enable_iomux_function_clock_impl(clocks, true); } pub fn release_iomux_function_clock(clocks: &mut ClockTree) { trace!("Releasing IOMUX_FUNCTION_CLOCK"); - if decrement_reference_count(&mut clocks.iomux_function_clock_refcount) { - trace!("Disabling IOMUX_FUNCTION_CLOCK"); - enable_iomux_function_clock_impl(clocks, false); - match unwrap!(clocks.iomux_function_clock) { - IomuxFunctionClockConfig::XtalClk => release_xtal_clk(clocks), - IomuxFunctionClockConfig::RcFastClk => release_rc_fast_clk(clocks), - IomuxFunctionClockConfig::PllF80m => release_pll_f80m(clocks), - } + trace!("Disabling IOMUX_FUNCTION_CLOCK"); + enable_iomux_function_clock_impl(clocks, false); + match unwrap!(clocks.iomux_function_clock) { + IomuxFunctionClockConfig::XtalClk => release_xtal_clk(clocks), + IomuxFunctionClockConfig::RcFastClk => release_rc_fast_clk(clocks), + IomuxFunctionClockConfig::PllF80m => release_pll_f80m(clocks), } } #[allow(unused_variables)] @@ -3034,6 +3065,54 @@ macro_rules! define_clock_tree_types { TimgCalibrationClockConfig::Xtal32kClk => xtal32k_clk_frequency(), } } + impl SdmInstance { + pub fn configure_function_clock( + self, + clocks: &mut ClockTree, + config: SdmFunctionClockConfig, + ) { + let old_config = clocks.sdm_function_clock[self as usize].replace(config); + refresh_sdm_function_clock_downstream(clocks, self); + self.configure_function_clock_impl(clocks, old_config, config); + } + pub fn function_clock_config( + self, + clocks: &mut ClockTree, + ) -> Option { + clocks.sdm_function_clock[self as usize] + } + pub fn request_function_clock(self, clocks: &mut ClockTree) { + trace!("Requesting {:?}::FUNCTION_CLOCK", self); + if increment_reference_count(&mut clocks.sdm_function_clock_refcount[self as usize]) + { + trace!("Enabling {:?}::FUNCTION_CLOCK", self); + request_iomux_function_clock(clocks); + self.enable_function_clock_impl(clocks, true); + } + } + pub fn release_function_clock(self, clocks: &mut ClockTree) { + trace!("Releasing {:?}::FUNCTION_CLOCK", self); + if decrement_reference_count(&mut clocks.sdm_function_clock_refcount[self as usize]) + { + trace!("Disabling {:?}::FUNCTION_CLOCK", self); + self.enable_function_clock_impl(clocks, false); + release_iomux_function_clock(clocks); + } + } + #[allow(unused_variables)] + pub fn function_clock_config_frequency( + clocks: &mut ClockTree, + config: SdmFunctionClockConfig, + ) -> u32 { + iomux_function_clock_frequency() + } + pub fn function_clock_frequency(self) -> u32 { + iomux_function_clock_frequency() + } + pub fn function_clock_source_frequency() -> u32 { + iomux_function_clock_frequency() + } + } impl I2cInstance { pub fn configure_function_clock( self, @@ -3876,6 +3955,9 @@ macro_rules! define_clock_tree_types { ::core::sync::atomic::Ordering::Release, ); } + for child_instance in [SdmInstance::GpioSd] { + refresh_sdm_function_clock_downstream(clocks, child_instance); + } } fn refresh_timg_calibration_clock_downstream(clocks: &mut ClockTree) { if let Some(config) = clocks.timg_calibration_clock { @@ -3885,6 +3967,7 @@ macro_rules! define_clock_tree_types { ); } } + fn refresh_sdm_function_clock_downstream(clocks: &mut ClockTree, instance: SdmInstance) {} fn refresh_i2c_function_clock_downstream(clocks: &mut ClockTree, instance: I2cInstance) { if let Some(config) = clocks.i2c_function_clock[instance as usize] { I2C_FUNCTION_CLOCK_FREQ_CACHE[instance as usize].store( @@ -4074,9 +4157,6 @@ macro_rules! implement_peripheral_clocks { crate::peripherals::GPIO_SD::regs() .clock_gate() .modify(|_, w| w.clk_en().bit(enable)); - crate::peripherals::GPIO_SD::regs() - .sigmadelta_misc() - .modify(|_, w| w.sigmadelta_clk_en().bit(enable)); } Peripheral::I2cExt0 => { crate::peripherals::SYSTEM::regs() diff --git a/esp-metadata-generated/src/_generated_esp32c6.rs b/esp-metadata-generated/src/_generated_esp32c6.rs index 1b35498e69f..9fe2599ed2f 100644 --- a/esp-metadata-generated/src/_generated_esp32c6.rs +++ b/esp-metadata-generated/src/_generated_esp32c6.rs @@ -1443,6 +1443,22 @@ macro_rules! for_each_wakeup_source { /// todo!() /// } /// +/// impl SdmInstance { +/// // SDM_FUNCTION_CLOCK +/// +/// fn enable_function_clock_impl(self, _clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_function_clock_impl( +/// self, +/// _clocks: &mut ClockTree, +/// _old_config: Option, +/// _new_config: SdmFunctionClockConfig, +/// ) { +/// todo!() +/// } +/// } /// impl I2cInstance { /// // I2C_FUNCTION_CLOCK /// @@ -1603,6 +1619,11 @@ macro_rules! for_each_wakeup_source { /// ``` macro_rules! define_clock_tree_types { () => { + #[derive(Clone, Copy, PartialEq, Eq, Debug)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum SdmInstance { + GpioSd = 0, + } #[derive(Clone, Copy, PartialEq, Eq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum I2cInstance { @@ -2065,6 +2086,18 @@ macro_rules! define_clock_tree_types { /// Selects `XTAL32K_CLK`. Xtal32kClk, } + /// Configures the `GPIO_SD_FUNCTION_CLOCK` clock node. + /// + /// The output is calculated as `OUTPUT = IOMUX_FUNCTION_CLOCK`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct SdmFunctionClockConfig {} + impl SdmFunctionClockConfig { + /// Creates a new configuration for the FUNCTION_CLOCK clock node. + pub const fn new() -> Self { + Self {} + } + } #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum I2cFunctionClockSclk { @@ -2290,6 +2323,7 @@ macro_rules! define_clock_tree_types { lp_fast_clk: Option, lp_slow_clk: Option, timg_calibration_clock: Option, + sdm_function_clock: [Option; 1], i2c_function_clock: [Option; 1], mcpwm_function_clock: [Option; 1], parl_io_rx_clock: [Option; 1], @@ -2309,10 +2343,10 @@ macro_rules! define_clock_tree_types { pll_f48m_refcount: u32, pll_f80m_refcount: u32, pll_f240m_refcount: u32, - iomux_function_clock_refcount: u32, ledc_sclk_refcount: u32, lp_fast_clk_refcount: u32, timg_calibration_clock_refcount: u32, + sdm_function_clock_refcount: [u32; 1], i2c_function_clock_refcount: [u32; 1], mcpwm_function_clock_refcount: [u32; 1], parl_io_rx_clock_refcount: [u32; 1], @@ -2401,6 +2435,10 @@ macro_rules! define_clock_tree_types { pub fn timg_calibration_clock(&self) -> Option { self.timg_calibration_clock } + /// Returns the current configuration of the GPIO_SD_FUNCTION_CLOCK clock tree node + pub fn gpio_sd_function_clock(&self) -> Option { + self.sdm_function_clock[SdmInstance::GpioSd as usize] + } /// Returns the current configuration of the I2C0_FUNCTION_CLOCK clock tree node pub fn i2c0_function_clock(&self) -> Option { self.i2c_function_clock[I2cInstance::I2c0 as usize] @@ -2478,6 +2516,7 @@ macro_rules! define_clock_tree_types { lp_fast_clk: None, lp_slow_clk: None, timg_calibration_clock: None, + sdm_function_clock: [None; 1], i2c_function_clock: [None; 1], mcpwm_function_clock: [None; 1], parl_io_rx_clock: [None; 1], @@ -2497,10 +2536,10 @@ macro_rules! define_clock_tree_types { pll_f48m_refcount: 0, pll_f80m_refcount: 0, pll_f240m_refcount: 0, - iomux_function_clock_refcount: 0, ledc_sclk_refcount: 0, lp_fast_clk_refcount: 0, timg_calibration_clock_refcount: 0, + sdm_function_clock_refcount: [0; 1], i2c_function_clock_refcount: [0; 1], mcpwm_function_clock_refcount: [0; 1], parl_io_rx_clock_refcount: [0; 1], @@ -3226,22 +3265,18 @@ macro_rules! define_clock_tree_types { ) { let old_selector = clocks.iomux_function_clock.replace(new_selector); refresh_iomux_function_clock_downstream(clocks); - if clocks.iomux_function_clock_refcount > 0 { - match new_selector { - IomuxFunctionClockConfig::PllF80m => request_pll_f80m(clocks), - IomuxFunctionClockConfig::RcFastClk => request_rc_fast_clk(clocks), - IomuxFunctionClockConfig::XtalClk => request_xtal_clk(clocks), - } - configure_iomux_function_clock_impl(clocks, old_selector, new_selector); - if let Some(old_selector) = old_selector { - match old_selector { - IomuxFunctionClockConfig::PllF80m => release_pll_f80m(clocks), - IomuxFunctionClockConfig::RcFastClk => release_rc_fast_clk(clocks), - IomuxFunctionClockConfig::XtalClk => release_xtal_clk(clocks), - } + match new_selector { + IomuxFunctionClockConfig::PllF80m => request_pll_f80m(clocks), + IomuxFunctionClockConfig::RcFastClk => request_rc_fast_clk(clocks), + IomuxFunctionClockConfig::XtalClk => request_xtal_clk(clocks), + } + configure_iomux_function_clock_impl(clocks, old_selector, new_selector); + if let Some(old_selector) = old_selector { + match old_selector { + IomuxFunctionClockConfig::PllF80m => release_pll_f80m(clocks), + IomuxFunctionClockConfig::RcFastClk => release_rc_fast_clk(clocks), + IomuxFunctionClockConfig::XtalClk => release_xtal_clk(clocks), } - } else { - configure_iomux_function_clock_impl(clocks, old_selector, new_selector); } } pub fn iomux_function_clock_config( @@ -3251,26 +3286,22 @@ macro_rules! define_clock_tree_types { } pub fn request_iomux_function_clock(clocks: &mut ClockTree) { trace!("Requesting IOMUX_FUNCTION_CLOCK"); - if increment_reference_count(&mut clocks.iomux_function_clock_refcount) { - trace!("Enabling IOMUX_FUNCTION_CLOCK"); - match unwrap!(clocks.iomux_function_clock) { - IomuxFunctionClockConfig::PllF80m => request_pll_f80m(clocks), - IomuxFunctionClockConfig::RcFastClk => request_rc_fast_clk(clocks), - IomuxFunctionClockConfig::XtalClk => request_xtal_clk(clocks), - } - enable_iomux_function_clock_impl(clocks, true); + trace!("Enabling IOMUX_FUNCTION_CLOCK"); + match unwrap!(clocks.iomux_function_clock) { + IomuxFunctionClockConfig::PllF80m => request_pll_f80m(clocks), + IomuxFunctionClockConfig::RcFastClk => request_rc_fast_clk(clocks), + IomuxFunctionClockConfig::XtalClk => request_xtal_clk(clocks), } + enable_iomux_function_clock_impl(clocks, true); } pub fn release_iomux_function_clock(clocks: &mut ClockTree) { trace!("Releasing IOMUX_FUNCTION_CLOCK"); - if decrement_reference_count(&mut clocks.iomux_function_clock_refcount) { - trace!("Disabling IOMUX_FUNCTION_CLOCK"); - enable_iomux_function_clock_impl(clocks, false); - match unwrap!(clocks.iomux_function_clock) { - IomuxFunctionClockConfig::PllF80m => release_pll_f80m(clocks), - IomuxFunctionClockConfig::RcFastClk => release_rc_fast_clk(clocks), - IomuxFunctionClockConfig::XtalClk => release_xtal_clk(clocks), - } + trace!("Disabling IOMUX_FUNCTION_CLOCK"); + enable_iomux_function_clock_impl(clocks, false); + match unwrap!(clocks.iomux_function_clock) { + IomuxFunctionClockConfig::PllF80m => release_pll_f80m(clocks), + IomuxFunctionClockConfig::RcFastClk => release_rc_fast_clk(clocks), + IomuxFunctionClockConfig::XtalClk => release_xtal_clk(clocks), } } #[allow(unused_variables)] @@ -3578,6 +3609,54 @@ macro_rules! define_clock_tree_types { TimgCalibrationClockConfig::Xtal32kClk => xtal32k_clk_frequency(), } } + impl SdmInstance { + pub fn configure_function_clock( + self, + clocks: &mut ClockTree, + config: SdmFunctionClockConfig, + ) { + let old_config = clocks.sdm_function_clock[self as usize].replace(config); + refresh_sdm_function_clock_downstream(clocks, self); + self.configure_function_clock_impl(clocks, old_config, config); + } + pub fn function_clock_config( + self, + clocks: &mut ClockTree, + ) -> Option { + clocks.sdm_function_clock[self as usize] + } + pub fn request_function_clock(self, clocks: &mut ClockTree) { + trace!("Requesting {:?}::FUNCTION_CLOCK", self); + if increment_reference_count(&mut clocks.sdm_function_clock_refcount[self as usize]) + { + trace!("Enabling {:?}::FUNCTION_CLOCK", self); + request_iomux_function_clock(clocks); + self.enable_function_clock_impl(clocks, true); + } + } + pub fn release_function_clock(self, clocks: &mut ClockTree) { + trace!("Releasing {:?}::FUNCTION_CLOCK", self); + if decrement_reference_count(&mut clocks.sdm_function_clock_refcount[self as usize]) + { + trace!("Disabling {:?}::FUNCTION_CLOCK", self); + self.enable_function_clock_impl(clocks, false); + release_iomux_function_clock(clocks); + } + } + #[allow(unused_variables)] + pub fn function_clock_config_frequency( + clocks: &mut ClockTree, + config: SdmFunctionClockConfig, + ) -> u32 { + iomux_function_clock_frequency() + } + pub fn function_clock_frequency(self) -> u32 { + iomux_function_clock_frequency() + } + pub fn function_clock_source_frequency() -> u32 { + iomux_function_clock_frequency() + } + } impl I2cInstance { pub fn configure_function_clock( self, @@ -4484,6 +4563,9 @@ macro_rules! define_clock_tree_types { ::core::sync::atomic::Ordering::Release, ); } + for child_instance in [SdmInstance::GpioSd] { + refresh_sdm_function_clock_downstream(clocks, child_instance); + } } fn refresh_ledc_sclk_downstream(clocks: &mut ClockTree) { if let Some(config) = clocks.ledc_sclk { @@ -4518,6 +4600,7 @@ macro_rules! define_clock_tree_types { ); } } + fn refresh_sdm_function_clock_downstream(clocks: &mut ClockTree, instance: SdmInstance) {} fn refresh_i2c_function_clock_downstream(clocks: &mut ClockTree, instance: I2cInstance) { if let Some(config) = clocks.i2c_function_clock[instance as usize] { I2C_FUNCTION_CLOCK_FREQ_CACHE[instance as usize].store( @@ -4859,9 +4942,6 @@ macro_rules! implement_peripheral_clocks { crate::peripherals::GPIO_SD::regs() .clock_gate() .modify(|_, w| w.clk_en().bit(enable)); - crate::peripherals::GPIO_SD::regs() - .sigmadelta_misc() - .modify(|_, w| w.function_clk_en().bit(enable)); } Peripheral::Hmac => { crate::peripherals::SYSTEM::regs() diff --git a/esp-metadata-generated/src/_generated_esp32h2.rs b/esp-metadata-generated/src/_generated_esp32h2.rs index 138d304fba8..88ab916a19a 100644 --- a/esp-metadata-generated/src/_generated_esp32h2.rs +++ b/esp-metadata-generated/src/_generated_esp32h2.rs @@ -1300,6 +1300,22 @@ macro_rules! for_each_wakeup_source { /// todo!() /// } /// +/// impl SdmInstance { +/// // SDM_FUNCTION_CLOCK +/// +/// fn enable_function_clock_impl(self, _clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_function_clock_impl( +/// self, +/// _clocks: &mut ClockTree, +/// _old_config: Option, +/// _new_config: SdmFunctionClockConfig, +/// ) { +/// todo!() +/// } +/// } /// impl I2cInstance { /// // I2C_FUNCTION_CLOCK /// @@ -1460,6 +1476,11 @@ macro_rules! for_each_wakeup_source { /// ``` macro_rules! define_clock_tree_types { () => { + #[derive(Clone, Copy, PartialEq, Eq, Debug)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum SdmInstance { + GpioSd = 0, + } #[derive(Clone, Copy, PartialEq, Eq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum I2cInstance { @@ -1646,6 +1667,18 @@ macro_rules! define_clock_tree_types { /// Selects `XTAL32K_CLK`. Xtal32kClk, } + /// Configures the `GPIO_SD_FUNCTION_CLOCK` clock node. + /// + /// The output is calculated as `OUTPUT = IOMUX_FUNCTION_CLOCK`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct SdmFunctionClockConfig {} + impl SdmFunctionClockConfig { + /// Creates a new configuration for the FUNCTION_CLOCK clock node. + pub const fn new() -> Self { + Self {} + } + } #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum I2cFunctionClockSclk { @@ -1860,6 +1893,7 @@ macro_rules! define_clock_tree_types { lp_fast_clk: Option, lp_slow_clk: Option, timg_calibration_clock: Option, + sdm_function_clock: [Option; 1], i2c_function_clock: [Option; 2], mcpwm_function_clock: [Option; 1], parl_io_rx_clock: [Option; 1], @@ -1873,12 +1907,12 @@ macro_rules! define_clock_tree_types { pll_f96m_clk_refcount: u32, pll_f48m_clk_refcount: u32, rc_fast_clk_refcount: u32, - iomux_function_clock_refcount: u32, xtal32k_clk_refcount: u32, hp_root_clk_refcount: u32, apb_clk_refcount: u32, lp_fast_clk_refcount: u32, timg_calibration_clock_refcount: u32, + sdm_function_clock_refcount: [u32; 1], i2c_function_clock_refcount: [u32; 2], mcpwm_function_clock_refcount: [u32; 1], parl_io_rx_clock_refcount: [u32; 1], @@ -1931,6 +1965,10 @@ macro_rules! define_clock_tree_types { pub fn timg_calibration_clock(&self) -> Option { self.timg_calibration_clock } + /// Returns the current configuration of the GPIO_SD_FUNCTION_CLOCK clock tree node + pub fn gpio_sd_function_clock(&self) -> Option { + self.sdm_function_clock[SdmInstance::GpioSd as usize] + } /// Returns the current configuration of the I2C0_FUNCTION_CLOCK clock tree node pub fn i2c0_function_clock(&self) -> Option { self.i2c_function_clock[I2cInstance::I2c0 as usize] @@ -2003,6 +2041,7 @@ macro_rules! define_clock_tree_types { lp_fast_clk: None, lp_slow_clk: None, timg_calibration_clock: None, + sdm_function_clock: [None; 1], i2c_function_clock: [None; 2], mcpwm_function_clock: [None; 1], parl_io_rx_clock: [None; 1], @@ -2016,12 +2055,12 @@ macro_rules! define_clock_tree_types { pll_f96m_clk_refcount: 0, pll_f48m_clk_refcount: 0, rc_fast_clk_refcount: 0, - iomux_function_clock_refcount: 0, xtal32k_clk_refcount: 0, hp_root_clk_refcount: 0, apb_clk_refcount: 0, lp_fast_clk_refcount: 0, timg_calibration_clock_refcount: 0, + sdm_function_clock_refcount: [0; 1], i2c_function_clock_refcount: [0; 2], mcpwm_function_clock_refcount: [0; 1], parl_io_rx_clock_refcount: [0; 1], @@ -2173,20 +2212,16 @@ macro_rules! define_clock_tree_types { ) { let old_selector = clocks.iomux_function_clock.replace(new_selector); refresh_iomux_function_clock_downstream(clocks); - if clocks.iomux_function_clock_refcount > 0 { - match new_selector { - IomuxFunctionClockConfig::XtalClk => request_xtal_clk(clocks), - IomuxFunctionClockConfig::PllF48m => request_pll_f48m_clk(clocks), - } - configure_iomux_function_clock_impl(clocks, old_selector, new_selector); - if let Some(old_selector) = old_selector { - match old_selector { - IomuxFunctionClockConfig::XtalClk => release_xtal_clk(clocks), - IomuxFunctionClockConfig::PllF48m => release_pll_f48m_clk(clocks), - } + match new_selector { + IomuxFunctionClockConfig::XtalClk => request_xtal_clk(clocks), + IomuxFunctionClockConfig::PllF48m => request_pll_f48m_clk(clocks), + } + configure_iomux_function_clock_impl(clocks, old_selector, new_selector); + if let Some(old_selector) = old_selector { + match old_selector { + IomuxFunctionClockConfig::XtalClk => release_xtal_clk(clocks), + IomuxFunctionClockConfig::PllF48m => release_pll_f48m_clk(clocks), } - } else { - configure_iomux_function_clock_impl(clocks, old_selector, new_selector); } } pub fn iomux_function_clock_config( @@ -2196,24 +2231,20 @@ macro_rules! define_clock_tree_types { } pub fn request_iomux_function_clock(clocks: &mut ClockTree) { trace!("Requesting IOMUX_FUNCTION_CLOCK"); - if increment_reference_count(&mut clocks.iomux_function_clock_refcount) { - trace!("Enabling IOMUX_FUNCTION_CLOCK"); - match unwrap!(clocks.iomux_function_clock) { - IomuxFunctionClockConfig::XtalClk => request_xtal_clk(clocks), - IomuxFunctionClockConfig::PllF48m => request_pll_f48m_clk(clocks), - } - enable_iomux_function_clock_impl(clocks, true); + trace!("Enabling IOMUX_FUNCTION_CLOCK"); + match unwrap!(clocks.iomux_function_clock) { + IomuxFunctionClockConfig::XtalClk => request_xtal_clk(clocks), + IomuxFunctionClockConfig::PllF48m => request_pll_f48m_clk(clocks), } + enable_iomux_function_clock_impl(clocks, true); } pub fn release_iomux_function_clock(clocks: &mut ClockTree) { trace!("Releasing IOMUX_FUNCTION_CLOCK"); - if decrement_reference_count(&mut clocks.iomux_function_clock_refcount) { - trace!("Disabling IOMUX_FUNCTION_CLOCK"); - enable_iomux_function_clock_impl(clocks, false); - match unwrap!(clocks.iomux_function_clock) { - IomuxFunctionClockConfig::XtalClk => release_xtal_clk(clocks), - IomuxFunctionClockConfig::PllF48m => release_pll_f48m_clk(clocks), - } + trace!("Disabling IOMUX_FUNCTION_CLOCK"); + enable_iomux_function_clock_impl(clocks, false); + match unwrap!(clocks.iomux_function_clock) { + IomuxFunctionClockConfig::XtalClk => release_xtal_clk(clocks), + IomuxFunctionClockConfig::PllF48m => release_pll_f48m_clk(clocks), } } #[allow(unused_variables)] @@ -2669,6 +2700,54 @@ macro_rules! define_clock_tree_types { TimgCalibrationClockConfig::Xtal32kClk => xtal32k_clk_frequency(), } } + impl SdmInstance { + pub fn configure_function_clock( + self, + clocks: &mut ClockTree, + config: SdmFunctionClockConfig, + ) { + let old_config = clocks.sdm_function_clock[self as usize].replace(config); + refresh_sdm_function_clock_downstream(clocks, self); + self.configure_function_clock_impl(clocks, old_config, config); + } + pub fn function_clock_config( + self, + clocks: &mut ClockTree, + ) -> Option { + clocks.sdm_function_clock[self as usize] + } + pub fn request_function_clock(self, clocks: &mut ClockTree) { + trace!("Requesting {:?}::FUNCTION_CLOCK", self); + if increment_reference_count(&mut clocks.sdm_function_clock_refcount[self as usize]) + { + trace!("Enabling {:?}::FUNCTION_CLOCK", self); + request_iomux_function_clock(clocks); + self.enable_function_clock_impl(clocks, true); + } + } + pub fn release_function_clock(self, clocks: &mut ClockTree) { + trace!("Releasing {:?}::FUNCTION_CLOCK", self); + if decrement_reference_count(&mut clocks.sdm_function_clock_refcount[self as usize]) + { + trace!("Disabling {:?}::FUNCTION_CLOCK", self); + self.enable_function_clock_impl(clocks, false); + release_iomux_function_clock(clocks); + } + } + #[allow(unused_variables)] + pub fn function_clock_config_frequency( + clocks: &mut ClockTree, + config: SdmFunctionClockConfig, + ) -> u32 { + iomux_function_clock_frequency() + } + pub fn function_clock_frequency(self) -> u32 { + iomux_function_clock_frequency() + } + pub fn function_clock_source_frequency() -> u32 { + iomux_function_clock_frequency() + } + } impl I2cInstance { pub fn configure_function_clock( self, @@ -3534,6 +3613,9 @@ macro_rules! define_clock_tree_types { ::core::sync::atomic::Ordering::Release, ); } + for child_instance in [SdmInstance::GpioSd] { + refresh_sdm_function_clock_downstream(clocks, child_instance); + } } fn refresh_hp_root_clk_downstream(clocks: &mut ClockTree) { if let Some(config) = clocks.hp_root_clk { @@ -3595,6 +3677,7 @@ macro_rules! define_clock_tree_types { ); } } + fn refresh_sdm_function_clock_downstream(clocks: &mut ClockTree, instance: SdmInstance) {} fn refresh_i2c_function_clock_downstream(clocks: &mut ClockTree, instance: I2cInstance) { if let Some(config) = clocks.i2c_function_clock[instance as usize] { I2C_FUNCTION_CLOCK_FREQ_CACHE[instance as usize].store( @@ -3835,9 +3918,6 @@ macro_rules! implement_peripheral_clocks { crate::peripherals::GPIO_SD::regs() .clock_gate() .modify(|_, w| w.clk_en().bit(enable)); - crate::peripherals::GPIO_SD::regs() - .sigmadelta_misc() - .modify(|_, w| w.function_clk_en().bit(enable)); } Peripheral::Hmac => { crate::peripherals::SYSTEM::regs() diff --git a/esp-metadata-generated/src/_generated_esp32p4.rs b/esp-metadata-generated/src/_generated_esp32p4.rs index 4c20be723c4..47d048dc7aa 100644 --- a/esp-metadata-generated/src/_generated_esp32p4.rs +++ b/esp-metadata-generated/src/_generated_esp32p4.rs @@ -1452,6 +1452,22 @@ macro_rules! for_each_wakeup_source { /// todo!() /// } /// +/// impl SdmInstance { +/// // SDM_FUNCTION_CLOCK +/// +/// fn enable_function_clock_impl(self, _clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_function_clock_impl( +/// self, +/// _clocks: &mut ClockTree, +/// _old_config: Option, +/// _new_config: SdmFunctionClockConfig, +/// ) { +/// todo!() +/// } +/// } /// impl TimgInstance { /// // TIMG_FUNCTION_CLOCK /// @@ -1595,6 +1611,11 @@ macro_rules! for_each_wakeup_source { /// ``` macro_rules! define_clock_tree_types { () => { + #[derive(Clone, Copy, PartialEq, Eq, Debug)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum SdmInstance { + GpioSd = 0, + } #[derive(Clone, Copy, PartialEq, Eq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum TimgInstance { @@ -1843,6 +1864,18 @@ macro_rules! define_clock_tree_types { /// Selects `XTAL32K_CLK`. Xtal32kClk, } + /// Configures the `GPIO_SD_FUNCTION_CLOCK` clock node. + /// + /// The output is calculated as `OUTPUT = IOMUX_FUNCTION_CLOCK`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct SdmFunctionClockConfig {} + impl SdmFunctionClockConfig { + /// Creates a new configuration for the FUNCTION_CLOCK clock node. + pub const fn new() -> Self { + Self {} + } + } /// The list of clock signals that the `TIMG0_FUNCTION_CLOCK` multiplexer can output. #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -2110,6 +2143,7 @@ macro_rules! define_clock_tree_types { lp_fast_clk: Option, lp_slow_clk: Option, timg_calibration_clock: Option, + sdm_function_clock: [Option; 1], timg_function_clock: [Option; 2], timg_wdt_clock: [Option; 2], uart_function_clock: [Option; 5], @@ -2127,11 +2161,11 @@ macro_rules! define_clock_tree_types { rc32k_clk_refcount: u32, pll_f80m_refcount: u32, pll_f120m_refcount: u32, - iomux_function_clock_refcount: u32, pll_f50m_refcount: u32, apb_clk_refcount: u32, lp_fast_clk_refcount: u32, timg_calibration_clock_refcount: u32, + sdm_function_clock_refcount: [u32; 1], timg_function_clock_refcount: [u32; 2], timg_wdt_clock_refcount: [u32; 2], uart_function_clock_refcount: [u32; 5], @@ -2195,6 +2229,10 @@ macro_rules! define_clock_tree_types { pub fn timg_calibration_clock(&self) -> Option { self.timg_calibration_clock } + /// Returns the current configuration of the GPIO_SD_FUNCTION_CLOCK clock tree node + pub fn gpio_sd_function_clock(&self) -> Option { + self.sdm_function_clock[SdmInstance::GpioSd as usize] + } /// Returns the current configuration of the TIMG0_FUNCTION_CLOCK clock tree node pub fn timg0_function_clock(&self) -> Option { self.timg_function_clock[TimgInstance::Timg0 as usize] @@ -2294,6 +2332,7 @@ macro_rules! define_clock_tree_types { lp_fast_clk: None, lp_slow_clk: None, timg_calibration_clock: None, + sdm_function_clock: [None; 1], timg_function_clock: [None; 2], timg_wdt_clock: [None; 2], uart_function_clock: [None; 5], @@ -2311,11 +2350,11 @@ macro_rules! define_clock_tree_types { rc32k_clk_refcount: 0, pll_f80m_refcount: 0, pll_f120m_refcount: 0, - iomux_function_clock_refcount: 0, pll_f50m_refcount: 0, apb_clk_refcount: 0, lp_fast_clk_refcount: 0, timg_calibration_clock_refcount: 0, + sdm_function_clock_refcount: [0; 1], timg_function_clock_refcount: [0; 2], timg_wdt_clock_refcount: [0; 2], uart_function_clock_refcount: [0; 5], @@ -2656,20 +2695,16 @@ macro_rules! define_clock_tree_types { ) { let old_selector = clocks.iomux_function_clock.replace(new_selector); refresh_iomux_function_clock_downstream(clocks); - if clocks.iomux_function_clock_refcount > 0 { - match new_selector { - IomuxFunctionClockConfig::XtalClk => request_xtal_clk(clocks), - IomuxFunctionClockConfig::PllF80m => request_pll_f80m(clocks), - } - configure_iomux_function_clock_impl(clocks, old_selector, new_selector); - if let Some(old_selector) = old_selector { - match old_selector { - IomuxFunctionClockConfig::XtalClk => release_xtal_clk(clocks), - IomuxFunctionClockConfig::PllF80m => release_pll_f80m(clocks), - } + match new_selector { + IomuxFunctionClockConfig::XtalClk => request_xtal_clk(clocks), + IomuxFunctionClockConfig::PllF80m => request_pll_f80m(clocks), + } + configure_iomux_function_clock_impl(clocks, old_selector, new_selector); + if let Some(old_selector) = old_selector { + match old_selector { + IomuxFunctionClockConfig::XtalClk => release_xtal_clk(clocks), + IomuxFunctionClockConfig::PllF80m => release_pll_f80m(clocks), } - } else { - configure_iomux_function_clock_impl(clocks, old_selector, new_selector); } } pub fn iomux_function_clock_config( @@ -2679,24 +2714,20 @@ macro_rules! define_clock_tree_types { } pub fn request_iomux_function_clock(clocks: &mut ClockTree) { trace!("Requesting IOMUX_FUNCTION_CLOCK"); - if increment_reference_count(&mut clocks.iomux_function_clock_refcount) { - trace!("Enabling IOMUX_FUNCTION_CLOCK"); - match unwrap!(clocks.iomux_function_clock) { - IomuxFunctionClockConfig::XtalClk => request_xtal_clk(clocks), - IomuxFunctionClockConfig::PllF80m => request_pll_f80m(clocks), - } - enable_iomux_function_clock_impl(clocks, true); + trace!("Enabling IOMUX_FUNCTION_CLOCK"); + match unwrap!(clocks.iomux_function_clock) { + IomuxFunctionClockConfig::XtalClk => request_xtal_clk(clocks), + IomuxFunctionClockConfig::PllF80m => request_pll_f80m(clocks), } + enable_iomux_function_clock_impl(clocks, true); } pub fn release_iomux_function_clock(clocks: &mut ClockTree) { trace!("Releasing IOMUX_FUNCTION_CLOCK"); - if decrement_reference_count(&mut clocks.iomux_function_clock_refcount) { - trace!("Disabling IOMUX_FUNCTION_CLOCK"); - enable_iomux_function_clock_impl(clocks, false); - match unwrap!(clocks.iomux_function_clock) { - IomuxFunctionClockConfig::XtalClk => release_xtal_clk(clocks), - IomuxFunctionClockConfig::PllF80m => release_pll_f80m(clocks), - } + trace!("Disabling IOMUX_FUNCTION_CLOCK"); + enable_iomux_function_clock_impl(clocks, false); + match unwrap!(clocks.iomux_function_clock) { + IomuxFunctionClockConfig::XtalClk => release_xtal_clk(clocks), + IomuxFunctionClockConfig::PllF80m => release_pll_f80m(clocks), } } #[allow(unused_variables)] @@ -3179,6 +3210,54 @@ macro_rules! define_clock_tree_types { TimgCalibrationClockConfig::Xtal32kClk => xtal32k_clk_frequency(), } } + impl SdmInstance { + pub fn configure_function_clock( + self, + clocks: &mut ClockTree, + config: SdmFunctionClockConfig, + ) { + let old_config = clocks.sdm_function_clock[self as usize].replace(config); + refresh_sdm_function_clock_downstream(clocks, self); + self.configure_function_clock_impl(clocks, old_config, config); + } + pub fn function_clock_config( + self, + clocks: &mut ClockTree, + ) -> Option { + clocks.sdm_function_clock[self as usize] + } + pub fn request_function_clock(self, clocks: &mut ClockTree) { + trace!("Requesting {:?}::FUNCTION_CLOCK", self); + if increment_reference_count(&mut clocks.sdm_function_clock_refcount[self as usize]) + { + trace!("Enabling {:?}::FUNCTION_CLOCK", self); + request_iomux_function_clock(clocks); + self.enable_function_clock_impl(clocks, true); + } + } + pub fn release_function_clock(self, clocks: &mut ClockTree) { + trace!("Releasing {:?}::FUNCTION_CLOCK", self); + if decrement_reference_count(&mut clocks.sdm_function_clock_refcount[self as usize]) + { + trace!("Disabling {:?}::FUNCTION_CLOCK", self); + self.enable_function_clock_impl(clocks, false); + release_iomux_function_clock(clocks); + } + } + #[allow(unused_variables)] + pub fn function_clock_config_frequency( + clocks: &mut ClockTree, + config: SdmFunctionClockConfig, + ) -> u32 { + iomux_function_clock_frequency() + } + pub fn function_clock_frequency(self) -> u32 { + iomux_function_clock_frequency() + } + pub fn function_clock_source_frequency() -> u32 { + iomux_function_clock_frequency() + } + } impl TimgInstance { pub fn configure_function_clock( self, @@ -4013,6 +4092,9 @@ macro_rules! define_clock_tree_types { ::core::sync::atomic::Ordering::Release, ); } + for child_instance in [SdmInstance::GpioSd] { + refresh_sdm_function_clock_downstream(clocks, child_instance); + } } fn refresh_cpu_root_clk_downstream(clocks: &mut ClockTree) { if let Some(config) = clocks.cpu_root_clk { @@ -4083,6 +4165,7 @@ macro_rules! define_clock_tree_types { ); } } + fn refresh_sdm_function_clock_downstream(clocks: &mut ClockTree, instance: SdmInstance) {} fn refresh_timg_function_clock_downstream(clocks: &mut ClockTree, instance: TimgInstance) { if let Some(config) = clocks.timg_function_clock[instance as usize] { TIMG_FUNCTION_CLOCK_FREQ_CACHE[instance as usize].store( @@ -4197,8 +4280,6 @@ macro_rules! implement_peripheral_clocks { Emac, /// GPIO_SD peripheral clock signal GpioSd, - /// GPIO_SD peripheral clock signal - GpioSd, /// HMAC peripheral clock signal Hmac, /// I2C0 peripheral clock signal @@ -4291,7 +4372,6 @@ macro_rules! implement_peripheral_clocks { Self::Ecdsa, Self::Emac, Self::GpioSd, - Self::GpioSd, Self::Hmac, Self::I2c0, Self::I2c1, @@ -4376,17 +4456,6 @@ macro_rules! implement_peripheral_clocks { crate::peripherals::GPIO_SD::regs() .clock_gate() .modify(|_, w| w.clk_en().bit(enable)); - crate::peripherals::GPIO_SD::regs() - .sigmadelta_misc() - .modify(|_, w| w.function_clk_en().bit(enable)); - } - Peripheral::GpioSd => { - crate::peripherals::GPIO_SD::regs() - .clock_gate() - .modify(|_, w| w.clk_en().bit(enable)); - crate::peripherals::GPIO_SD::regs() - .sigmadelta_misc() - .modify(|_, w| w.function_clk_en().bit(enable)); } Peripheral::Hmac => { crate::peripherals::HP_SYS_CLKRST::regs() @@ -4639,9 +4708,6 @@ macro_rules! implement_peripheral_clocks { Peripheral::GpioSd => { let _ = reset; } - Peripheral::GpioSd => { - let _ = reset; - } Peripheral::Hmac => { crate::peripherals::HP_SYS_CLKRST::regs() .hp_rst_en2() diff --git a/esp-metadata-generated/src/_generated_esp32s2.rs b/esp-metadata-generated/src/_generated_esp32s2.rs index e84837e3b1c..c86a2d98f20 100644 --- a/esp-metadata-generated/src/_generated_esp32s2.rs +++ b/esp-metadata-generated/src/_generated_esp32s2.rs @@ -1227,6 +1227,22 @@ macro_rules! for_each_wakeup_source { /// todo!() /// } /// +/// impl SdmInstance { +/// // SDM_FUNCTION_CLOCK +/// +/// fn enable_function_clock_impl(self, _clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_function_clock_impl( +/// self, +/// _clocks: &mut ClockTree, +/// _old_config: Option, +/// _new_config: SdmFunctionClockConfig, +/// ) { +/// todo!() +/// } +/// } /// impl I2cInstance { /// // I2C_FUNCTION_CLOCK /// @@ -1340,6 +1356,11 @@ macro_rules! for_each_wakeup_source { /// ``` macro_rules! define_clock_tree_types { () => { + #[derive(Clone, Copy, PartialEq, Eq, Debug)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum SdmInstance { + GpioSd = 0, + } #[derive(Clone, Copy, PartialEq, Eq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum I2cInstance { @@ -1640,6 +1661,18 @@ macro_rules! define_clock_tree_types { /// Selects `XTAL32K_CLK`. Xtal32kClk, } + /// Configures the `GPIO_SD_FUNCTION_CLOCK` clock node. + /// + /// The output is calculated as `OUTPUT = APB_CLK`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct SdmFunctionClockConfig {} + impl SdmFunctionClockConfig { + /// Creates a new configuration for the FUNCTION_CLOCK clock node. + pub const fn new() -> Self { + Self {} + } + } #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum I2cFunctionClockSclk { @@ -1791,6 +1824,7 @@ macro_rules! define_clock_tree_types { rtc_slow_clk: Option, rtc_fast_clk: Option, timg_calibration_clock: Option, + sdm_function_clock: [Option; 1], i2c_function_clock: [Option; 2], rmt_sclk: [Option; 1], spi_function_clock: [Option; 2], @@ -1807,6 +1841,7 @@ macro_rules! define_clock_tree_types { rtc_fast_clk_refcount: u32, uart_mem_clk_refcount: u32, timg_calibration_clock_refcount: u32, + sdm_function_clock_refcount: [u32; 1], i2c_function_clock_refcount: [u32; 2], rmt_sclk_refcount: [u32; 1], spi_function_clock_refcount: [u32; 2], @@ -1880,6 +1915,10 @@ macro_rules! define_clock_tree_types { pub fn timg_calibration_clock(&self) -> Option { self.timg_calibration_clock } + /// Returns the current configuration of the GPIO_SD_FUNCTION_CLOCK clock tree node + pub fn gpio_sd_function_clock(&self) -> Option { + self.sdm_function_clock[SdmInstance::GpioSd as usize] + } /// Returns the current configuration of the I2C0_FUNCTION_CLOCK clock tree node pub fn i2c0_function_clock(&self) -> Option { self.i2c_function_clock[I2cInstance::I2c0 as usize] @@ -1950,6 +1989,7 @@ macro_rules! define_clock_tree_types { rtc_slow_clk: None, rtc_fast_clk: None, timg_calibration_clock: None, + sdm_function_clock: [None; 1], i2c_function_clock: [None; 2], rmt_sclk: [None; 1], spi_function_clock: [None; 2], @@ -1966,6 +2006,7 @@ macro_rules! define_clock_tree_types { rtc_fast_clk_refcount: 0, uart_mem_clk_refcount: 0, timg_calibration_clock_refcount: 0, + sdm_function_clock_refcount: [0; 1], i2c_function_clock_refcount: [0; 2], rmt_sclk_refcount: [0; 1], spi_function_clock_refcount: [0; 2], @@ -2901,6 +2942,54 @@ macro_rules! define_clock_tree_types { TimgCalibrationClockConfig::Xtal32kClk => xtal32k_clk_frequency(), } } + impl SdmInstance { + pub fn configure_function_clock( + self, + clocks: &mut ClockTree, + config: SdmFunctionClockConfig, + ) { + let old_config = clocks.sdm_function_clock[self as usize].replace(config); + refresh_sdm_function_clock_downstream(clocks, self); + self.configure_function_clock_impl(clocks, old_config, config); + } + pub fn function_clock_config( + self, + clocks: &mut ClockTree, + ) -> Option { + clocks.sdm_function_clock[self as usize] + } + pub fn request_function_clock(self, clocks: &mut ClockTree) { + trace!("Requesting {:?}::FUNCTION_CLOCK", self); + if increment_reference_count(&mut clocks.sdm_function_clock_refcount[self as usize]) + { + trace!("Enabling {:?}::FUNCTION_CLOCK", self); + request_apb_clk(clocks); + self.enable_function_clock_impl(clocks, true); + } + } + pub fn release_function_clock(self, clocks: &mut ClockTree) { + trace!("Releasing {:?}::FUNCTION_CLOCK", self); + if decrement_reference_count(&mut clocks.sdm_function_clock_refcount[self as usize]) + { + trace!("Disabling {:?}::FUNCTION_CLOCK", self); + self.enable_function_clock_impl(clocks, false); + release_apb_clk(clocks); + } + } + #[allow(unused_variables)] + pub fn function_clock_config_frequency( + clocks: &mut ClockTree, + config: SdmFunctionClockConfig, + ) -> u32 { + apb_clk_frequency() + } + pub fn function_clock_frequency(self) -> u32 { + apb_clk_frequency() + } + pub fn function_clock_source_frequency() -> u32 { + apb_clk_frequency() + } + } impl I2cInstance { pub fn configure_function_clock( self, @@ -3545,6 +3634,9 @@ macro_rules! define_clock_tree_types { ::core::sync::atomic::Ordering::Release, ); } + for child_instance in [SdmInstance::GpioSd] { + refresh_sdm_function_clock_downstream(clocks, child_instance); + } for child_instance in [I2cInstance::I2c0, I2cInstance::I2c1] { refresh_i2c_function_clock_downstream(clocks, child_instance); } @@ -3578,6 +3670,7 @@ macro_rules! define_clock_tree_types { refresh_uart_function_clock_downstream(clocks, child_instance); } } + fn refresh_sdm_function_clock_downstream(clocks: &mut ClockTree, instance: SdmInstance) {} fn refresh_i2c_function_clock_downstream(clocks: &mut ClockTree, instance: I2cInstance) { if let Some(config) = clocks.i2c_function_clock[instance as usize] { I2C_FUNCTION_CLOCK_FREQ_CACHE[instance as usize].store( @@ -3780,9 +3873,6 @@ macro_rules! implement_peripheral_clocks { crate::peripherals::GPIO_SD::regs() .clock_gate() .modify(|_, w| w.clk_en().bit(enable)); - crate::peripherals::GPIO_SD::regs() - .sigmadelta_misc() - .modify(|_, w| w.function_clk_en().bit(enable)); } Peripheral::Hmac => { crate::peripherals::SYSTEM::regs() diff --git a/esp-metadata-generated/src/_generated_esp32s3.rs b/esp-metadata-generated/src/_generated_esp32s3.rs index 3750412a045..ed2061d7c12 100644 --- a/esp-metadata-generated/src/_generated_esp32s3.rs +++ b/esp-metadata-generated/src/_generated_esp32s3.rs @@ -1431,6 +1431,22 @@ macro_rules! for_each_wakeup_source { /// todo!() /// } /// +/// impl SdmInstance { +/// // SDM_FUNCTION_CLOCK +/// +/// fn enable_function_clock_impl(self, _clocks: &mut ClockTree, _en: bool) { +/// todo!() +/// } +/// +/// fn configure_function_clock_impl( +/// self, +/// _clocks: &mut ClockTree, +/// _old_config: Option, +/// _new_config: SdmFunctionClockConfig, +/// ) { +/// todo!() +/// } +/// } /// impl I2cInstance { /// // I2C_FUNCTION_CLOCK /// @@ -1560,6 +1576,11 @@ macro_rules! for_each_wakeup_source { /// ``` macro_rules! define_clock_tree_types { () => { + #[derive(Clone, Copy, PartialEq, Eq, Debug)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum SdmInstance { + GpioSd = 0, + } #[derive(Clone, Copy, PartialEq, Eq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum I2cInstance { @@ -1789,6 +1810,18 @@ macro_rules! define_clock_tree_types { /// Selects `XTAL32K_CLK`. Xtal32kClk, } + /// Configures the `GPIO_SD_FUNCTION_CLOCK` clock node. + /// + /// The output is calculated as `OUTPUT = APB_CLK`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct SdmFunctionClockConfig {} + impl SdmFunctionClockConfig { + /// Creates a new configuration for the FUNCTION_CLOCK clock node. + pub const fn new() -> Self { + Self {} + } + } #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum I2cFunctionClockSclk { @@ -1977,6 +2010,7 @@ macro_rules! define_clock_tree_types { rtc_fast_clk: Option, low_power_clk: Option, timg_calibration_clock: Option, + sdm_function_clock: [Option; 1], i2c_function_clock: [Option; 2], mcpwm_function_clock: [Option; 2], rmt_sclk: [Option; 1], @@ -1997,6 +2031,7 @@ macro_rules! define_clock_tree_types { low_power_clk_refcount: u32, uart_mem_clk_refcount: u32, timg_calibration_clock_refcount: u32, + sdm_function_clock_refcount: [u32; 1], i2c_function_clock_refcount: [u32; 2], mcpwm_function_clock_refcount: [u32; 2], rmt_sclk_refcount: [u32; 1], @@ -2063,6 +2098,10 @@ macro_rules! define_clock_tree_types { pub fn timg_calibration_clock(&self) -> Option { self.timg_calibration_clock } + /// Returns the current configuration of the GPIO_SD_FUNCTION_CLOCK clock tree node + pub fn gpio_sd_function_clock(&self) -> Option { + self.sdm_function_clock[SdmInstance::GpioSd as usize] + } /// Returns the current configuration of the I2C0_FUNCTION_CLOCK clock tree node pub fn i2c0_function_clock(&self) -> Option { self.i2c_function_clock[I2cInstance::I2c0 as usize] @@ -2151,6 +2190,7 @@ macro_rules! define_clock_tree_types { rtc_fast_clk: None, low_power_clk: None, timg_calibration_clock: None, + sdm_function_clock: [None; 1], i2c_function_clock: [None; 2], mcpwm_function_clock: [None; 2], rmt_sclk: [None; 1], @@ -2171,6 +2211,7 @@ macro_rules! define_clock_tree_types { low_power_clk_refcount: 0, uart_mem_clk_refcount: 0, timg_calibration_clock_refcount: 0, + sdm_function_clock_refcount: [0; 1], i2c_function_clock_refcount: [0; 2], mcpwm_function_clock_refcount: [0; 2], rmt_sclk_refcount: [0; 1], @@ -3054,6 +3095,54 @@ macro_rules! define_clock_tree_types { TimgCalibrationClockConfig::Xtal32kClk => xtal32k_clk_frequency(), } } + impl SdmInstance { + pub fn configure_function_clock( + self, + clocks: &mut ClockTree, + config: SdmFunctionClockConfig, + ) { + let old_config = clocks.sdm_function_clock[self as usize].replace(config); + refresh_sdm_function_clock_downstream(clocks, self); + self.configure_function_clock_impl(clocks, old_config, config); + } + pub fn function_clock_config( + self, + clocks: &mut ClockTree, + ) -> Option { + clocks.sdm_function_clock[self as usize] + } + pub fn request_function_clock(self, clocks: &mut ClockTree) { + trace!("Requesting {:?}::FUNCTION_CLOCK", self); + if increment_reference_count(&mut clocks.sdm_function_clock_refcount[self as usize]) + { + trace!("Enabling {:?}::FUNCTION_CLOCK", self); + request_apb_clk(clocks); + self.enable_function_clock_impl(clocks, true); + } + } + pub fn release_function_clock(self, clocks: &mut ClockTree) { + trace!("Releasing {:?}::FUNCTION_CLOCK", self); + if decrement_reference_count(&mut clocks.sdm_function_clock_refcount[self as usize]) + { + trace!("Disabling {:?}::FUNCTION_CLOCK", self); + self.enable_function_clock_impl(clocks, false); + release_apb_clk(clocks); + } + } + #[allow(unused_variables)] + pub fn function_clock_config_frequency( + clocks: &mut ClockTree, + config: SdmFunctionClockConfig, + ) -> u32 { + apb_clk_frequency() + } + pub fn function_clock_frequency(self) -> u32 { + apb_clk_frequency() + } + pub fn function_clock_source_frequency() -> u32 { + apb_clk_frequency() + } + } impl I2cInstance { pub fn configure_function_clock( self, @@ -3797,6 +3886,9 @@ macro_rules! define_clock_tree_types { ::core::sync::atomic::Ordering::Release, ); } + for child_instance in [SdmInstance::GpioSd] { + refresh_sdm_function_clock_downstream(clocks, child_instance); + } for child_instance in [RmtInstance::Rmt] { refresh_rmt_sclk_downstream(clocks, child_instance); } @@ -3825,6 +3917,7 @@ macro_rules! define_clock_tree_types { refresh_mcpwm_function_clock_downstream(clocks, child_instance); } } + fn refresh_sdm_function_clock_downstream(clocks: &mut ClockTree, instance: SdmInstance) {} fn refresh_mcpwm_function_clock_downstream( clocks: &mut ClockTree, instance: McpwmInstance, @@ -4034,9 +4127,6 @@ macro_rules! implement_peripheral_clocks { crate::peripherals::GPIO_SD::regs() .clock_gate() .modify(|_, w| w.clk_en().bit(enable)); - crate::peripherals::GPIO_SD::regs() - .sigmadelta_misc() - .modify(|_, w| w.function_clk_en().bit(enable)); } Peripheral::Hmac => { crate::peripherals::SYSTEM::regs() diff --git a/esp-metadata/devices/esp32/clocks.toml b/esp-metadata/devices/esp32/clocks.toml index 3966755e698..c9c5f45d30f 100644 --- a/esp-metadata/devices/esp32/clocks.toml +++ b/esp-metadata/devices/esp32/clocks.toml @@ -155,6 +155,9 @@ system_clocks = { clock_tree = [ { name = "MEM_CLOCK", type = "generic", output = "UART_MEM_CLK" }, { name = "BAUD_RATE_GENERATOR", type = "generic", output = "(FUNCTION_CLOCK * 16) / (integral * 16 + fractional)", params = { fractional = "0..16", integral = "0..0x100000" } }, ] }, + { group = "SDM", clocks = [ + { name = "FUNCTION_CLOCK", type = "generic", output = "APB_CLK" }, + ] }, { group = "RMT", clocks = [ { name = "SCLK", type = "mux", wake_locking = true, variants = [ { name = "REF_TICK", outputs = "REF_TICK" }, diff --git a/esp-metadata/devices/esp32/soc.toml b/esp-metadata/devices/esp32/soc.toml index 8fc2ce01dfc..1a5be8a40cc 100644 --- a/esp-metadata/devices/esp32/soc.toml +++ b/esp-metadata/devices/esp32/soc.toml @@ -40,7 +40,7 @@ peripherals = [ { name = "FLASH_ENCRYPTION" }, { name = "FRC_TIMER" }, { name = "GPIO" }, - { name = "GPIO_SD" }, + { name = "GPIO_SD", clock_group = "SDM" }, { name = "HINF" }, { name = "I2C0", interrupts = { peri = "I2C_EXT0" }, stable = true, clock_group = "I2C" }, { name = "I2C1", interrupts = { peri = "I2C_EXT1" }, stable = true, clock_group = "I2C" }, diff --git a/esp-metadata/devices/esp32c3/clocks.toml b/esp-metadata/devices/esp32c3/clocks.toml index 813a8328747..a15c072570a 100644 --- a/esp-metadata/devices/esp32c3/clocks.toml +++ b/esp-metadata/devices/esp32c3/clocks.toml @@ -83,6 +83,9 @@ system_clocks = { clock_tree = [ { name = "XTAL_CLK", outputs = "XTAL_CLK" }, ] }, ] }, + { group = "SDM", clocks = [ + { name = "FUNCTION_CLOCK", type = "generic", output = "APB_CLK" }, + ] }, { group = "RMT", clocks = [ { name = "SCLK", type = "mux", wake_locking = true, variants = [ { name = "APB_CLK", outputs = "APB_CLK", default = true }, @@ -146,7 +149,7 @@ peripheral_clocks = { templates = [ { name = "Timg0", template_params = { peripheral = "timergroup" }, keep_enabled = true }, { name = "Ledc" }, { name = "Rmt" }, - { name = "GpioSd", template_params = { clk_en_template = "crate::peripherals::GPIO_SD::regs().clock_gate().modify(|_, w| w.clk_en().bit(enable)); crate::peripherals::GPIO_SD::regs().sigmadelta_misc().modify(|_, w| w.function_clk_en().bit(enable));", rst_template = "let _ = reset;" } }, + { name = "GpioSd", template_params = { clk_en_template = "crate::peripherals::GPIO_SD::regs().clock_gate().modify(|_, w| w.clk_en().bit(enable));", rst_template = "let _ = reset;" } }, { name = "Uhci0" }, { name = "I2cExt0" }, { name = "Spi2" }, diff --git a/esp-metadata/devices/esp32c3/soc.toml b/esp-metadata/devices/esp32c3/soc.toml index 3b324e25988..db2c5dca769 100644 --- a/esp-metadata/devices/esp32c3/soc.toml +++ b/esp-metadata/devices/esp32c3/soc.toml @@ -43,7 +43,7 @@ peripherals = [ { name = "FE" }, { name = "FE2" }, { name = "GPIO" }, - { name = "GPIO_SD" }, + { name = "GPIO_SD", clock_group = "SDM" }, { name = "HMAC" }, { name = "I2C_ANA_MST" }, { name = "I2C0", interrupts = { peri = "I2C_EXT0" }, stable = true, clock_group = "I2C" }, diff --git a/esp-metadata/devices/esp32c5/clocks.toml b/esp-metadata/devices/esp32c5/clocks.toml index 717c2fe6e0d..9a2aabad6ee 100644 --- a/esp-metadata/devices/esp32c5/clocks.toml +++ b/esp-metadata/devices/esp32c5/clocks.toml @@ -90,6 +90,9 @@ system_clocks = { clock_tree = [ { name = "RC_FAST_CLK", outputs = "RC_FAST_CLK" }, ] }, ] }, + { group = "SDM", clocks = [ + { name = "FUNCTION_CLOCK", type = "generic", output = "IOMUX_FUNCTION_CLOCK" }, + ] }, { group = "RMT", clocks = [ { name = "SCLK", type = "mux", variants = [ { name = "XTAL_CLK", outputs = "XTAL_CLK" }, @@ -145,7 +148,7 @@ peripheral_clocks = { templates = [ { name = "I2s0", template_params = { peripheral = "i2s" } }, { name = "Pcnt" }, { name = "Rmt" }, - { name = "GpioSd", template_params = { clk_en_template = "crate::peripherals::GPIO_SD::regs().clock_gate().modify(|_, w| w.clk_en().bit(enable)); crate::peripherals::GPIO_SD::regs().sigmadelta_misc().modify(|_, w| w.sigmadelta_clk_en().bit(enable));", rst_template = "let _ = reset;" } }, + { name = "GpioSd", template_params = { clk_en_template = "crate::peripherals::GPIO_SD::regs().clock_gate().modify(|_, w| w.clk_en().bit(enable));", rst_template = "let _ = reset;" } }, { name = "Systimer", keep_enabled = true }, # can be clocked from XTAL_CLK or RC_FAST_CLK, the latter has no divider? { name = "ApbSarAdc", template_params = { peripheral = "saradc", clk_en_field = "saradc_reg_clk_en", rst_field = "saradc_reg_rst_en" }, keep_enabled = true }, { name = "ParlIo", template_params = { clk_en_field = "parl_clk_en", rst_field = "parl_rst_en" } }, diff --git a/esp-metadata/devices/esp32c5/soc.toml b/esp-metadata/devices/esp32c5/soc.toml index 32117defaa1..d896a8de465 100644 --- a/esp-metadata/devices/esp32c5/soc.toml +++ b/esp-metadata/devices/esp32c5/soc.toml @@ -48,7 +48,7 @@ peripherals = [ { name = "EFUSE", hidden = true }, { name = "ETM", pac = "SOC_ETM" }, { name = "GPIO" }, - { name = "GPIO_SD", pac = "GPIO_EXT" }, + { name = "GPIO_SD", pac = "GPIO_EXT", clock_group = "SDM" }, # { name = "HINF" }, { name = "HMAC" }, { name = "HP_APM" }, diff --git a/esp-metadata/devices/esp32c6/clocks.toml b/esp-metadata/devices/esp32c6/clocks.toml index 1d00471cdcd..59664283dcd 100644 --- a/esp-metadata/devices/esp32c6/clocks.toml +++ b/esp-metadata/devices/esp32c6/clocks.toml @@ -86,6 +86,9 @@ system_clocks = { clock_tree = [ ], div_num = "0 .. 0x100" } }, { name = "BAUD_RATE_GENERATOR", type = "generic", output = "(FUNCTION_CLOCK * 16) / (integral * 16 + fractional)", params = { fractional = "0..16", integral = "0..0x1000" } }, ] }, + { group = "SDM", clocks = [ + { name = "FUNCTION_CLOCK", type = "generic", output = "IOMUX_FUNCTION_CLOCK" }, + ] }, { group = "RMT", clocks = [ { name = "SCLK", type = "mux", wake_locking = true, variants = [ { name = "PLL_F80M", outputs = "PLL_F80M", default = true }, @@ -160,7 +163,7 @@ peripheral_clocks = { templates = [ { name = "Uhci0", template_params = { peripheral = "uhci" } }, { name = "Rmt" }, { name = "Ledc" }, - { name = "GpioSd", template_params = { clk_en_template = "crate::peripherals::GPIO_SD::regs().clock_gate().modify(|_, w| w.clk_en().bit(enable)); crate::peripherals::GPIO_SD::regs().sigmadelta_misc().modify(|_, w| w.function_clk_en().bit(enable));", rst_template = "let _ = reset;" } }, + { name = "GpioSd", template_params = { clk_en_template = "crate::peripherals::GPIO_SD::regs().clock_gate().modify(|_, w| w.clk_en().bit(enable));", rst_template = "let _ = reset;" } }, { name = "Timg0", template_params = { conf_register = "timergroup(0).conf", clk_en_field = "clk_en", rst_field = "rst_en" }, keep_enabled = true }, { name = "Timg1", template_params = { conf_register = "timergroup(1).conf", clk_en_field = "clk_en", rst_field = "rst_en" } }, { name = "Systimer", keep_enabled = true }, diff --git a/esp-metadata/devices/esp32c6/soc.toml b/esp-metadata/devices/esp32c6/soc.toml index 8a1b91dbd89..d45a0b4f671 100644 --- a/esp-metadata/devices/esp32c6/soc.toml +++ b/esp-metadata/devices/esp32c6/soc.toml @@ -41,7 +41,7 @@ peripherals = [ { name = "EFUSE", hidden = true }, { name = "EXTMEM" }, { name = "GPIO" }, - { name = "GPIO_SD" }, + { name = "GPIO_SD", clock_group = "SDM" }, { name = "HINF" }, { name = "HMAC" }, { name = "HP_APM" }, diff --git a/esp-metadata/devices/esp32h2/clocks.toml b/esp-metadata/devices/esp32h2/clocks.toml index ec18b6576ea..b533dd4a113 100644 --- a/esp-metadata/devices/esp32h2/clocks.toml +++ b/esp-metadata/devices/esp32h2/clocks.toml @@ -61,6 +61,9 @@ system_clocks = { clock_tree = [ ], div_num = "0 .. 0x100" } }, { name = "BAUD_RATE_GENERATOR", type = "generic", output = "(FUNCTION_CLOCK * 16) / (integral * 16 + fractional)", params = { fractional = "0..16", integral = "0..0x1000" } }, ] }, + { group = "SDM", clocks = [ + { name = "FUNCTION_CLOCK", type = "generic", output = "IOMUX_FUNCTION_CLOCK" }, + ] }, { group = "RMT", clocks = [ { name = "SCLK", type = "mux", wake_locking = true, variants = [ { name = "XTAL_CLK", outputs = "XTAL_CLK", default = true }, @@ -135,7 +138,7 @@ peripheral_clocks = { templates = [ { name = "Uhci0", template_params = { peripheral = "uhci" } }, { name = "Rmt" }, { name = "Ledc" }, - { name = "GpioSd", template_params = { clk_en_template = "crate::peripherals::GPIO_SD::regs().clock_gate().modify(|_, w| w.clk_en().bit(enable)); crate::peripherals::GPIO_SD::regs().sigmadelta_misc().modify(|_, w| w.function_clk_en().bit(enable));", rst_template = "let _ = reset;" } }, + { name = "GpioSd", template_params = { clk_en_template = "crate::peripherals::GPIO_SD::regs().clock_gate().modify(|_, w| w.clk_en().bit(enable));", rst_template = "let _ = reset;" } }, { name = "Timg0", template_params = { conf_register = "timergroup(0).conf", clk_en_field = "clk_en", rst_field = "rst_en" }, keep_enabled = true }, { name = "Timg1", template_params = { conf_register = "timergroup(1).conf", clk_en_field = "clk_en", rst_field = "rst_en" } }, { name = "Systimer", keep_enabled = true }, diff --git a/esp-metadata/devices/esp32h2/soc.toml b/esp-metadata/devices/esp32h2/soc.toml index d68fde3c90e..8e1fcad356a 100644 --- a/esp-metadata/devices/esp32h2/soc.toml +++ b/esp-metadata/devices/esp32h2/soc.toml @@ -39,7 +39,7 @@ peripherals = [ { name = "ECC" }, { name = "EFUSE", hidden = true }, { name = "GPIO" }, - { name = "GPIO_SD" }, + { name = "GPIO_SD", clock_group = "SDM" }, { name = "HMAC" }, { name = "HP_APM" }, { name = "HP_SYS" }, diff --git a/esp-metadata/devices/esp32p4/clocks.toml b/esp-metadata/devices/esp32p4/clocks.toml index 7991dbc1539..06d58c97eb7 100644 --- a/esp-metadata/devices/esp32p4/clocks.toml +++ b/esp-metadata/devices/esp32p4/clocks.toml @@ -83,6 +83,9 @@ system_clocks = { clock_tree = [ ], div_num = "0 .. 0x100" } }, { name = "BAUD_RATE_GENERATOR", type = "generic", output = "(FUNCTION_CLOCK * 16) / (integral * 16 + fractional)", params = { fractional = "0..16", integral = "0..0x1000" } }, ] }, + { group = "SDM", clocks = [ + { name = "FUNCTION_CLOCK", type = "generic", output = "IOMUX_FUNCTION_CLOCK" }, + ] }, { group = "TIMG", clocks = [ { name = "FUNCTION_CLOCK", type = "mux", variants = [ { name = "XTAL_CLK", outputs = "XTAL_CLK", default = true }, @@ -185,9 +188,8 @@ peripheral_clocks = { templates = [ { name = "Iomux", template_params = { clk_en_template = "{{control}}::regs().peri_clk_ctrl26().modify(|_, w| w.iomux_clk_en().bit(enable));", rst_group = "1" }, keep_enabled = true }, # PWM/Counter/RMT -- LCD/CAM clock gate lives in SOC_CLK_CTRL3 (not modelled in current PAC), keep stub. { name = "Ledc", template_params = { rst_group = "1", rst_field = "ledc" } }, - { name = "GpioSd", template_params = { clk_en_template = "crate::peripherals::GPIO_SD::regs().clock_gate().modify(|_, w| w.clk_en().bit(enable)); crate::peripherals::GPIO_SD::regs().sigmadelta_misc().modify(|_, w| w.function_clk_en().bit(enable));", rst_template = "let _ = reset;" } }, + { name = "GpioSd", template_params = { clk_en_template = "crate::peripherals::GPIO_SD::regs().clock_gate().modify(|_, w| w.clk_en().bit(enable));", rst_template = "let _ = reset;" } }, { name = "Pcnt", template_params = { clk_en_template = "{{apb_clk_template}}", rst_group = "1" } }, - { name = "GpioSd", template_params = { clk_en_template = "crate::peripherals::GPIO_SD::regs().clock_gate().modify(|_, w| w.clk_en().bit(enable)); crate::peripherals::GPIO_SD::regs().sigmadelta_misc().modify(|_, w| w.function_clk_en().bit(enable));", rst_template = "let _ = reset;" } }, { name = "Mcpwm0", template_params = { clk_en_template = "{{apb_clk_template}}", rst_group = "1", rst_field = "pwm0" } }, { name = "Mcpwm1", template_params = { clk_en_template = "{{apb_clk_template}}", rst_group = "1", rst_field = "pwm1" } }, # NOTE: `rmt_sys_clk_en` despite the name lives on SOC_CLK_CTRL2, not CTRL1. diff --git a/esp-metadata/devices/esp32p4/soc.toml b/esp-metadata/devices/esp32p4/soc.toml index c62b76df4fa..be7577984f2 100644 --- a/esp-metadata/devices/esp32p4/soc.toml +++ b/esp-metadata/devices/esp32p4/soc.toml @@ -55,7 +55,7 @@ peripherals = [ # Minimal set for esp-hal compilation { name = "EFUSE", hidden = true }, { name = "GPIO" }, - { name = "GPIO_SD" }, + { name = "GPIO_SD", clock_group = "SDM" }, { name = "SYSTEM", pac = "HP_SYS" }, { name = "HP_SYS" }, { name = "HP_SYS_CLKRST" }, diff --git a/esp-metadata/devices/esp32s2/clocks.toml b/esp-metadata/devices/esp32s2/clocks.toml index 76893887ff1..c70a25f825f 100644 --- a/esp-metadata/devices/esp32s2/clocks.toml +++ b/esp-metadata/devices/esp32s2/clocks.toml @@ -91,6 +91,9 @@ system_clocks = { clock_tree = [ { name = "BAUD_RATE_GENERATOR", type = "generic", output = "(FUNCTION_CLOCK * 16) / (integral * 16 + fractional)", params = { fractional = "0..16", integral = "0..0x100000" } }, { name = "MEM_CLOCK", type = "generic", output = "UART_MEM_CLK" }, ] }, + { group = "SDM", clocks = [ + { name = "FUNCTION_CLOCK", type = "generic", output = "APB_CLK" }, + ] }, { group = "RMT", clocks = [ { name = "SCLK", type = "mux", wake_locking = true, variants = [ { name = "REF_TICK", outputs = "REF_TICK" }, @@ -154,7 +157,7 @@ peripheral_clocks = { templates = [ { name = "Ledc" }, { name = "Pcnt" }, { name = "Rmt" }, - { name = "GpioSd", template_params = { clk_en_template = "crate::peripherals::GPIO_SD::regs().clock_gate().modify(|_, w| w.clk_en().bit(enable)); crate::peripherals::GPIO_SD::regs().sigmadelta_misc().modify(|_, w| w.function_clk_en().bit(enable));", rst_template = "let _ = reset;" } }, + { name = "GpioSd", template_params = { clk_en_template = "crate::peripherals::GPIO_SD::regs().clock_gate().modify(|_, w| w.clk_en().bit(enable));", rst_template = "let _ = reset;" } }, { name = "Uhci0" }, { name = "I2cExt0" }, { name = "Spi2" }, diff --git a/esp-metadata/devices/esp32s2/soc.toml b/esp-metadata/devices/esp32s2/soc.toml index ad60f856ce2..6b67e9b64f0 100644 --- a/esp-metadata/devices/esp32s2/soc.toml +++ b/esp-metadata/devices/esp32s2/soc.toml @@ -35,7 +35,7 @@ peripherals = [ { name = "FE" }, { name = "FE2" }, { name = "GPIO" }, - { name = "GPIO_SD" }, + { name = "GPIO_SD", clock_group = "SDM" }, { name = "HMAC" }, { name = "I2C_ANA_MST" }, { name = "I2C0", interrupts = { peri = "I2C_EXT0" }, stable = true, clock_group = "I2C" }, diff --git a/esp-metadata/devices/esp32s3/clocks.toml b/esp-metadata/devices/esp32s3/clocks.toml index 0737f51d350..b3e678183b4 100644 --- a/esp-metadata/devices/esp32s3/clocks.toml +++ b/esp-metadata/devices/esp32s3/clocks.toml @@ -86,6 +86,9 @@ system_clocks = { clock_tree = [ { name = "APB_CLK", outputs = "APB_CLK" }, ] }, ] }, + { group = "SDM", clocks = [ + { name = "FUNCTION_CLOCK", type = "generic", output = "APB_CLK" }, + ] }, { group = "RMT", clocks = [ { name = "SCLK", type = "mux", wake_locking = true, variants = [ { name = "APB_CLK", outputs = "APB_CLK", default = true }, @@ -157,7 +160,7 @@ peripheral_clocks = { templates = [ { name = "Ledc" }, { name = "Pcnt" }, { name = "Rmt" }, - { name = "GpioSd", template_params = { clk_en_template = "crate::peripherals::GPIO_SD::regs().clock_gate().modify(|_, w| w.clk_en().bit(enable)); crate::peripherals::GPIO_SD::regs().sigmadelta_misc().modify(|_, w| w.function_clk_en().bit(enable));", rst_template = "let _ = reset;" } }, + { name = "GpioSd", template_params = { clk_en_template = "crate::peripherals::GPIO_SD::regs().clock_gate().modify(|_, w| w.clk_en().bit(enable));", rst_template = "let _ = reset;" } }, { name = "Uhci0" }, { name = "I2cExt0" }, { name = "Spi2" }, diff --git a/esp-metadata/devices/esp32s3/soc.toml b/esp-metadata/devices/esp32s3/soc.toml index befc525fa9c..65e0c3c5d6b 100644 --- a/esp-metadata/devices/esp32s3/soc.toml +++ b/esp-metadata/devices/esp32s3/soc.toml @@ -38,7 +38,7 @@ peripherals = [ { name = "EXTMEM" }, { name = "MMU_TABLE", hidden = true }, { name = "GPIO" }, - { name = "GPIO_SD" }, + { name = "GPIO_SD", clock_group = "SDM" }, { name = "HMAC" }, { name = "I2C_ANA_MST" }, { name = "I2C0", interrupts = { peri = "I2C_EXT0" }, stable = true, clock_group = "I2C" }, From b8bb451f959bc6be054f9a2ee6d62b31bf47918b Mon Sep 17 00:00:00 2001 From: tzyt Date: Fri, 31 Jul 2026 15:57:49 +0800 Subject: [PATCH 18/26] use capability-based chip filter for HIL --- hil-test/src/bin/sdm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hil-test/src/bin/sdm.rs b/hil-test/src/bin/sdm.rs index ae069cb3fa4..aaf02600168 100644 --- a/hil-test/src/bin/sdm.rs +++ b/hil-test/src/bin/sdm.rs @@ -4,7 +4,7 @@ //! - SDM output => first common test pin //! - RMT RX => second common test pin -//% CHIPS: esp32 esp32c3 esp32c5 esp32c6 esp32h2 esp32s2 esp32s3 +//% CHIP_FILTER: sdm_driver_supported && rmt_driver_supported //% FEATURES: unstable #![no_std] From 76535e79e7513454a3ff40d82dc3cad187c51b2e Mon Sep 17 00:00:00 2001 From: tzyt Date: Fri, 31 Jul 2026 16:05:18 +0800 Subject: [PATCH 19/26] completely remove remaining file in example --- examples/peripheral/sdm/.cargo/config.toml | 24 ---------------------- 1 file changed, 24 deletions(-) delete mode 100644 examples/peripheral/sdm/.cargo/config.toml diff --git a/examples/peripheral/sdm/.cargo/config.toml b/examples/peripheral/sdm/.cargo/config.toml deleted file mode 100644 index 44dacc4278b..00000000000 --- a/examples/peripheral/sdm/.cargo/config.toml +++ /dev/null @@ -1,24 +0,0 @@ -[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"] From 923019bd815cb20be604dae5d9816ea8e7009c65 Mon Sep 17 00:00:00 2001 From: tzyt Date: Fri, 31 Jul 2026 16:48:39 +0800 Subject: [PATCH 20/26] fix lifetime issue of sdm ChannelCreator --- esp-hal/src/sdm.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/esp-hal/src/sdm.rs b/esp-hal/src/sdm.rs index 7382e9fea93..1e9d29b6de6 100644 --- a/esp-hal/src/sdm.rs +++ b/esp-hal/src/sdm.rs @@ -61,7 +61,7 @@ for_each_sdm_channel!( _instance: GPIO_SD<'d>, $( #[doc = concat!("Channel ", stringify!($ch), " creator.")] - pub []: ChannelCreator, + pub []: ChannelCreator<'d>, )* } @@ -271,13 +271,19 @@ impl ChannelConfigBuilder { /// mutably borrows the creator for the lifetime of the active [`Channel`], /// which prevents accidentally connecting the same channel twice. #[derive(Debug)] -pub struct ChannelCreator { +pub struct ChannelCreator<'d> { channel: usize, + // Conceptually retains the main driver even when this creator is moved out + // of it, preventing SDM from being reinitialized while the creator exists. + _sdm: PhantomData>, } -impl ChannelCreator { +impl ChannelCreator<'_> { const fn new(channel: usize) -> Self { - Self { channel } + Self { + channel, + _sdm: PhantomData, + } } /// Configures this channel and connects it to an output pin. @@ -305,7 +311,7 @@ impl ChannelCreator { #[derive(Debug)] pub struct Channel<'a> { channel: usize, - _creator: PhantomData<&'a mut ChannelCreator>, + _creator: PhantomData<&'a mut ChannelCreator<'a>>, _pin_guard: PinGuard, _clock_guard: SdmClockGuard, } From 6874f8d11e67bc9ffbd486d99a8587cf99f4b3e8 Mon Sep 17 00:00:00 2001 From: tzyt Date: Fri, 31 Jul 2026 17:11:40 +0800 Subject: [PATCH 21/26] Regenerate metadata after merge --- esp-metadata-generated/src/_generated_esp32c5.rs | 5 +++++ esp-metadata-generated/src/_generated_esp32c6.rs | 5 +++++ esp-metadata-generated/src/_generated_esp32h2.rs | 4 ++++ esp-metadata-generated/src/_generated_esp32p4.rs | 4 ++++ 4 files changed, 18 insertions(+) diff --git a/esp-metadata-generated/src/_generated_esp32c5.rs b/esp-metadata-generated/src/_generated_esp32c5.rs index ba6eb41b99a..c9ed4ca7b2c 100644 --- a/esp-metadata-generated/src/_generated_esp32c5.rs +++ b/esp-metadata-generated/src/_generated_esp32c5.rs @@ -457,6 +457,11 @@ macro_rules! property { ::soc::clocks::CryptoClkConfig::Fosc, crate ::soc::clocks::CryptoClkConfig::PllF480m] }; + ("clock_tree.iomux_function_clock") => { + [crate ::soc::clocks::IomuxFunctionClockConfig::XtalClk, crate + ::soc::clocks::IomuxFunctionClockConfig::RcFastClk, crate + ::soc::clocks::IomuxFunctionClockConfig::PllF80m] + }; ("clock_tree.timg_calibration_clock") => { [crate ::soc::clocks::TimgCalibrationClockConfig::OscSlowClk, crate ::soc::clocks::TimgCalibrationClockConfig::RcSlowClk, crate diff --git a/esp-metadata-generated/src/_generated_esp32c6.rs b/esp-metadata-generated/src/_generated_esp32c6.rs index c311198a0ba..651f7a18e91 100644 --- a/esp-metadata-generated/src/_generated_esp32c6.rs +++ b/esp-metadata-generated/src/_generated_esp32c6.rs @@ -475,6 +475,11 @@ macro_rules! property { [crate ::soc::clocks::MspiFastClkConfig::Hs, crate ::soc::clocks::MspiFastClkConfig::Ls] }; + ("clock_tree.iomux_function_clock") => { + [crate ::soc::clocks::IomuxFunctionClockConfig::PllF80m, crate + ::soc::clocks::IomuxFunctionClockConfig::RcFastClk, crate + ::soc::clocks::IomuxFunctionClockConfig::XtalClk] + }; ("clock_tree.ledc_sclk") => { [crate ::soc::clocks::LedcSclkConfig::PllF80m, crate ::soc::clocks::LedcSclkConfig::RcFastClk, crate diff --git a/esp-metadata-generated/src/_generated_esp32h2.rs b/esp-metadata-generated/src/_generated_esp32h2.rs index 6a7d1a26557..41543c619b2 100644 --- a/esp-metadata-generated/src/_generated_esp32h2.rs +++ b/esp-metadata-generated/src/_generated_esp32h2.rs @@ -424,6 +424,10 @@ macro_rules! property { ("soc.cpu_mcause_mask", str) => { stringify!(31) }; + ("clock_tree.iomux_function_clock") => { + [crate ::soc::clocks::IomuxFunctionClockConfig::XtalClk, crate + ::soc::clocks::IomuxFunctionClockConfig::PllF48m] + }; ("clock_tree.hp_root_clk") => { [crate ::soc::clocks::HpRootClkConfig::Pll96, crate ::soc::clocks::HpRootClkConfig::Pll64, crate diff --git a/esp-metadata-generated/src/_generated_esp32p4.rs b/esp-metadata-generated/src/_generated_esp32p4.rs index f1958b700b8..1bb3a733ae9 100644 --- a/esp-metadata-generated/src/_generated_esp32p4.rs +++ b/esp-metadata-generated/src/_generated_esp32p4.rs @@ -370,6 +370,10 @@ macro_rules! property { ("soc.cpu_mcause_mask", str) => { stringify!(63) }; + ("clock_tree.iomux_function_clock") => { + [crate ::soc::clocks::IomuxFunctionClockConfig::XtalClk, crate + ::soc::clocks::IomuxFunctionClockConfig::PllF80m] + }; ("clock_tree.cpu_root_clk") => { [crate ::soc::clocks::CpuRootClkConfig::Xtal, crate ::soc::clocks::CpuRootClkConfig::Cpll, crate From 91f66e28e37fccb2f7a3b0b18c997dcc6d6efa66 Mon Sep 17 00:00:00 2001 From: tzyt Date: Sat, 1 Aug 2026 12:33:01 +0800 Subject: [PATCH 22/26] correct with_frequency so that it select prescaler by closest output frequency --- esp-hal/src/sdm.rs | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/esp-hal/src/sdm.rs b/esp-hal/src/sdm.rs index 1e9d29b6de6..07fe80a6964 100644 --- a/esp-hal/src/sdm.rs +++ b/esp-hal/src/sdm.rs @@ -246,6 +246,9 @@ impl ChannelConfigBuilder { } /// Sets the requested output frequency. + /// + /// Selects the prescaler that produces the closest output frequency using + /// the currently configured SDM clock source. pub fn with_frequency(mut self, frequency: Rate) -> Result { self.config.raw_prescaler = raw_prescaler(prescaler_from_frequency(frequency)?); Ok(self.config) @@ -393,20 +396,34 @@ impl Drop for SdmFunctionClockGuard { } fn prescaler_from_frequency(frequency: Rate) -> Result { - let source_frequency = SdmInstance::GpioSd.function_clock_frequency(); - let requested_frequency = frequency.as_hz(); + let source_frequency = SdmInstance::GpioSd.function_clock_frequency() as u64; + let requested_frequency = frequency.as_hz() as u64; - if requested_frequency == 0 || requested_frequency > source_frequency { + if requested_frequency == 0 + || requested_frequency > source_frequency + || requested_frequency * 256 < source_frequency + { return Err(Error::UnreachableTargetFrequency); } - let prescaler = - (source_frequency as u64 + requested_frequency as u64 / 2) / requested_frequency as u64; - if prescaler == 0 || prescaler > 256 { - return Err(Error::UnreachableTargetFrequency); + // The closest output must be produced by one of the two integers around + // the ideal prescaler. + let lower = source_frequency / requested_frequency; + let upper = lower + 1; + if upper > 256 { + return Ok(lower as u16); } - Ok(prescaler as u16) + // Compare |source / prescaler - requested| without truncating either + // resulting frequency. + let lower_error = (source_frequency - requested_frequency * lower) * upper; + let upper_error = (requested_frequency * upper - source_frequency) * lower; + + Ok(if lower_error <= upper_error { + lower as u16 + } else { + upper as u16 + }) } fn check_prescaler(prescaler: u16) -> Result<(), Error> { From 98a98b5ba1b414b1acde43e5bad157b636d384d8 Mon Sep 17 00:00:00 2001 From: tzyt Date: Sat, 1 Aug 2026 15:10:11 +0800 Subject: [PATCH 23/26] remove configuration of iomux function clock through sdm and add documentation --- esp-hal/src/sdm.rs | 137 +++++++++++++++++----------------------- hil-test/src/bin/sdm.rs | 4 +- 2 files changed, 60 insertions(+), 81 deletions(-) diff --git a/esp-hal/src/sdm.rs b/esp-hal/src/sdm.rs index 07fe80a6964..8b961e6ecc2 100644 --- a/esp-hal/src/sdm.rs +++ b/esp-hal/src/sdm.rs @@ -12,14 +12,12 @@ //! //! ```rust, no_run //! # {before_snippet} -//! use esp_hal::{ -//! sdm::{Sdm, SdmConfig}, -//! time::Rate, -//! }; +//! use esp_hal::{sdm::Sdm, time::Rate}; //! -//! let mut sdm = Sdm::new(peripherals.GPIO_SD, SdmConfig::default()); +//! let mut sdm = Sdm::new(peripherals.GPIO_SD); //! let config = sdm //! .channel_config() +//! // Select the prescaler that produces the closest available frequency. //! .with_frequency(Rate::from_khz(500))? //! .with_duty(128); //! let mut channel = sdm.channel0.connect(peripherals.GPIO2, config); @@ -29,11 +27,60 @@ //! //! # {after_snippet} //! ``` +//! +//! ## Clock source +#![cfg_attr( + not(soc_has_clock_node_iomux_function_clock), + doc = r#" +The SDM function clock is derived from the APB clock. +"# +)] +#![cfg_attr( + soc_has_clock_node_iomux_function_clock, + doc = r#" +The SDM function clock is derived from the global `IOMUX_FUNCTION_CLOCK`. Its +source is shared by every consumer of that clock and is therefore configured +globally instead of through [`Sdm`]. The available sources and the default for +the selected target are listed by +[`IomuxFunctionClockConfig`](crate::clock::ll::IomuxFunctionClockConfig). + +The source can be selected as part of the global clock configuration before initializing the HAL: + +```rust, no_run +use esp_hal::{ + Config, + clock::{ClockConfig, ll::IomuxFunctionClockConfig}, +}; + +let clock_config = ClockConfig { + iomux_function_clock: Some(IomuxFunctionClockConfig::XtalClk), + ..ClockConfig::default() +}; +let peripherals = esp_hal::init(Config::default().with_cpu_clock(clock_config)); +``` + +The low-level clock-tree API can also change the source at runtime: + +```rust, no_run +use esp_hal::clock::ll::{ + ClockTree, + IomuxFunctionClockConfig, + configure_iomux_function_clock, +}; + +ClockTree::with(|clocks| { + configure_iomux_function_clock(clocks, IomuxFunctionClockConfig::XtalClk); +}); +``` + +Changing this global source affects every active consumer of `IOMUX_FUNCTION_CLOCK`. Existing peripheral dividers are not automatically recalculated. +"# +)] +//! Each channel's prescaler divides this function clock to produce its output +//! frequency. use core::{fmt, marker::PhantomData}; -#[cfg(soc_has_clock_node_iomux_function_clock)] -use crate::soc::clocks::{self, IomuxFunctionClockConfig}; use crate::{ gpio::{ OutputConfig, @@ -67,20 +114,7 @@ for_each_sdm_channel!( impl<'d> Sdm<'d> { /// Creates a new sigma-delta peripheral driver. - /// - /// The SDM clock source is shared by all SDM channels through the IO_MUX - /// clock, so it is selected here instead of being configurable per channel. - pub fn new(instance: GPIO_SD<'d>, config: SdmConfig) -> Self { - #[cfg(soc_has_clock_node_iomux_function_clock)] - ClockTree::with(|clocks| { - clocks::configure_iomux_function_clock( - clocks, - config.clock_source.into(), - ); - }); - #[cfg(not(soc_has_clock_node_iomux_function_clock))] - let _ = config; - + pub fn new(instance: GPIO_SD<'d>) -> Self { Self { _instance: instance, $( @@ -91,10 +125,9 @@ for_each_sdm_channel!( /// Creates a channel configuration builder. pub const fn channel_config(&self) -> ChannelConfigBuilder { - // this ensures that channels can be configured only after that SDM - // have been initialized with a selected clock source. Since - // `with_frequency` requires a clock source to calculate the prescaler, - // it is important that the SDM is initialized first. + // This ensures that channel configs can only be created after SDM has + // been initialized. `with_frequency` relies on the function clock that + // was configured during system initialization. ChannelConfigBuilder::new() } } @@ -115,60 +148,6 @@ for_each_sdm_channel!( }; ); -/// Sigma-delta peripheral configuration. -#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, procmacros::BuilderLite)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[non_exhaustive] -pub struct SdmConfig { - /// Clock source used by the shared SDM/IO_MUX clock. - clock_source: ClockSource, -} - -/// Source clock for the shared SDM/IO_MUX clock. -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[non_exhaustive] -pub enum ClockSource { - /// APB clock. - #[cfg(any(esp32, esp32c3, esp32s2, esp32s3))] - #[default] - Apb, - /// XTAL clock. - #[cfg(any(esp32c5, esp32c6, esp32h2, esp32p4))] - Xtal, - /// Fixed 80 MHz PLL clock. - #[cfg(any(esp32c5, esp32c6, esp32p4))] - #[cfg_attr(any(esp32c5, esp32c6, esp32p4), default)] - PllF80m, - /// Fixed 48 MHz PLL clock. - #[cfg(esp32h2)] - #[cfg_attr(esp32h2, default)] - PllF48m, -} - -#[cfg(any(esp32c5, esp32c6, esp32p4))] -// C5/C6 hardware also routes RC_FAST (called FOSC in some register descriptions) through the -// IO MUX. ESP-IDF does not expose it as an SDM clock source, so neither do we. It should be -// usable in principle, but has not been included in the supported SDM API. -impl From for IomuxFunctionClockConfig { - fn from(source: ClockSource) -> Self { - match source { - ClockSource::Xtal => Self::XtalClk, - ClockSource::PllF80m => Self::PllF80m, - } - } -} - -#[cfg(esp32h2)] -impl From for IomuxFunctionClockConfig { - fn from(source: ClockSource) -> Self { - match source { - ClockSource::Xtal => Self::XtalClk, - ClockSource::PllF48m => Self::PllF48m, - } - } -} - /// Sigma-delta configuration or runtime error. #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] diff --git a/hil-test/src/bin/sdm.rs b/hil-test/src/bin/sdm.rs index aaf02600168..4239134eeaf 100644 --- a/hil-test/src/bin/sdm.rs +++ b/hil-test/src/bin/sdm.rs @@ -16,7 +16,7 @@ use esp_hal::{ gpio::{AnyPin, Level, Output, OutputConfig, interconnect::PeripheralInput}, peripherals::{GPIO_SD, RMT}, rmt::{CHANNEL_RAM_SIZE, Channel, PulseCode, Rmt, Rx, RxChannelConfig, RxChannelCreator}, - sdm::{Sdm, SdmConfig}, + sdm::Sdm, time::Rate, }; use hil_test as _; @@ -219,7 +219,7 @@ fn expected_ratio_per_mille(duty: u8) -> u32 { } fn measure_high_ratio(ctx: &mut Context, duty: u8) -> Measurement { - let mut sdm = Sdm::new(ctx.gpio_sd.reborrow(), SdmConfig::default()); + let mut sdm = Sdm::new(ctx.gpio_sd.reborrow()); let config = sdm .channel_config() .with_frequency(SDM_FREQUENCY) From 7934cd57a93795de9be6fce5b009edde711b56a2 Mon Sep 17 00:00:00 2001 From: tzyt Date: Sat, 1 Aug 2026 15:35:04 +0800 Subject: [PATCH 24/26] capitalize error message --- esp-hal/src/sdm.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esp-hal/src/sdm.rs b/esp-hal/src/sdm.rs index 8b961e6ecc2..4e27c94f798 100644 --- a/esp-hal/src/sdm.rs +++ b/esp-hal/src/sdm.rs @@ -162,8 +162,8 @@ pub enum Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::UnreachableTargetFrequency => f.write_str("unreachable target frequency"), - Self::PrescalerOutOfRange => f.write_str("prescaler out of range"), + Self::UnreachableTargetFrequency => f.write_str("Unreachable target frequency"), + Self::PrescalerOutOfRange => f.write_str("Prescaler out of range"), } } } From 26dcc843d371c02f85abef691816310c2cd6a911 Mon Sep 17 00:00:00 2001 From: tzyt Date: Sat, 1 Aug 2026 15:50:28 +0800 Subject: [PATCH 25/26] add before and after snippet for documentation --- esp-hal/src/sdm.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/esp-hal/src/sdm.rs b/esp-hal/src/sdm.rs index 4e27c94f798..04642ffab77 100644 --- a/esp-hal/src/sdm.rs +++ b/esp-hal/src/sdm.rs @@ -47,6 +47,7 @@ the selected target are listed by The source can be selected as part of the global clock configuration before initializing the HAL: ```rust, no_run +# {before_snippet} use esp_hal::{ Config, clock::{ClockConfig, ll::IomuxFunctionClockConfig}, @@ -57,11 +58,13 @@ let clock_config = ClockConfig { ..ClockConfig::default() }; let peripherals = esp_hal::init(Config::default().with_cpu_clock(clock_config)); +# {after_snippet} ``` The low-level clock-tree API can also change the source at runtime: ```rust, no_run +# {before_snippet} use esp_hal::clock::ll::{ ClockTree, IomuxFunctionClockConfig, @@ -71,6 +74,7 @@ use esp_hal::clock::ll::{ ClockTree::with(|clocks| { configure_iomux_function_clock(clocks, IomuxFunctionClockConfig::XtalClk); }); +# {after_snippet} ``` Changing this global source affects every active consumer of `IOMUX_FUNCTION_CLOCK`. Existing peripheral dividers are not automatically recalculated. From 44be99a4cb30d7e84252799046cd080cc293da29 Mon Sep 17 00:00:00 2001 From: tzyt Date: Thu, 6 Aug 2026 23:22:25 +0800 Subject: [PATCH 26/26] Simplify RMT clock setup in SDM HIL test --- hil-test/src/bin/sdm.rs | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/hil-test/src/bin/sdm.rs b/hil-test/src/bin/sdm.rs index 4239134eeaf..96737d7824c 100644 --- a/hil-test/src/bin/sdm.rs +++ b/hil-test/src/bin/sdm.rs @@ -21,19 +21,9 @@ use esp_hal::{ }; use hil_test as _; -cfg_select! { - esp32h2 => { - const RMT_FREQUENCY: Rate = Rate::from_mhz(32); - // default clock source for h2 is 32Mhz - const RMT_DIVIDER: u8 = 32; - } - _ => { - const RMT_FREQUENCY: Rate = Rate::from_mhz(80); - // default clock source for other models is 80Mhz - const RMT_DIVIDER: u8 = 80; - } - // make sure that all models uses 1 Mhz clock -} +// Configure a 1 MHz RMT clock on every supported chip. +const RMT_DIVIDER: u8 = if cfg!(esp32h2) { 32 } else { 80 }; +const RMT_FREQUENCY: Rate = Rate::from_mhz(RMT_DIVIDER as u32); const SDM_FREQUENCY: Rate = Rate::from_khz(500); const SDM_WARM_UP_US: u32 = 1_000;