use rp2040_pac::DMA;
use super::{Channel, ChannelIndex, Pace, ReadTarget, WriteTarget};
use crate::{
atomic_register_access::{write_bitmask_clear, write_bitmask_set},
dma::ChannelRegs,
typelevel::Sealed,
};
use core::mem;
pub trait SingleChannel: Sealed {
fn ch(&self) -> &rp2040_pac::dma::CH;
fn id(&self) -> u8;
fn listen_irq0(&mut self) {
unsafe {
write_bitmask_set((*DMA::ptr()).inte0.as_ptr(), 1 << self.id());
}
}
fn unlisten_irq0(&mut self) {
unsafe {
write_bitmask_clear((*DMA::ptr()).inte0.as_ptr(), 1 << self.id());
}
}
fn check_irq0(&mut self) -> bool {
unsafe {
let status = (*DMA::ptr()).ints0.read().bits();
if (status & (1 << self.id())) != 0 {
(*DMA::ptr()).ints0.write(|w| w.bits(1 << self.id()));
true
} else {
false
}
}
}
fn listen_irq1(&mut self) {
unsafe {
write_bitmask_set((*DMA::ptr()).inte1.as_ptr(), 1 << self.id());
}
}
fn unlisten_irq1(&mut self) {
unsafe {
write_bitmask_clear((*DMA::ptr()).inte1.as_ptr(), 1 << self.id());
}
}
fn check_irq1(&mut self) -> bool {
unsafe {
let status = (*DMA::ptr()).ints1.read().bits();
if (status & (1 << self.id())) != 0 {
(*DMA::ptr()).ints1.write(|w| w.bits(1 << self.id()));
true
} else {
false
}
}
}
}
pub trait ChannelPair: SingleChannel + Sealed {
fn ch2(&self) -> &rp2040_pac::dma::CH;
fn id2(&self) -> u8;
}
impl<CH: ChannelIndex> SingleChannel for Channel<CH> {
fn ch(&self) -> &rp2040_pac::dma::CH {
self.regs()
}
fn id(&self) -> u8 {
CH::id()
}
}
impl<CH: ChannelIndex> Sealed for Channel<CH> {}
impl<CH1: ChannelIndex, CH2: ChannelIndex> SingleChannel for (Channel<CH1>, Channel<CH2>) {
fn ch(&self) -> &rp2040_pac::dma::CH {
self.0.regs()
}
fn id(&self) -> u8 {
CH1::id()
}
}
impl<CH1: ChannelIndex, CH2: ChannelIndex> Sealed for (Channel<CH1>, Channel<CH2>) {}
pub(crate) trait ChannelConfig {
fn config<WORD, FROM, TO>(
&mut self,
from: &FROM,
to: &mut TO,
pace: Pace,
chain_to: Option<u8>,
start: bool,
) where
FROM: ReadTarget<ReceivedWord = WORD>,
TO: WriteTarget<TransmittedWord = WORD>;
fn set_chain_to_enabled<CH: SingleChannel>(&mut self, other: &mut CH);
fn start(&mut self);
fn start_both<CH: SingleChannel>(&mut self, other: &mut CH);
}
impl<CH: SingleChannel> ChannelConfig for CH {
fn config<WORD, FROM, TO>(
&mut self,
from: &FROM,
to: &mut TO,
pace: Pace,
chain_to: Option<u8>,
start: bool,
) where
FROM: ReadTarget<ReceivedWord = WORD>,
TO: WriteTarget<TransmittedWord = WORD>,
{
assert!(
mem::size_of::<WORD>() != 8,
"DMA does not support transferring 64bit data"
);
let (src, src_count) = from.rx_address_count();
let src_incr = from.rx_increment();
let (dest, dest_count) = to.tx_address_count();
let dest_incr = to.tx_increment();
const TREQ_UNPACED: u8 = 0x3f;
let treq = match pace {
Pace::PreferSource => FROM::rx_treq().or_else(TO::tx_treq).unwrap_or(TREQ_UNPACED),
Pace::PreferSink => TO::tx_treq().or_else(FROM::rx_treq).unwrap_or(TREQ_UNPACED),
};
let len = u32::min(src_count, dest_count);
self.ch().ch_al1_ctrl.write(|w| unsafe {
w.data_size().bits(mem::size_of::<WORD>() as u8 >> 1);
w.incr_read().bit(src_incr);
w.incr_write().bit(dest_incr);
w.treq_sel().bits(treq);
w.chain_to().bits(chain_to.unwrap_or_else(|| self.id()));
w.en().bit(true);
w
});
self.ch().ch_read_addr.write(|w| unsafe { w.bits(src) });
self.ch().ch_trans_count.write(|w| unsafe { w.bits(len) });
if start {
self.ch()
.ch_al2_write_addr_trig
.write(|w| unsafe { w.bits(dest) });
} else {
self.ch().ch_write_addr.write(|w| unsafe { w.bits(dest) });
}
}
fn set_chain_to_enabled<CH2: SingleChannel>(&mut self, other: &mut CH2) {
self.ch().ch_al1_ctrl.modify(|_, w| unsafe {
w.chain_to().bits(other.id());
w.en().clear_bit();
w
});
if self.ch().ch_al1_ctrl.read().busy().bit_is_set() {
self.ch().ch_al1_ctrl.modify(|_, w| w.en().set_bit());
} else {
other.start();
}
}
fn start(&mut self) {
unsafe { &*rp2040_pac::DMA::ptr() }
.multi_chan_trigger
.write(|w| unsafe { w.bits(1 << self.id()) });
}
fn start_both<CH2: SingleChannel>(&mut self, other: &mut CH2) {
let channel_flags = 1 << self.id() | 1 << other.id();
unsafe { &*rp2040_pac::DMA::ptr() }
.multi_chan_trigger
.write(|w| unsafe { w.bits(channel_flags) });
}
}