diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index c54adfb77..f570383b6 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -31,6 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Peripheral singletons now implement `Debug`, `PartialEq`, `defmt::Format` and `Eq` (except AnyPeripherals) (#2682) - `BurstConfig`, a device-specific configuration for configuring DMA transfers in burst mode (#2543) - `{DmaRxBuf, DmaTxBuf, DmaRxTxBuf}::set_burst_config` (#2543) +- ESP32-S2: DMA support for AES (#2699) ### Changed diff --git a/esp-hal/src/aes/mod.rs b/esp-hal/src/aes/mod.rs index 2d4870321..3bca9d404 100644 --- a/esp-hal/src/aes/mod.rs +++ b/esp-hal/src/aes/mod.rs @@ -227,7 +227,7 @@ pub enum Endianness { /// transfer, which can significantly speed up operations when dealing with /// large data volumes. It supports various cipher modes such as ECB, CBC, OFB, /// CTR, CFB8, and CFB128. -#[cfg(any(esp32c3, esp32c6, esp32h2, esp32s3))] +#[cfg(any(esp32c3, esp32c6, esp32h2, esp32s2, esp32s3))] pub mod dma { use crate::{ aes::{Key, Mode}, @@ -449,7 +449,7 @@ pub mod dma { Ok(()) } - #[cfg(any(esp32c3, esp32s3))] + #[cfg(any(esp32c3, esp32s2, esp32s3))] fn reset_aes(&self) { unsafe { let s = crate::peripherals::SYSTEM::steal(); diff --git a/esp-hal/src/dma/mod.rs b/esp-hal/src/dma/mod.rs index e23c99a35..6c542da5a 100644 --- a/esp-hal/src/dma/mod.rs +++ b/esp-hal/src/dma/mod.rs @@ -897,7 +897,7 @@ pub enum DmaPeripheral { Mem2Mem5 = 5, #[cfg(not(esp32c2))] Aes = 6, - #[cfg(gdma)] + #[cfg(any(esp32s2, gdma))] Sha = 7, #[cfg(any(esp32c3, esp32c6, esp32h2, esp32s3))] Adc = 8, diff --git a/esp-hal/src/dma/pdma.rs b/esp-hal/src/dma/pdma.rs deleted file mode 100644 index 2464f48b9..000000000 --- a/esp-hal/src/dma/pdma.rs +++ /dev/null @@ -1,1041 +0,0 @@ -//! # Direct Memory Access -//! -//! ## Overview -//! The `pdma` module is part of the DMA driver of `ESP32` and `ESP32-S2`. -//! -//! This module provides efficient direct data transfer capabilities between -//! peripherals and memory without involving the CPU. It enables bidirectional -//! data transfers through DMA channels, making it particularly useful for -//! high-speed data transfers, such as [SPI] and [I2S] communication. -//! -//! [SPI]: ../spi/index.html -//! [I2S]: ../i2s/index.html - -use critical_section::CriticalSection; -use portable_atomic::{AtomicBool, Ordering}; - -use crate::{ - asynch::AtomicWaker, - dma::*, - interrupt::Priority, - macros::handler, - peripheral::{Peripheral, PeripheralRef}, - peripherals::Interrupt, -}; - -type SpiRegisterBlock = crate::peripherals::spi2::RegisterBlock; -type I2sRegisterBlock = crate::peripherals::i2s0::RegisterBlock; - -#[doc(hidden)] -pub trait PdmaChannel: crate::private::Sealed { - type RegisterBlock; - - fn register_block(&self) -> &Self::RegisterBlock; - fn tx_waker(&self) -> &'static AtomicWaker; - fn rx_waker(&self) -> &'static AtomicWaker; - fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool; - - fn peripheral_interrupt(&self) -> Interrupt; - fn async_handler(&self) -> InterruptHandler; - fn rx_async_flag(&self) -> &'static AtomicBool; - fn tx_async_flag(&self) -> &'static AtomicBool; -} - -/// The RX half of an arbitrary SPI DMA channel. -pub struct AnySpiDmaRxChannel(AnySpiDmaChannel); - -impl crate::private::Sealed for AnySpiDmaRxChannel {} -impl DmaRxChannel for AnySpiDmaRxChannel {} -impl Peripheral for AnySpiDmaRxChannel { - type P = Self; - - unsafe fn clone_unchecked(&self) -> Self::P { - Self(self.0.clone_unchecked()) - } -} - -/// The TX half of an arbitrary SPI DMA channel. -pub struct AnySpiDmaTxChannel(AnySpiDmaChannel); - -impl crate::private::Sealed for AnySpiDmaTxChannel {} -impl DmaTxChannel for AnySpiDmaTxChannel {} -impl Peripheral for AnySpiDmaTxChannel { - type P = Self; - - unsafe fn clone_unchecked(&self) -> Self::P { - Self(self.0.clone_unchecked()) - } -} - -impl RegisterAccess for AnySpiDmaTxChannel { - fn reset(&self) { - let spi = self.0.register_block(); - spi.dma_conf().modify(|_, w| w.out_rst().set_bit()); - spi.dma_conf().modify(|_, w| w.out_rst().clear_bit()); - } - - fn set_burst_mode(&self, burst_mode: BurstConfig) { - let spi = self.0.register_block(); - spi.dma_conf() - .modify(|_, w| w.out_data_burst_en().bit(burst_mode.is_burst_enabled())); - } - - fn set_descr_burst_mode(&self, burst_mode: bool) { - let spi = self.0.register_block(); - spi.dma_conf() - .modify(|_, w| w.outdscr_burst_en().bit(burst_mode)); - } - - fn set_peripheral(&self, _peripheral: u8) { - // no-op - } - - fn set_link_addr(&self, address: u32) { - let spi = self.0.register_block(); - spi.dma_out_link() - .modify(|_, w| unsafe { w.outlink_addr().bits(address) }); - } - - fn start(&self) { - let spi = self.0.register_block(); - spi.dma_out_link() - .modify(|_, w| w.outlink_start().set_bit()); - } - - fn stop(&self) { - let spi = self.0.register_block(); - spi.dma_out_link().modify(|_, w| w.outlink_stop().set_bit()); - } - - fn restart(&self) { - let spi = self.0.register_block(); - spi.dma_out_link() - .modify(|_, w| w.outlink_restart().set_bit()); - } - - fn set_check_owner(&self, check_owner: Option) { - if check_owner == Some(true) { - panic!("SPI DMA does not support checking descriptor ownership"); - } - } - - fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool { - self.0.is_compatible_with(peripheral) - } - - #[cfg(psram_dma)] - fn set_ext_mem_block_size(&self, size: DmaExtMemBKSize) { - let spi = self.0.register_block(); - spi.dma_conf() - .modify(|_, w| unsafe { w.ext_mem_bk_size().bits(size as u8) }); - } - - #[cfg(psram_dma)] - fn can_access_psram(&self) -> bool { - matches!(self.0, AnySpiDmaChannel(AnySpiDmaChannelInner::Spi2(_))) - } -} - -impl TxRegisterAccess for AnySpiDmaTxChannel { - fn set_auto_write_back(&self, enable: bool) { - // there is no `auto_wrback` for SPI - assert!(!enable); - } - - fn last_dscr_address(&self) -> usize { - let spi = self.0.register_block(); - spi.out_eof_des_addr().read().dma_out_eof_des_addr().bits() as usize - } - - fn peripheral_interrupt(&self) -> Option { - None - } - - fn async_handler(&self) -> Option { - None - } -} - -impl InterruptAccess for AnySpiDmaTxChannel { - fn enable_listen(&self, interrupts: EnumSet, enable: bool) { - let reg_block = self.0.register_block(); - reg_block.dma_int_ena().modify(|_, w| { - for interrupt in interrupts { - match interrupt { - DmaTxInterrupt::TotalEof => w.out_total_eof().bit(enable), - DmaTxInterrupt::DescriptorError => w.outlink_dscr_error().bit(enable), - DmaTxInterrupt::Eof => w.out_eof().bit(enable), - DmaTxInterrupt::Done => w.out_done().bit(enable), - }; - } - w - }); - } - - fn is_listening(&self) -> EnumSet { - let mut result = EnumSet::new(); - - let spi = self.0.register_block(); - let int_ena = spi.dma_int_ena().read(); - if int_ena.out_total_eof().bit_is_set() { - result |= DmaTxInterrupt::TotalEof; - } - if int_ena.outlink_dscr_error().bit_is_set() { - result |= DmaTxInterrupt::DescriptorError; - } - if int_ena.out_eof().bit_is_set() { - result |= DmaTxInterrupt::Eof; - } - if int_ena.out_done().bit_is_set() { - result |= DmaTxInterrupt::Done; - } - - result - } - - fn clear(&self, interrupts: impl Into>) { - let spi = self.0.register_block(); - spi.dma_int_clr().write(|w| { - for interrupt in interrupts.into() { - match interrupt { - DmaTxInterrupt::TotalEof => w.out_total_eof().clear_bit_by_one(), - DmaTxInterrupt::DescriptorError => w.outlink_dscr_error().clear_bit_by_one(), - DmaTxInterrupt::Eof => w.out_eof().clear_bit_by_one(), - DmaTxInterrupt::Done => w.out_done().clear_bit_by_one(), - }; - } - w - }); - } - - fn pending_interrupts(&self) -> EnumSet { - let mut result = EnumSet::new(); - - let spi = self.0.register_block(); - let int_raw = spi.dma_int_raw().read(); - if int_raw.out_total_eof().bit_is_set() { - result |= DmaTxInterrupt::TotalEof; - } - if int_raw.outlink_dscr_error().bit_is_set() { - result |= DmaTxInterrupt::DescriptorError; - } - if int_raw.out_eof().bit_is_set() { - result |= DmaTxInterrupt::Eof; - } - if int_raw.out_done().bit_is_set() { - result |= DmaTxInterrupt::Done; - } - - result - } - - fn waker(&self) -> &'static AtomicWaker { - self.0.tx_waker() - } - - fn is_async(&self) -> bool { - self.0.tx_async_flag().load(Ordering::Acquire) - } - - fn set_async(&self, is_async: bool) { - self.0.tx_async_flag().store(is_async, Ordering::Release); - } -} - -impl RegisterAccess for AnySpiDmaRxChannel { - fn reset(&self) { - let spi = self.0.register_block(); - spi.dma_conf().modify(|_, w| w.in_rst().set_bit()); - spi.dma_conf().modify(|_, w| w.in_rst().clear_bit()); - } - - fn set_burst_mode(&self, _burst_mode: BurstConfig) {} - - fn set_descr_burst_mode(&self, burst_mode: bool) { - let spi = self.0.register_block(); - spi.dma_conf() - .modify(|_, w| w.indscr_burst_en().bit(burst_mode)); - } - - fn set_peripheral(&self, _peripheral: u8) { - // no-op - } - - fn set_link_addr(&self, address: u32) { - let spi = self.0.register_block(); - spi.dma_in_link() - .modify(|_, w| unsafe { w.inlink_addr().bits(address) }); - } - - fn start(&self) { - let spi = self.0.register_block(); - spi.dma_in_link().modify(|_, w| w.inlink_start().set_bit()); - } - - fn stop(&self) { - let spi = self.0.register_block(); - spi.dma_in_link().modify(|_, w| w.inlink_stop().set_bit()); - } - - fn restart(&self) { - let spi = self.0.register_block(); - spi.dma_in_link() - .modify(|_, w| w.inlink_restart().set_bit()); - } - - fn set_check_owner(&self, check_owner: Option) { - if check_owner == Some(true) { - panic!("SPI DMA does not support checking descriptor ownership"); - } - } - - fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool { - self.0.is_compatible_with(peripheral) - } - - #[cfg(psram_dma)] - fn set_ext_mem_block_size(&self, size: DmaExtMemBKSize) { - let spi = self.0.register_block(); - spi.dma_conf() - .modify(|_, w| unsafe { w.ext_mem_bk_size().bits(size as u8) }); - } - - #[cfg(psram_dma)] - fn can_access_psram(&self) -> bool { - matches!(self.0, AnySpiDmaChannel(AnySpiDmaChannelInner::Spi2(_))) - } -} - -impl RxRegisterAccess for AnySpiDmaRxChannel { - fn peripheral_interrupt(&self) -> Option { - Some(self.0.peripheral_interrupt()) - } - - fn async_handler(&self) -> Option { - Some(self.0.async_handler()) - } -} - -impl InterruptAccess for AnySpiDmaRxChannel { - fn enable_listen(&self, interrupts: EnumSet, enable: bool) { - let reg_block = self.0.register_block(); - reg_block.dma_int_ena().modify(|_, w| { - for interrupt in interrupts { - match interrupt { - DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().bit(enable), - DmaRxInterrupt::ErrorEof => w.in_err_eof().bit(enable), - DmaRxInterrupt::DescriptorError => w.inlink_dscr_error().bit(enable), - DmaRxInterrupt::DescriptorEmpty => w.inlink_dscr_empty().bit(enable), - DmaRxInterrupt::Done => w.in_done().bit(enable), - }; - } - w - }); - } - - fn is_listening(&self) -> EnumSet { - let mut result = EnumSet::new(); - - let spi = self.0.register_block(); - let int_ena = spi.dma_int_ena().read(); - if int_ena.inlink_dscr_error().bit_is_set() { - result |= DmaRxInterrupt::DescriptorError; - } - if int_ena.inlink_dscr_empty().bit_is_set() { - result |= DmaRxInterrupt::DescriptorEmpty; - } - if int_ena.in_suc_eof().bit_is_set() { - result |= DmaRxInterrupt::SuccessfulEof; - } - if int_ena.in_err_eof().bit_is_set() { - result |= DmaRxInterrupt::ErrorEof; - } - if int_ena.in_done().bit_is_set() { - result |= DmaRxInterrupt::Done; - } - - result - } - - fn clear(&self, interrupts: impl Into>) { - let spi = self.0.register_block(); - spi.dma_int_clr().write(|w| { - for interrupt in interrupts.into() { - match interrupt { - DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().clear_bit_by_one(), - DmaRxInterrupt::ErrorEof => w.in_err_eof().clear_bit_by_one(), - DmaRxInterrupt::DescriptorError => w.inlink_dscr_error().clear_bit_by_one(), - DmaRxInterrupt::DescriptorEmpty => w.inlink_dscr_empty().clear_bit_by_one(), - DmaRxInterrupt::Done => w.in_done().clear_bit_by_one(), - }; - } - w - }); - } - - fn pending_interrupts(&self) -> EnumSet { - let mut result = EnumSet::new(); - - let spi = self.0.register_block(); - let int_raw = spi.dma_int_raw().read(); - if int_raw.inlink_dscr_error().bit_is_set() { - result |= DmaRxInterrupt::DescriptorError; - } - if int_raw.inlink_dscr_empty().bit_is_set() { - result |= DmaRxInterrupt::DescriptorEmpty; - } - if int_raw.in_suc_eof().bit_is_set() { - result |= DmaRxInterrupt::SuccessfulEof; - } - if int_raw.in_err_eof().bit_is_set() { - result |= DmaRxInterrupt::ErrorEof; - } - if int_raw.in_done().bit_is_set() { - result |= DmaRxInterrupt::Done; - } - - result - } - - fn waker(&self) -> &'static AtomicWaker { - self.0.rx_waker() - } - - fn is_async(&self) -> bool { - self.0.rx_async_flag().load(Ordering::Relaxed) - } - - fn set_async(&self, _is_async: bool) { - self.0.rx_async_flag().store(_is_async, Ordering::Relaxed); - } -} - -/// The RX half of an arbitrary I2S DMA channel. -pub struct AnyI2sDmaRxChannel(AnyI2sDmaChannel); - -impl crate::private::Sealed for AnyI2sDmaRxChannel {} -impl DmaRxChannel for AnyI2sDmaRxChannel {} -impl Peripheral for AnyI2sDmaRxChannel { - type P = Self; - - unsafe fn clone_unchecked(&self) -> Self::P { - Self(self.0.clone_unchecked()) - } -} - -/// The TX half of an arbitrary I2S DMA channel. -pub struct AnyI2sDmaTxChannel(AnyI2sDmaChannel); - -impl crate::private::Sealed for AnyI2sDmaTxChannel {} -impl DmaTxChannel for AnyI2sDmaTxChannel {} -impl Peripheral for AnyI2sDmaTxChannel { - type P = Self; - - unsafe fn clone_unchecked(&self) -> Self::P { - Self(self.0.clone_unchecked()) - } -} - -impl RegisterAccess for AnyI2sDmaTxChannel { - fn reset(&self) { - let reg_block = self.0.register_block(); - reg_block.lc_conf().modify(|_, w| w.out_rst().set_bit()); - reg_block.lc_conf().modify(|_, w| w.out_rst().clear_bit()); - } - - fn set_burst_mode(&self, burst_mode: BurstConfig) { - let reg_block = self.0.register_block(); - reg_block - .lc_conf() - .modify(|_, w| w.out_data_burst_en().bit(burst_mode.is_burst_enabled())); - } - - fn set_descr_burst_mode(&self, burst_mode: bool) { - let reg_block = self.0.register_block(); - reg_block - .lc_conf() - .modify(|_, w| w.outdscr_burst_en().bit(burst_mode)); - } - - fn set_link_addr(&self, address: u32) { - let reg_block = self.0.register_block(); - reg_block - .out_link() - .modify(|_, w| unsafe { w.outlink_addr().bits(address) }); - } - - fn set_peripheral(&self, _peripheral: u8) { - // no-op - } - - fn start(&self) { - let reg_block = self.0.register_block(); - reg_block - .out_link() - .modify(|_, w| w.outlink_start().set_bit()); - } - - fn stop(&self) { - let reg_block = self.0.register_block(); - reg_block - .out_link() - .modify(|_, w| w.outlink_stop().set_bit()); - } - - fn restart(&self) { - let reg_block = self.0.register_block(); - reg_block - .out_link() - .modify(|_, w| w.outlink_restart().set_bit()); - } - - fn set_check_owner(&self, check_owner: Option) { - let reg_block = self.0.register_block(); - reg_block - .lc_conf() - .modify(|_, w| w.check_owner().bit(check_owner.unwrap_or(true))); - } - - fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool { - self.0.is_compatible_with(peripheral) - } - - #[cfg(psram_dma)] - fn set_ext_mem_block_size(&self, size: DmaExtMemBKSize) { - let spi = self.0.register_block(); - spi.lc_conf() - .modify(|_, w| unsafe { w.ext_mem_bk_size().bits(size as u8) }); - } - - #[cfg(psram_dma)] - fn can_access_psram(&self) -> bool { - matches!(self.0, AnyI2sDmaChannel(AnyI2sDmaChannelInner::I2s0(_))) - } -} - -impl TxRegisterAccess for AnyI2sDmaTxChannel { - fn set_auto_write_back(&self, enable: bool) { - let reg_block = self.0.register_block(); - reg_block - .lc_conf() - .modify(|_, w| w.out_auto_wrback().bit(enable)); - } - - fn last_dscr_address(&self) -> usize { - let reg_block = self.0.register_block(); - reg_block - .out_eof_des_addr() - .read() - .out_eof_des_addr() - .bits() as usize - } - - fn peripheral_interrupt(&self) -> Option { - None - } - - fn async_handler(&self) -> Option { - None - } -} - -impl InterruptAccess for AnyI2sDmaTxChannel { - fn enable_listen(&self, interrupts: EnumSet, enable: bool) { - let reg_block = self.0.register_block(); - reg_block.int_ena().modify(|_, w| { - for interrupt in interrupts { - match interrupt { - DmaTxInterrupt::TotalEof => w.out_total_eof().bit(enable), - DmaTxInterrupt::DescriptorError => w.out_dscr_err().bit(enable), - DmaTxInterrupt::Eof => w.out_eof().bit(enable), - DmaTxInterrupt::Done => w.out_done().bit(enable), - }; - } - w - }); - } - - fn is_listening(&self) -> EnumSet { - let mut result = EnumSet::new(); - - let reg_block = self.0.register_block(); - let int_ena = reg_block.int_ena().read(); - if int_ena.out_total_eof().bit_is_set() { - result |= DmaTxInterrupt::TotalEof; - } - if int_ena.out_dscr_err().bit_is_set() { - result |= DmaTxInterrupt::DescriptorError; - } - if int_ena.out_eof().bit_is_set() { - result |= DmaTxInterrupt::Eof; - } - if int_ena.out_done().bit_is_set() { - result |= DmaTxInterrupt::Done; - } - - result - } - - fn pending_interrupts(&self) -> EnumSet { - let mut result = EnumSet::new(); - - let reg_block = self.0.register_block(); - let int_raw = reg_block.int_raw().read(); - if int_raw.out_total_eof().bit_is_set() { - result |= DmaTxInterrupt::TotalEof; - } - if int_raw.out_dscr_err().bit_is_set() { - result |= DmaTxInterrupt::DescriptorError; - } - if int_raw.out_eof().bit_is_set() { - result |= DmaTxInterrupt::Eof; - } - if int_raw.out_done().bit_is_set() { - result |= DmaTxInterrupt::Done; - } - - result - } - - fn clear(&self, interrupts: impl Into>) { - let reg_block = self.0.register_block(); - reg_block.int_clr().write(|w| { - for interrupt in interrupts.into() { - match interrupt { - DmaTxInterrupt::TotalEof => w.out_total_eof().clear_bit_by_one(), - DmaTxInterrupt::DescriptorError => w.out_dscr_err().clear_bit_by_one(), - DmaTxInterrupt::Eof => w.out_eof().clear_bit_by_one(), - DmaTxInterrupt::Done => w.out_done().clear_bit_by_one(), - }; - } - w - }); - } - - fn waker(&self) -> &'static AtomicWaker { - self.0.tx_waker() - } - - fn is_async(&self) -> bool { - self.0.tx_async_flag().load(Ordering::Relaxed) - } - - fn set_async(&self, _is_async: bool) { - self.0.tx_async_flag().store(_is_async, Ordering::Relaxed); - } -} - -impl RegisterAccess for AnyI2sDmaRxChannel { - fn reset(&self) { - let reg_block = self.0.register_block(); - reg_block.lc_conf().modify(|_, w| w.in_rst().set_bit()); - reg_block.lc_conf().modify(|_, w| w.in_rst().clear_bit()); - } - - fn set_burst_mode(&self, _burst_mode: BurstConfig) {} - - fn set_descr_burst_mode(&self, burst_mode: bool) { - let reg_block = self.0.register_block(); - reg_block - .lc_conf() - .modify(|_, w| w.indscr_burst_en().bit(burst_mode)); - } - - fn set_link_addr(&self, address: u32) { - let reg_block = self.0.register_block(); - reg_block - .in_link() - .modify(|_, w| unsafe { w.inlink_addr().bits(address) }); - } - - fn set_peripheral(&self, _peripheral: u8) { - // no-op - } - - fn start(&self) { - let reg_block = self.0.register_block(); - reg_block - .in_link() - .modify(|_, w| w.inlink_start().set_bit()); - } - - fn stop(&self) { - let reg_block = self.0.register_block(); - reg_block.in_link().modify(|_, w| w.inlink_stop().set_bit()); - } - - fn restart(&self) { - let reg_block = self.0.register_block(); - reg_block - .in_link() - .modify(|_, w| w.inlink_restart().set_bit()); - } - - fn set_check_owner(&self, check_owner: Option) { - let reg_block = self.0.register_block(); - reg_block - .lc_conf() - .modify(|_, w| w.check_owner().bit(check_owner.unwrap_or(true))); - } - - fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool { - self.0.is_compatible_with(peripheral) - } - - #[cfg(psram_dma)] - fn set_ext_mem_block_size(&self, size: DmaExtMemBKSize) { - let spi = self.0.register_block(); - spi.lc_conf() - .modify(|_, w| unsafe { w.ext_mem_bk_size().bits(size as u8) }); - } - - #[cfg(psram_dma)] - fn can_access_psram(&self) -> bool { - matches!(self.0, AnyI2sDmaChannel(AnyI2sDmaChannelInner::I2s0(_))) - } -} - -impl RxRegisterAccess for AnyI2sDmaRxChannel { - fn peripheral_interrupt(&self) -> Option { - Some(self.0.peripheral_interrupt()) - } - - fn async_handler(&self) -> Option { - Some(self.0.async_handler()) - } -} - -impl InterruptAccess for AnyI2sDmaRxChannel { - fn enable_listen(&self, interrupts: EnumSet, enable: bool) { - let reg_block = self.0.register_block(); - reg_block.int_ena().modify(|_, w| { - for interrupt in interrupts { - match interrupt { - DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().bit(enable), - DmaRxInterrupt::ErrorEof => w.in_err_eof().bit(enable), - DmaRxInterrupt::DescriptorError => w.in_dscr_err().bit(enable), - DmaRxInterrupt::DescriptorEmpty => w.in_dscr_empty().bit(enable), - DmaRxInterrupt::Done => w.in_done().bit(enable), - }; - } - w - }); - } - - fn is_listening(&self) -> EnumSet { - let mut result = EnumSet::new(); - - let reg_block = self.0.register_block(); - let int_ena = reg_block.int_ena().read(); - if int_ena.in_dscr_err().bit_is_set() { - result |= DmaRxInterrupt::DescriptorError; - } - if int_ena.in_dscr_empty().bit_is_set() { - result |= DmaRxInterrupt::DescriptorEmpty; - } - if int_ena.in_suc_eof().bit_is_set() { - result |= DmaRxInterrupt::SuccessfulEof; - } - if int_ena.in_err_eof().bit_is_set() { - result |= DmaRxInterrupt::ErrorEof; - } - if int_ena.in_done().bit_is_set() { - result |= DmaRxInterrupt::Done; - } - - result - } - - fn pending_interrupts(&self) -> EnumSet { - let mut result = EnumSet::new(); - - let reg_block = self.0.register_block(); - let int_raw = reg_block.int_raw().read(); - if int_raw.in_dscr_err().bit_is_set() { - result |= DmaRxInterrupt::DescriptorError; - } - if int_raw.in_dscr_empty().bit_is_set() { - result |= DmaRxInterrupt::DescriptorEmpty; - } - if int_raw.in_suc_eof().bit_is_set() { - result |= DmaRxInterrupt::SuccessfulEof; - } - if int_raw.in_err_eof().bit_is_set() { - result |= DmaRxInterrupt::ErrorEof; - } - if int_raw.in_done().bit_is_set() { - result |= DmaRxInterrupt::Done; - } - - result - } - - fn clear(&self, interrupts: impl Into>) { - let reg_block = self.0.register_block(); - reg_block.int_clr().write(|w| { - for interrupt in interrupts.into() { - match interrupt { - DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().clear_bit_by_one(), - DmaRxInterrupt::ErrorEof => w.in_err_eof().clear_bit_by_one(), - DmaRxInterrupt::DescriptorError => w.in_dscr_err().clear_bit_by_one(), - DmaRxInterrupt::DescriptorEmpty => w.in_dscr_empty().clear_bit_by_one(), - DmaRxInterrupt::Done => w.in_done().clear_bit_by_one(), - }; - } - w - }); - } - - fn waker(&self) -> &'static AtomicWaker { - self.0.rx_waker() - } - - fn is_async(&self) -> bool { - self.0.rx_async_flag().load(Ordering::Relaxed) - } - - fn set_async(&self, _is_async: bool) { - self.0.rx_async_flag().store(_is_async, Ordering::Relaxed); - } -} - -macro_rules! ImplPdmaChannel { - ($peri:ident, $register_block:ident, $instance:ident, $int:ident, [$($compatible:ident),*]) => { - paste::paste! { - #[doc = concat!("DMA channel suitable for ", stringify!([< $instance:upper >]))] - #[non_exhaustive] - #[derive(Debug, PartialEq, Eq)] - #[cfg_attr(feature = "defmt", derive(defmt::Format))] - pub struct [<$instance DmaChannel>] {} - - impl $crate::private::Sealed for [<$instance DmaChannel>] {} - - impl Peripheral for [<$instance DmaChannel>] { - type P = Self; - - unsafe fn clone_unchecked(&self) -> Self::P { - Self::steal() - } - } - - impl [<$instance DmaChannel>] { - /// Unsafely constructs a new DMA channel. - /// - /// # Safety - /// - /// The caller must ensure that only a single instance is used. - pub unsafe fn steal() -> Self { - Self {} - } - } - - impl DmaChannel for [<$instance DmaChannel>] { - type Rx = [<$peri DmaRxChannel>]; - type Tx = [<$peri DmaTxChannel>]; - - unsafe fn split_internal(self, _: $crate::private::Internal) -> (Self::Rx, Self::Tx) { - ([<$peri DmaRxChannel>](Self {}.into()), [<$peri DmaTxChannel>](Self {}.into())) - } - } - - impl DmaChannelExt for [<$instance DmaChannel>] { - fn rx_interrupts() -> impl InterruptAccess { - [<$peri DmaRxChannel>](Self {}.into()) - } - fn tx_interrupts() -> impl InterruptAccess { - [<$peri DmaTxChannel>](Self {}.into()) - } - } - - impl PdmaChannel for [<$instance DmaChannel>] { - type RegisterBlock = $register_block; - - fn register_block(&self) -> &Self::RegisterBlock { - unsafe { &*crate::peripherals::[< $instance:upper >]::PTR } - } - fn tx_waker(&self) -> &'static AtomicWaker { - static WAKER: AtomicWaker = AtomicWaker::new(); - &WAKER - } - fn rx_waker(&self) -> &'static AtomicWaker { - static WAKER: AtomicWaker = AtomicWaker::new(); - &WAKER - } - fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool { - let compatible_peripherals = [$(DmaPeripheral::$compatible),*]; - compatible_peripherals.contains(&peripheral) - } - - fn peripheral_interrupt(&self) -> Interrupt { - Interrupt::$int - } - - fn async_handler(&self) -> InterruptHandler { - #[handler(priority = Priority::max())] - pub(crate) fn interrupt_handler() { - super::asynch::handle_in_interrupt::<[< $instance DmaChannel >]>(); - super::asynch::handle_out_interrupt::<[< $instance DmaChannel >]>(); - } - - interrupt_handler - } - fn rx_async_flag(&self) -> &'static AtomicBool { - static FLAG: AtomicBool = AtomicBool::new(false); - &FLAG - } - fn tx_async_flag(&self) -> &'static AtomicBool { - static FLAG: AtomicBool = AtomicBool::new(false); - &FLAG - } - } - - impl DmaChannelConvert<[<$peri DmaChannel>]> for [<$instance DmaChannel>] { - fn degrade(self) -> [<$peri DmaChannel>] { - self.into() - } - } - - impl DmaChannelConvert<[<$peri DmaRxChannel>]> for [<$instance DmaChannel>] { - fn degrade(self) -> [<$peri DmaRxChannel>] { - [<$peri DmaRxChannel>](self.into()) - } - } - - impl DmaChannelConvert<[<$peri DmaTxChannel>]> for [<$instance DmaChannel>] { - fn degrade(self) -> [<$peri DmaTxChannel>] { - [<$peri DmaTxChannel>](self.into()) - } - } - } - }; -} - -ImplPdmaChannel!(AnySpi, SpiRegisterBlock, Spi2, SPI2_DMA, [Spi2]); -ImplPdmaChannel!(AnySpi, SpiRegisterBlock, Spi3, SPI3_DMA, [Spi3]); - -ImplPdmaChannel!(AnyI2s, I2sRegisterBlock, I2s0, I2S0, [I2s0]); -#[cfg(i2s1)] -ImplPdmaChannel!(AnyI2s, I2sRegisterBlock, I2s1, I2S1, [I2s1]); - -// Specific peripherals use specific channels. Note that this may be overly -// restrictive (ESP32 allows configuring 2 SPI DMA channels between 3 different -// peripherals), but for the current set of restrictions this is sufficient. -crate::dma::impl_dma_eligible!([Spi2DmaChannel] SPI2 => Spi2); -crate::dma::impl_dma_eligible!([Spi3DmaChannel] SPI3 => Spi3); -crate::dma::impl_dma_eligible!([I2s0DmaChannel] I2S0 => I2s0); -#[cfg(i2s1)] -crate::dma::impl_dma_eligible!([I2s1DmaChannel] I2S1 => I2s1); - -pub(super) fn init_dma(_cs: CriticalSection<'_>) { - #[cfg(esp32)] - { - // (only) on ESP32 we need to configure DPORT for the SPI DMA channels - // This assignes the DMA channels to the SPI peripherals, which is more - // restrictive than necessary but we currently support the same - // number of SPI peripherals as SPI DMA channels so it's not a big - // deal. - let dport = unsafe { crate::peripherals::DPORT::steal() }; - dport - .spi_dma_chan_sel() - .modify(|_, w| unsafe { w.spi2_dma_chan_sel().bits(1).spi3_dma_chan_sel().bits(2) }); - } -} - -impl<'d, CH, M> Channel<'d, M, CH> -where - CH: DmaChannel, - M: Mode, -{ - /// Asserts that the channel is compatible with the given peripheral. - pub fn runtime_ensure_compatible(&self, peripheral: &PeripheralRef<'_, impl DmaEligible>) { - assert!( - self.tx - .tx_impl - .is_compatible_with(peripheral.dma_peripheral()), - "This DMA channel is not compatible with {:?}", - peripheral.dma_peripheral() - ); - } -} - -crate::any_peripheral! { - /// An SPI-compatible type-erased DMA channel. - pub peripheral AnySpiDmaChannel { - Spi2(Spi2DmaChannel), - Spi3(Spi3DmaChannel), - } -} - -impl DmaChannel for AnySpiDmaChannel { - type Rx = AnySpiDmaRxChannel; - type Tx = AnySpiDmaTxChannel; - - unsafe fn split_internal(self, _: crate::private::Internal) -> (Self::Rx, Self::Tx) { - ( - AnySpiDmaRxChannel(unsafe { self.clone_unchecked() }), - AnySpiDmaTxChannel(unsafe { self.clone_unchecked() }), - ) - } -} - -impl PdmaChannel for AnySpiDmaChannel { - type RegisterBlock = SpiRegisterBlock; - - delegate::delegate! { - to match &self.0 { - AnySpiDmaChannelInner::Spi2(channel) => channel, - AnySpiDmaChannelInner::Spi3(channel) => channel, - } { - fn register_block(&self) -> &SpiRegisterBlock; - fn tx_waker(&self) -> &'static AtomicWaker; - fn rx_waker(&self) -> &'static AtomicWaker; - fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool; - fn peripheral_interrupt(&self) -> Interrupt; - fn async_handler(&self) -> InterruptHandler; - fn rx_async_flag(&self) -> &'static AtomicBool; - fn tx_async_flag(&self) -> &'static AtomicBool; - } - } -} - -crate::any_peripheral! { - /// An I2S-compatible type-erased DMA channel. - pub peripheral AnyI2sDmaChannel { - I2s0(I2s0DmaChannel), - #[cfg(i2s1)] - I2s1(I2s1DmaChannel), - } -} - -impl DmaChannel for AnyI2sDmaChannel { - type Rx = AnyI2sDmaRxChannel; - type Tx = AnyI2sDmaTxChannel; - - unsafe fn split_internal(self, _: crate::private::Internal) -> (Self::Rx, Self::Tx) { - ( - AnyI2sDmaRxChannel(unsafe { self.clone_unchecked() }), - AnyI2sDmaTxChannel(unsafe { self.clone_unchecked() }), - ) - } -} - -impl PdmaChannel for AnyI2sDmaChannel { - type RegisterBlock = I2sRegisterBlock; - - delegate::delegate! { - to match &self.0 { - AnyI2sDmaChannelInner::I2s0(channel) => channel, - #[cfg(i2s1)] - AnyI2sDmaChannelInner::I2s1(channel) => channel, - } { - fn register_block(&self) -> &I2sRegisterBlock; - fn tx_waker(&self) -> &'static AtomicWaker; - fn rx_waker(&self) -> &'static AtomicWaker; - fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool; - fn peripheral_interrupt(&self) -> Interrupt; - fn async_handler(&self) -> InterruptHandler; - fn rx_async_flag(&self) -> &'static AtomicBool; - fn tx_async_flag(&self) -> &'static AtomicBool; - } - } -} diff --git a/esp-hal/src/dma/pdma/crypto.rs b/esp-hal/src/dma/pdma/crypto.rs new file mode 100644 index 000000000..3ef91a55a --- /dev/null +++ b/esp-hal/src/dma/pdma/crypto.rs @@ -0,0 +1,526 @@ +use portable_atomic::{AtomicBool, Ordering}; + +use crate::{ + asynch::AtomicWaker, + dma::*, + interrupt::Priority, + peripheral::Peripheral, + peripherals::Interrupt, +}; + +pub(super) type CryptoRegisterBlock = crate::peripherals::crypto_dma::RegisterBlock; + +/// The RX half of a Crypto DMA channel. +pub struct CryptoDmaRxChannel(pub(crate) CryptoDmaChannel); + +impl crate::private::Sealed for CryptoDmaRxChannel {} +impl DmaRxChannel for CryptoDmaRxChannel {} +impl Peripheral for CryptoDmaRxChannel { + type P = Self; + + unsafe fn clone_unchecked(&self) -> Self::P { + Self(self.0.clone_unchecked()) + } +} + +/// The TX half of a Crypto DMA channel. +pub struct CryptoDmaTxChannel(pub(crate) CryptoDmaChannel); + +impl crate::private::Sealed for CryptoDmaTxChannel {} +impl DmaTxChannel for CryptoDmaTxChannel {} +impl Peripheral for CryptoDmaTxChannel { + type P = Self; + + unsafe fn clone_unchecked(&self) -> Self::P { + Self(self.0.clone_unchecked()) + } +} + +impl RegisterAccess for CryptoDmaTxChannel { + fn reset(&self) { + let register_block = self.0.register_block(); + register_block.conf().modify(|_, w| { + w.out_rst().set_bit(); + w.ahbm_rst().set_bit(); + w.ahbm_fifo_rst().set_bit() + }); + register_block.conf().modify(|_, w| { + w.out_rst().clear_bit(); + w.ahbm_rst().clear_bit(); + w.ahbm_fifo_rst().clear_bit() + }); + } + + fn set_burst_mode(&self, burst_mode: BurstConfig) { + let register_block = self.0.register_block(); + register_block + .conf() + .modify(|_, w| w.out_data_burst_en().bit(burst_mode.is_burst_enabled())); + } + + fn set_descr_burst_mode(&self, burst_mode: bool) { + let register_block = self.0.register_block(); + register_block + .conf() + .modify(|_, w| w.outdscr_burst_en().bit(burst_mode)); + } + + fn set_peripheral(&self, peripheral: u8) { + use esp32s2::crypto_dma::aes_sha_select::SELECT; + let peripheral = match peripheral { + p if p == DmaPeripheral::Aes as u8 => SELECT::Aes, + p if p == DmaPeripheral::Sha as u8 => SELECT::Sha, + _ => unreachable!(), + }; + let register_block = self.0.register_block(); + register_block + .aes_sha_select() + .modify(|_, w| w.select().variant(peripheral)); + } + + fn set_link_addr(&self, address: u32) { + let register_block = self.0.register_block(); + register_block + .out_link() + .modify(|_, w| unsafe { w.outlink_addr().bits(address) }); + } + + fn start(&self) { + let register_block = self.0.register_block(); + register_block + .out_link() + .modify(|_, w| w.outlink_start().set_bit()); + } + + fn stop(&self) { + let register_block = self.0.register_block(); + register_block + .out_link() + .modify(|_, w| w.outlink_stop().set_bit()); + } + + fn restart(&self) { + let register_block = self.0.register_block(); + register_block + .out_link() + .modify(|_, w| w.outlink_restart().set_bit()); + } + + fn set_check_owner(&self, check_owner: Option) { + if check_owner == Some(true) { + panic!("Crypto DMA does not support checking descriptor ownership"); + } + } + + fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool { + self.0.is_compatible_with(peripheral) + } + + #[cfg(psram_dma)] + fn set_ext_mem_block_size(&self, size: DmaExtMemBKSize) { + let register_block = self.0.register_block(); + register_block + .conf1() + .modify(|_, w| unsafe { w.ext_mem_bk_size().bits(size as u8) }); + } + + #[cfg(psram_dma)] + fn can_access_psram(&self) -> bool { + true + } +} + +impl TxRegisterAccess for CryptoDmaTxChannel { + fn set_auto_write_back(&self, enable: bool) { + // there is no `auto_wrback` for SPI + assert!(!enable); + } + + fn last_dscr_address(&self) -> usize { + let register_block = self.0.register_block(); + register_block + .out_eof_des_addr() + .read() + .out_eof_des_addr() + .bits() as usize + } + + fn peripheral_interrupt(&self) -> Option { + None + } + + fn async_handler(&self) -> Option { + None + } +} + +impl InterruptAccess for CryptoDmaTxChannel { + fn enable_listen(&self, interrupts: EnumSet, enable: bool) { + let reg_block = self.0.register_block(); + reg_block.int_ena().modify(|_, w| { + for interrupt in interrupts { + match interrupt { + DmaTxInterrupt::TotalEof => w.out_total_eof().bit(enable), + DmaTxInterrupt::DescriptorError => w.out_dscr_err().bit(enable), + DmaTxInterrupt::Eof => w.out_eof().bit(enable), + DmaTxInterrupt::Done => w.out_done().bit(enable), + }; + } + w + }); + } + + fn is_listening(&self) -> EnumSet { + let mut result = EnumSet::new(); + + let register_block = self.0.register_block(); + let int_ena = register_block.int_ena().read(); + if int_ena.out_total_eof().bit_is_set() { + result |= DmaTxInterrupt::TotalEof; + } + if int_ena.out_dscr_err().bit_is_set() { + result |= DmaTxInterrupt::DescriptorError; + } + if int_ena.out_eof().bit_is_set() { + result |= DmaTxInterrupt::Eof; + } + if int_ena.out_done().bit_is_set() { + result |= DmaTxInterrupt::Done; + } + + result + } + + fn clear(&self, interrupts: impl Into>) { + let register_block = self.0.register_block(); + register_block.int_clr().write(|w| { + for interrupt in interrupts.into() { + match interrupt { + DmaTxInterrupt::TotalEof => w.out_total_eof().clear_bit_by_one(), + DmaTxInterrupt::DescriptorError => w.out_dscr_err().clear_bit_by_one(), + DmaTxInterrupt::Eof => w.out_eof().clear_bit_by_one(), + DmaTxInterrupt::Done => w.out_done().clear_bit_by_one(), + }; + } + w + }); + } + + fn pending_interrupts(&self) -> EnumSet { + let mut result = EnumSet::new(); + + let register_block = self.0.register_block(); + let int_raw = register_block.int_raw().read(); + if int_raw.out_total_eof().bit_is_set() { + result |= DmaTxInterrupt::TotalEof; + } + if int_raw.out_dscr_err().bit_is_set() { + result |= DmaTxInterrupt::DescriptorError; + } + if int_raw.out_eof().bit_is_set() { + result |= DmaTxInterrupt::Eof; + } + if int_raw.out_done().bit_is_set() { + result |= DmaTxInterrupt::Done; + } + + result + } + + fn waker(&self) -> &'static AtomicWaker { + self.0.tx_waker() + } + + fn is_async(&self) -> bool { + self.0.tx_async_flag().load(Ordering::Acquire) + } + + fn set_async(&self, is_async: bool) { + self.0.tx_async_flag().store(is_async, Ordering::Release); + } +} + +impl RegisterAccess for CryptoDmaRxChannel { + fn reset(&self) { + let register_block = self.0.register_block(); + register_block.conf().modify(|_, w| { + w.in_rst().set_bit(); + w.ahbm_rst().set_bit(); + w.ahbm_fifo_rst().set_bit() + }); + register_block.conf().modify(|_, w| { + w.in_rst().clear_bit(); + w.ahbm_rst().clear_bit(); + w.ahbm_fifo_rst().clear_bit() + }); + } + + fn set_burst_mode(&self, _burst_mode: BurstConfig) {} + + fn set_descr_burst_mode(&self, burst_mode: bool) { + let register_block = self.0.register_block(); + register_block + .conf() + .modify(|_, w| w.indscr_burst_en().bit(burst_mode)); + } + + fn set_peripheral(&self, peripheral: u8) { + use esp32s2::crypto_dma::aes_sha_select::SELECT; + let peripheral = match peripheral { + p if p == DmaPeripheral::Aes as u8 => SELECT::Aes, + p if p == DmaPeripheral::Sha as u8 => SELECT::Sha, + _ => unreachable!(), + }; + let register_block = self.0.register_block(); + register_block + .aes_sha_select() + .modify(|_, w| w.select().variant(peripheral)); + } + + fn set_link_addr(&self, address: u32) { + let register_block = self.0.register_block(); + register_block + .in_link() + .modify(|_, w| unsafe { w.inlink_addr().bits(address) }); + } + + fn start(&self) { + let register_block = self.0.register_block(); + register_block + .in_link() + .modify(|_, w| w.inlink_start().set_bit()); + } + + fn stop(&self) { + let register_block = self.0.register_block(); + register_block + .in_link() + .modify(|_, w| w.inlink_stop().set_bit()); + } + + fn restart(&self) { + let register_block = self.0.register_block(); + register_block + .in_link() + .modify(|_, w| w.inlink_restart().set_bit()); + } + + fn set_check_owner(&self, check_owner: Option) { + if check_owner == Some(true) { + panic!("Crypto DMA does not support checking descriptor ownership"); + } + } + + fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool { + self.0.is_compatible_with(peripheral) + } + + #[cfg(psram_dma)] + fn set_ext_mem_block_size(&self, size: DmaExtMemBKSize) { + let register_block = self.0.register_block(); + register_block + .conf1() + .modify(|_, w| unsafe { w.ext_mem_bk_size().bits(size as u8) }); + } + + #[cfg(psram_dma)] + fn can_access_psram(&self) -> bool { + true + } +} + +impl RxRegisterAccess for CryptoDmaRxChannel { + fn peripheral_interrupt(&self) -> Option { + // We don't know if the channel is used by AES or SHA, so interrupt handler + // setup is the responsibility of the peripheral driver. + None + } + + fn async_handler(&self) -> Option { + None + } +} + +impl InterruptAccess for CryptoDmaRxChannel { + fn enable_listen(&self, interrupts: EnumSet, enable: bool) { + let reg_block = self.0.register_block(); + reg_block.int_ena().modify(|_, w| { + for interrupt in interrupts { + match interrupt { + DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().bit(enable), + DmaRxInterrupt::ErrorEof => w.in_err_eof().bit(enable), + DmaRxInterrupt::DescriptorError => w.in_dscr_err().bit(enable), + DmaRxInterrupt::DescriptorEmpty => w.in_dscr_empty().bit(enable), + DmaRxInterrupt::Done => w.in_done().bit(enable), + }; + } + w + }); + } + + fn is_listening(&self) -> EnumSet { + let mut result = EnumSet::new(); + + let register_block = self.0.register_block(); + let int_ena = register_block.int_ena().read(); + if int_ena.in_dscr_err().bit_is_set() { + result |= DmaRxInterrupt::DescriptorError; + } + if int_ena.in_dscr_err().bit_is_set() { + result |= DmaRxInterrupt::DescriptorEmpty; + } + if int_ena.in_suc_eof().bit_is_set() { + result |= DmaRxInterrupt::SuccessfulEof; + } + if int_ena.in_err_eof().bit_is_set() { + result |= DmaRxInterrupt::ErrorEof; + } + if int_ena.in_done().bit_is_set() { + result |= DmaRxInterrupt::Done; + } + + result + } + + fn clear(&self, interrupts: impl Into>) { + let register_block = self.0.register_block(); + register_block.int_clr().write(|w| { + for interrupt in interrupts.into() { + match interrupt { + DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().clear_bit_by_one(), + DmaRxInterrupt::ErrorEof => w.in_err_eof().clear_bit_by_one(), + DmaRxInterrupt::DescriptorError => w.in_dscr_err().clear_bit_by_one(), + DmaRxInterrupt::DescriptorEmpty => w.in_dscr_empty().clear_bit_by_one(), + DmaRxInterrupt::Done => w.in_done().clear_bit_by_one(), + }; + } + w + }); + } + + fn pending_interrupts(&self) -> EnumSet { + let mut result = EnumSet::new(); + + let register_block = self.0.register_block(); + let int_raw = register_block.int_raw().read(); + if int_raw.in_dscr_err().bit_is_set() { + result |= DmaRxInterrupt::DescriptorError; + } + if int_raw.in_dscr_empty().bit_is_set() { + result |= DmaRxInterrupt::DescriptorEmpty; + } + if int_raw.in_suc_eof().bit_is_set() { + result |= DmaRxInterrupt::SuccessfulEof; + } + if int_raw.in_err_eof().bit_is_set() { + result |= DmaRxInterrupt::ErrorEof; + } + if int_raw.in_done().bit_is_set() { + result |= DmaRxInterrupt::Done; + } + + result + } + + fn waker(&self) -> &'static AtomicWaker { + self.0.rx_waker() + } + + fn is_async(&self) -> bool { + self.0.rx_async_flag().load(Ordering::Relaxed) + } + + fn set_async(&self, _is_async: bool) { + self.0.rx_async_flag().store(_is_async, Ordering::Relaxed); + } +} + +#[doc = "DMA channel suitable for CRYPTO"] +#[non_exhaustive] +pub struct CryptoDmaChannel {} + +impl crate::private::Sealed for CryptoDmaChannel {} + +impl Peripheral for CryptoDmaChannel { + type P = Self; + unsafe fn clone_unchecked(&self) -> Self::P { + Self::steal() + } +} +impl CryptoDmaChannel { + #[doc = r" Unsafely constructs a new DMA channel."] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r""] + #[doc = r" The caller must ensure that only a single instance is used."] + pub unsafe fn steal() -> Self { + Self {} + } +} +impl DmaChannel for CryptoDmaChannel { + type Rx = CryptoDmaRxChannel; + type Tx = CryptoDmaTxChannel; + unsafe fn split_internal(self, _: crate::private::Internal) -> (Self::Rx, Self::Tx) { + (CryptoDmaRxChannel(Self {}), CryptoDmaTxChannel(Self {})) + } +} +impl DmaChannelExt for CryptoDmaChannel { + fn rx_interrupts() -> impl InterruptAccess { + CryptoDmaRxChannel(Self {}) + } + fn tx_interrupts() -> impl InterruptAccess { + CryptoDmaTxChannel(Self {}) + } +} +impl PdmaChannel for CryptoDmaChannel { + type RegisterBlock = CryptoRegisterBlock; + fn register_block(&self) -> &Self::RegisterBlock { + unsafe { &*crate::peripherals::CRYPTO_DMA::PTR } + } + fn tx_waker(&self) -> &'static AtomicWaker { + static WAKER: AtomicWaker = AtomicWaker::new(); + &WAKER + } + fn rx_waker(&self) -> &'static AtomicWaker { + static WAKER: AtomicWaker = AtomicWaker::new(); + &WAKER + } + fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool { + let compatible_peripherals = [DmaPeripheral::Aes, DmaPeripheral::Sha]; + compatible_peripherals.contains(&peripheral) + } + fn peripheral_interrupt(&self) -> Interrupt { + unreachable!("Crypto DMA has separate interrupts specific to AES and SHA") + } + fn async_handler(&self) -> InterruptHandler { + pub(crate) extern "C" fn __esp_hal_internal_interrupt_handler() { + super::asynch::handle_in_interrupt::(); + super::asynch::handle_out_interrupt::(); + } + #[allow(non_upper_case_globals)] + pub(crate) static interrupt_handler: crate::interrupt::InterruptHandler = + crate::interrupt::InterruptHandler::new( + __esp_hal_internal_interrupt_handler, + Priority::max(), + ); + interrupt_handler + } + fn rx_async_flag(&self) -> &'static AtomicBool { + static FLAG: AtomicBool = AtomicBool::new(false); + &FLAG + } + fn tx_async_flag(&self) -> &'static AtomicBool { + static FLAG: AtomicBool = AtomicBool::new(false); + &FLAG + } +} +impl DmaChannelConvert for CryptoDmaChannel { + fn degrade(self) -> CryptoDmaRxChannel { + CryptoDmaRxChannel(self) + } +} +impl DmaChannelConvert for CryptoDmaChannel { + fn degrade(self) -> CryptoDmaTxChannel { + CryptoDmaTxChannel(self) + } +} diff --git a/esp-hal/src/dma/pdma/i2s.rs b/esp-hal/src/dma/pdma/i2s.rs new file mode 100644 index 000000000..a1de20138 --- /dev/null +++ b/esp-hal/src/dma/pdma/i2s.rs @@ -0,0 +1,436 @@ +use portable_atomic::{AtomicBool, Ordering}; + +use crate::{asynch::AtomicWaker, dma::*, peripheral::Peripheral, peripherals::Interrupt}; + +pub(super) type I2sRegisterBlock = crate::peripherals::i2s0::RegisterBlock; + +/// The RX half of an arbitrary I2S DMA channel. +pub struct AnyI2sDmaRxChannel(pub(crate) AnyI2sDmaChannel); + +impl crate::private::Sealed for AnyI2sDmaRxChannel {} +impl DmaRxChannel for AnyI2sDmaRxChannel {} +impl Peripheral for AnyI2sDmaRxChannel { + type P = Self; + + unsafe fn clone_unchecked(&self) -> Self::P { + Self(self.0.clone_unchecked()) + } +} + +/// The TX half of an arbitrary I2S DMA channel. +pub struct AnyI2sDmaTxChannel(pub(crate) AnyI2sDmaChannel); + +impl crate::private::Sealed for AnyI2sDmaTxChannel {} +impl DmaTxChannel for AnyI2sDmaTxChannel {} +impl Peripheral for AnyI2sDmaTxChannel { + type P = Self; + + unsafe fn clone_unchecked(&self) -> Self::P { + Self(self.0.clone_unchecked()) + } +} + +impl RegisterAccess for AnyI2sDmaTxChannel { + fn reset(&self) { + let reg_block = self.0.register_block(); + reg_block.lc_conf().modify(|_, w| w.out_rst().set_bit()); + reg_block.lc_conf().modify(|_, w| w.out_rst().clear_bit()); + } + + fn set_burst_mode(&self, burst_mode: BurstConfig) { + let reg_block = self.0.register_block(); + reg_block + .lc_conf() + .modify(|_, w| w.out_data_burst_en().bit(burst_mode.is_burst_enabled())); + } + + fn set_descr_burst_mode(&self, burst_mode: bool) { + let reg_block = self.0.register_block(); + reg_block + .lc_conf() + .modify(|_, w| w.outdscr_burst_en().bit(burst_mode)); + } + + fn set_link_addr(&self, address: u32) { + let reg_block = self.0.register_block(); + reg_block + .out_link() + .modify(|_, w| unsafe { w.outlink_addr().bits(address) }); + } + + fn set_peripheral(&self, _peripheral: u8) { + // no-op + } + + fn start(&self) { + let reg_block = self.0.register_block(); + reg_block + .out_link() + .modify(|_, w| w.outlink_start().set_bit()); + } + + fn stop(&self) { + let reg_block = self.0.register_block(); + reg_block + .out_link() + .modify(|_, w| w.outlink_stop().set_bit()); + } + + fn restart(&self) { + let reg_block = self.0.register_block(); + reg_block + .out_link() + .modify(|_, w| w.outlink_restart().set_bit()); + } + + fn set_check_owner(&self, check_owner: Option) { + let reg_block = self.0.register_block(); + reg_block + .lc_conf() + .modify(|_, w| w.check_owner().bit(check_owner.unwrap_or(true))); + } + + fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool { + self.0.is_compatible_with(peripheral) + } + + #[cfg(psram_dma)] + fn set_ext_mem_block_size(&self, size: DmaExtMemBKSize) { + let spi = self.0.register_block(); + spi.lc_conf() + .modify(|_, w| unsafe { w.ext_mem_bk_size().bits(size as u8) }); + } + + #[cfg(psram_dma)] + fn can_access_psram(&self) -> bool { + matches!(self.0, AnyI2sDmaChannel(AnyI2sDmaChannelInner::I2s0(_))) + } +} + +impl TxRegisterAccess for AnyI2sDmaTxChannel { + fn set_auto_write_back(&self, enable: bool) { + let reg_block = self.0.register_block(); + reg_block + .lc_conf() + .modify(|_, w| w.out_auto_wrback().bit(enable)); + } + + fn last_dscr_address(&self) -> usize { + let reg_block = self.0.register_block(); + reg_block + .out_eof_des_addr() + .read() + .out_eof_des_addr() + .bits() as usize + } + + fn peripheral_interrupt(&self) -> Option { + None + } + + fn async_handler(&self) -> Option { + None + } +} + +impl InterruptAccess for AnyI2sDmaTxChannel { + fn enable_listen(&self, interrupts: EnumSet, enable: bool) { + let reg_block = self.0.register_block(); + reg_block.int_ena().modify(|_, w| { + for interrupt in interrupts { + match interrupt { + DmaTxInterrupt::TotalEof => w.out_total_eof().bit(enable), + DmaTxInterrupt::DescriptorError => w.out_dscr_err().bit(enable), + DmaTxInterrupt::Eof => w.out_eof().bit(enable), + DmaTxInterrupt::Done => w.out_done().bit(enable), + }; + } + w + }); + } + + fn is_listening(&self) -> EnumSet { + let mut result = EnumSet::new(); + + let reg_block = self.0.register_block(); + let int_ena = reg_block.int_ena().read(); + if int_ena.out_total_eof().bit_is_set() { + result |= DmaTxInterrupt::TotalEof; + } + if int_ena.out_dscr_err().bit_is_set() { + result |= DmaTxInterrupt::DescriptorError; + } + if int_ena.out_eof().bit_is_set() { + result |= DmaTxInterrupt::Eof; + } + if int_ena.out_done().bit_is_set() { + result |= DmaTxInterrupt::Done; + } + + result + } + + fn pending_interrupts(&self) -> EnumSet { + let mut result = EnumSet::new(); + + let reg_block = self.0.register_block(); + let int_raw = reg_block.int_raw().read(); + if int_raw.out_total_eof().bit_is_set() { + result |= DmaTxInterrupt::TotalEof; + } + if int_raw.out_dscr_err().bit_is_set() { + result |= DmaTxInterrupt::DescriptorError; + } + if int_raw.out_eof().bit_is_set() { + result |= DmaTxInterrupt::Eof; + } + if int_raw.out_done().bit_is_set() { + result |= DmaTxInterrupt::Done; + } + + result + } + + fn clear(&self, interrupts: impl Into>) { + let reg_block = self.0.register_block(); + reg_block.int_clr().write(|w| { + for interrupt in interrupts.into() { + match interrupt { + DmaTxInterrupt::TotalEof => w.out_total_eof().clear_bit_by_one(), + DmaTxInterrupt::DescriptorError => w.out_dscr_err().clear_bit_by_one(), + DmaTxInterrupt::Eof => w.out_eof().clear_bit_by_one(), + DmaTxInterrupt::Done => w.out_done().clear_bit_by_one(), + }; + } + w + }); + } + + fn waker(&self) -> &'static AtomicWaker { + self.0.tx_waker() + } + + fn is_async(&self) -> bool { + self.0.tx_async_flag().load(Ordering::Relaxed) + } + + fn set_async(&self, _is_async: bool) { + self.0.tx_async_flag().store(_is_async, Ordering::Relaxed); + } +} + +impl RegisterAccess for AnyI2sDmaRxChannel { + fn reset(&self) { + let reg_block = self.0.register_block(); + reg_block.lc_conf().modify(|_, w| w.in_rst().set_bit()); + reg_block.lc_conf().modify(|_, w| w.in_rst().clear_bit()); + } + + fn set_burst_mode(&self, _burst_mode: BurstConfig) {} + + fn set_descr_burst_mode(&self, burst_mode: bool) { + let reg_block = self.0.register_block(); + reg_block + .lc_conf() + .modify(|_, w| w.indscr_burst_en().bit(burst_mode)); + } + + fn set_link_addr(&self, address: u32) { + let reg_block = self.0.register_block(); + reg_block + .in_link() + .modify(|_, w| unsafe { w.inlink_addr().bits(address) }); + } + + fn set_peripheral(&self, _peripheral: u8) { + // no-op + } + + fn start(&self) { + let reg_block = self.0.register_block(); + reg_block + .in_link() + .modify(|_, w| w.inlink_start().set_bit()); + } + + fn stop(&self) { + let reg_block = self.0.register_block(); + reg_block.in_link().modify(|_, w| w.inlink_stop().set_bit()); + } + + fn restart(&self) { + let reg_block = self.0.register_block(); + reg_block + .in_link() + .modify(|_, w| w.inlink_restart().set_bit()); + } + + fn set_check_owner(&self, check_owner: Option) { + let reg_block = self.0.register_block(); + reg_block + .lc_conf() + .modify(|_, w| w.check_owner().bit(check_owner.unwrap_or(true))); + } + + fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool { + self.0.is_compatible_with(peripheral) + } + + #[cfg(psram_dma)] + fn set_ext_mem_block_size(&self, size: DmaExtMemBKSize) { + let spi = self.0.register_block(); + spi.lc_conf() + .modify(|_, w| unsafe { w.ext_mem_bk_size().bits(size as u8) }); + } + + #[cfg(psram_dma)] + fn can_access_psram(&self) -> bool { + matches!(self.0, AnyI2sDmaChannel(AnyI2sDmaChannelInner::I2s0(_))) + } +} + +impl RxRegisterAccess for AnyI2sDmaRxChannel { + fn peripheral_interrupt(&self) -> Option { + Some(self.0.peripheral_interrupt()) + } + + fn async_handler(&self) -> Option { + Some(self.0.async_handler()) + } +} + +impl InterruptAccess for AnyI2sDmaRxChannel { + fn enable_listen(&self, interrupts: EnumSet, enable: bool) { + let reg_block = self.0.register_block(); + reg_block.int_ena().modify(|_, w| { + for interrupt in interrupts { + match interrupt { + DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().bit(enable), + DmaRxInterrupt::ErrorEof => w.in_err_eof().bit(enable), + DmaRxInterrupt::DescriptorError => w.in_dscr_err().bit(enable), + DmaRxInterrupt::DescriptorEmpty => w.in_dscr_empty().bit(enable), + DmaRxInterrupt::Done => w.in_done().bit(enable), + }; + } + w + }); + } + + fn is_listening(&self) -> EnumSet { + let mut result = EnumSet::new(); + + let reg_block = self.0.register_block(); + let int_ena = reg_block.int_ena().read(); + if int_ena.in_dscr_err().bit_is_set() { + result |= DmaRxInterrupt::DescriptorError; + } + if int_ena.in_dscr_empty().bit_is_set() { + result |= DmaRxInterrupt::DescriptorEmpty; + } + if int_ena.in_suc_eof().bit_is_set() { + result |= DmaRxInterrupt::SuccessfulEof; + } + if int_ena.in_err_eof().bit_is_set() { + result |= DmaRxInterrupt::ErrorEof; + } + if int_ena.in_done().bit_is_set() { + result |= DmaRxInterrupt::Done; + } + + result + } + + fn pending_interrupts(&self) -> EnumSet { + let mut result = EnumSet::new(); + + let reg_block = self.0.register_block(); + let int_raw = reg_block.int_raw().read(); + if int_raw.in_dscr_err().bit_is_set() { + result |= DmaRxInterrupt::DescriptorError; + } + if int_raw.in_dscr_empty().bit_is_set() { + result |= DmaRxInterrupt::DescriptorEmpty; + } + if int_raw.in_suc_eof().bit_is_set() { + result |= DmaRxInterrupt::SuccessfulEof; + } + if int_raw.in_err_eof().bit_is_set() { + result |= DmaRxInterrupt::ErrorEof; + } + if int_raw.in_done().bit_is_set() { + result |= DmaRxInterrupt::Done; + } + + result + } + + fn clear(&self, interrupts: impl Into>) { + let reg_block = self.0.register_block(); + reg_block.int_clr().write(|w| { + for interrupt in interrupts.into() { + match interrupt { + DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().clear_bit_by_one(), + DmaRxInterrupt::ErrorEof => w.in_err_eof().clear_bit_by_one(), + DmaRxInterrupt::DescriptorError => w.in_dscr_err().clear_bit_by_one(), + DmaRxInterrupt::DescriptorEmpty => w.in_dscr_empty().clear_bit_by_one(), + DmaRxInterrupt::Done => w.in_done().clear_bit_by_one(), + }; + } + w + }); + } + + fn waker(&self) -> &'static AtomicWaker { + self.0.rx_waker() + } + + fn is_async(&self) -> bool { + self.0.rx_async_flag().load(Ordering::Relaxed) + } + + fn set_async(&self, _is_async: bool) { + self.0.rx_async_flag().store(_is_async, Ordering::Relaxed); + } +} + +crate::any_peripheral! { + /// An I2S-compatible type-erased DMA channel. + pub peripheral AnyI2sDmaChannel { + I2s0(I2s0DmaChannel), + #[cfg(i2s1)] + I2s1(I2s1DmaChannel), + } +} + +impl DmaChannel for AnyI2sDmaChannel { + type Rx = AnyI2sDmaRxChannel; + type Tx = AnyI2sDmaTxChannel; + + unsafe fn split_internal(self, _: crate::private::Internal) -> (Self::Rx, Self::Tx) { + ( + AnyI2sDmaRxChannel(unsafe { self.clone_unchecked() }), + AnyI2sDmaTxChannel(unsafe { self.clone_unchecked() }), + ) + } +} + +impl PdmaChannel for AnyI2sDmaChannel { + type RegisterBlock = I2sRegisterBlock; + + delegate::delegate! { + to match &self.0 { + AnyI2sDmaChannelInner::I2s0(channel) => channel, + #[cfg(i2s1)] + AnyI2sDmaChannelInner::I2s1(channel) => channel, + } { + fn register_block(&self) -> &I2sRegisterBlock; + fn tx_waker(&self) -> &'static AtomicWaker; + fn rx_waker(&self) -> &'static AtomicWaker; + fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool; + fn peripheral_interrupt(&self) -> Interrupt; + fn async_handler(&self) -> InterruptHandler; + fn rx_async_flag(&self) -> &'static AtomicBool; + fn tx_async_flag(&self) -> &'static AtomicBool; + } + } +} diff --git a/esp-hal/src/dma/pdma/mod.rs b/esp-hal/src/dma/pdma/mod.rs new file mode 100644 index 000000000..12d744f14 --- /dev/null +++ b/esp-hal/src/dma/pdma/mod.rs @@ -0,0 +1,212 @@ +//! # Direct Memory Access +//! +//! ## Overview +//! The `pdma` module is part of the DMA driver of `ESP32` and `ESP32-S2`. +//! +//! This module provides efficient direct data transfer capabilities between +//! peripherals and memory without involving the CPU. It enables bidirectional +//! data transfers through DMA channels, making it particularly useful for +//! high-speed data transfers, such as [SPI] and [I2S] communication. +//! +//! [SPI]: ../spi/index.html +//! [I2S]: ../i2s/index.html + +use critical_section::CriticalSection; +use portable_atomic::AtomicBool; + +use crate::{ + asynch::AtomicWaker, + dma::*, + interrupt::Priority, + macros::handler, + peripheral::{Peripheral, PeripheralRef}, + peripherals::Interrupt, +}; + +#[cfg(esp32s2)] +mod crypto; +mod i2s; +mod spi; + +#[cfg(esp32s2)] +pub use crypto::*; +pub use i2s::*; +pub use spi::*; + +#[doc(hidden)] +pub trait PdmaChannel: crate::private::Sealed { + type RegisterBlock; + + fn register_block(&self) -> &Self::RegisterBlock; + fn tx_waker(&self) -> &'static AtomicWaker; + fn rx_waker(&self) -> &'static AtomicWaker; + fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool; + + fn peripheral_interrupt(&self) -> Interrupt; + fn async_handler(&self) -> InterruptHandler; + fn rx_async_flag(&self) -> &'static AtomicBool; + fn tx_async_flag(&self) -> &'static AtomicBool; +} + +macro_rules! impl_pdma_channel { + ($peri:ident, $register_block:ident, $instance:ident, $int:ident, [$($compatible:ident),*]) => { + paste::paste! { + #[doc = concat!("DMA channel suitable for ", stringify!([< $instance:upper >]))] + #[non_exhaustive] + #[derive(Debug, PartialEq, Eq)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + pub struct [<$instance DmaChannel>] {} + + impl $crate::private::Sealed for [<$instance DmaChannel>] {} + + impl Peripheral for [<$instance DmaChannel>] { + type P = Self; + + unsafe fn clone_unchecked(&self) -> Self::P { + Self::steal() + } + } + + impl [<$instance DmaChannel>] { + /// Unsafely constructs a new DMA channel. + /// + /// # Safety + /// + /// The caller must ensure that only a single instance is used. + pub unsafe fn steal() -> Self { + Self {} + } + } + + impl DmaChannel for [<$instance DmaChannel>] { + type Rx = [<$peri DmaRxChannel>]; + type Tx = [<$peri DmaTxChannel>]; + + unsafe fn split_internal(self, _: $crate::private::Internal) -> (Self::Rx, Self::Tx) { + ([<$peri DmaRxChannel>](Self {}.into()), [<$peri DmaTxChannel>](Self {}.into())) + } + } + + impl DmaChannelExt for [<$instance DmaChannel>] { + fn rx_interrupts() -> impl InterruptAccess { + [<$peri DmaRxChannel>](Self {}.into()) + } + fn tx_interrupts() -> impl InterruptAccess { + [<$peri DmaTxChannel>](Self {}.into()) + } + } + + impl PdmaChannel for [<$instance DmaChannel>] { + type RegisterBlock = $register_block; + + fn register_block(&self) -> &Self::RegisterBlock { + unsafe { &*crate::peripherals::[< $instance:upper >]::PTR } + } + fn tx_waker(&self) -> &'static AtomicWaker { + static WAKER: AtomicWaker = AtomicWaker::new(); + &WAKER + } + fn rx_waker(&self) -> &'static AtomicWaker { + static WAKER: AtomicWaker = AtomicWaker::new(); + &WAKER + } + fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool { + let compatible_peripherals = [$(DmaPeripheral::$compatible),*]; + compatible_peripherals.contains(&peripheral) + } + + fn peripheral_interrupt(&self) -> Interrupt { + Interrupt::$int + } + + fn async_handler(&self) -> InterruptHandler { + #[handler(priority = Priority::max())] + pub(crate) fn interrupt_handler() { + super::asynch::handle_in_interrupt::<[< $instance DmaChannel >]>(); + super::asynch::handle_out_interrupt::<[< $instance DmaChannel >]>(); + } + + interrupt_handler + } + fn rx_async_flag(&self) -> &'static AtomicBool { + static FLAG: AtomicBool = AtomicBool::new(false); + &FLAG + } + fn tx_async_flag(&self) -> &'static AtomicBool { + static FLAG: AtomicBool = AtomicBool::new(false); + &FLAG + } + } + + impl DmaChannelConvert<[<$peri DmaChannel>]> for [<$instance DmaChannel>] { + fn degrade(self) -> [<$peri DmaChannel>] { + self.into() + } + } + + impl DmaChannelConvert<[<$peri DmaRxChannel>]> for [<$instance DmaChannel>] { + fn degrade(self) -> [<$peri DmaRxChannel>] { + [<$peri DmaRxChannel>](self.into()) + } + } + + impl DmaChannelConvert<[<$peri DmaTxChannel>]> for [<$instance DmaChannel>] { + fn degrade(self) -> [<$peri DmaTxChannel>] { + [<$peri DmaTxChannel>](self.into()) + } + } + } + }; +} + +impl_pdma_channel!(AnySpi, SpiRegisterBlock, Spi2, SPI2_DMA, [Spi2]); +impl_pdma_channel!(AnySpi, SpiRegisterBlock, Spi3, SPI3_DMA, [Spi3]); + +impl_pdma_channel!(AnyI2s, I2sRegisterBlock, I2s0, I2S0, [I2s0]); +#[cfg(i2s1)] +impl_pdma_channel!(AnyI2s, I2sRegisterBlock, I2s1, I2S1, [I2s1]); + +// Specific peripherals use specific channels. Note that this may be overly +// restrictive (ESP32 allows configuring 2 SPI DMA channels between 3 different +// peripherals), but for the current set of restrictions this is sufficient. +crate::dma::impl_dma_eligible!([Spi2DmaChannel] SPI2 => Spi2); +crate::dma::impl_dma_eligible!([Spi3DmaChannel] SPI3 => Spi3); +crate::dma::impl_dma_eligible!([I2s0DmaChannel] I2S0 => I2s0); +#[cfg(i2s1)] +crate::dma::impl_dma_eligible!([I2s1DmaChannel] I2S1 => I2s1); +#[cfg(esp32s2)] +crate::dma::impl_dma_eligible!([CryptoDmaChannel] AES => Aes); +#[cfg(esp32s2)] +crate::dma::impl_dma_eligible!([CryptoDmaChannel] SHA => Sha); + +pub(super) fn init_dma(_cs: CriticalSection<'_>) { + #[cfg(esp32)] + { + // (only) on ESP32 we need to configure DPORT for the SPI DMA channels + // This assignes the DMA channels to the SPI peripherals, which is more + // restrictive than necessary but we currently support the same + // number of SPI peripherals as SPI DMA channels so it's not a big + // deal. + let dport = unsafe { crate::peripherals::DPORT::steal() }; + dport + .spi_dma_chan_sel() + .modify(|_, w| unsafe { w.spi2_dma_chan_sel().bits(1).spi3_dma_chan_sel().bits(2) }); + } +} + +impl<'d, CH, M> Channel<'d, M, CH> +where + CH: DmaChannel, + M: Mode, +{ + /// Asserts that the channel is compatible with the given peripheral. + pub fn runtime_ensure_compatible(&self, peripheral: &PeripheralRef<'_, impl DmaEligible>) { + assert!( + self.tx + .tx_impl + .is_compatible_with(peripheral.dma_peripheral()), + "This DMA channel is not compatible with {:?}", + peripheral.dma_peripheral() + ); + } +} diff --git a/esp-hal/src/dma/pdma/spi.rs b/esp-hal/src/dma/pdma/spi.rs new file mode 100644 index 000000000..1d7e39a05 --- /dev/null +++ b/esp-hal/src/dma/pdma/spi.rs @@ -0,0 +1,414 @@ +use portable_atomic::{AtomicBool, Ordering}; + +use crate::{asynch::AtomicWaker, dma::*, peripheral::Peripheral, peripherals::Interrupt}; + +pub(super) type SpiRegisterBlock = crate::peripherals::spi2::RegisterBlock; + +/// The RX half of an arbitrary SPI DMA channel. +pub struct AnySpiDmaRxChannel(pub(crate) AnySpiDmaChannel); + +impl crate::private::Sealed for AnySpiDmaRxChannel {} +impl DmaRxChannel for AnySpiDmaRxChannel {} +impl Peripheral for AnySpiDmaRxChannel { + type P = Self; + + unsafe fn clone_unchecked(&self) -> Self::P { + Self(self.0.clone_unchecked()) + } +} + +/// The TX half of an arbitrary SPI DMA channel. +pub struct AnySpiDmaTxChannel(pub(crate) AnySpiDmaChannel); + +impl crate::private::Sealed for AnySpiDmaTxChannel {} +impl DmaTxChannel for AnySpiDmaTxChannel {} +impl Peripheral for AnySpiDmaTxChannel { + type P = Self; + + unsafe fn clone_unchecked(&self) -> Self::P { + Self(self.0.clone_unchecked()) + } +} + +impl RegisterAccess for AnySpiDmaTxChannel { + fn reset(&self) { + let spi = self.0.register_block(); + spi.dma_conf().modify(|_, w| w.out_rst().set_bit()); + spi.dma_conf().modify(|_, w| w.out_rst().clear_bit()); + } + + fn set_burst_mode(&self, burst_mode: BurstConfig) { + let spi = self.0.register_block(); + spi.dma_conf() + .modify(|_, w| w.out_data_burst_en().bit(burst_mode.is_burst_enabled())); + } + + fn set_descr_burst_mode(&self, burst_mode: bool) { + let spi = self.0.register_block(); + spi.dma_conf() + .modify(|_, w| w.outdscr_burst_en().bit(burst_mode)); + } + + fn set_peripheral(&self, _peripheral: u8) { + // no-op + } + + fn set_link_addr(&self, address: u32) { + let spi = self.0.register_block(); + spi.dma_out_link() + .modify(|_, w| unsafe { w.outlink_addr().bits(address) }); + } + + fn start(&self) { + let spi = self.0.register_block(); + spi.dma_out_link() + .modify(|_, w| w.outlink_start().set_bit()); + } + + fn stop(&self) { + let spi = self.0.register_block(); + spi.dma_out_link().modify(|_, w| w.outlink_stop().set_bit()); + } + + fn restart(&self) { + let spi = self.0.register_block(); + spi.dma_out_link() + .modify(|_, w| w.outlink_restart().set_bit()); + } + + fn set_check_owner(&self, check_owner: Option) { + if check_owner == Some(true) { + panic!("SPI DMA does not support checking descriptor ownership"); + } + } + + fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool { + self.0.is_compatible_with(peripheral) + } + + #[cfg(psram_dma)] + fn set_ext_mem_block_size(&self, size: DmaExtMemBKSize) { + let spi = self.0.register_block(); + spi.dma_conf() + .modify(|_, w| unsafe { w.ext_mem_bk_size().bits(size as u8) }); + } + + #[cfg(psram_dma)] + fn can_access_psram(&self) -> bool { + matches!(self.0, AnySpiDmaChannel(AnySpiDmaChannelInner::Spi2(_))) + } +} + +impl TxRegisterAccess for AnySpiDmaTxChannel { + fn set_auto_write_back(&self, enable: bool) { + // there is no `auto_wrback` for SPI + assert!(!enable); + } + + fn last_dscr_address(&self) -> usize { + let spi = self.0.register_block(); + spi.out_eof_des_addr().read().dma_out_eof_des_addr().bits() as usize + } + + fn peripheral_interrupt(&self) -> Option { + None + } + + fn async_handler(&self) -> Option { + None + } +} + +impl InterruptAccess for AnySpiDmaTxChannel { + fn enable_listen(&self, interrupts: EnumSet, enable: bool) { + let reg_block = self.0.register_block(); + reg_block.dma_int_ena().modify(|_, w| { + for interrupt in interrupts { + match interrupt { + DmaTxInterrupt::TotalEof => w.out_total_eof().bit(enable), + DmaTxInterrupt::DescriptorError => w.outlink_dscr_error().bit(enable), + DmaTxInterrupt::Eof => w.out_eof().bit(enable), + DmaTxInterrupt::Done => w.out_done().bit(enable), + }; + } + w + }); + } + + fn is_listening(&self) -> EnumSet { + let mut result = EnumSet::new(); + + let spi = self.0.register_block(); + let int_ena = spi.dma_int_ena().read(); + if int_ena.out_total_eof().bit_is_set() { + result |= DmaTxInterrupt::TotalEof; + } + if int_ena.outlink_dscr_error().bit_is_set() { + result |= DmaTxInterrupt::DescriptorError; + } + if int_ena.out_eof().bit_is_set() { + result |= DmaTxInterrupt::Eof; + } + if int_ena.out_done().bit_is_set() { + result |= DmaTxInterrupt::Done; + } + + result + } + + fn clear(&self, interrupts: impl Into>) { + let spi = self.0.register_block(); + spi.dma_int_clr().write(|w| { + for interrupt in interrupts.into() { + match interrupt { + DmaTxInterrupt::TotalEof => w.out_total_eof().clear_bit_by_one(), + DmaTxInterrupt::DescriptorError => w.outlink_dscr_error().clear_bit_by_one(), + DmaTxInterrupt::Eof => w.out_eof().clear_bit_by_one(), + DmaTxInterrupt::Done => w.out_done().clear_bit_by_one(), + }; + } + w + }); + } + + fn pending_interrupts(&self) -> EnumSet { + let mut result = EnumSet::new(); + + let spi = self.0.register_block(); + let int_raw = spi.dma_int_raw().read(); + if int_raw.out_total_eof().bit_is_set() { + result |= DmaTxInterrupt::TotalEof; + } + if int_raw.outlink_dscr_error().bit_is_set() { + result |= DmaTxInterrupt::DescriptorError; + } + if int_raw.out_eof().bit_is_set() { + result |= DmaTxInterrupt::Eof; + } + if int_raw.out_done().bit_is_set() { + result |= DmaTxInterrupt::Done; + } + + result + } + + fn waker(&self) -> &'static AtomicWaker { + self.0.tx_waker() + } + + fn is_async(&self) -> bool { + self.0.tx_async_flag().load(Ordering::Acquire) + } + + fn set_async(&self, is_async: bool) { + self.0.tx_async_flag().store(is_async, Ordering::Release); + } +} + +impl RegisterAccess for AnySpiDmaRxChannel { + fn reset(&self) { + let spi = self.0.register_block(); + spi.dma_conf().modify(|_, w| w.in_rst().set_bit()); + spi.dma_conf().modify(|_, w| w.in_rst().clear_bit()); + } + + fn set_burst_mode(&self, _burst_mode: BurstConfig) {} + + fn set_descr_burst_mode(&self, burst_mode: bool) { + let spi = self.0.register_block(); + spi.dma_conf() + .modify(|_, w| w.indscr_burst_en().bit(burst_mode)); + } + + fn set_peripheral(&self, _peripheral: u8) { + // no-op + } + + fn set_link_addr(&self, address: u32) { + let spi = self.0.register_block(); + spi.dma_in_link() + .modify(|_, w| unsafe { w.inlink_addr().bits(address) }); + } + + fn start(&self) { + let spi = self.0.register_block(); + spi.dma_in_link().modify(|_, w| w.inlink_start().set_bit()); + } + + fn stop(&self) { + let spi = self.0.register_block(); + spi.dma_in_link().modify(|_, w| w.inlink_stop().set_bit()); + } + + fn restart(&self) { + let spi = self.0.register_block(); + spi.dma_in_link() + .modify(|_, w| w.inlink_restart().set_bit()); + } + + fn set_check_owner(&self, check_owner: Option) { + if check_owner == Some(true) { + panic!("SPI DMA does not support checking descriptor ownership"); + } + } + + fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool { + self.0.is_compatible_with(peripheral) + } + + #[cfg(psram_dma)] + fn set_ext_mem_block_size(&self, size: DmaExtMemBKSize) { + let spi = self.0.register_block(); + spi.dma_conf() + .modify(|_, w| unsafe { w.ext_mem_bk_size().bits(size as u8) }); + } + + #[cfg(psram_dma)] + fn can_access_psram(&self) -> bool { + matches!(self.0, AnySpiDmaChannel(AnySpiDmaChannelInner::Spi2(_))) + } +} + +impl RxRegisterAccess for AnySpiDmaRxChannel { + fn peripheral_interrupt(&self) -> Option { + Some(self.0.peripheral_interrupt()) + } + + fn async_handler(&self) -> Option { + Some(self.0.async_handler()) + } +} + +impl InterruptAccess for AnySpiDmaRxChannel { + fn enable_listen(&self, interrupts: EnumSet, enable: bool) { + let reg_block = self.0.register_block(); + reg_block.dma_int_ena().modify(|_, w| { + for interrupt in interrupts { + match interrupt { + DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().bit(enable), + DmaRxInterrupt::ErrorEof => w.in_err_eof().bit(enable), + DmaRxInterrupt::DescriptorError => w.inlink_dscr_error().bit(enable), + DmaRxInterrupt::DescriptorEmpty => w.inlink_dscr_empty().bit(enable), + DmaRxInterrupt::Done => w.in_done().bit(enable), + }; + } + w + }); + } + + fn is_listening(&self) -> EnumSet { + let mut result = EnumSet::new(); + + let spi = self.0.register_block(); + let int_ena = spi.dma_int_ena().read(); + if int_ena.inlink_dscr_error().bit_is_set() { + result |= DmaRxInterrupt::DescriptorError; + } + if int_ena.inlink_dscr_empty().bit_is_set() { + result |= DmaRxInterrupt::DescriptorEmpty; + } + if int_ena.in_suc_eof().bit_is_set() { + result |= DmaRxInterrupt::SuccessfulEof; + } + if int_ena.in_err_eof().bit_is_set() { + result |= DmaRxInterrupt::ErrorEof; + } + if int_ena.in_done().bit_is_set() { + result |= DmaRxInterrupt::Done; + } + + result + } + + fn clear(&self, interrupts: impl Into>) { + let spi = self.0.register_block(); + spi.dma_int_clr().modify(|_, w| { + for interrupt in interrupts.into() { + match interrupt { + DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().clear_bit_by_one(), + DmaRxInterrupt::ErrorEof => w.in_err_eof().clear_bit_by_one(), + DmaRxInterrupt::DescriptorError => w.inlink_dscr_error().clear_bit_by_one(), + DmaRxInterrupt::DescriptorEmpty => w.inlink_dscr_empty().clear_bit_by_one(), + DmaRxInterrupt::Done => w.in_done().clear_bit_by_one(), + }; + } + w + }); + } + + fn pending_interrupts(&self) -> EnumSet { + let mut result = EnumSet::new(); + + let spi = self.0.register_block(); + let int_raw = spi.dma_int_raw().read(); + if int_raw.inlink_dscr_error().bit_is_set() { + result |= DmaRxInterrupt::DescriptorError; + } + if int_raw.inlink_dscr_empty().bit_is_set() { + result |= DmaRxInterrupt::DescriptorEmpty; + } + if int_raw.in_suc_eof().bit_is_set() { + result |= DmaRxInterrupt::SuccessfulEof; + } + if int_raw.in_err_eof().bit_is_set() { + result |= DmaRxInterrupt::ErrorEof; + } + if int_raw.in_done().bit_is_set() { + result |= DmaRxInterrupt::Done; + } + + result + } + + fn waker(&self) -> &'static AtomicWaker { + self.0.rx_waker() + } + + fn is_async(&self) -> bool { + self.0.rx_async_flag().load(Ordering::Relaxed) + } + + fn set_async(&self, _is_async: bool) { + self.0.rx_async_flag().store(_is_async, Ordering::Relaxed); + } +} + +crate::any_peripheral! { + /// An SPI-compatible type-erased DMA channel. + pub peripheral AnySpiDmaChannel { + Spi2(Spi2DmaChannel), + Spi3(Spi3DmaChannel), + } +} + +impl DmaChannel for AnySpiDmaChannel { + type Rx = AnySpiDmaRxChannel; + type Tx = AnySpiDmaTxChannel; + + unsafe fn split_internal(self, _: crate::private::Internal) -> (Self::Rx, Self::Tx) { + ( + AnySpiDmaRxChannel(unsafe { self.clone_unchecked() }), + AnySpiDmaTxChannel(unsafe { self.clone_unchecked() }), + ) + } +} + +impl PdmaChannel for AnySpiDmaChannel { + type RegisterBlock = SpiRegisterBlock; + + delegate::delegate! { + to match &self.0 { + AnySpiDmaChannelInner::Spi2(channel) => channel, + AnySpiDmaChannelInner::Spi3(channel) => channel, + } { + fn register_block(&self) -> &SpiRegisterBlock; + fn tx_waker(&self) -> &'static AtomicWaker; + fn rx_waker(&self) -> &'static AtomicWaker; + fn is_compatible_with(&self, peripheral: DmaPeripheral) -> bool; + fn peripheral_interrupt(&self) -> Interrupt; + fn async_handler(&self) -> InterruptHandler; + fn rx_async_flag(&self) -> &'static AtomicBool; + fn tx_async_flag(&self) -> &'static AtomicBool; + } + } +} diff --git a/esp-hal/src/soc/esp32s2/peripherals.rs b/esp-hal/src/soc/esp32s2/peripherals.rs index 868dd4c1f..becf9d22c 100644 --- a/esp-hal/src/soc/esp32s2/peripherals.rs +++ b/esp-hal/src/soc/esp32s2/peripherals.rs @@ -119,5 +119,6 @@ crate::peripherals! { DMA_SPI2: Spi2DmaChannel, DMA_SPI3: Spi3DmaChannel, DMA_I2S0: I2s0DmaChannel, + DMA_CRYPTO: CryptoDmaChannel, ] } diff --git a/esp-hal/src/system.rs b/esp-hal/src/system.rs index c5045827f..f930cf4e2 100755 --- a/esp-hal/src/system.rs +++ b/esp-hal/src/system.rs @@ -298,6 +298,7 @@ impl PeripheralClockControl { Peripheral::Dma => { perip_clk_en0.modify(|_, w| w.spi2_dma_clk_en().bit(enable)); perip_clk_en0.modify(|_, w| w.spi3_dma_clk_en().bit(enable)); + perip_clk_en1.modify(|_, w| w.crypto_dma_clk_en().bit(enable)); } #[cfg(esp32c3)] Peripheral::I2s0 => { @@ -484,6 +485,8 @@ impl PeripheralClockControl { perip_rst_en0.modify(|_, w| w.spi2_dma_rst().clear_bit()); perip_rst_en0.modify(|_, w| w.spi3_dma_rst().set_bit()); perip_rst_en0.modify(|_, w| w.spi3_dma_rst().clear_bit()); + perip_rst_en1.modify(|_, w| w.crypto_dma_rst().set_bit()); + perip_rst_en1.modify(|_, w| w.crypto_dma_rst().clear_bit()); } #[cfg(esp32c3)] Peripheral::I2s0 => { diff --git a/hil-test/tests/aes_dma.rs b/hil-test/tests/aes_dma.rs index 56db3ee82..1c27f0151 100644 --- a/hil-test/tests/aes_dma.rs +++ b/hil-test/tests/aes_dma.rs @@ -1,6 +1,6 @@ //! AES DMA Test -//% CHIPS: esp32c3 esp32c6 esp32h2 esp32s3 +//% CHIPS: esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 #![no_std] #![no_main] @@ -26,7 +26,13 @@ mod tests { #[test] fn test_aes_128_dma_encryption(peripherals: Peripherals) { - let dma_channel = peripherals.DMA_CH0; + cfg_if::cfg_if! { + if #[cfg(esp32s2)] { + let dma_channel = peripherals.DMA_CRYPTO; + } else { + let dma_channel = peripherals.DMA_CH0; + } + } let (mut output, rx_descriptors, input, tx_descriptors) = dma_buffers!(DMA_BUFFER_SIZE); @@ -64,7 +70,13 @@ mod tests { #[test] fn test_aes_128_dma_decryption(peripherals: Peripherals) { - let dma_channel = peripherals.DMA_CH0; + cfg_if::cfg_if! { + if #[cfg(esp32s2)] { + let dma_channel = peripherals.DMA_CRYPTO; + } else { + let dma_channel = peripherals.DMA_CH0; + } + } let (mut output, rx_descriptors, input, tx_descriptors) = dma_buffers!(DMA_BUFFER_SIZE); @@ -101,7 +113,13 @@ mod tests { #[test] fn test_aes_256_dma_encryption(peripherals: Peripherals) { - let dma_channel = peripherals.DMA_CH0; + cfg_if::cfg_if! { + if #[cfg(esp32s2)] { + let dma_channel = peripherals.DMA_CRYPTO; + } else { + let dma_channel = peripherals.DMA_CH0; + } + } let (mut output, rx_descriptors, input, tx_descriptors) = dma_buffers!(DMA_BUFFER_SIZE); @@ -139,7 +157,13 @@ mod tests { #[test] fn test_aes_256_dma_decryption(peripherals: Peripherals) { - let dma_channel = peripherals.DMA_CH0; + cfg_if::cfg_if! { + if #[cfg(esp32s2)] { + let dma_channel = peripherals.DMA_CRYPTO; + } else { + let dma_channel = peripherals.DMA_CH0; + } + } let (mut output, rx_descriptors, input, tx_descriptors) = dma_buffers!(DMA_BUFFER_SIZE);