From c93da8fc6d76cd6978c0cfbfb3ab42b0285af8d8 Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 8 Dec 2025 08:19:24 -0600 Subject: low-power: use scoped block stop Co-authored-by: hjeldin --- embassy-stm32/src/i2c/mod.rs | 2 +- embassy-stm32/src/i2c/v1.rs | 7 +++++++ embassy-stm32/src/i2c/v2.rs | 8 ++++++++ embassy-stm32/src/qspi/mod.rs | 4 +++- embassy-stm32/src/rcc/mod.rs | 10 ++++++++++ embassy-stm32/src/sdmmc/mod.rs | 12 +++++++++++- embassy-stm32/src/spi/mod.rs | 10 +++++++++- embassy-stm32/src/usart/mod.rs | 8 ++++---- 8 files changed, 53 insertions(+), 8 deletions(-) diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index ee60c3f44..0bf430ffc 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -226,7 +226,7 @@ impl<'d, M: Mode> I2c<'d, M, Master> { } fn enable_and_init(&mut self, config: Config) { - self.info.rcc.enable_and_reset(); + self.info.rcc.enable_and_reset_without_stop(); self.init(config); } } diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index 128a58db7..81a6d74c1 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -529,6 +529,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { /// Write. pub async fn write(&mut self, address: u8, write_buffer: &[u8]) -> Result<(), Error> { + let _scoped_block_stop = self.info.rcc.block_stop(); self.write_frame(address, write_buffer, FrameOptions::FirstAndLastFrame) .await?; @@ -537,6 +538,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { /// Read. pub async fn read(&mut self, address: u8, read_buffer: &mut [u8]) -> Result<(), Error> { + let _scoped_block_stop = self.info.rcc.block_stop(); self.read_frame(address, read_buffer, FrameOptions::FirstAndLastFrame) .await?; @@ -701,6 +703,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { /// Write, restart, read. pub async fn write_read(&mut self, address: u8, write_buffer: &[u8], read_buffer: &mut [u8]) -> Result<(), Error> { + let _scoped_block_stop = self.info.rcc.block_stop(); // Check empty read buffer before starting transaction. Otherwise, we would not generate the // stop condition below. if read_buffer.is_empty() { @@ -719,6 +722,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { /// /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction pub async fn transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { + let _scoped_block_stop = self.info.rcc.block_stop(); for (op, frame) in operation_frames(operations)? { match op { Operation::Read(read_buffer) => self.read_frame(address, read_buffer, frame).await?, @@ -1357,6 +1361,7 @@ impl<'d> I2c<'d, Async, MultiMaster> { /// (Read/Write) and the matched address. This method will suspend until /// an address match occurs. pub async fn listen(&mut self) -> Result { + let _scoped_block_stop = self.info.rcc.block_stop(); trace!("I2C slave: starting async listen for address match"); let state = self.state; let info = self.info; @@ -1421,6 +1426,7 @@ impl<'d> I2c<'d, Async, MultiMaster> { /// /// Returns the number of bytes stored in the buffer (not total received). pub async fn respond_to_write(&mut self, buffer: &mut [u8]) -> Result { + let _scoped_block_stop = self.info.rcc.block_stop(); trace!("I2C slave: starting respond_to_write, buffer_len={}", buffer.len()); if buffer.is_empty() { @@ -1454,6 +1460,7 @@ impl<'d> I2c<'d, Async, MultiMaster> { /// /// Returns the total number of bytes transmitted (data + padding). pub async fn respond_to_read(&mut self, data: &[u8]) -> Result { + let _scoped_block_stop = self.info.rcc.block_stop(); trace!("I2C slave: starting respond_to_read, data_len={}", data.len()); if data.is_empty() { diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 7ad9978b1..32ce83d40 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -1075,6 +1075,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { /// Write. pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { + let _scoped_block_stop = self.info.rcc.block_stop(); let timeout = self.timeout(); if write.is_empty() { self.write_internal(address.into(), write, true, timeout) @@ -1089,6 +1090,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { /// /// The buffers are concatenated in a single write transaction. pub async fn write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> { + let _scoped_block_stop = self.info.rcc.block_stop(); let timeout = self.timeout(); if write.is_empty() { @@ -1120,6 +1122,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { /// Read. pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { + let _scoped_block_stop = self.info.rcc.block_stop(); let timeout = self.timeout(); if buffer.is_empty() { @@ -1132,6 +1135,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { /// Write, restart, read. pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { + let _scoped_block_stop = self.info.rcc.block_stop(); let timeout = self.timeout(); if write.is_empty() { @@ -1157,6 +1161,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { /// /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { + let _scoped_block_stop = self.info.rcc.block_stop(); if operations.is_empty() { return Err(Error::ZeroLengthTransfer); } @@ -1677,6 +1682,7 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { /// /// The listen method is an asynchronous method but it does not require DMA to be asynchronous. pub async fn listen(&mut self) -> Result { + let _scoped_block_stop = self.info.rcc.block_stop(); let state = self.state; self.info.regs.cr1().modify(|reg| { reg.set_addrie(true); @@ -1733,12 +1739,14 @@ impl<'d> I2c<'d, Async, MultiMaster> { /// /// Returns the total number of bytes received. pub async fn respond_to_write(&mut self, buffer: &mut [u8]) -> Result { + let _scoped_block_stop = self.info.rcc.block_stop(); let timeout = self.timeout(); timeout.with(self.read_dma_internal_slave(buffer, timeout)).await } /// Respond to a read request from an I2C master. pub async fn respond_to_read(&mut self, write: &[u8]) -> Result { + let _scoped_block_stop = self.info.rcc.block_stop(); let timeout = self.timeout(); timeout.with(self.write_dma_internal_slave(write, timeout)).await } diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs index bb4f4f1d0..1f47f4845 100644 --- a/embassy-stm32/src/qspi/mod.rs +++ b/embassy-stm32/src/qspi/mod.rs @@ -111,7 +111,7 @@ impl<'d, T: Instance, M: PeriMode> Qspi<'d, T, M> { config: Config, fsel: FlashSelection, ) -> Self { - rcc::enable_and_reset::(); + rcc::enable_and_reset_without_stop::(); while T::REGS.sr().read().busy() {} @@ -403,6 +403,7 @@ impl<'d, T: Instance> Qspi<'d, T, Async> { /// Async read data, using DMA. pub async fn read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig) { + let _scoped_block_stop = T::RCC_INFO.block_stop(); let transfer = self.start_read_transfer(transaction, buf); transfer.await; } @@ -443,6 +444,7 @@ impl<'d, T: Instance> Qspi<'d, T, Async> { /// Async write data, using DMA. pub async fn write_dma(&mut self, buf: &[u8], transaction: TransferConfig) { + let _scoped_block_stop = T::RCC_INFO.block_stop(); let transfer = self.start_write_transfer(transaction, buf); transfer.await; } diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index a33ec5d7d..2a9a1595a 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -498,6 +498,16 @@ pub fn enable_and_reset() { T::RCC_INFO.enable_and_reset(); } +/// Enables and resets peripheral `T` without incrementing the stop refcount. +/// +/// # Safety +/// +/// Peripheral must not be in use. +// TODO: should this be `unsafe`? +pub fn enable_and_reset_without_stop() { + T::RCC_INFO.enable_and_reset_without_stop(); +} + /// Disables peripheral `T`. /// /// # Safety diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index e05131040..37ef7099f 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -675,7 +675,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { d7: Option>, config: Config, ) -> Self { - rcc::enable_and_reset::(); + rcc::enable_and_reset_without_stop::(); T::Interrupt::unpend(); unsafe { T::Interrupt::enable() }; @@ -1075,6 +1075,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { /// Read a data block. #[inline] pub async fn read_block(&mut self, block_idx: u32, buffer: &mut DataBlock) -> Result<(), Error> { + let _scoped_block_stop = T::RCC_INFO.block_stop(); let card_capacity = self.card()?.get_capacity(); // NOTE(unsafe) DataBlock uses align 4 @@ -1114,6 +1115,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { /// Read multiple data blocks. #[inline] pub async fn read_blocks(&mut self, block_idx: u32, blocks: &mut [DataBlock]) -> Result<(), Error> { + let _scoped_block_stop = T::RCC_INFO.block_stop(); let card_capacity = self.card()?.get_capacity(); // NOTE(unsafe) reinterpret buffer as &mut [u32] @@ -1160,6 +1162,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { /// Write a data block. pub async fn write_block(&mut self, block_idx: u32, buffer: &DataBlock) -> Result<(), Error> { + let _scoped_block_stop = T::RCC_INFO.block_stop(); let card = self.card.as_mut().ok_or(Error::NoCard)?; // NOTE(unsafe) DataBlock uses align 4 @@ -1216,6 +1219,7 @@ impl<'d, T: Instance> Sdmmc<'d, T> { /// Write multiple data blocks. pub async fn write_blocks(&mut self, block_idx: u32, blocks: &[DataBlock]) -> Result<(), Error> { + let _scoped_block_stop = T::RCC_INFO.block_stop(); let card = self.card.as_mut().ok_or(Error::NoCard)?; // NOTE(unsafe) reinterpret buffer as &[u32] @@ -1542,6 +1546,8 @@ impl<'d, T: Instance> Sdmmc<'d, T> { /// /// SD only. pub async fn init_sd_card(&mut self, freq: Hertz) -> Result<(), Error> { + let _scoped_block_stop = T::RCC_INFO.block_stop(); + self.init_internal(freq, SdmmcPeripheral::SdCard(Card::default())).await } @@ -1711,6 +1717,8 @@ impl<'d, T: Instance> Sdmmc<'d, T> { /// /// eMMC only. pub async fn init_emmc(&mut self, freq: Hertz) -> Result<(), Error> { + let _scoped_block_stop = T::RCC_INFO.block_stop(); + self.init_internal(freq, SdmmcPeripheral::Emmc(Emmc::default())).await } @@ -1845,6 +1853,7 @@ impl<'d, T: Instance> block_device_driver::BlockDevice<512> for Sdmmc<'d, T> { block_address: u32, buf: &mut [aligned::Aligned], ) -> Result<(), Self::Error> { + let _scoped_block_stop = T::RCC_INFO.block_stop(); // TODO: I think block_address needs to be adjusted by the partition start offset if buf.len() == 1 { let block = unsafe { &mut *(&mut buf[0] as *mut _ as *mut crate::sdmmc::DataBlock) }; @@ -1862,6 +1871,7 @@ impl<'d, T: Instance> block_device_driver::BlockDevice<512> for Sdmmc<'d, T> { block_address: u32, buf: &[aligned::Aligned], ) -> Result<(), Self::Error> { + let _scoped_block_stop = T::RCC_INFO.block_stop(); // TODO: I think block_address needs to be adjusted by the partition start offset if buf.len() == 1 { let block = unsafe { &*(&buf[0] as *const _ as *const crate::sdmmc::DataBlock) }; diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index 3b39fd2fb..c90e0cef4 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -230,7 +230,7 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> { let cpol = config.raw_polarity(); let lsbfirst = config.raw_byte_order(); - self.info.rcc.enable_and_reset(); + self.info.rcc.enable_and_reset_without_stop(); /* - Software NSS management (SSM = 1) @@ -848,6 +848,7 @@ impl<'d> Spi<'d, Async, Master> { impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> { /// SPI write, using DMA. pub async fn write(&mut self, data: &[W]) -> Result<(), Error> { + let _scoped_block_stop = self.info.rcc.block_stop(); if data.is_empty() { return Ok(()); } @@ -879,6 +880,7 @@ impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> { /// SPI read, using DMA. #[cfg(any(spi_v4, spi_v5, spi_v6))] pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> { + let _scoped_block_stop = self.info.rcc.block_stop(); if data.is_empty() { return Ok(()); } @@ -966,6 +968,7 @@ impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> { /// SPI read, using DMA. #[cfg(any(spi_v1, spi_v2, spi_v3))] pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> { + let _scoped_block_stop = self.info.rcc.block_stop(); if data.is_empty() { return Ok(()); } @@ -1013,6 +1016,7 @@ impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> { } async fn transfer_inner(&mut self, read: *mut [W], write: *const [W]) -> Result<(), Error> { + let _scoped_block_stop = self.info.rcc.block_stop(); assert_eq!(read.len(), write.len()); if read.len() == 0 { return Ok(()); @@ -1064,6 +1068,8 @@ impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> { /// The transfer runs for `max(read.len(), write.len())` bytes. If `read` is shorter extra bytes are ignored. /// If `write` is shorter it is padded with zero bytes. pub async fn transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> { + let _scoped_block_stop = self.info.rcc.block_stop(); + self.transfer_inner(read, write).await } @@ -1071,6 +1077,8 @@ impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> { /// /// This writes the contents of `data` on MOSI, and puts the received data on MISO in `data`, at the same time. pub async fn transfer_in_place(&mut self, data: &mut [W]) -> Result<(), Error> { + let _scoped_block_stop = self.info.rcc.block_stop(); + self.transfer_inner(data, data).await } } diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 8047d6005..d2c361c61 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -491,7 +491,7 @@ impl<'d> UartTx<'d, Async> { /// Initiate an asynchronous UART write pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> { - let _ = self.info.rcc.block_stop(); + let _scoped_block_stop = self.info.rcc.block_stop(); let r = self.info.regs; @@ -510,7 +510,7 @@ impl<'d> UartTx<'d, Async> { /// Wait until transmission complete pub async fn flush(&mut self) -> Result<(), Error> { - let _ = self.info.rcc.block_stop(); + let _scoped_block_stop = self.info.rcc.block_stop(); flush(&self.info, &self.state).await } @@ -730,7 +730,7 @@ impl<'d> UartRx<'d, Async> { /// Initiate an asynchronous UART read pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { - let _ = self.info.rcc.block_stop(); + let _scoped_block_stop = self.info.rcc.block_stop(); self.inner_read(buffer, false).await?; @@ -739,7 +739,7 @@ impl<'d> UartRx<'d, Async> { /// Initiate an asynchronous read with idle line detection enabled pub async fn read_until_idle(&mut self, buffer: &mut [u8]) -> Result { - let _ = self.info.rcc.block_stop(); + let _scoped_block_stop = self.info.rcc.block_stop(); self.inner_read(buffer, true).await } -- cgit