From f1bedbe3dcf0006a175f4c3e5dcc08dc9a447a98 Mon Sep 17 00:00:00 2001 From: Dominic Fischer <14130965+Dominaezzz@users.noreply.github.com> Date: Mon, 30 Sep 2024 14:32:18 +0100 Subject: [PATCH] Split DMA RegisterAccess trait into RX/TX (#2249) * Reduce macro usage in PDMA * Split DMA RegisterAccess trait into RX/TX * Move set_isr to Ext trait --------- Co-authored-by: Dominic Fischer --- esp-hal/src/dma/gdma.rs | 481 +++++++------- esp-hal/src/dma/mod.rs | 395 +++++------ esp-hal/src/dma/pdma.rs | 1386 +++++++++++++++++++++------------------ 3 files changed, 1192 insertions(+), 1070 deletions(-) diff --git a/esp-hal/src/dma/gdma.rs b/esp-hal/src/dma/gdma.rs index 6eff6b056..0b5992ec0 100644 --- a/esp-hal/src/dma/gdma.rs +++ b/esp-hal/src/dma/gdma.rs @@ -21,11 +21,17 @@ use crate::{ }; #[non_exhaustive] -pub struct Channel {} +#[doc(hidden)] +pub struct ChannelTxImpl {} -impl crate::private::Sealed for Channel {} +use embassy_sync::waitqueue::AtomicWaker; -impl Channel { +static TX_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [const { AtomicWaker::new() }; CHANNEL_COUNT]; +static RX_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [const { AtomicWaker::new() }; CHANNEL_COUNT]; + +impl crate::private::Sealed for ChannelTxImpl {} + +impl ChannelTxImpl { #[inline(always)] fn ch() -> &'static crate::peripherals::dma::ch::CH { let dma = unsafe { &*crate::peripherals::DMA::PTR }; @@ -34,74 +40,77 @@ impl Channel { #[cfg(any(esp32c2, esp32c3))] #[inline(always)] - fn in_int() -> &'static crate::peripherals::dma::int_ch::INT_CH { + fn int() -> &'static crate::peripherals::dma::int_ch::INT_CH { let dma = unsafe { &*crate::peripherals::DMA::PTR }; dma.int_ch(N as usize) } #[inline(always)] #[cfg(any(esp32c6, esp32h2))] - fn in_int() -> &'static crate::peripherals::dma::in_int_ch::IN_INT_CH { - let dma = unsafe { &*crate::peripherals::DMA::PTR }; - dma.in_int_ch(N as usize) - } - #[cfg(esp32s3)] - #[inline(always)] - fn in_int() -> &'static crate::peripherals::dma::ch::in_int::IN_INT { - let dma = unsafe { &*crate::peripherals::DMA::PTR }; - dma.ch(N as usize).in_int() - } - - #[cfg(any(esp32c2, esp32c3))] - #[inline(always)] - fn out_int() -> &'static crate::peripherals::dma::int_ch::INT_CH { - let dma = unsafe { &*crate::peripherals::DMA::PTR }; - dma.int_ch(N as usize) - } - #[inline(always)] - #[cfg(any(esp32c6, esp32h2))] - fn out_int() -> &'static crate::peripherals::dma::out_int_ch::OUT_INT_CH { + fn int() -> &'static crate::peripherals::dma::out_int_ch::OUT_INT_CH { let dma = unsafe { &*crate::peripherals::DMA::PTR }; dma.out_int_ch(N as usize) } #[cfg(esp32s3)] #[inline(always)] - fn out_int() -> &'static crate::peripherals::dma::ch::out_int::OUT_INT { + fn int() -> &'static crate::peripherals::dma::ch::out_int::OUT_INT { let dma = unsafe { &*crate::peripherals::DMA::PTR }; dma.ch(N as usize).out_int() } } -impl RegisterAccess for Channel { - #[cfg(gdma)] - fn set_mem2mem_mode(value: bool) { - Self::ch() - .in_conf0() - .modify(|_, w| w.mem_trans_en().bit(value)); +impl RegisterAccess for ChannelTxImpl { + fn reset(&self) { + let conf0 = Self::ch().out_conf0(); + conf0.modify(|_, w| w.out_rst().set_bit()); + conf0.modify(|_, w| w.out_rst().clear_bit()); } - #[cfg(esp32s3)] - fn set_out_ext_mem_block_size(size: DmaExtMemBKSize) { - Self::ch() - .out_conf1() - .modify(|_, w| unsafe { w.out_ext_mem_bk_size().bits(size as u8) }); - } - - fn set_out_burstmode(burst_mode: bool) { + fn set_burst_mode(&self, burst_mode: bool) { Self::ch().out_conf0().modify(|_, w| { w.out_data_burst_en().bit(burst_mode); w.outdscr_burst_en().bit(burst_mode) }); } - fn set_out_priority(priority: DmaPriority) { + fn set_priority(&self, priority: DmaPriority) { Self::ch() .out_pri() .write(|w| unsafe { w.tx_pri().bits(priority as u8) }); } - fn clear_out_interrupts() { + fn set_peripheral(&self, peripheral: u8) { + Self::ch() + .out_peri_sel() + .modify(|_, w| unsafe { w.peri_out_sel().bits(peripheral) }); + } + + fn set_link_addr(&self, address: u32) { + Self::ch() + .out_link() + .modify(|_, w| unsafe { w.outlink_addr().bits(address) }); + } + + fn start(&self) { + Self::ch() + .out_link() + .modify(|_, w| w.outlink_start().set_bit()); + } + + fn stop(&self) { + Self::ch() + .out_link() + .modify(|_, w| w.outlink_stop().set_bit()); + } + + fn restart(&self) { + Self::ch() + .out_link() + .modify(|_, w| w.outlink_restart().set_bit()); + } + + fn clear_interrupts(&self) { #[cfg(not(esp32s3))] - Self::out_int().clr().write(|w| { + Self::int().clr().write(|w| { w.out_eof().clear_bit_by_one(); w.out_dscr_err().clear_bit_by_one(); w.out_done().clear_bit_by_one(); @@ -111,7 +120,7 @@ impl RegisterAccess for Channel { }); #[cfg(esp32s3)] - Self::out_int().clr().write(|w| { + Self::int().clr().write(|w| { w.out_eof().clear_bit_by_one(); w.out_dscr_err().clear_bit_by_one(); w.out_done().clear_bit_by_one(); @@ -123,122 +132,27 @@ impl RegisterAccess for Channel { }); } - fn reset_out() { - let conf0 = Self::ch().out_conf0(); - conf0.modify(|_, w| w.out_rst().set_bit()); - conf0.modify(|_, w| w.out_rst().clear_bit()); - } - - fn set_out_descriptors(address: u32) { + #[cfg(esp32s3)] + fn set_ext_mem_block_size(&self, size: DmaExtMemBKSize) { Self::ch() - .out_link() - .modify(|_, w| unsafe { w.outlink_addr().bits(address) }); + .out_conf1() + .modify(|_, w| unsafe { w.out_ext_mem_bk_size().bits(size as u8) }); } +} - fn set_out_peripheral(peripheral: u8) { - Self::ch() - .out_peri_sel() - .modify(|_, w| unsafe { w.peri_out_sel().bits(peripheral) }); - } - - fn start_out() { - Self::ch() - .out_link() - .modify(|_, w| w.outlink_start().set_bit()); - } - - fn stop_out() { - Self::ch() - .out_link() - .modify(|_, w| w.outlink_stop().set_bit()); - } - - fn last_out_dscr_address() -> usize { +impl TxRegisterAccess for ChannelTxImpl { + fn last_dscr_address(&self) -> usize { Self::ch() .out_eof_des_addr() .read() .out_eof_des_addr() .bits() as _ } +} - #[cfg(esp32s3)] - fn set_in_ext_mem_block_size(size: DmaExtMemBKSize) { - Self::ch() - .in_conf1() - .modify(|_, w| unsafe { w.in_ext_mem_bk_size().bits(size as u8) }); - } - - fn set_in_burstmode(burst_mode: bool) { - Self::ch().in_conf0().modify(|_, w| { - w.in_data_burst_en().bit(burst_mode); - w.indscr_burst_en().bit(burst_mode) - }); - } - - fn set_in_priority(priority: DmaPriority) { - Self::ch() - .in_pri() - .write(|w| unsafe { w.rx_pri().bits(priority as u8) }); - } - - fn clear_in_interrupts() { - #[cfg(not(esp32s3))] - Self::in_int().clr().write(|w| { - w.in_suc_eof().clear_bit_by_one(); - w.in_err_eof().clear_bit_by_one(); - w.in_dscr_err().clear_bit_by_one(); - w.in_dscr_empty().clear_bit_by_one(); - w.in_done().clear_bit_by_one(); - w.infifo_ovf().clear_bit_by_one(); - w.infifo_udf().clear_bit_by_one() - }); - - #[cfg(esp32s3)] - Self::in_int().clr().write(|w| { - w.in_suc_eof().clear_bit_by_one(); - w.in_err_eof().clear_bit_by_one(); - w.in_dscr_err().clear_bit_by_one(); - w.in_dscr_empty().clear_bit_by_one(); - w.in_done().clear_bit_by_one(); - w.infifo_ovf_l1().clear_bit_by_one(); - w.infifo_ovf_l3().clear_bit_by_one(); - w.infifo_udf_l1().clear_bit_by_one(); - w.infifo_udf_l3().clear_bit_by_one() - }); - } - - fn reset_in() { - let conf0 = Self::ch().in_conf0(); - conf0.modify(|_, w| w.in_rst().set_bit()); - conf0.modify(|_, w| w.in_rst().clear_bit()); - } - - fn set_in_descriptors(address: u32) { - Self::ch() - .in_link() - .modify(|_, w| unsafe { w.inlink_addr().bits(address) }); - } - - fn set_in_peripheral(peripheral: u8) { - Self::ch() - .in_peri_sel() - .modify(|_, w| unsafe { w.peri_in_sel().bits(peripheral) }); - } - - fn start_in() { - Self::ch() - .in_link() - .modify(|_, w| w.inlink_start().set_bit()); - } - - fn stop_in() { - Self::ch() - .in_link() - .modify(|_, w| w.inlink_stop().set_bit()); - } - - fn listen_out(interrupts: impl Into>) { - Self::out_int().ena().modify(|_, w| { +impl InterruptAccess for ChannelTxImpl { + fn listen(&self, interrupts: impl Into>) { + Self::int().ena().modify(|_, w| { for interrupt in interrupts.into() { match interrupt { DmaTxInterrupt::TotalEof => w.out_total_eof().set_bit(), @@ -251,8 +165,8 @@ impl RegisterAccess for Channel { }) } - fn unlisten_out(interrupts: impl Into>) { - Self::out_int().ena().modify(|_, w| { + fn unlisten(&self, interrupts: impl Into>) { + Self::int().ena().modify(|_, w| { for interrupt in interrupts.into() { match interrupt { DmaTxInterrupt::TotalEof => w.out_total_eof().clear_bit(), @@ -265,10 +179,10 @@ impl RegisterAccess for Channel { }) } - fn is_listening_out() -> EnumSet { + fn is_listening(&self) -> EnumSet { let mut result = EnumSet::new(); - let int_ena = Self::out_int().ena().read(); + let int_ena = Self::int().ena().read(); if int_ena.out_total_eof().bit_is_set() { result |= DmaTxInterrupt::TotalEof; } @@ -285,10 +199,24 @@ impl RegisterAccess for Channel { result } - fn pending_out_interrupts() -> EnumSet { + fn clear(&self, interrupts: impl Into>) { + Self::int().clr().write(|w| { + for interrupt in interrupts.into() { + match interrupt { + DmaTxInterrupt::TotalEof => w.out_total_eof().clear_bit_by_one(), + DmaTxInterrupt::DescriptorError => w.out_dscr_err().clear_bit_by_one(), + DmaTxInterrupt::Eof => w.out_eof().clear_bit_by_one(), + DmaTxInterrupt::Done => w.out_done().clear_bit_by_one(), + }; + } + w + }) + } + + fn pending_interrupts(&self) -> EnumSet { let mut result = EnumSet::new(); - let int_raw = Self::out_int().raw().read(); + let int_raw = Self::int().raw().read(); if int_raw.out_total_eof().bit_is_set() { result |= DmaTxInterrupt::TotalEof; } @@ -305,22 +233,141 @@ impl RegisterAccess for Channel { result } - fn clear_out(interrupts: impl Into>) { - Self::out_int().clr().write(|w| { - for interrupt in interrupts.into() { - match interrupt { - DmaTxInterrupt::TotalEof => w.out_total_eof().clear_bit_by_one(), - DmaTxInterrupt::DescriptorError => w.out_dscr_err().clear_bit_by_one(), - DmaTxInterrupt::Eof => w.out_eof().clear_bit_by_one(), - DmaTxInterrupt::Done => w.out_done().clear_bit_by_one(), - }; - } - w - }) + fn waker(&self) -> &'static AtomicWaker { + &TX_WAKERS[N as usize] + } +} + +#[non_exhaustive] +#[doc(hidden)] +pub struct ChannelRxImpl {} + +impl crate::private::Sealed for ChannelRxImpl {} + +impl ChannelRxImpl { + #[inline(always)] + fn ch() -> &'static crate::peripherals::dma::ch::CH { + let dma = unsafe { &*crate::peripherals::DMA::PTR }; + dma.ch(N as usize) } - fn listen_in(interrupts: impl Into>) { - Self::in_int().ena().modify(|_, w| { + #[cfg(any(esp32c2, esp32c3))] + #[inline(always)] + fn int() -> &'static crate::peripherals::dma::int_ch::INT_CH { + let dma = unsafe { &*crate::peripherals::DMA::PTR }; + dma.int_ch(N as usize) + } + + #[inline(always)] + #[cfg(any(esp32c6, esp32h2))] + fn int() -> &'static crate::peripherals::dma::in_int_ch::IN_INT_CH { + let dma = unsafe { &*crate::peripherals::DMA::PTR }; + dma.in_int_ch(N as usize) + } + + #[cfg(esp32s3)] + #[inline(always)] + fn int() -> &'static crate::peripherals::dma::ch::in_int::IN_INT { + let dma = unsafe { &*crate::peripherals::DMA::PTR }; + dma.ch(N as usize).in_int() + } +} + +impl RegisterAccess for ChannelRxImpl { + fn reset(&self) { + let conf0 = Self::ch().in_conf0(); + conf0.modify(|_, w| w.in_rst().set_bit()); + conf0.modify(|_, w| w.in_rst().clear_bit()); + } + + fn set_burst_mode(&self, burst_mode: bool) { + Self::ch().in_conf0().modify(|_, w| { + w.in_data_burst_en().bit(burst_mode); + w.indscr_burst_en().bit(burst_mode) + }); + } + + fn set_priority(&self, priority: DmaPriority) { + Self::ch() + .in_pri() + .write(|w| unsafe { w.rx_pri().bits(priority as u8) }); + } + + fn set_peripheral(&self, peripheral: u8) { + Self::ch() + .in_peri_sel() + .modify(|_, w| unsafe { w.peri_in_sel().bits(peripheral) }); + } + + fn set_link_addr(&self, address: u32) { + Self::ch() + .in_link() + .modify(|_, w| unsafe { w.inlink_addr().bits(address) }); + } + + fn start(&self) { + Self::ch() + .in_link() + .modify(|_, w| w.inlink_start().set_bit()); + } + + fn stop(&self) { + Self::ch() + .in_link() + .modify(|_, w| w.inlink_stop().set_bit()); + } + + fn restart(&self) { + Self::ch() + .in_link() + .modify(|_, w| w.inlink_restart().set_bit()); + } + + fn clear_interrupts(&self) { + #[cfg(not(esp32s3))] + Self::int().clr().write(|w| { + w.in_suc_eof().clear_bit_by_one(); + w.in_err_eof().clear_bit_by_one(); + w.in_dscr_err().clear_bit_by_one(); + w.in_dscr_empty().clear_bit_by_one(); + w.in_done().clear_bit_by_one(); + w.infifo_ovf().clear_bit_by_one(); + w.infifo_udf().clear_bit_by_one() + }); + + #[cfg(esp32s3)] + Self::int().clr().write(|w| { + w.in_suc_eof().clear_bit_by_one(); + w.in_err_eof().clear_bit_by_one(); + w.in_dscr_err().clear_bit_by_one(); + w.in_dscr_empty().clear_bit_by_one(); + w.in_done().clear_bit_by_one(); + w.infifo_ovf_l1().clear_bit_by_one(); + w.infifo_ovf_l3().clear_bit_by_one(); + w.infifo_udf_l1().clear_bit_by_one(); + w.infifo_udf_l3().clear_bit_by_one() + }); + } + + #[cfg(esp32s3)] + fn set_ext_mem_block_size(&self, size: DmaExtMemBKSize) { + Self::ch() + .in_conf1() + .modify(|_, w| unsafe { w.in_ext_mem_bk_size().bits(size as u8) }); + } +} + +impl RxRegisterAccess for ChannelRxImpl { + fn set_mem2mem_mode(&self, value: bool) { + Self::ch() + .in_conf0() + .modify(|_, w| w.mem_trans_en().bit(value)); + } +} + +impl InterruptAccess for ChannelRxImpl { + fn listen(&self, interrupts: impl Into>) { + Self::int().ena().modify(|_, w| { for interrupt in interrupts.into() { match interrupt { DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().set_bit(), @@ -331,11 +378,11 @@ impl RegisterAccess for Channel { }; } w - }) + }); } - fn unlisten_in(interrupts: impl Into>) { - Self::in_int().ena().modify(|_, w| { + fn unlisten(&self, interrupts: impl Into>) { + Self::int().ena().modify(|_, w| { for interrupt in interrupts.into() { match interrupt { DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().clear_bit(), @@ -349,10 +396,10 @@ impl RegisterAccess for Channel { }) } - fn is_listening_in() -> EnumSet { + fn is_listening(&self) -> EnumSet { let mut result = EnumSet::new(); - let int_ena = Self::in_int().ena().read(); + let int_ena = Self::int().ena().read(); if int_ena.in_dscr_err().bit_is_set() { result |= DmaRxInterrupt::DescriptorError; } @@ -372,10 +419,25 @@ impl RegisterAccess for Channel { result } - fn pending_in_interrupts() -> EnumSet { + fn clear(&self, interrupts: impl Into>) { + Self::int().clr().write(|w| { + for interrupt in interrupts.into() { + match interrupt { + DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().clear_bit_by_one(), + DmaRxInterrupt::ErrorEof => w.in_err_eof().clear_bit_by_one(), + DmaRxInterrupt::DescriptorError => w.in_dscr_err().clear_bit_by_one(), + DmaRxInterrupt::DescriptorEmpty => w.in_dscr_empty().clear_bit_by_one(), + DmaRxInterrupt::Done => w.in_done().clear_bit_by_one(), + }; + } + w + }) + } + + fn pending_interrupts(&self) -> EnumSet { let mut result = EnumSet::new(); - let int_raw = Self::in_int().raw().read(); + let int_raw = Self::int().raw().read(); if int_raw.in_dscr_err().bit_is_set() { result |= DmaRxInterrupt::DescriptorError; } @@ -395,47 +457,7 @@ impl RegisterAccess for Channel { result } - fn clear_in(interrupts: impl Into>) { - Self::in_int().clr().write(|w| { - for interrupt in interrupts.into() { - match interrupt { - DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().clear_bit_by_one(), - DmaRxInterrupt::ErrorEof => w.in_err_eof().clear_bit_by_one(), - DmaRxInterrupt::DescriptorError => w.in_dscr_err().clear_bit_by_one(), - DmaRxInterrupt::DescriptorEmpty => w.in_dscr_empty().clear_bit_by_one(), - DmaRxInterrupt::Done => w.in_done().clear_bit_by_one(), - }; - } - w - }) - } -} - -#[non_exhaustive] -#[doc(hidden)] -pub struct ChannelTxImpl {} - -use embassy_sync::waitqueue::AtomicWaker; - -static TX_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [const { AtomicWaker::new() }; CHANNEL_COUNT]; -static RX_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [const { AtomicWaker::new() }; CHANNEL_COUNT]; - -impl crate::private::Sealed for ChannelTxImpl {} - -impl TxChannel> for ChannelTxImpl { - fn waker() -> &'static AtomicWaker { - &TX_WAKERS[N as usize] - } -} - -#[non_exhaustive] -#[doc(hidden)] -pub struct ChannelRxImpl {} - -impl crate::private::Sealed for ChannelRxImpl {} - -impl RxChannel> for ChannelRxImpl { - fn waker() -> &'static AtomicWaker { + fn waker(&self) -> &'static AtomicWaker { &RX_WAKERS[N as usize] } } @@ -477,10 +499,19 @@ macro_rules! impl_channel { impl crate::private::Sealed for [] {} impl DmaChannel for [] { - type Channel = Channel<$num>; type Rx = ChannelRxImpl<$num>; type Tx = ChannelTxImpl<$num>; type P = SuitablePeripheral<$num>; + } + + impl DmaChannelExt for [] { + fn get_rx_interrupts() -> impl InterruptAccess { + ChannelRxImpl::<$num> {} + } + + fn get_tx_interrupts() -> impl InterruptAccess { + ChannelTxImpl::<$num> {} + } fn set_isr(handler: $crate::interrupt::InterruptHandler) { let mut dma = unsafe { crate::peripherals::DMA::steal() }; @@ -497,11 +528,17 @@ macro_rules! impl_channel { burst_mode: bool, priority: DmaPriority, ) -> crate::dma::Channel<'a, [], M> { - let mut tx_impl = ChannelTxImpl {}; - tx_impl.init(burst_mode, priority); + let tx_impl = ChannelTxImpl {}; + tx_impl.set_burst_mode(burst_mode); + tx_impl.set_priority(priority); - let mut rx_impl = ChannelRxImpl {}; - rx_impl.init(burst_mode, priority); + let rx_impl = ChannelRxImpl {}; + rx_impl.set_burst_mode(burst_mode); + rx_impl.set_priority(priority); + // clear the mem2mem mode to avoid failed DMA if this + // channel was previously used for a mem2mem transfer. + #[cfg(gdma)] + rx_impl.set_mem2mem_mode(false); crate::dma::Channel { tx: ChannelTx::new(tx_impl, burst_mode), diff --git a/esp-hal/src/dma/mod.rs b/esp-hal/src/dma/mod.rs index b77b8bc07..03aed246d 100644 --- a/esp-hal/src/dma/mod.rs +++ b/esp-hal/src/dma/mod.rs @@ -1497,17 +1497,20 @@ impl RxCircularState { /// A description of a DMA Channel. pub trait DmaChannel: crate::private::Sealed { - #[doc(hidden)] - type Channel: RegisterAccess; - /// A description of the RX half of a DMA Channel. - type Rx: RxChannel; + type Rx: RxRegisterAccess + InterruptAccess; /// A description of the TX half of a DMA Channel. - type Tx: TxChannel; + type Tx: TxRegisterAccess + InterruptAccess; /// A suitable peripheral for this DMA channel. type P: PeripheralMarker; +} + +#[doc(hidden)] +pub trait DmaChannelExt: DmaChannel { + fn get_rx_interrupts() -> impl InterruptAccess; + fn get_tx_interrupts() -> impl InterruptAccess; #[doc(hidden)] fn set_isr(handler: InterruptHandler); @@ -1516,8 +1519,6 @@ pub trait DmaChannel: crate::private::Sealed { /// The functions here are not meant to be used outside the HAL #[doc(hidden)] pub trait Rx: crate::private::Sealed { - fn init(&mut self, burst_mode: bool, priority: DmaPriority); - unsafe fn prepare_transfer_without_start( &mut self, peri: DmaPeripheral, @@ -1569,53 +1570,7 @@ pub trait Rx: crate::private::Sealed { fn clear_interrupts(&self); - fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker; -} - -#[doc(hidden)] -pub trait RxChannel: crate::private::Sealed -where - R: RegisterAccess, -{ - fn init(&mut self, burst_mode: bool, priority: DmaPriority) { - R::set_in_burstmode(burst_mode); - R::set_in_priority(priority); - // clear the mem2mem mode to avoid failed DMA if this - // channel was previously used for a mem2mem transfer. - #[cfg(gdma)] - R::set_mem2mem_mode(false); - } - - unsafe fn prepare_transfer_without_start( - &mut self, - first_desc: *mut DmaDescriptor, - peri: DmaPeripheral, - ) -> Result<(), DmaError> { - compiler_fence(core::sync::atomic::Ordering::SeqCst); - - R::clear_in_interrupts(); - R::reset_in(); - R::set_in_descriptors(first_desc as u32); - R::set_in_peripheral(peri as u8); - - Ok(()) - } - - fn start_transfer(&mut self) -> Result<(), DmaError> { - R::start_in(); - - if R::pending_in_interrupts().contains(DmaRxInterrupt::DescriptorError) { - Err(DmaError::DescriptorError) - } else { - Ok(()) - } - } - - fn stop_transfer(&mut self) { - R::stop_in(); - } - - fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker; + fn waker(&self) -> &'static embassy_sync::waitqueue::AtomicWaker; } // DMA receive channel @@ -1649,10 +1604,6 @@ impl<'a, CH> Rx for ChannelRx<'a, CH> where CH: DmaChannel, { - fn init(&mut self, burst_mode: bool, priority: DmaPriority) { - self.rx_impl.init(burst_mode, priority); - } - unsafe fn prepare_transfer_without_start( &mut self, peri: DmaPeripheral, @@ -1685,8 +1636,14 @@ where } } - self.rx_impl - .prepare_transfer_without_start(chain.first() as _, peri) + compiler_fence(core::sync::atomic::Ordering::SeqCst); + + self.rx_impl.clear_interrupts(); + self.rx_impl.reset(); + self.rx_impl.set_link_addr(chain.first() as u32); + self.rx_impl.set_peripheral(peri as u8); + + Ok(()) } unsafe fn prepare_transfer( @@ -1701,46 +1658,61 @@ where return Err(DmaError::InvalidAlignment); } - self.rx_impl - .prepare_transfer_without_start(preparation.start, peri) + compiler_fence(core::sync::atomic::Ordering::SeqCst); + + self.rx_impl.clear_interrupts(); + self.rx_impl.reset(); + self.rx_impl.set_link_addr(preparation.start as u32); + self.rx_impl.set_peripheral(peri as u8); + + Ok(()) } fn start_transfer(&mut self) -> Result<(), DmaError> { - self.rx_impl.start_transfer() + self.rx_impl.start(); + + if self + .pending_in_interrupts() + .contains(DmaRxInterrupt::DescriptorError) + { + Err(DmaError::DescriptorError) + } else { + Ok(()) + } } fn stop_transfer(&mut self) { - self.rx_impl.stop_transfer() + self.rx_impl.stop() } #[cfg(esp32s3)] fn set_ext_mem_block_size(&self, size: DmaExtMemBKSize) { - CH::Channel::set_in_ext_mem_block_size(size); + self.rx_impl.set_ext_mem_block_size(size); } #[cfg(gdma)] fn set_mem2mem_mode(&mut self, value: bool) { - CH::Channel::set_mem2mem_mode(value); + self.rx_impl.set_mem2mem_mode(value); } fn listen_in(&self, interrupts: impl Into>) { - CH::Channel::listen_in(interrupts); + self.rx_impl.listen(interrupts); } fn unlisten_in(&self, interrupts: impl Into>) { - CH::Channel::unlisten_in(interrupts); + self.rx_impl.unlisten(interrupts); } fn is_listening_in(&self) -> EnumSet { - CH::Channel::is_listening_in() + self.rx_impl.is_listening() } fn clear_in(&self, interrupts: impl Into>) { - CH::Channel::clear_in(interrupts); + self.rx_impl.clear(interrupts); } fn pending_in_interrupts(&self) -> EnumSet { - CH::Channel::pending_in_interrupts() + self.rx_impl.pending_interrupts() } fn is_done(&self) -> bool { @@ -1749,19 +1721,17 @@ where } fn clear_interrupts(&self) { - CH::Channel::clear_in_interrupts(); + self.rx_impl.clear_interrupts(); } - fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { - CH::Rx::waker() + fn waker(&self) -> &'static embassy_sync::waitqueue::AtomicWaker { + self.rx_impl.waker() } } /// The functions here are not meant to be used outside the HAL #[doc(hidden)] pub trait Tx: crate::private::Sealed { - fn init(&mut self, burst_mode: bool, priority: DmaPriority); - unsafe fn prepare_transfer_without_start( &mut self, peri: DmaPeripheral, @@ -1803,69 +1773,11 @@ pub trait Tx: crate::private::Sealed { fn clear_interrupts(&self); - fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker; + fn waker(&self) -> &'static embassy_sync::waitqueue::AtomicWaker; fn last_out_dscr_address(&self) -> usize; } -#[doc(hidden)] -pub trait TxChannel: crate::private::Sealed -where - R: RegisterAccess, -{ - fn init(&mut self, burst_mode: bool, priority: DmaPriority) { - R::set_out_burstmode(burst_mode); - R::set_out_priority(priority); - } - - unsafe fn prepare_transfer_without_start( - &mut self, - first_desc: *mut DmaDescriptor, - peri: DmaPeripheral, - ) -> Result<(), DmaError> { - compiler_fence(core::sync::atomic::Ordering::SeqCst); - - R::clear_out_interrupts(); - R::reset_out(); - R::set_out_descriptors(first_desc as u32); - R::set_out_peripheral(peri as u8); - - Ok(()) - } - - fn start_transfer(&mut self) -> Result<(), DmaError> { - R::start_out(); - - if R::pending_out_interrupts().contains(DmaTxInterrupt::DescriptorError) { - Err(DmaError::DescriptorError) - } else { - Ok(()) - } - } - - fn stop_transfer(&mut self) { - R::stop_out(); - } - - fn listen_out(&self, interrupts: impl Into>) { - R::listen_out(interrupts) - } - fn unlisten_out(&self, interrupts: impl Into>) { - R::unlisten_out(interrupts) - } - fn is_listening_out(&self) -> EnumSet { - R::is_listening_out() - } - fn clear_out(&self, interrupts: impl Into>) { - R::clear_out(interrupts) - } - fn pending_out_interrupts(&self) -> EnumSet { - R::pending_out_interrupts() - } - - fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker; -} - /// DMA transmit channel #[doc(hidden)] pub struct ChannelTx<'a, CH> @@ -1897,10 +1809,6 @@ impl<'a, CH> Tx for ChannelTx<'a, CH> where CH: DmaChannel, { - fn init(&mut self, burst_mode: bool, priority: DmaPriority) { - self.tx_impl.init(burst_mode, priority); - } - unsafe fn prepare_transfer_without_start( &mut self, peri: DmaPeripheral, @@ -1922,8 +1830,15 @@ where crate::soc::cache_writeback_addr(des.buffer as u32, des.size() as u32); } } - self.tx_impl - .prepare_transfer_without_start(chain.first() as _, peri) + + compiler_fence(core::sync::atomic::Ordering::SeqCst); + + self.tx_impl.clear_interrupts(); + self.tx_impl.reset(); + self.tx_impl.set_link_addr(chain.first() as u32); + self.tx_impl.set_peripheral(peri as u8); + + Ok(()) } unsafe fn prepare_transfer( @@ -1945,95 +1860,127 @@ where } ); // TODO: Get burst mode from DmaBuf. - self.tx_impl - .prepare_transfer_without_start(preparation.start, peri) + + compiler_fence(core::sync::atomic::Ordering::SeqCst); + + self.tx_impl.clear_interrupts(); + self.tx_impl.reset(); + self.tx_impl.set_link_addr(preparation.start as u32); + self.tx_impl.set_peripheral(peri as u8); + + Ok(()) } fn start_transfer(&mut self) -> Result<(), DmaError> { - self.tx_impl.start_transfer() + self.tx_impl.start(); + + if self + .pending_out_interrupts() + .contains(DmaTxInterrupt::DescriptorError) + { + Err(DmaError::DescriptorError) + } else { + Ok(()) + } } fn stop_transfer(&mut self) { - self.tx_impl.stop_transfer() + self.tx_impl.stop() } #[cfg(esp32s3)] fn set_ext_mem_block_size(&self, size: DmaExtMemBKSize) { - CH::Channel::set_out_ext_mem_block_size(size); + self.tx_impl.set_ext_mem_block_size(size); } fn listen_out(&self, interrupts: impl Into>) { - CH::Channel::listen_out(interrupts); + self.tx_impl.listen(interrupts); } fn unlisten_out(&self, interrupts: impl Into>) { - CH::Channel::unlisten_out(interrupts); + self.tx_impl.unlisten(interrupts); } fn is_listening_out(&self) -> EnumSet { - CH::Channel::is_listening_out() + self.tx_impl.is_listening() } fn clear_out(&self, interrupts: impl Into>) { - CH::Channel::clear_out(interrupts); + self.tx_impl.clear(interrupts); } fn pending_out_interrupts(&self) -> EnumSet { - CH::Channel::pending_out_interrupts() + self.tx_impl.pending_interrupts() } - fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { - CH::Tx::waker() + fn waker(&self) -> &'static embassy_sync::waitqueue::AtomicWaker { + self.tx_impl.waker() } fn clear_interrupts(&self) { - CH::Channel::clear_out_interrupts(); + self.tx_impl.clear_interrupts(); } fn last_out_dscr_address(&self) -> usize { - CH::Channel::last_out_dscr_address() + self.tx_impl.last_dscr_address() } } #[doc(hidden)] pub trait RegisterAccess: crate::private::Sealed { + /// Reset the state machine of the channel and FIFO pointer. + fn reset(&self); + + /// Enable/Disable INCR burst transfer for channel reading + /// descriptor and accessing data in internal RAM. + fn set_burst_mode(&self, burst_mode: bool); + + /// The priority of the channel. The larger the value, the higher the + /// priority. + fn set_priority(&self, priority: DmaPriority); + + /// Select a peripheral for the channel. + fn set_peripheral(&self, peripheral: u8); + + /// Set the address of the first descriptor. + fn set_link_addr(&self, address: u32); + + /// Enable the channel for data transfer. + fn start(&self); + + /// Stop the channel from transferring data. + fn stop(&self); + + /// Mount a new descriptor. + fn restart(&self); + + /// Clear all interrupt bits + fn clear_interrupts(&self); + + #[cfg(esp32s3)] + fn set_ext_mem_block_size(&self, size: DmaExtMemBKSize); +} + +#[doc(hidden)] +pub trait RxRegisterAccess: RegisterAccess { #[cfg(gdma)] - fn set_mem2mem_mode(value: bool); - #[cfg(esp32s3)] - fn set_out_ext_mem_block_size(size: DmaExtMemBKSize); - fn set_out_burstmode(burst_mode: bool); - fn set_out_priority(priority: DmaPriority); - fn clear_out_interrupts(); - fn reset_out(); - fn set_out_descriptors(address: u32); - fn set_out_peripheral(peripheral: u8); - fn start_out(); - fn stop_out(); + fn set_mem2mem_mode(&self, value: bool); +} - fn listen_out(interrupts: impl Into>); - fn unlisten_out(interrupts: impl Into>); - fn is_listening_out() -> EnumSet; - fn clear_out(interrupts: impl Into>); - fn pending_out_interrupts() -> EnumSet; +#[doc(hidden)] +pub trait TxRegisterAccess: RegisterAccess { + /// Outlink descriptor address when EOF occurs of Tx channel. + fn last_dscr_address(&self) -> usize; +} - fn listen_in(interrupts: impl Into>); - fn unlisten_in(interrupts: impl Into>); - fn is_listening_in() -> EnumSet; - fn clear_in(interrupts: impl Into>); - fn pending_in_interrupts() -> EnumSet; - - fn last_out_dscr_address() -> usize; - - #[cfg(esp32s3)] - fn set_in_ext_mem_block_size(size: DmaExtMemBKSize); - fn set_in_burstmode(burst_mode: bool); - fn set_in_priority(priority: DmaPriority); - fn clear_in_interrupts(); - fn reset_in(); - fn set_in_descriptors(address: u32); - fn set_in_peripheral(peripheral: u8); - fn start_in(); - fn stop_in(); +#[doc(hidden)] +pub trait InterruptAccess: crate::private::Sealed { + fn listen(&self, interrupts: impl Into>); + fn unlisten(&self, interrupts: impl Into>); + fn is_listening(&self) -> EnumSet; + fn clear(&self, interrupts: impl Into>); + fn pending_interrupts(&self) -> EnumSet; + fn waker(&self) -> &'static embassy_sync::waitqueue::AtomicWaker; } /// DMA Channel @@ -2057,7 +2004,10 @@ where /// with [crate::interrupt::Priority::max()] /// /// Interrupts are not enabled at the peripheral level here. - pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) { + pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) + where + C: DmaChannelExt, + { C::set_isr(handler); } @@ -3001,7 +2951,7 @@ pub(crate) mod asynch { self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>, ) -> Poll { - TX::waker().register(cx.waker()); + self.tx.waker().register(cx.waker()); if self.tx.is_done() { self.tx.clear_interrupts(); Poll::Ready(Ok(())) @@ -3057,7 +3007,7 @@ pub(crate) mod asynch { self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>, ) -> Poll { - RX::waker().register(cx.waker()); + self.rx.waker().register(cx.waker()); if self.rx.is_done() { self.rx.clear_interrupts(); Poll::Ready(Ok(())) @@ -3123,7 +3073,7 @@ pub(crate) mod asynch { self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>, ) -> Poll { - TX::waker().register(cx.waker()); + self.tx.waker().register(cx.waker()); if self .tx .pending_out_interrupts() @@ -3187,7 +3137,7 @@ pub(crate) mod asynch { self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>, ) -> Poll { - RX::waker().register(cx.waker()); + self.rx.waker().register(cx.waker()); if self .rx .pending_in_interrupts() @@ -3229,49 +3179,58 @@ pub(crate) mod asynch { } } - fn handle_interrupt() { - if CH::Channel::pending_in_interrupts().is_disjoint( + fn handle_interrupt() { + let rx = CH::get_rx_interrupts(); + let tx = CH::get_tx_interrupts(); + + if rx.pending_interrupts().is_disjoint( DmaRxInterrupt::DescriptorError | DmaRxInterrupt::DescriptorEmpty | DmaRxInterrupt::ErrorEof, ) { - CH::Channel::unlisten_in( + rx.unlisten( DmaRxInterrupt::DescriptorError | DmaRxInterrupt::DescriptorEmpty | DmaRxInterrupt::ErrorEof | DmaRxInterrupt::SuccessfulEof | DmaRxInterrupt::Done, ); - CH::Rx::waker().wake() + rx.waker().wake() } - if CH::Channel::pending_out_interrupts().contains(DmaTxInterrupt::DescriptorError) { - CH::Channel::unlisten_out( + if tx + .pending_interrupts() + .contains(DmaTxInterrupt::DescriptorError) + { + tx.unlisten( DmaTxInterrupt::DescriptorError | DmaTxInterrupt::TotalEof | DmaTxInterrupt::Done, ); - CH::Tx::waker().wake() + tx.waker().wake() } - if CH::Channel::pending_in_interrupts().contains(DmaRxInterrupt::SuccessfulEof) { - CH::Channel::unlisten_in(DmaRxInterrupt::SuccessfulEof); - CH::Rx::waker().wake() - } - - if CH::Channel::pending_in_interrupts().contains(DmaRxInterrupt::Done) { - CH::Channel::unlisten_in(DmaRxInterrupt::Done); - CH::Rx::waker().wake() - } - - if CH::Channel::pending_out_interrupts().contains(DmaTxInterrupt::TotalEof) - && CH::Channel::is_listening_out().contains(DmaTxInterrupt::TotalEof) + if rx + .pending_interrupts() + .contains(DmaRxInterrupt::SuccessfulEof) { - CH::Channel::unlisten_out(DmaTxInterrupt::TotalEof); - CH::Tx::waker().wake() + rx.unlisten(DmaRxInterrupt::SuccessfulEof); + rx.waker().wake() } - if CH::Channel::pending_out_interrupts().contains(DmaTxInterrupt::Done) { - CH::Channel::unlisten_out(DmaTxInterrupt::Done); - CH::Tx::waker().wake() + if rx.pending_interrupts().contains(DmaRxInterrupt::Done) { + rx.unlisten(DmaRxInterrupt::Done); + rx.waker().wake() + } + + if tx.pending_interrupts().contains(DmaTxInterrupt::TotalEof) + && tx.is_listening().contains(DmaTxInterrupt::TotalEof) + { + tx.unlisten(DmaTxInterrupt::TotalEof); + tx.waker().wake() + } + + if tx.pending_interrupts().contains(DmaTxInterrupt::Done) { + tx.unlisten(DmaTxInterrupt::Done); + tx.waker().wake() } } diff --git a/esp-hal/src/dma/pdma.rs b/esp-hal/src/dma/pdma.rs index 20d47b0cf..cdd4574c2 100644 --- a/esp-hal/src/dma/pdma.rs +++ b/esp-hal/src/dma/pdma.rs @@ -11,12 +11,352 @@ //! [SPI]: ../spi/index.html //! [I2S]: ../i2s/index.html +use embassy_sync::waitqueue::AtomicWaker; + use crate::{ dma::*, peripheral::PeripheralRef, system::{Peripheral, PeripheralClockControl}, }; +type SpiRegisterBlock = crate::peripherals::spi2::RegisterBlock; +type I2sRegisterBlock = crate::peripherals::i2s0::RegisterBlock; + +#[doc(hidden)] +pub trait PdmaChannel: crate::private::Sealed { + type RegisterBlock; + + fn register_block() -> &'static Self::RegisterBlock; + fn tx_waker() -> &'static AtomicWaker; + fn rx_waker() -> &'static AtomicWaker; +} + +#[doc(hidden)] +pub struct SpiDmaTxChannelImpl(PhantomData); +#[doc(hidden)] +pub struct SpiDmaRxChannelImpl(PhantomData); + +impl crate::private::Sealed for SpiDmaTxChannelImpl {} +impl crate::private::Sealed for SpiDmaRxChannelImpl {} + +impl> RegisterAccess for SpiDmaTxChannelImpl { + fn reset(&self) { + let spi = C::register_block(); + spi.dma_conf().modify(|_, w| w.out_rst().set_bit()); + spi.dma_conf().modify(|_, w| w.out_rst().clear_bit()); + } + + fn set_burst_mode(&self, burst_mode: bool) { + let spi = C::register_block(); + spi.dma_conf() + .modify(|_, w| w.outdscr_burst_en().bit(burst_mode)); + } + + fn set_priority(&self, _priority: DmaPriority) {} + + fn set_peripheral(&self, _peripheral: u8) { + // no-op + } + + fn set_link_addr(&self, address: u32) { + let spi = C::register_block(); + spi.dma_out_link() + .modify(|_, w| unsafe { w.outlink_addr().bits(address) }); + } + + fn start(&self) { + let spi = C::register_block(); + spi.dma_out_link() + .modify(|_, w| w.outlink_start().set_bit()); + } + + fn stop(&self) { + let spi = C::register_block(); + spi.dma_out_link().modify(|_, w| w.outlink_stop().set_bit()); + } + + fn restart(&self) { + let spi = C::register_block(); + spi.dma_out_link() + .modify(|_, w| w.outlink_restart().set_bit()); + } + + fn clear_interrupts(&self) { + let spi = C::register_block(); + spi.dma_int_clr().write(|w| { + w.out_done().clear_bit_by_one(); + w.out_eof().clear_bit_by_one(); + w.out_total_eof().clear_bit_by_one(); + w.outlink_dscr_error().clear_bit_by_one() + }); + } +} + +impl> TxRegisterAccess for SpiDmaTxChannelImpl { + fn last_dscr_address(&self) -> usize { + let spi = C::register_block(); + spi.out_eof_des_addr().read().dma_out_eof_des_addr().bits() as usize + } +} + +impl> InterruptAccess + for SpiDmaTxChannelImpl +{ + fn listen(&self, interrupts: impl Into>) { + let spi = C::register_block(); + spi.dma_int_ena().modify(|_, w| { + for interrupt in interrupts.into() { + match interrupt { + DmaTxInterrupt::TotalEof => w.out_total_eof().set_bit(), + DmaTxInterrupt::DescriptorError => w.outlink_dscr_error().set_bit(), + DmaTxInterrupt::Eof => w.out_eof().set_bit(), + DmaTxInterrupt::Done => w.out_done().set_bit(), + }; + } + w + }) + } + + fn unlisten(&self, interrupts: impl Into>) { + let spi = C::register_block(); + spi.dma_int_ena().modify(|_, w| { + for interrupt in interrupts.into() { + match interrupt { + DmaTxInterrupt::TotalEof => w.out_total_eof().clear_bit(), + DmaTxInterrupt::DescriptorError => w.outlink_dscr_error().clear_bit(), + DmaTxInterrupt::Eof => w.out_eof().clear_bit(), + DmaTxInterrupt::Done => w.out_done().clear_bit(), + }; + } + w + }) + } + + fn is_listening(&self) -> EnumSet { + let mut result = EnumSet::new(); + + let spi = C::register_block(); + let int_ena = spi.dma_int_ena().read(); + if int_ena.out_total_eof().bit_is_set() { + result |= DmaTxInterrupt::TotalEof; + } + if int_ena.outlink_dscr_error().bit_is_set() { + result |= DmaTxInterrupt::DescriptorError; + } + if int_ena.out_eof().bit_is_set() { + result |= DmaTxInterrupt::Eof; + } + if int_ena.out_done().bit_is_set() { + result |= DmaTxInterrupt::Done; + } + + result + } + + fn clear(&self, interrupts: impl Into>) { + let spi = C::register_block(); + spi.dma_int_clr().write(|w| { + for interrupt in interrupts.into() { + match interrupt { + DmaTxInterrupt::TotalEof => w.out_total_eof().clear_bit_by_one(), + DmaTxInterrupt::DescriptorError => w.outlink_dscr_error().clear_bit_by_one(), + DmaTxInterrupt::Eof => w.out_eof().clear_bit_by_one(), + DmaTxInterrupt::Done => w.out_done().clear_bit_by_one(), + }; + } + w + }) + } + + fn pending_interrupts(&self) -> EnumSet { + let mut result = EnumSet::new(); + + let spi = C::register_block(); + let int_raw = spi.dma_int_raw().read(); + if int_raw.out_total_eof().bit_is_set() { + result |= DmaTxInterrupt::TotalEof; + } + if int_raw.outlink_dscr_error().bit_is_set() { + result |= DmaTxInterrupt::DescriptorError; + } + if int_raw.out_eof().bit_is_set() { + result |= DmaTxInterrupt::Eof; + } + if int_raw.out_done().bit_is_set() { + result |= DmaTxInterrupt::Done; + } + + result + } + + fn waker(&self) -> &'static AtomicWaker { + C::tx_waker() + } +} + +impl> RegisterAccess for SpiDmaRxChannelImpl { + fn reset(&self) { + let spi = C::register_block(); + spi.dma_conf().modify(|_, w| w.in_rst().set_bit()); + spi.dma_conf().modify(|_, w| w.in_rst().clear_bit()); + } + + fn set_burst_mode(&self, burst_mode: bool) { + let spi = C::register_block(); + spi.dma_conf() + .modify(|_, w| w.indscr_burst_en().bit(burst_mode)); + } + + fn set_priority(&self, _priority: DmaPriority) {} + + fn set_peripheral(&self, _peripheral: u8) { + // no-op + } + + fn set_link_addr(&self, address: u32) { + let spi = C::register_block(); + spi.dma_in_link() + .modify(|_, w| unsafe { w.inlink_addr().bits(address) }); + } + + fn start(&self) { + let spi = C::register_block(); + spi.dma_in_link().modify(|_, w| w.inlink_start().set_bit()); + } + + fn stop(&self) { + let spi = C::register_block(); + spi.dma_in_link().modify(|_, w| w.inlink_stop().set_bit()); + } + + fn restart(&self) { + let spi = C::register_block(); + spi.dma_in_link() + .modify(|_, w| w.inlink_restart().set_bit()); + } + + fn clear_interrupts(&self) { + let spi = C::register_block(); + spi.dma_int_clr().write(|w| { + w.in_done().clear_bit_by_one(); + w.in_err_eof().clear_bit_by_one(); + w.in_suc_eof().clear_bit_by_one(); + w.inlink_dscr_error().clear_bit_by_one() + }); + } +} + +impl> RxRegisterAccess for SpiDmaRxChannelImpl {} + +impl> InterruptAccess + for SpiDmaRxChannelImpl +{ + fn listen(&self, interrupts: impl Into>) { + let spi = C::register_block(); + spi.dma_int_ena().modify(|_, w| { + for interrupt in interrupts.into() { + match interrupt { + DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().set_bit(), + DmaRxInterrupt::ErrorEof => w.in_err_eof().set_bit(), + DmaRxInterrupt::DescriptorError => w.inlink_dscr_error().set_bit(), + DmaRxInterrupt::DescriptorEmpty => w.inlink_dscr_empty().set_bit(), + DmaRxInterrupt::Done => w.in_done().set_bit(), + }; + } + w + }) + } + + fn unlisten(&self, interrupts: impl Into>) { + let spi = C::register_block(); + spi.dma_int_ena().modify(|_, w| { + for interrupt in interrupts.into() { + match interrupt { + DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().clear_bit(), + DmaRxInterrupt::ErrorEof => w.in_err_eof().clear_bit(), + DmaRxInterrupt::DescriptorError => w.inlink_dscr_error().clear_bit(), + DmaRxInterrupt::DescriptorEmpty => w.inlink_dscr_empty().clear_bit(), + DmaRxInterrupt::Done => w.in_done().clear_bit(), + }; + } + w + }) + } + + fn is_listening(&self) -> EnumSet { + let mut result = EnumSet::new(); + + let spi = C::register_block(); + let int_ena = spi.dma_int_ena().read(); + if int_ena.inlink_dscr_error().bit_is_set() { + result |= DmaRxInterrupt::DescriptorError; + } + if int_ena.inlink_dscr_empty().bit_is_set() { + result |= DmaRxInterrupt::DescriptorEmpty; + } + if int_ena.in_suc_eof().bit_is_set() { + result |= DmaRxInterrupt::SuccessfulEof; + } + if int_ena.in_err_eof().bit_is_set() { + result |= DmaRxInterrupt::ErrorEof; + } + if int_ena.in_done().bit_is_set() { + result |= DmaRxInterrupt::Done; + } + + result + } + + fn clear(&self, interrupts: impl Into>) { + let spi = C::register_block(); + spi.dma_int_clr().modify(|_, w| { + for interrupt in interrupts.into() { + match interrupt { + DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().clear_bit_by_one(), + DmaRxInterrupt::ErrorEof => w.in_err_eof().clear_bit_by_one(), + DmaRxInterrupt::DescriptorError => w.inlink_dscr_error().clear_bit_by_one(), + DmaRxInterrupt::DescriptorEmpty => w.inlink_dscr_empty().clear_bit_by_one(), + DmaRxInterrupt::Done => w.in_done().clear_bit_by_one(), + }; + } + w + }) + } + + fn pending_interrupts(&self) -> EnumSet { + let mut result = EnumSet::new(); + + let spi = C::register_block(); + let int_raw = spi.dma_int_raw().read(); + if int_raw.inlink_dscr_error().bit_is_set() { + result |= DmaRxInterrupt::DescriptorError; + } + if int_raw.inlink_dscr_empty().bit_is_set() { + result |= DmaRxInterrupt::DescriptorEmpty; + } + if int_raw.in_suc_eof().bit_is_set() { + result |= DmaRxInterrupt::SuccessfulEof; + } + if int_raw.in_err_eof().bit_is_set() { + result |= DmaRxInterrupt::ErrorEof; + } + if int_raw.in_done().bit_is_set() { + result |= DmaRxInterrupt::Done; + } + + result + } + + fn waker(&self) -> &'static AtomicWaker { + C::rx_waker() + } +} + +#[doc(hidden)] +pub struct SpiDmaChannel(PhantomData); + +impl crate::private::Sealed for SpiDmaChannel {} + macro_rules! ImplSpiChannel { ($num: literal) => { paste::paste! { @@ -25,324 +365,46 @@ macro_rules! ImplSpiChannel { pub struct [] {} impl DmaChannel for [] { - type Channel = []; - type Rx = []; - type Tx = []; + type Rx = SpiDmaRxChannelImpl; + type Tx = SpiDmaTxChannelImpl; type P = []; + } - fn set_isr(handler: $crate::interrupt::InterruptHandler) { - let mut spi = unsafe { $crate::peripherals::[< SPI $num >]::steal() }; - spi.[< bind_spi $num _dma_interrupt>](handler.handler()); - $crate::interrupt::enable($crate::peripherals::Interrupt::[< SPI $num _DMA >], handler.priority()).unwrap(); + impl DmaChannelExt for [] { + fn get_rx_interrupts() -> impl InterruptAccess { + SpiDmaRxChannelImpl::(PhantomData) + } + fn get_tx_interrupts() -> impl InterruptAccess { + SpiDmaTxChannelImpl::(PhantomData) + } + + fn set_isr(handler: InterruptHandler) { + let interrupt = $crate::peripherals::Interrupt::[< SPI $num _DMA >]; + unsafe { + crate::interrupt::bind_interrupt(interrupt, handler.handler()); + } + crate::interrupt::enable(interrupt, handler.priority()).unwrap(); + } + } + + impl PdmaChannel for [] { + type RegisterBlock = SpiRegisterBlock; + + fn register_block() -> &'static SpiRegisterBlock { + unsafe { &*crate::peripherals::[]::PTR } + } + fn tx_waker() -> &'static embassy_sync::waitqueue::AtomicWaker { + static WAKER: embassy_sync::waitqueue::AtomicWaker = embassy_sync::waitqueue::AtomicWaker::new(); + &WAKER + } + fn rx_waker() -> &'static embassy_sync::waitqueue::AtomicWaker { + static WAKER: embassy_sync::waitqueue::AtomicWaker = embassy_sync::waitqueue::AtomicWaker::new(); + &WAKER } } impl $crate::private::Sealed for [] {} - impl RegisterAccess for [] { - fn set_out_burstmode(burst_mode: bool) { - let spi = unsafe { &*crate::peripherals::[]::PTR }; - spi.dma_conf() - .modify(|_, w| w.outdscr_burst_en().bit(burst_mode)); - } - - fn set_out_priority(_priority: DmaPriority) {} - - fn clear_out_interrupts() { - let spi = unsafe { &*crate::peripherals::[]::PTR }; - spi.dma_int_clr().write(|w| { - w.out_done().clear_bit_by_one(); - w.out_eof().clear_bit_by_one(); - w.out_total_eof().clear_bit_by_one(); - w.outlink_dscr_error().clear_bit_by_one() - }); - } - - fn reset_out() { - let spi = unsafe { &*crate::peripherals::[]::PTR }; - spi.dma_conf().modify(|_, w| w.out_rst().set_bit()); - spi.dma_conf().modify(|_, w| w.out_rst().clear_bit()); - } - - fn set_out_descriptors(address: u32) { - let spi = unsafe { &*crate::peripherals::[]::PTR }; - spi.dma_out_link() - .modify(|_, w| unsafe { w.outlink_addr().bits(address) }); - } - - fn set_out_peripheral(_peripheral: u8) { - // no-op - } - - fn start_out() { - let spi = unsafe { &*crate::peripherals::[]::PTR }; - spi.dma_out_link().modify(|_, w| w.outlink_start().set_bit()); - } - - fn stop_out() { - let spi = unsafe { &*crate::peripherals::[]::PTR }; - spi.dma_out_link().modify(|_, w| w.outlink_stop().set_bit()); - } - - fn last_out_dscr_address() -> usize { - let spi = unsafe { &*crate::peripherals::[]::PTR }; - spi.out_eof_des_addr().read().dma_out_eof_des_addr().bits() as usize - } - - fn set_in_burstmode(burst_mode: bool) { - let spi = unsafe { &*crate::peripherals::[]::PTR }; - spi.dma_conf() - .modify(|_, w| w.indscr_burst_en().bit(burst_mode)); - } - - fn set_in_priority(_priority: DmaPriority) {} - - fn clear_in_interrupts() { - let spi = unsafe { &*crate::peripherals::[]::PTR }; - spi.dma_int_clr().write(|w| { - w.in_done().clear_bit_by_one(); - w.in_err_eof().clear_bit_by_one(); - w.in_suc_eof().clear_bit_by_one(); - w.inlink_dscr_error().clear_bit_by_one() - }); - } - - fn reset_in() { - let spi = unsafe { &*crate::peripherals::[]::PTR }; - spi.dma_conf().modify(|_, w| w.in_rst().set_bit()); - spi.dma_conf().modify(|_, w| w.in_rst().clear_bit()); - } - - fn set_in_descriptors(address: u32) { - let spi = unsafe { &*crate::peripherals::[]::PTR }; - spi.dma_in_link() - .modify(|_, w| unsafe { w.inlink_addr().bits(address) }); - } - - fn set_in_peripheral(_peripheral: u8) { - // no-op - } - - fn start_in() { - let spi = unsafe { &*crate::peripherals::[]::PTR }; - spi.dma_in_link().modify(|_, w| w.inlink_start().set_bit()); - } - - fn stop_in() { - let spi = unsafe { &*crate::peripherals::[]::PTR }; - spi.dma_in_link().modify(|_, w| w.inlink_stop().set_bit()); - } - - fn listen_out(interrupts: impl Into>) { - let spi = unsafe { &*crate::peripherals::[]::PTR }; - spi.dma_int_ena().modify(|_, w| { - for interrupt in interrupts.into() { - match interrupt { - DmaTxInterrupt::TotalEof => w.out_total_eof().set_bit(), - DmaTxInterrupt::DescriptorError => w.outlink_dscr_error().set_bit(), - DmaTxInterrupt::Eof => w.out_eof().set_bit(), - DmaTxInterrupt::Done => w.out_done().set_bit(), - }; - } - w - }) - } - - fn unlisten_out(interrupts: impl Into>) { - let spi = unsafe { &*crate::peripherals::[]::PTR }; - spi.dma_int_ena().modify(|_, w| { - for interrupt in interrupts.into() { - match interrupt { - DmaTxInterrupt::TotalEof => w.out_total_eof().clear_bit(), - DmaTxInterrupt::DescriptorError => w.outlink_dscr_error().clear_bit(), - DmaTxInterrupt::Eof => w.out_eof().clear_bit(), - DmaTxInterrupt::Done => w.out_done().clear_bit(), - }; - } - w - }) - } - - fn is_listening_out() -> EnumSet { - let mut result = EnumSet::new(); - - let spi = unsafe { &*crate::peripherals::[]::PTR }; - let int_ena = spi.dma_int_ena().read(); - if int_ena.out_total_eof().bit_is_set() { - result |= DmaTxInterrupt::TotalEof; - } - if int_ena.outlink_dscr_error().bit_is_set() { - result |= DmaTxInterrupt::DescriptorError; - } - if int_ena.out_eof().bit_is_set() { - result |= DmaTxInterrupt::Eof; - } - if int_ena.out_done().bit_is_set() { - result |= DmaTxInterrupt::Done; - } - - result - } - - fn pending_out_interrupts() -> EnumSet { - let mut result = EnumSet::new(); - - let spi = unsafe { &*crate::peripherals::[]::PTR }; - let int_raw = spi.dma_int_raw().read(); - if int_raw.out_total_eof().bit_is_set() { - result |= DmaTxInterrupt::TotalEof; - } - if int_raw.outlink_dscr_error().bit_is_set() { - result |= DmaTxInterrupt::DescriptorError; - } - if int_raw.out_eof().bit_is_set() { - result |= DmaTxInterrupt::Eof; - } - if int_raw.out_done().bit_is_set() { - result |= DmaTxInterrupt::Done; - } - - result - } - - fn clear_out(interrupts: impl Into>) { - let spi = unsafe { &*crate::peripherals::[]::PTR }; - spi.dma_int_clr().write(|w| { - for interrupt in interrupts.into() { - match interrupt { - DmaTxInterrupt::TotalEof => w.out_total_eof().clear_bit_by_one(), - DmaTxInterrupt::DescriptorError => w.outlink_dscr_error().clear_bit_by_one(), - DmaTxInterrupt::Eof => w.out_eof().clear_bit_by_one(), - DmaTxInterrupt::Done => w.out_done().clear_bit_by_one(), - }; - } - w - }) - } - - fn listen_in(interrupts: impl Into>) { - let spi = unsafe { &*crate::peripherals::[]::PTR }; - spi.dma_int_ena().modify(|_, w| { - for interrupt in interrupts.into() { - match interrupt { - DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().set_bit(), - DmaRxInterrupt::ErrorEof => w.in_err_eof().set_bit(), - DmaRxInterrupt::DescriptorError => w.inlink_dscr_error().set_bit(), - DmaRxInterrupt::DescriptorEmpty => w.inlink_dscr_empty().set_bit(), - DmaRxInterrupt::Done => w.in_done().set_bit(), - }; - } - w - }) - } - - fn unlisten_in(interrupts: impl Into>) { - let spi = unsafe { &*crate::peripherals::[]::PTR }; - spi.dma_int_ena().modify(|_, w| { - for interrupt in interrupts.into() { - match interrupt { - DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().clear_bit(), - DmaRxInterrupt::ErrorEof => w.in_err_eof().clear_bit(), - DmaRxInterrupt::DescriptorError => w.inlink_dscr_error().clear_bit(), - DmaRxInterrupt::DescriptorEmpty => w.inlink_dscr_empty().clear_bit(), - DmaRxInterrupt::Done => w.in_done().clear_bit(), - }; - } - w - }) - } - - fn is_listening_in() -> EnumSet { - let mut result = EnumSet::new(); - - let spi = unsafe { &*crate::peripherals::[]::PTR }; - let int_ena = spi.dma_int_ena().read(); - if int_ena.inlink_dscr_error().bit_is_set() { - result |= DmaRxInterrupt::DescriptorError; - } - if int_ena.inlink_dscr_empty().bit_is_set() { - result |= DmaRxInterrupt::DescriptorEmpty; - } - if int_ena.in_suc_eof().bit_is_set() { - result |= DmaRxInterrupt::SuccessfulEof; - } - if int_ena.in_err_eof().bit_is_set() { - result |= DmaRxInterrupt::ErrorEof; - } - if int_ena.in_done().bit_is_set() { - result |= DmaRxInterrupt::Done; - } - - result - } - - fn pending_in_interrupts() -> EnumSet { - let mut result = EnumSet::new(); - - let spi = unsafe { &*crate::peripherals::[]::PTR }; - let int_raw = spi.dma_int_raw().read(); - if int_raw.inlink_dscr_error().bit_is_set() { - result |= DmaRxInterrupt::DescriptorError; - } - if int_raw.inlink_dscr_empty().bit_is_set() { - result |= DmaRxInterrupt::DescriptorEmpty; - } - if int_raw.in_suc_eof().bit_is_set() { - result |= DmaRxInterrupt::SuccessfulEof; - } - if int_raw.in_err_eof().bit_is_set() { - result |= DmaRxInterrupt::ErrorEof; - } - if int_raw.in_done().bit_is_set() { - result |= DmaRxInterrupt::Done; - } - - result - } - - fn clear_in(interrupts: impl Into>) { - let spi = unsafe { &*crate::peripherals::[]::PTR }; - spi.dma_int_clr().modify(|_, w| { - for interrupt in interrupts.into() { - match interrupt { - DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().clear_bit_by_one(), - DmaRxInterrupt::ErrorEof => w.in_err_eof().clear_bit_by_one(), - DmaRxInterrupt::DescriptorError => w.inlink_dscr_error().clear_bit_by_one(), - DmaRxInterrupt::DescriptorEmpty => w.inlink_dscr_empty().clear_bit_by_one(), - DmaRxInterrupt::Done => w.in_done().clear_bit_by_one(), - }; - } - w - }) - } - } - - #[non_exhaustive] - #[doc(hidden)] - pub struct [] {} - - impl $crate::private::Sealed for [] {} - - impl<'a> TxChannel<[]> for [] { - fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { - static WAKER: embassy_sync::waitqueue::AtomicWaker = embassy_sync::waitqueue::AtomicWaker::new(); - &WAKER - } - } - - #[non_exhaustive] - #[doc(hidden)] - pub struct [] {} - - impl $crate::private::Sealed for [] {} - - impl<'a> RxChannel<[]> for [] { - fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { - static WAKER: embassy_sync::waitqueue::AtomicWaker = embassy_sync::waitqueue::AtomicWaker::new(); - &WAKER - } - } - #[doc = concat!("Creates a channel for SPI", $num)] #[non_exhaustive] pub struct [] {} @@ -362,11 +424,13 @@ macro_rules! ImplSpiChannel { .modify(|_, w| unsafe { w.[< spi $num _dma_chan_sel>]().bits($num - 1) }); } - let mut tx_impl = [] {}; - tx_impl.init(burst_mode, priority); + let tx_impl = SpiDmaTxChannelImpl(PhantomData); + tx_impl.set_burst_mode(burst_mode); + tx_impl.set_priority(priority); - let mut rx_impl = [] {}; - rx_impl.init(burst_mode, priority); + let rx_impl = SpiDmaRxChannelImpl(PhantomData); + rx_impl.set_burst_mode(burst_mode); + rx_impl.set_priority(priority); Channel { tx: ChannelTx::new(tx_impl, burst_mode), @@ -407,8 +471,352 @@ macro_rules! ImplSpiChannel { }; } +#[doc(hidden)] +pub struct I2sDmaTxChannelImpl(PhantomData); +#[doc(hidden)] +pub struct I2sDmaRxChannelImpl(PhantomData); + +impl crate::private::Sealed for I2sDmaTxChannelImpl {} +impl crate::private::Sealed for I2sDmaRxChannelImpl {} + +impl> RegisterAccess for I2sDmaTxChannelImpl { + fn set_burst_mode(&self, burst_mode: bool) { + let reg_block = C::register_block(); + reg_block + .lc_conf() + .modify(|_, w| w.outdscr_burst_en().bit(burst_mode)); + } + + fn set_priority(&self, _priority: DmaPriority) {} + + fn clear_interrupts(&self) { + let reg_block = C::register_block(); + reg_block.int_clr().write(|w| { + w.out_done() + .clear_bit_by_one() + .out_eof() + .clear_bit_by_one() + .out_total_eof() + .clear_bit_by_one() + .out_dscr_err() + .clear_bit_by_one() + }); + } + + fn reset(&self) { + let reg_block = C::register_block(); + reg_block.lc_conf().modify(|_, w| w.out_rst().set_bit()); + reg_block.lc_conf().modify(|_, w| w.out_rst().clear_bit()); + } + + fn set_link_addr(&self, address: u32) { + let reg_block = C::register_block(); + reg_block + .out_link() + .modify(|_, w| unsafe { w.outlink_addr().bits(address) }); + } + + fn set_peripheral(&self, _peripheral: u8) { + // no-op + } + + fn start(&self) { + let reg_block = C::register_block(); + reg_block + .out_link() + .modify(|_, w| w.outlink_start().set_bit()); + } + + fn stop(&self) { + let reg_block = C::register_block(); + reg_block + .out_link() + .modify(|_, w| w.outlink_stop().set_bit()); + } + + fn restart(&self) { + let reg_block = C::register_block(); + reg_block + .out_link() + .modify(|_, w| w.outlink_restart().set_bit()); + } +} + +impl> TxRegisterAccess for I2sDmaTxChannelImpl { + fn last_dscr_address(&self) -> usize { + let reg_block = C::register_block(); + reg_block + .out_eof_des_addr() + .read() + .out_eof_des_addr() + .bits() as usize + } +} + +impl> InterruptAccess + for I2sDmaTxChannelImpl +{ + fn listen(&self, interrupts: impl Into>) { + let reg_block = C::register_block(); + reg_block.int_ena().modify(|_, w| { + for interrupt in interrupts.into() { + match interrupt { + DmaTxInterrupt::TotalEof => w.out_total_eof().set_bit(), + DmaTxInterrupt::DescriptorError => w.out_dscr_err().set_bit(), + DmaTxInterrupt::Eof => w.out_eof().set_bit(), + DmaTxInterrupt::Done => w.out_done().set_bit(), + }; + } + w + }) + } + + fn unlisten(&self, interrupts: impl Into>) { + let reg_block = C::register_block(); + reg_block.int_ena().modify(|_, w| { + for interrupt in interrupts.into() { + match interrupt { + DmaTxInterrupt::TotalEof => w.out_total_eof().clear_bit(), + DmaTxInterrupt::DescriptorError => w.out_dscr_err().clear_bit(), + DmaTxInterrupt::Eof => w.out_eof().clear_bit(), + DmaTxInterrupt::Done => w.out_done().clear_bit(), + }; + } + w + }) + } + + fn is_listening(&self) -> EnumSet { + let mut result = EnumSet::new(); + + let reg_block = C::register_block(); + let int_ena = reg_block.int_ena().read(); + if int_ena.out_total_eof().bit_is_set() { + result |= DmaTxInterrupt::TotalEof; + } + if int_ena.out_dscr_err().bit_is_set() { + result |= DmaTxInterrupt::DescriptorError; + } + if int_ena.out_eof().bit_is_set() { + result |= DmaTxInterrupt::Eof; + } + if int_ena.out_done().bit_is_set() { + result |= DmaTxInterrupt::Done; + } + + result + } + + fn pending_interrupts(&self) -> EnumSet { + let mut result = EnumSet::new(); + + let reg_block = C::register_block(); + let int_raw = reg_block.int_raw().read(); + if int_raw.out_total_eof().bit_is_set() { + result |= DmaTxInterrupt::TotalEof; + } + if int_raw.out_dscr_err().bit_is_set() { + result |= DmaTxInterrupt::DescriptorError; + } + if int_raw.out_eof().bit_is_set() { + result |= DmaTxInterrupt::Eof; + } + if int_raw.out_done().bit_is_set() { + result |= DmaTxInterrupt::Done; + } + + result + } + + fn clear(&self, interrupts: impl Into>) { + let reg_block = C::register_block(); + reg_block.int_clr().write(|w| { + for interrupt in interrupts.into() { + match interrupt { + DmaTxInterrupt::TotalEof => w.out_total_eof().clear_bit_by_one(), + DmaTxInterrupt::DescriptorError => w.out_dscr_err().clear_bit_by_one(), + DmaTxInterrupt::Eof => w.out_eof().clear_bit_by_one(), + DmaTxInterrupt::Done => w.out_done().clear_bit_by_one(), + }; + } + w + }) + } + + fn waker(&self) -> &'static AtomicWaker { + C::tx_waker() + } +} + +impl> RegisterAccess for I2sDmaRxChannelImpl { + fn set_burst_mode(&self, burst_mode: bool) { + let reg_block = C::register_block(); + reg_block + .lc_conf() + .modify(|_, w| w.indscr_burst_en().bit(burst_mode)); + } + + fn set_priority(&self, _priority: DmaPriority) {} + + fn clear_interrupts(&self) { + let reg_block = C::register_block(); + reg_block.int_clr().write(|w| { + w.in_done() + .clear_bit_by_one() + .in_err_eof() + .clear_bit_by_one() + .in_suc_eof() + .clear_bit_by_one() + .in_dscr_err() + .clear_bit_by_one() + }); + } + + fn reset(&self) { + let reg_block = C::register_block(); + reg_block.lc_conf().modify(|_, w| w.in_rst().set_bit()); + reg_block.lc_conf().modify(|_, w| w.in_rst().clear_bit()); + } + + fn set_link_addr(&self, address: u32) { + let reg_block = C::register_block(); + reg_block + .in_link() + .modify(|_, w| unsafe { w.inlink_addr().bits(address) }); + } + + fn set_peripheral(&self, _peripheral: u8) { + // no-op + } + + fn start(&self) { + let reg_block = C::register_block(); + reg_block + .in_link() + .modify(|_, w| w.inlink_start().set_bit()); + } + + fn stop(&self) { + let reg_block = C::register_block(); + reg_block.in_link().modify(|_, w| w.inlink_stop().set_bit()); + } + + fn restart(&self) { + let reg_block = C::register_block(); + reg_block + .in_link() + .modify(|_, w| w.inlink_restart().set_bit()); + } +} + +impl> RxRegisterAccess for I2sDmaRxChannelImpl {} + +impl> InterruptAccess + for I2sDmaRxChannelImpl +{ + fn listen(&self, interrupts: impl Into>) { + let reg_block = C::register_block(); + reg_block.int_ena().modify(|_, w| { + for interrupt in interrupts.into() { + match interrupt { + DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().set_bit(), + DmaRxInterrupt::ErrorEof => w.in_err_eof().set_bit(), + DmaRxInterrupt::DescriptorError => w.in_dscr_err().set_bit(), + DmaRxInterrupt::DescriptorEmpty => w.in_dscr_empty().set_bit(), + DmaRxInterrupt::Done => w.in_done().set_bit(), + }; + } + w + }) + } + + fn unlisten(&self, interrupts: impl Into>) { + let reg_block = C::register_block(); + reg_block.int_ena().modify(|_, w| { + for interrupt in interrupts.into() { + match interrupt { + DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().clear_bit(), + DmaRxInterrupt::ErrorEof => w.in_err_eof().clear_bit(), + DmaRxInterrupt::DescriptorError => w.in_dscr_err().clear_bit(), + DmaRxInterrupt::DescriptorEmpty => w.in_dscr_empty().clear_bit(), + DmaRxInterrupt::Done => w.in_done().clear_bit(), + }; + } + w + }) + } + + fn is_listening(&self) -> EnumSet { + let mut result = EnumSet::new(); + + let reg_block = C::register_block(); + let int_ena = reg_block.int_ena().read(); + if int_ena.in_dscr_err().bit_is_set() { + result |= DmaRxInterrupt::DescriptorError; + } + if int_ena.in_dscr_empty().bit_is_set() { + result |= DmaRxInterrupt::DescriptorEmpty; + } + if int_ena.in_suc_eof().bit_is_set() { + result |= DmaRxInterrupt::SuccessfulEof; + } + if int_ena.in_err_eof().bit_is_set() { + result |= DmaRxInterrupt::ErrorEof; + } + if int_ena.in_done().bit_is_set() { + result |= DmaRxInterrupt::Done; + } + + result + } + + fn pending_interrupts(&self) -> EnumSet { + let mut result = EnumSet::new(); + + let reg_block = C::register_block(); + let int_raw = reg_block.int_raw().read(); + if int_raw.in_dscr_err().bit_is_set() { + result |= DmaRxInterrupt::DescriptorError; + } + if int_raw.in_dscr_empty().bit_is_set() { + result |= DmaRxInterrupt::DescriptorEmpty; + } + if int_raw.in_suc_eof().bit_is_set() { + result |= DmaRxInterrupt::SuccessfulEof; + } + if int_raw.in_err_eof().bit_is_set() { + result |= DmaRxInterrupt::ErrorEof; + } + if int_raw.in_done().bit_is_set() { + result |= DmaRxInterrupt::Done; + } + + result + } + + fn clear(&self, interrupts: impl Into>) { + let reg_block = C::register_block(); + reg_block.int_clr().write(|w| { + for interrupt in interrupts.into() { + match interrupt { + DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().clear_bit_by_one(), + DmaRxInterrupt::ErrorEof => w.in_err_eof().clear_bit_by_one(), + DmaRxInterrupt::DescriptorError => w.in_dscr_err().clear_bit_by_one(), + DmaRxInterrupt::DescriptorEmpty => w.in_dscr_empty().clear_bit_by_one(), + DmaRxInterrupt::Done => w.in_done().clear_bit_by_one(), + }; + } + w + }) + } + + fn waker(&self) -> &'static AtomicWaker { + C::rx_waker() + } +} + macro_rules! ImplI2sChannel { - ($num: literal, $peripheral: literal) => { + ($num: literal) => { paste::paste! { #[doc = concat!("DMA channel suitable for I2S", $num)] pub struct [] {} @@ -416,323 +824,39 @@ macro_rules! ImplI2sChannel { impl $crate::private::Sealed for [] {} impl DmaChannel for [] { - type Channel = []; - type Rx = []; - type Tx = []; + type Rx = I2sDmaRxChannelImpl; + type Tx = I2sDmaTxChannelImpl; type P = []; + } - fn set_isr(handler: $crate::interrupt::InterruptHandler) { - let mut i2s = unsafe { $crate::peripherals::[< I2S $num >]::steal() }; - i2s.[< bind_i2s $num _interrupt>](handler.handler()); - $crate::interrupt::enable($crate::peripherals::Interrupt::[< I2S $num >], handler.priority()).unwrap(); + impl DmaChannelExt for [] { + fn get_rx_interrupts() -> impl InterruptAccess { + I2sDmaRxChannelImpl::(PhantomData) + } + fn get_tx_interrupts() -> impl InterruptAccess { + I2sDmaTxChannelImpl::(PhantomData) + } + + fn set_isr(handler: InterruptHandler) { + let interrupt = $crate::peripherals::Interrupt::[< I2S $num >]; + unsafe { + crate::interrupt::bind_interrupt(interrupt, handler.handler()); + } + crate::interrupt::enable(interrupt, handler.priority()).unwrap(); } } - impl RegisterAccess for [] { - fn set_out_burstmode(burst_mode: bool) { - let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; - reg_block.lc_conf() - .modify(|_, w| w.outdscr_burst_en().bit(burst_mode)); + impl PdmaChannel for [] { + type RegisterBlock = I2sRegisterBlock; + + fn register_block() -> &'static I2sRegisterBlock { + unsafe { &*crate::peripherals::[< I2S $num >]::PTR } } - - fn set_out_priority(_priority: DmaPriority) {} - - fn clear_out_interrupts() { - let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; - reg_block.int_clr().write(|w| { - w.out_done() - .clear_bit_by_one() - .out_eof() - .clear_bit_by_one() - .out_total_eof() - .clear_bit_by_one() - .out_dscr_err() - .clear_bit_by_one() - }); - } - - fn reset_out() { - let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; - reg_block.lc_conf().modify(|_, w| w.out_rst().set_bit()); - reg_block.lc_conf().modify(|_, w| w.out_rst().clear_bit()); - } - - fn set_out_descriptors(address: u32) { - let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; - reg_block.out_link() - .modify(|_, w| unsafe { w.outlink_addr().bits(address) }); - } - - fn set_out_peripheral(_peripheral: u8) { - // no-op - } - - fn start_out() { - let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; - reg_block.out_link().modify(|_, w| w.outlink_start().set_bit()); - } - - fn stop_out() { - let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; - reg_block.out_link().modify(|_, w| w.outlink_stop().set_bit()); - } - - fn last_out_dscr_address() -> usize { - let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; - reg_block.out_eof_des_addr().read().out_eof_des_addr().bits() as usize - } - - fn set_in_burstmode(burst_mode: bool) { - let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; - reg_block.lc_conf() - .modify(|_, w| w.indscr_burst_en().bit(burst_mode)); - } - - fn set_in_priority(_priority: DmaPriority) {} - - fn clear_in_interrupts() { - let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; - reg_block.int_clr().write(|w| { - w.in_done() - .clear_bit_by_one() - .in_err_eof() - .clear_bit_by_one() - .in_suc_eof() - .clear_bit_by_one() - .in_dscr_err() - .clear_bit_by_one() - }); - } - - fn reset_in() { - let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; - reg_block.lc_conf().modify(|_, w| w.in_rst().set_bit()); - reg_block.lc_conf().modify(|_, w| w.in_rst().clear_bit()); - } - - fn set_in_descriptors(address: u32) { - let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; - reg_block.in_link() - .modify(|_, w| unsafe { w.inlink_addr().bits(address) }); - } - - fn set_in_peripheral(_peripheral: u8) { - // no-op - } - - fn start_in() { - let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; - reg_block.in_link().modify(|_, w| w.inlink_start().set_bit()); - } - - fn stop_in() { - let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; - reg_block.in_link().modify(|_, w| w.inlink_stop().set_bit()); - } - - fn listen_out(interrupts: impl Into>) { - let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; - reg_block.int_ena().modify(|_, w| { - for interrupt in interrupts.into() { - match interrupt { - DmaTxInterrupt::TotalEof => w.out_total_eof().set_bit(), - DmaTxInterrupt::DescriptorError => w.out_dscr_err().set_bit(), - DmaTxInterrupt::Eof => w.out_eof().set_bit(), - DmaTxInterrupt::Done => w.out_done().set_bit(), - }; - } - w - }) - } - - fn unlisten_out(interrupts: impl Into>) { - let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; - reg_block.int_ena().modify(|_, w| { - for interrupt in interrupts.into() { - match interrupt { - DmaTxInterrupt::TotalEof => w.out_total_eof().clear_bit(), - DmaTxInterrupt::DescriptorError => w.out_dscr_err().clear_bit(), - DmaTxInterrupt::Eof => w.out_eof().clear_bit(), - DmaTxInterrupt::Done => w.out_done().clear_bit(), - }; - } - w - }) - } - - fn is_listening_out() -> EnumSet { - let mut result = EnumSet::new(); - - let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; - let int_ena = reg_block.int_ena().read(); - if int_ena.out_total_eof().bit_is_set() { - result |= DmaTxInterrupt::TotalEof; - } - if int_ena.out_dscr_err().bit_is_set() { - result |= DmaTxInterrupt::DescriptorError; - } - if int_ena.out_eof().bit_is_set() { - result |= DmaTxInterrupt::Eof; - } - if int_ena.out_done().bit_is_set() { - result |= DmaTxInterrupt::Done; - } - - result - } - - fn pending_out_interrupts() -> EnumSet { - let mut result = EnumSet::new(); - - let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; - let int_raw = reg_block.int_raw().read(); - if int_raw.out_total_eof().bit_is_set() { - result |= DmaTxInterrupt::TotalEof; - } - if int_raw.out_dscr_err().bit_is_set() { - result |= DmaTxInterrupt::DescriptorError; - } - if int_raw.out_eof().bit_is_set() { - result |= DmaTxInterrupt::Eof; - } - if int_raw.out_done().bit_is_set() { - result |= DmaTxInterrupt::Done; - } - - result - } - - fn clear_out(interrupts: impl Into>) { - let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; - reg_block.int_clr().write(|w| { - for interrupt in interrupts.into() { - match interrupt { - DmaTxInterrupt::TotalEof => w.out_total_eof().clear_bit_by_one(), - DmaTxInterrupt::DescriptorError => w.out_dscr_err().clear_bit_by_one(), - DmaTxInterrupt::Eof => w.out_eof().clear_bit_by_one(), - DmaTxInterrupt::Done => w.out_done().clear_bit_by_one(), - }; - } - w - }) - } - - fn listen_in(interrupts: impl Into>) { - let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; - reg_block.int_ena().modify(|_, w| { - for interrupt in interrupts.into() { - match interrupt { - DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().set_bit(), - DmaRxInterrupt::ErrorEof => w.in_err_eof().set_bit(), - DmaRxInterrupt::DescriptorError => w.in_dscr_err().set_bit(), - DmaRxInterrupt::DescriptorEmpty => w.in_dscr_empty().set_bit(), - DmaRxInterrupt::Done => w.in_done().set_bit(), - }; - } - w - }) - } - - fn unlisten_in(interrupts: impl Into>) { - let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; - reg_block.int_ena().modify(|_, w| { - for interrupt in interrupts.into() { - match interrupt { - DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().clear_bit(), - DmaRxInterrupt::ErrorEof => w.in_err_eof().clear_bit(), - DmaRxInterrupt::DescriptorError => w.in_dscr_err().clear_bit(), - DmaRxInterrupt::DescriptorEmpty => w.in_dscr_empty().clear_bit(), - DmaRxInterrupt::Done => w.in_done().clear_bit(), - }; - } - w - }) - } - - fn is_listening_in() -> EnumSet { - let mut result = EnumSet::new(); - - let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; - let int_ena = reg_block.int_ena().read(); - if int_ena.in_dscr_err().bit_is_set() { - result |= DmaRxInterrupt::DescriptorError; - } - if int_ena.in_dscr_empty().bit_is_set() { - result |= DmaRxInterrupt::DescriptorEmpty; - } - if int_ena.in_suc_eof().bit_is_set() { - result |= DmaRxInterrupt::SuccessfulEof; - } - if int_ena.in_err_eof().bit_is_set() { - result |= DmaRxInterrupt::ErrorEof; - } - if int_ena.in_done().bit_is_set() { - result |= DmaRxInterrupt::Done; - } - - result - } - - fn pending_in_interrupts() -> EnumSet { - let mut result = EnumSet::new(); - - let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; - let int_raw = reg_block.int_raw().read(); - if int_raw.in_dscr_err().bit_is_set() { - result |= DmaRxInterrupt::DescriptorError; - } - if int_raw.in_dscr_empty().bit_is_set() { - result |= DmaRxInterrupt::DescriptorEmpty; - } - if int_raw.in_suc_eof().bit_is_set() { - result |= DmaRxInterrupt::SuccessfulEof; - } - if int_raw.in_err_eof().bit_is_set() { - result |= DmaRxInterrupt::ErrorEof; - } - if int_raw.in_done().bit_is_set() { - result |= DmaRxInterrupt::Done; - } - - result - } - - fn clear_in(interrupts: impl Into>) { - let reg_block = unsafe { &*crate::peripherals::[<$peripheral>]::PTR }; - reg_block.int_clr().write(|w| { - for interrupt in interrupts.into() { - match interrupt { - DmaRxInterrupt::SuccessfulEof => w.in_suc_eof().clear_bit_by_one(), - DmaRxInterrupt::ErrorEof => w.in_err_eof().clear_bit_by_one(), - DmaRxInterrupt::DescriptorError => w.in_dscr_err().clear_bit_by_one(), - DmaRxInterrupt::DescriptorEmpty => w.in_dscr_empty().clear_bit_by_one(), - DmaRxInterrupt::Done => w.in_done().clear_bit_by_one(), - }; - } - w - }) - } - } - - #[doc(hidden)] - pub struct [] {} - - impl $crate::private::Sealed for [] {} - - impl<'a> TxChannel<[]> for [] { - fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { + fn tx_waker() -> &'static embassy_sync::waitqueue::AtomicWaker { static WAKER: embassy_sync::waitqueue::AtomicWaker = embassy_sync::waitqueue::AtomicWaker::new(); &WAKER } - } - - #[doc(hidden)] - pub struct [] {} - - impl $crate::private::Sealed for [] {} - - impl<'a> RxChannel<[]> for [] { - fn waker() -> &'static embassy_sync::waitqueue::AtomicWaker { + fn rx_waker() -> &'static embassy_sync::waitqueue::AtomicWaker { static WAKER: embassy_sync::waitqueue::AtomicWaker = embassy_sync::waitqueue::AtomicWaker::new(); &WAKER } @@ -747,11 +871,13 @@ macro_rules! ImplI2sChannel { burst_mode: bool, priority: DmaPriority, ) -> Channel<'a, [], M> { - let mut tx_impl = [] {}; - tx_impl.init(burst_mode, priority); + let tx_impl = I2sDmaTxChannelImpl(PhantomData); + tx_impl.set_burst_mode(burst_mode); + tx_impl.set_priority(priority); - let mut rx_impl = [] {}; - rx_impl.init(burst_mode, priority); + let rx_impl = I2sDmaRxChannelImpl(PhantomData); + rx_impl.set_burst_mode(burst_mode); + rx_impl.set_priority(priority); Channel { tx: ChannelTx::new(tx_impl, burst_mode), @@ -816,7 +942,7 @@ impl PeripheralMarker for I2s0DmaSuitablePeripheral {} impl I2sPeripheral for I2s0DmaSuitablePeripheral {} impl I2s0Peripheral for I2s0DmaSuitablePeripheral {} -ImplI2sChannel!(0, "I2S0"); +ImplI2sChannel!(0); #[doc(hidden)] #[non_exhaustive] @@ -826,7 +952,7 @@ impl I2sPeripheral for I2s1DmaSuitablePeripheral {} impl I2s1Peripheral for I2s1DmaSuitablePeripheral {} #[cfg(i2s1)] -ImplI2sChannel!(1, "I2S1"); +ImplI2sChannel!(1); /// DMA Peripheral ///