#![allow(unused_braces)]
use atsamd_hal_macros::{hal_cfg, hal_macro_helper};
use core::marker::PhantomData;
use modular_bitfield::prelude::*;
use seq_macro::seq;
#[hal_cfg(any("dmac-d11", "dmac-d21"))]
pub use crate::pac::dmac::chctrlb::{
Lvlselect as PriorityLevel, Trigactselect as TriggerAction, Trigsrcselect as TriggerSource,
};
#[hal_cfg("dmac-d5x")]
pub use crate::pac::dmac::channel::{
chctrla::{
Burstlenselect as BurstLength, Thresholdselect as FifoThreshold,
Trigactselect as TriggerAction, Trigsrcselect as TriggerSource,
},
chprilvl::Prilvlselect as PriorityLevel,
};
use super::{
channel::{Channel, Uninitialized},
sram,
};
use crate::{
pac::{Dmac, Pm},
typelevel::NoneT,
};
pub trait ChId {
const U8: u8;
const USIZE: usize;
}
#[bitfield]
#[repr(u16)]
pub struct PriorityLevelMask {
#[skip]
_reserved: B8,
pub level0: bool,
pub level1: bool,
pub level2: bool,
pub level3: bool,
#[skip]
_reserved: B4,
}
impl Default for PriorityLevelMask {
fn default() -> Self {
Self::new()
}
}
#[bitfield]
#[repr(u32)]
pub struct RoundRobinMask {
#[skip]
_reserved: B7,
pub level0: bool,
#[skip]
_reserved: B7,
pub level1: bool,
#[skip]
_reserved: B7,
pub level2: bool,
#[skip]
_reserved: B7,
pub level3: bool,
}
impl Default for RoundRobinMask {
fn default() -> Self {
Self::new()
}
}
macro_rules! define_channels_struct {
($num_channels:literal) => {
seq!(N in 0..$num_channels {
#(
pub enum Ch~N {}
impl ChId for Ch~N {
const U8: u8 = N;
const USIZE: usize = N;
}
)*
pub struct Channels(
#(
pub Channel<Ch~N, Uninitialized>,
)*
);
});
};
}
with_num_channels!(define_channels_struct);
#[cfg(feature = "async")]
macro_rules! define_channels_struct_future {
($num_channels:literal) => {
seq!(N in 0..$num_channels {
pub struct FutureChannels(
#(
pub Channel<Ch~N, super::channel::UninitializedFuture>,
)*
);
});
};
}
#[cfg(feature = "async")]
with_num_channels!(define_channels_struct_future);
pub struct DmaController<I = NoneT> {
dmac: Dmac,
_irqs: PhantomData<I>,
}
impl DmaController {
#[inline]
#[hal_macro_helper]
pub fn init(mut dmac: Dmac, _pm: &mut Pm) -> Self {
#[hal_cfg(any("dmac-d11", "dmac-d21"))]
{
_pm.ahbmask().modify(|_, w| w.dmac_().set_bit());
_pm.apbbmask().modify(|_, w| w.dmac_().set_bit());
}
Self::swreset(&mut dmac);
unsafe {
dmac.baseaddr()
.write(|w| w.baseaddr().bits(sram::descriptor_section_addr() as u32));
dmac.wrbaddr()
.write(|w| w.wrbaddr().bits(sram::writeback_addr() as u32));
}
dmac.ctrl().modify(|_, w| {
w.lvlen3().set_bit();
w.lvlen2().set_bit();
w.lvlen1().set_bit();
w.lvlen0().set_bit()
});
dmac.ctrl().modify(|_, w| w.dmaenable().set_bit());
Self {
dmac,
_irqs: PhantomData,
}
}
#[inline]
#[hal_macro_helper]
pub fn free(mut self, _channels: Channels, _pm: &mut Pm) -> Dmac {
self.dmac.ctrl().modify(|_, w| w.dmaenable().clear_bit());
Self::swreset(&mut self.dmac);
#[hal_cfg(any("dmac-d11", "dmac-d21"))]
{
_pm.apbbmask().modify(|_, w| w.dmac_().clear_bit());
_pm.ahbmask().modify(|_, w| w.dmac_().clear_bit());
}
self.dmac
}
}
impl<T> DmaController<T> {
#[inline]
pub fn enable_levels(&mut self, mask: PriorityLevelMask) {
let mask: u16 = mask.into();
unsafe {
self.dmac.ctrl().modify(|r, w| w.bits(r.bits() | mask));
}
}
#[inline]
pub fn disable_levels(&mut self, mask: PriorityLevelMask) {
let mask: u16 = mask.into();
unsafe {
self.dmac.ctrl().modify(|r, w| w.bits(r.bits() & !mask));
}
}
#[inline]
pub fn round_robin_arbitration(&mut self, mask: RoundRobinMask) {
let mask: u32 = mask.into();
unsafe {
self.dmac.prictrl0().modify(|r, w| w.bits(r.bits() | mask));
}
}
#[inline]
pub fn static_arbitration(&mut self, mask: RoundRobinMask) {
let mask: u32 = mask.into();
unsafe {
self.dmac.prictrl0().modify(|r, w| w.bits(r.bits() & !mask));
}
}
#[cfg(feature = "async")]
#[inline]
pub fn into_future<I>(self, _interrupts: I) -> DmaController<I>
where
I: crate::async_hal::interrupts::Binding<
crate::async_hal::interrupts::DMAC,
super::async_api::InterruptHandler,
>,
{
use crate::async_hal::interrupts::{DMAC, InterruptSource};
DMAC::unpend();
unsafe { DMAC::enable() };
DmaController {
dmac: self.dmac,
_irqs: PhantomData,
}
}
#[inline]
fn swreset(dmac: &mut Dmac) {
dmac.ctrl().modify(|_, w| w.swrst().set_bit());
while dmac.ctrl().read().swrst().bit_is_set() {}
}
}
#[cfg(feature = "async")]
impl<I> DmaController<I>
where
I: crate::async_hal::interrupts::Binding<
crate::async_hal::interrupts::DMAC,
super::async_api::InterruptHandler,
>,
{
#[inline]
#[hal_macro_helper]
pub fn free(mut self, _channels: FutureChannels, _pm: &mut Pm) -> Dmac {
self.dmac.ctrl().modify(|_, w| w.dmaenable().clear_bit());
Self::swreset(&mut self.dmac);
#[hal_cfg(any("dmac-d11", "dmac-d21"))]
{
_pm.apbbmask().modify(|_, w| w.dmac_().clear_bit());
_pm.ahbmask().modify(|_, w| w.dmac_().clear_bit());
}
self.dmac
}
}
macro_rules! define_split {
($num_channels:literal) => {
seq!(N in 0..$num_channels {
#[inline]
pub fn split(&mut self) -> Channels {
Channels(
#(
crate::dmac::channel::new_chan(core::marker::PhantomData),
)*
)
}
});
};
}
impl DmaController {
with_num_channels!(define_split);
}
#[cfg(feature = "async")]
macro_rules! define_split_future {
($num_channels:literal) => {
seq!(N in 0..$num_channels {
#[inline]
pub fn split(&mut self) -> FutureChannels {
FutureChannels(
#(
crate::dmac::channel::new_chan_future(core::marker::PhantomData),
)*
)
}
});
};
}
#[cfg(feature = "async")]
impl<I> DmaController<I>
where
I: crate::async_hal::interrupts::Binding<
crate::async_hal::interrupts::DMAC,
super::async_api::InterruptHandler,
>,
{
with_num_channels!(define_split_future);
}