1
//! # General Direct Memory Access (GDMA)
2
//!
3
//! ## Overview
4
//! GDMA is a feature that allows peripheral-to-memory, memory-to-peripheral,
5
//! and memory-to-memory data transfer at high speed. The CPU is not involved in
6
//! the GDMA transfer and therefore is more efficient with less workload.
7
//!
8
//! The `GDMA` module provides multiple DMA channels, each capable of managing
9
//! data transfer for various peripherals.
10

            
11
use core::marker::PhantomData;
12

            
13
use 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")]
25
mod implementation;
26

            
27
/// Immutable per-channel metadata owned by each `DMA_CH*` singleton.
28
pub(crate) struct ChannelInfo {
29
    /// Hardware channel index used to select the register bank.
30
    pub(crate) channel: u8,
31
    /// Interrupt handler for the RX (in) direction.
32
    pub(crate) handler_in: Option<InterruptHandler>,
33
    /// Interrupt handler for the TX (out) direction.
34
    pub(crate) handler_out: Option<InterruptHandler>,
35
    /// Peripheral interrupt for the RX (in) direction.
36
    pub(crate) isr_in: Option<Interrupt>,
37
    /// Peripheral interrupt for the TX (out) direction.
38
    pub(crate) isr_out: Option<Interrupt>,
39
    /// List of compatible peripheral IDs for this channel.
40
    pub(crate) compatible_peripherals: &'static [u8],
41
}
42

            
43
/// Mutable per-channel runtime state (wakers and async-mode flags).
44
pub(crate) struct ChannelState {
45
    /// Async waker for the TX (out) half of this channel.
46
    pub(crate) tx_waker: AtomicWaker,
47

            
48
    /// Async waker for the RX (in) half of this channel.
49
    pub(crate) rx_waker: AtomicWaker,
50

            
51
    /// Whether the TX half is currently in async mode (shared-interrupt chips only).
52
    #[cfg(not(dma_separate_in_out_interrupts))]
53
    pub(crate) tx_is_async: portable_atomic::AtomicBool,
54

            
55
    /// Whether the RX half is currently in async mode (shared-interrupt chips only).
56
    #[cfg(not(dma_separate_in_out_interrupts))]
57
    pub(crate) rx_is_async: portable_atomic::AtomicBool,
58
}
59

            
60
/// An arbitrary GDMA channel.
61
pub struct AhbGdmaChannel<'d> {
62
    info: &'static ChannelInfo,
63
    state: &'static ChannelState,
64
    _lifetime: PhantomData<&'d mut ()>,
65
}
66

            
67
impl 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")]
76
impl defmt::Format for AhbGdmaChannel<'_> {
77
    fn format(&self, fmt: defmt::Formatter<'_>) {
78
        defmt::write!(fmt, "AhbGdmaChannel {{ channel: {} }}", self.info.channel)
79
    }
80
}
81

            
82
impl 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

            
97
impl crate::private::Sealed for AhbGdmaChannel<'_> {}
98
impl<'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
/// An arbitrary GDMA RX channel.
111
#[derive(Debug)]
112
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
113
pub struct AhbGdmaRxChannel<'d>(AhbGdmaChannel<'d>);
114

            
115
/// An arbitrary GDMA TX channel.
116
#[derive(Debug)]
117
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
118
pub struct AhbGdmaTxChannel<'d>(AhbGdmaChannel<'d>);
119

            
120
impl crate::private::Sealed for AhbGdmaTxChannel<'_> {}
121
impl DmaTxChannel for AhbGdmaTxChannel<'_> {}
122

            
123
impl crate::private::Sealed for AhbGdmaRxChannel<'_> {}
124
impl DmaRxChannel for AhbGdmaRxChannel<'_> {}
125

            
126
macro_rules! impl_channel {
127
    // Single shared interrupt: one handler drives both the in and out paths.
128
    ($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
5
                fn interrupt_handler() {
134
                    asynch::handle_in_interrupt::<$ch<'static>>();
135
                    asynch::handle_out_interrupt::<$ch<'static>>();
136
5
                }
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
    // Split interrupts: separate handlers for the in and out paths.
172
    ($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

            
219
// Convert erased channel into erased TX/RX half structs
220
impl<'d> From<AhbGdmaChannel<'d>> for AhbGdmaRxChannel<'d> {
221
    fn from(this: AhbGdmaChannel<'d>) -> AhbGdmaRxChannel<'d> {
222
        AhbGdmaRxChannel(this)
223
    }
224
}
225

            
226
impl<'d> From<AhbGdmaChannel<'d>> for AhbGdmaTxChannel<'d> {
227
    fn from(this: AhbGdmaChannel<'d>) -> AhbGdmaTxChannel<'d> {
228
        AhbGdmaTxChannel(this)
229
    }
230
}
231

            
232
for_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

            
241
fn init_dma_racey() {
242
    // FIXME: reset/clock enable belongs to metadata
243
    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
}