| 1 | #![macro_use ] |
| 2 | |
| 3 | use core::future::Future; |
| 4 | use core::pin::Pin; |
| 5 | use core::sync::atomic::{fence, Ordering}; |
| 6 | use core::task::{Context, Poll}; |
| 7 | |
| 8 | use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; |
| 9 | use embassy_sync::waitqueue::AtomicWaker; |
| 10 | |
| 11 | use super::word::{Word, WordSize}; |
| 12 | use super::{AnyChannel, Channel, Dir, Request, STATE}; |
| 13 | use crate::interrupt::typelevel::Interrupt; |
| 14 | use crate::interrupt::Priority; |
| 15 | use crate::pac; |
| 16 | use crate::pac::gpdma::vals; |
| 17 | |
| 18 | pub(crate) struct ChannelInfo { |
| 19 | pub(crate) dma: pac::gpdma::Gpdma, |
| 20 | pub(crate) num: usize, |
| 21 | #[cfg (feature = "_dual-core" )] |
| 22 | pub(crate) irq: pac::Interrupt, |
| 23 | } |
| 24 | |
| 25 | /// GPDMA transfer options. |
| 26 | #[derive (Debug, Copy, Clone, PartialEq, Eq)] |
| 27 | #[cfg_attr (feature = "defmt" , derive(defmt::Format))] |
| 28 | #[non_exhaustive ] |
| 29 | pub struct TransferOptions {} |
| 30 | |
| 31 | impl Default for TransferOptions { |
| 32 | fn default() -> Self { |
| 33 | Self {} |
| 34 | } |
| 35 | } |
| 36 | |
| 37 | impl From<WordSize> for vals::Dw { |
| 38 | fn from(raw: WordSize) -> Self { |
| 39 | match raw { |
| 40 | WordSize::OneByte => Self::BYTE, |
| 41 | WordSize::TwoBytes => Self::HALF_WORD, |
| 42 | WordSize::FourBytes => Self::WORD, |
| 43 | } |
| 44 | } |
| 45 | } |
| 46 | |
| 47 | pub(crate) struct ChannelState { |
| 48 | waker: AtomicWaker, |
| 49 | } |
| 50 | |
| 51 | impl ChannelState { |
| 52 | pub(crate) const NEW: Self = Self { |
| 53 | waker: AtomicWaker::new(), |
| 54 | }; |
| 55 | } |
| 56 | |
| 57 | /// safety: must be called only once |
| 58 | pub(crate) unsafe fn init(cs: critical_section::CriticalSection, irq_priority: Priority) { |
| 59 | foreach_interrupt! { |
| 60 | ($peri:ident, gpdma, $block:ident, $signal_name:ident, $irq:ident) => { |
| 61 | crate::interrupt::typelevel::$irq::set_priority_with_cs(cs, irq_priority); |
| 62 | #[cfg(not(feature = "_dual-core" ))] |
| 63 | crate::interrupt::typelevel::$irq::enable(); |
| 64 | }; |
| 65 | } |
| 66 | crate::_generated::init_gpdma(); |
| 67 | } |
| 68 | |
| 69 | impl AnyChannel { |
| 70 | /// Safety: Must be called with a matching set of parameters for a valid dma channel |
| 71 | pub(crate) unsafe fn on_irq(&self) { |
| 72 | let info = self.info(); |
| 73 | #[cfg (feature = "_dual-core" )] |
| 74 | { |
| 75 | use embassy_hal_internal::interrupt::InterruptExt as _; |
| 76 | info.irq.enable(); |
| 77 | } |
| 78 | |
| 79 | let state = &STATE[self.id as usize]; |
| 80 | |
| 81 | let ch = info.dma.ch(info.num); |
| 82 | let sr = ch.sr().read(); |
| 83 | |
| 84 | if sr.dtef() { |
| 85 | panic!( |
| 86 | "DMA: data transfer error on DMA@{:08x} channel {}" , |
| 87 | info.dma.as_ptr() as u32, |
| 88 | info.num |
| 89 | ); |
| 90 | } |
| 91 | if sr.usef() { |
| 92 | panic!( |
| 93 | "DMA: user settings error on DMA@{:08x} channel {}" , |
| 94 | info.dma.as_ptr() as u32, |
| 95 | info.num |
| 96 | ); |
| 97 | } |
| 98 | |
| 99 | if sr.suspf() || sr.tcf() { |
| 100 | // disable all xxIEs to prevent the irq from firing again. |
| 101 | ch.cr().write(|_| {}); |
| 102 | |
| 103 | // Wake the future. It'll look at tcf and see it's set. |
| 104 | state.waker.wake(); |
| 105 | } |
| 106 | } |
| 107 | } |
| 108 | |
| 109 | /// DMA transfer. |
| 110 | #[must_use = "futures do nothing unless you `.await` or poll them" ] |
| 111 | pub struct Transfer<'a> { |
| 112 | channel: PeripheralRef<'a, AnyChannel>, |
| 113 | } |
| 114 | |
| 115 | impl<'a> Transfer<'a> { |
| 116 | /// Create a new read DMA transfer (peripheral to memory). |
| 117 | pub unsafe fn new_read<W: Word>( |
| 118 | channel: impl Peripheral<P = impl Channel> + 'a, |
| 119 | request: Request, |
| 120 | peri_addr: *mut W, |
| 121 | buf: &'a mut [W], |
| 122 | options: TransferOptions, |
| 123 | ) -> Self { |
| 124 | Self::new_read_raw(channel, request, peri_addr, buf, options) |
| 125 | } |
| 126 | |
| 127 | /// Create a new read DMA transfer (peripheral to memory), using raw pointers. |
| 128 | pub unsafe fn new_read_raw<W: Word>( |
| 129 | channel: impl Peripheral<P = impl Channel> + 'a, |
| 130 | request: Request, |
| 131 | peri_addr: *mut W, |
| 132 | buf: *mut [W], |
| 133 | options: TransferOptions, |
| 134 | ) -> Self { |
| 135 | into_ref!(channel); |
| 136 | |
| 137 | Self::new_inner( |
| 138 | channel.map_into(), |
| 139 | request, |
| 140 | Dir::PeripheralToMemory, |
| 141 | peri_addr as *const u32, |
| 142 | buf as *mut W as *mut u32, |
| 143 | buf.len(), |
| 144 | true, |
| 145 | W::size(), |
| 146 | options, |
| 147 | ) |
| 148 | } |
| 149 | |
| 150 | /// Create a new write DMA transfer (memory to peripheral). |
| 151 | pub unsafe fn new_write<W: Word>( |
| 152 | channel: impl Peripheral<P = impl Channel> + 'a, |
| 153 | request: Request, |
| 154 | buf: &'a [W], |
| 155 | peri_addr: *mut W, |
| 156 | options: TransferOptions, |
| 157 | ) -> Self { |
| 158 | Self::new_write_raw(channel, request, buf, peri_addr, options) |
| 159 | } |
| 160 | |
| 161 | /// Create a new write DMA transfer (memory to peripheral), using raw pointers. |
| 162 | pub unsafe fn new_write_raw<W: Word>( |
| 163 | channel: impl Peripheral<P = impl Channel> + 'a, |
| 164 | request: Request, |
| 165 | buf: *const [W], |
| 166 | peri_addr: *mut W, |
| 167 | options: TransferOptions, |
| 168 | ) -> Self { |
| 169 | into_ref!(channel); |
| 170 | |
| 171 | Self::new_inner( |
| 172 | channel.map_into(), |
| 173 | request, |
| 174 | Dir::MemoryToPeripheral, |
| 175 | peri_addr as *const u32, |
| 176 | buf as *const W as *mut u32, |
| 177 | buf.len(), |
| 178 | true, |
| 179 | W::size(), |
| 180 | options, |
| 181 | ) |
| 182 | } |
| 183 | |
| 184 | /// Create a new write DMA transfer (memory to peripheral), writing the same value repeatedly. |
| 185 | pub unsafe fn new_write_repeated<W: Word>( |
| 186 | channel: impl Peripheral<P = impl Channel> + 'a, |
| 187 | request: Request, |
| 188 | repeated: &'a W, |
| 189 | count: usize, |
| 190 | peri_addr: *mut W, |
| 191 | options: TransferOptions, |
| 192 | ) -> Self { |
| 193 | into_ref!(channel); |
| 194 | |
| 195 | Self::new_inner( |
| 196 | channel.map_into(), |
| 197 | request, |
| 198 | Dir::MemoryToPeripheral, |
| 199 | peri_addr as *const u32, |
| 200 | repeated as *const W as *mut u32, |
| 201 | count, |
| 202 | false, |
| 203 | W::size(), |
| 204 | options, |
| 205 | ) |
| 206 | } |
| 207 | |
| 208 | unsafe fn new_inner( |
| 209 | channel: PeripheralRef<'a, AnyChannel>, |
| 210 | request: Request, |
| 211 | dir: Dir, |
| 212 | peri_addr: *const u32, |
| 213 | mem_addr: *mut u32, |
| 214 | mem_len: usize, |
| 215 | incr_mem: bool, |
| 216 | data_size: WordSize, |
| 217 | _options: TransferOptions, |
| 218 | ) -> Self { |
| 219 | // BNDT is specified as bytes, not as number of transfers. |
| 220 | let Ok(bndt) = (mem_len * data_size.bytes()).try_into() else { |
| 221 | panic!("DMA transfers may not be larger than 65535 bytes." ); |
| 222 | }; |
| 223 | |
| 224 | let info = channel.info(); |
| 225 | let ch = info.dma.ch(info.num); |
| 226 | |
| 227 | // "Preceding reads and writes cannot be moved past subsequent writes." |
| 228 | fence(Ordering::SeqCst); |
| 229 | |
| 230 | let this = Self { channel }; |
| 231 | |
| 232 | ch.cr().write(|w| w.set_reset(true)); |
| 233 | ch.fcr().write(|w| w.0 = 0xFFFF_FFFF); // clear all irqs |
| 234 | ch.llr().write(|_| {}); // no linked list |
| 235 | ch.tr1().write(|w| { |
| 236 | w.set_sdw(data_size.into()); |
| 237 | w.set_ddw(data_size.into()); |
| 238 | w.set_sinc(dir == Dir::MemoryToPeripheral && incr_mem); |
| 239 | w.set_dinc(dir == Dir::PeripheralToMemory && incr_mem); |
| 240 | }); |
| 241 | ch.tr2().write(|w| { |
| 242 | w.set_dreq(match dir { |
| 243 | Dir::MemoryToPeripheral => vals::Dreq::DESTINATION_PERIPHERAL, |
| 244 | Dir::PeripheralToMemory => vals::Dreq::SOURCE_PERIPHERAL, |
| 245 | }); |
| 246 | w.set_reqsel(request); |
| 247 | }); |
| 248 | ch.tr3().write(|_| {}); // no address offsets. |
| 249 | ch.br1().write(|w| w.set_bndt(bndt)); |
| 250 | |
| 251 | match dir { |
| 252 | Dir::MemoryToPeripheral => { |
| 253 | ch.sar().write_value(mem_addr as _); |
| 254 | ch.dar().write_value(peri_addr as _); |
| 255 | } |
| 256 | Dir::PeripheralToMemory => { |
| 257 | ch.sar().write_value(peri_addr as _); |
| 258 | ch.dar().write_value(mem_addr as _); |
| 259 | } |
| 260 | } |
| 261 | |
| 262 | ch.cr().write(|w| { |
| 263 | // Enable interrupts |
| 264 | w.set_tcie(true); |
| 265 | w.set_useie(true); |
| 266 | w.set_dteie(true); |
| 267 | w.set_suspie(true); |
| 268 | |
| 269 | // Start it |
| 270 | w.set_en(true); |
| 271 | }); |
| 272 | |
| 273 | this |
| 274 | } |
| 275 | |
| 276 | /// Request the transfer to stop. |
| 277 | /// |
| 278 | /// This doesn't immediately stop the transfer, you have to wait until [`is_running`](Self::is_running) returns false. |
| 279 | pub fn request_stop(&mut self) { |
| 280 | let info = self.channel.info(); |
| 281 | let ch = info.dma.ch(info.num); |
| 282 | |
| 283 | ch.cr().modify(|w| w.set_susp(true)) |
| 284 | } |
| 285 | |
| 286 | /// Return whether this transfer is still running. |
| 287 | /// |
| 288 | /// If this returns `false`, it can be because either the transfer finished, or |
| 289 | /// it was requested to stop early with [`request_stop`](Self::request_stop). |
| 290 | pub fn is_running(&mut self) -> bool { |
| 291 | let info = self.channel.info(); |
| 292 | let ch = info.dma.ch(info.num); |
| 293 | |
| 294 | let sr = ch.sr().read(); |
| 295 | !sr.tcf() && !sr.suspf() |
| 296 | } |
| 297 | |
| 298 | /// Gets the total remaining transfers for the channel |
| 299 | /// Note: this will be zero for transfers that completed without cancellation. |
| 300 | pub fn get_remaining_transfers(&self) -> u16 { |
| 301 | let info = self.channel.info(); |
| 302 | let ch = info.dma.ch(info.num); |
| 303 | |
| 304 | ch.br1().read().bndt() |
| 305 | } |
| 306 | |
| 307 | /// Blocking wait until the transfer finishes. |
| 308 | pub fn blocking_wait(mut self) { |
| 309 | while self.is_running() {} |
| 310 | |
| 311 | // "Subsequent reads and writes cannot be moved ahead of preceding reads." |
| 312 | fence(Ordering::SeqCst); |
| 313 | |
| 314 | core::mem::forget(self); |
| 315 | } |
| 316 | } |
| 317 | |
| 318 | impl<'a> Drop for Transfer<'a> { |
| 319 | fn drop(&mut self) { |
| 320 | self.request_stop(); |
| 321 | while self.is_running() {} |
| 322 | |
| 323 | // "Subsequent reads and writes cannot be moved ahead of preceding reads." |
| 324 | fence(order:Ordering::SeqCst); |
| 325 | } |
| 326 | } |
| 327 | |
| 328 | impl<'a> Unpin for Transfer<'a> {} |
| 329 | impl<'a> Future for Transfer<'a> { |
| 330 | type Output = (); |
| 331 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { |
| 332 | let state: &ChannelState = &STATE[self.channel.id as usize]; |
| 333 | state.waker.register(cx.waker()); |
| 334 | |
| 335 | if self.is_running() { |
| 336 | Poll::Pending |
| 337 | } else { |
| 338 | Poll::Ready(()) |
| 339 | } |
| 340 | } |
| 341 | } |
| 342 | |