Skip to main content

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/lp_blinky/src/main.rs
21
22use crate::{
23    peripherals::{LP_AON, LP_CORE, LP_PERI, LPWR, PMU},
24    rtc_cntl::{
25        WakeupSource,
26        sleep::{SleepResource, WrappedSleepConfig},
27    },
28};
29
30/// Represents the possible wakeup sources for the LP (Low Power) core.
31#[derive(Debug, Clone, Copy)]
32pub enum LpCoreWakeupSource {
33    /// Wakeup source from the HP (High Performance) CPU.
34    HpCpu,
35}
36
37/// Clock sources for the LP core.
38#[derive(Debug, Clone, Copy)]
39pub enum LpCoreClockSource {
40    /// 17.5 MHz clock
41    ///
42    /// Might not be very accurate
43    RcFastClk,
44    /// 20 MHz clock
45    XtalD2Clk,
46}
47
48/// Represents the Low Power (LP) core peripheral.
49pub struct LpCore<'d> {
50    _lp_core: LP_CORE<'d>,
51}
52
53impl<'d> LpCore<'d> {
54    /// Creates a new instance using [LpCoreClockSource::RcFastClk].
55    pub fn new(lp_core: LP_CORE<'d>) -> Self {
56        LpCore::new_with_clock(lp_core, LpCoreClockSource::RcFastClk)
57    }
58
59    /// Creates a new instance using the given clock.
60    pub fn new_with_clock(lp_core: LP_CORE<'d>, clk_src: LpCoreClockSource) -> Self {
61        match clk_src {
62            LpCoreClockSource::RcFastClk => LPWR::regs()
63                .lp_clk_conf()
64                .modify(|_, w| w.fast_clk_sel().clear_bit()),
65            LpCoreClockSource::XtalD2Clk => LPWR::regs()
66                .lp_clk_conf()
67                .modify(|_, w| w.fast_clk_sel().set_bit()),
68        };
69
70        let mut this = Self { _lp_core: lp_core };
71        this.stop();
72
73        // clear all of LP_RAM - this makes sure .bss is cleared without relying
74        let lp_ram =
75            unsafe { core::slice::from_raw_parts_mut(0x5000_0000 as *mut u32, 16 * 1024 / 4) };
76        lp_ram.fill(0u32);
77
78        this
79    }
80
81    /// Stops the LP core.
82    pub fn stop(&mut self) {
83        ulp_lp_core_stop();
84    }
85
86    /// Starts the LP core.
87    pub fn run(&mut self, wakeup_src: LpCoreWakeupSource) {
88        ulp_lp_core_run(wakeup_src);
89    }
90
91    /// Lets the LP core wake the chip from sleep.
92    ///
93    /// The request stays until [`Self::disable_wakeup`] is called. It stays through a sleep,
94    /// through a deep-sleep wake, and after a drop of this driver. While the chip is awake, it
95    /// does nothing.
96    pub fn enable_wakeup(&mut self) {
97        WakeupSource::LpCore.enable_with_hooks(Some(keep_low_power_domain), None);
98    }
99
100    /// Stops the LP core from waking the chip.
101    pub fn disable_wakeup(&mut self) {
102        WakeupSource::LpCore.disable();
103    }
104}
105
106/// The LP core wakes the chip through the low-power peripherals, which also contain the timer that
107/// the core usually waits for. A sleep that powers these peripherals down does not get the request.
108#[crate::ram]
109fn keep_low_power_domain(config: &mut WrappedSleepConfig<'_>) {
110    config.keep_alive(SleepResource::LpPeripherals);
111}
112
113fn ulp_lp_core_stop() {
114    PMU::regs()
115        .lp_cpu_pwr1()
116        .modify(|_, w| unsafe { w.lp_cpu_wakeup_en().bits(0) });
117    PMU::regs()
118        .lp_cpu_pwr1()
119        .modify(|_, w| w.lp_cpu_sleep_req().set_bit());
120}
121
122fn ulp_lp_core_run(wakeup_src: LpCoreWakeupSource) {
123    let lp_aon = LP_AON::regs();
124    let pmu = PMU::regs();
125    let lp_peri = LP_PERI::regs();
126
127    // Enable LP-Core
128    lp_aon.lpcore().modify(|_, w| w.disable().clear_bit());
129
130    // Allow LP core to access LP memory during sleep
131    lp_aon
132        .lpbus()
133        .modify(|_, w| w.fast_mem_mux_sel().clear_bit());
134    lp_aon
135        .lpbus()
136        .modify(|_, w| w.fast_mem_mux_sel_update().set_bit());
137
138    // Enable stall at sleep request
139    pmu.lp_cpu_pwr0()
140        .modify(|_, w| w.lp_cpu_slp_stall_en().set_bit());
141
142    // Enable reset after wake-up
143    pmu.lp_cpu_pwr0()
144        .modify(|_, w| w.lp_cpu_slp_reset_en().set_bit());
145
146    // Set wake-up sources
147    let src = match wakeup_src {
148        LpCoreWakeupSource::HpCpu => 0x01,
149    };
150    pmu.lp_cpu_pwr1()
151        .modify(|_, w| unsafe { w.lp_cpu_wakeup_en().bits(src) });
152
153    // Enable JTAG debugging
154    lp_peri
155        .cpu()
156        .modify(|_, w| w.lpcore_dbgm_unavaliable().clear_bit());
157
158    // Clear the wake requests of a previous run. Such a request rejects the next light sleep, but
159    // it is not the event that the caller waits for.
160    pmu.int_clr().write(|w| {
161        w.sw().clear_bit_by_one();
162        w.lp_cpu_exc().clear_bit_by_one()
163    });
164
165    // wake up
166    match wakeup_src {
167        LpCoreWakeupSource::HpCpu => {
168            pmu.hp_lp_cpu_comm().write(|w| w.hp_trigger_lp().set_bit());
169        }
170    }
171}