diff options
Diffstat (limited to 'embassy-imxrt/src/dma.rs')
| -rw-r--r-- | embassy-imxrt/src/dma.rs | 418 |
1 files changed, 418 insertions, 0 deletions
diff --git a/embassy-imxrt/src/dma.rs b/embassy-imxrt/src/dma.rs new file mode 100644 index 000000000..e141447f3 --- /dev/null +++ b/embassy-imxrt/src/dma.rs | |||
| @@ -0,0 +1,418 @@ | |||
| 1 | //! DMA driver. | ||
| 2 | |||
| 3 | use core::future::Future; | ||
| 4 | use core::pin::Pin; | ||
| 5 | use core::sync::atomic::{compiler_fence, Ordering}; | ||
| 6 | use core::task::{Context, Poll}; | ||
| 7 | |||
| 8 | use embassy_hal_internal::{impl_peripheral, Peri, PeripheralType}; | ||
| 9 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 10 | use pac::dma0::channel::cfg::Periphreqen; | ||
| 11 | use pac::dma0::channel::xfercfg::{Dstinc, Srcinc, Width}; | ||
| 12 | |||
| 13 | use crate::clocks::enable_and_reset; | ||
| 14 | use crate::interrupt::InterruptExt; | ||
| 15 | use crate::peripherals::DMA0; | ||
| 16 | use crate::sealed::Sealed; | ||
| 17 | use crate::{interrupt, pac, peripherals, BitIter}; | ||
| 18 | |||
| 19 | #[cfg(feature = "rt")] | ||
| 20 | #[interrupt] | ||
| 21 | fn DMA0() { | ||
| 22 | let reg = unsafe { crate::pac::Dma0::steal() }; | ||
| 23 | |||
| 24 | if reg.intstat().read().activeerrint().bit() { | ||
| 25 | let err = reg.errint0().read().bits(); | ||
| 26 | |||
| 27 | for channel in BitIter(err) { | ||
| 28 | error!("DMA error interrupt on channel {}!", channel); | ||
| 29 | reg.errint0().write(|w| unsafe { w.err().bits(1 << channel) }); | ||
| 30 | CHANNEL_WAKERS[channel as usize].wake(); | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | if reg.intstat().read().activeint().bit() { | ||
| 35 | let ia = reg.inta0().read().bits(); | ||
| 36 | |||
| 37 | for channel in BitIter(ia) { | ||
| 38 | reg.inta0().write(|w| unsafe { w.ia().bits(1 << channel) }); | ||
| 39 | CHANNEL_WAKERS[channel as usize].wake(); | ||
| 40 | } | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | /// Initialize DMA controllers (DMA0 only, for now) | ||
| 45 | pub(crate) unsafe fn init() { | ||
| 46 | let sysctl0 = crate::pac::Sysctl0::steal(); | ||
| 47 | let dmactl0 = crate::pac::Dma0::steal(); | ||
| 48 | |||
| 49 | enable_and_reset::<DMA0>(); | ||
| 50 | |||
| 51 | interrupt::DMA0.disable(); | ||
| 52 | interrupt::DMA0.set_priority(interrupt::Priority::P3); | ||
| 53 | |||
| 54 | dmactl0.ctrl().modify(|_, w| w.enable().set_bit()); | ||
| 55 | |||
| 56 | // Set channel descriptor SRAM base address | ||
| 57 | // Descriptor base must be 1K aligned | ||
| 58 | let descriptor_base = core::ptr::addr_of!(DESCRIPTORS.descs) as u32; | ||
| 59 | dmactl0.srambase().write(|w| w.bits(descriptor_base)); | ||
| 60 | |||
| 61 | // Ensure AHB priority it highest (M4 == DMAC0) | ||
| 62 | sysctl0.ahbmatrixprior().modify(|_, w| w.m4().bits(0)); | ||
| 63 | |||
| 64 | interrupt::DMA0.unpend(); | ||
| 65 | interrupt::DMA0.enable(); | ||
| 66 | } | ||
| 67 | |||
| 68 | /// DMA read. | ||
| 69 | /// | ||
| 70 | /// SAFETY: Slice must point to a valid location reachable by DMA. | ||
| 71 | pub unsafe fn read<'a, C: Channel, W: Word>(ch: Peri<'a, C>, from: *const W, to: *mut [W]) -> Transfer<'a, C> { | ||
| 72 | let count = ((to.len() / W::size() as usize) - 1) as isize; | ||
| 73 | |||
| 74 | copy_inner( | ||
| 75 | ch, | ||
| 76 | from as *const u32, | ||
| 77 | (to as *mut u32).byte_offset(count * W::size()), | ||
| 78 | W::width(), | ||
| 79 | count, | ||
| 80 | false, | ||
| 81 | true, | ||
| 82 | true, | ||
| 83 | ) | ||
| 84 | } | ||
| 85 | |||
| 86 | /// DMA write. | ||
| 87 | /// | ||
| 88 | /// SAFETY: Slice must point to a valid location reachable by DMA. | ||
| 89 | pub unsafe fn write<'a, C: Channel, W: Word>(ch: Peri<'a, C>, from: *const [W], to: *mut W) -> Transfer<'a, C> { | ||
| 90 | let count = ((from.len() / W::size() as usize) - 1) as isize; | ||
| 91 | |||
| 92 | copy_inner( | ||
| 93 | ch, | ||
| 94 | (from as *const u32).byte_offset(count * W::size()), | ||
| 95 | to as *mut u32, | ||
| 96 | W::width(), | ||
| 97 | count, | ||
| 98 | true, | ||
| 99 | false, | ||
| 100 | true, | ||
| 101 | ) | ||
| 102 | } | ||
| 103 | |||
| 104 | /// DMA copy between slices. | ||
| 105 | /// | ||
| 106 | /// SAFETY: Slices must point to locations reachable by DMA. | ||
| 107 | pub unsafe fn copy<'a, C: Channel, W: Word>(ch: Peri<'a, C>, from: &[W], to: &mut [W]) -> Transfer<'a, C> { | ||
| 108 | let from_len = from.len(); | ||
| 109 | let to_len = to.len(); | ||
| 110 | assert_eq!(from_len, to_len); | ||
| 111 | |||
| 112 | let count = ((from_len / W::size() as usize) - 1) as isize; | ||
| 113 | |||
| 114 | copy_inner( | ||
| 115 | ch, | ||
| 116 | from.as_ptr().byte_offset(count * W::size()) as *const u32, | ||
| 117 | to.as_mut_ptr().byte_offset(count * W::size()) as *mut u32, | ||
| 118 | W::width(), | ||
| 119 | count, | ||
| 120 | true, | ||
| 121 | true, | ||
| 122 | false, | ||
| 123 | ) | ||
| 124 | } | ||
| 125 | |||
| 126 | fn copy_inner<'a, C: Channel>( | ||
| 127 | ch: Peri<'a, C>, | ||
| 128 | from: *const u32, | ||
| 129 | to: *mut u32, | ||
| 130 | width: Width, | ||
| 131 | count: isize, | ||
| 132 | incr_read: bool, | ||
| 133 | incr_write: bool, | ||
| 134 | periph: bool, | ||
| 135 | ) -> Transfer<'a, C> { | ||
| 136 | let p = ch.regs(); | ||
| 137 | |||
| 138 | unsafe { | ||
| 139 | DESCRIPTORS.descs[ch.number() as usize].src = from as u32; | ||
| 140 | DESCRIPTORS.descs[ch.number() as usize].dest = to as u32; | ||
| 141 | } | ||
| 142 | |||
| 143 | compiler_fence(Ordering::SeqCst); | ||
| 144 | |||
| 145 | p.errint0().write(|w| unsafe { w.err().bits(1 << ch.number()) }); | ||
| 146 | p.inta0().write(|w| unsafe { w.ia().bits(1 << ch.number()) }); | ||
| 147 | |||
| 148 | p.channel(ch.number().into()).cfg().write(|w| { | ||
| 149 | unsafe { w.chpriority().bits(0) } | ||
| 150 | .periphreqen() | ||
| 151 | .variant(match periph { | ||
| 152 | false => Periphreqen::Disabled, | ||
| 153 | true => Periphreqen::Enabled, | ||
| 154 | }) | ||
| 155 | .hwtrigen() | ||
| 156 | .clear_bit() | ||
| 157 | }); | ||
| 158 | |||
| 159 | p.intenset0().write(|w| unsafe { w.inten().bits(1 << ch.number()) }); | ||
| 160 | |||
| 161 | p.channel(ch.number().into()).xfercfg().write(|w| { | ||
| 162 | unsafe { w.xfercount().bits(count as u16) } | ||
| 163 | .cfgvalid() | ||
| 164 | .set_bit() | ||
| 165 | .clrtrig() | ||
| 166 | .set_bit() | ||
| 167 | .reload() | ||
| 168 | .clear_bit() | ||
| 169 | .setinta() | ||
| 170 | .set_bit() | ||
| 171 | .width() | ||
| 172 | .variant(width) | ||
| 173 | .srcinc() | ||
| 174 | .variant(match incr_read { | ||
| 175 | false => Srcinc::NoIncrement, | ||
| 176 | true => Srcinc::WidthX1, | ||
| 177 | // REVISIT: what about WidthX2 and WidthX4? | ||
| 178 | }) | ||
| 179 | .dstinc() | ||
| 180 | .variant(match incr_write { | ||
| 181 | false => Dstinc::NoIncrement, | ||
| 182 | true => Dstinc::WidthX1, | ||
| 183 | // REVISIT: what about WidthX2 and WidthX4? | ||
| 184 | }) | ||
| 185 | }); | ||
| 186 | |||
| 187 | p.enableset0().write(|w| unsafe { w.ena().bits(1 << ch.number()) }); | ||
| 188 | |||
| 189 | p.channel(ch.number().into()) | ||
| 190 | .xfercfg() | ||
| 191 | .modify(|_, w| w.swtrig().set_bit()); | ||
| 192 | |||
| 193 | compiler_fence(Ordering::SeqCst); | ||
| 194 | |||
| 195 | Transfer::new(ch) | ||
| 196 | } | ||
| 197 | |||
| 198 | /// DMA transfer driver. | ||
| 199 | #[must_use = "futures do nothing unless you `.await` or poll them"] | ||
| 200 | pub struct Transfer<'a, C: Channel> { | ||
| 201 | channel: Peri<'a, C>, | ||
| 202 | } | ||
| 203 | |||
| 204 | impl<'a, C: Channel> Transfer<'a, C> { | ||
| 205 | pub(crate) fn new(channel: Peri<'a, C>) -> Self { | ||
| 206 | Self { channel } | ||
| 207 | } | ||
| 208 | |||
| 209 | pub(crate) fn abort(&mut self) -> usize { | ||
| 210 | let p = self.channel.regs(); | ||
| 211 | |||
| 212 | p.abort0().write(|w| w.channel(self.channel.number()).set_bit()); | ||
| 213 | while p.busy0().read().bsy().bits() & (1 << self.channel.number()) != 0 {} | ||
| 214 | |||
| 215 | p.enableclr0() | ||
| 216 | .write(|w| unsafe { w.clr().bits(1 << self.channel.number()) }); | ||
| 217 | |||
| 218 | let width: u8 = p | ||
| 219 | .channel(self.channel.number().into()) | ||
| 220 | .xfercfg() | ||
| 221 | .read() | ||
| 222 | .width() | ||
| 223 | .variant() | ||
| 224 | .unwrap() | ||
| 225 | .into(); | ||
| 226 | |||
| 227 | let count = p | ||
| 228 | .channel(self.channel.number().into()) | ||
| 229 | .xfercfg() | ||
| 230 | .read() | ||
| 231 | .xfercount() | ||
| 232 | .bits() | ||
| 233 | + 1; | ||
| 234 | |||
| 235 | usize::from(count) * usize::from(width) | ||
| 236 | } | ||
| 237 | } | ||
| 238 | |||
| 239 | impl<'a, C: Channel> Drop for Transfer<'a, C> { | ||
| 240 | fn drop(&mut self) { | ||
| 241 | self.abort(); | ||
| 242 | } | ||
| 243 | } | ||
| 244 | |||
| 245 | impl<'a, C: Channel> Unpin for Transfer<'a, C> {} | ||
| 246 | impl<'a, C: Channel> Future for Transfer<'a, C> { | ||
| 247 | type Output = (); | ||
| 248 | |||
| 249 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||
| 250 | // Re-register the waker on each call to poll() because any calls to | ||
| 251 | // wake will deregister the waker. | ||
| 252 | CHANNEL_WAKERS[self.channel.number() as usize].register(cx.waker()); | ||
| 253 | |||
| 254 | if self.channel.regs().active0().read().act().bits() & (1 << self.channel.number()) == 0 { | ||
| 255 | Poll::Ready(()) | ||
| 256 | } else { | ||
| 257 | Poll::Pending | ||
| 258 | } | ||
| 259 | } | ||
| 260 | } | ||
| 261 | |||
| 262 | /// DMA channel descriptor | ||
| 263 | #[derive(Copy, Clone)] | ||
| 264 | #[repr(C)] | ||
| 265 | struct Descriptor { | ||
| 266 | reserved: u32, | ||
| 267 | src: u32, | ||
| 268 | dest: u32, | ||
| 269 | link: u32, | ||
| 270 | } | ||
| 271 | |||
| 272 | impl Descriptor { | ||
| 273 | const fn new() -> Self { | ||
| 274 | Self { | ||
| 275 | reserved: 0, | ||
| 276 | src: 0, | ||
| 277 | dest: 0, | ||
| 278 | link: 0, | ||
| 279 | } | ||
| 280 | } | ||
| 281 | } | ||
| 282 | |||
| 283 | #[repr(align(1024))] | ||
| 284 | struct Descriptors { | ||
| 285 | descs: [Descriptor; CHANNEL_COUNT], | ||
| 286 | } | ||
| 287 | |||
| 288 | impl Descriptors { | ||
| 289 | const fn new() -> Self { | ||
| 290 | Self { | ||
| 291 | descs: [const { Descriptor::new() }; CHANNEL_COUNT], | ||
| 292 | } | ||
| 293 | } | ||
| 294 | } | ||
| 295 | |||
| 296 | static mut DESCRIPTORS: Descriptors = Descriptors::new(); | ||
| 297 | static CHANNEL_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [const { AtomicWaker::new() }; CHANNEL_COUNT]; | ||
| 298 | pub(crate) const CHANNEL_COUNT: usize = 33; | ||
| 299 | |||
| 300 | /// DMA channel interface. | ||
| 301 | #[allow(private_bounds)] | ||
| 302 | pub trait Channel: PeripheralType + Sealed + Into<AnyChannel> + Sized + 'static { | ||
| 303 | /// Channel number. | ||
| 304 | fn number(&self) -> u8; | ||
| 305 | |||
| 306 | /// Channel registry block. | ||
| 307 | fn regs(&self) -> &'static pac::dma0::RegisterBlock { | ||
| 308 | unsafe { &*crate::pac::Dma0::ptr() } | ||
| 309 | } | ||
| 310 | } | ||
| 311 | |||
| 312 | /// DMA word. | ||
| 313 | #[allow(private_bounds)] | ||
| 314 | pub trait Word: Sealed { | ||
| 315 | /// Transfer width. | ||
| 316 | fn width() -> Width; | ||
| 317 | |||
| 318 | /// Size in bytes for the width. | ||
| 319 | fn size() -> isize; | ||
| 320 | } | ||
| 321 | |||
| 322 | impl Sealed for u8 {} | ||
| 323 | impl Word for u8 { | ||
| 324 | fn width() -> Width { | ||
| 325 | Width::Bit8 | ||
| 326 | } | ||
| 327 | |||
| 328 | fn size() -> isize { | ||
| 329 | 1 | ||
| 330 | } | ||
| 331 | } | ||
| 332 | |||
| 333 | impl Sealed for u16 {} | ||
| 334 | impl Word for u16 { | ||
| 335 | fn width() -> Width { | ||
| 336 | Width::Bit16 | ||
| 337 | } | ||
| 338 | |||
| 339 | fn size() -> isize { | ||
| 340 | 2 | ||
| 341 | } | ||
| 342 | } | ||
| 343 | |||
| 344 | impl Sealed for u32 {} | ||
| 345 | impl Word for u32 { | ||
| 346 | fn width() -> Width { | ||
| 347 | Width::Bit32 | ||
| 348 | } | ||
| 349 | |||
| 350 | fn size() -> isize { | ||
| 351 | 4 | ||
| 352 | } | ||
| 353 | } | ||
| 354 | |||
| 355 | /// Type erased DMA channel. | ||
| 356 | pub struct AnyChannel { | ||
| 357 | number: u8, | ||
| 358 | } | ||
| 359 | |||
| 360 | impl_peripheral!(AnyChannel); | ||
| 361 | |||
| 362 | impl Sealed for AnyChannel {} | ||
| 363 | impl Channel for AnyChannel { | ||
| 364 | fn number(&self) -> u8 { | ||
| 365 | self.number | ||
| 366 | } | ||
| 367 | } | ||
| 368 | |||
| 369 | macro_rules! channel { | ||
| 370 | ($name:ident, $num:expr) => { | ||
| 371 | impl Sealed for peripherals::$name {} | ||
| 372 | impl Channel for peripherals::$name { | ||
| 373 | fn number(&self) -> u8 { | ||
| 374 | $num | ||
| 375 | } | ||
| 376 | } | ||
| 377 | |||
| 378 | impl From<peripherals::$name> for crate::dma::AnyChannel { | ||
| 379 | fn from(val: peripherals::$name) -> Self { | ||
| 380 | Self { number: val.number() } | ||
| 381 | } | ||
| 382 | } | ||
| 383 | }; | ||
| 384 | } | ||
| 385 | |||
| 386 | channel!(DMA0_CH0, 0); | ||
| 387 | channel!(DMA0_CH1, 1); | ||
| 388 | channel!(DMA0_CH2, 2); | ||
| 389 | channel!(DMA0_CH3, 3); | ||
| 390 | channel!(DMA0_CH4, 4); | ||
| 391 | channel!(DMA0_CH5, 5); | ||
| 392 | channel!(DMA0_CH6, 6); | ||
| 393 | channel!(DMA0_CH7, 7); | ||
| 394 | channel!(DMA0_CH8, 8); | ||
| 395 | channel!(DMA0_CH9, 9); | ||
| 396 | channel!(DMA0_CH10, 10); | ||
| 397 | channel!(DMA0_CH11, 11); | ||
| 398 | channel!(DMA0_CH12, 12); | ||
| 399 | channel!(DMA0_CH13, 13); | ||
| 400 | channel!(DMA0_CH14, 14); | ||
| 401 | channel!(DMA0_CH15, 15); | ||
| 402 | channel!(DMA0_CH16, 16); | ||
| 403 | channel!(DMA0_CH17, 17); | ||
| 404 | channel!(DMA0_CH18, 18); | ||
| 405 | channel!(DMA0_CH19, 19); | ||
| 406 | channel!(DMA0_CH20, 20); | ||
| 407 | channel!(DMA0_CH21, 21); | ||
| 408 | channel!(DMA0_CH22, 22); | ||
| 409 | channel!(DMA0_CH23, 23); | ||
| 410 | channel!(DMA0_CH24, 24); | ||
| 411 | channel!(DMA0_CH25, 25); | ||
| 412 | channel!(DMA0_CH26, 26); | ||
| 413 | channel!(DMA0_CH27, 27); | ||
| 414 | channel!(DMA0_CH28, 28); | ||
| 415 | channel!(DMA0_CH29, 29); | ||
| 416 | channel!(DMA0_CH30, 30); | ||
| 417 | channel!(DMA0_CH31, 31); | ||
| 418 | channel!(DMA0_CH32, 32); | ||
