1use esp_sync::NonReentrantMutex;
4
5implement_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#[cfg_attr(not(feature = "rt"), expect(dead_code))]
37pub(crate) fn disable_peripherals() {
38    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        }
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
120pub(crate) struct PeripheralClockControl;
122
123impl PeripheralClockControl {
124    pub(crate) fn enable(peripheral: Peripheral) -> bool {
131        PERIPHERAL_REF_COUNT.with(|ref_counts| Self::enable_with_counts(peripheral, ref_counts))
132    }
133
134    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    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    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    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#[derive(Debug, Copy, Clone, PartialEq, Eq, strum::FromRepr)]
219#[cfg_attr(feature = "defmt", derive(defmt::Format))]
220#[repr(C)]
221pub enum Cpu {
222    ProCpu = 0,
224    #[cfg(multi_core)]
226    AppCpu = 1,
227}
228
229impl Cpu {
230    pub const COUNT: usize = 1 + cfg!(multi_core) as usize;
232
233    #[procmacros::doc_replace]
234    #[inline(always)]
245    pub fn current() -> Self {
246        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    #[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    #[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#[inline(always)]
297pub(crate) fn raw_core() -> usize {
298    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#[derive(Debug, Copy, Clone)]
314#[cfg_attr(feature = "defmt", derive(defmt::Format))]
315#[instability::unstable]
316pub enum SleepSource {
317    Undefined = 0,
319    All,
322    Ext0,
324    Ext1,
326    Timer,
328    TouchPad,
330    Ulp,
332    Gpio,
334    Uart,
336    Wifi,
338    Cocpu,
340    CocpuTrapTrig,
342    BT,
344}
345
346#[procmacros::doc_replace]
347#[inline]
358pub fn software_reset() -> ! {
359    crate::rom::software_reset()
360}
361
362#[instability::unstable]
364#[inline]
365pub fn software_reset_cpu(cpu: Cpu) {
366    crate::rom::software_reset_cpu(cpu as u32)
367}
368
369#[instability::unstable]
372#[inline]
373pub fn reset_reason() -> Option<SocResetReason> {
374    crate::rtc_cntl::reset_reason(Cpu::current())
375}
376
377#[instability::unstable]
379#[inline]
380pub fn wakeup_cause() -> SleepSource {
381    crate::rtc_cntl::wakeup_cause()
382}