//! DMA blocks for i.MX RT MCUs. //! //! eDMA works for all i.MX RT 1000 and 1100 series MCUs. //! The eDMA3 and eDMA4 is specifically targeted for the //! 1180 MCUs. #![no_std] pub mod dma; pub mod dmamux; pub mod tcd; pub mod element { /// An ID for an element size. #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[non_exhaustive] #[repr(u8)] pub enum ElementSize { /// `u8` transfer. U8 = 0, /// `u16` transfer. U16 = 1, /// `u32` transfer. U32 = 2, /// `u64` transfer. U64 = 3, } /// Describes a transferrable DMA element; basically, an unsigned /// integer of any size. pub trait Element: Copy + private::Sealed { /// An identifier describing the data transfer size /// /// Part of the TCD API; see documentation on TCD\[SSIZE\] /// and TCD\[DSIZE\] for more information. const DATA_TRANSFER_ID: ElementSize; } impl Element for u8 { const DATA_TRANSFER_ID: ElementSize = ElementSize::U8; } impl Element for u16 { const DATA_TRANSFER_ID: ElementSize = ElementSize::U16; } impl Element for u32 { const DATA_TRANSFER_ID: ElementSize = ElementSize::U32; } impl Element for u64 { const DATA_TRANSFER_ID: ElementSize = ElementSize::U64; } mod private { pub trait Sealed {} impl Sealed for u8 {} impl Sealed for u16 {} impl Sealed for u32 {} impl Sealed for u64 {} } } pub mod edma { use core::num::NonZeroU8; use crate::{dma::edma as dma, dmamux, element::ElementSize, tcd::edma as tcd}; use ral_registers::Instance; /// A DMA channel for an eDMA controller. /// /// The channel is three pointers wide. pub struct Channel { index: usize, dma: Instance, mux: Instance, } impl Channel { /// Create a new DMA channel. /// /// # Safety /// /// The channel formed by this call cannot alias another channel with the /// same index. The channel index must be valid for the hardware. pub const unsafe fn new( dma: Instance, mux: Instance, index: usize, ) -> Self { Self { index, dma, mux } } /// Returns the channel index. pub fn index(&self) -> usize { self.index } /// Enable the DMA channel. /// /// # Safety /// /// If the channel is incorrectly configured, it may access /// invalid memory. pub unsafe fn enable(&mut self) { ral_registers::write_reg!(dma, self.dma, SERQ, self.index as u8); } /// Disable the DMA channel. pub fn disable(&mut self) { ral_registers::write_reg!(dma, self.dma, CERQ, self.index as u8); } /// Returns `true` if the DMA channel is enabled. pub fn is_enabled(&self) -> bool { (1 << self.index) & ral_registers::read_reg!(dma, self.dma, ERQ) != 0 } fn tcd(&self) -> Instance { // Safety: caller claims the index is valid for // the DMA controller. unsafe { Instance::new_unchecked(&raw mut (*self.dma.as_ptr()).TCD[self.index]) } } /// Returns `true` if the hardware is signaling requests towards /// this DMA channel. pub fn is_hardware_signaling(&self) -> bool { (1 << self.index) & ral_registers::read_reg!(dma, self.dma, HRS) != 0 } /// Returns `true` if an interrupt has activated for this /// DMA channel. pub fn is_interrupt(&self) -> bool { (1 << self.index) & ral_registers::read_reg!(dma, self.dma, INT) != 0 } /// Clear the interrupt trigger. pub fn clear_interrupt(&self) { ral_registers::write_reg!(dma, self.dma, CINT, self.index as u8); } /// Returns `true` if this DMA channel has finished its transfer. pub fn is_done(&self) -> bool { ral_registers::read_reg!(tcd, self.tcd(), CSR, DONE == 1) } /// Clear the "done" signal from software. pub fn clear_done(&self) { ral_registers::write_reg!(dma, self.dma, CDNE, self.index as u8); } /// Returns `true` if this channel has an error. pub fn has_error(&self) -> bool { (1 << self.index) & ral_registers::read_reg!(dma, self.dma, ERR) != 0 } /// Clear this channel's error. pub fn clear_error(&self) { ral_registers::write_reg!(dma, self.dma, CERR, self.index as u8); } /// Returns `true` if this channel is actively transferring. pub fn is_active(&self) -> bool { ral_registers::read_reg!(tcd, self.tcd(), CSR, ACTIVE == 1) } /// Reset the control descriptor for the channel. /// /// Call this at least once, after acquiring the channel, to /// put it into a known-good state. pub fn reset(&mut self) { unsafe { self.tcd().as_ptr().write_bytes(0, 1) }; } /// Set the source address for a DMA transfer /// /// `saddr` should be a memory location that can provide the DMA controller /// with data. /// /// # Safety /// /// If the DMA channel is already enabled, the DMA engine may start reading this /// memory location. You must ensure that reads to `saddr` do not perform /// inappropriate side effects. You must ensure `saddr` is valid for the /// lifetime of the transfer. pub unsafe fn set_source_address(&self, saddr: *const ()) { ral_registers::write_reg!(tcd, self.tcd(), SADDR, saddr as u32); } /// Set the source offset *in bytes* /// /// `offset` could be negative, which would decrement the address. /// /// # Safety /// /// This method could allow a DMA engine to read beyond a buffer or /// address. You must ensure that the source is valid for these offsets. pub unsafe fn set_source_offset(&self, offset: i16) { ral_registers::write_reg!(tcd, self.tcd(), SOFF, offset); } /// Set the destination address for a DMA transfer /// /// `daddr` should be a memory location that can store data from the /// DMA controller. /// /// # Safety /// /// If the DMA channel is already enabled, the DMA engine may start /// writing to this address. You must ensure that writes to `daddr` /// are safe, and that the memory is valid for the lifetime of the /// transfer. pub unsafe fn set_destination_address(&self, daddr: *const ()) { ral_registers::write_reg!(tcd, self.tcd(), DADDR, daddr as u32); } /// Set the destination offset *in bytes* /// /// `offset` could be negative, which would decrement the address. /// /// # Safety /// /// This method could allow a DMA engine to write beyond the range of /// a buffer. You must ensure that the destination is valid for these /// offsets. pub unsafe fn set_destination_offset(&self, offset: i16) { ral_registers::write_reg!(tcd, self.tcd(), DOFF, offset); } /// Set the transfer attributes. /// /// The attributes describes the ways the source is read and /// the destination is written. The modulo mask allows the /// buffer to be treated as a circular buffer. /// /// # Safety /// /// Incorrect sizes and modulos may cause invalid memory accesses. pub unsafe fn set_attributes( &self, source_size: ElementSize, source_modulo: u8, destination_size: ElementSize, destination_modulo: u8, ) { ral_registers::write_reg!(tcd, self.tcd(), ATTR, SSIZE: source_size as u16, SMOD: source_modulo as u16, DSIZE: destination_size as u16, DMOD: destination_modulo as u16, ); } /// Set the source last address adjustment *in bytes* /// /// # Safety /// /// This could allow the DMA engine to reference an invalid source buffer. /// You must ensure that the adjustment performed by the DMA engine is /// valid, assuming that another DMA transfer immediately runs after the /// current transfer completes. pub unsafe fn set_source_last_address_adjustment(&self, adjustment: i32) { ral_registers::write_reg!(tcd, self.tcd(), SLAST, adjustment); } /// Set the destination last addrss adjustment *in bytes* /// /// # Safety /// /// This could allow the DMA engine to reference an invalid destination address. /// You must ensure that the adjustment performed by the DMA engine is /// valid, assuming that another DMA transfer immediately runs after the /// current transfer completes. pub unsafe fn set_destination_last_address_adjustment(&self, adjustment: i32) { ral_registers::write_reg!(tcd, self.tcd(), DLAST_SGA, adjustment); } /// Set the number of *bytes* to transfer per minor loop /// /// Describes how many bytes we should transfer for each DMA service request. /// Note that `nbytes` of `0` is interpreted as a 4GB transfer. /// /// # Safety /// /// This might allow the DMA engine to read beyond the source, or write beyond /// the destination. Caller must ensure that the number of bytes per minor loop /// is valid for the given transfer. pub unsafe fn set_minor_loop_bytes(&self, nbytes: u32) { ral_registers::write_reg!(tcd, self.tcd(), NBYTES, nbytes); } /// Tells the DMA channel how many transfer iterations to perform /// /// A 'transfer iteration' is a read from a source, and a write to a destination, with /// read and write sizes described by a minor loop. Each iteration requires a DMA /// service request, either from hardware or from software. The maximum number of iterations /// is 2^15. /// /// # Safety /// /// This may allow the DMA engine to read beyond the source, or write beyond /// the destination. Caller must ensure that the number of iterations is valid /// for the transfer. pub unsafe fn set_transfer_iterations(&mut self, iterations: u16) { let iterations = iterations & 0x7FFF; // Note that this is clearing the ELINK bit. We don't have support // for channel-to-channel linking right now. Clearing ELINK is intentional // to use the whole 15 bits for iterations. ral_registers::write_reg!(tcd, self.tcd(), CITER, iterations); ral_registers::write_reg!(tcd, self.tcd(), BITER, iterations); } /// Returns the beginning transfer iterations setting for the channel. /// /// This reflects the last call to `set_transfer_iterations`. pub fn beginning_transfer_iterations(&self) -> u16 { ral_registers::read_reg!(tcd, self.tcd(), BITER) } /// Enable or disable 'disable on completion' /// /// 'Disable on completion' lets the DMA channel automatically clear the request signal /// when it completes a transfer. pub fn set_disable_on_completion(&mut self, dreq: bool) { ral_registers::modify_reg!(tcd, self.tcd(), CSR, DREQ: dreq as u16); } /// Enable or disable interrupt generation when the transfer completes /// /// You're responsible for registering your interrupt handler. pub fn set_interrupt_on_completion(&mut self, intr: bool) { ral_registers::modify_reg!(tcd, self.tcd(), CSR, INTMAJOR: intr as u16); } /// Start a DMA transfer /// /// `start()` should be used to request service from the DMA controller. It's /// necessary for in-memory DMA transfers. Do not use it for hardware-initiated /// DMA transfers. DMA transfers that involve hardware will rely on the hardware /// to request DMA service. /// /// Flag is automatically cleared by hardware after it's asserted. pub fn start(&mut self) { ral_registers::modify_reg!(tcd, self.tcd(), CSR, START: 1); } /// Set the source signal for channel activation. /// /// The source is typically an upstream peripheral. /// Use `None` to clear the source. /// /// This call reads back the written source. If they're equal /// the return is `true`. The values may not be equal if the /// source is selected in another DMA channel. pub fn set_mux_source_signal(&self, signal: Option) -> bool { let signal = signal.map_or(0, NonZeroU8::get) as u32; ral_registers::modify_reg!(dmamux, self.mux, CHCFG[self.index], SOURCE: signal); signal == ral_registers::read_reg!(dmamux, self.mux, CHCFG[self.index], SOURCE) } /// Enable or disable the DMA MUX source signaling / triggering. /// /// Make sure to set up a source signal before enabling. pub fn set_mux_source_enable(&self, enable: bool) { ral_registers::modify_reg!(dmamux, self.mux, CHCFG[self.index], ENBL: enable as u32); } /// Set periodic triggering for this DMA channel. /// /// Only the first four DMA channels can periodically trigger /// on a PIT timer. Note that this isn't enabled. /// /// # Panics /// /// Panics if this isn't one of the first four DMA channels. pub fn set_mux_periodic(&self, periodic: bool) { assert!(self.index < 4); ral_registers::modify_reg!(dmamux, self.mux, CHCFG[self.index], TRIG: periodic as u32); } } } pub mod edma3 { use core::num::NonZeroU8; use crate::{dma::edma3 as dma, element::ElementSize, tcd::edma34 as tcd}; use ral_registers::Instance; /// A DMA channel for an eDMA controller. /// /// The channel is two pointers wide. pub struct Channel { index: usize, dma: Instance, } impl Channel { /// Create a new DMA channel. /// /// # Safety /// /// The channel formed by this call cannot alias another channel with the /// same index. The channel index must be valid for the hardware. pub const unsafe fn new(dma: Instance, index: usize) -> Self { Self { index, dma } } /// Enable the DMA channel. /// /// # Safety /// /// If the channel is incorrectly configured, it may access /// invalid memory. pub unsafe fn enable(&mut self) { ral_registers::modify_reg!(tcd, self.tcd(), CSR, ERQ: 1, DONE: 0); } /// Disable the DMA channel. pub fn disable(&mut self) { ral_registers::modify_reg!(tcd, self.tcd(), CSR, ERQ: 0, DONE: 0); } /// Returns `true` if the DMA channel is enabled. pub fn is_enabled(&self) -> bool { ral_registers::read_reg!(tcd, self.tcd(), CSR, ERQ != 0) } fn tcd(&self) -> Instance { // Safety: caller claims the index is valid for // the DMA controller. unsafe { Instance::new_unchecked(&raw mut (*self.dma.as_ptr()).TCD[self.index]) } } /// Returns `true` if the hardware is signaling requests towards /// this DMA channel. pub fn is_hardware_signaling(&self) -> bool { (1 << self.index) & ral_registers::read_reg!(dma, self.dma, HRS) != 0 } /// Returns `true` if an interrupt has activated for this /// DMA channel. pub fn is_interrupt(&self) -> bool { ral_registers::read_reg!(tcd, self.tcd(), INT) != 0 } /// Clear the interrupt trigger. pub fn clear_interrupt(&self) { ral_registers::write_reg!(tcd, self.tcd(), INT, 1); } /// Returns `true` if this DMA channel has finished its transfer. pub fn is_done(&self) -> bool { ral_registers::read_reg!(tcd, self.tcd(), CSR, DONE == 1) } /// Clear the "done" signal from software. pub fn clear_done(&self) { // Safety: the CSR register is the first register of the // TCD. The peripheral tolerates 8-bit access. We can poke // the DONE bit with a single write without risking a race // in the lower bits of the register. // // I should rewrite the register block to lay out the data // like I want... unsafe { let csr: *mut u32 = &raw mut (*self.tcd().as_ptr()).CSR; csr.cast::().add(3).write_volatile(1 << 6) }; } /// Returns `true` if this channel has an error. pub fn has_error(&self) -> bool { ral_registers::read_reg!(tcd, self.tcd(), ES) != 0 } /// Clear this channel's error. pub fn clear_error(&self) { ral_registers::write_reg!(tcd, self.tcd(), ES, ERR: 1); } /// Returns `true` if this channel is actively transferring. pub fn is_active(&self) -> bool { ral_registers::read_reg!(tcd, self.tcd(), CSR, ACTIVE == 1) } /// Reset the control descriptor for the channel. /// /// Call this at least once, after acquiring the channel, to /// put it into a known-good state. pub fn reset(&mut self) { unsafe { let tcd = &raw mut (*self.tcd().as_ptr()).TCD; tcd.write_bytes(0, 1); }; } /// Set the source address for a DMA transfer /// /// `saddr` should be a memory location that can provide the DMA controller /// with data. /// /// # Safety /// /// If the DMA channel is already enabled, the DMA engine may start reading this /// memory location. You must ensure that reads to `saddr` do not perform /// inappropriate side effects. You must ensure `saddr` is valid for the /// lifetime of the transfer. pub unsafe fn set_source_address(&self, saddr: *const ()) { ral_registers::write_reg!(tcd, self.tcd(), TCD.SADDR, saddr as u32); } /// Set the source offset *in bytes* /// /// `offset` could be negative, which would decrement the address. /// /// # Safety /// /// This method could allow a DMA engine to read beyond a buffer or /// address. You must ensure that the source is valid for these offsets. pub unsafe fn set_source_offset(&self, offset: i16) { ral_registers::write_reg!(tcd, self.tcd(), TCD.SOFF, offset); } /// Set the destination address for a DMA transfer /// /// `daddr` should be a memory location that can store data from the /// DMA controller. /// /// # Safety /// /// If the DMA channel is already enabled, the DMA engine may start /// writing to this address. You must ensure that writes to `daddr` /// are safe, and that the memory is valid for the lifetime of the /// transfer. pub unsafe fn set_destination_address(&self, daddr: *const ()) { ral_registers::write_reg!(tcd, self.tcd(), TCD.DADDR, daddr as u32); } /// Set the destination offset *in bytes* /// /// `offset` could be negative, which would decrement the address. /// /// # Safety /// /// This method could allow a DMA engine to write beyond the range of /// a buffer. You must ensure that the destination is valid for these /// offsets. pub unsafe fn set_destination_offset(&self, offset: i16) { ral_registers::write_reg!(tcd, self.tcd(), TCD.DOFF, offset); } /// Set the transfer attributes. /// /// The attributes describes the ways the source is read and /// the destination is written. The modulo mask allows the /// buffer to be treated as a circular buffer. /// /// # Safety /// /// Incorrect sizes and modulos may cause invalid memory accesses. pub unsafe fn set_attributes( &self, source_size: ElementSize, source_modulo: u8, destination_size: ElementSize, destination_modulo: u8, ) { ral_registers::write_reg!(tcd, self.tcd(), TCD.ATTR, SSIZE: source_size as u16, SMOD: source_modulo as u16, DSIZE: destination_size as u16, DMOD: destination_modulo as u16, ); } /// Set the source last address adjustment *in bytes* /// /// # Safety /// /// This could allow the DMA engine to reference an invalid source buffer. /// You must ensure that the adjustment performed by the DMA engine is /// valid, assuming that another DMA transfer immediately runs after the /// current transfer completes. pub unsafe fn set_source_last_address_adjustment(&self, adjustment: i32) { ral_registers::write_reg!(tcd, self.tcd(), TCD.SLAST, adjustment); } /// Set the destination last addrss adjustment *in bytes* /// /// # Safety /// /// This could allow the DMA engine to reference an invalid destination address. /// You must ensure that the adjustment performed by the DMA engine is /// valid, assuming that another DMA transfer immediately runs after the /// current transfer completes. pub unsafe fn set_destination_last_address_adjustment(&self, adjustment: i32) { ral_registers::write_reg!(tcd, self.tcd(), TCD.DLAST_SGA, adjustment); } /// Set the number of *bytes* to transfer per minor loop /// /// Describes how many bytes we should transfer for each DMA service request. /// Note that `nbytes` of `0` is interpreted as a 4GB transfer. /// /// # Safety /// /// This might allow the DMA engine to read beyond the source, or write beyond /// the destination. Caller must ensure that the number of bytes per minor loop /// is valid for the given transfer. pub unsafe fn set_minor_loop_bytes(&self, nbytes: u32) { ral_registers::write_reg!(tcd, self.tcd(), TCD.NBYTES, nbytes); } /// Tells the DMA channel how many transfer iterations to perform /// /// A 'transfer iteration' is a read from a source, and a write to a destination, with /// read and write sizes described by a minor loop. Each iteration requires a DMA /// service request, either from hardware or from software. The maximum number of iterations /// is 2^15. /// /// # Safety /// /// This may allow the DMA engine to read beyond the source, or write beyond /// the destination. Caller must ensure that the number of iterations is valid /// for the transfer. pub unsafe fn set_transfer_iterations(&mut self, iterations: u16) { let iterations = iterations & 0x7FFF; // Note that this is clearing the ELINK bit. We don't have support // for channel-to-channel linking right now. Clearing ELINK is intentional // to use the whole 15 bits for iterations. ral_registers::write_reg!(tcd, self.tcd(), TCD.CITER, iterations); ral_registers::write_reg!(tcd, self.tcd(), TCD.BITER, iterations); } /// Returns the beginning transfer iterations setting for the channel. /// /// This reflects the last call to `set_transfer_iterations`. pub fn beginning_transfer_iterations(&self) -> u16 { ral_registers::read_reg!(tcd, self.tcd(), TCD.BITER) } /// Enable or disable 'disable on completion' /// /// 'Disable on completion' lets the DMA channel automatically clear the request signal /// when it completes a transfer. pub fn set_disable_on_completion(&mut self, dreq: bool) { ral_registers::modify_reg!(tcd, self.tcd(), TCD.CSR, DREQ: dreq as u16); } /// Enable or disable interrupt generation when the transfer completes /// /// You're responsible for registering your interrupt handler. pub fn set_interrupt_on_completion(&mut self, intr: bool) { ral_registers::modify_reg!(tcd, self.tcd(), TCD.CSR, INTMAJOR: intr as u16); } /// Start a DMA transfer /// /// `start()` should be used to request service from the DMA controller. It's /// necessary for in-memory DMA transfers. Do not use it for hardware-initiated /// DMA transfers. DMA transfers that involve hardware will rely on the hardware /// to request DMA service. /// /// Flag is automatically cleared by hardware after it's asserted. pub fn start(&mut self) { ral_registers::modify_reg!(tcd, self.tcd(), TCD.CSR, START: 1); } /// Set the source signal for channel activation. /// /// The source is typically an upstream peripheral. /// Use `None` to disable the source. /// /// This call reads back the written source. If they're equal /// the return is `true`. The values may not be equal if the /// source is selected in another DMA channel. pub fn set_source_signal(&self, signal: Option) -> bool { let signal = signal.map_or(0, NonZeroU8::get) as u32; ral_registers::write_reg!(tcd, self.tcd(), MUX, SRC: signal); signal == ral_registers::read_reg!(tcd, self.tcd(), MUX, SRC) } } } pub mod edma4 { use core::num::NonZeroU8; use crate::{dma::edma4 as dma, element::ElementSize, tcd::edma34 as tcd}; use ral_registers::Instance; /// A DMA channel for an eDMA controller. /// /// The channel is two pointers wide. pub struct Channel { index: usize, dma: Instance, } impl Channel { /// Create a new DMA channel. /// /// # Safety /// /// The channel formed by this call cannot alias another channel with the /// same index. The channel index must be valid for the hardware. pub const unsafe fn new(dma: Instance, index: usize) -> Self { Self { index, dma } } /// Enable the DMA channel. /// /// # Safety /// /// If the channel is incorrectly configured, it may access /// invalid memory. pub unsafe fn enable(&mut self) { ral_registers::modify_reg!(tcd, self.tcd(), CSR, ERQ: 1, DONE: 0); } /// Disable the DMA channel. pub fn disable(&mut self) { ral_registers::modify_reg!(tcd, self.tcd(), CSR, ERQ: 0, DONE: 0); } /// Returns `true` if the DMA channel is enabled. pub fn is_enabled(&self) -> bool { ral_registers::read_reg!(tcd, self.tcd(), CSR, ERQ != 0) } fn tcd(&self) -> Instance { // Safety: caller claims the index is valid for // the DMA controller. unsafe { Instance::new_unchecked(&raw mut (*self.dma.as_ptr()).TCD[self.index]) } } /// Returns `true` if the hardware is signaling requests towards /// this DMA channel. pub fn is_hardware_signaling(&self) -> bool { if self.index < 32 { (1 << self.index) & ral_registers::read_reg!(dma, self.dma, HRS_LOW) != 0 } else { (1 << (self.index - 32)) & ral_registers::read_reg!(dma, self.dma, HRS_HIGH) != 0 } } /// Returns `true` if an interrupt has activated for this /// DMA channel. pub fn is_interrupt(&self) -> bool { ral_registers::read_reg!(tcd, self.tcd(), INT) != 0 } /// Clear the interrupt trigger. pub fn clear_interrupt(&self) { ral_registers::write_reg!(tcd, self.tcd(), INT, 1); } /// Returns `true` if this DMA channel has finished its transfer. pub fn is_done(&self) -> bool { ral_registers::read_reg!(tcd, self.tcd(), CSR, DONE == 1) } /// Clear the "done" signal from software. pub fn clear_done(&self) { // Safety: the CSR register is the first register of the // TCD. The peripheral tolerates 8-bit access. We can poke // the DONE bit with a single write without risking a race // in the lower bits of the register. // // I should rewrite the register block to lay out the data // like I want... unsafe { let csr: *mut u32 = &raw mut (*self.tcd().as_ptr()).CSR; csr.cast::().add(3).write_volatile(1 << 6) }; } /// Returns `true` if this channel has an error. pub fn has_error(&self) -> bool { ral_registers::read_reg!(tcd, self.tcd(), ES) != 0 } /// Clear this channel's error. pub fn clear_error(&self) { ral_registers::write_reg!(tcd, self.tcd(), ES, ERR: 1); } /// Returns `true` if this channel is actively transferring. pub fn is_active(&self) -> bool { ral_registers::read_reg!(tcd, self.tcd(), CSR, ACTIVE == 1) } /// Reset the control descriptor for the channel. /// /// Call this at least once, after acquiring the channel, to /// put it into a known-good state. pub fn reset(&mut self) { unsafe { let tcd = &raw mut (*self.tcd().as_ptr()).TCD; tcd.write_bytes(0, 1); }; } /// Set the source address for a DMA transfer /// /// `saddr` should be a memory location that can provide the DMA controller /// with data. /// /// # Safety /// /// If the DMA channel is already enabled, the DMA engine may start reading this /// memory location. You must ensure that reads to `saddr` do not perform /// inappropriate side effects. You must ensure `saddr` is valid for the /// lifetime of the transfer. pub unsafe fn set_source_address(&self, saddr: *const ()) { ral_registers::write_reg!(tcd, self.tcd(), TCD.SADDR, saddr as u32); } /// Set the source offset *in bytes* /// /// `offset` could be negative, which would decrement the address. /// /// # Safety /// /// This method could allow a DMA engine to read beyond a buffer or /// address. You must ensure that the source is valid for these offsets. pub unsafe fn set_source_offset(&self, offset: i16) { ral_registers::write_reg!(tcd, self.tcd(), TCD.SOFF, offset); } /// Set the destination address for a DMA transfer /// /// `daddr` should be a memory location that can store data from the /// DMA controller. /// /// # Safety /// /// If the DMA channel is already enabled, the DMA engine may start /// writing to this address. You must ensure that writes to `daddr` /// are safe, and that the memory is valid for the lifetime of the /// transfer. pub unsafe fn set_destination_address(&self, daddr: *const ()) { ral_registers::write_reg!(tcd, self.tcd(), TCD.DADDR, daddr as u32); } /// Set the destination offset *in bytes* /// /// `offset` could be negative, which would decrement the address. /// /// # Safety /// /// This method could allow a DMA engine to write beyond the range of /// a buffer. You must ensure that the destination is valid for these /// offsets. pub unsafe fn set_destination_offset(&self, offset: i16) { ral_registers::write_reg!(tcd, self.tcd(), TCD.DOFF, offset); } /// Set the transfer attributes. /// /// The attributes describes the ways the source is read and /// the destination is written. The modulo mask allows the /// buffer to be treated as a circular buffer. /// /// # Safety /// /// Incorrect sizes and modulos may cause invalid memory accesses. pub unsafe fn set_attributes( &self, source_size: ElementSize, source_modulo: u8, destination_size: ElementSize, destination_modulo: u8, ) { ral_registers::write_reg!(tcd, self.tcd(), TCD.ATTR, SSIZE: source_size as u16, SMOD: source_modulo as u16, DSIZE: destination_size as u16, DMOD: destination_modulo as u16, ); } /// Set the source last address adjustment *in bytes* /// /// # Safety /// /// This could allow the DMA engine to reference an invalid source buffer. /// You must ensure that the adjustment performed by the DMA engine is /// valid, assuming that another DMA transfer immediately runs after the /// current transfer completes. pub unsafe fn set_source_last_address_adjustment(&self, adjustment: i32) { ral_registers::write_reg!(tcd, self.tcd(), TCD.SLAST, adjustment); } /// Set the destination last addrss adjustment *in bytes* /// /// # Safety /// /// This could allow the DMA engine to reference an invalid destination address. /// You must ensure that the adjustment performed by the DMA engine is /// valid, assuming that another DMA transfer immediately runs after the /// current transfer completes. pub unsafe fn set_destination_last_address_adjustment(&self, adjustment: i32) { ral_registers::write_reg!(tcd, self.tcd(), TCD.DLAST_SGA, adjustment); } /// Set the number of *bytes* to transfer per minor loop /// /// Describes how many bytes we should transfer for each DMA service request. /// Note that `nbytes` of `0` is interpreted as a 4GB transfer. /// /// # Safety /// /// This might allow the DMA engine to read beyond the source, or write beyond /// the destination. Caller must ensure that the number of bytes per minor loop /// is valid for the given transfer. pub unsafe fn set_minor_loop_bytes(&self, nbytes: u32) { ral_registers::write_reg!(tcd, self.tcd(), TCD.NBYTES, nbytes); } /// Tells the DMA channel how many transfer iterations to perform /// /// A 'transfer iteration' is a read from a source, and a write to a destination, with /// read and write sizes described by a minor loop. Each iteration requires a DMA /// service request, either from hardware or from software. The maximum number of iterations /// is 2^15. /// /// # Safety /// /// This may allow the DMA engine to read beyond the source, or write beyond /// the destination. Caller must ensure that the number of iterations is valid /// for the transfer. pub unsafe fn set_transfer_iterations(&mut self, iterations: u16) { let iterations = iterations & 0x7FFF; // Note that this is clearing the ELINK bit. We don't have support // for channel-to-channel linking right now. Clearing ELINK is intentional // to use the whole 15 bits for iterations. ral_registers::write_reg!(tcd, self.tcd(), TCD.CITER, iterations); ral_registers::write_reg!(tcd, self.tcd(), TCD.BITER, iterations); } /// Returns the beginning transfer iterations setting for the channel. /// /// This reflects the last call to `set_transfer_iterations`. pub fn beginning_transfer_iterations(&self) -> u16 { ral_registers::read_reg!(tcd, self.tcd(), TCD.BITER) } /// Enable or disable 'disable on completion' /// /// 'Disable on completion' lets the DMA channel automatically clear the request signal /// when it completes a transfer. pub fn set_disable_on_completion(&mut self, dreq: bool) { ral_registers::modify_reg!(tcd, self.tcd(), TCD.CSR, DREQ: dreq as u16); } /// Enable or disable interrupt generation when the transfer completes /// /// You're responsible for registering your interrupt handler. pub fn set_interrupt_on_completion(&mut self, intr: bool) { ral_registers::modify_reg!(tcd, self.tcd(), TCD.CSR, INTMAJOR: intr as u16); } /// Start a DMA transfer /// /// `start()` should be used to request service from the DMA controller. It's /// necessary for in-memory DMA transfers. Do not use it for hardware-initiated /// DMA transfers. DMA transfers that involve hardware will rely on the hardware /// to request DMA service. /// /// Flag is automatically cleared by hardware after it's asserted. pub fn start(&mut self) { ral_registers::modify_reg!(tcd, self.tcd(), TCD.CSR, START: 1); } /// Set the source signal for channel activation. /// /// The source is typically an upstream peripheral. /// Use `None` to disable the source. /// /// This call reads back the written source. If they're equal /// the return is `true`. The values may not be equal if the /// source is selected in another DMA channel. pub fn set_source_signal(&self, signal: Option) -> bool { let signal = signal.map_or(0, NonZeroU8::get) as u32; ral_registers::write_reg!(tcd, self.tcd(), MUX, SRC: signal); signal == ral_registers::read_reg!(tcd, self.tcd(), MUX, SRC) } } }