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}