From 5f7a41fdd1d4296b029b294f4f87d3e37b3bf1f7 Mon Sep 17 00:00:00 2001 From: rmsyn Date: Mon, 26 May 2025 04:05:37 +0000 Subject: [PATCH 01/50] sdio: initial SDIO HAL implementation Provides the initial skeleton for the `embedded_hal::mmc::MmcOps` implementation for the SDIO peripherals on the ESP32C6 microcontroller. --- esp-hal/CHANGELOG.md | 1 + esp-hal/Cargo.toml | 4 +- esp-hal/src/lib.rs | 2 + esp-hal/src/sdio.rs | 214 ++++++++++++++++++++++++++++++++++++ esp-hal/src/sdio/slc.rs | 34 ++++++ esp-hal/src/sdio/slchost.rs | 34 ++++++ esp-lp-hal/Cargo.toml | 4 +- 7 files changed, 289 insertions(+), 4 deletions(-) create mode 100644 esp-hal/src/sdio.rs create mode 100644 esp-hal/src/sdio/slc.rs create mode 100644 esp-hal/src/sdio/slchost.rs diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index a90c538aa59..aafa2223baa 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -185,6 +185,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 7746917a91b..a4bb770580f 100644 --- a/esp-hal/Cargo.toml +++ b/esp-hal/Cargo.toml @@ -49,8 +49,8 @@ bitflags = "2.9.0" bytemuck = "1.22.0" cfg-if = "1.0.0" critical-section = { version = "1.2.0", features = ["restore-state-u32"] } -embedded-hal = "1.0.0" -embedded-hal-async = "1.0.0" +embedded-hal = { version = "1.0.0", git = "https://github.com/rmsyn/embedded-hal", branch = "embedded-hal/mmc" } +embedded-hal-async = { version = "1.0.0", git = "https://github.com/rmsyn/embedded-hal", branch = "embedded-hal/mmc" } enumset = "1.1.6" paste = "1.0.15" portable-atomic = { version = "1.11.0", default-features = false } diff --git a/esp-hal/src/lib.rs b/esp-hal/src/lib.rs index d6930e13a70..65db43e0428 100644 --- a/esp-hal/src/lib.rs +++ b/esp-hal/src/lib.rs @@ -244,6 +244,8 @@ pub mod i2c; pub mod peripherals; #[cfg(all(feature = "unstable", any(soc_has_hmac, soc_has_sha)))] mod reg_access; +#[cfg(any(feature = "esp32", feature = "esp32c6"))] +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..b2d9cc3476b --- /dev/null +++ b/esp-hal/src/sdio.rs @@ -0,0 +1,214 @@ +//! # Secure Digital I/O - Slave Mode +//! +//! ## Overiew +//! +//! The peripheral can be used to transfer data over the SDIO bus in `Slave` +//! mode. + +use embedded_hal::mmc::{ + CardMode, + CardType, + FifoStatus, + MmcCommon, + MmcDevice, + Reset, + command::MmcCommand, + response::MmcResponse, + tuning::{TuningMode, TuningWidth}, +}; + +mod slc; +mod slchost; + +pub use slc::*; +pub use slchost::*; + +/// SDIO peripheral instance. +pub trait PeripheralInstance: crate::private::Sealed { + /// Represents the peripheral information type containing the register + /// block. + type Info; + + /// Gets a static shared reference to the peripheral information. + fn info(&self) -> &'static Self::Info; +} + +/// Represents the SDIO 2.0 peripheral for the microcontroller. +#[derive(Debug)] +pub struct Sdio<'d> { + slc: AnySlc<'d>, + slchost: AnySlchost<'d>, +} + +impl<'d> Sdio<'d> { + /// Creates a new [Sdio]. + /// + /// # Example + /// + /// ```rust,no_run + /// use esp_hal::sdio::Sdio; + /// + /// use crate::peripheral::Peripherals; + /// + /// let dp = Peripheral::take().unwrap(); + /// let _sdio = Sdio::new(dp.slc, dp.slchost); + /// ``` + pub fn new(slc: impl SlcInstance + 'd, slchost: impl SlchostInstance + 'd) -> Self { + Self { + slc: slc.degrade(), + slchost: slchost.degrade(), + } + } + + /// Gets a static reference to the SLC information. + pub fn slc(&self) -> &'static SlcInfo { + self.slc.info() + } + + /// Gets a static reference to the SLCHOST information. + pub fn slchost(&self) -> &'static SlchostInfo { + self.slchost.info() + } +} + +/// 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, +} + +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 + } +} + +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"), + } + } +} + +impl core::error::Error for Error {} + +impl<'d> MmcCommon for Sdio<'d> { + 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()) + } + +<<<<<<< Updated upstream + fn init(&mut self) -> Result<(), Self::Error> { +======= + fn init(&mut self) -> Result<(), Error> { + // TODO: perform peripheral configuration + self.state_transition(State::Standby)?; + +>>>>>>> Stashed changes + Err(Error::unimplemented()) + } + + 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<'d> MmcDevice for Sdio<'d> { + 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/slc.rs b/esp-hal/src/sdio/slc.rs new file mode 100644 index 00000000000..dad1577fc86 --- /dev/null +++ b/esp-hal/src/sdio/slc.rs @@ -0,0 +1,34 @@ +use super::PeripheralInstance; +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 {} + +impl PeripheralInstance for AnySlc<'_> { + type Info = SlcInfo; + + fn info(&self) -> &'static Self::Info { + static INFO: SlcInfo = SlcInfo { + register_block: crate::peripherals::SLC::ptr(), + }; + + &INFO + } +} + +/// A peripheral singleton compatible with the SDIO SLC driver. +pub trait SlcInstance: PeripheralInstance + IntoAnySlc {} + +impl<'d> SlcInstance for AnySlc<'d> {} diff --git a/esp-hal/src/sdio/slchost.rs b/esp-hal/src/sdio/slchost.rs new file mode 100644 index 00000000000..4ad757c2fb2 --- /dev/null +++ b/esp-hal/src/sdio/slchost.rs @@ -0,0 +1,34 @@ +use super::PeripheralInstance; +use crate::pac::slchost; + +crate::any_peripheral! { + /// Any SDIO 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 {} + +impl PeripheralInstance for AnySlchost<'_> { + type Info = SlchostInfo; + + fn info(&self) -> &'static Self::Info { + static INFO: SlchostInfo = SlchostInfo { + register_block: crate::peripherals::SLCHOST::ptr(), + }; + + &INFO + } +} + +/// A peripheral singleton compatible with the SDIO SLCHOST driver. +pub trait SlchostInstance: PeripheralInstance + IntoAnySlchost {} + +impl<'d> SlchostInstance for AnySlchost<'d> {} diff --git a/esp-lp-hal/Cargo.toml b/esp-lp-hal/Cargo.toml index 4d8ef0388e8..f24ff70ee8a 100644 --- a/esp-lp-hal/Cargo.toml +++ b/esp-lp-hal/Cargo.toml @@ -37,8 +37,8 @@ test = false [dependencies] cfg-if = "1.0.0" document-features = "0.2.10" -embedded-hal = { version = "1.0.0", optional = true } -embedded-hal-nb = { version = "1.0.0", optional = true } +embedded-hal = { version = "1.0.0", git = "https://github.com/rmsyn/embedded-hal", branch = "embedded-hal/mmc", optional = true } +embedded-hal-nb = { version = "1.0.0", git = "https://github.com/rmsyn/embedded-hal", branch = "embedded-hal/mmc", optional = true } embedded-io = { version = "0.6.1", optional = true } esp32c6-lp = { version = "0.3.0", features = ["critical-section"], optional = true } esp32s2-ulp = { version = "0.3.0", features = ["critical-section"], optional = true } From 89b9c80da0e21d700811ba911d231f705d7f18e3 Mon Sep 17 00:00:00 2001 From: rmsyn Date: Fri, 30 May 2025 02:21:03 +0000 Subject: [PATCH 02/50] sdio: add GPIO pins configuration Adds `Pins` + `Mode` type for SDIO GPIO pin configuration. --- esp-hal/src/sdio.rs | 79 +++++++++++++++-- esp-hal/src/sdio/pins.rs | 177 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 247 insertions(+), 9 deletions(-) create mode 100644 esp-hal/src/sdio/pins.rs diff --git a/esp-hal/src/sdio.rs b/esp-hal/src/sdio.rs index b2d9cc3476b..625240cbd5b 100644 --- a/esp-hal/src/sdio.rs +++ b/esp-hal/src/sdio.rs @@ -17,11 +17,13 @@ use embedded_hal::mmc::{ tuning::{TuningMode, TuningWidth}, }; +mod pins; mod slc; mod slchost; -pub use slc::*; -pub use slchost::*; +pub use pins::Pins; +pub use slc::{AnySlc, SlcInfo, SlcInstance}; +pub use slchost::{AnySlchost, SlchostInfo, SlchostInstance}; /// SDIO peripheral instance. pub trait PeripheralInstance: crate::private::Sealed { @@ -33,30 +35,79 @@ pub trait PeripheralInstance: crate::private::Sealed { fn info(&self) -> &'static Self::Info; } +/// 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>, + pins: Pins<'d>, } impl<'d> Sdio<'d> { /// Creates a new [Sdio]. /// /// # Example + #[doc = crate::before_snippet!()] + /// ```rust, no_run + /// use esp_hal::sdio::{Mode, Pins, Sdio}; /// - /// ```rust,no_run - /// use esp_hal::sdio::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 + /// ); /// - /// use crate::peripheral::Peripherals; - /// - /// let dp = Peripheral::take().unwrap(); - /// let _sdio = Sdio::new(dp.slc, dp.slchost); + /// let _sdio = Sdio::new(peripherals.slc, peripherals.slchost, pins); /// ``` - pub fn new(slc: impl SlcInstance + 'd, slchost: impl SlchostInstance + 'd) -> Self { + pub fn new( + slc: impl SlcInstance + 'd, + slchost: impl SlchostInstance + 'd, + pins: Pins<'d>, + ) -> Self { Self { slc: slc.degrade(), slchost: slchost.degrade(), + pins, } } @@ -69,6 +120,16 @@ impl<'d> Sdio<'d> { pub fn slchost(&self) -> &'static SlchostInfo { self.slchost.info() } + + /// Gets a reference to the [Pins] information. + pub const fn pins(&self) -> &Pins<'_> { + &self.pins + } + + /// Gets the bus mode of the SDIO peripheral. + pub const fn bus_mode(&self) -> Mode { + self.pins.mode() + } } /// Represents the error variants for SDIO peripherals. diff --git a/esp-hal/src/sdio/pins.rs b/esp-hal/src/sdio/pins.rs new file mode 100644 index 00000000000..a45f63565f8 --- /dev/null +++ b/esp-hal/src/sdio/pins.rs @@ -0,0 +1,177 @@ +use super::{Error, Mode}; +use crate::gpio::{Flex, Level, Output, OutputConfig, OutputPin, Pin}; + +/// 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<'d> { + mode: Mode, + clk_sclk: Output<'d>, + cmd_mosi: Flex<'d>, + dat0_miso: Flex<'d>, + dat1_irq: Flex<'d>, + dat2: Flex<'d>, + dat3_cs: Flex<'d>, +} + +impl<'d> Pins<'d> { + /// Creates a new [Pins] from the provided GPIO pins. + /// + /// ```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 + /// ); + /// ``` + pub fn new( + mode: Mode, + clk_sclk: impl OutputPin + 'd, + cmd_mosi: impl Pin + 'd, + dat0_miso: impl Pin + 'd, + dat1_irq: impl Pin + 'd, + dat2: impl Pin + 'd, + dat3_cs: impl Pin + 'd, + ) -> Self { + Self { + mode, + clk_sclk: Output::new(clk_sclk, Level::Low, OutputConfig::default()), + cmd_mosi: Flex::new(cmd_mosi), + dat0_miso: Flex::new(dat0_miso), + dat1_irq: Flex::new(dat1_irq), + dat2: Flex::new(dat2), + dat3_cs: Flex::new(dat3_cs), + } + } + + /// 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 const fn clk(&self) -> Result<&Output<'_>, Error> { + match self.mode { + Mode::Sd1bit | Mode::Sd4bit => Ok(&self.clk_sclk), + Mode::Spi => Err(Error::General), + } + } + + /// Gets the SCLK signal for the [Pins]. + /// + /// Returns an [Error] if the [Mode] is not SPI. + pub const fn sclk(&self) -> Result<&Output<'_>, Error> { + match self.mode { + Mode::Spi => Ok(&self.clk_sclk), + Mode::Sd1bit | Mode::Sd4bit => Err(Error::General), + } + } + + /// Gets the CMD signal for the [Pins]. + /// + /// Returns an [Error] if the [Mode] is SPI. + pub const fn cmd(&self) -> Result<&Flex<'_>, Error> { + match self.mode { + Mode::Sd1bit | Mode::Sd4bit => Ok(&self.cmd_mosi), + Mode::Spi => Err(Error::General), + } + } + + /// Gets the MOSI signal for the [Pins]. + /// + /// Returns an [Error] if the [Mode] is not SPI. + pub const fn mosi(&self) -> Result<&Flex<'_>, Error> { + match self.mode { + Mode::Spi => Ok(&self.cmd_mosi), + Mode::Sd1bit | Mode::Sd4bit => Err(Error::General), + } + } + + /// Gets the DAT0 signal for the [Pins]. + /// + /// Returns an [Error] if the [Mode] is SPI. + pub const fn dat0(&self) -> Result<&Flex<'_>, Error> { + match self.mode { + Mode::Sd1bit | Mode::Sd4bit => Ok(&self.dat0_miso), + Mode::Spi => Err(Error::General), + } + } + + /// Gets the MISO signal for the [Pins]. + /// + /// Returns an [Error] if the [Mode] is not SPI. + pub const fn miso(&self) -> Result<&Flex<'_>, Error> { + match self.mode { + Mode::Spi => Ok(&self.cmd_mosi), + 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 const fn dat1(&self) -> Result<&Flex<'_>, Error> { + match self.mode { + Mode::Sd4bit => Ok(&self.dat1_irq), + Mode::Sd1bit | Mode::Spi => Err(Error::General), + } + } + + /// Gets the IRQ signal for the [Pins]. + /// + /// Returns an [Error] if the [Mode] is SD 4-bit. + pub const fn irq(&self) -> Result<&Flex<'_>, Error> { + match self.mode { + Mode::Sd1bit | Mode::Spi => Ok(&self.dat1_irq), + Mode::Sd4bit => Err(Error::General), + } + } + + /// Gets the DAT2 signal for the [Pins]. + /// + /// Returns an [Error] if the [Mode] is not SD 4-bit. + pub const fn dat2(&self) -> Result<&Flex<'_>, Error> { + match self.mode { + Mode::Sd4bit => Ok(&self.dat2), + Mode::Sd1bit | Mode::Spi => Err(Error::General), + } + } + + /// Gets the DAT3 signal for the [Pins]. + /// + /// Returns an [Error] if the [Mode] is SPI. + pub const fn dat3(&self) -> Result<&Flex<'_>, Error> { + match self.mode { + Mode::Sd4bit => Ok(&self.dat3_cs), + Mode::Sd1bit | Mode::Spi => Err(Error::General), + } + } + + /// Gets the CS signal for the [Pins]. + /// + /// Returns an [Error] if the [Mode] is not SPI. + pub const fn cs(&self) -> Result<&Flex<'_>, Error> { + match self.mode { + Mode::Spi => Ok(&self.dat3_cs), + Mode::Sd1bit | Mode::Sd4bit => Err(Error::General), + } + } +} From 4213c86d05ab93b693413031f6804601642ad0b8 Mon Sep 17 00:00:00 2001 From: rmsyn Date: Fri, 30 May 2025 03:07:59 +0000 Subject: [PATCH 03/50] sdio: add peripheral state Adds the `State` type to represent the SDIO states + transitions. --- esp-hal/src/sdio.rs | 36 +++++++++++++++++++++--- esp-hal/src/sdio/state.rs | 59 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 4 deletions(-) create mode 100644 esp-hal/src/sdio/state.rs diff --git a/esp-hal/src/sdio.rs b/esp-hal/src/sdio.rs index 625240cbd5b..271dad53a19 100644 --- a/esp-hal/src/sdio.rs +++ b/esp-hal/src/sdio.rs @@ -20,10 +20,12 @@ use embedded_hal::mmc::{ mod pins; mod slc; mod slchost; +mod state; pub use pins::Pins; pub use slc::{AnySlc, SlcInfo, SlcInstance}; pub use slchost::{AnySlchost, SlchostInfo, SlchostInstance}; +pub use state::State; /// SDIO peripheral instance. pub trait PeripheralInstance: crate::private::Sealed { @@ -77,6 +79,7 @@ pub struct Sdio<'d> { slc: AnySlc<'d>, slchost: AnySlchost<'d>, pins: Pins<'d>, + state: State, } impl<'d> Sdio<'d> { @@ -108,6 +111,7 @@ impl<'d> Sdio<'d> { slc: slc.degrade(), slchost: slchost.degrade(), pins, + state: State::new(), } } @@ -130,6 +134,18 @@ impl<'d> Sdio<'d> { pub const fn bus_mode(&self) -> Mode { self.pins.mode() } + + /// 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) + } } /// Represents the error variants for SDIO peripherals. @@ -143,6 +159,13 @@ pub enum Error { 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 { @@ -174,6 +197,12 @@ impl Error { 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 { @@ -189,6 +218,9 @@ impl core::fmt::Display for Error { 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}") + } } } } @@ -210,14 +242,10 @@ impl<'d> MmcCommon for Sdio<'d> { Err(Error::unimplemented()) } -<<<<<<< Updated upstream - fn init(&mut self) -> Result<(), Self::Error> { -======= fn init(&mut self) -> Result<(), Error> { // TODO: perform peripheral configuration self.state_transition(State::Standby)?; ->>>>>>> Stashed changes Err(Error::unimplemented()) } 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"), + } + } +} From 7c77c77582aa30bbf8fd1fbd31e85dfdb90825c3 Mon Sep 17 00:00:00 2001 From: rmsyn Date: Wed, 4 Jun 2025 02:28:30 +0000 Subject: [PATCH 04/50] sdio: use experimental `embedded-hal-sdio` crate Uses the experimental `embedded-hal-sdmmc` crate for SD/MMC traits. See discussion for reasoning: - - - --- esp-hal/Cargo.toml | 6 ++++-- esp-hal/src/lib.rs | 2 +- esp-hal/src/sdio.rs | 18 +++++++++--------- esp-hal/src/sdio/slc.rs | 2 +- esp-hal/src/sdio/slchost.rs | 2 +- esp-lp-hal/Cargo.toml | 4 ++-- 6 files changed, 18 insertions(+), 16 deletions(-) diff --git a/esp-hal/Cargo.toml b/esp-hal/Cargo.toml index a4bb770580f..0d36895dc3f 100644 --- a/esp-hal/Cargo.toml +++ b/esp-hal/Cargo.toml @@ -49,8 +49,8 @@ bitflags = "2.9.0" bytemuck = "1.22.0" cfg-if = "1.0.0" critical-section = { version = "1.2.0", features = ["restore-state-u32"] } -embedded-hal = { version = "1.0.0", git = "https://github.com/rmsyn/embedded-hal", branch = "embedded-hal/mmc" } -embedded-hal-async = { version = "1.0.0", git = "https://github.com/rmsyn/embedded-hal", branch = "embedded-hal/mmc" } +embedded-hal = "1.0.0" +embedded-hal-async = "1.0.0" enumset = "1.1.6" paste = "1.0.15" portable-atomic = { version = "1.11.0", default-features = false } @@ -80,6 +80,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.2", optional = true } # Logging interfaces, they are mutually exclusive so they need to be behind separate features. defmt = { version = "1.0.1", optional = true } @@ -279,6 +280,7 @@ unstable = [ "dep:rand_core-09", "dep:nb", "dep:ufmt-write", + "dep:embedded-hal-sdmmc", ] ## Libraries that depend on `esp-hal` should enable this feature to indicate their use of unstable APIs. diff --git a/esp-hal/src/lib.rs b/esp-hal/src/lib.rs index 65db43e0428..8adb3cb2e51 100644 --- a/esp-hal/src/lib.rs +++ b/esp-hal/src/lib.rs @@ -244,7 +244,7 @@ pub mod i2c; pub mod peripherals; #[cfg(all(feature = "unstable", any(soc_has_hmac, soc_has_sha)))] mod reg_access; -#[cfg(any(feature = "esp32", feature = "esp32c6"))] +#[cfg(all(feature = "unstable", any(feature = "esp32", feature = "esp32c6")))] pub mod sdio; #[cfg(any(soc_has_spi0, soc_has_spi1, soc_has_spi2, soc_has_spi3))] pub mod spi; diff --git a/esp-hal/src/sdio.rs b/esp-hal/src/sdio.rs index 271dad53a19..1311ec8f96f 100644 --- a/esp-hal/src/sdio.rs +++ b/esp-hal/src/sdio.rs @@ -5,15 +5,15 @@ //! The peripheral can be used to transfer data over the SDIO bus in `Slave` //! mode. -use embedded_hal::mmc::{ +use embedded_hal_sdmmc::{ CardMode, CardType, + Common, + Device, FifoStatus, - MmcCommon, - MmcDevice, Reset, - command::MmcCommand, - response::MmcResponse, + command::Command, + response::Response, tuning::{TuningMode, TuningWidth}, }; @@ -227,7 +227,7 @@ impl core::fmt::Display for Error { impl core::error::Error for Error {} -impl<'d> MmcCommon for Sdio<'d> { +impl Common for Sdio<'_> { type Error = Error; fn card_type(&self) -> CardType { @@ -292,12 +292,12 @@ impl<'d> MmcCommon for Sdio<'d> { fn clear_all_response_interrupt(&mut self) {} } -impl<'d> MmcDevice for Sdio<'d> { - fn read_command(&mut self) -> Result { +impl Device for Sdio<'_> { + fn read_command(&mut self) -> Result { Err(Error::unimplemented()) } - fn write_response(&mut self, _response: &R) -> Result<(), Error> { + fn write_response(&mut self, _response: &R) -> Result<(), Error> { Err(Error::unimplemented()) } } diff --git a/esp-hal/src/sdio/slc.rs b/esp-hal/src/sdio/slc.rs index dad1577fc86..132f87509d0 100644 --- a/esp-hal/src/sdio/slc.rs +++ b/esp-hal/src/sdio/slc.rs @@ -31,4 +31,4 @@ impl PeripheralInstance for AnySlc<'_> { /// A peripheral singleton compatible with the SDIO SLC driver. pub trait SlcInstance: PeripheralInstance + IntoAnySlc {} -impl<'d> SlcInstance for AnySlc<'d> {} +impl SlcInstance for AnySlc<'_> {} diff --git a/esp-hal/src/sdio/slchost.rs b/esp-hal/src/sdio/slchost.rs index 4ad757c2fb2..449e2036803 100644 --- a/esp-hal/src/sdio/slchost.rs +++ b/esp-hal/src/sdio/slchost.rs @@ -31,4 +31,4 @@ impl PeripheralInstance for AnySlchost<'_> { /// A peripheral singleton compatible with the SDIO SLCHOST driver. pub trait SlchostInstance: PeripheralInstance + IntoAnySlchost {} -impl<'d> SlchostInstance for AnySlchost<'d> {} +impl SlchostInstance for AnySlchost<'_> {} diff --git a/esp-lp-hal/Cargo.toml b/esp-lp-hal/Cargo.toml index f24ff70ee8a..4d8ef0388e8 100644 --- a/esp-lp-hal/Cargo.toml +++ b/esp-lp-hal/Cargo.toml @@ -37,8 +37,8 @@ test = false [dependencies] cfg-if = "1.0.0" document-features = "0.2.10" -embedded-hal = { version = "1.0.0", git = "https://github.com/rmsyn/embedded-hal", branch = "embedded-hal/mmc", optional = true } -embedded-hal-nb = { version = "1.0.0", git = "https://github.com/rmsyn/embedded-hal", branch = "embedded-hal/mmc", optional = true } +embedded-hal = { version = "1.0.0", optional = true } +embedded-hal-nb = { version = "1.0.0", optional = true } embedded-io = { version = "0.6.1", optional = true } esp32c6-lp = { version = "0.3.0", features = ["critical-section"], optional = true } esp32s2-ulp = { version = "0.3.0", features = ["critical-section"], optional = true } From 64cb6e79f52cea8ddf79b2472684fd3bb005a44e Mon Sep 17 00:00:00 2001 From: rmsyn Date: Tue, 10 Jun 2025 02:08:18 +0000 Subject: [PATCH 05/50] sdio: add `Sdio::bus_width` method Updates the `embedded-hal-sdmmc` dependency, and adds the `Sdio::bus_width` method to get the bus width of the SDIO peripheral. --- esp-hal/Cargo.toml | 2 +- esp-hal/src/sdio.rs | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/esp-hal/Cargo.toml b/esp-hal/Cargo.toml index 0d36895dc3f..6b169472d69 100644 --- a/esp-hal/Cargo.toml +++ b/esp-hal/Cargo.toml @@ -80,7 +80,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.2", 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 } diff --git a/esp-hal/src/sdio.rs b/esp-hal/src/sdio.rs index 1311ec8f96f..a99d96a9e95 100644 --- a/esp-hal/src/sdio.rs +++ b/esp-hal/src/sdio.rs @@ -12,6 +12,7 @@ use embedded_hal_sdmmc::{ Device, FifoStatus, Reset, + SdBusWidth as BusWidth, command::Command, response::Response, tuning::{TuningMode, TuningWidth}, @@ -135,6 +136,14 @@ impl<'d> Sdio<'d> { 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 From 8a3050818c8057004907ea404892f3b08b23781f Mon Sep 17 00:00:00 2001 From: rmsyn Date: Tue, 10 Jun 2025 02:30:24 +0000 Subject: [PATCH 06/50] sdio: add interrupt types Adds the `HostInterrupt` enum to represent device-to-host interrupt variants. --- esp-hal/src/sdio.rs | 2 + esp-hal/src/sdio/interrupt.rs | 98 +++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 esp-hal/src/sdio/interrupt.rs diff --git a/esp-hal/src/sdio.rs b/esp-hal/src/sdio.rs index a99d96a9e95..68f766bb62b 100644 --- a/esp-hal/src/sdio.rs +++ b/esp-hal/src/sdio.rs @@ -18,11 +18,13 @@ use embedded_hal_sdmmc::{ tuning::{TuningMode, TuningWidth}, }; +mod interrupt; mod pins; mod slc; mod slchost; mod state; +pub use interrupt::HostInterrupt; pub use pins::Pins; pub use slc::{AnySlc, SlcInfo, SlcInstance}; pub use slchost::{AnySlchost, SlchostInfo, SlchostInstance}; diff --git a/esp-hal/src/sdio/interrupt.rs b/esp-hal/src/sdio/interrupt.rs new file mode 100644 index 00000000000..118c6ece58d --- /dev/null +++ b/esp-hal/src/sdio/interrupt.rs @@ -0,0 +1,98 @@ +use super::Error; + +/// 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. +#[repr(u32)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum HostInterrupt { + /// General purpose host interrupt: 0. + General0 = 1 << 0, + /// General purpose host interrupt: 1. + General1 = 1 << 1, + /// General purpose host interrupt: 2. + General2 = 1 << 2, + /// General purpose host interrupt: 3. + General3 = 1 << 3, + /// General purpose host interrupt: 4. + General4 = 1 << 4, + /// General purpose host interrupt: 5. + General5 = 1 << 5, + /// General purpose host interrupt: 6. + General6 = 1 << 6, + /// General purpose host interrupt: 7. + General7 = 1 << 7, + /// New packet available to send to host. + SendNewPacket = 1 << 23, +} + +impl HostInterrupt { + /// Bit value for general purpose host interrupt: 0. + pub const GENERAL_0: u32 = 1 << 0; + /// Bit value for general purpose host interrupt: 1. + pub const GENERAL_1: u32 = 1 << 1; + /// Bit value for general purpose host interrupt: 2. + pub const GENERAL_2: u32 = 1 << 2; + /// Bit value for general purpose host interrupt: 3. + pub const GENERAL_3: u32 = 1 << 3; + /// Bit value for general purpose host interrupt: 4. + pub const GENERAL_4: u32 = 1 << 4; + /// Bit value for general purpose host interrupt: 5. + pub const GENERAL_5: u32 = 1 << 5; + /// Bit value for general purpose host interrupt: 6. + pub const GENERAL_6: u32 = 1 << 6; + /// Bit value for general purpose host interrupt: 7. + pub const GENERAL_7: u32 = 1 << 7; + /// Bit value for new packet available to send to host. + pub const SEND_NEW_PACKET: u32 = 1 << 23; + + /// Creates a new [HostInterrupt]. + pub const fn new() -> Self { + Self::General0 + } + + /// Converts the [HostInterrupt] into a [`u32`]. + #[inline] + pub const fn to_u32(self) -> u32 { + self as u32 + } + + /// Attempts to convert a [`u32`] into a [HostInterrupt]. + #[inline] + pub const fn try_from_u32(val: u32) -> Result { + match val { + Self::GENERAL_0 => Ok(Self::General0), + Self::GENERAL_1 => Ok(Self::General1), + Self::GENERAL_2 => Ok(Self::General2), + Self::GENERAL_3 => Ok(Self::General3), + Self::GENERAL_4 => Ok(Self::General4), + Self::GENERAL_5 => Ok(Self::General5), + Self::GENERAL_6 => Ok(Self::General6), + Self::GENERAL_7 => Ok(Self::General7), + Self::SEND_NEW_PACKET => Ok(Self::SendNewPacket), + _ => Err(Error::General), + } + } +} + +impl From for u32 { + fn from(val: HostInterrupt) -> Self { + val.to_u32() + } +} + +impl TryFrom for HostInterrupt { + type Error = Error; + + fn try_from(val: u32) -> Result { + Self::try_from_u32(val) + } +} + +impl Default for HostInterrupt { + fn default() -> Self { + Self::new() + } +} From f9abd118c9ea52eba7c4048783477c46322f781d Mon Sep 17 00:00:00 2001 From: rmsyn Date: Thu, 19 Jun 2025 02:30:58 +0000 Subject: [PATCH 07/50] fixup: esp-hal: fixup SDIO pin access --- esp-hal/src/sdio/pins.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/esp-hal/src/sdio/pins.rs b/esp-hal/src/sdio/pins.rs index a45f63565f8..61f88a1452b 100644 --- a/esp-hal/src/sdio/pins.rs +++ b/esp-hal/src/sdio/pins.rs @@ -130,8 +130,8 @@ impl<'d> Pins<'d> { /// Returns an [Error] if the [Mode] is not SD 4-bit. pub const fn dat1(&self) -> Result<&Flex<'_>, Error> { match self.mode { - Mode::Sd4bit => Ok(&self.dat1_irq), - Mode::Sd1bit | Mode::Spi => Err(Error::General), + Mode::Sd1bit | Mode::Sd4bit => Ok(&self.dat1_irq), + Mode::Spi => Err(Error::General), } } @@ -140,8 +140,8 @@ impl<'d> Pins<'d> { /// Returns an [Error] if the [Mode] is SD 4-bit. pub const fn irq(&self) -> Result<&Flex<'_>, Error> { match self.mode { - Mode::Sd1bit | Mode::Spi => Ok(&self.dat1_irq), - Mode::Sd4bit => Err(Error::General), + Mode::Spi => Ok(&self.dat1_irq), + Mode::Sd1bit | Mode::Sd4bit => Err(Error::General), } } @@ -150,8 +150,8 @@ impl<'d> Pins<'d> { /// Returns an [Error] if the [Mode] is not SD 4-bit. pub const fn dat2(&self) -> Result<&Flex<'_>, Error> { match self.mode { - Mode::Sd4bit => Ok(&self.dat2), - Mode::Sd1bit | Mode::Spi => Err(Error::General), + Mode::Sd1bit | Mode::Sd4bit => Ok(&self.dat2), + Mode::Spi => Err(Error::General), } } @@ -160,8 +160,8 @@ impl<'d> Pins<'d> { /// Returns an [Error] if the [Mode] is SPI. pub const fn dat3(&self) -> Result<&Flex<'_>, Error> { match self.mode { - Mode::Sd4bit => Ok(&self.dat3_cs), - Mode::Sd1bit | Mode::Spi => Err(Error::General), + Mode::Sd1bit | Mode::Sd4bit => Ok(&self.dat3_cs), + Mode::Spi => Err(Error::General), } } From 77586462e1ad34a7b16978dda16cfa92a7ffb86b Mon Sep 17 00:00:00 2001 From: rmsyn Date: Sat, 21 Jun 2025 03:39:35 +0000 Subject: [PATCH 08/50] sdio: add init pin configuration Adds the pin configuration for the `Sdio::init` HAL method. Leaves the `sdio::Configuration` implementation for a future commit. Leaves the `Sdio::hal_hw_init` implementation for a future commit. --- esp-hal/src/sdio.rs | 40 +++++++++++++++++++++++++++++++++++++--- esp-hal/src/sdio/pins.rs | 12 ++++++------ 2 files changed, 43 insertions(+), 9 deletions(-) diff --git a/esp-hal/src/sdio.rs b/esp-hal/src/sdio.rs index 68f766bb62b..f08c3622eb2 100644 --- a/esp-hal/src/sdio.rs +++ b/esp-hal/src/sdio.rs @@ -18,6 +18,8 @@ use embedded_hal_sdmmc::{ tuning::{TuningMode, TuningWidth}, }; +use crate::gpio::{InputConfig, OutputConfig, Pull}; + mod interrupt; mod pins; mod slc; @@ -157,6 +159,11 @@ impl<'d> Sdio<'d> { .valid_transition(state) .map(|_| self.state = state) } + + /// Performs final low-level HAL hardware initialization. + pub(crate) fn hal_hw_init(&mut self) -> Result<(), Error> { + Err(Error::unimplemented()) + } } /// Represents the error variants for SDIO peripherals. @@ -254,10 +261,37 @@ impl Common for Sdio<'_> { } fn init(&mut self) -> Result<(), Error> { - // TODO: perform peripheral configuration - self.state_transition(State::Standby)?; + self.pins.clk_sclk.apply_config(&OutputConfig::default()); - Err(Error::unimplemented()) + let input_pullup = InputConfig::default().with_pull(Pull::Up); + let output_pullup = OutputConfig::default().with_pull(Pull::Up); + + self.pins.cmd_mosi.apply_input_config(&input_pullup); + self.pins.cmd_mosi.apply_output_config(&output_pullup); + + self.pins.dat0_miso.apply_input_config(&input_pullup); + self.pins.dat0_miso.apply_output_config(&output_pullup); + + // TODO: Add an Configuration struct, and a SdioDevice::config member + // TODO: test config host_intr disabled + // if self.config.host_intr() { + self.pins.dat1_irq.apply_input_config(&input_pullup); + self.pins.dat1_irq.apply_output_config(&output_pullup); + // } + // + // TODO: test config dat2 disabled + // if self.config.dat2() { + self.pins.dat2.apply_input_config(&input_pullup); + self.pins.dat2.apply_output_config(&output_pullup); + // } + + self.pins.dat3_cs.apply_input_config(&input_pullup); + self.pins.dat3_cs.apply_output_config(&output_pullup); + + // TODO: implement HAL hardware init + self.hal_hw_init()?; + + self.state_transition(State::Standby) } fn set_sample_phase(&mut self, _sample_phase: u8) {} diff --git a/esp-hal/src/sdio/pins.rs b/esp-hal/src/sdio/pins.rs index 61f88a1452b..7ebe16d72b4 100644 --- a/esp-hal/src/sdio/pins.rs +++ b/esp-hal/src/sdio/pins.rs @@ -10,12 +10,12 @@ use crate::gpio::{Flex, Level, Output, OutputConfig, OutputPin, Pin}; #[derive(Debug)] pub struct Pins<'d> { mode: Mode, - clk_sclk: Output<'d>, - cmd_mosi: Flex<'d>, - dat0_miso: Flex<'d>, - dat1_irq: Flex<'d>, - dat2: Flex<'d>, - dat3_cs: Flex<'d>, + pub(crate) clk_sclk: Output<'d>, + pub(crate) cmd_mosi: Flex<'d>, + pub(crate) dat0_miso: Flex<'d>, + pub(crate) dat1_irq: Flex<'d>, + pub(crate) dat2: Flex<'d>, + pub(crate) dat3_cs: Flex<'d>, } impl<'d> Pins<'d> { From 67dd51e6a4f4a6e51793942c5df12e050d6b2c95 Mon Sep 17 00:00:00 2001 From: rmsyn Date: Thu, 26 Jun 2025 20:26:08 +0000 Subject: [PATCH 09/50] sdio: add low-level init Adds the low-level init function to configure low-level SDIO registers. --- esp-hal/src/sdio.rs | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/esp-hal/src/sdio.rs b/esp-hal/src/sdio.rs index f08c3622eb2..9959608d9e8 100644 --- a/esp-hal/src/sdio.rs +++ b/esp-hal/src/sdio.rs @@ -161,9 +161,36 @@ impl<'d> Sdio<'d> { } /// Performs final low-level HAL hardware initialization. - pub(crate) fn hal_hw_init(&mut self) -> Result<(), Error> { + pub(crate) fn hardware_init(&mut self) -> Result<(), Error> { + self.low_level_init()?; + Err(Error::unimplemented()) } + + /// Performs low-level initialization of the SDIO peripheral. + fn low_level_init(&mut self) -> Result<(), Error> { + let slc = unsafe { &*self.slc.info().register_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(()) + } } /// Represents the error variants for SDIO peripherals. @@ -261,6 +288,8 @@ impl Common for Sdio<'_> { } fn init(&mut self) -> Result<(), Error> { + // This implementation is based on the `esp-idf` driver: + // self.pins.clk_sclk.apply_config(&OutputConfig::default()); let input_pullup = InputConfig::default().with_pull(Pull::Up); @@ -289,7 +318,7 @@ impl Common for Sdio<'_> { self.pins.dat3_cs.apply_output_config(&output_pullup); // TODO: implement HAL hardware init - self.hal_hw_init()?; + self.hardware_init()?; self.state_transition(State::Standby) } From 072942b5a2d726afa4f8da7c6278dcacb5f7c7bf Mon Sep 17 00:00:00 2001 From: rmsyn Date: Thu, 26 Jun 2025 21:01:56 +0000 Subject: [PATCH 10/50] sdio: add HINF register block Adds the types needed to represent the `HINF` register block for SDIO peripherals. --- esp-hal/src/sdio.rs | 12 +++++++++++- esp-hal/src/sdio/hinf.rs | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 esp-hal/src/sdio/hinf.rs diff --git a/esp-hal/src/sdio.rs b/esp-hal/src/sdio.rs index 9959608d9e8..46e006eb103 100644 --- a/esp-hal/src/sdio.rs +++ b/esp-hal/src/sdio.rs @@ -20,12 +20,14 @@ use embedded_hal_sdmmc::{ use crate::gpio::{InputConfig, OutputConfig, Pull}; +mod hinf; mod interrupt; mod pins; mod slc; mod slchost; mod state; +pub use hinf::{AnyHinf, HinfInfo, HinfInstance}; pub use interrupt::HostInterrupt; pub use pins::Pins; pub use slc::{AnySlc, SlcInfo, SlcInstance}; @@ -83,6 +85,7 @@ pub enum Mode { pub struct Sdio<'d> { slc: AnySlc<'d>, slchost: AnySlchost<'d>, + hinf: AnyHinf<'d>, pins: Pins<'d>, state: State, } @@ -105,16 +108,18 @@ impl<'d> Sdio<'d> { /// peripherals.GPIO23, // DAT3/#CS /// ); /// - /// let _sdio = Sdio::new(peripherals.slc, peripherals.slchost, pins); + /// let _sdio = Sdio::new(peripherals.slc, peripherals.slchost, peripherals.hinf, pins); /// ``` pub fn new( slc: impl SlcInstance + 'd, slchost: impl SlchostInstance + 'd, + hinf: impl HinfInstance + 'd, pins: Pins<'d>, ) -> Self { Self { slc: slc.degrade(), slchost: slchost.degrade(), + hinf: hinf.degrade(), pins, state: State::new(), } @@ -130,6 +135,11 @@ impl<'d> Sdio<'d> { self.slchost.info() } + /// Gets a static reference to the HINF information. + pub fn hinf(&self) -> &'static HinfInfo { + self.hinf.info() + } + /// Gets a reference to the [Pins] information. pub const fn pins(&self) -> &Pins<'_> { &self.pins diff --git a/esp-hal/src/sdio/hinf.rs b/esp-hal/src/sdio/hinf.rs new file mode 100644 index 00000000000..e3140049d23 --- /dev/null +++ b/esp-hal/src/sdio/hinf.rs @@ -0,0 +1,34 @@ +use super::PeripheralInstance; +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 {} + +impl PeripheralInstance for AnyHinf<'_> { + type Info = HinfInfo; + + fn info(&self) -> &'static Self::Info { + static INFO: HinfInfo = HinfInfo { + register_block: crate::peripherals::HINF::ptr(), + }; + + &INFO + } +} + +/// A peripheral singleton compatible with the SDIO HINF driver. +pub trait HinfInstance: PeripheralInstance + IntoAnyHinf {} + +impl HinfInstance for AnyHinf<'_> {} From 8cbb7bcec8817bac35e3525a972fc70aadff3b64 Mon Sep 17 00:00:00 2001 From: rmsyn Date: Thu, 26 Jun 2025 21:15:38 +0000 Subject: [PATCH 11/50] sdio: add config + enable highspeed Adds a basic `Config` struct to represent SDIO peripheral configuration. Adds a method to enable highspeed on the SDIO peripheral based on configuration. --- esp-hal/src/sdio.rs | 33 +++++++++++++++++++++++++++++++-- esp-hal/src/sdio/config.rs | 20 ++++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 esp-hal/src/sdio/config.rs diff --git a/esp-hal/src/sdio.rs b/esp-hal/src/sdio.rs index 46e006eb103..b61068f439e 100644 --- a/esp-hal/src/sdio.rs +++ b/esp-hal/src/sdio.rs @@ -20,6 +20,7 @@ use embedded_hal_sdmmc::{ use crate::gpio::{InputConfig, OutputConfig, Pull}; +mod config; mod hinf; mod interrupt; mod pins; @@ -27,6 +28,7 @@ mod slc; mod slchost; mod state; +pub use config::Config; pub use hinf::{AnyHinf, HinfInfo, HinfInstance}; pub use interrupt::HostInterrupt; pub use pins::Pins; @@ -87,6 +89,7 @@ pub struct Sdio<'d> { slchost: AnySlchost<'d>, hinf: AnyHinf<'d>, pins: Pins<'d>, + config: Config, state: State, } @@ -96,7 +99,7 @@ impl<'d> Sdio<'d> { /// # Example #[doc = crate::before_snippet!()] /// ```rust, no_run - /// use esp_hal::sdio::{Mode, Pins, Sdio}; + /// use esp_hal::sdio::{Config, Mode, Pins, Sdio}; /// /// let pins = Pins::new( /// Mode::Sd4bit, @@ -108,19 +111,29 @@ impl<'d> Sdio<'d> { /// peripherals.GPIO23, // DAT3/#CS /// ); /// - /// let _sdio = Sdio::new(peripherals.slc, peripherals.slchost, peripherals.hinf, pins); + /// let config = Config::new(); + /// + /// let _sdio = Sdio::new( + /// peripherals.slc, + /// peripherals.slchost, + /// peripherals.hinf, + /// pins, + /// config, + /// ); /// ``` pub fn new( slc: impl SlcInstance + 'd, slchost: impl SlchostInstance + 'd, hinf: impl HinfInstance + 'd, pins: Pins<'d>, + config: Config, ) -> Self { Self { slc: slc.degrade(), slchost: slchost.degrade(), hinf: hinf.degrade(), pins, + config, state: State::new(), } } @@ -145,6 +158,11 @@ impl<'d> Sdio<'d> { &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() @@ -173,6 +191,7 @@ impl<'d> Sdio<'d> { /// Performs final low-level HAL hardware initialization. pub(crate) fn hardware_init(&mut self) -> Result<(), Error> { self.low_level_init()?; + self.low_level_enable_hs()?; Err(Error::unimplemented()) } @@ -201,6 +220,16 @@ impl<'d> Sdio<'d> { Ok(()) } + + /// Sets the high-speed supported bit to be read by the host. + fn low_level_enable_hs(&mut self) -> Result<(), Error> { + let hinf = unsafe { &*self.hinf.info().register_block }; + + hinf.cfg_data1() + .modify(|_, w| w.highspeed_enable().variant(self.config.hs)); + + Ok(()) + } } /// Represents the error variants for SDIO peripherals. diff --git a/esp-hal/src/sdio/config.rs b/esp-hal/src/sdio/config.rs new file mode 100644 index 00000000000..b008d9d7c24 --- /dev/null +++ b/esp-hal/src/sdio/config.rs @@ -0,0 +1,20 @@ +/// Represents SDIO configuration parameters. +#[repr(C)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct Config { + /// Indicates support for high-speed mode. + pub hs: bool, +} + +impl Config { + /// Creates a new [Config]. + pub const fn new() -> Self { + Self { hs: false } + } +} + +impl Default for Config { + fn default() -> Self { + Self::new() + } +} From b2269a75b0940a6e535bd7019e126f2c05494061 Mon Sep 17 00:00:00 2001 From: rmsyn Date: Thu, 26 Jun 2025 22:02:33 +0000 Subject: [PATCH 12/50] sdio: add set timing method Adds the `Timing` struct to represent SDIO timing settings. Adds a method to set low-level timing configuration for the SDIO peripheral. --- esp-hal/src/sdio.rs | 46 +++++++++++++++++++++++++++++++++++++- esp-hal/src/sdio/config.rs | 41 ++++++++++++++++++++++++++++++--- esp-hal/src/sdio/timing.rs | 26 +++++++++++++++++++++ 3 files changed, 109 insertions(+), 4 deletions(-) create mode 100644 esp-hal/src/sdio/timing.rs diff --git a/esp-hal/src/sdio.rs b/esp-hal/src/sdio.rs index b61068f439e..49f5c5bf533 100644 --- a/esp-hal/src/sdio.rs +++ b/esp-hal/src/sdio.rs @@ -27,6 +27,7 @@ mod pins; mod slc; mod slchost; mod state; +mod timing; pub use config::Config; pub use hinf::{AnyHinf, HinfInfo, HinfInstance}; @@ -35,6 +36,7 @@ pub use pins::Pins; pub use slc::{AnySlc, SlcInfo, SlcInstance}; pub use slchost::{AnySlchost, SlchostInfo, SlchostInstance}; pub use state::State; +pub use timing::Timing; /// SDIO peripheral instance. pub trait PeripheralInstance: crate::private::Sealed { @@ -192,6 +194,7 @@ impl<'d> Sdio<'d> { pub(crate) fn hardware_init(&mut self) -> Result<(), Error> { self.low_level_init()?; self.low_level_enable_hs()?; + self.low_level_set_timing()?; Err(Error::unimplemented()) } @@ -226,7 +229,48 @@ impl<'d> Sdio<'d> { let hinf = unsafe { &*self.hinf.info().register_block }; hinf.cfg_data1() - .modify(|_, w| w.highspeed_enable().variant(self.config.hs)); + .modify(|_, w| w.highspeed_enable().variant(self.config.hs())); + + Ok(()) + } + + fn low_level_set_timing(&mut self) -> Result<(), Error> { + let host = unsafe { &*self.slchost.info().register_block }; + + match self.config.timing() { + Timing::PsendPsample => { + host.conf().modify(|_, w| unsafe { + w.frc_sdio20().bits(0x1f); + w.frc_sdio11().bits(0x0); + w.frc_pos_samp().bits(0x1f); + w.frc_neg_samp().bits(0x0) + }); + } + Timing::PsendNsample => { + host.conf().modify(|_, w| unsafe { + w.frc_sdio20().bits(0x1f); + w.frc_sdio11().bits(0x0); + w.frc_pos_samp().bits(0x0); + w.frc_neg_samp().bits(0x1f) + }); + } + Timing::NsendPsample => { + host.conf().modify(|_, w| unsafe { + w.frc_sdio20().bits(0x0); + w.frc_sdio11().bits(0x1f); + w.frc_pos_samp().bits(0x1f); + w.frc_neg_samp().bits(0x0) + }); + } + Timing::NsendNsample => { + host.conf().modify(|_, w| unsafe { + w.frc_sdio20().bits(0x0); + w.frc_sdio11().bits(0x1f); + w.frc_pos_samp().bits(0x0); + w.frc_neg_samp().bits(0x1f) + }); + } + } Ok(()) } diff --git a/esp-hal/src/sdio/config.rs b/esp-hal/src/sdio/config.rs index b008d9d7c24..7d89a1ba16c 100644 --- a/esp-hal/src/sdio/config.rs +++ b/esp-hal/src/sdio/config.rs @@ -1,15 +1,50 @@ +use super::Timing; + /// Represents SDIO configuration parameters. #[repr(C)] #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct Config { - /// Indicates support for high-speed mode. - pub hs: bool, + hs: bool, + timing: Timing, } impl Config { /// Creates a new [Config]. pub const fn new() -> Self { - Self { hs: false } + 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 } } } 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() + } +} From fc4c756a574307796ea41950d3cb7c44c8d6930f Mon Sep 17 00:00:00 2001 From: rmsyn Date: Thu, 26 Jun 2025 22:43:09 +0000 Subject: [PATCH 13/50] sdio: change interrupt types to bitfields Changes the SDIO interrupt types to be bitfield structs. Adds the `DeviceInterrupt` type to represents interrupts sent from the host to the device. --- esp-hal/src/sdio/interrupt.rs | 149 +++++++++++++++++++--------------- 1 file changed, 83 insertions(+), 66 deletions(-) diff --git a/esp-hal/src/sdio/interrupt.rs b/esp-hal/src/sdio/interrupt.rs index 118c6ece58d..6d398d98cb3 100644 --- a/esp-hal/src/sdio/interrupt.rs +++ b/esp-hal/src/sdio/interrupt.rs @@ -1,97 +1,114 @@ -use super::Error; +use bitfield::bitfield; -/// 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. -#[repr(u32)] -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum HostInterrupt { +bitfield! { + /// 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. + #[repr(C)] + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + pub struct HostInterrupt(u8); /// General purpose host interrupt: 0. - General0 = 1 << 0, + pub general0, set_general0: 0; /// General purpose host interrupt: 1. - General1 = 1 << 1, + pub general1, set_general1: 1; /// General purpose host interrupt: 2. - General2 = 1 << 2, + pub general2, set_general2: 2; /// General purpose host interrupt: 3. - General3 = 1 << 3, + pub general3, set_general3: 3; /// General purpose host interrupt: 4. - General4 = 1 << 4, + pub general4, set_general4: 4; /// General purpose host interrupt: 5. - General5 = 1 << 5, + pub general5, set_general5: 5; /// General purpose host interrupt: 6. - General6 = 1 << 6, + pub general6, set_general6: 6; /// General purpose host interrupt: 7. - General7 = 1 << 7, - /// New packet available to send to host. - SendNewPacket = 1 << 23, + pub general7, set_general7: 7; } impl HostInterrupt { - /// Bit value for general purpose host interrupt: 0. - pub const GENERAL_0: u32 = 1 << 0; - /// Bit value for general purpose host interrupt: 1. - pub const GENERAL_1: u32 = 1 << 1; - /// Bit value for general purpose host interrupt: 2. - pub const GENERAL_2: u32 = 1 << 2; - /// Bit value for general purpose host interrupt: 3. - pub const GENERAL_3: u32 = 1 << 3; - /// Bit value for general purpose host interrupt: 4. - pub const GENERAL_4: u32 = 1 << 4; - /// Bit value for general purpose host interrupt: 5. - pub const GENERAL_5: u32 = 1 << 5; - /// Bit value for general purpose host interrupt: 6. - pub const GENERAL_6: u32 = 1 << 6; - /// Bit value for general purpose host interrupt: 7. - pub const GENERAL_7: u32 = 1 << 7; - /// Bit value for new packet available to send to host. - pub const SEND_NEW_PACKET: u32 = 1 << 23; - /// Creates a new [HostInterrupt]. pub const fn new() -> Self { - Self::General0 + Self(0) } - /// Converts the [HostInterrupt] into a [`u32`]. - #[inline] - pub const fn to_u32(self) -> u32 { - self as u32 + /// Gets the raw bit value of the [HostInterrupt]. + pub const fn bits(&self) -> u8 { + self.0 } +} - /// Attempts to convert a [`u32`] into a [HostInterrupt]. - #[inline] - pub const fn try_from_u32(val: u32) -> Result { - match val { - Self::GENERAL_0 => Ok(Self::General0), - Self::GENERAL_1 => Ok(Self::General1), - Self::GENERAL_2 => Ok(Self::General2), - Self::GENERAL_3 => Ok(Self::General3), - Self::GENERAL_4 => Ok(Self::General4), - Self::GENERAL_5 => Ok(Self::General5), - Self::GENERAL_6 => Ok(Self::General6), - Self::GENERAL_7 => Ok(Self::General7), - Self::SEND_NEW_PACKET => Ok(Self::SendNewPacket), - _ => Err(Error::General), - } +impl From for u8 { + fn from(val: HostInterrupt) -> Self { + val.bits() } } -impl From for u32 { - fn from(val: HostInterrupt) -> Self { - val.to_u32() +impl From for HostInterrupt { + fn from(val: u8) -> Self { + Self(val) } } -impl TryFrom for HostInterrupt { - type Error = Error; +impl Default for HostInterrupt { + fn default() -> Self { + Self::new() + } +} - fn try_from(val: u32) -> Result { - Self::try_from_u32(val) +bitfield! { + /// 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. + #[repr(C)] + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + pub struct DeviceInterrupt(u8); + /// General purpose host interrupt: 0. + pub general0, set_general0: 0; + /// General purpose host interrupt: 1. + pub general1, set_general1: 1; + /// General purpose host interrupt: 2. + pub general2, set_general2: 2; + /// General purpose host interrupt: 3. + pub general3, set_general3: 3; + /// General purpose host interrupt: 4. + pub general4, set_general4: 4; + /// General purpose host interrupt: 5. + pub general5, set_general5: 5; + /// General purpose host interrupt: 6. + pub general6, set_general6: 6; + /// General purpose host interrupt: 7. + pub general7, set_general7: 7; +} + +impl DeviceInterrupt { + /// Creates a new [DeviceInterrupt]. + pub const fn new() -> Self { + Self(0) + } + + /// Gets the raw bit value of the [DeviceInterrupt]. + pub const fn bits(&self) -> u8 { + self.0 } } -impl Default for HostInterrupt { +impl From for u8 { + fn from(val: DeviceInterrupt) -> Self { + val.bits() + } +} + +impl From for DeviceInterrupt { + fn from(val: u8) -> Self { + Self(val) + } +} + +impl Default for DeviceInterrupt { fn default() -> Self { Self::new() } From 550a7ff10d8638e1fb92ab1fc3b2597c0473ad9b Mon Sep 17 00:00:00 2001 From: rmsyn Date: Thu, 26 Jun 2025 22:50:41 +0000 Subject: [PATCH 14/50] sdio: add low-level interrupt enable method Adds a method to perform low-level interrupt enabling for the SDIO device peripheral. --- esp-hal/src/sdio.rs | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/esp-hal/src/sdio.rs b/esp-hal/src/sdio.rs index 49f5c5bf533..a3267db94c7 100644 --- a/esp-hal/src/sdio.rs +++ b/esp-hal/src/sdio.rs @@ -31,7 +31,7 @@ mod timing; pub use config::Config; pub use hinf::{AnyHinf, HinfInfo, HinfInstance}; -pub use interrupt::HostInterrupt; +pub use interrupt::{DeviceInterrupt, HostInterrupt}; pub use pins::Pins; pub use slc::{AnySlc, SlcInfo, SlcInstance}; pub use slchost::{AnySlchost, SlchostInfo, SlchostInstance}; @@ -195,12 +195,12 @@ impl<'d> Sdio<'d> { self.low_level_init()?; self.low_level_enable_hs()?; self.low_level_set_timing()?; - - Err(Error::unimplemented()) + self.low_level_set_timing()?; + self.low_level_dev_interrupt_enable(0xffu8.into()) } /// Performs low-level initialization of the SDIO peripheral. - fn low_level_init(&mut self) -> Result<(), Error> { + pub(crate) fn low_level_init(&mut self) -> Result<(), Error> { let slc = unsafe { &*self.slc.info().register_block }; slc.slcconf0().modify(|_, w| { @@ -225,7 +225,7 @@ impl<'d> Sdio<'d> { } /// Sets the high-speed supported bit to be read by the host. - fn low_level_enable_hs(&mut self) -> Result<(), Error> { + pub(crate) fn low_level_enable_hs(&mut self) -> Result<(), Error> { let hinf = unsafe { &*self.hinf.info().register_block }; hinf.cfg_data1() @@ -234,7 +234,8 @@ impl<'d> Sdio<'d> { Ok(()) } - fn low_level_set_timing(&mut self) -> Result<(), Error> { + /// Sets the communication timing. + pub(crate) fn low_level_set_timing(&mut self) -> Result<(), Error> { let host = unsafe { &*self.slchost.info().register_block }; match self.config.timing() { @@ -274,6 +275,27 @@ impl<'d> Sdio<'d> { Ok(()) } + + /// Sets which device interrupts to enable based on the provided mask. + pub(crate) fn low_level_dev_interrupt_enable( + &self, + mask: DeviceInterrupt, + ) -> Result<(), Error> { + let slc = unsafe { &*self.slc.info().register_block }; + + slc.slc0int_ena().modify(|_, w| { + w.sdio_slc_frhost_bit0_int_ena().variant(mask.general0()); + w.sdio_slc_frhost_bit1_int_ena().variant(mask.general1()); + w.sdio_slc_frhost_bit2_int_ena().variant(mask.general2()); + w.sdio_slc_frhost_bit3_int_ena().variant(mask.general3()); + w.sdio_slc_frhost_bit4_int_ena().variant(mask.general4()); + w.sdio_slc_frhost_bit5_int_ena().variant(mask.general5()); + w.sdio_slc_frhost_bit6_int_ena().variant(mask.general6()); + w.sdio_slc_frhost_bit7_int_ena().variant(mask.general7()) + }); + + Ok(()) + } } /// Represents the error variants for SDIO peripherals. From 94357a8b619273109faa832197d4f8e01982b8d7 Mon Sep 17 00:00:00 2001 From: rmsyn Date: Fri, 27 Jun 2025 05:42:08 +0000 Subject: [PATCH 15/50] fixup: sdio: pac-specific init functions Uses conditional compilation to perform PAC-specific initialization routines. Mainly handles different register naming conventions. --- esp-hal/src/sdio.rs | 154 ++++++++++++++++++++++++++++++------ esp-hal/src/sdio/hinf.rs | 13 +++ esp-hal/src/sdio/pins.rs | 4 + esp-hal/src/sdio/slc.rs | 13 +++ esp-hal/src/sdio/slchost.rs | 13 +++ 5 files changed, 172 insertions(+), 25 deletions(-) diff --git a/esp-hal/src/sdio.rs b/esp-hal/src/sdio.rs index a3267db94c7..b24e297e641 100644 --- a/esp-hal/src/sdio.rs +++ b/esp-hal/src/sdio.rs @@ -18,7 +18,10 @@ use embedded_hal_sdmmc::{ tuning::{TuningMode, TuningWidth}, }; -use crate::gpio::{InputConfig, OutputConfig, Pull}; +use crate::{ + gpio::{InputConfig, OutputConfig, Pull}, + pac, +}; mod config; mod hinf; @@ -98,9 +101,10 @@ pub struct Sdio<'d> { impl<'d> Sdio<'d> { /// Creates a new [Sdio]. /// - /// # Example - #[doc = crate::before_snippet!()] + /// ## Example + /// /// ```rust, no_run + #[doc = crate::before_snippet!()] /// use esp_hal::sdio::{Config, Mode, Pins, Sdio}; /// /// let pins = Pins::new( @@ -116,12 +120,14 @@ impl<'d> Sdio<'d> { /// let config = Config::new(); /// /// let _sdio = Sdio::new( - /// peripherals.slc, - /// peripherals.slchost, - /// peripherals.hinf, + /// peripherals.SLC, + /// peripherals.SLCHOST, + /// peripherals.HINF, /// pins, /// config, /// ); + /// # Ok(()) + /// # } /// ``` pub fn new( slc: impl SlcInstance + 'd, @@ -145,16 +151,31 @@ impl<'d> Sdio<'d> { 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 SLCHOST 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 @@ -192,16 +213,42 @@ impl<'d> Sdio<'d> { /// Performs final low-level HAL hardware initialization. pub(crate) fn hardware_init(&mut self) -> Result<(), Error> { - self.low_level_init()?; - self.low_level_enable_hs()?; - self.low_level_set_timing()?; - self.low_level_set_timing()?; - self.low_level_dev_interrupt_enable(0xffu8.into()) + self.pac_init()?; + self.pac_enable_hs()?; + self.pac_set_timing()?; + self.pac_dev_interrupt_enable(0xffu8.into()) + } + + /// Performs low-level initialization of the SDIO peripheral. + #[cfg(feature = "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. - pub(crate) fn low_level_init(&mut self) -> Result<(), Error> { - let slc = unsafe { &*self.slc.info().register_block }; + #[cfg(feature = "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(); @@ -225,18 +272,62 @@ impl<'d> Sdio<'d> { } /// Sets the high-speed supported bit to be read by the host. - pub(crate) fn low_level_enable_hs(&mut self) -> Result<(), Error> { - let hinf = unsafe { &*self.hinf.info().register_block }; - - hinf.cfg_data1() + #[cfg(any(feature = "esp32", feature = "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. - pub(crate) fn low_level_set_timing(&mut self) -> Result<(), Error> { - let host = unsafe { &*self.slchost.info().register_block }; + #[cfg(feature = "esp32")] + fn pac_set_timing(&mut self) -> Result<(), Error> { + let host = self.slchost_block(); + + match self.config.timing() { + Timing::PsendPsample => { + host.host_slchost_conf().modify(|_, w| unsafe { + w.host_frc_sdio20().bits(0x1f); + w.host_frc_sdio11().bits(0x0); + w.host_frc_pos_samp().bits(0x1f); + w.host_frc_neg_samp().bits(0x0) + }); + } + Timing::PsendNsample => { + host.host_slchost_conf().modify(|_, w| unsafe { + w.host_frc_sdio20().bits(0x1f); + w.host_frc_sdio11().bits(0x0); + w.host_frc_pos_samp().bits(0x0); + w.host_frc_neg_samp().bits(0x1f) + }); + } + Timing::NsendPsample => { + host.host_slchost_conf().modify(|_, w| unsafe { + w.host_frc_sdio20().bits(0x0); + w.host_frc_sdio11().bits(0x1f); + w.host_frc_pos_samp().bits(0x1f); + w.host_frc_neg_samp().bits(0x0) + }); + } + Timing::NsendNsample => { + host.host_slchost_conf().modify(|_, w| unsafe { + w.host_frc_sdio20().bits(0x0); + w.host_frc_sdio11().bits(0x1f); + w.host_frc_pos_samp().bits(0x0); + w.host_frc_neg_samp().bits(0x1f) + }); + } + } + + Ok(()) + } + + /// Sets the communication timing. + #[cfg(feature = "esp32c6")] + fn pac_set_timing(&mut self) -> Result<(), Error> { + let host = self.slchost_block(); match self.config.timing() { Timing::PsendPsample => { @@ -277,13 +368,26 @@ impl<'d> Sdio<'d> { } /// Sets which device interrupts to enable based on the provided mask. - pub(crate) fn low_level_dev_interrupt_enable( - &self, - mask: DeviceInterrupt, - ) -> Result<(), Error> { - let slc = unsafe { &*self.slc.info().register_block }; + #[cfg(feature = "esp32")] + fn pac_dev_interrupt_enable(&self, mask: DeviceInterrupt) -> Result<(), Error> { + self.slc_block()._0int_ena().modify(|_, w| { + w.frhost_bit0_int_ena().variant(mask.general0()); + w.frhost_bit1_int_ena().variant(mask.general1()); + w.frhost_bit2_int_ena().variant(mask.general2()); + w.frhost_bit3_int_ena().variant(mask.general3()); + w.frhost_bit4_int_ena().variant(mask.general4()); + w.frhost_bit5_int_ena().variant(mask.general5()); + w.frhost_bit6_int_ena().variant(mask.general6()); + w.frhost_bit7_int_ena().variant(mask.general7()) + }); + + Ok(()) + } - slc.slc0int_ena().modify(|_, w| { + /// Sets which device interrupts to enable based on the provided mask. + #[cfg(feature = "esp32c6")] + fn pac_dev_interrupt_enable(&self, mask: DeviceInterrupt) -> Result<(), Error> { + self.slc_block().slc0int_ena().modify(|_, w| { w.sdio_slc_frhost_bit0_int_ena().variant(mask.general0()); w.sdio_slc_frhost_bit1_int_ena().variant(mask.general1()); w.sdio_slc_frhost_bit2_int_ena().variant(mask.general2()); diff --git a/esp-hal/src/sdio/hinf.rs b/esp-hal/src/sdio/hinf.rs index e3140049d23..8ace23ada66 100644 --- a/esp-hal/src/sdio/hinf.rs +++ b/esp-hal/src/sdio/hinf.rs @@ -28,7 +28,20 @@ impl PeripheralInstance for AnyHinf<'_> { } } +impl PeripheralInstance for crate::peripherals::HINF<'_> { + type Info = HinfInfo; + + fn info(&self) -> &'static Self::Info { + static INFO: HinfInfo = HinfInfo { + register_block: crate::peripherals::HINF::ptr(), + }; + + &INFO + } +} + /// A peripheral singleton compatible with the SDIO HINF driver. pub trait HinfInstance: PeripheralInstance + IntoAnyHinf {} impl HinfInstance for AnyHinf<'_> {} +impl HinfInstance for crate::peripherals::HINF<'_> {} diff --git a/esp-hal/src/sdio/pins.rs b/esp-hal/src/sdio/pins.rs index 7ebe16d72b4..d2d4fb627df 100644 --- a/esp-hal/src/sdio/pins.rs +++ b/esp-hal/src/sdio/pins.rs @@ -21,6 +21,8 @@ pub struct Pins<'d> { impl<'d> Pins<'d> { /// Creates a new [Pins] from the provided GPIO pins. /// + /// ## Example + /// /// ```rust, no_run #[doc = crate::before_snippet!()] /// use esp_hal::sdio::{Mode, Pins}; @@ -34,6 +36,8 @@ impl<'d> Pins<'d> { /// peripherals.GPIO22, // DAT2 /// peripherals.GPIO23, // DAT3/#CS /// ); + /// # Ok(()) + /// # } /// ``` pub fn new( mode: Mode, diff --git a/esp-hal/src/sdio/slc.rs b/esp-hal/src/sdio/slc.rs index 132f87509d0..8298c9628ed 100644 --- a/esp-hal/src/sdio/slc.rs +++ b/esp-hal/src/sdio/slc.rs @@ -28,7 +28,20 @@ impl PeripheralInstance for AnySlc<'_> { } } +impl PeripheralInstance for crate::peripherals::SLC<'_> { + type Info = SlcInfo; + + fn info(&self) -> &'static Self::Info { + static INFO: SlcInfo = SlcInfo { + register_block: crate::peripherals::SLC::ptr(), + }; + + &INFO + } +} + /// A peripheral singleton compatible with the SDIO SLC driver. pub trait SlcInstance: PeripheralInstance + IntoAnySlc {} 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 index 449e2036803..1c236765ee7 100644 --- a/esp-hal/src/sdio/slchost.rs +++ b/esp-hal/src/sdio/slchost.rs @@ -28,7 +28,20 @@ impl PeripheralInstance for AnySlchost<'_> { } } +impl PeripheralInstance for crate::peripherals::SLCHOST<'_> { + type Info = SlchostInfo; + + fn info(&self) -> &'static Self::Info { + static INFO: SlchostInfo = SlchostInfo { + register_block: crate::peripherals::SLCHOST::ptr(), + }; + + &INFO + } +} + /// A peripheral singleton compatible with the SDIO SLCHOST driver. pub trait SlchostInstance: PeripheralInstance + IntoAnySlchost {} impl SlchostInstance for AnySlchost<'_> {} +impl SlchostInstance for crate::peripherals::SLCHOST<'_> {} From 6886104f065d97ff5da883f852795a0f99a8dbbe Mon Sep 17 00:00:00 2001 From: rmsyn Date: Fri, 27 Jun 2025 21:29:20 +0000 Subject: [PATCH 16/50] fixup: sdio: apply suggestions from @bugadani Applies fixes for typos and conditional compilation suggested by @bugadani. --- esp-hal/src/sdio.rs | 95 ++++++++++++--------------------------------- 1 file changed, 25 insertions(+), 70 deletions(-) diff --git a/esp-hal/src/sdio.rs b/esp-hal/src/sdio.rs index b24e297e641..82eb33f5235 100644 --- a/esp-hal/src/sdio.rs +++ b/esp-hal/src/sdio.rs @@ -171,7 +171,7 @@ impl<'d> Sdio<'d> { self.hinf.info() } - /// Convenience function to get a reference to the SLCHOST register block. + /// Convenience function to get a reference to the HINF register block. fn hinf_block(&self) -> &'_ pac::hinf::RegisterBlock { unsafe { &*self.hinf().register_block } } @@ -282,89 +282,45 @@ impl<'d> Sdio<'d> { } /// Sets the communication timing. - #[cfg(feature = "esp32")] fn pac_set_timing(&mut self) -> Result<(), Error> { - let host = self.slchost_block(); - match self.config.timing() { Timing::PsendPsample => { - host.host_slchost_conf().modify(|_, w| unsafe { - w.host_frc_sdio20().bits(0x1f); - w.host_frc_sdio11().bits(0x0); - w.host_frc_pos_samp().bits(0x1f); - w.host_frc_neg_samp().bits(0x0) - }); + self.set_timing_registers(0x1f, 0x0, 0x1f, 0x0); } Timing::PsendNsample => { - host.host_slchost_conf().modify(|_, w| unsafe { - w.host_frc_sdio20().bits(0x1f); - w.host_frc_sdio11().bits(0x0); - w.host_frc_pos_samp().bits(0x0); - w.host_frc_neg_samp().bits(0x1f) - }); + self.set_timing_registers(0x1f, 0x0, 0x0, 0x1f); } Timing::NsendPsample => { - host.host_slchost_conf().modify(|_, w| unsafe { - w.host_frc_sdio20().bits(0x0); - w.host_frc_sdio11().bits(0x1f); - w.host_frc_pos_samp().bits(0x1f); - w.host_frc_neg_samp().bits(0x0) - }); + self.set_timing_registers(0x0, 0x1f, 0x1f, 0x0); } Timing::NsendNsample => { - host.host_slchost_conf().modify(|_, w| unsafe { - w.host_frc_sdio20().bits(0x0); - w.host_frc_sdio11().bits(0x1f); - w.host_frc_pos_samp().bits(0x0); - w.host_frc_neg_samp().bits(0x1f) - }); + self.set_timing_registers(0x0, 0x1f, 0x0, 0x1f); } } Ok(()) } - /// Sets the communication timing. - #[cfg(feature = "esp32c6")] - fn pac_set_timing(&mut self) -> Result<(), Error> { - let host = self.slchost_block(); - - match self.config.timing() { - Timing::PsendPsample => { - host.conf().modify(|_, w| unsafe { - w.frc_sdio20().bits(0x1f); - w.frc_sdio11().bits(0x0); - w.frc_pos_samp().bits(0x1f); - w.frc_neg_samp().bits(0x0) - }); - } - Timing::PsendNsample => { - host.conf().modify(|_, w| unsafe { - w.frc_sdio20().bits(0x1f); - w.frc_sdio11().bits(0x0); - w.frc_pos_samp().bits(0x0); - w.frc_neg_samp().bits(0x1f) - }); - } - Timing::NsendPsample => { - host.conf().modify(|_, w| unsafe { - w.frc_sdio20().bits(0x0); - w.frc_sdio11().bits(0x1f); - w.frc_pos_samp().bits(0x1f); - w.frc_neg_samp().bits(0x0) - }); - } - Timing::NsendNsample => { - host.conf().modify(|_, w| unsafe { - w.frc_sdio20().bits(0x0); - w.frc_sdio11().bits(0x1f); - w.frc_pos_samp().bits(0x0); - w.frc_neg_samp().bits(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. @@ -526,7 +482,6 @@ impl Common for Sdio<'_> { self.pins.dat3_cs.apply_input_config(&input_pullup); self.pins.dat3_cs.apply_output_config(&output_pullup); - // TODO: implement HAL hardware init self.hardware_init()?; self.state_transition(State::Standby) From 120b8f3241c79abdc04d81702849a78a7809281d Mon Sep 17 00:00:00 2001 From: rmsyn Date: Sat, 28 Jun 2025 01:57:51 +0000 Subject: [PATCH 17/50] fixup: sdio: remove PeripheralInstance trait Removes the `PeripheralInstance` trait, since it is uneeded for the SDIO peripherals. --- esp-hal/src/sdio.rs | 10 ---------- esp-hal/src/sdio/hinf.rs | 24 ++++-------------------- esp-hal/src/sdio/slc.rs | 24 ++++-------------------- esp-hal/src/sdio/slchost.rs | 26 +++++--------------------- 4 files changed, 13 insertions(+), 71 deletions(-) diff --git a/esp-hal/src/sdio.rs b/esp-hal/src/sdio.rs index 82eb33f5235..225479382ea 100644 --- a/esp-hal/src/sdio.rs +++ b/esp-hal/src/sdio.rs @@ -41,16 +41,6 @@ pub use slchost::{AnySlchost, SlchostInfo, SlchostInstance}; pub use state::State; pub use timing::Timing; -/// SDIO peripheral instance. -pub trait PeripheralInstance: crate::private::Sealed { - /// Represents the peripheral information type containing the register - /// block. - type Info; - - /// Gets a static shared reference to the peripheral information. - fn info(&self) -> &'static Self::Info; -} - /// Represents the transmission modes for the SDIO peripheral. #[repr(u8)] #[derive(Clone, Copy, Debug, Eq, PartialEq)] diff --git a/esp-hal/src/sdio/hinf.rs b/esp-hal/src/sdio/hinf.rs index 8ace23ada66..a55c1088c17 100644 --- a/esp-hal/src/sdio/hinf.rs +++ b/esp-hal/src/sdio/hinf.rs @@ -1,4 +1,3 @@ -use super::PeripheralInstance; use crate::pac::hinf; crate::any_peripheral! { @@ -16,22 +15,10 @@ pub struct HinfInfo { unsafe impl Sync for HinfInfo {} -impl PeripheralInstance for AnyHinf<'_> { - type Info = HinfInfo; - - fn info(&self) -> &'static Self::Info { - static INFO: HinfInfo = HinfInfo { - register_block: crate::peripherals::HINF::ptr(), - }; - - &INFO - } -} - -impl PeripheralInstance for crate::peripherals::HINF<'_> { - type Info = HinfInfo; - - fn info(&self) -> &'static Self::Info { +/// A peripheral singleton compatible with the SDIO HINF driver. +pub trait HinfInstance: IntoAnyHinf { + /// Gets a static reference the the [HinfInfo]. + fn info(&self) -> &'static HinfInfo { static INFO: HinfInfo = HinfInfo { register_block: crate::peripherals::HINF::ptr(), }; @@ -40,8 +27,5 @@ impl PeripheralInstance for crate::peripherals::HINF<'_> { } } -/// A peripheral singleton compatible with the SDIO HINF driver. -pub trait HinfInstance: PeripheralInstance + IntoAnyHinf {} - impl HinfInstance for AnyHinf<'_> {} impl HinfInstance for crate::peripherals::HINF<'_> {} diff --git a/esp-hal/src/sdio/slc.rs b/esp-hal/src/sdio/slc.rs index 8298c9628ed..aec3e1d020d 100644 --- a/esp-hal/src/sdio/slc.rs +++ b/esp-hal/src/sdio/slc.rs @@ -1,4 +1,3 @@ -use super::PeripheralInstance; use crate::pac::slc; crate::any_peripheral! { @@ -16,22 +15,10 @@ pub struct SlcInfo { unsafe impl Sync for SlcInfo {} -impl PeripheralInstance for AnySlc<'_> { - type Info = SlcInfo; - - fn info(&self) -> &'static Self::Info { - static INFO: SlcInfo = SlcInfo { - register_block: crate::peripherals::SLC::ptr(), - }; - - &INFO - } -} - -impl PeripheralInstance for crate::peripherals::SLC<'_> { - type Info = SlcInfo; - - fn info(&self) -> &'static Self::Info { +/// A peripheral singleton compatible with the SDIO SLC driver. +pub trait SlcInstance: IntoAnySlc { + /// Gets a static reference the the [SlcInfo]. + fn info(&self) -> &'static SlcInfo { static INFO: SlcInfo = SlcInfo { register_block: crate::peripherals::SLC::ptr(), }; @@ -40,8 +27,5 @@ impl PeripheralInstance for crate::peripherals::SLC<'_> { } } -/// A peripheral singleton compatible with the SDIO SLC driver. -pub trait SlcInstance: PeripheralInstance + IntoAnySlc {} - 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 index 1c236765ee7..90e88e73364 100644 --- a/esp-hal/src/sdio/slchost.rs +++ b/esp-hal/src/sdio/slchost.rs @@ -1,8 +1,7 @@ -use super::PeripheralInstance; use crate::pac::slchost; crate::any_peripheral! { - /// Any SDIO peripheral. + /// Any SDIO SLCHOST peripheral. pub peripheral AnySlchost<'d> { Slchost(crate::peripherals::SLCHOST<'d>) } @@ -16,22 +15,10 @@ pub struct SlchostInfo { unsafe impl Sync for SlchostInfo {} -impl PeripheralInstance for AnySlchost<'_> { - type Info = SlchostInfo; - - fn info(&self) -> &'static Self::Info { - static INFO: SlchostInfo = SlchostInfo { - register_block: crate::peripherals::SLCHOST::ptr(), - }; - - &INFO - } -} - -impl PeripheralInstance for crate::peripherals::SLCHOST<'_> { - type Info = SlchostInfo; - - fn info(&self) -> &'static Self::Info { +/// A peripheral singleton compatible with the SDIO SLCHOST driver. +pub trait SlchostInstance: IntoAnySlchost { + /// Gets a static reference the the [SlchostInfo]. + fn info(&self) -> &'static SlchostInfo { static INFO: SlchostInfo = SlchostInfo { register_block: crate::peripherals::SLCHOST::ptr(), }; @@ -40,8 +27,5 @@ impl PeripheralInstance for crate::peripherals::SLCHOST<'_> { } } -/// A peripheral singleton compatible with the SDIO SLCHOST driver. -pub trait SlchostInstance: PeripheralInstance + IntoAnySlchost {} - impl SlchostInstance for AnySlchost<'_> {} impl SlchostInstance for crate::peripherals::SLCHOST<'_> {} From 8b2465b00528a950f1a0c50a9eef09a2c977b3c1 Mon Sep 17 00:00:00 2001 From: rmsyn Date: Sun, 29 Jun 2025 22:59:48 +0000 Subject: [PATCH 18/50] metadata: add missing `SDIO_*` signals for ESP32-C6 Adds the missing `SDIO_*` GPIO signals for the ESP32-C6 device. --- .../src/_build_script_utils.rs | 2 + .../src/_generated_esp32.rs | 249 +++++++++++------- .../src/_generated_esp32c6.rs | 84 +++--- esp-metadata/devices/esp32.toml | 24 +- esp-metadata/devices/esp32c6.toml | 1 + 5 files changed, 214 insertions(+), 146 deletions(-) diff --git a/esp-metadata-generated/src/_build_script_utils.rs b/esp-metadata-generated/src/_build_script_utils.rs index ab7d92eb1e0..7fd0ffabe69 100644 --- a/esp-metadata-generated/src/_build_script_utils.rs +++ b/esp-metadata-generated/src/_build_script_utils.rs @@ -1101,6 +1101,7 @@ impl Chip { "soc_has_rng", "soc_has_rsa", "soc_has_sha", + "soc_has_slc", "soc_has_slchost", "soc_has_etm", "soc_has_spi0", @@ -1320,6 +1321,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 2c966debf64..a7f682c584e 100644 --- a/esp-metadata-generated/src/_generated_esp32.rs +++ b/esp-metadata-generated/src/_generated_esp32.rs @@ -501,6 +501,9 @@ macro_rules! for_each_peripheral { _for_each_inner!((GPIO0 <= virtual())); _for_each_inner!((GPIO1 <= virtual())); _for_each_inner!((GPIO2 <= virtual())); _for_each_inner!((GPIO3 <= virtual())); _for_each_inner!((GPIO4 <= virtual())); _for_each_inner!((GPIO5 <= virtual())); + _for_each_inner!((GPIO6 <= virtual())); _for_each_inner!((GPIO7 <= virtual())); + _for_each_inner!((GPIO8 <= virtual())); _for_each_inner!((GPIO9 <= virtual())); + _for_each_inner!((GPIO10 <= virtual())); _for_each_inner!((GPIO11 <= virtual())); _for_each_inner!((GPIO12 <= virtual())); _for_each_inner!((GPIO13 <= virtual())); _for_each_inner!((GPIO14 <= virtual())); _for_each_inner!((GPIO15 <= virtual())); _for_each_inner!((GPIO16 <= virtual())); _for_each_inner!((GPIO17 <= virtual())); @@ -568,50 +571,52 @@ macro_rules! for_each_peripheral { <= virtual() (unstable))); _for_each_inner!((TOUCH <= virtual() (unstable))); _for_each_inner!((all(GPIO0 <= virtual()), (GPIO1 <= virtual()), (GPIO2 <= virtual()), (GPIO3 <= virtual()), (GPIO4 <= virtual()), (GPIO5 <= virtual()), - (GPIO12 <= virtual()), (GPIO13 <= virtual()), (GPIO14 <= virtual()), (GPIO15 <= - virtual()), (GPIO16 <= virtual()), (GPIO17 <= virtual()), (GPIO18 <= virtual()), - (GPIO19 <= virtual()), (GPIO21 <= virtual()), (GPIO22 <= virtual()), (GPIO23 <= - virtual()), (GPIO25 <= virtual()), (GPIO26 <= virtual()), (GPIO27 <= virtual()), - (GPIO32 <= virtual()), (GPIO33 <= virtual()), (GPIO34 <= virtual()), (GPIO35 <= - virtual()), (GPIO36 <= virtual()), (GPIO37 <= virtual()), (GPIO38 <= virtual()), - (GPIO39 <= virtual()), (AES <= AES() (unstable)), (APB_CTRL <= APB_CTRL() - (unstable)), (BB <= BB() (unstable)), (DPORT <= DPORT() (unstable)), (SYSTEM <= - DPORT() (unstable)), (EFUSE <= EFUSE() (unstable)), (EMAC_DMA <= EMAC_DMA() - (unstable)), (EMAC_EXT <= EMAC_EXT() (unstable)), (EMAC_MAC <= EMAC_MAC() - (unstable)), (FLASH_ENCRYPTION <= FLASH_ENCRYPTION() (unstable)), (FRC_TIMER <= - FRC_TIMER() (unstable)), (GPIO <= GPIO() (unstable)), (GPIO_SD <= GPIO_SD() - (unstable)), (HINF <= HINF() (unstable)), (I2C0 <= I2C0(I2C_EXT0 : { - bind_peri_interrupt, enable_peri_interrupt, disable_peri_interrupt })), (I2C1 <= - I2C1(I2C_EXT1 : { bind_peri_interrupt, enable_peri_interrupt, - disable_peri_interrupt })), (I2S0 <= I2S0(I2S0 : { bind_peri_interrupt, - enable_peri_interrupt, disable_peri_interrupt }) (unstable)), (I2S1 <= I2S1(I2S1 - : { bind_peri_interrupt, enable_peri_interrupt, disable_peri_interrupt }) - (unstable)), (IO_MUX <= IO_MUX() (unstable)), (LEDC <= LEDC() (unstable)), - (MCPWM0 <= MCPWM0() (unstable)), (MCPWM1 <= MCPWM1() (unstable)), (NRX <= NRX() - (unstable)), (PCNT <= PCNT() (unstable)), (RMT <= RMT() (unstable)), (RNG <= - RNG() (unstable)), (RSA <= RSA(RSA : { bind_peri_interrupt, - enable_peri_interrupt, disable_peri_interrupt }) (unstable)), (LPWR <= RTC_CNTL() - (unstable)), (RTC_I2C <= RTC_I2C() (unstable)), (RTC_IO <= RTC_IO() (unstable)), - (SDHOST <= SDHOST() (unstable)), (SENS <= SENS() (unstable)), (SHA <= SHA() - (unstable)), (SLC <= SLC() (unstable)), (SLCHOST <= SLCHOST() (unstable)), (SPI0 - <= SPI0() (unstable)), (SPI1 <= SPI1() (unstable)), (SPI2 <= SPI2(SPI2_DMA : { - bind_dma_interrupt, enable_dma_interrupt, disable_dma_interrupt }, SPI2 : { - bind_peri_interrupt, enable_peri_interrupt, disable_peri_interrupt })), (SPI3 <= - SPI3(SPI3_DMA : { bind_dma_interrupt, enable_dma_interrupt, disable_dma_interrupt - }, SPI3 : { bind_peri_interrupt, enable_peri_interrupt, disable_peri_interrupt - })), (TIMG0 <= TIMG0() (unstable)), (TIMG1 <= TIMG1() (unstable)), (TWAI0 <= - TWAI0() (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 })), (UART2 <= - UART2(UART2 : { bind_peri_interrupt, enable_peri_interrupt, - disable_peri_interrupt })), (UHCI0 <= UHCI0() (unstable)), (UHCI1 <= UHCI1() - (unstable)), (WIFI <= WIFI() (unstable)), (DMA_SPI2 <= SPI2() (unstable)), - (DMA_SPI3 <= SPI3() (unstable)), (DMA_I2S0 <= I2S0() (unstable)), (DMA_I2S1 <= - I2S1() (unstable)), (ADC1 <= virtual() (unstable)), (ADC2 <= virtual() - (unstable)), (BT <= virtual() (unstable)), (CPU_CTRL <= virtual() (unstable)), - (DAC1 <= virtual() (unstable)), (DAC2 <= virtual() (unstable)), (FLASH <= - virtual() (unstable)), (PSRAM <= virtual() (unstable)), (SW_INTERRUPT <= - virtual() (unstable)), (TOUCH <= virtual() (unstable)))); + (GPIO6 <= virtual()), (GPIO7 <= virtual()), (GPIO8 <= virtual()), (GPIO9 <= + virtual()), (GPIO10 <= virtual()), (GPIO11 <= virtual()), (GPIO12 <= virtual()), + (GPIO13 <= virtual()), (GPIO14 <= virtual()), (GPIO15 <= virtual()), (GPIO16 <= + virtual()), (GPIO17 <= virtual()), (GPIO18 <= virtual()), (GPIO19 <= virtual()), + (GPIO21 <= virtual()), (GPIO22 <= virtual()), (GPIO23 <= virtual()), (GPIO25 <= + virtual()), (GPIO26 <= virtual()), (GPIO27 <= virtual()), (GPIO32 <= virtual()), + (GPIO33 <= virtual()), (GPIO34 <= virtual()), (GPIO35 <= virtual()), (GPIO36 <= + virtual()), (GPIO37 <= virtual()), (GPIO38 <= virtual()), (GPIO39 <= virtual()), + (AES <= AES() (unstable)), (APB_CTRL <= APB_CTRL() (unstable)), (BB <= BB() + (unstable)), (DPORT <= DPORT() (unstable)), (SYSTEM <= DPORT() (unstable)), + (EFUSE <= EFUSE() (unstable)), (EMAC_DMA <= EMAC_DMA() (unstable)), (EMAC_EXT <= + EMAC_EXT() (unstable)), (EMAC_MAC <= EMAC_MAC() (unstable)), (FLASH_ENCRYPTION <= + FLASH_ENCRYPTION() (unstable)), (FRC_TIMER <= FRC_TIMER() (unstable)), (GPIO <= + GPIO() (unstable)), (GPIO_SD <= GPIO_SD() (unstable)), (HINF <= HINF() + (unstable)), (I2C0 <= I2C0(I2C_EXT0 : { bind_peri_interrupt, + enable_peri_interrupt, disable_peri_interrupt })), (I2C1 <= I2C1(I2C_EXT1 : { + bind_peri_interrupt, enable_peri_interrupt, disable_peri_interrupt })), (I2S0 <= + I2S0(I2S0 : { bind_peri_interrupt, enable_peri_interrupt, disable_peri_interrupt + }) (unstable)), (I2S1 <= I2S1(I2S1 : { bind_peri_interrupt, + enable_peri_interrupt, disable_peri_interrupt }) (unstable)), (IO_MUX <= IO_MUX() + (unstable)), (LEDC <= LEDC() (unstable)), (MCPWM0 <= MCPWM0() (unstable)), + (MCPWM1 <= MCPWM1() (unstable)), (NRX <= NRX() (unstable)), (PCNT <= PCNT() + (unstable)), (RMT <= RMT() (unstable)), (RNG <= RNG() (unstable)), (RSA <= + RSA(RSA : { bind_peri_interrupt, enable_peri_interrupt, disable_peri_interrupt }) + (unstable)), (LPWR <= RTC_CNTL() (unstable)), (RTC_I2C <= RTC_I2C() (unstable)), + (RTC_IO <= RTC_IO() (unstable)), (SDHOST <= SDHOST() (unstable)), (SENS <= SENS() + (unstable)), (SHA <= SHA() (unstable)), (SLC <= SLC() (unstable)), (SLCHOST <= + SLCHOST() (unstable)), (SPI0 <= SPI0() (unstable)), (SPI1 <= SPI1() (unstable)), + (SPI2 <= SPI2(SPI2_DMA : { bind_dma_interrupt, enable_dma_interrupt, + disable_dma_interrupt }, SPI2 : { bind_peri_interrupt, enable_peri_interrupt, + disable_peri_interrupt })), (SPI3 <= SPI3(SPI3_DMA : { bind_dma_interrupt, + enable_dma_interrupt, disable_dma_interrupt }, SPI3 : { bind_peri_interrupt, + enable_peri_interrupt, disable_peri_interrupt })), (TIMG0 <= TIMG0() (unstable)), + (TIMG1 <= TIMG1() (unstable)), (TWAI0 <= TWAI0() (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 })), (UART2 <= UART2(UART2 : { + bind_peri_interrupt, enable_peri_interrupt, disable_peri_interrupt })), (UHCI0 <= + UHCI0() (unstable)), (UHCI1 <= UHCI1() (unstable)), (WIFI <= WIFI() (unstable)), + (DMA_SPI2 <= SPI2() (unstable)), (DMA_SPI3 <= SPI3() (unstable)), (DMA_I2S0 <= + I2S0() (unstable)), (DMA_I2S1 <= I2S1() (unstable)), (ADC1 <= virtual() + (unstable)), (ADC2 <= virtual() (unstable)), (BT <= virtual() (unstable)), + (CPU_CTRL <= virtual() (unstable)), (DAC1 <= virtual() (unstable)), (DAC2 <= + virtual() (unstable)), (FLASH <= virtual() (unstable)), (PSRAM <= virtual() + (unstable)), (SW_INTERRUPT <= virtual() (unstable)), (TOUCH <= virtual() + (unstable)))); }; } /// This macro can be used to generate code for each `GPIOn` instance. @@ -648,67 +653,89 @@ 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!((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!((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() () ([Input] []))); - _for_each_inner!((38, GPIO38() () ([Input] []))); _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])), (12, GPIO12(_0 => MTDI _1 => HSPIQ _3 => + 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 + => 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 => + 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 => 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 _4 => SD_CLK) (_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!((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() + () ([Input] []))); _for_each_inner!((38, GPIO38() () ([Input] []))); + _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 => 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 => 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] [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 => U0CTS) (_1 => - VSPIQ _5 => EMAC_TXD0) ([Input] [Output])), (21, GPIO21(_1 => VSPIHD) (_1 => - VSPIHD _5 => EMAC_TX_EN) ([Input] [Output])), (22, GPIO22(_1 => VSPIWP) (_1 => - VSPIWP _3 => U0RTS _5 => EMAC_TXD1) ([Input] [Output])), (23, GPIO23(_1 => VSPID) - (_1 => VSPID _3 => HS1_STROBE) ([Input] [Output])), (25, GPIO25(_5 => EMAC_RXD0) - () ([Input] [Output])), (26, GPIO26(_5 => EMAC_RXD1) () ([Input] [Output])), (27, - GPIO27(_5 => EMAC_RX_DV) () ([Input] [Output])), (32, GPIO32() () ([Input] - [Output])), (33, GPIO33() () ([Input] [Output])), (34, GPIO34() () ([Input] [])), - (35, GPIO35() () ([Input] [])), (36, GPIO36() () ([Input] [])), (37, GPIO37() () - ([Input] [])), (38, GPIO38() () ([Input] [])), (39, GPIO39() () ([Input] [])))); + HSPICLK _4 => SD_CLK) (_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] + [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 => + U0CTS) (_1 => VSPIQ _5 => EMAC_TXD0) ([Input] [Output])), (21, GPIO21(_1 => + VSPIHD) (_1 => VSPIHD _5 => EMAC_TX_EN) ([Input] [Output])), (22, GPIO22(_1 => + VSPIWP) (_1 => VSPIWP _3 => U0RTS _5 => EMAC_TXD1) ([Input] [Output])), (23, + GPIO23(_1 => VSPID) (_1 => VSPID _3 => HS1_STROBE) ([Input] [Output])), (25, + GPIO25(_5 => EMAC_RXD0) () ([Input] [Output])), (26, GPIO26(_5 => EMAC_RXD1) () + ([Input] [Output])), (27, GPIO27(_5 => EMAC_RX_DV) () ([Input] [Output])), (32, + GPIO32() () ([Input] [Output])), (33, GPIO33() () ([Input] [Output])), (34, + GPIO34() () ([Input] [])), (35, GPIO35() () ([Input] [])), (36, GPIO36() () + ([Input] [])), (37, GPIO37() () ([Input] [])), (38, GPIO38() () ([Input] [])), + (39, GPIO39() () ([Input] [])))); }; } /// This macro can be used to generate code for each analog function of each GPIO. @@ -1053,10 +1080,16 @@ macro_rules! define_io_mux_signals { PCMCLK = 205, PCMDIN = 206, SD_CMD, + SD_CLK, SD_DATA0, SD_DATA1, SD_DATA2, SD_DATA3, + SDIO_CMD, + SDIO_DATA0, + SDIO_DATA1, + SDIO_DATA2, + SDIO_DATA3, HS1_DATA0, HS1_DATA1, HS1_DATA2, @@ -1262,12 +1295,18 @@ macro_rules! define_io_mux_signals { CLK_OUT1, CLK_OUT2, CLK_OUT3, - SD_CLK, SD_CMD, + SD_CLK, 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, @@ -1325,6 +1364,12 @@ macro_rules! define_io_mux_reg { 3 => iomux.gpio3(), 4 => iomux.gpio4(), 5 => iomux.gpio5(), + 6 => iomux.gpio6(), + 7 => iomux.gpio7(), + 8 => iomux.gpio8(), + 9 => iomux.gpio9(), + 10 => iomux.gpio10(), + 11 => iomux.gpio11(), 12 => iomux.gpio12(), 13 => iomux.gpio13(), 14 => iomux.gpio14(), diff --git a/esp-metadata-generated/src/_generated_esp32c6.rs b/esp-metadata-generated/src/_generated_esp32c6.rs index 77fdbbf7c8b..3b17a5dd70b 100644 --- a/esp-metadata-generated/src/_generated_esp32c6.rs +++ b/esp-metadata-generated/src/_generated_esp32c6.rs @@ -616,26 +616,27 @@ 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!((FLASH <= - virtual() (unstable))); _for_each_inner!((LP_CORE <= 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!((FLASH <= 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 @@ -682,27 +683,28 @@ macro_rules! for_each_peripheral { <= PMU() (unstable)), (RMT <= RMT() (unstable)), (RNG <= 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)), (FLASH <= 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)))); + disable_peri_interrupt }) (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)), (FLASH <= 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 9d9215763af..9d35483143e 100644 --- a/esp-metadata/devices/esp32.toml +++ b/esp-metadata/devices/esp32.toml @@ -143,10 +143,16 @@ 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 = "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 = "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" } }, @@ -327,10 +333,16 @@ input_signals = [ { name = "PCMDIN", id = 206 }, { name = "SD_CMD" }, + { name = "SD_CLK" }, { 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" }, @@ -535,12 +547,18 @@ output_signals = [ { name = "CLK_OUT1" }, { name = "CLK_OUT2" }, { name = "CLK_OUT3" }, - { name = "SD_CLK" }, { name = "SD_CMD" }, + { name = "SD_CLK" }, { 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 fc5c6dd9dff..f0675bf5592 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" }, From e0065453148f6e1e256d8269423f61ea59326537 Mon Sep 17 00:00:00 2001 From: rmsyn Date: Sun, 29 Jun 2025 23:22:28 +0000 Subject: [PATCH 19/50] sdio: refactor pins to use `OutputSignal` Refactors the `sdio::Pins` type to use the `gpio::PinGuard` + `gpio::interconnect::{InputSignal, OutputSignal}` types instead of the lower-level `Pin` types. --- esp-hal/src/gpio/mod.rs | 2 +- esp-hal/src/sdio.rs | 38 +------- esp-hal/src/sdio/pins.rs | 183 +++++++++++++++++++++++++++++---------- 3 files changed, 143 insertions(+), 80 deletions(-) diff --git a/esp-hal/src/gpio/mod.rs b/esp-hal/src/gpio/mod.rs index c501996e45d..e425affe984 100644 --- a/esp-hal/src/gpio/mod.rs +++ b/esp-hal/src/gpio/mod.rs @@ -1175,7 +1175,7 @@ impl<'d> Input<'d> { /// use esp_hal::gpio::{Input, InputConfig, Level}; /// let pin = Input::new(peripherals.GPIO5, InputConfig::default()); /// let level = pin.level(); - /// +/// /// # {after_snippet} /// ``` #[inline] diff --git a/esp-hal/src/sdio.rs b/esp-hal/src/sdio.rs index 225479382ea..36843a255f6 100644 --- a/esp-hal/src/sdio.rs +++ b/esp-hal/src/sdio.rs @@ -18,10 +18,7 @@ use embedded_hal_sdmmc::{ tuning::{TuningMode, TuningWidth}, }; -use crate::{ - gpio::{InputConfig, OutputConfig, Pull}, - pac, -}; +use crate::pac; mod config; mod hinf; @@ -83,7 +80,7 @@ pub struct Sdio<'d> { slc: AnySlc<'d>, slchost: AnySlchost<'d>, hinf: AnyHinf<'d>, - pins: Pins<'d>, + pins: Pins, config: Config, state: State, } @@ -123,7 +120,7 @@ impl<'d> Sdio<'d> { slc: impl SlcInstance + 'd, slchost: impl SlchostInstance + 'd, hinf: impl HinfInstance + 'd, - pins: Pins<'d>, + pins: Pins, config: Config, ) -> Self { Self { @@ -167,7 +164,7 @@ impl<'d> Sdio<'d> { } /// Gets a reference to the [Pins] information. - pub const fn pins(&self) -> &Pins<'_> { + pub const fn pins(&self) -> &Pins { &self.pins } @@ -445,33 +442,6 @@ impl Common for Sdio<'_> { fn init(&mut self) -> Result<(), Error> { // This implementation is based on the `esp-idf` driver: // - self.pins.clk_sclk.apply_config(&OutputConfig::default()); - - let input_pullup = InputConfig::default().with_pull(Pull::Up); - let output_pullup = OutputConfig::default().with_pull(Pull::Up); - - self.pins.cmd_mosi.apply_input_config(&input_pullup); - self.pins.cmd_mosi.apply_output_config(&output_pullup); - - self.pins.dat0_miso.apply_input_config(&input_pullup); - self.pins.dat0_miso.apply_output_config(&output_pullup); - - // TODO: Add an Configuration struct, and a SdioDevice::config member - // TODO: test config host_intr disabled - // if self.config.host_intr() { - self.pins.dat1_irq.apply_input_config(&input_pullup); - self.pins.dat1_irq.apply_output_config(&output_pullup); - // } - // - // TODO: test config dat2 disabled - // if self.config.dat2() { - self.pins.dat2.apply_input_config(&input_pullup); - self.pins.dat2.apply_output_config(&output_pullup); - // } - - self.pins.dat3_cs.apply_input_config(&input_pullup); - self.pins.dat3_cs.apply_output_config(&output_pullup); - self.hardware_init()?; self.state_transition(State::Standby) diff --git a/esp-hal/src/sdio/pins.rs b/esp-hal/src/sdio/pins.rs index d2d4fb627df..b920d515082 100644 --- a/esp-hal/src/sdio/pins.rs +++ b/esp-hal/src/sdio/pins.rs @@ -1,5 +1,30 @@ use super::{Error, Mode}; -use crate::gpio::{Flex, Level, Output, OutputConfig, OutputPin, Pin}; +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. /// @@ -8,17 +33,17 @@ use crate::gpio::{Flex, Level, Output, OutputConfig, OutputPin, Pin}; /// The CLK/SCLK pin is configured as an output, all other pins are input/output /// `Flex` pins. #[derive(Debug)] -pub struct Pins<'d> { +pub struct Pins { mode: Mode, - pub(crate) clk_sclk: Output<'d>, - pub(crate) cmd_mosi: Flex<'d>, - pub(crate) dat0_miso: Flex<'d>, - pub(crate) dat1_irq: Flex<'d>, - pub(crate) dat2: Flex<'d>, - pub(crate) dat3_cs: Flex<'d>, + 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<'d> Pins<'d> { +impl Pins { /// Creates a new [Pins] from the provided GPIO pins. /// /// ## Example @@ -39,24 +64,92 @@ impl<'d> Pins<'d> { /// # Ok(()) /// # } /// ``` - pub fn new( + pub fn new<'clk, 'cmd, 'd0, 'd1, 'd2, 'd3, CLK, CMD, DATA0, DATA1, DATA2, DATA3>( mode: Mode, - clk_sclk: impl OutputPin + 'd, - cmd_mosi: impl Pin + 'd, - dat0_miso: impl Pin + 'd, - dat1_irq: impl Pin + 'd, - dat2: impl Pin + 'd, - dat3_cs: impl Pin + 'd, - ) -> Self { + 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: Output::new(clk_sclk, Level::Low, OutputConfig::default()), - cmd_mosi: Flex::new(cmd_mosi), - dat0_miso: Flex::new(dat0_miso), - dat1_irq: Flex::new(dat1_irq), - dat2: Flex::new(dat2), - dat3_cs: Flex::new(dat3_cs), + 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]. @@ -72,9 +165,9 @@ impl<'d> Pins<'d> { /// Gets the CLK signal for the [Pins]. /// /// Returns an [Error] if the [Mode] is SPI. - pub const fn clk(&self) -> Result<&Output<'_>, Error> { + pub fn clk(&self) -> Result, Error> { match self.mode { - Mode::Sd1bit | Mode::Sd4bit => Ok(&self.clk_sclk), + Mode::Sd1bit | Mode::Sd4bit => self.unguard_pin(SdioPin::Clk), Mode::Spi => Err(Error::General), } } @@ -82,9 +175,9 @@ impl<'d> Pins<'d> { /// Gets the SCLK signal for the [Pins]. /// /// Returns an [Error] if the [Mode] is not SPI. - pub const fn sclk(&self) -> Result<&Output<'_>, Error> { + pub fn sclk(&self) -> Result, Error> { match self.mode { - Mode::Spi => Ok(&self.clk_sclk), + Mode::Spi => self.unguard_pin(SdioPin::Clk), Mode::Sd1bit | Mode::Sd4bit => Err(Error::General), } } @@ -92,9 +185,9 @@ impl<'d> Pins<'d> { /// Gets the CMD signal for the [Pins]. /// /// Returns an [Error] if the [Mode] is SPI. - pub const fn cmd(&self) -> Result<&Flex<'_>, Error> { + pub fn cmd(&self) -> Result, Error> { match self.mode { - Mode::Sd1bit | Mode::Sd4bit => Ok(&self.cmd_mosi), + Mode::Sd1bit | Mode::Sd4bit => self.unguard_pin(SdioPin::Cmd), Mode::Spi => Err(Error::General), } } @@ -102,9 +195,9 @@ impl<'d> Pins<'d> { /// Gets the MOSI signal for the [Pins]. /// /// Returns an [Error] if the [Mode] is not SPI. - pub const fn mosi(&self) -> Result<&Flex<'_>, Error> { + pub fn mosi(&self) -> Result, Error> { match self.mode { - Mode::Spi => Ok(&self.cmd_mosi), + Mode::Spi => self.unguard_pin(SdioPin::Cmd), Mode::Sd1bit | Mode::Sd4bit => Err(Error::General), } } @@ -112,9 +205,9 @@ impl<'d> Pins<'d> { /// Gets the DAT0 signal for the [Pins]. /// /// Returns an [Error] if the [Mode] is SPI. - pub const fn dat0(&self) -> Result<&Flex<'_>, Error> { + pub fn dat0(&self) -> Result, Error> { match self.mode { - Mode::Sd1bit | Mode::Sd4bit => Ok(&self.dat0_miso), + Mode::Sd1bit | Mode::Sd4bit => self.unguard_pin(SdioPin::Data0), Mode::Spi => Err(Error::General), } } @@ -122,9 +215,9 @@ impl<'d> Pins<'d> { /// Gets the MISO signal for the [Pins]. /// /// Returns an [Error] if the [Mode] is not SPI. - pub const fn miso(&self) -> Result<&Flex<'_>, Error> { + pub fn miso(&self) -> Result, Error> { match self.mode { - Mode::Spi => Ok(&self.cmd_mosi), + Mode::Spi => self.unguard_pin(SdioPin::Data0), Mode::Sd1bit | Mode::Sd4bit => Err(Error::General), } } @@ -132,9 +225,9 @@ impl<'d> Pins<'d> { /// Gets the DAT1 signal for the [Pins]. /// /// Returns an [Error] if the [Mode] is not SD 4-bit. - pub const fn dat1(&self) -> Result<&Flex<'_>, Error> { + pub fn dat1(&self) -> Result, Error> { match self.mode { - Mode::Sd1bit | Mode::Sd4bit => Ok(&self.dat1_irq), + Mode::Sd1bit | Mode::Sd4bit => self.unguard_pin(SdioPin::Data1), Mode::Spi => Err(Error::General), } } @@ -142,9 +235,9 @@ impl<'d> Pins<'d> { /// Gets the IRQ signal for the [Pins]. /// /// Returns an [Error] if the [Mode] is SD 4-bit. - pub const fn irq(&self) -> Result<&Flex<'_>, Error> { + pub fn irq(&self) -> Result, Error> { match self.mode { - Mode::Spi => Ok(&self.dat1_irq), + Mode::Spi => self.unguard_pin(SdioPin::Data1), Mode::Sd1bit | Mode::Sd4bit => Err(Error::General), } } @@ -152,9 +245,9 @@ impl<'d> Pins<'d> { /// Gets the DAT2 signal for the [Pins]. /// /// Returns an [Error] if the [Mode] is not SD 4-bit. - pub const fn dat2(&self) -> Result<&Flex<'_>, Error> { + pub fn dat2(&self) -> Result, Error> { match self.mode { - Mode::Sd1bit | Mode::Sd4bit => Ok(&self.dat2), + Mode::Sd1bit | Mode::Sd4bit => self.unguard_pin(SdioPin::Data2), Mode::Spi => Err(Error::General), } } @@ -162,9 +255,9 @@ impl<'d> Pins<'d> { /// Gets the DAT3 signal for the [Pins]. /// /// Returns an [Error] if the [Mode] is SPI. - pub const fn dat3(&self) -> Result<&Flex<'_>, Error> { + pub fn dat3(&self) -> Result, Error> { match self.mode { - Mode::Sd1bit | Mode::Sd4bit => Ok(&self.dat3_cs), + Mode::Sd1bit | Mode::Sd4bit => self.unguard_pin(SdioPin::Data3), Mode::Spi => Err(Error::General), } } @@ -172,9 +265,9 @@ impl<'d> Pins<'d> { /// Gets the CS signal for the [Pins]. /// /// Returns an [Error] if the [Mode] is not SPI. - pub const fn cs(&self) -> Result<&Flex<'_>, Error> { + pub fn cs(&self) -> Result, Error> { match self.mode { - Mode::Spi => Ok(&self.dat3_cs), + Mode::Spi => self.unguard_pin(SdioPin::Data3), Mode::Sd1bit | Mode::Sd4bit => Err(Error::General), } } From 42a13370de6a06caea183380f2f3657b0ab4c19e Mon Sep 17 00:00:00 2001 From: rmsyn Date: Sun, 29 Jun 2025 23:39:15 +0000 Subject: [PATCH 20/50] fixup: sdio: fix conditional compilation selectors Applies fixes for coniditional compilation selectors to match dev style guide. --- esp-hal/src/lib.rs | 2 +- esp-hal/src/sdio.rs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/esp-hal/src/lib.rs b/esp-hal/src/lib.rs index 8adb3cb2e51..9276a0f8e2b 100644 --- a/esp-hal/src/lib.rs +++ b/esp-hal/src/lib.rs @@ -244,7 +244,7 @@ pub mod i2c; pub mod peripherals; #[cfg(all(feature = "unstable", any(soc_has_hmac, soc_has_sha)))] mod reg_access; -#[cfg(all(feature = "unstable", any(feature = "esp32", feature = "esp32c6")))] +#[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; diff --git a/esp-hal/src/sdio.rs b/esp-hal/src/sdio.rs index 36843a255f6..f871aecf91c 100644 --- a/esp-hal/src/sdio.rs +++ b/esp-hal/src/sdio.rs @@ -207,7 +207,7 @@ impl<'d> Sdio<'d> { } /// Performs low-level initialization of the SDIO peripheral. - #[cfg(feature = "esp32")] + #[cfg(esp32)] fn pac_init(&mut self) -> Result<(), Error> { let slc = self.slc_block(); @@ -233,7 +233,7 @@ impl<'d> Sdio<'d> { } /// Performs low-level initialization of the SDIO peripheral. - #[cfg(feature = "esp32c6")] + #[cfg(esp32c6)] fn pac_init(&mut self) -> Result<(), Error> { let slc = self.slc_block(); @@ -259,7 +259,7 @@ impl<'d> Sdio<'d> { } /// Sets the high-speed supported bit to be read by the host. - #[cfg(any(feature = "esp32", feature = "esp32c6"))] + #[cfg(any(esp32, esp32c6))] fn pac_enable_hs(&mut self) -> Result<(), Error> { self.hinf_block() .cfg_data1() @@ -311,7 +311,7 @@ impl<'d> Sdio<'d> { } /// Sets which device interrupts to enable based on the provided mask. - #[cfg(feature = "esp32")] + #[cfg(esp32)] fn pac_dev_interrupt_enable(&self, mask: DeviceInterrupt) -> Result<(), Error> { self.slc_block()._0int_ena().modify(|_, w| { w.frhost_bit0_int_ena().variant(mask.general0()); @@ -328,7 +328,7 @@ impl<'d> Sdio<'d> { } /// Sets which device interrupts to enable based on the provided mask. - #[cfg(feature = "esp32c6")] + #[cfg(esp32c6)] fn pac_dev_interrupt_enable(&self, mask: DeviceInterrupt) -> Result<(), Error> { self.slc_block().slc0int_ena().modify(|_, w| { w.sdio_slc_frhost_bit0_int_ena().variant(mask.general0()); From abbd9a1aade8c59c7527d6b6b63fbe082f14ee7d Mon Sep 17 00:00:00 2001 From: rmsyn Date: Thu, 3 Jul 2025 02:53:17 +0000 Subject: [PATCH 21/50] fixup: sdio: remove unnecessary `IntoAny*` impls Removes the now defunct `IntoAny*` impls for SDIO peripherals. --- esp-hal/src/sdio/hinf.rs | 2 +- esp-hal/src/sdio/slc.rs | 2 +- esp-hal/src/sdio/slchost.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/esp-hal/src/sdio/hinf.rs b/esp-hal/src/sdio/hinf.rs index a55c1088c17..dc686ff0deb 100644 --- a/esp-hal/src/sdio/hinf.rs +++ b/esp-hal/src/sdio/hinf.rs @@ -16,7 +16,7 @@ pub struct HinfInfo { unsafe impl Sync for HinfInfo {} /// A peripheral singleton compatible with the SDIO HINF driver. -pub trait HinfInstance: IntoAnyHinf { +pub trait HinfInstance: any::Degrade { /// Gets a static reference the the [HinfInfo]. fn info(&self) -> &'static HinfInfo { static INFO: HinfInfo = HinfInfo { diff --git a/esp-hal/src/sdio/slc.rs b/esp-hal/src/sdio/slc.rs index aec3e1d020d..ebd8a6616a0 100644 --- a/esp-hal/src/sdio/slc.rs +++ b/esp-hal/src/sdio/slc.rs @@ -16,7 +16,7 @@ pub struct SlcInfo { unsafe impl Sync for SlcInfo {} /// A peripheral singleton compatible with the SDIO SLC driver. -pub trait SlcInstance: IntoAnySlc { +pub trait SlcInstance: any::Degrade { /// Gets a static reference the the [SlcInfo]. fn info(&self) -> &'static SlcInfo { static INFO: SlcInfo = SlcInfo { diff --git a/esp-hal/src/sdio/slchost.rs b/esp-hal/src/sdio/slchost.rs index 90e88e73364..8260b85296f 100644 --- a/esp-hal/src/sdio/slchost.rs +++ b/esp-hal/src/sdio/slchost.rs @@ -16,7 +16,7 @@ pub struct SlchostInfo { unsafe impl Sync for SlchostInfo {} /// A peripheral singleton compatible with the SDIO SLCHOST driver. -pub trait SlchostInstance: IntoAnySlchost { +pub trait SlchostInstance: any::Degrade { /// Gets a static reference the the [SlchostInfo]. fn info(&self) -> &'static SlchostInfo { static INFO: SlchostInfo = SlchostInfo { From c73daf41aec9bac3cb4f048ddf47f11b082590d8 Mon Sep 17 00:00:00 2001 From: rmsyn Date: Sat, 5 Jul 2025 02:28:36 +0000 Subject: [PATCH 22/50] dma: add `Owner` bool conversion --- esp-hal/src/dma/mod.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/esp-hal/src/dma/mod.rs b/esp-hal/src/dma/mod.rs index 40637a6adf2..cd2f9628b9c 100644 --- a/esp-hal/src/dma/mod.rs +++ b/esp-hal/src/dma/mod.rs @@ -940,6 +940,24 @@ 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, + } + } +} + #[doc(hidden)] #[instability::unstable] pub trait DmaEligible { From d939f2879f8cb0e3af94a08ec37974d974e7f706 Mon Sep 17 00:00:00 2001 From: rmsyn Date: Sat, 5 Jul 2025 02:31:30 +0000 Subject: [PATCH 23/50] sdio: add DMA descriptor types Adds DMA descriptor types based on the main DMA engine descriptors. The main difference is the bitfield layout used in the descriptor flags. --- esp-hal/src/sdio.rs | 2 + esp-hal/src/sdio/dma.rs | 194 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 196 insertions(+) create mode 100644 esp-hal/src/sdio/dma.rs diff --git a/esp-hal/src/sdio.rs b/esp-hal/src/sdio.rs index f871aecf91c..e2192b8b25b 100644 --- a/esp-hal/src/sdio.rs +++ b/esp-hal/src/sdio.rs @@ -21,6 +21,7 @@ use embedded_hal_sdmmc::{ use crate::pac; mod config; +mod dma; mod hinf; mod interrupt; mod pins; @@ -30,6 +31,7 @@ mod state; mod timing; pub use config::Config; +pub use dma::{DmaDescriptor, DmaDescriptorFlags}; pub use hinf::{AnyHinf, HinfInfo, HinfInstance}; pub use interrupt::{DeviceInterrupt, HostInterrupt}; pub use pins::Pins; diff --git a/esp-hal/src/sdio/dma.rs b/esp-hal/src/sdio/dma.rs new file mode 100644 index 00000000000..13e0c2e4ab3 --- /dev/null +++ b/esp-hal/src/sdio/dma.rs @@ -0,0 +1,194 @@ +//! SDIO DMA types for SoCs with a dedicated engine. + +use core::fmt::Debug; + +use crate::dma::Owner; + +bitfield::bitfield! { + /// DMA descriptor flags for the dedicated SDIO DMA engine. + #[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: 13, 0; + + /// 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: 27, 14; + + /// 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; + + /// Specifies who is allowed to access the buffer that this descriptor points to. + /// - 0: CPU can access the buffer; + /// - 1: The SDIO DMA controller can access the buffer. + pub owner, set_owner: 31; +} + +impl DmaDescriptorFlags { + /// Creates a new [DmaDescriptorFlags]. + pub const fn new() -> Self { + Self(0) + } +} + +impl Default for DmaDescriptorFlags { + fn default() -> Self { + Self::new() + } +} + +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("suc_eof", &self.suc_eof()) + .field("owner", &(if self.owner() { "DMA" } else { "CPU" })) + .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.length(), + self.suc_eof(), + if self.owner() { "DMA" } else { "CPU" } + ); + } +} + +/// A DMA transfer descriptor. +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct DmaDescriptor { + /// Descriptor flags. + pub flags: DmaDescriptorFlags, + + /// Address of the buffer. + pub buffer: *mut u8, + + /// 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, +} + +impl DmaDescriptor { + /// Creates an empty DMA descriptor used to initialize the descriptor list. + pub const fn new() -> Self { + Self { + flags: DmaDescriptorFlags::new(), + buffer: core::ptr::null_mut(), + next: core::ptr::null_mut(), + } + } + + /// Resets the descriptor for a new receive transfer. + pub fn reset_for_rx(&mut self) { + // Give ownership to the DMA + self.set_owner(Owner::Dma); + + // Clear this to allow hardware to set it when the peripheral returns an EOF + // bit. + self.set_suc_eof(false); + + // Clear this to allow hardware to set it when it's + // done receiving data for this descriptor. + self.set_length(0); + } + + /// Resets the descriptor for a new transmit transfer. See + /// [DmaDescriptorFlags::suc_eof] for more details on the `set_eof` + /// parameter. + pub fn reset_for_tx(&mut self, set_eof: bool) { + // Give ownership to the DMA + self.set_owner(Owner::Dma); + + // The `suc_eof` bit doesn't affect the transfer itself, but signals when the + // hardware should trigger an interrupt request. + self.set_suc_eof(set_eof); + } + + /// Returns the size of the buffer. See [DmaDescriptorFlags::size]. + pub fn size(&self) -> usize { + self.flags.size() as usize + } + + /// Set the size of the buffer. See [DmaDescriptorFlags::size]. + pub fn set_size(&mut self, len: usize) { + self.flags.set_size(len as u16) + } + + /// 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 + } + + /// Set the length of the descriptor. See [DmaDescriptorFlags::length]. + pub fn set_length(&mut self, len: usize) { + self.flags.set_length(len as u16) + } + + /// Returns the suc_eof bit. See [DmaDescriptorFlags::suc_eof]. + pub fn suc_eof(&self) -> bool { + self.flags.suc_eof() + } + + /// 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) + } + + /// Returns the owner. See [DmaDescriptorFlags::owner]. + pub fn owner(&self) -> Owner { + self.flags.owner().into() + } + + /// Set the owner. See [DmaDescriptorFlags::owner]. + pub fn set_owner(&mut self, owner: Owner) { + self.flags.set_owner(owner.into()) + } +} + +impl Default for DmaDescriptor { + fn default() -> Self { + Self::new() + } +} + +// 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 {} + +#[cfg(feature = "defmt")] +impl defmt::Format for DmaDescriptor { + fn format(&self, fmt: defmt::Formatter<'_>) { + defmt::write!( + fmt, + "flags: {}, buffer: {}, next: {}", + self.flags, + self.buffer, + self.next, + ); + } +} From 6a3c4b885385a64f79f8d9e04963849db3c07cb3 Mon Sep 17 00:00:00 2001 From: rmsyn Date: Fri, 11 Jul 2025 02:17:14 +0000 Subject: [PATCH 24/50] dma: add DescriptorFlagFields trait Adds the `DescriptorFlagFields` trait to generalize field accessor functions for descriptor flag types. Enables using a generic parameter for `DmaDescriptor`. --- esp-hal/src/dma/flags.rs | 71 ++++++++++++++++++++++++++++++++++++++++ esp-hal/src/dma/mod.rs | 3 ++ 2 files changed, 74 insertions(+) create mode 100644 esp-hal/src/dma/flags.rs diff --git a/esp-hal/src/dma/flags.rs b/esp-hal/src/dma/flags.rs new file mode 100644 index 00000000000..77801b4fb87 --- /dev/null +++ b/esp-hal/src/dma/flags.rs @@ -0,0 +1,71 @@ +use super::Owner; + +/// Represents operations to get bitfields of DMA descriptor flags. +pub trait DescriptorFlagFields { + /// 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 cd2f9628b9c..8aa2de863fc 100644 --- a/esp-hal/src/dma/mod.rs +++ b/esp-hal/src/dma/mod.rs @@ -377,6 +377,7 @@ impl DmaDescriptor { unsafe impl Send for DmaDescriptor {} mod buffers; +mod flags; #[cfg(gdma)] mod gdma; #[cfg(any(gdma, esp32s2))] @@ -384,6 +385,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))] From 2dc4df62e13ca0e6449b3ac9b39ac196820495df Mon Sep 17 00:00:00 2001 From: rmsyn Date: Fri, 11 Jul 2025 02:15:55 +0000 Subject: [PATCH 25/50] dma: implement DescriptorFlagFields Implements the `DescriptorFlagFields` trait for the relevant types. Small refactors to handle function naming differences. --- esp-hal/src/dma/buffers.rs | 4 +- esp-hal/src/dma/mod.rs | 126 ++++++++++++++++------------- esp-hal/src/sdio.rs | 2 +- esp-hal/src/sdio/dma.rs | 159 +++++++------------------------------ 4 files changed, 103 insertions(+), 188 deletions(-) diff --git a/esp-hal/src/dma/buffers.rs b/esp-hal/src/dma/buffers.rs index edfbfaca3b6..8f4f95a1ad4 100644 --- a/esp-hal/src/dma/buffers.rs +++ b/esp-hal/src/dma/buffers.rs @@ -1352,7 +1352,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 @@ -1527,7 +1527,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; diff --git a/esp-hal/src/dma/mod.rs b/esp-hal/src/dma/mod.rs index 8aa2de863fc..8fc95451348 100644 --- a/esp-hal/src/dma/mod.rs +++ b/esp-hal/src/dma/mod.rs @@ -220,44 +220,60 @@ 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 DescriptorFlagFields for DmaDescriptorFlags { + fn size(&self) -> usize { + self._size() as usize + } - /// 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 set_size(&mut self, size: usize) { + self._set_size(size as u16); + } - /// 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 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 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", &(if self.owner().into() { "DMA" } else { "CPU" })) .finish() } } @@ -312,7 +328,7 @@ 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); } /// Resets the descriptor for a new transmit transfer. See @@ -326,48 +342,39 @@ 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 size(&self) -> usize { + self.flags.size() } - /// 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 set_size(&mut self, size: usize) { + self.flags.set_size(size); } - /// Returns the size of the buffer. See [DmaDescriptorFlags::size]. - pub fn size(&self) -> usize { - self.flags.size() as usize + fn len(&self) -> usize { + self.flags.len() } - /// 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 set_len(&mut self, length: usize) { + self.flags.set_len(length); } - /// 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 suc_eof(&self) -> bool { + self.flags.suc_eof() } - /// 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 set_suc_eof(&mut self, suc_eof: bool) { + self.flags.set_suc_eof(suc_eof); } - /// Returns the owner. See [DmaDescriptorFlags::owner]. - pub fn owner(&self) -> Owner { - match self.flags.owner() { - false => Owner::Cpu, - true => Owner::Dma, - } + fn owner(&self) -> Owner { + self.flags.owner() + } + + fn set_owner(&mut self, owner: Owner) { + self.flags.set_owner(owner); } } @@ -926,7 +933,14 @@ pub enum DmaPeripheral { } /// The owner bit of a DMA descriptor. +<<<<<<< Updated upstream #[derive(PartialEq, PartialOrd)] +======= +/// +/// - 0: CPU can access the buffer; +/// - 1: The DMA controller can access the buffer. +#[derive(Clone, Copy, PartialEq, PartialOrd)] +>>>>>>> Stashed changes pub enum Owner { /// Owned by CPU Cpu = 0, @@ -1032,7 +1046,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? }) } @@ -1199,7 +1213,7 @@ 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); }) } @@ -1558,7 +1572,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..]; diff --git a/esp-hal/src/sdio.rs b/esp-hal/src/sdio.rs index e2192b8b25b..30190239993 100644 --- a/esp-hal/src/sdio.rs +++ b/esp-hal/src/sdio.rs @@ -31,7 +31,7 @@ mod state; mod timing; pub use config::Config; -pub use dma::{DmaDescriptor, DmaDescriptorFlags}; +pub use dma::DmaDescriptorFlags; pub use hinf::{AnyHinf, HinfInfo, HinfInstance}; pub use interrupt::{DeviceInterrupt, HostInterrupt}; pub use pins::Pins; diff --git a/esp-hal/src/sdio/dma.rs b/esp-hal/src/sdio/dma.rs index 13e0c2e4ab3..36c641b7f22 100644 --- a/esp-hal/src/sdio/dma.rs +++ b/esp-hal/src/sdio/dma.rs @@ -2,39 +2,24 @@ use core::fmt::Debug; -use crate::dma::Owner; +use crate::dma::{DescriptorFlagFields, Owner}; 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; - /// Specifies the size of the buffer that this descriptor points to. - pub size, set_size: 13, 0; - - /// 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: 27, 14; - - /// 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; - - /// Specifies who is allowed to access the buffer that this descriptor points to. - /// - 0: CPU can access the buffer; - /// - 1: The SDIO DMA controller can access the buffer. - pub owner, set_owner: 31; + _size, _set_size: 13, 0; + _length, _set_length: 27, 14; + _suc_eof, _set_suc_eof: 30; + _owner, _set_owner: 31; } impl DmaDescriptorFlags { @@ -54,9 +39,9 @@ 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", &(if self.owner().into() { "DMA" } else { "CPU" })) .finish() } } @@ -68,127 +53,43 @@ 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" } ); } } -/// A DMA transfer descriptor. -#[repr(C)] -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct DmaDescriptor { - /// Descriptor flags. - pub flags: DmaDescriptorFlags, - - /// Address of the buffer. - pub buffer: *mut u8, - - /// 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, -} - -impl DmaDescriptor { - /// Creates an empty DMA descriptor used to initialize the descriptor list. - pub const fn new() -> Self { - Self { - flags: DmaDescriptorFlags::new(), - buffer: core::ptr::null_mut(), - next: core::ptr::null_mut(), - } - } - - /// Resets the descriptor for a new receive transfer. - pub fn reset_for_rx(&mut self) { - // Give ownership to the DMA - self.set_owner(Owner::Dma); - - // Clear this to allow hardware to set it when the peripheral returns an EOF - // bit. - self.set_suc_eof(false); - - // Clear this to allow hardware to set it when it's - // done receiving data for this descriptor. - self.set_length(0); - } - - /// Resets the descriptor for a new transmit transfer. See - /// [DmaDescriptorFlags::suc_eof] for more details on the `set_eof` - /// parameter. - pub fn reset_for_tx(&mut self, set_eof: bool) { - // Give ownership to the DMA - self.set_owner(Owner::Dma); - - // The `suc_eof` bit doesn't affect the transfer itself, but signals when the - // hardware should trigger an interrupt request. - self.set_suc_eof(set_eof); +impl DescriptorFlagFields for DmaDescriptorFlags { + fn size(&self) -> usize { + self._size() as usize } - /// 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._set_size(size as u16); } - /// Set the size of the buffer. See [DmaDescriptorFlags::size]. - pub fn set_size(&mut self, len: usize) { - self.flags.set_size(len as u16) + fn len(&self) -> usize { + self._length() as usize } - /// 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 set_len(&mut self, length: usize) { + self._set_length(length as u16); } - /// 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 suc_eof(&self) -> bool { + self._suc_eof() } - /// Returns the suc_eof bit. See [DmaDescriptorFlags::suc_eof]. - pub fn suc_eof(&self) -> bool { - self.flags.suc_eof() + fn set_suc_eof(&mut self, suc_eof: bool) { + self._set_suc_eof(suc_eof); } - /// 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 owner(&self) -> Owner { + self._owner().into() } - /// Returns the owner. See [DmaDescriptorFlags::owner]. - pub fn owner(&self) -> Owner { - self.flags.owner().into() - } - - /// Set the owner. See [DmaDescriptorFlags::owner]. - pub fn set_owner(&mut self, owner: Owner) { - self.flags.set_owner(owner.into()) - } -} - -impl Default for DmaDescriptor { - fn default() -> Self { - Self::new() - } -} - -// 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 {} - -#[cfg(feature = "defmt")] -impl defmt::Format for DmaDescriptor { - fn format(&self, fmt: defmt::Formatter<'_>) { - defmt::write!( - fmt, - "flags: {}, buffer: {}, next: {}", - self.flags, - self.buffer, - self.next, - ); + fn set_owner(&mut self, owner: Owner) { + self._set_owner(owner.into()); } } From 4d3ffe17e807b5c953a2e82b7781c444605bba3c Mon Sep 17 00:00:00 2001 From: rmsyn Date: Fri, 11 Jul 2025 02:02:41 +0000 Subject: [PATCH 26/50] dma: better `Owner` ergonomics Adds some methods to the `Owner` type for better ergonomics. --- esp-hal/src/dma/mod.rs | 35 +++++++++++++++++++++++++++++------ esp-hal/src/sdio/dma.rs | 4 ++-- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/esp-hal/src/dma/mod.rs b/esp-hal/src/dma/mod.rs index 8fc95451348..c43046d699a 100644 --- a/esp-hal/src/dma/mod.rs +++ b/esp-hal/src/dma/mod.rs @@ -273,7 +273,7 @@ impl Debug for DmaDescriptorFlags { .field("size", &self.size()) .field("length", &self.len()) .field("suc_eof", &self.suc_eof()) - .field("owner", &(if self.owner().into() { "DMA" } else { "CPU" })) + .field("owner", &self.owner().to_str()) .finish() } } @@ -287,7 +287,7 @@ impl defmt::Format for DmaDescriptorFlags { self.size(), self.length(), self.suc_eof(), - if self.owner() { "DMA" } else { "CPU" } + self.owner().to_str(), ); } } @@ -933,14 +933,10 @@ pub enum DmaPeripheral { } /// The owner bit of a DMA descriptor. -<<<<<<< Updated upstream -#[derive(PartialEq, PartialOrd)] -======= /// /// - 0: CPU can access the buffer; /// - 1: The DMA controller can access the buffer. #[derive(Clone, Copy, PartialEq, PartialOrd)] ->>>>>>> Stashed changes pub enum Owner { /// Owned by CPU Cpu = 0, @@ -948,6 +944,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 { @@ -975,6 +992,12 @@ impl From for bool { } } +impl Default for Owner { + fn default() -> Self { + Self::new() + } +} + #[doc(hidden)] #[instability::unstable] pub trait DmaEligible { diff --git a/esp-hal/src/sdio/dma.rs b/esp-hal/src/sdio/dma.rs index 36c641b7f22..0dbc8918d29 100644 --- a/esp-hal/src/sdio/dma.rs +++ b/esp-hal/src/sdio/dma.rs @@ -41,7 +41,7 @@ impl Debug for DmaDescriptorFlags { .field("size", &self.size()) .field("length", &self.len()) .field("suc_eof", &self.suc_eof()) - .field("owner", &(if self.owner().into() { "DMA" } else { "CPU" })) + .field("owner", &self.owner().to_str()) .finish() } } @@ -55,7 +55,7 @@ impl defmt::Format for DmaDescriptorFlags { self.size(), self.len(), self.suc_eof(), - if self.owner() { "DMA" } else { "CPU" } + self.owner().to_str() ); } } From fc8cbed110d2c34fd57583039715ba5cd1c50af5 Mon Sep 17 00:00:00 2001 From: rmsyn Date: Fri, 11 Jul 2025 03:56:12 +0000 Subject: [PATCH 27/50] dma: make DmaDescriptor generic Refactors the `DmaDescriptor` type to `DmaDescriptorGeneric` taking a generic parameter for the flags field. Creates convenience aliases for the concrete types used in the general + SDIO dedicated DMA descriptors. --- esp-hal/src/dma/flags.rs | 20 ++++++++++---------- esp-hal/src/dma/mod.rs | 34 ++++++++++++++++++++++------------ esp-hal/src/sdio.rs | 2 +- esp-hal/src/sdio/dma.rs | 5 ++++- 4 files changed, 37 insertions(+), 24 deletions(-) diff --git a/esp-hal/src/dma/flags.rs b/esp-hal/src/dma/flags.rs index 77801b4fb87..ffe1426ad5a 100644 --- a/esp-hal/src/dma/flags.rs +++ b/esp-hal/src/dma/flags.rs @@ -37,22 +37,22 @@ pub trait DescriptorFlagFields { /// 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 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. + /// 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 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. + /// 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. diff --git a/esp-hal/src/dma/mod.rs b/esp-hal/src/dma/mod.rs index c43046d699a..faf939a2353 100644 --- a/esp-hal/src/dma/mod.rs +++ b/esp-hal/src/dma/mod.rs @@ -285,20 +285,23 @@ impl defmt::Format for DmaDescriptorFlags { fmt, "DmaDescriptorFlags {{ size: {}, length: {}, suc_eof: {}, owner: {} }}", self.size(), - self.length(), + self.len(), self.suc_eof(), self.owner().to_str(), ); } } +/// Convenience alias for the DMA descriptor used with the general DMA controller. +pub type DmaDescriptor = DmaDescriptorGeneric; + /// A DMA transfer descriptor. #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(C)] -pub struct DmaDescriptor { +pub struct DmaDescriptorGeneric { /// Descriptor flags. - pub flags: DmaDescriptorFlags, + pub flags: FLAGS, /// Address of the buffer. pub buffer: *mut u8, @@ -306,16 +309,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 DmaDescriptorGeneric { + 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) { @@ -344,7 +345,7 @@ impl DmaDescriptor { } } -impl DescriptorFlagFields for DmaDescriptor { +impl DescriptorFlagFields for DmaDescriptorGeneric { fn size(&self) -> usize { self.flags.size() } @@ -378,6 +379,15 @@ impl DescriptorFlagFields for DmaDescriptor { } } +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). diff --git a/esp-hal/src/sdio.rs b/esp-hal/src/sdio.rs index 30190239993..e2192b8b25b 100644 --- a/esp-hal/src/sdio.rs +++ b/esp-hal/src/sdio.rs @@ -31,7 +31,7 @@ mod state; mod timing; pub use config::Config; -pub use dma::DmaDescriptorFlags; +pub use dma::{DmaDescriptor, DmaDescriptorFlags}; pub use hinf::{AnyHinf, HinfInfo, HinfInstance}; pub use interrupt::{DeviceInterrupt, HostInterrupt}; pub use pins::Pins; diff --git a/esp-hal/src/sdio/dma.rs b/esp-hal/src/sdio/dma.rs index 0dbc8918d29..7ef1005f7bc 100644 --- a/esp-hal/src/sdio/dma.rs +++ b/esp-hal/src/sdio/dma.rs @@ -2,7 +2,10 @@ use core::fmt::Debug; -use crate::dma::{DescriptorFlagFields, Owner}; +use crate::dma::{DescriptorFlagFields, DmaDescriptorGeneric, Owner}; + +/// Convenience alias for the DMA descriptor used with the SDIO dedicated DMA controller. +pub type DmaDescriptor = DmaDescriptorGeneric; bitfield::bitfield! { /// DMA descriptor flags for the dedicated SDIO DMA engine. From 3600ddefa4d401e224b41978a840c347977db8c7 Mon Sep 17 00:00:00 2001 From: rmsyn Date: Sun, 13 Jul 2025 23:17:24 +0000 Subject: [PATCH 28/50] dma: make Preparation generic Makes the `Preparation` type generic over the `DescriptorFlagFields` trait. --- esp-hal/src/dma/buffers.rs | 7 +++++-- esp-hal/src/sdio.rs | 3 +-- esp-hal/src/sdio/dma.rs | 5 ++++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/esp-hal/src/dma/buffers.rs b/esp-hal/src/dma/buffers.rs index 8f4f95a1ad4..7705fcc65b2 100644 --- a/esp-hal/src/dma/buffers.rs +++ b/esp-hal/src/dma/buffers.rs @@ -351,12 +351,15 @@ pub enum TransferDirection { Out, } +/// Convenience alias for general DMA preparation. +pub type Preparation = PreparationGeneric; + /// 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 PreparationGeneric { /// The descriptor the DMA will start from. - pub start: *mut DmaDescriptor, + pub start: *mut DmaDescriptorGeneric, /// The direction of the DMA transfer. pub direction: TransferDirection, diff --git a/esp-hal/src/sdio.rs b/esp-hal/src/sdio.rs index e2192b8b25b..6a30eb52894 100644 --- a/esp-hal/src/sdio.rs +++ b/esp-hal/src/sdio.rs @@ -21,7 +21,7 @@ use embedded_hal_sdmmc::{ use crate::pac; mod config; -mod dma; +pub mod dma; mod hinf; mod interrupt; mod pins; @@ -31,7 +31,6 @@ mod state; mod timing; pub use config::Config; -pub use dma::{DmaDescriptor, DmaDescriptorFlags}; pub use hinf::{AnyHinf, HinfInfo, HinfInstance}; pub use interrupt::{DeviceInterrupt, HostInterrupt}; pub use pins::Pins; diff --git a/esp-hal/src/sdio/dma.rs b/esp-hal/src/sdio/dma.rs index 7ef1005f7bc..dec9d9ae56b 100644 --- a/esp-hal/src/sdio/dma.rs +++ b/esp-hal/src/sdio/dma.rs @@ -2,11 +2,14 @@ use core::fmt::Debug; -use crate::dma::{DescriptorFlagFields, DmaDescriptorGeneric, Owner}; +use crate::dma::{DescriptorFlagFields, DmaDescriptorGeneric, Owner, PreparationGeneric}; /// Convenience alias for the DMA descriptor used with the SDIO dedicated DMA controller. pub type DmaDescriptor = DmaDescriptorGeneric; +/// Convenience alias for SDIO dedicated DMA preparation. +pub type Preparation = PreparationGeneric; + bitfield::bitfield! { /// DMA descriptor flags for the dedicated SDIO DMA engine. /// From 6646d2283490926c3149110c10d3e6a1657ccec1 Mon Sep 17 00:00:00 2001 From: rmsyn Date: Sun, 13 Jul 2025 23:38:50 +0000 Subject: [PATCH 29/50] dma: make DmaLoopBuf generic Maks `DmaLoopBuf` generic over the `DescriptorFlagFields` trait. --- esp-hal/src/dma/buffers.rs | 20 ++++++++++++-------- esp-hal/src/sdio/dma.rs | 5 ++++- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/esp-hal/src/dma/buffers.rs b/esp-hal/src/dma/buffers.rs index 7705fcc65b2..0cabf757bc6 100644 --- a/esp-hal/src/dma/buffers.rs +++ b/esp-hal/src/dma/buffers.rs @@ -1495,6 +1495,9 @@ unsafe impl DmaRxBuffer for EmptyBuf { } } +/// Convenience alias for general DMA loop buffer. +pub type DmaLoopBuf = DmaLoopBufGeneric; + /// DMA Loop Buffer /// /// This consists of a single descriptor that points to itself and points to a @@ -1505,17 +1508,17 @@ 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 DmaLoopBufGeneric { + descriptor: &'static mut DmaDescriptorGeneric, buffer: &'static mut [u8], } -impl DmaLoopBuf { +impl DmaLoopBufGeneric { /// Create a new [DmaLoopBuf]. pub fn new( - descriptor: &'static mut DmaDescriptor, + descriptor: &'static mut DmaDescriptorGeneric, buffer: &'static mut [u8], - ) -> Result { + ) -> Result { if !is_slice_in_dram(buffer) { return Err(DmaBufError::UnsupportedMemoryRegion); } @@ -1539,11 +1542,12 @@ 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 DmaDescriptorGeneric, &'static mut [u8]) { (self.descriptor, self.buffer) } } +// TODO: make this generic after DmaTxBuffer is generic unsafe impl DmaTxBuffer for DmaLoopBuf { type View = DmaLoopBuf; type Final = DmaLoopBuf; @@ -1572,7 +1576,7 @@ unsafe impl DmaTxBuffer for DmaLoopBuf { } } -impl Deref for DmaLoopBuf { +impl Deref for DmaLoopBufGeneric { type Target = [u8]; fn deref(&self) -> &Self::Target { @@ -1580,7 +1584,7 @@ impl Deref for DmaLoopBuf { } } -impl DerefMut for DmaLoopBuf { +impl DerefMut for DmaLoopBufGeneric { fn deref_mut(&mut self) -> &mut Self::Target { self.buffer } diff --git a/esp-hal/src/sdio/dma.rs b/esp-hal/src/sdio/dma.rs index dec9d9ae56b..e9fdf423953 100644 --- a/esp-hal/src/sdio/dma.rs +++ b/esp-hal/src/sdio/dma.rs @@ -2,7 +2,7 @@ use core::fmt::Debug; -use crate::dma::{DescriptorFlagFields, DmaDescriptorGeneric, Owner, PreparationGeneric}; +use crate::dma::{DmaLoopBufGeneric, DescriptorFlagFields, DmaDescriptorGeneric, Owner, PreparationGeneric}; /// Convenience alias for the DMA descriptor used with the SDIO dedicated DMA controller. pub type DmaDescriptor = DmaDescriptorGeneric; @@ -10,6 +10,9 @@ pub type DmaDescriptor = DmaDescriptorGeneric; /// Convenience alias for SDIO dedicated DMA preparation. pub type Preparation = PreparationGeneric; +/// Convenience alias for SDIO dedicated DMA loop buffer. +pub type DmaLoopBuf = DmaLoopBufGeneric; + bitfield::bitfield! { /// DMA descriptor flags for the dedicated SDIO DMA engine. /// From d154163b5c49425972488d2c430556d93bdee33b Mon Sep 17 00:00:00 2001 From: rmsyn Date: Sun, 13 Jul 2025 23:44:31 +0000 Subject: [PATCH 30/50] dma: make DmaRxStreamingBuf generic Makes the `DmaRxStreamingBuf` type generic over the `DescriptorFieldFlags` trait. --- esp-hal/src/dma/buffers.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/esp-hal/src/dma/buffers.rs b/esp-hal/src/dma/buffers.rs index 0cabf757bc6..a75fca76265 100644 --- a/esp-hal/src/dma/buffers.rs +++ b/esp-hal/src/dma/buffers.rs @@ -1114,6 +1114,9 @@ unsafe impl DmaRxBuffer for DmaRxTxBuf { } } +/// Convenience alias for general DMA RX stream buffer. +pub type DmaRxStreamBuf = DmaRxStreamBufGeneric; + /// DMA Streaming Receive Buffer. /// /// This is a contiguous buffer linked together by DMA descriptors, and the @@ -1154,17 +1157,17 @@ 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 DmaRxStreamBufGeneric { + descriptors: &'static mut [DmaDescriptorGeneric], buffer: &'static mut [u8], burst: BurstConfig, } -impl DmaRxStreamBuf { +impl DmaRxStreamBufGeneric { /// Creates a new [DmaRxStreamBuf] evenly distributing the buffer between /// the provided descriptors. pub fn new( - descriptors: &'static mut [DmaDescriptor], + descriptors: &'static mut [DmaDescriptorGeneric], buffer: &'static mut [u8], ) -> Result { if !is_slice_in_dram(descriptors) { @@ -1214,11 +1217,12 @@ 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 [DmaDescriptorGeneric], &'static mut [u8]) { (self.descriptors, self.buffer) } } +// TODO: make generic after making DmaRxBuffer generic unsafe impl DmaRxBuffer for DmaRxStreamBuf { type View = DmaRxStreamBufView; type Final = DmaRxStreamBuf; @@ -1458,7 +1462,7 @@ unsafe impl DmaTxBuffer for EmptyBuf { } } - fn into_view(self) -> EmptyBuf { + fn into_view(self) -> Self { self } From bfa3e7c9755bf3a9f8fd50b6f466412ab34b2f6d Mon Sep 17 00:00:00 2001 From: rmsyn Date: Mon, 14 Jul 2025 00:05:07 +0000 Subject: [PATCH 31/50] dma: make DescriptorSet generic Makes the `DescriptorSet` type generic over the `DescriptorFlagFields` trait. --- esp-hal/src/dma/flags.rs | 3 +++ esp-hal/src/dma/mod.rs | 48 ++++++++++++++++++++++++++-------------- esp-hal/src/sdio/dma.rs | 17 +++++++++++++- 3 files changed, 51 insertions(+), 17 deletions(-) diff --git a/esp-hal/src/dma/flags.rs b/esp-hal/src/dma/flags.rs index ffe1426ad5a..270bac3ec8b 100644 --- a/esp-hal/src/dma/flags.rs +++ b/esp-hal/src/dma/flags.rs @@ -2,6 +2,9 @@ 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; diff --git a/esp-hal/src/dma/mod.rs b/esp-hal/src/dma/mod.rs index faf939a2353..a1760b5ae7b 100644 --- a/esp-hal/src/dma/mod.rs +++ b/esp-hal/src/dma/mod.rs @@ -234,6 +234,10 @@ bitfield::bitfield! { } impl DescriptorFlagFields for DmaDescriptorFlags { + fn empty() -> Self { + Self(0) + } + fn size(&self) -> usize { self._size() as usize } @@ -346,6 +350,14 @@ impl DmaDescriptorGeneric { } impl DescriptorFlagFields for DmaDescriptorGeneric { + fn empty() -> Self { + Self { + flags: FLAGS::empty(), + buffer: core::ptr::null_mut(), + next: core::ptr::null_mut(), + } + } + fn size(&self) -> usize { self.flags.size() } @@ -1141,21 +1153,25 @@ pub const fn descriptor_count(buffer_size: usize, chunk_size: usize, is_circular buffer_size.div_ceil(chunk_size) } +/// Convenience alias for the general DMA descriptor set. +pub(crate) type DescriptorSet<'a> = DescriptorSetGeneric<'a, DmaDescriptorFlags>; + +/// 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 DescriptorSetGeneric<'a, Flag: DescriptorFlagFields> { + descriptors: &'a mut [DmaDescriptorGeneric], } -impl<'a> DescriptorSet<'a> { +impl<'a, Flag: DescriptorFlagFields + Clone> DescriptorSetGeneric<'a, Flag> { /// 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 [DmaDescriptorGeneric]) -> Result { if !is_slice_in_dram(descriptors) { return Err(DmaBufError::UnsupportedMemoryRegion); } - descriptors.fill(DmaDescriptor::EMPTY); + descriptors.fill(DmaDescriptorGeneric::empty()); Ok(unsafe { Self::new_unchecked(descriptors) }) } @@ -1167,22 +1183,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 [DmaDescriptorGeneric]) -> 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 [DmaDescriptorGeneric] { self.descriptors } /// Returns a pointer to the first descriptor in the chain. - fn head(&mut self) -> *mut DmaDescriptor { + fn head(&mut self) -> *mut DmaDescriptorGeneric { 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 { @@ -1195,7 +1211,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<'_, Flag> { let mut was_last = false; self.descriptors.iter_mut().take_while(move |d| { if was_last { @@ -1227,7 +1243,7 @@ impl<'a> DescriptorSet<'a> { &mut self, len: usize, chunk_size: usize, - prepare: fn(&mut DmaDescriptor, usize), + prepare: fn(&mut DmaDescriptorGeneric, usize), ) -> Result<(), DmaBufError> { Self::set_up_descriptors(self.descriptors, len, chunk_size, false, prepare) } @@ -1252,11 +1268,11 @@ impl<'a> DescriptorSet<'a> { /// Returns a slice of descriptors that can cover a buffer of length `len`. fn descriptors_for_buffer_len( - descriptors: &mut [DmaDescriptor], + descriptors: &mut [DmaDescriptorGeneric], len: usize, chunk_size: usize, is_circular: bool, - ) -> Result<&mut [DmaDescriptor], DmaBufError> { + ) -> Result<&mut [DmaDescriptorGeneric], DmaBufError> { // First, pick enough descriptors to cover the buffer. let required_descriptors = descriptor_count(len, chunk_size, is_circular); if descriptors.len() < required_descriptors { @@ -1273,11 +1289,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 [DmaDescriptorGeneric], len: usize, chunk_size: usize, is_circular: bool, - prepare: impl Fn(&mut DmaDescriptor, usize), + prepare: impl Fn(&mut DmaDescriptorGeneric, usize), ) -> Result<(), DmaBufError> { let descriptors = Self::descriptors_for_buffer_len(descriptors, len, chunk_size, is_circular)?; @@ -1318,7 +1334,7 @@ impl<'a> DescriptorSet<'a> { /// repeatedly. fn set_up_buffer_ptrs( buffer: &mut [u8], - descriptors: &mut [DmaDescriptor], + descriptors: &mut [DmaDescriptorGeneric], chunk_size: usize, is_circular: bool, ) -> Result<(), DmaBufError> { diff --git a/esp-hal/src/sdio/dma.rs b/esp-hal/src/sdio/dma.rs index e9fdf423953..f997d0a5761 100644 --- a/esp-hal/src/sdio/dma.rs +++ b/esp-hal/src/sdio/dma.rs @@ -2,7 +2,14 @@ use core::fmt::Debug; -use crate::dma::{DmaLoopBufGeneric, DescriptorFlagFields, DmaDescriptorGeneric, Owner, PreparationGeneric}; +use crate::dma::{ + DescriptorFlagFields, + DescriptorSetGeneric, + DmaDescriptorGeneric, + DmaLoopBufGeneric, + Owner, + PreparationGeneric, +}; /// Convenience alias for the DMA descriptor used with the SDIO dedicated DMA controller. pub type DmaDescriptor = DmaDescriptorGeneric; @@ -13,6 +20,10 @@ pub type Preparation = PreparationGeneric; /// Convenience alias for SDIO dedicated DMA loop buffer. pub type DmaLoopBuf = DmaLoopBufGeneric; +/// Convenience alias for the general DMA descriptor set. +#[allow(unused)] +pub(crate) type DescriptorSet<'a> = DescriptorSetGeneric<'a, DmaDescriptorFlags>; + bitfield::bitfield! { /// DMA descriptor flags for the dedicated SDIO DMA engine. /// @@ -70,6 +81,10 @@ impl defmt::Format for DmaDescriptorFlags { } impl DescriptorFlagFields for DmaDescriptorFlags { + fn empty() -> Self { + Self(0) + } + fn size(&self) -> usize { self._size() as usize } From 81c339856e8b5b2366c3eab4226547839028e3ee Mon Sep 17 00:00:00 2001 From: rmsyn Date: Mon, 14 Jul 2025 00:13:51 +0000 Subject: [PATCH 32/50] dma: make DmaTxBuf generic Makes the `DmaTxBuf` generic over the `DescriptorFlagFields` trait. --- esp-hal/src/dma/buffers.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/esp-hal/src/dma/buffers.rs b/esp-hal/src/dma/buffers.rs index a75fca76265..376a6a5b261 100644 --- a/esp-hal/src/dma/buffers.rs +++ b/esp-hal/src/dma/buffers.rs @@ -488,6 +488,9 @@ pub unsafe trait DmaRxBuffer { /// descriptors/buffers. pub struct BufView(T); +/// Convenience alias for the general DMA TX buffer. +pub type DmaTxBuf = DmaTxBufGeneric; + /// DMA transmit buffer /// /// This is a contiguous buffer linked together by DMA descriptors of length @@ -495,13 +498,13 @@ 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 DmaTxBufGeneric { + descriptors: DescriptorSetGeneric<'static, Flag>, buffer: &'static mut [u8], burst: BurstConfig, } -impl DmaTxBuf { +impl DmaTxBufGeneric { /// Creates a new [DmaTxBuf] from some descriptors and a buffer. /// /// There must be enough descriptors for the provided buffer. @@ -511,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 [DmaDescriptorGeneric], buffer: &'static mut [u8], ) -> Result { Self::new_with_config(descriptors, buffer, BurstConfig::default()) @@ -526,12 +529,12 @@ 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 [DmaDescriptorGeneric], buffer: &'static mut [u8], config: impl Into, ) -> Result { let mut buf = Self { - descriptors: DescriptorSet::new(descriptors)?, + descriptors: DescriptorSetGeneric::new(descriptors)?, buffer, burst: BurstConfig::default(), }; @@ -566,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 [DmaDescriptorGeneric], &'static mut [u8]) { (self.descriptors.into_inner(), self.buffer) } @@ -636,6 +639,7 @@ impl DmaTxBuf { } } +// TODO: make generic after making DmaTxBuffer generic unsafe impl DmaTxBuffer for DmaTxBuf { type View = BufView; type Final = DmaTxBuf; From 7aa32e880ea4a4c99442db5ed4f41eaaafd0fd90 Mon Sep 17 00:00:00 2001 From: rmsyn Date: Mon, 14 Jul 2025 00:26:37 +0000 Subject: [PATCH 33/50] dma: make DmaRxBuf generic Makes the `DmaRxBuf` generic over the `DescriptorFlagFields` trait. --- esp-hal/src/dma/buffers.rs | 18 +++++++++++------- esp-hal/src/sdio/dma.rs | 10 +++++++++- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/esp-hal/src/dma/buffers.rs b/esp-hal/src/dma/buffers.rs index 376a6a5b261..d52b988d41a 100644 --- a/esp-hal/src/dma/buffers.rs +++ b/esp-hal/src/dma/buffers.rs @@ -679,6 +679,9 @@ unsafe impl DmaTxBuffer for DmaTxBuf { } } +/// Convenience alias for the general DMA RX buffer. +pub type DmaRxBuf = DmaRxBufGeneric; + /// DMA receive buffer /// /// This is a contiguous buffer linked together by DMA descriptors of length @@ -686,13 +689,13 @@ 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 DmaRxBufGeneric { + descriptors: DescriptorSetGeneric<'static, Flag>, buffer: &'static mut [u8], burst: BurstConfig, } -impl DmaRxBuf { +impl DmaRxBufGeneric { /// Creates a new [DmaRxBuf] from some descriptors and a buffer. /// /// There must be enough descriptors for the provided buffer. @@ -701,7 +704,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 [DmaDescriptorGeneric], buffer: &'static mut [u8], ) -> Result { Self::new_with_config(descriptors, buffer, BurstConfig::default()) @@ -716,12 +719,12 @@ 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 [DmaDescriptorGeneric], buffer: &'static mut [u8], config: impl Into, ) -> Result { let mut buf = Self { - descriptors: DescriptorSet::new(descriptors)?, + descriptors: DescriptorSetGeneric::new(descriptors)?, buffer, burst: BurstConfig::default(), }; @@ -755,7 +758,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 [DmaDescriptorGeneric], &'static mut [u8]) { (self.descriptors.into_inner(), self.buffer) } @@ -847,6 +850,7 @@ impl DmaRxBuf { } } +// TODO: make generic after making DmaRxBuffer generic unsafe impl DmaRxBuffer for DmaRxBuf { type View = BufView; type Final = DmaRxBuf; diff --git a/esp-hal/src/sdio/dma.rs b/esp-hal/src/sdio/dma.rs index f997d0a5761..f1c9b87ec17 100644 --- a/esp-hal/src/sdio/dma.rs +++ b/esp-hal/src/sdio/dma.rs @@ -7,6 +7,8 @@ use crate::dma::{ DescriptorSetGeneric, DmaDescriptorGeneric, DmaLoopBufGeneric, + DmaRxBufGeneric, + DmaTxBufGeneric, Owner, PreparationGeneric, }; @@ -20,10 +22,16 @@ pub type Preparation = PreparationGeneric; /// Convenience alias for SDIO dedicated DMA loop buffer. pub type DmaLoopBuf = DmaLoopBufGeneric; -/// Convenience alias for the general DMA descriptor set. +/// Convenience alias for the SDIO dedicated DMA descriptor set. #[allow(unused)] pub(crate) type DescriptorSet<'a> = DescriptorSetGeneric<'a, DmaDescriptorFlags>; +/// Convenience alias for the SDIO dedicated DMA RX buffer. +pub type DmaTxBuf = DmaTxBufGeneric; + +/// Convenience alias for the SDIO dedicated DMA RX buffer. +pub type DmaRxBuf = DmaRxBufGeneric; + bitfield::bitfield! { /// DMA descriptor flags for the dedicated SDIO DMA engine. /// From 6b663945a2a1fa090416f212e2d02e9757943bfb Mon Sep 17 00:00:00 2001 From: rmsyn Date: Mon, 14 Jul 2025 00:32:36 +0000 Subject: [PATCH 34/50] dma: make DmaRxTxBuf generic Makes the `DmaRxTxBuf` type generic over the `DescriptorFlagFields` trait. --- esp-hal/src/dma/buffers.rs | 25 +++++++++++++++---------- esp-hal/src/sdio/dma.rs | 4 ++++ 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/esp-hal/src/dma/buffers.rs b/esp-hal/src/dma/buffers.rs index d52b988d41a..9ad5b4c8944 100644 --- a/esp-hal/src/dma/buffers.rs +++ b/esp-hal/src/dma/buffers.rs @@ -895,6 +895,9 @@ unsafe impl DmaRxBuffer for DmaRxBuf { } } +/// Convenience alias for the general DMA RX/TX buffer. +pub type DmaRxTxBuf = DmaRxTxBufGeneric; + /// DMA transmit and receive buffer. /// /// This is a (single) contiguous buffer linked together by two sets of DMA @@ -903,14 +906,14 @@ 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 DmaRxTxBufGeneric { + rx_descriptors: DescriptorSetGeneric<'static, Flag>, + tx_descriptors: DescriptorSetGeneric<'static, Flag>, buffer: &'static mut [u8], burst: BurstConfig, } -impl DmaRxTxBuf { +impl DmaRxTxBufGeneric { /// Creates a new [DmaRxTxBuf] from some descriptors and a buffer. /// /// There must be enough descriptors for the provided buffer. @@ -919,13 +922,13 @@ 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 [DmaDescriptorGeneric], + tx_descriptors: &'static mut [DmaDescriptorGeneric], buffer: &'static mut [u8], ) -> Result { let mut buf = Self { - rx_descriptors: DescriptorSet::new(rx_descriptors)?, - tx_descriptors: DescriptorSet::new(tx_descriptors)?, + rx_descriptors: DescriptorSetGeneric::new(rx_descriptors)?, + tx_descriptors: DescriptorSetGeneric::new(tx_descriptors)?, buffer, burst: BurstConfig::default(), }; @@ -969,8 +972,8 @@ impl DmaRxTxBuf { pub fn split( self, ) -> ( - &'static mut [DmaDescriptor], - &'static mut [DmaDescriptor], + &'static mut [DmaDescriptorGeneric], + &'static mut [DmaDescriptorGeneric], &'static mut [u8], ) { ( @@ -1032,6 +1035,7 @@ impl DmaRxTxBuf { } } +// TODO: make generic after making DmaTxBuffer generic unsafe impl DmaTxBuffer for DmaRxTxBuf { type View = BufView; type Final = DmaRxTxBuf; @@ -1078,6 +1082,7 @@ unsafe impl DmaTxBuffer for DmaRxTxBuf { } } +// TODO: make generic after making DmaRxBuffer generic unsafe impl DmaRxBuffer for DmaRxTxBuf { type View = BufView; type Final = DmaRxTxBuf; diff --git a/esp-hal/src/sdio/dma.rs b/esp-hal/src/sdio/dma.rs index f1c9b87ec17..d05a4292f51 100644 --- a/esp-hal/src/sdio/dma.rs +++ b/esp-hal/src/sdio/dma.rs @@ -8,6 +8,7 @@ use crate::dma::{ DmaDescriptorGeneric, DmaLoopBufGeneric, DmaRxBufGeneric, + DmaRxTxBufGeneric, DmaTxBufGeneric, Owner, PreparationGeneric, @@ -32,6 +33,9 @@ pub type DmaTxBuf = DmaTxBufGeneric; /// Convenience alias for the SDIO dedicated DMA RX buffer. pub type DmaRxBuf = DmaRxBufGeneric; +/// Convenience alias for the SDIO dedicated DMA RX/TX buffer. +pub type DmaRxTxBuf = DmaRxTxBufGeneric; + bitfield::bitfield! { /// DMA descriptor flags for the dedicated SDIO DMA engine. /// From b8299b5e83c6359822edfdaf76b8a23b61cab57e Mon Sep 17 00:00:00 2001 From: rmsyn Date: Mon, 14 Jul 2025 01:40:22 +0000 Subject: [PATCH 35/50] dma: make ChannelTx generic Makes the `ChannelTx` struct generic over the `DescriptorFlagFields` trait. --- esp-hal/src/dma/mod.rs | 41 ++++++++++++++++++++++++++++++----------- esp-hal/src/sdio/dma.rs | 4 ++++ 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/esp-hal/src/dma/mod.rs b/esp-hal/src/dma/mod.rs index a1760b5ae7b..73e00de2747 100644 --- a/esp-hal/src/dma/mod.rs +++ b/esp-hal/src/dma/mod.rs @@ -1211,7 +1211,9 @@ impl<'a, Flag: DescriptorFlagFields + Clone> DescriptorSetGeneric<'a, Flag> { } /// Returns an iterator over the linked descriptors. - fn linked_iter_mut(&mut self) -> impl Iterator> + use<'_, Flag> { + fn linked_iter_mut( + &mut self, + ) -> impl Iterator> + use<'_, Flag> { let mut was_last = false; self.descriptors.iter_mut().take_while(move |d| { if was_last { @@ -2098,21 +2100,27 @@ where } } +/// Convenience alias for the general DMA transmit channel. +pub type ChannelTx = ChannelTxGeneric; + /// DMA transmit channel #[doc(hidden)] -pub struct ChannelTx +pub struct ChannelTxGeneric 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 ChannelTxGeneric where CH: DmaTxChannel, + F: DescriptorFlagFields, { /// Creates a new TX channel half. pub fn new(tx_impl: CH) -> Self { @@ -2128,19 +2136,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) -> ChannelTxGeneric { if let Some(handler) = self.tx_impl.async_handler() { self.set_interrupt_handler(handler); } + self.tx_impl.set_async(true); - ChannelTx { + + ChannelTxGeneric:: { tx_impl: self.tx_impl, _phantom: PhantomData, _guard: self._guard, + _flag: PhantomData, } } @@ -2158,28 +2170,33 @@ where } } -impl ChannelTx +impl ChannelTxGeneric 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) -> ChannelTxGeneric { if let Some(interrupt) = self.tx_impl.peripheral_interrupt() { crate::interrupt::disable(Cpu::current(), interrupt); } + self.tx_impl.set_async(false); - ChannelTx { + + ChannelTxGeneric:: { tx_impl: self.tx_impl, _phantom: PhantomData, _guard: self._guard, + _flag: PhantomData, } } } -impl ChannelTx +impl ChannelTxGeneric where Dm: DriverMode, CH: DmaTxChannel, + F: DescriptorFlagFields, { /// Configure the channel priority. #[cfg(gdma)] @@ -2222,18 +2239,20 @@ where } } -impl crate::private::Sealed for ChannelTx +impl crate::private::Sealed for ChannelTxGeneric where Dm: DriverMode, CH: DmaTxChannel, + F: DescriptorFlagFields, { } #[allow(unused)] -impl ChannelTx +impl ChannelTxGeneric 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/sdio/dma.rs b/esp-hal/src/sdio/dma.rs index d05a4292f51..829c4cc3e8a 100644 --- a/esp-hal/src/sdio/dma.rs +++ b/esp-hal/src/sdio/dma.rs @@ -3,6 +3,7 @@ use core::fmt::Debug; use crate::dma::{ + ChannelTxGeneric, DescriptorFlagFields, DescriptorSetGeneric, DmaDescriptorGeneric, @@ -36,6 +37,9 @@ pub type DmaRxBuf = DmaRxBufGeneric; /// Convenience alias for the SDIO dedicated DMA RX/TX buffer. pub type DmaRxTxBuf = DmaRxTxBufGeneric; +/// Convenience alias for the SDIO dedicated DMA transmit channel. +pub type ChannelTx = ChannelTxGeneric; + bitfield::bitfield! { /// DMA descriptor flags for the dedicated SDIO DMA engine. /// From 0c8c171ba0776515e420c60809ab432170805f92 Mon Sep 17 00:00:00 2001 From: rmsyn Date: Mon, 14 Jul 2025 16:05:15 +0000 Subject: [PATCH 36/50] fixup: sdio: convert interrupts to EnumSetType Use the `EnumSetType` derive macro, and `EnumSet` type for the `HostInterrupt` and `DeviceInterrupt` types to match current development conventions. --- esp-hal/src/sdio.rs | 60 +++++++++++----- esp-hal/src/sdio/interrupt.rs | 126 ++++++++++++++++++++++------------ 2 files changed, 122 insertions(+), 64 deletions(-) diff --git a/esp-hal/src/sdio.rs b/esp-hal/src/sdio.rs index 6a30eb52894..fdfe8d0386e 100644 --- a/esp-hal/src/sdio.rs +++ b/esp-hal/src/sdio.rs @@ -204,7 +204,7 @@ impl<'d> Sdio<'d> { self.pac_init()?; self.pac_enable_hs()?; self.pac_set_timing()?; - self.pac_dev_interrupt_enable(0xffu8.into()) + self.pac_dev_interrupt_enable(enumset::EnumSet::all()) } /// Performs low-level initialization of the SDIO peripheral. @@ -313,16 +313,27 @@ impl<'d> Sdio<'d> { /// Sets which device interrupts to enable based on the provided mask. #[cfg(esp32)] - fn pac_dev_interrupt_enable(&self, mask: DeviceInterrupt) -> Result<(), Error> { + 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.general0()); - w.frhost_bit1_int_ena().variant(mask.general1()); - w.frhost_bit2_int_ena().variant(mask.general2()); - w.frhost_bit3_int_ena().variant(mask.general3()); - w.frhost_bit4_int_ena().variant(mask.general4()); - w.frhost_bit5_int_ena().variant(mask.general5()); - w.frhost_bit6_int_ena().variant(mask.general6()); - w.frhost_bit7_int_ena().variant(mask.general7()) + 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(()) @@ -330,16 +341,27 @@ impl<'d> Sdio<'d> { /// Sets which device interrupts to enable based on the provided mask. #[cfg(esp32c6)] - fn pac_dev_interrupt_enable(&self, mask: DeviceInterrupt) -> Result<(), Error> { + 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.general0()); - w.sdio_slc_frhost_bit1_int_ena().variant(mask.general1()); - w.sdio_slc_frhost_bit2_int_ena().variant(mask.general2()); - w.sdio_slc_frhost_bit3_int_ena().variant(mask.general3()); - w.sdio_slc_frhost_bit4_int_ena().variant(mask.general4()); - w.sdio_slc_frhost_bit5_int_ena().variant(mask.general5()); - w.sdio_slc_frhost_bit6_int_ena().variant(mask.general6()); - w.sdio_slc_frhost_bit7_int_ena().variant(mask.general7()) + 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(()) diff --git a/esp-hal/src/sdio/interrupt.rs b/esp-hal/src/sdio/interrupt.rs index 6d398d98cb3..410d85c0938 100644 --- a/esp-hal/src/sdio/interrupt.rs +++ b/esp-hal/src/sdio/interrupt.rs @@ -1,41 +1,47 @@ -use bitfield::bitfield; +use enumset::EnumSetType; -bitfield! { - /// 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. - #[repr(C)] - #[derive(Clone, Copy, Debug, Eq, PartialEq)] - pub struct HostInterrupt(u8); +/// 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. - pub general0, set_general0: 0; + #[enumset(repr = "u8")] + General0 = 1, /// General purpose host interrupt: 1. - pub general1, set_general1: 1; + #[enumset(repr = "u8")] + General1 = 2, /// General purpose host interrupt: 2. - pub general2, set_general2: 2; + #[enumset(repr = "u8")] + General2 = 4, /// General purpose host interrupt: 3. - pub general3, set_general3: 3; + #[enumset(repr = "u8")] + General3 = 8, /// General purpose host interrupt: 4. - pub general4, set_general4: 4; + #[enumset(repr = "u8")] + General4 = 16, /// General purpose host interrupt: 5. - pub general5, set_general5: 5; + #[enumset(repr = "u8")] + General5 = 32, /// General purpose host interrupt: 6. - pub general6, set_general6: 6; + #[enumset(repr = "u8")] + General6 = 64, /// General purpose host interrupt: 7. - pub general7, set_general7: 7; + #[enumset(repr = "u8")] + General7 = 128, } impl HostInterrupt { /// Creates a new [HostInterrupt]. pub const fn new() -> Self { - Self(0) + Self::General0 } /// Gets the raw bit value of the [HostInterrupt]. pub const fn bits(&self) -> u8 { - self.0 + *self as u8 } } @@ -45,9 +51,21 @@ impl From for u8 { } } -impl From for HostInterrupt { - fn from(val: u8) -> Self { - Self(val) +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), + } } } @@ -57,42 +75,48 @@ impl Default for HostInterrupt { } } -bitfield! { - /// 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. - #[repr(C)] - #[derive(Clone, Copy, Debug, Eq, PartialEq)] - pub struct DeviceInterrupt(u8); +/// 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. - pub general0, set_general0: 0; + #[enumset(repr = "u8")] + General0 = 1, /// General purpose host interrupt: 1. - pub general1, set_general1: 1; + #[enumset(repr = "u8")] + General1 = 2, /// General purpose host interrupt: 2. - pub general2, set_general2: 2; + #[enumset(repr = "u8")] + General2 = 4, /// General purpose host interrupt: 3. - pub general3, set_general3: 3; + #[enumset(repr = "u8")] + General3 = 8, /// General purpose host interrupt: 4. - pub general4, set_general4: 4; + #[enumset(repr = "u8")] + General4 = 16, /// General purpose host interrupt: 5. - pub general5, set_general5: 5; + #[enumset(repr = "u8")] + General5 = 32, /// General purpose host interrupt: 6. - pub general6, set_general6: 6; + #[enumset(repr = "u8")] + General6 = 64, /// General purpose host interrupt: 7. - pub general7, set_general7: 7; + #[enumset(repr = "u8")] + General7 = 128, } impl DeviceInterrupt { /// Creates a new [DeviceInterrupt]. pub const fn new() -> Self { - Self(0) + Self::General0 } /// Gets the raw bit value of the [DeviceInterrupt]. pub const fn bits(&self) -> u8 { - self.0 + *self as u8 } } @@ -102,9 +126,21 @@ impl From for u8 { } } -impl From for DeviceInterrupt { - fn from(val: u8) -> Self { - Self(val) +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), + } } } From 47bdef318f5ddbd4d68a61c9dd88d34e33f060c8 Mon Sep 17 00:00:00 2001 From: rmsyn Date: Mon, 14 Jul 2025 16:50:54 +0000 Subject: [PATCH 37/50] fixup: dma: remove `Generic` suffix Removes `Generic` suffix from generic DMA types. Replaces default type aliases with a default concrete type in the generic parameter declaration. Authored-by: rmsyn Co-authored-by: Dominic Fischer <14130965+Dominaezzz@users.noreply.github.com> --- esp-hal/src/dma/buffers.rs | 94 +++++++++++++++----------------------- esp-hal/src/dma/mod.rs | 74 ++++++++++++++---------------- esp-hal/src/sdio/dma.rs | 34 +++++++------- 3 files changed, 89 insertions(+), 113 deletions(-) diff --git a/esp-hal/src/dma/buffers.rs b/esp-hal/src/dma/buffers.rs index 9ad5b4c8944..020c6085b34 100644 --- a/esp-hal/src/dma/buffers.rs +++ b/esp-hal/src/dma/buffers.rs @@ -351,15 +351,12 @@ pub enum TransferDirection { Out, } -/// Convenience alias for general DMA preparation. -pub type Preparation = PreparationGeneric; - /// 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 PreparationGeneric { +pub struct Preparation { /// The descriptor the DMA will start from. - pub start: *mut DmaDescriptorGeneric, + pub start: *mut DmaDescriptor, /// The direction of the DMA transfer. pub direction: TransferDirection, @@ -488,9 +485,6 @@ pub unsafe trait DmaRxBuffer { /// descriptors/buffers. pub struct BufView(T); -/// Convenience alias for the general DMA TX buffer. -pub type DmaTxBuf = DmaTxBufGeneric; - /// DMA transmit buffer /// /// This is a contiguous buffer linked together by DMA descriptors of length @@ -498,13 +492,13 @@ pub type DmaTxBuf = DmaTxBufGeneric; /// FIFO. See [DmaRxBuf] for receiving data. #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct DmaTxBufGeneric { - descriptors: DescriptorSetGeneric<'static, Flag>, +pub struct DmaTxBuf { + descriptors: DescriptorSet<'static, Flag>, buffer: &'static mut [u8], burst: BurstConfig, } -impl DmaTxBufGeneric { +impl DmaTxBuf { /// Creates a new [DmaTxBuf] from some descriptors and a buffer. /// /// There must be enough descriptors for the provided buffer. @@ -514,7 +508,7 @@ impl DmaTxBufGeneric { /// Both the descriptors and buffer must be in DMA-capable memory. /// Only DRAM is supported for descriptors. pub fn new( - descriptors: &'static mut [DmaDescriptorGeneric], + descriptors: &'static mut [DmaDescriptor], buffer: &'static mut [u8], ) -> Result { Self::new_with_config(descriptors, buffer, BurstConfig::default()) @@ -529,12 +523,12 @@ impl DmaTxBufGeneric { /// 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 [DmaDescriptorGeneric], + descriptors: &'static mut [DmaDescriptor], buffer: &'static mut [u8], config: impl Into, ) -> Result { let mut buf = Self { - descriptors: DescriptorSetGeneric::new(descriptors)?, + descriptors: DescriptorSet::new(descriptors)?, buffer, burst: BurstConfig::default(), }; @@ -569,7 +563,7 @@ impl DmaTxBufGeneric { } /// Consume the buf, returning the descriptors and buffer. - pub fn split(self) -> (&'static mut [DmaDescriptorGeneric], &'static mut [u8]) { + pub fn split(self) -> (&'static mut [DmaDescriptor], &'static mut [u8]) { (self.descriptors.into_inner(), self.buffer) } @@ -679,9 +673,6 @@ unsafe impl DmaTxBuffer for DmaTxBuf { } } -/// Convenience alias for the general DMA RX buffer. -pub type DmaRxBuf = DmaRxBufGeneric; - /// DMA receive buffer /// /// This is a contiguous buffer linked together by DMA descriptors of length @@ -689,13 +680,13 @@ pub type DmaRxBuf = DmaRxBufGeneric; /// See [DmaTxBuf] for transmitting data. #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct DmaRxBufGeneric { - descriptors: DescriptorSetGeneric<'static, Flag>, +pub struct DmaRxBuf { + descriptors: DescriptorSet<'static, Flag>, buffer: &'static mut [u8], burst: BurstConfig, } -impl DmaRxBufGeneric { +impl DmaRxBuf { /// Creates a new [DmaRxBuf] from some descriptors and a buffer. /// /// There must be enough descriptors for the provided buffer. @@ -704,7 +695,7 @@ impl DmaRxBufGeneric { /// Both the descriptors and buffer must be in DMA-capable memory. /// Only DRAM is supported. pub fn new( - descriptors: &'static mut [DmaDescriptorGeneric], + descriptors: &'static mut [DmaDescriptor], buffer: &'static mut [u8], ) -> Result { Self::new_with_config(descriptors, buffer, BurstConfig::default()) @@ -719,12 +710,12 @@ impl DmaRxBufGeneric { /// 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 [DmaDescriptorGeneric], + descriptors: &'static mut [DmaDescriptor], buffer: &'static mut [u8], config: impl Into, ) -> Result { let mut buf = Self { - descriptors: DescriptorSetGeneric::new(descriptors)?, + descriptors: DescriptorSet::new(descriptors)?, buffer, burst: BurstConfig::default(), }; @@ -758,7 +749,7 @@ impl DmaRxBufGeneric { } /// Consume the buf, returning the descriptors and buffer. - pub fn split(self) -> (&'static mut [DmaDescriptorGeneric], &'static mut [u8]) { + pub fn split(self) -> (&'static mut [DmaDescriptor], &'static mut [u8]) { (self.descriptors.into_inner(), self.buffer) } @@ -895,9 +886,6 @@ unsafe impl DmaRxBuffer for DmaRxBuf { } } -/// Convenience alias for the general DMA RX/TX buffer. -pub type DmaRxTxBuf = DmaRxTxBufGeneric; - /// DMA transmit and receive buffer. /// /// This is a (single) contiguous buffer linked together by two sets of DMA @@ -906,14 +894,14 @@ pub type DmaRxTxBuf = DmaRxTxBufGeneric; /// peripheral's FIFO. These are typically full-duplex transfers. #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct DmaRxTxBufGeneric { - rx_descriptors: DescriptorSetGeneric<'static, Flag>, - tx_descriptors: DescriptorSetGeneric<'static, Flag>, +pub struct DmaRxTxBuf { + rx_descriptors: DescriptorSet<'static, Flag>, + tx_descriptors: DescriptorSet<'static, Flag>, buffer: &'static mut [u8], burst: BurstConfig, } -impl DmaRxTxBufGeneric { +impl DmaRxTxBuf { /// Creates a new [DmaRxTxBuf] from some descriptors and a buffer. /// /// There must be enough descriptors for the provided buffer. @@ -922,13 +910,13 @@ impl DmaRxTxBufGeneric { /// Both the descriptors and buffer must be in DMA-capable memory. /// Only DRAM is supported. pub fn new( - rx_descriptors: &'static mut [DmaDescriptorGeneric], - tx_descriptors: &'static mut [DmaDescriptorGeneric], + rx_descriptors: &'static mut [DmaDescriptor], + tx_descriptors: &'static mut [DmaDescriptor], buffer: &'static mut [u8], ) -> Result { let mut buf = Self { - rx_descriptors: DescriptorSetGeneric::new(rx_descriptors)?, - tx_descriptors: DescriptorSetGeneric::new(tx_descriptors)?, + rx_descriptors: DescriptorSet::new(rx_descriptors)?, + tx_descriptors: DescriptorSet::new(tx_descriptors)?, buffer, burst: BurstConfig::default(), }; @@ -972,8 +960,8 @@ impl DmaRxTxBufGeneric { pub fn split( self, ) -> ( - &'static mut [DmaDescriptorGeneric], - &'static mut [DmaDescriptorGeneric], + &'static mut [DmaDescriptor], + &'static mut [DmaDescriptor], &'static mut [u8], ) { ( @@ -1127,9 +1115,6 @@ unsafe impl DmaRxBuffer for DmaRxTxBuf { } } -/// Convenience alias for general DMA RX stream buffer. -pub type DmaRxStreamBuf = DmaRxStreamBufGeneric; - /// DMA Streaming Receive Buffer. /// /// This is a contiguous buffer linked together by DMA descriptors, and the @@ -1170,17 +1155,17 @@ pub type DmaRxStreamBuf = DmaRxStreamBufGeneric; /// /// See [DmaRxStreamBufView] for APIs available whilst a transfer is in /// progress. -pub struct DmaRxStreamBufGeneric { - descriptors: &'static mut [DmaDescriptorGeneric], +pub struct DmaRxStreamBuf { + descriptors: &'static mut [DmaDescriptor], buffer: &'static mut [u8], burst: BurstConfig, } -impl DmaRxStreamBufGeneric { +impl DmaRxStreamBuf { /// Creates a new [DmaRxStreamBuf] evenly distributing the buffer between /// the provided descriptors. pub fn new( - descriptors: &'static mut [DmaDescriptorGeneric], + descriptors: &'static mut [DmaDescriptor], buffer: &'static mut [u8], ) -> Result { if !is_slice_in_dram(descriptors) { @@ -1230,7 +1215,7 @@ impl DmaRxStreamBufGeneric { } /// Consume the buf, returning the descriptors and buffer. - pub fn split(self) -> (&'static mut [DmaDescriptorGeneric], &'static mut [u8]) { + pub fn split(self) -> (&'static mut [DmaDescriptor], &'static mut [u8]) { (self.descriptors, self.buffer) } } @@ -1512,9 +1497,6 @@ unsafe impl DmaRxBuffer for EmptyBuf { } } -/// Convenience alias for general DMA loop buffer. -pub type DmaLoopBuf = DmaLoopBufGeneric; - /// DMA Loop Buffer /// /// This consists of a single descriptor that points to itself and points to a @@ -1525,15 +1507,15 @@ pub type DmaLoopBuf = DmaLoopBufGeneric; /// 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 DmaLoopBufGeneric { - descriptor: &'static mut DmaDescriptorGeneric, +pub struct DmaLoopBuf { + descriptor: &'static mut DmaDescriptor, buffer: &'static mut [u8], } -impl DmaLoopBufGeneric { +impl DmaLoopBuf { /// Create a new [DmaLoopBuf]. pub fn new( - descriptor: &'static mut DmaDescriptorGeneric, + descriptor: &'static mut DmaDescriptor, buffer: &'static mut [u8], ) -> Result { if !is_slice_in_dram(buffer) { @@ -1559,7 +1541,7 @@ impl DmaLoopBufGeneric { } /// Consume the buf, returning the descriptor and buffer. - pub fn split(self) -> (&'static mut DmaDescriptorGeneric, &'static mut [u8]) { + pub fn split(self) -> (&'static mut DmaDescriptor, &'static mut [u8]) { (self.descriptor, self.buffer) } } @@ -1593,7 +1575,7 @@ unsafe impl DmaTxBuffer for DmaLoopBuf { } } -impl Deref for DmaLoopBufGeneric { +impl Deref for DmaLoopBuf { type Target = [u8]; fn deref(&self) -> &Self::Target { @@ -1601,7 +1583,7 @@ impl Deref for DmaLoopBufGeneric { } } -impl DerefMut for DmaLoopBufGeneric { +impl DerefMut for DmaLoopBuf { fn deref_mut(&mut self) -> &mut Self::Target { self.buffer } diff --git a/esp-hal/src/dma/mod.rs b/esp-hal/src/dma/mod.rs index 73e00de2747..162e254accc 100644 --- a/esp-hal/src/dma/mod.rs +++ b/esp-hal/src/dma/mod.rs @@ -296,16 +296,16 @@ impl defmt::Format for DmaDescriptorFlags { } } -/// Convenience alias for the DMA descriptor used with the general DMA controller. -pub type DmaDescriptor = DmaDescriptorGeneric; +///// 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 DmaDescriptorGeneric { +pub struct DmaDescriptor { /// Descriptor flags. - pub flags: FLAGS, + pub flags: Flags, /// Address of the buffer. pub buffer: *mut u8, @@ -316,9 +316,9 @@ pub struct DmaDescriptorGeneric { pub next: *mut Self, } -impl DmaDescriptorGeneric { +impl DmaDescriptor { const _SIZE_CHECK: () = core::assert!( - core::mem::size_of::() == core::mem::size_of::(), + core::mem::size_of::() == core::mem::size_of::(), "descriptor flags must be the same size as `u32`" ); @@ -349,10 +349,10 @@ impl DmaDescriptorGeneric { } } -impl DescriptorFlagFields for DmaDescriptorGeneric { +impl DescriptorFlagFields for DmaDescriptor { fn empty() -> Self { Self { - flags: FLAGS::empty(), + flags: Flags::empty(), buffer: core::ptr::null_mut(), next: core::ptr::null_mut(), } @@ -1153,25 +1153,22 @@ pub const fn descriptor_count(buffer_size: usize, chunk_size: usize, is_circular buffer_size.div_ceil(chunk_size) } -/// Convenience alias for the general DMA descriptor set. -pub(crate) type DescriptorSet<'a> = DescriptorSetGeneric<'a, DmaDescriptorFlags>; - /// Represents a container for a set of DMA descriptors. #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub(crate) struct DescriptorSetGeneric<'a, Flag: DescriptorFlagFields> { - descriptors: &'a mut [DmaDescriptorGeneric], +pub(crate) struct DescriptorSet<'a, Flag: DescriptorFlagFields = DmaDescriptorFlags> { + descriptors: &'a mut [DmaDescriptor], } -impl<'a, Flag: DescriptorFlagFields + Clone> DescriptorSetGeneric<'a, Flag> { +impl<'a, Flag: DescriptorFlagFields + Clone> DescriptorSet<'a, Flag> { /// Creates a new `DescriptorSet` from a slice of descriptors and associates /// them with the given buffer. - fn new(descriptors: &'a mut [DmaDescriptorGeneric]) -> Result { + fn new(descriptors: &'a mut [DmaDescriptor]) -> Result { if !is_slice_in_dram(descriptors) { return Err(DmaBufError::UnsupportedMemoryRegion); } - descriptors.fill(DmaDescriptorGeneric::empty()); + descriptors.fill(DmaDescriptor::empty()); Ok(unsafe { Self::new_unchecked(descriptors) }) } @@ -1183,22 +1180,22 @@ impl<'a, Flag: DescriptorFlagFields + Clone> DescriptorSetGeneric<'a, Flag> { /// /// The caller must ensure that the descriptors are located in a supported /// memory region. - unsafe fn new_unchecked(descriptors: &'a mut [DmaDescriptorGeneric]) -> 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 [DmaDescriptorGeneric] { + fn into_inner(self) -> &'a mut [DmaDescriptor] { self.descriptors } /// Returns a pointer to the first descriptor in the chain. - fn head(&mut self) -> *mut DmaDescriptorGeneric { + 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 { @@ -1213,7 +1210,7 @@ impl<'a, Flag: DescriptorFlagFields + Clone> DescriptorSetGeneric<'a, Flag> { /// Returns an iterator over the linked descriptors. fn linked_iter_mut( &mut self, - ) -> impl Iterator> + use<'_, Flag> { + ) -> impl Iterator> + use<'_, Flag> { let mut was_last = false; self.descriptors.iter_mut().take_while(move |d| { if was_last { @@ -1245,7 +1242,7 @@ impl<'a, Flag: DescriptorFlagFields + Clone> DescriptorSetGeneric<'a, Flag> { &mut self, len: usize, chunk_size: usize, - prepare: fn(&mut DmaDescriptorGeneric, usize), + prepare: fn(&mut DmaDescriptor, usize), ) -> Result<(), DmaBufError> { Self::set_up_descriptors(self.descriptors, len, chunk_size, false, prepare) } @@ -1270,11 +1267,11 @@ impl<'a, Flag: DescriptorFlagFields + Clone> DescriptorSetGeneric<'a, Flag> { /// Returns a slice of descriptors that can cover a buffer of length `len`. fn descriptors_for_buffer_len( - descriptors: &mut [DmaDescriptorGeneric], + descriptors: &mut [DmaDescriptor], len: usize, chunk_size: usize, is_circular: bool, - ) -> Result<&mut [DmaDescriptorGeneric], 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 { @@ -1291,11 +1288,11 @@ impl<'a, Flag: DescriptorFlagFields + Clone> DescriptorSetGeneric<'a, Flag> { /// The actual descriptor setup is done in a callback, because different /// transfer directions require different descriptor setup. fn set_up_descriptors( - descriptors: &mut [DmaDescriptorGeneric], + descriptors: &mut [DmaDescriptor], len: usize, chunk_size: usize, is_circular: bool, - prepare: impl Fn(&mut DmaDescriptorGeneric, usize), + prepare: impl Fn(&mut DmaDescriptor, usize), ) -> Result<(), DmaBufError> { let descriptors = Self::descriptors_for_buffer_len(descriptors, len, chunk_size, is_circular)?; @@ -1336,7 +1333,7 @@ impl<'a, Flag: DescriptorFlagFields + Clone> DescriptorSetGeneric<'a, Flag> { /// repeatedly. fn set_up_buffer_ptrs( buffer: &mut [u8], - descriptors: &mut [DmaDescriptorGeneric], + descriptors: &mut [DmaDescriptor], chunk_size: usize, is_circular: bool, ) -> Result<(), DmaBufError> { @@ -2100,12 +2097,9 @@ where } } -/// Convenience alias for the general DMA transmit channel. -pub type ChannelTx = ChannelTxGeneric; - /// DMA transmit channel #[doc(hidden)] -pub struct ChannelTxGeneric +pub struct ChannelTx where Dm: DriverMode, CH: DmaTxChannel, @@ -2117,7 +2111,7 @@ where pub(crate) _flag: PhantomData, } -impl ChannelTxGeneric +impl ChannelTx where CH: DmaTxChannel, F: DescriptorFlagFields, @@ -2141,14 +2135,14 @@ where } /// Converts a blocking channel to an async channel. - pub(crate) fn into_async(mut self) -> ChannelTxGeneric { + 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); - ChannelTxGeneric:: { + ChannelTx:: { tx_impl: self.tx_impl, _phantom: PhantomData, _guard: self._guard, @@ -2170,20 +2164,20 @@ where } } -impl ChannelTxGeneric +impl ChannelTx where CH: DmaTxChannel, F: DescriptorFlagFields, { /// Converts an async channel into a blocking channel. - pub(crate) fn into_blocking(self) -> ChannelTxGeneric { + 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); - ChannelTxGeneric:: { + ChannelTx:: { tx_impl: self.tx_impl, _phantom: PhantomData, _guard: self._guard, @@ -2192,7 +2186,7 @@ where } } -impl ChannelTxGeneric +impl ChannelTx where Dm: DriverMode, CH: DmaTxChannel, @@ -2239,7 +2233,7 @@ where } } -impl crate::private::Sealed for ChannelTxGeneric +impl crate::private::Sealed for ChannelTx where Dm: DriverMode, CH: DmaTxChannel, @@ -2248,7 +2242,7 @@ where } #[allow(unused)] -impl ChannelTxGeneric +impl ChannelTx where Dm: DriverMode, CH: DmaTxChannel, diff --git a/esp-hal/src/sdio/dma.rs b/esp-hal/src/sdio/dma.rs index 829c4cc3e8a..617dc42c26b 100644 --- a/esp-hal/src/sdio/dma.rs +++ b/esp-hal/src/sdio/dma.rs @@ -3,42 +3,42 @@ use core::fmt::Debug; use crate::dma::{ - ChannelTxGeneric, + ChannelTx as ChannelTxType, DescriptorFlagFields, - DescriptorSetGeneric, - DmaDescriptorGeneric, - DmaLoopBufGeneric, - DmaRxBufGeneric, - DmaRxTxBufGeneric, - DmaTxBufGeneric, + DescriptorSet as DescriptorSetType, + DmaDescriptor as DmaDescriptorType, + DmaLoopBuf as DmaLoopBufType, + DmaRxBuf as DmaRxBufType, + DmaRxTxBuf as DmaRxTxBufType, + DmaTxBuf as DmaTxBufType, Owner, - PreparationGeneric, + Preparation as PreparationType, }; -/// Convenience alias for the DMA descriptor used with the SDIO dedicated DMA controller. -pub type DmaDescriptor = DmaDescriptorGeneric; +/// Convenience alias for the SDIO dedicated DMA descriptor. +pub type DmaDescriptor = DmaDescriptorType; /// Convenience alias for SDIO dedicated DMA preparation. -pub type Preparation = PreparationGeneric; +pub type Preparation = PreparationType; /// Convenience alias for SDIO dedicated DMA loop buffer. -pub type DmaLoopBuf = DmaLoopBufGeneric; +pub type DmaLoopBuf = DmaLoopBufType; /// Convenience alias for the SDIO dedicated DMA descriptor set. #[allow(unused)] -pub(crate) type DescriptorSet<'a> = DescriptorSetGeneric<'a, DmaDescriptorFlags>; +pub(crate) type DescriptorSet<'a> = DescriptorSetType<'a, DmaDescriptorFlags>; /// Convenience alias for the SDIO dedicated DMA RX buffer. -pub type DmaTxBuf = DmaTxBufGeneric; +pub type DmaTxBuf = DmaTxBufType; /// Convenience alias for the SDIO dedicated DMA RX buffer. -pub type DmaRxBuf = DmaRxBufGeneric; +pub type DmaRxBuf = DmaRxBufType; /// Convenience alias for the SDIO dedicated DMA RX/TX buffer. -pub type DmaRxTxBuf = DmaRxTxBufGeneric; +pub type DmaRxTxBuf = DmaRxTxBufType; /// Convenience alias for the SDIO dedicated DMA transmit channel. -pub type ChannelTx = ChannelTxGeneric; +pub type ChannelTx = ChannelTxType; bitfield::bitfield! { /// DMA descriptor flags for the dedicated SDIO DMA engine. From bca63d835edc9fb6cc46244cf38ff35c01c7521a Mon Sep 17 00:00:00 2001 From: rmsyn Date: Mon, 14 Jul 2025 17:15:15 +0000 Subject: [PATCH 38/50] dma: add generic parameter to ChannelRx Adds a generic paramter for the descriptor flags for the `ChannelRx` type. --- esp-hal/src/dma/mod.rs | 28 +++++++++++++++++++--------- esp-hal/src/sdio/dma.rs | 4 ++++ 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/esp-hal/src/dma/mod.rs b/esp-hal/src/dma/mod.rs index 162e254accc..efc319a3a9d 100644 --- a/esp-hal/src/dma/mod.rs +++ b/esp-hal/src/dma/mod.rs @@ -1830,19 +1830,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 { @@ -1864,11 +1867,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); } @@ -1877,6 +1881,7 @@ where rx_impl: self.rx_impl, _phantom: PhantomData, _guard: self._guard, + _flag: PhantomData, } } @@ -1894,12 +1899,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); } @@ -1908,14 +1914,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)] @@ -1956,18 +1964,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. @@ -2099,7 +2109,7 @@ where /// DMA transmit channel #[doc(hidden)] -pub struct ChannelTx +pub struct ChannelTx where Dm: DriverMode, CH: DmaTxChannel, diff --git a/esp-hal/src/sdio/dma.rs b/esp-hal/src/sdio/dma.rs index 617dc42c26b..d74010c41f5 100644 --- a/esp-hal/src/sdio/dma.rs +++ b/esp-hal/src/sdio/dma.rs @@ -3,6 +3,7 @@ use core::fmt::Debug; use crate::dma::{ + ChannelRx as ChannelRxType, ChannelTx as ChannelTxType, DescriptorFlagFields, DescriptorSet as DescriptorSetType, @@ -40,6 +41,9 @@ 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. /// From 69b19ba3fb81841269037bb204b4ed8906a800b7 Mon Sep 17 00:00:00 2001 From: rmsyn Date: Mon, 14 Jul 2025 18:20:48 +0000 Subject: [PATCH 39/50] dma: make DmaTxBuffer generic Makes the `DmaTxBuffer` generic over the `DescriptorFlagFields` trait. --- esp-hal/src/dma/buffers.rs | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/esp-hal/src/dma/buffers.rs b/esp-hal/src/dma/buffers.rs index 020c6085b34..90d6c426867 100644 --- a/esp-hal/src/dma/buffers.rs +++ b/esp-hal/src/dma/buffers.rs @@ -422,7 +422,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 +436,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; @@ -633,12 +633,11 @@ impl DmaTxBuf { } } -// TODO: make generic after making DmaTxBuffer generic -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); @@ -664,7 +663,7 @@ unsafe impl DmaTxBuffer for DmaTxBuf { } } - fn into_view(self) -> BufView { + fn into_view(self) -> Self::View { BufView(self) } @@ -1023,12 +1022,11 @@ impl DmaRxTxBuf { } } -// TODO: make generic after making DmaTxBuffer generic -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. @@ -1061,7 +1059,7 @@ unsafe impl DmaTxBuffer for DmaRxTxBuf { } } - fn into_view(self) -> BufView { + fn into_view(self) -> Self::View { BufView(self) } @@ -1546,12 +1544,11 @@ impl DmaLoopBuf { } } -// TODO: make this generic after DmaTxBuffer is generic -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)] From dee5564d30022e67ec04f9ecfa6eb47ad4b13a61 Mon Sep 17 00:00:00 2001 From: rmsyn Date: Mon, 14 Jul 2025 18:32:53 +0000 Subject: [PATCH 40/50] dma: make DmaRxBuffer generic Makes the `DmaRxBuffer` trait generic over the `DescriptorFlagFields` trait. --- esp-hal/src/dma/buffers.rs | 46 +++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/esp-hal/src/dma/buffers.rs b/esp-hal/src/dma/buffers.rs index 90d6c426867..e888274b3b5 100644 --- a/esp-hal/src/dma/buffers.rs +++ b/esp-hal/src/dma/buffers.rs @@ -456,7 +456,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 +470,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; @@ -840,12 +840,11 @@ impl DmaRxBuf { } } -// TODO: make generic after making DmaRxBuffer generic -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(); } @@ -876,7 +875,7 @@ unsafe impl DmaRxBuffer for DmaRxBuf { } } - fn into_view(self) -> BufView { + fn into_view(self) -> Self::View { BufView(self) } @@ -1068,12 +1067,11 @@ unsafe impl DmaTxBuffer for DmaRxTxBuf { } } -// TODO: make generic after making DmaRxBuffer generic -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(); } @@ -1104,7 +1102,7 @@ unsafe impl DmaRxBuffer for DmaRxTxBuf { } } - fn into_view(self) -> BufView { + fn into_view(self) -> Self::View { BufView(self) } @@ -1218,12 +1216,11 @@ impl DmaRxStreamBuf { } } -// TODO: make generic after making DmaRxBuffer generic -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() { @@ -1248,7 +1245,7 @@ unsafe impl DmaRxBuffer for DmaRxStreamBuf { } } - fn into_view(self) -> DmaRxStreamBufView { + fn into_view(self) -> Self::View { DmaRxStreamBufView { buf: self, descriptor_idx: 0, @@ -1262,13 +1259,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); From 8bdec9d745e049a939d7058917e7d450ea2df883 Mon Sep 17 00:00:00 2001 From: rmsyn Date: Mon, 14 Jul 2025 18:54:58 +0000 Subject: [PATCH 41/50] fixup: dma: consistent generic parameter naming Uses consistent naming + formatting for descriptor flag generic parameters. --- esp-hal/src/dma/buffers.rs | 82 +++++++++++++++++++++++--------------- esp-hal/src/dma/mod.rs | 50 ++++++++++++----------- 2 files changed, 77 insertions(+), 55 deletions(-) diff --git a/esp-hal/src/dma/buffers.rs b/esp-hal/src/dma/buffers.rs index e888274b3b5..2f8f5e43e2d 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, @@ -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, Flag>, +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) } @@ -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, Flag>, +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) } @@ -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, Flag>, - tx_descriptors: DescriptorSet<'static, Flag>, +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], ) { ( @@ -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,7 +1226,7 @@ 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) } } @@ -1505,15 +1520,18 @@ 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 { if !is_slice_in_dram(buffer) { @@ -1539,7 +1557,7 @@ 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) } } diff --git a/esp-hal/src/dma/mod.rs b/esp-hal/src/dma/mod.rs index efc319a3a9d..8e1d85ddf10 100644 --- a/esp-hal/src/dma/mod.rs +++ b/esp-hal/src/dma/mod.rs @@ -303,9 +303,12 @@ impl defmt::Format for DmaDescriptorFlags { #[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: Flags, + pub flags: F, /// Address of the buffer. pub buffer: *mut u8, @@ -316,9 +319,9 @@ pub struct DmaDescriptor { pub next: *mut Self, } -impl DmaDescriptor { +impl DmaDescriptor { const _SIZE_CHECK: () = core::assert!( - core::mem::size_of::() == core::mem::size_of::(), + core::mem::size_of::() == core::mem::size_of::(), "descriptor flags must be the same size as `u32`" ); @@ -349,10 +352,10 @@ impl DmaDescriptor { } } -impl DescriptorFlagFields for DmaDescriptor { +impl DescriptorFlagFields for DmaDescriptor { fn empty() -> Self { Self { - flags: Flags::empty(), + flags: F::empty(), buffer: core::ptr::null_mut(), next: core::ptr::null_mut(), } @@ -1156,14 +1159,17 @@ pub const fn descriptor_count(buffer_size: usize, chunk_size: usize, is_circular /// Represents a container for a set of DMA descriptors. #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub(crate) struct DescriptorSet<'a, Flag: DescriptorFlagFields = DmaDescriptorFlags> { - descriptors: &'a mut [DmaDescriptor], +pub(crate) struct DescriptorSet<'a, F = DmaDescriptorFlags> +where + F: DescriptorFlagFields, +{ + descriptors: &'a mut [DmaDescriptor], } -impl<'a, Flag: DescriptorFlagFields + Clone> DescriptorSet<'a, Flag> { +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); } @@ -1180,22 +1186,22 @@ impl<'a, Flag: DescriptorFlagFields + Clone> DescriptorSet<'a, Flag> { /// /// 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 { @@ -1208,9 +1214,7 @@ impl<'a, Flag: DescriptorFlagFields + Clone> DescriptorSet<'a, Flag> { } /// Returns an iterator over the linked descriptors. - fn linked_iter_mut( - &mut self, - ) -> impl Iterator> + use<'_, Flag> { + 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 { @@ -1242,7 +1246,7 @@ impl<'a, Flag: DescriptorFlagFields + Clone> DescriptorSet<'a, Flag> { &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) } @@ -1267,11 +1271,11 @@ impl<'a, Flag: DescriptorFlagFields + Clone> DescriptorSet<'a, Flag> { /// 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 { @@ -1288,11 +1292,11 @@ impl<'a, Flag: DescriptorFlagFields + Clone> DescriptorSet<'a, Flag> { /// 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)?; @@ -1333,7 +1337,7 @@ impl<'a, Flag: DescriptorFlagFields + Clone> DescriptorSet<'a, Flag> { /// repeatedly. fn set_up_buffer_ptrs( buffer: &mut [u8], - descriptors: &mut [DmaDescriptor], + descriptors: &mut [DmaDescriptor], chunk_size: usize, is_circular: bool, ) -> Result<(), DmaBufError> { From e6ac45c75e9ce7de1e4bd7d1b58acea68153a72f Mon Sep 17 00:00:00 2001 From: rmsyn Date: Wed, 16 Jul 2025 15:53:47 +0000 Subject: [PATCH 42/50] fixup: ci: esp toolchain version typo --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 51759c03fc0..fcc27c8989b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -118,7 +118,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 From 93e0e478e860aece0ab2887b7bf356f1722f32b0 Mon Sep 17 00:00:00 2001 From: rmsyn Date: Wed, 16 Jul 2025 16:26:51 +0000 Subject: [PATCH 43/50] fixup: dma: re-add `set_length` functions To avoid a semver bump, re-add the `set_length` methods to `DmaDescriptorFlags` and `DmaDescriptor`. --- esp-hal/src/dma/mod.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/esp-hal/src/dma/mod.rs b/esp-hal/src/dma/mod.rs index 8e1d85ddf10..72e723bfd7f 100644 --- a/esp-hal/src/dma/mod.rs +++ b/esp-hal/src/dma/mod.rs @@ -233,6 +233,18 @@ bitfield::bitfield! { _owner, _set_owner: 31; } +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) @@ -339,6 +351,11 @@ impl DmaDescriptor { 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 /// [DmaDescriptorFlags::suc_eof] for more details on the `set_eof` /// parameter. From cdc8de1e255cba7707507b3becac5a603710b7df Mon Sep 17 00:00:00 2001 From: rmsyn Date: Mon, 21 Jul 2025 02:41:51 +0000 Subject: [PATCH 44/50] sdio: add DMA atomic descriptor Adds the `AtomicDmaDescriptor` type to enable safely storing DMA descriptors in static variables. --- esp-hal/src/sdio/dma.rs | 43 +++++++ esp-hal/src/sdio/dma/descriptor.rs | 173 +++++++++++++++++++++++++++++ 2 files changed, 216 insertions(+) create mode 100644 esp-hal/src/sdio/dma/descriptor.rs diff --git a/esp-hal/src/sdio/dma.rs b/esp-hal/src/sdio/dma.rs index d74010c41f5..fa3db23835d 100644 --- a/esp-hal/src/sdio/dma.rs +++ b/esp-hal/src/sdio/dma.rs @@ -16,6 +16,10 @@ use crate::dma::{ Preparation as PreparationType, }; +mod descriptor; + +pub use descriptor::{AtomicDmaDescriptor, AtomicDmaDescriptors}; + /// Convenience alias for the SDIO dedicated DMA descriptor. pub type DmaDescriptor = DmaDescriptorType; @@ -67,6 +71,16 @@ impl 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 { @@ -75,6 +89,18 @@ impl Default for DmaDescriptorFlags { } } +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") @@ -137,3 +163,20 @@ impl DescriptorFlagFields for DmaDescriptorFlags { 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/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() + } +} From 00a6feab996d4356768bf4a92606405d4c679040 Mon Sep 17 00:00:00 2001 From: rmsyn Date: Mon, 21 Jul 2025 02:55:56 +0000 Subject: [PATCH 45/50] sdio: add atomic buffer Adds the `AtomicBuffer` type to allow storing byte buffers safely in static variables. --- esp-hal/src/sdio/dma.rs | 2 + esp-hal/src/sdio/dma/buffer.rs | 93 ++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 esp-hal/src/sdio/dma/buffer.rs diff --git a/esp-hal/src/sdio/dma.rs b/esp-hal/src/sdio/dma.rs index fa3db23835d..1a1f0427671 100644 --- a/esp-hal/src/sdio/dma.rs +++ b/esp-hal/src/sdio/dma.rs @@ -16,8 +16,10 @@ use crate::dma::{ Preparation as PreparationType, }; +mod buffer; mod descriptor; +pub use buffer::AtomicBuffer; pub use descriptor::{AtomicDmaDescriptor, AtomicDmaDescriptors}; /// Convenience alias for the SDIO dedicated DMA descriptor. 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() + } +} From a895ffd62e14ebc313bca399f60355f0be2929de Mon Sep 17 00:00:00 2001 From: rmsyn Date: Fri, 22 Aug 2025 23:17:43 +0000 Subject: [PATCH 46/50] dep: add `bitfielder` dependency Adds the `bitfielder` library as a more performant and ergonomic bitfield library. --- esp-hal/Cargo.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/esp-hal/Cargo.toml b/esp-hal/Cargo.toml index 6b169472d69..ca2b8e808c2 100644 --- a/esp-hal/Cargo.toml +++ b/esp-hal/Cargo.toml @@ -100,6 +100,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. @@ -281,6 +284,7 @@ unstable = [ "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. From 29dde9ed94fc392a88c287e17ab6d1505ee13e30 Mon Sep 17 00:00:00 2001 From: rmsyn Date: Sun, 7 Sep 2025 02:55:01 +0000 Subject: [PATCH 47/50] sdio: add command types Adds SDIO commands supported by ESP SDIO controllers. --- esp-hal/src/gpio/mod.rs | 2 +- esp-hal/src/sdio.rs | 5 + esp-hal/src/sdio/command.rs | 19 +++ esp-hal/src/sdio/command/block_mode.rs | 76 +++++++++ esp-hal/src/sdio/command/cmd0.rs | 117 +++++++++++++ esp-hal/src/sdio/command/cmd5.rs | 130 +++++++++++++++ esp-hal/src/sdio/command/cmd52.rs | 175 ++++++++++++++++++++ esp-hal/src/sdio/command/cmd53.rs | 192 ++++++++++++++++++++++ esp-hal/src/sdio/command/cmd53/op_code.rs | 73 ++++++++ esp-hal/src/sdio/command/crc.rs | 114 +++++++++++++ esp-hal/src/sdio/command/flag.rs | 140 ++++++++++++++++ esp-hal/src/sdio/command/fn_number.rs | 73 ++++++++ esp-hal/src/sdio/direction.rs | 69 ++++++++ esp-hal/src/sdio/io_ocr.rs | 112 +++++++++++++ 14 files changed, 1296 insertions(+), 1 deletion(-) create mode 100644 esp-hal/src/sdio/command.rs create mode 100644 esp-hal/src/sdio/command/block_mode.rs create mode 100644 esp-hal/src/sdio/command/cmd0.rs create mode 100644 esp-hal/src/sdio/command/cmd5.rs create mode 100644 esp-hal/src/sdio/command/cmd52.rs create mode 100644 esp-hal/src/sdio/command/cmd53.rs create mode 100644 esp-hal/src/sdio/command/cmd53/op_code.rs create mode 100644 esp-hal/src/sdio/command/crc.rs create mode 100644 esp-hal/src/sdio/command/flag.rs create mode 100644 esp-hal/src/sdio/command/fn_number.rs create mode 100644 esp-hal/src/sdio/direction.rs create mode 100644 esp-hal/src/sdio/io_ocr.rs diff --git a/esp-hal/src/gpio/mod.rs b/esp-hal/src/gpio/mod.rs index e425affe984..c501996e45d 100644 --- a/esp-hal/src/gpio/mod.rs +++ b/esp-hal/src/gpio/mod.rs @@ -1175,7 +1175,7 @@ impl<'d> Input<'d> { /// use esp_hal::gpio::{Input, InputConfig, Level}; /// let pin = Input::new(peripherals.GPIO5, InputConfig::default()); /// let level = pin.level(); -/// + /// /// # {after_snippet} /// ``` #[inline] diff --git a/esp-hal/src/sdio.rs b/esp-hal/src/sdio.rs index fdfe8d0386e..ad37d3e9d6f 100644 --- a/esp-hal/src/sdio.rs +++ b/esp-hal/src/sdio.rs @@ -20,10 +20,13 @@ use embedded_hal_sdmmc::{ use crate::pac; +pub mod command; mod config; +mod direction; pub mod dma; mod hinf; mod interrupt; +mod io_ocr; mod pins; mod slc; mod slchost; @@ -31,8 +34,10 @@ 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 io_ocr::IoOcr; pub use pins::Pins; pub use slc::{AnySlc, SlcInfo, SlcInstance}; pub use slchost::{AnySlchost, SlchostInfo, SlchostInstance}; diff --git a/esp-hal/src/sdio/command.rs b/esp-hal/src/sdio/command.rs new file mode 100644 index 00000000000..375929f0cf0 --- /dev/null +++ b/esp-hal/src/sdio/command.rs @@ -0,0 +1,19 @@ +//! SDIO command types. + +mod block_mode; +mod cmd0; +mod cmd5; +mod cmd52; +pub mod cmd53; +mod crc; +mod flag; +mod fn_number; + +pub use block_mode::BlockMode; +pub use cmd0::Cmd0; +pub use cmd5::Cmd5; +pub use cmd52::Cmd52; +pub use cmd53::Cmd53; +pub use crc::Crc; +pub use flag::{RawFlag, RwFlag}; +pub use fn_number::FunctionNumber; 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/cmd0.rs b/esp-hal/src/sdio/command/cmd0.rs new file mode 100644 index 00000000000..f84b403f225 --- /dev/null +++ b/esp-hal/src/sdio/command/cmd0.rs @@ -0,0 +1,117 @@ +use bitfielder::bitfield; + +use super::Crc; +use crate::sdio::{Direction, Error}; + +bitfield! { + /// Represents the SDIO CMD0 `GO_IDLE_STATE` command. + pub Cmd0(MSB0 [u8; 6]): u32 { + raw_s: 47; + raw_d: 46; + raw_cmd_index: 45, 40; + raw_crc: 7, 1; + raw_e: 0; + } +} + +impl Cmd0 { + /// Represents the [Cmd0] command byte length. + pub const LEN: usize = 6; + /// Represents the [Cmd0] command default byte value. + pub const DEFAULT_ARG: [u8; 4] = [0x0; 4]; + /// Represents the [Cmd0] command index. + pub const COMMAND_INDEX: u8 = 5; + + /// Creates a new [Cmd0]. + 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 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 [Cmd0]. + 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.verify_crc().is_err() => Err(Error::Crc), + cmd => Ok(cmd), + }, + } + } + + /// Converts the [Cmd0] into a byte array. + pub const fn into_bytes(self) -> [u8; Self::LEN] { + self.0 + } +} + +impl Default for Cmd0 { + fn default() -> Self { + Self::new() + } +} + +impl TryFrom<&[u8]> for Cmd0 { + type Error = Error; + + fn try_from(val: &[u8]) -> Result { + Self::try_from_bytes(val) + } +} + +impl From for [u8; Cmd0::LEN] { + fn from(val: Cmd0) -> Self { + val.into_bytes() + } +} diff --git a/esp-hal/src/sdio/command/cmd5.rs b/esp-hal/src/sdio/command/cmd5.rs new file mode 100644 index 00000000000..7ef68ad4c52 --- /dev/null +++ b/esp-hal/src/sdio/command/cmd5.rs @@ -0,0 +1,130 @@ +use bitfielder::bitfield; + +use super::Crc; +use crate::sdio::{Direction, Error, IoOcr}; + +bitfield! { + /// Represents the SDIO CMD5 `IO_SEND_OP_COND` command. + /// + /// Can be used to retrieve the contents of the I/O OCR register. + pub Cmd5(MSB0 [u8; 6]): u32 { + raw_s: 47; + raw_d: 46; + raw_cmd_index: 45, 40; + raw_io_ocr: 31, 8; + raw_crc: 7, 1; + raw_e: 0; + } +} + +impl Cmd5 { + /// Represents the [Cmd5] command byte length. + pub const LEN: usize = 6; + /// Represents the [Cmd5] command default byte value. + pub const DEFAULT_ARG: [u8; 4] = [0x0; 4]; + /// Represents the [Cmd5] command index. + pub const COMMAND_INDEX: u8 = 5; + + /// Creates a new [Cmd5]. + 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 I/O OCR argument. + pub const fn io_ocr(&self) -> IoOcr { + IoOcr::from_u32(self.raw_io_ocr()) + } + + /// Sets the I/O OCR argument. + pub fn set_io_ocr(&mut self, io_ocr: IoOcr) { + self.set_raw_io_ocr(io_ocr.into_u32()); + } + + /// 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 [Cmd5]. + 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.verify_crc().is_err() => Err(Error::Crc), + cmd => Ok(cmd), + }, + } + } + + /// Converts the [Cmd5] into a byte array. + pub const fn into_bytes(self) -> [u8; Self::LEN] { + self.0 + } +} + +impl Default for Cmd5 { + fn default() -> Self { + Self::new() + } +} + +impl TryFrom<&[u8]> for Cmd5 { + type Error = Error; + + fn try_from(val: &[u8]) -> Result { + Self::try_from_bytes(val) + } +} + +impl From for [u8; Cmd5::LEN] { + fn from(val: Cmd5) -> Self { + val.into_bytes() + } +} diff --git a/esp-hal/src/sdio/command/cmd52.rs b/esp-hal/src/sdio/command/cmd52.rs new file mode 100644 index 00000000000..64223561553 --- /dev/null +++ b/esp-hal/src/sdio/command/cmd52.rs @@ -0,0 +1,175 @@ +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_reg_addr: 33, 17; + 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/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/io_ocr.rs b/esp-hal/src/sdio/io_ocr.rs new file mode 100644 index 00000000000..10f7aa9c631 --- /dev/null +++ b/esp-hal/src/sdio/io_ocr.rs @@ -0,0 +1,112 @@ +use bitfielder::bitfield; + +use crate::sdio::Error; + +bitfield! { + /// Represents the I/O OCR register values for supported voltage windows. + pub IoOcr(MSB0 [u8; 3]): u8 { + /// Represents support for the 3.5-3.6 voltage window. + v35: 23; + /// Represents support for the 3.4-3.5 voltage window. + v34: 22; + /// Represents support for the 3.3-3.4 voltage window. + v33: 21; + /// Represents support for the 3.2-3.3 voltage window. + v32: 20; + /// Represents support for the 3.1-3.2 voltage window. + v31: 19; + /// Represents support for the 3.0-3.1 voltage window. + v30: 18; + /// Represents support for the 2.9-3.0 voltage window. + v29: 17; + /// Represents support for the 2.8-2.9 voltage window. + v28: 16; + /// Represents support for the 2.7-2.8 voltage window. + v27: 15; + /// Represents support for the 2.6-2.7 voltage window. + v26: 14; + /// Represents support for the 2.5-2.6 voltage window. + v25: 13; + /// Represents support for the 2.4-2.5 voltage window. + v24: 12; + /// Represents support for the 2.3-2.4 voltage window. + v23: 11; + /// Represents support for the 2.2-2.3 voltage window. + v22: 10; + /// Represents support for the 2.1-2.2 voltage window. + v21: 9; + /// Represents support for the 2.0-2.1 voltage window. + v20: 8; + } +} + +impl IoOcr { + /// Represents the byte length of the I/O OCR register. + pub const LEN: usize = 3; + /// Represents the bitmask of the I/O OCR register. + pub const MASK: u32 = 0x00ff_ff00; + /// Represents the default byte value. + pub const DEFAULT: [u8; Self::LEN] = [0u8; Self::LEN]; + + /// Creates a new [IoOcr]. + pub const fn new() -> Self { + Self(Self::DEFAULT) + } + + /// Converts a [`u32`] into a [IoOcr]. + pub const fn from_u32(val: u32) -> Self { + let [_, b0, b1, _] = (val & Self::MASK).to_be_bytes(); + Self([b0, b1, 0]) + } + + /// Converts a [IoOcr] into a [`u32`]. + pub const fn into_u32(self) -> u32 { + let [b0, b1, b2] = self.0; + u32::from_be_bytes([0, b0, b1, b2]) + } + + /// Attempts to convert a byte slice into an [IoOcr]. + pub const fn try_from_bytes(val: &[u8]) -> Result { + match val.len() { + len if len < Self::LEN => Err(Error::General), + _ => Ok(Self([val[0], val[1], val[2]])), + } + } + + /// Converts the [IoOcr] into a byte array. + pub const fn into_bytes(self) -> [u8; Self::LEN] { + self.0 + } +} + +impl Default for IoOcr { + fn default() -> Self { + Self::new() + } +} + +impl From for IoOcr { + fn from(val: u32) -> Self { + Self::from_u32(val) + } +} + +impl From for u32 { + fn from(val: IoOcr) -> Self { + val.into_u32() + } +} + +impl TryFrom<&[u8]> for IoOcr { + type Error = Error; + + fn try_from(val: &[u8]) -> Result { + Self::try_from_bytes(val) + } +} + +impl From for [u8; IoOcr::LEN] { + fn from(val: IoOcr) -> Self { + val.into_bytes() + } +} From 3a78dba5ab49e92fcd00f45f388236dfd05df8e4 Mon Sep 17 00:00:00 2001 From: rmsyn Date: Thu, 11 Sep 2025 02:28:02 +0000 Subject: [PATCH 48/50] sdio: add response types Adds the SD and SPI mode `R5` response types, and the corresponding field types. --- esp-hal/src/sdio.rs | 5 +- esp-hal/src/sdio/command.rs | 2 + esp-hal/src/sdio/command/index.rs | 57 +++++++++ esp-hal/src/sdio/response.rs | 12 ++ esp-hal/src/sdio/response/flags.rs | 44 +++++++ esp-hal/src/sdio/response/io_current_state.rs | 62 ++++++++++ esp-hal/src/sdio/response/r1.rs | 83 +++++++++++++ esp-hal/src/sdio/response/sd.rs | 7 ++ esp-hal/src/sdio/response/sd/r4.rs | 67 ++++++++++ esp-hal/src/sdio/response/sd/r5.rs | 115 ++++++++++++++++++ esp-hal/src/sdio/response/spi.rs | 7 ++ esp-hal/src/sdio/response/spi/r4.rs | 88 ++++++++++++++ esp-hal/src/sdio/response/spi/r4/r1.rs | 46 +++++++ esp-hal/src/sdio/response/spi/r5.rs | 87 +++++++++++++ 14 files changed, 681 insertions(+), 1 deletion(-) create mode 100644 esp-hal/src/sdio/command/index.rs create mode 100644 esp-hal/src/sdio/response.rs create mode 100644 esp-hal/src/sdio/response/flags.rs create mode 100644 esp-hal/src/sdio/response/io_current_state.rs create mode 100644 esp-hal/src/sdio/response/r1.rs create mode 100644 esp-hal/src/sdio/response/sd.rs create mode 100644 esp-hal/src/sdio/response/sd/r4.rs create mode 100644 esp-hal/src/sdio/response/sd/r5.rs create mode 100644 esp-hal/src/sdio/response/spi.rs create mode 100644 esp-hal/src/sdio/response/spi/r4.rs create mode 100644 esp-hal/src/sdio/response/spi/r4/r1.rs create mode 100644 esp-hal/src/sdio/response/spi/r5.rs diff --git a/esp-hal/src/sdio.rs b/esp-hal/src/sdio.rs index ad37d3e9d6f..810ff0d1322 100644 --- a/esp-hal/src/sdio.rs +++ b/esp-hal/src/sdio.rs @@ -5,6 +5,8 @@ //! 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, @@ -18,7 +20,7 @@ use embedded_hal_sdmmc::{ tuning::{TuningMode, TuningWidth}, }; -use crate::pac; +use crate::{gpio::Flex, pac}; pub mod command; mod config; @@ -28,6 +30,7 @@ mod hinf; mod interrupt; mod io_ocr; mod pins; +pub mod response; mod slc; mod slchost; mod state; diff --git a/esp-hal/src/sdio/command.rs b/esp-hal/src/sdio/command.rs index 375929f0cf0..817dda0bc9b 100644 --- a/esp-hal/src/sdio/command.rs +++ b/esp-hal/src/sdio/command.rs @@ -8,6 +8,7 @@ pub mod cmd53; mod crc; mod flag; mod fn_number; +mod index; pub use block_mode::BlockMode; pub use cmd0::Cmd0; @@ -17,3 +18,4 @@ pub use cmd53::Cmd53; pub use crc::Crc; pub use flag::{RawFlag, RwFlag}; pub use fn_number::FunctionNumber; +pub use index::CommandIndex; 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/response.rs b/esp-hal/src/sdio/response.rs new file mode 100644 index 00000000000..669ebac624e --- /dev/null +++ b/esp-hal/src/sdio/response.rs @@ -0,0 +1,12 @@ +//! SDIO response types. + +mod flags; +mod io_current_state; + +mod r1; +pub mod sd; +pub mod spi; + +pub use flags::Flags; +pub use io_current_state::IoCurrentState; +pub use r1::R1; 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/r1.rs b/esp-hal/src/sdio/response/r1.rs new file mode 100644 index 00000000000..e8664c7f610 --- /dev/null +++ b/esp-hal/src/sdio/response/r1.rs @@ -0,0 +1,83 @@ +use bitfielder::bitfield; + +use crate::sdio::Error; + +bitfield! { + /// Represents the R1 response type. + pub R1(u8): u8, + mask: 0x7f, + default: 0x00, + { + /// Represents whether a command's argument was invalid. + pub parameter_error: 6; + /// Represents whether a misaligned address was passed as a command argument. + pub address_error: 5; + /// Represents whether there was an error in the erase sequence commands. + pub erase_sequence_error: 4; + /// Represents whether the COM CRC check of the last command failed. + pub crc_error: 3; + /// Represents whether an illegal command was received. + pub illegal_command: 2; + /// Represents whether an erase sequence was cleared before executing. + pub erase_reset: 1; + /// Represents whether the card is in the idle state. + pub idle: 0; + } +} + +impl R1 { + /// Represents the byte length of the R1 response. + pub const LEN: usize = 1; + /// Represents the bitmask for the R1 response. + pub const MASK: u8 = 0x7f; + + /// Gets whether the response contains an error condition. + pub const fn is_err(&self) -> bool { + self.parameter_error() + || self.address_error() + || self.erase_sequence_error() + || self.crc_error() + || self.illegal_command() + } + + /// Attempts to convert a byte slice into a [R1]. + pub const fn try_from_bytes(val: &[u8]) -> Result { + match val.len() { + len if len < Self::LEN => Err(Error::General), + _ => Ok(Self(val[0] & Self::MASK)), + } + } + + /// Converts the [R1] into a byte array. + pub const fn into_bytes(self) -> [u8; Self::LEN] { + [self.0] + } +} + +impl TryFrom<&[u8]> for R1 { + type Error = Error; + + fn try_from(val: &[u8]) -> Result { + Self::try_from_bytes(val) + } +} + +impl From for [u8; R1::LEN] { + fn from(val: R1) -> Self { + val.into_bytes() + } +} + +impl core::fmt::Display for R1 { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "R1 {{")?; + write!(f, " parameter_error: {},", self.parameter_error())?; + write!(f, " address_error: {},", self.address_error())?; + write!(f, " erase_sequence_error: {},", self.erase_sequence_error())?; + write!(f, " crc_error: {},", self.crc_error())?; + write!(f, " illegal_command: {},", self.illegal_command())?; + write!(f, " erase_reset: {},", self.erase_reset())?; + write!(f, " idle: {}", self.idle())?; + write!(f, " }}") + } +} diff --git a/esp-hal/src/sdio/response/sd.rs b/esp-hal/src/sdio/response/sd.rs new file mode 100644 index 00000000000..502d94ce5d2 --- /dev/null +++ b/esp-hal/src/sdio/response/sd.rs @@ -0,0 +1,7 @@ +//! Response types for SD modes. + +mod r4; +mod r5; + +pub use r4::R4; +pub use r5::R5; diff --git a/esp-hal/src/sdio/response/sd/r4.rs b/esp-hal/src/sdio/response/sd/r4.rs new file mode 100644 index 00000000000..36171ec2bee --- /dev/null +++ b/esp-hal/src/sdio/response/sd/r4.rs @@ -0,0 +1,67 @@ +use bitfielder::bitfield; + +use crate::sdio::{Direction, Error, IoOcr}; + +bitfield! { + /// Represents the SD modes R4 (`IO_SEND_OP_COND`) response. + pub R4(MSB0 [u8; 6]): u32 { + raw_s: 47; + raw_direction: 46; + pub ready: 39; + pub number_of_functions: 38, 36; + pub memory_present: 35; + raw_io_ocr: 31, 8; + raw_e: 0; + } +} + +impl R4 { + /// Represents the byte length of the [R4] response. + pub const LEN: usize = 6; + + /// Gets the direction. + pub const fn direction(&self) -> Direction { + Direction::from_bool(self.raw_direction()) + } + + /// Gets the I/O OCR register. + pub const fn io_ocr(&self) -> IoOcr { + IoOcr::from_u32(self.raw_io_ocr()) + } + + /// Sets the I/O OCR register. + pub fn set_io_ocr(&mut self, val: IoOcr) { + self.set_raw_io_ocr(val.into_u32()); + } + + /// Attempts to convert a byte slice into a [R4]. + 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.raw_s() => Err(Error::General), + cmd if !cmd.raw_e() => Err(Error::General), + cmd => Ok(cmd), + }, + } + } + + /// Converts the [R4] into a byte array. + pub const fn into_bytes(self) -> [u8; Self::LEN] { + self.0 + } +} + +impl TryFrom<&[u8]> for R4 { + type Error = Error; + + fn try_from(val: &[u8]) -> Result { + Self::try_from_bytes(val) + } +} + +impl From for [u8; R4::LEN] { + fn from(val: R4) -> Self { + val.into_bytes() + } +} diff --git a/esp-hal/src/sdio/response/sd/r5.rs b/esp-hal/src/sdio/response/sd/r5.rs new file mode 100644 index 00000000000..efd43c22994 --- /dev/null +++ b/esp-hal/src/sdio/response/sd/r5.rs @@ -0,0 +1,115 @@ +use bitfielder::bitfield; + +use crate::sdio::{ + Direction, + Error, + command::{CommandIndex, Crc}, + response::Flags, +}; + +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 + } +} + +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/response/spi.rs b/esp-hal/src/sdio/response/spi.rs new file mode 100644 index 00000000000..5cb70284ab3 --- /dev/null +++ b/esp-hal/src/sdio/response/spi.rs @@ -0,0 +1,7 @@ +//! Response types for SPI mode. + +mod r4; +mod r5; + +pub use r4::{ModifiedR1, R4}; +pub use r5::R5; diff --git a/esp-hal/src/sdio/response/spi/r4.rs b/esp-hal/src/sdio/response/spi/r4.rs new file mode 100644 index 00000000000..9160481366c --- /dev/null +++ b/esp-hal/src/sdio/response/spi/r4.rs @@ -0,0 +1,88 @@ +use bitfielder::bitfield; + +use crate::sdio::{Error, IoOcr}; + +mod r1; + +pub use r1::ModifiedR1; + +bitfield! { + /// Represents the SPI modes R4 (`IO_SEND_OP_COND`) response. + pub R4(MSB0 [u8; 5]): u32 { + raw_modified_r1: 39, 32; + pub ready: 31; + pub number_of_functions: 30, 28; + pub memory_present: 27; + raw_io_ocr: 23, 0; + } +} + +impl R4 { + /// Represents the byte length of the [R4] response. + pub const LEN: usize = 5; + /// Represents the default byte value of the [R4] response. + pub const DEFAULT: [u8; Self::LEN] = [0u8; Self::LEN]; + + /// Creates a new [R4]. + pub const fn new() -> Self { + Self(Self::DEFAULT) + } + + /// Gets whether an error condition is set. + pub const fn is_err(&self) -> bool { + self.modified_r1().is_err() + } + + /// Gets the Modified R1 response. + pub const fn modified_r1(&self) -> ModifiedR1 { + ModifiedR1::from_inner(self.raw_modified_r1() as u8) + } + + /// Sets the Modified R1 response. + pub fn set_modified_r1(&mut self, val: ModifiedR1) { + self.set_raw_modified_r1(val.into_inner() as u32); + } + + /// Gets the I/O OCR register. + pub const fn io_ocr(&self) -> IoOcr { + IoOcr::from_u32(self.raw_io_ocr()) + } + + /// Sets the I/O OCR register. + pub fn set_io_ocr(&mut self, val: IoOcr) { + self.set_raw_io_ocr(val.into_u32()); + } + + /// Attempts to convert a byte slice into a [R4]. + pub const fn try_from_bytes(val: &[u8]) -> Result { + match val.len() { + len if len < Self::LEN => Err(Error::General), + _ => Ok(Self([val[0], val[1], val[2], val[3], val[4]])), + } + } + + /// Converts the [R4] into a byte array. + pub const fn into_bytes(self) -> [u8; Self::LEN] { + self.0 + } +} + +impl Default for R4 { + fn default() -> Self { + Self::new() + } +} + +impl TryFrom<&[u8]> for R4 { + type Error = Error; + + fn try_from(val: &[u8]) -> Result { + Self::try_from_bytes(val) + } +} + +impl From for [u8; R4::LEN] { + fn from(val: R4) -> Self { + val.into_bytes() + } +} diff --git a/esp-hal/src/sdio/response/spi/r4/r1.rs b/esp-hal/src/sdio/response/spi/r4/r1.rs new file mode 100644 index 00000000000..c36e806669b --- /dev/null +++ b/esp-hal/src/sdio/response/spi/r4/r1.rs @@ -0,0 +1,46 @@ +use bitfielder::bitfield; + +bitfield! { + /// Represents the Modified R1 response type used in SPI mode R4 response. + pub ModifiedR1(u8): u8, + mask: 0x5d, + default: 0x00, + { + /// Represents whether a command's argument was invalid. + pub parameter_error: 6; + /// Represents whether there was an error in command function number. + pub function_number_error: 4; + /// Represents whether the COM CRC check of the last command failed. + pub crc_error: 3; + /// Represents whether an illegal command was received. + pub illegal_command: 2; + /// Represents whether the card is in the idle state. + pub idle: 0; + } +} + +impl ModifiedR1 { + /// Gets whether an error condition is set. + pub const fn is_err(&self) -> bool { + self.parameter_error() + || self.function_number_error() + || self.crc_error() + || self.illegal_command() + } +} + +impl core::fmt::Display for ModifiedR1 { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "R1 {{")?; + write!(f, " parameter_error: {},", self.parameter_error())?; + write!( + f, + " function_number_error: {},", + self.function_number_error() + )?; + write!(f, " crc_error: {},", self.crc_error())?; + write!(f, " illegal_command: {},", self.illegal_command())?; + write!(f, " idle: {}", self.idle())?; + write!(f, " }}") + } +} diff --git a/esp-hal/src/sdio/response/spi/r5.rs b/esp-hal/src/sdio/response/spi/r5.rs new file mode 100644 index 00000000000..2553ee43122 --- /dev/null +++ b/esp-hal/src/sdio/response/spi/r5.rs @@ -0,0 +1,87 @@ +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 parameter_error: 14; + /// Indicates a function number error. + pub function_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; + + /// Gets whether the [R5] response has any errors set. + #[inline] + pub const fn is_err(&self) -> bool { + self.parameter_error() + || self.function_number_error() + || self.crc_error() + || self.illegal_command() + } + + /// 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() + } +} + +impl core::fmt::Display for R5 { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "R5 {{")?; + write!(f, " parameter_error: {},", self.parameter_error())?; + write!( + f, + " function_number_error: {},", + self.function_number_error() + )?; + write!(f, " crc_error: {},", self.crc_error())?; + write!(f, " illegal_command: {},", self.illegal_command())?; + write!(f, " idle: {},", self.idle())?; + write!(f, " data: {}", self.data())?; + write!(f, " }}") + } +} From 70733b38cd613a704cf16c83677f6cb96175933e Mon Sep 17 00:00:00 2001 From: rmsyn Date: Thu, 4 Sep 2025 22:04:15 +0000 Subject: [PATCH 49/50] WIP: sdio: add raw command/response functions Adds `Sdio::read_command_raw` + `Sdio::write_response_raw` functions to read and write raw data on the `CMD` line. TODO: add SDIO mode implementations NOTE: these functions may be unecessary, the controller seems to handle command/response in hardware (at least in SPI mode). --- esp-hal/src/sdio.rs | 170 ++++++++++++++++++++++++++++++++++++ esp-hal/src/sdio/command.rs | 14 +++ 2 files changed, 184 insertions(+) diff --git a/esp-hal/src/sdio.rs b/esp-hal/src/sdio.rs index 810ff0d1322..79617bb0646 100644 --- a/esp-hal/src/sdio.rs +++ b/esp-hal/src/sdio.rs @@ -47,6 +47,15 @@ pub use slchost::{AnySlchost, SlchostInfo, SlchostInstance}; pub use state::State; pub use timing::Timing; +/// Represents the direction of a SPI transmission. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum SpiDirection { + /// Indicates a read transmission (host-to-device). + Read, + /// Indicates a write transmission (device-to-host). + Write, +} + /// Represents the transmission modes for the SDIO peripheral. #[repr(u8)] #[derive(Clone, Copy, Debug, Eq, PartialEq)] @@ -374,6 +383,167 @@ impl<'d> Sdio<'d> { Ok(()) } + + /// Waits for the CLK/SCLK line to be idle. + pub(crate) fn wait_for_idle(&self) -> Result<(), Error> { + match self.pins.mode() { + Mode::Spi => { + // Mode 0 + 1 idles low, mode 2 + 3 idles high + let idle = matches!(self.config.spi_mode(), SpiMode::_2 | SpiMode::_3); + + while self.pins.sclk().map(|p| p.is_set_high() == idle)? { + core::hint::spin_loop(); + } + + Ok(()) + } + _ => { + while self.pins.clk().map(|p| !p.is_set_high())? { + core::hint::spin_loop(); + } + + Ok(()) + } + } + } + + /// Waits for a clock edge transition to indicate when to read/write data. + // TODO: configure SPI modes + pub(crate) fn wait_for_clock_edge(&self, direction: SpiDirection) -> Result<(), Error> { + match self.pins.mode() { + Mode::Spi => { + let mode = self.config.spi_mode(); + + // Mode 0 + 1 idles low, mode 2 + 3 idles high + let idle = matches!(mode, SpiMode::_2 | SpiMode::_3); + + // Checks if we are waiting for a first transition from the IDLE state + let first_transition = matches!( + (mode, direction), + (SpiMode::_0, SpiDirection::Read) + | (SpiMode::_2, SpiDirection::Read) + | (SpiMode::_1, SpiDirection::Write) + | (SpiMode::_3, SpiDirection::Write) + ); + + // Check if we need to wait for a second edge transition + let second_transition = matches!( + (mode, direction), + (SpiMode::_0, SpiDirection::Write) + | (SpiMode::_2, SpiDirection::Write) + | (SpiMode::_1, SpiDirection::Read) + | (SpiMode::_3, SpiDirection::Read) + ); + + let edge = second_transition ^ idle; + + // wait for first SCLK edge change from IDLE + if first_transition { + while self.pins.sclk().map(|p| p.is_set_high() == idle)? { + core::hint::spin_loop(); + } + } + + // wait for second edge transition for the appropriate mode + direction combinations + if second_transition { + while self.pins.sclk().map(|p| p.is_set_high() == edge)? { + core::hint::spin_loop(); + } + } + + Ok(()) + } + _ => Err(Error::Unimplemented), + } + } + + /// Waits for the CS pin to be asserted in SPI mode. + /// + /// Returns an error if not in SPI mode. + // TODO: add a timeout parameter + pub fn wait_for_cs(&self) -> Result<(), Error> { + match self.pins.mode() { + Mode::Spi => { + // block until CS pin is asserted (driven low) + while self.pins.cs().map(|p| p.is_set_high())? { + core::hint::spin_loop(); + } + + Ok(()) + } + _ => Err(Error::General), + } + } + + /// Asserts the CS pin to indicate starting of data transmission. + pub(crate) fn assert_cs(&self) -> Result<(), Error> { + // assert the CS pin + self.pins + .cs() + .map(Flex::new) + .map(|mut p| p.set_level(false.into())) + } + + /// Deasserts the CS pin to indicate ending of data transmission. + pub(crate) fn deassert_cs(&self) -> Result<(), Error> { + // deassert the CS pin + self.pins + .cs() + .map(Flex::new) + .map(|mut p| p.set_level(true.into())) + } + + /// Reads the raw command bytes from the wire. + pub fn read_command_raw(&mut self) -> Result<[u8; command::AnyCmd::LEN], Error> { + self.wait_for_idle()?; + + match self.pins.mode() { + Mode::Spi => { + self.wait_for_cs()?; + + let mut buf = [0u8; command::AnyCmd::LEN]; + + for b in buf.iter_mut() { + for i in 0..8 { + self.wait_for_clock_edge(SpiDirection::Read)?; + + 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 => { + self.assert_cs()?; + + let mut miso = self.pins.miso().map(Flex::new)?; + + for b in res.iter() { + for i in 0..8 { + self.wait_for_clock_edge(SpiDirection::Write)?; + + let shift = 7 - i; + let level = ((b >> shift) & 0x1) != 0; + + miso.set_level(level.into()); + } + } + + self.deassert_cs()?; + + Ok(()) + } + _ => Err(Error::Unimplemented), + } + } } /// Represents the error variants for SDIO peripherals. diff --git a/esp-hal/src/sdio/command.rs b/esp-hal/src/sdio/command.rs index 817dda0bc9b..e5843a7ee55 100644 --- a/esp-hal/src/sdio/command.rs +++ b/esp-hal/src/sdio/command.rs @@ -19,3 +19,17 @@ 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; +} From e5c9931d54602e19f04b5283739e7b6c66cc6f18 Mon Sep 17 00:00:00 2001 From: rmsyn Date: Thu, 11 Sep 2025 02:27:19 +0000 Subject: [PATCH 50/50] sdio: qa-test integration tests Adds a split `qa-test` SPI-mode SDIO integration test. The tests are split with the intention to flash each half of the test to separate devices, allowing for concurrent execution. --- esp-hal/src/sdio.rs | 4 +- esp-hal/src/sdio/config.rs | 18 +++ qa-test/src/bin/sdio-spi-device.rs | 139 +++++++++++++++++++++++ qa-test/src/bin/sdio-spi-host.rs | 173 +++++++++++++++++++++++++++++ 4 files changed, 332 insertions(+), 2 deletions(-) create mode 100644 qa-test/src/bin/sdio-spi-device.rs create mode 100644 qa-test/src/bin/sdio-spi-host.rs diff --git a/esp-hal/src/sdio.rs b/esp-hal/src/sdio.rs index 79617bb0646..6fe4fadb9fd 100644 --- a/esp-hal/src/sdio.rs +++ b/esp-hal/src/sdio.rs @@ -36,7 +36,7 @@ mod slchost; mod state; mod timing; -pub use config::Config; +pub use config::{Config, SpiMode}; pub use direction::Direction; pub use hinf::{AnyHinf, HinfInfo, HinfInstance}; pub use interrupt::{DeviceInterrupt, HostInterrupt}; @@ -408,7 +408,7 @@ impl<'d> Sdio<'d> { } /// Waits for a clock edge transition to indicate when to read/write data. - // TODO: configure SPI modes + // TODO: add timeout parameter pub(crate) fn wait_for_clock_edge(&self, direction: SpiDirection) -> Result<(), Error> { match self.pins.mode() { Mode::Spi => { diff --git a/esp-hal/src/sdio/config.rs b/esp-hal/src/sdio/config.rs index 7d89a1ba16c..3fceb6f5054 100644 --- a/esp-hal/src/sdio/config.rs +++ b/esp-hal/src/sdio/config.rs @@ -1,4 +1,5 @@ use super::Timing; +pub use crate::spi::Mode as SpiMode; /// Represents SDIO configuration parameters. #[repr(C)] @@ -6,6 +7,7 @@ use super::Timing; pub struct Config { hs: bool, timing: Timing, + spi_mode: SpiMode, } impl Config { @@ -14,6 +16,7 @@ impl Config { Self { hs: false, timing: Timing::new(), + spi_mode: SpiMode::_0, } } @@ -46,6 +49,21 @@ impl Config { pub fn with_timing(self, timing: Timing) -> Self { Self { timing, ..self } } + + /// Gets the SPI mode setting. + pub const fn spi_mode(&self) -> SpiMode { + self.spi_mode + } + + /// Sets the SPI mode seting. + pub fn set_spi_mode(&mut self, spi_mode: SpiMode) { + self.spi_mode = spi_mode; + } + + /// Builder funciton that sets the SPI mode setting. + pub fn with_spi_mode(self, spi_mode: SpiMode) -> Self { + Self { spi_mode, ..self } + } } impl Default for Config { 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..8a87d149d73 --- /dev/null +++ b/qa-test/src/bin/sdio-spi-device.rs @@ -0,0 +1,139 @@ +#![no_std] +#![no_main] + +use esp_backtrace as _; +use esp_hal::{ + dma::{DescriptorFlagFields, Owner}, + sdio::{ + Config, + Mode, + Pins, + Sdio, + SpiMode, + dma::{ + AtomicBuffer, + AtomicDmaDescriptor, + AtomicDmaDescriptors, + DmaDescriptor, + DmaDescriptorFlags, + }, + }, +}; + +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; +// Represents the default byte length of a SDIO block. +const BLOCK_LEN: usize = 512; +// 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>, +} + +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().with_spi_mode(SpiMode::_2); + + let sdio = Sdio::new( + peripherals.SLC, + peripherals.SLCHOST, + peripherals.HINF, + pins, + config, + ); + + Self { sdio } + } +} + +#[esp_hal::main] +fn main() -> ! { + let ctx = Context::new(); + + // TODO: perform data transfer + // 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(BLOCK_LEN); + flags.set_len(BLOCK_LEN); + + let tx_descriptor = &TX_DESCRIPTORS[0]; + + let packet = b"test RX packet"; + RX_BUFFER.write(packet.as_slice()); + + let buffer = unsafe { RX_BUFFER.as_ptr_mut() }; + tx_descriptor.update(DmaDescriptor { + flags, + buffer, + next: core::ptr::null_mut(), + }); + + let slc = unsafe { &*ctx.sdio.slc().register_block }; + + // configure SLC RX descriptors + slc.slc1rx_link_addr() + .write(|w| unsafe { w.bits(RX_DESCRIPTORS.address()) }); + slc.slc1rx_link() + .modify(|_, w| w.sdio_slc1_rxlink_start().set_bit()); + + loop { + core::hint::spin_loop() + } +} 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..78e1bc2bd7e --- /dev/null +++ b/qa-test/src/bin/sdio-spi-host.rs @@ -0,0 +1,173 @@ +//! SDIO SPI mode master driver example. + +#![no_std] +#![no_main] + +use esp_backtrace as _; +use esp_hal::{ + Blocking, + sdio::{ + command::{Cmd0, Cmd5, Cmd52, Cmd53, FunctionNumber, RwFlag}, + response::{ + R1, + spi::{R4, R5}, + }, + }, + spi::{Mode, master as spi}, + time::Rate, +}; +use esp_println::println; + +esp_bootloader_esp_idf::esp_app_desc!(); + +// Represents the byte length of a SDIO block. +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()); + + let config = spi::Config::default() + .with_mode(Mode::_2) + .with_frequency(Rate::from_mhz(40)); + + cfg_if::cfg_if! { + if #[cfg(feature = "esp32")] { + // Create SPI master for mock SDIO host + // HSPI config + let spi = spi::Spi::new( + peripherals.SPI2, + config, + ) + .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(40)), + //) + //.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, + config, + ) + .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(); + + ctx.spi + .write(&[0u8; 10]) + .expect("error sending SPI init cycles"); + + let mut poll = true; + + // continually send CMD0 until an IDLE R1 response + while poll { + let mut packet = Cmd0::new().into_bytes(); + ctx.spi + .transfer(&mut packet) + .expect("error writing SDIO from host"); + + // expect R1 response from SDIO device + match R1::try_from(packet.as_ref()) { + Ok(r1) if r1.idle() && !r1.is_err() => { + println!("response: {r1}"); + poll = false; + } + _ => (), + } + } + + println!("received R1 IDLE response ..."); + poll = true; + + while poll { + let mut packet = Cmd5::new().into_bytes(); + + // expect R4 response from SDIO device + ctx.spi + .transfer(&mut packet) + .expect("error transfer CMD5 from SPI host"); + + match R4::try_from(packet.as_ref()) { + Ok(r4) if r4.ready() && !r4.is_err() => { + poll = false; + println!("response: {r4:x?}"); + } + Ok(r4) if r4.is_err() => { + println!("error response: {}", r4.modified_r1()); + } + _ => (), + } + } + + println!("received R4 READY response ..."); + poll = true; + + let mut packet = [0u8; Cmd53::LEN + BLOCK_LEN]; + + while poll { + let mut cmd = Cmd53::new(); + + cmd.set_function_number(FunctionNumber::Io2); + cmd.set_read_write_flag(RwFlag::ReadOnly); + // Fixed-address transfer + cmd.set_register_address(0x0); + cmd.set_crc(); + + packet[..Cmd53::LEN].copy_from_slice(cmd.into_bytes().as_slice()); + + ctx.spi + .transfer(&mut packet) + .expect("error transfer CMD53 from SPI host"); + + match R5::try_from(packet.as_ref()) { + Ok(r5) if r5.idle() && !r5.is_err() => { + poll = false; + + println!("response: {r5}"); + + let data = &packet[R5::LEN..]; + println!("data: {data:x?}"); + } + Ok(r5) if r5.is_err() => { + println!("error response: {r5}"); + } + _ => (), + } + } + + loop { + core::hint::spin_loop() + } +}