From b28b61dc4aeb772502c61e4b0d9091569fac4a40 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 11 Dec 2025 15:56:27 -0800 Subject: [iMXRT] Add spi driver --- embassy-imxrt/src/flexcomm/mod.rs | 1 + embassy-imxrt/src/flexcomm/spi.rs | 1011 +++++++++++++++++++++++++++++++++++++ 2 files changed, 1012 insertions(+) create mode 100644 embassy-imxrt/src/flexcomm/spi.rs (limited to 'embassy-imxrt') diff --git a/embassy-imxrt/src/flexcomm/mod.rs b/embassy-imxrt/src/flexcomm/mod.rs index 27794042b..ed87c7fb4 100644 --- a/embassy-imxrt/src/flexcomm/mod.rs +++ b/embassy-imxrt/src/flexcomm/mod.rs @@ -1,5 +1,6 @@ //! Implements Flexcomm interface wrapper for easier usage across modules +pub mod spi; pub mod uart; use paste::paste; diff --git a/embassy-imxrt/src/flexcomm/spi.rs b/embassy-imxrt/src/flexcomm/spi.rs new file mode 100644 index 000000000..9dd776ac7 --- /dev/null +++ b/embassy-imxrt/src/flexcomm/spi.rs @@ -0,0 +1,1011 @@ +//! Serial Peripheral Interface (SPI) driver. + +use core::future::{Future, poll_fn}; +use core::marker::PhantomData; +use core::task::Poll; + +use embassy_embedded_hal::SetConfig; +use embassy_hal_internal::{Peri, PeripheralType}; +use embassy_sync::waitqueue::AtomicWaker; +pub use embedded_hal_1::spi::{MODE_0, MODE_1, MODE_2, MODE_3, Mode, Phase, Polarity}; +use paste::paste; + +use crate::flexcomm::Clock; +use crate::gpio::{AnyPin, GpioPin as Pin}; +use crate::interrupt; +use crate::interrupt::typelevel::Interrupt; +use crate::iopctl::{DriveMode, DriveStrength, Inverter, IopctlPin, Pull, SlewRate}; +use crate::pac::spi0::cfg::{Cpha, Cpol}; + +/// Driver move trait. +#[allow(private_bounds)] +pub trait IoMode: sealed::Sealed {} + +/// Blocking mode. +pub struct Blocking; +impl sealed::Sealed for Blocking {} +impl IoMode for Blocking {} + +/// Async mode. +pub struct Async; +impl sealed::Sealed for Async {} +impl IoMode for Async {} + +/// Spi errors. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error { + // No errors for now. +} + +/// Spi driver. +pub struct Spi<'a, M: IoMode> { + info: Info, + _phantom: PhantomData<&'a M>, +} + +impl<'a> Spi<'a, Blocking> { + /// Create a SPI driver in blocking mode. + pub fn new_blocking( + _inner: Peri<'a, T>, + sck: Peri<'a, impl SckPin + 'a>, + mosi: Peri<'a, impl MosiPin + 'a>, + miso: Peri<'a, impl MisoPin + 'a>, + config: Config, + ) -> Self { + sck.as_sck(); + mosi.as_mosi(); + miso.as_miso(); + + Self::new_inner(_inner, Some(sck.into()), Some(mosi.into()), Some(miso.into()), config) + } + + /// Create a TX-only SPI driver in blocking mode. + pub fn new_blocking_txonly( + _inner: Peri<'a, T>, + sck: Peri<'a, impl SckPin + 'a>, + mosi: Peri<'a, impl MosiPin + 'a>, + config: Config, + ) -> Self { + sck.as_sck(); + mosi.as_mosi(); + + Self::new_inner(_inner, Some(sck.into()), Some(mosi.into()), None, config) + } + + /// Create an RX-only SPI driver in blocking mode. + pub fn new_blocking_rxonly( + _inner: Peri<'a, T>, + sck: Peri<'a, impl SckPin + 'a>, + miso: Peri<'a, impl MisoPin + 'a>, + config: Config, + ) -> Self { + sck.as_sck(); + miso.as_miso(); + + Self::new_inner(_inner, Some(sck.into()), None, Some(miso.into()), config) + } + + /// Create an internal-loopback SPI driver in blocking mode. + /// + /// WARNING: This is only useful for testing as it doesn't use any + /// external pins. + pub fn new_blocking_loopback(_inner: Peri<'a, T>, config: Config) -> Self { + Self::new_inner(_inner, None, None, None, config) + } +} + +impl<'a, M: IoMode> Spi<'a, M> { + /// Read data from Spi blocking execution until done. + pub fn blocking_read(&mut self, data: &mut [u8]) -> Result<(), Error> { + critical_section::with(|_| { + self.info + .regs + .fifostat() + .modify(|_, w| w.txerr().set_bit().rxerr().set_bit()); + + for word in data.iter_mut() { + // wait until we have data in the RxFIFO. + while self.info.regs.fifostat().read().rxnotempty().bit_is_clear() {} + + self.info + .regs + .fifowr() + .write(|w| unsafe { w.txdata().bits(*word as u16).len().bits(7) }); + + *word = self.info.regs.fiford().read().rxdata().bits() as u8; + } + }); + + self.flush() + } + + /// Write data to Spi blocking execution until done. + pub fn blocking_write(&mut self, data: &[u8]) -> Result<(), Error> { + critical_section::with(|_| { + self.info + .regs + .fifostat() + .modify(|_, w| w.txerr().set_bit().rxerr().set_bit()); + + for (i, word) in data.iter().enumerate() { + // wait until we have space in the TxFIFO. + while self.info.regs.fifostat().read().txnotfull().bit_is_clear() {} + + self.info.regs.fifowr().write(|w| { + unsafe { w.txdata().bits(*word as u16).len().bits(7) } + .rxignore() + .set_bit(); + + if i == data.len() - 1 { + w.eot().set_bit(); + } + + w + }); + } + }); + + self.flush() + } + + /// Transfer data to SPI blocking execution until done. + pub fn blocking_transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> { + let len = read.len().max(write.len()); + + critical_section::with(|_| { + self.info + .regs + .fifostat() + .modify(|_, w| w.txerr().set_bit().rxerr().set_bit()); + + for i in 0..len { + let wb = write.get(i).copied().unwrap_or(0); + + // wait until we have space in the TxFIFO. + while self.info.regs.fifostat().read().txnotfull().bit_is_clear() {} + + self.info.regs.fifowr().write(|w| { + unsafe { w.txdata().bits(wb as u16).len().bits(7) }; + + if i == len - 1 { + w.eot().set_bit(); + } + + w + }); + + // wait until we have data in the RxFIFO. + while self.info.regs.fifostat().read().rxnotempty().bit_is_clear() {} + + let rb = self.info.regs.fiford().read().rxdata().bits() as u8; + + if let Some(r) = read.get_mut(i) { + *r = rb; + } + } + }); + + self.flush() + } + + /// Transfer data in place to SPI blocking execution until done. + pub fn blocking_transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Error> { + critical_section::with(|_| { + self.info + .regs + .fifostat() + .modify(|_, w| w.txerr().set_bit().rxerr().set_bit()); + + for word in data { + // wait until we have space in the TxFIFO. + while self.info.regs.fifostat().read().txnotfull().bit_is_clear() {} + self.info + .regs + .fifowr() + .write(|w| unsafe { w.txdata().bits(*word as u16) }); + + // wait until we have data in the RxFIFO. + while self.info.regs.fifostat().read().rxnotempty().bit_is_clear() {} + *word = self.info.regs.fiford().read().rxdata().bits() as u8; + } + }); + + self.flush() + } + + /// Block execution until Spi is done. + pub fn flush(&mut self) -> Result<(), Error> { + let regs = self.info.regs; + while regs.stat().read().mstidle().bit_is_clear() {} + Ok(()) + } +} + +impl<'a> Spi<'a, Async> { + /// Create a SPI driver in async mode. + pub fn new_async( + _inner: Peri<'a, T>, + sck: Peri<'a, impl SckPin + 'a>, + mosi: Peri<'a, impl MosiPin + 'a>, + miso: Peri<'a, impl MisoPin + 'a>, + _irq: impl interrupt::typelevel::Binding> + 'a, + config: Config, + ) -> Self { + sck.as_sck(); + mosi.as_mosi(); + miso.as_miso(); + + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + + Self::new_inner(_inner, Some(sck.into()), Some(mosi.into()), Some(miso.into()), config) + } + + /// Create a TX-only SPI driver in async mode. + pub fn new_async_txonly( + _inner: Peri<'a, T>, + sck: Peri<'a, impl SckPin + 'a>, + mosi: Peri<'a, impl MosiPin + 'a>, + _irq: impl interrupt::typelevel::Binding> + 'a, + config: Config, + ) -> Self { + sck.as_sck(); + mosi.as_mosi(); + + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + + Self::new_inner(_inner, Some(sck.into()), Some(mosi.into()), None, config) + } + + /// Create an RX-only SPI driver in async mode. + pub fn new_async_rxonly( + _inner: Peri<'a, T>, + sck: Peri<'a, impl SckPin + 'a>, + miso: Peri<'a, impl MisoPin + 'a>, + _irq: impl interrupt::typelevel::Binding> + 'a, + config: Config, + ) -> Self { + sck.as_sck(); + miso.as_miso(); + + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + + Self::new_inner(_inner, Some(sck.into()), None, Some(miso.into()), config) + } + + /// Create an internal-loopback SPI driver in async mode. + /// + /// WARNING: This is only useful for testing as it doesn't use any + /// external pins. + pub fn new_async_loopback( + _inner: Peri<'a, T>, + _irq: impl interrupt::typelevel::Binding> + 'a, + config: Config, + ) -> Self { + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + + Self::new_inner(_inner, None, None, None, config) + } + + /// Read data from Spi async execution until done. + pub async fn async_read(&mut self, data: &mut [u8]) -> Result<(), Error> { + critical_section::with(|_| { + self.info + .regs + .fifostat() + .modify(|_, w| w.txerr().set_bit().rxerr().set_bit()); + }); + + for word in data.iter_mut() { + // wait until we have data in the RxFIFO. + self.wait_for( + |me| { + if me.info.regs.fifostat().read().rxnotempty().bit_is_set() { + Poll::Ready(()) + } else { + Poll::Pending + } + }, + |me| { + me.info + .regs + .fifointenset() + .write(|w| w.rxlvl().set_bit().rxerr().set_bit()); + }, + ) + .await; + + self.info + .regs + .fifowr() + .write(|w| unsafe { w.txdata().bits(*word as u16).len().bits(7) }); + + *word = self.info.regs.fiford().read().rxdata().bits() as u8; + } + + self.async_flush().await; + + Ok(()) + } + + /// Write data to Spi async execution until done. + pub async fn async_write(&mut self, data: &[u8]) -> Result<(), Error> { + critical_section::with(|_| { + self.info + .regs + .fifostat() + .modify(|_, w| w.txerr().set_bit().rxerr().set_bit()); + }); + + for (i, word) in data.iter().enumerate() { + // wait until we have space in the TxFIFO. + self.wait_for( + |me| { + if me.info.regs.fifostat().read().txnotfull().bit_is_set() { + Poll::Ready(()) + } else { + Poll::Pending + } + }, + |me| { + me.info + .regs + .fifointenset() + .write(|w| w.txlvl().set_bit().txerr().set_bit()); + }, + ) + .await; + + self.info.regs.fifowr().write(|w| { + unsafe { w.txdata().bits(*word as u16).len().bits(7) } + .rxignore() + .set_bit(); + + if i == data.len() - 1 { + w.eot().set_bit(); + } + + w + }); + } + + self.async_flush().await; + + Ok(()) + } + + /// Transfer data to SPI async execution until done. + pub async fn async_transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> { + let len = read.len().max(write.len()); + + critical_section::with(|_| { + self.info + .regs + .fifostat() + .modify(|_, w| w.txerr().set_bit().rxerr().set_bit()); + }); + + for i in 0..len { + let wb = write.get(i).copied().unwrap_or(0); + + // wait until we have space in the TxFIFO. + self.wait_for( + |me| { + if me.info.regs.fifostat().read().txnotfull().bit_is_set() { + Poll::Ready(()) + } else { + Poll::Pending + } + }, + |me| { + me.info.regs.fifotrig().write(|w| w.txlvlena().set_bit()); + me.info + .regs + .fifointenset() + .write(|w| w.txlvl().set_bit().txerr().set_bit()); + }, + ) + .await; + + self.info.regs.fifowr().write(|w| { + unsafe { w.txdata().bits(wb as u16).len().bits(7) }; + + if i == len - 1 { + w.eot().set_bit(); + } + + w + }); + + // wait until we have data in the RxFIFO. + self.wait_for( + |me| { + if me.info.regs.fifostat().read().rxnotempty().bit_is_set() { + Poll::Ready(()) + } else { + Poll::Pending + } + }, + |me| { + me.info.regs.fifotrig().write(|w| w.rxlvlena().set_bit()); + me.info + .regs + .fifointenset() + .write(|w| w.rxlvl().set_bit().rxerr().set_bit()); + }, + ) + .await; + + let rb = self.info.regs.fiford().read().rxdata().bits() as u8; + + if let Some(r) = read.get_mut(i) { + *r = rb; + } + } + + self.async_flush().await; + + Ok(()) + } + + /// Transfer data in place to SPI async execution until done. + pub async fn async_transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Error> { + critical_section::with(|_| { + self.info + .regs + .fifostat() + .modify(|_, w| w.txerr().set_bit().rxerr().set_bit()); + }); + + for word in data { + // wait until we have space in the TxFIFO. + self.wait_for( + |me| { + if me.info.regs.fifostat().read().txnotfull().bit_is_set() { + Poll::Ready(()) + } else { + Poll::Pending + } + }, + |me| { + me.info + .regs + .fifointenset() + .write(|w| w.txlvl().set_bit().txerr().set_bit()); + }, + ) + .await; + + self.info + .regs + .fifowr() + .write(|w| unsafe { w.txdata().bits(*word as u16) }); + + // wait until we have data in the RxFIFO. + self.wait_for( + |me| { + if me.info.regs.fifostat().read().rxnotempty().bit_is_set() { + Poll::Ready(()) + } else { + Poll::Pending + } + }, + |me| { + me.info + .regs + .fifointenset() + .write(|w| w.rxlvl().set_bit().rxerr().set_bit()); + }, + ) + .await; + + *word = self.info.regs.fiford().read().rxdata().bits() as u8; + } + + self.async_flush().await; + + Ok(()) + } + + /// Async flush. + pub fn async_flush(&mut self) -> impl Future + use<'_, 'a> { + self.wait_for( + |me| { + if me.info.regs.stat().read().mstidle().bit_is_set() { + Poll::Ready(()) + } else { + Poll::Pending + } + }, + |me| { + me.info.regs.intenset().write(|w| w.mstidleen().set_bit()); + }, + ) + } + + /// Calls `f` to check if we are ready or not. + /// If not, `g` is called once the waker is set (to eg enable the required interrupts). + fn wait_for(&mut self, mut f: F, mut g: G) -> impl Future + use<'_, 'a, F, U, G> + where + F: FnMut(&mut Self) -> Poll, + G: FnMut(&mut Self), + { + poll_fn(move |cx| { + // Register waker before checking condition, to ensure that wakes/interrupts + // aren't lost between f() and g() + self.info.waker.register(cx.waker()); + let r = f(self); + + if r.is_pending() { + g(self); + } + + r + }) + } +} + +impl<'a, M: IoMode> Spi<'a, M> { + fn new_inner( + _inner: Peri<'a, T>, + sck: Option>, + mosi: Option>, + miso: Option>, + config: Config, + ) -> Self { + // REVISIT: allow selecting from multiple clocks. + let clk = Self::clock(&config); + + T::enable(clk); + T::into_spi(); + + Self::apply_config(T::info().regs, &config); + + let info = T::info(); + let regs = info.regs; + + critical_section::with(|_| match (sck.is_some(), mosi.is_some(), miso.is_some()) { + (true, true, true) => { + regs.fifocfg().modify(|_, w| { + w.enabletx() + .set_bit() + .emptytx() + .set_bit() + .enablerx() + .set_bit() + .emptyrx() + .set_bit() + }); + } + (true, false, true) => { + regs.fifocfg().modify(|_, w| { + w.enabletx() + .set_bit() + .emptytx() + .clear_bit() + .enablerx() + .set_bit() + .emptyrx() + .set_bit() + }); + } + (true, true, false) => { + regs.fifocfg().modify(|_, w| { + w.enabletx() + .set_bit() + .emptytx() + .set_bit() + .enablerx() + .clear_bit() + .emptyrx() + .set_bit() + }); + } + (false, _, _) => { + regs.fifocfg().modify(|_, w| { + w.enabletx() + .set_bit() + .emptytx() + .set_bit() + .enablerx() + .set_bit() + .emptyrx() + .set_bit() + }); + regs.cfg().modify(|_, w| w.loop_().enabled()); + } + _ => {} + }); + + Self { + info, + _phantom: PhantomData, + } + } + + fn set_config(&mut self, config: &Config) { + Self::apply_config(self.info.regs, config); + } + + fn clock(config: &Config) -> Clock { + const SFRO_CLOCK_SPEED_HZ: u32 = 16_000_000; + + if config.frequency > SFRO_CLOCK_SPEED_HZ { + Clock::Ffro + } else { + Clock::Sfro + } + } + + fn clock_frequency(clock: Clock) -> u32 { + match clock { + Clock::Sfro => 16_000_000, + Clock::Ffro => 48_000_000, + _ => unreachable!(), + } + } + + fn apply_config(regs: &'static crate::pac::spi0::RegisterBlock, config: &Config) { + let polarity = if config.mode.polarity == Polarity::IdleLow { + Cpol::Low + } else { + Cpol::High + }; + + let phase = if config.mode.phase == Phase::CaptureOnFirstTransition { + Cpha::Change + } else { + Cpha::Capture + }; + + let clk = Self::clock(config); + let div = Self::clock_frequency(clk) / config.frequency - 1; + + critical_section::with(|_| { + // disable SPI every time we need to modify configuration. + regs.cfg().modify(|_, w| w.enable().disabled()); + + regs.cfg().modify(|_, w| { + w.cpha() + .variant(phase) + .cpol() + .variant(polarity) + .loop_() + .disabled() + .master() + .master_mode() + }); + + regs.div().write(|w| unsafe { w.divval().bits(div as u16) }); + + regs.cfg().modify(|_, w| w.enable().enabled()); + }); + } +} + +/// Spi config. +#[derive(Clone)] +pub struct Config { + /// Frequency in Hertz. + pub frequency: u32, + /// SPI operating mode. + pub mode: Mode, +} + +impl Default for Config { + fn default() -> Self { + Self { + frequency: 1_000_000, + mode: MODE_0, + } + } +} + +struct Info { + regs: &'static crate::pac::spi0::RegisterBlock, + waker: &'static AtomicWaker, +} + +// SAFETY: safety for Send here is the same as the other accessors to +// unsafe blocks: it must be done from a single executor context. +// +// This is a temporary workaround -- a better solution might be to +// refactor Info to no longer maintain a reference to regs, but +// instead look up the correct register set and then perform +// operations within an unsafe block as we do for other peripherals +unsafe impl Send for Info {} + +trait SealedInstance { + fn info() -> Info; +} + +/// Spi interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let waker = T::info().waker; + let stat = T::info().regs.fifointstat().read(); + + if stat.perint().bit_is_set() { + T::info().regs.intenclr().write(|w| w.mstidle().clear_bit_by_one()); + } + + if stat.txlvl().bit_is_set() { + T::info().regs.fifointenclr().write(|w| w.txlvl().set_bit()); + } + + if stat.txerr().bit_is_set() { + T::info().regs.fifointenclr().write(|w| w.txerr().set_bit()); + } + + if stat.rxlvl().bit_is_set() { + T::info().regs.fifointenclr().write(|w| w.rxlvl().set_bit()); + } + + if stat.rxerr().bit_is_set() { + T::info().regs.fifointenclr().write(|w| w.rxerr().set_bit()); + } + + waker.wake(); + } +} + +/// Spi instance trait. +#[allow(private_bounds)] +pub trait Instance: crate::flexcomm::IntoSpi + SealedInstance + PeripheralType + 'static + Send { + /// Interrupt for this Spi instance. + type Interrupt: interrupt::typelevel::Interrupt; +} + +macro_rules! impl_instance { + ($($n:expr),*) => { + $( + paste!{ + impl SealedInstance for crate::peripherals::[] { + #[inline] + fn info() -> Info { + static WAKER: AtomicWaker = AtomicWaker::new(); + + Info { + regs: unsafe { &*crate::pac::[]::ptr() }, + waker: &WAKER, + } + } + } + + impl Instance for crate::peripherals::[] { + type Interrupt = crate::interrupt::typelevel::[]; + } + } + )* + } +} + +impl_instance!(0, 1, 2, 3, 4, 5, 6, 7, 14); + +mod sealed { + /// Seal a trait + pub trait Sealed {} +} + +impl sealed::Sealed for T {} + +/// IO configuration trait for Spi clk +pub trait SckPin: Pin + sealed::Sealed + PeripheralType { + /// convert the pin to appropriate function for Spi clk usage. + fn as_sck(&self); +} + +/// IO configuration trait for Spi mosi +pub trait MosiPin: Pin + sealed::Sealed + PeripheralType { + /// convert the pin to appropriate function for Spi mosi usage. + fn as_mosi(&self); +} + +/// IO configuration trait for Spi miso +pub trait MisoPin: Pin + sealed::Sealed + PeripheralType { + /// convert the pin to appropriate function for Spi miso usage. + fn as_miso(&self); +} + +macro_rules! impl_pin_trait { + ($fcn:ident, $mode:ident, $($pin:ident, $fn:ident),*) => { + paste! { + $( + impl [<$mode:camel Pin>] for crate::peripherals::$pin { + fn [](&self) { + // UM11147 table 530 pg 518 + self.set_function(crate::iopctl::Function::$fn) + .set_pull(Pull::None) + .enable_input_buffer() + .set_slew_rate(SlewRate::Standard) + .set_drive_strength(DriveStrength::Normal) + .disable_analog_multiplex() + .set_drive_mode(DriveMode::PushPull) + .set_input_inverter(Inverter::Disabled); + } + } + )* + } + } +} + +// FLEXCOMM0 +impl_pin_trait!(FLEXCOMM0, sck, PIO0_0, F1, PIO3_0, F5); +impl_pin_trait!(FLEXCOMM0, miso, PIO0_1, F1, PIO3_1, F5); +impl_pin_trait!(FLEXCOMM0, mosi, PIO0_2, F1, PIO3_2, F5); + +// FLEXCOMM1 +impl_pin_trait!(FLEXCOMM1, sck, PIO0_7, F1, PIO7_25, F1); +impl_pin_trait!(FLEXCOMM1, miso, PIO0_8, F1, PIO7_26, F1); +impl_pin_trait!(FLEXCOMM1, mosi, PIO0_9, F1, PIO7_28, F1); + +// FLEXCOMM2 +impl_pin_trait!(FLEXCOMM2, sck, PIO0_14, F1, PIO7_29, F5); +impl_pin_trait!(FLEXCOMM2, miso, PIO0_15, F1, PIO7_30, F5); +impl_pin_trait!(FLEXCOMM2, mosi, PIO0_16, F1, PIO7_31, F5); + +// FLEXCOMM3 +impl_pin_trait!(FLEXCOMM3, sck, PIO0_21, F1); +impl_pin_trait!(FLEXCOMM3, miso, PIO0_22, F1); +impl_pin_trait!(FLEXCOMM3, mosi, PIO0_23, F1); + +// FLEXCOMM4 +impl_pin_trait!(FLEXCOMM4, sck, PIO0_28, F1); +impl_pin_trait!(FLEXCOMM4, miso, PIO0_29, F1); +impl_pin_trait!(FLEXCOMM4, mosi, PIO0_30, F1); + +// FLEXCOMM5 +impl_pin_trait!(FLEXCOMM5, sck, PIO1_3, F1, PIO3_15, F5); +impl_pin_trait!(FLEXCOMM5, miso, PIO1_4, F1, PIO3_16, F5); +impl_pin_trait!(FLEXCOMM5, mosi, PIO1_5, F1, PIO3_17, F5); + +// FLEXCOMM6 +impl_pin_trait!(FLEXCOMM6, sck, PIO3_25, F1); +impl_pin_trait!(FLEXCOMM6, miso, PIO3_26, F1); +impl_pin_trait!(FLEXCOMM6, mosi, PIO3_27, F1); + +// FLEXCOMM7 +impl_pin_trait!(FLEXCOMM7, sck, PIO4_0, F1); +impl_pin_trait!(FLEXCOMM7, miso, PIO4_1, F1); +impl_pin_trait!(FLEXCOMM7, mosi, PIO4_2, F1); + +// FLEXCOMM14 +impl_pin_trait!(FLEXCOMM14, sck, PIO1_11, F1); +impl_pin_trait!(FLEXCOMM14, miso, PIO1_12, F1); +impl_pin_trait!(FLEXCOMM14, mosi, PIO1_13, F1); + +/// Spi Tx DMA trait. +#[allow(private_bounds)] +pub trait TxDma: crate::dma::Channel {} + +/// Spi Rx DMA trait. +#[allow(private_bounds)] +pub trait RxDma: crate::dma::Channel {} + +macro_rules! impl_dma { + ($fcn:ident, $mode:ident, $dma:ident) => { + paste! { + impl [<$mode Dma>] for crate::peripherals::$dma {} + } + }; +} + +impl_dma!(FLEXCOMM0, Rx, DMA0_CH0); +impl_dma!(FLEXCOMM0, Tx, DMA0_CH1); + +impl_dma!(FLEXCOMM1, Rx, DMA0_CH2); +impl_dma!(FLEXCOMM1, Tx, DMA0_CH3); + +impl_dma!(FLEXCOMM2, Rx, DMA0_CH4); +impl_dma!(FLEXCOMM2, Tx, DMA0_CH5); + +impl_dma!(FLEXCOMM3, Rx, DMA0_CH6); +impl_dma!(FLEXCOMM3, Tx, DMA0_CH7); + +impl_dma!(FLEXCOMM4, Rx, DMA0_CH8); +impl_dma!(FLEXCOMM4, Tx, DMA0_CH9); + +impl_dma!(FLEXCOMM5, Rx, DMA0_CH10); +impl_dma!(FLEXCOMM5, Tx, DMA0_CH11); + +impl_dma!(FLEXCOMM6, Rx, DMA0_CH12); +impl_dma!(FLEXCOMM6, Tx, DMA0_CH13); + +impl_dma!(FLEXCOMM7, Rx, DMA0_CH14); +impl_dma!(FLEXCOMM7, Tx, DMA0_CH15); + +impl_dma!(FLEXCOMM14, Rx, DMA0_CH16); +impl_dma!(FLEXCOMM14, Tx, DMA0_CH17); + +// ============================== + +impl<'d, M: IoMode> embedded_hal_02::blocking::spi::Transfer for Spi<'d, M> { + type Error = Error; + fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> { + self.blocking_transfer_in_place(words)?; + Ok(words) + } +} + +impl<'d, M: IoMode> embedded_hal_02::blocking::spi::Write for Spi<'d, M> { + type Error = Error; + + fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(words) + } +} + +impl embedded_hal_1::spi::Error for Error { + fn kind(&self) -> embedded_hal_1::spi::ErrorKind { + match *self {} + } +} + +impl<'d, M: IoMode> embedded_hal_1::spi::ErrorType for Spi<'d, M> { + type Error = Error; +} + +impl<'d, M: IoMode> embedded_hal_1::spi::SpiBus for Spi<'d, M> { + fn flush(&mut self) -> Result<(), Self::Error> { + self.flush() + } + + fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(words) + } + + fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(words) + } + + fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { + self.blocking_transfer(read, write) + } + + fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_transfer_in_place(words) + } +} + +impl<'d> embedded_hal_async::spi::SpiBus for Spi<'d, Async> { + async fn flush(&mut self) -> Result<(), Self::Error> { + self.async_flush().await; + + Ok(()) + } + + async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { + self.async_write(words).await + } + + async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { + self.async_read(words).await + } + + async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { + self.async_transfer(read, write).await + } + + async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { + self.async_transfer_in_place(words).await + } +} + +impl<'d, M: IoMode> SetConfig for Spi<'d, M> { + type Config = Config; + type ConfigError = (); + fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { + self.set_config(config); + + Ok(()) + } +} -- cgit