From 689c3624037d1fbc61e8e01b98df339681de6d39 Mon Sep 17 00:00:00 2001 From: Juraj Sadel Date: Wed, 9 Apr 2025 11:46:16 +0200 Subject: [PATCH] S2 sleep (#3341) * esp32s3 copy for esp32s2 * idf's rtc_sleep_pd * Add the new NRX, FE & FE2 peripherals from the PAC * Went through rtc_sleep_init -> apply * Add all else statements of idf's rtc_sleep_init and made the style match the cpp code more for easier checking * Finish checking/copying base_settings * Checked flags in apply * Allow base_settings * Remove unused variables * Reenable rtc_peri for Ext1WakeupSource * Correct comment. Probably copy-pasted and still present in esp32s3 * Remove TODO comments * Add the esp32s2 sleep work to the changelog * Add esp32s2 to qa-test where applicable * update pacs and update after rebase * changelog * provide missing ROM functions * Update PACs * review comments and cleanup * review * Fix wrong cfg condition --------- Co-authored-by: mennovf --- esp-hal/CHANGELOG.md | 2 + esp-hal/Cargo.toml | 14 +- esp-hal/src/rtc_cntl/mod.rs | 14 +- esp-hal/src/rtc_cntl/sleep/esp32s2.rs | 811 +++++++++++++++++++++++++ esp-hal/src/rtc_cntl/sleep/mod.rs | 59 +- esp-hal/src/soc/esp32s2/peripherals.rs | 4 + esp-hal/src/soc/esp32s2/regi2c.rs | 187 +++++- qa-test/src/bin/sleep_timer.rs | 2 +- qa-test/src/bin/sleep_timer_ext0.rs | 2 +- qa-test/src/bin/sleep_timer_ext1.rs | 2 +- qa-test/src/bin/sleep_timer_rtcio.rs | 9 +- 11 files changed, 1058 insertions(+), 48 deletions(-) create mode 100644 esp-hal/src/rtc_cntl/sleep/esp32s2.rs diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index ffa7e849a..f03787f03 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -16,6 +16,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `ESP_HAL_CONFIG_CRITICAL_SECTION_IMPL` to allow opting out of the default `critical-section` implementation (#3293) - All peripheral singletons (`GpioPin<...>`, `SPIn`, ...) now have a lifetime, as well as `steal`, `reborrow` and `clone_unchecked` methods (#3305) - `i2c::master::Operation` now implements `defmt::Format` (#3348) +- ESP32-S2: Support for light-/deep-sleep(#375) +- ESP32-S2: Support for light-/deep-sleep (#3341) ### Changed diff --git a/esp-hal/Cargo.toml b/esp-hal/Cargo.toml index 1b6bf7100..c022afe35 100644 --- a/esp-hal/Cargo.toml +++ b/esp-hal/Cargo.toml @@ -58,13 +58,13 @@ ufmt-write = "0.1.0" # IMPORTANT: # Each supported device MUST have its PAC included below along with a # corresponding feature. -esp32 = { version = "0.36.0", features = ["critical-section", "rt"], optional = true } -esp32c2 = { version = "0.25.0", features = ["critical-section", "rt"], optional = true } -esp32c3 = { version = "0.28.0", features = ["critical-section", "rt"], optional = true } -esp32c6 = { version = "0.19.0", features = ["critical-section", "rt"], optional = true } -esp32h2 = { version = "0.15.0", features = ["critical-section", "rt"], optional = true } -esp32s2 = { version = "0.27.0", features = ["critical-section", "rt"], optional = true } -esp32s3 = { version = "0.31.0", features = ["critical-section", "rt"], optional = true } +esp32 = { version = "0.36.0", git = "https://github.com/esp-rs/esp-pacs", rev = "37684f3", features = ["critical-section", "rt"], optional = true } +esp32c2 = { version = "0.25.0", git = "https://github.com/esp-rs/esp-pacs", rev = "37684f3", features = ["critical-section", "rt"], optional = true } +esp32c3 = { version = "0.28.0", git = "https://github.com/esp-rs/esp-pacs", rev = "37684f3", features = ["critical-section", "rt"], optional = true } +esp32c6 = { version = "0.19.0", git = "https://github.com/esp-rs/esp-pacs", rev = "37684f3", features = ["critical-section", "rt"], optional = true } +esp32h2 = { version = "0.15.0", git = "https://github.com/esp-rs/esp-pacs", rev = "37684f3", features = ["critical-section", "rt"], optional = true } +esp32s2 = { version = "0.27.0", git = "https://github.com/esp-rs/esp-pacs", rev = "37684f3", features = ["critical-section", "rt"], optional = true } +esp32s3 = { version = "0.31.0", git = "https://github.com/esp-rs/esp-pacs", rev = "37684f3", features = ["critical-section", "rt"], optional = true } [target.'cfg(target_arch = "riscv32")'.dependencies] riscv = { version = "0.12.1" } diff --git a/esp-hal/src/rtc_cntl/mod.rs b/esp-hal/src/rtc_cntl/mod.rs index e0839dae7..8977c964b 100644 --- a/esp-hal/src/rtc_cntl/mod.rs +++ b/esp-hal/src/rtc_cntl/mod.rs @@ -117,7 +117,7 @@ pub use self::rtc::SocResetReason; use crate::clock::XtalClock; #[cfg(not(esp32))] use crate::efuse::Efuse; -#[cfg(any(esp32, esp32s3, esp32c3, esp32c6, esp32c2))] +#[cfg(any(esp32, esp32s2, esp32s3, esp32c3, esp32c6, esp32c2))] use crate::rtc_cntl::sleep::{RtcSleepConfig, WakeSource, WakeTriggers}; use crate::{ clock::Clock, @@ -132,7 +132,7 @@ use crate::{ time::Rate, }; // only include sleep where it's been implemented -#[cfg(any(esp32, esp32s3, esp32c3, esp32c6, esp32c2))] +#[cfg(any(esp32, esp32s2, esp32s3, esp32c3, esp32c6, esp32c2))] pub mod sleep; #[cfg_attr(esp32, path = "rtc/esp32.rs")] @@ -300,7 +300,7 @@ impl<'d> Rtc<'d> { swd: Swd::new(), }; - #[cfg(any(esp32, esp32s3, esp32c3, esp32c6, esp32c2))] + #[cfg(any(esp32, esp32s2, esp32s3, esp32c3, esp32c6, esp32c2))] RtcSleepConfig::base_settings(&this); this @@ -326,7 +326,7 @@ impl<'d> Rtc<'d> { let l = rtc_cntl.time0().read().time_lo().bits(); (l, h) }; - #[cfg(any(esp32c2, esp32c3, esp32s3, esp32s2))] + #[cfg(any(esp32c2, esp32c3, esp32s2, esp32s3))] let (l, h) = { rtc_cntl.time_update().write(|w| w.time_update().set_bit()); let h = rtc_cntl.time_high0().read().timer_value0_high().bits(); @@ -457,7 +457,7 @@ impl<'d> Rtc<'d> { } /// Enter deep sleep and wake with the provided `wake_sources`. - #[cfg(any(esp32, esp32s3, esp32c3, esp32c6, esp32c2))] + #[cfg(any(esp32, esp32s2, esp32s3, esp32c3, esp32c6, esp32c2))] pub fn sleep_deep(&mut self, wake_sources: &[&dyn WakeSource]) -> ! { let config = RtcSleepConfig::deep(); self.sleep(&config, wake_sources); @@ -465,7 +465,7 @@ impl<'d> Rtc<'d> { } /// Enter light sleep and wake with the provided `wake_sources`. - #[cfg(any(esp32, esp32s3, esp32c3, esp32c6, esp32c2))] + #[cfg(any(esp32, esp32s2, esp32s3, esp32c3, esp32c6, esp32c2))] pub fn sleep_light(&mut self, wake_sources: &[&dyn WakeSource]) { let config = RtcSleepConfig::default(); self.sleep(&config, wake_sources); @@ -473,7 +473,7 @@ impl<'d> Rtc<'d> { /// Enter sleep with the provided `config` and wake with the provided /// `wake_sources`. - #[cfg(any(esp32, esp32s3, esp32c3, esp32c6, esp32c2))] + #[cfg(any(esp32, esp32s2, esp32s3, esp32c3, esp32c6, esp32c2))] pub fn sleep(&mut self, config: &RtcSleepConfig, wake_sources: &[&dyn WakeSource]) { let mut config = *config; let mut wakeup_triggers = WakeTriggers::default(); diff --git a/esp-hal/src/rtc_cntl/sleep/esp32s2.rs b/esp-hal/src/rtc_cntl/sleep/esp32s2.rs new file mode 100644 index 000000000..52e88542d --- /dev/null +++ b/esp-hal/src/rtc_cntl/sleep/esp32s2.rs @@ -0,0 +1,811 @@ +use super::{ + Ext0WakeupSource, + Ext1WakeupSource, + TimerWakeupSource, + WakeSource, + WakeTriggers, + WakeupLevel, +}; +use crate::{ + gpio::{RtcFunction, RtcPin}, + peripherals::{EXTMEM, LPWR, RTC_IO, SENS, SPI0, SPI1, SYSTEM}, + rtc_cntl::{sleep::RtcioWakeupSource, Clock, Rtc, RtcClock}, + soc::regi2c, +}; + +// Approximate mapping of voltages to RTC_CNTL_DBIAS_WAK, RTC_CNTL_DBIAS_SLP, +// RTC_CNTL_DIG_DBIAS_WAK, RTC_CNTL_DIG_DBIAS_SLP values. +// Valid if RTC_CNTL_DBG_ATTEN is 0. +/// Digital bias setting for 0.90V. +pub const RTC_CNTL_DBIAS_0V90: u8 = 0; +/// Digital bias setting for 0.95V. +pub const RTC_CNTL_DBIAS_0V95: u8 = 1; +/// Digital bias setting for 1.00V. +pub const RTC_CNTL_DBIAS_1V00: u8 = 2; +/// Digital bias setting for 1.05V. +pub const RTC_CNTL_DBIAS_1V05: u8 = 3; +/// Digital bias setting for 1.10V. +pub const RTC_CNTL_DBIAS_1V10: u8 = 4; +/// Digital bias setting for 1.15V. +pub const RTC_CNTL_DBIAS_1V15: u8 = 5; +/// Digital bias setting for 1.20V. +pub const RTC_CNTL_DBIAS_1V20: u8 = 6; +/// Digital bias setting for 1.25V. +pub const RTC_CNTL_DBIAS_1V25: u8 = 7; +/// Default monitor debug attenuation value. +pub const RTC_CNTL_DBG_ATTEN_MONITOR_DEFAULT: u8 = 0; +/// ULP co-processor touch start wait time during sleep, set to maximum. +pub const RTC_CNTL_ULPCP_TOUCH_START_WAIT_IN_SLEEP: u16 = 0xFF; +/// ULP co-processor touch start wait time default value. +pub const RTC_CNTL_ULPCP_TOUCH_START_WAIT_DEFAULT: u16 = 0x10; +/// Default wait time for PLL buffer during startup. +pub const RTC_CNTL_PLL_BUF_WAIT_DEFAULT: u8 = 20; +/// Default wait time for CK8M during startup. +pub const RTC_CNTL_CK8M_WAIT_DEFAULT: u8 = 20; +/// Default wait time for XTL buffer during startup. +pub const RTC_CNTL_XTL_BUF_WAIT_DEFAULT: u8 = 100; +/// Minimum sleep value. +pub const RTC_CNTL_MIN_SLP_VAL_MIN: u8 = 2; +/// Deep sleep debug attenuation setting for ultra-low power mode. +pub const RTC_CNTL_DBG_ATTEN_DEEPSLEEP_DEFAULT: u8 = 15; +/// Power-up setting for other blocks. +pub const OTHER_BLOCKS_POWERUP: u8 = 1; +/// Wait cycles for other blocks. +pub const OTHER_BLOCKS_WAIT: u16 = 1; +/// WiFi power-up cycles. +pub const WIFI_POWERUP_CYCLES: u8 = OTHER_BLOCKS_POWERUP; +/// WiFi wait cycles. +pub const WIFI_WAIT_CYCLES: u16 = OTHER_BLOCKS_WAIT; +/// RTC power-up cycles. +pub const RTC_POWERUP_CYCLES: u8 = OTHER_BLOCKS_POWERUP; +/// RTC wait cycles. +pub const RTC_WAIT_CYCLES: u16 = OTHER_BLOCKS_WAIT; +/// DG wrap power-up cycles. +pub const DG_WRAP_POWERUP_CYCLES: u8 = OTHER_BLOCKS_POWERUP; +/// DG wrap wait cycles. +pub const DG_WRAP_WAIT_CYCLES: u16 = OTHER_BLOCKS_WAIT; +/// DG peripheral power-up cycles. +pub const DG_PERI_POWERUP_CYCLES: u8 = OTHER_BLOCKS_POWERUP; +/// DG peripheral wait cycles. +pub const DG_PERI_WAIT_CYCLES: u16 = OTHER_BLOCKS_WAIT; +/// RTC memory power-up cycles. +pub const RTC_MEM_POWERUP_CYCLES: u8 = OTHER_BLOCKS_POWERUP; +/// RTC memory wait cycles. +pub const RTC_MEM_WAIT_CYCLES: u16 = OTHER_BLOCKS_WAIT; + +impl WakeSource for TimerWakeupSource { + fn apply( + &self, + rtc: &Rtc<'_>, + triggers: &mut WakeTriggers, + _sleep_config: &mut RtcSleepConfig, + ) { + triggers.set_timer(true); + let rtc_cntl = LPWR::regs(); + let clock_freq = RtcClock::slow_freq(); + // TODO: maybe add sleep time adjustlemnt like idf + // TODO: maybe add check to prevent overflow? + let clock_hz = clock_freq.frequency().as_hz() as u64; + let ticks = self.duration.as_micros() as u64 * clock_hz / 1_000_000u64; + // "alarm" time in slow rtc ticks + let now = rtc.time_since_boot_raw(); + let time_in_ticks = now + ticks; + unsafe { + rtc_cntl + .slp_timer0() + .write(|w| w.slp_val_lo().bits((time_in_ticks & 0xffffffff) as u32)); + + rtc_cntl + .int_clr() + .write(|w| w.main_timer().clear_bit_by_one()); + + rtc_cntl.slp_timer1().write(|w| { + w.slp_val_hi().bits(((time_in_ticks >> 32) & 0xffff) as u16); + w.main_timer_alarm_en().set_bit() + }); + } + } +} + +impl WakeSource for Ext0WakeupSource

{ + fn apply( + &self, + _rtc: &Rtc<'_>, + triggers: &mut WakeTriggers, + sleep_config: &mut RtcSleepConfig, + ) { + sleep_config.set_rtc_peri_pd_en(false); + triggers.set_ext0(true); + + // TODO: disable clock when not in use + SENS::regs() + .sar_io_mux_conf() + .modify(|_, w| w.iomux_clk_gate_en().set_bit()); + + // set pin to RTC function + self.pin + .borrow_mut() + .rtc_set_config(true, true, RtcFunction::Rtc); + + // rtcio_hal_ext0_set_wakeup_pin + unsafe { + let rtc_io = RTC_IO::regs(); + // set pin register field + rtc_io + .ext_wakeup0() + .modify(|_, w| w.sel().bits(self.pin.borrow().rtc_number())); + // set level register field + let rtc_cntl = LPWR::regs(); + rtc_cntl + .ext_wakeup_conf() + .modify(|_r, w| w.ext_wakeup0_lv().bit(self.level == WakeupLevel::High)); + } + } +} + +impl Drop for Ext0WakeupSource

{ + fn drop(&mut self) { + // should we have saved the pin configuration first? + // set pin back to IO_MUX (input_enable and func have no effect when pin is sent + // to IO_MUX) + self.pin + .borrow_mut() + .rtc_set_config(true, false, RtcFunction::Rtc); + } +} + +impl WakeSource for Ext1WakeupSource<'_, '_> { + fn apply( + &self, + _rtc: &Rtc<'_>, + triggers: &mut WakeTriggers, + sleep_config: &mut RtcSleepConfig, + ) { + sleep_config.set_rtc_peri_pd_en(false); + triggers.set_ext1(true); + + // TODO: disable clock when not in use + SENS::regs() + .sar_io_mux_conf() + .modify(|_, w| w.iomux_clk_gate_en().set_bit()); + + // set pins to RTC function + let mut pins = self.pins.borrow_mut(); + let mut bits = 0u32; + for pin in pins.iter_mut() { + pin.rtc_set_config(true, true, RtcFunction::Rtc); + bits |= 1 << pin.rtc_number(); + } + + unsafe { + let rtc_cntl = LPWR::regs(); + // clear previous wakeup status + rtc_cntl + .ext_wakeup1() + .modify(|_, w| w.status_clr().set_bit()); + // set pin register field + rtc_cntl.ext_wakeup1().modify(|_, w| w.sel().bits(bits)); + // set level register field + rtc_cntl + .ext_wakeup_conf() + .modify(|_r, w| w.ext_wakeup1_lv().bit(self.level == WakeupLevel::High)); + } + } +} + +impl Drop for Ext1WakeupSource<'_, '_> { + fn drop(&mut self) { + // should we have saved the pin configuration first? + // set pin back to IO_MUX (input_enable and func have no effect when pin is sent + // to IO_MUX) + let mut pins = self.pins.borrow_mut(); + for pin in pins.iter_mut() { + pin.rtc_set_config(true, false, RtcFunction::Rtc); + } + } +} + +impl RtcioWakeupSource<'_, '_> { + fn apply_pin(&self, pin: &mut dyn RtcPin, level: WakeupLevel) { + let rtcio = RTC_IO::regs(); + + pin.rtc_set_config(true, true, RtcFunction::Rtc); + + rtcio.pin(pin.number() as usize).modify(|_, w| unsafe { + w.gpio_pin_wakeup_enable().set_bit(); + w.gpio_pin_int_type().bits(match level { + WakeupLevel::Low => 4, + WakeupLevel::High => 5, + }) + }); + } +} + +impl WakeSource for RtcioWakeupSource<'_, '_> { + fn apply( + &self, + _rtc: &Rtc<'_>, + triggers: &mut WakeTriggers, + sleep_config: &mut RtcSleepConfig, + ) { + let mut pins = self.pins.borrow_mut(); + + if pins.is_empty() { + return; + } + + // don't power down RTC peripherals + sleep_config.set_rtc_peri_pd_en(false); + triggers.set_gpio(true); + + // Since we only use RTCIO pins, we can keep deep sleep enabled. + let sens = crate::peripherals::SENS::regs(); + + // TODO: disable clock when not in use + sens.sar_io_mux_conf() + .modify(|_, w| w.iomux_clk_gate_en().set_bit()); + + for (pin, level) in pins.iter_mut() { + self.apply_pin(*pin, *level); + } + } +} + +impl Drop for RtcioWakeupSource<'_, '_> { + fn drop(&mut self) { + // should we have saved the pin configuration first? + // set pin back to IO_MUX (input_enable and func have no effect when pin is sent + // to IO_MUX) + let mut pins = self.pins.borrow_mut(); + for (pin, _level) in pins.iter_mut() { + pin.rtc_set_config(true, false, RtcFunction::Rtc); + } + } +} + +bitfield::bitfield! { + /// Configuration for the RTC sleep behavior. + #[derive(Clone, Copy)] + pub struct RtcSleepConfig(u64); + impl Debug; + /// force normal voltage in sleep mode (digital domain memory) + pub lslp_mem_inf_fpu, set_lslp_mem_inf_fpu: 0; + /// keep low voltage in sleep mode (even if ULP/touch is used) + pub rtc_mem_inf_follow_cpu, set_rtc_mem_inf_follow_cpu: 1; + /// power down RTC fast memory + pub rtc_fastmem_pd_en, set_rtc_fastmem_pd_en: 2; + /// power down RTC slow memory + pub rtc_slowmem_pd_en, set_rtc_slowmem_pd_en: 3; + /// power down RTC peripherals + pub rtc_peri_pd_en, set_rtc_peri_pd_en: 4; + /// power down Wifi + pub wifi_pd_en, set_wifi_pd_en: 5; + /// Power down Internal 8M oscillator + pub int_8m_pd_en, set_int_8m_pd_en: 6; + /// power down digital domain + pub deep_slp, set_deep_slp: 8; + /// enable WDT flashboot mode + pub wdt_flashboot_mod_en, set_wdt_flashboot_mod_en: 9; + /// set bias for digital domain, in sleep mode + pub u8, dig_dbias_slp, set_dig_dbias_slp: 12, 10; + /// set bias for RTC domain, in sleep mode + pub u8, rtc_dbias_slp, set_rtc_dbias_slp: 16, 13; + /// circuit control parameter, in monitor mode + pub bias_sleep_monitor, set_bias_sleep_monitor: 17; + /// voltage parameter, in sleep mode + pub u8, dbg_atten_slp, set_dbg_atten_slp: 22, 18; + /// circuit control parameter, in sleep mode + pub bias_sleep_slp, set_bias_sleep_slp: 23; + /// circuit control parameter, in monitor mode + pub pd_cur_monitor, set_pd_cur_monitor: 24; + /// circuit control parameter, in sleep mode + pub pd_cur_slp, set_pd_cur_slp: 25; + /// power down VDDSDIO regulator + pub vddsdio_pd_en, set_vddsdio_pd_en: 26; + /// keep main XTAL powered up in sleep + pub xtal_fpu, set_xtal_fpu: 27; + /// keep rtc regulator powered up in sleep + pub rtc_regulator_fpu, set_rtc_regulator_fpu: 28; + /// enable deep sleep reject + pub deep_slp_reject, set_deep_slp_reject: 29; + /// enable light sleep reject + pub light_slp_reject, set_light_slp_reject: 30; +} + +impl Default for RtcSleepConfig { + fn default() -> Self { + let mut cfg = Self(Default::default()); + cfg.set_deep_slp_reject(true); + cfg.set_light_slp_reject(true); + cfg.set_rtc_dbias_slp(RTC_CNTL_DBIAS_1V10); + cfg.set_dig_dbias_slp(RTC_CNTL_DBIAS_1V10); + cfg.set_rtc_slowmem_pd_en(true); + cfg.set_rtc_fastmem_pd_en(true); + cfg + } +} + +fn rtc_sleep_pu(val: bool) { + // Note: Called rtc_sleep_pd in idf, but makes more sense like this with the + // single boolean argument + let rtc_cntl = LPWR::regs(); + let syscon = unsafe { &*esp32s2::SYSCON::ptr() }; + let bb = unsafe { &*esp32s2::BB::ptr() }; + let i2s = unsafe { &*esp32s2::I2S0::ptr() }; + let nrx = unsafe { &*esp32s2::NRX::ptr() }; + let fe = unsafe { &*esp32s2::FE::ptr() }; + let fe2 = unsafe { &*esp32s2::FE2::ptr() }; + + rtc_cntl + .dig_pwc() + .modify(|_, w| w.lslp_mem_force_pu().bit(val)); + + rtc_cntl + .pwc() + .modify(|_, w| w.slowmem_force_lpu().bit(val).fastmem_force_lpu().bit(val)); + + i2s.pd_conf() + .write(|w| w.plc_mem_force_pu().bit(val).fifo_force_pu().bit(val)); + + syscon.front_end_mem_pd().modify(|_r, w| { + w.dc_mem_force_pu() + .bit(val) + .pbus_mem_force_pu() + .bit(val) + .agc_mem_force_pu() + .bit(val) + }); + + bb.bbpd_ctrl() + .modify(|_r, w| w.fft_force_pu().bit(val).dc_est_force_pu().bit(val)); + + nrx.nrxpd_ctrl().modify(|_, w| { + w.rx_rot_force_pu() + .bit(val) + .vit_force_pu() + .bit(val) + .demap_force_pu() + .bit(val) + }); + + fe.gen_ctrl().modify(|_, w| w.iq_est_force_pu().bit(val)); + + fe2.tx_interp_ctrl() + .modify(|_, w| w.tx_inf_force_pu().bit(val)); +} + +impl RtcSleepConfig { + /// Configures the RTC for deep sleep mode. + pub fn deep() -> Self { + // Set up for ultra-low power sleep. Wakeup sources may modify these settings. + let mut cfg = Self::default(); + + cfg.set_lslp_mem_inf_fpu(false); + cfg.set_rtc_mem_inf_follow_cpu(true); // ? + cfg.set_rtc_fastmem_pd_en(true); + cfg.set_rtc_slowmem_pd_en(true); + cfg.set_rtc_peri_pd_en(true); + cfg.set_wifi_pd_en(true); + cfg.set_int_8m_pd_en(true); + + // Because of force_flags + cfg.set_vddsdio_pd_en(true); + + // because of dig_peri_pd_en + cfg.set_dig_dbias_slp(0); + + cfg.set_deep_slp(true); + cfg.set_wdt_flashboot_mod_en(false); + cfg.set_vddsdio_pd_en(true); + cfg.set_xtal_fpu(false); + cfg.set_deep_slp_reject(true); + cfg.set_light_slp_reject(true); + + // because of RTC_SLEEP_PD_DIG + // NOTE: Might be the a different case for RTC_SLEEP_PD_DIG in + // rtc_sleep_get_default_config + cfg.set_rtc_regulator_fpu(false); + cfg.set_dbg_atten_slp(RTC_CNTL_DBG_ATTEN_DEEPSLEEP_DEFAULT); + cfg.set_rtc_dbias_slp(0); + + // because of xtal_fpu + cfg.set_xtal_fpu(false); + cfg.set_bias_sleep_monitor(true); + cfg.set_pd_cur_monitor(true); + cfg.set_bias_sleep_slp(true); + cfg.set_pd_cur_slp(true); + + cfg + } + + pub(crate) fn base_settings(_rtc: &Rtc<'_>) { + // settings derived from esp_clk_init -> rtc_init + unsafe { + let rtc_cntl = LPWR::regs(); + let extmem = EXTMEM::regs(); + let system = SYSTEM::regs(); + + rtc_cntl + .dig_pwc() + .modify(|_, w| w.wifi_force_pd().clear_bit()); + rtc_cntl + .dig_iso() + .modify(|_, w| w.wifi_force_iso().clear_bit()); + + rtc_cntl.ana_conf().modify(|_, w| w.pvtmon_pu().clear_bit()); + + rtc_cntl.timer1().modify(|_, w| { + w.pll_buf_wait().bits(RTC_CNTL_PLL_BUF_WAIT_DEFAULT); + w.ck8m_wait().bits(RTC_CNTL_CK8M_WAIT_DEFAULT) + }); + + // idf: "Moved from rtc sleep to rtc init to save sleep function running time + // set shortest possible sleep time limit" + + rtc_cntl + .timer5() + .modify(|_, w| w.min_slp_val().bits(RTC_CNTL_MIN_SLP_VAL_MIN)); + + rtc_cntl.timer3().modify(|_, w| { + // set wifi timer + w.wifi_powerup_timer().bits(WIFI_POWERUP_CYCLES); + w.wifi_wait_timer().bits(WIFI_WAIT_CYCLES) + }); + + rtc_cntl.timer4().modify(|_, w| { + // set rtc peri timer + w.powerup_timer().bits(RTC_POWERUP_CYCLES); + w.wait_timer().bits(RTC_WAIT_CYCLES); + // set digital wrap timer + w.dg_wrap_powerup_timer().bits(DG_WRAP_POWERUP_CYCLES); + w.dg_wrap_wait_timer().bits(DG_WRAP_WAIT_CYCLES) + }); + + rtc_cntl.timer5().modify(|_, w| { + w.rtcmem_powerup_timer().bits(RTC_MEM_POWERUP_CYCLES); + w.rtcmem_wait_timer().bits(RTC_MEM_WAIT_CYCLES) + }); + + rtc_cntl.bias_conf().modify(|_, w| { + w.dec_heartbeat_width().set_bit(); + w.inc_heartbeat_period().set_bit() + }); + + // Reset RTC bias to default value (needed if waking up from deep sleep) + rtc_cntl.reg().modify(|_, w| { + w.dbias_wak().bits(RTC_CNTL_DBIAS_1V10); + w.dbias_slp().bits(RTC_CNTL_DBIAS_1V10) + }); + + // Set the wait time to the default value. + rtc_cntl.timer2().modify(|_, w| { + w.ulpcp_touch_start_wait() + .bits(RTC_CNTL_ULPCP_TOUCH_START_WAIT_DEFAULT) + }); + + // clkctl_init + { + // clear CMMU clock force on + extmem + .pro_cache_mmu_power_ctrl() + .modify(|_, w| w.pro_cache_mmu_mem_force_on().clear_bit()); + + // clear tag clock force on + extmem + .pro_dcache_tag_power_ctrl() + .modify(|_, w| w.pro_dcache_tag_mem_force_on().clear_bit()); + + extmem + .pro_icache_tag_power_ctrl() + .modify(|_, w| w.pro_icache_tag_mem_force_on().clear_bit()); + + system.rom_ctrl_0().modify(|_, w| w.rom_fo().bits(0)); + system.sram_ctrl_0().modify(|_, w| w.sram_fo().bits(0)); + + // clear register clock force on + SPI0::regs() + .clock_gate() + .modify(|_, w| w.clk_en().clear_bit()); + SPI1::regs() + .clock_gate() + .modify(|_, w| w.clk_en().clear_bit()); + } + + // pwrctl_init + { + rtc_cntl + .clk_conf() + .modify(|_, w| w.ck8m_force_pu().clear_bit()); + + rtc_cntl + .options0() + .modify(|_, w| w.xtl_force_pu().clear_bit()); + + // CLEAR APLL close + rtc_cntl.ana_conf().modify(|_, w| { + w.plla_force_pu().clear_bit(); + w.plla_force_pd().set_bit() + }); + + // cancel bbpll force pu if setting no force power up + rtc_cntl.options0().modify(|_, w| { + w.bbpll_force_pu().clear_bit(); + w.bbpll_i2c_force_pu().clear_bit(); + w.bb_i2c_force_pu().clear_bit() + }); + + // cancel RTC REG force PU + + rtc_cntl.pwc().modify(|_, w| w.force_pu().clear_bit()); + rtc_cntl.reg().modify(|_, w| { + w.regulator_force_pu().clear_bit(); + w.dboost_force_pu().clear_bit() + }); + + rtc_cntl.pwc().modify(|_, w| { + w.slowmem_force_pu().clear_bit(); + w.fastmem_force_pu().clear_bit(); + w.slowmem_force_noiso().clear_bit(); + w.fastmem_force_noiso().clear_bit() + }); + + rtc_cntl.reg().modify(|_, w| w.dboost_force_pd().set_bit()); + + // cancel sar i2c pd force + rtc_cntl + .ana_conf() + .modify(|_, w| w.sar_i2c_force_pd().clear_bit()); + // cancel digital pu force + // NOTE: duplicate from idf + rtc_cntl.pwc().modify(|_, w| { + w.slowmem_force_pu().clear_bit(); + w.fastmem_force_pu().clear_bit() + }); + + // If this mask is enabled, all soc memories cannot enter power down mode + // We should control soc memory power down mode from RTC, so we will not touch + // this register any more + + system + .mem_pd_mask() + .modify(|_, w| w.lslp_mem_pd_mask().clear_bit()); + + // If this pd_cfg is set to 1, all memory won't enter low power mode during + // light sleep If this pd_cfg is set to 0, all memory will enter low + // power mode during light sleep + rtc_sleep_pu(false); + + rtc_cntl.dig_pwc().modify(|_, w| { + w.dg_wrap_force_pu().clear_bit(); + w.wifi_force_pu().clear_bit() + }); + + rtc_cntl.dig_iso().modify(|_, w| { + w.dg_wrap_force_noiso().clear_bit(); + // NOTE: not present in idf. + w.dg_wrap_force_iso().clear_bit() + }); + + rtc_cntl.dig_iso().modify(|_, w| { + w.wifi_force_noiso().clear_bit(); + // NOTE: not present in idf. + w.wifi_force_iso().clear_bit() + }); + + rtc_cntl.pwc().modify(|_, w| w.force_noiso().clear_bit()); + + // cancel digital PADS force no iso + system + .cpu_per_conf() + .modify(|_, w| w.cpu_wait_mode_force_on().clear_bit()); + + // if DPORT_CPU_WAIT_MODE_FORCE_ON == 0, + // the cpu clk will be closed when cpu enter WAITI mode + rtc_cntl.dig_iso().modify(|_, w| { + w.dg_pad_force_unhold().clear_bit(); + w.dg_pad_force_noiso().clear_bit() + }); + } + + // force power down wifi and bt power domain + rtc_cntl + .dig_iso() + .modify(|_, w| w.wifi_force_iso().set_bit()); + + rtc_cntl + .dig_pwc() + .modify(|_, w| w.wifi_force_pd().set_bit()); + + rtc_cntl.int_ena().write(|w| w.bits(0)); + rtc_cntl.int_clr().write(|w| w.bits(u32::MAX)); + } + } + + pub(crate) fn apply(&self) { + // like esp-idf rtc_sleep_init() and deep_sleep_start() + let rtc_cntl = LPWR::regs(); + + if self.deep_slp() { + // "Due to hardware limitations, on S2 the brownout detector + // sometimes trigger during deep sleep to circumvent + // this we disable the brownout detector before sleeping' - from + // idf's deep_sleep_start() + unsafe { + // brownout_hal_config(brownlout_hal_config_t{0}) + rtc_cntl.brown_out().modify(|_, w| { + w.int_wait().bits(2); + w.close_flash_ena().clear_bit(); + w.pd_rf_ena().clear_bit(); + w.cnt_clr().set_bit() + }); + rtc_cntl.brown_out().modify(|_, w| { + // Set followed by clear in idf + w.cnt_clr().clear_bit(); + w.rst_wait().bits(0x3fff); + w.rst_ena().clear_bit(); + w.brown_out2_ena().set_bit(); + w.rst_sel().set_bit() + }); + regi2c::I2C_BOD_REG_THRESHOLD.write_field(0); + rtc_cntl.brown_out().modify(|_, w| w.ena().clear_bit()); + rtc_cntl.int_ena().modify(|_, w| w.brown_out().clear_bit()); + // NOTE: rtc_isr_deregister? + } + } + + if self.lslp_mem_inf_fpu() { + rtc_sleep_pu(true); + } + + let mem_folw_cpu = self.rtc_mem_inf_follow_cpu(); + rtc_cntl.pwc().modify(|_, w| { + w.slowmem_folw_cpu().bit(mem_folw_cpu); + w.fastmem_folw_cpu().bit(mem_folw_cpu) + }); + + let rtc_fastmem_pd_en = self.rtc_fastmem_pd_en(); + rtc_cntl.pwc().modify(|_, w| { + w.fastmem_pd_en().bit(rtc_fastmem_pd_en); + w.fastmem_force_pu().bit(!rtc_fastmem_pd_en); + w.fastmem_force_noiso().bit(!rtc_fastmem_pd_en) + }); + + let rtc_slowmem_pd_en = self.rtc_slowmem_pd_en(); + rtc_cntl.pwc().modify(|_, w| { + w.slowmem_pd_en().bit(rtc_slowmem_pd_en); + w.slowmem_force_pu().bit(!rtc_slowmem_pd_en); + w.slowmem_force_noiso().bit(!rtc_slowmem_pd_en) + }); + + let rtc_peri_pd_en = self.rtc_peri_pd_en(); + rtc_cntl.pwc().modify(|_, w| w.pd_en().bit(rtc_peri_pd_en)); + + if self.wifi_pd_en() { + rtc_cntl + .dig_iso() + .modify(|_, w| w.wifi_force_noiso().clear_bit()); + + rtc_cntl.dig_pwc().modify(|_, w| { + w.wifi_force_pu().clear_bit(); + w.wifi_pd_en().set_bit() + }); + } else { + rtc_cntl.dig_pwc().modify(|_, w| w.wifi_pd_en().clear_bit()); + } + + unsafe { + rtc_cntl.reg().modify(|_, w| { + w.dbias_slp().bits(self.rtc_dbias_slp()); + w.dig_reg_dbias_slp().bits(self.dig_dbias_slp()) + }); + + rtc_cntl.bias_conf().modify(|_, w| { + w.dbg_atten_monitor() + .bits(RTC_CNTL_DBG_ATTEN_MONITOR_DEFAULT); + w.bias_sleep_monitor().bit(self.bias_sleep_monitor()); + w.bias_sleep_deep_slp().bit(self.bias_sleep_slp()); + w.pd_cur_monitor().bit(self.pd_cur_monitor()); + w.pd_cur_deep_slp().bit(self.pd_cur_slp()); + w.dbg_atten_deep_slp().bits(self.dbg_atten_slp()) + }); + + if self.deep_slp() { + rtc_cntl + .dig_pwc() + .modify(|_, w| w.dg_wrap_pd_en().set_bit()); + + rtc_cntl.ana_conf().modify(|_, w| { + w.ckgen_i2c_pu().clear_bit(); + w.pll_i2c_pu().clear_bit(); + w.rfrx_pbus_pu().clear_bit(); + w.txrf_i2c_pu().clear_bit() + }); + + rtc_cntl + .options0() + .modify(|_, w| w.bb_i2c_force_pu().clear_bit()); + } else { + rtc_cntl + .dig_pwc() + .modify(|_, w| w.dg_wrap_pd_en().clear_bit()); + } + + let rtc_regulator_fpu = self.rtc_regulator_fpu(); + rtc_cntl + .reg() + .modify(|_, w| w.regulator_force_pu().bit(rtc_regulator_fpu)); + + let int_8m_pd_en = self.int_8m_pd_en(); + rtc_cntl + .clk_conf() + .modify(|_, w| w.ck8m_force_pu().bit(!int_8m_pd_en)); + + // enable VDDSDIO control by state machine + rtc_cntl.sdio_conf().modify(|_, w| { + w.sdio_force().clear_bit(); + w.sdio_reg_pd_en().bit(self.vddsdio_pd_en()) + }); + + rtc_cntl.slp_reject_conf().modify(|_, w| { + w.deep_slp_reject_en().bit(self.deep_slp_reject()); + w.light_slp_reject_en().bit(self.light_slp_reject()) + }); + + // Set wait cycle for touch or COCPU after deep sleep and light + // sleep. + + rtc_cntl.timer2().modify(|_, w| { + w.ulpcp_touch_start_wait() + .bits(RTC_CNTL_ULPCP_TOUCH_START_WAIT_IN_SLEEP) + }); + + rtc_cntl + .options0() + .modify(|_, w| w.xtl_force_pu().bit(self.xtal_fpu())); + } + } + + pub(crate) fn start_sleep(&self, wakeup_triggers: WakeTriggers) { + // TODO: Add reject triggers + unsafe { + LPWR::regs() + .reset_state() + .modify(|_, w| w.procpu_stat_vector_sel().set_bit()); + + // set bits for what can wake us up + LPWR::regs() + .wakeup_state() + .modify(|_, w| w.wakeup_ena().bits(wakeup_triggers.0.into())); + + // WARN: slp_wakeup is not set in esp-idf + LPWR::regs().state0().write(|w| { + w.sleep_en().set_bit(); + w.slp_wakeup().set_bit() + }); + } + } + + pub(crate) fn finish_sleep(&self) { + // In deep sleep mode, we never get here + unsafe { + LPWR::regs().int_clr().write(|w| { + w.slp_reject() + .clear_bit_by_one() + .slp_wakeup() + .clear_bit_by_one() + }); + + // restore config if it is a light sleep + if self.lslp_mem_inf_fpu() { + rtc_sleep_pu(true); + } + + // Recover default wait cycle for touch or COCPU after wakeup. + + LPWR::regs().timer2().modify(|_, w| { + w.ulpcp_touch_start_wait() + .bits(RTC_CNTL_ULPCP_TOUCH_START_WAIT_DEFAULT) + }); + } + } +} diff --git a/esp-hal/src/rtc_cntl/sleep/mod.rs b/esp-hal/src/rtc_cntl/sleep/mod.rs index 5895fe4f4..14480ab02 100644 --- a/esp-hal/src/rtc_cntl/sleep/mod.rs +++ b/esp-hal/src/rtc_cntl/sleep/mod.rs @@ -15,16 +15,17 @@ //! * `BT (Bluetooth) wake` - light sleep only use core::cell::RefCell; -#[cfg(any(esp32, esp32c3, esp32s3, esp32c6, esp32c2))] +#[cfg(any(esp32, esp32c3, esp32s2, esp32s3, esp32c6, esp32c2))] use core::time::Duration; -#[cfg(any(esp32, esp32s3))] +#[cfg(any(esp32, esp32s2, esp32s3))] use crate::gpio::RtcPin as RtcIoWakeupPinType; #[cfg(any(esp32c3, esp32c6, esp32c2))] use crate::gpio::RtcPinWithResistors as RtcIoWakeupPinType; use crate::rtc_cntl::Rtc; #[cfg_attr(esp32, path = "esp32.rs")] +#[cfg_attr(esp32s2, path = "esp32s2.rs")] #[cfg_attr(esp32s3, path = "esp32s3.rs")] #[cfg_attr(esp32c3, path = "esp32c3.rs")] #[cfg_attr(esp32c6, path = "esp32c6.rs")] @@ -68,13 +69,13 @@ pub enum WakeupLevel { /// # } /// ``` #[derive(Debug, Default, Clone, Copy)] -#[cfg(any(esp32, esp32c3, esp32s3, esp32c6, esp32c2))] +#[cfg(any(esp32, esp32c3, esp32s2, esp32s3, esp32c6, esp32c2))] pub struct TimerWakeupSource { /// The duration after which the wake-up event is triggered. duration: Duration, } -#[cfg(any(esp32, esp32c3, esp32s3, esp32c6, esp32c2))] +#[cfg(any(esp32, esp32c3, esp32s2, esp32s3, esp32c6, esp32c2))] impl TimerWakeupSource { /// Creates a new timer wake-up source with the specified duration. pub fn new(duration: Duration) -> Self { @@ -124,7 +125,7 @@ pub enum Error { /// /// # } /// ``` -#[cfg(any(esp32, esp32s3))] +#[cfg(any(esp32, esp32s2, esp32s3))] pub struct Ext0WakeupSource { /// The pin used as the wake-up source. pin: RefCell

, @@ -132,7 +133,7 @@ pub struct Ext0WakeupSource { level: WakeupLevel, } -#[cfg(any(esp32, esp32s3))] +#[cfg(any(esp32, esp32s2, esp32s3))] impl Ext0WakeupSource

{ /// Creates a new external wake-up source (Ext0``) with the specified pin /// and wake-up level. @@ -181,7 +182,7 @@ impl Ext0WakeupSource

{ /// /// # } /// ``` -#[cfg(any(esp32, esp32s3))] +#[cfg(any(esp32, esp32s2, esp32s3))] pub struct Ext1WakeupSource<'a, 'b> { /// A collection of pins used as wake-up sources. pins: RefCell<&'a mut [&'b mut dyn RtcIoWakeupPinType]>, @@ -189,7 +190,7 @@ pub struct Ext1WakeupSource<'a, 'b> { level: WakeupLevel, } -#[cfg(any(esp32, esp32s3))] +#[cfg(any(esp32, esp32s2, esp32s3))] impl<'a, 'b> Ext1WakeupSource<'a, 'b> { /// Creates a new external wake-up source (Ext1) with the specified pins and /// wake-up level. @@ -281,14 +282,14 @@ impl<'a, 'b> Ext1WakeupSource<'a, 'b> { /// let timer = TimerWakeupSource::new(Duration::from_secs(10)); #[cfg_attr(any(esp32c3, esp32c2), doc = "let mut pin_0 = peripherals.GPIO2;")] #[cfg_attr(any(esp32c3, esp32c2), doc = "let mut pin_1 = peripherals.GPIO3;")] -#[cfg_attr(esp32s3, doc = "let mut pin_0 = peripherals.GPIO17;")] -#[cfg_attr(esp32s3, doc = "let mut pin_1 = peripherals.GPIO18;")] +#[cfg_attr(any(esp32s2, esp32s3), doc = "let mut pin_0 = peripherals.GPIO17;")] +#[cfg_attr(any(esp32s2, esp32s3), doc = "let mut pin_1 = peripherals.GPIO18;")] #[cfg_attr( any(esp32c3, esp32c2), doc = "let wakeup_pins: &mut [(&mut dyn gpio::RtcPinWithResistors, WakeupLevel)] = &mut [" )] #[cfg_attr( - esp32s3, + any(esp32s2, esp32s3), doc = "let wakeup_pins: &mut [(&mut dyn gpio::RtcPin, WakeupLevel)] = &mut [" )] /// (&mut pin_0, WakeupLevel::Low), @@ -301,14 +302,14 @@ impl<'a, 'b> Ext1WakeupSource<'a, 'b> { /// /// # } /// ``` -#[cfg(any(esp32c3, esp32s3, esp32c2))] +#[cfg(any(esp32c3, esp32s2, esp32s3, esp32c2))] pub struct RtcioWakeupSource<'a, 'b> { pins: RefCell<&'a mut [(&'b mut dyn RtcIoWakeupPinType, WakeupLevel)]>, } -#[cfg(any(esp32c3, esp32s3, esp32c2))] +#[cfg(any(esp32c3, esp32s2, esp32s3, esp32c2))] impl<'a, 'b> RtcioWakeupSource<'a, 'b> { - /// Creates a new external wake-up source (Ext1). + /// Creates a new external GPIO wake-up source. pub fn new(pins: &'a mut [(&'b mut dyn RtcIoWakeupPinType, WakeupLevel)]) -> Self { Self { pins: RefCell::new(pins), @@ -436,7 +437,35 @@ macro_rules! uart_wakeup_impl { uart_wakeup_impl!(0); uart_wakeup_impl!(1); -#[cfg(not(pmu))] +#[cfg(esp32s2)] +bitfield::bitfield! { + /// Represents the wakeup triggers. + #[derive(Default, Clone, Copy)] + pub struct WakeTriggers(u16); + impl Debug; + /// EXT0 GPIO wakeup + pub ext0, set_ext0: 0; + /// EXT1 GPIO wakeup + pub ext1, set_ext1: 1; + /// GPIO wakeup (l5ght sleep only) + pub gpio, set_gpio: 2; + /// Timer wakeup + pub timer, set_timer: 3; + /// WiFi SoC wakeup + pub wifi_soc, set_wifi_soc: 5; + /// UART0 wakeup (light sleep only) + pub uart0, set_uart0: 6; + /// UART1 wakeup (light sleep only) + pub uart1, set_uart1: 7; + /// Touch wakeup + pub touch, set_touch: 8; + /// ULP-FSM wakeup + pub ulp, set_ulp: 11; + /// USB wakeup + pub usb, set_usb: 15; +} + +#[cfg(any(esp32, esp32c2, esp32c3, esp32s3))] bitfield::bitfield! { /// Represents the wakeup triggers. #[derive(Default, Clone, Copy)] diff --git a/esp-hal/src/soc/esp32s2/peripherals.rs b/esp-hal/src/soc/esp32s2/peripherals.rs index 5853b25a9..d63816652 100644 --- a/esp-hal/src/soc/esp32s2/peripherals.rs +++ b/esp-hal/src/soc/esp32s2/peripherals.rs @@ -41,6 +41,7 @@ crate::peripherals! { GPIO <= GPIO, GPIO_SD <= GPIO_SD, HMAC <= HMAC, + I2C_ANA_MST <= I2C_ANA_MST, I2S0 <= I2S0 (I2S0), INTERRUPT_CORE0 <= INTERRUPT_CORE0, IO_MUX <= IO_MUX, @@ -72,6 +73,9 @@ crate::peripherals! { USB_WRAP <= USB_WRAP, WIFI <= WIFI, XTS_AES <= XTS_AES, + NRX <= NRX, + FE <= FE, + FE2 <= FE2, ], pins: [ (0, [Input, Output, Analog, RtcIo]) diff --git a/esp-hal/src/soc/esp32s2/regi2c.rs b/esp-hal/src/soc/esp32s2/regi2c.rs index c6e4c1415..0f38a56b9 100644 --- a/esp-hal/src/soc/esp32s2/regi2c.rs +++ b/esp-hal/src/soc/esp32s2/regi2c.rs @@ -1,7 +1,10 @@ -use crate::rom::regi2c::{define_regi2c, RawRegI2cField, RegI2cMaster, RegI2cRegister}; +use crate::{ + peripherals::{I2C_ANA_MST, SYSCON}, + rom::regi2c::{define_regi2c, RawRegI2cField, RegI2cMaster, RegI2cRegister}, +}; define_regi2c! { - master: REGI2C_SAR_I2C(0x69, 1) { + master: REGI2C_SAR(0x69, 1) { reg: I2C_SAR_REG0(0) { field: ADC_SAR1_INITIAL_CODE_LOW(7..0) } @@ -34,18 +37,178 @@ define_regi2c! { field: ADC_SAR_DTEST_RTC(1..0) } } + master: REGI2C_BOD(0x61, 1) { + reg: I2C_BOD_REG5(5) { + field: I2C_BOD_REG_THRESHOLD(2..0) + } + } + master: REGI2C_BBPLL(0x66, 1) { + reg: I2C_BBPLL_REG0(0) { + field: I2C_BBPLL_IR_CAL_DELAY(3..0), + field: I2C_BBPLL_IR_CAL_CK_DIV(7..4) + } + reg: I2C_BBPLL_REG1(1) { + field: I2C_BBPLL_IR_CAL_EXT_CAP(3..0), + field: I2C_BBPLL_IR_CAL_ENX_CAP(4..4), + field: I2C_BBPLL_IR_CAL_RSTB(5..5), + field: I2C_BBPLL_IR_CAL_START(6..6), + field: I2C_BBPLL_IR_CAL_UNSTOP(7..7) + } + reg: I2C_BBPLL_REG2(2) { + field: I2C_BBPLL_OC_REF_DIV(3..0), + field: I2C_BBPLL_OC_DCHGP(6..4), + field: I2C_BBPLL_OC_ENB_FCAL(7..7) + } + reg: I2C_BBPLL_REG3(3) { + field: I2C_BBPLL_OC_DIV_7_0(7..0) + } + reg: I2C_BBPLL_REG4(4) { + field: I2C_BBPLL_RSTB_DIV_ADC(0..0), + field: I2C_BBPLL_MODE_HF(1..1), + field: I2C_BBPLL_DIV_ADC(3..2), + field: I2C_BBPLL_DIV_DAC(4..4), + field: I2C_BBPLL_DIV_CPU(5..5), + field: I2C_BBPLL_OC_ENB_VCON(6..6), + field: I2C_BBPLL_OC_TSCHGP(7..7) + } + reg: I2C_BBPLL_REG5(5) { + field: I2C_BBPLL_OC_DR1(2..0), + field: I2C_BBPLL_OC_DR3(6..4), + field: I2C_BBPLL_EN_USB(7..7) + } + reg: I2C_BBPLL_REG6(6) { + field: I2C_BBPLL_OC_DCUR(2..0), + field: I2C_BBPLL_INC_CUR(3..3), + field: I2C_BBPLL_OC_DHREF_SEL(5..4), + field: I2C_BBPLL_OC_DLREF_SEL(7..6) + } + reg: I2C_BBPLL_REG8(8) { + field: I2C_BBPLL_OR_CAL_CAP(3..0), + field: I2C_BBPLL_OR_CAL_UDF(4..4), + field: I2C_BBPLL_OR_CAL_OVF(5..5), + field: I2C_BBPLL_OR_CAL_END(6..6), + field: I2C_BBPLL_OR_LOCK(7..7) + } + reg: I2C_BBPLL_REG9(9) { + field: I2C_BBPLL_BBADC_DELAY2(3..2), + field: I2C_BBPLL_BBADC_DVDD(5..4), + field: I2C_BBPLL_BBADC_DREF(7..6) + } + reg: I2C_BBPLL_REG10(10) { + field: I2C_BBPLL_BBADC_DCUR(1..0), + field: I2C_BBPLL_BBADC_INPUT_SHORT(2..2), + field: I2C_BBPLL_ENT_PLL(3..3), + field: I2C_BBPLL_DTEST(5..4), + field: I2C_BBPLL_ENT_ADC(7..6) + } + } + master: REGI2C_APLL(0x6D, 1) { + reg: I2C_APLL_REG0(0) { + field: I2C_APLL_IR_CAL_DELAY(3..0), + field: I2C_APLL_IR_CAL_RSTB(4..4), + field: I2C_APLL_IR_CAL_START(5..5), + field: I2C_APLL_IR_CAL_UNSTOP(6..6), + field: I2C_APLL_OC_ENB_FCAL(7..7) + } + reg: I2C_APLL_REG1(1) { + field: I2C_APLL_IR_CAL_EXT_CAP(4..0), + field: I2C_APLL_IR_CAL_ENX_CAP(5..5), + field: I2C_APLL_OC_LBW(6..6) + } + reg: I2C_APLL_REG2(2) { + field: I2C_APLL_IR_CAL_CK_DIV(3..0), + field: I2C_APLL_OC_DCHGP(6..4), + field: I2C_APLL_OC_ENB_VCON(7..7) + } + reg: I2C_APLL_REG3(3) { + field: I2C_APLL_OR_CAL_CAP(4..0), + field: I2C_APLL_OR_CAL_UDF(5..5), + field: I2C_APLL_OR_CAL_OVF(6..6), + field: I2C_APLL_OR_CAL_END(7..7) + } + reg: I2C_APLL_REG4(4) { + field: I2C_APLL_OR_OUTPUT_DIV(4..0), + field: I2C_APLL_OC_TSCHGP(6..6), + field: I2C_APLL_EN_FAST_CAL(7..7) + } + reg: I2C_APLL_REG5(5) { + field: I2C_APLL_OC_DHREF_SEL(1..0), + field: I2C_APLL_OC_DLREF_SEL(3..2), + field: I2C_APLL_SDM_DITHER(4..4), + field: I2C_APLL_SDM_STOP(5..5), + field: I2C_APLL_SDM_RSTB(6..6) + } + reg: I2C_APLL_REG6(6) { + field: I2C_APLL_OC_DVDD(4..0) + } + reg: I2C_APLL_REG7(7) { + field: I2C_APLL_DSDM2(5..0) + } + reg: I2C_APLL_REG8(8) { + field: I2C_APLL_DSDM1(7..0) + } + reg: I2C_APLL_REG9(9) { + field: I2C_APLL_DSDM0(7..0) + } + } } -pub(crate) fn regi2c_read(block: u8, host_id: u8, reg_add: u8) -> u8 { - extern "C" { - pub(crate) fn esp_rom_regi2c_read(block: u8, block_hostid: u8, reg_add: u8) -> u8; - } - unsafe { esp_rom_regi2c_read(block, host_id, reg_add) } +pub(crate) fn i2c_rtc_enable_block(block: u8) { + I2C_ANA_MST::regs().config0().modify(|_, w| unsafe { + const MAGIC_DEFAULT: u16 = 0x1c40; + w.magic_ctrl().bits(MAGIC_DEFAULT) + }); + I2C_ANA_MST::regs().config1().modify(|_, w| unsafe { + const ALL_MASK_V: u16 = 0x7FFF; + w.all_mask().bits(ALL_MASK_V) + }); + + SYSCON::regs() + .wifi_clk_en() + .modify(|_, w| w.mac_clk_en().set_bit()); + + match block { + v if v == REGI2C_APLL.master => I2C_ANA_MST::regs() + .config1() + .modify(|_, w| w.apll().clear_bit()), + v if v == REGI2C_BBPLL.master => I2C_ANA_MST::regs() + .config1() + .modify(|_, w| w.bbpll().clear_bit()), + v if v == REGI2C_SAR.master => I2C_ANA_MST::regs() + .config1() + .modify(|_, w| w.sar().clear_bit()), + v if v == REGI2C_BOD.master => I2C_ANA_MST::regs() + .config1() + .modify(|_, w| w.bod().clear_bit()), + _ => unreachable!(), + }; } -pub(crate) fn regi2c_write(block: u8, host_id: u8, reg_add: u8, data: u8) { - extern "C" { - pub(crate) fn rom_i2c_writeReg(block: u8, block_hostid: u8, reg_add: u8, indata: u8); - } - unsafe { rom_i2c_writeReg(block, host_id, reg_add, data) }; +pub(crate) fn regi2c_read(block: u8, _host_id: u8, reg_add: u8) -> u8 { + i2c_rtc_enable_block(block); + + I2C_ANA_MST::regs() + .config2() + .modify(|_, w| unsafe { w.slave_id().bits(block).addr().bits(reg_add) }); + + while I2C_ANA_MST::regs().config2().read().busy().bit_is_set() {} + + I2C_ANA_MST::regs().config2().read().data().bits() +} + +pub(crate) fn regi2c_write(block: u8, _host_id: u8, reg_add: u8, data: u8) { + i2c_rtc_enable_block(block); + + I2C_ANA_MST::regs().config2().modify(|_, w| unsafe { + w.slave_id() + .bits(block) + .addr() + .bits(reg_add) + .wr_cntl() + .bit(true) + .data() + .bits(data) + }); + + while I2C_ANA_MST::regs().config2().read().busy().bit_is_set() {} } diff --git a/qa-test/src/bin/sleep_timer.rs b/qa-test/src/bin/sleep_timer.rs index bd395c7e1..559208877 100644 --- a/qa-test/src/bin/sleep_timer.rs +++ b/qa-test/src/bin/sleep_timer.rs @@ -1,6 +1,6 @@ //! Demonstrates deep sleep with timer wakeup -//% CHIPS: esp32 esp32c3 esp32c6 esp32s3 esp32c2 +//% CHIPS: esp32 esp32c3 esp32c6 esp32s2 esp32s3 esp32c2 #![no_std] #![no_main] diff --git a/qa-test/src/bin/sleep_timer_ext0.rs b/qa-test/src/bin/sleep_timer_ext0.rs index f0ea7585a..1966ef9fb 100644 --- a/qa-test/src/bin/sleep_timer_ext0.rs +++ b/qa-test/src/bin/sleep_timer_ext0.rs @@ -3,7 +3,7 @@ //! The following wiring is assumed: //! - ext0 wakeup pin => GPIO4 -//% CHIPS: esp32 esp32s3 +//% CHIPS: esp32 esp32s2 esp32s3 #![no_std] #![no_main] diff --git a/qa-test/src/bin/sleep_timer_ext1.rs b/qa-test/src/bin/sleep_timer_ext1.rs index 0a9ae37a0..4a6fa4594 100644 --- a/qa-test/src/bin/sleep_timer_ext1.rs +++ b/qa-test/src/bin/sleep_timer_ext1.rs @@ -3,7 +3,7 @@ //! The following wiring is assumed: //! - ext1 wakeup pins => GPIO2, GPIO4 -//% CHIPS: esp32 esp32s3 +//% CHIPS: esp32 esp32s2 esp32s3 #![no_std] #![no_main] diff --git a/qa-test/src/bin/sleep_timer_rtcio.rs b/qa-test/src/bin/sleep_timer_rtcio.rs index 59ba3dcf4..3101881ff 100644 --- a/qa-test/src/bin/sleep_timer_rtcio.rs +++ b/qa-test/src/bin/sleep_timer_rtcio.rs @@ -4,10 +4,11 @@ //! The following wiring is assumed for ESP32C3: //! - RTC wakeup pin => GPIO2 (low level) //! - RTC wakeup pin => GPIO3 (high level) -//! The following wiring is assumed for ESP32S3: -//! - RTC wakeup pin => GPIO18 (low level) +//! The following wiring is assumed for ESP32S2/ESP32S3: +//! - RTC wakeup pin => GPIO17 (low level) +//! - RTC wakeup pin => GPIO18 (high level) -//% CHIPS: esp32c3 esp32s3 esp32c2 +//% CHIPS: esp32c3 esp32s2 esp32s3 esp32c2 #![no_std] #![no_main] @@ -57,7 +58,7 @@ fn main() -> ! { (&mut pin2, WakeupLevel::Low), (&mut pin3, WakeupLevel::High), ]; - } else if #[cfg(feature = "esp32s3")] { + } else if #[cfg(any(feature = "esp32s2", feature = "esp32s3"))] { let mut pin17 = peripherals.GPIO17; let mut pin18 = peripherals.GPIO18; let _pin17_input = Input::new(pin17.reborrow(), config);