aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-stm32/src/adc/v3.rs2
-rw-r--r--embassy-stm32/src/i2c/v2.rs10
-rw-r--r--embassy-stm32/src/low_power.rs161
-rw-r--r--embassy-stm32/src/time_driver.rs8
4 files changed, 98 insertions, 83 deletions
diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs
index 62c2da557..170b08a25 100644
--- a/embassy-stm32/src/adc/v3.rs
+++ b/embassy-stm32/src/adc/v3.rs
@@ -463,7 +463,7 @@ impl<'d, T: Instance> Adc<'d, T> {
463 ); 463 );
464 464
465 #[cfg(all(feature = "low-power", stm32wlex))] 465 #[cfg(all(feature = "low-power", stm32wlex))]
466 let _device_busy = crate::low_power::DeviceBusy::new(); 466 let _device_busy = crate::low_power::DeviceBusy::new_stop1();
467 467
468 // Ensure no conversions are ongoing and ADC is enabled. 468 // Ensure no conversions are ongoing and ADC is enabled.
469 Self::cancel_conversions(); 469 Self::cancel_conversions();
diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs
index 6f2d03bd1..978a401d5 100644
--- a/embassy-stm32/src/i2c/v2.rs
+++ b/embassy-stm32/src/i2c/v2.rs
@@ -820,7 +820,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
820 /// Write. 820 /// Write.
821 pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { 821 pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> {
822 #[cfg(all(feature = "low-power", stm32wlex))] 822 #[cfg(all(feature = "low-power", stm32wlex))]
823 let _device_busy = crate::low_power::DeviceBusy::new(); 823 let _device_busy = crate::low_power::DeviceBusy::new_stop1();
824 let timeout = self.timeout(); 824 let timeout = self.timeout();
825 if write.is_empty() { 825 if write.is_empty() {
826 self.write_internal(address.into(), write, true, timeout) 826 self.write_internal(address.into(), write, true, timeout)
@@ -836,7 +836,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
836 /// The buffers are concatenated in a single write transaction. 836 /// The buffers are concatenated in a single write transaction.
837 pub async fn write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> { 837 pub async fn write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> {
838 #[cfg(all(feature = "low-power", stm32wlex))] 838 #[cfg(all(feature = "low-power", stm32wlex))]
839 let _device_busy = crate::low_power::DeviceBusy::new(); 839 let _device_busy = crate::low_power::DeviceBusy::new_stop1();
840 let timeout = self.timeout(); 840 let timeout = self.timeout();
841 841
842 if write.is_empty() { 842 if write.is_empty() {
@@ -861,7 +861,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
861 /// Read. 861 /// Read.
862 pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { 862 pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> {
863 #[cfg(all(feature = "low-power", stm32wlex))] 863 #[cfg(all(feature = "low-power", stm32wlex))]
864 let _device_busy = crate::low_power::DeviceBusy::new(); 864 let _device_busy = crate::low_power::DeviceBusy::new_stop1();
865 let timeout = self.timeout(); 865 let timeout = self.timeout();
866 866
867 if buffer.is_empty() { 867 if buffer.is_empty() {
@@ -875,7 +875,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
875 /// Write, restart, read. 875 /// Write, restart, read.
876 pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { 876 pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> {
877 #[cfg(all(feature = "low-power", stm32wlex))] 877 #[cfg(all(feature = "low-power", stm32wlex))]
878 let _device_busy = crate::low_power::DeviceBusy::new(); 878 let _device_busy = crate::low_power::DeviceBusy::new_stop1();
879 let timeout = self.timeout(); 879 let timeout = self.timeout();
880 880
881 if write.is_empty() { 881 if write.is_empty() {
@@ -902,7 +902,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
902 /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction 902 /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction
903 pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { 903 pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> {
904 #[cfg(all(feature = "low-power", stm32wlex))] 904 #[cfg(all(feature = "low-power", stm32wlex))]
905 let _device_busy = crate::low_power::DeviceBusy::new(); 905 let _device_busy = crate::low_power::DeviceBusy::new_stop1();
906 let _ = addr; 906 let _ = addr;
907 let _ = operations; 907 let _ = operations;
908 todo!() 908 todo!()
diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs
index cde3153f6..938db2686 100644
--- a/embassy-stm32/src/low_power.rs
+++ b/embassy-stm32/src/low_power.rs
@@ -57,10 +57,11 @@ use core::marker::PhantomData;
57use core::sync::atomic::{Ordering, compiler_fence}; 57use core::sync::atomic::{Ordering, compiler_fence};
58 58
59use cortex_m::peripheral::SCB; 59use cortex_m::peripheral::SCB;
60use critical_section::CriticalSection;
60use embassy_executor::*; 61use embassy_executor::*;
61 62
62use crate::interrupt; 63use crate::interrupt;
63use crate::time_driver::{RtcDriver, get_driver}; 64use crate::time_driver::get_driver;
64 65
65const THREAD_PENDER: usize = usize::MAX; 66const THREAD_PENDER: usize = usize::MAX;
66 67
@@ -68,46 +69,59 @@ use crate::rtc::Rtc;
68 69
69static mut EXECUTOR: Option<Executor> = None; 70static mut EXECUTOR: Option<Executor> = None;
70 71
71#[cfg(stm32wlex)] 72/// Prevent the device from going into the stop mode if held
72pub(crate) use self::busy::DeviceBusy; 73pub struct DeviceBusy(StopMode);
73#[cfg(stm32wlex)]
74mod busy {
75 use core::sync::atomic::{AtomicU32, Ordering};
76 74
77 // Count of devices blocking STOP 75impl DeviceBusy {
78 static STOP_BLOCKED: AtomicU32 = AtomicU32::new(0); 76 /// Create a new DeviceBusy with stop1.
77 pub fn new_stop1() -> Self {
78 Self::new(StopMode::Stop1)
79 }
79 80
80 /// Check if STOP1 is blocked. 81 /// Create a new DeviceBusy with stop2.
81 pub(crate) fn stop_blocked() -> bool { 82 pub fn new_stop2() -> Self {
82 STOP_BLOCKED.load(Ordering::SeqCst) > 0 83 Self::new(StopMode::Stop2)
83 } 84 }
84 85
85 /// When ca device goes busy it will construct one of these where it will be dropped when the device goes idle. 86 /// Create a new DeviceBusy.
86 pub(crate) struct DeviceBusy {} 87 pub fn new(stop_mode: StopMode) -> Self {
88 critical_section::with(|_| unsafe {
89 match stop_mode {
90 StopMode::Stop1 => {
91 crate::rcc::REFCOUNT_STOP1 += 1;
92 }
93 StopMode::Stop2 => {
94 crate::rcc::REFCOUNT_STOP2 += 1;
95 }
96 }
97 });
87 98
88 impl DeviceBusy { 99 Self(stop_mode)
89 /// Create a new DeviceBusy.
90 pub(crate) fn new() -> Self {
91 STOP_BLOCKED.fetch_add(1, Ordering::SeqCst);
92 trace!("low power: device busy: Stop:{}", STOP_BLOCKED.load(Ordering::SeqCst));
93 Self {}
94 }
95 } 100 }
101}
96 102
97 impl Drop for DeviceBusy { 103impl Drop for DeviceBusy {
98 fn drop(&mut self) { 104 fn drop(&mut self) {
99 STOP_BLOCKED.fetch_sub(1, Ordering::SeqCst); 105 critical_section::with(|_| unsafe {
100 trace!("low power: device idle: Stop:{}", STOP_BLOCKED.load(Ordering::SeqCst)); 106 match self.0 {
101 } 107 StopMode::Stop1 => {
108 crate::rcc::REFCOUNT_STOP1 -= 1;
109 }
110 StopMode::Stop2 => {
111 crate::rcc::REFCOUNT_STOP2 -= 1;
112 }
113 }
114 });
102 } 115 }
103} 116}
117
104#[cfg(not(stm32u0))] 118#[cfg(not(stm32u0))]
105foreach_interrupt! { 119foreach_interrupt! {
106 (RTC, rtc, $block:ident, WKUP, $irq:ident) => { 120 (RTC, rtc, $block:ident, WKUP, $irq:ident) => {
107 #[interrupt] 121 #[interrupt]
108 #[allow(non_snake_case)] 122 #[allow(non_snake_case)]
109 unsafe fn $irq() { 123 unsafe fn $irq() {
110 EXECUTOR.as_mut().unwrap().on_wakeup_irq(); 124 Executor::on_wakeup_irq();
111 } 125 }
112 }; 126 };
113} 127}
@@ -118,27 +132,28 @@ foreach_interrupt! {
118 #[interrupt] 132 #[interrupt]
119 #[allow(non_snake_case)] 133 #[allow(non_snake_case)]
120 unsafe fn $irq() { 134 unsafe fn $irq() {
121 EXECUTOR.as_mut().unwrap().on_wakeup_irq(); 135 Executor::on_wakeup_irq();
122 } 136 }
123 }; 137 };
124} 138}
125 139
126#[allow(dead_code)] 140#[allow(dead_code)]
127pub(crate) unsafe fn on_wakeup_irq() { 141pub(crate) unsafe fn on_wakeup_irq() {
128 if EXECUTOR.is_some() { 142 Executor::on_wakeup_irq();
129 trace!("low power: wakeup irq");
130 EXECUTOR.as_mut().unwrap().on_wakeup_irq();
131 }
132} 143}
133 144
134/// Configure STOP mode with RTC. 145/// Configure STOP mode with RTC.
135pub fn stop_with_rtc(rtc: Rtc) { 146pub fn stop_with_rtc(rtc: Rtc) {
136 unsafe { EXECUTOR.as_mut().unwrap() }.stop_with_rtc(rtc) 147 assert!(unsafe { EXECUTOR.is_some() });
148
149 Executor::stop_with_rtc(rtc)
137} 150}
138 151
139/// Reconfigure the RTC, if set. 152/// Reconfigure the RTC, if set.
140pub fn reconfigure_rtc(f: impl FnOnce(&mut Rtc)) { 153pub fn reconfigure_rtc(f: impl FnOnce(&mut Rtc)) {
141 unsafe { EXECUTOR.as_mut().unwrap() }.reconfigure_rtc(f); 154 assert!(unsafe { EXECUTOR.is_some() });
155
156 Executor::reconfigure_rtc(f);
142} 157}
143 158
144/// Get whether the core is ready to enter the given stop mode. 159/// Get whether the core is ready to enter the given stop mode.
@@ -146,11 +161,11 @@ pub fn reconfigure_rtc(f: impl FnOnce(&mut Rtc)) {
146/// This will return false if some peripheral driver is in use that 161/// This will return false if some peripheral driver is in use that
147/// prevents entering the given stop mode. 162/// prevents entering the given stop mode.
148pub fn stop_ready(stop_mode: StopMode) -> bool { 163pub fn stop_ready(stop_mode: StopMode) -> bool {
149 match unsafe { EXECUTOR.as_mut().unwrap() }.stop_mode() { 164 critical_section::with(|cs| match Executor::stop_mode(cs) {
150 Some(StopMode::Stop2) => true, 165 Some(StopMode::Stop2) => true,
151 Some(StopMode::Stop1) => stop_mode == StopMode::Stop1, 166 Some(StopMode::Stop1) => stop_mode == StopMode::Stop1,
152 None => false, 167 None => false,
153 } 168 })
154} 169}
155 170
156/// Available Stop modes. 171/// Available Stop modes.
@@ -193,7 +208,6 @@ pub struct Executor {
193 inner: raw::Executor, 208 inner: raw::Executor,
194 not_send: PhantomData<*mut ()>, 209 not_send: PhantomData<*mut ()>,
195 scb: SCB, 210 scb: SCB,
196 time_driver: &'static RtcDriver,
197} 211}
198 212
199impl Executor { 213impl Executor {
@@ -206,7 +220,6 @@ impl Executor {
206 inner: raw::Executor::new(THREAD_PENDER as *mut ()), 220 inner: raw::Executor::new(THREAD_PENDER as *mut ()),
207 not_send: PhantomData, 221 not_send: PhantomData,
208 scb: cortex_m::Peripherals::steal().SCB, 222 scb: cortex_m::Peripherals::steal().SCB,
209 time_driver: get_driver(),
210 }); 223 });
211 224
212 let executor = EXECUTOR.as_mut().unwrap(); 225 let executor = EXECUTOR.as_mut().unwrap();
@@ -215,54 +228,50 @@ impl Executor {
215 }) 228 })
216 } 229 }
217 230
218 unsafe fn on_wakeup_irq(&mut self) { 231 pub(self) unsafe fn on_wakeup_irq() {
219 #[cfg(stm32wlex)] 232 critical_section::with(|cs| {
220 { 233 if !get_driver().is_rtc_set(cs) {
221 let extscr = crate::pac::PWR.extscr().read(); 234 trace!("low power: wakeup irq; rtc not set");
222 if extscr.c1stop2f() || extscr.c1stopf() { 235
223 // when we wake from any stop mode we need to re-initialize the rcc 236 return;
224 crate::rcc::apply_resume_config(); 237 }
225 if extscr.c1stop2f() { 238
226 // when we wake from STOP2, we need to re-initialize the time driver 239 #[cfg(stm32wlex)]
227 critical_section::with(|cs| crate::time_driver::init_timer(cs)); 240 {
228 // reset the refcounts for STOP2 and STOP1 (initializing the time driver will increment one of them for the timer) 241 let extscr = crate::pac::PWR.extscr().read();
229 // and given that we just woke from STOP2, we can reset them 242 if extscr.c1stop2f() || extscr.c1stopf() {
230 crate::rcc::REFCOUNT_STOP2 = 0; 243 // when we wake from any stop mode we need to re-initialize the rcc
231 crate::rcc::REFCOUNT_STOP1 = 0; 244 crate::rcc::apply_resume_config();
245 if extscr.c1stop2f() {
246 // when we wake from STOP2, we need to re-initialize the time driver
247 crate::time_driver::init_timer(cs);
248 // reset the refcounts for STOP2 and STOP1 (initializing the time driver will increment one of them for the timer)
249 // and given that we just woke from STOP2, we can reset them
250 crate::rcc::REFCOUNT_STOP2 = 0;
251 crate::rcc::REFCOUNT_STOP1 = 0;
252 }
232 } 253 }
233 } 254 }
234 } 255 get_driver().resume_time();
235 self.time_driver.resume_time(); 256 trace!("low power: resume");
236 trace!("low power: resume"); 257 });
237 } 258 }
238 259
239 pub(self) fn stop_with_rtc(&mut self, rtc: Rtc) { 260 pub(self) fn stop_with_rtc(rtc: Rtc) {
240 self.time_driver.set_rtc(rtc); 261 get_driver().set_rtc(rtc);
241 self.time_driver.reconfigure_rtc(|rtc| rtc.enable_wakeup_line()); 262 get_driver().reconfigure_rtc(|rtc| rtc.enable_wakeup_line());
242 263
243 trace!("low power: stop with rtc configured"); 264 trace!("low power: stop with rtc configured");
244 } 265 }
245 266
246 pub(self) fn reconfigure_rtc(&mut self, f: impl FnOnce(&mut Rtc)) { 267 pub(self) fn reconfigure_rtc(f: impl FnOnce(&mut Rtc)) {
247 self.time_driver.reconfigure_rtc(f); 268 get_driver().reconfigure_rtc(f);
248 }
249
250 fn stop1_ok_to_enter(&self) -> bool {
251 #[cfg(stm32wlex)]
252 if self::busy::stop_blocked() {
253 return false;
254 }
255 unsafe { crate::rcc::REFCOUNT_STOP1 == 0 }
256 }
257
258 fn stop2_ok_to_enter(&self) -> bool {
259 self.stop1_ok_to_enter() && unsafe { crate::rcc::REFCOUNT_STOP2 == 0 }
260 } 269 }
261 270
262 fn stop_mode(&self) -> Option<StopMode> { 271 fn stop_mode(_cs: CriticalSection) -> Option<StopMode> {
263 if self.stop2_ok_to_enter() { 272 if unsafe { crate::rcc::REFCOUNT_STOP2 == 0 && crate::rcc::REFCOUNT_STOP1 == 0 } {
264 Some(StopMode::Stop2) 273 Some(StopMode::Stop2)
265 } else if self.stop1_ok_to_enter() { 274 } else if unsafe { crate::rcc::REFCOUNT_STOP1 == 0 } {
266 Some(StopMode::Stop1) 275 Some(StopMode::Stop1)
267 } else { 276 } else {
268 None 277 None
@@ -291,14 +300,14 @@ impl Executor {
291 300
292 compiler_fence(Ordering::SeqCst); 301 compiler_fence(Ordering::SeqCst);
293 302
294 let stop_mode = self.stop_mode(); 303 let stop_mode = critical_section::with(|cs| Self::stop_mode(cs));
295 304
296 if stop_mode.is_none() { 305 if stop_mode.is_none() {
297 trace!("low power: not ready to stop"); 306 trace!("low power: not ready to stop");
298 return; 307 return;
299 } 308 }
300 309
301 if self.time_driver.pause_time().is_err() { 310 if get_driver().pause_time().is_err() {
302 trace!("low power: failed to pause time"); 311 trace!("low power: failed to pause time");
303 return; 312 return;
304 } 313 }
diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs
index 4956d1f68..152d3d22e 100644
--- a/embassy-stm32/src/time_driver.rs
+++ b/embassy-stm32/src/time_driver.rs
@@ -413,6 +413,12 @@ impl RtcDriver {
413 } 413 }
414 414
415 #[cfg(feature = "low-power")] 415 #[cfg(feature = "low-power")]
416 /// Has the rtc been set?
417 pub(crate) fn is_rtc_set(&self, cs: CriticalSection) -> bool {
418 self.rtc.borrow(cs).borrow().is_some()
419 }
420
421 #[cfg(feature = "low-power")]
416 /// Set the rtc but panic if it's already been set 422 /// Set the rtc but panic if it's already been set
417 pub(crate) fn reconfigure_rtc(&self, f: impl FnOnce(&mut Rtc)) { 423 pub(crate) fn reconfigure_rtc(&self, f: impl FnOnce(&mut Rtc)) {
418 critical_section::with(|cs| f(self.rtc.borrow(cs).borrow_mut().as_mut().unwrap())); 424 critical_section::with(|cs| f(self.rtc.borrow(cs).borrow_mut().as_mut().unwrap()));
@@ -543,7 +549,7 @@ impl Driver for RtcDriver {
543} 549}
544 550
545#[cfg(feature = "low-power")] 551#[cfg(feature = "low-power")]
546pub(crate) fn get_driver() -> &'static RtcDriver { 552pub(crate) const fn get_driver() -> &'static RtcDriver {
547 &DRIVER 553 &DRIVER
548} 554}
549 555