Skip to main content

esp_hal/
system.rs

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