esp_hal/soc/esp32c6/
lp_core.rs

1//! # Control the LP core
2//!
3//! ## Overview
4//! The `LP_CORE` driver provides an interface for controlling and managing the
5//! low power core of `ESP` chips, allowing efficient low power operation and
6//! wakeup from sleep based on configurable sources. The low power core is
7//! responsible for executing low power tasks while the high power core is in
8//! sleep mode.
9//!
10//! The `LpCore` struct provides methods to stop and run the low power core.
11//!
12//! The `stop` method stops the low power core, putting it into a sleep state.
13//!
14//! The `run` method starts the low power core and specifies the wakeup source.
15//!
16//! ⚠️: The examples for LP Core are quite extensive, so for a more
17//! detailed study of how to use this LP Core please visit [the repository
18//! with corresponding example].
19//!
20//! [the repository with corresponding example]: https://github.com/esp-rs/esp-hal/blob/main/examples/peripheral/lp_core/src/main.rs
21
22use crate::peripherals::{LP_AON, LP_CORE, LP_PERI, LPWR, PMU};
23
24/// Represents the possible wakeup sources for the LP (Low Power) core.
25#[derive(Debug, Clone, Copy)]
26pub enum LpCoreWakeupSource {
27    /// Wakeup source from the HP (High Performance) CPU.
28    HpCpu,
29}
30
31/// Clock sources for the LP core
32#[derive(Debug, Clone, Copy)]
33pub enum LpCoreClockSource {
34    /// 17.5 MHz clock
35    ///
36    /// Might not be very accurate
37    RcFastClk,
38    /// 20 MHz clock
39    XtalD2Clk,
40}
41
42/// Represents the Low Power (LP) core peripheral.
43pub struct LpCore<'d> {
44    _lp_core: LP_CORE<'d>,
45}
46
47impl<'d> LpCore<'d> {
48    /// Create a new instance using [LpCoreClockSource::RcFastClk]
49    pub fn new(lp_core: LP_CORE<'d>) -> Self {
50        LpCore::new_with_clock(lp_core, LpCoreClockSource::RcFastClk)
51    }
52
53    /// Create a new instance using the given clock
54    pub fn new_with_clock(lp_core: LP_CORE<'d>, clk_src: LpCoreClockSource) -> Self {
55        match clk_src {
56            LpCoreClockSource::RcFastClk => LPWR::regs()
57                .lp_clk_conf()
58                .modify(|_, w| w.fast_clk_sel().clear_bit()),
59            LpCoreClockSource::XtalD2Clk => LPWR::regs()
60                .lp_clk_conf()
61                .modify(|_, w| w.fast_clk_sel().set_bit()),
62        };
63
64        let mut this = Self { _lp_core: lp_core };
65        this.stop();
66
67        // clear all of LP_RAM - this makes sure .bss is cleared without relying
68        let lp_ram =
69            unsafe { core::slice::from_raw_parts_mut(0x5000_0000 as *mut u32, 16 * 1024 / 4) };
70        lp_ram.fill(0u32);
71
72        this
73    }
74
75    /// Stop the LP core
76    pub fn stop(&mut self) {
77        ulp_lp_core_stop();
78    }
79
80    /// Start the LP core
81    pub fn run(&mut self, wakeup_src: LpCoreWakeupSource) {
82        ulp_lp_core_run(wakeup_src);
83    }
84}
85
86fn ulp_lp_core_stop() {
87    PMU::regs()
88        .lp_cpu_pwr1()
89        .modify(|_, w| unsafe { w.lp_cpu_wakeup_en().bits(0) });
90    PMU::regs()
91        .lp_cpu_pwr1()
92        .modify(|_, w| w.lp_cpu_sleep_req().set_bit());
93}
94
95fn ulp_lp_core_run(wakeup_src: LpCoreWakeupSource) {
96    let lp_aon = LP_AON::regs();
97    let pmu = PMU::regs();
98    let lp_peri = LP_PERI::regs();
99
100    // Enable LP-Core
101    lp_aon.lpcore().modify(|_, w| w.disable().clear_bit());
102
103    // Allow LP core to access LP memory during sleep
104    lp_aon
105        .lpbus()
106        .modify(|_, w| w.fast_mem_mux_sel().clear_bit());
107    lp_aon
108        .lpbus()
109        .modify(|_, w| w.fast_mem_mux_sel_update().set_bit());
110
111    // Enable stall at sleep request
112    pmu.lp_cpu_pwr0()
113        .modify(|_, w| w.lp_cpu_slp_stall_en().set_bit());
114
115    // Enable reset after wake-up
116    pmu.lp_cpu_pwr0()
117        .modify(|_, w| w.lp_cpu_slp_reset_en().set_bit());
118
119    // Set wake-up sources
120    let src = match wakeup_src {
121        LpCoreWakeupSource::HpCpu => 0x01,
122    };
123    pmu.lp_cpu_pwr1()
124        .modify(|_, w| unsafe { w.lp_cpu_wakeup_en().bits(src) });
125
126    // Enable JTAG debugging
127    lp_peri
128        .cpu()
129        .modify(|_, w| w.lpcore_dbgm_unavaliable().clear_bit());
130
131    // wake up
132    match wakeup_src {
133        LpCoreWakeupSource::HpCpu => {
134            pmu.hp_lp_cpu_comm().write(|w| w.hp_trigger_lp().set_bit());
135        }
136    }
137}