Skip to content

Commit 053c947

Browse files
committed
fix(stm32wb55): sync cpu2 via hsem when entering sleep
1 parent 7c8831c commit 053c947

File tree

4 files changed

+148
-7
lines changed

4 files changed

+148
-7
lines changed

embassy-stm32/src/low_power.rs

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,10 @@ use core::arch::asm;
5858
use core::marker::PhantomData;
5959
use core::sync::atomic::{compiler_fence, Ordering};
6060

61-
use cortex_m::peripheral::SCB;
62-
use embassy_executor::*;
63-
6461
use crate::interrupt;
6562
use crate::time_driver::{get_driver, RtcDriver};
63+
use cortex_m::peripheral::SCB;
64+
use embassy_executor::*;
6665

6766
const THREAD_PENDER: usize = usize::MAX;
6867

@@ -98,10 +97,22 @@ pub(crate) unsafe fn on_wakeup_irq() {
9897
}
9998

10099
/// Configure STOP mode with RTC.
100+
#[cfg(all(feature = "low-power", not(any(stm32wb))))]
101101
pub fn stop_with_rtc(rtc: &'static Rtc) {
102102
unsafe { EXECUTOR.as_mut().unwrap() }.stop_with_rtc(rtc)
103103
}
104104

105+
/// Configure STOP mode with RTC and HSEM. The hardware semaphore is needed for configuring the coprocessor
106+
#[cfg(all(feature = "low-power", any(stm32wb)))]
107+
pub fn stop_with_rtc_and_hsem(
108+
rtc: &'static Rtc,
109+
hsem: &'static mut crate::hsem::HardwareSemaphore<'static, crate::peripherals::HSEM>,
110+
) {
111+
let exec = unsafe { EXECUTOR.as_mut().unwrap() };
112+
exec.set_hsem(hsem);
113+
exec.stop_with_rtc(rtc)
114+
}
115+
105116
/// Get whether the core is ready to enter the given stop mode.
106117
///
107118
/// This will return false if some peripheral driver is in use that
@@ -124,10 +135,10 @@ pub enum StopMode {
124135
Stop2,
125136
}
126137

127-
#[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0))]
138+
#[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0, stm32wb))]
128139
use stm32_metapac::pwr::vals::Lpms;
129140

130-
#[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0))]
141+
#[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0, stm32wb))]
131142
impl Into<Lpms> for StopMode {
132143
fn into(self) -> Lpms {
133144
match self {
@@ -152,6 +163,12 @@ pub struct Executor {
152163
not_send: PhantomData<*mut ()>,
153164
scb: SCB,
154165
time_driver: &'static RtcDriver,
166+
167+
#[cfg(all(feature = "low-power", any(stm32wb)))]
168+
/// Special treatment of coprocessor requires a hardware semaphore
169+
hsem: embassy_sync::blocking_mutex::NoopMutex<
170+
core::cell::Cell<Option<&'static mut crate::hsem::HardwareSemaphore<'static, crate::peripherals::HSEM>>>,
171+
>,
155172
}
156173

157174
impl Executor {
@@ -165,6 +182,8 @@ impl Executor {
165182
not_send: PhantomData,
166183
scb: cortex_m::Peripherals::steal().SCB,
167184
time_driver: get_driver(),
185+
#[cfg(all(feature = "low-power", any(stm32wb)))]
186+
hsem: embassy_sync::blocking_mutex::NoopMutex::new(core::cell::Cell::default()),
168187
});
169188

170189
let executor = EXECUTOR.as_mut().unwrap();
@@ -178,6 +197,15 @@ impl Executor {
178197
trace!("low power: resume");
179198
}
180199

200+
#[cfg(all(feature = "low-power", any(stm32wb)))]
201+
pub(self) fn set_hsem(
202+
&mut self,
203+
hsem: &'static mut crate::hsem::HardwareSemaphore<'static, crate::peripherals::HSEM>,
204+
) {
205+
assert!(self.hsem.lock(|x| x.replace(Some(hsem))).is_none());
206+
trace!("low power: hsem configured");
207+
}
208+
181209
pub(self) fn stop_with_rtc(&mut self, rtc: &'static Rtc) {
182210
self.time_driver.set_rtc(rtc);
183211

@@ -198,7 +226,17 @@ impl Executor {
198226

199227
#[allow(unused_variables)]
200228
fn configure_stop(&mut self, stop_mode: StopMode) {
201-
#[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0))]
229+
#[cfg(all(feature = "low-power", any(stm32wb)))]
230+
{
231+
// Special treatment for MCUs with a coprocessor
232+
unsafe {
233+
self.hsem.lock_mut(|x| {
234+
crate::rcc::low_power::stm32wb_configure_clocks_enter_stop_mode(x.get_mut().as_mut().unwrap());
235+
});
236+
}
237+
}
238+
239+
#[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0, stm32wb))]
202240
crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into()));
203241
#[cfg(stm32h5)]
204242
crate::pac::PWR.pmcr().modify(|v| {
@@ -263,6 +301,15 @@ impl Executor {
263301
executor.inner.poll();
264302
self.configure_pwr();
265303
asm!("wfe");
304+
305+
#[cfg(all(feature = "low-power", any(stm32wb)))]
306+
{
307+
// Special treatment for MCUs with a coprocessor
308+
self.hsem.lock_mut(|x|
309+
310+
// Inentionally not calling from a critical section, as the critical section is handled internally
311+
crate::rcc::low_power::stm32wb_configure_clocks_exit_stop_mode(x.get_mut().as_mut().unwrap()));
312+
}
266313
};
267314
}
268315
}

embassy-stm32/src/rcc/l.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,9 @@ pub const WPAN_DEFAULT: Config = Config {
133133
mux: super::mux::ClockMux::default(),
134134
};
135135

136+
#[cfg(all(feature = "low-power", any(stm32wb)))]
137+
pub(super) static mut CURRENT_RCC_CONFIG: core::mem::MaybeUninit<Config> = core::mem::MaybeUninit::uninit();
138+
136139
fn msi_enable(range: MSIRange) {
137140
#[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl, stm32u0))]
138141
RCC.cr().modify(|w| {
@@ -149,6 +152,13 @@ fn msi_enable(range: MSIRange) {
149152
}
150153

151154
pub(crate) unsafe fn init(config: Config) {
155+
#[cfg(all(feature = "low-power", any(stm32wb)))]
156+
{
157+
// Store the current RCC config in to restore it when exiting low power mode.
158+
// This is needed because these MCUs require a clock source switch before entering low power mode.
159+
CURRENT_RCC_CONFIG = core::mem::MaybeUninit::new(config);
160+
}
161+
152162
// Switch to MSI to prevent problems with PLL configuration.
153163
if !RCC.cr().read().msion() {
154164
// Turn on MSI and configure it to 4MHz.
@@ -670,4 +680,4 @@ mod pll {
670680

671681
PllOutput { p, q, r }
672682
}
673-
}
683+
}

embassy-stm32/src/rcc/low_power.rs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
use crate::hsem::{self, HardwareSemaphore};
2+
3+
/// It is required to switch to the HSI clock before entering stop mode
4+
/// See an5289-how-to-build-wireless-applications-with-stm32wb-mcus-stmicroelectronics p. 23-24
5+
#[cfg(all(feature = "low-power", stm32wb))]
6+
pub(crate) fn stm32wb_configure_clocks_enter_stop_mode<T: hsem::Instance>(hsem: &mut HardwareSemaphore<T>) {
7+
use crate::pac::PWR;
8+
use crate::pac::{rcc::vals::Sw, RCC};
9+
use hsem::SemId;
10+
use stm32_metapac::rcc::vals::Smps;
11+
12+
critical_section::with(|_cs| {
13+
// Poll Sem3 until granted
14+
// TODO: Add some kind of timeout here and panic?
15+
trace!("Polling Sem3 until granted");
16+
while let Err(_) = hsem.one_step_lock(SemId::Id3) {}
17+
trace!("Sem3 granted");
18+
19+
// Get Sem4
20+
match hsem.one_step_lock(SemId::Id4) {
21+
Ok(_) => {
22+
// Sem4 granted
23+
if PWR.extscr().read().c2ds() {
24+
// C2DS set - unlock Sem4
25+
hsem.unlock(SemId::Id4, None);
26+
}
27+
}
28+
Err(_) => {
29+
// Sem4 not granted
30+
// Set HSION
31+
RCC.cr().modify(|w| {
32+
w.set_hsion(true);
33+
});
34+
35+
// Wait for HSIRDY
36+
while !RCC.cr().read().hsirdy() {}
37+
38+
// Set SW to HSI
39+
RCC.cfgr().modify(|w| {
40+
w.set_sw(Sw::HSI);
41+
});
42+
43+
// Wait for SWS to report HSI
44+
while !RCC.cfgr().read().sws().eq(&Sw::HSI) {}
45+
46+
// Set SMPSSEL to HSI
47+
RCC.smpscr().modify(|w| {
48+
w.set_smpssel(Smps::HSI);
49+
});
50+
}
51+
}
52+
53+
// Unlock Sem3
54+
hsem.unlock(SemId::Id3, None);
55+
});
56+
}
57+
58+
/// __Attention__: Must not be called from within a critical section
59+
#[cfg(all(feature = "low-power", stm32wb))]
60+
pub(crate) fn stm32wb_configure_clocks_exit_stop_mode<T: hsem::Instance>(hsem: &mut HardwareSemaphore<T>) {
61+
use super::{init, CURRENT_RCC_CONFIG};
62+
use crate::hsem::SemId;
63+
64+
// Release Sem4
65+
hsem.unlock(SemId::Id4, None);
66+
67+
// Enter the critical section afterwards
68+
critical_section::with(|_cs| {
69+
// Poll Sem3 until granted
70+
// TODO: Add some kind of timeout here and panic?
71+
while let Err(_) = hsem.one_step_lock(SemId::Id3) {}
72+
73+
// Restore the RCC config from before entering stop mode
74+
// Diveating from The flow chart of Figure 7 in AN5289
75+
// We always reconfigure the clocks as we don't know if HSE is suitable for the application
76+
let config = unsafe { CURRENT_RCC_CONFIG.assume_init() };
77+
unsafe { init(config) };
78+
79+
hsem.unlock(SemId::Id3, None);
80+
});
81+
}

embassy-stm32/src/rcc/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ pub use hsi48::*;
3030
#[cfg_attr(stm32wba, path = "wba.rs")]
3131
mod _version;
3232

33+
#[cfg(any(feature = "low-power", any(stm32wb)))]
34+
pub mod low_power;
35+
3336
pub use _version::*;
3437
use stm32_metapac::RCC;
3538

0 commit comments

Comments
 (0)