Skip to main content

esp_hal/
system.rs

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