1
use enumset::{EnumSet, EnumSetType};
2

            
3
use crate::{
4
    asynch::AtomicWaker,
5
    dma::{BurstConfig, DmaRxInterrupt, DmaTxInterrupt},
6
    interrupt::InterruptHandler,
7
    peripherals::Interrupt,
8
    private::{Internal, Sealed},
9
    system::PeripheralGuard,
10
};
11

            
12
for_each_dma_engine! {
13
    ("AHB_GDMA") => {
14
        mod gdma;
15
        pub use gdma::*;
16
    };
17
    ("AXI_GDMA") => {
18
        mod axi_gdma;
19
        pub use axi_gdma::*;
20
    };
21
    ("COPY_DMA") => {
22
        mod copy;
23
        pub use copy::*;
24
    };
25
    ("CRYPTO_DMA") => {
26
        mod crypto;
27
        pub use crypto::*;
28
    };
29
    ("I2S_DMA") => {
30
        mod i2s;
31
        pub use i2s::*;
32
    };
33
    ("SPI_DMA") => {
34
        mod spi;
35
        pub use spi::*;
36
    };
37
}
38

            
39
/// Implemented by peripheral singletons that can be used with a DMA engine.
40
pub trait DmaEligiblePeripheral<D: DmaChannel> {
41
    /// Returns the `DmaPeripheral` ID for runtime compatibility checks.
42
    fn dma_peripheral(&self) -> DmaPeripheral;
43
}
44

            
45
for_each_peripheral! {
46
    (dma_eligible $(( $peri:ident, $name:ident, $id:literal, $any_ch:ident )),*) => {
47
        /// DMA-eligible peripheral selector values; values are engine-local (matching hardware where applicable).
48
        #[derive(Debug, Clone, Copy, PartialEq, Eq)]
49
        #[cfg_attr(feature = "defmt", derive(defmt::Format))]
50
        #[doc(hidden)]
51
        pub struct DmaPeripheral(pub(crate) u8);
52
        impl DmaPeripheral {
53
            $(
54
                #[doc = concat!("DMA accesses ", stringify!($name))]
55
                pub const $peri: Self = Self($id);
56
            )*
57
        }
58

            
59
        $(
60
            impl<'d> DmaEligiblePeripheral<$any_ch<'d>> for crate::peripherals::$peri<'d> {
61
                fn dma_peripheral(&self) -> DmaPeripheral {
62
                    DmaPeripheral::$peri
63
                }
64
            }
65
        )*
66
    };
67
}
68

            
69
#[doc(hidden)]
70
pub trait RegisterAccess: Sealed {
71
    #[allow(private_interfaces)]
72
    fn enable(&self) -> Option<PeripheralGuard>;
73

            
74
    /// Resets the state machine of the channel and FIFO pointer.
75
    fn reset(&self);
76

            
77
    /// Enables or disables INCR burst transfer for channel reading
78
    /// accessing data in internal RAM.
79
    fn set_burst_mode(&self, burst_mode: BurstConfig);
80

            
81
    /// Enables or disables burst transfer for channel reading
82
    /// descriptors in internal RAM.
83
    fn set_descr_burst_mode(&self, burst_mode: bool);
84

            
85
    /// The priority of the channel. The larger the value, the higher the
86
    /// priority.
87
    #[cfg(dma_max_priority_is_set)]
88
    fn set_priority(&self, priority: crate::dma::DmaPriority);
89

            
90
    /// Selects a peripheral for the channel.
91
    fn set_peripheral(&self, _peripheral: u8) {}
92

            
93
    /// Sets the address of the first descriptor.
94
    fn set_link_addr(&self, address: u32);
95

            
96
    /// Enables the channel for data transfer.
97
    fn start(&self);
98

            
99
    /// Stops the channel from transferring data.
100
    fn stop(&self);
101

            
102
    /// Mounts a new descriptor.
103
    fn restart(&self);
104

            
105
    /// Configures the bit to enable checking the owner attribute of the
106
    /// descriptor.
107
    fn set_check_owner(&self, check_owner: Option<bool>);
108

            
109
    #[cfg(dma_ext_mem_configurable_block_size)]
110
    fn set_ext_mem_block_size(&self, size: crate::dma::DmaExtMemBKSize);
111

            
112
    #[cfg(dma_can_access_psram)]
113
    fn can_access_psram(&self) -> bool;
114

            
115
    fn compatible_peripherals(&self) -> &[u8];
116

            
117
    fn runtime_ensure_compatible(&self, peripheral: DmaPeripheral) {
118
        let peripherals = self.compatible_peripherals();
119
        assert!(
120
333
            peripherals.contains(&peripheral.0),
121
            "This DMA channel is not compatible with peripheral id {}",
122
            peripheral.0
123
        );
124
    }
125
}
126

            
127
#[doc(hidden)]
128
pub trait RxRegisterAccess: RegisterAccess {
129
    #[cfg(dma_supports_mem2mem)]
130
    fn set_mem2mem_mode(&self, value: bool);
131

            
132
    fn peripheral_interrupt(&self) -> Option<Interrupt>;
133
    fn async_handler(&self) -> Option<InterruptHandler>;
134
}
135

            
136
#[doc(hidden)]
137
pub trait TxRegisterAccess: RegisterAccess {
138
    /// Returns whether the DMA's FIFO is empty.
139
    fn is_fifo_empty(&self) -> bool;
140

            
141
    /// Enables or disables outlink-writeback.
142
    fn set_auto_write_back(&self, enable: bool);
143

            
144
    /// Outlink descriptor address when EOF occurs of Tx channel.
145
    fn last_dscr_address(&self) -> usize;
146

            
147
    fn peripheral_interrupt(&self) -> Option<Interrupt>;
148
    fn async_handler(&self) -> Option<InterruptHandler>;
149
}
150

            
151
#[doc(hidden)]
152
pub trait InterruptAccess<T: EnumSetType>: Sealed {
153
    fn listen(&self, interrupts: impl Into<EnumSet<T>>) {
154
5
        self.enable_listen(interrupts.into(), true)
155
    }
156
    fn unlisten(&self, interrupts: impl Into<EnumSet<T>>) {
157
5
        self.enable_listen(interrupts.into(), false)
158
    }
159

            
160
88
    fn clear_all(&self) {
161
        self.clear(EnumSet::all());
162
88
    }
163

            
164
    fn enable_listen(&self, interrupts: EnumSet<T>, enable: bool);
165
    fn is_listening(&self) -> EnumSet<T>;
166
    fn clear(&self, interrupts: impl Into<EnumSet<T>>);
167
    fn pending_interrupts(&self) -> EnumSet<T>;
168
    fn waker(&self) -> &'static AtomicWaker;
169

            
170
    fn is_async(&self) -> bool;
171
    fn set_async(&self, is_async: bool);
172
}
173

            
174
#[instability::unstable]
175
pub trait DmaRxChannel: RxRegisterAccess + InterruptAccess<DmaRxInterrupt> {}
176

            
177
#[instability::unstable]
178
pub trait DmaTxChannel: TxRegisterAccess + InterruptAccess<DmaTxInterrupt> {}
179

            
180
/// A description of a DMA Channel.
181
pub trait DmaChannel: Sized + crate::private::Sealed {
182
    /// A description of the RX half of a DMA Channel.
183
    type Rx: DmaRxChannel + From<Self>;
184

            
185
    /// A description of the TX half of a DMA Channel.
186
    type Tx: DmaTxChannel + From<Self>;
187

            
188
    /// Splits the DMA channel into its RX and TX halves.
189
    #[cfg(dma_separate_in_out_interrupts)] // TODO relax this to allow splitting on all chips
190
    fn split(self) -> (Self::Rx, Self::Tx) {
191
        // This function is exposed safely on chips that have separate IN and OUT
192
        // interrupt handlers.
193
        // TODO: this includes the P4 as well.
194
        unsafe { self.split_internal(Internal) }
195
    }
196

            
197
    /// Splits the DMA channel into its RX and TX halves.
198
    ///
199
    /// # Safety
200
    ///
201
    /// Must only be used if the separate halves are used by the same peripheral.
202
    unsafe fn split_internal(self, _: Internal) -> (Self::Rx, Self::Tx);
203
}
204

            
205
#[doc(hidden)]
206
pub trait DmaChannelExt: DmaChannel {
207
    fn rx_interrupts() -> impl InterruptAccess<DmaRxInterrupt>;
208
    fn tx_interrupts() -> impl InterruptAccess<DmaTxInterrupt>;
209
}
210

            
211
macro_rules! impl_channel_common {
212
    ($peri:ident, $instance:ident) => {
213
        paste::paste! {
214
            impl<'d> DmaChannel for $instance<'d> {
215
                type Rx = [<$peri RxChannel>]<'d>;
216
                type Tx = [<$peri TxChannel>]<'d>;
217

            
218
                unsafe fn split_internal(self, _: $crate::private::Internal) -> (Self::Rx, Self::Tx) {
219
                    unsafe {
220
                        (
221
                            [<$peri RxChannel>](Self::steal().into()),
222
                            [<$peri TxChannel>](Self::steal().into()),
223
                        )
224
                    }
225
                }
226
            }
227

            
228
            // Convert concrete channel into erased TX/RX half structs
229
            impl<'d> From<$instance<'d>> for [<$peri RxChannel>]<'d> {
230
                fn from(this: $instance<'d>) -> [<$peri RxChannel>]<'d> {
231
                    [<$peri RxChannel>](this.into())
232
                }
233
            }
234

            
235
            impl<'d> From<$instance<'d>> for [<$peri TxChannel>]<'d> {
236
                fn from(this: $instance<'d>) -> [<$peri TxChannel>]<'d> {
237
                    [<$peri TxChannel>](this.into())
238
                }
239
            }
240

            
241
            impl crate::dma::DmaChannelExt for $instance<'_> {
242
                fn rx_interrupts() -> impl InterruptAccess<DmaRxInterrupt> {
243
                    [<$peri RxChannel>]::from(unsafe { Self::steal() })
244
                }
245

            
246
                fn tx_interrupts() -> impl InterruptAccess<DmaTxInterrupt> {
247
                    [<$peri TxChannel>]::from(unsafe { Self::steal() })
248
                }
249
            }
250
        }
251
    };
252
}
253
pub(crate) use impl_channel_common;