1
//! # System Control
2

            
3
#![allow(dead_code)]
4

            
5
use esp_sync::NonReentrantMutex;
6

            
7
cfg_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
16
implement_peripheral_clocks!();
17

            
18
impl 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

            
28
struct RefCounts {
29
    counts: [usize; Peripheral::COUNT],
30
}
31

            
32
impl RefCounts {
33
    pub const fn new() -> Self {
34
        Self {
35
            counts: [0; Peripheral::COUNT],
36
        }
37
    }
38
}
39

            
40
static PERIPHERAL_REF_COUNT: NonReentrantMutex<RefCounts> =
41
    NonReentrantMutex::new(RefCounts::new());
42

            
43
/// Disables all peripherals.
44
///
45
/// Peripherals listed in [KEEP_ENABLED] are NOT disabled.
46
#[cfg_attr(not(feature = "rt"), expect(dead_code))]
47
pub(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
325
            refcounts.counts[*p as usize] += 1;
52
        }
53
        for p in Peripheral::ALL {
54
7475
            let ref_count = refcounts.counts[*p as usize];
55
7475
            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))]
64
pub(crate) struct PeripheralGuard {
65
    peripheral: Peripheral,
66
}
67

            
68
impl 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
166
    pub(crate) fn new(p: Peripheral) -> Self {
76
        Self::new_with(p, || {})
77
166
    }
78
}
79

            
80
impl 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

            
90
impl Drop for PeripheralGuard {
91
116
    fn drop(&mut self) {
92
384
        PeripheralClockControl::disable(self.peripheral);
93
    }
94
}
95

            
96
#[derive(Debug)]
97
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
98
pub(crate) struct GenericPeripheralGuard<const P: u8> {}
99

            
100
impl<const P: u8> GenericPeripheralGuard<P> {
101
    pub(crate) fn new_with(init: fn()) -> Self {
102
        let p = const { Peripheral::try_from(P).unwrap() };
103
18
        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

            
115
impl<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

            
125
impl<const P: u8> Drop for GenericPeripheralGuard<P> {
126
    fn drop(&mut self) {
127
        let peripheral = const { Peripheral::try_from(P).unwrap() };
128
13
        PeripheralClockControl::disable(peripheral);
129
    }
130
}
131

            
132
/// Controls the enablement of peripheral clocks.
133
pub(crate) struct PeripheralClockControl;
134

            
135
impl PeripheralClockControl {
136
27
    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
18
                init();
141
            }
142
        });
143
27
    }
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 whether it actually enabled the peripheral.
151
78
    pub(crate) fn enable(peripheral: Peripheral) -> bool {
152
        PERIPHERAL_REF_COUNT.with(|ref_counts| Self::enable_with_counts(peripheral, ref_counts))
153
78
    }
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 whether 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 whether it actually disabled the peripheral.
171
778
    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
765
    }
176

            
177
    fn enable_forced_with_counts(
178
        peripheral: Peripheral,
179
        enable: bool,
180
        force: bool,
181
        ref_counts: &mut RefCounts,
182
    ) -> bool {
183
842
        let ref_count = &mut ref_counts.counts[peripheral as usize];
184
        if !force {
185
844
            let prev = *ref_count;
186
            if enable {
187
530
                *ref_count += 1;
188
                trace!("Enable {:?} {} -> {}", peripheral, prev, *ref_count);
189
530
                if prev > 0 {
190
                    return false;
191
                }
192
            } else {
193
778
                assert!(prev != 0);
194
778
                *ref_count -= 1;
195
                trace!("Disable {:?} {} -> {}", peripheral, prev, *ref_count);
196
778
                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
4
    pub(crate) fn reset(peripheral: Peripheral) {
222
        PERIPHERAL_REF_COUNT.with(|_| unsafe { Self::reset_racey(peripheral) })
223
4
    }
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)]
232
pub enum Cpu {
233
    /// The first core.
234
    ProCpu = 0,
235
    /// The second core.
236
    #[cfg(multi_core)]
237
    AppCpu = 1,
238
}
239

            
240
impl 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)]
305
pub(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

            
314
use crate::rtc_cntl::SocResetReason;
315

            
316
#[procmacros::doc_replace]
317
/// Performs a software reset on the chip.
318
///
319
/// # Examples
320
///
321
/// ```rust, no_run
322
/// # {before_snippet}
323
/// use esp_hal::system::software_reset;
324
/// software_reset();
325
/// # {after_snippet}
326
/// ```
327
#[inline]
328
pub fn software_reset() -> ! {
329
    let _uart0_sclk_guard = ensure_uart0_sclk_enabled();
330
    #[cfg(any(esp32h4, 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]
338
pub 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"]
347
pub(crate) struct Uart0SclkGuard {
348
    release: bool,
349
}
350

            
351
impl Drop for Uart0SclkGuard {
352
    fn drop(&mut self) {
353
        if self.release {
354
            release_uart0_sclk();
355
        }
356
    }
357
}
358

            
359
/// Ensures 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)]
366
pub(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)]
373
fn 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))]
386
fn request_uart0_sclk() -> bool {
387
    false
388
}
389

            
390
#[cfg(soc_has_clock_node_uart_function_clock)]
391
fn 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))]
398
fn release_uart0_sclk() {}
399

            
400
/// Guard for the clock shared by the crypto accelerators.
401
#[cfg(feature = "unstable")]
402
#[must_use = "dropping the guard releases the crypto clock"]
403
pub(crate) struct CryptoClockGuard;
404

            
405
#[cfg(feature = "unstable")]
406
impl CryptoClockGuard {
407
    /// Request the clock that drives the crypto accelerators.
408
    ///
409
    /// The clock is called `CRYPTO_CLK` on most chips, and `CRYPTO_PWM_CLK` on the ESP32-S3, where
410
    /// the MCPWM peripherals share it.
411
    #[inline(always)]
412
    pub(crate) fn new() -> Self {
413
270
        Self::request();
414
        Self
415
    }
416

            
417
322
    fn request() {
418
        cfg_select! {
419
            soc_has_clock_node_crypto_clk => {
420
                crate::soc::clocks::ClockTree::with(|clocks| {
421
                    crate::soc::clocks::request_crypto_clk(clocks);
422
                });
423
            }
424
            soc_has_clock_node_crypto_pwm_clk => {
425
                crate::soc::clocks::ClockTree::with(|clocks| {
426
                    crate::soc::clocks::request_crypto_pwm_clk(clocks);
427
                });
428
            }
429
            _ => {}
430
        }
431
309
    }
432

            
433
    fn release() {
434
        cfg_select! {
435
            soc_has_clock_node_crypto_clk => {
436
                crate::soc::clocks::ClockTree::with(|clocks| {
437
                    crate::soc::clocks::release_crypto_clk(clocks);
438
                });
439
            }
440
            soc_has_clock_node_crypto_pwm_clk => {
441
                crate::soc::clocks::ClockTree::with(|clocks| {
442
                    crate::soc::clocks::release_crypto_pwm_clk(clocks);
443
                });
444
            }
445
            _ => {}
446
        }
447
    }
448
}
449

            
450
#[cfg(feature = "unstable")]
451
impl Drop for CryptoClockGuard {
452
11
    fn drop(&mut self) {
453
        Self::release();
454
11
    }
455
}
456

            
457
/// Retrieves the reason for the last reset as a SocResetReason enum value.
458
/// Returns `None` if the reset reason cannot be determined.
459
#[instability::unstable]
460
#[inline]
461
pub fn reset_reason() -> Option<SocResetReason> {
462
    crate::rtc_cntl::reset_reason(Cpu::current())
463
}
464

            
465
/// Retrieves the cause(s) of the last wakeup event.
466
///
467
/// Returns the [`WakeupReason`][crate::rtc_cntl::WakeupReason] describing the source(s) that ended
468
/// the most recent sleep. The result is empty if the chip was not woken from sleep.
469
#[cfg(sleep_driver_supported)]
470
#[instability::unstable]
471
#[inline]
472
pub fn wakeup_cause() -> crate::rtc_cntl::WakeupReason {
473
    crate::rtc_cntl::wakeup_cause()
474
}