esp_hal/
system.rs

1//! # System Control
2
3use esp_sync::NonReentrantMutex;
4
5// Implements the Peripheral enum based on esp-metadata/device.soc/peripheral_clocks
6implement_peripheral_clocks!();
7
8impl Peripheral {
9    pub const fn try_from(value: u8) -> Option<Peripheral> {
10        if value >= Peripheral::COUNT as u8 {
11            return None;
12        }
13
14        Some(unsafe { core::mem::transmute::<u8, Peripheral>(value) })
15    }
16}
17
18struct RefCounts {
19    counts: [usize; Peripheral::COUNT],
20}
21
22impl RefCounts {
23    pub const fn new() -> Self {
24        Self {
25            counts: [0; Peripheral::COUNT],
26        }
27    }
28}
29
30static PERIPHERAL_REF_COUNT: NonReentrantMutex<RefCounts> =
31    NonReentrantMutex::new(RefCounts::new());
32
33/// Disable all peripherals.
34///
35/// Peripherals listed in [KEEP_ENABLED] are NOT disabled.
36#[cfg_attr(not(feature = "rt"), expect(dead_code))]
37pub(crate) fn disable_peripherals() {
38    // Take the critical section up front to avoid taking it multiple times.
39    PERIPHERAL_REF_COUNT.with(|refcounts| {
40        for p in Peripheral::KEEP_ENABLED {
41            refcounts.counts[*p as usize] += 1;
42        }
43        for p in Peripheral::ALL {
44            let ref_count = refcounts.counts[*p as usize];
45            if ref_count == 0 {
46                PeripheralClockControl::enable_forced_with_counts(*p, false, true, refcounts);
47            }
48        }
49    })
50}
51
52#[derive(Debug, PartialEq, Eq)]
53#[cfg_attr(feature = "defmt", derive(defmt::Format))]
54pub(crate) struct PeripheralGuard {
55    peripheral: Peripheral,
56}
57
58impl PeripheralGuard {
59    pub(crate) fn new_with(p: Peripheral, init: fn()) -> Self {
60        if PeripheralClockControl::enable(p) {
61            PeripheralClockControl::reset(p);
62            init();
63        }
64
65        Self { peripheral: p }
66    }
67
68    pub(crate) fn new(p: Peripheral) -> Self {
69        Self::new_with(p, || {})
70    }
71}
72
73impl Drop for PeripheralGuard {
74    fn drop(&mut self) {
75        PeripheralClockControl::disable(self.peripheral);
76    }
77}
78
79#[derive(Debug)]
80#[cfg_attr(feature = "defmt", derive(defmt::Format))]
81pub(crate) struct GenericPeripheralGuard<const P: u8> {}
82
83impl<const P: u8> GenericPeripheralGuard<P> {
84    pub(crate) fn new_with(init: fn()) -> Self {
85        let peripheral = const { Peripheral::try_from(P).unwrap() };
86
87        PERIPHERAL_REF_COUNT.with(|ref_counts| {
88            if PeripheralClockControl::enable_with_counts(peripheral, ref_counts) {
89                unsafe { PeripheralClockControl::reset_racey(peripheral) };
90                init();
91            }
92        });
93
94        Self {}
95    }
96
97    #[cfg_attr(not(feature = "unstable"), allow(unused))]
98    pub(crate) fn new() -> Self {
99        Self::new_with(|| {})
100    }
101}
102
103impl<const P: u8> Clone for GenericPeripheralGuard<P> {
104    fn clone(&self) -> Self {
105        Self::new()
106    }
107
108    fn clone_from(&mut self, _source: &Self) {
109        // This is a no-op since the ref count for P remains the same.
110    }
111}
112
113impl<const P: u8> Drop for GenericPeripheralGuard<P> {
114    fn drop(&mut self) {
115        let peripheral = const { Peripheral::try_from(P).unwrap() };
116        PeripheralClockControl::disable(peripheral);
117    }
118}
119
120/// Controls the enablement of peripheral clocks.
121pub(crate) struct PeripheralClockControl;
122
123impl PeripheralClockControl {
124    /// Enables the given peripheral.
125    ///
126    /// This keeps track of enabling a peripheral - i.e. a peripheral
127    /// is only enabled with the first call attempt to enable it.
128    ///
129    /// Returns `true` if it actually enabled the peripheral.
130    pub(crate) fn enable(peripheral: Peripheral) -> bool {
131        PERIPHERAL_REF_COUNT.with(|ref_counts| Self::enable_with_counts(peripheral, ref_counts))
132    }
133
134    /// Enables the given peripheral.
135    ///
136    /// This keeps track of enabling a peripheral - i.e. a peripheral
137    /// is only enabled with the first call attempt to enable it.
138    ///
139    /// Returns `true` if it actually enabled the peripheral.
140    fn enable_with_counts(peripheral: Peripheral, ref_counts: &mut RefCounts) -> bool {
141        Self::enable_forced_with_counts(peripheral, true, false, ref_counts)
142    }
143
144    /// Disables the given peripheral.
145    ///
146    /// This keeps track of disabling a peripheral - i.e. it only
147    /// gets disabled when the number of enable/disable attempts is balanced.
148    ///
149    /// Returns `true` if it actually disabled the peripheral.
150    ///
151    /// Before disabling a peripheral it will also get reset
152    pub(crate) fn disable(peripheral: Peripheral) -> bool {
153        PERIPHERAL_REF_COUNT.with(|ref_counts| {
154            Self::enable_forced_with_counts(peripheral, false, false, ref_counts)
155        })
156    }
157
158    fn enable_forced_with_counts(
159        peripheral: Peripheral,
160        enable: bool,
161        force: bool,
162        ref_counts: &mut RefCounts,
163    ) -> bool {
164        let ref_count = &mut ref_counts.counts[peripheral as usize];
165        if !force {
166            let prev = *ref_count;
167            if enable {
168                *ref_count += 1;
169                trace!("Enable {:?} {} -> {}", peripheral, prev, *ref_count);
170                if prev > 0 {
171                    return false;
172                }
173            } else {
174                assert!(prev != 0);
175                *ref_count -= 1;
176                trace!("Disable {:?} {} -> {}", peripheral, prev, *ref_count);
177                if prev > 1 {
178                    return false;
179                }
180            };
181        } else if !enable {
182            assert!(*ref_count == 0);
183        }
184
185        if !enable {
186            unsafe { Self::reset_racey(peripheral) };
187        }
188
189        debug!("Enable {:?} {}", peripheral, enable);
190        unsafe { enable_internal_racey(peripheral, enable) };
191
192        true
193    }
194
195    /// Resets the given peripheral
196    pub(crate) unsafe fn reset_racey(peripheral: Peripheral) {
197        debug!("Reset {:?}", peripheral);
198
199        unsafe {
200            assert_peri_reset_racey(peripheral, true);
201            assert_peri_reset_racey(peripheral, false);
202        }
203    }
204
205    /// Resets the given peripheral
206    pub(crate) fn reset(peripheral: Peripheral) {
207        PERIPHERAL_REF_COUNT.with(|_| unsafe { Self::reset_racey(peripheral) })
208    }
209}
210
211#[cfg(any(esp32, esp32s3))]
212#[allow(unused_imports)]
213pub use crate::soc::cpu_control::*;
214
215/// Available CPU cores
216///
217/// The actual number of available cores depends on the target.
218#[derive(Debug, Copy, Clone, PartialEq, Eq, strum::FromRepr)]
219#[cfg_attr(feature = "defmt", derive(defmt::Format))]
220#[repr(C)]
221pub enum Cpu {
222    /// The first core
223    ProCpu = 0,
224    /// The second core
225    #[cfg(multi_core)]
226    AppCpu = 1,
227}
228
229impl Cpu {
230    /// The number of available cores.
231    pub const COUNT: usize = 1 + cfg!(multi_core) as usize;
232
233    #[procmacros::doc_replace]
234    /// Returns the core the application is currently executing on
235    ///
236    /// ```rust, no_run
237    /// # {before_snippet}
238    /// #
239    /// use esp_hal::system::Cpu;
240    /// let current_cpu = Cpu::current();
241    /// #
242    /// # {after_snippet}
243    /// ```
244    #[inline(always)]
245    pub fn current() -> Self {
246        // This works for both RISCV and Xtensa because both
247        // get_raw_core functions return zero, _or_ something
248        // greater than zero; 1 in the case of RISCV and 0x2000
249        // in the case of Xtensa.
250        match raw_core() {
251            0 => Cpu::ProCpu,
252            #[cfg(all(multi_core, riscv))]
253            1 => Cpu::AppCpu,
254            #[cfg(all(multi_core, xtensa))]
255            0x2000 => Cpu::AppCpu,
256            _ => unreachable!(),
257        }
258    }
259
260    /// Returns an iterator over the "other" cores.
261    #[inline(always)]
262    #[instability::unstable]
263    pub fn other() -> impl Iterator<Item = Self> {
264        cfg_if::cfg_if! {
265            if #[cfg(multi_core)] {
266                match Self::current() {
267                    Cpu::ProCpu => [Cpu::AppCpu].into_iter(),
268                    Cpu::AppCpu => [Cpu::ProCpu].into_iter(),
269                }
270            } else {
271                [].into_iter()
272            }
273        }
274    }
275
276    /// Returns an iterator over all cores.
277    #[inline(always)]
278    pub(crate) fn all() -> impl Iterator<Item = Self> {
279        cfg_if::cfg_if! {
280            if #[cfg(multi_core)] {
281                [Cpu::ProCpu, Cpu::AppCpu].into_iter()
282            } else {
283                [Cpu::ProCpu].into_iter()
284            }
285        }
286    }
287}
288
289/// Returns the raw value of the mhartid register.
290///
291/// On RISC-V, this is the hardware thread ID.
292///
293/// On Xtensa, this returns the result of reading the PRID register logically
294/// ANDed with 0x2000, the 13th bit in the register. Espressif Xtensa chips use
295/// this bit to determine the core id.
296#[inline(always)]
297pub(crate) fn raw_core() -> usize {
298    // This method must never return UNUSED_THREAD_ID_VALUE
299    cfg_if::cfg_if! {
300        if #[cfg(all(multi_core, riscv))] {
301            riscv::register::mhartid::read()
302        } else if #[cfg(all(multi_core, xtensa))] {
303            (xtensa_lx::get_processor_id() & 0x2000) as usize
304        } else {
305            0
306        }
307    }
308}
309
310use crate::rtc_cntl::SocResetReason;
311
312/// Source of the wakeup event
313#[derive(Debug, Copy, Clone)]
314#[cfg_attr(feature = "defmt", derive(defmt::Format))]
315#[instability::unstable]
316pub enum SleepSource {
317    /// In case of deep sleep, reset was not caused by exit from deep sleep
318    Undefined = 0,
319    /// Not a wakeup cause, used to disable all wakeup sources with
320    /// esp_sleep_disable_wakeup_source
321    All,
322    /// Wakeup caused by external signal using RTC_IO
323    Ext0,
324    /// Wakeup caused by external signal using RTC_CNTL
325    Ext1,
326    /// Wakeup caused by timer
327    Timer,
328    /// Wakeup caused by touchpad
329    TouchPad,
330    /// Wakeup caused by ULP program
331    Ulp,
332    /// Wakeup caused by GPIO (light sleep only on ESP32, S2 and S3)
333    Gpio,
334    /// Wakeup caused by UART (light sleep only)
335    Uart,
336    /// Wakeup caused by WIFI (light sleep only)
337    Wifi,
338    /// Wakeup caused by COCPU int
339    Cocpu,
340    /// Wakeup caused by COCPU crash
341    CocpuTrapTrig,
342    /// Wakeup caused by BT (light sleep only)
343    BT,
344}
345
346#[procmacros::doc_replace]
347/// Performs a software reset on the chip.
348///
349/// # Example
350///
351/// ```rust, no_run
352/// # {before_snippet}
353/// use esp_hal::system::software_reset;
354/// software_reset();
355/// # {after_snippet}
356/// ```
357#[inline]
358pub fn software_reset() -> ! {
359    crate::rom::software_reset()
360}
361
362/// Resets the given CPU, leaving peripherals unchanged.
363#[instability::unstable]
364#[inline]
365pub fn software_reset_cpu(cpu: Cpu) {
366    crate::rom::software_reset_cpu(cpu as u32)
367}
368
369/// Retrieves the reason for the last reset as a SocResetReason enum value.
370/// Returns `None` if the reset reason cannot be determined.
371#[instability::unstable]
372#[inline]
373pub fn reset_reason() -> Option<SocResetReason> {
374    crate::rtc_cntl::reset_reason(Cpu::current())
375}
376
377/// Retrieves the cause of the last wakeup event as a SleepSource enum value.
378#[instability::unstable]
379#[inline]
380pub fn wakeup_cause() -> SleepSource {
381    crate::rtc_cntl::wakeup_cause()
382}