diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8159992836f..f44d5eb8b99 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -71,7 +71,7 @@ jobs: # Install the Rust toolchain for Xtensa devices: - uses: esp-rs/xtensa-toolchain@v1.6 with: - version: 1.88.0.0 + version: 1.88.0 # Install the Rust stable toolchain for RISC-V devices: - uses: dtolnay/rust-toolchain@v1 diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 92869644009..22362389571 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -137,6 +137,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - UART: Added HW and SW flow control config option (#3435) - I2C master: `SoftwareTimeout` and `Config::with_software_timeout`. (#3577) - `esp_hal::time::{Instant, Duration}` now implement `Hash` (#3577) +- Added `Sdio` support (#3503) ### Changed diff --git a/esp-hal/Cargo.toml b/esp-hal/Cargo.toml index 77ab44e67bb..2ad8aa01d39 100644 --- a/esp-hal/Cargo.toml +++ b/esp-hal/Cargo.toml @@ -55,6 +55,7 @@ embassy-usb-synopsys-otg = { version = "0.3.0", optional = true } embedded-can = { version = "0.4.1", optional = true } esp-synopsys-usb-otg = { version = "0.4.2", optional = true } nb = { version = "1.1.0", optional = true } +embedded-hal-sdmmc = { version = "0.1.0-alpha.3", optional = true } # Logging interfaces, they are mutually exclusive so they need to be behind separate features. defmt = { version = "1.0.1", optional = true } @@ -70,6 +71,9 @@ rand_core-06 = { package = "rand_core", version = "0.6.4", optional rand_core-09 = { package = "rand_core", version = "0.9.0", optional = true } ufmt-write = { version = "0.1.0", optional = true } +# Alternative bitfield implementation with better performance +bitfielder = { version = "0.1.1", optional = true } + # IMPORTANT: # Each supported device MUST have its PAC included below along with a # corresponding feature. @@ -248,6 +252,8 @@ unstable = [ "dep:rand_core-09", "dep:nb", "dep:ufmt-write", + "dep:embedded-hal-sdmmc", + "dep:bitfielder", ] ## Libraries that depend on `esp-hal` should enable this feature to indicate their use of unstable APIs. diff --git a/esp-hal/src/dma/buffers.rs b/esp-hal/src/dma/buffers.rs index 9c59cbdfc92..ebcf4225e36 100644 --- a/esp-hal/src/dma/buffers.rs +++ b/esp-hal/src/dma/buffers.rs @@ -354,9 +354,12 @@ pub enum TransferDirection { /// Holds all the information needed to configure a DMA channel for a transfer. #[derive(PartialEq, Eq, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct Preparation { +pub struct Preparation +where + F: DescriptorFlagFields, +{ /// The descriptor the DMA will start from. - pub start: *mut DmaDescriptor, + pub start: *mut DmaDescriptor, /// The direction of the DMA transfer. pub direction: TransferDirection, @@ -422,7 +425,7 @@ pub struct Preparation { /// /// The implementing type must keep all its descriptors and the buffers they /// point to valid while the buffer is being transferred. -pub unsafe trait DmaTxBuffer { +pub unsafe trait DmaTxBuffer { /// A type providing operations that are safe to perform on the buffer /// whilst the DMA is actively using it. type View; @@ -436,7 +439,7 @@ pub unsafe trait DmaTxBuffer { /// information required to use this buffer. /// /// Note: This operation is idempotent. - fn prepare(&mut self) -> Preparation; + fn prepare(&mut self) -> Preparation; /// This is called before the DMA starts using the buffer. fn into_view(self) -> Self::View; @@ -456,7 +459,7 @@ pub unsafe trait DmaTxBuffer { /// /// The implementing type must keep all its descriptors and the buffers they /// point to valid while the buffer is being transferred. -pub unsafe trait DmaRxBuffer { +pub unsafe trait DmaRxBuffer { /// A type providing operations that are safe to perform on the buffer /// whilst the DMA is actively using it. type View; @@ -470,7 +473,7 @@ pub unsafe trait DmaRxBuffer { /// information required to use this buffer. /// /// Note: This operation is idempotent. - fn prepare(&mut self) -> Preparation; + fn prepare(&mut self) -> Preparation; /// This is called before the DMA starts using the buffer. fn into_view(self) -> Self::View; @@ -492,13 +495,16 @@ pub struct BufView(T); /// FIFO. See [DmaRxBuf] for receiving data. #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct DmaTxBuf { - descriptors: DescriptorSet<'static>, +pub struct DmaTxBuf +where + F: DescriptorFlagFields + 'static, +{ + descriptors: DescriptorSet<'static, F>, buffer: &'static mut [u8], burst: BurstConfig, } -impl DmaTxBuf { +impl DmaTxBuf { /// Creates a new [DmaTxBuf] from some descriptors and a buffer. /// /// There must be enough descriptors for the provided buffer. @@ -508,7 +514,7 @@ impl DmaTxBuf { /// Both the descriptors and buffer must be in DMA-capable memory. /// Only DRAM is supported for descriptors. pub fn new( - descriptors: &'static mut [DmaDescriptor], + descriptors: &'static mut [DmaDescriptor], buffer: &'static mut [u8], ) -> Result { Self::new_with_config(descriptors, buffer, BurstConfig::default()) @@ -523,7 +529,7 @@ impl DmaTxBuf { /// Both the descriptors and buffer must be in DMA-capable memory. /// Only DRAM is supported for descriptors. pub fn new_with_config( - descriptors: &'static mut [DmaDescriptor], + descriptors: &'static mut [DmaDescriptor], buffer: &'static mut [u8], config: impl Into, ) -> Result { @@ -563,7 +569,7 @@ impl DmaTxBuf { } /// Consume the buf, returning the descriptors and buffer. - pub fn split(self) -> (&'static mut [DmaDescriptor], &'static mut [u8]) { + pub fn split(self) -> (&'static mut [DmaDescriptor], &'static mut [u8]) { (self.descriptors.into_inner(), self.buffer) } @@ -633,11 +639,11 @@ impl DmaTxBuf { } } -unsafe impl DmaTxBuffer for DmaTxBuf { - type View = BufView; - type Final = DmaTxBuf; +unsafe impl DmaTxBuffer for DmaTxBuf { + type View = BufView>; + type Final = DmaTxBuf; - fn prepare(&mut self) -> Preparation { + fn prepare(&mut self) -> Preparation { cfg_if::cfg_if! { if #[cfg(psram_dma)] { let is_data_in_psram = !is_valid_ram_address(self.buffer.as_ptr() as usize); @@ -663,7 +669,7 @@ unsafe impl DmaTxBuffer for DmaTxBuf { } } - fn into_view(self) -> BufView { + fn into_view(self) -> Self::View { BufView(self) } @@ -679,13 +685,16 @@ unsafe impl DmaTxBuffer for DmaTxBuf { /// See [DmaTxBuf] for transmitting data. #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct DmaRxBuf { - descriptors: DescriptorSet<'static>, +pub struct DmaRxBuf +where + F: DescriptorFlagFields + 'static, +{ + descriptors: DescriptorSet<'static, F>, buffer: &'static mut [u8], burst: BurstConfig, } -impl DmaRxBuf { +impl DmaRxBuf { /// Creates a new [DmaRxBuf] from some descriptors and a buffer. /// /// There must be enough descriptors for the provided buffer. @@ -694,7 +703,7 @@ impl DmaRxBuf { /// Both the descriptors and buffer must be in DMA-capable memory. /// Only DRAM is supported. pub fn new( - descriptors: &'static mut [DmaDescriptor], + descriptors: &'static mut [DmaDescriptor], buffer: &'static mut [u8], ) -> Result { Self::new_with_config(descriptors, buffer, BurstConfig::default()) @@ -709,7 +718,7 @@ impl DmaRxBuf { /// Both the descriptors and buffer must be in DMA-capable memory. /// Only DRAM is supported for descriptors. pub fn new_with_config( - descriptors: &'static mut [DmaDescriptor], + descriptors: &'static mut [DmaDescriptor], buffer: &'static mut [u8], config: impl Into, ) -> Result { @@ -748,7 +757,7 @@ impl DmaRxBuf { } /// Consume the buf, returning the descriptors and buffer. - pub fn split(self) -> (&'static mut [DmaDescriptor], &'static mut [u8]) { + pub fn split(self) -> (&'static mut [DmaDescriptor], &'static mut [u8]) { (self.descriptors.into_inner(), self.buffer) } @@ -840,11 +849,11 @@ impl DmaRxBuf { } } -unsafe impl DmaRxBuffer for DmaRxBuf { - type View = BufView; - type Final = DmaRxBuf; +unsafe impl DmaRxBuffer for DmaRxBuf { + type View = BufView>; + type Final = DmaRxBuf; - fn prepare(&mut self) -> Preparation { + fn prepare(&mut self) -> Preparation { for desc in self.descriptors.linked_iter_mut() { desc.reset_for_rx(); } @@ -875,7 +884,7 @@ unsafe impl DmaRxBuffer for DmaRxBuf { } } - fn into_view(self) -> BufView { + fn into_view(self) -> Self::View { BufView(self) } @@ -892,14 +901,17 @@ unsafe impl DmaRxBuffer for DmaRxBuf { /// peripheral's FIFO. These are typically full-duplex transfers. #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct DmaRxTxBuf { - rx_descriptors: DescriptorSet<'static>, - tx_descriptors: DescriptorSet<'static>, +pub struct DmaRxTxBuf +where + F: DescriptorFlagFields + 'static, +{ + rx_descriptors: DescriptorSet<'static, F>, + tx_descriptors: DescriptorSet<'static, F>, buffer: &'static mut [u8], burst: BurstConfig, } -impl DmaRxTxBuf { +impl DmaRxTxBuf { /// Creates a new [DmaRxTxBuf] from some descriptors and a buffer. /// /// There must be enough descriptors for the provided buffer. @@ -908,8 +920,8 @@ impl DmaRxTxBuf { /// Both the descriptors and buffer must be in DMA-capable memory. /// Only DRAM is supported. pub fn new( - rx_descriptors: &'static mut [DmaDescriptor], - tx_descriptors: &'static mut [DmaDescriptor], + rx_descriptors: &'static mut [DmaDescriptor], + tx_descriptors: &'static mut [DmaDescriptor], buffer: &'static mut [u8], ) -> Result { let mut buf = Self { @@ -958,8 +970,8 @@ impl DmaRxTxBuf { pub fn split( self, ) -> ( - &'static mut [DmaDescriptor], - &'static mut [DmaDescriptor], + &'static mut [DmaDescriptor], + &'static mut [DmaDescriptor], &'static mut [u8], ) { ( @@ -1021,11 +1033,11 @@ impl DmaRxTxBuf { } } -unsafe impl DmaTxBuffer for DmaRxTxBuf { - type View = BufView; - type Final = DmaRxTxBuf; +unsafe impl DmaTxBuffer for DmaRxTxBuf { + type View = BufView>; + type Final = DmaRxTxBuf; - fn prepare(&mut self) -> Preparation { + fn prepare(&mut self) -> Preparation { for desc in self.tx_descriptors.linked_iter_mut() { // In non-circular mode, we only set `suc_eof` for the last descriptor to signal // the end of the transfer. @@ -1058,7 +1070,7 @@ unsafe impl DmaTxBuffer for DmaRxTxBuf { } } - fn into_view(self) -> BufView { + fn into_view(self) -> Self::View { BufView(self) } @@ -1067,11 +1079,11 @@ unsafe impl DmaTxBuffer for DmaRxTxBuf { } } -unsafe impl DmaRxBuffer for DmaRxTxBuf { - type View = BufView; - type Final = DmaRxTxBuf; +unsafe impl DmaRxBuffer for DmaRxTxBuf { + type View = BufView>; + type Final = DmaRxTxBuf; - fn prepare(&mut self) -> Preparation { + fn prepare(&mut self) -> Preparation { for desc in self.rx_descriptors.linked_iter_mut() { desc.reset_for_rx(); } @@ -1102,7 +1114,7 @@ unsafe impl DmaRxBuffer for DmaRxTxBuf { } } - fn into_view(self) -> BufView { + fn into_view(self) -> Self::View { BufView(self) } @@ -1151,17 +1163,20 @@ unsafe impl DmaRxBuffer for DmaRxTxBuf { /// /// See [DmaRxStreamBufView] for APIs available whilst a transfer is in /// progress. -pub struct DmaRxStreamBuf { - descriptors: &'static mut [DmaDescriptor], +pub struct DmaRxStreamBuf +where + F: DescriptorFlagFields + 'static, +{ + descriptors: &'static mut [DmaDescriptor], buffer: &'static mut [u8], burst: BurstConfig, } -impl DmaRxStreamBuf { +impl DmaRxStreamBuf { /// Creates a new [DmaRxStreamBuf] evenly distributing the buffer between /// the provided descriptors. pub fn new( - descriptors: &'static mut [DmaDescriptor], + descriptors: &'static mut [DmaDescriptor], buffer: &'static mut [u8], ) -> Result { if !is_slice_in_dram(descriptors) { @@ -1211,16 +1226,16 @@ impl DmaRxStreamBuf { } /// Consume the buf, returning the descriptors and buffer. - pub fn split(self) -> (&'static mut [DmaDescriptor], &'static mut [u8]) { + pub fn split(self) -> (&'static mut [DmaDescriptor], &'static mut [u8]) { (self.descriptors, self.buffer) } } -unsafe impl DmaRxBuffer for DmaRxStreamBuf { - type View = DmaRxStreamBufView; - type Final = DmaRxStreamBuf; +unsafe impl DmaRxBuffer for DmaRxStreamBuf { + type View = DmaRxStreamBufView; + type Final = DmaRxStreamBuf; - fn prepare(&mut self) -> Preparation { + fn prepare(&mut self) -> Preparation { // Link up all the descriptors (but not in a circle). let mut next = null_mut(); for desc in self.descriptors.iter_mut().rev() { @@ -1245,7 +1260,7 @@ unsafe impl DmaRxBuffer for DmaRxStreamBuf { } } - fn into_view(self) -> DmaRxStreamBufView { + fn into_view(self) -> Self::View { DmaRxStreamBufView { buf: self, descriptor_idx: 0, @@ -1259,13 +1274,16 @@ unsafe impl DmaRxBuffer for DmaRxStreamBuf { } /// A view into a [DmaRxStreamBuf] -pub struct DmaRxStreamBufView { - buf: DmaRxStreamBuf, +pub struct DmaRxStreamBufView +where + F: DescriptorFlagFields + 'static, +{ + buf: DmaRxStreamBuf, descriptor_idx: usize, descriptor_offset: usize, } -impl DmaRxStreamBufView { +impl DmaRxStreamBufView { /// Returns the number of bytes that are available to read from the buf. pub fn available_bytes(&self) -> usize { let (tail, head) = self.buf.descriptors.split_at(self.descriptor_idx); @@ -1352,7 +1370,7 @@ impl DmaRxStreamBufView { // Reset the descriptor for reuse. desc.set_owner(Owner::Dma); desc.set_suc_eof(false); - desc.set_length(0); + desc.set_len(0); // Before connecting this descriptor to the end of the list, the next descriptor // must be disconnected from this one to prevent the DMA from @@ -1455,7 +1473,7 @@ unsafe impl DmaTxBuffer for EmptyBuf { } } - fn into_view(self) -> EmptyBuf { + fn into_view(self) -> Self { self } @@ -1502,17 +1520,20 @@ unsafe impl DmaRxBuffer for EmptyBuf { /// than this, the DMA channel will spend more time reading the descriptor than /// it does reading the buffer, which may leave it unable to keep up with the /// bandwidth requirements of some peripherals at high frequencies. -pub struct DmaLoopBuf { - descriptor: &'static mut DmaDescriptor, +pub struct DmaLoopBuf +where + F: DescriptorFlagFields + 'static, +{ + descriptor: &'static mut DmaDescriptor, buffer: &'static mut [u8], } -impl DmaLoopBuf { +impl DmaLoopBuf { /// Create a new [DmaLoopBuf]. pub fn new( - descriptor: &'static mut DmaDescriptor, + descriptor: &'static mut DmaDescriptor, buffer: &'static mut [u8], - ) -> Result { + ) -> Result { if !is_slice_in_dram(buffer) { return Err(DmaBufError::UnsupportedMemoryRegion); } @@ -1527,7 +1548,7 @@ impl DmaLoopBuf { descriptor.set_owner(Owner::Dma); // Doesn't matter descriptor.set_suc_eof(false); - descriptor.set_length(buffer.len()); + descriptor.set_len(buffer.len()); descriptor.set_size(buffer.len()); descriptor.buffer = buffer.as_mut_ptr(); descriptor.next = descriptor; @@ -1536,16 +1557,16 @@ impl DmaLoopBuf { } /// Consume the buf, returning the descriptor and buffer. - pub fn split(self) -> (&'static mut DmaDescriptor, &'static mut [u8]) { + pub fn split(self) -> (&'static mut DmaDescriptor, &'static mut [u8]) { (self.descriptor, self.buffer) } } -unsafe impl DmaTxBuffer for DmaLoopBuf { - type View = DmaLoopBuf; - type Final = DmaLoopBuf; +unsafe impl DmaTxBuffer for DmaLoopBuf { + type View = Self; + type Final = Self; - fn prepare(&mut self) -> Preparation { + fn prepare(&mut self) -> Preparation { Preparation { start: self.descriptor, #[cfg(psram_dma)] @@ -1569,7 +1590,7 @@ unsafe impl DmaTxBuffer for DmaLoopBuf { } } -impl Deref for DmaLoopBuf { +impl Deref for DmaLoopBuf { type Target = [u8]; fn deref(&self) -> &Self::Target { @@ -1577,7 +1598,7 @@ impl Deref for DmaLoopBuf { } } -impl DerefMut for DmaLoopBuf { +impl DerefMut for DmaLoopBuf { fn deref_mut(&mut self) -> &mut Self::Target { self.buffer } diff --git a/esp-hal/src/dma/flags.rs b/esp-hal/src/dma/flags.rs new file mode 100644 index 00000000000..270bac3ec8b --- /dev/null +++ b/esp-hal/src/dma/flags.rs @@ -0,0 +1,74 @@ +use super::Owner; + +/// Represents operations to get bitfields of DMA descriptor flags. +pub trait DescriptorFlagFields { + /// Creates an empty descriptor flag. + fn empty() -> Self; + + /// Gets the specified size of the descriptor buffer. + fn size(&self) -> usize; + + /// Sets the specified size of the descriptor buffer. + fn set_size(&mut self, size: usize); + + /// Returns the specified number of valid bytes in the buffer that this descriptor points to. + /// + /// This field in a transmit descriptor is written by software and indicates how many bytes can + /// be read from the buffer. + /// + /// This field in a receive descriptor is written by hardware automatically and indicates how + /// many valid bytes have been stored into the buffer. + fn len(&self) -> usize; + + /// Sets the specified number of valid bytes in the buffer that this descriptor points to. + /// + /// This field in a transmit descriptor is written by software and indicates how many bytes can + /// be read from the buffer. + /// + /// This field in a receive descriptor is written by hardware automatically and indicates how + /// many valid bytes have been stored into the buffer. + fn set_len(&mut self, length: usize); + + /// Returns whether the flags describe an empty descriptor. + /// + /// # Note + /// + /// Returns true when the descriptor buffer contains no valid bytes. + fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Returns the `SUC_EOF` bit. + /// + /// For receive descriptors, software needs to clear this bit to 0, and hardware will set it to + /// 1 after receiving data containing the EOF flag. + /// For transmit descriptors, software needs to set this bit to 1 as needed. + /// If software configures this bit to 1 in a descriptor, the DMA will include the EOF flag in + /// the data sent to the corresponding peripheral, indicating to the peripheral that this + /// data segment marks the end of one transfer phase. + fn suc_eof(&self) -> bool; + + /// Sets the `SUC_EOF` bit. + /// + /// For receive descriptors, software needs to clear this bit to 0, and hardware will set it to + /// 1 after receiving data containing the EOF flag. + /// For transmit descriptors, software needs to set this bit to 1 as needed. + /// If software configures this bit to 1 in a descriptor, the DMA will include the EOF flag in + /// the data sent to the corresponding peripheral, indicating to the peripheral that this + /// data segment marks the end of one transfer phase. + fn set_suc_eof(&mut self, suc_eof: bool); + + /// Returns the owner. + /// + /// Specifies who is allowed to access the buffer that this descriptor points to. + /// + /// See [Owner] for details. + fn owner(&self) -> Owner; + + /// Sets the owner. + /// + /// Specifies who is allowed to access the buffer that this descriptor points to. + /// + /// See [Owner] for details. + fn set_owner(&mut self, owner: Owner); +} diff --git a/esp-hal/src/dma/mod.rs b/esp-hal/src/dma/mod.rs index cd0308249dd..90e65c682e7 100644 --- a/esp-hal/src/dma/mod.rs +++ b/esp-hal/src/dma/mod.rs @@ -220,44 +220,76 @@ where bitfield::bitfield! { /// DMA descriptor flags. + /// + /// See [DescriptorFlagFields] for field details. #[derive(Clone, Copy, PartialEq, Eq)] pub struct DmaDescriptorFlags(u32); u16; - /// Specifies the size of the buffer that this descriptor points to. - pub size, set_size: 11, 0; + _size, _set_size: 11, 0; + _length, _set_length: 23, 12; + _suc_eof, _set_suc_eof: 30; + _owner, _set_owner: 31; +} - /// Specifies the number of valid bytes in the buffer that this descriptor points to. - /// - /// This field in a transmit descriptor is written by software and indicates how many bytes can - /// be read from the buffer. - /// - /// This field in a receive descriptor is written by hardware automatically and indicates how - /// many valid bytes have been stored into the buffer. - pub length, set_length: 23, 12; +impl DmaDescriptorFlags { + /// Returns the length of valid bytes in the descriptor buffer. + pub fn length(&self) -> usize { + self.len() + } + + /// Sets the length of valid bytes in the descriptor buffer. + pub fn set_length(&mut self, length: usize) { + self.set_len(length); + } +} + +impl DescriptorFlagFields for DmaDescriptorFlags { + fn empty() -> Self { + Self(0) + } + + fn size(&self) -> usize { + self._size() as usize + } + + fn set_size(&mut self, size: usize) { + self._set_size(size as u16); + } + + fn len(&self) -> usize { + self._length() as usize + } + + fn set_len(&mut self, length: usize) { + self._set_length(length as u16); + } + + fn suc_eof(&self) -> bool { + self._suc_eof() + } + + fn set_suc_eof(&mut self, suc_eof: bool) { + self._set_suc_eof(suc_eof); + } - /// For receive descriptors, software needs to clear this bit to 0, and hardware will set it to 1 after receiving - /// data containing the EOF flag. - /// For transmit descriptors, software needs to set this bit to 1 as needed. - /// If software configures this bit to 1 in a descriptor, the DMA will include the EOF flag in the data sent to - /// the corresponding peripheral, indicating to the peripheral that this data segment marks the end of one - /// transfer phase. - pub suc_eof, set_suc_eof: 30; + fn owner(&self) -> Owner { + self._owner().into() + } - /// Specifies who is allowed to access the buffer that this descriptor points to. - /// - 0: CPU can access the buffer; - /// - 1: The GDMA controller can access the buffer. - pub owner, set_owner: 31; + fn set_owner(&mut self, owner: Owner) { + self._set_owner(owner.into()); + } } impl Debug for DmaDescriptorFlags { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("DmaDescriptorFlags") .field("size", &self.size()) - .field("length", &self.length()) + .field("length", &self.len()) .field("suc_eof", &self.suc_eof()) - .field("owner", &(if self.owner() { "DMA" } else { "CPU" })) + .field("owner", &self.owner().to_str()) .finish() } } @@ -269,20 +301,26 @@ impl defmt::Format for DmaDescriptorFlags { fmt, "DmaDescriptorFlags {{ size: {}, length: {}, suc_eof: {}, owner: {} }}", self.size(), - self.length(), + self.len(), self.suc_eof(), - if self.owner() { "DMA" } else { "CPU" } + self.owner().to_str(), ); } } +///// Convenience alias for the DMA descriptor used with the general DMA controller. +// pub type DmaDescriptor = DmaDescriptor; + /// A DMA transfer descriptor. #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(C)] -pub struct DmaDescriptor { +pub struct DmaDescriptor +where + F: DescriptorFlagFields, +{ /// Descriptor flags. - pub flags: DmaDescriptorFlags, + pub flags: F, /// Address of the buffer. pub buffer: *mut u8, @@ -290,16 +328,14 @@ pub struct DmaDescriptor { /// Address of the next descriptor. /// If the current descriptor is the last one, this value is 0. /// This field can only point to internal RAM. - pub next: *mut DmaDescriptor, + pub next: *mut Self, } -impl DmaDescriptor { - /// An empty DMA descriptor used to initialize the descriptor list. - pub const EMPTY: Self = Self { - flags: DmaDescriptorFlags(0), - buffer: core::ptr::null_mut(), - next: core::ptr::null_mut(), - }; +impl DmaDescriptor { + const _SIZE_CHECK: () = core::assert!( + core::mem::size_of::() == core::mem::size_of::(), + "descriptor flags must be the same size as `u32`" + ); /// Resets the descriptor for a new receive transfer. pub fn reset_for_rx(&mut self) { @@ -312,7 +348,12 @@ impl DmaDescriptor { // Clear this to allow hardware to set it when it's // done receiving data for this descriptor. - self.set_length(0); + self.set_len(0); + } + + /// Sets the length of valid bytes in the descriptor buffer. + pub fn set_length(&mut self, length: usize) { + self.set_len(length); } /// Resets the descriptor for a new transmit transfer. See @@ -326,57 +367,66 @@ impl DmaDescriptor { // hardware should trigger an interrupt request. self.set_suc_eof(set_eof); } +} - /// Set the size of the buffer. See [DmaDescriptorFlags::size]. - pub fn set_size(&mut self, len: usize) { - self.flags.set_size(len as u16) +impl DescriptorFlagFields for DmaDescriptor { + fn empty() -> Self { + Self { + flags: F::empty(), + buffer: core::ptr::null_mut(), + next: core::ptr::null_mut(), + } } - /// Set the length of the descriptor. See [DmaDescriptorFlags::length]. - pub fn set_length(&mut self, len: usize) { - self.flags.set_length(len as u16) + fn size(&self) -> usize { + self.flags.size() } - /// Returns the size of the buffer. See [DmaDescriptorFlags::size]. - pub fn size(&self) -> usize { - self.flags.size() as usize + fn set_size(&mut self, size: usize) { + self.flags.set_size(size); } - /// Returns the length of the descriptor. See [DmaDescriptorFlags::length]. - #[allow(clippy::len_without_is_empty)] - pub fn len(&self) -> usize { - self.flags.length() as usize + fn len(&self) -> usize { + self.flags.len() } - /// Set the suc_eof bit. See [DmaDescriptorFlags::suc_eof]. - pub fn set_suc_eof(&mut self, suc_eof: bool) { - self.flags.set_suc_eof(suc_eof) + fn set_len(&mut self, length: usize) { + self.flags.set_len(length); } - /// Set the owner. See [DmaDescriptorFlags::owner]. - pub fn set_owner(&mut self, owner: Owner) { - let owner = match owner { - Owner::Cpu => false, - Owner::Dma => true, - }; - self.flags.set_owner(owner) + fn suc_eof(&self) -> bool { + self.flags.suc_eof() } - /// Returns the owner. See [DmaDescriptorFlags::owner]. - pub fn owner(&self) -> Owner { - match self.flags.owner() { - false => Owner::Cpu, - true => Owner::Dma, - } + fn set_suc_eof(&mut self, suc_eof: bool) { + self.flags.set_suc_eof(suc_eof); + } + + fn owner(&self) -> Owner { + self.flags.owner() + } + + fn set_owner(&mut self, owner: Owner) { + self.flags.set_owner(owner); } } +impl DmaDescriptor { + /// An empty DMA descriptor used to initialize the descriptor list. + pub const EMPTY: Self = Self { + flags: DmaDescriptorFlags(0), + buffer: core::ptr::null_mut(), + next: core::ptr::null_mut(), + }; +} + // The pointers in the descriptor can be Sent. // Marking this Send also allows DmaBuffer implementations to automatically be // Send (where the compiler sees fit). unsafe impl Send for DmaDescriptor {} mod buffers; +mod flags; #[cfg(gdma)] mod gdma; #[cfg(any(gdma, esp32s2))] @@ -384,6 +434,8 @@ mod m2m; #[cfg(pdma)] mod pdma; +pub use flags::DescriptorFlagFields; + /// Kinds of interrupt to listen to. #[derive(Debug, EnumSetType)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -923,7 +975,10 @@ pub enum DmaPeripheral { } /// The owner bit of a DMA descriptor. -#[derive(PartialEq, PartialOrd)] +/// +/// - 0: CPU can access the buffer; +/// - 1: The DMA controller can access the buffer. +#[derive(Clone, Copy, PartialEq, PartialOrd)] pub enum Owner { /// Owned by CPU Cpu = 0, @@ -931,6 +986,27 @@ pub enum Owner { Dma = 1, } +impl Owner { + /// Creates a new [Owner]. + pub const fn new() -> Self { + Self::Cpu + } + + /// Converts the [Owner] into a string. + pub const fn to_str(self) -> &'static str { + match self { + Self::Cpu => "CPU", + Self::Dma => "DMA", + } + } +} + +impl From for &'static str { + fn from(val: Owner) -> Self { + val.to_str() + } +} + impl From for Owner { fn from(value: u32) -> Self { match value { @@ -940,6 +1016,30 @@ impl From for Owner { } } +impl From for Owner { + fn from(value: bool) -> Self { + match value { + false => Self::Cpu, + true => Self::Dma, + } + } +} + +impl From for bool { + fn from(value: Owner) -> Self { + match value { + Owner::Cpu => false, + Owner::Dma => true, + } + } +} + +impl Default for Owner { + fn default() -> Self { + Self::new() + } +} + #[doc(hidden)] #[instability::unstable] pub trait DmaEligible { @@ -1011,7 +1111,7 @@ impl DescriptorChain { // In non-circular mode, we only set `suc_eof` for the last descriptor to signal // the end of the transfer. desc.reset_for_tx(desc.next.is_null() || is_circular); - desc.set_length(chunk_size); // align to 32 bits? + desc.set_len(chunk_size); // align to 32 bits? }) } @@ -1073,21 +1173,25 @@ pub const fn descriptor_count(buffer_size: usize, chunk_size: usize, is_circular buffer_size.div_ceil(chunk_size) } +/// Represents a container for a set of DMA descriptors. #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -struct DescriptorSet<'a> { - descriptors: &'a mut [DmaDescriptor], +pub(crate) struct DescriptorSet<'a, F = DmaDescriptorFlags> +where + F: DescriptorFlagFields, +{ + descriptors: &'a mut [DmaDescriptor], } -impl<'a> DescriptorSet<'a> { +impl<'a, F: DescriptorFlagFields + Clone> DescriptorSet<'a, F> { /// Creates a new `DescriptorSet` from a slice of descriptors and associates /// them with the given buffer. - fn new(descriptors: &'a mut [DmaDescriptor]) -> Result { + fn new(descriptors: &'a mut [DmaDescriptor]) -> Result { if !is_slice_in_dram(descriptors) { return Err(DmaBufError::UnsupportedMemoryRegion); } - descriptors.fill(DmaDescriptor::EMPTY); + descriptors.fill(DmaDescriptor::empty()); Ok(unsafe { Self::new_unchecked(descriptors) }) } @@ -1099,22 +1203,22 @@ impl<'a> DescriptorSet<'a> { /// /// The caller must ensure that the descriptors are located in a supported /// memory region. - unsafe fn new_unchecked(descriptors: &'a mut [DmaDescriptor]) -> Self { + unsafe fn new_unchecked(descriptors: &'a mut [DmaDescriptor]) -> Self { Self { descriptors } } /// Consumes the `DescriptorSet` and returns the inner slice of descriptors. - fn into_inner(self) -> &'a mut [DmaDescriptor] { + fn into_inner(self) -> &'a mut [DmaDescriptor] { self.descriptors } /// Returns a pointer to the first descriptor in the chain. - fn head(&mut self) -> *mut DmaDescriptor { + fn head(&mut self) -> *mut DmaDescriptor { self.descriptors.as_mut_ptr() } /// Returns an iterator over the linked descriptors. - fn linked_iter(&self) -> impl Iterator { + fn linked_iter(&self) -> impl Iterator> { let mut was_last = false; self.descriptors.iter().take_while(move |d| { if was_last { @@ -1127,7 +1231,7 @@ impl<'a> DescriptorSet<'a> { } /// Returns an iterator over the linked descriptors. - fn linked_iter_mut(&mut self) -> impl Iterator + use<'_> { + fn linked_iter_mut(&mut self) -> impl Iterator> + use<'_, F> { let mut was_last = false; self.descriptors.iter_mut().take_while(move |d| { if was_last { @@ -1159,7 +1263,7 @@ impl<'a> DescriptorSet<'a> { &mut self, len: usize, chunk_size: usize, - prepare: fn(&mut DmaDescriptor, usize), + prepare: fn(&mut DmaDescriptor, usize), ) -> Result<(), DmaBufError> { Self::set_up_descriptors(self.descriptors, len, chunk_size, false, prepare) } @@ -1178,17 +1282,17 @@ impl<'a> DescriptorSet<'a> { /// See [`Self::set_up_descriptors`] for more details. fn set_tx_length(&mut self, len: usize, chunk_size: usize) -> Result<(), DmaBufError> { self.set_length(len, chunk_size, |desc, chunk_size| { - desc.set_length(chunk_size); + desc.set_len(chunk_size); }) } /// Returns a slice of descriptors that can cover a buffer of length `len`. fn descriptors_for_buffer_len( - descriptors: &mut [DmaDescriptor], + descriptors: &mut [DmaDescriptor], len: usize, chunk_size: usize, is_circular: bool, - ) -> Result<&mut [DmaDescriptor], DmaBufError> { + ) -> Result<&mut [DmaDescriptor], DmaBufError> { // First, pick enough descriptors to cover the buffer. let required_descriptors = descriptor_count(len, chunk_size, is_circular); if descriptors.len() < required_descriptors { @@ -1205,11 +1309,11 @@ impl<'a> DescriptorSet<'a> { /// The actual descriptor setup is done in a callback, because different /// transfer directions require different descriptor setup. fn set_up_descriptors( - descriptors: &mut [DmaDescriptor], + descriptors: &mut [DmaDescriptor], len: usize, chunk_size: usize, is_circular: bool, - prepare: impl Fn(&mut DmaDescriptor, usize), + prepare: impl Fn(&mut DmaDescriptor, usize), ) -> Result<(), DmaBufError> { let descriptors = Self::descriptors_for_buffer_len(descriptors, len, chunk_size, is_circular)?; @@ -1250,7 +1354,7 @@ impl<'a> DescriptorSet<'a> { /// repeatedly. fn set_up_buffer_ptrs( buffer: &mut [u8], - descriptors: &mut [DmaDescriptor], + descriptors: &mut [DmaDescriptor], chunk_size: usize, is_circular: bool, ) -> Result<(), DmaBufError> { @@ -1537,7 +1641,7 @@ impl RxCircularState { descr.set_owner(Owner::Dma); descr.set_suc_eof(false); - descr.set_length(0); + descr.set_len(0); descr_ptr.write_volatile(descr); remaining_buffer = &mut remaining_buffer[count..]; @@ -1747,19 +1851,22 @@ fn create_guard(_ch: &impl RegisterAccess) -> PeripheralGuard { // DMA receive channel #[non_exhaustive] #[doc(hidden)] -pub struct ChannelRx +pub struct ChannelRx where Dm: DriverMode, CH: DmaRxChannel, + F: DescriptorFlagFields, { pub(crate) rx_impl: CH, pub(crate) _phantom: PhantomData, pub(crate) _guard: PeripheralGuard, + pub(crate) _flag: PhantomData, } -impl ChannelRx +impl ChannelRx where CH: DmaRxChannel, + F: DescriptorFlagFields, { /// Creates a new RX channel half. pub fn new(rx_impl: CH) -> Self { @@ -1781,11 +1888,12 @@ where rx_impl, _phantom: PhantomData, _guard, + _flag: PhantomData, } } /// Converts a blocking channel to an async channel. - pub(crate) fn into_async(mut self) -> ChannelRx { + pub(crate) fn into_async(mut self) -> ChannelRx { if let Some(handler) = self.rx_impl.async_handler() { self.set_interrupt_handler(handler); } @@ -1794,6 +1902,7 @@ where rx_impl: self.rx_impl, _phantom: PhantomData, _guard: self._guard, + _flag: PhantomData, } } @@ -1811,12 +1920,13 @@ where } } -impl ChannelRx +impl ChannelRx where CH: DmaRxChannel, + F: DescriptorFlagFields, { /// Converts an async channel into a blocking channel. - pub(crate) fn into_blocking(self) -> ChannelRx { + pub(crate) fn into_blocking(self) -> ChannelRx { if let Some(interrupt) = self.rx_impl.peripheral_interrupt() { crate::interrupt::disable(Cpu::current(), interrupt); } @@ -1825,14 +1935,16 @@ where rx_impl: self.rx_impl, _phantom: PhantomData, _guard: self._guard, + _flag: PhantomData, } } } -impl ChannelRx +impl ChannelRx where Dm: DriverMode, CH: DmaRxChannel, + F: DescriptorFlagFields, { /// Configure the channel. #[cfg(gdma)] @@ -1873,18 +1985,20 @@ where } } -impl crate::private::Sealed for ChannelRx +impl crate::private::Sealed for ChannelRx where Dm: DriverMode, CH: DmaRxChannel, + F: DescriptorFlagFields, { } #[allow(unused)] -impl ChannelRx +impl ChannelRx where Dm: DriverMode, CH: DmaRxChannel, + F: DescriptorFlagFields, { // TODO: used by I2S, which should be rewritten to use the Preparation-based // API. @@ -2016,19 +2130,22 @@ where /// DMA transmit channel #[doc(hidden)] -pub struct ChannelTx +pub struct ChannelTx where Dm: DriverMode, CH: DmaTxChannel, + F: DescriptorFlagFields, { pub(crate) tx_impl: CH, pub(crate) _phantom: PhantomData, pub(crate) _guard: PeripheralGuard, + pub(crate) _flag: PhantomData, } -impl ChannelTx +impl ChannelTx where CH: DmaTxChannel, + F: DescriptorFlagFields, { /// Creates a new TX channel half. pub fn new(tx_impl: CH) -> Self { @@ -2044,19 +2161,23 @@ where tx_impl, _phantom: PhantomData, _guard, + _flag: PhantomData, } } /// Converts a blocking channel to an async channel. - pub(crate) fn into_async(mut self) -> ChannelTx { + pub(crate) fn into_async(mut self) -> ChannelTx { if let Some(handler) = self.tx_impl.async_handler() { self.set_interrupt_handler(handler); } + self.tx_impl.set_async(true); - ChannelTx { + + ChannelTx:: { tx_impl: self.tx_impl, _phantom: PhantomData, _guard: self._guard, + _flag: PhantomData, } } @@ -2074,28 +2195,33 @@ where } } -impl ChannelTx +impl ChannelTx where CH: DmaTxChannel, + F: DescriptorFlagFields, { /// Converts an async channel into a blocking channel. - pub(crate) fn into_blocking(self) -> ChannelTx { + pub(crate) fn into_blocking(self) -> ChannelTx { if let Some(interrupt) = self.tx_impl.peripheral_interrupt() { crate::interrupt::disable(Cpu::current(), interrupt); } + self.tx_impl.set_async(false); - ChannelTx { + + ChannelTx:: { tx_impl: self.tx_impl, _phantom: PhantomData, _guard: self._guard, + _flag: PhantomData, } } } -impl ChannelTx +impl ChannelTx where Dm: DriverMode, CH: DmaTxChannel, + F: DescriptorFlagFields, { /// Configure the channel priority. #[cfg(gdma)] @@ -2138,18 +2264,20 @@ where } } -impl crate::private::Sealed for ChannelTx +impl crate::private::Sealed for ChannelTx where Dm: DriverMode, CH: DmaTxChannel, + F: DescriptorFlagFields, { } #[allow(unused)] -impl ChannelTx +impl ChannelTx where Dm: DriverMode, CH: DmaTxChannel, + F: DescriptorFlagFields, { // TODO: used by I2S, which should be rewritten to use the Preparation-based // API. diff --git a/esp-hal/src/lib.rs b/esp-hal/src/lib.rs index fdeaa980d10..46a0e392ebe 100644 --- a/esp-hal/src/lib.rs +++ b/esp-hal/src/lib.rs @@ -243,6 +243,8 @@ pub mod i2c; pub mod peripherals; #[cfg(all(feature = "unstable", any(soc_has_hmac, soc_has_sha)))] mod reg_access; +#[cfg(all(feature = "unstable", sd_slave))] +pub mod sdio; #[cfg(any(soc_has_spi0, soc_has_spi1, soc_has_spi2, soc_has_spi3))] pub mod spi; pub mod system; diff --git a/esp-hal/src/sdio.rs b/esp-hal/src/sdio.rs new file mode 100644 index 00000000000..af3ddf55cbf --- /dev/null +++ b/esp-hal/src/sdio.rs @@ -0,0 +1,570 @@ +//! # Secure Digital I/O - Slave Mode +//! +//! ## Overiew +//! +//! The peripheral can be used to transfer data over the SDIO bus in `Slave` +//! mode. + +#![allow(clippy::identity_op)] + +use embedded_hal_sdmmc::{ + CardMode, + CardType, + Common, + Device, + FifoStatus, + Reset, + SdBusWidth as BusWidth, + command::Command, + response::Response, + tuning::{TuningMode, TuningWidth}, +}; + +use crate::gpio::Flex; +use crate::pac; + +pub mod command; +mod config; +mod direction; +pub mod dma; +mod hinf; +mod interrupt; +mod pins; +pub mod response; +mod slc; +mod slchost; +mod state; +mod timing; + +pub use config::Config; +pub use direction::Direction; +pub use hinf::{AnyHinf, HinfInfo, HinfInstance}; +pub use interrupt::{DeviceInterrupt, HostInterrupt}; +pub use pins::Pins; +pub use slc::{AnySlc, SlcInfo, SlcInstance}; +pub use slchost::{AnySlchost, SlchostInfo, SlchostInstance}; +pub use state::State; +pub use timing::Timing; + +/// Represents the transmission modes for the SDIO peripheral. +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum Mode { + /// SPI transmission mode. + /// + /// Uses the following I/O signals: + /// + /// - `SCLK` + /// - `MOSI` + /// - `MISO` + /// - `IRQ` + /// - `#CS` + Spi, + /// SD 1-bit transmission mode. + /// + /// Uses the following I/O signals: + /// + /// - `CLK` + /// - `CMD` + /// - `DAT0` + /// - `IRQ` + Sd1bit, + /// SD 4-bit transmission mode. + /// + /// Uses the following I/O signals: + /// + /// - `CLK` + /// - `CMD` + /// - `DAT0` + /// - `DAT1` + /// - `DAT2` + /// - `DAT3` + Sd4bit, +} + +/// Represents the SDIO 2.0 peripheral for the microcontroller. +#[derive(Debug)] +pub struct Sdio<'d> { + slc: AnySlc<'d>, + slchost: AnySlchost<'d>, + hinf: AnyHinf<'d>, + pins: Pins, + config: Config, + state: State, +} + +impl<'d> Sdio<'d> { + /// Creates a new [Sdio]. + /// + /// ## Example + /// + /// ```rust, no_run + #[doc = crate::before_snippet!()] + /// use esp_hal::sdio::{Config, Mode, Pins, Sdio}; + /// + /// let pins = Pins::new( + /// Mode::Sd4bit, + /// peripherals.GPIO19, // CLK/SCLK + /// peripherals.GPIO18, // CMD/MOSI + /// peripherals.GPIO20, // DAT0/MISO + /// peripherals.GPIO21, // DAT1/IRQ + /// peripherals.GPIO22, // DAT2 + /// peripherals.GPIO23, // DAT3/#CS + /// ); + /// + /// let config = Config::new(); + /// + /// let _sdio = Sdio::new( + /// peripherals.SLC, + /// peripherals.SLCHOST, + /// peripherals.HINF, + /// pins, + /// config, + /// ); + /// # Ok(()) + /// # } + /// ``` + pub fn new( + slc: impl SlcInstance + 'd, + slchost: impl SlchostInstance + 'd, + hinf: impl HinfInstance + 'd, + pins: Pins, + config: Config, + ) -> Self { + Self { + slc: slc.degrade(), + slchost: slchost.degrade(), + hinf: hinf.degrade(), + pins, + config, + state: State::new(), + } + } + + /// Gets a static reference to the SLC information. + pub fn slc(&self) -> &'static SlcInfo { + self.slc.info() + } + + /// Convenience function to get a reference to the SLC register block. + fn slc_block(&self) -> &'_ pac::slc::RegisterBlock { + unsafe { &*self.slc().register_block } + } + + /// Gets a static reference to the SLCHOST information. + pub fn slchost(&self) -> &'static SlchostInfo { + self.slchost.info() + } + + /// Convenience function to get a reference to the SLCHOST register block. + fn slchost_block(&self) -> &'_ pac::slchost::RegisterBlock { + unsafe { &*self.slchost().register_block } + } + + /// Gets a static reference to the HINF information. + pub fn hinf(&self) -> &'static HinfInfo { + self.hinf.info() + } + + /// Convenience function to get a reference to the HINF register block. + fn hinf_block(&self) -> &'_ pac::hinf::RegisterBlock { + unsafe { &*self.hinf().register_block } + } + + /// Gets a reference to the [Pins] information. + pub const fn pins(&self) -> &Pins { + &self.pins + } + + /// Gets a reference to the [Config] information. + pub const fn config(&self) -> &Config { + &self.config + } + + /// Gets the bus mode of the SDIO peripheral. + pub const fn bus_mode(&self) -> Mode { + self.pins.mode() + } + + /// Gets the bus width of the SDIO peripheral. + pub const fn bus_width(&self) -> BusWidth { + match self.bus_mode() { + Mode::Spi | Mode::Sd1bit => BusWidth::_1bit, + Mode::Sd4bit => BusWidth::_4bit, + } + } + + /// Gets the current [State] of the SDIO peripheral. + pub const fn state(&self) -> State { + self.state + } + + /// Transitions the SDIO peripheral to the requested [State]. + pub(crate) fn state_transition(&mut self, state: State) -> Result<(), Error> { + self.state + .valid_transition(state) + .map(|_| self.state = state) + } + + /// Performs final low-level HAL hardware initialization. + pub(crate) fn hardware_init(&mut self) -> Result<(), Error> { + self.pac_init()?; + self.pac_enable_hs()?; + self.pac_set_timing()?; + self.pac_dev_interrupt_enable(enumset::EnumSet::all()) + } + + /// Performs low-level initialization of the SDIO peripheral. + #[cfg(esp32)] + fn pac_init(&mut self) -> Result<(), Error> { + let slc = self.slc_block(); + + slc.conf0().modify(|_, w| { + w.slc0_rx_auto_wrback().set_bit(); + w.slc0_token_auto_clr().clear_bit(); + + w.slc0_rx_loop_test().clear_bit(); + w.slc0_tx_loop_test().clear_bit() + }); + + slc.conf1().modify(|_, w| { + w.slc0_rx_stitch_en().clear_bit(); + w.slc0_tx_stitch_en().clear_bit(); + + w.slc0_len_auto_clr().clear_bit() + }); + + slc.rx_dscr_conf() + .modify(|_, w| w.slc0_token_no_replace().set_bit()); + + Ok(()) + } + + /// Performs low-level initialization of the SDIO peripheral. + #[cfg(esp32c6)] + fn pac_init(&mut self) -> Result<(), Error> { + let slc = self.slc_block(); + + slc.slcconf0().modify(|_, w| { + w.sdio_slc0_rx_auto_wrback().set_bit(); + w.sdio_slc0_token_auto_clr().clear_bit(); + + w.sdio_slc0_rx_loop_test().clear_bit(); + w.sdio_slc0_tx_loop_test().clear_bit() + }); + + slc.slcconf1().modify(|_, w| { + w.sdio_slc0_rx_stitch_en().clear_bit(); + w.sdio_slc0_tx_stitch_en().clear_bit(); + + w.sdio_slc0_len_auto_clr().clear_bit() + }); + + slc.slc_rx_dscr_conf() + .modify(|_, w| w.sdio_slc0_token_no_replace().set_bit()); + + Ok(()) + } + + /// Sets the high-speed supported bit to be read by the host. + #[cfg(any(esp32, esp32c6))] + fn pac_enable_hs(&mut self) -> Result<(), Error> { + self.hinf_block() + .cfg_data1() + .modify(|_, w| w.highspeed_enable().variant(self.config.hs())); + + Ok(()) + } + + /// Sets the communication timing. + fn pac_set_timing(&mut self) -> Result<(), Error> { + match self.config.timing() { + Timing::PsendPsample => { + self.set_timing_registers(0x1f, 0x0, 0x1f, 0x0); + } + Timing::PsendNsample => { + self.set_timing_registers(0x1f, 0x0, 0x0, 0x1f); + } + Timing::NsendPsample => { + self.set_timing_registers(0x0, 0x1f, 0x1f, 0x0); + } + Timing::NsendNsample => { + self.set_timing_registers(0x0, 0x1f, 0x0, 0x1f); + } + } + + Ok(()) + } + + #[cfg(esp32)] + fn set_timing_registers(&self, sdio20: u8, sdio11: u8, pos_samp: u8, neg_samp: u8) { + self.slchost_block() + .host_slchost_conf() + .modify(|_, w| unsafe { + w.host_frc_sdio20().bits(sdio20); + w.host_frc_sdio11().bits(sdio11); + w.host_frc_pos_samp().bits(pos_samp); + w.host_frc_neg_samp().bits(neg_samp) + }); + } + + #[cfg(esp32c6)] + fn set_timing_registers(&self, sdio20: u8, sdio11: u8, pos_samp: u8, neg_samp: u8) { + self.slchost_block().conf().modify(|_, w| unsafe { + w.frc_sdio20().bits(sdio20); + w.frc_sdio11().bits(sdio11); + w.frc_pos_samp().bits(pos_samp); + w.frc_neg_samp().bits(neg_samp) + }); + } + + /// Sets which device interrupts to enable based on the provided mask. + #[cfg(esp32)] + fn pac_dev_interrupt_enable( + &self, + mask: enumset::EnumSet, + ) -> Result<(), Error> { + self.slc_block()._0int_ena().modify(|_, w| { + w.frhost_bit0_int_ena() + .variant(mask.contains(DeviceInterrupt::General0)); + w.frhost_bit1_int_ena() + .variant(mask.contains(DeviceInterrupt::General1)); + w.frhost_bit2_int_ena() + .variant(mask.contains(DeviceInterrupt::General2)); + w.frhost_bit3_int_ena() + .variant(mask.contains(DeviceInterrupt::General3)); + w.frhost_bit4_int_ena() + .variant(mask.contains(DeviceInterrupt::General4)); + w.frhost_bit5_int_ena() + .variant(mask.contains(DeviceInterrupt::General5)); + w.frhost_bit6_int_ena() + .variant(mask.contains(DeviceInterrupt::General6)); + w.frhost_bit7_int_ena() + .variant(mask.contains(DeviceInterrupt::General7)) + }); + + Ok(()) + } + + /// Sets which device interrupts to enable based on the provided mask. + #[cfg(esp32c6)] + fn pac_dev_interrupt_enable( + &self, + mask: enumset::EnumSet, + ) -> Result<(), Error> { + self.slc_block().slc0int_ena().modify(|_, w| { + w.sdio_slc_frhost_bit0_int_ena() + .variant(mask.contains(DeviceInterrupt::General0)); + w.sdio_slc_frhost_bit1_int_ena() + .variant(mask.contains(DeviceInterrupt::General1)); + w.sdio_slc_frhost_bit2_int_ena() + .variant(mask.contains(DeviceInterrupt::General2)); + w.sdio_slc_frhost_bit3_int_ena() + .variant(mask.contains(DeviceInterrupt::General3)); + w.sdio_slc_frhost_bit4_int_ena() + .variant(mask.contains(DeviceInterrupt::General4)); + w.sdio_slc_frhost_bit5_int_ena() + .variant(mask.contains(DeviceInterrupt::General5)); + w.sdio_slc_frhost_bit6_int_ena() + .variant(mask.contains(DeviceInterrupt::General6)); + w.sdio_slc_frhost_bit7_int_ena() + .variant(mask.contains(DeviceInterrupt::General7)) + }); + + Ok(()) + } + + /// Reads the raw command bytes from the wire. + pub fn read_command_raw(&mut self) -> Result<[u8; command::AnyCmd::LEN], Error> { + match self.pins.mode() { + Mode::Spi => { + let mut buf = [0u8; command::AnyCmd::LEN]; + + for b in buf.iter_mut() { + for i in 0..8 { + let shift = 7 - i; + *b |= self.pins.mosi().map(|p| (p.is_set_high() as u8) << shift)?; + } + } + + Ok(buf) + } + _ => Err(Error::Unimplemented), + } + } + + /// Writes the raw response bytes to the wire. + pub fn write_response_raw(&mut self, res: &[u8]) -> Result<(), Error> { + match self.pins.mode() { + Mode::Spi => { + let mut miso = self.pins.miso().map(Flex::new)?; + + for b in res.iter() { + for i in 0..8 { + let shift = 7 - i; + let level = ((b >> shift) & 0x1) != 0; + miso.set_level(level.into()); + } + } + + Ok(()) + } + _ => Err(Error::Unimplemented), + } + } +} + +/// Represents the error variants for SDIO peripherals. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum Error { + /// Indicates a general error occured. + General, + /// Indicates use of an illegal command. + IllegalCommand, + /// Indicates a CRC error from the previous command. + Crc, + /// The function and/or type is unimplemented. + Unimplemented, + /// Indicates an invalid state transition. + InvalidTransition { + /// Represents the current state. + from: State, + /// Represents the transition state. + to: State, + }, +} + +impl Error { + /// Creates a new [Error]. + pub const fn new() -> Self { + Self::Unimplemented + } + + /// Creates an general [Error]. + #[inline] + pub const fn general() -> Self { + Self::General + } + + /// Creates an illegal command [Error]. + #[inline] + pub const fn illegal_command() -> Self { + Self::IllegalCommand + } + + /// Creates an crc [Error]. + #[inline] + pub const fn crc() -> Self { + Self::Crc + } + + /// Creates an unimplemented [Error]. + #[inline] + pub const fn unimplemented() -> Self { + Self::Unimplemented + } + + /// Creates an invalid state transition [Error]. + #[inline] + pub const fn invalid_transition(from: State, to: State) -> Self { + Self::InvalidTransition { from, to } + } +} + +impl Default for Error { + fn default() -> Self { + Self::new() + } +} + +impl core::fmt::Display for Error { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::General => write!(f, "general"), + Self::IllegalCommand => write!(f, "illegal command"), + Self::Crc => write!(f, "CRC"), + Self::Unimplemented => write!(f, "unimplemented"), + Self::InvalidTransition { from, to } => { + write!(f, "invalid state transition, from: {from}, to: {to}") + } + } + } +} + +impl core::error::Error for Error {} + +impl Common for Sdio<'_> { + type Error = Error; + + fn card_type(&self) -> CardType { + CardType::Sd + } + + fn card_mode(&self) -> CardMode { + CardMode::Sdio + } + + fn setup_bus(&mut self) -> Result<(), Error> { + Err(Error::unimplemented()) + } + + fn init(&mut self) -> Result<(), Error> { + // This implementation is based on the `esp-idf` driver: + // + self.hardware_init()?; + + self.state_transition(State::Standby) + } + + fn set_sample_phase(&mut self, _sample_phase: u8) {} + + fn fifo_ready(&self, _fifo_status: FifoStatus) -> Result<(), Error> { + Err(Error::unimplemented()) + } + + fn wait_for_reset(&mut self, _reset: Reset, _timeout: u64) -> Result<(), Error> { + Err(Error::unimplemented()) + } + + fn wait_while_busy(&mut self, _timout_us: u64) -> Result<(), Error> { + Err(Error::unimplemented()) + } + + fn read_data(&mut self, _data: &mut [u8]) -> Result<(), Error> { + Err(Error::unimplemented()) + } + + fn write_data(&mut self, _data: &[u8]) -> Result<(), Error> { + Err(Error::unimplemented()) + } + + fn send_tuning(&mut self, _mode: TuningMode, _width: TuningWidth) -> Result<(), Error> { + Err(Error::unimplemented()) + } + + fn interrupt(&self) -> u32 { + 0 + } + + fn set_interrupt(&mut self, _int: u32) {} + + fn clear_all_interrupt(&mut self) {} + + fn response_interrupt(&self) -> u32 { + 0 + } + + fn set_response_interrupt(&mut self, _int: u32) {} + + fn clear_all_response_interrupt(&mut self) {} +} + +impl Device for Sdio<'_> { + fn read_command(&mut self) -> Result { + Err(Error::unimplemented()) + } + + fn write_response(&mut self, _response: &R) -> Result<(), Error> { + Err(Error::unimplemented()) + } +} diff --git a/esp-hal/src/sdio/command.rs b/esp-hal/src/sdio/command.rs new file mode 100644 index 00000000000..f48f7c66130 --- /dev/null +++ b/esp-hal/src/sdio/command.rs @@ -0,0 +1,31 @@ +//! SDIO command types. + +mod block_mode; +mod cmd52; +pub mod cmd53; +mod crc; +mod flag; +mod fn_number; +mod index; + +pub use block_mode::BlockMode; +pub use cmd52::Cmd52; +pub use cmd53::Cmd53; +pub use crc::Crc; +pub use flag::{RawFlag, RwFlag}; +pub use fn_number::FunctionNumber; +pub use index::CommandIndex; + +/// Represents any SDIO command structure. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum AnyCmd { + /// Represents the `IO_RW_DIRECT` command. + Cmd52(Cmd52), + /// Represents the `IO_RW_EXTENDED` command. + Cmd53(Cmd53), +} + +impl AnyCmd { + /// Represents the byte length of the command. + pub const LEN: usize = 6; +} diff --git a/esp-hal/src/sdio/command/block_mode.rs b/esp-hal/src/sdio/command/block_mode.rs new file mode 100644 index 00000000000..d44a9463207 --- /dev/null +++ b/esp-hal/src/sdio/command/block_mode.rs @@ -0,0 +1,76 @@ +/// Represents the block mode setting in a CMD53 command. +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum BlockMode { + /// Indicates a default bytes-mode transfer. + Bytes = 0, + /// Indicates a block-mode transfer. + Block = 1, +} + +impl BlockMode { + /// Creates a new [BlockMode]. + pub const fn new() -> Self { + Self::Bytes + } + + /// Converts a bool into a [BlockMode]. + #[inline] + pub const fn from_bool(val: bool) -> Self { + match val { + false => Self::Bytes, + true => Self::Block, + } + } + + /// Converts a [BlockMode] into a bool. + #[inline] + pub const fn into_bool(self) -> bool { + match self { + Self::Bytes => false, + Self::Block => true, + } + } + + /// Converts a [`u8`] into a [BlockMode]. + #[inline] + pub const fn from_u8(val: u8) -> Self { + Self::from_bool(val != 0) + } + + /// Converts a [BlockMode] into a [`u8`]. + #[inline] + pub const fn into_u8(self) -> u8 { + self.into_bool() as u8 + } +} + +impl Default for BlockMode { + fn default() -> Self { + Self::new() + } +} + +impl From for BlockMode { + fn from(val: bool) -> Self { + Self::from_bool(val) + } +} + +impl From for BlockMode { + fn from(val: u8) -> Self { + Self::from_u8(val) + } +} + +impl From for bool { + fn from(val: BlockMode) -> Self { + val.into_bool() + } +} + +impl From for u8 { + fn from(val: BlockMode) -> Self { + val.into_u8() + } +} diff --git a/esp-hal/src/sdio/command/cmd52.rs b/esp-hal/src/sdio/command/cmd52.rs new file mode 100644 index 00000000000..f041e573158 --- /dev/null +++ b/esp-hal/src/sdio/command/cmd52.rs @@ -0,0 +1,177 @@ +use bitfielder::bitfield; + +use super::{Crc, FunctionNumber, RawFlag, RwFlag}; +use crate::sdio::{Direction, Error}; + +bitfield! { + /// Represents the SDIO CMD52 `IO_RW_DIRECT` command. + /// + /// Can be used to access register info, and transfer data. + pub Cmd52(MSB0 [u8; 6]): u32 { + raw_s: 47; + raw_d: 46; + raw_cmd_index: 45, 40; + raw_rw_flag: 39; + raw_fn_number: 38, 36; + raw_raw_flag: 35; + raw_stuff1: 34; + raw_reg_addr: 33, 17; + raw_stuff0: 16; + raw_data: 15, 8; + raw_crc: 7, 1; + raw_e: 0; + } +} + +impl Cmd52 { + /// Represents the [Cmd52] command byte length. + pub const LEN: usize = 6; + /// Represents the [Cmd52] command default byte value. + pub const DEFAULT_ARG: [u8; 4] = [0x0; 4]; + /// Represents the [Cmd52] command index. + pub const COMMAND_INDEX: u8 = 53; + + /// Creates a new [Cmd52]. + pub const fn new() -> Self { + Self(Self::default_bytes()) + } + + #[inline] + const fn start() -> u8 { + 0x40 | Self::COMMAND_INDEX + } + + #[inline] + const fn default_bytes() -> [u8; Self::LEN] { + let [a0, a1, a2, a3] = Self::DEFAULT_ARG; + let raw = [Self::start(), a0, a1, a2, a3]; + let crc = Crc::calculate(&raw); + let [r0, r1, r2, r3, r4] = raw; + + [r0, r1, r2, r3, r4, (crc.into_u8() << 1) | 1] + } + + /// Gets the direction. + pub const fn direction(&self) -> Direction { + Direction::from_bool(self.raw_d()) + } + + /// Gets the command index. + pub const fn command_index(&self) -> u8 { + self.raw_cmd_index() as u8 + } + + /// Gets the function number. + pub const fn function_number(&self) -> Result { + FunctionNumber::try_from_u8(self.raw_fn_number() as u8) + } + + /// Sets the function number. + pub fn set_function_number(&mut self, number: FunctionNumber) { + self.set_raw_fn_number(number.into_u8().into()); + } + + /// Gets the read-write flag. + pub const fn read_write_flag(&self) -> RwFlag { + RwFlag::from_bool(self.raw_rw_flag()) + } + + /// Sets the read-write flag. + pub fn set_read_write_flag(&mut self, flag: RwFlag) { + self.set_raw_rw_flag(flag.into_bool()); + } + + /// Gets the read-write flag. + pub const fn read_after_write_flag(&self) -> RawFlag { + RawFlag::from_bool(self.raw_raw_flag()) + } + + /// Sets the read-write flag. + pub fn set_read_after_write_flag(&mut self, flag: RawFlag) { + self.set_raw_raw_flag(flag.into_bool()); + } + + /// Gets the register address. + pub const fn register_address(&self) -> u16 { + self.raw_reg_addr() as u16 + } + + /// Sets the register address. + pub fn set_register_address(&mut self, addr: u16) { + self.set_raw_reg_addr(addr.into()); + } + + /// Gets the data. + pub const fn data(&self) -> u8 { + self.raw_data() as u8 + } + + /// Sets the data. + pub fn set_data(&mut self, data: u8) { + self.set_raw_data(data.into()); + } + + /// Gets the CRC. + pub const fn crc(&self) -> Crc { + Crc::from_u8(self.raw_crc() as u8) + } + + /// Calculates and sets the CRC. + pub fn set_crc(&mut self) { + self.set_raw_crc(self.calculate_crc().into_u8().into()); + } + + /// Calculates the CRC. + pub const fn calculate_crc(&self) -> Crc { + let [b0, b1, b2, b3, b4, _] = self.0; + Crc::calculate(&[b0, b1, b2, b3, b4]) + } + + /// Verifies if the CRC matches the calculated value. + pub const fn verify_crc(&self) -> Result<(), Error> { + match self.calculate_crc().into_u8() == self.crc().into_u8() { + false => Err(Error::Crc), + true => Ok(()), + } + } + + /// Attempts to convert a byte slice into a [Cmd52]. + pub const fn try_from_bytes(val: &[u8]) -> Result { + match val.len() { + len if len < Self::LEN => Err(Error::General), + _ => match Self([val[0], val[1], val[2], val[3], val[4], val[5]]) { + cmd if cmd.command_index() != Self::COMMAND_INDEX => Err(Error::General), + cmd if cmd.raw_s() => Err(Error::General), + cmd if !cmd.raw_e() => Err(Error::General), + cmd if cmd.function_number().is_err() => Err(Error::General), + cmd if cmd.verify_crc().is_err() => Err(Error::Crc), + cmd => Ok(cmd), + }, + } + } + + /// Converts the [Cmd52] into a byte array. + pub const fn into_bytes(self) -> [u8; Self::LEN] { + self.0 + } +} + +impl Default for Cmd52 { + fn default() -> Self { + Self::new() + } +} + +impl TryFrom<&[u8]> for Cmd52 { + type Error = Error; + + fn try_from(val: &[u8]) -> Result { + Self::try_from_bytes(val) + } +} + +impl From for [u8; Cmd52::LEN] { + fn from(val: Cmd52) -> Self { + val.into_bytes() + } +} diff --git a/esp-hal/src/sdio/command/cmd53.rs b/esp-hal/src/sdio/command/cmd53.rs new file mode 100644 index 00000000000..6c3fe77b931 --- /dev/null +++ b/esp-hal/src/sdio/command/cmd53.rs @@ -0,0 +1,192 @@ +//! CMD53 `IO_RW_EXTENDED` SDIO command. + +use bitfielder::bitfield; + +use super::{BlockMode, Crc, FunctionNumber, RwFlag}; +use crate::sdio::{Direction, Error}; + +mod op_code; + +pub use op_code::OpCode; + +bitfield! { + /// Represents the SDIO CMD53 `IO_RW_EXTENDED` command. + /// + /// Can be used to transfer packets of an arbitrary length. + pub Cmd53(MSB0 [u8; 6]): u32 { + raw_s: 47; + raw_d: 46; + raw_cmd_index: 45, 40; + raw_rw_flag: 39; + raw_fn_number: 38, 36; + raw_block_mode: 35; + raw_op_code: 34; + raw_reg_addr: 33, 17; + raw_count: 16, 8; + raw_crc: 7, 1; + raw_e: 0; + } +} + +impl Cmd53 { + /// Represents the [Cmd53] command byte length. + pub const LEN: usize = 6; + /// Represents the [Cmd53] command default byte value. + pub const DEFAULT_ARG: [u8; 4] = [0x0; 4]; + /// Represents the [Cmd53] command index. + pub const COMMAND_INDEX: u8 = 53; + + /// Creates a new [Cmd53]. + pub const fn new() -> Self { + Self(Self::default_bytes()) + } + + #[inline] + const fn start() -> u8 { + 0x40 | Self::COMMAND_INDEX + } + + #[inline] + const fn default_bytes() -> [u8; Self::LEN] { + let [a0, a1, a2, a3] = Self::DEFAULT_ARG; + let raw = [Self::start(), a0, a1, a2, a3]; + let crc = Crc::calculate(&raw); + let [r0, r1, r2, r3, r4] = raw; + + [r0, r1, r2, r3, r4, (crc.into_u8() << 1) | 1] + } + + /// Gets the direction. + pub const fn direction(&self) -> Direction { + Direction::from_bool(self.raw_d()) + } + + /// Gets the command index. + pub const fn command_index(&self) -> u8 { + self.raw_cmd_index() as u8 + } + + /// Gets the function number. + pub const fn function_number(&self) -> Result { + FunctionNumber::try_from_u8(self.raw_fn_number() as u8) + } + + /// Sets the function number. + pub fn set_function_number(&mut self, number: FunctionNumber) { + self.set_raw_fn_number(number.into_u8().into()); + } + + /// Gets the block mode. + pub const fn block_mode(&self) -> BlockMode { + BlockMode::from_bool(self.raw_block_mode()) + } + + /// Sets the block mode. + pub fn set_block_mode(&mut self, mode: BlockMode) { + self.set_raw_block_mode(mode.into_bool()); + } + + /// Gets the read-write flag. + pub const fn read_write_flag(&self) -> RwFlag { + RwFlag::from_bool(self.raw_rw_flag()) + } + + /// Sets the read-write flag. + pub fn set_read_write_flag(&mut self, flag: RwFlag) { + self.set_raw_rw_flag(flag.into_bool()); + } + + /// Gets the OP code. + pub const fn op_code(&self) -> OpCode { + OpCode::from_bool(self.raw_op_code()) + } + + /// Sets the OP code. + pub fn set_op_code(&mut self, op_code: OpCode) { + self.set_raw_op_code(op_code.into_bool()); + } + + /// Gets the register address. + pub const fn register_address(&self) -> u32 { + self.raw_reg_addr() + } + + /// Sets the register address. + pub fn set_register_address(&mut self, addr: u32) { + self.set_raw_reg_addr(addr); + } + + /// Gets the block or bytes count depending on the [BlockMode] setting. + pub const fn count(&self) -> u16 { + self.raw_count() as u16 + } + + /// Sets the block or bytes count depending on the [BlockMode] setting. + pub fn set_count(&mut self, count: u16) { + self.set_raw_count(count.into()); + } + + /// Gets the CRC. + pub const fn crc(&self) -> Crc { + Crc::from_u8(self.raw_crc() as u8) + } + + /// Calculates and sets the CRC. + pub fn set_crc(&mut self) { + self.set_raw_crc(self.calculate_crc().into_u8().into()); + } + + /// Calculates the CRC. + pub const fn calculate_crc(&self) -> Crc { + let [b0, b1, b2, b3, b4, _] = self.0; + Crc::calculate(&[b0, b1, b2, b3, b4]) + } + + /// Verifies if the CRC matches the calculated value. + pub const fn verify_crc(&self) -> Result<(), Error> { + match self.calculate_crc().into_u8() == self.crc().into_u8() { + false => Err(Error::Crc), + true => Ok(()), + } + } + + /// Attempts to convert a byte slice into a [Cmd53]. + pub const fn try_from_bytes(val: &[u8]) -> Result { + match val.len() { + len if len < Self::LEN => Err(Error::General), + _ => match Self([val[0], val[1], val[2], val[3], val[4], val[5]]) { + cmd if cmd.command_index() != Self::COMMAND_INDEX => Err(Error::General), + cmd if cmd.raw_s() => Err(Error::General), + cmd if !cmd.raw_e() => Err(Error::General), + cmd if cmd.function_number().is_err() => Err(Error::General), + cmd if cmd.verify_crc().is_err() => Err(Error::Crc), + cmd => Ok(cmd), + }, + } + } + + /// Converts the [Cmd53] into a byte array. + pub const fn into_bytes(self) -> [u8; Self::LEN] { + self.0 + } +} + +impl Default for Cmd53 { + fn default() -> Self { + Self::new() + } +} + +impl TryFrom<&[u8]> for Cmd53 { + type Error = Error; + + fn try_from(val: &[u8]) -> Result { + Self::try_from_bytes(val) + } +} + +impl From for [u8; Cmd53::LEN] { + fn from(val: Cmd53) -> Self { + val.into_bytes() + } +} diff --git a/esp-hal/src/sdio/command/cmd53/op_code.rs b/esp-hal/src/sdio/command/cmd53/op_code.rs new file mode 100644 index 00000000000..927e6e56367 --- /dev/null +++ b/esp-hal/src/sdio/command/cmd53/op_code.rs @@ -0,0 +1,73 @@ +/// Represents the `OP Code` field of CMD53. +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum OpCode { + /// Multi-byte read-write to a fixed address. + FixedAddress = 0, + /// Multi-byte read-write to an incrementing address. + IncrementingAddress = 1, +} + +impl OpCode { + /// Creates a new [OpCode]. + pub const fn new() -> Self { + Self::FixedAddress + } + + /// Converts a bool into a [OpCode]. + #[inline] + pub const fn from_bool(val: bool) -> Self { + match val { + false => Self::FixedAddress, + true => Self::IncrementingAddress, + } + } + + /// Converts an [OpCode] into a bool. + #[inline] + pub const fn into_bool(self) -> bool { + matches!(self, Self::IncrementingAddress) + } + + /// Converts a [`u8`] into a [OpCode]. + #[inline] + pub const fn from_u8(val: u8) -> Self { + Self::from_bool(val != 0) + } + + /// Converts an [OpCode] into a [`u8`]. + #[inline] + pub const fn into_u8(self) -> u8 { + self.into_bool() as u8 + } +} + +impl Default for OpCode { + fn default() -> Self { + Self::new() + } +} + +impl From for OpCode { + fn from(val: bool) -> Self { + Self::from_bool(val) + } +} + +impl From for bool { + fn from(val: OpCode) -> Self { + val.into_bool() + } +} + +impl From for OpCode { + fn from(val: u8) -> Self { + Self::from_u8(val) + } +} + +impl From for u8 { + fn from(val: OpCode) -> Self { + val.into_u8() + } +} diff --git a/esp-hal/src/sdio/command/crc.rs b/esp-hal/src/sdio/command/crc.rs new file mode 100644 index 00000000000..a9247481b84 --- /dev/null +++ b/esp-hal/src/sdio/command/crc.rs @@ -0,0 +1,114 @@ +//! CRC7 checksum algorithm for command checksums. +//! +//! Authored-by: sdmmc-core developers +//! Originally licensed as GPLv3 +//! Permitted for distribution under APACHE or MIT by esp-rs + +const CRC7_POLY: u8 = 0b1000_1001; +const CRC7_MASK: u8 = 0x7f; + +/// Represents the 7-bit CRC used to protect SD memory card commands, responses, and data transfer +/// messages. +#[repr(C)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct Crc(u8); + +impl Crc { + /// Creates a new [Crc]. + pub const fn new() -> Self { + Self(0) + } + + /// Gets the inner representation of the [Crc] bits. + pub const fn into_u8(self) -> u8 { + self.0 + } + + /// Converts a [`u8`] into a [Crc]. + pub const fn from_u8(val: u8) -> Self { + Self(val & CRC7_MASK) + } + + /// Calculates the CRC7 value according the algorithm defined in the simplified physical + /// specification. See section "4.5 Cyclic Redundancy Code" for details. + /// + /// ```no_build,no_run + /// Generator Polynomial: G(x) = x^7 + x^3 + 1 + /// M(x) = (first bit) * x^n + (second bit) * x^n-1 + ... + (last bit) * x^0 + /// CRC[6..0] = Remainder[(M(x) * x^7) / G(x)] + /// ``` + /// + /// Implementation based on the lookup table algorithm from: [hazelnusse/crc7](https://github.com/hazelnusse/crc7). + pub const fn calculate(data: &[u8]) -> Self { + let mut crc = 0; + let mut i = 0; + let len = data.len(); + + while i < len { + crc = Self::crc_table((crc << 1) ^ data[i]); + i = i.saturating_add(1); + } + + Self(crc) + } + + // Calculates the CRC-7 lookup value based on the `crc` value. + #[inline(always)] + const fn crc_table(mut crc: u8) -> u8 { + crc ^= Self::crc_rem(crc); + let mut j = 1; + + while j < 8 { + crc = (crc << 1) ^ Self::crc_rem(crc << 1); + j += 1; + } + + crc + } + + // Used to clear leading bit from CRC value. + // + // If the leading bit is set, adds the CRC-7 polynomial to correct the value. + #[inline(always)] + const fn crc_rem(val: u8) -> u8 { + if val & 0x80 != 0 { CRC7_POLY } else { 0 } + } +} + +impl Default for Crc { + fn default() -> Self { + Self::new() + } +} + +impl From for Crc { + fn from(val: u8) -> Self { + Self::from_u8(val) + } +} + +impl From for u8 { + fn from(val: Crc) -> Self { + val.into_u8() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_crc7() { + [0b1001010, 0b0101010, 0b0110011] + .map(Crc::from) + .into_iter() + .zip([ + [0b0100_0000, 0x00, 0x00, 0x00, 0x00], + [0b0101_0001, 0x00, 0x00, 0x00, 0x00], + [0b0001_0001, 0x00, 0x00, 0b0000_1001, 0x00], + ]) + .for_each(|(exp_crc, data)| { + assert_eq!(Crc::calculate(data.as_ref()), exp_crc); + }); + } +} diff --git a/esp-hal/src/sdio/command/flag.rs b/esp-hal/src/sdio/command/flag.rs new file mode 100644 index 00000000000..1e8744a66e4 --- /dev/null +++ b/esp-hal/src/sdio/command/flag.rs @@ -0,0 +1,140 @@ +/// Represents the read-write SDIO command flag. +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum RwFlag { + /// Indicates a read-only command. + ReadOnly = 0, + /// Indicates a read-write command. + ReadWrite = 1, +} + +impl RwFlag { + /// Creates a new [RwFlag]. + pub const fn new() -> Self { + Self::ReadOnly + } + + /// Converts the [RwFlag] into a bool. + pub const fn into_bool(self) -> bool { + matches!(self, Self::ReadWrite) + } + + /// Converts a bool into a [RwFlag]. + pub const fn from_bool(val: bool) -> Self { + match val { + false => Self::ReadOnly, + true => Self::ReadWrite, + } + } + + /// Converts the [RwFlag] into a [`u8`]. + pub const fn into_u8(self) -> u8 { + self.into_bool() as u8 + } + + /// Converts a [`u8`] into a [RwFlag]. + pub const fn from_u8(val: u8) -> Self { + Self::from_bool(val != 0) + } +} + +impl Default for RwFlag { + fn default() -> Self { + Self::new() + } +} + +impl From for RwFlag { + fn from(val: bool) -> Self { + Self::from_bool(val) + } +} + +impl From for bool { + fn from(val: RwFlag) -> Self { + val.into_bool() + } +} + +impl From for RwFlag { + fn from(val: u8) -> Self { + Self::from_u8(val) + } +} + +impl From for u8 { + fn from(val: RwFlag) -> Self { + val.into_u8() + } +} + +/// Represents the read-after-write SDIO command flag. +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum RawFlag { + /// Indicates the command will only write to the device register/address. + WriteOnly = 0, + /// Indicates that the command expects device data from the same register/address in the + /// response. + ReadAfterWrite = 1, +} + +impl RawFlag { + /// Creates a new [RawFlag]. + pub const fn new() -> Self { + Self::WriteOnly + } + + /// Converts the [RawFlag] into a bool. + pub const fn into_bool(self) -> bool { + matches!(self, Self::ReadAfterWrite) + } + + /// Converts a bool into a [RawFlag]. + pub const fn from_bool(val: bool) -> Self { + match val { + false => Self::WriteOnly, + true => Self::ReadAfterWrite, + } + } + + /// Converts the [RawFlag] into a [`u8`]. + pub const fn into_u8(self) -> u8 { + self.into_bool() as u8 + } + + /// Converts a [`u8`] into a [RawFlag]. + pub const fn from_u8(val: u8) -> Self { + Self::from_bool(val != 0) + } +} + +impl Default for RawFlag { + fn default() -> Self { + Self::new() + } +} + +impl From for RawFlag { + fn from(val: bool) -> Self { + Self::from_bool(val) + } +} + +impl From for bool { + fn from(val: RawFlag) -> Self { + val.into_bool() + } +} + +impl From for RawFlag { + fn from(val: u8) -> Self { + Self::from_u8(val) + } +} + +impl From for u8 { + fn from(val: RawFlag) -> Self { + val.into_u8() + } +} diff --git a/esp-hal/src/sdio/command/fn_number.rs b/esp-hal/src/sdio/command/fn_number.rs new file mode 100644 index 00000000000..bf7aeb65126 --- /dev/null +++ b/esp-hal/src/sdio/command/fn_number.rs @@ -0,0 +1,73 @@ +use crate::sdio::Error; + +/// Represents the SDIO command function number. +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum FunctionNumber { + /// Indicates access to: + /// - CCCR (Card Common Control Registers) + /// - FBR (Function Basic Registers) + /// - CIS (Card Information Structure) + Registers = 0, + /// Indicates use for I/O transfers. + /// + /// Can be used in parallel with the other I/O function. + Io1 = 1, + /// Indicates use for I/O transfers. + /// + /// Can be used in parallel with the other I/O function. + Io2 = 2, +} + +impl FunctionNumber { + /// Byte value for registers access. + pub const REGISTERS: u8 = 0; + /// Byte value for I/O 1 access. + pub const IO1: u8 = 1; + /// Byte value for I/O 2 access. + pub const IO2: u8 = 2; + + /// Creates a new [FunctionNumber]. + pub const fn new() -> Self { + Self::Registers + } + + /// Attempts to convert a [`u8`] into a [FunctionNumber]. + pub const fn try_from_u8(val: u8) -> Result { + match val { + Self::REGISTERS => Ok(Self::Registers), + Self::IO1 => Ok(Self::Io1), + Self::IO2 => Ok(Self::Io2), + _ => Err(Error::General), + } + } + + /// Converts a [FunctionNumber] into a [`u8`]. + pub const fn into_u8(self) -> u8 { + match self { + Self::Registers => Self::REGISTERS, + Self::Io1 => Self::IO1, + Self::Io2 => Self::IO2, + } + } +} + +impl Default for FunctionNumber { + fn default() -> Self { + Self::new() + } +} + +impl TryFrom for FunctionNumber { + type Error = Error; + + fn try_from(val: u8) -> Result { + Self::try_from_u8(val) + } +} + +impl From for u8 { + fn from(val: FunctionNumber) -> Self { + val.into_u8() + } +} diff --git a/esp-hal/src/sdio/command/index.rs b/esp-hal/src/sdio/command/index.rs new file mode 100644 index 00000000000..97fae7390fe --- /dev/null +++ b/esp-hal/src/sdio/command/index.rs @@ -0,0 +1,57 @@ +use crate::sdio::Error; + +/// Represents the command indices supported by ESP32 SDIO controllers. +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum CommandIndex { + /// Represents the I/O Read/Write Direct command (CMD52) index. + IoRwDirect = 52, + /// Represents the I/O Read/Write Extended command (CMD53) index. + IoRwExtended = 53, +} + +impl CommandIndex { + /// Represents the raw index value of the CMD52 index. + pub const IO_RW_DIRECT: u8 = 52; + /// Represents the raw index value of the CMD53 index. + pub const IO_RW_EXTENDED: u8 = 53; + + /// Creates a new [CommandIndex]. + pub const fn new() -> Self { + Self::IoRwDirect + } + + /// Attempts to convert an inner value into a [CommandIndex]. + pub const fn try_from_inner(val: u8) -> Result { + match val { + Self::IO_RW_DIRECT => Ok(Self::IoRwDirect), + Self::IO_RW_EXTENDED => Ok(Self::IoRwExtended), + _ => Err(Error::IllegalCommand), + } + } + + /// Converts the [CommandIndex] into an inner value. + pub const fn into_inner(self) -> u8 { + self as u8 + } +} + +impl Default for CommandIndex { + fn default() -> Self { + Self::new() + } +} + +impl TryFrom for CommandIndex { + type Error = Error; + + fn try_from(val: u8) -> Result { + Self::try_from_inner(val) + } +} + +impl From for u8 { + fn from(val: CommandIndex) -> Self { + val.into_inner() + } +} diff --git a/esp-hal/src/sdio/config.rs b/esp-hal/src/sdio/config.rs new file mode 100644 index 00000000000..7d89a1ba16c --- /dev/null +++ b/esp-hal/src/sdio/config.rs @@ -0,0 +1,55 @@ +use super::Timing; + +/// Represents SDIO configuration parameters. +#[repr(C)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct Config { + hs: bool, + timing: Timing, +} + +impl Config { + /// Creates a new [Config]. + pub const fn new() -> Self { + Self { + hs: false, + timing: Timing::new(), + } + } + + /// Gets the highspeed enable setting. + pub const fn hs(&self) -> bool { + self.hs + } + + /// Sets the highspeed enable setting. + pub fn set_hs(&mut self, hs: bool) { + self.hs = hs; + } + + /// Builder funciton that sets the highspeed enable setting. + pub fn with_hs(self, hs: bool) -> Self { + Self { hs, ..self } + } + + /// Gets the timing setting. + pub const fn timing(&self) -> Timing { + self.timing + } + + /// Sets the timing setting. + pub fn set_timing(&mut self, timing: Timing) { + self.timing = timing; + } + + /// Builder funciton that sets the timing setting. + pub fn with_timing(self, timing: Timing) -> Self { + Self { timing, ..self } + } +} + +impl Default for Config { + fn default() -> Self { + Self::new() + } +} diff --git a/esp-hal/src/sdio/direction.rs b/esp-hal/src/sdio/direction.rs new file mode 100644 index 00000000000..a6285238848 --- /dev/null +++ b/esp-hal/src/sdio/direction.rs @@ -0,0 +1,69 @@ +/// Represents the command-response direction. +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum Direction { + /// Represents a device-to-host transfer, usually a response. + DeviceToHost = 0, + /// Represents a host-to-device transfer, usually a command. + HostToDevice = 1, +} + +impl Direction { + /// Creates a new [Direction]. + pub const fn new() -> Self { + Self::DeviceToHost + } + + /// Converts the [Direction] into a bool. + pub const fn into_bool(self) -> bool { + matches!(self, Self::HostToDevice) + } + + /// Converts a bool into a [Direction]. + pub const fn from_bool(val: bool) -> Self { + match val { + false => Self::DeviceToHost, + true => Self::HostToDevice, + } + } + + /// Converts the [Direction] into a [`u8`]. + pub const fn into_u8(self) -> u8 { + self.into_bool() as u8 + } + + /// Converts a [`u8`] into a [Direction]. + pub const fn from_u8(val: u8) -> Self { + Self::from_bool(val != 0) + } +} + +impl Default for Direction { + fn default() -> Self { + Self::new() + } +} + +impl From for Direction { + fn from(val: bool) -> Self { + Self::from_bool(val) + } +} + +impl From for bool { + fn from(val: Direction) -> Self { + val.into_bool() + } +} + +impl From for Direction { + fn from(val: u8) -> Self { + Self::from_u8(val) + } +} + +impl From for u8 { + fn from(val: Direction) -> Self { + val.into_u8() + } +} diff --git a/esp-hal/src/sdio/dma.rs b/esp-hal/src/sdio/dma.rs new file mode 100644 index 00000000000..1a1f0427671 --- /dev/null +++ b/esp-hal/src/sdio/dma.rs @@ -0,0 +1,184 @@ +//! SDIO DMA types for SoCs with a dedicated engine. + +use core::fmt::Debug; + +use crate::dma::{ + ChannelRx as ChannelRxType, + ChannelTx as ChannelTxType, + DescriptorFlagFields, + DescriptorSet as DescriptorSetType, + DmaDescriptor as DmaDescriptorType, + DmaLoopBuf as DmaLoopBufType, + DmaRxBuf as DmaRxBufType, + DmaRxTxBuf as DmaRxTxBufType, + DmaTxBuf as DmaTxBufType, + Owner, + Preparation as PreparationType, +}; + +mod buffer; +mod descriptor; + +pub use buffer::AtomicBuffer; +pub use descriptor::{AtomicDmaDescriptor, AtomicDmaDescriptors}; + +/// Convenience alias for the SDIO dedicated DMA descriptor. +pub type DmaDescriptor = DmaDescriptorType; + +/// Convenience alias for SDIO dedicated DMA preparation. +pub type Preparation = PreparationType; + +/// Convenience alias for SDIO dedicated DMA loop buffer. +pub type DmaLoopBuf = DmaLoopBufType; + +/// Convenience alias for the SDIO dedicated DMA descriptor set. +#[allow(unused)] +pub(crate) type DescriptorSet<'a> = DescriptorSetType<'a, DmaDescriptorFlags>; + +/// Convenience alias for the SDIO dedicated DMA RX buffer. +pub type DmaTxBuf = DmaTxBufType; + +/// Convenience alias for the SDIO dedicated DMA RX buffer. +pub type DmaRxBuf = DmaRxBufType; + +/// Convenience alias for the SDIO dedicated DMA RX/TX buffer. +pub type DmaRxTxBuf = DmaRxTxBufType; + +/// Convenience alias for the SDIO dedicated DMA transmit channel. +pub type ChannelTx = ChannelTxType; + +/// Convenience alias for the SDIO dedicated DMA receive channel. +pub type ChannelRx = ChannelRxType; + +bitfield::bitfield! { + /// DMA descriptor flags for the dedicated SDIO DMA engine. + /// + /// Based on the general [DMA](crate::dma::DmaDescriptorFlags) implementation, + /// with corrections for bitfield layout. + /// + /// All of the fields have the same semantics. See [DescriptorFlagFields]. + #[derive(Clone, Copy, PartialEq, Eq)] + pub struct DmaDescriptorFlags(u32); + + u16; + + _size, _set_size: 13, 0; + _length, _set_length: 27, 14; + _suc_eof, _set_suc_eof: 30; + _owner, _set_owner: 31; +} + +impl DmaDescriptorFlags { + /// Creates a new [DmaDescriptorFlags]. + pub const fn new() -> Self { + Self(0) + } + + /// Converts a [`u32`] into a [DmaDescriptorFlags]. + pub const fn from_u32(val: u32) -> Self { + Self(val) + } + + /// Converts a [DmaDescriptorFlags] into a [`u32`]. + pub const fn into_u32(self) -> u32 { + self.0 + } +} + +impl Default for DmaDescriptorFlags { + fn default() -> Self { + Self::new() + } +} + +impl From for DmaDescriptorFlags { + fn from(val: u32) -> Self { + Self::from_u32(val) + } +} + +impl From for u32 { + fn from(val: DmaDescriptorFlags) -> Self { + val.into_u32() + } +} + +impl Debug for DmaDescriptorFlags { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("DmaDescriptorFlags") + .field("size", &self.size()) + .field("length", &self.len()) + .field("suc_eof", &self.suc_eof()) + .field("owner", &self.owner().to_str()) + .finish() + } +} + +#[cfg(feature = "defmt")] +impl defmt::Format for DmaDescriptorFlags { + fn format(&self, fmt: defmt::Formatter<'_>) { + defmt::write!( + fmt, + "DmaDescriptorFlags {{ size: {}, length: {}, suc_eof: {}, owner: {} }}", + self.size(), + self.len(), + self.suc_eof(), + self.owner().to_str() + ); + } +} + +impl DescriptorFlagFields for DmaDescriptorFlags { + fn empty() -> Self { + Self(0) + } + + fn size(&self) -> usize { + self._size() as usize + } + + fn set_size(&mut self, size: usize) { + self._set_size(size as u16); + } + + fn len(&self) -> usize { + self._length() as usize + } + + fn set_len(&mut self, length: usize) { + self._set_length(length as u16); + } + + fn suc_eof(&self) -> bool { + self._suc_eof() + } + + fn set_suc_eof(&mut self, suc_eof: bool) { + self._set_suc_eof(suc_eof); + } + + fn owner(&self) -> Owner { + self._owner().into() + } + + fn set_owner(&mut self, owner: Owner) { + self._set_owner(owner.into()); + } +} + +impl DmaDescriptor { + /// Creates a new [DmaDescriptor]. + pub const fn new() -> Self { + Self { + flags: DmaDescriptorFlags::new(), + buffer: core::ptr::null_mut(), + next: core::ptr::null_mut(), + } + } +} + +impl Default for DmaDescriptor { + fn default() -> Self { + Self::new() + } +} diff --git a/esp-hal/src/sdio/dma/buffer.rs b/esp-hal/src/sdio/dma/buffer.rs new file mode 100644 index 00000000000..61dd5c55f78 --- /dev/null +++ b/esp-hal/src/sdio/dma/buffer.rs @@ -0,0 +1,93 @@ +use core::sync::atomic::{AtomicU8, Ordering}; + +/// Represents a byte buffer that can safely be stored in a static variable. +pub struct AtomicBuffer { + buffer: [AtomicU8; N], +} + +impl AtomicBuffer { + /// Creates a new [AtomicBuffer]. + pub const fn new() -> Self { + // needed for array initialization because AtomicU8 does not impl Copy + #[allow(clippy::declare_interior_mutable_const)] + const EMPTY_BYTE: AtomicU8 = AtomicU8::new(0); + + Self { + buffer: [EMPTY_BYTE; N], + } + } + + /// Gets the address of the inner buffer. + pub fn address(&self) -> u32 { + self.buffer.as_ptr() as u32 + } + + /// Gets a mutable pointer to the inner buffer. + /// + /// This method should be used to provide the buffer to the DMA engine. + /// + /// # Safety + /// + /// Callers MUST ensure that all other writes to the buffer have completed. + /// Before reading from the buffer, callers MUST ensure the DMA transfers have completed. + pub unsafe fn as_ptr_mut(&self) -> *mut u8 { + self.buffer.as_ptr() as *mut _ + } + + /// Reads bytes from the [AtomicBuffer] into a mutable byte buffer. + /// + /// # Notes + /// + /// Transfers up to the minimum of length of the two buffers. + pub fn read(&self, out: &mut [u8]) { + self.buffer + .iter() + .zip(out.iter_mut()) + .for_each(|(lhs, rhs)| { + *rhs = lhs.load(Ordering::Acquire); + }); + } + + /// Writes bytes into the [AtomicBuffer] from a byte buffer. + /// + /// # Notes + /// + /// Transfers up to the minimum of length of the two buffers. + pub fn write(&self, out: &[u8]) { + self.buffer.iter().zip(out).for_each(|(lhs, rhs)| { + lhs.store(*rhs, Ordering::Release); + }); + } + + /// Transfers bytes from one [AtomicBuffer] into another. + /// + /// # Notes + /// + /// Transfers up to the minimum of length of the two buffers. + pub fn transfer(&self, rhs: &AtomicBuffer) { + self.buffer + .iter() + .zip(rhs.buffer.iter()) + .for_each(|(lhs, rhs)| { + rhs.store(lhs.load(Ordering::Acquire), Ordering::Release); + }); + } + + /// Converts the [AtomicBuffer] into a byte array. + pub fn into_inner(self) -> [u8; N] { + self.buffer.map(|a| a.load(Ordering::Acquire)) + } + + /// Converts a byte array into an [AtomicBuffer]. + pub fn from_inner(buf: [u8; N]) -> Self { + Self { + buffer: buf.map(AtomicU8::new), + } + } +} + +impl Default for AtomicBuffer { + fn default() -> Self { + Self::new() + } +} diff --git a/esp-hal/src/sdio/dma/descriptor.rs b/esp-hal/src/sdio/dma/descriptor.rs new file mode 100644 index 00000000000..b31e7fa534e --- /dev/null +++ b/esp-hal/src/sdio/dma/descriptor.rs @@ -0,0 +1,173 @@ +use core::sync::atomic::{AtomicPtr, AtomicU32, Ordering}; + +use super::{DmaDescriptor, DmaDescriptorFlags}; + +/// Represents an atomic DMA decriptor for storage in static variables. +#[repr(C)] +pub struct AtomicDmaDescriptor { + flags: AtomicU32, + buffer: AtomicPtr, + next: AtomicPtr, +} + +impl AtomicDmaDescriptor { + const _SIZE_ASSERT: () = + core::assert!(core::mem::size_of::() == core::mem::size_of::()); + + /// Creates a new [AtomicDmaDescriptor]. + pub const fn new() -> Self { + Self { + flags: AtomicU32::new(0), + buffer: AtomicPtr::new(core::ptr::null_mut()), + next: AtomicPtr::new(core::ptr::null_mut()), + } + } + + /// Gets the DMA descriptor flags. + pub fn flags(&self) -> DmaDescriptorFlags { + self.flags.load(Ordering::Acquire).into() + } + + /// Sets the DMA descriptor flags. + pub fn set_flags(&self, flags: DmaDescriptorFlags) { + self.flags.store(flags.into(), Ordering::Release); + } + + /// Gets the DMA descriptor buffer. + pub fn buffer(&self) -> *mut u8 { + self.buffer.load(Ordering::Acquire) + } + + /// Sets the DMA descriptor buffer. + pub fn set_buffer(&self, buffer: *mut u8) { + self.buffer.store(buffer, Ordering::Release); + } + + /// Gets the next DMA descriptor. + pub fn next(&self) -> *mut DmaDescriptor { + self.next.load(Ordering::Acquire) as *mut _ + } + + /// Sets the next DMA descriptor. + pub fn set_next(&self, next: *mut DmaDescriptor) { + self.next.store(next as *mut _, Ordering::Release); + } + + /// Updates the [AtomicDmaDescriptor] from a DMA descriptor. + pub fn update(&self, desc: DmaDescriptor) { + self.set_flags(desc.flags); + self.set_buffer(desc.buffer); + self.set_next(desc.next); + } +} + +impl From for DmaDescriptor { + fn from(val: AtomicDmaDescriptor) -> Self { + Self::from(&val) + } +} + +impl From<&AtomicDmaDescriptor> for DmaDescriptor { + fn from(val: &AtomicDmaDescriptor) -> Self { + Self { + flags: val.flags(), + buffer: val.buffer(), + next: val.next(), + } + } +} + +impl From for AtomicDmaDescriptor { + fn from(val: DmaDescriptor) -> Self { + Self { + flags: AtomicU32::new(val.flags.into()), + buffer: AtomicPtr::new(val.buffer), + next: AtomicPtr::new(val.next as *mut _), + } + } +} + +impl Clone for AtomicDmaDescriptor { + fn clone(&self) -> Self { + Self { + flags: AtomicU32::new(self.flags().into()), + buffer: AtomicPtr::new(self.buffer()), + next: AtomicPtr::new(self.next() as *mut _), + } + } +} + +impl Default for AtomicDmaDescriptor { + fn default() -> Self { + Self::new() + } +} + +impl PartialEq for AtomicDmaDescriptor { + fn eq(&self, rhs: &Self) -> bool { + self.flags() == rhs.flags() && self.buffer() == rhs.buffer() && self.next() == rhs.next() + } +} + +impl Eq for AtomicDmaDescriptor {} + +impl core::fmt::Debug for AtomicDmaDescriptor { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + core::write!(f, "{:?}", DmaDescriptor::from(self)) + } +} + +#[cfg(feature = "defmt")] +impl defmt::Format for AtomicDmaDescriptor { + fn format(&self, fmt: defmt::Formatter<'_>) { + defmt::write!(fmt, "{}", DmaDescriptor::from(self),); + } +} + +/// Represents a container for [AtomicDmaDescriptors]. +/// +/// Useful for storing a list of DMA descriptors in static memory. +#[repr(C)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct AtomicDmaDescriptors { + descriptors: [AtomicDmaDescriptor; N], +} + +impl AtomicDmaDescriptors { + /// Creates a new [AtomicDmaDescriptors]. + pub const fn new() -> Self { + // Needed for array initialization because AtomicDmaDescriptor does not impl Copy + #[allow(clippy::declare_interior_mutable_const)] + const EMPTY: AtomicDmaDescriptor = AtomicDmaDescriptor::new(); + + Self { + descriptors: [EMPTY; N], + } + } + + /// Gets a reference to the list of DMA descriptors. + pub const fn descriptors(&self) -> &[AtomicDmaDescriptor] { + &self.descriptors + } + + /// Gets the address of the inner descriptor list. + pub fn address(&self) -> u32 { + self.descriptors.as_ptr() as u32 + } +} + +impl core::ops::Index for AtomicDmaDescriptors { + type Output = AtomicDmaDescriptor; + + #[inline(always)] + fn index(&self, index: usize) -> &Self::Output { + self.descriptors.index(index) + } +} + +impl Default for AtomicDmaDescriptors { + fn default() -> Self { + Self::new() + } +} diff --git a/esp-hal/src/sdio/hinf.rs b/esp-hal/src/sdio/hinf.rs new file mode 100644 index 00000000000..dc686ff0deb --- /dev/null +++ b/esp-hal/src/sdio/hinf.rs @@ -0,0 +1,31 @@ +use crate::pac::hinf; + +crate::any_peripheral! { + /// Any SDIO HINF peripheral. + pub peripheral AnyHinf<'d> { + Hinf(crate::peripherals::HINF<'d>) + } +} + +/// Represents the HINF registers for SDIO peripherals. +pub struct HinfInfo { + /// Represents the HINF register block. + pub register_block: *const hinf::RegisterBlock, +} + +unsafe impl Sync for HinfInfo {} + +/// A peripheral singleton compatible with the SDIO HINF driver. +pub trait HinfInstance: any::Degrade { + /// Gets a static reference the the [HinfInfo]. + fn info(&self) -> &'static HinfInfo { + static INFO: HinfInfo = HinfInfo { + register_block: crate::peripherals::HINF::ptr(), + }; + + &INFO + } +} + +impl HinfInstance for AnyHinf<'_> {} +impl HinfInstance for crate::peripherals::HINF<'_> {} diff --git a/esp-hal/src/sdio/interrupt.rs b/esp-hal/src/sdio/interrupt.rs new file mode 100644 index 00000000000..410d85c0938 --- /dev/null +++ b/esp-hal/src/sdio/interrupt.rs @@ -0,0 +1,151 @@ +use enumset::EnumSetType; + +/// Represents an interrupt to send to the host. +/// +/// # Note +/// +/// Values derived from [esp-idf](https://github.com/espressif/esp-idf/blob/v5.4.1/components/hal/include/hal/sdio_slave_types.h) SDIO driver. +#[derive(Debug, EnumSetType)] +pub enum HostInterrupt { + /// General purpose host interrupt: 0. + #[enumset(repr = "u8")] + General0 = 1, + /// General purpose host interrupt: 1. + #[enumset(repr = "u8")] + General1 = 2, + /// General purpose host interrupt: 2. + #[enumset(repr = "u8")] + General2 = 4, + /// General purpose host interrupt: 3. + #[enumset(repr = "u8")] + General3 = 8, + /// General purpose host interrupt: 4. + #[enumset(repr = "u8")] + General4 = 16, + /// General purpose host interrupt: 5. + #[enumset(repr = "u8")] + General5 = 32, + /// General purpose host interrupt: 6. + #[enumset(repr = "u8")] + General6 = 64, + /// General purpose host interrupt: 7. + #[enumset(repr = "u8")] + General7 = 128, +} + +impl HostInterrupt { + /// Creates a new [HostInterrupt]. + pub const fn new() -> Self { + Self::General0 + } + + /// Gets the raw bit value of the [HostInterrupt]. + pub const fn bits(&self) -> u8 { + *self as u8 + } +} + +impl From for u8 { + fn from(val: HostInterrupt) -> Self { + val.bits() + } +} + +impl TryFrom for HostInterrupt { + type Error = u8; + + fn try_from(val: u8) -> Result { + match val { + 1 => Ok(Self::General0), + 2 => Ok(Self::General1), + 4 => Ok(Self::General2), + 8 => Ok(Self::General3), + 16 => Ok(Self::General4), + 32 => Ok(Self::General5), + 64 => Ok(Self::General6), + 128 => Ok(Self::General7), + _ => Err(val), + } + } +} + +impl Default for HostInterrupt { + fn default() -> Self { + Self::new() + } +} + +/// Represents an interrupt to sent from the host. +/// +/// # Note +/// +/// Values derived from [esp-idf](https://github.com/espressif/esp-idf/blob/v5.4.1/components/hal/esp32/include/hal/sdio_slave_ll.h) SDIO driver. +#[derive(Debug, EnumSetType)] +pub enum DeviceInterrupt { + /// General purpose host interrupt: 0. + #[enumset(repr = "u8")] + General0 = 1, + /// General purpose host interrupt: 1. + #[enumset(repr = "u8")] + General1 = 2, + /// General purpose host interrupt: 2. + #[enumset(repr = "u8")] + General2 = 4, + /// General purpose host interrupt: 3. + #[enumset(repr = "u8")] + General3 = 8, + /// General purpose host interrupt: 4. + #[enumset(repr = "u8")] + General4 = 16, + /// General purpose host interrupt: 5. + #[enumset(repr = "u8")] + General5 = 32, + /// General purpose host interrupt: 6. + #[enumset(repr = "u8")] + General6 = 64, + /// General purpose host interrupt: 7. + #[enumset(repr = "u8")] + General7 = 128, +} + +impl DeviceInterrupt { + /// Creates a new [DeviceInterrupt]. + pub const fn new() -> Self { + Self::General0 + } + + /// Gets the raw bit value of the [DeviceInterrupt]. + pub const fn bits(&self) -> u8 { + *self as u8 + } +} + +impl From for u8 { + fn from(val: DeviceInterrupt) -> Self { + val.bits() + } +} + +impl TryFrom for DeviceInterrupt { + type Error = u8; + + fn try_from(val: u8) -> Result { + match val { + 1 => Ok(Self::General0), + 2 => Ok(Self::General1), + 4 => Ok(Self::General2), + 8 => Ok(Self::General3), + 16 => Ok(Self::General4), + 32 => Ok(Self::General5), + 64 => Ok(Self::General6), + 128 => Ok(Self::General7), + _ => Err(val), + } + } +} + +impl Default for DeviceInterrupt { + fn default() -> Self { + Self::new() + } +} diff --git a/esp-hal/src/sdio/pins.rs b/esp-hal/src/sdio/pins.rs new file mode 100644 index 00000000000..b920d515082 --- /dev/null +++ b/esp-hal/src/sdio/pins.rs @@ -0,0 +1,274 @@ +use super::{Error, Mode}; +use crate::gpio::{ + AnyPin, + DriveMode, + InputSignal, + OutputConfig, + OutputSignal, + PinGuard, + Pull, + interconnect, +}; + +/// Represents SDIO pin signals. +pub(crate) enum SdioPin { + /// Represents the SDIO CLK signal. + Clk = 19, + /// Represents the SDIO CMD signal. + Cmd = 18, + /// Represents the SDIO DATA0 signal. + Data0 = 20, + /// Represents the SDIO DATA1 signal. + Data1 = 21, + /// Represents the SDIO DATA2 signal. + Data2 = 22, + /// Represents the SDIO DATA3 signal. + Data3 = 23, +} + +/// Represents the GPIO pins used for SDIO communication. +/// +/// The pins are configurable for the SPI + SD (1-bit, 4-bit) operation modes. +/// +/// The CLK/SCLK pin is configured as an output, all other pins are input/output +/// `Flex` pins. +#[derive(Debug)] +pub struct Pins { + mode: Mode, + pub(crate) clk_sclk: PinGuard, + pub(crate) cmd_mosi: PinGuard, + pub(crate) dat0_miso: PinGuard, + pub(crate) dat1_irq: PinGuard, + pub(crate) dat2: PinGuard, + pub(crate) dat3_cs: PinGuard, +} + +impl Pins { + /// Creates a new [Pins] from the provided GPIO pins. + /// + /// ## Example + /// + /// ```rust, no_run + #[doc = crate::before_snippet!()] + /// use esp_hal::sdio::{Mode, Pins}; + /// + /// let _pins = Pins::new( + /// Mode::Sd4bit, + /// peripherals.GPIO19, // CLK/SCLK + /// peripherals.GPIO18, // CMD/MOSI + /// peripherals.GPIO20, // DAT0/MISO + /// peripherals.GPIO21, // DAT1/IRQ + /// peripherals.GPIO22, // DAT2 + /// peripherals.GPIO23, // DAT3/#CS + /// ); + /// # Ok(()) + /// # } + /// ``` + pub fn new<'clk, 'cmd, 'd0, 'd1, 'd2, 'd3, CLK, CMD, DATA0, DATA1, DATA2, DATA3>( + mode: Mode, + clk_sclk: CLK, + cmd_mosi: CMD, + dat0_miso: DATA0, + dat1_irq: DATA1, + dat2: DATA2, + dat3_cs: DATA3, + ) -> Self + where + CLK: Into>, + CMD: Into>, + DATA0: Into>, + DATA1: Into>, + DATA2: Into>, + DATA3: Into>, + { + Self { + mode, + clk_sclk: Self::connect_pin(clk_sclk.into(), OutputSignal::SDIO_CLK, None), + cmd_mosi: Self::connect_pin( + cmd_mosi.into(), + OutputSignal::SDIO_CMD, + Some(InputSignal::SDIO_CMD), + ), + dat0_miso: Self::connect_pin( + dat0_miso.into(), + OutputSignal::SDIO_DATA0, + Some(InputSignal::SDIO_DATA0), + ), + dat1_irq: Self::connect_pin( + dat1_irq.into(), + OutputSignal::SDIO_DATA1, + Some(InputSignal::SDIO_DATA1), + ), + dat2: Self::connect_pin( + dat2.into(), + OutputSignal::SDIO_DATA2, + Some(InputSignal::SDIO_DATA2), + ), + dat3_cs: Self::connect_pin( + dat3_cs.into(), + OutputSignal::SDIO_DATA3, + Some(InputSignal::SDIO_DATA3), + ), + } + } + + fn connect_pin( + pin: interconnect::OutputSignal<'_>, + output: OutputSignal, + input: Option, + ) -> PinGuard { + pin.set_output_high(true); + + pin.apply_output_config( + &OutputConfig::default() + .with_drive_mode(DriveMode::OpenDrain) + .with_pull(Pull::Up), + ); + + pin.set_output_enable(true); + + if let Some(in_signal) = input.as_ref() { + pin.set_input_enable(true); + in_signal.connect_to(&pin); + } else { + pin.set_input_enable(false); + } + + interconnect::OutputSignal::connect_with_guard(pin, output) + } + + fn unguard_pin(&self, pin: SdioPin) -> Result, Error> { + let pin = match pin { + SdioPin::Clk => &self.clk_sclk, + SdioPin::Cmd => &self.cmd_mosi, + SdioPin::Data0 => &self.dat0_miso, + SdioPin::Data1 => &self.dat1_irq, + SdioPin::Data2 => &self.dat2, + SdioPin::Data3 => &self.dat3_cs, + }; + + pin.pin_number() + .map(|n| unsafe { AnyPin::steal(n) }) + .ok_or(Error::General) + } + + /// Gets the [Mode] of the [Pins]. + pub const fn mode(&self) -> Mode { + self.mode + } + + /// Sets the [Mode] of the [Pins]. + pub fn set_mode(&mut self, mode: Mode) { + self.mode = mode; + } + + /// Gets the CLK signal for the [Pins]. + /// + /// Returns an [Error] if the [Mode] is SPI. + pub fn clk(&self) -> Result, Error> { + match self.mode { + Mode::Sd1bit | Mode::Sd4bit => self.unguard_pin(SdioPin::Clk), + Mode::Spi => Err(Error::General), + } + } + + /// Gets the SCLK signal for the [Pins]. + /// + /// Returns an [Error] if the [Mode] is not SPI. + pub fn sclk(&self) -> Result, Error> { + match self.mode { + Mode::Spi => self.unguard_pin(SdioPin::Clk), + Mode::Sd1bit | Mode::Sd4bit => Err(Error::General), + } + } + + /// Gets the CMD signal for the [Pins]. + /// + /// Returns an [Error] if the [Mode] is SPI. + pub fn cmd(&self) -> Result, Error> { + match self.mode { + Mode::Sd1bit | Mode::Sd4bit => self.unguard_pin(SdioPin::Cmd), + Mode::Spi => Err(Error::General), + } + } + + /// Gets the MOSI signal for the [Pins]. + /// + /// Returns an [Error] if the [Mode] is not SPI. + pub fn mosi(&self) -> Result, Error> { + match self.mode { + Mode::Spi => self.unguard_pin(SdioPin::Cmd), + Mode::Sd1bit | Mode::Sd4bit => Err(Error::General), + } + } + + /// Gets the DAT0 signal for the [Pins]. + /// + /// Returns an [Error] if the [Mode] is SPI. + pub fn dat0(&self) -> Result, Error> { + match self.mode { + Mode::Sd1bit | Mode::Sd4bit => self.unguard_pin(SdioPin::Data0), + Mode::Spi => Err(Error::General), + } + } + + /// Gets the MISO signal for the [Pins]. + /// + /// Returns an [Error] if the [Mode] is not SPI. + pub fn miso(&self) -> Result, Error> { + match self.mode { + Mode::Spi => self.unguard_pin(SdioPin::Data0), + Mode::Sd1bit | Mode::Sd4bit => Err(Error::General), + } + } + + /// Gets the DAT1 signal for the [Pins]. + /// + /// Returns an [Error] if the [Mode] is not SD 4-bit. + pub fn dat1(&self) -> Result, Error> { + match self.mode { + Mode::Sd1bit | Mode::Sd4bit => self.unguard_pin(SdioPin::Data1), + Mode::Spi => Err(Error::General), + } + } + + /// Gets the IRQ signal for the [Pins]. + /// + /// Returns an [Error] if the [Mode] is SD 4-bit. + pub fn irq(&self) -> Result, Error> { + match self.mode { + Mode::Spi => self.unguard_pin(SdioPin::Data1), + Mode::Sd1bit | Mode::Sd4bit => Err(Error::General), + } + } + + /// Gets the DAT2 signal for the [Pins]. + /// + /// Returns an [Error] if the [Mode] is not SD 4-bit. + pub fn dat2(&self) -> Result, Error> { + match self.mode { + Mode::Sd1bit | Mode::Sd4bit => self.unguard_pin(SdioPin::Data2), + Mode::Spi => Err(Error::General), + } + } + + /// Gets the DAT3 signal for the [Pins]. + /// + /// Returns an [Error] if the [Mode] is SPI. + pub fn dat3(&self) -> Result, Error> { + match self.mode { + Mode::Sd1bit | Mode::Sd4bit => self.unguard_pin(SdioPin::Data3), + Mode::Spi => Err(Error::General), + } + } + + /// Gets the CS signal for the [Pins]. + /// + /// Returns an [Error] if the [Mode] is not SPI. + pub fn cs(&self) -> Result, Error> { + match self.mode { + Mode::Spi => self.unguard_pin(SdioPin::Data3), + Mode::Sd1bit | Mode::Sd4bit => Err(Error::General), + } + } +} diff --git a/esp-hal/src/sdio/response.rs b/esp-hal/src/sdio/response.rs new file mode 100644 index 00000000000..8a593c364fd --- /dev/null +++ b/esp-hal/src/sdio/response.rs @@ -0,0 +1,10 @@ +//! SDIO response types. + +mod flags; +mod io_current_state; + +pub mod sd; +pub mod spi; + +pub use flags::Flags; +pub use io_current_state::IoCurrentState; diff --git a/esp-hal/src/sdio/response/flags.rs b/esp-hal/src/sdio/response/flags.rs new file mode 100644 index 00000000000..25b5b472901 --- /dev/null +++ b/esp-hal/src/sdio/response/flags.rs @@ -0,0 +1,44 @@ +use bitfielder::bitfield; + +use super::IoCurrentState; +use crate::sdio::Error; + +bitfield! { + /// Represents the SDIO R5 response flags. + pub Flags(u8): u8, + mask: 0xfb, + default: 0, + { + /// Indicates a CRC error in the previous command. + pub crc_error: 7; + /// Indicates an illegal command. + pub illegal_command: 6; + raw_io_state: 5, 4; + /// Indicates a general error. + pub error: 3; + /// Indicates an invalid function number. + pub function_number: 1; + /// Indicates the command's argument was out-of-range. + pub out_of_range: 0; + } +} + +impl Flags { + /// Gets the SDIO card current state. + pub const fn io_current_state(&self) -> Result { + IoCurrentState::try_from_inner(self.raw_io_state()) + } + + /// Sets the SDIO card current state. + pub fn set_io_current_state(&mut self, val: IoCurrentState) { + self.set_raw_io_state(val.into_inner()); + } + + /// Attempts to convert an inner value into a [Flags]. + pub const fn try_from_inner(val: u8) -> Result { + match Self(val) { + f if f.io_current_state().is_err() => Err(Error::General), + f => Ok(f), + } + } +} diff --git a/esp-hal/src/sdio/response/io_current_state.rs b/esp-hal/src/sdio/response/io_current_state.rs new file mode 100644 index 00000000000..5feb0cdac49 --- /dev/null +++ b/esp-hal/src/sdio/response/io_current_state.rs @@ -0,0 +1,62 @@ +use crate::sdio::Error; + +/// Represents the current card state. +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum IoCurrentState { + /// Indicates the card is disabled. + Disabled = 0, + /// Indicates the card's DAT lines are free. + Command = 1, + /// Indicates an active data transfer. + Transfer = 2, +} + +impl IoCurrentState { + /// Represents the raw disabled value. + pub const DISABLED: u8 = 0; + /// Represents the raw command value. + pub const COMMAND: u8 = 1; + /// Represents the raw transfer value. + pub const TRANSFER: u8 = 2; + + /// Creates a new [IoCurrentState]. + pub const fn new() -> Self { + Self::Disabled + } + + /// Attempts to convert an inner value into a [IoCurrentState]. + pub const fn try_from_inner(val: u8) -> Result { + match val { + Self::DISABLED => Ok(Self::Disabled), + Self::COMMAND => Ok(Self::Command), + Self::TRANSFER => Ok(Self::Transfer), + _ => Err(Error::General), + } + } + + /// Attempts to convert an inner value into a [IoCurrentState]. + pub const fn into_inner(self) -> u8 { + self as u8 + } +} + +impl Default for IoCurrentState { + fn default() -> Self { + Self::new() + } +} + +impl TryFrom for IoCurrentState { + type Error = Error; + + fn try_from(val: u8) -> Result { + Self::try_from_inner(val) + } +} + +impl From for u8 { + fn from(val: IoCurrentState) -> Self { + val.into_inner() + } +} diff --git a/esp-hal/src/sdio/response/sd.rs b/esp-hal/src/sdio/response/sd.rs new file mode 100644 index 00000000000..95efd3fb479 --- /dev/null +++ b/esp-hal/src/sdio/response/sd.rs @@ -0,0 +1,103 @@ +//! Response types for SD modes. + +use bitfielder::bitfield; + +use super::Flags; +use crate::sdio::{ + Direction, + Error, + command::{CommandIndex, Crc}, +}; + +bitfield! { + /// Represents the SD modes R5 (`IO_RW_DIRECT` + `IO_RW_EXTENDED`) response. + pub R5(MSB0 [u8; 6]): u8 { + raw_s: 47; + raw_direction: 46; + raw_command_index: 45, 40; + /// Indicates an illegal command. + pub illegal_command: 10; + raw_flags: 23, 16; + /// Represents the R/W response data. + pub data: 15, 8; + raw_crc: 7, 1; + raw_e: 0; + } +} + +impl R5 { + /// Represents the byte length of the [R5] response. + pub const LEN: usize = 6; + + /// Gets the direction. + pub const fn direction(&self) -> Direction { + Direction::from_bool(self.raw_direction()) + } + + /// Gets the command index. + pub const fn command_index(&self) -> Result { + CommandIndex::try_from_inner(self.raw_command_index()) + } + + /// Sets the command index. + pub fn set_command_index(&mut self, val: CommandIndex) { + self.set_raw_command_index(val.into_inner()); + } + + /// Gets the response flags. + pub const fn flags(&self) -> Result { + Flags::try_from_inner(self.raw_flags()) + } + + /// Sets the response flags. + pub fn set_flags(&mut self, val: Flags) { + self.set_raw_flags(val.into_inner()); + } + + /// Gets the CRC. + pub const fn crc(&self) -> Crc { + Crc::from_u8(self.raw_crc()) + } + + /// Calculates and sets the CRC. + pub fn set_crc(&mut self) { + self.set_raw_crc(self.calculate_crc().into_u8()); + } + + /// Calculates the CRC. + pub const fn calculate_crc(&self) -> Crc { + let [b0, b1, b2, b3, b4, _] = self.0; + Crc::calculate(&[b0, b1, b2, b3, b4]) + } + + /// Verifies if the CRC matches the calculated value. + pub const fn verify_crc(&self) -> Result<(), Error> { + match self.calculate_crc().into_u8() == self.crc().into_u8() { + false => Err(Error::Crc), + true => Ok(()), + } + } + + /// Attempts to convert a byte slice into a [R5]. + pub const fn try_from_bytes(val: &[u8]) -> Result { + match val.len() { + len if len < Self::LEN => Err(Error::General), + _ => match Self([val[0], val[1], val[2], val[3], val[4], val[5]]) { + cmd if cmd.command_index().is_err() => Err(Error::IllegalCommand), + cmd if cmd.raw_s() => Err(Error::General), + cmd if !cmd.raw_e() => Err(Error::General), + cmd if cmd.flags().is_err() => Err(Error::General), + cmd if cmd.command_index().is_err() && !cmd.illegal_command() => { + Err(Error::IllegalCommand) + } + cmd if cmd.verify_crc().is_err() => Err(Error::Crc), + cmd => Ok(cmd), + }, + } + } + + /// Converts the [R5] into a byte array. + pub const fn into_bytes(self) -> [u8; Self::LEN] { + self.0 + } +} diff --git a/esp-hal/src/sdio/response/spi.rs b/esp-hal/src/sdio/response/spi.rs new file mode 100644 index 00000000000..254f93ac9b2 --- /dev/null +++ b/esp-hal/src/sdio/response/spi.rs @@ -0,0 +1,63 @@ +//! Response types for SPI mode. + +use bitfielder::bitfield; + +use crate::sdio::Error; + +bitfield! { + /// Represents the SPI mode R5 (`IO_RW_DIRECT` + `IO_RW_EXTENDED`) response. + pub R5(u16): u8, + mask: 0x5d_ff, + default: 0, + { + /// Represents the start bit (always 0). + raw_s: 15; + /// Indicates a parameter error. + pub param_error: 14; + /// Indicates a function number error. + pub fn_number_error: 12; + /// Indicates a CRC error. + pub crc_error: 11; + /// Indicates an illegal command. + pub illegal_command: 10; + /// Indicates idle status. + pub idle: 8; + /// Represents the R/W response data. + pub data: 7, 0; + } +} + +impl R5 { + /// Represents the byte length of the [R5] response. + pub const LEN: usize = 2; + + /// Attempts to convert a byte slice into a [R5] response. + pub const fn try_from_bytes(val: &[u8]) -> Result { + match val.len() { + len if len < Self::LEN => Err(Error::General), + _ => match Self(u16::from_be_bytes([val[0], val[1]])) { + r5 if r5.raw_s() => Err(Error::General), + r5 => Ok(r5), + }, + } + } + + /// Converts the [R5] response into a byte array. + pub const fn into_bytes(self) -> [u8; Self::LEN] { + self.0.to_be_bytes() + } +} + +impl TryFrom<&[u8]> for R5 { + type Error = Error; + + fn try_from(val: &[u8]) -> Result { + Self::try_from_bytes(val) + } +} + +impl From for [u8; R5::LEN] { + fn from(val: R5) -> Self { + val.into_bytes() + } +} diff --git a/esp-hal/src/sdio/slc.rs b/esp-hal/src/sdio/slc.rs new file mode 100644 index 00000000000..ebd8a6616a0 --- /dev/null +++ b/esp-hal/src/sdio/slc.rs @@ -0,0 +1,31 @@ +use crate::pac::slc; + +crate::any_peripheral! { + /// Any SDIO SLC peripheral. + pub peripheral AnySlc<'d> { + Slc(crate::peripherals::SLC<'d>) + } +} + +/// Represents the SLC registers for SDIO peripherals. +pub struct SlcInfo { + /// Represents the SLC register block. + pub register_block: *const slc::RegisterBlock, +} + +unsafe impl Sync for SlcInfo {} + +/// A peripheral singleton compatible with the SDIO SLC driver. +pub trait SlcInstance: any::Degrade { + /// Gets a static reference the the [SlcInfo]. + fn info(&self) -> &'static SlcInfo { + static INFO: SlcInfo = SlcInfo { + register_block: crate::peripherals::SLC::ptr(), + }; + + &INFO + } +} + +impl SlcInstance for AnySlc<'_> {} +impl SlcInstance for crate::peripherals::SLC<'_> {} diff --git a/esp-hal/src/sdio/slchost.rs b/esp-hal/src/sdio/slchost.rs new file mode 100644 index 00000000000..8260b85296f --- /dev/null +++ b/esp-hal/src/sdio/slchost.rs @@ -0,0 +1,31 @@ +use crate::pac::slchost; + +crate::any_peripheral! { + /// Any SDIO SLCHOST peripheral. + pub peripheral AnySlchost<'d> { + Slchost(crate::peripherals::SLCHOST<'d>) + } +} + +/// Represents the SLCHOST registers for SDIO peripherals. +pub struct SlchostInfo { + /// Represents the SLCHOST register block. + pub register_block: *const slchost::RegisterBlock, +} + +unsafe impl Sync for SlchostInfo {} + +/// A peripheral singleton compatible with the SDIO SLCHOST driver. +pub trait SlchostInstance: any::Degrade { + /// Gets a static reference the the [SlchostInfo]. + fn info(&self) -> &'static SlchostInfo { + static INFO: SlchostInfo = SlchostInfo { + register_block: crate::peripherals::SLCHOST::ptr(), + }; + + &INFO + } +} + +impl SlchostInstance for AnySlchost<'_> {} +impl SlchostInstance for crate::peripherals::SLCHOST<'_> {} diff --git a/esp-hal/src/sdio/state.rs b/esp-hal/src/sdio/state.rs new file mode 100644 index 00000000000..e2ff0738d17 --- /dev/null +++ b/esp-hal/src/sdio/state.rs @@ -0,0 +1,59 @@ +use super::Error; + +/// Represents valid states for the SDIO peripheral. +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum State { + /// Represents the initial state. + /// + /// The peripheral is preparing for operation, e.g. configuring bus width, + /// mode, speed etc. + Init, + /// Represents the standby state. + /// + /// The peripheral is in standby waiting for a command. + Standby, + /// Represents the command state. + /// + /// The peripheral is processing a command from the host. + Command, + /// Represents the transfer state. + /// + /// The peripheral is sending/receiving a data transfer. + Transfer, +} + +impl State { + /// Creates a new [State]. + pub const fn new() -> Self { + Self::Init + } + + /// Checks if the [State] transition is valid. + pub const fn valid_transition(self, state: Self) -> Result<(), Error> { + match (self, state) { + (Self::Init, Self::Standby) => Ok(()), + (Self::Standby, Self::Command) => Ok(()), + (Self::Command, Self::Init | Self::Standby | Self::Transfer) => Ok(()), + (Self::Transfer, Self::Init | Self::Command) => Ok(()), + _ => Err(Error::invalid_transition(self, state)), + } + } +} + +impl Default for State { + fn default() -> Self { + Self::new() + } +} + +impl core::fmt::Display for State { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::Init => write!(f, "init"), + Self::Standby => write!(f, "standby"), + Self::Command => write!(f, "command"), + Self::Transfer => write!(f, "transfer"), + } + } +} diff --git a/esp-hal/src/sdio/timing.rs b/esp-hal/src/sdio/timing.rs new file mode 100644 index 00000000000..ba64eb3ce0d --- /dev/null +++ b/esp-hal/src/sdio/timing.rs @@ -0,0 +1,26 @@ +/// Represents SDIO device timing settings. +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum Timing { + /// Send at posedge, and sample at posedge. Default for HS. + PsendPsample = 0, + /// Send at negedge, and sample at posedge. Default for DS. + NsendPsample, + /// Send at posedge, and sample at negedge. + PsendNsample, + /// Send at negedge, and sample at negedge. + NsendNsample, +} + +impl Timing { + /// Creates a new [Timing]. + pub const fn new() -> Self { + Self::PsendPsample + } +} + +impl Default for Timing { + fn default() -> Self { + Self::new() + } +} diff --git a/esp-metadata-generated/src/_build_script_utils.rs b/esp-metadata-generated/src/_build_script_utils.rs index 10ee19b0adb..9eb21be5ff2 100644 --- a/esp-metadata-generated/src/_build_script_utils.rs +++ b/esp-metadata-generated/src/_build_script_utils.rs @@ -1049,6 +1049,7 @@ impl Chip { "soc_has_rng", "soc_has_rsa", "soc_has_sha", + "soc_has_slc", "soc_has_slchost", "soc_has_etm", "soc_has_spi0", @@ -1251,6 +1252,7 @@ impl Chip { "cargo:rustc-cfg=soc_has_rng", "cargo:rustc-cfg=soc_has_rsa", "cargo:rustc-cfg=soc_has_sha", + "cargo:rustc-cfg=soc_has_slc", "cargo:rustc-cfg=soc_has_slchost", "cargo:rustc-cfg=soc_has_etm", "cargo:rustc-cfg=soc_has_spi0", diff --git a/esp-metadata-generated/src/_generated_esp32.rs b/esp-metadata-generated/src/_generated_esp32.rs index 9779026d5e0..8dc1436eff6 100644 --- a/esp-metadata-generated/src/_generated_esp32.rs +++ b/esp-metadata-generated/src/_generated_esp32.rs @@ -532,45 +532,46 @@ macro_rules! for_each_gpio { _for_each_inner!((0, GPIO0(_5 => EMAC_TX_CLK) (_1 => CLK_OUT1 _5 => EMAC_TX_CLK) ([Input] [Output]))); _for_each_inner!((1, GPIO1(_5 => EMAC_RXD2) (_0 => U0TXD _1 => CLK_OUT3) ([Input] [Output]))); _for_each_inner!((2, GPIO2(_1 => HSPIWP _3 => - HS2_DATA0 _4 => SD_DATA0) (_1 => HSPIWP _3 => HS2_DATA0 _4 => SD_DATA0) ([Input] - [Output]))); _for_each_inner!((3, GPIO3(_0 => U0RXD) (_1 => CLK_OUT2) ([Input] - [Output]))); _for_each_inner!((4, GPIO4(_1 => HSPIHD _3 => HS2_DATA1 _4 => - SD_DATA1 _5 => EMAC_TX_ER) (_1 => HSPIHD _3 => HS2_DATA1 _4 => SD_DATA1 _5 => - EMAC_TX_ER) ([Input] [Output]))); _for_each_inner!((5, GPIO5(_1 => VSPICS0 _3 => - HS1_DATA6 _5 => EMAC_RX_CLK) (_1 => VSPICS0 _3 => HS1_DATA6) ([Input] - [Output]))); _for_each_inner!((6, GPIO6(_1 => SPICLK _4 => U1CTS) (_0 => SD_CLK + HS2_DATA0 _4 => SDIO_DATA0) (_1 => HSPIWP _3 => HS2_DATA0 _4 => SDIO_DATA0) + ([Input] [Output]))); _for_each_inner!((3, GPIO3(_0 => U0RXD) (_1 => CLK_OUT2) + ([Input] [Output]))); _for_each_inner!((4, GPIO4(_1 => HSPIHD _3 => HS2_DATA1 _4 + => SDIO_DATA1 _5 => EMAC_TX_ER) (_1 => HSPIHD _3 => HS2_DATA1 _4 => SDIO_DATA1 _5 + => EMAC_TX_ER) ([Input] [Output]))); _for_each_inner!((5, GPIO5(_1 => VSPICS0 _3 + => HS1_DATA6 _5 => EMAC_RX_CLK) (_1 => VSPICS0 _3 => HS1_DATA6) ([Input] + [Output]))); _for_each_inner!((6, GPIO6(_1 => SPICLK _4 => U1CTS) (_0 => SDIO_CLK _1 => SPICLK _3 => HS1_CLK) ([Input] [Output]))); _for_each_inner!((7, GPIO7(_0 - => SD_DATA0 _1 => SPIQ _3 => HS1_DATA0) (_0 => SD_DATA0 _1 => SPIQ _3 => + => SDIO_DATA0 _1 => SPIQ _3 => HS1_DATA0) (_0 => SDIO_DATA0 _1 => SPIQ _3 => HS1_DATA0 _4 => U2RTS) ([Input] [Output]))); _for_each_inner!((8, GPIO8(_0 => - SD_DATA1 _1 => SPID _3 => HS1_DATA1 _4 => U2CTS) (_0 => SD_DATA1 _1 => SPID _3 => - HS1_DATA1) ([Input] [Output]))); _for_each_inner!((9, GPIO9(_0 => SD_DATA2 _1 => - SPIHD _3 => HS1_DATA2 _4 => U1RXD) (_0 => SD_DATA2 _1 => SPIHD _3 => HS1_DATA2) - ([Input] [Output]))); _for_each_inner!((10, GPIO10(_0 => SD_DATA3 _1 => SPIWP _3 - => HS1_DATA3) (_0 => SD_DATA3 _1 => SPIWP _3 => HS1_DATA3 _4 => U1TXD) ([Input] - [Output]))); _for_each_inner!((11, GPIO11(_0 => SD_CMD _1 => SPICS0) (_0 => - SD_CMD _1 => SPICS0 _3 => HS1_CMD _4 => U1RTS) ([Input] [Output]))); - _for_each_inner!((12, GPIO12(_0 => MTDI _1 => HSPIQ _3 => HS2_DATA2 _4 => - SD_DATA2) (_1 => HSPIQ _3 => HS2_DATA2 _4 => SD_DATA2 _5 => EMAC_TXD3) ([Input] - [Output]))); _for_each_inner!((13, GPIO13(_0 => MTCK _1 => HSPID _3 => HS2_DATA3 - _4 => SD_DATA3 _5 => EMAC_RX_ER) (_1 => HSPID _3 => HS2_DATA3 _4 => SD_DATA3 _5 - => EMAC_RX_ER) ([Input] [Output]))); _for_each_inner!((14, GPIO14(_0 => MTMS _1 - => HSPICLK) (_1 => HSPICLK _3 => HS2_CLK _4 => SD_CLK _5 => EMAC_TXD2) ([Input] - [Output]))); _for_each_inner!((15, GPIO15(_1 => HSPICS0 _4 => SD_CMD _5 => - EMAC_RXD3) (_0 => MTDO _1 => HSPICS0 _3 => HS2_CMD _4 => SD_CMD) ([Input] - [Output]))); _for_each_inner!((16, GPIO16(_3 => HS1_DATA4 _4 => U2RXD) (_3 => - HS1_DATA4 _5 => EMAC_CLK_OUT) ([Input] [Output]))); _for_each_inner!((17, - GPIO17(_3 => HS1_DATA5) (_3 => HS1_DATA5 _4 => U2TXD _5 => EMAC_CLK_180) ([Input] - [Output]))); _for_each_inner!((18, GPIO18(_1 => VSPICLK _3 => HS1_DATA7) (_1 => - VSPICLK _3 => HS1_DATA7) ([Input] [Output]))); _for_each_inner!((19, GPIO19(_1 => - VSPIQ _3 => U0CTS) (_1 => VSPIQ _5 => EMAC_TXD0) ([Input] [Output]))); - _for_each_inner!((20, GPIO20() () ([Input] [Output]))); _for_each_inner!((21, - GPIO21(_1 => VSPIHD) (_1 => VSPIHD _5 => EMAC_TX_EN) ([Input] [Output]))); - _for_each_inner!((22, GPIO22(_1 => VSPIWP) (_1 => VSPIWP _3 => U0RTS _5 => - EMAC_TXD1) ([Input] [Output]))); _for_each_inner!((23, GPIO23(_1 => VSPID) (_1 => - VSPID _3 => HS1_STROBE) ([Input] [Output]))); _for_each_inner!((25, GPIO25(_5 => - EMAC_RXD0) () ([Input] [Output]))); _for_each_inner!((26, GPIO26(_5 => EMAC_RXD1) - () ([Input] [Output]))); _for_each_inner!((27, GPIO27(_5 => EMAC_RX_DV) () - ([Input] [Output]))); _for_each_inner!((32, GPIO32() () ([Input] [Output]))); + SDIO_DATA1 _1 => SPID _3 => HS1_DATA1 _4 => U2CTS) (_0 => SDIO_DATA1 _1 => SPID + _3 => HS1_DATA1) ([Input] [Output]))); _for_each_inner!((9, GPIO9(_0 => + SDIO_DATA2 _1 => SPIHD _3 => HS1_DATA2 _4 => U1RXD) (_0 => SDIO_DATA2 _1 => SPIHD + _3 => HS1_DATA2) ([Input] [Output]))); _for_each_inner!((10, GPIO10(_0 => + SDIO_DATA3 _1 => SPIWP _3 => HS1_DATA3) (_0 => SDIO_DATA3 _1 => SPIWP _3 => + HS1_DATA3 _4 => U1TXD) ([Input] [Output]))); _for_each_inner!((11, GPIO11(_0 => + SDIO_CMD _1 => SPICS0) (_0 => SDIO_CMD _1 => SPICS0 _3 => HS1_CMD _4 => U1RTS) + ([Input] [Output]))); _for_each_inner!((12, GPIO12(_0 => MTDI _1 => HSPIQ _3 => + HS2_DATA2 _4 => SDIO_DATA2) (_1 => HSPIQ _3 => HS2_DATA2 _4 => SDIO_DATA2 _5 => + EMAC_TXD3) ([Input] [Output]))); _for_each_inner!((13, GPIO13(_0 => MTCK _1 => + HSPID _3 => HS2_DATA3 _4 => SDIO_DATA3 _5 => EMAC_RX_ER) (_1 => HSPID _3 => + HS2_DATA3 _4 => SDIO_DATA3 _5 => EMAC_RX_ER) ([Input] [Output]))); + _for_each_inner!((14, GPIO14(_0 => MTMS _1 => HSPICLK) (_1 => HSPICLK _3 => + HS2_CLK _4 => SDIO_CLK _5 => EMAC_TXD2) ([Input] [Output]))); + _for_each_inner!((15, GPIO15(_1 => HSPICS0 _4 => SDIO_CMD _5 => EMAC_RXD3) (_0 => + MTDO _1 => HSPICS0 _3 => HS2_CMD _4 => SDIO_CMD) ([Input] [Output]))); + _for_each_inner!((16, GPIO16(_3 => HS1_DATA4 _4 => U2RXD) (_3 => HS1_DATA4 _5 => + EMAC_CLK_OUT) ([Input] [Output]))); _for_each_inner!((17, GPIO17(_3 => HS1_DATA5) + (_3 => HS1_DATA5 _4 => U2TXD _5 => EMAC_CLK_180) ([Input] [Output]))); + _for_each_inner!((18, GPIO18(_1 => VSPICLK _3 => HS1_DATA7) (_1 => VSPICLK _3 => + HS1_DATA7) ([Input] [Output]))); _for_each_inner!((19, GPIO19(_1 => VSPIQ _3 => + U0CTS) (_1 => VSPIQ _5 => EMAC_TXD0) ([Input] [Output]))); _for_each_inner!((20, + GPIO20() () ([Input] [Output]))); _for_each_inner!((21, GPIO21(_1 => VSPIHD) (_1 + => VSPIHD _5 => EMAC_TX_EN) ([Input] [Output]))); _for_each_inner!((22, GPIO22(_1 + => VSPIWP) (_1 => VSPIWP _3 => U0RTS _5 => EMAC_TXD1) ([Input] [Output]))); + _for_each_inner!((23, GPIO23(_1 => VSPID) (_1 => VSPID _3 => HS1_STROBE) ([Input] + [Output]))); _for_each_inner!((25, GPIO25(_5 => EMAC_RXD0) () ([Input] + [Output]))); _for_each_inner!((26, GPIO26(_5 => EMAC_RXD1) () ([Input] + [Output]))); _for_each_inner!((27, GPIO27(_5 => EMAC_RX_DV) () ([Input] + [Output]))); _for_each_inner!((32, GPIO32() () ([Input] [Output]))); _for_each_inner!((33, GPIO33() () ([Input] [Output]))); _for_each_inner!((34, GPIO34() () ([Input] []))); _for_each_inner!((35, GPIO35() () ([Input] []))); _for_each_inner!((36, GPIO36() () ([Input] []))); _for_each_inner!((37, GPIO37() @@ -578,29 +579,30 @@ macro_rules! for_each_gpio { _for_each_inner!((39, GPIO39() () ([Input] []))); _for_each_inner!((all(0, GPIO0(_5 => EMAC_TX_CLK) (_1 => CLK_OUT1 _5 => EMAC_TX_CLK) ([Input] [Output])), (1, GPIO1(_5 => EMAC_RXD2) (_0 => U0TXD _1 => CLK_OUT3) ([Input] [Output])), (2, - GPIO2(_1 => HSPIWP _3 => HS2_DATA0 _4 => SD_DATA0) (_1 => HSPIWP _3 => HS2_DATA0 - _4 => SD_DATA0) ([Input] [Output])), (3, GPIO3(_0 => U0RXD) (_1 => CLK_OUT2) - ([Input] [Output])), (4, GPIO4(_1 => HSPIHD _3 => HS2_DATA1 _4 => SD_DATA1 _5 => - EMAC_TX_ER) (_1 => HSPIHD _3 => HS2_DATA1 _4 => SD_DATA1 _5 => EMAC_TX_ER) - ([Input] [Output])), (5, GPIO5(_1 => VSPICS0 _3 => HS1_DATA6 _5 => EMAC_RX_CLK) - (_1 => VSPICS0 _3 => HS1_DATA6) ([Input] [Output])), (6, GPIO6(_1 => SPICLK _4 => - U1CTS) (_0 => SD_CLK _1 => SPICLK _3 => HS1_CLK) ([Input] [Output])), (7, - GPIO7(_0 => SD_DATA0 _1 => SPIQ _3 => HS1_DATA0) (_0 => SD_DATA0 _1 => SPIQ _3 => - HS1_DATA0 _4 => U2RTS) ([Input] [Output])), (8, GPIO8(_0 => SD_DATA1 _1 => SPID - _3 => HS1_DATA1 _4 => U2CTS) (_0 => SD_DATA1 _1 => SPID _3 => HS1_DATA1) ([Input] - [Output])), (9, GPIO9(_0 => SD_DATA2 _1 => SPIHD _3 => HS1_DATA2 _4 => U1RXD) (_0 - => SD_DATA2 _1 => SPIHD _3 => HS1_DATA2) ([Input] [Output])), (10, GPIO10(_0 => - SD_DATA3 _1 => SPIWP _3 => HS1_DATA3) (_0 => SD_DATA3 _1 => SPIWP _3 => HS1_DATA3 - _4 => U1TXD) ([Input] [Output])), (11, GPIO11(_0 => SD_CMD _1 => SPICS0) (_0 => - SD_CMD _1 => SPICS0 _3 => HS1_CMD _4 => U1RTS) ([Input] [Output])), (12, - GPIO12(_0 => MTDI _1 => HSPIQ _3 => HS2_DATA2 _4 => SD_DATA2) (_1 => HSPIQ _3 => - HS2_DATA2 _4 => SD_DATA2 _5 => EMAC_TXD3) ([Input] [Output])), (13, GPIO13(_0 => - MTCK _1 => HSPID _3 => HS2_DATA3 _4 => SD_DATA3 _5 => EMAC_RX_ER) (_1 => HSPID _3 - => HS2_DATA3 _4 => SD_DATA3 _5 => EMAC_RX_ER) ([Input] [Output])), (14, GPIO14(_0 - => MTMS _1 => HSPICLK) (_1 => HSPICLK _3 => HS2_CLK _4 => SD_CLK _5 => EMAC_TXD2) - ([Input] [Output])), (15, GPIO15(_1 => HSPICS0 _4 => SD_CMD _5 => EMAC_RXD3) (_0 - => MTDO _1 => HSPICS0 _3 => HS2_CMD _4 => SD_CMD) ([Input] [Output])), (16, - GPIO16(_3 => HS1_DATA4 _4 => U2RXD) (_3 => HS1_DATA4 _5 => EMAC_CLK_OUT) ([Input] + GPIO2(_1 => HSPIWP _3 => HS2_DATA0 _4 => SDIO_DATA0) (_1 => HSPIWP _3 => + HS2_DATA0 _4 => SDIO_DATA0) ([Input] [Output])), (3, GPIO3(_0 => U0RXD) (_1 => + CLK_OUT2) ([Input] [Output])), (4, GPIO4(_1 => HSPIHD _3 => HS2_DATA1 _4 => + SDIO_DATA1 _5 => EMAC_TX_ER) (_1 => HSPIHD _3 => HS2_DATA1 _4 => SDIO_DATA1 _5 => + EMAC_TX_ER) ([Input] [Output])), (5, GPIO5(_1 => VSPICS0 _3 => HS1_DATA6 _5 => + EMAC_RX_CLK) (_1 => VSPICS0 _3 => HS1_DATA6) ([Input] [Output])), (6, GPIO6(_1 => + SPICLK _4 => U1CTS) (_0 => SDIO_CLK _1 => SPICLK _3 => HS1_CLK) ([Input] + [Output])), (7, GPIO7(_0 => SDIO_DATA0 _1 => SPIQ _3 => HS1_DATA0) (_0 => + SDIO_DATA0 _1 => SPIQ _3 => HS1_DATA0 _4 => U2RTS) ([Input] [Output])), (8, + GPIO8(_0 => SDIO_DATA1 _1 => SPID _3 => HS1_DATA1 _4 => U2CTS) (_0 => SDIO_DATA1 + _1 => SPID _3 => HS1_DATA1) ([Input] [Output])), (9, GPIO9(_0 => SDIO_DATA2 _1 => + SPIHD _3 => HS1_DATA2 _4 => U1RXD) (_0 => SDIO_DATA2 _1 => SPIHD _3 => HS1_DATA2) + ([Input] [Output])), (10, GPIO10(_0 => SDIO_DATA3 _1 => SPIWP _3 => HS1_DATA3) + (_0 => SDIO_DATA3 _1 => SPIWP _3 => HS1_DATA3 _4 => U1TXD) ([Input] [Output])), + (11, GPIO11(_0 => SDIO_CMD _1 => SPICS0) (_0 => SDIO_CMD _1 => SPICS0 _3 => + HS1_CMD _4 => U1RTS) ([Input] [Output])), (12, GPIO12(_0 => MTDI _1 => HSPIQ _3 + => HS2_DATA2 _4 => SDIO_DATA2) (_1 => HSPIQ _3 => HS2_DATA2 _4 => SDIO_DATA2 _5 + => EMAC_TXD3) ([Input] [Output])), (13, GPIO13(_0 => MTCK _1 => HSPID _3 => + HS2_DATA3 _4 => SDIO_DATA3 _5 => EMAC_RX_ER) (_1 => HSPID _3 => HS2_DATA3 _4 => + SDIO_DATA3 _5 => EMAC_RX_ER) ([Input] [Output])), (14, GPIO14(_0 => MTMS _1 => + HSPICLK) (_1 => HSPICLK _3 => HS2_CLK _4 => SDIO_CLK _5 => EMAC_TXD2) ([Input] + [Output])), (15, GPIO15(_1 => HSPICS0 _4 => SDIO_CMD _5 => EMAC_RXD3) (_0 => MTDO + _1 => HSPICS0 _3 => HS2_CMD _4 => SDIO_CMD) ([Input] [Output])), (16, GPIO16(_3 + => HS1_DATA4 _4 => U2RXD) (_3 => HS1_DATA4 _5 => EMAC_CLK_OUT) ([Input] [Output])), (17, GPIO17(_3 => HS1_DATA5) (_3 => HS1_DATA5 _4 => U2TXD _5 => EMAC_CLK_180) ([Input] [Output])), (18, GPIO18(_1 => VSPICLK _3 => HS1_DATA7) (_1 => VSPICLK _3 => HS1_DATA7) ([Input] [Output])), (19, GPIO19(_1 => VSPIQ _3 => @@ -957,11 +959,11 @@ macro_rules! define_io_mux_signals { PCMFSYNC = 204, PCMCLK = 205, PCMDIN = 206, - SD_CMD, - SD_DATA0, - SD_DATA1, - SD_DATA2, - SD_DATA3, + SDIO_CMD, + SDIO_DATA0, + SDIO_DATA1, + SDIO_DATA2, + SDIO_DATA3, HS1_DATA0, HS1_DATA1, HS1_DATA2, @@ -1167,12 +1169,12 @@ macro_rules! define_io_mux_signals { CLK_OUT1, CLK_OUT2, CLK_OUT3, - SD_CLK, - SD_CMD, - SD_DATA0, - SD_DATA1, - SD_DATA2, - SD_DATA3, + SDIO_CLK, + SDIO_CMD, + SDIO_DATA0, + SDIO_DATA1, + SDIO_DATA2, + SDIO_DATA3, HS1_CLK, HS1_CMD, HS1_DATA0, diff --git a/esp-metadata-generated/src/_generated_esp32c6.rs b/esp-metadata-generated/src/_generated_esp32c6.rs index 401c85c7086..fae646e410d 100644 --- a/esp-metadata-generated/src/_generated_esp32c6.rs +++ b/esp-metadata-generated/src/_generated_esp32c6.rs @@ -503,30 +503,31 @@ macro_rules! for_each_peripheral { bind_peri_interrupt, enable_peri_interrupt, disable_peri_interrupt }) (unstable))); _for_each_inner!((SHA <= SHA(SHA : { bind_peri_interrupt, enable_peri_interrupt, disable_peri_interrupt }) (unstable))); - _for_each_inner!((SLCHOST <= SLCHOST() (unstable))); _for_each_inner!((ETM <= - SOC_ETM() (unstable))); _for_each_inner!((SPI0 <= SPI0() (unstable))); - _for_each_inner!((SPI1 <= SPI1() (unstable))); _for_each_inner!((SPI2 <= - SPI2(SPI2 : { bind_peri_interrupt, enable_peri_interrupt, disable_peri_interrupt - }))); _for_each_inner!((SYSTEM <= PCR() (unstable))); _for_each_inner!((SYSTIMER - <= SYSTIMER() (unstable))); _for_each_inner!((TEE <= TEE() (unstable))); - _for_each_inner!((TIMG0 <= TIMG0() (unstable))); _for_each_inner!((TIMG1 <= - TIMG1() (unstable))); _for_each_inner!((TRACE0 <= TRACE() (unstable))); - _for_each_inner!((TWAI0 <= TWAI0() (unstable))); _for_each_inner!((TWAI1 <= - TWAI1() (unstable))); _for_each_inner!((UART0 <= UART0(UART0 : { - bind_peri_interrupt, enable_peri_interrupt, disable_peri_interrupt }))); - _for_each_inner!((UART1 <= UART1(UART1 : { bind_peri_interrupt, - enable_peri_interrupt, disable_peri_interrupt }))); _for_each_inner!((UHCI0 <= - UHCI0() (unstable))); _for_each_inner!((USB_DEVICE <= USB_DEVICE(USB_DEVICE : { - bind_peri_interrupt, enable_peri_interrupt, disable_peri_interrupt }) - (unstable))); _for_each_inner!((DMA_CH0 <= virtual() (unstable))); - _for_each_inner!((DMA_CH1 <= virtual() (unstable))); _for_each_inner!((DMA_CH2 <= - virtual() (unstable))); _for_each_inner!((ADC1 <= virtual() (unstable))); - _for_each_inner!((BT <= virtual() (unstable))); _for_each_inner!((LP_CORE <= - virtual() (unstable))); _for_each_inner!((SW_INTERRUPT <= virtual() (unstable))); - _for_each_inner!((TSENS <= virtual() (unstable))); _for_each_inner!((WIFI <= - virtual() (unstable))); _for_each_inner!((MEM2MEM1 <= virtual() (unstable))); - _for_each_inner!((MEM2MEM4 <= virtual() (unstable))); _for_each_inner!((MEM2MEM5 - <= virtual() (unstable))); _for_each_inner!((MEM2MEM10 <= virtual() (unstable))); + _for_each_inner!((SLC <= SLC() (unstable))); _for_each_inner!((SLCHOST <= + SLCHOST() (unstable))); _for_each_inner!((ETM <= SOC_ETM() (unstable))); + _for_each_inner!((SPI0 <= SPI0() (unstable))); _for_each_inner!((SPI1 <= SPI1() + (unstable))); _for_each_inner!((SPI2 <= SPI2(SPI2 : { bind_peri_interrupt, + enable_peri_interrupt, disable_peri_interrupt }))); _for_each_inner!((SYSTEM <= + PCR() (unstable))); _for_each_inner!((SYSTIMER <= SYSTIMER() (unstable))); + _for_each_inner!((TEE <= TEE() (unstable))); _for_each_inner!((TIMG0 <= TIMG0() + (unstable))); _for_each_inner!((TIMG1 <= TIMG1() (unstable))); + _for_each_inner!((TRACE0 <= TRACE() (unstable))); _for_each_inner!((TWAI0 <= + TWAI0() (unstable))); _for_each_inner!((TWAI1 <= TWAI1() (unstable))); + _for_each_inner!((UART0 <= UART0(UART0 : { bind_peri_interrupt, + enable_peri_interrupt, disable_peri_interrupt }))); _for_each_inner!((UART1 <= + UART1(UART1 : { bind_peri_interrupt, enable_peri_interrupt, + disable_peri_interrupt }))); _for_each_inner!((UHCI0 <= UHCI0() (unstable))); + _for_each_inner!((USB_DEVICE <= USB_DEVICE(USB_DEVICE : { bind_peri_interrupt, + enable_peri_interrupt, disable_peri_interrupt }) (unstable))); + _for_each_inner!((DMA_CH0 <= virtual() (unstable))); _for_each_inner!((DMA_CH1 <= + virtual() (unstable))); _for_each_inner!((DMA_CH2 <= virtual() (unstable))); + _for_each_inner!((ADC1 <= virtual() (unstable))); _for_each_inner!((BT <= + virtual() (unstable))); _for_each_inner!((LP_CORE <= virtual() (unstable))); + _for_each_inner!((SW_INTERRUPT <= virtual() (unstable))); _for_each_inner!((TSENS + <= virtual() (unstable))); _for_each_inner!((WIFI <= virtual() (unstable))); + _for_each_inner!((MEM2MEM1 <= virtual() (unstable))); _for_each_inner!((MEM2MEM4 + <= virtual() (unstable))); _for_each_inner!((MEM2MEM5 <= virtual() (unstable))); + _for_each_inner!((MEM2MEM10 <= virtual() (unstable))); _for_each_inner!((MEM2MEM11 <= virtual() (unstable))); _for_each_inner!((MEM2MEM12 <= virtual() (unstable))); _for_each_inner!((MEM2MEM13 <= virtual() (unstable))); @@ -570,26 +571,26 @@ macro_rules! for_each_peripheral { <= RNG() (unstable)), (RSA <= RSA(RSA : { bind_peri_interrupt, enable_peri_interrupt, disable_peri_interrupt }) (unstable)), (SHA <= SHA(SHA : { bind_peri_interrupt, enable_peri_interrupt, disable_peri_interrupt }) - (unstable)), (SLCHOST <= SLCHOST() (unstable)), (ETM <= SOC_ETM() (unstable)), - (SPI0 <= SPI0() (unstable)), (SPI1 <= SPI1() (unstable)), (SPI2 <= SPI2(SPI2 : { - bind_peri_interrupt, enable_peri_interrupt, disable_peri_interrupt })), (SYSTEM - <= PCR() (unstable)), (SYSTIMER <= SYSTIMER() (unstable)), (TEE <= TEE() - (unstable)), (TIMG0 <= TIMG0() (unstable)), (TIMG1 <= TIMG1() (unstable)), - (TRACE0 <= TRACE() (unstable)), (TWAI0 <= TWAI0() (unstable)), (TWAI1 <= TWAI1() - (unstable)), (UART0 <= UART0(UART0 : { bind_peri_interrupt, - enable_peri_interrupt, disable_peri_interrupt })), (UART1 <= UART1(UART1 : { - bind_peri_interrupt, enable_peri_interrupt, disable_peri_interrupt })), (UHCI0 <= - UHCI0() (unstable)), (USB_DEVICE <= USB_DEVICE(USB_DEVICE : { - bind_peri_interrupt, enable_peri_interrupt, disable_peri_interrupt }) - (unstable)), (DMA_CH0 <= virtual() (unstable)), (DMA_CH1 <= virtual() - (unstable)), (DMA_CH2 <= virtual() (unstable)), (ADC1 <= virtual() (unstable)), - (BT <= virtual() (unstable)), (LP_CORE <= virtual() (unstable)), (SW_INTERRUPT <= - virtual() (unstable)), (TSENS <= virtual() (unstable)), (WIFI <= virtual() - (unstable)), (MEM2MEM1 <= virtual() (unstable)), (MEM2MEM4 <= virtual() - (unstable)), (MEM2MEM5 <= virtual() (unstable)), (MEM2MEM10 <= virtual() - (unstable)), (MEM2MEM11 <= virtual() (unstable)), (MEM2MEM12 <= virtual() - (unstable)), (MEM2MEM13 <= virtual() (unstable)), (MEM2MEM14 <= virtual() - (unstable)), (MEM2MEM15 <= virtual() (unstable)))); + (unstable)), (SLC <= SLC() (unstable)), (SLCHOST <= SLCHOST() (unstable)), (ETM + <= SOC_ETM() (unstable)), (SPI0 <= SPI0() (unstable)), (SPI1 <= SPI1() + (unstable)), (SPI2 <= SPI2(SPI2 : { bind_peri_interrupt, enable_peri_interrupt, + disable_peri_interrupt })), (SYSTEM <= PCR() (unstable)), (SYSTIMER <= SYSTIMER() + (unstable)), (TEE <= TEE() (unstable)), (TIMG0 <= TIMG0() (unstable)), (TIMG1 <= + TIMG1() (unstable)), (TRACE0 <= TRACE() (unstable)), (TWAI0 <= TWAI0() + (unstable)), (TWAI1 <= TWAI1() (unstable)), (UART0 <= UART0(UART0 : { + bind_peri_interrupt, enable_peri_interrupt, disable_peri_interrupt })), (UART1 <= + UART1(UART1 : { bind_peri_interrupt, enable_peri_interrupt, + disable_peri_interrupt })), (UHCI0 <= UHCI0() (unstable)), (USB_DEVICE <= + USB_DEVICE(USB_DEVICE : { bind_peri_interrupt, enable_peri_interrupt, + disable_peri_interrupt }) (unstable)), (DMA_CH0 <= virtual() (unstable)), + (DMA_CH1 <= virtual() (unstable)), (DMA_CH2 <= virtual() (unstable)), (ADC1 <= + virtual() (unstable)), (BT <= virtual() (unstable)), (LP_CORE <= virtual() + (unstable)), (SW_INTERRUPT <= virtual() (unstable)), (TSENS <= virtual() + (unstable)), (WIFI <= virtual() (unstable)), (MEM2MEM1 <= virtual() (unstable)), + (MEM2MEM4 <= virtual() (unstable)), (MEM2MEM5 <= virtual() (unstable)), + (MEM2MEM10 <= virtual() (unstable)), (MEM2MEM11 <= virtual() (unstable)), + (MEM2MEM12 <= virtual() (unstable)), (MEM2MEM13 <= virtual() (unstable)), + (MEM2MEM14 <= virtual() (unstable)), (MEM2MEM15 <= virtual() (unstable)))); }; } /// This macro can be used to generate code for each `GPIOn` instance. diff --git a/esp-metadata/devices/esp32.toml b/esp-metadata/devices/esp32.toml index 3c9290e5bd7..4e2b03e0a13 100644 --- a/esp-metadata/devices/esp32.toml +++ b/esp-metadata/devices/esp32.toml @@ -140,20 +140,20 @@ remap_iomux_pin_registers = true pins = [ { pin = 0, functions = { 1 = "CLK_OUT1", 5 = "EMAC_TX_CLK" }, analog = { 1 = "ADC2_CH1", 2 = "TOUCH1" }, rtc = { 0 = "RTC_GPIO11", 1 = "SAR_I2C_SDA" } }, { pin = 1, functions = { 0 = "U0TXD", 1 = "CLK_OUT3", 5 = "EMAC_RXD2" } }, - { pin = 2, functions = { 1 = "HSPIWP", 3 = "HS2_DATA0", 4 = "SD_DATA0" }, analog = { 1 = "ADC2_CH2", 2 = "TOUCH2" }, rtc = { 0 = "RTC_GPIO12", 1 = "SAR_I2C_SCL" } }, + { pin = 2, functions = { 1 = "HSPIWP", 3 = "HS2_DATA0", 4 = "SDIO_DATA0" }, analog = { 1 = "ADC2_CH2", 2 = "TOUCH2" }, rtc = { 0 = "RTC_GPIO12", 1 = "SAR_I2C_SCL" } }, { pin = 3, functions = { 0 = "U0RXD", 1 = "CLK_OUT2" } }, - { pin = 4, functions = { 1 = "HSPIHD", 3 = "HS2_DATA1", 4 = "SD_DATA1", 5 = "EMAC_TX_ER" }, analog = { 1 = "ADC2_CH0", 2 = "TOUCH0" }, rtc = { 0 = "RTC_GPIO10", 1 = "SAR_I2C_SCL" } }, + { pin = 4, functions = { 1 = "HSPIHD", 3 = "HS2_DATA1", 4 = "SDIO_DATA1", 5 = "EMAC_TX_ER" }, analog = { 1 = "ADC2_CH0", 2 = "TOUCH0" }, rtc = { 0 = "RTC_GPIO10", 1 = "SAR_I2C_SCL" } }, { pin = 5, functions = { 1 = "VSPICS0", 3 = "HS1_DATA6", 5 = "EMAC_RX_CLK" } }, - { pin = 6, functions = { 0 = "SD_CLK", 1 = "SPICLK", 3 = "HS1_CLK", 4 = "U1CTS" } }, - { pin = 7, functions = { 0 = "SD_DATA0", 1 = "SPIQ", 3 = "HS1_DATA0", 4 = "U2RTS" } }, - { pin = 8, functions = { 0 = "SD_DATA1", 1 = "SPID", 3 = "HS1_DATA1", 4 = "U2CTS" } }, - { pin = 9, functions = { 0 = "SD_DATA2", 1 = "SPIHD", 3 = "HS1_DATA2", 4 = "U1RXD" } }, - { pin = 10, functions = { 0 = "SD_DATA3", 1 = "SPIWP", 3 = "HS1_DATA3", 4 = "U1TXD" } }, - { pin = 11, functions = { 0 = "SD_CMD", 1 = "SPICS0", 3 = "HS1_CMD", 4 = "U1RTS" } }, - { pin = 12, functions = { 0 = "MTDI", 1 = "HSPIQ", 3 = "HS2_DATA2", 4 = "SD_DATA2", 5 = "EMAC_TXD3" }, analog = { 1 = "ADC2_CH5", 2 = "TOUCH5" }, rtc = { 0 = "RTC_GPIO15" } }, - { pin = 13, functions = { 0 = "MTCK", 1 = "HSPID", 3 = "HS2_DATA3", 4 = "SD_DATA3", 5 = "EMAC_RX_ER" }, analog = { 1 = "ADC2_CH4", 2 = "TOUCH4" }, rtc = { 0 = "RTC_GPIO14" } }, - { pin = 14, functions = { 0 = "MTMS", 1 = "HSPICLK", 3 = "HS2_CLK", 4 = "SD_CLK", 5 = "EMAC_TXD2" }, analog = { 1 = "ADC2_CH6", 2 = "TOUCH6" }, rtc = { 0 = "RTC_GPIO16" } }, - { pin = 15, functions = { 0 = "MTDO", 1 = "HSPICS0", 3 = "HS2_CMD", 4 = "SD_CMD", 5 = "EMAC_RXD3" }, analog = { 1 = "ADC2_CH3", 2 = "TOUCH3" }, rtc = { 0 = "RTC_GPIO13", 1 = "SAR_I2C_SDA" } }, + { pin = 6, functions = { 0 = "SDIO_CLK", 1 = "SPICLK", 3 = "HS1_CLK", 4 = "U1CTS" } }, + { pin = 7, functions = { 0 = "SDIO_DATA0", 1 = "SPIQ", 3 = "HS1_DATA0", 4 = "U2RTS" } }, + { pin = 8, functions = { 0 = "SDIO_DATA1", 1 = "SPID", 3 = "HS1_DATA1", 4 = "U2CTS" } }, + { pin = 9, functions = { 0 = "SDIO_DATA2", 1 = "SPIHD", 3 = "HS1_DATA2", 4 = "U1RXD" } }, + { pin = 10, functions = { 0 = "SDIO_DATA3", 1 = "SPIWP", 3 = "HS1_DATA3", 4 = "U1TXD" } }, + { pin = 11, functions = { 0 = "SDIO_CMD", 1 = "SPICS0", 3 = "HS1_CMD", 4 = "U1RTS" } }, + { pin = 12, functions = { 0 = "MTDI", 1 = "HSPIQ", 3 = "HS2_DATA2", 4 = "SDIO_DATA2", 5 = "EMAC_TXD3" }, analog = { 1 = "ADC2_CH5", 2 = "TOUCH5" }, rtc = { 0 = "RTC_GPIO15" } }, + { pin = 13, functions = { 0 = "MTCK", 1 = "HSPID", 3 = "HS2_DATA3", 4 = "SDIO_DATA3", 5 = "EMAC_RX_ER" }, analog = { 1 = "ADC2_CH4", 2 = "TOUCH4" }, rtc = { 0 = "RTC_GPIO14" } }, + { pin = 14, functions = { 0 = "MTMS", 1 = "HSPICLK", 3 = "HS2_CLK", 4 = "SDIO_CLK", 5 = "EMAC_TXD2" }, analog = { 1 = "ADC2_CH6", 2 = "TOUCH6" }, rtc = { 0 = "RTC_GPIO16" } }, + { pin = 15, functions = { 0 = "MTDO", 1 = "HSPICS0", 3 = "HS2_CMD", 4 = "SDIO_CMD", 5 = "EMAC_RXD3" }, analog = { 1 = "ADC2_CH3", 2 = "TOUCH3" }, rtc = { 0 = "RTC_GPIO13", 1 = "SAR_I2C_SDA" } }, { pin = 16, functions = { 3 = "HS1_DATA4", 4 = "U2RXD", 5 = "EMAC_CLK_OUT" }, analog = {}, rtc = {} }, { pin = 17, functions = { 3 = "HS1_DATA5", 4 = "U2TXD", 5 = "EMAC_CLK_180" }, analog = {}, rtc = {} }, { pin = 18, functions = { 1 = "VSPICLK", 3 = "HS1_DATA7" }, analog = {}, rtc = {} }, @@ -329,11 +329,11 @@ input_signals = [ { name = "PCMCLK", id = 205 }, { name = "PCMDIN", id = 206 }, - { name = "SD_CMD" }, - { name = "SD_DATA0" }, - { name = "SD_DATA1" }, - { name = "SD_DATA2" }, - { name = "SD_DATA3" }, + { name = "SDIO_CMD" }, + { name = "SDIO_DATA0" }, + { name = "SDIO_DATA1" }, + { name = "SDIO_DATA2" }, + { name = "SDIO_DATA3" }, { name = "HS1_DATA0" }, { name = "HS1_DATA1" }, { name = "HS1_DATA2" }, @@ -538,12 +538,12 @@ output_signals = [ { name = "CLK_OUT1" }, { name = "CLK_OUT2" }, { name = "CLK_OUT3" }, - { name = "SD_CLK" }, - { name = "SD_CMD" }, - { name = "SD_DATA0" }, - { name = "SD_DATA1" }, - { name = "SD_DATA2" }, - { name = "SD_DATA3" }, + { name = "SDIO_CLK" }, + { name = "SDIO_CMD" }, + { name = "SDIO_DATA0" }, + { name = "SDIO_DATA1" }, + { name = "SDIO_DATA2" }, + { name = "SDIO_DATA3" }, { name = "HS1_CLK" }, { name = "HS1_CMD" }, { name = "HS1_DATA0" }, diff --git a/esp-metadata/devices/esp32c6.toml b/esp-metadata/devices/esp32c6.toml index eb2d29e74f0..a00f1f0f289 100644 --- a/esp-metadata/devices/esp32c6.toml +++ b/esp-metadata/devices/esp32c6.toml @@ -65,6 +65,7 @@ peripherals = [ { name = "RNG" }, { name = "RSA", interrupts = { peri = "RSA" } }, { name = "SHA", interrupts = { peri = "SHA" } }, + { name = "SLC" }, { name = "SLCHOST" }, { name = "ETM", pac = "SOC_ETM" }, { name = "SPI0" }, diff --git a/hil-test/Cargo.toml b/hil-test/Cargo.toml index 9191a781f05..7ff1abf4246 100644 --- a/hil-test/Cargo.toml +++ b/hil-test/Cargo.toml @@ -223,6 +223,10 @@ required-features = ["esp-radio", "esp-alloc"] name = "otadata" harness = false +[[test]] +name = "sdio" +harness = false + [dependencies] allocator-api2 = { version = "0.3.0", default-features = false, features = ["alloc"] } cfg-if = "1.0.0" diff --git a/hil-test/tests/sdio.rs b/hil-test/tests/sdio.rs new file mode 100644 index 00000000000..28936bb5a21 --- /dev/null +++ b/hil-test/tests/sdio.rs @@ -0,0 +1,299 @@ +//! SDIO Tests +//! +//! # Pin Connections +//! +//! On the ESP32-C6, there is no SDIO host implemented in hardware, so one needs to be mocked using +//! a SPI peripheral. +//! +//! The following physical pin connections are needed on a ESP32C6-DevKitC-1: +//! +//! - SPI CLK (GPIO6, pin 5) => SDIO CLK (GPIO19, pin 19) +//! - SPI CS0 (GPIO16, pin 2) => SDIO CS (GPIO23, pin 23) +//! - SPI MOSI (GPIO7, pin 6) => SDIO MOSI (GPIO18, pin 18) +//! - SPI MISO (GPIO2, pin 12) => SDIO MISO (GPIO20, pin 20) + +//% CHIPS: esp32 esp32c6 +//% FEATURES: unstable + +#![no_std] +#![no_main] + +#[cfg(feature = "esp32c6")] +use esp_hal::interrupt::{self, IsrCallback, Priority}; +#[cfg(feature = "esp32c6")] +use esp_hal::peripherals::Interrupt; +use esp_hal::{ + Blocking, + dma::{DescriptorFlagFields, Owner}, + sdio::{ + Config, + Mode, + Pins, + Sdio, + command::Cmd52, + dma::{ + AtomicBuffer, + AtomicDmaDescriptor, + AtomicDmaDescriptors, + DmaDescriptor, + DmaDescriptorFlags, + }, + response::spi::R5, + }, + spi::master as spi, + time::Rate, +}; +use hil_test as _; + +esp_bootloader_esp_idf::esp_app_desc!(); + +// Use 10KB of data to fit inside SRAM +// One RX + one TX buffer +const DATA_SIZE: usize = 1024 * 5; +// Use 6KB of descriptors to fit inside SRAM +const DESC_SIZE: usize = 1024 * 6; +// Total number of DMA descriptors. +const DESC_NUM: usize = DESC_SIZE / core::mem::size_of::() / 2; +// Represents the set of Receive DMA descriptors. +static RX_DESCRIPTORS: AtomicDmaDescriptors = AtomicDmaDescriptors::new(); +// Represents the set of Transmit DMA descriptors. +static TX_DESCRIPTORS: AtomicDmaDescriptors = AtomicDmaDescriptors::new(); +// Represents the Receive DMA buffer. +static RX_BUFFER: AtomicBuffer = AtomicBuffer::new(); +// Represents the Transmit DMA buffer. +static TX_BUFFER: AtomicBuffer = AtomicBuffer::new(); + +struct Context { + sdio: Sdio<'static>, + spi: spi::Spi<'static, Blocking>, +} + +#[cfg(test)] +#[embedded_test::tests(default_timeout = 3)] +mod tests { + use super::*; + + // Represents one block length. + const TEST_BLOCK_LEN: usize = 512; + // Represents one packet length with multiple blocks. + // const TEST_PACKET_LEN: usize = 2048; + + #[init] + fn init() -> Context { + let peripherals = esp_hal::init(esp_hal::Config::default()); + + cfg_if::cfg_if! { + if #[cfg(esp32)] { + // GPIO Slot 1 config + let pins = Pins::new( + Mode::Spi, + peripherals.GPIO6, // CLK/SCK + peripherals.GPIO11, // CMD/MOSI + peripherals.GPIO7, // DAT0/MISO + peripherals.GPIO8, // DAT1/IRQ + peripherals.GPIO9, // DAT2 + peripherals.GPIO10, // DAT3/#CS + ); + + // GPIO Slot 2 config + //let pins = Pins::new( + // Mode::Spi, + // peripherals.GPIO14, // CLK/SCK + // peripherals.GPIO15, // CMD/MOSI + // peripherals.GPIO2, // DAT0/MISO + // peripherals.GPIO4, // DAT1/IRQ + // peripherals.GPIO12, // DAT2 + // peripherals.GPIO13, // DAT3/#CS + //); + + // Create SPI master for mock SDIO host + // HSPI config + let spi = spi::Spi::new( + peripherals.SPI2, + spi::Config::default().with_frequency(Rate::from_mhz(10)), + ) + .unwrap() + .with_sck(peripherals.GPIO14) + .with_mosi(peripherals.GPIO13) + .with_miso(peripherals.GPIO12) + .with_cs(peripherals.GPIO15); + + // Create SPI master for mock SDIO host + // VSPI config + //let spi = spi::Spi::new( + // peripherals.SPI3, + // spi::Config::default().with_frequency(Rate::from_mhz(10)), + //) + //.unwrap() + //.with_sck(peripherals.GPIO18) + //.with_mosi(peripherals.GPIO23) + //.with_miso(peripherals.GPIO19) + //.with_cs(peripherals.GPIO5); + } else if #[cfg(esp32c6)] { + let pins = Pins::new( + Mode::Spi, + peripherals.GPIO19, // CLK/SCLK + peripherals.GPIO18, // CMD/MOSI + peripherals.GPIO20, // DAT0/MISO + peripherals.GPIO21, // DAT1/IRQ + peripherals.GPIO22, // DAT2 + peripherals.GPIO23, // DAT3/#CS + ); + + // Create SPI master for mock SDIO host + let spi = spi::Spi::new( + peripherals.SPI2, + spi::Config::default().with_frequency(Rate::from_mhz(10)), + ) + .unwrap() + .with_sck(peripherals.GPIO6) + .with_mosi(peripherals.GPIO7) + .with_miso(peripherals.GPIO2) + .with_cs(peripherals.GPIO16); + } else { + panic!("unsupported platform"); + } + } + + let config = Config::new(); + + let sdio = Sdio::new( + peripherals.SLC, + peripherals.SLCHOST, + peripherals.HINF, + pins, + config, + ); + + Context { sdio, spi } + } + + #[test] + fn test_dma_rx_descriptors(mut ctx: Context) { + // indicate a transfer of a single SDIO block + let mut flags = DmaDescriptorFlags::new(); + flags.set_owner(Owner::Dma); + flags.set_suc_eof(true); + flags.set_size(TEST_BLOCK_LEN); + flags.set_len(TEST_BLOCK_LEN); + + let rx_descriptor = &RX_DESCRIPTORS[0]; + + let test_buffer: [u8; TEST_BLOCK_LEN] = core::array::from_fn(|i| i as u8); + RX_BUFFER.write(&test_buffer); + + let buffer = unsafe { RX_BUFFER.as_ptr_mut() }; + rx_descriptor.update(DmaDescriptor { + flags, + buffer, + next: core::ptr::null_mut(), + }); + + // TODO: mock transfers to the SDIO host + let mut test_rx = [0u8; TEST_BLOCK_LEN]; + + ctx.spi + .read(&mut test_rx) + .expect("error reading from SDIO host"); + // assert_eq!(test_buffer, test_rx); + } + + #[test] + fn test_dma_tx_descriptors(mut ctx: Context) { + // indicate a transfer of a single SDIO block + let mut flags = DmaDescriptorFlags::new(); + flags.set_owner(Owner::Dma); + flags.set_suc_eof(true); + flags.set_size(TEST_BLOCK_LEN); + flags.set_len(TEST_BLOCK_LEN); + + let tx_descriptor = &TX_DESCRIPTORS[0]; + + let test_packet = Cmd52::new(); + + let mut test_buffer: [u8; TEST_BLOCK_LEN] = [0u8; TEST_BLOCK_LEN]; + test_buffer[..Cmd52::LEN].copy_from_slice(test_packet.into_bytes().as_ref()); + + let buffer = unsafe { TX_BUFFER.as_ptr_mut() }; + tx_descriptor.update(DmaDescriptor { + flags, + buffer, + next: core::ptr::null_mut(), + }); + + let _sdio = &ctx.sdio; + + enable_slc_interrupt(); + + // TODO: mock transfers from the SDIO host + ctx.spi + .write(&test_buffer) + .expect("error writing SDIO from host"); + + let mut res_buffer = [0u8; TEST_BLOCK_LEN]; + // expect R5 response from SDIO client + ctx.spi + .read(&mut res_buffer) + .expect("error reading SDIO from host"); + + // TODO: implement response types, and check for expected response + assert_ne!(res_buffer, [0u8; TEST_BLOCK_LEN]); + + let exp_res = R5::new().into_bytes(); + assert_eq!(&res_buffer[..R5::LEN], exp_res.as_ref()); + } +} + +#[cfg(feature = "esp32")] +fn enable_slc_interrupt() {} + +#[cfg(feature = "esp32")] +#[allow(unused)] +extern "C" fn slc_handler() { + // TODO: implement the handler +} + +#[cfg(feature = "esp32c6")] +fn enable_slc_interrupt() { + // enable the SLC0 interrupt, and set the handler + interrupt::enable(Interrupt::SLC0, Priority::Priority1).expect("error enabling SLC0 interrupt"); + unsafe { interrupt::bind_interrupt(Interrupt::SLC0, IsrCallback::new(slc0_handler)) }; +} + +#[cfg(feature = "esp32c6")] +extern "C" fn slc0_handler() { + // write data to the host + // configuring DMA info + // TODO: move this into the Sdio type + // let slc = unsafe { &*ctx.sdio.slc().register_block }; + // + // let buf_start = TX_BUFFER.address(); + // let buf_end = buf_start + (test_buffer.len() as u32); + // + // slc.slc1_tx_sharemem_start() + // .write(|w| unsafe { w.bits(buf_start) }); + // slc.slc1_tx_sharemem_end() + // .write(|w| unsafe { w.bits(buf_end) }); + // + // configure SLC interrupts + // slc.slc1int_ena1().modify(|_, w| { + // w.sdio_slc1_tx_dscr_err_int_ena1().set_bit(); + // w.sdio_slc1_tx_ovf_int_ena1().set_bit() + // }); + // TODO: match interrupt source to properly handle interrupts, e.g. send, receive, ... + let peripherals = esp_hal::init(esp_hal::Config::default()); + let slc = &*peripherals.SLC.register_block(); + + // configure SLC TX descriptors + slc.slc1tx_link_addr() + .write(|w| unsafe { w.bits(TX_DESCRIPTORS.address()) }); + slc.slc1tx_link() + .modify(|_, w| w.sdio_slc1_txlink_start().set_bit()); + + let mut dma_buf = [0u8; 512]; + TX_BUFFER.read(&mut dma_buf); + + let cmd = Cmd52::try_from_bytes(&dma_buf); + + assert!(cmd.is_ok()); +} diff --git a/qa-test/src/bin/sdio-spi-device.rs b/qa-test/src/bin/sdio-spi-device.rs new file mode 100644 index 00000000000..5550fcaffd1 --- /dev/null +++ b/qa-test/src/bin/sdio-spi-device.rs @@ -0,0 +1,95 @@ +#![no_std] +#![no_main] + +use esp_backtrace as _; +use esp_hal::sdio::{ + Config, + Mode, + Pins, + Sdio, + command::Cmd52, + response::spi::R5, +}; + +esp_bootloader_esp_idf::esp_app_desc!(); + +struct Context { + sdio: Sdio<'static>, +} + +impl Context { + /// Creates a new context for the SDIO SPI slave controller. + pub fn new() -> Self { + let peripherals = esp_hal::init(esp_hal::Config::default()); + + cfg_if::cfg_if! { + if #[cfg(feature = "esp32")] { + // GPIO Slot 1 config + let pins = Pins::new( + Mode::Spi, + peripherals.GPIO6, // CLK/SCK + peripherals.GPIO11, // CMD/MOSI + peripherals.GPIO7, // DAT0/MISO + peripherals.GPIO8, // DAT1/IRQ + peripherals.GPIO9, // DAT2 + peripherals.GPIO10, // DAT3/#CS + ); + + // GPIO Slot 2 config + //let pins = Pins::new( + // Mode::Spi, + // peripherals.GPIO14, // CLK/SCK + // peripherals.GPIO15, // CMD/MOSI + // peripherals.GPIO2, // DAT0/MISO + // peripherals.GPIO4, // DAT1/IRQ + // peripherals.GPIO12, // DAT2 + // peripherals.GPIO13, // DAT3/#CS + //); + } else if #[cfg(feature = "esp32c6")] { + let pins = Pins::new( + Mode::Spi, + peripherals.GPIO19, // CLK/SCLK + peripherals.GPIO18, // CMD/MOSI + peripherals.GPIO20, // DAT0/MISO + peripherals.GPIO21, // DAT1/IRQ + peripherals.GPIO22, // DAT2 + peripherals.GPIO23, // DAT3/#CS + ); + } else { + panic!("unsupported platform"); + } + } + + let config = Config::new(); + + let sdio = Sdio::new( + peripherals.SLC, + peripherals.SLCHOST, + peripherals.HINF, + pins, + config, + ); + + Self { sdio } + } +} + +#[esp_hal::main] +fn main() -> ! { + let mut ctx = Context::new(); + + loop { + let cmd_buf = ctx.sdio + .read_command_raw() + .expect("error writing SDIO from host"); + + let exp_packet = Cmd52::new(); + + assert_eq!(Cmd52::try_from_bytes(&cmd_buf), Ok(exp_packet)); + + let exp_res = R5::new().into_bytes(); + + ctx.sdio.write_response_raw(&exp_res) + .expect("error writing SDIO response to host"); + } +} diff --git a/qa-test/src/bin/sdio-spi-host.rs b/qa-test/src/bin/sdio-spi-host.rs new file mode 100644 index 00000000000..1f742a763b8 --- /dev/null +++ b/qa-test/src/bin/sdio-spi-host.rs @@ -0,0 +1,102 @@ +//! SDIO SPI mode master driver example. + +#![no_std] +#![no_main] + +use esp_backtrace as _; +use esp_hal::{ + Blocking, + sdio::{ + command::Cmd52, + response::spi::R5, + }, + spi::master as spi, + time::Rate, +}; + +esp_bootloader_esp_idf::esp_app_desc!(); + +// Represents one block length. +const BLOCK_LEN: usize = 512; + +struct Context { + spi: spi::Spi<'static, Blocking>, +} + +impl Context { + /// Creates a new context for the SDIO SPI master controller. + pub fn new() -> Self { + let peripherals = esp_hal::init(esp_hal::Config::default()); + + cfg_if::cfg_if! { + if #[cfg(feature = "esp32")] { + // Create SPI master for mock SDIO host + // HSPI config + let spi = spi::Spi::new( + peripherals.SPI2, + spi::Config::default().with_frequency(Rate::from_mhz(10)), + ) + .unwrap() + .with_sck(peripherals.GPIO14) + .with_mosi(peripherals.GPIO13) + .with_miso(peripherals.GPIO12) + .with_cs(peripherals.GPIO15); + + // Create SPI master for mock SDIO host + // VSPI config + //let spi = spi::Spi::new( + // peripherals.SPI3, + // spi::Config::default().with_frequency(Rate::from_mhz(10)), + //) + //.unwrap() + //.with_sck(peripherals.GPIO18) + //.with_mosi(peripherals.GPIO23) + //.with_miso(peripherals.GPIO19) + //.with_cs(peripherals.GPIO5); + } else if #[cfg(feature = "esp32c6")] { + // Create SPI master for mock SDIO host + let spi = spi::Spi::new( + peripherals.SPI2, + spi::Config::default().with_frequency(Rate::from_mhz(10)), + ) + .unwrap() + .with_sck(peripherals.GPIO6) + .with_mosi(peripherals.GPIO7) + .with_miso(peripherals.GPIO2) + .with_cs(peripherals.GPIO16); + } else { + panic!("unsupported platform"); + } + } + + Self { spi } + } +} + + +#[esp_hal::main] +fn main() -> ! { + let mut ctx = Context::new(); + + loop { + ctx.spi.write(&[0u8; 10]).expect("error sending SPI init cycles"); + + + let test_packet = Cmd52::new(); + ctx.spi + .write(&test_packet.into_bytes()) + .expect("error writing SDIO from host"); + + // expect R5 response from SDIO client + let mut res_buffer = [0u8; BLOCK_LEN]; + ctx.spi + .read(&mut res_buffer) + .expect("error reading SDIO from host"); + + // TODO: implement response types, and check for expected response + assert_ne!(res_buffer, [0u8; BLOCK_LEN]); + + let exp_res = R5::new().into_bytes(); + assert_eq!(&res_buffer[..R5::LEN], exp_res.as_ref()); + } +}