1//! # Abstractions to setup and use the DMA controller
2//!
3//! # Initializing
4//!
5//! The DMAC should be initialized using the
6//! [`DmaController::init`] method. It will consume the
7//! DMAC object generated by the PAC. By default, all four priority levels
8//! will be enabled, but can be selectively enabled/disabled through the
9//! [`DmaController::enable_levels`] ansd [`DmaController::disable_levels`]
10//! methods.
11//!
12//! # Splitting Channels
13//!
14//! Using the [`DmaController::split`] method will return
15//! a struct containing handles to individual channels.
16//!
17//! # Releasing the DMAC
18//!
19//! Using the [`free`](DmaController::free) method will
20//! deinitialize the DMAC and return the underlying PAC object.
21#![allow(unused_braces)]
2223use atsamd_hal_macros::{hal_cfg, hal_macro_helper};
24use core::marker::PhantomData;
2526use modular_bitfield::prelude::*;
27use seq_macro::seq;
2829#[hal_cfg(any("dmac-d11", "dmac-d21"))]
30pub use crate::pac::dmac::chctrlb::{
31 Lvlselect as PriorityLevel, Trigactselect as TriggerAction, Trigsrcselect as TriggerSource,
32};
3334#[hal_cfg("dmac-d5x")]
35pub use crate::pac::dmac::channel::{
36 chctrla::{
37 Burstlenselect as BurstLength, Thresholdselect as FifoThreshold,
38 Trigactselect as TriggerAction, Trigsrcselect as TriggerSource,
39 },
40 chprilvl::Prilvlselect as PriorityLevel,
41};
4243use super::{
44 channel::{Channel, Uninitialized},
45 sram,
46};
47use crate::{
48 pac::{Dmac, Pm},
49 typelevel::NoneT,
50};
5152/// Trait representing a DMA channel ID
53pub trait ChId {
54const U8: u8;
55const USIZE: usize;
56}
5758/// Mask representing which priority levels should be enabled/disabled
59#[bitfield]
60#[repr(u16)]
61pub struct PriorityLevelMask {
62#[skip]
63_reserved: B8,
64/// Level 0
65pub level0: bool,
66/// Level 1
67pub level1: bool,
68/// Level 2
69pub level2: bool,
70/// Level 3
71pub level3: bool,
72#[skip]
73_reserved: B4,
74}
7576impl Default for PriorityLevelMask {
77fn default() -> Self {
78Self::new()
79 }
80}
8182/// Mask representing which priority levels should be configured as round-robin
83#[bitfield]
84#[repr(u32)]
85pub struct RoundRobinMask {
86#[skip]
87_reserved: B7,
88/// Level 0
89pub level0: bool,
90#[skip]
91_reserved: B7,
92/// Level 1
93pub level1: bool,
94#[skip]
95_reserved: B7,
96/// Level 2
97pub level2: bool,
98#[skip]
99_reserved: B7,
100/// Level 3
101pub level3: bool,
102}
103104impl Default for RoundRobinMask {
105fn default() -> Self {
106Self::new()
107 }
108}
109110macro_rules! define_channels_struct {
111 ($num_channels:literal) => {
112seq!(N in 0..$num_channels {
113 #(
114/// Type alias for a channel number
115pub enum Ch~N {}
116117impl ChId for Ch~N {
118const U8: u8 = N;
119const USIZE: usize = N;
120 }
121 )*
122123/// Struct generating individual handles to each DMA channel
124pub struct Channels(
125 #(
126pub Channel<Ch~N, Uninitialized>,
127 )*
128 );
129 });
130 };
131}
132133with_num_channels!(define_channels_struct);
134135#[cfg(feature = "async")]
136macro_rules! define_channels_struct_future {
137 ($num_channels:literal) => {
138seq!(N in 0..$num_channels {
139/// Struct generating individual handles to each DMA channel for `async` operation
140pub struct FutureChannels(
141 #(
142pub Channel<Ch~N, super::channel::UninitializedFuture>,
143 )*
144 );
145 });
146 };
147}
148149#[cfg(feature = "async")]
150with_num_channels!(define_channels_struct_future);
151152/// Initialized DMA Controller
153pub struct DmaController<I = NoneT> {
154 dmac: Dmac,
155 _irqs: PhantomData<I>,
156}
157158impl DmaController {
159/// Initialize the DMAC and return a DmaController object useable by
160 /// [`Transfer`](super::transfer::Transfer)'s. By default, all
161 /// priority levels are enabled unless subsequently disabled using the
162 /// `level_x_enabled` methods.
163#[inline]
164 #[hal_macro_helper]
165pub fn init(mut dmac: Dmac, _pm: &mut Pm) -> Self {
166// ----- Initialize clocking ----- //
167#[hal_cfg(any("dmac-d11", "dmac-d21"))]
168{
169// Enable clocking
170_pm.ahbmask().modify(|_, w| w.dmac_().set_bit());
171 _pm.apbbmask().modify(|_, w| w.dmac_().set_bit());
172 }
173174Self::swreset(&mut dmac);
175176// SAFETY:
177 //
178 // This is safe because we write a whole u32 to 32-bit registers,
179 // and the descriptor array addesses will never change since they are static.
180 // We just need to ensure the writeback and descriptor_section addresses
181 // are valid.
182unsafe {
183 dmac.baseaddr()
184 .write(|w| w.baseaddr().bits(sram::descriptor_section_addr() as u32));
185 dmac.wrbaddr()
186 .write(|w| w.wrbaddr().bits(sram::writeback_addr() as u32));
187 }
188189// ----- Select priority levels ----- //
190dmac.ctrl().modify(|_, w| {
191 w.lvlen3().set_bit();
192 w.lvlen2().set_bit();
193 w.lvlen1().set_bit();
194 w.lvlen0().set_bit()
195 });
196197// Enable DMA controller
198dmac.ctrl().modify(|_, w| w.dmaenable().set_bit());
199200Self {
201 dmac,
202 _irqs: PhantomData,
203 }
204 }
205206/// Release the DMAC and return the register block.
207 ///
208 /// **Note**: The [`Channels`] struct is consumed by this method. This means
209 /// that any [`Channel`] obtained by [`split`](DmaController::split) must be
210 /// moved back into the [`Channels`] struct before being able to pass it
211 /// into [`free`](DmaController::free).
212#[inline]
213 #[hal_macro_helper]
214pub fn free(mut self, _channels: Channels, _pm: &mut Pm) -> Dmac {
215self.dmac.ctrl().modify(|_, w| w.dmaenable().clear_bit());
216217Self::swreset(&mut self.dmac);
218219#[hal_cfg(any("dmac-d11", "dmac-d21"))]
220{
221// Disable the DMAC clocking
222_pm.apbbmask().modify(|_, w| w.dmac_().clear_bit());
223 _pm.ahbmask().modify(|_, w| w.dmac_().clear_bit());
224 }
225226// Release the DMAC
227self.dmac
228 }
229}
230231impl<T> DmaController<T> {
232/// Enable multiple priority levels simultaneously
233#[inline]
234pub fn enable_levels(&mut self, mask: PriorityLevelMask) {
235// SAFETY This is safe because the use of bitfields ensures that only the
236 // LVLENx bits are written to. The fact that we are given a mask means we need
237 // to do the bit-level setting ourselves.
238let mask: u16 = mask.into();
239unsafe {
240self.dmac.ctrl().modify(|r, w| w.bits(r.bits() | mask));
241 }
242 }
243244/// Disable multiple priority levels simultaneously
245#[inline]
246pub fn disable_levels(&mut self, mask: PriorityLevelMask) {
247// SAFETY This is safe because the use of bitfields ensures that only the
248 // LVLENx bits are written to. The fact that we are given a mask means we need
249 // to do the bit-level clearing ourselves.
250let mask: u16 = mask.into();
251unsafe {
252self.dmac.ctrl().modify(|r, w| w.bits(r.bits() & !mask));
253 }
254 }
255256/// Enable round-robin arbitration for multiple priority levels
257 /// simultaneously
258#[inline]
259pub fn round_robin_arbitration(&mut self, mask: RoundRobinMask) {
260// SAFETY This is safe because the use of bitfields ensures that only the
261 // RRLVLENx bits are written to. The fact that we are given a mask means we need
262 // to do the bit-level setting ourselves.
263let mask: u32 = mask.into();
264unsafe {
265self.dmac.prictrl0().modify(|r, w| w.bits(r.bits() | mask));
266 }
267 }
268269/// Disable round-robin arbitration (ie, enable static priorities) for
270 /// multiple priority levels simultaneously
271#[inline]
272pub fn static_arbitration(&mut self, mask: RoundRobinMask) {
273// SAFETY This is safe because the use of bitfields ensures that only the
274 // RRLVLENx bits are written to. The fact that we are given a mask means we need
275 // to do the bit-level clearing ourselves.
276let mask: u32 = mask.into();
277unsafe {
278self.dmac.prictrl0().modify(|r, w| w.bits(r.bits() & !mask));
279 }
280 }
281282/// Use the [`DmaController`] in async mode. You are required to provide the
283 /// struct created by the
284 /// [`bind_interrupts`](crate::bind_interrupts) macro to prove
285 /// that the interrupt sources have been correctly configured. This function
286 /// will automatically enable the relevant NVIC interrupt sources. However,
287 /// you are required to configure the desired interrupt priorities prior to
288 /// calling this method. Consult [`crate::async_hal::interrupts`]
289 /// module-level documentation for more information.
290 /// [`bind_interrupts`](crate::bind_interrupts).
291#[cfg(feature = "async")]
292 #[inline]
293pub fn into_future<I>(self, _interrupts: I) -> DmaController<I>
294where
295I: crate::async_hal::interrupts::Binding<
296crate::async_hal::interrupts::DMAC,
297super::async_api::InterruptHandler,
298 >,
299 {
300use crate::async_hal::interrupts::{InterruptSource, DMAC};
301302 DMAC::unpend();
303unsafe { DMAC::enable() };
304305 DmaController {
306 dmac: self.dmac,
307 _irqs: PhantomData,
308 }
309 }
310311/// Issue a software reset to the DMAC and wait for reset to complete
312#[inline]
313fn swreset(dmac: &mut Dmac) {
314 dmac.ctrl().modify(|_, w| w.swrst().set_bit());
315while dmac.ctrl().read().swrst().bit_is_set() {}
316 }
317}
318319#[cfg(feature = "async")]
320impl<I> DmaController<I>
321where
322I: crate::async_hal::interrupts::Binding<
323crate::async_hal::interrupts::DMAC,
324super::async_api::InterruptHandler,
325 >,
326{
327/// Release the DMAC and return the register block.
328 ///
329 /// **Note**: The [`Channels`] struct is consumed by this method. This means
330 /// that any [`Channel`] obtained by [`split`](DmaController::split) must be
331 /// moved back into the [`Channels`] struct before being able to pass it
332 /// into [`free`](DmaController::free).
333#[inline]
334 #[hal_macro_helper]
335pub fn free(mut self, _channels: FutureChannels, _pm: &mut Pm) -> Dmac {
336self.dmac.ctrl().modify(|_, w| w.dmaenable().clear_bit());
337338Self::swreset(&mut self.dmac);
339340#[hal_cfg(any("dmac-d11", "dmac-d21"))]
341{
342// Disable the DMAC clocking
343_pm.apbbmask().modify(|_, w| w.dmac_().clear_bit());
344 _pm.ahbmask().modify(|_, w| w.dmac_().clear_bit());
345 }
346347// Release the DMAC
348self.dmac
349 }
350}
351352macro_rules! define_split {
353 ($num_channels:literal) => {
354seq!(N in 0..$num_channels {
355/// Split the DMAC into individual channels
356#[inline]
357pub fn split(&mut self) -> Channels {
358 Channels(
359 #(
360crate::dmac::channel::new_chan(core::marker::PhantomData),
361 )*
362 )
363 }
364 });
365 };
366}
367368impl DmaController {
369with_num_channels!(define_split);
370}
371372#[cfg(feature = "async")]
373macro_rules! define_split_future {
374 ($num_channels:literal) => {
375seq!(N in 0..$num_channels {
376/// Split the DMAC into individual channels
377#[inline]
378pub fn split(&mut self) -> FutureChannels {
379 FutureChannels(
380 #(
381crate::dmac::channel::new_chan_future(core::marker::PhantomData),
382 )*
383 )
384 }
385 });
386 };
387}
388389#[cfg(feature = "async")]
390impl<I> DmaController<I>
391where
392I: crate::async_hal::interrupts::Binding<
393crate::async_hal::interrupts::DMAC,
394super::async_api::InterruptHandler,
395 >,
396{
397with_num_channels!(define_split_future);
398}