diff --git a/esp-hal/src/spi/slave.rs b/esp-hal/src/spi/slave.rs index 675d66b2d3..3921532456 100644 --- a/esp-hal/src/spi/slave.rs +++ b/esp-hal/src/spi/slave.rs @@ -75,7 +75,7 @@ use core::marker::PhantomData; use super::{Error, SpiMode}; use crate::{ - dma::{DescriptorChain, DmaChannelConvert, DmaEligible, PeripheralMarker, Rx, Tx}, + dma::{DmaChannelConvert, DmaEligible, PeripheralMarker}, gpio::{ interconnect::{PeripheralInput, PeripheralOutput}, InputSignal, @@ -144,16 +144,16 @@ where // TODO: with_pins et. al. sclk.enable_input(true, private::Internal); - this.spi.sclk_signal().connect_to(sclk); + this.spi.info().sclk.connect_to(sclk); mosi.enable_input(true, private::Internal); - this.spi.mosi_signal().connect_to(mosi); + this.spi.info().mosi.connect_to(mosi); miso.set_to_push_pull_output(private::Internal); - this.spi.miso_signal().connect_to(miso); + this.spi.info().miso.connect_to(miso); cs.enable_input(true, private::Internal); - this.spi.cs_signal().connect_to(cs); + this.spi.info().cs.connect_to(cs); this } @@ -161,7 +161,7 @@ where pub(crate) fn new_internal(spi: impl Peripheral

+ 'd, mode: SpiMode) -> Spi<'d, M, T> { crate::into_ref!(spi); - let mut spi = Spi { + let spi = Spi { spi, data_mode: mode, _mode: PhantomData, @@ -170,8 +170,8 @@ where PeripheralClockControl::reset(spi.spi.peripheral()); PeripheralClockControl::enable(spi.spi.peripheral()); - spi.spi.init(); - spi.spi.set_data_mode(mode, false); + spi.spi.info().init(); + spi.spi.info().set_data_mode(mode, false); spi } @@ -208,7 +208,7 @@ pub mod dma { /// descriptors. #[cfg_attr(esp32, doc = "\n\n**Note**: ESP32 only supports Mode 1 and 3.")] pub fn with_dma( - mut self, + self, channel: Channel<'d, CH, DM>, rx_descriptors: &'static mut [DmaDescriptor], tx_descriptors: &'static mut [DmaDescriptor], @@ -218,7 +218,7 @@ pub mod dma { DM: Mode, Channel<'d, CH, M>: From>, { - self.spi.set_data_mode(self.data_mode, true); + self.spi.info().set_data_mode(self.data_mode, true); SpiDma::new(self.spi, channel.into(), rx_descriptors, tx_descriptors) } } @@ -253,10 +253,10 @@ pub mod dma { fn peripheral_wait_dma(&mut self, is_rx: bool, is_tx: bool) { while !((!is_tx || self.channel.tx.is_done()) && (!is_rx || self.channel.rx.is_done()) - && !self.spi.is_bus_busy()) + && !self.spi.info().is_bus_busy()) {} - self.spi.flush().ok(); + self.spi.info().flush().ok(); } fn peripheral_dma_stop(&mut self) { @@ -318,6 +318,14 @@ pub mod dma { tx_chain: DescriptorChain::new(tx_descriptors), } } + + fn driver(&self) -> DmaDriver { + DmaDriver { + info: self.spi.info(), + dma_peripheral: self.spi.dma_peripheral(), + } + } + /// Register a buffer for a DMA write. /// /// This will return a [DmaTransferTx]. The maximum amount of data to be @@ -338,7 +346,7 @@ pub mod dma { } unsafe { - self.spi + self.driver() .start_transfer_dma( &mut self.rx_chain, &mut self.tx_chain, @@ -373,7 +381,7 @@ pub mod dma { } unsafe { - self.spi + self.driver() .start_transfer_dma( &mut self.rx_chain, &mut self.tx_chain, @@ -412,7 +420,7 @@ pub mod dma { } unsafe { - self.spi + self.driver() .start_transfer_dma( &mut self.rx_chain, &mut self.tx_chain, @@ -427,163 +435,197 @@ pub mod dma { } } } -} -#[doc(hidden)] -pub trait InstanceDma: Instance + DmaEligible { - #[allow(clippy::too_many_arguments)] - unsafe fn start_transfer_dma( - &mut self, - rx_chain: &mut DescriptorChain, - tx_chain: &mut DescriptorChain, - read_buffer_ptr: *mut u8, - read_buffer_len: usize, - write_buffer_ptr: *const u8, - write_buffer_len: usize, - rx: &mut RX, - tx: &mut TX, - ) -> Result<(), Error> - where - RX: Rx, - TX: Tx, - { - let reg_block = self.register_block(); + struct DmaDriver { + info: &'static Info, + dma_peripheral: crate::dma::DmaPeripheral, + } - self.enable_dma(); + impl DmaDriver { + #[allow(clippy::too_many_arguments)] + unsafe fn start_transfer_dma( + &self, + rx_chain: &mut DescriptorChain, + tx_chain: &mut DescriptorChain, + read_buffer_ptr: *mut u8, + read_buffer_len: usize, + write_buffer_ptr: *const u8, + write_buffer_len: usize, + rx: &mut RX, + tx: &mut TX, + ) -> Result<(), Error> + where + RX: Rx, + TX: Tx, + { + self.enable_dma(); - reset_spi(reg_block); + self.info.reset_spi(); - if read_buffer_len > 0 { - rx_chain.fill_for_rx(false, read_buffer_ptr, read_buffer_len)?; - rx.prepare_transfer_without_start(self.dma_peripheral(), rx_chain)?; - } + if read_buffer_len > 0 { + rx_chain.fill_for_rx(false, read_buffer_ptr, read_buffer_len)?; + rx.prepare_transfer_without_start(self.dma_peripheral, rx_chain)?; + } - if write_buffer_len > 0 { - tx_chain.fill_for_tx(false, write_buffer_ptr, write_buffer_len)?; - tx.prepare_transfer_without_start(self.dma_peripheral(), tx_chain)?; - } + if write_buffer_len > 0 { + tx_chain.fill_for_tx(false, write_buffer_ptr, write_buffer_len)?; + tx.prepare_transfer_without_start(self.dma_peripheral, tx_chain)?; + } - #[cfg(esp32)] - self.prepare_length_and_lines(read_buffer_len, write_buffer_len); + #[cfg(esp32)] + self.info + .prepare_length_and_lines(read_buffer_len, write_buffer_len); - reset_dma_before_usr_cmd(reg_block); + self.reset_dma_before_usr_cmd(); - #[cfg(not(esp32))] - reg_block - .dma_conf() - .modify(|_, w| w.dma_slv_seg_trans_en().clear_bit()); + let reg_block = self.info.register_block(); + #[cfg(not(esp32))] + reg_block + .dma_conf() + .modify(|_, w| w.dma_slv_seg_trans_en().clear_bit()); - self.clear_dma_interrupts(); - self.setup_for_flush(); - reg_block.cmd().modify(|_, w| w.usr().set_bit()); + self.clear_dma_interrupts(); + self.info.setup_for_flush(); + reg_block.cmd().modify(|_, w| w.usr().set_bit()); - if read_buffer_len > 0 { - rx.start_transfer()?; - } + if read_buffer_len > 0 { + rx.start_transfer()?; + } - if write_buffer_len > 0 { - tx.start_transfer()?; - } + if write_buffer_len > 0 { + tx.start_transfer()?; + } - Ok(()) - } + Ok(()) + } - fn enable_dma(&self) { - let reg_block = self.register_block(); - #[cfg(gdma)] - { + fn reset_dma_before_usr_cmd(&self) { + let reg_block = self.info.register_block(); + #[cfg(gdma)] reg_block.dma_conf().modify(|_, w| { - w.dma_tx_ena().set_bit(); - w.dma_rx_ena().set_bit(); - w.rx_eof_en().clear_bit() + w.rx_afifo_rst().set_bit(); + w.buf_afifo_rst().set_bit(); + w.dma_afifo_rst().set_bit() }); + + #[cfg(pdma)] + let _ = reg_block; } - #[cfg(pdma)] - { - fn set_rst_bit(reg_block: &RegisterBlock, bit: bool) { + fn enable_dma(&self) { + let reg_block = self.info.register_block(); + #[cfg(gdma)] + { reg_block.dma_conf().modify(|_, w| { - w.in_rst().bit(bit); - w.out_rst().bit(bit); - w.ahbm_fifo_rst().bit(bit); - w.ahbm_rst().bit(bit) + w.dma_tx_ena().set_bit(); + w.dma_rx_ena().set_bit(); + w.rx_eof_en().clear_bit() }); + } - #[cfg(esp32s2)] - reg_block - .dma_conf() - .modify(|_, w| w.dma_infifo_full_clr().bit(bit)); + #[cfg(pdma)] + { + fn set_rst_bit(reg_block: &RegisterBlock, bit: bool) { + reg_block.dma_conf().modify(|_, w| { + w.in_rst().bit(bit); + w.out_rst().bit(bit); + w.ahbm_fifo_rst().bit(bit); + w.ahbm_rst().bit(bit) + }); + + #[cfg(esp32s2)] + reg_block + .dma_conf() + .modify(|_, w| w.dma_infifo_full_clr().bit(bit)); + } + set_rst_bit(reg_block, true); + set_rst_bit(reg_block, false); } - set_rst_bit(reg_block, true); - set_rst_bit(reg_block, false); } - } - fn clear_dma_interrupts(&self) { - let reg_block = self.register_block(); + fn clear_dma_interrupts(&self) { + let reg_block = self.info.register_block(); - #[cfg(gdma)] - reg_block.dma_int_clr().write(|w| { - w.dma_infifo_full_err().clear_bit_by_one(); - w.dma_outfifo_empty_err().clear_bit_by_one(); - w.trans_done().clear_bit_by_one(); - w.mst_rx_afifo_wfull_err().clear_bit_by_one(); - w.mst_tx_afifo_rempty_err().clear_bit_by_one() - }); + #[cfg(gdma)] + reg_block.dma_int_clr().write(|w| { + w.dma_infifo_full_err().clear_bit_by_one(); + w.dma_outfifo_empty_err().clear_bit_by_one(); + w.trans_done().clear_bit_by_one(); + w.mst_rx_afifo_wfull_err().clear_bit_by_one(); + w.mst_tx_afifo_rempty_err().clear_bit_by_one() + }); - #[cfg(pdma)] - reg_block.dma_int_clr().write(|w| { - w.inlink_dscr_empty().clear_bit_by_one(); - w.outlink_dscr_error().clear_bit_by_one(); - w.inlink_dscr_error().clear_bit_by_one(); - w.in_done().clear_bit_by_one(); - w.in_err_eof().clear_bit_by_one(); - w.in_suc_eof().clear_bit_by_one(); - w.out_done().clear_bit_by_one(); - w.out_eof().clear_bit_by_one(); - w.out_total_eof().clear_bit_by_one() - }); + #[cfg(pdma)] + reg_block.dma_int_clr().write(|w| { + w.inlink_dscr_empty().clear_bit_by_one(); + w.outlink_dscr_error().clear_bit_by_one(); + w.inlink_dscr_error().clear_bit_by_one(); + w.in_done().clear_bit_by_one(); + w.in_err_eof().clear_bit_by_one(); + w.in_suc_eof().clear_bit_by_one(); + w.out_done().clear_bit_by_one(); + w.out_eof().clear_bit_by_one(); + w.out_total_eof().clear_bit_by_one() + }); + } } } -fn reset_spi(reg_block: &RegisterBlock) { - #[cfg(esp32)] - { - reg_block.slave().modify(|_, w| w.sync_reset().set_bit()); - reg_block.slave().modify(|_, w| w.sync_reset().clear_bit()); - } - - #[cfg(not(esp32))] - { - reg_block.slave().modify(|_, w| w.soft_reset().set_bit()); - reg_block.slave().modify(|_, w| w.soft_reset().clear_bit()); - } +/// SPI peripheral instance. +pub trait Instance: Peripheral

+ Into + PeripheralMarker + 'static { + /// Returns the peripheral data describing this SPI instance. + fn info(&self) -> &'static Info; } -fn reset_dma_before_usr_cmd(reg_block: &RegisterBlock) { - #[cfg(gdma)] - reg_block.dma_conf().modify(|_, w| { - w.rx_afifo_rst().set_bit(); - w.buf_afifo_rst().set_bit(); - w.dma_afifo_rst().set_bit() - }); - - #[cfg(pdma)] - let _ = reg_block; -} +/// A marker for DMA-capable SPI peripheral instances. +pub trait InstanceDma: Instance + DmaEligible {} impl InstanceDma for crate::peripherals::SPI2 {} #[cfg(spi3)] impl InstanceDma for crate::peripherals::SPI3 {} -#[doc(hidden)] -pub trait Instance: Peripheral

+ Into + PeripheralMarker + 'static { - fn register_block(&self) -> &RegisterBlock; +/// Peripheral data describing a particular SPI instance. +#[non_exhaustive] +pub struct Info { + /// Pointer to the register block for this SPI instance. + /// + /// Use [Self::register_block] to access the register block. + pub register_block: *const RegisterBlock, + + /// SCLK signal. + pub sclk: InputSignal, + + /// MOSI signal. + pub mosi: InputSignal, + + /// MISO signal. + pub miso: OutputSignal, + + /// Chip select signal. + pub cs: InputSignal, +} + +impl Info { + /// Returns the register block for this SPI instance. + pub fn register_block(&self) -> &RegisterBlock { + unsafe { &*self.register_block } + } + + fn reset_spi(&self) { + let reg_block = self.register_block(); + + #[cfg(esp32)] + { + reg_block.slave().modify(|_, w| w.sync_reset().set_bit()); + reg_block.slave().modify(|_, w| w.sync_reset().clear_bit()); + } - fn sclk_signal(&self) -> InputSignal; - fn mosi_signal(&self) -> InputSignal; - fn miso_signal(&self) -> OutputSignal; - fn cs_signal(&self) -> InputSignal; + #[cfg(not(esp32))] + { + reg_block.slave().modify(|_, w| w.soft_reset().set_bit()); + reg_block.slave().modify(|_, w| w.soft_reset().clear_bit()); + } + } #[cfg(esp32)] fn prepare_length_and_lines(&self, rx_len: usize, tx_len: usize) { @@ -604,7 +646,7 @@ pub trait Instance: Peripheral

+ Into + PeripheralMarker + 'st } /// Initialize for full-duplex 1 bit mode - fn init(&mut self) { + fn init(&self) { let reg_block = self.register_block(); reg_block.clock().write(|w| unsafe { w.bits(0) }); @@ -617,7 +659,7 @@ pub trait Instance: Peripheral

+ Into + PeripheralMarker + 'st w.mode().set_bit() }); - reset_spi(reg_block); + self.reset_spi(); reg_block.user().modify(|_, w| { w.doutdin().set_bit(); @@ -628,7 +670,7 @@ pub trait Instance: Peripheral

+ Into + PeripheralMarker + 'st reg_block.misc().write(|w| unsafe { w.bits(0) }); } - fn set_data_mode(&mut self, data_mode: SpiMode, dma: bool) -> &mut Self { + fn set_data_mode(&self, data_mode: SpiMode, dma: bool) { let reg_block = self.register_block(); #[cfg(esp32)] { @@ -699,8 +741,6 @@ pub trait Instance: Peripheral

+ Into + PeripheralMarker + 'st .bit(matches!(data_mode, SpiMode::Mode1 | SpiMode::Mode3)) }); } - - self } fn is_bus_busy(&self) -> bool { @@ -717,7 +757,7 @@ pub trait Instance: Peripheral

+ Into + PeripheralMarker + 'st } // Check if the bus is busy and if it is wait for it to be idle - fn flush(&mut self) -> Result<(), Error> { + fn flush(&self) -> Result<(), Error> { while self.is_bus_busy() { // Wait for bus to be clear } @@ -738,107 +778,46 @@ pub trait Instance: Peripheral

+ Into + PeripheralMarker + 'st } } -#[cfg(spi2)] -impl Instance for crate::peripherals::SPI2 { - #[inline(always)] - fn register_block(&self) -> &RegisterBlock { - self - } - - #[inline(always)] - fn sclk_signal(&self) -> InputSignal { - cfg_if::cfg_if! { - if #[cfg(esp32)] { - InputSignal::HSPICLK - } else { - InputSignal::FSPICLK - } - } - } - - #[inline(always)] - fn mosi_signal(&self) -> InputSignal { - cfg_if::cfg_if! { - if #[cfg(esp32)] { - InputSignal::HSPID - } else { - InputSignal::FSPID - } - } - } - - #[inline(always)] - fn miso_signal(&self) -> OutputSignal { - cfg_if::cfg_if! { - if #[cfg(esp32)] { - OutputSignal::HSPIQ - } else { - OutputSignal::FSPIQ - } - } - } - - #[inline(always)] - fn cs_signal(&self) -> InputSignal { - cfg_if::cfg_if! { - if #[cfg(esp32)] { - InputSignal::HSPICS0 - } else { - InputSignal::FSPICS0 - } - } +impl PartialEq for Info { + fn eq(&self, other: &Self) -> bool { + self.register_block == other.register_block } } -#[cfg(spi3)] -impl Instance for crate::peripherals::SPI3 { - #[inline(always)] - fn register_block(&self) -> &RegisterBlock { - self - } - - #[inline(always)] - fn sclk_signal(&self) -> InputSignal { - cfg_if::cfg_if! { - if #[cfg(esp32)] { - InputSignal::VSPICLK - } else { - InputSignal::SPI3_CLK - } - } - } - - #[inline(always)] - fn mosi_signal(&self) -> InputSignal { - cfg_if::cfg_if! { - if #[cfg(esp32)] { - InputSignal::VSPID - } else { - InputSignal::SPI3_D - } - } - } - - #[inline(always)] - fn miso_signal(&self) -> OutputSignal { - cfg_if::cfg_if! { - if #[cfg(esp32)] { - OutputSignal::VSPIQ - } else { - OutputSignal::SPI3_Q +unsafe impl Sync for Info {} + +macro_rules! spi_instance { + ($num:literal, $sclk:ident, $mosi:ident, $miso:ident, $cs:ident) => { + paste::paste! { + impl Instance for crate::peripherals::[] { + #[inline(always)] + fn info(&self) -> &'static Info { + static INFO: Info = Info { + register_block: crate::peripherals::[]::PTR, + sclk: InputSignal::$sclk, + mosi: InputSignal::$mosi, + miso: OutputSignal::$miso, + cs: InputSignal::$cs, + }; + + &INFO + } } } - } + }; +} - #[inline(always)] - fn cs_signal(&self) -> InputSignal { - cfg_if::cfg_if! { - if #[cfg(esp32)] { - InputSignal::VSPICS0 - } else { - InputSignal::SPI3_CS0 - } - } +cfg_if::cfg_if! { + if #[cfg(esp32)] { + #[cfg(spi2)] + spi_instance!(2, HSPICLK, HSPID, HSPIQ, HSPICS0); + #[cfg(spi3)] + spi_instance!(3, VSPICLK, VSPID, VSPIQ, VSPICS0); + } else { + #[cfg(spi2)] + spi_instance!(2, FSPICLK, FSPID, FSPIQ, FSPICS0); + #[cfg(spi3)] + spi_instance!(3, SPI3_CLK, SPI3_D, SPI3_Q, SPI3_CS0); } } @@ -849,11 +828,7 @@ impl Instance for super::AnySpi { #[cfg(spi3)] super::AnySpiInner::Spi3(spi) => spi, } { - fn register_block(&self) -> &RegisterBlock; - fn sclk_signal(&self) -> InputSignal; - fn mosi_signal(&self) -> InputSignal; - fn miso_signal(&self) -> OutputSignal; - fn cs_signal(&self) -> InputSignal; + fn info(&self) -> &'static Info; } } }