diff --git a/CHANGELOG.md b/CHANGELOG.md index 184a20a0d84..037c5da5251 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - ESP32-C6: Properly initialize PMU (#974) - Implement overriding base mac address (#1044) - Add `rt-riscv` and `rt-xtensa` features to enable/disable runtime support (#1057) +- ESP32-C6: Implement deep sleep (#918) ### Changed diff --git a/esp-hal-common/src/clock/clocks_ll/esp32c6.rs b/esp-hal-common/src/clock/clocks_ll/esp32c6.rs index 97454324e1d..da2819a2b86 100644 --- a/esp-hal-common/src/clock/clocks_ll/esp32c6.rs +++ b/esp-hal-common/src/clock/clocks_ll/esp32c6.rs @@ -1,4 +1,11 @@ -use crate::clock::{ApbClock, Clock, CpuClock, PllClock, XtalClock}; +use core::cell::Cell; + +use critical_section::{CriticalSection, Mutex}; + +use crate::{ + clock::{ApbClock, Clock, CpuClock, PllClock, XtalClock}, + rtc_cntl::rtc::CpuClockSource, +}; extern "C" { fn ets_update_cpu_frequency(ticks_per_us: u32); @@ -36,22 +43,109 @@ const I2C_MST_BBPLL_STOP_FORCE_HIGH: u32 = 1 << 2; const I2C_MST_BBPLL_STOP_FORCE_LOW: u32 = 1 << 3; const I2C_MST_BBPLL_CAL_DONE: u32 = 1 << 24; -const MODEM_LPCON_CLK_CONF_FORCE_ON_REG: u32 = DR_REG_MODEM_LPCON_BASE + 0x1c; -const MODEM_LPCON_CLK_I2C_MST_FO: u32 = 1 << 2; -const MODEM_LPCON_I2C_MST_CLK_CONF_REG: u32 = DR_REG_MODEM_LPCON_BASE + 0x10; -const MODEM_LPCON_CLK_I2C_MST_SEL_160M: u32 = 1 << 0; +bitfield::bitfield! { + #[derive(Clone, Copy, Default)] + // `modem_clock_device_t` + pub struct ModemClockDevice(u32); + + pub bool, adc_common_fe, set_adc_common_fe: 0; + pub bool, private_fe , set_private_fe : 1; + pub bool, coexist , set_coexist : 2; + pub bool, i2c_master , set_i2c_master : 3; + pub bool, wifi_mac , set_wifi_mac : 4; + pub bool, wifi_bb , set_wifi_bb : 5; + pub bool, etm , set_etm : 6; + pub bool, ble_mac , set_ble_mac : 7; + pub bool, ble_bb , set_ble_bb : 8; + pub bool, _802154_mac , set_802154_mac : 9; + pub bool, datadump , set_datadump : 10; +} + +struct Refcounted { + refcount: Mutex>, + on_enabled: fn(), + on_disabled: fn(), +} + +impl Refcounted { + const fn new(on_enabled: fn(), on_disabled: fn()) -> Self { + Refcounted { + refcount: Mutex::new(Cell::new(0)), + on_enabled, + on_disabled, + } + } + + fn enable(&self, enable: bool, cs: CriticalSection) { + let refcount = self.refcount.borrow(cs); + let count = refcount.get(); + + if enable { + refcount.set(count + 1); + if count == 1 { + (self.on_enabled)(); + } + } else { + if count == 1 { + (self.on_disabled)(); + } + refcount.set(count - 1); + } + } +} + +unsafe fn modem_lpcon<'a>() -> &'a esp32c6::modem_lpcon::RegisterBlock { + &*esp32c6::MODEM_LPCON::ptr() +} + +unsafe fn pcr<'a>() -> &'a esp32c6::pcr::RegisterBlock { + &*esp32c6::PCR::ptr() +} + +fn modem_clock_device_enable(sources: ModemClockDevice, enable: bool) { + // TODO: implement the rest + static I2C_MASTER: Refcounted = Refcounted::new( + || unsafe { + modem_lpcon() + .clk_conf() + .modify(|_, w| w.clk_i2c_mst_en().set_bit()); + }, + || unsafe { + modem_lpcon() + .clk_conf() + .modify(|_, w| w.clk_i2c_mst_en().clear_bit()); + }, + ); + + critical_section::with(|cs| { + if sources.i2c_master() { + I2C_MASTER.enable(enable, cs); + } + }); +} + +// rtc_clk_bbpll_configure +pub(crate) fn esp32c6_rtc_bbpll_configure(xtal_freq: XtalClock, pll_freq: PllClock) { + esp32c6_rtc_bbpll_configure_raw(xtal_freq.mhz(), pll_freq.mhz()) +} + +pub(crate) fn esp32c6_rtc_bbpll_configure_raw(_xtal_freq: u32, pll_freq: u32) { + // clk_ll_bbpll_set_freq_mhz + // The target SPLL is fixed to 480MHz + // Do nothing + debug_assert!(pll_freq == 480); -pub(crate) fn esp32c6_rtc_bbpll_configure(_xtal_freq: XtalClock, _pll_freq: PllClock) { unsafe { // enable i2c mst clk by force on temporarily - (MODEM_LPCON_CLK_CONF_FORCE_ON_REG as *mut u32).write_volatile( - (MODEM_LPCON_CLK_CONF_FORCE_ON_REG as *mut u32).read_volatile() - | MODEM_LPCON_CLK_I2C_MST_FO, - ); - (MODEM_LPCON_I2C_MST_CLK_CONF_REG as *mut u32).write_volatile( - (MODEM_LPCON_I2C_MST_CLK_CONF_REG as *mut u32).read_volatile() - | MODEM_LPCON_CLK_I2C_MST_SEL_160M, - ); + let mut i2c_clock = ModemClockDevice::default(); + i2c_clock.set_i2c_master(true); + let i2c_clock = i2c_clock; + + modem_clock_device_enable(i2c_clock, true); + + modem_lpcon() + .i2c_mst_clk_conf() + .modify(|_, w| w.clk_i2c_mst_sel_160m().set_bit()); let i2c_mst_ana_conf0_reg_ptr = I2C_MST_ANA_CONF0_REG as *mut u32; // BBPLL CALIBRATION START @@ -128,6 +222,8 @@ pub(crate) fn esp32c6_rtc_bbpll_configure(_xtal_freq: XtalClock, _pll_freq: PllC i2c_mst_ana_conf0_reg_ptr.write_volatile( i2c_mst_ana_conf0_reg_ptr.read_volatile() & !I2C_MST_BBPLL_STOP_FORCE_LOW, ); + + modem_clock_device_enable(i2c_clock, false); } } @@ -147,21 +243,33 @@ pub(crate) fn esp32c6_rtc_bbpll_enable() { .modify(|_, w| w.tie_high_global_bbpll_icg().set_bit()); } -pub(crate) fn esp32c6_rtc_update_to_xtal(freq: XtalClock, _div: u8) { - let pcr = unsafe { &*crate::peripherals::PCR::PTR }; +pub(crate) fn esp32c6_rtc_update_to_xtal(freq: XtalClock, div: u8) { + esp32c6_rtc_update_to_xtal_raw(freq.mhz(), div) +} + +pub(crate) fn esp32c6_rtc_update_to_xtal_raw(freq_mhz: u32, div: u8) { + unsafe { + esp32c6_ahb_set_ls_divider(div); + esp32c6_cpu_set_ls_divider(div); + CpuClockSource::Xtal.select(); + ets_update_cpu_frequency(freq_mhz); + } +} + +pub(crate) fn esp32c6_rtc_update_to_8m() { unsafe { - ets_update_cpu_frequency(freq.mhz()); - // Set divider from XTAL to APB clock. Need to set divider to 1 (reg. value 0) - // first. - pcr.apb_freq_conf() - .modify(|_, w| w.apb_div_num().bits(0).apb_div_num().bits(_div - 1)); - - // Switch clock source - pcr.sysclk_conf().modify(|_, w| w.soc_clk_sel().bits(0)); + esp32c6_ahb_set_ls_divider(1); + esp32c6_cpu_set_ls_divider(1); + CpuClockSource::RcFast.select(); + ets_update_cpu_frequency(20); } } pub(crate) fn esp32c6_rtc_freq_to_pll_mhz(cpu_clock_speed: CpuClock) { + esp32c6_rtc_freq_to_pll_mhz_raw(cpu_clock_speed.mhz()); +} + +pub(crate) fn esp32c6_rtc_freq_to_pll_mhz_raw(cpu_clock_speed_mhz: u32) { // On ESP32C6, MSPI source clock's default HS divider leads to 120MHz, which is // unusable before calibration Therefore, before switching SOC_ROOT_CLK to // HS, we need to set MSPI source clock HS divider to make it run at @@ -172,7 +280,7 @@ pub(crate) fn esp32c6_rtc_freq_to_pll_mhz(cpu_clock_speed: CpuClock) { unsafe { pcr.cpu_freq_conf().modify(|_, w| { w.cpu_hs_div_num() - .bits(((480 / cpu_clock_speed.mhz() / 3) - 1) as u8) + .bits(((480 / cpu_clock_speed_mhz / 3) - 1) as u8) .cpu_hs_120m_force() .clear_bit() }); @@ -180,10 +288,9 @@ pub(crate) fn esp32c6_rtc_freq_to_pll_mhz(cpu_clock_speed: CpuClock) { pcr.cpu_freq_conf() .modify(|_, w| w.cpu_hs_120m_force().clear_bit()); - pcr.sysclk_conf().modify(|_, w| { - w.soc_clk_sel().bits(1) // PLL = 1 - }); - ets_update_cpu_frequency(cpu_clock_speed.mhz()); + CpuClockSource::PLL.select(); + + ets_update_cpu_frequency(cpu_clock_speed_mhz); } } @@ -286,19 +393,19 @@ fn regi2c_enable_block(block: u8) { // Before config I2C register, enable corresponding slave. match block { - REGI2C_BBPLL => { + v if v == REGI2C_BBPLL => { reg_set_bit(LP_I2C_ANA_MST_DEVICE_EN_REG, REGI2C_BBPLL_DEVICE_EN); } - REGI2C_BIAS => { + v if v == REGI2C_BIAS => { reg_set_bit(LP_I2C_ANA_MST_DEVICE_EN_REG, REGI2C_BIAS_DEVICE_EN); } - REGI2C_DIG_REG => { + v if v == REGI2C_DIG_REG => { reg_set_bit(LP_I2C_ANA_MST_DEVICE_EN_REG, REGI2C_DIG_REG_DEVICE_EN); } - REGI2C_ULP_CAL => { + v if v == REGI2C_ULP_CAL => { reg_set_bit(LP_I2C_ANA_MST_DEVICE_EN_REG, REGI2C_ULP_CAL_DEVICE_EN); } - REGI2C_SAR_I2C => { + v if v == REGI2C_SAR_I2C => { reg_set_bit(LP_I2C_ANA_MST_DEVICE_EN_REG, REGI2C_SAR_I2C_DEVICE_EN); } _ => (), @@ -307,19 +414,19 @@ fn regi2c_enable_block(block: u8) { fn regi2c_disable_block(block: u8) { match block { - REGI2C_BBPLL => { + v if v == REGI2C_BBPLL => { reg_clr_bit(LP_I2C_ANA_MST_DEVICE_EN_REG, REGI2C_BBPLL_DEVICE_EN); } - REGI2C_BIAS => { + v if v == REGI2C_BIAS => { reg_clr_bit(LP_I2C_ANA_MST_DEVICE_EN_REG, REGI2C_BIAS_DEVICE_EN); } - REGI2C_DIG_REG => { + v if v == REGI2C_DIG_REG => { reg_clr_bit(LP_I2C_ANA_MST_DEVICE_EN_REG, REGI2C_DIG_REG_DEVICE_EN); } - REGI2C_ULP_CAL => { + v if v == REGI2C_ULP_CAL => { reg_clr_bit(LP_I2C_ANA_MST_DEVICE_EN_REG, REGI2C_ULP_CAL_DEVICE_EN); } - REGI2C_SAR_I2C => { + v if v == REGI2C_SAR_I2C => { reg_clr_bit(LP_I2C_ANA_MST_DEVICE_EN_REG, REGI2C_SAR_I2C_DEVICE_EN); } _ => (), @@ -367,3 +474,51 @@ pub(crate) fn regi2c_write_mask(block: u8, _host_id: u8, reg_add: u8, msb: u8, l regi2c_disable_block(block); } + +// clk_ll_ahb_set_ls_divider +fn esp32c6_ahb_set_ls_divider(div: u8) { + unsafe { + pcr() + .ahb_freq_conf() + .modify(|_, w| w.ahb_ls_div_num().bits(div - 1)); + } +} + +// clk_ll_cpu_set_ls_divider +fn esp32c6_cpu_set_ls_divider(div: u8) { + unsafe { + pcr() + .cpu_freq_conf() + .modify(|_, w| w.cpu_ls_div_num().bits(div - 1)); + } +} + +// clk_ll_cpu_get_ls_divider +pub(crate) fn esp32c6_cpu_get_ls_divider() -> u8 { + unsafe { + let cpu_ls_div = pcr().cpu_freq_conf().read().cpu_ls_div_num().bits(); + let hp_root_ls_div = pcr().sysclk_conf().read().ls_div_num().bits(); + (hp_root_ls_div + 1) * (cpu_ls_div + 1) + } +} + +// clk_ll_cpu_get_hs_divider +pub(crate) fn esp32c6_cpu_get_hs_divider() -> u8 { + unsafe { + let force_120m = pcr().cpu_freq_conf().read().cpu_hs_120m_force().bit(); + let cpu_hs_div = pcr().cpu_freq_conf().read().cpu_hs_div_num().bits(); + if cpu_hs_div == 0 && force_120m { + return 4; + } + let hp_root_hs_div = pcr().sysclk_conf().read().hs_div_num().bits(); + (hp_root_hs_div + 1) * (cpu_hs_div + 1) + } +} + +// clk_ll_bbpll_get_freq_mhz +pub(crate) fn esp32c6_bbpll_get_freq_mhz() -> u32 { + // The target has a fixed 480MHz SPLL + const CLK_LL_PLL_480M_FREQ_MHZ: u32 = 480; + + CLK_LL_PLL_480M_FREQ_MHZ +} diff --git a/esp-hal-common/src/clock/mod.rs b/esp-hal-common/src/clock/mod.rs index e1427a736cb..044179bf083 100644 --- a/esp-hal-common/src/clock/mod.rs +++ b/esp-hal-common/src/clock/mod.rs @@ -145,7 +145,7 @@ impl Clock for XtalClock { } #[allow(unused)] -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] pub(crate) enum PllClock { #[cfg(esp32h2)] Pll8MHz, @@ -169,6 +169,33 @@ pub(crate) enum PllClock { Pll480MHz, } +impl Clock for PllClock { + fn frequency(&self) -> HertzU32 { + match self { + #[cfg(esp32h2)] + Self::Pll8MHz => HertzU32::MHz(8), + #[cfg(any(esp32c6, esp32h2))] + Self::Pll48MHz => HertzU32::MHz(48), + #[cfg(esp32h2)] + Self::Pll64MHz => HertzU32::MHz(64), + #[cfg(esp32c6)] + Self::Pll80MHz => HertzU32::MHz(80), + #[cfg(esp32h2)] + Self::Pll96MHz => HertzU32::MHz(96), + #[cfg(esp32c6)] + Self::Pll120MHz => HertzU32::MHz(120), + #[cfg(esp32c6)] + Self::Pll160MHz => HertzU32::MHz(160), + #[cfg(esp32c6)] + Self::Pll240MHz => HertzU32::MHz(240), + #[cfg(not(any(esp32c2, esp32c6, esp32h2)))] + Self::Pll320MHz => HertzU32::MHz(320), + #[cfg(not(esp32h2))] + Self::Pll480MHz => HertzU32::MHz(480), + } + } +} + #[allow(unused)] #[derive(Debug, Clone, Copy)] pub(crate) enum ApbClock { diff --git a/esp-hal-common/src/gpio.rs b/esp-hal-common/src/gpio.rs index dcaf8dc086a..cd767162c30 100644 --- a/esp-hal-common/src/gpio.rs +++ b/esp-hal-common/src/gpio.rs @@ -158,14 +158,14 @@ pub enum RtcFunction { pub trait RTCPin: Pin { #[cfg(xtensa)] fn rtc_number(&self) -> u8; - #[cfg(xtensa)] + #[cfg(any(xtensa, esp32c6))] fn rtc_set_config(&mut self, input_enable: bool, mux: bool, func: RtcFunction); fn rtcio_pad_hold(&mut self, enable: bool); // Unsafe because `level` needs to be a valid setting for the // rtc_cntl.gpio_wakeup.gpio_pinX_int_type - #[cfg(esp32c3)] + #[cfg(any(esp32c3, esp32c6))] unsafe fn apply_wakeup(&mut self, wakeup: bool, level: u8); } @@ -2549,6 +2549,72 @@ pub mod lp_gpio { } } } + + impl crate::gpio::RTCPin for GpioPin { + unsafe fn apply_wakeup(&mut self, wakeup: bool, level: u8) { + let lp_io = &*crate::peripherals::LP_IO::ptr(); + lp_io.[< pin $gpionum >]().modify(|_, w| { + w + .[< lp_gpio $gpionum _wakeup_enable >]().bit(wakeup) + .[< lp_gpio $gpionum _int_type >]().bits(level) + }); + } + + fn rtcio_pad_hold(&mut self, enable: bool) { + let mask = 1 << $gpionum; + unsafe { + let lp_aon = &*crate::peripherals::LP_AON::ptr(); + + lp_aon.gpio_hold0().modify(|r, w| { + if enable { + w.gpio_hold0().bits(r.gpio_hold0().bits() | mask) + } else { + w.gpio_hold0().bits(r.gpio_hold0().bits() & !mask) + } + }); + } + } + + /// Set the LP properties of the pin. If `mux` is true then then pin is + /// routed to LP_IO, when false it is routed to IO_MUX. + fn rtc_set_config(&mut self, input_enable: bool, mux: bool, func: crate::gpio::RtcFunction) { + let mask = 1 << $gpionum; + unsafe { + // Select LP_IO + let lp_aon = &*crate::peripherals::LP_AON::ptr(); + lp_aon + .gpio_mux() + .modify(|r, w| { + if mux { + w.sel().bits(r.sel().bits() | mask) + } else { + w.sel().bits(r.sel().bits() & !mask) + } + }); + + // Configure input, function and select normal operation registers + let lp_io = &*crate::peripherals::LP_IO::ptr(); + lp_io.[< gpio $gpionum >]().modify(|_, w| { + w + .[< lp_gpio $gpionum _slp_sel >]().bit(false) + .[< lp_gpio $gpionum _fun_ie >]().bit(input_enable) + .[< lp_gpio $gpionum _mcu_sel >]().bits(func as u8) + }); + } + } + } + + impl crate::gpio::RTCPinWithResistors for GpioPin { + fn rtcio_pullup(&mut self, enable: bool) { + let lp_io = unsafe { &*crate::peripherals::LP_IO::ptr() }; + lp_io.[< gpio $gpionum >]().modify(|_, w| w.[< lp_gpio $gpionum _fun_wpu >]().bit(enable)); + } + + fn rtcio_pulldown(&mut self, enable: bool) { + let lp_io = unsafe { &*crate::peripherals::LP_IO::ptr() }; + lp_io.[< gpio $gpionum >]().modify(|_, w| w.[< lp_gpio $gpionum _fun_wpd >]().bit(enable)); + } + } )+ } } diff --git a/esp-hal-common/src/rtc_cntl/mod.rs b/esp-hal-common/src/rtc_cntl/mod.rs index e933a967297..a29b23d47a8 100644 --- a/esp-hal-common/src/rtc_cntl/mod.rs +++ b/esp-hal-common/src/rtc_cntl/mod.rs @@ -83,7 +83,7 @@ use crate::efuse::Efuse; use crate::peripherals::{LP_TIMER, LP_WDT}; #[cfg(not(any(esp32c6, esp32h2)))] use crate::peripherals::{RTC_CNTL, TIMG0}; -#[cfg(any(esp32, esp32s3, esp32c3))] +#[cfg(any(esp32, esp32s3, esp32c3, esp32c6))] use crate::rtc_cntl::sleep::{RtcSleepConfig, WakeSource, WakeTriggers}; use crate::{ clock::Clock, @@ -92,7 +92,7 @@ use crate::{ Cpu, }; // only include sleep where its been implemented -#[cfg(any(esp32, esp32s3, esp32c3))] +#[cfg(any(esp32, esp32s3, esp32c3, esp32c6))] pub mod sleep; #[cfg(any(esp32c6, esp32h2))] @@ -107,7 +107,7 @@ type RtcCntl = crate::peripherals::RTC_CNTL; #[cfg_attr(esp32h2, path = "rtc/esp32h2.rs")] #[cfg_attr(esp32s2, path = "rtc/esp32s2.rs")] #[cfg_attr(esp32s3, path = "rtc/esp32s3.rs")] -mod rtc; +pub(crate) mod rtc; #[cfg(any(esp32c6, esp32h2))] pub use rtc::RtcClock; @@ -212,7 +212,7 @@ impl<'d> Rtc<'d> { swd: Swd::new(), }; - #[cfg(any(esp32, esp32s3, esp32c3))] + #[cfg(any(esp32, esp32s3, esp32c3, esp32c6))] RtcSleepConfig::base_settings(&this); this @@ -276,7 +276,7 @@ impl<'d> Rtc<'d> { } /// enter deep sleep and wake with the provided `wake_sources` - #[cfg(any(esp32, esp32s3, esp32c3))] + #[cfg(any(esp32, esp32s3, esp32c3, esp32c6))] pub fn sleep_deep<'a>( &mut self, wake_sources: &[&'a dyn WakeSource], @@ -288,7 +288,7 @@ impl<'d> Rtc<'d> { } /// enter light sleep and wake with the provided `wake_sources` - #[cfg(any(esp32, esp32s3, esp32c3))] + #[cfg(any(esp32, esp32s3, esp32c3, esp32c6))] pub fn sleep_light<'a>( &mut self, wake_sources: &[&'a dyn WakeSource], @@ -300,7 +300,7 @@ impl<'d> Rtc<'d> { /// enter sleep with the provided `config` and wake with the provided /// `wake_sources` - #[cfg(any(esp32, esp32s3, esp32c3))] + #[cfg(any(esp32, esp32s3, esp32c3, esp32c6))] pub fn sleep<'a>( &mut self, config: &RtcSleepConfig, diff --git a/esp-hal-common/src/rtc_cntl/rtc/esp32c6.rs b/esp-hal-common/src/rtc_cntl/rtc/esp32c6.rs index 6229c666f97..5b6355d5253 100644 --- a/esp-hal-common/src/rtc_cntl/rtc/esp32c6.rs +++ b/esp-hal-common/src/rtc_cntl/rtc/esp32c6.rs @@ -6,7 +6,20 @@ use fugit::HertzU32; use strum::FromRepr; use crate::{ - clock::{clocks_ll::regi2c_write_mask, Clock, XtalClock}, + clock::{ + clocks_ll::{ + esp32c6_bbpll_get_freq_mhz, + esp32c6_cpu_get_hs_divider, + esp32c6_cpu_get_ls_divider, + esp32c6_rtc_bbpll_configure_raw, + esp32c6_rtc_freq_to_pll_mhz_raw, + esp32c6_rtc_update_to_8m, + esp32c6_rtc_update_to_xtal_raw, + regi2c_write_mask, + }, + Clock, + XtalClock, + }, peripherals::TIMG0, soc::efuse::Efuse, system::RadioPeripherals, @@ -943,6 +956,16 @@ bitfield::bitfield! { pub bool, xpd_xtal , set_xpd_xtal : 31; } +#[derive(Clone, Copy, Default)] +// pmu_sleep_power_config_t.1 +pub struct LpSysPower { + // This is a best-guess assignment of the variants in the union `pmu_lp_power_t` union + // In esp-idf, all three fields are `pmu_lp_power_t` + pub dig_power: LpDigPower, + pub clk_power: LpClkPower, + pub xtal: LpXtalPower, +} + bitfield::bitfield! { #[derive(Clone, Copy, Default)] // pmu_lp_analog_t.0 @@ -1399,10 +1422,8 @@ pub struct RtcClock; impl RtcClock { const CAL_FRACT: u32 = 19; - /// Get main XTAL frequency - /// This is the value stored in RTC register RTC_XTAL_FREQ_REG by the - /// bootloader, as passed to rtc_clk_init function. - fn get_xtal_freq() -> XtalClock { + // rtc_clk_xtal_freq_get + fn get_xtal_freq_mhz() -> u32 { let xtal_freq_reg = unsafe { lp_aon().store4().read().bits() }; // Values of RTC_XTAL_FREQ_REG and RTC_APB_FREQ_REG are stored as two copies in @@ -1414,10 +1435,17 @@ impl RtcClock { let reg_val_to_clk_val = |val| val & u16::MAX as u32; if !clk_val_is_valid(xtal_freq_reg) { - return XtalClock::RtcXtalFreq40M; + return 40; } - match reg_val_to_clk_val(xtal_freq_reg) { + reg_val_to_clk_val(xtal_freq_reg) + } + + /// Get main XTAL frequency + /// This is the value stored in RTC register RTC_XTAL_FREQ_REG by the + /// bootloader, as passed to rtc_clk_init function. + fn get_xtal_freq() -> XtalClock { + match Self::get_xtal_freq_mhz() { 40 => XtalClock::RtcXtalFreq40M, other => XtalClock::RtcXtalFreqOther(other), } @@ -1475,7 +1503,7 @@ impl RtcClock { /// Calibration of RTC_SLOW_CLK is performed using a special feature of /// TIMG0. This feature counts the number of XTAL clock cycles within a /// given number of RTC_SLOW_CLK cycles. - fn calibrate_internal(mut cal_clk: RtcCalSel, slowclk_cycles: u32) -> u32 { + pub(crate) fn calibrate_internal(mut cal_clk: RtcCalSel, slowclk_cycles: u32) -> u32 { const SOC_CLK_RC_FAST_FREQ_APPROX: u32 = 17_500_000; const SOC_CLK_RC_SLOW_FREQ_APPROX: u32 = 136_000; const SOC_CLK_XTAL32K_FREQ_APPROX: u32 = 32768; @@ -1796,3 +1824,155 @@ impl RtcClock { (100_000_000 * 1000 / period) as u16 } } + +pub(crate) fn rtc_clk_cpu_freq_set_xtal() { + // rtc_clk_cpu_set_to_default_config + let freq = RtcClock::get_xtal_freq_mhz(); + + esp32c6_rtc_update_to_xtal_raw(freq, 1); + + // TODO: don't turn off the bbpll if some consumers depend on bbpll + // if !s_bbpll_digi_consumers_ref_count { + rtc_clk_bbpll_disable(); + //} +} + +#[derive(Clone, Copy, PartialEq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub(crate) struct UnsupportedClockSource; + +#[derive(Clone, Copy, PartialEq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub(crate) enum CpuClockSource { + Xtal, + PLL, + RcFast, +} + +impl CpuClockSource { + pub(crate) fn current() -> Result { + let source = match unsafe { pcr().sysclk_conf().read().soc_clk_sel().bits() } { + 0 => CpuClockSource::Xtal, + 1 => CpuClockSource::PLL, + 2 => CpuClockSource::RcFast, + _ => return Err(UnsupportedClockSource), + }; + + Ok(source) + } + + pub(crate) fn select(self) { + unsafe { + pcr().sysclk_conf().modify(|_, w| { + w.soc_clk_sel().bits(match self { + CpuClockSource::Xtal => 0, + CpuClockSource::PLL => 1, + CpuClockSource::RcFast => 2, + }) + }); + } + } +} + +#[derive(Clone, Copy)] +pub(crate) struct SavedClockConfig { + /// The clock from which CPU clock is derived + pub source: CpuClockSource, + + /// Source clock frequency + pub source_freq_mhz: u32, + + /// Divider, freq_mhz = SOC_ROOT_CLK freq_mhz / div + pub div: u8, +} + +impl SavedClockConfig { + pub(crate) fn save() -> Self { + let source = unwrap!(CpuClockSource::current()); + + let div; + let source_freq_mhz; + match source { + CpuClockSource::Xtal => { + div = esp32c6_cpu_get_ls_divider(); + source_freq_mhz = RtcClock::get_xtal_freq_mhz(); + } + CpuClockSource::PLL => { + div = esp32c6_cpu_get_hs_divider(); + source_freq_mhz = esp32c6_bbpll_get_freq_mhz(); + } + CpuClockSource::RcFast => { + div = esp32c6_cpu_get_ls_divider(); + source_freq_mhz = 20; + } + } + + SavedClockConfig { + source, + source_freq_mhz, + div, + } + } + + fn freq_mhz(&self) -> u32 { + self.source_freq_mhz / self.div as u32 + } + + // rtc_clk_cpu_freq_set_config + pub(crate) fn restore(self) { + let old_source = unwrap!(CpuClockSource::current()); + + match self.source { + CpuClockSource::Xtal => esp32c6_rtc_update_to_xtal_raw(self.freq_mhz(), self.div), + CpuClockSource::RcFast => esp32c6_rtc_update_to_8m(), + CpuClockSource::PLL => { + if old_source != CpuClockSource::PLL { + rtc_clk_bbpll_enable(); + esp32c6_rtc_bbpll_configure_raw( + RtcClock::get_xtal_freq_mhz(), + self.source_freq_mhz, + ); + } + esp32c6_rtc_freq_to_pll_mhz_raw(self.freq_mhz()); + } + } + + if old_source == CpuClockSource::PLL && self.source != CpuClockSource::PLL + // && !s_bbpll_digi_consumers_ref_count + { + // We don't turn off the bbpll if some consumers depend on bbpll + rtc_clk_bbpll_disable(); + } + } +} + +fn rtc_clk_bbpll_enable() { + unsafe { + pmu().imm_hp_ck_power().modify(|_, w| { + w.tie_high_xpd_bb_i2c() + .set_bit() + .tie_high_xpd_bbpll() + .set_bit() + .tie_high_xpd_bbpll_i2c() + .set_bit() + }); + pmu() + .imm_hp_ck_power() + .modify(|_, w| w.tie_high_global_bbpll_icg().set_bit()); + } +} + +fn rtc_clk_bbpll_disable() { + unsafe { + pmu() + .imm_hp_ck_power() + .modify(|_, w| w.tie_low_global_bbpll_icg().set_bit()); + + pmu().imm_hp_ck_power().modify(|_, w| { + w.tie_low_xpd_bbpll() + .set_bit() + .tie_low_xpd_bbpll_i2c() + .set_bit() + }); + } +} diff --git a/esp-hal-common/src/rtc_cntl/rtc/esp32_sleep.rs b/esp-hal-common/src/rtc_cntl/sleep/esp32.rs similarity index 100% rename from esp-hal-common/src/rtc_cntl/rtc/esp32_sleep.rs rename to esp-hal-common/src/rtc_cntl/sleep/esp32.rs diff --git a/esp-hal-common/src/rtc_cntl/rtc/esp32c3_sleep.rs b/esp-hal-common/src/rtc_cntl/sleep/esp32c3.rs similarity index 99% rename from esp-hal-common/src/rtc_cntl/rtc/esp32c3_sleep.rs rename to esp-hal-common/src/rtc_cntl/sleep/esp32c3.rs index 6bcf640eb09..9a298f82986 100644 --- a/esp-hal-common/src/rtc_cntl/rtc/esp32c3_sleep.rs +++ b/esp-hal-common/src/rtc_cntl/sleep/esp32c3.rs @@ -1,6 +1,6 @@ use super::{TimerWakeupSource, WakeSource, WakeTriggers, WakeupLevel}; use crate::{ - gpio::RTCPinWithResistors, + gpio::{RTCPinWithResistors, RtcFunction}, regi2c_write_mask, rtc_cntl::{sleep::RtcioWakeupSource, Clock, RtcClock}, Rtc, @@ -173,7 +173,7 @@ fn isolate_digital_gpio() { // make pad work as gpio (otherwise, deep_sleep bottom current will rise) io_mux .gpio(pin_num) - .modify(|_, w| w.mcu_sel().variant(PIN_FUNC_GPIO)); + .modify(|_, w| w.mcu_sel().variant(RtcFunction::Digital as u8)); } } } diff --git a/esp-hal-common/src/rtc_cntl/sleep/esp32c6.rs b/esp-hal-common/src/rtc_cntl/sleep/esp32c6.rs new file mode 100644 index 00000000000..17964593dd6 --- /dev/null +++ b/esp-hal-common/src/rtc_cntl/sleep/esp32c6.rs @@ -0,0 +1,973 @@ +use core::ops::Not; + +use crate::{ + efuse::Efuse, + gpio::{Pins, RtcFunction}, + peripherals::Peripherals, + rtc_cntl::{ + rtc::{ + rtc_clk_cpu_freq_set_xtal, + HpAnalog, + HpSysCntlReg, + HpSysPower, + LpAnalog, + LpSysPower, + RtcCalSel, + SavedClockConfig, + }, + sleep::{Ext1WakeupSource, WakeSource, WakeTriggers, WakeupLevel}, + RtcClock, + }, + Rtc, +}; + +impl Ext1WakeupSource<'_, '_> { + /// Returns the currently configured wakeup pins. + fn wakeup_pins() -> u8 { + unsafe { lp_aon().ext_wakeup_cntl().read().ext_wakeup_sel().bits() } + } + + fn wake_io_reset(gpio: &mut Pins) { + use crate::gpio::RTCPin; + + fn uninit_pin(pin: &mut impl RTCPin, wakeup_pins: u8) { + if wakeup_pins & (1 << pin.number()) != 0 { + pin.rtcio_pad_hold(false); + pin.rtc_set_config(false, false, RtcFunction::Rtc); + } + } + + let wakeup_pins = Ext1WakeupSource::wakeup_pins(); + uninit_pin(&mut gpio.gpio0, wakeup_pins); + uninit_pin(&mut gpio.gpio1, wakeup_pins); + uninit_pin(&mut gpio.gpio2, wakeup_pins); + uninit_pin(&mut gpio.gpio3, wakeup_pins); + uninit_pin(&mut gpio.gpio4, wakeup_pins); + uninit_pin(&mut gpio.gpio5, wakeup_pins); + uninit_pin(&mut gpio.gpio6, wakeup_pins); + uninit_pin(&mut gpio.gpio7, wakeup_pins); + } +} + +impl WakeSource for Ext1WakeupSource<'_, '_> { + fn apply(&self, _rtc: &Rtc, triggers: &mut WakeTriggers, _sleep_config: &mut RtcSleepConfig) { + // We don't have to keep the LP domain powered if we hold the wakeup pin states. + triggers.set_ext1(true); + + // set pins to RTC function + let mut pins = self.pins.borrow_mut(); + let mut pin_mask = 0u8; + let mut level_mask = 0u8; + for (pin, level) in pins.iter_mut() { + pin_mask |= 1 << pin.number(); + level_mask |= match level { + WakeupLevel::High => 1 << pin.number(), + WakeupLevel::Low => 0, + }; + + pin.rtc_set_config(true, true, RtcFunction::Rtc); + pin.rtcio_pad_hold(true); + } + + unsafe { + // clear previous wakeup status + lp_aon() + .ext_wakeup_cntl() + .modify(|_, w| w.ext_wakeup_status_clr().set_bit()); + + // set pin + level register fields + lp_aon().ext_wakeup_cntl().modify(|r, w| { + w.ext_wakeup_sel() + .bits(r.ext_wakeup_sel().bits() | pin_mask) + .ext_wakeup_lv() + .bits(r.ext_wakeup_lv().bits() & !pin_mask | level_mask) + }); + } + } +} + +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, _level) in pins.iter_mut() { + pin.rtc_set_config(true, false, RtcFunction::Rtc); + } + } +} + +#[derive(Clone, Copy)] +// pmu_sleep_analog_config_t +pub struct AnalogSleepConfig { + pub hp_sys: HpAnalog, + // pub lp_sys_active: LpAnalog, // unused + pub lp_sys_sleep: LpAnalog, +} + +impl AnalogSleepConfig { + fn defaults_deep_sleep() -> Self { + Self { + // PMU_SLEEP_ANALOG_DSLP_CONFIG_DEFAULT + hp_sys: { + let mut cfg = HpAnalog::default(); + + cfg.bias.set_pd_cur(false); + cfg.bias.set_bias_sleep(false); + cfg.regulator0.set_xpd(false); + cfg.bias.set_dbg_atten(0); + + cfg + }, + // lp_sys_active: LpAnalog::default(), + lp_sys_sleep: { + let mut cfg = LpAnalog::default(); + + cfg.regulator1.set_drv_b(0); + cfg.bias.set_pd_cur(true); + cfg.bias.set_bias_sleep(true); + cfg.regulator0.set_slp_xpd(false); + cfg.regulator0.set_slp_dbias(0); + cfg.regulator0.set_xpd(true); + cfg.bias.set_dbg_atten(12); + cfg.regulator0.set_dbias(23); // 0.7V + + cfg + }, + } + } + + fn defaults_light_sleep(pd_flags: PowerDownFlags) -> Self { + let mut this = Self { + // PMU_SLEEP_ANALOG_LSLP_CONFIG_DEFAULT + hp_sys: { + let mut cfg = HpAnalog::default(); + + cfg.regulator1.set_drv_b(0); + cfg.bias.set_pd_cur(true); + cfg.bias.set_bias_sleep(true); + cfg.regulator0.set_xpd(true); + cfg.bias.set_dbg_atten(0); + cfg.regulator0.set_dbias(1); // 0.6V + + cfg + }, + // lp_sys_active: LpAnalog::default(), + lp_sys_sleep: { + let mut cfg = LpAnalog::default(); + + cfg.regulator1.set_drv_b(0); + cfg.bias.set_pd_cur(true); + cfg.bias.set_bias_sleep(true); + cfg.regulator0.set_slp_xpd(false); + cfg.regulator0.set_slp_dbias(0); + cfg.regulator0.set_xpd(true); + cfg.bias.set_dbg_atten(0); + cfg.regulator0.set_dbias(12); // 0.7V + + cfg + }, + }; + + if !pd_flags.pd_xtal() { + this.hp_sys.bias.set_pd_cur(false); + this.hp_sys.bias.set_bias_sleep(false); + this.hp_sys.regulator0.set_dbias(25); + + this.lp_sys_sleep.bias.set_pd_cur(false); + this.lp_sys_sleep.bias.set_bias_sleep(false); + this.lp_sys_sleep.regulator0.set_dbias(26); + } + + this + } + + fn apply(&self) { + // pmu_sleep_analog_init + + unsafe { + // HP SLEEP (hp_sleep_*) + pmu().hp_sleep_bias().modify(|_, w| { + w.hp_sleep_dbg_atten() // pmu_ll_hp_set_dbg_atten + .bits(self.hp_sys.bias.dbg_atten() as u8) + .hp_sleep_pd_cur() // pmu_ll_hp_set_current_power_off + .bit(self.hp_sys.bias.pd_cur()) + .sleep() // pmu_ll_hp_set_bias_sleep_enable + .bit(self.hp_sys.bias.bias_sleep()) + }); + pmu().hp_sleep_hp_regulator0().modify(|_, w| { + w.hp_sleep_hp_regulator_xpd() // pmu_ll_hp_set_regulator_xpd + .bit(self.hp_sys.regulator0.xpd()) + .hp_sleep_hp_regulator_dbias() // pmu_ll_hp_set_regulator_dbias + .bits(self.hp_sys.regulator0.dbias() as u8) + }); + pmu().hp_sleep_hp_regulator1().modify(|_, w| { + w.hp_sleep_hp_regulator_drv_b() // pmu_ll_hp_set_regulator_driver_bar + .bits(self.hp_sys.regulator1.drv_b()) + }); + + // LP SLEEP (lp_sleep_*) + pmu().lp_sleep_bias().modify(|_, w| { + w.lp_sleep_dbg_atten() // pmu_ll_lp_set_dbg_atten + .bits(self.lp_sys_sleep.bias.dbg_atten() as u8) + .lp_sleep_pd_cur() // pmu_ll_lp_set_current_power_off + .bit(self.lp_sys_sleep.bias.pd_cur()) + .sleep() // pmu_ll_lp_set_bias_sleep_enable + .bit(self.lp_sys_sleep.bias.bias_sleep()) + }); + + pmu().lp_sleep_lp_regulator0().modify(|_, w| { + w.lp_sleep_lp_regulator_slp_xpd() // pmu_ll_lp_set_regulator_slp_xpd + .bit(self.lp_sys_sleep.regulator0.slp_xpd()) + .lp_sleep_lp_regulator_xpd() // pmu_ll_lp_set_regulator_xpd + .bit(self.lp_sys_sleep.regulator0.xpd()) + .lp_sleep_lp_regulator_slp_dbias() // pmu_ll_lp_set_regulator_sleep_dbias + .bits(self.lp_sys_sleep.regulator0.slp_dbias() as u8) + .lp_sleep_lp_regulator_dbias() // pmu_ll_lp_set_regulator_dbias + .bits(self.lp_sys_sleep.regulator0.dbias() as u8) + }); + + pmu().lp_sleep_lp_regulator1().modify(|_, w| { + w.lp_sleep_lp_regulator_drv_b() // pmu_ll_lp_set_regulator_driver_bar + .bits(self.lp_sys_sleep.regulator1.drv_b() as u8) + }); + } + } +} + +#[derive(Clone, Copy)] +// pmu_sleep_digital_config_t +pub struct DigitalSleepConfig { + pub syscntl: HpSysCntlReg, +} + +impl DigitalSleepConfig { + fn defaults_light_sleep(pd_flags: PowerDownFlags) -> Self { + Self { + // PMU_SLEEP_DIGITAL_LSLP_CONFIG_DEFAULT + syscntl: { + let mut cfg = HpSysCntlReg::default(); + + cfg.set_dig_pad_slp_sel(pd_flags.pd_top().not()); + + cfg + }, + } + } + + fn apply(&self) { + // pmu_sleep_digital_init + unsafe { + pmu().hp_sleep_hp_sys_cntl().modify(|_, w| { + w.hp_sleep_dig_pad_slp_sel() + .bit(self.syscntl.dig_pad_slp_sel()) + }) + }; + } +} + +#[derive(Clone, Copy)] +// pmu_sleep_power_config_t +pub struct PowerSleepConfig { + pub hp_sys: HpSysPower, + pub lp_sys_active: LpSysPower, + pub lp_sys_sleep: LpSysPower, +} + +impl PowerSleepConfig { + fn defaults(pd_flags: PowerDownFlags) -> Self { + let mut this = Self { + hp_sys: HpSysPower::default(), + lp_sys_active: LpSysPower::default(), + lp_sys_sleep: LpSysPower::default(), + }; + this.apply_flags(pd_flags); + this + } + + fn apply_flags(&mut self, pd_flags: PowerDownFlags) { + // PMU_SLEEP_POWER_CONFIG_DEFAULT + self.hp_sys + .dig_power + .set_vdd_spi_pd_en(pd_flags.pd_vddsdio()); + self.hp_sys.dig_power.set_wifi_pd_en(pd_flags.pd_modem()); + self.hp_sys.dig_power.set_cpu_pd_en(pd_flags.pd_cpu()); + self.hp_sys.dig_power.set_aon_pd_en(pd_flags.pd_hp_aon()); + self.hp_sys.dig_power.set_top_pd_en(pd_flags.pd_top()); + + self.hp_sys.clk.set_i2c_iso_en(true); + self.hp_sys.clk.set_i2c_retention(true); + + self.hp_sys.xtal.set_xpd_xtal(pd_flags.pd_xtal().not()); + + self.lp_sys_active.clk_power.set_xpd_xtal32k(true); + self.lp_sys_active.clk_power.set_xpd_rc32k(true); + self.lp_sys_active.clk_power.set_xpd_fosc(true); + + self.lp_sys_sleep + .dig_power + .set_peri_pd_en(pd_flags.pd_lp_periph()); + self.lp_sys_sleep.dig_power.set_mem_dslp(true); + + self.lp_sys_sleep + .clk_power + .set_xpd_xtal32k(pd_flags.pd_xtal32k().not()); + self.lp_sys_sleep + .clk_power + .set_xpd_rc32k(pd_flags.pd_rc32k().not()); + self.lp_sys_sleep + .clk_power + .set_xpd_fosc(pd_flags.pd_rc_fast().not()); + + self.lp_sys_sleep + .xtal + .set_xpd_xtal(pd_flags.pd_xtal().not()); + } + + fn apply(&self) { + // pmu_sleep_power_init + + unsafe { + // HP SLEEP (hp_sleep_*) + pmu() + .hp_sleep_dig_power() + .modify(|_, w| w.bits(self.hp_sys.dig_power.0)); + pmu() + .hp_sleep_hp_ck_power() + .modify(|_, w| w.bits(self.hp_sys.clk.0)); + pmu() + .hp_sleep_xtal() + .modify(|_, w| w.hp_sleep_xpd_xtal().bit(self.hp_sys.xtal.xpd_xtal())); + + // LP ACTIVE (hp_sleep_lp_*) + pmu() + .hp_sleep_lp_dig_power() + .modify(|_, w| w.bits(self.lp_sys_active.dig_power.0)); + pmu() + .hp_sleep_lp_ck_power() + .modify(|_, w| w.bits(self.lp_sys_active.clk_power.0)); + + // LP SLEEP (lp_sleep_*) + pmu() + .lp_sleep_lp_dig_power() + .modify(|_, w| w.bits(self.lp_sys_sleep.dig_power.0)); + pmu() + .lp_sleep_lp_ck_power() + .modify(|_, w| w.bits(self.lp_sys_sleep.clk_power.0)); + pmu() + .lp_sleep_xtal() + .modify(|_, w| w.lp_sleep_xpd_xtal().bit(self.lp_sys_sleep.xtal.xpd_xtal())); + } + } +} + +#[derive(Clone, Copy)] +// pmu_hp_param_t +pub struct HpParam { + pub modem_wakeup_wait_cycle: u32, + pub analog_wait_target_cycle: u16, + pub digital_power_down_wait_cycle: u16, + pub digital_power_supply_wait_cycle: u16, + pub digital_power_up_wait_cycle: u16, + pub pll_stable_wait_cycle: u16, + pub modify_icg_cntl_wait_cycle: u8, + pub switch_icg_cntl_wait_cycle: u8, + pub min_slp_slow_clk_cycle: u8, +} + +#[derive(Clone, Copy)] +// pmu_lp_param_t +pub struct LpParam { + pub digital_power_supply_wait_cycle: u16, + pub min_slp_slow_clk_cycle: u8, + pub analog_wait_target_cycle: u8, + pub digital_power_down_wait_cycle: u8, + pub digital_power_up_wait_cycle: u8, +} + +#[derive(Clone, Copy)] +// pmu_hp_lp_param_t +pub struct HpLpParam { + // union of two u16 variants, I've elected to not complicate things... + pub xtal_stable_wait_cycle: u16, +} + +#[derive(Clone, Copy)] +// pmu_sleep_param_config_t +pub struct ParamSleepConfig { + pub hp_sys: HpParam, + pub lp_sys: LpParam, + pub hp_lp: HpLpParam, +} +impl ParamSleepConfig { + const PMU_SLEEP_PARAM_CONFIG_DEFAULT: Self = Self { + hp_sys: HpParam { + min_slp_slow_clk_cycle: 10, + analog_wait_target_cycle: 2419, + digital_power_supply_wait_cycle: 32, + digital_power_up_wait_cycle: 32, + modem_wakeup_wait_cycle: 20700, + pll_stable_wait_cycle: 2, + + digital_power_down_wait_cycle: 0, + modify_icg_cntl_wait_cycle: 0, + switch_icg_cntl_wait_cycle: 0, + }, + lp_sys: LpParam { + min_slp_slow_clk_cycle: 10, + analog_wait_target_cycle: 23, + digital_power_supply_wait_cycle: 32, + digital_power_up_wait_cycle: 32, + + digital_power_down_wait_cycle: 0, + }, + hp_lp: HpLpParam { + xtal_stable_wait_cycle: 30, + }, + }; + + fn apply(&self) { + // pmu_sleep_param_init + + unsafe { + pmu().slp_wakeup_cntl3().modify(|_, w| { + w.hp_min_slp_val() // pmu_ll_hp_set_min_sleep_cycle + .bits(self.hp_sys.min_slp_slow_clk_cycle) + .lp_min_slp_val() // pmu_ll_lp_set_min_sleep_cycle + .bits(self.lp_sys.min_slp_slow_clk_cycle) + }); + + pmu().slp_wakeup_cntl7().modify(|_, w| { + w.ana_wait_target() // pmu_ll_hp_set_analog_wait_target_cycle + .bits(self.hp_sys.analog_wait_target_cycle) + }); + + pmu().power_wait_timer0().modify(|_, w| { + w.dg_hp_wait_timer() // pmu_ll_hp_set_digital_power_supply_wait_cycle + .bits(self.hp_sys.digital_power_supply_wait_cycle) + .dg_hp_powerup_timer() // pmu_ll_hp_set_digital_power_up_wait_cycle + .bits(self.hp_sys.digital_power_up_wait_cycle) + }); + + pmu().power_wait_timer1().modify(|_, w| { + w.dg_lp_wait_timer() // pmu_ll_lp_set_digital_power_supply_wait_cycle + .bits(self.lp_sys.digital_power_supply_wait_cycle) + .dg_lp_powerup_timer() // pmu_ll_lp_set_digital_power_up_wait_cycle + .bits(self.lp_sys.digital_power_up_wait_cycle) + }); + + pmu().slp_wakeup_cntl5().modify(|_, w| { + w.lp_ana_wait_target() // pmu_ll_lp_set_analog_wait_target_cycle + .bits(self.lp_sys.analog_wait_target_cycle) + .modem_wait_target() // pmu_ll_hp_set_modem_wakeup_wait_cycle + .bits(self.hp_sys.modem_wakeup_wait_cycle) + }); + pmu().power_ck_wait_cntl().modify(|_, w| { + w.wait_xtl_stable() // pmu_ll_hp_set_xtal_stable_wait_cycle + .bits(self.hp_lp.xtal_stable_wait_cycle) + .wait_pll_stable() // pmu_ll_hp_set_pll_stable_wait_cycle + .bits(self.hp_sys.pll_stable_wait_cycle) + }); + } + } + + fn defaults(config: SleepTimeConfig, pd_flags: PowerDownFlags, pd_xtal: bool) -> Self { + let mut param = Self::PMU_SLEEP_PARAM_CONFIG_DEFAULT; + + // pmu_sleep_param_config_default + param.hp_sys.min_slp_slow_clk_cycle = + config.us_to_slowclk(MachineConstants::HP_MIN_SLP_TIME_US) as u8; + param.hp_sys.analog_wait_target_cycle = + config.us_to_fastclk(MachineConstants::HP_ANALOG_WAIT_TIME_US) as u16; + param.hp_sys.digital_power_supply_wait_cycle = + config.us_to_fastclk(MachineConstants::HP_POWER_SUPPLY_WAIT_TIME_US) as u16; + param.hp_sys.digital_power_up_wait_cycle = + config.us_to_fastclk(MachineConstants::HP_POWER_UP_WAIT_TIME_US) as u16; + param.hp_sys.pll_stable_wait_cycle = + config.us_to_fastclk(MachineConstants::HP_PLL_WAIT_STABLE_TIME_US) as u16; + + let hw_wait_time_us = config.pmu_sleep_calculate_hw_wait_time(pd_flags); + + let modem_wakeup_wait_time_us = (config.sleep_time_adjustment + + MachineConstants::MODEM_STATE_SKIP_TIME_US + + MachineConstants::HP_REGDMA_RF_ON_WORK_TIME_US) + .saturating_sub(hw_wait_time_us); + param.hp_sys.modem_wakeup_wait_cycle = config.us_to_fastclk(modem_wakeup_wait_time_us); + + param.lp_sys.min_slp_slow_clk_cycle = + config.us_to_slowclk(MachineConstants::LP_MIN_SLP_TIME_US) as u8; + param.lp_sys.analog_wait_target_cycle = + config.us_to_slowclk(MachineConstants::LP_ANALOG_WAIT_TIME_US) as u8; + param.lp_sys.digital_power_supply_wait_cycle = + config.us_to_fastclk(MachineConstants::LP_POWER_SUPPLY_WAIT_TIME_US) as u16; + param.lp_sys.digital_power_up_wait_cycle = + config.us_to_fastclk(MachineConstants::LP_POWER_UP_WAIT_TIME_US) as u8; + + // This looks different from esp-idf but it is the same: + // Both `xtal_stable_wait_cycle` and `xtal_stable_wait_slow_clk_cycle` are + // u16 variants of the same union + param.hp_lp.xtal_stable_wait_cycle = if pd_xtal { + config.us_to_slowclk(MachineConstants::LP_XTAL_WAIT_STABLE_TIME_US) as u16 + } else { + config.us_to_fastclk(MachineConstants::HP_XTAL_WAIT_STABLE_TIME_US) as u16 + }; + + param + } +} + +#[derive(Clone, Copy)] +struct SleepTimeConfig { + sleep_time_adjustment: u32, + // TODO: esp-idf does some calibration here to determine slowclk_period + slowclk_period: u32, + fastclk_period: u32, +} + +const CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ: u32 = 160; + +impl SleepTimeConfig { + const RTC_CLK_CAL_FRACT: u32 = 19; + + fn rtc_clk_cal_fast(mut slowclk_cycles: u32) -> u32 { + let xtal_freq = 40; // esp-idf has a very complicated way of determining this + + // The Fosc CLK of calibration circuit is divided by 32 for ECO1. + // So we need to divide the calibrate cycles of the FOSC for ECO1 and above + // chips by 32 to avoid excessive calibration time. + if Efuse::chip_revision() >= 1 { + slowclk_cycles = slowclk_cycles / 32; + } + + let xtal_cycles = + RtcClock::calibrate_internal(RtcCalSel::RtcCalRcFast, slowclk_cycles) as u64; + + let divider: u64 = xtal_freq as u64 * slowclk_cycles as u64; + let period_64: u64 = ((xtal_cycles << Self::RTC_CLK_CAL_FRACT) + divider / 2 - 1) / divider; + (period_64 & (u32::MAX as u64)) as u32 + } + + fn new(_deep: bool) -> Self { + // https://github.com/espressif/esp-idf/commit/e1d24ebd7f43c7c7ded183bc8800b20af3bf014b + + // Calibrate rtc slow clock + // TODO: do an actual calibration instead of a read + let slowclk_period = unsafe { lp_aon().store1().read().lp_aon_store1().bits() }; + + // Calibrate rtc fast clock, only PMU supported chips sleep process is needed. + const FAST_CLK_SRC_CAL_CYCLES: u32 = 2048; + let fastclk_period = Self::rtc_clk_cal_fast(FAST_CLK_SRC_CAL_CYCLES); + + Self { + sleep_time_adjustment: 0, + slowclk_period, + fastclk_period, + } + } + + fn light_sleep(pd_flags: PowerDownFlags) -> Self { + const LIGHT_SLEEP_TIME_OVERHEAD_US: u32 = 56; + + let mut this = Self::new(false); + + let sw = LIGHT_SLEEP_TIME_OVERHEAD_US; // TODO + let hw = this.pmu_sleep_calculate_hw_wait_time(pd_flags); + + this.sleep_time_adjustment = sw + hw; + + this + } + + fn deep_sleep() -> Self { + let mut this = Self::new(true); + + this.sleep_time_adjustment = 250 + 100 * 240 / CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ; + + this + } + + fn us_to_slowclk(&self, us: u32) -> u32 { + (us << Self::RTC_CLK_CAL_FRACT) / self.slowclk_period + } + + fn slowclk_to_us(&self, rtc_cycles: u32) -> u32 { + (rtc_cycles * self.slowclk_period) >> Self::RTC_CLK_CAL_FRACT + } + + fn us_to_fastclk(&self, us: u32) -> u32 { + (us << Self::RTC_CLK_CAL_FRACT) / self.fastclk_period + } + + fn pmu_sleep_calculate_hw_wait_time(&self, pd_flags: PowerDownFlags) -> u32 { + // LP core hardware wait time, microsecond + let lp_wakeup_wait_time_us = self.slowclk_to_us(MachineConstants::LP_WAKEUP_WAIT_CYCLE); + let lp_clk_switch_time_us = self.slowclk_to_us(MachineConstants::LP_CLK_SWITCH_CYCLE); + let lp_clk_power_on_wait_time_us = if pd_flags.pd_xtal() { + MachineConstants::LP_XTAL_WAIT_STABLE_TIME_US + } else { + self.slowclk_to_us(MachineConstants::LP_CLK_POWER_ON_WAIT_CYCLE) + }; + + let lp_hw_wait_time_us = MachineConstants::LP_MIN_SLP_TIME_US + + MachineConstants::LP_ANALOG_WAIT_TIME_US + + lp_clk_power_on_wait_time_us + + lp_wakeup_wait_time_us + + lp_clk_switch_time_us + + MachineConstants::LP_POWER_SUPPLY_WAIT_TIME_US + + MachineConstants::LP_POWER_UP_WAIT_TIME_US; + + // HP core hardware wait time, microsecond + let hp_digital_power_up_wait_time_us = MachineConstants::HP_POWER_SUPPLY_WAIT_TIME_US + + MachineConstants::HP_POWER_UP_WAIT_TIME_US; + let hp_regdma_wait_time_us = u32::max( + MachineConstants::HP_REGDMA_S2M_WORK_TIME_US + + MachineConstants::HP_REGDMA_M2A_WORK_TIME_US, + MachineConstants::HP_REGDMA_S2A_WORK_TIME_US, + ); + let hp_clock_wait_time_us = MachineConstants::HP_XTAL_WAIT_STABLE_TIME_US + + MachineConstants::HP_PLL_WAIT_STABLE_TIME_US; + + let hp_hw_wait_time_us = MachineConstants::HP_ANALOG_WAIT_TIME_US + + u32::max( + hp_digital_power_up_wait_time_us + hp_regdma_wait_time_us, + hp_clock_wait_time_us, + ); + + #[rustfmt::skip] // ASCII art + /* When the SOC wakeup (lp timer or GPIO wakeup) and Modem wakeup (Beacon wakeup) complete, + * the soc wakeup will be delayed until the RF is turned on in Modem state. + * + * modem wakeup TBTT, RF on by HW + * | | + * \|/ \|/ + * PMU_HP_ACTIVE /------ + * PMU_HP_MODEM /------------////////////////// + * PMU_HP_SLEEP ----------------------////////////////// + * /|\ /|\ /|\ /|\ /|\ /|\ + * |<- some hw wait ->| | | |<- M2A switch ->| + * | slow cycles & | soc wakeup | | + * | FOSC cycles |<- S2M switch ->| | + * | | + * |<-- PMU guard time, also the maximum time for the SOC -->| + * | wake-up delay | + */ + const CONFIG_ESP_WIFI_ENHANCED_LIGHT_SLEEP: bool = true; + + let (rf_on_protect_time_us, sync_time_us) = if CONFIG_ESP_WIFI_ENHANCED_LIGHT_SLEEP { + ( + MachineConstants::HP_REGDMA_RF_ON_WORK_TIME_US, + MachineConstants::HP_CLOCK_DOMAIN_SYNC_TIME_US, + ) + } else { + (0, 0) + }; + + lp_hw_wait_time_us + hp_hw_wait_time_us + sync_time_us + rf_on_protect_time_us + } +} + +#[derive(Clone, Copy)] +// pmu_sleep_config_t + deep sleep flag + pd flags +pub struct RtcSleepConfig { + pub deep: bool, + pub pd_flags: PowerDownFlags, +} + +impl Default for RtcSleepConfig { + fn default() -> Self { + // from pmu_sleep_param_config_default + // sleep flags will be applied by wakeup methods and apply + + Self { + deep: false, + pd_flags: { + let mut pd_flags = PowerDownFlags(0); + + pd_flags.set_pd_lp_periph(true); + pd_flags.set_pd_cpu(true); + pd_flags.set_pd_xtal32k(true); + pd_flags.set_pd_rc32k(true); + pd_flags.set_pd_rc_fast(true); + pd_flags.set_pd_xtal(true); + pd_flags.set_pd_top(true); + pd_flags.set_pd_modem(true); + pd_flags.set_pd_vddsdio(true); + + pd_flags + }, + } + } +} + +unsafe fn pmu<'a>() -> &'a esp32c6::pmu::RegisterBlock { + &*esp32c6::PMU::ptr() +} + +unsafe fn lp_aon<'a>() -> &'a esp32c6::lp_aon::RegisterBlock { + &*esp32c6::LP_AON::ptr() +} + +bitfield::bitfield! { + #[derive(Clone, Copy)] + /// Power domains to be powered down during sleep + pub struct PowerDownFlags(u32); + + pub u32, pd_top , set_pd_top : 0; + pub u32, pd_vddsdio , set_pd_vddsdio : 1; + pub u32, pd_modem , set_pd_modem : 2; + pub u32, pd_hp_periph, set_pd_hp_periph: 3; + pub u32, pd_cpu , set_pd_cpu : 4; + pub u32, pd_hp_aon , set_pd_hp_aon : 5; + pub u32, pd_mem_g0 , set_pd_mem_g0 : 6; + pub u32, pd_mem_g1 , set_pd_mem_g1 : 7; + pub u32, pd_mem_g2 , set_pd_mem_g2 : 8; + pub u32, pd_mem_g3 , set_pd_mem_g3 : 9; + pub u32, pd_xtal , set_pd_xtal : 10; + pub u32, pd_rc_fast , set_pd_rc_fast : 11; + pub u32, pd_xtal32k , set_pd_xtal32k : 12; + pub u32, pd_rc32k , set_pd_rc32k : 13; + pub u32, pd_lp_periph, set_pd_lp_periph: 14; +} + +impl PowerDownFlags { + pub fn pd_mem(self) -> bool { + self.pd_mem_g0() && self.pd_mem_g1() && self.pd_mem_g2() && self.pd_mem_g3() + } + + pub fn set_pd_mem(&mut self, value: bool) { + self.set_pd_mem_g0(value); + self.set_pd_mem_g1(value); + self.set_pd_mem_g2(value); + self.set_pd_mem_g3(value); + } +} + +// Constants defined in `PMU_SLEEP_MC_DEFAULT()` +struct MachineConstants; +impl MachineConstants { + const LP_MIN_SLP_TIME_US: u32 = 450; + const LP_WAKEUP_WAIT_CYCLE: u32 = 4; + const LP_ANALOG_WAIT_TIME_US: u32 = 154; + const LP_XTAL_WAIT_STABLE_TIME_US: u32 = 250; + const LP_CLK_SWITCH_CYCLE: u32 = 1; + const LP_CLK_POWER_ON_WAIT_CYCLE: u32 = 1; + const LP_POWER_SUPPLY_WAIT_TIME_US: u32 = 2; + const LP_POWER_UP_WAIT_TIME_US: u32 = 2; + + const HP_MIN_SLP_TIME_US: u32 = 450; + const HP_CLOCK_DOMAIN_SYNC_TIME_US: u32 = 150; + const HP_SYSTEM_DFS_UP_WORK_TIME_US: u32 = 124; + const HP_ANALOG_WAIT_TIME_US: u32 = 154; + const HP_POWER_SUPPLY_WAIT_TIME_US: u32 = 2; + const HP_POWER_UP_WAIT_TIME_US: u32 = 2; + const HP_REGDMA_S2M_WORK_TIME_US: u32 = 172; + const HP_REGDMA_S2A_WORK_TIME_US: u32 = 480; + const HP_REGDMA_M2A_WORK_TIME_US: u32 = 278; + // Unused, but defined in esp-idf. May be needed later. + // const HP_REGDMA_A2S_WORK_TIME_US: u32 = 382; + const HP_REGDMA_RF_ON_WORK_TIME_US: u32 = 70; + // Unused, but defined in esp-idf. May be needed later. + // const HP_REGDMA_RF_OFF_WORK_TIME_US: u32 = 23; + const HP_XTAL_WAIT_STABLE_TIME_US: u32 = 250; + const HP_PLL_WAIT_STABLE_TIME_US: u32 = 1; + + const MODEM_STATE_SKIP_TIME_US: u32 = Self::HP_REGDMA_M2A_WORK_TIME_US + + Self::HP_SYSTEM_DFS_UP_WORK_TIME_US + + Self::LP_MIN_SLP_TIME_US; +} + +impl RtcSleepConfig { + pub fn deep_slp(&self) -> bool { + self.deep + } + + pub fn deep() -> Self { + // Set up for ultra-low power sleep. Wakeup sources may modify these settings. + let mut cfg = Self::default(); + + cfg.deep = true; + + cfg + } + + pub(crate) fn base_settings(_rtc: &Rtc) { + Self::wake_io_reset(); + } + + fn wake_io_reset() { + // loosely based on esp_deep_sleep_wakeup_io_reset + + use crate::gpio::GpioExt; + + let peripherals = unsafe { + // We're stealing peripherals to do some uninitialization after waking up from + // deep sleep. We have to be careful to only touch settings that were enabled + // by deep sleep setup. + Peripherals::steal() + }; + let mut gpio = peripherals.GPIO.split(); + + Ext1WakeupSource::wake_io_reset(&mut gpio); + } + + /// Finalize power-down flags, apply configuration based on the flags. + pub(crate) fn apply(&mut self) { + if self.deep { + // force-disable certain power domains + self.pd_flags.set_pd_top(true); + self.pd_flags.set_pd_vddsdio(true); + self.pd_flags.set_pd_modem(true); + self.pd_flags.set_pd_hp_periph(true); + self.pd_flags.set_pd_cpu(true); + self.pd_flags.set_pd_mem(true); + self.pd_flags.set_pd_xtal(true); + self.pd_flags.set_pd_hp_aon(true); + } + } + + /// Configures wakeup options and enters sleep. + /// + /// This function does not return if deep sleep is requested. + pub(crate) fn start_sleep(&self, wakeup_triggers: WakeTriggers) { + const PMU_EXT0_WAKEUP_EN: u32 = 1 << 0; + const PMU_EXT1_WAKEUP_EN: u32 = 1 << 1; + const PMU_GPIO_WAKEUP_EN: u32 = 1 << 2; + const PMU_LP_TIMER_WAKEUP_EN: u32 = 1 << 4; + const PMU_WIFI_SOC_WAKEUP_EN: u32 = 1 << 5; + const PMU_UART0_WAKEUP_EN: u32 = 1 << 6; + const PMU_UART1_WAKEUP_EN: u32 = 1 << 7; + const PMU_SDIO_WAKEUP_EN: u32 = 1 << 8; + const PMU_BLE_SOC_WAKEUP_EN: u32 = 1 << 10; + const PMU_LP_CORE_WAKEUP_EN: u32 = 1 << 11; + const PMU_USB_WAKEUP_EN: u32 = 1 << 14; + const MODEM_REJECT: u32 = 1 << 16; + + const RTC_SLEEP_REJECT_MASK: u32 = PMU_EXT0_WAKEUP_EN + | PMU_EXT1_WAKEUP_EN + | PMU_GPIO_WAKEUP_EN + | PMU_LP_TIMER_WAKEUP_EN + | PMU_WIFI_SOC_WAKEUP_EN + | PMU_UART0_WAKEUP_EN + | PMU_UART1_WAKEUP_EN + | PMU_SDIO_WAKEUP_EN + | PMU_BLE_SOC_WAKEUP_EN + | PMU_LP_CORE_WAKEUP_EN + | PMU_USB_WAKEUP_EN; + + let wakeup_mask = wakeup_triggers.0 as u32; + let reject_mask = if self.deep { + 0 + } else { + // TODO: MODEM_REJECT if s_sleep_modem.wifi.phy_link != NULL + let reject_mask = RTC_SLEEP_REJECT_MASK | MODEM_REJECT; + wakeup_mask & reject_mask + }; + + let cpu_freq_config = SavedClockConfig::save(); + rtc_clk_cpu_freq_set_xtal(); + + // pmu_sleep_config_default + pmu_sleep_init. + + let power = PowerSleepConfig::defaults(self.pd_flags); + power.apply(); + + // Needs to happen after rtc_clk_cpu_freq_set_xtal + let config = if self.deep { + SleepTimeConfig::deep_sleep() + } else { + SleepTimeConfig::light_sleep(self.pd_flags) + }; + + let mut param = + ParamSleepConfig::defaults(config, self.pd_flags, power.hp_sys.xtal.xpd_xtal()); + + if self.deep { + const PMU_LP_ANALOG_WAIT_TARGET_TIME_DSLP_US: u32 = 500; + param.lp_sys.analog_wait_target_cycle = + config.us_to_slowclk(PMU_LP_ANALOG_WAIT_TARGET_TIME_DSLP_US) as u8; + + AnalogSleepConfig::defaults_deep_sleep().apply(); + } else { + AnalogSleepConfig::defaults_light_sleep(self.pd_flags).apply(); + DigitalSleepConfig::defaults_light_sleep(self.pd_flags).apply(); + } + + param.apply(); + + // like esp-idf pmu_sleep_start() + + unsafe { + // lp_aon_hal_inform_wakeup_type - tells ROM which wakeup stub to run + lp_aon() + .store9() + .modify(|r, w| w.bits(r.bits() & !0x01 | self.deep as u32)); + + // pmu_ll_hp_set_wakeup_enable + pmu().slp_wakeup_cntl2().write(|w| w.bits(wakeup_mask)); + + // pmu_ll_hp_set_reject_enable + pmu().slp_wakeup_cntl1().modify(|_, w| { + w.slp_reject_en() + .bit(true) + .sleep_reject_ena() + .bits(reject_mask) + }); + + // pmu_ll_hp_clear_reject_cause + pmu() + .slp_wakeup_cntl4() + .write(|w| w.slp_reject_cause_clr().bit(true)); + + pmu().hp_int_clr().write(|w| { + w.sw_int_clr() // pmu_ll_hp_clear_sw_intr_status + .bit(true) + .soc_sleep_reject_int_clr() // pmu_ll_hp_clear_reject_intr_status + .bit(true) + .soc_wakeup_int_clr() // pmu_ll_hp_clear_wakeup_intr_status + .bit(true) + }); + + // misc_modules_sleep_prepare + + // TODO: IDF-7370 + #[cfg(not(pmu))] + if !(self.deep && wakeup_triggers.touch) { + let saradc = &*esp32c6::APB_SARADC::ptr(); + saradc + .ctrl() + .modify(|_, w| w.saradc2_pwdet_drv().bit(false)); + } + + // Start entry into sleep mode + + // pmu_ll_hp_set_sleep_enable + pmu().slp_wakeup_cntl0().write(|w| w.sleep_req().bit(true)); + + // In pd_cpu lightsleep and deepsleep mode, we never get here + loop { + let int_raw = pmu().int_raw().read(); + if int_raw.soc_wakeup_int_raw().bit_is_set() + || int_raw.soc_sleep_reject_int_raw().bit_is_set() + { + break; + } + } + } + + // esp-idf returns if the sleep was rejected, we don't return anything + + cpu_freq_config.restore(); + } + + /// Cleans up after sleep + pub(crate) fn finish_sleep(&self) { + // like esp-idf pmu_sleep_finish() + // In "pd_cpu lightsleep" and "deepsleep" modes we never get here + + // esp-idf returns if the sleep was rejected, we do nothing + // pmu_ll_hp_is_sleep_reject(PMU_instance()->hal->dev) + + Self::wake_io_reset(); + } +} diff --git a/esp-hal-common/src/rtc_cntl/rtc/esp32s3_sleep.rs b/esp-hal-common/src/rtc_cntl/sleep/esp32s3.rs similarity index 100% rename from esp-hal-common/src/rtc_cntl/rtc/esp32s3_sleep.rs rename to esp-hal-common/src/rtc_cntl/sleep/esp32s3.rs diff --git a/esp-hal-common/src/rtc_cntl/sleep.rs b/esp-hal-common/src/rtc_cntl/sleep/mod.rs similarity index 56% rename from esp-hal-common/src/rtc_cntl/sleep.rs rename to esp-hal-common/src/rtc_cntl/sleep/mod.rs index ebe12d68cb9..f5b50d7d1f8 100644 --- a/esp-hal-common/src/rtc_cntl/sleep.rs +++ b/esp-hal-common/src/rtc_cntl/sleep/mod.rs @@ -1,7 +1,8 @@ //! # RTC Control Sleep Module //! //! ## Overview -//! The `sleep` module in the `RTC CNTL (Real-Time Clock Control)` driver +//! +//! The `sleep` module in the `RTC CNTL (Real-Time Control)` driver //! provides functionality to manage sleep and wakeup sources for `ESP` chips. //! The `RTC_CNTL` is responsible for controlling the power and sleep behavior //! of the chip. @@ -19,18 +20,23 @@ //! * `ULP (Ultra-Low Power)` wake //! * `BT (Bluetooth) wake` - light sleep only -use core::{cell::RefCell, time::Duration}; +use core::cell::RefCell; +#[cfg(any(esp32, esp32c3, esp32s3))] +use core::time::Duration; -#[cfg(esp32c3)] -use crate::gpio::RTCPinWithResistors; -use crate::{gpio::RTCPin, Rtc}; +#[cfg(any(esp32, esp32s3))] +use crate::gpio::RTCPin as RtcIoWakeupPinType; +#[cfg(any(esp32c3, esp32c6))] +use crate::gpio::RTCPinWithResistors as RtcIoWakeupPinType; +use crate::Rtc; -#[cfg_attr(esp32, path = "rtc/esp32_sleep.rs")] -#[cfg_attr(esp32s3, path = "rtc/esp32s3_sleep.rs")] -#[cfg_attr(esp32c3, path = "rtc/esp32c3_sleep.rs")] -mod rtc_sleep; +#[cfg_attr(esp32, path = "esp32.rs")] +#[cfg_attr(esp32s3, path = "esp32s3.rs")] +#[cfg_attr(esp32c3, path = "esp32c3.rs")] +#[cfg_attr(esp32c6, path = "esp32c6.rs")] +mod sleep_impl; -pub use rtc_sleep::*; +pub use sleep_impl::*; #[derive(Debug, Default, Clone, Copy, PartialEq)] pub enum WakeupLevel { @@ -40,10 +46,12 @@ pub enum WakeupLevel { } #[derive(Debug, Default, Clone, Copy)] +#[cfg(any(esp32, esp32c3, esp32s3))] pub struct TimerWakeupSource { duration: Duration, } +#[cfg(any(esp32, esp32c3, esp32s3))] impl TimerWakeupSource { pub fn new(duration: Duration) -> Self { Self { duration } @@ -57,14 +65,15 @@ pub enum Error { TooManyWakeupSources, } -#[allow(unused)] #[derive(Debug)] -pub struct Ext0WakeupSource<'a, P: RTCPin> { +#[cfg(any(esp32, esp32s3))] +pub struct Ext0WakeupSource<'a, P: RtcIoWakeupPinType> { pin: RefCell<&'a mut P>, level: WakeupLevel, } -impl<'a, P: RTCPin> Ext0WakeupSource<'a, P> { +#[cfg(any(esp32, esp32s3))] +impl<'a, P: RtcIoWakeupPinType> Ext0WakeupSource<'a, P> { pub fn new(pin: &'a mut P, level: WakeupLevel) -> Self { Self { pin: RefCell::new(pin), @@ -73,14 +82,15 @@ impl<'a, P: RTCPin> Ext0WakeupSource<'a, P> { } } -#[allow(unused)] +#[cfg(any(esp32, esp32s3))] pub struct Ext1WakeupSource<'a, 'b> { - pins: RefCell<&'a mut [&'b mut dyn RTCPin]>, + pins: RefCell<&'a mut [&'b mut dyn RtcIoWakeupPinType]>, level: WakeupLevel, } +#[cfg(any(esp32, esp32s3))] impl<'a, 'b> Ext1WakeupSource<'a, 'b> { - pub fn new(pins: &'a mut [&'b mut dyn RTCPin], level: WakeupLevel) -> Self { + pub fn new(pins: &'a mut [&'b mut dyn RtcIoWakeupPinType], level: WakeupLevel) -> Self { Self { pins: RefCell::new(pins), level, @@ -88,35 +98,40 @@ impl<'a, 'b> Ext1WakeupSource<'a, 'b> { } } +#[cfg(esp32c6)] +pub struct Ext1WakeupSource<'a, 'b> { + pins: RefCell<&'a mut [(&'b mut dyn RtcIoWakeupPinType, WakeupLevel)]>, +} + +#[cfg(esp32c6)] +impl<'a, 'b> Ext1WakeupSource<'a, 'b> { + pub fn new(pins: &'a mut [(&'b mut dyn RtcIoWakeupPinType, WakeupLevel)]) -> Self { + Self { + pins: RefCell::new(pins), + } + } +} + /// RTC_IO wakeup source /// /// RTC_IO wakeup allows configuring any combination of RTC_IO pins with /// arbitrary wakeup levels to wake up the chip from sleep. This wakeup source /// can be used to wake up from both light and deep sleep. -#[allow(unused)] +#[cfg(any(esp32c3, esp32s3))] pub struct RtcioWakeupSource<'a, 'b> { - #[cfg(xtensa)] - pins: RefCell<&'a mut [(&'b mut dyn RTCPin, WakeupLevel)]>, - #[cfg(esp32c3)] - pins: RefCell<&'a mut [(&'b mut dyn RTCPinWithResistors, WakeupLevel)]>, + pins: RefCell<&'a mut [(&'b mut dyn RtcIoWakeupPinType, WakeupLevel)]>, } +#[cfg(any(esp32c3, esp32s3))] impl<'a, 'b> RtcioWakeupSource<'a, 'b> { - #[cfg(xtensa)] - pub fn new(pins: &'a mut [(&'b mut dyn RTCPin, WakeupLevel)]) -> Self { - Self { - pins: RefCell::new(pins), - } - } - - #[cfg(esp32c3)] - pub fn new(pins: &'a mut [(&'b mut dyn RTCPinWithResistors, WakeupLevel)]) -> Self { + pub fn new(pins: &'a mut [(&'b mut dyn RtcIoWakeupPinType, WakeupLevel)]) -> Self { Self { pins: RefCell::new(pins), } } } +#[cfg(not(pmu))] bitfield::bitfield! { #[derive(Default, Clone, Copy)] pub struct WakeTriggers(u16); @@ -145,6 +160,38 @@ bitfield::bitfield! { pub bt, set_bt: 10; } +#[cfg(pmu)] +bitfield::bitfield! { + #[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 + pub gpio, set_gpio: 2; + /// WiFi beacon wakeup + pub wifi_beacon, set_wifi_beacon: 3; + /// Timer wakeup + pub timer, set_timer: 4; + /// WiFi SoC wakeup + pub wifi_soc, set_wifi_soc: 5; + /// UART0 wakeup + pub uart0, set_uart0: 6; + /// UART1 wakeup + pub uart1, set_uart1: 7; + /// SDIO wakeup + pub sdio, set_sdio: 8; + /// BT wakeup + pub bt, set_bt: 10; + /// LP core wakeup + pub lp_core, set_lp_core: 11; + /// USB wakeup + pub usb, set_usb: 14; +} + pub trait WakeSource { fn apply(&self, rtc: &Rtc, triggers: &mut WakeTriggers, sleep_config: &mut RtcSleepConfig); } diff --git a/esp32c6-hal/examples/sleep_lpio.rs b/esp32c6-hal/examples/sleep_lpio.rs new file mode 100644 index 00000000000..e1fe4238757 --- /dev/null +++ b/esp32c6-hal/examples/sleep_lpio.rs @@ -0,0 +1,54 @@ +//! Demonstrates deep sleep with gpio2 (low) and gpio3 (high) as wakeup sources. + +#![no_std] +#![no_main] + +use esp32c6_hal::{ + clock::ClockControl, + entry, + gpio::RTCPinWithResistors, + peripherals::Peripherals, + prelude::*, + rtc_cntl::{ + get_reset_reason, + get_wakeup_cause, + sleep::{Ext1WakeupSource, WakeupLevel}, + SocResetReason, + }, + Cpu, + Delay, + Rtc, + IO, +}; +use esp_backtrace as _; +use esp_println::println; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let system = peripherals.SYSTEM.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let mut pin2 = io.pins.gpio2; + let mut pin3 = io.pins.gpio3; + + println!("up and runnning!"); + let reason = get_reset_reason(Cpu::ProCpu).unwrap_or(SocResetReason::ChipPowerOn); + println!("reset reason: {:?}", reason); + let wake_reason = get_wakeup_cause(); + println!("wake reason: {:?}", wake_reason); + + let mut delay = Delay::new(&clocks); + let wakeup_pins: &mut [(&mut dyn RTCPinWithResistors, WakeupLevel)] = &mut [ + (&mut pin2, WakeupLevel::Low), + (&mut pin3, WakeupLevel::High), + ]; + + let rtcio = Ext1WakeupSource::new(wakeup_pins); + println!("sleeping!"); + delay.delay_ms(100u32); + rtc.sleep_deep(&[&rtcio], &mut delay); +}