diff --git a/esp-hal/src/analog/adc/mod.rs b/esp-hal/src/analog/adc/mod.rs index f50ce194a5f..262b59c03f9 100644 --- a/esp-hal/src/analog/adc/mod.rs +++ b/esp-hal/src/analog/adc/mod.rs @@ -66,6 +66,67 @@ mod implementation; #[cfg(feature = "unstable")] pub use self::implementation::*; +#[cfg(not(esp32))] +fn on_apb_saradc_clock_release() { + #[cfg(any(esp32s2, esp32s3))] + { + let sensors = crate::peripherals::SENS::regs(); + + #[cfg(esp32s2)] + sensors + .sar_meas1_ctrl1() + .modify(|_, w| w.rtc_saradc_clkgate_en().clear_bit()); + + #[cfg(esp32s3)] + sensors + .sar_peri_clk_gate_conf() + .modify(|_, w| w.saradc_clk_en().clear_bit()); + + sensors.sar_power_xpd_sar().modify(|_, w| unsafe { + w.sarclk_en().clear_bit(); + w.force_xpd_sar().bits(0) + }); + } + + #[cfg(riscv)] + { + crate::peripherals::APB_SARADC::regs() + .ctrl() + .modify(|_, w| unsafe { w.xpd_sar_force().bits(0) }); + } +} + +/// A guard that tracks SAR ADC usage and powers down the SAR analog domain +/// when the last user is dropped. +// Without this, leaving the ADC "enabled" before entering deep sleep keeps the SAR analog block +// powered and significantly increases sleep-time current consumption — see . +#[cfg(not(esp32))] +pub(crate) struct SarAdcGuard { + _inner: crate::system::GenericPeripheralGuard<{ crate::system::Peripheral::ApbSarAdc as u8 }>, +} + +#[cfg(not(esp32))] +static SAR_ADC_USERS: portable_atomic::AtomicUsize = portable_atomic::AtomicUsize::new(0); + +#[cfg(not(esp32))] +impl SarAdcGuard { + pub(crate) fn new() -> Self { + SAR_ADC_USERS.fetch_add(1, portable_atomic::Ordering::AcqRel); + Self { + _inner: crate::system::GenericPeripheralGuard::new(), + } + } +} + +#[cfg(not(esp32))] +impl Drop for SarAdcGuard { + fn drop(&mut self) { + if SAR_ADC_USERS.fetch_sub(1, portable_atomic::Ordering::AcqRel) == 1 { + on_apb_saradc_clock_release(); + } + } +} + /// The approximate attenuation of the ADC pin. /// /// The effective measurement range for a given attenuation is dependent on the diff --git a/esp-hal/src/analog/adc/riscv.rs b/esp-hal/src/analog/adc/riscv.rs index a34655dc4da..d60c5d7226c 100644 --- a/esp-hal/src/analog/adc/riscv.rs +++ b/esp-hal/src/analog/adc/riscv.rs @@ -19,7 +19,7 @@ use portable_atomic::{AtomicU32, Ordering}; use procmacros::handler; pub use self::calibration::*; -use super::{AdcCalSource, AdcConfig, Attenuation}; +use super::{AdcCalSource, AdcConfig, Attenuation, SarAdcGuard}; #[cfg(any(esp32c2, esp32c3, esp32c5, esp32c6, esp32h2))] use crate::efuse::AdcCalibUnit; use crate::{ @@ -29,7 +29,6 @@ use crate::{ interrupt::{InterruptConfigurable, InterruptHandler}, peripherals::{APB_SARADC, Interrupt}, soc::regi2c, - system::{GenericPeripheralGuard, Peripheral}, }; mod calibration; @@ -312,7 +311,7 @@ pub struct Adc<'d, ADCX, Dm: crate::DriverMode> { _adc: ADCX, attenuations: [Option; NUM_ATTENS], active_channel: Option, - _guard: GenericPeripheralGuard<{ Peripheral::ApbSarAdc as u8 }>, + _guard: SarAdcGuard, _phantom: PhantomData<(Dm, &'d mut ())>, } @@ -323,7 +322,7 @@ where /// Configure a given ADC instance using the provided configuration, and /// initialize the ADC for use pub fn new(adc_instance: ADCX, config: AdcConfig) -> Self { - let guard = GenericPeripheralGuard::new(); + let guard = SarAdcGuard::new(); APB_SARADC::regs().ctrl().modify(|_, w| unsafe { w.start_force().set_bit(); diff --git a/esp-hal/src/analog/adc/xtensa.rs b/esp-hal/src/analog/adc/xtensa.rs index faa2e43c3bd..62861dc90e7 100644 --- a/esp-hal/src/analog/adc/xtensa.rs +++ b/esp-hal/src/analog/adc/xtensa.rs @@ -2,13 +2,12 @@ use core::marker::PhantomData; #[cfg(esp32s3)] pub use self::calibration::*; -use super::{AdcCalScheme, AdcCalSource, AdcChannel, AdcConfig, AdcPin, Attenuation}; +use super::{AdcCalScheme, AdcCalSource, AdcChannel, AdcConfig, AdcPin, Attenuation, SarAdcGuard}; #[cfg(esp32s3)] use crate::efuse::AdcCalibUnit; use crate::{ peripherals::{APB_SARADC, SENS}, soc::regi2c, - system::{GenericPeripheralGuard, Peripheral}, }; mod calibration; @@ -325,7 +324,7 @@ pub struct Adc<'d, ADC, Dm: crate::DriverMode> { _adc: ADC, active_channel: Option, last_init_code: u16, - _guard: GenericPeripheralGuard<{ Peripheral::ApbSarAdc as u8 }>, + _guard: SarAdcGuard, _phantom: PhantomData<(Dm, &'d mut ())>, } @@ -336,7 +335,7 @@ where /// Configure a given ADC instance using the provided configuration, and /// initialize the ADC for use pub fn new(adc_instance: ADCX, config: AdcConfig) -> Self { - let guard = GenericPeripheralGuard::new(); + let guard = SarAdcGuard::new(); let sensors = SENS::regs(); // Set attenuation for pins diff --git a/esp-hal/src/rng/trng.rs b/esp-hal/src/rng/trng.rs index 6d6b676a6b6..4b01dffafa0 100644 --- a/esp-hal/src/rng/trng.rs +++ b/esp-hal/src/rng/trng.rs @@ -6,6 +6,8 @@ static TRNG_ENABLED: AtomicUsize = AtomicUsize::new(0); static TRNG_USERS: AtomicUsize = AtomicUsize::new(0); use super::Rng; +#[cfg(not(esp32))] +use crate::analog::adc::SarAdcGuard; use crate::peripherals::{ADC1, RNG}; /// Ensures random numbers are cryptographically secure. @@ -13,16 +15,26 @@ use crate::peripherals::{ADC1, RNG}; pub struct TrngSource<'d> { _rng: RNG<'d>, _adc: ADC1<'d>, + #[cfg(not(esp32))] + _apb_saradc_guard: SarAdcGuard, } impl<'d> TrngSource<'d> { /// Enables the SAR ADC entropy source. // TODO: this is not final. A single ADC channel should be sufficient. #[instability::unstable] - pub fn new(_rng: RNG<'d>, _adc: ADC1<'d>) -> Self { + pub fn new(rng: RNG<'d>, adc: ADC1<'d>) -> Self { + #[cfg(not(esp32))] + let apb_saradc_guard = SarAdcGuard::new(); + crate::soc::trng::ensure_randomness(); unsafe { Self::increase_entropy_source_counter() } - Self { _rng, _adc } + Self { + _rng: rng, + _adc: adc, + #[cfg(not(esp32))] + _apb_saradc_guard: apb_saradc_guard, + } } /// Increases the internal entropy source counter.