From d76cdf6ac2ab7e3cf70455f29f559d63b4c82835 Mon Sep 17 00:00:00 2001 From: Kirill Mikhailov Date: Tue, 26 May 2026 18:01:39 +0200 Subject: [PATCH 1/7] I2C: add clock configuration --- esp-hal/src/i2c/master/low_level/mod.rs | 8 +++++--- esp-hal/src/i2c/master/low_level/v1.rs | 1 + esp-hal/src/i2c/master/low_level/v2.rs | 1 + esp-hal/src/i2c/master/low_level/v3.rs | 8 ++++++-- esp-hal/src/i2c/master/mod.rs | 27 +++++++++++++++++++++++++ 5 files changed, 40 insertions(+), 5 deletions(-) diff --git a/esp-hal/src/i2c/master/low_level/mod.rs b/esp-hal/src/i2c/master/low_level/mod.rs index 8b8ee38d787..7f5ff54592d 100644 --- a/esp-hal/src/i2c/master/low_level/mod.rs +++ b/esp-hal/src/i2c/master/low_level/mod.rs @@ -175,6 +175,7 @@ fn set_filter( /// Configures the clock and timing parameters for the I2C peripheral. fn configure_clock( info: &Info, + clock_source: ClockSource, sclk_div: u32, scl_low_period: u32, scl_high_period: u32, @@ -189,21 +190,22 @@ fn configure_clock( ) -> Result<(), ConfigError> { unsafe { cfg_if::cfg_if! { + // sclk_sel: 0 = XTAL, 1 = RcFast if #[cfg(all(soc_has_pcr, soc_has_i2c1))] { crate::peripherals::PCR::regs().i2c_sclk_conf(info.id as usize).modify(|_, w| { - w.i2c_sclk_sel().clear_bit(); + w.i2c_sclk_sel().bit(matches!(clock_source, ClockSource::RcFast)); w.i2c_sclk_div_num().bits((sclk_div - 1) as u8); w.i2c_sclk_en().set_bit() }); } else if #[cfg(soc_has_pcr)] { crate::peripherals::PCR::regs().i2c_sclk_conf().modify(|_, w| { - w.i2c_sclk_sel().clear_bit(); + w.i2c_sclk_sel().bit(matches!(clock_source, ClockSource::RcFast)); w.i2c_sclk_div_num().bits((sclk_div - 1) as u8); w.i2c_sclk_en().set_bit() }); } else if #[cfg(i2c_master_version = "3")] { info.regs().clk_conf().modify(|_, w| { - w.sclk_sel().clear_bit(); + w.sclk_sel().bit(matches!(clock_source, ClockSource::RcFast)); w.sclk_div_num().bits((sclk_div - 1) as u8) }); } diff --git a/esp-hal/src/i2c/master/low_level/v1.rs b/esp-hal/src/i2c/master/low_level/v1.rs index b6b4504f8fd..9f62fb62da9 100644 --- a/esp-hal/src/i2c/master/low_level/v1.rs +++ b/esp-hal/src/i2c/master/low_level/v1.rs @@ -59,6 +59,7 @@ pub(super) fn set_frequency(driver: &Driver<'_>, clock_config: &Config) -> Resul configure_clock( driver.info, + clock_config.clock_source, 0, scl_low_period, scl_high_period, diff --git a/esp-hal/src/i2c/master/low_level/v2.rs b/esp-hal/src/i2c/master/low_level/v2.rs index 4bd628c4cd8..d7ba25a7929 100644 --- a/esp-hal/src/i2c/master/low_level/v2.rs +++ b/esp-hal/src/i2c/master/low_level/v2.rs @@ -39,6 +39,7 @@ pub(super) fn set_frequency(driver: &Driver<'_>, clock_config: &Config) -> Resul configure_clock( driver.info, + clock_config.clock_source, 0, scl_low_period, scl_high_period, diff --git a/esp-hal/src/i2c/master/low_level/v3.rs b/esp-hal/src/i2c/master/low_level/v3.rs index 54829265b7f..77725b6fed1 100644 --- a/esp-hal/src/i2c/master/low_level/v3.rs +++ b/esp-hal/src/i2c/master/low_level/v3.rs @@ -1,4 +1,4 @@ -use super::{Config, ConfigError, Driver, RegisterBlock, configure_clock}; +use super::{ClockSource, Config, ConfigError, Driver, RegisterBlock, configure_clock}; /// Sets the frequency of the I2C interface by calculating and applying the /// associated timings - corresponds to i2c_ll_cal_bus_clk and @@ -6,7 +6,10 @@ use super::{Config, ConfigError, Driver, RegisterBlock, configure_clock}; pub(super) fn set_frequency(driver: &Driver<'_>, clock_config: &Config) -> Result<(), ConfigError> { let timeout = clock_config.timeout; - let source_clk = crate::soc::clocks::xtal_clk_frequency(); + let source_clk = match clock_config.clock_source { + ClockSource::Xtal => crate::soc::clocks::xtal_clk_frequency(), + ClockSource::RcFast => crate::soc::clocks::rc_fast_clk_frequency(), + }; let bus_freq = clock_config.frequency.as_hz(); @@ -52,6 +55,7 @@ pub(super) fn set_frequency(driver: &Driver<'_>, clock_config: &Config) -> Resul configure_clock( driver.info, + clock_config.clock_source, clkm_div, scl_low_period, scl_high_period, diff --git a/esp-hal/src/i2c/master/mod.rs b/esp-hal/src/i2c/master/mod.rs index 8009ba37ded..3311dc6ca06 100644 --- a/esp-hal/src/i2c/master/mod.rs +++ b/esp-hal/src/i2c/master/mod.rs @@ -563,6 +563,25 @@ enum Ack { Nack = 1, } +/// Clock source for the I2C peripheral. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[instability::unstable] +#[non_exhaustive] +pub enum ClockSource { + /// APB clock. + #[cfg(not(i2c_master_version = "3"))] + #[default] + Apb, + /// Crystal oscillator (XTAL). + #[cfg(i2c_master_version = "3")] + #[default] + Xtal, + /// RC fast oscillator. + #[cfg(i2c_master_version = "3")] + RcFast, +} + /// I2C driver configuration #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, procmacros::BuilderLite)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -599,6 +618,12 @@ pub struct Config { #[cfg(i2c_master_has_fsm_timeouts)] #[builder_lite(unstable)] scl_main_st_timeout: FsmTimeout, + + /// The clock source for the I2C peripheral. + /// + /// Default value: [`ClockSource::default()`]. + #[builder_lite(unstable)] + clock_source: ClockSource, } impl Default for Config { @@ -617,6 +642,8 @@ impl Default for Config { scl_st_timeout: Default::default(), #[cfg(i2c_master_has_fsm_timeouts)] scl_main_st_timeout: Default::default(), + + clock_source: Default::default(), } } } From b51c5bca5886ecae15779dbd21033453df680d5f Mon Sep 17 00:00:00 2001 From: Kirill Mikhailov Date: Tue, 26 May 2026 19:44:27 +0200 Subject: [PATCH 2/7] Add test, add RefTick to V2 I2C --- esp-hal/src/i2c/master/low_level/mod.rs | 5 +++++ esp-hal/src/i2c/master/low_level/v2.rs | 8 +++++--- esp-hal/src/i2c/master/mod.rs | 3 +++ hil-test/src/bin/i2c.rs | 27 +++++++++++++++++++++++++ 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/esp-hal/src/i2c/master/low_level/mod.rs b/esp-hal/src/i2c/master/low_level/mod.rs index 7f5ff54592d..69c9a0bfe51 100644 --- a/esp-hal/src/i2c/master/low_level/mod.rs +++ b/esp-hal/src/i2c/master/low_level/mod.rs @@ -208,6 +208,11 @@ fn configure_clock( w.sclk_sel().bit(matches!(clock_source, ClockSource::RcFast)); w.sclk_div_num().bits((sclk_div - 1) as u8) }); + } else if #[cfg(i2c_master_version = "2")] { + // ref_always_on: 1 = APB, 0 = REF_TICK + info.regs().ctr().modify(|_, w| { + w.ref_always_on().bit(!matches!(clock_source, ClockSource::RefTick)) + }); } } diff --git a/esp-hal/src/i2c/master/low_level/v2.rs b/esp-hal/src/i2c/master/low_level/v2.rs index d7ba25a7929..3863da754c2 100644 --- a/esp-hal/src/i2c/master/low_level/v2.rs +++ b/esp-hal/src/i2c/master/low_level/v2.rs @@ -1,4 +1,4 @@ -use super::{Config, ConfigError, Driver, RegisterBlock, configure_clock}; +use super::{ClockSource, Config, ConfigError, Driver, RegisterBlock, configure_clock}; /// Sets the frequency of the I2C interface by calculating and applying the /// associated timings - corresponds to i2c_ll_cal_bus_clk and @@ -6,8 +6,10 @@ use super::{Config, ConfigError, Driver, RegisterBlock, configure_clock}; pub(super) fn set_frequency(driver: &Driver<'_>, clock_config: &Config) -> Result<(), ConfigError> { let timeout = clock_config.timeout; - // TODO: could be REF_TICK - let source_clk = crate::soc::clocks::apb_clk_frequency(); + let source_clk = match clock_config.clock_source { + ClockSource::Apb => crate::soc::clocks::apb_clk_frequency(), + ClockSource::RefTick => 1_000_000, + }; let bus_freq = clock_config.frequency.as_hz(); diff --git a/esp-hal/src/i2c/master/mod.rs b/esp-hal/src/i2c/master/mod.rs index 3311dc6ca06..6aaef08fb5c 100644 --- a/esp-hal/src/i2c/master/mod.rs +++ b/esp-hal/src/i2c/master/mod.rs @@ -573,6 +573,9 @@ pub enum ClockSource { #[cfg(not(i2c_master_version = "3"))] #[default] Apb, + /// REF_TICK (1 MHz reference clock). + #[cfg(i2c_master_version = "2")] + RefTick, /// Crystal oscillator (XTAL). #[cfg(i2c_master_version = "3")] #[default] diff --git a/hil-test/src/bin/i2c.rs b/hil-test/src/bin/i2c.rs index 4e57f1e0103..affc832cb04 100644 --- a/hil-test/src/bin/i2c.rs +++ b/hil-test/src/bin/i2c.rs @@ -375,6 +375,33 @@ mod tests { ticker.next().await; } } + #[test] + // Only APB is supported on v1, so skip the test for that version. + #[cfg(not(i2c_master_version = "1"))] + fn test_read_cali_with_different_clock_sources(mut ctx: Context) { + use esp_hal::i2c::master::ClockSource; + + cfg_if::cfg_if! { + if #[cfg(i2c_master_version = "3")] { + let configs = [ClockSource::Xtal, ClockSource::RcFast]; + } else { + let configs = [ClockSource::Apb, ClockSource::RefTick]; + } + } + + for clock_source in configs { + ctx.i2c + .apply_config(&Config::default().with_clock_source(clock_source)) + .unwrap_or_else(|e| panic!("{:?}: failed to apply {:?}", e, clock_source)); + + let mut read_data = [0u8; 22]; + ctx.i2c + .write_read(DUT_ADDRESS, READ_DATA_COMMAND, &mut read_data) + .unwrap_or_else(|e| panic!("{:?}: failed to read with {:?}", e, clock_source)); + + assert_ne!(read_data, [0u8; 22]); + } + } #[test] #[cfg(esp32s3)] From 65f91c79a12854dcb13aad9e9da05187f399e2a5 Mon Sep 17 00:00:00 2001 From: Kirill Mikhailov Date: Wed, 27 May 2026 17:00:57 +0200 Subject: [PATCH 3/7] Use clock tree --- esp-hal/src/i2c/master/mod.rs | 20 +-- esp-hal/src/soc/esp32/clocks.rs | 17 ++ esp-hal/src/soc/esp32c2/clocks.rs | 28 ++- esp-hal/src/soc/esp32c3/clocks.rs | 28 ++- esp-hal/src/soc/esp32c5/clocks.rs | 26 +++ esp-hal/src/soc/esp32c6/clocks.rs | 26 +++ esp-hal/src/soc/esp32c61/clocks.rs | 26 +++ esp-hal/src/soc/esp32h2/clocks.rs | 32 ++++ esp-hal/src/soc/esp32p4/clocks.rs | 41 +++++ esp-hal/src/soc/esp32s2/clocks.rs | 28 ++- esp-hal/src/soc/esp32s3/clocks.rs | 33 +++- .../src/_build_script_utils.rs | 21 +++ .../src/_generated_esp32.rs | 118 +++++++++++++ .../src/_generated_esp32c2.rs | 154 +++++++++++++++++ .../src/_generated_esp32c3.rs | 154 +++++++++++++++++ .../src/_generated_esp32c5.rs | 154 +++++++++++++++++ .../src/_generated_esp32c6.rs | 154 +++++++++++++++++ .../src/_generated_esp32c61.rs | 154 +++++++++++++++++ .../src/_generated_esp32h2.rs | 159 ++++++++++++++++++ .../src/_generated_esp32p4.rs | 156 +++++++++++++++++ .../src/_generated_esp32s2.rs | 146 ++++++++++++++++ .../src/_generated_esp32s3.rs | 159 ++++++++++++++++++ esp-metadata/devices/esp32.toml | 9 +- esp-metadata/devices/esp32c2.toml | 8 +- esp-metadata/devices/esp32c3.toml | 8 +- esp-metadata/devices/esp32c5.toml | 8 +- esp-metadata/devices/esp32c6.toml | 8 +- esp-metadata/devices/esp32c61.toml | 8 +- esp-metadata/devices/esp32h2.toml | 10 +- esp-metadata/devices/esp32p4.toml | 10 +- esp-metadata/devices/esp32s2.toml | 10 +- esp-metadata/devices/esp32s3.toml | 10 +- 32 files changed, 1885 insertions(+), 38 deletions(-) diff --git a/esp-hal/src/i2c/master/mod.rs b/esp-hal/src/i2c/master/mod.rs index 6aaef08fb5c..c7f7b6b03af 100644 --- a/esp-hal/src/i2c/master/mod.rs +++ b/esp-hal/src/i2c/master/mod.rs @@ -564,26 +564,8 @@ enum Ack { } /// Clock source for the I2C peripheral. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[instability::unstable] -#[non_exhaustive] -pub enum ClockSource { - /// APB clock. - #[cfg(not(i2c_master_version = "3"))] - #[default] - Apb, - /// REF_TICK (1 MHz reference clock). - #[cfg(i2c_master_version = "2")] - RefTick, - /// Crystal oscillator (XTAL). - #[cfg(i2c_master_version = "3")] - #[default] - Xtal, - /// RC fast oscillator. - #[cfg(i2c_master_version = "3")] - RcFast, -} +pub use crate::soc::clocks::I2cFunctionClockSclk as ClockSource; /// I2C driver configuration #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, procmacros::BuilderLite)] diff --git a/esp-hal/src/soc/esp32/clocks.rs b/esp-hal/src/soc/esp32/clocks.rs index 7befd18d0ff..a1407add13e 100644 --- a/esp-hal/src/soc/esp32/clocks.rs +++ b/esp-hal/src/soc/esp32/clocks.rs @@ -833,3 +833,20 @@ impl UartInstance { }); } } + +impl I2cInstance { + // I2C_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: I2cFunctionClockConfig, + ) { + // ESP32 I2C is hardwired to APB; no clock source selection register. + } +} diff --git a/esp-hal/src/soc/esp32c2/clocks.rs b/esp-hal/src/soc/esp32c2/clocks.rs index e134d0e771e..52eba2f5393 100644 --- a/esp-hal/src/soc/esp32c2/clocks.rs +++ b/esp-hal/src/soc/esp32c2/clocks.rs @@ -19,7 +19,7 @@ use esp_rom_sys::rom::{ets_delay_us, ets_update_cpu_frequency_rom}; use crate::{ clock::RtcClock, - peripherals::{I2C_ANA_MST, LPWR, SYSTEM, TIMG0, UART0, UART1}, + peripherals::{I2C_ANA_MST, I2C0, LPWR, SYSTEM, TIMG0, UART0, UART1}, rtc_cntl::Rtc, soc::regi2c, time::Rate, @@ -738,3 +738,29 @@ impl UartInstance { }); } } + +impl I2cInstance { + // I2C_FUNCTION_CLOCK + + fn enable_function_clock_impl(self, _clocks: &mut ClockTree, en: bool) { + let _ = self; + I2C0::regs() + .clk_conf() + .modify(|_, w| w.sclk_active().bit(en)); + } + + fn configure_function_clock_impl( + self, + _clocks: &mut ClockTree, + _old_config: Option, + new_config: I2cFunctionClockConfig, + ) { + let _ = self; + // sclk_sel: 0 = XTAL, 1 = RC_FAST + I2C0::regs().clk_conf().modify(|_, w| unsafe { + w.sclk_sel() + .bit(matches!(new_config.sclk, I2cFunctionClockSclk::RcFast)); + w.sclk_div_num().bits(new_config.div_num as _) + }); + } +} diff --git a/esp-hal/src/soc/esp32c3/clocks.rs b/esp-hal/src/soc/esp32c3/clocks.rs index d416df47cb6..c960ae03d4d 100644 --- a/esp-hal/src/soc/esp32c3/clocks.rs +++ b/esp-hal/src/soc/esp32c3/clocks.rs @@ -17,7 +17,7 @@ use esp_rom_sys::rom::{ets_delay_us, ets_update_cpu_frequency_rom}; use crate::{ - peripherals::{APB_CTRL, I2C_ANA_MST, LPWR, SYSTEM, TIMG0, TIMG1, UART0, UART1}, + peripherals::{APB_CTRL, I2C_ANA_MST, I2C0, LPWR, SYSTEM, TIMG0, TIMG1, UART0, UART1}, soc::regi2c, time::Rate, }; @@ -731,3 +731,29 @@ impl UartInstance { }); } } + +impl I2cInstance { + // I2C_FUNCTION_CLOCK + + fn enable_function_clock_impl(self, _clocks: &mut ClockTree, en: bool) { + let _ = self; + I2C0::regs() + .clk_conf() + .modify(|_, w| w.sclk_active().bit(en)); + } + + fn configure_function_clock_impl( + self, + _clocks: &mut ClockTree, + _old_config: Option, + new_config: I2cFunctionClockConfig, + ) { + let _ = self; + // sclk_sel: 0 = XTAL, 1 = RC_FAST + I2C0::regs().clk_conf().modify(|_, w| unsafe { + w.sclk_sel() + .bit(matches!(new_config.sclk, I2cFunctionClockSclk::RcFast)); + w.sclk_div_num().bits(new_config.div_num as _) + }); + } +} diff --git a/esp-hal/src/soc/esp32c5/clocks.rs b/esp-hal/src/soc/esp32c5/clocks.rs index ba5680cbdcd..5b7cb56b074 100644 --- a/esp-hal/src/soc/esp32c5/clocks.rs +++ b/esp-hal/src/soc/esp32c5/clocks.rs @@ -704,3 +704,29 @@ impl UartInstance { }); } } + +impl I2cInstance { + // I2C_FUNCTION_CLOCK + + fn enable_function_clock_impl(self, _clocks: &mut ClockTree, en: bool) { + let _ = self; + PCR::regs() + .i2c_sclk_conf() + .modify(|_, w| w.i2c_sclk_en().bit(en)); + } + + fn configure_function_clock_impl( + self, + _clocks: &mut ClockTree, + _old_config: Option, + new_config: I2cFunctionClockConfig, + ) { + let _ = self; + // sclk_sel: 0 = XTAL, 1 = RC_FAST + PCR::regs().i2c_sclk_conf().modify(|_, w| unsafe { + w.i2c_sclk_sel() + .bit(matches!(new_config.sclk, I2cFunctionClockSclk::RcFast)); + w.i2c_sclk_div_num().bits(new_config.div_num as _) + }); + } +} diff --git a/esp-hal/src/soc/esp32c6/clocks.rs b/esp-hal/src/soc/esp32c6/clocks.rs index a155308139c..d2493bb7999 100644 --- a/esp-hal/src/soc/esp32c6/clocks.rs +++ b/esp-hal/src/soc/esp32c6/clocks.rs @@ -780,3 +780,29 @@ impl UartInstance { }); } } + +impl I2cInstance { + // I2C_FUNCTION_CLOCK + + fn enable_function_clock_impl(self, _clocks: &mut ClockTree, en: bool) { + let _ = self; + PCR::regs() + .i2c_sclk_conf() + .modify(|_, w| w.i2c_sclk_en().bit(en)); + } + + fn configure_function_clock_impl( + self, + _clocks: &mut ClockTree, + _old_config: Option, + new_config: I2cFunctionClockConfig, + ) { + let _ = self; + // sclk_sel: 0 = XTAL, 1 = RC_FAST + PCR::regs().i2c_sclk_conf().modify(|_, w| unsafe { + w.i2c_sclk_sel() + .bit(matches!(new_config.sclk, I2cFunctionClockSclk::RcFast)); + w.i2c_sclk_div_num().bits(new_config.div_num as _) + }); + } +} diff --git a/esp-hal/src/soc/esp32c61/clocks.rs b/esp-hal/src/soc/esp32c61/clocks.rs index 2e21fcf8ec7..63fb93cdba3 100644 --- a/esp-hal/src/soc/esp32c61/clocks.rs +++ b/esp-hal/src/soc/esp32c61/clocks.rs @@ -544,3 +544,29 @@ impl UartInstance { }); } } + +impl I2cInstance { + // I2C_FUNCTION_CLOCK + + fn enable_function_clock_impl(self, _clocks: &mut ClockTree, en: bool) { + let _ = self; + PCR::regs() + .i2c_sclk_conf() + .modify(|_, w| w.i2c_sclk_en().bit(en)); + } + + fn configure_function_clock_impl( + self, + _clocks: &mut ClockTree, + _old_config: Option, + new_config: I2cFunctionClockConfig, + ) { + let _ = self; + // sclk_sel: 0 = XTAL, 1 = RC_FAST + PCR::regs().i2c_sclk_conf().modify(|_, w| unsafe { + w.i2c_sclk_sel() + .bit(matches!(new_config.sclk, I2cFunctionClockSclk::RcFast)); + w.i2c_sclk_div_num().bits(new_config.div_num as _) + }); + } +} diff --git a/esp-hal/src/soc/esp32h2/clocks.rs b/esp-hal/src/soc/esp32h2/clocks.rs index 12a1f7517dd..7e5d82fb231 100644 --- a/esp-hal/src/soc/esp32h2/clocks.rs +++ b/esp-hal/src/soc/esp32h2/clocks.rs @@ -614,3 +614,35 @@ impl UartInstance { }); } } + +impl I2cInstance { + // I2C_FUNCTION_CLOCK + + fn enable_function_clock_impl(self, _clocks: &mut ClockTree, en: bool) { + let i2c = match self { + I2cInstance::I2c0 => 0, + I2cInstance::I2c1 => 1, + }; + PCR::regs() + .i2c_sclk_conf(i2c) + .modify(|_, w| w.i2c_sclk_en().bit(en)); + } + + fn configure_function_clock_impl( + self, + _clocks: &mut ClockTree, + _old_config: Option, + new_config: I2cFunctionClockConfig, + ) { + let i2c = match self { + I2cInstance::I2c0 => 0, + I2cInstance::I2c1 => 1, + }; + // sclk_sel: 0 = XTAL, 1 = RC_FAST + PCR::regs().i2c_sclk_conf(i2c).modify(|_, w| unsafe { + w.i2c_sclk_sel() + .bit(matches!(new_config.sclk, I2cFunctionClockSclk::RcFast)); + w.i2c_sclk_div_num().bits(new_config.div_num as _) + }); + } +} diff --git a/esp-hal/src/soc/esp32p4/clocks.rs b/esp-hal/src/soc/esp32p4/clocks.rs index 7d2ddfbb98d..39e855ed9fb 100644 --- a/esp-hal/src/soc/esp32p4/clocks.rs +++ b/esp-hal/src/soc/esp32p4/clocks.rs @@ -308,6 +308,47 @@ impl UartInstance { } } +impl I2cInstance { + // I2C_FUNCTION_CLOCK + + fn enable_function_clock_impl(self, _clocks: &mut ClockTree, en: bool) { + HP_SYS_CLKRST::regs() + .peri_clk_ctrl10() + .modify(|_, w| match self { + I2cInstance::I2c0 => w.i2c0_clk_en().bit(en), + I2cInstance::I2c1 => w.i2c1_clk_en().bit(en), + }); + } + + fn configure_function_clock_impl( + self, + _clocks: &mut ClockTree, + _old_config: Option, + new_config: I2cFunctionClockConfig, + ) { + // clk_src_sel: 0 = XTAL, 1 = RC_FAST + let rc_fast = matches!(new_config.sclk, I2cFunctionClockSclk::RcFast); + match self { + I2cInstance::I2c0 => { + HP_SYS_CLKRST::regs() + .peri_clk_ctrl10() + .modify(|_, w| unsafe { + w.i2c0_clk_src_sel().bit(rc_fast); + w.i2c0_clk_div_num().bits(new_config.div_num as _) + }); + } + I2cInstance::I2c1 => { + HP_SYS_CLKRST::regs() + .peri_clk_ctrl10() + .modify(|_, w| w.i2c1_clk_src_sel().bit(rc_fast)); + HP_SYS_CLKRST::regs() + .peri_clk_ctrl11() + .modify(|_, w| unsafe { w.i2c1_clk_div_num().bits(new_config.div_num as _) }); + } + } + } +} + // Per-instance clock impl for TIMG impl TimgInstance { diff --git a/esp-hal/src/soc/esp32s2/clocks.rs b/esp-hal/src/soc/esp32s2/clocks.rs index c9ad3b26c17..ec9bcee5c49 100644 --- a/esp-hal/src/soc/esp32s2/clocks.rs +++ b/esp-hal/src/soc/esp32s2/clocks.rs @@ -18,7 +18,7 @@ use esp_rom_sys::rom::{ets_delay_us, ets_update_cpu_frequency_rom}; use crate::{ - peripherals::{I2C_ANA_MST, LPWR, RMT, SYSCON, SYSTEM, TIMG0, TIMG1, UART0, UART1}, + peripherals::{I2C_ANA_MST, I2C0, I2C1, LPWR, RMT, SYSCON, SYSTEM, TIMG0, TIMG1, UART0, UART1}, soc::regi2c, time::Rate, }; @@ -762,3 +762,29 @@ impl UartInstance { // Nothing to do. } } + +impl I2cInstance { + // I2C_FUNCTION_CLOCK + + fn enable_function_clock_impl(self, _clocks: &mut ClockTree, _en: bool) { + // No dedicated enable bit on ESP32-S2; clock selection via ref_always_on. + let _ = self; + } + + fn configure_function_clock_impl( + self, + _clocks: &mut ClockTree, + _old_config: Option, + new_config: I2cFunctionClockConfig, + ) { + let regs = match self { + I2cInstance::I2c0 => I2C0::regs(), + I2cInstance::I2c1 => I2C1::regs(), + }; + // ref_always_on: 1 = APB, 0 = REF_TICK + regs.ctr().modify(|_, w| { + w.ref_always_on() + .bit(!matches!(new_config.sclk, I2cFunctionClockSclk::RefTick)) + }); + } +} diff --git a/esp-hal/src/soc/esp32s3/clocks.rs b/esp-hal/src/soc/esp32s3/clocks.rs index ce2c685f11e..861aa67ba41 100644 --- a/esp-hal/src/soc/esp32s3/clocks.rs +++ b/esp-hal/src/soc/esp32s3/clocks.rs @@ -17,7 +17,7 @@ use esp_rom_sys::rom::{ets_delay_us, ets_update_cpu_frequency_rom}; use crate::{ - peripherals::{I2C_ANA_MST, LPWR, RMT, SYSTEM, TIMG0, TIMG1, UART0, UART1, UART2}, + peripherals::{I2C_ANA_MST, I2C0, I2C1, LPWR, RMT, SYSTEM, TIMG0, TIMG1, UART0, UART1, UART2}, soc::regi2c, time::Rate, }; @@ -922,3 +922,34 @@ impl UartInstance { // Nothing to do. } } + +impl I2cInstance { + // I2C_FUNCTION_CLOCK + // Note: on ESP32-S3 both I2C0 and I2C1 share the same sclk_sel hardware bit (HW errata). + + fn enable_function_clock_impl(self, _clocks: &mut ClockTree, en: bool) { + let regs = match self { + I2cInstance::I2c0 => I2C0::regs(), + I2cInstance::I2c1 => I2C1::regs(), + }; + regs.clk_conf().modify(|_, w| w.sclk_active().bit(en)); + } + + fn configure_function_clock_impl( + self, + _clocks: &mut ClockTree, + _old_config: Option, + new_config: I2cFunctionClockConfig, + ) { + let regs = match self { + I2cInstance::I2c0 => I2C0::regs(), + I2cInstance::I2c1 => I2C1::regs(), + }; + // sclk_sel: 0 = XTAL, 1 = RC_FAST + regs.clk_conf().modify(|_, w| unsafe { + w.sclk_sel() + .bit(matches!(new_config.sclk, I2cFunctionClockSclk::RcFast)); + w.sclk_div_num().bits(new_config.div_num as _) + }); + } +} diff --git a/esp-metadata-generated/src/_build_script_utils.rs b/esp-metadata-generated/src/_build_script_utils.rs index 08dc7b1c71b..8f25fa78d97 100644 --- a/esp-metadata-generated/src/_build_script_utils.rs +++ b/esp-metadata-generated/src/_build_script_utils.rs @@ -392,6 +392,7 @@ impl Chip { "soc_has_clock_node_uart_mem_clk", "soc_has_clock_node_timg_calibration_clock", "soc_has_clock_node_mcpwm_function_clock", + "soc_has_clock_node_i2c_function_clock", "soc_has_clock_node_uart_function_clock", "soc_has_clock_node_uart_mem_clock", "soc_has_clock_node_uart_baud_rate_generator", @@ -606,6 +607,7 @@ impl Chip { "cargo:rustc-cfg=soc_has_clock_node_uart_mem_clk", "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_i2c_function_clock", "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", @@ -938,6 +940,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_i2c_function_clock", "has_dram_region", "has_dram2_uninit_region", "spi_master_version=\"3\"", @@ -1106,6 +1109,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_i2c_function_clock", "cargo:rustc-cfg=has_dram_region", "cargo:rustc-cfg=has_dram2_uninit_region", "cargo:rustc-cfg=spi_master_version=\"3\"", @@ -1429,6 +1433,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_i2c_function_clock", "has_dram_region", "has_dram2_uninit_region", "spi_master_version=\"3\"", @@ -1648,6 +1653,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_i2c_function_clock", "cargo:rustc-cfg=has_dram_region", "cargo:rustc-cfg=has_dram2_uninit_region", "cargo:rustc-cfg=spi_master_version=\"3\"", @@ -2023,6 +2029,7 @@ impl Chip { "soc_has_clock_node_rmt_sclk", "soc_has_clock_node_parl_io_rx_clock", "soc_has_clock_node_parl_io_tx_clock", + "soc_has_clock_node_i2c_function_clock", "has_dram_region", "has_dram2_uninit_region", "spi_master_version=\"3\"", @@ -2290,6 +2297,7 @@ impl Chip { "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", + "cargo:rustc-cfg=soc_has_clock_node_i2c_function_clock", "cargo:rustc-cfg=has_dram_region", "cargo:rustc-cfg=has_dram2_uninit_region", "cargo:rustc-cfg=spi_master_version=\"3\"", @@ -2718,6 +2726,7 @@ impl Chip { "soc_has_clock_node_mcpwm_function_clock", "soc_has_clock_node_parl_io_rx_clock", "soc_has_clock_node_parl_io_tx_clock", + "soc_has_clock_node_i2c_function_clock", "has_dram_region", "has_dram2_uninit_region", "spi_master_version=\"3\"", @@ -3007,6 +3016,7 @@ impl Chip { "cargo:rustc-cfg=soc_has_clock_node_mcpwm_function_clock", "cargo:rustc-cfg=soc_has_clock_node_parl_io_rx_clock", "cargo:rustc-cfg=soc_has_clock_node_parl_io_tx_clock", + "cargo:rustc-cfg=soc_has_clock_node_i2c_function_clock", "cargo:rustc-cfg=has_dram_region", "cargo:rustc-cfg=has_dram2_uninit_region", "cargo:rustc-cfg=spi_master_version=\"3\"", @@ -3365,6 +3375,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_i2c_function_clock", "has_dram_region", "has_dram2_uninit_region", "spi_master_version=\"3\"", @@ -3577,6 +3588,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_i2c_function_clock", "cargo:rustc-cfg=has_dram_region", "cargo:rustc-cfg=has_dram2_uninit_region", "cargo:rustc-cfg=spi_master_version=\"3\"", @@ -3977,6 +3989,7 @@ impl Chip { "soc_has_clock_node_mcpwm_function_clock", "soc_has_clock_node_parl_io_rx_clock", "soc_has_clock_node_parl_io_tx_clock", + "soc_has_clock_node_i2c_function_clock", "has_dram_region", "has_dram2_uninit_region", "spi_master_version=\"3\"", @@ -4233,6 +4246,7 @@ impl Chip { "cargo:rustc-cfg=soc_has_clock_node_mcpwm_function_clock", "cargo:rustc-cfg=soc_has_clock_node_parl_io_rx_clock", "cargo:rustc-cfg=soc_has_clock_node_parl_io_tx_clock", + "cargo:rustc-cfg=soc_has_clock_node_i2c_function_clock", "cargo:rustc-cfg=has_dram_region", "cargo:rustc-cfg=has_dram2_uninit_region", "cargo:rustc-cfg=spi_master_version=\"3\"", @@ -4526,6 +4540,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_i2c_function_clock", "has_dram_region", "has_dram2_uninit_region", "spi_master_version=\"3\"", @@ -4706,6 +4721,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_i2c_function_clock", "cargo:rustc-cfg=has_dram_region", "cargo:rustc-cfg=has_dram2_uninit_region", "cargo:rustc-cfg=spi_master_version=\"3\"", @@ -5171,6 +5187,7 @@ impl Chip { "soc_has_clock_node_uart_baud_rate_generator", "soc_has_clock_node_uart_mem_clock", "soc_has_clock_node_rmt_sclk", + "soc_has_clock_node_i2c_function_clock", "has_dram_region", "has_dram2_uninit_region", "spi_master_version=\"2\"", @@ -5401,6 +5418,7 @@ impl Chip { "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_rmt_sclk", + "cargo:rustc-cfg=soc_has_clock_node_i2c_function_clock", "cargo:rustc-cfg=has_dram_region", "cargo:rustc-cfg=has_dram2_uninit_region", "cargo:rustc-cfg=spi_master_version=\"2\"", @@ -5856,6 +5874,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_i2c_function_clock", "has_dram_region", "has_dram2_uninit_region", "spi_master_version=\"3\"", @@ -6119,6 +6138,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_i2c_function_clock", "cargo:rustc-cfg=has_dram_region", "cargo:rustc-cfg=has_dram2_uninit_region", "cargo:rustc-cfg=spi_master_version=\"3\"", @@ -6561,6 +6581,7 @@ pub fn emit_check_cfg_directives() { println!("cargo:rustc-check-cfg=cfg(soc_has_clock_node_uart_mem_clk)"); println!("cargo:rustc-check-cfg=cfg(soc_has_clock_node_timg_calibration_clock)"); println!("cargo:rustc-check-cfg=cfg(soc_has_clock_node_mcpwm_function_clock)"); + println!("cargo:rustc-check-cfg=cfg(soc_has_clock_node_i2c_function_clock)"); println!("cargo:rustc-check-cfg=cfg(soc_has_clock_node_uart_function_clock)"); println!("cargo:rustc-check-cfg=cfg(soc_has_clock_node_uart_mem_clock)"); println!("cargo:rustc-check-cfg=cfg(soc_has_clock_node_uart_baud_rate_generator)"); diff --git a/esp-metadata-generated/src/_generated_esp32.rs b/esp-metadata-generated/src/_generated_esp32.rs index 4663eb5c9e7..b004ea12a1b 100644 --- a/esp-metadata-generated/src/_generated_esp32.rs +++ b/esp-metadata-generated/src/_generated_esp32.rs @@ -870,6 +870,22 @@ macro_rules! for_each_sha_algorithm { /// todo!() /// } /// +/// impl I2cInstance { +/// // I2C_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: I2cFunctionClockConfig, +/// ) { +/// todo!() +/// } +/// } /// impl McpwmInstance { /// // MCPWM_FUNCTION_CLOCK /// @@ -951,6 +967,12 @@ macro_rules! for_each_sha_algorithm { /// ``` macro_rules! define_clock_tree_types { () => { + #[derive(Clone, Copy, PartialEq, Eq, Debug)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum I2cInstance { + I2c0 = 0, + I2c1 = 1, + } #[derive(Clone, Copy, PartialEq, Eq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum McpwmInstance { @@ -1279,6 +1301,30 @@ macro_rules! define_clock_tree_types { /// Selects `XTAL32K_CLK`. Xtal32kClk, } + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum I2cFunctionClockSclk { + #[default] + /// Selects `APB_CLK`. + Apb, + } + /// Configures the `I2C0_FUNCTION_CLOCK` clock node. + /// + /// The output is calculated as `OUTPUT = sclk`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct I2cFunctionClockConfig { + sclk: I2cFunctionClockSclk, + } + impl I2cFunctionClockConfig { + /// Creates a new configuration for the FUNCTION_CLOCK clock node. + pub const fn new(sclk: I2cFunctionClockSclk) -> Self { + Self { sclk } + } + fn sclk(self) -> I2cFunctionClockSclk { + self.sclk + } + } /// The list of clock signals that the `MCPWM0_FUNCTION_CLOCK` multiplexer can output. #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -1396,6 +1442,7 @@ macro_rules! define_clock_tree_types { rtc_slow_clk: Option, rtc_fast_clk: Option, timg_calibration_clock: Option, + i2c_function_clock: [Option; 2], mcpwm_function_clock: [Option; 2], rmt_sclk: [Option; 1], uart_function_clock: [Option; 3], @@ -1413,6 +1460,7 @@ macro_rules! define_clock_tree_types { rtc_fast_clk_refcount: u32, uart_mem_clk_refcount: u32, timg_calibration_clock_refcount: u32, + i2c_function_clock_refcount: [u32; 2], mcpwm_function_clock_refcount: [u32; 2], rmt_sclk_refcount: [u32; 1], uart_function_clock_refcount: [u32; 3], @@ -1492,6 +1540,14 @@ macro_rules! define_clock_tree_types { pub fn timg_calibration_clock(&self) -> Option { self.timg_calibration_clock } + /// 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] + } + /// Returns the current configuration of the I2C1_FUNCTION_CLOCK clock tree node + pub fn i2c1_function_clock(&self) -> Option { + self.i2c_function_clock[I2cInstance::I2c1 as usize] + } /// Returns the current configuration of the MCPWM0_FUNCTION_CLOCK clock tree node pub fn mcpwm0_function_clock(&self) -> Option { self.mcpwm_function_clock[McpwmInstance::Mcpwm0 as usize] @@ -1560,6 +1616,7 @@ macro_rules! define_clock_tree_types { rtc_slow_clk: None, rtc_fast_clk: None, timg_calibration_clock: None, + i2c_function_clock: [None; 2], mcpwm_function_clock: [None; 2], rmt_sclk: [None; 1], uart_function_clock: [None; 3], @@ -1577,6 +1634,7 @@ macro_rules! define_clock_tree_types { rtc_fast_clk_refcount: 0, uart_mem_clk_refcount: 0, timg_calibration_clock_refcount: 0, + i2c_function_clock_refcount: [0; 2], mcpwm_function_clock_refcount: [0; 2], rmt_sclk_refcount: [0; 1], uart_function_clock_refcount: [0; 3], @@ -1619,6 +1677,8 @@ macro_rules! define_clock_tree_types { ::core::sync::atomic::AtomicU32::new(0); static REF_TICK_PLL_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = ::core::sync::atomic::AtomicU32::new(0); + static I2C_FUNCTION_CLOCK_FREQ_CACHE: [::core::sync::atomic::AtomicU32; 2] = + [const { ::core::sync::atomic::AtomicU32::new(0) }; 2]; static REF_TICK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = ::core::sync::atomic::AtomicU32::new(0); static RMT_SCLK_FREQ_CACHE: [::core::sync::atomic::AtomicU32; 1] = @@ -2523,6 +2583,53 @@ macro_rules! define_clock_tree_types { pub fn timg_calibration_clock_frequency() -> u32 { TIMG_CALIBRATION_CLOCK_FREQ_CACHE.load(::core::sync::atomic::Ordering::Acquire) } + impl I2cInstance { + pub fn configure_function_clock( + self, + clocks: &mut ClockTree, + config: I2cFunctionClockConfig, + ) { + let old_config = clocks.i2c_function_clock[self as usize].replace(config); + refresh_i2c_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.i2c_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.i2c_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.i2c_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( + self, + clocks: &mut ClockTree, + config: I2cFunctionClockConfig, + ) -> u32 { + apb_clk_frequency() + } + pub fn function_clock_frequency(self) -> u32 { + I2C_FUNCTION_CLOCK_FREQ_CACHE[self as usize] + .load(::core::sync::atomic::Ordering::Acquire) + } + } impl McpwmInstance { pub fn configure_function_clock( self, @@ -3006,6 +3113,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 [I2cInstance::I2c0, I2cInstance::I2c1] { + refresh_i2c_function_clock_downstream(clocks, child_instance); + } for child_instance in [RmtInstance::Rmt] { refresh_rmt_sclk_downstream(clocks, child_instance); } @@ -3053,6 +3163,14 @@ macro_rules! define_clock_tree_types { } refresh_ref_tick_downstream(clocks); } + 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( + instance.function_clock_config_frequency(clocks, config), + ::core::sync::atomic::Ordering::Release, + ); + } + } fn refresh_ref_tick_downstream(clocks: &mut ClockTree) { if let Some(config) = clocks.ref_tick { REF_TICK_FREQ_CACHE.store( diff --git a/esp-metadata-generated/src/_generated_esp32c2.rs b/esp-metadata-generated/src/_generated_esp32c2.rs index 742d13d2968..ceb08eb0672 100644 --- a/esp-metadata-generated/src/_generated_esp32c2.rs +++ b/esp-metadata-generated/src/_generated_esp32c2.rs @@ -292,6 +292,9 @@ macro_rules! property { ("clock_tree.uart.baud_rate_generator.integral") => { (0, 4095) }; + ("clock_tree.i2c.function_clock.div_num") => { + (0, 255) + }; ("spi_master.version") => { 3 }; @@ -842,6 +845,22 @@ macro_rules! for_each_sha_algorithm { /// todo!() /// } /// +/// impl I2cInstance { +/// // I2C_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: I2cFunctionClockConfig, +/// ) { +/// todo!() +/// } +/// } /// impl TimgInstance { /// // TIMG_FUNCTION_CLOCK /// @@ -922,6 +941,11 @@ macro_rules! for_each_sha_algorithm { /// ``` macro_rules! define_clock_tree_types { () => { + #[derive(Clone, Copy, PartialEq, Eq, Debug)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum I2cInstance { + I2c0 = 0, + } #[derive(Clone, Copy, PartialEq, Eq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum TimgInstance { @@ -1128,6 +1152,45 @@ macro_rules! define_clock_tree_types { /// Selects `OSC_SLOW_CLK`. Osc32kClk, } + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum I2cFunctionClockSclk { + #[default] + /// Selects `XTAL_CLK`. + Xtal, + /// Selects `RC_FAST_CLK`. + RcFast, + } + /// Configures the `I2C0_FUNCTION_CLOCK` clock node. + /// + /// The output is calculated as `OUTPUT = sclk / (div_num + 1)`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct I2cFunctionClockConfig { + sclk: I2cFunctionClockSclk, + div_num: u32, + } + impl I2cFunctionClockConfig { + /// Creates a new configuration for the FUNCTION_CLOCK clock node. + /// + /// ## Panics + /// + /// Panics if the div_num value is outside the + /// valid range (0 ..= 255). + pub const fn new(sclk: I2cFunctionClockSclk, div_num: u32) -> Self { + ::core::assert!( + div_num <= 255, + "`I2C0_FUNCTION_CLOCK` div_num must be between 0 and 255 (inclusive)." + ); + Self { sclk, div_num } + } + fn sclk(self) -> I2cFunctionClockSclk { + self.sclk + } + fn div_num(self) -> u32 { + self.div_num as u32 + } + } /// 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))] @@ -1257,6 +1320,7 @@ macro_rules! define_clock_tree_types { rtc_fast_clk: Option, low_power_clk: Option, timg_calibration_clock: Option, + i2c_function_clock: [Option; 1], timg_function_clock: [Option; 1], timg_wdt_clock: [Option; 1], uart_function_clock: [Option; 2], @@ -1275,6 +1339,7 @@ macro_rules! define_clock_tree_types { low_power_clk_refcount: u32, uart_mem_clk_refcount: u32, timg_calibration_clock_refcount: u32, + i2c_function_clock_refcount: [u32; 1], timg_function_clock_refcount: [u32; 1], timg_wdt_clock_refcount: [u32; 1], uart_function_clock_refcount: [u32; 2], @@ -1338,6 +1403,10 @@ macro_rules! define_clock_tree_types { pub fn timg_calibration_clock(&self) -> Option { self.timg_calibration_clock } + /// 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] + } /// 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] @@ -1386,6 +1455,7 @@ macro_rules! define_clock_tree_types { rtc_fast_clk: None, low_power_clk: None, timg_calibration_clock: None, + i2c_function_clock: [None; 1], timg_function_clock: [None; 1], timg_wdt_clock: [None; 1], uart_function_clock: [None; 2], @@ -1404,6 +1474,7 @@ macro_rules! define_clock_tree_types { low_power_clk_refcount: 0, uart_mem_clk_refcount: 0, timg_calibration_clock_refcount: 0, + i2c_function_clock_refcount: [0; 1], timg_function_clock_refcount: [0; 1], timg_wdt_clock_refcount: [0; 1], uart_function_clock_refcount: [0; 2], @@ -1430,6 +1501,8 @@ macro_rules! define_clock_tree_types { ::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] = + [const { ::core::sync::atomic::AtomicU32::new(0) }; 1]; static TIMG_FUNCTION_CLOCK_FREQ_CACHE: [::core::sync::atomic::AtomicU32; 1] = [const { ::core::sync::atomic::AtomicU32::new(0) }; 1]; static TIMG_WDT_CLOCK_FREQ_CACHE: [::core::sync::atomic::AtomicU32; 1] = @@ -2251,6 +2324,76 @@ macro_rules! define_clock_tree_types { pub fn timg_calibration_clock_frequency() -> u32 { TIMG_CALIBRATION_CLOCK_FREQ_CACHE.load(::core::sync::atomic::Ordering::Acquire) } + impl I2cInstance { + pub fn configure_function_clock( + self, + clocks: &mut ClockTree, + config: I2cFunctionClockConfig, + ) { + let old_config = clocks.i2c_function_clock[self as usize].replace(config); + refresh_i2c_function_clock_downstream(clocks, self); + if clocks.i2c_function_clock_refcount[self as usize] > 0 { + match config.sclk { + I2cFunctionClockSclk::Xtal => request_xtal_clk(clocks), + I2cFunctionClockSclk::RcFast => request_rc_fast_clk(clocks), + } + self.configure_function_clock_impl(clocks, old_config, config); + if let Some(old_config) = old_config { + match old_config.sclk { + I2cFunctionClockSclk::Xtal => release_xtal_clk(clocks), + I2cFunctionClockSclk::RcFast => release_rc_fast_clk(clocks), + } + } + } else { + self.configure_function_clock_impl(clocks, old_config, config); + } + } + pub fn function_clock_config( + self, + clocks: &mut ClockTree, + ) -> Option { + clocks.i2c_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.i2c_function_clock_refcount[self as usize]) + { + trace!("Enabling {:?}::FUNCTION_CLOCK", self); + match unwrap!(clocks.i2c_function_clock[self as usize]).sclk { + I2cFunctionClockSclk::Xtal => request_xtal_clk(clocks), + I2cFunctionClockSclk::RcFast => request_rc_fast_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.i2c_function_clock_refcount[self as usize]) + { + trace!("Disabling {:?}::FUNCTION_CLOCK", self); + self.enable_function_clock_impl(clocks, false); + match unwrap!(clocks.i2c_function_clock[self as usize]).sclk { + I2cFunctionClockSclk::Xtal => release_xtal_clk(clocks), + I2cFunctionClockSclk::RcFast => release_rc_fast_clk(clocks), + } + } + } + #[allow(unused_variables)] + pub fn function_clock_config_frequency( + self, + clocks: &mut ClockTree, + config: I2cFunctionClockConfig, + ) -> u32 { + (match config.sclk { + I2cFunctionClockSclk::Xtal => xtal_clk_frequency(), + I2cFunctionClockSclk::RcFast => rc_fast_clk_frequency(), + } / (config.div_num() + 1)) + } + pub fn function_clock_frequency(self) -> u32 { + I2C_FUNCTION_CLOCK_FREQ_CACHE[self as usize] + .load(::core::sync::atomic::Ordering::Acquire) + } + } impl TimgInstance { pub fn configure_function_clock( self, @@ -2629,6 +2772,9 @@ macro_rules! define_clock_tree_types { refresh_cpu_pll_div_downstream(clocks); refresh_rtc_fast_clk_downstream(clocks); refresh_low_power_clk_downstream(clocks); + for child_instance in [I2cInstance::I2c0] { + refresh_i2c_function_clock_downstream(clocks, child_instance); + } for child_instance in [TimgInstance::Timg0] { refresh_timg_function_clock_downstream(clocks, child_instance); refresh_timg_wdt_clock_downstream(clocks, child_instance); @@ -2725,6 +2871,14 @@ macro_rules! define_clock_tree_types { ); } } + 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( + instance.function_clock_config_frequency(clocks, config), + ::core::sync::atomic::Ordering::Release, + ); + } + } 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( diff --git a/esp-metadata-generated/src/_generated_esp32c3.rs b/esp-metadata-generated/src/_generated_esp32c3.rs index 6aa81a36462..ea1ed0163e9 100644 --- a/esp-metadata-generated/src/_generated_esp32c3.rs +++ b/esp-metadata-generated/src/_generated_esp32c3.rs @@ -376,6 +376,9 @@ macro_rules! property { ("clock_tree.uart.baud_rate_generator.integral") => { (0, 4095) }; + ("clock_tree.i2c.function_clock.div_num") => { + (0, 255) + }; ("spi_master.version") => { 3 }; @@ -1104,6 +1107,22 @@ macro_rules! for_each_sha_algorithm { /// todo!() /// } /// +/// impl I2cInstance { +/// // I2C_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: I2cFunctionClockConfig, +/// ) { +/// todo!() +/// } +/// } /// impl RmtInstance { /// // RMT_SCLK /// @@ -1200,6 +1219,11 @@ macro_rules! for_each_sha_algorithm { /// ``` macro_rules! define_clock_tree_types { () => { + #[derive(Clone, Copy, PartialEq, Eq, Debug)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum I2cInstance { + I2c0 = 0, + } #[derive(Clone, Copy, PartialEq, Eq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum RmtInstance { @@ -1399,6 +1423,45 @@ macro_rules! define_clock_tree_types { /// Selects `XTAL32K_CLK`. Xtal32kClk, } + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum I2cFunctionClockSclk { + #[default] + /// Selects `XTAL_CLK`. + Xtal, + /// Selects `RC_FAST_CLK`. + RcFast, + } + /// Configures the `I2C0_FUNCTION_CLOCK` clock node. + /// + /// The output is calculated as `OUTPUT = sclk / (div_num + 1)`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct I2cFunctionClockConfig { + sclk: I2cFunctionClockSclk, + div_num: u32, + } + impl I2cFunctionClockConfig { + /// Creates a new configuration for the FUNCTION_CLOCK clock node. + /// + /// ## Panics + /// + /// Panics if the div_num value is outside the + /// valid range (0 ..= 255). + pub const fn new(sclk: I2cFunctionClockSclk, div_num: u32) -> Self { + ::core::assert!( + div_num <= 255, + "`I2C0_FUNCTION_CLOCK` div_num must be between 0 and 255 (inclusive)." + ); + Self { sclk, div_num } + } + fn sclk(self) -> I2cFunctionClockSclk { + self.sclk + } + fn div_num(self) -> u32 { + self.div_num as u32 + } + } /// The list of clock signals that the `RMT_SCLK` multiplexer can output. #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -1540,6 +1603,7 @@ macro_rules! define_clock_tree_types { rtc_fast_clk: Option, low_power_clk: Option, timg_calibration_clock: Option, + i2c_function_clock: [Option; 1], rmt_sclk: [Option; 1], timg_function_clock: [Option; 2], timg_wdt_clock: [Option; 2], @@ -1556,6 +1620,7 @@ macro_rules! define_clock_tree_types { low_power_clk_refcount: u32, uart_mem_clk_refcount: u32, timg_calibration_clock_refcount: u32, + i2c_function_clock_refcount: [u32; 1], rmt_sclk_refcount: [u32; 1], timg_function_clock_refcount: [u32; 2], timg_wdt_clock_refcount: [u32; 2], @@ -1620,6 +1685,10 @@ macro_rules! define_clock_tree_types { pub fn timg_calibration_clock(&self) -> Option { self.timg_calibration_clock } + /// 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] + } /// Returns the current configuration of the RMT_SCLK clock tree node pub fn rmt_sclk(&self) -> Option { self.rmt_sclk[RmtInstance::Rmt as usize] @@ -1680,6 +1749,7 @@ macro_rules! define_clock_tree_types { rtc_fast_clk: None, low_power_clk: None, timg_calibration_clock: None, + i2c_function_clock: [None; 1], rmt_sclk: [None; 1], timg_function_clock: [None; 2], timg_wdt_clock: [None; 2], @@ -1696,6 +1766,7 @@ macro_rules! define_clock_tree_types { low_power_clk_refcount: 0, uart_mem_clk_refcount: 0, timg_calibration_clock_refcount: 0, + i2c_function_clock_refcount: [0; 1], rmt_sclk_refcount: [0; 1], timg_function_clock_refcount: [0; 2], timg_wdt_clock_refcount: [0; 2], @@ -1725,6 +1796,8 @@ macro_rules! define_clock_tree_types { ::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] = + [const { ::core::sync::atomic::AtomicU32::new(0) }; 1]; static UART_MEM_CLOCK_FREQ_CACHE: [::core::sync::atomic::AtomicU32; 2] = [const { ::core::sync::atomic::AtomicU32::new(0) }; 2]; static APB_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = @@ -2463,6 +2536,76 @@ macro_rules! define_clock_tree_types { pub fn timg_calibration_clock_frequency() -> u32 { TIMG_CALIBRATION_CLOCK_FREQ_CACHE.load(::core::sync::atomic::Ordering::Acquire) } + impl I2cInstance { + pub fn configure_function_clock( + self, + clocks: &mut ClockTree, + config: I2cFunctionClockConfig, + ) { + let old_config = clocks.i2c_function_clock[self as usize].replace(config); + refresh_i2c_function_clock_downstream(clocks, self); + if clocks.i2c_function_clock_refcount[self as usize] > 0 { + match config.sclk { + I2cFunctionClockSclk::Xtal => request_xtal_clk(clocks), + I2cFunctionClockSclk::RcFast => request_rc_fast_clk(clocks), + } + self.configure_function_clock_impl(clocks, old_config, config); + if let Some(old_config) = old_config { + match old_config.sclk { + I2cFunctionClockSclk::Xtal => release_xtal_clk(clocks), + I2cFunctionClockSclk::RcFast => release_rc_fast_clk(clocks), + } + } + } else { + self.configure_function_clock_impl(clocks, old_config, config); + } + } + pub fn function_clock_config( + self, + clocks: &mut ClockTree, + ) -> Option { + clocks.i2c_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.i2c_function_clock_refcount[self as usize]) + { + trace!("Enabling {:?}::FUNCTION_CLOCK", self); + match unwrap!(clocks.i2c_function_clock[self as usize]).sclk { + I2cFunctionClockSclk::Xtal => request_xtal_clk(clocks), + I2cFunctionClockSclk::RcFast => request_rc_fast_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.i2c_function_clock_refcount[self as usize]) + { + trace!("Disabling {:?}::FUNCTION_CLOCK", self); + self.enable_function_clock_impl(clocks, false); + match unwrap!(clocks.i2c_function_clock[self as usize]).sclk { + I2cFunctionClockSclk::Xtal => release_xtal_clk(clocks), + I2cFunctionClockSclk::RcFast => release_rc_fast_clk(clocks), + } + } + } + #[allow(unused_variables)] + pub fn function_clock_config_frequency( + self, + clocks: &mut ClockTree, + config: I2cFunctionClockConfig, + ) -> u32 { + (match config.sclk { + I2cFunctionClockSclk::Xtal => xtal_clk_frequency(), + I2cFunctionClockSclk::RcFast => rc_fast_clk_frequency(), + } / (config.div_num() + 1)) + } + pub fn function_clock_frequency(self) -> u32 { + I2C_FUNCTION_CLOCK_FREQ_CACHE[self as usize] + .load(::core::sync::atomic::Ordering::Acquire) + } + } impl RmtInstance { pub fn configure_sclk(self, clocks: &mut ClockTree, new_selector: RmtSclkConfig) { let old_selector = clocks.rmt_sclk[self as usize].replace(new_selector); @@ -2911,6 +3054,9 @@ macro_rules! define_clock_tree_types { refresh_system_pre_div_in_downstream(clocks); refresh_rtc_fast_clk_downstream(clocks); refresh_low_power_clk_downstream(clocks); + for child_instance in [I2cInstance::I2c0] { + refresh_i2c_function_clock_downstream(clocks, child_instance); + } for child_instance in [UartInstance::Uart0, UartInstance::Uart1] { refresh_uart_mem_clock_downstream(clocks, child_instance); refresh_uart_function_clock_downstream(clocks, child_instance); @@ -3011,6 +3157,14 @@ macro_rules! define_clock_tree_types { ); } } + 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( + instance.function_clock_config_frequency(clocks, config), + ::core::sync::atomic::Ordering::Release, + ); + } + } fn refresh_uart_mem_clock_downstream(clocks: &mut ClockTree, instance: UartInstance) { if let Some(config) = clocks.uart_mem_clock[instance as usize] { UART_MEM_CLOCK_FREQ_CACHE[instance as usize].store( diff --git a/esp-metadata-generated/src/_generated_esp32c5.rs b/esp-metadata-generated/src/_generated_esp32c5.rs index 51ffedae905..2bc4eefe904 100644 --- a/esp-metadata-generated/src/_generated_esp32c5.rs +++ b/esp-metadata-generated/src/_generated_esp32c5.rs @@ -421,6 +421,9 @@ macro_rules! property { ("clock_tree.uart.baud_rate_generator.integral") => { (0, 4095) }; + ("clock_tree.i2c.function_clock.div_num") => { + (0, 255) + }; ("spi_master.version") => { 3 }; @@ -1232,6 +1235,22 @@ macro_rules! for_each_sha_algorithm { /// todo!() /// } /// +/// impl I2cInstance { +/// // I2C_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: I2cFunctionClockConfig, +/// ) { +/// todo!() +/// } +/// } /// impl ParlIoInstance { /// // PARL_IO_RX_CLOCK /// @@ -1344,6 +1363,11 @@ macro_rules! for_each_sha_algorithm { /// ``` macro_rules! define_clock_tree_types { () => { + #[derive(Clone, Copy, PartialEq, Eq, Debug)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum I2cInstance { + I2c0 = 0, + } #[derive(Clone, Copy, PartialEq, Eq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ParlIoInstance { @@ -1520,6 +1544,45 @@ macro_rules! define_clock_tree_types { /// Selects `XTAL32K_CLK`. Xtal32kClk, } + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum I2cFunctionClockSclk { + #[default] + /// Selects `XTAL_CLK`. + Xtal, + /// Selects `RC_FAST_CLK`. + RcFast, + } + /// Configures the `I2C0_FUNCTION_CLOCK` clock node. + /// + /// The output is calculated as `OUTPUT = sclk / (div_num + 1)`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct I2cFunctionClockConfig { + sclk: I2cFunctionClockSclk, + div_num: u32, + } + impl I2cFunctionClockConfig { + /// Creates a new configuration for the FUNCTION_CLOCK clock node. + /// + /// ## Panics + /// + /// Panics if the div_num value is outside the + /// valid range (0 ..= 255). + pub const fn new(sclk: I2cFunctionClockSclk, div_num: u32) -> Self { + ::core::assert!( + div_num <= 255, + "`I2C0_FUNCTION_CLOCK` div_num must be between 0 and 255 (inclusive)." + ); + Self { sclk, div_num } + } + fn sclk(self) -> I2cFunctionClockSclk { + self.sclk + } + fn div_num(self) -> u32 { + self.div_num as u32 + } + } /// The list of clock signals that the `PARL_IO_RX_CLOCK` multiplexer can output. #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -1673,6 +1736,7 @@ macro_rules! define_clock_tree_types { lp_slow_clk: Option, crypto_clk: Option, timg_calibration_clock: Option, + i2c_function_clock: [Option; 1], parl_io_rx_clock: [Option; 1], parl_io_tx_clock: [Option; 1], rmt_sclk: [Option; 1], @@ -1700,6 +1764,7 @@ macro_rules! define_clock_tree_types { lp_slow_clk_refcount: u32, crypto_clk_refcount: u32, timg_calibration_clock_refcount: u32, + i2c_function_clock_refcount: [u32; 1], parl_io_rx_clock_refcount: [u32; 1], parl_io_tx_clock_refcount: [u32; 1], rmt_sclk_refcount: [u32; 1], @@ -1749,6 +1814,10 @@ macro_rules! define_clock_tree_types { pub fn timg_calibration_clock(&self) -> Option { self.timg_calibration_clock } + /// 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] + } /// Returns the current configuration of the PARL_IO_RX_CLOCK clock tree node pub fn parl_io_rx_clock(&self) -> Option { self.parl_io_rx_clock[ParlIoInstance::ParlIo as usize] @@ -1805,6 +1874,7 @@ macro_rules! define_clock_tree_types { lp_slow_clk: None, crypto_clk: None, timg_calibration_clock: None, + i2c_function_clock: [None; 1], parl_io_rx_clock: [None; 1], parl_io_tx_clock: [None; 1], rmt_sclk: [None; 1], @@ -1832,6 +1902,7 @@ macro_rules! define_clock_tree_types { lp_slow_clk_refcount: 0, crypto_clk_refcount: 0, timg_calibration_clock_refcount: 0, + i2c_function_clock_refcount: [0; 1], parl_io_rx_clock_refcount: [0; 1], parl_io_tx_clock_refcount: [0; 1], rmt_sclk_refcount: [0; 1], @@ -1858,6 +1929,8 @@ macro_rules! define_clock_tree_types { ::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] = + [const { ::core::sync::atomic::AtomicU32::new(0) }; 1]; static PARL_IO_RX_CLOCK_FREQ_CACHE: [::core::sync::atomic::AtomicU32; 1] = [const { ::core::sync::atomic::AtomicU32::new(0) }; 1]; static PARL_IO_TX_CLOCK_FREQ_CACHE: [::core::sync::atomic::AtomicU32; 1] = @@ -2569,6 +2642,76 @@ macro_rules! define_clock_tree_types { pub fn timg_calibration_clock_frequency() -> u32 { TIMG_CALIBRATION_CLOCK_FREQ_CACHE.load(::core::sync::atomic::Ordering::Acquire) } + impl I2cInstance { + pub fn configure_function_clock( + self, + clocks: &mut ClockTree, + config: I2cFunctionClockConfig, + ) { + let old_config = clocks.i2c_function_clock[self as usize].replace(config); + refresh_i2c_function_clock_downstream(clocks, self); + if clocks.i2c_function_clock_refcount[self as usize] > 0 { + match config.sclk { + I2cFunctionClockSclk::Xtal => request_xtal_clk(clocks), + I2cFunctionClockSclk::RcFast => request_rc_fast_clk(clocks), + } + self.configure_function_clock_impl(clocks, old_config, config); + if let Some(old_config) = old_config { + match old_config.sclk { + I2cFunctionClockSclk::Xtal => release_xtal_clk(clocks), + I2cFunctionClockSclk::RcFast => release_rc_fast_clk(clocks), + } + } + } else { + self.configure_function_clock_impl(clocks, old_config, config); + } + } + pub fn function_clock_config( + self, + clocks: &mut ClockTree, + ) -> Option { + clocks.i2c_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.i2c_function_clock_refcount[self as usize]) + { + trace!("Enabling {:?}::FUNCTION_CLOCK", self); + match unwrap!(clocks.i2c_function_clock[self as usize]).sclk { + I2cFunctionClockSclk::Xtal => request_xtal_clk(clocks), + I2cFunctionClockSclk::RcFast => request_rc_fast_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.i2c_function_clock_refcount[self as usize]) + { + trace!("Disabling {:?}::FUNCTION_CLOCK", self); + self.enable_function_clock_impl(clocks, false); + match unwrap!(clocks.i2c_function_clock[self as usize]).sclk { + I2cFunctionClockSclk::Xtal => release_xtal_clk(clocks), + I2cFunctionClockSclk::RcFast => release_rc_fast_clk(clocks), + } + } + } + #[allow(unused_variables)] + pub fn function_clock_config_frequency( + self, + clocks: &mut ClockTree, + config: I2cFunctionClockConfig, + ) -> u32 { + (match config.sclk { + I2cFunctionClockSclk::Xtal => xtal_clk_frequency(), + I2cFunctionClockSclk::RcFast => rc_fast_clk_frequency(), + } / (config.div_num() + 1)) + } + pub fn function_clock_frequency(self) -> u32 { + I2C_FUNCTION_CLOCK_FREQ_CACHE[self as usize] + .load(::core::sync::atomic::Ordering::Acquire) + } + } impl ParlIoInstance { pub fn configure_rx_clock( self, @@ -3123,6 +3266,9 @@ macro_rules! define_clock_tree_types { refresh_hp_root_clk_downstream(clocks); refresh_lp_fast_clk_downstream(clocks); refresh_crypto_clk_downstream(clocks); + for child_instance in [I2cInstance::I2c0] { + refresh_i2c_function_clock_downstream(clocks, child_instance); + } for child_instance in [ParlIoInstance::ParlIo] { refresh_parl_io_rx_clock_downstream(clocks, child_instance); refresh_parl_io_tx_clock_downstream(clocks, child_instance); @@ -3205,6 +3351,14 @@ macro_rules! define_clock_tree_types { ); } } + 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( + instance.function_clock_config_frequency(clocks, config), + ::core::sync::atomic::Ordering::Release, + ); + } + } fn refresh_parl_io_rx_clock_downstream(clocks: &mut ClockTree, instance: ParlIoInstance) { if let Some(config) = clocks.parl_io_rx_clock[instance as usize] { PARL_IO_RX_CLOCK_FREQ_CACHE[instance as usize].store( diff --git a/esp-metadata-generated/src/_generated_esp32c6.rs b/esp-metadata-generated/src/_generated_esp32c6.rs index 7a33641d27a..94dd6e2e4b5 100644 --- a/esp-metadata-generated/src/_generated_esp32c6.rs +++ b/esp-metadata-generated/src/_generated_esp32c6.rs @@ -403,6 +403,9 @@ macro_rules! property { ("clock_tree.uart.baud_rate_generator.integral") => { (0, 4095) }; + ("clock_tree.i2c.function_clock.div_num") => { + (0, 255) + }; ("spi_master.version") => { 3 }; @@ -1243,6 +1246,22 @@ macro_rules! for_each_sha_algorithm { /// todo!() /// } /// +/// impl I2cInstance { +/// // I2C_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: I2cFunctionClockConfig, +/// ) { +/// todo!() +/// } +/// } /// impl McpwmInstance { /// // MCPWM_FUNCTION_CLOCK /// @@ -1371,6 +1390,11 @@ macro_rules! for_each_sha_algorithm { /// ``` macro_rules! define_clock_tree_types { () => { + #[derive(Clone, Copy, PartialEq, Eq, Debug)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum I2cInstance { + I2c0 = 0, + } #[derive(Clone, Copy, PartialEq, Eq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum McpwmInstance { @@ -1811,6 +1835,45 @@ macro_rules! define_clock_tree_types { /// Selects `XTAL32K_CLK`. Xtal32kClk, } + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum I2cFunctionClockSclk { + #[default] + /// Selects `XTAL_CLK`. + Xtal, + /// Selects `RC_FAST_CLK`. + RcFast, + } + /// Configures the `I2C0_FUNCTION_CLOCK` clock node. + /// + /// The output is calculated as `OUTPUT = sclk / (div_num + 1)`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct I2cFunctionClockConfig { + sclk: I2cFunctionClockSclk, + div_num: u32, + } + impl I2cFunctionClockConfig { + /// Creates a new configuration for the FUNCTION_CLOCK clock node. + /// + /// ## Panics + /// + /// Panics if the div_num value is outside the + /// valid range (0 ..= 255). + pub const fn new(sclk: I2cFunctionClockSclk, div_num: u32) -> Self { + ::core::assert!( + div_num <= 255, + "`I2C0_FUNCTION_CLOCK` div_num must be between 0 and 255 (inclusive)." + ); + Self { sclk, div_num } + } + fn sclk(self) -> I2cFunctionClockSclk { + self.sclk + } + fn div_num(self) -> u32 { + self.div_num as u32 + } + } /// The list of clock signals that the `MCPWM0_FUNCTION_CLOCK` multiplexer can output. #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -1984,6 +2047,7 @@ macro_rules! define_clock_tree_types { lp_fast_clk: Option, lp_slow_clk: Option, timg_calibration_clock: Option, + i2c_function_clock: [Option; 1], mcpwm_function_clock: [Option; 1], parl_io_rx_clock: [Option; 1], parl_io_tx_clock: [Option; 1], @@ -2004,6 +2068,7 @@ macro_rules! define_clock_tree_types { ledc_sclk_refcount: u32, lp_fast_clk_refcount: u32, timg_calibration_clock_refcount: u32, + i2c_function_clock_refcount: [u32; 1], mcpwm_function_clock_refcount: [u32; 1], parl_io_rx_clock_refcount: [u32; 1], parl_io_tx_clock_refcount: [u32; 1], @@ -2086,6 +2151,10 @@ macro_rules! define_clock_tree_types { pub fn timg_calibration_clock(&self) -> Option { self.timg_calibration_clock } + /// 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] + } /// Returns the current configuration of the MCPWM0_FUNCTION_CLOCK clock tree node pub fn mcpwm0_function_clock(&self) -> Option { self.mcpwm_function_clock[McpwmInstance::Mcpwm0 as usize] @@ -2154,6 +2223,7 @@ macro_rules! define_clock_tree_types { lp_fast_clk: None, lp_slow_clk: None, timg_calibration_clock: None, + i2c_function_clock: [None; 1], mcpwm_function_clock: [None; 1], parl_io_rx_clock: [None; 1], parl_io_tx_clock: [None; 1], @@ -2174,6 +2244,7 @@ macro_rules! define_clock_tree_types { ledc_sclk_refcount: 0, lp_fast_clk_refcount: 0, timg_calibration_clock_refcount: 0, + i2c_function_clock_refcount: [0; 1], mcpwm_function_clock_refcount: [0; 1], parl_io_rx_clock_refcount: [0; 1], parl_io_tx_clock_refcount: [0; 1], @@ -2195,6 +2266,8 @@ macro_rules! define_clock_tree_types { ::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] = + [const { ::core::sync::atomic::AtomicU32::new(0) }; 1]; static MCPWM_FUNCTION_CLOCK_FREQ_CACHE: [::core::sync::atomic::AtomicU32; 1] = [const { ::core::sync::atomic::AtomicU32::new(0) }; 1]; static PARL_IO_RX_CLOCK_FREQ_CACHE: [::core::sync::atomic::AtomicU32; 1] = @@ -3080,6 +3153,76 @@ macro_rules! define_clock_tree_types { pub fn timg_calibration_clock_frequency() -> u32 { TIMG_CALIBRATION_CLOCK_FREQ_CACHE.load(::core::sync::atomic::Ordering::Acquire) } + impl I2cInstance { + pub fn configure_function_clock( + self, + clocks: &mut ClockTree, + config: I2cFunctionClockConfig, + ) { + let old_config = clocks.i2c_function_clock[self as usize].replace(config); + refresh_i2c_function_clock_downstream(clocks, self); + if clocks.i2c_function_clock_refcount[self as usize] > 0 { + match config.sclk { + I2cFunctionClockSclk::Xtal => request_xtal_clk(clocks), + I2cFunctionClockSclk::RcFast => request_rc_fast_clk(clocks), + } + self.configure_function_clock_impl(clocks, old_config, config); + if let Some(old_config) = old_config { + match old_config.sclk { + I2cFunctionClockSclk::Xtal => release_xtal_clk(clocks), + I2cFunctionClockSclk::RcFast => release_rc_fast_clk(clocks), + } + } + } else { + self.configure_function_clock_impl(clocks, old_config, config); + } + } + pub fn function_clock_config( + self, + clocks: &mut ClockTree, + ) -> Option { + clocks.i2c_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.i2c_function_clock_refcount[self as usize]) + { + trace!("Enabling {:?}::FUNCTION_CLOCK", self); + match unwrap!(clocks.i2c_function_clock[self as usize]).sclk { + I2cFunctionClockSclk::Xtal => request_xtal_clk(clocks), + I2cFunctionClockSclk::RcFast => request_rc_fast_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.i2c_function_clock_refcount[self as usize]) + { + trace!("Disabling {:?}::FUNCTION_CLOCK", self); + self.enable_function_clock_impl(clocks, false); + match unwrap!(clocks.i2c_function_clock[self as usize]).sclk { + I2cFunctionClockSclk::Xtal => release_xtal_clk(clocks), + I2cFunctionClockSclk::RcFast => release_rc_fast_clk(clocks), + } + } + } + #[allow(unused_variables)] + pub fn function_clock_config_frequency( + self, + clocks: &mut ClockTree, + config: I2cFunctionClockConfig, + ) -> u32 { + (match config.sclk { + I2cFunctionClockSclk::Xtal => xtal_clk_frequency(), + I2cFunctionClockSclk::RcFast => rc_fast_clk_frequency(), + } / (config.div_num() + 1)) + } + pub fn function_clock_frequency(self) -> u32 { + I2C_FUNCTION_CLOCK_FREQ_CACHE[self as usize] + .load(::core::sync::atomic::Ordering::Acquire) + } + } impl McpwmInstance { pub fn configure_function_clock( self, @@ -3731,6 +3874,9 @@ macro_rules! define_clock_tree_types { refresh_soc_root_clk_downstream(clocks); refresh_ledc_sclk_downstream(clocks); refresh_lp_fast_clk_downstream(clocks); + for child_instance in [I2cInstance::I2c0] { + refresh_i2c_function_clock_downstream(clocks, child_instance); + } for child_instance in [McpwmInstance::Mcpwm0] { refresh_mcpwm_function_clock_downstream(clocks, child_instance); } @@ -3791,6 +3937,14 @@ macro_rules! define_clock_tree_types { ); } } + 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( + instance.function_clock_config_frequency(clocks, config), + ::core::sync::atomic::Ordering::Release, + ); + } + } fn refresh_mcpwm_function_clock_downstream( clocks: &mut ClockTree, instance: McpwmInstance, diff --git a/esp-metadata-generated/src/_generated_esp32c61.rs b/esp-metadata-generated/src/_generated_esp32c61.rs index 112704308ee..58a8871fe67 100644 --- a/esp-metadata-generated/src/_generated_esp32c61.rs +++ b/esp-metadata-generated/src/_generated_esp32c61.rs @@ -337,6 +337,9 @@ macro_rules! property { ("clock_tree.uart.baud_rate_generator.integral") => { (0, 4095) }; + ("clock_tree.i2c.function_clock.div_num") => { + (0, 255) + }; ("spi_master.version") => { 3 }; @@ -853,6 +856,22 @@ macro_rules! for_each_sha_algorithm { /// todo!() /// } /// +/// impl I2cInstance { +/// // I2C_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: I2cFunctionClockConfig, +/// ) { +/// todo!() +/// } +/// } /// impl TimgInstance { /// // TIMG_FUNCTION_CLOCK /// @@ -918,6 +937,11 @@ macro_rules! for_each_sha_algorithm { /// ``` macro_rules! define_clock_tree_types { () => { + #[derive(Clone, Copy, PartialEq, Eq, Debug)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum I2cInstance { + I2c0 = 0, + } #[derive(Clone, Copy, PartialEq, Eq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum TimgInstance { @@ -1068,6 +1092,45 @@ macro_rules! define_clock_tree_types { /// Selects `XTAL32K_CLK`. Xtal32kClk, } + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum I2cFunctionClockSclk { + #[default] + /// Selects `XTAL_CLK`. + Xtal, + /// Selects `RC_FAST_CLK`. + RcFast, + } + /// Configures the `I2C0_FUNCTION_CLOCK` clock node. + /// + /// The output is calculated as `OUTPUT = sclk / (div_num + 1)`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct I2cFunctionClockConfig { + sclk: I2cFunctionClockSclk, + div_num: u32, + } + impl I2cFunctionClockConfig { + /// Creates a new configuration for the FUNCTION_CLOCK clock node. + /// + /// ## Panics + /// + /// Panics if the div_num value is outside the + /// valid range (0 ..= 255). + pub const fn new(sclk: I2cFunctionClockSclk, div_num: u32) -> Self { + ::core::assert!( + div_num <= 255, + "`I2C0_FUNCTION_CLOCK` div_num must be between 0 and 255 (inclusive)." + ); + Self { sclk, div_num } + } + fn sclk(self) -> I2cFunctionClockSclk { + self.sclk + } + fn div_num(self) -> u32 { + self.div_num as u32 + } + } /// 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))] @@ -1184,6 +1247,7 @@ macro_rules! define_clock_tree_types { lp_fast_clk: Option, lp_slow_clk: Option, timg_calibration_clock: Option, + i2c_function_clock: [Option; 1], timg_function_clock: [Option; 2], timg_wdt_clock: [Option; 2], uart_function_clock: [Option; 2], @@ -1205,6 +1269,7 @@ macro_rules! define_clock_tree_types { lp_fast_clk_refcount: u32, lp_slow_clk_refcount: u32, timg_calibration_clock_refcount: u32, + i2c_function_clock_refcount: [u32; 1], timg_function_clock_refcount: [u32; 2], timg_wdt_clock_refcount: [u32; 2], uart_function_clock_refcount: [u32; 2], @@ -1247,6 +1312,10 @@ macro_rules! define_clock_tree_types { pub fn timg_calibration_clock(&self) -> Option { self.timg_calibration_clock } + /// 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] + } /// 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] @@ -1290,6 +1359,7 @@ macro_rules! define_clock_tree_types { lp_fast_clk: None, lp_slow_clk: None, timg_calibration_clock: None, + i2c_function_clock: [None; 1], timg_function_clock: [None; 2], timg_wdt_clock: [None; 2], uart_function_clock: [None; 2], @@ -1311,6 +1381,7 @@ macro_rules! define_clock_tree_types { lp_fast_clk_refcount: 0, lp_slow_clk_refcount: 0, timg_calibration_clock_refcount: 0, + i2c_function_clock_refcount: [0; 1], timg_function_clock_refcount: [0; 2], timg_wdt_clock_refcount: [0; 2], uart_function_clock_refcount: [0; 2], @@ -1332,6 +1403,8 @@ macro_rules! define_clock_tree_types { ::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] = + [const { ::core::sync::atomic::AtomicU32::new(0) }; 1]; static TIMG_FUNCTION_CLOCK_FREQ_CACHE: [::core::sync::atomic::AtomicU32; 2] = [const { ::core::sync::atomic::AtomicU32::new(0) }; 2]; static TIMG_WDT_CLOCK_FREQ_CACHE: [::core::sync::atomic::AtomicU32; 2] = @@ -1935,6 +2008,76 @@ macro_rules! define_clock_tree_types { pub fn timg_calibration_clock_frequency() -> u32 { TIMG_CALIBRATION_CLOCK_FREQ_CACHE.load(::core::sync::atomic::Ordering::Acquire) } + impl I2cInstance { + pub fn configure_function_clock( + self, + clocks: &mut ClockTree, + config: I2cFunctionClockConfig, + ) { + let old_config = clocks.i2c_function_clock[self as usize].replace(config); + refresh_i2c_function_clock_downstream(clocks, self); + if clocks.i2c_function_clock_refcount[self as usize] > 0 { + match config.sclk { + I2cFunctionClockSclk::Xtal => request_xtal_clk(clocks), + I2cFunctionClockSclk::RcFast => request_rc_fast_clk(clocks), + } + self.configure_function_clock_impl(clocks, old_config, config); + if let Some(old_config) = old_config { + match old_config.sclk { + I2cFunctionClockSclk::Xtal => release_xtal_clk(clocks), + I2cFunctionClockSclk::RcFast => release_rc_fast_clk(clocks), + } + } + } else { + self.configure_function_clock_impl(clocks, old_config, config); + } + } + pub fn function_clock_config( + self, + clocks: &mut ClockTree, + ) -> Option { + clocks.i2c_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.i2c_function_clock_refcount[self as usize]) + { + trace!("Enabling {:?}::FUNCTION_CLOCK", self); + match unwrap!(clocks.i2c_function_clock[self as usize]).sclk { + I2cFunctionClockSclk::Xtal => request_xtal_clk(clocks), + I2cFunctionClockSclk::RcFast => request_rc_fast_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.i2c_function_clock_refcount[self as usize]) + { + trace!("Disabling {:?}::FUNCTION_CLOCK", self); + self.enable_function_clock_impl(clocks, false); + match unwrap!(clocks.i2c_function_clock[self as usize]).sclk { + I2cFunctionClockSclk::Xtal => release_xtal_clk(clocks), + I2cFunctionClockSclk::RcFast => release_rc_fast_clk(clocks), + } + } + } + #[allow(unused_variables)] + pub fn function_clock_config_frequency( + self, + clocks: &mut ClockTree, + config: I2cFunctionClockConfig, + ) -> u32 { + (match config.sclk { + I2cFunctionClockSclk::Xtal => xtal_clk_frequency(), + I2cFunctionClockSclk::RcFast => rc_fast_clk_frequency(), + } / (config.div_num() + 1)) + } + pub fn function_clock_frequency(self) -> u32 { + I2C_FUNCTION_CLOCK_FREQ_CACHE[self as usize] + .load(::core::sync::atomic::Ordering::Acquire) + } + } impl TimgInstance { pub fn configure_function_clock( self, @@ -2280,6 +2423,9 @@ macro_rules! define_clock_tree_types { } refresh_hp_root_clk_downstream(clocks); refresh_lp_fast_clk_downstream(clocks); + for child_instance in [I2cInstance::I2c0] { + refresh_i2c_function_clock_downstream(clocks, child_instance); + } for child_instance in [TimgInstance::Timg0, TimgInstance::Timg1] { refresh_timg_function_clock_downstream(clocks, child_instance); refresh_timg_wdt_clock_downstream(clocks, child_instance); @@ -2347,6 +2493,14 @@ macro_rules! define_clock_tree_types { ); } } + 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( + instance.function_clock_config_frequency(clocks, config), + ::core::sync::atomic::Ordering::Release, + ); + } + } 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( diff --git a/esp-metadata-generated/src/_generated_esp32h2.rs b/esp-metadata-generated/src/_generated_esp32h2.rs index 4936fd9467a..b4946614e8e 100644 --- a/esp-metadata-generated/src/_generated_esp32h2.rs +++ b/esp-metadata-generated/src/_generated_esp32h2.rs @@ -400,6 +400,9 @@ macro_rules! property { ("clock_tree.uart.baud_rate_generator.integral") => { (0, 4095) }; + ("clock_tree.i2c.function_clock.div_num") => { + (0, 255) + }; ("spi_master.version") => { 3 }; @@ -1091,6 +1094,22 @@ macro_rules! for_each_sha_algorithm { /// todo!() /// } /// +/// impl I2cInstance { +/// // I2C_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: I2cFunctionClockConfig, +/// ) { +/// todo!() +/// } +/// } /// impl McpwmInstance { /// // MCPWM_FUNCTION_CLOCK /// @@ -1219,6 +1238,12 @@ macro_rules! for_each_sha_algorithm { /// ``` macro_rules! define_clock_tree_types { () => { + #[derive(Clone, Copy, PartialEq, Eq, Debug)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum I2cInstance { + I2c0 = 0, + I2c1 = 1, + } #[derive(Clone, Copy, PartialEq, Eq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum McpwmInstance { @@ -1384,6 +1409,45 @@ macro_rules! define_clock_tree_types { /// Selects `XTAL32K_CLK`. Xtal32kClk, } + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum I2cFunctionClockSclk { + #[default] + /// Selects `XTAL_CLK`. + Xtal, + /// Selects `RC_FAST_CLK`. + RcFast, + } + /// Configures the `I2C0_FUNCTION_CLOCK` clock node. + /// + /// The output is calculated as `OUTPUT = sclk / (div_num + 1)`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct I2cFunctionClockConfig { + sclk: I2cFunctionClockSclk, + div_num: u32, + } + impl I2cFunctionClockConfig { + /// Creates a new configuration for the FUNCTION_CLOCK clock node. + /// + /// ## Panics + /// + /// Panics if the div_num value is outside the + /// valid range (0 ..= 255). + pub const fn new(sclk: I2cFunctionClockSclk, div_num: u32) -> Self { + ::core::assert!( + div_num <= 255, + "`I2C0_FUNCTION_CLOCK` div_num must be between 0 and 255 (inclusive)." + ); + Self { sclk, div_num } + } + fn sclk(self) -> I2cFunctionClockSclk { + self.sclk + } + fn div_num(self) -> u32 { + self.div_num as u32 + } + } /// The list of clock signals that the `MCPWM0_FUNCTION_CLOCK` multiplexer can output. #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -1546,6 +1610,7 @@ macro_rules! define_clock_tree_types { lp_fast_clk: Option, lp_slow_clk: Option, timg_calibration_clock: Option, + i2c_function_clock: [Option; 2], mcpwm_function_clock: [Option; 1], parl_io_rx_clock: [Option; 1], parl_io_tx_clock: [Option; 1], @@ -1562,6 +1627,7 @@ macro_rules! define_clock_tree_types { apb_clk_refcount: u32, lp_fast_clk_refcount: u32, timg_calibration_clock_refcount: u32, + i2c_function_clock_refcount: [u32; 2], mcpwm_function_clock_refcount: [u32; 1], parl_io_rx_clock_refcount: [u32; 1], parl_io_tx_clock_refcount: [u32; 1], @@ -1608,6 +1674,14 @@ macro_rules! define_clock_tree_types { pub fn timg_calibration_clock(&self) -> Option { self.timg_calibration_clock } + /// 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] + } + /// Returns the current configuration of the I2C1_FUNCTION_CLOCK clock tree node + pub fn i2c1_function_clock(&self) -> Option { + self.i2c_function_clock[I2cInstance::I2c1 as usize] + } /// Returns the current configuration of the MCPWM0_FUNCTION_CLOCK clock tree node pub fn mcpwm0_function_clock(&self) -> Option { self.mcpwm_function_clock[McpwmInstance::Mcpwm0 as usize] @@ -1667,6 +1741,7 @@ macro_rules! define_clock_tree_types { lp_fast_clk: None, lp_slow_clk: None, timg_calibration_clock: None, + i2c_function_clock: [None; 2], mcpwm_function_clock: [None; 1], parl_io_rx_clock: [None; 1], parl_io_tx_clock: [None; 1], @@ -1683,6 +1758,7 @@ macro_rules! define_clock_tree_types { apb_clk_refcount: 0, lp_fast_clk_refcount: 0, timg_calibration_clock_refcount: 0, + i2c_function_clock_refcount: [0; 2], mcpwm_function_clock_refcount: [0; 1], parl_io_rx_clock_refcount: [0; 1], parl_io_tx_clock_refcount: [0; 1], @@ -1708,6 +1784,8 @@ macro_rules! define_clock_tree_types { ::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; 2] = + [const { ::core::sync::atomic::AtomicU32::new(0) }; 2]; static MCPWM_FUNCTION_CLOCK_FREQ_CACHE: [::core::sync::atomic::AtomicU32; 1] = [const { ::core::sync::atomic::AtomicU32::new(0) }; 1]; static PARL_IO_RX_CLOCK_FREQ_CACHE: [::core::sync::atomic::AtomicU32; 1] = @@ -2199,6 +2277,76 @@ macro_rules! define_clock_tree_types { pub fn timg_calibration_clock_frequency() -> u32 { TIMG_CALIBRATION_CLOCK_FREQ_CACHE.load(::core::sync::atomic::Ordering::Acquire) } + impl I2cInstance { + pub fn configure_function_clock( + self, + clocks: &mut ClockTree, + config: I2cFunctionClockConfig, + ) { + let old_config = clocks.i2c_function_clock[self as usize].replace(config); + refresh_i2c_function_clock_downstream(clocks, self); + if clocks.i2c_function_clock_refcount[self as usize] > 0 { + match config.sclk { + I2cFunctionClockSclk::Xtal => request_xtal_clk(clocks), + I2cFunctionClockSclk::RcFast => request_rc_fast_clk(clocks), + } + self.configure_function_clock_impl(clocks, old_config, config); + if let Some(old_config) = old_config { + match old_config.sclk { + I2cFunctionClockSclk::Xtal => release_xtal_clk(clocks), + I2cFunctionClockSclk::RcFast => release_rc_fast_clk(clocks), + } + } + } else { + self.configure_function_clock_impl(clocks, old_config, config); + } + } + pub fn function_clock_config( + self, + clocks: &mut ClockTree, + ) -> Option { + clocks.i2c_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.i2c_function_clock_refcount[self as usize]) + { + trace!("Enabling {:?}::FUNCTION_CLOCK", self); + match unwrap!(clocks.i2c_function_clock[self as usize]).sclk { + I2cFunctionClockSclk::Xtal => request_xtal_clk(clocks), + I2cFunctionClockSclk::RcFast => request_rc_fast_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.i2c_function_clock_refcount[self as usize]) + { + trace!("Disabling {:?}::FUNCTION_CLOCK", self); + self.enable_function_clock_impl(clocks, false); + match unwrap!(clocks.i2c_function_clock[self as usize]).sclk { + I2cFunctionClockSclk::Xtal => release_xtal_clk(clocks), + I2cFunctionClockSclk::RcFast => release_rc_fast_clk(clocks), + } + } + } + #[allow(unused_variables)] + pub fn function_clock_config_frequency( + self, + clocks: &mut ClockTree, + config: I2cFunctionClockConfig, + ) -> u32 { + (match config.sclk { + I2cFunctionClockSclk::Xtal => xtal_clk_frequency(), + I2cFunctionClockSclk::RcFast => rc_fast_clk_frequency(), + } / (config.div_num() + 1)) + } + pub fn function_clock_frequency(self) -> u32 { + I2C_FUNCTION_CLOCK_FREQ_CACHE[self as usize] + .load(::core::sync::atomic::Ordering::Acquire) + } + } impl McpwmInstance { pub fn configure_function_clock( self, @@ -2819,6 +2967,9 @@ macro_rules! define_clock_tree_types { } refresh_hp_root_clk_downstream(clocks); refresh_lp_fast_clk_downstream(clocks); + for child_instance in [I2cInstance::I2c0, I2cInstance::I2c1] { + refresh_i2c_function_clock_downstream(clocks, child_instance); + } for child_instance in [McpwmInstance::Mcpwm0] { refresh_mcpwm_function_clock_downstream(clocks, child_instance); } @@ -2897,6 +3048,14 @@ macro_rules! define_clock_tree_types { ); } } + 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( + instance.function_clock_config_frequency(clocks, config), + ::core::sync::atomic::Ordering::Release, + ); + } + } fn refresh_mcpwm_function_clock_downstream( clocks: &mut ClockTree, instance: McpwmInstance, diff --git a/esp-metadata-generated/src/_generated_esp32p4.rs b/esp-metadata-generated/src/_generated_esp32p4.rs index ddfc449be62..ee518d756bf 100644 --- a/esp-metadata-generated/src/_generated_esp32p4.rs +++ b/esp-metadata-generated/src/_generated_esp32p4.rs @@ -334,6 +334,9 @@ macro_rules! property { ("clock_tree.uart.baud_rate_generator.integral") => { (0, 4095) }; + ("clock_tree.i2c.function_clock.div_num") => { + (0, 255) + }; ("spi_master.version") => { 3 }; @@ -1138,6 +1141,22 @@ macro_rules! for_each_sha_algorithm { /// todo!() /// } /// } +/// impl I2cInstance { +/// // I2C_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: I2cFunctionClockConfig, +/// ) { +/// todo!() +/// } +/// } /// ``` macro_rules! define_clock_tree_types { () => { @@ -1156,6 +1175,12 @@ macro_rules! define_clock_tree_types { Uart3 = 3, Uart4 = 4, } + #[derive(Clone, Copy, PartialEq, Eq, Debug)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum I2cInstance { + I2c0 = 0, + I2c1 = 1, + } /// 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))] @@ -1414,6 +1439,45 @@ macro_rules! define_clock_tree_types { self.integral as u32 } } + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum I2cFunctionClockSclk { + #[default] + /// Selects `XTAL_CLK`. + Xtal, + /// Selects `RC_FAST_CLK`. + RcFast, + } + /// Configures the `I2C0_FUNCTION_CLOCK` clock node. + /// + /// The output is calculated as `OUTPUT = sclk / (div_num + 1)`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct I2cFunctionClockConfig { + sclk: I2cFunctionClockSclk, + div_num: u32, + } + impl I2cFunctionClockConfig { + /// Creates a new configuration for the FUNCTION_CLOCK clock node. + /// + /// ## Panics + /// + /// Panics if the div_num value is outside the + /// valid range (0 ..= 255). + pub const fn new(sclk: I2cFunctionClockSclk, div_num: u32) -> Self { + ::core::assert!( + div_num <= 255, + "`I2C0_FUNCTION_CLOCK` div_num must be between 0 and 255 (inclusive)." + ); + Self { sclk, div_num } + } + fn sclk(self) -> I2cFunctionClockSclk { + self.sclk + } + fn div_num(self) -> u32 { + self.div_num as u32 + } + } /// Represents the device's clock tree. pub struct ClockTree { cpu_root_clk: Option, @@ -1428,6 +1492,7 @@ macro_rules! define_clock_tree_types { timg_wdt_clock: [Option; 2], uart_function_clock: [Option; 5], uart_baud_rate_generator: [Option; 5], + i2c_function_clock: [Option; 2], cpll_clk_refcount: u32, spll_clk_refcount: u32, mpll_clk_refcount: u32, @@ -1448,6 +1513,7 @@ macro_rules! define_clock_tree_types { timg_wdt_clock_refcount: [u32; 2], uart_function_clock_refcount: [u32; 5], uart_baud_rate_generator_refcount: [u32; 5], + i2c_function_clock_refcount: [u32; 2], } impl ClockTree { /// Locks the clock tree for exclusive access. @@ -1542,6 +1608,14 @@ macro_rules! define_clock_tree_types { pub fn uart4_baud_rate_generator(&self) -> Option { self.uart_baud_rate_generator[UartInstance::Uart4 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] + } + /// Returns the current configuration of the I2C1_FUNCTION_CLOCK clock tree node + pub fn i2c1_function_clock(&self) -> Option { + self.i2c_function_clock[I2cInstance::I2c1 as usize] + } } static CLOCK_TREE: ::esp_sync::NonReentrantMutex = ::esp_sync::NonReentrantMutex::new(ClockTree { @@ -1557,6 +1631,7 @@ macro_rules! define_clock_tree_types { timg_wdt_clock: [None; 2], uart_function_clock: [None; 5], uart_baud_rate_generator: [None; 5], + i2c_function_clock: [None; 2], cpll_clk_refcount: 0, spll_clk_refcount: 0, mpll_clk_refcount: 0, @@ -1577,6 +1652,7 @@ macro_rules! define_clock_tree_types { timg_wdt_clock_refcount: [0; 2], uart_function_clock_refcount: [0; 5], uart_baud_rate_generator_refcount: [0; 5], + i2c_function_clock_refcount: [0; 2], }); static CPU_ROOT_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = ::core::sync::atomic::AtomicU32::new(0); @@ -1602,6 +1678,8 @@ macro_rules! define_clock_tree_types { [const { ::core::sync::atomic::AtomicU32::new(0) }; 5]; static UART_BAUD_RATE_GENERATOR_FREQ_CACHE: [::core::sync::atomic::AtomicU32; 5] = [const { ::core::sync::atomic::AtomicU32::new(0) }; 5]; + static I2C_FUNCTION_CLOCK_FREQ_CACHE: [::core::sync::atomic::AtomicU32; 2] = + [const { ::core::sync::atomic::AtomicU32::new(0) }; 2]; fn request_xtal_clk(_clocks: &mut ClockTree) {} fn release_xtal_clk(_clocks: &mut ClockTree) {} pub fn xtal_clk_frequency() -> u32 { @@ -2518,6 +2596,76 @@ macro_rules! define_clock_tree_types { .load(::core::sync::atomic::Ordering::Acquire) } } + impl I2cInstance { + pub fn configure_function_clock( + self, + clocks: &mut ClockTree, + config: I2cFunctionClockConfig, + ) { + let old_config = clocks.i2c_function_clock[self as usize].replace(config); + refresh_i2c_function_clock_downstream(clocks, self); + if clocks.i2c_function_clock_refcount[self as usize] > 0 { + match config.sclk { + I2cFunctionClockSclk::Xtal => request_xtal_clk(clocks), + I2cFunctionClockSclk::RcFast => request_rc_fast_clk(clocks), + } + self.configure_function_clock_impl(clocks, old_config, config); + if let Some(old_config) = old_config { + match old_config.sclk { + I2cFunctionClockSclk::Xtal => release_xtal_clk(clocks), + I2cFunctionClockSclk::RcFast => release_rc_fast_clk(clocks), + } + } + } else { + self.configure_function_clock_impl(clocks, old_config, config); + } + } + pub fn function_clock_config( + self, + clocks: &mut ClockTree, + ) -> Option { + clocks.i2c_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.i2c_function_clock_refcount[self as usize]) + { + trace!("Enabling {:?}::FUNCTION_CLOCK", self); + match unwrap!(clocks.i2c_function_clock[self as usize]).sclk { + I2cFunctionClockSclk::Xtal => request_xtal_clk(clocks), + I2cFunctionClockSclk::RcFast => request_rc_fast_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.i2c_function_clock_refcount[self as usize]) + { + trace!("Disabling {:?}::FUNCTION_CLOCK", self); + self.enable_function_clock_impl(clocks, false); + match unwrap!(clocks.i2c_function_clock[self as usize]).sclk { + I2cFunctionClockSclk::Xtal => release_xtal_clk(clocks), + I2cFunctionClockSclk::RcFast => release_rc_fast_clk(clocks), + } + } + } + #[allow(unused_variables)] + pub fn function_clock_config_frequency( + self, + clocks: &mut ClockTree, + config: I2cFunctionClockConfig, + ) -> u32 { + (match config.sclk { + I2cFunctionClockSclk::Xtal => xtal_clk_frequency(), + I2cFunctionClockSclk::RcFast => rc_fast_clk_frequency(), + } / (config.div_num() + 1)) + } + pub fn function_clock_frequency(self) -> u32 { + I2C_FUNCTION_CLOCK_FREQ_CACHE[self as usize] + .load(::core::sync::atomic::Ordering::Acquire) + } + } /// Clock tree configuration. /// /// The fields of this struct are optional, with the following caveats: @@ -2689,6 +2837,14 @@ macro_rules! define_clock_tree_types { ); } } + 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( + instance.function_clock_config_frequency(clocks, config), + ::core::sync::atomic::Ordering::Release, + ); + } + } }; } /// Implement the `Peripheral` enum and enable/disable/reset functions. diff --git a/esp-metadata-generated/src/_generated_esp32s2.rs b/esp-metadata-generated/src/_generated_esp32s2.rs index 8e258db5c01..5b40bfd58e4 100644 --- a/esp-metadata-generated/src/_generated_esp32s2.rs +++ b/esp-metadata-generated/src/_generated_esp32s2.rs @@ -1066,6 +1066,22 @@ macro_rules! for_each_sha_algorithm { /// todo!() /// } /// +/// impl I2cInstance { +/// // I2C_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: I2cFunctionClockConfig, +/// ) { +/// todo!() +/// } +/// } /// impl RmtInstance { /// // RMT_SCLK /// @@ -1147,6 +1163,12 @@ macro_rules! for_each_sha_algorithm { /// ``` macro_rules! define_clock_tree_types { () => { + #[derive(Clone, Copy, PartialEq, Eq, Debug)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum I2cInstance { + I2c0 = 0, + I2c1 = 1, + } #[derive(Clone, Copy, PartialEq, Eq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum RmtInstance { @@ -1427,6 +1449,32 @@ macro_rules! define_clock_tree_types { /// Selects `XTAL32K_CLK`. Xtal32kClk, } + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum I2cFunctionClockSclk { + #[default] + /// Selects `APB_CLK`. + Apb, + /// Selects `REF_TICK`. + RefTick, + } + /// Configures the `I2C0_FUNCTION_CLOCK` clock node. + /// + /// The output is calculated as `OUTPUT = sclk`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct I2cFunctionClockConfig { + sclk: I2cFunctionClockSclk, + } + impl I2cFunctionClockConfig { + /// Creates a new configuration for the FUNCTION_CLOCK clock node. + pub const fn new(sclk: I2cFunctionClockSclk) -> Self { + Self { sclk } + } + fn sclk(self) -> I2cFunctionClockSclk { + self.sclk + } + } /// The list of clock signals that the `RMT_SCLK` multiplexer can output. #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -1544,6 +1592,7 @@ macro_rules! define_clock_tree_types { rtc_slow_clk: Option, rtc_fast_clk: Option, timg_calibration_clock: Option, + i2c_function_clock: [Option; 2], rmt_sclk: [Option; 1], timg_function_clock: [Option; 2], uart_function_clock: [Option; 2], @@ -1558,6 +1607,7 @@ macro_rules! define_clock_tree_types { rtc_fast_clk_refcount: u32, uart_mem_clk_refcount: u32, timg_calibration_clock_refcount: u32, + i2c_function_clock_refcount: [u32; 2], rmt_sclk_refcount: [u32; 1], timg_function_clock_refcount: [u32; 2], uart_function_clock_refcount: [u32; 2], @@ -1629,6 +1679,14 @@ macro_rules! define_clock_tree_types { pub fn timg_calibration_clock(&self) -> Option { self.timg_calibration_clock } + /// 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] + } + /// Returns the current configuration of the I2C1_FUNCTION_CLOCK clock tree node + pub fn i2c1_function_clock(&self) -> Option { + self.i2c_function_clock[I2cInstance::I2c1 as usize] + } /// Returns the current configuration of the RMT_SCLK clock tree node pub fn rmt_sclk(&self) -> Option { self.rmt_sclk[RmtInstance::Rmt as usize] @@ -1683,6 +1741,7 @@ macro_rules! define_clock_tree_types { rtc_slow_clk: None, rtc_fast_clk: None, timg_calibration_clock: None, + i2c_function_clock: [None; 2], rmt_sclk: [None; 1], timg_function_clock: [None; 2], uart_function_clock: [None; 2], @@ -1697,6 +1756,7 @@ macro_rules! define_clock_tree_types { rtc_fast_clk_refcount: 0, uart_mem_clk_refcount: 0, timg_calibration_clock_refcount: 0, + i2c_function_clock_refcount: [0; 2], rmt_sclk_refcount: [0; 1], timg_function_clock_refcount: [0; 2], uart_function_clock_refcount: [0; 2], @@ -1735,6 +1795,8 @@ macro_rules! define_clock_tree_types { ::core::sync::atomic::AtomicU32::new(0); static REF_TICK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = ::core::sync::atomic::AtomicU32::new(0); + static I2C_FUNCTION_CLOCK_FREQ_CACHE: [::core::sync::atomic::AtomicU32; 2] = + [const { ::core::sync::atomic::AtomicU32::new(0) }; 2]; static RMT_SCLK_FREQ_CACHE: [::core::sync::atomic::AtomicU32; 1] = [const { ::core::sync::atomic::AtomicU32::new(0) }; 1]; static TIMG_FUNCTION_CLOCK_FREQ_CACHE: [::core::sync::atomic::AtomicU32; 2] = @@ -2545,6 +2607,76 @@ macro_rules! define_clock_tree_types { pub fn timg_calibration_clock_frequency() -> u32 { TIMG_CALIBRATION_CLOCK_FREQ_CACHE.load(::core::sync::atomic::Ordering::Acquire) } + impl I2cInstance { + pub fn configure_function_clock( + self, + clocks: &mut ClockTree, + config: I2cFunctionClockConfig, + ) { + let old_config = clocks.i2c_function_clock[self as usize].replace(config); + refresh_i2c_function_clock_downstream(clocks, self); + if clocks.i2c_function_clock_refcount[self as usize] > 0 { + match config.sclk { + I2cFunctionClockSclk::Apb => request_apb_clk(clocks), + I2cFunctionClockSclk::RefTick => request_ref_tick(clocks), + } + self.configure_function_clock_impl(clocks, old_config, config); + if let Some(old_config) = old_config { + match old_config.sclk { + I2cFunctionClockSclk::Apb => release_apb_clk(clocks), + I2cFunctionClockSclk::RefTick => release_ref_tick(clocks), + } + } + } else { + self.configure_function_clock_impl(clocks, old_config, config); + } + } + pub fn function_clock_config( + self, + clocks: &mut ClockTree, + ) -> Option { + clocks.i2c_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.i2c_function_clock_refcount[self as usize]) + { + trace!("Enabling {:?}::FUNCTION_CLOCK", self); + match unwrap!(clocks.i2c_function_clock[self as usize]).sclk { + I2cFunctionClockSclk::Apb => request_apb_clk(clocks), + I2cFunctionClockSclk::RefTick => request_ref_tick(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.i2c_function_clock_refcount[self as usize]) + { + trace!("Disabling {:?}::FUNCTION_CLOCK", self); + self.enable_function_clock_impl(clocks, false); + match unwrap!(clocks.i2c_function_clock[self as usize]).sclk { + I2cFunctionClockSclk::Apb => release_apb_clk(clocks), + I2cFunctionClockSclk::RefTick => release_ref_tick(clocks), + } + } + } + #[allow(unused_variables)] + pub fn function_clock_config_frequency( + self, + clocks: &mut ClockTree, + config: I2cFunctionClockConfig, + ) -> u32 { + match config.sclk { + I2cFunctionClockSclk::Apb => apb_clk_frequency(), + I2cFunctionClockSclk::RefTick => ref_tick_frequency(), + } + } + pub fn function_clock_frequency(self) -> u32 { + I2C_FUNCTION_CLOCK_FREQ_CACHE[self as usize] + .load(::core::sync::atomic::Ordering::Acquire) + } + } impl RmtInstance { pub fn configure_sclk(self, clocks: &mut ClockTree, new_selector: RmtSclkConfig) { let old_selector = clocks.rmt_sclk[self as usize].replace(new_selector); @@ -3044,6 +3176,9 @@ macro_rules! define_clock_tree_types { ::core::sync::atomic::Ordering::Release, ); } + for child_instance in [I2cInstance::I2c0, I2cInstance::I2c1] { + refresh_i2c_function_clock_downstream(clocks, child_instance); + } for child_instance in [RmtInstance::Rmt] { refresh_rmt_sclk_downstream(clocks, child_instance); } @@ -3061,6 +3196,9 @@ macro_rules! define_clock_tree_types { ::core::sync::atomic::Ordering::Release, ); } + for child_instance in [I2cInstance::I2c0, I2cInstance::I2c1] { + refresh_i2c_function_clock_downstream(clocks, child_instance); + } for child_instance in [RmtInstance::Rmt] { refresh_rmt_sclk_downstream(clocks, child_instance); } @@ -3068,6 +3206,14 @@ macro_rules! define_clock_tree_types { refresh_uart_function_clock_downstream(clocks, child_instance); } } + 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( + instance.function_clock_config_frequency(clocks, config), + ::core::sync::atomic::Ordering::Release, + ); + } + } 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( diff --git a/esp-metadata-generated/src/_generated_esp32s3.rs b/esp-metadata-generated/src/_generated_esp32s3.rs index 875457de944..4536c70ddb6 100644 --- a/esp-metadata-generated/src/_generated_esp32s3.rs +++ b/esp-metadata-generated/src/_generated_esp32s3.rs @@ -382,6 +382,9 @@ macro_rules! property { ("clock_tree.uart.baud_rate_generator.integral") => { (0, 4095) }; + ("clock_tree.i2c.function_clock.div_num") => { + (0, 255) + }; ("spi_master.version") => { 3 }; @@ -1080,6 +1083,22 @@ macro_rules! for_each_sha_algorithm { /// todo!() /// } /// +/// impl I2cInstance { +/// // I2C_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: I2cFunctionClockConfig, +/// ) { +/// todo!() +/// } +/// } /// impl McpwmInstance { /// // MCPWM_FUNCTION_CLOCK /// @@ -1177,6 +1196,12 @@ macro_rules! for_each_sha_algorithm { /// ``` macro_rules! define_clock_tree_types { () => { + #[derive(Clone, Copy, PartialEq, Eq, Debug)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum I2cInstance { + I2c0 = 0, + I2c1 = 1, + } #[derive(Clone, Copy, PartialEq, Eq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum McpwmInstance { @@ -1386,6 +1411,45 @@ macro_rules! define_clock_tree_types { /// Selects `XTAL32K_CLK`. Xtal32kClk, } + #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub enum I2cFunctionClockSclk { + #[default] + /// Selects `XTAL_CLK`. + Xtal, + /// Selects `RC_FAST_CLK`. + RcFast, + } + /// Configures the `I2C0_FUNCTION_CLOCK` clock node. + /// + /// The output is calculated as `OUTPUT = sclk / (div_num + 1)`. + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct I2cFunctionClockConfig { + sclk: I2cFunctionClockSclk, + div_num: u32, + } + impl I2cFunctionClockConfig { + /// Creates a new configuration for the FUNCTION_CLOCK clock node. + /// + /// ## Panics + /// + /// Panics if the div_num value is outside the + /// valid range (0 ..= 255). + pub const fn new(sclk: I2cFunctionClockSclk, div_num: u32) -> Self { + ::core::assert!( + div_num <= 255, + "`I2C0_FUNCTION_CLOCK` div_num must be between 0 and 255 (inclusive)." + ); + Self { sclk, div_num } + } + fn sclk(self) -> I2cFunctionClockSclk { + self.sclk + } + fn div_num(self) -> u32 { + self.div_num as u32 + } + } /// The list of clock signals that the `MCPWM0_FUNCTION_CLOCK` multiplexer can output. #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -1525,6 +1589,7 @@ macro_rules! define_clock_tree_types { rtc_fast_clk: Option, low_power_clk: Option, timg_calibration_clock: Option, + i2c_function_clock: [Option; 2], mcpwm_function_clock: [Option; 2], rmt_sclk: [Option; 1], timg_function_clock: [Option; 2], @@ -1543,6 +1608,7 @@ macro_rules! define_clock_tree_types { low_power_clk_refcount: u32, uart_mem_clk_refcount: u32, timg_calibration_clock_refcount: u32, + i2c_function_clock_refcount: [u32; 2], mcpwm_function_clock_refcount: [u32; 2], rmt_sclk_refcount: [u32; 1], timg_function_clock_refcount: [u32; 2], @@ -1607,6 +1673,14 @@ macro_rules! define_clock_tree_types { pub fn timg_calibration_clock(&self) -> Option { self.timg_calibration_clock } + /// 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] + } + /// Returns the current configuration of the I2C1_FUNCTION_CLOCK clock tree node + pub fn i2c1_function_clock(&self) -> Option { + self.i2c_function_clock[I2cInstance::I2c1 as usize] + } /// Returns the current configuration of the MCPWM0_FUNCTION_CLOCK clock tree node pub fn mcpwm0_function_clock(&self) -> Option { self.mcpwm_function_clock[McpwmInstance::Mcpwm0 as usize] @@ -1679,6 +1753,7 @@ macro_rules! define_clock_tree_types { rtc_fast_clk: None, low_power_clk: None, timg_calibration_clock: None, + i2c_function_clock: [None; 2], mcpwm_function_clock: [None; 2], rmt_sclk: [None; 1], timg_function_clock: [None; 2], @@ -1697,6 +1772,7 @@ macro_rules! define_clock_tree_types { low_power_clk_refcount: 0, uart_mem_clk_refcount: 0, timg_calibration_clock_refcount: 0, + i2c_function_clock_refcount: [0; 2], mcpwm_function_clock_refcount: [0; 2], rmt_sclk_refcount: [0; 1], timg_function_clock_refcount: [0; 2], @@ -1726,6 +1802,8 @@ macro_rules! define_clock_tree_types { ::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; 2] = + [const { ::core::sync::atomic::AtomicU32::new(0) }; 2]; static UART_MEM_CLOCK_FREQ_CACHE: [::core::sync::atomic::AtomicU32; 3] = [const { ::core::sync::atomic::AtomicU32::new(0) }; 3]; static APB_CLK_FREQ_CACHE: ::core::sync::atomic::AtomicU32 = @@ -2498,6 +2576,76 @@ macro_rules! define_clock_tree_types { pub fn timg_calibration_clock_frequency() -> u32 { TIMG_CALIBRATION_CLOCK_FREQ_CACHE.load(::core::sync::atomic::Ordering::Acquire) } + impl I2cInstance { + pub fn configure_function_clock( + self, + clocks: &mut ClockTree, + config: I2cFunctionClockConfig, + ) { + let old_config = clocks.i2c_function_clock[self as usize].replace(config); + refresh_i2c_function_clock_downstream(clocks, self); + if clocks.i2c_function_clock_refcount[self as usize] > 0 { + match config.sclk { + I2cFunctionClockSclk::Xtal => request_xtal_clk(clocks), + I2cFunctionClockSclk::RcFast => request_rc_fast_clk(clocks), + } + self.configure_function_clock_impl(clocks, old_config, config); + if let Some(old_config) = old_config { + match old_config.sclk { + I2cFunctionClockSclk::Xtal => release_xtal_clk(clocks), + I2cFunctionClockSclk::RcFast => release_rc_fast_clk(clocks), + } + } + } else { + self.configure_function_clock_impl(clocks, old_config, config); + } + } + pub fn function_clock_config( + self, + clocks: &mut ClockTree, + ) -> Option { + clocks.i2c_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.i2c_function_clock_refcount[self as usize]) + { + trace!("Enabling {:?}::FUNCTION_CLOCK", self); + match unwrap!(clocks.i2c_function_clock[self as usize]).sclk { + I2cFunctionClockSclk::Xtal => request_xtal_clk(clocks), + I2cFunctionClockSclk::RcFast => request_rc_fast_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.i2c_function_clock_refcount[self as usize]) + { + trace!("Disabling {:?}::FUNCTION_CLOCK", self); + self.enable_function_clock_impl(clocks, false); + match unwrap!(clocks.i2c_function_clock[self as usize]).sclk { + I2cFunctionClockSclk::Xtal => release_xtal_clk(clocks), + I2cFunctionClockSclk::RcFast => release_rc_fast_clk(clocks), + } + } + } + #[allow(unused_variables)] + pub fn function_clock_config_frequency( + self, + clocks: &mut ClockTree, + config: I2cFunctionClockConfig, + ) -> u32 { + (match config.sclk { + I2cFunctionClockSclk::Xtal => xtal_clk_frequency(), + I2cFunctionClockSclk::RcFast => rc_fast_clk_frequency(), + } / (config.div_num() + 1)) + } + pub fn function_clock_frequency(self) -> u32 { + I2C_FUNCTION_CLOCK_FREQ_CACHE[self as usize] + .load(::core::sync::atomic::Ordering::Acquire) + } + } impl McpwmInstance { pub fn configure_function_clock( self, @@ -2940,6 +3088,9 @@ macro_rules! define_clock_tree_types { refresh_system_pre_div_in_downstream(clocks); refresh_rtc_fast_clk_downstream(clocks); refresh_low_power_clk_downstream(clocks); + for child_instance in [I2cInstance::I2c0, I2cInstance::I2c1] { + refresh_i2c_function_clock_downstream(clocks, child_instance); + } for child_instance in [ UartInstance::Uart0, UartInstance::Uart1, @@ -3043,6 +3194,14 @@ macro_rules! define_clock_tree_types { ); } } + 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( + instance.function_clock_config_frequency(clocks, config), + ::core::sync::atomic::Ordering::Release, + ); + } + } fn refresh_uart_mem_clock_downstream(clocks: &mut ClockTree, instance: UartInstance) { if let Some(config) = clocks.uart_mem_clock[instance as usize] { UART_MEM_CLOCK_FREQ_CACHE[instance as usize].store( diff --git a/esp-metadata/devices/esp32.toml b/esp-metadata/devices/esp32.toml index 520fff05039..e976ed38fa7 100644 --- a/esp-metadata/devices/esp32.toml +++ b/esp-metadata/devices/esp32.toml @@ -54,8 +54,8 @@ peripherals = [ { name = "GPIO" }, { name = "GPIO_SD" }, { name = "HINF" }, - { name = "I2C0", interrupts = { peri = "I2C_EXT0" }, stable = true }, - { name = "I2C1", interrupts = { peri = "I2C_EXT1" }, stable = true }, + { name = "I2C0", interrupts = { peri = "I2C_EXT0" }, stable = true, clock_group = "I2C" }, + { name = "I2C1", interrupts = { peri = "I2C_EXT1" }, stable = true, clock_group = "I2C" }, { name = "I2S0", interrupts = { peri = "I2S0" }, dma_peripheral = 0 }, { name = "I2S1", interrupts = { peri = "I2S1" }, dma_peripheral = 1 }, { name = "IO_MUX" }, @@ -245,6 +245,11 @@ clocks = { system_clocks = { clock_tree = [ { name = "PLL_F160M", outputs = "PLL_F160M_CLK", default = true } ] } ] }, + { group = "I2C", clocks = [ + { name = "FUNCTION_CLOCK", type = "generic", output = "sclk", params = { sclk = [ + { name = "APB", outputs = "APB_CLK", default = true }, + ] } }, + ] }, { group = "UART", clocks = [ { name = "FUNCTION_CLOCK", type = "generic", output = "sclk", params = { sclk = [ { name = "APB", outputs = "APB_CLK", default = true }, diff --git a/esp-metadata/devices/esp32c2.toml b/esp-metadata/devices/esp32c2.toml index b8754f56f45..89baf51421c 100644 --- a/esp-metadata/devices/esp32c2.toml +++ b/esp-metadata/devices/esp32c2.toml @@ -49,7 +49,7 @@ peripherals = [ { name = "EXTMEM" }, { name = "GPIO" }, { name = "I2C_ANA_MST" }, - { name = "I2C0", interrupts = { peri = "I2C_EXT0" }, stable = true }, + { name = "I2C0", interrupts = { peri = "I2C_EXT0" }, stable = true, clock_group = "I2C" }, { name = "INTERRUPT_CORE0" }, { name = "IO_MUX" }, { name = "LEDC" }, @@ -174,6 +174,12 @@ clocks = { 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..0x1000" } }, ] }, + { group = "I2C", clocks = [ + { name = "FUNCTION_CLOCK", type = "generic", output = "sclk / (div_num + 1)", params = { sclk = [ + { name = "XTAL", outputs = "XTAL_CLK", default = true }, + { name = "RC_FAST", outputs = "RC_FAST_CLK" }, + ], div_num = "0 .. 0x100" } }, + ] }, ] }, peripheral_clocks = { templates = [ # templates { name = "clk_en_template", value = "{{control}}::regs().{{clk_en_register}}().modify(|_, w| w.{{clk_en_field}}().bit(enable));" }, diff --git a/esp-metadata/devices/esp32c3.toml b/esp-metadata/devices/esp32c3.toml index 5aa01f133ed..ece1dac37c5 100644 --- a/esp-metadata/devices/esp32c3.toml +++ b/esp-metadata/devices/esp32c3.toml @@ -54,7 +54,7 @@ peripherals = [ { name = "GPIO_SD" }, { name = "HMAC" }, { name = "I2C_ANA_MST" }, - { name = "I2C0", interrupts = { peri = "I2C_EXT0" }, stable = true }, + { name = "I2C0", interrupts = { peri = "I2C_EXT0" }, stable = true, clock_group = "I2C" }, { name = "I2S0", interrupts = { peri = "I2S0" }, dma_peripheral = 3 }, { name = "INTERRUPT_CORE0" }, { name = "IO_MUX" }, @@ -191,6 +191,12 @@ clocks = { 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..0x1000" } }, ] }, + { group = "I2C", clocks = [ + { name = "FUNCTION_CLOCK", type = "generic", output = "sclk / (div_num + 1)", params = { sclk = [ + { name = "XTAL", outputs = "XTAL_CLK", default = true }, + { name = "RC_FAST", outputs = "RC_FAST_CLK" }, + ], div_num = "0 .. 0x100" } }, + ] }, ] }, peripheral_clocks = { templates = [ # templates { name = "clk_en_template", value = "{{control}}::regs().{{clk_en_register}}().modify(|_, w| w.{{clk_en_field}}().bit(enable));" }, diff --git a/esp-metadata/devices/esp32c5.toml b/esp-metadata/devices/esp32c5.toml index a0f46df2208..40da42d3f74 100644 --- a/esp-metadata/devices/esp32c5.toml +++ b/esp-metadata/devices/esp32c5.toml @@ -57,7 +57,7 @@ peripherals = [ { name = "HP_SYS" }, { name = "HUK" }, { name = "I2C_ANA_MST" }, - { name = "I2C0", interrupts = { peri = "I2C_EXT0" }, stable = true }, + { name = "I2C0", interrupts = { peri = "I2C_EXT0" }, stable = true, clock_group = "I2C" }, { name = "I2S0", interrupts = { peri = "I2S0" }, dma_peripheral = 3 }, { name = "IEEE802154", interrupts = { mac = "ZB_MAC" } }, { name = "INTERRUPT_CORE0" }, @@ -245,6 +245,12 @@ clocks = { system_clocks = { clock_tree = [ { name = "PLL_F240M", outputs = "PLL_F240M", default = true }, ] }, ] }, + { group = "I2C", clocks = [ + { name = "FUNCTION_CLOCK", type = "generic", output = "sclk / (div_num + 1)", params = { sclk = [ + { name = "XTAL", outputs = "XTAL_CLK", default = true }, + { name = "RC_FAST", outputs = "RC_FAST_CLK" }, + ], div_num = "0 .. 0x100" } }, + ] }, ] }, peripheral_clocks = { templates = [ # templates { name = "default_clk_en_template", value = "{{control}}::regs().{{conf_register}}().modify(|_, w| w.{{clk_en_field}}().bit(enable));" }, diff --git a/esp-metadata/devices/esp32c6.toml b/esp-metadata/devices/esp32c6.toml index c39d5bbf9af..3a4c8155453 100644 --- a/esp-metadata/devices/esp32c6.toml +++ b/esp-metadata/devices/esp32c6.toml @@ -59,7 +59,7 @@ peripherals = [ { name = "HP_APM" }, { name = "HP_SYS" }, { name = "I2C_ANA_MST" }, - { name = "I2C0", interrupts = { peri = "I2C_EXT0" }, stable = true }, + { name = "I2C0", interrupts = { peri = "I2C_EXT0" }, stable = true, clock_group = "I2C" }, { name = "I2S0", interrupts = { peri = "I2S0" }, dma_peripheral = 3 }, { name = "IEEE802154", interrupts = { mac = "ZB_MAC" } }, { name = "INTERRUPT_CORE0" }, @@ -255,6 +255,12 @@ clocks = { system_clocks = { clock_tree = [ { name = "PLL_F240M", outputs = "PLL_F240M", default = true }, ] }, ] }, + { group = "I2C", clocks = [ + { name = "FUNCTION_CLOCK", type = "generic", output = "sclk / (div_num + 1)", params = { sclk = [ + { name = "XTAL", outputs = "XTAL_CLK", default = true }, + { name = "RC_FAST", outputs = "RC_FAST_CLK" }, + ], div_num = "0 .. 0x100" } }, + ] }, ] }, peripheral_clocks = { templates = [ # templates { name = "default_clk_en_template", value = "{{control}}::regs().{{conf_register}}().modify(|_, w| w.{{clk_en_field}}().bit(enable));" }, diff --git a/esp-metadata/devices/esp32c61.toml b/esp-metadata/devices/esp32c61.toml index 987a2930d96..9f1574929f3 100644 --- a/esp-metadata/devices/esp32c61.toml +++ b/esp-metadata/devices/esp32c61.toml @@ -50,7 +50,7 @@ peripherals = [ { name = "HP_APM" }, { name = "HP_SYS" }, { name = "I2C_ANA_MST" }, - { name = "I2C0", interrupts = { peri = "I2C_EXT0" }, stable = true }, + { name = "I2C0", interrupts = { peri = "I2C_EXT0" }, stable = true, clock_group = "I2C" }, { name = "I2S0", interrupts = { peri = "I2S0" }, dma_peripheral = 3 }, # { name = "IEEE802154" }, # TODO: add PAC manually later { name = "INTERRUPT_CORE0" }, @@ -188,6 +188,12 @@ clocks = { system_clocks = { clock_tree = [ { name = "PLL_F80M", outputs = "PLL_F80M" }, ] }, ] }, + { group = "I2C", clocks = [ + { name = "FUNCTION_CLOCK", type = "generic", output = "sclk / (div_num + 1)", params = { sclk = [ + { name = "XTAL", outputs = "XTAL_CLK", default = true }, + { name = "RC_FAST", outputs = "RC_FAST_CLK" }, + ], div_num = "0 .. 0x100" } }, + ] }, ] }, peripheral_clocks = { templates = [ { name = "default_clk_en_template", value = "{{control}}::regs().{{conf_register}}().modify(|_, w| w.{{clk_en_field}}().bit(enable));" }, diff --git a/esp-metadata/devices/esp32h2.toml b/esp-metadata/devices/esp32h2.toml index c4e75430d5d..b875d9639d1 100644 --- a/esp-metadata/devices/esp32h2.toml +++ b/esp-metadata/devices/esp32h2.toml @@ -47,8 +47,8 @@ peripherals = [ { name = "HP_APM" }, { name = "HP_SYS" }, { name = "I2C_ANA_MST" }, - { name = "I2C0", interrupts = { peri = "I2C_EXT0" }, stable = true }, - { name = "I2C1", interrupts = { peri = "I2C_EXT1" }, stable = true }, + { name = "I2C0", interrupts = { peri = "I2C_EXT0" }, stable = true, clock_group = "I2C" }, + { name = "I2C1", interrupts = { peri = "I2C_EXT1" }, stable = true, clock_group = "I2C" }, { name = "I2S0", interrupts = { peri = "I2S0" }, dma_peripheral = 3 }, { name = "IEEE802154", interrupts = { mac = "ZB_MAC" } }, { name = "INTERRUPT_CORE0" }, @@ -208,6 +208,12 @@ clocks = { system_clocks = { clock_tree = [ { name = "PLL_F96M", outputs = "PLL_F96M_CLK", default = true }, ] }, ] }, + { group = "I2C", clocks = [ + { name = "FUNCTION_CLOCK", type = "generic", output = "sclk / (div_num + 1)", params = { sclk = [ + { name = "XTAL", outputs = "XTAL_CLK", default = true }, + { name = "RC_FAST", outputs = "RC_FAST_CLK" }, + ], div_num = "0 .. 0x100" } }, + ] }, ] }, peripheral_clocks = { templates = [ # templates { name = "default_clk_en_template", value = "{{control}}::regs().{{conf_register}}().modify(|_, w| w.{{clk_en_field}}().bit(enable));" }, diff --git a/esp-metadata/devices/esp32p4.toml b/esp-metadata/devices/esp32p4.toml index 4f6bb262659..fd72338b291 100644 --- a/esp-metadata/devices/esp32p4.toml +++ b/esp-metadata/devices/esp32p4.toml @@ -149,6 +149,12 @@ clocks = { system_clocks = { clock_tree = [ { name = "RC_FAST_CLK", outputs = "RC_FAST_CLK" }, ] }, ] }, + { group = "I2C", clocks = [ + { name = "FUNCTION_CLOCK", type = "generic", output = "sclk / (div_num + 1)", params = { sclk = [ + { name = "XTAL", outputs = "XTAL_CLK", default = true }, + { name = "RC_FAST", outputs = "RC_FAST_CLK" }, + ], div_num = "0 .. 0x100" } }, + ] }, ] }, peripheral_clocks = { templates = [ # ESP32-P4 peripheral clock and reset gates -- all live in HP_SYS_CLKRST. # Clock enables: SOC_CLK_CTRL0/1/2 + PERI_CLK_CTRL{21,26}. @@ -276,8 +282,8 @@ peripherals = [ { name = "UART4", interrupts = { peri = "UART4" }, clock_group = "UART" }, { name = "SPI2", interrupts = { peri = "SPI2" }, dma_peripheral = 1, stable = true }, { name = "SPI3", interrupts = { peri = "SPI3" }, dma_peripheral = 2, stable = true }, - { name = "I2C0", interrupts = { peri = "I2C0" } }, - { name = "I2C1", interrupts = { peri = "I2C1" } }, + { name = "I2C0", interrupts = { peri = "I2C0" }, clock_group = "I2C" }, + { name = "I2C1", interrupts = { peri = "I2C1" }, clock_group = "I2C" }, { name = "TWAI0", interrupts = { peri = "TWAI0" } }, { name = "TWAI1", interrupts = { peri = "TWAI1" } }, { name = "TWAI2", interrupts = { peri = "TWAI2" } }, diff --git a/esp-metadata/devices/esp32s2.toml b/esp-metadata/devices/esp32s2.toml index 6d86f136437..e8097001118 100644 --- a/esp-metadata/devices/esp32s2.toml +++ b/esp-metadata/devices/esp32s2.toml @@ -52,8 +52,8 @@ peripherals = [ { name = "GPIO_SD" }, { name = "HMAC" }, { name = "I2C_ANA_MST" }, - { name = "I2C0", interrupts = { peri = "I2C_EXT0" }, stable = true }, - { name = "I2C1", interrupts = { peri = "I2C_EXT1" }, stable = true }, + { name = "I2C0", interrupts = { peri = "I2C_EXT0" }, stable = true, clock_group = "I2C" }, + { name = "I2C1", interrupts = { peri = "I2C_EXT1" }, stable = true, clock_group = "I2C" }, { name = "I2S0", interrupts = { peri = "I2S0" }, dma_peripheral = 0 }, { name = "INTERRUPT_CORE0" }, { name = "IO_MUX" }, @@ -197,6 +197,12 @@ clocks = { system_clocks = { clock_tree = [ { name = "APB_CLK", outputs = "APB_CLK", default = true }, ] }, ] }, + { group = "I2C", clocks = [ + { name = "FUNCTION_CLOCK", type = "generic", output = "sclk", params = { sclk = [ + { name = "APB", outputs = "APB_CLK", default = true }, + { name = "REF_TICK", outputs = "REF_TICK" }, + ] } }, + ] }, ] }, peripheral_clocks = { templates = [ # templates { name = "clk_en_template", value = "{{control}}::regs().{{clk_en_register}}().modify(|_, w| w.{{clk_en_field}}().bit(enable));" }, diff --git a/esp-metadata/devices/esp32s3.toml b/esp-metadata/devices/esp32s3.toml index 7dd039bf502..65dd0d28853 100644 --- a/esp-metadata/devices/esp32s3.toml +++ b/esp-metadata/devices/esp32s3.toml @@ -56,8 +56,8 @@ peripherals = [ { name = "GPIO_SD" }, { name = "HMAC" }, { name = "I2C_ANA_MST" }, - { name = "I2C0", interrupts = { peri = "I2C_EXT0" }, stable = true }, - { name = "I2C1", interrupts = { peri = "I2C_EXT1" }, stable = true }, + { name = "I2C0", interrupts = { peri = "I2C_EXT0" }, stable = true, clock_group = "I2C" }, + { name = "I2C1", interrupts = { peri = "I2C_EXT1" }, stable = true, clock_group = "I2C" }, { name = "I2S0", interrupts = { peri = "I2S0" }, dma_peripheral = 3 }, { name = "I2S1", interrupts = { peri = "I2S1" }, dma_peripheral = 4 }, { name = "INTERRUPT_CORE0" }, @@ -215,6 +215,12 @@ clocks = { system_clocks = { clock_tree = [ { name = "BAUD_RATE_GENERATOR", type = "generic", output = "(FUNCTION_CLOCK * 16) / (integral * 16 + fractional)", params = { fractional = "0..16", integral = "0..0x1000" } }, { name = "MEM_CLOCK", type = "generic", output = "UART_MEM_CLK" }, ] }, + { group = "I2C", clocks = [ + { name = "FUNCTION_CLOCK", type = "generic", output = "sclk / (div_num + 1)", params = { sclk = [ + { name = "XTAL", outputs = "XTAL_CLK", default = true }, + { name = "RC_FAST", outputs = "RC_FAST_CLK" }, + ], div_num = "0 .. 0x100" } }, + ] }, ] }, peripheral_clocks = { templates = [ # templates { name = "clk_en_template", value = "{{control}}::regs().{{clk_en_register}}().modify(|_, w| w.{{clk_en_field}}().bit(enable));" }, From 0190a4be5bea42a88ea24653c80c530783260ac1 Mon Sep 17 00:00:00 2001 From: Kirill Mikhailov Date: Wed, 27 May 2026 17:36:17 +0200 Subject: [PATCH 4/7] use clocktree --- esp-hal/src/i2c/master/low_level/mod.rs | 77 +++++++++------ esp-hal/src/i2c/master/low_level/v1.rs | 14 ++- esp-hal/src/i2c/master/low_level/v2.rs | 94 +++++++++--------- esp-hal/src/i2c/master/low_level/v3.rs | 122 ++++++++++++------------ esp-hal/src/i2c/master/mod.rs | 11 ++- 5 files changed, 173 insertions(+), 145 deletions(-) diff --git a/esp-hal/src/i2c/master/low_level/mod.rs b/esp-hal/src/i2c/master/low_level/mod.rs index 69c9a0bfe51..dc043b59974 100644 --- a/esp-hal/src/i2c/master/low_level/mod.rs +++ b/esp-hal/src/i2c/master/low_level/mod.rs @@ -1,4 +1,5 @@ use super::*; +use crate::soc::clocks::{ClockTree, I2cFunctionClockConfig}; #[cfg_attr(i2c_master_version = "1", path = "v1.rs")] #[cfg_attr(i2c_master_version = "2", path = "v2.rs")] @@ -172,11 +173,11 @@ fn set_filter( #[expect(clippy::too_many_arguments)] #[allow(unused)] -/// Configures the clock and timing parameters for the I2C peripheral. +/// Configures the timing parameters for the I2C peripheral. +/// +/// Clock source selection is handled separately via the clock tree. fn configure_clock( info: &Info, - clock_source: ClockSource, - sclk_div: u32, scl_low_period: u32, scl_high_period: u32, scl_wait_high_period: u32, @@ -189,33 +190,6 @@ fn configure_clock( timeout: Option, ) -> Result<(), ConfigError> { unsafe { - cfg_if::cfg_if! { - // sclk_sel: 0 = XTAL, 1 = RcFast - if #[cfg(all(soc_has_pcr, soc_has_i2c1))] { - crate::peripherals::PCR::regs().i2c_sclk_conf(info.id as usize).modify(|_, w| { - w.i2c_sclk_sel().bit(matches!(clock_source, ClockSource::RcFast)); - w.i2c_sclk_div_num().bits((sclk_div - 1) as u8); - w.i2c_sclk_en().set_bit() - }); - } else if #[cfg(soc_has_pcr)] { - crate::peripherals::PCR::regs().i2c_sclk_conf().modify(|_, w| { - w.i2c_sclk_sel().bit(matches!(clock_source, ClockSource::RcFast)); - w.i2c_sclk_div_num().bits((sclk_div - 1) as u8); - w.i2c_sclk_en().set_bit() - }); - } else if #[cfg(i2c_master_version = "3")] { - info.regs().clk_conf().modify(|_, w| { - w.sclk_sel().bit(matches!(clock_source, ClockSource::RcFast)); - w.sclk_div_num().bits((sclk_div - 1) as u8) - }); - } else if #[cfg(i2c_master_version = "2")] { - // ref_always_on: 1 = APB, 0 = REF_TICK - info.regs().ctr().modify(|_, w| { - w.ref_always_on().bit(!matches!(clock_source, ClockSource::RefTick)) - }); - } - } - // scl period info.regs() .scl_low_period() @@ -364,6 +338,15 @@ impl Info { w }); } + + pub(super) fn clock_instance(&self) -> crate::soc::clocks::I2cInstance { + use crate::soc::clocks::I2cInstance; + #[cfg(soc_has_i2c1)] + if self.id == 1 { + return I2cInstance::I2c1; + } + I2cInstance::I2c0 + } } impl PartialEq for Info { @@ -374,6 +357,40 @@ impl PartialEq for Info { unsafe impl Sync for Info {} +#[derive(Debug)] +pub(super) struct I2cClockGuard<'t> { + i2c: AnyI2c<'t>, +} + +impl<'t> I2cClockGuard<'t> { + pub(super) fn new(i2c: AnyI2c<'t>) -> Self { + ClockTree::with(|clocks| { + let clock = i2c.info().clock_instance(); + cfg_if::cfg_if! { + if #[cfg(i2c_master_version = "3")] { + let config = I2cFunctionClockConfig::new(Default::default(), 0); + } else { + let config = I2cFunctionClockConfig::new(Default::default()); + } + } + clock.configure_function_clock(clocks, config); + clock.request_function_clock(clocks); + }); + Self { i2c } + } +} + +impl Drop for I2cClockGuard<'_> { + fn drop(&mut self) { + ClockTree::with(|clocks| { + self.i2c + .info() + .clock_instance() + .release_function_clock(clocks); + }); + } +} + #[derive(Clone, Copy)] enum Deadline { None, diff --git a/esp-hal/src/i2c/master/low_level/v1.rs b/esp-hal/src/i2c/master/low_level/v1.rs index 9f62fb62da9..cd790fe0a16 100644 --- a/esp-hal/src/i2c/master/low_level/v1.rs +++ b/esp-hal/src/i2c/master/low_level/v1.rs @@ -1,14 +1,20 @@ use super::{Config, ConfigError, Driver, RegisterBlock, configure_clock}; +use crate::soc::clocks::{ClockTree, I2cFunctionClockConfig}; /// Sets the frequency of the I2C interface by calculating and applying the /// associated timings - corresponds to i2c_ll_cal_bus_clk and /// i2c_ll_set_bus_timing in ESP-IDF pub(super) fn set_frequency(driver: &Driver<'_>, clock_config: &Config) -> Result<(), ConfigError> { let timeout = clock_config.timeout; - - let source_clk = crate::soc::clocks::apb_clk_frequency(); - let bus_freq = clock_config.frequency.as_hz(); + let sclk = clock_config.clock_source; + let clock = driver.info.clock_instance(); + + // ESP32 I2C is hardwired to APB; configure_function_clock is a no-op. + let source_clk = ClockTree::with(|clocks| { + clock.configure_function_clock(clocks, I2cFunctionClockConfig::new(sclk)); + clock.function_clock_config_frequency(clocks, I2cFunctionClockConfig::new(sclk)) + }); let half_cycle: u32 = source_clk / bus_freq / 2; let scl_low = half_cycle; @@ -59,8 +65,6 @@ pub(super) fn set_frequency(driver: &Driver<'_>, clock_config: &Config) -> Resul configure_clock( driver.info, - clock_config.clock_source, - 0, scl_low_period, scl_high_period, 0, diff --git a/esp-hal/src/i2c/master/low_level/v2.rs b/esp-hal/src/i2c/master/low_level/v2.rs index 3863da754c2..25937d98040 100644 --- a/esp-hal/src/i2c/master/low_level/v2.rs +++ b/esp-hal/src/i2c/master/low_level/v2.rs @@ -1,61 +1,61 @@ -use super::{ClockSource, Config, ConfigError, Driver, RegisterBlock, configure_clock}; +use super::{Config, ConfigError, Driver, RegisterBlock, configure_clock}; +use crate::soc::clocks::{ClockTree, I2cFunctionClockConfig}; /// Sets the frequency of the I2C interface by calculating and applying the /// associated timings - corresponds to i2c_ll_cal_bus_clk and /// i2c_ll_set_bus_timing in ESP-IDF pub(super) fn set_frequency(driver: &Driver<'_>, clock_config: &Config) -> Result<(), ConfigError> { let timeout = clock_config.timeout; - - let source_clk = match clock_config.clock_source { - ClockSource::Apb => crate::soc::clocks::apb_clk_frequency(), - ClockSource::RefTick => 1_000_000, - }; - let bus_freq = clock_config.frequency.as_hz(); + let sclk = clock_config.clock_source; + let clock = driver.info.clock_instance(); + + ClockTree::with(|clocks| -> Result<(), ConfigError> { + let source_clk = + clock.function_clock_config_frequency(clocks, I2cFunctionClockConfig::new(sclk)); - let half_cycle: u32 = source_clk / bus_freq / 2; - // SCL - let scl_low = half_cycle; - // default, scl_wait_high < scl_high - let scl_high = half_cycle / 2 + 2; - let scl_wait_high = half_cycle - scl_high; - let sda_hold = half_cycle / 2; - // scl_wait_high < sda_sample <= scl_high - let sda_sample = half_cycle / 2 - 1; - let setup = half_cycle; - let hold = half_cycle; + let half_cycle: u32 = source_clk / bus_freq / 2; + // SCL + let scl_low = half_cycle; + // default, scl_wait_high < scl_high + let scl_high = half_cycle / 2 + 2; + let scl_wait_high = half_cycle - scl_high; + let sda_hold = half_cycle / 2; + // scl_wait_high < sda_sample <= scl_high + let sda_sample = half_cycle / 2 - 1; + let setup = half_cycle; + let hold = half_cycle; - // scl period - let scl_low_period = scl_low - 1; - let scl_high_period = scl_high; - let scl_wait_high_period = scl_wait_high; - // sda sample - let sda_hold_time = sda_hold; - let sda_sample_time = sda_sample; - // setup - let scl_rstart_setup_time = setup; - let scl_stop_setup_time = setup; - // hold - let scl_start_hold_time = hold - 1; - let scl_stop_hold_time = hold; + // scl period + let scl_low_period = scl_low - 1; + let scl_high_period = scl_high; + let scl_wait_high_period = scl_wait_high; + // sda sample + let sda_hold_time = sda_hold; + let sda_sample_time = sda_sample; + // setup + let scl_rstart_setup_time = setup; + let scl_stop_setup_time = setup; + // hold + let scl_start_hold_time = hold - 1; + let scl_stop_hold_time = hold; - configure_clock( - driver.info, - clock_config.clock_source, - 0, - scl_low_period, - scl_high_period, - scl_wait_high_period, - sda_hold_time, - sda_sample_time, - scl_rstart_setup_time, - scl_stop_setup_time, - scl_start_hold_time, - scl_stop_hold_time, - timeout.apb_cycles(half_cycle)?, - )?; + clock.configure_function_clock(clocks, I2cFunctionClockConfig::new(sclk)); - Ok(()) + configure_clock( + driver.info, + scl_low_period, + scl_high_period, + scl_wait_high_period, + sda_hold_time, + sda_sample_time, + scl_rstart_setup_time, + scl_stop_setup_time, + scl_start_hold_time, + scl_stop_hold_time, + timeout.apb_cycles(half_cycle)?, + ) + }) } /// Resets the transmit and receive FIFO buffers. diff --git a/esp-hal/src/i2c/master/low_level/v3.rs b/esp-hal/src/i2c/master/low_level/v3.rs index 77725b6fed1..bd5b7426d0b 100644 --- a/esp-hal/src/i2c/master/low_level/v3.rs +++ b/esp-hal/src/i2c/master/low_level/v3.rs @@ -1,75 +1,75 @@ -use super::{ClockSource, Config, ConfigError, Driver, RegisterBlock, configure_clock}; +use super::{Config, ConfigError, Driver, RegisterBlock, configure_clock}; +use crate::soc::clocks::{ClockTree, I2cFunctionClockConfig}; /// Sets the frequency of the I2C interface by calculating and applying the /// associated timings - corresponds to i2c_ll_cal_bus_clk and /// i2c_ll_set_bus_timing in ESP-IDF pub(super) fn set_frequency(driver: &Driver<'_>, clock_config: &Config) -> Result<(), ConfigError> { let timeout = clock_config.timeout; - - let source_clk = match clock_config.clock_source { - ClockSource::Xtal => crate::soc::clocks::xtal_clk_frequency(), - ClockSource::RcFast => crate::soc::clocks::rc_fast_clk_frequency(), - }; - let bus_freq = clock_config.frequency.as_hz(); + let sclk = clock_config.clock_source; + let clock = driver.info.clock_instance(); - let clkm_div: u32 = source_clk / (bus_freq * 1024) + 1; - let sclk_freq: u32 = source_clk / clkm_div; - let half_cycle: u32 = sclk_freq / bus_freq / 2; - // SCL - let scl_low = half_cycle; - // default, scl_wait_high < scl_high - // Make 80KHz as a boundary here, because when working at lower frequency, too - // much scl_wait_high will faster the frequency according to some - // hardware behaviors. - let scl_wait_high = if bus_freq >= 80 * 1000 { - half_cycle / 2 - 2 - } else { - half_cycle / 4 - }; - let scl_high = half_cycle - scl_wait_high; - let sda_hold = half_cycle / 4; - let sda_sample = half_cycle / 2; - let setup = half_cycle; - let hold = half_cycle; + ClockTree::with(|clocks| -> Result<(), ConfigError> { + let source_clk = + clock.function_clock_config_frequency(clocks, I2cFunctionClockConfig::new(sclk, 0)); + + let clkm_div: u32 = source_clk / (bus_freq * 1024) + 1; + let sclk_freq: u32 = source_clk / clkm_div; + let half_cycle: u32 = sclk_freq / bus_freq / 2; + // SCL + let scl_low = half_cycle; + // default, scl_wait_high < scl_high + // Make 80KHz as a boundary here, because when working at lower frequency, too + // much scl_wait_high will faster the frequency according to some + // hardware behaviors. + let scl_wait_high = if bus_freq >= 80 * 1000 { + half_cycle / 2 - 2 + } else { + half_cycle / 4 + }; + let scl_high = half_cycle - scl_wait_high; + let sda_hold = half_cycle / 4; + let sda_sample = half_cycle / 2; + let setup = half_cycle; + let hold = half_cycle; - // According to the Technical Reference Manual, the following timings must be - // subtracted by 1. However, according to the practical measurement and - // some hardware behaviour, if wait_high_period and scl_high minus one. - // The SCL frequency would be a little higher than expected. Therefore, the - // solution here is not to minus scl_high as well as scl_wait high, and - // the frequency will be absolutely accurate to all frequency - // to some extent. - let scl_low_period = scl_low - 1; - let scl_high_period = scl_high; - let scl_wait_high_period = scl_wait_high; - // sda sample - let sda_hold_time = sda_hold - 1; - let sda_sample_time = sda_sample - 1; - // setup - let scl_rstart_setup_time = setup - 1; - let scl_stop_setup_time = setup - 1; - // hold - let scl_start_hold_time = hold - 1; - let scl_stop_hold_time = hold - 1; + // According to the Technical Reference Manual, the following timings must be + // subtracted by 1. However, according to the practical measurement and + // some hardware behaviour, if wait_high_period and scl_high minus one. + // The SCL frequency would be a little higher than expected. Therefore, the + // solution here is not to minus scl_high as well as scl_wait high, and + // the frequency will be absolutely accurate to all frequency + // to some extent. + let scl_low_period = scl_low - 1; + let scl_high_period = scl_high; + let scl_wait_high_period = scl_wait_high; + // sda sample + let sda_hold_time = sda_hold - 1; + let sda_sample_time = sda_sample - 1; + // setup + let scl_rstart_setup_time = setup - 1; + let scl_stop_setup_time = setup - 1; + // hold + let scl_start_hold_time = hold - 1; + let scl_stop_hold_time = hold - 1; - configure_clock( - driver.info, - clock_config.clock_source, - clkm_div, - scl_low_period, - scl_high_period, - scl_wait_high_period, - sda_hold_time, - sda_sample_time, - scl_rstart_setup_time, - scl_stop_setup_time, - scl_start_hold_time, - scl_stop_hold_time, - timeout.apb_cycles(half_cycle)?, - )?; + clock.configure_function_clock(clocks, I2cFunctionClockConfig::new(sclk, clkm_div - 1)); - Ok(()) + configure_clock( + driver.info, + scl_low_period, + scl_high_period, + scl_wait_high_period, + sda_hold_time, + sda_sample_time, + scl_rstart_setup_time, + scl_stop_setup_time, + scl_start_hold_time, + scl_stop_hold_time, + timeout.apb_cycles(half_cycle)?, + ) + }) } /// Resets the transmit and receive FIFO buffers. diff --git a/esp-hal/src/i2c/master/mod.rs b/esp-hal/src/i2c/master/mod.rs index c7f7b6b03af..3c206d90feb 100644 --- a/esp-hal/src/i2c/master/mod.rs +++ b/esp-hal/src/i2c/master/mod.rs @@ -148,8 +148,8 @@ use crate::{ mod eh; mod low_level; -use low_level::Driver; pub use low_level::{AnyI2c, Instance}; +use low_level::{Driver, I2cClockGuard}; const I2C_FIFO_SIZE: usize = property!("i2c_master.fifo_size"); // Chunk writes/reads by this size @@ -656,6 +656,7 @@ pub struct I2c<'d, Dm: DriverMode> { i2c: AnyI2c<'d>, phantom: PhantomData, guard: PeripheralGuard, + clock_guard: I2cClockGuard<'d>, config: DriverConfig, } @@ -692,10 +693,14 @@ impl<'d> I2c<'d, Blocking> { let sda_pin = PinGuard::new_unconnected(); let scl_pin = PinGuard::new_unconnected(); + let i2c_any = i2c.degrade(); + let clock_guard = I2cClockGuard::new(unsafe { i2c_any.clone_unchecked() }); + let i2c = I2c { - i2c: i2c.degrade(), + i2c: i2c_any, phantom: PhantomData, guard, + clock_guard, config: DriverConfig { config, sda_pin, @@ -723,6 +728,7 @@ impl<'d> I2c<'d, Blocking> { i2c: self.i2c, phantom: PhantomData, guard: self.guard, + clock_guard: self.clock_guard, config: self.config, } } @@ -815,6 +821,7 @@ impl<'d> I2c<'d, Async> { i2c: self.i2c, phantom: PhantomData, guard: self.guard, + clock_guard: self.clock_guard, config: self.config, } } From e115dfe07b8a74c08918263326b1ccef682a83ce Mon Sep 17 00:00:00 2001 From: Kirill Mikhailov Date: Thu, 28 May 2026 00:49:45 +0200 Subject: [PATCH 5/7] Clock instance -> part of Info fmt --- esp-hal/src/i2c/master/low_level/mod.rs | 28 +++++++++++++++---------- esp-hal/src/i2c/master/low_level/v1.rs | 2 +- esp-hal/src/i2c/master/low_level/v2.rs | 2 +- esp-hal/src/i2c/master/low_level/v3.rs | 2 +- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/esp-hal/src/i2c/master/low_level/mod.rs b/esp-hal/src/i2c/master/low_level/mod.rs index dc043b59974..105fcac2fe3 100644 --- a/esp-hal/src/i2c/master/low_level/mod.rs +++ b/esp-hal/src/i2c/master/low_level/mod.rs @@ -278,6 +278,9 @@ pub struct Info { /// SDA input signal. pub sda_input: InputSignal, + + /// I2C clock group instance. + pub clock_instance: crate::soc::clocks::I2cInstance, } impl Info { @@ -338,15 +341,6 @@ impl Info { w }); } - - pub(super) fn clock_instance(&self) -> crate::soc::clocks::I2cInstance { - use crate::soc::clocks::I2cInstance; - #[cfg(soc_has_i2c1)] - if self.id == 1 { - return I2cInstance::I2c1; - } - I2cInstance::I2c0 - } } impl PartialEq for Info { @@ -365,7 +359,7 @@ pub(super) struct I2cClockGuard<'t> { impl<'t> I2cClockGuard<'t> { pub(super) fn new(i2c: AnyI2c<'t>) -> Self { ClockTree::with(|clocks| { - let clock = i2c.info().clock_instance(); + let clock = i2c.info().clock_instance; cfg_if::cfg_if! { if #[cfg(i2c_master_version = "3")] { let config = I2cFunctionClockConfig::new(Default::default(), 0); @@ -385,7 +379,7 @@ impl Drop for I2cClockGuard<'_> { ClockTree::with(|clocks| { self.i2c .info() - .clock_instance() + .clock_instance .release_function_clock(clocks); }); } @@ -1867,6 +1861,18 @@ for_each_i2c_master!( scl_input: InputSignal::$scl, sda_output: OutputSignal::$sda, sda_input: InputSignal::$sda, + clock_instance: { + cfg_if::cfg_if! { + if #[cfg(soc_has_i2c1)] { + match $id { + 0 => crate::soc::clocks::I2cInstance::I2c0, + _ => crate::soc::clocks::I2cInstance::I2c1, + } + } else { + crate::soc::clocks::I2cInstance::I2c0 + } + } + }, }; (&PERIPHERAL, &STATE) } diff --git a/esp-hal/src/i2c/master/low_level/v1.rs b/esp-hal/src/i2c/master/low_level/v1.rs index cd790fe0a16..25ec5b9946a 100644 --- a/esp-hal/src/i2c/master/low_level/v1.rs +++ b/esp-hal/src/i2c/master/low_level/v1.rs @@ -8,7 +8,7 @@ pub(super) fn set_frequency(driver: &Driver<'_>, clock_config: &Config) -> Resul let timeout = clock_config.timeout; let bus_freq = clock_config.frequency.as_hz(); let sclk = clock_config.clock_source; - let clock = driver.info.clock_instance(); + let clock = driver.info.clock_instance; // ESP32 I2C is hardwired to APB; configure_function_clock is a no-op. let source_clk = ClockTree::with(|clocks| { diff --git a/esp-hal/src/i2c/master/low_level/v2.rs b/esp-hal/src/i2c/master/low_level/v2.rs index 25937d98040..623ebdc2bf1 100644 --- a/esp-hal/src/i2c/master/low_level/v2.rs +++ b/esp-hal/src/i2c/master/low_level/v2.rs @@ -8,7 +8,7 @@ pub(super) fn set_frequency(driver: &Driver<'_>, clock_config: &Config) -> Resul let timeout = clock_config.timeout; let bus_freq = clock_config.frequency.as_hz(); let sclk = clock_config.clock_source; - let clock = driver.info.clock_instance(); + let clock = driver.info.clock_instance; ClockTree::with(|clocks| -> Result<(), ConfigError> { let source_clk = diff --git a/esp-hal/src/i2c/master/low_level/v3.rs b/esp-hal/src/i2c/master/low_level/v3.rs index bd5b7426d0b..158bd4b06c0 100644 --- a/esp-hal/src/i2c/master/low_level/v3.rs +++ b/esp-hal/src/i2c/master/low_level/v3.rs @@ -8,7 +8,7 @@ pub(super) fn set_frequency(driver: &Driver<'_>, clock_config: &Config) -> Resul let timeout = clock_config.timeout; let bus_freq = clock_config.frequency.as_hz(); let sclk = clock_config.clock_source; - let clock = driver.info.clock_instance(); + let clock = driver.info.clock_instance; ClockTree::with(|clocks| -> Result<(), ConfigError> { let source_clk = From 33a7d4ff31c7a49e8850c78791b5aba1ddf06e1b Mon Sep 17 00:00:00 2001 From: Kirill Mikhailov Date: Thu, 28 May 2026 11:18:40 +0200 Subject: [PATCH 6/7] address review, simplify clock instance selection --- esp-hal/src/i2c/master/low_level/mod.rs | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/esp-hal/src/i2c/master/low_level/mod.rs b/esp-hal/src/i2c/master/low_level/mod.rs index 105fcac2fe3..240c01571ad 100644 --- a/esp-hal/src/i2c/master/low_level/mod.rs +++ b/esp-hal/src/i2c/master/low_level/mod.rs @@ -1861,18 +1861,7 @@ for_each_i2c_master!( scl_input: InputSignal::$scl, sda_output: OutputSignal::$sda, sda_input: InputSignal::$sda, - clock_instance: { - cfg_if::cfg_if! { - if #[cfg(soc_has_i2c1)] { - match $id { - 0 => crate::soc::clocks::I2cInstance::I2c0, - _ => crate::soc::clocks::I2cInstance::I2c1, - } - } else { - crate::soc::clocks::I2cInstance::I2c0 - } - } - }, + clock_instance: paste::paste! { crate::soc::clocks::I2cInstance::[] }, }; (&PERIPHERAL, &STATE) } From 9428712bd409882e900b894ca71dea26ad8fa502 Mon Sep 17 00:00:00 2001 From: Kirill Mikhailov Date: Thu, 28 May 2026 12:25:41 +0200 Subject: [PATCH 7/7] Simplify cfg_if --- esp-hal/src/i2c/master/low_level/mod.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/esp-hal/src/i2c/master/low_level/mod.rs b/esp-hal/src/i2c/master/low_level/mod.rs index 240c01571ad..6cbed27e8c2 100644 --- a/esp-hal/src/i2c/master/low_level/mod.rs +++ b/esp-hal/src/i2c/master/low_level/mod.rs @@ -360,13 +360,11 @@ impl<'t> I2cClockGuard<'t> { pub(super) fn new(i2c: AnyI2c<'t>) -> Self { ClockTree::with(|clocks| { let clock = i2c.info().clock_instance; - cfg_if::cfg_if! { - if #[cfg(i2c_master_version = "3")] { - let config = I2cFunctionClockConfig::new(Default::default(), 0); - } else { - let config = I2cFunctionClockConfig::new(Default::default()); - } - } + let config = I2cFunctionClockConfig::new( + Default::default(), + #[cfg(i2c_master_version = "3")] + 0, + ); clock.configure_function_clock(clocks, config); clock.request_function_clock(clocks); });