SPI slave: remove public hidden API (#2470)

* Redo declaration as macro

* Add Info

* Move impl out of Instance traits

* Publish traits
This commit is contained in:
Dániel Buga 2024-11-07 10:41:16 +01:00 committed by GitHub
parent 0da6eec089
commit 8782429e9f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -75,7 +75,7 @@ use core::marker::PhantomData;
use super::{Error, SpiMode}; use super::{Error, SpiMode};
use crate::{ use crate::{
dma::{DescriptorChain, DmaChannelConvert, DmaEligible, PeripheralMarker, Rx, Tx}, dma::{DmaChannelConvert, DmaEligible, PeripheralMarker},
gpio::{ gpio::{
interconnect::{PeripheralInput, PeripheralOutput}, interconnect::{PeripheralInput, PeripheralOutput},
InputSignal, InputSignal,
@ -144,16 +144,16 @@ where
// TODO: with_pins et. al. // TODO: with_pins et. al.
sclk.enable_input(true, private::Internal); 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); 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); 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); cs.enable_input(true, private::Internal);
this.spi.cs_signal().connect_to(cs); this.spi.info().cs.connect_to(cs);
this this
} }
@ -161,7 +161,7 @@ where
pub(crate) fn new_internal(spi: impl Peripheral<P = T> + 'd, mode: SpiMode) -> Spi<'d, M, T> { pub(crate) fn new_internal(spi: impl Peripheral<P = T> + 'd, mode: SpiMode) -> Spi<'d, M, T> {
crate::into_ref!(spi); crate::into_ref!(spi);
let mut spi = Spi { let spi = Spi {
spi, spi,
data_mode: mode, data_mode: mode,
_mode: PhantomData, _mode: PhantomData,
@ -170,8 +170,8 @@ where
PeripheralClockControl::reset(spi.spi.peripheral()); PeripheralClockControl::reset(spi.spi.peripheral());
PeripheralClockControl::enable(spi.spi.peripheral()); PeripheralClockControl::enable(spi.spi.peripheral());
spi.spi.init(); spi.spi.info().init();
spi.spi.set_data_mode(mode, false); spi.spi.info().set_data_mode(mode, false);
spi spi
} }
@ -208,7 +208,7 @@ pub mod dma {
/// descriptors. /// descriptors.
#[cfg_attr(esp32, doc = "\n\n**Note**: ESP32 only supports Mode 1 and 3.")] #[cfg_attr(esp32, doc = "\n\n**Note**: ESP32 only supports Mode 1 and 3.")]
pub fn with_dma<CH, DM>( pub fn with_dma<CH, DM>(
mut self, self,
channel: Channel<'d, CH, DM>, channel: Channel<'d, CH, DM>,
rx_descriptors: &'static mut [DmaDescriptor], rx_descriptors: &'static mut [DmaDescriptor],
tx_descriptors: &'static mut [DmaDescriptor], tx_descriptors: &'static mut [DmaDescriptor],
@ -218,7 +218,7 @@ pub mod dma {
DM: Mode, DM: Mode,
Channel<'d, CH, M>: From<Channel<'d, CH, DM>>, Channel<'d, CH, M>: From<Channel<'d, CH, DM>>,
{ {
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) 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) { fn peripheral_wait_dma(&mut self, is_rx: bool, is_tx: bool) {
while !((!is_tx || self.channel.tx.is_done()) while !((!is_tx || self.channel.tx.is_done())
&& (!is_rx || self.channel.rx.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) { fn peripheral_dma_stop(&mut self) {
@ -318,6 +318,14 @@ pub mod dma {
tx_chain: DescriptorChain::new(tx_descriptors), 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. /// Register a buffer for a DMA write.
/// ///
/// This will return a [DmaTransferTx]. The maximum amount of data to be /// This will return a [DmaTransferTx]. The maximum amount of data to be
@ -338,7 +346,7 @@ pub mod dma {
} }
unsafe { unsafe {
self.spi self.driver()
.start_transfer_dma( .start_transfer_dma(
&mut self.rx_chain, &mut self.rx_chain,
&mut self.tx_chain, &mut self.tx_chain,
@ -373,7 +381,7 @@ pub mod dma {
} }
unsafe { unsafe {
self.spi self.driver()
.start_transfer_dma( .start_transfer_dma(
&mut self.rx_chain, &mut self.rx_chain,
&mut self.tx_chain, &mut self.tx_chain,
@ -412,7 +420,7 @@ pub mod dma {
} }
unsafe { unsafe {
self.spi self.driver()
.start_transfer_dma( .start_transfer_dma(
&mut self.rx_chain, &mut self.rx_chain,
&mut self.tx_chain, &mut self.tx_chain,
@ -427,163 +435,197 @@ pub mod dma {
} }
} }
} }
}
#[doc(hidden)] struct DmaDriver {
pub trait InstanceDma: Instance + DmaEligible { info: &'static Info,
#[allow(clippy::too_many_arguments)] dma_peripheral: crate::dma::DmaPeripheral,
unsafe fn start_transfer_dma<RX, TX>(
&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();
self.enable_dma();
reset_spi(reg_block);
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)?;
}
#[cfg(esp32)]
self.prepare_length_and_lines(read_buffer_len, write_buffer_len);
reset_dma_before_usr_cmd(reg_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());
if read_buffer_len > 0 {
rx.start_transfer()?;
}
if write_buffer_len > 0 {
tx.start_transfer()?;
}
Ok(())
} }
fn enable_dma(&self) { impl DmaDriver {
let reg_block = self.register_block(); #[allow(clippy::too_many_arguments)]
#[cfg(gdma)] unsafe fn start_transfer_dma<RX, TX>(
&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();
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 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.info
.prepare_length_and_lines(read_buffer_len, write_buffer_len);
self.reset_dma_before_usr_cmd();
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.info.setup_for_flush();
reg_block.cmd().modify(|_, w| w.usr().set_bit());
if read_buffer_len > 0 {
rx.start_transfer()?;
}
if write_buffer_len > 0 {
tx.start_transfer()?;
}
Ok(())
}
fn reset_dma_before_usr_cmd(&self) {
let reg_block = self.info.register_block();
#[cfg(gdma)]
reg_block.dma_conf().modify(|_, w| { reg_block.dma_conf().modify(|_, w| {
w.dma_tx_ena().set_bit(); w.rx_afifo_rst().set_bit();
w.dma_rx_ena().set_bit(); w.buf_afifo_rst().set_bit();
w.rx_eof_en().clear_bit() w.dma_afifo_rst().set_bit()
});
#[cfg(pdma)]
let _ = reg_block;
}
fn enable_dma(&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()
});
}
#[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);
}
}
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(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)]
{
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);
}
}
fn clear_dma_interrupts(&self) {
let reg_block = self.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(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) { /// SPI peripheral instance.
#[cfg(esp32)] pub trait Instance: Peripheral<P = Self> + Into<AnySpi> + PeripheralMarker + 'static {
{ /// Returns the peripheral data describing this SPI instance.
reg_block.slave().modify(|_, w| w.sync_reset().set_bit()); fn info(&self) -> &'static Info;
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());
}
} }
fn reset_dma_before_usr_cmd(reg_block: &RegisterBlock) { /// A marker for DMA-capable SPI peripheral instances.
#[cfg(gdma)] pub trait InstanceDma: Instance + DmaEligible {}
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;
}
impl InstanceDma for crate::peripherals::SPI2 {} impl InstanceDma for crate::peripherals::SPI2 {}
#[cfg(spi3)] #[cfg(spi3)]
impl InstanceDma for crate::peripherals::SPI3 {} impl InstanceDma for crate::peripherals::SPI3 {}
#[doc(hidden)] /// Peripheral data describing a particular SPI instance.
pub trait Instance: Peripheral<P = Self> + Into<AnySpi> + PeripheralMarker + 'static { #[non_exhaustive]
fn register_block(&self) -> &RegisterBlock; 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,
fn sclk_signal(&self) -> InputSignal; /// SCLK signal.
fn mosi_signal(&self) -> InputSignal; pub sclk: InputSignal,
fn miso_signal(&self) -> OutputSignal;
fn cs_signal(&self) -> 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());
}
#[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)] #[cfg(esp32)]
fn prepare_length_and_lines(&self, rx_len: usize, tx_len: usize) { fn prepare_length_and_lines(&self, rx_len: usize, tx_len: usize) {
@ -604,7 +646,7 @@ pub trait Instance: Peripheral<P = Self> + Into<AnySpi> + PeripheralMarker + 'st
} }
/// Initialize for full-duplex 1 bit mode /// Initialize for full-duplex 1 bit mode
fn init(&mut self) { fn init(&self) {
let reg_block = self.register_block(); let reg_block = self.register_block();
reg_block.clock().write(|w| unsafe { w.bits(0) }); reg_block.clock().write(|w| unsafe { w.bits(0) });
@ -617,7 +659,7 @@ pub trait Instance: Peripheral<P = Self> + Into<AnySpi> + PeripheralMarker + 'st
w.mode().set_bit() w.mode().set_bit()
}); });
reset_spi(reg_block); self.reset_spi();
reg_block.user().modify(|_, w| { reg_block.user().modify(|_, w| {
w.doutdin().set_bit(); w.doutdin().set_bit();
@ -628,7 +670,7 @@ pub trait Instance: Peripheral<P = Self> + Into<AnySpi> + PeripheralMarker + 'st
reg_block.misc().write(|w| unsafe { w.bits(0) }); 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(); let reg_block = self.register_block();
#[cfg(esp32)] #[cfg(esp32)]
{ {
@ -699,8 +741,6 @@ pub trait Instance: Peripheral<P = Self> + Into<AnySpi> + PeripheralMarker + 'st
.bit(matches!(data_mode, SpiMode::Mode1 | SpiMode::Mode3)) .bit(matches!(data_mode, SpiMode::Mode1 | SpiMode::Mode3))
}); });
} }
self
} }
fn is_bus_busy(&self) -> bool { fn is_bus_busy(&self) -> bool {
@ -717,7 +757,7 @@ pub trait Instance: Peripheral<P = Self> + Into<AnySpi> + PeripheralMarker + 'st
} }
// Check if the bus is busy and if it is wait for it to be idle // 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() { while self.is_bus_busy() {
// Wait for bus to be clear // Wait for bus to be clear
} }
@ -738,107 +778,46 @@ pub trait Instance: Peripheral<P = Self> + Into<AnySpi> + PeripheralMarker + 'st
} }
} }
#[cfg(spi2)] impl PartialEq for Info {
impl Instance for crate::peripherals::SPI2 { fn eq(&self, other: &Self) -> bool {
#[inline(always)] self.register_block == other.register_block
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
}
}
} }
} }
#[cfg(spi3)] unsafe impl Sync for Info {}
impl Instance for crate::peripherals::SPI3 {
#[inline(always)]
fn register_block(&self) -> &RegisterBlock {
self
}
#[inline(always)] macro_rules! spi_instance {
fn sclk_signal(&self) -> InputSignal { ($num:literal, $sclk:ident, $mosi:ident, $miso:ident, $cs:ident) => {
cfg_if::cfg_if! { paste::paste! {
if #[cfg(esp32)] { impl Instance for crate::peripherals::[<SPI $num>] {
InputSignal::VSPICLK #[inline(always)]
} else { fn info(&self) -> &'static Info {
InputSignal::SPI3_CLK static INFO: Info = Info {
register_block: crate::peripherals::[<SPI $num>]::PTR,
sclk: InputSignal::$sclk,
mosi: InputSignal::$mosi,
miso: OutputSignal::$miso,
cs: InputSignal::$cs,
};
&INFO
}
} }
} }
} };
}
#[inline(always)] cfg_if::cfg_if! {
fn mosi_signal(&self) -> InputSignal { if #[cfg(esp32)] {
cfg_if::cfg_if! { #[cfg(spi2)]
if #[cfg(esp32)] { spi_instance!(2, HSPICLK, HSPID, HSPIQ, HSPICS0);
InputSignal::VSPID #[cfg(spi3)]
} else { spi_instance!(3, VSPICLK, VSPID, VSPIQ, VSPICS0);
InputSignal::SPI3_D } else {
} #[cfg(spi2)]
} spi_instance!(2, FSPICLK, FSPID, FSPIQ, FSPICS0);
} #[cfg(spi3)]
spi_instance!(3, SPI3_CLK, SPI3_D, SPI3_Q, SPI3_CS0);
#[inline(always)]
fn miso_signal(&self) -> OutputSignal {
cfg_if::cfg_if! {
if #[cfg(esp32)] {
OutputSignal::VSPIQ
} else {
OutputSignal::SPI3_Q
}
}
}
#[inline(always)]
fn cs_signal(&self) -> InputSignal {
cfg_if::cfg_if! {
if #[cfg(esp32)] {
InputSignal::VSPICS0
} else {
InputSignal::SPI3_CS0
}
}
} }
} }
@ -849,11 +828,7 @@ impl Instance for super::AnySpi {
#[cfg(spi3)] #[cfg(spi3)]
super::AnySpiInner::Spi3(spi) => spi, super::AnySpiInner::Spi3(spi) => spi,
} { } {
fn register_block(&self) -> &RegisterBlock; fn info(&self) -> &'static Info;
fn sclk_signal(&self) -> InputSignal;
fn mosi_signal(&self) -> InputSignal;
fn miso_signal(&self) -> OutputSignal;
fn cs_signal(&self) -> InputSignal;
} }
} }
} }