use num_traits::{AsPrimitive, PrimInt};
use crate::dmac::{AnyChannel, Beat, Buffer, Ready, channel, sram::DmacDescriptor};
use crate::ehal::spi::SpiBus;
use crate::sercom::dma::{
SercomPtr, SharedSliceBuffer, SinkSourceBuffer, read_dma, read_dma_linked, write_dma,
write_dma_linked,
};
use super::{
Capability, Config, DataWidth, Duplex, Error, MasterMode, OpMode, Receive, Sercom, Size, Slave,
Spi, Transmit, ValidConfig, ValidPads, Word,
};
impl<P, M, Z, D, R, T> Spi<Config<P, M, Z>, D, R, T>
where
P: ValidPads,
M: OpMode,
Z: Size,
Config<P, M, Z>: ValidConfig,
D: Capability,
Z::Word: Beat,
{
#[inline]
pub(in super::super) fn sercom_ptr(&self) -> SercomPtr<Z::Word> {
SercomPtr(self.config.regs.spi().data().as_ptr() as *mut _)
}
}
impl<P, M, Z, D, R, T, S> Spi<Config<P, M, Z>, D, R, T>
where
P: ValidPads,
M: OpMode,
Z: Size + 'static,
Config<P, M, Z>: ValidConfig<Sercom = S>,
D: Transmit,
S: Sercom,
Z::Word: PrimInt + AsPrimitive<DataWidth> + Beat,
DataWidth: AsPrimitive<Z::Word>,
T: AnyChannel<Status = Ready>,
{
pub(super) fn write_dma(&mut self, buf: &[Z::Word]) -> Result<usize, Error> {
if buf.is_empty() {
return Ok(0);
}
self.config.as_mut().regs.rx_disable();
let sercom_ptr = self.sercom_ptr();
let tx = self._tx_channel.as_mut();
let mut words = crate::sercom::dma::SharedSliceBuffer::from_slice(buf);
unsafe {
crate::sercom::dma::write_dma::<_, _, S>(tx, sercom_ptr, &mut words);
}
while !tx.xfer_complete() {
core::hint::spin_loop();
}
tx.stop();
if D::RX_ENABLE {
self.config.as_mut().regs.rx_enable();
}
self._tx_channel.as_mut().xfer_success()?;
Ok(buf.len())
}
}
impl<P, M, S, C, D, R, T> Spi<Config<P, M, C>, D, R, T>
where
Config<P, M, C>: ValidConfig<Sercom = S>,
S: Sercom,
P: ValidPads,
M: MasterMode,
C: Size + 'static,
C::Word: PrimInt + AsPrimitive<DataWidth> + Beat,
DataWidth: AsPrimitive<C::Word>,
D: Capability,
R: AnyChannel<Status = Ready>,
T: AnyChannel<Status = Ready>,
{
#[inline]
fn transfer_blocking<Source: Buffer<Beat = C::Word>, Dest: Buffer<Beat = C::Word>>(
&mut self,
dest: &mut Dest,
source: &mut Source,
) -> Result<(), Error> {
let sercom_ptr = self.sercom_ptr();
let rx = self._rx_channel.as_mut();
let tx = self._tx_channel.as_mut();
unsafe {
read_dma::<_, _, S>(rx, sercom_ptr.clone(), dest);
write_dma::<_, _, S>(tx, sercom_ptr, source);
}
while !(rx.xfer_complete() && tx.xfer_complete()) {
core::hint::spin_loop();
}
tx.stop();
rx.stop();
self.read_status().check_bus_error()?;
self._rx_channel
.as_mut()
.xfer_success()
.and(self._tx_channel.as_mut().xfer_success())?;
Ok(())
}
#[inline]
pub(super) fn read_dma_master(&mut self, mut words: &mut [C::Word]) -> Result<(), Error> {
if words.is_empty() {
return Ok(());
}
let mut source_word = self.config.nop_word.as_();
let mut source = SinkSourceBuffer::new(&mut source_word, words.len());
self.transfer_blocking(&mut words, &mut source)
}
}
impl<P, M, S, C, R, T> SpiBus<Word<C>> for Spi<Config<P, M, C>, Duplex, R, T>
where
Config<P, M, C>: ValidConfig<Sercom = S>,
S: Sercom,
P: ValidPads,
M: MasterMode,
C: Size + 'static,
C::Word: PrimInt + AsPrimitive<DataWidth> + Beat,
DataWidth: AsPrimitive<C::Word>,
R: AnyChannel<Status = Ready>,
T: AnyChannel<Status = Ready>,
{
#[inline]
fn read(&mut self, words: &mut [C::Word]) -> Result<(), Self::Error> {
self.read_dma_master(words)
}
#[inline]
fn write(&mut self, words: &[C::Word]) -> Result<(), Self::Error> {
self.write_dma(words)?;
Ok(())
}
#[inline]
fn transfer(&mut self, mut read: &mut [C::Word], write: &[C::Word]) -> Result<(), Self::Error> {
use core::cmp::Ordering;
if write.is_empty() && read.is_empty() {
return Ok(());
}
if write.is_empty() {
return self.read_dma_master(read);
} else if read.is_empty() {
self.write_dma(write)?;
return Ok(());
}
let mut linked_descriptor = DmacDescriptor::default();
let mut source_sink_word = self.config.nop_word.as_();
let mut sercom_ptr = self.sercom_ptr();
let (read_link, write_link) = match read.len().cmp(&write.len()) {
Ordering::Equal => {
let mut write = SharedSliceBuffer::from_slice(write);
return self.transfer_blocking(&mut read, &mut write);
}
Ordering::Less => {
let mut sink =
SinkSourceBuffer::new(&mut source_sink_word, write.len() - read.len());
unsafe {
channel::write_descriptor(
&mut linked_descriptor,
&mut sercom_ptr,
&mut sink,
core::ptr::null_mut(),
);
}
(Some(&mut linked_descriptor), None)
}
Ordering::Greater => {
let mut source =
SinkSourceBuffer::new(&mut source_sink_word, read.len() - write.len());
unsafe {
channel::write_descriptor(
&mut linked_descriptor,
&mut source,
&mut sercom_ptr,
core::ptr::null_mut(),
);
}
(None, Some(&mut linked_descriptor))
}
};
let rx = self._rx_channel.as_mut();
let tx = self._tx_channel.as_mut();
let mut write = SharedSliceBuffer::from_slice(write);
unsafe {
read_dma_linked::<_, _, S>(rx, sercom_ptr.clone(), &mut read, read_link);
write_dma_linked::<_, _, S>(tx, sercom_ptr, &mut write, write_link);
}
while !(rx.xfer_complete() && tx.xfer_complete()) {
core::hint::spin_loop();
}
tx.stop();
rx.stop();
self.read_status().check_bus_error()?;
self._rx_channel
.as_mut()
.xfer_success()
.and(self._tx_channel.as_mut().xfer_success())?;
Ok(())
}
#[inline]
fn transfer_in_place(&mut self, words: &mut [C::Word]) -> Result<(), Self::Error> {
unsafe {
let mut read_buf = SharedSliceBuffer::from_slice_unchecked(words);
let mut write_buf = SharedSliceBuffer::from_slice(words);
self.transfer_blocking(&mut read_buf, &mut write_buf)
}
}
#[inline]
fn flush(&mut self) -> Result<(), Error> {
self.flush_tx();
Ok(())
}
}
impl<P, M, Z, D, R, T, S> embedded_io::Write for Spi<Config<P, M, Z>, D, R, T>
where
P: ValidPads,
M: OpMode,
Z: Size<Word = u8> + 'static,
Config<P, M, Z>: ValidConfig<Sercom = S>,
D: Transmit,
S: Sercom,
T: AnyChannel<Status = Ready>,
{
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
Spi::write_dma(self, buf)
}
fn flush(&mut self) -> Result<(), Self::Error> {
self.flush_tx();
Ok(())
}
}
impl<P, M, Z, D, R, T, S> embedded_io::Read for Spi<Config<P, M, Z>, D, R, T>
where
P: ValidPads,
M: MasterMode,
Z: Size<Word = u8> + 'static,
Config<P, M, Z>: ValidConfig<Sercom = S>,
D: Receive,
S: Sercom,
R: AnyChannel<Status = Ready>,
T: AnyChannel<Status = Ready>,
{
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
self.read_dma_master(buf)?;
Ok(buf.len())
}
}
impl<P, Z, D, R, T, S> embedded_io::Read for Spi<Config<P, Slave, Z>, D, R, T>
where
P: ValidPads,
Z: Size<Word = u8> + 'static,
Config<P, Slave, Z>: ValidConfig<Sercom = S>,
D: Receive,
S: Sercom,
R: AnyChannel<Status = Ready>,
{
fn read(&mut self, mut buf: &mut [u8]) -> Result<usize, Self::Error> {
if buf.is_empty() {
return Ok(0);
}
self.flush_rx()?;
let sercom_ptr = self.sercom_ptr();
let rx = self._rx_channel.as_mut();
unsafe {
read_dma::<_, _, S>(rx, sercom_ptr.clone(), &mut buf);
}
while !(rx.xfer_complete()) {
core::hint::spin_loop();
}
rx.stop();
self.read_status().check_bus_error()?;
self._rx_channel.as_mut().xfer_success()?;
Ok(buf.len())
}
}