1use core::marker::PhantomData;
12
13use crate::{
14 asynch::AtomicWaker,
15 dma::*,
16 handler,
17 interrupt::Priority,
18 peripherals::{DMA, Interrupt, pac},
19 system::{Peripheral, PeripheralGuard},
20};
21
22#[cfg_attr(dma_gdma_version = "1", path = "ahb_v1.rs")]
23#[cfg_attr(dma_gdma_version = "2", path = "ahb_v2.rs")]
24#[cfg_attr(dma_gdma_version = "3", path = "ahb_v3.rs")]
25mod implementation;
26
27pub(crate) struct ChannelInfo {
29 pub(crate) channel: u8,
31 pub(crate) handler_in: Option<InterruptHandler>,
33 pub(crate) handler_out: Option<InterruptHandler>,
35 pub(crate) isr_in: Option<Interrupt>,
37 pub(crate) isr_out: Option<Interrupt>,
39 pub(crate) compatible_peripherals: &'static [u8],
41}
42
43pub(crate) struct ChannelState {
45 pub(crate) tx_waker: AtomicWaker,
47
48 pub(crate) rx_waker: AtomicWaker,
50
51 #[cfg(not(dma_separate_in_out_interrupts))]
53 pub(crate) tx_is_async: portable_atomic::AtomicBool,
54
55 #[cfg(not(dma_separate_in_out_interrupts))]
57 pub(crate) rx_is_async: portable_atomic::AtomicBool,
58}
59
60pub struct AhbGdmaChannel<'d> {
62 info: &'static ChannelInfo,
63 state: &'static ChannelState,
64 _lifetime: PhantomData<&'d mut ()>,
65}
66
67impl core::fmt::Debug for AhbGdmaChannel<'_> {
68 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
69 f.debug_struct("AhbGdmaChannel")
70 .field("channel", &self.info.channel)
71 .finish()
72 }
73}
74
75#[cfg(feature = "defmt")]
76impl defmt::Format for AhbGdmaChannel<'_> {
77 fn format(&self, fmt: defmt::Formatter<'_>) {
78 defmt::write!(fmt, "AhbGdmaChannel {{ channel: {} }}", self.info.channel)
79 }
80}
81
82impl AhbGdmaChannel<'_> {
83 #[cfg(not(dma_mem2mem_requires_peripheral))]
84 pub(crate) fn channel_index(&self) -> u8 {
85 self.info.channel
86 }
87
88 pub(crate) unsafe fn clone_unchecked(&self) -> Self {
89 Self {
90 info: self.info,
91 state: self.state,
92 _lifetime: PhantomData,
93 }
94 }
95}
96
97impl crate::private::Sealed for AhbGdmaChannel<'_> {}
98impl<'d> DmaChannel for AhbGdmaChannel<'d> {
99 type Rx = AhbGdmaRxChannel<'d>;
100 type Tx = AhbGdmaTxChannel<'d>;
101
102 unsafe fn split_internal(self, _: crate::private::Internal) -> (Self::Rx, Self::Tx) {
103 (
104 AhbGdmaRxChannel(unsafe { self.clone_unchecked() }),
105 AhbGdmaTxChannel(self),
106 )
107 }
108}
109
110#[derive(Debug)]
112#[cfg_attr(feature = "defmt", derive(defmt::Format))]
113pub struct AhbGdmaRxChannel<'d>(AhbGdmaChannel<'d>);
114
115#[derive(Debug)]
117#[cfg_attr(feature = "defmt", derive(defmt::Format))]
118pub struct AhbGdmaTxChannel<'d>(AhbGdmaChannel<'d>);
119
120impl crate::private::Sealed for AhbGdmaTxChannel<'_> {}
121impl DmaTxChannel for AhbGdmaTxChannel<'_> {}
122
123impl crate::private::Sealed for AhbGdmaRxChannel<'_> {}
124impl DmaRxChannel for AhbGdmaRxChannel<'_> {}
125
126macro_rules! impl_channel {
127 ($ch:ident, $num:literal, $interrupt_in:ident, compatible = [$($compatible:ident),*]) => {
129 use $crate::peripherals::$ch;
130 impl $ch<'_> {
131 pub(super) fn info() -> &'static ChannelInfo {
132 #[handler(priority = Priority::max())]
133 fn interrupt_handler() {
134 asynch::handle_in_interrupt::<$ch<'static>>();
135 asynch::handle_out_interrupt::<$ch<'static>>();
136 }
137 static INFO: ChannelInfo = ChannelInfo {
138 channel: $num,
139 handler_in: Some(interrupt_handler),
140 handler_out: None,
141 isr_in: Some(Interrupt::$interrupt_in),
142 isr_out: None,
143 compatible_peripherals: &[$(crate::dma::DmaPeripheral::$compatible.0),*],
144 };
145 &INFO
146 }
147
148 pub(super) fn state() -> &'static ChannelState {
149 static STATE: ChannelState = ChannelState {
150 tx_waker: AtomicWaker::new(),
151 rx_waker: AtomicWaker::new(),
152 tx_is_async: portable_atomic::AtomicBool::new(false),
153 rx_is_async: portable_atomic::AtomicBool::new(false),
154 };
155 &STATE
156 }
157 }
158
159 impl<'d> From<$ch<'d>> for AhbGdmaChannel<'d> {
160 fn from(_ch: $ch<'d>) -> AhbGdmaChannel<'d> {
161 AhbGdmaChannel {
162 info: $ch::info(),
163 state: $ch::state(),
164 _lifetime: core::marker::PhantomData,
165 }
166 }
167 }
168 crate::dma::impl_channel_common!(AhbGdma, $ch);
169 };
170
171 ($ch:ident, $num:literal, $interrupt_in:ident, $interrupt_out:ident, compatible = [$($compatible:ident),*]) => {
173 use $crate::peripherals::$ch;
174 impl $ch<'_> {
175 pub(super) fn info() -> &'static ChannelInfo {
176 #[handler(priority = Priority::max())]
177 fn interrupt_handler_in() {
178 asynch::handle_in_interrupt::<$ch<'static>>();
179 }
180
181 #[handler(priority = Priority::max())]
182 fn interrupt_handler_out() {
183 asynch::handle_out_interrupt::<$ch<'static>>();
184 }
185
186 static INFO: ChannelInfo = ChannelInfo {
187 channel: $num,
188 handler_in: Some(interrupt_handler_in),
189 handler_out: Some(interrupt_handler_out),
190 isr_in: Some(Interrupt::$interrupt_in),
191 isr_out: Some(Interrupt::$interrupt_out),
192 compatible_peripherals: &[$(crate::dma::DmaPeripheral::$compatible.0),*],
193 };
194 &INFO
195 }
196
197 pub(super) fn state() -> &'static ChannelState {
198 static STATE: ChannelState = ChannelState {
199 tx_waker: AtomicWaker::new(),
200 rx_waker: AtomicWaker::new(),
201 };
202 &STATE
203 }
204 }
205
206 impl<'d> From<$ch<'d>> for AhbGdmaChannel<'d> {
207 fn from(_ch: $ch<'d>) -> AhbGdmaChannel<'d> {
208 AhbGdmaChannel {
209 info: $ch::info(),
210 state: $ch::state(),
211 _lifetime: core::marker::PhantomData,
212 }
213 }
214 }
215 crate::dma::impl_channel_common!(AhbGdma, $ch);
216 };
217}
218
219impl<'d> From<AhbGdmaChannel<'d>> for AhbGdmaRxChannel<'d> {
221 fn from(this: AhbGdmaChannel<'d>) -> AhbGdmaRxChannel<'d> {
222 AhbGdmaRxChannel(this)
223 }
224}
225
226impl<'d> From<AhbGdmaChannel<'d>> for AhbGdmaTxChannel<'d> {
227 fn from(this: AhbGdmaChannel<'d>) -> AhbGdmaTxChannel<'d> {
228 AhbGdmaTxChannel(this)
229 }
230}
231
232for_each_dma_channel! {
233 ("AHB_GDMA", $ch:ident, $num:literal, interrupt = $interrupt:ident, compatible = [$($compatible:ident),*]) => {
234 impl_channel!($ch, $num, $interrupt, compatible = [$($compatible),*]);
235 };
236 ("AHB_GDMA", $ch:ident, $num:literal, interrupt_in = $interrupt_in:ident, interrupt_out = $interrupt_out:ident, compatible = [$($compatible:ident),*]) => {
237 impl_channel!($ch, $num, $interrupt_in, $interrupt_out, compatible = [$($compatible),*]);
238 };
239}
240
241fn init_dma_racey() {
242 use crate::RegisterToggle;
244 DMA::regs()
245 .misc_conf()
246 .toggle(|w, en| w.ahbm_rst_inter().bit(en));
247 DMA::regs().misc_conf().modify(|_, w| w.clk_en().set_bit());
248
249 implementation::setup();
250}